LCOV - code coverage report
Current view: top level - src/backend/utils/misc - ps_status.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 72.4 % 116 84
Test Date: 2026-04-01 08:16:03 Functions: 100.0 % 8 8
Legend: Lines:     hit not hit

            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-2026, 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)
      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(_AIX) || defined(__sun) || defined(__darwin__) || defined(__GNU__)
      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(_AIX) || defined(__linux__) || defined(__darwin__) || defined(__GNU__)
      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         2071 : save_ps_display_args(int argc, char **argv)
     131              : {
     132         2071 :     save_argc = argc;
     133         2071 :     save_argv = argv;
     134              : 
     135              : #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              :     {
     142         2071 :         char       *end_of_area = NULL;
     143              :         char      **new_environ;
     144              :         int         i;
     145              : 
     146              :         /*
     147              :          * check for contiguous argv strings
     148              :          */
     149        11772 :         for (i = 0; i < argc; i++)
     150              :         {
     151         9701 :             if (i == 0 || end_of_area + 1 == argv[i])
     152         9701 :                 end_of_area = argv[i] + strlen(argv[i]);
     153              :         }
     154              : 
     155         2071 :         if (end_of_area == NULL)    /* probably can't happen? */
     156              :         {
     157            0 :             ps_buffer = NULL;
     158            0 :             ps_buffer_size = 0;
     159            0 :             return argv;
     160              :         }
     161              : 
     162              :         /*
     163              :          * check for contiguous environ strings following argv
     164              :          */
     165        78198 :         for (i = 0; environ[i] != NULL; i++)
     166              :         {
     167        76127 :             if (end_of_area + 1 == environ[i])
     168              :             {
     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              :                 {
     192        76127 :                     end_of_area = environ[i] + strlen(environ[i]);
     193              :                 }
     194              :             }
     195              :         }
     196              : 
     197         2071 :         ps_buffer = argv[0];
     198         2071 :         last_status_len = ps_buffer_size = end_of_area - argv[0];
     199              : 
     200              :         /*
     201              :          * move the environment out of the way
     202              :          */
     203         2071 :         new_environ = (char **) malloc((i + 1) * sizeof(char *));
     204         2071 :         if (!new_environ)
     205              :         {
     206            0 :             write_stderr("out of memory\n");
     207            0 :             exit(1);
     208              :         }
     209        78198 :         for (i = 0; environ[i] != NULL; i++)
     210              :         {
     211        76127 :             new_environ[i] = strdup(environ[i]);
     212        76127 :             if (!new_environ[i])
     213              :             {
     214            0 :                 write_stderr("out of memory\n");
     215            0 :                 exit(1);
     216              :             }
     217              :         }
     218         2071 :         new_environ[i] = NULL;
     219         2071 :         environ = new_environ;
     220              : 
     221              :         /* See notes about Valgrind above. */
     222              : #ifdef USE_VALGRIND
     223              :         ps_status_new_environ = new_environ;
     224              : #endif
     225              :     }
     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[]. (We use custom pg_getopt_start/next() functions nowadays
     238              :      * that don't do that, but those other dependencies might still exist.)
     239              :      */
     240              :     {
     241              :         char      **new_argv;
     242              :         int         i;
     243              : 
     244         2071 :         new_argv = (char **) malloc((argc + 1) * sizeof(char *));
     245         2071 :         if (!new_argv)
     246              :         {
     247            0 :             write_stderr("out of memory\n");
     248            0 :             exit(1);
     249              :         }
     250        11772 :         for (i = 0; i < argc; i++)
     251              :         {
     252         9701 :             new_argv[i] = strdup(argv[i]);
     253         9701 :             if (!new_argv[i])
     254              :             {
     255            0 :                 write_stderr("out of memory\n");
     256            0 :                 exit(1);
     257              :             }
     258              :         }
     259         2071 :         new_argv[argc] = NULL;
     260              : 
     261              : #if defined(__darwin__)
     262              : 
     263              :         /*
     264              :          * macOS has a static copy of the argv pointer, which we may fix like
     265              :          * so:
     266              :          */
     267              :         *_NSGetArgv() = new_argv;
     268              : #endif
     269              : 
     270         2071 :         argv = new_argv;
     271              :     }
     272              : #endif                          /* PS_USE_CLOBBER_ARGV */
     273              : 
     274         2071 :     return argv;
     275              : }
     276              : 
     277              : /*
     278              :  * Call this once during subprocess startup to set the identification
     279              :  * values.
     280              :  *
     281              :  * If fixed_part is NULL, a default will be obtained from MyBackendType.
     282              :  *
     283              :  * At this point, the original argv[] array may be overwritten.
     284              :  */
     285              : void
     286        24258 : init_ps_display(const char *fixed_part)
     287              : {
     288              : #ifndef PS_USE_NONE
     289              :     bool        save_update_process_title;
     290              : #endif
     291              : 
     292              :     Assert(fixed_part || MyBackendType);
     293        24258 :     if (!fixed_part)
     294         6710 :         fixed_part = GetBackendTypeDesc(MyBackendType);
     295              : 
     296              : #ifndef PS_USE_NONE
     297              :     /* no ps display for stand-alone backend */
     298        24258 :     if (!IsUnderPostmaster)
     299            0 :         return;
     300              : 
     301              :     /* no ps display if you didn't call save_ps_display_args() */
     302        24258 :     if (!save_argv)
     303            0 :         return;
     304              : 
     305              : #ifdef PS_USE_CLOBBER_ARGV
     306              :     /* If ps_buffer is a pointer, it might still be null */
     307        24258 :     if (!ps_buffer)
     308            0 :         return;
     309              : 
     310              :     /* make extra argv slots point at end_of_area (a NUL) */
     311       125542 :     for (int i = 1; i < save_argc; i++)
     312       101284 :         save_argv[i] = ps_buffer + ps_buffer_size;
     313              : #endif                          /* PS_USE_CLOBBER_ARGV */
     314              : 
     315              :     /*
     316              :      * Make fixed prefix of ps display.
     317              :      */
     318              : 
     319              : #if defined(PS_USE_SETPROCTITLE) || defined(PS_USE_SETPROCTITLE_FAST)
     320              : 
     321              :     /*
     322              :      * apparently setproctitle() already adds a `progname:' prefix to the ps
     323              :      * line
     324              :      */
     325              : #define PROGRAM_NAME_PREFIX ""
     326              : #else
     327              : #define PROGRAM_NAME_PREFIX "postgres: "
     328              : #endif
     329              : 
     330        24258 :     if (*cluster_name == '\0')
     331              :     {
     332         4659 :         snprintf(ps_buffer, ps_buffer_size,
     333              :                  PROGRAM_NAME_PREFIX "%s ",
     334              :                  fixed_part);
     335              :     }
     336              :     else
     337              :     {
     338        19599 :         snprintf(ps_buffer, ps_buffer_size,
     339              :                  PROGRAM_NAME_PREFIX "%s: %s ",
     340              :                  cluster_name, fixed_part);
     341              :     }
     342              : 
     343        24258 :     ps_buffer_cur_len = ps_buffer_fixed_size = strlen(ps_buffer);
     344              : 
     345              :     /*
     346              :      * On the first run, force the update.
     347              :      */
     348        24258 :     save_update_process_title = update_process_title;
     349        24258 :     update_process_title = true;
     350        24258 :     set_ps_display("");
     351        24258 :     update_process_title = save_update_process_title;
     352              : #endif                          /* not PS_USE_NONE */
     353              : }
     354              : 
     355              : #ifndef PS_USE_NONE
     356              : /*
     357              :  * update_ps_display_precheck
     358              :  *      Helper function to determine if updating the process title is
     359              :  *      something that we need to do.
     360              :  */
     361              : static bool
     362      1155024 : update_ps_display_precheck(void)
     363              : {
     364              :     /* update_process_title=off disables updates */
     365      1155024 :     if (!update_process_title)
     366            0 :         return false;
     367              : 
     368              :     /* no ps display for stand-alone backend */
     369      1155024 :     if (!IsUnderPostmaster)
     370        69400 :         return false;
     371              : 
     372              : #ifdef PS_USE_CLOBBER_ARGV
     373              :     /* If ps_buffer is a pointer, it might still be null */
     374      1085624 :     if (!ps_buffer)
     375            0 :         return false;
     376              : #endif
     377              : 
     378      1085624 :     return true;
     379              : }
     380              : #endif                          /* not PS_USE_NONE */
     381              : 
     382              : /*
     383              :  * set_ps_display_suffix
     384              :  *      Adjust the process title to append 'suffix' onto the end with a space
     385              :  *      between it and the current process title.
     386              :  */
     387              : void
     388         1441 : set_ps_display_suffix(const char *suffix)
     389              : {
     390              : #ifndef PS_USE_NONE
     391              :     size_t      len;
     392              : 
     393              :     /* first, check if we need to update the process title */
     394         1441 :     if (!update_ps_display_precheck())
     395            0 :         return;
     396              : 
     397              :     /* if there's already a suffix, overwrite it */
     398         1441 :     if (ps_buffer_nosuffix_len > 0)
     399            0 :         ps_buffer_cur_len = ps_buffer_nosuffix_len;
     400              :     else
     401         1441 :         ps_buffer_nosuffix_len = ps_buffer_cur_len;
     402              : 
     403         1441 :     len = strlen(suffix);
     404              : 
     405              :     /* check if we have enough space to append the suffix */
     406         1441 :     if (ps_buffer_cur_len + len + 1 >= ps_buffer_size)
     407              :     {
     408              :         /* not enough space.  Check the buffer isn't full already */
     409            0 :         if (ps_buffer_cur_len < ps_buffer_size - 1)
     410              :         {
     411              :             /* append a space before the suffix */
     412            0 :             ps_buffer[ps_buffer_cur_len++] = ' ';
     413              : 
     414              :             /* just add what we can and fill the ps_buffer */
     415            0 :             memcpy(ps_buffer + ps_buffer_cur_len, suffix,
     416            0 :                    ps_buffer_size - ps_buffer_cur_len - 1);
     417            0 :             ps_buffer[ps_buffer_size - 1] = '\0';
     418            0 :             ps_buffer_cur_len = ps_buffer_size - 1;
     419              :         }
     420              :     }
     421              :     else
     422              :     {
     423         1441 :         ps_buffer[ps_buffer_cur_len++] = ' ';
     424         1441 :         memcpy(ps_buffer + ps_buffer_cur_len, suffix, len + 1);
     425         1441 :         ps_buffer_cur_len = ps_buffer_cur_len + len;
     426              :     }
     427              : 
     428              :     Assert(strlen(ps_buffer) == ps_buffer_cur_len);
     429              : 
     430              :     /* and set the new title */
     431         1441 :     flush_ps_display();
     432              : #endif                          /* not PS_USE_NONE */
     433              : }
     434              : 
     435              : /*
     436              :  * set_ps_display_remove_suffix
     437              :  *      Remove the process display suffix added by set_ps_display_suffix
     438              :  */
     439              : void
     440         1438 : set_ps_display_remove_suffix(void)
     441              : {
     442              : #ifndef PS_USE_NONE
     443              :     /* first, check if we need to update the process title */
     444         1438 :     if (!update_ps_display_precheck())
     445            0 :         return;
     446              : 
     447              :     /* check we added a suffix */
     448         1438 :     if (ps_buffer_nosuffix_len == 0)
     449            0 :         return;                 /* no suffix */
     450              : 
     451              :     /* remove the suffix from ps_buffer */
     452         1438 :     ps_buffer[ps_buffer_nosuffix_len] = '\0';
     453         1438 :     ps_buffer_cur_len = ps_buffer_nosuffix_len;
     454         1438 :     ps_buffer_nosuffix_len = 0;
     455              : 
     456              :     Assert(ps_buffer_cur_len == strlen(ps_buffer));
     457              : 
     458              :     /* and set the new title */
     459         1438 :     flush_ps_display();
     460              : #endif                          /* not PS_USE_NONE */
     461              : }
     462              : 
     463              : /*
     464              :  * Call this to update the ps status display to a fixed prefix plus an
     465              :  * indication of what you're currently doing passed in the argument.
     466              :  *
     467              :  * 'len' must be the same as strlen(activity)
     468              :  */
     469              : void
     470      1152145 : set_ps_display_with_len(const char *activity, size_t len)
     471              : {
     472              :     Assert(strlen(activity) == len);
     473              : 
     474              : #ifndef PS_USE_NONE
     475              :     /* first, check if we need to update the process title */
     476      1152145 :     if (!update_ps_display_precheck())
     477        69400 :         return;
     478              : 
     479              :     /* wipe out any suffix when the title is completely changed */
     480      1082745 :     ps_buffer_nosuffix_len = 0;
     481              : 
     482              :     /* Update ps_buffer to contain both fixed part and activity */
     483      1082745 :     if (ps_buffer_fixed_size + len >= ps_buffer_size)
     484              :     {
     485              :         /* handle the case where ps_buffer doesn't have enough space */
     486            0 :         memcpy(ps_buffer + ps_buffer_fixed_size, activity,
     487            0 :                ps_buffer_size - ps_buffer_fixed_size - 1);
     488            0 :         ps_buffer[ps_buffer_size - 1] = '\0';
     489            0 :         ps_buffer_cur_len = ps_buffer_size - 1;
     490              :     }
     491              :     else
     492              :     {
     493      1082745 :         memcpy(ps_buffer + ps_buffer_fixed_size, activity, len + 1);
     494      1082745 :         ps_buffer_cur_len = ps_buffer_fixed_size + len;
     495              :     }
     496              :     Assert(strlen(ps_buffer) == ps_buffer_cur_len);
     497              : 
     498              :     /* Transmit new setting to kernel, if necessary */
     499      1082745 :     flush_ps_display();
     500              : #endif                          /* not PS_USE_NONE */
     501              : }
     502              : 
     503              : #ifndef PS_USE_NONE
     504              : static void
     505      1085624 : flush_ps_display(void)
     506              : {
     507              : #ifdef PS_USE_SETPROCTITLE
     508              :     setproctitle("%s", ps_buffer);
     509              : #elif defined(PS_USE_SETPROCTITLE_FAST)
     510              :     setproctitle_fast("%s", ps_buffer);
     511              : #endif
     512              : 
     513              : #ifdef PS_USE_CLOBBER_ARGV
     514              :     /* pad unused memory; need only clobber remainder of old status string */
     515      1085624 :     if (last_status_len > ps_buffer_cur_len)
     516       473112 :         MemSet(ps_buffer + ps_buffer_cur_len, PS_PADDING,
     517              :                last_status_len - ps_buffer_cur_len);
     518      1085624 :     last_status_len = ps_buffer_cur_len;
     519              : #endif                          /* PS_USE_CLOBBER_ARGV */
     520              : 
     521              : #ifdef PS_USE_WIN32
     522              :     {
     523              :         /*
     524              :          * Win32 does not support showing any changed arguments. To make it at
     525              :          * all possible to track which backend is doing what, we create a
     526              :          * named object that can be viewed with for example Process Explorer.
     527              :          */
     528              :         static HANDLE ident_handle = INVALID_HANDLE_VALUE;
     529              :         char        name[PS_BUFFER_SIZE + 32];
     530              : 
     531              :         if (ident_handle != INVALID_HANDLE_VALUE)
     532              :             CloseHandle(ident_handle);
     533              : 
     534              :         sprintf(name, "pgident(%d): %s", MyProcPid, ps_buffer);
     535              : 
     536              :         ident_handle = CreateEvent(NULL, TRUE, FALSE, name);
     537              :     }
     538              : #endif                          /* PS_USE_WIN32 */
     539      1085624 : }
     540              : #endif                          /* not PS_USE_NONE */
     541              : 
     542              : /*
     543              :  * Returns what's currently in the ps display, in case someone needs
     544              :  * it.  Note that only the activity part is returned.  On some platforms
     545              :  * the string will not be null-terminated, so return the effective
     546              :  * length into *displen.
     547              :  */
     548              : const char *
     549           18 : get_ps_display(int *displen)
     550              : {
     551              : #ifdef PS_USE_CLOBBER_ARGV
     552              :     /* If ps_buffer is a pointer, it might still be null */
     553           18 :     if (!ps_buffer)
     554              :     {
     555            0 :         *displen = 0;
     556            0 :         return "";
     557              :     }
     558              : #endif
     559              : 
     560              : #ifndef PS_USE_NONE
     561           18 :     *displen = (int) (ps_buffer_cur_len - ps_buffer_fixed_size);
     562              : 
     563           18 :     return ps_buffer + ps_buffer_fixed_size;
     564              : #else
     565              :     *displen = 0;
     566              :     return "";
     567              : #endif
     568              : }
        

Generated by: LCOV version 2.0-1