xref: /PHP-5.5/sapi/cli/ps_title.c (revision 73c1be26)
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  */
save_ps_args(int argc,char ** argv)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  */
is_ps_title_available()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  */
ps_title_errno(int rc)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  */
set_ps_title(const char * title)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  */
get_ps_title(int * displen,const char ** string)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  */
cleanup_ps_args(char ** argv)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 }
438