Skip to content

Commit

Permalink
Add RunAs
Browse files Browse the repository at this point in the history
also works for graphical applications
  • Loading branch information
phil294 committed Jul 20, 2023
1 parent 6d72f6c commit 500f2d8
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 28 deletions.
21 changes: 10 additions & 11 deletions docs/index.html
Expand Up @@ -532,7 +532,7 @@ <h2>Table of contents </h2>
<a href="#Run.htm">Run/RunWait</a>
</li>
<li>
<a class="tbd" href="#RunAs.htm">RunAs</a>
<a href="#RunAs.htm">RunAs</a>
</li>
<li>
<a class="tbd" href="#Shutdown.htm">Shutdown</a>
Expand Down Expand Up @@ -2506,8 +2506,8 @@ <h2 class="calibre9"><span class="calibre23">The "Last Found" Window </span></h2
<td height="16" class="calibre4">Runs an external program.</td>
</tr>
<tr class="calibre3">
<td height="16" class="tbd calibre4"><a href="#RunAs.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">RunAs</a></td>
<td height="16" class="calibre4">Specifies a set of user credentials to use for all subsequent uses of <a href="#Run.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">Run</a> and <a href="#Run.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">RunWait</a>. 2000/XP or later ONLY.</td>
<td height="16" class="calibre4"><a href="#RunAs.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">RunAs</a></td>
<td height="16" class="calibre4">Specifies a set of user credentials to use for all subsequent uses of <a href="#Run.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">Run</a> and <a href="#Run.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">RunWait</a>.</td>
</tr>
<tr class="calibre3">
<td height="16" class="calibre4"><a href="#Run.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">RunWait</a></td>
Expand Down Expand Up @@ -11074,15 +11074,15 @@ <h2 id="Examples">Examples</h2>
Run, mailto:support@autohotkey.com ; This should open the default e-mail application.</pre>
</div>
</div>
<div class="calibreMain tbd">
<div class="calibreMain">
<div class="calibreEbookContent">
<a id="RunAs.htm" href="#RunAs.htm">#</a> <h2 class="calibre17">RunAs</h2> [v1.0.08+]
<a id="RunAs.htm" href="#RunAs.htm">#</a> <h2 class="calibre17">RunAs</h2>
<hr size="2" class="calibre24" />
<p class="calibre8">Specifies a set of user credentials to use for all subsequent uses of <a href="#Run.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">Run</a> and <a href="#Run.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">RunWait</a>. 2000/XP or later ONLY.</p>
<p class="calibre8">Specifies a set of user credentials to use for all subsequent uses of <a href="#Run.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">Run</a> and <a href="#Run.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">RunWait</a>. <span class="x11">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.</span></p>
<table cellspacing="5" width="100%" class="calibre28">
<tbody class="calibre2">
<tr class="calibre3">
<td height="48" class="calibre4">RunAs [, User, Password, Domain] </td>
<td height="48" class="calibre4">RunAs [, User, Password, <span class="rm">Domain</span>] </td>
</tr>
</tbody>
</table>
Expand All @@ -11096,9 +11096,9 @@ <h2 id="Examples">Examples</h2>
</tr>
<tr class="calibre3">
<td class="calibre4">Password</td>
<td class="calibre4"><em class="calibre21">User</em>'s password.</td>
<td class="calibre4"><em class="calibre21">User</em>'s password. <span class="x11">Must not be blank.</span></td>
</tr>
<tr class="calibre3">
<tr class="calibre3 rm">
<td class="calibre4">Domain</td>
<td class="calibre4">
<p class="calibre8"><em class="calibre21">User</em>'s domain. To use a local account, leave this blank. If that fails to work, try using @YourComputerName.</p>
Expand All @@ -11108,11 +11108,10 @@ <h2 id="Examples">Examples</h2>
</table>
<p class="calibre8"> </p>
<p class="calibre8"><strong class="calibre14">Remarks</strong></p>
<p class="calibre8">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.</p>
<p class="calibre8">This command does nothing other than notify AutoHotkey to use (or not use) alternate user credentials for all subsequent uses of <a href="#Run.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">Run</a> and <a href="#Run.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">RunWait</a>.</p>
<p class="calibre8"><a href="#ErrorLevel.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">ErrorLevel</a> is not changed by this command. If an invalid <em class="calibre21">User</em>, <em class="calibre21">Password</em>, or <em class="calibre21">Domain</em> is specified, <a href="#Run.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">Run</a> and <a href="#Run.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">RunWait</a> will display an error message explaining the problem.</p>
<p class="calibre8">While the RunAs feature is in effect, <a href="#Run.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">Run</a> and <a href="#Run.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">RunWait</a> will not able to launch documents, URLs, or system verbs. In other words, the file to be launched must be an executable file.</p>
<p class="calibre8"> 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).</p>
<p class="calibre8 rm"> 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).</p>
<p class="calibre8"> </p>
<p class="calibre8"><strong class="calibre14">Related</strong></p>
<p class="calibre8"><a href="#Run.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">Run</a>, <a href="#Run.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">RunWait</a> </p>
Expand Down
9 changes: 9 additions & 0 deletions 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
64 changes: 47 additions & 17 deletions src/cmd/misc/run.cr
Expand Up @@ -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|
Expand All @@ -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*
Expand All @@ -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)
Expand All @@ -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..]
Expand All @@ -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

Expand Down
2 changes: 2 additions & 0 deletions src/run/runner.cr
Expand Up @@ -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
Expand Down

0 comments on commit 500f2d8

Please sign in to comment.