1 /*------------------------------------------------------------------------------
2 *
3 * Copyright (c) 2011-2021, EURid vzw. All rights reserved.
4 * The YADIFA TM software product is provided under the BSD 3-clause license:
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of EURid nor the names of its contributors may be
16 * used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 *------------------------------------------------------------------------------
32 *
33 */
34
35 /** @defgroup
36 * @ingroup
37 * @brief
38 *
39 *
40 *
41 * @{
42 *
43 *----------------------------------------------------------------------------*/
44
45 #include "dnscore/dnscore-config.h"
46
47 #include <sys/file.h>
48 #include <fcntl.h>
49 #include <ctype.h>
50 #include <signal.h>
51
52 #include "dnscore/pid.h"
53
54 #include "dnscore/sys_types.h"
55 #include "dnscore/logger.h"
56 #include "dnscore/parser.h"
57 #include "dnscore/fdtools.h"
58 #include "dnscore/process.h"
59
60
61 /*------------------------------------------------------------------------------
62 * GLOBAL VARIABLES */
63
64 extern logger_handle *g_system_logger;
65 #define MODULE_MSG_HANDLE g_system_logger
66
67 /*------------------------------------------------------------------------------
68 * STATIC PROTOTYPES */
69
70 /*------------------------------------------------------------------------------
71 * FUNCTIONS */
72
73 /** \brief Read \b pid \b file, program quits on log_quit
74 *
75 * @param[in] path
76 * @param[in] file_name
77 *
78 * @retval pid
79 * @retval NOK (negative number),
80 * @return otherwise log_quit will stop the program with correct exit code
81 */
82 pid_t
pid_file_read(const char * pid_file_path)83 pid_file_read(const char *pid_file_path)
84 {
85 ssize_t received;
86 int fd;
87 char *p;
88 u32 pid;
89 ya_result ret;
90 char buffer[8 + 1];
91
92 /* ------------------------------------------------------------ */
93
94 yassert(pid_file_path != NULL);
95
96 if(strlen(pid_file_path) > PATH_MAX)
97 {
98 log_err("pid file '%s': path is bigger than %i", pid_file_path, PATH_MAX);
99
100 return INVALID_PATH;
101 }
102
103 if(FAIL(fd = open_ex(pid_file_path, O_RDONLY|O_CLOEXEC)))
104 {
105 ret = ERRNO_ERROR;
106
107 log_debug("pid file '%s': cannot open: %r", pid_file_path, ret);
108
109 return ret; /* no file found : not running assumed */
110 }
111
112 received = readfully(fd, buffer, sizeof(buffer) - 1);
113
114 if(0 >= received)
115 {
116 if(received < 0)
117 {
118 ret = received;
119 log_err("pid file '%s': cannot read pid: %r", pid_file_path, ret);
120 }
121 else
122 {
123 ret = UNEXPECTED_EOF;
124 }
125
126 return ret;
127 }
128
129 buffer[received] = '\0'; /* Append a terminator for strlen */
130
131 p = buffer;
132 while(isdigit(*p)!=0) p++; /* Cut after the first character that is not a digit (ie: CR LF ...) */
133 *p = '\0';
134
135 if(FAIL(ret = parse_u32_check_range(buffer, &pid, 0, MAX_S32, BASE_10)))
136 {
137 log_err("pid file '%s': invalid pid number: %r", pid_file_path, ret);
138
139 return ret;
140 }
141
142 close_ex(fd); /* close the pid file */
143
144 return (pid_t)pid;
145 }
146
147 /** \brief Create or overwrite the \b pid \b file with its new process id
148 *
149 * @param[in] config is a config_data structure
150 *
151 * @retval OK
152 * @retval YDF_ERROR_CHOWN if can not "chown"
153 * @return otherwise log_quit will stop the program with correct exit code
154 */
155 ya_result
pid_file_create(pid_t * pid,const char * pid_file_path,uid_t new_uid,gid_t new_gid)156 pid_file_create(pid_t *pid, const char *pid_file_path, uid_t new_uid, gid_t new_gid)
157 {
158 ya_result ret;
159 int fd;
160 mode_t permissions = 0644;
161 #ifndef WIN32
162 uid_t uid = getuid();
163 #endif
164 char buffer[16];
165 pid_t pid_tmp;
166
167 if(pid == NULL)
168 {
169 pid = &pid_tmp;
170 }
171
172 /* ------------------------------------------------------------ */
173
174 yassert(pid_file_path != NULL);
175
176 if(strlen(pid_file_path) > PATH_MAX)
177 {
178 log_err("pid file '%s': path is bigger than %i", pid_file_path, PATH_MAX);
179
180 return INVALID_PATH;
181 }
182
183 *pid = getpid_ex();
184 int buffer_len = snprintf(buffer, sizeof(buffer), "%d\n", *pid); // VS complains for something that's Windows specific and wrong at the moment anyhow.
185
186 yassert(buffer_len > 0);
187
188 fd = open_create_ex(pid_file_path, O_WRONLY | O_CREAT | O_TRUNC, permissions);
189
190 if(fd >= 0)
191 {
192 for(;;)
193 {
194 #ifndef WIN32
195 if(flock(fd, LOCK_EX|LOCK_NB) < 0)
196 {
197 ret = errno;
198 if(ret == EINTR)
199 {
200 continue;
201 }
202
203 if(ret == EWOULDBLOCK)
204 {
205 // already locked
206 close_ex(fd);
207 return PID_LOCKED;
208 }
209
210 abort();
211 }
212 #endif
213
214 break;
215 }
216
217 if(ISOK(ret = pid_check_running_program(pid_file_path, NULL)))
218 {
219 // got the lock
220
221 if(writefully(fd, buffer, buffer_len) > 0)
222 {
223 ret = SUCCESS;
224 #ifndef WIN32
225 if(uid == 0) // only applicable if you are root
226 {
227 if(chown(pid_file_path, new_uid, new_gid) >= 0)
228 {
229 log_debug("pid file '%s': created", pid_file_path);
230 }
231 else
232 {
233 ret = ERRNO_ERROR;
234 log_err("pid file '%s': cannot change owner.group to %i.%i: %r", pid_file_path, new_uid, new_gid, ret);
235 pid_file_destroy(pid_file_path);
236 }
237 }
238 #endif
239 }
240 else
241 {
242 ret = ERRNO_ERROR;
243 log_err("pid file '%s': cannot write pid: %r", pid_file_path, ret);
244 pid_file_destroy(pid_file_path);
245 }
246 }
247 }
248 else
249 {
250 ret = ERRNO_ERROR;
251 log_err("pid file '%s': cannot create: %r", pid_file_path, ret);
252 }
253
254 close_ex(fd);
255
256 return ret;
257 }
258
259 /** \brief Check if program is already running
260 *
261 * @param[in] config is a config_data structure
262 *
263 * @return NONE
264 * @return otherwise log_quit will stop the program with correct exit code
265 */
266 ya_result
pid_check_running_program(const char * pid_file_path,pid_t * out_pid)267 pid_check_running_program(const char *pid_file_path, pid_t *out_pid)
268 {
269 #ifndef WIN32
270 yassert(pid_file_path != NULL);
271 pid_t pid;
272
273 /* ------------------------------------------------------------ */
274
275 yassert(pid_file_path != NULL);
276
277 if(strlen(pid_file_path) > PATH_MAX)
278 {
279 log_err("pid file '%s': path is bigger than %i", pid_file_path, PATH_MAX);
280
281 return INVALID_PATH;
282 }
283
284 if(ISOK(pid = pid_file_read(pid_file_path)))
285 {
286 if((pid != getpid_ex()) && ((kill(pid, 0) == 0) || (errno == EPERM)))
287 {
288 if(out_pid != NULL)
289 {
290 *out_pid = pid;
291 }
292 return PID_LOCKED;
293 }
294 }
295 #endif
296 return SUCCESS;
297 }
298
299 void
pid_file_destroy(const char * pid_file_path)300 pid_file_destroy(const char *pid_file_path)
301 {
302 yassert(pid_file_path != NULL);
303
304 if(strlen(pid_file_path) > PATH_MAX)
305 {
306 log_err("pid file '%s': path is bigger than %i", pid_file_path, PATH_MAX);
307 return;
308 }
309
310 if(FAIL(unlink(pid_file_path)))
311 {
312 int ret = ERRNO_ERROR;
313
314 // don't complain if the file has already been destroyed
315 if(ret != ENOENT)
316 {
317 log_err("pid file '%s': cannot delete: %r", pid_file_path, ret);
318 }
319 }
320 }
321
322 /** @} */
323