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

   1 /*
   2  * Copyright 2015-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 <stdlib.h>
  18 #include <string.h>
  19 #include <sys/stat.h>
  20 #include <sys/types.h>
  21 #include <dirent.h>
  22 #include <ctype.h>
  23 
  24 /*!
  25  * \internal
  26  * \brief Get process ID and name associated with a /proc directory entry
  27  *
  28  * \param[in]  entry    Directory entry (must be result of readdir() on /proc)
  29  * \param[out] name     If not NULL, a char[16] to hold the process name
  30  * \param[out] pid      If not NULL, will be set to process ID of entry
  31  *
  32  * \return Standard Pacemaker return code
  33  * \note This should be called only on Linux systems, as not all systems that
  34  *       support /proc store process names and IDs in the same way. The kernel
  35  *       limits the process name to the first 15 characters (plus terminator).
  36  *       It would be nice if there were a public kernel API constant for that
  37  *       limit, but there isn't.
  38  */
  39 static int
  40 pcmk__procfs_process_info(struct dirent *entry, char *name, pid_t *pid)
     /* [previous][next][first][last][top][bottom][index][help] */
  41 {
  42     int fd, local_pid;
  43     FILE *file;
  44     struct stat statbuf;
  45     char procpath[128] = { 0 };
  46 
  47     /* We're only interested in entries whose name is a PID,
  48      * so skip anything non-numeric or that is too long.
  49      *
  50      * 114 = 128 - strlen("/proc/") - strlen("/status") - 1
  51      */
  52     local_pid = atoi(entry->d_name);
  53     if ((local_pid <= 0) || (strlen(entry->d_name) > 114)) {
  54         return -1;
  55     }
  56     if (pid) {
  57         *pid = (pid_t) local_pid;
  58     }
  59 
  60     /* Get this entry's file information */
  61     strcpy(procpath, "/proc/");
  62     strcat(procpath, entry->d_name);
  63     fd = open(procpath, O_RDONLY);
  64     if (fd < 0 ) {
  65         return -1;
  66     }
  67     if (fstat(fd, &statbuf) < 0) {
  68         close(fd);
  69         return -1;
  70     }
  71     close(fd);
  72 
  73     /* We're only interested in subdirectories */
  74     if (!S_ISDIR(statbuf.st_mode)) {
  75         return -1;
  76     }
  77 
  78     /* Read the first entry ("Name:") from the process's status file.
  79      * We could handle the valgrind case if we parsed the cmdline file
  80      * instead, but that's more of a pain than it's worth.
  81      */
  82     if (name != NULL) {
  83         strcat(procpath, "/status");
  84         file = fopen(procpath, "r");
  85         if (!file) {
  86             return -1;
  87         }
  88         if (fscanf(file, "Name:\t%15[^\n]", name) != 1) {
  89             fclose(file);
  90             return -1;
  91         }
  92         name[15] = 0;
  93         fclose(file);
  94     }
  95 
  96     return 0;
  97 }
  98 
  99 /*!
 100  * \internal
 101  * \brief Return process ID of a named process
 102  *
 103  * \param[in] name  Process name (as used in /proc/.../status)
 104  *
 105  * \return Process ID of named process if running, 0 otherwise
 106  *
 107  * \note This will return 0 if the process is being run via valgrind.
 108  *       This should be called only on Linux systems.
 109  */
 110 pid_t
 111 pcmk__procfs_pid_of(const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 112 {
 113     DIR *dp;
 114     struct dirent *entry;
 115     pid_t pid = 0;
 116     char entry_name[64] = { 0 };
 117 
 118     dp = opendir("/proc");
 119     if (dp == NULL) {
 120         crm_notice("Can not read /proc directory to track existing components");
 121         return 0;
 122     }
 123 
 124     while ((entry = readdir(dp)) != NULL) {
 125         if ((pcmk__procfs_process_info(entry, entry_name, &pid) == pcmk_rc_ok)
 126             && pcmk__str_eq(entry_name, name, pcmk__str_casei)
 127             && (pcmk__pid_active(pid, NULL) == pcmk_rc_ok)) {
 128 
 129             crm_info("Found %s active as process %lld", name, (long long) pid);
 130             break;
 131         }
 132         pid = 0;
 133     }
 134     closedir(dp);
 135     return pid;
 136 }
 137 
 138 /*!
 139  * \internal
 140  * \brief Calculate number of logical CPU cores from procfs
 141  *
 142  * \return Number of cores (or 1 if unable to determine)
 143  */
 144 unsigned int
 145 pcmk__procfs_num_cores(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 146 {
 147     int cores = 0;
 148     FILE *stream = NULL;
 149 
 150     /* Parse /proc/stat instead of /proc/cpuinfo because it's smaller */
 151     stream = fopen("/proc/stat", "r");
 152     if (stream == NULL) {
 153         crm_perror(LOG_INFO, "Could not open /proc/stat");
 154     } else {
 155         char buffer[2048];
 156 
 157         while (fgets(buffer, sizeof(buffer), stream)) {
 158             if (pcmk__starts_with(buffer, "cpu") && isdigit(buffer[3])) {
 159                 ++cores;
 160             }
 161         }
 162         fclose(stream);
 163     }
 164     return cores? cores : 1;
 165 }

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