1: /*
   2:  * ddd.c - double dd (version 2)
   3:  *
   4:  * Copyright 1988 Helsinki University of Technology.
   5:  * All rights reserved.
   6:  *
   7:  * Permission granted to distribute, use and modify
   8:  * this code for uncommercial purposes, provided
   9:  * that this copyright notice is not changed.
  10:  *
  11:  * Author: Tapani Lindgren (nispa@cs.hut.fi)
  12:  *
  13:  * Ddd is a dd clone that operates as two processes;
  14:  * one process reads while the other one writes and vice versa.
  15:  * This way the throughput may be up to twice as good as that of dd,
  16:  * especially with slow devices such as tape drives.
  17:  *
  18:  * ***** WARNING ***** (For U.S. residents & citizens only)
  19:  *
  20:  * Do not use this program!  Get rid of it as soon as you can!
  21:  * It will probably corrupt all your data, break down your computer
  22:  * and cause severe injury to the operators.
  23:  * Even reading the source code may give you a headache.
  24:  * I warned you!  I will take no responsibility whatsoever!
  25:  */
  26: 
  27: /* declarations common to all unix versions */
  28: #include <stdio.h>     /* for fprintf() and stderr() */
  29: #include <signal.h>    /* for SIGTERM */
  30: extern char *malloc();
  31: 
  32: /* version dependent declarations */
  33: 
  34: #ifdef BSD
  35: #include <sys/wait.h>  /* for union wait */
  36: #include <sys/file.h>  /* for O_RDONLY and O_WRONLY */
  37: extern char *sprintf();
  38: #endif
  39: 
  40: #ifdef SYSV
  41: #include <fcntl.h>     /* for O_RDONLY and O_WRONLY */
  42: void exit();
  43: void perror();
  44: #endif
  45: 
  46: 
  47: 
  48: /* macros to find min or max of two values */
  49: #define min(a,b) ((a)<(b)? (a): (b))
  50: #define max(a,b) ((a)>(b)? (a): (b))
  51: 
  52: /* inherited file descriptors */
  53: #define STDIN 0
  54: #define STDOUT 1
  55: 
  56: /* boolean values */
  57: #define FALSE 0
  58: #define TRUE 1
  59: 
  60: /* pipes have a read end and a write end */
  61: #define P_REND 0
  62: #define P_WEND 1
  63: 
  64: /* there are two pipes; one for read tokens and one for write tokens */
  65: #define RTOK_P 0
  66: #define WTOK_P 1
  67: 
  68: /* token bytes passed along pipes */
  69: #define TOK_CONT 0     /* go ahead */
  70: #define TOK_DONE 1     /* end of data */
  71: #define TOK_ERROR 2    /* something's wrong, you've better stop now */
  72: 
  73: /* input/output full/short record counters are in a table;
  74:  indexes defined below */
  75: #define FULLIN 0
  76: #define SHORTIN 1
  77: #define FULLOUT 2
  78: #define SHORTOUT 3
  79: 
  80: /* defaults */
  81: #define DEFBS 512      /* default block size */
  82: 
  83: /* forward declarations */
  84: int doerror();
  85: 
  86: /* global variables */
  87: static int
  88:   ifd, ofd,    /* input/output file descriptors */
  89:   ibs, obs,    /* input/output block sizes */
  90:   cbs, /* "conversion" buffer size */
  91:   pid, /* pid of child (in parent) or 0 (in child) */
  92:   eof = FALSE, /* have we encountered end-of-file */
  93:   pipefd[2][2],        /* read/write fd's for 2 pipes */
  94:   counters[4] = {0, 0, 0, 0},  /* input/output full/short record counters */
  95:   buflen;      /* count of characters read into buffer */
  96: static char
  97:   *buffer;     /* address of buffer */
  98: 
  99: 
 100: main(argc, argv)
 101: int argc;
 102: char *argv[];
 103: {
 104:   (void) catchsignals();       /* prepare for interrupts etc */
 105:   (void) setdefaults();        /* set default values for parameters */
 106:   (void) parsearguments(argc, argv);   /* parse arguments */
 107:   (void) initbuffer(); /* initialize buffer */
 108:   (void) inittokens(); /* create one READ and one WRITE token */
 109:   (void) dofork();     /* 1 will be 2 */
 110:   while (!eof) {       /* enter main loop */
 111:     (void) gettoken(RTOK_P);   /* compete for first/next read turn */
 112:     (void) readbuffer();       /* fill buffer with input */
 113:     (void) gettoken(WTOK_P);   /* make sure we also get the next write turn */
 114:     /* now others may read if they wish (and if there's any data left */
 115:     (void) passtoken(RTOK_P, eof? TOK_DONE: TOK_CONT);
 116:     (void) writebuffer();      /* ... while we write to output */
 117:     /* this cycle is done now */
 118:     if (!eof) (void) passtoken(WTOK_P, TOK_CONT);
 119:   }    /* end of main loop */
 120:   (void) passcounters(RTOK_P); /* send record counters to our partner */
 121:   (void) terminate(0); /* and exit (no errors) */
 122:   /* NOTREACHED */
 123: }      /* end of main() */
 124: 
 125: 
 126: catchsignals()
 127: /* arrange for some signals to be catched, so that statistics can be printed */
 128: {
 129:   static int siglist[] = {
 130:     SIGINT, SIGQUIT, SIGILL, SIGFPE,
 131:     SIGBUS, SIGSEGV, SIGSYS, SIGPIPE,
 132:     SIGALRM, SIGTERM, 0
 133:     };
 134:   int *sigp;
 135: 
 136:   for (sigp = siglist; *sigp != 0; sigp++)
 137:     (void) signal(*sigp, doerror);
 138: }      /* end of catchsignals() */
 139: 
 140: doerror()
 141: /* what we do if we get an error or catch a signal */
 142: {
 143:   /* send error token to both pipes */
 144:   (void) passtoken(RTOK_P, TOK_ERROR);
 145:   (void) passtoken(WTOK_P, TOK_ERROR);
 146:   /* also send i/o record counters */
 147:   (void) passcounters(RTOK_P);
 148:   (void) passcounters(RTOK_P);
 149:   /* terminate with error status */
 150:   (void) terminate(1);
 151: }
 152: 
 153: terminate(stat)
 154: int stat;
 155: {
 156:   /* parent will try to wait for child */
 157: #ifdef BSD
 158:   if (pid) (void) wait((union wait *) 0);
 159: #endif
 160: #ifdef SYSV
 161:   if (pid) (void) wait((int *) 0);
 162: #endif
 163: 
 164:   exit(stat);
 165: }
 166: 
 167: setdefaults()
 168: /* set default values */
 169: {
 170:   ifd = STDIN;
 171:   ofd = STDOUT;
 172:   ibs = obs = DEFBS;   /* block sizes */
 173:   cbs = 0;     /* initially; will be set to max(ibs, obs, cbs) */
 174: }
 175: 
 176: parsearguments(argc, argv)
 177: int argc;
 178: char *argv[];
 179:   /* parse arguments */
 180: {
 181:   /* constant strings "array" for recognizing options */
 182:   static struct {
 183:     char *IF, *OF, *CBS, *IBS, *OBS, *BS, *NOTHING;
 184:   } consts = {
 185:     "if=", "of=", "cbs=", "ibs=", "obs=", "bs=", ""
 186:     };
 187:   char **constp;       /* const structure pointer */
 188: 
 189:   for (argc--, argv++; argc > 0; argc--, argv++) {
 190:     constp = (char **) &consts;
 191:     while (**constp && strncmp(*argv, *constp, strlen(*constp)))
 192:       constp++;
 193:     /* constp now points to one of the pointers in consts structure */
 194:     *argv += strlen(*constp);  /* skip the constant part of the argument */
 195:     if (constp == &consts.IF) {        /* open another file for input */
 196:       ifd = open(*argv, O_RDONLY);
 197:       if (ifd < 0) perror (*argv);
 198:     } else if (constp == &consts.OF) {
 199:       ofd = open(*argv, O_WRONLY | O_CREAT);   /* open file for output */
 200:       if (ofd < 0) perror (*argv);
 201:     } else if (constp == &consts.CBS) {        /* set buffer size */
 202:       cbs = evalopt(*argv);
 203:     } else if (constp == &consts.IBS) {        /* set input block size */
 204:       ibs = evalopt(*argv);
 205:     } else if (constp == &consts.OBS) {        /* set output block size */
 206:       obs = evalopt(*argv);
 207:     } else if (constp == &consts.BS) { /* set input and output block sizes */
 208:       ibs = obs = evalopt(*argv);
 209:     } else {
 210:       (void) fprintf(stderr,
 211:                     "usage: ddd [if=name] [of=name] [bs=n] [ibs=n obs=n]\n");
 212:       exit(1);
 213:     }
 214:   } /* end of for loop */
 215: } /* end of parsearguments() */
 216: 
 217: evalopt(p) /* return numerical value of string */
 218: char *p;
 219: {
 220:   int temp = 0;
 221: 
 222:   for ( ; *p >= '0' && *p <= '9'; p++)
 223:     temp = temp * 10 + *p - '0';
 224:   if (temp < 1) {
 225:     (void) fprintf(stderr, "ddd: illegal size option\n");
 226:     exit(1);
 227:   }
 228:   switch (*p) {
 229:   case '\0':
 230:     return(temp);
 231:   case 'w':
 232:   case 'W':
 233:     return(temp << 2); /* 4-byte words */
 234:   case 'b':
 235:   case 'B':
 236:     return(temp << 9); /* 512-byte blocks */
 237:   case 'k':
 238:   case 'K':
 239:     return(temp << 10);        /* kilobytes */
 240:   default:
 241:     (void) fprintf(stderr, "ddd: bad size option\n");
 242:     exit(1);
 243:   }
 244:   /* NOTREACHED */
 245: }      /* end of evalopt() */
 246: 
 247: initbuffer()
 248: /* initialize buffer */
 249: {
 250:   cbs = max(cbs, max(ibs, obs));       /* determine buffer size */
 251:   if (cbs % ibs || cbs % obs) {
 252:     (void) fprintf(stderr, "ddd: warning: incompatible block/buffer sizes\n");
 253:   }
 254:   buffer = malloc((unsigned) cbs);
 255:   if (buffer == NULL) {
 256:     (void) perror("ddd: cannot allocate buffer");
 257:     exit(1);
 258:   }
 259: }      /* end of initbuffer() */
 260: 
 261: inittokens()
 262: /* initialize token passing system with 2 pipes */
 263: {
 264:   if(pipe(pipefd[RTOK_P]) < 0 || pipe(pipefd[WTOK_P]) < 0) {
 265:     (void) perror("ddd: cannot create token pipes");
 266:     exit(1);
 267:   }
 268:   /* create initial tokens */
 269:   (void) passtoken(RTOK_P, TOK_CONT);
 270:   (void) passtoken(WTOK_P, TOK_CONT);
 271: }      /* end of inittokens() */
 272: 
 273: passtoken(pipenum, token)
 274: int pipenum;
 275: char token;
 276: /* pass a token to a pipe */
 277: {
 278:   if (write(pipefd[pipenum][P_WEND], &token, 1) < 1) {
 279:     (void) perror("ddd: cannot write token to pipe");
 280:     exit(1);
 281:   }
 282: }      /* end of passtoken() */
 283: 
 284: gettoken(pipenum)
 285: int pipenum;
 286: /* wait to read a token from the pipe; also see if we should stop */
 287: {
 288:   char tokenbuf;
 289: 
 290:   if (read(pipefd[pipenum][P_REND], &tokenbuf, 1) < 1) {
 291:     (void) perror("ddd: cannot read token from pipe");
 292:     exit(1);
 293:   }
 294:   if (tokenbuf != TOK_CONT) {  /* we did not get what we wanted */
 295:     (void) getcounters(pipenum);       /* report record counters */
 296:     terminate(tokenbuf == TOK_DONE);   /* TOK_DONE means no error */
 297:   }
 298: }      /* end of gettoken() */
 299: 
 300: passcounters(pipenum)
 301: int pipenum;
 302: /* pass read/write counters to the other process */
 303: {
 304:   if (write(pipefd[pipenum][P_WEND], (char *) counters,
 305:            sizeof(counters)) < sizeof(counters)) {
 306:     (void) perror("ddd: cannot write counters to pipe");
 307:     exit(1);
 308:   }
 309: }
 310: 
 311: getcounters(pipenum)
 312: int pipenum;
 313: /* report input/output record counts */
 314: {
 315:   int hiscounters[4];
 316: 
 317:   if (read(pipefd[pipenum][P_REND], (char *) hiscounters,
 318:           sizeof(hiscounters)) < sizeof(hiscounters)) {
 319:     (void) perror("ddd: cannot read counters from pipe");
 320:     exit(1);
 321:   }
 322:   (void) fprintf(stderr,
 323:                 "%d+%d records in\n%d+%d records out\n",
 324:                 counters[FULLIN] + hiscounters[FULLIN],
 325:                 counters[SHORTIN] + hiscounters[SHORTIN],
 326:                 counters[FULLOUT] + hiscounters[FULLOUT],
 327:                 counters[SHORTOUT] + hiscounters[SHORTOUT]
 328:                 );
 329: }      /* end of printcounters() */
 330: 
 331: dofork()
 332: /* fork into 2 processes */
 333: {
 334:   if ((pid = fork()) < 0) {
 335:     (void) perror("ddd: warning: cannot fork");
 336:     /* But continue and do our job anyway, as regular dd */
 337:   }
 338: }
 339: 
 340: readbuffer()
 341: /* read buffer from input */
 342: {
 343:   int iolen, ioresult;
 344: 
 345:   buflen = 0;
 346:   while (buflen < cbs && !eof) {
 347:     iolen = min(ibs, cbs - buflen);
 348: #ifdef BSD
 349:     ioresult = read(ifd, &buffer[buflen], iolen);
 350: #endif
 351: #ifdef SYSV
 352:     ioresult = read(ifd, &buffer[buflen], (unsigned) iolen);
 353: #endif
 354:     if (ioresult == 0) {       /* end of file */
 355:       eof = TRUE;
 356:     } else if (ioresult < 0) {
 357:       (void) perror("ddd: read error");
 358:       (void) doerror();
 359:     }
 360:     buflen += ioresult;        /* update current count of chars in buffer */
 361:     /* if we got any data, update appropriate input record count */
 362:     if (ioresult > 0) counters[(ioresult == ibs)? FULLIN: SHORTIN]++;
 363:   }
 364: }      /* end of readbuffer() */
 365: 
 366: writebuffer()
 367: /* writing phase */
 368: {
 369:   int ocount, iolen, ioresult;
 370: 
 371:   ocount = 0;  /* count of chars written */
 372:   while (ocount < buflen) {
 373:     iolen = min(obs, buflen - ocount);
 374: #ifdef BSD
 375:     ioresult = write(ofd, &buffer[ocount], iolen);
 376: #endif
 377: #ifdef SYSV
 378:     ioresult = write(ofd, &buffer[ocount], (unsigned) iolen);
 379: #endif
 380:     if (ioresult < iolen) {
 381:       perror("ddd: write error");
 382:       (void) doerror();
 383:     }
 384:     ocount += ioresult;
 385:     /* count output records */
 386:     counters[(ioresult == obs)? FULLOUT: SHORTOUT]++;
 387:   }
 388: }      /* end of writebuffer() */

Defined functions

catchsignals defined in line 126; used 1 times
doerror defined in line 140; used 4 times
dofork defined in line 331; used 1 times
evalopt defined in line 217; used 4 times
getcounters defined in line 311; used 1 times
gettoken defined in line 284; used 2 times
initbuffer defined in line 247; used 1 times
inittokens defined in line 261; used 1 times
main defined in line 100; never used
parsearguments defined in line 176; used 1 times
passcounters defined in line 300; used 3 times
passtoken defined in line 273; used 6 times
readbuffer defined in line 340; used 1 times
setdefaults defined in line 167; used 1 times
terminate defined in line 153; used 3 times
writebuffer defined in line 366; used 1 times

Defined variables

buffer defined in line 97; used 6 times
cbs defined in line 90; used 9 times
eof defined in line 92; used 5 times
ibs defined in line 89; used 7 times
ifd defined in line 88; used 5 times
obs defined in line 89; used 7 times
ofd defined in line 88; used 5 times
pid defined in line 91; used 3 times

Defined macros

DEFBS defined in line 81; used 1 times
FALSE defined in line 57; used 1 times
  • in line 92
FULLIN defined in line 75; used 3 times
FULLOUT defined in line 77; used 3 times
P_REND defined in line 61; used 2 times
P_WEND defined in line 62; used 2 times
RTOK_P defined in line 65; used 8 times
SHORTIN defined in line 76; used 3 times
SHORTOUT defined in line 78; used 3 times
STDIN defined in line 53; used 1 times
STDOUT defined in line 54; used 1 times
TOK_CONT defined in line 69; used 5 times
TOK_DONE defined in line 70; used 2 times
TOK_ERROR defined in line 71; used 2 times
TRUE defined in line 58; used 1 times
WTOK_P defined in line 66; used 5 times
max defined in line 50; used 2 times
  • in line 250(2)
min defined in line 49; used 2 times
Last modified: 1990-09-27
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 863
Valid CSS Valid XHTML 1.0 Strict