]> git.saurik.com Git - apple/syslog.git/blob - libsystem_asl.tproj/src/asl.c
29640c15ccdc107c868a074526236abe8826b902
[apple/syslog.git] / libsystem_asl.tproj / src / asl.c
1 /*
2 * Copyright (c) 2004-2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <assert.h>
25 #include <string.h>
26 #include <stdint.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <ctype.h>
30 #include <unistd.h>
31 #include <stdarg.h>
32 #include <syslog.h>
33 #include <errno.h>
34 #include <limits.h>
35 #include <time.h>
36 #include <sys/time.h>
37 #include <sys/fcntl.h>
38 #include <crt_externs.h>
39 #include <asl.h>
40 #include <regex.h>
41 #include <notify.h>
42 #include <mach/mach.h>
43 #include <mach/std_types.h>
44 #include <mach/mig.h>
45 #include <mach/mach_types.h>
46 #include <sys/types.h>
47 #include <servers/bootstrap.h>
48 #include <bootstrap_priv.h>
49 #include <pthread.h>
50 #include <dispatch/dispatch.h>
51 #include <libkern/OSAtomic.h>
52 #include <asl_ipc.h>
53 #include "asl_core.h"
54 #include "asl_msg.h"
55 #include "asl_store.h"
56 #include "asl_private.h"
57
58 #define streq(A, B) (strcmp(A, B) == 0)
59 #define strcaseeq(A, B) (strcasecmp(A, B) == 0)
60
61 #define forever for(;;)
62
63 #define FETCH_BATCH 256
64
65 #define LEVEL_MASK 0x0000000f
66 #define EVAL_MASK 0x000000f0
67 #define EVAL_IGNORE 0x00000000
68 #define EVAL_ASLFILE 0x00000010
69 #define EVAL_SEND 0x00000020
70 #define EVAL_TUNNEL 0x00000040
71 #define EVAL_FILE 0x00000080
72 #define EVAL_QUOTA 0x00000100
73
74 /*
75 * Clients get a max of 36000 messages per hour.
76 * Their quota gets refilled at a rate of 10 messages per second.
77 */
78 #define QUOTA_MPH 36000
79 #define QUOTA_MPS 10
80 #define QUOTA_MSG_INTERVAL 60
81
82 #define QUOTA_MSG "*** LOG MESSAGE QUOTA EXCEEDED - SOME MESSAGES FROM THIS PROCESS HAVE BEEN DISCARDED ***"
83 #define QUOTA_LEVEL "2"
84
85 /* forward */
86 time_t asl_parse_time(const char *);
87 const char *asl_syslog_faciliy_num_to_name(int n);
88 static int _asl_send_message(aslclient ac, uint32_t eval, asl_msg_t *msg, const char *mstring);
89 __private_extern__ asl_client_t *_asl_open_default();
90
91 /* private asl_file SPI */
92 __private_extern__ uint32_t asl_file_open_write_fd(int fd, asl_file_t **s);
93
94 /* notify SPI */
95 uint32_t notify_register_plain(const char *name, int *out_token);
96
97 /* fork handling in syslog.c */
98 extern void _syslog_fork_child();
99
100 typedef struct
101 {
102 int fd;
103 asl_msg_t *msg;
104 dispatch_semaphore_t sem;
105 } asl_aux_context_t;
106
107 typedef struct
108 {
109 int notify_count;
110 int rc_change_token;
111 int notify_token;
112 int master_token;
113 uint64_t proc_filter;
114 uint64_t master_filter;
115 time_t last_send;
116 time_t last_oq_msg;
117 uint32_t quota;
118 dispatch_once_t port_lookup_once;
119 mach_port_t server_port;
120 char *sender;
121 pthread_mutex_t lock;
122 int aux_count;
123 asl_aux_context_t **aux_ctx;
124 asl_client_t *asl;
125 } _asl_global_t;
126
127
128 #ifndef BUILDING_VARIANT
129 __private_extern__ _asl_global_t _asl_global = {0, -1, -1, -1, 0LL, 0LL, 0LL, 0LL, 0, 0, MACH_PORT_NULL, NULL, PTHREAD_MUTEX_INITIALIZER, 0, NULL, NULL};
130
131 #define ASL_SERVICE_NAME "com.apple.system.logger"
132
133 /*
134 * Called from the child process inside fork() to clean up
135 * inherited state from the parent process.
136 *
137 * NB. A lock isn't required, since we're single threaded in this call.
138 */
139 void
140 _asl_fork_child()
141 {
142 _asl_global.notify_count = 0;
143 _asl_global.rc_change_token = -1;
144 _asl_global.master_token = -1;
145 _asl_global.notify_token = -1;
146 _asl_global.quota = 0;
147 _asl_global.last_send = 0;
148 _asl_global.last_oq_msg = 0;
149
150 _asl_global.port_lookup_once = 0;
151 _asl_global.server_port = MACH_PORT_NULL;
152
153 pthread_mutex_init(&(_asl_global.lock), NULL);
154 }
155
156 /*
157 * asl_remote_notify_name: returns the notification key for remote-control filter
158 * changes for this process.
159 */
160 char *
161 asl_remote_notify_name()
162 {
163 pid_t pid = getpid();
164 uid_t euid = geteuid();
165 char *str = NULL;
166
167 if (euid == 0) asprintf(&str, "%s.%d", NOTIFY_PREFIX_SYSTEM, pid);
168 else asprintf(&str, "user.uid.%d.syslog.%d", euid, pid);
169
170 return str;
171 }
172
173 static int
174 _asl_notify_open(int do_lock)
175 {
176 char *notify_name;
177 uint32_t status;
178
179 if (do_lock != 0) pthread_mutex_lock(&_asl_global.lock);
180
181 _asl_global.notify_count++;
182
183 if (_asl_global.notify_token != -1)
184 {
185 if (do_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
186 return 0;
187 }
188
189 if (_asl_global.rc_change_token == -1)
190 {
191 status = notify_register_check(NOTIFY_RC, &_asl_global.rc_change_token);
192 if (status != NOTIFY_STATUS_OK) _asl_global.rc_change_token = -1;
193 }
194
195 if (_asl_global.master_token == -1)
196 {
197 status = notify_register_plain(NOTIFY_SYSTEM_MASTER, &_asl_global.master_token);
198 if (status != NOTIFY_STATUS_OK) _asl_global.master_token = -1;
199 }
200
201 notify_name = asl_remote_notify_name();
202 if (notify_name != NULL)
203 {
204 status = notify_register_plain(notify_name, &_asl_global.notify_token);
205 free(notify_name);
206 if (status != NOTIFY_STATUS_OK) _asl_global.notify_token = -1;
207 }
208
209 if (do_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
210
211 if (_asl_global.notify_token == -1) return -1;
212 return 0;
213 }
214
215 static void
216 _asl_notify_close()
217 {
218 pthread_mutex_lock(&_asl_global.lock);
219
220 if (_asl_global.notify_count > 0) _asl_global.notify_count--;
221
222 if (_asl_global.notify_count > 0)
223 {
224 pthread_mutex_unlock(&_asl_global.lock);
225 return;
226 }
227
228 if (_asl_global.rc_change_token >= 0) notify_cancel(_asl_global.rc_change_token);
229 _asl_global.rc_change_token = -1;
230
231 if (_asl_global.master_token >= 0) notify_cancel(_asl_global.master_token);
232 _asl_global.master_token = -1;
233
234 if (_asl_global.notify_token >= 0) notify_cancel(_asl_global.notify_token);
235 _asl_global.notify_token = -1;
236
237 pthread_mutex_unlock(&_asl_global.lock);
238 }
239
240 static void
241 _asl_global_init()
242 {
243 if (_asl_global.server_port == MACH_PORT_NULL)
244 {
245 mach_port_t newport = MACH_PORT_NULL;
246 char *str = getenv("ASL_DISABLE");
247 if ((str == NULL) || strcmp(str, "1"))
248 {
249 bootstrap_look_up2(bootstrap_port, ASL_SERVICE_NAME, &newport, 0, BOOTSTRAP_PRIVILEGED_SERVER);
250 if (newport != MACH_PORT_NULL)
251 {
252 if (!OSAtomicCompareAndSwap32Barrier(MACH_PORT_NULL, newport, (int32_t *)&_asl_global.server_port))
253 {
254 mach_port_deallocate(mach_task_self(), newport);
255 }
256 }
257 }
258 }
259 }
260
261 static void
262 _asl_global_reset()
263 {
264 mach_port_t tmp = _asl_global.server_port;
265 _asl_global.server_port = MACH_PORT_NULL;
266 mach_port_deallocate(mach_task_self(), tmp);
267 }
268
269 aslclient
270 asl_open(const char *ident, const char *facility, uint32_t opts)
271 {
272 char *name, *x;
273 asl_client_t *asl;
274
275 asl = (asl_client_t *)calloc(1, sizeof(asl_client_t));
276 if (asl == NULL)
277 {
278 errno = ENOMEM;
279 return NULL;
280 }
281
282 asl->options = opts;
283
284 asl->sock = -1;
285
286 _asl_global_init();
287
288 asl->pid = getpid();
289 asl->uid = getuid();
290 asl->gid = getgid();
291
292 asl->filter = ASL_FILTER_MASK_UPTO(ASL_LEVEL_NOTICE);
293
294 if (ident != NULL)
295 {
296 asl->name = strdup(ident);
297 if (asl->name == NULL)
298 {
299 if (asl->sock >= 0) close(asl->sock);
300 free(asl);
301 return NULL;
302 }
303 }
304 else
305 {
306 name = *(*_NSGetArgv());
307 if (name != NULL)
308 {
309 x = strrchr(name, '/');
310 if (x != NULL) x++;
311 else x = name;
312 asl->name = strdup(x);
313 if (asl->name == NULL)
314 {
315 if (asl->sock >= 0) close(asl->sock);
316 free(asl);
317 return NULL;
318 }
319 }
320 }
321
322 asl->facility = NULL;
323 if (facility != NULL) asl->facility = strdup(facility);
324 else asl->facility = strdup(asl_syslog_faciliy_num_to_name(LOG_USER));
325 if (asl->facility == NULL)
326 {
327 if (asl->sock >= 0) close(asl->sock);
328 if (asl->name != NULL) free(asl->name);
329 free(asl);
330 return NULL;
331 }
332
333 if (!(asl->options & ASL_OPT_NO_REMOTE)) _asl_notify_open(1);
334
335 if (asl->options & ASL_OPT_STDERR)
336 {
337 /* only add stderr if it is valid */
338 if (fcntl(STDERR_FILENO, F_GETFD) >= 0)
339 {
340 asl_add_output_file((aslclient)asl, STDERR_FILENO, ASL_MSG_FMT_STD, ASL_TIME_FMT_LCL, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG), ASL_ENCODE_SAFE);
341 }
342 else
343 {
344 /* stderr has been closed, ignore ASL_OPT_STDERR flag */
345 asl->options &= ~ASL_OPT_STDERR;
346 }
347 }
348
349 asl->refcount = 1;
350
351 return (aslclient)asl;
352 }
353
354 aslclient
355 asl_open_from_file(int fd, const char *ident, const char *facility)
356 {
357 char *name, *x;
358 asl_client_t *asl;
359 uint32_t status;
360
361 asl = (asl_client_t *)calloc(1, sizeof(asl_client_t));
362 if (asl == NULL)
363 {
364 errno = ENOMEM;
365 return NULL;
366 }
367
368 asl->options = ASL_OPT_NO_REMOTE;
369 asl->sock = -1;
370
371 asl->pid = getpid();
372 asl->uid = getuid();
373 asl->gid = getgid();
374
375 asl->filter = ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG);
376
377 if (ident != NULL)
378 {
379 asl->name = strdup(ident);
380 if (asl->name == NULL)
381 {
382 free(asl);
383 return NULL;
384 }
385 }
386 else
387 {
388 name = *(*_NSGetArgv());
389 if (name != NULL)
390 {
391 x = strrchr(name, '/');
392 if (x != NULL) x++;
393 else x = name;
394 asl->name = strdup(x);
395 if (asl->name == NULL)
396 {
397 free(asl);
398 return NULL;
399 }
400 }
401 }
402
403 asl->facility = NULL;
404 if (facility != NULL) asl->facility = strdup(facility);
405 else asl->facility = strdup(asl_syslog_faciliy_num_to_name(LOG_USER));
406 if (asl->facility == NULL)
407 {
408 if (asl->name != NULL) free(asl->name);
409 free(asl);
410 return NULL;
411 }
412
413 status = asl_file_open_write_fd(fd, &(asl->aslfile));
414 if (status != ASL_STATUS_OK)
415 {
416 if (asl->name != NULL) free(asl->name);
417 if (asl->facility != NULL) free(asl->facility);
418 free(asl);
419 return NULL;
420 }
421
422 asl->aslfileid = 1;
423 asl->refcount = 1;
424
425 return (aslclient)asl;
426 }
427
428 __private_extern__ void
429 asl_client_release(asl_client_t *asl)
430 {
431 uint32_t i;
432
433 if (asl == NULL) return;
434
435 if (OSAtomicDecrement32(&asl->refcount) > 0) return;
436
437 free(asl->name);
438 free(asl->facility);
439
440 if (asl->sock >= 0) close(asl->sock);
441 if (!(asl->options & ASL_OPT_NO_REMOTE)) _asl_notify_close();
442
443 for (i = 0; i < asl->out_count; i++)
444 {
445 free(asl->out_list[i].mfmt);
446 free(asl->out_list[i].tfmt);
447 }
448
449 free(asl->out_list);
450
451 memset(asl, 0, sizeof(asl_client_t));
452 free(asl);
453 }
454
455 void
456 asl_close(aslclient ac)
457 {
458 asl_client_release((asl_client_t *)ac);
459 }
460
461 __private_extern__ asl_client_t *
462 asl_client_retain(asl_client_t *asl)
463 {
464 int32_t new;
465
466 if (asl == NULL) return NULL;
467
468 new = OSAtomicIncrement32(&asl->refcount);
469 assert(new >= 1);
470
471 return asl;
472 }
473
474 __private_extern__ asl_client_t *
475 _asl_open_default()
476 {
477 static dispatch_once_t once;
478
479 dispatch_once(&once, ^{
480 /*
481 * Do a sleight-of-hand with ASL_OPT_NO_REMOTE to avoid a deadlock
482 * since asl_open(xxx, yyy, 0) calls _asl_notify_open(1)
483 * which locks _asl_global.lock.
484 */
485 _asl_global.asl = asl_open(NULL, NULL, ASL_OPT_NO_REMOTE);
486
487 /* Reset options to clear ASL_OPT_NO_REMOTE bit */
488 if (_asl_global.asl != NULL) _asl_global.asl->options = 0;
489
490 /* Now call _asl_notify_open(0) to finish the work */
491 _asl_notify_open(0);
492 });
493
494 return _asl_global.asl;
495 }
496
497 /*
498 * asl_add_file: write log messages to the given file descriptor
499 * Log messages will be written to this file as well as to the server.
500 */
501 int
502 asl_add_output_file(aslclient ac, int fd, const char *mfmt, const char *tfmt, int filter, int text_encoding)
503 {
504 uint32_t i;
505 int use_global_lock;
506 asl_client_t *asl;
507
508 use_global_lock = 0;
509 asl = (asl_client_t *)ac;
510 if (asl == NULL)
511 {
512 asl = _asl_open_default();
513 if (asl == NULL) return -1;
514 pthread_mutex_lock(&_asl_global.lock);
515 use_global_lock = 1;
516 }
517
518 for (i = 0; i < asl->out_count; i++)
519 {
520 if (asl->out_list[i].fd == fd)
521 {
522 /* update message format, time format, filter, and text encoding */
523 free(asl->out_list[i].mfmt);
524 asl->out_list[i].mfmt = NULL;
525 if (mfmt != NULL) asl->out_list[i].mfmt = strdup(mfmt);
526
527 free(asl->out_list[i].tfmt);
528 asl->out_list[i].tfmt = NULL;
529 if (tfmt != NULL) asl->out_list[i].tfmt = strdup(tfmt);
530
531 asl->out_list[i].encoding = text_encoding;
532 asl->out_list[i].filter = filter;
533
534 if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
535 return 0;
536 }
537 }
538
539 if (asl->out_count == 0)
540 {
541 asl->out_list = (asl_out_file_t *)calloc(1, sizeof(asl_out_file_t));
542 }
543 else
544 {
545 asl->out_list = (asl_out_file_t *)reallocf(asl->out_list, (1 + asl->out_count) * sizeof(asl_out_file_t));
546 }
547
548 if (asl->out_list == NULL)
549 {
550 if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
551 return -1;
552 }
553
554 asl->out_list[asl->out_count].fd = fd;
555 asl->out_list[asl->out_count].encoding = text_encoding;
556 asl->out_list[asl->out_count].filter = filter;
557 if (mfmt != NULL) asl->out_list[asl->out_count].mfmt = strdup(mfmt);
558 if (tfmt != NULL) asl->out_list[asl->out_count].tfmt = strdup(tfmt);
559
560 asl->out_count++;
561
562 if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
563 return 0;
564 }
565
566 int
567 asl_set_output_file_filter(aslclient ac, int fd, int filter)
568 {
569 uint32_t i, last;
570 int use_global_lock;
571 asl_client_t *asl;
572
573 use_global_lock = 0;
574 asl = (asl_client_t *)ac;
575 if (asl == NULL)
576 {
577 asl = _asl_open_default();
578 if (asl == NULL) return -1;
579 pthread_mutex_lock(&_asl_global.lock);
580 use_global_lock = 1;
581 }
582
583 last = 0;
584
585 for (i = 0; i < asl->out_count; i++)
586 {
587 if (asl->out_list[i].fd == fd)
588 {
589 /* update filter */
590 last = asl->out_list[i].filter;
591 asl->out_list[i].filter = filter;
592 break;
593 }
594 }
595
596 if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
597 return last;
598 }
599
600 /* SPI - Deprecated */
601 int
602 asl_add_output(aslclient ac, int fd, const char *mfmt, const char *tfmt, uint32_t text_encoding)
603 {
604 return asl_add_output_file(ac, fd, mfmt, tfmt, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG), text_encoding);
605 }
606
607 int
608 asl_add_log_file(aslclient ac, int fd)
609 {
610 return asl_add_output_file(ac, fd, ASL_MSG_FMT_STD, ASL_TIME_FMT_LCL, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG), ASL_ENCODE_SAFE);
611 }
612
613 /*
614 * asl_remove_output: stop writing log messages to the given file descriptor
615 */
616 int
617 asl_remove_output_file(aslclient ac, int fd)
618 {
619 uint32_t i;
620 int x, use_global_lock;
621 asl_client_t *asl;
622
623 use_global_lock = 0;
624 asl = (asl_client_t *)ac;
625 if (asl == NULL)
626 {
627 asl = _asl_open_default();
628 if (asl == NULL) return -1;
629 pthread_mutex_lock(&_asl_global.lock);
630 use_global_lock = 1;
631 }
632
633 if (asl->out_count == 0)
634 {
635 if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
636 return 0;
637 }
638
639 x = -1;
640 for (i = 0; i < asl->out_count; i++)
641 {
642 if (asl->out_list[i].fd == fd)
643 {
644 x = i;
645 break;
646 }
647 }
648
649 if (x == -1)
650 {
651 if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
652 return 0;
653 }
654
655 free(asl->out_list[x].mfmt);
656 free(asl->out_list[x].tfmt);
657
658 for (i = x + 1; i < asl->out_count; i++, x++)
659 {
660 asl->out_list[x] = asl->out_list[i];
661 }
662
663 asl->out_count--;
664
665 if (asl->out_count == 0)
666 {
667 free(asl->out_list);
668 asl->out_list = NULL;
669 }
670 else
671 {
672 asl->out_list = (asl_out_file_t *)reallocf(asl->out_list, asl->out_count * sizeof(asl_out_file_t));
673
674 if (asl->out_list == NULL)
675 {
676 asl->out_count = 0;
677 if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
678 return -1;
679 }
680 }
681
682 if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
683 return 0;
684 }
685
686 int
687 asl_remove_output(aslclient ac, int fd)
688 {
689 return asl_remove_output_file(ac, fd);
690 }
691
692 int
693 asl_remove_log_file(aslclient ac, int fd)
694 {
695 return asl_remove_output_file(ac, fd);
696 }
697
698 int
699 asl_set_filter(aslclient ac, int f)
700 {
701 int last, use_global_lock;
702 asl_client_t *asl;
703
704 use_global_lock = 0;
705 asl = (asl_client_t *)ac;
706 if (asl == NULL)
707 {
708 asl = _asl_open_default();
709 if (asl == NULL) return -1;
710 pthread_mutex_lock(&_asl_global.lock);
711 use_global_lock = 1;
712 }
713
714 last = asl->filter;
715 asl->filter = f;
716
717 if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
718 return last;
719 }
720
721 /*
722 * Evaluate client / message / level to determine what to do with a message.
723 * Checks filters, tunneling, and log files. Returns EVAL_IGNORE if the message
724 * can be ignored. Otherwise it returns the bits below, ORed with the level.
725 *
726 * EVAL_ASLFILE - will write to an asl file (see asl_open_from_file)
727 * EVAL_SEND - will send to syslogd
728 * EVAL_TUNNEL - will send to syslogd with tunneling enabled
729 * EVAL_FILE - will write to file
730 */
731 uint32_t
732 _asl_evaluate_send(aslclient ac, aslmsg msg, int slevel)
733 {
734 asl_client_t *asl = (asl_client_t *)ac;
735 uint32_t level, lmask, filter, status, tunnel;
736 int check, out;
737 uint64_t v64;
738 const char *val;
739
740 if (asl == NULL)
741 {
742 asl = _asl_open_default();
743 if (asl == NULL) return EVAL_IGNORE;
744 }
745
746 check = ASL_LEVEL_DEBUG;
747 if (slevel >= 0) check = slevel;
748
749 val = asl_get((aslmsg)msg, ASL_KEY_LEVEL);
750 if (val != NULL) check = atoi(val);
751
752 if (check < ASL_LEVEL_EMERG) check = ASL_LEVEL_EMERG;
753 else if (check > ASL_LEVEL_DEBUG) check = ASL_LEVEL_DEBUG;
754 level = check;
755
756 out = check;
757
758 if (asl->aslfile != NULL) return (out | EVAL_ASLFILE);
759
760 lmask = ASL_FILTER_MASK(level);
761
762 filter = asl->filter & 0xff;
763 tunnel = (asl->filter & ASL_FILTER_MASK_TUNNEL) >> 8;
764
765 if (!(asl->options & ASL_OPT_NO_REMOTE))
766 {
767 pthread_mutex_lock(&_asl_global.lock);
768
769 if (_asl_global.rc_change_token >= 0)
770 {
771 /* initialize or re-check process-specific and master filters */
772 check = 0;
773 status = notify_check(_asl_global.rc_change_token, &check);
774 if ((status == NOTIFY_STATUS_OK) && (check != 0))
775 {
776 if (_asl_global.master_token >= 0)
777 {
778 v64 = 0;
779 status = notify_get_state(_asl_global.master_token, &v64);
780 if (status == NOTIFY_STATUS_OK) _asl_global.master_filter = v64;
781 }
782
783 if (_asl_global.notify_token >= 0)
784 {
785 v64 = 0;
786 status = notify_get_state(_asl_global.notify_token, &v64);
787 if (status == NOTIFY_STATUS_OK) _asl_global.proc_filter = v64;
788 }
789 }
790 }
791
792 pthread_mutex_unlock(&_asl_global.lock);
793
794 /* master filter overrides local filter */
795 if (_asl_global.master_filter != 0)
796 {
797 filter = _asl_global.master_filter;
798 tunnel = 1;
799 }
800
801 /* process-specific filter overrides local and master */
802 if (_asl_global.proc_filter != 0)
803 {
804 filter = _asl_global.proc_filter;
805 tunnel = 1;
806 }
807 }
808
809 if ((filter != 0) && ((filter & lmask) != 0))
810 {
811 out |= EVAL_SEND;
812 if (tunnel != 0) out |= EVAL_TUNNEL;
813 if (asl->out_count > 0) out |= EVAL_FILE;
814
815 return out;
816 }
817
818 if ((asl->options & ASL_OPT_SYSLOG_LEGACY) && (filter != 0) && ((filter & lmask) == 0))
819 {
820 return EVAL_IGNORE;
821 }
822
823 if (asl->out_count > 0) return (out | EVAL_FILE);
824
825 return EVAL_IGNORE;
826 }
827
828 #endif /* BUILDING_VARIANT */
829
830 /*
831 * _asl_lib_vlog
832 * Internal routine used by asl_vlog.
833 * msg: an aslmsg
834 * eval: log level and send flags for the message
835 * format: A formating string
836 * ap: va_list for the format
837 * returns 0 for success, non-zero for failure
838 */
839 static int
840 _asl_lib_vlog(aslclient ac, uint32_t eval, aslmsg msg, const char *format, va_list ap)
841 {
842 int saved_errno = errno;
843 int status;
844 char *str, *fmt, estr[NL_TEXTMAX];
845 uint32_t i, len, elen, expand;
846 asl_client_t *asl;
847
848 asl = (asl_client_t *)ac;
849 if (asl == NULL)
850 {
851 /*
852 * Initialize _asl_global so that asl_new will have global data.
853 * Not strictly necessary, but helps performance.
854 */
855 asl = _asl_open_default();
856 if (asl == NULL) return -1;
857 }
858
859 if (format == NULL) return -1;
860
861 /* insert strerror for %m */
862 len = 0;
863 elen = 0;
864
865 expand = 0;
866 for (i = 0; format[i] != '\0'; i++)
867 {
868 if (format[i] == '%')
869 {
870 if (format[i+1] == '\0') len++;
871 else if (format[i+1] == 'm')
872 {
873 expand = 1;
874 strerror_r(saved_errno, estr, sizeof(estr));
875 elen = strlen(estr);
876 len += elen;
877 i++;
878 }
879 else
880 {
881 len += 2;
882 i++;
883 }
884 }
885 else len++;
886 }
887
888 fmt = (char *)format;
889
890 if (expand != 0)
891 {
892 fmt = malloc(len + 1);
893 if (fmt == NULL)
894 {
895 if (estr != NULL) free(estr);
896 return -1;
897 }
898
899 len = 0;
900
901 for (i = 0; format[i] != '\0'; i++)
902 {
903 if (format[i] == '%')
904 {
905 if (format[i+1] == '\0')
906 {
907 }
908 else if ((format[i+1] == 'm') && (elen != 0))
909 {
910 memcpy(fmt+len, estr, elen);
911 len += elen;
912 i++;
913 }
914 else
915 {
916 fmt[len++] = format[i++];
917 fmt[len++] = format[i];
918 }
919 }
920 else fmt[len++] = format[i];
921 }
922
923 fmt[len] = '\0';
924 }
925
926 str = NULL;
927 vasprintf(&str, fmt, ap);
928 if (expand != 0) free(fmt);
929
930 if (str == NULL) return -1;
931
932 status = _asl_send_message(ac, eval, (asl_msg_t *)msg, str);
933 free(str);
934
935 return status;
936 }
937
938 /*
939 * asl_vlog
940 * Similar to asl_log, but take a va_list instead of a list of arguments.
941 * msg: an aslmsg
942 * level: the log level of the associated message
943 * format: A formating string
944 * ap: va_list for the format
945 * returns 0 for success, non-zero for failure
946 */
947 int
948 asl_vlog(aslclient ac, aslmsg msg, int level, const char *format, va_list ap)
949 {
950 uint32_t eval = _asl_evaluate_send(ac, msg, level);
951 if (eval == EVAL_IGNORE) return 0;
952
953 return _asl_lib_vlog(ac, eval, msg, format, ap);
954 }
955
956 /*
957 * _asl_lib_log
958 * SPI used by ASL_PREFILTER_LOG. Converts format arguments to a va_list and
959 * forwards the call to _asl_lib_vlog.
960 * msg: an aslmsg
961 * eval: log level and send flags for the message
962 * format: A formating string
963 * ... args for format
964 * returns 0 for success, non-zero for failure
965 */
966 int
967 _asl_lib_log(aslclient ac, uint32_t eval, aslmsg msg, const char *format, ...)
968 {
969 int status;
970 if (eval == EVAL_IGNORE) return 0;
971
972 va_list ap;
973 va_start(ap, format);
974 status = _asl_lib_vlog(ac, eval, msg, format, ap);
975 va_end(ap);
976
977 return status;
978 }
979
980 /*
981 * asl_log
982 * Processes an ASL log message.
983 * msg: an aslmsg
984 * level: the log level of the associated message
985 * format: A formating string
986 * ... args for format
987 * returns 0 for success, non-zero for failure
988 */
989 int
990 asl_log(aslclient ac, aslmsg msg, int level, const char *format, ...)
991 {
992 int status;
993 uint32_t eval = _asl_evaluate_send(ac, msg, level);
994 if (eval == EVAL_IGNORE) return 0;
995
996 va_list ap;
997 va_start(ap, format);
998 status = _asl_lib_vlog(ac, eval, msg, format, ap);
999 va_end(ap);
1000
1001 return status;
1002 }
1003
1004 #ifndef BUILDING_VARIANT
1005
1006 /*
1007 * asl_get_filter: gets the values for the local, master, and remote filters,
1008 * and indicates which one is active.
1009 */
1010 int
1011 asl_get_filter(aslclient ac, int *local, int *master, int *remote, int *active)
1012 {
1013 asl_client_t *asl, *asl_default;
1014 int l, m, r, x;
1015 int status, check;
1016 uint64_t v64;
1017
1018 l = 0;
1019 m = 0;
1020 r = 0;
1021 x = 0;
1022
1023 asl_default = _asl_open_default();
1024
1025 asl = (asl_client_t *)ac;
1026 if (asl == NULL) asl = asl_default;
1027 if (asl != NULL) l = asl->filter & 0xff;
1028
1029 if ((asl_default != NULL) && (!(asl_default->options & ASL_OPT_NO_REMOTE)))
1030 {
1031 pthread_mutex_lock(&_asl_global.lock);
1032
1033 if (_asl_global.rc_change_token >= 0)
1034 {
1035 /* initialize or re-check process-specific and master filters */
1036 check = 0;
1037 status = notify_check(_asl_global.rc_change_token, &check);
1038 if ((status == NOTIFY_STATUS_OK) && (check != 0))
1039 {
1040 if (_asl_global.master_token >= 0)
1041 {
1042 v64 = 0;
1043 status = notify_get_state(_asl_global.master_token, &v64);
1044 if (status == NOTIFY_STATUS_OK) _asl_global.master_filter = v64;
1045 }
1046
1047 if (_asl_global.notify_token >= 0)
1048 {
1049 v64 = 0;
1050 status = notify_get_state(_asl_global.notify_token, &v64);
1051 if (status == NOTIFY_STATUS_OK) _asl_global.proc_filter = v64;
1052 }
1053 }
1054 }
1055
1056 m = _asl_global.master_filter;
1057 if (m != 0) x = 1;
1058
1059 r = _asl_global.proc_filter;
1060 if (r != 0) x = 2;
1061
1062 pthread_mutex_unlock(&_asl_global.lock);
1063 }
1064
1065 if (local != NULL) *local = l;
1066 if (master != NULL) *master = m;
1067 if (remote != NULL) *remote = r;
1068 if (active != NULL) *active = x;
1069
1070 return 0;
1071 }
1072
1073 static int
1074 _asl_send_message(aslclient ac, uint32_t eval, asl_msg_t *msg, const char *mstring)
1075 {
1076 uint32_t i, len, level, lmask, outstatus;
1077 const char *val;
1078 char *name, *x;
1079 time_t tick;
1080 struct timeval tval;
1081 int status;
1082 asl_client_t *asl;
1083 int use_global_lock;
1084 kern_return_t kstatus;
1085 char aux_val[64];
1086 char aux_host[_POSIX_HOST_NAME_MAX];
1087 asl_msg_t *aux;
1088
1089 if (eval == EVAL_IGNORE) return 0;
1090
1091 level = eval & LEVEL_MASK;
1092 eval &= EVAL_MASK;
1093 lmask = ASL_FILTER_MASK(level);
1094
1095 use_global_lock = 0;
1096 asl = (asl_client_t *)ac;
1097 if (asl == NULL)
1098 {
1099 asl = _asl_open_default();
1100 if (asl == NULL) return -1;
1101 use_global_lock = 1;
1102 }
1103
1104 if (asl->aslfile != NULL) use_global_lock = 1;
1105
1106 /*
1107 * Time, TimeNanoSec, Host, PID, UID, and GID values get set here.
1108 * Also sets Sender & Facility (if unset) and "ASLOption store" if remote control is active.
1109 */
1110 aux = asl_msg_new(ASL_TYPE_MSG);
1111
1112 if (mstring != NULL) asl_msg_set_key_val(aux, ASL_KEY_MSG, mstring);
1113
1114 snprintf(aux_val, sizeof(aux_val), "%u", level);
1115 asl_msg_set_key_val(aux, ASL_KEY_LEVEL, aux_val);
1116
1117 memset(&tval, 0, sizeof(struct timeval));
1118
1119 status = gettimeofday(&tval, NULL);
1120 if (status == 0)
1121 {
1122 snprintf(aux_val, sizeof(aux_val), "%lu", tval.tv_sec);
1123 asl_msg_set_key_val(aux, ASL_KEY_TIME, aux_val);
1124 snprintf(aux_val, sizeof(aux_val), "%d", tval.tv_usec * 1000);
1125 asl_msg_set_key_val(aux, ASL_KEY_TIME_NSEC, aux_val);
1126 }
1127 else
1128 {
1129 tick = time(NULL);
1130 snprintf(aux_val, sizeof(aux_val), "%lu", tick);
1131 asl_msg_set_key_val(aux, ASL_KEY_TIME, aux_val);
1132 }
1133
1134 memset(&aux_host, 0, _POSIX_HOST_NAME_MAX);
1135 if (gethostname(aux_host, _POSIX_HOST_NAME_MAX) == 0)
1136 {
1137 asl_msg_set_key_val(aux, ASL_KEY_HOST, aux_host);
1138 }
1139
1140 snprintf(aux_val, sizeof(aux_val), "%u", getpid());
1141 asl_msg_set_key_val(aux, ASL_KEY_PID, aux_val);
1142
1143 snprintf(aux_val, sizeof(aux_val), "%d", getuid());
1144 asl_msg_set_key_val(aux, ASL_KEY_UID, aux_val);
1145
1146 snprintf(aux_val, sizeof(aux_val), "%d", getgid());
1147 asl_msg_set_key_val(aux, ASL_KEY_GID, aux_val);
1148
1149 /*
1150 * Set Sender if needed
1151 */
1152 status = asl_msg_lookup((asl_msg_t *)msg, ASL_KEY_SENDER, &val, NULL);
1153 if ((status != 0) || (val == NULL))
1154 {
1155 if ((ac != NULL) && (ac->name != NULL))
1156 {
1157 /* Use the Sender name from the client handle */
1158 asl_msg_set_key_val(aux, ASL_KEY_SENDER, ac->name);
1159 }
1160 else
1161 {
1162 /* Get the value for ASL_KEY_SENDER from cache */
1163 if (_asl_global.sender == NULL)
1164 {
1165 name = *(*_NSGetArgv());
1166 if (name != NULL)
1167 {
1168 x = strrchr(name, '/');
1169 if (x != NULL) x++;
1170 else x = name;
1171
1172 pthread_mutex_lock(&_asl_global.lock);
1173 if (_asl_global.sender == NULL) _asl_global.sender = strdup(x);
1174 pthread_mutex_unlock(&_asl_global.lock);
1175 }
1176 }
1177
1178 if (_asl_global.sender != NULL) asl_msg_set_key_val(aux, ASL_KEY_SENDER, _asl_global.sender);
1179 else asl_msg_set_key_val(aux, ASL_KEY_SENDER, "Unknown");
1180 }
1181 }
1182
1183 /*
1184 * Set Facility
1185 */
1186 status = asl_msg_lookup((asl_msg_t *)msg, ASL_KEY_FACILITY, &val, NULL);
1187 if ((status != 0) || (val == NULL))
1188 {
1189 if ((ac != NULL) && (ac->facility != NULL))
1190 {
1191 /* Use the Facility name from the client handle */
1192 asl_msg_set_key_val(aux, ASL_KEY_FACILITY, ac->facility);
1193 }
1194 }
1195
1196 /* Set "ASLOption store" if tunneling */
1197
1198 if (eval & EVAL_TUNNEL)
1199 {
1200 val = asl_get((aslmsg)msg, ASL_KEY_OPTION);
1201 if (val == NULL)
1202 {
1203 asl_msg_set_key_val(aux, ASL_KEY_OPTION, ASL_OPT_STORE);
1204 }
1205 else
1206 {
1207 char *aux_option = NULL;
1208 asprintf(&aux_option, "%s %s", ASL_OPT_STORE, val);
1209 asl_msg_set_key_val(aux, ASL_KEY_OPTION, aux_option);
1210 free(aux_option);
1211 }
1212 }
1213
1214 outstatus = -1;
1215
1216 if (use_global_lock != 0) pthread_mutex_lock(&_asl_global.lock);
1217
1218 aux = asl_msg_merge(aux, msg);
1219
1220 /*
1221 * If there is an aslfile this is a stand-alone file client.
1222 * Just save to the file.
1223 */
1224 if (asl->aslfile != NULL)
1225 {
1226 outstatus = ASL_STATUS_FAILED;
1227
1228 if (aux != NULL)
1229 {
1230 outstatus = asl_file_save(asl->aslfile, (aslmsg)aux, &(asl->aslfileid));
1231 asl->aslfileid++;
1232 }
1233
1234 asl_msg_release(aux);
1235
1236 if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
1237 return outstatus;
1238 }
1239
1240 _asl_global_init();
1241 outstatus = 0;
1242
1243 /* manage quota */
1244 if ((eval & EVAL_TUNNEL) == 0)
1245 {
1246 time_t last_send = _asl_global.last_send;
1247 time_t last_oq = _asl_global.last_oq_msg;
1248 uint32_t qcurr = _asl_global.quota;
1249 time_t delta;
1250 uint32_t qinc, qnew;
1251
1252 qnew = qcurr;
1253
1254 /* add QUOTA_MPS to quota for each second we've been idle */
1255 if (tval.tv_sec > last_send)
1256 {
1257 delta = tval.tv_sec - last_send;
1258
1259 qinc = QUOTA_MPH;
1260 if (delta < (QUOTA_MPH / QUOTA_MPS)) qinc = delta * QUOTA_MPS;
1261
1262 qnew = MIN(QUOTA_MPH, qcurr + qinc);
1263 OSAtomicCompareAndSwapLongBarrier(last_send, tval.tv_sec, (long *)&_asl_global.last_send);
1264 }
1265
1266 if (qnew == 0)
1267 {
1268 if ((tval.tv_sec - last_oq) > QUOTA_MSG_INTERVAL)
1269 {
1270 eval |= EVAL_QUOTA;
1271 OSAtomicCompareAndSwapLongBarrier(last_oq, tval.tv_sec, (long *)&_asl_global.last_oq_msg);
1272 }
1273 else
1274 {
1275 eval &= ~EVAL_SEND;
1276 }
1277 }
1278 else
1279 {
1280 OSAtomicCompareAndSwap32Barrier(qcurr, qnew - 1, (int32_t *)&_asl_global.quota);
1281 }
1282 }
1283
1284 if ((_asl_global.server_port != MACH_PORT_NULL) && (eval & EVAL_SEND))
1285 {
1286 asl_string_t *send_str;
1287 const char *str;
1288 size_t vmsize;
1289
1290 if (eval & EVAL_QUOTA)
1291 {
1292 asl_msg_set_key_val(aux, ASL_KEY_LEVEL, QUOTA_LEVEL);
1293 asl_msg_set_key_val(aux, ASL_KEY_MSG, QUOTA_MSG);
1294 }
1295
1296 send_str = asl_msg_to_string_raw(ASL_STRING_MIG, aux, "raw");
1297 len = asl_string_length(send_str);
1298 vmsize = asl_string_allocated_size(send_str);
1299 str = asl_string_free_return_bytes(send_str);
1300
1301 if (len != 0)
1302 {
1303 /* send a mach message to syslogd */
1304 kstatus = _asl_server_message(_asl_global.server_port, (caddr_t)str, len);
1305 if (kstatus != KERN_SUCCESS)
1306 {
1307 /* retry once if the call failed */
1308 _asl_global_reset();
1309 _asl_global_init();
1310 kstatus = _asl_server_message(_asl_global.server_port, (caddr_t)str, len);
1311 if (kstatus != KERN_SUCCESS)
1312 {
1313 _asl_global_reset();
1314 vm_deallocate(mach_task_self(), (vm_address_t)str, vmsize);
1315 outstatus = -1;
1316 }
1317 }
1318 }
1319 else if (vmsize >0) vm_deallocate(mach_task_self(), (vm_address_t)str, vmsize);
1320 }
1321
1322 if ((aux != NULL) && (asl->out_count > 0))
1323 {
1324 /* write to file descriptors */
1325 for (i = 0; i < asl->out_count; i++)
1326 {
1327 if ((asl->out_list[i].fd >= 0) && (asl->out_list[i].filter != 0) && ((asl->out_list[i].filter & lmask) != 0))
1328 {
1329 char *str;
1330
1331 len = 0;
1332 str = asl_format_message(aux, asl->out_list[i].mfmt, asl->out_list[i].tfmt, asl->out_list[i].encoding, &len);
1333 if (str == NULL) continue;
1334
1335 status = write(asl->out_list[i].fd, str, len - 1);
1336 if (status < 0)
1337 {
1338 /* soft error for fd 2 (stderr) */
1339 if (asl->out_list[i].fd != 2) outstatus = -1;
1340 asl->out_list[i].fd = -1;
1341 }
1342
1343 free(str);
1344 }
1345 }
1346 }
1347
1348 asl_msg_release(aux);
1349
1350 if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
1351
1352 return outstatus;
1353 }
1354
1355 /*
1356 * asl_send: send a message
1357 * This routine may be used instead of asl_log() or asl_vlog() if asl_set()
1358 * has been used to set all of a message's attributes.
1359 * eval: hints about what to do with the message
1360 * msg: an aslmsg
1361 * returns 0 for success, non-zero for failure
1362 */
1363 int
1364 asl_send(aslclient ac, aslmsg msg)
1365 {
1366 int status = 0;
1367 uint32_t eval = _asl_evaluate_send(ac, msg, -1);
1368 if (eval != 0) status = _asl_send_message(ac, eval, (asl_msg_t *)msg, NULL);
1369
1370 return status;
1371 }
1372
1373 static int
1374 _asl_aux_save_context(asl_aux_context_t *ctx)
1375 {
1376 if (ctx == NULL) return -1;
1377
1378 pthread_mutex_lock(&_asl_global.lock);
1379
1380 _asl_global.aux_ctx = (asl_aux_context_t **)reallocf(_asl_global.aux_ctx, (_asl_global.aux_count + 1) * sizeof(asl_aux_context_t *));
1381 if (_asl_global.aux_ctx == NULL)
1382 {
1383 _asl_global.aux_count = 0;
1384 return -1;
1385 }
1386
1387 _asl_global.aux_ctx[_asl_global.aux_count++] = ctx;
1388
1389 pthread_mutex_unlock(&_asl_global.lock);
1390
1391 return 0;
1392 }
1393
1394 /*
1395 * Creates an auxiliary file that may be used to save arbitrary data. The ASL message msg
1396 * will be saved at the time that the auxiliary file is created. The message will include
1397 * any keys and values found in msg, and it will include the title and Uniform Type
1398 * Identifier specified. Output parameter out_fd will contain the file descriptor of the
1399 * new auxiliary file.
1400 */
1401 static int
1402 _asl_auxiliary(asl_msg_t *msg, const char *title, const char *uti, const char *url, int *out_fd)
1403 {
1404 asl_msg_t *aux;
1405 asl_string_t *send_str;
1406 const char *str;
1407 fileport_t fileport;
1408 kern_return_t kstatus;
1409 size_t len, vmsize;
1410 uint32_t newurllen, where;
1411 int status, fd, fdpair[2];
1412 caddr_t newurl;
1413 dispatch_queue_t pipe_q;
1414 dispatch_io_t pipe_channel;
1415 dispatch_semaphore_t sem;
1416
1417 aux = asl_msg_new(ASL_TYPE_MSG);
1418
1419 if (title != NULL)
1420 {
1421 asl_msg_set_key_val(aux, ASL_KEY_AUX_TITLE, title);
1422 }
1423
1424 if (uti == NULL)
1425 {
1426 asl_msg_set_key_val(aux, ASL_KEY_AUX_UTI, "public.data");
1427 }
1428 else
1429 {
1430 asl_msg_set_key_val(aux, ASL_KEY_AUX_UTI, uti);
1431 }
1432
1433 if (url != NULL)
1434 {
1435 asl_msg_set_key_val(aux, ASL_KEY_AUX_URL, url);
1436 }
1437
1438 aux = asl_msg_merge(aux, msg);
1439
1440 /* if (out_fd == NULL), this is from asl_log_auxiliary_location */
1441 if (out_fd == NULL)
1442 {
1443 uint32_t eval = _asl_evaluate_send(NULL, (aslmsg)aux, -1);
1444 status = _asl_send_message(NULL, eval, aux, NULL);
1445 asl_msg_release(aux);
1446 return status;
1447 }
1448
1449 where = asl_store_location();
1450
1451 if (where == ASL_STORE_LOCATION_MEMORY)
1452 {
1453 /* create a pipe */
1454
1455 asl_aux_context_t *ctx = (asl_aux_context_t *)calloc(1, sizeof(asl_aux_context_t));
1456 if (ctx == NULL) return -1;
1457
1458 status = pipe(fdpair);
1459 if (status < 0)
1460 {
1461 free(ctx);
1462 return -1;
1463 }
1464
1465 /* give read end to dispatch_io_read */
1466 fd = fdpair[0];
1467 sem = dispatch_semaphore_create(0);
1468 ctx->sem = sem;
1469 ctx->fd = fdpair[1];
1470
1471 status = _asl_aux_save_context(ctx);
1472 if (status != 0)
1473 {
1474 close(fdpair[0]);
1475 close(fdpair[1]);
1476 dispatch_release(sem);
1477 free(ctx);
1478 return -1;
1479 }
1480
1481 pipe_q = dispatch_queue_create("PipeQ", NULL);
1482 pipe_channel = dispatch_io_create(DISPATCH_IO_STREAM, fd, pipe_q, ^(int err){
1483 close(fd);
1484 });
1485
1486 *out_fd = fdpair[1];
1487
1488 dispatch_io_set_low_water(pipe_channel, SIZE_MAX);
1489
1490 dispatch_io_read(pipe_channel, 0, SIZE_MAX, pipe_q, ^(bool done, dispatch_data_t pipedata, int err){
1491 if (err == 0)
1492 {
1493 size_t len = dispatch_data_get_size(pipedata);
1494 if (len > 0)
1495 {
1496 const char *bytes = NULL;
1497 char *encoded;
1498 uint32_t eval;
1499
1500 dispatch_data_t md = dispatch_data_create_map(pipedata, (const void **)&bytes, &len);
1501 encoded = asl_core_encode_buffer(bytes, len);
1502 asl_msg_set_key_val(aux, ASL_KEY_AUX_DATA, encoded);
1503 free(encoded);
1504 eval = _asl_evaluate_send(NULL, (aslmsg)aux, -1);
1505 _asl_send_message(NULL, eval, aux, NULL);
1506 asl_msg_release(aux);
1507 dispatch_release(md);
1508 }
1509 }
1510
1511 if (done)
1512 {
1513 dispatch_semaphore_signal(sem);
1514 dispatch_release(pipe_channel);
1515 dispatch_release(pipe_q);
1516 }
1517 });
1518
1519 return 0;
1520 }
1521
1522 _asl_global_init();
1523 if (_asl_global.server_port == MACH_PORT_NULL) return -1;
1524
1525 send_str = asl_msg_to_string_raw(ASL_STRING_MIG, aux, "raw");
1526 len = asl_string_length(send_str);
1527 vmsize = asl_string_allocated_size(send_str);
1528 str = asl_string_free_return_bytes(send_str);
1529
1530 if (len == 0)
1531 {
1532 asl_msg_release(aux);
1533 vm_deallocate(mach_task_self(), (vm_address_t)str, vmsize);
1534 return -1;
1535 }
1536
1537 status = 0;
1538 fileport = MACH_PORT_NULL;
1539 status = KERN_SUCCESS;
1540
1541 kstatus = _asl_server_create_aux_link(_asl_global.server_port, (caddr_t)str, len, &fileport, &newurl, &newurllen, &status);
1542 if (kstatus != KERN_SUCCESS)
1543 {
1544 /* retry once if the call failed */
1545 _asl_global_reset();
1546 _asl_global_init();
1547 kstatus = _asl_server_create_aux_link(_asl_global.server_port, (caddr_t)str, len, &fileport, &newurl, &newurllen, &status);
1548 if (kstatus != KERN_SUCCESS)
1549 {
1550 _asl_global_reset();
1551 vm_deallocate(mach_task_self(), (vm_address_t)str, vmsize);
1552 asl_msg_release(aux);
1553 return -1;
1554 }
1555 }
1556
1557 if (status != 0)
1558 {
1559 asl_msg_release(aux);
1560 return status;
1561 }
1562
1563 if (newurl != NULL)
1564 {
1565 asl_msg_set_key_val(aux, ASL_KEY_AUX_URL, newurl);
1566 vm_deallocate(mach_task_self(), (vm_address_t)newurl, newurllen);
1567 }
1568
1569 if (fileport == MACH_PORT_NULL)
1570 {
1571 asl_msg_release(aux);
1572 return -1;
1573 }
1574
1575 fd = fileport_makefd(fileport);
1576 mach_port_deallocate(mach_task_self(), fileport);
1577 if (fd < 0)
1578 {
1579 asl_msg_release(aux);
1580 status = -1;
1581 }
1582 else
1583 {
1584 asl_aux_context_t *ctx = (asl_aux_context_t *)calloc(1, sizeof(asl_aux_context_t));
1585 if (ctx == NULL)
1586 {
1587 status = -1;
1588 }
1589 else
1590 {
1591 *out_fd = fd;
1592
1593 ctx->fd = fd;
1594 ctx->msg = aux;
1595
1596 status = _asl_aux_save_context(ctx);
1597 }
1598 }
1599
1600 return status;
1601 }
1602
1603 int
1604 asl_create_auxiliary_file(aslmsg msg, const char *title, const char *uti, int *out_fd)
1605 {
1606 if (out_fd == NULL) return -1;
1607
1608 return _asl_auxiliary((asl_msg_t *)msg, title, uti, NULL, out_fd);
1609 }
1610
1611 int
1612 asl_log_auxiliary_location(aslmsg msg, const char *title, const char *uti, const char *url)
1613 {
1614 return _asl_auxiliary((asl_msg_t *)msg, title, uti, url, NULL);
1615 }
1616
1617 /*
1618 * Close an auxiliary file.
1619 * Sends the cached auxiliary message to syslogd.
1620 */
1621 int
1622 asl_close_auxiliary_file(int fd)
1623 {
1624 int i, j, status;
1625 asl_msg_t *aux_msg;
1626 dispatch_semaphore_t aux_sem = NULL;
1627
1628 pthread_mutex_lock(&(_asl_global.lock));
1629
1630 aux_msg = NULL;
1631 status = -1;
1632
1633 for (i = 0; i < _asl_global.aux_count; i++)
1634 {
1635 if (_asl_global.aux_ctx[i]->fd == fd)
1636 {
1637 status = 0;
1638
1639 aux_msg = _asl_global.aux_ctx[i]->msg;
1640 aux_sem = _asl_global.aux_ctx[i]->sem;
1641
1642 free(_asl_global.aux_ctx[i]);
1643
1644 for (j = i + 1; j < _asl_global.aux_count; i++, j++)
1645 {
1646 _asl_global.aux_ctx[i] = _asl_global.aux_ctx[j];
1647 }
1648
1649 _asl_global.aux_count--;
1650
1651 if (_asl_global.aux_count == 0)
1652 {
1653 free(_asl_global.aux_ctx);
1654 _asl_global.aux_ctx = NULL;
1655 }
1656 else
1657 {
1658 _asl_global.aux_ctx = (asl_aux_context_t **)reallocf(_asl_global.aux_ctx, _asl_global.aux_count * sizeof(asl_aux_context_t *));
1659 if (_asl_global.aux_ctx == NULL)
1660 {
1661 _asl_global.aux_count = 0;
1662 status = -1;
1663 }
1664 }
1665
1666 break;
1667 }
1668 }
1669
1670 pthread_mutex_unlock(&(_asl_global.lock));
1671
1672 close(fd);
1673
1674 if (aux_msg != NULL)
1675 {
1676 uint32_t eval = _asl_evaluate_send(NULL, (aslmsg)aux_msg, -1);
1677 if (_asl_send_message(NULL, eval, aux_msg, NULL) != ASL_STATUS_OK) status = -1;
1678 asl_msg_release(aux_msg);
1679 }
1680
1681 if (aux_sem != NULL)
1682 {
1683 dispatch_semaphore_wait(aux_sem, DISPATCH_TIME_FOREVER);
1684 dispatch_release(aux_sem);
1685 }
1686
1687 return status;
1688 }
1689
1690 /*
1691 * asl_search: Search for messages matching the criteria described
1692 * by the aslmsg. The caller should set the attributes to match using
1693 * asl_set_query() or asl_set(). The operatoin ASL_QUERY_OP_EQUAL is
1694 * used for attributes set with asl_set().
1695 * a: an aslmsg
1696 * returns: a set of messages that can be iterated over using aslresp_next(),
1697 * and the values can be retrieved using aslresp_get.
1698 */
1699
1700 /*
1701 * This routine searches the ASL datastore on disk (/var/log/asl).
1702 * It is called my asl_search if syslogd is not running or if syslogd
1703 * indicates that an in-memory store is not being used.
1704 */
1705 static aslresponse
1706 _asl_search_store(aslclient ac, aslmsg a)
1707 {
1708 asl_search_result_t query, *out;
1709 asl_msg_t *q, *qlist[1];
1710 uint32_t status, op;
1711 uint64_t last_id, start_id;
1712 asl_store_t *store;
1713 const char *val;
1714
1715 if (a == NULL) return NULL;
1716
1717 q = (asl_msg_t *)a;
1718
1719 /* check for "ASLMessageId >[=] n" and set start_id */
1720 start_id = 0;
1721 val = NULL;
1722
1723 status = asl_msg_lookup(q, ASL_KEY_MSG_ID, &val, &op);
1724 if ((status == 0) && (val != NULL) && (op & ASL_QUERY_OP_GREATER))
1725 {
1726 if (op & ASL_QUERY_OP_EQUAL) start_id = atoll(val);
1727 else start_id = atoll(val) + 1;
1728 }
1729
1730 store = NULL;
1731 status = asl_store_open_read(NULL, &store);
1732 if (status != 0) return NULL;
1733 if (store == NULL) return NULL;
1734
1735 out = NULL;
1736 last_id = 0;
1737
1738 qlist[0] = (asl_msg_t *)a;
1739 memset(&query, 0, sizeof(asl_search_result_t));
1740 query.count = 1;
1741 query.msg = qlist;
1742
1743 status = asl_store_match(store, &query, &out, &last_id, start_id, 0, 1);
1744 asl_store_close(store);
1745
1746 return out;
1747 }
1748
1749 static uint32_t
1750 _asl_search_concat_results(asl_search_result_t *batch, asl_search_result_t **out)
1751 {
1752 uint32_t i, j;
1753
1754 if (out == NULL) return ASL_STATUS_FAILED;
1755
1756 /* nothing to do if batch is NULL or contains no messages */
1757 if (batch == NULL) return 0;
1758 if (batch->count == 0)
1759 {
1760 aslresponse_free(batch);
1761 return 0;
1762 }
1763
1764 if (*out == NULL) *out = (asl_search_result_t *)calloc(1, sizeof(asl_search_result_t));
1765 if (*out == NULL)
1766 {
1767 aslresponse_free(batch);
1768 return ASL_STATUS_FAILED;
1769 }
1770
1771 if ((*out)->count == 0)
1772 {
1773 (*out)->msg = (asl_msg_t **)calloc(batch->count, sizeof(asl_msg_t *));
1774 }
1775 else
1776 {
1777 (*out)->msg = (asl_msg_t **)reallocf((*out)->msg, ((*out)->count + batch->count) * sizeof(asl_msg_t *));
1778 }
1779
1780 if ((*out)->msg == NULL)
1781 {
1782 aslresponse_free(batch);
1783 free(*out);
1784 *out = NULL;
1785 return ASL_STATUS_FAILED;
1786 }
1787
1788 for (i = 0, j = (*out)->count; i < batch->count; i++, j++) (*out)->msg[j] = batch->msg[i];
1789
1790 (*out)->count += batch->count;
1791 free(batch->msg);
1792 free(batch);
1793 return ASL_STATUS_OK;
1794 }
1795
1796 static aslresponse
1797 _asl_search_memory(aslclient ac, aslmsg a)
1798 {
1799 asl_search_result_t *batch, *out;
1800 char *qstr, *str, *res;
1801 uint32_t len, reslen, status;
1802 uint64_t cmax, qmin;
1803 kern_return_t kstatus;
1804 caddr_t vmstr;
1805
1806 if (a == NULL) return 0;
1807
1808 _asl_global_init();
1809 if (_asl_global.server_port == MACH_PORT_NULL) return NULL;
1810
1811 len = 0;
1812 qstr = asl_msg_to_string((asl_msg_t *)a, &len);
1813
1814 str = NULL;
1815 if (qstr == NULL)
1816 {
1817 asprintf(&str, "0\n");
1818 len = 3;
1819 }
1820 else
1821 {
1822 asprintf(&str, "1\n%s\n", qstr);
1823 len += 3;
1824 free(qstr);
1825 }
1826
1827 if (str == NULL) return NULL;
1828
1829 /*
1830 * Fetch a batch of results each time through the loop.
1831 * Fetching small batches rebuces the load on syslogd.
1832 */
1833 out = NULL;
1834 qmin = 0;
1835 cmax = 0;
1836
1837 forever
1838 {
1839 res = NULL;
1840 reslen = 0;
1841 status = ASL_STATUS_OK;
1842
1843 kstatus = vm_allocate(mach_task_self(), (vm_address_t *)&vmstr, len, TRUE);
1844 if (kstatus != KERN_SUCCESS) return NULL;
1845
1846 memmove(vmstr, str, len);
1847
1848 status = 0;
1849 kstatus = _asl_server_query_2(_asl_global.server_port, vmstr, len, qmin, FETCH_BATCH, 0, (caddr_t *)&res, &reslen, &cmax, (int *)&status);
1850 if (kstatus != KERN_SUCCESS)
1851 {
1852 /* retry once if the call failed */
1853 _asl_global_reset();
1854 _asl_global_init();
1855 kstatus = _asl_server_query_2(_asl_global.server_port, vmstr, len, qmin, FETCH_BATCH, 0, (caddr_t *)&res, &reslen, &cmax, (int *)&status);
1856 if (kstatus != KERN_SUCCESS)
1857 {
1858 _asl_global_reset();
1859 break;
1860 }
1861 }
1862
1863 if (res == NULL) break;
1864
1865 batch = asl_list_from_string(res);
1866 vm_deallocate(mach_task_self(), (vm_address_t)res, reslen);
1867
1868 status = _asl_search_concat_results(batch, &out);
1869 if (status != ASL_STATUS_OK) break;
1870 if ((out == NULL) || (out->count < FETCH_BATCH)) break;
1871
1872 if (cmax >= qmin) qmin = cmax + 1;
1873 }
1874
1875 free(str);
1876
1877 return out;
1878 }
1879
1880 aslmsg
1881 _asl_server_control_query(void)
1882 {
1883 asl_search_result_t *list = NULL;
1884 char *qstr, *res;
1885 uint32_t len, reslen, status;
1886 uint64_t cmax, qmin;
1887 kern_return_t kstatus;
1888 caddr_t vmstr;
1889 asl_msg_t *m = NULL;
1890 static const char ctlstr[] = "1\nQ [= ASLOption control]\n";
1891
1892 _asl_global_init();
1893 if (_asl_global.server_port == MACH_PORT_NULL) return NULL;
1894
1895 len = strlen(ctlstr) + 1;
1896
1897 qmin = 0;
1898 cmax = 0;
1899 res = NULL;
1900 reslen = 0;
1901
1902 kstatus = vm_allocate(mach_task_self(), (vm_address_t *)&vmstr, len, TRUE);
1903 if (kstatus != KERN_SUCCESS) return NULL;
1904
1905 memmove(vmstr, ctlstr, len);
1906
1907 status = 0;
1908 kstatus = _asl_server_query_2(_asl_global.server_port, vmstr, len, qmin, FETCH_BATCH, 0, (caddr_t *)&res, &reslen, &cmax, (int *)&status);
1909 if (kstatus != KERN_SUCCESS)
1910 {
1911 /* retry once if the call failed */
1912 _asl_global_reset();
1913 _asl_global_init();
1914 kstatus = _asl_server_query_2(_asl_global.server_port, vmstr, len, qmin, FETCH_BATCH, 0, (caddr_t *)&res, &reslen, &cmax, (int *)&status);
1915 if (kstatus != KERN_SUCCESS) _asl_global_reset();
1916 }
1917
1918 list = asl_list_from_string(res);
1919 vm_deallocate(mach_task_self(), (vm_address_t)res, reslen);
1920
1921 if (list == NULL) return NULL;
1922 if (list->count > 0) m = asl_msg_retain(list->msg[0]);
1923 aslresponse_free((aslresponse)list);
1924 return (aslmsg)m;
1925 }
1926
1927 int
1928 asl_store_location()
1929 {
1930 kern_return_t kstatus;
1931 char *res;
1932 uint32_t reslen, status;
1933 uint64_t cmax;
1934
1935 _asl_global_init();
1936 if (_asl_global.server_port == MACH_PORT_NULL) return ASL_STORE_LOCATION_FILE;
1937
1938 res = NULL;
1939 reslen = 0;
1940 cmax = 0;
1941 status = ASL_STATUS_OK;
1942
1943 kstatus = _asl_server_query_2(_asl_global.server_port, NULL, 0, 0, -1, 0, (caddr_t *)&res, &reslen, &cmax, (int *)&status);
1944 if (kstatus != KERN_SUCCESS)
1945 {
1946 /* retry once if the call failed */
1947 _asl_global_reset();
1948 _asl_global_init();
1949 kstatus = _asl_server_query_2(_asl_global.server_port, NULL, 0, 0, -1, 0, (caddr_t *)&res, &reslen, &cmax, (int *)&status);
1950 }
1951
1952 /* res should never be returned, but just to be certain we don't leak VM ... */
1953 if (res != NULL) vm_deallocate(mach_task_self(), (vm_address_t)res, reslen);
1954
1955 if (kstatus != KERN_SUCCESS)
1956 {
1957 _asl_global_reset();
1958 return ASL_STORE_LOCATION_FILE;
1959 }
1960
1961 if (status == ASL_STATUS_OK) return ASL_STORE_LOCATION_MEMORY;
1962 return ASL_STORE_LOCATION_FILE;
1963 }
1964
1965 aslresponse
1966 asl_search(aslclient ac, aslmsg a)
1967 {
1968 int where;
1969 asl_search_result_t *out;
1970
1971 _asl_global_init();
1972
1973 where = asl_store_location();
1974 if (where == ASL_STORE_LOCATION_FILE) out = _asl_search_store(ac, a);
1975 else out = _asl_search_memory(ac, a);
1976
1977 return out;
1978 }
1979
1980 int
1981 asl_syslog_faciliy_name_to_num(const char *name)
1982 {
1983 if (name == NULL) return -1;
1984
1985 if (strcaseeq(name, "auth")) return LOG_AUTH;
1986 if (strcaseeq(name, "authpriv")) return LOG_AUTHPRIV;
1987 if (strcaseeq(name, "cron")) return LOG_CRON;
1988 if (strcaseeq(name, "daemon")) return LOG_DAEMON;
1989 if (strcaseeq(name, "ftp")) return LOG_FTP;
1990 if (strcaseeq(name, "install")) return LOG_INSTALL;
1991 if (strcaseeq(name, "kern")) return LOG_KERN;
1992 if (strcaseeq(name, "lpr")) return LOG_LPR;
1993 if (strcaseeq(name, "mail")) return LOG_MAIL;
1994 if (strcaseeq(name, "netinfo")) return LOG_NETINFO;
1995 if (strcaseeq(name, "remoteauth")) return LOG_REMOTEAUTH;
1996 if (strcaseeq(name, "news")) return LOG_NEWS;
1997 if (strcaseeq(name, "security")) return LOG_AUTH;
1998 if (strcaseeq(name, "syslog")) return LOG_SYSLOG;
1999 if (strcaseeq(name, "user")) return LOG_USER;
2000 if (strcaseeq(name, "uucp")) return LOG_UUCP;
2001 if (strcaseeq(name, "local0")) return LOG_LOCAL0;
2002 if (strcaseeq(name, "local1")) return LOG_LOCAL1;
2003 if (strcaseeq(name, "local2")) return LOG_LOCAL2;
2004 if (strcaseeq(name, "local3")) return LOG_LOCAL3;
2005 if (strcaseeq(name, "local4")) return LOG_LOCAL4;
2006 if (strcaseeq(name, "local5")) return LOG_LOCAL5;
2007 if (strcaseeq(name, "local6")) return LOG_LOCAL6;
2008 if (strcaseeq(name, "local7")) return LOG_LOCAL7;
2009 if (strcaseeq(name, "launchd")) return LOG_LAUNCHD;
2010
2011 return -1;
2012 }
2013
2014 const char *
2015 asl_syslog_faciliy_num_to_name(int n)
2016 {
2017 if (n < 0) return NULL;
2018
2019 if (n == LOG_AUTH) return "auth";
2020 if (n == LOG_AUTHPRIV) return "authpriv";
2021 if (n == LOG_CRON) return "cron";
2022 if (n == LOG_DAEMON) return "daemon";
2023 if (n == LOG_FTP) return "ftp";
2024 if (n == LOG_INSTALL) return "install";
2025 if (n == LOG_KERN) return "kern";
2026 if (n == LOG_LPR) return "lpr";
2027 if (n == LOG_MAIL) return "mail";
2028 if (n == LOG_NETINFO) return "netinfo";
2029 if (n == LOG_REMOTEAUTH) return "remoteauth";
2030 if (n == LOG_NEWS) return "news";
2031 if (n == LOG_AUTH) return "security";
2032 if (n == LOG_SYSLOG) return "syslog";
2033 if (n == LOG_USER) return "user";
2034 if (n == LOG_UUCP) return "uucp";
2035 if (n == LOG_LOCAL0) return "local0";
2036 if (n == LOG_LOCAL1) return "local1";
2037 if (n == LOG_LOCAL2) return "local2";
2038 if (n == LOG_LOCAL3) return "local3";
2039 if (n == LOG_LOCAL4) return "local4";
2040 if (n == LOG_LOCAL5) return "local5";
2041 if (n == LOG_LOCAL6) return "local6";
2042 if (n == LOG_LOCAL7) return "local7";
2043 if (n == LOG_LAUNCHD) return "launchd";
2044
2045 return NULL;
2046 }
2047
2048 /*
2049 * utility for converting a time string into a time_t
2050 * we only deal with the following formats:
2051 * Canonical form YYYY.MM.DD hh:mm:ss UTC
2052 * ctime() form Mth dd hh:mm:ss (e.g. Aug 25 09:54:37)
2053 * absolute form - # seconds since the epoch (e.g. 1095789191)
2054 * relative time - seconds before or after now (e.g. -300, +43200)
2055 * relative time - days/hours/minutes/seconds before or after now (e.g. -1d, +6h, +30m, -10s)
2056 */
2057
2058 #define CANONICAL_TIME_REX "^[0-9][0-9][0-9][0-9].[01]?[0-9].[0-3]?[0-9][ ]+[0-2]?[0-9]:[0-5][0-9]:[0-5][0-9][ ]+UTC$"
2059 #define CTIME_REX "^[adfjmnos][aceopu][bcglnprtvy][ ]+[0-3]?[0-9][ ]+[0-2]?[0-9]:[0-5][0-9]:[0-5][0-9]$"
2060 #define ABSOLUTE_TIME_REX "^[0-9]+[s]?$"
2061 #define RELATIVE_TIME_REX "^[\\+-\\][0-9]+[smhdw]?$"
2062
2063 #define SECONDS_PER_MINUTE 60
2064 #define SECONDS_PER_HOUR 3600
2065 #define SECONDS_PER_DAY 86400
2066 #define SECONDS_PER_WEEK 604800
2067
2068 static regex_t rex_canon, rex_ctime, rex_abs, rex_rel;
2069 static int reg_status = 0;
2070
2071 /*
2072 * We use the last letter in the month name to determine
2073 * the month number (0-11). There are two collisions:
2074 * Jan and Jun both end in n
2075 * Mar and Apr both end in r
2076 * In these cases we check the second letter.
2077 *
2078 * The MTH_LAST array maps the last letter to a number.
2079 */
2080 static const int8_t MTH_LAST[] = {-1, 1, 11, -1, -1, -1, 7, -1, -1, -1, -1, 6, -1, 5, -1, 8, -1, 3, -1, 9, -1, 10, -1, -1, 4, -1};
2081
2082 static int
2083 _month_num(char *s)
2084 {
2085 int i;
2086 int8_t v8;
2087
2088 v8 = -1;
2089 if (s[2] > 90) v8 = s[2] - 'a';
2090 else v8 = s[2] - 'A';
2091
2092 if ((v8 < 0) || (v8 > 25)) return -1;
2093
2094 v8 = MTH_LAST[v8];
2095 if (v8 < 0) return -1;
2096
2097 i = v8;
2098 if ((i == 5) && ((s[1] == 'a') || (s[1] == 'A'))) return 0;
2099 if ((i == 3) && ((s[1] == 'a') || (s[1] == 'A'))) return 2;
2100 return i;
2101 }
2102
2103 time_t
2104 asl_parse_time(const char *in)
2105 {
2106 int len, y;
2107 struct tm t;
2108 time_t tick, delta, factor;
2109 char *str, *p, *x;
2110 static dispatch_once_t once;
2111
2112 if (in == NULL) return -1;
2113
2114 dispatch_once(&once, ^{
2115 int status;
2116 int rflags = REG_EXTENDED | REG_NOSUB | REG_ICASE;
2117
2118 memset(&rex_canon, 0, sizeof(regex_t));
2119 status = regcomp(&rex_canon, CANONICAL_TIME_REX, rflags);
2120 if (status != 0) reg_status = -1;
2121
2122 memset(&rex_ctime, 0, sizeof(regex_t));
2123 status = regcomp(&rex_ctime, CTIME_REX, rflags);
2124 if (status != 0) reg_status = -1;
2125
2126 memset(&rex_abs, 0, sizeof(regex_t));
2127 status = regcomp(&rex_abs, ABSOLUTE_TIME_REX, rflags);
2128 if (status != 0) reg_status = -1;
2129
2130 memset(&rex_rel, 0, sizeof(regex_t));
2131 status = regcomp(&rex_rel, RELATIVE_TIME_REX, rflags);
2132 if (status != 0) reg_status = -1;
2133 });
2134
2135 if (reg_status < 0) return -1;
2136
2137 len = strlen(in) + 1;
2138
2139 if (regexec(&rex_abs, in, 0, NULL, 0) == 0)
2140 {
2141 /*
2142 * Absolute time (number of seconds since the epoch)
2143 */
2144 str = strdup(in);
2145 if (str == NULL) return -1;
2146
2147 if ((str[len-2] == 's') || (str[len-2] == 'S')) str[len-2] = '\0';
2148
2149 tick = atol(str);
2150 free(str);
2151
2152 return tick;
2153 }
2154 else if (regexec(&rex_rel, in, 0, NULL, 0) == 0)
2155 {
2156 /*
2157 * Reletive time (number of seconds before or after right now)
2158 */
2159 str = strdup(in);
2160 if (str == NULL) return -1;
2161
2162 factor = 1;
2163
2164 if ((str[len-2] == 's') || (str[len-2] == 'S'))
2165 {
2166 str[len-2] = '\0';
2167 }
2168 else if ((str[len-2] == 'm') || (str[len-2] == 'M'))
2169 {
2170 str[len-2] = '\0';
2171 factor = SECONDS_PER_MINUTE;
2172 }
2173 else if ((str[len-2] == 'h') || (str[len-2] == 'H'))
2174 {
2175 str[len-2] = '\0';
2176 factor = SECONDS_PER_HOUR;
2177 }
2178 else if ((str[len-2] == 'd') || (str[len-2] == 'D'))
2179 {
2180 str[len-2] = '\0';
2181 factor = SECONDS_PER_DAY;
2182 }
2183 else if ((str[len-2] == 'w') || (str[len-2] == 'W'))
2184 {
2185 str[len-2] = '\0';
2186 factor = SECONDS_PER_WEEK;
2187 }
2188
2189 tick = time(NULL);
2190 delta = factor * atol(str);
2191 tick += delta;
2192
2193 free(str);
2194
2195 return tick;
2196 }
2197 else if (regexec(&rex_canon, in, 0, NULL, 0) == 0)
2198 {
2199 memset(&t, 0, sizeof(struct tm));
2200 str = strdup(in);
2201 if (str == NULL) return -1;
2202
2203 /* Get year */
2204 x = str;
2205 p = strchr(x, '.');
2206 *p = '\0';
2207 t.tm_year = atoi(x) - 1900;
2208
2209 /* Get month */
2210 x = p + 1;
2211 p = strchr(x, '.');
2212 *p = '\0';
2213 t.tm_mon = atoi(x) - 1;
2214
2215 /* Get day */
2216 x = p + 1;
2217 p = strchr(x, ' ');
2218 *p = '\0';
2219 t.tm_mday = atoi(x);
2220
2221 /* Get hour */
2222 for (x = p + 1; *x == ' '; x++);
2223 p = strchr(x, ':');
2224 *p = '\0';
2225 t.tm_hour = atoi(x);
2226
2227 /* Get minutes */
2228 x = p + 1;
2229 p = strchr(x, ':');
2230 *p = '\0';
2231 t.tm_min = atoi(x);
2232
2233 /* Get seconds */
2234 x = p + 1;
2235 p = strchr(x, ' ');
2236 *p = '\0';
2237 t.tm_sec = atoi(x);
2238
2239 free(str);
2240 return timegm(&t);
2241 }
2242 else if (regexec(&rex_ctime, in, 0, NULL, 0) == 0)
2243 {
2244 /* We assume it's in the current year */
2245 memset(&t, 0, sizeof(struct tm));
2246 tick = time(NULL);
2247 gmtime_r(&tick, &t);
2248 y = t.tm_year;
2249
2250 memset(&t, 0, sizeof(struct tm));
2251 str = strdup(in);
2252 if (str == NULL) return -1;
2253
2254 t.tm_year = y;
2255 t.tm_mon = _month_num(str);
2256 if (t.tm_mon < 0) return -1;
2257
2258 for (x = strchr(str, ' '); *x == ' '; x++);
2259 p = strchr(x, ' ');
2260 *p = '\0';
2261 t.tm_mday = atoi(x);
2262
2263 /* Get hour */
2264 for (x = p + 1; *x == ' '; x++);
2265 p = strchr(x, ':');
2266 *p = '\0';
2267 t.tm_hour = atoi(x);
2268
2269 /* Get minutes */
2270 x = p + 1;
2271 p = strchr(x, ':');
2272 *p = '\0';
2273 t.tm_min = atoi(x);
2274
2275 /* Get seconds */
2276 x = p + 1;
2277 t.tm_sec = atoi(x);
2278
2279 t.tm_isdst = -1;
2280
2281 free(str);
2282 return mktime(&t);
2283 }
2284
2285 return -1;
2286 }
2287
2288 #endif /* BUILDING_VARIANT */