/* pipemeter - A program to show status of a pipe Copyright Clint Byrum 2002, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --- */ #include #include #include #include #include #include #include #include #include #include #include #include #define VERSION "0.7.0" #define DEFAULT_BLOCK_SIZE 8192 #define PBAR "[------------------------------------------------------------]" #define STARS "************************************************************" #define PBAR_LEN 60 // Includes /s #define SPEED_LEN 12 #define PCT_LEN 6 // TODO: Ncurses to make it purty /* You sure we need ncurses? It's getting close without it... and I dunno if ncurses functions are safe to call from a signal handler (must read, must read...) --B. */ /* agreed ... no curses for now. But maybe a hook for an X interface * of some kind(a seperate binary maybe... */ // TODO: async/multi threaded to seperate reading and writing. void show_just_rate(int sig); void nshow_just_rate(int sig); void show_progress(int sig); void parseopts(int argc, char *argv[],char **buffer); void formatbytes(char *obuffer,double b); void setup_window(void); // XXX: keep an eye on these ... sooo much global :P long long bytes; long long lastbytes; long long filesize = 0; double itimer_seconds; long block_size; char *filename; // XXX: hrm... I just don't like this char ncurses_enabled=0; char *progressbar; int main(int argc, char *argv[]) { struct itimerval it; //FIXME: these should be initialized! (sig_empty or something?) // (I have a book I don't feel like looking for right now that explains it) struct sigaction sa,sa_orig; struct timeval interval; long bytesin; char *buffer; int in,out; out=fileno(stdout); lastbytes=0; bytes=0; parseopts(argc,argv,&buffer); if(filename == NULL) { in=fileno(stdin); } else { //fprintf(stderr, "opening %s for reading\n\n\n", filename); in = open(filename, O_RDONLY); if(in < 0) { perror("can't open input file"); exit(2); } if(filesize) { fprintf(stderr, "Warning: -s overrides size of file given by -f!\n"); } else { struct stat s; if(fstat(in, &s) != 0) { perror("fstat"); exit(3); } filesize = s.st_size; //fprintf(stderr, "filesize is %lld\n", filesize); /* note: trying to use fstat on a file descriptor opened to /dev/zero results in a st_size of 0, which by coincidence disables the progress bar. This is exactly what I wanted, but I don't know how portable it is. Also: giving -f file on the command line shows the progress bar for regular files, with no way to disable it. This is *not* what I wanted, but will be OK for now. -- B. */ } } progressbar=(char *)malloc(PBAR_LEN*sizeof(char)); initscr(); // XXX: this segfaults if its anywhere else strcpy(progressbar,PBAR); // setup timers if(ncurses_enabled) { setup_window(); sa.sa_handler=nshow_just_rate; } else { sa.sa_handler= filesize ? show_progress : show_just_rate; } sigaction(SIGALRM, &sa, &sa_orig); interval.tv_usec = itimer_seconds * 1000000; interval.tv_sec = 0; it.it_value=interval; it.it_interval=interval; if(setitimer(ITIMER_REAL,&it,NULL)) { fprintf(stderr,"Error setting itimer %d.\n",errno); exit(1); } while((bytesin=read(in,buffer,block_size))) { bytes += bytesin; write(out,buffer,bytesin); } if(filesize) { show_progress(0); fprintf(stderr, "\n"); } return 0; } void show_just_rate(int sig) { /* if no progress bar, we use this function */ char numbuf[512]; // XXX: hrm... bad form, but so much simpler double bytespersecond=(double)(bytes-lastbytes)/(double)itimer_seconds; formatbytes(numbuf,bytespersecond); fprintf(stderr,"%s/s\r",numbuf); lastbytes=bytes; } void show_progress(int sig) { //char buf[512]; // XXX: yes, this is bad form. char buf2[512]; // XXX: hopefully we fix them before there are 100 or so double percent = (double)bytes / (double)filesize * 100; int progress = (double)bytes / (double)filesize * PBAR_LEN; double bytespersecond = (double)(bytes-lastbytes)/(double)itimer_seconds; formatbytes(buf2,bytespersecond); lastbytes=bytes; if(progress > PBAR_LEN) { fprintf(stderr, "Ow! -s filesize parameter (%lld) overflowed!\n", filesize); progress = PBAR_LEN; // just keep printing full progress bars and warnings. } //strcpy(buf, "[------------------------------------------------------------]"); //strncpy(buf+1, "************************************************************", progress); strncpy(progressbar+1,STARS,progress); //fprintf(stderr, "%d ", progress); fprintf(stderr, progressbar); fprintf(stderr," %s/s", buf2); fprintf(stderr, " %2.1f%%\r", percent); } void nshow_just_rate(int sig) { char numbuf[512]; // XXX: hrm... bad form, but so much simpler double bytespersecond=(double)(bytes-lastbytes)/(double)itimer_seconds; formatbytes(numbuf,bytespersecond); //fprintf(stderr,"%s/s\r",numbuf); addstr(numbuf); lastbytes=bytes; refresh(); } void parseopts(int argc, char *argv[], char **buffer) { int c; // Set some defaults //itimer_interval=1; itimer_seconds=1; block_size=DEFAULT_BLOCK_SIZE; filename = NULL; do { c=getopt(argc,argv,"f:s:b:i:Vn"); switch(c) { case -1: // No more options break; case 0: // No options passed, this is fine. break; case 'b': block_size=strtol(optarg,NULL,10); if(block_size==LONG_MIN || block_size==LONG_MAX || block_size <= 0) { fprintf(stderr,"Bad block size: %s\n",optarg); exit(1); } break; case 'i': itimer_seconds=strtod(optarg,NULL); if(itimer_seconds <= 0.0) { fprintf(stderr,"Bad interval: %s\n",optarg); exit(1); } break; case 's': filesize=strtol(optarg,NULL,10); if(filesize < 1) { fprintf(stderr, "Bad filesize: %s\n", optarg); exit(1); } break; case 'f': filename = optarg; break; case 'n': ncurses_enabled=1; break; case 'V': // Exiting here because otherwise we'll start trying to read/write fprintf(stderr,"pipemeter v%s\n",VERSION); exit(0); case '?': case ':': // XXX: better errors default: fprintf(stderr,"usage: pipemeter { -b blocksize } { -i interval }\n"); fprintf(stderr,"debug: %c %d\n",(char)c,c); exit(1); } } while(c > 0); *buffer=(char *)malloc(block_size*sizeof(char)); } void formatbytes(char *obuffer,double b) { double tmp; // If these are ever changed, SPEED_LEN must be updated if(b>1073741824) { tmp=b/1073741824; sprintf(obuffer,"%6.2fG",tmp); } else if(b>1048576) { tmp=b/1048576; sprintf(obuffer,"%6.2fM",tmp); } else if(b>2048) { // under 2k, let it be tmp=b/1024; sprintf(obuffer,"%6.2fk",tmp); } else sprintf(obuffer,"%6.2fB",b); } void setup_window(void) { int mx,my; int i,lim; int pbarlen; //initscr(); if(has_colors()) { start_color(); init_pair(COLOR_GREEN,COLOR_GREEN,COLOR_BLACK); init_pair(COLOR_BLUE,COLOR_BLUE,COLOR_BLACK); init_pair(COLOR_WHITE,COLOR_WHITE,COLOR_BLACK); } if(filesize){ getmaxyx(stdscr,my,mx); pbarlen = mx-SPEED_LEN+PCT_LEN; // XXX: lets make sure the progress bar is right-sized free(progressbar); progressbar=(char *)malloc(((mx+1)*sizeof(char))); // lim=mx-1; progressbar[0]='['; for(i=0;i