view usr/src/cmd/fs.d/nfs/nfslog/nfslogd.c @ 4:1a15d5aaf794

synchronized with onnv_86 (6202) in onnv-gate
author Koji Uno <koji.uno@sun.com>
date Mon, 31 Aug 2009 14:38:03 +0900
parents c9caec207d52
children
line wrap: on
line source

/*
 * 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	"%Z%%M%	%I%	%E% 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);
		}
	}
}