diff --git a/src/AppConsole.vala b/src/AppConsole.vala index 5f66028..9b05e09 100644 --- a/src/AppConsole.vala +++ b/src/AppConsole.vala @@ -75,6 +75,8 @@ public class AppConsole : GLib.Object { return 0; } + Main.copy_env(); + LOG_ENABLE = false; init_tmp(); LOG_ENABLE = true; diff --git a/src/AppGtk.vala b/src/AppGtk.vala index a6f5a0f..940b16b 100644 --- a/src/AppGtk.vala +++ b/src/AppGtk.vala @@ -66,6 +66,8 @@ public class AppGtk : GLib.Object { } } + Main.copy_env(); + Gtk.init(ref args); GTK_INITIALIZED = true; diff --git a/src/Core/Main.vala b/src/Core/Main.vala index 6bd8c83..e00d601 100644 --- a/src/Core/Main.vala +++ b/src/Core/Main.vala @@ -376,6 +376,24 @@ public class Main : GLib.Object{ } } + // copy env from the spawning parent to this + public static void copy_env() { + Pid user_pid = TeeJee.ProcessHelper.get_user_process(); + string[]? user_env = TeeJee.ProcessHelper.get_process_env(user_pid); + if(user_env == null) { + return; + } + + // copy all required enviroment vars from the user to this process + string[] targets = {"DISPLAY", "XAUTHORITY", "DBUS_SESSION_BUS_ADDRESS"}; + foreach (string target in targets) { + string user_var = TeeJee.ProcessHelper.get_env(user_env, target); + if(user_var != null) { + GLib.Environment.set_variable(target, user_var, true); + } + } + } + private int[]? get_btrfs_version_array () { string stdout; string stderr; diff --git a/src/Utility/TeeJee.FileSystem.vala b/src/Utility/TeeJee.FileSystem.vala index 5e5a8b0..ab90328 100644 --- a/src/Utility/TeeJee.FileSystem.vala +++ b/src/Utility/TeeJee.FileSystem.vala @@ -131,6 +131,37 @@ namespace TeeJee.FileSystem{ return null; } + // read a file with a delimiter into an array + // this function is not very optimized so avoid using it for large files + public string[]? file_read_array(string file_path, char delimiter = '\n'){ + try{ + uint8[] content; + GLib.FileUtils.get_data(file_path, out content); + + // count elements + string[] parsed = {""}; + int element = 0; + foreach (uint8 byte in content) { + // create a new element + if(byte == delimiter) { + element ++; + parsed += ""; + continue; + } + + parsed[element] += ((char) byte).to_string(); + } + + return parsed; + } + catch (Error e){ + log_error (e.message); + log_error(_("Failed to read file") + ": %s".printf(file_path)); + } + + return null; + } + public bool file_write (string file_path, string contents){ /* Write text to file */ diff --git a/src/Utility/TeeJee.Process.vala b/src/Utility/TeeJee.Process.vala index 7ecd815..e3e39ee 100644 --- a/src/Utility/TeeJee.Process.vala +++ b/src/Utility/TeeJee.Process.vala @@ -302,6 +302,71 @@ namespace TeeJee.ProcessHelper{ return GLib.Path.get_basename(link); } + // get the parent pid of process or self + public Pid get_process_parent(Pid process = -1) { + string pidStr = (process <= 0 ? "self" : process.to_string()); + string path = "/proc/%s/stat".printf(pidStr); + string stats = file_read(path); + string details = stats.split(")", 2)[1]; + string[] splitted = details.split(" ", 3); + if(splitted.length == 3) { + return int.parse(splitted[2]); + } + + log_debug("can not parse process stat %s".printf(stats)); + return -1; + } + + // get the effective user pid of an process + // returns -1 on error + public int get_euid_of_process(Pid process) { + GLib.File file = GLib.File.new_for_path("/proc/%d".printf(process)); + try { + GLib.FileInfo info = file.query_info(FileAttribute.UNIX_UID, GLib.FileQueryInfoFlags.NONE); + return (int) info.get_attribute_uint32(FileAttribute.UNIX_UID); + } catch(GLib.Error e) { + log_debug("failed to fetch user of process %i %s".printf(process, e.message)); + } + return -1; + } + + // find the first parent process, that is owned by the user and not root + public Pid get_user_process() { + Pid ppid = -1; + int targetUser = TeeJee.System.get_user_id(); + int user = 0; + + do { + ppid = get_process_parent(ppid); + user = get_euid_of_process (ppid); + } while(user != targetUser && ppid > 1); + if(user == targetUser) { + return ppid; + } + return -1; + } + + // get the env of an process + public string[]? get_process_env(Pid pid) { + if(pid < 1) { + return null; + } + return file_read_array("/proc/%i/environ".printf(pid), '\0'); + } + + // get the value of name in env if it exists or return default_value + public string? get_env(string[] env, string name, string? default_value = null) { + foreach(string env_var in env) { + string[] splitted = env_var.split("=", 2); + if(splitted[0] == name) { + if (splitted.length == 2) { + return splitted[1]; + } + } + } + return default_value; + } + public Pid[] get_process_children (Pid parent_pid){ /* Returns the list of child processes owned by a given process */ diff --git a/src/timeshift-launcher b/src/timeshift-launcher index 1e4face..1d5c8a3 100755 --- a/src/timeshift-launcher +++ b/src/timeshift-launcher @@ -9,16 +9,16 @@ else # user is not admin if echo $- | grep "i" >/dev/null 2>&1; then # script is running in interactive mode - su - -c "pkexec env DISPLAY=$DISPLAY XAUTHORITY=$XAUTHORITY ${app_command}" + su - -c "${app_command}" else # script is running in non-interactive mode if [ "$XDG_SESSION_TYPE" = "wayland" ] ; then xhost +SI:localuser:root - pkexec env DISPLAY=$DISPLAY XAUTHORITY=$XAUTHORITY ${app_command} + pkexec "${app_command}" xhost -SI:localuser:root xhost elif command -v pkexec >/dev/null 2>&1; then - pkexec env DISPLAY=$DISPLAY XAUTHORITY=$XAUTHORITY ${app_command} + pkexec "${app_command}" elif command -v sudo >/dev/null 2>&1; then x-terminal-emulator -e "sudo ${app_command}" elif command -v su >/dev/null 2>&1; then