xref: /PHP-7.0/sapi/cli/ps_title.c (revision de7a6b12)
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 typedef BOOL (WINAPI *MySetConsoleTitle)(LPCTSTR);
113 typedef DWORD (WINAPI *MyGetConsoleTitle)(LPTSTR, DWORD);
114 #elif defined(PS_USE_CLOBBER_ARGV)
115 static char *ps_buffer;         /* will point to argv area */
116 static size_t ps_buffer_size;   /* space determined at run time */
117 static char *empty_environ[] = {0}; /* empty environment */
118 #else
119 #define PS_BUFFER_SIZE 256
120 static char ps_buffer[PS_BUFFER_SIZE];
121 static const size_t ps_buffer_size = PS_BUFFER_SIZE;
122 #endif
123 
124 static size_t ps_buffer_cur_len; /* actual string length in ps_buffer */
125 
126 /* save the original argv[] location here */
127 static int save_argc;
128 static char** save_argv;
129 
130 /*
131  * This holds the 'locally' allocated environ from the save_ps_args method.
132  * This is subsequently free'd at exit.
133  */
134 #if defined(PS_USE_CLOBBER_ARGV)
135 static char** frozen_environ, **new_environ;
136 #endif
137 
138 /*
139  * Call this method early, before any code has used the original argv passed in
140  * from main().
141  * If needed, this code will make deep copies of argv and environ and return
142  * these to the caller for further use. The original argv is then 'clobbered'
143  * to store the process title.
144  */
save_ps_args(int argc,char ** argv)145 char** save_ps_args(int argc, char** argv)
146 {
147     save_argc = argc;
148     save_argv = argv;
149 
150 #if defined(PS_USE_CLOBBER_ARGV)
151     /*
152      * If we're going to overwrite the argv area, count the available space.
153      * Also move the environment to make additional room.
154      */
155     {
156         char* end_of_area = NULL;
157         int non_contiguous_area = 0;
158         int i;
159 
160         /*
161          * check for contiguous argv strings
162          */
163         for (i = 0; (non_contiguous_area == 0) && (i < argc); i++)
164         {
165             if (i != 0 && end_of_area + 1 != argv[i])
166                 non_contiguous_area = 1;
167             end_of_area = argv[i] + strlen(argv[i]);
168         }
169 
170         /*
171          * check for contiguous environ strings following argv
172          */
173         for (i = 0; (non_contiguous_area == 0) && (environ[i] != NULL); i++)
174         {
175             if (end_of_area + 1 != environ[i])
176                 non_contiguous_area = 1;
177             end_of_area = environ[i] + strlen(environ[i]);
178         }
179 
180         if (non_contiguous_area != 0)
181             goto clobber_error;
182 
183         ps_buffer = argv[0];
184         ps_buffer_size = end_of_area - argv[0];
185 
186         /*
187          * move the environment out of the way
188          */
189         new_environ = (char **) malloc((i + 1) * sizeof(char *));
190         frozen_environ = (char **) malloc((i + 1) * sizeof(char *));
191         if (!new_environ || !frozen_environ)
192             goto clobber_error;
193         for (i = 0; environ[i] != NULL; i++)
194         {
195             new_environ[i] = strdup(environ[i]);
196             if (!new_environ[i])
197                 goto clobber_error;
198         }
199         new_environ[i] = NULL;
200         environ = new_environ;
201         memcpy((char *)frozen_environ, (char *)new_environ, sizeof(char *) * (i + 1));
202 
203     }
204 #endif /* PS_USE_CLOBBER_ARGV */
205 
206 #if defined(PS_USE_CHANGE_ARGV) || defined(PS_USE_CLOBBER_ARGV)
207     /*
208      * If we're going to change the original argv[] then make a copy for
209      * argument parsing purposes.
210      *
211      * (NB: do NOT think to remove the copying of argv[]!
212      * On some platforms, getopt() keeps pointers into the argv array, and
213      * will get horribly confused when it is re-called to analyze a subprocess'
214      * argument string if the argv storage has been clobbered meanwhile.
215      * Other platforms have other dependencies on argv[].)
216      */
217     {
218         char** new_argv;
219         int i;
220 
221         new_argv = (char **) malloc((argc + 1) * sizeof(char *));
222         if (!new_argv)
223             goto clobber_error;
224         for (i = 0; i < argc; i++)
225         {
226             new_argv[i] = strdup(argv[i]);
227             if (!new_argv[i]) {
228 				free(new_argv);
229                 goto clobber_error;
230 			}
231         }
232         new_argv[argc] = NULL;
233 
234 #if defined(DARWIN)
235         /*
236          * Darwin (and perhaps other NeXT-derived platforms?) has a static
237          * copy of the argv pointer, which we may fix like so:
238          */
239         *_NSGetArgv() = new_argv;
240 #endif
241 
242         argv = new_argv;
243 
244     }
245 #endif /* PS_USE_CHANGE_ARGV or PS_USE_CLOBBER_ARGV */
246 
247 #if defined(PS_USE_CLOBBER_ARGV)
248     {
249         /* make extra argv slots point at end_of_area (a NUL) */
250         int i;
251         for (i = 1; i < save_argc; i++)
252             save_argv[i] = ps_buffer + ps_buffer_size;
253     }
254 #endif /* PS_USE_CLOBBER_ARGV */
255 
256 #ifdef PS_USE_CHANGE_ARGV
257     save_argv[0] = ps_buffer; /* ps_buffer here is a static const array of size PS_BUFFER_SIZE */
258     save_argv[1] = NULL;
259 #endif /* PS_USE_CHANGE_ARGV */
260 
261     return argv;
262 
263 #if defined(PS_USE_CHANGE_ARGV) || defined(PS_USE_CLOBBER_ARGV)
264 clobber_error:
265     /* probably can't happen?!
266      * if we ever get here, argv still points to originally passed
267      * in argument
268      */
269     save_argv = NULL;
270     save_argc = 0;
271     ps_buffer = NULL;
272     ps_buffer_size = 0;
273     return argv;
274 #endif /* PS_USE_CHANGE_ARGV or PS_USE_CLOBBER_ARGV */
275 }
276 
277 /*
278  * Returns PS_TITLE_SUCCESS if the OS supports this functionality
279  * and the init function was called.
280  * Otherwise returns NOT_AVAILABLE or NOT_INITIALIZED
281  */
is_ps_title_available()282 int is_ps_title_available()
283 {
284 #ifdef PS_USE_NONE
285     return PS_TITLE_NOT_AVAILABLE; /* disabled functionality */
286 #endif
287 
288     if (!save_argv)
289         return PS_TITLE_NOT_INITIALIZED;
290 
291 #ifdef PS_USE_CLOBBER_ARGV
292     if (!ps_buffer)
293         return PS_TITLE_BUFFER_NOT_AVAILABLE;
294 #endif /* PS_USE_CLOBBER_ARGV */
295 
296     return PS_TITLE_SUCCESS;
297 }
298 
299 /*
300  * Convert error codes into error strings
301  */
ps_title_errno(int rc)302 const char* ps_title_errno(int rc)
303 {
304     switch(rc)
305     {
306     case PS_TITLE_SUCCESS:
307         return "Success";
308 
309     case PS_TITLE_NOT_AVAILABLE:
310         return "Not available on this OS";
311 
312     case PS_TITLE_NOT_INITIALIZED:
313         return "Not initialized correctly";
314 
315     case PS_TITLE_BUFFER_NOT_AVAILABLE:
316         return "Buffer not contiguous";
317 
318 #ifdef PS_USE_WIN32
319     case PS_TITLE_WINDOWS_ERROR:
320         sprintf(windows_error_details, "Windows error code: %lu", GetLastError());
321         return windows_error_details;
322 #endif
323     }
324 
325     return "Unknown error code";
326 }
327 
328 /*
329  * Set a new process title.
330  * Returns the appropriate error code if if there's an error
331  * (like the functionality is compile time disabled, or the
332  * save_ps_args() was not called.
333  * Else returns 0 on success.
334  */
set_ps_title(const char * title)335 int set_ps_title(const char* title)
336 {
337     int rc = is_ps_title_available();
338     if (rc != PS_TITLE_SUCCESS)
339         return rc;
340 
341     strncpy(ps_buffer, title, ps_buffer_size);
342     ps_buffer[ps_buffer_size - 1] = '\0';
343     ps_buffer_cur_len = strlen(ps_buffer);
344 
345 #ifdef PS_USE_SETPROCTITLE
346     setproctitle("%s", ps_buffer);
347 #endif
348 
349 #ifdef PS_USE_PSTAT
350     {
351         union pstun pst;
352 
353         pst.pst_command = ps_buffer;
354         pstat(PSTAT_SETCMD, pst, ps_buffer_cur_len, 0, 0);
355     }
356 #endif /* PS_USE_PSTAT */
357 
358 #ifdef PS_USE_PS_STRINGS
359     PS_STRINGS->ps_nargvstr = 1;
360     PS_STRINGS->ps_argvstr = ps_buffer;
361 #endif /* PS_USE_PS_STRINGS */
362 
363 #ifdef PS_USE_CLOBBER_ARGV
364     /* pad unused memory */
365     if (ps_buffer_cur_len < ps_buffer_size)
366     {
367         memset(ps_buffer + ps_buffer_cur_len, PS_PADDING,
368                ps_buffer_size - ps_buffer_cur_len);
369     }
370 #endif /* PS_USE_CLOBBER_ARGV */
371 
372 #ifdef PS_USE_WIN32
373     {
374     	MySetConsoleTitle set_title = NULL;
375 	HMODULE hMod = LoadLibrary("kernel32.dll");
376 
377 	if (!hMod) {
378             return PS_TITLE_WINDOWS_ERROR;
379 	}
380 
381 	/* NOTE we don't use _UNICODE*/
382 	set_title = (MySetConsoleTitle)GetProcAddress(hMod, "SetConsoleTitleA");
383 	if (!set_title) {
384             return PS_TITLE_WINDOWS_ERROR;
385 	}
386 
387         if (!set_title(ps_buffer)) {
388             return PS_TITLE_WINDOWS_ERROR;
389 	}
390     }
391 #endif /* PS_USE_WIN32 */
392 
393     return PS_TITLE_SUCCESS;
394 }
395 
396 /*
397  * Returns the current ps_buffer value into string.  On some platforms
398  * the string will not be null-terminated, so return the effective
399  * length into *displen.
400  * The return code indicates the error.
401  */
get_ps_title(int * displen,const char ** string)402 int get_ps_title(int *displen, const char** string)
403 {
404     int rc = is_ps_title_available();
405     if (rc != PS_TITLE_SUCCESS)
406         return rc;
407 
408 #ifdef PS_USE_WIN32
409     {
410     	MyGetConsoleTitle get_title = NULL;
411 	HMODULE hMod = LoadLibrary("kernel32.dll");
412 
413 	if (!hMod) {
414             return PS_TITLE_WINDOWS_ERROR;
415 	}
416 
417 	/* NOTE we don't use _UNICODE*/
418 	get_title = (MyGetConsoleTitle)GetProcAddress(hMod, "GetConsoleTitleA");
419 	if (!get_title) {
420             return PS_TITLE_WINDOWS_ERROR;
421 	}
422 
423         if (!(ps_buffer_cur_len = get_title(ps_buffer, (DWORD)ps_buffer_size))) {
424             return PS_TITLE_WINDOWS_ERROR;
425 	}
426     }
427 #endif
428     *displen = (int)ps_buffer_cur_len;
429     *string = ps_buffer;
430     return PS_TITLE_SUCCESS;
431 }
432 
433 /*
434  * Clean up the allocated argv and environ if applicable. Only call
435  * this right before exiting.
436  * This isn't needed per-se because the OS will clean-up anyway, but
437  * having and calling this will ensure Valgrind doesn't output 'false
438  * positives'.
439  */
cleanup_ps_args(char ** argv)440 void cleanup_ps_args(char **argv)
441 {
442 #ifndef PS_USE_NONE
443     if (save_argv)
444     {
445         save_argv = NULL;
446         save_argc = 0;
447 
448 #ifdef PS_USE_CLOBBER_ARGV
449         {
450             int i;
451             for (i = 0; frozen_environ[i] != NULL; i++)
452                 free(frozen_environ[i]);
453             free(frozen_environ);
454             free(new_environ);
455             /* leave a sane environment behind since some atexit() handlers
456                 call getenv(). */
457             environ = empty_environ;
458         }
459 #endif /* PS_USE_CLOBBER_ARGV */
460 
461 #if defined(PS_USE_CHANGE_ARGV) || defined(PS_USE_CLOBBER_ARGV)
462         {
463             int i;
464             for (i=0; argv[i] != NULL; i++)
465                 free(argv[i]);
466             free(argv);
467         }
468 #endif /* PS_USE_CHANGE_ARGV or PS_USE_CLOBBER_ARGV */
469     }
470 #endif /* PS_USE_NONE */
471 
472     return;
473 }
474