Line data Source code
1 : /*--------------------------------------------------------------------
2 : * ps_status.c
3 : *
4 : * Routines to support changing the ps display of PostgreSQL backends
5 : * to contain some useful information. Mechanism differs wildly across
6 : * platforms.
7 : *
8 : * src/backend/utils/misc/ps_status.c
9 : *
10 : * Copyright (c) 2000-2025, PostgreSQL Global Development Group
11 : * various details abducted from various places
12 : *--------------------------------------------------------------------
13 : */
14 :
15 : #include "postgres.h"
16 :
17 : #include <unistd.h>
18 : #if defined(__darwin__)
19 : #include <crt_externs.h>
20 : #endif
21 :
22 : #include "miscadmin.h"
23 : #include "utils/guc.h"
24 : #include "utils/ps_status.h"
25 :
26 : #if !defined(WIN32) || defined(_MSC_VER)
27 : extern char **environ;
28 : #endif
29 :
30 : /* GUC variable */
31 : bool update_process_title = DEFAULT_UPDATE_PROCESS_TITLE;
32 :
33 : /*
34 : * Alternative ways of updating ps display:
35 : *
36 : * PS_USE_SETPROCTITLE_FAST
37 : * use the function setproctitle_fast(const char *, ...)
38 : * (FreeBSD)
39 : * PS_USE_SETPROCTITLE
40 : * use the function setproctitle(const char *, ...)
41 : * (other BSDs)
42 : * PS_USE_CLOBBER_ARGV
43 : * write over the argv and environment area
44 : * (Linux and most SysV-like systems)
45 : * PS_USE_WIN32
46 : * push the string out as the name of a Windows event
47 : * PS_USE_NONE
48 : * don't update ps display
49 : * (This is the default, as it is safest.)
50 : */
51 : #if defined(HAVE_SETPROCTITLE_FAST)
52 : #define PS_USE_SETPROCTITLE_FAST
53 : #elif defined(HAVE_SETPROCTITLE)
54 : #define PS_USE_SETPROCTITLE
55 : #elif defined(__linux__) || defined(__sun) || defined(__darwin__)
56 : #define PS_USE_CLOBBER_ARGV
57 : #elif defined(WIN32)
58 : #define PS_USE_WIN32
59 : #else
60 : #define PS_USE_NONE
61 : #endif
62 :
63 :
64 : /* Different systems want the buffer padded differently */
65 : #if defined(__linux__) || defined(__darwin__)
66 : #define PS_PADDING '\0'
67 : #else
68 : #define PS_PADDING ' '
69 : #endif
70 :
71 :
72 : #ifndef PS_USE_NONE
73 :
74 : #ifndef PS_USE_CLOBBER_ARGV
75 : /* all but one option need a buffer to write their ps line in */
76 : #define PS_BUFFER_SIZE 256
77 : static char ps_buffer[PS_BUFFER_SIZE];
78 : static const size_t ps_buffer_size = PS_BUFFER_SIZE;
79 : #else /* PS_USE_CLOBBER_ARGV */
80 : static char *ps_buffer; /* will point to argv area */
81 : static size_t ps_buffer_size; /* space determined at run time */
82 : static size_t last_status_len; /* use to minimize length of clobber */
83 : #endif /* PS_USE_CLOBBER_ARGV */
84 :
85 : static size_t ps_buffer_cur_len; /* nominal strlen(ps_buffer) */
86 :
87 : static size_t ps_buffer_fixed_size; /* size of the constant prefix */
88 :
89 : /*
90 : * Length of ps_buffer before the suffix was appended to the end, or 0 if we
91 : * didn't set a suffix.
92 : */
93 : static size_t ps_buffer_nosuffix_len;
94 :
95 : static void flush_ps_display(void);
96 :
97 : #endif /* not PS_USE_NONE */
98 :
99 : /* save the original argv[] location here */
100 : static int save_argc;
101 : static char **save_argv;
102 :
103 : /*
104 : * Valgrind seems not to consider the global "environ" variable as a valid
105 : * root pointer; so when we allocate a new environment array, it claims that
106 : * data is leaked. To fix that, keep our own statically-allocated copy of the
107 : * pointer. (Oddly, this doesn't seem to be a problem for "argv".)
108 : */
109 : #if defined(PS_USE_CLOBBER_ARGV) && defined(USE_VALGRIND)
110 : extern char **ps_status_new_environ;
111 : char **ps_status_new_environ;
112 : #endif
113 :
114 :
115 : /*
116 : * Call this early in startup to save the original argc/argv values.
117 : * If needed, we make a copy of the original argv[] array to preserve it
118 : * from being clobbered by subsequent ps_display actions.
119 : *
120 : * (The original argv[] will not be overwritten by this routine, but may be
121 : * overwritten during init_ps_display. Also, the physical location of the
122 : * environment strings may be moved, so this should be called before any code
123 : * that might try to hang onto a getenv() result. But see hack for musl
124 : * within.)
125 : *
126 : * Note that in case of failure this cannot call elog() as that is not
127 : * initialized yet. We rely on write_stderr() instead.
128 : */
129 : char **
130 3758 : save_ps_display_args(int argc, char **argv)
131 : {
132 3758 : save_argc = argc;
133 3758 : save_argv = argv;
134 :
135 : #if defined(PS_USE_CLOBBER_ARGV)
136 :
137 : /*
138 : * If we're going to overwrite the argv area, count the available space.
139 : * Also move the environment strings to make additional room.
140 : */
141 : {
142 3758 : char *end_of_area = NULL;
143 : char **new_environ;
144 : int i;
145 :
146 : /*
147 : * check for contiguous argv strings
148 : */
149 21604 : for (i = 0; i < argc; i++)
150 : {
151 17846 : if (i == 0 || end_of_area + 1 == argv[i])
152 17846 : end_of_area = argv[i] + strlen(argv[i]);
153 : }
154 :
155 3758 : if (end_of_area == NULL) /* probably can't happen? */
156 : {
157 0 : ps_buffer = NULL;
158 0 : ps_buffer_size = 0;
159 0 : return argv;
160 : }
161 :
162 : /*
163 : * check for contiguous environ strings following argv
164 : */
165 144502 : for (i = 0; environ[i] != NULL; i++)
166 : {
167 140744 : if (end_of_area + 1 == environ[i])
168 : {
169 : /*
170 : * The musl dynamic linker keeps a static pointer to the
171 : * initial value of LD_LIBRARY_PATH, if that is defined in the
172 : * process's environment. Therefore, we must not overwrite the
173 : * value of that setting and thus cannot advance end_of_area
174 : * beyond it. Musl does not define any identifying compiler
175 : * symbol, so we have to do this unless we see a symbol
176 : * identifying a Linux libc we know is safe.
177 : */
178 : #if defined(__linux__) && (!defined(__GLIBC__) && !defined(__UCLIBC__))
179 : if (strncmp(environ[i], "LD_LIBRARY_PATH=", 16) == 0)
180 : {
181 : /*
182 : * We can overwrite the name, but stop at the equals sign.
183 : * Future loop iterations will not find any more
184 : * contiguous space, but we don't break early because we
185 : * need to count the total number of environ[] entries.
186 : */
187 : end_of_area = environ[i] + 15;
188 : }
189 : else
190 : #endif
191 : {
192 140744 : end_of_area = environ[i] + strlen(environ[i]);
193 : }
194 : }
195 : }
196 :
197 3758 : ps_buffer = argv[0];
198 3758 : last_status_len = ps_buffer_size = end_of_area - argv[0];
199 :
200 : /*
201 : * move the environment out of the way
202 : */
203 3758 : new_environ = (char **) malloc((i + 1) * sizeof(char *));
204 3758 : if (!new_environ)
205 : {
206 0 : write_stderr("out of memory\n");
207 0 : exit(1);
208 : }
209 144502 : for (i = 0; environ[i] != NULL; i++)
210 : {
211 140744 : new_environ[i] = strdup(environ[i]);
212 140744 : if (!new_environ[i])
213 : {
214 0 : write_stderr("out of memory\n");
215 0 : exit(1);
216 : }
217 : }
218 3758 : new_environ[i] = NULL;
219 3758 : environ = new_environ;
220 :
221 : /* See notes about Valgrind above. */
222 : #ifdef USE_VALGRIND
223 : ps_status_new_environ = new_environ;
224 : #endif
225 : }
226 :
227 : /*
228 : * If we're going to change the original argv[] then make a copy for
229 : * argument parsing purposes.
230 : *
231 : * NB: do NOT think to remove the copying of argv[], even though
232 : * postmaster.c finishes looking at argv[] long before we ever consider
233 : * changing the ps display. On some platforms, getopt() keeps pointers
234 : * into the argv array, and will get horribly confused when it is
235 : * re-called to analyze a subprocess' argument string if the argv storage
236 : * has been clobbered meanwhile. Other platforms have other dependencies
237 : * on argv[].
238 : */
239 : {
240 : char **new_argv;
241 : int i;
242 :
243 3758 : new_argv = (char **) malloc((argc + 1) * sizeof(char *));
244 3758 : if (!new_argv)
245 : {
246 0 : write_stderr("out of memory\n");
247 0 : exit(1);
248 : }
249 21604 : for (i = 0; i < argc; i++)
250 : {
251 17846 : new_argv[i] = strdup(argv[i]);
252 17846 : if (!new_argv[i])
253 : {
254 0 : write_stderr("out of memory\n");
255 0 : exit(1);
256 : }
257 : }
258 3758 : new_argv[argc] = NULL;
259 :
260 : #if defined(__darwin__)
261 :
262 : /*
263 : * macOS has a static copy of the argv pointer, which we may fix like
264 : * so:
265 : */
266 : *_NSGetArgv() = new_argv;
267 : #endif
268 :
269 3758 : argv = new_argv;
270 : }
271 : #endif /* PS_USE_CLOBBER_ARGV */
272 :
273 3758 : return argv;
274 : }
275 :
276 : /*
277 : * Call this once during subprocess startup to set the identification
278 : * values.
279 : *
280 : * If fixed_part is NULL, a default will be obtained from MyBackendType.
281 : *
282 : * At this point, the original argv[] array may be overwritten.
283 : */
284 : void
285 43622 : init_ps_display(const char *fixed_part)
286 : {
287 : #ifndef PS_USE_NONE
288 : bool save_update_process_title;
289 : #endif
290 :
291 : Assert(fixed_part || MyBackendType);
292 43622 : if (!fixed_part)
293 12936 : fixed_part = GetBackendTypeDesc(MyBackendType);
294 :
295 : #ifndef PS_USE_NONE
296 : /* no ps display for stand-alone backend */
297 43622 : if (!IsUnderPostmaster)
298 0 : return;
299 :
300 : /* no ps display if you didn't call save_ps_display_args() */
301 43622 : if (!save_argv)
302 0 : return;
303 :
304 : #ifdef PS_USE_CLOBBER_ARGV
305 : /* If ps_buffer is a pointer, it might still be null */
306 43622 : if (!ps_buffer)
307 0 : return;
308 :
309 : /* make extra argv slots point at end_of_area (a NUL) */
310 225736 : for (int i = 1; i < save_argc; i++)
311 182114 : save_argv[i] = ps_buffer + ps_buffer_size;
312 : #endif /* PS_USE_CLOBBER_ARGV */
313 :
314 : /*
315 : * Make fixed prefix of ps display.
316 : */
317 :
318 : #if defined(PS_USE_SETPROCTITLE) || defined(PS_USE_SETPROCTITLE_FAST)
319 :
320 : /*
321 : * apparently setproctitle() already adds a `progname:' prefix to the ps
322 : * line
323 : */
324 : #define PROGRAM_NAME_PREFIX ""
325 : #else
326 : #define PROGRAM_NAME_PREFIX "postgres: "
327 : #endif
328 :
329 43622 : if (*cluster_name == '\0')
330 : {
331 8512 : snprintf(ps_buffer, ps_buffer_size,
332 : PROGRAM_NAME_PREFIX "%s ",
333 : fixed_part);
334 : }
335 : else
336 : {
337 35110 : snprintf(ps_buffer, ps_buffer_size,
338 : PROGRAM_NAME_PREFIX "%s: %s ",
339 : cluster_name, fixed_part);
340 : }
341 :
342 43622 : ps_buffer_cur_len = ps_buffer_fixed_size = strlen(ps_buffer);
343 :
344 : /*
345 : * On the first run, force the update.
346 : */
347 43622 : save_update_process_title = update_process_title;
348 43622 : update_process_title = true;
349 43622 : set_ps_display("");
350 43622 : update_process_title = save_update_process_title;
351 : #endif /* not PS_USE_NONE */
352 : }
353 :
354 : #ifndef PS_USE_NONE
355 : /*
356 : * update_ps_display_precheck
357 : * Helper function to determine if updating the process title is
358 : * something that we need to do.
359 : */
360 : static bool
361 1896092 : update_ps_display_precheck(void)
362 : {
363 : /* update_process_title=off disables updates */
364 1896092 : if (!update_process_title)
365 0 : return false;
366 :
367 : /* no ps display for stand-alone backend */
368 1896092 : if (!IsUnderPostmaster)
369 149226 : return false;
370 :
371 : #ifdef PS_USE_CLOBBER_ARGV
372 : /* If ps_buffer is a pointer, it might still be null */
373 1746866 : if (!ps_buffer)
374 0 : return false;
375 : #endif
376 :
377 1746866 : return true;
378 : }
379 : #endif /* not PS_USE_NONE */
380 :
381 : /*
382 : * set_ps_display_suffix
383 : * Adjust the process title to append 'suffix' onto the end with a space
384 : * between it and the current process title.
385 : */
386 : void
387 2776 : set_ps_display_suffix(const char *suffix)
388 : {
389 : #ifndef PS_USE_NONE
390 : size_t len;
391 :
392 : /* first, check if we need to update the process title */
393 2776 : if (!update_ps_display_precheck())
394 0 : return;
395 :
396 : /* if there's already a suffix, overwrite it */
397 2776 : if (ps_buffer_nosuffix_len > 0)
398 0 : ps_buffer_cur_len = ps_buffer_nosuffix_len;
399 : else
400 2776 : ps_buffer_nosuffix_len = ps_buffer_cur_len;
401 :
402 2776 : len = strlen(suffix);
403 :
404 : /* check if we have enough space to append the suffix */
405 2776 : if (ps_buffer_cur_len + len + 1 >= ps_buffer_size)
406 : {
407 : /* not enough space. Check the buffer isn't full already */
408 0 : if (ps_buffer_cur_len < ps_buffer_size - 1)
409 : {
410 : /* append a space before the suffix */
411 0 : ps_buffer[ps_buffer_cur_len++] = ' ';
412 :
413 : /* just add what we can and fill the ps_buffer */
414 0 : memcpy(ps_buffer + ps_buffer_cur_len, suffix,
415 0 : ps_buffer_size - ps_buffer_cur_len - 1);
416 0 : ps_buffer[ps_buffer_size - 1] = '\0';
417 0 : ps_buffer_cur_len = ps_buffer_size - 1;
418 : }
419 : }
420 : else
421 : {
422 2776 : ps_buffer[ps_buffer_cur_len++] = ' ';
423 2776 : memcpy(ps_buffer + ps_buffer_cur_len, suffix, len + 1);
424 2776 : ps_buffer_cur_len = ps_buffer_cur_len + len;
425 : }
426 :
427 : Assert(strlen(ps_buffer) == ps_buffer_cur_len);
428 :
429 : /* and set the new title */
430 2776 : flush_ps_display();
431 : #endif /* not PS_USE_NONE */
432 : }
433 :
434 : /*
435 : * set_ps_display_remove_suffix
436 : * Remove the process display suffix added by set_ps_display_suffix
437 : */
438 : void
439 2770 : set_ps_display_remove_suffix(void)
440 : {
441 : #ifndef PS_USE_NONE
442 : /* first, check if we need to update the process title */
443 2770 : if (!update_ps_display_precheck())
444 0 : return;
445 :
446 : /* check we added a suffix */
447 2770 : if (ps_buffer_nosuffix_len == 0)
448 0 : return; /* no suffix */
449 :
450 : /* remove the suffix from ps_buffer */
451 2770 : ps_buffer[ps_buffer_nosuffix_len] = '\0';
452 2770 : ps_buffer_cur_len = ps_buffer_nosuffix_len;
453 2770 : ps_buffer_nosuffix_len = 0;
454 :
455 : Assert(ps_buffer_cur_len == strlen(ps_buffer));
456 :
457 : /* and set the new title */
458 2770 : flush_ps_display();
459 : #endif /* not PS_USE_NONE */
460 : }
461 :
462 : /*
463 : * Call this to update the ps status display to a fixed prefix plus an
464 : * indication of what you're currently doing passed in the argument.
465 : *
466 : * 'len' must be the same as strlen(activity)
467 : */
468 : void
469 1890546 : set_ps_display_with_len(const char *activity, size_t len)
470 : {
471 : Assert(strlen(activity) == len);
472 :
473 : #ifndef PS_USE_NONE
474 : /* first, check if we need to update the process title */
475 1890546 : if (!update_ps_display_precheck())
476 149226 : return;
477 :
478 : /* wipe out any suffix when the title is completely changed */
479 1741320 : ps_buffer_nosuffix_len = 0;
480 :
481 : /* Update ps_buffer to contain both fixed part and activity */
482 1741320 : if (ps_buffer_fixed_size + len >= ps_buffer_size)
483 : {
484 : /* handle the case where ps_buffer doesn't have enough space */
485 0 : memcpy(ps_buffer + ps_buffer_fixed_size, activity,
486 0 : ps_buffer_size - ps_buffer_fixed_size - 1);
487 0 : ps_buffer[ps_buffer_size - 1] = '\0';
488 0 : ps_buffer_cur_len = ps_buffer_size - 1;
489 : }
490 : else
491 : {
492 1741320 : memcpy(ps_buffer + ps_buffer_fixed_size, activity, len + 1);
493 1741320 : ps_buffer_cur_len = ps_buffer_fixed_size + len;
494 : }
495 : Assert(strlen(ps_buffer) == ps_buffer_cur_len);
496 :
497 : /* Transmit new setting to kernel, if necessary */
498 1741320 : flush_ps_display();
499 : #endif /* not PS_USE_NONE */
500 : }
501 :
502 : #ifndef PS_USE_NONE
503 : static void
504 1746866 : flush_ps_display(void)
505 : {
506 : #ifdef PS_USE_SETPROCTITLE
507 : setproctitle("%s", ps_buffer);
508 : #elif defined(PS_USE_SETPROCTITLE_FAST)
509 : setproctitle_fast("%s", ps_buffer);
510 : #endif
511 :
512 : #ifdef PS_USE_CLOBBER_ARGV
513 : /* pad unused memory; need only clobber remainder of old status string */
514 1746866 : if (last_status_len > ps_buffer_cur_len)
515 733870 : MemSet(ps_buffer + ps_buffer_cur_len, PS_PADDING,
516 : last_status_len - ps_buffer_cur_len);
517 1746866 : last_status_len = ps_buffer_cur_len;
518 : #endif /* PS_USE_CLOBBER_ARGV */
519 :
520 : #ifdef PS_USE_WIN32
521 : {
522 : /*
523 : * Win32 does not support showing any changed arguments. To make it at
524 : * all possible to track which backend is doing what, we create a
525 : * named object that can be viewed with for example Process Explorer.
526 : */
527 : static HANDLE ident_handle = INVALID_HANDLE_VALUE;
528 : char name[PS_BUFFER_SIZE + 32];
529 :
530 : if (ident_handle != INVALID_HANDLE_VALUE)
531 : CloseHandle(ident_handle);
532 :
533 : sprintf(name, "pgident(%d): %s", MyProcPid, ps_buffer);
534 :
535 : ident_handle = CreateEvent(NULL, TRUE, FALSE, name);
536 : }
537 : #endif /* PS_USE_WIN32 */
538 1746866 : }
539 : #endif /* not PS_USE_NONE */
540 :
541 : /*
542 : * Returns what's currently in the ps display, in case someone needs
543 : * it. Note that only the activity part is returned. On some platforms
544 : * the string will not be null-terminated, so return the effective
545 : * length into *displen.
546 : */
547 : const char *
548 36 : get_ps_display(int *displen)
549 : {
550 : #ifdef PS_USE_CLOBBER_ARGV
551 : /* If ps_buffer is a pointer, it might still be null */
552 36 : if (!ps_buffer)
553 : {
554 0 : *displen = 0;
555 0 : return "";
556 : }
557 : #endif
558 :
559 : #ifndef PS_USE_NONE
560 36 : *displen = (int) (ps_buffer_cur_len - ps_buffer_fixed_size);
561 :
562 36 : return ps_buffer + ps_buffer_fixed_size;
563 : #else
564 : *displen = 0;
565 : return "";
566 : #endif
567 : }
|