pacemaker  2.0.2-debe490
Scalable High-Availability cluster resource manager
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
pid.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2019 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 crm_pid_active(long pid, const char *daemon)
24 {
25  static int 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 
33  if (have_proc_pid == 0) {
34  /* evaluation of /proc/PID/exe applicability via self-introspection */
35  char proc_path[PATH_MAX], exe_path[PATH_MAX];
36  snprintf(proc_path, sizeof(proc_path), "/proc/%lu/exe",
37  (long unsigned int) getpid());
38  have_proc_pid = 1;
39  if (readlink(proc_path, exe_path, sizeof(exe_path) - 1) < 0) {
40  have_proc_pid = -1;
41  }
42  }
43 
44  if (pid <= 0) {
45  return -1;
46 
47  } else if ((rc = kill(pid, 0)) < 0 && errno == ESRCH) {
48  return 0; /* no such PID detected */
49 
50  } else if (rc < 0 && (daemon == NULL || have_proc_pid == -1)) {
51  if (last_asked_pid != pid) {
52  crm_info("Cannot examine PID %ld: %s", pid, strerror(errno));
53  last_asked_pid = pid;
54  }
55  return -2; /* errno != ESRCH */
56 
57  } else if (rc == 0 && (daemon == NULL || have_proc_pid == -1)) {
58  return 1; /* kill as the only indicator, cannot double check */
59 
60  } else if (daemon != NULL) {
61  /* make sure PID hasn't been reused by another process
62  XXX: might still be just a zombie, which could confuse decisions */
63  bool checked_through_kill = (rc == 0);
64  char proc_path[PATH_MAX], exe_path[PATH_MAX], myexe_path[PATH_MAX];
65  snprintf(proc_path, sizeof(proc_path), "/proc/%ld/exe", pid);
66 
67  rc = readlink(proc_path, exe_path, sizeof(exe_path) - 1);
68  if ((rc < 0) && (errno == EACCES)) {
69  if (last_asked_pid != pid) {
70  crm_info("Could not read from %s: %s", proc_path,
71  strerror(errno));
72  last_asked_pid = pid;
73  }
74  return checked_through_kill ? 1 : -2;
75  } else if (rc < 0) {
76  if (last_asked_pid != pid) {
77  crm_err("Could not read from %s: %s (%d)", proc_path,
78  strerror(errno), errno);
79  last_asked_pid = pid;
80  }
81  return 0; /* most likely errno == ENOENT */
82  }
83  exe_path[rc] = '\0';
84 
85  if (daemon[0] != '/') {
86  rc = snprintf(myexe_path, sizeof(myexe_path), CRM_DAEMON_DIR"/%s",
87  daemon);
88  } else {
89  rc = snprintf(myexe_path, sizeof(myexe_path), "%s", daemon);
90  }
91 
92  if (rc > 0 && rc < sizeof(myexe_path) && !strcmp(exe_path, myexe_path)) {
93  return 1;
94  }
95  }
96 
97  return 0;
98 }
99 
100 #define LOCKSTRLEN 11
101 
102 long
103 crm_read_pidfile(const char *filename)
104 {
105  int fd;
106  struct stat sbuf;
107  long pid = -ENOENT;
108  char buf[LOCKSTRLEN + 1];
109 
110  fd = open(filename, O_RDONLY);
111  if (fd < 0) {
112  goto bail;
113  }
114 
115  if ((fstat(fd, &sbuf) >= 0) && (sbuf.st_size < LOCKSTRLEN)) {
116  sleep(2); /* if someone was about to create one,
117  * give'm a sec to do so
118  */
119  }
120 
121  if (read(fd, buf, sizeof(buf)) < 1) {
122  goto bail;
123  }
124 
125  if (sscanf(buf, "%ld", &pid) > 0) {
126  if (pid <= 0) {
127  pid = -ESRCH;
128  } else {
129  crm_trace("Got pid %lu from %s\n", pid, filename);
130  }
131  }
132 
133  bail:
134  if (fd >= 0) {
135  close(fd);
136  }
137  return pid;
138 }
139 
140 long
141 crm_pidfile_inuse(const char *filename, long mypid, const char *daemon)
142 {
143  long pid = crm_read_pidfile(filename);
144 
145  if (pid < 2) {
146  // Invalid pid
147  pid = -ENOENT;
148  unlink(filename);
149 
150  } else if (mypid && (pid == mypid)) {
151  // In use by us
152  pid = pcmk_ok;
153 
154  } else if (crm_pid_active(pid, daemon) == FALSE) {
155  // Contains a stale value
156  unlink(filename);
157  pid = -ENOENT;
158 
159  } else if (mypid && (pid != mypid)) {
160  // Locked by existing process
161  pid = -EEXIST;
162  }
163 
164  return pid;
165 }
166 
167 int
168 crm_lock_pidfile(const char *filename, const char *name)
169 {
170  long mypid = 0;
171  int fd = 0;
172  int rc = 0;
173  char buf[LOCKSTRLEN + 2];
174 
175  mypid = (unsigned long) getpid();
176 
177  rc = crm_pidfile_inuse(filename, 0, name);
178  if (rc == -ENOENT) {
179  // Exists, but the process is not active
180 
181  } else if (rc != pcmk_ok) {
182  // Locked by existing process
183  return rc;
184  }
185 
186  fd = open(filename, O_CREAT | O_WRONLY | O_EXCL, 0644);
187  if (fd < 0) {
188  return -errno;
189  }
190 
191  snprintf(buf, sizeof(buf), "%*ld\n", LOCKSTRLEN - 1, mypid);
192  rc = write(fd, buf, LOCKSTRLEN);
193  close(fd);
194 
195  if (rc != LOCKSTRLEN) {
196  crm_perror(LOG_ERR, "Incomplete write to %s", filename);
197  return -errno;
198  }
199 
200  return crm_pidfile_inuse(filename, mypid, name);
201 }
A dumping ground.
int crm_lock_pidfile(const char *filename, const char *name)
Definition: pid.c:168
uint32_t pid
Definition: internal.h:83
char * strerror(int errnum)
int daemon(int nochdir, int noclose)
#define CRM_DAEMON_DIR
Definition: config.h:26
#define crm_trace(fmt, args...)
Definition: logging.h:246
long crm_pidfile_inuse(const char *filename, long mypid, const char *daemon)
Definition: pid.c:141
int crm_pid_active(long pid, const char *daemon)
Definition: pid.c:23
#define crm_perror(level, fmt, args...)
Log a system error message.
Definition: logging.h:218
#define crm_err(fmt, args...)
Definition: logging.h:240
#define pcmk_ok
Definition: results.h:57
#define LOCKSTRLEN
Definition: pid.c:100
#define crm_info(fmt, args...)
Definition: logging.h:243
long crm_read_pidfile(const char *filename)
Definition: pid.c:103