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