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 **
117 3280 : save_ps_display_args(int argc, char **argv)
118 : {
119 3280 : save_argc = argc;
120 3280 : 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 : {
129 3280 : char *end_of_area = NULL;
130 : char **new_environ;
131 : int i;
132 :
133 : /*
134 : * check for contiguous argv strings
135 : */
136 18134 : for (i = 0; i < argc; i++)
137 : {
138 14854 : if (i == 0 || end_of_area + 1 == argv[i])
139 14854 : end_of_area = argv[i] + strlen(argv[i]);
140 : }
141 :
142 3280 : if (end_of_area == NULL) /* probably can't happen? */
143 : {
144 0 : ps_buffer = NULL;
145 0 : ps_buffer_size = 0;
146 0 : return argv;
147 : }
148 :
149 : /*
150 : * check for contiguous environ strings following argv
151 : */
152 116536 : for (i = 0; environ[i] != NULL; i++)
153 : {
154 113256 : 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 : {
179 113256 : end_of_area = environ[i] + strlen(environ[i]);
180 : }
181 : }
182 : }
183 :
184 3280 : ps_buffer = argv[0];
185 3280 : last_status_len = ps_buffer_size = end_of_area - argv[0];
186 :
187 : /*
188 : * move the environment out of the way
189 : */
190 3280 : new_environ = (char **) malloc((i + 1) * sizeof(char *));
191 3280 : if (!new_environ)
192 : {
193 0 : write_stderr("out of memory\n");
194 0 : exit(1);
195 : }
196 116536 : for (i = 0; environ[i] != NULL; i++)
197 : {
198 113256 : new_environ[i] = strdup(environ[i]);
199 113256 : if (!new_environ[i])
200 : {
201 0 : write_stderr("out of memory\n");
202 0 : exit(1);
203 : }
204 : }
205 3280 : new_environ[i] = NULL;
206 3280 : 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 :
225 3280 : new_argv = (char **) malloc((argc + 1) * sizeof(char *));
226 3280 : if (!new_argv)
227 : {
228 0 : write_stderr("out of memory\n");
229 0 : exit(1);
230 : }
231 18134 : for (i = 0; i < argc; i++)
232 : {
233 14854 : new_argv[i] = strdup(argv[i]);
234 14854 : if (!new_argv[i])
235 : {
236 0 : write_stderr("out of memory\n");
237 0 : exit(1);
238 : }
239 : }
240 3280 : 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 3280 : argv = new_argv;
252 : }
253 : #endif /* PS_USE_CLOBBER_ARGV */
254 :
255 3280 : 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
267 35588 : init_ps_display(const char *fixed_part)
268 : {
269 : #ifndef PS_USE_NONE
270 : bool save_update_process_title;
271 : #endif
272 :
273 : Assert(fixed_part || MyBackendType);
274 35588 : if (!fixed_part)
275 6224 : fixed_part = GetBackendTypeDesc(MyBackendType);
276 :
277 : #ifndef PS_USE_NONE
278 : /* no ps display for stand-alone backend */
279 35588 : if (!IsUnderPostmaster)
280 0 : return;
281 :
282 : /* no ps display if you didn't call save_ps_display_args() */
283 35588 : if (!save_argv)
284 0 : return;
285 :
286 : #ifdef PS_USE_CLOBBER_ARGV
287 : /* If ps_buffer is a pointer, it might still be null */
288 35588 : if (!ps_buffer)
289 0 : return;
290 :
291 : /* make extra argv slots point at end_of_area (a NUL) */
292 175206 : for (int i = 1; i < save_argc; i++)
293 139618 : 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 :
311 35588 : if (*cluster_name == '\0')
312 : {
313 6238 : snprintf(ps_buffer, ps_buffer_size,
314 : PROGRAM_NAME_PREFIX "%s ",
315 : fixed_part);
316 : }
317 : else
318 : {
319 29350 : snprintf(ps_buffer, ps_buffer_size,
320 : PROGRAM_NAME_PREFIX "%s: %s ",
321 : cluster_name, fixed_part);
322 : }
323 :
324 35588 : ps_buffer_cur_len = ps_buffer_fixed_size = strlen(ps_buffer);
325 :
326 : /*
327 : * On the first run, force the update.
328 : */
329 35588 : save_update_process_title = update_process_title;
330 35588 : update_process_title = true;
331 35588 : set_ps_display("");
332 35588 : 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
343 1684952 : update_ps_display_precheck(void)
344 : {
345 : /* update_process_title=off disables updates */
346 1684952 : if (!update_process_title)
347 0 : return false;
348 :
349 : /* no ps display for stand-alone backend */
350 1684952 : if (!IsUnderPostmaster)
351 130560 : return false;
352 :
353 : #ifdef PS_USE_CLOBBER_ARGV
354 : /* If ps_buffer is a pointer, it might still be null */
355 1554392 : if (!ps_buffer)
356 0 : return false;
357 : #endif
358 :
359 1554392 : 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 2696 : 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 2696 : if (!update_ps_display_precheck())
376 0 : return;
377 :
378 : /* if there's already a suffix, overwrite it */
379 2696 : if (ps_buffer_nosuffix_len > 0)
380 0 : ps_buffer_cur_len = ps_buffer_nosuffix_len;
381 : else
382 2696 : ps_buffer_nosuffix_len = ps_buffer_cur_len;
383 :
384 2696 : len = strlen(suffix);
385 :
386 : /* check if we have enough space to append the suffix */
387 2696 : if (ps_buffer_cur_len + len + 1 >= ps_buffer_size)
388 : {
389 : /* not enough space. Check the buffer isn't full already */
390 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 : {
404 2696 : ps_buffer[ps_buffer_cur_len++] = ' ';
405 2696 : memcpy(ps_buffer + ps_buffer_cur_len, suffix, len + 1);
406 2696 : ps_buffer_cur_len = ps_buffer_cur_len + len;
407 : }
408 :
409 : Assert(strlen(ps_buffer) == ps_buffer_cur_len);
410 :
411 : /* and set the new title */
412 2696 : 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 2682 : set_ps_display_remove_suffix(void)
422 : {
423 : #ifndef PS_USE_NONE
424 : /* first, check if we need to update the process title */
425 2682 : if (!update_ps_display_precheck())
426 0 : return;
427 :
428 : /* check we added a suffix */
429 2682 : if (ps_buffer_nosuffix_len == 0)
430 0 : return; /* no suffix */
431 :
432 : /* remove the suffix from ps_buffer */
433 2682 : ps_buffer[ps_buffer_nosuffix_len] = '\0';
434 2682 : ps_buffer_cur_len = ps_buffer_nosuffix_len;
435 2682 : ps_buffer_nosuffix_len = 0;
436 :
437 : Assert(ps_buffer_cur_len == strlen(ps_buffer));
438 :
439 : /* and set the new title */
440 2682 : 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 1679574 : set_ps_display_with_len(const char *activity, size_t len)
452 : {
453 : Assert(strlen(activity) == len);
454 :
455 : #ifndef PS_USE_NONE
456 : /* first, check if we need to update the process title */
457 1679574 : if (!update_ps_display_precheck())
458 130560 : return;
459 :
460 : /* wipe out any suffix when the title is completely changed */
461 1549014 : ps_buffer_nosuffix_len = 0;
462 :
463 : /* Update ps_buffer to contain both fixed part and activity */
464 1549014 : if (ps_buffer_fixed_size + len >= ps_buffer_size)
465 : {
466 : /* handle the case where ps_buffer doesn't have enough space */
467 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 : {
474 1549014 : memcpy(ps_buffer + ps_buffer_fixed_size, activity, len + 1);
475 1549014 : ps_buffer_cur_len = ps_buffer_fixed_size + len;
476 : }
477 : Assert(strlen(ps_buffer) == ps_buffer_cur_len);
478 :
479 : /* Transmit new setting to kernel, if necessary */
480 1549014 : flush_ps_display();
481 : #endif /* not PS_USE_NONE */
482 : }
483 :
484 : #ifndef PS_USE_NONE
485 : static void
486 1554392 : 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 */
496 1554392 : if (last_status_len > ps_buffer_cur_len)
497 677904 : MemSet(ps_buffer + ps_buffer_cur_len, PS_PADDING,
498 : last_status_len - ps_buffer_cur_len);
499 1554392 : 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 */
520 1554392 : }
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 *
530 36 : get_ps_display(int *displen)
531 : {
532 : #ifdef PS_USE_CLOBBER_ARGV
533 : /* If ps_buffer is a pointer, it might still be null */
534 36 : if (!ps_buffer)
535 : {
536 0 : *displen = 0;
537 0 : return "";
538 : }
539 : #endif
540 :
541 : #ifndef PS_USE_NONE
542 36 : *displen = (int) (ps_buffer_cur_len - ps_buffer_fixed_size);
543 :
544 36 : return ps_buffer + ps_buffer_fixed_size;
545 : #else
546 : *displen = 0;
547 : return "";
548 : #endif
549 : }
|