aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--Makefile39
-rw-r--r--addts.178
-rw-r--r--addts.c125
4 files changed, 247 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..426b81a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+addts
+.emacs*
+Makefile
+*~
+*.tar.gz
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..8b6dec7
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,39 @@
+PREFIX=/usr/local
+PROJECT=addts
+VERSION=1.0
+
+# Installation directories:
+INSTALLDIR=$(PREFIX)/bin
+MANDIR=$(PREFIX)/share/man
+MAN1DIR=$(MANDIR)/man1
+
+addts: addts.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -oaddts addts.c
+
+clean:
+ rm -f *.o addts
+
+install: addts
+ install addts $(DESTDIR)$(INSTALLDIR)
+ install addts.1 $(DESTDIR)$(MAN1DIR)
+
+DISTDIR=$(PROJECT)-$(VERSION)
+DISTFILES=addts.c addts.1 Makefile
+
+distdir:
+ test -d $(DISTDIR) || mkdir $(DISTDIR)
+ cp $(DISTFILES) $(DISTDIR)
+
+dist: distdir
+ tar zcf $(DISTDIR).tar.gz $(DISTDIR)
+ rm -rf $(DISTDIR)
+
+distcheck: dist
+ tar xfz $(DISTDIR).tar.gz
+ if $(MAKE) -C $(DISTDIR) $(DISTCHECKFLAGS); then \
+ echo "$(DISTDIR).tar.gz ready for distribution"; \
+ rm -rf $(DISTDIR); \
+ else \
+ exit 2; \
+ fi
+
diff --git a/addts.1 b/addts.1
new file mode 100644
index 0000000..bd4664b
--- /dev/null
+++ b/addts.1
@@ -0,0 +1,78 @@
+.TH ADDTS 1 "June 20, 2018" ADDTS "User Commands"
+.SH NAME
+addts \- add timestamps at the beginning of each line
+.SH SYNOPSIS
+.nh
+.na
+\fBaddts\fR\
+ [\fB\-au\fR]\
+ [\fB\-f \fIFMT\fR]\
+ [\fB\-i \fINUM\fR]\
+ [\fB\-w \fICHR\fR]\
+ [\fIFILE\fR]
+.PP
+\fBaddts\fR \fB\-?\fR
+.ad
+.hy
+.SH DESCRIPTION
+Reads standard input and writes it to standard output, adding a
+timestamp at the begginning of each line. If \fIFILE\fR is suppied,
+writes to it instead. Typical usage is for Apache forensic log:
+.PP
+.EX
+ForensicLog "|/usr/bin/addts -a /var/log/httpd/forensic.log"
+.EE
+.SH OPTIONS
+.TP
+.B \-a
+Append to \fIFILE\fR. Without this option, \fIFILE\fR is truncated
+after opening.
+.TP
+.BI \-f " FMT"
+Define timestamp format. \fIFMT\fR is a
+.BR strftime (3)
+format string, with an additional format conversion specifier
+.BR %@ ,
+which is replaced with micronseconds.
+.sp
+See also \fB\-w\fR, below.
+.TP
+.BI \-i " NUM"
+Pass first \fINUM\fR lines unchanged.
+.TP
+.B \-u
+Report times in UTC.
+.TP
+.BI \-w " CHR"
+Replace each occurrense of character \fICHR\fR in format string with
+single whitespace. Use this option if your Apache version is unable to
+correctly process quoted arguments in the \fBForensicLog\fR statement.
+Example usage (newlines added for readability):
+.sp
+.EX
+ForensicLog "|/usr/bin/addts -w_ -f %Y-%m-%d_%H:%M:%S.%@:_ \\
+ -a /var/log/httpd/forensic.log"
+.EE
+.SH BUGS
+Formatted timestamp cannot be longer than 511 bytes.
+.SH "SEE ALSO"
+.BR strftime (3).
+.SH AUTHORS
+Sergey Poznyakoff <gray@gnu.org.ua>.
+.SH COPYRIGHT
+Copyright \(co 2018 Sergey Poznyakoff
+.br
+.na
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
+.br
+.ad
+This is free software: you are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law.
+.\" Local variables:
+.\" eval: (add-hook 'write-file-hooks 'time-stamp)
+.\" time-stamp-start: ".TH [A-Z_][A-Z0-9_.\\-]* [0-9] \""
+.\" time-stamp-format: "%:B %:d, %:y"
+.\" time-stamp-end: "\""
+.\" time-stamp-line-limit: 20
+.\" end:
+
diff --git a/addts.c b/addts.c
new file mode 100644
index 0000000..abd3c9a
--- /dev/null
+++ b/addts.c
@@ -0,0 +1,125 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/time.h>
+
+static char default_fmt[] = "%Y-%m-%d %H:%M:%S: ";
+
+int
+main(int argc, char **argv)
+{
+ int c;
+ char buf[512];
+ int eol = 0;
+ int utc_opt = 0;
+ int append_opt = 0;
+ int ws_opt = 0;
+ unsigned long ignore = 0;
+ char *p;
+ char *fmt = NULL;
+ FILE *fp;
+
+ while ((c = getopt(argc, argv, "?af:i:uw:")) != EOF) {
+ switch (c) {
+ case 'a':
+ append_opt = 1;
+ break;
+ case 'f':
+ fmt = optarg;
+ break;
+ case 'i':
+ errno = 0;
+ ignore = strtoul(optarg, &p, 10);
+ if (errno || *p) {
+ fprintf(stderr,
+ "%s: -i argument is not a number\n",
+ argv[0]);
+ return 1;
+ }
+ break;
+ case 'u':
+ utc_opt = 1;
+ break;
+ case 'w':
+ ws_opt = optarg[0];
+ break;
+ default:
+ if (optopt == 0) {
+ printf("usage: %s [-a] [-f FMT] [-i N] [-u] [OUT-FILE]\n", argv[0]);
+ return 0;
+ }
+ return 1;
+ }
+ }
+
+ if (fmt) {
+ if (ws_opt) {
+ for (p = fmt; *p; p++)
+ if (*p == ws_opt)
+ *p = ' ';
+ }
+ } else
+ fmt = default_fmt;
+
+ argc -= optind;
+ argv += optind;
+
+ switch (argc) {
+ case 0:
+ fp = stdout;
+ break;
+ case 1:
+ fp = fopen(argv[0], append_opt ? "a" : "w");
+ if (!fp) {
+ perror(argv[0]);
+ return 1;
+ }
+ break;
+ default:
+ fprintf(stderr, "%s: too many arguments\n", argv[0]);
+ return 1;
+ }
+ setvbuf(fp, NULL, _IOLBF, 0);
+
+ if (ignore)
+ ignore--;
+ else
+ eol = 1;
+
+ while ((c = getchar()) != EOF) {
+ if (c == '\n') {
+ if (ignore)
+ ignore--;
+ else
+ eol = 1;
+ } else if (eol) {
+ struct timeval tv;
+ struct tm *tm;
+ size_t sz;
+ char *start, *p;
+
+ gettimeofday(&tv, NULL);
+ tm = (utc_opt ? gmtime : localtime)(&tv.tv_sec);
+ sz = strftime(buf, sizeof(buf), fmt, tm);
+ if (sz == 0 || sz == sizeof(buf)) {
+ strcpy(buf, "[OVERFLOW]: ");
+ sz = strlen(buf);
+ }
+ start = buf;
+ while ((p = strstr(start, "%@")) != NULL) {
+ fwrite(start, 1, p - start, fp);
+ fprintf(fp, "%06d", tv.tv_usec);
+ sz -= p - start + 2;
+ start = p + 2;
+ }
+ fwrite(start, 1, sz, fp);
+ eol = 0;
+ }
+ fputc(c, fp);
+ }
+ fclose(fp);
+ return 0;
+}

Return to:

Send suggestions and report system problems to the System administrator.