X-Git-Url: https://git.saurik.com/apple/syslog.git/blobdiff_plain/5dd30d768c7cd795c2a3b974a6a1809dd8a3becf..f3df4c032d7a59379e2d8e1a5cf8a8f0e9ea9f63:/util.tproj/syslog.c?ds=inline diff --git a/util.tproj/syslog.c b/util.tproj/syslog.c index e03833e..1cfb675 100644 --- a/util.tproj/syslog.c +++ b/util.tproj/syslog.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2007-2011 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -21,6 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ +#include #include #include #include @@ -28,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -35,12 +37,19 @@ #include #include #include +#include #include #include #include +#include +#include #include -#include +#include "asl_ipc.h" +#include #include +#include +#include +#include "asl_common.h" #define MOD_CASE_FOLD 'C' #define MOD_REGEX 'R' @@ -58,6 +67,8 @@ #define ASL_QUERY_OP_NOT 0x1000 +#define QUERY_FLAG_SEARCH_REVERSE 0x00000001 + #define FACILITY_CONSOLE "com.apple.console" #define SEARCH_EOF -1 @@ -68,7 +79,6 @@ #define PROC_NOT_UNIQUE -2 #define RC_MASTER -1 -#define RC_SYSLOGD -2 #define CHUNK 64 #define forever for(;;) @@ -76,14 +86,11 @@ #define SEND_FORMAT_LEGACY 0 #define SEND_FORMAT_ASL 1 -#define TIME_SEC 0x00000001 -#define TIME_UTC 0x00000002 -#define TIME_LCL 0x00000004 - -#define FORMAT_RAW 0x00000010 -#define FORMAT_LEGACY 0x00000020 -#define FORMAT_STD 0x00000040 -#define FORMAT_XML 0x00000080 +#define FORMAT_RAW 0x00000100 +#define FORMAT_LEGACY 0x00000200 +#define FORMAT_STD 0x00000400 +#define FORMAT_XML 0x00000800 +#define COMPRESS_DUPS 0x00010000 #define EXPORT 0x00000100 @@ -94,30 +101,59 @@ #define ASL_FILTER_MASK_PACE 0x0f #define ASL_FILTER_MASK_PAC 0x07 -#define FETCH_BATCH 256 - -#define _PATH_ASL_STORE "/var/log/asl.db" -static asl_store_t **dbstore = NULL; -static uint32_t store_count = 0; -static uint32_t store_raw = 1; - -static asl_store_t *export = NULL; +#define FETCH_BATCH 1024 +#define MAX_RANDOM 8192 + +#define DB_SELECT_ASL 0 +#define DB_SELECT_STORE 1 +#define DB_SELECT_FILES 2 +#define DB_SELECT_SYSLOGD 3 +#define DB_SELECT_LEGACY 4 + +/* STD and BSD format messages start with 'DAY MMM DD HH:MM:SS ' timestamp */ +#define STD_BSD_DATE_LEN 20 + +/* Max message size for direct watch */ +#define MAX_DIRECT_SIZE 16384 + +/* Buffer for direct watch data */ +#define DIRECT_BUF_SIZE 1024 + +static asl_file_list_t *db_files = NULL; +static asl_store_t *store = NULL; +static asl_file_t *legacy = NULL; +static asl_file_t *export = NULL; +static const char *sort_key = NULL; +static const char *sort_key_2 = NULL; +static int sort_numeric = 0; +static char *last_printmsg_str = NULL; +static int last_printmsg_count = 0; +static const char *tfmt = NULL; + +#if TARGET_OS_EMBEDDED +static uint32_t dbselect = DB_SELECT_SYSLOGD; +#else +static uint32_t dbselect = DB_SELECT_ASL; +#endif /* notify SPI */ uint32_t notify_register_plain(const char *name, int *out_token); -extern char *asl_msg_to_string(aslmsg msg, uint32_t *len); -extern asl_msg_t *asl_msg_from_string(const char *buf); -extern 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); -extern int asl_msg_cmp(asl_msg_t *a, asl_msg_t *b); +//extern asl_msg_t *asl_msg_from_string(const char *buf); +//extern char *asl_list_to_string(asl_msg_list_t *list, uint32_t *outlen); +//extern asl_msg_list_t *asl_list_from_string(const char *buf); +//extern int asl_msg_cmp(asl_msg_t *a, asl_msg_t *b); +asl_msg_t *_asl_server_control_query(void); extern time_t asl_parse_time(const char *in); /* END PRIVATE API */ +static mach_port_t asl_server_port = MACH_PORT_NULL; + static const char *myname = "syslog"; -#define ASL_SERVICE_NAME "com.apple.system.logger" -static mach_port_t asl_server_port = MACH_PORT_NULL; +/* forward */ +asl_msg_list_t *syslogd_query(asl_msg_list_t *q, uint64_t start, int count, int dir, uint64_t *last); +static void printmsg(FILE *f, asl_msg_t *msg, char *fmt, int pflags); void usage() @@ -140,30 +176,44 @@ usage() fprintf(stderr, " n = Notice\n"); fprintf(stderr, " i = Info\n"); fprintf(stderr, " d = Debug\n"); - fprintf(stderr, " a minus sign preceeding a single letter means \"up to\" that level\n"); + fprintf(stderr, " a minus sign preceding a single letter means \"up to\" that level\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "%s -config [params...]\n", myname); + fprintf(stderr, " without params, fetch and print syslogd parameters and statistics\n"); + fprintf(stderr, " otherwise, set or reset syslogd configuration parameters\n"); fprintf(stderr, "\n"); - fprintf(stderr, "%s -p [-db [file]...] [-k key [[op] val]]... [-o -k key [[op] val]] ...]...\n", myname); - fprintf(stderr, " -db prune /var/log/asl.db or named file, rather than sending a prune command to syslogd.\n"); - fprintf(stderr, " -p prune datastore according to input expression (see below)\n"); + fprintf(stderr, "%s -module [name [action]]\n", myname); + fprintf(stderr, " with no name, prints configuration for all ASL output modules\n"); + fprintf(stderr, " with name and no action, prints configuration for named ASL output module\n"); + fprintf(stderr, " supported actions - module name required, use '*' (with single quotes) for all modules:\n"); + fprintf(stderr, " enable [01] enables (or disables with 0) named module\n"); + fprintf(stderr, " does not apply to com.apple.asl when '*' is used\n"); + fprintf(stderr, " checkpoint [file] checkpoints all files or specified file for named module\n"); fprintf(stderr, "\n"); - fprintf(stderr, "%s [-db [file]...] [-x file] [-w [N]] [-F format] [-u] [-k key [[op] val]]... [-o -k key [[op] val]] ...]...\n", myname); - fprintf(stderr, " -db read /var/log/asl.db or named file, rather than querying syslogd.\n"); - fprintf(stderr, " use \"-\" to explicitly include a connection to syslogd.\n"); - fprintf(stderr, " -x export to named database, rather than printing\n"); - fprintf(stderr, " -w watch database (^C to quit)\n"); - fprintf(stderr, " prints the last N matching lines (default 10) before waiting\n"); - fprintf(stderr, " \"-w 0\" prints all matching lines before waiting\n"); - fprintf(stderr, " -F output format may be \"std\", \"raw\", \"bsd\", or \"xml\"\n"); - fprintf(stderr, " format may also be a string containing variables of the form\n"); - fprintf(stderr, " $Key or $(Key) - use the latter for non-whitespace delimited variables\n"); - fprintf(stderr, " -T timestamp format may be \"sec\" (seconds), \"utc\" (UTC), or \"local\" (local timezone)\n"); - fprintf(stderr, " -u print timestamps using UTC (equivalent to \"-T utc\")\n"); - fprintf(stderr, " -k key/value match\n"); - fprintf(stderr, " if no operator or value is given, checks for the existance of the key\n"); - fprintf(stderr, " if no operator is given, default is \"%s\"\n", OP_EQ); - fprintf(stderr, " -C alias for \"-k Facility com.apple.console\"\n"); - fprintf(stderr, " -o begins a new query\n"); - fprintf(stderr, " queries are \'OR\'ed together\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"); + fprintf(stderr, " -x export to named ASL format file, rather than printing\n"); + fprintf(stderr, " -w watch data store (^C to quit)\n"); + fprintf(stderr, " prints the last N matching lines (default 10) before waiting\n"); + fprintf(stderr, " \"-w all\" prints all matching lines before waiting\n"); + fprintf(stderr, " \"-w boot\" prints all matching lines since last system boot before waiting\n"); + fprintf(stderr, " -F output format may be \"std\", \"raw\", \"bsd\", or \"xml\"\n"); + fprintf(stderr, " format may also be a string containing variables of the form\n"); + fprintf(stderr, " $Key or $(Key) - use the latter for non-whitespace delimited variables\n"); + fprintf(stderr, " -T timestamp format may be \"sec\" (seconds), \"utc\" (UTC), or \"local\" (local timezone)\n"); + fprintf(stderr, " -E text encoding may be \"vis\", \"safe\", or \"none\"\n"); + fprintf(stderr, " -nodc no duplicate message compression\n"); + fprintf(stderr, " -u print timestamps using UTC (equivalent to \"-T utc\")\n"); + fprintf(stderr, " -sort sort messages using value for specified key1 (secondary sort by key2 if provided)\n"); + fprintf(stderr, " -nsort numeric sort messages using value for specified key1 (secondary sort by key2 if provided)\n"); + fprintf(stderr, " -k key/value match\n"); + fprintf(stderr, " if no operator or value is given, checks for the existence of the key\n"); + fprintf(stderr, " if no operator is given, default is \"%s\"\n", OP_EQ); + fprintf(stderr, " -B only process log messages since last system boot\n"); + fprintf(stderr, " -C alias for \"-k Facility com.apple.console\"\n"); + fprintf(stderr, " -o begins a new query\n"); + fprintf(stderr, " queries are \'OR\'ed together\n"); fprintf(stderr, "operators are zero or more modifiers followed by a comparison\n"); fprintf(stderr, " %s equal\n", OP_EQ); fprintf(stderr, " %s not equal\n", OP_NE); @@ -203,6 +253,160 @@ asl_level_string(int level) return "Unknown"; } +int +module_control(int argc, char *argv[]) +{ + const char *val = NULL; + uint64_t last; + char *str; + + asl_msg_t *ctl = _asl_server_control_query(); + if (ctl == NULL) + { + fprintf(stderr, "can't get status information from syslogd\n"); + return -1; + } + + argc -= 2; + argv += 2; + + if (argc < 2) + { + int first = 1; + + /* print config */ + asl_out_module_t *m = asl_out_module_init(); + asl_out_module_t *x = m; + + while (x != NULL) + { + if ((argc == 0) || (!strcmp(argv[0], x->name))) + { + asl_msg_lookup(ctl, x->name, &val, NULL); + + if (first == 0) printf("\n"); + first = 0; + + if (x->name == NULL) printf("ASL out module has no name\n"); + else printf("ASL out module: %s %s[current status: %s]\n", x->name, (x->flags & MODULE_FLAG_LOCAL) ? "local " : "", (val == NULL) ? "unknown" : val ); + + asl_out_module_print(stdout, x); + } + + x = x->next; + } + + asl_msg_release(ctl); + asl_out_module_free(m); + return 0; + } + + /* name enable [val] */ + /* name disable [val] */ + if ((!strcmp(argv[1], "enable")) || (!strcmp(argv[1], "disable"))) + { + int want = -1; + int status = -1; + asl_msg_t *cm; + asl_client_t *ac; + + if (!strcmp(argv[1], "enable")) + { + if (argc < 3) want = 1; + else if (!strcmp(argv[2], "1")) want = 1; + else if (!strcmp(argv[2], "0")) want = 0; + else + { + printf("invalid value %s for %s %s - expecting 0 or 1\n", argv[2], argv[0], argv[1]); + exit(-1); + } + } + else + { + if (argc < 3) want = 0; + else if (!strcmp(argv[2], "1")) want = 0; + else if (!strcmp(argv[2], "0")) want = 1; + else + { + printf("invalid value %s for %s %s - expecting 0 or 1\n", argv[2], argv[0], argv[1]); + exit(-1); + } + } + + asl_msg_lookup(ctl, argv[0], &val, NULL); + if (val != NULL) + { + if (!strcmp(val, "enabled")) status = 1; + else status = 0; + } + + asl_msg_release(ctl); + + if (want < 0) + { + printf("internal error: want = -1\n"); + exit(-1); + } + + if (want == status) + { + printf("module %s is already %s\n", argv[0], val); + return 0; + } + + cm = asl_msg_new(ASL_TYPE_MSG); + asprintf(&str, "@ %s enable %d", argv[0], want); + + if ((cm == NULL) || (str == NULL)) + { + fprintf(stderr, "can't allocate memory - exiting\n"); + exit(-1); + } + + ac = asl_client_open(NULL, NULL, 0); + asl_client_set_filter(ac, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG)); + asl_msg_set_key_val(cm, ASL_KEY_LEVEL, "7"); + asl_msg_set_key_val(cm, ASL_KEY_OPTION, "control"); + asl_msg_set_key_val(cm, ASL_KEY_MSG, str); + asl_client_send(ac, cm); + + asl_client_release(ac); + asl_msg_release(cm); + free(str); + return 0; + } + + asl_msg_release(ctl); + + /* name checkpoint [file] */ + if (!strcmp(argv[1], "checkpoint")) + { + asl_msg_list_t *q = asl_msg_list_new(); + asl_msg_t *qm = asl_msg_new(ASL_TYPE_QUERY); + + if ((q == NULL) || (qm == NULL)) + { + fprintf(stderr, "can't allocate memory - exiting\n"); + exit(-1); + } + + asl_msg_list_append(q, qm); + asl_msg_release(qm); + + asl_msg_set_key_val_op(qm, ASL_KEY_OPTION, "control", ASL_QUERY_OP_EQUAL); + asprintf(&str, "%s checkpoint%s%s", argv[0], (argc > 2) ? " " : "", (argc > 2) ? argv[2] : ""); + asl_msg_set_key_val_op(qm, "action", str, ASL_QUERY_OP_EQUAL); + + asl_msg_list_t *res = syslogd_query((asl_msg_list_t *)q, 0, 0, 1, &last); + free(q); + asl_msg_list_release(res); + return 0; + } + + printf("unknown module control: %s\n", argv[1]); + exit(-1); +} + int procinfo(char *pname, int *pid, int *uid) { @@ -290,32 +494,12 @@ procinfo(char *pname, int *pid, int *uid) } int -rcontrol_get_string(const char *prefix, int pid, int *val) +rcontrol_get_string(const char *name, int *val) { int t, status; - char *name; uint64_t x; - status = NOTIFY_STATUS_OK; - - if (pid == RC_SYSLOGD) - { - status = notify_register_plain(NOTIFY_SYSTEM_ASL_FILTER, &t); - } - else if (pid == RC_MASTER) - { - status = notify_register_plain(NOTIFY_SYSTEM_MASTER, &t); - } - else - { - name = NULL; - asprintf(&name, "%s.%d", prefix, pid); - if (name == NULL) return NOTIFY_STATUS_FAILED; - - status = notify_register_plain(name, &t); - free(name); - } - + status = notify_register_plain(name, &t); if (status != NOTIFY_STATUS_OK) return status; x = 0; @@ -328,37 +512,17 @@ rcontrol_get_string(const char *prefix, int pid, int *val) } int -rcontrol_set_string(const char *prefix, int pid, int filter) +rcontrol_set_string(const char *name, int filter) { int t, status; - char *name; uint64_t x; - status = NOTIFY_STATUS_OK; - - if (pid == RC_SYSLOGD) - { - status = notify_register_plain(NOTIFY_SYSTEM_ASL_FILTER, &t); - } - else if (pid == RC_MASTER) - { - status = notify_register_plain(NOTIFY_SYSTEM_MASTER, &t); - } - else - { - name = NULL; - asprintf(&name, "%s.%d", prefix, pid); - if (name == NULL) return NOTIFY_STATUS_FAILED; - - status = notify_register_plain(name, &t); - free(name); - } - + status = notify_register_plain(name, &t); if (status != NOTIFY_STATUS_OK) return status; x = filter; status = notify_set_state(t, x); - if ((pid == RC_SYSLOGD) && (status == NOTIFY_STATUS_OK)) status = notify_post(NOTIFY_SYSTEM_ASL_FILTER); + notify_post(NOTIFY_RC); notify_cancel(t); return status; } @@ -511,31 +675,40 @@ asl_filter_string(int f) return str; } +const char * +rcontrol_name(pid_t pid, uid_t uid) +{ + static char str[1024]; + + if (pid == RC_MASTER) return NOTIFY_SYSTEM_MASTER; + + memset(str, 0, sizeof(str)); + if (uid == 0) snprintf(str, sizeof(str) - 1, "%s.%d", NOTIFY_PREFIX_SYSTEM, pid); + else snprintf(str, sizeof(str) - 1, "user.uid.%d.syslog.%d", uid, pid); + return str; +} + int -rcontrol_get(const char *prefix, int pid) +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(NULL, pid, &filter); + 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; } - status = rcontrol_get_string(prefix, pid, &filter); + status = rcontrol_get_string(rcontrol_name(pid, uid), &filter); if (status == NOTIFY_STATUS_OK) { printf("Process %d syslog filter mask: %s\n", pid, asl_filter_string(filter)); @@ -547,31 +720,31 @@ rcontrol_get(const char *prefix, int pid) } int -rcontrol_set(const char *prefix, int pid, int filter) +rcontrol_set(pid_t pid, uid_t uid, int filter) { int status; - const char *name; + 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(NULL, pid, filter); + status = rcontrol_set_string(rcname, filter); if (status == NOTIFY_STATUS_OK) { - printf("Set %s syslog filter mask: %s\n", name, asl_filter_string(filter)); + if (pid == RC_MASTER) status = notify_post(NOTIFY_SYSTEM_MASTER); 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; } - status = rcontrol_set_string(prefix, pid, filter); + status = rcontrol_set_string(rcname, filter); if (status == NOTIFY_STATUS_OK) { - printf("Set process %d syslog filter mask set: %s\n", pid, asl_filter_string(filter)); + status = notify_post(rcname); return 0; } @@ -580,14 +753,13 @@ rcontrol_set(const char *prefix, int pid, int filter) } int -rsend(aslmsg msg, char *rhost) +rsend(asl_msg_t *msg, char *rhost) { char *str, *out; uint32_t len, level; char *timestr; const char *val; time_t tick; - struct tm gtime; int s; struct sockaddr_in dst; struct hostent *h; @@ -609,28 +781,23 @@ rsend(aslmsg msg, char *rhost) level = ASL_LEVEL_DEBUG; - val = asl_get(msg, ASL_KEY_LEVEL); + val = asl_msg_get_val_for_key(msg, ASL_KEY_LEVEL); if (val != NULL) level = atoi(val); - memset(>ime, 0, sizeof(struct tm)); - timestr = NULL; tick = time(NULL); - gmtime_r(&tick, >ime); - - /* Canonical form: YYYY.MM.DD hh:mm:ss UTC */ - asprintf(×tr, "%d.%02d.%02d %02d:%02d:%02d UTC", gtime.tm_year + 1900, gtime.tm_mon + 1, gtime.tm_mday, gtime.tm_hour, gtime.tm_min, gtime.tm_sec); - + timestr = NULL; + asprintf(×tr, "%lu", tick); if (timestr != NULL) { - asl_set(msg, ASL_KEY_TIME, timestr); + asl_msg_set_key_val(msg, ASL_KEY_TIME, timestr); free(timestr); } - if (gethostname(myname, MAXHOSTNAMELEN) == 0) asl_set(msg, ASL_KEY_HOST, myname); + if (gethostname(myname, MAXHOSTNAMELEN) == 0) asl_msg_set_key_val(msg, ASL_KEY_HOST, myname); len = 0; - str = asl_msg_to_string(msg, &len); + str = asl_msg_to_string((asl_msg_t *)msg, &len); if (str == NULL) return -1; asprintf(&out, "%10u %s\n", len+1, str); @@ -726,11 +893,31 @@ asl_string_to_level(const char *s) return -1; } +const char * +asl_string_to_char_level(const char *s) +{ + if (s == NULL) return NULL; + + if ((s[0] >= '0') && (s[0] <= '7') && (s[1] == '\0')) return s; + + if (!strncasecmp(s, "em", 2)) return "0"; + else if (!strncasecmp(s, "p", 1)) return "0"; + else if (!strncasecmp(s, "a", 1)) return "1"; + else if (!strncasecmp(s, "c", 1)) return "2"; + else if (!strncasecmp(s, "er", 2)) return "3"; + else if (!strncasecmp(s, "x", 1)) return "3"; + else if (!strncasecmp(s, "w", 1)) return "4"; + else if (!strncasecmp(s, "n", 1)) return "5"; + else if (!strncasecmp(s, "i", 1)) return "6"; + else if (!strncasecmp(s, "d", 1)) return "7"; + + return NULL; +} + int syslog_remote_control(int argc, char *argv[]) { int pid, uid, status, mask; - const char *prefix; if ((argc < 3) || (argc > 4)) { @@ -751,9 +938,8 @@ syslog_remote_control(int argc, char *argv[]) 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) { @@ -780,13 +966,9 @@ syslog_remote_control(int argc, char *argv[]) if (pid == 0) pid = RC_MASTER; - prefix = NOTIFY_PREFIX_USER; - if (uid == 0) prefix = NOTIFY_PREFIX_SYSTEM; - if (argc == 4) { if ((pid == RC_MASTER) && (!strcasecmp(argv[3], "off"))) mask = 0; - else if ((pid == RC_SYSLOGD) && (!strcasecmp(argv[3], "off"))) mask = 0; else { mask = asl_string_to_filter(argv[3]); @@ -797,11 +979,11 @@ syslog_remote_control(int argc, char *argv[]) } } - rcontrol_set(prefix, pid, mask); + rcontrol_set(pid, uid, mask); } else { - rcontrol_get(prefix, pid); + rcontrol_get(pid, uid); } return 0; @@ -811,8 +993,8 @@ int syslog_send(int argc, char *argv[]) { int i, start, kv, len, rfmt, rlevel; - aslclient asl; - aslmsg m; + asl_client_t *asl; + asl_msg_t *m; char tmp[64], *str, *rhost; kv = 0; @@ -846,14 +1028,14 @@ syslog_send(int argc, char *argv[]) } } - asl = asl_open(myname, "syslog", 0); - asl_set_filter(asl, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG)); + asl = asl_client_open(myname, "syslog", 0); + asl_client_set_filter(asl, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG)); - m = asl_new(ASL_TYPE_MSG); - asl_set(m, ASL_KEY_SENDER, myname); + m = asl_msg_new(ASL_TYPE_MSG); + asl_msg_set_key_val(m, ASL_KEY_SENDER, myname); sprintf(tmp, "%d", rlevel); - asl_set(m, ASL_KEY_LEVEL, tmp); + asl_msg_set_key_val(m, ASL_KEY_LEVEL, tmp); str = NULL; @@ -869,16 +1051,21 @@ syslog_send(int argc, char *argv[]) strcat(str, argv[i]); if ((i+1) < argc) strcat(str, " "); } - asl_set(m, ASL_KEY_MSG, str); + asl_msg_set_key_val(m, ASL_KEY_MSG, str); } else { - for (i = start + 1; i < argc; i += 2) asl_set(m, argv[i], argv[i + 1]); + for (i = start + 1; i < argc; i += 2) + { + if (!strcmp(argv[i], "-k")) i++; + asl_msg_set_key_val(m, argv[i], argv[i + 1]); + if (!strcmp(argv[i], ASL_KEY_LEVEL)) rlevel = atoi(argv[i + 1]); + } } if (rhost == NULL) { - asl_send(asl, m); + asl_client_send(asl, m); } else if (rfmt == SEND_FORMAT_ASL) { @@ -889,94 +1076,157 @@ syslog_send(int argc, char *argv[]) rlegacy(str, rlevel, rhost); } - asl_free(m); + asl_msg_release(m); if (str != NULL) free(str); - asl_close(asl); + asl_client_release(asl); return 0; } -static void -print_xml_header(FILE *f) +int +syslog_config(int argc, char *argv[]) { - if (f == NULL) return; + int i; + uint32_t x; + uid_t uid; + asl_client_t *asl; + asl_msg_t *m; + asl_string_t *str; + const char *key, *val; - fprintf(f, "\n"); - fprintf(f, "\n"); - fprintf(f, "\n"); - fprintf(f, "\n"); -} + if (argc == 2) + { + asl_msg_t *ctl = _asl_server_control_query(); + if (ctl == NULL) + { + fprintf(stderr, "can't get status information from syslogd\n"); + return -1; + } -static void -print_xml_trailer(FILE *f) -{ - if (f == NULL) return; + for (x = asl_msg_fetch(ctl, 0, &key, &val, NULL); x != IndexNull; x = asl_msg_fetch(ctl, x, &key, &val, NULL)) + { + printf("%s %s\n", key, val); + } - fprintf(f, "\n"); - fprintf(f, "\n"); + asl_msg_release(ctl); + return 0; + } + + 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_client_open(myname, "syslog", 0); + + m = asl_msg_new(ASL_TYPE_MSG); + asl_msg_set_key_val(m, ASL_KEY_LEVEL, ASL_STRING_NOTICE); + asl_msg_set_key_val(m, ASL_KEY_OPTION, ASL_OPT_CONTROL); + asl_msg_set_key_val(m, ASL_KEY_SENDER, myname); + asl_msg_set_key_val(m, ASL_KEY_MSG, asl_string_bytes(str)); + + asl_client_send(asl, m); + + asl_string_release(str); + asl_msg_release(m); + asl_client_release(asl); + + return 0; } -static void -print_xml_str(FILE *f, const char *str) +int +syslog_control(int argc, char *argv[]) { - uint32_t i; + int i; + uid_t uid; + asl_client_t *asl; + asl_msg_t *m; + asl_string_t *str; - if (f == NULL) return; - if (str == NULL) return; + uid = geteuid(); + if (uid != 0) + { + fprintf(stderr, "syslog control limited to use by superuser\n"); + return -1; + } - for (i = 0; str[i] != '\0'; i++) + str = asl_string_new(0); + asl_string_append(str, "@ "); + + for (i = 2; i < argc; i++) { - if (str[i] == '&') fprintf(f, "&"); - else if (str[i] == '<') fprintf(f, "<"); - else if (str[i] == '>') fprintf(f, ">"); - else if (str[i] == '"') fprintf(f, """); - else if (str[i] == '\'') fprintf(f, "'"); - else fprintf(f, "%c", str[i]); + asl_string_append(str, argv[i]); + if ((i + 1) < argc) asl_string_append(str, " "); } + + asl = asl_client_open(myname, "syslog", 0); + + m = asl_msg_new(ASL_TYPE_MSG); + asl_msg_set_key_val(m, ASL_KEY_LEVEL, ASL_STRING_NOTICE); + asl_msg_set_key_val(m, ASL_KEY_OPTION, ASL_OPT_CONTROL); + asl_msg_set_key_val(m, ASL_KEY_SENDER, myname); + asl_msg_set_key_val(m, ASL_KEY_MSG, asl_string_bytes(str)); + + asl_client_send(asl, m); + + asl_string_release(str); + asl_msg_release(m); + asl_client_release(asl); + + return 0; } static void -printsafe(FILE *f, const char *str) +print_xml_header(FILE *f) { - uint8_t c; - uint32_t i; - if (f == NULL) return; - if (str == NULL) return; - for (i = 0; str[i] != '\0'; i++) - { - c = str[i]; + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); +} - if (isascii(c) && iscntrl(c)) - { - if (c == '\n') printf("\\n"); - else if (c == '\t') printf("\t"); - else printf("^%c", c ^ 0100); - } - else printf("%c", c); - } +static void +print_xml_trailer(FILE *f) +{ + if (f == NULL) return; + + fprintf(f, "\n"); + fprintf(f, "\n"); } static void printmsg(FILE *f, asl_msg_t *msg, char *fmt, int pflags) { char *str; - const char *mf, *tf; - uint32_t len, status; - uint64_t msgid; + const char *mf; + uint32_t encode, len, status; + uint64_t xid; if (f == NULL) { if (export != NULL) { - status = asl_store_save(export, msg, -1, -1, &msgid); + xid = 0; + status = asl_file_save(export, msg, &xid); if (status != ASL_STATUS_OK) { - fprintf(stderr, "export database write failed: %s\n", asl_store_error(status)); - asl_store_close(export); + fprintf(stderr, "export file write failed: %s\n", asl_core_error(status)); + asl_file_close(export); export = NULL; } } @@ -984,73 +1234,102 @@ printmsg(FILE *f, asl_msg_t *msg, char *fmt, int pflags) return; } + encode = pflags & 0x0000000f; + mf = ASL_MSG_FMT_RAW; if (fmt != NULL) mf = (const char *)fmt; else if (pflags & FORMAT_STD) mf = ASL_MSG_FMT_STD; 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(msg, mf, tf, &len); - if (str != NULL) + str = asl_format_message((asl_msg_t *)msg, mf, tfmt, encode, &len); + if (str == NULL) return; + + if (pflags & COMPRESS_DUPS) + { + if (last_printmsg_str != NULL) + { + if (!strcmp(str + STD_BSD_DATE_LEN, last_printmsg_str + STD_BSD_DATE_LEN)) + { + last_printmsg_count++; + free(str); + } + else + { + if (last_printmsg_count > 0) + { + fprintf(f, "--- last message repeated %d time%s ---\n", last_printmsg_count, (last_printmsg_count == 1) ? "" : "s"); + } + + free(last_printmsg_str); + last_printmsg_str = str; + last_printmsg_count = 0; + + fprintf(f, "%s", str); + } + } + else + { + last_printmsg_str = str; + last_printmsg_count = 0; + + fprintf(f, "%s", str); + } + } + else { fprintf(f, "%s", str); free(str); } } -uint32_t -send_prune(asl_search_result_t *pl) +asl_msg_list_t * +store_query(asl_msg_list_t *q, uint64_t start, int count, int dir, uint64_t *last) { - char *str; - caddr_t vmstr; - uint32_t len, status; - kern_return_t kstatus; - security_token_t sec; - - if (asl_server_port == MACH_PORT_NULL) return 1; - - len = 0; - str = asl_list_to_string(pl, &len); - - kstatus = vm_allocate(mach_task_self(), (vm_address_t *)&vmstr, len, TRUE); - if (kstatus != KERN_SUCCESS) + if (store == NULL) { - free(str); - return 1; + uint32_t status = asl_store_open_read(NULL, &store); + if (status != 0) return NULL; } - memmove(vmstr, str, len); - free(str); - - sec.val[0] = -1; - sec.val[1] = -1; - status = 0; + return asl_store_match(store, q, last, start, count, 0, dir); +} - kstatus = _asl_server_prune(asl_server_port, (caddr_t)vmstr, len, (int *)&status, &sec); - if (kstatus != KERN_SUCCESS) status = 1; +asl_msg_list_t * +file_query(asl_msg_list_t *q, uint64_t start, int count, int dir, uint64_t *last) +{ + return asl_file_list_match(db_files, q, last, start, count, 0, dir);; +} - return status; +asl_msg_list_t * +legacy_query(asl_msg_list_t *q, uint64_t start, int count, int dir, uint64_t *last) +{ + return asl_file_match(legacy, q, last, start, count, 0, dir); } -asl_search_result_t * -send_query(asl_search_result_t *q, uint64_t start, int count, int dir, uint64_t *last) +asl_msg_list_t * +syslogd_query(asl_msg_list_t *q, uint64_t start, int count, int dir, uint64_t *last) { char *str, *res; caddr_t vmstr; uint32_t len, reslen, status; + int flags; kern_return_t kstatus; - security_token_t sec; - asl_search_result_t *l; + asl_msg_list_t *l; - if (asl_server_port == MACH_PORT_NULL) return NULL; + if (asl_server_port == MACH_PORT_NULL) + { + kstatus = bootstrap_look_up2(bootstrap_port, ASL_SERVICE_NAME, &asl_server_port, 0, BOOTSTRAP_PRIVILEGED_SERVER); + if (kstatus != KERN_SUCCESS) + { + fprintf(stderr, "query failed: can't contact syslogd\n"); + return NULL; + } + } len = 0; - str = asl_list_to_string(q, &len); + str = asl_msg_list_to_string(q, &len); kstatus = vm_allocate(mach_task_self(), (vm_address_t *)&vmstr, len, TRUE); if (kstatus != KERN_SUCCESS) @@ -1064,80 +1343,281 @@ send_query(asl_search_result_t *q, uint64_t start, int count, int dir, uint64_t res = NULL; reslen = 0; - sec.val[0] = -1; - sec.val[1] = -1; status = 0; + flags = 0; + if (dir < 0) flags = QUERY_FLAG_SEARCH_REVERSE; - kstatus = _asl_server_query(asl_server_port, (caddr_t)vmstr, len, start, count, dir, (caddr_t *)&res, &reslen, last, (int *)&status, &sec); + kstatus = _asl_server_query_2(asl_server_port, (caddr_t)vmstr, len, start, count, flags, (caddr_t *)&res, &reslen, last, (int *)&status); if (res == NULL) return NULL; - l = asl_list_from_string(res); + l = asl_msg_list_from_string(res); vm_deallocate(mach_task_self(), (vm_address_t)res, reslen); return l; } -asl_search_result_t * -db_query(asl_store_t *s, asl_search_result_t *q, uint64_t qmin, uint64_t *cmax) +void +filter_and_print(asl_msg_t *msg, asl_msg_list_t *ql, FILE *f, char *pfmt, int pflags) { - uint32_t status; - asl_search_result_t *res; + int i, do_match, did_match; - res = NULL; - status = asl_store_match(s, q, &res, cmax, qmin, 0, 1, 0, 0); - if (status != 0) return NULL; + if (msg == NULL) return; + + do_match = 1; + if (ql == NULL) do_match = 0; + else if (ql->count == 0) do_match = 0; + + did_match = 1; + + if (do_match != 0) + { + did_match = 0; + + for (i = 0; (i < ql->count) && (did_match == 0); i++) + { + did_match = asl_msg_cmp(ql->msg[i], (asl_msg_t *)msg); + } + } - return res; + if (did_match != 0) printmsg(f, msg, pfmt, pflags); } +#if TARGET_OS_EMBEDDED void -search_once(FILE *f, char *pfmt, int pflags, asl_search_result_t *ql, uint64_t qmin, uint64_t *cmax, uint32_t count, uint32_t tail) +syslogd_direct_watch(FILE *f, char *pfmt, int pflags, asl_msg_list_t *ql) { - asl_search_result_t *res; - int i, j; - - if (pflags & FORMAT_XML) print_xml_header(f); + struct sockaddr_in address; + int i, bytes, sock, stream, status; + uint32_t n, inlen; + uint16_t port; + socklen_t addresslength; + char *str, buf[DIRECT_BUF_SIZE]; + asl_msg_t *msg; - i = 0; - while (i < store_count) + if (asl_server_port == MACH_PORT_NULL) { - res = NULL; - if ((dbstore[i] == NULL) && (store_raw == 0)) + status = bootstrap_look_up(bootstrap_port, ASL_SERVICE_NAME, &asl_server_port); + if (status != KERN_SUCCESS) { - if (count == 0) - { - res = send_query(ql, qmin, 0, 0, cmax); - i++; - } - else + fprintf(stderr, "query failed: can't contact syslogd\n"); + exit(1); + } + } + + addresslength = sizeof(address); + sock = socket(AF_INET, SOCK_STREAM, 0); + port = (arc4random() % (IPPORT_HILASTAUTO - IPPORT_HIFIRSTAUTO)) + IPPORT_HIFIRSTAUTO; + + memset(&address, 0, addresslength); + address.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + address.sin_family = AF_INET; + address.sin_port = htons(port); + + status = bind(sock, (struct sockaddr *)&address, sizeof(address)); + + for (i = 0; (i < MAX_RANDOM) && (status < 0); i++) + { + port = (arc4random() % (IPPORT_HILASTAUTO - IPPORT_HIFIRSTAUTO)) + IPPORT_HIFIRSTAUTO; + address.sin_port = htons(port); + + status = bind(sock, (struct sockaddr *)&address, sizeof(address)); + } + + if (status < 0) + { + fprintf(stderr, "query failed: can't find a port to connect to syslogd\n"); + exit(1); + } + + bytes = 0; + + if (listen(sock, 1) == -1) + { + perror("listen"); + exit(1); + } + + i = htons(port); + _asl_server_register_direct_watch(asl_server_port, i); + + stream = accept(sock, (struct sockaddr*)&address, &addresslength); + if (stream == -1) + { + perror("accept"); + exit(1); + } + + forever + { + inlen = 0; + errno = 0; + bytes = recvfrom(stream, &n, sizeof(n), 0, NULL, NULL); + 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; + + str = NULL; + if (inlen <= DIRECT_BUF_SIZE) + { + str = buf; + } + else + { + str = calloc(1, inlen + 1); + if (str == NULL) { - res = send_query(ql, qmin, count, 0, cmax); - if (*cmax > qmin) qmin = *cmax; - if (res == NULL) i++; - else if (res->count < count) i++; + fprintf(stderr, "\ncan't allocate memory - exiting\n"); + close(stream); + close(sock); + exit(1); } } - else + + n = 0; + while (n < inlen) { - res = db_query(dbstore[i], ql, qmin, cmax); - i++; + errno = 0; + bytes = recvfrom(stream, str + n, inlen - n, 0, NULL, NULL); + 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); } - if (res != NULL) + msg = asl_msg_from_string(str); + if (str != buf) free(str); + filter_and_print(msg, ql, f, pfmt, pflags); + asl_msg_release(msg); + } + + close(stream); + close(sock); + + address.sin_addr.s_addr = 0; +} +#endif + +int +sort_compare_key(asl_msg_t *a, asl_msg_t *b, const char *key) +{ + const char *va, *vb; + uint64_t na, nb; + + if (key == NULL) return 0; + + va = asl_msg_get_val_for_key(a, key); + vb = asl_msg_get_val_for_key(b, key); + + if (va == NULL) return -1; + if (vb == NULL) return 1; + + if (sort_numeric == 1) + { + na = atoll(va); + nb = atoll(vb); + if (na < nb) return -1; + if (na > nb) return 1; + return 0; + } + + return strcmp(va, vb); +} + +int +sort_compare(const void *ap, const void *bp) +{ + int cmp; + asl_msg_t *a, *b; + + if (sort_key == NULL) return 0; + + a = (asl_msg_t *)ap; + b = (asl_msg_t *)bp; + + cmp = sort_compare_key(a, b, sort_key); + if ((cmp == 0) && (sort_key_2 != NULL)) cmp = sort_compare_key(a, b, sort_key_2); + + return cmp; +} + +void +search_once(FILE *f, char *pfmt, int pflags, asl_msg_list_t *ql, uint64_t qmin, uint64_t *cmax, uint32_t count, uint32_t batch, int dir, uint32_t tail) +{ + asl_msg_list_t *res; + int i, more, total; + + if (pflags & FORMAT_XML) print_xml_header(f); + + res = NULL; + more = 1; + total = 0; + + while (more == 1) + { + if (batch == 0) more = 0; + + if ((dbselect == DB_SELECT_ASL) || (dbselect == DB_SELECT_STORE)) res = store_query(ql, qmin, batch, dir, cmax); + else if (dbselect == DB_SELECT_FILES) res = file_query(ql, qmin, batch, dir, cmax); + else if (dbselect == DB_SELECT_SYSLOGD) res = syslogd_query(ql, qmin, batch, dir, cmax); + else if (dbselect == DB_SELECT_LEGACY) res = legacy_query(ql, qmin, batch, dir, cmax); + + if ((dir >= 0) && (*cmax > qmin)) qmin = *cmax + 1; + else if ((dir < 0) && (*cmax < qmin)) qmin = *cmax - 1; + + if (res == NULL) + { + more = 0; + } + else { - j = 0; + if ((batch > 0) && (res->count < batch)) more = 0; + total += res->count; + if ((count > 0) && (total >= count)) more = 0; + + i = 0; if (tail != 0) { - j = res->count - tail; + i = res->count - tail; tail = 0; - if (j < 0) j = 0; + if (i < 0) i = 0; } - for (; j < res->count; j++) printmsg(f, res->msg[j], pfmt, pflags); + if (sort_key != NULL) + { + qsort(res->msg, res->count, sizeof(asl_msg_t *), sort_compare); + } - aslresponse_free((aslresponse)res); + if ((f != NULL) || (export != NULL)) + { + for (; i < res->count; i++) printmsg(f, res->msg[i], pfmt, pflags); + } + + asl_msg_list_release(res); } } + if ((pflags & COMPRESS_DUPS) && (last_printmsg_count > 0)) + { + fprintf(f, "--- last message repeated %d time%s ---\n", last_printmsg_count, (last_printmsg_count == 1) ? "" : "s"); + free(last_printmsg_str); + last_printmsg_str = NULL; + last_printmsg_count = 0; + } + if (pflags & FORMAT_XML) print_xml_trailer(f); } @@ -1258,11 +1738,30 @@ int add_op(asl_msg_t *q, char *key, char *op, char *val, uint32_t flags) { uint32_t o; + const char *qval; if (key == NULL) return -1; if (q == NULL) return -1; + qval = NULL; + if (strcmp(key, ASL_KEY_TIME) == 0) + { + qval = (const char *)val; + } + else if ((strcmp(key, ASL_KEY_LEVEL) == 0) && (_isanumber(val) == 0)) + { + /* Convert level strings to numeric values */ + qval = asl_string_to_char_level(val); + if (qval == NULL) + { + fprintf(stderr, "invalid value for \"Level\"key: %s\n", val); + return -1; + } + } + o = ASL_QUERY_OP_NULL; + if (val == NULL) o = ASL_QUERY_OP_TRUE; + if (op != NULL) { o = optype(op); @@ -1273,90 +1772,152 @@ add_op(asl_msg_t *q, char *key, char *op, char *val, uint32_t flags) return -1; } - if ((o & ASL_QUERY_OP_NUMERIC) && (strcmp(key, ASL_KEY_TIME) != 0) && (_isanumber(val) == 0)) + if ((qval == NULL) && (o & ASL_QUERY_OP_NUMERIC) && (_isanumber(val) == 0)) { fprintf(stderr, "non-numeric value supplied for numeric operator %s %s %s\n", key, op, val); return -1; } - } o |= flags; - asl_set_query(q, key, val, o); + if (qval != NULL) asl_msg_set_key_val_op(q, key, qval, o); + else asl_msg_set_key_val_op(q, key, val, o); return 0; } -static void -add_store(const char *name, uint32_t flags) +static uint32_t +add_db_file(const char *name) { - asl_store_t *s; + asl_file_t *s; uint32_t status; + if (dbselect == DB_SELECT_LEGACY) + { + fprintf(stderr, "syslog can only read one legacy format database\n"); + fprintf(stderr, "can't combine legacy and non-legacy databases in a single search\n"); + exit(1); + } + + /* shouldn't happen */ + if (name == NULL) return DB_SELECT_ASL; + s = NULL; + status = asl_file_open_read(name, &s); + if (status != ASL_STATUS_OK) + { + fprintf(stderr, "data store file %s open failed: %s \n", name, asl_core_error(status)); + exit(1); + } - if (name != NULL) + if (s == NULL) { - status = asl_store_open(name, flags, &s); - if (status != ASL_STATUS_OK) - { - fprintf(stderr, "database %s open failed: %s \n", name, asl_store_error(status)); - exit(1); - } + fprintf(stderr, "data store file %s open failed\n", name); + exit(1); + } - if (s == NULL) + if (s->flags & ASL_FILE_FLAG_LEGACY_STORE) + { + if (db_files != NULL) { - fprintf(stderr, "database %s open failed\n", name); + fprintf(stderr, "syslog can only read a single legacy format database\n"); + fprintf(stderr, "can't combine legacy and non-legacy databases in a single search\n"); exit(1); } + + legacy = s; + return DB_SELECT_LEGACY; } - else + + db_files = asl_file_list_add(db_files, s); + return DB_SELECT_FILES; +} + +static uint32_t +add_db_dir(const char *name) +{ + DIR *dp; + struct dirent *dent; + uint32_t status; + asl_file_t *s; + char *path; + + /* + * Try opening as a data store + */ + status = asl_store_open_read(name, &store); + if (status == 0) { - store_raw = 0; + if (name == NULL) return DB_SELECT_ASL; + if (!strcmp(name, PATH_ASL_STORE)) return DB_SELECT_ASL; + return DB_SELECT_STORE; } - if (store_count == 0) dbstore = (asl_store_t **)calloc(1, sizeof(asl_store_t *)); - else dbstore = (asl_store_t **)reallocf(dbstore, (store_count + 1) * sizeof(asl_store_t *)); - - if (dbstore == NULL) + /* + * Open all readable files + */ + dp = opendir(name); + if (dp == NULL) { - fprintf(stderr, "Can't allocate memory!\n"); + fprintf(stderr, "%s: %s\n", name, strerror(errno)); exit(1); } - dbstore[store_count] = s; - store_count++; + while ((dent = readdir(dp)) != NULL) + { + if (dent->d_name[0] == '.') continue; + + path = NULL; + asprintf(&path, "%s/%s", name, dent->d_name); + + /* + * asl_file_open_read will fail if path is NULL, + * if the file is not an ASL store file, + * or if it isn't readable. + */ + s = NULL; + status = asl_file_open_read(path, &s); + if (path != NULL) free(path); + if ((status != ASL_STATUS_OK) || (s == NULL)) continue; + + db_files = asl_file_list_add(db_files, s); + } + + closedir(dp); + + return DB_SELECT_FILES; } int main(int argc, char *argv[]) { FILE *outfile; - int i, j, n, watch, prune, status, pflags, tflags, sflags, iamroot, user_tflag; + 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, *lx, *res; + asl_msg_list_t *qlist; asl_msg_t *cq; - char *logname, *pfmt; - const char *dbname, *exportname; - uint32_t flags, tail_count, batch; + char *pfmt; + const char *exportname; + uint32_t flags, tail_count, batch, encode; uint64_t qmin, cmax; - kern_return_t kstatus; watch = 0; - prune = 0; iamroot = 0; user_tflag = 0; - logname = NULL; pfmt = NULL; flags = 0; tail_count = 0; batch = FETCH_BATCH; - sflags = ASL_STORE_FLAG_READ_ONLY; - pflags = FORMAT_STD; - tflags = TIME_LCL; + pflags = FORMAT_STD | COMPRESS_DUPS; + encode = ASL_ENCODE_SAFE; cq = NULL; - dbname = _PATH_ASL_STORE; exportname = NULL; + export_preserve_id = 0; + saw_dash_d = 0; + since_boot = 0; + + i = asl_store_location(); + if (i == ASL_STORE_LOCATION_MEMORY) dbselect = DB_SELECT_SYSLOGD; if (getuid() == 0) iamroot = 1; @@ -1368,6 +1929,31 @@ main(int argc, char *argv[]) exit(0); } + 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], "-control")) || (!strcmp(argv[i], "--control"))) + { + syslog_control(argc, argv); + exit(0); + } + + if ((!strcmp(argv[i], "-module")) || (!strcmp(argv[i], "--module"))) + { + module_control(argc, argv); + exit(0); + } + if (!strcmp(argv[i], "-s")) { syslog_send(argc, argv); @@ -1379,20 +1965,16 @@ main(int argc, char *argv[]) syslog_remote_control(argc, argv); exit(0); } - - if (!strcmp(argv[i], "-p")) - { - prune = 1; - sflags = 0; - } } - qlist = (asl_search_result_t *)calloc(1, sizeof(asl_search_result_t)); + qlist = asl_msg_list_new(); if (qlist == NULL) exit(1); + cq = NULL; + for (i = 1; i < argc; i++) { - if (!strcmp(argv[i], "-db")) + if (!strcmp(argv[i], "-f")) { if ((i + 1) < argc) { @@ -1400,32 +1982,65 @@ main(int argc, char *argv[]) { if (!strcmp(argv[j], "-")) { - /* -db - means add syslogd search (dbstore is NULL) */ - add_store(NULL, sflags); + dbselect = DB_SELECT_SYSLOGD; + i++; + break; } - else if (argv[j][0] == '-') + else if (argv[j][0] == '-') { - if (j == (i + 1)) - { - /* No databases: add /var/log/asl.db */ - add_store(_PATH_ASL_STORE, sflags); - i = j - 1; - } + break; + } + else + { + dbselect = add_db_file(argv[j]); + i++; + } + } + } + } + else if (!strcmp(argv[i], "-d")) + { + saw_dash_d = i + 1; + if (saw_dash_d < argc) + { + for (j = saw_dash_d; j < argc; j++) + { + if (!strcmp(argv[j], "store")) + { + dbselect = add_db_dir(PATH_ASL_STORE); + i++; + } + else if (!strcmp(argv[j], "archive")) + { + dbselect = add_db_dir(PATH_ASL_ARCHIVE); + i++; + } + else if (argv[j][0] == '-') + { break; } else { - add_store(argv[j], sflags); + dbselect = add_db_dir(argv[j]); + i++; } } } else { - /* No databases: add /var/log/asl.db */ - add_store(_PATH_ASL_STORE, sflags); + fprintf(stderr, "missing directory name following -d flag\n"); + return -1; } } + else if (!strcmp(argv[i], "-b")) + { + batch = atoi(argv[++i]); + } + else if (!strcmp(argv[i], "-B")) + { + since_boot = 1; + } else if (!strcmp(argv[i], "-w")) { watch = 1; @@ -1433,41 +2048,101 @@ main(int argc, char *argv[]) if (((i + 1) < argc) && (argv[i + 1][0] != '-')) { i++; - tail_count = atoi(argv[i]); + if (!strcmp(argv[i], "all")) + { + tail_count = (uint32_t)-1; + } + else if (!strcmp(argv[i], "boot")) + { + since_boot = 1; + } + else + { + tail_count = atoi(argv[i]); + } + } + } + else if (!strcmp(argv[i], "-sort")) + { + if (((i + 1) < argc) && (argv[i + 1][0] != '-')) + { + i++; + sort_key = argv[i]; + + if (((i + 1) < argc) && (argv[i + 1][0] != '-')) + { + i++; + sort_key_2 = argv[i]; + } } + else + { + sort_key = ASL_KEY_MSG_ID; + } + + batch = 0; + } + else if (!strcmp(argv[i], "-nsort")) + { + if (((i + 1) < argc) && (argv[i + 1][0] != '-')) + { + i++; + sort_key = argv[i]; + + if (((i + 1) < argc) && (argv[i + 1][0] != '-')) + { + i++; + sort_key_2 = argv[i]; + } + } + else + { + sort_key = ASL_KEY_MSG_ID; + } + + sort_numeric = 1; + batch = 0; } else if (!strcmp(argv[i], "-u")) { - tflags = TIME_UTC; + tfmt = "Z"; user_tflag = 1; } - else if (!strcmp(argv[i], "-x")) + else if ((!strcmp(argv[i], "-x")) || (!strcmp(argv[i], "-X"))) { if ((i + 1) >= argc) { - aslresponse_free(qlist); + asl_msg_list_release(qlist); usage(); exit(1); } + if (!strcmp(argv[i], "-x")) export_preserve_id = 1; + exportname = argv[++i]; } - else if (!strcmp(argv[i], "-f")) + else if (!strcmp(argv[i], "-E")) { if ((i + 1) >= argc) { - aslresponse_free(qlist); + asl_msg_list_release(qlist); usage(); exit(1); } - logname = argv[++i]; + i++; + + if (!strcmp(argv[i], "vis")) encode = ASL_ENCODE_ASL; + else if (!strcmp(argv[i], "safe")) encode = ASL_ENCODE_SAFE; + else if (!strcmp(argv[i], "xml")) encode = ASL_ENCODE_XML; + else if (!strcmp(argv[i], "none")) encode = ASL_ENCODE_NONE; + else if ((argv[i][0] >= '0') && (argv[i][0] <= '9') && (argv[i][1] == '\0')) encode = atoi(argv[i]); } else if (!strcmp(argv[i], "-F")) { if ((i + 1) >= argc) { - aslresponse_free(qlist); + asl_msg_list_release(qlist); usage(); exit(1); } @@ -1477,19 +2152,20 @@ main(int argc, char *argv[]) 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")) { - pflags = FORMAT_STD; + pflags = FORMAT_STD | COMPRESS_DUPS; } else if (!strcmp(argv[i], "bsd")) { - pflags = FORMAT_LEGACY; + pflags = FORMAT_LEGACY | COMPRESS_DUPS; } else if (!strcmp(argv[i], "xml")) { pflags = FORMAT_XML; + encode = ASL_ENCODE_XML; } else { @@ -1501,38 +2177,29 @@ main(int argc, char *argv[]) { if ((i + 1) >= argc) { - aslresponse_free(qlist); + asl_msg_list_release(qlist); usage(); exit(1); } 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 = pflags & ~COMPRESS_DUPS; } else if (!strcmp(argv[i], "-o")) { flags = 0; - if (qlist->count == 0) + if (cq != NULL) { - qlist->msg = (asl_msg_t **)calloc(1, sizeof(asl_msg_t *)); + asl_msg_list_append(qlist, cq); + asl_msg_release(cq); + cq = NULL; } - else - { - qlist->msg = (asl_msg_t **)reallocf(qlist->msg, (qlist->count + 1) * sizeof(asl_msg_t *)); - } - - if (qlist->msg == NULL) exit(1); - - cq = asl_new(ASL_TYPE_QUERY); - qlist->msg[qlist->count] = cq; - qlist->count++; } else if (!strcmp(argv[i], "-n")) { @@ -1540,22 +2207,23 @@ main(int argc, char *argv[]) } else if (!strcmp(argv[i], "-C")) { - if (qlist->count == 0) + if (cq != NULL) { - qlist->msg = (asl_msg_t **)calloc(1, sizeof(asl_msg_t *)); - if (qlist->msg == NULL) exit(1); - - cq = asl_new(ASL_TYPE_QUERY); - qlist->msg[qlist->count] = cq; - qlist->count++; + asl_msg_list_append(qlist, cq); + asl_msg_release(cq); + cq = NULL; } + flags = 0; + cq = asl_msg_new(ASL_TYPE_QUERY); status = add_op(cq, ASL_KEY_FACILITY, OP_EQ, FACILITY_CONSOLE, flags); + asl_msg_list_append(qlist, cq); + asl_msg_release(cq); + cq = NULL; - flags = 0; if (status != 0) { - aslresponse_free(qlist); + asl_msg_list_release(qlist); exit(1); } } @@ -1584,15 +2252,7 @@ main(int argc, char *argv[]) continue; } - if (qlist->count == 0) - { - qlist->msg = (asl_msg_t **)calloc(1, sizeof(asl_msg_t *)); - if (qlist->msg == NULL) exit(1); - - cq = asl_new(ASL_TYPE_QUERY); - qlist->msg[qlist->count] = cq; - qlist->count++; - } + if (cq == NULL) cq = asl_msg_new(ASL_TYPE_QUERY); status = 0; if (n == 1) status = add_op(cq, argv[i], NULL, NULL, flags); @@ -1602,102 +2262,58 @@ main(int argc, char *argv[]) flags = 0; if (status != 0) { - aslresponse_free(qlist); + asl_msg_list_release(qlist); exit(1); } - } - } - - pflags |= tflags; - - if (store_count == 0) add_store(NULL, sflags); - kstatus = bootstrap_look_up(bootstrap_port, ASL_SERVICE_NAME, &asl_server_port); - if (kstatus != KERN_SUCCESS) - { - if (prune == 1) - { - fprintf(stderr, "prune operation failed: can't contact syslogd server\n"); - exit(1); + i += (n - 1); } - - if (iamroot == 0) + else { - fprintf(stderr, "operation failed: can't contact syslogd server\n"); + fprintf(stderr, "syslog: unknown option \"%s\"\n", argv[i]); + fprintf(stderr, "run \"syslog -help\" for usage\n"); exit(1); } - - /* force raw access (for single-user mode when syslogd is not running) */ - if (store_raw == 0) - { - fprintf(stderr, "*** can't contact syslogd server - using read-only database access ***\n"); - add_store(_PATH_ASL_STORE, ASL_STORE_FLAG_READ_ONLY); - store_raw = 1; - } } - if (prune == 1) + if (cq != NULL) { - if (watch == 1) - { - fprintf(stderr, "Warning: -w flag has no effect when pruning\n"); - watch = 0; - } + asl_msg_list_append(qlist, cq); + asl_msg_release(cq); + cq = NULL; + } - if (qlist->count == 0) - { - fprintf(stderr, "no queries for pruning\n"); + pflags |= encode; - aslresponse_free(qlist); - for (j = 0; j < store_count; j++) asl_store_close(dbstore[j]); - if (dbstore != NULL) free(dbstore); + outfile = stdout; - exit(0); + /* + * Catch and report some cases where watch (-w) doesn't work + */ + if (watch == 1) + { + if (sort_key != NULL) + { + fprintf(stderr, "Warning: -w flag has no effect with -sort flag\n"); + watch = 0; } - for (i = 0; i < store_count; i++) + if (dbselect == DB_SELECT_FILES) { - status = ASL_STATUS_OK; - - if ((dbstore[i] == NULL) && (store_raw == 0)) + if (saw_dash_d == 0) { - if (iamroot == 0) - { - fprintf(stderr, "you must be root to prune the log database\n"); - - aslresponse_free(qlist); - for (j = 0; j < store_count; j++) asl_store_close(dbstore[j]); - if (dbstore != NULL) free(dbstore); - - exit(1); - } - - status = send_prune(qlist); + fprintf(stderr, "Warning: -w flag not supported for a set of one or more files\n"); } else { - status = asl_store_prune(dbstore[i], qlist); + fprintf(stderr, "Warning: directory \"%s\" is not an ASL data store\n", argv[saw_dash_d]); + fprintf(stderr, " -w flag not supported for a set of one or more files\n"); } - if (status != ASL_STATUS_OK) - { - fprintf(stderr, "database prune failed: %s\n", asl_store_error(status)); - - aslresponse_free(qlist); - for (j = 0; j < store_count; j++) asl_store_close(dbstore[j]); - if (dbstore != NULL) free(dbstore); - - exit(1); - } + watch = 0; } - - aslresponse_free(qlist); - - exit(0); } - outfile = stdout; - if (exportname != NULL) { if (watch == 1) @@ -1706,14 +2322,21 @@ main(int argc, char *argv[]) watch = 0; } - status = asl_store_open(exportname, 0, &export); + status = asl_file_open_write(exportname, 0644, -1, -1, &export); if (status != ASL_STATUS_OK) { - aslresponse_free(qlist); - fprintf(stderr, "export database open failed: %s\n", asl_store_error(status)); + asl_msg_list_release(qlist); + fprintf(stderr, "export file open failed: %s\n", asl_core_error(status)); exit(1); } + /* + * allow the string cache to be unlimited to maximize string dup compression + * preserve message IDs + */ + export->flags = ASL_FILE_FLAG_UNLIMITED_CACHE; + if (export_preserve_id != 0) export->flags |= ASL_FILE_FLAG_PRESERVE_MSG_ID; + outfile = NULL; pflags = EXPORT; } @@ -1723,75 +2346,79 @@ main(int argc, char *argv[]) notify_file = -1; notify_token = -1; - if (watch == 1) + /* set starting point */ + if (since_boot == 1) { - if (store_raw == 1) - { - fprintf(stderr, "Warning: -w flag can only be used to watch syslogd's active database\n"); - watch = 0; - } - else if (store_count > 1) - { - fprintf(stderr, "Warning: -w flag has no effect with multiple databases\n"); - watch = 0; - } - else - { - status = notify_register_file_descriptor("com.apple.system.logger.message", ¬ify_file, 0, ¬ify_token); - if (status != NOTIFY_STATUS_OK) notify_token = -1; - } - } + /* search back for last "BOOT_TIME (ut_type == 2) record */ + asl_msg_list_t *bt; + asl_msg_t *bq; - if ((qlist->count == 0) && (watch == 1) && (store_raw == 0)) - { - lx = (asl_search_result_t *)calloc(1, sizeof(asl_search_result_t)); - if (lx == NULL) exit(1); - - lx->count = 1; - lx->msg = (asl_msg_t **)calloc(1, sizeof(asl_msg_t *)); - if (lx->msg == NULL) + bt = asl_msg_list_new(); + if (bt == NULL) { - aslresponse_free(lx); + fprintf(stderr, "\ncan't allocate memory - exiting\n"); exit(1); } - - lx->msg[0] = asl_new(ASL_TYPE_QUERY); - if (lx->msg[0] == NULL) + + bq = asl_msg_new(ASL_TYPE_QUERY); + if (bq == NULL) { - aslresponse_free(lx); + fprintf(stderr, "\ncan't allocate memory - exiting\n"); exit(1); } - - asl_set_query(lx->msg[0], "Level", "0", ASL_QUERY_OP_NUMERIC | ASL_QUERY_OP_GREATER_EQUAL); + + asl_msg_list_append(bt, bq); + asl_msg_release(bq); + + asl_msg_set_key_val_op(bq, "ut_type", "2", ASL_QUERY_OP_EQUAL); + + search_once(NULL, NULL, 0, (asl_msg_list_t *)bt, -1, &qmin, 1, 1, -1, 0); + asl_msg_list_release(bt); + + if (qmin > 0) qmin--; + tail_count = 0; + } + else if (watch == 1) + { + /* go back tail_count records from last record */ qmin = -1; - res = send_query(lx, qmin, 1, 1, &cmax); - aslresponse_free(lx); - aslresponse_free(res); - qmin = cmax - tail_count; + search_once(NULL, NULL, 0, qlist, qmin, &cmax, 1, 1, -1, 0); + + if (cmax >= tail_count) qmin = cmax - tail_count; + else qmin = 0; + tail_count = 0; } - if (qlist->count == 0) + if ((watch == 1) && (dbselect == DB_SELECT_ASL)) { - qlist->msg = (asl_msg_t **)calloc(1, sizeof(asl_msg_t *)); - if (qlist->msg == NULL) exit(1); - - cq = asl_new(ASL_TYPE_QUERY); - qlist->msg[qlist->count] = cq; - qlist->count++; + status = notify_register_file_descriptor("com.apple.system.logger.message", ¬ify_file, 0, ¬ify_token); + if (status != NOTIFY_STATUS_OK) notify_token = -1; } - search_once(outfile, pfmt, pflags, qlist, qmin, &cmax, batch, tail_count); + /* output should be line buffered */ + if (outfile != NULL) setlinebuf(outfile); + + search_once(outfile, pfmt, pflags, qlist, qmin + 1, &cmax, 0, 0, 1, tail_count); if (watch == 1) { - if (notify_token == -1) + if (dbselect == DB_SELECT_SYSLOGD) + { +#if TARGET_OS_EMBEDDED + 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) { forever { usleep(500000); if (cmax > qmin) qmin = cmax; - search_once(outfile, pfmt, pflags, qlist, qmin, &cmax, 0, 0); + search_once(outfile, pfmt, pflags, qlist, qmin + 1, &cmax, 0, batch, 1, 0); } } else @@ -1799,16 +2426,16 @@ main(int argc, char *argv[]) while (read(notify_file, &i, 4) == 4) { if (cmax > qmin) qmin = cmax; - search_once(outfile, pfmt, pflags, qlist, qmin, &cmax, 0, 0); + search_once(outfile, pfmt, pflags, qlist, qmin + 1, &cmax, 0, batch, 1, 0); } } } - for (i = 0; i < store_count; i++) asl_store_close(dbstore[i]); - if (dbstore != NULL) free(dbstore); - if (export != NULL) asl_store_close(export); + if (db_files != NULL) asl_file_list_close(db_files); + if (store != NULL) asl_store_release(store); + if (export != NULL) asl_file_release(export); - aslresponse_free(qlist); + asl_msg_list_release(qlist); exit(0); }