2 * Copyright (c) 2007-2009 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 #include <sys/types.h>
29 #include <sys/socket.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>
40 #include <netinet/in.h>
41 #include <sys/event.h>
42 #include <servers/bootstrap.h>
48 #include <asl_ipc_server.h>
52 #define forever for(;;)
54 #define LIST_SIZE_DELTA 256
56 #define SEND_NOTIFICATION 0xfadefade
58 #define QUERY_FLAG_SEARCH_REVERSE 0x00000001
59 #define SEARCH_FORWARD 1
60 #define SEARCH_BACKWARD -1
62 static pthread_mutex_t db_lock
= PTHREAD_MUTEX_INITIALIZER
;
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
);
69 static task_name_t
*client_tasks
= NULL
;
70 static uint32_t client_tasks_count
= 0;
74 mach_msg_header_t head
;
75 union __RequestUnion__asl_ipc_subsystem request
;
80 mach_msg_header_t head
;
81 union __ReplyUnion__asl_ipc_subsystem reply
;
87 if ((global
.dbtype
& DB_TYPE_FILE
) && (global
.file_db
!= NULL
))
89 global
.store_flags
|= STORE_FLAGS_FILE_CACHE_SWEEP_REQUESTED
;
91 /* wake up the output worker thread */
92 pthread_cond_signal(&global
.work_queue_cond
);
102 if ((global
.dbtype
& DB_TYPE_FILE
) && (global
.file_db
== NULL
))
104 memset(&sb
, 0, sizeof(struct stat
));
105 if (stat(PATH_ASL_STORE
, &sb
) == 0)
107 /* must be a directory */
108 if ((sb
.st_mode
& S_IFDIR
) == 0)
110 asldebug("error: %s is not a directory", PATH_ASL_STORE
);
118 /* /var/log/asl doesn't exist - create it */
119 if (mkdir(PATH_ASL_STORE
, 0755) != 0)
121 asldebug("error: can't create data store %s: %s\n", PATH_ASL_STORE
, strerror(errno
));
127 /* stat failed for some other reason */
128 asldebug("error: can't stat data store %s: %s\n", PATH_ASL_STORE
, strerror(errno
));
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.
137 status
= bb_convert(PATH_ASL_STORE
);
138 if (status
!= ASL_STATUS_OK
)
140 asldebug("ASL data store conversion failed!: %s\n", asl_core_error(status
));
143 status
= asl_store_open_write(NULL
, &(global
.file_db
));
144 if (status
!= ASL_STATUS_OK
)
146 asldebug("asl_store_open_write: %s\n", asl_core_error(status
));
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
);
155 if ((global
.dbtype
& DB_TYPE_MEMORY
) && (global
.memory_db
== NULL
))
157 status
= asl_memory_open(global
.db_memory_max
, &(global
.memory_db
));
158 if (status
!= ASL_STATUS_OK
)
160 asldebug("asl_memory_open: %s\n", asl_core_error(status
));
164 if ((global
.dbtype
& DB_TYPE_MINI
) && (global
.mini_db
== NULL
))
166 status
= asl_mini_memory_open(global
.db_mini_max
, &(global
.mini_db
));
167 if (status
!= ASL_STATUS_OK
)
169 asldebug("asl_mini_memory_open: %s\n", asl_core_error(status
));
175 * Takes messages off the work queue and saves them.
176 * Runs in it's own thread.
183 mach_msg_empty_send_t
*empty
;
184 kern_return_t kstatus
;
186 empty
= (mach_msg_empty_send_t
*)calloc(1, sizeof(mach_msg_empty_send_t
));
187 if (empty
== NULL
) return;
193 /* blocks until work is available */
194 work
= asl_work_dequeue(&count
);
198 if ((global
.dbtype
& DB_TYPE_FILE
) && (global
.store_flags
& STORE_FLAGS_FILE_CACHE_SWEEP_REQUESTED
))
200 asl_store_sweep_file_cache(global
.file_db
);
201 global
.store_flags
&= ~STORE_FLAGS_FILE_CACHE_SWEEP_REQUESTED
;
207 for (i
= 0; i
< count
; i
++)
209 asl_message_match_and_log(work
[i
]);
210 asl_msg_release(work
[i
]);
215 if (global
.store_flags
& STORE_FLAGS_FILE_CACHE_SWEEP_REQUESTED
)
217 asl_store_sweep_file_cache(global
.file_db
);
218 global
.store_flags
&= ~STORE_FLAGS_FILE_CACHE_SWEEP_REQUESTED
;
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
;
227 kstatus
= mach_msg(&(empty
->header
), MACH_SEND_MSG
| MACH_SEND_TIMEOUT
, empty
->header
.msgh_size
, 0, MACH_PORT_NULL
, 2, MACH_PORT_NULL
);
232 * Called from asl_action.c to save messgaes to the ASL data store
235 db_save_message(asl_msg_t
*msg
)
240 pthread_mutex_lock(&db_lock
);
244 if (global
.dbtype
& DB_TYPE_FILE
)
246 status
= asl_store_save(global
.file_db
, msg
);
247 if (status
!= ASL_STATUS_OK
)
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
;
255 status
= asl_store_save(global
.file_db
, msg
);
256 if (status
!= ASL_STATUS_OK
)
258 asldebug("(retry) asl_store_save: %s\n", asl_core_error(status
));
259 asl_store_close(global
.file_db
);
260 global
.file_db
= NULL
;
262 global
.dbtype
|= DB_TYPE_MEMORY
;
263 if (global
.memory_db
== NULL
)
265 status
= asl_memory_open(global
.db_memory_max
, &(global
.memory_db
));
266 if (status
!= ASL_STATUS_OK
)
268 asldebug("asl_memory_open: %s\n", asl_core_error(status
));
275 if (global
.dbtype
& DB_TYPE_MEMORY
)
278 status
= asl_memory_save(global
.memory_db
, msg
, &msgid
);
279 if (status
!= ASL_STATUS_OK
)
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
;
288 status
= asl_memory_save(global
.memory_db
, msg
, &msgid
);
289 if (status
!= ASL_STATUS_OK
)
291 asldebug("(retry) asl_memory_save: %s\n", asl_core_error(status
));
292 asl_memory_close(global
.memory_db
);
293 global
.memory_db
= NULL
;
298 if (global
.dbtype
& DB_TYPE_MINI
)
300 status
= asl_mini_memory_save(global
.mini_db
, msg
, &msgid
);
301 if (status
!= ASL_STATUS_OK
)
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
;
309 status
= asl_mini_memory_save(global
.mini_db
, msg
, &msgid
);
310 if (status
!= ASL_STATUS_OK
)
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
;
320 pthread_mutex_unlock(&db_lock
);
325 disaster_message(asl_msg_t
*msg
)
332 if ((global
.dbtype
& DB_TYPE_MINI
) == 0)
334 if (global
.mini_db
== NULL
)
336 status
= asl_mini_memory_open(global
.db_mini_max
, &(global
.mini_db
));
337 if (status
!= ASL_STATUS_OK
) asldebug("asl_mini_memory_open: %s\n", asl_core_error(status
));
338 else asl_mini_memory_save(global
.mini_db
, msg
, &msgid
);
344 * Do a database search.
347 db_query(aslresponse query
, aslresponse
*res
, uint64_t startid
, int count
, int flags
, uint64_t *lastid
, int32_t ruid
, int32_t rgid
)
349 uint32_t status
, ucount
;
353 dir
= SEARCH_FORWARD
;
354 if (flags
& QUERY_FLAG_SEARCH_REVERSE
) dir
= SEARCH_BACKWARD
;
356 pthread_mutex_lock(&db_lock
);
358 status
= ASL_STATUS_FAILED
;
360 if (global
.dbtype
& DB_TYPE_MEMORY
) status
= asl_memory_match(global
.memory_db
, query
, res
, lastid
, startid
, ucount
, dir
, ruid
, rgid
);
361 else status
= asl_mini_memory_match(global
.mini_db
, query
, res
, lastid
, startid
, ucount
, dir
);
363 pthread_mutex_unlock(&db_lock
);
369 register_session(task_name_t task_name
, pid_t pid
)
371 mach_port_t previous
;
374 if (task_name
== MACH_PORT_NULL
) return;
375 if (global
.dead_session_port
== MACH_PORT_NULL
) return;
377 for (i
= 0; i
< client_tasks_count
; i
++) if (task_name
== client_tasks
[i
]) return;
379 if (client_tasks_count
== 0) client_tasks
= (task_name_t
*)calloc(1, sizeof(task_name_t
));
380 else client_tasks
= (task_name_t
*)reallocf(client_tasks
, (client_tasks_count
+ 1) * sizeof(task_name_t
));
382 if (client_tasks
== NULL
) return;
383 client_tasks
[client_tasks_count
] = task_name
;
384 client_tasks_count
++;
386 asldebug("register_session: %u PID %d\n", (unsigned int)task_name
, (int)pid
);
388 /* register for port death notification */
389 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
);
390 mach_port_deallocate(mach_task_self(), previous
);
392 asl_client_count_increment();
396 cancel_session(task_name_t task_name
)
400 for (i
= 0; (i
< client_tasks_count
) && (task_name
!= client_tasks
[i
]); i
++);
402 if (i
>= client_tasks_count
) return;
404 if (client_tasks_count
== 1)
408 client_tasks_count
= 0;
412 for (i
++; i
< client_tasks_count
; i
++) client_tasks
[i
-1] = client_tasks
[i
];
413 client_tasks_count
--;
414 client_tasks
= (task_name_t
*)reallocf(client_tasks
, client_tasks_count
* sizeof(task_name_t
));
417 asldebug("cancel_session: %u\n", (unsigned int)task_name
);
418 mach_port_destroy(mach_task_self(), task_name
);
419 asl_client_count_decrement();
423 * Receives messages on the "com.apple.system.logger" mach port.
424 * Services database search requests.
425 * Runs in it's own thread.
430 kern_return_t kstatus
;
431 asl_request_msg
*request
;
432 asl_reply_msg
*reply
;
434 uint32_t rbits
, sbits
;
435 uint32_t flags
, snooze
;
436 struct timeval now
, send_time
;
437 mach_dead_name_notification_t
*deadname
;
439 send_time
.tv_sec
= 0;
440 send_time
.tv_usec
= 0;
442 rqs
= sizeof(asl_request_msg
) + MAX_TRAILER_SIZE
;
443 rps
= sizeof(asl_reply_msg
) + MAX_TRAILER_SIZE
;
444 reply
= (asl_reply_msg
*)calloc(1, rps
);
445 if (reply
== NULL
) return;
447 rbits
= MACH_RCV_MSG
| MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT
) | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0
);
448 sbits
= MACH_SEND_MSG
| MACH_SEND_TIMEOUT
;
456 /* Check if it's time to post a database change notification */
457 if (send_time
.tv_sec
!= 0)
459 gettimeofday(&now
, NULL
);
460 if ((now
.tv_sec
> send_time
.tv_sec
) || ((now
.tv_sec
== send_time
.tv_sec
) && (now
.tv_usec
> send_time
.tv_usec
)))
462 notify_post(ASL_DB_NOTIFICATION
);
463 send_time
.tv_sec
= 0;
464 send_time
.tv_usec
= 0;
469 /* mach_msg timeout is in milliseconds */
470 snooze
= ((send_time
.tv_sec
- now
.tv_sec
) * 1000) + ((send_time
.tv_usec
- now
.tv_usec
) / 1000);
474 request
= (asl_request_msg
*)calloc(1, rqs
);
475 if (request
== NULL
) continue;
477 request
->head
.msgh_local_port
= global
.server_port
;
478 request
->head
.msgh_size
= rqs
;
480 memset(reply
, 0, rps
);
483 if (snooze
!= 0) flags
|= MACH_RCV_TIMEOUT
;
485 kstatus
= mach_msg(&(request
->head
), flags
, 0, rqs
, global
.listen_set
, snooze
, MACH_PORT_NULL
);
486 if (request
->head
.msgh_id
== SEND_NOTIFICATION
)
488 if (send_time
.tv_sec
== 0)
490 gettimeofday(&send_time
, NULL
);
491 send_time
.tv_sec
+= 1;
498 if (request
->head
.msgh_id
== MACH_NOTIFY_DEAD_NAME
)
500 deadname
= (mach_dead_name_notification_t
*)request
;
501 cancel_session(deadname
->not_port
);
506 kstatus
= asl_ipc_server(&(request
->head
), &(reply
->head
));
507 kstatus
= mach_msg(&(reply
->head
), sbits
, reply
->head
.msgh_size
, 0, MACH_PORT_NULL
, 10, MACH_PORT_NULL
);
508 if (kstatus
== MACH_SEND_INVALID_DEST
)
510 mach_port_destroy(mach_task_self(), request
->head
.msgh_remote_port
);
522 mach_msg_type_number_t requestCnt
,
527 mach_msg_type_number_t
*replyCnt
,
530 security_token_t
*token
535 char *out
, *vmbuffer
;
537 kern_return_t kstatus
;
539 *status
= ASL_STATUS_OK
;
540 query
= asl_list_from_string(request
);
541 vm_deallocate(mach_task_self(), (vm_address_t
)request
, requestCnt
);
544 *status
= db_query(query
, &res
, startid
, count
, flags
, lastid
, token
->val
[0], token
->val
[1]);
546 aslresponse_free(query
);
547 if (*status
!= ASL_STATUS_OK
)
549 if (res
!= NULL
) aslresponse_free(res
);
555 out
= asl_list_to_string((asl_search_result_t
*)res
, &outlen
);
556 aslresponse_free(res
);
558 if ((out
== NULL
) || (outlen
== 0)) return KERN_SUCCESS
;
560 kstatus
= vm_allocate(mach_task_self(), (vm_address_t
*)&vmbuffer
, outlen
, TRUE
);
561 if (kstatus
!= KERN_SUCCESS
)
567 memmove(vmbuffer
, out
, outlen
);
578 __asl_server_query_timeout
582 mach_msg_type_number_t requestCnt
,
587 mach_msg_type_number_t
*replyCnt
,
590 security_token_t
*token
593 return __asl_server_query(server
, request
, requestCnt
, startid
, count
, flags
, reply
, replyCnt
, lastid
, status
, token
);
601 mach_msg_type_number_t requestCnt
,
603 security_token_t
*token
614 mach_msg_type_number_t messageCnt
,
623 kern_return_t kstatus
;
624 mach_port_name_t client
;
626 asldebug("__asl_server_message: %s\n", (message
== NULL
) ? "NULL" : message
);
628 m
= asl_msg_from_string(message
);
629 vm_deallocate(mach_task_self(), (vm_address_t
)message
, messageCnt
);
634 audit_token_to_au32(token
, NULL
, &uid
, &gid
, NULL
, NULL
, &pid
, NULL
, NULL
);
636 client
= MACH_PORT_NULL
;
637 kstatus
= task_name_for_pid(mach_task_self(), pid
, &client
);
638 if (kstatus
== KERN_SUCCESS
) register_session(client
, pid
);
640 if (m
== NULL
) return KERN_SUCCESS
;
642 snprintf(tmp
, sizeof(tmp
), "%d", uid
);
643 asl_set(m
, ASL_KEY_UID
, tmp
);
645 snprintf(tmp
, sizeof(tmp
), "%d", gid
);
646 asl_set(m
, ASL_KEY_GID
, tmp
);
648 snprintf(tmp
, sizeof(tmp
), "%d", pid
);
649 asl_set(m
, ASL_KEY_PID
, tmp
);
651 /* verify and enqueue for processing */
652 asl_enqueue_message(SOURCE_ASL_MESSAGE
, NULL
, m
);