root/lib/common/procfs.c

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

DEFINITIONS

This source file includes following definitions.
  1. crm_procfs_process_info
  2. crm_procfs_pid_of
  3. crm_procfs_num_cores

   1 /*
   2  * Copyright (C) 2015 Andrew Beekhof <andrew@beekhof.net>
   3  *
   4  * This library is free software; you can redistribute it and/or
   5  * modify it under the terms of the GNU Lesser General Public
   6  * License as published by the Free Software Foundation; either
   7  * version 2.1 of the License, or (at your option) any later version.
   8  *
   9  * This library is distributed in the hope that it will be useful,
  10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12  * Lesser General Public License for more details.
  13  *
  14  * You should have received a copy of the GNU Lesser General Public
  15  * License along with this library; if not, write to the Free Software
  16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  17  */
  18 
  19 #include <crm_internal.h>
  20 
  21 #ifndef _GNU_SOURCE
  22 #  define _GNU_SOURCE
  23 #endif
  24 
  25 #include <stdio.h>
  26 #include <stdlib.h>
  27 #include <string.h>
  28 #include <sys/stat.h>
  29 #include <sys/types.h>
  30 #include <dirent.h>
  31 #include <ctype.h>
  32 
  33 /*!
  34  * \internal
  35  * \brief Get process ID and name associated with a /proc directory entry
  36  *
  37  * \param[in]  entry    Directory entry (must be result of readdir() on /proc)
  38  * \param[out] name     If not NULL, a char[64] to hold the process name
  39  * \param[out] pid      If not NULL, will be set to process ID of entry
  40  *
  41  * \return 0 on success, -1 if entry is not for a process or info not found
  42  *
  43  * \note This should be called only on Linux systems, as not all systems that
  44  *       support /proc store process names and IDs in the same way.
  45  */
  46 int
  47 crm_procfs_process_info(struct dirent *entry, char *name, int *pid)
     /* [previous][next][first][last][top][bottom][index][help] */
  48 {
  49     int fd, local_pid;
  50     FILE *file;
  51     struct stat statbuf;
  52     char key[16] = { 0 }, procpath[128] = { 0 };
  53 
  54     /* We're only interested in entries whose name is a PID,
  55      * so skip anything non-numeric or that is too long.
  56      *
  57      * 114 = 128 - strlen("/proc/") - strlen("/status") - 1
  58      */
  59     local_pid = atoi(entry->d_name);
  60     if ((local_pid <= 0) || (strlen(entry->d_name) > 114)) {
  61         return -1;
  62     }
  63     if (pid) {
  64         *pid = local_pid;
  65     }
  66 
  67     /* Get this entry's file information */
  68     strcpy(procpath, "/proc/");
  69     strcat(procpath, entry->d_name);
  70     fd = open(procpath, O_RDONLY);
  71     if (fd < 0 ) {
  72         return -1;
  73     }
  74     if (fstat(fd, &statbuf) < 0) {
  75         close(fd);
  76         return -1;
  77     }
  78     close(fd);
  79 
  80     /* We're only interested in subdirectories */
  81     if (!S_ISDIR(statbuf.st_mode)) {
  82         return -1;
  83     }
  84 
  85     /* Read the first entry ("Name:") from the process's status file.
  86      * We could handle the valgrind case if we parsed the cmdline file
  87      * instead, but that's more of a pain than it's worth.
  88      */
  89     if (name != NULL) {
  90         strcat(procpath, "/status");
  91         file = fopen(procpath, "r");
  92         if (!file) {
  93             return -1;
  94         }
  95         if ((fscanf(file, "%15s%63s", key, name) != 2)
  96             || safe_str_neq(key, "Name:")) {
  97             fclose(file);
  98             return -1;
  99         }
 100         fclose(file);
 101     }
 102 
 103     return 0;
 104 }
 105 
 106 /*!
 107  * \internal
 108  * \brief Return process ID of a named process
 109  *
 110  * \param[in] name  Process name (as used in /proc/.../status)
 111  *
 112  * \return Process ID of named process if running, 0 otherwise
 113  *
 114  * \note This will return 0 if the process is being run via valgrind.
 115  *       This should be called only on Linux systems.
 116  */
 117 int
 118 crm_procfs_pid_of(const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 119 {
 120     DIR *dp;
 121     struct dirent *entry;
 122     int pid = 0;
 123     char entry_name[64] = { 0 };
 124 
 125     dp = opendir("/proc");
 126     if (dp == NULL) {
 127         crm_notice("Can not read /proc directory to track existing components");
 128         return 0;
 129     }
 130 
 131     while ((entry = readdir(dp)) != NULL) {
 132         if ((crm_procfs_process_info(entry, entry_name, &pid) == 0)
 133             && safe_str_eq(entry_name, name)
 134             && (crm_pid_active(pid, NULL) == 1)) {
 135 
 136             crm_info("Found %s active as process %d", name, pid);
 137             break;
 138         }
 139         pid = 0;
 140     }
 141     closedir(dp);
 142     return pid;
 143 }
 144 
 145 /*!
 146  * \internal
 147  * \brief Calculate number of logical CPU cores from procfs
 148  *
 149  * \return Number of cores (or 1 if unable to determine)
 150  */
 151 unsigned int
 152 crm_procfs_num_cores(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 153 {
 154     int cores = 0;
 155     FILE *stream = NULL;
 156 
 157     /* Parse /proc/stat instead of /proc/cpuinfo because it's smaller */
 158     stream = fopen("/proc/stat", "r");
 159     if (stream == NULL) {
 160         crm_perror(LOG_INFO, "Could not open /proc/stat");
 161     } else {
 162         char buffer[2048];
 163 
 164         while (fgets(buffer, sizeof(buffer), stream)) {
 165             if (crm_starts_with(buffer, "cpu") && isdigit(buffer[3])) {
 166                 ++cores;
 167             }
 168         }
 169         fclose(stream);
 170     }
 171     return cores? cores : 1;
 172 }

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