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