]> git.saurik.com Git - apple/syslog.git/blob - syslogd.tproj/dbserver.c
syslog-69.0.3.tar.gz
[apple/syslog.git] / syslogd.tproj / dbserver.c
1 /*
2 * Copyright (c) 2007-2008 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 <errno.h>
39 #include <netinet/in.h>
40 #include <sys/event.h>
41 #include <servers/bootstrap.h>
42 #include <pthread.h>
43 #include <notify.h>
44 #include <sys/time.h>
45 #include <asl.h>
46 #include <asl_ipc.h>
47 #include <asl_core.h>
48 #include "daemon.h"
49
50 #define forever for(;;)
51
52 #define LIST_SIZE_DELTA 256
53
54 #define SEND_NOTIFICATION 0xfadefade
55
56 #define QUERY_FLAG_SEARCH_REVERSE 0x00000001
57 #define SEARCH_FORWARD 1
58 #define SEARCH_BACKWARD -1
59
60 static pthread_mutex_t db_lock = PTHREAD_MUTEX_INITIALIZER;
61 static pthread_mutex_t queue_lock = PTHREAD_MUTEX_INITIALIZER;
62 static pthread_cond_t queue_cond = PTHREAD_COND_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
68 static asl_search_result_t work_queue = {0, 0, NULL};
69
70 static time_t file_sweep_last = 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 list_append_msg(asl_search_result_t *list, asl_msg_t *msg, uint32_t retain)
86 {
87 if (list == NULL) return;
88 if (msg == NULL) return;
89
90 /*
91 * NB: curr is the list size
92 * grow list if necessary
93 */
94 if (list->count == list->curr)
95 {
96 if (list->curr == 0)
97 {
98 list->msg = (asl_msg_t **)calloc(LIST_SIZE_DELTA, sizeof(asl_msg_t *));
99 }
100 else
101 {
102 list->msg = (asl_msg_t **)reallocf(list->msg, (list->curr + LIST_SIZE_DELTA) * sizeof(asl_msg_t *));
103 }
104
105 if (list->msg == NULL)
106 {
107 list->curr = 0;
108 list->count = 0;
109 return;
110 }
111
112 list->curr += LIST_SIZE_DELTA;
113 }
114
115 if (retain != 0) asl_msg_retain(msg);
116 list->msg[list->count] = msg;
117 list->count++;
118 }
119
120 void
121 db_ping_store(time_t now)
122 {
123 time_t delta;
124
125 if ((global.dbtype & DB_TYPE_FILE) && (global.file_db != NULL))
126 {
127 delta = now - file_sweep_last;
128 if (delta >= global.asl_store_ping_time)
129 {
130 asl_store_sweep_file_cache(global.file_db);
131 file_sweep_last = now;
132 }
133 }
134 }
135
136 void
137 db_enqueue(asl_msg_t *m)
138 {
139 if (m == NULL) return;
140
141 pthread_mutex_lock(&queue_lock);
142 list_append_msg(&work_queue, m, 1);
143 pthread_mutex_unlock(&queue_lock);
144 pthread_cond_signal(&queue_cond);
145 }
146
147 asl_msg_t **
148 db_dequeue(uint32_t *count)
149 {
150 asl_msg_t **work;
151
152 pthread_mutex_lock(&queue_lock);
153 pthread_cond_wait(&queue_cond, &queue_lock);
154
155 work = NULL;
156 *count = 0;
157
158 if (work_queue.count == 0)
159 {
160 pthread_mutex_unlock(&queue_lock);
161 return NULL;
162 }
163
164 work = work_queue.msg;
165 *count = work_queue.count;
166
167 work_queue.count = 0;
168 work_queue.curr = 0;
169 work_queue.msg = NULL;
170
171 pthread_mutex_unlock(&queue_lock);
172 return work;
173 }
174
175 void
176 db_asl_open()
177 {
178 uint32_t status;
179 struct stat sb;
180
181 if ((global.dbtype & DB_TYPE_FILE) && (global.file_db == NULL))
182 {
183 memset(&sb, 0, sizeof(struct stat));
184 if (stat(PATH_ASL_STORE, &sb) == 0)
185 {
186 /* must be a directory */
187 if ((sb.st_mode & S_IFDIR) == 0)
188 {
189 asldebug("error: %s is not a directory", PATH_ASL_STORE);
190 return;
191 }
192 }
193 else
194 {
195 if (errno == ENOENT)
196 {
197 /* /var/log/asl doesn't exist - create it */
198 if (mkdir(PATH_ASL_STORE, 0755) != 0)
199 {
200 asldebug("error: can't create data store %s: %s\n", PATH_ASL_STORE, strerror(errno));
201 return;
202 }
203 }
204 else
205 {
206 /* stat failed for some other reason */
207 asldebug("error: can't stat data store %s: %s\n", PATH_ASL_STORE, strerror(errno));
208 return;
209 }
210 }
211
212 status = asl_store_open_write(NULL, &(global.file_db));
213 if (status != ASL_STATUS_OK)
214 {
215 asldebug("asl_store_open_write: %s\n", asl_core_error(status));
216 }
217 else
218 {
219 if (global.db_file_max != 0) asl_store_max_file_size(global.file_db, global.db_file_max);
220 }
221
222 if (global.did_store_sweep == 0)
223 {
224 status = asl_store_signal_sweep(global.file_db);
225 if (status == ASL_STATUS_OK) global.did_store_sweep = 1;
226 }
227 }
228
229 if ((global.dbtype & DB_TYPE_MEMORY) && (global.memory_db == NULL))
230 {
231 status = asl_memory_open(global.db_memory_max, &(global.memory_db));
232 if (status != ASL_STATUS_OK)
233 {
234 asldebug("asl_memory_open: %s\n", asl_core_error(status));
235 }
236 }
237
238 if ((global.dbtype & DB_TYPE_MINI) && (global.mini_db == NULL))
239 {
240 status = asl_mini_memory_open(global.db_mini_max, &(global.mini_db));
241 if (status != ASL_STATUS_OK)
242 {
243 asldebug("asl_mini_memory_open: %s\n", asl_core_error(status));
244 }
245 }
246 }
247
248 /*
249 * Takes messages off the work queue and saves them in the database.
250 * Runs in it's own thread.
251 */
252 void
253 db_worker()
254 {
255 asl_msg_t **work;
256 uint64_t msgid;
257 uint32_t i, count, status;
258 mach_msg_empty_send_t *msg;
259 kern_return_t kstatus;
260
261 msg = (mach_msg_empty_send_t *)calloc(1, sizeof(mach_msg_empty_send_t));
262 if (msg == NULL) return;
263
264 msg->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSGH_BITS_ZERO);
265 msg->header.msgh_remote_port = global.server_port;
266 msg->header.msgh_local_port = MACH_PORT_NULL;
267 msg->header.msgh_size = sizeof(mach_msg_empty_send_t);
268 msg->header.msgh_id = SEND_NOTIFICATION;
269
270 forever
271 {
272 count = 0;
273 work = db_dequeue(&count);
274
275 if (work == NULL) continue;
276
277 pthread_mutex_lock(&db_lock);
278
279 db_asl_open();
280
281 for (i = 0; i < count; i++)
282 {
283 if (global.dbtype & DB_TYPE_FILE)
284 {
285 status = asl_store_save(global.file_db, work[i]);
286 if (status != ASL_STATUS_OK)
287 {
288 /* write failed - reopen & retry */
289 asldebug("asl_store_save: %s\n", asl_core_error(status));
290 asl_store_close(global.file_db);
291 global.file_db = NULL;
292
293 db_asl_open();
294 status = asl_store_save(global.file_db, work[i]);
295 if (status != ASL_STATUS_OK)
296 {
297 asldebug("(retry) asl_store_save: %s\n", asl_core_error(status));
298 asl_store_close(global.file_db);
299 global.file_db = NULL;
300
301 global.dbtype |= DB_TYPE_MEMORY;
302 if (global.memory_db == NULL)
303 {
304 status = asl_memory_open(global.db_memory_max, &(global.memory_db));
305 if (status != ASL_STATUS_OK)
306 {
307 asldebug("asl_memory_open: %s\n", asl_core_error(status));
308 }
309 }
310 }
311 }
312 }
313
314 if (global.dbtype & DB_TYPE_MEMORY)
315 {
316 msgid = 0;
317 status = asl_memory_save(global.memory_db, work[i], &msgid);
318 if (status != ASL_STATUS_OK)
319 {
320 /* save failed - reopen & retry*/
321 asldebug("asl_memory_save: %s\n", asl_core_error(status));
322 asl_memory_close(global.memory_db);
323 global.memory_db = NULL;
324
325 db_asl_open();
326 msgid = 0;
327 status = asl_memory_save(global.memory_db, work[i], &msgid);
328 if (status != ASL_STATUS_OK)
329 {
330 asldebug("(retry) asl_memory_save: %s\n", asl_core_error(status));
331 asl_memory_close(global.memory_db);
332 global.memory_db = NULL;
333 }
334 }
335 }
336
337 if (global.dbtype & DB_TYPE_MINI)
338 {
339 status = asl_mini_memory_save(global.mini_db, work[i], &msgid);
340 if (status != ASL_STATUS_OK)
341 {
342 /* save failed - reopen & retry*/
343 asldebug("asl_mini_memory_save: %s\n", asl_core_error(status));
344 asl_mini_memory_close(global.mini_db);
345 global.mini_db = NULL;
346
347 db_asl_open();
348 status = asl_mini_memory_save(global.mini_db, work[i], &msgid);
349 if (status != ASL_STATUS_OK)
350 {
351 asldebug("(retry) asl_memory_save: %s\n", asl_core_error(status));
352 asl_mini_memory_close(global.mini_db);
353 global.mini_db = NULL;
354 }
355 }
356 }
357
358 if ((i % 500) == 499)
359 {
360 pthread_mutex_unlock(&db_lock);
361 pthread_mutex_lock(&db_lock);
362 }
363 }
364
365 pthread_mutex_unlock(&db_lock);
366
367 for (i = 0; i < count; i++) asl_msg_release(work[i]);
368 free(work);
369
370 kstatus = mach_msg(&(msg->header), MACH_SEND_MSG, msg->header.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
371 }
372 }
373
374 void
375 disaster_message(asl_msg_t *msg)
376 {
377 uint64_t msgid;
378 uint32_t status;
379
380 msgid = 0;
381
382 if ((global.dbtype & DB_TYPE_MINI) == 0)
383 {
384 if (global.mini_db == NULL)
385 {
386 status = asl_mini_memory_open(global.db_mini_max, &(global.mini_db));
387 if (status != ASL_STATUS_OK) asldebug("asl_mini_memory_open: %s\n", asl_core_error(status));
388 else asl_mini_memory_save(global.mini_db, msg, &msgid);
389 }
390 }
391 }
392
393 /*
394 * Do a database search.
395 */
396 uint32_t
397 db_query(aslresponse query, aslresponse *res, uint64_t startid, int count, int flags, uint64_t *lastid, int32_t ruid, int32_t rgid)
398 {
399 uint32_t status, ucount;
400 int32_t dir;
401
402 ucount = count;
403 dir = SEARCH_FORWARD;
404 if (flags & QUERY_FLAG_SEARCH_REVERSE) dir = SEARCH_BACKWARD;
405
406 pthread_mutex_lock(&db_lock);
407
408 status = ASL_STATUS_FAILED;
409
410 if (global.dbtype & DB_TYPE_MEMORY) status = asl_memory_match(global.memory_db, query, res, lastid, startid, ucount, dir, ruid, rgid);
411 else status = asl_mini_memory_match(global.mini_db, query, res, lastid, startid, ucount, dir);
412
413 pthread_mutex_unlock(&db_lock);
414
415 return status;
416 }
417
418 /*
419 * Receives messages on the "com.apple.system.logger" mach port.
420 * Services database search requests.
421 * Runs in it's own thread.
422 */
423 void
424 database_server()
425 {
426 kern_return_t kstatus;
427 asl_request_msg *request;
428 asl_reply_msg *reply;
429 uint32_t rqs, rps;
430 uint32_t rbits, sbits;
431 uint32_t flags, snooze;
432 struct timeval now, send_time;
433
434 send_time.tv_sec = 0;
435 send_time.tv_usec = 0;
436
437 rqs = sizeof(asl_request_msg) + MAX_TRAILER_SIZE;
438 rps = sizeof(asl_reply_msg) + MAX_TRAILER_SIZE;
439 reply = (asl_reply_msg *)calloc(1, rps);
440 if (reply == NULL) return;
441
442 rbits = MACH_RCV_MSG | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_SENDER) | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0);
443 sbits = MACH_SEND_MSG | MACH_SEND_TIMEOUT;
444
445 forever
446 {
447 snooze = 0;
448 now.tv_sec = 0;
449 now.tv_usec = 0;
450
451 /* Check if it's time to post a database change notification */
452 if (send_time.tv_sec != 0)
453 {
454 gettimeofday(&now, NULL);
455 if ((now.tv_sec > send_time.tv_sec) || ((now.tv_sec == send_time.tv_sec) && (now.tv_usec > send_time.tv_usec)))
456 {
457 notify_post(ASL_DB_NOTIFICATION);
458 notify_post(SELF_DB_NOTIFICATION);
459 send_time.tv_sec = 0;
460 send_time.tv_usec = 0;
461 snooze = 0;
462 }
463 else
464 {
465 /* mach_msg timeout is in milliseconds */
466 snooze = ((send_time.tv_sec - now.tv_sec) * 1000) + ((send_time.tv_usec - now.tv_usec) / 1000);
467 }
468 }
469
470 request = (asl_request_msg *)calloc(1, rqs);
471 if (request == NULL) continue;
472
473 request->head.msgh_local_port = global.server_port;
474 request->head.msgh_size = rqs;
475
476 memset(reply, 0, rps);
477
478 flags = rbits;
479 if (snooze != 0) flags |= MACH_RCV_TIMEOUT;
480
481 kstatus = mach_msg(&(request->head), flags, 0, rqs, global.server_port, snooze, MACH_PORT_NULL);
482 if (request->head.msgh_id == SEND_NOTIFICATION)
483 {
484 if (send_time.tv_sec == 0)
485 {
486 gettimeofday(&send_time, NULL);
487 send_time.tv_sec += 1;
488 }
489
490 free(request);
491 continue;
492 }
493
494 kstatus = asl_ipc_server(&(request->head), &(reply->head));
495 kstatus = mach_msg(&(reply->head), sbits, reply->head.msgh_size, 0, MACH_PORT_NULL, 10, MACH_PORT_NULL);
496 if (kstatus == MACH_SEND_INVALID_DEST)
497 {
498 mach_port_destroy(mach_task_self(), request->head.msgh_remote_port);
499 }
500
501 free(request);
502 }
503 }
504
505 kern_return_t
506 __asl_server_query
507 (
508 mach_port_t server,
509 caddr_t request,
510 mach_msg_type_number_t requestCnt,
511 uint64_t startid,
512 int count,
513 int flags,
514 caddr_t *reply,
515 mach_msg_type_number_t *replyCnt,
516 uint64_t *lastid,
517 int *status,
518 security_token_t *token
519 )
520 {
521 aslresponse query;
522 aslresponse res;
523 char *out, *vmbuffer;
524 uint32_t outlen;
525 kern_return_t kstatus;
526
527 *status = ASL_STATUS_OK;
528 query = asl_list_from_string(request);
529 vm_deallocate(mach_task_self(), (vm_address_t)request, requestCnt);
530 res = NULL;
531
532 *status = db_query(query, &res, startid, count, flags, lastid, token->val[0], token->val[1]);
533
534 aslresponse_free(query);
535 if (*status != ASL_STATUS_OK)
536 {
537 if (res != NULL) aslresponse_free(res);
538 return KERN_SUCCESS;
539 }
540
541 out = NULL;
542 outlen = 0;
543 out = asl_list_to_string((asl_search_result_t *)res, &outlen);
544 aslresponse_free(res);
545
546 if ((out == NULL) || (outlen == 0)) return KERN_SUCCESS;
547
548 kstatus = vm_allocate(mach_task_self(), (vm_address_t *)&vmbuffer, outlen, TRUE);
549 if (kstatus != KERN_SUCCESS)
550 {
551 free(out);
552 return kstatus;
553 }
554
555 memmove(vmbuffer, out, outlen);
556 free(out);
557
558 *reply = vmbuffer;
559 *replyCnt = outlen;
560
561 return KERN_SUCCESS;
562 }
563
564
565 kern_return_t
566 __asl_server_query_timeout
567 (
568 mach_port_t server,
569 caddr_t request,
570 mach_msg_type_number_t requestCnt,
571 uint64_t startid,
572 int count,
573 int flags,
574 caddr_t *reply,
575 mach_msg_type_number_t *replyCnt,
576 uint64_t *lastid,
577 int *status,
578 security_token_t *token
579 )
580 {
581 return __asl_server_query(server, request, requestCnt, startid, count, flags, reply, replyCnt, lastid, status, token);
582 }
583
584 kern_return_t
585 __asl_server_prune
586 (
587 mach_port_t server,
588 caddr_t request,
589 mach_msg_type_number_t requestCnt,
590 int *status,
591 security_token_t *token
592 )
593 {
594 return KERN_SUCCESS;
595 }