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