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 /*
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 wchar_t *ps_buffer_w = php_win32_cp_any_to_w(ps_buffer);
375
376 if (!ps_buffer_w || !SetConsoleTitleW(ps_buffer_w)) {
377 return PS_TITLE_WINDOWS_ERROR;
378 }
379
380 free(ps_buffer_w);
381 }
382 #endif /* PS_USE_WIN32 */
383
384 return PS_TITLE_SUCCESS;
385 }
386
387 /*
388 * Returns the current ps_buffer value into string. On some platforms
389 * the string will not be null-terminated, so return the effective
390 * length into *displen.
391 * The return code indicates the error.
392 */
get_ps_title(int * displen,const char ** string)393 int get_ps_title(int *displen, const char** string)
394 {
395 int rc = is_ps_title_available();
396 if (rc != PS_TITLE_SUCCESS)
397 return rc;
398
399 #ifdef PS_USE_WIN32
400 {
401 wchar_t ps_buffer_w[MAX_PATH];
402 char *tmp;
403
404 if (!(ps_buffer_cur_len = GetConsoleTitleW(ps_buffer_w, (DWORD)sizeof(ps_buffer_w)))) {
405 return PS_TITLE_WINDOWS_ERROR;
406 }
407
408 tmp = php_win32_cp_conv_w_to_any(ps_buffer_w, PHP_WIN32_CP_IGNORE_LEN, &ps_buffer_cur_len);
409 if (!tmp) {
410 return PS_TITLE_WINDOWS_ERROR;
411 }
412
413 ps_buffer_cur_len = ps_buffer_cur_len > sizeof(ps_buffer)-1 ? sizeof(ps_buffer)-1 : ps_buffer_cur_len;
414
415 memmove(ps_buffer, tmp, ps_buffer_cur_len);
416 free(tmp);
417 }
418 #endif
419 *displen = (int)ps_buffer_cur_len;
420 *string = ps_buffer;
421 return PS_TITLE_SUCCESS;
422 }
423
424 /*
425 * Clean up the allocated argv and environ if applicable. Only call
426 * this right before exiting.
427 * This isn't needed per-se because the OS will clean-up anyway, but
428 * having and calling this will ensure Valgrind doesn't output 'false
429 * positives'.
430 */
cleanup_ps_args(char ** argv)431 void cleanup_ps_args(char **argv)
432 {
433 #ifndef PS_USE_NONE
434 if (save_argv)
435 {
436 save_argv = NULL;
437 save_argc = 0;
438
439 #ifdef PS_USE_CLOBBER_ARGV
440 {
441 int i;
442 for (i = 0; frozen_environ[i] != NULL; i++)
443 free(frozen_environ[i]);
444 free(frozen_environ);
445 free(new_environ);
446 /* leave a sane environment behind since some atexit() handlers
447 call getenv(). */
448 environ = empty_environ;
449 }
450 #endif /* PS_USE_CLOBBER_ARGV */
451
452 #if defined(PS_USE_CHANGE_ARGV) || defined(PS_USE_CLOBBER_ARGV)
453 {
454 int i;
455 for (i=0; argv[i] != NULL; i++)
456 free(argv[i]);
457 free(argv);
458 }
459 #endif /* PS_USE_CHANGE_ARGV or PS_USE_CLOBBER_ARGV */
460 }
461 #endif /* PS_USE_NONE */
462
463 return;
464 }
465