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 : /*
105 : * Call this early in startup to save the original argc/argv values.
106 : * If needed, we make a copy of the original argv[] array to preserve it
107 : * from being clobbered by subsequent ps_display actions.
108 : *
109 : * (The original argv[] will not be overwritten by this routine, but may be
110 : * overwritten during init_ps_display. Also, the physical location of the
111 : * environment strings may be moved, so this should be called before any code
112 : * that might try to hang onto a getenv() result. But see hack for musl
113 : * within.)
114 : *
115 : * Note that in case of failure this cannot call elog() as that is not
116 : * initialized yet. We rely on write_stderr() instead.
117 : */
118 : char **
119 3308 : save_ps_display_args(int argc, char **argv)
120 : {
121 3308 : save_argc = argc;
122 3308 : save_argv = argv;
123 :
124 : #if defined(PS_USE_CLOBBER_ARGV)
125 :
126 : /*
127 : * If we're going to overwrite the argv area, count the available space.
128 : * Also move the environment strings to make additional room.
129 : */
130 : {
131 3308 : char *end_of_area = NULL;
132 : char **new_environ;
133 : int i;
134 :
135 : /*
136 : * check for contiguous argv strings
137 : */
138 18706 : for (i = 0; i < argc; i++)
139 : {
140 15398 : if (i == 0 || end_of_area + 1 == argv[i])
141 15398 : end_of_area = argv[i] + strlen(argv[i]);
142 : }
143 :
144 3308 : if (end_of_area == NULL) /* probably can't happen? */
145 : {
146 0 : ps_buffer = NULL;
147 0 : ps_buffer_size = 0;
148 0 : return argv;
149 : }
150 :
151 : /*
152 : * check for contiguous environ strings following argv
153 : */
154 117520 : for (i = 0; environ[i] != NULL; i++)
155 : {
156 114212 : if (end_of_area + 1 == environ[i])
157 : {
158 : /*
159 : * The musl dynamic linker keeps a static pointer to the
160 : * initial value of LD_LIBRARY_PATH, if that is defined in the
161 : * process's environment. Therefore, we must not overwrite the
162 : * value of that setting and thus cannot advance end_of_area
163 : * beyond it. Musl does not define any identifying compiler
164 : * symbol, so we have to do this unless we see a symbol
165 : * identifying a Linux libc we know is safe.
166 : */
167 : #if defined(__linux__) && (!defined(__GLIBC__) && !defined(__UCLIBC__))
168 : if (strncmp(environ[i], "LD_LIBRARY_PATH=", 16) == 0)
169 : {
170 : /*
171 : * We can overwrite the name, but stop at the equals sign.
172 : * Future loop iterations will not find any more
173 : * contiguous space, but we don't break early because we
174 : * need to count the total number of environ[] entries.
175 : */
176 : end_of_area = environ[i] + 15;
177 : }
178 : else
179 : #endif
180 : {
181 114212 : end_of_area = environ[i] + strlen(environ[i]);
182 : }
183 : }
184 : }
185 :
186 3308 : ps_buffer = argv[0];
187 3308 : last_status_len = ps_buffer_size = end_of_area - argv[0];
188 :
189 : /*
190 : * move the environment out of the way
191 : */
192 3308 : new_environ = (char **) malloc((i + 1) * sizeof(char *));
193 3308 : if (!new_environ)
194 : {
195 0 : write_stderr("out of memory\n");
196 0 : exit(1);
197 : }
198 117520 : for (i = 0; environ[i] != NULL; i++)
199 : {
200 114212 : new_environ[i] = strdup(environ[i]);
201 114212 : if (!new_environ[i])
202 : {
203 0 : write_stderr("out of memory\n");
204 0 : exit(1);
205 : }
206 : }
207 3308 : new_environ[i] = NULL;
208 3308 : environ = new_environ;
209 : }
210 :
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[], even though
216 : * postmaster.c finishes looking at argv[] long before we ever consider
217 : * changing the ps display. On some platforms, getopt() keeps pointers
218 : * into the argv array, and will get horribly confused when it is
219 : * re-called to analyze a subprocess' argument string if the argv storage
220 : * has been clobbered meanwhile. Other platforms have other dependencies
221 : * on argv[].
222 : */
223 : {
224 : char **new_argv;
225 : int i;
226 :
227 3308 : new_argv = (char **) malloc((argc + 1) * sizeof(char *));
228 3308 : if (!new_argv)
229 : {
230 0 : write_stderr("out of memory\n");
231 0 : exit(1);
232 : }
233 18706 : for (i = 0; i < argc; i++)
234 : {
235 15398 : new_argv[i] = strdup(argv[i]);
236 15398 : if (!new_argv[i])
237 : {
238 0 : write_stderr("out of memory\n");
239 0 : exit(1);
240 : }
241 : }
242 3308 : new_argv[argc] = NULL;
243 :
244 : #if defined(__darwin__)
245 :
246 : /*
247 : * macOS has a static copy of the argv pointer, which we may fix like
248 : * so:
249 : */
250 : *_NSGetArgv() = new_argv;
251 : #endif
252 :
253 3308 : argv = new_argv;
254 : }
255 : #endif /* PS_USE_CLOBBER_ARGV */
256 :
257 3308 : return argv;
258 : }
259 :
260 : /*
261 : * Call this once during subprocess startup to set the identification
262 : * values.
263 : *
264 : * If fixed_part is NULL, a default will be obtained from MyBackendType.
265 : *
266 : * At this point, the original argv[] array may be overwritten.
267 : */
268 : void
269 37302 : init_ps_display(const char *fixed_part)
270 : {
271 : #ifndef PS_USE_NONE
272 : bool save_update_process_title;
273 : #endif
274 :
275 : Assert(fixed_part || MyBackendType);
276 37302 : if (!fixed_part)
277 7456 : fixed_part = GetBackendTypeDesc(MyBackendType);
278 :
279 : #ifndef PS_USE_NONE
280 : /* no ps display for stand-alone backend */
281 37302 : if (!IsUnderPostmaster)
282 0 : return;
283 :
284 : /* no ps display if you didn't call save_ps_display_args() */
285 37302 : if (!save_argv)
286 0 : return;
287 :
288 : #ifdef PS_USE_CLOBBER_ARGV
289 : /* If ps_buffer is a pointer, it might still be null */
290 37302 : if (!ps_buffer)
291 0 : return;
292 :
293 : /* make extra argv slots point at end_of_area (a NUL) */
294 182142 : for (int i = 1; i < save_argc; i++)
295 144840 : save_argv[i] = ps_buffer + ps_buffer_size;
296 : #endif /* PS_USE_CLOBBER_ARGV */
297 :
298 : /*
299 : * Make fixed prefix of ps display.
300 : */
301 :
302 : #if defined(PS_USE_SETPROCTITLE) || defined(PS_USE_SETPROCTITLE_FAST)
303 :
304 : /*
305 : * apparently setproctitle() already adds a `progname:' prefix to the ps
306 : * line
307 : */
308 : #define PROGRAM_NAME_PREFIX ""
309 : #else
310 : #define PROGRAM_NAME_PREFIX "postgres: "
311 : #endif
312 :
313 37302 : if (*cluster_name == '\0')
314 : {
315 6258 : snprintf(ps_buffer, ps_buffer_size,
316 : PROGRAM_NAME_PREFIX "%s ",
317 : fixed_part);
318 : }
319 : else
320 : {
321 31044 : snprintf(ps_buffer, ps_buffer_size,
322 : PROGRAM_NAME_PREFIX "%s: %s ",
323 : cluster_name, fixed_part);
324 : }
325 :
326 37302 : ps_buffer_cur_len = ps_buffer_fixed_size = strlen(ps_buffer);
327 :
328 : /*
329 : * On the first run, force the update.
330 : */
331 37302 : save_update_process_title = update_process_title;
332 37302 : update_process_title = true;
333 37302 : set_ps_display("");
334 37302 : update_process_title = save_update_process_title;
335 : #endif /* not PS_USE_NONE */
336 : }
337 :
338 : #ifndef PS_USE_NONE
339 : /*
340 : * update_ps_display_precheck
341 : * Helper function to determine if updating the process title is
342 : * something that we need to do.
343 : */
344 : static bool
345 1704202 : update_ps_display_precheck(void)
346 : {
347 : /* update_process_title=off disables updates */
348 1704202 : if (!update_process_title)
349 0 : return false;
350 :
351 : /* no ps display for stand-alone backend */
352 1704202 : if (!IsUnderPostmaster)
353 130732 : return false;
354 :
355 : #ifdef PS_USE_CLOBBER_ARGV
356 : /* If ps_buffer is a pointer, it might still be null */
357 1573470 : if (!ps_buffer)
358 0 : return false;
359 : #endif
360 :
361 1573470 : return true;
362 : }
363 : #endif /* not PS_USE_NONE */
364 :
365 : /*
366 : * set_ps_display_suffix
367 : * Adjust the process title to append 'suffix' onto the end with a space
368 : * between it and the current process title.
369 : */
370 : void
371 2594 : set_ps_display_suffix(const char *suffix)
372 : {
373 : #ifndef PS_USE_NONE
374 : size_t len;
375 :
376 : /* first, check if we need to update the process title */
377 2594 : if (!update_ps_display_precheck())
378 0 : return;
379 :
380 : /* if there's already a suffix, overwrite it */
381 2594 : if (ps_buffer_nosuffix_len > 0)
382 0 : ps_buffer_cur_len = ps_buffer_nosuffix_len;
383 : else
384 2594 : ps_buffer_nosuffix_len = ps_buffer_cur_len;
385 :
386 2594 : len = strlen(suffix);
387 :
388 : /* check if we have enough space to append the suffix */
389 2594 : if (ps_buffer_cur_len + len + 1 >= ps_buffer_size)
390 : {
391 : /* not enough space. Check the buffer isn't full already */
392 0 : if (ps_buffer_cur_len < ps_buffer_size - 1)
393 : {
394 : /* append a space before the suffix */
395 0 : ps_buffer[ps_buffer_cur_len++] = ' ';
396 :
397 : /* just add what we can and fill the ps_buffer */
398 0 : memcpy(ps_buffer + ps_buffer_cur_len, suffix,
399 0 : ps_buffer_size - ps_buffer_cur_len - 1);
400 0 : ps_buffer[ps_buffer_size - 1] = '\0';
401 0 : ps_buffer_cur_len = ps_buffer_size - 1;
402 : }
403 : }
404 : else
405 : {
406 2594 : ps_buffer[ps_buffer_cur_len++] = ' ';
407 2594 : memcpy(ps_buffer + ps_buffer_cur_len, suffix, len + 1);
408 2594 : ps_buffer_cur_len = ps_buffer_cur_len + len;
409 : }
410 :
411 : Assert(strlen(ps_buffer) == ps_buffer_cur_len);
412 :
413 : /* and set the new title */
414 2594 : flush_ps_display();
415 : #endif /* not PS_USE_NONE */
416 : }
417 :
418 : /*
419 : * set_ps_display_remove_suffix
420 : * Remove the process display suffix added by set_ps_display_suffix
421 : */
422 : void
423 2582 : set_ps_display_remove_suffix(void)
424 : {
425 : #ifndef PS_USE_NONE
426 : /* first, check if we need to update the process title */
427 2582 : if (!update_ps_display_precheck())
428 0 : return;
429 :
430 : /* check we added a suffix */
431 2582 : if (ps_buffer_nosuffix_len == 0)
432 0 : return; /* no suffix */
433 :
434 : /* remove the suffix from ps_buffer */
435 2582 : ps_buffer[ps_buffer_nosuffix_len] = '\0';
436 2582 : ps_buffer_cur_len = ps_buffer_nosuffix_len;
437 2582 : ps_buffer_nosuffix_len = 0;
438 :
439 : Assert(ps_buffer_cur_len == strlen(ps_buffer));
440 :
441 : /* and set the new title */
442 2582 : flush_ps_display();
443 : #endif /* not PS_USE_NONE */
444 : }
445 :
446 : /*
447 : * Call this to update the ps status display to a fixed prefix plus an
448 : * indication of what you're currently doing passed in the argument.
449 : *
450 : * 'len' must be the same as strlen(activity)
451 : */
452 : void
453 1699026 : set_ps_display_with_len(const char *activity, size_t len)
454 : {
455 : Assert(strlen(activity) == len);
456 :
457 : #ifndef PS_USE_NONE
458 : /* first, check if we need to update the process title */
459 1699026 : if (!update_ps_display_precheck())
460 130732 : return;
461 :
462 : /* wipe out any suffix when the title is completely changed */
463 1568294 : ps_buffer_nosuffix_len = 0;
464 :
465 : /* Update ps_buffer to contain both fixed part and activity */
466 1568294 : if (ps_buffer_fixed_size + len >= ps_buffer_size)
467 : {
468 : /* handle the case where ps_buffer doesn't have enough space */
469 0 : memcpy(ps_buffer + ps_buffer_fixed_size, activity,
470 0 : ps_buffer_size - ps_buffer_fixed_size - 1);
471 0 : ps_buffer[ps_buffer_size - 1] = '\0';
472 0 : ps_buffer_cur_len = ps_buffer_size - 1;
473 : }
474 : else
475 : {
476 1568294 : memcpy(ps_buffer + ps_buffer_fixed_size, activity, len + 1);
477 1568294 : ps_buffer_cur_len = ps_buffer_fixed_size + len;
478 : }
479 : Assert(strlen(ps_buffer) == ps_buffer_cur_len);
480 :
481 : /* Transmit new setting to kernel, if necessary */
482 1568294 : flush_ps_display();
483 : #endif /* not PS_USE_NONE */
484 : }
485 :
486 : #ifndef PS_USE_NONE
487 : static void
488 1573470 : flush_ps_display(void)
489 : {
490 : #ifdef PS_USE_SETPROCTITLE
491 : setproctitle("%s", ps_buffer);
492 : #elif defined(PS_USE_SETPROCTITLE_FAST)
493 : setproctitle_fast("%s", ps_buffer);
494 : #endif
495 :
496 : #ifdef PS_USE_CLOBBER_ARGV
497 : /* pad unused memory; need only clobber remainder of old status string */
498 1573470 : if (last_status_len > ps_buffer_cur_len)
499 683974 : MemSet(ps_buffer + ps_buffer_cur_len, PS_PADDING,
500 : last_status_len - ps_buffer_cur_len);
501 1573470 : last_status_len = ps_buffer_cur_len;
502 : #endif /* PS_USE_CLOBBER_ARGV */
503 :
504 : #ifdef PS_USE_WIN32
505 : {
506 : /*
507 : * Win32 does not support showing any changed arguments. To make it at
508 : * all possible to track which backend is doing what, we create a
509 : * named object that can be viewed with for example Process Explorer.
510 : */
511 : static HANDLE ident_handle = INVALID_HANDLE_VALUE;
512 : char name[PS_BUFFER_SIZE + 32];
513 :
514 : if (ident_handle != INVALID_HANDLE_VALUE)
515 : CloseHandle(ident_handle);
516 :
517 : sprintf(name, "pgident(%d): %s", MyProcPid, ps_buffer);
518 :
519 : ident_handle = CreateEvent(NULL, TRUE, FALSE, name);
520 : }
521 : #endif /* PS_USE_WIN32 */
522 1573470 : }
523 : #endif /* not PS_USE_NONE */
524 :
525 : /*
526 : * Returns what's currently in the ps display, in case someone needs
527 : * it. Note that only the activity part is returned. On some platforms
528 : * the string will not be null-terminated, so return the effective
529 : * length into *displen.
530 : */
531 : const char *
532 36 : get_ps_display(int *displen)
533 : {
534 : #ifdef PS_USE_CLOBBER_ARGV
535 : /* If ps_buffer is a pointer, it might still be null */
536 36 : if (!ps_buffer)
537 : {
538 0 : *displen = 0;
539 0 : return "";
540 : }
541 : #endif
542 :
543 : #ifndef PS_USE_NONE
544 36 : *displen = (int) (ps_buffer_cur_len - ps_buffer_fixed_size);
545 :
546 36 : return ps_buffer + ps_buffer_fixed_size;
547 : #else
548 : *displen = 0;
549 : return "";
550 : #endif
551 : }
|