public inbox for linux-audit@redhat.com
 help / color / mirror / Atom feed
* [patches] Implement mode=forward in audisp-remote
       [not found] <1864744184.428047.1300532843837.JavaMail.root@zmail07.collab.prod.int.phx2.redhat.com>
@ 2011-03-19 11:09 ` Miloslav Trmac
  2011-03-22 17:43   ` Steve Grubb
  0 siblings, 1 reply; 3+ messages in thread
From: Miloslav Trmac @ 2011-03-19 11:09 UTC (permalink / raw)
  To: Steve Grubb; +Cc: linux-audit

[-- Attachment #1: Type: text/plain, Size: 753 bytes --]

Hello,
the attached patch series implements the store-and-forward mode in audisp-remote.

In mode=forward, as audisp-remote receives audit records, it automatically writes them to a local file.  Therefore neither an unexpected termination of audisp-remote nor problems with the remote server can cause loss of the audit records, and audisp-remote will try to resend all of the pending records before sending any later received audit record, or after restarting audisp-remote.  (Note that loss of audit records is still possible in other cases, e.g. when the system crashes before the records are received by audisp-remote, or when the local queue file is corrupted.)

Detailed description of the approach is included in the individual patches.
    Mirek

[-- Attachment #2: 11-drop-event_t --]
[-- Type: application/octet-stream, Size: 3515 bytes --]

Drop event_t, use a character buffer in main().

Memory allocation/deallocation becomes an internal matter of
queue.c instead of the current transfers of memory ownership
on enqueue() and peek_queue().
Index: audit/audisp/plugins/remote/audisp-remote.c
===================================================================
--- audit.orig/audisp/plugins/remote/audisp-remote.c
+++ audit/audisp/plugins/remote/audisp-remote.c
@@ -340,7 +340,6 @@ static void do_overflow_action(void)
 
 int main(int argc, char *argv[])
 {
-	event_t *e;
 	struct sigaction sa;
 	int rc, q_len;
 
@@ -383,6 +382,7 @@ int main(int argc, char *argv[])
 	do {
 		fd_set rfd;
 		struct timeval tv;
+		char event[MAX_AUDIT_MESSAGE_LENGTH];
 		int n, fds = ifd + 1;
 
 		/* Load configuration */
@@ -428,8 +428,7 @@ int main(int argc, char *argv[])
 		if (hup != 0 || stop != 0)
 			continue;
 
-		e = (event_t *)malloc(sizeof(event_t));
-		if (fgets_unlocked(e->data, MAX_AUDIT_MESSAGE_LENGTH, in)) {
+		if (fgets_unlocked(event, sizeof(event), in)) {
 			if (!transport_ok && remote_ended && 
 				config.remote_ending_action == FA_RECONNECT) {
 				quiet = 1;
@@ -438,25 +437,20 @@ int main(int argc, char *argv[])
 				quiet = 0;
 			}
 			/* Strip out EOE records */
-			if (strstr(e->data,"type=EOE msg=audit(")) {
-				free(e);
+			if (strstr(event,"type=EOE msg=audit("))
 				continue;
-			}
-			if (enqueue(e) != 0)
+			if (enqueue(event) != 0)
 				do_overflow_action();
 			rc = 0;
 			while (!suspend && rc >= 0 && transport_ok &&
-			       (e = peek_queue()) != NULL) {
-				rc = relay_event(e->data, 
-					strnlen(e->data,
+			       peek_queue(event, sizeof(event)) != 0) {
+				rc = relay_event(event,
+					strnlen(event,
 					MAX_AUDIT_MESSAGE_LENGTH));
-				if (rc >= 0) {
-					free(e);
+				if (rc >= 0)
 					dequeue(); // delete it
-				}
 			}
-		} else
-			free(e);
+		}
 		if (feof(in))
 			break;
 	} while (stop == 0);
Index: audit/audisp/plugins/remote/queue.c
===================================================================
--- audit.orig/audisp/plugins/remote/queue.c
+++ audit/audisp/plugins/remote/queue.c
@@ -600,43 +600,32 @@ int init_queue(remote_conf_t *config)
 	return 0;
 }
 
-int enqueue(event_t *e)
+int enqueue(const char *data)
 {
-	int ret;
-
-	if (q_append(q, e->data) == 0)
-		ret = 0;
+	if (q_append(q, data) == 0)
+		return 0;
 	else if (errno == ENOSPC)
-		ret = -1;
+		return -1;
 	else {
 		queue_error();
-		ret = 0;
+		return 0;
 	}
-	free(e);
-	return ret;
 }
 
-event_t *peek_queue(void)
+int peek_queue(char *buf, size_t size)
 {
-	event_t *e;
 	int r;
 
-	e = malloc(sizeof(*e));
-	if (e == NULL)
-		goto err;
-	r = q_peek(q, e->data, sizeof(e->data));
-	if (r == 0) {
-		free(e);
-		return NULL;
-	}
+	r = q_peek(q, buf, size);
+	if (r == 0)
+		return 0;
 	if (r != 1)
 		goto err;
-	return e;
+	return 1;
 
 err:
 	queue_error();
-	free(e);
-	return NULL;
+	return 0;
 }
 
 void dequeue(void)
Index: audit/audisp/plugins/remote/queue.h
===================================================================
--- audit.orig/audisp/plugins/remote/queue.h
+++ audit/audisp/plugins/remote/queue.h
@@ -28,15 +28,9 @@
 #include "libaudit.h"
 #include "remote-config.h"
 
-typedef struct event
-{
-	char data[MAX_AUDIT_MESSAGE_LENGTH];
-} event_t;
-
-
 int init_queue(remote_conf_t *config);
-int enqueue(event_t *e);
-event_t *peek_queue(void);
+int enqueue(const char *data);
+int peek_queue(char *buf, size_t);
 void dequeue(void);
 int queue_length(void);
 void destroy_queue(void);

[-- Attachment #3: 12-fold-old-queue-interface --]
[-- Type: application/octet-stream, Size: 6193 bytes --]

Fold old queue interface into main()

and use "struct queue" in main() directly.
Index: audit/audisp/plugins/remote/audisp-remote.c
===================================================================
--- audit.orig/audisp/plugins/remote/audisp-remote.c
+++ audit/audisp/plugins/remote/audisp-remote.c
@@ -56,6 +56,10 @@
 #define CONFIG_FILE "/etc/audisp/audisp-remote.conf"
 #define BUF_SIZE 32
 
+/* MAX_AUDIT_MESSAGE_LENGTH, aligned to 4 KB so that an average q_append() only
+   writes to two disk disk blocks (1 aligned data block, 1 header block). */
+#define QUEUE_ENTRY_SIZE (3*4096)
+
 /* Error types */
 #define ET_SUCCESS	 0
 #define ET_PERMANENT	-1
@@ -93,6 +97,12 @@ gss_ctx_id_t my_context;
 #define USE_GSS (config.enable_krb5)
 #endif
 
+/* Compile-time expression verification */
+#define verify(E) do {				\
+		char verify__[(E) ? 1 : -1];	\
+		(void)verify__;			\
+	} while (0)
+
 /*
  * SIGTERM handler
  */
@@ -294,7 +304,7 @@ static int generic_remote_warning_handle
 }
 
 /* Report and handle a queue error, using errno. */
-void queue_error(void)
+static void queue_error(void)
 {
 	char *errno_str;
 	va_list ap;
@@ -338,10 +348,31 @@ static void do_overflow_action(void)
         }
 }
 
+/* Initialize and return a queue depending on user's configuration.
+   On error return NULL and set errno. */
+static struct queue *init_queue(void)
+{
+	const char *path;
+	int q_flags;
+
+	if (config.queue_file != NULL)
+		path = config.queue_file;
+	else
+		path = "/var/lib/auditd-remote/queue";
+	q_flags = Q_IN_MEMORY;
+	if (config.mode == M_STORE_AND_FORWARD)
+		/* FIXME: let user control Q_SYNC? */
+		q_flags |= Q_IN_FILE | Q_CREAT | Q_RESIZE;
+	verify(QUEUE_ENTRY_SIZE >= MAX_AUDIT_MESSAGE_LENGTH);
+	return q_open(q_flags, path, config.queue_depth, QUEUE_ENTRY_SIZE);
+}
+
 int main(int argc, char *argv[])
 {
 	struct sigaction sa;
-	int rc, q_len;
+	struct queue *queue;
+	int rc;
+	size_t q_len;
 
 	/* Register sighandlers */
 	sa.sa_flags = 0;
@@ -368,8 +399,9 @@ int main(int argc, char *argv[])
 	rc = init_transport();
 	if (rc == ET_PERMANENT)
 		return 1;
-	if (init_queue(&config) != 0) {
-		syslog(LOG_ERR, "Error initializing audit record queue");
+	queue = init_queue();
+	if (queue == NULL) {
+		syslog(LOG_ERR, "Error initializing audit record queue: %m");
 		return 1;
 	}
 
@@ -439,16 +471,28 @@ int main(int argc, char *argv[])
 			/* Strip out EOE records */
 			if (strstr(event,"type=EOE msg=audit("))
 				continue;
-			if (enqueue(event) != 0)
-				do_overflow_action();
-			rc = 0;
-			while (!suspend && rc >= 0 && transport_ok &&
-			       peek_queue(event, sizeof(event)) != 0) {
-				rc = relay_event(event,
+			if (q_append(queue, event) != 0) {
+				if (errno == ENOSPC)
+					do_overflow_action();
+				else
+					queue_error();
+			}
+			while (!suspend && transport_ok) {
+				rc = q_peek(queue, event, sizeof(event));
+				if (rc == 0)
+					break;
+				if (rc != 1) {
+					queue_error();
+					break;
+				}
+				if (relay_event(event,
 					strnlen(event,
-					MAX_AUDIT_MESSAGE_LENGTH));
-				if (rc >= 0)
-					dequeue(); // delete it
+					MAX_AUDIT_MESSAGE_LENGTH)) < 0)
+					break;
+				if (q_drop_head(queue) != 0) {
+					queue_error();
+					break;
+				}
 			}
 		}
 		if (feof(in))
@@ -456,8 +500,8 @@ int main(int argc, char *argv[])
 	} while (stop == 0);
 	close(sock);
 	free_config(&config);
-	q_len = queue_length();
-	destroy_queue();
+	q_len = q_queue_length(queue);
+	q_close(queue);
 	if (stop)
 		syslog(LOG_NOTICE, "audisp-remote is exiting on stop request");
 
Index: audit/audisp/plugins/remote/queue.c
===================================================================
--- audit.orig/audisp/plugins/remote/queue.c
+++ audit/audisp/plugins/remote/queue.c
@@ -569,78 +569,3 @@ err_errno_q:
 	errno = saved_errno;
 	return NULL;
 }
-
-\f /* audisp-remote interface */
-
-/* MAX_AUDIT_MESSAGE_LENGTH, aligned to 4 KB so that an average q_append() only
-   writes to two disk disk blocks (1 aligned data block, 1 header block). */
-#define QUEUE_ENTRY_SIZE (3*4096)
-
-extern void queue_error(void); /* This will go away in a few more patches. */
-
-static struct queue *q;
-
-int init_queue(remote_conf_t *config)
-{
-	const char *path;
-	int q_flags;
-
-	if (config->queue_file != NULL)
-		path = config->queue_file;
-	else
-		path = "/var/lib/auditd-remote/queue";
-	q_flags = Q_IN_MEMORY;
-	if (config->mode == M_STORE_AND_FORWARD)
-		/* FIXME: let user control Q_SYNC? */
-		q_flags |= Q_IN_FILE | Q_CREAT | Q_RESIZE;
-	verify(QUEUE_ENTRY_SIZE >= MAX_AUDIT_MESSAGE_LENGTH);
-	q = q_open(q_flags, path, config->queue_depth, QUEUE_ENTRY_SIZE);
-	if (q == NULL)
-		return -1;
-	return 0;
-}
-
-int enqueue(const char *data)
-{
-	if (q_append(q, data) == 0)
-		return 0;
-	else if (errno == ENOSPC)
-		return -1;
-	else {
-		queue_error();
-		return 0;
-	}
-}
-
-int peek_queue(char *buf, size_t size)
-{
-	int r;
-
-	r = q_peek(q, buf, size);
-	if (r == 0)
-		return 0;
-	if (r != 1)
-		goto err;
-	return 1;
-
-err:
-	queue_error();
-	return 0;
-}
-
-void dequeue(void)
-{
-	if (q_drop_head(q) != 0)
-		queue_error();
-}
-
-int queue_length(void)
-{
-	return q_queue_length(q);
-}
-
-void destroy_queue(void)
-{
-	q_close(q);
-}
-
Index: audit/audisp/plugins/remote/queue.h
===================================================================
--- audit.orig/audisp/plugins/remote/queue.h
+++ audit/audisp/plugins/remote/queue.h
@@ -25,17 +25,6 @@
 #define QUEUE_HEADER
 
 #include <sys/types.h>
-#include "libaudit.h"
-#include "remote-config.h"
-
-int init_queue(remote_conf_t *config);
-int enqueue(const char *data);
-int peek_queue(char *buf, size_t);
-void dequeue(void);
-int queue_length(void);
-void destroy_queue(void);
-
-\f /* The new interface */
 
 struct queue;
 
Index: audit/audisp/plugins/remote/test-queue.c
===================================================================
--- audit.orig/audisp/plugins/remote/test-queue.c
+++ audit/audisp/plugins/remote/test-queue.c
@@ -72,12 +72,6 @@ err__(int line, const char *message, ...
 	abort();
 }
 
-/* This will go away in a few patches. */
-void queue_error(void)
-{
-	err("Queue error");
-}
-
 static void
 init_sample_entries(void)
 {

[-- Attachment #4: 13-flush-queue-on-startup --]
[-- Type: application/octet-stream, Size: 2052 bytes --]

Flush record queue on startup

When starting audisp-remote, send the contents of the queue to the
remote server.  Note that this may significantly delay the time
between startup of the plugin and the time the plugin starts
actually processing input if the remote server responds slowly.
Index: audit/audisp/plugins/remote/audisp-remote.c
===================================================================
--- audit.orig/audisp/plugins/remote/audisp-remote.c
+++ audit/audisp/plugins/remote/audisp-remote.c
@@ -367,6 +367,30 @@ static struct queue *init_queue(void)
 	return q_open(q_flags, path, config.queue_depth, QUEUE_ENTRY_SIZE);
 }
 
+/* Send as many items from QUEUE to the remote system as possible */
+static void flush_queue(struct queue *queue)
+{
+	while (!suspend && transport_ok) {
+		char event[MAX_AUDIT_MESSAGE_LENGTH];
+		int rc;
+
+		rc = q_peek(queue, event, sizeof(event));
+		if (rc == 0)
+			break;
+		if (rc != 1) {
+			queue_error();
+			break;
+		}
+		if (relay_event(event, strnlen(event, MAX_AUDIT_MESSAGE_LENGTH))
+		    < 0)
+			break;
+		if (q_drop_head(queue) != 0) {
+			queue_error();
+			break;
+		}
+	}
+}
+
 int main(int argc, char *argv[])
 {
 	struct sigaction sa;
@@ -411,7 +435,8 @@ int main(int argc, char *argv[])
 	capng_apply(CAPNG_SELECT_BOTH);
 #endif
 
-	do {
+	flush_queue(queue);
+	while (stop == 0 && !feof(in)) {
 		fd_set rfd;
 		struct timeval tv;
 		char event[MAX_AUDIT_MESSAGE_LENGTH];
@@ -477,27 +502,9 @@ int main(int argc, char *argv[])
 				else
 					queue_error();
 			}
-			while (!suspend && transport_ok) {
-				rc = q_peek(queue, event, sizeof(event));
-				if (rc == 0)
-					break;
-				if (rc != 1) {
-					queue_error();
-					break;
-				}
-				if (relay_event(event,
-					strnlen(event,
-					MAX_AUDIT_MESSAGE_LENGTH)) < 0)
-					break;
-				if (q_drop_head(queue) != 0) {
-					queue_error();
-					break;
-				}
-			}
+			flush_queue(queue);
 		}
-		if (feof(in))
-			break;
-	} while (stop == 0);
+	}
 	close(sock);
 	free_config(&config);
 	q_len = q_queue_length(queue);

[-- Attachment #5: 01-dont-discard-data --]
[-- Type: application/octet-stream, Size: 747 bytes --]

On SIGHUP or when stopping, don't read a record only to throw it away.

Index: audit/audisp/plugins/remote/audisp-remote.c
===================================================================
--- audit.orig/audisp/plugins/remote/audisp-remote.c
+++ audit/audisp/plugins/remote/audisp-remote.c
@@ -382,9 +382,11 @@ int main(int argc, char *argv[])
 		if (!FD_ISSET(ifd, &rfd))
 			continue;
 
+		if (hup != 0 || stop != 0)
+			continue;
+
 		e = (event_t *)malloc(sizeof(event_t));
-		if (fgets_unlocked(e->data, MAX_AUDIT_MESSAGE_LENGTH, in) &&
-							hup==0 && stop==0) {
+		if (fgets_unlocked(e->data, MAX_AUDIT_MESSAGE_LENGTH, in)) {
 			if (!transport_ok && remote_ended && 
 				config.remote_ending_action == FA_RECONNECT) {
 				quiet = 1;

[-- Attachment #6: 02-fix-leak-on-input-error --]
[-- Type: application/octet-stream, Size: 423 bytes --]

Fix memory leak if fgets_unlocked() fails

Index: audit/audisp/plugins/remote/audisp-remote.c
===================================================================
--- audit.orig/audisp/plugins/remote/audisp-remote.c
+++ audit/audisp/plugins/remote/audisp-remote.c
@@ -411,7 +411,8 @@ int main(int argc, char *argv[])
 					free(e);
 				}
 			}
-		}
+		} else
+			free(e);
 		if (feof(in))
 			break;
 	} while (stop == 0);

[-- Attachment #7: 03-fix-config-mode-type --]
[-- Type: application/octet-stream, Size: 479 bytes --]

Fix type of config_t::mode

Index: audit/audisp/plugins/remote/remote-config.h
===================================================================
--- audit.orig/audisp/plugins/remote/remote-config.h
+++ audit/audisp/plugins/remote/remote-config.h
@@ -38,7 +38,7 @@ typedef struct remote_conf
 	unsigned int port;
 	unsigned int local_port;
 	transport_t transport;
-	mode_t mode;
+	rmode_t mode;
 	unsigned int queue_depth;
 	format_t format;
 	unsigned int network_retry_time;

[-- Attachment #8: 04-use-make-dependencies --]
[-- Type: application/octet-stream, Size: 619 bytes --]

Use make dependencies.

This is not technically necessary for any of the future patches, but
it makes development much more convenient.
Index: audit/audisp/plugins/remote/Makefile.am
===================================================================
--- audit.orig/audisp/plugins/remote/Makefile.am
+++ audit/audisp/plugins/remote/Makefile.am
@@ -22,7 +22,6 @@
 
 CONFIG_CLEAN_FILES = *.loT *.rej *.orig
 EXTRA_DIST = au-remote.conf audisp-remote.conf $(man_MANS)
-AUTOMAKE_OPTIONS = no-dependencies
 INCLUDES = -I${top_srcdir} -I${top_srcdir}/lib 
 prog_confdir = $(sysconfdir)/audisp
 prog_conf = audisp-remote.conf

[-- Attachment #9: 05-decouple-do_overflow_action --]
[-- Type: application/octet-stream, Size: 4992 bytes --]

Move do_overlow_action() to audisp-remote.c

This decouples queue.c from "config", making it possible to cleanly
link queue.c into a test program.  We can also get rid of one copy of
change_runlevel().

Index: audit/audisp/plugins/remote/audisp-remote.c
===================================================================
--- audit.orig/audisp/plugins/remote/audisp-remote.c
+++ audit/audisp/plugins/remote/audisp-remote.c
@@ -298,6 +298,35 @@ static void send_heartbeat (void)
 	relay_event (NULL, 0);
 }
 
+static void do_overflow_action(void)
+{
+        switch (config.overflow_action)
+        {
+                case OA_IGNORE:
+			break;
+                case OA_SYSLOG:
+			syslog(LOG_ERR, "queue is full - dropping event");
+                        break;
+                case OA_SUSPEND:
+                        syslog(LOG_ALERT,
+                            "Audisp-remote is suspending event processing due to overflowing its queue.");
+                        break;
+                case OA_SINGLE:
+                        syslog(LOG_ALERT,
+                                "Audisp-remote is now changing the system to single user mode due to overflowing its queue");
+                        change_runlevel(SINGLE);
+                        break;
+                case OA_HALT:
+                        syslog(LOG_ALERT,
+                                "Audisp-remote is now halting the system due to overflowing its queue");
+                        change_runlevel(HALT);
+                        break;
+                default:
+                        syslog(LOG_ALERT, "Unknown overflow action requested");
+                        break;
+        }
+}
+
 int main(int argc, char *argv[])
 {
 	event_t *e;
@@ -399,7 +428,8 @@ int main(int argc, char *argv[])
 				free(e);
 				continue;
 			}
-			enqueue(e);
+			if (enqueue(e) != 0)
+				do_overflow_action();
 			rc = 0;
 			while (!suspend && rc >= 0 && transport_ok &&
 							(e = dequeue(1))) {
Index: audit/audisp/plugins/remote/queue.c
===================================================================
--- audit.orig/audisp/plugins/remote/queue.c
+++ audit/audisp/plugins/remote/queue.c
@@ -22,16 +22,10 @@
 
 #include "config.h"
 #include <stdlib.h>
-#include <unistd.h>
-#include <syslog.h>
 #include "queue.h"
-#include "remote-config.h"
 
 static volatile event_t **q;
 static unsigned int q_next, q_last, q_depth;
-static const char *SINGLE = "1";
-static const char *HALT = "0";
-extern remote_conf_t config;
 
 int init_queue(unsigned int size)
 {
@@ -50,58 +44,7 @@ int init_queue(unsigned int size)
 	return 0;
 }
 
-static void change_runlevel(const char *level)
-{
-	char *argv[3];
-	int pid;
-	static const char *init_pgm = "/sbin/init";
-
-	pid = fork();
-	if (pid < 0) {
-		syslog(LOG_ALERT, "Audisp-remote failed to fork switching runlevels");
-		return;
-	}
-	if (pid)	// Parent
-		return;
-	// Child
-	argv[0] = (char *)init_pgm;
-	argv[1] = (char *)level;
-	argv[2] = NULL;
-	execve(init_pgm, argv, NULL);
-	syslog(LOG_ALERT, "Audisp-remote failed to exec %s", init_pgm);
-	exit(1);
-}
-
-static void do_overflow_action(void)
-{
-        switch (config.overflow_action)
-        {
-                case OA_IGNORE:
-			break;
-                case OA_SYSLOG:
-			syslog(LOG_ERR, "queue is full - dropping event");
-                        break;
-                case OA_SUSPEND:
-                        syslog(LOG_ALERT,
-                            "Audisp-remote is suspending event processing due to overflowing its queue.");
-                        break;
-                case OA_SINGLE:
-                        syslog(LOG_ALERT,
-                                "Audisp-remote is now changing the system to single user mode due to overflowing its queue");
-                        change_runlevel(SINGLE);
-                        break;
-                case OA_HALT:
-                        syslog(LOG_ALERT,
-                                "Audisp-remote is now halting the system due to overflowing its queue");
-                        change_runlevel(HALT);
-                        break;
-                default:
-                        syslog(LOG_ALERT, "Unknown overflow action requested");
-                        break;
-        }
-}
-
-void enqueue(event_t *e)
+int enqueue(event_t *e)
 {
 	unsigned int n;
 
@@ -110,10 +53,10 @@ void enqueue(event_t *e)
 	if (q[n] == NULL) {
 		q[n] = e;
 		q_next = (n+1) % q_depth;
+		return 0;
 	} else {
-		// Overflowed the queue
-		do_overflow_action();
 		free(e);
+		return -1;
 	}
 }
 
Index: audit/audisp/plugins/remote/queue.h
===================================================================
--- audit.orig/audisp/plugins/remote/queue.h
+++ audit/audisp/plugins/remote/queue.h
@@ -32,7 +32,7 @@ typedef struct event
 
 
 int init_queue(unsigned int size);
-void enqueue(event_t *e);
+int enqueue(event_t *e);
 event_t *dequeue(int peek);
 void increase_queue_depth(unsigned int size);
 int queue_length(void);

[-- Attachment #10: 06-drop-increase_queue_depth --]
[-- Type: application/octet-stream, Size: 1215 bytes --]

Drop increase_queue_depth() from the queue interface.

It isn't used anyway, and the persistent queue implementation doesn't
currently provide this operation.  Removing this allows a clean
reimplementation of the queue interface.

Index: audit/audisp/plugins/remote/queue.c
===================================================================
--- audit.orig/audisp/plugins/remote/queue.c
+++ audit/audisp/plugins/remote/queue.c
@@ -80,20 +80,6 @@ event_t *dequeue(int peek)
 	return e;
 }
 
-/* void increase_queue_depth(unsigned int size)
-{
-	if (size > q_depth) {
-		int i;
-		void *tmp_q;
-
-		tmp_q = realloc(q, size * sizeof(event_t *));
-		q = tmp_q;
-		for (i=q_depth; i<size; i++)
-			q[i] = NULL;
-		q_depth = size;
-	}
-} */
-
 int queue_length(void)
 {
 	if (q_next == q_last)
Index: audit/audisp/plugins/remote/queue.h
===================================================================
--- audit.orig/audisp/plugins/remote/queue.h
+++ audit/audisp/plugins/remote/queue.h
@@ -34,7 +34,6 @@ typedef struct event
 int init_queue(unsigned int size);
 int enqueue(event_t *e);
 event_t *dequeue(int peek);
-void increase_queue_depth(unsigned int size);
 int queue_length(void);
 void destroy_queue(void);
 

[-- Attachment #11: 07-implement-persistent-queue --]
[-- Type: application/octet-stream, Size: 27269 bytes --]

Implement an optionally persistent queue data structure.

This includes a minimal test suite, but does not hook it up to
audisp-remote.

The queue data structure can keep data only in memory, only on disk
(writing it to disk and reading from disk), or in both (writing everything
to disk, but reading from disk only data stored in a previous run).
audisp-remote will use the last option for performance.

The queue file format starts with a fixed header, followed by an array
of slots for strings.  Due to the fixed size of each slot the file format
is rather inefficient, but it is also very simple.

The file is preallocated and the string slots will be aligned to a 4KB
boundary, so it should be necessary to only write one block to disk
when audisp-remote receives a (short) audit record.

With the default queue size of 200 items the file will be about 2.4
megabytes large, which is probably not really worth worrying about.

If necessary, the space utilization could be improved by storing strings
consecutively instead of using pre-arranged slots.

The queue file format is intended to be resilient against unexpected
termination of the process, and should be resilient against unexpected
system crash as long as the OS does not reorder writes (the string data
is written before the header that indicates that it is present) - but
ultimately resiliency against such failures is limited by other
links in the audit record transmission chain - if the record is lost
within auditd or audispd, having a resilient queue file format does
not help; audit records generated within the kernel are necessarily
lost if the system crashes before they are read by auditd because
the kernel will not be able to regenerate/retransmit them after the next
boot.
Index: audit/audisp/plugins/remote/Makefile.am
===================================================================
--- audit.orig/audisp/plugins/remote/Makefile.am
+++ audit/audisp/plugins/remote/Makefile.am
@@ -30,12 +30,16 @@ plugin_conf = au-remote.conf
 sbin_PROGRAMS = audisp-remote
 noinst_HEADERS = remote-config.h queue.h
 man_MANS = audisp-remote.8 audisp-remote.conf.5
+check_PROGRAMS = test-queue
+TESTS = $(check_PROGRAMS)
 
 audisp_remote_SOURCES = audisp-remote.c remote-config.c queue.c
 audisp_remote_CFLAGS = -fPIE -DPIE -g -D_REENTRANT -D_GNU_SOURCE -Wundef
 audisp_remote_LDFLAGS = -pie -Wl,-z,relro $(gss_libs)
 audisp_remote_LDADD = $(CAPNG_LDADD)
 
+test_queue_SOURCES = queue.c test-queue.c
+
 install-data-hook:
 	mkdir -p -m 0750 ${DESTDIR}${plugin_confdir}
 	$(INSTALL_DATA) -D -m 640 ${srcdir}/$(plugin_conf) ${DESTDIR}${plugin_confdir}
Index: audit/audisp/plugins/remote/test-queue.c
===================================================================
--- /dev/null
+++ audit/audisp/plugins/remote/test-queue.c
@@ -0,0 +1,369 @@
+/* test-queue.c -- test suite for persistent-queue.c
+ * Copyright 2011 Red Hat Inc., Durham, North Carolina.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Authors:
+ *   Miloslav Trmač <mitr@redhat.com>
+ */
+
+#include "config.h"
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include "queue.h"
+
+#define NUM_ENTRIES 7
+/* 3*4096, larger than MAX_AUDIT_MESSAGE_LENGTH.  The same value is used in the
+   main audisp-remote code. */
+#define ENTRY_SIZE 12288
+
+static char filename[] = "/tmp/tqXXXXXX";
+static struct queue *q;
+
+static char *sample_entries[NUM_ENTRIES - 1];
+#define NUM_SAMPLE_ENTRIES (sizeof(sample_entries) / sizeof(*sample_entries))
+
+#define die(...) die__(__LINE__, __VA_ARGS__)
+static void __attribute__((format (printf, 2, 3)))
+die__(int line, const char *message, ...)
+{
+	va_list ap;
+
+	fprintf(stderr, "test-queue: %d: ", line);
+	va_start(ap, message);
+	vfprintf(stderr, message, ap);
+	va_end(ap);
+	putc('\n', stderr);
+	abort();
+}
+
+#define err(...) err__(__LINE__, __VA_ARGS__)
+static void __attribute__((format (printf, 2, 3)))
+err__(int line, const char *message, ...)
+{
+	char *errno_str;
+	va_list ap;
+
+	errno_str = strerror(errno);
+	fprintf(stderr, "test-queue: %d: ", line);
+	va_start(ap, message);
+	vfprintf(stderr, message, ap);
+	va_end(ap);
+	fprintf(stderr, ": %s\n", errno_str);
+	abort();
+}
+
+static void
+init_sample_entries(void)
+{
+	size_t i;
+
+	for (i = 0; i < NUM_SAMPLE_ENTRIES; i++) {
+		char *e;
+		size_t j, len;
+
+		len = rand() % ENTRY_SIZE;
+		e = malloc(len + 1);
+		if (e == NULL)
+			err("malloc");
+		for (j = 0; j < len; j++)
+			e[j] = rand() % CHAR_MAX + 1;
+		e[j] = '\0';
+		sample_entries[i] = e;
+	}
+}
+
+static void
+free_sample_entries(void)
+{
+	size_t i;
+
+	for (i = 0; i < NUM_SAMPLE_ENTRIES; i++)
+		free(sample_entries[i]);
+}
+
+static void
+test_q_open(void)
+{
+	struct queue *q2;
+
+	/* Test that flags are honored */
+	q2 = q_open(Q_IN_FILE | Q_CREAT | Q_EXCL, filename, NUM_ENTRIES,
+		    ENTRY_SIZE);
+	if (q2 != NULL)
+		die("q_open didn't fail");
+	if (errno != EEXIST)
+		err("q_open");
+
+	/* Test that locking is enforced.  Use a separate process because
+	   fcntl()/lockf() locking is attached to processes, not file
+	   descriptors. */
+	fflush(NULL);
+	switch (fork()) {
+	case -1:
+		err("fork");
+	case 0:
+		q2 = q_open(Q_IN_FILE, filename, NUM_ENTRIES, ENTRY_SIZE);
+		if (q2 != NULL)
+			die("q_open didn't fail");
+		if (errno != EBUSY)
+			err("q_open");
+		_exit(0);
+	default: {
+		int status;
+
+		if (wait(&status) == (pid_t)-1)
+			err("wait");
+		if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+			die("wait status %d", status);
+	}
+	}
+}
+
+static void
+test_empty_q (void)
+{
+	char buf[ENTRY_SIZE];
+
+	if (q_peek(q, buf, sizeof(buf)) != 0)
+		die("q_peek reports non-empty");
+
+	if (q_drop_head(q) != -1)
+		die("q_drop_head didn't fail");
+	if (errno != EINVAL)
+		err("q_drop_head");
+
+	if (q_queue_length(q) != 0)
+		die("Unexpected q_queue_length");
+}
+
+static void
+test_basic_data (void)
+{
+	char buf[ENTRY_SIZE + 1];
+	int i;
+
+	if (q_append(q, " ") != 0)
+		die("q_append");
+
+	memset (buf, 'A', ENTRY_SIZE);
+	buf[ENTRY_SIZE] = '\0';
+	if (q_append(q, buf) != -1)
+		die("q_append didn't fail");
+	if (errno != EINVAL)
+		err("q_append");
+
+	buf[ENTRY_SIZE - 1] = '\0';
+	if (q_append(q, buf) != 0)
+		die("q_append");
+
+	if (q_queue_length(q) != 2)
+		die("Unexpected q_queue_length");
+
+	if (q_peek(q, buf, sizeof(buf)) != 1)
+		err("q_peek");
+	if (strcmp(buf, " ") != 0)
+		die("invalid data returned");
+	if (q_drop_head(q) != 0)
+		err("q_drop_head");
+
+	if (q_peek(q, buf, ENTRY_SIZE - 1) != -1)
+		err("q_peek didn't fail");
+	if (errno != ERANGE)
+		err("q_peek");
+	for (i = 0; i < 2; i++) {
+		size_t j;
+
+		if (q_peek(q, buf, sizeof(buf)) != 1)
+			err("q_peek");
+		for (j = 0; j < ENTRY_SIZE - 1; j++) {
+			if (buf[j] != 'A')
+				die("invalid data at %zu", j);
+		}
+		if (buf[j] != '\0')
+			die("invalid data at %zu", j);
+	}
+	if (q_drop_head(q) != 0)
+		err("q_drop_head");
+
+	if (q_queue_length(q) != 0)
+		die("Unexpected q_queue_length");
+}
+
+static void
+append_sample_entries(size_t count)
+{
+	size_t i;
+
+	for (i = 0; i < count; i++) {
+		if (q_append(q, sample_entries[i % NUM_SAMPLE_ENTRIES]) != 0)
+			die("q_append %zu", i);
+	}
+}
+
+static void
+verify_sample_entries(size_t count)
+{
+	char buf[ENTRY_SIZE + 1];
+	size_t i;
+
+	if (q_queue_length(q) != count)
+		die("Unexpected q_queue_length");
+	for (i = 0; i < count; i++) {
+		if (q_peek(q, buf, sizeof(buf)) != 1)
+			err("q_peek %zu", i);
+		if (strcmp(buf, sample_entries[i % NUM_SAMPLE_ENTRIES]) != 0)
+			die("invalid data %zu", i);
+		if (q_drop_head(q) != 0)
+			err("q_drop_head");
+	}
+	if (q_peek(q, buf, sizeof(buf)) != 0)
+		die("q_peek reports non-empty");
+}
+
+static void
+test_run(int flags)
+{
+	size_t j;
+
+	q = q_open(flags | Q_CREAT | Q_EXCL, filename, NUM_ENTRIES, ENTRY_SIZE);
+	if (q == NULL)
+		err("q_open");
+
+	if ((flags & Q_IN_FILE) != 0)
+		test_q_open();
+
+	/* Do this enough times to get a wraparound */
+	for (j = 0; j < NUM_ENTRIES; j++) {
+		test_empty_q();
+		test_basic_data();
+	}
+
+	append_sample_entries(NUM_ENTRIES - 1);
+	if (q_queue_length(q) != NUM_ENTRIES - 1)
+		die("Unexpected q_queue_length");
+
+	q_close(q);
+
+	q = q_open(flags, filename, NUM_ENTRIES, ENTRY_SIZE);
+	if (q == NULL)
+		err("q_open");
+	if ((flags & Q_IN_FILE) != 0)
+		/* Test that the queue can be reopened and data has been
+		   preserved. */
+		verify_sample_entries(NUM_ENTRIES - 1);
+	else
+		/* Test that a new in-memory queue is empty. */
+		verify_sample_entries(0);
+	q_close(q);
+
+	if ((flags & Q_IN_FILE) != 0 && unlink(filename) != 0)
+		err("unlink");
+}
+
+static void
+test_resizing(void)
+{
+	size_t j;
+
+	q = q_open(Q_IN_FILE | Q_CREAT | Q_EXCL, filename, NUM_ENTRIES,
+		   ENTRY_SIZE);
+	if (q == NULL)
+		err("q_open");
+
+	append_sample_entries(NUM_ENTRIES);
+	if (q_queue_length(q) != NUM_ENTRIES)
+		die("Unexpected q_queue_length");
+
+	q_close(q);
+
+	/* Verify num_entries is validated */
+	q = q_open(Q_IN_FILE, filename, NUM_ENTRIES + 1, ENTRY_SIZE);
+	if (q != NULL)
+		die("q_open didn't fail");
+	if (errno != EINVAL)
+		err("q_open");
+	q = q_open(Q_IN_FILE, filename, NUM_ENTRIES - 1, ENTRY_SIZE);
+	if (q != NULL)
+		die("q_open didn't fail");
+	if (errno != EINVAL)
+		err("q_open");
+
+	/* Test increasing size */
+	q = q_open(Q_IN_FILE | Q_RESIZE, filename, 2 * NUM_ENTRIES, ENTRY_SIZE);
+	if (q == NULL)
+		err("q_open");
+	verify_sample_entries(NUM_ENTRIES);
+
+	append_sample_entries(NUM_ENTRIES);
+	q_close(q);
+
+	/* Test decreasing size */
+	q = q_open(Q_IN_FILE | Q_RESIZE, filename, NUM_ENTRIES / 2, ENTRY_SIZE);
+	if (q != NULL)
+		die("q_open didn't fail");
+	if (errno != ENOSPC)
+		err("q_open");
+	q = q_open(Q_IN_FILE | Q_RESIZE, filename, NUM_ENTRIES, ENTRY_SIZE);
+	if (q == NULL)
+		err("q_open");
+	verify_sample_entries(NUM_ENTRIES);
+	q_close(q);
+
+	if (unlink(filename) != 0)
+		err("unlink");
+}
+
+int
+main(void)
+{
+	static const int flags[] = {
+		Q_IN_MEMORY,
+		Q_IN_FILE,
+		Q_IN_FILE | Q_SYNC,
+		Q_IN_MEMORY | Q_IN_FILE
+	};
+
+	int fd;
+	size_t i;
+
+	init_sample_entries();
+
+	/* We really want tmpnam() here (safe due to the Q_EXCL below), but
+	   gcc warns on any use of tmpnam(). */
+	fd = mkstemp(filename);
+	if (fd == -1)
+		err("tmpnam");
+	if (close(fd) != 0)
+		err("close");
+	if (unlink(filename) != 0)
+		err("unlink");
+
+	for (i = 0; i < sizeof(flags) / sizeof(*flags); i++)
+		test_run(flags[i]);
+
+	test_resizing();
+
+	free_sample_entries();
+
+	return EXIT_SUCCESS;
+}
Index: audit/audisp/plugins/remote/queue.c
===================================================================
--- audit.orig/audisp/plugins/remote/queue.c
+++ audit/audisp/plugins/remote/queue.c
@@ -1,4 +1,4 @@
-/* queue.c --
+/* queue.c - a string queue implementation
  * Copyright 2009, 2011 Red Hat Inc., Durham, North Carolina.
  * All Rights Reserved.
  *
@@ -21,9 +21,557 @@
  */
 
 #include "config.h"
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdint.h>
 #include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
 #include "queue.h"
 
+struct queue
+{
+	int flags;		/* Q_* */
+	int fd;			/* -1 if !Q_IN_FILE */
+	/* NULL if !Q_IN_MEMORY.  [i] contains a memory copy of the queue entry
+	   "i", if known - it may be NULL even if entry exists. */
+	char **memory;
+	size_t num_entries;
+	size_t entry_size;
+	size_t queue_head;
+	size_t queue_length;
+	unsigned char buffer[];	/* Used only locally within q_peek() */
+};
+
+\f /* Infrastructure */
+
+/* Compile-time expression verification */
+#define verify(E) do {				\
+		char verify__[(E) ? 1 : -1];	\
+		(void)verify__;			\
+	} while (0)
+
+/* Like pread(), except that it handles partial reads, and returns 0 on
+   success. */
+static int full_pread(int fd, void *buf, size_t size, off_t offset)
+{
+	while (size != 0) {
+		ssize_t run, res;
+
+		if (size > SSIZE_MAX)
+			run = SSIZE_MAX;
+		else
+			run = size;
+		res = pread(fd, buf, run, offset);
+		if (res < 0)
+			return -1;
+		if (res == 0) {
+			errno = ENXIO; /* Any better value? */
+			return -1;
+		}
+		buf = (unsigned char *)buf + res;
+		size -= res;
+		offset += res;
+	}
+	return 0;
+}
+
+/* Like pwrite(), except that it handles partial writes, and returns 0 on
+   success. */
+static int full_pwrite(int fd, const void *buf, size_t size, off_t offset)
+{
+	while (size != 0) {
+		ssize_t run, res;
+
+		if (size > SSIZE_MAX)
+			run = SSIZE_MAX;
+		else
+			run = size;
+		res = pwrite(fd, buf, run, offset);
+		if (res < 0)
+			return -1;
+		if (res == 0) {
+			errno = ENXIO; /* Any better value? */
+			return -1;
+		}
+		buf = (const unsigned char *)buf + res;
+		size -= res;
+		offset += res;
+	}
+	return 0;
+}
+
+\f /* File format and utilities */
+
+/* The mutable part of struct file_header */
+struct fh_state {
+	uint32_t queue_head;	/* 0-based index of the first non-empty entry */
+	uint32_t queue_length;	/* [0, num_entries] */
+};
+
+/* All integer values are in network byte order (big endian) */
+struct file_header
+{
+	uint8_t magic[14];	/* See fh_magic below */
+	uint8_t version;	/* File format version, see FH_VERSION* below */
+	uint8_t reserved;	/* Must be 0 */
+	/* Total file size is (num_entries + 1) * entry_size.  This must fit
+	   into SIZE_MAX because the "len" parameter of posix_fallocate has
+	   a size_t type. */
+	uint32_t num_entries;	/* Total number of entries allocated */
+	uint32_t entry_size;
+	struct fh_state s;
+};
+
+/* Contains a '\0' byte to unambiguously mark the file as a binary file. */
+static const uint8_t fh_magic[14] = "\0audisp-remote";
+#define FH_VERSION_0 0x00
+
+/* Return file position for ENTRY in Q */
+static size_t entry_offset (const struct queue *q, size_t entry)
+{
+	return (entry + 1) * q->entry_size;
+}
+
+/* Synchronize Q if required and return 0.
+   On error, return -1 and set errno. */
+static int q_sync(struct queue *q)
+{
+	if ((q->flags & Q_SYNC) == 0)
+		return 0;
+	return fdatasync(q->fd);
+}
+
+/* Sync file's fh_state with Q, q_sync (Q), and return 0.
+   On error, return -1 and set errno. */
+static int sync_fh_state (struct queue *q)
+{
+	struct fh_state s;
+
+	if (q->fd == -1)
+		return 0;
+
+	s.queue_head = htonl(q->queue_head);
+	s.queue_length = htonl(q->queue_length);
+	if (full_pwrite(q->fd, &s, sizeof(s), offsetof(struct file_header, s))
+	    != 0)
+		return -1;
+	return q_sync(q);
+}
+
+\f /* Implementation */
+
+/* Open PATH for Q, update Q from it, and return 0.
+   On error, return -1 and set errno; Q->fd may be set even on error. */
+static int q_open_file(struct queue *q, const char *path)
+{
+	int open_flags, fd_flags;
+	struct stat st;
+	struct file_header fh;
+
+	open_flags = O_RDWR;
+	if ((q->flags & Q_CREAT) != 0)
+		open_flags |= O_CREAT;
+	if ((q->flags & Q_EXCL) != 0)
+		open_flags |= O_EXCL;
+	q->fd = open(path, open_flags, S_IRUSR | S_IWUSR);
+	if (q->fd == -1)
+		return -1;
+
+	fd_flags = fcntl(q->fd, F_GETFD);
+	if (fd_flags < 0)
+		return -1;
+	if (fcntl(q->fd, F_SETFD, fd_flags | FD_CLOEXEC) == -1)
+		return -1;
+
+	/* File locking in POSIX is pretty much broken... let's hope nobody
+	   attempts to open a single file twice within the same process.
+	   open() above has initialized the file offset to 0, so the lockf()
+	   below affects the whole file. */
+	if (lockf(q->fd, F_TLOCK, 0) != 0) {
+		if (errno == EACCES || errno == EAGAIN)
+			errno = EBUSY; /* This makes more sense... */
+		return -1;
+	}
+
+	if (fstat(q->fd, &st) != 0)
+		return -1;
+	if (st.st_size == 0) {
+		verify(sizeof(fh.magic) == sizeof(fh_magic));
+		memcpy(fh.magic, fh_magic, sizeof(fh.magic));
+		fh.version = FH_VERSION_0;
+		fh.reserved = 0;
+		fh.num_entries = htonl(q->num_entries);
+		fh.entry_size = htonl(q->entry_size);
+		fh.s.queue_head = htonl(0);
+		fh.s.queue_length = htonl(0);
+		if (full_pwrite(q->fd, &fh, sizeof(fh), 0) != 0)
+			return -1;
+		if (q_sync(q) != 0)
+			return -1;
+		if (posix_fallocate(q->fd, 0,
+				    (q->num_entries + 1) * q->entry_size) != 0)
+			return -1;
+	} else {
+		uint32_t file_entries;
+		if (full_pread(q->fd, &fh, sizeof(fh), 0) != 0)
+			return -1;
+		if (memcmp(fh.magic, fh_magic, sizeof(fh.magic)) != 0
+		    || fh.version != FH_VERSION_0 || fh.reserved != 0
+		    || fh.entry_size != htonl(q->entry_size)) {
+			errno = EINVAL;
+			return -1;
+		}
+		file_entries = ntohl(fh.num_entries);
+		if (file_entries > SIZE_MAX / q->entry_size - 1
+		    || ((uintmax_t)st.st_size
+			!= (file_entries + 1) * q->entry_size)) {
+			errno = EINVAL;
+			return -1;
+		}
+	}
+	/* Note that this may change q->num_entries! */
+	q->num_entries = ntohl(fh.num_entries);
+	q->queue_head = ntohl(fh.s.queue_head);
+	q->queue_length = ntohl(fh.s.queue_length);
+	if (q->queue_head >= q->num_entries
+	    || q->queue_length > q->num_entries) {
+		errno = EINVAL;
+		return -1;
+	}
+	return 0;
+}
+
+/* Like q_open(), but does not handle Q_RESIZE, and NUM_ENTRIES is only used
+   when creating a new file. */
+static struct queue *q_open_no_resize(int q_flags, const char *path,
+				      size_t num_entries, size_t entry_size)
+{
+	struct queue *q;
+	int saved_errno;
+
+	if ((q_flags & (Q_IN_MEMORY | Q_IN_FILE)) == 0) {
+		errno = EINVAL;
+		return NULL;
+	}
+	if (num_entries == 0 || num_entries > UINT32_MAX
+	    || entry_size < 1 /* for trailing NUL */
+	    || entry_size < sizeof(struct file_header) /* for Q_IN_FILE */
+	    /* to allocate "struct queue" including its buffer*/
+	    || entry_size > UINT32_MAX - sizeof(struct queue)) {
+		errno = EINVAL;
+		return NULL;
+	}
+	if (entry_size > SIZE_MAX
+	    || num_entries > SIZE_MAX / entry_size - 1 /* for Q_IN_FILE */
+	    || num_entries > SIZE_MAX / sizeof(*q->memory)) {
+		errno = EINVAL;
+		return NULL;
+	}
+
+	q = malloc(sizeof(*q) + entry_size);
+	if (q == NULL)
+		return NULL;
+	q->flags = q_flags;
+	q->fd = -1;
+	q->memory = NULL;
+	q->num_entries = num_entries;
+	q->entry_size = entry_size;
+	q->queue_head = 0;
+	q->queue_length = 0;
+
+	if ((q_flags & Q_IN_MEMORY) != 0) {
+		size_t i;
+
+		q->memory = malloc(num_entries * sizeof(*q->memory));
+		if (q->memory == NULL)
+			goto err;
+		for (i = 0; i < num_entries; i++)
+			q->memory[i] = NULL;
+	}
+
+	if ((q_flags & Q_IN_FILE) != 0 && q_open_file(q, path) != 0)
+		goto err;
+
+	return q;
+
+err:
+	saved_errno = errno;
+	if (q->fd != -1)
+		close(q->fd);
+	free(q->memory);
+	free(q);
+	errno = saved_errno;
+	return NULL;
+}
+
+void q_close(struct queue *q)
+{
+	if (q->fd != -1)
+		close(q->fd); /* Also releases the file lock */
+	if (q->memory != NULL) {
+		size_t i;
+
+		for (i = 0; i < q->num_entries; i++)
+			free(q->memory[i]);
+		free(q->memory);
+	}
+	free(q);
+}
+
+/* Internal use only: add DATA to Q, but don't update fh_state. */
+static int q_append_no_sync_fh_state(struct queue *q, const char *data)
+{
+	size_t data_size, entry_index;
+	char *copy;
+
+	if (q->queue_length == q->num_entries) {
+		errno = ENOSPC;
+		return -1;
+	}
+
+	data_size = strlen(data) + 1;
+	if (data_size > q->entry_size) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	entry_index = (q->queue_head + q->queue_length) % q->num_entries;
+	if (q->memory != NULL) {
+		if (q->memory[entry_index] != NULL) {
+			errno = EIO; /* This is _really_ unexpected. */
+			return -1;
+		}
+		copy = malloc(data_size);
+		if (copy == NULL)
+			return -1;
+		memcpy(copy, data, data_size);
+	} else
+		copy = NULL;
+
+	if (q->fd != -1) {
+		size_t offset;
+
+		offset = entry_offset(q, entry_index);
+		if (full_pwrite(q->fd, data, data_size, offset) != 0) {
+			int saved_errno;
+
+			saved_errno = errno;
+			if (copy != NULL)
+				free(copy);
+			errno = saved_errno;
+			return -1;
+		}
+	}
+
+	if (copy != NULL)
+		q->memory[entry_index] = copy;
+
+	q->queue_length++;
+
+	return 0;
+}
+
+int q_append(struct queue *q, const char *data)
+{
+	int r;
+
+	r = q_append_no_sync_fh_state(q, data);
+	if (r != 0)
+		return r;
+
+	return sync_fh_state(q); /* Calls q_sync() */
+}
+
+int q_peek(struct queue *q, char *buf, size_t size)
+{
+	const unsigned char *data;
+	size_t data_size;
+
+	if (q->queue_length == 0)
+		return 0;
+
+	if (q->memory != NULL && q->memory[q->queue_head] != NULL) {
+		data = q->memory[q->queue_head];
+		data_size = strlen(data) + 1;
+	} else if (q->fd != -1) {
+		const unsigned char *end;
+
+		if (full_pread(q->fd, q->buffer, q->entry_size,
+			       entry_offset(q, q->queue_head)) != 0)
+			return -1;
+		data = q->buffer;
+		end = memchr(q->buffer, '\0', q->entry_size);
+		if (end == NULL) {
+			/* FIXME: silently drop this entry? */
+			errno = EBADMSG;
+			return -1;
+		}
+		data_size = (end - data) + 1;
+
+		if (q->memory != NULL) {
+			char *copy;
+
+			copy = malloc(data_size);
+			if (copy != NULL) { /* Silently ignore failures. */
+				memcpy(copy, data, data_size);
+				q->memory[q->queue_head] = copy;
+			}
+		}
+	} else {
+		errno = EIO; /* This is _really_ unexpected. */
+		return -1;
+	}
+
+	if (size < data_size) {
+		errno = ERANGE;
+		return -1;
+	}
+	memcpy(buf, data, data_size);
+	return 1;
+}
+
+/* Internal use only: drop head of Q, but don't write this into the file */
+static int q_drop_head_memory_only(struct queue *q)
+{
+	if (q->queue_length == 0) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (q->memory != NULL) {
+		free(q->memory[q->queue_head]);
+		q->memory[q->queue_head] = NULL;
+	}
+
+	q->queue_head++;
+	if (q->queue_head == q->num_entries)
+		q->queue_head = 0;
+	q->queue_length--;
+	return 0;
+}
+
+int q_drop_head(struct queue *q)
+{
+	int r;
+
+	r = q_drop_head_memory_only(q);
+	if (r != 0)
+		return r;
+
+	return sync_fh_state(q); /* Calls q_sync() */
+}
+
+size_t q_queue_length(struct queue *q)
+{
+	return q->queue_length;
+}
+
+struct queue *q_open(int q_flags, const char *path, size_t num_entries,
+		     size_t entry_size)
+{
+	struct queue *q, *q2;
+	char *tmp_path, *buf;
+	size_t path_len;
+	int saved_errno, fd;
+
+	q = q_open_no_resize(q_flags, path, num_entries, entry_size);
+	if (q == NULL || q->num_entries == num_entries)
+		return q;
+
+	if ((q->flags & Q_RESIZE) == 0) {
+		saved_errno = EINVAL;
+		goto err_errno_q;
+	}
+
+	if (q->queue_length > num_entries) {
+		saved_errno = ENOSPC;
+		goto err_errno_q;
+	}
+
+	buf = malloc(entry_size);
+	if (buf == NULL) {
+		saved_errno = errno;
+		goto err_errno_q;
+	}
+
+	path_len = strlen(path);
+	tmp_path = malloc(path_len + 7);
+	if (tmp_path == NULL) {
+		saved_errno = errno;
+		goto err_errno_buf;
+	}
+	memcpy(tmp_path, path, path_len);
+	memcpy(tmp_path + path_len, "XXXXXX", 7);
+	/* We really want tmpnam() here (safe due to the Q_EXCL below), but gcc
+	   warns on any use of tmpnam(). */
+	fd = mkstemp(tmp_path);
+	if (fd == -1) {
+		saved_errno = errno;
+		goto err_errno_tmp_path;
+	}
+	if (close(fd) != 0 || unlink(tmp_path) != 0) {
+		saved_errno = errno;
+		goto err_errno_tmp_file;
+	}
+
+	q2 = q_open_no_resize(q_flags | Q_CREAT | Q_EXCL, tmp_path, num_entries,
+			      entry_size);
+	if (q2 == NULL) {
+		saved_errno = errno;
+		goto err_errno_tmp_file;
+	}
+	if (q2->num_entries != num_entries) {
+		errno = EIO;	/* This is _really_ unexpected. */
+		goto err_q2;
+	}
+
+	for (;;) {
+		int r;
+
+		r = q_peek(q, buf, entry_size);
+		if (r == 0)
+			break;
+		if (r != 1)
+			goto err_q2;
+
+		if (q_append_no_sync_fh_state(q2, buf) != 0)
+			goto err_q2;
+		if (q_drop_head_memory_only(q) != 0)
+			goto err_q2;
+	}
+	if (sync_fh_state(q2) != 0)
+		goto err_q2;
+
+	if (rename(tmp_path, path) != 0)
+		goto err_q2;
+
+	q_close(q);
+	free(buf);
+	free(tmp_path);
+	return q2;
+
+err_q2:
+	saved_errno = errno;
+	q_close(q2);
+err_errno_tmp_file:
+	unlink(tmp_path);
+err_errno_tmp_path:
+	free(tmp_path);
+err_errno_buf:
+	free(buf);
+err_errno_q:
+	q_close(q);
+	errno = saved_errno;
+	return NULL;
+}
+
+\f /* The old interface */
+
 static volatile event_t **q;
 static unsigned int q_next, q_last, q_depth;
 
Index: audit/audisp/plugins/remote/queue.h
===================================================================
--- audit.orig/audisp/plugins/remote/queue.h
+++ audit/audisp/plugins/remote/queue.h
@@ -1,5 +1,5 @@
-/* queue.h --
- * Copyright 2009 Red Hat Inc., Durham, North Carolina.
+/* queue.h -- a queue abstraction
+ * Copyright 2009, 2011 Red Hat Inc., Durham, North Carolina.
  * All Rights Reserved.
  *
  * This library is free software; you can redistribute it and/or
@@ -18,11 +18,13 @@
  *
  * Authors:
  *      Steve Grubb <sgrubb@redhat.com>
+ *      Miloslav Trmač <mitr@redhat.com>
  */
 
 #ifndef QUEUE_HEADER
 #define QUEUE_HEADER
 
+#include <sys/types.h>
 #include "libaudit.h"
 
 typedef struct event
@@ -37,5 +39,52 @@ event_t *dequeue(int peek);
 int queue_length(void);
 void destroy_queue(void);
 
+\f /* The new interface */
+
+struct queue;
+
+enum {
+	/* Queue storage.  Both options can be set at the same time. */
+	Q_IN_MEMORY = 1 << 0,	/* Keep a copy of the queue in memory */
+	Q_IN_FILE = 1 << 1,	/* Store the queue in a file */
+	/* Other flags */
+	/* With Q_IN_FILE, create the queue if it does not exist */
+	Q_CREAT = 1 << 2,
+	Q_EXCL = 1 << 3,	/* With Q_CREAT, don't open an existing queue */
+	Q_SYNC = 1 << 4, /* With Q_IN_FILE, fdatasync() after each operation */
+	/* With Q_IN_FILE, resize the queue length if necessary */
+	Q_RESIZE = 1 << 5,
+};
+
+/* Open a queue using Q_FLAGS and return it.
+
+   If Q_IN_FILE, use PATH for the file.
+   If Q_IN_FILE, NUM_ENTRIES must be the same for all users of the file unless
+   Q_RESIZE is set.
+   ENTRY_SIZE is the maximum length of a stored string, including the trailing
+   NUL.  If Q_IN_FILE, it must be the same for all users of the file.
+
+   On error, return NULL and set errno.
+
+   Note that the returned queue may not be concurrently accessed by more than
+   one thread. */
+struct queue *q_open(int q_flags, const char *path, size_t num_entries,
+		     size_t entry_size);
+/* Close Q. */
+void q_close(struct queue *q);
+
+/* Add DATA to tail of Q and return 0.
+   On error, return -1 and set errno. */
+int q_append(struct queue *q, const char *data);
+/* Peek at head of Q, storing it into BUF of SIZE.
+   Return 1 if an entry exists, 0 if queue is empty.
+   On error, return -1 and set errno. */
+int q_peek(struct queue *q, char *buf, size_t size);
+/* Drop head of Q and return 0.
+   On error, return -1 and set errno. */
+int q_drop_head(struct queue *q);
+/* Return the number of entires in Q. */
+size_t q_queue_length(struct queue *q);
+
 #endif
 

[-- Attachment #12: 08-config-persistence --]
[-- Type: application/octet-stream, Size: 7283 bytes --]

Add configuration for persistent queues

This only adds configuration handling, the configuration will
be used in later patches.
Index: audit/audisp/plugins/remote/audisp-remote.conf.5
===================================================================
--- audit.orig/audisp/plugins/remote/audisp-remote.conf.5
+++ audit/audisp/plugins/remote/audisp-remote.conf.5
@@ -1,4 +1,4 @@
-.TH AUDISP-REMOTE.CONF: "5" "Dec 2008" "Red Hat" "System Administration Utilities"
+.TH AUDISP-REMOTE.CONF: "5" "Mar 2011" "Red Hat" "System Administration Utilities"
 .SH NAME
 audisp-remote.conf \- the audisp-remote configuration file
 .SH DESCRIPTION
@@ -32,10 +32,15 @@ If set to
 .IR immediate ,
 the remote logging app will attempt to send events immediately after getting them.
 .I forward
-, which is not implemented yet, means that it will store the events to disk and then attempt to send the records. If the connection cannot be made, it will queue records until it can connection to the remote system. The depth of the queue is controlled by the
+means that it will store the events to disk and then attempt to send the records. If the connection cannot be made, it will queue records until it can connect to the remote system. The depth of the queue is controlled by the
 .I queue_depth
 option.
 .TP
+.I queue_file
+Path of a file used for the event queue if
+.I mode
+is set to \fIforward\fP.  The default is \fB/var/lib/auditd-remote/queue\fP.
+.TP
 .I queue_depth
 This option is an unsigned integer that determines how many records can be buffered to disk or in memory before considering it to be a failure sending. This parameter affects the
 .I forward
@@ -52,7 +57,11 @@ the remote end, and to receive status me
 If
 .I ascii
 is given instead, each message is a simple ASCII text line with no
-overhead at all.
+overhead at all.  If
+.I mode
+is set to \fIforward\fP,
+.I format
+must be \fImanaged\fP.
 .TP
 .I network_retry_time
 The time, in seconds, between retries when a network error is
@@ -127,6 +136,10 @@ Likewise, this parameter tells the syste
 remote end signals a warning we don't recognize.  The default is to
 log it to syslog.
 .TP
+.I queue_error_action
+Likewise, this parameter tells the system what action to take if there
+is a problem working with a local record queue.  The default is to exit.
+.TP
 .I overflow_action
 This parameter tells the system what action to take if the
 internal event queue overflows. Valid values are
Index: audit/audisp/plugins/remote/remote-config.c
===================================================================
--- audit.orig/audisp/plugins/remote/remote-config.c
+++ audit/audisp/plugins/remote/remote-config.c
@@ -68,6 +68,8 @@ static int transport_parser(struct nv_pa
 		remote_conf_t *config);
 static int mode_parser(struct nv_pair *nv, int line, 
 		remote_conf_t *config);
+static int queue_file_parser(struct nv_pair *nv, int line,
+		remote_conf_t *config);
 static int depth_parser(struct nv_pair *nv, int line, 
 		remote_conf_t *config);
 static int format_parser(struct nv_pair *nv, int line, 
@@ -96,6 +98,7 @@ AP(disk_full)
 AP(disk_error)
 AP(generic_error)
 AP(generic_warning)
+AP(queue_error)
 #undef AP
 static int remote_ending_action_parser(struct nv_pair *nv, int line,
                 remote_conf_t *config);
@@ -110,6 +113,7 @@ static const struct kw_pair keywords[] =
   {"local_port",       local_port_parser,	0 },
   {"transport",        transport_parser,	0 },
   {"mode",             mode_parser,		0 },
+  {"queue_file",       queue_file_parser,	0 },
   {"queue_depth",      depth_parser,		0 },
   {"format",           format_parser,		0 },
   {"network_retry_time",     network_retry_time_parser,         0 },
@@ -127,6 +131,7 @@ static const struct kw_pair keywords[] =
   {"remote_ending_action",   remote_ending_action_parser,	1 },
   {"generic_error_action",   generic_error_action_parser,	1 },
   {"generic_warning_action", generic_warning_action_parser,	1 },
+  {"queue_error_action",     queue_error_action_parser,		1 },
   {"overflow_action",        overflow_action_parser,		1 },
   { NULL,                    NULL,                              0 }
 };
@@ -140,7 +145,7 @@ static const struct nv_list transport_wo
 static const struct nv_list mode_words[] =
 {
   {"immediate",  M_IMMEDIATE },
-//  {"forward",    M_STORE_AND_FORWARD },
+  {"forward",    M_STORE_AND_FORWARD },
   { NULL,  0 }
 };
 
@@ -192,6 +197,7 @@ void clear_config(remote_conf_t *config)
 	config->local_port = 0;
 	config->transport = T_TCP;
 	config->mode = M_IMMEDIATE;
+	config->queue_file = NULL;
 	config->queue_depth = 200;
 	config->format = F_MANAGED;
 
@@ -208,6 +214,7 @@ void clear_config(remote_conf_t *config)
 	IA(remote_ending, FA_SUSPEND);
 	IA(generic_error, FA_SYSLOG);
 	IA(generic_warning, FA_SYSLOG);
+	IA(queue_error, FA_STOP);
 #undef IA
 	config->overflow_action = OA_SYSLOG;
 
@@ -543,6 +550,21 @@ static int mode_parser(struct nv_pair *n
 	return 1;
 }
 
+static int queue_file_parser(struct nv_pair *nv, int line,
+		remote_conf_t *config)
+{
+	if (nv->value) {
+		if (*nv->value != '/') {
+			syslog(LOG_ERR, "Absolute path needed for %s - line %d",
+			       nv->value, line);
+			return 1;
+		}
+		config->queue_file = strdup(nv->value);
+	} else
+		config->queue_file = NULL;
+	return 0;
+}
+
 static int depth_parser(struct nv_pair *nv, int line,
 		remote_conf_t *config)
 {
@@ -581,6 +603,7 @@ AP(disk_full)
 AP(disk_error)
 AP(generic_error)
 AP(generic_warning)
+AP(queue_error)
 #undef AP
 
 static int overflow_action_parser(struct nv_pair *nv, int line,
@@ -729,12 +752,19 @@ static int sanity_check(remote_conf_t *c
 // port should be less that 32k
 // queue_depth should be less than 100k
 // If fail_action is F_EXEC, fail_exec must exist
+	if (config->mode == M_STORE_AND_FORWARD
+	    && config->format != F_MANAGED) {
+		syslog(LOG_ERR, "\"mode=forward\" is valid only with "
+		       "\"format=managed\"");
+		return 1;
+	}
 	return 0;
 }
 
 void free_config(remote_conf_t *config)
 {
 	free((void *)config->remote_server);
+	free((void *)config->queue_file);
 	free((void *)config->network_failure_exe);
 	free((void *)config->disk_low_exe);
 	free((void *)config->disk_full_exe);
@@ -742,6 +772,7 @@ void free_config(remote_conf_t *config)
 	free((void *)config->remote_ending_exe);
 	free((void *)config->generic_error_exe);
 	free((void *)config->generic_warning_exe);
+	free((void *)config->queue_error_exe);
 	free((void *)config->krb5_principal);
 	free((void *)config->krb5_client_name);
 	free((void *)config->krb5_key_file);
Index: audit/audisp/plugins/remote/remote-config.h
===================================================================
--- audit.orig/audisp/plugins/remote/remote-config.h
+++ audit/audisp/plugins/remote/remote-config.h
@@ -39,6 +39,7 @@ typedef struct remote_conf
 	unsigned int local_port;
 	transport_t transport;
 	rmode_t mode;
+	const char *queue_file;
 	unsigned int queue_depth;
 	format_t format;
 	unsigned int network_retry_time;
@@ -64,6 +65,8 @@ typedef struct remote_conf
 	const char *generic_error_exe;
 	failure_action_t generic_warning_action;
 	const char *generic_warning_exe;
+	failure_action_t queue_error_action;
+	const char *queue_error_exe;
 	overflow_action_t overflow_action;
 } remote_conf_t;
 

[-- Attachment #13: 09-use-persistent-queue --]
[-- Type: application/octet-stream, Size: 5684 bytes --]

Use the new queue implementation for persistence.

This only attaches "struct queue" to the existing interface,
with minimal interface changes.  Refactoring will follow.

Note that this does not expose the Q_SYNC flag to users, so
some records may be lost upon a system crash.  I'm ambivalent
about this - on one hand it is desirable to be able to protect
the data against a crash, on the other hand the audit record may be
lost by a crash even before audisp-remote gets a chance to write
it to disk, and perhaps it's better not to promise anything than
to promise and not deliver.
Index: audit/audisp/plugins/remote/audisp-remote.c
===================================================================
--- audit.orig/audisp/plugins/remote/audisp-remote.c
+++ audit/audisp/plugins/remote/audisp-remote.c
@@ -293,6 +293,17 @@ static int generic_remote_warning_handle
 			  config.generic_warning_exe);
 }
 
+/* Report and handle a queue error, using errno. */
+void queue_error(void)
+{
+	char *errno_str;
+	va_list ap;
+
+	errno_str = strerror(errno);
+	do_action("queue error", errno_str, LOG_ERR, config.queue_error_action,
+		  config.queue_error_exe);
+}
+
 static void send_heartbeat (void)
 {
 	relay_event (NULL, 0);
@@ -358,7 +369,10 @@ int main(int argc, char *argv[])
 	rc = init_transport();
 	if (rc == ET_PERMANENT)
 		return 1;
-	init_queue(config.queue_depth);
+	if (init_queue(&config) != 0) {
+		syslog(LOG_ERR, "Error initializing audit record queue");
+		return 1;
+	}
 
 #ifdef HAVE_LIBCAP_NG
 	// Drop all capabilities
@@ -437,7 +451,8 @@ int main(int argc, char *argv[])
 					strnlen(e->data,
 					MAX_AUDIT_MESSAGE_LENGTH));
 				if (rc >= 0) {
-					dequeue(0); // delete it
+					free(e);
+					e = dequeue(0); // delete it
 					free(e);
 				}
 			}
Index: audit/audisp/plugins/remote/queue.c
===================================================================
--- audit.orig/audisp/plugins/remote/queue.c
+++ audit/audisp/plugins/remote/queue.c
@@ -163,7 +163,7 @@ static int sync_fh_state (struct queue *
 	return q_sync(q);
 }
 
-\f /* Implementation */
+\f /* Queue implementation */
 
 /* Open PATH for Q, update Q from it, and return 0.
    On error, return -1 and set errno; Q->fd may be set even on error. */
@@ -570,81 +570,84 @@ err_errno_q:
 	return NULL;
 }
 
-\f /* The old interface */
+\f /* audisp-remote interface */
 
-static volatile event_t **q;
-static unsigned int q_next, q_last, q_depth;
+/* MAX_AUDIT_MESSAGE_LENGTH, aligned to 4 KB so that an average q_append() only
+   writes to two disk disk blocks (1 aligned data block, 1 header block). */
+#define QUEUE_ENTRY_SIZE (3*4096)
 
-int init_queue(unsigned int size)
+extern void queue_error(void); /* This will go away in a few more patches. */
+
+static struct queue *q;
+
+int init_queue(remote_conf_t *config)
 {
-	unsigned int i;
+	const char *path;
+	int q_flags;
 
-	q_next = 0;
-	q_last = 0;
-	q_depth = size;
-	q = malloc(q_depth * sizeof(event_t *));
+	if (config->queue_file != NULL)
+		path = config->queue_file;
+	else
+		path = "/var/lib/auditd-remote/queue";
+	q_flags = Q_IN_MEMORY;
+	if (config->mode == M_STORE_AND_FORWARD)
+		/* FIXME: let user control Q_SYNC? */
+		q_flags |= Q_IN_FILE | Q_CREAT | Q_RESIZE;
+	verify(QUEUE_ENTRY_SIZE >= MAX_AUDIT_MESSAGE_LENGTH);
+	q = q_open(q_flags, path, config->queue_depth, QUEUE_ENTRY_SIZE);
 	if (q == NULL)
 		return -1;
-
-	for (i=0; i<q_depth; i++) 
-		q[i] = NULL;
-
 	return 0;
 }
 
 int enqueue(event_t *e)
 {
-	unsigned int n;
+	int ret;
 
-	// OK, add event
-	n = q_next%q_depth;
-	if (q[n] == NULL) {
-		q[n] = e;
-		q_next = (n+1) % q_depth;
-		return 0;
-	} else {
-		free(e);
-		return -1;
+	if (q_append(q, e->data) == 0)
+		ret = 0;
+	else if (errno == ENOSPC)
+		ret = -1;
+	else {
+		queue_error();
+		ret = 0;
 	}
+	free(e);
+	return ret;
 }
 
 event_t *dequeue(int peek)
 {
 	event_t *e;
-	unsigned int n;
-
-	// OK, grab the next event
-	n = q_last%q_depth;
-	if (q[n] != NULL) {
-		e = (event_t *)q[n];
-		if (peek == 0) {
-			q[n] = NULL;
-			q_last = (n+1) % q_depth;
-		}
-	} else
-		e = NULL;
+	int r;
 
-	// Process the event
+	e = malloc(sizeof(*e));
+	if (e == NULL)
+		goto err;
+	r = q_peek(q, e->data, sizeof(e->data));
+	if (r == 0) {
+		free(e);
+		return NULL;
+	}
+	if (r != 1)
+		goto err;
+	if (!peek && q_drop_head(q) != 0)
+		goto err;
 	return e;
+
+err:
+	queue_error();
+	free(e);
+	return NULL;
 }
 
 int queue_length(void)
 {
-	if (q_next == q_last)
-		return 0;
-	if (q_last > q_next)
-		return (q_depth + q_next) - q_last;
-	else
-		return q_next - q_last;
+	return q_queue_length(q);
 }
 
 void destroy_queue(void)
 {
-	unsigned int i;
-
-	for (i=0; i<q_depth; i++)
-		free((void *)q[i]);
-
-	free(q);
+	q_close(q);
 }
 
Index: audit/audisp/plugins/remote/queue.h
===================================================================
--- audit.orig/audisp/plugins/remote/queue.h
+++ audit/audisp/plugins/remote/queue.h
@@ -26,6 +26,7 @@
 
 #include <sys/types.h>
 #include "libaudit.h"
+#include "remote-config.h"
 
 typedef struct event
 {
@@ -33,7 +34,7 @@ typedef struct event
 } event_t;
 
 
-int init_queue(unsigned int size);
+int init_queue(remote_conf_t *config);
 int enqueue(event_t *e);
 event_t *dequeue(int peek);
 int queue_length(void);
Index: audit/audisp/plugins/remote/test-queue.c
===================================================================
--- audit.orig/audisp/plugins/remote/test-queue.c
+++ audit/audisp/plugins/remote/test-queue.c
@@ -72,6 +72,12 @@ err__(int line, const char *message, ...
 	abort();
 }
 
+/* This will go away in a few patches. */
+void queue_error(void)
+{
+	err("Queue error");
+}
+
 static void
 init_sample_entries(void)
 {

[-- Attachment #14: 10-split-dequeue --]
[-- Type: application/octet-stream, Size: 1982 bytes --]

Split dequeue(int) into peek_queue(void) and dequeue(void)

dequeue() does two completely separate things, and main()
does not use the event returned by dequeue(0).  Split them, which
cleans up both the caller and the callee.
Index: audit/audisp/plugins/remote/audisp-remote.c
===================================================================
--- audit.orig/audisp/plugins/remote/audisp-remote.c
+++ audit/audisp/plugins/remote/audisp-remote.c
@@ -446,14 +446,13 @@ int main(int argc, char *argv[])
 				do_overflow_action();
 			rc = 0;
 			while (!suspend && rc >= 0 && transport_ok &&
-							(e = dequeue(1))) {
+			       (e = peek_queue()) != NULL) {
 				rc = relay_event(e->data, 
 					strnlen(e->data,
 					MAX_AUDIT_MESSAGE_LENGTH));
 				if (rc >= 0) {
 					free(e);
-					e = dequeue(0); // delete it
-					free(e);
+					dequeue(); // delete it
 				}
 			}
 		} else
Index: audit/audisp/plugins/remote/queue.c
===================================================================
--- audit.orig/audisp/plugins/remote/queue.c
+++ audit/audisp/plugins/remote/queue.c
@@ -616,7 +616,7 @@ int enqueue(event_t *e)
 	return ret;
 }
 
-event_t *dequeue(int peek)
+event_t *peek_queue(void)
 {
 	event_t *e;
 	int r;
@@ -631,8 +631,6 @@ event_t *dequeue(int peek)
 	}
 	if (r != 1)
 		goto err;
-	if (!peek && q_drop_head(q) != 0)
-		goto err;
 	return e;
 
 err:
@@ -641,6 +639,12 @@ err:
 	return NULL;
 }
 
+void dequeue(void)
+{
+	if (q_drop_head(q) != 0)
+		queue_error();
+}
+
 int queue_length(void)
 {
 	return q_queue_length(q);
Index: audit/audisp/plugins/remote/queue.h
===================================================================
--- audit.orig/audisp/plugins/remote/queue.h
+++ audit/audisp/plugins/remote/queue.h
@@ -36,7 +36,8 @@ typedef struct event
 
 int init_queue(remote_conf_t *config);
 int enqueue(event_t *e);
-event_t *dequeue(int peek);
+event_t *peek_queue(void);
+void dequeue(void);
 int queue_length(void);
 void destroy_queue(void);
 

[-- Attachment #15: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [patches] Implement mode=forward in audisp-remote
  2011-03-19 11:09 ` [patches] Implement mode=forward in audisp-remote Miloslav Trmac
@ 2011-03-22 17:43   ` Steve Grubb
  2011-03-24 13:38     ` Miloslav Trmac
  0 siblings, 1 reply; 3+ messages in thread
From: Steve Grubb @ 2011-03-22 17:43 UTC (permalink / raw)
  To: Miloslav Trmac; +Cc: linux-audit

Hello,

On Saturday, March 19, 2011 07:09:58 am Miloslav Trmac wrote:
> the attached patch series implements the store-and-forward mode in
> audisp-remote.

Thanks for this patch set!  I think this about does everything we wanted to accomplish 
for a 2.1 release. Its committed to svn now if anyone wants to try this out. I set the 
suggested persistent queue to: /var/spool/audit/remote.log. 

Please anyone interested in remote logging test this out. I will probably push out a 
new release in a week so that there is time for testing and feedback before final 
release.

-Steve

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [patches] Implement mode=forward in audisp-remote
  2011-03-22 17:43   ` Steve Grubb
@ 2011-03-24 13:38     ` Miloslav Trmac
  0 siblings, 0 replies; 3+ messages in thread
From: Miloslav Trmac @ 2011-03-24 13:38 UTC (permalink / raw)
  To: Steve Grubb; +Cc: linux-audit

[-- Attachment #1: Type: text/plain, Size: 213 bytes --]

Hello,
----- Original Message -----
> I set the suggested persistent queue to: /var/spool/audit/remote.log.
Right, that is a better default location.  The attached patch updates the path in other places.
    Mirek

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: audisp-remote.patch --]
[-- Type: text/x-patch; name=audisp-remote.patch, Size: 1617 bytes --]

Index: audisp/plugins/remote/audisp-remote.c
===================================================================
--- audisp/plugins/remote/audisp-remote.c	(revision 470)
+++ audisp/plugins/remote/audisp-remote.c	(working copy)
@@ -358,7 +358,7 @@
 	if (config.queue_file != NULL)
 		path = config.queue_file;
 	else
-		path = "/var/lib/auditd-remote/queue";
+		path = "/var/spool/audit/remote.log";
 	q_flags = Q_IN_MEMORY;
 	if (config.mode == M_STORE_AND_FORWARD)
 		/* FIXME: let user control Q_SYNC? */
Index: audisp/plugins/remote/audisp-remote.conf.5
===================================================================
--- audisp/plugins/remote/audisp-remote.conf.5	(revision 470)
+++ audisp/plugins/remote/audisp-remote.conf.5	(working copy)
@@ -25,9 +25,6 @@
 .IR tcp ,
 the remote logging app will just make a normal clear text connection to the remote system. This is not used if kerberos is enabled.
 .TP
-.I queue_file
-This is the absolute path to the file to be used as a persistent queue.
-.TP
 .I mode
 This parameter tells the remote logging app what strategy to use getting records to the remote system. Valid values are
 .IR immediate ", and " forward " .
@@ -42,7 +39,7 @@
 .I queue_file
 Path of a file used for the event queue if
 .I mode
-is set to \fIforward\fP.  The default is \fB/var/lib/auditd-remote/queue\fP.
+is set to \fIforward\fP.  The default is \fB/var/spool/audit/remote.log\fP.
 .TP
 .I queue_depth
 This option is an unsigned integer that determines how many records can be buffered to disk or in memory before considering it to be a failure sending. This parameter affects the

[-- Attachment #3: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2011-03-24 13:38 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <1864744184.428047.1300532843837.JavaMail.root@zmail07.collab.prod.int.phx2.redhat.com>
2011-03-19 11:09 ` [patches] Implement mode=forward in audisp-remote Miloslav Trmac
2011-03-22 17:43   ` Steve Grubb
2011-03-24 13:38     ` Miloslav Trmac

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox