1 /* Copyright (C) 2016-2020 Greenbone Networks GmbH
2 *
3 * SPDX-License-Identifier: AGPL-3.0-or-later
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /**
20 * @file utils.c
21 * @brief Generic utilities
22 *
23 * Generic helper utilities. None of these are GVM specific. They could
24 * be used anywhere.
25 */
26
27 /**
28 * @brief Enable extra functions.
29 *
30 * time.h in glibc2 needs this for strptime.
31 */
32 #define _XOPEN_SOURCE
33
34 /**
35 * @brief Needed for nanosleep.
36 */
37 //#define _POSIX_C_SOURCE 199309L
38
39 #include "utils.h"
40
41 #include <assert.h>
42 #include <ctype.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <sys/file.h>
48 #include <sys/types.h>
49 #include <sys/stat.h>
50 #include <unistd.h>
51
52 #undef G_LOG_DOMAIN
53 /**
54 * @brief GLib log domain.
55 */
56 #define G_LOG_DOMAIN "md manage"
57
58
59 /* Sleep. */
60
61 /**
62 * @brief Sleep for some number of microseconds, handling interrupts.
63 *
64 * @param[in] microseconds Number of microseconds.
65 *
66 * @return 0 success, -1 error (with errno set).
67 */
68 int
gvm_usleep(unsigned int microseconds)69 gvm_usleep (unsigned int microseconds)
70 {
71 struct timespec a, b, *requested, *remaining;
72 int ret;
73
74 requested = &a;
75 remaining = &b;
76
77 requested->tv_sec = microseconds / 1000000;
78 requested->tv_nsec = (microseconds % 1000000) * 1000;
79
80 while ((ret = nanosleep (requested, remaining)) && (errno == EINTR))
81 {
82 struct timespec *temp;
83 temp = requested;
84 requested = remaining;
85 remaining = temp;
86 }
87 if (ret)
88 return -1;
89 return 0;
90 }
91
92 /**
93 * @brief Sleep for some number of seconds, handling interrupts.
94 *
95 * @param[in] seconds Number of seconds.
96 *
97 * @return 0 success, -1 error (with errno set).
98 */
99 int
gvm_sleep(unsigned int seconds)100 gvm_sleep (unsigned int seconds)
101 {
102 return gvm_usleep (seconds * 1000000);
103 }
104
105
106 /* Time. */
107
108 /**
109 * @brief Convert a UTC time into seconds since epoch.
110 *
111 * @param[in] format Format of time.
112 * @param[in] text_time Time as text.
113 *
114 * @return Time since epoch. 0 on error.
115 */
116 static int
parse_utc_time(const char * format,const char * text_time)117 parse_utc_time (const char *format, const char *text_time)
118 {
119 int epoch_time;
120 struct tm tm;
121 gchar *tz;
122
123 /* Scanner sends UTC in ctime format: "Wed Jun 30 21:49:08 1993". */
124
125 /* Store current TZ. */
126 tz = getenv ("TZ") ? g_strdup (getenv ("TZ")) : NULL;
127
128 if (setenv ("TZ", "UTC", 1) == -1)
129 {
130 g_warning ("%s: Failed to switch to UTC", __func__);
131 if (tz != NULL)
132 setenv ("TZ", tz, 1);
133 g_free (tz);
134 return 0;
135 }
136
137 memset (&tm, 0, sizeof (struct tm));
138 if (strptime ((char*) text_time, format, &tm) == NULL)
139 {
140 g_warning ("%s: Failed to parse time", __func__);
141 if (tz != NULL)
142 setenv ("TZ", tz, 1);
143 g_free (tz);
144 return 0;
145 }
146 epoch_time = mktime (&tm);
147 if (epoch_time == -1)
148 {
149 g_warning ("%s: Failed to make time", __func__);
150 if (tz != NULL)
151 setenv ("TZ", tz, 1);
152 g_free (tz);
153 return 0;
154 }
155
156 /* Revert to stored TZ. */
157 if (tz)
158 {
159 if (setenv ("TZ", tz, 1) == -1)
160 {
161 g_warning ("%s: Failed to switch to original TZ", __func__);
162 g_free (tz);
163 return 0;
164 }
165 }
166 else
167 unsetenv ("TZ");
168
169 g_free (tz);
170 return epoch_time;
171 }
172
173 /**
174 * @brief Parses a time string using strptime, resetting the data structure.
175 *
176 * @param[in] text_time The time string to parse.
177 * @param[in] format The format string.
178 * @param[out] tm The tm date structure to write to.
179 *
180 * @return Pointer to first character not processed by strptime.
181 */
182 static char *
strptime_with_reset(const char * text_time,const char * format,struct tm * tm)183 strptime_with_reset (const char *text_time, const char *format, struct tm* tm)
184 {
185 memset (tm, 0, sizeof (struct tm));
186 tm->tm_isdst = -1;
187 return strptime ((char*) text_time, format, tm);
188 }
189
190 /**
191 * @brief Converts a tm struct into seconds since epoch with a given timezone.
192 *
193 * @param[in] tm The time data structure.
194 * @param[in] new_tz The timezone to use or NULL for UTC.
195 *
196 * @return The seconds since epoch from the given time data.
197 */
198 static time_t
mktime_with_tz(struct tm * tm,const char * new_tz)199 mktime_with_tz (struct tm *tm, const char *new_tz)
200 {
201 gchar *tz;
202 int epoch_time;
203
204 /* Store current TZ. */
205 tz = getenv ("TZ") ? g_strdup (getenv ("TZ")) : NULL;
206
207 /* Set new TZ */
208 if (setenv ("TZ",
209 new_tz
210 ? new_tz
211 : "UTC",
212 1)
213 == -1)
214 {
215 g_warning ("%s: Failed to switch to timezone %s",
216 __func__, new_tz);
217 if (tz != NULL)
218 setenv ("TZ", tz, 1);
219 g_free (tz);
220 return 0;
221 }
222
223 /* Get the time */
224 epoch_time = mktime (tm);
225
226 /* Revert to stored TZ. */
227 if (tz)
228 {
229 if (setenv ("TZ", tz, 1) == -1)
230 {
231 g_warning ("%s: Failed to switch to original TZ", __func__);
232 g_free (tz);
233 return 0;
234 }
235 }
236 else
237 unsetenv ("TZ");
238
239 return epoch_time;
240 }
241
242 /**
243 * @brief Convert a UTC ctime string into seconds since the epoch.
244 *
245 * @param[in] text_time Time as text in ctime format.
246 *
247 * @return Time since epoch. 0 on error.
248 */
249 int
parse_utc_ctime(const char * text_time)250 parse_utc_ctime (const char *text_time)
251 {
252 return parse_utc_time ("%a %b %d %H:%M:%S %Y", text_time);
253 }
254
255 /**
256 * @brief Convert a feed timestamp into seconds since epoch.
257 *
258 * @param[in] text_time Time as text in ctime format.
259 *
260 * @return Time since epoch. 0 on error.
261 */
262 int
parse_feed_timestamp(const char * text_time)263 parse_feed_timestamp (const char *text_time)
264 {
265 return parse_utc_time ("%Y%m%d", text_time);
266 }
267
268 /**
269 * @brief Convert a ctime into seconds since epoch.
270 *
271 * Use the current timezone.
272 *
273 * @param[in] text_time Time as text in ctime format.
274 *
275 * @return Time since epoch.
276 */
277 int
parse_ctime(const char * text_time)278 parse_ctime (const char *text_time)
279 {
280 int epoch_time;
281 struct tm tm;
282
283 /* ctime format: "Wed Jun 30 21:49:08 1993". */
284
285 memset (&tm, 0, sizeof (struct tm));
286 if (strptime ((char*) text_time, "%a %b %d %H:%M:%S %Y", &tm) == NULL)
287 {
288 g_warning ("%s: Failed to parse time '%s'", __func__, text_time);
289 return 0;
290 }
291 epoch_time = mktime (&tm);
292 if (epoch_time == -1)
293 {
294 g_warning ("%s: Failed to make time '%s'", __func__, text_time);
295 return 0;
296 }
297
298 return epoch_time;
299 }
300
301 /**
302 * @brief Calculate difference between now and epoch_time in days
303 *
304 * @param[in] epoch_time Time in seconds from epoch.
305 *
306 * @return Int days bettween now and epoch_time or -1 if epoch_time is in the
307 * past
308 */
309 int
days_from_now(time_t * epoch_time)310 days_from_now (time_t *epoch_time)
311 {
312 time_t now = time (NULL);
313 int diff = *epoch_time - now;
314
315 if (diff < 0) return -1;
316 return diff / 86400; /* 60 sec * 60 min * 24 h */
317 }
318
319 /**
320 * @brief Convert an ISO time into seconds since epoch.
321 *
322 * If no offset is specified, the given timezone is used (UTC in case of NULL).
323 *
324 * @param[in] text_time Time as text in ISO format: 2011-11-03T09:23:28+02:00.
325 * @param[in] fallback_tz The fallback timezone if offset is missing.
326 *
327 * @return Time since epoch. 0 on error.
328 */
329 time_t
parse_iso_time_tz(const char * text_time,const char * fallback_tz)330 parse_iso_time_tz (const char *text_time, const char *fallback_tz)
331 {
332 static GRegex *regex = NULL;
333 GMatchInfo *match_info;
334 struct tm tm;
335 int epoch_time;
336
337 epoch_time = 0;
338
339 if (regex == NULL)
340 regex = g_regex_new ("^([0-9]{4}-[0-9]{2}-[0-9]{2})"
341 "[T ]([0-9]{2}:[0-9]{2})"
342 "(:[0-9]{2})?(?:\\.[0-9]+)?"
343 "(Z|[+-][0-9]{2}:?[0-9]{2})?$",
344 0, 0, NULL);
345
346 if (g_regex_match (regex, text_time, 0, &match_info))
347 {
348 gchar *date_str, *time_str, *secs_str, *offset_str, *cleaned_text_time;
349
350 /* Converting the date-time string to a more strictly defined format
351 * makes it easier to parse variants of the ISO time:
352 * - Using a space to separate the date and time instead of "T"
353 * - Omitting the seconds
354 * - Having fractional seconds
355 */
356 date_str = g_match_info_fetch (match_info, 1);
357 time_str = g_match_info_fetch (match_info, 2);
358 secs_str = g_match_info_fetch (match_info, 3);
359 offset_str = g_match_info_fetch (match_info, 4);
360 cleaned_text_time
361 = g_strdup_printf ("%sT%s%s%s",
362 date_str ? date_str : "",
363 time_str ? time_str : "",
364 secs_str && strcmp (secs_str, "")
365 ? secs_str : ":00",
366 offset_str ? offset_str : "");
367 #if !defined(__GLIBC__)
368 if (strptime_with_reset ((char*) cleaned_text_time, "%Y-%m-%dT%T", &tm))
369 #else
370 if (strptime_with_reset ((char*) cleaned_text_time, "%FT%T%z", &tm))
371 #endif
372 {
373 /* ISO time with numeric offset (e.g. 2020-06-01T01:02:03+04:30) */
374 tm.tm_sec = tm.tm_sec - tm.tm_gmtoff;
375 tm.tm_gmtoff = 0;
376 epoch_time = mktime_with_tz (&tm, "UTC");
377 }
378 #if !defined(__GLIBC__)
379 else if (strptime_with_reset ((char*) cleaned_text_time, "%Y-%m-%dT%T", &tm))
380 #else
381 else if (strptime_with_reset ((char*) cleaned_text_time, "%FT%TZ", &tm))
382 #endif
383 {
384 /* ISO time with "Z" for UTC timezone (e.g. 2020-06-01T01:02:03Z) */
385 epoch_time = mktime_with_tz (&tm, "UTC");
386 }
387 #if !defined(__GLIBC__)
388 else if (strptime_with_reset ((char*) cleaned_text_time, "%Y-%m-%dT%T", &tm))
389 #else
390 else if (strptime_with_reset ((char*) cleaned_text_time, "%FT%T", &tm))
391 #endif
392 {
393 /* ISO time without timezone suffix (e.g. 2020-06-01T01:02:03) */
394 epoch_time = mktime_with_tz (&tm, fallback_tz ? fallback_tz : "UTC");
395 }
396 else
397 g_warning ("%s: Could not parse time %s", __func__, text_time);
398
399 g_free (date_str);
400 g_free (time_str);
401 g_free (secs_str);
402 g_free (offset_str);
403 g_free (cleaned_text_time);
404 }
405 else
406 g_warning ("%s: Could not parse time %s", __func__, text_time);
407
408 g_match_info_free (match_info);
409
410 if (epoch_time == -1)
411 {
412 g_warning ("%s: mktime failed for time %s", __func__, text_time);
413 return 0;
414 }
415
416 return epoch_time;
417 }
418
419 /**
420 * @brief Create an ISO time from seconds since epoch.
421 *
422 * @param[in] epoch_time Time in seconds from epoch.
423 * @param[out] abbrev Abbreviation for current timezone.
424 *
425 * @return Pointer to ISO time in static memory, or NULL on error.
426 */
427 static char *
iso_time_internal(time_t * epoch_time,const char ** abbrev)428 iso_time_internal (time_t *epoch_time, const char **abbrev)
429 {
430 struct tm tm;
431 static char time_string[100];
432
433 if (localtime_r (epoch_time, &tm) == NULL)
434 return NULL;
435 #if defined(__FreeBSD__) || defined(__DragonFly__)
436 if (tm.tm_gmtoff == 0)
437 #else
438 if (timezone == 0)
439 #endif
440 {
441 #if !defined(__GLIBC__)
442 if (strftime (time_string, 98, "%Y-%m-%dT%T", &tm) == 0)
443 #else
444 if (strftime (time_string, 98, "%FT%TZ", &tm) == 0)
445 #endif
446 return NULL;
447
448 if (abbrev)
449 *abbrev = "UTC";
450 }
451 else
452 {
453 int len;
454
455 #if !defined(__GLIBC__)
456 if (strftime (time_string, 98, "%Y-%m-%dT%T", &tm) == 0)
457 #else
458 if (strftime (time_string, 98, "%FT%T%z", &tm) == 0)
459 #endif
460 return NULL;
461
462 /* Insert the ISO 8601 colon by hand. */
463 len = strlen (time_string);
464 time_string[len + 1] = '\0';
465 time_string[len] = time_string[len - 1];
466 time_string[len - 1] = time_string[len - 2];
467 time_string[len - 2] = ':';
468
469 if (abbrev)
470 {
471 static char abbrev_string[100];
472 if (strftime (abbrev_string, 98, "%Z", &tm) == 0)
473 return NULL;
474 *abbrev = abbrev_string;
475 }
476 }
477
478 return time_string;
479 }
480
481 /**
482 * @brief Create an ISO time from seconds since epoch.
483 *
484 * @param[in] epoch_time Time in seconds from epoch.
485 *
486 * @return Pointer to ISO time in static memory, or NULL on error.
487 */
488 char *
iso_time(time_t * epoch_time)489 iso_time (time_t *epoch_time)
490 {
491 return iso_time_internal (epoch_time, NULL);
492 }
493
494 /**
495 * @brief Create an ISO time from seconds since epoch, given a timezone.
496 *
497 * @param[in] epoch_time Time in seconds from epoch.
498 * @param[in] zone Timezone.
499 * @param[out] abbrev Timezone abbreviation.
500 *
501 * @return Pointer to ISO time in static memory, or NULL on error.
502 */
503 char *
iso_time_tz(time_t * epoch_time,const char * zone,const char ** abbrev)504 iso_time_tz (time_t *epoch_time, const char *zone, const char **abbrev)
505 {
506 gchar *tz;
507 char *ret;
508
509 if (zone == NULL)
510 return iso_time (epoch_time);
511
512 /* Store current TZ. */
513 tz = getenv ("TZ") ? g_strdup (getenv ("TZ")) : NULL;
514
515 if (setenv ("TZ", zone, 1) == -1)
516 {
517 g_warning ("%s: Failed to switch to zone", __func__);
518 if (tz != NULL)
519 setenv ("TZ", tz, 1);
520 g_free (tz);
521 return iso_time (epoch_time);
522 }
523
524 tzset ();
525 ret = iso_time_internal (epoch_time, abbrev);
526
527 /* Revert to stored TZ. */
528 if (tz)
529 {
530 if (setenv ("TZ", tz, 1) == -1)
531 {
532 g_warning ("%s: Failed to switch to original TZ", __func__);
533 g_free (tz);
534 return ret;
535 }
536 }
537 else
538 unsetenv ("TZ");
539
540 g_free (tz);
541 return ret;
542 }
543
544
545 /* Locks. */
546
547 /**
548 * @brief Lock a file.
549 *
550 * @param[in] lockfile Lockfile.
551 * @param[in] lockfile_name Basename or full path of lock file.
552 * @param[in] operation LOCK_EX (exclusive) or LOCK_SH (shared).
553 * Maybe ORd with LOCK_NB to prevent blocking.
554 * @param[in] name_is_full_path Whether the name is a full path.
555 *
556 * @return 0 success, 1 already locked, -1 error
557 */
558 static int
lock_internal(lockfile_t * lockfile,const gchar * lockfile_name,int operation,gboolean name_is_full_path)559 lock_internal (lockfile_t *lockfile, const gchar *lockfile_name,
560 int operation, gboolean name_is_full_path)
561 {
562 mode_t old_umask;
563 int fd;
564 gchar *full_name;
565
566 /* Open the lock file. */
567
568 if (name_is_full_path)
569 full_name = g_strdup (lockfile_name);
570 else
571 full_name = g_build_filename (GVM_RUN_DIR, lockfile_name, NULL);
572
573 old_umask = umask (0);
574 fd = open (full_name, O_RDWR | O_CREAT,
575 /* "-rw-rw-r--" */
576 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
577 if (fd == -1)
578 {
579 g_warning ("Failed to open lock file '%s': %s", full_name,
580 strerror (errno));
581 umask (old_umask);
582 lockfile->name = NULL;
583 g_free (full_name);
584 return -1;
585 }
586 umask (old_umask);
587
588 /* Lock the lockfile. */
589
590 if (flock (fd, operation)) /* Blocks, unless operation includes LOCK_NB. */
591 {
592 int flock_errno;
593
594 flock_errno = errno;
595 lockfile->name = NULL;
596 g_free (full_name);
597 if (close (fd))
598 g_warning ("%s: failed to close lock file fd: %s",
599 __func__,
600 strerror (errno));
601 if (flock_errno == EWOULDBLOCK)
602 return 1;
603 g_warning ("%s: flock: %s", __func__, strerror (flock_errno));
604 return -1;
605 }
606
607 lockfile->fd = fd;
608 lockfile->name = full_name;
609
610 return 0;
611 }
612
613 /**
614 * @brief Lock a file exclusively.
615 *
616 * Block until file is locked.
617 *
618 * @param[in] lockfile Lockfile.
619 * @param[in] lockfile_basename Basename of lock file.
620 *
621 * @return 0 success, 1 already locked, -1 error
622 */
623 int
lockfile_lock(lockfile_t * lockfile,const gchar * lockfile_basename)624 lockfile_lock (lockfile_t *lockfile, const gchar *lockfile_basename)
625 {
626 g_debug ("%s: lock '%s'", __func__, lockfile_basename);
627 return lock_internal (lockfile, lockfile_basename, LOCK_EX, FALSE);
628 }
629
630 /**
631 * @brief Lock a file exclusively, without blocking.
632 *
633 * @param[in] lockfile Lockfile.
634 * @param[in] lockfile_basename Basename of lock file.
635 *
636 * @return 0 success, 1 already locked, -1 error
637 */
638 int
lockfile_lock_nb(lockfile_t * lockfile,const gchar * lockfile_basename)639 lockfile_lock_nb (lockfile_t *lockfile, const gchar *lockfile_basename)
640 {
641 g_debug ("%s: lock '%s'", __func__, lockfile_basename);
642 return lock_internal (lockfile, lockfile_basename, LOCK_EX | LOCK_NB, FALSE);
643 }
644
645 /**
646 * @brief Lock a file exclusively, without blocking, given a full path.
647 *
648 * @param[in] lockfile Lockfile.
649 * @param[in] lockfile_path Full path of lock file.
650 *
651 * @return 0 success, 1 already locked, -1 error
652 */
653 int
lockfile_lock_path_nb(lockfile_t * lockfile,const gchar * lockfile_path)654 lockfile_lock_path_nb (lockfile_t *lockfile, const gchar *lockfile_path)
655 {
656 g_debug ("%s: lock '%s'", __func__, lockfile_path);
657 return lock_internal (lockfile, lockfile_path, LOCK_EX | LOCK_NB, TRUE);
658 }
659
660 /**
661 * @brief Lock a file with a shared lock.
662 *
663 * @param[in] lockfile Lockfile.
664 * @param[in] lockfile_basename Basename of lock file.
665 *
666 * @return 0 success, 1 already locked, -1 error
667 */
668 int
lockfile_lock_shared_nb(lockfile_t * lockfile,const gchar * lockfile_basename)669 lockfile_lock_shared_nb (lockfile_t *lockfile, const gchar *lockfile_basename)
670 {
671 g_debug ("%s: lock '%s'", __func__, lockfile_basename);
672 return lock_internal (lockfile, lockfile_basename, LOCK_SH | LOCK_NB, FALSE);
673 }
674
675 /**
676 * @brief Unlock a file.
677 *
678 * @param[in] lockfile Lockfile.
679 *
680 * @return 0 success, -1 error
681 */
682 int
lockfile_unlock(lockfile_t * lockfile)683 lockfile_unlock (lockfile_t *lockfile)
684 {
685 if (lockfile->name == NULL)
686 return 0;
687
688 assert (lockfile->fd);
689
690 g_debug ("%s: unlock '%s'", __func__, lockfile->name);
691
692 /* Close the lock file. */
693
694 if (close (lockfile->fd))
695 {
696 g_free (lockfile->name);
697 lockfile->name = NULL;
698 g_warning ("Failed to close lock file: %s", strerror (errno));
699 return -1;
700 }
701
702 /* Clear the lock file data. */
703
704 g_free (lockfile->name);
705 lockfile->name = NULL;
706
707 return 0;
708 }
709
710 /**
711 * @brief Check if a file is locked.
712 *
713 * @param[in] lockfile_basename Basename of lock file.
714 *
715 * @return 0 free, 1 locked, -1 error
716 */
717 int
lockfile_locked(const gchar * lockfile_basename)718 lockfile_locked (const gchar *lockfile_basename)
719 {
720 int ret;
721 lockfile_t lockfile;
722
723 g_debug ("%s: check '%s'", __func__, lockfile_basename);
724
725 ret = lockfile_lock_nb (&lockfile, lockfile_basename);
726 if ((ret == 0) && lockfile_unlock (&lockfile))
727 return -1;
728 return ret;
729 }
730
731
732 /* UUIDs. */
733
734 /**
735 * @brief Check whether a string is a UUID.
736 *
737 * @param[in] uuid Potential UUID.
738 *
739 * @return 1 yes, 0 no.
740 */
741 int
is_uuid(const char * uuid)742 is_uuid (const char *uuid)
743 {
744 while (*uuid) if (isxdigit (*uuid) || (*uuid == '-')) uuid++; else return 0;
745 return 1;
746 }
747
748
749 /* XML. */
750
751 /**
752 * @brief Create entity from XML file.
753 *
754 * @param[in] path Path to XML.
755 * @param[out] config Config tree.
756 *
757 * @return 0 success, -1 error.
758 */
759 int
parse_xml_file(const gchar * path,entity_t * config)760 parse_xml_file (const gchar *path, entity_t *config)
761 {
762 gsize xml_len;
763 char *xml;
764 GError *error;
765
766 /* Buffer the file. */
767
768 error = NULL;
769 g_file_get_contents (path,
770 &xml,
771 &xml_len,
772 &error);
773 if (error)
774 {
775 g_warning ("%s: Failed to read file: %s",
776 __func__,
777 error->message);
778 g_error_free (error);
779 return -1;
780 }
781
782 /* Parse the buffer into an entity. */
783
784 if (parse_entity (xml, config))
785 {
786 g_free (xml);
787 g_warning ("%s: Failed to parse XML", __func__);
788 return -1;
789 }
790 g_free (xml);
791
792 return 0;
793 }
794
795
796 /* Signals. */
797
798 /**
799 * @brief Setup signal handler.
800 *
801 * Exit on failure.
802 *
803 * @param[in] signal Signal.
804 * @param[in] handler Handler.
805 * @param[in] block Whether to block all other signals during handler.
806 */
807 void
setup_signal_handler(int signal,void (* handler)(int),int block)808 setup_signal_handler (int signal, void (*handler) (int), int block)
809 {
810 struct sigaction action;
811
812 memset (&action, '\0', sizeof (action));
813 if (block)
814 sigfillset (&action.sa_mask);
815 else
816 sigemptyset (&action.sa_mask);
817 action.sa_handler = handler;
818 if (sigaction (signal, &action, NULL) == -1)
819 {
820 g_critical ("%s: failed to register %s handler",
821 __func__, strsignal (signal));
822 exit (EXIT_FAILURE);
823 }
824 }
825
826 /**
827 * @brief Setup signal handler.
828 *
829 * Exit on failure.
830 *
831 * @param[in] signal Signal.
832 * @param[in] handler Handler.
833 * @param[in] block Whether to block all other signals during handler.
834 */
835 void
setup_signal_handler_info(int signal,void (* handler)(int,siginfo_t *,void *),int block)836 setup_signal_handler_info (int signal,
837 void (*handler) (int, siginfo_t *, void *),
838 int block)
839 {
840 struct sigaction action;
841
842 memset (&action, '\0', sizeof (action));
843 if (block)
844 sigfillset (&action.sa_mask);
845 else
846 sigemptyset (&action.sa_mask);
847 action.sa_flags |= SA_SIGINFO;
848 action.sa_sigaction = handler;
849 if (sigaction (signal, &action, NULL) == -1)
850 {
851 g_critical ("%s: failed to register %s handler",
852 __func__, strsignal (signal));
853 exit (EXIT_FAILURE);
854 }
855 }
856
857
858 /* Forking. */
859
860 /**
861 * @brief Fork, setting default handlers for TERM, INT and QUIT in child.
862 *
863 * This should be used for pretty much all processes forked directly from
864 * the main gvmd process, because the main process's signal handlers will
865 * not longer work, because the child does not use the pselect loop.
866 *
867 * @return PID from fork.
868 */
869 int
fork_with_handlers()870 fork_with_handlers ()
871 {
872 pid_t pid;
873
874 pid = fork ();
875 if (pid == 0)
876 {
877 setup_signal_handler (SIGTERM, SIG_DFL, 0);
878 setup_signal_handler (SIGINT, SIG_DFL, 0);
879 setup_signal_handler (SIGQUIT, SIG_DFL, 0);
880 }
881 return pid;
882 }
883