return 1;
}
- top = s->string_count - 1;
+ range = top = s->string_count - 1;
bot = 0;
mid = top / 2;
- range = top - bot;
while (range > 1)
{
ms = (mem_string_t *)s->string_cache[mid];
if (s == NULL) return ASL_STATUS_INVALID_STORE;
if (res == NULL) return ASL_STATUS_INVALID_ARG;
- do_match = 1;
qp = NULL;
qtype = NULL;
rescount = 0;
uint32_t asl_memory_match(asl_memory_t *s, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction, int32_t ruid, int32_t rgid);
-#endif __ASL_MEMORY_H__
+#endif /* __ASL_MEMORY_H__ */
#include <asl_core.h>
#include "asl_mini_memory.h"
-#include <unistd.h>x
+#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/errno.h>
return 1;
}
- top = s->string_count - 1;
+ range = top = s->string_count - 1;
bot = 0;
mid = top / 2;
- range = top - bot;
while (range > 1)
{
ms = (mini_mem_string_t *)s->string_cache[mid];
r->level = ASL_LEVEL_DEBUG;
r->pid = -1;
r->time = (uint64_t)-1;
+ r->nano = (uint32_t)-1;
key = NULL;
val = NULL;
{
if (val != NULL) r->time = asl_parse_time(val);
}
+ else if (!strcmp(key, ASL_KEY_TIME_NSEC))
+ {
+ if (val != NULL) r->nano = atoi(val);
+ }
else if (!strcmp(key, ASL_KEY_SENDER))
{
if (val != NULL) r->sender = asl_mini_memory_string_retain(s, val, 1);
asl_set(msg, ASL_KEY_TIME, tmp);
}
+ /* Nanoseconds */
+ if (r->nano != (uint32_t)-1)
+ {
+ snprintf(tmp, sizeof(tmp), "%u", r->nano);
+ asl_set(msg, ASL_KEY_TIME_NSEC, tmp);
+ }
+
/* Sender */
if (r->sender != NULL)
{
*type |= ASL_QUERY_MATCH_TIME;
out->time = asl_parse_time(val);
}
+ else if (!strcmp(key, ASL_KEY_TIME_NSEC))
+ {
+ if (val == NULL) continue;
+
+ if (*type & ASL_QUERY_MATCH_NANO)
+ {
+ asl_mini_memory_record_free(s, out);
+ *type = ASL_QUERY_MATCH_SLOW;
+ return NULL;
+ }
+
+ *type |= ASL_QUERY_MATCH_NANO;
+ out->nano = atoll(val);
+ }
else if (!strcmp(key, ASL_KEY_LEVEL))
{
if (val == NULL) continue;
if ((qtype & ASL_QUERY_MATCH_MSG_ID) && (q->mid != r->mid)) return 0;
if ((qtype & ASL_QUERY_MATCH_TIME) && (q->time != r->time)) return 0;
+ if ((qtype & ASL_QUERY_MATCH_NANO) && (q->nano != r->nano)) return 0;
if ((qtype & ASL_QUERY_MATCH_LEVEL) && (q->level != r->level)) return 0;
if ((qtype & ASL_QUERY_MATCH_PID) && (q->pid != r->pid)) return 0;
if ((qtype & ASL_QUERY_MATCH_SENDER) && (q->sender != r->sender)) return 0;
if (s == NULL) return ASL_STATUS_INVALID_STORE;
if (res == NULL) return ASL_STATUS_INVALID_ARG;
- do_match = 1;
qp = NULL;
qtype = NULL;
rescount = 0;
/*
- * Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2007-2011 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* @APPLE_LICENSE_HEADER_END@
*/
-#ifndef __asl_mini_memory_H__
-#define __asl_mini_memory_H__
+#ifndef __ASL_MINI_MEMORY_H__
+#define __ASL_MINI_MEMORY_H__
#include <stdint.h>
#include <asl.h>
{
uint32_t mid;
uint64_t time;
+ uint32_t nano;
uint8_t level;
uint8_t flags;
uint32_t pid;
uint32_t asl_mini_memory_match(asl_mini_memory_t *s, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction);
-#endif __asl_mini_memory_H__
+#endif /* __ASL_MINI_MEMORY_H__ */
memcpy(s, out, len);
+ if (s[len - 1] != '\n') len++;
s[len - 1] = '\0';
return s;
}
archiveVersion = 1;
classes = {
};
- objectVersion = 45;
+ objectVersion = 46;
objects = {
/* Begin PBXAggregateTarget section */
5020A3821098EEFD00982ED6 /* System.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5020A3811098EEFD00982ED6 /* System.framework */; };
5039176F1091408B0001165E /* aslmanager.c in Sources */ = {isa = PBXBuildFile; fileRef = 5039176C1091408B0001165E /* aslmanager.c */; };
503917B81091410E0001165E /* asl_action.c in Sources */ = {isa = PBXBuildFile; fileRef = 503917A61091410E0001165E /* asl_action.c */; };
- 503917B91091410E0001165E /* asl_in.c in Sources */ = {isa = PBXBuildFile; fileRef = 503917A71091410E0001165E /* asl_in.c */; };
503917BA1091410E0001165E /* bb_convert.c in Sources */ = {isa = PBXBuildFile; fileRef = 503917A91091410E0001165E /* bb_convert.c */; };
503917BB1091410E0001165E /* bsd_in.c in Sources */ = {isa = PBXBuildFile; fileRef = 503917AA1091410E0001165E /* bsd_in.c */; };
503917BC1091410E0001165E /* bsd_out.c in Sources */ = {isa = PBXBuildFile; fileRef = 503917AB1091410E0001165E /* bsd_out.c */; };
5039176D1091408B0001165E /* com.apple.aslmanager.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = com.apple.aslmanager.plist; path = aslmanager.tproj/com.apple.aslmanager.plist; sourceTree = "<group>"; };
5039177C109140C30001165E /* syslogd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = syslogd; sourceTree = BUILT_PRODUCTS_DIR; };
503917A61091410E0001165E /* asl_action.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = asl_action.c; path = syslogd.tproj/asl_action.c; sourceTree = "<group>"; };
- 503917A71091410E0001165E /* asl_in.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = asl_in.c; path = syslogd.tproj/asl_in.c; sourceTree = "<group>"; };
503917A81091410E0001165E /* asl.conf.5 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = asl.conf.5; path = syslogd.tproj/asl.conf.5; sourceTree = "<group>"; };
503917A91091410E0001165E /* bb_convert.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = bb_convert.c; path = syslogd.tproj/bb_convert.c; sourceTree = "<group>"; };
503917AA1091410E0001165E /* bsd_in.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = bsd_in.c; path = syslogd.tproj/bsd_in.c; sourceTree = "<group>"; };
children = (
503917A61091410E0001165E /* asl_action.c */,
2DB4DA0A125FC69A001CDC45 /* after_install.sh */,
- 503917A71091410E0001165E /* asl_in.c */,
503917A81091410E0001165E /* asl.conf.5 */,
503917A91091410E0001165E /* bb_convert.c */,
503917AA1091410E0001165E /* bsd_in.c */,
/* Begin PBXProject section */
08FB7793FE84155DC02AAC07 /* Project object */ = {
isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0440;
+ };
buildConfigurationList = 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "syslog" */;
- compatibilityVersion = "Xcode 3.1";
+ compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 1;
knownRegions = (
buildActionMask = 2147483647;
files = (
503917B81091410E0001165E /* asl_action.c in Sources */,
- 503917B91091410E0001165E /* asl_in.c in Sources */,
503917BA1091410E0001165E /* bb_convert.c in Sources */,
503917BB1091410E0001165E /* bsd_in.c in Sources */,
503917BC1091410E0001165E /* bsd_out.c in Sources */,
GCC_PREFIX_HEADER = "";
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- PREBINDING = NO;
USE_HEADERMAP = NO;
VERSIONING_SYSTEM = "apple-generic";
ZERO_LINK = NO;
buildSettings = {
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
- GCC_ENABLE_FIX_AND_CONTINUE = NO;
PRODUCT_NAME = All;
ZERO_LINK = NO;
};
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_DYNAMIC_NO_PIC = NO;
- GCC_ENABLE_FIX_AND_CONTINUE = NO;
INSTALL_PATH = /usr/sbin;
OTHER_CFLAGS = (
"-Wall",
"-DINET6",
);
- PREBINDING = NO;
PRODUCT_NAME = aslmanager;
ZERO_LINK = NO;
};
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_DYNAMIC_NO_PIC = NO;
- GCC_ENABLE_FIX_AND_CONTINUE = NO;
HEADER_SEARCH_PATHS = (
"$(BUILT_PRODUCTS_DIR)/$(CURRENT_ARCH)",
"$(PROJECT_DIR)/aslcommon",
"-DCONFIG_MAC",
"-DREMOTE_IPV4",
);
- PREBINDING = NO;
PRODUCT_NAME = syslogd;
ZERO_LINK = NO;
};
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_DYNAMIC_NO_PIC = NO;
- GCC_ENABLE_FIX_AND_CONTINUE = NO;
HEADER_SEARCH_PATHS = (
"$(BUILT_PRODUCTS_DIR)/$(CURRENT_ARCH)",
"$(PROJECT_DIR)/aslcommon",
"$(OTHER_CFLAGS)",
"-DCONFIG_IPHONE",
);
- PREBINDING = NO;
PRODUCT_NAME = syslog;
ZERO_LINK = NO;
};
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
- GCC_ENABLE_FIX_AND_CONTINUE = NO;
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/aslcommon";
INSTALL_PATH = /usr/local/lib;
OTHER_CFLAGS = (
"-Wall",
"-D__MigTypeCheck=1",
);
- PREBINDING = NO;
PRODUCT_NAME = aslcommon;
ZERO_LINK = NO;
};
#! /bin/bash
+set -e
install -d -m 0755 -o root -g wheel "$DSTROOT"/private/var/log/asl
-PRODUCT=$(xcodebuild -sdk ${SDKROOT} -version PlatformPath | head -1 | sed 's,^.*/\([^/]*\)\.platform$,\1,')
+PRODUCT=$(xcodebuild -sdk "${SDKROOT}" -version PlatformPath | head -1 | sed 's,^.*/\([^/]*\)\.platform$,\1,')
+
+if [ ${SDKROOT}x = x ]; then
+ PRODUCT=MacOSX
+fi
+
if [ ${PRODUCT}x = x ]; then
- PRODUCT=MacOSX
+ PRODUCT=MacOSX
fi
if [ ${PRODUCT} = iPhone ]; then
DESTDIR="$DSTROOT"/System/Library/LaunchDaemons
install -d -m 0755 -o root -g wheel "$DESTDIR"
install -m 0644 -o root -g wheel "$SRCROOT"/syslogd.tproj/com.apple.syslogd.plist "$DESTDIR"
+if [ ${PRODUCT} = iPhoneOS ]; then
+ /usr/libexec/PlistBuddy \
+ -c "Add :POSIXSpawnType string Interactive" \
+ "$DESTDIR"/com.apple.syslogd.plist
+fi
plutil -convert binary1 "$DESTDIR"/com.apple.syslogd.plist
mkfile 8 "$DSTROOT"/private/var/log/asl/SweepStore
-.\"Copyright (c) 2004-2009 Apple Inc. All rights reserved.
+.\"Copyright (c) 2004-2011 Apple Inc. All rights reserved.
.\"
.\"@APPLE_LICENSE_HEADER_START@
.\"
.Nm syslogd
receives messages that match the query pattern.
.Pp
-Parameter setting lines in the configuration file begin with an equal sign ("="),
+Parameter setting lines in the configuration file begin with an equal sign
+.Dq = ,
and are generally of the form:
.Pp
.Dl = parameter_name value ...
Most parameter settings require a single value, although some may take several values.
See the PARAMETER SETTINGS section below for details.
.Pp
-Query-action rules in the file begin with a question-mark ("?") or a "Q", and generally have the form:
+Query-action rules in the file begin with a question-mark ``?'' or a
+.Dq Q ,
+and generally have the form:
.Pp
.Dl ? query action ...
.Pp
.It debug
Enables or disables internal debugging output.
This is probably of little interest to most users.
-The debug parameter requires a value of "1" to enable debug output, or a value of "0" to disable it.
-An option file name may follow the "0" or "1".
+The debug parameter requires a value of
+.Dq 1
+to enable debug output, or a value of
+.Dq 0
+to disable it.
+An option file name may follow the
+.Dq 0
+or
+.Dq 1 .
If a file name is provided, debug messages are written to that file.
Otherwise, debug writes are treated as log messages.
.Pp
-.It cutoff
-Sets the ASL data store cutoff level, given as an integer in the range 0 to 7 as an argument.
-The cutoff level is 7 by default, allowing any message that matches a "store" action
-(see QUERY-ACTION RULES below) to be saved.
-Setting the cutoff to a lower value will prevent messages with log priority levels numerically
-greater that the specified cutoff from being saved in the ASL data store.
-.Pp
.It mark_time
Sets the time interval for the mark facility.
The default is 0 seconds, which indicates that mark messages are not generated.
.Pp
.It dup_delay
-Sets the maximum time that the bsd_out module will allow before writing a "last message repeated <N> times"
-message in a log file specified in /etc/syslog.conf.
+Sets the maximum time before writing a
+.Dq "last message repeated <N> times"
+message in a log file when duplicate messages have been detected.
The default is 30 seconds.
.Pp
.It utmp_ttl
Sets the time-to-live for messages used by the utmp, wtmp, and lastlog subsystems.
The default is 31622400 seconds (approximately 1 year).
.Pp
-.It fs_ttl
-Sets the time-to-live for filesystem error messages generated by the kernel.
-The default is 31622400 seconds (approximately 1 year).
-.Pp
.It mps_limit
Sets the per-process message per second quota.
The default is value is 500.
.Pp
.Dl [= Sender foobar]
.Pp
-matches any message with key="Sender" and val="foobar".
+matches any message with value
+.Dq foobar
+for the
+.Dq Sender
+key.
The query
.Pp
.Dl [CA= Color gr]
.Pp
-matches any message with key=Color and val beginning with the letters GR, Gr, gr, or gR
-(C meaning casefold, A meaning prefix).
+matches any message with a value beginning with the letters GR, Gr, gr, or gR
+(C meaning casefold, A meaning prefix) for the
+.Dq Color
+key.
The example query above,
.Pp
.Dl [= Sender foobar] [N< Level 3]
.Pp
-matches any message from "foobar" with a level numerically less than 3
+matches any message from
+.Dq foobar
+with a level numerically less than 3
(string values are converted to integers, and the comparison is done on the integer values).
Note that the string values may be used equivalently for the Level key,
so the example above may also be written as:
.Pp
.Dl [= Sender foobar] [< Level Error]
.Pp
-String values for levels may be any of the set "emergency", "alert", "critical", "error",
-"warning", "notice", "info", or "debug". These strings may be upper, lower, or mixed case.
+String values for levels may be any of the set
+.Dq emergency ,
+.Dq alert ,
+.Dq critical ,
+.Dq error ,
+.Dq warning ,
+.Dq notice ,
+.Dq info , or
+.Dq debug .
+These strings may be upper, lower, or mixed case.
.Pp
-The "T" operator is useful to test for the presence of a particular key.
+The
+.Dq T
+operator is useful to test for the presence of a particular key.
.Pp
.Dl [T Flavor]
.Pp
-Will match any message that has a "Flavor" key, regardless of its value.
+Will match any message that has a
+.Dq Flavor
+key, regardless of its value.
.Pp
.Ss Actions
The following actions are available.
.Nm syslogd
to post a notification with
.Fn notify_post .
-The notification key must appear as a single parameter following the "notify" action.
+The notification key must appear as a single parameter following the
+.Dq notify
+action.
.Pp
.It access
Sets read access controls for messages that match the associated query pattern.
.Nm syslogd
will restrict read access to matching messages to a specific user and group.
-The user ID number and group ID number must follow the "access" keyword as parameters.
+The user ID number and group ID number must follow the
+.Dq access
+keyword as parameters.
.Pp
.It store
Causes
command line utility.
A new file will be created if one does not exist.
If a new file is being created, the UID, GID, and mode of the file may be specified using the options
-"uid=UUU", "gid=GGG", and "mode=MMMM", where UUU and GGG are a user ID and group ID, and MMMM is a
-mode specification of the form "0644" (for an octal number) or DDD for a decimal number.
+.Dq uid=UUU ,
+.Dq gid=GGG ,
+and
+.Dq mode=MMMM ,
+where UUU and GGG are a user ID and group ID, and MMMM is a
+mode specification of the form
+.Dq 0644
+with a leading zero for an octal number or DDD for a decimal number.
.Pp
Two other optional parameters may also follow the pathname.
.Pp
If a separate log message data store file is specified as a parameter, then
.Nm syslogd
will open the database, save a matching message, and then close the database.
-If a high volume of messages is expected, specifying the "stayopen" option will improve performance.
+If a high volume of messages is expected, specifying the
+.Dq stayopen
+option will improve performance.
.Pp
Also, if a separate log message data store file is specified as a parameter,
matching messages will be excluded from all further processing.
-Adding the "continue" option will cause syslogd to save matching messages in the specified store file
+Adding the
+.Dq continue
+option will cause syslogd to save matching messages in the specified store file
and then continue processing matching messages in accordance with the actions
specified in /etc/asl.conf and /etc/syslog.conf.
.Pp
.Nm asl.conf
configuration file contains no matching rules for the main ASL data store, then
.Nm syslogd
-will save all messages, subject to filtering in accordance with the log cutoff level.
+will save all messages.
.Pp
.It store_directory
Causes matching messages to be stored in a log message data store file in a separate directory.
.Nm syslogd
will not create the directory path.
.Pp
-Messages saved to a store directory are saved in files that are named "yyyy.mm.dd.asl",
-where "yyyy", "mm", and "dd" are the year, month (01 to 12) and day of the month (01 to 31) associated with
+Messages saved to a store directory are saved in files that are named
+.Dq yyyy.mm.dd.asl ,
+where
+.Dq yyyy ,
+.Dq mm ,
+and
+.Dq dd
+are the year, month (01 to 12) and day of the month (01 to 31) associated with
matching messages.
This has the effect of saving messages in a separate file for each day.
.Pp
-The "uid=UUU", "gid=GGG", "mode=MMMM", and "continue" options available for the "store" action
+The
+.Dq uid=UUU ,
+.Dq gid=GGG ,
+.Dq mode=MMMM ,
+and
+.Dq continue
+options available for the
+.Dq store
+action
may also be specified for a store directory.
The uid, gid, and mode specification will be used when the individual daily store files are created.
.Pp
+.It file
+Causes matching messages to be stored in a log file.
+The file's path name must follow as the first parameter.
+The file's directory must exist.
+If the path already exists, it must be a plain file.
+Otherwise
+.Nm syslogd
+will create the file.
+The file's owner will be root, and the file's group will be admin.
+A file mode may be specified as an option of the form
+.Dq mode=MMMM
+as described above.
+One or more UIDs may be given as the values of options of the form
+.Dq uid=UUU .
+One or more GIDs may be given as the values of options of the form
+.Dq gid=GGG .
+If any UIDs or GIDs are provided, the specified users and groups will be given read access to the file.
+Note that UIDs and GIDs should be defined in the local Open Directory database, since
+.Nm syslogd
+starts and may create the log file before network directory services are available.
+Unknown UIDs and GIDs will be ignored when setting access controls.
+.Pp
+By default, log files will be written using the same format used for printing by
+.Nm syslog
+when the
+.Fl F Ar std
+flag is supplied.
+A print format may be specified as the value of the
+.Dq format=FMT
+option.
+The default is
+.Dq format=std .
+Alternate file formats, including
+.Dq bsd
+and
+.Dq raw
+are supported.
+Custom formats may be specified as well, using the syntax supported by
+.Nm syslog Fl F .
+Space and tab character in a custom format string must be escaped with a leading backslash character.
+Custom format strings may include variables of the form
+.Dq $Name
+.Dq $(Name)
+or
+.Dq $((Name)(fmt)) .
+which will be expanded to the associated with the named key.
+The first form may be used in most cases.
+The second form may be used if the variable is not delimited by whitespace.
+The third form permits the selection of alternate output formats for certain keys,
+such as Time and Level.
+See
+.Xr syslog 1
+for details.
+.Pp
+For example, the option:
+.Pp
+.Dl format=$((Time)(Z))\ $Host\ $(Sender)[$(PID)]\ <$((Level)(str))>:\ $Message
+.Pp
+produces output similar to the
+.Dq std
+format, but using the UTC (Zulu) timezone.
+.Pp
+By default, files printed using the
+.Dq bsd
+and
+.Dq std
+formats will suppress printing duplicates.
+If two or more messages are logged within 30 seconds, and which differ only in time,
+then the second and subsequent messages will not be printed.
+When a different message is logged, or 30 seconds have elapsed since the initial
+message was logged, a line with the text
+.Dl --- last message repeated N times ---
+will be added to the file.
+The default may be disabled using the
+.Dq no_dup_supress
+option.
+.Pp
.It broadcast
Causes syslogd to write the text of matching messages to all terminal windows.
-If optional text follows the "broadcast" keyword, then that text is written rather that the matching message text.
+If optional text follows the
+.Dq broadcast
+keyword, then that text is written rather that the matching message text.
.Pp
.It ignore
Causes a matching message to be ignored in all subsequent matching rules.
.It aslmanager_debug
Enables or disables internal debugging output.
This is probably of little interest to most users.
-The debug parameter requires a value of "1" to enable debug output, or a value of "0" to disable it.
+The debug parameter requires a value of
+.Dq 1
+to enable debug output, or a value of
+.Dq 0
+to disable it.
Debug messages are sent to
.Nm syslogd .
.Pp
.Pp
.It archive
Enables or disables archiving.
-The archive parameter requires a value of "1" to enable archiving, or a value of "0" to disable it.
-An option archive directory path may follow the "0" or "1".
+The archive parameter requires a value of
+.Dq 1
+to enable archiving, or a value of
+.Dq 0
+to disable it.
+An option archive directory path may follow the
+.Dq 0
+or
+.Dq 1 .
If enabled, files removed from the ASL data store are moved to the archive directory.
The default archive directory path is /var/log/asl.archive.
.Pp
* Copyright (c) 2004-2011 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
- *
+ *
* 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 2.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.opensource.apple.com/apsl/ 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,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
- *
+ *
* @APPLE_LICENSE_HEADER_END@
*/
#include <netdb.h>
#include <notify.h>
#include <pthread.h>
-#include <asl_core.h>
+#include <sys/acl.h>
+#include <membership.h>
#include "daemon.h"
+#include <dispatch/private.h>
#define _PATH_WALL "/usr/bin/wall"
#define _PATH_ASL_CONF "/etc/asl.conf"
#define MY_ID "asl_action"
+#define MAX_FAILURES 5
+
#define ACTION_NONE 0
#define ACTION_IGNORE 1
#define ACTION_NOTIFY 2
#define ACTION_BROADCAST 3
#define ACTION_ACCESS 4
-#define ACTION_STORE 5
-#define ACTION_STORE_DIR 6
-#define ACTION_FORWARD 7
+#define ACTION_ASL_STORE 5 /* Save in main ASL Database */
+#define ACTION_ASL_FILE 6 /* Save in an ASL format data file */
+#define ACTION_ASL_DIR 7 /* Save in an ASL directory */
+#define ACTION_FILE 8
+#define ACTION_FORWARD 9
#define forever for(;;)
+#define ACT_FLAG_HAS_LOGGED 0x80000000
+#define ACT_FLAG_CLEAR_LOGGED 0x7fffffff
+
#define ACT_STORE_FLAG_STAY_OPEN 0x00000001
#define ACT_STORE_FLAG_CONTINUE 0x00000002
-static asl_msg_t *query = NULL;
-static int reset = RESET_NONE;
-static pthread_mutex_t reset_lock = PTHREAD_MUTEX_INITIALIZER;
+#define ACT_FILE_FLAG_DUP_SUPRESS 0x00000001
+#define ACT_FILE_FLAG_ROTATE 0x00000002
+
+static dispatch_queue_t asl_action_queue;
+static time_t last_file_day;
typedef struct action_rule_s
{
uid_t uid;
gid_t gid;
uint64_t next_id;
+ uint32_t fails;
uint32_t flags;
+ uint32_t refcount;
uint32_t p_year;
uint32_t p_month;
uint32_t p_day;
};
+struct file_data
+{
+ int fd;
+ char *path;
+ char *fmt;
+ const char *tfmt;
+ mode_t mode;
+ uid_t *uid;
+ uint32_t nuid;
+ gid_t *gid;
+ uint32_t ngid;
+ size_t max_size;
+ uint32_t fails;
+ uint32_t flags;
+ uint32_t refcount;
+ time_t stamp;
+ uint32_t last_hash;
+ uint32_t last_count;
+ time_t last_time;
+ dispatch_source_t dup_timer;
+ char *last_msg;
+};
+
static action_rule_t *asl_action_rule = NULL;
static action_rule_t *asl_datastore_rule = NULL;
-static int filter_token = -1;
-int asl_action_close();
static int _parse_config_file(const char *);
extern void db_save_message(aslmsg m);
+/* forward */
+int _act_file_open(struct file_data *fdata);
+static void _act_file_init(action_rule_t *r);
+static void _act_store_init(action_rule_t *r);
+
static char *
_next_word(char **s)
{
return out;
}
-static void
-_do_reset(void)
-{
- pthread_mutex_lock(&reset_lock);
-
- if (reset == RESET_CONFIG)
- {
- asl_action_close();
- _parse_config_file(_PATH_ASL_CONF);
- }
-
- reset = RESET_NONE;
-
- pthread_mutex_unlock(&reset_lock);
-}
-
/*
* Config File format:
* Set parameter rule - initializes a parameter.
* = param args...
* Query rule - if a message matches the query, then the action is invoked.
* The rule may be identified by either "?" or "Q".
- * ? [k v] [k v] ... action args...
- * Q [k v] [k v] ... action args...
+ * ? [k v] [k v] ... action args...
+ * Q [k v] [k v] ... action args...
* Universal match rule - the action is invoked for all messages
* * action args...
*/
else if (!strcasecmp(act, "notify")) out->action = ACTION_NOTIFY;
else if (!strcasecmp(act, "broadcast")) out->action = ACTION_BROADCAST;
else if (!strcasecmp(act, "access")) out->action = ACTION_ACCESS;
- else if (!strcasecmp(act, "store")) out->action = ACTION_STORE;
- else if (!strcasecmp(act, "save")) out->action = ACTION_STORE;
- else if (!strcasecmp(act, "store_directory")) out->action = ACTION_STORE_DIR;
- else if (!strcasecmp(act, "store_dir")) out->action = ACTION_STORE_DIR;
+ else if (!strcasecmp(act, "store")) out->action = ACTION_ASL_STORE;
+ else if (!strcasecmp(act, "save")) out->action = ACTION_ASL_STORE;
+ else if (!strcasecmp(act, "store_file")) out->action = ACTION_ASL_FILE;
+ else if (!strcasecmp(act, "store_directory")) out->action = ACTION_ASL_DIR;
+ else if (!strcasecmp(act, "store_dir")) out->action = ACTION_ASL_DIR;
+ else if (!strcasecmp(act, "file")) out->action = ACTION_FILE;
else if (!strcasecmp(act, "forward")) out->action = ACTION_FORWARD;
if (p != NULL)
return -1;
}
- if ((out->action == ACTION_STORE) && (out->options == NULL))
+ /* store /some/path means save to a file */
+ if ((out->action == ACTION_ASL_STORE) && (out->options != NULL)) out->action = ACTION_ASL_FILE;
+
+ if (out->action == ACTION_FILE) _act_file_init(out);
+ else if ((out->action == ACTION_ASL_FILE) || (out->action == ACTION_ASL_DIR)) _act_store_init(out);
+
+ if (out->action == ACTION_ASL_STORE)
{
- asldebug("action = ACTION_STORE options = NULL\n");
+ asldebug("action = ACTION_ASL_STORE\n");
if (asl_datastore_rule == NULL) asl_datastore_rule = out;
else
{
return 0;
}
-/*
- * Used to sed config parameters.
- * Line format "= name value"
- */
-static int
-_parse_set_param(char *s)
-{
- char **l;
- uint32_t intval, count, v32a, v32b, v32c;
-
- if (s == NULL) return -1;
- if (s[0] == '\0') return 0;
-
- /* skip '=' and whitespace */
- s++;
- while ((*s == ' ') || (*s == '\t')) s++;
-
- l = explode(s, " \t");
- if (l == NULL) return -1;
-
- for (count = 0; l[count] != NULL; count++);
-
- /* name is required */
- if (count == 0)
- {
- freeList(l);
- return -1;
- }
-
- /* value is required */
- if (count == 1)
- {
- freeList(l);
- return -1;
- }
-
- if (!strcasecmp(l[0], "debug"))
- {
- /* = debug {0|1} [file] */
- intval = atoi(l[1]);
- config_debug(intval, l[2]);
- }
- else if (!strcasecmp(l[0], "cutoff"))
- {
- /* = cutoff level */
- intval = atoi(l[1]);
- if (intval > ASL_LEVEL_DEBUG) intval = ASL_LEVEL_DEBUG;
- global.asl_log_filter = ASL_FILTER_MASK_UPTO(intval);
- }
- else if (!strcasecmp(l[0], "mark_time"))
- {
- /* = mark_time seconds */
- OSSpinLockLock(&global.lock);
- global.mark_time = atoll(l[1]);
- OSSpinLockUnlock(&global.lock);
- }
- else if (!strcasecmp(l[0], "dup_delay"))
- {
- /* = bsd_max_dup_time seconds */
- OSSpinLockLock(&global.lock);
- global.bsd_max_dup_time = atoll(l[1]);
- OSSpinLockUnlock(&global.lock);
- }
- else if (!strcasecmp(l[0], "asl_store_ping_time"))
- {
- /* NB this is private / unpublished */
- /* = asl_store_ping_time seconds */
- OSSpinLockLock(&global.lock);
- global.asl_store_ping_time = atoll(l[1]);
- OSSpinLockUnlock(&global.lock);
- }
- else if (!strcasecmp(l[0], "utmp_ttl"))
- {
- /* = utmp_ttl seconds */
- OSSpinLockLock(&global.lock);
- global.utmp_ttl = (time_t)atoll(l[1]);
- OSSpinLockUnlock(&global.lock);
- }
- else if (!strcasecmp(l[0], "fs_ttl"))
- {
- /* = fs_ttl seconds */
- OSSpinLockLock(&global.lock);
- global.fs_ttl = (time_t)atoll(l[1]);
- OSSpinLockUnlock(&global.lock);
- }
- else if (!strcasecmp(l[0], "mps_limit"))
- {
- /* = mps_limit number */
- OSSpinLockLock(&global.lock);
- global.mps_limit = (uint32_t)atol(l[1]);
- OSSpinLockUnlock(&global.lock);
- }
- else if (!strcasecmp(l[0], "max_file_size"))
- {
- /* = max_file_size bytes */
- pthread_mutex_lock(global.db_lock);
-
- if (global.dbtype & DB_TYPE_FILE)
- {
- asl_store_close(global.file_db);
- global.file_db = NULL;
- global.db_file_max = atoi(l[1]);
- }
-
- pthread_mutex_unlock(global.db_lock);
- }
- else if ((!strcasecmp(l[0], "db")) || (!strcasecmp(l[0], "database")) || (!strcasecmp(l[0], "datastore")))
- {
- /* NB this is private / unpublished */
- /* = db type [max]... */
-
- v32a = 0;
- v32b = 0;
- v32c = 0;
-
- if ((l[1][0] >= '0') && (l[1][0] <= '9'))
- {
- intval = atoi(l[1]);
- if ((count >= 3) && (strcmp(l[2], "-"))) v32a = atoi(l[2]);
- if ((count >= 4) && (strcmp(l[3], "-"))) v32b = atoi(l[3]);
- if ((count >= 5) && (strcmp(l[4], "-"))) v32c = atoi(l[4]);
- }
- else if (!strcasecmp(l[1], "file"))
- {
- intval = DB_TYPE_FILE;
- if ((count >= 3) && (strcmp(l[2], "-"))) v32a = atoi(l[2]);
- }
- else if (!strncasecmp(l[1], "mem", 3))
- {
- intval = DB_TYPE_MEMORY;
- if ((count >= 3) && (strcmp(l[2], "-"))) v32b = atoi(l[2]);
- }
- else if (!strncasecmp(l[1], "min", 3))
- {
- intval = DB_TYPE_MINI;
- if ((count >= 3) && (strcmp(l[2], "-"))) v32c = atoi(l[2]);
- }
- else
- {
- freeList(l);
- return -1;
- }
-
- if (v32a == 0) v32a = global.db_file_max;
- if (v32b == 0) v32b = global.db_memory_max;
- if (v32c == 0) v32c = global.db_mini_max;
-
- config_data_store(intval, v32a, v32b, v32c);
- }
-
- freeList(l);
- return 0;
-}
-
static int
_parse_line(char *s)
{
case '=':
{
/* Set parameter */
- status = _parse_set_param(s);
+ status = control_set_param(s);
break;
}
default:
ASL_KEY_MSG, _PATH_ASL_CONF, s,
ASL_KEY_UID, ASL_KEY_GID);
- asl_log_string(str);
+ internal_log_message(str);
free(str);
}
return status;
}
-static void
+static void
_act_notify(action_rule_t *r)
{
if (r == NULL) return;
mask = umask(0);
status = mkdir(sd->dir, sd->mode);
umask(mask);
-
+
if (status != 0) return ASL_STATUS_WRITE_FAILED;
-
- if ((sd->uid != 0) || (sd->gid != 0))
- {
- if (chown(sd->dir, sd->uid, sd->gid) != 0) return ASL_STATUS_WRITE_FAILED;
- }
+ if (chown(sd->dir, sd->uid, sd->gid) != 0) return ASL_STATUS_WRITE_FAILED;
}
else
{
/* Unexpected stat error */
return ASL_STATUS_FAILED;
}
-
+
path = NULL;
asprintf(&path, "%s/%s", sd->dir, FILE_ASL_STORE_DATA);
if (path == NULL) return ASL_STATUS_NO_MEMORY;
return ASL_STATUS_FAILED;
}
- if ((sd->uid != 0) || (sd->gid != 0))
+ if (chown(path, sd->uid, sd->gid) != 0)
{
- if (chown(path, sd->uid, sd->gid) != 0)
- {
- free(path);
- return ASL_STATUS_WRITE_FAILED;
- }
+ free(path);
+ return ASL_STATUS_WRITE_FAILED;
}
}
else
}
static void
-_act_store(action_rule_t *r, aslmsg msg)
+_act_store_init(action_rule_t *r)
{
- struct store_data *sd;
- asl_file_t *s;
- uint8_t x;
- uint32_t status;
- uint64_t mid;
- mode_t tmp_mode, mask;
- char *str, *opts, *p;
- const char *val;
- time_t tick;
+ struct store_data *sd, *xd;
+ char *str, *opts, *p, *path;
+ action_rule_t *x;
- s = NULL;
+ /* check if the store data is already set up */
+ if (r->data != NULL) return;
- /* _act_store is not used for the main ASL data store */
- if (r->options == NULL) return;
+ opts = r->options;
+ path = _next_word(&opts);
- if (r->data == NULL)
+ if ((path == NULL) || (path[0] != '/'))
{
- /* set up store data */
- sd = (struct store_data *)calloc(1, sizeof(struct store_data));
- if (sd == NULL) return;
+ str = NULL;
+ asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Invalid path for \"%s\" action: %s]",
+ ASL_KEY_SENDER,
+ ASL_KEY_LEVEL, ASL_LEVEL_ERR,
+ ASL_KEY_PID, getpid(),
+ ASL_KEY_MSG,
+ (r->action == ACTION_ASL_FILE) ? "store" : "store_directory",
+ (path == NULL) ? "no path specified" : path);
- opts = r->options;
- sd->store = NULL;
+ internal_log_message(str);
+ free(str);
+ r->action = ACTION_NONE;
+ free(path);
+ return;
+ }
- if (r->action == ACTION_STORE)
+ /* check if a previous rule has set up this path (ACTION_ASL_FILE) or dir (ACTION_ASL_DIR) */
+ for (x = asl_action_rule; x != NULL; x = x->next)
+ {
+ if ((x->action == r->action) && (x->data != NULL))
{
- sd->path = _next_word(&opts);
- if ((sd->path == NULL) || (sd->path[0] != '/'))
- {
- str = NULL;
- asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Invalid path for \"store\" action: %s]",
- ASL_KEY_SENDER,
- ASL_KEY_LEVEL, ASL_LEVEL_ERR,
- ASL_KEY_PID, getpid(),
- ASL_KEY_MSG, (sd->path == NULL) ? "no path specified" : sd->path);
+ xd = (struct store_data *)x->data;
+ p = xd->path;
+ if (r->action == ACTION_ASL_DIR) p = xd->dir;
- asl_log_string(str);
- free(str);
- free(sd);
- r->action = ACTION_NONE;
- return;
- }
- }
- else if (r->action == ACTION_STORE_DIR)
- {
- sd->dir = _next_word(&opts);
- if ((sd->dir == NULL) || (sd->dir[0] != '/'))
+ if ((p != NULL) && (!strcmp(path, p)))
{
- str = NULL;
- asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Invalid path for \"store_directory\" action: %s]",
- ASL_KEY_SENDER,
- ASL_KEY_LEVEL, ASL_LEVEL_ERR,
- ASL_KEY_PID, getpid(),
- ASL_KEY_MSG, (sd->dir == NULL) ? "no path specified" : sd->dir);
-
- asl_log_string(str);
- free(str);
- free(sd);
- r->action = ACTION_NONE;
+ free(path);
+ xd->refcount++;
+ r->data = x->data;
return;
}
}
+ }
+
+ /* set up store data */
+ sd = (struct store_data *)calloc(1, sizeof(struct store_data));
+ if (sd == NULL) return;
- sd->mode = 0755;
- sd->next_id = 0;
- sd->uid = 0;
- sd->gid = 0;
- sd->flags = 0;
+ sd->refcount = 1;
+ sd->mode = 0755;
+ sd->next_id = 0;
+ sd->uid = 0;
+ sd->gid = 0;
+ sd->flags = 0;
- while (NULL != (p = _next_word(&opts)))
+ if (r->action == ACTION_ASL_DIR) sd->dir = path;
+ else sd->path = path;
+
+ while (NULL != (p = _next_word(&opts)))
+ {
+ if (!strcmp(p, "stayopen"))
{
- if (!strcmp(p, "stayopen"))
- {
- sd->flags |= ACT_STORE_FLAG_STAY_OPEN;
- }
- else if (!strcmp(p, "continue"))
- {
- sd->flags |= ACT_STORE_FLAG_CONTINUE;
- }
- else if (!strncmp(p, "mode=0", 6))
- {
- sd->mode = 0;
- x = *(p + 6);
- if ((x < '0') || (x > '7'))
- {
- free(p);
- free(sd->path);
- free(sd->dir);
- free(sd);
- r->action = ACTION_NONE;
- return;
- }
+ sd->flags |= ACT_STORE_FLAG_STAY_OPEN;
+ }
+ else if (!strcmp(p, "continue"))
+ {
+ sd->flags |= ACT_STORE_FLAG_CONTINUE;
+ }
+ else if (!strncmp(p, "mode=", 5)) sd->mode = strtol(p+5, NULL, 0);
+ else if (!strncmp(p, "uid=", 4)) sd->uid = atoi(p+4);
+ else if (!strncmp(p, "gid=", 4)) sd->gid = atoi(p+4);
- tmp_mode = x - '0';
- sd->mode += tmp_mode << 6;
+ free(p);
+ p = NULL;
+ }
- x = *(p + 7);
- if ((x < '0') || (x > '7'))
- {
- free(p);
- free(sd->path);
- free(sd->dir);
- free(sd);
- r->action = ACTION_NONE;
- return;
- }
+ r->data = sd;
+}
- tmp_mode = x - '0';
- sd->mode += tmp_mode << 3;
+/*
+ * Save a message to an ASL format file (ACTION_ASL_FILE)
+ * or to an ASL directory (ACTION_ASL_DIR).
+ */
+static void
+_act_store(action_rule_t *r, aslmsg msg)
+{
+ struct store_data *sd;
+ asl_file_t *s;
+ uint32_t status;
+ uint64_t mid;
+ mode_t mask;
+ char *str, *opts;
+ const char *val;
+ time_t tick;
- x = *(p + 8);
- if ((x < '0') || (x > '7'))
- {
- free(p);
- free(sd->path);
- free(sd->dir);
- free(sd);
- r->action = ACTION_NONE;
- return;
- }
+ s = NULL;
- tmp_mode = x - '0';
- sd->mode += tmp_mode;
- }
- else if (!strncmp(p, "mode=", 5)) sd->mode = atoi(p+4);
- else if (!strncmp(p, "uid=", 4)) sd->uid = atoi(p+4);
- else if (!strncmp(p, "gid=", 4)) sd->gid = atoi(p+4);
+ if (r->data == NULL) return;
- free(p);
- p = NULL;
- }
+ sd = (struct store_data *)r->data;
- r->data = sd;
- }
- else
- {
- sd = (struct store_data *)r->data;
- }
+ if (sd->flags & ACT_FLAG_HAS_LOGGED) return;
+ sd->flags |= ACT_FLAG_HAS_LOGGED;
- if (r->action == ACTION_STORE_DIR)
+ if (r->action == ACTION_ASL_DIR)
{
val = asl_get(msg, ASL_KEY_TIME);
if (val == NULL) return;
{
asldebug("_act_store_dir_setup %s failed: %s\n", sd->path, asl_core_error(status));
- /* disable further activity */
- asl_file_close(sd->store);
- sd->store = NULL;
- r->action = ACTION_NONE;
- return;
+ sd->fails++;
+
+ /* disable further activity after multiple failures */
+ if (sd->fails > MAX_FAILURES)
+ {
+ char *str = NULL;
+ asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Disabling writes to path %s following %u failures (%s)]",
+ ASL_KEY_SENDER,
+ ASL_KEY_LEVEL, ASL_LEVEL_ERR,
+ ASL_KEY_PID, getpid(),
+ ASL_KEY_MSG, sd->path, sd->fails, asl_core_error(status));
+
+ internal_log_message(str);
+ free(str);
+
+ asl_file_close(sd->store);
+ sd->store = NULL;
+ r->action = ACTION_NONE;
+ return;
+ }
+ }
+ else
+ {
+ sd->fails = 0;
}
}
{
asldebug("asl_file_open_write %s failed: %s\n", sd->path, asl_core_error(status));
- /* disable further activity */
- asl_file_close(sd->store);
- sd->store = NULL;
- r->action = ACTION_NONE;
- return;
+ sd->fails++;
+
+ /* disable further activity after multiple failures */
+ if (sd->fails > MAX_FAILURES)
+ {
+ char *str = NULL;
+ asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Disabling writes to path %s following %u failures (%s)]",
+ ASL_KEY_SENDER,
+ ASL_KEY_LEVEL, ASL_LEVEL_ERR,
+ ASL_KEY_PID, getpid(),
+ ASL_KEY_MSG, sd->path, sd->fails, asl_core_error(status));
+
+ internal_log_message(str);
+ free(str);
+
+ asl_file_close(sd->store);
+ sd->store = NULL;
+ r->action = ACTION_NONE;
+ return;
+ }
+ }
+ else if (status == ASL_STATUS_OK)
+ {
+ sd->fails = 0;
}
sd->store = s;
}
- if (r->action != ACTION_STORE_DIR)
+ if (r->action != ACTION_ASL_DIR)
{
status = _act_store_file_setup(sd);
if (status != ASL_STATUS_OK)
{
asldebug("_act_store_file_setup %s failed: %s\n", sd->path, asl_core_error(status));
- /* disable further activity */
- asl_file_close(sd->store);
- sd->store = NULL;
- r->action = ACTION_NONE;
- return;
+ sd->fails++;
+
+ /* disable further activity after multiple failures */
+ if (sd->fails > MAX_FAILURES)
+ {
+ char *str = NULL;
+ asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Disabling writes to path %s following %u failures (%s)]",
+ ASL_KEY_SENDER,
+ ASL_KEY_LEVEL, ASL_LEVEL_ERR,
+ ASL_KEY_PID, getpid(),
+ ASL_KEY_MSG, sd->path, sd->fails, asl_core_error(status));
+
+ internal_log_message(str);
+ free(str);
+
+ asl_file_close(sd->store);
+ sd->store = NULL;
+ r->action = ACTION_NONE;
+ return;
+ }
+ }
+ else
+ {
+ sd->fails = 0;
}
}
{
asldebug("asl_file_save %s failed: %s\n", sd->path, asl_core_error(status));
- /* disable further activity on this file */
- asl_file_close(sd->store);
- sd->store = NULL;
- r->action = ACTION_NONE;
- return;
+ sd->fails++;
+
+ /* disable further activity after multiple failures */
+ if (sd->fails > MAX_FAILURES)
+ {
+ char *str = NULL;
+ asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Disabling writes to path %s following %u failures (%s)]",
+ ASL_KEY_SENDER,
+ ASL_KEY_LEVEL, ASL_LEVEL_ERR,
+ ASL_KEY_PID, getpid(),
+ ASL_KEY_MSG, sd->path, sd->fails, asl_core_error(status));
+
+ internal_log_message(str);
+ free(str);
+
+ asl_file_close(sd->store);
+ sd->store = NULL;
+ r->action = ACTION_NONE;
+ return;
+ }
+ }
+ else
+ {
+ sd->fails = 0;
}
if ((sd->flags & ACT_STORE_FLAG_STAY_OPEN) == 0)
}
}
-static void
-_act_forward(action_rule_t *r, aslmsg msg)
+static int
+_act_file_send_repeat_msg(struct file_data *fdata)
{
- /* To do: <rdar://problem/6130747> Add a "forward" action to asl.conf */
-}
+ char vt[32], *msg;
+ int len, status, closeit;
+ time_t now = time(NULL);
-static void
-send_to_asl_store(aslmsg msg)
-{
- const char *vlevel, *val;
- uint64_t v64;
- uint32_t status, level, lmask;
- int x, log_me;
- action_rule_t *r;
+ if (fdata == NULL) return -1;
+
+ free(fdata->last_msg);
+ fdata->last_msg = NULL;
+
+ if (fdata->last_count == 0) return 0;
+
+ /* stop the timer */
+ dispatch_suspend(fdata->dup_timer);
+
+ memset(vt, 0, sizeof(vt));
+ ctime_r(&now, vt);
+ vt[19] = '\0';
- if (filter_token == -1)
+ msg = NULL;
+ asprintf(&msg, "%s --- last message repeated %u time%s ---\n", vt + 4, fdata->last_count, (fdata->last_count == 1) ? "" : "s");
+ fdata->last_count = 0;
+ if (msg == NULL) return -1;
+
+ closeit = 0;
+ if (fdata->fd < 0)
{
- /* set up com.apple.syslog.asl_filter */
- status = notify_register_check(NOTIFY_SYSTEM_ASL_FILTER, &filter_token);
- if (status != NOTIFY_STATUS_OK)
+ closeit = 1;
+ fdata->fd = _act_file_open(fdata);
+ if (fdata->fd < 0)
{
- filter_token = -1;
+ asldebug("%s: error opening for repeat message (%s): %s\n", MY_ID, fdata->path, strerror(errno));
+ return -1;
+ }
+ }
+
+ len = strlen(msg);
+ status = write(fdata->fd, msg, len);
+ free(msg);
+ if (closeit != 0)
+ {
+ close(fdata->fd);
+ fdata->fd = -1;
+ }
+
+ if ((status < 0) || (status < len))
+ {
+ asldebug("%s: error writing repeat message (%s): %s\n", MY_ID, fdata->path, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * N.B. This is basic file rotation support.
+ * More rotation options will be added in the future, along
+ * with support in aslmanager for compression and deletion.
+ */
+static void
+_act_file_rotate_file_data(struct file_data *fdata, time_t now)
+{
+ char str[MAXPATHLEN];
+ size_t len;
+ int width;
+
+ if (now == 0) now = time(NULL);
+
+ /* flush duplicates if pending */
+ _act_file_send_repeat_msg(fdata);
+
+ /* sleep to prevent a sub-second rotation */
+ while (now == fdata->stamp)
+ {
+ sleep(1);
+ now = time(NULL);
+ }
+
+ len = strlen(fdata->path);
+ width = len - 4;
+ if ((len > 4) && (!strcasecmp(fdata->path + width, ".log")))
+ {
+ /* ".log" rename: abc.log -> abc.timestamp.log */
+ snprintf(str, sizeof(str), "%.*s.%lu.log", width, fdata->path, fdata->stamp);
+ }
+ else
+ {
+ snprintf(str, sizeof(str), "%s.%lu", fdata->path, fdata->stamp);
+ }
+
+ rename(fdata->path, str);
+
+ fdata->stamp = now;
+}
+
+int
+_act_file_open(struct file_data *fdata)
+{
+ acl_t acl;
+ uuid_t uuid;
+ acl_entry_t entry;
+ acl_permset_t perms;
+ int status;
+ int fd = -1;
+ mode_t mask;
+ struct stat sb;
+ uint32_t i;
+
+ memset(&sb, 0, sizeof(struct stat));
+ status = stat(fdata->path, &sb);
+ if (status == 0)
+ {
+ /* must be a regular file */
+ if (!S_ISREG(sb.st_mode)) return -1;
+
+ /* use st_birthtimespec if stamp is zero */
+ if (fdata->stamp == 0) fdata->stamp = sb.st_birthtimespec.tv_sec;
+
+ /* rotate if over size limit */
+ if ((fdata->max_size > 0) && (sb.st_size > fdata->max_size))
+ {
+ _act_file_rotate_file_data(fdata, 0);
}
else
{
- status = notify_check(filter_token, &x);
- if (status == NOTIFY_STATUS_OK)
+ /* open existing file */
+ fd = open(fdata->path, O_RDWR | O_APPEND | O_EXCL, 0);
+ return fd;
+ }
+ }
+ else if (errno != ENOENT)
+ {
+ return -1;
+ }
+
+#if TARGET_OS_EMBEDDED
+ return open(fdata->path, O_RDWR | O_CREAT | O_EXCL, (fdata->mode & 0666));
+#else
+
+ acl = acl_init(1);
+
+ for (i = 0; i < fdata->ngid; i++)
+ {
+ status = mbr_gid_to_uuid(fdata->gid[i], uuid);
+ if (status != 0)
+ {
+ char *str = NULL;
+ asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Unknown GID %d for \"file\" action: %s]",
+ ASL_KEY_SENDER,
+ ASL_KEY_LEVEL, ASL_LEVEL_ERR,
+ ASL_KEY_PID, getpid(),
+ ASL_KEY_MSG, fdata->gid[i], fdata->path);
+
+ internal_log_message(str);
+ free(str);
+ continue;
+ }
+
+ status = acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY);
+ if (status != 0) goto asl_file_create_return;
+
+ status = acl_set_tag_type(entry, ACL_EXTENDED_ALLOW);
+ if (status != 0) goto asl_file_create_return;
+
+ status = acl_set_qualifier(entry, &uuid);
+ if (status != 0) goto asl_file_create_return;
+
+ status = acl_get_permset(entry, &perms);
+ if (status != 0) goto asl_file_create_return;
+
+ status = acl_add_perm(perms, ACL_READ_DATA);
+ if (status != 0) goto asl_file_create_return;
+ }
+
+ for (i = 0; i < fdata->nuid; i++)
+ {
+ status = mbr_uid_to_uuid(fdata->uid[i], uuid);
+ if (status != 0)
+ {
+ char *str = NULL;
+ asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Unknown UID %d for \"file\" action: %s]",
+ ASL_KEY_SENDER,
+ ASL_KEY_LEVEL, ASL_LEVEL_ERR,
+ ASL_KEY_PID, getpid(),
+ ASL_KEY_MSG, fdata->uid[i], fdata->path);
+
+ internal_log_message(str);
+ free(str);
+ continue;
+ }
+
+ status = acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY);
+ if (status != 0) goto asl_file_create_return;
+
+ status = acl_set_tag_type(entry, ACL_EXTENDED_ALLOW);
+ if (status != 0) goto asl_file_create_return;
+
+ status = acl_set_qualifier(entry, &uuid);
+ if (status != 0) goto asl_file_create_return;
+
+ status = acl_get_permset(entry, &perms);
+ if (status != 0) goto asl_file_create_return;
+
+ status = acl_add_perm(perms, ACL_READ_DATA);
+ if (status != 0) goto asl_file_create_return;
+ }
+
+ mask = umask(0);
+ fd = open(fdata->path, O_RDWR | O_CREAT | O_EXCL, (fdata->mode & 0666));
+ umask(mask);
+ if (fd < 0) goto asl_file_create_return;
+
+ errno = 0;
+ status = acl_set_fd(fd, acl);
+
+ if (status != 0)
+ {
+ close(fd);
+ fd = -1;
+ unlink(fdata->path);
+ }
+
+asl_file_create_return:
+
+ acl_free(acl);
+ return fd;
+#endif
+}
+
+static void
+_act_file_rotate(const char *path)
+{
+ action_rule_t *r;
+ struct file_data *fdata;
+ time_t now = time(NULL);
+
+ for (r = asl_action_rule; r != NULL; r = r->next)
+ {
+ if (r->action == ACTION_FILE)
+ {
+ fdata = (struct file_data *)r->data;
+ if (fdata->flags & ACT_FILE_FLAG_ROTATE)
{
- v64 = global.asl_log_filter;
- status = notify_set_state(filter_token, v64);
+ if ((path == NULL) || ((fdata->path != NULL) && !strcmp(fdata->path, path)))
+ {
+ _act_file_rotate_file_data(fdata, now);
+ }
}
- if (status != NOTIFY_STATUS_OK)
+ }
+ }
+}
+
+static char *
+_act_file_format_string(char *s)
+{
+ char *fmt;
+ size_t i, len, n;
+
+ if (s == NULL) return NULL;
+
+ len = strlen(s);
+ n = 0;
+ for (i = 0; i < len; i++) if (s[i] == '\\') n++;
+
+ fmt = malloc(1 + len - n);
+ if (fmt == NULL) return NULL;
+
+ for (i = 0, n = 0; i < len; i++) if (s[i] != '\\') fmt[n++] = s[i];
+ fmt[n] = '\0';
+ return fmt;
+}
+
+static size_t
+_act_file_max_size(char *s)
+{
+ size_t len, n, max;
+ char x;
+
+ if (s == NULL) return 0;
+
+ len = strlen(s);
+ if (len == 0) return 0;
+
+ n = 1;
+ x = s[len - 1];
+ if (x > 90) x -= 32;
+ if (x == 'K') n = 1ll << 10;
+ else if (x == 'M') n = 1ll << 20;
+ else if (x == 'G') n = 1ll << 30;
+ else if (x == 'T') n = 1ll << 40;
+
+ max = atoll(s) * n;
+ return max;
+}
+
+static void
+_act_file_add_uid(struct file_data *fdata, char *s)
+{
+ if (fdata == NULL) return;
+ if (s == NULL) return;
+
+ fdata->uid = reallocf(fdata->uid, (fdata->nuid + 1) * sizeof(uid_t));
+ if (fdata->uid == NULL)
+ {
+ fdata->nuid = 0;
+ return;
+ }
+
+ fdata->uid[fdata->nuid++] = atoi(s);
+}
+
+static void
+_act_file_add_gid(struct file_data *fdata, char *s)
+{
+ if (fdata == NULL) return;
+ if (s == NULL) return;
+
+ fdata->gid = reallocf(fdata->gid, (fdata->ngid + 1) * sizeof(gid_t));
+ if (fdata->gid == NULL)
+ {
+ fdata->ngid = 0;
+ return;
+ }
+
+ fdata->gid[fdata->ngid++] = atoi(s);
+}
+
+static void
+_act_file_init(action_rule_t *r)
+{
+ struct file_data *fdata, *xdata;
+ char *str, *opts, *p, *path;
+ action_rule_t *x;
+
+ /* check if the file data is already set up */
+ if (r->data != NULL) return;
+
+ /* requires at least a path */
+ if (r->options == NULL) return;
+ opts = r->options;
+ path = _next_word(&opts);
+
+ if ((path == NULL) || (path[0] != '/'))
+ {
+ str = NULL;
+ asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Invalid path for \"file\" action: %s]",
+ ASL_KEY_SENDER,
+ ASL_KEY_LEVEL, ASL_LEVEL_ERR,
+ ASL_KEY_PID, getpid(),
+ ASL_KEY_MSG, (path == NULL) ? "no path specified" : path);
+
+ internal_log_message(str);
+ free(str);
+ free(path);
+ r->action = ACTION_NONE;
+ return;
+ }
+
+ /* check if a previous rule has set up this path */
+ for (x = asl_action_rule; x != NULL; x = x->next)
+ {
+ if ((x->action == ACTION_FILE) && (x->data != NULL))
+ {
+ xdata = (struct file_data *)x->data;
+ if ((xdata->path != NULL) && (!strcmp(path, xdata->path)))
{
- notify_cancel(filter_token);
- filter_token = -1;
+ free(path);
+ xdata->refcount++;
+ r->data = x->data;
+ return;
}
}
}
- /* ASLOption "store" forces a message to be saved */
- log_me = asl_check_option(msg, ASL_OPT_STORE);
- if (log_me == 1)
+ /* set up file data */
+ fdata = (struct file_data *)calloc(1, sizeof(struct file_data));
+ if (fdata == NULL) return;
+
+ fdata->refcount = 1;
+ fdata->path = path;
+
+ /*
+ * options:
+ * mode= set file creation mode
+ * uid= user added to read ACL
+ * gid= group added to read ACL
+ * format= format string (also fmt=)
+ * no_dup_supress no duplicate supression
+ *
+ * rotate automatic daily rotation
+ * this is basic rotation - more support is TBD
+ */
+ fdata->mode = 0644;
+ fdata->flags = ACT_FILE_FLAG_DUP_SUPRESS;
+
+ while (NULL != (p = _next_word(&opts)))
{
- db_save_message(msg);
- return;
+ if (!strncmp(p, "mode=", 5)) fdata->mode = strtol(p+5, NULL, 0);
+ else if (!strncmp(p, "uid=", 4)) _act_file_add_uid(fdata, p+4);
+ else if (!strncmp(p, "gid=", 4)) _act_file_add_gid(fdata, p+4);
+ else if (!strncmp(p, "fmt=", 4)) fdata->fmt = _act_file_format_string(p+4);
+ else if (!strncmp(p, "format=", 7)) fdata->fmt = _act_file_format_string(p+7);
+ else if (!strncmp(p, "no_dup_supress", 14)) fdata->flags &= ~ACT_FILE_FLAG_DUP_SUPRESS;
+ else if (!strncmp(p, "rotate", 6)) fdata->flags |= ACT_FILE_FLAG_ROTATE;
+ else if (!strncmp(p, "max_size=", 9)) fdata->max_size = _act_file_max_size(p+9);
+
+ free(p);
+ p = NULL;
}
- log_me = 0;
- if (filter_token >= 0)
+ if (fdata->fmt == NULL) fdata->fmt = strdup("std");
+
+ /* duplicate compression is only possible for std and bsd formats */
+ if (strcmp(fdata->fmt, "std") && strcmp(fdata->fmt, "bsd")) fdata->flags &= ~ACT_FILE_FLAG_DUP_SUPRESS;
+
+ /* set time format for raw output */
+ if (!strcmp(fdata->fmt, "raw")) fdata->tfmt = "sec";
+
+ r->data = fdata;
+}
+
+static void
+_act_file(action_rule_t *r, aslmsg msg)
+{
+ struct file_data *fdata;
+ int is_dup;
+ uint32_t len, msg_hash = 0;
+ char *str;
+ time_t now, today;
+ struct tm ctm;
+
+ if (r->data == NULL) return;
+
+ fdata = (struct file_data *)r->data;
+
+ now = time(NULL);
+ today = now;
+
+ memset(&ctm, 0, sizeof(struct tm));
+ if (localtime_r((const time_t *)&now, &ctm) != NULL)
{
- x = 0;
- status = notify_check(filter_token, &x);
- if ((status == NOTIFY_STATUS_OK) && (x == 1))
+ ctm.tm_sec = 0;
+ ctm.tm_min = 0;
+ ctm.tm_hour = 0;
+ today = mktime(&ctm);
+ }
+
+ /* check for rotation */
+ if ((last_file_day != 0) && (last_file_day != today))
+ {
+ _act_file_rotate(NULL);
+ }
+
+ last_file_day = today;
+
+
+ /*
+ * asl.conf may contain multuple rules for the same file, eg:
+ * ? [= Facility zippy] /var/log/abc.log
+ * ? [= Color purple] /var/log/abc.log
+ *
+ * To prevent duplicates we set a flag bit when a message is logged
+ * to this file, and bail out if it has already been logged.
+ * Note that asl_out_message clears the flag bit in all file_data
+ * structures before processing each message.
+ */
+ if (fdata->flags & ACT_FLAG_HAS_LOGGED) return;
+ fdata->flags |= ACT_FLAG_HAS_LOGGED;
+
+ is_dup = 0;
+
+ str = asl_format_message((asl_msg_t *)msg, fdata->fmt, fdata->tfmt, ASL_ENCODE_SAFE, &len);
+
+ if (fdata->flags & ACT_FILE_FLAG_DUP_SUPRESS)
+ {
+ if (fdata->dup_timer == NULL)
{
- v64 = 0;
- status = notify_get_state(filter_token, &v64);
- if ((status == NOTIFY_STATUS_OK) && (v64 != 0)) global.asl_log_filter = v64;
+ /* create a timer to flush dups on this file */
+ fdata->dup_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, asl_action_queue);
+ dispatch_source_set_event_handler(fdata->dup_timer, ^{ _act_file_send_repeat_msg((struct file_data *)r->data); });
+ }
+
+ if ((global.bsd_max_dup_time > 0) && (str != NULL) && (fdata->last_msg != NULL))
+ {
+ msg_hash = asl_core_string_hash(str + 16, len - 16);
+ if ((fdata->last_hash == msg_hash) && (!strcmp(fdata->last_msg, str + 16)))
+ {
+ if ((now - fdata->last_time) < global.bsd_max_dup_time) is_dup = 1;
+ }
}
}
- /* PID 0 (kernel) or PID 1 (launchd) messages are saved */
- val = asl_get(msg, ASL_KEY_PID);
- if ((val != NULL) && (atoi(val) <= 1)) log_me = 1;
+ if (is_dup == 1)
+ {
+ if (fdata->last_count == 0)
+ {
+ /* start the timer */
+ dispatch_source_set_timer(fdata->dup_timer, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * global.bsd_max_dup_time), DISPATCH_TIME_FOREVER, 0);
+ dispatch_resume(fdata->dup_timer);
+ }
+
+ fdata->last_count++;
+ }
else
{
- vlevel = asl_get(msg, ASL_KEY_LEVEL);
- level = 7;
- if (vlevel != NULL) level = atoi(vlevel);
- lmask = ASL_FILTER_MASK(level);
- if ((lmask & global.asl_log_filter) != 0) log_me = 1;
+ fdata->fd = _act_file_open(fdata);
+ if (fdata->fd < 0)
+ {
+ asldebug("_act_file_open %s failed: %s\n", fdata->path, strerror(errno));
+
+ fdata->fails++;
+
+ /* disable further activity after multiple failures */
+ if (fdata->fails > MAX_FAILURES)
+ {
+ char *tmp = NULL;
+ asprintf(&tmp, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Disabling writes to path %s following %u failures (%s)]",
+ ASL_KEY_SENDER,
+ ASL_KEY_LEVEL, ASL_LEVEL_ERR,
+ ASL_KEY_PID, getpid(),
+ ASL_KEY_MSG, fdata->path, fdata->fails, strerror(errno));
+
+ internal_log_message(tmp);
+ free(tmp);
+
+ r->action = ACTION_NONE;
+ free(str);
+ return;
+ }
+ }
+ else
+ {
+ fdata->fails = 0;
+ }
+
+ /*
+ * The current message is not a duplicate. If fdata->last_count > 0
+ * we need to write a "last message repeated N times" log entry.
+ * _act_file_send_repeat_msg will free last_msg and do nothing if
+ * last_count == 0, but we test and free here to avoid a function call.
+ */
+ if (fdata->last_count > 0)
+ {
+ _act_file_send_repeat_msg(fdata);
+ }
+ else
+ {
+ free(fdata->last_msg);
+ fdata->last_msg = NULL;
+ }
+
+ if (str != NULL) fdata->last_msg = strdup(str + 16);
+
+ fdata->last_hash = msg_hash;
+ fdata->last_count = 0;
+ fdata->last_time = now;
+
+ if ((str != NULL) && (len > 1)) write(fdata->fd, str, len - 1);
+ close(fdata->fd);
+ fdata->fd = -1;
}
- if (log_me == 0) return;
+ free(str);
+}
+
+static void
+_act_forward(action_rule_t *r, aslmsg msg)
+{
+ /* To do: <rdar://problem/6130747> Add a "forward" action to asl.conf */
+}
+
+static void
+_send_to_asl_store(aslmsg msg)
+{
+ int log_me;
+ action_rule_t *r;
+
+ /* ASLOption "store" forces a message to be saved */
+ log_me = asl_check_option(msg, ASL_OPT_STORE);
+ if (log_me == 1)
+ {
+ db_save_message(msg);
+ return;
+ }
/* if there are no rules, save the message */
if (asl_datastore_rule == NULL)
}
}
-int
-asl_action_sendmsg(aslmsg msg, const char *outid)
+static void
+_asl_action_message(aslmsg msg)
{
action_rule_t *r;
- if (reset != RESET_NONE) _do_reset();
+ if (msg == NULL) return;
- if (msg == NULL) return -1;
+ /* reset flag bit used for file duplicate avoidance */
+ for (r = asl_action_rule; r != NULL; r = r->next)
+ {
+ if ((r->action == ACTION_FILE) && (r->data != NULL))
+ {
+ ((struct file_data *)(r->data))->flags &= ACT_FLAG_CLEAR_LOGGED;
+ }
+ else if (((r->action == ACTION_ASL_DIR) || (r->action == ACTION_ASL_FILE)) && (r->data != NULL))
+ {
+ ((struct store_data *)(r->data))->flags &= ACT_FLAG_CLEAR_LOGGED;
+ }
+ }
for (r = asl_action_rule; r != NULL; r = r->next)
{
if (asl_msg_cmp(r->query, (asl_msg_t *)msg) == 1)
{
- if ((r->action == ACTION_STORE) || (r->action == ACTION_STORE_DIR))
+ if ((r->action == ACTION_ASL_FILE) || (r->action == ACTION_ASL_DIR))
{
_act_store(r, msg);
- if (asl_check_option(msg, ASL_OPT_IGNORE) != 0) return -1;
+ if (asl_check_option(msg, ASL_OPT_IGNORE) != 0) return;
}
if (r->action == ACTION_NONE) continue;
- else if (r->action == ACTION_IGNORE) return -1;
+ else if (r->action == ACTION_IGNORE) return;
else if (r->action == ACTION_ACCESS) _act_access_control(r, msg);
else if (r->action == ACTION_NOTIFY) _act_notify(r);
else if (r->action == ACTION_BROADCAST) _act_broadcast(r, msg);
+ else if (r->action == ACTION_FILE) _act_file(r, msg);
else if (r->action == ACTION_FORWARD) _act_forward(r, msg);
}
}
- if (asl_check_option(msg, ASL_OPT_IGNORE) != 0) return -1;
+ if (asl_check_option(msg, ASL_OPT_IGNORE) != 0) return;
- send_to_asl_store(msg);
+ _send_to_asl_store(msg);
+}
- return 0;
+void
+asl_out_message(aslmsg msg)
+{
+ dispatch_flush_continuation_cache();
+
+ asl_msg_retain((asl_msg_t *)msg);
+
+ dispatch_async(asl_action_queue, ^{
+ _asl_action_message(msg);
+ asl_msg_release((asl_msg_t *)msg);
+ });
}
static int
int
asl_action_init(void)
{
- asldebug("%s: init\n", MY_ID);
-
- query = asl_msg_new(ASL_TYPE_QUERY);
- aslevent_addmatch(query, MY_ID);
- aslevent_addoutput(asl_action_sendmsg, MY_ID);
+ static dispatch_once_t once;
+ asldebug("%s: init\n", MY_ID);
_parse_config_file(_PATH_ASL_CONF);
- return 0;
-}
-int
-asl_action_reset(void)
-{
- reset = global.reset;
+ dispatch_once(&once, ^{
+ asl_action_queue = dispatch_queue_create("ASL Action Queue", NULL);
+ });
+
return 0;
}
int
-asl_action_close(void)
+_asl_action_close_internal(void)
{
action_rule_t *r, *n;
struct store_data *sd;
+ struct file_data *fdata;
n = NULL;
for (r = asl_action_rule; r != NULL; r = n)
{
n = r->next;
-
- if (((r->action == ACTION_STORE) || (r->action == ACTION_STORE_DIR) || (r->action == ACTION_NONE)) && (r->data != NULL))
+ if (r->data != NULL)
{
- sd = (struct store_data *)r->data;
- if (sd->store != NULL) asl_file_close(sd->store);
- if (sd->storedata != NULL) fclose(sd->storedata);
- free(sd->path);
- free(sd->dir);
- sd->store = NULL;
- free(sd);
+ if (((r->action == ACTION_ASL_FILE) || (r->action == ACTION_ASL_DIR) || (r->action == ACTION_NONE)))
+ {
+ sd = (struct store_data *)r->data;
+ if (sd->refcount > 0) sd->refcount--;
+ if (sd->refcount == 0)
+ {
+ if (sd->store != NULL) asl_file_close(sd->store);
+ if (sd->storedata != NULL) fclose(sd->storedata);
+
+ free(sd->dir);
+ free(sd->path);
+ free(sd);
+ }
+ }
+
+ if (r->action == ACTION_FILE)
+ {
+ fdata = (struct file_data *)r->data;
+ if (fdata->refcount > 0) fdata->refcount--;
+ if (fdata->refcount == 0)
+ {
+ _act_file_send_repeat_msg(fdata);
+
+ if (fdata->dup_timer != NULL)
+ {
+ dispatch_source_cancel(fdata->dup_timer);
+ dispatch_resume(fdata->dup_timer);
+ dispatch_release(fdata->dup_timer);
+ }
+
+ free(fdata->path);
+ free(fdata->fmt);
+ free(fdata->uid);
+ free(fdata->gid);
+ free(fdata->last_msg);
+ free(fdata);
+ }
+ }
}
if (r->query != NULL) asl_msg_release(r->query);
return 0;
}
+
+int
+asl_action_close(void)
+{
+ dispatch_async(asl_action_queue, ^{
+ _asl_action_close_internal();
+ });
+
+ return 0;
+}
+
+int
+asl_action_reset(void)
+{
+ dispatch_async(asl_action_queue, ^{
+ _asl_action_close_internal();
+ asl_action_init();
+ });
+
+ return 0;
+}
+
+int
+asl_action_file_rotate(const char *path)
+{
+ /*
+ * The caller may want to know when the rotation has been completed,
+ * so this is synchronous. Also ensures the string stays intact while we work.
+ */
+ dispatch_sync(asl_action_queue, ^{
+ _act_file_rotate(path);
+ });
+
+ return 0;
+}
+
+++ /dev/null
-/*
- * Copyright (c) 2004-2010 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * 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 2.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.opensource.apple.com/apsl/ 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, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/fcntl.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/uio.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <errno.h>
-#include "daemon.h"
-
-#define forever for(;;)
-
-#define ASL_SOCKET_NAME "AppleSystemLogger"
-#define MY_ID "asl_in"
-
-static int sock = -1;
-
-aslmsg
-asl_in_getmsg(int fd)
-{
- char *out;
- aslmsg msg;
- uint32_t len, n;
- char tmp[16];
- int status;
- uid_t uid;
- gid_t gid;
-
- n = read(fd, tmp, 11);
- if (n < 11)
- {
- if (n == 0)
- {
- asl_client_count_decrement();
-
- close(fd);
- aslevent_removefd(fd);
- return NULL;
- }
-
- if (n < 0)
- {
- asldebug("%s: read error (len=%d): %s\n", MY_ID, n, strerror(errno));
- if (errno != EINTR)
- {
- asl_client_count_decrement();
-
- close(fd);
- aslevent_removefd(fd);
- return NULL;
- }
- }
-
- return NULL;
- }
-
- len = atoi(tmp);
- if (len == 0) return NULL;
-
- 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)
- {
- asl_client_count_decrement();
-
- close(fd);
- aslevent_removefd(fd);
- free(out);
- return NULL;
- }
- }
- }
-
- asldebug("asl_in_getmsg: %s\n", (out == NULL) ? "NULL" : out);
-
- uid = -2;
- gid = -2;
-
- status = getpeereid(fd, &uid, &gid);
- msg = (aslmsg)asl_msg_from_string(out);
- if (msg == NULL)
- {
- free(out);
- return NULL;
- }
-
- snprintf(tmp, sizeof(tmp), "%d", uid);
- asl_set(msg, ASL_KEY_UID, tmp);
-
- snprintf(tmp, sizeof(tmp), "%d", gid);
- asl_set(msg, ASL_KEY_GID, tmp);
-
- free(out);
- return msg;
-}
-
-aslmsg
-asl_in_new_connection(int fd)
-{
- int clientfd;
-
- asldebug("%s: accepting connection\n", MY_ID);
- clientfd = accept(fd, NULL, 0);
- if (clientfd < 0)
- {
- asldebug("%s: error connecting 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;
- }
-
- asl_client_count_increment();
-
- aslevent_addfd(SOURCE_ASL_SOCKET, clientfd, ADDFD_FLAGS_LOCAL, asl_in_getmsg, NULL, NULL);
- return NULL;
-}
-
-int
-asl_in_init(void)
-{
- int rbufsize;
- int len;
- launch_data_t sockets_dict, fd_array, fd_dict;
-
- asldebug("%s: init\n", MY_ID);
- if (sock >= 0) return sock;
- if (global.launch_dict == NULL)
- {
- asldebug("%s: launchd dict is NULL\n", MY_ID);
- return -1;
- }
-
- sockets_dict = launch_data_dict_lookup(global.launch_dict, LAUNCH_JOBKEY_SOCKETS);
- if (sockets_dict == NULL)
- {
- asldebug("%s: launchd lookup of LAUNCH_JOBKEY_SOCKETS failed\n", MY_ID);
- return -1;
- }
-
- fd_array = launch_data_dict_lookup(sockets_dict, ASL_SOCKET_NAME);
- if (fd_array == NULL)
- {
- asldebug("%s: launchd lookup of ASL_SOCKET_NAME failed\n", MY_ID);
- return -1;
- }
-
- len = launch_data_array_get_count(fd_array);
- if (len <= 0)
- {
- asldebug("%s: launchd fd array is empty\n", MY_ID);
- return -1;
- }
-
- if (len > 1)
- {
- asldebug("%s: warning! launchd fd array has %d sockets\n", MY_ID, len);
- }
-
- fd_dict = launch_data_array_get_index(fd_array, 0);
- if (fd_dict == NULL)
- {
- asldebug("%s: launchd file discriptor array element 0 is NULL\n", MY_ID);
- return -1;
- }
-
- sock = launch_data_get_fd(fd_dict);
-
- 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 %d (%s): %s\n", MY_ID, sock, _PATH_ASL_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_ASL_IN, strerror(errno));
- close(sock);
- sock = -1;
- return -1;
- }
-
- return aslevent_addfd(SOURCE_ASL_SOCKET, sock, ADDFD_FLAGS_LOCAL, asl_in_new_connection, NULL, NULL);
-}
-
-int
-asl_in_close(void)
-{
- if (sock < 0) return 1;
-
- aslevent_removefd(sock);
- close(sock);
- sock = -1;
-
- return 0;
-}
-
-int
-asl_in_reset(void)
-{
- return 0;
-}
closedir(dp);
- e = list;
for (e = list; e != NULL; e = e->next)
{
status = do_ASLExpireTime_filter(e->name);
/*
- * Copyright (c) 2004-2010 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2011 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#define MAXLINE 4096
static int sock = -1;
+static dispatch_source_t in_src;
+static dispatch_queue_t in_queue;
-aslmsg
+void
bsd_in_acceptmsg(int fd)
{
uint32_t len;
int n;
- char line[MAXLINE + 1];
+ char line[MAXLINE];
struct sockaddr_un sun;
+ aslmsg m;
len = sizeof(struct sockaddr_un);
n = recvfrom(fd, line, MAXLINE, 0, (struct sockaddr *)&sun, &len);
- if (n <= 0) return NULL;
+ if (n <= 0) return;
line[n] = '\0';
- return asl_input_parse(line, n, NULL, SOURCE_BSD_SOCKET);
+ m = asl_input_parse(line, n, NULL, SOURCE_BSD_SOCKET);
+ dispatch_async(global.work_queue, ^{ process_message(m, SOURCE_BSD_SOCKET); });
}
int
-bsd_in_init(void)
+bsd_in_init()
{
int rbufsize;
int len;
launch_data_t sockets_dict, fd_array, fd_dict;
+ static dispatch_once_t once;
+
+ dispatch_once(&once, ^{
+ in_queue = dispatch_queue_create(MY_ID, NULL);
+ });
asldebug("%s: init\n", MY_ID);
- if (sock >= 0) return sock;
+ if (sock >= 0) return -1;
if (global.launch_dict == NULL)
{
return -1;
}
- return aslevent_addfd(SOURCE_BSD_SOCKET, sock, ADDFD_FLAGS_LOCAL, bsd_in_acceptmsg, NULL, NULL);
+ in_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, (uintptr_t)sock, 0, in_queue);
+ dispatch_source_set_event_handler(in_src, ^{ bsd_in_acceptmsg(sock); });
+
+ dispatch_resume(in_src);
+ return 0;
}
int
{
if (sock < 0) return 1;
- aslevent_removefd(sock);
+ dispatch_source_cancel(in_src);
+ dispatch_release(in_src);
+ in_src = NULL;
+
close(sock);
sock = -1;
#define DST_TYPE_WALL 4
#define DST_TYPE_NOTE 5
-#define CLOSE_ON_IDLE_SEC 60
+#define CLOSE_ON_IDLE_SEC 300
-static asl_msg_t *query = NULL;
-static int reset = RESET_NONE;
-static pthread_mutex_t reset_lock = PTHREAD_MUTEX_INITIALIZER;
+static dispatch_queue_t bsd_out_queue;
+static dispatch_source_t bsd_idle_timer;
struct config_rule
{
uint32_t last_hash;
uint32_t last_count;
time_t last_time;
+ dispatch_source_t dup_timer;
char *last_msg;
TAILQ_ENTRY(config_rule) entries;
};
extern uint32_t asl_core_string_hash(const char *s, uint32_t inlen);
-int bsd_out_close();
-int bsd_out_network_reset(void);
-static int _parse_config_file(const char *);
-
-static void
-_do_reset(void)
-{
- pthread_mutex_lock(&reset_lock);
- if (reset == RESET_NONE)
- {
- pthread_mutex_unlock(&reset_lock);
- return;
- }
-
- if (reset == RESET_CONFIG)
- {
- bsd_out_close();
- _parse_config_file(_PATH_SYSLOG_CONF);
- }
- else if (reset == RESET_NETWORK)
- {
- bsd_out_network_reset();
- }
-
- reset = RESET_NONE;
-
- pthread_mutex_unlock(&reset_lock);
-}
-
static int
_level_for_name(const char *name)
{
}
static int
-_syslog_send_repeat_msg(struct config_rule *r)
+_bsd_send_repeat_msg(struct config_rule *r)
{
char vt[32], *msg;
time_t tick;
if (r->type != DST_TYPE_FILE) return 0;
if (r->last_count == 0) return 0;
+ /* stop the timer */
+ dispatch_suspend(r->dup_timer);
+
tick = time(NULL);
memset(vt, 0, sizeof(vt));
ctime_r(&tick, vt);
msg = NULL;
asprintf(&msg, "%s: --- last message repeated %u time%s ---\n", vt + 4, r->last_count, (r->last_count == 1) ? "" : "s");
+ r->last_count = 0;
if (msg == NULL) return -1;
len = strlen(msg);
}
static int
-_syslog_send(aslmsg msg, struct config_rule *r, char **out, char **fwd, time_t now)
+_bsd_send(aslmsg msg, struct config_rule *r, char **out, char **fwd, time_t now)
{
char *sf, *outmsg;
const char *vlevel, *vfacility;
*fwd = sf;
}
- outlen = 0;
if (r->type == DST_TYPE_SOCK) outlen = strlen(*fwd);
else outlen = strlen(*out);
* If current message is NOT a duplicate and r->last_count > 0
* we need to write a "last message was repeated N times" log entry
*/
- if ((r->type == DST_TYPE_FILE) && (is_dup == 0) && (r->last_count > 0)) _syslog_send_repeat_msg(r);
+ if ((r->type == DST_TYPE_FILE) && (is_dup == 0) && (r->last_count > 0)) _bsd_send_repeat_msg(r);
do_write = 1;
*/
vfacility = asl_get(msg, ASL_KEY_FACILITY);
if ((vfacility != NULL) && (!strcmp(vfacility, FACILITY_KERNEL)) && (r->type == DST_TYPE_CONS)) do_write = 0;
- if ((do_write == 1) && (r->type == DST_TYPE_FILE) && (is_dup == 1)) do_write = 0;
+ if ((do_write == 1) && (r->type == DST_TYPE_FILE) && (is_dup == 1))
+ {
+ do_write = 0;
+
+ if (r->dup_timer == NULL)
+ {
+ /* create a timer to flush dups on this file */
+ r->dup_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, bsd_out_queue);
+ dispatch_source_set_event_handler(r->dup_timer, ^{ _bsd_send_repeat_msg(r); });
+ }
+
+ if (r->last_count == 0)
+ {
+ /* start the timer */
+ dispatch_source_set_timer(r->dup_timer, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * global.bsd_max_dup_time), DISPATCH_TIME_FOREVER, 0);
+ dispatch_resume(r->dup_timer);
+ }
+ }
if (do_write == 0) status = outlen;
else status = write(r->fd, *out, outlen);
}
static int
-_syslog_rule_match(aslmsg msg, struct config_rule *r)
+_bsd_rule_match(aslmsg msg, struct config_rule *r)
{
- uint32_t i, test, f, pri;
+ uint32_t i, test, f;
+ int32_t pri;
const char *val;
if (msg == NULL) return 0;
return test;
}
-int
-bsd_out_sendmsg(aslmsg msg, const char *outid)
+static int
+_bsd_match_and_send(aslmsg msg)
{
struct config_rule *r;
char *out, *fwd;
- time_t tick;
- uint64_t delta;
-
- if (reset != RESET_NONE) _do_reset();
+ time_t now;
if (msg == NULL) return -1;
out = NULL;
fwd = NULL;
- tick = time(NULL);
- global.bsd_flush_time = 0;
+ now = time(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, tick);
- if ((r->type == DST_TYPE_FILE) && (r->last_count > 0))
- {
- delta = tick - r->last_time;
- if (delta < global.bsd_max_dup_time)
- {
- delta = global.bsd_max_dup_time - delta;
- if (global.bsd_flush_time == 0) global.bsd_flush_time = delta;
- else if (delta < global.bsd_flush_time) global.bsd_flush_time = delta;
- }
- }
+ if (_bsd_rule_match(msg, r) == 1) _bsd_send(msg, r, &out, &fwd, now);
}
free(out);
}
void
-bsd_close_idle_files(time_t now)
+bsd_out_message(aslmsg msg)
{
+ if (msg == NULL) return;
+
+ asl_msg_retain((asl_msg_t *)msg);
+
+ dispatch_async(bsd_out_queue, ^{
+ _bsd_match_and_send(msg);
+ asl_msg_release((asl_msg_t *)msg);
+ });
+}
+
+static void
+_bsd_close_idle_files()
+{
+ time_t now;
struct config_rule *r;
uint64_t delta;
+ now = time(NULL);
+
for (r = bsd_out_rule.tqh_first; r != NULL; r = r->entries.tqe_next)
{
/* only applies to files */
if (r->type != DST_TYPE_FILE) continue;
/*
- * If the last message repeat count is non-zero, a bsd_flush_duplicates()
+ * If the last message repeat count is non-zero, a _bsd_flush_duplicates()
* call will occur within 30 seconds. Don't bother closing the file.
*/
if (r->last_count > 0) continue;
}
}
-void
-bsd_flush_duplicates(time_t now)
-{
- struct config_rule *r;
- uint64_t delta;
-
- global.bsd_flush_time = 0;
-
- for (r = bsd_out_rule.tqh_first; r != NULL; r = r->entries.tqe_next)
- {
- if (r->type != DST_TYPE_FILE) continue;
-
- if (r->last_count > 0)
- {
- delta = now - r->last_time;
- if (delta < global.bsd_max_dup_time)
- {
- delta = global.bsd_max_dup_time - delta;
- if (global.bsd_flush_time == 0) global.bsd_flush_time = delta;
- else if (delta < global.bsd_flush_time) global.bsd_flush_time = delta;
- }
- else
- {
- _syslog_dst_open(r);
- _syslog_send_repeat_msg(r);
-
- r->last_count = 0;
- }
- }
- }
-}
-
static int
_parse_config_file(const char *confname)
{
int
bsd_out_init(void)
{
+ static dispatch_once_t once;
+
asldebug("%s: init\n", MY_ID);
TAILQ_INIT(&bsd_out_rule);
+ _parse_config_file(_PATH_SYSLOG_CONF);
- query = asl_msg_new(ASL_TYPE_QUERY);
- aslevent_addmatch(query, MY_ID);
- aslevent_addoutput(bsd_out_sendmsg, MY_ID);
+ dispatch_once(&once, ^{
+ bsd_out_queue = dispatch_queue_create("BSD Out Queue", NULL);
- _parse_config_file(_PATH_SYSLOG_CONF);
- return 0;
-}
+ /* start a timer to close idle files */
+ bsd_idle_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, bsd_out_queue);
+ dispatch_source_set_event_handler(bsd_idle_timer, ^{ _bsd_close_idle_files(); });
+ dispatch_source_set_timer(bsd_idle_timer, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * CLOSE_ON_IDLE_SEC), NSEC_PER_SEC * CLOSE_ON_IDLE_SEC, 0);
+ dispatch_resume(bsd_idle_timer);
+ });
-int
-bsd_out_reset(void)
-{
- reset = global.reset;
return 0;
}
-int
-bsd_out_close(void)
+static int
+_bsd_out_close_internal(void)
{
struct config_rule *r, *n;
int i;
{
n = r->entries.tqe_next;
- free(r->dst);
+ if (r->dup_timer != NULL)
+ {
+ if (r->last_count > 0) _bsd_send_repeat_msg(r);
+ dispatch_source_cancel(r->dup_timer);
+ dispatch_resume(r->dup_timer);
+ dispatch_release(r->dup_timer);
+ }
+
+ free(r->dst);
free(r->addr);
free(r->last_msg);
free(r->fac_prefix_len);
free(r->facility);
}
+
TAILQ_REMOVE(&bsd_out_rule, r, entries);
free(r);
}
}
int
-bsd_out_network_reset(void)
+bsd_out_close(void)
{
- struct config_rule *r;
+ dispatch_async(bsd_out_queue, ^{
+ _bsd_out_close_internal();
+ });
- for (r = bsd_out_rule.tqh_first; r != NULL; r = r->entries.tqe_next)
- {
- if (r->type == DST_TYPE_SOCK)
- {
- close(r->fd);
- r->fd = -1;
- if (r->addr != NULL)
- {
- free(r->addr);
- r->addr = NULL;
- }
- }
- }
+ return 0;
+}
+
+int
+bsd_out_reset(void)
+{
+ dispatch_async(bsd_out_queue, ^{
+ _bsd_out_close_internal();
+ bsd_out_init();
+ });
return 0;
}
<key>MachServices</key>
<dict>
<key>com.apple.system.logger</key>
- <true/>
+ <dict>
+ <key>ResetAtClose</key>
+ <true/>
+ </dict>
</dict>
<key>Sockets</key>
<dict>
/*
- * Copyright (c) 2004-2010 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2011 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#define VERIFY_STATUS_EXCEEDED_QUOTA 2
extern void disaster_message(aslmsg m);
+extern int asl_action_reset(void);
+
static char myname[MAXHOSTNAMELEN + 1] = {0};
+static int name_change_token = -1;
static OSSpinLock count_lock = 0;
#define QUOTA_TABLE_SLOTS 8
#define QUOTA_EXCEEDED_MESSAGE "*** process %d exceeded %d log message per second limit - remaining messages this second discarded ***"
+#define QUOTA_KERN_EXCEEDED_MESSAGE "*** kernel exceeded %d log message per second limit - remaining messages this second discarded ***"
#define QUOTA_EXCEEDED_LEVEL "3"
+#define DEFAULT_DB_FILE_MAX 25600000
+#define DEFAULT_DB_MEMORY_MAX 8192
+#define DEFAULT_DB_MINI_MAX 256
+#define DEFAULT_MPS_LIMIT 500
+#define DEFAULT_REMOTE_DELAY 5000
+#define DEFAULT_BSD_MAX_DUP_SEC 30
+#define DEFAULT_MARK_SEC 0
+#define DEFAULT_UTMP_TTL_SEC 31622400
+
static time_t quota_table_time = 0;
static pid_t quota_table_pid[QUOTA_TABLE_SIZE];
static int32_t quota_table_quota[QUOTA_TABLE_SIZE];
+static int32_t kern_quota;
+static int32_t kern_level;
static const char *kern_notify_key[] =
{
"com.apple.system.log.kernel.debug"
};
-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;
+static int kern_notify_token[8] = {-1, -1, -1, -1, -1, -1, -1, -1 };
static char **
_insertString(char *s, char **l, uint32_t x)
*/
static uint32_t
-quota_check(pid_t pid, time_t now, aslmsg msg)
+quota_check(pid_t pid, time_t now, aslmsg msg, uint32_t level)
{
int i, x, maxx, max;
- char *str;
+ char *str, lstr[2];
if (msg == NULL) return VERIFY_STATUS_INVALID_MESSAGE;
if (global.mps_limit == 0) return VERIFY_STATUS_OK;
- OSSpinLockLock(&global.lock);
-
if (quota_table_time != now)
{
memset(quota_table_pid, 0, sizeof(quota_table_pid));
+ kern_quota = global.mps_limit;
+ kern_level = 7;
quota_table_time = now;
}
+ /* kernel gets it's own quota */
+ if (pid == 0)
+ {
+ if (level < kern_level) kern_level = level;
+ if (kern_quota > 0) kern_quota--;
+
+ if (kern_quota > 0) return VERIFY_STATUS_OK;
+ if (kern_quota < 0) return VERIFY_STATUS_EXCEEDED_QUOTA;
+
+ kern_quota = -1;
+
+ str = NULL;
+ asprintf(&str, QUOTA_KERN_EXCEEDED_MESSAGE, global.mps_limit);
+ if (str != NULL)
+ {
+ asl_set(msg, ASL_KEY_MSG, str);
+ free(str);
+ lstr[0] = kern_level + '0';
+ lstr[1] = 0;
+ asl_set(msg, ASL_KEY_LEVEL, lstr);
+ }
+
+ return VERIFY_STATUS_OK;
+ }
+
/* hash is last 10 bits of the pid, shifted up 3 bits to allow 8 slots per bucket */
x = (pid & 0x000003ff) << 3;
maxx = x;
quota_table_pid[x] = pid;
quota_table_quota[x] = global.mps_limit;
- OSSpinLockUnlock(&global.lock);
return VERIFY_STATUS_OK;
}
asl_set(msg, ASL_KEY_LEVEL, QUOTA_EXCEEDED_LEVEL);
}
- OSSpinLockUnlock(&global.lock);
return VERIFY_STATUS_OK;
}
if (quota_table_quota[x] < 0)
{
- OSSpinLockUnlock(&global.lock);
return VERIFY_STATUS_EXCEEDED_QUOTA;
}
- OSSpinLockUnlock(&global.lock);
return VERIFY_STATUS_OK;
}
quota_table_pid[maxx] = pid;
quota_table_quota[maxx] = global.mps_limit;
- OSSpinLockUnlock(&global.lock);
return VERIFY_STATUS_OK;
}
return 0;
}
-int
-aslevent_init(void)
-{
- TAILQ_INIT(&Eventq);
- TAILQ_INIT(&Outq);
- TAILQ_INIT(&Matchq);
-
- return 0;
-}
-
-int
-aslevent_log(aslmsg 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);
- if (status < 0) break;
- }
- }
-
- 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
-asl_message_match_and_log(aslmsg msg)
-{
- struct aslmatch *i;
- int status = -1;
-
- if (msg == NULL) return;
-
- for (i = Matchq.tqh_first; i != NULL; i = i->entries.tqe_next)
- {
- if (asl_msg_cmp(i->query, (asl_msg_t *)msg) != 0)
- {
- status = aslevent_log(msg, i->outid);
- if (status < 0) break;
- }
- }
-}
-
-int
-aslevent_removefd(int fd)
+const char *
+whatsmyhostname()
{
- struct aslevent *e, *next;
-
- e = Eventq.tqh_first;
-
- while (e != NULL)
- {
- next = e->entries.tqe_next;
- if (fd == e->fd)
- {
- e->fd = -1;
- return 0;
- }
-
- e = next;
- }
-
- return -1;
-}
+ static dispatch_once_t once;
+ char *dot;
+ int check, status;
-static const char *_source_name(int n)
-{
- switch (n)
- {
- case SOURCE_INTERNAL: return "internal";
- case SOURCE_ASL_SOCKET: return "ASL socket";
- case SOURCE_BSD_SOCKET: return "BSD socket";
- case SOURCE_UDP_SOCKET: return "UDP socket";
- case SOURCE_KERN: return "kernel";
- case SOURCE_ASL_MESSAGE: return "ASL message";
- case SOURCE_LAUNCHD: return "launchd";
- case SOURCE_SESSION: return "session";
- default: return "unknown";
- }
+ dispatch_once(&once, ^{
+ snprintf(myname, sizeof(myname), "%s", "localhost");
+ notify_register_check(kNotifySCHostNameChange, &name_change_token);
+ });
- return "unknown";
-}
+ check = 1;
+ status = 0;
-void
-aslevent_check()
-{
- struct aslevent *e, *next;
- fd_set test;
- struct timeval zero = {0};
- int max;
+ if (name_change_token >= 0) status = notify_check(name_change_token, &check);
- e = Eventq.tqh_first;
+ if ((status == 0) && (check == 0)) return (const char *)myname;
- while (e != NULL)
+ if (gethostname(myname, MAXHOSTNAMELEN) < 0)
{
- next = e->entries.tqe_next;
- if (e->fd >= 0)
- {
- FD_ZERO(&test);
- FD_SET(e->fd, &test);
- max = e->fd + 1;
-
- if (select(max, &test, NULL, NULL, &zero) < 0)
- {
- asldebug("aslevent_check: fd %d source %d (%s) errno %d\n", e->fd, e->source, _source_name(e->source), e->fd);
- }
- }
-
- e = next;
+ snprintf(myname, sizeof(myname), "%s", "localhost");
}
-}
-
-const char *
-whatsmyhostname()
-{
- char *dot;
-
- if (gethostname(myname, MAXHOSTNAMELEN) < 0)
+ else
{
- memset(myname, 0, sizeof(myname));
- return "localhost";
+ dot = strchr(myname, '.');
+ if (dot != NULL) *dot = '\0';
}
- dot = strchr(myname, '.');
- if (dot != NULL) *dot = '\0';
-
return (const char *)myname;
}
OSSpinLockUnlock(&count_lock);
}
-int
-aslevent_addfd(int source, int fd, uint32_t flags, aslreadfn readfn, aslwritefn writefn, aslexceptfn exceptfn)
-{
- struct aslevent *e;
- int found = 0, status;
-#ifdef LOCAL_PEERCRED
- struct xucred cr;
-#endif
- socklen_t 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));
- memset(str, 0, sizeof(str));
-
- len = sizeof(struct sockaddr_storage);
-
- if (flags & ADDFD_FLAGS_LOCAL)
- {
- snprintf(str, sizeof(str), "localhost");
- sender = str;
-
-#ifdef LOCAL_PEERCRED
- len = sizeof(cr);
-
- status = getsockopt(fd, LOCAL_PEERCRED, 1, &cr, &len);
- if (status == 0)
- {
- u = cr.cr_uid;
- g = cr.cr_gid;
- }
-#endif
- }
- else
- {
- status = getpeername(fd, (struct sockaddr *)&ss, &len);
- if (status == 0)
- {
- if (len == 0)
- {
- /* UNIX Domain socket */
- snprintf(str, sizeof(str), "localhost");
- sender = str;
- }
- else
- {
- if (inet_ntop(ss.ss_family, (struct sockaddr *)&ss, str, 256) == NULL) sender = str;
- }
- }
- }
-
- asldebug("source %d fd %d flags 0x%08x UID %d GID %d Sender %s\n", source, fd, flags, 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);
- if (e->sender == NULL) return -1;
- }
-
- e->uid = u;
- e->gid = g;
- found = 1;
- }
- }
-
- if (found) return 0;
-
- e = calloc(1, sizeof(struct aslevent));
- if (e == NULL) return -1;
-
- e->source = source;
- e->fd = fd;
- e->readfn = readfn;
- e->writefn = writefn;
- e->exceptfn = exceptfn;
- e->sender = NULL;
- if (sender != NULL)
- {
- e->sender = strdup(sender);
- if (e->sender == NULL) return -1;
- }
-
- e->uid = u;
- e->gid = g;
-
- TAILQ_INSERT_TAIL(&Eventq, e, entries);
-
- return 0;
-}
-
/*
* Checks message content and sets attributes as required
*
*/
static uint32_t
-aslmsg_verify(uint32_t source, struct aslevent *e, aslmsg msg, int32_t *kern_post_level)
+aslmsg_verify(aslmsg msg, uint32_t source, int32_t *kern_post_level, uid_t *uid_out)
{
const char *val, *fac, *ruval, *rgval;
char buf[64];
time_t tick, now;
uid_t uid;
+ gid_t gid;
uint32_t status, level, fnum;
pid_t pid;
if (msg == NULL) return VERIFY_STATUS_INVALID_MESSAGE;
if (kern_post_level != NULL) *kern_post_level = -1;
-
- /* Time */
- now = time(NULL);
-
- tick = 0;
- val = asl_get(msg, ASL_KEY_TIME);
- if (val != NULL) tick = asl_parse_time(val);
-
- /* Set time to now if it is unset or from the future (not allowed!) */
- if ((tick == 0) || (tick > now)) tick = now;
-
- /* Canonical form: seconds since the epoch */
- snprintf(buf, sizeof(buf) - 1, "%lu", tick);
- asl_set(msg, ASL_KEY_TIME, buf);
-
- /* Host */
- if (e == NULL) asl_set(msg, ASL_KEY_HOST, whatsmyhostname());
- else if (e->sender != NULL)
- {
- if (!strcmp(e->sender, "localhost")) asl_set(msg, ASL_KEY_HOST, whatsmyhostname());
- else asl_set(msg, ASL_KEY_HOST, e->sender);
- }
+ if (uid_out != NULL) *uid_out = -2;
/* PID */
pid = 0;
if (val != NULL) pid = (pid_t)atoi(val);
}
+ /* Time */
+ now = time(NULL);
+
+ /* Level */
+ val = asl_get(msg, ASL_KEY_LEVEL);
+ level = ASL_LEVEL_DEBUG;
+ if ((val != NULL) && (val[1] == '\0') && (val[0] >= '0') && (val[0] <= '7')) level = val[0] - '0';
+ snprintf(buf, sizeof(buf), "%d", level);
+ asl_set(msg, ASL_KEY_LEVEL, buf);
+
/*
- * if quotas are enabled and pid > 1 (not kernel or launchd)
- * and no processes are watching, then check quota
+ * check quota if no processes are watching
*/
- if ((global.mps_limit > 0) && (pid > 1) && (global.watchers_active == 0))
+ if (global.watchers_active == 0)
{
- status = quota_check(pid, now, msg);
+ status = quota_check(pid, now, msg, level);
if (status != VERIFY_STATUS_OK) return status;
}
- /* UID */
+ tick = 0;
+ val = asl_get(msg, ASL_KEY_TIME);
+ if (val != NULL) tick = asl_parse_time(val);
+
+ /* Set time to now if it is unset or from the future (not allowed!) */
+ if ((tick == 0) || (tick > now)) tick = now;
+
+ /* Canonical form: seconds since the epoch */
+ snprintf(buf, sizeof(buf) - 1, "%lu", tick);
+ asl_set(msg, ASL_KEY_TIME, buf);
+
+ val = asl_get(msg, ASL_KEY_HOST);
+ if (val == NULL) asl_set(msg, ASL_KEY_HOST, whatsmyhostname());
+
uid = -2;
val = asl_get(msg, ASL_KEY_UID);
-
- switch (source)
+ if (val != NULL)
{
- case SOURCE_KERN:
- case SOURCE_INTERNAL:
- {
- /* we know the UID is 0 */
- uid = 0;
- asl_set(msg, ASL_KEY_UID, "0");
- break;
- }
- case SOURCE_ASL_SOCKET:
- case SOURCE_ASL_MESSAGE:
- case SOURCE_LAUNCHD:
- {
- /* we trust the UID in the message */
- if (val != NULL) uid = atoi(val);
- break;
- }
- case SOURCE_BSD_SOCKET:
- case SOURCE_UDP_SOCKET:
- {
- 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
- {
- uid = e->uid;
- snprintf(buf, sizeof(buf), "%d", e->uid);
- asl_set(msg, ASL_KEY_UID, buf);
- }
- }
- else if ((e != NULL) && (e->uid != 99))
- {
- uid = e->uid;
- snprintf(buf, sizeof(buf), "%d", e->uid);
- asl_set(msg, ASL_KEY_UID, buf);
- }
- }
- default:
- {
- asl_set(msg, ASL_KEY_UID, "-2");
- }
+ uid = atoi(val);
+ if ((uid == 0) && strcmp(val, "0")) uid = -2;
+ if (uid_out != NULL) *uid_out = uid;
}
- /* GID */
+ gid = -2;
val = asl_get(msg, ASL_KEY_GID);
+ if (val != NULL)
+ {
+ gid = atoi(val);
+ if ((gid == 0) && strcmp(val, "0")) gid = -2;
+ }
+ /* UID & GID */
switch (source)
{
case SOURCE_KERN:
case SOURCE_INTERNAL:
{
- /* we know the GID is 0 */
+ asl_set(msg, ASL_KEY_UID, "0");
asl_set(msg, ASL_KEY_GID, "0");
break;
}
case SOURCE_ASL_MESSAGE:
case SOURCE_LAUNCHD:
{
- /* we trust the GID in the message */
+ /* we trust the UID & GID in the message */
break;
}
- case SOURCE_BSD_SOCKET:
- case SOURCE_UDP_SOCKET:
- {
- 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);
- }
- }
default:
{
- asl_set(msg, ASL_KEY_GID, "-2");
+ /* we do not trust the UID 0 or GID 0 or 80 in the message */
+ if (uid == 0) asl_set(msg, ASL_KEY_UID, "-2");
+ if ((gid == 0) || (gid == 80)) asl_set(msg, ASL_KEY_GID, "-2");
}
}
asl_set(msg, ASL_KEY_SENDER, "Unknown");
}
- /* Level */
- val = asl_get(msg, ASL_KEY_LEVEL);
- level = ASL_LEVEL_DEBUG;
- if ((val != NULL) && (val[1] == '\0') && (val[0] >= '0') && (val[0] <= '7')) level = val[0] - '0';
- snprintf(buf, sizeof(buf), "%d", level);
- asl_set(msg, ASL_KEY_LEVEL, buf);
-
/* Facility */
fac = asl_get(msg, ASL_KEY_FACILITY);
if (fac == NULL)
asl_set(msg, ASL_KEY_EXPIRE_TIME, buf);
}
- /* Set DB Expire Time for Filestsrem errors */
+ /* Set DB Expire Time for Filesystem errors */
if (!strcmp(fac, FSLOG_VAL_FACILITY))
{
- snprintf(buf, sizeof(buf), "%lu", tick + global.fs_ttl);
+ snprintf(buf, sizeof(buf), "%lu", tick + FS_TTL_SEC);
asl_set(msg, ASL_KEY_EXPIRE_TIME, buf);
}
return VERIFY_STATUS_OK;
}
-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)
- {
- if (e->fd < 0) continue;
-
-// 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_cleanup()
-{
- struct aslevent *e, *next;
-
- e = Eventq.tqh_first;
-
- while (e != NULL)
- {
- next = e->entries.tqe_next;
- if (e->fd < 0)
- {
- TAILQ_REMOVE(&Eventq, e, entries);
- if (e->sender != NULL) free(e->sender);
- free(e);
- }
-
- e = next;
- }
-}
-
void
list_append_msg(asl_search_result_t *list, aslmsg msg)
{
}
void
-work_enqueue(aslmsg m)
+init_globals(void)
{
- pthread_mutex_lock(global.work_queue_lock);
- list_append_msg(global.work_queue, m);
- pthread_mutex_unlock(global.work_queue_lock);
- pthread_cond_signal(&global.work_queue_cond);
+ OSSpinLockLock(&global.lock);
+
+ global.debug = 0;
+ free(global.debug_file);
+ global.debug_file = NULL;
+
+#ifdef CONFIG_IPHONE
+ global.dbtype = DB_TYPE_MINI;
+#else
+ global.dbtype = DB_TYPE_FILE;
+#endif
+ global.db_file_max = DEFAULT_DB_FILE_MAX;
+ global.db_memory_max = DEFAULT_DB_MEMORY_MAX;
+ global.db_mini_max = DEFAULT_DB_MINI_MAX;
+ global.mps_limit = DEFAULT_MPS_LIMIT;
+ global.remote_delay_time = DEFAULT_REMOTE_DELAY;
+ global.bsd_max_dup_time = DEFAULT_BSD_MAX_DUP_SEC;
+ global.mark_time = DEFAULT_MARK_SEC;
+ global.utmp_ttl = DEFAULT_UTMP_TTL_SEC;
+
+ OSSpinLockUnlock(&global.lock);
}
-void
-asl_enqueue_message(uint32_t source, struct aslevent *e, aslmsg msg)
+/*
+ * Used to set config parameters.
+ * Line format "= name value"
+ */
+int
+control_set_param(const char *s)
{
- int32_t kplevel;
- uint32_t status;
+ char **l;
+ uint32_t intval, count, v32a, v32b, v32c;
- if (msg == NULL) return;
+ if (s == NULL) return -1;
+ if (s[0] == '\0') return 0;
- kplevel = -1;
- status = aslmsg_verify(source, e, msg, &kplevel);
- if (status == VERIFY_STATUS_OK)
+ /* skip '=' and whitespace */
+ s++;
+ while ((*s == ' ') || (*s == '\t')) s++;
+
+ l = explode(s, " \t");
+ if (l == NULL) return -1;
+
+ for (count = 0; l[count] != NULL; count++);
+
+ /* name is required */
+ if (count == 0)
{
- if ((source == SOURCE_KERN) && (kplevel >= 0)) notify_post(kern_notify_key[kplevel]);
- work_enqueue(msg);
+ freeList(l);
+ return -1;
}
- else
+
+ /* value is required */
+ if (count == 1)
{
- asl_free(msg);
+ freeList(l);
+ return -1;
}
-}
-
-aslmsg *
-work_dequeue(uint32_t *count)
-{
- aslmsg *work;
- pthread_mutex_lock(global.work_queue_lock);
- if (global.work_queue->count == 0)
+ if (!strcasecmp(l[0], "debug"))
+ {
+ /* = debug {0|1} [file] */
+ intval = atoi(l[1]);
+ config_debug(intval, l[2]);
+ }
+ else if (!strcasecmp(l[0], "mark_time"))
{
- pthread_cond_wait(&global.work_queue_cond, global.work_queue_lock);
+ /* = mark_time seconds */
+ OSSpinLockLock(&global.lock);
+ global.mark_time = atoll(l[1]);
+ OSSpinLockUnlock(&global.lock);
}
+ else if (!strcasecmp(l[0], "dup_delay"))
+ {
+ /* = bsd_max_dup_time seconds */
+ OSSpinLockLock(&global.lock);
+ global.bsd_max_dup_time = atoll(l[1]);
+ OSSpinLockUnlock(&global.lock);
+ }
+ else if (!strcasecmp(l[0], "remote_delay"))
+ {
+ /* = remote_delay microseconds */
+ OSSpinLockLock(&global.lock);
+ global.remote_delay_time = atol(l[1]);
+ OSSpinLockUnlock(&global.lock);
+ }
+ else if (!strcasecmp(l[0], "utmp_ttl"))
+ {
+ /* = utmp_ttl seconds */
+ OSSpinLockLock(&global.lock);
+ global.utmp_ttl = (time_t)atoll(l[1]);
+ OSSpinLockUnlock(&global.lock);
+ }
+ else if (!strcasecmp(l[0], "mps_limit"))
+ {
+ /* = mps_limit number */
+ OSSpinLockLock(&global.lock);
+ global.mps_limit = (uint32_t)atol(l[1]);
+ OSSpinLockUnlock(&global.lock);
+ }
+ else if (!strcasecmp(l[0], "max_file_size"))
+ {
+ /* = max_file_size bytes */
+ pthread_mutex_lock(global.db_lock);
- work = NULL;
- *count = 0;
+ if (global.dbtype & DB_TYPE_FILE)
+ {
+ asl_store_close(global.file_db);
+ global.file_db = NULL;
+ global.db_file_max = atoi(l[1]);
+ }
- if (global.work_queue->count == 0)
+ pthread_mutex_unlock(global.db_lock);
+ }
+ else if ((!strcasecmp(l[0], "db")) || (!strcasecmp(l[0], "database")) || (!strcasecmp(l[0], "datastore")))
{
- pthread_mutex_unlock(global.work_queue_lock);
- return NULL;
+ /* NB this is private / unpublished */
+ /* = db type [max]... */
+
+ v32a = 0;
+ v32b = 0;
+ v32c = 0;
+
+ if ((l[1][0] >= '0') && (l[1][0] <= '9'))
+ {
+ intval = atoi(l[1]);
+ if ((count >= 3) && (strcmp(l[2], "-"))) v32a = atoi(l[2]);
+ if ((count >= 4) && (strcmp(l[3], "-"))) v32b = atoi(l[3]);
+ if ((count >= 5) && (strcmp(l[4], "-"))) v32c = atoi(l[4]);
+ }
+ else if (!strcasecmp(l[1], "file"))
+ {
+ intval = DB_TYPE_FILE;
+ if ((count >= 3) && (strcmp(l[2], "-"))) v32a = atoi(l[2]);
+ }
+ else if (!strncasecmp(l[1], "mem", 3))
+ {
+ intval = DB_TYPE_MEMORY;
+ if ((count >= 3) && (strcmp(l[2], "-"))) v32b = atoi(l[2]);
+ }
+ else if (!strncasecmp(l[1], "min", 3))
+ {
+ intval = DB_TYPE_MINI;
+ if ((count >= 3) && (strcmp(l[2], "-"))) v32c = atoi(l[2]);
+ }
+ else
+ {
+ freeList(l);
+ return -1;
+ }
+
+ if (v32a == 0) v32a = global.db_file_max;
+ if (v32b == 0) v32b = global.db_memory_max;
+ if (v32c == 0) v32c = global.db_mini_max;
+
+ config_data_store(intval, v32a, v32b, v32c);
}
- work = (aslmsg *)(global.work_queue->msg);
- *count = global.work_queue->count;
+ freeList(l);
+ return 0;
+}
- global.work_queue->count = 0;
- global.work_queue->curr = 0;
- global.work_queue->msg = NULL;
+static int
+control_message(aslmsg msg)
+{
+ const char *str = asl_get(msg, ASL_KEY_MSG);
- pthread_mutex_unlock(global.work_queue_lock);
- return work;
+ if (str == NULL) return 0;
+
+ if (!strncmp(str, "= reset", 7))
+ {
+ init_globals();
+ return asl_action_reset();
+ }
+ else if (!strncmp(str, "= rotate", 8))
+ {
+ const char *p = str + 8;
+ while ((*p == ' ') || (*p == '\t')) p++;
+ if (*p == '\0') p = NULL;
+ return asl_action_file_rotate(p);
+ }
+ else if (!strncmp(str, "= ", 2))
+ {
+ return control_set_param(str);
+ }
+
+ return 0;
}
void
-aslevent_handleevent(fd_set *rd, fd_set *wr, fd_set *ex)
+process_message(aslmsg msg, uint32_t source)
{
- struct aslevent *e;
- char *out = NULL;
- aslmsg msg;
- int32_t cleanup;
+ int32_t kplevel;
+ uint32_t status;
+ uid_t uid;
-// asldebug("--> aslevent_handleevent\n");
+ if (msg == NULL) return;
- cleanup = 0;
+ kplevel = -1;
+ uid = -2;
- for (e = Eventq.tqh_first; e != NULL; e = e->entries.tqe_next)
+ status = aslmsg_verify(msg, source, &kplevel, &uid);
+ if (status == VERIFY_STATUS_OK)
{
- if (e->fd < 0)
- {
- cleanup = 1;
- continue;
- }
-
- if (FD_ISSET(e->fd, rd) && (e->readfn != NULL))
+ if ((source == SOURCE_KERN) && (kplevel >= 0))
{
-// asldebug("handling read event on %d\n", e->fd);
- msg = e->readfn(e->fd);
- if (msg == NULL) continue;
+ if (kplevel > 7) kplevel = 7;
+ if (kern_notify_token[kplevel] < 0)
+ {
+ status = notify_register_plain(kern_notify_key[kplevel], &(kern_notify_token[kplevel]));
+ if (status != 0) asldebug("notify_register_plain(%s) failed status %u\n", status);
+ }
- asl_enqueue_message(e->source, e, msg);
+ notify_post(kern_notify_key[kplevel]);
}
- 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\n");
- }
- }
+ if ((uid == 0) && asl_check_option(msg, ASL_OPT_CONTROL)) control_message(msg);
- if (cleanup != 0) aslevent_cleanup();
+ /* send message to output modules */
+ asl_out_message(msg);
+ if (global.bsd_out_enabled) bsd_out_message(msg);
+ }
-// asldebug("<-- aslevent_handleevent\n");
+ asl_free(msg);
}
int
-asl_log_string(const char *str)
+internal_log_message(const char *str)
{
aslmsg msg;
msg = (aslmsg)asl_msg_from_string(str);
if (msg == NULL) return 1;
- asl_enqueue_message(SOURCE_INTERNAL, NULL, msg);
+ dispatch_async(global.work_queue, ^{ process_message(msg, SOURCE_INTERNAL); });
return 0;
}
asldebug(const char *str, ...)
{
va_list v;
- int status;
- FILE *dfp;
+ FILE *dfp = NULL;
- OSSpinLockLock(&global.lock);
- if (global.debug == 0)
- {
- OSSpinLockUnlock(&global.lock);
- return 0;
- }
+ if (global.debug == 0) return 0;
- dfp = stderr;
- if (global.debug_file != NULL) dfp = fopen(global.debug_file, "a");
- if (dfp == NULL)
- {
- OSSpinLockUnlock(&global.lock);
- return 0;
- }
+ if (global.debug_file == NULL) dfp = fopen(_PATH_SYSLOGD_LOG, "a");
+ else dfp = fopen(global.debug_file, "a");
+ if (dfp == NULL) return 0;
va_start(v, str);
- status = vfprintf(dfp, str, v);
+ vfprintf(dfp, str, v);
va_end(v);
- if (global.debug_file != NULL) fclose(dfp);
- OSSpinLockUnlock(&global.lock);
+ fclose(dfp);
- return status;
+ return 0;
}
void
ASL_KEY_PID, getpid(),
ASL_KEY_MSG, ASL_KEY_UID, ASL_KEY_GID);
- asl_log_string(str);
+ internal_log_message(str);
if (str != NULL) free(str);
}
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_HOST, whatsmyhostname());
return msg;
}
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 if (hval != NULL) asl_set(msg, ASL_KEY_HOST, hval);
- else asl_set(msg, ASL_KEY_HOST, rhost);
-
- if (hval != NULL) free(hval);
+ if (hval != NULL)
+ {
+ asl_set(msg, ASL_KEY_HOST, hval);
+ free(hval);
+ }
+ else if (rhost != NULL)
+ {
+ asl_set(msg, ASL_KEY_HOST, rhost);
+ }
return msg;
}
asl_input_parse(const char *in, int len, char *rhost, uint32_t source)
{
aslmsg msg;
- int status, x, legacy;
+ int status, x, legacy, off;
asldebug("asl_input_parse: %s\n", (in == NULL) ? "NULL" : in);
/*
* 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 '['.
+ * ASL input may start with a length (10 bytes) followed by a space and a '['.
+ * The length is optional, so ASL messages may also just start with '['.
*/
if ((in[0] != '<') && (len > 11))
{
if (legacy == 1) return asl_syslog_input_convert(in, len, rhost, source);
- msg = (aslmsg)asl_msg_from_string(in + 11);
+ off = 11;
+ if (in[0] == '[') off = 0;
+
+ msg = (aslmsg)asl_msg_from_string(in + off);
if (msg == NULL) return NULL;
if (rhost != NULL) asl_set(msg, ASL_KEY_HOST, rhost);
memcpy(s, out, len);
+ if (s[len - 1] != '\n') len++;
s[len - 1] = '\0';
return s;
}
asl_set(m, ASL_KEY_TIME, str);
}
- /* Host */
- asl_set(m, ASL_KEY_HOST, whatsmyhostname());
-
/* Facility */
asl_set(m, ASL_KEY_FACILITY, FACILITY_CONSOLE);
asl_set(m, ASL_KEY_MSG, msg);
}
- /* verify and push to receivers */
- asl_enqueue_message(SOURCE_LAUNCHD, NULL, m);
+ dispatch_async(global.work_queue, ^{ process_message(m, SOURCE_LAUNCHD); });
}
-void
-launchd_drain()
-{
- forever
- {
- _vprocmgr_log_drain(NULL, NULL, launchd_callback);
- }
-}
/*
- * Copyright (c) 2004-2010 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2011 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <time.h>
#include <asl.h>
#include <asl_msg.h>
+#include <asl_core.h>
#include <asl_private.h>
#include <asl_store.h>
#include "asl_memory.h"
#include "asl_mini_memory.h"
#include <notify.h>
+#include <notify_keys.h>
#include <launch.h>
+#include <dispatch/dispatch.h>
#include <libkern/OSAtomic.h>
#define ADDFD_FLAGS_LOCAL 0x00000001
-#define ASL_DB_NOTIFICATION "com.apple.system.logger.message"
#define SELF_DB_NOTIFICATION "self.logger.message"
#define ASL_OPT_IGNORE "ignore"
#define ASL_OPT_STORE "store"
+#define ASL_OPT_CONTROL "control"
+#define ASL_OPT_DB_FILE "asl_db_file"
+#define ASL_OPT_DB_MINI "asl_db_mini_memory"
+#define ASL_OPT_DB_MEMORY "asl_db_memory"
#define _PATH_PIDFILE "/var/run/syslog.pid"
-#define _PATH_ASL_IN "/var/run/asl_input"
#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"
+#define _PATH_SYSLOGD_LOG "/var/log/syslogd.log"
#define DB_TYPE_FILE 0x00000001
#define DB_TYPE_MEMORY 0x00000002
#define STORE_FLAGS_FILE_CACHE_SWEEP_REQUESTED 0x00000001
-#define RESET_NONE 0
-#define RESET_CONFIG 1
-#define RESET_NETWORK 2
+#define FS_TTL_SEC 31622400
+
+typedef struct
+{
+ const char *name;
+ int enabled;
+ int (*init)(void);
+ int (*reset)(void);
+ int (*close)(void);
+} module_t;
struct global_s
{
int disaster_occurred;
mach_port_t listen_set;
mach_port_t server_port;
- mach_port_t self_port;
mach_port_t dead_session_port;
launch_data_t launch_dict;
uint32_t store_flags;
time_t start_time;
int lockdown_session_fd;
int watchers_active;
- int kfd;
int reset;
uint64_t bsd_flush_time;
pthread_mutex_t *db_lock;
- pthread_mutex_t *work_queue_lock;
pthread_cond_t work_queue_cond;
- asl_search_result_t *work_queue;
+ dispatch_queue_t work_queue;
+ dispatch_source_t mark_timer;
+ dispatch_source_t sig_hup_src;
asl_store_t *file_db;
asl_memory_t *memory_db;
asl_mini_memory_t *mini_db;
asl_mini_memory_t *disaster_db;
+ int module_count;
+ int bsd_out_enabled;
+ module_t **module;
/* parameters below are configurable as command-line args or in /etc/asl.conf */
- int asl_log_filter;
int debug;
char *debug_file;
int dbtype;
uint32_t db_memory_max;
uint32_t db_mini_max;
uint32_t mps_limit;
+ uint32_t remote_delay_time;
uint64_t bsd_max_dup_time;
- uint64_t asl_store_ping_time;
uint64_t mark_time;
time_t utmp_ttl;
- time_t fs_ttl;
};
extern struct global_s global;
-typedef aslmsg (*aslreadfn)(int);
-typedef char *(*aslwritefn)(const char *, int);
-typedef char *(*aslexceptfn)(int);
-typedef int (*aslsendmsgfn)(aslmsg msg, const char *outid);
-
-struct aslevent
-{
- int source;
- 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 module_list
-{
- char *name;
- void *module;
- int (*init)(void);
- int (*reset)(void);
- int (*close)(void);
- TAILQ_ENTRY(module_list) entries;
-};
+void init_globals(void);
void config_debug(int enable, const char *path);
void config_data_store(int type, uint32_t file_max, uint32_t memory_max, uint32_t mini_max);
-void config_timers(uint64_t bsd_max_dup, uint64_t asl_store_ping, uint64_t utmp, uint64_t fs);
char **explode(const char *s, const char *delim);
void freeList(char **l);
-int aslevent_init(void);
-int aslevent_fdsets(fd_set *, fd_set *, fd_set *);
-void aslevent_handleevent(fd_set *, fd_set *, fd_set *);
void asl_mark(void);
void asl_archive(void);
-void aslevent_check(void);
void asl_client_count_increment();
void asl_client_count_decrement();
char *get_line_from_file(FILE *f);
int asldebug(const char *, ...);
-int asl_log_string(const char *str);
+int internal_log_message(const char *str);
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);
-int aslevent_addfd(int source, int fd, uint32_t flags, aslreadfn readfn, aslwritefn writefn, aslexceptfn exceptfn);
-int aslevent_removefd(int fd);
-int aslevent_addmatch(asl_msg_t *query, char *outid);
-
int asl_check_option(aslmsg msg, const char *opt);
-int aslevent_addoutput(aslsendmsgfn, const char *outid);
-
-void asl_enqueue_message(uint32_t source, struct aslevent *e, aslmsg msg);
-aslmsg *work_dequeue(uint32_t *count);
-void asl_message_match_and_log(aslmsg msg);
void send_to_direct_watchers(asl_msg_t *msg);
+void launchd_callback();
+
int asl_syslog_faciliy_name_to_num(const char *fac);
const char *asl_syslog_faciliy_num_to_name(int num);
aslmsg asl_input_parse(const char *in, int len, char *rhost, uint32_t source);
-void db_ping_store(void);
+const char *whatsmyhostname();
+void process_message(aslmsg m, uint32_t source);
+void asl_out_message(aslmsg msg);
+int asl_action_file_rotate(const char *path);
+void bsd_out_message(aslmsg msg);
+int control_set_param(const char *s);
/* notify SPI */
uint32_t notify_register_plain(const char *name, int *out_token);
#include <pthread.h>
#include <notify.h>
#include <sys/time.h>
-#include <asl.h>
+#include "daemon.h"
#include "asl_ipc.h"
#include "asl_ipcServer.h"
-#include <asl_core.h>
-#include <asl_store.h>
-#include "daemon.h"
#define forever for(;;)
#define SEARCH_FORWARD 1
#define SEARCH_BACKWARD -1
-static pthread_mutex_t db_lock = PTHREAD_MUTEX_INITIALIZER;
+#define MAX_AGAIN 100
+
+static dispatch_queue_t asl_server_queue;
extern char *asl_list_to_string(asl_search_result_t *list, uint32_t *outlen);
extern asl_search_result_t *asl_list_from_string(const char *buf);
union __ReplyUnion__asl_ipc_subsystem reply;
} asl_reply_msg;
-void
-db_ping_store(void)
-{
- if ((global.dbtype & DB_TYPE_FILE) && (global.file_db != NULL))
- {
- global.store_flags |= STORE_FLAGS_FILE_CACHE_SWEEP_REQUESTED;
-
- /* wake up the output worker thread */
- pthread_cond_signal(&global.work_queue_cond);
- }
-}
-
-void
-db_asl_open()
+static void
+db_asl_open(uint32_t dbtype)
{
uint32_t status;
struct stat sb;
- if ((global.dbtype & DB_TYPE_FILE) && (global.file_db == NULL))
+ if ((dbtype & DB_TYPE_FILE) && (global.file_db == NULL))
{
memset(&sb, 0, sizeof(struct stat));
if (stat(PATH_ASL_STORE, &sb) == 0)
}
}
- if ((global.dbtype & DB_TYPE_MEMORY) && (global.memory_db == NULL))
+ if ((dbtype & DB_TYPE_MEMORY) && (global.memory_db == NULL))
{
status = asl_memory_open(global.db_memory_max, &(global.memory_db));
if (status != ASL_STATUS_OK)
}
}
- if ((global.dbtype & DB_TYPE_MINI) && (global.mini_db == NULL))
+ if ((dbtype & DB_TYPE_MINI) && (global.mini_db == NULL))
{
status = asl_mini_memory_open(global.db_mini_max, &(global.mini_db));
if (status != ASL_STATUS_OK)
}
}
-/*
- * Takes messages off the work queue and saves them.
- * Runs in it's own thread.
- */
void
-output_worker()
+send_to_direct_watchers(asl_msg_t *msg)
{
- aslmsg *work;
- uint32_t i, count;
- mach_msg_empty_send_t *empty;
- kern_return_t kstatus;
+ uint32_t i, j, nlen, outlen, cleanup, total_sent, again;
+ ssize_t sent;
+ char *out;
- empty = (mach_msg_empty_send_t *)calloc(1, sizeof(mach_msg_empty_send_t));
- if (empty == NULL) return;
+#ifdef LOCKDOWN
+ static struct timeval last_time;
- forever
+ if (global.lockdown_session_fd >= 0)
{
- count = 0;
-
- /* blocks until work is available */
- work = work_dequeue(&count);
-
- if (work == NULL)
+ if (global.remote_delay_time > 0)
{
- if ((global.dbtype & DB_TYPE_FILE) && (global.store_flags & STORE_FLAGS_FILE_CACHE_SWEEP_REQUESTED))
+ struct timeval now;
+ uint64_t delta;
+
+ if (gettimeofday(&now, NULL) == 0)
{
- asl_store_sweep_file_cache(global.file_db);
- global.store_flags &= ~STORE_FLAGS_FILE_CACHE_SWEEP_REQUESTED;
- }
-
- continue;
- }
-
- for (i = 0; i < count; i++)
- {
- asl_message_match_and_log(work[i]);
- asl_free(work[i]);
- }
+ if (last_time.tv_sec != 0)
+ {
+ if (now.tv_sec > last_time.tv_sec)
+ {
+ now.tv_sec -= 1;
+ now.tv_usec += 1000000;
+ }
+
+ delta = now.tv_sec - last_time.tv_sec;
+ delta *= 1000000;
+ delta += (now.tv_usec - last_time.tv_usec);
+ if (delta < global.remote_delay_time)
+ {
+ usleep(delta);
+ }
+ }
- free(work);
+ if (now.tv_usec >= 1000000)
+ {
+ now.tv_sec += 1;
+ now.tv_usec -= 1000000;
+ }
- if (global.store_flags & STORE_FLAGS_FILE_CACHE_SWEEP_REQUESTED)
- {
- asl_store_sweep_file_cache(global.file_db);
- global.store_flags &= ~STORE_FLAGS_FILE_CACHE_SWEEP_REQUESTED;
+ last_time = now;
+ }
}
- empty->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSGH_BITS_ZERO);
- empty->header.msgh_remote_port = global.self_port;
- empty->header.msgh_local_port = MACH_PORT_NULL;
- empty->header.msgh_size = sizeof(mach_msg_empty_send_t);
- empty->header.msgh_id = SEND_NOTIFICATION;
-
- kstatus = mach_msg(&(empty->header), MACH_SEND_MSG | MACH_SEND_TIMEOUT, empty->header.msgh_size, 0, MACH_PORT_NULL, 100, MACH_PORT_NULL);
- }
-}
-
-void
-send_to_direct_watchers(asl_msg_t *msg)
-{
- uint32_t i, j, nlen, outlen, cleanup, total_sent;
- ssize_t sent;
- char *out;
-
-#ifdef LOCKDOWN
- if (global.lockdown_session_fd >= 0)
- {
- /* PurpleConsole eats newlines */
out = asl_format_message(msg, ASL_MSG_FMT_STD, ASL_TIME_FMT_LCL, ASL_ENCODE_SAFE, &outlen);
- if ((write(global.lockdown_session_fd, "\n", 1) < 0) ||
- (write(global.lockdown_session_fd, out, outlen) < 0) ||
- (write(global.lockdown_session_fd, "\n", 1) < 0))
+ if ((write(global.lockdown_session_fd, out, outlen) < 0) || (write(global.lockdown_session_fd, "\n", 1) < 0))
{
+ asldebug("send_to_direct_watchers: lockdown write error: %d %s\n", errno, strerror(errno));
close(global.lockdown_session_fd);
global.lockdown_session_fd = -1;
global.watchers_active = direct_watch_count + ((global.lockdown_session_fd < 0) ? 0 : 1);
}
free(out);
+
}
#endif
- if (direct_watch_count == 0) return;
+ if (direct_watch_count == 0)
+ {
+ direct_watch = NULL;
+ return;
+ }
+
+ if (direct_watch == NULL)
+ {
+ direct_watch_count = 0;
+ return;
+ }
cleanup = 0;
out = asl_msg_to_string(msg, &outlen);
else
{
total_sent = 0;
+ again = 0;
+
while (total_sent < outlen)
{
+ errno = 0;
sent = send(direct_watch[i], out + total_sent, outlen - total_sent, 0);
- if (sent < 0)
+ if (sent <= 0)
{
+ asldebug("send_to_direct_watchers: send returned %d (errno %d)\n", sent, errno);
+ if (errno == EAGAIN)
+ {
+ if (again > MAX_AGAIN)
+ {
+ asldebug("send_to_direct_watchers: exceeded EAGAIN limit - closing connection\n");
+ break;
+ }
+ else
+ {
+ again++;
+ errno = 0;
+ continue;
+ }
+ }
+
close(direct_watch[i]);
direct_watch[i] = -1;
cleanup = 1;
db_save_message(aslmsg msg)
{
uint64_t msgid;
- uint32_t status;
+ uint32_t status, dbtype;
+ static int armed;
+ static dispatch_source_t timer_src;
+ static dispatch_once_t once;
+
+ dispatch_once(&once, ^{
+ timer_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
+ dispatch_source_set_event_handler(timer_src, ^{
+ notify_post(kNotifyASLDBUpdate);
+ dispatch_suspend(timer_src);
+ armed = 0;
+ });
+ armed = 0;
+ });
send_to_direct_watchers((asl_msg_t *)msg);
- pthread_mutex_lock(&db_lock);
+ dbtype = global.dbtype;
+
+ if (asl_check_option(msg, ASL_OPT_DB_FILE)) dbtype |= DB_TYPE_FILE;
+ if (asl_check_option(msg, ASL_OPT_DB_MINI)) dbtype |= DB_TYPE_MINI;
+ if (asl_check_option(msg, ASL_OPT_DB_MEMORY)) dbtype |= DB_TYPE_MEMORY;
- db_asl_open();
+ db_asl_open(dbtype);
- if (global.dbtype & DB_TYPE_FILE)
+ if (dbtype & DB_TYPE_FILE)
{
status = asl_store_save(global.file_db, msg);
if (status != ASL_STATUS_OK)
asl_store_close(global.file_db);
global.file_db = NULL;
- db_asl_open();
+ db_asl_open(dbtype);
status = asl_store_save(global.file_db, msg);
if (status != ASL_STATUS_OK)
{
global.file_db = NULL;
global.dbtype |= DB_TYPE_MEMORY;
+ dbtype |= DB_TYPE_MEMORY;
if (global.memory_db == NULL)
{
status = asl_memory_open(global.db_memory_max, &(global.memory_db));
}
}
- if (global.dbtype & DB_TYPE_MEMORY)
+ if (dbtype & DB_TYPE_MEMORY)
{
msgid = 0;
status = asl_memory_save(global.memory_db, msg, &msgid);
asl_memory_close(global.memory_db);
global.memory_db = NULL;
- db_asl_open();
+ db_asl_open(dbtype);
msgid = 0;
status = asl_memory_save(global.memory_db, msg, &msgid);
if (status != ASL_STATUS_OK)
}
}
- if (global.dbtype & DB_TYPE_MINI)
+ if (dbtype & DB_TYPE_MINI)
{
status = asl_mini_memory_save(global.mini_db, msg, &msgid);
if (status != ASL_STATUS_OK)
asl_mini_memory_close(global.mini_db);
global.mini_db = NULL;
- db_asl_open();
+ db_asl_open(dbtype);
status = asl_mini_memory_save(global.mini_db, msg, &msgid);
if (status != ASL_STATUS_OK)
{
}
}
- pthread_mutex_unlock(&db_lock);
+ if (armed == 0)
+ {
+ armed = 1;
+ dispatch_source_set_timer(timer_src, dispatch_walltime(NULL, NSEC_PER_SEC / 2), DISPATCH_TIME_FOREVER, 0);
+ dispatch_resume(timer_src);
+ }
}
void
dir = SEARCH_FORWARD;
if (flags & QUERY_FLAG_SEARCH_REVERSE) dir = SEARCH_BACKWARD;
- pthread_mutex_lock(&db_lock);
-
status = ASL_STATUS_FAILED;
if (global.dbtype & DB_TYPE_MEMORY)
status = asl_mini_memory_match(global.mini_db, query, res, lastid, startid, ucount, dir);
}
- pthread_mutex_unlock(&db_lock);
-
return status;
}
static uint32_t
register_direct_watch(uint16_t port)
{
+#ifdef CONFIG_IPHONE
uint32_t i;
int sock, flags;
struct sockaddr_in address;
global.watchers_active = direct_watch_count + ((global.lockdown_session_fd < 0) ? 0 : 1);
return ASL_STATUS_OK;
+#else
+ return ASL_STATUS_FAILED;
+#endif
}
static void
cancel_direct_watch(uint16_t port)
{
+#ifdef CONFIG_IPHONE
uint32_t i;
for (i = 0; (i < direct_watch_count) && (port != direct_watch_port[i]); i++);
if (global.lockdown_session_fd >= 0) global.watchers_active = 1;
}
}
+#endif
}
/*
void
database_server()
{
- kern_return_t kstatus;
asl_request_msg *request;
- asl_reply_msg *reply;
- uint32_t rqs, rps;
+ uint32_t rqs;
uint32_t rbits, sbits;
- uint32_t flags, snooze;
+ uint32_t flags;
struct timeval now, send_time;
mach_dead_name_notification_t *deadname;
send_time.tv_usec = 0;
rqs = sizeof(asl_request_msg) + MAX_TRAILER_SIZE;
- rps = sizeof(asl_reply_msg) + MAX_TRAILER_SIZE;
- reply = (asl_reply_msg *)calloc(1, rps);
- if (reply == NULL) return;
rbits = MACH_RCV_MSG | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT) | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0);
sbits = MACH_SEND_MSG | MACH_SEND_TIMEOUT;
+ asl_server_queue = dispatch_queue_create("ASL Server Queue", NULL);
+
forever
{
- snooze = 0;
now.tv_sec = 0;
now.tv_usec = 0;
- /* Check if it's time to post a database change notification */
- if (send_time.tv_sec != 0)
- {
- gettimeofday(&now, NULL);
- if ((now.tv_sec > send_time.tv_sec) || ((now.tv_sec == send_time.tv_sec) && (now.tv_usec > send_time.tv_usec)))
- {
- notify_post(ASL_DB_NOTIFICATION);
- send_time.tv_sec = 0;
- send_time.tv_usec = 0;
- snooze = 0;
- }
- else
- {
- /* mach_msg timeout is in milliseconds */
- snooze = ((send_time.tv_sec - now.tv_sec) * 1000) + ((send_time.tv_usec - now.tv_usec) / 1000);
- }
- }
-
request = (asl_request_msg *)calloc(1, rqs);
if (request == NULL) continue;
request->head.msgh_local_port = global.server_port;
request->head.msgh_size = rqs;
- memset(reply, 0, rps);
-
flags = rbits;
- if (snooze != 0) flags |= MACH_RCV_TIMEOUT;
- kstatus = mach_msg(&(request->head), flags, 0, rqs, global.listen_set, snooze, MACH_PORT_NULL);
- if (request->head.msgh_id == SEND_NOTIFICATION)
- {
- if (send_time.tv_sec == 0)
- {
- gettimeofday(&send_time, NULL);
- send_time.tv_sec += 1;
- }
-
- free(request);
- continue;
- }
+ (void)mach_msg(&(request->head), flags, 0, rqs, global.listen_set, 0, MACH_PORT_NULL);
if (request->head.msgh_id == MACH_NOTIFY_DEAD_NAME)
{
deadname = (mach_dead_name_notification_t *)request;
- cancel_session(deadname->not_port);
+ dispatch_async(asl_server_queue, ^{
+ cancel_session(deadname->not_port);
+ /* dead name notification includes a dead name right */
+ mach_port_deallocate(mach_task_self(), deadname->not_port);
+ free(request);
+ });
- /* dead name notification includes a dead name right - release it here */
- mach_port_deallocate(mach_task_self(), deadname->not_port);
- free(request);
continue;
}
- kstatus = asl_ipc_server(&(request->head), &(reply->head));
- kstatus = mach_msg(&(reply->head), sbits, reply->head.msgh_size, 0, MACH_PORT_NULL, 10, MACH_PORT_NULL);
- if (kstatus == MACH_SEND_INVALID_DEST)
- {
- /* release send right for the port */
- mach_port_deallocate(mach_task_self(), request->head.msgh_remote_port);
- }
+ dispatch_async(asl_server_queue, ^{
+ kern_return_t ks;
+ asl_reply_msg *reply = calloc(1, sizeof(asl_reply_msg) + MAX_TRAILER_SIZE);
+
+ asl_ipc_server(&(request->head), &(reply->head));
+ ks = mach_msg(&(reply->head), sbits, reply->head.msgh_size, 0, MACH_PORT_NULL, 10, MACH_PORT_NULL);
+ free(reply);
+ if ((ks == MACH_SEND_INVALID_DEST) || (ks == MACH_SEND_TIMED_OUT))
+ {
+ /* clean up */
+ mach_msg_destroy(&(reply->head));
+ }
- free(request);
+ free(request);
+ });
}
}
uid = (uid_t)-1;
gid = (gid_t)-1;
- pid = (gid_t)-1;
+ pid = (pid_t)-1;
audit_token_to_au32(token, NULL, &uid, &gid, NULL, NULL, &pid, NULL, NULL);
client = MACH_PORT_NULL;
snprintf(tmp, sizeof(tmp), "%d", pid);
asl_set(msg, ASL_KEY_PID, tmp);
- /* verify and enqueue for processing */
- asl_enqueue_message(SOURCE_ASL_MESSAGE, NULL, msg);
+ dispatch_async(global.work_queue, ^{ process_message(msg, SOURCE_ASL_MESSAGE); });
return KERN_SUCCESS;
}
uid = (uid_t)-1;
gid = (gid_t)-1;
- pid = (gid_t)-1;
+ pid = (pid_t)-1;
audit_token_to_au32(token, NULL, &uid, &gid, NULL, NULL, &pid, NULL, NULL);
client = MACH_PORT_NULL;
)
{
uint16_t p16 = port;
+ pid_t pid = (pid_t)-1;
+
+ audit_token_to_au32(token, NULL, NULL, NULL, NULL, NULL, &pid, NULL, NULL);
- asldebug("__asl_server_register_direct_watch: %hu\n", ntohs(p16));
+ asldebug("__asl_server_register_direct_watch: pid %u port %hu\n", pid, ntohs(p16));
register_direct_watch(p16);
/*
- * Copyright (c) 2004-2010 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2011 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#define forever for(;;)
#define MY_ID "klog_in"
-#define MAXLINE 4096
+#define BUFF_SIZE 4096
-static int kx = 0;
-static char kline[MAXLINE + 1];
+static char inbuf[BUFF_SIZE];
+static int bx;
+static int kfd = -1;
+static dispatch_source_t in_src;
+static dispatch_queue_t in_queue;
-aslmsg
-klog_in_acceptmsg(int fd)
+void
+klog_in_acceptdata(int fd)
{
- int n;
- char c;
+ ssize_t len;
+ uint32_t i;
+ char *p, *q;
+ aslmsg m;
- n = read(fd, &c, 1);
+ len = read(fd, inbuf + bx, BUFF_SIZE - bx);
+ if (len <= 0) return;
- while ((n == 1) && (c != '\n'))
+ p = inbuf;
+ q = p + bx;
+
+ for (i = 0; i < len; i++, q++)
{
- if (kx < MAXLINE) kline[kx++] = c;
- n = read(fd, &c, 1);
+ if (*q == '\n')
+ {
+ *q = '\0';
+ m = asl_input_parse(p, q - p, NULL, SOURCE_KERN);
+ dispatch_async(global.work_queue, ^{ process_message(m, SOURCE_KERN); });
+ p = q + 1;
+ }
}
- if (kx == 0) return NULL;
-
- n = kx - 1;
- kline[kx] = '\0';
- kx = 0;
-
- return asl_input_parse(kline, n, NULL, SOURCE_KERN);
+ if (p != inbuf)
+ {
+ memmove(inbuf, p, BUFF_SIZE - bx - 1);
+ bx = q - p;
+ }
}
int
-klog_in_init(void)
+klog_in_init()
{
+ static dispatch_once_t once;
+
+ dispatch_once(&once, ^{
+ in_queue = dispatch_queue_create(MY_ID, NULL);
+ });
+
asldebug("%s: init\n", MY_ID);
- if (global.kfd >= 0) return global.kfd;
+ if (kfd >= 0) return 0;
- global.kfd = open(_PATH_KLOG, O_RDONLY, 0);
- if (global.kfd < 0)
+ 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(global.kfd, F_SETFL, O_NONBLOCK) < 0)
+ if (fcntl(kfd, F_SETFL, O_NONBLOCK) < 0)
{
- close(global.kfd);
- global.kfd = -1;
- asldebug("%s: couldn't set O_NONBLOCK for fd %d (%s): %s\n", MY_ID, global.kfd, _PATH_KLOG, strerror(errno));
+ 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(SOURCE_KERN, global.kfd, ADDFD_FLAGS_LOCAL, klog_in_acceptmsg, NULL, NULL);
+ in_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, (uintptr_t)kfd, 0, in_queue);
+ dispatch_source_set_event_handler(in_src, ^{ klog_in_acceptdata(kfd); });
+
+ dispatch_resume(in_src);
+ return 0;
}
int
klog_in_close(void)
{
- if (global.kfd < 0) return 1;
+ if (kfd < 0) return 1;
+
+ dispatch_source_cancel(in_src);
+ dispatch_release(in_src);
+ in_src = NULL;
- aslevent_removefd(global.kfd);
- close(global.kfd);
- global.kfd = -1;
+ close(kfd);
+ kfd = -1;
return 0;
}
/*
- * Copyright (c) 2004-2010 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2011 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <netdb.h>
#include <pthread.h>
#include <notify.h>
-#include <asl_core.h>
-#include "asl_memory.h"
#include "daemon.h"
#define forever for(;;)
static int rfd6 = -1;
static int rfdl = -1;
+static dispatch_source_t in_src_local;
+static dispatch_source_t in_src_tcp;
+static dispatch_queue_t in_queue;
+
#ifdef NSS32
typedef uint32_t notify_state_t;
extern int notify_set_state(int, notify_state_t);
void
session(void *x)
{
- int i, s, wfd, status, pfmt, watch, wtoken, nfd, do_prompt, filter;
+ int i, s, wfd, status, pfmt, watch, wtoken, nfd, do_prompt;
aslresponse res;
asl_search_result_t ql;
uint32_t outlen;
asl_msg_t *qlq[1];
char str[1024], *p, *qs, *out;
ssize_t len;
- fd_set readfds;
+ fd_set readfds, errfds;
uint64_t low_id, high_id;
- notify_state_t nstate;
uint32_t dbselect, flags;
session_args_t *sp;
flags = sp->flags;
free(x);
+ asldebug("%s %d: starting interactive session for %ssocket %d\n", MY_ID, s, (flags & SESSION_FLAGS_LOCKDOWN) ? "lockdown " : "", s);
+
+ do_prompt = 1;
watch = WATCH_OFF;
wfd = -1;
wtoken = -1;
query = NULL;
memset(&ql, 0, sizeof(asl_search_result_t));
- snprintf(str, sizeof(str) - 1, "\n========================\nASL is here to serve you\n");
+ snprintf(str, sizeof(str), "\n========================\nASL is here to serve you\n");
if (write(s, str, strlen(str)) < 0)
{
close(s);
return;
}
- do_prompt = 1;
-
forever
{
- if (do_prompt > 0)
+ if (((flags & SESSION_FLAGS_LOCKDOWN) == 0) && (do_prompt > 0))
{
- snprintf(str, sizeof(str) - 1, "> ");
+ snprintf(str, sizeof(str), "> ");
SESSION_WRITE(s, str);
}
do_prompt = 1;
+
memset(str, 0, sizeof(str));
FD_ZERO(&readfds);
FD_SET(s, &readfds);
+ FD_ZERO(&errfds);
+ FD_SET(s, &errfds);
nfd = s;
if (wfd != -1)
if (wfd > nfd) nfd = wfd;
}
- status = select(nfd + 1, &readfds, NULL, NULL, NULL);
+ status = select(nfd + 1, &readfds, NULL, &errfds, NULL);
+ if (status == 0) continue;
+ if (status < 0)
+ {
+ asldebug("%s %d: select %d %s\n", MY_ID, s, errno, strerror(errno));
+ goto exit_session;
+ }
+
+ if (FD_ISSET(s, &errfds))
+ {
+ asldebug("%s %d: error on socket %d\n", MY_ID, s, s);
+ goto exit_session;
+ }
if ((wfd != -1) && (FD_ISSET(wfd, &readfds)))
{
- len = read(wfd, &i, sizeof(int));
+ (void)read(wfd, &i, sizeof(int));
}
if (FD_ISSET(s, &readfds))
{
len = read(s, str, sizeof(str) - 1);
- if (len <= 0) goto exit_session;
+ if (len <= 0)
+ {
+ asldebug("%s %d: read error on socket %d: %d %s\n", MY_ID, s, s, errno, strerror(errno));
+ goto exit_session;
+ }
while ((len > 1) && ((str[len - 1] == '\n') || (str[len - 1] == '\r')))
{
if ((!strcmp(str, "q")) || (!strcmp(str, "quit")) || (!strcmp(str, "exit")))
{
- snprintf(str, sizeof(str) - 1, "Goodbye\n");
+ snprintf(str, sizeof(str), "Goodbye\n");
write(s, str, strlen(str));
close(s);
s = -1;
if ((!strcmp(str, "?")) || (!strcmp(str, "help")))
{
- snprintf(str, sizeof(str) - 1, "Commands\n");
- SESSION_WRITE(s, str);
- snprintf(str, sizeof(str) - 1, " quit exit session\n");
- SESSION_WRITE(s, str);
- snprintf(str, sizeof(str) - 1, " select [val] get [set] current database\n");
+ snprintf(str, sizeof(str), "Commands\n");
SESSION_WRITE(s, str);
- snprintf(str, sizeof(str) - 1, " val must be \"file\", \"mem\", or \"mini\"\n");
+ snprintf(str, sizeof(str), " quit exit session\n");
SESSION_WRITE(s, str);
- snprintf(str, sizeof(str) - 1, " file [on/off] enable / disable file store\n");
+ snprintf(str, sizeof(str), " select [val] get [set] current database\n");
SESSION_WRITE(s, str);
- snprintf(str, sizeof(str) - 1, " memory [on/off] enable / disable memory store\n");
+ snprintf(str, sizeof(str), " val must be \"file\", \"mem\", or \"mini\"\n");
SESSION_WRITE(s, str);
- snprintf(str, sizeof(str) - 1, " mini [on/off] enable / disable mini memory store\n");
+ snprintf(str, sizeof(str), " file [on/off] enable / disable file store\n");
SESSION_WRITE(s, str);
- snprintf(str, sizeof(str) - 1, " stats database statistics\n");
+ snprintf(str, sizeof(str), " memory [on/off] enable / disable memory store\n");
SESSION_WRITE(s, str);
- snprintf(str, sizeof(str) - 1, " flush flush database\n");
+ snprintf(str, sizeof(str), " mini [on/off] enable / disable mini memory store\n");
SESSION_WRITE(s, str);
- snprintf(str, sizeof(str) - 1, " dbsize [val] get [set] database size (# of records)\n");
+ snprintf(str, sizeof(str), " stats database statistics\n");
SESSION_WRITE(s, str);
- snprintf(str, sizeof(str) - 1, " filter [val] get [set] current database filter\n");
+ snprintf(str, sizeof(str), " flush flush database\n");
SESSION_WRITE(s, str);
- snprintf(str, sizeof(str) - 1, " [p]anic (emergency) [a]lert [c]ritical [e]rror\n");
+ snprintf(str, sizeof(str), " dbsize [val] get [set] database size (# of records)\n");
SESSION_WRITE(s, str);
- snprintf(str, sizeof(str) - 1, " [w]arning [n]otice [i]nfo [d]ebug\n");
+ snprintf(str, sizeof(str), " watch print new messages as they arrive\n");
SESSION_WRITE(s, str);
- snprintf(str, sizeof(str) - 1, " watch print new messages as they arrive\n");
+ snprintf(str, sizeof(str), " stop stop watching for new messages\n");
SESSION_WRITE(s, str);
- snprintf(str, sizeof(str) - 1, " stop stop watching for new messages\n");
+ snprintf(str, sizeof(str), " raw use raw format for printing messages\n");
SESSION_WRITE(s, str);
- snprintf(str, sizeof(str) - 1, " raw use raw format for printing messages\n");
+ snprintf(str, sizeof(str), " std use standard format for printing messages\n");
SESSION_WRITE(s, str);
- snprintf(str, sizeof(str) - 1, " std use standard format for printing messages\n");
+ snprintf(str, sizeof(str), " * show all log messages\n");
SESSION_WRITE(s, str);
- snprintf(str, sizeof(str) - 1, " * show all log messages\n");
+ snprintf(str, sizeof(str), " * key val equality search for messages (single key/value pair)\n");
SESSION_WRITE(s, str);
- snprintf(str, sizeof(str) - 1, " * key val equality search for messages (single key/value pair)\n");
+ snprintf(str, sizeof(str), " * op key val search for matching messages (single key/value pair)\n");
SESSION_WRITE(s, str);
- snprintf(str, sizeof(str) - 1, " * op key val search for matching messages (single key/value pair)\n");
+ snprintf(str, sizeof(str), " * [op key val] ... search for matching messages (multiple key/value pairs)\n");
SESSION_WRITE(s, str);
- snprintf(str, sizeof(str) - 1, " * [op key val] ... search for matching messages (multiple key/value pairs)\n");
+ snprintf(str, sizeof(str), " operators: = < > ! (not equal) T (key exists) R (regex)\n");
SESSION_WRITE(s, str);
- snprintf(str, sizeof(str) - 1, " operators: = < > ! (not equal) T (key exists) R (regex)\n");
+ snprintf(str, sizeof(str), " modifiers (must follow operator):\n");
SESSION_WRITE(s, str);
- snprintf(str, sizeof(str) - 1, " modifiers (must follow operator):\n");
+ snprintf(str, sizeof(str), " C=casefold N=numeric S=substring A=prefix Z=suffix\n");
SESSION_WRITE(s, str);
- snprintf(str, sizeof(str) - 1, " C=casefold N=numeric S=substring A=prefix Z=suffix\n");
- SESSION_WRITE(s, str);
- snprintf(str, sizeof(str) - 1, "\n");
+ snprintf(str, sizeof(str), "\n");
SESSION_WRITE(s, str);
continue;
}
while ((*p == ' ') || (*p == '\t')) p++;
if (*p == '\0')
{
- if (dbselect == 0) snprintf(str, sizeof(str) - 1, "no store\n");
- else if (dbselect == DB_TYPE_FILE) snprintf(str, sizeof(str) - 1, "file store\n");
- else if (dbselect == DB_TYPE_MEMORY) snprintf(str, sizeof(str) - 1, "memory store\n");
- else if (dbselect == DB_TYPE_MINI) snprintf(str, sizeof(str) - 1, "mini memory store\n");
+ if (dbselect == 0) snprintf(str, sizeof(str), "no store\n");
+ else if (dbselect == DB_TYPE_FILE) snprintf(str, sizeof(str), "file store\n");
+ else if (dbselect == DB_TYPE_MEMORY) snprintf(str, sizeof(str), "memory store\n");
+ else if (dbselect == DB_TYPE_MINI) snprintf(str, sizeof(str), "mini memory store\n");
SESSION_WRITE(s, str);
continue;
}
{
if ((global.dbtype & DB_TYPE_FILE) == 0)
{
- snprintf(str, sizeof(str) - 1, "file database is not enabled\n");
+ snprintf(str, sizeof(str), "file database is not enabled\n");
SESSION_WRITE(s, str);
continue;
}
{
if ((global.dbtype & DB_TYPE_MEMORY) == 0)
{
- snprintf(str, sizeof(str) - 1, "memory database is not enabled\n");
+ snprintf(str, sizeof(str), "memory database is not enabled\n");
SESSION_WRITE(s, str);
continue;
}
{
if (global.mini_db != NULL)
{
- snprintf(str, sizeof(str) - 1, "mini memory database is enabled for disaster messages\n");
+ snprintf(str, sizeof(str), "mini memory database is enabled for disaster messages\n");
SESSION_WRITE(s, str);
}
else
{
- snprintf(str, sizeof(str) - 1, "mini memory database is not enabled\n");
+ snprintf(str, sizeof(str), "mini memory database is not enabled\n");
SESSION_WRITE(s, str);
continue;
}
}
else
{
- snprintf(str, sizeof(str) - 1, "unknown database type\n");
+ snprintf(str, sizeof(str), "unknown database type\n");
SESSION_WRITE(s, str);
continue;
}
- snprintf(str, sizeof(str) - 1, "OK\n");
+ snprintf(str, sizeof(str), "OK\n");
SESSION_WRITE(s, str);
continue;
}
while ((*p == ' ') || (*p == '\t')) p++;
if (*p == '\0')
{
- snprintf(str, sizeof(str) - 1, "file database is %senabled\n", (global.dbtype & DB_TYPE_FILE) ? "" : "not ");
+ snprintf(str, sizeof(str), "file database is %senabled\n", (global.dbtype & DB_TYPE_FILE) ? "" : "not ");
SESSION_WRITE(s, str);
if ((global.dbtype & DB_TYPE_FILE) != 0) dbselect = DB_TYPE_FILE;
continue;
if (!strcmp(p, "on")) global.dbtype |= DB_TYPE_FILE;
else if (!strcmp(p, "off")) global.dbtype &= ~ DB_TYPE_FILE;
- snprintf(str, sizeof(str) - 1, "OK\n");
+ snprintf(str, sizeof(str), "OK\n");
SESSION_WRITE(s, str);
continue;
}
while ((*p == ' ') || (*p == '\t')) p++;
if (*p == '\0')
{
- snprintf(str, sizeof(str) - 1, "memory database is %senabled\n", (global.dbtype & DB_TYPE_MEMORY) ? "" : "not ");
+ snprintf(str, sizeof(str), "memory database is %senabled\n", (global.dbtype & DB_TYPE_MEMORY) ? "" : "not ");
SESSION_WRITE(s, str);
if ((global.dbtype & DB_TYPE_MEMORY) != 0) dbselect = DB_TYPE_MEMORY;
continue;
if (!strcmp(p, "on")) global.dbtype |= DB_TYPE_MEMORY;
else if (!strcmp(p, "off")) global.dbtype &= ~ DB_TYPE_MEMORY;
- snprintf(str, sizeof(str) - 1, "OK\n");
+ snprintf(str, sizeof(str), "OK\n");
SESSION_WRITE(s, str);
continue;
}
while ((*p == ' ') || (*p == '\t')) p++;
if (*p == '\0')
{
- snprintf(str, sizeof(str) - 1, "mini database is %senabled\n", (global.dbtype & DB_TYPE_MINI) ? "" : "not ");
+ snprintf(str, sizeof(str), "mini database is %senabled\n", (global.dbtype & DB_TYPE_MINI) ? "" : "not ");
SESSION_WRITE(s, str);
if ((global.dbtype & DB_TYPE_MINI) != 0) dbselect = DB_TYPE_MINI;
continue;
if (!strcmp(p, "on")) global.dbtype |= DB_TYPE_MINI;
else if (!strcmp(p, "off")) global.dbtype &= ~ DB_TYPE_MINI;
- snprintf(str, sizeof(str) - 1, "OK\n");
+ snprintf(str, sizeof(str), "OK\n");
SESSION_WRITE(s, str);
continue;
}
{
if (dbselect == 0)
{
- snprintf(str, sizeof(str) - 1, "no store\n");
+ snprintf(str, sizeof(str), "no store\n");
SESSION_WRITE(s, str);
continue;
}
while ((*p == ' ') || (*p == '\t')) p++;
if (*p == '\0')
{
- snprintf(str, sizeof(str) - 1, "DB size %u\n", remote_db_size(dbselect));
+ snprintf(str, sizeof(str), "DB size %u\n", remote_db_size(dbselect));
SESSION_WRITE(s, str);
continue;
}
i = atoi(p);
remote_db_set_size(dbselect, i);
- snprintf(str, sizeof(str) - 1, "OK\n");
+ snprintf(str, sizeof(str), "OK\n");
SESSION_WRITE(s, str);
continue;
}
- else if (!strncmp(str, "filter", 6))
- {
- p = str + 6;
- while ((*p == ' ') || (*p == '\t')) p++;
- if (*p == '\0')
- {
- snprintf(str, sizeof(str) - 1, "%s%s%s%s%s%s%s%s\n",
- (global.asl_log_filter & ASL_FILTER_MASK_EMERG) ? "p" : "",
- (global.asl_log_filter & ASL_FILTER_MASK_ALERT) ? "a" : "",
- (global.asl_log_filter & ASL_FILTER_MASK_CRIT) ? "c" : "",
- (global.asl_log_filter & ASL_FILTER_MASK_ERR) ? "e" : "",
- (global.asl_log_filter & ASL_FILTER_MASK_WARNING) ? "w" : "",
- (global.asl_log_filter & ASL_FILTER_MASK_NOTICE) ? "n" : "",
- (global.asl_log_filter & ASL_FILTER_MASK_INFO) ? "i" : "",
- (global.asl_log_filter & ASL_FILTER_MASK_DEBUG) ? "d" : "");
- SESSION_WRITE(s, str);
- continue;
- }
-
- filter = 0;
- if ((*p >= '0') && (*p <= '7'))
- {
- i = atoi(p);
- filter = ASL_FILTER_MASK_UPTO(i);
- }
- else
- {
- while (*p != '\0')
- {
- if ((*p == 'p') || (*p == 'P')) filter |= ASL_FILTER_MASK_EMERG;
- else if ((*p == 'a') || (*p == 'A')) filter |= ASL_FILTER_MASK_ALERT;
- else if ((*p == 'c') || (*p == 'C')) filter |= ASL_FILTER_MASK_CRIT;
- else if ((*p == 'e') || (*p == 'E')) filter |= ASL_FILTER_MASK_ERR;
- else if ((*p == 'w') || (*p == 'W')) filter |= ASL_FILTER_MASK_WARNING;
- else if ((*p == 'n') || (*p == 'N')) filter |= ASL_FILTER_MASK_NOTICE;
- else if ((*p == 'i') || (*p == 'I')) filter |= ASL_FILTER_MASK_INFO;
- else if ((*p == 'd') || (*p == 'D')) filter |= ASL_FILTER_MASK_DEBUG;
- p++;
- }
- }
-
- status = notify_register_check(NOTIFY_SYSTEM_ASL_FILTER, &i);
- if (status != NOTIFY_STATUS_OK)
- {
- snprintf(str, sizeof(str) - 1, "FAILED %d\n", status);
- SESSION_WRITE(s, str);
- }
- else
- {
- nstate = filter;
- status = notify_set_state(i, nstate);
- if (status != NOTIFY_STATUS_OK)
- {
- snprintf(str, sizeof(str) - 1, "FAILED %d\n", status);
- SESSION_WRITE(s, str);
- continue;
- }
-
- status = notify_post(NOTIFY_SYSTEM_ASL_FILTER);
- notify_cancel(i);
-
- global.asl_log_filter = filter;
-
- snprintf(str, sizeof(str) - 1, "OK\n");
- SESSION_WRITE(s, str);
- }
-
- continue;
- }
else if (!strcmp(str, "stop"))
{
if (watch != WATCH_OFF)
if (query != NULL) free(query);
query = NULL;
- snprintf(str, sizeof(str) - 1, "OK\n");
+ snprintf(str, sizeof(str), "OK\n");
SESSION_WRITE(s, str);
continue;
}
- snprintf(str, sizeof(str) - 1, "not watching!\n");
+ snprintf(str, sizeof(str), "not watching!\n");
SESSION_WRITE(s, str);
continue;
}
}
else if (!strcmp(str, "watch"))
{
- if (watch != WATCH_OFF)
+ if (((flags & SESSION_FLAGS_LOCKDOWN) == 0) && (watch != WATCH_OFF))
{
- snprintf(str, sizeof(str) - 1, "already watching!\n");
+ snprintf(str, sizeof(str), "already watching!\n");
SESSION_WRITE(s, str);
continue;
}
}
else
{
- status = notify_register_file_descriptor(ASL_DB_NOTIFICATION, &wfd, 0, &wtoken);
+ status = notify_register_file_descriptor(kNotifyASLDBUpdate, &wfd, 0, &wtoken);
if (status != 0)
{
- snprintf(str, sizeof(str) - 1, "notify_register_file_descriptor failed: %d\n", status);
+ snprintf(str, sizeof(str), "notify_register_file_descriptor failed: %d\n", status);
SESSION_WRITE(s, str);
continue;
}
watch = WATCH_RUN;
}
- snprintf(str, sizeof(str) - 1, "OK\n");
+ snprintf(str, sizeof(str), "OK\n");
SESSION_WRITE(s, str);
do_prompt = 2;
}
}
else
{
- snprintf(str, sizeof(str) - 1, "unrecognized command\n");
+ snprintf(str, sizeof(str), "unrecognized command\n");
SESSION_WRITE(s, str);
- snprintf(str, sizeof(str) - 1, "enter \"help\" for help\n");
+ snprintf(str, sizeof(str), "enter \"help\" for help\n");
SESSION_WRITE(s, str);
continue;
}
* the initial messages are sent to PurpleConsole before setting
* global.lockdown_session_fd to allow this query to complete before
* dbserver starts sending. To prevent a race between this query and
- * when the first message is sent by send_to_direct_watchers, we hold
- * the work queue lock between the time of the query and the time that
- * lockdown_session_fd is set.
+ * when messages are sent by send_to_direct_watchers, we suspend the
+ * work queue here and resume it when lockdown_session_fd is set.
*/
if ((flags & SESSION_FLAGS_LOCKDOWN) && (watch == WATCH_RUN)) continue;
- if (watch == WATCH_LOCKDOWN_START)
- {
- pthread_mutex_lock(global.work_queue_lock);
- }
+ if (watch == WATCH_LOCKDOWN_START) dispatch_suspend(global.work_queue);
if (query != NULL)
{
memset(&res, 0, sizeof(aslresponse));
high_id = 0;
- status = db_query(&ql, (aslresponse *)&res, low_id, 0, 0, &high_id, 0, 0);
+ (void)db_query(&ql, (aslresponse *)&res, low_id, 0, 0, &high_id, 0, 0);
if ((watch == WATCH_RUN) && (high_id >= low_id)) low_id = high_id + 1;
{
if (watch == WATCH_OFF)
{
- snprintf(str, sizeof(str) - 1, "-nil-\n");
+ snprintf(str, sizeof(str), "-nil-\n");
SESSION_WRITE(s, str);
}
else
{
if (watch == WATCH_RUN)
{
- snprintf(str, sizeof(str) - 1, "\n");
+ snprintf(str, sizeof(str), "\n");
SESSION_WRITE(s, str);
}
write(s, out, outlen);
free(out);
- snprintf(str, sizeof(str) - 1, "\n");
+ snprintf(str, sizeof(str), "\n");
SESSION_WRITE(s, str);
}
else
{
if (watch == WATCH_RUN)
{
- snprintf(str, sizeof(str) - 1, "\n");
+ snprintf(str, sizeof(str), "\n");
SESSION_WRITE(s, str);
}
+ snprintf(str, sizeof(str), "\n");
for (i = 0; i < res->count; i++)
{
+ int wstatus;
+
out = asl_format_message(res->msg[i], ASL_MSG_FMT_STD, ASL_TIME_FMT_LCL, ASL_ENCODE_SAFE, &outlen);
- write(s, out, outlen);
+
+ do
+ {
+ int n = 0;
+
+ errno = 0;
+ wstatus = write(s, out, outlen);
+ if (wstatus < 0)
+ {
+ asldebug("%s %d: %d/%d write data failed: %d %s\n", MY_ID, s, i, res->count, errno, strerror(errno));
+ if (errno == EAGAIN)
+ {
+ n++;
+ if (n > 10) break;
+ usleep(10000);
+ }
+ else
+ {
+ goto exit_session;
+ }
+ }
+ } while (errno == EAGAIN);
+
free(out);
+ if (global.remote_delay_time > 0) usleep(global.remote_delay_time);
}
}
global.watchers_active++;
watch = WATCH_RUN;
- pthread_mutex_unlock(global.work_queue_lock);
+ dispatch_resume(global.work_queue);
}
}
exit_session:
+ asldebug("%s %d: terminating session for %ssocket %d\n", MY_ID, s, (flags & SESSION_FLAGS_LOCKDOWN) ? "lockdown " : "", s);
+
if (s >= 0)
{
if (s == global.lockdown_session_fd) global.lockdown_session_fd = -1;
if (global.watchers_active > 0) global.watchers_active--;
close(s);
- s = -1;
}
- if (watch != WATCH_OFF) notify_cancel(wtoken);
+ if (watch == WATCH_LOCKDOWN_START) dispatch_resume(global.work_queue);
+ if (wtoken >= 0) notify_cancel(wtoken);
if (query != NULL) asl_msg_release(query);
pthread_exit(NULL);
}
remote_acceptmsg(int fd, int tcp)
{
socklen_t fromlen;
- int s, status, flags, v;
+ int s, flags, status, v;
pthread_attr_t attr;
pthread_t t;
struct sockaddr_storage from;
chmod(SYSLOG_SOCK_PATH, 0666);
- aslevent_addfd(SOURCE_SESSION, fd, 0, remote_acceptmsg_local, NULL, NULL);
+ in_src_local = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, (uintptr_t)fd, 0, in_queue);
+ dispatch_source_set_event_handler(in_src_local, ^{ remote_acceptmsg_local(fd); });
+ dispatch_resume(in_src_local);
+
return fd;
}
return -1;
}
- aslevent_addfd(SOURCE_SESSION, fd, 0, remote_acceptmsg_tcp, NULL, NULL);
+ in_src_tcp = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, (uintptr_t)fd, 0, in_queue);
+ dispatch_source_set_event_handler(in_src_tcp, ^{ remote_acceptmsg_tcp(fd); });
+ dispatch_resume(in_src_tcp);
+
return fd;
}
int
remote_init(void)
{
+ static dispatch_once_t once;
+
+ dispatch_once(&once, ^{
+ in_queue = dispatch_queue_create(MY_ID, NULL);
+ });
+
asldebug("%s: init\n", MY_ID);
#ifdef LOCKDOWN
{
if (rfdl >= 0)
{
- aslevent_removefd(rfdl);
close(rfdl);
}
if (rfd4 >= 0)
{
- aslevent_removefd(rfd4);
close(rfd4);
}
if (rfd6 >= 0)
{
- aslevent_removefd(rfd6);
close(rfd6);
}
int
remote_reset(void)
{
+ return 0;
+
remote_close();
return remote_init();
}
-.\"Copyright (c) 2004-2008 Apple Inc. All rights reserved.
+.\"Copyright (c) 2004-2011 Apple Inc. All rights reserved.
.\"
.\"@APPLE_LICENSE_HEADER_START@
.\"
.Op Fl d
.Op Fl D
.Op Fl m Ar mark_interval
-.Op Fl c Ar log_cutoff
.Op Fl l Ar lib_path
.Op Fl db_max Ar size
.Op Fl utmp_ttl Ar time
-.Op Fl fs_ttl Ar time
.Op Fl mps_limit Ar quota
.Op Fl dup_delay Ar time
.Op Fl module_name Li {0|1}
The
.Dq mark
facility is disabled if the setting is zero minutes.
-.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 value is set to allow all message priorities.
-Message filtering is primarily specified by the rules in the /etc/asl.conf file.
-However, if there are no matching rules for the ASL data store in the asl.conf file,
-then all messages that are allowed by the cutoff filter are saved.
-.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.
-.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 db_max
Sets the size limit in bytes for individual files in the data store.
The default value for
.Xr aslmanager 8
manual), these messages will be copied to an archive
after the regular time-to-live interval, but will persist in the data store until their own expiry time.
-.It Fl fs_ttl
-Sets the time-to-live in seconds for filesystem error messages generated by the kernel.
-The default is 31622400 seconds (approximately 1 year).
-As in the case of
-.Fl utmp_ttl ,
-if archival is enabled, these messages will be copied to an archive after the regular time-to-live
-interval but will persist in the data store until their own expiry time.
.It Fl mps_limit
Sets the per-process quota for messages per second allowed by
.Nm .
/*
- * Copyright (c) 2004-2010 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2011 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <dlfcn.h>
#include <libgen.h>
#include <notify.h>
+#include <notify_keys.h>
#include <utmpx.h>
+#include <vproc_priv.h>
#include "daemon.h"
#define SERVICE_NAME "com.apple.system.logger"
#define SERVER_STATUS_ACTIVE 1
#define SERVER_STATUS_ON_DEMAND 2
-#define DEFAULT_MARK_SEC 0
-#define DEFAULT_UTMP_TTL_SEC 31622400
-#define DEFAULT_FS_TTL_SEC 31622400
-#define DEFAULT_BSD_MAX_DUP_SEC 30
-#define DEFAULT_MPS_LIMIT 500
#define BILLION 1000000000
#define NOTIFY_DELAY 1
-#define NETWORK_CHANGE_NOTIFICATION "com.apple.system.config.network_change"
-
#define streq(A,B) (strcmp(A,B)==0)
#define forever for(;;)
-static uint64_t time_start = 0;
-static uint64_t mark_last = 0;
-static uint64_t ping_last = 0;
-static uint64_t time_last = 0;
-
extern int __notify_78945668_info__;
extern int _malloc_no_asl_log;
-static TAILQ_HEAD(ml, module_list) Moduleq;
-
/* global */
struct global_s global;
-/* 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();
+/* Input Modules */
+int klog_in_init(void);
+int klog_in_reset(void);
+int klog_in_close(void);
static int activate_klog_in = 1;
-int bsd_in_init();
-int bsd_in_reset();
-int bsd_in_close();
+int bsd_in_init(void);
+int bsd_in_reset(void);
+int bsd_in_close(void);
static int activate_bsd_in = 1;
-int bsd_out_init();
-int bsd_out_reset();
-int bsd_out_close();
+int udp_in_init(void);
+int udp_in_reset(void);
+int udp_in_close(void);
+static int activate_udp_in = 1;
+
+/* Output Modules */
+int bsd_out_init(void);
+int bsd_out_reset(void);
+int bsd_out_close(void);
static int activate_bsd_out = 1;
-int remote_init();
-int remote_reset();
-int remote_close();
-static int activate_remote = 0;
+int asl_action_init(void);
+int asl_action_reset(void);
+int asl_action_close(void);
+static int activate_asl_action = 1;
-int udp_in_init();
-int udp_in_reset();
-int udp_in_close();
-static int activate_udp_in = 1;
+/* Interactive Module */
+int remote_init(void);
+int remote_reset(void);
+int remote_close(void);
+static int remote_enabled = 0;
extern void database_server();
-extern void output_worker();
-extern void launchd_drain();
-extern void bsd_flush_duplicates(time_t now);
-extern void bsd_close_idle_files(time_t now);
-/*
- * 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()
+static void
+init_modules()
{
- struct module_list *tmp;
+ module_t *m_klog_in, *m_bsd_in, *m_bsd_out, *m_udp_in;
+ module_t *m_asl, *m_remote;
- /*
- * The order of these initializations is important.
- * When messages are sent to output modules, they are
- * sent in the same order as these initializations.
- * asl_action may add modify messages, for example to
- * add access controls, so it must come first.
- */
- if (activate_asl_action != 0)
+ /* ASL module (configured by /etc/asl.conf) */
+ m_asl = (module_t *)calloc(1, sizeof(module_t));
+ if (m_asl == NULL)
{
- tmp = calloc(1, sizeof(struct module_list));
- if (tmp == NULL) return 1;
-
- tmp->name = strdup("asl_action");
- if (tmp->name == NULL)
- {
- free(tmp);
- return 1;
- }
-
- tmp->module = NULL;
- tmp->init = asl_action_init;
- tmp->reset = asl_action_reset;
- tmp->close = asl_action_close;
- TAILQ_INSERT_TAIL(&Moduleq, tmp, entries);
+ asldebug("alloc failed (init_modules asl_action)\n");
+ exit(1);
}
- if (activate_asl_in != 0)
- {
- tmp = calloc(1, sizeof(struct module_list));
- if (tmp == NULL) return 1;
+ m_asl->name = "asl_action";
+ m_asl->enabled = activate_asl_action;
+ m_asl->init = asl_action_init;
+ m_asl->reset = asl_action_reset;
+ m_asl->close = asl_action_close;
- tmp->name = strdup("asl_in");
- if (tmp->name == NULL)
- {
- free(tmp);
- return 1;
- }
-
- 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 (m_asl->enabled) m_asl->init();
- if (activate_klog_in != 0)
+ /* BSD output module (configured by /etc/syslog.conf) */
+ m_bsd_out = (module_t *)calloc(1, sizeof(module_t));
+ if (m_bsd_out == NULL)
{
- tmp = calloc(1, sizeof(struct module_list));
- if (tmp == NULL) return 1;
-
- tmp->name = strdup("klog_in");
- if (tmp->name == NULL)
- {
- free(tmp);
- return 1;
- }
-
- tmp->module = NULL;
- tmp->init = klog_in_init;
- tmp->reset = klog_in_reset;
- tmp->close = klog_in_close;
- TAILQ_INSERT_TAIL(&Moduleq, tmp, entries);
+ asldebug("alloc failed (init_modules bsd_out)\n");
+ exit(1);
}
- if (activate_bsd_in != 0)
- {
- tmp = calloc(1, sizeof(struct module_list));
- if (tmp == NULL) return 1;
-
- tmp->name = strdup("bsd_in");
- if (tmp->name == NULL)
- {
- free(tmp);
- return 1;
- }
-
- tmp->module = NULL;
- tmp->init = bsd_in_init;
- tmp->reset = bsd_in_reset;
- tmp->close = bsd_in_close;
- TAILQ_INSERT_TAIL(&Moduleq, tmp, entries);
- }
+ m_bsd_out->name = "bsd_out";
+ m_bsd_out->enabled = activate_bsd_out;
+ m_bsd_out->init = bsd_out_init;
+ m_bsd_out->reset = bsd_out_reset;
+ m_bsd_out->close = bsd_out_close;
- if (activate_bsd_out != 0)
+ if (m_bsd_out->enabled)
{
- tmp = calloc(1, sizeof(struct module_list));
- if (tmp == NULL) return 1;
-
- tmp->name = strdup("bsd_out");
- if (tmp->name == NULL)
- {
- free(tmp);
- return 1;
- }
-
- tmp->module = NULL;
- tmp->init = bsd_out_init;
- tmp->reset = bsd_out_reset;
- tmp->close = bsd_out_close;
- TAILQ_INSERT_TAIL(&Moduleq, tmp, entries);
+ m_bsd_out->init();
+ global.bsd_out_enabled = 1;
}
- if (activate_remote != 0)
+ /* kernel input module */
+ m_klog_in = (module_t *)calloc(1, sizeof(module_t));
+ if (m_klog_in == NULL)
{
- tmp = calloc(1, sizeof(struct module_list));
- if (tmp == NULL) return 1;
-
- tmp->name = strdup("remote");
- if (tmp->name == NULL)
- {
- free(tmp);
- return 1;
- }
-
- tmp->module = NULL;
- tmp->init = remote_init;
- tmp->reset = remote_reset;
- tmp->close = remote_close;
- TAILQ_INSERT_TAIL(&Moduleq, tmp, entries);
+ asldebug("alloc failed (init_modules klog_in)\n");
+ exit(1);
}
- if (activate_udp_in != 0)
- {
- tmp = calloc(1, sizeof(struct module_list));
- if (tmp == NULL) return 1;
+ m_klog_in->name = "klog_in";
+ m_klog_in->enabled = activate_klog_in;
+ m_klog_in->init = klog_in_init;
+ m_klog_in->reset = klog_in_reset;
+ m_klog_in->close = klog_in_close;
- tmp->name = strdup("udp_in");
- if (tmp->name == NULL)
- {
- free(tmp);
- return 1;
- }
+ if (m_klog_in->enabled) m_klog_in->init();
- tmp->module = NULL;
- tmp->init = udp_in_init;
- tmp->reset = udp_in_reset;
- tmp->close = udp_in_close;
- TAILQ_INSERT_TAIL(&Moduleq, tmp, entries);
+ /* BSD (UNIX domain socket) input module */
+ m_bsd_in = (module_t *)calloc(1, sizeof(module_t));
+ if (m_bsd_in == NULL)
+ {
+ asldebug("alloc failed (init_modules bsd_in)\n");
+ exit(1);
}
- return 0;
-}
+ m_bsd_in->name = "bsd_in";
+ m_bsd_in->enabled = activate_bsd_in;
+ m_bsd_in->init = bsd_in_init;
+ m_bsd_in->reset = bsd_in_reset;
+ m_bsd_in->close = bsd_in_close;
-/*
- * 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(const 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;
+ if (m_bsd_in->enabled) m_bsd_in->init();
- while (NULL != (de = readdir(d)))
+ /* network (syslog protocol) input module */
+ m_udp_in = (module_t *)calloc(1, sizeof(module_t));
+ if (m_udp_in == NULL)
{
- 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;
+ asldebug("alloc failed (init_modules udp_in)\n");
+ exit(1);
+ }
- c = dlopen(modulepath, RTLD_LOCAL);
- if (c == NULL)
- {
- free(modulepath);
- continue;
- }
+ m_udp_in->name = "udp_in";
+ m_udp_in->enabled = activate_udp_in;
+ m_udp_in->init = udp_in_init;
+ m_udp_in->reset = udp_in_reset;
+ m_udp_in->close = udp_in_close;
- tmp = calloc(1, sizeof(struct module_list));
- if (tmp == NULL)
- {
- free(modulepath);
- dlclose(c);
- continue;
- }
+ if (m_udp_in->enabled) m_udp_in->init();
- bn = basename(modulepath);
- tmp->name = strdup(bn);
- if (tmp->name == NULL)
- {
- free(tmp);
- return 1;
- }
+ /* remote (iOS support) module */
+ m_remote = (module_t *)calloc(1, sizeof(module_t));
+ if (m_remote == NULL)
+ {
+ asldebug("alloc failed (init_modules remote)\n");
+ exit(1);
+ }
- tmp->module = c;
- TAILQ_INSERT_TAIL(&Moduleq, tmp, entries);
+ m_remote->name = "remote";
+ m_remote->enabled = remote_enabled;
+ m_remote->init = remote_init;
+ m_remote->reset = remote_reset;
+ m_remote->close = remote_close;
- tmp->init = dlsym(tmp->module, "aslmod_init");
- tmp->reset = dlsym(tmp->module, "aslmod_reset");
- tmp->close = dlsym(tmp->module, "aslmod_close");
+ if (m_remote->enabled) m_remote->init();
- free(modulepath);
+ /* save modules in global.module array */
+ global.module_count = 6;
+ global.module = (module_t **)calloc(global.module_count, sizeof(module_t *));
+ if (global.module == NULL)
+ {
+ asldebug("alloc failed (init_modules)\n");
+ exit(1);
}
- closedir(d);
-
- return 0;
+ global.module[0] = m_asl;
+ global.module[1] = m_bsd_out;
+ global.module[2] = m_klog_in;
+ global.module[3] = m_bsd_in;
+ global.module[4] = m_udp_in;
+ global.module[5] = m_remote;
}
static void
{
struct stat sb;
FILE *fp;
+ pid_t pid = getpid();
+
+ asldebug("\nsyslogd %d start\n", pid);
if (first != NULL)
{
fp = fopen(_PATH_PIDFILE, "w");
if (fp != NULL)
{
- fprintf(fp, "%d\n", getpid());
+ fprintf(fp, "%d\n", pid);
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)
-{
- global.reset = RESET_CONFIG;
-}
-
-static void
-catch_siginfo(int x)
-{
- global.reset = RESET_NETWORK;
-}
-
-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();
- }
-}
-
-/*
- * perform timed activities and set next run-loop timeout
- */
-static void
-timed_events(struct timeval **run)
-{
- time_t now, delta, t;
- static struct timeval next;
-
- now = time(NULL);
-
- *run = NULL;
- next.tv_sec = 0;
- next.tv_usec = 0;
-
- if (time_start == 0)
- {
- /* startup */
- time_start = now;
- time_last = now;
- mark_last = now;
- ping_last = now;
- }
-
- /*
- * At startup, we try sending a notification once a second.
- * Once it succeeds, we set the Libc global __notify_78945668_info__ to 0
- * which lets Libc's localtime calculations use notifyd to invalidate
- * cached timezones. This prevents a deadlock in localtime.
- */
- if (__notify_78945668_info__ < 0)
- {
- if (notify_post("com.apple.system.syslogd") == NOTIFY_STATUS_OK) __notify_78945668_info__ = 0;
- else next.tv_sec = 1;
- }
-
- if (time_last > now)
- {
- /*
- * Despite Albert Einstein's assurance, time has gone backward.
- * Reset "last" times to current time.
- */
- time_last = now;
- mark_last = now;
- ping_last = now;
- }
-
- /*
- * Tickle bsd_out module to flush duplicates.
- */
- if (global.bsd_flush_time > 0)
- {
- bsd_flush_duplicates(now);
- bsd_close_idle_files(now);
- if (global.bsd_flush_time > 0)
- {
- if (next.tv_sec == 0) next.tv_sec = global.bsd_flush_time;
- else if (global.bsd_flush_time < next.tv_sec) next.tv_sec = global.bsd_flush_time;
- }
- }
-
- /*
- * Tickle asl_store to sweep file cache
- */
- if (global.asl_store_ping_time > 0)
- {
- delta = now - ping_last;
- if (delta >= global.asl_store_ping_time)
- {
- db_ping_store();
- bsd_close_idle_files(now);
- ping_last = now;
- t = global.asl_store_ping_time;
- }
- else
- {
- t = global.asl_store_ping_time - delta;
- }
-
- if (next.tv_sec == 0) next.tv_sec = t;
- else if (t < next.tv_sec) next.tv_sec = t;
- }
-
- /*
- * Send MARK
- */
- if (global.mark_time > 0)
- {
- delta = now - mark_last;
- if (delta >= global.mark_time)
- {
- asl_mark();
- mark_last = now;
- t = global.mark_time;
- }
- else
- {
- t = global.mark_time - delta;
- }
-
- if (next.tv_sec == 0) next.tv_sec = t;
- else if (t < next.tv_sec) next.tv_sec = t;
- }
-
- /*
- * set output timeout parameter if runloop needs to have a timer
- */
- if (next.tv_sec > 0) *run = &next;
-
- time_last = now;
-}
-
void
-init_config()
+launch_config()
{
launch_data_t tmp, pdict;
kern_return_t status;
if (global.launch_dict == NULL)
{
- fprintf(stderr, "%d launchd checkin failed\n", getpid());
+ asldebug("%d launchd checkin failed\n", getpid());
exit(1);
}
tmp = launch_data_dict_lookup(global.launch_dict, LAUNCH_JOBKEY_MACHSERVICES);
if (tmp == NULL)
{
- fprintf(stderr, "%d launchd lookup of LAUNCH_JOBKEY_MACHSERVICES failed\n", getpid());
+ asldebug("%d launchd lookup of LAUNCH_JOBKEY_MACHSERVICES failed\n", getpid());
exit(1);
}
pdict = launch_data_dict_lookup(tmp, SERVICE_NAME);
if (pdict == NULL)
{
- fprintf(stderr, "%d launchd lookup of SERVICE_NAME failed\n", getpid());
+ asldebug("%d launchd lookup of SERVICE_NAME failed\n", getpid());
exit(1);
}
global.server_port = launch_data_get_machport(pdict);
- /* port for receiving internal messages */
- status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &(global.self_port));
- if (status != KERN_SUCCESS)
- {
- fprintf(stderr, "mach_port_allocate self_port failed: %d", status);
- exit(1);
- }
-
- status = mach_port_insert_right(mach_task_self(), global.self_port, global.self_port, MACH_MSG_TYPE_MAKE_SEND);
- if (status != KERN_SUCCESS)
- {
- fprintf(stderr, "Can't make send right for self_port: %d\n", status);
- exit(1);
- }
-
/* port for receiving MACH_NOTIFY_DEAD_NAME notifications */
status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &(global.dead_session_port));
if (status != KERN_SUCCESS)
{
- fprintf(stderr, "mach_port_allocate dead_session_port failed: %d", status);
+ asldebug("mach_port_allocate dead_session_port failed: %d", status);
exit(1);
}
status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &(global.listen_set));
if (status != KERN_SUCCESS)
{
- fprintf(stderr, "mach_port_allocate listen_set failed: %d", status);
+ asldebug("mach_port_allocate listen_set failed: %d", status);
exit(1);
}
status = mach_port_move_member(mach_task_self(), global.server_port, global.listen_set);
if (status != KERN_SUCCESS)
{
- fprintf(stderr, "mach_port_move_member server_port failed: %d", status);
- exit(1);
- }
-
- status = mach_port_move_member(mach_task_self(), global.self_port, global.listen_set);
- if (status != KERN_SUCCESS)
- {
- fprintf(stderr, "mach_port_move_member self_port failed: %d", status);
+ asldebug("mach_port_move_member server_port failed: %d", status);
exit(1);
}
status = mach_port_move_member(mach_task_self(), global.dead_session_port, global.listen_set);
if (status != KERN_SUCCESS)
{
- fprintf(stderr, "mach_port_move_member dead_session_port failed (%u)", status);
+ asldebug("mach_port_move_member dead_session_port failed (%u)", status);
exit(1);
}
}
OSSpinLockLock(&global.lock);
global.debug = enable;
- if (global.debug_file != NULL) free(global.debug_file);
+ free(global.debug_file);
global.debug_file = NULL;
if (path != NULL) global.debug_file = strdup(path);
snprintf(buf, sizeof(buf), "%u", getpid());
asl_set(msg, ASL_KEY_PID, buf);
asl_set(msg, ASL_KEY_MSG, "--- syslogd restarted ---");
- asl_enqueue_message(SOURCE_INTERNAL, NULL, msg);
+ dispatch_async(global.work_queue, ^{ process_message(msg, SOURCE_INTERNAL); });
return;
}
snprintf(buf, sizeof(buf), "%u%s", (unsigned int)utx.ut_tv.tv_usec, (utx.ut_tv.tv_usec == 0) ? "" : "000");
asl_set(msg, ASL_KEY_TIME_NSEC, buf);
- asl_enqueue_message(SOURCE_INTERNAL, NULL, msg);
+ dispatch_async(global.work_queue, ^{ process_message(msg, SOURCE_INTERNAL); });
}
int
main(int argc, const char *argv[])
{
- struct module_list *mod;
- fd_set rd, wr, ex, kern;
- int32_t fd, i, max, status, daemonize;
- const char *mp;
- struct timeval *runloop_timer, zto;
- pthread_attr_t attr;
- pthread_t t;
- int network_change_token;
+ int32_t i;
+ int network_change_token, asl_db_token;
char tstr[32];
time_t now;
- int first_syslogs_start = 1;
+ int first_syslogd_start = 1;
/* Set I/O policy */
setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_PASSIVE);
global.db_lock = (pthread_mutex_t *)calloc(1, sizeof(pthread_mutex_t));
pthread_mutex_init(global.db_lock, NULL);
- global.work_queue_lock = (pthread_mutex_t *)calloc(1, sizeof(pthread_mutex_t));
- pthread_mutex_init(global.work_queue_lock, NULL);
-
- pthread_cond_init(&global.work_queue_cond, NULL);
-
- global.work_queue = (asl_search_result_t *)calloc(1, sizeof(asl_search_result_t));
+ /*
+ * Create work queue, but suspend until output modules are initialized.
+ */
+ global.work_queue = dispatch_queue_create("Work Queue", NULL);
+ dispatch_suspend(global.work_queue);
- global.asl_log_filter = ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG);
- global.db_file_max = 16384000;
- global.db_memory_max = 8192;
- global.db_mini_max = 256;
- global.bsd_max_dup_time = DEFAULT_BSD_MAX_DUP_SEC;
- global.utmp_ttl = DEFAULT_UTMP_TTL_SEC;
- global.fs_ttl = DEFAULT_FS_TTL_SEC;
- global.mps_limit = DEFAULT_MPS_LIMIT;
- global.kfd = -1;
global.lockdown_session_fd = -1;
-#ifdef CONFIG_MAC
- global.dbtype = DB_TYPE_FILE;
- global.db_file_max = 25600000;
- global.asl_store_ping_time = 150;
-#endif
+ init_globals();
#ifdef CONFIG_IPHONE
- global.dbtype = DB_TYPE_MINI;
- activate_remote = 1;
+ remote_enabled = 1;
activate_bsd_out = 0;
#endif
- mp = _PATH_MODULE_LIB;
- daemonize = 0;
- __notify_78945668_info__ = 0xf0000000;
- zto.tv_sec = 0;
- zto.tv_usec = 0;
-
/* prevent malloc from calling ASL on error */
_malloc_no_asl_log = 1;
else if (streq(argv[i], "iphone"))
{
global.dbtype = DB_TYPE_MINI;
- activate_remote = 1;
+ remote_enabled = 1;
}
}
}
}
}
}
- else if (streq(argv[i], "-D"))
- {
- daemonize = 1;
- }
else if (streq(argv[i], "-m"))
{
if ((i + 1) < argc) global.mark_time = 60 * atoll(argv[++i]);
{
if ((i + 1) < argc) global.utmp_ttl = atol(argv[++i]);
}
- else if (streq(argv[i], "-fs_ttl"))
- {
- if ((i + 1) < argc) global.fs_ttl = atol(argv[++i]);
- }
else if (streq(argv[i], "-mps_limit"))
{
if ((i + 1) < argc) global.mps_limit = atol(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')) global.asl_log_filter = ASL_FILTER_MASK_UPTO(atoi(argv[i]));
- }
- }
else if (streq(argv[i], "-dup_delay"))
{
if ((i + 1) < argc) global.bsd_max_dup_time = atoll(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]);
{
if ((i + 1) < argc) activate_bsd_in = atoi(argv[++i]);
}
+ else if (streq(argv[i], "-udp_in"))
+ {
+ if ((i + 1) < argc) activate_udp_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], "-remote"))
{
- if ((i + 1) < argc) activate_remote = atoi(argv[++i]);
- }
- else if (streq(argv[i], "-udp_in"))
- {
- if ((i + 1) < argc) activate_udp_in = atoi(argv[++i]);
+ if ((i + 1) < argc) remote_enabled = atoi(argv[++i]);
}
}
{
global.dbtype = DB_TYPE_FILE;
global.db_file_max = 25600000;
- global.asl_store_ping_time = 150;
}
- TAILQ_INIT(&Moduleq);
- static_modules();
- load_modules(mp);
- aslevent_init();
-
- if (global.debug == 0)
- {
- if (daemonize != 0)
- {
- if (fork() != 0) exit(0);
-
- detach();
- closeall();
- }
+ signal(SIGHUP, SIG_IGN);
- writepid(&first_syslogs_start);
- }
+ writepid(&first_syslogd_start);
- init_config();
+ /*
+ * Log UTMPX boot time record
+ */
+ write_boot_log(first_syslogd_start);
- signal(SIGHUP, catch_sighup);
- signal(SIGINFO, catch_siginfo);
+ asldebug("reading launch plist\n");
+ launch_config();
- /* register for network change notifications if the udp_in module is active */
- network_change_token = -1;
- if (activate_udp_in != 0) notify_register_signal(NETWORK_CHANGE_NOTIFICATION, SIGINFO, &network_change_token);
+ asldebug("initializing modules\n");
+ init_modules();
+ dispatch_resume(global.work_queue);
- for (mod = Moduleq.tqh_first; mod != NULL; mod = mod->entries.tqe_next)
- {
- fd = mod->init();
- if (fd < 0) continue;
- }
+ /* network change notification resets UDP and BSD modules */
+ notify_register_dispatch(kNotifySCNetworkChange, &network_change_token, global.work_queue, ^(int x){
+ if (activate_udp_in != 0) udp_in_reset();
+ if (activate_bsd_out != 0) bsd_out_reset();
+ });
- /*
- * Start database server thread
- */
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
- pthread_create(&t, &attr, (void *(*)(void *))database_server, NULL);
- pthread_attr_destroy(&attr);
+ /* SIGHUP resets all modules */
+ global.sig_hup_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t)SIGHUP, 0, dispatch_get_main_queue());
+ dispatch_source_set_event_handler(global.sig_hup_src, ^{
+ dispatch_async(global.work_queue, ^{
+ int i;
- /*
- * Start output worker thread
- */
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
- pthread_create(&t, &attr, (void *(*)(void *))output_worker, NULL);
- pthread_attr_destroy(&attr);
+ asldebug("SIGHUP reset\n");
+ for (i = 0; i < global.module_count; i++)
+ {
+ if (global.module[i]->enabled != 0) global.module[i]->reset();
+ }
+ });
+ });
- FD_ZERO(&rd);
- FD_ZERO(&wr);
- FD_ZERO(&ex);
+ dispatch_resume(global.sig_hup_src);
- /*
- * Log UTMPX boot time record
- */
- write_boot_log(first_syslogs_start);
+ /* register for DB notification (posted by dbserver) for performance */
+ notify_register_plain(kNotifyASLDBUpdate, &asl_db_token);
- /*
- * drain /dev/klog first
- */
- if (global.kfd >= 0)
+ /* timer for MARK facility */
+ if (global.mark_time > 0)
{
- FD_ZERO(&kern);
- FD_SET(global.kfd, &kern);
- max = global.kfd + 1;
- while (select(max, &kern, NULL, NULL, &zto) > 0)
- {
- aslevent_handleevent(&kern, &wr, &ex);
- }
+ global.mark_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
+ dispatch_source_set_event_handler(global.mark_timer, ^{
+ asl_mark();
+ });
+ dispatch_source_set_timer(global.mark_timer, dispatch_walltime(NULL, global.mark_time * NSEC_PER_SEC), global.mark_time * NSEC_PER_SEC, 0);
+ dispatch_resume(global.mark_timer);
}
/*
- * Start launchd drain thread
+ * Start launchd service
+ * This pins a thread in _vprocmgr_log_drain. Eventually we will either
+ * remove the whole stderr/stdout -> ASL mechanism entirely, or come up
+ * with a communication channel that we can trigger with a dispatch source.
*/
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
- pthread_create(&t, &attr, (void *(*)(void *))launchd_drain, NULL);
- pthread_attr_destroy(&attr);
-
- runloop_timer = NULL;
- timed_events(&runloop_timer);
-
- forever
- {
- /* aslevent_fdsets clears the fdsets, then sets any non-zero fds from the aslevent list */
- max = aslevent_fdsets(&rd, &wr, &ex) + 1;
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ forever _vprocmgr_log_drain(NULL, NULL, launchd_callback);
+ });
- status = select(max, &rd, &wr, &ex, runloop_timer);
- if ((status < 0) && (errno == EBADF))
- {
- /* Catastrophic error! */
- aslevent_check();
- abort();
- }
-
- if ((global.kfd >= 0) && FD_ISSET(global.kfd, &rd))
- {
- /* drain /dev/klog */
- FD_ZERO(&kern);
- FD_SET(global.kfd, &kern);
- max = global.kfd + 1;
-
- while (select(max, &kern, NULL, NULL, &zto) > 0)
- {
- aslevent_handleevent(&kern, &wr, &ex);
- }
- }
+ /*
+ * Start mach server
+ * Parks a thread in database_server. In notifyd, we found that the overhead of
+ * a dispatch source for mach calls was too high, especially on iOS.
+ */
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ database_server();
+ });
- if (status > 0) aslevent_handleevent(&rd, &wr, &ex);
+ dispatch_main();
- if ((global.reset != RESET_NONE) || (status < 0))
- {
- send_reset();
- global.reset = RESET_NONE;
- }
-
- timed_events(&runloop_timer);
- }
+ /* NOTREACHED */
+ return 0;
}
/*
- * Copyright (c) 2004-2010 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2011 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#define MAXSOCK 16
static int nsock = 0;
static int ufd[MAXSOCK];
+static dispatch_source_t ufd_src[MAXSOCK];
static char uline[MAXLINE + 1];
+static dispatch_source_t in_src[MAXSOCK];
+static dispatch_queue_t in_queue;
+
#define FMT_LEGACY 0
#define FMT_ASL 1
-aslmsg
+void
udp_in_acceptmsg(int fd)
{
socklen_t fromlen;
char fromstr[64], *r, *p;
struct sockaddr_in *s4;
struct sockaddr_in6 *s6;
+ aslmsg m;
fromlen = sizeof(struct sockaddr_storage);
memset(&from, 0, fromlen);
len = recvfrom(fd, uline, MAXLINE, 0, (struct sockaddr *)&from, &fromlen);
- if (len <= 0) return NULL;
+ if (len <= 0) return;
fromstr[0] = '\0';
r = NULL;
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);
+ asldebug("%s: fd %d recvfrom %s len %d\n", MY_ID, fd, 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);
+ asldebug("%s: fd %d recvfrom %s len %d\n", MY_ID, fd, fromstr, len);
}
uline[len] = '\0';
p = strrchr(uline, '\n');
if (p != NULL) *p = '\0';
- return asl_input_parse(uline, len, r, SOURCE_UDP_SOCKET);
+ m = asl_input_parse(uline, len, r, SOURCE_UDP_SOCKET);
+ dispatch_async(global.work_queue, ^{ process_message(m, SOURCE_UDP_SOCKET); });
}
int
-udp_in_init(void)
+udp_in_init()
{
- int i, rbufsize, len;
+ int i, rbufsize, len, fd;
launch_data_t sockets_dict, fd_array, fd_dict;
+ static dispatch_once_t once;
+
+ dispatch_once(&once, ^{
+ in_queue = dispatch_queue_create(MY_ID, NULL);
+ });
asldebug("%s: init\n", MY_ID);
if (nsock > 0) return 0;
+
if (global.launch_dict == NULL)
{
asldebug("%s: launchd dict is NULL\n", MY_ID);
for (i = 0; i < nsock; i++)
{
+ ufd[i] = -1;
+
fd_dict = launch_data_array_get_index(fd_array, i);
if (fd_dict == NULL)
{
return -1;
}
- ufd[i] = launch_data_get_fd(fd_dict);
+ fd = launch_data_get_fd(fd_dict);
rbufsize = 128 * 1024;
len = sizeof(rbufsize);
- if (setsockopt(ufd[i], SOL_SOCKET, SO_RCVBUF, &rbufsize, len) < 0)
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rbufsize, len) < 0)
{
- asldebug("%s: couldn't set receive buffer size for socket %d: %s\n", MY_ID, ufd[i], strerror(errno));
- close(ufd[i]);
- ufd[i] = -1;
- continue;
+ asldebug("%s: couldn't set receive buffer size for file descriptor %d: %s\n", MY_ID, fd, strerror(errno));
}
- if (fcntl(ufd[i], F_SETFL, O_NONBLOCK) < 0)
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
{
- asldebug("%s: couldn't set O_NONBLOCK for socket %d: %s\n", MY_ID, ufd[i], strerror(errno));
- close(ufd[i]);
- ufd[i] = -1;
- continue;
+ asldebug("%s: couldn't set O_NONBLOCK for file descriptor %d: %s\n", MY_ID, fd, strerror(errno));
}
- }
- for (i = 0; i < nsock; i++) if (ufd[i] != -1) aslevent_addfd(SOURCE_UDP_SOCKET, ufd[i], 0, udp_in_acceptmsg, NULL, NULL);
+ ufd[i] = fd;
+
+ in_src[i] = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, (uintptr_t)fd, 0, in_queue);
+ dispatch_source_set_event_handler(in_src[i], ^{ udp_in_acceptmsg(fd); });
+
+ dispatch_resume(in_src[i]);
+ }
return 0;
}
+/* N.B. Does NOT close fds. They "belong" to launchd. */
int
udp_in_close(void)
{
int i;
- if (nsock == 0) return 1;
+ if (nsock == 0) return -1;
for (i = 0; i < nsock; i++)
{
- if (ufd[i] != -1)
+ if (ufd_src[i] != NULL)
{
- aslevent_removefd(ufd[i]);
- close(ufd[i]);
+ dispatch_source_cancel(in_src[i]);
+ dispatch_release(in_src[i]);
+ in_src[i] = NULL;
}
- ufd[i] = -1;
+ if (ufd[i] != -1)
+ {
+ ufd[i] = -1;
+ }
}
nsock = 0;
int
udp_in_reset(void)
{
- return 0;
+ if (udp_in_close() != 0) return -1;
+ return udp_in_init();
}
-.\"Copyright (c) 2004-2008 Apple Inc. All rights reserved.
+.\"Copyright (c) 2004-2011 Apple Inc. All rights reserved.
.\"
.\"@APPLE_LICENSE_HEADER_START@
.\"
.D1 ""
.Nm
.Fl c Ar process Op filter
+.D1 ""
+.Nm
+.Fl config Op options
.Sh DESCRIPTION
.Nm
is a command-line utility for a variety of tasks relating to the Apple System Log (ASL) facility.
.Pp
If invoked with no arguments,
.Nm
-fetchs all messages from the active data store.
+fetches all messages from the active data store.
Messages are then printed to standard output,
subject to formatting options and character encoding as described below.
Some log messages are read-access controlled,
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.
+.Dq $Name ,
+.Dq $(Name) ,
+or
+.Dq $((Name)(format)) .
+which will be expanded to the value associated with the named key.
For example, the command:
.Pp
-.Dl syslog -F '$Time $Host $(Sender)[$(PID)]: $Message'
+.Dl syslog -F '$Time $Host $(Sender)[$(PID)] <$((Level)(str))>: $Message'
.Pp
produces output similar to the
-.Dq bsd
+.Dq std
format.
+The simple
+.Dq $Name
+form is sufficient in most cases.
+However, the second form:
+.Dq $(Name)
+must be used if the name is not delimited by white space.
+The third form allows different formats of the value to be printed.
+For example, a message priority level may appear as an integer value (e.g.
+.Dq 3 )
+or as a string (``Error'').
+The following print formats are known.
+.Pp
+.Bl -tag -width "$((Time)([+|-]HH[:MM]))"
+.It $((Level)(str))
+Formats a Level value as a string, for example
+.Dq Error ,
+.Dq Alert ,
+.Dq Warning ,
+and so on.
+Note that $(Level) or $Level formats the value as an integer 0 through 7.
+.It $((Time)(sec))
+Formats a Time value as the number of seconds since the Epoch.
+.It $((Time)(raw))
+Alias for $((Time)(sec)).
+.It $((Time)(local))
+Formats a Time value as a string of the form
+.Dq "Mmm dd hh:mm:ss" ,
+where Mmm is the abbreviation for the month, dd is the date (1 - 31) and hh:mm:ss is the time.
+The local timezone is used.
+.It $((Time)(lcl))
+Alias for $((Time)(local)).
+.It $((Time)(utc))
+Formats a Time value as a string of the form
+.Dq "yyyy-mm-dd hh:mm:ssZ" ,
+using Coordinated Universal Time, or the
+.Dq Zulu
+time zone.
+.It $((Time)(zulu))
+Alias for $((Time)(utc)).
+.It $((Time)(X))
+Where X may be any letter in the range A - Z or a - z.
+Formats the Time using the format
+.Dq "yyyy-mm-dd hh:mm:ssX" ,
+using the specified nautical timezone.
+Z is the same as UTC/Zulu time. Timezones A - M (except J) decrease by one hour to the east of the
+Zulu time zone.
+Timezones N - Y increase by one hour to the west of Z.
+M and Y have the same clock time, but differ by one day.
+J is used to indicate the local timezone.
+When printing using $((Time)(J)), the output format is
+.Dq "yyyy-mm-dd hh:mm:ss" ,
+without a trailing timezone letter.
+.It $((Time)([+|-]HH[:MM]))
+Specifies an offset (+ or -) of the indicated number of hours (HH) and optionally minutes (MM) to UTC.
+The value is formatted as a string of the form
+.Dq "yyyy-mm-dd hh:mm:ss[+|-]HH:MM" .
+.El
.Pp
-Timestamps may be printed in three formats.
-Times are generally converted to local time, except when the
-.Fl F Ar sec
+If a custom format is not being used to specify the format for Time values, then Time values
+are generally converted to local time, except when the
+.Fl F Ar raw
option is used, in which case times are printed as the number of seconds since the epoch.
The
.Fl T Ar format
-option may be used to explicity control the format used for timestamps.
+option may be used to control the format used for timestamps.
The value of
.Ar format
may be one of the following:
.Pp
-.Bl -tag -width "local"
-.It sec
+.Bl -tag -width "local or lcl"
+.It sec or raw
Times are printed as the number of seconds since the epoch.
-.It local
+.It local or lcl
Times are converted to the local time zone, and printed with the format
-.Dl MMM DD HH:MM:SS
-.It utc
+.Dl mmm dd hh:mm:ss
+where mmm is the month name abbreviated as three characters.
+.It utc or zulu
Times are converted to UTC, and printed with the format
-.Dl YYYY.MM.DD HH:MM:SS UTC
+.Dl yyyy-mm-dd hh:mm:ssZ
+.It A-Z
+Times are converted to the indicated nautical time zone,
+printed in the same format as UTC.
+.Dq J
+is interpreted as the local timezone and printed in the same format,
+but without a trailing timezone letter.
+.It [+|-]hh[:mm]
+The specified offset is used to adjust time.
.El
.Pp
The
By default, control characters and non-printable characters are encoded in the output stream.
In some cases this may make messages less natural in appearance.
The encoding is designed to preserve all the information in the log message,
-and to prevent malicious users from spoofing or obsucring information in log messages.
+and to prevent malicious users from spoofing or obscuring information in log messages.
.Pp
Text in the
.Dq std ,
Ampersand, less than, greater than, quotation mark, and apostrophe characters are encoded according to XML conventions.
Embedded control characters are encoded as
.Dq &#xNN;
-where NN is the character's hexidecimal value.
+where NN is the character's hexadecimal value.
.Pp
Values that do not contain legal UTF8 are encoded in base-64 and printed as data objects.
.Pp
The
.Fl E Ar format
-option may be used to explicity control the text encoding.
+option may be used to explicitly control the text encoding.
The value of
.Ar format
may be one of the following:
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
+The
.Nm syslogd
-daemon.
-The daemon also contains filters which determines which messages are saved in the data store.
-This determines which messages are seen when reading messages using the
-.Nm
-utility, or when viewing data store messages in the Console utility application.
+server follows filtering rules specified in the /etc/asl.conf file.
+When the remote-control mechanism is used to change the filter of a process,
+.Nm syslogd
+will save any messages received from that process until the remote-control filter is turned off.
+.Ss SERVER CONFIGURATION
+When
+.Nm syslogd
+starts up, and when it receives a HUP signal, it re-reads its configuration settings from /etc/asl.conf.
+It is sometimes useful to change configuration parameters temporarily, without needing to make changes
+to the configuration file.
+Any of the configuration options that may be set in the file (following an ``='' character) may also
+be sent to syslogd using the
+.Fl config
+flag (without an ``='' character).
+For example, to temporarily disable the message-per-second limit:
.Pp
-The default data store filter mask permits all messages with priority levels from Emergency to Debug (level 0 to 7).
-The level may be inspected using:
+.Dl syslog -config mps_limit 0
.Pp
-.Dl syslog -c syslogd
+Note that only the superuser (root) may change configuration parameters.
.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:
+In addition to the parameter setting options that are described in the
+.Xr asl.conf 5
+manual page, an additional option:
.Pp
-.Dl syslog -c syslog -e
+.Dl syslog -config reset
.Pp
-The
-.Nm syslogd
-server also follows filtering rules specified in the /etc/asl.conf file.
-When the remote-control mechanism is used to change the filter of a process,
+will cause
.Nm syslogd
-will save any messages received from that process until the remote-control filter is turned off.
-It is no longer necessary to adjust the filtering for both a process and for
-.Nm syslogd
-to have messages saved in the ASL data store.
+to reset its configuration.
.Sh SEE ALSO
.Xr syslogd 8 ,
.Xr logger 1 ,
.Xr asl 3 ,
.Xr syslog 3 ,
+.Xr asl.conf 5 .
.Sh HISTORY
The
.Nm
/*
- * Copyright (c) 2007-2010 Apple Inc. All rights reserved.
+ * Copyright (c) 2007-2011 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
/* Shared with Libc */
#define NOTIFY_RC "com.apple.asl.remote"
+/* XXX add to asl_private.h */
+#define ASL_OPT_CONTROL "control"
+
#define SEARCH_EOF -1
#define SEARCH_NULL 0
#define SEARCH_MATCH 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 TIME_SEC 0x00000010
-#define TIME_UTC 0x00000020
-#define TIME_LCL 0x00000040
-
#define FORMAT_RAW 0x00000100
#define FORMAT_LEGACY 0x00000200
#define FORMAT_STD 0x00000400
static int sort_numeric = 0;
static char *last_printmsg_str = NULL;
static int last_printmsg_count = 0;
+static const char *tfmt = NULL;
#ifdef CONFIG_IPHONE
static uint32_t dbselect = DB_SELECT_SYSLOGD;
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 -config params...\n", myname);
+ fprintf(stderr, " set or reset syslogd configuration parameters\n");
+ fprintf(stderr, "\n");
fprintf(stderr, "%s [-f file...] [-d path...] [-x file] [-w [N]] [-F format] [-nocompress] [-u] [-sort key1 [key2]] [-nsort key1 [key2]] [-k key [[op] val]]... [-o -k key [[op] val]] ...]...\n", myname);
fprintf(stderr, " -f read named file[s], rather than standard log message store.\n");
fprintf(stderr, " -d read all file in named directory path, rather than standard log message store.\n");
{
static char str[1024];
- if (pid == RC_SYSLOGD) return NOTIFY_SYSTEM_ASL_FILTER;
if (pid == RC_MASTER) return NOTIFY_SYSTEM_MASTER;
memset(str, 0, sizeof(str));
rcontrol_get(pid_t pid, uid_t uid)
{
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(rcontrol_name(pid, uid), &filter);
if (status == NOTIFY_STATUS_OK)
{
- printf("%s filter mask: %s\n", name, asl_filter_string(filter));
+ printf("Master filter mask: %s\n", asl_filter_string(filter));
return 0;
}
- printf("Unable to determine %s filter mask\n", name);
+ printf("Unable to determine master filter mask\n");
return -1;
}
rcontrol_set(pid_t pid, uid_t uid, int filter)
{
int status;
- const char *name, *rcname;
+ const char *rcname;
rcname = rcontrol_name(pid, uid);
if (pid < 0)
{
- name = "Master";
- if (pid == RC_SYSLOGD) name = "ASL Data Store";
status = rcontrol_set_string(rcname, filter);
if (status == NOTIFY_STATUS_OK)
return 0;
}
- printf("Unable to set %s syslog filter mask: %s\n", name, notify_status_string(status));
+ printf("Unable to set master syslog filter mask: %s\n", notify_status_string(status));
return -1;
}
if ((!strcmp(argv[2], "syslogd")) || (!strcmp(argv[2], "syslog")))
{
- pid = RC_SYSLOGD;
- uid = 0;
- status = 0;
+ fprintf(stderr, "%s: does not have a filter mask\n", argv[2]);
+ return -1;
}
else if (_isanumber(argv[2]) != 0)
{
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]);
int
syslog_send(int argc, char *argv[])
{
- int i, start, kv, len, rfmt, rlevel, filter, status;
+ int i, start, kv, len, rfmt, rlevel;
aslclient asl;
aslmsg m;
char tmp[64], *str, *rhost;
if (rhost == NULL)
{
- filter = 0;
- status = rcontrol_get_string(rcontrol_name(RC_SYSLOGD, 0), &filter);
- if (status != 0)
- {
- fprintf(stderr, "Warning: Can't get current syslogd ASL filter value\n");
- }
- else if ((ASL_FILTER_MASK(rlevel) & filter) == 0)
- {
- fprintf(stderr, "Warning: The current syslogd ASL filter value (%s)\n", asl_filter_string(filter));
- fprintf(stderr, " will exclude this message from the ASL database\n");
- }
-
asl_send(asl, m);
}
else if (rfmt == SEND_FORMAT_ASL)
return 0;
}
+int
+syslog_config(int argc, char *argv[])
+{
+ int i;
+ uid_t uid;
+ aslclient asl;
+ aslmsg m;
+ asl_string_t *str;
+
+ uid = geteuid();
+ if (uid != 0)
+ {
+ fprintf(stderr, "syslogd parameters may only be set by the superuser\n");
+ return -1;
+ }
+
+ str = asl_string_new(0);
+ asl_string_append(str, "= ");
+
+ for (i = 2; i < argc; i++)
+ {
+ asl_string_append(str, argv[i]);
+ if ((i + 1) < argc) asl_string_append(str, " ");
+ }
+
+ asl = asl_open(myname, "syslog", 0);
+
+ m = asl_new(ASL_TYPE_MSG);
+ asl_set(m, ASL_KEY_LEVEL, ASL_STRING_NOTICE);
+ asl_set(m, ASL_KEY_OPTION, ASL_OPT_CONTROL);
+ asl_set(m, ASL_KEY_SENDER, myname);
+ asl_set(m, ASL_KEY_MSG, asl_string_bytes(str));
+
+ asl_send(asl, m);
+
+ asl_string_free(str);
+ asl_free(m);
+ asl_close(asl);
+
+ return 0;
+}
+
static void
print_xml_header(FILE *f)
{
printmsg(FILE *f, aslmsg msg, char *fmt, int pflags)
{
char *str;
- const char *mf, *tf;
+ const char *mf;
uint32_t encode, len, status;
uint64_t xid;
else if (pflags & FORMAT_LEGACY) mf = ASL_MSG_FMT_BSD;
else if (pflags & FORMAT_XML) mf = ASL_MSG_FMT_XML;
- tf = ASL_TIME_FMT_SEC;
- if (pflags & TIME_UTC) tf = ASL_TIME_FMT_UTC;
- if (pflags & TIME_LCL) tf = ASL_TIME_FMT_LCL;
-
len = 0;
- str = asl_format_message((asl_msg_t *)msg, mf, tf, encode, &len);
+ str = asl_format_message((asl_msg_t *)msg, mf, tfmt, encode, &len);
if (str == NULL) return;
if (pflags & COMPRESS_DUPS)
if (did_match != 0) printmsg(f, msg, pfmt, pflags);
}
+#ifdef CONFIG_IPHONE
void
syslogd_direct_watch(FILE *f, char *pfmt, int pflags, asl_search_result_t *ql)
{
struct sockaddr_in address;
- int i, bytes, n, inlen, sock, stream, status;
+ int i, bytes, sock, stream, status;
+ uint32_t n, inlen;
uint16_t port;
socklen_t addresslength;
char *str, buf[DIRECT_BUF_SIZE];
}
}
-reset:
-
addresslength = sizeof(address);
sock = socket(AF_INET, SOCK_STREAM, 0);
port = (arc4random() % (IPPORT_HILASTAUTO - IPPORT_HIFIRSTAUTO)) + IPPORT_HIFIRSTAUTO;
- address.sin_addr.s_addr = 0;
+ memset(&address, 0, addresslength);
+ address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
address.sin_family = AF_INET;
address.sin_port = htons(port);
forever
{
inlen = 0;
+ errno = 0;
bytes = recvfrom(stream, &n, sizeof(n), 0, NULL, NULL);
- if (bytes == 0) break;
+ if (bytes <= 0)
+ {
+ fprintf(stderr, "\nrecvfrom (message length) returned %d (errno %d) - exiting\n", bytes, errno);
+ break;
+ }
else inlen = ntohl(n);
if (inlen == 0) continue;
- if (inlen > MAX_DIRECT_SIZE)
- {
- fprintf(f, "\n*** received invalid message data [%d] from syslogd - resetting connection ***\n\n", inlen);
- close(stream);
- close(sock);
- goto reset;
- }
str = NULL;
if (inlen <= DIRECT_BUF_SIZE)
n = 0;
while (n < inlen)
{
+ errno = 0;
bytes = recvfrom(stream, str + n, inlen - n, 0, NULL, NULL);
- if (bytes == 0) break;
+ if (bytes <= 0)
+ {
+ fprintf(stderr, "\nrecvfrom (message body) returned %d (errno %d) at length %d of %d - exiting\n", bytes, errno, n, inlen);
+ break;
+ }
else n += bytes;
}
+ if (n < inlen)
+ {
+ fprintf(stderr, "\ntruncated message: expected %d bytes received %d bytes\n", inlen, n);
+ close(stream);
+ close(sock);
+ exit(1);
+ }
+
msg = (aslmsg)asl_msg_from_string(str);
if (str != buf) free(str);
filter_and_print(msg, ql, f, pfmt, pflags);
address.sin_addr.s_addr = 0;
}
+#endif
int
sort_compare_key(const void *a, const void *b, const char *key)
main(int argc, char *argv[])
{
FILE *outfile;
- int i, j, n, watch, status, pflags, tflags, iamroot, user_tflag, export_preserve_id, saw_dash_d, since_boot;
+ int i, j, n, watch, status, pflags, iamroot, user_tflag, export_preserve_id, saw_dash_d, since_boot;
int notify_file, notify_token;
asl_search_result_t *qlist;
asl_msg_t *cq;
tail_count = 0;
batch = FETCH_BATCH;
pflags = FORMAT_STD | COMPRESS_DUPS;
- tflags = TIME_LCL;
encode = ASL_ENCODE_SAFE;
cq = NULL;
exportname = NULL;
exit(0);
}
- if (!strcmp(argv[i], "-time"))
+ if ((!strcmp(argv[i], "-time")) || (!strcmp(argv[i], "--time")))
{
qmin = time(NULL);
printf("%llu\n", qmin);
exit(0);
}
+ if ((!strcmp(argv[i], "-config")) || (!strcmp(argv[i], "--config")))
+ {
+ syslog_config(argc, argv);
+ exit(0);
+ }
+
if (!strcmp(argv[i], "-s"))
{
syslog_send(argc, argv);
}
else if (!strcmp(argv[i], "-u"))
{
- tflags = TIME_UTC;
+ tfmt = "Z";
user_tflag = 1;
}
else if ((!strcmp(argv[i], "-x")) || (!strcmp(argv[i], "-X")))
if (!strcmp(argv[i], "raw"))
{
pflags = FORMAT_RAW;
- if (user_tflag == 0) tflags = TIME_SEC;
+ if (user_tflag == 0) tfmt = "sec";
}
else if (!strcmp(argv[i], "std"))
{
}
i++;
+ tfmt = argv[i];
user_tflag = 1;
-
- if (!strcmp(argv[i], "sec")) tflags = TIME_SEC;
- else if (!strcmp(argv[i], "utc")) tflags = TIME_UTC;
- else if (!strcmp(argv[i], "local")) tflags = TIME_LCL;
- else if (!strcmp(argv[i], "lcl")) tflags = TIME_LCL;
- else tflags = TIME_LCL;
}
else if (!strcmp(argv[i], "-nodc"))
{
}
}
- pflags |= tflags;
pflags |= encode;
outfile = stdout;
asl_set_query(bq, "ut_type", "2", ASL_QUERY_OP_EQUAL);
bt->count = 1;
bt->msg[0] = (asl_msg_t *)bq;
-
+
/* XXX */
search_once(NULL, NULL, 0, bt, -1, &qmin, 1, 1, -1, 0);
asl_free(bq);
free(bt->msg);
free(bt);
-
+
if (qmin > 0) qmin--;
tail_count = 0;
}
{
if (dbselect == DB_SELECT_SYSLOGD)
{
+#ifdef CONFIG_IPHONE
syslogd_direct_watch(outfile, pfmt, pflags, qlist);
+#else
+ fprintf(stderr, "Warning: -w flag cannot be used when querying syslogd directly\n");
+ exit(1);
+#endif
}
else if (notify_token == -1)
{