Annotation of parser3/src/main/pa_exec.C, revision 1.83
1.1 paf 1: /** @file
2: Parser: program executing for different OS-es.
3:
1.82 moko 4: Copyright (c) 2001-2012 Art. Lebedev Studio (http://www.artlebedev.com)
1.25 paf 5: Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
1.1 paf 6:
1.18 paf 7: @todo setrlimit
1.1 paf 8: */
1.38 paf 9:
1.1 paf 10: #include "pa_config_includes.h"
11:
1.19 paf 12: #include "pa_exec.h"
13: #include "pa_exception.h"
14: #include "pa_common.h"
15:
1.83 ! moko 16: volatile const char * IDENT_PA_EXEC_C="$Id: pa_exec.C,v 1.82 2012/03/16 09:24:13 moko Exp $" IDENT_PA_EXEC_H;
1.82 moko 17:
1.1 paf 18: #ifdef WIN32
19:
1.83 ! moko 20: #include <windows.h>
1.1 paf 21:
22: /// this func from http://www.ccas.ru/~posp/popov/spawn.htm
1.36 paf 23: static DWORD CreateHiddenConsoleProcess(LPCTSTR szCmdLine,
1.65 paf 24: LPCTSTR szScriptFileSpec,
1.51 paf 25: char *szEnv,
1.73 misha 26: PROCESS_INFORMATION* ppi,
27: LPHANDLE phInWrite,
28: LPHANDLE phOutRead,
29: LPHANDLE phErrRead)
1.1 paf 30: {
1.51 paf 31: DWORD result=0;
1.36 paf 32: BOOL fCreated;
1.51 paf 33: STARTUPINFO si;
34: SECURITY_ATTRIBUTES sa={0};
35: HANDLE hInRead;
36: HANDLE hOutWrite;
37: HANDLE hErrWrite;
38:
39: // Create pipes
40: // initialize security attributes for handle inheritance (for WinNT)
41: sa.nLength=sizeof(sa);
42: sa.bInheritHandle=TRUE;
43: sa.lpSecurityDescriptor=NULL;
44:
45: // create STDIN pipe
46: if(!CreatePipe(&hInRead, phInWrite, &sa, 0))
47: goto error;
48:
49: // create STDOUT pipe
50: if(!CreatePipe(phOutRead, &hOutWrite, &sa, 0))
51: goto error;
52:
53: // create STDERR pipe
54: if(!CreatePipe(phErrRead, &hErrWrite, &sa, 0))
55: goto error;
56:
57: // process startup information
58: memset(&si, 0, sizeof(si));
59: si.cb=sizeof(si);
60: si.dwFlags=STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
61: // child process' console must be hidden for Win95 compatibility
62: si.wShowWindow=SW_HIDE;
63: // assign "other" sides of pipes
64: si.hStdInput=hInRead;
65: si.hStdOutput=hOutWrite;
66: si.hStdError=hErrWrite;
67:
1.31 paf 68: // calculating script's directory
69: char dir[MAX_STRING];
1.65 paf 70: strncpy(dir, szScriptFileSpec, MAX_STRING-1); dir[MAX_STRING-1]=0;
1.33 paf 71: lsplit(dir,' '); // trim arguments
1.31 paf 72: rsplit(dir,'/'); rsplit(dir,'\\'); // trim filename
1.51 paf 73:
74: // Create a child process (suspended)
75: fCreated=CreateProcess(NULL,
76: (LPTSTR)szCmdLine,
77: NULL,
78: NULL,
79: TRUE,
80: CREATE_NO_WINDOW,
81: szEnv,
82: dir,
83: &si,
84: ppi);
1.36 paf 85: if(!fCreated)
86: result=GetLastError();
1.51 paf 87:
88: CloseHandle(hInRead);
89: CloseHandle(hOutWrite);
90: CloseHandle(hErrWrite);
91:
92: if(!fCreated)
93: goto error;
94:
95: return result;
96:
1.1 paf 97: error:
1.36 paf 98: if(!result/*yet*/)
99: result=GetLastError(); // get it
1.51 paf 100:
101: CloseHandle(*phInWrite);
102: CloseHandle(*phOutRead);
103: CloseHandle(*phErrRead);
104:
105: return result;
106: }
1.36 paf 107:
1.51 paf 108: static void read_pipe(String& result, HANDLE hOutRead, String::Language lang){
109: while(true) {
1.77 misha 110: char *buf=new(PointerFreeGC) char[MAX_STRING+1];
1.78 misha 111: DWORD size=0;
1.77 misha 112: if(!ReadFile(hOutRead, buf, MAX_STRING, &size, NULL) || !size)
1.51 paf 113: break;
114: buf[size]=0;
115: result.append_know_length(buf, size, lang);
116: }
1.1 paf 117: }
118:
1.70 misha 119: static void read_pipe(File_read_result& result, HANDLE hOutRead){
1.71 misha 120:
1.77 misha 121: char *buf=(char*)pa_malloc(MAX_STRING+1);
122: DWORD bufsize = MAX_STRING;
1.70 misha 123:
124: result.headers = 0;
125: result.length = 0;
126: result.str = 0;
127: result.success = false;
128:
129: while(true) {
1.77 misha 130: DWORD size=0;
131: if(!ReadFile(hOutRead, buf + result.length, bufsize - result.length, &size, NULL) || !size)
1.70 misha 132: break;
1.77 misha 133: result.length += size;
134: if(result.length >= bufsize){
135: bufsize *= 2;
136: buf=(char*)pa_realloc(buf, bufsize+1);
1.70 misha 137: }
1.77 misha 138: result.str=buf;
1.70 misha 139: }
140: }
141:
1.51 paf 142: static const char* buildCommand(const char* file_spec_cstr, const ArrayString& argv) {
143: const char* result=file_spec_cstr;
1.4 paf 144: if(FILE *f=fopen(file_spec_cstr, "r")) {
1.51 paf 145: try {
1.1 paf 146: char buf[MAX_STRING];
147: size_t size=fread(buf, 1, MAX_STRING-1, f);
148: if(size>2) {
149: buf[size]=0;
150: if(strncmp(buf, "#!", 2)==0) {
1.51 paf 151: const char* begin=buf+2;
1.65 paf 152: while(*begin==' ') // alx: were an old magic for some linux-es
1.4 paf 153: begin++;
1.68 paf 154: if(const char *end=strchr(begin, '\n')) {
1.51 paf 155: String string(pa_strdup(begin, end-begin));
1.1 paf 156: string << " " << file_spec_cstr;
1.8 parser 157: result=string.cstr();
1.1 paf 158: }
159: }
160: }
1.51 paf 161: } catch(...) {
162: fclose(f);
163: rethrow;
164: }
1.1 paf 165: fclose(f);
166: }
1.51 paf 167: { // appending argv
168: String string(result);
169: for(size_t i=0; i<argv.count(); i++) {
170: string << " ";
171: string << *argv[i];
1.8 parser 172: }
173:
1.51 paf 174: result=string.cstr();
1.8 parser 175: }
176:
177: return result;
1.1 paf 178: }
179:
1.51 paf 180: #else
1.1 paf 181:
1.51 paf 182: static pid_t execve_piped(const char* file_spec_cstr,
1.27 paf 183: char * const argv[], char * const env[],
184: int *pipe_in, int *pipe_out, int *pipe_err) {
1.46 paf 185: pid_t pid;
1.1 paf 186: int in_fds[2];
187: int out_fds[2];
188: int err_fds[2];
189: int save_errno;
190:
191: if(pipe_in && pipe(in_fds)<0) {
192: save_errno=errno;
193: errno=save_errno;
194: return 0;
195: }
196:
197: if(pipe_out && pipe(out_fds)<0) {
198: save_errno=errno;
199: if(pipe_in) {
200: close(in_fds[0]); close(in_fds[1]);
201: }
202: errno=save_errno;
203: return 0;
204: }
205:
206: if(pipe_err && pipe(err_fds)<0) {
207: save_errno=errno;
208: if(pipe_in) {
209: close(in_fds[0]); close(in_fds[1]);
210: }
211: if(pipe_out) {
212: close(out_fds[0]); close(out_fds[1]);
213: }
214: errno=save_errno;
215: return 0;
216: }
217:
218: if((pid=fork())<0) {
219: save_errno=errno;
220: if(pipe_in) {
221: close(in_fds[0]); close(in_fds[1]);
222: }
223: if(pipe_out) {
224: close(out_fds[0]); close(out_fds[1]);
225: }
226: if(pipe_err) {
227: close(err_fds[0]); close(err_fds[1]);
228: }
229: errno=save_errno;
1.46 paf 230: return -1;
1.1 paf 231: }
232:
233: if(!pid) {
234: /* Child process */
235:
236: if(pipe_out) {
237: close(out_fds[0]);
238: dup2(out_fds[1], STDOUT_FILENO);
239: close(out_fds[1]);
240: }
241:
242: if(pipe_in) {
243: close(in_fds[1]);
244: dup2(in_fds[0], STDIN_FILENO);
245: close(in_fds[0]);
246: }
247:
248: if(pipe_err) {
249: close(err_fds[0]);
250: dup2(err_fds[1], STDERR_FILENO);
251: close(err_fds[1]);
252: }
253:
1.42 paf 254: /* grabbed this from Apache source: */
255: /* HP-UX SIGCHLD fix goes here, if someone will remind me what it is... */
1.1 paf 256: signal(SIGCHLD, SIG_DFL); /* Was that it? */
257:
1.31 paf 258: // chdir to script's directory
259: char dir[MAX_STRING];
260: strncpy(dir, file_spec_cstr, MAX_STRING-1); dir[MAX_STRING-1]=0;
261: rsplit(dir,'/'); // trim filename
262: chdir(dir);
263:
264: // execute
265: execve(file_spec_cstr, argv, env);
1.1 paf 266: exit(-errno);
267: }
268:
269: /* Parent process */
270:
271: if(pipe_out) {
272: close(out_fds[1]);
273: *pipe_out=out_fds[0];
274: }
275:
276: if(pipe_in) {
277: close(in_fds[0]);
278: *pipe_in=in_fds[1];
279: }
280:
281: if(pipe_err) {
282: close(err_fds[1]);
283: *pipe_err=err_fds[0];
284: }
285:
286: return pid;
287: }
288:
289: static int get_exit_status(int pid) {
290: int status;
1.58 paf 291: pid_t cid;
292: while ((cid=waitpid(pid, &status, WUNTRACED)) == -1 && errno == EINTR);
293: if(!cid)
1.1 paf 294: return -1;
295: return WIFEXITED(status) ?
296: WEXITSTATUS(status) : -2;
297: }
298:
1.51 paf 299: static void read_pipe(String& result, int file, String::Language lang){
1.1 paf 300: while(true) {
1.77 misha 301: char *buf=new(PointerFreeGC) char[MAX_STRING+1];
302: ssize_t length=read(file, buf, MAX_STRING);
1.51 paf 303: if(length<=0)
1.49 paf 304: break;
1.51 paf 305: buf[length]=0;
306: result.append_know_length(buf, length, lang);
307: }
1.1 paf 308: }
309:
1.70 misha 310: static void read_pipe(File_read_result& result, int file){
1.77 misha 311: char *buf=(char*)pa_malloc(MAX_STRING+1);
312: ssize_t bufsize = MAX_STRING;
1.70 misha 313:
314: result.headers = 0;
315: result.length = 0;
316: result.str = 0;
317: result.success = false;
318:
319: while(true) {
1.77 misha 320: ssize_t size=read(file, buf + result.length, bufsize - result.length);
1.70 misha 321: if(size <= 0)
322: break;
1.77 misha 323: result.length += size;
324: if(result.length >= bufsize){
325: bufsize *= 2;
326: buf=(char*)pa_realloc(buf, bufsize+1);
1.70 misha 327: }
1.77 misha 328: result.str=buf;
1.70 misha 329: }
330: }
331:
1.51 paf 332: #endif
1.1 paf 333:
1.51 paf 334: #ifndef DOXYGEN
335: struct Append_env_pair_info {
1.1 paf 336: #ifdef WIN32
1.62 paf 337: String::Body& body;
338: Append_env_pair_info(String::Body& abody): body(abody) {}
1.51 paf 339: #else
340: char **env_ref;
341: #endif
342: };
343: #endif
1.83 ! moko 344:
1.75 misha 345: ///@test maybe here and at argv construction --- untaint_cstr(String::L_AS_IS
1.51 paf 346: static void append_env_pair(HashStringString::key_type key, HashStringString::value_type value,
347: Append_env_pair_info *info) {
348: #ifdef WIN32
1.62 paf 349: info->body << key << "=" << value;
350: info->body.append_know_length("\1", 1); // placeholder for of zero byte
1.1 paf 351: #else
1.52 paf 352: String::Body body;
1.63 paf 353: body << key << "=" << value.cstr();
1.1 paf 354:
1.61 paf 355: *(info->env_ref++)=body.cstrm();
1.1 paf 356: #endif
357: }
1.31 paf 358:
1.51 paf 359: PA_exec_result pa_exec(
1.81 misha 360: bool forced_allow,
1.32 paf 361: const String& file_spec,
1.51 paf 362: const HashStringString* env,
363: const ArrayString& argv,
364: String& in) {
365: PA_exec_result result;
1.1 paf 366:
1.28 paf 367: #ifdef NO_PA_EXECS
1.32 paf 368: if(!forced_allow)
1.69 misha 369: throw Exception(PARSER_RUNTIME,
1.32 paf 370: &file_spec,
371: "parser execs are disabled [recompile parser without --disable-execs configure option]");
372: #endif
1.28 paf 373:
1.1 paf 374: #ifdef WIN32
375:
376: PROCESS_INFORMATION pi;
377: HANDLE hInWrite, hOutRead, hErrRead;
1.75 misha 378: const char* script_spec_cstr=file_spec.taint_cstr(String::L_FILE_SPEC);
1.65 paf 379: const char* cmd=buildCommand(script_spec_cstr, argv);
1.53 paf 380: char* env_cstr=0;
1.1 paf 381: if(env) {
1.62 paf 382: String::Body body;
383: Append_env_pair_info info(body);
1.68 paf 384: env->for_each<Append_env_pair_info*>(append_env_pair, &info);
1.62 paf 385: env_cstr=info.body.cstrm();
1.51 paf 386: for(char* replacer=env_cstr; *replacer; replacer++)
387: if(*replacer=='\1')
388: *replacer=0;
1.1 paf 389: }
1.65 paf 390: if(DWORD error=CreateHiddenConsoleProcess(cmd, script_spec_cstr, env_cstr, &pi, &hInWrite, &hOutRead, &hErrRead)) {
1.36 paf 391: char szErrorDesc[MAX_STRING];
1.51 paf 392: const char* param="the file you tried to run";
1.36 paf 393: size_t error_size=FormatMessage(
394: FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_ARGUMENT_ARRAY , NULL, error,
395: MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
1.51 paf 396: szErrorDesc, sizeof(szErrorDesc), (va_list *)¶m);
1.36 paf 397: if(error_size>3) // ".\r\n"
398: szErrorDesc[error_size-3]=0;
1.73 misha 399:
1.72 misha 400: throw Exception("file.execute",
1.36 paf 401: &file_spec,
1.56 paf 402: "exec failed - %s (%u). Consider adding shbang line (#!x:\\interpreter\\command line)",
403: error_size?szErrorDesc:"<unknown>", error);
1.36 paf 404: } else {
1.51 paf 405: const char* in_cstr=in.cstr();
1.5 paf 406: DWORD written_size;
1.51 paf 407: WriteFile(hInWrite, in_cstr, in.length(), &written_size, NULL);
1.5 paf 408: // EOF for stupid text reads
409: // normally they should read CONTENT_LENGTH bytes,
410: // without this char
411: WriteFile(hInWrite, "\x1A", 1, &written_size, NULL);
1.9 parser 412: CloseHandle(hInWrite);
1.70 misha 413: read_pipe(result.out, hOutRead);
1.9 parser 414: CloseHandle(hOutRead);
1.83 ! moko 415: read_pipe(result.err, hErrRead, String::L_TAINTED);
1.9 parser 416: CloseHandle(hErrRead);
1.83 ! moko 417: // We must close the handles to the new process and its main thread
! 418: // to prevent handle and memory leaks.
1.1 paf 419: CloseHandle(pi.hProcess);
1.73 misha 420: CloseHandle(pi.hThread);
1.1 paf 421: }
422:
423: #else
1.53 paf 424:
425: // execve needs non const
1.75 misha 426: char* file_spec_cstr=file_spec.taint_cstrm(String::L_FILE_SPEC);
1.1 paf 427:
428: int pipe_write, pipe_read, pipe_err;
1.31 paf 429:
1.32 paf 430: if(!forced_allow) {
431: struct stat finfo;
432: if(stat(file_spec_cstr, &finfo)!=0)
1.34 paf 433: throw Exception("file.missing",
1.51 paf 434: &file_spec,
435: "stat failed: %s (%d), actual filename '%s'",
436: strerror(errno), errno, file_spec_cstr);
1.31 paf 437:
1.50 paf 438: check_safe_mode(finfo, file_spec, file_spec_cstr);
1.32 paf 439: }
1.31 paf 440:
1.76 misha 441: char* argv_cstrs[1+100+1]={file_spec_cstr, 0};
1.51 paf 442: const int argv_size=argv.count();
443: const int argv_max=sizeof(argv_cstrs)/sizeof(argv_cstrs[0])-1-1;
444: if(argv_size>argv_max)
1.69 misha 445: throw Exception(PARSER_RUNTIME,
1.51 paf 446: &file_spec,
447: "too many arguments (%d > max %d)", argv_size, argv_max);
448: for(int i=0; i<argv_size; i++)
449: argv_cstrs[1+i]=argv[i]->cstrm();
450: argv_cstrs[1+argv_size]=0;
451:
452: char **env_cstrs;
1.1 paf 453: if(env) {
1.51 paf 454: env_cstrs=new(PointerFreeGC) char *[env->count()+1/*0*/];
455: Append_env_pair_info info={env_cstrs};
456: env->for_each(append_env_pair, &info);
457: *info.env_ref=0;
458: } else
459: env_cstrs=0;
1.31 paf 460:
1.46 paf 461: pid_t pid=execve_piped(
1.1 paf 462: file_spec_cstr,
1.27 paf 463: argv_cstrs, env_cstrs,
1.22 paf 464: &pipe_write, &pipe_read, &pipe_err);
1.46 paf 465: if(pid>0) {
1.21 paf 466: // in child
1.73 misha 467: if(!in.is_empty()) {// there is some in data
1.51 paf 468: const char* in_cstr=in.cstr();
469: write(pipe_write, in_cstr, in.length());
470: }
1.1 paf 471: close(pipe_write);
1.70 misha 472: read_pipe(result.out, pipe_read);
1.5 paf 473: close(pipe_read);
1.51 paf 474: read_pipe(result.err, pipe_err, String::L_TAINTED);
1.1 paf 475: close(pipe_err);
476:
1.51 paf 477: result.status=get_exit_status(pid); // negative may mean "-errno[execl()]"
1.59 paf 478: } else {
479: const char* str=strerror(errno);
1.72 misha 480: throw Exception("file.execute",
1.2 paf 481: &file_spec,
1.59 paf 482: "%s error: %s (%d)", pid<0?"fork":"pipe", str?str:"<unknown>", errno);
483: }
1.1 paf 484: #endif
485:
1.51 paf 486: return result;
1.1 paf 487: }
E-mail: