1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 6 |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1997-2009 The PHP Group |
6 : +----------------------------------------------------------------------+
7 : | This source file is subject to version 3.01 of the PHP license, |
8 : | that is bundled with this package in the file LICENSE, and is |
9 : | available through the world-wide-web at the following url: |
10 : | http://www.php.net/license/3_01.txt |
11 : | If you did not receive a copy of the PHP license and are unable to |
12 : | obtain it through the world-wide-web, please send a note to |
13 : | license@php.net so we can mail you a copy immediately. |
14 : +----------------------------------------------------------------------+
15 : | Author: Rasmus Lerdorf <rasmus@php.net> |
16 : | Ilia Alshanetsky <iliaa@php.net> |
17 : +----------------------------------------------------------------------+
18 : */
19 : /* $Id: exec.c 289624 2009-10-14 01:32:07Z iliaa $ */
20 :
21 : #include <stdio.h>
22 : #include "php.h"
23 : #include <ctype.h>
24 : #include "php_string.h"
25 : #include "ext/standard/head.h"
26 : #include "ext/standard/file.h"
27 : #include "basic_functions.h"
28 : #include "exec.h"
29 : #include "php_globals.h"
30 : #include "SAPI.h"
31 :
32 : #if HAVE_SYS_WAIT_H
33 : #include <sys/wait.h>
34 : #endif
35 : #if HAVE_SIGNAL_H
36 : #include <signal.h>
37 : #endif
38 :
39 : #if HAVE_SYS_TYPES_H
40 : #include <sys/types.h>
41 : #endif
42 : #if HAVE_SYS_STAT_H
43 : #include <sys/stat.h>
44 : #endif
45 : #if HAVE_FCNTL_H
46 : #include <fcntl.h>
47 : #endif
48 :
49 : #if HAVE_NICE && HAVE_UNISTD_H
50 : #include <unistd.h>
51 : #endif
52 :
53 : /* {{{ php_exec
54 : * If type==0, only last line of output is returned (exec)
55 : * If type==1, all lines will be printed and last lined returned (system)
56 : * If type==2, all lines will be saved to given array (exec with &$array)
57 : * If type==3, output will be printed binary, no lines will be saved or returned (passthru)
58 : *
59 : * Unicode command strings are encoding using filesystem_encoding, returned data is not decoded back to unicode
60 : */
61 : PHPAPI int php_exec(int type, char *cmd, zval *array, zval *return_value TSRMLS_DC)
62 18 : {
63 : FILE *fp;
64 : char *buf;
65 : int l, pclose_return;
66 18 : char *cmd_p, *b, *d=NULL;
67 : php_stream *stream;
68 18 : size_t buflen, bufl = 0;
69 : #if PHP_SIGCHILD
70 : void (*sig_handler)() = NULL;
71 : #endif
72 :
73 18 : cmd_p = cmd;
74 :
75 : #if PHP_SIGCHILD
76 : sig_handler = signal (SIGCHLD, SIG_DFL);
77 : #endif
78 :
79 : #ifdef PHP_WIN32
80 : fp = VCWD_POPEN(cmd_p, "rb");
81 : #else
82 18 : fp = VCWD_POPEN(cmd_p, "r");
83 : #endif
84 18 : if (!fp) {
85 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to fork [%s]", cmd);
86 0 : goto err;
87 : }
88 :
89 18 : stream = php_stream_fopen_from_pipe(fp, "rb");
90 :
91 18 : buf = (char *) emalloc(EXEC_INPUT_BUF);
92 18 : buflen = EXEC_INPUT_BUF;
93 :
94 18 : if (type != 3) {
95 4 : b = buf;
96 :
97 30 : while (php_stream_get_line(stream, ZSTR(b), EXEC_INPUT_BUF, &bufl)) {
98 : /* no new line found, let's read some more */
99 22 : if (b[bufl - 1] != '\n' && !php_stream_eof(stream)) {
100 8 : if (buflen < (bufl + (b - buf) + EXEC_INPUT_BUF)) {
101 4 : bufl += b - buf;
102 4 : buflen = bufl + EXEC_INPUT_BUF;
103 4 : buf = erealloc(buf, buflen);
104 4 : b = buf + bufl;
105 : } else {
106 4 : b += bufl;
107 : }
108 8 : continue;
109 14 : } else if (b != buf) {
110 3 : bufl += b - buf;
111 : }
112 :
113 14 : if (type == 1) {
114 : int ob_level;
115 0 : PHPWRITE(buf, bufl);
116 0 : if ((php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_GET_LEVEL, &ob_level TSRMLS_CC) == SUCCESS) && ob_level < 1) {
117 0 : sapi_flush(TSRMLS_C);
118 : }
119 14 : } else if (type == 2) {
120 : /* strip trailing whitespaces */
121 14 : l = bufl;
122 28 : while (l-- && isspace(((unsigned char *)buf)[l]));
123 14 : if (l != (bufl - 1)) {
124 14 : bufl = l + 1;
125 14 : buf[bufl] = '\0';
126 : }
127 14 : add_next_index_stringl(array, buf, bufl, 1);
128 : }
129 14 : b = buf;
130 : }
131 4 : if (bufl) {
132 : /* strip trailing whitespaces if we have not done so already */
133 0 : if ((type == 2 && bufl && !l) || type != 2) {
134 0 : l = bufl;
135 0 : while (l-- && isspace(((unsigned char *)buf)[l]));
136 0 : if (l != (bufl - 1)) {
137 0 : bufl = l + 1;
138 0 : buf[bufl] = '\0';
139 : }
140 0 : if (type == 2) {
141 0 : add_next_index_stringl(array, buf, bufl, 1);
142 : }
143 : }
144 :
145 : /* Return last line from the shell command */
146 0 : RETVAL_STRINGL(buf, bufl, 1);
147 : } else { /* should return NULL, but for BC we return "" */
148 4 : RETVAL_EMPTY_STRING();
149 : }
150 : } else {
151 8651 : while((bufl = php_stream_read(stream, buf, EXEC_INPUT_BUF)) > 0) {
152 8623 : PHPWRITE(buf, bufl);
153 : }
154 : }
155 :
156 18 : pclose_return = php_stream_close(stream);
157 18 : efree(buf);
158 :
159 18 : done:
160 : #if PHP_SIGCHILD
161 : if (sig_handler) {
162 : signal(SIGCHLD, sig_handler);
163 : }
164 : #endif
165 18 : if (d) {
166 0 : efree(d);
167 : }
168 18 : return pclose_return;
169 0 : err:
170 0 : pclose_return = -1;
171 0 : goto done;
172 : }
173 : /* }}} */
174 :
175 : static void php_exec_ex(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{ */
176 18 : {
177 : char *cmd;
178 : int cmd_len;
179 : zend_uchar cmd_type;
180 18 : zval *ret_code=NULL, *ret_array=NULL;
181 : int ret;
182 :
183 18 : if (mode) {
184 14 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "t|z/", &cmd, &cmd_len, &cmd_type, &ret_code) == FAILURE) {
185 0 : RETURN_FALSE;
186 : }
187 : } else {
188 4 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "t|z/z/", &cmd, &cmd_len, &cmd_type, &ret_array, &ret_code) == FAILURE) {
189 0 : RETURN_FALSE;
190 : }
191 : }
192 18 : if (!cmd_len) {
193 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot execute a blank command");
194 0 : RETURN_FALSE;
195 : }
196 :
197 18 : if (cmd_type == IS_UNICODE) {
198 18 : if (FAILURE == php_stream_path_encode(NULL, &cmd, &cmd_len, (UChar*)cmd, cmd_len, REPORT_ERRORS, FG(default_context))) {
199 0 : RETURN_FALSE;
200 : }
201 : }
202 :
203 18 : if (!ret_array) {
204 15 : ret = php_exec(mode, cmd, NULL, return_value TSRMLS_CC);
205 : } else {
206 3 : if (Z_TYPE_P(ret_array) != IS_ARRAY) {
207 1 : zval_dtor(ret_array);
208 1 : array_init(ret_array);
209 : }
210 3 : ret = php_exec(2, cmd, ret_array, return_value TSRMLS_CC);
211 : }
212 18 : if (ret_code) {
213 2 : zval_dtor(ret_code);
214 2 : ZVAL_LONG(ret_code, ret);
215 : }
216 :
217 18 : if (cmd_type == IS_UNICODE) {
218 18 : efree(cmd);
219 : }
220 : }
221 : /* }}} */
222 :
223 : /* {{{ proto string exec(string command [, array &output [, int &return_value]]) U
224 : Execute an external program */
225 : PHP_FUNCTION(exec)
226 4 : {
227 4 : php_exec_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
228 4 : }
229 : /* }}} */
230 :
231 : /* {{{ proto int system(string command [, int &return_value]) U
232 : Execute an external program and display output */
233 : PHP_FUNCTION(system)
234 0 : {
235 0 : php_exec_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
236 0 : }
237 : /* }}} */
238 :
239 : /* {{{ proto void passthru(string command [, int &return_value]) U
240 : Execute an external program and display raw output */
241 : PHP_FUNCTION(passthru)
242 14 : {
243 14 : php_exec_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, 3);
244 14 : }
245 : /* }}} */
246 :
247 : /* {{{ php_escape_shell_cmd
248 : Escape all chars that could possibly be used to
249 : break out of a shell command
250 :
251 : This function emalloc's a string and returns the pointer.
252 : Remember to efree it when done with it.
253 :
254 : *NOT* safe for binary strings
255 : */
256 : PHPAPI char *php_escape_shell_cmd(char *str)
257 15 : {
258 15 : register int x, y, l = strlen(str);
259 : char *cmd;
260 15 : char *p = NULL;
261 15 : size_t estimate = (2 * l) + 1;
262 :
263 : TSRMLS_FETCH();
264 :
265 15 : cmd = safe_emalloc(2, l, 1);
266 :
267 495 : for (x = 0, y = 0; x < l; x++) {
268 480 : int mb_len = php_mblen(str + x, (l - x));
269 :
270 : /* skip non-valid multibyte characters */
271 480 : if (mb_len < 0) {
272 0 : continue;
273 480 : } else if (mb_len > 1) {
274 5 : memcpy(cmd + y, str + x, mb_len);
275 5 : y += mb_len;
276 5 : x += mb_len - 1;
277 5 : continue;
278 : }
279 :
280 475 : switch (str[x]) {
281 : #ifndef PHP_WIN32
282 : case '"':
283 : case '\'':
284 2 : if (!p && (p = memchr(str + x + 1, str[x], l - x - 1))) {
285 : /* noop */
286 2 : } else if (p && *p == str[x]) {
287 1 : p = NULL;
288 : } else {
289 0 : cmd[y++] = '\\';
290 : }
291 2 : cmd[y++] = str[x];
292 2 : break;
293 : #else
294 : /* % is Windows specific for enviromental variables, ^%PATH% will
295 : output PATH whil ^%PATH^% not. escapeshellcmd will escape all %.
296 : */
297 : case '%':
298 : cmd[y++] = ' ';
299 : break;
300 : #endif
301 : case '#': /* This is character-set independent */
302 : case '&':
303 : case ';':
304 : case '`':
305 : case '|':
306 : case '*':
307 : case '?':
308 : case '~':
309 : case '<':
310 : case '>':
311 : case '^':
312 : case '(':
313 : case ')':
314 : case '[':
315 : case ']':
316 : case '{':
317 : case '}':
318 : case '$':
319 : case '\\':
320 : case '\x0A': /* excluding these two */
321 : case '\xFF':
322 : #ifdef PHP_WIN32
323 : cmd[y++] = '^';
324 : #else
325 6 : cmd[y++] = '\\';
326 : #endif
327 : /* fall-through */
328 : default:
329 473 : cmd[y++] = str[x];
330 :
331 : }
332 : }
333 15 : cmd[y] = '\0';
334 :
335 15 : if ((estimate - y) > 4096) {
336 : /* realloc if the estimate was way overill
337 : * Arbitrary cutoff point of 4096 */
338 0 : cmd = erealloc(cmd, y + 1);
339 : }
340 :
341 15 : return cmd;
342 : }
343 : /* }}} */
344 :
345 : /* {{{ php_escape_shell_arg
346 : */
347 : PHPAPI char *php_escape_shell_arg(char *str)
348 44 : {
349 44 : int x, y = 0, l = strlen(str);
350 : char *cmd;
351 44 : size_t estimate = (4 * l) + 3;
352 :
353 : TSRMLS_FETCH();
354 :
355 44 : cmd = safe_emalloc(4, l, 3); /* worst case */
356 :
357 : #ifdef PHP_WIN32
358 : cmd[y++] = '"';
359 : #else
360 44 : cmd[y++] = '\'';
361 : #endif
362 :
363 681 : for (x = 0; x < l; x++) {
364 637 : int mb_len = php_mblen(str + x, (l - x));
365 :
366 : /* skip non-valid multibyte characters */
367 637 : if (mb_len < 0) {
368 0 : continue;
369 637 : } else if (mb_len > 1) {
370 4 : memcpy(cmd + y, str + x, mb_len);
371 4 : y += mb_len;
372 4 : x += mb_len - 1;
373 4 : continue;
374 : }
375 :
376 633 : switch (str[x]) {
377 : #ifdef PHP_WIN32
378 : case '"':
379 : case '%':
380 : cmd[y++] = ' ';
381 : break;
382 : #else
383 : case '\'':
384 7 : cmd[y++] = '\'';
385 7 : cmd[y++] = '\\';
386 7 : cmd[y++] = '\'';
387 : #endif
388 : /* fall-through */
389 : default:
390 633 : cmd[y++] = str[x];
391 : }
392 : }
393 : #ifdef PHP_WIN32
394 : cmd[y++] = '"';
395 : #else
396 44 : cmd[y++] = '\'';
397 : #endif
398 44 : cmd[y] = '\0';
399 :
400 44 : if ((estimate - y) > 4096) {
401 : /* realloc if the estimate was way overill
402 : * Arbitrary cutoff point of 4096 */
403 0 : cmd = erealloc(cmd, y + 1);
404 : }
405 44 : return cmd;
406 : }
407 : /* }}} */
408 :
409 : /* {{{ proto string escapeshellcmd(string command) U
410 : Escape shell metacharacters */
411 : PHP_FUNCTION(escapeshellcmd)
412 13 : {
413 : char *command;
414 : int command_len;
415 : zend_uchar command_type;
416 13 : char *cmd = NULL;
417 :
418 13 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "t", &command, &command_len, &command_type) == FAILURE) {
419 0 : return;
420 : }
421 :
422 13 : if (command_type == IS_UNICODE) {
423 13 : if (FAILURE == php_stream_path_encode(NULL, &command, &command_len, (UChar*)command, command_len, REPORT_ERRORS, FG(default_context))) {
424 0 : RETURN_FALSE;
425 : }
426 : }
427 :
428 13 : if (command_len) {
429 13 : cmd = php_escape_shell_cmd(command);
430 13 : RETVAL_STRING(cmd, 0);
431 : } else {
432 0 : RETVAL_EMPTY_STRING();
433 : }
434 :
435 13 : if (command_type == IS_UNICODE) {
436 13 : efree(command);
437 : }
438 : }
439 : /* }}} */
440 :
441 : /* {{{ proto string escapeshellarg(string arg) U
442 : Quote and escape an argument for use in a shell command */
443 : PHP_FUNCTION(escapeshellarg)
444 49 : {
445 : char *argument;
446 : int argument_len;
447 : zend_uchar argument_type;
448 49 : char *cmd = NULL;
449 :
450 49 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "t", &argument, &argument_len, &argument_type) == FAILURE) {
451 5 : return;
452 : }
453 :
454 44 : if (argument_type == IS_UNICODE) {
455 44 : if (FAILURE == php_stream_path_encode(NULL, &argument, &argument_len, (UChar*)argument, argument_len, REPORT_ERRORS, FG(default_context))) {
456 0 : RETURN_FALSE;
457 : }
458 : }
459 :
460 44 : if (argument) {
461 44 : cmd = php_escape_shell_arg(argument);
462 44 : RETVAL_STRING(cmd, 0);
463 : }
464 :
465 44 : if (argument_type == IS_UNICODE) {
466 44 : efree(argument);
467 : }
468 : }
469 : /* }}} */
470 :
471 : /* {{{ proto string shell_exec(string cmd) U
472 : Execute command via shell and return complete output as string */
473 : PHP_FUNCTION(shell_exec)
474 119 : {
475 : FILE *in;
476 : size_t total_readbytes;
477 : char *command;
478 : int command_len;
479 : zend_uchar command_type;
480 : char *ret;
481 : php_stream *stream;
482 :
483 119 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "t", &command, &command_len, &command_type) == FAILURE) {
484 0 : return;
485 : }
486 :
487 119 : if (command_type == IS_UNICODE) {
488 119 : if (FAILURE == php_stream_path_encode(NULL, &command, &command_len, (UChar*)command, command_len, REPORT_ERRORS, FG(default_context))) {
489 0 : RETURN_FALSE;
490 : }
491 : }
492 :
493 : #ifdef PHP_WIN32
494 : if ((in=VCWD_POPEN(command, "rt"))==NULL) {
495 : #else
496 119 : if ((in=VCWD_POPEN(command, "r"))==NULL) {
497 : #endif
498 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to execute '%s'", command);
499 0 : RETURN_FALSE;
500 : }
501 :
502 119 : stream = php_stream_fopen_from_pipe(in, "rb");
503 119 : total_readbytes = php_stream_copy_to_mem(stream, (void *)&ret, PHP_STREAM_COPY_ALL, 0);
504 119 : php_stream_close(stream);
505 :
506 119 : if (total_readbytes > 0) {
507 117 : RETVAL_STRINGL(ret, total_readbytes, 0);
508 : }
509 :
510 119 : if (command_type == IS_UNICODE) {
511 119 : efree(command);
512 : }
513 : }
514 : /* }}} */
515 :
516 : #ifdef HAVE_NICE
517 : /* {{{ proto bool proc_nice(int priority) U
518 : Change the priority of the current process */
519 : PHP_FUNCTION(proc_nice)
520 25 : {
521 : long pri;
522 :
523 25 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &pri) == FAILURE) {
524 12 : RETURN_FALSE;
525 : }
526 :
527 13 : errno = 0;
528 13 : nice(pri);
529 13 : if (errno) {
530 1 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Only a super user may attempt to increase the priority of a process");
531 1 : RETURN_FALSE;
532 : }
533 :
534 12 : RETURN_TRUE;
535 : }
536 : /* }}} */
537 : #endif
538 :
539 : /*
540 : * Local variables:
541 : * tab-width: 4
542 : * c-basic-offset: 4
543 : * End:
544 : * vim600: sw=4 ts=4 fdm=marker
545 : * vim<600: sw=4 ts=4
546 : */
|