audit_worker.c revision 179178
1193323Sed/*
2193323Sed * Copyright (c) 1999-2005 Apple Computer, Inc.
3193323Sed * Copyright (c) 2006-2008 Robert N. M. Watson
4193323Sed * All rights reserved.
5193323Sed *
6193323Sed * Redistribution and use in source and binary forms, with or without
7193323Sed * modification, are permitted provided that the following conditions
8193323Sed * are met:
9193323Sed * 1.  Redistributions of source code must retain the above copyright
10193323Sed *     notice, this list of conditions and the following disclaimer.
11193323Sed * 2.  Redistributions in binary form must reproduce the above copyright
12193323Sed *     notice, this list of conditions and the following disclaimer in the
13193323Sed *     documentation and/or other materials provided with the distribution.
14193323Sed * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15193323Sed *     its contributors may be used to endorse or promote products derived
16193323Sed *     from this software without specific prior written permission.
17193323Sed *
18193323Sed * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND
19193323Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20193323Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21193323Sed * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR
22193323Sed * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23193323Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24193323Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25193323Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26193323Sed * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27193323Sed * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28193323Sed * POSSIBILITY OF SUCH DAMAGE.
29193323Sed */
30193323Sed
31193323Sed#include <sys/cdefs.h>
32193323Sed__FBSDID("$FreeBSD: head/sys/security/audit/audit_worker.c 179178 2008-05-21 13:59:05Z rwatson $");
33193323Sed
34193323Sed#include <sys/param.h>
35193323Sed#include <sys/condvar.h>
36193323Sed#include <sys/conf.h>
37193323Sed#include <sys/file.h>
38193323Sed#include <sys/filedesc.h>
39193323Sed#include <sys/fcntl.h>
40193323Sed#include <sys/ipc.h>
41193323Sed#include <sys/kernel.h>
42193323Sed#include <sys/kthread.h>
43193323Sed#include <sys/malloc.h>
44193323Sed#include <sys/mount.h>
45193323Sed#include <sys/namei.h>
46193323Sed#include <sys/proc.h>
47193323Sed#include <sys/queue.h>
48193323Sed#include <sys/socket.h>
49193323Sed#include <sys/socketvar.h>
50193323Sed#include <sys/protosw.h>
51193323Sed#include <sys/domain.h>
52193323Sed#include <sys/sx.h>
53193323Sed#include <sys/sysproto.h>
54193323Sed#include <sys/sysent.h>
55193323Sed#include <sys/systm.h>
56193323Sed#include <sys/ucred.h>
57193323Sed#include <sys/uio.h>
58193323Sed#include <sys/un.h>
59193323Sed#include <sys/unistd.h>
60193323Sed#include <sys/vnode.h>
61193323Sed
62193323Sed#include <bsm/audit.h>
63193323Sed#include <bsm/audit_internal.h>
64193323Sed#include <bsm/audit_kevents.h>
65193323Sed
66193323Sed#include <netinet/in.h>
67193323Sed#include <netinet/in_pcb.h>
68193323Sed
69193323Sed#include <security/audit/audit.h>
70193323Sed#include <security/audit/audit_private.h>
71193323Sed
72193323Sed#include <vm/uma.h>
73193323Sed
74193323Sed/*
75193323Sed * Worker thread that will schedule disk I/O, etc.
76193323Sed */
77193323Sedstatic struct proc		*audit_thread;
78193323Sed
79193323Sed/*
80193323Sed * audit_cred and audit_vp are the stored credential and vnode to use for
81193323Sed * active audit trail.  They are protected by audit_worker_sx, which will be
82193323Sed * held across all I/O and all rotation to prevent them from being replaced
83193323Sed * (rotated) while in use.  The audit_file_rotate_wait flag is set when the
84193323Sed * kernel has delivered a trigger to auditd to rotate the trail, and is
85193323Sed * cleared when the next rotation takes place.  It is also protected by
86193323Sed * audit_worker_sx.
87193323Sed */
88193323Sedstatic int		 audit_file_rotate_wait;
89193323Sedstatic struct sx	 audit_worker_sx;
90193323Sedstatic struct ucred	*audit_cred;
91193323Sedstatic struct vnode	*audit_vp;
92193323Sed
93193323Sed/*
94193323Sed * Write an audit record to a file, performed as the last stage after both
95193323Sed * preselection and BSM conversion.  Both space management and write failures
96193323Sed * are handled in this function.
97193323Sed *
98193323Sed * No attempt is made to deal with possible failure to deliver a trigger to
99193323Sed * the audit daemon, since the message is asynchronous anyway.
100193323Sed */
101193323Sedstatic void
102193323Sedaudit_record_write(struct vnode *vp, struct ucred *cred, void *data,
103193323Sed    size_t len)
104193323Sed{
105193323Sed	static struct timeval last_lowspace_trigger;
106193323Sed	static struct timeval last_fail;
107193323Sed	static int cur_lowspace_trigger;
108193323Sed	struct statfs *mnt_stat;
109193323Sed	int error, vfslocked;
110193323Sed	static int cur_fail;
111193323Sed	struct vattr vattr;
112193323Sed	long temp;
113193323Sed
114193323Sed	sx_assert(&audit_worker_sx, SA_LOCKED);	/* audit_file_rotate_wait. */
115193323Sed
116193323Sed	if (vp == NULL)
117193323Sed		return;
118193323Sed
119193323Sed 	mnt_stat = &vp->v_mount->mnt_stat;
120193323Sed	vfslocked = VFS_LOCK_GIANT(vp->v_mount);
121193323Sed
122193323Sed	/*
123193323Sed	 * First, gather statistics on the audit log file and file system so
124193323Sed	 * that we know how we're doing on space.  Consider failure of these
125193323Sed	 * operations to indicate a future inability to write to the file.
126193323Sed	 */
127193323Sed	error = VFS_STATFS(vp->v_mount, mnt_stat, curthread);
128193323Sed	if (error)
129193323Sed		goto fail;
130193323Sed	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
131193323Sed	error = VOP_GETATTR(vp, &vattr, cred, curthread);
132193323Sed	VOP_UNLOCK(vp, 0);
133193323Sed	if (error)
134193323Sed		goto fail;
135193323Sed	audit_fstat.af_currsz = vattr.va_size;
136193323Sed
137193323Sed	/*
138193323Sed	 * We handle four different space-related limits:
139193323Sed	 *
140193323Sed	 * - A fixed (hard) limit on the minimum free blocks we require on
141193323Sed	 *   the file system, and results in record loss, a trigger, and
142193323Sed	 *   possible fail stop due to violating invariants.
143193323Sed	 *
144193323Sed	 * - An administrative (soft) limit, which when fallen below, results
145193323Sed	 *   in the kernel notifying the audit daemon of low space.
146193323Sed	 *
147193323Sed	 * - An audit trail size limit, which when gone above, results in the
148193323Sed	 *   kernel notifying the audit daemon that rotation is desired.
149193323Sed	 *
150193323Sed	 * - The total depth of the kernel audit record exceeding free space,
151193323Sed	 *   which can lead to possible fail stop (with drain), in order to
152193323Sed	 *   prevent violating invariants.  Failure here doesn't halt
153193323Sed	 *   immediately, but prevents new records from being generated.
154193323Sed	 *
155193323Sed	 * Possibly, the last of these should be handled differently, always
156193323Sed	 * allowing a full queue to be lost, rather than trying to prevent
157193323Sed	 * loss.
158193323Sed	 *
159193323Sed	 * First, handle the hard limit, which generates a trigger and may
160193323Sed	 * fail stop.  This is handled in the same manner as ENOSPC from
161193323Sed	 * VOP_WRITE, and results in record loss.
162193323Sed	 */
163193323Sed	if (mnt_stat->f_bfree < AUDIT_HARD_LIMIT_FREE_BLOCKS) {
164193323Sed		error = ENOSPC;
165193323Sed		goto fail_enospc;
166193323Sed	}
167193323Sed
168193323Sed	/*
169193323Sed	 * Second, handle falling below the soft limit, if defined; we send
170193323Sed	 * the daemon a trigger and continue processing the record.  Triggers
171193323Sed	 * are limited to 1/sec.
172193323Sed	 */
173193323Sed	if (audit_qctrl.aq_minfree != 0) {
174193323Sed		temp = mnt_stat->f_blocks / (100 / audit_qctrl.aq_minfree);
175193323Sed		if (mnt_stat->f_bfree < temp) {
176193323Sed			if (ppsratecheck(&last_lowspace_trigger,
177193323Sed			    &cur_lowspace_trigger, 1)) {
178193323Sed				(void)audit_send_trigger(
179193323Sed				    AUDIT_TRIGGER_LOW_SPACE);
180193323Sed				printf("Warning: audit space low\n");
181193323Sed			}
182193323Sed		}
183193323Sed	}
184193323Sed
185193323Sed	/*
186193323Sed	 * If the current file is getting full, generate a rotation trigger
187193323Sed	 * to the daemon.  This is only approximate, which is fine as more
188193323Sed	 * records may be generated before the daemon rotates the file.
189193323Sed	 */
190193323Sed	if ((audit_fstat.af_filesz != 0) && (audit_file_rotate_wait == 0) &&
191193323Sed	    (vattr.va_size >= audit_fstat.af_filesz)) {
192193323Sed		sx_assert(&audit_worker_sx, SA_XLOCKED);
193193323Sed
194193323Sed		audit_file_rotate_wait = 1;
195193323Sed		(void)audit_send_trigger(AUDIT_TRIGGER_ROTATE_KERNEL);
196193323Sed	}
197193323Sed
198193323Sed	/*
199193323Sed	 * If the estimated amount of audit data in the audit event queue
200193323Sed	 * (plus records allocated but not yet queued) has reached the amount
201193323Sed	 * of free space on the disk, then we need to go into an audit fail
202193323Sed	 * stop state, in which we do not permit the allocation/committing of
203193323Sed	 * any new audit records.  We continue to process records but don't
204193323Sed	 * allow any activities that might generate new records.  In the
205193323Sed	 * future, we might want to detect when space is available again and
206193323Sed	 * allow operation to continue, but this behavior is sufficient to
207193323Sed	 * meet fail stop requirements in CAPP.
208193323Sed	 */
209193323Sed	if (audit_fail_stop) {
210193323Sed		if ((unsigned long)((audit_q_len + audit_pre_q_len + 1) *
211193323Sed		    MAX_AUDIT_RECORD_SIZE) / mnt_stat->f_bsize >=
212193323Sed		    (unsigned long)(mnt_stat->f_bfree)) {
213193323Sed			if (ppsratecheck(&last_fail, &cur_fail, 1))
214193323Sed				printf("audit_record_write: free space "
215193323Sed				    "below size of audit queue, failing "
216193323Sed				    "stop\n");
217193323Sed			audit_in_failure = 1;
218193323Sed		} else if (audit_in_failure) {
219193323Sed			/*
220193323Sed			 * Note: if we want to handle recovery, this is the
221193323Sed			 * spot to do it: unset audit_in_failure, and issue a
222193323Sed			 * wakeup on the cv.
223193323Sed			 */
224193323Sed		}
225193323Sed	}
226193323Sed
227193323Sed	error = vn_rdwr(UIO_WRITE, vp, data, len, (off_t)0, UIO_SYSSPACE,
228193323Sed	    IO_APPEND|IO_UNIT, cred, NULL, NULL, curthread);
229193323Sed	if (error == ENOSPC)
230193323Sed		goto fail_enospc;
231193323Sed	else if (error)
232193323Sed		goto fail;
233193323Sed
234193323Sed	/*
235193323Sed	 * Catch completion of a queue drain here; if we're draining and the
236193323Sed	 * queue is now empty, fail stop.  That audit_fail_stop is implicitly
237193323Sed	 * true, since audit_in_failure can only be set of audit_fail_stop is
238193323Sed	 * set.
239193323Sed	 *
240193323Sed	 * Note: if we handle recovery from audit_in_failure, then we need to
241193323Sed	 * make panic here conditional.
242193323Sed	 */
243193323Sed	if (audit_in_failure) {
244193323Sed		if (audit_q_len == 0 && audit_pre_q_len == 0) {
245193323Sed			VOP_LOCK(vp, LK_EXCLUSIVE | LK_RETRY);
246193323Sed			(void)VOP_FSYNC(vp, MNT_WAIT, curthread);
247193323Sed			VOP_UNLOCK(vp, 0);
248193323Sed			panic("Audit store overflow; record queue drained.");
249193323Sed		}
250193323Sed	}
251193323Sed
252193323Sed	VFS_UNLOCK_GIANT(vfslocked);
253193323Sed	return;
254193323Sed
255193323Sedfail_enospc:
256193323Sed	/*
257193323Sed	 * ENOSPC is considered a special case with respect to failures, as
258193323Sed	 * this can reflect either our preemptive detection of insufficient
259193323Sed	 * space, or ENOSPC returned by the vnode write call.
260193323Sed	 */
261193323Sed	if (audit_fail_stop) {
262193323Sed		VOP_LOCK(vp, LK_EXCLUSIVE | LK_RETRY);
263193323Sed		(void)VOP_FSYNC(vp, MNT_WAIT, curthread);
264193323Sed		VOP_UNLOCK(vp, 0);
265193323Sed		panic("Audit log space exhausted and fail-stop set.");
266193323Sed	}
267193323Sed	(void)audit_send_trigger(AUDIT_TRIGGER_NO_SPACE);
268193323Sed	audit_suspended = 1;
269193323Sed
270193323Sed	/* FALLTHROUGH */
271193323Sedfail:
272193323Sed	/*
273193323Sed	 * We have failed to write to the file, so the current record is
274193323Sed	 * lost, which may require an immediate system halt.
275193323Sed	 */
276193323Sed	if (audit_panic_on_write_fail) {
277193323Sed		VOP_LOCK(vp, LK_EXCLUSIVE | LK_RETRY);
278193323Sed		(void)VOP_FSYNC(vp, MNT_WAIT, curthread);
279193323Sed		VOP_UNLOCK(vp, 0);
280193323Sed		panic("audit_worker: write error %d\n", error);
281193323Sed	} else if (ppsratecheck(&last_fail, &cur_fail, 1))
282193323Sed		printf("audit_worker: write error %d\n", error);
283193323Sed	VFS_UNLOCK_GIANT(vfslocked);
284193323Sed}
285193323Sed
286193323Sed/*
287193323Sed * Given a kernel audit record, process as required.  Kernel audit records
288193323Sed * are converted to one, or possibly two, BSM records, depending on whether
289193323Sed * there is a user audit record present also.  Kernel records need be
290193323Sed * converted to BSM before they can be written out.  Both types will be
291193323Sed * written to disk, and audit pipes.
292193323Sed */
293193323Sedstatic void
294193323Sedaudit_worker_process_record(struct kaudit_record *ar)
295193323Sed{
296193323Sed	struct au_record *bsm;
297193323Sed	au_class_t class;
298193323Sed	au_event_t event;
299193323Sed	au_id_t auid;
300193323Sed	int error, sorf;
301193323Sed	int trail_locked;
302193323Sed
303193323Sed	/*
304193323Sed	 * We hold the audit_worker_sx lock over both writes, if there are
305193323Sed	 * two, so that the two records won't be split across a rotation and
306193323Sed	 * end up in two different trail files.
307193323Sed	 */
308193323Sed	if (((ar->k_ar_commit & AR_COMMIT_USER) &&
309193323Sed	    (ar->k_ar_commit & AR_PRESELECT_USER_TRAIL)) ||
310193323Sed	    (ar->k_ar_commit & AR_PRESELECT_TRAIL)) {
311193323Sed		sx_xlock(&audit_worker_sx);
312193323Sed		trail_locked = 1;
313193323Sed	} else
314193323Sed		trail_locked = 0;
315193323Sed
316193323Sed	/*
317193323Sed	 * First, handle the user record, if any: commit to the system trail
318193323Sed	 * and audit pipes as selected.
319193323Sed	 */
320193323Sed	if ((ar->k_ar_commit & AR_COMMIT_USER) &&
321193323Sed	    (ar->k_ar_commit & AR_PRESELECT_USER_TRAIL)) {
322193323Sed		sx_assert(&audit_worker_sx, SA_XLOCKED);
323193323Sed		audit_record_write(audit_vp, audit_cred, ar->k_udata,
324193323Sed		    ar->k_ulen);
325193323Sed	}
326193323Sed
327193323Sed	if ((ar->k_ar_commit & AR_COMMIT_USER) &&
328193323Sed	    (ar->k_ar_commit & AR_PRESELECT_USER_PIPE))
329193323Sed		audit_pipe_submit_user(ar->k_udata, ar->k_ulen);
330193323Sed
331193323Sed	if (!(ar->k_ar_commit & AR_COMMIT_KERNEL) ||
332193323Sed	    ((ar->k_ar_commit & AR_PRESELECT_PIPE) == 0 &&
333193323Sed	    (ar->k_ar_commit & AR_PRESELECT_TRAIL) == 0))
334193323Sed		goto out;
335193323Sed
336193323Sed	auid = ar->k_ar.ar_subj_auid;
337193323Sed	event = ar->k_ar.ar_event;
338193323Sed	class = au_event_class(event);
339193323Sed	if (ar->k_ar.ar_errno == 0)
340193323Sed		sorf = AU_PRS_SUCCESS;
341193323Sed	else
342193323Sed		sorf = AU_PRS_FAILURE;
343193323Sed
344193323Sed	error = kaudit_to_bsm(ar, &bsm);
345193323Sed	switch (error) {
346193323Sed	case BSM_NOAUDIT:
347193323Sed		goto out;
348193323Sed
349193323Sed	case BSM_FAILURE:
350193323Sed		printf("audit_worker_process_record: BSM_FAILURE\n");
351193323Sed		goto out;
352193323Sed
353193323Sed	case BSM_SUCCESS:
354193323Sed		break;
355193323Sed
356193323Sed	default:
357193323Sed		panic("kaudit_to_bsm returned %d", error);
358193323Sed	}
359193323Sed
360193323Sed	if (ar->k_ar_commit & AR_PRESELECT_TRAIL) {
361193323Sed		sx_assert(&audit_worker_sx, SA_XLOCKED);
362193323Sed		audit_record_write(audit_vp, audit_cred, bsm->data, bsm->len);
363193323Sed	}
364193323Sed
365193323Sed	if (ar->k_ar_commit & AR_PRESELECT_PIPE)
366193323Sed		audit_pipe_submit(auid, event, class, sorf,
367193323Sed		    ar->k_ar_commit & AR_PRESELECT_TRAIL, bsm->data,
368193323Sed		    bsm->len);
369193323Sed
370193323Sed	kau_free(bsm);
371193323Sedout:
372193323Sed	if (trail_locked)
373193323Sed		sx_xunlock(&audit_worker_sx);
374193323Sed}
375193323Sed
376193323Sed/*
377193323Sed * The audit_worker thread is responsible for watching the event queue,
378193323Sed * dequeueing records, converting them to BSM format, and committing them to
379193323Sed * disk.  In order to minimize lock thrashing, records are dequeued in sets
380193323Sed * to a thread-local work queue.
381193323Sed *
382193323Sed * Note: this means that the effect bound on the size of the pending record
383193323Sed * queue is 2x the length of the global queue.
384193323Sed */
385193323Sedstatic void
386193323Sedaudit_worker(void *arg)
387193323Sed{
388193323Sed	struct kaudit_queue ar_worklist;
389193323Sed	struct kaudit_record *ar;
390193323Sed	int lowater_signal;
391193323Sed
392193323Sed	TAILQ_INIT(&ar_worklist);
393193323Sed	mtx_lock(&audit_mtx);
394193323Sed	while (1) {
395193323Sed		mtx_assert(&audit_mtx, MA_OWNED);
396193323Sed
397193323Sed		/*
398193323Sed		 * Wait for a record.
399193323Sed		 */
400193323Sed		while (TAILQ_EMPTY(&audit_q))
401193323Sed			cv_wait(&audit_worker_cv, &audit_mtx);
402193323Sed
403193323Sed		/*
404193323Sed		 * If there are records in the global audit record queue,
405193323Sed		 * transfer them to a thread-local queue and process them
406193323Sed		 * one by one.  If we cross the low watermark threshold,
407193323Sed		 * signal any waiting processes that they may wake up and
408193323Sed		 * continue generating records.
409193323Sed		 */
410193323Sed		lowater_signal = 0;
411193323Sed		while ((ar = TAILQ_FIRST(&audit_q))) {
412193323Sed			TAILQ_REMOVE(&audit_q, ar, k_q);
413193323Sed			audit_q_len--;
414193323Sed			if (audit_q_len == audit_qctrl.aq_lowater)
415193323Sed				lowater_signal++;
416193323Sed			TAILQ_INSERT_TAIL(&ar_worklist, ar, k_q);
417193323Sed		}
418193323Sed		if (lowater_signal)
419193323Sed			cv_broadcast(&audit_watermark_cv);
420193323Sed
421193323Sed		mtx_unlock(&audit_mtx);
422193323Sed		while ((ar = TAILQ_FIRST(&ar_worklist))) {
423193323Sed			TAILQ_REMOVE(&ar_worklist, ar, k_q);
424193323Sed			audit_worker_process_record(ar);
425193323Sed			audit_free(ar);
426193323Sed		}
427193323Sed		mtx_lock(&audit_mtx);
428193323Sed	}
429193323Sed}
430193323Sed
431193323Sed/*
432193323Sed * audit_rotate_vnode() is called by a user or kernel thread to configure or
433193323Sed * de-configure auditing on a vnode.  The arguments are the replacement
434193323Sed * credential (referenced) and vnode (referenced and opened) to substitute
435193323Sed * for the current credential and vnode, if any.  If either is set to NULL,
436193323Sed * both should be NULL, and this is used to indicate that audit is being
437193323Sed * disabled.  Any previous cred/vnode will be closed and freed.  We re-enable
438193323Sed * generating rotation requests to auditd.
439193323Sed */
440193323Sedvoid
441193323Sedaudit_rotate_vnode(struct ucred *cred, struct vnode *vp)
442193323Sed{
443193323Sed	struct ucred *old_audit_cred;
444193323Sed	struct vnode *old_audit_vp;
445193323Sed	int vfslocked;
446193323Sed
447193323Sed	KASSERT((cred != NULL && vp != NULL) || (cred == NULL && vp == NULL),
448193323Sed	    ("audit_rotate_vnode: cred %p vp %p", cred, vp));
449193323Sed
450193323Sed	/*
451193323Sed	 * Rotate the vnode/cred, and clear the rotate flag so that we will
452193323Sed	 * send a rotate trigger if the new file fills.
453193323Sed	 */
454193323Sed	sx_xlock(&audit_worker_sx);
455193323Sed	old_audit_cred = audit_cred;
456193323Sed	old_audit_vp = audit_vp;
457193323Sed	audit_cred = cred;
458193323Sed	audit_vp = vp;
459193323Sed	audit_file_rotate_wait = 0;
460193323Sed	audit_enabled = (audit_vp != NULL);
461193323Sed	sx_xunlock(&audit_worker_sx);
462193323Sed
463193323Sed	/*
464193323Sed	 * If there was an old vnode/credential, close and free.
465193323Sed	 */
466193323Sed	if (old_audit_vp != NULL) {
467193323Sed		vfslocked = VFS_LOCK_GIANT(old_audit_vp->v_mount);
468193323Sed		vn_close(old_audit_vp, AUDIT_CLOSE_FLAGS, old_audit_cred,
469193323Sed		    curthread);
470193323Sed		VFS_UNLOCK_GIANT(vfslocked);
471193323Sed		crfree(old_audit_cred);
472193323Sed	}
473193323Sed}
474193323Sed
475193323Sedvoid
476193323Sedaudit_worker_init(void)
477193323Sed{
478193323Sed	int error;
479193323Sed
480193323Sed	sx_init(&audit_worker_sx, "audit_worker_sx");
481193323Sed	error = kproc_create(audit_worker, NULL, &audit_thread, RFHIGHPID,
482193323Sed	    0, "audit");
483193323Sed	if (error)
484193323Sed		panic("audit_worker_init: kproc_create returned %d", error);
485193323Sed}
486193323Sed