#include /* errno */ #include /* O_RDONLY et al. */ #include /* SIGKILL, etc. */ #include /* printf, fprintf, EOF, etc. */ #include /* exit(3). */ #include /* strlen(3), strrchr(3), etc. */ #include /* fstat(2) */ #include /* wait(2) */ #include /* open(2), close(2), getopt(3), etc. */ /* ** Constants. */ #define BFRSZ 256 #define INVALID_DESC -1 /* ** Macro to terminate program on failed system call. */ #define SYSCALL(expr) \ do \ { \ if ( (expr) == -1 ) \ { \ int _err = errno; \ fprintf( stderr, \ "%s (line %d): %s.\n", \ pgmName, \ __LINE__, \ strerror( _err ) ); \ kill( 0, SIGKILL ); /* Terminate process group. */ \ } \ } while ( 0 ) /* ** Usage macro. */ #define VALIDOPTS ":v" #define USAGE() \ do \ { \ fprintf( stderr, \ "Usage: %s [-v] path\n", \ pgmName ); \ exit( 127 ); \ } while ( 0 ) /* ** Main program. Write into a FIFO, and then call fstat(2) to determine the ** amount written. Unfortunately, due to the rules defining how FIFOs are ** opened, this program is somewhat more complicated than it should be; i.e. it ** forks a child process to handle one end of the pipe. */ int main( int argc, char *argv[ ] ) { char bfr[ BFRSZ ]; int fifoRead = INVALID_DESC; int fifoWrite; struct stat info; int opt; pid_t pid; char *pgmName; size_t sz; int verbose = 0; /* ** Set program name (w/o directory prefix). */ pgmName = strrchr( argv[ 0 ], '/' ); pgmName = pgmName == NULL ? argv[ 0 ] : pgmName+1; /* ** Process command options. */ while ( ( opt = getopt( argc, argv, VALIDOPTS ) ) != EOF ) { switch ( opt ) { case 'v': /* Verbose mode. */ verbose = 1; break; default: USAGE( ); /* Terminates program. */ } /* End SWITCH on command option. */ } /* End WHILE processing command options. */ /* ** Check command line arguments. */ if ( argc != ( optind+1 ) ) { USAGE( ); /* Terminates program. */ } /* ** Create the FIFO. */ if ( verbose ) fprintf( stderr, "Creating FIFO...\n" ); if ( ( mknod( argv[ 1 ], S_IFIFO | 0666, 0 ) < 0 ) && ( errno != EEXIST ) ) { SYSCALL( -1 ); /* Force error termination. */ } /* ** Main loop. The child process writes into the FIFO, and the parent reads ** it, based on info from fstat(2). */ do { /* ** Fork a child to write to the FIFO. Note this is a new child with each ** loop iteration (makes it easy for the parent to sync up). */ SYSCALL( pid = fork( ) ); if ( pid == 0 ) /* Child process. */ { /* ** Open FIFO for writing. */ if ( verbose ) fprintf( stderr, "Opening FIFO for writing...\n" ); SYSCALL( fifoWrite = open( argv[ 1 ], O_WRONLY ) ); /* ** Read a line of input from the user. */ fprintf( stderr, "Enter a string to write into the FIFO: " ); errno = 0; /* To differentiate between EOF and error. */ if ( fgets( bfr, BFRSZ, stdin ) == NULL ) { if ( errno != 0 ) { SYSCALL( -1 ); /* Root out the underlying system error. */ } /* ** You'd think fgets(3) would stick an ASCII NUL into the buffer on ** EOF like it does normally, but apparently it doesn't! So ** explicitly add it manually. This causes a null string to be ** written to the FIFO when EOF occurs (i.e. one NUL character). */ *bfr = '\0'; } /* End IF fgets(3) returned NULL. */ /* ** Write the FIFO and terminate the child process. */ sz = strlen( bfr ) + 1; /* +1 for null terminator. */ if ( sz == BFRSZ ) { /* ** Buffer is full. The user _may_ (not necessarily) have entered a ** string that is too long for the buffer and was truncated. Force ** a newline at the end just in case. [Not to be confused with the ** '\0' end-of-string.] The tail end of the string is lost when ** the child process terminates. */ bfr[ sz-2 ] = '\n'; } /* End IF buffer full. */ if ( verbose ) { fprintf( stderr, "Child writing %u bytes into FIFO...\n", sz ); } SYSCALL( write( fifoWrite, bfr, sz ) ); SYSCALL( fstat( fifoWrite, &info ) ); if ( info.st_size != sz ) { fprintf( stderr, "%s: WARNING - %lu (of %u requested) bytes " "written into FIFO.\n", pgmName, info.st_size, sz ); } SYSCALL( close( fifoWrite ) ); exit( 0 ); } /* End IF child process. */ /* ** Parent process. Open the FIFO for reading (if not already open). */ if ( fifoRead == INVALID_DESC ) { if ( verbose ) fprintf( stderr, "Opening FIFO for reading...\n" ); SYSCALL( fifoRead = open( argv[ 1 ], O_RDONLY ) ); } /* ** Wait for the child to write the pipe. */ wait( NULL ); /* ** Read in from the pipe. fstat(2) tells the number of bytes to read ** (i.e. the amount of data in the pipe). */ SYSCALL( fstat( fifoRead, &info ) ); printf( "%lu bytes in FIFO.\n", info.st_size ); *bfr = '\0'; /* In case fstat(2) messes up (st_size == 0). */ SYSCALL( read( fifoRead, bfr, info.st_size ) ); fputs( bfr, stdout ); } while ( info.st_size > 1 ); /* 1 = EOF; 0 = child error termination. */ SYSCALL( close( fifoRead ) ); return 0; } /* End main() */