]>
Commit | Line | Data |
---|---|---|
e78ec86d MW |
1 | /* |
2 | * Copyright (C) 2010 Martin Willi | |
3 | * Copyright (C) 2010 revosec AG | |
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 | ||
f452a5a1 | 16 | #define _GNU_SOURCE |
e78ec86d MW |
17 | #include <unistd.h> |
18 | #include <stdio.h> | |
19 | #include <errno.h> | |
20 | #include <signal.h> | |
21 | #include <getopt.h> | |
f452a5a1 | 22 | #include <dlfcn.h> |
b318e0ab | 23 | #include <libgen.h> |
e78ec86d MW |
24 | |
25 | #include "conftest.h" | |
d1041fa4 | 26 | #include "config.h" |
f452a5a1 | 27 | #include "hooks/hook.h" |
e78ec86d | 28 | |
d35d6691 | 29 | #include <bus/listeners/file_logger.h> |
e78ec86d | 30 | #include <threading/thread.h> |
968e5e66 | 31 | #include <credentials/certificates/x509.h> |
e78ec86d MW |
32 | |
33 | /** | |
34 | * Conftest globals struct | |
35 | */ | |
36 | conftest_t *conftest; | |
37 | ||
38 | /** | |
39 | * Print usage information | |
40 | */ | |
f452a5a1 | 41 | static void usage(FILE *out) |
e78ec86d | 42 | { |
e78ec86d MW |
43 | fprintf(out, "Usage:\n"); |
44 | fprintf(out, " --help show usage information\n"); | |
45 | fprintf(out, " --version show conftest version\n"); | |
46 | fprintf(out, " --suite <file> global testsuite configuration " | |
47 | "(default: ./suite.conf)\n"); | |
48 | fprintf(out, " --test <file> test specific configuration\n"); | |
49 | } | |
50 | ||
51 | /** | |
52 | * Handle SIGSEGV/SIGILL signals raised by threads | |
53 | */ | |
54 | static void segv_handler(int signal) | |
55 | { | |
b318e0ab | 56 | fprintf(stderr, "thread %u received %d\n", thread_current_id(), signal); |
e78ec86d MW |
57 | abort(); |
58 | } | |
59 | ||
60 | /** | |
61 | * Load suite and test specific configurations | |
62 | */ | |
63 | static bool load_configs(char *suite_file, char *test_file) | |
64 | { | |
65 | if (!test_file) | |
66 | { | |
67 | fprintf(stderr, "Missing test configuration file.\n"); | |
68 | return FALSE; | |
69 | } | |
70 | if (access(suite_file, R_OK) != 0) | |
71 | { | |
72 | fprintf(stderr, "Reading suite configuration file '%s' failed: %s.\n", | |
73 | suite_file, strerror(errno)); | |
74 | return FALSE; | |
75 | } | |
76 | if (access(test_file, R_OK) != 0) | |
77 | { | |
78 | fprintf(stderr, "Reading test configuration file '%s' failed: %s.\n", | |
79 | test_file, strerror(errno)); | |
80 | return FALSE; | |
81 | } | |
98ef2865 | 82 | conftest->test = settings_create(suite_file); |
32973044 | 83 | conftest->test->load_files(conftest->test, test_file, TRUE); |
98ef2865 | 84 | conftest->suite_dir = strdup(dirname(suite_file)); |
b318e0ab MW |
85 | return TRUE; |
86 | } | |
87 | ||
88 | /** | |
90994a8a | 89 | * Load trusted/untrusted certificates |
b318e0ab | 90 | */ |
48ef0011 | 91 | static bool load_cert(settings_t *settings, bool trusted) |
b318e0ab MW |
92 | { |
93 | enumerator_t *enumerator; | |
90994a8a | 94 | char *key, *value; |
b318e0ab | 95 | |
90994a8a MW |
96 | enumerator = settings->create_key_value_enumerator(settings, |
97 | trusted ? "certs.trusted" : "certs.untrusted"); | |
b318e0ab MW |
98 | while (enumerator->enumerate(enumerator, &key, &value)) |
99 | { | |
90994a8a MW |
100 | certificate_t *cert = NULL; |
101 | ||
48ef0011 | 102 | if (strncaseeq(key, "x509", strlen("x509"))) |
a784c9e0 | 103 | { |
90994a8a MW |
104 | cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, |
105 | CERT_X509, BUILD_FROM_FILE, value, BUILD_END); | |
a784c9e0 | 106 | } |
48ef0011 | 107 | else if (strncaseeq(key, "crl", strlen("crl"))) |
b318e0ab | 108 | { |
90994a8a MW |
109 | cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, |
110 | CERT_X509_CRL, BUILD_FROM_FILE, value, BUILD_END); | |
b318e0ab | 111 | } |
90994a8a | 112 | else |
a784c9e0 MW |
113 | { |
114 | fprintf(stderr, "certificate type '%s' not supported\n", key); | |
115 | enumerator->destroy(enumerator); | |
116 | return FALSE; | |
117 | } | |
b318e0ab MW |
118 | if (!cert) |
119 | { | |
90994a8a MW |
120 | fprintf(stderr, "loading %strusted certificate '%s' from '%s' " |
121 | "failed\n", trusted ? "" : "un", key, value); | |
b318e0ab MW |
122 | enumerator->destroy(enumerator); |
123 | return FALSE; | |
124 | } | |
90994a8a | 125 | conftest->creds->add_cert(conftest->creds, trusted, cert); |
b318e0ab MW |
126 | } |
127 | enumerator->destroy(enumerator); | |
90994a8a MW |
128 | return TRUE; |
129 | } | |
130 | ||
131 | /** | |
132 | * Load certificates from the confiuguration file | |
133 | */ | |
134 | static bool load_certs(settings_t *settings, char *dir) | |
135 | { | |
136 | char wd[PATH_MAX]; | |
137 | ||
138 | if (getcwd(wd, sizeof(wd)) == NULL) | |
139 | { | |
140 | fprintf(stderr, "getting cwd failed: %s\n", strerror(errno)); | |
141 | return FALSE; | |
142 | } | |
143 | if (chdir(dir) != 0) | |
144 | { | |
145 | fprintf(stderr, "opening directory '%s' failed: %s\n", | |
146 | dir, strerror(errno)); | |
147 | return FALSE; | |
148 | } | |
149 | ||
48ef0011 MW |
150 | if (!load_cert(settings, TRUE) || |
151 | !load_cert(settings, FALSE)) | |
90994a8a MW |
152 | { |
153 | return FALSE; | |
154 | } | |
b318e0ab | 155 | |
a13c1d64 MW |
156 | if (chdir(wd) != 0) |
157 | { | |
158 | fprintf(stderr, "opening directory '%s' failed: %s\n", | |
159 | wd, strerror(errno)); | |
160 | return FALSE; | |
161 | } | |
e78ec86d MW |
162 | return TRUE; |
163 | } | |
164 | ||
3f759bb7 MW |
165 | /** |
166 | * Load private keys from the confiuguration file | |
167 | */ | |
168 | static bool load_keys(settings_t *settings, char *dir) | |
169 | { | |
170 | enumerator_t *enumerator; | |
171 | char *type, *value, wd[PATH_MAX]; | |
172 | private_key_t *key; | |
173 | key_type_t key_type; | |
174 | ||
175 | if (getcwd(wd, sizeof(wd)) == NULL) | |
176 | { | |
177 | fprintf(stderr, "getting cwd failed: %s\n", strerror(errno)); | |
178 | return FALSE; | |
179 | } | |
180 | if (chdir(dir) != 0) | |
181 | { | |
182 | fprintf(stderr, "opening directory '%s' failed: %s\n", | |
183 | dir, strerror(errno)); | |
184 | return FALSE; | |
185 | } | |
186 | ||
187 | enumerator = settings->create_key_value_enumerator(settings, "keys"); | |
188 | while (enumerator->enumerate(enumerator, &type, &value)) | |
189 | { | |
48ef0011 | 190 | if (strncaseeq(type, "ecdsa", strlen("ecdsa"))) |
3f759bb7 MW |
191 | { |
192 | key_type = KEY_ECDSA; | |
193 | } | |
48ef0011 | 194 | else if (strncaseeq(type, "rsa", strlen("rsa"))) |
3f759bb7 MW |
195 | { |
196 | key_type = KEY_RSA; | |
197 | } | |
198 | else | |
199 | { | |
84545f6e | 200 | fprintf(stderr, "unknown key type: '%s'\n", type); |
3f759bb7 MW |
201 | enumerator->destroy(enumerator); |
202 | return FALSE; | |
203 | } | |
204 | key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, key_type, | |
205 | BUILD_FROM_FILE, value, BUILD_END); | |
206 | if (!key) | |
207 | { | |
208 | fprintf(stderr, "loading %s key from '%s' failed\n", type, value); | |
209 | enumerator->destroy(enumerator); | |
210 | return FALSE; | |
211 | } | |
212 | conftest->creds->add_key(conftest->creds, key); | |
213 | } | |
214 | enumerator->destroy(enumerator); | |
215 | ||
216 | if (chdir(wd) != 0) | |
217 | { | |
218 | fprintf(stderr, "opening directory '%s' failed: %s\n", | |
219 | wd, strerror(errno)); | |
220 | return FALSE; | |
221 | } | |
222 | return TRUE; | |
223 | } | |
224 | ||
968e5e66 MW |
225 | /** |
226 | * Load certificate distribution points | |
227 | */ | |
228 | static void load_cdps(settings_t *settings) | |
229 | { | |
230 | enumerator_t *enumerator; | |
231 | identification_t *id; | |
232 | char *ca, *uri, *section; | |
97d30f00 | 233 | certificate_type_t type; |
968e5e66 MW |
234 | x509_t *x509; |
235 | ||
236 | enumerator = settings->create_section_enumerator(settings, "cdps"); | |
237 | while (enumerator->enumerate(enumerator, §ion)) | |
238 | { | |
97d30f00 MW |
239 | if (strncaseeq(section, "crl", strlen("crl"))) |
240 | { | |
241 | type = CERT_X509_CRL; | |
242 | } | |
243 | else if (strncaseeq(section, "ocsp", strlen("ocsp"))) | |
244 | { | |
245 | type = CERT_X509_OCSP_RESPONSE; | |
246 | } | |
247 | else | |
968e5e66 MW |
248 | { |
249 | fprintf(stderr, "unknown cdp type '%s', ignored\n", section); | |
250 | continue; | |
251 | } | |
252 | ||
253 | uri = settings->get_str(settings, "cdps.%s.uri", NULL, section); | |
254 | ca = settings->get_str(settings, "cdps.%s.ca", NULL, section); | |
255 | if (!ca || !uri) | |
256 | { | |
257 | fprintf(stderr, "cdp '%s' misses ca/uri, ignored\n", section); | |
258 | continue; | |
259 | } | |
260 | x509 = lib->creds->create(lib->creds, CRED_CERTIFICATE, | |
261 | CERT_X509, BUILD_FROM_FILE, ca, BUILD_END); | |
262 | if (!x509) | |
263 | { | |
264 | fprintf(stderr, "loading cdp '%s' ca failed, ignored\n", section); | |
265 | continue; | |
266 | } | |
267 | id = identification_create_from_encoding(ID_KEY_ID, | |
268 | x509->get_subjectKeyIdentifier(x509)); | |
97d30f00 | 269 | conftest->creds->add_cdp(conftest->creds, type, id, uri); |
968e5e66 MW |
270 | DESTROY_IF((certificate_t*)x509); |
271 | id->destroy(id); | |
272 | } | |
273 | enumerator->destroy(enumerator); | |
274 | } | |
275 | ||
f452a5a1 MW |
276 | /** |
277 | * Load configured hooks | |
278 | */ | |
279 | static bool load_hooks() | |
280 | { | |
281 | enumerator_t *enumerator; | |
a60b8928 MW |
282 | char *name, *pos, buf[64]; |
283 | hook_t *(*create)(char*); | |
f452a5a1 MW |
284 | hook_t *hook; |
285 | ||
286 | enumerator = conftest->test->create_section_enumerator(conftest->test, | |
287 | "hooks"); | |
288 | while (enumerator->enumerate(enumerator, &name)) | |
289 | { | |
a60b8928 MW |
290 | pos = strchr(name, '-'); |
291 | if (pos) | |
292 | { | |
a05f3b20 TB |
293 | snprintf(buf, sizeof(buf), "%.*s_hook_create", (int)(pos - name), |
294 | name); | |
a60b8928 MW |
295 | } |
296 | else | |
297 | { | |
298 | snprintf(buf, sizeof(buf), "%s_hook_create", name); | |
299 | } | |
f452a5a1 MW |
300 | create = dlsym(RTLD_DEFAULT, buf); |
301 | if (create) | |
302 | { | |
a60b8928 | 303 | hook = create(name); |
f452a5a1 MW |
304 | if (hook) |
305 | { | |
306 | conftest->hooks->insert_last(conftest->hooks, hook); | |
307 | charon->bus->add_listener(charon->bus, &hook->listener); | |
308 | } | |
309 | } | |
310 | else | |
311 | { | |
312 | fprintf(stderr, "dlsym() for hook '%s' failed: %s\n", name, dlerror()); | |
313 | enumerator->destroy(enumerator); | |
314 | return FALSE; | |
315 | } | |
316 | } | |
317 | enumerator->destroy(enumerator); | |
318 | return TRUE; | |
319 | } | |
320 | ||
e78ec86d MW |
321 | /** |
322 | * atexit() cleanup handler | |
323 | */ | |
324 | static void cleanup() | |
325 | { | |
18a8893e | 326 | file_logger_t *logger; |
f452a5a1 MW |
327 | hook_t *hook; |
328 | ||
e78ec86d | 329 | DESTROY_IF(conftest->test); |
b318e0ab MW |
330 | lib->credmgr->remove_set(lib->credmgr, &conftest->creds->set); |
331 | conftest->creds->destroy(conftest->creds); | |
73a3013f | 332 | DESTROY_IF(conftest->actions); |
f452a5a1 MW |
333 | while (conftest->hooks->remove_last(conftest->hooks, |
334 | (void**)&hook) == SUCCESS) | |
335 | { | |
336 | charon->bus->remove_listener(charon->bus, &hook->listener); | |
337 | hook->destroy(hook); | |
338 | } | |
339 | conftest->hooks->destroy(conftest->hooks); | |
d1041fa4 MW |
340 | if (conftest->config) |
341 | { | |
3b3e5c0d MW |
342 | if (charon->backends) |
343 | { | |
344 | charon->backends->remove_backend(charon->backends, | |
345 | &conftest->config->backend); | |
346 | } | |
d1041fa4 MW |
347 | conftest->config->destroy(conftest->config); |
348 | } | |
18a8893e TB |
349 | while (conftest->loggers->remove_last(conftest->loggers, |
350 | (void**)&logger) == SUCCESS) | |
351 | { | |
352 | charon->bus->remove_logger(charon->bus, &logger->logger); | |
353 | logger->destroy(logger); | |
354 | } | |
355 | conftest->loggers->destroy(conftest->loggers); | |
b318e0ab | 356 | free(conftest->suite_dir); |
e78ec86d MW |
357 | free(conftest); |
358 | libcharon_deinit(); | |
359 | libhydra_deinit(); | |
360 | library_deinit(); | |
361 | } | |
362 | ||
dbec133e MW |
363 | /** |
364 | * Load log levels for a logger from section | |
365 | */ | |
366 | static void load_log_levels(file_logger_t *logger, char *section) | |
367 | { | |
368 | debug_t group; | |
369 | level_t def; | |
370 | ||
371 | def = conftest->test->get_int(conftest->test, "log.%s.default", 1, section); | |
372 | for (group = 0; group < DBG_MAX; group++) | |
373 | { | |
374 | logger->set_level(logger, group, | |
375 | conftest->test->get_int(conftest->test, "log.%s.%N", def, | |
376 | section, debug_lower_names, group)); | |
377 | } | |
378 | } | |
379 | ||
13c17785 TK |
380 | /** |
381 | * Load logger options for a logger from section | |
382 | */ | |
383 | static void load_logger_options(file_logger_t *logger, char *section) | |
384 | { | |
385 | bool ike_name; | |
386 | char *time_format; | |
387 | ||
388 | time_format = conftest->test->get_str(conftest->test, | |
389 | "log.%s.time_format", NULL, section); | |
390 | ike_name = conftest->test->get_bool(conftest->test, | |
391 | "log.%s.ike_name", FALSE, section); | |
392 | ||
393 | logger->set_options(logger, time_format, ike_name); | |
394 | } | |
395 | ||
dbec133e MW |
396 | /** |
397 | * Load logger configuration | |
398 | */ | |
399 | static void load_loggers(file_logger_t *logger) | |
400 | { | |
401 | enumerator_t *enumerator; | |
402 | char *section; | |
dbec133e MW |
403 | |
404 | load_log_levels(logger, "stdout"); | |
13c17785 | 405 | load_logger_options(logger, "stdout"); |
b78698f7 TK |
406 | /* Re-add the logger to propagate configuration changes to the |
407 | * logging system */ | |
408 | charon->bus->add_logger(charon->bus, &logger->logger); | |
dbec133e MW |
409 | |
410 | enumerator = conftest->test->create_section_enumerator(conftest->test, "log"); | |
411 | while (enumerator->enumerate(enumerator, §ion)) | |
412 | { | |
413 | if (!streq(section, "stdout")) | |
414 | { | |
d35d6691 | 415 | logger = file_logger_create(section); |
13c17785 | 416 | load_logger_options(logger, section); |
d35d6691 | 417 | logger->open(logger, FALSE, FALSE); |
dbec133e | 418 | load_log_levels(logger, section); |
172fdf8b | 419 | charon->bus->add_logger(charon->bus, &logger->logger); |
18a8893e | 420 | conftest->loggers->insert_last(conftest->loggers, logger); |
dbec133e MW |
421 | } |
422 | } | |
423 | enumerator->destroy(enumerator); | |
424 | } | |
425 | ||
e78ec86d MW |
426 | /** |
427 | * Main function, starts the conftest daemon. | |
428 | */ | |
429 | int main(int argc, char *argv[]) | |
430 | { | |
431 | struct sigaction action; | |
432 | int status = 0; | |
433 | sigset_t set; | |
434 | int sig; | |
11ac36b0 | 435 | char *suite_file = "suite.conf", *test_file = NULL, *preload, *plugins; |
e78ec86d MW |
436 | file_logger_t *logger; |
437 | ||
34d3bfcf | 438 | if (!library_init(NULL, "conftest")) |
e78ec86d MW |
439 | { |
440 | library_deinit(); | |
441 | return SS_RC_LIBSTRONGSWAN_INTEGRITY; | |
442 | } | |
443 | if (!libhydra_init("conftest")) | |
444 | { | |
445 | libhydra_deinit(); | |
446 | library_deinit(); | |
447 | return SS_RC_INITIALIZATION_FAILED; | |
448 | } | |
18758e3d | 449 | if (!libcharon_init("conftest")) |
e78ec86d MW |
450 | { |
451 | libcharon_deinit(); | |
452 | libhydra_deinit(); | |
453 | library_deinit(); | |
454 | return SS_RC_INITIALIZATION_FAILED; | |
455 | } | |
456 | ||
457 | INIT(conftest, | |
b318e0ab | 458 | .creds = mem_cred_create(), |
18a8893e TB |
459 | .config = config_create(), |
460 | .hooks = linked_list_create(), | |
461 | .loggers = linked_list_create(), | |
e78ec86d | 462 | ); |
18a8893e | 463 | lib->credmgr->add_set(lib->credmgr, &conftest->creds->set); |
dbec133e | 464 | |
d35d6691 TB |
465 | logger = file_logger_create("stdout"); |
466 | logger->set_options(logger, NULL, FALSE); | |
467 | logger->open(logger, FALSE, FALSE); | |
e78ec86d | 468 | logger->set_level(logger, DBG_ANY, LEVEL_CTRL); |
172fdf8b | 469 | charon->bus->add_logger(charon->bus, &logger->logger); |
18a8893e | 470 | conftest->loggers->insert_last(conftest->loggers, logger); |
b318e0ab | 471 | |
e78ec86d MW |
472 | atexit(cleanup); |
473 | ||
474 | while (TRUE) | |
475 | { | |
476 | struct option long_opts[] = { | |
477 | { "help", no_argument, NULL, 'h' }, | |
478 | { "version", no_argument, NULL, 'v' }, | |
479 | { "suite", required_argument, NULL, 's' }, | |
480 | { "test", required_argument, NULL, 't' }, | |
481 | { 0,0,0,0 } | |
482 | }; | |
483 | switch (getopt_long(argc, argv, "", long_opts, NULL)) | |
484 | { | |
485 | case EOF: | |
486 | break; | |
487 | case 'h': | |
f452a5a1 | 488 | usage(stdout); |
e78ec86d MW |
489 | return 0; |
490 | case 'v': | |
491 | printf("strongSwan %s conftest\n", VERSION); | |
492 | return 0; | |
493 | case 's': | |
494 | suite_file = optarg; | |
495 | continue; | |
496 | case 't': | |
497 | test_file = optarg; | |
498 | continue; | |
499 | default: | |
f452a5a1 | 500 | usage(stderr); |
e78ec86d MW |
501 | return 1; |
502 | } | |
503 | break; | |
504 | } | |
505 | ||
506 | if (!load_configs(suite_file, test_file)) | |
507 | { | |
508 | return 1; | |
509 | } | |
dbec133e MW |
510 | load_loggers(logger); |
511 | ||
11ac36b0 TB |
512 | preload = conftest->test->get_str(conftest->test, "preload", ""); |
513 | if (asprintf(&plugins, "%s %s", preload, PLUGINS) < 0) | |
9a99b745 MW |
514 | { |
515 | return 1; | |
516 | } | |
11ac36b0 | 517 | if (!charon->initialize(charon, plugins)) |
e78ec86d | 518 | { |
11ac36b0 | 519 | free(plugins); |
e78ec86d MW |
520 | return 1; |
521 | } | |
607f8e99 | 522 | lib->plugins->status(lib->plugins, LEVEL_CTRL); |
11ac36b0 | 523 | free(plugins); |
607f8e99 | 524 | |
98ef2865 | 525 | if (!load_certs(conftest->test, conftest->suite_dir)) |
b318e0ab MW |
526 | { |
527 | return 1; | |
528 | } | |
98ef2865 | 529 | if (!load_keys(conftest->test, conftest->suite_dir)) |
3f759bb7 MW |
530 | { |
531 | return 1; | |
532 | } | |
968e5e66 | 533 | load_cdps(conftest->test); |
f452a5a1 MW |
534 | if (!load_hooks()) |
535 | { | |
536 | return 1; | |
537 | } | |
d1041fa4 | 538 | charon->backends->add_backend(charon->backends, &conftest->config->backend); |
d1041fa4 | 539 | conftest->config->load(conftest->config, conftest->test); |
73a3013f | 540 | conftest->actions = actions_create(); |
e78ec86d MW |
541 | |
542 | /* set up thread specific handlers */ | |
543 | action.sa_handler = segv_handler; | |
544 | action.sa_flags = 0; | |
545 | sigemptyset(&action.sa_mask); | |
546 | sigaddset(&action.sa_mask, SIGINT); | |
547 | sigaddset(&action.sa_mask, SIGTERM); | |
548 | sigaddset(&action.sa_mask, SIGHUP); | |
549 | sigaction(SIGSEGV, &action, NULL); | |
550 | sigaction(SIGILL, &action, NULL); | |
551 | sigaction(SIGBUS, &action, NULL); | |
552 | action.sa_handler = SIG_IGN; | |
553 | sigaction(SIGPIPE, &action, NULL); | |
554 | pthread_sigmask(SIG_SETMASK, &action.sa_mask, NULL); | |
555 | ||
556 | /* start thread pool */ | |
557 | charon->start(charon); | |
558 | ||
559 | /* handle SIGINT/SIGTERM in main thread */ | |
560 | sigemptyset(&set); | |
561 | sigaddset(&set, SIGINT); | |
562 | sigaddset(&set, SIGHUP); | |
563 | sigaddset(&set, SIGTERM); | |
564 | sigprocmask(SIG_BLOCK, &set, NULL); | |
565 | ||
566 | while (sigwait(&set, &sig) == 0) | |
567 | { | |
568 | switch (sig) | |
569 | { | |
570 | case SIGINT: | |
571 | case SIGTERM: | |
572 | fprintf(stderr, "\nshutting down...\n"); | |
573 | break; | |
574 | default: | |
575 | continue; | |
576 | } | |
577 | break; | |
578 | } | |
579 | return status; | |
580 | } |