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