Back to index

php5  5.3.10
exec.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | PHP Version 5                                                        |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1997-2012 The PHP Group                                |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 3.01 of the PHP license,      |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.php.net/license/3_01.txt                                  |
00011    | If you did not receive a copy of the PHP license and are unable to   |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@php.net so we can mail you a copy immediately.               |
00014    +----------------------------------------------------------------------+
00015    | Author: Rasmus Lerdorf <rasmus@php.net>                              |
00016    |         Ilia Alshanetsky <iliaa@php.net>                             |
00017    +----------------------------------------------------------------------+
00018  */
00019 /* $Id: exec.c 321634 2012-01-01 13:15:04Z felipe $ */
00020 
00021 #include <stdio.h>
00022 #include "php.h"
00023 #include <ctype.h>
00024 #include "php_string.h"
00025 #include "safe_mode.h"
00026 #include "ext/standard/head.h"
00027 #include "ext/standard/file.h"
00028 #include "basic_functions.h"
00029 #include "exec.h"
00030 #include "php_globals.h"
00031 #include "SAPI.h"
00032 
00033 #if HAVE_SYS_WAIT_H
00034 #include <sys/wait.h>
00035 #endif
00036 #if HAVE_SIGNAL_H
00037 #include <signal.h>
00038 #endif
00039 
00040 #if HAVE_SYS_TYPES_H
00041 #include <sys/types.h>
00042 #endif
00043 #if HAVE_SYS_STAT_H
00044 #include <sys/stat.h>
00045 #endif
00046 #if HAVE_FCNTL_H
00047 #include <fcntl.h>
00048 #endif
00049 
00050 #if HAVE_NICE && HAVE_UNISTD_H
00051 #include <unistd.h>
00052 #endif
00053 
00054 /* {{{ php_exec
00055  * If type==0, only last line of output is returned (exec)
00056  * If type==1, all lines will be printed and last lined returned (system)
00057  * If type==2, all lines will be saved to given array (exec with &$array)
00058  * If type==3, output will be printed binary, no lines will be saved or returned (passthru)
00059  *
00060  */
00061 PHPAPI int php_exec(int type, char *cmd, zval *array, zval *return_value TSRMLS_DC)
00062 {
00063        FILE *fp;
00064        char *buf, *tmp=NULL;
00065        int l = 0, pclose_return;
00066        char *cmd_p, *b, *c, *d=NULL;
00067        php_stream *stream;
00068        size_t buflen, bufl = 0;
00069 #if PHP_SIGCHILD
00070        void (*sig_handler)() = NULL;
00071 #endif
00072 
00073        if (PG(safe_mode)) {
00074               if ((c = strchr(cmd, ' '))) {
00075                      *c = '\0';
00076                      c++;
00077               }
00078               if (strstr(cmd, "..")) {
00079                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "No '..' components allowed in path");
00080                      goto err;
00081               }
00082               
00083               b = strrchr(cmd, PHP_DIR_SEPARATOR);
00084 
00085 #ifdef PHP_WIN32
00086               if (b && *b == '\\' && b == cmd) {
00087                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid absolute path.");
00088                      goto err;
00089               }
00090 #endif
00091 
00092               spprintf(&d, 0, "%s%s%s%s%s", PG(safe_mode_exec_dir), (b ? "" : "/"), (b ? b : cmd), (c ? " " : ""), (c ? c : ""));
00093               if (c) {
00094                      *(c - 1) = ' ';
00095               }
00096               cmd_p = php_escape_shell_cmd(d);
00097               efree(d);
00098               d = cmd_p;
00099        } else {
00100               cmd_p = cmd;
00101        }
00102 
00103 #if PHP_SIGCHILD
00104        sig_handler = signal (SIGCHLD, SIG_DFL);
00105 #endif
00106 
00107 #ifdef PHP_WIN32
00108        fp = VCWD_POPEN(cmd_p, "rb");
00109 #else
00110        fp = VCWD_POPEN(cmd_p, "r");
00111 #endif
00112        if (!fp) {
00113               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to fork [%s]", cmd);
00114               goto err;
00115        }
00116 
00117        stream = php_stream_fopen_from_pipe(fp, "rb");
00118 
00119        buf = (char *) emalloc(EXEC_INPUT_BUF);
00120        buflen = EXEC_INPUT_BUF;
00121 
00122        if (type != 3) {
00123               b = buf;
00124               
00125               while (php_stream_get_line(stream, b, EXEC_INPUT_BUF, &bufl)) {
00126                      /* no new line found, let's read some more */
00127                      if (b[bufl - 1] != '\n' && !php_stream_eof(stream)) {
00128                             if (buflen < (bufl + (b - buf) + EXEC_INPUT_BUF)) {
00129                                    bufl += b - buf;
00130                                    buflen = bufl + EXEC_INPUT_BUF;
00131                                    buf = erealloc(buf, buflen);
00132                                    b = buf + bufl;
00133                             } else {
00134                                    b += bufl;
00135                             }
00136                             continue;
00137                      } else if (b != buf) {
00138                             bufl += b - buf;
00139                      }
00140 
00141                      if (type == 1) {
00142                             PHPWRITE(buf, bufl);
00143                             if (OG(ob_nesting_level) < 1) {
00144                                    sapi_flush(TSRMLS_C);
00145                             }
00146                      } else if (type == 2) {
00147                             /* strip trailing whitespaces */
00148                             l = bufl;
00149                             while (l-- && isspace(((unsigned char *)buf)[l]));
00150                             if (l != (int)(bufl - 1)) {
00151                                    bufl = l + 1;
00152                                    buf[bufl] = '\0';
00153                             }
00154                             add_next_index_stringl(array, buf, bufl, 1);
00155                      }
00156                      b = buf;
00157               }
00158               if (bufl) {
00159                      /* strip trailing whitespaces if we have not done so already */
00160                      if ((type == 2 && buf != b) || type != 2) {
00161                             l = bufl;
00162                             while (l-- && isspace(((unsigned char *)buf)[l]));
00163                             if (l != (int)(bufl - 1)) {
00164                                    bufl = l + 1;
00165                                    buf[bufl] = '\0';
00166                             }
00167                             if (type == 2) {
00168                                    add_next_index_stringl(array, buf, bufl, 1);
00169                             }
00170                      }
00171 
00172                      /* Return last line from the shell command */
00173                      if (PG(magic_quotes_runtime)) {
00174                             int len;
00175 
00176                             tmp = php_addslashes(buf, bufl, &len, 0 TSRMLS_CC);
00177                             RETVAL_STRINGL(tmp, len, 0);
00178                      } else {
00179                             RETVAL_STRINGL(buf, bufl, 1);
00180                      }
00181               } else { /* should return NULL, but for BC we return "" */
00182                      RETVAL_EMPTY_STRING();
00183               }
00184        } else {
00185               while((bufl = php_stream_read(stream, buf, EXEC_INPUT_BUF)) > 0) {
00186                      PHPWRITE(buf, bufl);
00187               }
00188        }
00189 
00190        pclose_return = php_stream_close(stream);
00191        efree(buf);
00192 
00193 done:
00194 #if PHP_SIGCHILD
00195        if (sig_handler) {
00196               signal(SIGCHLD, sig_handler);
00197        }
00198 #endif
00199        if (d) {
00200               efree(d);
00201        }
00202        return pclose_return;
00203 err:
00204        pclose_return = -1;
00205        goto done;
00206 }
00207 /* }}} */
00208 
00209 static void php_exec_ex(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{ */
00210 {
00211        char *cmd;
00212        int cmd_len;
00213        zval *ret_code=NULL, *ret_array=NULL;
00214        int ret;
00215 
00216        if (mode) {
00217               if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z/", &cmd, &cmd_len, &ret_code) == FAILURE) {
00218                      RETURN_FALSE;
00219               }
00220        } else {
00221               if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z/z/", &cmd, &cmd_len, &ret_array, &ret_code) == FAILURE) {
00222                      RETURN_FALSE;
00223               }
00224        }
00225        if (!cmd_len) {
00226               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot execute a blank command");
00227               RETURN_FALSE;
00228        }
00229 
00230        if (!ret_array) {
00231               ret = php_exec(mode, cmd, NULL, return_value TSRMLS_CC);
00232        } else {
00233               if (Z_TYPE_P(ret_array) != IS_ARRAY) {
00234                      zval_dtor(ret_array);
00235                      array_init(ret_array);
00236               }
00237               ret = php_exec(2, cmd, ret_array, return_value TSRMLS_CC);
00238        }
00239        if (ret_code) {
00240               zval_dtor(ret_code);
00241               ZVAL_LONG(ret_code, ret);
00242        }
00243 }
00244 /* }}} */
00245 
00246 /* {{{ proto string exec(string command [, array &output [, int &return_value]])
00247    Execute an external program */
00248 PHP_FUNCTION(exec)
00249 {
00250        php_exec_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
00251 }
00252 /* }}} */
00253 
00254 /* {{{ proto int system(string command [, int &return_value])
00255    Execute an external program and display output */
00256 PHP_FUNCTION(system)
00257 {
00258        php_exec_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
00259 }
00260 /* }}} */
00261 
00262 /* {{{ proto void passthru(string command [, int &return_value])
00263    Execute an external program and display raw output */
00264 PHP_FUNCTION(passthru)
00265 {
00266        php_exec_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, 3);
00267 }
00268 /* }}} */
00269 
00270 /* {{{ php_escape_shell_cmd
00271    Escape all chars that could possibly be used to
00272    break out of a shell command
00273 
00274    This function emalloc's a string and returns the pointer.
00275    Remember to efree it when done with it.
00276 
00277    *NOT* safe for binary strings
00278 */
00279 PHPAPI char *php_escape_shell_cmd(char *str)
00280 {
00281        register int x, y, l = strlen(str);
00282        char *cmd;
00283        char *p = NULL;
00284        size_t estimate = (2 * l) + 1;
00285 
00286        TSRMLS_FETCH();
00287 
00288        cmd = safe_emalloc(2, l, 1);
00289 
00290        for (x = 0, y = 0; x < l; x++) {
00291               int mb_len = php_mblen(str + x, (l - x));
00292 
00293               /* skip non-valid multibyte characters */
00294               if (mb_len < 0) {
00295                      continue;
00296               } else if (mb_len > 1) {
00297                      memcpy(cmd + y, str + x, mb_len);
00298                      y += mb_len;
00299                      x += mb_len - 1;
00300                      continue;
00301               }
00302 
00303               switch (str[x]) {
00304 #ifndef PHP_WIN32
00305                      case '"':
00306                      case '\'':
00307                             if (!p && (p = memchr(str + x + 1, str[x], l - x - 1))) {
00308                                    /* noop */
00309                             } else if (p && *p == str[x]) {
00310                                    p = NULL;
00311                             } else {
00312                                    cmd[y++] = '\\';
00313                             }
00314                             cmd[y++] = str[x];
00315                             break;
00316 #else
00317                      /* % is Windows specific for enviromental variables, ^%PATH% will 
00318                             output PATH whil ^%PATH^% not. escapeshellcmd will escape all %.
00319                      */
00320                      case '%':
00321                      case '"':
00322                      case '\'':
00323 #endif
00324                      case '#': /* This is character-set independent */
00325                      case '&':
00326                      case ';':
00327                      case '`':
00328                      case '|':
00329                      case '*':
00330                      case '?':
00331                      case '~':
00332                      case '<':
00333                      case '>':
00334                      case '^':
00335                      case '(':
00336                      case ')':
00337                      case '[':
00338                      case ']':
00339                      case '{':
00340                      case '}':
00341                      case '$':
00342                      case '\\':
00343                      case '\x0A': /* excluding these two */
00344                      case '\xFF':
00345 #ifdef PHP_WIN32
00346                             cmd[y++] = '^';
00347 #else
00348                             cmd[y++] = '\\';
00349 #endif
00350                             /* fall-through */
00351                      default:
00352                             cmd[y++] = str[x];
00353 
00354               }
00355        }
00356        cmd[y] = '\0';
00357 
00358        if ((estimate - y) > 4096) {
00359               /* realloc if the estimate was way overill
00360                * Arbitrary cutoff point of 4096 */
00361               cmd = erealloc(cmd, y + 1);
00362        }
00363 
00364        return cmd;
00365 }
00366 /* }}} */
00367 
00368 /* {{{ php_escape_shell_arg
00369  */
00370 PHPAPI char *php_escape_shell_arg(char *str)
00371 {
00372        int x, y = 0, l = strlen(str);
00373        char *cmd;
00374        size_t estimate = (4 * l) + 3;
00375 
00376        TSRMLS_FETCH();
00377 
00378        cmd = safe_emalloc(4, l, 3); /* worst case */
00379 
00380 #ifdef PHP_WIN32
00381        cmd[y++] = '"';
00382 #else
00383        cmd[y++] = '\'';
00384 #endif
00385 
00386        for (x = 0; x < l; x++) {
00387               int mb_len = php_mblen(str + x, (l - x));
00388 
00389               /* skip non-valid multibyte characters */
00390               if (mb_len < 0) {
00391                      continue;
00392               } else if (mb_len > 1) {
00393                      memcpy(cmd + y, str + x, mb_len);
00394                      y += mb_len;
00395                      x += mb_len - 1;
00396                      continue;
00397               }
00398 
00399               switch (str[x]) {
00400 #ifdef PHP_WIN32
00401               case '"':
00402               case '%':
00403                      cmd[y++] = ' ';
00404                      break;
00405 #else
00406               case '\'':
00407                      cmd[y++] = '\'';
00408                      cmd[y++] = '\\';
00409                      cmd[y++] = '\'';
00410 #endif
00411                      /* fall-through */
00412               default:
00413                      cmd[y++] = str[x];
00414               }
00415        }
00416 #ifdef PHP_WIN32
00417        cmd[y++] = '"';
00418 #else
00419        cmd[y++] = '\'';
00420 #endif
00421        cmd[y] = '\0';
00422 
00423        if ((estimate - y) > 4096) {
00424               /* realloc if the estimate was way overill
00425                * Arbitrary cutoff point of 4096 */
00426               cmd = erealloc(cmd, y + 1);
00427        }
00428        return cmd;
00429 }
00430 /* }}} */
00431 
00432 /* {{{ proto string escapeshellcmd(string command)
00433    Escape shell metacharacters */
00434 PHP_FUNCTION(escapeshellcmd)
00435 {
00436        char *command;
00437        int command_len;
00438        char *cmd = NULL;
00439 
00440        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &command, &command_len) == FAILURE) {
00441               return;
00442        }
00443 
00444        if (command_len) {
00445               cmd = php_escape_shell_cmd(command);
00446               RETVAL_STRING(cmd, 0);
00447        } else {
00448               RETVAL_EMPTY_STRING();
00449        }
00450 }
00451 /* }}} */
00452 
00453 /* {{{ proto string escapeshellarg(string arg)
00454    Quote and escape an argument for use in a shell command */
00455 PHP_FUNCTION(escapeshellarg)
00456 {
00457        char *argument;
00458        int argument_len;
00459        char *cmd = NULL;
00460 
00461        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &argument, &argument_len) == FAILURE) {
00462               return;
00463        }
00464 
00465        if (argument) {
00466               cmd = php_escape_shell_arg(argument);
00467               RETVAL_STRING(cmd, 0);
00468        }
00469 }
00470 /* }}} */
00471 
00472 /* {{{ proto string shell_exec(string cmd)
00473    Execute command via shell and return complete output as string */
00474 PHP_FUNCTION(shell_exec)
00475 {
00476        FILE *in;
00477        size_t total_readbytes;
00478        char *command;
00479        int command_len;
00480        char *ret;
00481        php_stream *stream;
00482 
00483        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &command, &command_len) == FAILURE) {
00484               return;
00485        }
00486 
00487        if (PG(safe_mode)) {
00488               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot execute using backquotes in Safe Mode");
00489               RETURN_FALSE;
00490        }
00491 
00492 #ifdef PHP_WIN32
00493        if ((in=VCWD_POPEN(command, "rt"))==NULL) {
00494 #else
00495        if ((in=VCWD_POPEN(command, "r"))==NULL) {
00496 #endif
00497               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to execute '%s'", command);
00498               RETURN_FALSE;
00499        }
00500 
00501        stream = php_stream_fopen_from_pipe(in, "rb");
00502        total_readbytes = php_stream_copy_to_mem(stream, &ret, PHP_STREAM_COPY_ALL, 0);
00503        php_stream_close(stream);
00504 
00505        if (total_readbytes > 0) {
00506               RETVAL_STRINGL(ret, total_readbytes, 0);
00507        }
00508 }
00509 /* }}} */
00510 
00511 #ifdef HAVE_NICE
00512 /* {{{ proto bool proc_nice(int priority)
00513    Change the priority of the current process */
00514 PHP_FUNCTION(proc_nice)
00515 {
00516        long pri;
00517 
00518        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &pri) == FAILURE) {
00519               RETURN_FALSE;
00520        }
00521 
00522        errno = 0;
00523        nice(pri);
00524        if (errno) {
00525               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Only a super user may attempt to increase the priority of a process");
00526               RETURN_FALSE;
00527        }
00528 
00529        RETURN_TRUE;
00530 }
00531 /* }}} */
00532 #endif
00533 
00534 /*
00535  * Local variables:
00536  * tab-width: 4
00537  * c-basic-offset: 4
00538  * End:
00539  * vim600: sw=4 ts=4 fdm=marker
00540  * vim<600: sw=4 ts=4
00541  */