pacemaker  3.0.0-d8340737c4
Scalable High-Availability cluster resource manager
pid.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-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 <string.h>
14 #include <sys/stat.h>
15 
16 #include <crm/crm.h>
17 
18 int
19 pcmk__pid_active(pid_t pid, const char *daemon)
20 {
21  static pid_t last_asked_pid = 0; /* log spam prevention */
22  int rc = 0;
23 
24  if (pid <= 0) {
25  return EINVAL;
26  }
27 
28  rc = kill(pid, 0);
29  if ((rc < 0) && (errno == ESRCH)) {
30  return ESRCH; /* no such PID detected */
31 
32  } else if ((daemon == NULL) || !pcmk__procfs_has_pids()) {
33  // The kill result is all we have, we can't check the name
34 
35  if (rc == 0) {
36  return pcmk_rc_ok;
37  }
38  rc = errno;
39  if (last_asked_pid != pid) {
40  crm_info("Cannot examine PID %lld: %s",
41  (long long) pid, pcmk_rc_str(rc));
42  last_asked_pid = pid;
43  }
44  return rc; /* errno != ESRCH */
45 
46  } else {
47  /* make sure PID hasn't been reused by another process
48  XXX: might still be just a zombie, which could confuse decisions */
49  bool checked_through_kill = (rc == 0);
50  char exe_path[PATH_MAX], myexe_path[PATH_MAX];
51 
52  rc = pcmk__procfs_pid2path(pid, exe_path, sizeof(exe_path));
53  if (rc != pcmk_rc_ok) {
54  if (rc != EACCES) {
55  // Check again to filter out races
56  if ((kill(pid, 0) < 0) && (errno == ESRCH)) {
57  return ESRCH;
58  }
59  }
60  if (last_asked_pid != pid) {
61  if (rc == EACCES) {
62  crm_info("Could not get executable for PID %lld: %s "
63  QB_XS " rc=%d",
64  (long long) pid, pcmk_rc_str(rc), rc);
65  } else {
66  crm_err("Could not get executable for PID %lld: %s "
67  QB_XS " rc=%d",
68  (long long) pid, pcmk_rc_str(rc), rc);
69  }
70  last_asked_pid = pid;
71  }
72  if (rc == EACCES) {
73  // Trust kill if it was OK (we can't double-check via path)
74  return checked_through_kill? pcmk_rc_ok : EACCES;
75  } else {
76  return ESRCH; /* most likely errno == ENOENT */
77  }
78  }
79 
80  if (daemon[0] != '/') {
81  rc = snprintf(myexe_path, sizeof(myexe_path), CRM_DAEMON_DIR"/%s",
82  daemon);
83  } else {
84  rc = snprintf(myexe_path, sizeof(myexe_path), "%s", daemon);
85  }
86 
87  if (rc > 0 && rc < sizeof(myexe_path) && !strcmp(exe_path, myexe_path)) {
88  return pcmk_rc_ok;
89  }
90  }
91 
92  return ESRCH;
93 }
94 
95 #define LOCKSTRLEN 11
96 
106 int
107 pcmk__read_pidfile(const char *filename, pid_t *pid)
108 {
109  int fd;
110  struct stat sbuf;
111  int rc = pcmk_rc_ok;
112  long long pid_read = 0;
113  char buf[LOCKSTRLEN + 1];
114 
115  CRM_CHECK((filename != NULL) && (pid != NULL), return EINVAL);
116 
117  fd = open(filename, O_RDONLY);
118  if (fd < 0) {
119  return errno;
120  }
121 
122  if ((fstat(fd, &sbuf) >= 0) && (sbuf.st_size < LOCKSTRLEN)) {
123  sleep(2); /* if someone was about to create one,
124  * give'm a sec to do so
125  */
126  }
127 
128  if (read(fd, buf, sizeof(buf)) < 1) {
129  rc = errno;
130  goto bail;
131  }
132 
133  errno = 0;
134  rc = sscanf(buf, "%lld", &pid_read);
135 
136  if (rc > 0) {
137  if (pid_read <= 0) {
138  rc = ESRCH;
139  } else {
140  rc = pcmk_rc_ok;
141  *pid = (pid_t) pid_read;
142  crm_trace("Read pid %lld from %s", pid_read, filename);
143  }
144  } else if (rc == 0) {
145  rc = ENODATA;
146  } else {
147  rc = errno;
148  }
149 
150  bail:
151  close(fd);
152  return rc;
153 }
154 
167 int
168 pcmk__pidfile_matches(const char *filename, pid_t expected_pid,
169  const char *expected_name, pid_t *pid)
170 {
171  pid_t pidfile_pid = 0;
172  int rc = pcmk__read_pidfile(filename, &pidfile_pid);
173 
174  if (pid) {
175  *pid = pidfile_pid;
176  }
177 
178  if (rc != pcmk_rc_ok) {
179  // Error reading PID file or invalid contents
180  unlink(filename);
181  rc = ENOENT;
182 
183  } else if ((expected_pid > 0) && (pidfile_pid == expected_pid)) {
184  // PID in file matches what was expected
185  rc = pcmk_rc_ok;
186 
187  } else if (pcmk__pid_active(pidfile_pid, expected_name) == ESRCH) {
188  // Contains a stale value
189  unlink(filename);
190  rc = ENOENT;
191 
192  } else if ((expected_pid > 0) && (pidfile_pid != expected_pid)) {
193  // Locked by existing process
194  rc = EEXIST;
195  }
196 
197  return rc;
198 }
199 
209 int
210 pcmk__lock_pidfile(const char *filename, const char *name)
211 {
212  pid_t mypid = getpid();
213  int fd = 0;
214  int rc = 0;
215  char buf[LOCKSTRLEN + 2];
216 
217  rc = pcmk__pidfile_matches(filename, 0, name, NULL);
218  if ((rc != pcmk_rc_ok) && (rc != ENOENT)) {
219  // Locked by existing process
220  return rc;
221  }
222 
223  fd = open(filename, O_CREAT | O_WRONLY | O_EXCL, 0644);
224  if (fd < 0) {
225  return errno;
226  }
227 
228  snprintf(buf, sizeof(buf), "%*lld\n", LOCKSTRLEN - 1, (long long) mypid);
229  rc = write(fd, buf, LOCKSTRLEN);
230  close(fd);
231 
232  if (rc != LOCKSTRLEN) {
233  crm_perror(LOG_ERR, "Incomplete write to %s", filename);
234  return errno;
235  }
236 
237  rc = pcmk__pidfile_matches(filename, mypid, name, NULL);
238  if (rc != pcmk_rc_ok) {
239  // Something is really wrong -- maybe I/O error on read back?
240  unlink(filename);
241  }
242  return rc;
243 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:213
A dumping ground.
int pcmk__read_pidfile(const char *filename, pid_t *pid)
Definition: pid.c:107
const char * name
Definition: cib.c:26
int pcmk__pid_active(pid_t pid, const char *daemon)
Definition: pid.c:19
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:609
bool pcmk__procfs_has_pids(void)
Definition: procfs.c:207
uint32_t pid
Definition: cpg.c:49
#define CRM_DAEMON_DIR
Definition: config.h:21
#define crm_trace(fmt, args...)
Definition: logging.h:372
int pcmk__pidfile_matches(const char *filename, pid_t expected_pid, const char *expected_name, pid_t *pid)
Definition: pid.c:168
#define ENODATA
Definition: portability.h:61
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition: logging.h:299
#define crm_err(fmt, args...)
Definition: logging.h:359
int pcmk__procfs_pid2path(pid_t pid, char path[], size_t path_size)
Definition: procfs.c:175
#define LOCKSTRLEN
Definition: pid.c:95
int pcmk__lock_pidfile(const char *filename, const char *name)
Definition: pid.c:210
#define crm_info(fmt, args...)
Definition: logging.h:367