davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr) (01/10/90)
Thanks to suggestions from several people I have added some new features to unbundle. It now splits into two processes and uses pipes to send data, giving double buffering. I also added an (optional) argument for the size of the input volume, for one person who has a driver which messes up on end of tape, and another who only wants to use the outer tracks of a floppy for critical backup. The prompts are now consistent, and the documentation is expanded and corrected. I think that's about all the enhancement this program can stand, but if someone has a thought for something *useful* I'll consider it. This will be posted to sources.misc in a few weeks unless someone finds problems. : #!/bin/sh # shar+ created from directory /u/local/src/bundle # 10:44 on Wed Jan 10, 1990 by local echo 'x - whats.new (text)' sed << 'E!O!F' 's/^X//' > whats.new X bundle change history 1.2 revised 1/10/90 X X01-10-90 X X Added input volume size limit to unbundle. This allows reading a fixed Xquantity of data from each volume. Ubdated the documentation to match Xall this. X X01-06-90 X X Added fork and pipe operations to unbundle. Speedup is very slight on Xthe test systems, but may be larger on selected hardware. X X01-02-90 X X Added fork and pipe operations to bundle to make it faster. E!O!F newsize=`wc -c < whats.new` if [ $newsize -ne 437 ] then echo "File whats.new was $newsize bytes, 437 expected" fi echo 'x - bundle.1 (text)' sed << 'E!O!F' 's/^X//' > bundle.1 X'\" Copyright 1986,87,90 by Bill Davidsen. This code may be used for X'\" personal or commercial purposes. It may be freely distributed X'\" in unmodified source form providing this notice is kept intact. X.TH bundle 1 "1990 release" X'\" bundle doc v1.5 (1/10/90) X.SH NAME X bundle \- buffer and copy \fIstdin\fR to a physical device X unbundle \- buffer and copy a physical device to \fIstdout\fR X.SH SYNOPSIS X\fBbundle\fR special dev_size buf_size X.br X\fBunbundle\fR special buf_size X.br X\fBunbundle\fP special dev_size buf_size X.SH DESCRIPTION XThese programs allow a physical device to be the head or end of a pipe, Xallows software which does not know about volumes to operate on Xmulti-volume sets, and improves the performance of programs such as X\fItar\fR and \fIcpio\fR. X.P X\fBSpecial\fR is the name of a raw device, such as /dev/rmt0. X\fBdev_size\fR is the size of a volume the device, given in bytes or Xkilobytes. When \fBdev_size\fR bytes have been written to the output Xdevice, the operator will be prompted for a media change. The dev_size Xis optional for unbundle, and is only used when a fixed quantity of data Xis written on a volume. This is sometimes done when (a) tape drivers Xdon't handle end of tape correctly, or (b) for best reliability on Xfloppy disk, using only the outer tracks. X.P X\fBBuf_size\fR is the size of the buffer for the write or read. For Xtape this should be the desired tape block size. For disk this should Xthe size of a cylinder. For disk output writing a cylinder at a time Xrather than odd sectors can reduce real time by a factor of three. X.SH EXAMPLES X # \fIwriting to 395k floppy volumes\fP X ls *.c | cpio -o | bundle /dev/rfp021 395k 5k X unbundle /dev/rfp021 5k | cpio -idm X.sp X # \fIwriting 4MB tape volumes\fP X tar cf - /usr/local | bundle /dev/rmt0 4000k 8k X unbundle /dev/rmt0 8k | tar cf - X.sp X # \fIusing only part of a floppy disk\fP X find . -depth -print | bundle /dev/rfd096 800k 15k X unbundle /dev/rfd096 800k 15k | cpio -icdm X.SH WARNINGS XThe volume promts are written to the controlling terminal. If other Xprograms running, such as \fBtar\fP or \fBcpio\fP produce output the Xterminal messages are mingles. Use of the \fBv\fP option with these Xprograms is often very confusing. \fIunbundle\fR normally reads to EOF Xon the input device. If the device does not handle EOF well there may Xbe errors. If the \fBdev_size\fR is not a multiple of the X\fBbuf_size\fR, bizarre errors may occur. X.SH FILES X/dev/\fBspecial\fR X.SH SEE ALSO Xtar, cpio. X.SH AUTHOR XBill Davidsen (davidsen@crdos1.crd.ge.com) X.SH COPYRIGHT XCopyright 1986,87,90 by Bill Davidsen, all rights reserved. This program Xmay be distributed in any manner providing that further distribution is Xnot restricted and the package is kept intact. Modified versions must Xinclude the original source and documentation. X'\" For more details, see man(7), as well as man(1), manroff(1), and mmt(1) E!O!F newsize=`wc -c < bundle.1` if [ $newsize -ne 2906 ] then echo "File bundle.1 was $newsize bytes, 2906 expected" fi echo 'x - bundle.c (text)' sed << 'E!O!F' 's/^X//' > bundle.c X/***************************************************************** X | Program name: bundle.c X |---------------------------------------------------------------- X | accept standard input and write to a device named on the command X | line. The total bytes on the device and the buffer size are X | given on the command line. When the device is full, prompt X | for a new medium in the device and continue. X | X | Command form: X | bundle device Dsize Bsize X |---------------------------------------------------------------- X | Author: Bill Davidsen, 8/16/86 X | Version 1.8 X | Last modified: 1/10/90 X |---------------------------------------------------------------- X | Copyright 1986,87 by Bill Davidsen. This code may be used for X | personal or commercial purposes. It may be freely distributed X | in source form providing this notice is kept intact. X *****************************************************************/ X X#include <stdio.h> X X#ifdef DEBUG X#define debug(f,v) fprintf(stderr, f, v) X#else X#define debug(f,v) X#endif X Xstatic char *SCCS = "@(#) bundle 1.8"; X Xmain (argc, argv) X int argc; X char *argv[]; X{ X char *buffer, /* i/o buffer */ X *malloc (); X register long left; /* room left in the buffer */ X long bufsize, devsize; /* buffer and device size */ X long devleft = 0; /* room left on device */ X int istat, /* stdin read status */ X ostat, /* device write status */ X dev, /* device name from open */ X MediaNum = 1; /* sequential media # */ X FILE *tty, *fopen (); X long getsize (); /* get size from argument */ X int pipex[2]; /* pipe for double buffering */ X int wklen; /* working pie read length */ X X /* validate arguments */ X if (argc != 4) X { /* invalid */ X printf ("Format:\n bundle device DevSize BufSize\n"); X printf ("Where size may be bytes (as 5120) or k (as 5k)\n"); X exit (1); X } X X devsize = getsize (argv[2]); X debug ("Device size %ld", devsize); X bufsize = getsize (argv[3]); X debug (", buffer size %ld\n", bufsize); X if (devsize < 1 || devsize < bufsize) X { /* invalid specification of size */ X printf ("Invalid buffer and/or device size\n"); X exit (1); X } X X /* open the terminal for prompt */ X tty = fopen ("/dev/tty", "r"); X if (tty == NULL) X { /* mystery failure */ X printf ("Unable to open /dev/tty for prompt\n"); X exit (1); X } X X /* open the buffer */ X buffer = malloc (bufsize); X debug ("Buffer allocated\n", 0); X if (buffer == NULL) X { /* can't mallocate */ X printf ("Can't allocate space for the buffer\n"); X exit (1); X } X X /* see if you can open the device */ X printf ("Mount volume #1 on %s and press RETURN: ", argv[1]); X while (getc (tty) != '\n'); X dev = open (argv[1], 1); X if (dev < 0) X { /* invalid name */ X printf ("Can't access device %s\n", argv[1]); X exit (1); X } X devleft = devsize; /* set media ready */ X debug ("Enter copy loop\n\n", 0); X X /* open the pipe */ X if (pipe(pipex)) { X fprintf(stderr, "Can't open pipe for double buffering\n"); X exit(1); X } X X if (fork() == 0) { X /* child - copy stdin to pipe */ X for (wklen = 0; X (istat = read(0, buffer+wklen, (int)(bufsize - wklen))) > 0 X || wklen; X ) { X if (istat > 0) wklen += istat; X debug("Child read %5d bytes\n", istat); X debug(" buffer has %d bytes\n", wklen); X X /* output the size read, then the data */ X if (istat <= 0 || wklen == bufsize) { X write(pipex[1], (char *)&wklen, sizeof(int)); X if (write(pipex[1], buffer, wklen) != wklen) { X fprintf(stderr, "Child can't write pipe!\n"); X exit(1); X } X else debug("Child wrote %d bytes\n", wklen); X wklen = 0; X } X } X X /* output a size of zero */ X istat = 0; X write(pipex[1], (char *)&istat, sizeof(int)); X } X else { X /* main copy loop */ X while (read(pipex[0], (char *)&istat, sizeof(int)), istat) { X /* show the size factor and read those bytes */ X debug ("Input read size %d\n", istat); X for (wklen = 0; wklen < istat; ) { X wklen += read(pipex[0], buffer+wklen, istat-wklen); X } X X if (devleft < istat) X { /* change media */ X close (dev); X printf ("\007Mount volume #%d on %s and press RETURN: ", X ++MediaNum, argv[1]); X while (getc (tty) != '\n'); X dev = open (argv[1], 1); X devleft = devsize; X } X X /* write the buffer */ X ostat = write (dev, buffer, bufsize); X debug ("Output write size %d\n", ostat); X X if (bufsize != ostat) X { /* error on write */ X printf ("Error on device write!\n"); X exit (1); X } X devleft -= ostat; X } X } X X exit(0); X} X X/***************************************************************** X | Procedure: getsize X |---------------------------------------------------------------- X | Convert the size string to bytes. If the last character is X | 'k', multiply by 1024 X ****************************************************************/ X Xlong Xgetsize (string) X char *string; X{ X register long len = strlen (string), X val = atol (string); X X if (string[len - 1] == 'k') X val *= 1024; X X return val; X} E!O!F newsize=`wc -c < bundle.c` if [ $newsize -ne 5387 ] then echo "File bundle.c was $newsize bytes, 5387 expected" fi echo 'x - unbundle.c (text)' sed << 'E!O!F' 's/^X//' > unbundle.c X/***************************************************************** X | Program: unbundle X | accept input from a device and write to stdout X |---------------------------------------------------------------- X | Arguments: X | 1) device name X | 2) block size in bytes or k X | Version: 1.9 X | Last modified: 1/10/90 X |---------------------------------------------------------------- X | Copyright 1986,87 by Bill Davidsen. This code may be used for X | personal or commercial purposes. It may be freely distributed X | in source form providing this notice is kept intact. X ****************************************************************/ X X#include <stdio.h> X#include <signal.h> X X/* define signal for BSD or USG */ X#ifndef SIGCHLD X#ifdef SIGCLD X#define SIGCHLD SIGCLD X#endif X#endif X X#ifdef DEBUG X#define debug(f,v) fprintf(stderr, f, v) X#else X#define debug(f,v) X#endif X Xextern wrapup(); X Xmain (argc, argv) X int argc; X char *argv[]; X{ X char *buf, *malloc (); X int bufsize, dn, mnum = 1, ch, readsize; X int pid; /* child process id */ X long vol_limit = 0; /* max volume size */ X long vol_size; /* read on this volume */ X static char *SCCSid = "@(#)unbundle 1.9"; X long getsize (); /* size of buffer */ X int pipes[2]; /* hold read and write pipes */ X X if (argc < 3 || argc > 4) X { /* bad calling sequence */ X fprintf (stderr, "%s: wrong # of arguments\n", argv[0]); X fprintf (stderr, "Calling seq:\n %s device [volsize] bufsize\n", X argv[0]); X exit (1); X } X X /* try to open the device */ X dn = open (argv[1], 0); X if (dn < 0) X { /* open failed */ X fprintf (stderr, "%s: can't open device %s\n", argv[0], argv[1]); X exit (1); X } X close (dn); /* so I can reopen */ X X /* open the pipe */ X if (pipe(pipes)) { X fprintf(stderr, "Can't create pipes (out of inodes?)\n"); X exit(1); X } X X /* allocate the buffer */ X if (argc == 4) { X /* have volume and buffer size */ X vol_limit = getsize(argv[2]); X bufsize = getsize(argv[3]); X } X else { X /* just the buffer size */ X bufsize = getsize (argv[2]); X } X debug("Volsize: %ld, ", vol_limit); X debug ("Bufsize: %d\n", bufsize); X X buf = malloc (bufsize); X if (buf == NULL) X { /* can't allocate the buffer */ X fprintf (stderr, "Can't allocate %d byte buffer\n", bufsize); X exit (1); X } X X /* here I split the process */ X pid = fork(); X if (pid) { X /* setup for signal when child done */ X signal(SIGCHLD, wrapup); X } X X for (;;) X { /* get a fresh mount and read it */ X if (pid) { X /* parent will read the input device */ X fprintf (stderr, "Mount input volume #%d on %s and press return: ", X mnum++, argv[1]); X read (0, &ch, 1); X dn = open (argv[1], 0); X X if (vol_limit) { X /* must read a limited amount from each volume */ X vol_size = 0; X while (vol_size < vol_limit) { X /* calculate max read size */ X readsize = vol_limit - vol_size; X if (readsize > bufsize) readsize = bufsize; X X /* now read the data and copy */ X readsize = read (dn, buf, bufsize); X if (readsize < 1) break; X write(pipes[1], buf, readsize); X vol_size += readsize; X } X } X else { X /* just copy the data */ X while (readsize = read (dn, buf, bufsize)) { X write (pipes[1], buf, readsize); X } X } X close (dn); X } X else { X /* child will do pipe => stdout */ X while ((readsize = read(pipes[0], buf, bufsize))) { X if (write(1, buf, readsize) < 1) break; X } X } X } X X /* return good status */ X exit(0); X} X X/* X | Procedure: getsize X |- X | Convert the size string to bytes. If the last character is X | 'k', multiply by 1024 X */ X Xlong Xgetsize (string) X char *string; X{ X register int len = strlen (string); X register long val = atol (string); X X if (string[len - 1] == 'k') X val *= 1024; X X return val; X} X X/* wrapup parent on signal */ X Xwrapup() { X exit(0); X} E!O!F newsize=`wc -c < unbundle.c` if [ $newsize -ne 3767 ] then echo "File unbundle.c was $newsize bytes, 3767 expected" fi echo 'x - Makefile (text)' sed << 'E!O!F' 's/^X//' > Makefile X# makefile for bundle items v1.11 1/10/90 X X# ================ this you might wnat to change X X# for compressed backup with date preservation uncomment one of these XBACKUP = zoo X#BACKUP = tar X X# ================ this is all you want to change! X X# distribution and source files XDIST = whats.new bundle.1 bundle.c unbundle.c Makefile XSCCS = s.bundle.c s.unbundle.c s.Makefile s.bundle.1 \ X s.whats.new XOTHER = buntest.c unbuntest.c X XRESULTS = bundle unbundle XTESTPROG = buntest unbuntest X X# if a library is needed for nap() XNAPLIB = -lx X X# which shar to use XSHAR = shar X X# makerule for SCCS which doesn't leave writable files on error X.c~.c: X get $? X X# start making here X Xall : $(RESULTS) $(TESTPROG) X Xtest : all buntest X Xbundle : bundle.o X $(CC) -o bundle bundle.o X Xunbundle : unbundle.o X $(CC) -o unbundle unbundle.o X Xbuntest : buntest.o X $(CC) -o buntest buntest.o $(NAPLIB) X Xunbuntest : unbuntest.o X $(CC) -o unbuntest unbuntest.o $(NAPLIB) X X# things with now reasonable makerules by default Xbundle.1 : X get s.bundle.1 X Xwhats.new : X get s.whats.new X X# shar for distribution Xshar : bundle.shar Xbundle.shar : $(DIST) X shar $(DIST) > bundle.shar X X# zoo backup - what the author uses Xzoo : check1 $(SCCS) $(OTHER) X zoo aunPP bundle.zoo $(SCCS) $(OTHER) X X# check for any files not in SCCS Xcheck1 : X ls p.* && echo "Modified versions!" && exit 1 || exit 0 X X# if you like backup via compressed tar files Xtar : check1 $(SCCS) $(OTHER) X tar cvf - $(SCCS) $(OTHER) | compress -v > bundle.tar.Z X X# cleanup all the files other than the shar, zoo, and executables Xclean : $(BACKUP) shar X @echo "Removing working files";\ X rm -f $(DIST) $(SCCS) $(OTHER) bundle.bak *.o buntest unbuntest E!O!F newsize=`wc -c < Makefile` if [ $newsize -ne 1690 ] then echo "File Makefile was $newsize bytes, 1690 expected" fi exit 0 -- bill davidsen (davidsen@crdos1.crd.GE.COM -or- uunet!crdgw1!crdos1!davidsen) "The world is filled with fools. They blindly follow their so-called 'reason' in the face of the church and common sense. Any fool can see that the world is flat!" - anon