]> git.saurik.com Git - apple/syslog.git/blob - syslogd.tproj/dbserver.c
09f90eef8f10760c0d313e85b071426357cd8b52
[apple/syslog.git] / syslogd.tproj / dbserver.c
1 /*
2 * Copyright (c) 2007-2009 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 <sys/types.h>
25 #include <sys/stat.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <sys/socket.h>
30 #include <sys/un.h>
31 #include <sys/ipc.h>
32 #include <sys/mman.h>
33 #include <sys/fcntl.h>
34 #include <sys/signal.h>
35 #include <sys/errno.h>
36 #include <mach/mach.h>
37 #include <mach/mach_error.h>
38 #include <bsm/libbsm.h>
39 #include <errno.h>
40 #include <netinet/in.h>
41 #include <sys/event.h>
42 #include <servers/bootstrap.h>
43 #include <pthread.h>
44 #include <notify.h>
45 #include <sys/time.h>
46 #include <asl.h>
47 #include <asl_ipc.h>
48 #include <asl_ipc_server.h>
49 #include <asl_core.h>
50 #include "daemon.h"
51
52 #define forever for(;;)
53
54 #define LIST_SIZE_DELTA 256
55
56 #define SEND_NOTIFICATION 0xfadefade
57
58 #define QUERY_FLAG_SEARCH_REVERSE 0x00000001
59 #define SEARCH_FORWARD 1
60 #define SEARCH_BACKWARD -1
61
62 static pthread_mutex_t db_lock = PTHREAD_MUTEX_INITIALIZER;
63
64 extern char *asl_list_to_string(asl_search_result_t *list, uint32_t *outlen);
65 extern asl_search_result_t *asl_list_from_string(const char *buf);
66 extern boolean_t asl_ipc_server(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP);
67 extern uint32_t bb_convert(const char *name);
68
69 static task_name_t *client_tasks = NULL;
70 static uint32_t client_tasks_count = 0;
71
72 typedef union
73 {
74 mach_msg_header_t head;
75 union __RequestUnion__asl_ipc_subsystem request;
76 } asl_request_msg;
77
78 typedef union
79 {
80 mach_msg_header_t head;
81 union __ReplyUnion__asl_ipc_subsystem reply;
82 } asl_reply_msg;
83
84 void
85 db_ping_store(void)
86 {
87 if ((global.dbtype & DB_TYPE_FILE) && (global.file_db != NULL))
88 {
89 global.store_flags |= STORE_FLAGS_FILE_CACHE_SWEEP_REQUESTED;
90
91 /* wake up the output worker thread */
92 pthread_cond_signal(&global.work_queue_cond);
93 }
94 }
95
96 void
97 db_asl_open()
98 {
99 uint32_t status;
100 struct stat sb;
101
102 if ((global.dbtype & DB_TYPE_FILE) && (global.file_db == NULL))
103 {
104 memset(&sb, 0, sizeof(struct stat));
105 if (stat(PATH_ASL_STORE, &sb) == 0)
106 {
107 /* must be a directory */
108 if ((sb.st_mode & S_IFDIR) == 0)
109 {
110 asldebug("error: %s is not a directory", PATH_ASL_STORE);
111 return;
112 }
113 }
114 else
115 {
116 if (errno == ENOENT)
117 {
118 /* /var/log/asl doesn't exist - create it */
119 if (mkdir(PATH_ASL_STORE, 0755) != 0)
120 {
121 asldebug("error: can't create data store %s: %s\n", PATH_ASL_STORE, strerror(errno));
122 return;
123 }
124 }
125 else
126 {
127 /* stat failed for some other reason */
128 asldebug("error: can't stat data store %s: %s\n", PATH_ASL_STORE, strerror(errno));
129 return;
130 }
131 }
132
133 /*
134 * One-time store conversion from the old "LongTTL" style to the new "Best Before" style.
135 * bb_convert returns quickly if the store has already been converted.
136 */
137 status = bb_convert(PATH_ASL_STORE);
138 if (status != ASL_STATUS_OK)
139 {
140 asldebug("ASL data store conversion failed!: %s\n", asl_core_error(status));
141 }
142
143 status = asl_store_open_write(NULL, &(global.file_db));
144 if (status != ASL_STATUS_OK)
145 {
146 asldebug("asl_store_open_write: %s\n", asl_core_error(status));
147 }
148 else
149 {
150 if (global.db_file_max != 0) asl_store_max_file_size(global.file_db, global.db_file_max);
151 asl_store_signal_sweep(global.file_db);
152 }
153 }
154
155 if ((global.dbtype & DB_TYPE_MEMORY) && (global.memory_db == NULL))
156 {
157 status = asl_memory_open(global.db_memory_max, &(global.memory_db));
158 if (status != ASL_STATUS_OK)
159 {
160 asldebug("asl_memory_open: %s\n", asl_core_error(status));
161 }
162 }
163
164 if ((global.dbtype & DB_TYPE_MINI) && (global.mini_db == NULL))
165 {
166 status = asl_mini_memory_open(global.db_mini_max, &(global.mini_db));
167 if (status != ASL_STATUS_OK)
168 {
169 asldebug("asl_mini_memory_open: %s\n", asl_core_error(status));
170 }
171 }
172 }
173
174 /*
175 * Takes messages off the work queue and saves them.
176 * Runs in it's own thread.
177 */
178 void
179 output_worker()
180 {
181 asl_msg_t **work;
182 uint32_t i, count;
183 mach_msg_empty_send_t *empty;
184 kern_return_t kstatus;
185
186 empty = (mach_msg_empty_send_t *)calloc(1, sizeof(mach_msg_empty_send_t));
187 if (empty == NULL) return;
188
189 forever
190 {
191 count = 0;
192
193 /* blocks until work is available */
194 work = asl_work_dequeue(&count);
195
196 if (work == NULL)
197 {
198 if ((global.dbtype & DB_TYPE_FILE) && (global.store_flags & STORE_FLAGS_FILE_CACHE_SWEEP_REQUESTED))
199 {
200 asl_store_sweep_file_cache(global.file_db);
201 global.store_flags &= ~STORE_FLAGS_FILE_CACHE_SWEEP_REQUESTED;
202 }
203
204 continue;
205 }
206
207 for (i = 0; i < count; i++)
208 {
209 asl_message_match_and_log(work[i]);
210 asl_msg_release(work[i]);
211 }
212
213 free(work);
214
215 if (global.store_flags & STORE_FLAGS_FILE_CACHE_SWEEP_REQUESTED)
216 {
217 asl_store_sweep_file_cache(global.file_db);
218 global.store_flags &= ~STORE_FLAGS_FILE_CACHE_SWEEP_REQUESTED;
219 }
220
221 empty->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSGH_BITS_ZERO);
222 empty->header.msgh_remote_port = global.self_port;
223 empty->header.msgh_local_port = MACH_PORT_NULL;
224 empty->header.msgh_size = sizeof(mach_msg_empty_send_t);
225 empty->header.msgh_id = SEND_NOTIFICATION;
226
227 kstatus = mach_msg(&(empty->header), MACH_SEND_MSG | MACH_SEND_TIMEOUT, empty->header.msgh_size, 0, MACH_PORT_NULL, 2, MACH_PORT_NULL);
228 }
229 }
230
231 /*
232 * Called from asl_action.c to save messgaes to the ASL data store
233 */
234 void
235 db_save_message(asl_msg_t *msg)
236 {
237 uint64_t msgid;
238 uint32_t status;
239
240 pthread_mutex_lock(&db_lock);
241
242 db_asl_open();
243
244 if (global.dbtype & DB_TYPE_FILE)
245 {
246 status = asl_store_save(global.file_db, msg);
247 if (status != ASL_STATUS_OK)
248 {
249 /* write failed - reopen & retry */
250 asldebug("asl_store_save: %s\n", asl_core_error(status));
251 asl_store_close(global.file_db);
252 global.file_db = NULL;
253
254 db_asl_open();
255 status = asl_store_save(global.file_db, msg);
256 if (status != ASL_STATUS_OK)
257 {
258 asldebug("(retry) asl_store_save: %s\n", asl_core_error(status));
259 asl_store_close(global.file_db);
260 global.file_db = NULL;
261
262 global.dbtype |= DB_TYPE_MEMORY;
263 if (global.memory_db == NULL)
264 {
265 status = asl_memory_open(global.db_memory_max, &(global.memory_db));
266 if (status != ASL_STATUS_OK)
267 {
268 asldebug("asl_memory_open: %s\n", asl_core_error(status));
269 }
270 }
271 }
272 }
273 }
274
275 if (global.dbtype & DB_TYPE_MEMORY)
276 {
277 msgid = 0;
278 status = asl_memory_save(global.memory_db, msg, &msgid);
279 if (status != ASL_STATUS_OK)
280 {
281 /* save failed - reopen & retry*/
282 asldebug("asl_memory_save: %s\n", asl_core_error(status));
283 asl_memory_close(global.memory_db);
284 global.memory_db = NULL;
285
286 db_asl_open();
287 msgid = 0;
288 status = asl_memory_save(global.memory_db, msg, &msgid);
289 if (status != ASL_STATUS_OK)
290 {
291 asldebug("(retry) asl_memory_save: %s\n", asl_core_error(status));
292 asl_memory_close(global.memory_db);
293 global.memory_db = NULL;
294 }
295 }
296 }
297
298 if (global.dbtype & DB_TYPE_MINI)
299 {
300 status = asl_mini_memory_save(global.mini_db, msg, &msgid);
301 if (status != ASL_STATUS_OK)
302 {
303 /* save failed - reopen & retry*/
304 asldebug("asl_mini_memory_save: %s\n", asl_core_error(status));
305 asl_mini_memory_close(global.mini_db);
306 global.mini_db = NULL;
307
308 db_asl_open();
309 status = asl_mini_memory_save(global.mini_db, msg, &msgid);
310 if (status != ASL_STATUS_OK)
311 {
312 asldebug("(retry) asl_memory_save: %s\n", asl_core_error(status));
313 asl_mini_memory_close(global.mini_db);
314 global.mini_db = NULL;
315 }
316 }
317 }
318
319
320 pthread_mutex_unlock(&db_lock);
321
322 }
323
324 void
325 disaster_message(asl_msg_t *msg)
326 {
327 uint64_t msgid;
328 uint32_t status;
329
330 global.disaster_occurred = 1;
331
332 msgid = 0;
333
334 if ((global.dbtype & DB_TYPE_MINI) == 0)
335 {
336 if (global.mini_db == NULL)
337 {
338 status = asl_mini_memory_open(global.db_mini_max, &(global.mini_db));
339 if (status != ASL_STATUS_OK) asldebug("asl_mini_memory_open: %s\n", asl_core_error(status));
340 else asl_mini_memory_save(global.mini_db, msg, &msgid);
341 }
342 }
343 }
344
345 /*
346 * Do a database search.
347 */
348 uint32_t
349 db_query(aslresponse query, aslresponse *res, uint64_t startid, int count, int flags, uint64_t *lastid, int32_t ruid, int32_t rgid)
350 {
351 uint32_t status, ucount;
352 int32_t dir;
353
354 /*
355 * Special case: if count is -1, we return ASL_STATUS_OK to indicate that the store is
356 * in memory, and ASL_STATUS_INVALID_STORE to indicate that the file store is in use.
357 */
358 if (count == -1)
359 {
360 if ((global.dbtype & DB_TYPE_MEMORY) || (global.dbtype & DB_TYPE_MINI)) return ASL_STATUS_OK;
361 else return ASL_STATUS_INVALID_STORE;
362 }
363
364 ucount = count;
365 dir = SEARCH_FORWARD;
366 if (flags & QUERY_FLAG_SEARCH_REVERSE) dir = SEARCH_BACKWARD;
367
368 pthread_mutex_lock(&db_lock);
369
370 status = ASL_STATUS_FAILED;
371
372 if (global.dbtype & DB_TYPE_MEMORY)
373 {
374 status = asl_memory_match(global.memory_db, query, res, lastid, startid, ucount, dir, ruid, rgid);
375 }
376 else if (global.dbtype & DB_TYPE_MINI)
377 {
378 status = asl_mini_memory_match(global.mini_db, query, res, lastid, startid, ucount, dir);
379 }
380 else if (global.disaster_occurred != 0)
381 {
382 /* KernelEventAgent calls us to get the kernel disaster messages. */
383 status = asl_mini_memory_match(global.mini_db, query, res, lastid, startid, ucount, dir);
384 }
385
386 pthread_mutex_unlock(&db_lock);
387
388 return status;
389 }
390
391 static void
392 register_session(task_name_t task_name, pid_t pid)
393 {
394 mach_port_t previous;
395 uint32_t i;
396
397 if (task_name == MACH_PORT_NULL) return;
398 if (global.dead_session_port == MACH_PORT_NULL) return;
399
400 for (i = 0; i < client_tasks_count; i++) if (task_name == client_tasks[i]) return;
401
402 if (client_tasks_count == 0) client_tasks = (task_name_t *)calloc(1, sizeof(task_name_t));
403 else client_tasks = (task_name_t *)reallocf(client_tasks, (client_tasks_count + 1) * sizeof(task_name_t));
404
405 if (client_tasks == NULL) return;
406 client_tasks[client_tasks_count] = task_name;
407 client_tasks_count++;
408
409 asldebug("register_session: %u PID %d\n", (unsigned int)task_name, (int)pid);
410
411 /* register for port death notification */
412 mach_port_request_notification(mach_task_self(), task_name, MACH_NOTIFY_DEAD_NAME, 0, global.dead_session_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous);
413 mach_port_deallocate(mach_task_self(), previous);
414
415 asl_client_count_increment();
416 }
417
418 static void
419 cancel_session(task_name_t task_name)
420 {
421 uint32_t i;
422
423 for (i = 0; (i < client_tasks_count) && (task_name != client_tasks[i]); i++);
424
425 if (i >= client_tasks_count) return;
426
427 if (client_tasks_count == 1)
428 {
429 free(client_tasks);
430 client_tasks = NULL;
431 client_tasks_count = 0;
432 }
433 else
434 {
435 for (i++; i < client_tasks_count; i++) client_tasks[i-1] = client_tasks[i];
436 client_tasks_count--;
437 client_tasks = (task_name_t *)reallocf(client_tasks, client_tasks_count * sizeof(task_name_t));
438 }
439
440 asldebug("cancel_session: %u\n", (unsigned int)task_name);
441 mach_port_destroy(mach_task_self(), task_name);
442 asl_client_count_decrement();
443 }
444
445 /*
446 * Receives messages on the "com.apple.system.logger" mach port.
447 * Services database search requests.
448 * Runs in it's own thread.
449 */
450 void
451 database_server()
452 {
453 kern_return_t kstatus;
454 asl_request_msg *request;
455 asl_reply_msg *reply;
456 uint32_t rqs, rps;
457 uint32_t rbits, sbits;
458 uint32_t flags, snooze;
459 struct timeval now, send_time;
460 mach_dead_name_notification_t *deadname;
461
462 send_time.tv_sec = 0;
463 send_time.tv_usec = 0;
464
465 rqs = sizeof(asl_request_msg) + MAX_TRAILER_SIZE;
466 rps = sizeof(asl_reply_msg) + MAX_TRAILER_SIZE;
467 reply = (asl_reply_msg *)calloc(1, rps);
468 if (reply == NULL) return;
469
470 rbits = MACH_RCV_MSG | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT) | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0);
471 sbits = MACH_SEND_MSG | MACH_SEND_TIMEOUT;
472
473 forever
474 {
475 snooze = 0;
476 now.tv_sec = 0;
477 now.tv_usec = 0;
478
479 /* Check if it's time to post a database change notification */
480 if (send_time.tv_sec != 0)
481 {
482 gettimeofday(&now, NULL);
483 if ((now.tv_sec > send_time.tv_sec) || ((now.tv_sec == send_time.tv_sec) && (now.tv_usec > send_time.tv_usec)))
484 {
485 notify_post(ASL_DB_NOTIFICATION);
486 send_time.tv_sec = 0;
487 send_time.tv_usec = 0;
488 snooze = 0;
489 }
490 else
491 {
492 /* mach_msg timeout is in milliseconds */
493 snooze = ((send_time.tv_sec - now.tv_sec) * 1000) + ((send_time.tv_usec - now.tv_usec) / 1000);
494 }
495 }
496
497 request = (asl_request_msg *)calloc(1, rqs);
498 if (request == NULL) continue;
499
500 request->head.msgh_local_port = global.server_port;
501 request->head.msgh_size = rqs;
502
503 memset(reply, 0, rps);
504
505 flags = rbits;
506 if (snooze != 0) flags |= MACH_RCV_TIMEOUT;
507
508 kstatus = mach_msg(&(request->head), flags, 0, rqs, global.listen_set, snooze, MACH_PORT_NULL);
509 if (request->head.msgh_id == SEND_NOTIFICATION)
510 {
511 if (send_time.tv_sec == 0)
512 {
513 gettimeofday(&send_time, NULL);
514 send_time.tv_sec += 1;
515 }
516
517 free(request);
518 continue;
519 }
520
521 if (request->head.msgh_id == MACH_NOTIFY_DEAD_NAME)
522 {
523 deadname = (mach_dead_name_notification_t *)request;
524 cancel_session(deadname->not_port);
525 free(request);
526 continue;
527 }
528
529 kstatus = asl_ipc_server(&(request->head), &(reply->head));
530 kstatus = mach_msg(&(reply->head), sbits, reply->head.msgh_size, 0, MACH_PORT_NULL, 10, MACH_PORT_NULL);
531 if (kstatus == MACH_SEND_INVALID_DEST)
532 {
533 mach_port_destroy(mach_task_self(), request->head.msgh_remote_port);
534 }
535
536 free(request);
537 }
538 }
539
540 kern_return_t
541 __asl_server_query
542 (
543 mach_port_t server,
544 caddr_t request,
545 mach_msg_type_number_t requestCnt,
546 uint64_t startid,
547 int count,
548 int flags,
549 caddr_t *reply,
550 mach_msg_type_number_t *replyCnt,
551 uint64_t *lastid,
552 int *status,
553 security_token_t *token
554 )
555 {
556 aslresponse query;
557 aslresponse res;
558 char *out, *vmbuffer;
559 uint32_t outlen;
560 kern_return_t kstatus;
561
562 *status = ASL_STATUS_OK;
563 query = asl_list_from_string(request);
564 vm_deallocate(mach_task_self(), (vm_address_t)request, requestCnt);
565 res = NULL;
566
567 *status = db_query(query, &res, startid, count, flags, lastid, token->val[0], token->val[1]);
568
569 aslresponse_free(query);
570 if (*status != ASL_STATUS_INVALID_STORE)
571 {
572 /* ignore */
573 }
574 else if (*status != ASL_STATUS_OK)
575 {
576 if (res != NULL) aslresponse_free(res);
577 return KERN_SUCCESS;
578 }
579
580 out = NULL;
581 outlen = 0;
582 out = asl_list_to_string((asl_search_result_t *)res, &outlen);
583 aslresponse_free(res);
584
585 if ((out == NULL) || (outlen == 0)) return KERN_SUCCESS;
586
587 kstatus = vm_allocate(mach_task_self(), (vm_address_t *)&vmbuffer, outlen, TRUE);
588 if (kstatus != KERN_SUCCESS)
589 {
590 free(out);
591 return kstatus;
592 }
593
594 memmove(vmbuffer, out, outlen);
595 free(out);
596
597 *reply = vmbuffer;
598 *replyCnt = outlen;
599
600 return KERN_SUCCESS;
601 }
602
603
604 kern_return_t
605 __asl_server_query_timeout
606 (
607 mach_port_t server,
608 caddr_t request,
609 mach_msg_type_number_t requestCnt,
610 uint64_t startid,
611 int count,
612 int flags,
613 caddr_t *reply,
614 mach_msg_type_number_t *replyCnt,
615 uint64_t *lastid,
616 int *status,
617 security_token_t *token
618 )
619 {
620 return __asl_server_query(server, request, requestCnt, startid, count, flags, reply, replyCnt, lastid, status, token);
621 }
622
623 kern_return_t
624 __asl_server_prune
625 (
626 mach_port_t server,
627 caddr_t request,
628 mach_msg_type_number_t requestCnt,
629 int *status,
630 security_token_t *token
631 )
632 {
633 return KERN_SUCCESS;
634 }
635
636 kern_return_t
637 __asl_server_message
638 (
639 mach_port_t server,
640 caddr_t message,
641 mach_msg_type_number_t messageCnt,
642 audit_token_t token
643 )
644 {
645 asl_msg_t *m;
646 char tmp[64];
647 uid_t uid;
648 gid_t gid;
649 pid_t pid;
650 kern_return_t kstatus;
651 mach_port_name_t client;
652
653 asldebug("__asl_server_message: %s\n", (message == NULL) ? "NULL" : message);
654
655 m = asl_msg_from_string(message);
656 vm_deallocate(mach_task_self(), (vm_address_t)message, messageCnt);
657
658 uid = (uid_t)-1;
659 gid = (gid_t)-1;
660 pid = (gid_t)-1;
661 audit_token_to_au32(token, NULL, &uid, &gid, NULL, NULL, &pid, NULL, NULL);
662
663 client = MACH_PORT_NULL;
664 kstatus = task_name_for_pid(mach_task_self(), pid, &client);
665 if (kstatus == KERN_SUCCESS) register_session(client, pid);
666
667 if (m == NULL) return KERN_SUCCESS;
668
669 snprintf(tmp, sizeof(tmp), "%d", uid);
670 asl_set(m, ASL_KEY_UID, tmp);
671
672 snprintf(tmp, sizeof(tmp), "%d", gid);
673 asl_set(m, ASL_KEY_GID, tmp);
674
675 snprintf(tmp, sizeof(tmp), "%d", pid);
676 asl_set(m, ASL_KEY_PID, tmp);
677
678 /* verify and enqueue for processing */
679 asl_enqueue_message(SOURCE_ASL_MESSAGE, NULL, m);
680
681 return KERN_SUCCESS;
682 }