r4924 - in developers/werner: . stdlog

werner at docs.openmoko.org werner at docs.openmoko.org
Mon Feb 23 07:32:58 CET 2009


Author: werner
Date: 2009-02-23 07:32:57 +0100 (Mon, 23 Feb 2009)
New Revision: 4924

Added:
   developers/werner/stdlog/
   developers/werner/stdlog/Makefile
   developers/werner/stdlog/stdlog.c
Log:
"Script" workalike but without requiring a tty.



Added: developers/werner/stdlog/Makefile
===================================================================
--- developers/werner/stdlog/Makefile	                        (rev 0)
+++ developers/werner/stdlog/Makefile	2009-02-23 06:32:57 UTC (rev 4924)
@@ -0,0 +1,11 @@
+CFLAGS=-Wall -g
+
+.PHONY:		clean spotless
+
+stdlog:
+
+clean:
+		rm -f *.o
+
+spotless:	clean
+		rm -f stdlog

Added: developers/werner/stdlog/stdlog.c
===================================================================
--- developers/werner/stdlog/stdlog.c	                        (rev 0)
+++ developers/werner/stdlog/stdlog.c	2009-02-23 06:32:57 UTC (rev 4924)
@@ -0,0 +1,329 @@
+/*
+ * stdlog.c - Log stdin/stdout to a file (similar to "script", but without tty)
+ *
+ * Copyright (C) 2007 by OpenMoko, Inc.
+ * Written by Werner Almesberger <werner at openmoko.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <signal.h>
+#include <wait.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/select.h>
+
+
+#define COPY_BUFFER 4096
+
+
+static int fds[3][2];
+static pid_t pid;
+static int file;
+static time_t t0, last;
+static int timestamps = 0;
+
+
+static void child(char *const *argv)
+{
+    int i;
+
+    for (i = 0; i != 3; i++) {
+	if (close(fds[i][!i]) < 0) {
+	    perror("close");
+	    exit(1);
+	}
+	if (dup2(fds[i][!!i], i) < 0) {
+	    perror("dup2");
+	    exit(1);
+	}
+    }
+    execvp(*argv, argv);
+    perror(*argv);
+    exit(1);
+}
+
+
+static void write_all(int fd, void *data, size_t size)
+{
+    size_t done;
+    size_t wrote;
+
+    for (done = 0; done != size; done += wrote) {
+	wrote = write(fd, data+done, size-done);
+	if (wrote < 0) {
+	    perror("write");
+	    exit(1);
+	}
+	if (!wrote)
+	    return;
+    }
+}
+
+
+static void write_file(void *data, size_t size)
+{
+    static char buffer[COPY_BUFFER];
+    static size_t buf_len;
+    const char *p, *nl = NULL;
+
+    if (buf_len)
+	write_all(file, buffer, buf_len);
+    buf_len = 0;
+    for (p = data; p != data+size; p++)
+	if (*p == '\n')
+	    nl = p;
+    if (!nl)
+	write_all(file, data, size);
+    else {
+	write_all(file, data, nl-(char *) data+1);
+	buf_len = size-(nl-(char *) data+1);
+	memcpy(buffer, nl+1, buf_len);
+    }
+}
+
+
+static void copy(int from, int *to)
+{
+    char buf[COPY_BUFFER];
+    ssize_t got;
+
+    assert(*to != -1);
+    got = read(from, buf, sizeof(buf));
+    if (got < 0) {
+	perror("read");
+	exit(1);
+    }
+    if (!got) {
+	if (close(*to) < 0) {
+	    perror("close");
+	    exit(1);
+	}
+	*to = -1;
+	return;
+    }
+    write_all(*to, buf, got);
+    if (from)
+	write_file(buf, got);
+}
+
+
+static void set_signal(int sig, void (*handler)(int sig))
+{
+    if (signal(sig, handler) == SIG_ERR) {
+	perror("signal");
+	exit(1);
+    }
+}
+
+
+static void forward(int sig)
+{
+    if (kill(pid, sig) < 0)
+	perror("kill");
+}
+
+
+static void many_signals(void (*handler)(int sig))
+{
+    static int signals[] = {
+	SIGHUP, SIGINT, SIGQUIT, SIGUSR1, SIGUSR2, SIGTERM, 0
+    };
+    int *sig;
+
+    for (sig = signals; *sig; sig++)
+	set_signal(*sig, handler);
+}
+
+
+static void sigchld(int sig)
+{
+    /* do nothing - all we need is the EINTR */
+}
+
+
+static void block(int how)
+{
+    sigset_t set;
+
+    if (sigemptyset(&set) < 0) {
+	perror("sigemptyset");
+	exit(1);
+    }
+    if (sigaddset(&set, SIGCHLD) < 0) {
+	perror("sigaddset");
+	exit(1);
+    }
+    if (sigprocmask(how, &set, NULL) < 0) {
+	perror("sigprocmask");
+	exit(1);
+    }
+}
+
+
+static void do_timestamp(void)
+{
+    char buf[20];
+    time_t t;
+    int len;
+
+    if (time(&t) < 0) {
+	perror("time");
+	exit(1);
+    }
+    if (t-last < timestamps)
+	return;
+    last += timestamps;
+    len = sprintf(buf, "--- %ld ---\n", (long) (t-t0));
+    write_file(buf, len);
+}
+
+
+static void parent(void)
+{
+    struct timeval to;
+    int i, max_fd;
+    pid_t pid;
+    int status;
+    int out = 1, err = 2;
+
+    for (i = 0; i != 3; i++)
+	if (close(fds[i][!!i]) < 0) {
+	    perror("close");
+	    exit(1);
+	}
+    block(SIG_BLOCK);
+    set_signal(SIGCHLD, sigchld);
+    many_signals(forward);
+    max_fd = fds[1][0] > fds[2][0] ? fds[1][0] : fds[2][0];
+    do {
+	fd_set set;
+	int n;
+
+	FD_ZERO(&set);
+	if (fds[0][1] != -1)
+	    FD_SET(0, &set);
+	if (out != -1)
+	    FD_SET(fds[1][0], &set);
+	if (err != -1)
+	    FD_SET(fds[2][0], &set);
+	to.tv_sec = 1;
+	to.tv_usec = 0;
+	/*
+	 * This can still race. However, if it does, we'll still catch the
+	 * process through the timeout. "pselect" would be better.
+	 */
+	block(SIG_UNBLOCK);
+	n = select(max_fd+1, &set, NULL, NULL, &to);
+	block(SIG_BLOCK);
+	if (n < 0 && errno != EINTR) {
+	    perror("select");
+	    exit(1);
+	}
+	if (FD_ISSET(0, &set))
+	    copy(0, &fds[0][1]);
+	if (FD_ISSET(fds[1][0], &set))
+	    copy(fds[1][0], &out);
+	if (FD_ISSET(fds[2][0], &set))
+	    copy(fds[2][0], &err);
+	if (timestamps)
+	    do_timestamp();
+	pid = waitpid(-1, &status, WNOHANG);
+    }
+    while (!pid);
+    if (pid < 0) {
+	perror("wait");
+	exit(1);
+    }
+    if (WIFEXITED(status))
+	_exit(WEXITSTATUS(status));
+    if (WIFSIGNALED(status)) {
+	many_signals(SIG_DFL);
+	raise(WTERMSIG(status));
+    }
+    fprintf(stderr, "status %d\n", status);
+    _exit(1);
+}
+
+
+static void usage(const char *name)
+{
+    fprintf(stderr,
+"usage: %s [-a] [-t] file command ...\n"
+"  -a       append to existing log file\n"
+"  -t secs  periodically write a timestamp\n"
+  , name);
+    exit(1);
+}
+
+
+int main(int argc, char *const *argv)
+{
+    int append = 0;
+    int c, i;
+
+    while ((c = getopt(argc, argv, "+at:")) != EOF)
+	switch (c) {
+	    char *end;
+
+	    case 'a':
+		append = 1;
+		break;
+	    case 't':
+		timestamps = strtoul(optarg, &end, 0);
+		if (*end) {
+		    usage(*argv);
+		    exit(1);
+		}
+		break;
+	    default:
+		usage(*argv);
+	}
+    if (argc-optind < 2)
+	usage(*argv);
+    file = open(argv[optind],
+      O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0666);
+    if (file < 0) {
+	perror(argv[optind]);
+	exit(1);
+    }
+    for (i = 0; i != 3; i++)
+	if (pipe(fds[i]) < 0) {
+	    perror("pipe");
+	    exit(1);
+	}
+    if (timestamps) {
+	if (time(&t0) < 0) {
+	    perror("time");
+	    exit(1);
+	}
+	last = t0;
+    }
+    pid = fork();
+    if (!pid)
+	child(argv+optind+1);
+    else
+	parent();
+    return 1;
+}




More information about the commitlog mailing list