signals: New function signal_name().
[sliver-openvswitch.git] / lib / signals.c
1 /*
2  * Copyright (c) 2008, 2009, 2011 Nicira Networks.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <config.h>
18 #include "signals.h"
19 #include <assert.h>
20 #include <errno.h>
21 #include <limits.h>
22 #include <signal.h>
23 #include <unistd.h>
24 #include "poll-loop.h"
25 #include "socket-util.h"
26 #include "type-props.h"
27 #include "util.h"
28
29 #if defined(_NSIG)
30 #define N_SIGNALS _NSIG
31 #elif defined(NSIG)
32 #define N_SIGNALS NSIG
33 #else
34 /* We could try harder to get the maximum signal number, but in practice we
35  * only care about SIGHUP, which is normally signal 1 anyway. */
36 #define N_SIGNALS 32
37 #endif
38
39 struct signal {
40     int signr;
41 };
42
43 static volatile sig_atomic_t signaled[N_SIGNALS];
44
45 static int fds[2];
46
47 static void signal_handler(int signr);
48
49 /* Initializes the signals subsystem (if it is not already initialized).  Calls
50  * exit() if initialization fails.
51  *
52  * Calling this function is optional; it will be called automatically by
53  * signal_start() if necessary.  Calling it explicitly allows the client to
54  * prevent the process from exiting at an unexpected time. */
55 void
56 signal_init(void)
57 {
58     static bool inited;
59     if (!inited) {
60         inited = true;
61         if (pipe(fds)) {
62             ovs_fatal(errno, "could not create pipe");
63         }
64         set_nonblocking(fds[0]);
65         set_nonblocking(fds[1]);
66     }
67 }
68
69 /* Sets up a handler for 'signr' and returns a structure that represents it.
70  *
71  * Only one handler for a given signal may be registered at a time. */
72 struct signal *
73 signal_register(int signr)
74 {
75     struct sigaction sa;
76     struct signal *s;
77
78     signal_init();
79
80     /* Set up signal handler. */
81     assert(signr >= 1 && signr < N_SIGNALS);
82     memset(&sa, 0, sizeof sa);
83     sa.sa_handler = signal_handler;
84     sigemptyset(&sa.sa_mask);
85     sa.sa_flags = SA_RESTART;
86     if (sigaction(signr, &sa, NULL)) {
87         ovs_fatal(errno, "sigaction(%d) failed", signr);
88     }
89
90     /* Return structure. */
91     s = xmalloc(sizeof *s);
92     s->signr = signr;
93     return s;
94 }
95
96 /* Returns true if signal 's' has been received since the last call to this
97  * function with argument 's'. */
98 bool
99 signal_poll(struct signal *s)
100 {
101     char buf[_POSIX_PIPE_BUF];
102     ignore(read(fds[0], buf, sizeof buf));
103     if (signaled[s->signr]) {
104         signaled[s->signr] = 0;
105         return true;
106     }
107     return false;
108 }
109
110 /* Causes the next call to poll_block() to wake up when signal_poll(s) would
111  * return true. */
112 void
113 signal_wait(struct signal *s)
114 {
115     if (signaled[s->signr]) {
116         poll_immediate_wake();
117     } else {
118         poll_fd_wait(fds[0], POLLIN);
119     }
120 }
121 \f
122 static void
123 signal_handler(int signr)
124 {
125     if (signr >= 1 && signr < N_SIGNALS) {
126         ignore(write(fds[1], "", 1));
127         signaled[signr] = true;
128     }
129 }
130
131 /* Returns the name of signal 'signum' as a string.  The string may be in a
132  * static buffer that is reused from one call to the next.
133  *
134  * The string is probably a (possibly multi-word) description of the signal
135  * (e.g. "Hangup") instead of just the stringified version of the macro
136  * (e.g. "SIGHUP"). */
137 const char *
138 signal_name(int signum)
139 {
140     const char *name = NULL;
141 #ifdef HAVE_STRSIGNAL
142     name = strsignal(signum);
143 #endif
144     if (!name) {
145         static char buffer[7 + INT_STRLEN(int) + 1];
146         sprintf(buffer, "signal %d", signum);
147         name = buffer;
148     }
149     return name;
150 }