Fix undefined behaviour in pulseaudio --start.
[platform/upstream/pulseaudio.git] / src / pulsecore / lock-autospawn.c
1 /***
2   This file is part of PulseAudio.
3
4   Copyright 2008 Lennart Poettering
5
6   PulseAudio is free software; you can redistribute it and/or modify
7   it under the terms of the GNU Lesser General Public License as published
8   by the Free Software Foundation; either version 2.1 of the License,
9   or (at your option) any later version.
10
11   PulseAudio 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 License
17   along with PulseAudio; 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 <errno.h>
27 #include <string.h>
28 #include <signal.h>
29
30 #ifdef HAVE_PTHREAD
31 #include <pthread.h>
32 #endif
33
34 #include <pulse/gccmacro.h>
35 #include <pulse/xmalloc.h>
36
37 #include <pulsecore/i18n.h>
38 #include <pulsecore/poll.h>
39 #include <pulsecore/mutex.h>
40 #include <pulsecore/thread.h>
41 #include <pulsecore/core-util.h>
42
43 #include "lock-autospawn.h"
44
45 /* So, why do we have this complex code here with threads and pipes
46  * and stuff? For two reasons: POSIX file locks are per-process, not
47  * per-file descriptor. That means that two contexts within the same
48  * process that try to create the autospawn lock might end up assuming
49  * they both managed to lock the file. And then, POSIX locking
50  * operations are synchronous. If two contexts run from the same event
51  * loop it must be made sure that they do not block each other, but
52  * that the locking operation can happen asynchronously. */
53
54 #define AUTOSPAWN_LOCK "autospawn.lock"
55
56 static pa_mutex *mutex;
57
58 static unsigned n_ref = 0;
59 static int lock_fd = -1;
60 static pa_mutex *lock_fd_mutex = NULL;
61 static pa_thread *thread = NULL;
62 static int pipe_fd[2] = { -1, -1 };
63
64 static enum {
65     STATE_IDLE,
66     STATE_OWNING,
67     STATE_TAKEN,
68     STATE_FAILED
69 } state = STATE_IDLE;
70
71 static void destroy_mutex(void) PA_GCC_DESTRUCTOR;
72
73 static int ref(void) {
74
75     if (n_ref > 0) {
76
77         pa_assert(pipe_fd[0] >= 0);
78         pa_assert(pipe_fd[1] >= 0);
79         pa_assert(lock_fd_mutex);
80
81         n_ref++;
82
83         return 0;
84     }
85
86     pa_assert(!lock_fd_mutex);
87     pa_assert(state == STATE_IDLE);
88     pa_assert(lock_fd < 0);
89     pa_assert(!thread);
90     pa_assert(pipe_fd[0] < 0);
91     pa_assert(pipe_fd[1] < 0);
92
93     if (pa_pipe_cloexec(pipe_fd) < 0)
94         return -1;
95
96     pa_make_fd_nonblock(pipe_fd[1]);
97     pa_make_fd_nonblock(pipe_fd[0]);
98
99     lock_fd_mutex = pa_mutex_new(false, false);
100
101     n_ref = 1;
102     return 0;
103 }
104
105 static void unref(bool after_fork) {
106
107     pa_assert(n_ref > 0);
108     pa_assert(pipe_fd[0] >= 0);
109     pa_assert(pipe_fd[1] >= 0);
110     pa_assert(lock_fd_mutex);
111
112     n_ref--;
113
114     if (n_ref > 0)
115         return;
116
117     /* Join threads only in the process the new thread was created in
118      * to avoid undefined behaviour.
119      * POSIX.1-2008 XSH 2.9.2 Thread IDs: "applications should only assume
120      * that thread IDs are usable and unique within a single process." */
121     if (thread) {
122         if (after_fork)
123             pa_thread_free_nojoin(thread);
124         else
125             pa_thread_free(thread);
126         thread = NULL;
127     }
128
129     pa_mutex_lock(lock_fd_mutex);
130
131     pa_assert(state != STATE_TAKEN);
132
133     if (state == STATE_OWNING) {
134
135         pa_assert(lock_fd >= 0);
136
137         if (after_fork)
138             pa_close(lock_fd);
139         else {
140             char *lf;
141
142             if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK)))
143                 pa_log_warn(_("Cannot access autospawn lock."));
144
145             pa_unlock_lockfile(lf, lock_fd);
146             pa_xfree(lf);
147         }
148     }
149
150     lock_fd = -1;
151     state = STATE_IDLE;
152
153     pa_mutex_unlock(lock_fd_mutex);
154
155     pa_mutex_free(lock_fd_mutex);
156     lock_fd_mutex = NULL;
157
158     pa_close(pipe_fd[0]);
159     pa_close(pipe_fd[1]);
160     pipe_fd[0] = pipe_fd[1] = -1;
161 }
162
163 static void ping(void) {
164     ssize_t s;
165
166     pa_assert(pipe_fd[1] >= 0);
167
168     for (;;) {
169         char x = 'x';
170
171         if ((s = pa_write(pipe_fd[1], &x, 1, NULL)) == 1)
172             break;
173
174         pa_assert(s < 0);
175
176         if (errno == EAGAIN)
177             break;
178
179         pa_assert(errno == EINTR);
180     }
181 }
182
183 static void wait_for_ping(void) {
184     ssize_t s;
185     char x;
186     struct pollfd pfd;
187     int k;
188
189     pa_assert(pipe_fd[0] >= 0);
190
191     memset(&pfd, 0, sizeof(pfd));
192     pfd.fd = pipe_fd[0];
193     pfd.events = POLLIN;
194
195     if ((k = pa_poll(&pfd, 1, -1)) != 1) {
196         pa_assert(k < 0);
197         pa_assert(errno == EINTR);
198     } else if ((s = pa_read(pipe_fd[0], &x, 1, NULL)) != 1) {
199         pa_assert(s < 0);
200         pa_assert(errno == EAGAIN);
201     }
202 }
203
204 static void empty_pipe(void) {
205     char x[16];
206     ssize_t s;
207
208     pa_assert(pipe_fd[0] >= 0);
209
210     if ((s = pa_read(pipe_fd[0], &x, sizeof(x), NULL)) < 1) {
211         pa_assert(s < 0);
212         pa_assert(errno == EAGAIN);
213     }
214 }
215
216 static void thread_func(void *u) {
217     int fd;
218     char *lf;
219
220 #ifdef HAVE_PTHREAD
221     sigset_t fullset;
222
223     /* No signals in this thread please */
224     sigfillset(&fullset);
225     pthread_sigmask(SIG_BLOCK, &fullset, NULL);
226 #endif
227
228     if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK))) {
229         pa_log_warn(_("Cannot access autospawn lock."));
230         goto fail;
231     }
232
233     if ((fd = pa_lock_lockfile(lf)) < 0)
234         goto fail;
235
236     pa_mutex_lock(lock_fd_mutex);
237     pa_assert(state == STATE_IDLE);
238     lock_fd = fd;
239     state = STATE_OWNING;
240     pa_mutex_unlock(lock_fd_mutex);
241
242     goto finish;
243
244 fail:
245     pa_mutex_lock(lock_fd_mutex);
246     pa_assert(state == STATE_IDLE);
247     state = STATE_FAILED;
248     pa_mutex_unlock(lock_fd_mutex);
249
250 finish:
251     pa_xfree(lf);
252
253     ping();
254 }
255
256 static int start_thread(void) {
257
258     if (!thread)
259         if (!(thread = pa_thread_new("autospawn", thread_func, NULL)))
260             return -1;
261
262     return 0;
263 }
264
265 static void create_mutex(void) {
266     PA_ONCE_BEGIN {
267         mutex = pa_mutex_new(false, false);
268     } PA_ONCE_END;
269 }
270
271 static void destroy_mutex(void) {
272     if (mutex)
273         pa_mutex_free(mutex);
274 }
275
276 int pa_autospawn_lock_init(void) {
277     int ret = -1;
278
279     create_mutex();
280     pa_mutex_lock(mutex);
281
282     if (ref() < 0)
283         ret = -1;
284     else
285         ret = pipe_fd[0];
286
287     pa_mutex_unlock(mutex);
288
289     return ret;
290 }
291
292 int pa_autospawn_lock_acquire(bool block) {
293     int ret = -1;
294
295     create_mutex();
296     pa_mutex_lock(mutex);
297     pa_assert(n_ref >= 1);
298
299     pa_mutex_lock(lock_fd_mutex);
300
301     for (;;) {
302
303         empty_pipe();
304
305         if (state == STATE_OWNING) {
306             state = STATE_TAKEN;
307             ret = 1;
308             break;
309         }
310
311         if (state == STATE_FAILED) {
312             ret = -1;
313             break;
314         }
315
316         if (state == STATE_IDLE)
317             if (start_thread() < 0)
318                 break;
319
320         if (!block) {
321             ret = 0;
322             break;
323         }
324
325         pa_mutex_unlock(lock_fd_mutex);
326         pa_mutex_unlock(mutex);
327
328         wait_for_ping();
329
330         pa_mutex_lock(mutex);
331         pa_mutex_lock(lock_fd_mutex);
332     }
333
334     pa_mutex_unlock(lock_fd_mutex);
335
336     pa_mutex_unlock(mutex);
337
338     return ret;
339 }
340
341 void pa_autospawn_lock_release(void) {
342
343     create_mutex();
344     pa_mutex_lock(mutex);
345     pa_assert(n_ref >= 1);
346
347     pa_assert(state == STATE_TAKEN);
348     state = STATE_OWNING;
349
350     ping();
351
352     pa_mutex_unlock(mutex);
353 }
354
355 void pa_autospawn_lock_done(bool after_fork) {
356
357     create_mutex();
358     pa_mutex_lock(mutex);
359     pa_assert(n_ref >= 1);
360
361     unref(after_fork);
362
363     pa_mutex_unlock(mutex);
364 }