--- /dev/null
+#
+# Generated by the Apple Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = syslog
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Aggregate
+
+TOOLS = syslogd.tproj util.tproj
+
+OTHERSRCS = Makefile Makefile.preamble Makefile.postamble
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = aggregate.make
+LIBS =
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
--- /dev/null
+# Empty for now
--- /dev/null
+CLEAN_ALL_SUBPROJECTS = YES
--- /dev/null
+{
+ FILESTABLE = {
+ OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble);
+ SUBPROJECTS = (syslogd.tproj, util.tproj);
+ };
+ LANGUAGE = English;
+ MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles";
+ NEXTSTEP_BUILDTOOL = /usr/bin/gnumake;
+ NEXTSTEP_JAVA_COMPILER = /usr/bin/javac;
+ NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc;
+ PROJECTNAME = network_cmds;
+ PROJECTTYPE = Aggregate;
+ PROJECTVERSION = 2.8;
+}
--- /dev/null
+#
+# Generated by the NeXT Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = syslogd
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+HFILES = daemon.h
+
+CFILES = asl_action.c asl_in.c bsd_in.c bsd_out.c daemon.c klog_in.c syslogd.c udp_in.c
+
+OTHERSRCS = Makefile.preamble Makefile Makefile.postamble syslogd.8 syslog.conf.5 com.apple.syslogd.plist
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /usr/sbin
+WINDOWS_INSTALLDIR = /usr/sbin
+PDO_UNIX_INSTALLDIR = /usr/sbin
+LIBS =
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
--- /dev/null
+###############################################################################
+# NeXT Makefile.postamble Template
+# Copyright 1993, NeXT Computer, Inc.
+#
+# This Makefile is used for configuring the standard app makefiles associated
+# with ProjectBuilder.
+#
+# Use this template to set attributes for a project, sub-project, bundle, or
+# palette. Each node in the project's tree of sub-projects and bundles
+# should have it's own Makefile.preamble and Makefile.postamble. Additional
+# rules (e.g., after_install) that are defined by the developer should be
+# defined in this file.
+#
+###############################################################################
+#
+# Here are the variables exported by the common "app" makefiles that can be
+# used in any customizations you make to the template below:
+#
+# PRODUCT_ROOT - Name of top-level app-wrapper (e.g., Webster.app)
+# OFILE_DIR - Directory into which .o object files are generated.
+# (Note that this name is calculated based on the target
+# architectures specified in Project Builder).
+# DERIVED_SRC_DIR - Directory used for all other derived files
+# ALL_CFLAGS - All the flags passed to the cc(1) driver for compilations
+#
+# NAME - name of application, bundle, subproject, palette, etc.
+# LANGUAGE - langage in which the project is written (default "English")
+# ENGLISH - boolean flag set iff $(LANGUAGE) = "English"
+# JAPANESE - boolean flag set iff $(LANGUAGE) = "Japanese"
+# LOCAL_RESOURCES - localized resources (e.g. nib's, images) of project
+# GLOBAL_RESOURCES - non-localized resources of project
+# PROJECTVERSION - version of ProjectBuilder that output Makefile
+# APPICON - application icon file
+# DOCICONS - dock icon files
+# ICONSECTIONS - Specifies icon sections when linking executable
+#
+# CLASSES - Class implementation files in project.
+# HFILES - Header files in project.
+# MFILES - Other Objective-C source files in project.
+# CFILES - Other C source files in project.
+# PSWFILES - .psw files in the project
+# PSWMFILES - .pswm files in the project
+# SUBPROJECTS - Subprojects of this project
+# BUNDLES - Bundle subprojects of this project
+# OTHERSRCS - Other miscellaneous sources of this project
+# OTHERLINKED - Source files not matching a standard source extention
+#
+# LIBS - Libraries to link with when making app target
+# DEBUG_LIBS - Libraries to link with when making debug target
+# PROF_LIBS - Libraries to link with when making profile target
+# OTHERLINKEDOFILES - Other relocatable files to (always) link in.
+#
+# APP_MAKEFILE_DIR - Directory in which to find generic set of Makefiles
+# MAKEFILEDIR - Directory in which to find $(MAKEFILE)
+# MAKEFILE - Top level mechanism Makefile (e.g., app.make, bundle.make)
+# INSTALLDIR - Directory app will be installed into by 'install' target
+
+
+# Change defaults assumed by the standard app makefiles here. Edit the
+# following default values as appropriate. (Note that if no Makefile.postamble
+# exists, these values will have defaults set in common.make).
+
+# Add Makefile.preamble, Makefile.postamble, and Makefile.dependencies here if
+# you would like changes to them to invalidate previous builds. The project
+# depends on $(MAKEFILES) so that changes to Makefiles will trigger a re-build.
+#MAKEFILES = Makefile
+
+# Optimization flag passed to compiler:
+#OPTIMIZATION_CFLAG = -O
+
+# Flags always passed to compiler:
+#COMMON_CFLAGS = $(PROJECT_SPECIFIC_CFLAGS) -g -Wall
+
+# Flags passed to compiler in normal 'app' compiles:
+#NORMAL_CFLAGS = $(COMMON_CFLAGS) $(OPTIMIZATION_CFLAG)
+
+# Flags passed to compiler in 'debug' compiles:
+#DEBUG_CFLAGS = $(COMMON_CFLAGS) -DDEBUG
+
+# Flags passed to compiler in 'profile' compiles
+#PROFILE_CFLAGS = $(COMMON_CFLAGS) -pg $(OPTIMIZATION_CFLAG) -DPROFILE
+
+# Flags passed to yacc
+#YFLAGS = -d
+
+# Ownership and permissions of files installed by 'install' target
+#INSTALL_AS_USER = root # User to chown app to
+#INSTALL_AS_GROUP = wheel # Group to chgrp app to
+#INSTALL_PERMISSIONS = # If set, 'install' chmod's executable to this
+
+# Options to strip for bundles, apps with bundles, and apps without bundles,
+# respectively.
+#RELOCATABLE_STRIP_OPTS = -x -u
+#DYLD_APP_STRIP_OPTS = -A -n
+#APP_STRIP_OPTS =
+#TOOL_STRIP_OPTS =
+#LIBRARY_STRIP_OPTS = -x -S # Note: -S strips debugging symbols
+# (Note: APP_STRIP_OPTS and TOOL_STRIP_OPTS default to empty, but
+# developers doing their own dynamic loading should set this to
+# $(DYLD_APP_STRIP_OPTS)).
+STRIPFLAGS = -x
+
+
+#########################################################################
+# Put rules to extend the behavior of the standard Makefiles here. Typical
+# user-defined rules are before_install and after_install (please don't
+# redefine things like install or app, as they are owned by the top-level
+# Makefile API), which are rules that get invoked before and after the install
+# target runs. Such rules should be specified with the '::' syntax rather than
+# a single colon.
+
+# a rule with which to install the man page
+install-man-page:
+ install -d $(DSTROOT)/usr/share/man/man5
+ install -d $(DSTROOT)/usr/share/man/man8
+ install -c -m 444 syslog.conf.5 $(DSTROOT)/usr/share/man/man5/syslog.conf.5
+ install -c -m 444 syslogd.8 $(DSTROOT)/usr/share/man/man8/syslogd.8
+ install -d $(DSTROOT)/System/Library/LaunchDaemons
+ install -c -m 444 com.apple.syslogd.plist $(DSTROOT)/System/Library/LaunchDaemons
--- /dev/null
+###############################################################################
+# NeXT Makefile.preamble Template
+# Copyright 1993, NeXT Computer, Inc.
+#
+# This Makefile is used for configuring the standard app makefiles associated
+# with ProjectBuilder.
+#
+# Use this template to set attributes for a project, sub-project, bundle, or
+# palette. Each node in the project's tree of sub-projects and bundles
+# should have it's own Makefile.preamble and Makefile.postamble.
+#
+###############################################################################
+## Configure the flags passed to $(CC) here. These flags will also be
+## inherited by all nested sub-projects and bundles. Put your -I, -D, -U, and
+## -L flags here. To change the default flags that get passed to ${CC}
+## (e.g. change -O to -O2), see Makefile.postamble.
+
+# Flags passed to compiler (in addition to -g, -O, etc)
+OTHER_CFLAGS = -DINET6 -Dsocklen_t=int
+# Flags passed to ld (in addition to -ObjC, etc.)
+OTHER_LDFLAGS =
+
+BUNDLELDFLAGS = # use iff project is a bundle
+PALETTELDFLAGS = # use iff project is a palette
+
+## Specify which headers in this project should be published to the outside
+## world in a flat header directory given in PUBLIC_HEADER_DIR (which will be
+## prepended by DSTROOT, below. Any subset of these public headers can be
+## precompiled automatically after installation, with extra user-defined flags.
+PUBLIC_HEADER_DIR =
+PUBLIC_HEADERS =
+PUBLIC_PRECOMPILED_HEADERS =
+PUBLIC_PRECOMPILED_HEADERS_CFLAGS =
+
+## Configure what is linked in at each level here. Libraries are only used in
+## the final 'app' linking step. Final 'app' linking is only done via the
+## 'app', 'debug', and 'profile' targets when they are invoked for
+## the top-level app.
+
+# Additional libs to link apps against ('app' target)
+#OTHER_LIBS =
+# Additional libs to link apps against ('debug' target)
+OTHER_DEBUG_LIBS =
+# Additional libs to link apps against ('profile' target)
+OTHER_PROF_LIBS =
+
+# More 'app' libraries when $(JAPANESE) = "YES"
+OTHER_JAPANESE_LIBS =
+# More 'debug' libraries when $(JAPANESE) = "YES"
+OTHER_JAPANESE_DEBUG_LIBS =
+# More 'profile' libs when $(JAPANESE) = "YES"
+OTHER_JAPANESE_PROF_LIBS =
+
+# If this is a bundle, and you *know* the enclosing application will not
+# be linking with a library which you require in your bundle code, then
+# mention it here so that it gets linked into the bundle. Note that this
+# is wasteful but sometimes necessary.
+BUNDLE_LIBS =
+
+## Configure how things get built here. Additional dependencies, sourcefiles,
+## derived files, and build order should be specified here.
+
+# Other dependencies of this project
+OTHER_PRODUCT_DEPENDS =
+# Built *before* building subprojects/bundles
+OTHER_INITIAL_TARGETS =
+# Other source files maintained by .pre/postamble
+OTHER_SOURCEFILES =
+# Additional files to be removed by `make clean'
+OTHER_GARBAGE =
+# Precompiled headers to be built before any compilation occurs (e.g., draw.p)
+PRECOMPS =
+
+# Targets to be built before installation
+OTHER_INSTALL_DEPENDS =
+
+# A virtual root directory (other than /) to be prepended to the $(INSTALLDIR)
+# passed from ProjectBuilder.
+DSTROOT =
+
+# Set the following to "YES" if you want the old behavior of recursively
+# cleaning all nested subprojects during 'make clean'.
+CLEAN_ALL_SUBPROJECTS =
+
+## Add more obscure source files here to cause them to be automatically
+## processed by the appropriate tool. Note that these files should also be
+## added to "Supporting Files" in ProjectBuilder. The desired .o files that
+## result from these files should also be added to OTHER_OFILES above so they
+## will be linked in.
+
+# .msg files that should have msgwrap run on them
+MSGFILES =
+# .defs files that should have mig run on them
+DEFSFILES =
+# .mig files (no .defs files) that should have mig run on them
+MIGFILES =
+
+## Add additional Help directories here (add them to the project as "Other
+## Resources" in Project Builder) so that they will be compressed into .store
+## files and copied into the app wrapper. If the help directories themselves
+## need to also be in the app wrapper, then a cp command will need to be added
+## in an after_install target.
+OTHER_HELP_DIRS =
+
+# Don't add more rules here unless you want the first one to be the default
+# target for make! Put all your targets in Makefile.postamble.
+
+# To include a version string, project source must exist in a directory named
+# $(NAME).%d[.%d][.%d] and the following line must be uncommented.
+
+OTHER_GENERATED_OFILES = $(VERS_OFILE)
+
+# to allow installing man page after main install
+AFTER_INSTALL += install-man-page
+
+-include ../Makefile.include
--- /dev/null
+{
+ DOCICONFILES = ();
+ FILESTABLE = {
+ C_FILES = ();
+ H_FILES = (daemon.h);
+ OTHER_LIBS = ();
+ OTHER_LINKED = (asl_action.c, asl_in.c, bsd_in.c, bsd_out.c, daemon.c, klog_in.c, syslogd.c);
+ OTHER_SOURCES = (Makefile.preamble, Makefile, Makefile.postamble, com.apple.syslogd.plist, syslogd.8, syslog.conf.5);
+ PRECOMPILED_HEADERS = ();
+ PROJECT_HEADERS = ();
+ PUBLIC_HEADERS = ();
+ SUBPROJECTS = ();
+ };
+ GENERATEMAIN = YES;
+ LANGUAGE = English;
+ LOCALIZABLE_FILES = {};
+ NEXTSTEP_BUILDDIR = "";
+ NEXTSTEP_BUILDTOOL = /bin/make;
+ NEXTSTEP_COMPILEROPTIONS = "";
+ NEXTSTEP_INSTALLDIR = /usr/sbin;
+ NEXTSTEP_JAVA_COMPILER = /usr/bin/javac;
+ NEXTSTEP_LINKEROPTIONS = "";
+ NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc;
+ PDO_UNIX_BUILDDIR = "";
+ PDO_UNIX_BUILDTOOL = /bin/make;
+ PDO_UNIX_COMPILEROPTIONS = "";
+ PDO_UNIX_INSTALLDIR = /usr/sbin;
+ PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac";
+ PDO_UNIX_LINKEROPTIONS = "";
+ PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc";
+ PROJECTNAME = syslogd;
+ PROJECTTYPE = Tool;
+ PROJECTVERSION = 2.8;
+ WINDOWS_BUILDDIR = "";
+ WINDOWS_BUILDTOOL = /bin/make;
+ WINDOWS_COMPILEROPTIONS = "";
+ WINDOWS_INSTALLDIR = /usr/sbin;
+ WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe";
+ WINDOWS_LINKEROPTIONS = "";
+ WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc";
+}
--- /dev/null
+/*
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * "Portions Copyright (c) 2004 Apple Computer, Inc. All Rights
+ * Reserved. This file contains Original Code and/or Modifications of
+ * Original Code as defined in and that are subject to the Apple Public
+ * Source License Version 1.0 (the 'License'). You may not use this file
+ * except in compliance with the License. Please obtain a copy of the
+ * License at http://www.apple.com/publicsource and read it before using
+ * this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
+ * License for the specific language governing rights and limitations
+ * under the License."
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/uio.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <netdb.h>
+#include <notify.h>
+#include "daemon.h"
+
+#define _PATH_ASL_CONF "/etc/asl.conf"
+#define MY_ID "asl_action"
+
+#define ASL_KEY_FACILITY "Facility"
+#define IndexNull ((uint32_t)-1)
+#define forever for(;;)
+
+static asl_msg_t *query = NULL;
+static int reset = 0;
+
+struct action_rule
+{
+ asl_msg_t *query;
+ char *action;
+ char *options;
+ TAILQ_ENTRY(action_rule) entries;
+};
+
+static TAILQ_HEAD(cr, action_rule) asl_action_rule;
+
+int asl_action_close();
+static int _parse_notify_file(const char *);
+
+static void
+_do_reset(void)
+{
+ asl_action_close();
+ _parse_notify_file(_PATH_ASL_CONF);
+}
+
+/*
+ * Config File format:
+ * Q [k v] [k v] ... action args...
+ */
+
+/* Skip over query */
+static char *
+_find_action(char *s)
+{
+ char *p;
+
+ p = s;
+ if (p == NULL) return NULL;
+ if (*p != 'Q') return NULL;
+
+ p++;
+
+ forever
+ {
+ /* Find next [ */
+ while ((*p == ' ') || (*p == '\t')) p++;
+
+ if (*p == '\0') return NULL;
+ if (*p != '[') return p;
+
+ /* skip to closing ] */
+ while (*p != ']')
+ {
+ p++;
+ if (*p == '\\')
+ {
+ p++;
+ if (*p == ']') p++;
+ }
+ }
+
+ if (*p == ']') p++;
+ }
+
+ return NULL;
+}
+
+static int
+_parse_line(char *s)
+{
+ char *act, *p;
+ struct action_rule *out;
+
+ if (s == NULL) return -1;
+ while ((*s == ' ') || (*s == '\t')) s++;
+ if (*s == '#') return -1;
+
+ act = _find_action(s);
+
+ if (act == NULL) return -1;
+ out = (struct action_rule *)calloc(1, sizeof(struct action_rule));
+ if (out == NULL) return -1;
+
+ p = strchr(act, ' ');
+ if (p != NULL) *p = '\0';
+ out->action = strdup(act);
+
+ if (out->action == NULL)
+ {
+ free(out);
+ return -1;
+ }
+
+ if (p != NULL)
+ {
+ out->options = strdup(p+1);
+
+ if (out->options == NULL)
+ {
+ free(out->action);
+ free(out);
+ return -1;
+ }
+ }
+
+ p = act - 1;
+
+ *p = '\0';
+ out->query = asl_msg_from_string(s);
+
+ if (out->query == NULL)
+ {
+ free(out->action);
+ if (out->options != NULL) free(out->options);
+ free(out);
+ return -1;
+ }
+
+ TAILQ_INSERT_TAIL(&asl_action_rule, out, entries);
+
+ return 0;
+}
+
+static void
+_act_notify(struct action_rule *r)
+{
+ if (r == NULL) return;
+ if (r->options == NULL) return;
+ notify_post(r->options);
+}
+
+int
+asl_action_sendmsg(asl_msg_t *msg, const char *outid)
+{
+ struct action_rule *r;
+
+
+ if (reset != 0)
+ {
+ _do_reset();
+ reset = 0;
+ }
+
+ if (msg == NULL) return -1;
+
+ for (r = asl_action_rule.tqh_first; r != NULL; r = r->entries.tqe_next)
+ {
+ if (asl_msg_cmp(r->query, msg) == 1)
+ {
+ if (r->action == NULL) continue;
+ if (!strcmp(r->action, "notify")) _act_notify(r);
+ }
+ }
+
+ return 0;
+}
+
+static int
+_parse_notify_file(const char *name)
+{
+ FILE *cf;
+ char *line;
+
+ cf = fopen(name, "r");
+ if (cf == NULL) return 1;
+
+ while (NULL != (line = get_line_from_file(cf)))
+ {
+ _parse_line(line);
+ free(line);
+ }
+
+ fclose(cf);
+
+ return 0;
+}
+
+int
+asl_action_init(void)
+{
+ asldebug("%s: init\n", MY_ID);
+
+ TAILQ_INIT(&asl_action_rule);
+
+ query = asl_new(ASL_TYPE_QUERY);
+ aslevent_addmatch(query, MY_ID);
+ aslevent_addoutput(asl_action_sendmsg, MY_ID);
+
+ _parse_notify_file(_PATH_ASL_CONF);
+ return 0;
+}
+
+int
+asl_action_reset(void)
+{
+ reset = 1;
+ return 0;
+}
+
+int
+asl_action_close(void)
+{
+ struct action_rule *r, *n;
+
+ n = NULL;
+ for (r = asl_action_rule.tqh_first; r != NULL; r = n)
+ {
+ n = r->entries.tqe_next;
+
+ if (r->query != NULL) asl_free(r->query);
+ if (r->action != NULL) free(r->action);
+ if (r->options != NULL) free(r->options);
+
+ TAILQ_REMOVE(&asl_action_rule, r, entries);
+ free(r);
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * "Portions Copyright (c) 2004 Apple Computer, Inc. All Rights
+ * Reserved. This file contains Original Code and/or Modifications of
+ * Original Code as defined in and that are subject to the Apple Public
+ * Source License Version 1.0 (the 'License'). You may not use this file
+ * except in compliance with the License. Please obtain a copy of the
+ * License at http://www.apple.com/publicsource and read it before using
+ * this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
+ * License for the specific language governing rights and limitations
+ * under the License."
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/uio.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include "daemon.h"
+
+#define forever for(;;)
+
+#define MY_ID "asl"
+
+static int sock = -1;
+static FILE *aslfile = NULL;
+static asl_msg_t *query = NULL;
+
+extern int asl_log_filter;
+
+#define MATCH_EOF -1
+#define MATCH_NULL 0
+#define MATCH_TRUE 1
+#define MATCH_FALSE 2
+
+extern int prune;
+
+static int filter_token = -1;
+
+struct prune_query_entry
+{
+ asl_msg_t *query;
+ TAILQ_ENTRY(prune_query_entry) entries;
+};
+
+static TAILQ_HEAD(pql, prune_query_entry) pquery;
+
+static int
+_search_next(FILE *log, char **outstr)
+{
+ char *str;
+ aslmsg m;
+ int match, i;
+ struct prune_query_entry *p;
+
+ *outstr = NULL;
+
+ if (log == NULL) return MATCH_EOF;
+
+ str = get_line_from_file(log);
+ if (str == NULL) return MATCH_EOF;
+
+ m = asl_msg_from_string(str);
+ if (m == NULL)
+ {
+ free(str);
+ return MATCH_NULL;
+ }
+
+ *outstr = str;
+
+ for (i = 0, p = pquery.tqh_first; p != NULL; p = p->entries.tqe_next, i++)
+ {
+ match = asl_msg_cmp(p->query, m);
+ if (match == 1)
+ {
+ asl_free(m);
+ return MATCH_TRUE;
+ }
+ }
+
+ asl_free(m);
+ return MATCH_FALSE;
+}
+
+/*
+ * Pruning the main output file (asl.log)
+ *
+ * The prune file (_PATH_ASL_PRUNE) is set up by the syslog command-line utiliy.
+ * It contains a set of queries. The main output file is read, and each
+ * message is matched against these queries. If any query matches, we save
+ * that message. Anything that doesn't match is discarded.
+ *
+ */
+int
+asl_prune(asl_msg_t *inq)
+{
+ char *pname, *str;
+ FILE *pfile, *qfile;
+ struct prune_query_entry *p, *n;
+ asl_msg_t *q;
+ int status, incount, outcount;
+
+ asldebug("syslogd: pruning %s\n", _PATH_ASL_OUT);
+
+ if (inq != NULL)
+ {
+ TAILQ_INIT(&pquery);
+ p = (struct prune_query_entry *)calloc(1, sizeof(struct prune_query_entry));
+ if (p == NULL) return -1;
+
+ p->query = inq;
+ TAILQ_INSERT_TAIL(&pquery, p, entries);
+ }
+ else
+ {
+ qfile = fopen(_PATH_ASL_PRUNE, "r");
+ if (qfile == NULL)
+ {
+ asldebug("syslogd: can't read %s: %s\n", _PATH_ASL_PRUNE, strerror(errno));
+ return 0;
+ }
+
+ TAILQ_INIT(&pquery);
+
+ forever
+ {
+ str = get_line_from_file(qfile);
+ if (str == NULL) break;
+
+ q = asl_msg_from_string(str);
+ asldebug("syslogd: prune line %s\n", str);
+
+ free(str);
+ if (q == NULL) continue;
+
+ if (q->type != ASL_TYPE_QUERY)
+ {
+ asl_free(q);
+ continue;
+ }
+
+ p = (struct prune_query_entry *)calloc(1, sizeof(struct prune_query_entry));
+ if (p == NULL) return -1;
+
+ p->query = q;
+ TAILQ_INSERT_TAIL(&pquery, p, entries);
+ }
+ }
+
+ pname = NULL;
+ asprintf(&pname, "%s.%d", _PATH_ASL_OUT, getpid());
+ if (pname == NULL) return -1;
+
+ pfile = fopen(pname, "w");
+ if (pfile == NULL)
+ {
+ asldebug("syslogd: can't write %s: %s\n", pname, strerror(errno));
+ free(pname);
+ return -1;
+ }
+
+ fclose(aslfile);
+ aslfile = fopen(_PATH_ASL_OUT, "r");
+ if (aslfile == NULL)
+ {
+ asldebug("syslogd: can't read %s: %s\n", _PATH_ASL_OUT, strerror(errno));
+ free(pname);
+ aslfile = fopen(_PATH_ASL_OUT, "a");
+ return -1;
+ }
+
+ incount = 0;
+ outcount = 0;
+
+ do
+ {
+ str = NULL;
+ incount++;
+ status = _search_next(aslfile, &str);
+
+ /*
+ * Pruning deletes records that match the search.
+ * If the match fails, we keep the record.
+ */
+ if (status == MATCH_FALSE)
+ {
+ outcount++;
+ fprintf(pfile, "%s\n", str);
+ }
+
+ if (str != NULL) free(str);
+ }
+ while (status != MATCH_EOF);
+
+ fclose(pfile);
+ fclose(aslfile);
+
+ unlink(_PATH_ASL_OUT);
+ rename(pname, _PATH_ASL_OUT);
+ free(pname);
+ unlink(_PATH_ASL_PRUNE);
+ aslfile = fopen(_PATH_ASL_OUT, "a");
+
+ n = NULL;
+ for (p = pquery.tqh_first; p != NULL; p = n)
+ {
+ n = p->entries.tqe_next;
+
+ if (p->query != NULL) asl_free(p->query);
+
+ TAILQ_REMOVE(&pquery, p, entries);
+ free(p);
+ }
+
+ asldebug("syslogd: prune %d records in, %d records out\n", incount, outcount);
+
+ return 0;
+}
+
+asl_msg_t *
+asl_in_getmsg(int fd)
+{
+ char *out;
+ asl_msg_t *m;
+ uint32_t len, n;
+ char ls[16];
+
+ n = read(fd, ls, 11);
+ if (n < 11)
+ {
+ if (n <= 0)
+ {
+ asldebug("%s: read error (len): %s\n", MY_ID, strerror(errno));
+ if (errno != EINTR)
+ {
+ close(fd);
+ aslevent_removefd(fd);
+ return NULL;
+ }
+ }
+
+ return NULL;
+ }
+
+ len = atoi(ls);
+ asldebug("%s: expecting message length %d bytes\n", MY_ID, len);
+ out = malloc(len);
+ if (out == NULL) return NULL;
+
+ n = read(fd, out, len);
+ if (n < len)
+ {
+ if (n <= 0)
+ {
+ asldebug("%s: read error (body): %s\n", MY_ID, strerror(errno));
+ if (errno != EINTR)
+ {
+ close(fd);
+ aslevent_removefd(fd);
+ free(out);
+ return NULL;
+ }
+ }
+ }
+
+ m = asl_msg_from_string(out);
+ free(out);
+ return m;
+}
+
+asl_msg_t *
+asl_in_acceptmsg(int fd)
+{
+ int clientfd;
+
+ asldebug("%s: accepting message\n", MY_ID);
+ clientfd = accept(fd, NULL, 0);
+ if (clientfd < 0)
+ {
+ asldebug("%s: error accepting socket fd %d: %s\n", MY_ID, fd, strerror(errno));
+ return NULL;
+ }
+
+ if (fcntl(clientfd, F_SETFL, O_NONBLOCK) < 0)
+ {
+ close(clientfd);
+ clientfd = -1;
+ asldebug("%s: couldn't set O_NONBLOCK for fd %d: %s\n", MY_ID, clientfd, strerror(errno));
+ return NULL;
+ }
+
+ aslevent_addfd(clientfd, asl_in_getmsg, NULL, NULL);
+ return NULL;
+}
+
+int
+aslmod_sendmsg(asl_msg_t *msg, const char *outid)
+{
+ const char *vlevel;
+ char *mstr;
+ uint32_t n, lmask;
+ int status, x, level;
+
+ if (aslfile == NULL) return -1;
+
+ /* set up com.apple.syslog.asl_filter */
+ if (filter_token == -1)
+ {
+ status = notify_register_check(NOTIFY_SYSTEM_ASL_FILTER, &filter_token);
+ if (status != NOTIFY_STATUS_OK)
+ {
+ filter_token = -1;
+ }
+ else
+ {
+ status = notify_check(filter_token, &x);
+ if (status == NOTIFY_STATUS_OK) status = notify_set_state(filter_token, asl_log_filter);
+ if (status != NOTIFY_STATUS_OK)
+ {
+ notify_cancel(filter_token);
+ filter_token = -1;
+ }
+ }
+ }
+
+ if (filter_token >= 0)
+ {
+ x = 1;
+ status = notify_check(filter_token, &x);
+ if ((status == NOTIFY_STATUS_OK) && (x == 1))
+ {
+ x = asl_log_filter;
+ status = notify_get_state(filter_token, &x);
+ if ((status == NOTIFY_STATUS_OK) && (x != 0)) asl_log_filter = x;
+ }
+ }
+
+ vlevel = asl_get(msg, ASL_KEY_LEVEL);
+ level = 7;
+ if (vlevel != NULL) level = atoi(vlevel);
+ lmask = ASL_FILTER_MASK(level);
+ if ((lmask & asl_log_filter) == 0) return 0;
+
+ mstr = asl_msg_to_string(msg, &n);
+ if (mstr != NULL)
+ {
+ fprintf(aslfile, "%s\n", mstr);
+ fflush(aslfile);
+ free(mstr);
+ }
+
+ return 0;
+}
+
+int
+asl_in_init(void)
+{
+ struct sockaddr_un sun;
+ int rbufsize;
+ int len;
+
+ asldebug("%s: init\n", MY_ID);
+ if (sock >= 0) return sock;
+
+ unlink(_PATH_ASL_IN);
+ sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0)
+ {
+ asldebug("%s: couldn't create socket for %s: %s\n", MY_ID, _PATH_ASL_IN, strerror(errno));
+ return -1;
+ }
+
+ asldebug("%s: creating %s for fd %d\n", MY_ID, _PATH_ASL_IN, sock);
+
+ memset(&sun, 0, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ strcpy(sun.sun_path, _PATH_ASL_IN);
+
+ len = sizeof(struct sockaddr_un);
+ if (bind(sock, (struct sockaddr *)&sun, len) < 0)
+ {
+ asldebug("%s: couldn't bind socket %d for %s: %s\n", MY_ID, sock, _PATH_ASL_IN, strerror(errno));
+ close(sock);
+ sock = -1;
+ return -1;
+ }
+
+ rbufsize = 128 * 1024;
+ len = sizeof(rbufsize);
+
+ if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rbufsize, len) < 0)
+ {
+ asldebug("%s: couldn't set receive buffer size for %s: %s\n", MY_ID, sock, _PATH_ASL_IN, strerror(errno));
+ close(sock);
+ sock = -1;
+ return -1;
+ }
+
+ if (listen(sock, SOMAXCONN) < 0)
+ {
+ asldebug("%s: couldn't listen on socket %d for %s: %s\n", MY_ID, sock, _PATH_ASL_IN, strerror(errno));
+ close(sock);
+ sock = -1;
+ unlink(_PATH_ASL_IN);
+ return -1;
+ }
+
+ if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0)
+ {
+ asldebug("%s: couldn't set O_NONBLOCK for socket %d (%s): %s\n", MY_ID, sock, _PATH_ASL_IN, strerror(errno));
+ close(sock);
+ sock = -1;
+ return -1;
+ }
+
+ chmod(_PATH_ASL_IN, 0666);
+
+ /* Add logger routine for main output file */
+ aslfile = fopen(_PATH_ASL_OUT, "a");
+ if (aslfile != NULL)
+ {
+ query = asl_new(ASL_TYPE_QUERY);
+ aslevent_addmatch(query, MY_ID);
+ aslevent_addoutput(aslmod_sendmsg, MY_ID);
+ }
+
+ return aslevent_addfd(sock, asl_in_acceptmsg, NULL, NULL);
+}
+
+int
+asl_in_reset(void)
+{
+ return 0;
+}
+
+int
+asl_in_close(void)
+{
+ if (sock < 0) return 1;
+
+ if (filter_token >= 0) notify_cancel(filter_token);
+ filter_token = -1;
+ asl_log_filter = 0;
+
+ asl_free(query);
+ close(sock);
+ if (aslfile != NULL) fclose(aslfile);
+ unlink(_PATH_ASL_IN);
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * "Portions Copyright (c) 2004 Apple Computer, Inc. All Rights
+ * Reserved. This file contains Original Code and/or Modifications of
+ * Original Code as defined in and that are subject to the Apple Public
+ * Source License Version 1.0 (the 'License'). You may not use this file
+ * except in compliance with the License. Please obtain a copy of the
+ * License at http://www.apple.com/publicsource and read it before using
+ * this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
+ * License for the specific language governing rights and limitations
+ * under the License."
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/fcntl.h>
+#include <sys/un.h>
+#include <sys/uio.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include "daemon.h"
+
+#define MY_ID "bsd_in"
+#define MAXLINE 1024
+
+static int sock = -1;
+
+asl_msg_t *
+bsd_in_acceptmsg(int fd)
+{
+ uint32_t len;
+ int n;
+ char line[MAXLINE + 1];
+ struct sockaddr_un sun;
+
+ len = sizeof(struct sockaddr_un);
+ n = recvfrom(fd, line, MAXLINE, 0, (struct sockaddr *)&sun, &len);
+
+ if (n <= 0) return NULL;
+
+ line[n] = '\0';
+ return asl_syslog_input_convert(line, n, NULL, 0);
+}
+
+int
+bsd_in_init(void)
+{
+ struct sockaddr_un sun;
+ int rbufsize;
+ int len;
+
+ asldebug("%s: init\n", MY_ID);
+ if (sock >= 0) return sock;
+
+ unlink(_PATH_SYSLOG_IN);
+ sock = socket(AF_UNIX, SOCK_DGRAM, 0);
+ if (sock < 0)
+ {
+ asldebug("%s: couldn't create socket for %s: %s\n", MY_ID, _PATH_SYSLOG_IN, strerror(errno));
+ return -1;
+ }
+
+ asldebug("%s: creating %s for fd %d\n", MY_ID, _PATH_SYSLOG_IN, sock);
+
+ memset(&sun, 0, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ strcpy(sun.sun_path, _PATH_SYSLOG_IN);
+
+ len = sizeof(struct sockaddr_un);
+ if (bind(sock, (struct sockaddr *)&sun, len) < 0)
+ {
+ asldebug("%s: couldn't bind socket %d for %s: %s\n", MY_ID, sock, _PATH_SYSLOG_IN, strerror(errno));
+ close(sock);
+ sock = -1;
+ return -1;
+ }
+
+ rbufsize = 128 * 1024;
+ len = sizeof(rbufsize);
+
+ if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rbufsize, len) < 0)
+ {
+ asldebug("%s: couldn't set receive buffer size for socket %d (%s): %s\n", MY_ID, sock, _PATH_SYSLOG_IN, strerror(errno));
+ close(sock);
+ sock = -1;
+ return -1;
+ }
+
+ if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0)
+ {
+ asldebug("%s: couldn't set O_NONBLOCK for socket %d (%s): %s\n", MY_ID, sock, _PATH_SYSLOG_IN, strerror(errno));
+ close(sock);
+ sock = -1;
+ return -1;
+ }
+
+ chmod(_PATH_SYSLOG_IN, 0666);
+
+ return aslevent_addfd(sock, bsd_in_acceptmsg, NULL, NULL);
+}
+
+int
+bsd_in_reset(void)
+{
+ return 0;
+}
+
+int
+bsd_in_close(void)
+{
+ if (sock < 0) return 1;
+
+ close(sock);
+ unlink(_PATH_SYSLOG_IN);
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * "Portions Copyright (c) 2004 Apple Computer, Inc. All Rights
+ * Reserved. This file contains Original Code and/or Modifications of
+ * Original Code as defined in and that are subject to the Apple Public
+ * Source License Version 1.0 (the 'License'). You may not use this file
+ * except in compliance with the License. Please obtain a copy of the
+ * License at http://www.apple.com/publicsource and read it before using
+ * this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
+ * License for the specific language governing rights and limitations
+ * under the License."
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/uio.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <netdb.h>
+#include <notify.h>
+#include "daemon.h"
+
+#define MY_ID "bsd_out"
+
+#define _PATH_WALL "/usr/bin/wall"
+#define ASL_KEY_FACILITY "Facility"
+#define FACILITY_KERNEL "kern"
+#define _PATH_CONSOLE "/dev/console"
+#define IndexNull ((uint32_t)-1)
+
+#define DST_TYPE_NONE 0
+#define DST_TYPE_FILE 1
+#define DST_TYPE_CONS 2
+#define DST_TYPE_SOCK 3
+#define DST_TYPE_WALL 4
+#define DST_TYPE_NOTE 5
+
+static asl_msg_t *query = NULL;
+static int reset = 0;
+
+struct config_rule
+{
+ uint32_t count;
+ char *dst;
+ int fd;
+ int type;
+ struct sockaddr *addr;
+ char **facility;
+ int *pri;
+ TAILQ_ENTRY(config_rule) entries;
+};
+
+static TAILQ_HEAD(cr, config_rule) bsd_out_rule;
+
+int bsd_out_close();
+static int _parse_config_file(const char *);
+
+static void
+_do_reset(void)
+{
+ bsd_out_close();
+ _parse_config_file(_PATH_SYSLOG_CONF);
+}
+
+static char **
+_insertString(char *s, char **l, uint32_t x)
+{
+ int i, len;
+
+ if (s == NULL) return l;
+ if (l == NULL)
+ {
+ l = (char **)malloc(2 * sizeof(char *));
+ l[0] = strdup(s);
+ l[1] = NULL;
+ return l;
+ }
+
+ for (i = 0; l[i] != NULL; i++);
+ len = i + 1; /* count the NULL on the end of the list too! */
+
+ l = (char **)realloc(l, (len + 1) * sizeof(char *));
+
+ if ((x >= (len - 1)) || (x == IndexNull))
+ {
+ l[len - 1] = strdup(s);
+ l[len] = NULL;
+ return l;
+ }
+
+ for (i = len; i > x; i--) l[i] = l[i - 1];
+ l[x] = strdup(s);
+ return l;
+}
+
+static char **
+_explode(char *s, char *delim)
+{
+ char **l = NULL;
+ char *p, *t;
+ int i, n;
+
+ if (s == NULL) return NULL;
+
+ p = s;
+ while (p[0] != '\0')
+ {
+ for (i = 0; ((p[i] != '\0') && (strchr(delim, p[i]) == NULL)); i++);
+ n = i;
+ t = malloc(n + 1);
+ for (i = 0; i < n; i++) t[i] = p[i];
+ t[n] = '\0';
+ l = _insertString(t, l, IndexNull);
+ free(t);
+ t = NULL;
+ if (p[i] == '\0') return l;
+ if (p[i + 1] == '\0') l = _insertString("", l, IndexNull);
+ p = p + i + 1;
+ }
+
+ return l;
+}
+
+static void
+_freeList(char **l)
+{
+ int i;
+
+ if (l == NULL) return;
+ for (i = 0; l[i] != NULL; i++) free(l[i]);
+ free(l);
+}
+
+static int
+_level_for_name(const char *name)
+{
+ if (name == NULL) return -1;
+
+ if (!strcasecmp(name, "emerg")) return ASL_LEVEL_EMERG;
+ if (!strcasecmp(name, "panic")) return ASL_LEVEL_EMERG;
+ if (!strcasecmp(name, "alert")) return ASL_LEVEL_ALERT;
+ if (!strcasecmp(name, "crit")) return ASL_LEVEL_CRIT;
+ if (!strcasecmp(name, "err")) return ASL_LEVEL_ERR;
+ if (!strcasecmp(name, "error")) return ASL_LEVEL_ERR;
+ if (!strcasecmp(name, "warn")) return ASL_LEVEL_WARNING;
+ if (!strcasecmp(name, "warning")) return ASL_LEVEL_WARNING;
+ if (!strcasecmp(name, "notice")) return ASL_LEVEL_NOTICE;
+ if (!strcasecmp(name, "info")) return ASL_LEVEL_INFO;
+ if (!strcasecmp(name, "debug")) return ASL_LEVEL_DEBUG;
+ if (!strcmp(name, "*")) return ASL_LEVEL_DEBUG;
+
+ /* special case */
+ if (!strcasecmp(name, "none")) return -2;
+
+ return -1;
+}
+
+static int
+_syslog_dst_open(struct config_rule *r)
+{
+ int i;
+ char *node, *serv;
+ struct addrinfo hints, *gai, *ai;
+
+ if (r == NULL) return -1;
+
+ r->fd = -1;
+
+ if (r->dst[0] == '/')
+ {
+ r->fd = open(r->dst, O_WRONLY | O_APPEND | O_CREAT, 0644);
+ if (r->fd < 0)
+ {
+ asldebug("%s: open failed for file: %s (%s)\n", MY_ID, r->dst, strerror(errno));
+ return -1;
+ }
+
+ r->type = DST_TYPE_FILE;
+ if (!strcmp(r->dst, _PATH_CONSOLE)) r->type = DST_TYPE_CONS;
+
+ return 0;
+ }
+
+ if (r->dst[0] == '!')
+ {
+ r->type = DST_TYPE_NOTE;
+ return 0;
+ }
+
+ if (r->dst[0] == '@')
+ {
+ node = strdup(r->dst + 1);
+ if (node == NULL) return -1;
+
+ serv = NULL;
+ serv = strrchr(node, ':');
+ if (serv != NULL) *serv++ = '\0';
+ else serv = "syslog";
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ i = getaddrinfo(node, serv, &hints, &gai);
+ free(node);
+ if (i != 0)
+ {
+ asldebug("%s: getaddrinfo failed for node %s service %s: (%s)\n", MY_ID, node, serv, gai_strerror(i));
+ return -1;
+ }
+
+ for (ai = gai; ai != NULL; ai = ai->ai_next)
+ {
+ r->fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if (r->fd < 0) continue;
+
+ r->addr = (struct sockaddr *)calloc(1, ai->ai_addrlen);
+ memcpy(r->addr, ai->ai_addr, ai->ai_addrlen);
+
+ break;
+ }
+
+ freeaddrinfo(gai);
+
+ if (r->fd < 0)
+ {
+ asldebug("%s: connection failed for %s\n", MY_ID, (r->dst) + 1);
+ return -1;
+ }
+
+ if (fcntl(r->fd, F_SETFL, O_NONBLOCK) < 0)
+ {
+ close(r->fd);
+ r->fd = -1;
+ asldebug("%s: couldn't set O_NONBLOCK for fd %d: %s\n", MY_ID, r->fd, strerror(errno));
+ return -1;
+ }
+
+ r->type = DST_TYPE_SOCK;
+ return 0;
+
+ }
+
+ if (strcmp(r->dst, "*") == 0)
+ {
+ r->type = DST_TYPE_WALL;
+ return 0;
+ }
+
+ /* Can't deal with dst! */
+ asldebug("%s: unsupported / unknown output name: %s\n", MY_ID, r->dst);
+ return -1;
+}
+
+static int
+_parse_line(char *s)
+{
+ char **semi, **comma;
+ int i, j, n, lasts, lastc, pri;
+ struct config_rule *out;
+
+ if (s == NULL) return -1;
+ while ((*s == ' ') || (*s == '\t')) s++;
+ if (*s == '#') return -1;
+
+ semi = _explode(s, "; \t");
+
+ if (semi == NULL) return -1;
+ out = (struct config_rule *)calloc(1, sizeof(struct config_rule));
+ if (out == NULL) return -1;
+
+ n = 0;
+ lasts = -1;
+ for (i = 0; semi[i] != NULL; i++)
+ {
+ if (semi[i][0] == '\0') continue;
+ n++;
+ lasts = i;
+ }
+
+ out->dst = strdup(semi[lasts]);
+ _syslog_dst_open(out);
+
+ for (i = 0; i < lasts; i++)
+ {
+ if (semi[i][0] == '\0') continue;
+ comma = _explode(semi[i], ",.");
+ lastc = -1;
+ for (j = 0; comma[j] != NULL; j++)
+ {
+ if (comma[j][0] == '\0') continue;
+ lastc = j;
+ }
+
+ for (j = 0; j < lastc; j++)
+ {
+ if (comma[j][0] == '\0') continue;
+ pri = _level_for_name(comma[lastc]);
+ if (pri == -1) continue;
+
+ if (out->count == 0)
+ {
+ out->facility = (char **)calloc(1, sizeof(char *));
+ out->pri = (int *)calloc(1, sizeof(int));
+ }
+ else
+ {
+ out->facility = (char **)realloc(out->facility, (out->count + 1) * sizeof(char *));
+ out->pri = (int *)realloc(out->pri, (out->count + 1) * sizeof(int));
+ }
+ out->facility[out->count] = strdup(comma[j]);
+ out->pri[out->count] = pri;
+ out->count++;
+ }
+
+ _freeList(comma);
+ }
+
+ _freeList(semi);
+
+ TAILQ_INSERT_TAIL(&bsd_out_rule, out, entries);
+
+ return 0;
+}
+
+static int
+_syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd)
+{
+ char *so, *sf, *vt, *p;
+ const char *vtime, *vhost, *vident, *vpid, *vmsg, *vlevel, *vfacility;
+ size_t outlen, n;
+ int pf, fc, status;
+ FILE *pw;
+ time_t tick;
+
+ if (out == NULL) return -1;
+ if (fwd == NULL) return -1;
+
+ if (r->type == DST_TYPE_NOTE)
+ {
+ notify_post(r->dst+1);
+ return 0;
+ }
+
+ vt = NULL;
+
+ /* Build output string if it hasn't been built by a previous rule-match */
+ if (*out == NULL)
+ {
+ vtime = asl_get(msg, ASL_KEY_TIME);
+ if (vtime != NULL)
+ {
+ tick = asl_parse_time(vtime);
+ if (tick != (time_t)-1)
+ {
+ p = ctime(&tick);
+ vt = malloc(16);
+ if (vt == NULL) return -1;
+ memcpy(vt, p+4, 15);
+ vt[15] = '\0';
+ }
+ }
+ else if (strlen(vtime) < 24) vt = strdup(vtime);
+ else
+ {
+ vt = malloc(16);
+ if (vt == NULL) return -1;
+ memcpy(vt, vtime+4, 15);
+ vt[15] = '\0';
+ }
+
+ if (vt == NULL)
+ {
+ tick = time(NULL);
+ p = ctime(&tick);
+ vt = malloc(16);
+ if (vt == NULL) return -1;
+ memcpy(vt, p+4, 15);
+ vt[15] = '\0';
+ }
+
+ vhost = asl_get(msg, ASL_KEY_HOST);
+ if (vhost == NULL) vhost = "localhost";
+
+ vident = asl_get(msg, ASL_KEY_SENDER);
+ if ((vident != NULL) && (!strcmp(vident, "Unknown"))) vident = NULL;
+
+ vpid = asl_get(msg, ASL_KEY_PID);
+ if ((vpid != NULL) && (!strcmp(vpid, "-1"))) vpid = NULL;
+
+ if ((vpid != NULL) && (vident == NULL)) vident = "Unknown";
+
+ vmsg = asl_get(msg, ASL_KEY_MSG);
+
+ n = 0;
+ if (vt != NULL) n += (strlen(vt) + 1);
+ if (vhost != NULL) n += (strlen(vhost) + 1);
+ if (vident != NULL) n += strlen(vident);
+ n += 2;
+ if (vpid != NULL) n += (strlen(vpid) + 2);
+ if (vmsg != NULL) n += strlen(vmsg);
+
+ if (n == 0) return -1;
+ n += 2;
+
+ so = calloc(1, n);
+ if (so == NULL) return -1;
+
+ if (vt != NULL)
+ {
+ strcat(so, vt);
+ strcat(so, " ");
+ }
+
+ if (vhost != NULL)
+ {
+ strcat(so, vhost);
+ strcat(so, " ");
+ }
+
+ if (vident != NULL)
+ {
+ strcat(so, vident);
+ if (vpid != NULL)
+ {
+ strcat(so, "[");
+ strcat(so, vpid);
+ strcat(so, "]");
+ }
+ }
+
+ strcat(so, ": ");
+
+ if (vmsg != NULL)
+ {
+ strcat(so, vmsg);
+ strcat(so, "\n");
+ }
+
+ free(vt);
+ *out = so;
+ }
+
+ if ((*fwd == NULL) && (r->type == DST_TYPE_SOCK))
+ {
+ pf = 7;
+ vlevel = asl_get(msg, ASL_KEY_LEVEL);
+ if (vlevel != NULL) pf = atoi(vlevel);
+
+ fc = asl_syslog_faciliy_name_to_num(asl_get(msg, ASL_KEY_FACILITY));
+ if (fc > 0) pf |= fc;
+
+ sf = NULL;
+ asprintf(&sf, "<%d>%s", pf, *out);
+ if (sf == NULL) return -1;
+ *fwd = sf;
+ }
+
+ outlen = 0;
+ if (r->type == DST_TYPE_SOCK) outlen = strlen(*fwd);
+ else outlen = strlen(*out);
+
+ if ((r->type == DST_TYPE_FILE) || (r->type == DST_TYPE_CONS))
+ {
+ /*
+ * Special case for kernel messages.
+ * Don't write kernel messages to /dev/console.
+ * The kernel printf routine already sends them to /dev/console
+ * so writing them here would cause duplicates.
+ */
+ vfacility = asl_get(msg, ASL_KEY_FACILITY);
+ if ((vfacility != NULL) && (!strcmp(vfacility, FACILITY_KERNEL)) && (r->type == DST_TYPE_CONS)) return 0;
+
+ status = write(r->fd, *out, outlen);
+ if ((status < 0) || (status < outlen))
+ {
+ asldebug("%s: error writing message (%s): %s\n", MY_ID, r->dst, strerror(errno));
+
+ /* Try re-opening the file (once) and write again */
+ close(r->fd);
+ r->fd = open(r->dst, O_WRONLY | O_APPEND | O_CREAT, 0644);
+ if (r->fd < 0)
+ {
+ asldebug("%s: re-open failed for file: %s (%s)\n", MY_ID, r->dst, strerror(errno));
+ return -1;
+ }
+
+ status = write(r->fd, *out, outlen);
+ if ((status < 0) || (status < outlen))
+ {
+ asldebug("%s: error re-writing message (%s): %s\n", MY_ID, r->dst, strerror(errno));
+ }
+ }
+ }
+ else if (r->type == DST_TYPE_SOCK)
+ {
+ status = sendto(r->fd, *fwd, outlen, 0, r->addr, r->addr->sa_len);
+ if (status < 0) asldebug("%s: error sending message (%s): %s\n", MY_ID, r->dst, strerror(errno));
+ }
+ else if (r->type == DST_TYPE_WALL)
+ {
+ pw = popen(_PATH_WALL, "w");
+ if (pw < 0)
+ {
+ asldebug("%s: error sending wall message: %s\n", MY_ID, strerror(errno));
+ return -1;
+ }
+
+ fprintf(pw, *out);
+ pclose(pw);
+ }
+
+ return 0;
+}
+
+static int
+_syslog_rule_match(asl_msg_t *msg, struct config_rule *r)
+{
+ uint32_t i, test, f, pri;
+ const char *val;
+
+ if (msg == NULL) return 0;
+ if (r == NULL) return 0;
+ if (r->count == 0) return 0;
+
+ test = 0;
+
+ for (i = 0; i < r->count; i++)
+ {
+ if (r->pri[i] == -1) continue;
+
+ if ((test == 1) && (r->pri[i] >= 0)) continue;
+ if ((test == 0) && (r->pri[i] == -2)) continue;
+
+ f = 0;
+ if (strcmp(r->facility[i], "*") == 0) f = 1;
+ else
+ {
+ val = asl_get(msg, ASL_KEY_FACILITY);
+ if ((val != NULL) && (strcasecmp(r->facility[i], val) == 0)) f = 1;
+ }
+
+ if (f == 0) continue;
+
+ /* Turn off matching facility with priority "none" */
+ if (r->pri[i] == -2)
+ {
+ test = 0;
+ continue;
+ }
+
+ val = asl_get(msg, ASL_KEY_LEVEL);
+ if (val == NULL) continue;
+
+ pri = atoi(val);
+ if (pri < 0) continue;
+
+ if (pri <= r->pri[i]) test = 1;
+ }
+
+ return test;
+}
+
+int
+bsd_out_sendmsg(asl_msg_t *msg, const char *outid)
+{
+ struct config_rule *r;
+ char *out, *fwd;
+
+ if (reset != 0)
+ {
+ _do_reset();
+ reset = 0;
+ }
+
+ if (msg == NULL) return -1;
+
+ out = NULL;
+ fwd = NULL;
+
+ for (r = bsd_out_rule.tqh_first; r != NULL; r = r->entries.tqe_next)
+ {
+ if (_syslog_rule_match(msg, r) == 1) _syslog_send(msg, r, &out, &fwd);
+ }
+
+ if (out != NULL) free(out);
+ if (fwd != NULL) free(fwd);
+
+ return 0;
+}
+
+static int
+_parse_config_file(const char *confname)
+{
+ FILE *cf;
+ char *line;
+
+ cf = fopen(confname, "r");
+ if (cf == NULL) return 1;
+
+ while (NULL != (line = get_line_from_file(cf)))
+ {
+ _parse_line(line);
+ free(line);
+ }
+
+ fclose(cf);
+
+ return 0;
+}
+
+int
+bsd_out_init(void)
+{
+ asldebug("%s: init\n", MY_ID);
+
+ TAILQ_INIT(&bsd_out_rule);
+
+ query = asl_new(ASL_TYPE_QUERY);
+ aslevent_addmatch(query, MY_ID);
+ aslevent_addoutput(bsd_out_sendmsg, MY_ID);
+
+ _parse_config_file(_PATH_SYSLOG_CONF);
+ return 0;
+}
+
+int
+bsd_out_reset(void)
+{
+ reset = 1;
+ return 0;
+}
+
+int
+bsd_out_close(void)
+{
+ struct config_rule *r, *n;
+ int i;
+
+ n = NULL;
+ for (r = bsd_out_rule.tqh_first; r != NULL; r = n)
+ {
+ n = r->entries.tqe_next;
+
+ if (r->dst != NULL) free(r->dst);
+ if (r->fd > 0) close(r->fd);
+ if (r->addr != NULL) free(r->addr);
+ if (r->facility != NULL)
+ {
+ for (i = 0; i < r->count; i++)
+ {
+ if (r->facility[i] != NULL) free(r->facility[i]);
+ }
+ free(r->facility);
+ }
+ if (r->pri != NULL) free(r->pri);
+
+ TAILQ_REMOVE(&bsd_out_rule, r, entries);
+ free(r);
+ }
+
+ return 0;
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Label</key>
+ <string>com.apple.syslogd</string>
+ <key>ServiceDescription</key>
+ <string>Apple System Log Daemon</string>
+ <key>OnDemand</key>
+ <false/>
+ <key>ProgramArguments</key>
+ <array>
+ <string>/usr/sbin/syslogd</string>
+ </array>
+ <key>ServiceIPC</key>
+ <false/>
+</dict>
+</plist>
--- /dev/null
+/*
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * "Portions Copyright (c) 2004 Apple Computer, Inc. All Rights
+ * Reserved. This file contains Original Code and/or Modifications of
+ * Original Code as defined in and that are subject to the Apple Public
+ * Source License Version 1.0 (the 'License'). You may not use this file
+ * except in compliance with the License. Please obtain a copy of the
+ * License at http://www.apple.com/publicsource and read it before using
+ * this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
+ * License for the specific language governing rights and limitations
+ * under the License."
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/ucred.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/queue.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#define SYSLOG_NAMES
+#include <syslog.h>
+#include "daemon.h"
+
+#define streq(A,B) (strcmp(A,B)==0)
+
+static char myname[MAXHOSTNAMELEN + 1] = {0};
+static int gotname = 0;
+
+struct aslevent
+{
+ int fd;
+ unsigned char read:1;
+ unsigned char write:1;
+ unsigned char except:1;
+ aslreadfn readfn;
+ aslwritefn writefn;
+ aslexceptfn exceptfn;
+ char *sender;
+ uid_t uid;
+ gid_t gid;
+ TAILQ_ENTRY(aslevent) entries;
+};
+
+struct asloutput
+{
+ aslsendmsgfn sendmsg;
+ const char *outid;
+ TAILQ_ENTRY(asloutput) entries;
+};
+
+struct aslmatch
+{
+ char *outid;
+ asl_msg_t *query;
+ TAILQ_ENTRY(aslmatch) entries;
+};
+
+TAILQ_HEAD(ae, aslevent) Eventq;
+TAILQ_HEAD(ao, asloutput) Outq;
+TAILQ_HEAD(am, aslmatch) Matchq;
+
+int
+aslevent_init(void)
+{
+ TAILQ_INIT(&Eventq);
+ TAILQ_INIT(&Outq);
+ TAILQ_INIT(&Matchq);
+
+ return 0;
+}
+
+int
+aslevent_log(asl_msg_t *msg, char *outid)
+{
+ struct asloutput *i;
+ int status = -1;
+
+ for (i = Outq.tqh_first; i != NULL; i = i->entries.tqe_next)
+ {
+ if ((outid != NULL) && (strcmp(i->outid, outid) == 0))
+ {
+ status = i->sendmsg(msg, outid);
+ }
+ }
+
+ return status;
+}
+
+int
+aslevent_addmatch(asl_msg_t *query, char *outid)
+{
+ struct aslmatch *tmp;
+
+ if (query == NULL) return -1;
+ if (outid == NULL) return -1;
+
+ tmp = calloc(1, sizeof(struct aslmatch));
+ if (tmp == NULL) return -1;
+ tmp->query = query;
+ tmp->outid = outid;
+ TAILQ_INSERT_TAIL(&Matchq, tmp, entries);
+
+ return 0;
+}
+
+void
+aslevent_match(asl_msg_t *msg)
+{
+ struct aslmatch *i;
+
+ if (msg == NULL) return;
+
+ for (i = Matchq.tqh_first; i != NULL; i = i->entries.tqe_next)
+ {
+ if (asl_msg_cmp(i->query, msg) != 0)
+ {
+ aslevent_log(msg, i->outid);
+ }
+ }
+}
+
+int
+aslevent_removefd(int fd)
+{
+ struct aslevent *i;
+ int found = -1;
+
+ for (i = Eventq.tqh_first; i != NULL; i = i->entries.tqe_next)
+ {
+ if (fd == i->fd)
+ {
+ asldebug("removing %d\n", i->fd);
+ TAILQ_REMOVE(&Eventq, i, entries);
+ found = 0;
+ if (i->sender != NULL) free(i->sender);
+ free(i);
+ i = NULL;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+const char *
+whatsmyhostname()
+{
+ char *dot;
+
+ if (gotname != 0) return (const char *)myname;
+
+ if (gethostname(myname, MAXHOSTNAMELEN) < 0)
+ {
+ memset(myname, 0, sizeof(myname));
+ return "localhost";
+ }
+
+ if (strcmp(myname, "localhost")) gotname = 1;
+
+ dot = strchr(myname, '.');
+ if (dot != NULL) *dot = '\0';
+
+ return (const char *)myname;
+}
+
+int
+aslevent_addfd(int fd, aslreadfn readfn, aslwritefn writefn, aslexceptfn exceptfn)
+{
+ struct aslevent *e;
+ int found = 0;
+#ifdef LOCAL_PEERCRED
+ struct xucred cr;
+#endif
+ int len;
+ uid_t u;
+ gid_t g;
+ struct sockaddr_storage ss;
+ char *sender, str[256];
+
+ u = 99;
+ g = 99;
+ sender = NULL;
+
+ memset(&ss, 0, sizeof(struct sockaddr_storage));
+
+ len = sizeof(struct sockaddr_storage);
+
+ if (getpeername(fd, (struct sockaddr *)&ss, &len) == 0)
+ {
+ if (len == 0)
+ {
+ /* UNIX Domain socket */
+ snprintf(str, sizeof(str), whatsmyhostname());
+ sender = str;
+ }
+ else
+ {
+ if (inet_ntop(ss.ss_family, (struct sockaddr *)&ss, str, 256) == 0) sender = str;
+ }
+ }
+
+#ifdef LOCAL_PEERCRED
+ len = sizeof(cr);
+
+ if (getsockopt(fd, LOCAL_PEERCRED, 1, &cr, &len) == 0)
+ {
+ u = cr.cr_uid;
+ g = cr.cr_gid;
+ }
+#endif
+
+ asldebug("fd %d UID %d GID %d Sender %s\n", fd, u, g, (sender == NULL) ? "NULL" : sender );
+
+ for (e = Eventq.tqh_first; e != NULL; e = e->entries.tqe_next)
+ {
+ if (fd == e->fd)
+ {
+ e->readfn = readfn;
+ e->writefn = writefn;
+ e->exceptfn = exceptfn;
+ if (e->sender != NULL) free(e->sender);
+ e->sender = NULL;
+ if (sender != NULL) e->sender = strdup(sender);
+ e->uid = u;
+ e->gid = g;
+ found = 1;
+ }
+ }
+
+ if (found) return 0;
+
+ e = calloc(1, sizeof(struct aslevent));
+ if (e == NULL) return -1;
+
+ e->fd = fd;
+ e->readfn = readfn;
+ e->writefn = writefn;
+ e->exceptfn = exceptfn;
+ e->sender = NULL;
+ if (sender != NULL) e->sender = strdup(sender);
+ e->uid = u;
+ e->gid = g;
+
+ TAILQ_INSERT_TAIL(&Eventq, e, entries);
+
+ return 0;
+}
+
+int
+aslmsg_verify(struct aslevent *e, asl_msg_t *msg)
+{
+ const char *val;
+ char buf[32], *timestr;
+ time_t tick;
+ struct tm gtime;
+
+ if (msg == NULL) return -1;
+
+ /* Time */
+ tick = 0;
+ val = asl_get(msg, ASL_KEY_TIME);
+ if (val != NULL) tick = asl_parse_time(val);
+
+ if (tick == 0) tick = time(NULL);
+ memset(>ime, 0, sizeof(struct tm));
+ gmtime_r(&tick, >ime);
+
+ /* Canonical form: YYYY.MM.DD hh:mm:ss UTC */
+ asprintf(×tr, "%d.%02d.%02d %02d:%02d:%02d UTC", gtime.tm_year + 1900, gtime.tm_mon + 1, gtime.tm_mday, gtime.tm_hour, gtime.tm_min, gtime.tm_sec);
+ if (timestr != NULL)
+ {
+ asl_set(msg, ASL_KEY_TIME, timestr);
+ free(timestr);
+ }
+
+ /* Host */
+ if (e == NULL) asl_set(msg, ASL_KEY_HOST, whatsmyhostname());
+ else if (e->sender != NULL) asl_set(msg, ASL_KEY_HOST, e->sender);
+
+ /* Sender */
+ val = asl_get(msg, ASL_KEY_SENDER);
+ if (val == NULL) asl_set(msg, ASL_KEY_SENDER, "Unknown");
+
+ /* PID */
+ val = asl_get(msg, ASL_KEY_PID);
+ if (val == NULL) asl_set(msg, ASL_KEY_PID, "0");
+
+ /* UID */
+ val = asl_get(msg, ASL_KEY_UID);
+ if (val == NULL)
+ {
+ if (e == NULL) asl_set(msg, ASL_KEY_UID, "-2");
+ else if (e->uid == 99) asl_set(msg, ASL_KEY_UID, "-2");
+ else
+ {
+ snprintf(buf, sizeof(buf), "%d", e->uid);
+ asl_set(msg, ASL_KEY_UID, buf);
+ }
+ }
+ else if ((e != NULL) && (e->uid != 99))
+ {
+ snprintf(buf, sizeof(buf), "%d", e->uid);
+ asl_set(msg, ASL_KEY_UID, buf);
+ }
+
+ /* GID */
+ val = asl_get(msg, ASL_KEY_GID);
+ if (val == NULL)
+ {
+ if (e == NULL) asl_set(msg, ASL_KEY_GID, "-2");
+ else if (e->gid == 99) asl_set(msg, ASL_KEY_GID, "-2");
+ else
+ {
+ snprintf(buf, sizeof(buf), "%d", e->gid);
+ asl_set(msg, ASL_KEY_GID, buf);
+ }
+ }
+ else if ((e != NULL) && (e->gid != 99))
+ {
+ snprintf(buf, sizeof(buf), "%d", e->gid);
+ asl_set(msg, ASL_KEY_GID, buf);
+ }
+
+ /* Level */
+ val = asl_get(msg, ASL_KEY_LEVEL);
+ if (val == NULL) asl_set(msg, ASL_KEY_LEVEL, "7");
+
+ return 0;
+}
+
+int
+aslevent_addoutput(aslsendmsgfn fn, const char *outid)
+{
+ struct asloutput *tmp;
+
+ tmp = calloc(1, sizeof(struct asloutput));
+ if (tmp == NULL) return -1;
+
+ tmp->sendmsg = fn;
+ tmp->outid = outid;
+
+ TAILQ_INSERT_TAIL(&Outq, tmp, entries);
+
+ return 0;
+}
+
+int
+aslevent_fdsets(fd_set *rd, fd_set *wr, fd_set *ex)
+{
+ struct aslevent *e;
+ int status = 0;
+
+ asldebug("--> aslevent_fdsets\n");
+ FD_ZERO(rd);
+ FD_ZERO(wr);
+ FD_ZERO(ex);
+
+ for (e = Eventq.tqh_first; e != NULL; e = e->entries.tqe_next)
+ {
+ asldebug("adding fd %d\n", e->fd);
+ if (e->readfn)
+ {
+ FD_SET(e->fd, rd);
+ status = MAX(e->fd, status);
+ }
+
+ if (e->writefn)
+ {
+ FD_SET(e->fd, wr);
+ status = MAX(e->fd, status);
+ }
+
+ if (e->exceptfn)
+ {
+ FD_SET(e->fd, ex);
+ status = MAX(e->fd, status);
+ }
+ }
+
+ asldebug("<--aslevent_fdsets\n");
+ return status;
+}
+
+void
+aslevent_handleevent(fd_set rd, fd_set wr, fd_set ex, char *errstr)
+{
+ struct aslevent *e;
+ char *out = NULL;
+ asl_msg_t *msg;
+
+ asldebug("--> aslevent_handleevent\n");
+ if (errstr) errstr = NULL;
+
+ for (e = Eventq.tqh_first; e != NULL; e = e->entries.tqe_next)
+ {
+ if (FD_ISSET(e->fd, &rd) && (e->readfn != NULL))
+ {
+ asldebug("handling read event on %d\n", e->fd);
+ msg = e->readfn(e->fd);
+ if (msg == NULL)
+ {
+ asldebug("error reading message\n");
+ continue;
+ }
+
+ if (aslmsg_verify(e, msg) < 0)
+ {
+ asl_free(msg);
+ asldebug("recieved invalid message\n");
+ }
+ else
+ {
+ aslevent_match(msg);
+ asl_free(msg);
+ }
+ }
+
+ if (FD_ISSET(e->fd, &ex) && e->exceptfn)
+ {
+ asldebug("handling except event on %d\n", e->fd);
+ out = e->exceptfn(e->fd);
+ if (out == NULL) asldebug("error writing message\n");
+ }
+ }
+
+ asldebug("<-- aslevent_handleevent\n");
+}
+
+int
+asl_log_string(const char *str)
+{
+ asl_msg_t *msg;
+
+ if (str == NULL) return 1;
+
+ msg = asl_msg_from_string(str);
+ if (aslmsg_verify(NULL, msg) < 0)
+ {
+ asl_free(msg);
+ return -1;
+ }
+
+ aslevent_match(msg);
+ asl_free(msg);
+ return 0;
+}
+
+void
+aslmark(void)
+{
+ char *str;
+
+ str = NULL;
+ asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [%s -- MARK --] [%s 0] [%s 0] [Facility syslog]",
+ ASL_KEY_SENDER,
+ ASL_KEY_LEVEL, ASL_LEVEL_INFO,
+ ASL_KEY_PID, getpid(),
+ ASL_KEY_MSG, ASL_KEY_UID, ASL_KEY_GID);
+
+ asl_log_string(str);
+ if (str != NULL) free(str);
+}
+
+asl_msg_t *
+asl_syslog_input_convert(const char *in, int len, char *rhost, int kern)
+{
+ int pf, pri, index, n;
+ char *p, *colon, *brace, *tmp, *tval, *sval, *pval, *mval;
+ char prival[8];
+ const char *fval;
+ asl_msg_t *msg;
+ struct tm time;
+ time_t tick;
+
+ if (in == NULL) return NULL;
+ if (len <= 0) return NULL;
+
+ asldebug("asl_syslog_input_convert: %s\n", in);
+
+ pri = LOG_DEBUG;
+ tval = NULL;
+ sval = NULL;
+ pval = NULL;
+ mval = NULL;
+ fval = NULL;
+
+ index = 0;
+ p = (char *)in;
+
+ while ((index < len) && ((*p == ' ') || (*p == '\t')))
+ {
+ p++;
+ index++;
+ }
+
+ if (index >= len) return NULL;
+
+ if (*p == '<')
+ {
+ p++;
+ index++;
+
+ n = sscanf(p, "%d", &pf);
+ if (n == 1)
+ {
+ pri = pf & 0x7;
+ if (pf > 0x7) fval = asl_syslog_faciliy_num_to_name(pf & LOG_FACMASK);
+ }
+
+ while ((index < len) && (*p != '>'))
+ {
+ p++;
+ index++;
+ }
+
+ if (index < len)
+ {
+ p++;
+ index++;
+ }
+ }
+
+ snprintf(prival, sizeof(prival), "%d", pri);
+
+ if (((len - index) > 15) && (p[9] == ':') && (p[12] == ':') && (p[15] == ' '))
+ {
+ tmp = malloc(16);
+ memcpy(tmp, p, 15);
+ tmp[15] = '\0';
+
+ tick = asl_parse_time(tmp);
+ if (tick == (time_t)-1)
+ {
+ tval = tmp;
+ }
+ else
+ {
+ free(tmp);
+ gmtime_r(&tick, &time);
+ asprintf(&tval, "%d.%02d.%02d %02d:%02d:%02d UTC", time.tm_year + 1900, time.tm_mon + 1, time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec);
+ }
+
+ p += 16;
+ index += 16;
+ }
+
+ if (kern != 0)
+ {
+ msg = (asl_msg_t *)calloc(1, sizeof(asl_msg_t));
+ msg->type = ASL_TYPE_MSG;
+
+ asl_set(msg, ASL_KEY_SENDER, "kernel");
+
+ asl_set(msg, "Facility", "kern");
+ if (tval != NULL)
+ {
+ asl_set(msg, ASL_KEY_TIME, tval);
+ free(tval);
+ }
+
+ asl_set(msg, ASL_KEY_MSG, p);
+
+ asl_set(msg, ASL_KEY_LEVEL, prival);
+
+ asl_set(msg, ASL_KEY_PID, "0");
+
+ asl_set(msg, ASL_KEY_UID, "0");
+
+ asl_set(msg, ASL_KEY_GID, "0");
+
+ asl_set(msg, ASL_KEY_HOST, whatsmyhostname());
+
+ return msg;
+ }
+
+ colon = strchr(p, ':');
+ brace = strchr(p, '[');
+
+ if (colon != NULL)
+ {
+ if ((brace != NULL) && (brace < colon))
+ {
+ n = brace - p;
+ sval = malloc(n + 1);
+ memcpy(sval, p, n);
+ sval[n] = '\0';
+
+ n = colon - (brace + 1) - 1;
+ pval = malloc(n + 1);
+ memcpy(pval, (brace + 1), n);
+ pval[n] = '\0';
+ }
+ else
+ {
+ n = colon - p;
+ sval = malloc(n + 1);
+ memcpy(sval, p, n);
+ sval[n] = '\0';
+ }
+
+ n = colon - p;
+ p = colon + 1;
+ index += (n + 1);
+ }
+
+ if (*p == ' ')
+ {
+ p++;
+ index++;
+ }
+
+ n = len - index;
+ if (n > 0)
+ {
+ mval = malloc(n + 1);
+ memcpy(mval, p, n);
+ mval[n] = '\0';
+ }
+
+ if (fval == NULL) fval = asl_syslog_faciliy_num_to_name(LOG_USER);
+
+ msg = (asl_msg_t *)calloc(1, sizeof(asl_msg_t));
+ msg->type = ASL_TYPE_MSG;
+
+ if (tval != NULL)
+ {
+ asl_set(msg, ASL_KEY_TIME, tval);
+ free(tval);
+ }
+
+ if (fval != NULL) asl_set(msg, "Facility", fval);
+ else asl_set(msg, "Facility", "user");
+
+ if (sval != NULL)
+ {
+ asl_set(msg, ASL_KEY_SENDER, sval);
+ free(sval);
+ }
+
+ if (pval != NULL)
+ {
+ asl_set(msg, ASL_KEY_PID, pval);
+ free(pval);
+ }
+ else asl_set(msg, ASL_KEY_PID, "-1");
+
+ if (mval != NULL)
+ {
+ asl_set(msg, ASL_KEY_MSG, mval);
+ free(mval);
+ }
+
+ asl_set(msg, ASL_KEY_LEVEL, prival);
+ asl_set(msg, ASL_KEY_UID, "-2");
+ asl_set(msg, ASL_KEY_GID, "-2");
+
+ if (rhost == NULL) asl_set(msg, ASL_KEY_HOST, whatsmyhostname());
+ else asl_set(msg, ASL_KEY_HOST, rhost);
+
+ if (msg->count == 0)
+ {
+ asl_free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+char *
+get_line_from_file(FILE *f)
+{
+ char *s, *out;
+ size_t len;
+
+ out = fgetln(f, &len);
+ if (out == NULL) return NULL;
+ if (len == 0) return NULL;
+
+ s = malloc(len + 1);
+ memcpy(s, out, len);
+
+ s[len - 1] = '\0';
+ return s;
+}
--- /dev/null
+/*
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * "Portions Copyright (c) 2004 Apple Computer, Inc. All Rights
+ * Reserved. This file contains Original Code and/or Modifications of
+ * Original Code as defined in and that are subject to the Apple Public
+ * Source License Version 1.0 (the 'License'). You may not use this file
+ * except in compliance with the License. Please obtain a copy of the
+ * License at http://www.apple.com/publicsource and read it before using
+ * this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
+ * License for the specific language governing rights and limitations
+ * under the License."
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifndef __DAEMON_H__
+#define __DAEMON_H__
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <time.h>
+#include <asl.h>
+#include <asl_private.h>
+#include <notify.h>
+
+#define _PATH_PIDFILE "/var/run/syslog.pid"
+#define _PATH_ASL_IN "/var/run/asl_input"
+#define _PATH_ASL_PRUNE "/var/run/asl_prune"
+#define _PATH_ASL_OUT "/var/log/asl.log"
+#define _PATH_SYSLOG_CONF "/etc/syslog.conf"
+#define _PATH_SYSLOG_IN "/var/run/syslog"
+#define _PATH_KLOG "/dev/klog"
+#define _PATH_MODULE_LIB "/usr/lib/asl"
+
+struct module_list
+{
+ char *name;
+ void *module;
+ int (*init)(void);
+ int (*reset)(void);
+ int (*close)(void);
+ TAILQ_ENTRY(module_list) entries;
+};
+
+int aslevent_init(void);
+int aslevent_fdsets(fd_set *, fd_set *, fd_set *);
+void aslevent_handleevent(fd_set, fd_set, fd_set, char *);
+void aslmark(void);
+
+char *get_line_from_file(FILE *f);
+
+int asldebug(const char *, ...);
+int asl_log_string(const char *str);
+
+char *asl_msg_to_string(asl_msg_t *msg, uint32_t *len);
+asl_msg_t *asl_msg_from_string(const char *buf);
+int asl_msg_cmp(asl_msg_t *a, asl_msg_t *b);
+time_t asl_parse_time(const char *str);
+
+typedef asl_msg_t *(*aslreadfn)(int);
+typedef char *(*aslwritefn)(const char *, int);
+typedef char *(*aslexceptfn)(int);
+typedef int (*aslsendmsgfn)(asl_msg_t *msg, const char *outid);
+
+int aslevent_addfd(int fd, aslreadfn, aslwritefn, aslexceptfn);
+int aslevent_removefd(int fd);
+int aslevent_addmatch(asl_msg_t *query, char *outid);
+
+int aslevent_addoutput(aslsendmsgfn, const char *outid);
+
+int asl_syslog_faciliy_name_to_num(const char *fac);
+const char *asl_syslog_faciliy_num_to_name(int num);
+asl_msg_t *asl_syslog_input_convert(const char *in, int len, char *rhost, int flag);
+int asl_prune(asl_msg_t *pq);
+
+/* notify SPI */
+uint32_t notify_get_state(int token, int *state);
+uint32_t notify_set_state(int token, int state);
+uint32_t notify_register_plain(const char *name, int *out_token);
+
+#endif /* __DAEMON_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * "Portions Copyright (c) 2004 Apple Computer, Inc. All Rights
+ * Reserved. This file contains Original Code and/or Modifications of
+ * Original Code as defined in and that are subject to the Apple Public
+ * Source License Version 1.0 (the 'License'). You may not use this file
+ * except in compliance with the License. Please obtain a copy of the
+ * License at http://www.apple.com/publicsource and read it before using
+ * this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
+ * License for the specific language governing rights and limitations
+ * under the License."
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/uio.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include "daemon.h"
+
+#define forever for(;;)
+
+#define MY_ID "klog_in"
+#define MAXLINE 4096
+
+static int kfd = -1;
+
+static int kx = 0;
+static char kline[MAXLINE + 1];
+
+asl_msg_t *
+klog_in_acceptmsg(int fd)
+{
+ int n;
+ char c;
+
+ n = read(fd, &c, 1);
+
+ while ((n == 1) && (c != '\n'))
+ {
+ if (kx < MAXLINE) kline[kx++] = c;
+ n = read(fd, &c, 1);
+ }
+
+ if (kx == 0) return NULL;
+
+ n = kx - 1;
+ kline[kx] = '\0';
+ kx = 0;
+
+ return asl_syslog_input_convert(kline, n, NULL, 1);
+}
+
+int
+klog_in_init(void)
+{
+ asldebug("%s: init\n", MY_ID);
+ if (kfd >= 0) return kfd;
+
+ kfd = open(_PATH_KLOG, O_RDONLY, 0);
+ if (kfd < 0)
+ {
+ asldebug("%s: couldn't open %s: %s\n", MY_ID, _PATH_KLOG, strerror(errno));
+ return -1;
+ }
+
+ if (fcntl(kfd, F_SETFL, O_NONBLOCK) < 0)
+ {
+ close(kfd);
+ kfd = -1;
+ asldebug("%s: couldn't set O_NONBLOCK for fd %d (%s): %s\n", MY_ID, kfd, _PATH_KLOG, strerror(errno));
+ return -1;
+ }
+
+ return aslevent_addfd(kfd, klog_in_acceptmsg, NULL, NULL);
+}
+
+int
+klog_in_reset(void)
+{
+ return 0;
+}
+
+int
+klog_in_close(void)
+{
+ if (kfd < 0) return 1;
+
+ close(kfd);
+ kfd = -1;
+
+ return 0;
+}
--- /dev/null
+.\" Copyright (c) 1990, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" from: @(#)syslog.conf.5 8.1 (Berkeley) 6/9/93
+.\" $NetBSD: syslog.conf.5,v 1.4 1996/01/02 17:41:46 perry Exp $
+.\"
+.Dd June 9, 1993
+.Dt SYSLOG.CONF 5
+.Os
+.Sh NAME
+.Nm syslog.conf
+.Nd
+.Xr syslogd 8
+configuration file
+.Sh DESCRIPTION
+The
+.Nm syslog.conf
+file is the configuration file for the
+.Xr syslogd 8
+program.
+It consists of lines with two fields: the
+.Em selector
+field which specifies the types of messages and priorities to which the
+line applies, and an
+.Em action
+field which specifies the action to be taken if a message
+.Xr syslogd
+receives matches the selection criteria.
+The
+.Em selector
+field is separated from the
+.Em action
+field by one or more tab characters.
+.Pp
+The
+.Em Selectors
+function
+are encoded as a
+.Em facility ,
+a period (``.''), and a
+.Em level ,
+with no intervening white-space.
+Both the
+.Em facility
+and the
+.Em level
+are case insensitive.
+.Pp
+The
+.Em facility
+describes the part of the system generating the message, and is one of
+the following keywords: auth, authpriv, cron, daemon, kern, lpr, mail,
+mark, news, syslog, user, uucp and local0 through local7.
+These keywords (with the exception of mark) correspond to the
+similar
+.Dq Dv LOG_
+values specified to the
+.Xr openlog 3
+and
+.Xr syslog 3
+library routines.
+.Pp
+The
+.Em level
+describes the severity of the message, and is a keyword from the
+following ordered list (higher to lower): emerg, alert, crit, err,
+warning, notice, info and debug.
+These keywords correspond to the
+similar
+.Pq Dv LOG_
+values specified to the
+.Xr syslog
+library routine.
+.Pp
+See
+.Xr syslog 3
+for a further descriptions of both the
+.Em facility
+and
+.Em level
+keywords and their significance.
+.Pp
+If a received message matches the specified
+.Em facility
+and is of the specified
+.Em level
+.Em (or a higher level) ,
+the action specified in the
+.Em action
+field will be taken.
+.Pp
+Multiple
+.Em selectors
+may be specified for a single
+.Em action
+by separating them with semicolon (``;'') characters.
+It is important to note, however, that each
+.Em selector
+can modify the ones preceding it.
+.Pp
+Multiple
+.Em facilities
+may be specified for a single
+.Em level
+by separating them with comma (``,'') characters.
+.Pp
+An asterisk (``*'') can be used to specify all
+.Em facilities
+or all
+.Em levels .
+.Pp
+The special
+.Em facility
+``mark'' receives a message at priority ``info'' every 20 minutes
+(see
+.Xr syslogd 8 ) .
+This is not enabled by a
+.Em facility
+field containing an asterisk.
+.Pp
+The special
+.Em level
+``none'' disables a particular
+.Em facility .
+.Pp
+The
+.Em action
+field of each line specifies the action to be taken when the
+.Em selector
+field selects a message.
+There are four forms:
+.Bl -bullet
+.It
+A pathname (beginning with a leading slash).
+Selected messages are appended to the file.
+.It
+A hostname (preceded by an at (``@'') sign).
+Selected messages are forwarded to the
+.Xr syslogd
+program on the named host.
+.It
+A comma separated list of users.
+Selected messages are written to those users
+if they are logged in.
+.It
+An asterisk.
+Selected messages are written to all logged-in users.
+.El
+.Pp
+Blank lines and lines whose first non-blank character is a hash (``#'')
+character are ignored.
+.Sh EXAMPLES
+.Pp
+A configuration file might appear as follows:
+.Bd -literal
+# Log all kernel messages, authentication messages of
+# level notice or higher and anything of level err or
+# higher to the console.
+# Don't log private authentication messages!
+*.err;kern.*;auth.notice;authpriv.none /dev/console
+
+# Log anything (except mail) of level info or higher.
+# Don't log private authentication messages!
+*.info;mail.none;authpriv.none /var/log/messages
+
+# The authpriv file has restricted access.
+authpriv.* /var/log/secure
+
+# Log all the mail messages in one place.
+mail.* /var/log/maillog
+
+# Everybody gets emergency messages, plus log them on another
+# machine.
+*.emerg *
+*.emerg @arpa.berkeley.edu
+
+# Root and Eric get alert and higher messages.
+*.alert root,eric
+
+# Save mail and news errors of level err and higher in a
+# special file.
+uucp,news.crit /var/log/spoolerr
+.Ed
+.Sh FILES
+.Bl -tag -width /etc/syslog.conf -compact
+.It Pa /etc/syslog.conf
+The
+.Xr syslogd 8
+configuration file.
+.El
+.Sh BUGS
+The effects of multiple selectors are sometimes not intuitive.
+For example ``mail.crit,*.err'' will select ``mail'' facility messages at
+the level of ``err'' or higher, not at the level of ``crit'' or higher.
+.Sh SEE ALSO
+.Xr syslog 3 ,
+.Xr syslogd 8
+.Sh HISTORY
+The
+.Nm
+file appeared in
+.Bx 4.3 ,
+along with
+.Xr syslogd 8 .
--- /dev/null
+.\" Copyright (c) 2004 Apple Computer
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 4. Neither the name of Apple Computer nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"
+.Dd October 18, 2004
+.Dt SYSLOGD 8
+.Os "Mac OS X"
+.Sh NAME
+.Nm syslogd
+.Nd Apple System Log server
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl D
+.Op Fl m Ar mark_interval
+.Op Fl p Ar prune_days
+.Op Fl c Ar log_cutoff
+.Op Fl l Ar lib_path
+.Op Fl u
+.Op Fl module_name Li {0|1}
+.Sh DESCRIPTION
+The
+.Nm
+server receives and processes log messages.
+Several modules receive input messages through various channels,
+including UNIX domain sockets associated with the
+.Xr syslog 3 ,
+.Xr asl 3 ,
+and kernel printf APIs,
+and optionally from a UDP socket if the
+.Dq udp_in
+module is enabled.
+.Pp
+The Apple System Log facility comprises the
+.Xr asl 3
+API, a new
+.Nm
+server, and the
+.Xr syslog 1
+command-line utility.
+The system supports structured and extensible messages,
+permitting advanced message browsing and management through search APIs and
+other components of the Apple system log facility.
+.Pp
+Log messages are retained in a data store,
+subject to pruning and input filtering as described below,
+to simplify the task of locating log messages and to facilitate browsing and searching.
+The data store is intended to become a replacement for the numerous log files that are currently
+found in various locations on the system.
+Those files will be phased out in future versions of Mac OS.
+.Pp
+The following options are recognized:
+.Bl -tag -width indent
+.It Fl d
+Run
+.Nm
+in debugging mode.
+The server stays attached to the controlling terminal and prints debugging messages.
+.It Fl D
+Start as a daemon.
+This option forces
+.Nm
+to fork and have the child process become a daemon.
+Since
+.Nm
+is started by
+.Nm launchd ,
+this is not normally required.
+.It Fl m
+Set the number of minutes between
+.Dq mark
+messages.
+The default is 20 minutes.
+The
+.Dq mark
+facility is disabled if the setting is zero minutes.
+.It Fl p
+.Nm
+saves log messages in a data store that may be searched using the
+.Xr syslog 1
+utility or with the
+.Xr asl 3
+API.
+The data store is pruned daily by the /etc/daily cron job to keep it from growing without bound.
+Since many systems are shut down overnight (when the daily cron job runs),
+the data store is also pruned shortly after
+.Nm
+starts up as the system boots.
+By default, log messages in the data store that are more than 7 days old are removed.
+The setting of the
+.Fl p Ar prune_days
+overrides the default.
+A setting of zero days disables pruning of the data store when
+.Nm
+starts up.
+.It Fl c
+Sets a cutoff filter for log priorities for messages to be retained in the log message data store.
+The value of
+.Ar log_cutoff
+must be between 0 and 7, corresponding to log priorities LOG_EMERG or ASL_LEVEL_EMERG
+and LOG_DEBUG or ASL_LEVEL_DEBUG as defined in the
+.Xr syslog 3
+and
+.Xr asl 3
+header files.
+Received messages with a priority or level value greater than the cutoff will not be saved in the data store.
+The default filter will retain messages in the range 0 (Emergency) to 5 (Notice) inclusive.
+.Pp
+Note that a this filter value may be adjusted while
+.Nm
+is running using the
+.Nm syslog
+command-line utility.
+See the
+.Xr syslog 1
+manual.
+The filter may be adjusted using the
+.Dq -c
+option, e.g.
+.Pp
+.Li sudo syslog -c syslogd -d
+.Pp
+will set the filter to retain messages in the range 0 (Emergency) to 7 (Debug).
+.It Fl l
+Specifies an alternate path for loading plug-in modules.
+By default,
+.Nm
+checks for plug-in modules in the directory /usr/lib/asl.
+.It Fl u
+Enables the
+.Dq udp_in
+module, configuring
+.Nm
+to act as a network log message receiver.
+The server will receive messages on the standard
+.Dq syslog
+UDP port.
+Note that this opens the server to potential denial-of-service attacks,
+as a malicious remote sender can flood the server with messages.
+The
+.Fl u
+option is equivalent to using the
+.Fl udp_in Li 1
+option.
+.El
+.Pp
+The remaining options of the form
+.Fl module_name Li {0|1}
+may be used to disable (0) or enable (1) the action of several of
+.Mn 's
+internal modules.
+.Bl -tag -width "-asl_action"
+.It Fl asl_in
+The
+.Dq asl_in
+module receives log messages on the UNIX domain socket associated with the
+.Xr asl 3
+API.
+The module may be disabled using
+.Fl asl_in Li 0 .
+The module is normally enabled.
+.It Fl asl_action
+The
+.Dq asl_action
+module examines the stream of received log messages and acts upon them according to the rules specified
+in the file /etc/asl.conf.
+See
+.Xr asl.conf 5
+for details.
+.It Fl klog_in
+The
+.Dq klog_in
+module receives log messages on the UNIX domain socket associated with the kernel logging API.
+The module may be disabled using
+.Fl klog_in Li 0 .
+The module is normally enabled.
+.It Fl bsd_in
+The
+.Dq bsd_in
+module receives log messages on the UNIX domain socket associated with the
+.Xr syslog 3
+API.
+The module may be disabled using
+.Fl bsd_in Li 0 .
+The module is normally enabled.
+.It Fl bsd_out
+The
+.Dq bsd_out
+module examines the stream of received log messages and acts upon them according to the rules specified
+in the file /etc/syslog.conf.
+See
+.Xr syslog.conf 5
+for details.
+This module exists for backward compatibility with previous
+.Nm
+implementations.
+Apple encourages use of the
+.Xr syslog 1
+and
+.Xr asl 3
+search APIs over the use of the log files that are specified in the /etc/syslog.conf file.
+Future versions of Mac OS will move functions that are currently handled by the
+.Dq bsd_out
+module to the
+.Dq asl_action
+module.
+.It Fl udp_in
+The
+.Dq udp_in
+module receives log messages on the UDP socket associated with the Internet syslog message protocol.
+The module may be enabled using
+.Fl udp_in Li 1 .
+The module is normally disabled.
+This module may also be enabled using the
+.Fl u
+option.
+.El
+.Pp
+.Nm
+initializes its built-in modules and loads plug-ins during its start-up.
+The data store is pruned approximately 5 minutes after startup.
+.Pp
+.Nm
+reinitializes in response to a HUP signal.
+.Sh FILES
+.Bl -tag -width /var/run/syslog.pid -compact
+.It Pa /etc/syslog.conf
+bsd_out module configuration file
+.It Pa /etc/asl.conf
+asl_action module configuration file
+.It Pa /var/run/syslog.pid
+process ID file
+.It Pa /var/run/log
+name of the
+.Ux
+domain datagram log socket
+.It Pa /dev/klog
+kernel log device
+.El
+.Sh SEE ALSO
+.Xr syslog 1 ,
+.Xr logger 1 ,
+.Xr asl 3 ,
+.Xr syslog 3 ,
+.Xr asl.conf 5
+.Xr syslog.conf 5
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.3 .
+.Pp
+The Apple System Log facility was introduced in Mac OS X 10.4.
--- /dev/null
+/*
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * "Portions Copyright (c) 2004 Apple Computer, Inc. All Rights
+ * Reserved. This file contains Original Code and/or Modifications of
+ * Original Code as defined in and that are subject to the Apple Public
+ * Source License Version 1.0 (the 'License'). You may not use this file
+ * except in compliance with the License. Please obtain a copy of the
+ * License at http://www.apple.com/publicsource and read it before using
+ * this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
+ * License for the specific language governing rights and limitations
+ * under the License."
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/fcntl.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <libgen.h>
+#include <notify.h>
+#include "daemon.h"
+
+#define DEFAULT_MARK_SEC 0
+#define DEFAULT_PRUNE_DAYS 7
+#define PRUNE_AFTER_START_DELAY 300
+
+#define NOTIFY_DELAY 1
+
+#define streq(A,B) (strcmp(A,B)==0)
+#define forever for(;;)
+
+static int debug = 0;
+static int reset = 0;
+
+static TAILQ_HEAD(ml, module_list) Moduleq;
+
+/* global */
+int asl_log_filter = ASL_FILTER_MASK_UPTO(ASL_LEVEL_NOTICE);
+int prune = 0;
+
+extern int __notify_78945668_info__;
+
+/* Static Modules */
+int asl_in_init();
+int asl_in_reset();
+int asl_in_close();
+static int activate_asl_in = 1;
+
+int asl_action_init();
+int asl_action_reset();
+int asl_action_close();
+static int activate_asl_action = 1;
+
+int klog_in_init();
+int klog_in_reset();
+int klog_in_close();
+static int activate_klog_in = 1;
+
+int bsd_in_init();
+int bsd_in_reset();
+int bsd_in_close();
+static int activate_bsd_in = 1;
+
+int bsd_out_init();
+int bsd_out_reset();
+int bsd_out_close();
+static int activate_bsd_out = 1;
+
+int udp_in_init();
+int udp_in_reset();
+int udp_in_close();
+static int activate_udp_in = 0;
+
+/*
+ * Module approach: only one type of module. This module may implement
+ * the set of functions necessary for any of the functions (input, output,
+ * etc.) Prior to using the modules, dlsym() is consulted to see what it
+ * implements.
+ */
+
+static int
+static_modules()
+{
+ struct module_list *tmp;
+
+ if (activate_asl_in != 0)
+ {
+ tmp = calloc(1, sizeof(struct module_list));
+ if (tmp == NULL) return 1;
+ tmp->name = strdup("asl_in");
+ tmp->module = NULL;
+ tmp->init = asl_in_init;
+ tmp->reset = asl_in_reset;
+ tmp->close = asl_in_close;
+ TAILQ_INSERT_TAIL(&Moduleq, tmp, entries);
+ }
+
+ if (activate_asl_action != 0)
+ {
+ tmp = calloc(1, sizeof(struct module_list));
+ if (tmp == NULL) return 1;
+ tmp->name = strdup("asl_action");
+ tmp->module = NULL;
+ tmp->init = asl_action_init;
+ tmp->reset = asl_action_reset;
+ tmp->close = asl_action_close;
+ TAILQ_INSERT_TAIL(&Moduleq, tmp, entries);
+ }
+
+ if (activate_klog_in != 0)
+ {
+ tmp = calloc(1, sizeof(struct module_list));
+ if (tmp == NULL) return 1;
+ tmp->name = strdup("klog_in");
+ tmp->module = NULL;
+ tmp->init = klog_in_init;
+ tmp->reset = klog_in_reset;
+ tmp->close = klog_in_close;
+ TAILQ_INSERT_TAIL(&Moduleq, tmp, entries);
+ }
+
+ if (activate_bsd_in != 0)
+ {
+ tmp = calloc(1, sizeof(struct module_list));
+ if (tmp == NULL) return 1;
+ tmp->name = strdup("bsd_in");
+ tmp->module = NULL;
+ tmp->init = bsd_in_init;
+ tmp->reset = bsd_in_reset;
+ tmp->close = bsd_in_close;
+ TAILQ_INSERT_TAIL(&Moduleq, tmp, entries);
+ }
+
+ if (activate_bsd_out != 0)
+ {
+ tmp = calloc(1, sizeof(struct module_list));
+ if (tmp == NULL) return 1;
+ tmp->name = strdup("bsd_out");
+ tmp->module = NULL;
+ tmp->init = bsd_out_init;
+ tmp->reset = bsd_out_reset;
+ tmp->close = bsd_out_close;
+ TAILQ_INSERT_TAIL(&Moduleq, tmp, entries);
+ }
+
+ if (activate_udp_in != 0)
+ {
+ tmp = calloc(1, sizeof(struct module_list));
+ if (tmp == NULL) return 1;
+ tmp->name = strdup("udp_in");
+ tmp->module = NULL;
+ tmp->init = udp_in_init;
+ tmp->reset = udp_in_reset;
+ tmp->close = udp_in_close;
+ TAILQ_INSERT_TAIL(&Moduleq, tmp, entries);
+ }
+
+ return 0;
+}
+
+/*
+ * Loads all the modules. This DOES NOT call the modules initializer
+ * functions. It simply scans the modules directory looking for modules
+ * and loads them. This does not mean the module will be used.
+ */
+static int
+load_modules(char *mp)
+{
+ DIR *d;
+ struct dirent *de;
+ struct module_list *tmp;
+ void *c, *bn;
+ char *modulepath = NULL;
+
+ d = opendir(mp);
+ if (d == NULL) return -1;
+
+ while (NULL != (de = readdir(d)))
+ {
+ if (de->d_name[0] == '.') continue;
+
+ /* Must have ".so" in the name" */
+ if (!strstr(de->d_name, ".so")) continue;
+
+ asprintf(&modulepath, "%s/%s", mp, de->d_name);
+ if (!modulepath) continue;
+
+ c = dlopen(modulepath, RTLD_LOCAL);
+ if (c == NULL)
+ {
+ free(modulepath);
+ continue;
+ }
+
+ tmp = calloc(1, sizeof(struct module_list));
+ if (tmp == NULL)
+ {
+ free(modulepath);
+ dlclose(c);
+ continue;
+ }
+
+ bn = basename(modulepath);
+ tmp->name = strdup(bn);
+ tmp->module = c;
+ TAILQ_INSERT_TAIL(&Moduleq, tmp, entries);
+
+ tmp->init = dlsym(tmp->module, "aslmod_init");
+ tmp->reset = dlsym(tmp->module, "aslmod_reset");
+ tmp->close = dlsym(tmp->module, "aslmod_close");
+
+ free(modulepath);
+ }
+
+ closedir(d);
+
+ return 0;
+}
+
+static void
+writepid(void)
+{
+ FILE *fp;
+
+ fp = fopen(_PATH_PIDFILE, "w");
+ if (fp != NULL)
+ {
+ fprintf(fp, "%d\n", getpid());
+ fclose(fp);
+ }
+}
+
+static void
+closeall(void)
+{
+ int i;
+
+ for (i = getdtablesize() - 1; i >= 0; i--) close(i);
+
+ open("/dev/null", O_RDWR, 0);
+ dup(0);
+ dup(0);
+}
+
+static void
+detach(void)
+{
+ signal(SIGINT, SIG_IGN);
+ signal(SIGPIPE, SIG_IGN);
+ setsid();
+}
+
+static void
+catch_sighup(int x)
+{
+ reset = 1;
+}
+
+static void
+catch_sigwinch(int x)
+{
+ prune = 1;
+}
+
+static void
+send_reset(void)
+{
+ struct module_list *mod;
+
+ for (mod = Moduleq.tqh_first; mod != NULL; mod = mod->entries.tqe_next)
+ {
+ if (mod->reset != NULL) mod->reset();
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct module_list *mod;
+ fd_set rd, wr, ex;
+ int fd, i, max, status, pdays, daemonize;
+ time_t lastmark, msec, ssec, tick, delta, ptime, notify_time;
+ char *mp, *str;
+ struct timeval timeout, *pto;
+ asl_msg_t *pq;
+
+ mp = _PATH_MODULE_LIB;
+ msec = DEFAULT_MARK_SEC;
+ pdays = DEFAULT_PRUNE_DAYS;
+ daemonize = 0;
+ __notify_78945668_info__ = -1;
+
+ for (i = 1; i < argc; i++)
+ {
+ if (streq(argv[i], "-d"))
+ {
+ debug = 1;
+ }
+ if (streq(argv[i], "-D"))
+ {
+ daemonize = 1;
+ }
+ if (streq(argv[i], "-u"))
+ {
+ activate_udp_in = 1;
+ }
+ else if (streq(argv[i], "-m"))
+ {
+ if ((i + 1) < argc) msec = 60 * atoi(argv[++i]);
+ }
+ else if (streq(argv[i], "-p"))
+ {
+ if ((i + 1) < argc) pdays = atoi(argv[++i]);
+ }
+ else if (streq(argv[i], "-l"))
+ {
+ if ((i + 1) < argc) mp = argv[++i];
+ }
+ else if (streq(argv[i], "-c"))
+ {
+ if ((i + 1) < argc)
+ {
+ i++;
+ if ((argv[i][0] >= '0') && (argv[i][0] <= '7') && (argv[i][1] == '\0')) asl_log_filter = ASL_FILTER_MASK_UPTO(atoi(argv[i]));
+ }
+ }
+ else if (streq(argv[i], "-asl_in"))
+ {
+ if ((i + 1) < argc) activate_asl_in = atoi(argv[++i]);
+ }
+ else if (streq(argv[i], "-asl_action"))
+ {
+ if ((i + 1) < argc) activate_asl_action = atoi(argv[++i]);
+ }
+ else if (streq(argv[i], "-klog_in"))
+ {
+ if ((i + 1) < argc) activate_klog_in = atoi(argv[++i]);
+ }
+ else if (streq(argv[i], "-bsd_in"))
+ {
+ if ((i + 1) < argc) activate_bsd_in = atoi(argv[++i]);
+ }
+ else if (streq(argv[i], "-bsd_out"))
+ {
+ if ((i + 1) < argc) activate_bsd_out = atoi(argv[++i]);
+ }
+ else if (streq(argv[i], "-udp_in"))
+ {
+ if ((i + 1) < argc) activate_udp_in = atoi(argv[++i]);
+ }
+ }
+
+ TAILQ_INIT(&Moduleq);
+ static_modules();
+ load_modules(mp);
+ aslevent_init();
+
+ if (debug == 0)
+ {
+ if (daemonize != 0)
+ {
+ if (fork() != 0) exit(0);
+
+ detach();
+ closeall();
+ }
+
+ writepid();
+ }
+
+ signal(SIGHUP, catch_sighup);
+ signal(SIGWINCH, catch_sigwinch);
+
+ FD_ZERO(&rd);
+ FD_ZERO(&wr);
+ FD_ZERO(&ex);
+
+ for (mod = Moduleq.tqh_first; mod != NULL; mod = mod->entries.tqe_next)
+ {
+ fd = mod->init();
+ if (fd < 0) continue;
+ }
+
+ lastmark = time(NULL);
+ notify_time = lastmark;
+ memset(&timeout, 0, sizeof(struct timeval));
+ pto = NULL;
+
+ ssec = msec;
+ if (ssec > 0)
+ {
+ timeout.tv_sec = ssec;
+ pto = &timeout;
+ }
+
+ ptime = 0;
+ if (pdays > 0) ptime = lastmark + PRUNE_AFTER_START_DELAY;
+
+ forever
+ {
+ if (pto != NULL) pto->tv_sec = ssec;
+ max = aslevent_fdsets(&rd, &wr, &ex);
+
+ status = select(max+1, &rd, &wr, &ex, pto);
+
+ if (reset != 0)
+ {
+ send_reset();
+ reset = 0;
+ }
+
+ if (pto != NULL)
+ {
+ tick = time(NULL);
+ delta = tick - lastmark;
+ if (delta >= msec)
+ {
+ lastmark = tick;
+ aslmark();
+ }
+ }
+
+ if (prune != 0)
+ {
+ asl_prune(NULL);
+ prune = 0;
+ ptime = 0;
+ }
+ else if (ptime != 0)
+ {
+ tick = time(NULL);
+ if (tick >= ptime)
+ {
+ pq = asl_new(ASL_TYPE_QUERY);
+ str = NULL;
+ asprintf(&str, "-%dd", pdays);
+ asl_set_query(pq, ASL_KEY_TIME, str, ASL_QUERY_OP_LESS);
+ if (str != NULL) free(str);
+
+ /* asl_prune frees the query */
+ asl_prune(pq);
+ ptime = 0;
+ }
+ }
+
+ if (__notify_78945668_info__ < 0)
+ {
+ tick = time(NULL);
+ if (tick >= notify_time)
+ {
+ if (notify_post("com.apple.system.syslogd") == NOTIFY_STATUS_OK) __notify_78945668_info__ = 0;
+ else notify_time = tick + NOTIFY_DELAY;
+ }
+ }
+
+ if (status != 0) aslevent_handleevent(rd, wr, ex, NULL);
+ }
+}
+
+int
+asldebug(const char *str, ...)
+{
+ va_list v;
+
+ if (debug == 0) return 0;
+
+ va_start(v, str);
+ return vprintf(str, v);
+}
--- /dev/null
+/*
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * "Portions Copyright (c) 2004 Apple Computer, Inc. All Rights
+ * Reserved. This file contains Original Code and/or Modifications of
+ * Original Code as defined in and that are subject to the Apple Public
+ * Source License Version 1.0 (the 'License'). You may not use this file
+ * except in compliance with the License. Please obtain a copy of the
+ * License at http://www.apple.com/publicsource and read it before using
+ * this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
+ * License for the specific language governing rights and limitations
+ * under the License."
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <sys/uio.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <netdb.h>
+#include "daemon.h"
+
+#define forever for(;;)
+
+#define MY_ID "udp_in"
+#define MAXLINE 4096
+
+#define MAXSOCK 16
+static int nsock = 0;
+static int ufd[MAXSOCK];
+
+static char uline[MAXLINE + 1];
+
+#define FMT_LEGACY 0
+#define FMT_ASL 1
+
+asl_msg_t *
+udp_convert(int fmt, char *s, int len, char *from)
+{
+ char *out;
+ asl_msg_t *m;
+
+ out = NULL;
+ m = NULL;
+
+ if (fmt == FMT_ASL)
+ {
+ m = asl_msg_from_string(s);
+ if (from != NULL) asl_set(m, ASL_KEY_HOST, from);
+ return m;
+ }
+
+ return asl_syslog_input_convert(uline, len, from, 0);
+}
+
+asl_msg_t *
+udp_in_acceptmsg(int fd)
+{
+ int format, status, x, fromlen;
+ size_t off;
+ ssize_t len;
+ struct sockaddr_storage from;
+ char fromstr[64], *r, *p;
+ struct sockaddr_in *s4;
+ struct sockaddr_in6 *s6;
+
+ fromlen = sizeof(struct sockaddr_storage);
+ memset(&from, 0, fromlen);
+
+ len = recvfrom(fd, uline, MAXLINE, 0, (struct sockaddr *)&from, &fromlen);
+ if (len <= 0) return NULL;
+
+ fromstr[0] = '\0';
+ r = NULL;
+
+ if (from.ss_family == AF_INET)
+ {
+ s4 = (struct sockaddr_in *)&from;
+ inet_ntop(from.ss_family, &(s4->sin_addr), fromstr, 64);
+ r = fromstr;
+ asldebug("%s: recvfrom %s len %d\n", MY_ID, fromstr, len);
+ }
+ else if (from.ss_family == AF_INET6)
+ {
+ s6 = (struct sockaddr_in6 *)&from;
+ inet_ntop(from.ss_family, &(s6->sin6_addr), fromstr, 64);
+ r = fromstr;
+ asldebug("%s: recvfrom %s len %d\n", MY_ID, fromstr, len);
+ }
+
+ uline[len] = '\0';
+
+ p = strrchr(uline, '\n');
+ if (p != NULL) *p = '\0';
+
+
+ /*
+ * Determine if the input is "old" syslog format or new ASL format.
+ * Old format lines should start with "<", but they can just be
+ * straight text. ASL input starts with a length (10 bytes)
+ * followed by a space and a '['.
+ */
+ format = FMT_LEGACY;
+ off = 0;
+
+ if ((uline[0] != '<') && (len > 11))
+ {
+ status = sscanf(uline, "%d ", &x);
+ if (status == 1)
+ {
+ if ((uline[10] == ' ') && (uline[11] == '['))
+ {
+ format = FMT_ASL;
+ off = 11;
+ }
+ }
+ }
+
+ return udp_convert(format, uline+off, len-off, r);
+}
+
+int
+udp_in_init(void)
+{
+ struct addrinfo hints, *gai, *ai;
+ int status, i;
+
+ asldebug("%s: init\n", MY_ID);
+ if (nsock > 0) return 0;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+
+ status = getaddrinfo(NULL, "syslog", &hints, &gai);
+ if (status != 0) return -1;
+
+ for (ai = gai; (ai != NULL) && (nsock < MAXSOCK); ai = ai->ai_next)
+ {
+ ufd[nsock] = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if (ufd[nsock] < 0)
+ {
+ asldebug("%s: socket: %s\n", MY_ID, strerror(errno));
+ continue;
+ }
+
+ if (bind(ufd[nsock], ai->ai_addr, ai->ai_addrlen) < 0)
+ {
+ asldebug("%s: bind: %s\n", MY_ID, strerror(errno));
+ close(ufd[nsock]);
+ continue;
+ }
+
+ nsock++;
+ }
+
+ freeaddrinfo(gai);
+
+ if (nsock == 0)
+ {
+ asldebug("%s: no input sockets\n", MY_ID);
+ return -1;
+ }
+
+ for (i = 0; i < nsock; i++) aslevent_addfd(ufd[i], udp_in_acceptmsg, NULL, NULL);
+ return 0;
+}
+
+int
+udp_in_reset(void)
+{
+ return 0;
+}
+
+int
+udp_in_close(void)
+{
+ int i;
+
+ if (nsock == 0) return 1;
+
+ for (i = 0; i < nsock; i++)
+ {
+ close(ufd[i]);
+ ufd[i] = -1;
+ }
+
+ nsock = 0;
+
+ return 0;
+}
--- /dev/null
+#
+# Generated by the NeXT Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = syslog
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+HFILES =
+
+CFILES = syslog.c
+
+OTHERSRCS = Makefile.preamble Makefile Makefile.postamble syslog.1
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /usr/bin
+WINDOWS_INSTALLDIR = /usr/bin
+PDO_UNIX_INSTALLDIR = /usr/bin
+LIBS =
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
--- /dev/null
+###############################################################################
+# NeXT Makefile.postamble Template
+# Copyright 1993, NeXT Computer, Inc.
+#
+# This Makefile is used for configuring the standard app makefiles associated
+# with ProjectBuilder.
+#
+# Use this template to set attributes for a project, sub-project, bundle, or
+# palette. Each node in the project's tree of sub-projects and bundles
+# should have it's own Makefile.preamble and Makefile.postamble. Additional
+# rules (e.g., after_install) that are defined by the developer should be
+# defined in this file.
+#
+###############################################################################
+#
+# Here are the variables exported by the common "app" makefiles that can be
+# used in any customizations you make to the template below:
+#
+# PRODUCT_ROOT - Name of top-level app-wrapper (e.g., Webster.app)
+# OFILE_DIR - Directory into which .o object files are generated.
+# (Note that this name is calculated based on the target
+# architectures specified in Project Builder).
+# DERIVED_SRC_DIR - Directory used for all other derived files
+# ALL_CFLAGS - All the flags passed to the cc(1) driver for compilations
+#
+# NAME - name of application, bundle, subproject, palette, etc.
+# LANGUAGE - langage in which the project is written (default "English")
+# ENGLISH - boolean flag set iff $(LANGUAGE) = "English"
+# JAPANESE - boolean flag set iff $(LANGUAGE) = "Japanese"
+# LOCAL_RESOURCES - localized resources (e.g. nib's, images) of project
+# GLOBAL_RESOURCES - non-localized resources of project
+# PROJECTVERSION - version of ProjectBuilder that output Makefile
+# APPICON - application icon file
+# DOCICONS - dock icon files
+# ICONSECTIONS - Specifies icon sections when linking executable
+#
+# CLASSES - Class implementation files in project.
+# HFILES - Header files in project.
+# MFILES - Other Objective-C source files in project.
+# CFILES - Other C source files in project.
+# PSWFILES - .psw files in the project
+# PSWMFILES - .pswm files in the project
+# SUBPROJECTS - Subprojects of this project
+# BUNDLES - Bundle subprojects of this project
+# OTHERSRCS - Other miscellaneous sources of this project
+# OTHERLINKED - Source files not matching a standard source extention
+#
+# LIBS - Libraries to link with when making app target
+# DEBUG_LIBS - Libraries to link with when making debug target
+# PROF_LIBS - Libraries to link with when making profile target
+# OTHERLINKEDOFILES - Other relocatable files to (always) link in.
+#
+# APP_MAKEFILE_DIR - Directory in which to find generic set of Makefiles
+# MAKEFILEDIR - Directory in which to find $(MAKEFILE)
+# MAKEFILE - Top level mechanism Makefile (e.g., app.make, bundle.make)
+# INSTALLDIR - Directory app will be installed into by 'install' target
+
+
+# Change defaults assumed by the standard app makefiles here. Edit the
+# following default values as appropriate. (Note that if no Makefile.postamble
+# exists, these values will have defaults set in common.make).
+
+# Add Makefile.preamble, Makefile.postamble, and Makefile.dependencies here if
+# you would like changes to them to invalidate previous builds. The project
+# depends on $(MAKEFILES) so that changes to Makefiles will trigger a re-build.
+#MAKEFILES = Makefile
+
+# Optimization flag passed to compiler:
+#OPTIMIZATION_CFLAG = -O
+
+# Flags always passed to compiler:
+#COMMON_CFLAGS = $(PROJECT_SPECIFIC_CFLAGS) -g -Wall
+
+# Flags passed to compiler in normal 'app' compiles:
+#NORMAL_CFLAGS = $(COMMON_CFLAGS) $(OPTIMIZATION_CFLAG)
+
+# Flags passed to compiler in 'debug' compiles:
+#DEBUG_CFLAGS = $(COMMON_CFLAGS) -DDEBUG
+
+# Flags passed to compiler in 'profile' compiles
+#PROFILE_CFLAGS = $(COMMON_CFLAGS) -pg $(OPTIMIZATION_CFLAG) -DPROFILE
+
+# Flags passed to yacc
+#YFLAGS = -d
+
+# Ownership and permissions of files installed by 'install' target
+#INSTALL_AS_USER = root # User to chown app to
+#INSTALL_AS_GROUP = wheel # Group to chgrp app to
+#INSTALL_PERMISSIONS = # If set, 'install' chmod's executable to this
+
+# Options to strip for bundles, apps with bundles, and apps without bundles,
+# respectively.
+#RELOCATABLE_STRIP_OPTS = -x -u
+#DYLD_APP_STRIP_OPTS = -A -n
+#APP_STRIP_OPTS =
+#TOOL_STRIP_OPTS =
+#LIBRARY_STRIP_OPTS = -x -S # Note: -S strips debugging symbols
+# (Note: APP_STRIP_OPTS and TOOL_STRIP_OPTS default to empty, but
+# developers doing their own dynamic loading should set this to
+# $(DYLD_APP_STRIP_OPTS)).
+STRIPFLAGS = -x
+
+
+#########################################################################
+# Put rules to extend the behavior of the standard Makefiles here. Typical
+# user-defined rules are before_install and after_install (please don't
+# redefine things like install or app, as they are owned by the top-level
+# Makefile API), which are rules that get invoked before and after the install
+# target runs. Such rules should be specified with the '::' syntax rather than
+# a single colon.
+
+# a rule with which to install the man page
+install-man-page:
+ install -d $(DSTROOT)/usr/share/man/man1
+ install -c -m 444 syslog.1 $(DSTROOT)/usr/share/man/man1/syslog.1
--- /dev/null
+###############################################################################
+# NeXT Makefile.preamble Template
+# Copyright 1993, NeXT Computer, Inc.
+#
+# This Makefile is used for configuring the standard app makefiles associated
+# with ProjectBuilder.
+#
+# Use this template to set attributes for a project, sub-project, bundle, or
+# palette. Each node in the project's tree of sub-projects and bundles
+# should have it's own Makefile.preamble and Makefile.postamble.
+#
+###############################################################################
+## Configure the flags passed to $(CC) here. These flags will also be
+## inherited by all nested sub-projects and bundles. Put your -I, -D, -U, and
+## -L flags here. To change the default flags that get passed to ${CC}
+## (e.g. change -O to -O2), see Makefile.postamble.
+
+# Flags passed to compiler (in addition to -g, -O, etc)
+OTHER_CFLAGS = -DINET6 -Dsocklen_t=int
+# Flags passed to ld (in addition to -ObjC, etc.)
+OTHER_LDFLAGS =
+
+BUNDLELDFLAGS = # use iff project is a bundle
+PALETTELDFLAGS = # use iff project is a palette
+
+## Specify which headers in this project should be published to the outside
+## world in a flat header directory given in PUBLIC_HEADER_DIR (which will be
+## prepended by DSTROOT, below. Any subset of these public headers can be
+## precompiled automatically after installation, with extra user-defined flags.
+PUBLIC_HEADER_DIR =
+PUBLIC_HEADERS =
+PUBLIC_PRECOMPILED_HEADERS =
+PUBLIC_PRECOMPILED_HEADERS_CFLAGS =
+
+## Configure what is linked in at each level here. Libraries are only used in
+## the final 'app' linking step. Final 'app' linking is only done via the
+## 'app', 'debug', and 'profile' targets when they are invoked for
+## the top-level app.
+
+# Additional libs to link apps against ('app' target)
+#OTHER_LIBS =
+# Additional libs to link apps against ('debug' target)
+OTHER_DEBUG_LIBS =
+# Additional libs to link apps against ('profile' target)
+OTHER_PROF_LIBS =
+
+# More 'app' libraries when $(JAPANESE) = "YES"
+OTHER_JAPANESE_LIBS =
+# More 'debug' libraries when $(JAPANESE) = "YES"
+OTHER_JAPANESE_DEBUG_LIBS =
+# More 'profile' libs when $(JAPANESE) = "YES"
+OTHER_JAPANESE_PROF_LIBS =
+
+# If this is a bundle, and you *know* the enclosing application will not
+# be linking with a library which you require in your bundle code, then
+# mention it here so that it gets linked into the bundle. Note that this
+# is wasteful but sometimes necessary.
+BUNDLE_LIBS =
+
+## Configure how things get built here. Additional dependencies, sourcefiles,
+## derived files, and build order should be specified here.
+
+# Other dependencies of this project
+OTHER_PRODUCT_DEPENDS =
+# Built *before* building subprojects/bundles
+OTHER_INITIAL_TARGETS =
+# Other source files maintained by .pre/postamble
+OTHER_SOURCEFILES =
+# Additional files to be removed by `make clean'
+OTHER_GARBAGE =
+# Precompiled headers to be built before any compilation occurs (e.g., draw.p)
+PRECOMPS =
+
+# Targets to be built before installation
+OTHER_INSTALL_DEPENDS =
+
+# A virtual root directory (other than /) to be prepended to the $(INSTALLDIR)
+# passed from ProjectBuilder.
+DSTROOT =
+
+# Set the following to "YES" if you want the old behavior of recursively
+# cleaning all nested subprojects during 'make clean'.
+CLEAN_ALL_SUBPROJECTS =
+
+## Add more obscure source files here to cause them to be automatically
+## processed by the appropriate tool. Note that these files should also be
+## added to "Supporting Files" in ProjectBuilder. The desired .o files that
+## result from these files should also be added to OTHER_OFILES above so they
+## will be linked in.
+
+# .msg files that should have msgwrap run on them
+MSGFILES =
+# .defs files that should have mig run on them
+DEFSFILES =
+# .mig files (no .defs files) that should have mig run on them
+MIGFILES =
+
+## Add additional Help directories here (add them to the project as "Other
+## Resources" in Project Builder) so that they will be compressed into .store
+## files and copied into the app wrapper. If the help directories themselves
+## need to also be in the app wrapper, then a cp command will need to be added
+## in an after_install target.
+OTHER_HELP_DIRS =
+
+# Don't add more rules here unless you want the first one to be the default
+# target for make! Put all your targets in Makefile.postamble.
+
+# To include a version string, project source must exist in a directory named
+# $(NAME).%d[.%d][.%d] and the following line must be uncommented.
+
+OTHER_GENERATED_OFILES = $(VERS_OFILE)
+
+# to allow installing man page after main install
+AFTER_INSTALL += install-man-page
+
+-include ../Makefile.include
--- /dev/null
+{
+ DOCICONFILES = ();
+ FILESTABLE = {
+ C_FILES = ();
+ H_FILES = ();
+ OTHER_LIBS = ();
+ OTHER_LINKED = (syslog.c);
+ OTHER_SOURCES = (Makefile.preamble, Makefile, Makefile.postamble, syslog.1);
+ PRECOMPILED_HEADERS = ();
+ PROJECT_HEADERS = ();
+ PUBLIC_HEADERS = ();
+ SUBPROJECTS = ();
+ };
+ GENERATEMAIN = YES;
+ LANGUAGE = English;
+ LOCALIZABLE_FILES = {};
+ NEXTSTEP_BUILDDIR = "";
+ NEXTSTEP_BUILDTOOL = /bin/make;
+ NEXTSTEP_COMPILEROPTIONS = "";
+ NEXTSTEP_INSTALLDIR = /usr/sbin;
+ NEXTSTEP_JAVA_COMPILER = /usr/bin/javac;
+ NEXTSTEP_LINKEROPTIONS = "";
+ NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc;
+ PDO_UNIX_BUILDDIR = "";
+ PDO_UNIX_BUILDTOOL = /bin/make;
+ PDO_UNIX_COMPILEROPTIONS = "";
+ PDO_UNIX_INSTALLDIR = /usr/sbin;
+ PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac";
+ PDO_UNIX_LINKEROPTIONS = "";
+ PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc";
+ PROJECTNAME = syslogd;
+ PROJECTTYPE = Tool;
+ PROJECTVERSION = 2.8;
+ WINDOWS_BUILDDIR = "";
+ WINDOWS_BUILDTOOL = /bin/make;
+ WINDOWS_COMPILEROPTIONS = "";
+ WINDOWS_INSTALLDIR = /usr/sbin;
+ WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe";
+ WINDOWS_LINKEROPTIONS = "";
+ WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc";
+}
--- /dev/null
+.\" Copyright (c) 2004 Apple Computer
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 4. Neither the name of Apple Computer nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"
+.Dd October 18, 2004
+.Dt SYSLOG 1
+.Os "Mac OS X"
+.Sh NAME
+.Nm syslog
+.Nd Apple System Log utility
+.Sh SYNOPSIS
+.Nm
+.Fl help
+.D1 ""
+.Nm
+.Fl s
+.Op Fl r Ar host
+.Op Fl l Ar level
+message...
+.D1 ""
+.Nm
+.Fl s
+.Op Fl r Ar host
+.Fl k
+key val
+.Op key val
+.Li ...
+.D1 ""
+.Nm
+.Op Fl w
+.Op Fl F Ar format
+.Ar expression
+.D1 ""
+.Nm
+.Fl p Ar expression
+.D1 ""
+.Nm
+.Fl c Ar process Op filter
+.Sh DESCRIPTION
+.Nm
+is a command-line utility for a variety of tasks relating to the Apple System Log facility.
+It provides mechanisms for sending and viewing log messages,
+pruning the contents of the system's log message data store,
+and for controlling the flow of log messages from client processes.
+.Pp
+When invoked with the
+.Fl help
+option,
+.Nm
+prints a usage message.
+.Ss SENDING MESSAGES
+The
+.Fl s
+option is used send log messages to the
+.Xr syslogd 8
+log message daemon,
+either locally or to a remote server if the
+.Fl r Ar host
+option in used.
+.Pp
+There are two main forms of the command.
+If the
+.Fl k
+option is used, then it must be followed by a list of keys and values.
+A structured message will be sent to the server with the keys and values given as arguments.
+If a key or a value has embedded white space, it must be enclosed in quotes.
+.Pp
+If the
+.Fl k
+option is not specified, then the rest of the command line is treated as the message text.
+The text may be preceded by
+.Fl l Ar level
+to set the log level (priority) of the message.
+Levels may be an integer value corresponding the the log levels specified in
+.Xr syslog 3
+or
+.Xr asl 3 ,
+or they may be a string.
+String values are case insensitive, and should be one of:
+.Pp
+.Bl -tag -compact
+.It Emergency
+(level 0)
+.It Alert
+(level 1)
+.It Critical
+(level 2)
+.It Error
+(level 3)
+.It Warning
+(level 4)
+.It Notice
+(level 5)
+.It Info
+(level 6)
+.It Debug
+(level 7)
+.El
+.Pp
+The string
+.Dq Panic
+is an alias for
+.Dq Emergency .
+.Nm
+only requires one or two leading characters for a level specification.
+A single character suffices in most cases.
+Use
+.Dq P
+or
+.Dq \&Em
+for Panic / Emergency, and
+.Dq \&Er
+or
+.Dq X
+for Error).
+.Ss READING MESSAGES
+The
+.Nm syslogd
+daemon receives messages from a variety of input sources.
+Received messages are processed by a set of output modules,
+each of which may act on messages in different ways.
+.Pp
+Two of the standard modules filter messages using criteria like the sender and the priority level of the message,
+and write copies of these messages to different output streams.
+One module does this filtering and filing task using the configuration specified in the
+.Xr syslog.conf 5
+file.
+The output files specified in that configuration may be examined by any file printing or editing utility,
+e.g.
+.Pp
+.Dl cat /var/log/system.log
+.Pp
+Another module saves messages in a data store, which may be searched using the
+.Nm
+command.
+.Pp
+If invoked with no arguments,
+.Nm
+simply prints all of the messages saved in the data store. If the
+.Fl w
+option is used,
+.Nm
+waits for new messages to be added to the data store.
+Messages already in the store are ignored.
+This usage is similar to watching a log file using, e.g.
+.Pp
+.Dl tail -f /var/log/system.log
+.Pp
+Messages are printed in a format similar to that used in the system.log file,
+except that the message priority level is printed between angle-brackets.
+.Pp
+The
+.Fl u
+option forces all time stamps to be printed using UTC.
+This overrides printing of time stamps using the local time zone.
+.Pp
+The output format may by changed by specifying the
+.Fl F Ar format
+option.
+The value of
+.Ar format
+may be one of the following:
+.Pp
+.Bl -tag -width "xxxx"
+.It bsd
+Format used by the
+.Nm syslogd
+daemon for system log files, e.g. /var/log/system.log.
+.It std
+Standard (default) format.
+Similar to
+.Dq bsd ,
+but includes the message priority level.
+.It raw
+Prints the complete message structure.
+Each key/value pair is enclosed in square brackets.
+Embedded closing brackets and white space are escaped.
+Time stamps are printed using UTC rather than being converted to the local time zone.
+.El
+.Pp
+The value of the
+.Ar format
+argument may also be a custom print format string.
+A custom format should in most cases be enclosed in single quotes to prevent the shell from substituting
+special characters and breaking at white space.
+.Pp
+Custom format strings may include variables of the form
+.Dq $Name
+(or
+.Dq $(Name)
+if the variable is not delimited by whitespace)
+which will be expanded to the associated with the named key.
+For example, the command:
+.Pp
+.Dl syslog -F '$Time $Host $(Sender)[$(PID)]: $Message'
+.Pp
+produces output similar to the
+.Dq bsd
+format.
+.Pp
+If no further command line options are specified,
+.Nm
+displays all messages,
+either all those saved in the data store,
+or all new messages if
+.Fl w
+is used.
+However, an expression may be specified using the
+.Fl k
+and
+.Fl o
+options.
+.Ss EXPRESSIONS
+Expressions specify matching criteria.
+They may be used when reading messages to filter for messages of interest.
+Expressions are also required when pruning the system log file with the
+.Fl p
+option.
+.Pp
+A simple expression is a list of one or more key/value pairs.
+A match is made when a message has the given value for the specified key.
+For example, to find all messages send by the portmap process:
+.Pp
+.Dl syslog -k Sender portmap
+.Pp
+The
+.Fl k
+option may be followed by one, two, or three arguments.
+A single argument causes a match to occur if a message has the specified key, regardless of value.
+If a pair of arguments is specified, a match occurs when a message has exactly the specified value for a given key.
+If three arguments are given, they are of the form
+.Fl k Ar key operation value .
+.Nm
+supports the following matching operators:
+.Pp
+.Bl -tag -width "xxx" -compact
+.It eq
+equal
+.It ne
+not equal
+.It gt
+greater than
+.It ge
+greater than or equal to
+.It lt
+less than
+.It le
+less than or equal to
+.El
+.Pp
+Additionally, the operator may be preceded by one or more of the following modifiers:
+.Pp
+.Bl -tag -width "xxx" -compact
+.It C
+case-fold
+.It R
+regular expression (see
+.Xr regex 3 )
+.It S
+substring
+.It A
+prefix
+.It Z
+suffix
+.It N
+numeric comparison
+.El
+.Pp
+An simple expression matches a message if all of the key-value operations match.
+Logically, the result is an AND of all of key-value operations.
+The
+.Fl o
+option separates simple expressions and provides an OR operation.
+If two or more simple expressions are given, separated by
+.Fl o
+options, then a match occurs is a message matches any of the simple expressions.
+For example, to find all messages which have either a
+.Dq Sender
+value of
+.Dq portmap
+or that have a numeric priority level of 4 or less:
+.Pp
+.Dl syslog -k Sender portmap -o -k Level Nle 4
+.Pp
+A special convention exists for matching time stamps.
+An unsigned integer value is regarded as the given number of seconds since
+0 hours, 0 minutes, 0 seconds, January 1, 1970, Coordinated Universal Time.
+An negative integer value is regarded as the given number of seconds before the current time.
+For example, to find all messages of priority level 3 (error) or less which were logged in the last 30 seconds:
+.Pp
+.Dl syslog -k Level Nle 3 -k Time ge -30
+.Pp
+a relative time value may be optionally followed by one of the characters
+.Dq s ,
+.Dq m ,
+.Dq h ,
+.Dq d ,
+or
+.Dq w
+to specify seconds, minutes, hours, days, or weeks respectively.
+Upper case may be used equivalently.
+A week is taken to be 7 complete days (i.e. 604800 seconds).
+.Ss PRUNING
+The Apple System Log facility saves received messages, subject to filtering criteria described in the
+FILTERING CONTROLS section below.
+Pruning is required to prevent unlimited growth of the data store.
+.Pp
+The
+.Nm syslogd
+daemon itself will prune the data store shortly after it starts up.
+See the
+.Xr syslogd 8
+manual for more details on startup pruning.
+During extended operation of
+.Nm syslogd ,
+pruning is accomplished by using the
+.Fl p
+option of
+.Nm syslog .
+The
+.Fl p
+option must be followed by an expression (see above).
+The contents of the data store are filtered using the given expression.
+Messages that match the expression are deleted.
+.Pp
+A daily pruning operation is performed by the
+.Nm cron
+utility.
+The command is specified in the /etc/periodic/daily/500.daily file.
+.Ss FILTERING CONTROLS
+Clients of the Apple System Log facility using either the
+.Xr asl 3
+or
+.Xr syslog 3
+interfaces may specify a log filter mask.
+The mask specifies which messages should be sent to the
+.Nm syslogd
+daemon by specifying a yes/no setting for each priority level.
+Many clients set a filter mask to avoid sending relatively unimportant messages.
+Debug or Info priority level messages are generally only useful for debugging operations.
+By setting a filter mask, a process can improve performance by avoiding spending
+time sending messages that are in most cases unnecessary.
+.Pp
+The
+.Fl c
+option may be used to control filtering.
+In addition to the internal filter value that processes may set as described above,
+the system maintains a global
+.Dq master
+filter.
+This filter is normally
+.Dq off ,
+meaning that it has no effect.
+If a value is set for the master filter, it overrides the local filter for all processes.
+Root user access is required to set the master filter value.
+.Pp
+The current setting of the master filter mask may be inspected using:
+.Pp
+.Dl syslog -c 0
+.Pp
+The value of the master filter mask my be set by providing a second argument following
+.Fl c Ar 0 .
+The value may a set of characters from the set
+.Dq pacewnid .
+These correspond to the priority levels Emergency (Panic), Alert, Critical, Error, Warning, Notice, Info, and Debug.
+The character
+.Dq x
+may be used for Error, as it is used for sending messages.
+The master filter may be unset with:
+.Pp
+.Dl syslog -c 0 off
+.Pp
+Since it is common to use the filter as a
+.Dq cutoff
+mechanism, for example to cut off messages with Debug and Info priority,
+a single character from the list above may be specified, preceded by a minus sign.
+In this case,
+.Nm
+uses a filter mask starting at level 0 (Emergency)
+.Dq up to
+the given level.
+For example, to set the master filter level to cause all processes to log messages from Emergency up to Debug:
+.Pp
+.Dl syslog -c 0 -d
+.Pp
+While the master filter level may be set to control the messages produced by all processes,
+another filter mask may be specified for an individual process.
+If a per-process filter mask is set, it overrides both the local filter mask and the master filter mask.
+The current setting for a per-process filter mask may be inspected using
+.Fl c Ar process ,
+where
+.Ar process
+is either a PID or the name of a process.
+If a name is used, it must uniquely identify a process.
+To set a per-process filter mask, an second argument may be supplied following
+.Fl c Ar process
+as described above for the master filter mask.
+Root access is required to set the per-process filter mask for system (UID 0) processes.
+.Pp
+The filtering described above takes place in the client library to determine which messages are sent to the
+.Nm syslogd
+daemon.
+The daemon also contains a filter which determines which messages are saved in the data store.
+Note that this additionally determines which messages are seen when reading messages using the
+.Nm
+utility.
+.Pp
+The default data store filter mask saves messages with priority levels from Emergency to Notice (level 0 to 5).
+The level may be inspected using:
+.Pp
+.Dl syslog -c syslogd
+.Pp
+To set the data store filter mask, an second argument may be supplied following
+.Fl c Li syslog
+as described above.
+For example, to save messages with priority level Error or less in the data store:
+.Pp
+.Dl syslog -c syslog -e
+.Sh SEE ALSO
+.Xr syslogd 8 ,
+.Xr logger 1 ,
+.Xr asl 3 ,
+.Xr syslog 3 ,
+.Sh HISTORY
+The
+.Nm
+utility appeared in Mac OS X 10.4.
--- /dev/null
+/*
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * "Portions Copyright (c) 2004 Apple Computer, Inc. All Rights
+ * Reserved. This file contains Original Code and/or Modifications of
+ * Original Code as defined in and that are subject to the Apple Public
+ * Source License Version 1.0 (the 'License'). You may not use this file
+ * except in compliance with the License. Please obtain a copy of the
+ * License at http://www.apple.com/publicsource and read it before using
+ * this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
+ * License for the specific language governing rights and limitations
+ * under the License."
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <time.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <notify.h>
+#include <asl.h>
+#include <asl_private.h>
+
+#define MOD_CASE_FOLD 'C'
+#define MOD_REGEX 'R'
+#define MOD_SUBSTRING 'S'
+#define MOD_PREFIX 'A'
+#define MOD_SUFFIX 'Z'
+#define MOD_NUMERIC 'N'
+
+#define OP_EQ "eq"
+#define OP_NE "ne"
+#define OP_GT "gt"
+#define OP_GE "ge"
+#define OP_LT "lt"
+#define OP_LE "le"
+
+#define ASL_QUERY_OP_NOT 0x1000
+
+#define SEARCH_EOF -1
+#define SEARCH_NULL 0
+#define SEARCH_MATCH 1
+
+#define PROC_NOT_FOUND -1
+#define PROC_NOT_UNIQUE -2
+
+#define RC_MASTER -1
+#define RC_SYSLOGD -2
+
+#define CHUNK 64
+#define forever for(;;)
+
+#define SEND_FORMAT_LEGACY 0
+#define SEND_FORMAT_ASL 1
+
+#define PRINT_LOCALTIME 0x00000001
+#define PRINT_LEGACY_FMT 0x00000002
+#define PRINT_STD_FMT 0x00000004
+
+#define ASL_FILTER_MASK_PACEWNID 0xff
+#define ASL_FILTER_MASK_PACEWNI 0x7f
+#define ASL_FILTER_MASK_PACEWN 0x3f
+#define ASL_FILTER_MASK_PACEW 0x1f
+#define ASL_FILTER_MASK_PACE 0x0f
+#define ASL_FILTER_MASK_PAC 0x07
+
+
+/* BEGIN PRIVATE API */
+#define _PATH_ASL_PRUNE "/var/run/asl_prune"
+#define _PATH_SYSLOGD_PID "/var/run/syslog.pid"
+
+/* notify SPI */
+uint32_t notify_get_state(int token, int *state);
+uint32_t notify_set_state(int token, int state);
+uint32_t notify_register_plain(const char *name, int *out_token);
+
+extern char *asl_msg_to_string(aslmsg msg, uint32_t *len);
+extern asl_msg_t *asl_msg_from_string(const char *buf);
+extern int asl_msg_cmp(asl_msg_t *a, asl_msg_t *b);
+extern time_t asl_parse_time(const char *in);
+/* END PRIVATE API */
+
+static const char *myname = "syslog";
+
+void
+usage()
+{
+ fprintf(stderr, "usage:\n");
+ fprintf(stderr, "%s -s [-r host] [-l level] message...\n", myname);
+ fprintf(stderr, " send a message\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "%s -s [-r host] -k key val [key val]...\n", myname);
+ fprintf(stderr, " send a message with the given keys and values\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "%s -c process [filter]\n", myname);
+ fprintf(stderr, " get (set if filter is specified) syslog filter for process (pid or name)\n");
+ fprintf(stderr, " level may be any combination of the characters \"p a c e w n i d\"\n");
+ fprintf(stderr, " p = Emergency (\"Panic\")\n");
+ fprintf(stderr, " a = Alert\n");
+ fprintf(stderr, " c = Critical\n");
+ fprintf(stderr, " e = Error\n");
+ fprintf(stderr, " w = Warning\n");
+ fprintf(stderr, " n = Notice\n");
+ fprintf(stderr, " i = Info\n");
+ fprintf(stderr, " d = Debug\n");
+ fprintf(stderr, " a minus sign preceeding a single letter means \"up to\" that level\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "%s -p [-k key [[op] val]]... [-o -k key [[op] val]] ...]...\n", myname);
+ fprintf(stderr, " -p prune datastore according to input expression (see below)\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "%s [-w] [-F format] [-u] [-k key [[op] val]]... [-o -k key [[op] val]] ...]...\n", myname);
+ fprintf(stderr, " -w watch file (^C to quit)\n");
+ fprintf(stderr, " -F output format may be \"std\", \"raw\", or \"bsd\"\n");
+ fprintf(stderr, " format may also be a string containing variables of the form\n");
+ fprintf(stderr, " $Key or $(Key) - use the latter for non-whitespace delimited variables\n");
+ fprintf(stderr, " -u force printing of all timestamps using UTC\n");
+ fprintf(stderr, " -k key/value match\n");
+ fprintf(stderr, " if no operator or value is given, checks for the existance of the key\n");
+ fprintf(stderr, " if no operator is given, default is \"%s\"\n", OP_EQ);
+ fprintf(stderr, " -o begins a new query\n");
+ fprintf(stderr, " queries are \'OR\'ed together\n");
+ fprintf(stderr, "operators are zero or more modifiers followed by a comparison\n");
+ fprintf(stderr, " %s equal\n", OP_EQ);
+ fprintf(stderr, " %s not equal\n", OP_NE);
+ fprintf(stderr, " %s greater than\n", OP_GT);
+ fprintf(stderr, " %s greater or equal\n", OP_GE);
+ fprintf(stderr, " %s less than\n", OP_LT);
+ fprintf(stderr, " %s less or equal\n", OP_LE);
+ fprintf(stderr, "optional modifiers for operators\n");
+ fprintf(stderr, " %c case-fold\n", MOD_CASE_FOLD);
+ fprintf(stderr, " %c regular expression\n", MOD_REGEX);
+ fprintf(stderr, " %c substring\n", MOD_SUBSTRING);
+ fprintf(stderr, " %c prefix\n", MOD_PREFIX);
+ fprintf(stderr, " %c suffix\n", MOD_SUFFIX);
+ fprintf(stderr, " %c numeric comparison\n", MOD_NUMERIC);
+}
+
+const char *
+notify_status_string(int status)
+{
+ if (status == NOTIFY_STATUS_OK) return "OK";
+ if (status == NOTIFY_STATUS_INVALID_NAME) return "Process not registered";
+ if (status == NOTIFY_STATUS_NOT_AUTHORIZED) return "Not authorized";
+ return "Operation failed";
+}
+
+const char *
+asl_level_string(int level)
+{
+ if (level == ASL_LEVEL_EMERG) return ASL_STRING_EMERG;
+ if (level == ASL_LEVEL_ALERT) return ASL_STRING_ALERT;
+ if (level == ASL_LEVEL_CRIT) return ASL_STRING_CRIT;
+ if (level == ASL_LEVEL_ERR) return ASL_STRING_ERR;
+ if (level == ASL_LEVEL_WARNING) return ASL_STRING_WARNING;
+ if (level == ASL_LEVEL_NOTICE) return ASL_STRING_NOTICE;
+ if (level == ASL_LEVEL_INFO) return ASL_STRING_INFO;
+ if (level == ASL_LEVEL_DEBUG) return ASL_STRING_DEBUG;
+ return "Unknown";
+}
+
+int
+procinfo(char *pname, int *pid, int *uid)
+{
+ int mib[4];
+ int i, status, nprocs;
+ size_t miblen, size;
+ struct kinfo_proc *procs, *newprocs;
+
+ size = 0;
+ procs = NULL;
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_ALL;
+ mib[3] = 0;
+ miblen = 3;
+
+ status = sysctl(mib, miblen, NULL, &size, NULL, 0);
+ do
+ {
+ size += size / 10;
+ newprocs = realloc(procs, size);
+ if (newprocs == 0)
+ {
+ if (procs != NULL) free(procs);
+ return PROC_NOT_FOUND;
+ }
+
+ procs = newprocs;
+ status = sysctl(mib, miblen, procs, &size, NULL, 0);
+ } while ((status == -1) && (errno == ENOMEM));
+
+ if (status == -1)
+ {
+ if (procs != NULL) free(procs);
+ return PROC_NOT_FOUND;
+ }
+
+ if (size % sizeof(struct kinfo_proc) != 0)
+ {
+ if (procs != NULL) free(procs);
+ return PROC_NOT_FOUND;
+ }
+
+ if (procs == NULL) return PROC_NOT_FOUND;
+
+ nprocs = size / sizeof(struct kinfo_proc);
+
+ if (pname == NULL)
+ {
+ /* Search for a pid */
+ for (i = 0; i < nprocs; i++)
+ {
+ if (*pid == procs[i].kp_proc.p_pid)
+ {
+ *uid = procs[i].kp_eproc.e_ucred.cr_uid;
+ return 0;
+ }
+ }
+
+ return PROC_NOT_FOUND;
+ }
+
+ *pid = PROC_NOT_FOUND;
+
+ for (i = 0; i < nprocs; i++)
+ {
+ if (!strcmp(procs[i].kp_proc.p_comm, pname))
+ {
+ if (*pid != PROC_NOT_FOUND)
+ {
+ free(procs);
+ return PROC_NOT_UNIQUE;
+ }
+
+ *pid = procs[i].kp_proc.p_pid;
+ *uid = procs[i].kp_eproc.e_ucred.cr_uid;
+ }
+ }
+
+ free(procs);
+ if (*pid == PROC_NOT_FOUND) return PROC_NOT_FOUND;
+
+ return 0;
+}
+
+int
+rcontrol_get_string(const char *prefix, int pid, int *val)
+{
+ int t, x, status;
+ char *name;
+
+ status = NOTIFY_STATUS_OK;
+
+ if (pid == RC_SYSLOGD)
+ {
+ status = notify_register_plain(NOTIFY_SYSTEM_ASL_FILTER, &t);
+ }
+ else if (pid == RC_MASTER)
+ {
+ status = notify_register_plain(NOTIFY_SYSTEM_MASTER, &t);
+ }
+ else
+ {
+ name = NULL;
+ asprintf(&name, "%s.%d", prefix, pid);
+ if (name == NULL) return NOTIFY_STATUS_FAILED;
+
+ status = notify_register_plain(name, &t);
+ free(name);
+ }
+
+ if (status != NOTIFY_STATUS_OK) return status;
+
+ x = 0;
+ status = notify_get_state(t, &x);
+ notify_cancel(t);
+
+ *val = x;
+
+ return status;
+}
+
+int
+rcontrol_set_string(const char *prefix, int pid, int filter)
+{
+ int t, status;
+ char *name;
+
+ status = NOTIFY_STATUS_OK;
+
+ if (pid == RC_SYSLOGD)
+ {
+ status = notify_register_plain(NOTIFY_SYSTEM_ASL_FILTER, &t);
+ }
+ else if (pid == RC_MASTER)
+ {
+ status = notify_register_plain(NOTIFY_SYSTEM_MASTER, &t);
+ }
+ else
+ {
+ name = NULL;
+ asprintf(&name, "%s.%d", prefix, pid);
+ if (name == NULL) return NOTIFY_STATUS_FAILED;
+
+ status = notify_register_plain(name, &t);
+ free(name);
+ }
+
+ if (status != NOTIFY_STATUS_OK) return status;
+ status = notify_set_state(t, filter);
+ if ((pid == RC_SYSLOGD) && (status == NOTIFY_STATUS_OK)) status = notify_post(NOTIFY_SYSTEM_ASL_FILTER);
+ notify_cancel(t);
+ return status;
+}
+
+int
+asl_string_to_filter(char *s)
+{
+ int f, i;
+
+ if (s == NULL) return 0;
+ if (s[0] == '\0') return 0;
+
+ if ((s[0] >= '0') && (s[0] <= '9')) return ASL_FILTER_MASK(atoi(s));
+
+ if (s[0] == '-')
+ {
+ if ((s[1] == 'P') || (s[1] == 'p')) i = ASL_LEVEL_EMERG;
+ else if ((s[1] == 'A') || (s[1] == 'a')) i = ASL_LEVEL_ALERT;
+ else if ((s[1] == 'C') || (s[1] == 'c')) i = ASL_LEVEL_CRIT;
+ else if ((s[1] == 'E') || (s[1] == 'e')) i = ASL_LEVEL_ERR;
+ else if ((s[1] == 'X') || (s[1] == 'x')) i = ASL_LEVEL_ERR;
+ else if ((s[1] == 'W') || (s[1] == 'w')) i = ASL_LEVEL_WARNING;
+ else if ((s[1] == 'N') || (s[1] == 'n')) i = ASL_LEVEL_NOTICE;
+ else if ((s[1] == 'I') || (s[1] == 'i')) i = ASL_LEVEL_INFO;
+ else if ((s[1] == 'D') || (s[1] == 'd')) i = ASL_LEVEL_DEBUG;
+ else i = atoi(s + 1);
+ f = ASL_FILTER_MASK_UPTO(i);
+ return f;
+ }
+
+ f = 0;
+ for (i = 0; s[i] != '\0'; i++)
+ {
+ if ((s[i] == 'P') || (s[i] == 'p')) f |= ASL_FILTER_MASK_EMERG;
+ else if ((s[i] == 'A') || (s[i] == 'a')) f |= ASL_FILTER_MASK_ALERT;
+ else if ((s[i] == 'C') || (s[i] == 'c')) f |= ASL_FILTER_MASK_CRIT;
+ else if ((s[i] == 'E') || (s[i] == 'e')) f |= ASL_FILTER_MASK_ERR;
+ else if ((s[i] == 'X') || (s[i] == 'x')) f |= ASL_FILTER_MASK_ERR;
+ else if ((s[i] == 'W') || (s[i] == 'w')) f |= ASL_FILTER_MASK_WARNING;
+ else if ((s[i] == 'N') || (s[i] == 'n')) f |= ASL_FILTER_MASK_NOTICE;
+ else if ((s[i] == 'I') || (s[i] == 'i')) f |= ASL_FILTER_MASK_INFO;
+ else if ((s[i] == 'D') || (s[i] == 'd')) f |= ASL_FILTER_MASK_DEBUG;
+ }
+
+ return f;
+}
+
+char *
+asl_filter_string(int f)
+{
+ static char str[1024];
+ int i;
+
+ memset(str, 0, sizeof(str));
+ i = 0;
+
+ if ((f == ASL_FILTER_MASK_PACEWNID) != 0)
+ {
+ strcat(str, "Emergency - Debug");
+ return str;
+ }
+
+ if ((f == ASL_FILTER_MASK_PACEWNI) != 0)
+ {
+ strcat(str, "Emergency - Info");
+ return str;
+ }
+
+ if ((f == ASL_FILTER_MASK_PACEWN) != 0)
+ {
+ strcat(str, "Emergency - Notice");
+ return str;
+ }
+
+ if ((f == ASL_FILTER_MASK_PACEW) != 0)
+ {
+ strcat(str, "Emergency - Warning");
+ return str;
+ }
+
+ if ((f == ASL_FILTER_MASK_PACE) != 0)
+ {
+ strcat(str, "Emergency - Error");
+ return str;
+ }
+
+ if ((f == ASL_FILTER_MASK_PAC) != 0)
+ {
+ strcat(str, "Emergency - Critical");
+ return str;
+ }
+
+ if ((f & ASL_FILTER_MASK_EMERG) != 0)
+ {
+ strcat(str, "Emergency");
+ i++;
+ }
+
+ if ((f & ASL_FILTER_MASK_ALERT) != 0)
+ {
+ if (i > 0) strcat(str, ", ");
+ strcat(str, "Alert");
+ i++;
+ }
+
+ if ((f & ASL_FILTER_MASK_CRIT) != 0)
+ {
+ if (i > 0) strcat(str, ", ");
+ strcat(str, "Critical");
+ i++;
+ }
+
+ if ((f & ASL_FILTER_MASK_ERR) != 0)
+ {
+ if (i > 0) strcat(str, ", ");
+ strcat(str, "Error");
+ i++;
+ }
+
+ if ((f & ASL_FILTER_MASK_WARNING) != 0)
+ {
+ if (i > 0) strcat(str, ", ");
+ strcat(str, "Warning");
+ i++;
+ }
+
+ if ((f & ASL_FILTER_MASK_NOTICE) != 0)
+ {
+ if (i > 0) strcat(str, ", ");
+ strcat(str, "Notice");
+ i++;
+ }
+
+ if ((f & ASL_FILTER_MASK_INFO) != 0)
+ {
+ if (i > 0) strcat(str, ", ");
+ strcat(str, "Info");
+ i++;
+ }
+
+ if ((f & ASL_FILTER_MASK_DEBUG) != 0)
+ {
+ if (i > 0) strcat(str, ", ");
+ strcat(str, "Debug");
+ i++;
+ }
+
+ if (i == 0) sprintf(str, "Off");
+
+ return str;
+}
+
+int
+rcontrol_get(const char *prefix, int pid)
+{
+ int filter, status;
+ const char *name;
+
+ filter = 0;
+
+ if (pid < 0)
+ {
+ name = "Master";
+ if (pid == RC_SYSLOGD) name = "ASL Data Store";
+
+ status = rcontrol_get_string(NULL, pid, &filter);
+ if (status == NOTIFY_STATUS_OK)
+ {
+ printf("%s filter mask: %s\n", name, asl_filter_string(filter));
+ return 0;
+ }
+
+ printf("Unable to determine %s filter mask\n", name);
+ return -1;
+ }
+
+ status = rcontrol_get_string(prefix, pid, &filter);
+ if (status == NOTIFY_STATUS_OK)
+ {
+ printf("Process %d syslog filter mask: %s\n", pid, asl_filter_string(filter));
+ return 0;
+ }
+
+ printf("Unable to determine syslog filter mask for pid %d\n", pid);
+ return -1;
+}
+
+int
+rcontrol_set(const char *prefix, int pid, int filter)
+{
+ int status;
+ const char *name;
+
+ if (pid < 0)
+ {
+ name = "Master";
+ if (pid == RC_SYSLOGD) name = "ASL Data Store";
+ status = rcontrol_set_string(NULL, pid, filter);
+
+ if (status == NOTIFY_STATUS_OK)
+ {
+ printf("Set %s syslog filter mask: %s\n", name, asl_filter_string(filter));
+ return 0;
+ }
+
+ printf("Unable to set %s syslog filter mask: %s\n", name, notify_status_string(status));
+ return -1;
+ }
+
+ status = rcontrol_set_string(prefix, pid, filter);
+ if (status == NOTIFY_STATUS_OK)
+ {
+ printf("Set process %d syslog filter mask set: %s\n", pid, asl_filter_string(filter));
+ return 0;
+ }
+
+ printf("Unable to set syslog filter mask for pid %d: %s\n", pid, notify_status_string(status));
+ return -1;
+}
+
+int
+rsend(aslmsg msg, char *rhost)
+{
+ char *str, *out;
+ uint32_t len, level;
+ char *timestr;
+ const char *val;
+ time_t tick;
+ struct tm gtime;
+ int s;
+ struct sockaddr_in dst;
+ struct hostent *h;
+ char myname[MAXHOSTNAMELEN + 1];
+
+ if (msg == NULL) return 0;
+
+ h = gethostbyname(rhost);
+ if (h == NULL) return -1;
+
+ s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (s <= 0) return -1;
+
+ memset(&dst, 0, sizeof(struct sockaddr_in));
+ memcpy(&(dst.sin_addr.s_addr), h->h_addr_list[0], 4);
+ dst.sin_family = AF_INET;
+ dst.sin_port = 514;
+ dst.sin_len = sizeof(struct sockaddr_in);
+
+ level = ASL_LEVEL_DEBUG;
+
+ val = asl_get(msg, ASL_KEY_LEVEL);
+ if (val != NULL) level = atoi(val);
+
+ memset(>ime, 0, sizeof(struct tm));
+ timestr = NULL;
+
+ tick = time(NULL);
+ gmtime_r(&tick, >ime);
+
+ /* Canonical form: YYYY.MM.DD hh:mm:ss UTC */
+ asprintf(×tr, "%d.%02d.%02d %02d:%02d:%02d UTC", gtime.tm_year + 1900, gtime.tm_mon + 1, gtime.tm_mday, gtime.tm_hour, gtime.tm_min, gtime.tm_sec);
+
+ if (timestr != NULL)
+ {
+ asl_set(msg, ASL_KEY_TIME, timestr);
+ free(timestr);
+ }
+
+ if (gethostname(myname, MAXHOSTNAMELEN) == 0) asl_set(msg, ASL_KEY_HOST, myname);
+
+ len = 0;
+ str = asl_msg_to_string(msg, &len);
+ if (str == NULL) return -1;
+
+ asprintf(&out, "%10u %s\n", len+1, str);
+ free(str);
+ if (out == NULL) return -1;
+
+ sendto(s, out, len+12, 0, (const struct sockaddr *)&dst, sizeof(struct sockaddr_in));
+
+ free(out);
+ close(s);
+ return 0;
+}
+
+int
+rlegacy(char *msg, int level, char *rhost)
+{
+ char *out;
+ uint32_t len;
+ time_t tick;
+ char *ltime;
+ int s;
+ struct sockaddr_in dst;
+ struct hostent *h;
+ char myname[MAXHOSTNAMELEN + 1];
+
+ if (msg == NULL) return 0;
+
+ h = gethostbyname(rhost);
+ if (h == NULL) return -1;
+
+ s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (s <= 0) return -1;
+
+ memset(&dst, 0, sizeof(struct sockaddr_in));
+ memcpy(&(dst.sin_addr.s_addr), h->h_addr_list[0], 4);
+ dst.sin_family = AF_INET;
+ dst.sin_port = 514;
+ dst.sin_len = sizeof(struct sockaddr_in);
+
+ tick = time(NULL);
+ ltime = ctime(&tick);
+ ltime[19] = '\0';
+
+ gethostname(myname, MAXHOSTNAMELEN);
+
+ asprintf(&out, "<%d>%s %s syslog[%d]: %s", level, ltime+4, myname, getpid(), msg);
+ len = strlen(out);
+ sendto(s, out, len, 0, (const struct sockaddr *)&dst, sizeof(struct sockaddr_in));
+
+ free(out);
+ close(s);
+ return 0;
+}
+
+static int
+_isanumber(char *s)
+{
+ int i;
+
+ if (s == NULL) return 0;
+
+ i = 0;
+ if ((s[0] == '-') || (s[0] == '+')) i = 1;
+
+ if (s[i] == '\0') return 0;
+
+ for (; s[i] != '\0'; i++)
+ {
+ if (!isdigit(s[i])) return 0;
+ }
+
+ return 1;
+}
+
+int
+asl_string_to_level(const char *s)
+{
+ if (s == NULL) return -1;
+
+ if ((s[0] >= '0') && (s[0] <= '7') && (s[1] == '\0')) return atoi(s);
+
+ if (!strncasecmp(s, "em", 2)) return ASL_LEVEL_EMERG;
+ else if (!strncasecmp(s, "p", 1)) return ASL_LEVEL_EMERG;
+ else if (!strncasecmp(s, "a", 1)) return ASL_LEVEL_ALERT;
+ else if (!strncasecmp(s, "c", 1)) return ASL_LEVEL_CRIT;
+ else if (!strncasecmp(s, "er", 2)) return ASL_LEVEL_ERR;
+ else if (!strncasecmp(s, "x", 1)) return ASL_LEVEL_ERR;
+ else if (!strncasecmp(s, "w", 1)) return ASL_LEVEL_WARNING;
+ else if (!strncasecmp(s, "n", 1)) return ASL_LEVEL_NOTICE;
+ else if (!strncasecmp(s, "i", 1)) return ASL_LEVEL_INFO;
+ else if (!strncasecmp(s, "d", 1)) return ASL_LEVEL_DEBUG;
+
+ return -1;
+}
+
+int
+syslog_remote_control(int argc, char *argv[])
+{
+ int pid, uid, status, mask;
+ const char *prefix;
+
+ if ((argc < 3) || (argc > 4))
+ {
+ fprintf(stderr, "usage:\n");
+ fprintf(stderr, "%s -c process [mask]\n", myname);
+ fprintf(stderr, " get (set if mask is specified) syslog filter mask for process (pid or name)\n");
+ fprintf(stderr, " process may be pid or process name\n");
+ fprintf(stderr, " use \"-c 0\" to get master syslog filter mask\n");
+ fprintf(stderr, " use \"-c 0 off\" to disable master syslog filter mask\n");
+ fprintf(stderr, "\n");
+ return -1;
+ }
+
+ pid = RC_MASTER;
+ uid = -2;
+
+ status = PROC_NOT_FOUND;
+
+ if ((!strcmp(argv[2], "syslogd")) || (!strcmp(argv[2], "syslog")))
+ {
+ pid = RC_SYSLOGD;
+ uid = 0;
+ status = 0;
+ }
+ else if (_isanumber(argv[2]) != 0)
+ {
+ pid = atoi(argv[2]);
+ status = procinfo(NULL, &pid, &uid);
+ }
+ else
+ {
+ status = procinfo(argv[2], &pid, &uid);
+ }
+
+ if (status == PROC_NOT_FOUND)
+ {
+ fprintf(stderr, "%s: process not found\n", argv[2]);
+ return -1;
+ }
+
+ if (status == PROC_NOT_UNIQUE)
+ {
+ fprintf(stderr, "%s: multiple processes found\n", argv[2]);
+ fprintf(stderr, "use pid to identify a process uniquely\n");
+ return -1;
+ }
+
+ if (pid == 0) pid = RC_MASTER;
+
+ prefix = NOTIFY_PREFIX_USER;
+ if (uid == 0) prefix = NOTIFY_PREFIX_SYSTEM;
+
+ if (argc == 4)
+ {
+ if ((pid == RC_MASTER) && (!strcasecmp(argv[3], "off"))) mask = 0;
+ else if ((pid == RC_SYSLOGD) && (!strcasecmp(argv[3], "off"))) mask = 0;
+ else
+ {
+ mask = asl_string_to_filter(argv[3]);
+ if (mask < 0)
+ {
+ printf("unknown syslog mask: %s\n", argv[3]);
+ return -1;
+ }
+ }
+
+ rcontrol_set(prefix, pid, mask);
+ }
+ else
+ {
+ rcontrol_get(prefix, pid);
+ }
+
+ return 0;
+}
+
+int
+syslog_send(int argc, char *argv[])
+{
+ int i, start, kv, len, rfmt, rlevel;
+ aslclient asl;
+ aslmsg m;
+ char tmp[64], *str, *rhost;
+
+ kv = 0;
+ rhost = NULL;
+ rfmt = SEND_FORMAT_ASL;
+ start = 1;
+ rlevel = 7;
+
+ for (i = 1; i < argc; i++)
+ {
+ if (!strcmp(argv[i], "-s")) start = i+1;
+ else if (!strcmp(argv[i], "-k")) kv = 1;
+ else if (!strcmp(argv[i], "-r"))
+ {
+ rhost = argv[++i];
+ start = i+1;
+ rfmt = SEND_FORMAT_LEGACY;
+ }
+ else if (!strcmp(argv[i], "-R"))
+ {
+ rhost = argv[++i];
+ start = i+1;
+ }
+ else if (!strcmp(argv[i], "-l"))
+ {
+ rlevel = asl_string_to_level(argv[++i]);
+ if (rlevel < 0)
+ {
+ fprintf(stderr, "Unknown level: %s\n", argv[i]);
+ return(-1);
+ }
+ start = i+1;
+ }
+ }
+
+ asl = asl_open(myname, "syslog", 0);
+ asl_set_filter(asl, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG));
+
+ m = asl_new(ASL_TYPE_MSG);
+ asl_set(m, ASL_KEY_SENDER, myname);
+
+ sprintf(tmp, "%d", rlevel);
+ asl_set(m, ASL_KEY_LEVEL, tmp);
+
+ str = NULL;
+
+ if (kv == 0)
+ {
+ len = 0;
+ for (i = start; i < argc; i++) len += (strlen(argv[i]) + 1);
+ str = calloc(len + 1, 1);
+ for (i = start; i < argc; i++)
+ {
+ strcat(str, argv[i]);
+ if ((i+1) < argc) strcat(str, " ");
+ }
+ asl_set(m, ASL_KEY_MSG, str);
+ }
+ else
+ {
+ for (i = start + 1; i < argc; i += 2) asl_set(m, argv[i], argv[i + 1]);
+ }
+
+ if (rhost == NULL)
+ {
+ asl_send(asl, m);
+ }
+ else if (rfmt == SEND_FORMAT_ASL)
+ {
+ rsend(m, rhost);
+ }
+ else if ((rfmt == SEND_FORMAT_LEGACY) && (str != NULL))
+ {
+ rlegacy(str, rlevel, rhost);
+ }
+
+ asl_free(m);
+
+ if (str != NULL) free(str);
+
+ asl_close(asl);
+
+ return 0;
+}
+
+static void
+printmsg(FILE *f, asl_msg_t *msg, char *mstr, char *fmt, int pflags)
+{
+ char *k, *t, c;
+ const char *v;
+ int i, j, l, paren, oval;
+ time_t tick;
+
+ if ((pflags & PRINT_STD_FMT) || (pflags & PRINT_LEGACY_FMT))
+ {
+ /* LEGACY: Mth dd hh:mm:ss host sender[pid]: message */
+ /* STD: Mth dd hh:mm:ss host sender[pid] <Level>: message */
+
+ /* Time */
+ v = asl_get(msg, ASL_KEY_TIME);
+ tick = 0;
+ if (v == NULL)
+ {
+ fprintf(f, "***Time unknown ");
+ }
+ else
+ {
+ tick = asl_parse_time(v);
+ t = ctime(&tick);
+ if (t == NULL) fprintf(f, "***Time unknown ");
+ else
+ {
+ t[19] = '\0';
+ fprintf(f, "%s ", t + 4);
+ }
+ }
+
+ /* Host */
+ v = asl_get(msg, ASL_KEY_HOST);
+ if (v != NULL) fprintf(f, "%s ", v);
+
+ /* Sender */
+ v = asl_get(msg, ASL_KEY_SENDER);
+ if (v != NULL) fprintf(f, "%s", v);
+
+ /* PID */
+ v = asl_get(msg, ASL_KEY_PID);
+ if ((v != NULL) && (v[0] != '-')) fprintf(f, "[%s]", v);
+
+ v = asl_get(msg, ASL_KEY_LEVEL);
+ i = -1;
+ if (_isanumber((char *)v)) i = atoi(v);
+ if (pflags & PRINT_STD_FMT) fprintf(f, " <%s>", asl_level_string(i));
+
+ fprintf(f, ": ");
+
+ /* Message */
+ v = asl_get(msg, ASL_KEY_MSG);
+ if (v != NULL) fprintf(f, "%s", v);
+
+ fprintf(f, "\n");
+ return;
+ }
+
+ if (fmt == NULL)
+ {
+ fprintf(f, "%s\n", mstr);
+ return;
+ }
+
+ for (i = 0; fmt[i] != '\0'; i++)
+ {
+ if (fmt[i] == '$')
+ {
+ i++;
+ paren = 0;
+
+ if (fmt[i] == '(')
+ {
+ paren = 1;
+ i++;
+ }
+
+ k = calloc(1, 1);
+ l = 0;
+
+ for (j = i; fmt[j] != '\0'; j++)
+ {
+ c = '\0';
+ if (fmt[j] == '\\') c = fmt[++j];
+ else if ((paren == 1) && (fmt[j] ==')')) break;
+ else if (fmt[j] != ' ') c = fmt[j];
+
+ if (c == '\0') break;
+
+ k = realloc(k, l + 1);
+ k[l] = c;
+ k[l + 1] = '\0';
+ l++;
+ }
+
+ if (paren == 1) j++;
+ i = j;
+ if (l > 0)
+ {
+ v = asl_get(msg, k);
+ if (v != NULL)
+ {
+ if ((pflags & PRINT_LOCALTIME) && (!strcmp(k, ASL_KEY_TIME)))
+ {
+ /* convert UTC time to localtime */
+ tick = asl_parse_time(v);
+ t = ctime(&tick);
+ if (t == NULL) fprintf(f, "%s", v);
+ else
+ {
+ t[19] = '\0';
+ fprintf(f, "%s", t + 4);
+ }
+ }
+ else
+ {
+ fprintf(f, "%s", v);
+ }
+ }
+ }
+ free(k);
+ }
+
+ if (fmt[i] == '\\')
+ {
+ i++;
+ if (fmt[i] == '$') fprintf(f, "$");
+ else if (fmt[i] == 'e') fprintf(f, "\e");
+ else if (fmt[i] == 'a') fprintf(f, "\a");
+ else if (fmt[i] == 'b') fprintf(f, "\b");
+ else if (fmt[i] == 'f') fprintf(f, "\f");
+ else if (fmt[i] == 'n') fprintf(f, "\n");
+ else if (fmt[i] == 'r') fprintf(f, "\r");
+ else if (fmt[i] == 't') fprintf(f, "\t");
+ else if (fmt[i] == 'v') fprintf(f, "\v");
+ else if (fmt[i] == '\'') fprintf(f, "\'");
+ else if (fmt[i] == '\\') fprintf(f, "\\");
+ else if (isdigit(fmt[i]))
+ {
+ oval = fmt[i] - '0';
+ if (isdigit(fmt[i+1]))
+ {
+ i++;
+ oval = (oval * 8) + (fmt[i] - '0');
+ if (isdigit(fmt[i+1]))
+ {
+ i++;
+ oval = (oval * 8) + (fmt[i] - '0');
+ }
+ }
+ c = oval;
+ fputc(c, stdout);
+ }
+ continue;
+ }
+
+ if (fmt[i] == '\0') break;
+ fputc(fmt[i], stdout);
+ }
+
+ fprintf(f, "\n");
+}
+
+static char *
+getnextline(FILE *fp, int watch)
+{
+ char *out, c;
+ int len, count;
+
+ len = CHUNK;
+ count = 0;
+ out = calloc(len + 1, 1);
+
+ forever
+ {
+ c = getc(fp);
+ if (c == EOF)
+ {
+ if (watch == 0)
+ {
+ if (count == 0)
+ {
+ free(out);
+ return NULL;
+ }
+ return out;
+ }
+ clearerr(fp);
+ usleep(250000);
+ continue;
+ }
+
+ if (c == '\n') return out;
+ if (c == '\0') return out;
+
+ if (count == len)
+ {
+ len += CHUNK;
+ out = realloc(out, len + 1);
+ }
+
+ out[count++] = c;
+ out[count] = '\0';
+ }
+
+ return NULL;
+}
+
+int
+search_next(asl_msg_t **q, int nq, FILE *log, int watch, aslmsg *outmsg, char **outstr)
+{
+ char *str;
+ aslmsg m;
+ int i, match;
+
+ *outmsg = NULL;
+ *outstr = NULL;
+
+ if (log == NULL) return SEARCH_EOF;
+
+ str = getnextline(log, watch);
+ if (str == NULL) return SEARCH_EOF;
+
+ m = asl_msg_from_string(str);
+ if (m == NULL)
+ {
+ free(str);
+ return SEARCH_NULL;
+ }
+
+ match = 0;
+ if (q == NULL) match = 1;
+ for (i = 0; (i < nq) && (match == 0); i++)
+ {
+ match = asl_msg_cmp(q[i], m);
+ if ((q[i]->count > 0) && (q[i]->op[0] & ASL_QUERY_OP_NOT))
+ {
+ match = !match;
+ }
+ }
+
+ if (match == 0)
+ {
+ free(str);
+ asl_free(m);
+ return SEARCH_NULL;
+ }
+
+ *outmsg = m;
+ *outstr = str;
+ return SEARCH_MATCH;
+}
+
+uint32_t
+optype(char *o)
+{
+ uint32_t op, i;
+
+ op = ASL_QUERY_OP_NULL;
+
+ if (o == NULL) return op;
+
+ for (i = 0; o[i] != '\0'; i++)
+ {
+ if (o[i] == MOD_CASE_FOLD) op |= ASL_QUERY_OP_CASEFOLD;
+ else if (o[i] == MOD_REGEX) op |= ASL_QUERY_OP_REGEX;
+ else if (o[i] == MOD_NUMERIC) op |= ASL_QUERY_OP_NUMERIC;
+ else if (o[i] == MOD_SUBSTRING) op |= ASL_QUERY_OP_SUBSTRING;
+ else if (o[i] == MOD_PREFIX) op |= ASL_QUERY_OP_PREFIX;
+ else if (o[i] == MOD_SUFFIX) op |= ASL_QUERY_OP_SUFFIX;
+
+ else if (!strncasecmp(o+i, OP_EQ, sizeof(OP_EQ)))
+ {
+ op |= ASL_QUERY_OP_EQUAL;
+ i += (sizeof(OP_EQ) - 2);
+ }
+ else if (!strncasecmp(o+i, OP_NE, sizeof(OP_NE)))
+ {
+ op |= ASL_QUERY_OP_NOT_EQUAL;
+ i += (sizeof(OP_NE) - 2);
+ }
+ else if (!strncasecmp(o+i, OP_GT, sizeof(OP_GT)))
+ {
+ op |= ASL_QUERY_OP_GREATER;
+ i += (sizeof(OP_GT) - 2);
+ }
+ else if (!strncasecmp(o+i, OP_GE, sizeof(OP_GE)))
+ {
+ op |= ASL_QUERY_OP_GREATER_EQUAL;
+ i += (sizeof(OP_GE) - 2);
+ }
+ else if (!strncasecmp(o+i, OP_LT, sizeof(OP_LT)))
+ {
+ op |= ASL_QUERY_OP_LESS;
+ i += (sizeof(OP_LT) - 2);
+ }
+ else if (!strncasecmp(o+i, OP_LE, sizeof(OP_LE)))
+ {
+ op |= ASL_QUERY_OP_LESS_EQUAL;
+ i += (sizeof(OP_LE) - 2);
+ }
+ else
+ {
+ fprintf(stderr, "invalid option: %s\n", o);
+ return 0;
+ }
+ }
+
+ /* sanity check */
+ if (op & ASL_QUERY_OP_NUMERIC)
+ {
+ if (op & ASL_QUERY_OP_CASEFOLD)
+ {
+ fprintf(stderr, "warning: case fold modifier has no effect with numeric comparisons\n");
+ op &= ~ASL_QUERY_OP_CASEFOLD;
+ }
+
+ if (op & ASL_QUERY_OP_REGEX)
+ {
+ fprintf(stderr, "warning: regex modifier has no effect with numeric comparisons\n");
+ op &= ~ASL_QUERY_OP_REGEX;
+ }
+
+ if (op & ASL_QUERY_OP_SUBSTRING)
+ {
+ fprintf(stderr, "warning: substring modifier has no effect with numeric comparisons\n");
+ op &= ~ASL_QUERY_OP_SUBSTRING;
+ }
+
+ if (op & ASL_QUERY_OP_PREFIX)
+ {
+ fprintf(stderr, "warning: prefix modifier has no effect with numeric comparisons\n");
+ op &= ~ASL_QUERY_OP_PREFIX;
+ }
+
+ if (op & ASL_QUERY_OP_SUFFIX)
+ {
+ fprintf(stderr, "warning: suffix modifier has no effect with numeric comparisons\n");
+ op &= ~ASL_QUERY_OP_SUFFIX;
+ }
+ }
+
+ if (op & ASL_QUERY_OP_REGEX)
+ {
+ if (op & ASL_QUERY_OP_SUBSTRING)
+ {
+ fprintf(stderr, "warning: substring modifier has no effect with regular expression comparisons\n");
+ op &= ~ASL_QUERY_OP_SUBSTRING;
+ }
+
+ if (op & ASL_QUERY_OP_PREFIX)
+ {
+ fprintf(stderr, "warning: prefix modifier has no effect with regular expression comparisons\n");
+ op &= ~ASL_QUERY_OP_PREFIX;
+ }
+
+ if (op & ASL_QUERY_OP_SUFFIX)
+ {
+ fprintf(stderr, "warning: suffix modifier has no effect with regular expression comparisons\n");
+ op &= ~ASL_QUERY_OP_SUFFIX;
+ }
+ }
+
+ return op;
+}
+
+int
+add_op(asl_msg_t *q, char *key, char *op, char *val, uint32_t flags)
+{
+ uint32_t o;
+
+ if (key == NULL) return -1;
+ if (q == NULL) return -1;
+
+ o = ASL_QUERY_OP_NULL;
+ if (op != NULL)
+ {
+ o = optype(op);
+ if (o == ASL_QUERY_OP_NULL) return -1;
+ if (val == NULL)
+ {
+ fprintf(stderr, "no value supplied for operator %s %s\n", key, op);
+ return -1;
+ }
+
+ if ((o & ASL_QUERY_OP_NUMERIC) && (_isanumber(val) == 0))
+ {
+ fprintf(stderr, "non-numeric value supplied for numeric operator %s %s %s\n", key, op, val);
+ return -1;
+ }
+
+ }
+
+ o |= flags;
+ asl_set_query(q, key, val, o);
+
+ return 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+ FILE *log, *pf, *outfile;
+ int i, j, n, qcount, qn, watch, prune, status, pflags, tflag;
+ asl_msg_t **qlist, *outmsg;
+ char *logname, *outname, *pfmt, *outstr;
+ pid_t syslogd_pid;
+ uint32_t flags;
+
+ qn = 0;
+ watch = 0;
+ prune = 0;
+ logname = _PATH_ASL_OUT;
+ qlist = NULL;
+ qcount = 0;
+ pfmt = NULL;
+ flags = 0;
+ pflags = PRINT_STD_FMT;
+ tflag = PRINT_LOCALTIME;
+
+ for (i = 1; i < argc; i++)
+ {
+ if (!strcmp(argv[i], "-s"))
+ {
+ syslog_send(argc, argv);
+ exit(0);
+ }
+
+ if (!strcmp(argv[i], "-c"))
+ {
+ syslog_remote_control(argc, argv);
+ exit(0);
+ }
+ }
+
+ for (i = 1; i < argc; i++)
+ {
+ if ((!strcmp(argv[i], "-help")) || (!strcmp(argv[i], "--help")))
+ {
+ usage();
+ exit(0);
+ }
+ else if (!strcmp(argv[i], "-w"))
+ {
+ watch = 1;
+ }
+ else if (!strcmp(argv[i], "-u"))
+ {
+ tflag = 0;
+ }
+ else if (!strcmp(argv[i], "-p"))
+ {
+ prune = 1;
+ }
+ else if (!strcmp(argv[i], "-f"))
+ {
+ if ((i + 1) >= argc)
+ {
+ usage();
+ exit(1);
+ }
+
+ logname = argv[++i];
+ }
+ else if (!strcmp(argv[i], "-F"))
+ {
+ if ((i + 1) >= argc)
+ {
+ usage();
+ exit(1);
+ }
+
+ i++;
+ if (!strcmp(argv[i], "raw"))
+ {
+ pflags = 0;
+ tflag = 0;
+ }
+ else if (!strcmp(argv[i], "std"))
+ {
+ pflags = PRINT_STD_FMT;
+ }
+ else if (!strcmp(argv[i], "bsd"))
+ {
+ pflags = PRINT_LEGACY_FMT;
+ }
+ else
+ {
+ pflags = 0;
+ pfmt = argv[i];
+ }
+ }
+ else if (!strcmp(argv[i], "-o"))
+ {
+ flags = 0;
+
+ if (qlist == NULL)
+ {
+ qlist = (asl_msg_t **)calloc(1, sizeof(asl_msg_t *));
+ }
+ else
+ {
+ qlist = (asl_msg_t **)realloc(qlist, (qcount + 1) * sizeof(asl_msg_t *));
+ }
+
+ qcount++;
+ qn = qcount - 1;
+ qlist[qn] = asl_new(ASL_TYPE_QUERY);
+ }
+ else if (!strcmp(argv[i], "-n"))
+ {
+ flags = ASL_QUERY_OP_NOT;
+ }
+ else if (!strcmp(argv[i], "-k"))
+ {
+ i++;
+ for (n = i; n < argc; n++)
+ {
+ if (!strcmp(argv[n], "-o")) break;
+ if (!strcmp(argv[n], "-n")) break;
+ if (!strcmp(argv[n], "-k")) break;
+ if ((n - i) > 2)
+ {
+ fprintf(stderr, "invalid sequence: -k");
+ for (j = i; j <= n; j++) fprintf(stderr, " %s", argv[j]);
+ fprintf(stderr, "\n");
+ usage();
+ exit(1);
+ }
+ }
+
+ n -= i;
+ if (n == 0)
+ {
+ i--;
+ continue;
+ }
+
+ if (qlist == NULL)
+ {
+ qlist = (asl_msg_t **)calloc(1, sizeof(asl_msg_t *));
+ qcount = 1;
+ qn = 0;
+ qlist[qn] = asl_new(ASL_TYPE_QUERY);
+ }
+
+ status = 0;
+ if (n == 1) status = add_op(qlist[qn], argv[i], NULL, NULL, flags);
+ else if (n == 2) status = add_op(qlist[qn], argv[i], OP_EQ, argv[i+1], flags);
+ else status = add_op(qlist[qn], argv[i], argv[i+1], argv[i+2], flags);
+
+ flags = 0;
+ if (status != 0) exit(1);
+ }
+ }
+
+ pflags |= tflag;
+
+ if (prune == 1)
+ {
+ if (watch == 1)
+ {
+ fprintf(stderr, "-w flag has no effect when pruning log file\n");
+ }
+
+ if (getuid() != 0)
+ {
+ fprintf(stderr, "you must be root to prune the log file\n");
+ exit(1);
+ }
+
+ if (qlist == NULL)
+ {
+ fprintf(stderr, "no queries for pruning\n");
+ exit(0);
+ }
+
+ pf = fopen(_PATH_SYSLOGD_PID, "r");
+ if (pf == NULL)
+ {
+ perror(_PATH_SYSLOGD_PID);
+ exit(1);
+ }
+
+ status = fscanf(pf, "%d", &syslogd_pid);
+ fclose(pf);
+ if (status != 1)
+ {
+ fprintf(stderr, "can't read syslogd pid from %s\n", _PATH_SYSLOGD_PID);
+ exit(1);
+ }
+
+ unlink(_PATH_ASL_PRUNE);
+ pf = fopen(_PATH_ASL_PRUNE, "w");
+ if (pf == NULL)
+ {
+ perror(_PATH_ASL_PRUNE);
+ exit(1);
+ }
+
+ for (i = 0; i < qcount; i++)
+ {
+ outstr = asl_msg_to_string(qlist[i], &j);
+ fprintf(pf, "%s\n", outstr);
+ free(outstr);
+ }
+
+ fclose(pf);
+
+ kill(syslogd_pid, SIGWINCH);
+
+ exit(0);
+ }
+
+ log = NULL;
+
+ if (!strcmp(logname, "-")) log = stdin;
+ else log = fopen(logname, "r");
+
+ if (log == NULL)
+ {
+ perror(logname);
+ exit(1);
+ }
+
+ if (watch == 1) fseek(log, 0, SEEK_END);
+ outname = NULL;
+ outfile = stdout;
+
+ do
+ {
+ outmsg = NULL;
+ outstr = NULL;
+ status = search_next(qlist, qcount, log, watch, &outmsg, &outstr);
+
+ if (status == SEARCH_MATCH)
+ {
+ printmsg(outfile, outmsg, outstr, pfmt, pflags);
+ }
+
+ if (outstr != NULL) free(outstr);
+ if (outmsg != NULL) asl_free(outmsg);
+ }
+ while (status != SEARCH_EOF);
+
+ fclose(log);
+
+ for (i = 0; i < qcount; i++) asl_free(qlist[i]);
+ if (qlist != NULL) free(qlist);
+
+ exit(0);
+}