xref: /inferno-os/emu/Nt/cmd.c (revision 9dc22068e29604f4b484e746112a9a4efe6fd57f)
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