|
|
1.1 paf 1: /** @file
2: Parser: program executing for different OS-es.
3:
4: Copyright(c) 2000,2001 ArtLebedev Group(http://www.artlebedev.com)
1.17 ! paf 5: Author: Alexander Petrosyan <paf@design.ru>(http://paf.design.ru)
1.1 paf 6:
1.17 ! paf 7: $Id: pa_exec.C,v 1.16 2001/10/30 15:08:19 paf Exp $
1.1 paf 8: */
9:
10: #include "pa_config_includes.h"
11:
12: #ifdef WIN32
13: # include <windows.h>
14: #else
15: # include <signal.h>
16: # include <sys/types.h>
17: # include <sys/wait.h>
18: #endif
19:
20: #include <stdio.h>
21: #include <errno.h>
22:
23: #include "pa_exec.h"
24: #include "pa_exception.h"
25: #include "pa_common.h"
26:
27:
28: #ifdef WIN32
29:
30: /// this func from http://www.ccas.ru/~posp/popov/spawn.htm
1.9 parser 31: static BOOL WINAPI CreateHiddenConsoleProcess(LPCTSTR szChildName,
1.1 paf 32: char *szEnv,
33: PROCESS_INFORMATION* ppi,
34: LPHANDLE phInWrite,
35: LPHANDLE phOutRead,
1.9 parser 36: LPHANDLE phErrRead)
1.1 paf 37: {
38: BOOL fCreated;
39: STARTUPINFO si;
40: SECURITY_ATTRIBUTES sa={0};
41: HANDLE hInRead;
42: HANDLE hOutWrite;
43: HANDLE hErrWrite;
44:
45: // Create pipes
46: // initialize security attributes for handle inheritance (for WinNT)
1.9 parser 47: sa.nLength=sizeof(sa);
48: sa.bInheritHandle=TRUE;
49: sa.lpSecurityDescriptor=NULL;
1.1 paf 50:
51: // create STDIN pipe
1.9 parser 52: if(!CreatePipe(&hInRead, phInWrite, &sa, 0))
1.1 paf 53: goto error;
54:
55: // create STDOUT pipe
1.9 parser 56: if(!CreatePipe(phOutRead, &hOutWrite, &sa, 0))
1.1 paf 57: goto error;
58:
59: // create STDERR pipe
1.9 parser 60: if(!CreatePipe(phErrRead, &hErrWrite, &sa, 0))
1.1 paf 61: goto error;
62:
63: // process startup information
1.9 parser 64: memset(&si, 0, sizeof(si));
65: si.cb=sizeof(si);
66: si.dwFlags=STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
1.1 paf 67: // child process' console must be hidden for Win95 compatibility
1.9 parser 68: si.wShowWindow=SW_HIDE;
1.1 paf 69: // assign "other" sides of pipes
1.9 parser 70: si.hStdInput=hInRead;
71: si.hStdOutput=hOutWrite;
72: si.hStdError=hErrWrite;
1.1 paf 73:
74: // Create a child process (suspended)
1.9 parser 75: fCreated=CreateProcess(NULL,
1.1 paf 76: (LPTSTR)szChildName,
77: NULL,
78: NULL,
79: TRUE,
1.9 parser 80: 0, //todo CREATE_NO_WINDOW,
1.1 paf 81: szEnv,
82: NULL,
83: &si,
1.9 parser 84: ppi);
1.1 paf 85:
1.9 parser 86: CloseHandle(hInRead);
87: CloseHandle(hOutWrite);
88: CloseHandle(hErrWrite);
1.1 paf 89:
1.9 parser 90: if(!fCreated)
1.1 paf 91: goto error;
92:
93: return TRUE;
94:
95: error:
1.9 parser 96: CloseHandle(*phInWrite);
97: CloseHandle(*phOutRead);
98: CloseHandle(*phErrRead);
1.1 paf 99:
100: return FALSE;
101: }
102:
1.2 paf 103: static void read_pipe(String& result, HANDLE hOutRead, const char *file_spec){
1.1 paf 104: while(true) {
105: char *buf=(char *)result.pool().malloc(MAX_STRING);
106: unsigned long size;
1.9 parser 107: ReadFile(hOutRead, buf, MAX_STRING, &size, NULL);
1.1 paf 108: if(!size)
109: break;
1.3 paf 110: result.APPEND_AS_IS(buf, size, file_spec, 0);
1.1 paf 111: }
112: }
113:
114:
115: static const char *buildCommand(Pool& pool,
116: const String& origin_string,
117: const char *file_spec_cstr, const Array *argv) {
1.8 parser 118: const char *result=file_spec_cstr;
1.4 paf 119: if(FILE *f=fopen(file_spec_cstr, "r")) {
1.1 paf 120: char buf[MAX_STRING];
121: size_t size=fread(buf, 1, MAX_STRING-1, f);
122: if(size>2) {
123: buf[size]=0;
124: if(strncmp(buf, "#!", 2)==0) {
1.4 paf 125: const char *begin=buf+2;
126: if(*begin==' ') // alx: were an old magic for some linux-es
127: begin++;
128: if(char *end=strchr(begin, '\n')) {
1.1 paf 129: String string(pool);
1.4 paf 130: string.APPEND_AS_IS(begin, end-begin,
1.1 paf 131: origin_string.origin().file, 0);
132: string << " " << file_spec_cstr;
133: if(argv)
134: for(int i=0; i<argv->size(); i++)
1.16 paf 135: string << argv->get_string(i)->cstr();
1.8 parser 136: result=string.cstr();
1.1 paf 137: }
138: }
139: }
140: fclose(f);
141: }
1.8 parser 142: if(argv) {
143: String buf(pool);
144: buf << result;
145: for(int i=0; i<argv->size(); i++) {
146: buf << " ";
147: buf << *argv->get_string(i);
148: }
149:
1.16 paf 150: result=buf.cstr();
1.8 parser 151: }
152:
153: return result;
1.1 paf 154: }
155:
156: #else
157:
158: static int execle_piped(const char *path,
1.8 parser 159: const char *arg1, const char *arg2,
160: const char *arg3, const char *arg4,
161: const char *arg5, const char *arg6,
162: const char *arg7, const char *arg8,
163: const char *arg9, const char *arg10,
1.1 paf 164: char * const env[],
165: int *pipe_in, int *pipe_out, int *pipe_err) {
166: int pid;
167: int in_fds[2];
168: int out_fds[2];
169: int err_fds[2];
170: int save_errno;
171:
172: if(pipe_in && pipe(in_fds)<0) {
173: save_errno=errno;
174: errno=save_errno;
175: return 0;
176: }
177:
178: if(pipe_out && pipe(out_fds)<0) {
179: save_errno=errno;
180: if(pipe_in) {
181: close(in_fds[0]); close(in_fds[1]);
182: }
183: errno=save_errno;
184: return 0;
185: }
186:
187: if(pipe_err && pipe(err_fds)<0) {
188: save_errno=errno;
189: if(pipe_in) {
190: close(in_fds[0]); close(in_fds[1]);
191: }
192: if(pipe_out) {
193: close(out_fds[0]); close(out_fds[1]);
194: }
195: errno=save_errno;
196: return 0;
197: }
198:
199: if((pid=fork())<0) {
200: save_errno=errno;
201: if(pipe_in) {
202: close(in_fds[0]); close(in_fds[1]);
203: }
204: if(pipe_out) {
205: close(out_fds[0]); close(out_fds[1]);
206: }
207: if(pipe_err) {
208: close(err_fds[0]); close(err_fds[1]);
209: }
210: errno=save_errno;
211: return 0;
212: }
213:
214: if(!pid) {
215: /* Child process */
216:
217: if(pipe_out) {
218: close(out_fds[0]);
219: dup2(out_fds[1], STDOUT_FILENO);
220: close(out_fds[1]);
221: }
222:
223: if(pipe_in) {
224: close(in_fds[1]);
225: dup2(in_fds[0], STDIN_FILENO);
226: close(in_fds[0]);
227: }
228:
229: if(pipe_err) {
230: close(err_fds[0]);
231: dup2(err_fds[1], STDERR_FILENO);
232: close(err_fds[1]);
233: }
234:
235: /* HP-UX SIGCHLD fix goes here, if someone will remind me what it is... */
236: signal(SIGCHLD, SIG_DFL); /* Was that it? */
237:
238: execle(path, arg1, arg2, arg3, arg4, arg5, NULL, env);
239: exit(-errno);
240: }
241:
242: /* Parent process */
243:
244: if(pipe_out) {
245: close(out_fds[1]);
246: *pipe_out=out_fds[0];
247: }
248:
249: if(pipe_in) {
250: close(in_fds[0]);
251: *pipe_in=in_fds[1];
252: }
253:
254: if(pipe_err) {
255: close(err_fds[1]);
256: *pipe_err=err_fds[0];
257: }
258:
259: return pid;
260: }
261:
262: static int get_exit_status(int pid) {
263: int status;
264: if(!waitpid(pid, &status, 0))
265: return -1;
266: return WIFEXITED(status) ?
267: WEXITSTATUS(status) : -2;
268: }
269:
1.2 paf 270: static void read_pipe(String& result, int file, const char *file_spec){
1.1 paf 271: while(true) {
1.2 paf 272: char *buf=(char *)result.pool().malloc(MAX_STRING);
1.1 paf 273: size_t size=read(file, buf, MAX_STRING);
274: if(!size)
275: break;
1.3 paf 276: result.APPEND_AS_IS(buf, size, file_spec, 0);
1.1 paf 277: }
278: }
279:
280: #endif
281:
282: static void append_env_pair(const Hash::Key& key, Hash::Val *value, void *info) {
283: #ifdef WIN32
284: String& string=*static_cast<String *>(info);
285:
286: string << key << "=" << *static_cast<String *>(value);
1.3 paf 287: string.APPEND_AS_IS("", 1, 0, 0); // zero byte
1.1 paf 288: #else
289: String string(key.pool());
290: string << key << "=" << *static_cast<String *>(value);
291:
1.2 paf 292: char ***env_ref=static_cast<char ***>(info);
293: **env_ref=string.cstr(); (*env_ref)++;
1.1 paf 294: #endif
295: }
296: int pa_exec(const String& file_spec,
297: const Hash *env,
298: const Array *argv,
299: const String& in, String& out, String& err) {
300: Pool& pool=file_spec.pool();
301:
302: #ifdef WIN32
303:
304: char pwd[MAX_STRING];
305: GetCurrentDirectory(sizeof(pwd), pwd);
1.12 parser 306: char *dir=file_spec.cstr(String::UL_FILE_SPEC);
1.1 paf 307: rsplit(dir, '/'); SetCurrentDirectory(dir);
308:
309: PROCESS_INFORMATION pi;
310: HANDLE hInWrite, hOutRead, hErrRead;
1.12 parser 311: char *file_spec_cstr=file_spec.cstr(String::UL_FILE_SPEC);
1.1 paf 312: const char *cmd=buildCommand(file_spec.pool(), file_spec, file_spec_cstr, argv);
313: char *env_cstr=0;
314: if(env) {
315: String string(env->pool());
316: env->for_each(append_env_pair, &string);
1.16 paf 317: env_cstr=string.cstr();
1.1 paf 318: }
1.9 parser 319: if(CreateHiddenConsoleProcess(cmd, env_cstr, &pi, &hInWrite, &hOutRead, &hErrRead)) {
1.1 paf 320: SetCurrentDirectory(pwd);
321:
1.16 paf 322: const char *in_cstr=in.cstr();
1.5 paf 323: DWORD written_size;
324: WriteFile(hInWrite, in_cstr, in.size(), &written_size, NULL);
325: // EOF for stupid text reads
326: // normally they should read CONTENT_LENGTH bytes,
327: // without this char
328: WriteFile(hInWrite, "\x1A", 1, &written_size, NULL);
1.9 parser 329: CloseHandle(hInWrite);
1.1 paf 330: read_pipe(out, hOutRead, file_spec_cstr);
1.9 parser 331: CloseHandle(hOutRead);
1.5 paf 332: read_pipe(err, hErrRead, file_spec_cstr);
1.9 parser 333: CloseHandle(hErrRead);
1.1 paf 334: /*
335: from http://www.apache.org/websrc/cvsweb.cgi/apache-1.3/src/main/util_script.c?rev=1.151&content-type=text/vnd.viewcvs-markup
336:
337: * We must close the handles to the new process and its main thread
338: * to prevent handle and memory leaks.
339: */
340: CloseHandle(pi.hProcess);
341: CloseHandle(pi.hThread);
342: } else {
343: SetCurrentDirectory(pwd);
344:
345: DWORD error=GetLastError();
346: char szErrorDesc[MAX_STRING];
347: FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error,
348: MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
349: szErrorDesc, sizeof(szErrorDesc), NULL);
350: size_t error_size=strlen(szErrorDesc);
351: if(error_size>3) // ".\r\n"
352: szErrorDesc[error_size-3]=0;
353:
1.15 parser 354: throw Exception(0, 0,
1.1 paf 355: &file_spec,
1.10 parser 356: "(real filename=\"%s\") exec failed - %s (%ld)",
1.11 parser 357: cmd,
1.10 parser 358: szErrorDesc, (long)error);
1.1 paf 359: }
360:
361: #else
362:
363: int pipe_write, pipe_read, pipe_err;
1.2 paf 364: const char *argv_cstrs[5]={"", "", "", "", ""};
1.1 paf 365: if(argv) {
366: int size=min(5, argv->size());
367: for(int i=0; i<size; i++)
1.16 paf 368: argv_cstrs[i]=argv->get_string(i)->cstr();
1.1 paf 369: }
1.12 parser 370: const char *file_spec_cstr=file_spec.cstr(String::UL_FILE_SPEC);
1.2 paf 371: char **env_cstrs=0;
1.1 paf 372: if(env) {
1.2 paf 373: env_cstrs=
1.1 paf 374: (char **)env->pool().malloc(sizeof(char *)*(env->size()+1/*0*/));
1.2 paf 375: char **env_ref=env_cstrs;
376: env->for_each(append_env_pair, &env_ref);
377: *env_ref=0;
1.1 paf 378: }
1.2 paf 379: if(int pid=execle_piped(
1.1 paf 380: file_spec_cstr,
1.2 paf 381: argv_cstrs[0], argv_cstrs[1], argv_cstrs[2], argv_cstrs[3], argv_cstrs[4],
1.8 parser 382: argv_cstrs[5], argv_cstrs[6], argv_cstrs[7], argv_cstrs[8], argv_cstrs[9],
1.2 paf 383: env_cstrs,
1.1 paf 384: &pipe_write, &pipe_read, &pipe_err)) {
385:
1.16 paf 386: const char *in_cstr=in.cstr();
1.1 paf 387: write(pipe_write, in_cstr, in.size());
388: close(pipe_write);
389: read_pipe(out, pipe_read, file_spec_cstr);
1.5 paf 390: close(pipe_read);
1.1 paf 391: read_pipe(err, pipe_err, file_spec_cstr);
392: close(pipe_err);
393:
394: return get_exit_status(pid); // negative may mean "-errno[execl()]"
395: } else
1.15 parser 396: throw Exception(0, 0,
1.2 paf 397: &file_spec,
1.1 paf 398: "pipe error");
399: #endif
400:
401: return 0;
402: }