xref: /inferno-os/emu/Nt/cmd.c (revision 46439007cf417cbd9ac8049bb4122c890097a0fa)
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 *rpfd, int *wpfd)
108 {
109 	STARTUPINFO si;
110 	SECURITY_ATTRIBUTES sec;
111 	HANDLE rh, wh, srh, swh;
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 	if(!CreatePipe(&rh, &swh, &sec, 0)) {
130 		print("can't create pipe\n");
131 		free(cmd);
132 		free(wcmd);
133 		free(wdir);
134 		return nil;
135 	}
136 	if(!CreatePipe(&srh, &wh, &sec, 0)) {
137 		print("can't create pipe\n");
138 		CloseHandle(rh);
139 		CloseHandle(swh);
140 		free(cmd);
141 		free(wcmd);
142 		free(wdir);
143 		return nil;
144 	}
145 	rh = exporthandle(rh, 1);
146 	wh = exporthandle(wh, 1);
147 	if (rh == nil || wh == nil) {
148 		print("can't dup pipes\n");
149 		CloseHandle(rh);
150 		CloseHandle(swh);
151 		CloseHandle(wh);
152 		CloseHandle(srh);
153 		free(cmd);
154 		free(wcmd);
155 		free(wdir);
156 		return nil;
157 	}
158 
159 	memset(&si, 0, sizeof(si));
160 	si.cb = sizeof(si);
161 	si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
162 	si.wShowWindow = SW_SHOW;
163 	si.hStdInput = rh;
164 	si.hStdOutput = wh;
165 	si.hStdError = exporthandle(wh, 0);
166 
167 	prio = 0;
168 	if(nice){
169 		prio = IDLE_PRIORITY_CLASS;
170 		if(nice > 1)
171 			prio |= CREATE_SUSPENDED;
172 	}
173 
174 	/* default of nil for wpath seems to be what we want; nil for env exports our current one */
175 	if(!CreateProcess(nil/*wpath*/, wcmd, 0, 0, 1,
176 	   CREATE_NEW_PROCESS_GROUP|CREATE_DEFAULT_ERROR_MODE|prio,
177 	   0 /*env*/, wdir, &si, &pinfo)){
178 		print("can't create process '%Q' %d\n", wcmd, GetLastError());
179 		CloseHandle(si.hStdInput);
180 		CloseHandle(swh);
181 		CloseHandle(si.hStdOutput);
182 		CloseHandle(si.hStdError);
183 		CloseHandle(srh);
184 		free(cmd);
185 		free(wcmd);
186 		free(wdir);
187 		return nil;
188 	}
189 
190 	*rpfd = nth2fd(srh);
191 	*wpfd = nth2fd(swh);
192 	if(*wpfd == 1 || *wpfd == 2)
193 		panic("invalid mapping of handle to fd");
194 	CloseHandle(si.hStdInput);
195 	CloseHandle(si.hStdOutput);
196 	CloseHandle(si.hStdError);
197 
198 	if(prio & CREATE_SUSPENDED){
199 		if(nice > 1)
200 			SetThreadPriority(pinfo.hThread,
201 				nice>3? THREAD_PRIORITY_IDLE:
202 				nice>2? THREAD_PRIORITY_LOWEST:
203 				THREAD_PRIORITY_BELOW_NORMAL);
204 		ResumeThread(pinfo.hThread);
205 	}
206 	CloseHandle(pinfo.hThread);
207 	/* don't close process handle */
208 	free(cmd);
209 	free(wcmd);
210 	free(wdir);
211 	return pinfo.hProcess;
212 }
213 
214 int
215 oscmdwait(void *v, char *buf, int n)
216 {
217 	int status;
218 	HANDLE proc = (HANDLE)v;
219 
220 	/* need not worry about being interrupted */
221 	if(WaitForSingleObject(proc, INFINITE) == WAIT_FAILED)
222 		return -1;
223 	if(!GetExitCodeProcess(proc, &status))
224 		status = 1;
225 	if(status)
226 		n = snprint(buf, n, "0 0 0 0 'status %d'", status);
227 	else
228 		n = snprint(buf, n, "0 0 0 0 ''");
229 	return n;
230 
231 }
232 
233 int
234 oscmdkill(void *v)
235 {
236 	if(TerminateProcess((HANDLE)v, 666) == FALSE)
237 		return -1;
238 	return 0;
239 }
240 
241 void
242 oscmdfree(void *v)
243 {
244 	CloseHandle((HANDLE)v);
245 }
246