sigwaitinfo() may fail with EINTR if interrupted by an unblocked signal not in the set
[strongswan.git] / src / charon-nm / charon-nm.c
1 /*
2 * Copyright (C) 2012 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include <stdio.h>
17 #include <syslog.h>
18 #include <signal.h>
19 #include <sys/types.h>
20 #include <unistd.h>
21 #include <errno.h>
22
23 #include <hydra.h>
24 #include <daemon.h>
25
26 #include <library.h>
27 #include <utils/backtrace.h>
28 #include <threading/thread.h>
29
30 #include <nm/nm_backend.h>
31
32 /**
33 * Default user and group
34 */
35 #ifndef IPSEC_USER
36 #define IPSEC_USER NULL
37 #endif
38
39 #ifndef IPSEC_GROUP
40 #define IPSEC_GROUP NULL
41 #endif
42
43 /**
44 * Hook in library for debugging messages
45 */
46 extern void (*dbg) (debug_t group, level_t level, char *fmt, ...);
47
48 /**
49 * Simple logging hook for library logs, using syslog output
50 */
51 static void dbg_syslog(debug_t group, level_t level, char *fmt, ...)
52 {
53 if (level <= 1)
54 {
55 char buffer[8192], groupstr[4];
56 va_list args;
57
58 va_start(args, fmt);
59 /* write in memory buffer first */
60 vsnprintf(buffer, sizeof(buffer), fmt, args);
61 /* cache group name */
62 snprintf(groupstr, sizeof(groupstr), "%N", debug_names, group);
63 syslog(LOG_DAEMON|LOG_INFO, "00[%s] %s", groupstr, buffer);
64 va_end(args);
65 }
66 }
67
68 /**
69 * Run the daemon and handle unix signals
70 */
71 static void run()
72 {
73 sigset_t set;
74
75 /* handle SIGINT and SIGTERM in this handler */
76 sigemptyset(&set);
77 sigaddset(&set, SIGINT);
78 sigaddset(&set, SIGTERM);
79 sigprocmask(SIG_BLOCK, &set, NULL);
80
81 while (TRUE)
82 {
83 int sig;
84
85 sig = sigwaitinfo(&set, NULL);
86 if (sig == -1)
87 {
88 if (errno == EINTR)
89 { /* ignore signals we didn't wait for */
90 continue;
91 }
92 DBG1(DBG_DMN, "waiting for signal failed: %s", strerror(errno));
93 return;
94 }
95 switch (sig)
96 {
97 case SIGINT:
98 {
99 DBG1(DBG_DMN, "signal of type SIGINT received. Shutting down");
100 charon->bus->alert(charon->bus, ALERT_SHUTDOWN_SIGNAL, sig);
101 return;
102 }
103 case SIGTERM:
104 {
105 DBG1(DBG_DMN, "signal of type SIGTERM received. Shutting down");
106 charon->bus->alert(charon->bus, ALERT_SHUTDOWN_SIGNAL, sig);
107 return;
108 }
109 }
110 }
111 }
112
113 /**
114 * Handle SIGSEGV/SIGILL signals raised by threads
115 */
116 static void segv_handler(int signal)
117 {
118 backtrace_t *backtrace;
119
120 DBG1(DBG_DMN, "thread %u received %d", thread_current_id(), signal);
121 backtrace = backtrace_create(2);
122 backtrace->log(backtrace, stderr, TRUE);
123 backtrace->destroy(backtrace);
124
125 DBG1(DBG_DMN, "killing ourself, received critical signal");
126 abort();
127 }
128
129 /**
130 * Lookup UID and GID
131 */
132 static bool lookup_uid_gid()
133 {
134 char *name;
135
136 name = lib->settings->get_str(lib->settings, "charon-nm.user",
137 IPSEC_USER);
138 if (name && !lib->caps->resolve_uid(lib->caps, name))
139 {
140 return FALSE;
141 }
142 name = lib->settings->get_str(lib->settings, "charon-nm.group",
143 IPSEC_GROUP);
144 if (name && !lib->caps->resolve_gid(lib->caps, name))
145 {
146 return FALSE;
147 }
148 return TRUE;
149 }
150
151 /**
152 * Main function, starts NetworkManager backend.
153 */
154 int main(int argc, char *argv[])
155 {
156 struct sigaction action;
157 int status = SS_RC_INITIALIZATION_FAILED;
158
159 /* logging for library during initialization, as we have no bus yet */
160 dbg = dbg_syslog;
161
162 /* LD causes a crash probably due to Glib */
163 setenv("LEAK_DETECTIVE_DISABLE", "1", 1);
164
165 /* initialize library */
166 if (!library_init(NULL, "charon-nm"))
167 {
168 library_deinit();
169 exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
170 }
171
172 if (lib->integrity &&
173 !lib->integrity->check_file(lib->integrity, "charon-nm", argv[0]))
174 {
175 dbg_syslog(DBG_DMN, 1, "integrity check of charon-nm failed");
176 library_deinit();
177 exit(SS_RC_DAEMON_INTEGRITY);
178 }
179
180 if (!libhydra_init())
181 {
182 dbg_syslog(DBG_DMN, 1, "initialization failed - aborting charon-nm");
183 libhydra_deinit();
184 library_deinit();
185 exit(SS_RC_INITIALIZATION_FAILED);
186 }
187
188 if (!libcharon_init())
189 {
190 dbg_syslog(DBG_DMN, 1, "initialization failed - aborting charon-nm");
191 goto deinit;
192 }
193
194 if (!lookup_uid_gid())
195 {
196 dbg_syslog(DBG_DMN, 1, "invalid uid/gid - aborting charon-nm");
197 goto deinit;
198 }
199
200 /* make sure we log to the DAEMON facility by default */
201 lib->settings->set_int(lib->settings, "charon-nm.syslog.daemon.default",
202 lib->settings->get_int(lib->settings,
203 "charon-nm.syslog.daemon.default", 1));
204 charon->load_loggers(charon, NULL, FALSE);
205
206 /* use random ports to avoid conflicts with regular charon */
207 lib->settings->set_int(lib->settings, "charon-nm.port", 0);
208 lib->settings->set_int(lib->settings, "charon-nm.port_natt_t", 0);
209
210 DBG1(DBG_DMN, "Starting charon NetworkManager backend (strongSwan "VERSION")");
211 if (lib->integrity)
212 {
213 DBG1(DBG_DMN, "integrity tests enabled:");
214 DBG1(DBG_DMN, "lib 'libstrongswan': passed file and segment integrity tests");
215 DBG1(DBG_DMN, "lib 'libhydra': passed file and segment integrity tests");
216 DBG1(DBG_DMN, "lib 'libcharon': passed file and segment integrity tests");
217 DBG1(DBG_DMN, "daemon 'charon-nm': passed file integrity test");
218 }
219
220 /* register NM backend to be loaded with plugins */
221 nm_backend_register();
222
223 /* initialize daemon */
224 if (!charon->initialize(charon,
225 lib->settings->get_str(lib->settings, "charon-nm.load", PLUGINS)))
226 {
227 DBG1(DBG_DMN, "initialization failed - aborting charon-nm");
228 goto deinit;
229 }
230 lib->plugins->status(lib->plugins, LEVEL_CTRL);
231
232 if (!lib->caps->drop(lib->caps))
233 {
234 DBG1(DBG_DMN, "capability dropping failed - aborting charon-nm");
235 goto deinit;
236 }
237
238 /* add handler for SEGV and ILL,
239 * INT and TERM are handled by sigwaitinfo() in run() */
240 action.sa_handler = segv_handler;
241 action.sa_flags = 0;
242 sigemptyset(&action.sa_mask);
243 sigaddset(&action.sa_mask, SIGINT);
244 sigaddset(&action.sa_mask, SIGTERM);
245 sigaction(SIGSEGV, &action, NULL);
246 sigaction(SIGILL, &action, NULL);
247 sigaction(SIGBUS, &action, NULL);
248 action.sa_handler = SIG_IGN;
249 sigaction(SIGPIPE, &action, NULL);
250
251 pthread_sigmask(SIG_SETMASK, &action.sa_mask, NULL);
252
253 /* start daemon (i.e. the threads in the thread-pool) */
254 charon->start(charon);
255
256 /* main thread goes to run loop */
257 run();
258
259 status = 0;
260
261 deinit:
262 libcharon_deinit();
263 libhydra_deinit();
264 library_deinit();
265 return status;
266 }
267