pacemaker  2.1.4-dc6eb4362
Scalable High-Availability cluster resource manager
pid.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-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 <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 #if SUPPORT_PROCFS
27  static int have_proc_pid = 0;
28 #else
29  static int have_proc_pid = -1;
30 #endif
31  int rc = 0;
32  bool no_name_check = ((daemon == NULL) || (have_proc_pid == -1));
33 
34  if (have_proc_pid == 0) {
35  /* evaluation of /proc/PID/exe applicability via self-introspection */
36  char proc_path[PATH_MAX], exe_path[PATH_MAX];
37  snprintf(proc_path, sizeof(proc_path), "/proc/%lld/exe",
38  (long long) getpid());
39  have_proc_pid = 1;
40  if (readlink(proc_path, exe_path, sizeof(exe_path) - 1) < 0) {
41  have_proc_pid = -1;
42  }
43  }
44 
45  if (pid <= 0) {
46  return EINVAL;
47  }
48 
49  rc = kill(pid, 0);
50  if ((rc < 0) && (errno == ESRCH)) {
51  return ESRCH; /* no such PID detected */
52 
53  } else if ((rc < 0) && no_name_check) {
54  rc = errno;
55  if (last_asked_pid != pid) {
56  crm_info("Cannot examine PID %lld: %s",
57  (long long) pid, strerror(errno));
58  last_asked_pid = pid;
59  }
60  return rc; /* errno != ESRCH */
61 
62  } else if ((rc == 0) && no_name_check) {
63  return pcmk_rc_ok; /* kill as the only indicator, cannot double check */
64 
65  } else if (daemon != NULL) {
66  /* make sure PID hasn't been reused by another process
67  XXX: might still be just a zombie, which could confuse decisions */
68  bool checked_through_kill = (rc == 0);
69  char proc_path[PATH_MAX], exe_path[PATH_MAX], myexe_path[PATH_MAX];
70  snprintf(proc_path, sizeof(proc_path), "/proc/%lld/exe",
71  (long long) pid);
72 
73  rc = readlink(proc_path, exe_path, sizeof(exe_path) - 1);
74  if (rc < 0) {
75  int rdlnk_errno = errno;
76 
77  if (rdlnk_errno != EACCES) {
78  int rc = kill(pid,0); /* check once again - filter out races */
79 
80  if ((rc < 0) && (errno == ESRCH)) {
81  return ESRCH;
82  }
83  }
84  if (last_asked_pid != pid) {
85  if (rdlnk_errno == EACCES) {
86  crm_info("Could not read from %s: %s " CRM_XS " errno=%d",
87  proc_path, strerror(rdlnk_errno), rdlnk_errno);
88  } else {
89  crm_err("Could not read from %s: %s " CRM_XS " errno=%d",
90  proc_path, strerror(rdlnk_errno), rdlnk_errno);
91  }
92  last_asked_pid = pid;
93  }
94  if ((rdlnk_errno == EACCES) && checked_through_kill) {
95  // Trust kill result, can't double-check via path
96  return pcmk_rc_ok;
97  } else if (rdlnk_errno == EACCES) {
98  return EACCES;
99  } else {
100  return ESRCH; /* most likely errno == ENOENT */
101  }
102  }
103  exe_path[rc] = '\0';
104 
105  if (daemon[0] != '/') {
106  rc = snprintf(myexe_path, sizeof(myexe_path), CRM_DAEMON_DIR"/%s",
107  daemon);
108  } else {
109  rc = snprintf(myexe_path, sizeof(myexe_path), "%s", daemon);
110  }
111 
112  if (rc > 0 && rc < sizeof(myexe_path) && !strcmp(exe_path, myexe_path)) {
113  return pcmk_rc_ok;
114  }
115  }
116 
117  return ESRCH;
118 }
119 
120 #define LOCKSTRLEN 11
121 
131 int
132 pcmk__read_pidfile(const char *filename, pid_t *pid)
133 {
134  int fd;
135  struct stat sbuf;
136  int rc = pcmk_rc_unknown_format;
137  long long pid_read = 0;
138  char buf[LOCKSTRLEN + 1];
139 
140  CRM_CHECK((filename != NULL) && (pid != NULL), return EINVAL);
141 
142  fd = open(filename, O_RDONLY);
143  if (fd < 0) {
144  return errno;
145  }
146 
147  if ((fstat(fd, &sbuf) >= 0) && (sbuf.st_size < LOCKSTRLEN)) {
148  sleep(2); /* if someone was about to create one,
149  * give'm a sec to do so
150  */
151  }
152 
153  if (read(fd, buf, sizeof(buf)) < 1) {
154  rc = errno;
155  goto bail;
156  }
157 
158  if (sscanf(buf, "%lld", &pid_read) > 0) {
159  if (pid_read <= 0) {
160  rc = ESRCH;
161  } else {
162  rc = pcmk_rc_ok;
163  *pid = (pid_t) pid_read;
164  crm_trace("Read pid %lld from %s", pid_read, filename);
165  }
166  }
167 
168  bail:
169  close(fd);
170  return rc;
171 }
172 
185 int
186 pcmk__pidfile_matches(const char *filename, pid_t expected_pid,
187  const char *expected_name, pid_t *pid)
188 {
189  pid_t pidfile_pid = 0;
190  int rc = pcmk__read_pidfile(filename, &pidfile_pid);
191 
192  if (pid) {
193  *pid = pidfile_pid;
194  }
195 
196  if (rc != pcmk_rc_ok) {
197  // Error reading PID file or invalid contents
198  unlink(filename);
199  rc = ENOENT;
200 
201  } else if ((expected_pid > 0) && (pidfile_pid == expected_pid)) {
202  // PID in file matches what was expected
203  rc = pcmk_rc_ok;
204 
205  } else if (pcmk__pid_active(pidfile_pid, expected_name) == ESRCH) {
206  // Contains a stale value
207  unlink(filename);
208  rc = ENOENT;
209 
210  } else if ((expected_pid > 0) && (pidfile_pid != expected_pid)) {
211  // Locked by existing process
212  rc = EEXIST;
213  }
214 
215  return rc;
216 }
217 
227 int
228 pcmk__lock_pidfile(const char *filename, const char *name)
229 {
230  pid_t mypid = getpid();
231  int fd = 0;
232  int rc = 0;
233  char buf[LOCKSTRLEN + 2];
234 
235  rc = pcmk__pidfile_matches(filename, 0, name, NULL);
236  if ((rc != pcmk_rc_ok) && (rc != ENOENT)) {
237  // Locked by existing process
238  return rc;
239  }
240 
241  fd = open(filename, O_CREAT | O_WRONLY | O_EXCL, 0644);
242  if (fd < 0) {
243  return errno;
244  }
245 
246  snprintf(buf, sizeof(buf), "%*lld\n", LOCKSTRLEN - 1, (long long) mypid);
247  rc = write(fd, buf, LOCKSTRLEN);
248  close(fd);
249 
250  if (rc != LOCKSTRLEN) {
251  crm_perror(LOG_ERR, "Incomplete write to %s", filename);
252  return errno;
253  }
254 
255  rc = pcmk__pidfile_matches(filename, mypid, name, NULL);
256  if (rc != pcmk_rc_ok) {
257  // Something is really wrong -- maybe I/O error on read back?
258  unlink(filename);
259  }
260  return rc;
261 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:226
A dumping ground.
int pcmk__read_pidfile(const char *filename, pid_t *pid)
Definition: pid.c:132
const char * name
Definition: cib.c:24
int pcmk__pid_active(pid_t pid, const char *daemon)
Definition: pid.c:23
char * strerror(int errnum)
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:364
int pcmk__pidfile_matches(const char *filename, pid_t expected_pid, const char *expected_name, pid_t *pid)
Definition: pid.c:186
#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:309
#define crm_err(fmt, args...)
Definition: logging.h:358
#define LOCKSTRLEN
Definition: pid.c:120
int pcmk__lock_pidfile(const char *filename, const char *name)
Definition: pid.c:228
#define crm_info(fmt, args...)
Definition: logging.h:361