php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
ps_title.c
Go to the documentation of this file.
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>
39extern 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
113static char windows_error_details[64];
114static char ps_buffer[MAX_PATH];
115static const size_t ps_buffer_size = MAX_PATH;
116#elif defined(PS_USE_CLOBBER_ARGV)
117static char *ps_buffer; /* will point to argv area */
118static size_t ps_buffer_size; /* space determined at run time */
119static char *empty_environ[] = {0}; /* empty environment */
120#else
121#define PS_BUFFER_SIZE 256
122static char ps_buffer[PS_BUFFER_SIZE];
123static const size_t ps_buffer_size = PS_BUFFER_SIZE;
124#endif
125
126static size_t ps_buffer_cur_len; /* actual string length in ps_buffer */
127
128/* save the original argv[] location here */
129static int save_argc;
130static 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)
137static 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 */
147char** 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)
269clobber_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 */
288{
289#ifdef PS_USE_NONE
290 return PS_TITLE_NOT_AVAILABLE; /* disabled functionality */
291#endif
292
293 if (!save_argv)
295
296#ifdef PS_USE_CLOBBER_ARGV
297 if (!ps_buffer)
299#endif /* PS_USE_CLOBBER_ARGV */
300
301 return PS_TITLE_SUCCESS;
302}
303
304/*
305 * Convert error codes into error strings
306 */
307const char* ps_title_errno(int rc)
308{
309 switch(rc)
310 {
311 case PS_TITLE_SUCCESS:
312 return "Success";
313
315 return "Not available on this OS";
316
318 return "Not initialized correctly";
319
321 return "Buffer not contiguous";
322
323#ifdef PS_USE_WIN32
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 */
340int 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)) {
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 */
397int 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)))) {
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) {
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 */
435void 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}
#define php_win32_cp_any_to_w(in)
Definition codepage.h:115
#define PHP_WIN32_CP_IGNORE_LEN
Definition codepage.h:30
#define DWORD
Definition exif.c:1762
memcpy(ptr1, ptr2, size)
memset(ptr, 0, type->size)
#define NULL
Definition gdcache.h:45
#define memmove(a, b, c)
#define strlcpy
Definition php.h:159
char ** environ
#define PS_PADDING
Definition ps_title.c:109
const char * ps_title_errno(int rc)
Definition ps_title.c:307
int is_ps_title_available(void)
Definition ps_title.c:287
void cleanup_ps_args(char **argv)
Definition ps_title.c:435
char ** save_ps_args(int argc, char **argv)
Definition ps_title.c:147
int get_ps_title(size_t *displen, const char **string)
Definition ps_title.c:397
int set_ps_title(const char *title)
Definition ps_title.c:340
#define PS_BUFFER_SIZE
Definition ps_title.c:121
#define PS_TITLE_WINDOWS_ERROR
Definition ps_title.h:24
#define PS_TITLE_BUFFER_NOT_AVAILABLE
Definition ps_title.h:23
#define PS_TITLE_NOT_INITIALIZED
Definition ps_title.h:22
#define PS_TITLE_NOT_AVAILABLE
Definition ps_title.h:21
#define PS_TITLE_SUCCESS
Definition ps_title.h:20
strlen(string $string)
#define snprintf