├── .gitignore
├── App.config
├── Program.cs
├── Properties
└── AssemblyInfo.cs
├── isadmin.ps1
├── sudo.csproj
├── sudo.sln
└── testscript.ps1
/.gitignore:
--------------------------------------------------------------------------------
1 | /bin/*
2 | /obj/*
3 | *.user
4 | *.suo
5 |
--------------------------------------------------------------------------------
/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using System.Reflection;
6 | using System.Runtime.InteropServices;
7 |
8 | namespace sudo {
9 |
10 | class Program {
11 | // elevating with cmd.exe instead of calling sudo.exe directly
12 | // gives a blue UAC instead of the yellow "unknown publisher" one.
13 | const bool ElevateWithCmd = false;
14 |
15 | [DllImport("kernel32.dll", SetLastError = true)]
16 | static extern bool AttachConsole(uint dwProcessId);
17 |
18 | [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
19 | static extern bool FreeConsole();
20 |
21 | static void Main(string[] args) {
22 | if(args.Length == 0) {
23 | Console.Error.WriteLine("usage: sudo ");
24 | Environment.Exit(1);
25 | }
26 |
27 | if(args[0] == "-do") {
28 | if(args.Length < 4) { Console.Error.Write("not enough arguments"); Environment.Exit(1); }
29 |
30 | var exit_code = Do(args[1], args[2], string.Join(" ", args.Skip(3)));
31 | Environment.Exit(exit_code);
32 | return;
33 | }
34 |
35 | var pid = Process.GetCurrentProcess().Id;
36 | var sudo_exe = Assembly.GetExecutingAssembly().Location;
37 | var pwd = Environment.CurrentDirectory;
38 |
39 | var elev_args = "-do \"" + pwd + "\" " + pid + " " + Serialize(args);
40 |
41 | var p = new Process();
42 | if(ElevateWithCmd) {
43 | p.StartInfo.FileName = "cmd";
44 | p.StartInfo.Arguments = "/s /c \"\"" + sudo_exe + "\" " + elev_args + "\"";
45 | } else {
46 | p.StartInfo.FileName = sudo_exe;
47 | p.StartInfo.Arguments = elev_args;
48 | }
49 | p.StartInfo.Verb = "runas";
50 | p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
51 |
52 | try {
53 | p.Start();
54 | } catch(Win32Exception) { Environment.Exit(1); } // user didn't provide consent
55 |
56 | p.WaitForExit();
57 | Environment.Exit(p.ExitCode);
58 | }
59 |
60 | static int Do(string dir, string parent_pid, string cmd) {
61 | uint pid;
62 | if(!uint.TryParse(parent_pid, out pid)) {
63 | Console.WriteLine("Couldn't get pid"); return 1;
64 | }
65 |
66 | FreeConsole();
67 | AttachConsole(pid);
68 |
69 | var p = new Process();
70 | var start = p.StartInfo;
71 | start.FileName = "powershell.exe";
72 | start.Arguments = "-noprofile " + cmd + "\nexit $lastexitcode";
73 | start.UseShellExecute = false;
74 | start.WorkingDirectory = dir;
75 |
76 | p.Start();
77 | p.WaitForExit();
78 | return p.ExitCode;
79 | }
80 |
81 | static string Serialize(string[] args) {
82 | return string.Join(" ", args.Select(a => a.Contains(' ') ? "'" + a + "'" : a));
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("sudo")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("sudo")]
13 | [assembly: AssemblyCopyright("Copyright © 2013")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("1b8dd3eb-bbd6-4ddd-b9a9-afd8d872ced1")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/isadmin.ps1:
--------------------------------------------------------------------------------
1 | $id = [Security.Principal.WindowsIdentity]::GetCurrent()
2 | write-host "is admin? " -nonewline
3 | ([Security.Principal.WindowsPrincipal]($id)).isinrole("Administrators").tostring().tolower()
--------------------------------------------------------------------------------
/sudo.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {24F20E15-A9C9-4EC1-875A-80E5F19029AA}
8 | Exe
9 | Properties
10 | sudo
11 | sudo
12 | v4.5
13 | 512
14 |
15 |
16 | AnyCPU
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | AnyCPU
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | Always
51 |
52 |
53 | PreserveNewest
54 |
55 |
56 |
57 |
64 |
--------------------------------------------------------------------------------
/sudo.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Express 2012 for Windows Desktop
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "sudo", "sudo.csproj", "{24F20E15-A9C9-4EC1-875A-80E5F19029AA}"
5 | EndProject
6 | Global
7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
8 | Debug|Any CPU = Debug|Any CPU
9 | Release|Any CPU = Release|Any CPU
10 | EndGlobalSection
11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
12 | {24F20E15-A9C9-4EC1-875A-80E5F19029AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
13 | {24F20E15-A9C9-4EC1-875A-80E5F19029AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
14 | {24F20E15-A9C9-4EC1-875A-80E5F19029AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
15 | {24F20E15-A9C9-4EC1-875A-80E5F19029AA}.Release|Any CPU.Build.0 = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(SolutionProperties) = preSolution
18 | HideSolutionNode = FALSE
19 | EndGlobalSection
20 | EndGlobal
21 |
--------------------------------------------------------------------------------
/testscript.ps1:
--------------------------------------------------------------------------------
1 | $name = read-host "enter your name"
2 | write-host "hi, $name" -f darkgreen
3 | for($i = 1; $i -lt 6; $i++) {
4 | write-host $i
5 | start-sleep -m 100
6 | }
7 |
8 | $id = [Security.Principal.WindowsIdentity]::GetCurrent()
9 | $admin = ([Security.Principal.WindowsPrincipal]($id)).isinrole("Administrators")
10 |
11 | "admin? $admin"
12 |
13 | write-error "error!"
14 |
15 | exit 123
--------------------------------------------------------------------------------