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() */