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 : }
|