Line data Source code
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 0 : pcmk__pid_active(pid_t pid, const char *daemon)
24 : {
25 : static pid_t last_asked_pid = 0; /* log spam prevention */
26 0 : int rc = 0;
27 :
28 0 : if (pid <= 0) {
29 0 : return EINVAL;
30 : }
31 :
32 0 : rc = kill(pid, 0);
33 0 : if ((rc < 0) && (errno == ESRCH)) {
34 0 : return ESRCH; /* no such PID detected */
35 :
36 0 : } else if ((daemon == NULL) || !pcmk__procfs_has_pids()) {
37 : // The kill result is all we have, we can't check the name
38 :
39 0 : if (rc == 0) {
40 0 : return pcmk_rc_ok;
41 : }
42 0 : rc = errno;
43 0 : if (last_asked_pid != pid) {
44 0 : crm_info("Cannot examine PID %lld: %s",
45 : (long long) pid, pcmk_rc_str(rc));
46 0 : last_asked_pid = pid;
47 : }
48 0 : 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 0 : bool checked_through_kill = (rc == 0);
54 : char exe_path[PATH_MAX], myexe_path[PATH_MAX];
55 :
56 0 : rc = pcmk__procfs_pid2path(pid, exe_path, sizeof(exe_path));
57 0 : if (rc != pcmk_rc_ok) {
58 0 : if (rc != EACCES) {
59 : // Check again to filter out races
60 0 : if ((kill(pid, 0) < 0) && (errno == ESRCH)) {
61 0 : return ESRCH;
62 : }
63 : }
64 0 : if (last_asked_pid != pid) {
65 0 : if (rc == EACCES) {
66 0 : 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 0 : 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 0 : last_asked_pid = pid;
75 : }
76 0 : if (rc == EACCES) {
77 : // Trust kill if it was OK (we can't double-check via path)
78 0 : return checked_through_kill? pcmk_rc_ok : EACCES;
79 : } else {
80 0 : return ESRCH; /* most likely errno == ENOENT */
81 : }
82 : }
83 :
84 0 : if (daemon[0] != '/') {
85 0 : rc = snprintf(myexe_path, sizeof(myexe_path), CRM_DAEMON_DIR"/%s",
86 : daemon);
87 : } else {
88 0 : rc = snprintf(myexe_path, sizeof(myexe_path), "%s", daemon);
89 : }
90 :
91 0 : if (rc > 0 && rc < sizeof(myexe_path) && !strcmp(exe_path, myexe_path)) {
92 0 : return pcmk_rc_ok;
93 : }
94 : }
95 :
96 0 : return ESRCH;
97 : }
98 :
99 : #define LOCKSTRLEN 11
100 :
101 : /*!
102 : * \internal
103 : * \brief Read a process ID from a file
104 : *
105 : * \param[in] filename Process ID file to read
106 : * \param[out] pid Where to put PID that was read
107 : *
108 : * \return Standard Pacemaker return code
109 : */
110 : int
111 0 : pcmk__read_pidfile(const char *filename, pid_t *pid)
112 : {
113 : int fd;
114 : struct stat sbuf;
115 0 : int rc = pcmk_rc_ok;
116 0 : long long pid_read = 0;
117 : char buf[LOCKSTRLEN + 1];
118 :
119 0 : CRM_CHECK((filename != NULL) && (pid != NULL), return EINVAL);
120 :
121 0 : fd = open(filename, O_RDONLY);
122 0 : if (fd < 0) {
123 0 : return errno;
124 : }
125 :
126 0 : if ((fstat(fd, &sbuf) >= 0) && (sbuf.st_size < LOCKSTRLEN)) {
127 0 : sleep(2); /* if someone was about to create one,
128 : * give'm a sec to do so
129 : */
130 : }
131 :
132 0 : if (read(fd, buf, sizeof(buf)) < 1) {
133 0 : rc = errno;
134 0 : goto bail;
135 : }
136 :
137 0 : errno = 0;
138 0 : rc = sscanf(buf, "%lld", &pid_read);
139 :
140 0 : if (rc > 0) {
141 0 : if (pid_read <= 0) {
142 0 : rc = ESRCH;
143 : } else {
144 0 : rc = pcmk_rc_ok;
145 0 : *pid = (pid_t) pid_read;
146 0 : crm_trace("Read pid %lld from %s", pid_read, filename);
147 : }
148 0 : } else if (rc == 0) {
149 0 : rc = ENODATA;
150 : } else {
151 0 : rc = errno;
152 : }
153 :
154 0 : bail:
155 0 : close(fd);
156 0 : return rc;
157 : }
158 :
159 : /*!
160 : * \internal
161 : * \brief Check whether a process from a PID file matches expected values
162 : *
163 : * \param[in] filename Path of PID file
164 : * \param[in] expected_pid If positive, compare to this PID
165 : * \param[in] expected_name If not NULL, the PID from the PID file is valid
166 : * only if it is active as a process with this name
167 : * \param[out] pid If not NULL, store PID found in PID file here
168 : *
169 : * \return Standard Pacemaker return code
170 : */
171 : int
172 0 : pcmk__pidfile_matches(const char *filename, pid_t expected_pid,
173 : const char *expected_name, pid_t *pid)
174 : {
175 0 : pid_t pidfile_pid = 0;
176 0 : int rc = pcmk__read_pidfile(filename, &pidfile_pid);
177 :
178 0 : if (pid) {
179 0 : *pid = pidfile_pid;
180 : }
181 :
182 0 : if (rc != pcmk_rc_ok) {
183 : // Error reading PID file or invalid contents
184 0 : unlink(filename);
185 0 : rc = ENOENT;
186 :
187 0 : } else if ((expected_pid > 0) && (pidfile_pid == expected_pid)) {
188 : // PID in file matches what was expected
189 0 : rc = pcmk_rc_ok;
190 :
191 0 : } else if (pcmk__pid_active(pidfile_pid, expected_name) == ESRCH) {
192 : // Contains a stale value
193 0 : unlink(filename);
194 0 : rc = ENOENT;
195 :
196 0 : } else if ((expected_pid > 0) && (pidfile_pid != expected_pid)) {
197 : // Locked by existing process
198 0 : rc = EEXIST;
199 : }
200 :
201 0 : return rc;
202 : }
203 :
204 : /*!
205 : * \internal
206 : * \brief Create a PID file for the current process (if not already existent)
207 : *
208 : * \param[in] filename Name of PID file to create
209 : * \param[in] name Name of current process
210 : *
211 : * \return Standard Pacemaker return code
212 : */
213 : int
214 0 : pcmk__lock_pidfile(const char *filename, const char *name)
215 : {
216 0 : pid_t mypid = getpid();
217 0 : int fd = 0;
218 0 : int rc = 0;
219 : char buf[LOCKSTRLEN + 2];
220 :
221 0 : rc = pcmk__pidfile_matches(filename, 0, name, NULL);
222 0 : if ((rc != pcmk_rc_ok) && (rc != ENOENT)) {
223 : // Locked by existing process
224 0 : return rc;
225 : }
226 :
227 0 : fd = open(filename, O_CREAT | O_WRONLY | O_EXCL, 0644);
228 0 : if (fd < 0) {
229 0 : return errno;
230 : }
231 :
232 0 : snprintf(buf, sizeof(buf), "%*lld\n", LOCKSTRLEN - 1, (long long) mypid);
233 0 : rc = write(fd, buf, LOCKSTRLEN);
234 0 : close(fd);
235 :
236 0 : if (rc != LOCKSTRLEN) {
237 0 : crm_perror(LOG_ERR, "Incomplete write to %s", filename);
238 0 : return errno;
239 : }
240 :
241 0 : rc = pcmk__pidfile_matches(filename, mypid, name, NULL);
242 0 : if (rc != pcmk_rc_ok) {
243 : // Something is really wrong -- maybe I/O error on read back?
244 0 : unlink(filename);
245 : }
246 0 : return rc;
247 : }
|