Blob Blame History Raw
diff -up ./doc/sudoers.cat.orig ./doc/sudoers.cat
--- ./doc/sudoers.cat.orig	2017-03-21 13:31:00.953951199 +0100
+++ ./doc/sudoers.cat	2017-03-21 14:14:18.679116865 +0100
@@ -1549,6 +1549,16 @@ SSUUDDOOEERRSS OOPPTTIIOONN
                        will be truncated and overwritten unless _i_o_l_o_g___f_i_l_e
                        ends in six or more Xs.
 
+     iolog_flush       If set, ssuuddoo will flush I/O log data to disk after each
+                       write instead of buffering it.  This makes it possible
+                       to view the logs in real-time as the program is
+                       executing but may significantly reduce the
+                       effectiveness of I/O log compression.  This flag is _o_f_f
+                       by default.
+
+                       This setting is only supported by version 1.8.20 or
+                       higher.
+
      iolog_group       The group name to look up when setting the group ID on
                        new I/O log files and directories.  By default, I/O log
                        files and directories inherit the group ID of the
@@ -2141,10 +2151,14 @@ II//OO LLOOGG FFIILLEESS
      _s_t_d_e_r_r    standard error to a pipe or redirected to a file
 
      All files other than _l_o_g are compressed in gzip format unless the
-     _c_o_m_p_r_e_s_s___i_o option has been disabled.  Due to buffering, the I/O log data
-     will not be complete until the ssuuddoo command has completed.  The output
-     portion of an I/O log file can be viewed with the sudoreplay(1m) utility,
-     which can also be used to list or search the available logs.
+     _c_o_m_p_r_e_s_s___i_o flag has been disabled.  Due to buffering, it is not normally
+     possible to display the I/O logs in real-time as the program is executing
+     The I/O log data will not be complete until the program run by ssuuddoo has
+     exited or has been terminated by a signal.  The _i_o_l_o_g___f_l_u_s_h flag can be
+     used to disable buffering, in which case I/O log data is written to disk
+     as soon as it is available.  The output portion of an I/O log file can be
+     viewed with the sudoreplay(1m) utility, which can also be used to list or
+     search the available logs.
 
      Note that user input may contain sensitive information such as passwords
      (even if they are not echoed to the screen), which will be stored in the
diff -up ./doc/sudoers.man.in.orig ./doc/sudoers.man.in
--- ./doc/sudoers.man.in.orig	2017-03-21 14:22:33.804283190 +0100
+++ ./doc/sudoers.man.in	2017-03-21 14:22:21.136664667 +0100
@@ -3199,6 +3199,19 @@ ends in six or
 more
 \fRX\fRs.
 .TP 18n
+iolog_flush
+If set,
+\fBsudo\fR
+will flush I/O log data to disk after each write instead of buffering it.
+This makes it possible to view the logs in real-time as the program
+is executing but may significantly reduce the effectiveness of I/O
+log compression.
+This flag is
+\fIoff\fR
+by default.
+.sp
+This setting is only supported by version 1.8.20 or higher.
+.TP 18n
 iolog_group
 The group name to look up when setting the group ID on new I/O log
 files and directories.
@@ -4298,10 +4311,16 @@ All files other than
 \fIlog\fR
 are compressed in gzip format unless the
 \fIcompress_io\fR
-option has been disabled.
-Due to buffering, the I/O log data will not be complete until the
+flag has been disabled.
+Due to buffering, it is not normally possible to display the I/O logs in
+real-time as the program is executing
+The I/O log data will not be complete until the program run by
 \fBsudo\fR
-command has completed.
+has exited or has been terminated by a signal.
+The
+\fIiolog_flush\fR
+flag can be used to disable buffering, in which case I/O log data
+is written to disk as soon as it is available.
 The output portion of an I/O log file can be viewed with the
 sudoreplay(@mansectsu@)
 utility, which can also be used to list or search the available logs.
diff -up ./doc/sudoers.mdoc.in.orig ./doc/sudoers.mdoc.in
--- ./doc/sudoers.mdoc.in.orig	2017-03-21 14:23:46.652089432 +0100
+++ ./doc/sudoers.mdoc.in	2017-03-21 14:26:43.686758162 +0100
@@ -2998,6 +2998,18 @@ overwritten unless
 ends in six or
 more
 .Li X Ns s .
+.It iolog_flush
+If set,
+.Nm sudo
+will flush I/O log data to disk after each write instead of buffering it.
+This makes it possible to view the logs in real-time as the program
+is executing but may significantly reduce the effectiveness of I/O
+log compression.
+This flag is
+.Em off
+by default.
+.Pp
+This setting is only supported by version 1.8.20 or higher.
 .It iolog_group
 The group name to look up when setting the group ID on new I/O log
 files and directories.
@@ -3991,10 +4003,16 @@ All files other than
 .Pa log
 are compressed in gzip format unless the
 .Em compress_io
-option has been disabled.
-Due to buffering, the I/O log data will not be complete until the
-.Nm sudo
-command has completed.
+flag has been disabled.
+Due to buffering, it is not normally possible to display the I/O logs in
+real-time as the program is executing
+The I/O log data will not be complete until the program run by
+.Nm sudo
+has exited or has been terminated by a signal.
+The
+.Em iolog_flush
+flag can be used to disable buffering, in which case I/O log data
+is written to disk as soon as it is available.
 The output portion of an I/O log file can be viewed with the
 .Xr sudoreplay @mansectsu@
 utility, which can also be used to list or search the available logs.
diff -up ./plugins/sudoers/def_data.c.orig ./plugins/sudoers/def_data.c
--- ./plugins/sudoers/def_data.c.orig	2017-03-21 13:24:10.682064806 +0100
+++ ./plugins/sudoers/def_data.c	2017-03-21 13:25:09.805322057 +0100
@@ -447,6 +447,10 @@ struct sudo_defs_types sudo_defs_table[]
        N_("Ignore unknown Defaults entries in sudoers instead of producing a warning"),
        NULL,
     }, {
+	"iolog_flush", T_FLAG,
+	N_("Flush I/O log data to disk immediately instead of buffering it"),
+	NULL,
+    }, {
 	NULL, 0, NULL
     }
 };
diff -up ./plugins/sudoers/def_data.h.orig ./plugins/sudoers/def_data.h
--- ./plugins/sudoers/def_data.h.orig	2017-03-21 13:25:20.489006524 +0100
+++ ./plugins/sudoers/def_data.h	2017-03-21 13:28:09.251022290 +0100
@@ -210,6 +210,8 @@
 #define def_legacy_group_processing (sudo_defs_table[I_LEGACY_GROUP_PROCESSING].sd_un.flag)
 #define I_IGNORE_UNKNOWN_DEFAULTS 105
 #define def_ignore_unknown_defaults (sudo_defs_table[I_IGNORE_UNKNOWN_DEFAULTS].sd_un.flag)
+#define I_IOLOG_FLUSH           106
+#define def_iolog_flush         (sudo_defs_table[I_IOLOG_FLUSH].sd_un.flag)
 
 enum def_tuple {
 	never,
diff -up ./plugins/sudoers/def_data.in.orig ./plugins/sudoers/def_data.in
--- ./plugins/sudoers/def_data.in.orig	2017-03-21 13:28:35.115258413 +0100
+++ ./plugins/sudoers/def_data.in	2017-03-21 13:30:03.239655739 +0100
@@ -331,3 +331,6 @@ legacy_group_processing
 ignore_unknown_defaults
         T_FLAG
         "Ignore unknown Defaults entries in sudoers instead of producing a warning"
+iolog_flush
+	T_FLAG
+	"Flush I/O log data to disk immediately instead of buffering it"
diff -up ./plugins/sudoers/iolog.c.orig ./plugins/sudoers/iolog.c
--- ./plugins/sudoers/iolog.c.orig	2017-03-21 13:12:39.471464160 +0100
+++ ./plugins/sudoers/iolog.c	2017-03-21 13:21:49.279230759 +0100
@@ -709,6 +709,7 @@ iolog_deserialize_info(struct iolog_deta
 
 /*
  * Write the "/log" file that contains the user and command info.
+ * This file is not compressed.
  */
 static bool
 write_info_log(char *pathbuf, size_t len, struct iolog_details *details,
@@ -747,6 +748,57 @@ write_info_log(char *pathbuf, size_t len
     debug_return_bool(ret);
 }
 
+#ifdef HAVE_ZLIB_H
+static const char *
+gzstrerror(gzFile file)
+{
+    int errnum;
+
+    return gzerror(file, &errnum);
+}
+#endif /* HAVE_ZLIB_H */
+
+/*
+ * Write to an I/O log, compressing if iolog_compress is enabled.
+ * If def_iolog_flush is true, flush the buffer immediately.
+ */
+static const char *
+iolog_write(const void *buf, unsigned int len, int idx)
+{
+    const char *errstr = NULL;
+    debug_decl(iolog_write, SUDOERS_DEBUG_PLUGIN)
+
+#ifdef HAVE_ZLIB_H
+    if (iolog_compress) {
+	if (gzwrite(io_log_files[idx].fd.g, buf, len) != (int)len) {
+	    errstr = gzstrerror(io_log_files[idx].fd.g);
+	    goto done;
+	}
+	if (def_iolog_flush) {
+	    if (gzflush(io_log_files[idx].fd.g, Z_SYNC_FLUSH) != Z_OK) {
+		errstr = gzstrerror(io_log_files[idx].fd.g);
+		goto done;
+	    }
+	}
+    } else
+#endif
+    {
+	if (fwrite(buf, 1, len, io_log_files[idx].fd.f) != len) {
+	    errstr = strerror(errno);
+	    goto done;
+	}
+	if (def_iolog_flush) {
+	    if (fflush(io_log_files[idx].fd.f) != 0) {
+		errstr = strerror(errno);
+		goto done;
+	    }
+	}
+    }
+
+done:
+    debug_return_const_str(errstr);
+}
+
 static int
 sudoers_io_open(unsigned int version, sudo_conv_t conversation,
     sudo_printf_t plugin_printf, char * const settings[],
@@ -914,13 +966,15 @@ sudoers_io_version(int verbose)
 
 /*
  * Generic I/O logging function.  Called by the I/O logging entry points.
+ * Returns 1 on success and -1 on error.
  */
 static int
 sudoers_io_log(const char *buf, unsigned int len, int idx)
 {
     struct timeval now, delay;
+    char tbuf[1024];
     const char *errstr = NULL;
-    int ret = true;
+    int ret = -1;
     debug_decl(sudoers_io_version, SUDOERS_DEBUG_PLUGIN)
 
     if (io_log_files[idx].fd.v == NULL) {
@@ -931,41 +985,28 @@ sudoers_io_log(const char *buf, unsigned
 
     gettimeofday(&now, NULL);
 
-#ifdef HAVE_ZLIB_H
-    if (iolog_compress) {
-	if (gzwrite(io_log_files[idx].fd.g, (const voidp)buf, len) != (int)len) {
-	    int errnum;
+    /* Write I/O log file entry. */
+    errstr = iolog_write(buf, len, idx);
+    if (errstr != NULL)
+	goto done;
 
-	    errstr = gzerror(io_log_files[idx].fd.g, &errnum);
-	    ret = -1;
-	}
-    } else
-#endif
-    {
-	if (fwrite(buf, 1, len, io_log_files[idx].fd.f) != len) {
-	    errstr = strerror(errno);
-	    ret = -1;
-	}
-    }
+    /* Write timing file entry. */
     sudo_timevalsub(&now, &last_time, &delay);
-#ifdef HAVE_ZLIB_H
-    if (iolog_compress) {
-	if (gzprintf(io_log_files[IOFD_TIMING].fd.g, "%d %f %u\n", idx,
-	    delay.tv_sec + ((double)delay.tv_usec / 1000000), len) == 0) {
-	    int errnum;
-
-	    errstr = gzerror(io_log_files[IOFD_TIMING].fd.g, &errnum);
-	    ret = -1;
-	}
-    } else
-#endif
-    {
-	if (fprintf(io_log_files[IOFD_TIMING].fd.f, "%d %f %u\n", idx,
-	    delay.tv_sec + ((double)delay.tv_usec / 1000000), len) < 0) {
-	    errstr = strerror(errno);
-	    ret = -1;
-	}
+    len = (unsigned int)snprintf(tbuf, sizeof(tbuf), "%d %f %u\n", idx,
+	delay.tv_sec + ((double)delay.tv_usec / 1000000), len);
+    if (len >= sizeof(tbuf)) {
+	/* Not actually possible due to the size of tbuf[]. */
+	errstr = strerror(EOVERFLOW);
+	goto done;
     }
+    errstr = iolog_write(tbuf, len, IOFD_TIMING);
+    if (errstr != NULL)
+	goto done;
+
+    /* Success. */
+    ret = 1;
+
+done:
     last_time.tv_sec = now.tv_sec;
     last_time.tv_usec = now.tv_usec;
 
@@ -979,7 +1020,7 @@ sudoers_io_log(const char *buf, unsigned
 
 	/* Ignore errors if they occur if the policy says so. */
 	if (iolog_details.ignore_iolog_errors)
-	    ret = true;
+	    ret = 1;
     }
 
     debug_return_int(ret);