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