root/lib/common/procfs.c

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

DEFINITIONS

This source file includes following definitions.
  1. pcmk__procfs_process_info
  2. pcmk__procfs_pid_of
  3. pcmk__procfs_num_cores
  4. pcmk__procfs_pid2path
  5. pcmk__procfs_has_pids

   1 /*
   2  * Copyright 2015-2024 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 #include <stdio.h>
  13 #include <stdlib.h>
  14 #include <string.h>
  15 #include <sys/stat.h>
  16 #include <sys/types.h>
  17 #include <dirent.h>
  18 #include <ctype.h>
  19 
  20 /*!
  21  * \internal
  22  * \brief Get process ID and name associated with a /proc directory entry
  23  *
  24  * \param[in]  entry    Directory entry (must be result of readdir() on /proc)
  25  * \param[out] name     If not NULL, a char[16] to hold the process name
  26  * \param[out] pid      If not NULL, will be set to process ID of entry
  27  *
  28  * \return Standard Pacemaker return code
  29  * \note This should be called only on Linux systems, as not all systems that
  30  *       support /proc store process names and IDs in the same way. The kernel
  31  *       limits the process name to the first 15 characters (plus terminator).
  32  *       It would be nice if there were a public kernel API constant for that
  33  *       limit, but there isn't.
  34  */
  35 static int
  36 pcmk__procfs_process_info(const struct dirent *entry, char *name, pid_t *pid)
     /* [previous][next][first][last][top][bottom][index][help] */
  37 {
  38     int fd, local_pid;
  39     FILE *file;
  40     struct stat statbuf;
  41     char procpath[128] = { 0 };
  42 
  43     /* We're only interested in entries whose name is a PID,
  44      * so skip anything non-numeric or that is too long.
  45      *
  46      * 114 = 128 - strlen("/proc/") - strlen("/status") - 1
  47      */
  48     local_pid = atoi(entry->d_name);
  49     if ((local_pid <= 0) || (strlen(entry->d_name) > 114)) {
  50         return -1;
  51     }
  52     if (pid) {
  53         *pid = (pid_t) local_pid;
  54     }
  55 
  56     /* Get this entry's file information */
  57     strcpy(procpath, "/proc/");
  58     strcat(procpath, entry->d_name);
  59     fd = open(procpath, O_RDONLY);
  60     if (fd < 0 ) {
  61         return -1;
  62     }
  63     if (fstat(fd, &statbuf) < 0) {
  64         close(fd);
  65         return -1;
  66     }
  67     close(fd);
  68 
  69     /* We're only interested in subdirectories */
  70     if (!S_ISDIR(statbuf.st_mode)) {
  71         return -1;
  72     }
  73 
  74     /* Read the first entry ("Name:") from the process's status file.
  75      * We could handle the valgrind case if we parsed the cmdline file
  76      * instead, but that's more of a pain than it's worth.
  77      */
  78     if (name != NULL) {
  79         strcat(procpath, "/status");
  80         file = fopen(procpath, "r");
  81         if (!file) {
  82             return -1;
  83         }
  84         if (fscanf(file, "Name:\t%15[^\n]", name) != 1) {
  85             fclose(file);
  86             return -1;
  87         }
  88         name[15] = 0;
  89         fclose(file);
  90     }
  91 
  92     return 0;
  93 }
  94 
  95 /*!
  96  * \internal
  97  * \brief Return process ID of a named process
  98  *
  99  * \param[in] name  Process name (as used in /proc/.../status)
 100  *
 101  * \return Process ID of named process if running, 0 otherwise
 102  *
 103  * \note This will return 0 if the process is being run via valgrind.
 104  *       This should be called only on Linux systems.
 105  */
 106 pid_t
 107 pcmk__procfs_pid_of(const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 108 {
 109     DIR *dp;
 110     struct dirent *entry;
 111     pid_t pid = 0;
 112     char entry_name[64] = { 0 };
 113 
 114     dp = opendir("/proc");
 115     if (dp == NULL) {
 116         crm_notice("Can not read /proc directory to track existing components");
 117         return 0;
 118     }
 119 
 120     while ((entry = readdir(dp)) != NULL) {
 121         if ((pcmk__procfs_process_info(entry, entry_name, &pid) == pcmk_rc_ok)
 122             && pcmk__str_eq(entry_name, name, pcmk__str_casei)
 123             && (pcmk__pid_active(pid, NULL) == pcmk_rc_ok)) {
 124 
 125             crm_info("Found %s active as process %lld", name, (long long) pid);
 126             break;
 127         }
 128         pid = 0;
 129     }
 130     closedir(dp);
 131     return pid;
 132 }
 133 
 134 /*!
 135  * \internal
 136  * \brief Calculate number of logical CPU cores from procfs
 137  *
 138  * \return Number of cores (or 1 if unable to determine)
 139  */
 140 unsigned int
 141 pcmk__procfs_num_cores(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 142 {
 143     int cores = 0;
 144     FILE *stream = NULL;
 145 
 146     /* Parse /proc/stat instead of /proc/cpuinfo because it's smaller */
 147     stream = fopen("/proc/stat", "r");
 148     if (stream == NULL) {
 149         crm_perror(LOG_INFO, "Could not open /proc/stat");
 150     } else {
 151         char buffer[2048];
 152 
 153         while (fgets(buffer, sizeof(buffer), stream)) {
 154             if (pcmk__starts_with(buffer, "cpu") && isdigit(buffer[3])) {
 155                 ++cores;
 156             }
 157         }
 158         fclose(stream);
 159     }
 160     return cores? cores : 1;
 161 }
 162 
 163 /*!
 164  * \internal
 165  * \brief Get the executable path corresponding to a process ID
 166  *
 167  * \param[in]  pid        Process ID to check
 168  * \param[out] path       Where to store executable path
 169  * \param[in]  path_size  Size of \p path in characters (ideally PATH_MAX)
 170  *
 171  * \return Standard Pacemaker error code (as possible errno values from
 172  *         readlink())
 173  */
 174 int
 175 pcmk__procfs_pid2path(pid_t pid, char path[], size_t path_size)
     /* [previous][next][first][last][top][bottom][index][help] */
 176 {
 177 #if HAVE_LINUX_PROCFS
 178     char procfs_exe_path[PATH_MAX];
 179     ssize_t link_rc;
 180 
 181     if (snprintf(procfs_exe_path, PATH_MAX, "/proc/%lld/exe",
 182                  (long long) pid) >= PATH_MAX) {
 183         return ENAMETOOLONG; // Truncated (shouldn't be possible in practice)
 184     }
 185 
 186     link_rc = readlink(procfs_exe_path, path, path_size - 1);
 187     if (link_rc < 0) {
 188         return errno;
 189     } else if (link_rc >= (path_size - 1)) {
 190         return ENAMETOOLONG;
 191     }
 192 
 193     path[link_rc] = '\0';
 194     return pcmk_rc_ok;
 195 #else
 196     return EOPNOTSUPP;
 197 #endif // HAVE_LINUX_PROCFS
 198 }
 199 
 200 /*!
 201  * \internal
 202  * \brief Check whether process ID information is available from procfs
 203  *
 204  * \return true if process ID information is available, otherwise false
 205  */
 206 bool
 207 pcmk__procfs_has_pids(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 208 {
 209 #if HAVE_LINUX_PROCFS
 210     static bool have_pids = false;
 211     static bool checked = false;
 212 
 213     if (!checked) {
 214         char path[PATH_MAX];
 215 
 216         have_pids = pcmk__procfs_pid2path(getpid(), path, sizeof(path)) == pcmk_rc_ok;
 217         checked = true;
 218     }
 219     return have_pids;
 220 #else
 221     return false;
 222 #endif // HAVE_LINUX_PROCFS
 223 }

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