1/***
2  This file is part of PulseAudio.
3
4  Copyright 2004-2008 Lennart Poettering
5  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
7  PulseAudio is free software; you can redistribute it and/or modify
8  it under the terms of the GNU Lesser General Public License as published
9  by the Free Software Foundation; either version 2.1 of the License,
10  or (at your option) any later version.
11
12  PulseAudio is distributed in the hope that it will be useful, but
13  WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  General Public License for more details.
16
17  You should have received a copy of the GNU Lesser General Public License
18  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
19***/
20
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
25#include <stdio.h>
26#include <signal.h>
27#include <errno.h>
28#include <stdlib.h>
29#include <string.h>
30#include <unistd.h>
31
32#ifdef HAVE_WINDOWS_H
33#include <windows.h>
34#endif
35
36#include <pulse/xmalloc.h>
37
38#include <pulsecore/core-error.h>
39#include <pulsecore/core-util.h>
40#include <pulsecore/i18n.h>
41#include <pulsecore/log.h>
42#include <pulsecore/macro.h>
43
44#include "mainloop-signal.h"
45
46struct pa_signal_event {
47    int sig;
48#ifdef HAVE_SIGACTION
49    struct sigaction saved_sigaction;
50#else
51    void (*saved_handler)(int sig);
52#endif
53    void *userdata;
54    pa_signal_cb_t callback;
55    pa_signal_destroy_cb_t destroy_callback;
56    pa_signal_event *previous, *next;
57};
58
59static pa_mainloop_api *api = NULL;
60static int signal_pipe[2] = { -1, -1 };
61static pa_io_event* io_event = NULL;
62static pa_signal_event *signals = NULL;
63
64static void signal_handler(int sig) {
65    int saved_errno;
66
67    saved_errno = errno;
68
69#ifndef HAVE_SIGACTION
70    signal(sig, signal_handler);
71#endif
72
73    /* XXX: If writing fails, there's nothing we can do? */
74    (void) pa_write(signal_pipe[1], &sig, sizeof(sig), NULL);
75
76    errno = saved_errno;
77}
78
79static void dispatch(pa_mainloop_api*a, int sig) {
80    pa_signal_event *s;
81
82    for (s = signals; s; s = s->next)
83        if (s->sig == sig) {
84            pa_assert(s->callback);
85            s->callback(a, s, sig, s->userdata);
86            break;
87        }
88}
89
90static void callback(pa_mainloop_api*a, pa_io_event*e, int fd, pa_io_event_flags_t f, void *userdata) {
91    ssize_t r;
92    int sig;
93
94    pa_assert(a);
95    pa_assert(e);
96    pa_assert(f == PA_IO_EVENT_INPUT);
97    pa_assert(e == io_event);
98    pa_assert(fd == signal_pipe[0]);
99
100    if ((r = pa_read(signal_pipe[0], &sig, sizeof(sig), NULL)) < 0) {
101        if (errno == EAGAIN)
102            return;
103
104        pa_log("read(): %s", pa_cstrerror(errno));
105        return;
106    }
107
108    if (r != sizeof(sig)) {
109        pa_log("short read()");
110        return;
111    }
112
113    dispatch(a, sig);
114}
115
116int pa_signal_init(pa_mainloop_api *a) {
117
118    pa_assert(a);
119    pa_assert(!api);
120    pa_assert(signal_pipe[0] == -1);
121    pa_assert(signal_pipe[1] == -1);
122    pa_assert(!io_event);
123
124    if (pa_pipe_cloexec(signal_pipe) < 0) {
125        pa_log("pipe(): %s", pa_cstrerror(errno));
126        return -1;
127    }
128
129    pa_make_fd_nonblock(signal_pipe[0]);
130    pa_make_fd_nonblock(signal_pipe[1]);
131
132    api = a;
133
134    pa_assert_se(io_event = api->io_new(api, signal_pipe[0], PA_IO_EVENT_INPUT, callback, NULL));
135
136    return 0;
137}
138
139void pa_signal_done(void) {
140    while (signals)
141        pa_signal_free(signals);
142
143    if (io_event) {
144        pa_assert(api);
145        api->io_free(io_event);
146        io_event = NULL;
147    }
148
149    pa_close_pipe(signal_pipe);
150
151    api = NULL;
152}
153
154pa_signal_event* pa_signal_new(int sig, pa_signal_cb_t _callback, void *userdata) {
155    pa_signal_event *e = NULL;
156
157#ifdef HAVE_SIGACTION
158    struct sigaction sa;
159#endif
160
161    pa_assert(sig > 0);
162    pa_assert(_callback);
163
164    pa_init_i18n();
165
166    for (e = signals; e; e = e->next)
167        if (e->sig == sig)
168            return NULL;
169
170    e = pa_xnew(pa_signal_event, 1);
171    e->sig = sig;
172    e->callback = _callback;
173    e->userdata = userdata;
174    e->destroy_callback = NULL;
175
176#ifdef HAVE_SIGACTION
177    memset(&sa, 0, sizeof(sa));
178    sa.sa_handler = signal_handler;
179    sigemptyset(&sa.sa_mask);
180    sa.sa_flags = SA_RESTART;
181
182    if (sigaction(sig, &sa, &e->saved_sigaction) < 0)
183#else
184    if ((e->saved_handler = signal(sig, signal_handler)) == SIG_ERR)
185#endif
186        goto fail;
187
188    e->previous = NULL;
189    e->next = signals;
190    signals = e;
191
192    return e;
193fail:
194    pa_xfree(e);
195    return NULL;
196}
197
198void pa_signal_free(pa_signal_event *e) {
199    pa_assert(e);
200
201    if (e->next)
202        e->next->previous = e->previous;
203    if (e->previous)
204        e->previous->next = e->next;
205    else
206        signals = e->next;
207
208#ifdef HAVE_SIGACTION
209    pa_assert_se(sigaction(e->sig, &e->saved_sigaction, NULL) == 0);
210#else
211    pa_assert_se(signal(e->sig, e->saved_handler) == signal_handler);
212#endif
213
214    if (e->destroy_callback)
215        e->destroy_callback(api, e, e->userdata);
216
217    pa_xfree(e);
218}
219
220void pa_signal_set_destroy(pa_signal_event *e, pa_signal_destroy_cb_t _callback) {
221    pa_assert(e);
222
223    e->destroy_callback = _callback;
224}
225