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