summaryrefslogtreecommitdiff
path: root/usr/src/cmd/logadm
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/logadm')
-rw-r--r--usr/src/cmd/logadm/main.c83
-rw-r--r--usr/src/cmd/logadm/tester25
2 files changed, 92 insertions, 16 deletions
diff --git a/usr/src/cmd/logadm/main.c b/usr/src/cmd/logadm/main.c
index d1f105f7be..b03782c3c3 100644
--- a/usr/src/cmd/logadm/main.c
+++ b/usr/src/cmd/logadm/main.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, Joyent, Inc. All rights reserved.
*
* logadm/main.c -- main routines for logadm
*
@@ -37,6 +38,7 @@
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/filio.h>
+#include <sys/sysmacros.h>
#include <time.h>
#include <utime.h>
#include "err.h"
@@ -828,7 +830,7 @@ rotateto(struct fn *fnp, struct opts *opts, int n, struct fn *recentlog,
fn_free(dirname);
/* do the rename */
- if (opts_count(opts, "c") != NULL) {
+ if (n == 0 && opts_count(opts, "c") != NULL) {
docopytruncate(opts, fn_s(fnp), fn_s(newfile));
} else if (n == 0 && opts_count(opts, "M")) {
struct fn *rawcmd = fn_new(opts_optarg(opts, "M"));
@@ -1100,10 +1102,12 @@ docmd(struct opts *opts, const char *msg, const char *cmd,
static void
docopytruncate(struct opts *opts, const char *file, const char *file_copy)
{
- int fi, fo, len;
- char buf[4096];
+ int fi, fo;
+ char buf[128 * 1024];
struct stat s;
struct utimbuf times;
+ off_t written = 0, rem, last = 0, thresh = 1024 * 1024;
+ ssize_t len;
/* print info if necessary */
if (opts_count(opts, "vn") != NULL) {
@@ -1129,7 +1133,7 @@ docopytruncate(struct opts *opts, const char *file, const char *file_copy)
}
/* create new file for copy destination with correct attributes */
- if ((fo = open(file_copy, O_CREAT|O_APPEND|O_WRONLY, s.st_mode)) < 0) {
+ if ((fo = open(file_copy, O_CREAT|O_TRUNC|O_WRONLY, s.st_mode)) < 0) {
err(EF_SYS, "cannot create file: %s", file_copy);
(void) close(fi);
return;
@@ -1137,6 +1141,77 @@ docopytruncate(struct opts *opts, const char *file, const char *file_copy)
(void) fchown(fo, s.st_uid, s.st_gid);
+ /*
+ * Now we'll loop, reading the log file and writing it to our copy
+ * until the bytes remaining are beneath our atomicity threshold -- at
+ * which point we'll lock the file and copy the remainder atomically.
+ * The body of this loop is non-atomic with respect to writers, the
+ * rationale being that total atomicity (that is, locking the file for
+ * the entire duration of the copy) comes at too great a cost for a
+ * large log file, as the writer (i.e., the daemon whose log is being
+ * rolled) can be blocked for an unacceptable duration. (For one
+ * particularly loquacious daemon, this period was observed to be
+ * several minutes in length -- a time so long that it induced
+ * additional failures in dependent components.) Note that this means
+ * that if the log file is not always appended to -- if it is opened
+ * without O_APPEND or otherwise truncated outside of logadm -- this
+ * will result in our log snapshot being incorrect. But of course, in
+ * either of these cases, the use of logadm at all is itself
+ * suspect...
+ */
+ do {
+ if (fstat(fi, &s) < 0) {
+ err(EF_SYS, "cannot stat: %s", file);
+ (void) close(fi);
+ (void) close(fo);
+ (void) remove(file_copy);
+ return;
+ }
+
+ if ((rem = s.st_size - written) < thresh) {
+ if (rem >= 0)
+ break;
+
+ /*
+ * If the file became smaller, something fishy is going
+ * on; we'll truncate our copy, reset our seek offset
+ * and break into the atomic copy.
+ */
+ (void) ftruncate(fo, 0);
+ (void) lseek(fo, 0, SEEK_SET);
+ (void) lseek(fi, 0, SEEK_SET);
+ break;
+ }
+
+ if (written != 0 && rem > last) {
+ /*
+ * We're falling behind -- this file is getting bigger
+ * faster than we're able to write it; break out and
+ * lock the file to block the writer.
+ */
+ break;
+ }
+
+ last = rem;
+
+ while (rem > 0) {
+ if ((len = read(fi, buf, MIN(sizeof (buf), rem))) <= 0)
+ break;
+
+ if (write(fo, buf, len) == len) {
+ rem -= len;
+ written += len;
+ continue;
+ }
+
+ err(EF_SYS, "cannot write into file %s", file_copy);
+ (void) close(fi);
+ (void) close(fo);
+ (void) remove(file_copy);
+ return;
+ }
+ } while (len >= 0);
+
/* lock log file so that nobody can write into it before we are done */
if (fchmod(fi, s.st_mode|S_ISGID) < 0)
err(EF_SYS, "cannot set mandatory lock bit for: %s", file);
diff --git a/usr/src/cmd/logadm/tester b/usr/src/cmd/logadm/tester
index 1ff4985e6d..ab7c32cd0b 100644
--- a/usr/src/cmd/logadm/tester
+++ b/usr/src/cmd/logadm/tester
@@ -21,6 +21,7 @@
#
#
# Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2013, Joyent, Inc. All rights reserved.
#
#
# tester - run logadm tests
@@ -281,7 +282,7 @@ sub set_testconffile {
# logadm typically runs early every morning via an entry in
# root's crontab (see crontab(1)).
#
-/var/adm/messages -C 4 -P 'Thu Nov 1 16:56:42 2001' -a 'kill -HUP `cat /var/run/syslog.pid`'
+/var/adm/messages -C 4 -P 'Thu Nov 1 16:56:42 2001' -a 'kill -HUP `cat /var/run/*syslog*pid`'
/var/cron/log -s 512k -t /var/cron/olog
/var/lp/logs/lpsched -C 2 -N -t '$file.$N'
#
@@ -289,7 +290,7 @@ sub set_testconffile {
#
/var/adm/pacct -C 0 -a '/usr/lib/acct/accton pacct' -g adm -m 664 -o adm -p never
apache -C 24 -a '/usr/apache/bin/apachectl graceful' -p 1m -t '/var/apache/old-logs/$basename.%Y-%m' '/var/apache/logs/*{access,error}_log'
-/var/log/syslog -C 8 -P 'Thu Nov 1 09:16:38 2001' -a 'kill -HUP `cat /var/run/syslog.pid`'
+/var/log/syslog -C 8 -P 'Thu Nov 1 09:16:38 2001' -a 'kill -HUP `cat /var/run/*syslog*pid`'
/var/apache/logs/access_log -P 'Thu Nov 1 08:27:56 2001'
/var/apache/logs/error_log -P 'Thu Nov 1 08:27:56 2001'
/var/apache/logs/suexec_log -P 'Thu Nov 1 08:27:56 2001'
@@ -818,12 +819,12 @@ sub logadmV1 {
set_testconffile;
set_file('std.out.expect', <<'EOF');
-/var/adm/messages -C 4 -P 'Thu Nov 1 16:56:42 2001' -a 'kill -HUP `cat /var/run/syslog.pid`'
+/var/adm/messages -C 4 -P 'Thu Nov 1 16:56:42 2001' -a 'kill -HUP `cat /var/run/*syslog*pid`'
/var/cron/log -s 512k -t /var/cron/olog
/var/lp/logs/lpsched -C 2 -N -t '$file.$N'
/var/adm/pacct -C 0 -a '/usr/lib/acct/accton pacct' -g adm -m 664 -o adm -p never
apache -C 24 -a '/usr/apache/bin/apachectl graceful' -p 1m -t '/var/apache/old-logs/$basename.%Y-%m' '/var/apache/logs/*{access,error}_log'
-/var/log/syslog -C 8 -P 'Thu Nov 1 09:16:38 2001' -a 'kill -HUP `cat /var/run/syslog.pid`'
+/var/log/syslog -C 8 -P 'Thu Nov 1 09:16:38 2001' -a 'kill -HUP `cat /var/run/*syslog*pid`'
/var/apache/logs/access_log -P 'Thu Nov 1 08:27:56 2001'
/var/apache/logs/error_log -P 'Thu Nov 1 08:27:56 2001'
/var/apache/logs/suexec_log -P 'Thu Nov 1 08:27:56 2001'
@@ -1179,8 +1180,8 @@ sub logadm7 {
# logadm typically runs early every morning via an entry in
# root's crontab (see crontab(1)).
#
-dir1/syslog -C 8 -a 'echo kill -HUP `cat /etc/syslog.pid` >> cmd.out'
-dir2/messages -C 4 -a 'echo kill -HUP `cat /etc/syslog.pid` >> cmd.out'
+dir1/syslog -C 8 -a 'echo kill -HUP `cat /var/run/*syslog*pid` >> cmd.out'
+dir2/messages -C 4 -a 'echo kill -HUP `cat /var/run/*syslog*pid` >> cmd.out'
#
# The entry below is used by turnacct(1M)
#
@@ -1189,7 +1190,7 @@ EOF
system("/bin/cp logadm.conf logadm.conf.orig");
- $pid=`cat /etc/syslog.pid`;
+ $pid=`cat /var/run/*syslog*pid`;
chomp $pid;
set_file('cmd.out.expect', <<"EOF");
kill -HUP $pid
@@ -1260,7 +1261,7 @@ $bindir/logadm -f logadm.conf -F logadm.timestamps dir1/syslog dir2/messages >st
echo something > dir1/syslog
echo something > dir2/messages
$bindir/logadm -f logadm.conf -F logadm.timestamps >std.out3 2>std.err3 || exit 1
-exec $bindir/logadm -f logadm.conf -F logadm.timestamps dir2/messages -p now -a 'echo second kill -HUP `cat /etc/syslog.pid` >> cmd.out' >std.out4 2>std.err4
+exec $bindir/logadm -f logadm.conf -F logadm.timestamps dir2/messages -p now -a 'echo second kill -HUP `cat /var/run/*syslog*pid` >> cmd.out' >std.out4 2>std.err4
EOF
}
@@ -1748,8 +1749,8 @@ sub logadm14 {
# logadm typically runs early every morning via an entry in
# root's crontab (see crontab(1)).
#
-dir1/syslog -C 8 -a 'echo kill -HUP `cat /etc/syslog.pid` >> cmd.out'
-dir2/messages -C 4 -a 'echo kill -HUP `cat /etc/syslog.pid` >> cmd.out'
+dir1/syslog -C 8 -a 'echo kill -HUP `cat /var/run/*syslog*pid` >> cmd.out'
+dir2/messages -C 4 -a 'echo kill -HUP `cat /var/run/*syslog*pid` >> cmd.out'
#
# The entry below is used by turnacct(1M)
#
@@ -1806,7 +1807,7 @@ EOF
chmod 664 dir2/messages
# processing logname: /var/adm/pacct
# using default template: $file.$n
-sh -c echo kill -HUP `cat /etc/syslog.pid` >> cmd.out # -a cmd
+sh -c echo kill -HUP `cat /var/run/*syslog*pid` >> cmd.out # -a cmd
# logadm.conf and logadm.timestamps unchanged
EOF
@@ -2082,7 +2083,7 @@ EOF
sub logadm20 {
set_file('logadm.conf', <<'EOF');
# non-trivial entry
-/var/log/syslog -C 8 -a 'kill -HUP `cat /var/run/syslog.pid`'
+/var/log/syslog -C 8 -a 'kill -HUP `cat /var/run/*syslog*pid`'
EOF
set_file('std.err.expect', <<'EOF');