]> git.saurik.com Git - apple/syslog.git/blob - syslogd.tproj/dbserver.c
a90035588f9323f19d57b9ede4400e5716525a68
[apple/syslog.git] / syslogd.tproj / dbserver.c
1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <unistd.h>
6 #include <sys/socket.h>
7 #include <sys/un.h>
8 #include <sys/ipc.h>
9 #include <sys/mman.h>
10 #include <sys/fcntl.h>
11 #include <sys/signal.h>
12 #include <mach/mach.h>
13 #include <mach/mach_error.h>
14 #include <errno.h>
15 #include <netinet/in.h>
16 #include <sys/event.h>
17 #include <servers/bootstrap.h>
18 #include <pthread.h>
19 #include <notify.h>
20 #include <sys/time.h>
21 #include <asl.h>
22 #include <asl_ipc.h>
23 #include <asl_store.h>
24 #include "daemon.h"
25
26 #define forever for(;;)
27
28 #define LIST_SIZE_DELTA 256
29 #define MAX_PRE_DISASTER_COUNT 64
30 #define MAX_DISASTER_COUNT LIST_SIZE_DELTA
31
32 #define SEND_NOTIFICATION 0xfadefade
33
34 #define QUERY_FLAG_SEARCH_REVERSE 0x00000001
35 #define SEARCH_FORWARD 1
36 #define SEARCH_BACKWARD -1
37
38 static asl_store_t *store = NULL;
39 static int disaster_occurred = 0;
40
41 extern mach_port_t server_port;
42 extern int archive_enable;
43 extern uint64_t db_curr_size;
44 extern uint64_t db_curr_empty;
45
46 static pthread_mutex_t db_lock = PTHREAD_MUTEX_INITIALIZER;
47 static pthread_mutex_t queue_lock = PTHREAD_MUTEX_INITIALIZER;
48 static pthread_cond_t queue_cond = PTHREAD_COND_INITIALIZER;
49
50 extern char *asl_list_to_string(asl_search_result_t *list, uint32_t *outlen);
51 extern asl_search_result_t *asl_list_from_string(const char *buf);
52
53 static time_t last_archive_sod = 0;
54
55 static asl_search_result_t work_queue = {0, 0, NULL};
56 static asl_search_result_t disaster_log = {0, 0, NULL};
57
58 extern boolean_t asl_ipc_server
59 (
60 mach_msg_header_t *InHeadP,
61 mach_msg_header_t *OutHeadP
62 );
63
64 typedef union
65 {
66 mach_msg_header_t head;
67 union __RequestUnion__asl_ipc_subsystem request;
68 } asl_request_msg;
69
70 typedef union
71 {
72 mach_msg_header_t head;
73 union __ReplyUnion__asl_ipc_subsystem reply;
74 } asl_reply_msg;
75
76 void
77 list_append_msg(asl_search_result_t *list, asl_msg_t *msg, uint32_t retain)
78 {
79 if (list == NULL) return;
80 if (msg == NULL) return;
81
82 /*
83 * NB: curr is the list size
84 * grow list if necessary
85 */
86 if (list->count == list->curr)
87 {
88 if (list->curr == 0)
89 {
90 list->msg = (asl_msg_t **)calloc(LIST_SIZE_DELTA, sizeof(asl_msg_t *));
91 }
92 else
93 {
94 list->msg = (asl_msg_t **)reallocf(list->msg, (list->curr + LIST_SIZE_DELTA) * sizeof(asl_msg_t *));
95 }
96
97 if (list->msg == NULL)
98 {
99 list->curr = 0;
100 list->count = 0;
101 return;
102 }
103
104 list->curr += LIST_SIZE_DELTA;
105 }
106
107 if (retain != 0) asl_msg_retain(msg);
108 list->msg[list->count] = msg;
109 list->count++;
110 }
111
112 void
113 disaster_message(asl_msg_t *m)
114 {
115 uint32_t i;
116
117 if (disaster_occurred == 0)
118 {
119 /* retain last MAX_PRE_DISASTER_COUNT messages */
120 while (disaster_log.count >= MAX_PRE_DISASTER_COUNT)
121 {
122 asl_msg_release(disaster_log.msg[0]);
123 for (i = 1; i < disaster_log.count; i++) disaster_log.msg[i - 1] = disaster_log.msg[i];
124 disaster_log.count--;
125 }
126 }
127
128 if (disaster_log.count < MAX_DISASTER_COUNT) list_append_msg(&disaster_log, m, 1);
129 }
130
131 void
132 db_enqueue(asl_msg_t *m)
133 {
134 if (m == NULL) return;
135
136 pthread_mutex_lock(&queue_lock);
137 list_append_msg(&work_queue, m, 1);
138 pthread_mutex_unlock(&queue_lock);
139 pthread_cond_signal(&queue_cond);
140 }
141
142 asl_msg_t **
143 db_dequeue(uint32_t *count)
144 {
145 asl_msg_t **work;
146
147 pthread_mutex_lock(&queue_lock);
148 pthread_cond_wait(&queue_cond, &queue_lock);
149
150 work = NULL;
151 *count = 0;
152
153 if (work_queue.count == 0)
154 {
155 pthread_mutex_unlock(&queue_lock);
156 return NULL;
157 }
158
159 work = work_queue.msg;
160 *count = work_queue.count;
161
162 work_queue.count = 0;
163 work_queue.curr = 0;
164 work_queue.msg = NULL;
165
166 pthread_mutex_unlock(&queue_lock);
167 return work;
168 }
169
170 /*
171 * Takes messages off the work queue and saves them in the database.
172 * Runs in it's own thread.
173 */
174 void
175 db_worker()
176 {
177 asl_msg_t **work;
178 uint64_t msgid;
179 uint32_t i, count, status;
180 mach_msg_empty_send_t *msg;
181 kern_return_t kstatus;
182
183 msg = (mach_msg_empty_send_t *)calloc(1, sizeof(mach_msg_empty_send_t));
184 if (msg == NULL) return;
185
186 msg->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSGH_BITS_ZERO);
187 msg->header.msgh_remote_port = server_port;
188 msg->header.msgh_local_port = MACH_PORT_NULL;
189 msg->header.msgh_size = sizeof(mach_msg_empty_send_t);
190 msg->header.msgh_id = SEND_NOTIFICATION;
191
192 forever
193 {
194 count = 0;
195 work = db_dequeue(&count);
196
197 if (work == NULL) continue;
198
199 pthread_mutex_lock(&db_lock);
200
201 if (store == NULL)
202 {
203 status = asl_store_open(_PATH_ASL_DB, 0, &store);
204 if (status != ASL_STATUS_OK)
205 {
206 disaster_occurred = 1;
207 store = NULL;
208 }
209 }
210
211 for (i = 0; (i < count) && (store != NULL); i++)
212 {
213 msgid = 0;
214 status = ASL_STATUS_OK;
215
216 status = asl_store_save(store, work[i], -1, -1, &msgid);
217 if (status != ASL_STATUS_OK)
218 {
219 /* write failed - reopen store */
220 asl_store_close(store);
221 store = NULL;
222
223 status = asl_store_open(_PATH_ASL_DB, 0, &store);
224 if (status != ASL_STATUS_OK)
225 {
226 disaster_occurred = 1;
227 store = NULL;
228 }
229
230 /* if the store re-opened, retry the save */
231 if (store != NULL)
232 {
233 status = asl_store_save(store, work[i], -1, -1, &msgid);
234 if (status != ASL_STATUS_OK)
235 {
236 disaster_occurred = 1;
237 store = NULL;
238 }
239 }
240 }
241
242 if ((i % 500) == 499)
243 {
244 pthread_mutex_unlock(&db_lock);
245 pthread_mutex_lock(&db_lock);
246 }
247 }
248
249 db_curr_size = 0;
250 db_curr_empty = 0;
251
252 if (store != NULL)
253 {
254 db_curr_size = (store->record_count + 1) * DB_RECORD_LEN;
255 db_curr_empty = store->empty_count * DB_RECORD_LEN;
256 }
257
258 pthread_mutex_unlock(&db_lock);
259
260 for (i = 0; i < count; i++) asl_msg_release(work[i]);
261 free(work);
262
263 kstatus = mach_msg(&(msg->header), MACH_SEND_MSG, msg->header.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
264 }
265 }
266
267 static char *
268 disaster_query(aslresponse query, uint32_t *outlen)
269 {
270 asl_search_result_t res;
271 uint32_t i, j, match;
272 char *out;
273
274 if (outlen == NULL) return NULL;
275 *outlen = 0;
276
277 if ((query == NULL) || ((query != NULL) && (query->count == 0))) return asl_list_to_string(&disaster_log, outlen);
278
279 memset(&res, 0, sizeof(asl_search_result_t));
280
281 for (i = 0; i < disaster_log.count; i++)
282 {
283 match = 1;
284
285 for (j = 0; (j < query->count) && (match == 1); j++)
286 {
287 match = asl_msg_cmp(query->msg[j], disaster_log.msg[i]);
288 }
289
290 if (match == 1) list_append_msg(&res, disaster_log.msg[i], 0);
291 }
292
293 if (res.count == 0) return NULL;
294
295 out = asl_list_to_string((aslresponse)&res, outlen);
296 free(res.msg);
297 return out;
298 }
299
300 /*
301 * Do a database search.
302 */
303 uint32_t
304 db_query(aslresponse query, aslresponse *res, uint64_t startid, int count, int flags, uint64_t *lastid, int32_t ruid, int32_t rgid)
305 {
306 uint32_t status, ucount;
307 int32_t dir;
308
309 ucount = count;
310 dir = SEARCH_FORWARD;
311 if (flags & QUERY_FLAG_SEARCH_REVERSE) dir = SEARCH_BACKWARD;
312
313 pthread_mutex_lock(&db_lock);
314
315 if (store == NULL)
316 {
317 status = asl_store_open(_PATH_ASL_DB, 0, &store);
318 if (status != ASL_STATUS_OK) store = NULL;
319 }
320
321 status = asl_store_match(store, query, res, lastid, startid, ucount, dir, ruid, rgid);
322
323 pthread_mutex_unlock(&db_lock);
324
325 return status;
326 }
327
328 /*
329 * Prune the database.
330 */
331 uint32_t
332 db_prune(aslresponse query)
333 {
334 uint32_t status;
335
336 if (disaster_occurred == 1) return ASL_STATUS_FAILED;
337
338 pthread_mutex_lock(&db_lock);
339
340 if (store == NULL)
341 {
342 status = asl_store_open(_PATH_ASL_DB, 0, &store);
343 if (status != ASL_STATUS_OK) store = NULL;
344 }
345
346 status = asl_store_prune(store, query);
347
348 pthread_mutex_unlock(&db_lock);
349
350 return status;
351 }
352
353 /*
354 * Database archiver
355 */
356 uint32_t
357 db_archive(time_t cut, uint64_t max_size)
358 {
359 time_t sod;
360 struct tm ctm;
361 char *archive_file_name;
362 uint32_t status;
363
364 archive_file_name = NULL;
365 memset(&ctm, 0, sizeof(struct tm));
366
367 if (localtime_r((const time_t *)&cut, &ctm) == NULL) return ASL_STATUS_FAILED;
368
369 if (archive_enable != 0)
370 {
371 asprintf(&archive_file_name, "%s/asl.%d.%02d.%02d.archive", _PATH_ASL_DIR, ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday);
372 if (archive_file_name == NULL) return ASL_STATUS_NO_MEMORY;
373 }
374
375 ctm.tm_sec = 0;
376 ctm.tm_min = 0;
377 ctm.tm_hour = 0;
378
379 sod = mktime(&ctm);
380
381 /* if the day changed, archive message received before the start of the day */
382 if (sod > last_archive_sod)
383 {
384 last_archive_sod = sod;
385 status = db_archive(sod - 1, 0);
386 /* NB return status not checked */
387 }
388
389 pthread_mutex_lock(&db_lock);
390
391 if (store == NULL)
392 {
393 status = asl_store_open(_PATH_ASL_DB, 0, &store);
394 if (status != ASL_STATUS_OK) store = NULL;
395 }
396
397 status = asl_store_archive(store, cut, archive_file_name);
398 if (status == ASL_STATUS_OK) status = asl_store_compact(store);
399 if ((status == ASL_STATUS_OK) && (max_size > 0)) status = asl_store_truncate(store, max_size, archive_file_name);
400
401 db_curr_size = 0;
402 db_curr_empty = 0;
403
404 if (store != NULL)
405 {
406 db_curr_size = (store->record_count + 1) * DB_RECORD_LEN;
407 db_curr_empty = store->empty_count * DB_RECORD_LEN;
408 }
409
410 pthread_mutex_unlock(&db_lock);
411
412 if (archive_file_name != NULL) free(archive_file_name);
413
414 return status;
415 }
416
417 uint32_t
418 db_compact(void)
419 {
420 uint32_t status;
421
422 pthread_mutex_lock(&db_lock);
423
424 if (store == NULL)
425 {
426 status = asl_store_open(_PATH_ASL_DB, 0, &store);
427 if (status != ASL_STATUS_OK)
428 {
429 pthread_mutex_unlock(&db_lock);
430 return status;
431 }
432 }
433
434 status = asl_store_compact(store);
435
436 db_curr_size = (store->record_count + 1) * DB_RECORD_LEN;
437 db_curr_empty = store->empty_count * DB_RECORD_LEN;
438
439 pthread_mutex_unlock(&db_lock);
440
441 return status;
442 }
443
444 /*
445 * Receives messages on the "com.apple.system.logger" mach port.
446 * Services database search and pruning requests.
447 * Runs in it's own thread.
448 */
449 void
450 database_server()
451 {
452 kern_return_t kstatus;
453 asl_request_msg *request;
454 asl_reply_msg *reply;
455 uint32_t rqs, rps;
456 uint32_t rbits, sbits;
457 uint32_t flags, snooze;
458 struct timeval now, send_time;
459
460 send_time.tv_sec = 0;
461 send_time.tv_usec = 0;
462
463 rqs = sizeof(asl_request_msg) + MAX_TRAILER_SIZE;
464 rps = sizeof(asl_reply_msg) + MAX_TRAILER_SIZE;
465 reply = (asl_reply_msg *)calloc(1, rps);
466 if (reply == NULL) return;
467
468 rbits = MACH_RCV_MSG | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_SENDER) | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0);
469 sbits = MACH_SEND_MSG | MACH_SEND_TIMEOUT;
470
471 forever
472 {
473 snooze = 0;
474 now.tv_sec = 0;
475 now.tv_usec = 0;
476
477 /* Check if it's time to post a database change notification */
478 if (send_time.tv_sec != 0)
479 {
480 gettimeofday(&now, NULL);
481 if ((now.tv_sec > send_time.tv_sec) || ((now.tv_sec == send_time.tv_sec) && (now.tv_usec > send_time.tv_usec)))
482 {
483 notify_post(ASL_DB_NOTIFICATION);
484 send_time.tv_sec = 0;
485 send_time.tv_usec = 0;
486 snooze = 0;
487 }
488 else
489 {
490 /* mach_msg timeout is in milliseconds */
491 snooze = ((send_time.tv_sec - now.tv_sec) * 1000) + ((send_time.tv_usec - now.tv_usec) / 1000);
492 }
493 }
494
495 request = (asl_request_msg *)calloc(1, rqs);
496 if (request == NULL) continue;
497
498 request->head.msgh_local_port = server_port;
499 request->head.msgh_size = rqs;
500
501 memset(reply, 0, rps);
502
503 flags = rbits;
504 if (snooze != 0) flags |= MACH_RCV_TIMEOUT;
505
506 kstatus = mach_msg(&(request->head), flags, 0, rqs, server_port, snooze, MACH_PORT_NULL);
507 if (request->head.msgh_id == SEND_NOTIFICATION)
508 {
509 if (send_time.tv_sec == 0)
510 {
511 gettimeofday(&send_time, NULL);
512 send_time.tv_sec += 1;
513 }
514
515 free(request);
516 continue;
517 }
518
519 kstatus = asl_ipc_server(&(request->head), &(reply->head));
520 kstatus = mach_msg(&(reply->head), sbits, reply->head.msgh_size, 0, MACH_PORT_NULL, 10, MACH_PORT_NULL);
521 if (kstatus == MACH_SEND_INVALID_DEST)
522 {
523 mach_port_destroy(mach_task_self(), request->head.msgh_remote_port);
524 }
525
526 free(request);
527 }
528 }
529
530 kern_return_t
531 __asl_server_query
532 (
533 mach_port_t server,
534 caddr_t request,
535 mach_msg_type_number_t requestCnt,
536 uint64_t startid,
537 int count,
538 int flags,
539 caddr_t *reply,
540 mach_msg_type_number_t *replyCnt,
541 uint64_t *lastid,
542 int *status,
543 security_token_t *token
544 )
545 {
546 aslresponse query;
547 aslresponse res;
548 char *out, *vmbuffer;
549 uint32_t outlen;
550 kern_return_t kstatus;
551
552 *status = ASL_STATUS_OK;
553 query = asl_list_from_string(request);
554 vm_deallocate(mach_task_self(), (vm_address_t)request, requestCnt);
555 res = NULL;
556
557 /*
558 * If the database went offline (i.e. the filesystem died),
559 * just search the disaster_log messages.
560 */
561 if (disaster_occurred == 1)
562 {
563 out = NULL;
564 outlen = 0;
565 out = disaster_query(query, &outlen);
566 aslresponse_free(query);
567 }
568 else
569 {
570 *status = db_query(query, &res, startid, count, flags, lastid, token->val[0], token->val[1]);
571
572 aslresponse_free(query);
573 if (*status != ASL_STATUS_OK)
574 {
575 if (res != NULL) aslresponse_free(res);
576 return KERN_SUCCESS;
577 }
578
579 out = NULL;
580 outlen = 0;
581 out = asl_list_to_string((asl_search_result_t *)res, &outlen);
582 aslresponse_free(res);
583 }
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 aslresponse query;
634
635 *status = ASL_STATUS_OK;
636
637 if (request == NULL) return KERN_SUCCESS;
638
639 query = asl_list_from_string(request);
640
641 vm_deallocate(mach_task_self(), (vm_address_t)request, requestCnt);
642
643 /* only root may prune the database */
644 if (token->val[0] != 0)
645 {
646 *status = ASL_STATUS_FAILED;
647 return KERN_SUCCESS;
648 }
649
650 *status = db_prune(query);
651 aslresponse_free(query);
652
653 return KERN_SUCCESS;
654 }