source: mainline/uspace/lib/posix/src/signal.c

Last change on this file was 9b8be79, checked in by Jiří Zárevúcky <jiri.zarevucky@…>, 7 years ago

libposix: Change header organization and remove passthrough headers

Posix headers now function like an overlay. The system include directories
are searched after posix directories. The headers don't need to be patched
for export now. libposix files now include headers using bracket notation
instead of quoted notation.

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