diff --git a/src/Core/Main.vala b/src/Core/Main.vala index fe72628..069b853 100644 --- a/src/Core/Main.vala +++ b/src/Core/Main.vala @@ -206,7 +206,7 @@ public class Main : GLib.Object{ } check_and_remove_timeshift_btrfs(); - + // init log ------------------ try { @@ -3922,12 +3922,7 @@ public class Main : GLib.Object{ ret_val = exec_script_sync(cmd, out std_out, out std_err); if (ret_val == 0){ required_space = long.parse(std_out.replace(",","").strip()); - - cmd = "wc -l '%s'".printf(escape_single_quote(file_log)); - ret_val = exec_script_sync(cmd, out std_out, out std_err); - if (ret_val == 0){ - file_count = long.parse(std_out.split(" ")[0].strip()); - } + file_count = file_line_count(file_log) ?? 0; thr_success = true; } @@ -4380,11 +4375,8 @@ public class Main : GLib.Object{ string cmd = "umount '%s'".printf(escape_single_quote(mdir2)); int retval = exec_sync(cmd); - - string cmd2 = "rmdir '%s'".printf(escape_single_quote(mdir2)); - int retval2 = exec_sync(cmd2); - - if (retval2 != 0){ + + if (retval != 0){ log_debug("E: Failed to unmount"); log_debug("Ret=%d".printf(retval)); //ignore @@ -4392,6 +4384,11 @@ public class Main : GLib.Object{ else{ log_debug("Unmounted successfully"); } + + // delete directory + if(!dir_empty_delete(mdir2)) { + log_debug("E: Failed to delete %s".printf(mdir2)); + } } } } diff --git a/src/Core/SnapshotRepo.vala b/src/Core/SnapshotRepo.vala index ba270f0..321835b 100644 --- a/src/Core/SnapshotRepo.vala +++ b/src/Core/SnapshotRepo.vala @@ -883,55 +883,20 @@ public class SnapshotRepo : GLib.Object{ } private void delete_directory_thread(){ - string cmd = ""; - string std_out; - string std_err; - int ret_val; + thr_success = TeeJee.FileSystem.dir_delete_recursive(thr_args1); - try{ - var f = File.new_for_path(thr_args1); - if(f.query_exists()){ - cmd = "rm -rf \"%s\"".printf(thr_args1); - - if (LOG_COMMANDS) { log_debug(cmd); } - - Process.spawn_command_line_sync(cmd, out std_out, out std_err, out ret_val); - - if (ret_val != 0){ - log_error(_("Failed to remove") + ": '%s'".printf(thr_args1)); - thr_success = false; - thr_running = false; - return; - } - else{ - log_msg(_("Removed") + ": '%s'".printf(thr_args1)); - thr_success = true; - thr_running = false; - return; - } - } - else{ - log_error(_("Directory not found") + ": '%s'".printf(thr_args1)); - thr_success = true; - thr_running = false; - } - } - catch(Error e){ - log_error (e.message); - thr_success = false; - thr_running = false; - return; + if (thr_success) { + log_msg(_("Removed") + ": '%s'".printf(thr_args1)); + } else{ + log_error(_("Failed to remove") + ": '%s'".printf(thr_args1)); } + + thr_running = false; } // symlinks ---------------------------------------- public void create_symlinks(){ - string cmd = ""; - string std_out; - string std_err; - int ret_val; - cleanup_symlink_dir("boot"); cleanup_symlink_dir("hourly"); cleanup_symlink_dir("daily"); @@ -939,21 +904,19 @@ public class SnapshotRepo : GLib.Object{ cleanup_symlink_dir("monthly"); cleanup_symlink_dir("ondemand"); - string path; - foreach(var bak in snapshots){ - foreach(string tag in bak.tags){ - - path = "%s-%s".printf(snapshots_path, tag); - cmd = "ln --symbolic \"../snapshots/%s\" -t \"%s\"".printf(bak.name, path); - - if (LOG_COMMANDS) { log_debug(cmd); } - - ret_val = exec_sync(cmd, out std_out, out std_err); - if (ret_val != 0){ - log_error (std_err); - log_error(_("Failed to create symlinks") + ": snapshots-%s".printf(tag)); - return; + foreach(Snapshot bak in snapshots){ + foreach(string tag in bak.tags) { + string linkTarget = "%s-%s/%s".printf(snapshots_path, tag, bak.name); + string linkValue = "../snapshots/" + bak.name; + try { + File f = File.new_for_path(linkTarget); + if (!f.make_symbolic_link(linkValue)) { + log_error(_("Failed to create symlinks") + ": %s".printf(linkTarget)); + } + } catch(Error e) { + log_debug(e.message); + log_error(_("Failed to create symlinks") + ": %s".printf(linkTarget)); } } } @@ -962,32 +925,18 @@ public class SnapshotRepo : GLib.Object{ } public void cleanup_symlink_dir(string tag){ - string cmd = ""; - string std_out; - string std_err; - int ret_val; - try{ string path = "%s-%s".printf(snapshots_path, tag); - var f = File.new_for_path(path); - if (f.query_exists()){ - cmd = "rm -rf \"%s\"".printf(path + "/"); - - if (LOG_COMMANDS) { log_debug(cmd); } - - Process.spawn_command_line_sync(cmd, out std_out, out std_err, out ret_val); - if (ret_val != 0){ - log_error (std_err); - log_error(_("Failed to delete symlinks") + ": 'snapshots-%s'".printf(tag)); - return; - } + if(!TeeJee.FileSystem.dir_delete_recursive(path)) { + log_error(_("Failed to delete symlinks") + ": '%s'".printf(path)); } + File f = File.new_for_path(path); f.make_directory_with_parents(); } catch (Error e) { - log_error (e.message); - } + log_error (e.message); + } } } diff --git a/src/Utility/AppLock.vala b/src/Utility/AppLock.vala index c1f509e..667b16e 100644 --- a/src/Utility/AppLock.vala +++ b/src/Utility/AppLock.vala @@ -45,7 +45,7 @@ public class AppLock : GLib.Object { lock_message = txt.split(";")[1].strip(); long pid = long.parse(process_id); - if (process_is_running(pid)){ + if (get_process_exe_name(pid).contains("timeshift")){ log_msg(_("Another instance of this application is running") + " (PID=%ld)".printf(pid)); return false; diff --git a/src/Utility/AsyncTask.vala b/src/Utility/AsyncTask.vala index b247554..96c4fbe 100644 --- a/src/Utility/AsyncTask.vala +++ b/src/Utility/AsyncTask.vala @@ -394,9 +394,7 @@ public abstract class AsyncTask : GLib.Object{ if (status == AppStatus.RUNNING) { process_set_priority (child_pid, prio); - Pid sub_child_pid; - foreach (long pid in get_process_children (child_pid)) { - sub_child_pid = (Pid) pid; + foreach (Pid sub_child_pid in get_process_children (child_pid)) { process_set_priority (sub_child_pid, prio); } } diff --git a/src/Utility/DeleteFileTask.vala b/src/Utility/DeleteFileTask.vala index f0fdfd5..0381ab9 100644 --- a/src/Utility/DeleteFileTask.vala +++ b/src/Utility/DeleteFileTask.vala @@ -69,7 +69,7 @@ public class DeleteFileTask : AsyncTask{ log_error (e.message); } } - + public void prepare() { string script_text = build_script(); log_debug(script_text); @@ -113,13 +113,10 @@ public class DeleteFileTask : AsyncTask{ cmd += " '%s/'".printf(escape_single_quote(dest_path)); } else{ - cmd += "rm"; + cmd += "rm -rf"; if (verbose){ - cmd += " -rfv"; - } - else{ - cmd += " -rf"; + cmd += "v"; } cmd += " '%s'".printf(escape_single_quote(dest_path)); diff --git a/src/Utility/LinuxDistro.vala b/src/Utility/LinuxDistro.vala index 8f60659..8fa13d3 100644 --- a/src/Utility/LinuxDistro.vala +++ b/src/Utility/LinuxDistro.vala @@ -152,50 +152,6 @@ public class LinuxDistro : GLib.Object{ return info; } - public static string get_running_desktop_name(){ - - /* Return the names of the current Desktop environment */ - - int pid = -1; - - pid = get_pid_by_name("cinnamon"); - if (pid > 0){ - return "Cinnamon"; - } - - pid = get_pid_by_name("xfdesktop"); - if (pid > 0){ - return "Xfce"; - } - - pid = get_pid_by_name("lxsession"); - if (pid > 0){ - return "LXDE"; - } - - pid = get_pid_by_name("gnome-shell"); - if (pid > 0){ - return "Gnome"; - } - - pid = get_pid_by_name("wingpanel"); - if (pid > 0){ - return "Elementary"; - } - - pid = get_pid_by_name("unity-panel-service"); - if (pid > 0){ - return "Unity"; - } - - pid = get_pid_by_name("plasma-desktop"); - if (pid > 0){ - return "KDE"; - } - - return "Unknown"; - } - public string dist_type { owned get{ diff --git a/src/Utility/RsyncTask.vala b/src/Utility/RsyncTask.vala index 16ae179..e43e3b6 100644 --- a/src/Utility/RsyncTask.vala +++ b/src/Utility/RsyncTask.vala @@ -289,7 +289,7 @@ public class RsyncTask : AsyncTask{ log_debug("log_file = %s".printf(log_file)); prg_count = 0; - prg_count_total = file_line_count(log_file); + prg_count_total = file_line_count(log_file) ?? 0; try { string line; diff --git a/src/Utility/TeeJee.FileSystem.vala b/src/Utility/TeeJee.FileSystem.vala index 13226ad..125c0c7 100644 --- a/src/Utility/TeeJee.FileSystem.vala +++ b/src/Utility/TeeJee.FileSystem.vala @@ -88,12 +88,28 @@ namespace TeeJee.FileSystem{ } } - public int64 file_line_count (string file_path){ - /* Count number of lines in text file */ - string cmd = "wc -l '%s'".printf(escape_single_quote(file_path)); - string std_out, std_err; - exec_sync(cmd, out std_out, out std_err); - return long.parse(std_out.split("\t")[0]); + public int64? file_line_count (string file_path){ + /* Count number of lines in text file returns null on error */ + + try { + long line_nums = 0; + char symbol; + + File file = File.new_for_path(file_path); + FileInputStream inStream = file.read(); + BufferedInputStream bis = new BufferedInputStream.sized(inStream, (size_t) (1 * MiB)); + while((symbol = (char) bis.read_byte()) != -1) { + if(symbol == '\n') { + line_nums ++; + } + } + bis.close(); + return line_nums; + } catch(Error e) { + log_error (e.message); + log_error(_("Failed to read file") + ": %s".printf(file_path)); + } + return null; } public string? file_read (string file_path){ @@ -282,33 +298,70 @@ namespace TeeJee.FileSystem{ } } - public bool dir_delete (string dir_path, bool show_message = false){ - - /* Recursively deletes directory along with contents */ - - if (!dir_exists(dir_path)){ - return true; + // delete an empty directory + public static bool dir_empty_delete(string path) { + File dirf = File.new_for_path(path); + try { + if (dirf.query_file_type(FileQueryInfoFlags.NONE) == FileType.DIRECTORY) { + return dirf.delete(); // only succeeds, if the dir is empty + } + } catch(Error ioe) { + if(ioe.matches(IOError.quark(), IOError.NOT_FOUND)) { + // directory does not exist + return true; + } } - - string cmd = "rm -rf '%s'".printf(escape_single_quote(dir_path)); - - log_debug(cmd); - - string std_out, std_err; - int status = exec_sync(cmd, out std_out, out std_err); - + return false; + } + + public static bool dir_delete_recursive(string dir) { + File f = File.new_for_path(dir); + if(f.query_exists()) { + try { + FileEnumerator enumerator = f.enumerate_children(FileAttribute.STANDARD_NAME, FileQueryInfoFlags.NOFOLLOW_SYMLINKS); + FileInfo info; + while ((info = enumerator.next_file()) != null) { + string name = info.get_name(); + if(info.get_file_type() == FileType.DIRECTORY) { + + // ignore . and .. + if(name == "." || name == "..") { + continue; + } + + if(!dir_delete_recursive(dir + "/" + name)) { + return false; + } + } else { + File file = File.new_for_path(dir + "/" + name); + file.delete(); + } + } + f.delete(); + } catch(Error err) { + log_error(_("Can not enumerate folder %s").printf(dir)); + return false; + } + } + return true; + } + + public static bool dir_delete(string dir_path, bool show_message = false) { + + /* Recursively deletes directory along with contents */ + + bool status = dir_delete_recursive(dir_path); + if (show_message){ - if (status == 0){ + if (status){ log_msg(_("Deleted directory") + ": %s".printf(dir_path)); } else{ log_error(_("Failed to delete directory") + ": %s".printf(dir_path)); - log_error(std_out); - log_error(std_err); } } - - return (status == 0); + + return status; } public bool dir_is_empty (string dir_path){ @@ -320,7 +373,7 @@ namespace TeeJee.FileSystem{ var dir = File.parse_name (dir_path); if (dir.query_exists()) { FileInfo info; - var enu = dir.enumerate_children ("%s".printf(FileAttribute.STANDARD_NAME), 0); + var enu = dir.enumerate_children (FileAttribute.STANDARD_NAME, 0); while ((info = enu.next_file()) != null) { is_empty = false; break; diff --git a/src/Utility/TeeJee.Process.vala b/src/Utility/TeeJee.Process.vala index 1f55311..8673e6d 100644 --- a/src/Utility/TeeJee.Process.vala +++ b/src/Utility/TeeJee.Process.vala @@ -272,107 +272,113 @@ namespace TeeJee.ProcessHelper{ } } - // dep: pidof, TODO: Rewrite using /proc - public int get_pid_by_name (string name){ - - /* Get the process ID for a process with given name */ - - string std_out, std_err; - exec_sync("pidof \"%s\"".printf(name), out std_out, out std_err); - - if (std_out != null){ - string[] arr = std_out.split ("\n"); - if (arr.length > 0){ - return int.parse (arr[0]); - } - } - - return -1; - } - - // dep: ps TODO: Rewrite using /proc - public bool process_is_running(long pid){ - - /* Checks if given process is running */ - - string cmd = ""; - string std_out; - string std_err; - int ret_val; - + // return the name of the executeable of a given pid or self if pid is <= 0 + // returns an empty string on error or if the pid could not be found + public string get_process_exe_name(long pid = -1){ try{ - cmd = "ps --pid %ld".printf(pid); - Process.spawn_command_line_sync(cmd, out std_out, out std_err, out ret_val); + string pidStr = (pid <= 0 ? "self" : pid.to_string()); + string path = "/proc/%s/exe".printf(pidStr); + char[] buf = new char[4096]; + Posix.readlink(path, buf); + return GLib.Path.get_basename((string) buf); } catch (Error e) { log_error (e.message); - return false; } - - return (ret_val == 0); + return ""; } - // dep: ps TODO: Rewrite using /proc - public int[] get_process_children (Pid parent_pid){ + public Pid[] get_process_children (Pid parent_pid){ - /* Returns the list of child processes spawned by given process */ + /* Returns the list of child processes owned by a given process */ - string std_out, std_err; - exec_sync("ps --ppid %d".printf(parent_pid), out std_out, out std_err); + // no explicit check for the existence of /proc/ as this might be a time-of-check-time-of-use bug. + File procfs = File.new_for_path("/proc/"); - int pid; - int[] procList = {}; - string[] arr; + try { + FileEnumerator enumerator = procfs.enumerate_children(FileAttribute.STANDARD_NAME, FileQueryInfoFlags.NOFOLLOW_SYMLINKS); + FileInfo info; + Pid[] childList = {}; + while ((info = enumerator.next_file()) != null) { + if(info.get_file_type() != FileType.DIRECTORY) { + // only interested in directorys + continue; + } + + string name = info.get_name(); + + uint64 pid; + if(!uint64.try_parse(name, out pid)) { + // make sure to not access any other directorys that may be present in /proc for some reason + continue; + } - foreach (string line in std_out.split ("\n")){ - arr = line.strip().split (" "); - if (arr.length < 1) { continue; } + string? fileCont = file_read("/proc/%s/stat".printf(name)); + if(fileCont == null) { + // stat file of pid might not be readable (because of permissions or the process died since we got its pid) + continue; + } - pid = 0; - pid = int.parse (arr[0]); + // the format of the stat file is documented in man 5 proc + // it begging is: pid (comm) status ppid ... + + // the process name could contain a space or ) and confuse the parsing. + // so we make sure to take the last ) and only parse the stuff after that. + int index = fileCont.last_index_of_char(')'); + string parseline = fileCont.substring(index); + string[] splitted = parseline.split(" ", 4); // we are not interested in the part after ppid so just leave it a big string + if(splitted.length != 4) { + // format of stat file is not matching - should never happen + log_error("can not parse state of %ld".printf((long) pid)); + continue; + } - if (pid != 0){ - procList += pid; + uint64 ppid = uint64.parse(splitted[2]); + if(ppid != 0 && ppid == parent_pid) { + // the process is a child of the target parent process + childList += (Pid) pid; + } } + return childList; + } catch (Error e) { + log_error(e.message); + log_error(_("Failed to get child processes of %ld").printf(parent_pid)); } - return procList; + return {}; } // manage process --------------------------------- public void process_quit(Pid process_pid, bool killChildren = true){ - /* Kills specified process and its children (optional). + /* Terminates specified process and its children (optional). * Sends signal SIGTERM to the process to allow it to quit gracefully. * */ - int[] child_pids = get_process_children (process_pid); - Posix.kill (process_pid, Posix.Signal.TERM); - - if (killChildren){ - Pid childPid; - foreach (long pid in child_pids){ - childPid = (Pid) pid; - Posix.kill (childPid, Posix.Signal.TERM); - } - } + process_send_signal(process_pid, Posix.Signal.TERM, killChildren); } - public void process_kill(Pid process_pid, bool killChildren = true){ + public void process_kill(Pid process_pid, bool killChildren = true) { /* Kills specified process and its children (optional). * Sends signal SIGKILL to the process to kill it forcefully. * It is recommended to use the function process_quit() instead. * */ - int[] child_pids = get_process_children (process_pid); - Posix.kill (process_pid, Posix.Signal.KILL); - - if (killChildren){ - Pid childPid; - foreach (long pid in child_pids){ - childPid = (Pid) pid; - Posix.kill (childPid, Posix.Signal.KILL); + process_send_signal(process_pid, Posix.Signal.KILL, killChildren); + } + + public void process_send_signal(Pid process_pid, Posix.Signal sig, bool children = true) { + + /* Sends a signal to a process and its children (optional). */ + + // get the childs before sending the signal, as the childs might not be accessible afterwards + Pid[] child_pids = get_process_children (process_pid); + Posix.kill (process_pid, sig); + + if (children){ + foreach (Pid pid in child_pids){ + Posix.kill (pid, sig); } } }