Allow "make check"-style testing to work with musl C library.
[pgsql.git] / src / backend / utils / misc / ps_status.c
blob9da6377402024466030a2fa3eb2db4b1f489e3b0
1 /*--------------------------------------------------------------------
2 * ps_status.c
4 * Routines to support changing the ps display of PostgreSQL backends
5 * to contain some useful information. Mechanism differs wildly across
6 * platforms.
8 * src/backend/utils/misc/ps_status.c
10 * Copyright (c) 2000-2024, PostgreSQL Global Development Group
11 * various details abducted from various places
12 *--------------------------------------------------------------------
15 #include "postgres.h"
17 #include <unistd.h>
18 #if defined(__darwin__)
19 #include <crt_externs.h>
20 #endif
22 #include "miscadmin.h"
23 #include "utils/guc.h"
24 #include "utils/ps_status.h"
26 extern char **environ;
28 /* GUC variable */
29 bool update_process_title = DEFAULT_UPDATE_PROCESS_TITLE;
32 * Alternative ways of updating ps display:
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.)
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
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
70 #ifndef PS_USE_NONE
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 */
83 static size_t ps_buffer_cur_len; /* nominal strlen(ps_buffer) */
85 static size_t ps_buffer_fixed_size; /* size of the constant prefix */
88 * Length of ps_buffer before the suffix was appended to the end, or 0 if we
89 * didn't set a suffix.
91 static size_t ps_buffer_nosuffix_len;
93 static void flush_ps_display(void);
95 #endif /* not PS_USE_NONE */
97 /* save the original argv[] location here */
98 static int save_argc;
99 static char **save_argv;
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.
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.)
113 * Note that in case of failure this cannot call elog() as that is not
114 * initialized yet. We rely on write_stderr() instead.
116 char **
117 save_ps_display_args(int argc, char **argv)
119 save_argc = argc;
120 save_argv = argv;
122 #if defined(PS_USE_CLOBBER_ARGV)
125 * If we're going to overwrite the argv area, count the available space.
126 * Also move the environment strings to make additional room.
129 char *end_of_area = NULL;
130 char **new_environ;
131 int i;
134 * check for contiguous argv strings
136 for (i = 0; i < argc; i++)
138 if (i == 0 || end_of_area + 1 == argv[i])
139 end_of_area = argv[i] + strlen(argv[i]);
142 if (end_of_area == NULL) /* probably can't happen? */
144 ps_buffer = NULL;
145 ps_buffer_size = 0;
146 return argv;
150 * check for contiguous environ strings following argv
152 for (i = 0; environ[i] != NULL; i++)
154 if (end_of_area + 1 == environ[i])
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.
165 #if defined(__linux__) && (!defined(__GLIBC__) && !defined(__UCLIBC__))
166 if (strncmp(environ[i], "LD_LIBRARY_PATH=", 16) == 0)
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.
174 end_of_area = environ[i] + 15;
176 else
177 #endif
179 end_of_area = environ[i] + strlen(environ[i]);
184 ps_buffer = argv[0];
185 last_status_len = ps_buffer_size = end_of_area - argv[0];
188 * move the environment out of the way
190 new_environ = (char **) malloc((i + 1) * sizeof(char *));
191 if (!new_environ)
193 write_stderr("out of memory\n");
194 exit(1);
196 for (i = 0; environ[i] != NULL; i++)
198 new_environ[i] = strdup(environ[i]);
199 if (!new_environ[i])
201 write_stderr("out of memory\n");
202 exit(1);
205 new_environ[i] = NULL;
206 environ = new_environ;
210 * If we're going to change the original argv[] then make a copy for
211 * argument parsing purposes.
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[].
222 char **new_argv;
223 int i;
225 new_argv = (char **) malloc((argc + 1) * sizeof(char *));
226 if (!new_argv)
228 write_stderr("out of memory\n");
229 exit(1);
231 for (i = 0; i < argc; i++)
233 new_argv[i] = strdup(argv[i]);
234 if (!new_argv[i])
236 write_stderr("out of memory\n");
237 exit(1);
240 new_argv[argc] = NULL;
242 #if defined(__darwin__)
245 * macOS has a static copy of the argv pointer, which we may fix like
246 * so:
248 *_NSGetArgv() = new_argv;
249 #endif
251 argv = new_argv;
253 #endif /* PS_USE_CLOBBER_ARGV */
255 return argv;
259 * Call this once during subprocess startup to set the identification
260 * values.
262 * If fixed_part is NULL, a default will be obtained from MyBackendType.
264 * At this point, the original argv[] array may be overwritten.
266 void
267 init_ps_display(const char *fixed_part)
269 #ifndef PS_USE_NONE
270 bool save_update_process_title;
271 #endif
273 Assert(fixed_part || MyBackendType);
274 if (!fixed_part)
275 fixed_part = GetBackendTypeDesc(MyBackendType);
277 #ifndef PS_USE_NONE
278 /* no ps display for stand-alone backend */
279 if (!IsUnderPostmaster)
280 return;
282 /* no ps display if you didn't call save_ps_display_args() */
283 if (!save_argv)
284 return;
286 #ifdef PS_USE_CLOBBER_ARGV
287 /* If ps_buffer is a pointer, it might still be null */
288 if (!ps_buffer)
289 return;
291 /* make extra argv slots point at end_of_area (a NUL) */
292 for (int i = 1; i < save_argc; i++)
293 save_argv[i] = ps_buffer + ps_buffer_size;
294 #endif /* PS_USE_CLOBBER_ARGV */
297 * Make fixed prefix of ps display.
300 #if defined(PS_USE_SETPROCTITLE) || defined(PS_USE_SETPROCTITLE_FAST)
303 * apparently setproctitle() already adds a `progname:' prefix to the ps
304 * line
306 #define PROGRAM_NAME_PREFIX ""
307 #else
308 #define PROGRAM_NAME_PREFIX "postgres: "
309 #endif
311 if (*cluster_name == '\0')
313 snprintf(ps_buffer, ps_buffer_size,
314 PROGRAM_NAME_PREFIX "%s ",
315 fixed_part);
317 else
319 snprintf(ps_buffer, ps_buffer_size,
320 PROGRAM_NAME_PREFIX "%s: %s ",
321 cluster_name, fixed_part);
324 ps_buffer_cur_len = ps_buffer_fixed_size = strlen(ps_buffer);
327 * On the first run, force the update.
329 save_update_process_title = update_process_title;
330 update_process_title = true;
331 set_ps_display("");
332 update_process_title = save_update_process_title;
333 #endif /* not PS_USE_NONE */
336 #ifndef PS_USE_NONE
338 * update_ps_display_precheck
339 * Helper function to determine if updating the process title is
340 * something that we need to do.
342 static bool
343 update_ps_display_precheck(void)
345 /* update_process_title=off disables updates */
346 if (!update_process_title)
347 return false;
349 /* no ps display for stand-alone backend */
350 if (!IsUnderPostmaster)
351 return false;
353 #ifdef PS_USE_CLOBBER_ARGV
354 /* If ps_buffer is a pointer, it might still be null */
355 if (!ps_buffer)
356 return false;
357 #endif
359 return true;
361 #endif /* not PS_USE_NONE */
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.
368 void
369 set_ps_display_suffix(const char *suffix)
371 #ifndef PS_USE_NONE
372 size_t len;
374 /* first, check if we need to update the process title */
375 if (!update_ps_display_precheck())
376 return;
378 /* if there's already a suffix, overwrite it */
379 if (ps_buffer_nosuffix_len > 0)
380 ps_buffer_cur_len = ps_buffer_nosuffix_len;
381 else
382 ps_buffer_nosuffix_len = ps_buffer_cur_len;
384 len = strlen(suffix);
386 /* check if we have enough space to append the suffix */
387 if (ps_buffer_cur_len + len + 1 >= ps_buffer_size)
389 /* not enough space. Check the buffer isn't full already */
390 if (ps_buffer_cur_len < ps_buffer_size - 1)
392 /* append a space before the suffix */
393 ps_buffer[ps_buffer_cur_len++] = ' ';
395 /* just add what we can and fill the ps_buffer */
396 memcpy(ps_buffer + ps_buffer_cur_len, suffix,
397 ps_buffer_size - ps_buffer_cur_len - 1);
398 ps_buffer[ps_buffer_size - 1] = '\0';
399 ps_buffer_cur_len = ps_buffer_size - 1;
402 else
404 ps_buffer[ps_buffer_cur_len++] = ' ';
405 memcpy(ps_buffer + ps_buffer_cur_len, suffix, len + 1);
406 ps_buffer_cur_len = ps_buffer_cur_len + len;
409 Assert(strlen(ps_buffer) == ps_buffer_cur_len);
411 /* and set the new title */
412 flush_ps_display();
413 #endif /* not PS_USE_NONE */
417 * set_ps_display_remove_suffix
418 * Remove the process display suffix added by set_ps_display_suffix
420 void
421 set_ps_display_remove_suffix(void)
423 #ifndef PS_USE_NONE
424 /* first, check if we need to update the process title */
425 if (!update_ps_display_precheck())
426 return;
428 /* check we added a suffix */
429 if (ps_buffer_nosuffix_len == 0)
430 return; /* no suffix */
432 /* remove the suffix from ps_buffer */
433 ps_buffer[ps_buffer_nosuffix_len] = '\0';
434 ps_buffer_cur_len = ps_buffer_nosuffix_len;
435 ps_buffer_nosuffix_len = 0;
437 Assert(ps_buffer_cur_len == strlen(ps_buffer));
439 /* and set the new title */
440 flush_ps_display();
441 #endif /* not PS_USE_NONE */
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.
448 * 'len' must be the same as strlen(activity)
450 void
451 set_ps_display_with_len(const char *activity, size_t len)
453 Assert(strlen(activity) == len);
455 #ifndef PS_USE_NONE
456 /* first, check if we need to update the process title */
457 if (!update_ps_display_precheck())
458 return;
460 /* wipe out any suffix when the title is completely changed */
461 ps_buffer_nosuffix_len = 0;
463 /* Update ps_buffer to contain both fixed part and activity */
464 if (ps_buffer_fixed_size + len >= ps_buffer_size)
466 /* handle the case where ps_buffer doesn't have enough space */
467 memcpy(ps_buffer + ps_buffer_fixed_size, activity,
468 ps_buffer_size - ps_buffer_fixed_size - 1);
469 ps_buffer[ps_buffer_size - 1] = '\0';
470 ps_buffer_cur_len = ps_buffer_size - 1;
472 else
474 memcpy(ps_buffer + ps_buffer_fixed_size, activity, len + 1);
475 ps_buffer_cur_len = ps_buffer_fixed_size + len;
477 Assert(strlen(ps_buffer) == ps_buffer_cur_len);
479 /* Transmit new setting to kernel, if necessary */
480 flush_ps_display();
481 #endif /* not PS_USE_NONE */
484 #ifndef PS_USE_NONE
485 static void
486 flush_ps_display(void)
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
494 #ifdef PS_USE_CLOBBER_ARGV
495 /* pad unused memory; need only clobber remainder of old status string */
496 if (last_status_len > ps_buffer_cur_len)
497 MemSet(ps_buffer + ps_buffer_cur_len, PS_PADDING,
498 last_status_len - ps_buffer_cur_len);
499 last_status_len = ps_buffer_cur_len;
500 #endif /* PS_USE_CLOBBER_ARGV */
502 #ifdef PS_USE_WIN32
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.
509 static HANDLE ident_handle = INVALID_HANDLE_VALUE;
510 char name[PS_BUFFER_SIZE + 32];
512 if (ident_handle != INVALID_HANDLE_VALUE)
513 CloseHandle(ident_handle);
515 sprintf(name, "pgident(%d): %s", MyProcPid, ps_buffer);
517 ident_handle = CreateEvent(NULL, TRUE, FALSE, name);
519 #endif /* PS_USE_WIN32 */
521 #endif /* not PS_USE_NONE */
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.
529 const char *
530 get_ps_display(int *displen)
532 #ifdef PS_USE_CLOBBER_ARGV
533 /* If ps_buffer is a pointer, it might still be null */
534 if (!ps_buffer)
536 *displen = 0;
537 return "";
539 #endif
541 #ifndef PS_USE_NONE
542 *displen = (int) (ps_buffer_cur_len - ps_buffer_fixed_size);
544 return ps_buffer + ps_buffer_fixed_size;
545 #else
546 *displen = 0;
547 return "";
548 #endif