source: mainline/uspace/lib/posix/source/signal.c@ 0d0b319

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 0d0b319 was 0d0b319, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

Flip error constants to positive values, and update libposix for the change.

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