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]](../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)
*/
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]](../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)
*/
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]](../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)
*/
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 }