pacemaker  3.0.0-d8340737c4
Scalable High-Availability cluster resource manager
procfs.c
Go to the documentation of this file.
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 
35 static int
36 pcmk__procfs_process_info(const struct dirent *entry, char *name, pid_t *pid)
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 
106 pid_t
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 
140 unsigned int
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 
174 int
175 pcmk__procfs_pid2path(pid_t pid, char path[], size_t path_size)
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 
206 bool
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 }
#define crm_notice(fmt, args...)
Definition: logging.h:365
const char * name
Definition: cib.c:26
int pcmk__procfs_pid2path(pid_t pid, char path[], size_t path_size)
Definition: procfs.c:175
bool pcmk__procfs_has_pids(void)
Definition: procfs.c:207
uint32_t pid
Definition: cpg.c:49
unsigned int pcmk__procfs_num_cores(void)
Definition: procfs.c:141
int pcmk__pid_active(pid_t pid, const char *daemon)
Definition: pid.c:19
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition: logging.h:299
const char * path
Definition: cib.c:28
pid_t pcmk__procfs_pid_of(const char *name)
Definition: procfs.c:107
bool pcmk__starts_with(const char *str, const char *prefix)
Check whether a string starts with a certain sequence.
Definition: strings.c:558
#define crm_info(fmt, args...)
Definition: logging.h:367