1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985
|
/* SPDX-FileCopyrightText: 2017-2023 Greenbone AG
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
/**
* @file
* @brief Implementation of logging methods.
*
* This file contains all methods needed for logging. To enable logging,
* methods in this file are called.
*
* The module reuses glib datatypes and api for memory management and logging.
*/
#include "logging.h"
#include "gvm_sentry.h" /* for gvm_sentry_log */
#include "logging_domain.h"
#include <errno.h> /* for errno */
#include <libgen.h> /* for dirname */
#include <stdio.h> /* for fflush, fprintf, stderr */
#include <stdlib.h> /* for atoi */
#include <string.h> /* for strcasecmp, strlen, strerror */
#define SYSLOG_NAMES
#include <syslog.h> /* for LOG_INFO, facilitynames, closelog, openlog */
#undef SYSLOG_NAMES
#include <time.h> /* for localtime, time, time_t */
#include <unistd.h> /* for getpid */
#undef G_LOG_DOMAIN
/**
* @brief GLib log domain.
*/
#define G_LOG_DOMAIN "libgvm base"
/**
* @brief Timezone to use for logs.
*
* NULL means to use the current timezone.
*/
static gchar *log_tz = NULL;
/**
* @brief Returns time as specified in time_fmt strftime format.
*
* @param time_fmt ptr to the string format to use. The strftime
* man page documents the conversion specification. An
* example time_fmt string is "%Y-%m-%d %H:%M:%S".
*
* @return NULL in case the format string is NULL. A ptr to a
* string that contains the formatted date time value.
* This value must be freed using glib's g_free.
*/
gchar *
get_time (gchar *time_fmt)
{
time_t now;
struct tm ts;
gchar buf[80], *original_tz;
if (!time_fmt)
return NULL;
if (log_tz)
{
original_tz = getenv ("TZ") ? g_strdup (getenv ("TZ")) : NULL;
setenv ("TZ", log_tz, 1);
tzset ();
}
/* Get the current time. */
now = time (NULL);
/* Format and print the time, "ddd yyyy-mm-dd hh:mm:ss zzz." */
localtime_r (&now, &ts);
strftime (buf, sizeof (buf), time_fmt, &ts);
if (log_tz)
{
/* Revert to stored TZ. */
if (original_tz)
{
setenv ("TZ", original_tz, 1);
g_free (original_tz);
tzset ();
}
else
unsetenv ("TZ");
}
return g_strdup_printf ("%s", buf);
}
/**
* @brief Return the integer corresponding to a log level string.
*
* @param level Level name or integer.
*
* @return Log level integer if level matches a level name, else 0.
*/
static gint
level_int_from_string (const gchar *level)
{
if (level && strlen (level) > 0)
{
if (level[0] >= '0' && level[0] <= '9')
return atoi (level);
if (strcasecmp (level, "critical") == 0)
return G_LOG_LEVEL_CRITICAL;
if (strcasecmp (level, "debug") == 0)
return G_LOG_LEVEL_DEBUG;
if (strcasecmp (level, "error") == 0)
return G_LOG_LEVEL_ERROR;
if (strcasecmp (level, "info") == 0)
return G_LOG_LEVEL_INFO;
if (strcasecmp (level, "message") == 0)
return G_LOG_LEVEL_MESSAGE;
if (strcasecmp (level, "warning") == 0)
return G_LOG_LEVEL_WARNING;
}
return 0;
}
/**
* @brief Return the integer corresponding to a syslog facility string.
*
* @param facility Facility name.
*
* @return Facility integer if facility matches a facility name, else
* LOG_LOCAL0.
*/
static gint
facility_int_from_string (const gchar *facility)
{
if (facility && strlen (facility) > 0)
{
int i = 0;
while (facilitynames[i].c_name != NULL)
{
if (g_ascii_strcasecmp (facility, facilitynames[i].c_name) == 0)
return facilitynames[i].c_val;
i++;
}
}
return LOG_LOCAL0;
}
/**
* @brief Loads parameters from a config file into a linked list.
*
* @param config_file A string containing the path to the configuration file
* to load.
*
* @return NULL in case the config file could not be loaded or an error
* occurred otherwise, a singly linked list of parameter groups
* is returned.
*/
GSList *
load_log_configuration (gchar *config_file)
{
GKeyFile *key_file;
GKeyFileFlags flags;
GError *error = NULL;
/* key_file *_has_* functions requires this. */
// FIXME: If a g_* function that takes error fails, then free error.
/* Groups found in the conf file. */
gchar **groups;
/* Temp variable to iterate over groups. */
gchar **group;
/* The link list for the structure above and it's tmp helper */
GSList *log_domain_list = NULL;
/* Create a new GKeyFile object and a bitwise list of flags. */
key_file = g_key_file_new ();
flags = G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS;
/* Load the GKeyFile from conf or return. */
if (!g_key_file_load_from_file (key_file, config_file, flags, &error))
{
g_error ("%s: %s", config_file, error->message);
}
/* Get all the groups available. */
groups = g_key_file_get_groups (key_file, NULL);
/* Point to the group head. */
group = groups;
/* Iterate till we get to the end of the array. */
while (*group != NULL)
{
/* Structure to hold per group settings. */
gvm_logging_domain_t *log_domain_entry =
gvm_logging_domain_new (g_strdup (*group));
/* Look for the prepend string. */
if (g_key_file_has_key (key_file, *group, "prepend", &error))
{
gvm_logging_domain_set_prepend_string (
log_domain_entry,
g_key_file_get_value (key_file, *group, "prepend", &error));
}
/* Look for the log_separator string. */
if (g_key_file_has_key (key_file, *group, "separator", &error))
{
gvm_logging_domain_set_prepend_separator (
log_domain_entry,
g_key_file_get_value (key_file, *group, "separator", &error));
}
/* Look for the prepend time format string. */
if (g_key_file_has_key (key_file, *group, "prepend_time_format", &error))
{
gvm_logging_domain_set_prepend_time_format (
log_domain_entry,
g_key_file_get_value (key_file, *group, "prepend_time_format",
&error));
}
/* Look for the log file string. */
if (g_key_file_has_key (key_file, *group, "file", &error))
{
gvm_logging_domain_set_log_file (
log_domain_entry,
g_key_file_get_value (key_file, *group, "file", &error));
}
/* Look for the prepend log level string. */
if (g_key_file_has_key (key_file, *group, "level", &error))
{
gchar *level;
level = g_key_file_get_value (key_file, *group, "level", &error);
level = g_strchug (level);
gvm_logging_domain_set_default_level (log_domain_entry,
level_int_from_string (level));
g_free (level);
}
/* Look for the syslog_facility string. */
if (g_key_file_has_key (key_file, *group, "syslog_facility", &error))
{
gvm_logging_domain_set_syslog_facility (
log_domain_entry,
g_key_file_get_value (key_file, *group, "syslog_facility", &error));
}
else
gvm_logging_domain_set_syslog_facility (log_domain_entry,
g_strdup ("local0"));
/* Look for the syslog_ident string. */
if (g_key_file_has_key (key_file, *group, "syslog_ident", &error))
{
gvm_logging_domain_set_syslog_ident (
log_domain_entry,
g_key_file_get_value (key_file, *group, "syslog_ident", &error));
}
else
gvm_logging_domain_set_syslog_ident (log_domain_entry,
g_strdup (*group));
/* Attach the struct to the list. */
log_domain_list = g_slist_prepend (log_domain_list, log_domain_entry);
group++;
}
/* Free the groups array. */
g_strfreev (groups);
/* Free the key file. */
g_key_file_free (key_file);
return log_domain_list;
}
/**
* @brief Frees all resources loaded by the config loader.
*
* @param log_domain_list Head of the link list.
*/
void
free_log_configuration (GSList *log_domain_list)
{
GSList *log_domain_list_tmp;
/* Free the struct fields then the struct and then go the next
* item in the link list.
*/
/* Go to the head of the list. */
log_domain_list_tmp = log_domain_list;
while (log_domain_list_tmp != NULL)
{
gvm_logging_domain_t *log_domain_entry;
/* Get the list data which is an gvm_logging_t struct. */
log_domain_entry = log_domain_list_tmp->data;
/* Free the struct contents. */
gvm_logging_domain_free (log_domain_entry);
/* Go to the next item. */
log_domain_list_tmp = g_slist_next (log_domain_list_tmp);
}
/* Free the link list. */
g_slist_free (log_domain_list);
}
/**
* @brief Returns immediately.
*
* @param log_domain A string containing the message's log domain.
* @param log_level Flags defining the message's log level.
* @param message A string containing the log message.
* @param gvm_log_config_list A pointer to the configuration linked list.
*/
void
gvm_log_silent (const char *log_domain, GLogLevelFlags log_level,
const char *message, gpointer gvm_log_config_list)
{
(void) log_domain;
(void) log_level;
(void) message;
(void) gvm_log_config_list;
return;
}
static GMutex *logger_mutex = NULL;
/**
* @brief Initialize logger_mutex mutex if it was not done before.
*/
static void
gvm_log_lock_init (void)
{
if (logger_mutex == NULL)
{
logger_mutex = g_malloc (sizeof (*logger_mutex));
g_mutex_init (logger_mutex);
}
}
/**
* @brief Try to lock logger_mutex.
*/
void
gvm_log_lock (void)
{
/* Initialize logger lock if not done already. */
gvm_log_lock_init ();
g_mutex_lock (logger_mutex);
}
/**
* @brief Unlock logger_mutex.
*/
void
gvm_log_unlock (void)
{
g_mutex_unlock (logger_mutex);
}
static char *reference = NULL;
/**
* @brief Set the log reference object.
*
* In order to be able to see which logs are related to each other, we define a
* common reference for them. E.g. when multiple scans in OpenVAS are running
* simultaniousely it is possible to detect all log corresponding to the same
* scan. The log reference is optional and must be set before calling
* setup_log_handlers. The data given must be freed by calling
* free_log_reference(). If called multiple times the old reference gets freed
* and the new one is set instead.
*
* @param ref
*/
void
set_log_reference (char *ref)
{
if (reference)
g_free ((char *) reference);
reference = ref;
}
/**
* @brief Get the log reference object
*
* This function returns the current log reference. This enables the possibility
* to save or modify the current reference value.
*
* @return char*
*/
char *
get_log_reference (void)
{
return (char *) reference;
}
/**
* @brief Free the log reference object
*
* The reference object is used to detect corresponding logs.
*
*/
void
free_log_reference (void)
{
if (reference)
g_free ((char *) reference);
reference = NULL;
}
/**
* @brief Creates the formatted string and outputs it to the log destination.
*
* @param log_domain A string containing the message's log domain.
* @param log_level Flags defining the message's log level.
* @param message A string containing the log message.
* @param gvm_log_config_list A pointer to the configuration linked list.
*/
void
gvm_log_func (const char *log_domain, GLogLevelFlags log_level,
const char *message, gpointer gvm_log_config_list)
{
gchar *prepend;
gchar *prepend_buf;
gchar *prepend_tmp;
gchar *prepend_tmp1;
gchar *tmp;
gchar *tmpstr;
int messagelen;
/* For link list operations. */
GSList *log_domain_list_tmp;
gvm_logging_domain_t *log_domain_entry = NULL;
/* Channel to log through. */
GIOChannel *channel = NULL;
GError *error = NULL;
/* The default parameters to be used. The group '*' will override
* these defaults if it's found.
*/
gchar *prepend_format = "%t %s %p - ";
gchar *time_format = "%Y-%m-%d %Hh%M.%S %Z";
gchar *log_separator = ":";
gchar *log_file = "-";
GLogLevelFlags default_level = G_LOG_LEVEL_DEBUG;
gchar *syslog_facility = "local0";
gchar *syslog_ident = NULL;
/* Let's load the default configuration file directives from the
* linked list. Scanning the link list twice is inefficient but
* leaves the source cleaner.
*/
if (gvm_log_config_list != NULL && log_domain != NULL)
{
/* Go to the head of the list. */
log_domain_list_tmp = (GSList *) gvm_log_config_list;
while (log_domain_list_tmp != NULL)
{
gvm_logging_domain_t *entry;
entry = log_domain_list_tmp->data;
/* Override defaults if the current linklist group name is '*'. */
if (g_ascii_strcasecmp (gvm_logging_domain_get_log_domain (entry),
"*")
== 0)
{
/* Get the list data for later use. */
log_domain_entry = entry;
/* Override defaults if the group items are not null. */
if (gvm_logging_domain_get_prepend_string (log_domain_entry))
prepend_format =
gvm_logging_domain_get_prepend_string (log_domain_entry);
if (gvm_logging_domain_get_prepend_time_format (log_domain_entry))
time_format =
gvm_logging_domain_get_prepend_time_format (log_domain_entry);
if (gvm_logging_domain_get_log_file (log_domain_entry))
log_file = gvm_logging_domain_get_log_file (log_domain_entry);
if (gvm_logging_domain_get_default_level (log_domain_entry))
default_level =
*gvm_logging_domain_get_default_level (log_domain_entry);
if (gvm_logging_domain_get_log_channel (log_domain_entry))
channel = gvm_logging_domain_get_log_channel (log_domain_entry);
if (gvm_logging_domain_get_syslog_facility (log_domain_entry))
syslog_facility =
gvm_logging_domain_get_syslog_facility (log_domain_entry);
if (gvm_logging_domain_get_prepend_separator (log_domain_entry))
log_separator =
gvm_logging_domain_get_prepend_separator (log_domain_entry);
break;
}
/* Go to the next item. */
log_domain_list_tmp = g_slist_next (log_domain_list_tmp);
}
}
/* Let's load the configuration file directives if a linked list item for
* the log domain group exists.
*/
if (gvm_log_config_list != NULL && log_domain != NULL)
{
/* Go to the head of the list. */
log_domain_list_tmp = (GSList *) gvm_log_config_list;
while (log_domain_list_tmp != NULL)
{
gvm_logging_domain_t *entry;
entry = log_domain_list_tmp->data;
/* Search for the log domain in the link list. */
if (g_ascii_strcasecmp (gvm_logging_domain_get_log_domain (entry),
log_domain)
== 0)
{
/* Get the list data which is an gvm_logging_t struct. */
log_domain_entry = entry;
/* Get the struct contents. */
if (gvm_logging_domain_get_prepend_string (log_domain_entry))
prepend_format =
gvm_logging_domain_get_prepend_string (log_domain_entry);
if (gvm_logging_domain_get_prepend_time_format (log_domain_entry))
time_format =
gvm_logging_domain_get_prepend_time_format (log_domain_entry);
if (gvm_logging_domain_get_log_file (log_domain_entry))
log_file = gvm_logging_domain_get_log_file (log_domain_entry);
if (gvm_logging_domain_get_default_level (log_domain_entry))
default_level =
*gvm_logging_domain_get_default_level (log_domain_entry);
if (gvm_logging_domain_get_log_channel (log_domain_entry))
channel = gvm_logging_domain_get_log_channel (log_domain_entry);
if (gvm_logging_domain_get_syslog_facility (log_domain_entry))
syslog_facility =
gvm_logging_domain_get_syslog_facility (log_domain_entry);
if (gvm_logging_domain_get_syslog_ident (log_domain_entry))
syslog_ident =
gvm_logging_domain_get_syslog_ident (log_domain_entry);
if (gvm_logging_domain_get_prepend_separator (log_domain_entry))
log_separator =
gvm_logging_domain_get_prepend_separator (log_domain_entry);
break;
}
/* Go to the next item. */
log_domain_list_tmp = g_slist_next (log_domain_list_tmp);
}
}
/* If the current log entry is less severe than the specified log level,
* let's exit.
*/
if (default_level < log_level)
return;
/* Prepend buf is a newly allocated empty string. Makes life easier. */
prepend_buf = g_strdup ("");
/* Make the tmp pointer (for iteration) point to the format string. */
tmp = prepend_format;
while (*tmp != '\0')
{
/* If the current char is a % and the next one is a p, get the pid. */
if ((*tmp == '%') && (*(tmp + 1) == 'p'))
{
if (reference)
{
prepend_tmp =
g_strdup_printf ("%s%d%s%s", prepend_buf, (int) getpid (),
log_separator, reference);
}
else
{
/* Use g_strdup, a new string returned. Store it in a tmp var
* until we free the old one. */
prepend_tmp =
g_strdup_printf ("%s%d", prepend_buf, (int) getpid ());
}
/* Free the old string. */
g_free (prepend_buf);
/* Point the buf ptr to the new string. */
prepend_buf = prepend_tmp;
/* Skip over the two chars we've processed '%p'. */
tmp += 2;
}
else if ((*tmp == '%') && (*(tmp + 1) == 't'))
{
/* Get time returns a newly allocated string.
* Store it in a tmp var.
*/
prepend_tmp1 = get_time (time_format);
if (!prepend_tmp1)
{
prepend_tmp1 = g_strdup ("");
}
/* Use g_strdup. New string returned. Store it in a tmp var until
* we free the old one.
*/
prepend_tmp = g_strdup_printf ("%s%s", prepend_buf, prepend_tmp1);
/* Free the time tmp var. */
g_free (prepend_tmp1);
/* Free the old string. */
g_free (prepend_buf);
/* Point the buf ptr to the new string. */
prepend_buf = prepend_tmp;
/* Skip over the two chars we've processed '%t.' */
tmp += 2;
}
else if ((*tmp == '%') && (*(tmp + 1) == 's'))
{
/* Use g_strdup. New string returned. Store it in a tmp var until
* we free the old one.
*/
prepend_tmp = g_strdup_printf ("%s%s", prepend_buf, log_separator);
/* Free the old string. */
g_free (prepend_buf);
/* Point the buf ptr to the new string. */
prepend_buf = prepend_tmp;
/* Skip over the two chars we've processed '%s.' */
tmp += 2;
}
else
{
/* Jump to the next character. */
tmp++;
}
}
/* Step through all possible messages prefixing them with an appropriate
* tag.
*/
switch (log_level)
{
case G_LOG_FLAG_RECURSION:
prepend = g_strdup_printf ("RECURSION%s%s", log_separator, prepend_buf);
break;
case G_LOG_FLAG_FATAL:
prepend = g_strdup_printf ("FATAL%s%s", log_separator, prepend_buf);
break;
case G_LOG_LEVEL_ERROR:
prepend = g_strdup_printf ("ERROR%s%s", log_separator, prepend_buf);
break;
case G_LOG_LEVEL_CRITICAL:
prepend = g_strdup_printf ("CRITICAL%s%s", log_separator, prepend_buf);
break;
case G_LOG_LEVEL_WARNING:
prepend = g_strdup_printf ("WARNING%s%s", log_separator, prepend_buf);
break;
case G_LOG_LEVEL_MESSAGE:
prepend = g_strdup_printf ("MESSAGE%s%s", log_separator, prepend_buf);
break;
case G_LOG_LEVEL_INFO:
prepend = g_strdup_printf (" INFO%s%s", log_separator, prepend_buf);
break;
case G_LOG_LEVEL_DEBUG:
prepend = g_strdup_printf (" DEBUG%s%s", log_separator, prepend_buf);
break;
default:
prepend = g_strdup_printf ("UNKNOWN%s%s", log_separator, prepend_buf);
break;
}
/* If the current log entry is more severe than the specified log
* level, print out the message. In case MESSAGE already ends in a
* LF and there is not only the LF, remove the LF to avoid empty
* lines in the log.
*/
messagelen = message ? strlen (message) : 0;
if (messagelen > 1 && message[messagelen - 1] == '\n')
messagelen--;
tmpstr = g_strdup_printf ("%s%s%s%s %.*s\n", log_domain ? log_domain : "",
log_separator, prepend, log_separator, messagelen,
message);
g_free (prepend);
if (log_level <= G_LOG_LEVEL_WARNING)
gvm_sentry_log (message);
gvm_log_lock ();
/* Output everything to stderr if logfile is NULL, an empty string or "-". */
if (!log_file || g_ascii_strcasecmp (log_file, "-") == 0
|| !g_strcmp0 (log_file, ""))
{
fprintf (stderr, "%s", tmpstr);
fflush (stderr);
}
/* Output everything to syslog if logfile is "syslog" */
else if (g_ascii_strcasecmp (log_file, "syslog") == 0)
{
int facility = facility_int_from_string (syslog_facility);
int syslog_level = LOG_INFO;
openlog (syslog_ident, LOG_CONS | LOG_PID | LOG_NDELAY, facility);
switch (log_level)
{
case G_LOG_FLAG_FATAL:
syslog_level = LOG_ALERT;
break;
case G_LOG_LEVEL_ERROR:
syslog_level = LOG_ERR;
break;
case G_LOG_LEVEL_CRITICAL:
syslog_level = LOG_CRIT;
break;
case G_LOG_LEVEL_WARNING:
syslog_level = LOG_WARNING;
break;
case G_LOG_LEVEL_MESSAGE:
syslog_level = LOG_NOTICE;
break;
case G_LOG_LEVEL_INFO:
syslog_level = LOG_INFO;
break;
case G_LOG_LEVEL_DEBUG:
syslog_level = LOG_DEBUG;
break;
default:
syslog_level = LOG_INFO;
break;
}
/* Syslog doesn't support messages longer than 1kb. The overflow data
will not be logged or will be shown in the hypervisor console
if it runs on a virtual machine. */
if (messagelen > 1000)
{
int pos;
char *message_aux, *message_aux2;
char buffer[1000];
message_aux2 = g_strdup (message);
message_aux = message_aux2;
for (pos = 0; pos <= messagelen; pos = pos + sizeof (buffer) - 1)
{
memcpy (buffer, message_aux, sizeof (buffer) - 1);
buffer[sizeof (buffer) - 1] = '\0';
message_aux = &(message_aux[sizeof (buffer) - 1]);
syslog (syslog_level, "%s", buffer);
}
g_free (message_aux2);
}
else
syslog (syslog_level, "%s", message);
closelog ();
}
else
{
/* Open a channel and store it in the struct or
* retrieve and use an already existing channel.
*/
if (channel == NULL)
{
channel = g_io_channel_new_file (log_file, "a", &error);
if (!channel)
{
gchar *log = g_strdup (log_file);
gchar *dir = dirname (log);
/* Check error. In case of the directory does not exist, it will
* be handle below. In other case a message is printed to the
* stderr since the channel is still not created/accessible.
*/
if (error->code != G_FILE_ERROR_NOENT)
fprintf (stderr, "Can not open '%s' logfile: %s\n", log_file,
error->message);
g_error_free (error);
/* Ensure directory exists. */
if (g_mkdir_with_parents (dir, 0755)) /* "rwxr-xr-x" */
{
g_warning ("Failed to create log file directory %s: %s", dir,
strerror (errno));
g_free (log);
g_free (tmpstr);
g_free (prepend_buf);
return;
}
g_free (log);
/* Try again. */
error = NULL;
channel = g_io_channel_new_file (log_file, "a", &error);
if (!channel)
{
g_error ("Can not open '%s' logfile: %s", log_file,
error->message);
}
}
/* Store it in the struct for later use. */
if (log_domain_entry != NULL)
gvm_logging_domain_set_log_channel (log_domain_entry, channel);
}
g_io_channel_write_chars (channel, (const gchar *) tmpstr, -1, NULL,
&error);
g_io_channel_flush (channel, NULL);
}
gvm_log_unlock ();
g_free (tmpstr);
g_free (prepend_buf);
}
/**
* @brief This function logs debug messages from gnutls.
*
* @param level GnuTLS log level (integer from 0 to 99 according to GnuTLS
* documentation.
* @param message GnuTLS log message.
*
* To enable GNUTLS debug messages, the environment variable @c
* OPENVAS_GNUTLS_DEBUG is to be set to the desired log level as
* described in the GNUTLS manual.
*/
void
log_func_for_gnutls (int level, const char *message)
{
g_log ("x gnutls", G_LOG_LEVEL_INFO, "tls(%d): %s", level, message);
}
/**
* @brief Check permissions of log file and log file directory.
*
* Do not check permissions if log file is syslog or empty string.
*
* @param log_domain_entry Log domain entry.
*
* @return 0 on success, -1 on error.
*/
static int
check_log_file (gvm_logging_domain_t *log_domain_entry)
{
GIOChannel *channel = NULL;
const gchar *log_file;
log_file = gvm_logging_domain_get_log_file (log_domain_entry);
// No log file was specified, log file is empty or set to "-" then
// stderr will be used as default later on. See gvm_log_func.
if (!log_file || g_ascii_strcasecmp (log_file, "-") == 0
|| !g_strcmp0 (log_file, ""))
return 0;
// If syslog is used we do not need to check the log file permissions.
if (g_ascii_strcasecmp (log_file, "syslog") == 0)
return 0;
channel = g_io_channel_new_file (log_file, "a", NULL);
if (!channel)
{
gchar *log = g_strdup (log_file);
gchar *dir = dirname (log);
/* Ensure directory exists. */
if (g_mkdir_with_parents (dir, 0755)) /* "rwxr-xr-x" */
{
g_free (log);
return -1;
}
g_free (log);
/* Try again. */
channel = g_io_channel_new_file (log_file, "a", NULL);
if (!channel)
return -1;
}
g_io_channel_shutdown (channel, TRUE, NULL);
g_io_channel_unref (channel);
return 0;
}
/**
* @brief Set the log timezone.
*
* This is the timezone used for dates in log messages. If NULL then
* the current timezone is used.
*
* @param tz Timezone.
*/
void
set_log_tz (const gchar *tz)
{
g_free (log_tz);
log_tz = tz ? g_strdup (tz) : NULL;
}
static int
setup_log_handlers_internal (GSList *gvm_log_config_list, GLogFunc log_func,
GLogFunc default_log_func,
GLogFunc default_domain_log_func)
{
GSList *log_domain_list_tmp;
int err;
int ret = 0;
if (gvm_log_config_list != NULL)
{
/* Go to the head of the list. */
log_domain_list_tmp = (GSList *) gvm_log_config_list;
while (log_domain_list_tmp != NULL)
{
gvm_logging_domain_t *log_domain_entry;
/* Get the list data which is an gvm_logging_t struct. */
log_domain_entry = log_domain_list_tmp->data;
err = check_log_file (log_domain_entry);
if (err)
{
ret = -1;
/* Go to the next item. */
log_domain_list_tmp = g_slist_next (log_domain_list_tmp);
continue;
}
if (g_ascii_strcasecmp (
gvm_logging_domain_get_log_domain (log_domain_entry), "*"))
{
g_log_set_handler (
gvm_logging_domain_get_log_domain (log_domain_entry),
(GLogLevelFlags) (G_LOG_LEVEL_DEBUG | G_LOG_LEVEL_INFO
| G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_WARNING
| G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_ERROR
| G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION),
(GLogFunc) log_func, gvm_log_config_list);
}
else
{
g_log_set_default_handler (default_log_func, gvm_log_config_list);
}
/* Go to the next item. */
log_domain_list_tmp = g_slist_next (log_domain_list_tmp);
}
}
g_log_set_handler (
"",
(GLogLevelFlags) (G_LOG_LEVEL_DEBUG | G_LOG_LEVEL_INFO | G_LOG_LEVEL_MESSAGE
| G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL
| G_LOG_LEVEL_ERROR | G_LOG_FLAG_FATAL
| G_LOG_FLAG_RECURSION),
default_domain_log_func, gvm_log_config_list);
return ret;
}
/**
* @brief Sets up routing of logdomains to log handlers.
*
* Iterates over the link list and adds the groups to the handler.
*
* @param gvm_log_config_list A pointer to the configuration linked list.
*
* @return 0 on success, -1 if not able to create log file directory or open log
* file for some domain.
*/
int
setup_log_handlers (GSList *gvm_log_config_list)
{
return setup_log_handlers_internal (gvm_log_config_list, gvm_log_func,
gvm_log_func, gvm_log_func);
}
|