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
);