pacemaker  2.1.6-802a72226b
Scalable High-Availability cluster resource manager
procfs.c
Go to the documentation of this file.
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 
39 static int
40 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  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 
110 pid_t
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 
144 unsigned int
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 }
166 
178 int
179 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  if (snprintf(procfs_exe_path, PATH_MAX, "/proc/%lld/exe",
186  (long long) pid) >= PATH_MAX) {
187  return ENAMETOOLONG; // Truncated (shouldn't be possible in practice)
188  }
189 
190  link_rc = readlink(procfs_exe_path, path, path_size - 1);
191  if (link_rc < 0) {
192  return errno;
193  } else if (link_rc >= (path_size - 1)) {
194  return ENAMETOOLONG;
195  }
196 
197  path[link_rc] = '\0';
198  return pcmk_rc_ok;
199 #else
200  return EOPNOTSUPP;
201 #endif // HAVE_LINUX_PROCFS
202 }
203 
210 bool
212 {
213 #if HAVE_LINUX_PROCFS
214  static bool have_pids = false;
215  static bool checked = false;
216 
217  if (!checked) {
218  char path[PATH_MAX];
219 
220  have_pids = pcmk__procfs_pid2path(getpid(), path, sizeof(path)) == pcmk_rc_ok;
221  checked = true;
222  }
223  return have_pids;
224 #else
225  return false;
226 #endif // HAVE_LINUX_PROCFS
227 }
#define crm_notice(fmt, args...)
Definition: logging.h:379
const char * name
Definition: cib.c:24
int pcmk__procfs_pid2path(pid_t pid, char path[], size_t path_size)
Definition: procfs.c:179
bool pcmk__procfs_has_pids(void)
Definition: procfs.c:211
uint32_t pid
Definition: cpg.c:46
unsigned int pcmk__procfs_num_cores(void)
Definition: procfs.c:145
int pcmk__pid_active(pid_t pid, const char *daemon)
Definition: pid.c:23
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition: logging.h:319
const char * path
Definition: cib.c:26
pid_t pcmk__procfs_pid_of(const char *name)
Definition: procfs.c:111
bool pcmk__starts_with(const char *str, const char *prefix)
Check whether a string starts with a certain sequence.
Definition: strings.c:484
#define crm_info(fmt, args...)
Definition: logging.h:380