4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright (c) 1999-2000 by Sun Microsystems, Inc.
24 * All rights reserved.
27 #pragma ident "%Z%%M% %I% %E% SMI"
30 * Postprocessor for NFS server logging.
32 #include <arpa/inet.h>
37 #include <netinet/in.h>
48 #include <sys/types.h>
49 #include <sys/utsname.h>
51 #include <sys/resource.h>
52 #include <rpc/clnt_stat.h>
54 #include <nfs/export.h>
55 #include <nfs/nfs_log.h>
58 #include "buffer_list.h"
59 #include "../lib/nfslog_config.h"
60 #include "../lib/nfslogtab.h"
62 enum pidfile_operation
{
63 PID_STARTUP
, PID_SHUTDOWN
66 static int nfslogtab_deactivate_after_boot(void);
68 nfslogtab_remove(struct buffer_ent
**, struct buffer_ent
**, boolean_t
);
69 static int cycle_logs(nfsl_config_t
*, int);
70 static void enable_logcycling(void);
71 static int process_pidfile(enum pidfile_operation
);
72 static void short_cleanup(void);
73 static void full_cleanup(void);
74 static void transactions_timeout(nfsl_config_t
*);
75 static void close_all_translogs(nfsl_config_t
*);
76 int cycle_log(char *, int);
77 static boolean_t
is_cycle_needed(char *, void **, boolean_t
, int *);
80 * Configuration information.
84 boolean_t test
= B_FALSE
;
85 time_t mapping_update_interval
= MAPPING_UPDATE_INTERVAL
;
86 /* prune_timeout measures how old a database entry must be to be pruned */
87 time_t prune_timeout
= (SECSPERHOUR
* 7 * 24);
88 int max_logs_preserve
= MAX_LOGS_PRESERVE
;
89 uint_t idle_time
= IDLE_TIME
;
90 static mode_t Umask
= NFSLOG_UMASK
;
91 static long cycle_frequency
= CYCLE_FREQUENCY
;
92 /* prune_frequency measures how often should prune_dbs be called */
93 static long prune_frequency
= (SECSPERHOUR
* 24);
94 static int min_size
= MIN_PROCESSING_SIZE
;
95 static volatile bool_t need2cycle
= FALSE
;
96 static volatile bool_t need2prune
= FALSE
;
97 boolean_t keep_running
= B_TRUE
;
98 boolean_t quick_cleaning
= B_FALSE
;
102 main(int argc
, char **argv
)
109 timestruc_t logtab_update
;
110 time_t process_start
, last_prune
= time(0);
111 time_t last_cycle
= time(0); /* last time logs were cycled */
112 int processed
, buffers_processed
;
113 struct buffer_ent
*buffer_list
= NULL
, *bep
, *next
;
114 nfsl_config_t
*config_list
= NULL
;
115 char *fhtable_to_prune
= NULL
;
118 * Check to make sure user is root.
120 if (geteuid() != 0) {
121 (void) fprintf(stderr
, gettext("%s must be run as root\n"),
127 * Read defaults file.
129 if (defopen(NFSLOG_OPTIONS_FILE
) == 0) {
130 if ((defp
= defread("DEBUG=")) != NULL
) {
133 (void) printf("debug=%d\n", debug
);
135 if ((defp
= defread("TEST=")) != NULL
) {
136 if (strcmp(defp
, "TRUE") == 0)
140 (void) printf("test=TRUE\n");
142 (void) printf("test=FALSE\n");
146 * Set Umask for log and fhtable creation.
148 if ((defp
= defread("UMASK=")) != NULL
) {
149 if (sscanf(defp
, "%lo", &Umask
) != 1)
150 Umask
= NFSLOG_UMASK
;
153 * Minimum size buffer should reach before processing.
155 if ((defp
= defread("MIN_PROCESSING_SIZE=")) != NULL
) {
156 min_size
= atoi(defp
);
158 (void) printf("min_size=%d\n", min_size
);
161 * Number of seconds the daemon should
162 * sleep waiting for more work.
164 if ((defp
= defread("IDLE_TIME=")) != NULL
) {
165 idle_time
= (uint_t
)atoi(defp
);
167 (void) printf("idle_time=%d\n", idle_time
);
170 * Maximum number of logs to preserve.
172 if ((defp
= defread("MAX_LOGS_PRESERVE=")) != NULL
) {
173 max_logs_preserve
= atoi(defp
);
175 (void) printf("max_logs_preserve=%d\n",
180 * Frequency of atime updates.
182 if ((defp
= defread("MAPPING_UPDATE_INTERVAL=")) != NULL
) {
183 mapping_update_interval
= atoi(defp
);
185 (void) printf("mapping_update_interval=%ld\n",
186 mapping_update_interval
);
190 * Time to remove entries
192 if ((defp
= defread("PRUNE_TIMEOUT=")) != NULL
) {
194 * Prune timeout is in hours but we want
195 * deal with the time in seconds internally.
197 prune_timeout
= atoi(defp
);
198 prune_timeout
*= SECSPERHOUR
;
199 if (prune_timeout
< prune_frequency
)
200 prune_frequency
= prune_timeout
;
202 (void) printf("prune_timeout=%ld\n",
207 * fhtable to prune when start (for debug/test purposes)
209 if ((defp
= defread("PRUNE_FHTABLE=")) != NULL
) {
211 * Specify full pathname of fhtable to prune before
212 * any processing is to be done
214 if (fhtable_to_prune
= malloc(strlen(defp
) + 1)) {
215 (void) strcpy(fhtable_to_prune
, defp
);
217 (void) printf("fhtable to prune=%s\n",
221 syslog(LOG_ERR
, gettext(
222 "malloc fhtable_to_prune error %s\n"),
227 * Log cycle frequency.
229 if ((defp
= defread("CYCLE_FREQUENCY=")) != NULL
) {
230 cycle_frequency
= atol(defp
);
232 (void) printf("cycle_frequency=%ld\n",
237 * defopen of NULL closes the open defaults file.
239 (void) defopen(NULL
);
242 if (Umask
> ((mode_t
)0777))
243 Umask
= NFSLOG_UMASK
;
246 if (getrlimit(RLIMIT_FSIZE
, &rl
) < 0) {
248 (void) fprintf(stderr
, gettext(
249 "getrlimit failed error is %d - %s\n"),
250 error
, strerror(error
));
253 if (min_size
< 0 || min_size
> rl
.rlim_cur
) {
254 (void) fprintf(stderr
, gettext(
255 "MIN_PROCESSING_SIZE out of range, should be >= 0 and "
256 "< %d. Check %s.\n"), rl
.rlim_cur
, NFSLOG_OPTIONS_FILE
);
259 if (idle_time
> INT_MAX
) {
260 (void) fprintf(stderr
, gettext(
261 "IDLE_TIME out of range, should be >= 0 and "
262 "< %d. Check %s.\n"), INT_MAX
, NFSLOG_OPTIONS_FILE
);
265 if (max_logs_preserve
< 0 || max_logs_preserve
> INT_MAX
) {
266 (void) fprintf(stderr
, gettext(
267 "MAX_LOGS_PRESERVE out of range, should be >= 0 and "
268 "< %d. Check %s.\n"), INT_MAX
, NFSLOG_OPTIONS_FILE
);
271 if (mapping_update_interval
< 0|| mapping_update_interval
> INT_MAX
) {
272 (void) fprintf(stderr
, gettext(
273 "MAPPING_UPDATE_INTERVAL out of range, "
274 "should be >= 0 and "
275 "< %d. Check %s.\n"), INT_MAX
, NFSLOG_OPTIONS_FILE
);
278 if (cycle_frequency
< 0 || cycle_frequency
> INT_MAX
) {
279 (void) fprintf(stderr
, gettext(
280 "CYCLE_FREQUENCY out of range, should be >= 0 and "
281 "< %d. Check %s.\n"), INT_MAX
, NFSLOG_OPTIONS_FILE
);
284 /* get value in seconds */
285 cycle_frequency
= cycle_frequency
* 60 * 60;
288 * If we dump core, it will be /core
291 (void) fprintf(stderr
, gettext("chdir /: %s"), strerror(errno
));
294 * Config errors to stderr
296 nfsl_errs_to_syslog
= B_FALSE
;
301 (void) fprintf(stderr
, gettext("%s: fork failure\n"),
308 * Config errors to syslog
310 nfsl_errs_to_syslog
= B_TRUE
;
313 (void) setlocale(LC_ALL
, "");
314 #if !defined(TEXT_DOMAIN)
315 #define TEXT_DOMAIN "SYS_TEST"
317 (void) textdomain(TEXT_DOMAIN
);
320 * Check to see if nfslogd is already running.
322 if (process_pidfile(PID_STARTUP
) != 0) {
326 (void) sigset(SIGUSR1
, (void (*)(int))enable_logcycling
);
327 (void) sigset(SIGHUP
, (void (*)(int))full_cleanup
);
328 (void) sigset(SIGTERM
, (void(*)(int))short_cleanup
);
332 * Close existing file descriptors, open "/dev/null" as
333 * standard input, output, and error, and detach from
334 * controlling terminal.
336 if (!debug
&& !test
) {
338 (void) open("/dev/null", O_RDONLY
);
339 (void) open("/dev/null", O_WRONLY
);
345 openlog(argv
[0], LOG_PID
, LOG_DAEMON
);
347 public_fh
.fh_len
= NFS_FHMAXDATA
;
348 public_fh
.fh_xlen
= NFS_FHMAXDATA
;
351 * Call once at startup to handle the nfslogtab
353 if (nfslogtab_deactivate_after_boot() == -1)
357 * Get a list of buffers that need to be processed.
359 if (error
= getbuffer_list(&buffer_list
, &logtab_update
)) {
360 syslog(LOG_ERR
, gettext("Could not read %s: %s"),
361 NFSLOGTAB
, strerror(error
));
366 * Get the configuration list.
368 if (error
= nfsl_getconfig_list(&config_list
)) {
369 syslog(LOG_ERR
, gettext(
370 "Could not obtain configuration list: %s"),
376 * loop to process the work being generated by the NFS server
378 while (keep_running
) {
379 buffers_processed
= 0;
380 (void) checkbuffer_list(&buffer_list
, &logtab_update
);
382 while (buffer_list
== NULL
) {
386 (void) sleep(idle_time
);
390 * We have been interrupted and asked to
391 * flush our transactions and exit.
393 close_all_translogs(config_list
);
396 (void) checkbuffer_list(&buffer_list
, &logtab_update
);
399 process_start
= time(0);
401 if (error
= nfsl_checkconfig_list(&config_list
, NULL
)) {
402 syslog(LOG_ERR
, gettext(
403 "Could not update configuration list: %s"),
405 nfsl_freeconfig_list(&config_list
);
409 if (difftime(time(0), last_cycle
) > cycle_frequency
)
412 error
= cycle_logs(config_list
, max_logs_preserve
);
414 syslog(LOG_WARNING
, gettext(
415 "One or more logfiles couldn't be cycled, "
416 "continuing regular processing"));
419 last_cycle
= time(0);
421 if (difftime(time(0), last_prune
) > prune_frequency
)
423 if (need2prune
|| fhtable_to_prune
) {
424 error
= prune_dbs(fhtable_to_prune
);
426 syslog(LOG_WARNING
, gettext(
427 "Error in cleaning database files"));
430 last_prune
= time(0);
431 /* After the first time, use the normal procedure */
432 free(fhtable_to_prune
);
433 fhtable_to_prune
= NULL
;
436 for (bep
= buffer_list
; bep
!= NULL
; bep
= next
) {
439 error
= process_buffer(bep
, &config_list
,
440 min_size
, idle_time
, &processed
);
441 if (error
== 0 && processed
) {
443 syslog(LOG_ERR
, gettext(
445 "processed successfully."),
449 nfslogtab_remove(&buffer_list
, &bep
, B_FALSE
);
450 } else if (error
== ENOENT
) {
451 syslog(LOG_ERR
, gettext("Removed entry"
452 "\t\"%s\t%s\t%d\" from %s"),
454 bep
->be_sharepnt
->se_name
,
455 bep
->be_sharepnt
->se_state
,
458 nfslogtab_remove(&buffer_list
, &bep
, B_TRUE
);
459 } else if (error
&& error
!= bep
->be_error
) {
461 * An error different from what we've reported
464 syslog(LOG_ERR
, gettext(
465 "Cannot process buffer file '%s' - "
466 "will retry on every iteration."),
471 bep
->be_error
= error
;
472 buffers_processed
+= processed
;
475 transactions_timeout(config_list
);
481 * Sleep idle_time minus however long it took us
482 * to process the buffers.
485 (uint_t
)(difftime(time(0), process_start
));
486 if (process_time
< idle_time
)
487 (void) sleep(idle_time
- process_time
);
493 * Make sure to clean house before we exit
495 close_all_translogs(config_list
);
496 free_buffer_list(&buffer_list
);
497 nfsl_freeconfig_list(&config_list
);
499 (void) process_pidfile(PID_SHUTDOWN
);
508 (void) fprintf(stderr
,
509 "SIGTERM received, setting state to terminate...\n");
511 quick_cleaning
= B_TRUE
;
512 keep_running
= B_FALSE
;
519 (void) fprintf(stderr
,
520 "SIGHUP received, setting state to shutdown...\n");
522 quick_cleaning
= keep_running
= B_FALSE
;
526 * Removes nfslogtab entries matching the specified buffer_ent,
527 * if 'inactive_only' is set, then only inactive entries are removed.
528 * The buffer_list and sharepoint list entries are removed appropriately.
529 * Returns 0 on success, error otherwise.
533 struct buffer_ent
**buffer_list
,
534 struct buffer_ent
**bep
,
539 struct sharepnt_ent
*sep
, *next
;
541 fd
= fopen(NFSLOGTAB
, "r+");
545 syslog(LOG_ERR
, gettext("%s - %s\n"), NFSLOGTAB
,
550 if (lockf(fileno(fd
), F_LOCK
, 0L) < 0) {
552 syslog(LOG_ERR
, gettext("cannot lock %s - %s\n"), NFSLOGTAB
,
558 for (sep
= (*bep
)->be_sharepnt
; sep
!= NULL
; sep
= next
) {
560 if (!allstates
&& sep
->se_state
== LES_ACTIVE
)
562 if (error
= logtab_rement(fd
, (*bep
)->be_name
, sep
->se_name
,
563 NULL
, sep
->se_state
)) {
564 syslog(LOG_ERR
, gettext("cannot update %s\n"),
569 remove_sharepnt_ent(&((*bep
)->be_sharepnt
), sep
);
572 if ((*bep
)->be_sharepnt
== NULL
) {
574 * All sharepoints were removed from NFSLOGTAB.
575 * Remove this buffer from our list.
577 remove_buffer_ent(buffer_list
, *bep
);
581 errout
: (void) fclose(fd
);
587 * Deactivates entries if nfslogtab is older than the boot time.
590 nfslogtab_deactivate_after_boot(void)
595 fd
= fopen(NFSLOGTAB
, "r+");
598 if (error
!= ENOENT
) {
599 syslog(LOG_ERR
, gettext("%s: %s\n"), NFSLOGTAB
,
606 if (lockf(fileno(fd
), F_LOCK
, 0L) < 0) {
608 syslog(LOG_ERR
, gettext("cannot lock %s: %s\n"),
609 NFSLOGTAB
, strerror(error
));
614 if (logtab_deactivate_after_boot(fd
) == -1) {
615 syslog(LOG_ERR
, gettext(
616 "Cannot deactivate all entries in %s\n"), NFSLOGTAB
);
626 * Enables the log file cycling flag.
629 enable_logcycling(void)
635 * Cycle all log files that have been active since the last cycling.
636 * This means it's not simply listed in the configuration file, but
637 * there's information associated with it.
640 cycle_logs(nfsl_config_t
*listp
, int max_logs_preserve
)
643 void *processed_list
= NULL
;
644 int error
= 0, total_errors
= 0;
646 for (clp
= listp
; clp
!= NULL
; clp
= clp
->nc_next
) {
650 * Process transpath log.
652 if (clp
->nc_logpath
) {
653 if (is_cycle_needed(clp
->nc_logpath
, &processed_list
,
655 if (clp
->nc_transcookie
!= NULL
) {
656 nfslog_close_transactions(
657 &clp
->nc_transcookie
);
658 assert(clp
->nc_transcookie
== NULL
);
660 error
= cycle_log(clp
->nc_logpath
,
665 total_errors
+= error
;
668 * Process elfpath log.
670 if (clp
->nc_rpclogpath
) {
671 if (is_cycle_needed(clp
->nc_rpclogpath
, &processed_list
,
673 error
= cycle_log(clp
->nc_rpclogpath
,
678 total_errors
+= error
;
683 * Free the list of processed entries.
685 (void) is_cycle_needed(NULL
, &processed_list
, B_TRUE
, &error
);
687 return (total_errors
);
691 * Returns TRUE if this log has not yet been cycled, FALSE otherwise.
692 * '*head' points to the list of entries that have been processed.
693 * If this is a new entry, it gets inserted at the beginning of the
694 * list, and returns TRUE.
696 * The list is freed if 'need2free' is set, and returns FALSE.
697 * Sets 'error' on failure, and returns FALSE.
700 is_cycle_needed(char *path
, void **list
, boolean_t need2free
, int *error
)
707 head
= (struct list
*)(*list
);
710 * Free the list and return
712 for (p
= head
; p
!= NULL
; p
= next
) {
720 assert(path
!= NULL
);
722 for (p
= head
; p
!= NULL
; p
= p
->next
) {
724 * Have we seen this before?
726 if (strcmp(p
->log
, path
) == 0)
733 if ((p
= (struct list
*)malloc(sizeof (*p
))) == NULL
) {
735 syslog(LOG_ERR
, gettext("Cannot allocate memory."));
746 * cycle given log file.
749 cycle_log(char *filename
, int max_logs_preserve
)
757 if (max_logs_preserve
== 0)
760 if (stat(filename
, &st
) == -1) {
761 if (errno
== ENOENT
) {
769 file_1
= (char *)malloc(PATH_MAX
);
770 file_2
= (char *)malloc(PATH_MAX
);
771 for (i
= max_logs_preserve
- 2; i
>= 0; i
--) {
772 (void) sprintf(file_1
, "%s.%d", filename
, i
);
773 (void) sprintf(file_2
, "%s.%d", filename
, (i
+ 1));
774 if (rename(file_1
, file_2
) == -1) {
776 if (error
!= ENOENT
) {
777 syslog(LOG_ERR
, gettext(
778 "cycle_log: can not rename %s to %s: %s"),
779 file_1
, file_2
, strerror(error
));
784 (void) sprintf(file_1
, "%s.0", filename
);
785 if (rename(filename
, file_1
) == -1) {
787 if (error
!= ENOENT
) {
788 syslog(LOG_ERR
, gettext(
789 "cycle_log: can not rename %s to %s: %s"),
790 filename
, file_1
, strerror(error
));
803 * If operation = PID_STARTUP then checks the nfslogd.pid file, it is opened
804 * if it exists, read and the pid is checked for an active process. If no
805 * active process is found, the pid of this process is written to the file,
806 * and 0 is returned, otherwise non-zero error is returned.
808 * If operation = PID_SHUTDOWN then removes the nfslogd.pid file and 0 is
812 process_pidfile(enum pidfile_operation op
)
817 char *PidFile
= NFSLOGD_PIDFILE
;
820 if (op
== PID_STARTUP
)
821 open_flags
= O_RDWR
| O_CREAT
;
823 assert(op
== PID_SHUTDOWN
);
827 if ((fd
= open(PidFile
, open_flags
, 0600)) < 0) {
829 if (error
== ENOENT
&& op
== PID_SHUTDOWN
) {
831 * We were going to remove it anyway
836 (void) fprintf(stderr
, gettext(
837 "cannot open or create pid file %s\n"), PidFile
);
840 if (lockf(fd
, F_LOCK
, 0) < 0) {
842 (void) fprintf(stderr
, gettext(
843 "Cannot lock %s - %s\n"), PidFile
,
847 if ((read_count
= read(fd
, &pid
, sizeof (pid
))) < 0) {
849 (void) fprintf(stderr
, gettext(
850 "Can not read from file %s - %s\n"), PidFile
,
855 if (op
== PID_STARTUP
) {
856 if (read_count
> 0) {
857 if (kill(pid
, 0) == 0) {
859 (void) fprintf(stderr
, gettext(
860 "Terminated - nfslogd(%ld) already "
863 } else if (errno
!= ESRCH
) {
865 (void) fprintf(stderr
, gettext(
866 "Unexpected error returned %s\n"),
873 * rewind the file to overwrite old pid
875 (void) lseek(fd
, 0, SEEK_SET
);
876 if (write(fd
, &mypid
, sizeof (mypid
)) < 0) {
878 (void) fprintf(stderr
, gettext(
879 "Cannot update %s: %s\n"),
880 PidFile
, strerror(error
));
883 assert(pid
== mypid
);
884 if (unlink(PidFile
)) {
886 syslog(LOG_ERR
, gettext("Cannot remove %s: %s"),
897 * Forces a timeout on all open transactions.
900 transactions_timeout(nfsl_config_t
*clp
)
902 for (; clp
!= NULL
; clp
= clp
->nc_next
) {
903 if (clp
->nc_transcookie
!= NULL
) {
904 nfslog_process_trans_timeout(
905 (struct nfslog_trans_file
*)clp
->nc_transcookie
,
912 * Closes all transaction logs causing outstanding transactions
913 * to be flushed to their respective log.
916 close_all_translogs(nfsl_config_t
*clp
)
918 for (; clp
!= NULL
; clp
= clp
->nc_next
) {
919 if (clp
->nc_transcookie
!= NULL
) {
920 nfslog_close_transactions(&clp
->nc_transcookie
);
921 assert(clp
->nc_transcookie
== NULL
);