]> git.saurik.com Git - apple/syslog.git/commitdiff
syslog-13.tar.gz mac-os-x-104 mac-os-x-1041 mac-os-x-1042 mac-os-x-1043 v13
authorApple <opensource@apple.com>
Thu, 3 Feb 2005 01:13:20 +0000 (01:13 +0000)
committerApple <opensource@apple.com>
Thu, 3 Feb 2005 01:13:20 +0000 (01:13 +0000)
26 files changed:
Makefile [new file with mode: 0644]
Makefile.postamble [new file with mode: 0644]
Makefile.preamble [new file with mode: 0644]
PB.project [new file with mode: 0644]
syslogd.tproj/Makefile [new file with mode: 0644]
syslogd.tproj/Makefile.postamble [new file with mode: 0644]
syslogd.tproj/Makefile.preamble [new file with mode: 0644]
syslogd.tproj/PB.project [new file with mode: 0644]
syslogd.tproj/asl_action.c [new file with mode: 0644]
syslogd.tproj/asl_in.c [new file with mode: 0644]
syslogd.tproj/bsd_in.c [new file with mode: 0644]
syslogd.tproj/bsd_out.c [new file with mode: 0644]
syslogd.tproj/com.apple.syslogd.plist [new file with mode: 0644]
syslogd.tproj/daemon.c [new file with mode: 0644]
syslogd.tproj/daemon.h [new file with mode: 0644]
syslogd.tproj/klog_in.c [new file with mode: 0644]
syslogd.tproj/syslog.conf.5 [new file with mode: 0644]
syslogd.tproj/syslogd.8 [new file with mode: 0644]
syslogd.tproj/syslogd.c [new file with mode: 0644]
syslogd.tproj/udp_in.c [new file with mode: 0644]
util.tproj/Makefile [new file with mode: 0644]
util.tproj/Makefile.postamble [new file with mode: 0644]
util.tproj/Makefile.preamble [new file with mode: 0644]
util.tproj/PB.project [new file with mode: 0644]
util.tproj/syslog.1 [new file with mode: 0644]
util.tproj/syslog.c [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..75883c0
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,40 @@
+#
+# 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
diff --git a/Makefile.postamble b/Makefile.postamble
new file mode 100644 (file)
index 0000000..fd69f0b
--- /dev/null
@@ -0,0 +1 @@
+# Empty for now
diff --git a/Makefile.preamble b/Makefile.preamble
new file mode 100644 (file)
index 0000000..a6b1e76
--- /dev/null
@@ -0,0 +1 @@
+CLEAN_ALL_SUBPROJECTS = YES
diff --git a/PB.project b/PB.project
new file mode 100644 (file)
index 0000000..4f85f4f
--- /dev/null
@@ -0,0 +1,14 @@
+{
+    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; 
+}
diff --git a/syslogd.tproj/Makefile b/syslogd.tproj/Makefile
new file mode 100644 (file)
index 0000000..c93682c
--- /dev/null
@@ -0,0 +1,46 @@
+#
+# 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
diff --git a/syslogd.tproj/Makefile.postamble b/syslogd.tproj/Makefile.postamble
new file mode 100644 (file)
index 0000000..015aa9d
--- /dev/null
@@ -0,0 +1,119 @@
+###############################################################################
+#  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
diff --git a/syslogd.tproj/Makefile.preamble b/syslogd.tproj/Makefile.preamble
new file mode 100644 (file)
index 0000000..c044467
--- /dev/null
@@ -0,0 +1,116 @@
+###############################################################################
+#  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
diff --git a/syslogd.tproj/PB.project b/syslogd.tproj/PB.project
new file mode 100644 (file)
index 0000000..7c3db9e
--- /dev/null
@@ -0,0 +1,41 @@
+{
+    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"; 
+}
diff --git a/syslogd.tproj/asl_action.c b/syslogd.tproj/asl_action.c
new file mode 100644 (file)
index 0000000..92b7da1
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * 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;
+}
diff --git a/syslogd.tproj/asl_in.c b/syslogd.tproj/asl_in.c
new file mode 100644 (file)
index 0000000..7a1723e
--- /dev/null
@@ -0,0 +1,469 @@
+/*
+ * 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;
+}
diff --git a/syslogd.tproj/bsd_in.c b/syslogd.tproj/bsd_in.c
new file mode 100644 (file)
index 0000000..d1ccd50
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * 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;
+}
diff --git a/syslogd.tproj/bsd_out.c b/syslogd.tproj/bsd_out.c
new file mode 100644 (file)
index 0000000..a24fdaf
--- /dev/null
@@ -0,0 +1,679 @@
+/*
+ * 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;
+}
diff --git a/syslogd.tproj/com.apple.syslogd.plist b/syslogd.tproj/com.apple.syslogd.plist
new file mode 100644 (file)
index 0000000..8d7574f
--- /dev/null
@@ -0,0 +1,18 @@
+<?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>
diff --git a/syslogd.tproj/daemon.c b/syslogd.tproj/daemon.c
new file mode 100644 (file)
index 0000000..775a0d7
--- /dev/null
@@ -0,0 +1,700 @@
+/*
+ * 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(&gtime, 0, sizeof(struct tm));
+       gmtime_r(&tick, &gtime);
+
+       /* Canonical form: YYYY.MM.DD hh:mm:ss UTC */
+       asprintf(&timestr, "%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;
+}
diff --git a/syslogd.tproj/daemon.h b/syslogd.tproj/daemon.h
new file mode 100644 (file)
index 0000000..c99e628
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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__ */
diff --git a/syslogd.tproj/klog_in.c b/syslogd.tproj/klog_in.c
new file mode 100644 (file)
index 0000000..5edd1d7
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * 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;
+}
diff --git a/syslogd.tproj/syslog.conf.5 b/syslogd.tproj/syslog.conf.5
new file mode 100644 (file)
index 0000000..e3b88b9
--- /dev/null
@@ -0,0 +1,232 @@
+.\" 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 .
diff --git a/syslogd.tproj/syslogd.8 b/syslogd.tproj/syslogd.8
new file mode 100644 (file)
index 0000000..2dd6da1
--- /dev/null
@@ -0,0 +1,277 @@
+.\" 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.
diff --git a/syslogd.tproj/syslogd.c b/syslogd.tproj/syslogd.c
new file mode 100644 (file)
index 0000000..0377654
--- /dev/null
@@ -0,0 +1,487 @@
+/*
+ * 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);
+}
diff --git a/syslogd.tproj/udp_in.c b/syslogd.tproj/udp_in.c
new file mode 100644 (file)
index 0000000..fa91a69
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * 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;
+}
diff --git a/util.tproj/Makefile b/util.tproj/Makefile
new file mode 100644 (file)
index 0000000..6049f02
--- /dev/null
@@ -0,0 +1,46 @@
+#
+# 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
diff --git a/util.tproj/Makefile.postamble b/util.tproj/Makefile.postamble
new file mode 100644 (file)
index 0000000..936823b
--- /dev/null
@@ -0,0 +1,115 @@
+###############################################################################
+#  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
diff --git a/util.tproj/Makefile.preamble b/util.tproj/Makefile.preamble
new file mode 100644 (file)
index 0000000..c044467
--- /dev/null
@@ -0,0 +1,116 @@
+###############################################################################
+#  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
diff --git a/util.tproj/PB.project b/util.tproj/PB.project
new file mode 100644 (file)
index 0000000..4b81855
--- /dev/null
@@ -0,0 +1,41 @@
+{
+    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"; 
+}
diff --git a/util.tproj/syslog.1 b/util.tproj/syslog.1
new file mode 100644 (file)
index 0000000..e0aaf3c
--- /dev/null
@@ -0,0 +1,451 @@
+.\" 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.
diff --git a/util.tproj/syslog.c b/util.tproj/syslog.c
new file mode 100644 (file)
index 0000000..74e69e7
--- /dev/null
@@ -0,0 +1,1521 @@
+/*
+ * 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(&gtime, 0, sizeof(struct tm));
+       timestr = NULL;
+
+       tick = time(NULL);
+       gmtime_r(&tick, &gtime);
+
+       /* Canonical form: YYYY.MM.DD hh:mm:ss UTC */
+       asprintf(&timestr, "%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);
+}