/src/postgres/src/backend/utils/misc/ps_status.c
Line | Count | Source |
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 | | save_ps_display_args(int argc, char **argv) |
131 | 0 | { |
132 | 0 | save_argc = argc; |
133 | 0 | save_argv = argv; |
134 | |
|
135 | 0 | #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 | 0 | { |
142 | 0 | char *end_of_area = NULL; |
143 | 0 | char **new_environ; |
144 | 0 | int i; |
145 | | |
146 | | /* |
147 | | * check for contiguous argv strings |
148 | | */ |
149 | 0 | for (i = 0; i < argc; i++) |
150 | 0 | { |
151 | 0 | if (i == 0 || end_of_area + 1 == argv[i]) |
152 | 0 | end_of_area = argv[i] + strlen(argv[i]); |
153 | 0 | } |
154 | |
|
155 | 0 | if (end_of_area == NULL) /* probably can't happen? */ |
156 | 0 | { |
157 | 0 | ps_buffer = NULL; |
158 | 0 | ps_buffer_size = 0; |
159 | 0 | return argv; |
160 | 0 | } |
161 | | |
162 | | /* |
163 | | * check for contiguous environ strings following argv |
164 | | */ |
165 | 0 | for (i = 0; environ[i] != NULL; i++) |
166 | 0 | { |
167 | 0 | if (end_of_area + 1 == environ[i]) |
168 | 0 | { |
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 | 0 | { |
192 | 0 | end_of_area = environ[i] + strlen(environ[i]); |
193 | 0 | } |
194 | 0 | } |
195 | 0 | } |
196 | |
|
197 | 0 | ps_buffer = argv[0]; |
198 | 0 | last_status_len = ps_buffer_size = end_of_area - argv[0]; |
199 | | |
200 | | /* |
201 | | * move the environment out of the way |
202 | | */ |
203 | 0 | new_environ = (char **) malloc((i + 1) * sizeof(char *)); |
204 | 0 | if (!new_environ) |
205 | 0 | { |
206 | 0 | write_stderr("out of memory\n"); |
207 | 0 | exit(1); |
208 | 0 | } |
209 | 0 | for (i = 0; environ[i] != NULL; i++) |
210 | 0 | { |
211 | 0 | new_environ[i] = strdup(environ[i]); |
212 | 0 | if (!new_environ[i]) |
213 | 0 | { |
214 | 0 | write_stderr("out of memory\n"); |
215 | 0 | exit(1); |
216 | 0 | } |
217 | 0 | } |
218 | 0 | new_environ[i] = NULL; |
219 | 0 | environ = new_environ; |
220 | | |
221 | | /* See notes about Valgrind above. */ |
222 | | #ifdef USE_VALGRIND |
223 | | ps_status_new_environ = new_environ; |
224 | | #endif |
225 | 0 | } |
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 | 0 | { |
240 | 0 | char **new_argv; |
241 | 0 | int i; |
242 | |
|
243 | 0 | new_argv = (char **) malloc((argc + 1) * sizeof(char *)); |
244 | 0 | if (!new_argv) |
245 | 0 | { |
246 | 0 | write_stderr("out of memory\n"); |
247 | 0 | exit(1); |
248 | 0 | } |
249 | 0 | for (i = 0; i < argc; i++) |
250 | 0 | { |
251 | 0 | new_argv[i] = strdup(argv[i]); |
252 | 0 | if (!new_argv[i]) |
253 | 0 | { |
254 | 0 | write_stderr("out of memory\n"); |
255 | 0 | exit(1); |
256 | 0 | } |
257 | 0 | } |
258 | 0 | 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 | 0 | argv = new_argv; |
270 | 0 | } |
271 | 0 | #endif /* PS_USE_CLOBBER_ARGV */ |
272 | | |
273 | 0 | return argv; |
274 | 0 | } |
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 | | init_ps_display(const char *fixed_part) |
286 | 0 | { |
287 | 0 | #ifndef PS_USE_NONE |
288 | 0 | bool save_update_process_title; |
289 | 0 | #endif |
290 | |
|
291 | 0 | Assert(fixed_part || MyBackendType); |
292 | 0 | if (!fixed_part) |
293 | 0 | fixed_part = GetBackendTypeDesc(MyBackendType); |
294 | |
|
295 | 0 | #ifndef PS_USE_NONE |
296 | | /* no ps display for stand-alone backend */ |
297 | 0 | if (!IsUnderPostmaster) |
298 | 0 | return; |
299 | | |
300 | | /* no ps display if you didn't call save_ps_display_args() */ |
301 | 0 | if (!save_argv) |
302 | 0 | return; |
303 | | |
304 | 0 | #ifdef PS_USE_CLOBBER_ARGV |
305 | | /* If ps_buffer is a pointer, it might still be null */ |
306 | 0 | if (!ps_buffer) |
307 | 0 | return; |
308 | | |
309 | | /* make extra argv slots point at end_of_area (a NUL) */ |
310 | 0 | for (int i = 1; i < save_argc; i++) |
311 | 0 | save_argv[i] = ps_buffer + ps_buffer_size; |
312 | 0 | #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 | 0 | #define PROGRAM_NAME_PREFIX "postgres: " |
327 | 0 | #endif |
328 | |
|
329 | 0 | if (*cluster_name == '\0') |
330 | 0 | { |
331 | 0 | snprintf(ps_buffer, ps_buffer_size, |
332 | 0 | PROGRAM_NAME_PREFIX "%s ", |
333 | 0 | fixed_part); |
334 | 0 | } |
335 | 0 | else |
336 | 0 | { |
337 | 0 | snprintf(ps_buffer, ps_buffer_size, |
338 | 0 | PROGRAM_NAME_PREFIX "%s: %s ", |
339 | 0 | cluster_name, fixed_part); |
340 | 0 | } |
341 | |
|
342 | 0 | ps_buffer_cur_len = ps_buffer_fixed_size = strlen(ps_buffer); |
343 | | |
344 | | /* |
345 | | * On the first run, force the update. |
346 | | */ |
347 | 0 | save_update_process_title = update_process_title; |
348 | 0 | update_process_title = true; |
349 | 0 | set_ps_display(""); |
350 | 0 | update_process_title = save_update_process_title; |
351 | 0 | #endif /* not PS_USE_NONE */ |
352 | 0 | } |
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 | | update_ps_display_precheck(void) |
362 | 0 | { |
363 | | /* update_process_title=off disables updates */ |
364 | 0 | if (!update_process_title) |
365 | 0 | return false; |
366 | | |
367 | | /* no ps display for stand-alone backend */ |
368 | 0 | if (!IsUnderPostmaster) |
369 | 0 | return false; |
370 | | |
371 | 0 | #ifdef PS_USE_CLOBBER_ARGV |
372 | | /* If ps_buffer is a pointer, it might still be null */ |
373 | 0 | if (!ps_buffer) |
374 | 0 | return false; |
375 | 0 | #endif |
376 | | |
377 | 0 | return true; |
378 | 0 | } |
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 | | set_ps_display_suffix(const char *suffix) |
388 | 0 | { |
389 | 0 | #ifndef PS_USE_NONE |
390 | 0 | size_t len; |
391 | | |
392 | | /* first, check if we need to update the process title */ |
393 | 0 | if (!update_ps_display_precheck()) |
394 | 0 | return; |
395 | | |
396 | | /* if there's already a suffix, overwrite it */ |
397 | 0 | if (ps_buffer_nosuffix_len > 0) |
398 | 0 | ps_buffer_cur_len = ps_buffer_nosuffix_len; |
399 | 0 | else |
400 | 0 | ps_buffer_nosuffix_len = ps_buffer_cur_len; |
401 | |
|
402 | 0 | len = strlen(suffix); |
403 | | |
404 | | /* check if we have enough space to append the suffix */ |
405 | 0 | if (ps_buffer_cur_len + len + 1 >= ps_buffer_size) |
406 | 0 | { |
407 | | /* not enough space. Check the buffer isn't full already */ |
408 | 0 | if (ps_buffer_cur_len < ps_buffer_size - 1) |
409 | 0 | { |
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 | 0 | } |
419 | 0 | } |
420 | 0 | else |
421 | 0 | { |
422 | 0 | ps_buffer[ps_buffer_cur_len++] = ' '; |
423 | 0 | memcpy(ps_buffer + ps_buffer_cur_len, suffix, len + 1); |
424 | 0 | ps_buffer_cur_len = ps_buffer_cur_len + len; |
425 | 0 | } |
426 | |
|
427 | 0 | Assert(strlen(ps_buffer) == ps_buffer_cur_len); |
428 | | |
429 | | /* and set the new title */ |
430 | 0 | flush_ps_display(); |
431 | 0 | #endif /* not PS_USE_NONE */ |
432 | 0 | } |
433 | | |
434 | | /* |
435 | | * set_ps_display_remove_suffix |
436 | | * Remove the process display suffix added by set_ps_display_suffix |
437 | | */ |
438 | | void |
439 | | set_ps_display_remove_suffix(void) |
440 | 0 | { |
441 | 0 | #ifndef PS_USE_NONE |
442 | | /* first, check if we need to update the process title */ |
443 | 0 | if (!update_ps_display_precheck()) |
444 | 0 | return; |
445 | | |
446 | | /* check we added a suffix */ |
447 | 0 | if (ps_buffer_nosuffix_len == 0) |
448 | 0 | return; /* no suffix */ |
449 | | |
450 | | /* remove the suffix from ps_buffer */ |
451 | 0 | ps_buffer[ps_buffer_nosuffix_len] = '\0'; |
452 | 0 | ps_buffer_cur_len = ps_buffer_nosuffix_len; |
453 | 0 | ps_buffer_nosuffix_len = 0; |
454 | |
|
455 | 0 | Assert(ps_buffer_cur_len == strlen(ps_buffer)); |
456 | | |
457 | | /* and set the new title */ |
458 | 0 | flush_ps_display(); |
459 | 0 | #endif /* not PS_USE_NONE */ |
460 | 0 | } |
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 | | set_ps_display_with_len(const char *activity, size_t len) |
470 | 0 | { |
471 | 0 | Assert(strlen(activity) == len); |
472 | |
|
473 | 0 | #ifndef PS_USE_NONE |
474 | | /* first, check if we need to update the process title */ |
475 | 0 | if (!update_ps_display_precheck()) |
476 | 0 | return; |
477 | | |
478 | | /* wipe out any suffix when the title is completely changed */ |
479 | 0 | ps_buffer_nosuffix_len = 0; |
480 | | |
481 | | /* Update ps_buffer to contain both fixed part and activity */ |
482 | 0 | if (ps_buffer_fixed_size + len >= ps_buffer_size) |
483 | 0 | { |
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 | 0 | } |
490 | 0 | else |
491 | 0 | { |
492 | 0 | memcpy(ps_buffer + ps_buffer_fixed_size, activity, len + 1); |
493 | 0 | ps_buffer_cur_len = ps_buffer_fixed_size + len; |
494 | 0 | } |
495 | 0 | Assert(strlen(ps_buffer) == ps_buffer_cur_len); |
496 | | |
497 | | /* Transmit new setting to kernel, if necessary */ |
498 | 0 | flush_ps_display(); |
499 | 0 | #endif /* not PS_USE_NONE */ |
500 | 0 | } |
501 | | |
502 | | #ifndef PS_USE_NONE |
503 | | static void |
504 | | flush_ps_display(void) |
505 | 0 | { |
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 | 0 | #ifdef PS_USE_CLOBBER_ARGV |
513 | | /* pad unused memory; need only clobber remainder of old status string */ |
514 | 0 | if (last_status_len > ps_buffer_cur_len) |
515 | 0 | MemSet(ps_buffer + ps_buffer_cur_len, PS_PADDING, |
516 | 0 | last_status_len - ps_buffer_cur_len); |
517 | 0 | last_status_len = ps_buffer_cur_len; |
518 | 0 | #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 | 0 | } |
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 | | get_ps_display(int *displen) |
549 | 0 | { |
550 | 0 | #ifdef PS_USE_CLOBBER_ARGV |
551 | | /* If ps_buffer is a pointer, it might still be null */ |
552 | 0 | if (!ps_buffer) |
553 | 0 | { |
554 | 0 | *displen = 0; |
555 | 0 | return ""; |
556 | 0 | } |
557 | 0 | #endif |
558 | | |
559 | 0 | #ifndef PS_USE_NONE |
560 | 0 | *displen = (int) (ps_buffer_cur_len - ps_buffer_fixed_size); |
561 | |
|
562 | 0 | return ps_buffer + ps_buffer_fixed_size; |
563 | | #else |
564 | | *displen = 0; |
565 | | return ""; |
566 | | #endif |
567 | 0 | } |