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]](../icons/n_left.png)
![[next]](../icons/right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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]](../icons/left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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 }