8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / lib / auditd_plugins / remote / audit_remote.c
blob9224022daad52556ab5fa0e4dc09f12bdacbcfd8
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24 * send audit records to remote host
29 * auditd_plugin_open(), auditd_plugin() and auditd_plugin_close()
30 * implement a replaceable library for use by auditd; they are a
31 * project private interface and may change without notice.
34 #include <arpa/inet.h>
35 #include <assert.h>
36 #include <audit_plugin.h>
37 #include <bsm/audit.h>
38 #include <bsm/audit_record.h>
39 #include <bsm/libbsm.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <gssapi/gssapi.h>
43 #include <libintl.h>
44 #include <netdb.h>
45 #include <pthread.h>
46 #include <rpc/rpcsec_gss.h>
47 #include <secdb.h>
48 #include <signal.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <strings.h>
53 #include <ctype.h>
54 #include <sys/param.h>
55 #include <sys/socket.h>
56 #include <sys/types.h>
57 #include <unistd.h>
58 #include <poll.h>
60 #include "audit_remote.h"
62 #define DEFAULT_TIMEOUT 5 /* default connection timeout (in secs) */
63 #define NOSUCCESS_DELAY 20 /* unsuccessful delivery to all p_hosts */
65 #define FL_SET B_TRUE /* set_fdfl(): set the flag */
66 #define FL_UNSET B_FALSE /* set_fdfl(): unset the flag */
68 static int nosuccess_cnt; /* unsuccessful delivery counter */
70 static int retries; /* connection retries */
71 int timeout; /* connection timeout */
72 static int timeout_p_timeout; /* p_timeout attr storage */
74 /* semi-exponential timeout back off; x .. attempts, y .. timeout */
75 #define BOFF_TIMEOUT(x, y) (x < 3 ? y * 2 * x : y * 8)
77 /* general plugin lock */
78 pthread_mutex_t plugin_mutex = PTHREAD_MUTEX_INITIALIZER;
80 static struct hostlist_s *current_host;
81 static struct hostlist_s *hosts;
82 static struct hostlist_s *hosts_prev;
84 extern struct transq_hdr_s transq_hdr;
85 static long transq_count_max;
86 extern pthread_mutex_t transq_lock;
88 extern pthread_t recv_tid;
90 extern boolean_t notify_pipe_ready;
91 extern int notify_pipe[2];
93 #if DEBUG
94 FILE *dfile; /* debug file */
95 #endif
98 * set_transq_count_max() - sets the transq_count_max value based on kernel
99 * audit queue high water mark. This is backup solution for a case, when the
100 * the default qsize zero value is (intentionally) set in the audit_remote(5)
101 * plugin configuration.
103 static auditd_rc_t
104 set_transq_count_max()
106 struct au_qctrl qctrl;
108 if (auditon(A_GETQCTRL, (caddr_t)&qctrl, 0) != -1) {
109 transq_count_max = qctrl.aq_hiwater;
110 DPRINT((dfile, "Transmission queue max length set to %ld\n",
111 transq_count_max));
112 return (AUDITD_SUCCESS);
115 DPRINT((dfile, "Setting the transmission queue max length failed\n"));
116 return (AUDITD_RETRY);
120 * get_port_default() - set the default port number; note, that "solaris-audit"
121 * used below in the code is the IANA assigned service name for the secure
122 * remote solaris audit logging.
124 static auditd_rc_t
125 get_port_default(int *port_default)
128 struct servent serventry;
129 char serventry_buf[1024];
131 if (getservbyname_r("solaris-audit", "tcp", &serventry,
132 (char *)&serventry_buf, sizeof (serventry_buf)) == NULL) {
133 DPRINT((dfile, "unable to get default port number\n"));
134 #if DEBUG
135 if (errno == ERANGE) {
136 DPRINT((dfile, "low on buffer\n"));
138 #endif
139 return (AUDITD_INVALID);
141 *port_default = ntohs(serventry.s_port);
142 DPRINT((dfile, "default port: %d\n", *port_default));
144 return (AUDITD_SUCCESS);
148 * trim_me() - trims the white space characters around the specified string.
149 * Inputs - pointer to the beginning of the string (str_ptr); returns - pointer
150 * to the trimmed string. Function returns NULL pointer in case of received
151 * empty string, NULL pointer or in case the pointed string consists of white
152 * space characters only.
154 static char *
155 trim_me(char *str_ptr) {
157 char *str_end;
159 if (str_ptr == NULL || *str_ptr == '\0') {
160 return (NULL);
163 while (isspace(*str_ptr)) {
164 str_ptr++;
166 if (*str_ptr == '\0') {
167 return (NULL);
170 str_end = str_ptr + strlen(str_ptr);
172 while (str_end > str_ptr && isspace(str_end[-1])) {
173 str_end--;
175 *str_end = '\0';
177 return (str_ptr);
181 * Frees host list - should be called while keeping auditd_mutex.
183 static void
184 freehostlist(hostlist_t **hostlist_ptr)
186 hostlist_t *h, *n;
188 h = *hostlist_ptr;
190 while (h != NULL) {
191 n = h->next_host;
192 freehostent(h->host);
193 free(h);
194 h = n;
196 *hostlist_ptr = NULL;
200 * parsehosts() end parses the host string (hosts_str)
202 static auditd_rc_t
203 parsehosts(char *hosts_str, char **error)
205 char *hostportmech, *hpm;
206 char *hostname;
207 char *port_str;
208 char *mech_str;
209 int port;
210 int port_default = -1;
211 gss_OID mech_oid;
212 char *lasts_hpm;
213 hostlist_t *lasthost = NULL;
214 hostlist_t *hosts_new = NULL;
215 hostlist_t *newhost;
216 struct hostent *hostentry;
217 int error_num;
218 int rc;
219 #if DEBUG
220 char addr_buf[INET6_ADDRSTRLEN];
221 int num_of_hosts = 0;
222 #endif
224 DPRINT((dfile, "parsing %s\n", hosts_str));
225 while ((hostportmech = strtok_r(hosts_str, ",", &lasts_hpm)) != NULL) {
227 hosts_str = NULL;
228 hostname = NULL;
229 port_str = NULL;
230 port = port_default;
231 mech_str = NULL;
232 mech_oid = GSS_C_NO_OID;
234 DPRINT((dfile, "parsing host:port:mech %s\n", hostportmech));
236 if (strncmp(hostportmech, ":", 1 == 0)) { /* ":port:" case */
237 *error = strdup(gettext("no hostname specified"));
238 return (AUDITD_INVALID);
241 /* parse single host:port:mech target */
242 while ((hpm = strsep(&hostportmech, ":")) != NULL) {
244 if (hostname == NULL) {
245 hostname = hpm;
246 continue;
248 if (port_str == NULL) {
249 port_str = hpm;
250 continue;
252 if (mech_str == NULL) {
253 mech_str = hpm;
254 continue;
257 /* too many colons in the hostportmech string */
258 *error = strdup(gettext("invalid host:port:mech "
259 "specification"));
260 return (AUDITD_INVALID);
263 if (hostname == NULL || *hostname == '\0') {
264 *error = strdup(gettext("invalid hostname "
265 "specification"));
266 return (AUDITD_INVALID);
269 /* trim hostname */
270 hostname = trim_me(hostname);
271 if (hostname == NULL || *hostname == '\0') {
272 *error = strdup(gettext("empty hostname "
273 "specification"));
274 return (AUDITD_INVALID);
277 DPRINT((dfile, "resolving address for %s\n", hostname));
279 hostentry = getipnodebyname(hostname, AF_INET6, 0, &error_num);
280 if (!hostentry) {
281 hostentry = getipnodebyname(hostname, AF_INET, 0,
282 &error_num);
284 if (!hostentry) {
285 if (error_num == TRY_AGAIN) {
286 *error = strdup(gettext("host not found, "
287 "try later"));
288 return (AUDITD_RETRY);
289 } else {
290 *error = strdup(gettext("host not found"));
291 return (AUDITD_INVALID);
294 DPRINT((dfile, "hostentry: h_name=%s, addr_len=%d, addr=%s\n",
295 hostentry->h_name, hostentry->h_length,
296 inet_ntop(hostentry->h_addrtype,
297 hostentry->h_addr_list[0], addr_buf,
298 INET6_ADDRSTRLEN)));
300 /* trim port */
301 port_str = trim_me(port_str);
302 if (port_str == NULL || *port_str == '\0') {
303 if (port_default == -1 &&
304 (rc = get_port_default(&port_default))
305 != AUDITD_SUCCESS) {
306 *error = strdup(gettext(
307 "unable to get default port number"));
308 return (rc);
310 port = port_default;
311 DPRINT((dfile, "port: %d (default)\n", port));
312 } else {
313 errno = 0;
314 port = atoi(port_str);
315 if (errno != 0 || port < 1 || port > USHRT_MAX) {
316 *error = strdup(gettext("invalid port number"));
317 return (AUDITD_INVALID);
319 DPRINT((dfile, "port: %d\n", port));
322 /* trim mechanism */
323 mech_str = trim_me(mech_str);
324 if (mech_str != NULL && *mech_str != '\0') {
325 if (rpc_gss_mech_to_oid(mech_str, &mech_oid) != TRUE) {
326 *error = strdup(gettext("unknown mechanism"));
327 return (AUDITD_INVALID);
329 DPRINT((dfile, "mechanism: %s\n", mech_str));
330 #if DEBUG
331 } else {
332 DPRINT((dfile, "mechanism: null (default)\n"));
333 #endif
336 /* add this host to host list */
337 newhost = malloc(sizeof (hostlist_t));
338 if (newhost == NULL) {
339 *error = strdup(gettext("no memory"));
340 return (AUDITD_NO_MEMORY);
342 newhost->host = hostentry;
343 newhost->port = htons(port);
344 newhost->mech = mech_oid;
345 newhost->next_host = NULL;
346 if (lasthost != NULL) {
347 lasthost->next_host = newhost;
348 lasthost = lasthost->next_host;
349 } else {
350 lasthost = newhost;
351 hosts_new = newhost;
353 #if DEBUG
354 num_of_hosts++;
355 #endif
358 (void) pthread_mutex_lock(&plugin_mutex);
359 if (hosts_prev == NULL) {
360 hosts_prev = hosts;
362 hosts = hosts_new;
363 current_host = hosts;
364 (void) pthread_mutex_unlock(&plugin_mutex);
366 DPRINT((dfile, "Configured %d hosts.\n", num_of_hosts));
368 return (AUDITD_SUCCESS);
372 #if DEBUG
373 static char *
374 auditd_message(auditd_rc_t msg_code) {
375 char *rc_msg;
377 switch (msg_code) {
378 case AUDITD_SUCCESS:
379 rc_msg = strdup("ok");
380 break;
381 case AUDITD_RETRY:
382 rc_msg = strdup("retry after a delay");
383 break;
384 case AUDITD_NO_MEMORY:
385 rc_msg = strdup("can't allocate memory");
386 break;
387 case AUDITD_INVALID:
388 rc_msg = strdup("bad input");
389 break;
390 case AUDITD_COMM_FAIL:
391 rc_msg = strdup("communications failure");
392 break;
393 case AUDITD_FATAL:
394 rc_msg = strdup("other error");
395 break;
396 case AUDITD_FAIL:
397 rc_msg = strdup("other non-fatal error");
398 break;
400 return (rc_msg);
402 #endif
405 * rsn_to_msg() - translation of the reason of closure identifier to the more
406 * human readable/understandable form.
408 static char *
409 rsn_to_msg(close_rsn_t reason)
411 char *rc_msg;
413 switch (reason) {
414 case RSN_UNDEFINED:
415 rc_msg = strdup(gettext("not defined reason of failure"));
416 break;
417 case RSN_INIT_POLL:
418 rc_msg = strdup(gettext("poll() initialization failed"));
419 break;
420 case RSN_TOK_RECV_FAILED:
421 rc_msg = strdup(gettext("token receiving failed"));
422 break;
423 case RSN_TOK_TOO_BIG:
424 rc_msg = strdup(gettext("unacceptable token size"));
425 break;
426 case RSN_TOK_UNVERIFIABLE:
427 rc_msg = strdup(gettext("received unverifiable token"));
428 break;
429 case RSN_SOCKET_CLOSE:
430 rc_msg = strdup(gettext("closed socket"));
431 break;
432 case RSN_SOCKET_CREATE:
433 rc_msg = strdup(gettext("socket creation failed"));
434 break;
435 case RSN_CONNECTION_CREATE:
436 rc_msg = strdup(gettext("connection creation failed"));
437 break;
438 case RSN_PROTOCOL_NEGOTIATE:
439 rc_msg = strdup(gettext("protocol negotiation failed"));
440 break;
441 case RSN_GSS_CTX_ESTABLISH:
442 rc_msg = strdup(gettext("context establishing failed"));
443 break;
444 case RSN_GSS_CTX_EXP:
445 rc_msg = strdup(gettext("context expired"));
446 break;
447 case RSN_UNKNOWN_AF:
448 rc_msg = strdup(gettext("unknown address family"));
449 break;
450 case RSN_MEMORY_ALLOCATE:
451 rc_msg = strdup(gettext("memory allocation failed"));
452 break;
453 default: /* RSN_OTHER_ERR */
454 rc_msg = strdup(gettext("other, not classified error"));
455 break;
457 return (rc_msg);
461 * set_fdfl() - based on set_fl (FL_SET/FL_UNSET) un/sets the fl flag associated
462 * with fd file descriptor.
464 static boolean_t
465 set_fdfl(int fd, int fl, boolean_t set_fl)
467 int flags;
469 /* power of two test - only single bit flags are allowed */
470 if (!fl || (fl & (fl-1))) {
471 DPRINT((dfile, "incorrect flag - %d isn't power of two\n", fl));
472 return (B_FALSE);
475 if ((flags = fcntl(fd, F_GETFL, 0)) < 0) {
476 DPRINT((dfile, "cannot get file descriptor flags\n"));
477 return (B_FALSE);
480 if (set_fl) { /* set the fl flag */
481 if (flags & fl) {
482 return (B_TRUE);
485 flags |= fl;
487 } else { /* unset the fl flag */
488 if (~flags & fl) {
489 return (B_TRUE);
492 flags &= ~fl;
495 if (fcntl(fd, F_SETFL, flags) == -1) {
496 DPRINT((dfile, "cannot %s file descriptor flags\n",
497 (set_fl ? "set" : "unset")));
498 return (B_FALSE);
501 DPRINT((dfile, "fd: %d - flag: 0%o was %s\n", fd, fl,
502 (set_fl ? "set" : "unset")));
503 return (B_TRUE);
508 * create_notify_pipe() - creates the notification pipe. Function returns
509 * B_TRUE/B_FALSE on success/failure.
511 static boolean_t
512 create_notify_pipe(int *notify_pipe, char **error)
515 if (pipe(notify_pipe) < 0) {
516 DPRINT((dfile, "Cannot create notify pipe: %s\n",
517 strerror(errno)));
518 *error = strdup(gettext("failed to create notification pipe"));
519 return (B_FALSE);
520 } else {
521 DPRINT((dfile, "Pipe created in:%d out:%d\n", notify_pipe[0],
522 notify_pipe[1]));
523 /* make (only) the pipe "in" end nonblocking */
524 if (!set_fdfl(notify_pipe[0], O_NONBLOCK, FL_UNSET) ||
525 !set_fdfl(notify_pipe[1], O_NONBLOCK, FL_SET)) {
526 DPRINT((dfile, "Cannot prepare blocking scheme on top "
527 "of the notification pipe: %s\n", strerror(errno)));
528 (void) close(notify_pipe[0]);
529 (void) close(notify_pipe[1]);
531 *error = strdup(gettext("failed to prepare blocking "
532 "scheme on top of the notification pipe"));
533 return (B_FALSE);
537 return (B_TRUE);
542 * auditd_plugin() sends a record via a tcp connection.
544 * Operation:
545 * - 1 tcp connection opened at a time, referenced by current_host->sockfd
546 * - tries to (open and) send a record to the current_host where its address
547 * is taken from the first hostent h_addr_list entry
548 * - if connection times out, tries second host
549 * - if all hosts where tried tries again for retries number of times
550 * - if everything fails, it bails out with AUDITD_RETRY
552 * Note, that space on stack allocated for any error message returned along
553 * with AUDITD_RETRY is subsequently freed by auditd.
556 auditd_rc_t
557 auditd_plugin(const char *input, size_t in_len, uint64_t sequence, char **error)
559 int rc = AUDITD_FAIL;
560 int send_record_rc = SEND_RECORD_FAIL;
561 hostlist_t *start_host;
562 int attempts = 0;
563 char *ext_error; /* extended error string */
564 close_rsn_t err_rsn = RSN_UNDEFINED;
565 char *rsn_msg;
567 #if DEBUG
568 char *rc_msg;
569 static uint64_t last_sequence = 0;
571 if ((last_sequence > 0) && (sequence != last_sequence + 1)) {
572 DPRINT((dfile, "audit_remote: buffer sequence=%llu "
573 "but prev=%llu\n", sequence, last_sequence));
575 last_sequence = sequence;
577 DPRINT((dfile, "audit_remote: input seq=%llu, len=%d\n",
578 sequence, in_len));
579 #endif
581 (void) pthread_mutex_lock(&transq_lock);
583 if (transq_hdr.count == transq_count_max) {
584 DPRINT((dfile, "Transmission queue is full (%ld)\n",
585 transq_hdr.count));
586 (void) pthread_mutex_unlock(&transq_lock);
587 *error = strdup(gettext("retransmission queue is full"));
588 return (AUDITD_RETRY);
590 (void) pthread_mutex_unlock(&transq_lock);
593 (void) pthread_mutex_lock(&plugin_mutex);
595 /* cycle over the hosts and possibly deliver the record */
596 start_host = current_host;
597 while (rc != AUDITD_SUCCESS) {
598 DPRINT((dfile, "Trying to send record to %s [attempt:%d/%d]\n",
599 current_host->host->h_name, attempts + 1, retries));
601 send_record_rc = send_record(current_host, input, in_len,
602 sequence, &err_rsn);
603 DPRINT((dfile, "send_record() returned %d - ", send_record_rc));
605 switch (send_record_rc) {
606 case SEND_RECORD_SUCCESS:
607 DPRINT((dfile, "success\n"));
608 nosuccess_cnt = 0;
609 rc = AUDITD_SUCCESS;
610 if (hosts_prev != NULL) {
611 freehostlist(&hosts_prev);
612 DPRINT((dfile, "stale host list freed\n"));
614 break;
615 case SEND_RECORD_NEXT:
616 DPRINT((dfile, "retry the same host: %s (penalty) "
617 "rsn:%d\n", current_host->host->h_name, err_rsn));
618 attempts++;
619 break;
620 case SEND_RECORD_RETRY:
621 DPRINT((dfile, "retry the same host: %s (no penalty) "
622 "rsn:%d\n", current_host->host->h_name, err_rsn));
623 break;
626 if (send_record_rc == SEND_RECORD_NEXT) {
628 /* warn about unsuccessful auditd record delivery */
629 rsn_msg = rsn_to_msg(err_rsn);
630 (void) asprintf(&ext_error,
631 "retry %d connection %s:%d %s", attempts + 1,
632 current_host->host->h_name,
633 ntohs(current_host->port), rsn_msg);
634 if (ext_error == NULL) {
635 free(rsn_msg);
636 *error = strdup(gettext("no memory"));
637 rc = AUDITD_NO_MEMORY;
638 break;
640 __audit_dowarn2("plugin", "audit_remote.so", "retry",
641 ext_error, attempts + 1);
642 free(rsn_msg);
643 free(ext_error);
645 if (attempts < retries) {
646 /* semi-exponential timeout back off */
647 timeout = BOFF_TIMEOUT(attempts, timeout);
648 DPRINT((dfile, "New timeout=%d\n", timeout));
649 } else {
650 /* get next host */
651 current_host = current_host->next_host;
652 if (current_host == NULL) {
653 current_host = hosts;
655 timeout = timeout_p_timeout;
656 DPRINT((dfile, "New timeout=%d\n", timeout));
657 attempts = 0;
660 /* one cycle finished */
661 if (current_host == start_host && attempts == 0) {
662 nosuccess_cnt++;
663 (void) asprintf(&ext_error, "all hosts defined "
664 "as p_hosts were tried to deliver "
665 "the audit record to with no success "
666 "- sleeping for %d seconds",
667 NOSUCCESS_DELAY);
668 if (ext_error == NULL) {
669 *error = strdup(gettext("no memory"));
670 rc = AUDITD_NO_MEMORY;
671 break;
673 __audit_dowarn2("plugin", "audit_remote.so",
674 "retry", ext_error, nosuccess_cnt);
675 free(ext_error);
676 (void) sleep(NOSUCCESS_DELAY);
679 } /* if (send_record_rc == SEND_RECORD_NEXT) */
681 err_rsn = RSN_UNDEFINED;
683 } /* while (rc != AUDITD_SUCCESS) */
685 (void) pthread_mutex_unlock(&plugin_mutex);
687 #if DEBUG
688 rc_msg = auditd_message(rc);
689 DPRINT((dfile, "audit_remote: returning: %s\n", rc_msg));
690 free(rc_msg);
691 #endif
693 return (rc);
697 * auditd_plugin_open() may be called multiple times; on initial open or
698 * `audit -s`, then kvlist != NULL; on `audit -n`, then kvlist == NULL.
699 * For more information see audit(1M).
701 * Note, that space on stack allocated for any error message returned along
702 * with AUDITD_RETRY is subsequently freed by auditd.
705 auditd_rc_t
706 auditd_plugin_open(const kva_t *kvlist, char **ret_list, char **error)
708 kva_t *kv;
709 char *val_str;
710 int val;
711 long val_l;
712 int rc = 0;
714 *error = NULL;
715 *ret_list = NULL;
716 kv = (kva_t *)kvlist;
718 #if DEBUG
719 dfile = __auditd_debug_file_open();
720 #endif
722 /* initial open or audit -s */
723 if (kvlist != NULL) {
724 DPRINT((dfile, "Action: initial open or `audit -s`\n"));
725 val_str = kva_match(kv, "p_timeout");
726 if (val_str == NULL) {
727 *error = strdup(
728 gettext("p_timeout attribute not found"));
729 return (AUDITD_RETRY);
731 DPRINT((dfile, "val_str=%s\n", val_str));
732 errno = 0;
733 val = atoi(val_str);
734 if (errno == 0 && val >= 1) {
735 timeout_p_timeout = val;
736 timeout = val;
737 } else {
738 timeout_p_timeout = DEFAULT_TIMEOUT;
739 timeout = timeout_p_timeout;
740 DPRINT((dfile, "p_timeout set to default value: %d\n",
741 timeout));
744 val_str = kva_match(kv, "p_retries");
745 if (val_str == NULL) {
746 *error = strdup(
747 gettext("p_retries attribute not found"));
748 return (AUDITD_RETRY);
750 DPRINT((dfile, "val_str=%s\n", val_str));
751 errno = 0;
752 val = atoi(val_str);
753 if (errno == 0 && val >= 0) {
754 retries = val;
757 val_str = kva_match(kv, "qsize");
758 if (val_str == NULL) {
759 *error = strdup(gettext("qsize attribute not found"));
760 return (AUDITD_RETRY);
762 DPRINT((dfile, "qsize=%s\n", val_str));
763 errno = 0;
764 val_l = atol(val_str);
765 if (errno == 0 && val_l >= 0) {
766 transq_count_max = val_l;
768 if (transq_count_max == 0 &&
769 (rc = set_transq_count_max()) != AUDITD_SUCCESS) {
770 *error = strdup(gettext("cannot get kernel "
771 "auditd queue high water mark\n"));
772 return (rc);
774 DPRINT((dfile, "timeout=%d, retries=%d, transq_count_max=%ld\n",
775 timeout, retries, transq_count_max));
777 val_str = kva_match(kv, "p_hosts");
778 if (val_str == NULL) {
779 *error = strdup(gettext("no hosts configured"));
780 return (AUDITD_RETRY);
782 if ((rc = parsehosts(val_str, error)) != AUDITD_SUCCESS) {
783 return (rc);
786 /* create the notification pipe towards the receiving thread */
787 if (!notify_pipe_ready) {
788 if (create_notify_pipe(notify_pipe, error)) {
789 notify_pipe_ready = B_TRUE;
790 } else {
791 return (AUDITD_RETRY);
795 #if DEBUG
796 } else { /* audit -n */
797 DPRINT((dfile, "Action: `audit -n`\n"));
798 #endif
801 return (AUDITD_SUCCESS);
805 * auditd_plugin_close() performs shutdown operations. The return values are
806 * used by auditd to output warnings via the audit_warn(1M) script and the
807 * string returned via "error_text", is passed to audit_warn.
809 * Note, that space on stack allocated for any error message returned along
810 * with AUDITD_RETRY is subsequently freed by auditd.
813 auditd_rc_t
814 auditd_plugin_close(char **error)
816 reset_transport(DO_EXIT, DO_SYNC);
817 if (pthread_join(recv_tid, NULL) != 0) {
818 *error = strdup(gettext("unable to close receiving thread"));
819 return (AUDITD_RETRY);
822 (void) pthread_mutex_lock(&plugin_mutex);
823 freehostlist(&hosts);
824 freehostlist(&hosts_prev);
825 (void) pthread_mutex_unlock(&plugin_mutex);
826 current_host = NULL;
827 *error = NULL;
828 return (AUDITD_SUCCESS);