]> granicus.if.org Git - php/commitdiff
Support for CLI process title (https://wiki.php.net/rfc/cli_process_title)
authorKeyur Govande <kgovande@gmail.com>
Thu, 7 Mar 2013 18:54:34 +0000 (18:54 +0000)
committerKeyur Govande <kgovande@gmail.com>
Thu, 7 Mar 2013 18:54:34 +0000 (18:54 +0000)
A new commit into branch 5.5

sapi/cli/config.m4
sapi/cli/config.w32
sapi/cli/php_cli.c
sapi/cli/php_cli_process_title.c [new file with mode: 0644]
sapi/cli/php_cli_process_title.h [new file with mode: 0644]
sapi/cli/php_cli_server.c
sapi/cli/php_cli_server.h
sapi/cli/ps_title.c [new file with mode: 0644]
sapi/cli/ps_title.h [new file with mode: 0644]
sapi/cli/tests/cli_process_title_unix.phpt [new file with mode: 0644]
sapi/cli/tests/cli_process_title_windows.phpt [new file with mode: 0644]

index cdfa1f7daff2aa7666e61af36aa91a27f909e181..9a1b98da46193c8f736caa20990a5e0b1aba80ac 100644 (file)
@@ -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 <machine/vmparam.h>
+#include <sys/exec.h>
+],
+[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*)
index 4d0dad58e84b49f10b0533ef0adf2e0d7d6d2831..adcbb2b496acbd46526dee6a4cbd85431288873d 100644 (file)
@@ -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");
 }
 
index c01f3705b59bc3d5f3a33269d52751be09a1865e..4b8bae7f78a9cc0a9f9b0157297e8e8e1d2a20d0 100644 (file)
@@ -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,        arginfo_cli_get_process_title)
        {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 */
@@ -1385,6 +1398,11 @@ out:
        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/php_cli_process_title.c b/sapi/cli/php_cli_process_title.c
new file mode 100644 (file)
index 0000000..c50c412
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+  +----------------------------------------------------------------------+
+  | 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;
+    }
+
+    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()
+   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;
+
+        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));
+                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 (file)
index 0000000..b4b0861
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+  +----------------------------------------------------------------------+
+  | 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()
+
+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);
+
+#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
+ */
index 1d2ceff691a569d67bb89bfdff953082a1995774..a2b85d47b0f98437d7a964d903c4cf329da2355c 100644 (file)
 #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,        arginfo_cli_get_process_title)
+       {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) {
index ed716f99f809eb624771002dcfb1ccc05c68a96f..9a29626c7f05695eab2df97965f7483c7f096694 100644 (file)
@@ -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 (file)
index 0000000..a2e47f0
--- /dev/null
@@ -0,0 +1,426 @@
+/*
+ * 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 <stdio.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef PHP_WIN32
+#include "config.w32.h"
+#include <windows.h>
+#include <process.h>
+#else
+#include "php_config.h"
+extern char** environ;
+#endif
+
+#ifdef HAVE_SYS_PSTAT_H
+#include <sys/pstat.h> /* for HP-UX */
+#endif
+#ifdef HAVE_PS_STRINGS
+#include <machine/vmparam.h> /* for old BSD */
+#include <sys/exec.h>
+#endif
+#if defined(DARWIN)
+#include <crt_externs.h>
+#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
+
+#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 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
+#define PS_BUFFER_SIZE 256
+static char ps_buffer[PS_BUFFER_SIZE];
+static const size_t ps_buffer_size = PS_BUFFER_SIZE;
+#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 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)
+{
+    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 *));
+        if (!new_environ)
+            goto clobber_error;
+        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 *));
+        if (!new_argv)
+            goto clobber_error;
+        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
+     */
+    save_argv = NULL;
+    save_argc = 0;
+    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";
+
+#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";
+}
+
+/*
+ * 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
+    {
+        if (!SetConsoleTitle(ps_buffer))
+            return PS_TITLE_WINDOWS_ERROR;
+    }
+#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;
+
+#ifdef PS_USE_WIN32
+    if (!(ps_buffer_cur_len = GetConsoleTitle(ps_buffer, ps_buffer_size)))
+        return PS_TITLE_WINDOWS_ERROR;
+#endif
+    *displen = (int)ps_buffer_cur_len;
+    *string = ps_buffer;
+    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
+        {
+            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
new file mode 100644 (file)
index 0000000..5623653
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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 <kgovande@gmail.com>                          |
+   +----------------------------------------------------------------------+
+ */
+
+/* $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
+#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 const char* ps_title_errno(int rc);
+
+extern int is_ps_title_available();
+
+extern void cleanup_ps_args(char **argv);
+
+#endif // PS_TITLE_HEADER
diff --git a/sapi/cli/tests/cli_process_title_unix.phpt b/sapi/cli/tests/cli_process_title_unix.phpt
new file mode 100644 (file)
index 0000000..c263270
--- /dev/null
@@ -0,0 +1,49 @@
+--TEST--
+Check cli_process_title support on Unix
+--SKIPIF--
+<?php
+if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN')
+  die("skip");
+?>
+--FILE--
+<?php
+echo "*** Testing setting the process title ***\n";
+
+$set_title = $original_title = uniqid("title", true);
+$pid = getmypid();
+
+if (cli_set_process_title($original_title) === true)
+  echo "Successfully set title\n";
+
+$ps_output = shell_exec("ps -p $pid -o command | tail -n 1");
+
+if ($ps_output === null)
+{
+  echo "ps failed\n";
+  die();
+}
+
+$loaded_title = trim($ps_output);
+if (strpos(strtoupper(substr(PHP_OS, 0, 13)), "BSD") !== false)
+{
+  // Fix up title for BSD
+  $set_title = "php: $original_title (php)";
+}
+
+if ($loaded_title == $set_title)
+  echo "Successfully verified title using ps\n";
+else
+  echo "Actually loaded from ps: $loaded_title\n";
+
+$read_title = cli_get_process_title();
+if ($read_title == $original_title)
+  echo "Successfully verified title using get\n";
+else
+  echo "Actually loaded from get: $read_title\n";
+
+?>
+--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 (file)
index 0000000..309c09c
--- /dev/null
@@ -0,0 +1,82 @@
+--TEST--
+Check cli_process_title support in Windows
+--SKIPIF--
+<?php
+if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')
+  die("skip");
+?>
+--FILE--
+<?php
+
+// On Windows 8 and Server 2012, this test does not work the same way. When the PowerShell
+// command "get-process" is executed using shell_exec, it overwrites the ConsoleTitle with
+// "Windows PowerShell" and this title ONLY clears away when the php.exe process exits
+// i.e. the test finishes.
+// On older versions like Windows 7 though, running the command appends
+// "Windows PowerShell" to the ConsoleTitle temporarily and the title reverts
+// back to the original once shell_exec is done.
+// Hence on Windows 8, we don't verify that the title is actually set by
+// cli_set_process_title(). We're only making the API calls to ensure there are
+// no warnings/errors.
+
+$is_windows8 = false;
+$ps_output = shell_exec("PowerShell \"(Get-Host).UI.RawUI.WindowTitle\"");
+if ($ps_output === null)
+{
+  echo "Get-Host failed\n";
+  die();
+}
+
+$ps_output = trim($ps_output);
+if (($ps_output == "Windows PowerShell") || ($ps_output == "Administrator: Windows PowerShell"))
+  $is_windows8 = true;
+
+echo "*** Testing setting the process title ***\n";
+
+$original_title = uniqid("title", true);
+$pid = getmypid();
+
+if (cli_set_process_title($original_title) === true)
+  echo "Successfully set title\n";
+
+if ($is_windows8)
+{
+  $loaded_title = $original_title;
+}
+else
+{
+  $loaded_title = shell_exec("PowerShell \"get-process cmd*,powershell* | Select-Object mainWindowTitle | ft -hide\"");
+
+  if ($loaded_title === null)
+  {
+    echo "Reading title using get-process failed\n";
+    die();
+  }
+
+  // Kind of convoluted. So when the test is run on Windows 7 or older, the console where
+  // the run-tests.php is executed forks a php.exe, which forks a cmd.exe, which then forks
+  // a final php.exe to run the actual test. But the console title is set for the original console.
+  // I couldn't figure out a good way to navigate this, so we're "grep'ing" all possible
+  // console windows for our very unique title. It should occur exactly once in the grep
+  // output.
+  if (substr_count($loaded_title, $original_title, 0) == 1)
+    $loaded_title = $original_title;
+}
+
+if ($loaded_title == $original_title)
+  echo "Successfully verified title using get-process\n";
+else
+  echo "Actually loaded from get-process: $loaded_title\n";
+
+$read_title = cli_get_process_title();
+if (substr_count($read_title, $original_title, 0) == 1)
+  echo "Successfully verified title using get\n";
+else
+  echo "Actually loaded from get: $read_title\n";
+
+?>
+--EXPECTF--
+*** Testing setting the process title ***
+Successfully set title
+Successfully verified title using get-process
+Successfully verified title using get
\ No newline at end of file