6 #include <sys/socket.h>
10 #include <sys/fcntl.h>
11 #include <sys/signal.h>
12 #include <mach/mach.h>
13 #include <mach/mach_error.h>
15 #include <netinet/in.h>
16 #include <sys/event.h>
17 #include <servers/bootstrap.h>
23 #include <asl_store.h>
26 #define forever for(;;)
28 #define LIST_SIZE_DELTA 256
29 #define MAX_PRE_DISASTER_COUNT 64
30 #define MAX_DISASTER_COUNT LIST_SIZE_DELTA
32 #define SEND_NOTIFICATION 0xfadefade
34 #define QUERY_FLAG_SEARCH_REVERSE 0x00000001
35 #define SEARCH_FORWARD 1
36 #define SEARCH_BACKWARD -1
38 static asl_store_t
*store
= NULL
;
39 static int disaster_occurred
= 0;
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
;
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
;
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
);
53 static time_t last_archive_sod
= 0;
55 static asl_search_result_t work_queue
= {0, 0, NULL
};
56 static asl_search_result_t disaster_log
= {0, 0, NULL
};
58 extern boolean_t asl_ipc_server
60 mach_msg_header_t
*InHeadP
,
61 mach_msg_header_t
*OutHeadP
66 mach_msg_header_t head
;
67 union __RequestUnion__asl_ipc_subsystem request
;
72 mach_msg_header_t head
;
73 union __ReplyUnion__asl_ipc_subsystem reply
;
77 list_append_msg(asl_search_result_t
*list
, asl_msg_t
*msg
, uint32_t retain
)
79 if (list
== NULL
) return;
80 if (msg
== NULL
) return;
83 * NB: curr is the list size
84 * grow list if necessary
86 if (list
->count
== list
->curr
)
90 list
->msg
= (asl_msg_t
**)calloc(LIST_SIZE_DELTA
, sizeof(asl_msg_t
*));
94 list
->msg
= (asl_msg_t
**)reallocf(list
->msg
, (list
->curr
+ LIST_SIZE_DELTA
) * sizeof(asl_msg_t
*));
97 if (list
->msg
== NULL
)
104 list
->curr
+= LIST_SIZE_DELTA
;
107 if (retain
!= 0) asl_msg_retain(msg
);
108 list
->msg
[list
->count
] = msg
;
113 disaster_message(asl_msg_t
*m
)
117 if (disaster_occurred
== 0)
119 /* retain last MAX_PRE_DISASTER_COUNT messages */
120 while (disaster_log
.count
>= MAX_PRE_DISASTER_COUNT
)
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
--;
128 if (disaster_log
.count
< MAX_DISASTER_COUNT
) list_append_msg(&disaster_log
, m
, 1);
132 db_enqueue(asl_msg_t
*m
)
134 if (m
== NULL
) return;
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
);
143 db_dequeue(uint32_t *count
)
147 pthread_mutex_lock(&queue_lock
);
148 pthread_cond_wait(&queue_cond
, &queue_lock
);
153 if (work_queue
.count
== 0)
155 pthread_mutex_unlock(&queue_lock
);
159 work
= work_queue
.msg
;
160 *count
= work_queue
.count
;
162 work_queue
.count
= 0;
164 work_queue
.msg
= NULL
;
166 pthread_mutex_unlock(&queue_lock
);
171 * Takes messages off the work queue and saves them in the database.
172 * Runs in it's own thread.
179 uint32_t i
, count
, status
;
180 mach_msg_empty_send_t
*msg
;
181 kern_return_t kstatus
;
183 msg
= (mach_msg_empty_send_t
*)calloc(1, sizeof(mach_msg_empty_send_t
));
184 if (msg
== NULL
) return;
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
;
195 work
= db_dequeue(&count
);
197 if (work
== NULL
) continue;
199 pthread_mutex_lock(&db_lock
);
203 status
= asl_store_open(_PATH_ASL_DB
, 0, &store
);
204 if (status
!= ASL_STATUS_OK
)
206 disaster_occurred
= 1;
211 for (i
= 0; (i
< count
) && (store
!= NULL
); i
++)
214 status
= ASL_STATUS_OK
;
216 status
= asl_store_save(store
, work
[i
], -1, -1, &msgid
);
217 if (status
!= ASL_STATUS_OK
)
219 /* write failed - reopen store */
220 asl_store_close(store
);
223 status
= asl_store_open(_PATH_ASL_DB
, 0, &store
);
224 if (status
!= ASL_STATUS_OK
)
226 disaster_occurred
= 1;
230 /* if the store re-opened, retry the save */
233 status
= asl_store_save(store
, work
[i
], -1, -1, &msgid
);
234 if (status
!= ASL_STATUS_OK
)
236 disaster_occurred
= 1;
242 if ((i
% 500) == 499)
244 pthread_mutex_unlock(&db_lock
);
245 pthread_mutex_lock(&db_lock
);
254 db_curr_size
= (store
->record_count
+ 1) * DB_RECORD_LEN
;
255 db_curr_empty
= store
->empty_count
* DB_RECORD_LEN
;
258 pthread_mutex_unlock(&db_lock
);
260 for (i
= 0; i
< count
; i
++) asl_msg_release(work
[i
]);
263 kstatus
= mach_msg(&(msg
->header
), MACH_SEND_MSG
, msg
->header
.msgh_size
, 0, MACH_PORT_NULL
, MACH_MSG_TIMEOUT_NONE
, MACH_PORT_NULL
);
268 disaster_query(aslresponse query
, uint32_t *outlen
)
270 asl_search_result_t res
;
271 uint32_t i
, j
, match
;
274 if (outlen
== NULL
) return NULL
;
277 if ((query
== NULL
) || ((query
!= NULL
) && (query
->count
== 0))) return asl_list_to_string(&disaster_log
, outlen
);
279 memset(&res
, 0, sizeof(asl_search_result_t
));
281 for (i
= 0; i
< disaster_log
.count
; i
++)
285 for (j
= 0; (j
< query
->count
) && (match
== 1); j
++)
287 match
= asl_msg_cmp(query
->msg
[j
], disaster_log
.msg
[i
]);
290 if (match
== 1) list_append_msg(&res
, disaster_log
.msg
[i
], 0);
293 if (res
.count
== 0) return NULL
;
295 out
= asl_list_to_string((aslresponse
)&res
, outlen
);
301 * Do a database search.
304 db_query(aslresponse query
, aslresponse
*res
, uint64_t startid
, int count
, int flags
, uint64_t *lastid
, int32_t ruid
, int32_t rgid
)
306 uint32_t status
, ucount
;
310 dir
= SEARCH_FORWARD
;
311 if (flags
& QUERY_FLAG_SEARCH_REVERSE
) dir
= SEARCH_BACKWARD
;
313 pthread_mutex_lock(&db_lock
);
317 status
= asl_store_open(_PATH_ASL_DB
, 0, &store
);
318 if (status
!= ASL_STATUS_OK
) store
= NULL
;
321 status
= asl_store_match(store
, query
, res
, lastid
, startid
, ucount
, dir
, ruid
, rgid
);
323 pthread_mutex_unlock(&db_lock
);
329 * Prune the database.
332 db_prune(aslresponse query
)
336 if (disaster_occurred
== 1) return ASL_STATUS_FAILED
;
338 pthread_mutex_lock(&db_lock
);
342 status
= asl_store_open(_PATH_ASL_DB
, 0, &store
);
343 if (status
!= ASL_STATUS_OK
) store
= NULL
;
346 status
= asl_store_prune(store
, query
);
348 pthread_mutex_unlock(&db_lock
);
357 db_archive(time_t cut
, uint64_t max_size
)
361 char *archive_file_name
;
364 archive_file_name
= NULL
;
365 memset(&ctm
, 0, sizeof(struct tm
));
367 if (localtime_r((const time_t *)&cut
, &ctm
) == NULL
) return ASL_STATUS_FAILED
;
369 if (archive_enable
!= 0)
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
;
381 /* if the day changed, archive message received before the start of the day */
382 if (sod
> last_archive_sod
)
384 last_archive_sod
= sod
;
385 status
= db_archive(sod
- 1, 0);
386 /* NB return status not checked */
389 pthread_mutex_lock(&db_lock
);
393 status
= asl_store_open(_PATH_ASL_DB
, 0, &store
);
394 if (status
!= ASL_STATUS_OK
) store
= NULL
;
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
);
406 db_curr_size
= (store
->record_count
+ 1) * DB_RECORD_LEN
;
407 db_curr_empty
= store
->empty_count
* DB_RECORD_LEN
;
410 pthread_mutex_unlock(&db_lock
);
412 if (archive_file_name
!= NULL
) free(archive_file_name
);
422 pthread_mutex_lock(&db_lock
);
426 status
= asl_store_open(_PATH_ASL_DB
, 0, &store
);
427 if (status
!= ASL_STATUS_OK
)
429 pthread_mutex_unlock(&db_lock
);
434 status
= asl_store_compact(store
);
436 db_curr_size
= (store
->record_count
+ 1) * DB_RECORD_LEN
;
437 db_curr_empty
= store
->empty_count
* DB_RECORD_LEN
;
439 pthread_mutex_unlock(&db_lock
);
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.
452 kern_return_t kstatus
;
453 asl_request_msg
*request
;
454 asl_reply_msg
*reply
;
456 uint32_t rbits
, sbits
;
457 uint32_t flags
, snooze
;
458 struct timeval now
, send_time
;
460 send_time
.tv_sec
= 0;
461 send_time
.tv_usec
= 0;
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;
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
;
477 /* Check if it's time to post a database change notification */
478 if (send_time
.tv_sec
!= 0)
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
)))
483 notify_post(ASL_DB_NOTIFICATION
);
484 send_time
.tv_sec
= 0;
485 send_time
.tv_usec
= 0;
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);
495 request
= (asl_request_msg
*)calloc(1, rqs
);
496 if (request
== NULL
) continue;
498 request
->head
.msgh_local_port
= server_port
;
499 request
->head
.msgh_size
= rqs
;
501 memset(reply
, 0, rps
);
504 if (snooze
!= 0) flags
|= MACH_RCV_TIMEOUT
;
506 kstatus
= mach_msg(&(request
->head
), flags
, 0, rqs
, server_port
, snooze
, MACH_PORT_NULL
);
507 if (request
->head
.msgh_id
== SEND_NOTIFICATION
)
509 if (send_time
.tv_sec
== 0)
511 gettimeofday(&send_time
, NULL
);
512 send_time
.tv_sec
+= 1;
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
)
523 mach_port_destroy(mach_task_self(), request
->head
.msgh_remote_port
);
535 mach_msg_type_number_t requestCnt
,
540 mach_msg_type_number_t
*replyCnt
,
543 security_token_t
*token
548 char *out
, *vmbuffer
;
550 kern_return_t kstatus
;
552 *status
= ASL_STATUS_OK
;
553 query
= asl_list_from_string(request
);
554 vm_deallocate(mach_task_self(), (vm_address_t
)request
, requestCnt
);
558 * If the database went offline (i.e. the filesystem died),
559 * just search the disaster_log messages.
561 if (disaster_occurred
== 1)
565 out
= disaster_query(query
, &outlen
);
566 aslresponse_free(query
);
570 *status
= db_query(query
, &res
, startid
, count
, flags
, lastid
, token
->val
[0], token
->val
[1]);
572 aslresponse_free(query
);
573 if (*status
!= ASL_STATUS_OK
)
575 if (res
!= NULL
) aslresponse_free(res
);
581 out
= asl_list_to_string((asl_search_result_t
*)res
, &outlen
);
582 aslresponse_free(res
);
585 if ((out
== NULL
) || (outlen
== 0)) return KERN_SUCCESS
;
587 kstatus
= vm_allocate(mach_task_self(), (vm_address_t
*)&vmbuffer
, outlen
, TRUE
);
588 if (kstatus
!= KERN_SUCCESS
)
594 memmove(vmbuffer
, out
, outlen
);
605 __asl_server_query_timeout
609 mach_msg_type_number_t requestCnt
,
614 mach_msg_type_number_t
*replyCnt
,
617 security_token_t
*token
620 return __asl_server_query(server
, request
, requestCnt
, startid
, count
, flags
, reply
, replyCnt
, lastid
, status
, token
);
628 mach_msg_type_number_t requestCnt
,
630 security_token_t
*token
635 *status
= ASL_STATUS_OK
;
637 if (request
== NULL
) return KERN_SUCCESS
;
639 query
= asl_list_from_string(request
);
641 vm_deallocate(mach_task_self(), (vm_address_t
)request
, requestCnt
);
643 /* only root may prune the database */
644 if (token
->val
[0] != 0)
646 *status
= ASL_STATUS_FAILED
;
650 *status
= db_prune(query
);
651 aslresponse_free(query
);