root/sapi/cli/ps_title.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. save_ps_args
  2. is_ps_title_available
  3. ps_title_errno
  4. set_ps_title
  5. get_ps_title
  6. cleanup_ps_args

   1 /*
   2  * PostgreSQL is released under the PostgreSQL License, a liberal Open Source
   3  * license, similar to the BSD or MIT licenses.
   4  * PostgreSQL Database Management System (formerly known as Postgres, then as
   5  * Postgres95)
   6  *
   7  * Portions Copyright (c) 1996-2015, The PostgreSQL Global Development Group
   8  *
   9  * Portions Copyright (c) 1994, The Regents of the University of California
  10  *
  11  * Permission to use, copy, modify, and distribute this software and its
  12  * documentation for any purpose, without fee, and without a written
  13  * agreement is hereby granted, provided that the above copyright notice
  14  * and this paragraph and the following two paragraphs appear in all copies.
  15  *
  16  * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
  17  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
  18  * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
  19  * EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
  20  * SUCH DAMAGE.
  21  *
  22  * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
  23  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  24  * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
  25  * "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
  26  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  27  *
  28  * The following code is adopted from the PostgreSQL's ps_status(.h/.c).
  29  */
  30 
  31 #include "ps_title.h"
  32 #include <stdio.h>
  33 
  34 #ifdef HAVE_UNISTD_H
  35 #include <unistd.h>
  36 #endif
  37 
  38 #include <string.h>
  39 #include <stdlib.h>
  40 
  41 #ifdef PHP_WIN32
  42 #include "config.w32.h"
  43 #include <windows.h>
  44 #include <process.h>
  45 #else
  46 #include "php_config.h"
  47 extern char** environ;
  48 #endif
  49 
  50 #ifdef HAVE_SYS_PSTAT_H
  51 #include <sys/pstat.h> /* for HP-UX */
  52 #endif
  53 #ifdef HAVE_PS_STRINGS
  54 #include <machine/vmparam.h> /* for old BSD */
  55 #include <sys/exec.h>
  56 #endif
  57 #if defined(DARWIN)
  58 #include <crt_externs.h>
  59 #endif
  60 
  61 /*
  62  * Ways of updating ps display:
  63  *
  64  * PS_USE_SETPROCTITLE
  65  *         use the function setproctitle(const char *, ...)
  66  *         (newer BSD systems)
  67  * PS_USE_PSTAT
  68  *         use the pstat(PSTAT_SETCMD, )
  69  *         (HPUX)
  70  * PS_USE_PS_STRINGS
  71  *         assign PS_STRINGS->ps_argvstr = "string"
  72  *         (some BSD systems)
  73  * PS_USE_CHANGE_ARGV
  74  *         assign argv[0] = "string"
  75  *         (some other BSD systems)
  76  * PS_USE_CLOBBER_ARGV
  77  *         write over the argv and environment area
  78  *         (Linux and most SysV-like systems)
  79  * PS_USE_WIN32
  80  *         push the string out as the name of a Windows event
  81  * PS_USE_NONE
  82  *         don't update ps display
  83  *         (This is the default, as it is safest.)
  84  */
  85 #if defined(HAVE_SETPROCTITLE)
  86 #define PS_USE_SETPROCTITLE
  87 #elif defined(HAVE_SYS_PSTAT_H) && defined(PSTAT_SETCMD)
  88 #define PS_USE_PSTAT
  89 #elif defined(HAVE_PS_STRINGS)
  90 #define PS_USE_PS_STRINGS
  91 #elif defined(BSD) && !defined(DARWIN)
  92 #define PS_USE_CHANGE_ARGV
  93 #elif defined(__linux__) || defined(_AIX) || defined(__sgi) || (defined(sun) && !defined(BSD)) || defined(ultrix) || defined(__osf__) || defined(DARWIN)
  94 #define PS_USE_CLOBBER_ARGV
  95 #elif defined(PHP_WIN32)
  96 #define PS_USE_WIN32
  97 #else
  98 #define PS_USE_NONE
  99 #endif
 100 
 101 /* Different systems want the buffer padded differently */
 102 #if defined(_AIX) || defined(__linux__) || defined(DARWIN)
 103 #define PS_PADDING '\0'
 104 #else
 105 #define PS_PADDING ' '
 106 #endif
 107 
 108 #ifdef PS_USE_WIN32
 109 static char windows_error_details[64];
 110 static char ps_buffer[MAX_PATH];
 111 static const size_t ps_buffer_size = MAX_PATH;
 112 #elif defined(PS_USE_CLOBBER_ARGV)
 113 static char *ps_buffer;         /* will point to argv area */
 114 static size_t ps_buffer_size;   /* space determined at run time */
 115 static char *empty_environ[] = {0}; /* empty environment */
 116 #else
 117 #define PS_BUFFER_SIZE 256
 118 static char ps_buffer[PS_BUFFER_SIZE];
 119 static const size_t ps_buffer_size = PS_BUFFER_SIZE;
 120 #endif
 121 
 122 static size_t ps_buffer_cur_len; /* actual string length in ps_buffer */
 123 
 124 /* save the original argv[] location here */
 125 static int save_argc;
 126 static char** save_argv;
 127 
 128 /* 
 129  * This holds the 'locally' allocated environ from the save_ps_args method.
 130  * This is subsequently free'd at exit.
 131  */
 132 static char** frozen_environ, **new_environ;
 133 
 134 /*
 135  * Call this method early, before any code has used the original argv passed in
 136  * from main().
 137  * If needed, this code will make deep copies of argv and environ and return
 138  * these to the caller for further use. The original argv is then 'clobbered'
 139  * to store the process title.
 140  */
 141 char** save_ps_args(int argc, char** argv)
 142 {
 143     save_argc = argc;
 144     save_argv = argv;
 145 
 146 #if defined(PS_USE_CLOBBER_ARGV)
 147     /*
 148      * If we're going to overwrite the argv area, count the available space.
 149      * Also move the environment to make additional room.
 150      */
 151     {
 152         char* end_of_area = NULL;
 153         int non_contiguous_area = 0;
 154         int i;
 155 
 156         /*
 157          * check for contiguous argv strings
 158          */
 159         for (i = 0; (non_contiguous_area == 0) && (i < argc); i++)
 160         {
 161             if (i != 0 && end_of_area + 1 != argv[i])
 162                 non_contiguous_area = 1;
 163             end_of_area = argv[i] + strlen(argv[i]);
 164         }
 165 
 166         /*
 167          * check for contiguous environ strings following argv
 168          */
 169         for (i = 0; (non_contiguous_area == 0) && (environ[i] != NULL); i++)
 170         {
 171             if (end_of_area + 1 != environ[i])
 172                 non_contiguous_area = 1;
 173             end_of_area = environ[i] + strlen(environ[i]);
 174         }
 175 
 176         if (non_contiguous_area != 0)
 177             goto clobber_error;
 178 
 179         ps_buffer = argv[0];
 180         ps_buffer_size = end_of_area - argv[0];
 181 
 182         /*
 183          * move the environment out of the way
 184          */
 185         new_environ = (char **) malloc((i + 1) * sizeof(char *));
 186         frozen_environ = (char **) malloc((i + 1) * sizeof(char *));
 187         if (!new_environ || !frozen_environ)
 188             goto clobber_error;
 189         for (i = 0; environ[i] != NULL; i++)
 190         {
 191             new_environ[i] = strdup(environ[i]);
 192             if (!new_environ[i])
 193                 goto clobber_error;
 194         }
 195         new_environ[i] = NULL;
 196         environ = new_environ;
 197         memcpy((char *)frozen_environ, (char *)new_environ, sizeof(char *) * (i + 1));
 198 
 199     }
 200 #endif /* PS_USE_CLOBBER_ARGV */
 201 
 202 #if defined(PS_USE_CHANGE_ARGV) || defined(PS_USE_CLOBBER_ARGV)
 203     /*
 204      * If we're going to change the original argv[] then make a copy for
 205      * argument parsing purposes.
 206      *
 207      * (NB: do NOT think to remove the copying of argv[]!
 208      * On some platforms, getopt() keeps pointers into the argv array, and
 209      * will get horribly confused when it is re-called to analyze a subprocess'
 210      * argument string if the argv storage has been clobbered meanwhile.
 211      * Other platforms have other dependencies on argv[].)
 212      */
 213     {
 214         char** new_argv;
 215         int i;
 216 
 217         new_argv = (char **) malloc((argc + 1) * sizeof(char *));
 218         if (!new_argv)
 219             goto clobber_error;
 220         for (i = 0; i < argc; i++)
 221         {
 222             new_argv[i] = strdup(argv[i]);
 223             if (!new_argv[i])
 224                 goto clobber_error;
 225         }
 226         new_argv[argc] = NULL;
 227 
 228 #if defined(DARWIN)
 229         /*
 230          * Darwin (and perhaps other NeXT-derived platforms?) has a static
 231          * copy of the argv pointer, which we may fix like so:
 232          */
 233         *_NSGetArgv() = new_argv;
 234 #endif
 235 
 236         argv = new_argv;
 237 
 238     }
 239 #endif /* PS_USE_CHANGE_ARGV or PS_USE_CLOBBER_ARGV */
 240 
 241 #if defined(PS_USE_CLOBBER_ARGV)
 242     {
 243         /* make extra argv slots point at end_of_area (a NUL) */
 244         int i;
 245         for (i = 1; i < save_argc; i++)
 246             save_argv[i] = ps_buffer + ps_buffer_size;
 247     }
 248 #endif /* PS_USE_CLOBBER_ARGV */
 249 
 250 #ifdef PS_USE_CHANGE_ARGV
 251     save_argv[0] = ps_buffer; /* ps_buffer here is a static const array of size PS_BUFFER_SIZE */
 252     save_argv[1] = NULL;
 253 #endif /* PS_USE_CHANGE_ARGV */
 254 
 255     return argv;
 256 
 257 #if defined(PS_USE_CHANGE_ARGV) || defined(PS_USE_CLOBBER_ARGV)
 258 clobber_error:
 259     /* probably can't happen?!
 260      * if we ever get here, argv still points to originally passed
 261      * in argument
 262      */
 263     save_argv = NULL;
 264     save_argc = 0;
 265     ps_buffer = NULL;
 266     ps_buffer_size = 0;
 267     return argv;
 268 #endif /* PS_USE_CHANGE_ARGV or PS_USE_CLOBBER_ARGV */
 269 }
 270 
 271 /*
 272  * Returns PS_TITLE_SUCCESS if the OS supports this functionality
 273  * and the init function was called.
 274  * Otherwise returns NOT_AVAILABLE or NOT_INITIALIZED
 275  */
 276 int is_ps_title_available()
 277 {
 278 #ifdef PS_USE_NONE
 279     return PS_TITLE_NOT_AVAILABLE; /* disabled functionality */
 280 #endif
 281 
 282     if (!save_argv)
 283         return PS_TITLE_NOT_INITIALIZED;
 284 
 285 #ifdef PS_USE_CLOBBER_ARGV
 286     if (!ps_buffer)
 287         return PS_TITLE_BUFFER_NOT_AVAILABLE;
 288 #endif /* PS_USE_CLOBBER_ARGV */
 289 
 290     return PS_TITLE_SUCCESS;
 291 }
 292 
 293 /*
 294  * Convert error codes into error strings
 295  */
 296 const char* ps_title_errno(int rc)
 297 {
 298     switch(rc)
 299     {
 300     case PS_TITLE_SUCCESS:
 301         return "Success";
 302 
 303     case PS_TITLE_NOT_AVAILABLE:
 304         return "Not available on this OS";
 305 
 306     case PS_TITLE_NOT_INITIALIZED:
 307         return "Not initialized correctly";
 308 
 309     case PS_TITLE_BUFFER_NOT_AVAILABLE:
 310         return "Buffer not contiguous";
 311 
 312 #ifdef PS_USE_WIN32
 313     case PS_TITLE_WINDOWS_ERROR:
 314         sprintf(windows_error_details, "Windows error code: %d", GetLastError());
 315         return windows_error_details;
 316 #endif
 317     }
 318 
 319     return "Unknown error code";
 320 }
 321 
 322 /*
 323  * Set a new process title.
 324  * Returns the appropriate error code if if there's an error
 325  * (like the functionality is compile time disabled, or the
 326  * save_ps_args() was not called.
 327  * Else returns 0 on success.
 328  */
 329 int set_ps_title(const char* title)
 330 {
 331     int rc = is_ps_title_available();
 332     if (rc != PS_TITLE_SUCCESS)
 333         return rc;
 334 
 335     strncpy(ps_buffer, title, ps_buffer_size);
 336     ps_buffer[ps_buffer_size - 1] = '\0';
 337     ps_buffer_cur_len = strlen(ps_buffer);
 338 
 339 #ifdef PS_USE_SETPROCTITLE
 340     setproctitle("%s", ps_buffer);
 341 #endif
 342 
 343 #ifdef PS_USE_PSTAT
 344     {
 345         union pstun pst;
 346 
 347         pst.pst_command = ps_buffer;
 348         pstat(PSTAT_SETCMD, pst, ps_buffer_cur_len, 0, 0);
 349     }
 350 #endif /* PS_USE_PSTAT */
 351 
 352 #ifdef PS_USE_PS_STRINGS
 353     PS_STRINGS->ps_nargvstr = 1;
 354     PS_STRINGS->ps_argvstr = ps_buffer;
 355 #endif /* PS_USE_PS_STRINGS */
 356 
 357 #ifdef PS_USE_CLOBBER_ARGV
 358     /* pad unused memory */
 359     if (ps_buffer_cur_len < ps_buffer_size)
 360     {
 361         memset(ps_buffer + ps_buffer_cur_len, PS_PADDING,
 362                ps_buffer_size - ps_buffer_cur_len);
 363     }
 364 #endif /* PS_USE_CLOBBER_ARGV */
 365 
 366 #ifdef PS_USE_WIN32
 367     {
 368         if (!SetConsoleTitle(ps_buffer))
 369             return PS_TITLE_WINDOWS_ERROR;
 370     }
 371 #endif /* PS_USE_WIN32 */
 372 
 373     return PS_TITLE_SUCCESS;
 374 }
 375 
 376 /*
 377  * Returns the current ps_buffer value into string.  On some platforms
 378  * the string will not be null-terminated, so return the effective
 379  * length into *displen.
 380  * The return code indicates the error.
 381  */
 382 int get_ps_title(int *displen, const char** string)
 383 {
 384     int rc = is_ps_title_available();
 385     if (rc != PS_TITLE_SUCCESS)
 386         return rc;
 387 
 388 #ifdef PS_USE_WIN32
 389     if (!(ps_buffer_cur_len = GetConsoleTitle(ps_buffer, ps_buffer_size)))
 390         return PS_TITLE_WINDOWS_ERROR;
 391 #endif
 392     *displen = (int)ps_buffer_cur_len;
 393     *string = ps_buffer;
 394     return PS_TITLE_SUCCESS;
 395 }
 396 
 397 /*
 398  * Clean up the allocated argv and environ if applicable. Only call
 399  * this right before exiting.
 400  * This isn't needed per-se because the OS will clean-up anyway, but
 401  * having and calling this will ensure Valgrind doesn't output 'false
 402  * positives'.
 403  */
 404 void cleanup_ps_args(char **argv)
 405 {
 406 #ifndef PS_USE_NONE
 407     if (save_argv)
 408     {
 409         save_argv = NULL;
 410         save_argc = 0;
 411 
 412 #ifdef PS_USE_CLOBBER_ARGV
 413         {
 414             int i;
 415             for (i = 0; frozen_environ[i] != NULL; i++)
 416                 free(frozen_environ[i]);
 417             free(frozen_environ);
 418             free(new_environ);
 419             /* leave a sane environment behind since some atexit() handlers
 420                 call getenv(). */
 421             environ = empty_environ;
 422         }
 423 #endif /* PS_USE_CLOBBER_ARGV */
 424 
 425 #if defined(PS_USE_CHANGE_ARGV) || defined(PS_USE_CLOBBER_ARGV)
 426         {
 427             int i;
 428             for (i=0; argv[i] != NULL; i++)
 429                 free(argv[i]);
 430             free(argv);
 431         }
 432 #endif /* PS_USE_CHANGE_ARGV or PS_USE_CLOBBER_ARGV */
 433     }
 434 #endif /* PS_USE_NONE */
 435 
 436     return;
 437 }

/* [<][>][^][v][top][bottom][index][help] */