From b16a592aee171655b85f8e45c0604c38494ab8ea Mon Sep 17 00:00:00 2001 From: Apple Date: Thu, 3 Feb 2005 01:13:20 +0000 Subject: [PATCH] syslog-13.tar.gz --- Makefile | 40 + Makefile.postamble | 1 + Makefile.preamble | 1 + PB.project | 14 + syslogd.tproj/Makefile | 46 + syslogd.tproj/Makefile.postamble | 119 ++ syslogd.tproj/Makefile.preamble | 116 ++ syslogd.tproj/PB.project | 41 + syslogd.tproj/asl_action.c | 263 +++++ syslogd.tproj/asl_in.c | 469 ++++++++ syslogd.tproj/bsd_in.c | 131 +++ syslogd.tproj/bsd_out.c | 679 +++++++++++ syslogd.tproj/com.apple.syslogd.plist | 18 + syslogd.tproj/daemon.c | 700 ++++++++++++ syslogd.tproj/daemon.h | 91 ++ syslogd.tproj/klog_in.c | 110 ++ syslogd.tproj/syslog.conf.5 | 232 ++++ syslogd.tproj/syslogd.8 | 277 +++++ syslogd.tproj/syslogd.c | 487 ++++++++ syslogd.tproj/udp_in.c | 210 ++++ util.tproj/Makefile | 46 + util.tproj/Makefile.postamble | 115 ++ util.tproj/Makefile.preamble | 116 ++ util.tproj/PB.project | 41 + util.tproj/syslog.1 | 451 ++++++++ util.tproj/syslog.c | 1521 +++++++++++++++++++++++++ 26 files changed, 6335 insertions(+) create mode 100644 Makefile create mode 100644 Makefile.postamble create mode 100644 Makefile.preamble create mode 100644 PB.project create mode 100644 syslogd.tproj/Makefile create mode 100644 syslogd.tproj/Makefile.postamble create mode 100644 syslogd.tproj/Makefile.preamble create mode 100644 syslogd.tproj/PB.project create mode 100644 syslogd.tproj/asl_action.c create mode 100644 syslogd.tproj/asl_in.c create mode 100644 syslogd.tproj/bsd_in.c create mode 100644 syslogd.tproj/bsd_out.c create mode 100644 syslogd.tproj/com.apple.syslogd.plist create mode 100644 syslogd.tproj/daemon.c create mode 100644 syslogd.tproj/daemon.h create mode 100644 syslogd.tproj/klog_in.c create mode 100644 syslogd.tproj/syslog.conf.5 create mode 100644 syslogd.tproj/syslogd.8 create mode 100644 syslogd.tproj/syslogd.c create mode 100644 syslogd.tproj/udp_in.c create mode 100644 util.tproj/Makefile create mode 100644 util.tproj/Makefile.postamble create mode 100644 util.tproj/Makefile.preamble create mode 100644 util.tproj/PB.project create mode 100644 util.tproj/syslog.1 create mode 100644 util.tproj/syslog.c diff --git a/Makefile b/Makefile new file mode 100644 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 index 0000000..fd69f0b --- /dev/null +++ b/Makefile.postamble @@ -0,0 +1 @@ +# Empty for now diff --git a/Makefile.preamble b/Makefile.preamble new file mode 100644 index 0000000..a6b1e76 --- /dev/null +++ b/Makefile.preamble @@ -0,0 +1 @@ +CLEAN_ALL_SUBPROJECTS = YES diff --git a/PB.project b/PB.project new file mode 100644 index 0000000..4f85f4f --- /dev/null +++ b/PB.project @@ -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 index 0000000..c93682c --- /dev/null +++ b/syslogd.tproj/Makefile @@ -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 index 0000000..015aa9d --- /dev/null +++ b/syslogd.tproj/Makefile.postamble @@ -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 index 0000000..c044467 --- /dev/null +++ b/syslogd.tproj/Makefile.preamble @@ -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 index 0000000..7c3db9e --- /dev/null +++ b/syslogd.tproj/PB.project @@ -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 index 0000000..92b7da1 --- /dev/null +++ b/syslogd.tproj/asl_action.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 index 0000000..7a1723e --- /dev/null +++ b/syslogd.tproj/asl_in.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 index 0000000..d1ccd50 --- /dev/null +++ b/syslogd.tproj/bsd_in.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 index 0000000..a24fdaf --- /dev/null +++ b/syslogd.tproj/bsd_out.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 index 0000000..8d7574f --- /dev/null +++ b/syslogd.tproj/com.apple.syslogd.plist @@ -0,0 +1,18 @@ + + + + + Label + com.apple.syslogd + ServiceDescription + Apple System Log Daemon + OnDemand + + ProgramArguments + + /usr/sbin/syslogd + + ServiceIPC + + + diff --git a/syslogd.tproj/daemon.c b/syslogd.tproj/daemon.c new file mode 100644 index 0000000..775a0d7 --- /dev/null +++ b/syslogd.tproj/daemon.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SYSLOG_NAMES +#include +#include "daemon.h" + +#define streq(A,B) (strcmp(A,B)==0) + +static char myname[MAXHOSTNAMELEN + 1] = {0}; +static int gotname = 0; + +struct aslevent +{ + int fd; + unsigned char read:1; + unsigned char write:1; + unsigned char except:1; + aslreadfn readfn; + aslwritefn writefn; + aslexceptfn exceptfn; + char *sender; + uid_t uid; + gid_t gid; + TAILQ_ENTRY(aslevent) entries; +}; + +struct asloutput +{ + aslsendmsgfn sendmsg; + const char *outid; + TAILQ_ENTRY(asloutput) entries; +}; + +struct aslmatch +{ + char *outid; + asl_msg_t *query; + TAILQ_ENTRY(aslmatch) entries; +}; + +TAILQ_HEAD(ae, aslevent) Eventq; +TAILQ_HEAD(ao, asloutput) Outq; +TAILQ_HEAD(am, aslmatch) Matchq; + +int +aslevent_init(void) +{ + TAILQ_INIT(&Eventq); + TAILQ_INIT(&Outq); + TAILQ_INIT(&Matchq); + + return 0; +} + +int +aslevent_log(asl_msg_t *msg, char *outid) +{ + struct asloutput *i; + int status = -1; + + for (i = Outq.tqh_first; i != NULL; i = i->entries.tqe_next) + { + if ((outid != NULL) && (strcmp(i->outid, outid) == 0)) + { + status = i->sendmsg(msg, outid); + } + } + + return status; +} + +int +aslevent_addmatch(asl_msg_t *query, char *outid) +{ + struct aslmatch *tmp; + + if (query == NULL) return -1; + if (outid == NULL) return -1; + + tmp = calloc(1, sizeof(struct aslmatch)); + if (tmp == NULL) return -1; + tmp->query = query; + tmp->outid = outid; + TAILQ_INSERT_TAIL(&Matchq, tmp, entries); + + return 0; +} + +void +aslevent_match(asl_msg_t *msg) +{ + struct aslmatch *i; + + if (msg == NULL) return; + + for (i = Matchq.tqh_first; i != NULL; i = i->entries.tqe_next) + { + if (asl_msg_cmp(i->query, msg) != 0) + { + aslevent_log(msg, i->outid); + } + } +} + +int +aslevent_removefd(int fd) +{ + struct aslevent *i; + int found = -1; + + for (i = Eventq.tqh_first; i != NULL; i = i->entries.tqe_next) + { + if (fd == i->fd) + { + asldebug("removing %d\n", i->fd); + TAILQ_REMOVE(&Eventq, i, entries); + found = 0; + if (i->sender != NULL) free(i->sender); + free(i); + i = NULL; + return 0; + } + } + + return -1; +} + +const char * +whatsmyhostname() +{ + char *dot; + + if (gotname != 0) return (const char *)myname; + + if (gethostname(myname, MAXHOSTNAMELEN) < 0) + { + memset(myname, 0, sizeof(myname)); + return "localhost"; + } + + if (strcmp(myname, "localhost")) gotname = 1; + + dot = strchr(myname, '.'); + if (dot != NULL) *dot = '\0'; + + return (const char *)myname; +} + +int +aslevent_addfd(int fd, aslreadfn readfn, aslwritefn writefn, aslexceptfn exceptfn) +{ + struct aslevent *e; + int found = 0; +#ifdef LOCAL_PEERCRED + struct xucred cr; +#endif + int len; + uid_t u; + gid_t g; + struct sockaddr_storage ss; + char *sender, str[256]; + + u = 99; + g = 99; + sender = NULL; + + memset(&ss, 0, sizeof(struct sockaddr_storage)); + + len = sizeof(struct sockaddr_storage); + + if (getpeername(fd, (struct sockaddr *)&ss, &len) == 0) + { + if (len == 0) + { + /* UNIX Domain socket */ + snprintf(str, sizeof(str), whatsmyhostname()); + sender = str; + } + else + { + if (inet_ntop(ss.ss_family, (struct sockaddr *)&ss, str, 256) == 0) sender = str; + } + } + +#ifdef LOCAL_PEERCRED + len = sizeof(cr); + + if (getsockopt(fd, LOCAL_PEERCRED, 1, &cr, &len) == 0) + { + u = cr.cr_uid; + g = cr.cr_gid; + } +#endif + + asldebug("fd %d UID %d GID %d Sender %s\n", fd, u, g, (sender == NULL) ? "NULL" : sender ); + + for (e = Eventq.tqh_first; e != NULL; e = e->entries.tqe_next) + { + if (fd == e->fd) + { + e->readfn = readfn; + e->writefn = writefn; + e->exceptfn = exceptfn; + if (e->sender != NULL) free(e->sender); + e->sender = NULL; + if (sender != NULL) e->sender = strdup(sender); + e->uid = u; + e->gid = g; + found = 1; + } + } + + if (found) return 0; + + e = calloc(1, sizeof(struct aslevent)); + if (e == NULL) return -1; + + e->fd = fd; + e->readfn = readfn; + e->writefn = writefn; + e->exceptfn = exceptfn; + e->sender = NULL; + if (sender != NULL) e->sender = strdup(sender); + e->uid = u; + e->gid = g; + + TAILQ_INSERT_TAIL(&Eventq, e, entries); + + return 0; +} + +int +aslmsg_verify(struct aslevent *e, asl_msg_t *msg) +{ + const char *val; + char buf[32], *timestr; + time_t tick; + struct tm gtime; + + if (msg == NULL) return -1; + + /* Time */ + tick = 0; + val = asl_get(msg, ASL_KEY_TIME); + if (val != NULL) tick = asl_parse_time(val); + + if (tick == 0) tick = time(NULL); + memset(>ime, 0, sizeof(struct tm)); + gmtime_r(&tick, >ime); + + /* Canonical form: YYYY.MM.DD hh:mm:ss UTC */ + asprintf(×tr, "%d.%02d.%02d %02d:%02d:%02d UTC", gtime.tm_year + 1900, gtime.tm_mon + 1, gtime.tm_mday, gtime.tm_hour, gtime.tm_min, gtime.tm_sec); + if (timestr != NULL) + { + asl_set(msg, ASL_KEY_TIME, timestr); + free(timestr); + } + + /* Host */ + if (e == NULL) asl_set(msg, ASL_KEY_HOST, whatsmyhostname()); + else if (e->sender != NULL) asl_set(msg, ASL_KEY_HOST, e->sender); + + /* Sender */ + val = asl_get(msg, ASL_KEY_SENDER); + if (val == NULL) asl_set(msg, ASL_KEY_SENDER, "Unknown"); + + /* PID */ + val = asl_get(msg, ASL_KEY_PID); + if (val == NULL) asl_set(msg, ASL_KEY_PID, "0"); + + /* UID */ + val = asl_get(msg, ASL_KEY_UID); + if (val == NULL) + { + if (e == NULL) asl_set(msg, ASL_KEY_UID, "-2"); + else if (e->uid == 99) asl_set(msg, ASL_KEY_UID, "-2"); + else + { + snprintf(buf, sizeof(buf), "%d", e->uid); + asl_set(msg, ASL_KEY_UID, buf); + } + } + else if ((e != NULL) && (e->uid != 99)) + { + snprintf(buf, sizeof(buf), "%d", e->uid); + asl_set(msg, ASL_KEY_UID, buf); + } + + /* GID */ + val = asl_get(msg, ASL_KEY_GID); + if (val == NULL) + { + if (e == NULL) asl_set(msg, ASL_KEY_GID, "-2"); + else if (e->gid == 99) asl_set(msg, ASL_KEY_GID, "-2"); + else + { + snprintf(buf, sizeof(buf), "%d", e->gid); + asl_set(msg, ASL_KEY_GID, buf); + } + } + else if ((e != NULL) && (e->gid != 99)) + { + snprintf(buf, sizeof(buf), "%d", e->gid); + asl_set(msg, ASL_KEY_GID, buf); + } + + /* Level */ + val = asl_get(msg, ASL_KEY_LEVEL); + if (val == NULL) asl_set(msg, ASL_KEY_LEVEL, "7"); + + return 0; +} + +int +aslevent_addoutput(aslsendmsgfn fn, const char *outid) +{ + struct asloutput *tmp; + + tmp = calloc(1, sizeof(struct asloutput)); + if (tmp == NULL) return -1; + + tmp->sendmsg = fn; + tmp->outid = outid; + + TAILQ_INSERT_TAIL(&Outq, tmp, entries); + + return 0; +} + +int +aslevent_fdsets(fd_set *rd, fd_set *wr, fd_set *ex) +{ + struct aslevent *e; + int status = 0; + + asldebug("--> aslevent_fdsets\n"); + FD_ZERO(rd); + FD_ZERO(wr); + FD_ZERO(ex); + + for (e = Eventq.tqh_first; e != NULL; e = e->entries.tqe_next) + { + asldebug("adding fd %d\n", e->fd); + if (e->readfn) + { + FD_SET(e->fd, rd); + status = MAX(e->fd, status); + } + + if (e->writefn) + { + FD_SET(e->fd, wr); + status = MAX(e->fd, status); + } + + if (e->exceptfn) + { + FD_SET(e->fd, ex); + status = MAX(e->fd, status); + } + } + + asldebug("<--aslevent_fdsets\n"); + return status; +} + +void +aslevent_handleevent(fd_set rd, fd_set wr, fd_set ex, char *errstr) +{ + struct aslevent *e; + char *out = NULL; + asl_msg_t *msg; + + asldebug("--> aslevent_handleevent\n"); + if (errstr) errstr = NULL; + + for (e = Eventq.tqh_first; e != NULL; e = e->entries.tqe_next) + { + if (FD_ISSET(e->fd, &rd) && (e->readfn != NULL)) + { + asldebug("handling read event on %d\n", e->fd); + msg = e->readfn(e->fd); + if (msg == NULL) + { + asldebug("error reading message\n"); + continue; + } + + if (aslmsg_verify(e, msg) < 0) + { + asl_free(msg); + asldebug("recieved invalid message\n"); + } + else + { + aslevent_match(msg); + asl_free(msg); + } + } + + if (FD_ISSET(e->fd, &ex) && e->exceptfn) + { + asldebug("handling except event on %d\n", e->fd); + out = e->exceptfn(e->fd); + if (out == NULL) asldebug("error writing message\n"); + } + } + + asldebug("<-- aslevent_handleevent\n"); +} + +int +asl_log_string(const char *str) +{ + asl_msg_t *msg; + + if (str == NULL) return 1; + + msg = asl_msg_from_string(str); + if (aslmsg_verify(NULL, msg) < 0) + { + asl_free(msg); + return -1; + } + + aslevent_match(msg); + asl_free(msg); + return 0; +} + +void +aslmark(void) +{ + char *str; + + str = NULL; + asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [%s -- MARK --] [%s 0] [%s 0] [Facility syslog]", + ASL_KEY_SENDER, + ASL_KEY_LEVEL, ASL_LEVEL_INFO, + ASL_KEY_PID, getpid(), + ASL_KEY_MSG, ASL_KEY_UID, ASL_KEY_GID); + + asl_log_string(str); + if (str != NULL) free(str); +} + +asl_msg_t * +asl_syslog_input_convert(const char *in, int len, char *rhost, int kern) +{ + int pf, pri, index, n; + char *p, *colon, *brace, *tmp, *tval, *sval, *pval, *mval; + char prival[8]; + const char *fval; + asl_msg_t *msg; + struct tm time; + time_t tick; + + if (in == NULL) return NULL; + if (len <= 0) return NULL; + + asldebug("asl_syslog_input_convert: %s\n", in); + + pri = LOG_DEBUG; + tval = NULL; + sval = NULL; + pval = NULL; + mval = NULL; + fval = NULL; + + index = 0; + p = (char *)in; + + while ((index < len) && ((*p == ' ') || (*p == '\t'))) + { + p++; + index++; + } + + if (index >= len) return NULL; + + if (*p == '<') + { + p++; + index++; + + n = sscanf(p, "%d", &pf); + if (n == 1) + { + pri = pf & 0x7; + if (pf > 0x7) fval = asl_syslog_faciliy_num_to_name(pf & LOG_FACMASK); + } + + while ((index < len) && (*p != '>')) + { + p++; + index++; + } + + if (index < len) + { + p++; + index++; + } + } + + snprintf(prival, sizeof(prival), "%d", pri); + + if (((len - index) > 15) && (p[9] == ':') && (p[12] == ':') && (p[15] == ' ')) + { + tmp = malloc(16); + memcpy(tmp, p, 15); + tmp[15] = '\0'; + + tick = asl_parse_time(tmp); + if (tick == (time_t)-1) + { + tval = tmp; + } + else + { + free(tmp); + gmtime_r(&tick, &time); + asprintf(&tval, "%d.%02d.%02d %02d:%02d:%02d UTC", time.tm_year + 1900, time.tm_mon + 1, time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec); + } + + p += 16; + index += 16; + } + + if (kern != 0) + { + msg = (asl_msg_t *)calloc(1, sizeof(asl_msg_t)); + msg->type = ASL_TYPE_MSG; + + asl_set(msg, ASL_KEY_SENDER, "kernel"); + + asl_set(msg, "Facility", "kern"); + if (tval != NULL) + { + asl_set(msg, ASL_KEY_TIME, tval); + free(tval); + } + + asl_set(msg, ASL_KEY_MSG, p); + + asl_set(msg, ASL_KEY_LEVEL, prival); + + asl_set(msg, ASL_KEY_PID, "0"); + + asl_set(msg, ASL_KEY_UID, "0"); + + asl_set(msg, ASL_KEY_GID, "0"); + + asl_set(msg, ASL_KEY_HOST, whatsmyhostname()); + + return msg; + } + + colon = strchr(p, ':'); + brace = strchr(p, '['); + + if (colon != NULL) + { + if ((brace != NULL) && (brace < colon)) + { + n = brace - p; + sval = malloc(n + 1); + memcpy(sval, p, n); + sval[n] = '\0'; + + n = colon - (brace + 1) - 1; + pval = malloc(n + 1); + memcpy(pval, (brace + 1), n); + pval[n] = '\0'; + } + else + { + n = colon - p; + sval = malloc(n + 1); + memcpy(sval, p, n); + sval[n] = '\0'; + } + + n = colon - p; + p = colon + 1; + index += (n + 1); + } + + if (*p == ' ') + { + p++; + index++; + } + + n = len - index; + if (n > 0) + { + mval = malloc(n + 1); + memcpy(mval, p, n); + mval[n] = '\0'; + } + + if (fval == NULL) fval = asl_syslog_faciliy_num_to_name(LOG_USER); + + msg = (asl_msg_t *)calloc(1, sizeof(asl_msg_t)); + msg->type = ASL_TYPE_MSG; + + if (tval != NULL) + { + asl_set(msg, ASL_KEY_TIME, tval); + free(tval); + } + + if (fval != NULL) asl_set(msg, "Facility", fval); + else asl_set(msg, "Facility", "user"); + + if (sval != NULL) + { + asl_set(msg, ASL_KEY_SENDER, sval); + free(sval); + } + + if (pval != NULL) + { + asl_set(msg, ASL_KEY_PID, pval); + free(pval); + } + else asl_set(msg, ASL_KEY_PID, "-1"); + + if (mval != NULL) + { + asl_set(msg, ASL_KEY_MSG, mval); + free(mval); + } + + asl_set(msg, ASL_KEY_LEVEL, prival); + asl_set(msg, ASL_KEY_UID, "-2"); + asl_set(msg, ASL_KEY_GID, "-2"); + + if (rhost == NULL) asl_set(msg, ASL_KEY_HOST, whatsmyhostname()); + else asl_set(msg, ASL_KEY_HOST, rhost); + + if (msg->count == 0) + { + asl_free(msg); + return NULL; + } + + return msg; +} + +char * +get_line_from_file(FILE *f) +{ + char *s, *out; + size_t len; + + out = fgetln(f, &len); + if (out == NULL) return NULL; + if (len == 0) return NULL; + + s = malloc(len + 1); + memcpy(s, out, len); + + s[len - 1] = '\0'; + return s; +} diff --git a/syslogd.tproj/daemon.h b/syslogd.tproj/daemon.h new file mode 100644 index 0000000..c99e628 --- /dev/null +++ b/syslogd.tproj/daemon.h @@ -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 +#include +#include +#include +#include +#include +#include + +#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 index 0000000..5edd1d7 --- /dev/null +++ b/syslogd.tproj/klog_in.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 index 0000000..e3b88b9 --- /dev/null +++ b/syslogd.tproj/syslog.conf.5 @@ -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 index 0000000..2dd6da1 --- /dev/null +++ b/syslogd.tproj/syslogd.8 @@ -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 index 0000000..0377654 --- /dev/null +++ b/syslogd.tproj/syslogd.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 index 0000000..fa91a69 --- /dev/null +++ b/syslogd.tproj/udp_in.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 index 0000000..6049f02 --- /dev/null +++ b/util.tproj/Makefile @@ -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 index 0000000..936823b --- /dev/null +++ b/util.tproj/Makefile.postamble @@ -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 index 0000000..c044467 --- /dev/null +++ b/util.tproj/Makefile.preamble @@ -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 index 0000000..4b81855 --- /dev/null +++ b/util.tproj/PB.project @@ -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 index 0000000..e0aaf3c --- /dev/null +++ b/util.tproj/syslog.1 @@ -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 index 0000000..74e69e7 --- /dev/null +++ b/util.tproj/syslog.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MOD_CASE_FOLD 'C' +#define MOD_REGEX 'R' +#define MOD_SUBSTRING 'S' +#define MOD_PREFIX 'A' +#define MOD_SUFFIX 'Z' +#define MOD_NUMERIC 'N' + +#define OP_EQ "eq" +#define OP_NE "ne" +#define OP_GT "gt" +#define OP_GE "ge" +#define OP_LT "lt" +#define OP_LE "le" + +#define ASL_QUERY_OP_NOT 0x1000 + +#define SEARCH_EOF -1 +#define SEARCH_NULL 0 +#define SEARCH_MATCH 1 + +#define PROC_NOT_FOUND -1 +#define PROC_NOT_UNIQUE -2 + +#define RC_MASTER -1 +#define RC_SYSLOGD -2 + +#define CHUNK 64 +#define forever for(;;) + +#define SEND_FORMAT_LEGACY 0 +#define SEND_FORMAT_ASL 1 + +#define PRINT_LOCALTIME 0x00000001 +#define PRINT_LEGACY_FMT 0x00000002 +#define PRINT_STD_FMT 0x00000004 + +#define ASL_FILTER_MASK_PACEWNID 0xff +#define ASL_FILTER_MASK_PACEWNI 0x7f +#define ASL_FILTER_MASK_PACEWN 0x3f +#define ASL_FILTER_MASK_PACEW 0x1f +#define ASL_FILTER_MASK_PACE 0x0f +#define ASL_FILTER_MASK_PAC 0x07 + + +/* BEGIN PRIVATE API */ +#define _PATH_ASL_PRUNE "/var/run/asl_prune" +#define _PATH_SYSLOGD_PID "/var/run/syslog.pid" + +/* notify SPI */ +uint32_t notify_get_state(int token, int *state); +uint32_t notify_set_state(int token, int state); +uint32_t notify_register_plain(const char *name, int *out_token); + +extern char *asl_msg_to_string(aslmsg msg, uint32_t *len); +extern asl_msg_t *asl_msg_from_string(const char *buf); +extern int asl_msg_cmp(asl_msg_t *a, asl_msg_t *b); +extern time_t asl_parse_time(const char *in); +/* END PRIVATE API */ + +static const char *myname = "syslog"; + +void +usage() +{ + fprintf(stderr, "usage:\n"); + fprintf(stderr, "%s -s [-r host] [-l level] message...\n", myname); + fprintf(stderr, " send a message\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "%s -s [-r host] -k key val [key val]...\n", myname); + fprintf(stderr, " send a message with the given keys and values\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "%s -c process [filter]\n", myname); + fprintf(stderr, " get (set if filter is specified) syslog filter for process (pid or name)\n"); + fprintf(stderr, " level may be any combination of the characters \"p a c e w n i d\"\n"); + fprintf(stderr, " p = Emergency (\"Panic\")\n"); + fprintf(stderr, " a = Alert\n"); + fprintf(stderr, " c = Critical\n"); + fprintf(stderr, " e = Error\n"); + fprintf(stderr, " w = Warning\n"); + fprintf(stderr, " n = Notice\n"); + fprintf(stderr, " i = Info\n"); + fprintf(stderr, " d = Debug\n"); + fprintf(stderr, " a minus sign preceeding a single letter means \"up to\" that level\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "%s -p [-k key [[op] val]]... [-o -k key [[op] val]] ...]...\n", myname); + fprintf(stderr, " -p prune datastore according to input expression (see below)\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "%s [-w] [-F format] [-u] [-k key [[op] val]]... [-o -k key [[op] val]] ...]...\n", myname); + fprintf(stderr, " -w watch file (^C to quit)\n"); + fprintf(stderr, " -F output format may be \"std\", \"raw\", or \"bsd\"\n"); + fprintf(stderr, " format may also be a string containing variables of the form\n"); + fprintf(stderr, " $Key or $(Key) - use the latter for non-whitespace delimited variables\n"); + fprintf(stderr, " -u force printing of all timestamps using UTC\n"); + fprintf(stderr, " -k key/value match\n"); + fprintf(stderr, " if no operator or value is given, checks for the existance of the key\n"); + fprintf(stderr, " if no operator is given, default is \"%s\"\n", OP_EQ); + fprintf(stderr, " -o begins a new query\n"); + fprintf(stderr, " queries are \'OR\'ed together\n"); + fprintf(stderr, "operators are zero or more modifiers followed by a comparison\n"); + fprintf(stderr, " %s equal\n", OP_EQ); + fprintf(stderr, " %s not equal\n", OP_NE); + fprintf(stderr, " %s greater than\n", OP_GT); + fprintf(stderr, " %s greater or equal\n", OP_GE); + fprintf(stderr, " %s less than\n", OP_LT); + fprintf(stderr, " %s less or equal\n", OP_LE); + fprintf(stderr, "optional modifiers for operators\n"); + fprintf(stderr, " %c case-fold\n", MOD_CASE_FOLD); + fprintf(stderr, " %c regular expression\n", MOD_REGEX); + fprintf(stderr, " %c substring\n", MOD_SUBSTRING); + fprintf(stderr, " %c prefix\n", MOD_PREFIX); + fprintf(stderr, " %c suffix\n", MOD_SUFFIX); + fprintf(stderr, " %c numeric comparison\n", MOD_NUMERIC); +} + +const char * +notify_status_string(int status) +{ + if (status == NOTIFY_STATUS_OK) return "OK"; + if (status == NOTIFY_STATUS_INVALID_NAME) return "Process not registered"; + if (status == NOTIFY_STATUS_NOT_AUTHORIZED) return "Not authorized"; + return "Operation failed"; +} + +const char * +asl_level_string(int level) +{ + if (level == ASL_LEVEL_EMERG) return ASL_STRING_EMERG; + if (level == ASL_LEVEL_ALERT) return ASL_STRING_ALERT; + if (level == ASL_LEVEL_CRIT) return ASL_STRING_CRIT; + if (level == ASL_LEVEL_ERR) return ASL_STRING_ERR; + if (level == ASL_LEVEL_WARNING) return ASL_STRING_WARNING; + if (level == ASL_LEVEL_NOTICE) return ASL_STRING_NOTICE; + if (level == ASL_LEVEL_INFO) return ASL_STRING_INFO; + if (level == ASL_LEVEL_DEBUG) return ASL_STRING_DEBUG; + return "Unknown"; +} + +int +procinfo(char *pname, int *pid, int *uid) +{ + int mib[4]; + int i, status, nprocs; + size_t miblen, size; + struct kinfo_proc *procs, *newprocs; + + size = 0; + procs = NULL; + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_ALL; + mib[3] = 0; + miblen = 3; + + status = sysctl(mib, miblen, NULL, &size, NULL, 0); + do + { + size += size / 10; + newprocs = realloc(procs, size); + if (newprocs == 0) + { + if (procs != NULL) free(procs); + return PROC_NOT_FOUND; + } + + procs = newprocs; + status = sysctl(mib, miblen, procs, &size, NULL, 0); + } while ((status == -1) && (errno == ENOMEM)); + + if (status == -1) + { + if (procs != NULL) free(procs); + return PROC_NOT_FOUND; + } + + if (size % sizeof(struct kinfo_proc) != 0) + { + if (procs != NULL) free(procs); + return PROC_NOT_FOUND; + } + + if (procs == NULL) return PROC_NOT_FOUND; + + nprocs = size / sizeof(struct kinfo_proc); + + if (pname == NULL) + { + /* Search for a pid */ + for (i = 0; i < nprocs; i++) + { + if (*pid == procs[i].kp_proc.p_pid) + { + *uid = procs[i].kp_eproc.e_ucred.cr_uid; + return 0; + } + } + + return PROC_NOT_FOUND; + } + + *pid = PROC_NOT_FOUND; + + for (i = 0; i < nprocs; i++) + { + if (!strcmp(procs[i].kp_proc.p_comm, pname)) + { + if (*pid != PROC_NOT_FOUND) + { + free(procs); + return PROC_NOT_UNIQUE; + } + + *pid = procs[i].kp_proc.p_pid; + *uid = procs[i].kp_eproc.e_ucred.cr_uid; + } + } + + free(procs); + if (*pid == PROC_NOT_FOUND) return PROC_NOT_FOUND; + + return 0; +} + +int +rcontrol_get_string(const char *prefix, int pid, int *val) +{ + int t, x, status; + char *name; + + status = NOTIFY_STATUS_OK; + + if (pid == RC_SYSLOGD) + { + status = notify_register_plain(NOTIFY_SYSTEM_ASL_FILTER, &t); + } + else if (pid == RC_MASTER) + { + status = notify_register_plain(NOTIFY_SYSTEM_MASTER, &t); + } + else + { + name = NULL; + asprintf(&name, "%s.%d", prefix, pid); + if (name == NULL) return NOTIFY_STATUS_FAILED; + + status = notify_register_plain(name, &t); + free(name); + } + + if (status != NOTIFY_STATUS_OK) return status; + + x = 0; + status = notify_get_state(t, &x); + notify_cancel(t); + + *val = x; + + return status; +} + +int +rcontrol_set_string(const char *prefix, int pid, int filter) +{ + int t, status; + char *name; + + status = NOTIFY_STATUS_OK; + + if (pid == RC_SYSLOGD) + { + status = notify_register_plain(NOTIFY_SYSTEM_ASL_FILTER, &t); + } + else if (pid == RC_MASTER) + { + status = notify_register_plain(NOTIFY_SYSTEM_MASTER, &t); + } + else + { + name = NULL; + asprintf(&name, "%s.%d", prefix, pid); + if (name == NULL) return NOTIFY_STATUS_FAILED; + + status = notify_register_plain(name, &t); + free(name); + } + + if (status != NOTIFY_STATUS_OK) return status; + status = notify_set_state(t, filter); + if ((pid == RC_SYSLOGD) && (status == NOTIFY_STATUS_OK)) status = notify_post(NOTIFY_SYSTEM_ASL_FILTER); + notify_cancel(t); + return status; +} + +int +asl_string_to_filter(char *s) +{ + int f, i; + + if (s == NULL) return 0; + if (s[0] == '\0') return 0; + + if ((s[0] >= '0') && (s[0] <= '9')) return ASL_FILTER_MASK(atoi(s)); + + if (s[0] == '-') + { + if ((s[1] == 'P') || (s[1] == 'p')) i = ASL_LEVEL_EMERG; + else if ((s[1] == 'A') || (s[1] == 'a')) i = ASL_LEVEL_ALERT; + else if ((s[1] == 'C') || (s[1] == 'c')) i = ASL_LEVEL_CRIT; + else if ((s[1] == 'E') || (s[1] == 'e')) i = ASL_LEVEL_ERR; + else if ((s[1] == 'X') || (s[1] == 'x')) i = ASL_LEVEL_ERR; + else if ((s[1] == 'W') || (s[1] == 'w')) i = ASL_LEVEL_WARNING; + else if ((s[1] == 'N') || (s[1] == 'n')) i = ASL_LEVEL_NOTICE; + else if ((s[1] == 'I') || (s[1] == 'i')) i = ASL_LEVEL_INFO; + else if ((s[1] == 'D') || (s[1] == 'd')) i = ASL_LEVEL_DEBUG; + else i = atoi(s + 1); + f = ASL_FILTER_MASK_UPTO(i); + return f; + } + + f = 0; + for (i = 0; s[i] != '\0'; i++) + { + if ((s[i] == 'P') || (s[i] == 'p')) f |= ASL_FILTER_MASK_EMERG; + else if ((s[i] == 'A') || (s[i] == 'a')) f |= ASL_FILTER_MASK_ALERT; + else if ((s[i] == 'C') || (s[i] == 'c')) f |= ASL_FILTER_MASK_CRIT; + else if ((s[i] == 'E') || (s[i] == 'e')) f |= ASL_FILTER_MASK_ERR; + else if ((s[i] == 'X') || (s[i] == 'x')) f |= ASL_FILTER_MASK_ERR; + else if ((s[i] == 'W') || (s[i] == 'w')) f |= ASL_FILTER_MASK_WARNING; + else if ((s[i] == 'N') || (s[i] == 'n')) f |= ASL_FILTER_MASK_NOTICE; + else if ((s[i] == 'I') || (s[i] == 'i')) f |= ASL_FILTER_MASK_INFO; + else if ((s[i] == 'D') || (s[i] == 'd')) f |= ASL_FILTER_MASK_DEBUG; + } + + return f; +} + +char * +asl_filter_string(int f) +{ + static char str[1024]; + int i; + + memset(str, 0, sizeof(str)); + i = 0; + + if ((f == ASL_FILTER_MASK_PACEWNID) != 0) + { + strcat(str, "Emergency - Debug"); + return str; + } + + if ((f == ASL_FILTER_MASK_PACEWNI) != 0) + { + strcat(str, "Emergency - Info"); + return str; + } + + if ((f == ASL_FILTER_MASK_PACEWN) != 0) + { + strcat(str, "Emergency - Notice"); + return str; + } + + if ((f == ASL_FILTER_MASK_PACEW) != 0) + { + strcat(str, "Emergency - Warning"); + return str; + } + + if ((f == ASL_FILTER_MASK_PACE) != 0) + { + strcat(str, "Emergency - Error"); + return str; + } + + if ((f == ASL_FILTER_MASK_PAC) != 0) + { + strcat(str, "Emergency - Critical"); + return str; + } + + if ((f & ASL_FILTER_MASK_EMERG) != 0) + { + strcat(str, "Emergency"); + i++; + } + + if ((f & ASL_FILTER_MASK_ALERT) != 0) + { + if (i > 0) strcat(str, ", "); + strcat(str, "Alert"); + i++; + } + + if ((f & ASL_FILTER_MASK_CRIT) != 0) + { + if (i > 0) strcat(str, ", "); + strcat(str, "Critical"); + i++; + } + + if ((f & ASL_FILTER_MASK_ERR) != 0) + { + if (i > 0) strcat(str, ", "); + strcat(str, "Error"); + i++; + } + + if ((f & ASL_FILTER_MASK_WARNING) != 0) + { + if (i > 0) strcat(str, ", "); + strcat(str, "Warning"); + i++; + } + + if ((f & ASL_FILTER_MASK_NOTICE) != 0) + { + if (i > 0) strcat(str, ", "); + strcat(str, "Notice"); + i++; + } + + if ((f & ASL_FILTER_MASK_INFO) != 0) + { + if (i > 0) strcat(str, ", "); + strcat(str, "Info"); + i++; + } + + if ((f & ASL_FILTER_MASK_DEBUG) != 0) + { + if (i > 0) strcat(str, ", "); + strcat(str, "Debug"); + i++; + } + + if (i == 0) sprintf(str, "Off"); + + return str; +} + +int +rcontrol_get(const char *prefix, int pid) +{ + int filter, status; + const char *name; + + filter = 0; + + if (pid < 0) + { + name = "Master"; + if (pid == RC_SYSLOGD) name = "ASL Data Store"; + + status = rcontrol_get_string(NULL, pid, &filter); + if (status == NOTIFY_STATUS_OK) + { + printf("%s filter mask: %s\n", name, asl_filter_string(filter)); + return 0; + } + + printf("Unable to determine %s filter mask\n", name); + return -1; + } + + status = rcontrol_get_string(prefix, pid, &filter); + if (status == NOTIFY_STATUS_OK) + { + printf("Process %d syslog filter mask: %s\n", pid, asl_filter_string(filter)); + return 0; + } + + printf("Unable to determine syslog filter mask for pid %d\n", pid); + return -1; +} + +int +rcontrol_set(const char *prefix, int pid, int filter) +{ + int status; + const char *name; + + if (pid < 0) + { + name = "Master"; + if (pid == RC_SYSLOGD) name = "ASL Data Store"; + status = rcontrol_set_string(NULL, pid, filter); + + if (status == NOTIFY_STATUS_OK) + { + printf("Set %s syslog filter mask: %s\n", name, asl_filter_string(filter)); + return 0; + } + + printf("Unable to set %s syslog filter mask: %s\n", name, notify_status_string(status)); + return -1; + } + + status = rcontrol_set_string(prefix, pid, filter); + if (status == NOTIFY_STATUS_OK) + { + printf("Set process %d syslog filter mask set: %s\n", pid, asl_filter_string(filter)); + return 0; + } + + printf("Unable to set syslog filter mask for pid %d: %s\n", pid, notify_status_string(status)); + return -1; +} + +int +rsend(aslmsg msg, char *rhost) +{ + char *str, *out; + uint32_t len, level; + char *timestr; + const char *val; + time_t tick; + struct tm gtime; + int s; + struct sockaddr_in dst; + struct hostent *h; + char myname[MAXHOSTNAMELEN + 1]; + + if (msg == NULL) return 0; + + h = gethostbyname(rhost); + if (h == NULL) return -1; + + s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (s <= 0) return -1; + + memset(&dst, 0, sizeof(struct sockaddr_in)); + memcpy(&(dst.sin_addr.s_addr), h->h_addr_list[0], 4); + dst.sin_family = AF_INET; + dst.sin_port = 514; + dst.sin_len = sizeof(struct sockaddr_in); + + level = ASL_LEVEL_DEBUG; + + val = asl_get(msg, ASL_KEY_LEVEL); + if (val != NULL) level = atoi(val); + + memset(>ime, 0, sizeof(struct tm)); + timestr = NULL; + + tick = time(NULL); + gmtime_r(&tick, >ime); + + /* Canonical form: YYYY.MM.DD hh:mm:ss UTC */ + asprintf(×tr, "%d.%02d.%02d %02d:%02d:%02d UTC", gtime.tm_year + 1900, gtime.tm_mon + 1, gtime.tm_mday, gtime.tm_hour, gtime.tm_min, gtime.tm_sec); + + if (timestr != NULL) + { + asl_set(msg, ASL_KEY_TIME, timestr); + free(timestr); + } + + if (gethostname(myname, MAXHOSTNAMELEN) == 0) asl_set(msg, ASL_KEY_HOST, myname); + + len = 0; + str = asl_msg_to_string(msg, &len); + if (str == NULL) return -1; + + asprintf(&out, "%10u %s\n", len+1, str); + free(str); + if (out == NULL) return -1; + + sendto(s, out, len+12, 0, (const struct sockaddr *)&dst, sizeof(struct sockaddr_in)); + + free(out); + close(s); + return 0; +} + +int +rlegacy(char *msg, int level, char *rhost) +{ + char *out; + uint32_t len; + time_t tick; + char *ltime; + int s; + struct sockaddr_in dst; + struct hostent *h; + char myname[MAXHOSTNAMELEN + 1]; + + if (msg == NULL) return 0; + + h = gethostbyname(rhost); + if (h == NULL) return -1; + + s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (s <= 0) return -1; + + memset(&dst, 0, sizeof(struct sockaddr_in)); + memcpy(&(dst.sin_addr.s_addr), h->h_addr_list[0], 4); + dst.sin_family = AF_INET; + dst.sin_port = 514; + dst.sin_len = sizeof(struct sockaddr_in); + + tick = time(NULL); + ltime = ctime(&tick); + ltime[19] = '\0'; + + gethostname(myname, MAXHOSTNAMELEN); + + asprintf(&out, "<%d>%s %s syslog[%d]: %s", level, ltime+4, myname, getpid(), msg); + len = strlen(out); + sendto(s, out, len, 0, (const struct sockaddr *)&dst, sizeof(struct sockaddr_in)); + + free(out); + close(s); + return 0; +} + +static int +_isanumber(char *s) +{ + int i; + + if (s == NULL) return 0; + + i = 0; + if ((s[0] == '-') || (s[0] == '+')) i = 1; + + if (s[i] == '\0') return 0; + + for (; s[i] != '\0'; i++) + { + if (!isdigit(s[i])) return 0; + } + + return 1; +} + +int +asl_string_to_level(const char *s) +{ + if (s == NULL) return -1; + + if ((s[0] >= '0') && (s[0] <= '7') && (s[1] == '\0')) return atoi(s); + + if (!strncasecmp(s, "em", 2)) return ASL_LEVEL_EMERG; + else if (!strncasecmp(s, "p", 1)) return ASL_LEVEL_EMERG; + else if (!strncasecmp(s, "a", 1)) return ASL_LEVEL_ALERT; + else if (!strncasecmp(s, "c", 1)) return ASL_LEVEL_CRIT; + else if (!strncasecmp(s, "er", 2)) return ASL_LEVEL_ERR; + else if (!strncasecmp(s, "x", 1)) return ASL_LEVEL_ERR; + else if (!strncasecmp(s, "w", 1)) return ASL_LEVEL_WARNING; + else if (!strncasecmp(s, "n", 1)) return ASL_LEVEL_NOTICE; + else if (!strncasecmp(s, "i", 1)) return ASL_LEVEL_INFO; + else if (!strncasecmp(s, "d", 1)) return ASL_LEVEL_DEBUG; + + return -1; +} + +int +syslog_remote_control(int argc, char *argv[]) +{ + int pid, uid, status, mask; + const char *prefix; + + if ((argc < 3) || (argc > 4)) + { + fprintf(stderr, "usage:\n"); + fprintf(stderr, "%s -c process [mask]\n", myname); + fprintf(stderr, " get (set if mask is specified) syslog filter mask for process (pid or name)\n"); + fprintf(stderr, " process may be pid or process name\n"); + fprintf(stderr, " use \"-c 0\" to get master syslog filter mask\n"); + fprintf(stderr, " use \"-c 0 off\" to disable master syslog filter mask\n"); + fprintf(stderr, "\n"); + return -1; + } + + pid = RC_MASTER; + uid = -2; + + status = PROC_NOT_FOUND; + + if ((!strcmp(argv[2], "syslogd")) || (!strcmp(argv[2], "syslog"))) + { + pid = RC_SYSLOGD; + uid = 0; + status = 0; + } + else if (_isanumber(argv[2]) != 0) + { + pid = atoi(argv[2]); + status = procinfo(NULL, &pid, &uid); + } + else + { + status = procinfo(argv[2], &pid, &uid); + } + + if (status == PROC_NOT_FOUND) + { + fprintf(stderr, "%s: process not found\n", argv[2]); + return -1; + } + + if (status == PROC_NOT_UNIQUE) + { + fprintf(stderr, "%s: multiple processes found\n", argv[2]); + fprintf(stderr, "use pid to identify a process uniquely\n"); + return -1; + } + + if (pid == 0) pid = RC_MASTER; + + prefix = NOTIFY_PREFIX_USER; + if (uid == 0) prefix = NOTIFY_PREFIX_SYSTEM; + + if (argc == 4) + { + if ((pid == RC_MASTER) && (!strcasecmp(argv[3], "off"))) mask = 0; + else if ((pid == RC_SYSLOGD) && (!strcasecmp(argv[3], "off"))) mask = 0; + else + { + mask = asl_string_to_filter(argv[3]); + if (mask < 0) + { + printf("unknown syslog mask: %s\n", argv[3]); + return -1; + } + } + + rcontrol_set(prefix, pid, mask); + } + else + { + rcontrol_get(prefix, pid); + } + + return 0; +} + +int +syslog_send(int argc, char *argv[]) +{ + int i, start, kv, len, rfmt, rlevel; + aslclient asl; + aslmsg m; + char tmp[64], *str, *rhost; + + kv = 0; + rhost = NULL; + rfmt = SEND_FORMAT_ASL; + start = 1; + rlevel = 7; + + for (i = 1; i < argc; i++) + { + if (!strcmp(argv[i], "-s")) start = i+1; + else if (!strcmp(argv[i], "-k")) kv = 1; + else if (!strcmp(argv[i], "-r")) + { + rhost = argv[++i]; + start = i+1; + rfmt = SEND_FORMAT_LEGACY; + } + else if (!strcmp(argv[i], "-R")) + { + rhost = argv[++i]; + start = i+1; + } + else if (!strcmp(argv[i], "-l")) + { + rlevel = asl_string_to_level(argv[++i]); + if (rlevel < 0) + { + fprintf(stderr, "Unknown level: %s\n", argv[i]); + return(-1); + } + start = i+1; + } + } + + asl = asl_open(myname, "syslog", 0); + asl_set_filter(asl, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG)); + + m = asl_new(ASL_TYPE_MSG); + asl_set(m, ASL_KEY_SENDER, myname); + + sprintf(tmp, "%d", rlevel); + asl_set(m, ASL_KEY_LEVEL, tmp); + + str = NULL; + + if (kv == 0) + { + len = 0; + for (i = start; i < argc; i++) len += (strlen(argv[i]) + 1); + str = calloc(len + 1, 1); + for (i = start; i < argc; i++) + { + strcat(str, argv[i]); + if ((i+1) < argc) strcat(str, " "); + } + asl_set(m, ASL_KEY_MSG, str); + } + else + { + for (i = start + 1; i < argc; i += 2) asl_set(m, argv[i], argv[i + 1]); + } + + if (rhost == NULL) + { + asl_send(asl, m); + } + else if (rfmt == SEND_FORMAT_ASL) + { + rsend(m, rhost); + } + else if ((rfmt == SEND_FORMAT_LEGACY) && (str != NULL)) + { + rlegacy(str, rlevel, rhost); + } + + asl_free(m); + + if (str != NULL) free(str); + + asl_close(asl); + + return 0; +} + +static void +printmsg(FILE *f, asl_msg_t *msg, char *mstr, char *fmt, int pflags) +{ + char *k, *t, c; + const char *v; + int i, j, l, paren, oval; + time_t tick; + + if ((pflags & PRINT_STD_FMT) || (pflags & PRINT_LEGACY_FMT)) + { + /* LEGACY: Mth dd hh:mm:ss host sender[pid]: message */ + /* STD: Mth dd hh:mm:ss host sender[pid] : 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); +} -- 2.45.2