xref: /inferno-os/emu/Nt/cmd.c (revision 7ef44d652ae9e5e1f5b3465d73684e4a54de73c0)
1 #define Unknown win_Unknown
2 #define UNICODE
3 #include	<windows.h>
4 #include <winbase.h>
5 #include	<winsock.h>
6 #undef Unknown
7 #include	"dat.h"
8 #include	"fns.h"
9 #include	"error.h"
10 
11 extern int	nth2fd(HANDLE);
12 extern wchar_t	*widen(char*);
13 
14 /*
15  * thanks to rcsh for these.
16  *
17  * windows quoting rules - I think
18  * Words are separated by space or tab
19  * Words containing a space or tab can be quoted using "
20  * 2N backslashes + " ==> N backslashes and end quote
21  * 2N+1 backslashes + " ==> N backslashes + literal "
22  * N backslashes not followed by " ==> N backslashes
23  */
24 static char *
25 dblquote(char *cmd, char *s)
26 {
27 	int nb;
28 	char *p;
29 
30 	for(p=s; *p; p++)
31 		if(*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == '"')
32 			break;
33 
34 	if(*p == 0){				/* easy case */
35 		strcpy(cmd, s);
36 		return cmd+(p-s);
37 	}
38 
39 	*cmd++ = '"';
40 	for(;;) {
41 		for(nb=0; *s=='\\'; nb++)
42 			*cmd++ = *s++;
43 
44 		if(*s == 0) {			/* trailing backslashes -> 2N */
45 			while(nb-- > 0)
46 				*cmd++ = '\\';
47 			break;
48 		}
49 
50 		if(*s == '"') {			/* literal quote -> 2N+1 backslashes */
51 			while(nb-- > 0)
52 				*cmd++ = '\\';
53 			*cmd++ = '\\';		/* escape the quote */
54 		}
55 		*cmd++ = *s++;
56 	}
57 
58 	*cmd++ = '"';
59 	*cmd = 0;
60 
61 	return cmd;
62 }
63 
64 static char *
65 ntquotedcmd(char **argv)
66 {
67 	int i, n;
68 	char *cmd, *p;
69 
70 		/* conservatively calculate length of command;
71 		 * backslash expansion can cause growth in dblquote().
72 		 */
73 	for(i=0,n=0; argv[i]; i++)
74 		n += 2*strlen(argv[i]);
75 	n++;
76 
77 	cmd = malloc(n);
78 	if(cmd == nil)
79 		return nil;
80 	for(i=0,p=cmd; argv[i]; i++) {
81 		p = dblquote(p, argv[i]);
82 		*p++ = ' ';
83 	}
84 	if(p != cmd)
85 		p--;
86 	*p = 0;
87 
88 	return cmd;
89 }
90 
91 static HANDLE
92 exporthandle(HANDLE h, int close)
93 {
94 	HANDLE cp, dh;
95 	DWORD flags = DUPLICATE_SAME_ACCESS;
96 	if (close)
97 		flags |= DUPLICATE_CLOSE_SOURCE;
98 	cp = GetCurrentProcess();
99 	if (!DuplicateHandle(cp, h, cp, &dh, DUPLICATE_SAME_ACCESS, 1, flags))
100 		return nil;
101 	return dh;
102 }
103 
104 /* TO DO: check that oserrstr will have the right text on error */
105 
106 void*
107 oscmd(char **args, int nice, char *dir, int *fd)
108 {
109 	STARTUPINFO si;
110 	SECURITY_ATTRIBUTES sec;
111 	HANDLE rh, wh, eh, srh, swh, seh;
112 	PROCESS_INFORMATION pinfo;
113 	char *cmd;
114 	wchar_t *wcmd, *wdir;
115 	int prio;
116 
117 	wdir = nil;
118 	if(dir != nil)
119 		wdir = widen(dir);
120 
121 	cmd = ntquotedcmd(args);
122 	if(cmd == nil)
123 		error(Enomem);
124 
125 	wcmd = widen(cmd);
126 	sec.nLength = sizeof(sec);
127 	sec.lpSecurityDescriptor = 0;
128 	sec.bInheritHandle = 0;
129 	rh = wh = eh = srh = swh = seh = nil;
130 	if(!CreatePipe(&rh, &swh, &sec, 0))
131 		goto Error;
132 	if(!CreatePipe(&srh, &wh, &sec, 0))
133 		goto Error;
134 	if(!CreatePipe(&seh, &eh, &sec, 0))
135 		goto Error;
136 	rh = exporthandle(rh, 1);
137 	if(rh == nil)
138 		goto Error;
139 	wh = exporthandle(wh, 1);
140 	if(wh == nil)
141 		goto Error;
142 	eh = exporthandle(eh, 1);
143 	if(eh == nil)
144 		goto Error;
145 
146 	memset(&si, 0, sizeof(si));
147 	si.cb = sizeof(si);
148 	si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
149 	si.wShowWindow = SW_SHOW;
150 	si.hStdInput = rh;
151 	si.hStdOutput = wh;
152 	si.hStdError = eh;
153 
154 	prio = 0;
155 	if(nice){
156 		prio = IDLE_PRIORITY_CLASS;
157 		if(nice > 1)
158 			prio |= CREATE_SUSPENDED;
159 	}
160 
161 	/* default of nil for wpath seems to be what we want; nil for env exports our current one */
162 	if(!CreateProcess(nil/*wpath*/, wcmd, 0, 0, 1,
163 	   CREATE_NEW_PROCESS_GROUP|CREATE_DEFAULT_ERROR_MODE|prio,
164 	   0 /*env*/, wdir, &si, &pinfo)){
165 		//print("can't create process '%Q' %d\n", wcmd, GetLastError());
166 		goto Error;
167 	}
168 
169 	fd[0] = nth2fd(swh);
170 	fd[1] = nth2fd(srh);
171 	fd[2] = nth2fd(seh);
172 	if(fd[1] == 1 || fd[2] == 2)
173 		panic("invalid mapping of handle to fd");
174 	CloseHandle(si.hStdInput);
175 	CloseHandle(si.hStdOutput);
176 	CloseHandle(si.hStdError);
177 
178 	if(prio & CREATE_SUSPENDED){
179 		if(nice > 1)
180 			SetThreadPriority(pinfo.hThread,
181 				nice>3? THREAD_PRIORITY_IDLE:
182 				nice>2? THREAD_PRIORITY_LOWEST:
183 				THREAD_PRIORITY_BELOW_NORMAL);
184 		ResumeThread(pinfo.hThread);
185 	}
186 	CloseHandle(pinfo.hThread);
187 	/* don't close process handle */
188 	free(cmd);
189 	free(wcmd);
190 	free(wdir);
191 	return pinfo.hProcess;
192 
193 Error:
194 	if(rh)
195 		CloseHandle(rh);
196 	if(wh)
197 		CloseHandle(wh);
198 	if(eh)
199 		CloseHandle(eh);
200 	if(srh)
201 		CloseHandle(srh);
202 	if(swh)
203 		CloseHandle(swh);
204 	if(seh)
205 		CloseHandle(seh);
206 	free(cmd);
207 	free(wcmd);
208 	free(wdir);
209 	return nil;
210 }
211 
212 int
213 oscmdwait(void *v, char *buf, int n)
214 {
215 	int status;
216 	HANDLE proc = (HANDLE)v;
217 
218 	/* need not worry about being interrupted */
219 	if(WaitForSingleObject(proc, INFINITE) == WAIT_FAILED)
220 		return -1;
221 	if(!GetExitCodeProcess(proc, &status))
222 		status = 1;
223 	if(status)
224 		n = snprint(buf, n, "0 0 0 0 'status %d'", status);
225 	else
226 		n = snprint(buf, n, "0 0 0 0 ''");
227 	return n;
228 
229 }
230 
231 int
232 oscmdkill(void *v)
233 {
234 	if(TerminateProcess((HANDLE)v, 666) == FALSE)
235 		return -1;
236 	return 0;
237 }
238 
239 void
240 oscmdfree(void *v)
241 {
242 	CloseHandle((HANDLE)v);
243 }
244