diff usr/src/cmd/fs.d/nfs/nfslog/nfslogd.c @ 0:c9caec207d52 b86

Initial porting based on b86
author Koji Uno <koji.uno@sun.com>
date Tue, 02 Jun 2009 18:56:50 +0900
parents
children 1a15d5aaf794
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fs.d/nfs/nfslog/nfslogd.c	Tue Jun 02 18:56:50 2009 +0900
@@ -0,0 +1,924 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident	"@(#)nfslogd.c	1.71	05/06/08 SMI"
+
+/*
+ * Postprocessor for NFS server logging.
+ */
+#include <arpa/inet.h>
+#include <assert.h>
+#include <deflt.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <signal.h>
+#include <syslog.h>
+#include <limits.h>
+#include <libintl.h>
+#include <locale.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <sys/stat.h>
+#include <sys/resource.h>
+#include <rpc/clnt_stat.h>
+#include <nfs/nfs.h>
+#include <nfs/export.h>
+#include <nfs/nfs_log.h>
+#include "fhtab.h"
+#include "nfslogd.h"
+#include "buffer_list.h"
+#include "../lib/nfslog_config.h"
+#include "../lib/nfslogtab.h"
+
+enum pidfile_operation {
+	PID_STARTUP, PID_SHUTDOWN
+};
+
+static int nfslogtab_deactivate_after_boot(void);
+static int
+	nfslogtab_remove(struct buffer_ent **, struct buffer_ent **, boolean_t);
+static int cycle_logs(nfsl_config_t *, int);
+static void enable_logcycling(void);
+static int process_pidfile(enum pidfile_operation);
+static void short_cleanup(void);
+static void full_cleanup(void);
+static void transactions_timeout(nfsl_config_t *);
+static void close_all_translogs(nfsl_config_t *);
+int cycle_log(char *, int);
+static boolean_t is_cycle_needed(char *, void **, boolean_t, int *);
+
+/*
+ * Configuration information.
+ */
+
+int debug = 0;
+boolean_t test = B_FALSE;
+time_t mapping_update_interval = MAPPING_UPDATE_INTERVAL;
+/* prune_timeout measures how old a database entry must be to be pruned */
+time_t prune_timeout = (SECSPERHOUR * 7 * 24);
+int max_logs_preserve = MAX_LOGS_PRESERVE;
+uint_t idle_time = IDLE_TIME;
+static mode_t Umask = NFSLOG_UMASK;
+static long cycle_frequency = CYCLE_FREQUENCY;
+/* prune_frequency measures how often should prune_dbs be called */
+static long prune_frequency = (SECSPERHOUR * 24);
+static int min_size = MIN_PROCESSING_SIZE;
+static volatile bool_t need2cycle = FALSE;
+static volatile bool_t need2prune = FALSE;
+boolean_t keep_running = B_TRUE;
+boolean_t quick_cleaning = B_FALSE;
+
+/*ARGSUSED*/
+int
+main(int argc, char **argv)
+{
+	struct rlimit rl;
+	int error = 0;
+	char *defp;
+	pid_t pid;
+
+	timestruc_t logtab_update;
+	time_t process_start, last_prune = time(0);
+	time_t last_cycle = time(0);	/* last time logs were cycled */
+	int processed, buffers_processed;
+	struct buffer_ent *buffer_list = NULL, *bep, *next;
+	nfsl_config_t *config_list = NULL;
+	char	*fhtable_to_prune = NULL;
+
+	/*
+	 * Check to make sure user is root.
+	 */
+	if (geteuid() != 0) {
+		(void) fprintf(stderr, gettext("%s must be run as root\n"),
+			argv[0]);
+		exit(1);
+	}
+
+	/*
+	 * Read defaults file.
+	 */
+	if (defopen(NFSLOG_OPTIONS_FILE) == 0) {
+		if ((defp = defread("DEBUG=")) != NULL) {
+			debug = atoi(defp);
+			if (debug > 0)
+				(void) printf("debug=%d\n", debug);
+		}
+		if ((defp = defread("TEST=")) != NULL) {
+			if (strcmp(defp, "TRUE") == 0)
+				test = B_TRUE;
+			if (debug > 0) {
+				if (test)
+					(void) printf("test=TRUE\n");
+				else
+					(void) printf("test=FALSE\n");
+			}
+		}
+		/*
+		 * Set Umask for log and fhtable creation.
+		 */
+		if ((defp = defread("UMASK=")) != NULL) {
+			if (sscanf(defp, "%lo", &Umask) != 1)
+				Umask = NFSLOG_UMASK;
+		}
+		/*
+		 * Minimum size buffer should reach before processing.
+		 */
+		if ((defp = defread("MIN_PROCESSING_SIZE=")) != NULL) {
+			min_size = atoi(defp);
+			if (debug > 0)
+				(void) printf("min_size=%d\n", min_size);
+		}
+		/*
+		 * Number of seconds the daemon should
+		 * sleep waiting for more work.
+		 */
+		if ((defp = defread("IDLE_TIME=")) != NULL) {
+			idle_time = (uint_t)atoi(defp);
+			if (debug > 0)
+				(void) printf("idle_time=%d\n", idle_time);
+		}
+		/*
+		 * Maximum number of logs to preserve.
+		 */
+		if ((defp = defread("MAX_LOGS_PRESERVE=")) != NULL) {
+			max_logs_preserve = atoi(defp);
+			if (debug > 0) {
+				(void) printf("max_logs_preserve=%d\n",
+					max_logs_preserve);
+			}
+		}
+		/*
+		 * Frequency of atime updates.
+		 */
+		if ((defp = defread("MAPPING_UPDATE_INTERVAL=")) != NULL) {
+			mapping_update_interval = atoi(defp);
+			if (debug > 0) {
+				(void) printf("mapping_update_interval=%ld\n",
+					mapping_update_interval);
+			}
+		}
+		/*
+		 * Time to remove entries
+		 */
+		if ((defp = defread("PRUNE_TIMEOUT=")) != NULL) {
+			/*
+			 * Prune timeout is in hours but we want
+			 * deal with the time in seconds internally.
+			 */
+			prune_timeout = atoi(defp);
+			prune_timeout *= SECSPERHOUR;
+			if (prune_timeout < prune_frequency)
+				prune_frequency = prune_timeout;
+			if (debug > 0) {
+				(void) printf("prune_timeout=%ld\n",
+					prune_timeout);
+			}
+		}
+		/*
+		 * fhtable to prune when start (for debug/test purposes)
+		 */
+		if ((defp = defread("PRUNE_FHTABLE=")) != NULL) {
+			/*
+			 * Specify full pathname of fhtable to prune before
+			 * any processing is to be done
+			 */
+			if (fhtable_to_prune = malloc(strlen(defp) + 1)) {
+				(void) strcpy(fhtable_to_prune, defp);
+				if (debug > 0) {
+					(void) printf("fhtable to prune=%s\n",
+							fhtable_to_prune);
+				}
+			} else {
+				syslog(LOG_ERR, gettext(
+					"malloc fhtable_to_prune error %s\n"),
+					strerror(errno));
+			}
+		}
+		/*
+		 * Log cycle frequency.
+		 */
+		if ((defp = defread("CYCLE_FREQUENCY=")) != NULL) {
+			cycle_frequency = atol(defp);
+			if (debug > 0) {
+				(void) printf("cycle_frequency=%ld\n",
+					cycle_frequency);
+			}
+		}
+		/*
+		 * defopen of NULL closes the open defaults file.
+		 */
+		(void) defopen((char *)NULL);
+	}
+
+	if (Umask > ((mode_t)0777))
+		Umask = NFSLOG_UMASK;
+	(void) umask(Umask);
+
+	if (getrlimit(RLIMIT_FSIZE, &rl) < 0) {
+		error = errno;
+		(void) fprintf(stderr, gettext(
+			"getrlimit failed error is %d - %s\n"),
+			error, strerror(error));
+		exit(1);
+	}
+	if (min_size < 0 || min_size > rl.rlim_cur) {
+		(void) fprintf(stderr, gettext(
+			"MIN_PROCESSING_SIZE out of range, should be >= 0 and "
+			"< %d. Check %s.\n"), rl.rlim_cur, NFSLOG_OPTIONS_FILE);
+		exit(1);
+	}
+	if (idle_time > INT_MAX) {
+		(void) fprintf(stderr, gettext(
+			"IDLE_TIME out of range, should be >= 0 and "
+			"< %d. Check %s.\n"), INT_MAX, NFSLOG_OPTIONS_FILE);
+		exit(1);
+	}
+	if (max_logs_preserve < 0 || max_logs_preserve > INT_MAX) {
+		(void) fprintf(stderr, gettext(
+			"MAX_LOGS_PRESERVE out of range, should be >= 0 and "
+			"< %d. Check %s.\n"), INT_MAX, NFSLOG_OPTIONS_FILE);
+		exit(1);
+	}
+	if (mapping_update_interval < 0|| mapping_update_interval > INT_MAX) {
+		(void) fprintf(stderr, gettext(
+			"MAPPING_UPDATE_INTERVAL out of range, "
+			"should be >= 0 and "
+			"< %d. Check %s.\n"), INT_MAX, NFSLOG_OPTIONS_FILE);
+		exit(1);
+	}
+	if (cycle_frequency < 0 || cycle_frequency > INT_MAX) {
+		(void) fprintf(stderr, gettext(
+			"CYCLE_FREQUENCY out of range, should be >= 0 and "
+			"< %d. Check %s.\n"), INT_MAX, NFSLOG_OPTIONS_FILE);
+		exit(1);
+	}
+	/* get value in seconds */
+	cycle_frequency = cycle_frequency * 60 * 60;
+
+	/*
+	 * If we dump core, it will be /core
+	 */
+	if (chdir("/") < 0)
+		(void) fprintf(stderr, gettext("chdir /: %s"), strerror(errno));
+
+	/*
+	 * Config errors to stderr
+	 */
+	nfsl_errs_to_syslog = B_FALSE;
+
+#ifndef DEBUG
+	pid = fork();
+	if (pid == -1) {
+		(void) fprintf(stderr, gettext("%s: fork failure\n"),
+			argv[0]);
+		exit(1);
+	}
+	if (pid != 0)
+		exit(0);
+	/*
+	 * Config errors to syslog
+	 */
+	nfsl_errs_to_syslog = B_TRUE;
+#endif /* DEBUG */
+
+	(void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN)
+#define	TEXT_DOMAIN	"SYS_TEST"
+#endif
+	(void) textdomain(TEXT_DOMAIN);
+
+	/*
+	 * Check to see if nfslogd is already running.
+	 */
+	if (process_pidfile(PID_STARTUP) != 0) {
+		exit(1);
+	}
+
+	(void) sigset(SIGUSR1, (void (*)(int))enable_logcycling);
+	(void) sigset(SIGHUP, (void (*)(int))full_cleanup);
+	(void) sigset(SIGTERM, (void(*)(int))short_cleanup);
+
+#ifndef DEBUG
+	/*
+	 * Close existing file descriptors, open "/dev/null" as
+	 * standard input, output, and error, and detach from
+	 * controlling terminal.
+	 */
+	if (!debug && !test) {
+		closefrom(0);
+		(void) open("/dev/null", O_RDONLY);
+		(void) open("/dev/null", O_WRONLY);
+		(void) dup(1);
+	}
+	(void) setsid();
+#endif /* DEBUG */
+
+	openlog(argv[0], LOG_PID, LOG_DAEMON);
+
+	public_fh.fh_len = NFS_FHMAXDATA;
+	public_fh.fh_xlen = NFS_FHMAXDATA;
+
+	/*
+	 * Call once at startup to handle the nfslogtab
+	 */
+	if (nfslogtab_deactivate_after_boot() == -1)
+		exit(1);
+
+	/*
+	 * Get a list of buffers that need to be processed.
+	 */
+	if (error = getbuffer_list(&buffer_list, &logtab_update)) {
+		syslog(LOG_ERR, gettext("Could not read %s: %s"),
+			NFSLOGTAB, strerror(error));
+		goto done;
+	}
+
+	/*
+	 * Get the configuration list.
+	 */
+	if (error = nfsl_getconfig_list(&config_list)) {
+		syslog(LOG_ERR, gettext(
+			"Could not obtain configuration list: %s"),
+			strerror(error));
+		goto done;
+	}
+
+	/*
+	 * loop to process the work being generated by the NFS server
+	 */
+	while (keep_running) {
+		buffers_processed = 0;
+		(void) checkbuffer_list(&buffer_list, &logtab_update);
+
+		while (buffer_list == NULL) {
+			/*
+			 * Nothing to do
+			 */
+			(void) sleep(idle_time);
+
+			if (!keep_running) {
+				/*
+				 * We have been interrupted and asked to
+				 * flush our transactions and exit.
+				 */
+				close_all_translogs(config_list);
+				goto done;
+			}
+			(void) checkbuffer_list(&buffer_list, &logtab_update);
+		}
+
+		process_start = time(0);
+
+		if (error = nfsl_checkconfig_list(&config_list, NULL)) {
+			syslog(LOG_ERR, gettext(
+				"Could not update configuration list: %s"),
+				strerror(error));
+			nfsl_freeconfig_list(&config_list);
+			goto done;
+		}
+
+		if (difftime(time(0), last_cycle) > cycle_frequency)
+			need2cycle = TRUE;
+		if (need2cycle) {
+			error = cycle_logs(config_list, max_logs_preserve);
+			if (error) {
+				syslog(LOG_WARNING, gettext(
+				    "One or more logfiles couldn't be cycled, "
+				    "continuing regular processing"));
+			}
+			need2cycle = FALSE;
+			last_cycle = time(0);
+		}
+		if (difftime(time(0), last_prune) > prune_frequency)
+			need2prune = TRUE;
+		if (need2prune || fhtable_to_prune) {
+			error = prune_dbs(fhtable_to_prune);
+			if (error) {
+				syslog(LOG_WARNING, gettext(
+				    "Error in cleaning database files"));
+			}
+			need2prune = FALSE;
+			last_prune = time(0);
+			/* After the first time, use the normal procedure */
+			free(fhtable_to_prune);
+			fhtable_to_prune = NULL;
+		}
+
+		for (bep = buffer_list; bep != NULL; bep = next) {
+			next = bep->be_next;
+			processed = 0;
+			error = process_buffer(bep, &config_list,
+				min_size, idle_time, &processed);
+			if (error == 0 && processed) {
+				if (bep->be_error) {
+					syslog(LOG_ERR, gettext(
+						"Buffer file '%s' "
+						"processed successfully."),
+						bep->be_name);
+				}
+				error =
+				nfslogtab_remove(&buffer_list, &bep, B_FALSE);
+			} else if (error == ENOENT) {
+				syslog(LOG_ERR, gettext("Removed entry"
+					"\t\"%s\t%s\t%d\" from %s"),
+					bep->be_name,
+					bep->be_sharepnt->se_name,
+					bep->be_sharepnt->se_state,
+					NFSLOGTAB);
+				error =
+				nfslogtab_remove(&buffer_list, &bep, B_TRUE);
+			} else if (error && error != bep->be_error) {
+				/*
+				 * An error different from what we've reported
+				 * before occured.
+				 */
+				syslog(LOG_ERR, gettext(
+					"Cannot process buffer file '%s' - "
+					"will retry on every iteration."),
+					bep->be_name);
+			}
+
+			if (bep != NULL)
+				bep->be_error = error;
+			buffers_processed += processed;
+		}
+
+		transactions_timeout(config_list);
+
+		if (keep_running) {
+			uint_t process_time;
+
+			/*
+			 * Sleep idle_time minus however long it took us
+			 * to process the buffers.
+			 */
+			process_time =
+				(uint_t)(difftime(time(0), process_start));
+			if (process_time < idle_time)
+				(void) sleep(idle_time - process_time);
+		}
+	}
+
+done:
+	/*
+	 * Make sure to clean house before we exit
+	 */
+	close_all_translogs(config_list);
+	free_buffer_list(&buffer_list);
+	nfsl_freeconfig_list(&config_list);
+
+	(void) process_pidfile(PID_SHUTDOWN);
+
+	return (error);
+}
+
+static void
+short_cleanup(void)
+{
+	if (debug) {
+		(void) fprintf(stderr,
+			"SIGTERM received, setting state to terminate...\n");
+	}
+	quick_cleaning = B_TRUE;
+	keep_running = B_FALSE;
+}
+
+static void
+full_cleanup(void)
+{
+	if (debug) {
+		(void) fprintf(stderr,
+			"SIGHUP received, setting state to shutdown...\n");
+	}
+	quick_cleaning = keep_running = B_FALSE;
+}
+
+/*
+ * Removes nfslogtab entries matching the specified buffer_ent,
+ * if 'inactive_only' is set, then only inactive entries are removed.
+ * The buffer_list and sharepoint list entries are removed appropriately.
+ * Returns 0 on success, error otherwise.
+ */
+static int
+nfslogtab_remove(
+	struct buffer_ent **buffer_list,
+	struct buffer_ent **bep,
+	boolean_t allstates)
+{
+	FILE *fd;
+	int error = 0;
+	struct sharepnt_ent *sep, *next;
+
+	fd = fopen(NFSLOGTAB, "r+");
+	rewind(fd);
+	if (fd == NULL) {
+		error = errno;
+		syslog(LOG_ERR, gettext("%s - %s\n"), NFSLOGTAB,
+			strerror(error));
+		return (error);
+	}
+
+	if (lockf(fileno(fd), F_LOCK, 0L) < 0) {
+		error = errno;
+		syslog(LOG_ERR, gettext("cannot lock %s - %s\n"), NFSLOGTAB,
+			strerror(error));
+		(void) fclose(fd);
+		return (error);
+	}
+
+	for (sep = (*bep)->be_sharepnt; sep != NULL; sep = next) {
+		next = sep->se_next;
+		if (!allstates && sep->se_state == LES_ACTIVE)
+			continue;
+		if (error = logtab_rement(fd, (*bep)->be_name, sep->se_name,
+					NULL, sep->se_state)) {
+			syslog(LOG_ERR, gettext("cannot update %s\n"),
+				NFSLOGTAB);
+			error = EIO;
+			goto errout;
+		}
+		remove_sharepnt_ent(&((*bep)->be_sharepnt), sep);
+	}
+
+	if ((*bep)->be_sharepnt == NULL) {
+		/*
+		 * All sharepoints were removed from NFSLOGTAB.
+		 * Remove this buffer from our list.
+		 */
+		remove_buffer_ent(buffer_list, *bep);
+		*bep = NULL;
+	}
+
+errout: (void) fclose(fd);
+
+	return (error);
+}
+
+/*
+ * Deactivates entries if nfslogtab is older than the boot time.
+ */
+static int
+nfslogtab_deactivate_after_boot(void)
+{
+	FILE *fd;
+	int error = 0;
+
+	fd = fopen(NFSLOGTAB, "r+");
+	if (fd == NULL) {
+		error = errno;
+		if (error != ENOENT) {
+			syslog(LOG_ERR, gettext("%s: %s\n"), NFSLOGTAB,
+				strerror(error));
+			return (-1);
+		}
+		return (0);
+	}
+
+	if (lockf(fileno(fd), F_LOCK, 0L) < 0) {
+		error = errno;
+		syslog(LOG_ERR, gettext("cannot lock %s: %s\n"),
+			NFSLOGTAB, strerror(error));
+		(void) fclose(fd);
+		return (-1);
+	}
+
+	if (logtab_deactivate_after_boot(fd) == -1) {
+		syslog(LOG_ERR, gettext(
+			"Cannot deactivate all entries in %s\n"), NFSLOGTAB);
+		(void) fclose(fd);
+		return (-1);
+	}
+
+	(void) fclose(fd);
+	return (0);
+}
+
+/*
+ * Enables the log file cycling flag.
+ */
+static void
+enable_logcycling(void)
+{
+	need2cycle = TRUE;
+}
+
+/*
+ * Cycle all log files that have been active since the last cycling.
+ * This means it's not simply listed in the configuration file, but
+ * there's information associated with it.
+ */
+static int
+cycle_logs(nfsl_config_t *listp, int max_logs_preserve)
+{
+	nfsl_config_t *clp;
+	void *processed_list = NULL;
+	int error = 0, total_errors = 0;
+
+	for (clp = listp; clp != NULL; clp = clp->nc_next) {
+		error = 0;
+
+		/*
+		 * Process transpath log.
+		 */
+		if (clp->nc_logpath) {
+			if (is_cycle_needed(clp->nc_logpath, &processed_list,
+			    B_FALSE, &error)) {
+				if (clp->nc_transcookie != NULL) {
+					nfslog_close_transactions(
+						&clp->nc_transcookie);
+					assert(clp->nc_transcookie == NULL);
+				}
+				error = cycle_log(clp->nc_logpath,
+					max_logs_preserve);
+			} else if (error)
+				goto errout;
+		}
+		total_errors += error;
+
+		/*
+		 * Process elfpath log.
+		 */
+		if (clp->nc_rpclogpath) {
+			if (is_cycle_needed(clp->nc_rpclogpath, &processed_list,
+			    B_FALSE, &error)) {
+				error = cycle_log(clp->nc_rpclogpath,
+					max_logs_preserve);
+			} else if (error)
+				goto errout;
+		}
+		total_errors += error;
+	}
+
+errout:
+	/*
+	 * Free the list of processed entries.
+	 */
+	(void) is_cycle_needed(NULL, &processed_list, B_TRUE, &error);
+
+	return (total_errors);
+}
+
+/*
+ * Returns TRUE if this log has not yet been cycled, FALSE otherwise.
+ * '*head' points to the list of entries that have been processed.
+ * If this is a new entry, it gets inserted at the beginning of the
+ * list, and returns TRUE.
+ *
+ * The list is freed if 'need2free' is set, and returns FALSE.
+ * Sets 'error' on failure, and returns FALSE.
+ */
+static boolean_t
+is_cycle_needed(char *path, void **list, boolean_t need2free, int *error)
+{
+	struct list {
+		char *log;
+		struct list *next;
+	} *head, *next, *p;
+
+	head = (struct list *)(*list);
+	if (need2free) {
+		/*
+		 * Free the list and return
+		 */
+		for (p = head; p != NULL; p = next) {
+			next = p->next;
+			free(p);
+		}
+		head = NULL;
+		return (B_FALSE);
+	}
+
+	assert(path != NULL);
+	*error = 0;
+	for (p = head; p != NULL; p = p->next) {
+		/*
+		 * Have we seen this before?
+		 */
+		if (strcmp(p->log, path) == 0)
+			return (B_FALSE);
+	}
+
+	/*
+	 * Add it to the list
+	 */
+	if ((p = (struct list *)malloc(sizeof (*p))) == NULL) {
+		*error = ENOMEM;
+		syslog(LOG_ERR, gettext("Cannot allocate memory."));
+		return (B_FALSE);
+	}
+	p->log = path;
+	p->next = head;
+	head = p;
+
+	return (B_TRUE);
+}
+
+/*
+ * cycle given log file.
+ */
+int
+cycle_log(char *filename, int max_logs_preserve)
+{
+	int i;
+	char *file_1;
+	char *file_2;
+	int error = 0;
+	struct stat st;
+
+	if (max_logs_preserve == 0)
+		return (0);
+
+	if (stat(filename, &st) == -1) {
+		if (errno == ENOENT) {
+			/*
+			 * Nothing to cycle.
+			 */
+			return (0);
+		}
+		return (errno);
+	}
+	file_1 = (char *)malloc(PATH_MAX);
+	file_2 = (char *)malloc(PATH_MAX);
+	for (i = max_logs_preserve - 2; i >= 0; i--) {
+		(void) sprintf(file_1, "%s.%d", filename, i);
+		(void) sprintf(file_2, "%s.%d", filename, (i + 1));
+		if (rename(file_1, file_2) == -1) {
+			error = errno;
+			if (error != ENOENT) {
+				syslog(LOG_ERR, gettext(
+				    "cycle_log: can not rename %s to %s: %s"),
+				    file_1, file_2, strerror(error));
+				goto out;
+			}
+		}
+	}
+	(void) sprintf(file_1, "%s.0", filename);
+	if (rename(filename, file_1) == -1) {
+		error = errno;
+		if (error != ENOENT) {
+			syslog(LOG_ERR, gettext(
+				"cycle_log: can not rename %s to %s: %s"),
+				filename, file_1, strerror(error));
+			goto out;
+		}
+	}
+	error = 0;
+
+out:	free(file_1);
+	free(file_2);
+
+	return (error);
+}
+
+/*
+ * If operation = PID_STARTUP then checks the nfslogd.pid file, it is opened
+ * if it exists, read and the pid is checked for an active process. If no
+ * active process is found, the pid of this process is written to the file,
+ * and 0 is returned, otherwise non-zero error is returned.
+ *
+ * If operation = PID_SHUTDOWN then removes the nfslogd.pid file and 0 is
+ * returned.
+ */
+static int
+process_pidfile(enum pidfile_operation op)
+{
+	int fd, read_count;
+	int error = 0;
+	pid_t pid, mypid;
+	char *PidFile = NFSLOGD_PIDFILE;
+	int open_flags;
+
+	if (op == PID_STARTUP)
+		open_flags = O_RDWR | O_CREAT;
+	else {
+		assert(op == PID_SHUTDOWN);
+		open_flags = O_RDWR;
+	}
+
+	if ((fd = open(PidFile, open_flags, 0600)) < 0) {
+		error = errno;
+		if (error == ENOENT && op == PID_SHUTDOWN) {
+			/*
+			 * We were going to remove it anyway
+			 */
+			error = 0;
+			goto out;
+		}
+		(void) fprintf(stderr, gettext(
+			"cannot open or create pid file %s\n"), PidFile);
+		goto out;
+	}
+	if (lockf(fd, F_LOCK, 0) < 0) {
+		error = errno;
+		(void) fprintf(stderr, gettext(
+			"Cannot lock %s - %s\n"), PidFile,
+			strerror(error));
+		goto out;
+	}
+	if ((read_count = read(fd, &pid, sizeof (pid))) < 0) {
+		error = errno;
+		(void) fprintf(stderr, gettext(
+			"Can not read from file %s - %s\n"), PidFile,
+			strerror(error));
+	}
+
+	mypid = getpid();
+	if (op == PID_STARTUP) {
+		if (read_count > 0) {
+			if (kill(pid, 0) == 0) {
+				error = EEXIST;
+				(void) fprintf(stderr, gettext(
+					"Terminated - nfslogd(%ld) already "
+					"running.\n"), pid);
+				goto out;
+			} else if (errno != ESRCH) {
+				error = errno;
+				(void) fprintf(stderr, gettext(
+					"Unexpected error returned %s\n"),
+					strerror(error));
+				goto out;
+			}
+		}
+		pid = mypid;
+		/*
+		 * rewind the file to overwrite old pid
+		 */
+		(void) lseek(fd, 0, SEEK_SET);
+		if (write(fd, &mypid, sizeof (mypid)) < 0) {
+			error = errno;
+			(void) fprintf(stderr, gettext(
+				"Cannot update %s: %s\n"),
+				PidFile, strerror(error));
+		}
+	} else {
+		assert(pid == mypid);
+		if (unlink(PidFile)) {
+			error = errno;
+			syslog(LOG_ERR, gettext("Cannot remove %s: %s"),
+				strerror(error));
+		}
+	}
+out:
+	if (fd >= 0)
+		(void) close(fd);
+	return (error);
+}
+
+/*
+ * Forces a timeout on all open transactions.
+ */
+static void
+transactions_timeout(nfsl_config_t *clp)
+{
+	for (; clp != NULL; clp = clp->nc_next) {
+		if (clp->nc_transcookie != NULL) {
+			nfslog_process_trans_timeout(
+			    (struct nfslog_trans_file *)clp->nc_transcookie,
+			    FALSE);
+		}
+	}
+}
+
+/*
+ * Closes all transaction logs causing outstanding transactions
+ * to be flushed to their respective log.
+ */
+static void
+close_all_translogs(nfsl_config_t *clp)
+{
+	for (; clp != NULL; clp = clp->nc_next) {
+		if (clp->nc_transcookie != NULL) {
+			nfslog_close_transactions(&clp->nc_transcookie);
+			assert(clp->nc_transcookie == NULL);
+		}
+	}
+}