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