root/lib/common/pid.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pcmk__pid_active
  2. pcmk__read_pidfile
  3. pcmk__pidfile_matches
  4. pcmk__lock_pidfile

   1 /*
   2  * Copyright 2004-2020 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU Lesser General Public License
   7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #ifndef _GNU_SOURCE
  13 #  define _GNU_SOURCE
  14 #endif
  15 
  16 #include <stdio.h>
  17 #include <string.h>
  18 #include <sys/stat.h>
  19 
  20 #include <crm/crm.h>
  21 
  22 int
  23 pcmk__pid_active(pid_t pid, const char *daemon)
     /* [previous][next][first][last][top][bottom][index][help] */
  24 {
  25     static pid_t last_asked_pid = 0;  /* log spam prevention */
  26 #if SUPPORT_PROCFS
  27     static int have_proc_pid = 0;
  28 #else
  29     static int have_proc_pid = -1;
  30 #endif
  31     int rc = 0;
  32     bool no_name_check = ((daemon == NULL) || (have_proc_pid == -1));
  33 
  34     if (have_proc_pid == 0) {
  35         /* evaluation of /proc/PID/exe applicability via self-introspection */
  36         char proc_path[PATH_MAX], exe_path[PATH_MAX];
  37         snprintf(proc_path, sizeof(proc_path), "/proc/%lld/exe",
  38                  (long long) getpid());
  39         have_proc_pid = 1;
  40         if (readlink(proc_path, exe_path, sizeof(exe_path) - 1) < 0) {
  41             have_proc_pid = -1;
  42         }
  43     }
  44 
  45     if (pid <= 0) {
  46         return EINVAL;
  47     }
  48 
  49     rc = kill(pid, 0);
  50     if ((rc < 0) && (errno == ESRCH)) {
  51         return ESRCH;  /* no such PID detected */
  52 
  53     } else if ((rc < 0) && no_name_check) {
  54         rc = errno;
  55         if (last_asked_pid != pid) {
  56             crm_info("Cannot examine PID %lld: %s",
  57                      (long long) pid, strerror(errno));
  58             last_asked_pid = pid;
  59         }
  60         return rc; /* errno != ESRCH */
  61 
  62     } else if ((rc == 0) && no_name_check) {
  63         return pcmk_rc_ok; /* kill as the only indicator, cannot double check */
  64 
  65     } else if (daemon != NULL) {
  66         /* make sure PID hasn't been reused by another process
  67            XXX: might still be just a zombie, which could confuse decisions */
  68         bool checked_through_kill = (rc == 0);
  69         char proc_path[PATH_MAX], exe_path[PATH_MAX], myexe_path[PATH_MAX];
  70         snprintf(proc_path, sizeof(proc_path), "/proc/%lld/exe",
  71                  (long long) pid);
  72 
  73         rc = readlink(proc_path, exe_path, sizeof(exe_path) - 1);
  74         if (rc < 0) {
  75             int rdlnk_errno = errno;
  76 
  77             if (rdlnk_errno != EACCES) {
  78                 int rc = kill(pid,0); /* check once again - filter out races */
  79 
  80                 if ((rc < 0) && (errno == ESRCH)) {
  81                     return ESRCH;
  82                 }
  83             }
  84             if (last_asked_pid != pid) {
  85                 if (rdlnk_errno == EACCES) {
  86                     crm_info("Could not read from %s: %s " CRM_XS " errno=%d",
  87                              proc_path, strerror(rdlnk_errno), rdlnk_errno);
  88                 } else {
  89                     crm_err("Could not read from %s: %s " CRM_XS " errno=%d",
  90                             proc_path, strerror(rdlnk_errno), rdlnk_errno);
  91                 }
  92                 last_asked_pid = pid;
  93             }
  94             if ((rdlnk_errno == EACCES) && checked_through_kill) {
  95                 // Trust kill result, can't double-check via path
  96                 return pcmk_rc_ok;
  97             } else if (rdlnk_errno == EACCES) {
  98                 return EACCES;
  99             } else {
 100                 return ESRCH;  /* most likely errno == ENOENT */
 101             }
 102         }
 103         exe_path[rc] = '\0';
 104 
 105         if (daemon[0] != '/') {
 106             rc = snprintf(myexe_path, sizeof(myexe_path), CRM_DAEMON_DIR"/%s",
 107                           daemon);
 108         } else {
 109             rc = snprintf(myexe_path, sizeof(myexe_path), "%s", daemon);
 110         }
 111 
 112         if (rc > 0 && rc < sizeof(myexe_path) && !strcmp(exe_path, myexe_path)) {
 113             return pcmk_rc_ok;
 114         }
 115     }
 116 
 117     return ESRCH;
 118 }
 119 
 120 #define LOCKSTRLEN      11
 121 
 122 /*!
 123  * \internal
 124  * \brief Read a process ID from a file
 125  *
 126  * \param[in]  filename  Process ID file to read
 127  * \param[out] pid       Where to put PID that was read
 128  *
 129  * \return Standard Pacemaker return code
 130  */
 131 int
 132 pcmk__read_pidfile(const char *filename, pid_t *pid)
     /* [previous][next][first][last][top][bottom][index][help] */
 133 {
 134     int fd;
 135     struct stat sbuf;
 136     int rc = pcmk_rc_unknown_format;
 137     long long pid_read = 0;
 138     char buf[LOCKSTRLEN + 1];
 139 
 140     CRM_CHECK((filename != NULL) && (pid != NULL), return EINVAL);
 141 
 142     fd = open(filename, O_RDONLY);
 143     if (fd < 0) {
 144         return errno;
 145     }
 146 
 147     if ((fstat(fd, &sbuf) >= 0) && (sbuf.st_size < LOCKSTRLEN)) {
 148         sleep(2);           /* if someone was about to create one,
 149                              * give'm a sec to do so
 150                              */
 151     }
 152 
 153     if (read(fd, buf, sizeof(buf)) < 1) {
 154         rc = errno;
 155         goto bail;
 156     }
 157 
 158     if (sscanf(buf, "%lld", &pid_read) > 0) {
 159         if (pid_read <= 0) {
 160             rc = ESRCH;
 161         } else {
 162             rc = pcmk_rc_ok;
 163             *pid = (pid_t) pid_read;
 164             crm_trace("Read pid %lld from %s", pid_read, filename);
 165         }
 166     }
 167 
 168   bail:
 169     close(fd);
 170     return rc;
 171 }
 172 
 173 /*!
 174  * \internal
 175  * \brief Check whether a process from a PID file matches expected values
 176  *
 177  * \param[in]  filename       Path of PID file
 178  * \param[in]  expected_pid   If positive, compare to this PID
 179  * \param[in]  expected_name  If not NULL, the PID from the PID file is valid
 180  *                            only if it is active as a process with this name
 181  * \param[out] pid            If not NULL, store PID found in PID file here
 182  *
 183  * \return Standard Pacemaker return code
 184  */
 185 int
 186 pcmk__pidfile_matches(const char *filename, pid_t expected_pid,
     /* [previous][next][first][last][top][bottom][index][help] */
 187                       const char *expected_name, pid_t *pid)
 188 {
 189     pid_t pidfile_pid = 0;
 190     int rc = pcmk__read_pidfile(filename, &pidfile_pid);
 191 
 192     if (pid) {
 193         *pid = pidfile_pid;
 194     }
 195 
 196     if (rc != pcmk_rc_ok) {
 197         // Error reading PID file or invalid contents
 198         unlink(filename);
 199         rc = ENOENT;
 200 
 201     } else if ((expected_pid > 0) && (pidfile_pid == expected_pid)) {
 202         // PID in file matches what was expected
 203         rc = pcmk_rc_ok;
 204 
 205     } else if (pcmk__pid_active(pidfile_pid, expected_name) == ESRCH) {
 206         // Contains a stale value
 207         unlink(filename);
 208         rc = ENOENT;
 209 
 210     } else if ((expected_pid > 0) && (pidfile_pid != expected_pid)) {
 211         // Locked by existing process
 212         rc = EEXIST;
 213     }
 214 
 215     return rc;
 216 }
 217 
 218 /*!
 219  * \internal
 220  * \brief Create a PID file for the current process (if not already existent)
 221  *
 222  * \param[in] filename   Name of PID file to create
 223  * \param[in] name       Name of current process
 224  *
 225  * \return Standard Pacemaker return code
 226  */
 227 int
 228 pcmk__lock_pidfile(const char *filename, const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 229 {
 230     pid_t mypid = getpid();
 231     int fd = 0;
 232     int rc = 0;
 233     char buf[LOCKSTRLEN + 2];
 234 
 235     rc = pcmk__pidfile_matches(filename, 0, name, NULL);
 236     if ((rc != pcmk_rc_ok) && (rc != ENOENT)) {
 237         // Locked by existing process
 238         return rc;
 239     }
 240 
 241     fd = open(filename, O_CREAT | O_WRONLY | O_EXCL, 0644);
 242     if (fd < 0) {
 243         return errno;
 244     }
 245 
 246     snprintf(buf, sizeof(buf), "%*lld\n", LOCKSTRLEN - 1, (long long) mypid);
 247     rc = write(fd, buf, LOCKSTRLEN);
 248     close(fd);
 249 
 250     if (rc != LOCKSTRLEN) {
 251         crm_perror(LOG_ERR, "Incomplete write to %s", filename);
 252         return errno;
 253     }
 254 
 255     rc = pcmk__pidfile_matches(filename, mypid, name, NULL);
 256     if (rc != pcmk_rc_ok) {
 257         // Something is really wrong -- maybe I/O error on read back?
 258         unlink(filename);
 259     }
 260     return rc;
 261 }

/* [previous][next][first][last][top][bottom][index][help] */