+
+#if !defined(GNUNET_CULL_LOGGING)
+/**
+ * Utility function - reallocates logdefs array to be twice as large.
+ */
+static void
+resize_logdefs ()
+{
+ logdefs_size = (logdefs_size + 1) * 2;
+ logdefs = GNUNET_realloc (logdefs, logdefs_size * sizeof (struct LogDef));
+}
+
+
+/**
+ * Abort the process, generate a core dump if possible.
+ */
+void
+GNUNET_abort_ ()
+{
+#if WINDOWS
+ DebugBreak ();
+#endif
+ abort ();
+}
+
+
+/**
+ * Rotate logs, deleting the oldest log.
+ *
+ * @param new_name new name to add to the rotation
+ */
+static void
+log_rotate (const char *new_name)
+{
+ static char *rotation[ROTATION_KEEP];
+ static unsigned int rotation_off;
+ char *discard;
+
+ if ('\0' == *new_name)
+ return; /* not a real log file name */
+ discard = rotation[rotation_off % ROTATION_KEEP];
+ if (NULL != discard)
+ {
+ /* Note: can't log errors during logging (recursion!), so this
+ operation MUST silently fail... */
+ (void) UNLINK (discard);
+ GNUNET_free (discard);
+ }
+ rotation[rotation_off % ROTATION_KEEP] = GNUNET_strdup (new_name);
+ rotation_off++;
+}
+
+
+/**
+ * Setup the log file.
+ *
+ * @param tm timestamp for which we should setup logging
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+static int
+setup_log_file (const struct tm *tm)
+{
+ static char last_fn[PATH_MAX + 1];
+ char fn[PATH_MAX + 1];
+ int altlog_fd;
+ int dup_return;
+ FILE *altlog;
+ char *leftsquare;
+
+ if (NULL == log_file_name)
+ return GNUNET_SYSERR;
+ if (0 == strftime (fn, sizeof (fn), log_file_name, tm))
+ return GNUNET_SYSERR;
+ leftsquare = strrchr (fn, '[');
+ if ( (NULL != leftsquare) && (']' == leftsquare[1]) )
+ {
+ char *logfile_copy = GNUNET_strdup (fn);
+
+ logfile_copy[leftsquare - fn] = '\0';
+ logfile_copy[leftsquare - fn + 1] = '\0';
+ snprintf (fn,
+ PATH_MAX,
+ "%s%d%s",
+ logfile_copy,
+ getpid (),
+ &logfile_copy[leftsquare - fn + 2]);
+ GNUNET_free (logfile_copy);
+ }
+ if (0 == strcmp (fn, last_fn))
+ return GNUNET_OK; /* no change */
+ log_rotate (last_fn);
+ strcpy (last_fn, fn);
+#if WINDOWS
+ altlog_fd = OPEN (fn, O_APPEND |
+ O_BINARY |
+ O_WRONLY | O_CREAT,
+ _S_IREAD | _S_IWRITE);
+#else
+ altlog_fd = OPEN (fn, O_APPEND |
+ O_WRONLY | O_CREAT,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+#endif
+ if (-1 != altlog_fd)
+ {
+ if (NULL != GNUNET_stderr)
+ fclose (GNUNET_stderr);
+ dup_return = dup2 (altlog_fd, 2);
+ (void) close (altlog_fd);
+ if (-1 != dup_return)
+ {
+ altlog = fdopen (2, "ab");
+ if (NULL == altlog)
+ {
+ (void) close (2);
+ altlog_fd = -1;
+ }
+ }
+ else
+ {
+ altlog_fd = -1;
+ }
+ }
+ if (-1 == altlog_fd)
+ {
+ GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", fn);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_stderr = altlog;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Utility function - adds a parsed definition to logdefs array.
+ *
+ * @param component see struct LogDef, can't be NULL
+ * @param file see struct LogDef, can't be NULL
+ * @param function see struct LogDef, can't be NULL
+ * @param from_line see struct LogDef
+ * @param to_line see struct LogDef
+ * @param level see struct LogDef, must be >= 0
+ * @param force see struct LogDef
+ * @return 0 on success, regex-specific error otherwise
+ */
+static int
+add_definition (const char *component,
+ const char *file,
+ const char *function,
+ int from_line,
+ int to_line,
+ int level,
+ int force)
+{
+ struct LogDef n;
+ int r;
+
+ if (logdefs_size == logdefs_len)
+ resize_logdefs ();
+ memset (&n, 0, sizeof (n));
+ if (0 == strlen (component))
+ component = (char *) ".*";
+ r = regcomp (&n.component_regex, (const char *) component, REG_NOSUB);
+ if (0 != r)
+ {
+ return r;
+ }
+ if (0 == strlen (file))
+ file = (char *) ".*";
+ r = regcomp (&n.file_regex, (const char *) file, REG_NOSUB);
+ if (0 != r)
+ {
+ regfree (&n.component_regex);
+ return r;
+ }
+ if ((NULL == function) || (0 == strlen (function)))
+ function = (char *) ".*";
+ r = regcomp (&n.function_regex, (const char *) function, REG_NOSUB);
+ if (0 != r)
+ {
+ regfree (&n.component_regex);
+ regfree (&n.file_regex);
+ return r;
+ }
+ n.from_line = from_line;
+ n.to_line = to_line;
+ n.level = level;
+ n.force = force;
+ logdefs[logdefs_len++] = n;
+ return 0;
+}
+
+
+/**
+ * Decides whether a particular logging call should or should not be allowed
+ * to be made. Used internally by GNUNET_log*()
+ *
+ * @param caller_level loglevel the caller wants to use
+ * @param comp component name the caller uses (NULL means that global
+ * component name is used)
+ * @param file file name containing the logging call, usually __FILE__
+ * @param function function which tries to make a logging call,
+ * usually __FUNCTION__
+ * @param line line at which the call is made, usually __LINE__
+ * @return 0 to disallow the call, 1 to allow it
+ */
+int
+GNUNET_get_log_call_status (int caller_level,
+ const char *comp,
+ const char *file,
+ const char *function,
+ int line)
+{
+ struct LogDef *ld;
+ int i;
+ int force_only;
+
+ if (NULL == comp)
+ /* Use default component */
+ comp = component_nopid;
+
+ /* We have no definitions to override globally configured log level,
+ * so just use it right away.
+ */
+ if ( (min_level >= 0) && (GNUNET_NO == gnunet_force_log_present) )
+ return caller_level <= min_level;
+
+ /* Only look for forced definitions? */
+ force_only = min_level >= 0;
+ for (i = 0; i < logdefs_len; i++)
+ {
+ ld = &logdefs[i];
+ if (( (!force_only) || ld->force) &&
+ (line >= ld->from_line && line <= ld->to_line) &&
+ (0 == regexec (&ld->component_regex, comp, 0, NULL, 0)) &&
+ (0 == regexec (&ld->file_regex, file, 0, NULL, 0)) &&
+ (0 == regexec (&ld->function_regex, function, 0, NULL, 0)))
+ {
+ /* We're finished */
+ return caller_level <= ld->level;
+ }
+ }
+ /* No matches - use global level, if defined */
+ if (min_level >= 0)
+ return caller_level <= min_level;
+ /* All programs/services previously defaulted to WARNING.
+ * Now WE default to WARNING, and THEY default to NULL.
+ */
+ return caller_level <= GNUNET_ERROR_TYPE_WARNING;
+}
+
+
+/**
+ * Utility function - parses a definition
+ *
+ * Definition format:
+ * component;file;function;from_line-to_line;level[/component...]
+ * All entries are mandatory, but may be empty.
+ * Empty entries for component, file and function are treated as
+ * "matches anything".
+ * Empty line entry is treated as "from 0 to INT_MAX"
+ * Line entry with only one line is treated as "this line only"
+ * Entry for level MUST NOT be empty.
+ * Entries for component, file and function that consist of a
+ * single character "*" are treated (at the moment) the same way
+ * empty entries are treated (wildcard matching is not implemented (yet?)).
+ * file entry is matched to the end of __FILE__. That is, it might be
+ * a base name, or a base name with leading directory names (some compilers
+ * define __FILE__ to absolute file path).
+ *
+ * @param constname name of the environment variable from which to get the
+ * string to be parsed
+ * @param force 1 if definitions found in constname are to be forced
+ * @return number of added definitions
+ */
+static int
+parse_definitions (const char *constname, int force)
+{
+ char *def;
+ const char *tmp;
+ char *comp = NULL;
+ char *file = NULL;
+ char *function = NULL;
+ char *p;
+ char *start;
+ char *t;
+ short state;
+ int level;
+ int from_line, to_line;
+ int counter = 0;
+ int keep_looking = 1;
+
+ tmp = getenv (constname);
+ if (NULL == tmp)
+ return 0;
+ def = GNUNET_strdup (tmp);
+ from_line = 0;
+ to_line = INT_MAX;
+ for (p = def, state = 0, start = def; keep_looking; p++)
+ {
+ switch (p[0])
+ {
+ case ';': /* found a field separator */
+ p[0] = '\0';
+ switch (state)
+ {
+ case 0: /* within a component name */
+ comp = start;
+ break;
+ case 1: /* within a file name */
+ file = start;
+ break;
+ case 2: /* within a function name */
+ /* after a file name there must be a function name */
+ function = start;
+ break;
+ case 3: /* within a from-to line range */
+ if (strlen (start) > 0)
+ {
+ errno = 0;
+ from_line = strtol (start, &t, 10);
+ if ( (0 != errno) || (from_line < 0) )
+ {
+ GNUNET_free (def);
+ return counter;
+ }
+ if ( (t < p) && ('-' == t[0]) )
+ {
+ errno = 0;
+ start = t + 1;
+ to_line = strtol (start, &t, 10);
+ if ( (0 != errno) || (to_line < 0) || (t != p) )
+ {
+ GNUNET_free (def);
+ return counter;
+ }
+ }
+ else /* one number means "match this line only" */
+ to_line = from_line;
+ }
+ else /* default to 0-max */
+ {
+ from_line = 0;
+ to_line = INT_MAX;
+ }
+ break;
+ }
+ start = p + 1;
+ state++;
+ break;
+ case '\0': /* found EOL */
+ keep_looking = 0;
+ /* fall through to '/' */
+ case '/': /* found a definition separator */
+ switch (state)
+ {
+ case 4: /* within a log level */
+ p[0] = '\0';
+ state = 0;
+ level = get_type ((const char *) start);
+ if ( (GNUNET_ERROR_TYPE_INVALID == level) ||
+ (GNUNET_ERROR_TYPE_UNSPECIFIED == level) ||
+ (0 != add_definition (comp, file, function, from_line, to_line,
+ level, force)) )
+ {
+ GNUNET_free (def);
+ return counter;
+ }
+ counter++;
+ start = p + 1;
+ break;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ GNUNET_free (def);
+ return counter;
+}
+
+
+/**
+ * Utility function - parses GNUNET_LOG and GNUNET_FORCE_LOG.
+ */
+static void
+parse_all_definitions ()
+{
+ if (GNUNET_NO == gnunet_log_parsed)
+ parse_definitions ("GNUNET_LOG", 0);
+ gnunet_log_parsed = GNUNET_YES;
+ if (GNUNET_NO == gnunet_force_log_parsed)
+ gnunet_force_log_present =
+ parse_definitions ("GNUNET_FORCE_LOG", 1) > 0 ? GNUNET_YES : GNUNET_NO;
+ gnunet_force_log_parsed = GNUNET_YES;
+}
+#endif
+
+