audit: Add cmd helper for processing audit options
[openafs.git] / src / audit / audit.c
index b5e1084..1eb5556 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright 2000, International Business Machines Corporation and others.
  * All Rights Reserved.
- * 
+ *
  * This software has been released under the terms of the IBM Public
  * License.  For details, see the LICENSE file in the top-level source
  * directory or online at http://www.openafs.org/dl/license10.html
 #include <afsconfig.h>
 #include <afs/param.h>
 
-RCSID
-    ("$Header$");
+#include <roken.h>
 
-#include <fcntl.h>
-#include <stdarg.h>
 #ifdef AFS_AIX32_ENV
 #include <sys/audit.h>
+#else
+#define AUDIT_OK 0
+#define AUDIT_FAIL 1
+#define AUDIT_FAIL_AUTH 2
+#define AUDIT_FAIL_ACCESS 3
+#define AUDIT_FAIL_PRIV 4
 #endif /* AFS_AIX32_ENV */
-#include <errno.h>
 
+#include <afs/opr.h>
 #include "afs/afsint.h"
+#include "afs/butc.h"
 #include <rx/rx.h>
 #include <rx/rxkad.h>
 #include "audit.h"
+#include "audit-api.h"
 #include "lock.h"
-#ifdef AFS_AIX32_ENV
-#include <sys/audit.h>
-#endif
+
 #include <afs/afsutil.h>
 
-char *bufferPtr;
-int bufferLen;
-int osi_audit_all = (-1);      /* Not determined yet */
-int osi_echo_trail = (-1);
+extern struct osi_audit_ops audit_file_ops;
+#ifdef HAVE_SYS_IPC_H
+extern struct osi_audit_ops audit_sysvmq_ops;
+#endif
+
+static struct {
+    void *rock;
+    int (*islocal)(void *rock, char *name, char *inst, char *cell);
+} audit_user_check = { NULL, NULL };
+
+static struct {
+    const char *name;
+    const struct osi_audit_ops *ops;
+} audit_interfaces[] = {
+
+    { "file", &audit_file_ops },
+#ifdef HAVE_SYS_IPC_H
+    { "sysvmq", &audit_sysvmq_ops },
+#endif
+};
+/* default_interface indexes into the audit_interfaces list. */
+static int default_interface = 0; /* Set default to the file interface */
 
-#ifdef AFS_AIX_ENV /** all these functions are only defined for AIX */
+#define N_INTERFACES (sizeof(audit_interfaces) / sizeof(audit_interfaces[0]))
 
-#ifndef        AFS_OSF_ENV
 /*
- * These variadic functions work under AIX, and not all systems (osf1)
+ * Audit interface calling sequence:
+ * osi_audit_interface - sets the default audit interface
+ * osi_audit_file
+ *    create_instance - Called during command arg processing (-auditlog)
+ *    open_file - Called during command arg processing (-auditlog)
+ * osi_audit_open_interface
+ *    open_interface - Called after thread environment has been established
+ * osi_audit_close_interface
+ *    close_interface - Called during main process shutdown
+ * osi_audit
+ *    send_msg - Called during audit events
  */
-/* ************************************************************************** */
-/* AIX requires a buffer filled with values to record with each audit event.
- * aixmakebuf creates that buffer from the variable list of values we are given.
- * ************************************************************************** */
-static
-aixmakebuf(audEvent, vaList)
-     char *audEvent;
-     char *vaList;
+struct audit_log {
+    struct opr_queue link;
+    const struct osi_audit_ops *audit_ops;
+    int auditout_open;
+    void *context;
+};
+
+struct audit_msg {
+    char buf[OSI_AUDIT_MAXMSG];
+    size_t len; /* length of the string in 'buf' (not including the trailing '\0') */
+    int truncated; /* was this message truncated during formatting? */
+};
+
+#ifdef AFS_PTHREAD_ENV
+static pthread_mutex_t audit_lock;
+static pthread_once_t audit_lock_once = PTHREAD_ONCE_INIT;
+#endif
+
+/* Chain of active interfaces */
+static struct opr_queue audit_logs = {&audit_logs, &audit_logs};
+
+static int osi_audit_all = (-1);       /* Not determined yet */
+static int osi_echo_trail = (-1);
+
+static int auditout_open = 0;          /* True if any interface is open */
+
+static int osi_audit_check(void);
+
+/*
+ * Send the message to all the interfaces
+ * @pre audit_lock held
+ */
+static void
+multi_send_msg(struct audit_msg *msg)
+{
+    struct opr_queue *cursor;
+
+    if (msg->len < 1) /* Don't send empty strings */
+       return;
+
+    for (opr_queue_Scan(&audit_logs, cursor)) {
+       struct audit_log *alog;
+       alog = opr_queue_Entry(cursor, struct audit_log, link);
+       if (alog->auditout_open) {
+           alog->audit_ops->send_msg(alog->context, msg->buf, msg->len,
+                                     msg->truncated);
+       }
+    }
+}
+static void
+append_msg(struct audit_msg *msg, const char *format, ...)
+{
+    va_list ap;
+    int remaining, printed;
+
+    if (msg->truncated) {
+       return;
+    }
+
+    if (msg->len >= OSI_AUDIT_MAXMSG - 1) {
+       /* Make sure we have at least some space left */
+       msg->truncated = 1;
+       return;
+    }
+
+    remaining = OSI_AUDIT_MAXMSG - msg->len;
+
+    va_start(ap, format);
+
+    printed = vsnprintf(&msg->buf[msg->len], remaining, format, ap);
+
+    if (printed < 0) {
+       /* Error during formatting. */
+       msg->truncated = 1;
+       printed = 0;
+
+    } else if (printed >= remaining) {
+       /* We tried to write more characters than we had space for. */
+       msg->truncated = 1;
+       printed = remaining - 1;
+
+       /* Make sure we're still terminated. */
+       msg->buf[OSI_AUDIT_MAXMSG - 1] = '\0';
+    }
+
+    msg->len += printed;
+    opr_Assert(msg->len < OSI_AUDIT_MAXMSG);
+
+    va_end(ap);
+}
+#ifdef AFS_AIX32_ENV
+static char *bufferPtr;
+static int bufferLen;
+
+static void
+audmakebuf(char *audEvent, va_list vaList)
 {
     int code;
     int vaEntry;
     int vaInt;
     afs_int32 vaLong;
     char *vaStr;
-    char *vaLst;
-    char hname[20];
     struct AFSFid *vaFid;
 
     vaEntry = va_arg(vaList, int);
     while (vaEntry != AUD_END) {
        switch (vaEntry) {
        case AUD_STR:           /* String */
-           vaStr = (char *)va_arg(vaList, int);
+        case AUD_NAME:          /* Name */
+        case AUD_ACL:           /* ACL */
+           vaStr = (char *)va_arg(vaList, char *);
            if (vaStr) {
                strcpy(bufferPtr, vaStr);
                bufferPtr += strlen(vaStr) + 1;
@@ -73,6 +191,7 @@ aixmakebuf(audEvent, vaList)
            }
            break;
        case AUD_INT:           /* Integer */
+        case AUD_ID:            /* ViceId */
            vaInt = va_arg(vaList, int);
            *(int *)bufferPtr = vaInt;
            bufferPtr += sizeof(vaInt);
@@ -84,12 +203,8 @@ aixmakebuf(audEvent, vaList)
            *(afs_int32 *) bufferPtr = vaLong;
            bufferPtr += sizeof(vaLong);
            break;
-       case AUD_LST:           /* Ptr to another list */
-           vaLst = (char *)va_arg(vaList, int);
-           aixmakebuf(audEvent, vaLst);
-           break;
        case AUD_FID:           /* AFSFid - contains 3 entries */
-           vaFid = (struct AFSFid *)va_arg(vaList, int);
+           vaFid = (struct AFSFid *)va_arg(vaList, struct AFSFid *);
            if (vaFid) {
                memcpy(bufferPtr, vaFid, sizeof(struct AFSFid));
            } else {
@@ -105,14 +220,13 @@ aixmakebuf(audEvent, vaList)
            {
                struct AFSCBFids *Fids;
 
-               Fids = (struct AFSCBFids *)va_arg(vaList, int);
+               Fids = (struct AFSCBFids *)va_arg(vaList, struct AFSCBFids *);
                if (Fids && Fids->AFSCBFids_len) {
                    *((u_int *) bufferPtr) = Fids->AFSCBFids_len;
                    bufferPtr += sizeof(u_int);
                    memcpy(bufferPtr, Fids->AFSCBFids_val,
                           sizeof(struct AFSFid));
                } else {
-                   struct AFSFid dummy;
                    *((u_int *) bufferPtr) = 0;
                    bufferPtr += sizeof(u_int);
                    memset(bufferPtr, 0, sizeof(struct AFSFid));
@@ -120,6 +234,95 @@ aixmakebuf(audEvent, vaList)
                bufferPtr += sizeof(struct AFSFid);
                break;
            }
+       /* butc tape label */
+       case AUD_TLBL:
+           {
+               struct tc_tapeLabel *label;
+
+               label = (struct tc_tapeLabel *)va_arg(vaList,
+                                                     struct tc_tapeLabel *);
+               if (label)
+                   memcpy(bufferPtr, label, sizeof(*label));
+               else
+                   memset(bufferPtr, 0, sizeof(*label));
+               bufferPtr += sizeof(label);
+               break;
+           }
+       /* butc dump interface */
+       case AUD_TDI:
+           {
+               struct tc_dumpInterface *di;
+
+               di = (struct tc_dumpInterface *)
+                       va_arg(vaList, struct tc_dumpInterface *);
+               if (di)
+                   memcpy(bufferPtr, di, sizeof(*di));
+               else
+                   memset(bufferPtr, 0, sizeof(*di));
+               bufferPtr += sizeof(*di);
+               break;
+           }
+       /*
+        * butc dump array
+        * An array of dump descriptions, but the AIX audit package assumes fixed
+        * length, so we can only do the first one for now.
+        */
+       case AUD_TDA:
+           {
+               struct tc_dumpArray *da;
+
+               da = (struct tc_dumpArray *)
+                       va_arg(vaList, struct tc_dumpArray *);
+               if (da && da->tc_dumpArray_len) {
+                   memcpy(bufferPtr, &da->tc_dumpArray_len, sizeof(u_int));
+                   bufferPtr += sizeof(u_int);
+                   memcpy(bufferPtr, da->tc_dumpArray_val,
+                          sizeof(da->tc_dumpArray_val[0]));
+               } else {
+                   memset(bufferPtr, 0, sizeof(u_int));
+                   bufferPtr += sizeof(u_int);
+                   memset(bufferPtr, 0, sizeof(da->tc_dumpArray_val[0]));
+               }
+               bufferPtr += sizeof(da->tc_dumpArray_val[0]);
+               break;
+           }
+       /*
+        * butc restore array
+        * An array of restore descriptions, but the AIX audit package assumes
+        * fixed length, so we can only do the first one for now.
+        */
+       case AUD_TRA:
+           {
+               struct tc_restoreArray *ra;
+
+               ra = (struct tc_restoreArray *)
+                       va_arg(vaList, struct tc_restoreArray *);
+               if (ra && ra->tc_restoreArray_len) {
+                   memcpy(bufferPtr, &ra->tc_restoreArray_len, sizeof(u_int));
+                   bufferPtr += sizeof(u_int);
+                   memcpy(bufferPtr, ra->tc_restoreArray_val,
+                          sizeof(ra->tc_restoreArray_val[0]));
+               } else {
+                   memset(bufferPtr, 0, sizeof(u_int));
+                   bufferPtr += sizeof(u_int);
+                   memset(bufferPtr, 0, sizeof(ra->tc_restoreArray_val[0]));
+               }
+               bufferPtr += sizeof(ra->tc_restoreArray_val[0]);
+               break;
+           }
+       /* butc tape controller status */
+           {
+               struct tciStatusS *status;
+
+               status = (struct tciStatusS *)va_arg(vaList,
+                                                    struct tciStatusS *);
+               if (status)
+                   memcpy(bufferPtr, status, sizeof(*status));
+               else
+                   memset(bufferPtr, 0, sizeof(*status));
+               bufferPtr += sizeof(*status);
+               break;
+           }
        default:
 #ifdef AFS_AIX32_ENV
            code =
@@ -133,136 +336,279 @@ aixmakebuf(audEvent, vaList)
        vaEntry = va_arg(vaList, int);
     }                          /* end while */
 }
+#endif
 
-static
-printbuf(audEvent, errCode, vaList)
-     char *audEvent;
-     afs_int32 errCode;
-     char *vaList;
+static void
+printbuf(int rec, char *audEvent, char *afsName, afs_int32 hostId,
+        afs_int32 errCode, va_list vaList)
 {
     int vaEntry;
     int vaInt;
     afs_int32 vaLong;
     char *vaStr;
-    char *vaLst;
-    char hname[20];
     struct AFSFid *vaFid;
     struct AFSCBFids *vaFids;
+    struct tc_tapeLabel *vaLabel;
+    struct tc_dumpInterface *vaDI;
+    struct tc_dumpArray *vaDA;
+    struct tc_restoreArray *vaRA;
+    struct tciStatusS *vaTCstatus;
+    int num = LogThreadNum();
+    struct in_addr hostAddr;
+    time_t currenttime;
+    char tbuffer[26];
+    struct tm tm;
+    struct audit_msg *msg = calloc(1, sizeof(*msg));
 
-    if (osi_echo_trail < 0)
-       osi_audit_check();
-    if (!osi_echo_trail)
+    if (!msg) {
        return;
+    }
 
-    if (strcmp(audEvent, "VALST") != 0)
-       printf("%s %d ", audEvent, errCode);
+    /* Don't print the timestamp or thread id if we recursed */
+    if (rec == 0) {
+       currenttime = time(0);
+       if (strftime(tbuffer, sizeof(tbuffer), "%a %b %d %H:%M:%S %Y ",
+                    localtime_r(&currenttime, &tm)) !=0)
+           append_msg(msg, tbuffer);
+
+       if (num > -1)
+           append_msg(msg, "[%d] ", num);
+    }
+
+    append_msg(msg, "EVENT %s CODE %d ", audEvent, errCode);
+
+    if (afsName) {
+       hostAddr.s_addr = hostId;
+       append_msg(msg, "NAME %s HOST %s ", afsName, inet_ntoa(hostAddr));
+    }
 
     vaEntry = va_arg(vaList, int);
     while (vaEntry != AUD_END) {
        switch (vaEntry) {
        case AUD_STR:           /* String */
-           vaStr = (char *)va_arg(vaList, int);
+           vaStr = (char *)va_arg(vaList, char *);
+           if (vaStr)
+               append_msg(msg, "STR %s ", vaStr);
+           else
+               append_msg(msg, "STR <null>");
+           break;
+       case AUD_NAME:          /* Name */
+           vaStr = (char *)va_arg(vaList, char *);
+           if (vaStr)
+               append_msg(msg, "NAME %s ", vaStr);
+           else
+               append_msg(msg, "NAME <null>");
+           break;
+       case AUD_ACL:           /* ACL */
+           vaStr = (char *)va_arg(vaList, char *);
            if (vaStr)
-               printf("%s ", vaStr);
+               append_msg(msg, "ACL %s ", vaStr);
            else
-               printf("<null>", vaStr);
+               append_msg(msg, "ACL <null>");
            break;
        case AUD_INT:           /* Integer */
            vaInt = va_arg(vaList, int);
-           printf("%d ", vaInt);
+           append_msg(msg, "INT %d ", vaInt);
+           break;
+       case AUD_ID:            /* ViceId */
+           vaInt = va_arg(vaList, int);
+           append_msg(msg, "ID %d ", vaInt);
            break;
        case AUD_DATE:          /* Date    */
+           vaLong = va_arg(vaList, afs_int32);
+           append_msg(msg, "DATE %u ", vaLong);
+           break;
        case AUD_HOST:          /* Host ID */
            vaLong = va_arg(vaList, afs_int32);
-           printf("%u ", vaLong);
+            hostAddr.s_addr = vaLong;
+           append_msg(msg, "HOST %s ", inet_ntoa(hostAddr));
            break;
        case AUD_LONG:          /* afs_int32    */
            vaLong = va_arg(vaList, afs_int32);
-           printf("%d ", vaLong);
-           break;
-       case AUD_LST:           /* Ptr to another list */
-           vaLst = (char *)va_arg(vaList, int);
-           printbuf("VALST", 0, vaLst);
+           append_msg(msg, "LONG %d ", vaLong);
            break;
        case AUD_FID:           /* AFSFid - contains 3 entries */
-           vaFid = (struct AFSFid *)va_arg(vaList, int);
+           vaFid = va_arg(vaList, struct AFSFid *);
            if (vaFid)
-               printf("%u:%u:%u ", vaFid->Volume, vaFid->Vnode,
-                      vaFid->Unique);
+               append_msg(msg, "FID %u:%u:%u ", vaFid->Volume, vaFid->Vnode,
+                          vaFid->Unique);
            else
-               printf("%u:%u:%u ", 0, 0, 0);
+               append_msg(msg, "FID %u:%u:%u ", 0, 0, 0);
            break;
        case AUD_FIDS:          /* array of Fids */
-           vaFids = (struct AFSCBFids *)va_arg(vaList, int);
-           vaFid = NULL;
+           vaFids = va_arg(vaList, struct AFSCBFids *);
 
-           if (vaFids)
-               vaFid = vaFids->AFSCBFids_val;
-           if (vaFid)
-               printf("%u %u:%u:%u ", vaFids->AFSCBFids_len, vaFid->Volume,
-                      vaFid->Vnode, vaFid->Unique);
+           if (vaFids) {
+                unsigned int i;
+
+                vaFid = vaFids->AFSCBFids_val;
+
+                if (vaFid) {
+                   append_msg(msg, "FIDS %u ", vaFids->AFSCBFids_len);
+                    for ( i = 1; i <= vaFids->AFSCBFids_len; i++, vaFid++ )
+                       append_msg(msg, "FID %u:%u:%u ", vaFid->Volume,
+                                  vaFid->Vnode, vaFid->Unique);
+                } else
+                   append_msg(msg, "FIDS 0 FID 0:0:0 ");
+
+            }
+           break;
+       case AUD_TLBL:          /* butc tape label */
+           vaLabel = va_arg(vaList, struct tc_tapeLabel *);
+
+           if (vaLabel) {
+               append_msg(msg, "TAPELABEL %d:%.*s:%.*s:%u ",
+                          vaLabel->size,
+                          TC_MAXTAPELEN, vaLabel->afsname,
+                          TC_MAXTAPELEN, vaLabel->pname,
+                          vaLabel->tapeId);
+           } else {
+               append_msg(msg, "TAPELABEL <null>");
+           }
+           break;
+       case AUD_TDI:
+           vaDI = va_arg(vaList, struct tc_dumpInterface *);
+
+           if (vaDI) {
+               append_msg(msg,
+    "TCDUMPINTERFACE %.*s:%.*s:%.*s:%d:%d:%d:%d:%.*s:%.*s:%d:%d:%d:%d:%d ",
+    TC_MAXDUMPPATH, vaDI->dumpPath, TC_MAXNAMELEN, vaDI->volumeSetName,
+    TC_MAXNAMELEN, vaDI->dumpName, vaDI->parentDumpId, vaDI->dumpLevel,
+    vaDI->doAppend,
+    vaDI->tapeSet.id, TC_MAXHOSTLEN, vaDI->tapeSet.tapeServer,
+    TC_MAXFORMATLEN, vaDI->tapeSet.format, vaDI->tapeSet.maxTapes,
+    vaDI->tapeSet.a, vaDI->tapeSet.b, vaDI->tapeSet.expDate,
+    vaDI->tapeSet.expType);
+           } else {
+               append_msg(msg, "TCDUMPINTERFACE <null>");
+           }
+           break;
+       case AUD_TDA:
+           vaDA = va_arg(vaList, struct tc_dumpArray *);
+
+           if (vaDA) {
+               u_int i;
+               struct tc_dumpDesc *desc;
+               struct in_addr hostAddr;
+
+               desc = vaDA->tc_dumpArray_val;
+               if (desc) {
+                   append_msg(msg, "DUMPS %d ", vaDA->tc_dumpArray_len);
+                   for (i = 0; i < vaDA->tc_dumpArray_len; i++, desc++) {
+                       hostAddr.s_addr = desc->hostAddr;
+                       append_msg(msg, "DUMP %d:%d:%.*s:%d:%d:%d:%s ",
+                                  desc->vid, desc->vtype, TC_MAXNAMELEN, desc->name,
+                                  desc->partition, desc->date, desc->cloneDate,
+                                  inet_ntoa(hostAddr));
+                   }
+               } else {
+                   append_msg(msg, "DUMPS 0 DUMP 0:0::0:0:0:0.0.0.0");
+               }
+           }
+           break;
+       case AUD_TRA:
+           vaRA = va_arg(vaList, struct tc_restoreArray *);
+
+           if (vaRA) {
+               u_int i;
+               struct tc_restoreDesc *desc;
+               struct in_addr hostAddr;
+
+               desc = vaRA->tc_restoreArray_val;
+               if (desc) {
+                   append_msg(msg, "RESTORES %d ",
+                                         vaRA->tc_restoreArray_len);
+                   for(i = 0; i < vaRA->tc_restoreArray_len; i++, desc++) {
+                       hostAddr.s_addr = desc->hostAddr;
+                       append_msg(msg,
+                                  "RESTORE %d:%.*s:%d:%d:%d:%d:%d:%d:%d:%s:%.*s:%.*s ",
+                                  desc->flags, TC_MAXTAPELEN, desc->tapeName,
+                                  desc->dbDumpId, desc->initialDumpId,
+                                  desc->position, desc->origVid, desc->vid,
+                                  desc->partition, desc->dumpLevel,
+                                  inet_ntoa(hostAddr), TC_MAXNAMELEN,
+                                  desc->oldName, TC_MAXNAMELEN, desc->newName);
+                   }
+               } else {
+                   append_msg(msg,
+                       "RESTORES 0 RESTORE 0::0:0:0:0:0:0:0:0.0.0.0::: ");
+               }
+           }
+           break;
+       case AUD_TSTT:
+           vaTCstatus = va_arg(vaList, struct tciStatusS *);
+
+           if (vaTCstatus)
+               append_msg(msg, "TCSTATUS %.*s:%d:%d:%d:%d:%.*s:%d:%d ",
+                          TC_MAXNAMELEN, vaTCstatus->taskName,
+                          vaTCstatus->taskId, vaTCstatus->flags,
+                          vaTCstatus->dbDumpId, vaTCstatus->nKBytes,
+                          TC_MAXNAMELEN, vaTCstatus->volumeName,
+                          vaTCstatus->volsFailed,
+                          vaTCstatus->lastPolled);
            else
-               printf("0 0:0:0 ");
+               append_msg(msg, "TCSTATUS <null>");
            break;
        default:
-           printf("--badval-- ");
+           append_msg(msg, "--badval-- ");
            break;
        }                       /* end switch */
        vaEntry = va_arg(vaList, int);
     }                          /* end while */
 
-    if (strcmp(audEvent, "VALST") != 0)
-       printf("\n");
-}
-#else
-static
-aixmakebuf(audEvent, vaList)
-     char *audEvent;
-     va_list vaList;
-{
-    return;
+    MUTEX_ENTER(&audit_lock);
+    multi_send_msg(msg);
+    MUTEX_EXIT(&audit_lock);
+
+    free(msg);
 }
 
-static
-printbuf(audEvent, errCode, vaList)
-     char *audEvent;
-     long errCode;
-     va_list vaList;
+#ifdef AFS_PTHREAD_ENV
+static void
+osi_audit_init_lock(void)
 {
-    return;
+    MUTEX_INIT(&audit_lock, "audit", MUTEX_DEFAULT, 0);
 }
-
 #endif
 
+void
+osi_audit_init(void)
+{
+#ifdef AFS_PTHREAD_ENV
+    pthread_once(&audit_lock_once, osi_audit_init_lock);
+#endif /* AFS_PTHREAD_ENV */
+}
 
 /* ************************************************************************** */
 /* The routine that acually does the audit call.
  * ************************************************************************** */
-int
-osi_audit(char *audEvent,      /* Event name (15 chars or less) */
-         afs_int32 errCode,    /* The error code */
-         ...)
+static int
+osi_audit_internal(char *audEvent,     /* Event name (15 chars or less) */
+                  afs_int32 errCode,   /* The error code */
+                  char *afsName,
+                  afs_int32 hostId,
+                  va_list vaList)
 {
 #ifdef AFS_AIX32_ENV
     afs_int32 code;
     afs_int32 err;
+    static char BUFFER[32768];
     int result;
+#endif
 
-    va_list vaList;
-    static struct Lock audbuflock = { 0, 0, 0, 0,
 #ifdef AFS_PTHREAD_ENV
-       PTHREAD_MUTEX_INITIALIZER,
-       PTHREAD_COND_INITIALIZER,
-       PTHREAD_COND_INITIALIZER
+    /* i'm pretty sure all the server apps now call osi_audit_init(),
+     * but to be extra careful we'll leave this in here for a
+     * while to make sure */
+    pthread_once(&audit_lock_once, osi_audit_init_lock);
 #endif /* AFS_PTHREAD_ENV */
-    };
-    static char BUFFER[32768];
 
-    if (osi_audit_all < 0)
+    if ((osi_audit_all < 0) || (osi_echo_trail < 0))
        osi_audit_check();
-    if (!osi_audit_all)
-       return;
+    if (!osi_audit_all && !auditout_open)
+       return 0;
 
+#ifdef AFS_AIX32_ENV
     switch (errCode) {
     case 0:
        result = AUDIT_OK;
@@ -278,7 +624,6 @@ osi_audit(char *audEvent,   /* Event name (15 chars or less) */
        break;
     case VL_PERM:              /* vlserver.h  */
     case BUDB_NOTPERMITTED:    /* budb_errs.h */
-/*  case KRB_RD_AP_UNAUTHOR : */
     case BZACCESS:             /* bnode.h     */
     case VOLSERBAD_ACCESS:     /* volser.h    */
        result = AUDIT_FAIL_PRIV;
@@ -287,36 +632,50 @@ osi_audit(char *audEvent, /* Event name (15 chars or less) */
        result = AUDIT_FAIL;
        break;
     }
+#endif
 
-    ObtainWriteLock(&audbuflock);
+#ifdef AFS_AIX32_ENV
+    MUTEX_ENTER(&audit_lock);
     bufferPtr = BUFFER;
 
     /* Put the error code into the buffer list */
     *(int *)bufferPtr = errCode;
     bufferPtr += sizeof(errCode);
 
-    va_start(vaList, errCode);
-    aixmakebuf(audEvent, vaList);
-
-    va_start(vaList, errCode);
-    printbuf(audEvent, errCode, vaList);
+    audmakebuf(audEvent, vaList);
 
     bufferLen = (int)((afs_int32) bufferPtr - (afs_int32) & BUFFER[0]);
     code = auditlog(audEvent, result, BUFFER, bufferLen);
-#ifdef notdef
-    if (code) {
-       err = errno;
-       code = auditlog("AFS_Aud_Fail", result, &err, sizeof(err));
-       if (code)
-           printf("Error while writing audit entry: %d.\n", errno);
+    MUTEX_EXIT(&audit_lock);
+#else
+    if (auditout_open) {
+       printbuf(0, audEvent, afsName, hostId, errCode, vaList);
     }
-#endif /* notdef */
-    ReleaseWriteLock(&audbuflock);
 #endif
+
+    return 0;
+}
+int
+osi_audit(char *audEvent,      /* Event name (15 chars or less) */
+         afs_int32 errCode,    /* The error code */
+         ...)
+{
+    va_list vaList;
+
+    if ((osi_audit_all < 0) || (osi_echo_trail < 0))
+       osi_audit_check();
+    if (!osi_audit_all && !auditout_open)
+       return 0;
+
+    va_start(vaList, errCode);
+    osi_audit_internal(audEvent, errCode, NULL, 0, vaList);
+    va_end(vaList);
+
+    return 0;
 }
 
 /* ************************************************************************** */
-/* Given a RPC call structure, this routine extracts the name and host id from the 
+/* Given a RPC call structure, this routine extracts the name and host id from the
  * call and includes it within the audit information.
  * ************************************************************************** */
 int
@@ -326,15 +685,14 @@ osi_auditU(struct rx_call *call, char *audEvent, int errCode, ...)
     struct rx_peer *peer;
     afs_int32 secClass;
     afs_int32 code;
-    char afsName[MAXKTCNAMELEN];
+    char afsName[MAXKTCNAMELEN + MAXKTCNAMELEN + MAXKTCREALMLEN + 3];
     afs_int32 hostId;
     va_list vaList;
 
-
     if (osi_audit_all < 0)
        osi_audit_check();
-    if (!osi_audit_all)
-       return;
+    if (!osi_audit_all && !auditout_open)
+       return 0;
 
     strcpy(afsName, "--Unknown--");
     hostId = 0;
@@ -342,52 +700,68 @@ osi_auditU(struct rx_call *call, char *audEvent, int errCode, ...)
     if (call) {
        conn = rx_ConnectionOf(call);   /* call -> conn) */
        if (conn) {
-           secClass = rx_SecurityClassOf(conn);        /* conn -> securityIndex */
-           if (secClass == 0) {        /* unauthenticated */
+            secClass = rx_SecurityClassOf(conn);       /* conn -> securityIndex */
+           if (secClass == RX_SECIDX_NULL) {   /* unauthenticated */
                osi_audit("AFS_Aud_Unauth", (-1), AUD_STR, audEvent, AUD_END);
                strcpy(afsName, "--UnAuth--");
-           } else if (secClass == 2) { /* authenticated */
-               code =
-                   rxkad_GetServerInfo(conn, NULL, NULL, afsName, NULL, NULL,
+           } else if (secClass == RX_SECIDX_KAD || secClass == RX_SECIDX_KAE) {
+               /* authenticated with rxkad */
+                char tcell[MAXKTCREALMLEN];
+                char name[MAXKTCNAMELEN];
+                char inst[MAXKTCNAMELEN];
+
+                code =
+                   rxkad_GetServerInfo(conn, NULL, NULL, name, inst, tcell,
                                        NULL);
                if (code) {
-                   osi_audit("AFS_Aud_NoAFSId", (-1), AUD_STR, audEvent,
-                             AUD_END);
+                   osi_audit("AFS_Aud_NoAFSId", (-1), AUD_STR, audEvent, AUD_END);
                    strcpy(afsName, "--NoName--");
+               } else {
+                   afs_int32 islocal = 0;
+                   if (audit_user_check.islocal) {
+                       islocal =
+                           audit_user_check.islocal(audit_user_check.rock,
+                                                    name, inst, tcell);
+                   }
+                   strlcpy(afsName, name, sizeof(afsName));
+                   if (inst[0]) {
+                       strlcat(afsName, ".", sizeof(afsName));
+                       strlcat(afsName, inst, sizeof(afsName));
+                   }
+                   if (tcell[0] && !islocal) {
+                       strlcat(afsName, "@", sizeof(afsName));
+                       strlcat(afsName, tcell, sizeof(afsName));
+                   }
                }
-           } else {            /* Unauthenticated & unknown */
-
-               osi_audit("AFS_Aud_UnknSec", (-1), AUD_STR, audEvent,
-                         AUD_END);
+           } else {            /* Unauthenticated and/or unknown */
+               osi_audit("AFS_Aud_UnknSec", (-1), AUD_STR, audEvent, AUD_END);
+                strcpy(afsName, "--Unknown--");
            }
-
            peer = rx_PeerOf(conn);     /* conn -> peer */
            if (peer)
                hostId = rx_HostOf(peer);       /* peer -> host */
            else
                osi_audit("AFS_Aud_NoHost", (-1), AUD_STR, audEvent, AUD_END);
        } else {                /* null conn */
-
            osi_audit("AFS_Aud_NoConn", (-1), AUD_STR, audEvent, AUD_END);
        }
     } else {                   /* null call */
-
        osi_audit("AFS_Aud_NoCall", (-1), AUD_STR, audEvent, AUD_END);
     }
-
     va_start(vaList, errCode);
-    osi_audit(audEvent, errCode, AUD_STR, afsName, AUD_HOST, hostId, AUD_LST,
-             vaList, AUD_END);
+    osi_audit_internal(audEvent, errCode, afsName, hostId, vaList);
+    va_end(vaList);
+    return 0;
 }
 
 /* ************************************************************************** */
 /* Determines whether auditing is on or off by looking at the Audit file.
- * If the string AFS_AUDIT_AllEvents is defined in the file, then auditing will be 
+ * If the string AFS_AUDIT_AllEvents is defined in the file, then auditing will be
  * enabled.
  * ************************************************************************** */
 
 int
-osi_audit_check()
+osi_audit_check(void)
 {
     FILE *fds;
     int onoff;
@@ -418,21 +792,341 @@ osi_audit_check()
 
     /* Now set whether we audit all events from here on out */
     osi_audit_all = onoff;
-}
 
+    return 0;
+}
 
-#else /* ! AFS_AIX_ENV */
+/*!
+ * Helper for processing cmd line options
+ *
+ * Process the parameters for the -audit-interface and -auditlog command line
+ * options
+ *
+ * @param[in] default_iface
+ *     String for the default audit interface or NULL
+ * @param[in] audit_loglist
+ *     Pointer to a cmd_item list of audit logs or NULL
+ *
+ * @return status
+ * @retval     0       - success
+ * @retval     -1      - Error (message printed to stderr)
+ */
 
 int
-osi_audit(char *audEvent, afs_int32 errCode, ...)
+osi_audit_cmd_Options(char *default_iface, struct cmd_item *audit_loglist)
 {
+
+    if (default_iface) {
+       if (osi_audit_interface(default_iface)) {
+           fprintf(stderr, "Invalid auditinterface '%s'\n", default_iface);
+           return -1;
+       }
+    }
+
+    while (audit_loglist != NULL) {
+       if (osi_audit_file(audit_loglist->data)) {
+           fprintf(stderr, "Error processing auditlog options '%s'\n",
+                   audit_loglist->data);
+           return -1;
+       }
+       audit_loglist = audit_loglist->next;
+    }
     return 0;
 }
 
+ /*
+ * Handle parsing a string: [interface_name:]filespec[:options]
+ * The string a:b will parse 'a' as the interface name and 'b' as the filespec.
+ * Note that the string pointed by optionstr will be modified
+ * by strtok_r by inserting '\0' between the tokens.
+ * The values returned in interface_name, filespec and options
+ * are pointers to the 'sub-strings' within optionstr.
+ */
+static int
+parse_file_options(char *optionstr,
+                  const char **interface_name,
+                  char **filespec,
+                  char **options)
+{
+    int code = 0;
+    char *opt_cursor = optionstr;
+    char *tok1 = NULL, *tok2 = NULL, *tok3 = NULL, *tokptrsave = NULL;
+
+    /*
+     * Handle the fact that strtok doesn't handle empty fields e.g. a::b
+     * and will return tok1-> a tok2-> b
+     */
+
+    /* 1st field */
+    if (*opt_cursor != ':') {
+       tok1 = strtok_r(opt_cursor, ":", &tokptrsave);
+       opt_cursor = strtok_r(NULL, "", &tokptrsave);
+    } else  {
+       /* Skip the ':' */
+       opt_cursor++;
+    }
+
+    /* 2nd field */
+    if (opt_cursor != NULL) {
+       if (*opt_cursor != ':') {
+           tok2 = strtok_r(opt_cursor, ":", &tokptrsave);
+           opt_cursor = strtok_r(NULL, "", &tokptrsave);
+       } else {
+           /* Skip the ':' */
+           opt_cursor++;
+       }
+    }
+
+    /* 3rd field is just the remainder if any */
+    tok3 = opt_cursor;
+
+    if (tok1 == NULL || strlen(tok1) == 0) {
+       fprintf(stderr, "Missing -auditlog parameter\n");
+       code = EINVAL;
+       goto done;
+    }
+
+    /* If only one token, then it's the filespec */
+    if (tok2 == NULL && tok3 == NULL) {
+       *filespec = tok1;
+    } else {
+       *interface_name = tok1;
+       *filespec = tok2;
+       *options = tok3;
+    }
+
+ done:
+    return code;
+}
+
+/*
+ * Parse the options looking for comma-seperated values.
+ */
+static int
+parse_option_string(const struct osi_audit_ops *ops, void *rock, char *options)
+{
+    int code = 0;
+    char *tok1, *tokptrsave = NULL;
+
+    tok1 = strtok_r(options, ",", &tokptrsave);
+    while (tok1) {
+       /* Handle opt=val or just opt */
+       char *opt, *val;
+       char *optvalsave;
+
+       opt = strtok_r(tok1, "=", &optvalsave);
+       val = strtok_r(NULL, "", &optvalsave);
+
+       code = ops->set_option(rock, opt, val);
+
+       if (code) {
+           /* Bad option */
+           goto done;
+       }
+       tok1 = strtok_r(NULL, ",", &tokptrsave);
+    }
+
+ done:
+    return code;
+}
+
+/*
+ * Process -auditlog
+ *  [interface]:filespec[:options]
+ *      interface - interface name (optional - defaults to default_interface)
+ *      filespec - depends on the interface (required)
+ *      options - optional string passed to interface
+ * Returns 0 - success
+ *        EINVAL - option error
+ *        ENOMEM - error allocating memory
+ */
+
 int
-osi_auditU(struct rx_call *call, char *audEvent, int errCode, ...)
+osi_audit_file(const char *fileplusoptions)
 {
-    return 0;
+
+    int idx;
+    int code;
+
+    char *optionstr = NULL;
+
+    const char *interface_name = NULL;
+    char *filespec = NULL;
+    char *options = NULL;
+
+    const struct osi_audit_ops *ops = NULL;
+    struct audit_log *new_alog = NULL;
+
+    /* Use the default unless specified */
+    interface_name = audit_interfaces[default_interface].name;
+
+    /* dup of the input string so the parsing can safely modify it */
+    optionstr = strdup(fileplusoptions);
+    if (!optionstr) {
+       code = ENOMEM;
+       goto done;
+    }
+
+    code = parse_file_options(optionstr, &interface_name, &filespec, &options);
+    if (code)
+       goto done;
+
+    if (interface_name && strlen(interface_name) != 0) {
+       for (idx = 0; idx < N_INTERFACES; idx++) {
+           if (strcmp(interface_name, audit_interfaces[idx].name) == 0) {
+               ops = audit_interfaces[idx].ops;
+               break;
+           }
+       }
+       if (ops == NULL) {
+           /* Couldn't find the interface name */
+           fprintf(stderr, "Could not find the specified audit interface %s\n", interface_name);
+           code = EINVAL;
+           goto done;
+       }
+    } else {
+       fprintf(stderr, "Missing interface name\n");
+       code = EINVAL;
+       goto done;
+    }
+
+    if (filespec == NULL || strlen(filespec) == 0) {
+       fprintf(stderr, "Missing auditlog path for %s interface\n", interface_name);
+       code = EINVAL;
+       goto done;
+    }
+
+    opr_Assert(ops->create_interface != NULL);
+    opr_Assert(ops->close_interface != NULL);
+    opr_Assert(ops->open_file != NULL);
+    opr_Assert(ops->send_msg != NULL);
+    opr_Assert(ops->print_interface_stats != NULL);
+    /* open_interface, set_option and close_interface are optional */
+
+    new_alog = calloc(1, sizeof(*new_alog));
+    if (!new_alog) {
+       code = ENOMEM;
+       goto done;
+    }
+
+    new_alog->audit_ops = ops;
+    new_alog->auditout_open = 0;
+
+    new_alog->context = ops->create_interface();
+    if (new_alog->context == NULL) {
+           code = ENOMEM;
+           goto done;
+    }
+
+    if (options != NULL && ops->set_option != NULL) {
+       /* Split the option string at commas */
+       code = parse_option_string(ops, new_alog->context, options);
+       if (code)
+           goto done;
+    }
+
+    code = ops->open_file(new_alog->context, filespec);
+    if (code) {
+       /* Error opening file */
+       goto done;
+    }
+
+    new_alog->auditout_open = 1;
+    auditout_open = 1;
+
+    /* Add to chain of active interfaces */
+    opr_queue_Append(&audit_logs, &new_alog->link);
+    new_alog = NULL;
+
+    code = 0;
+
+ done:
+     if (code) {
+        /* Error condition present.. */
+        if (new_alog) {
+            ops->close_interface(&new_alog->context);
+            free(new_alog);
+        }
+     }
+
+    if (optionstr)
+       free(optionstr);
+    return code;
 }
 
-#endif
+/*
+ * Set the default interface
+ * return 0 for success
+ *        EINVAL missing or invalid interface name
+ */
+int
+osi_audit_interface(const char *interface)
+{
+    int idx;
+
+    if (interface == NULL || strlen(interface) == 0)
+       return EINVAL;
+
+    for (idx = 0; idx < N_INTERFACES; idx++) {
+       if (strcmp(interface, audit_interfaces[idx].name) == 0) {
+           default_interface = idx;
+           return 0;
+       }
+    }
+    return EINVAL;
+}
+
+/*
+ * Let the interfaces finish initialization
+ */
+void
+osi_audit_open(void)
+{
+    struct opr_queue *cursor;
+
+    for (opr_queue_Scan(&audit_logs, cursor)) {
+       struct audit_log *alog;
+       alog = opr_queue_Entry(cursor, struct audit_log, link);
+       if (alog->auditout_open && alog->audit_ops->open_interface != NULL)
+           alog->audit_ops->open_interface(alog->context);
+    }
+}
+
+/*
+ * Shutdown the interfaces
+ */
+void
+osi_audit_close(void)
+{
+    struct opr_queue *cursor, *cursorsave;
+
+    for (opr_queue_ScanSafe(&audit_logs, cursor, cursorsave)) {
+       struct audit_log *alog;
+       alog = opr_queue_Entry(cursor, struct audit_log, link);
+       alog->audit_ops->close_interface(&alog->context);
+       opr_queue_Remove(&alog->link);
+       free(alog);
+    }
+}
+
+void
+osi_audit_set_user_check(void *rock,
+                        int (*islocal) (void *rock, char *name, char *inst,
+                                        char *cell))
+{
+    audit_user_check.rock = rock;
+    audit_user_check.islocal = islocal;
+}
+
+void
+audit_PrintStats(FILE *out)
+{
+    struct opr_queue *cursor;
+
+    for (opr_queue_Scan(&audit_logs, cursor)) {
+       struct audit_log *alog;
+       alog = opr_queue_Entry(cursor, struct audit_log, link);
+       if (alog->auditout_open)
+           alog->audit_ops->print_interface_stats(alog->context, out);
+    }
+}