diff --git a/docs/index.html b/docs/index.html index 9891325..9063a1b 100644 --- a/docs/index.html +++ b/docs/index.html @@ -532,7 +532,7 @@

Table of contents

Run/RunWait
  • - RunAs + RunAs
  • Shutdown @@ -2506,8 +2506,8 @@

    The "Last Found" Window

    Runs an external program. - RunAs - Specifies a set of user credentials to use for all subsequent uses of Run and RunWait. 2000/XP or later ONLY. + RunAs + Specifies a set of user credentials to use for all subsequent uses of Run and RunWait. RunWait @@ -11074,15 +11074,15 @@

    Examples

    Run, mailto:support@autohotkey.com ; This should open the default e-mail application. -
    +
    - #

    RunAs

    [v1.0.08+] + #

    RunAs


    -

    Specifies a set of user credentials to use for all subsequent uses of Run and RunWait. 2000/XP or later ONLY.

    +

    Specifies a set of user credentials to use for all subsequent uses of Run and RunWait. Also works for graphical applications. Note: Using this command may lead to the specified password to appear in the task manager, visible to other programs.

    - +
    RunAs [, User, Password, Domain] RunAs [, User, Password, Domain]
    @@ -11096,9 +11096,9 @@

    Examples

    Password - User's password. + User's password. Must not be blank. - + Domain

    User's domain. To use a local account, leave this blank. If that fails to work, try using @YourComputerName.

    @@ -11108,11 +11108,10 @@

    Examples

     

    Remarks

    -

    Only Windows XP/2000 or later are supported; this command has no effect on other operating systems. NT4 users should install and use the SU command from the NT Resource Kit instead.

    This command does nothing other than notify AutoHotkey to use (or not use) alternate user credentials for all subsequent uses of Run and RunWait.

    ErrorLevel is not changed by this command. If an invalid User, Password, or Domain is specified, Run and RunWait will display an error message explaining the problem.

    While the RunAs feature is in effect, Run and RunWait will not able to launch documents, URLs, or system verbs. In other words, the file to be launched must be an executable file.

    -

    The "Secondary Logon" service must be set to manual or automatic for this command to work (the OS should automatically start it upon demand if set to manual).

    +

    The "Secondary Logon" service must be set to manual or automatic for this command to work (the OS should automatically start it upon demand if set to manual).

     

    Related

    Run, RunWait

    diff --git a/src/cmd/misc/run-as.cr b/src/cmd/misc/run-as.cr new file mode 100644 index 0000000..e3d865d --- /dev/null +++ b/src/cmd/misc/run-as.cr @@ -0,0 +1,9 @@ +# RunAs [, User, Password, Domain] +class Cmd::Misc::RunAs < Cmd::Base + def self.min_args; 0 end + def self.max_args; 3 end + def run(thread, args) + thread.runner.settings.run_as_user = args[0]? + thread.runner.settings.run_as_password = args[1]? + end +end \ No newline at end of file diff --git a/src/cmd/misc/run.cr b/src/cmd/misc/run.cr index ec47635..e92ecb8 100644 --- a/src/cmd/misc/run.cr +++ b/src/cmd/misc/run.cr @@ -3,6 +3,13 @@ class Cmd::Misc::Run < Cmd::Base def self.min_args; 1 end def self.max_args; 6 end @wait = false + private def args_to_str(args) + args.map { |a| arg_to_str(a) } + .join(' ') + end + private def arg_to_str(arg) + "'" + arg.gsub("'", "'\\''") + "'" + end # returns either `false` on failure or `i64` exit code or pid, depending on *@wait*. private def try_execute(thread, line, chdir = nil, stdout = false, stderr = false) args = Process.parse_arguments(line).map do |arg| @@ -11,22 +18,40 @@ class Cmd::Misc::Run < Cmd::Base end end return false, "", "" if ! args[0]? || ! ::Process.find_executable(args[0]) - {% if ! flag?(:release) %} - puts "[debug] runcmd[#{thread.id}]: cmd=#{args[0]} args=#{args[1..]}" - {% end %} chdir = nil if chdir && chdir.empty? stdout_m = stdout ? IO::Memory.new : Process::Redirect::Close stderr_m = stderr ? IO::Memory.new : Process::Redirect::Close - if @wait - cmd = args[0] - params = args[1..] + args_str = args_to_str(args) # (see below) + run_as_user = thread.runner.settings.run_as_user + run_as_password = thread.runner.settings.run_as_password + if run_as_user && run_as_password + # `sudo` asks for the *current* user's password which we don't want, so we have to + # use `su` instead. + # https://stackoverflow.com/a/3959957/3779853 + # Normally you'd use `Process.run(shell: false, stdin: IO::Memory.new(run_as_password)` but that + # somehow doesn't work: `Password: su: Authentication token manipulation error` so sadly we have + # to build our own shell cmd string to make use of Bash IO piping instead. + user_str = arg_to_str(run_as_user) + # We want to set `--login` so that vars like `$USER` etc are normalized for the user which is + # required for some programs. But that also means we have to set some X vars for it to work: + env_str = "DISPLAY=#{arg_to_str(ENV["DISPLAY"]?||"")} XAUTHORITY=#{arg_to_str(ENV["XAUTHORITY"]?||"")}" + args_str = "#{env_str} #{args_str}" + cmd = "su --login #{user_str} -c #{arg_to_str(args_str)} <<< #{arg_to_str(run_as_password)}" + # Cannot use pkexec because it doesn't allow running as other user without admin rights or + # gksu which isn't installed everywhere. + `xhost si:localuser:#{user_str}` else + cmd = args_str + end + if ! @wait # Neither `bash -c 'stuff & disown'` nor `nohup` nor `p.close` nor io close args # work to prevent the spawned process from quitting once our main process exits, # only setsid does. - cmd = "setsid" - params = args + cmd = "setsid #{cmd}" end + {% if ! flag?(:release) %} + puts "[debug] runcmd[#{thread.id}]: cmd=#{args[0]} args=#{args[1..]}, outcmd=#{cmd}" + {% end %} env = { # AppImage/linuxdeploy-plugin-gtk sets several env vars *for the main binary itself* @@ -38,11 +63,14 @@ class Cmd::Misc::Run < Cmd::Base "ahk_x11_LC_ALL_backup" => nil } begin - p = Process.new(cmd, params, chdir: chdir, output: stdout_m, error: stderr_m, env: env) + p = Process.new(cmd, shell: true, chdir: chdir, output: stdout_m, error: stderr_m, env: env) rescue e : IO::Error return false, "", "" end ret = @wait ? p.wait.exit_code.to_i64 : p.pid + if run_as_user && run_as_password + `xhost -si:localuser:#{user_str}` + end return ret, stdout_m.to_s, stderr_m.to_s end def run(thread, args) @@ -65,7 +93,7 @@ class Cmd::Misc::Run < Cmd::Base elsif target_raw.starts_with?("explore ") target_raw = target_raw[8..] target = target[8..] - edit = false + open = true elsif target_raw.starts_with?("print ") target_raw = "lp " + target_raw[6..] target = "lp " + target[6..] @@ -91,14 +119,16 @@ class Cmd::Misc::Run < Cmd::Base success, stdout, stderr = try_execute(thread, "gtk-launch '#{`xdg-mime query default text/plain`.strip}' '#{target_raw}'", chdir: pwd, stdout: !!output_stdout, stderr: !!output_stderr) end - thread.runner.display.gtk.act do - begin - if target.starts_with?("www.") - target = "http://#{target}" + if ! thread.runner.settings.run_as_user + thread.runner.display.gtk.act do + begin + if target.starts_with?("www.") + target = "http://#{target}" + end + # works for uris etc., but not for local files + success = ::Gio.app_info_launch_default_for_uri(target, nil) + rescue end - # works for uris etc., but not for local files - success = ::Gio.app_info_launch_default_for_uri(target, nil) - rescue end end diff --git a/src/run/runner.cr b/src/run/runner.cr index ac2f338..31105eb 100644 --- a/src/run/runner.cr +++ b/src/run/runner.cr @@ -24,6 +24,8 @@ module Run # for the dynamic creation of hotkeys. property max_threads_per_hotkey = 1_u8 property no_tray_icon = false + property run_as_user : String? = nil + property run_as_password : String? = nil end # can start a completely fresh and isolated ahk execution instance with its own