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 <stdbool.h>
50 #include <string.h>
51 #include <stdlib.h>
52
53 #ifdef HAVE_SYS_PSTAT_H
54 #include <sys/pstat.h> /* for HP-UX */
55 #endif
56 #ifdef HAVE_PS_STRINGS
57 #include <machine/vmparam.h> /* for old BSD */
58 #include <sys/exec.h>
59 #endif
60 #if defined(DARWIN)
61 #include <crt_externs.h>
62 #endif
63
64 /*
65 * Ways of updating ps display:
66 *
67 * PS_USE_SETPROCTITLE
68 * use the function setproctitle(const char *, ...)
69 * (newer BSD systems)
70 * PS_USE_PSTAT
71 * use the pstat(PSTAT_SETCMD, )
72 * (HPUX)
73 * PS_USE_PS_STRINGS
74 * assign PS_STRINGS->ps_argvstr = "string"
75 * (some BSD systems)
76 * PS_USE_CHANGE_ARGV
77 * assign argv[0] = "string"
78 * (some other BSD systems)
79 * PS_USE_CLOBBER_ARGV
80 * write over the argv and environment area
81 * (Linux and most SysV-like systems)
82 * PS_USE_WIN32
83 * push the string out as the name of a Windows event
84 * PS_USE_NONE
85 * don't update ps display
86 * (This is the default, as it is safest.)
87 */
88 #if defined(HAVE_SETPROCTITLE)
89 #define PS_USE_SETPROCTITLE
90 #elif defined(HAVE_SYS_PSTAT_H) && defined(PSTAT_SETCMD)
91 #define PS_USE_PSTAT
92 #elif defined(HAVE_PS_STRINGS)
93 #define PS_USE_PS_STRINGS
94 #elif defined(BSD) && !defined(DARWIN)
95 #define PS_USE_CHANGE_ARGV
96 #elif defined(__linux__) || defined(_AIX) || defined(__sgi) || (defined(sun) && !defined(BSD)) || defined(ultrix) || defined(__osf__) || defined(DARWIN)
97 #define PS_USE_CLOBBER_ARGV
98 #elif defined(PHP_WIN32)
99 #define PS_USE_WIN32
100 #else
101 #define PS_USE_NONE
102 #endif
103
104 /* Different systems want the buffer padded differently */
105 #if defined(_AIX) || defined(__linux__) || defined(DARWIN)
106 #define PS_PADDING '\0'
107 #else
108 #define PS_PADDING ' '
109 #endif
110
111 #ifdef PS_USE_WIN32
112 static char windows_error_details[64];
113 static char ps_buffer[MAX_PATH];
114 static const size_t ps_buffer_size = MAX_PATH;
115 #elif defined(PS_USE_CLOBBER_ARGV)
116 static char *ps_buffer; /* will point to argv area */
117 static size_t ps_buffer_size; /* space determined at run time */
118 static char *empty_environ[] = {0}; /* empty environment */
119 #else
120 #define PS_BUFFER_SIZE 256
121 static char ps_buffer[PS_BUFFER_SIZE];
122 static const size_t ps_buffer_size = PS_BUFFER_SIZE;
123 #endif
124
125 static size_t ps_buffer_cur_len; /* actual string length in ps_buffer */
126
127 /* save the original argv[] location here */
128 static int save_argc;
129 static char** save_argv;
130
131 /*
132 * This holds the 'locally' allocated environ from the save_ps_args method.
133 * This is subsequently free'd at exit.
134 */
135 #if defined(PS_USE_CLOBBER_ARGV)
136 static char** frozen_environ, **new_environ;
137 #endif
138
139 /*
140 * Call this method early, before any code has used the original argv passed in
141 * from main().
142 * If needed, this code will make deep copies of argv and environ and return
143 * these to the caller for further use. The original argv is then 'clobbered'
144 * to store the process title.
145 */
save_ps_args(int argc,char ** argv)146 char** save_ps_args(int argc, char** argv)
147 {
148 save_argc = argc;
149 save_argv = argv;
150
151 #if defined(PS_USE_CLOBBER_ARGV)
152 /*
153 * If we're going to overwrite the argv area, count the available space.
154 * Also move the environment to make additional room.
155 */
156 {
157 char* end_of_area = NULL;
158 bool is_contiguous_area = true;
159 int i;
160
161 /*
162 * check for contiguous argv strings
163 */
164 for (i = 0; is_contiguous_area && (i < argc); i++)
165 {
166 if (i != 0 && end_of_area + 1 != argv[i]) {
167 is_contiguous_area = false;
168 }
169 end_of_area = argv[i] + strlen(argv[i]);
170 }
171
172 if (!is_contiguous_area) {
173 goto clobber_error;
174 }
175
176 /*
177 * check for contiguous environ strings following argv
178 */
179 for (i = 0; environ[i] != NULL; i++)
180 {
181 if (end_of_area + 1 == environ[i]) {
182 end_of_area = environ[i] + strlen(environ[i]);
183 }
184 }
185
186 ps_buffer = argv[0];
187 ps_buffer_size = end_of_area - argv[0];
188
189 /*
190 * move the environment out of the way
191 */
192 new_environ = (char **) malloc((i + 1) * sizeof(char *));
193 frozen_environ = (char **) malloc((i + 1) * sizeof(char *));
194 if (!new_environ || !frozen_environ) {
195 goto clobber_error;
196 }
197 for (i = 0; environ[i] != NULL; i++)
198 {
199 new_environ[i] = strdup(environ[i]);
200 if (!new_environ[i])
201 goto clobber_error;
202 }
203 new_environ[i] = NULL;
204 environ = new_environ;
205 memcpy((char *)frozen_environ, (char *)new_environ, sizeof(char *) * (i + 1));
206
207 }
208 #endif /* PS_USE_CLOBBER_ARGV */
209
210 #if defined(PS_USE_CHANGE_ARGV) || defined(PS_USE_CLOBBER_ARGV)
211 /*
212 * If we're going to change the original argv[] then make a copy for
213 * argument parsing purposes.
214 *
215 * (NB: do NOT think to remove the copying of argv[]!
216 * On some platforms, getopt() keeps pointers into the argv array, and
217 * will get horribly confused when it is re-called to analyze a subprocess'
218 * argument string if the argv storage has been clobbered meanwhile.
219 * Other platforms have other dependencies on argv[].)
220 */
221 {
222 char** new_argv;
223 int i;
224
225 new_argv = (char **) malloc((argc + 1) * sizeof(char *));
226 if (!new_argv)
227 goto clobber_error;
228 for (i = 0; i < argc; i++)
229 {
230 new_argv[i] = strdup(argv[i]);
231 if (!new_argv[i]) {
232 free(new_argv);
233 goto clobber_error;
234 }
235 }
236 new_argv[argc] = NULL;
237
238 #if defined(DARWIN)
239 /*
240 * Darwin (and perhaps other NeXT-derived platforms?) has a static
241 * copy of the argv pointer, which we may fix like so:
242 */
243 *_NSGetArgv() = new_argv;
244 #endif
245
246 argv = new_argv;
247
248 }
249 #endif /* PS_USE_CHANGE_ARGV or PS_USE_CLOBBER_ARGV */
250
251 #if defined(PS_USE_CLOBBER_ARGV)
252 {
253 /* make extra argv slots point at end_of_area (a NUL) */
254 int i;
255 for (i = 1; i < save_argc; i++)
256 save_argv[i] = ps_buffer + ps_buffer_size;
257 }
258 #endif /* PS_USE_CLOBBER_ARGV */
259
260 #ifdef PS_USE_CHANGE_ARGV
261 save_argv[0] = ps_buffer; /* ps_buffer here is a static const array of size PS_BUFFER_SIZE */
262 save_argv[1] = NULL;
263 #endif /* PS_USE_CHANGE_ARGV */
264
265 return argv;
266
267 #if defined(PS_USE_CHANGE_ARGV) || defined(PS_USE_CLOBBER_ARGV)
268 clobber_error:
269 /* probably can't happen?!
270 * if we ever get here, argv still points to originally passed
271 * in argument
272 */
273 save_argv = NULL;
274 save_argc = 0;
275 ps_buffer = NULL;
276 ps_buffer_size = 0;
277 return argv;
278 #endif /* PS_USE_CHANGE_ARGV or PS_USE_CLOBBER_ARGV */
279 }
280
281 /*
282 * Returns PS_TITLE_SUCCESS if the OS supports this functionality
283 * and the init function was called.
284 * Otherwise returns NOT_AVAILABLE or NOT_INITIALIZED
285 */
is_ps_title_available(void)286 int is_ps_title_available(void)
287 {
288 #ifdef PS_USE_NONE
289 return PS_TITLE_NOT_AVAILABLE; /* disabled functionality */
290 #endif
291
292 if (!save_argv)
293 return PS_TITLE_NOT_INITIALIZED;
294
295 #ifdef PS_USE_CLOBBER_ARGV
296 if (!ps_buffer)
297 return PS_TITLE_BUFFER_NOT_AVAILABLE;
298 #endif /* PS_USE_CLOBBER_ARGV */
299
300 return PS_TITLE_SUCCESS;
301 }
302
303 /*
304 * Convert error codes into error strings
305 */
ps_title_errno(int rc)306 const char* ps_title_errno(int rc)
307 {
308 switch(rc)
309 {
310 case PS_TITLE_SUCCESS:
311 return "Success";
312
313 case PS_TITLE_NOT_AVAILABLE:
314 return "Not available on this OS";
315
316 case PS_TITLE_NOT_INITIALIZED:
317 return "Not initialized correctly";
318
319 case PS_TITLE_BUFFER_NOT_AVAILABLE:
320 return "Buffer not contiguous";
321
322 #ifdef PS_USE_WIN32
323 case PS_TITLE_WINDOWS_ERROR:
324 sprintf(windows_error_details, "Windows error code: %lu", GetLastError());
325 return windows_error_details;
326 #endif
327 }
328
329 return "Unknown error code";
330 }
331
332 /*
333 * Set a new process title.
334 * Returns the appropriate error code if if there's an error
335 * (like the functionality is compile time disabled, or the
336 * save_ps_args() was not called.
337 * Else returns 0 on success.
338 */
set_ps_title(const char * title)339 int set_ps_title(const char* title)
340 {
341 int rc = is_ps_title_available();
342 if (rc != PS_TITLE_SUCCESS)
343 return rc;
344
345 strncpy(ps_buffer, title, ps_buffer_size);
346 ps_buffer[ps_buffer_size - 1] = '\0';
347 ps_buffer_cur_len = strlen(ps_buffer);
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