xref: /PHP-8.1/sapi/cli/ps_title.c (revision c6ae7a55)
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 #ifdef PHP_WIN32
32 #include "config.w32.h"
33 #include <windows.h>
34 #include <process.h>
35 #include "win32/codepage.h"
36 #else
37 #include "php_config.h"
38 extern char** environ;
39 #endif
40 
41 #include "ps_title.h"
42 #include <stdio.h>
43 
44 #ifdef HAVE_UNISTD_H
45 #include <sys/types.h>
46 #include <unistd.h>
47 #endif
48 
49 #include <string.h>
50 #include <stdlib.h>
51 
52 #ifdef HAVE_SYS_PSTAT_H
53 #include <sys/pstat.h> /* for HP-UX */
54 #endif
55 #ifdef HAVE_PS_STRINGS
56 #include <machine/vmparam.h> /* for old BSD */
57 #include <sys/exec.h>
58 #endif
59 #if defined(DARWIN)
60 #include <crt_externs.h>
61 #endif
62 
63 /*
64  * Ways of updating ps display:
65  *
66  * PS_USE_SETPROCTITLE
67  *         use the function setproctitle(const char *, ...)
68  *         (newer BSD systems)
69  * PS_USE_PSTAT
70  *         use the pstat(PSTAT_SETCMD, )
71  *         (HPUX)
72  * PS_USE_PS_STRINGS
73  *         assign PS_STRINGS->ps_argvstr = "string"
74  *         (some BSD systems)
75  * PS_USE_CHANGE_ARGV
76  *         assign argv[0] = "string"
77  *         (some other BSD systems)
78  * PS_USE_CLOBBER_ARGV
79  *         write over the argv and environment area
80  *         (Linux and most SysV-like systems)
81  * PS_USE_WIN32
82  *         push the string out as the name of a Windows event
83  * PS_USE_NONE
84  *         don't update ps display
85  *         (This is the default, as it is safest.)
86  */
87 #if defined(HAVE_SETPROCTITLE)
88 #define PS_USE_SETPROCTITLE
89 #elif defined(HAVE_SYS_PSTAT_H) && defined(PSTAT_SETCMD)
90 #define PS_USE_PSTAT
91 #elif defined(HAVE_PS_STRINGS)
92 #define PS_USE_PS_STRINGS
93 #elif defined(BSD) && !defined(DARWIN)
94 #define PS_USE_CHANGE_ARGV
95 #elif defined(__linux__) || defined(_AIX) || defined(__sgi) || (defined(sun) && !defined(BSD)) || defined(ultrix) || defined(__osf__) || defined(DARWIN)
96 #define PS_USE_CLOBBER_ARGV
97 #elif defined(PHP_WIN32)
98 #define PS_USE_WIN32
99 #else
100 #define PS_USE_NONE
101 #endif
102 
103 /* Different systems want the buffer padded differently */
104 #if defined(_AIX) || defined(__linux__) || defined(DARWIN)
105 #define PS_PADDING '\0'
106 #else
107 #define PS_PADDING ' '
108 #endif
109 
110 #ifdef PS_USE_WIN32
111 static char windows_error_details[64];
112 static char ps_buffer[MAX_PATH];
113 static const size_t ps_buffer_size = MAX_PATH;
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         if (non_contiguous_area != 0) {
171             goto clobber_error;
172         }
173 
174         /*
175          * check for contiguous environ strings following argv
176          */
177         for (i = 0; environ[i] != NULL; i++)
178         {
179             if (end_of_area + 1 == environ[i]) {
180                 end_of_area = environ[i] + strlen(environ[i]);
181             }
182         }
183 
184         ps_buffer = argv[0];
185         ps_buffer_size = end_of_area - argv[0];
186 
187         /*
188          * move the environment out of the way
189          */
190         new_environ = (char **) malloc((i + 1) * sizeof(char *));
191         frozen_environ = (char **) malloc((i + 1) * sizeof(char *));
192         if (!new_environ || !frozen_environ)
193             goto clobber_error;
194         for (i = 0; environ[i] != NULL; i++)
195         {
196             new_environ[i] = strdup(environ[i]);
197             if (!new_environ[i])
198                 goto clobber_error;
199         }
200         new_environ[i] = NULL;
201         environ = new_environ;
202         memcpy((char *)frozen_environ, (char *)new_environ, sizeof(char *) * (i + 1));
203 
204     }
205 #endif /* PS_USE_CLOBBER_ARGV */
206 
207 #if defined(PS_USE_CHANGE_ARGV) || defined(PS_USE_CLOBBER_ARGV)
208     /*
209      * If we're going to change the original argv[] then make a copy for
210      * argument parsing purposes.
211      *
212      * (NB: do NOT think to remove the copying of argv[]!
213      * On some platforms, getopt() keeps pointers into the argv array, and
214      * will get horribly confused when it is re-called to analyze a subprocess'
215      * argument string if the argv storage has been clobbered meanwhile.
216      * Other platforms have other dependencies on argv[].)
217      */
218     {
219         char** new_argv;
220         int i;
221 
222         new_argv = (char **) malloc((argc + 1) * sizeof(char *));
223         if (!new_argv)
224             goto clobber_error;
225         for (i = 0; i < argc; i++)
226         {
227             new_argv[i] = strdup(argv[i]);
228             if (!new_argv[i]) {
229                 free(new_argv);
230                 goto clobber_error;
231             }
232         }
233         new_argv[argc] = NULL;
234 
235 #if defined(DARWIN)
236         /*
237          * Darwin (and perhaps other NeXT-derived platforms?) has a static
238          * copy of the argv pointer, which we may fix like so:
239          */
240         *_NSGetArgv() = new_argv;
241 #endif
242 
243         argv = new_argv;
244 
245     }
246 #endif /* PS_USE_CHANGE_ARGV or PS_USE_CLOBBER_ARGV */
247 
248 #if defined(PS_USE_CLOBBER_ARGV)
249     {
250         /* make extra argv slots point at end_of_area (a NUL) */
251         int i;
252         for (i = 1; i < save_argc; i++)
253             save_argv[i] = ps_buffer + ps_buffer_size;
254     }
255 #endif /* PS_USE_CLOBBER_ARGV */
256 
257 #ifdef PS_USE_CHANGE_ARGV
258     save_argv[0] = ps_buffer; /* ps_buffer here is a static const array of size PS_BUFFER_SIZE */
259     save_argv[1] = NULL;
260 #endif /* PS_USE_CHANGE_ARGV */
261 
262     return argv;
263 
264 #if defined(PS_USE_CHANGE_ARGV) || defined(PS_USE_CLOBBER_ARGV)
265 clobber_error:
266     /* probably can't happen?!
267      * if we ever get here, argv still points to originally passed
268      * in argument
269      */
270     save_argv = NULL;
271     save_argc = 0;
272     ps_buffer = NULL;
273     ps_buffer_size = 0;
274     return argv;
275 #endif /* PS_USE_CHANGE_ARGV or PS_USE_CLOBBER_ARGV */
276 }
277 
278 /*
279  * Returns PS_TITLE_SUCCESS if the OS supports this functionality
280  * and the init function was called.
281  * Otherwise returns NOT_AVAILABLE or NOT_INITIALIZED
282  */
is_ps_title_available()283 int is_ps_title_available()
284 {
285 #ifdef PS_USE_NONE
286     return PS_TITLE_NOT_AVAILABLE; /* disabled functionality */
287 #endif
288 
289     if (!save_argv)
290         return PS_TITLE_NOT_INITIALIZED;
291 
292 #ifdef PS_USE_CLOBBER_ARGV
293     if (!ps_buffer)
294         return PS_TITLE_BUFFER_NOT_AVAILABLE;
295 #endif /* PS_USE_CLOBBER_ARGV */
296 
297     return PS_TITLE_SUCCESS;
298 }
299 
300 /*
301  * Convert error codes into error strings
302  */
ps_title_errno(int rc)303 const char* ps_title_errno(int rc)
304 {
305     switch(rc)
306     {
307     case PS_TITLE_SUCCESS:
308         return "Success";
309 
310     case PS_TITLE_NOT_AVAILABLE:
311         return "Not available on this OS";
312 
313     case PS_TITLE_NOT_INITIALIZED:
314         return "Not initialized correctly";
315 
316     case PS_TITLE_BUFFER_NOT_AVAILABLE:
317         return "Buffer not contiguous";
318 
319 #ifdef PS_USE_WIN32
320     case PS_TITLE_WINDOWS_ERROR:
321         sprintf(windows_error_details, "Windows error code: %lu", GetLastError());
322         return windows_error_details;
323 #endif
324     }
325 
326     return "Unknown error code";
327 }
328 
329 /*
330  * Set a new process title.
331  * Returns the appropriate error code if if there's an error
332  * (like the functionality is compile time disabled, or the
333  * save_ps_args() was not called.
334  * Else returns 0 on success.
335  */
set_ps_title(const char * title)336 int set_ps_title(const char* title)
337 {
338     int rc = is_ps_title_available();
339     if (rc != PS_TITLE_SUCCESS)
340         return rc;
341 
342     strncpy(ps_buffer, title, ps_buffer_size);
343     ps_buffer[ps_buffer_size - 1] = '\0';
344     ps_buffer_cur_len = strlen(ps_buffer);
345 
346 #ifdef PS_USE_SETPROCTITLE
347     setproctitle("%s", ps_buffer);
348 #endif
349 
350 #ifdef PS_USE_PSTAT
351     {
352         union pstun pst;
353 
354         pst.pst_command = ps_buffer;
355         pstat(PSTAT_SETCMD, pst, ps_buffer_cur_len, 0, 0);
356     }
357 #endif /* PS_USE_PSTAT */
358 
359 #ifdef PS_USE_PS_STRINGS
360     PS_STRINGS->ps_nargvstr = 1;
361     PS_STRINGS->ps_argvstr = ps_buffer;
362 #endif /* PS_USE_PS_STRINGS */
363 
364 #ifdef PS_USE_CLOBBER_ARGV
365     /* pad unused memory */
366     if (ps_buffer_cur_len < ps_buffer_size)
367     {
368         memset(ps_buffer + ps_buffer_cur_len, PS_PADDING,
369                ps_buffer_size - ps_buffer_cur_len);
370     }
371 #endif /* PS_USE_CLOBBER_ARGV */
372 
373 #ifdef PS_USE_WIN32
374     {
375 	wchar_t *ps_buffer_w = php_win32_cp_any_to_w(ps_buffer);
376 
377         if (!ps_buffer_w || !SetConsoleTitleW(ps_buffer_w)) {
378             return PS_TITLE_WINDOWS_ERROR;
379 	}
380 
381 	free(ps_buffer_w);
382     }
383 #endif /* PS_USE_WIN32 */
384 
385     return PS_TITLE_SUCCESS;
386 }
387 
388 /*
389  * Returns the current ps_buffer value into string.  On some platforms
390  * the string will not be null-terminated, so return the effective
391  * length into *displen.
392  * The return code indicates the error.
393  */
get_ps_title(int * displen,const char ** string)394 int get_ps_title(int *displen, const char** string)
395 {
396     int rc = is_ps_title_available();
397     if (rc != PS_TITLE_SUCCESS)
398         return rc;
399 
400 #ifdef PS_USE_WIN32
401     {
402 	wchar_t ps_buffer_w[MAX_PATH];
403 	char *tmp;
404 
405         if (!(ps_buffer_cur_len = GetConsoleTitleW(ps_buffer_w, (DWORD)sizeof(ps_buffer_w)))) {
406             return PS_TITLE_WINDOWS_ERROR;
407 	}
408 
409 	tmp = php_win32_cp_conv_w_to_any(ps_buffer_w, PHP_WIN32_CP_IGNORE_LEN, &ps_buffer_cur_len);
410 	if (!tmp) {
411             return PS_TITLE_WINDOWS_ERROR;
412 	}
413 
414 	ps_buffer_cur_len = ps_buffer_cur_len > sizeof(ps_buffer)-1 ? sizeof(ps_buffer)-1 : ps_buffer_cur_len;
415 
416 	memmove(ps_buffer, tmp, ps_buffer_cur_len);
417 	free(tmp);
418     }
419 #endif
420     *displen = (int)ps_buffer_cur_len;
421     *string = ps_buffer;
422     return PS_TITLE_SUCCESS;
423 }
424 
425 /*
426  * Clean up the allocated argv and environ if applicable. Only call
427  * this right before exiting.
428  * This isn't needed per-se because the OS will clean-up anyway, but
429  * having and calling this will ensure Valgrind doesn't output 'false
430  * positives'.
431  */
cleanup_ps_args(char ** argv)432 void cleanup_ps_args(char **argv)
433 {
434 #ifndef PS_USE_NONE
435     if (save_argv)
436     {
437         save_argv = NULL;
438         save_argc = 0;
439 
440 #ifdef PS_USE_CLOBBER_ARGV
441         {
442             int i;
443             for (i = 0; frozen_environ[i] != NULL; i++)
444                 free(frozen_environ[i]);
445             free(frozen_environ);
446             free(new_environ);
447             /* leave a sane environment behind since some atexit() handlers
448                 call getenv(). */
449             environ = empty_environ;
450         }
451 #endif /* PS_USE_CLOBBER_ARGV */
452 
453 #if defined(PS_USE_CHANGE_ARGV) || defined(PS_USE_CLOBBER_ARGV)
454         {
455             int i;
456             for (i=0; argv[i] != NULL; i++)
457                 free(argv[i]);
458             free(argv);
459         }
460 #endif /* PS_USE_CHANGE_ARGV or PS_USE_CLOBBER_ARGV */
461     }
462 #endif /* PS_USE_NONE */
463 
464     return;
465 }
466