change pa_log() and friends to not require a trailing \n on all logged strings
[profile/ivi/pulseaudio.git] / src / polypcore / pid.c
1 /* $Id$ */
2
3 /***
4   This file is part of polypaudio.
5  
6   polypaudio is free software; you can redistribute it and/or modify
7   it under the terms of the GNU Lesser General Public License as
8   published by the Free Software Foundation; either version 2 of the
9   License, or (at your option) any later version.
10  
11   polypaudio is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   General Public License for more details.
15  
16   You should have received a copy of the GNU Lesser General Public
17   License along with polypaudio; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include <errno.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <string.h>
32 #include <stdio.h>
33 #include <assert.h>
34 #include <stdlib.h>
35 #include <limits.h>
36 #include <signal.h>
37
38 #ifdef HAVE_WINDOWS_H
39 #include <windows.h>
40 #endif
41
42 #include <polypcore/util.h>
43 #include <polypcore/log.h>
44
45 #include "pid.h"
46
47 /* Read the PID data from the file descriptor fd, and return it. If no
48  * pid could be read, return 0, on failure (pid_t) -1 */
49 static pid_t read_pid(const char *fn, int fd) {
50     ssize_t r;
51     char t[20], *e;
52     uint32_t pid;
53
54     assert(fn && fd >= 0);
55
56     if ((r = pa_loop_read(fd, t, sizeof(t)-1)) < 0) {
57         pa_log(__FILE__": WARNING: failed to read PID file '%s': %s", fn, strerror(errno));
58         return (pid_t) -1;
59     }
60
61     if (r == 0)
62         return (pid_t) 0;
63     
64     t[r] = 0;
65     if ((e = strchr(t, '\n')))
66         *e = 0;
67
68     if (pa_atou(t, &pid) < 0) {
69         pa_log(__FILE__": WARNING: failed to parse PID file '%s'", fn);
70         return (pid_t) -1;
71     }
72
73     return (pid_t) pid;
74 }
75
76 static int open_pid_file(const char *fn, int mode) {
77     int fd = -1;
78     int lock = -1;
79     
80     for (;;) {
81         struct stat st;
82         
83         pa_make_secure_parent_dir(fn);
84         
85         if ((fd = open(fn, mode, S_IRUSR|S_IWUSR)) < 0) {
86             if (mode != O_RDONLY || errno != ENOENT)
87                 pa_log(__FILE__": WARNING: failed to open PID file '%s': %s", fn, strerror(errno));
88             goto fail;
89         }
90
91         /* Try to lock the file. If that fails, go without */
92         if (pa_lock_fd(fd, 1) < 0)
93             goto fail;
94         
95         if (fstat(fd, &st) < 0) {
96             pa_log(__FILE__": Failed to fstat() PID file '%s': %s", fn, strerror(errno));
97             goto fail;
98         }
99
100         /* Does the file still exist in the file system? When ye, w're done, otherwise restart */
101         if (st.st_nlink >= 1)
102             break;
103
104         if (pa_lock_fd(fd, 0) < 0)
105             goto fail;
106
107         if (close(fd) < 0) {
108             pa_log(__FILE__": Failed to close file '%s': %s", fn, strerror(errno));
109             goto fail;
110         }
111
112         fd = -1;
113     }
114
115     return fd;
116
117 fail:
118
119     if (fd < 0) {
120         if (lock >= 0)
121             pa_lock_fd(fd, 0);
122         
123         close(fd);
124     }
125
126     return -1;
127 }
128
129 /* Create a new PID file for the current process. */
130 int pa_pid_file_create(void) {
131     int fd = -1;
132     int ret = -1;
133     char fn[PATH_MAX];
134     char t[20];
135     pid_t pid;
136     size_t l;
137
138 #ifdef OS_IS_WIN32
139     HANDLE process;
140 #endif
141
142     pa_runtime_path("pid", fn, sizeof(fn));
143
144     if ((fd = open_pid_file(fn, O_CREAT|O_RDWR)) < 0)
145         goto fail;
146
147     if ((pid = read_pid(fn, fd)) == (pid_t) -1)
148         pa_log(__FILE__": corrupt PID file, overwriting.");
149     else if (pid > 0) {
150 #ifdef OS_IS_WIN32
151         if ((process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid)) != NULL) {
152             CloseHandle(process);
153 #else
154         if (kill(pid, 0) >= 0 || errno != ESRCH) {
155 #endif
156             pa_log(__FILE__": daemon already running.");
157             goto fail;
158         }
159
160         pa_log(__FILE__": stale PID file, overwriting.");
161     }
162
163     /* Overwrite the current PID file */
164     if (lseek(fd, 0, SEEK_SET) == (off_t) -1 || ftruncate(fd, 0) < 0) {
165         pa_log(__FILE__": failed to truncate PID fil: %s.", strerror(errno));
166         goto fail;
167     }
168     
169     snprintf(t, sizeof(t), "%lu\n", (unsigned long) getpid());
170     l = strlen(t);
171     
172     if (pa_loop_write(fd, t, l) != (ssize_t) l) {
173         pa_log(__FILE__": failed to write PID file.");
174         goto fail;
175     }
176
177     ret = 0;
178     
179 fail:
180     if (fd >= 0) {
181         pa_lock_fd(fd, 0);
182         close(fd);
183     }
184     
185     return ret;
186 }
187
188 /* Remove the PID file, if it is ours */
189 int pa_pid_file_remove(void) {
190     int fd = -1;
191     char fn[PATH_MAX];
192     int ret = -1;
193     pid_t pid;
194
195     pa_runtime_path("pid", fn, sizeof(fn));
196
197     if ((fd = open_pid_file(fn, O_RDWR)) < 0) {
198         pa_log(__FILE__": WARNING: failed to open PID file '%s': %s", fn, strerror(errno));
199         goto fail;
200     }
201
202     if ((pid = read_pid(fn, fd)) == (pid_t) -1)
203         goto fail;
204
205     if (pid != getpid()) {
206         pa_log(__FILE__": WARNING: PID file '%s' not mine!", fn);
207         goto fail;
208     }
209
210     if (ftruncate(fd, 0) < 0) {
211         pa_log(__FILE__": failed to truncate PID file '%s': %s", fn, strerror(errno));
212         goto fail;
213     }
214
215 #ifdef OS_IS_WIN32
216     pa_lock_fd(fd, 0);
217     close(fd);
218     fd = -1;
219 #endif
220
221     if (unlink(fn) < 0) {
222         pa_log(__FILE__": failed to remove PID file '%s': %s", fn, strerror(errno));
223         goto fail;
224     }
225
226     ret = 0;
227     
228 fail:
229
230     if (fd >= 0) {
231         pa_lock_fd(fd, 0);
232         close(fd);
233     }
234
235     return ret;
236 }
237
238 /* Check whether the daemon is currently running, i.e. if a PID file
239  * exists and the PID therein too. Returns 0 on succcess, -1
240  * otherwise. If pid is non-NULL and a running daemon was found,
241  * return its PID therein */
242 int pa_pid_file_check_running(pid_t *pid) {
243     return pa_pid_file_kill(0, pid);
244 }
245
246 #ifndef OS_IS_WIN32
247
248 /* Kill a current running daemon. Return non-zero on success, -1
249  * otherwise. If successful *pid contains the PID of the daemon
250  * process. */
251 int pa_pid_file_kill(int sig, pid_t *pid) {
252     int fd = -1;
253     char fn[PATH_MAX];
254     int ret = -1;
255     pid_t _pid;
256
257     if (!pid)
258         pid = &_pid;
259     
260     pa_runtime_path("pid", fn, sizeof(fn));
261     
262     if ((fd = open_pid_file(fn, O_RDONLY)) < 0)
263         goto fail;
264     
265     if ((*pid = read_pid(fn, fd)) == (pid_t) -1)
266         goto fail;
267
268     ret = kill(*pid, sig);
269     
270 fail:
271     
272     if (fd >= 0) {
273         pa_lock_fd(fd, 0);
274         close(fd);
275     }
276
277     return ret;
278     
279 }
280
281 #else /* OS_IS_WIN32 */
282
283 int pa_pid_file_kill(int sig, pid_t *pid) {
284     return -1;
285 }
286
287 #endif