- if (!filename)
- fp = stderr;
- else {
- /* Purge the log file if the max size is defined. */
- if (maxsize) {
- fp = fopen(filename, "r");
- if (fp) {
- int filelen;
-
- filelen = fseek(fp, (off_t)0L, SEEK_END);
- fseek(fp, (off_t)0L, SEEK_SET);
-
- /* Purge? */
- if (filelen >= maxsize)
- unlink(filename);
-
- fclose(fp);
- }
- }
-
- /* Open the log file */
- if ((fp = fopen(filename, "a+")) == NULL) {
- fprintf(stderr, "warning: could not open log file "
- "%s: %s\n", filename, strerror(errno));
- fprintf(stderr, "warning: log messages will be displayed on "
- "the screen\n");
- fp = stderr;
+/* Given an open log file, checks the size and rotates it if there is a
+ * max size set lower then the current size */
+static void silc_log_checksize(SilcLog log)
+{
+ char newname[127];
+ long size;
+
+ if (!log || !log->fp || !log->maxsize)
+ return; /* we are not interested */
+
+ if ((size = ftell(log->fp)) < 0) {
+ /* OMG, EBADF is here.. we'll try our best.. */
+ FILE *oldfp = log->fp;
+ fclose(oldfp); /* we can discard the error */
+ log->fp = NULL; /* make sure we don't get here recursively */
+ SILC_LOG_ERROR(("Error while checking size of the log file %s, fp=%p",
+ log->filename, oldfp));
+ return;
+ }
+ if (size < log->maxsize)
+ return;
+
+ /* It's too big */
+ fprintf(log->fp, "[%s] [%s] Cycling log file, over max "
+ "logsize (%lu kilobytes)\n",
+ silc_get_time(0), log->typename, (unsigned long)log->maxsize / 1024);
+ fflush(log->fp);
+ fclose(log->fp);
+ memset(newname, 0, sizeof(newname));
+ snprintf(newname, sizeof(newname) - 1, "%s.old", log->filename);
+ unlink(newname);
+
+ /* I heard the following syscall may cause portability issues, but I don't
+ * have any other solution since SILC library doesn't provide any other
+ * function like this. -Johnny */
+ rename(log->filename, newname);
+ if (!(log->fp = fopen(log->filename, "w")))
+ SILC_LOG_WARNING(("Couldn't reopen logfile %s for type \"%s\": %s",
+ log->filename, log->typename, strerror(errno)));
+#ifdef HAVE_CHMOD
+ chmod(log->filename, 0600);
+#endif /* HAVE_CHMOD */
+}
+
+/* Reset a logging channel (close and reopen) */
+
+static bool silc_log_reset(SilcLog log)
+{
+ if (!log) return FALSE;
+ if (log->fp) {
+ fflush(log->fp);
+ fclose(log->fp);
+ }
+ if (!log->filename[0]) return FALSE;
+ if (!(log->fp = fopen(log->filename, "a+"))) {
+ SILC_LOG_WARNING(("Couldn't reset logfile %s for type \"%s\": %s",
+ log->filename, log->typename, strerror(errno)));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Internal timeout callback to flush log channels and check file sizes */
+
+SILC_TASK_CALLBACK(silc_log_fflush_callback)
+{
+ unsigned int u;
+ if (!silc_log_quick) {
+ silc_log_flush_all();
+ SILC_FOREACH_LOG(u)
+ silc_log_checksize(&silclogs[u]);
+ }
+ silc_log_starting = FALSE;
+ if (silc_log_flushdelay < SILC_LOG_FLUSH_MIN_DELAY)
+ silc_log_flushdelay = SILC_LOG_FLUSH_MIN_DELAY;
+ silc_schedule_task_add((SilcSchedule) context, 0, silc_log_fflush_callback,
+ context, silc_log_flushdelay, 0, SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
+}
+
+/* Outputs the log message to the first available channel. Channels are
+ * ordered by importance (see SilcLogType documentation).
+ * More important channels can be printed on less important ones, but not
+ * vice-versa. */
+
+void silc_log_output(SilcLogType type, char *string)
+{
+ const char *typename = NULL;
+ FILE *fp;
+ SilcLog log;
+
+ if ((type > SILC_LOG_MAX) || !(log = silc_log_find_by_type(type)))
+ goto end;
+
+ /* Save the original typename, because even if we redirect the message
+ * to another channel we'll keep however the original channel name */
+ typename = log->typename;
+
+ /* If there is a custom callback set, use it and return. */
+ if (log->cb) {
+ if ((*log->cb)(type, string, log->context))
+ goto end;
+ }
+
+ if (!silc_log_scheduled) {
+ if (silc_log_no_init == FALSE) {
+ fprintf(stderr,
+ "Warning, trying to output without log files initialization, "
+ "log output is going to stderr\n");
+ silc_log_no_init = TRUE;