LCOV - code coverage report
Current view: top level - common - procfs.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 9 71 12.7 %
Date: 2024-05-07 11:09:47 Functions: 1 5 20.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright 2015-2022 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           0 : pcmk__procfs_process_info(const struct dirent *entry, char *name, pid_t *pid)
      41             : {
      42             :     int fd, local_pid;
      43             :     FILE *file;
      44             :     struct stat statbuf;
      45           0 :     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           0 :     local_pid = atoi(entry->d_name);
      53           0 :     if ((local_pid <= 0) || (strlen(entry->d_name) > 114)) {
      54           0 :         return -1;
      55             :     }
      56           0 :     if (pid) {
      57           0 :         *pid = (pid_t) local_pid;
      58             :     }
      59             : 
      60             :     /* Get this entry's file information */
      61           0 :     strcpy(procpath, "/proc/");
      62           0 :     strcat(procpath, entry->d_name);
      63           0 :     fd = open(procpath, O_RDONLY);
      64           0 :     if (fd < 0 ) {
      65           0 :         return -1;
      66             :     }
      67           0 :     if (fstat(fd, &statbuf) < 0) {
      68           0 :         close(fd);
      69           0 :         return -1;
      70             :     }
      71           0 :     close(fd);
      72             : 
      73             :     /* We're only interested in subdirectories */
      74           0 :     if (!S_ISDIR(statbuf.st_mode)) {
      75           0 :         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           0 :     if (name != NULL) {
      83           0 :         strcat(procpath, "/status");
      84           0 :         file = fopen(procpath, "r");
      85           0 :         if (!file) {
      86           0 :             return -1;
      87             :         }
      88           0 :         if (fscanf(file, "Name:\t%15[^\n]", name) != 1) {
      89           0 :             fclose(file);
      90           0 :             return -1;
      91             :         }
      92           0 :         name[15] = 0;
      93           0 :         fclose(file);
      94             :     }
      95             : 
      96           0 :     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           0 : pcmk__procfs_pid_of(const char *name)
     112             : {
     113             :     DIR *dp;
     114             :     struct dirent *entry;
     115           0 :     pid_t pid = 0;
     116           0 :     char entry_name[64] = { 0 };
     117             : 
     118           0 :     dp = opendir("/proc");
     119           0 :     if (dp == NULL) {
     120           0 :         crm_notice("Can not read /proc directory to track existing components");
     121           0 :         return 0;
     122             :     }
     123             : 
     124           0 :     while ((entry = readdir(dp)) != NULL) {
     125           0 :         if ((pcmk__procfs_process_info(entry, entry_name, &pid) == pcmk_rc_ok)
     126           0 :             && pcmk__str_eq(entry_name, name, pcmk__str_casei)
     127           0 :             && (pcmk__pid_active(pid, NULL) == pcmk_rc_ok)) {
     128             : 
     129           0 :             crm_info("Found %s active as process %lld", name, (long long) pid);
     130           0 :             break;
     131             :         }
     132           0 :         pid = 0;
     133             :     }
     134           0 :     closedir(dp);
     135           0 :     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           0 : pcmk__procfs_num_cores(void)
     146             : {
     147           0 :     int cores = 0;
     148           0 :     FILE *stream = NULL;
     149             : 
     150             :     /* Parse /proc/stat instead of /proc/cpuinfo because it's smaller */
     151           0 :     stream = fopen("/proc/stat", "r");
     152           0 :     if (stream == NULL) {
     153           0 :         crm_perror(LOG_INFO, "Could not open /proc/stat");
     154             :     } else {
     155             :         char buffer[2048];
     156             : 
     157           0 :         while (fgets(buffer, sizeof(buffer), stream)) {
     158           0 :             if (pcmk__starts_with(buffer, "cpu") && isdigit(buffer[3])) {
     159           0 :                 ++cores;
     160             :             }
     161             :         }
     162           0 :         fclose(stream);
     163             :     }
     164           0 :     return cores? cores : 1;
     165             : }
     166             : 
     167             : /*!
     168             :  * \internal
     169             :  * \brief Get the executable path corresponding to a process ID
     170             :  *
     171             :  * \param[in]  pid        Process ID to check
     172             :  * \param[out] path       Where to store executable path
     173             :  * \param[in]  path_size  Size of \p path in characters (ideally PATH_MAX)
     174             :  *
     175             :  * \return Standard Pacemaker error code (as possible errno values from
     176             :  *         readlink())
     177             :  */
     178             : int
     179           5 : pcmk__procfs_pid2path(pid_t pid, char path[], size_t path_size)
     180             : {
     181             : #if HAVE_LINUX_PROCFS
     182             :     char procfs_exe_path[PATH_MAX];
     183             :     ssize_t link_rc;
     184             : 
     185           5 :     if (snprintf(procfs_exe_path, PATH_MAX, "/proc/%lld/exe",
     186             :                  (long long) pid) >= PATH_MAX) {
     187           0 :         return ENAMETOOLONG; // Truncated (shouldn't be possible in practice)
     188             :     }
     189             : 
     190           5 :     link_rc = readlink(procfs_exe_path, path, path_size - 1);
     191           5 :     if (link_rc < 0) {
     192           2 :         return errno;
     193           3 :     } else if (link_rc >= (path_size - 1)) {
     194           1 :         return ENAMETOOLONG;
     195             :     }
     196             : 
     197           2 :     path[link_rc] = '\0';
     198           2 :     return pcmk_rc_ok;
     199             : #else
     200             :     return EOPNOTSUPP;
     201             : #endif // HAVE_LINUX_PROCFS
     202             : }
     203             : 
     204             : /*!
     205             :  * \internal
     206             :  * \brief Check whether process ID information is available from procfs
     207             :  *
     208             :  * \return true if process ID information is available, otherwise false
     209             :  */
     210             : bool
     211           0 : pcmk__procfs_has_pids(void)
     212             : {
     213             : #if HAVE_LINUX_PROCFS
     214             :     static bool have_pids = false;
     215             :     static bool checked = false;
     216             : 
     217           0 :     if (!checked) {
     218             :         char path[PATH_MAX];
     219             : 
     220           0 :         have_pids = pcmk__procfs_pid2path(getpid(), path, sizeof(path)) == pcmk_rc_ok;
     221           0 :         checked = true;
     222             :     }
     223           0 :     return have_pids;
     224             : #else
     225             :     return false;
     226             : #endif // HAVE_LINUX_PROCFS
     227             : }

Generated by: LCOV version 1.14