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

Generated by: LCOV version 1.16