# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-CC= gcc
-CFLAGS= $(WARNINGS) $(OPTIMISE) $(DEBUG)
-CPPFLAGS= -DRWBUFFER_SIZE_MB=$(RWBUFFER_SIZE_MB)
-
-WARNINGS= -Wall -Wwrite-strings -Wmissing-prototypes \
- -Wstrict-prototypes -Wpointer-arith
-OPTIMISE= -O2
-DEBUG= -g
-RWBUFFER_SIZE_MB=16
-
SYSTEM_GROUP= root
prefix=/usr/local
INSTALL_PROGRAM= $(INSTALL_SCRIPT) -s
INSTALL_DIRECTORY= $(INSTALL) -m 2755 -o root -g $(SYSTEM_GROUP) -d
-CTARGETS= readbuffer writebuffer
BINSCRIPTS= checkallused loaded driver takedown whatsthis labeltape
SHARESCRIPTS= bringup full increm
SHAREFILES= backuplib.pl
EXAMPLES= relativity chiark
-all: $(CTARGETS)
-
-readbuffer: readbuffer.o rwbuffer.o
-writebuffer: writebuffer.o rwbuffer.o
-readbuffer.o writebuffer.o rwbuffer.o: rwbuffer.h
+all:
install: all
$(INSTALL_DIRECTORY) $(confdir) $(bindir) $(sharedir) $(vardir) $(man1dir)
- $(INSTALL_PROGRAM) $(CTARGETS) $(bindir)
set -e; for s in $(BINSCRIPTS); do \
$(INSTALL_SCRIPT) $$s $(bindir)/backup-$$s; done
$(INSTALL_SHARE) $(SHAREFILES) $(sharedir)
$(INSTALL_SCRIPT) $(SHARESCRIPTS) $(sharedir)
- $(INSTALL) -m 644 readbuffer.1 ${man1dir}/readbuffer.1
- $(INSTALL) -m 644 writebuffer.1 ${man1dir}/writebuffer.1
install-docs:
$(INSTALL_DIRECTORY) $(txtdocdir)
rm -f *~ ./#*# *.o
distclean realclean: clean
- rm -f $(CTARGETS)
readbuffer
writebuffer
+trivsoundd
--- /dev/null
+# Makefile
+# simple make settings
+#
+# This file is part of chiark backup, a system for backing up GNU/Linux and
+# other UN*X-compatible machines, as used on chiark.greenend.org.uk.
+#
+# chiark backup is:
+# Copyright (C) 1997-1998,2000-2001 Ian Jackson <ian@chiark.greenend.org.uk>
+# Copyright (C) 1999 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+#
+# This 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, or (at your option) any later version.
+#
+# This 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+CC= gcc
+CFLAGS= $(WARNINGS) $(OPTIMISE) $(DEBUG)
+CPPFLAGS= -DRWBUFFER_SIZE_MB=$(RWBUFFER_SIZE_MB)
+
+WARNINGS= -Wall -Wwrite-strings -Wmissing-prototypes \
+ -Wstrict-prototypes -Wpointer-arith
+OPTIMISE= -O2
+DEBUG= -g
+RWBUFFER_SIZE_MB=16
+
+SYSTEM_GROUP= root
+
+prefix=/usr/local
+
+bindir=$(prefix)/bin
+sbindir=$(prefix)/sbin
+mandir=${prefix}/man
+man1dir=${mandir}/man1
+man8dir=${mandir}/man8
+
+INSTALL= install -c
+INSTALL_PROGRAM= $(INSTALL_SCRIPT) -s
+
+PROGRAMS= readbuffer writebuffer
+DAEMONS= trivsoundd
+MAN1PAGES= readbuffer.1 writebuffer.1
+MAN8PAGES= trivsoundd.8
+
+TARGETS= $(PROGRAMS) $(DAEMONS)
+
+all: $(TARGETS)
+
+readbuffer: readbuffer.o rwbuffer.o
+writebuffer: writebuffer.o wrbufcore.o rwbuffer.o
+trivsoundd: trivsoundd.o wrbufcore.o rwbuffer.o
+readbuffer.o writebuffer.o rwbuffer.o wrbufcoore.o: rwbuffer.h
+
+install: all
+ $(INSTALL_DIRECTORY) $(bindir) $(sbindir)
+ $(INSTALL_PROGRAM) $(PROGRAMS) $(bindir)
+ $(INSTALL_PROGRAM) $(DAEMONS) $(sbindir)
+
+install-docs:
+ $(INSTALL_DIRECTORY) $(man1dir) $(man8dir) $(txtdocdir)
+ $(INSTALL) -m 644 $(MAN1PAGES) ${man1dir}/.
+ $(INSTALL) -m 644 $(MAN8PAGES) ${man8dir}/.
+
+install-examples:
+
+clean:
+ rm -f *~ ./#*# *.o
+
+distclean realclean: clean
+ rm -f $(TARGETS)
--- /dev/null
+/*
+ * dlist.h
+ * - macros for handling doubly linked lists
+ */
+/*
+ * This file is
+ * Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk>
+ *
+ * It is part of adns, which is
+ * Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+ * Copyright (C) 1999 Tony Finch <dot@dotat.at>
+ *
+ * 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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef ADNS_DLIST_H_INCLUDED
+#define ADNS_DLIST_H_INCLUDED
+
+#define LIST_INIT(list) ((list).head= (list).tail= 0)
+#define LINK_INIT(link) ((link).next= (link).back= 0)
+
+#define LIST_UNLINK_PART(list,node,part) \
+ do { \
+ if ((node)->part back) (node)->part back->part next= (node)->part next; \
+ else (list).head= (node)->part next; \
+ if ((node)->part next) (node)->part next->part back= (node)->part back; \
+ else (list).tail= (node)->part back; \
+ } while(0)
+
+#define LIST_LINK_TAIL_PART(list,node,part) \
+ do { \
+ (node)->part next= 0; \
+ (node)->part back= (list).tail; \
+ if ((list).tail) (list).tail->part next= (node); else (list).head= (node); \
+ (list).tail= (node); \
+ } while(0)
+
+#define LIST_UNLINK(list,node) LIST_UNLINK_PART(list,node,)
+#define LIST_LINK_TAIL(list,node) LIST_LINK_TAIL_PART(list,node,)
+
+#endif
*
*/
-#include <sys/time.h>
-#include <sys/types.h>
-#include <assert.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <errno.h>
-#include <unistd.h>
-
#include "rwbuffer.h"
const char *progname= "readbuffer";
*
*/
-#include <sys/time.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <assert.h>
-#include <ctype.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <sys/mman.h>
-
#include "rwbuffer.h"
#ifndef RWBUFFER_SIZE_MB_DEF
fd_set readfds;
fd_set writefds;
+static int opt_mlock=0;
+
int min(int a, int b) { return a<=b ? a : b; }
static void usage(FILE *f) {
exit(12);
}
-static void nonblock(int fd, int yesno) {
+void nonblock(int fd, int yesno) {
int r;
r= fcntl(fd,F_GETFL,0); if (r == -1) { perror("fcntl getfl"); exit(8); }
if (yesno) r |= O_NDELAY;
nonblock(0,0); nonblock(1,0);
}
+void startupcore(void) {
+ buf= xmalloc(buffersize);
+
+ if (opt_mlock) {
+ if (mlock(buf,buffersize)) { perror("mlock"); exit(2); }
+ }
+
+ used=0; wp=rp=buf; seeneof=0;
+ if (atexit(unnonblock)) { perror("atexit"); exit(16); }
+}
+
void startup(const char *const *argv) {
const char *arg;
char *ep;
unsigned long opt_buffersize=RWBUFFER_SIZE_MB_DEF;
- int opt_mlock=0;
assert(argv[0]);
}
buffersize= opt_buffersize*1024*1024;
- buf= xmalloc(buffersize);
-
- if (opt_mlock) {
- if (mlock(buf,buffersize)) { perror("mlock"); exit(2); }
- }
-
- used=0; wp=rp=buf; seeneof=0;
- if (atexit(unnonblock)) { perror("atexit"); exit(16); }
+ startupcore();
nonblock(0,1); nonblock(1,1);
}
#ifndef RWBUFFER_H
#define RWBUFFER_H
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <assert.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+
+#include "dlist.h"
+
+
int min(int a, int b);
void callselect(void);
void startup(const char *const *argv);
+void startupcore(void);
+void *xmalloc(size_t sz);
+void nonblock(int fd, int yesno);
extern const char *progname; /* must be defined by main .c file */
extern fd_set readfds;
extern fd_set writefds;
+
+void wrbufcore_startup(void);
+void wrbufcore_prepselect(int rdfd, int wrfd);
+void wrbufcore_afterselect(int rdfd, int wrfd);
+
+
#endif /*RWBUFFER_H*/
--- /dev/null
+#!/bin/bash
+set -e
+pf=`tempfile`
+rm -f $pf
+mknod -m600 $pf p
+exec 3<>$pf
+exec >$pf 4<$pf
+rm -f $pf
+exec 3>&-
+(exec logger -p user.info -t trivsoundd <&4 >/dev/null 2>&1) &
+exec 4<&- 2>&1 </dev/null
+exec ${0%%-start}
--- /dev/null
+.TH writebuffer 1 2001-10-21 chiark-backup
+.SH NAME
+writebuffer \- write output to devices which don't like constant stopping and starting
+.SH SYNOPSIS
+.B writebuffer
+.RB [ --mlock ]
+.RI [ size ]
+.SH DESCRIPTION
+.B writebuffer
+reads data on standard input and writes it to standard output. It
+will buffer internally up to \fIsize\fR megabytes and will only write
+data when the buffer is at least 75% full or when there is no more
+input to fill the buffer.
+.PP
+It is intended for use in situations where many small writes are
+undesirable for performance reasons, e.g. tape drives.
+.SH OPTIONS
+.TP
+.B --mlock
+Calls
+.BR mlock (2)
+to lock the buffer into memory.
+.SH "SEE ALSO"
+.BR readbuffer (1),
+.BR mlock (2)
*
*/
-#include <sys/time.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <assert.h>
-#include <ctype.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <sys/mman.h>
-
#include "rwbuffer.h"
-const char *progname= "triv-sound-d";
+const char *progname= "trivsoundd";
-static int master;
+static int maxstartdelay=60, maxbadaccept=10;
-int main(int argc, const char *const *argv) {
- const char *bindname;
- union {
- struct sockaddr sa;
- struct sockaddr_in sin;
- struct sockaddr_un sun;
- } su;
- socklen_t sulen;
-
- bindname= *++argv;
- if (!bindname) usageerr("need bind argument");
- startup(argv);
-
- memset(&su,0,sizeof(su));
-
- if (bindname[0]=='/' || bindname[0]='.') {
- if (strlen(bindname) >= sizeof(su.sun.sun_path))
- usageerr("AF_UNIX bind path too long");
- sulen= sizeof(su.sun);
- su.sun.sun_family= AF_UNIX;
- strcpy(su.sun.sun_path, bindname);
- } else if ((colon= strrchr(bindname,':'))) {
- char *copy, *ep;
- unsigned long portul;
-
- sulen= sizeof(su.sin);
- su.sin.sin_family= AF_INET;
-
- copy= xmalloc(colon - bindname + 1);
- memcpy(copy,bindname, colon - bindname + 1);
- copy[colon - bindname]= 0;
- portul= strtoul(colon+1,0,&ep);
- if (!*ep) {
- if (!portul || portul>=65536) usageerr("invalid port number");
- su.sin.sin_port= htons(portul);
- } else {
- struct servent *se= getservbyname(colon+1, "tcp");
- if (!se) { fprintf(stderr,"unknown service `%s'\n",colon+1); exit(4); }
- su.sin.sin_port= htons(se->s_port);
+struct inqnode {
+ struct inqnode *next, *back;
+ time_t accepted;
+ int fd;
+};
+
+static struct { struct inqnode *head, *tail; } inq;
+static int sdev;
+static time_t now;
+
+static void opensounddevice(void) {
+ int r;
+ char cbuf[200];
+
+ sdev= open("/dev/dsp", O_WRONLY);
+ if (sdev<0) { perror("open sound device"); exit(8); }
+
+ snprintf(cbuf, sizeof(cbuf), "sox -t raw -s -w -r 44100 -c 2"
+ " - </dev/null -t ossdsp - >&%d", sdev);
+ r= system(cbuf); if (r) { fprintf(stderr,"sox gave %d\n",r); exit(5); }
+}
+
+static void selectcopy(void) {
+ int slave= inq.head ? inq.head->fd : -1;
+ wrbufcore_prepselect(slave, sdev);
+ FD_SET(0,&readfds);
+ callselect();
+ wrbufcore_afterselect(slave, sdev);
+}
+
+static void expireoldconns(void) {
+ struct inqnode *searchold, *nextsearchold;
+
+ for (searchold= inq.head ? inq.head->next : 0;
+ searchold;
+ searchold= nextsearchold) {
+ nextsearchold= searchold;
+ if (searchold->accepted < now-maxstartdelay) {
+ printf("expired %p\n",searchold);
+ LIST_UNLINK(inq,searchold);
+ free(searchold);
}
+ }
+}
- if (!inet_aton(copy,&su.sin.sin_addr)) {
- struct hostent *he= gethostbyname(copy);
- if (!he) { herror(copy); exit(4); }
- if (he->h_addrtype != AF_INET ||
- he->h_length != sizeof(su.sin.sin_addr) ||
- !he->h_addrs[0] ||
- he->h_addrs[1]) {
- fprintf(stderr,"hostname lookup `%s' did not yield"
- " exactly one IPv4 address\n",copy);
+static void acceptnewconns(void) {
+ static int bad;
+
+ int slave;
+ struct inqnode *new;
+
+ if (!FD_ISSET(0,&readfds)) return;
+
+ slave= accept(0,0,0);
+ if (slave < 0) {
+ if (!(errno == EINTR ||
+ errno == EAGAIN ||
+ errno == EWOULDBLOCK)) {
+ perror("accept");
+ bad++;
+ if (bad > maxbadaccept) {
+ fprintf(stderr,"accept failures repeating\n");
exit(4);
}
- memcpy(su.sin.sin_addr, he->h_addrs[0], sizeof(su.sin.sin_addr));
}
+ /* any transient error will just send us round again via select */
+ return;
}
- master= socket(su.sa.sa_family,SOCK_STREAM,0);
- if (master<0) { perror("socket"); exit(8); }
+ bad= 0;
+ new= xmalloc(sizeof(struct inqnode));
+ new->accepted= now;
+ new->fd= slave;
+ LIST_LINK_TAIL(inq,new);
-
-
- if (
+ printf("accepted %p\n",new);
+}
- sdigit((unsigned char)bindname[0])) {
-
+static void switchinput(void) {
+ struct inqnode *old;
+ if (!seeneof) return;
+ old= inq.head;
+ assert(old);
+ printf("finished %p\n",old);
+ close(old->fd);
+ LIST_UNLINK(inq,old);
+ free(old);
+ seeneof= 0;
+}
- if (argv[0]
-
- startup(argv);
- writebuf_startup();
- writebuf_mainloop();
- exit(0);
+int main(int argc, const char *const *argv) {
+ assert(argv[0]);
+ if (argv[1]) { fputs("no arguments allowed\n",stderr); exit(12); }
+
+ buffersize= 44100*4* 5/*seconds*/;
+
+ opensounddevice();
+ nonblock(sdev,1);
+ nonblock(0,1);
+
+ startupcore();
+ wrbufcore_startup();
+
+ printf("started\n");
+ for (;;) {
+ selectcopy();
+ if (time(&now)==(time_t)-1) { perror("time(2)"); exit(4); }
+ expireoldconns();
+ acceptnewconns();
+ switchinput();
+ }
}
*
*/
+#include "rwbuffer.h"
+
static size_t waitfill;
static int writing;
writing=0;
}
-void wrbufcore_mainloop(void) {
- int r;
+void wrbufcore_prepselect(int rdfd, int wrfd) {
+ FD_ZERO(&readfds);
+ if (rdfd>=0 && !seeneof && used+1<buffersize) FD_SET(rdfd,&readfds);
- while (!seeneof || used) {
-
- FD_ZERO(&readfds); if (!seeneof && used+1<buffersize) FD_SET(0,&readfds);
- FD_ZERO(&writefds); if (writing) FD_SET(1,&writefds);
+ FD_ZERO(&writefds);
+ if (writing) FD_SET(wrfd,&writefds);
+}
- callselect();
+void wrbufcore_afterselect(int rdfd, int wrfd) {
+ int r;
- if (FD_ISSET(1,&writefds) && !FD_ISSET(0,&readfds) && !used) {
- writing= 0;
- FD_CLR(1,&writefds);
- }
+ if (FD_ISSET(wrfd,&writefds) &&
+ !(rdfd>=0 && FD_ISSET(rdfd,&readfds)) &&
+ !used) {
+ writing= 0;
+ FD_CLR(wrfd,&writefds);
+ }
- if (FD_ISSET(0,&readfds)) {
- r= read(0,rp,min(buffersize-1-used,buf+buffersize-rp));
- if (!r) {
- seeneof=1; writing=1;
- } else if (r<0) {
- if (!(errno == EAGAIN || errno == EINTR)) { perror("read"); exit(1); }
- } else {
- used+= r;
- rp+= r;
- if (rp == buf+buffersize) rp=buf;
- }
- if (used > waitfill) writing=1;
+ if (rdfd>=0 && FD_ISSET(rdfd,&readfds)) {
+ r= read(rdfd,rp,min(buffersize-1-used,buf+buffersize-rp));
+ if (!r) {
+ seeneof=1; writing=1;
+ } else if (r<0) {
+ if (!(errno == EAGAIN || errno == EINTR)) { perror("read"); exit(1); }
+ } else {
+ used+= r;
+ rp+= r;
+ if (rp == buf+buffersize) rp=buf;
}
+ if (used > waitfill) writing=1;
+ }
- if (FD_ISSET(1,&writefds) && used) {
- r= write(1,wp,min(used,buf+buffersize-wp));
- if (r<=0) {
- if (!(errno == EAGAIN || errno == EINTR)) { perror("write"); exit(1); }
- } else {
- used-= r;
- wp+= r;
- if (wp == buf+buffersize) wp=buf;
- }
+ if (FD_ISSET(wrfd,&writefds) && used) {
+ r= write(wrfd,wp,min(used,buf+buffersize-wp));
+ if (r<=0) {
+ if (!(errno == EAGAIN || errno == EINTR)) { perror("write"); exit(1); }
+ } else {
+ used-= r;
+ wp+= r;
+ if (wp == buf+buffersize) wp=buf;
}
}
}
*
*/
-#include <sys/time.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <errno.h>
-#include <unistd.h>
-
#include "rwbuffer.h"
const char *progname= "writebuffer";
int main(int argc, const char *const *argv) {
- int r, writing;
-
startup(argv);
- writebuf_startup();
- writebuf_mainloop();
+ wrbufcore_startup();
+ while (!seeneof || used) {
+ wrbufcore_prepselect(0,1);
+ callselect();
+ wrbufcore_afterselect(0,1);
+ }
exit(0);
}