From 717f8eef2283698324c5d74752b34c6ba2682951 Mon Sep 17 00:00:00 2001 From: Keyur Govande Date: Tue, 19 Feb 2013 05:06:37 +0000 Subject: [PATCH 01/10] Add in support for setting a process title. RFC: https://wiki.php.net/rfc/cli_process_title Discussion: http://marc.info/?t=136021582000003&r=1&w=2 --- sapi/cli/config.m4 | 19 +- sapi/cli/config.w32 | 4 +- sapi/cli/php_cli.c | 13 + sapi/cli/php_cli_process_title.c | 76 +++++ sapi/cli/php_cli_process_title.h | 40 +++ sapi/cli/php_cli_server.c | 8 + sapi/cli/php_cli_server.h | 1 + sapi/cli/ps_title.c | 384 ++++++++++++++++++++++++++ sapi/cli/ps_title.h | 39 +++ sapi/cli/tests/cli_process_title.phpt | 46 +++ 10 files changed, 627 insertions(+), 3 deletions(-) create mode 100644 sapi/cli/php_cli_process_title.c create mode 100644 sapi/cli/php_cli_process_title.h create mode 100644 sapi/cli/ps_title.c create mode 100644 sapi/cli/ps_title.h create mode 100644 sapi/cli/tests/cli_process_title.phpt diff --git a/sapi/cli/config.m4 b/sapi/cli/config.m4 index cdfa1f7daff2a..9a1b98da46193 100644 --- a/sapi/cli/config.m4 +++ b/sapi/cli/config.m4 @@ -6,6 +6,23 @@ PHP_ARG_ENABLE(cli,, [ --disable-cli Disable building CLI version of PHP (this forces --without-pear)], yes, no) +AC_CHECK_FUNCS(setproctitle) + +AC_CHECK_HEADERS([sys/pstat.h]) + +AC_CACHE_CHECK([for PS_STRINGS], [cli_cv_var_PS_STRINGS], +[AC_TRY_LINK( +[#include +#include +], +[PS_STRINGS->ps_nargvstr = 1; +PS_STRINGS->ps_argvstr = "foo";], +[cli_cv_var_PS_STRINGS=yes], +[cli_cv_var_PS_STRINGS=no])]) +if test "$cli_cv_var_PS_STRINGS" = yes ; then + AC_DEFINE([HAVE_PS_STRINGS], [], [Define to 1 if the PS_STRINGS thing exists.]) +fi + AC_MSG_CHECKING(for CLI build) if test "$PHP_CLI" != "no"; then PHP_ADD_MAKEFILE_FRAGMENT($abs_srcdir/sapi/cli/Makefile.frag) @@ -14,7 +31,7 @@ if test "$PHP_CLI" != "no"; then SAPI_CLI_PATH=sapi/cli/php dnl Select SAPI - PHP_SELECT_SAPI(cli, program, php_cli.c php_http_parser.c php_cli_server.c,, '$(SAPI_CLI_PATH)') + PHP_SELECT_SAPI(cli, program, php_cli.c php_http_parser.c php_cli_server.c ps_title.c php_cli_process_title.c,, '$(SAPI_CLI_PATH)') case $host_alias in *aix*) diff --git a/sapi/cli/config.w32 b/sapi/cli/config.w32 index 4d0dad58e84b4..adcbb2b496acb 100644 --- a/sapi/cli/config.w32 +++ b/sapi/cli/config.w32 @@ -6,7 +6,7 @@ ARG_ENABLE('crt-debug', 'Enable CRT memory dumps for debugging sent to STDERR', ARG_ENABLE('cli-win32', 'Build console-less CLI version of PHP', 'no'); if (PHP_CLI == "yes") { - SAPI('cli', 'php_cli.c php_http_parser.c php_cli_server.c', 'php.exe'); + SAPI('cli', 'php_cli.c php_http_parser.c php_cli_server.c php_cli_process_title.c ps_title.c', 'php.exe'); ADD_FLAG("LIBS_CLI", "ws2_32.lib"); if (PHP_CRT_DEBUG == "yes") { ADD_FLAG("CFLAGS_CLI", "/D PHP_WIN32_DEBUG_HEAP"); @@ -15,7 +15,7 @@ if (PHP_CLI == "yes") { } if (PHP_CLI_WIN32 == "yes") { - SAPI('cli_win32', 'cli_win32.c', 'php-win.exe'); + SAPI('cli_win32', 'cli_win32.c php_cli_process_title.c ps_title.c', 'php-win.exe'); ADD_FLAG("LDFLAGS_CLI_WIN32", "/stack:8388608"); } diff --git a/sapi/cli/php_cli.c b/sapi/cli/php_cli.c index c01f3705b59bc..ffb6a69c4cc9d 100644 --- a/sapi/cli/php_cli.c +++ b/sapi/cli/php_cli.c @@ -86,6 +86,9 @@ #include "php_cli_server.h" #endif +#include "ps_title.h" +#include "php_cli_process_title.h" + #ifndef PHP_WIN32 # define php_select(m, r, w, e, t) select(m, r, w, e, t) #else @@ -478,6 +481,8 @@ ZEND_END_ARG_INFO() static const zend_function_entry additional_functions[] = { ZEND_FE(dl, arginfo_dl) + PHP_FE(cli_set_process_title, arginfo_cli_set_process_title) + PHP_FE(cli_get_process_title, NULL) {NULL, NULL, NULL} }; @@ -1200,6 +1205,7 @@ int main(int argc, char *argv[]) int argc = __argc; char **argv = __argv; #endif + int c; int exit_status = SUCCESS; int module_started = 0, sapi_started = 0; @@ -1211,6 +1217,12 @@ int main(int argc, char *argv[]) int ini_ignore = 0; sapi_module_struct *sapi_module = &cli_sapi_module; + /* + * Do not move this initialization. It needs to happen before argv is used + * in any way. + */ + argv = save_ps_args(argc, argv); + cli_sapi_module.additional_functions = additional_functions; #if defined(PHP_WIN32) && defined(_DEBUG) && defined(PHP_WIN32_DEBUG_HEAP) @@ -1299,6 +1311,7 @@ int main(int argc, char *argv[]) #ifndef PHP_CLI_WIN32_NO_CONSOLE case 'S': sapi_module = &cli_server_sapi_module; + cli_server_sapi_module.additional_functions = server_additional_functions; break; #endif case 'h': /* help & quit */ diff --git a/sapi/cli/php_cli_process_title.c b/sapi/cli/php_cli_process_title.c new file mode 100644 index 0000000000000..8631f82893c4d --- /dev/null +++ b/sapi/cli/php_cli_process_title.c @@ -0,0 +1,76 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Keyur Govande (kgovande@gmail.com) | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_cli_process_title.h" +#include "ps_title.h" + +/* {{{ proto boolean cli_set_process_title(string arg) + Return a boolean to confirm if the process title was successfully changed or not */ +PHP_FUNCTION(cli_set_process_title) +{ + char *title = NULL; + int title_len; + int rc; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &title, &title_len) == FAILURE) { + RETURN_FALSE; + } + + rc = set_ps_title(title); + if (rc == PS_TITLE_SUCCESS) { + RETURN_TRUE; + } + + php_error_docref(NULL TSRMLS_CC, E_WARNING, "cli_set_process_title had an error: %s", ps_title_errno(rc)); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ proto string cli_get_process_title(string arg) + Return a string with the current process title. NULL if error. */ +PHP_FUNCTION(cli_get_process_title) +{ + int length = 0; + const char* title = NULL; + int rc; + + rc = get_ps_title(&length, &title); + if (rc != PS_TITLE_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "cli_get_process_title had an error: %s", ps_title_errno(rc)); + RETURN_NULL(); + } + + RETURN_STRINGL(title, length, 1); +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/sapi/cli/php_cli_process_title.h b/sapi/cli/php_cli_process_title.h new file mode 100644 index 0000000000000..62ef5673db57e --- /dev/null +++ b/sapi/cli/php_cli_process_title.h @@ -0,0 +1,40 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Keyur Govande (kgovande@gmail.com) | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef PHP_PS_TITLE_HEADER +#define PHP_PS_TITLE_HEADER + +ZEND_BEGIN_ARG_INFO(arginfo_cli_set_process_title, 0) + ZEND_ARG_INFO(0, title) +ZEND_END_ARG_INFO() + +PHP_FUNCTION(cli_set_process_title); +PHP_FUNCTION(cli_get_process_title); + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/sapi/cli/php_cli_server.c b/sapi/cli/php_cli_server.c index 1d2ceff691a56..b124a18b9c5dc 100644 --- a/sapi/cli/php_cli_server.c +++ b/sapi/cli/php_cli_server.c @@ -103,6 +103,8 @@ #include "php_http_parser.h" #include "php_cli_server.h" +#include "php_cli_process_title.h" + #define OUTPUT_NOT_CHECKED -1 #define OUTPUT_IS_TTY 1 #define OUTPUT_NOT_TTY 0 @@ -429,6 +431,12 @@ zend_module_entry cli_server_module_entry = { }; /* }}} */ +const zend_function_entry server_additional_functions[] = { + PHP_FE(cli_set_process_title, arginfo_cli_set_process_title) + PHP_FE(cli_get_process_title, NULL) + {NULL, NULL, NULL} +}; + static int sapi_cli_server_startup(sapi_module_struct *sapi_module) /* {{{ */ { if (php_module_startup(sapi_module, &cli_server_module_entry, 1) == FAILURE) { diff --git a/sapi/cli/php_cli_server.h b/sapi/cli/php_cli_server.h index ed716f99f809e..9a29626c7f056 100644 --- a/sapi/cli/php_cli_server.h +++ b/sapi/cli/php_cli_server.h @@ -23,6 +23,7 @@ #include "SAPI.h" +extern const zend_function_entry server_additional_functions[]; extern sapi_module_struct cli_server_sapi_module; extern int do_cli_server(int argc, char **argv TSRMLS_DC); diff --git a/sapi/cli/ps_title.c b/sapi/cli/ps_title.c new file mode 100644 index 0000000000000..610def73c5f15 --- /dev/null +++ b/sapi/cli/ps_title.c @@ -0,0 +1,384 @@ +/* + * PostgreSQL is released under the PostgreSQL License, a liberal Open Source + * license, similar to the BSD or MIT licenses. + * PostgreSQL Database Management System (formerly known as Postgres, then as + * Postgres95) + * + * Portions Copyright (c) 1996-2013, The PostgreSQL Global Development Group + * + * Portions Copyright (c) 1994, The Regents of the University of California + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without a written + * agreement is hereby granted, provided that the above copyright notice + * and this paragraph and the following two paragraphs appear in all copies. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING + * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, + * EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN + * "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * The following code is adopted from the PostgreSQL's ps_status(.h/.c). + */ + +#include "ps_title.h" +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#include + +#ifdef PHP_WIN32 +#include "config.w32.h" +#include +#include +#else +#include "php_config.h" +extern char** environ; +#endif + +#ifdef HAVE_SYS_PSTAT_H +#include // for HP-UX +#endif +#ifdef HAVE_PS_STRINGS +#include // for old BSD +#include +#endif +#if defined(DARWIN) +#include +#endif + +/* + * Ways of updating ps display: + * + * PS_USE_SETPROCTITLE + * use the function setproctitle(const char *, ...) + * (newer BSD systems) + * PS_USE_PSTAT + * use the pstat(PSTAT_SETCMD, ) + * (HPUX) + * PS_USE_PS_STRINGS + * assign PS_STRINGS->ps_argvstr = "string" + * (some BSD systems) + * PS_USE_CHANGE_ARGV + * assign argv[0] = "string" + * (some other BSD systems) + * PS_USE_CLOBBER_ARGV + * write over the argv and environment area + * (Linux and most SysV-like systems) + * PS_USE_WIN32 + * push the string out as the name of a Windows event + * PS_USE_NONE + * don't update ps display + * (This is the default, as it is safest.) + */ +#if defined(HAVE_SETPROCTITLE) +#define PS_USE_SETPROCTITLE +#elif defined(HAVE_SYS_PSTAT_H) && defined(PSTAT_SETCMD) +#define PS_USE_PSTAT +#elif defined(HAVE_PS_STRINGS) +#define PS_USE_PS_STRINGS +#elif defined(BSD) && !defined(DARWIN) +#define PS_USE_CHANGE_ARGV +#elif defined(__linux__) || defined(_AIX) || defined(__sgi) || (defined(sun) && !defined(BSD)) || defined(ultrix) || defined(__osf__) || defined(DARWIN) +#define PS_USE_CLOBBER_ARGV +#elif defined(PHP_WIN32) +#define PS_USE_WIN32 +#else +#define PS_USE_NONE +#endif + +/* Different systems want the buffer padded differently */ +#if defined(_AIX) || defined(__linux__) || defined(DARWIN) +#define PS_PADDING '\0' +#else +#define PS_PADDING ' ' +#endif + +#ifndef PS_USE_CLOBBER_ARGV +#define PS_BUFFER_SIZE 256 +static char ps_buffer[PS_BUFFER_SIZE]; +static const size_t ps_buffer_size = PS_BUFFER_SIZE; +#else +static char *ps_buffer; /* will point to argv area */ +static size_t ps_buffer_size; /* space determined at run time */ +#endif + +static size_t ps_buffer_cur_len; /* actual string length in ps_buffer */ + +/* save the original argv[] location here */ +static int save_argc; +static char** save_argv; + + +/* + * Call this early in startup to save the original argc/argv values. + * If needed, we make a copy of the original argv[] array to preserve it + * from being clobbered by subsequent ps_display actions. + * + * (The original argv[] will not be overwritten by this routine. + * Also, the physical location of the environment strings may be moved, + * so this should be called before any code that might try to hang onto a + * getenv() result.) + */ +char** save_ps_args(int argc, char** argv) +{ + save_argc = argc; + save_argv = argv; + +#if defined(PS_USE_CLOBBER_ARGV) + /* + * If we're going to overwrite the argv area, count the available space. + * Also move the environment to make additional room. + */ + { + char* end_of_area = NULL; + int non_contiguous_area = 0; + char** new_environ; + int i; + + /* + * check for contiguous argv strings + */ + for (i = 0; (non_contiguous_area == 0) && (i < argc); i++) + { + if (i != 0 && end_of_area + 1 != argv[i]) + non_contiguous_area = 1; + end_of_area = argv[i] + strlen(argv[i]); + } + + /* + * check for contiguous environ strings following argv + */ + for (i = 0; (non_contiguous_area == 0) && (environ[i] != NULL); i++) + { + if (end_of_area + 1 != environ[i]) + non_contiguous_area = 1; + end_of_area = environ[i] + strlen(environ[i]); + } + + if (non_contiguous_area != 0) + goto clobber_error; + + ps_buffer = argv[0]; + ps_buffer_size = end_of_area - argv[0]; + + /* + * move the environment out of the way + */ + new_environ = (char **) malloc((i + 1) * sizeof(char *)); + for (i = 0; environ[i] != NULL; i++) + { + new_environ[i] = strdup(environ[i]); + if (!new_environ[i]) + goto clobber_error; + } + new_environ[i] = NULL; + environ = new_environ; + + } +#endif /* PS_USE_CLOBBER_ARGV */ + +#if defined(PS_USE_CHANGE_ARGV) || defined(PS_USE_CLOBBER_ARGV) + /* + * If we're going to change the original argv[] then make a copy for + * argument parsing purposes. + * + * (NB: do NOT think to remove the copying of argv[]! + * On some platforms, getopt() keeps pointers into the argv array, and + * will get horribly confused when it is re-called to analyze a subprocess' + * argument string if the argv storage has been clobbered meanwhile. + * Other platforms have other dependencies on argv[].) + */ + { + char** new_argv; + int i; + + new_argv = (char **) malloc((argc + 1) * sizeof(char *)); + for (i = 0; i < argc; i++) + { + new_argv[i] = strdup(argv[i]); + if (!new_argv[i]) + goto clobber_error; + } + new_argv[argc] = NULL; + +#if defined(DARWIN) + /* + * Darwin (and perhaps other NeXT-derived platforms?) has a static + * copy of the argv pointer, which we may fix like so: + */ + *_NSGetArgv() = new_argv; +#endif + + argv = new_argv; + + } +#endif /* PS_USE_CHANGE_ARGV or PS_USE_CLOBBER_ARGV */ + +#if defined(PS_USE_CLOBBER_ARGV) + { + /* make extra argv slots point at end_of_area (a NUL) */ + int i; + for (i = 1; i < save_argc; i++) + save_argv[i] = ps_buffer + ps_buffer_size; + } +#endif /* PS_USE_CLOBBER_ARGV */ + +#ifdef PS_USE_CHANGE_ARGV + save_argv[0] = ps_buffer; // ps_buffer here is a static const array of size PS_BUFFER_SIZE + save_argv[1] = NULL; +#endif /* PS_USE_CHANGE_ARGV */ + + return argv; + +#if defined(PS_USE_CHANGE_ARGV) || defined(PS_USE_CLOBBER_ARGV) +clobber_error: + /* probably can't happen?! + * if we ever get here, argv still points to originally passed + * in argument + */ + ps_buffer = NULL; + ps_buffer_size = 0; + return argv; +#endif /* PS_USE_CHANGE_ARGV or PS_USE_CLOBBER_ARGV */ +} + +/* + * Returns PS_TITLE_SUCCESS if the OS supports this functionality + * and the init function was called. + * Otherwise returns NOT_AVAILABLE or NOT_INITIALIZED + */ +int is_ps_title_available() +{ +#ifdef PS_USE_NONE + return PS_TITLE_NOT_AVAILABLE; // disabled functionality +#endif + + if (!save_argv) + return PS_TITLE_NOT_INITIALIZED; + +#ifdef PS_USE_CLOBBER_ARGV + if (!ps_buffer) + return PS_TITLE_BUFFER_NOT_AVAILABLE; +#endif /* PS_USE_CLOBBER_ARGV */ + + return PS_TITLE_SUCCESS; +} + +/* + * Convert error codes into error strings + */ +const char* ps_title_errno(int rc) +{ + switch(rc) + { + case PS_TITLE_SUCCESS: + return "Success"; + + case PS_TITLE_NOT_AVAILABLE: + return "Not available on this OS"; + + case PS_TITLE_NOT_INITIALIZED: + return "Not initialized correctly"; + + case PS_TITLE_BUFFER_NOT_AVAILABLE: + return "Buffer not contiguous"; + } + + return "Unknown error code"; +} + +/* + * Set a new process title. + * Returns the appropriate error code if if there's an error + * (like the functionality is compile time disabled, or the + * save_ps_args() was not called. + * Else returns 0 on success. + */ +int set_ps_title(const char* title) +{ + int rc = is_ps_title_available(); + if (rc != PS_TITLE_SUCCESS) + return rc; + + strncpy(ps_buffer, title, ps_buffer_size); + ps_buffer[ps_buffer_size - 1] = '\0'; + ps_buffer_cur_len = strlen(ps_buffer); + +#ifdef PS_USE_SETPROCTITLE + setproctitle("%s", ps_buffer); +#endif + +#ifdef PS_USE_PSTAT + { + union pstun pst; + + pst.pst_command = ps_buffer; + pstat(PSTAT_SETCMD, pst, ps_buffer_cur_len, 0, 0); + } +#endif /* PS_USE_PSTAT */ + +#ifdef PS_USE_PS_STRINGS + PS_STRINGS->ps_nargvstr = 1; + PS_STRINGS->ps_argvstr = ps_buffer; +#endif /* PS_USE_PS_STRINGS */ + +#ifdef PS_USE_CLOBBER_ARGV + /* pad unused memory */ + if (ps_buffer_cur_len < ps_buffer_size) + { + memset(ps_buffer + ps_buffer_cur_len, PS_PADDING, + ps_buffer_size - ps_buffer_cur_len); + } +#endif /* PS_USE_CLOBBER_ARGV */ + +#ifdef PS_USE_WIN32 + { + /* + * Win32 does not support showing any changed arguments. To make it at + * all possible to track which backend is doing what, we create a + * named object that can be viewed with for example Process Explorer. + */ + static HANDLE ident_handle = INVALID_HANDLE_VALUE; + char name[PS_BUFFER_SIZE + 32]; + + if (ident_handle != INVALID_HANDLE_VALUE) + CloseHandle(ident_handle); + + sprintf(name, "php-process(%d): %s", _getpid(), ps_buffer); + + ident_handle = CreateEvent(NULL, TRUE, FALSE, name); + } +#endif /* PS_USE_WIN32 */ + + return PS_TITLE_SUCCESS; +} + +/* + * Returns the current ps_buffer value into string. On some platforms + * the string will not be null-terminated, so return the effective + * length into *displen. + * The return code indicates the error. + */ +int get_ps_title(int *displen, const char** string) +{ + int rc = is_ps_title_available(); + if (rc != PS_TITLE_SUCCESS) + return rc; + + *displen = ps_buffer_cur_len; + *string = ps_buffer; + return PS_TITLE_SUCCESS; +} + diff --git a/sapi/cli/ps_title.h b/sapi/cli/ps_title.h new file mode 100644 index 0000000000000..00eddfd48b740 --- /dev/null +++ b/sapi/cli/ps_title.h @@ -0,0 +1,39 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Keyur Govande | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifndef PS_TITLE_HEADER +#define PS_TITLE_HEADER + +#define PS_TITLE_SUCCESS 0 +#define PS_TITLE_NOT_AVAILABLE 1 +#define PS_TITLE_NOT_INITIALIZED 2 +#define PS_TITLE_BUFFER_NOT_AVAILABLE 3 + +extern char** save_ps_args(int argc, char** argv); + +extern int set_ps_title(const char* new_str); + +extern int get_ps_title(int* displen, const char** string); + +extern const char* ps_title_errno(int rc); + +extern int is_ps_title_available(); + +#endif // PS_TITLE_HEADER diff --git a/sapi/cli/tests/cli_process_title.phpt b/sapi/cli/tests/cli_process_title.phpt new file mode 100644 index 0000000000000..8ee2894e71ccf --- /dev/null +++ b/sapi/cli/tests/cli_process_title.phpt @@ -0,0 +1,46 @@ +--TEST-- +Check cli_process_title support +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +*** Testing setting the process title *** +Successfully set title +Successfully verified title using ps +Successfully verified title using get From c4c53b68dc72057984051b077532ce07dddd5acb Mon Sep 17 00:00:00 2001 From: Keyur Govande Date: Fri, 22 Feb 2013 00:50:59 +0000 Subject: [PATCH 02/10] Correct method signature in comments; fix return statement --- sapi/cli/php_cli_process_title.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sapi/cli/php_cli_process_title.c b/sapi/cli/php_cli_process_title.c index 8631f82893c4d..d31a56d2e43fe 100644 --- a/sapi/cli/php_cli_process_title.c +++ b/sapi/cli/php_cli_process_title.c @@ -35,7 +35,7 @@ PHP_FUNCTION(cli_set_process_title) int rc; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &title, &title_len) == FAILURE) { - RETURN_FALSE; + return; } rc = set_ps_title(title); @@ -48,7 +48,7 @@ PHP_FUNCTION(cli_set_process_title) } /* }}} */ -/* {{{ proto string cli_get_process_title(string arg) +/* {{{ proto string cli_get_process_title() Return a string with the current process title. NULL if error. */ PHP_FUNCTION(cli_get_process_title) { From 028dd66fca404398007bcd4ad01752418f7b171f Mon Sep 17 00:00:00 2001 From: Keyur Govande Date: Fri, 22 Feb 2013 19:07:28 +0000 Subject: [PATCH 03/10] Check malloc rc (hat-tip Christopher Jones) --- sapi/cli/ps_title.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sapi/cli/ps_title.c b/sapi/cli/ps_title.c index 610def73c5f15..81ea6c78f864d 100644 --- a/sapi/cli/ps_title.c +++ b/sapi/cli/ps_title.c @@ -177,6 +177,8 @@ char** save_ps_args(int argc, char** argv) * move the environment out of the way */ new_environ = (char **) malloc((i + 1) * sizeof(char *)); + if (!new_environ) + goto clobber_error; for (i = 0; environ[i] != NULL; i++) { new_environ[i] = strdup(environ[i]); @@ -205,6 +207,8 @@ char** save_ps_args(int argc, char** argv) int i; new_argv = (char **) malloc((argc + 1) * sizeof(char *)); + if (!new_argv) + goto clobber_error; for (i = 0; i < argc; i++) { new_argv[i] = strdup(argv[i]); From 4d2b6f5f078919c473072ee9b60b170ec2405ea7 Mon Sep 17 00:00:00 2001 From: Keyur Govande Date: Fri, 22 Feb 2013 19:21:58 +0000 Subject: [PATCH 04/10] Reset everything when memory alloc fails --- sapi/cli/ps_title.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sapi/cli/ps_title.c b/sapi/cli/ps_title.c index 81ea6c78f864d..7ff1b9dab800c 100644 --- a/sapi/cli/ps_title.c +++ b/sapi/cli/ps_title.c @@ -252,6 +252,8 @@ char** save_ps_args(int argc, char** argv) * if we ever get here, argv still points to originally passed * in argument */ + save_argv = NULL; + save_argc = 0; ps_buffer = NULL; ps_buffer_size = 0; return argv; From 9a881e0108c58c165082ee866605fe932dafc67f Mon Sep 17 00:00:00 2001 From: Keyur Govande Date: Fri, 22 Feb 2013 21:45:04 +0000 Subject: [PATCH 05/10] Clean up allocated argv/environ if applicable. This will ensure clean valgrind runs. (hat-tip to Christopher Jones for the idea) --- sapi/cli/php_cli.c | 5 +++++ sapi/cli/ps_title.c | 51 ++++++++++++++++++++++++++++++++++++++------- sapi/cli/ps_title.h | 2 ++ 3 files changed, 50 insertions(+), 8 deletions(-) diff --git a/sapi/cli/php_cli.c b/sapi/cli/php_cli.c index ffb6a69c4cc9d..e3e9a5ef311f2 100644 --- a/sapi/cli/php_cli.c +++ b/sapi/cli/php_cli.c @@ -1398,6 +1398,11 @@ int main(int argc, char *argv[]) tsrm_shutdown(); #endif + /* + * Do not move this de-initialization. It needs to happen right before + * exiting. + */ + cleanup_ps_args(argv); exit(exit_status); } /* }}} */ diff --git a/sapi/cli/ps_title.c b/sapi/cli/ps_title.c index 7ff1b9dab800c..c0fdba9312ee5 100644 --- a/sapi/cli/ps_title.c +++ b/sapi/cli/ps_title.c @@ -122,14 +122,11 @@ static char** save_argv; /* - * Call this early in startup to save the original argc/argv values. - * If needed, we make a copy of the original argv[] array to preserve it - * from being clobbered by subsequent ps_display actions. - * - * (The original argv[] will not be overwritten by this routine. - * Also, the physical location of the environment strings may be moved, - * so this should be called before any code that might try to hang onto a - * getenv() result.) + * Call this method early, before any code has used the original argv passed in + * from main(). + * If needed, this code will make deep copies of argv and environ and return + * these to the caller for further use. The original argv is then 'clobbered' + * to store the process title. */ char** save_ps_args(int argc, char** argv) { @@ -388,3 +385,41 @@ int get_ps_title(int *displen, const char** string) return PS_TITLE_SUCCESS; } +/* + * Clean up the allocated argv and environ if applicable. Only call + * this right before exiting. + * This isn't needed per-se because the OS will clean-up anyway, but + * having and calling this will ensure Valgrind doesn't output 'false + * positives'. + */ +void cleanup_ps_args(char **argv) +{ +#ifndef PS_USE_NONE + if (save_argv) + { + save_argv = NULL; + save_argc = 0; + +#ifdef PS_USE_CLOBBER_ARGV + { + // clean up environ + int i; + for (i = 0; environ[i] != NULL; i++) + free(environ[i]); + free(environ); + } +#endif /* PS_USE_CLOBBER_ARGV */ + +#if defined(PS_USE_CHANGE_ARGV) || defined(PS_USE_CLOBBER_ARGV) + { + int i; + for (i=0; argv[i] != NULL; i++) + free(argv[i]); + free(argv); + } +#endif /* PS_USE_CHANGE_ARGV or PS_USE_CLOBBER_ARGV */ + } +#endif /* PS_USE_NONE */ + + return; +} diff --git a/sapi/cli/ps_title.h b/sapi/cli/ps_title.h index 00eddfd48b740..de1627487a0ac 100644 --- a/sapi/cli/ps_title.h +++ b/sapi/cli/ps_title.h @@ -36,4 +36,6 @@ extern const char* ps_title_errno(int rc); extern int is_ps_title_available(); +extern void cleanup_ps_args(char **argv); + #endif // PS_TITLE_HEADER From b54f25ca90f3176210ffddaa6cb1c484af236fe7 Mon Sep 17 00:00:00 2001 From: Keyur Govande Date: Fri, 22 Feb 2013 22:07:14 +0000 Subject: [PATCH 06/10] Fixes for review comments by auroraeosrose --- sapi/cli/php_cli.c | 2 +- sapi/cli/php_cli_process_title.c | 4 ++++ sapi/cli/php_cli_process_title.h | 3 +++ sapi/cli/php_cli_server.c | 2 +- sapi/cli/ps_title.c | 9 ++++----- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/sapi/cli/php_cli.c b/sapi/cli/php_cli.c index e3e9a5ef311f2..4b8bae7f78a9c 100644 --- a/sapi/cli/php_cli.c +++ b/sapi/cli/php_cli.c @@ -482,7 +482,7 @@ ZEND_END_ARG_INFO() static const zend_function_entry additional_functions[] = { ZEND_FE(dl, arginfo_dl) PHP_FE(cli_set_process_title, arginfo_cli_set_process_title) - PHP_FE(cli_get_process_title, NULL) + PHP_FE(cli_get_process_title, arginfo_cli_get_process_title) {NULL, NULL, NULL} }; diff --git a/sapi/cli/php_cli_process_title.c b/sapi/cli/php_cli_process_title.c index d31a56d2e43fe..c50c412b1fe99 100644 --- a/sapi/cli/php_cli_process_title.c +++ b/sapi/cli/php_cli_process_title.c @@ -56,6 +56,10 @@ PHP_FUNCTION(cli_get_process_title) const char* title = NULL; int rc; + if (zend_parse_parameters_none() == FAILURE) { + return; + } + rc = get_ps_title(&length, &title); if (rc != PS_TITLE_SUCCESS) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "cli_get_process_title had an error: %s", ps_title_errno(rc)); diff --git a/sapi/cli/php_cli_process_title.h b/sapi/cli/php_cli_process_title.h index 62ef5673db57e..b4b0861a2ac93 100644 --- a/sapi/cli/php_cli_process_title.h +++ b/sapi/cli/php_cli_process_title.h @@ -25,6 +25,9 @@ ZEND_BEGIN_ARG_INFO(arginfo_cli_set_process_title, 0) ZEND_ARG_INFO(0, title) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO(arginfo_cli_get_process_title, 0) +ZEND_END_ARG_INFO() + PHP_FUNCTION(cli_set_process_title); PHP_FUNCTION(cli_get_process_title); diff --git a/sapi/cli/php_cli_server.c b/sapi/cli/php_cli_server.c index b124a18b9c5dc..a2b85d47b0f98 100644 --- a/sapi/cli/php_cli_server.c +++ b/sapi/cli/php_cli_server.c @@ -433,7 +433,7 @@ zend_module_entry cli_server_module_entry = { const zend_function_entry server_additional_functions[] = { PHP_FE(cli_set_process_title, arginfo_cli_set_process_title) - PHP_FE(cli_get_process_title, NULL) + PHP_FE(cli_get_process_title, arginfo_cli_get_process_title) {NULL, NULL, NULL} }; diff --git a/sapi/cli/ps_title.c b/sapi/cli/ps_title.c index c0fdba9312ee5..abc472543e477 100644 --- a/sapi/cli/ps_title.c +++ b/sapi/cli/ps_title.c @@ -48,10 +48,10 @@ extern char** environ; #endif #ifdef HAVE_SYS_PSTAT_H -#include // for HP-UX +#include /* for HP-UX */ #endif #ifdef HAVE_PS_STRINGS -#include // for old BSD +#include /* for old BSD */ #include #endif #if defined(DARWIN) @@ -237,7 +237,7 @@ char** save_ps_args(int argc, char** argv) #endif /* PS_USE_CLOBBER_ARGV */ #ifdef PS_USE_CHANGE_ARGV - save_argv[0] = ps_buffer; // ps_buffer here is a static const array of size PS_BUFFER_SIZE + save_argv[0] = ps_buffer; /* ps_buffer here is a static const array of size PS_BUFFER_SIZE */ save_argv[1] = NULL; #endif /* PS_USE_CHANGE_ARGV */ @@ -265,7 +265,7 @@ char** save_ps_args(int argc, char** argv) int is_ps_title_available() { #ifdef PS_USE_NONE - return PS_TITLE_NOT_AVAILABLE; // disabled functionality + return PS_TITLE_NOT_AVAILABLE; /* disabled functionality */ #endif if (!save_argv) @@ -402,7 +402,6 @@ void cleanup_ps_args(char **argv) #ifdef PS_USE_CLOBBER_ARGV { - // clean up environ int i; for (i = 0; environ[i] != NULL; i++) free(environ[i]); From 2cc4c525985349fdd0d041761c34e1a2072ce7b9 Mon Sep 17 00:00:00 2001 From: Keyur Govande Date: Tue, 26 Feb 2013 02:02:40 -0500 Subject: [PATCH 07/10] Use SetConsoleTitle on Windows, instead of an Event. Also update test to run on Windows using some PowerShell commands. --- sapi/cli/ps_title.c | 40 ++++++++++++++------------- sapi/cli/ps_title.h | 3 +- sapi/cli/tests/cli_process_title.phpt | 31 ++++++++++++++++----- 3 files changed, 47 insertions(+), 27 deletions(-) diff --git a/sapi/cli/ps_title.c b/sapi/cli/ps_title.c index abc472543e477..4ea294523eaef 100644 --- a/sapi/cli/ps_title.c +++ b/sapi/cli/ps_title.c @@ -105,13 +105,17 @@ extern char** environ; #define PS_PADDING ' ' #endif -#ifndef PS_USE_CLOBBER_ARGV +#ifdef PS_USE_WIN32 +static char windows_error_details[64]; +static char ps_buffer[MAX_PATH]; +static const size_t ps_buffer_size = MAX_PATH; +#elif PS_USE_CLOBBER_ARGV +static char *ps_buffer; /* will point to argv area */ +static size_t ps_buffer_size; /* space determined at run time */ +#else #define PS_BUFFER_SIZE 256 static char ps_buffer[PS_BUFFER_SIZE]; static const size_t ps_buffer_size = PS_BUFFER_SIZE; -#else -static char *ps_buffer; /* will point to argv area */ -static size_t ps_buffer_size; /* space determined at run time */ #endif static size_t ps_buffer_cur_len; /* actual string length in ps_buffer */ @@ -297,6 +301,12 @@ const char* ps_title_errno(int rc) case PS_TITLE_BUFFER_NOT_AVAILABLE: return "Buffer not contiguous"; + +#ifdef PS_USE_WIN32 + case PS_TITLE_WINDOWS_ERROR: + sprintf(windows_error_details, "Windows error code: %d", GetLastError()); + return windows_error_details; +#endif } return "Unknown error code"; @@ -348,20 +358,8 @@ int set_ps_title(const char* title) #ifdef PS_USE_WIN32 { - /* - * Win32 does not support showing any changed arguments. To make it at - * all possible to track which backend is doing what, we create a - * named object that can be viewed with for example Process Explorer. - */ - static HANDLE ident_handle = INVALID_HANDLE_VALUE; - char name[PS_BUFFER_SIZE + 32]; - - if (ident_handle != INVALID_HANDLE_VALUE) - CloseHandle(ident_handle); - - sprintf(name, "php-process(%d): %s", _getpid(), ps_buffer); - - ident_handle = CreateEvent(NULL, TRUE, FALSE, name); + if (!SetConsoleTitle(ps_buffer)) + return PS_TITLE_WINDOWS_ERROR; } #endif /* PS_USE_WIN32 */ @@ -374,12 +372,16 @@ int set_ps_title(const char* title) * length into *displen. * The return code indicates the error. */ -int get_ps_title(int *displen, const char** string) +int get_ps_title(size_t *displen, const char** string) { int rc = is_ps_title_available(); if (rc != PS_TITLE_SUCCESS) return rc; +#ifdef PS_USE_WIN32 + if (!(ps_buffer_cur_len = GetConsoleTitle(ps_buffer, ps_buffer_size))) + return PS_TITLE_WINDOWS_ERROR; +#endif *displen = ps_buffer_cur_len; *string = ps_buffer; return PS_TITLE_SUCCESS; diff --git a/sapi/cli/ps_title.h b/sapi/cli/ps_title.h index de1627487a0ac..308652f6bc3a4 100644 --- a/sapi/cli/ps_title.h +++ b/sapi/cli/ps_title.h @@ -25,12 +25,13 @@ #define PS_TITLE_NOT_AVAILABLE 1 #define PS_TITLE_NOT_INITIALIZED 2 #define PS_TITLE_BUFFER_NOT_AVAILABLE 3 +#define PS_TITLE_WINDOWS_ERROR 4 extern char** save_ps_args(int argc, char** argv); extern int set_ps_title(const char* new_str); -extern int get_ps_title(int* displen, const char** string); +extern int get_ps_title(size_t* displen, const char** string); extern const char* ps_title_errno(int rc); diff --git a/sapi/cli/tests/cli_process_title.phpt b/sapi/cli/tests/cli_process_title.phpt index 8ee2894e71ccf..5998a931f98c9 100644 --- a/sapi/cli/tests/cli_process_title.phpt +++ b/sapi/cli/tests/cli_process_title.phpt @@ -1,21 +1,22 @@ --TEST-- Check cli_process_title support ---SKIPIF-- - --FILE-- Date: Tue, 26 Feb 2013 07:23:18 +0000 Subject: [PATCH 08/10] Fix typo with #elif. PHP strings don't work with size_t. Cast from size_t to int. --- sapi/cli/ps_title.c | 6 +++--- sapi/cli/ps_title.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sapi/cli/ps_title.c b/sapi/cli/ps_title.c index 4ea294523eaef..a2e47f0316562 100644 --- a/sapi/cli/ps_title.c +++ b/sapi/cli/ps_title.c @@ -109,7 +109,7 @@ extern char** environ; static char windows_error_details[64]; static char ps_buffer[MAX_PATH]; static const size_t ps_buffer_size = MAX_PATH; -#elif PS_USE_CLOBBER_ARGV +#elif defined(PS_USE_CLOBBER_ARGV) static char *ps_buffer; /* will point to argv area */ static size_t ps_buffer_size; /* space determined at run time */ #else @@ -372,7 +372,7 @@ int set_ps_title(const char* title) * length into *displen. * The return code indicates the error. */ -int get_ps_title(size_t *displen, const char** string) +int get_ps_title(int *displen, const char** string) { int rc = is_ps_title_available(); if (rc != PS_TITLE_SUCCESS) @@ -382,7 +382,7 @@ int get_ps_title(size_t *displen, const char** string) if (!(ps_buffer_cur_len = GetConsoleTitle(ps_buffer, ps_buffer_size))) return PS_TITLE_WINDOWS_ERROR; #endif - *displen = ps_buffer_cur_len; + *displen = (int)ps_buffer_cur_len; *string = ps_buffer; return PS_TITLE_SUCCESS; } diff --git a/sapi/cli/ps_title.h b/sapi/cli/ps_title.h index 308652f6bc3a4..5623653e488f1 100644 --- a/sapi/cli/ps_title.h +++ b/sapi/cli/ps_title.h @@ -31,7 +31,7 @@ extern char** save_ps_args(int argc, char** argv); extern int set_ps_title(const char* new_str); -extern int get_ps_title(size_t* displen, const char** string); +extern int get_ps_title(int* displen, const char** string); extern const char* ps_title_errno(int rc); From 3443ecee2d02b002521aeb7b548c121ccbc81508 Mon Sep 17 00:00:00 2001 From: Keyur Govande Date: Wed, 27 Feb 2013 19:54:04 +0000 Subject: [PATCH 09/10] Split test for Windows and Unix. Also fix test for Windows 8: It no longer verifies the title using PowerShell. It only executes the API calls. On Windows 7 and older though, we still continue to verify the title. --- sapi/cli/tests/cli_process_title.phpt | 63 -------------- sapi/cli/tests/cli_process_title_unix.phpt | 49 +++++++++++ sapi/cli/tests/cli_process_title_windows.phpt | 87 +++++++++++++++++++ 3 files changed, 136 insertions(+), 63 deletions(-) delete mode 100644 sapi/cli/tests/cli_process_title.phpt create mode 100644 sapi/cli/tests/cli_process_title_unix.phpt create mode 100644 sapi/cli/tests/cli_process_title_windows.phpt diff --git a/sapi/cli/tests/cli_process_title.phpt b/sapi/cli/tests/cli_process_title.phpt deleted file mode 100644 index 5998a931f98c9..0000000000000 --- a/sapi/cli/tests/cli_process_title.phpt +++ /dev/null @@ -1,63 +0,0 @@ ---TEST-- -Check cli_process_title support ---FILE-- - ---EXPECTF-- -*** Testing setting the process title *** -Successfully set title -Successfully verified title using ps -Successfully verified title using get diff --git a/sapi/cli/tests/cli_process_title_unix.phpt b/sapi/cli/tests/cli_process_title_unix.phpt new file mode 100644 index 0000000000000..c2632704c559e --- /dev/null +++ b/sapi/cli/tests/cli_process_title_unix.phpt @@ -0,0 +1,49 @@ +--TEST-- +Check cli_process_title support on Unix +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +*** Testing setting the process title *** +Successfully set title +Successfully verified title using ps +Successfully verified title using get diff --git a/sapi/cli/tests/cli_process_title_windows.phpt b/sapi/cli/tests/cli_process_title_windows.phpt new file mode 100644 index 0000000000000..c877819c1725c --- /dev/null +++ b/sapi/cli/tests/cli_process_title_windows.phpt @@ -0,0 +1,87 @@ +--TEST-- +Check cli_process_title support in Windows +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +*** Testing setting the process title *** +Successfully set title +Successfully verified title using get-process +Successfully verified title using get From 5d1313714dab797d301642601be59240ae91d5c8 Mon Sep 17 00:00:00 2001 From: Keyur Govande Date: Wed, 27 Feb 2013 23:39:03 -0500 Subject: [PATCH 10/10] Add some more title logic for Windows flavors. Test should now pass on XP, 7, 8, Server2008, Server2012. --- sapi/cli/tests/cli_process_title_windows.phpt | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/sapi/cli/tests/cli_process_title_windows.phpt b/sapi/cli/tests/cli_process_title_windows.phpt index c877819c1725c..309c09c0e192e 100644 --- a/sapi/cli/tests/cli_process_title_windows.phpt +++ b/sapi/cli/tests/cli_process_title_windows.phpt @@ -8,10 +8,10 @@ if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') --FILE--