source: mainline/uspace/lib/posix/source/signal.c@ 10de842

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 10de842 was 193d280c, checked in by Martin Decky <martin@…>, 10 years ago

cstyle improvements
replace traditional K&R-style function declarations and definitions

  • Property mode set to 100644
File size: 13.7 KB
Line 
1/*
2 * Copyright (c) 2011 Jiri Zarevucky
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libposix
30 * @{
31 */
32/** @file Signal handling.
33 */
34
35#define LIBPOSIX_INTERNAL
36#define __POSIX_DEF__(x) posix_##x
37
38#include "posix/signal.h"
39#include "internal/common.h"
40#include "posix/limits.h"
41#include "posix/stdlib.h"
42#include "posix/string.h"
43#include "posix/errno.h"
44
45#include "libc/fibril_synch.h"
46#include "libc/task.h"
47
48/* This file implements a fairly dumb and incomplete "simulation" of
49 * POSIX signals. Since HelenOS doesn't support signals and mostly doesn't
50 * have any equivalent functionality, most of the signals are useless. The
51 * main purpose of this implementation is thus to help port applications using
52 * signals with minimal modification, but if the application uses signals for
53 * anything non-trivial, it's quite probable it won't work properly even if
54 * it builds without problems.
55 */
56
57/* Used to serialize signal handling. */
58static FIBRIL_MUTEX_INITIALIZE(_signal_mutex);
59
60static LIST_INITIALIZE(_signal_queue);
61
62static posix_sigset_t _signal_mask = 0;
63
64#define DEFAULT_HANDLER { .sa_handler = SIG_DFL, \
65 .sa_mask = 0, .sa_flags = 0, .sa_sigaction = NULL }
66
67/* Actions associated with each signal number. */
68static struct posix_sigaction _signal_actions[_TOP_SIGNAL + 1] = {
69 DEFAULT_HANDLER, DEFAULT_HANDLER, DEFAULT_HANDLER, DEFAULT_HANDLER,
70 DEFAULT_HANDLER, DEFAULT_HANDLER, DEFAULT_HANDLER, DEFAULT_HANDLER,
71 DEFAULT_HANDLER, DEFAULT_HANDLER, DEFAULT_HANDLER, DEFAULT_HANDLER,
72 DEFAULT_HANDLER, DEFAULT_HANDLER, DEFAULT_HANDLER, DEFAULT_HANDLER,
73 DEFAULT_HANDLER, DEFAULT_HANDLER, DEFAULT_HANDLER, DEFAULT_HANDLER,
74 DEFAULT_HANDLER, DEFAULT_HANDLER, DEFAULT_HANDLER, DEFAULT_HANDLER,
75 DEFAULT_HANDLER, DEFAULT_HANDLER, DEFAULT_HANDLER, DEFAULT_HANDLER
76};
77
78/**
79 * Default signal handler. Executes the default action for each signal,
80 * as reasonable within HelenOS.
81 *
82 * @param signo Signal number.
83 */
84void __posix_default_signal_handler(int signo)
85{
86 switch (signo) {
87 case SIGABRT:
88 abort();
89 case SIGQUIT:
90 fprintf(stderr, "Quit signal raised. Exiting.\n");
91 exit(EXIT_FAILURE);
92 case SIGINT:
93 fprintf(stderr, "Interrupt signal caught. Exiting.\n");
94 exit(EXIT_FAILURE);
95 case SIGTERM:
96 fprintf(stderr, "Termination signal caught. Exiting.\n");
97 exit(EXIT_FAILURE);
98 case SIGSTOP:
99 fprintf(stderr, "Stop signal caught, but unsupported. Ignoring.\n");
100 break;
101 case SIGKILL:
102 /* This will only occur when raise or similar is called. */
103 /* Commit suicide. */
104 task_kill(task_get_id());
105
106 /* Should not be reached. */
107 abort();
108 case SIGFPE:
109 case SIGBUS:
110 case SIGILL:
111 case SIGSEGV:
112 posix_psignal(signo, "Hardware exception raised by user code");
113 abort();
114 case SIGSYS:
115 case SIGXCPU:
116 case SIGXFSZ:
117 case SIGTRAP:
118 case SIGHUP:
119 case SIGPIPE:
120 case SIGPOLL:
121 case SIGURG:
122 case SIGTSTP:
123 case SIGTTIN:
124 case SIGTTOU:
125 posix_psignal(signo, "Unsupported signal caught");
126 abort();
127 case SIGCHLD:
128 case SIGUSR1:
129 case SIGUSR2:
130 case SIGALRM:
131 case SIGVTALRM:
132 case SIGPROF:
133 case SIGCONT:
134 /* ignored */
135 break;
136 }
137}
138
139/**
140 * Just an empty function to get an unique pointer value for comparison.
141 *
142 * @param signo Signal number.
143 */
144void __posix_hold_signal_handler(int signo)
145{
146 /* Nothing */
147}
148
149/**
150 * Empty function to be used as ignoring handler.
151 *
152 * @param signo Signal number.
153 */
154void __posix_ignore_signal_handler(int signo)
155{
156 /* Nothing */
157}
158
159/**
160 * Clear the signal set.
161 *
162 * @param set Pointer to the signal set.
163 * @return Always returns zero.
164 */
165int posix_sigemptyset(posix_sigset_t *set)
166{
167 assert(set != NULL);
168
169 *set = 0;
170 return 0;
171}
172
173/**
174 * Fill the signal set (i.e. add all signals).
175 *
176 * @param set Pointer to the signal set.
177 * @return Always returns zero.
178 */
179int posix_sigfillset(posix_sigset_t *set)
180{
181 assert(set != NULL);
182
183 *set = UINT32_MAX;
184 return 0;
185}
186
187/**
188 * Add a signal to the set.
189 *
190 * @param set Pointer to the signal set.
191 * @param signo Signal number to add.
192 * @return Always returns zero.
193 */
194int posix_sigaddset(posix_sigset_t *set, int signo)
195{
196 assert(set != NULL);
197
198 *set |= (1 << signo);
199 return 0;
200}
201
202/**
203 * Delete a signal from the set.
204 *
205 * @param set Pointer to the signal set.
206 * @param signo Signal number to remove.
207 * @return Always returns zero.
208 */
209int posix_sigdelset(posix_sigset_t *set, int signo)
210{
211 assert(set != NULL);
212
213 *set &= ~(1 << signo);
214 return 0;
215}
216
217/**
218 * Inclusion test for a signal set.
219 *
220 * @param set Pointer to the signal set.
221 * @param signo Signal number to query.
222 * @return 1 if the signal is in the set, 0 otherwise.
223 */
224int posix_sigismember(const posix_sigset_t *set, int signo)
225{
226 assert(set != NULL);
227
228 return (*set & (1 << signo)) != 0;
229}
230
231/**
232 * Unsafe variant of the sigaction() function.
233 * Doesn't do any checking of its arguments and
234 * does not deal with thread-safety.
235 *
236 * @param sig
237 * @param act
238 * @param oact
239 */
240static void _sigaction_unsafe(int sig, const struct posix_sigaction *restrict act,
241 struct posix_sigaction *restrict oact)
242{
243 if (oact != NULL) {
244 memcpy(oact, &_signal_actions[sig],
245 sizeof(struct posix_sigaction));
246 }
247
248 if (act != NULL) {
249 memcpy(&_signal_actions[sig], act,
250 sizeof(struct posix_sigaction));
251 }
252}
253
254/**
255 * Sets a new action for the given signal number.
256 *
257 * @param sig Signal number to set action for.
258 * @param act If not NULL, contents of this structure are
259 * used as the new action for the signal.
260 * @param oact If not NULL, the original action associated with the signal
261 * is stored in the structure pointer to.
262 * @return -1 with errno set on failure, 0 on success.
263 */
264int posix_sigaction(int sig, const struct posix_sigaction *restrict act,
265 struct posix_sigaction *restrict oact)
266{
267 if (sig > _TOP_SIGNAL || (act != NULL &&
268 (sig == SIGKILL || sig == SIGSTOP))) {
269 errno = EINVAL;
270 return -1;
271 }
272
273 if (sig > _TOP_CATCHABLE_SIGNAL) {
274 posix_psignal(sig,
275 "WARNING: registering handler for a partially"
276 " or fully unsupported signal. This handler may only be"
277 " invoked by the raise() function, which may not be what"
278 " the application developer intended");
279 }
280
281 fibril_mutex_lock(&_signal_mutex);
282 _sigaction_unsafe(sig, act, oact);
283 fibril_mutex_unlock(&_signal_mutex);
284
285 return 0;
286}
287
288/**
289 * Sets a new handler for the given signal number.
290 *
291 * @param sig Signal number to set handler for.
292 * @param func Handler function.
293 * @return SIG_ERR on failure, original handler on success.
294 */
295void (*posix_signal(int sig, void (*func)(int)))(int)
296{
297 struct posix_sigaction new = {
298 .sa_handler = func,
299 .sa_mask = 0,
300 .sa_flags = 0,
301 .sa_sigaction = NULL
302 };
303 struct posix_sigaction old;
304 if (posix_sigaction(sig, func == NULL ? NULL : &new, &old) == 0) {
305 return old.sa_handler;
306 } else {
307 return SIG_ERR;
308 }
309}
310
311typedef struct {
312 link_t link;
313 int signo;
314 posix_siginfo_t siginfo;
315} signal_queue_item;
316
317/**
318 * Queue blocked signal.
319 *
320 * @param signo Signal number.
321 * @param siginfo Additional information about the signal.
322 */
323static void _queue_signal(int signo, posix_siginfo_t *siginfo)
324{
325 assert(signo >= 0 && signo <= _TOP_SIGNAL);
326 assert(siginfo != NULL);
327
328 signal_queue_item *item = malloc(sizeof(signal_queue_item));
329 link_initialize(&(item->link));
330 item->signo = signo;
331 memcpy(&item->siginfo, siginfo, sizeof(posix_siginfo_t));
332 list_append(&(item->link), &_signal_queue);
333}
334
335
336/**
337 * Executes an action associated with the given signal.
338 *
339 * @param signo Signal number.
340 * @param siginfo Additional information about the circumstances of this raise.
341 * @return 0 if the action has been successfully executed. -1 if the signal is
342 * blocked.
343 */
344static int _raise_sigaction(int signo, posix_siginfo_t *siginfo)
345{
346 assert(signo >= 0 && signo <= _TOP_SIGNAL);
347 assert(siginfo != NULL);
348
349 fibril_mutex_lock(&_signal_mutex);
350
351 struct posix_sigaction action = _signal_actions[signo];
352
353 if (posix_sigismember(&_signal_mask, signo) ||
354 action.sa_handler == SIG_HOLD) {
355 _queue_signal(signo, siginfo);
356 fibril_mutex_unlock(&_signal_mutex);
357 return -1;
358 }
359
360 /* Modifying signal mask is unnecessary,
361 * signal handling is serialized.
362 */
363
364 if ((action.sa_flags & SA_RESETHAND) && signo != SIGILL && signo != SIGTRAP) {
365 _signal_actions[signo] = (struct posix_sigaction) DEFAULT_HANDLER;
366 }
367
368 if (action.sa_flags & SA_SIGINFO) {
369 assert(action.sa_sigaction != NULL);
370 action.sa_sigaction(signo, siginfo, NULL);
371 } else {
372 assert(action.sa_handler != NULL);
373 action.sa_handler(signo);
374 }
375
376 fibril_mutex_unlock(&_signal_mutex);
377
378 return 0;
379}
380
381/**
382 * Raise all unblocked previously queued signals.
383 */
384static void _dequeue_unblocked_signals(void)
385{
386 link_t *iterator = _signal_queue.head.next;
387 link_t *next;
388
389 while (iterator != &(_signal_queue).head) {
390 next = iterator->next;
391
392 signal_queue_item *item =
393 list_get_instance(iterator, signal_queue_item, link);
394
395 if (!posix_sigismember(&_signal_mask, item->signo) &&
396 _signal_actions[item->signo].sa_handler != SIG_HOLD) {
397 list_remove(&(item->link));
398 _raise_sigaction(item->signo, &(item->siginfo));
399 free(item);
400 }
401
402 iterator = next;
403 }
404}
405
406/**
407 * Raise a signal for the calling process.
408 *
409 * @param sig Signal number.
410 * @return -1 with errno set on failure, 0 on success.
411 */
412int posix_raise(int sig)
413{
414 if (sig >= 0 && sig <= _TOP_SIGNAL) {
415 posix_siginfo_t siginfo = {
416 .si_signo = sig,
417 .si_code = SI_USER
418 };
419 return _raise_sigaction(sig, &siginfo);
420 } else {
421 errno = EINVAL;
422 return -1;
423 }
424}
425
426/**
427 * Raises a signal for a selected process.
428 *
429 * @param pid PID of the process for which the signal shall be raised.
430 * @param signo Signal to raise.
431 * @return -1 with errno set on failure (possible errors include unsupported
432 * action, invalid signal number, lack of permissions, etc.), 0 on success.
433 */
434int posix_kill(posix_pid_t pid, int signo)
435{
436 if (pid < 1) {
437 // TODO
438 errno = ENOTSUP;
439 return -1;
440 }
441
442 if (signo > _TOP_SIGNAL) {
443 errno = EINVAL;
444 return -1;
445 }
446
447 if (pid == (posix_pid_t) task_get_id()) {
448 return posix_raise(signo);
449 }
450
451 switch (signo) {
452 case SIGKILL:
453 task_kill(pid);
454 break;
455 default:
456 /* Nothing else supported yet. */
457 errno = ENOTSUP;
458 return -1;
459 }
460
461 return 0;
462}
463
464/**
465 * Send a signal to a process group. Always fails at the moment because of
466 * lack of this functionality in HelenOS.
467 *
468 * @param pid PID of the process group.
469 * @param sig Signal number.
470 * @return -1 on failure, 0 on success (see kill()).
471 */
472int posix_killpg(posix_pid_t pid, int sig)
473{
474 assert(pid > 1);
475 return posix_kill(-pid, sig);
476}
477
478/**
479 * Outputs information about the signal to the standard error stream.
480 *
481 * @param pinfo SigInfo struct to write.
482 * @param message String to output alongside human-readable signal description.
483 */
484void posix_psiginfo(const posix_siginfo_t *pinfo, const char *message)
485{
486 assert(pinfo != NULL);
487 posix_psignal(pinfo->si_signo, message);
488 // TODO: print si_code
489}
490
491/**
492 * Outputs information about the signal to the standard error stream.
493 *
494 * @param signum Signal number.
495 * @param message String to output alongside human-readable signal description.
496 */
497void posix_psignal(int signum, const char *message)
498{
499 char *sigmsg = posix_strsignal(signum);
500 if (message == NULL || *message == '\0') {
501 fprintf(stderr, "%s\n", sigmsg);
502 } else {
503 fprintf(stderr, "%s: %s\n", message, sigmsg);
504 }
505}
506
507/**
508 * Manipulate the signal mask of the calling thread.
509 *
510 * @param how What to do with the mask.
511 * @param set Signal set to work with.
512 * @param oset If not NULL, the original signal mask is coppied here.
513 * @return 0 success, errorcode on failure.
514 */
515int posix_thread_sigmask(int how, const posix_sigset_t *restrict set,
516 posix_sigset_t *restrict oset)
517{
518 fibril_mutex_lock(&_signal_mutex);
519
520 if (oset != NULL) {
521 *oset = _signal_mask;
522 }
523 if (set != NULL) {
524 switch (how) {
525 case SIG_BLOCK:
526 _signal_mask |= *set;
527 break;
528 case SIG_UNBLOCK:
529 _signal_mask &= ~*set;
530 break;
531 case SIG_SETMASK:
532 _signal_mask = *set;
533 break;
534 default:
535 fibril_mutex_unlock(&_signal_mutex);
536 return EINVAL;
537 }
538 }
539
540 _dequeue_unblocked_signals();
541
542 fibril_mutex_unlock(&_signal_mutex);
543
544 return 0;
545}
546
547/**
548 * Manipulate the signal mask of the process.
549 *
550 * @param how What to do with the mask.
551 * @param set Signal set to work with.
552 * @param oset If not NULL, the original signal mask is coppied here.
553 * @return 0 on success, -1 with errno set on failure.
554 */
555int posix_sigprocmask(int how, const posix_sigset_t *restrict set,
556 posix_sigset_t *restrict oset)
557{
558 int result = posix_thread_sigmask(how, set, oset);
559 if (result != 0) {
560 errno = result;
561 return -1;
562 }
563 return 0;
564}
565
566/** @}
567 */
Note: See TracBrowser for help on using the repository browser.