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