Files
VisualSapfor/src/Visual_DVM_2021/Passes/SSH/ConnectionPass.java
2023-12-05 16:15:12 +03:00

415 lines
20 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package Visual_DVM_2021.Passes.SSH;
import Common.Constants;
import Common.Utils.Utils;
import Common.Utils.Validators.ShellParser;
import GlobalData.Machine.Machine;
import GlobalData.RemoteFile.RemoteFile;
import GlobalData.User.User;
import ProjectData.Project.db_project_info;
import Visual_DVM_2021.Passes.PassException;
import Visual_DVM_2021.Passes.Pass_2021;
import com.jcraft.jsch.*;
import javafx.util.Pair;
import java.io.*;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.Vector;
//убираем лишний класс-прослойку.
//старый server pass Пока не трогаем.
//https://stackoverflow.com/questions/15108923/sftp-file-transfer-using-java-jsch
//https://javadevblog.com/kak-dobavit-biblioteku-jar-fajl-v-proekt-intellij-idea.html
//http://www.jcraft.com/jsch/
//http://developer-remarks.blogspot.com/2013/05/ssh-via-jsch-example.html
//https://stackoverflow.com/questions/4194439/sending-commands-to-server-via-jsch-shell-channel
public abstract class ConnectionPass<T> extends Pass_2021<T> {
//-----------------------------------------------
public static final String modules = "modules";
//--------------
public static final String starter = "starter";
public static final String launcher = "launcher";
public static final String planner = "planner";
//--------------
public static final String Process_r_header = "Process_r.h";
public static final String starter_code = "starter.cpp";
public static final String launcher_code = "launcher.cpp";
//--------------
public static final String projects = "projects";
public static final String compilers = "compilers";
public static final String tests = "tests";
public Machine machine = null;
public User user = null;
//тут как в WinScp - 2 независимых канала. один для файлов. другой для команд.
public ChannelSftp sftpChannel = null;
public ChannelShell shellChannel = null;
public ChannelExec execChannel = null;
//----------------------------------------------------
protected JSch jsch;
protected Session session = null;
protected boolean needsInitialize() {
return true;
}
//-------------------------------------
protected boolean isConnected = false; //пока
public ChannelSftp getSftpChannel() {
return sftpChannel;
}
//учитывать. что пока сокет не создан, прервать соединение нельзя.
public void Connect() throws Exception {
isConnected = false;
session = (jsch = new JSch()).getSession(user.login, machine.address, machine.port);
switch (user.authentication) {
case password:
session.setPassword(user.password);
break;
/* пока не совсем ясно как это делать.
case key:
// https://stackoverflow.com/questions/47422025/java-sftp-authentication-with-ppk-file
//todo перешифровать ключ https://www.example-code.com/java/ssh_ppk_to_pem.asp
jsch.addIdentity(user.password);
break;
*/
}
session.setConfig("StrictHostKeyChecking", "no");
//UI.Print("Establishing Connection...");
ShowMessage1("соединение с машиной " + Utils.Brackets(machine.getURL()) + "..");
session.connect(0);
//UI.Print("Connection established.");
//UI.Print("Creating SFTP Channel.");
sftpChannel = (ChannelSftp) session.openChannel("sftp");
sftpChannel.connect();
isConnected = true; // теперь можно прерывать метод.
if (needsInitialize()) {
RemoteFile userWorkspace = new RemoteFile(user.workspace, true);
if (!Exists(userWorkspace))
throw new WorkspaceNotFoundException(
"Рабочее пространство пользователя " + Utils.Brackets(user.login)
+ " на машине " + Utils.Brackets(machine.getURL())
+ "\n" + Utils.Brackets(user.workspace) + "\е найдено.\n" +
"Требуется выполнить повторную инициализацию пользователя."
);
}
}
public void Disconnect() {
if (sftpChannel != null) sftpChannel.disconnect();
if (execChannel != null) execChannel.disconnect();
if (session != null) session.disconnect();
//----------------------
sftpChannel = null;
execChannel = null;
session = null;
isConnected = false;
//---------------------
// UI.Print(DebugPrintLevel.Session, "session ended");
}
@Override
protected void performFinish() throws Exception {
Disconnect();
}
public void Command(String... commands) throws Exception {
if (commands.length > 0) {
String command = String.join("\n", commands);
// UI.Print(DebugPrintLevel.Session, command);
// UI.Print(DebugPrintLevel.Session, "Creating Exec Channel.");
execChannel = (ChannelExec) session.openChannel("exec");
execChannel.setErrStream(System.err);
execChannel.setCommand(command);
execChannel.connect();
BufferedReader in = new BufferedReader(new InputStreamReader(execChannel.getInputStream()));
while (in.readLine() != null) ;
execChannel.disconnect();
}
}
public String CommandWithAnswer(char end, String... commands) throws Exception {
String output = "";
if (commands.length > 0) {
String command = String.join("\n", commands);
// UI.Print(DebugPrintLevel.Session, command);
// UI.Print(DebugPrintLevel.Session, "Creating Exec Channel.");
// System.out.println(Utils.Brackets(command));
execChannel = (ChannelExec) session.openChannel("exec");
execChannel.setErrStream(System.err);
InputStreamReader reader = new InputStreamReader(execChannel.getInputStream());
execChannel.setCommand(command);
execChannel.connect();
char[] chars = new char[1];
while (reader.read(chars) >= 0) if (chars[0] == end) break;
else output += chars[0];
execChannel.disconnect();
}
// System.out.println(Utils.Brackets(output));
return output;
}
//https://stackoverflow.com/questions/4194439/sending-commands-to-server-via-jsch-shell-channel
@Override
protected void CheckException(Exception ex) {
if (ex instanceof WorkspaceNotFoundException) {
Log.Writeln_(ex.getMessage());
return;
}
Throwable cause = getCauseRec(ex);
if ((cause instanceof UnknownHostException) || (cause instanceof SocketException)) {
Log.Writeln(machine.getFullDescription() + " не найден(а).\n" + "Проверьте наличие подключения к сети.");
} else if (cause instanceof SocketTimeoutException) {
Log.Writeln(machine.getFullDescription() + " не отвечает.");
} else super.CheckException(ex);
}
public Vector<String> read_file_lines(String path_r) throws Exception {
Vector<String> res = new Vector<>();
InputStream out = sftpChannel.get(path_r);
BufferedReader br = new BufferedReader(new InputStreamReader(out));
String line;
while ((line = br.readLine()) != null) res.add(line);
br.close();
return res;
}
public void getSingleFile(String src, String dst) throws Exception {
sftpChannel.get(src, dst);
}
//с проверкой.
public boolean tryGetSingleFileWithMaxSize(RemoteFile src, File dst, int maxSize) throws Exception {
if (Exists(src)) {
if ((maxSize == 0) || (getFileKBSize(src.full_name) <= maxSize)) {
getSingleFile(src.full_name, dst.getAbsolutePath());
return true;
} else {
Utils.WriteToFile(dst, "Размер файла превышает " + maxSize + " KB.\n" + "Файл не загружен. Его можно просмотреть на машине по адресу\n" + Utils.Brackets(src.full_name));
}
}
return false;
}
public void putSingleFile(File src, RemoteFile dst) throws Exception {
sftpChannel.put(src.getAbsolutePath(), dst.full_name);
}
public void tryMKDir(RemoteFile dir) throws Exception {
if (!Exists(dir)) sftpChannel.mkdir(dir.full_name);
}
public void tryRM(RemoteFile file) throws Exception {
if (Exists(file))
sftpChannel.rm(file.full_name);
}
public void putSingleFile(String src, String dst) throws Exception {
sftpChannel.put(src, dst);
}
public void put_resource(String res_name) throws Exception {
putSingleFile(Utils.CreateTempResourceFile(res_name).getAbsolutePath(), res_name);
}
@Override
public void body() throws Exception {
Connect();
ServerAction();
}
protected void ServerAction() throws Exception {
}
//https://losst.ru/komanda-find-v-linux#%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D1%8B%D0%B5_%D0%BF%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80%D1%8B_%D0%BA%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D1%8B_find
public String getStarter() {
return String.join("/", user.workspace, modules, starter);
}
public String getLauncher() {
return String.join("/", user.workspace, modules, launcher);
}
//насчет даты.
//http://i-leon.ru/tools/time
//этот метод для того чтобы не пересылать подверсии. В случае тестов их наличие исключено!
public void SynchronizeProjectSubDirsR(db_project_info project, File local_dir, RemoteFile remote_dir, boolean data) throws Exception {
ShowMessage2("синхронизация: " + local_dir.getName());
Vector<File> local_subdirs = project.getSubdirectoriesSimple(local_dir);
Vector<File> local_files = project.getActiveFilesForSynchronization(local_dir, data);
//------------------------------------------------------------------------
LinkedHashMap<String, RemoteFile> remote_subdirs = new LinkedHashMap<>();
LinkedHashMap<String, RemoteFile> remote_files = new LinkedHashMap<>();
Vector<ChannelSftp.LsEntry> files = sftpChannel.ls(remote_dir.full_name);
for (ChannelSftp.LsEntry file : files) {
if (file.getAttrs().isDir()) {
if (!file.getFilename().equals(".") && !file.getFilename().equals(".."))
remote_subdirs.put(file.getFilename(), new RemoteFile(remote_dir.full_name, file.getFilename(), true));
} else {
RemoteFile rf = new RemoteFile(remote_dir.full_name, file.getFilename());
rf.updateTime = RemoteFile.convertUpdateTime(file.getAttrs().getMTime());
remote_files.put(file.getFilename(), rf);
}
}
for (File lsd : local_subdirs) {
RemoteFile rsd = null;
if (!remote_subdirs.containsKey(lsd.getName()))
sftpChannel.mkdir((rsd = new RemoteFile(remote_dir.full_name, lsd.getName(), true)).full_name);
else rsd = remote_subdirs.get(lsd.getName());
SynchronizeProjectSubDirsR(project, lsd, rsd, data);
}
for (File lf : local_files) {
RemoteFile rf = null;
if (!remote_files.containsKey(lf.getName())) {
rf = new RemoteFile(remote_dir.full_name, lf.getName());
ShowMessage2(lf.getName());
putSingleFile(lf, rf);
} else {
rf = remote_files.get(lf.getName());
if (lf.lastModified() > rf.updateTime) {
ShowMessage2(lf.getName());
putSingleFile(lf, rf);
}
}
}
}
protected int getFileKBSize(String path) throws Exception {
return Integer.parseInt(CommandWithAnswer('\t', "du " + Utils.DQuotes(path)));
}
//https://stackoverflow.com/questions/4194439/sending-commands-to-server-via-jsch-shell-channel
public String ShellCommand(String... commands) throws Exception {
shellChannel = (ChannelShell) session.openChannel("shell");
PipedInputStream in = new PipedInputStream();
PipedOutputStream out = new PipedOutputStream();
shellChannel.setInputStream(in);
shellChannel.setOutputStream(out);
PipedOutputStream pin = new PipedOutputStream(in);
PipedInputStream pout = new PipedInputStream(out);
shellChannel.connect();
InputStreamReader fromServer = new InputStreamReader(pout);
ShellParser.setUserName(user.login);
ShellParser.ReadInvitation(fromServer);
// System.out.println("first invitation read");
String result = "";
//формат работы с шеллом.
//<эхо-команды\n><ответ команды><приглашение\n><приглашение>
for (String command : commands) {
// System.out.println(command);
pin.write((command + "\r\n").getBytes());
ShellParser.ReadLine(fromServer);
// UI.Info("echo read");
ShellParser.ReadInvitation(fromServer);
// UI.Info("first invitation read");
result = ShellParser.getCommandResult(fromServer);
// UI.Info("result = " + Utils.Brackets(result));
}
shellChannel.disconnect();
return result;
}
public Vector<RemoteFile> getFilesByExtensions(RemoteFile dir, String... extensions) throws Exception {
Vector<RemoteFile> res = new Vector<>();
Vector<ChannelSftp.LsEntry> files = sftpChannel.ls(dir.full_name);
for (ChannelSftp.LsEntry file : files) {
String[] data = file.getFilename().split("\\.");
if (data.length > 1) {
String file_extension = data[data.length - 1];
for (String extension : extensions) {
if (file_extension.equalsIgnoreCase(extension))
res.add(new RemoteFile(dir.full_name, file.getFilename()));
}
}
}
return res;
}
public void deleteFilesByExtensions(RemoteFile dir, String... extensions) throws Exception {
Vector<RemoteFile> to_delete = getFilesByExtensions(dir, extensions);
for (RemoteFile file : to_delete)
sftpChannel.rm(file.full_name);
}
public void copy(RemoteFile src, RemoteFile dst) throws Exception {
ShellCommand("cp " + Utils.DQuotes(src.full_name) + " " + Utils.DQuotes(dst.full_name));
}
//-------
public void writeToFile(String text, RemoteFile dst) throws Exception {
sftpChannel.put(new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)), dst.full_name);
sftpChannel.chmod(0777, dst.full_name);
}
public void SynchronizeSubDirsR(File local_dir, RemoteFile remote_dir) throws Exception {
File[] local_subdirs = local_dir.listFiles(File::isDirectory);
File[] local_files = local_dir.listFiles(File::isFile);
//------------------------------------------------------------------------
LinkedHashMap<String, RemoteFile> remote_subdirs = new LinkedHashMap<>();
LinkedHashMap<String, RemoteFile> remote_files = new LinkedHashMap<>();
Vector<ChannelSftp.LsEntry> files = sftpChannel.ls(remote_dir.full_name);
for (ChannelSftp.LsEntry file : files) {
if (file.getAttrs().isDir()) {
if (!file.getFilename().equals(".") && !file.getFilename().equals(".."))
remote_subdirs.put(file.getFilename(), new RemoteFile(remote_dir.full_name, file.getFilename(), true));
} else {
RemoteFile rf = new RemoteFile(remote_dir.full_name, file.getFilename());
rf.updateTime = RemoteFile.convertUpdateTime(file.getAttrs().getMTime());
remote_files.put(file.getFilename(), rf);
}
}
if (local_subdirs != null) {
for (File lsd : local_subdirs) {
if (!lsd.getName().equals(Constants.data)) {
RemoteFile rsd = null;
if (!remote_subdirs.containsKey(lsd.getName()))
sftpChannel.mkdir((rsd = new RemoteFile(remote_dir.full_name, lsd.getName(), true)).full_name);
else rsd = remote_subdirs.get(lsd.getName());
SynchronizeSubDirsR(lsd, rsd);
}
}
}
if (local_files != null) {
for (File lf : local_files) {
RemoteFile rf = null;
if (!remote_files.containsKey(lf.getName())) {
rf = new RemoteFile(remote_dir.full_name, lf.getName());
putSingleFile(lf, rf);
} else {
rf = remote_files.get(lf.getName());
if (lf.lastModified() > rf.updateTime) {
putSingleFile(lf, rf);
}
}
}
}
}
public void RMDIR(String dir) throws Exception {
if (!dir.isEmpty() && !dir.equals("/") && !dir.equals("\\") && !dir.equals("*")) {
ShellCommand("rm -rf " + Utils.DQuotes(dir));
} else throw new PassException("Недопустимый путь для удаления папки " + Utils.DQuotes(dir));
}
//скорее всего,временные методы. они есть в UserConnection, при удаленном запуске тестирования
//--------------------------------------------------------------------------------
public void MKDIR(RemoteFile dir) throws Exception {
if (!Exists(dir)) sftpChannel.mkdir(dir.full_name);
}
//--
public Pair<RemoteFile, RemoteFile> performScript(RemoteFile directory, String... commands) throws Exception {
RemoteFile script_file = new RemoteFile(directory.full_name, Constants.script);
RemoteFile out = new RemoteFile(directory.full_name, Constants.out_file);
RemoteFile err = new RemoteFile(directory.full_name, Constants.err_file);
//
Vector<RemoteFile> files = new Vector<>();
files.add(script_file);
files.add(out);
files.add(err);
for (RemoteFile file : files) {
if (Exists(file))
sftpChannel.rm(file.full_name);
}
//--
writeToFile(String.join("\n", commands), script_file);
//--
ShellCommand("cd " + Utils.DQuotes(directory.full_name),
script_file.full_name + " 1>" + Constants.out_file + " 2>" + Constants.err_file);
return new Pair<>(out, err);
}
//--
//--сам себя
public boolean Exists(String file_full_name) throws Exception {
try {
sftpChannel.stat(file_full_name);
return true;
} catch (SftpException e) {
if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
// file doesn't exist
return false;
} else {
// something else went wrong
throw e;
}
}
}
public boolean Exists(RemoteFile file) throws Exception {
return Exists(file.full_name);
}
}