1: /* Functions for dealing with PID files (client side)
2: *
3: * Copyright (c) 2009-2015 Joachim Nilsson <troglobit@gmail.com>
4: *
5: * Permission to use, copy, modify, and/or distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: */
17:
18: #include <errno.h>
19: #include <stdio.h>
20: #include <stdlib.h>
21: #include <signal.h>
22: #include <unistd.h>
23:
24: extern char *__pidfile_name;
25: extern char *chomp(char *str);
26:
27: /**
28: * pidfile_read - Reads a PID value from a pidfile.
29: * @pidfile: File containing PID, usually in /var/run/<PROC>.pid
30: *
31: * This function takes a @pidfile and returns the PID found therein.
32: *
33: * Returns:
34: * On invalid @pidfile -1 and errno set to %EINVAL, when @pidfile does not exist -1
35: * and errno set to %ENOENT. When the pidfile is empty or when its contents cannot
36: * be translated this function returns zero (0), on success this function returns
37: * a PID value greater than one. PID 1 is reserved for the system init process.
38: */
39: pid_t pidfile_read(const char *pidfile)
40: {
41: int pid = 0;
42: char buf[16];
43: FILE *fp;
44:
45: if (!pidfile) {
46: errno = EINVAL;
47: return -1;
48: }
49:
50: fp = fopen(pidfile, "r");
51: if (!fp)
52: return -1;
53:
54: if (fgets(buf, sizeof(buf), fp)) {
55: char *ptr = chomp(buf);
56:
57: if (ptr) {
58: errno = 0;
59: pid = strtoul(ptr, NULL, 0);
60: if (errno)
61: pid = 0; /* Failed conversion. */
62: }
63: }
64: fclose(fp);
65:
66: return pid;
67: }
68:
69: /**
70: * pidfile_poll - Poll for the existence of a pidfile and return PID
71: * @pidfile: Path to pidfile to poll for
72: *
73: * This function polls for the pidfile at @pidfile for at most 5 seconds
74: * before timing out. If the file is created within that time span the
75: * file is read and its PID contents returned.
76: *
77: * Returns:
78: * The PID read from @pidfile, or zero on timeout.
79: */
80: pid_t pidfile_poll(const char *pidfile)
81: {
82: pid_t pid = 0;
83: int tries = 0;
84:
85: /* Timeout = 100 * 50ms = 5s */
86: while ((pid = pidfile_read(pidfile)) <= 0 && tries++ < 100)
87: usleep(50000); /* Wait 50ms between retries */
88:
89: if (pid < 0)
90: pid = 0;
91:
92: return pid;
93: }
94:
95: /**
96: * pidfile_signal - Send signal to a PID and cleanup pidfile afterwards
97: * @pidfile: File containing PID, usually in /var/run/<PROC>.pid
98: * @signal: Signal to send to PID found in @pidfile.
99: *
100: * If @signal is any of %SIGTERM, %SIGKILL, or if kill() returns -1 the
101: * @pidfile is removed.
102: *
103: * Returns:
104: * POSIX OK(0) on success, non-zero otherwise.
105: */
106: int pidfile_signal(const char *pidfile, int signal)
107: {
108: int pid = -1, ret = -1;
109:
110: pid = pidfile_read(pidfile);
111: if (pid <= 0)
112: return 1;
113:
114: ret = kill(pid, signal);
115: if ((ret == -1) || (signal == SIGTERM) || (signal == SIGKILL))
116: (void)remove(pidfile);
117:
118: return 0;
119: }
120:
121:
122: /********************************* UNIT TESTS ************************************/
123: #ifdef UNITTEST
124: #include <paths.h>
125: #include "lite.h"
126:
127: extern char *__progname;
128: static char PIDFILE[42];
129:
130: static void sigterm_handler(int UNUSED(signo))
131: {
132: printf("Exiting ...\n");
133: }
134:
135: static void sigalrm_handler(int UNUSED(signo))
136: {
137: printf("Testing pidfile() ...\n");
138: pidfile(NULL);
139:
140: if (!fexist(PIDFILE))
141: err(1, "pidfile() failed creating %s", PIDFILE);
142: }
143:
144: int main(void)
145: {
146: char cmd[80];
147:
148: snprintf(PIDFILE, sizeof(PIDFILE), "%s%s.pid", _PATH_VARRUN, __progname);
149:
150: signal(SIGTERM, sigterm_handler);
151: signal(SIGALRM, sigalrm_handler);
152: alarm(1);
153:
154: printf("Testing pidfile_poll() ...\n");
155: if (!pidfile_poll(PIDFILE))
156: printf("Timed out!\n");
157: else
158: printf("We got signal!\n");
159:
160: printf("Reading pid file, should be %d ...\n", getpid());
161: printf("=> %d\n", pidfile_read(PIDFILE));
162:
163: printf("\nComparing __pidfile_name with our guessed PID filename ...\n");
164: printf("strcmp(\"%s\",\n \"%s\") => %s\n\n", __pidfile_name, PIDFILE,
165: !strcmp(__pidfile_name, PIDFILE) ? "OK" : "FAIL");
166:
167: /* Occular verification that calling pidfile() again updates mtime */
168: snprintf(cmd, sizeof(cmd), "ls -l --full-time %s", PIDFILE);
169: system(cmd);
170: printf("Before ^^ pidfile() vv after ...\n");
171: pidfile(NULL);
172: system(cmd);
173:
174: printf("Signalling ourselves ...\n");
175:
176: return pidfile_signal(PIDFILE, SIGTERM);
177: }
178: #endif /* UTEST_PIDFN */
179:
180: /**
181: * Local Variables:
182: * compile-command: "make V=1 -f pidfilefn.mk"
183: * version-control: t
184: * indent-tabs-mode: t
185: * c-file-style: "linux"
186: * End:
187: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>