LCOV - code coverage report
Current view: top level - src/backend/utils/misc - ps_status.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 84 116 72.4 %
Date: 2024-04-19 14:10:55 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-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        3078 : save_ps_display_args(int argc, char **argv)
     118             : {
     119        3078 :     save_argc = argc;
     120        3078 :     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        3078 :         char       *end_of_area = NULL;
     130             :         char      **new_environ;
     131             :         int         i;
     132             : 
     133             :         /*
     134             :          * check for contiguous argv strings
     135             :          */
     136       16834 :         for (i = 0; i < argc; i++)
     137             :         {
     138       13756 :             if (i == 0 || end_of_area + 1 == argv[i])
     139       13756 :                 end_of_area = argv[i] + strlen(argv[i]);
     140             :         }
     141             : 
     142        3078 :         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      109432 :         for (i = 0; environ[i] != NULL; i++)
     153             :         {
     154      106354 :             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      106354 :                     end_of_area = environ[i] + strlen(environ[i]);
     180             :                 }
     181             :             }
     182             :         }
     183             : 
     184        3078 :         ps_buffer = argv[0];
     185        3078 :         last_status_len = ps_buffer_size = end_of_area - argv[0];
     186             : 
     187             :         /*
     188             :          * move the environment out of the way
     189             :          */
     190        3078 :         new_environ = (char **) malloc((i + 1) * sizeof(char *));
     191        3078 :         if (!new_environ)
     192             :         {
     193           0 :             write_stderr("out of memory\n");
     194           0 :             exit(1);
     195             :         }
     196      109432 :         for (i = 0; environ[i] != NULL; i++)
     197             :         {
     198      106354 :             new_environ[i] = strdup(environ[i]);
     199      106354 :             if (!new_environ[i])
     200             :             {
     201           0 :                 write_stderr("out of memory\n");
     202           0 :                 exit(1);
     203             :             }
     204             :         }
     205        3078 :         new_environ[i] = NULL;
     206        3078 :         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        3078 :         new_argv = (char **) malloc((argc + 1) * sizeof(char *));
     226        3078 :         if (!new_argv)
     227             :         {
     228           0 :             write_stderr("out of memory\n");
     229           0 :             exit(1);
     230             :         }
     231       16834 :         for (i = 0; i < argc; i++)
     232             :         {
     233       13756 :             new_argv[i] = strdup(argv[i]);
     234       13756 :             if (!new_argv[i])
     235             :             {
     236           0 :                 write_stderr("out of memory\n");
     237           0 :                 exit(1);
     238             :             }
     239             :         }
     240        3078 :         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        3078 :         argv = new_argv;
     252             :     }
     253             : #endif                          /* PS_USE_CLOBBER_ARGV */
     254             : 
     255        3078 :     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       29860 : 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       29860 :     if (!fixed_part)
     275        4832 :         fixed_part = GetBackendTypeDesc(MyBackendType);
     276             : 
     277             : #ifndef PS_USE_NONE
     278             :     /* no ps display for stand-alone backend */
     279       29860 :     if (!IsUnderPostmaster)
     280           0 :         return;
     281             : 
     282             :     /* no ps display if you didn't call save_ps_display_args() */
     283       29860 :     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       29860 :     if (!ps_buffer)
     289           0 :         return;
     290             : 
     291             :     /* make extra argv slots point at end_of_area (a NUL) */
     292      152202 :     for (int i = 1; i < save_argc; i++)
     293      122342 :         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       29860 :     if (*cluster_name == '\0')
     312             :     {
     313        6152 :         snprintf(ps_buffer, ps_buffer_size,
     314             :                  PROGRAM_NAME_PREFIX "%s ",
     315             :                  fixed_part);
     316             :     }
     317             :     else
     318             :     {
     319       23708 :         snprintf(ps_buffer, ps_buffer_size,
     320             :                  PROGRAM_NAME_PREFIX "%s: %s ",
     321             :                  cluster_name, fixed_part);
     322             :     }
     323             : 
     324       29860 :     ps_buffer_cur_len = ps_buffer_fixed_size = strlen(ps_buffer);
     325             : 
     326             :     /*
     327             :      * On the first run, force the update.
     328             :      */
     329       29860 :     save_update_process_title = update_process_title;
     330       29860 :     update_process_title = true;
     331       29860 :     set_ps_display("");
     332       29860 :     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     1503934 : update_ps_display_precheck(void)
     344             : {
     345             :     /* update_process_title=off disables updates */
     346     1503934 :     if (!update_process_title)
     347           0 :         return false;
     348             : 
     349             :     /* no ps display for stand-alone backend */
     350     1503934 :     if (!IsUnderPostmaster)
     351      114792 :         return false;
     352             : 
     353             : #ifdef PS_USE_CLOBBER_ARGV
     354             :     /* If ps_buffer is a pointer, it might still be null */
     355     1389142 :     if (!ps_buffer)
     356           0 :         return false;
     357             : #endif
     358             : 
     359     1389142 :     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        3484 : 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        3484 :     if (!update_ps_display_precheck())
     376           0 :         return;
     377             : 
     378             :     /* if there's already a suffix, overwrite it */
     379        3484 :     if (ps_buffer_nosuffix_len > 0)
     380           0 :         ps_buffer_cur_len = ps_buffer_nosuffix_len;
     381             :     else
     382        3484 :         ps_buffer_nosuffix_len = ps_buffer_cur_len;
     383             : 
     384        3484 :     len = strlen(suffix);
     385             : 
     386             :     /* check if we have enough space to append the suffix */
     387        3484 :     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        3484 :         ps_buffer[ps_buffer_cur_len++] = ' ';
     405        3484 :         memcpy(ps_buffer + ps_buffer_cur_len, suffix, len + 1);
     406        3484 :         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        3484 :     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        3472 : set_ps_display_remove_suffix(void)
     422             : {
     423             : #ifndef PS_USE_NONE
     424             :     /* first, check if we need to update the process title */
     425        3472 :     if (!update_ps_display_precheck())
     426           0 :         return;
     427             : 
     428             :     /* check we added a suffix */
     429        3472 :     if (ps_buffer_nosuffix_len == 0)
     430           0 :         return;                 /* no suffix */
     431             : 
     432             :     /* remove the suffix from ps_buffer */
     433        3472 :     ps_buffer[ps_buffer_nosuffix_len] = '\0';
     434        3472 :     ps_buffer_cur_len = ps_buffer_nosuffix_len;
     435        3472 :     ps_buffer_nosuffix_len = 0;
     436             : 
     437             :     Assert(ps_buffer_cur_len == strlen(ps_buffer));
     438             : 
     439             :     /* and set the new title */
     440        3472 :     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     1496978 : 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     1496978 :     if (!update_ps_display_precheck())
     458      114792 :         return;
     459             : 
     460             :     /* wipe out any suffix when the title is completely changed */
     461     1382186 :     ps_buffer_nosuffix_len = 0;
     462             : 
     463             :     /* Update ps_buffer to contain both fixed part and activity */
     464     1382186 :     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     1382186 :         memcpy(ps_buffer + ps_buffer_fixed_size, activity, len + 1);
     475     1382186 :         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     1382186 :     flush_ps_display();
     481             : #endif                          /* not PS_USE_NONE */
     482             : }
     483             : 
     484             : #ifndef PS_USE_NONE
     485             : static void
     486     1389142 : 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     1389142 :     if (last_status_len > ps_buffer_cur_len)
     497      645812 :         MemSet(ps_buffer + ps_buffer_cur_len, PS_PADDING,
     498             :                last_status_len - ps_buffer_cur_len);
     499     1389142 :     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     1389142 : }
     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             : }

Generated by: LCOV version 1.14