Introduce TKM specific charon daemon (charon-tkm)
[strongswan.git] / src / charon-tkm / src / charon-tkm.c
1 /*
2 * Copyright (C) 2012 Tobias Brunner
3 * Copyright (C) 2012 Reto Buerki
4 * Copyright (C) 2012 Adrian-Ken Rueegsegger
5 * Hochschule fuer Technik Rapperswil
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
16 */
17
18 #define _GNU_SOURCE
19
20 #include <stdio.h>
21 #include <syslog.h>
22 #include <signal.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26 #include <libgen.h>
27
28 #include <hydra.h>
29 #include <daemon.h>
30
31 #include <library.h>
32 #include <utils/backtrace.h>
33 #include <threading/thread.h>
34
35 #include <tkm/client.h>
36
37 /**
38 * PID file, in which charon-tkm stores its process id
39 */
40 static char *pidfile_name = NULL;
41
42 /**
43 * Global reference to PID file (required to truncate, if undeletable)
44 */
45 static FILE *pidfile = NULL;
46
47 /**
48 * Hook in library for debugging messages
49 */
50 extern void (*dbg) (debug_t group, level_t level, char *fmt, ...);
51
52 /**
53 * Simple logging hook for library logs, using syslog output
54 */
55 static void dbg_syslog(debug_t group, level_t level, char *fmt, ...)
56 {
57 if (level <= 1)
58 {
59 char buffer[8192];
60 va_list args;
61
62 va_start(args, fmt);
63 /* write in memory buffer first */
64 vsnprintf(buffer, sizeof(buffer), fmt, args);
65 syslog(LOG_DAEMON|LOG_INFO, "00[%s] %s", debug_names->names[group],
66 buffer);
67 va_end(args);
68 }
69 }
70
71 /**
72 * Run the daemon and handle unix signals
73 */
74 static void run()
75 {
76 sigset_t set;
77
78 /* handle SIGINT and SIGTERM in this handler */
79 sigemptyset(&set);
80 sigaddset(&set, SIGINT);
81 sigaddset(&set, SIGTERM);
82 sigprocmask(SIG_BLOCK, &set, NULL);
83
84 while (TRUE)
85 {
86 int sig;
87 int error;
88
89 error = sigwait(&set, &sig);
90 if (error)
91 {
92 DBG1(DBG_DMN, "error %d while waiting for a signal", error);
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 default:
110 {
111 DBG1(DBG_DMN, "unknown signal %d received. Ignored", sig);
112 break;
113 }
114 }
115 }
116 }
117
118 /**
119 * Handle SIGSEGV/SIGILL signals raised by threads
120 */
121 static void segv_handler(int signal)
122 {
123 backtrace_t *backtrace;
124
125 DBG1(DBG_DMN, "thread %u received %d", thread_current_id(), signal);
126 backtrace = backtrace_create(2);
127 backtrace->log(backtrace, stderr, TRUE);
128 backtrace->destroy(backtrace);
129
130 DBG1(DBG_DMN, "killing ourself, received critical signal");
131 abort();
132 }
133
134 /**
135 * Lookup UID and GID
136 */
137 static bool lookup_uid_gid()
138 {
139 #ifdef IPSEC_USER
140 if (!charon->caps->resolve_uid(charon->caps, IPSEC_USER))
141 {
142 return FALSE;
143 }
144 #endif
145 #ifdef IPSEC_GROUP
146 if (!charon->caps->resolve_gid(charon->caps, IPSEC_GROUP))
147 {
148 return FALSE;
149 }
150 #endif
151 return TRUE;
152 }
153
154 /**
155 * Check/create PID file, return TRUE if already running
156 */
157 static bool check_pidfile()
158 {
159 struct stat stb;
160
161 if (stat(pidfile_name, &stb) == 0)
162 {
163 pidfile = fopen(pidfile_name, "r");
164 if (pidfile)
165 {
166 char buf[64];
167 pid_t pid = 0;
168
169 memset(buf, 0, sizeof(buf));
170 if (fread(buf, 1, sizeof(buf), pidfile))
171 {
172 buf[sizeof(buf) - 1] = '\0';
173 pid = atoi(buf);
174 }
175 fclose(pidfile);
176 if (pid && kill(pid, 0) == 0)
177 { /* such a process is running */
178 return TRUE;
179 }
180 }
181 DBG1(DBG_DMN, "removing pidfile '%s', process not running", pidfile_name);
182 unlink(pidfile_name);
183 }
184
185 /* create new pidfile */
186 pidfile = fopen(pidfile_name, "w");
187 if (pidfile)
188 {
189 ignore_result(fchown(fileno(pidfile),
190 charon->caps->get_uid(charon->caps),
191 charon->caps->get_gid(charon->caps)));
192 fprintf(pidfile, "%d\n", getpid());
193 fflush(pidfile);
194 }
195 return FALSE;
196 }
197
198 /**
199 * Delete/truncate the PID file
200 */
201 static void unlink_pidfile()
202 {
203 /* because unlinking the PID file may fail, we truncate it to ensure the
204 * daemon can be properly restarted. one probable cause for this is the
205 * combination of not running as root and the effective user lacking
206 * permissions on the parent dir(s) of the PID file */
207 if (pidfile)
208 {
209 ignore_result(ftruncate(fileno(pidfile), 0));
210 fclose(pidfile);
211 }
212 unlink(pidfile_name);
213 }
214 /**
215 * Main function, starts TKM backend.
216 */
217 int main(int argc, char *argv[])
218 {
219 char *dmn_name;
220 if (argc > 0 && strlen(argv[0]) > 0)
221 {
222 dmn_name = basename(argv[0]);
223 }
224 else
225 {
226 dmn_name = "charon-tkm";
227 }
228
229 struct sigaction action;
230 int status = SS_RC_INITIALIZATION_FAILED;
231
232 /* logging for library during initialization, as we have no bus yet */
233 dbg = dbg_syslog;
234
235 /* initialize library */
236 if (!library_init(NULL))
237 {
238 library_deinit();
239 exit(status);
240 }
241
242 if (!libhydra_init(dmn_name))
243 {
244 dbg_syslog(DBG_DMN, 1, "initialization failed - aborting %s", dmn_name);
245 libhydra_deinit();
246 library_deinit();
247 exit(status);
248 }
249
250 if (!libcharon_init(dmn_name))
251 {
252 dbg_syslog(DBG_DMN, 1, "initialization failed - aborting %s", dmn_name);
253 goto deinit;
254 }
255
256 if (!lookup_uid_gid())
257 {
258 dbg_syslog(DBG_DMN, 1, "invalid uid/gid - aborting %s", dmn_name);
259 goto deinit;
260 }
261
262 /* make sure we log to the DAEMON facility by default */
263 lib->settings->set_int(lib->settings, "%s.syslog.daemon.default",
264 lib->settings->get_int(lib->settings, "%s.syslog.daemon.default", 1,
265 dmn_name), dmn_name);
266 charon->load_loggers(charon, NULL, FALSE);
267
268 DBG1(DBG_DMN, "Starting charon with TKM backend (strongSwan "VERSION")");
269
270 /* initialize daemon */
271 if (!charon->initialize(charon, PLUGINS))
272 {
273 DBG1(DBG_DMN, "initialization failed - aborting %s", dmn_name);
274 goto deinit;
275 }
276
277 /* set global pidfile name depending on daemon name */
278 if (asprintf(&pidfile_name, IPSEC_PIDDIR"/%s.pid", dmn_name) < 0)
279 {
280 DBG1(DBG_DMN, "unable to set pidfile name - aborting %s", dmn_name);
281 goto deinit;
282 };
283
284 if (check_pidfile())
285 {
286 DBG1(DBG_DMN, "%s already running (\"%s\" exists)", dmn_name,
287 pidfile_name);
288 goto deinit;
289 }
290
291 if (!charon->caps->drop(charon->caps))
292 {
293 DBG1(DBG_DMN, "capability dropping failed - aborting %s", dmn_name);
294 goto deinit;
295 }
296
297 /* initialize TKM client lib */
298 tkmlib_init();
299
300 /* add handler for SEGV and ILL,
301 * INT and TERM are handled by sigwait() in run() */
302 action.sa_handler = segv_handler;
303 action.sa_flags = 0;
304 sigemptyset(&action.sa_mask);
305 sigaddset(&action.sa_mask, SIGINT);
306 sigaddset(&action.sa_mask, SIGTERM);
307 sigaction(SIGSEGV, &action, NULL);
308 sigaction(SIGILL, &action, NULL);
309 sigaction(SIGBUS, &action, NULL);
310 action.sa_handler = SIG_IGN;
311 sigaction(SIGPIPE, &action, NULL);
312
313 pthread_sigmask(SIG_SETMASK, &action.sa_mask, NULL);
314
315 /* start daemon (i.e. the threads in the thread-pool) */
316 charon->start(charon);
317
318 /* main thread goes to run loop */
319 run();
320
321 unlink_pidfile();
322 status = 0;
323 tkmlib_final();
324
325 deinit:
326 libcharon_deinit();
327 libhydra_deinit();
328 library_deinit();
329 return status;
330 }