+
+ /* can't forget server notices... */
+ if (flags & LOG_DOSNOTICE)
+ sendto_opmask_butone(0, ldata->snomask ? ldata->snomask : desc->snomask,
+ "%s", buf);
+}
+
+/** Log an appropriate message for kills.
+ * @param[in] victim %Client being killed.
+ * @param[in] killer %User or server doing the killing.
+ * @param[in] inpath Peer that sent us the KILL message.
+ * @param[in] path Kill path that sent to us by \a inpath.
+ * @param[in] msg Kill reason.
+ */
+void
+log_write_kill(const struct Client *victim, const struct Client *killer,
+ const char *inpath, const char *path, const char *msg)
+{
+ if (MyUser(victim))
+ log_write(IsServer(killer) ? LS_SERVKILL : LS_OPERKILL, L_TRACE, 0,
+ "A local client %#C KILLED by %#C Path: %s!%s %s",
+ victim, killer, inpath, path, msg);
+ else
+ log_write(IsServer(killer) ? LS_SERVKILL : LS_OPERKILL, L_TRACE, 0,
+ "KILL from %C For %C Path: %s!%s %s", killer, victim, inpath,
+ path, msg);
+}
+
+/** Find a reference-counted LogFile by file name.
+ * @param[in] file Name of file.
+ * @return A log file descriptor with LogFile::ref at least 1.
+ */
+static struct LogFile *
+log_file_create(const char *file)
+{
+ struct LogFile *tmp;
+
+ assert(0 != file);
+
+ /* if one already exists for that file, return it */
+ for (tmp = logInfo.filelist; tmp; tmp = tmp->next)
+ if (!strcmp(tmp->file, file)) {
+ tmp->ref++;
+ return tmp;
+ }
+
+ if (logInfo.freelist) { /* pop one off the free list */
+ tmp = logInfo.freelist;
+ logInfo.freelist = tmp->next;
+ } else /* allocate a new one */
+ tmp = (struct LogFile*) MyMalloc(sizeof(struct LogFile));
+
+ tmp->fd = -1; /* initialize the structure */
+ tmp->ref = 1;
+ DupString(tmp->file, file);
+
+ tmp->next = logInfo.filelist; /* link it into the list... */
+ tmp->prev_p = &logInfo.filelist;
+ if (logInfo.filelist)
+ logInfo.filelist->prev_p = &tmp->next;
+ logInfo.filelist = tmp;
+
+ return tmp;
+}
+
+/** Dereference a log file.
+ * If the reference count is exactly one on entry to this function,
+ * the file is closed and its structure is freed.
+ * @param[in] lf Log file to dereference.
+ */
+static void
+log_file_destroy(struct LogFile *lf)
+{
+ assert(0 != lf);
+
+ if (--lf->ref == 0) {
+ if (lf->next) /* clip it out of the list */
+ lf->next->prev_p = lf->prev_p;
+ *lf->prev_p = lf->next;
+
+ lf->prev_p = 0; /* we won't use it for the free list */
+ if (lf->fd >= 0)
+ close(lf->fd);
+ lf->fd = -1;
+ MyFree(lf->file); /* free the file name */
+
+ lf->next = logInfo.freelist; /* stack it onto the free list */
+ logInfo.freelist = lf;
+ }
+}
+
+/** Look up a log subsystem by name.
+ * @param[in] subsys Subsystem name.
+ * @return Pointer to the subsystem's LogDesc, or NULL if none exists.
+ */
+static struct LogDesc *
+log_find(const char *subsys)
+{
+ int i;
+
+ assert(0 != subsys);
+
+ /* find the named subsystem */
+ for (i = 0; i < LS_LAST_SYSTEM; i++)
+ if (!ircd_strcmp(subsys, logDesc[i].name))
+ return &logDesc[i];
+
+ return 0; /* not found */
+}
+
+/** Return canonical version of log subsystem name.
+ * @param[in] subsys Subsystem name.
+ * @return A constant string containing the canonical name.
+ */
+char *
+log_canon(const char *subsys)
+{
+ struct LogDesc *desc;
+
+ if (!(desc = log_find(subsys)))
+ return 0;
+
+ return desc->name;
+}
+
+/** Look up a log level by name.
+ * @param[in] level Log level name.
+ * @return LogLevel enumeration, or L_LAST_LEVEL if none exists.
+ */
+static enum LogLevel
+log_lev_find(const char *level)
+{
+ int i;
+
+ assert(0 != level);
+
+ /* find the named level */
+ for (i = 0; levelData[i].string; i++)
+ if (!ircd_strcmp(level, levelData[i].string))
+ return levelData[i].level;
+
+ return L_LAST_LEVEL; /* not found */
+}
+
+/** Look up the canonical name for a log level.
+ * @param[in] lev
+ * @return A constant string containing the level's canonical name.
+ */
+static char *
+log_lev_name(enum LogLevel lev)
+{
+ assert(-1 < (int)lev);
+ assert((int)lev < L_LAST_LEVEL);
+ assert(lev == levelData[lev].level);
+
+ return levelData[lev].string;
+}
+
+/** Look up a syslog facility by name.
+ * @param[in] facility Facility name.
+ * @return Syslog facility value, or LOG_NOTFOUND if none exists.
+ */
+static int
+log_fac_find(const char *facility)
+{
+ int i;
+
+ assert(0 != facility);
+
+ /* find the named facility */
+ for (i = 0; facilities[i].name; i++)
+ if (!ircd_strcmp(facility, facilities[i].name))
+ return facilities[i].facility;
+
+ return LOG_NOTFOUND; /* not found */
+}
+
+/** Look up the name for a syslog facility.
+ * @param[in] fac Facility value.
+ * @return Canonical name for facility, or NULL if none exists.
+ */
+static char *
+log_fac_name(int fac)
+{
+ int i;
+
+ /* find the facility */
+ for (i = 0; facilities[i].name; i++)
+ if (facilities[i].facility == fac)
+ return facilities[i].name;
+
+ return 0; /* not found; should never happen */
+}
+
+/** Look up a server notice mask by name.
+ * @param[in] maskname Name of server notice mask.
+ * @return Bitmask for server notices, or 0 if none exists.
+ */
+static unsigned int
+log_sno_find(const char *maskname)
+{
+ int i;
+
+ assert(0 != maskname);
+
+ /* find the named snomask */
+ for (i = 0; masks[i].name; i++)
+ if (!ircd_strcmp(maskname, masks[i].name))
+ return masks[i].snomask;
+
+ return SNO_NOTFOUND; /* not found */
+}
+
+/** Look up the canonical name for a server notice mask.
+ * @param[in] sno Server notice mask.
+ * @return Canonical name for the mask, or NULL if none exists.
+ */
+static char *
+log_sno_name(unsigned int sno)
+{
+ int i;
+
+ /* find the snomask */
+ for (i = 0; masks[i].name; i++)
+ if (masks[i].snomask == sno)
+ return masks[i].name;
+
+ return 0; /* not found; should never happen */
+}
+
+/** Set a log file for a particular subsystem.
+ * @param[in] subsys Subsystem name.
+ * @param[in] filename Log file to write to.
+ * @return Zero on success; non-zero on error.
+ */
+int
+log_set_file(const char *subsys, const char *filename)
+{
+ struct LogDesc *desc;
+
+ /* find subsystem */
+ if (!(desc = log_find(subsys)))
+ return 2;
+
+ if (filename)
+ desc->mark |= LOG_MARK_FILE; /* mark that file has been changed */
+ else
+ desc->mark &= ~LOG_MARK_FILE; /* file has been reset to defaults */
+
+ /* no change, don't go to the trouble of destroying and recreating */
+ if (desc->file && desc->file->file && filename &&
+ !strcmp(desc->file->file, filename))
+ return 0;
+
+ /* debug log is special, since it has to be opened on fd 2 */
+ if (desc->subsys == LS_DEBUG)
+ return log_debug_file(filename);
+
+ if (desc->file) /* destroy previous entry... */
+ log_file_destroy(desc->file);
+
+ /* set the file to use */
+ desc->file = filename ? log_file_create(filename) : 0;
+
+ return 0;
+}
+
+/** Find the log file name for a subsystem.
+ * @param[in] subsys Subsystem name.
+ * @return Log file for the subsystem, or NULL if not being logged to a file.
+ */
+char *
+log_get_file(const char *subsys)
+{
+ struct LogDesc *desc;
+
+ /* find subsystem */
+ if (!(desc = log_find(subsys)))
+ return 0;
+
+ return desc->file ? desc->file->file : 0;
+}
+
+/** Set the syslog facility for a particular subsystem.
+ * @param[in] subsys Subsystem name.
+ * @param[in] facility Facility name to log to.
+ * @return Zero on success; non-zero on error.
+ */
+int
+log_set_facility(const char *subsys, const char *facility)
+{
+ struct LogDesc *desc;
+ int fac;
+
+ /* find subsystem */
+ if (!(desc = log_find(subsys)))
+ return 2;
+
+ /* set syslog facility */
+ if (EmptyString(facility)) {
+ desc->facility = desc->def_fac;
+ desc->mark &= ~LOG_MARK_FACILITY;
+ } else if ((fac = log_fac_find(facility)) != LOG_NOTFOUND) {
+ desc->facility = fac;
+ if (fac == desc->def_fac)
+ desc->mark &= ~LOG_MARK_FACILITY;
+ else
+ desc->mark |= LOG_MARK_FACILITY;
+ } else
+ return 1;
+
+ return 0;
+}
+
+/** Find the facility name for a subsystem.
+ * @param[in] subsys Subsystem name.
+ * @return Facility name being used, or NULL if not being logged to syslog.
+ */
+char *
+log_get_facility(const char *subsys)
+{
+ struct LogDesc *desc;
+
+ /* find subsystem */
+ if (!(desc = log_find(subsys)))
+ return 0;
+
+ /* find the facility's name */
+ return log_fac_name(desc->facility);
+}
+
+/** Set the server notice mask for a subsystem.
+ * @param[in] subsys Subsystem name.
+ * @param[in] snomask Server notice mask name.
+ * @return Zero on success; non-zero on error.
+ */
+int
+log_set_snomask(const char *subsys, const char *snomask)
+{
+ struct LogDesc *desc;
+ unsigned int sno = SNO_DEFAULT;
+
+ /* find subsystem */
+ if (!(desc = log_find(subsys)))
+ return 2;
+
+ /* set snomask value */
+ if (EmptyString(snomask)) {
+ desc->snomask = desc->def_sno;
+ desc->mark &= ~LOG_MARK_SNOMASK;
+ } else if ((sno = log_sno_find(snomask)) != SNO_NOTFOUND) {
+ desc->snomask = sno;
+ if (sno == desc->def_sno)
+ desc->mark &= ~LOG_MARK_SNOMASK;
+ else
+ desc->mark |= LOG_MARK_SNOMASK;
+ } else
+ return 1;
+
+ return 0;
+}
+
+/** Find the server notice mask name for a subsystem.
+ * @param[in] subsys Subsystem name.
+ * @return Name of server notice mask being used, or NULL if none.
+ */
+char *
+log_get_snomask(const char *subsys)
+{
+ struct LogDesc *desc;
+
+ /* find subsystem */
+ if (!(desc = log_find(subsys)))
+ return 0;
+
+ /* find the snomask value's name */
+ return log_sno_name(desc->snomask);
+}
+
+/** Set the verbosity level for a subsystem.
+ * @param[in] subsys Subsystem name.
+ * @param[in] level Minimum log level.
+ * @return Zero on success; non-zero on error.
+ */
+int
+log_set_level(const char *subsys, const char *level)
+{
+ struct LogDesc *desc;
+ enum LogLevel lev;
+
+ /* find subsystem */
+ if (!(desc = log_find(subsys)))
+ return 2;
+
+ /* set logging level */
+ if (EmptyString(level)) {
+ desc->level = L_DEFAULT;
+ desc->mark &= ~LOG_MARK_LEVEL;
+ } else if ((lev = log_lev_find(level)) != L_LAST_LEVEL) {
+ desc->level = lev;
+ if (lev == L_DEFAULT)
+ desc->mark &= ~LOG_MARK_LEVEL;
+ else
+ desc->mark |= LOG_MARK_LEVEL;
+ } else
+ return 1;
+
+ return 0;
+}
+
+/** Find the verbosity level for a subsystem.
+ * @param[in] subsys Subsystem name.
+ * @return Minimum verbosity level being used, or NULL on error.
+ */
+char *
+log_get_level(const char *subsys)
+{
+ struct LogDesc *desc;
+
+ /* find subsystem */
+ if (!(desc = log_find(subsys)))
+ return 0;
+
+ /* find the level's name */
+ return log_lev_name(desc->level);
+}
+
+/** Set the default syslog facility.
+ * @param[in] facility Syslog facility name.
+ * @return Zero on success, non-zero on error.
+ */
+int
+log_set_default(const char *facility)
+{
+ int fac, oldfac;
+
+ oldfac = logInfo.facility;
+
+ if (EmptyString(facility))
+ logInfo.facility = LOG_USER;
+ else if ((fac = log_fac_find(facility)) != LOG_NOTFOUND &&
+ fac != LOG_NONE && fac != LOG_DEFAULT)
+ logInfo.facility = fac;
+ else
+ return 1;
+
+ if (logInfo.facility != oldfac) {
+ closelog(); /* reopen syslog with new facility setting */
+ openlog(logInfo.procname, LOG_PID | LOG_NDELAY, logInfo.facility);
+ }
+
+ return 0;
+}
+
+/** Find the default syslog facility name.
+ * @return Canonical name of default syslog facility, or NULL if none.
+ */
+char *
+log_get_default(void)
+{
+ /* find the facility's name */
+ return log_fac_name(logInfo.facility);
+}
+
+/** Clear all marks. */
+void
+log_feature_unmark(void)
+{
+ int i;
+
+ for (i = 0; i < LS_LAST_SYSTEM; i++)
+ logDesc[i].mark = 0;
+}
+
+/** Reset unmodified fields in all log subsystems to their defaults.
+ * @param[in] flag If non-zero, clear default syslog facility.
+ */
+int
+log_feature_mark(int flag)
+{
+ int i;
+
+ if (flag)
+ log_set_default(0);
+
+ for (i = 0; i < LS_LAST_SYSTEM; i++) {
+ if (!(logDesc[i].mark & LOG_MARK_FILE)) {
+ if (logDesc[i].subsys != LS_DEBUG) { /* debug is special */
+ if (logDesc[i].file) /* destroy previous entry... */
+ log_file_destroy(logDesc[i].file);
+ logDesc[i].file = 0;
+ }
+ }
+
+ if (!(logDesc[i].mark & LOG_MARK_FACILITY)) /* set default facility */
+ logDesc[i].facility = logDesc[i].def_fac;
+
+ if (!(logDesc[i].mark & LOG_MARK_SNOMASK)) /* set default snomask */
+ logDesc[i].snomask = logDesc[i].def_sno;
+
+ if (!(logDesc[i].mark & LOG_MARK_LEVEL)) /* set default level */
+ logDesc[i].level = L_DEFAULT;
+ }
+
+ return 0; /* we don't have a notify handler */
+}
+
+/** Feature list callback to report log settings.
+ * @param[in] to Client requesting list.
+ * @param[in] flag If non-zero, report default syslog facility.
+ */
+void
+log_feature_report(struct Client *to, int flag)
+{
+ int i;
+
+ for (i = 0; i < LS_LAST_SYSTEM; i++)
+ {
+ if (logDesc[i].mark & LOG_MARK_FILE) /* report file */
+ send_reply(to, SND_EXPLICIT | RPL_STATSFLINE, "F LOG %s FILE %s",
+ logDesc[i].name, (logDesc[i].file && logDesc[i].file->file ?
+ logDesc[i].file->file : "(terminal)"));
+
+ if (logDesc[i].mark & LOG_MARK_FACILITY) /* report facility */
+ send_reply(to, SND_EXPLICIT | RPL_STATSFLINE, "F LOG %s FACILITY %s",
+ logDesc[i].name, log_fac_name(logDesc[i].facility));
+
+ if (logDesc[i].mark & LOG_MARK_SNOMASK) /* report snomask */
+ send_reply(to, SND_EXPLICIT | RPL_STATSFLINE, "F LOG %s SNOMASK %s",
+ logDesc[i].name, log_sno_name(logDesc[i].snomask));
+
+ if (logDesc[i].mark & LOG_MARK_LEVEL) /* report log level */
+ send_reply(to, SND_EXPLICIT | RPL_STATSFLINE, "F LOG %s LEVEL %s",
+ logDesc[i].name, log_lev_name(logDesc[i].level));
+ }
+
+ if (flag) /* report default facility */
+ send_reply(to, SND_EXPLICIT | RPL_STATSFLINE, "F LOG %s",
+ log_fac_name(logInfo.facility));