#define _POSIX_C_SOURCE 200809L #include /* assert */ #include /* err */ #include /* errno, EAGAIN, EBADF */ #include /* fcntl */ #include /* strncmp */ #include /* uint16_t */ #include /* exit */ #include /* send, socketpair */ #include /* waitpid */ #include /* alarm, close, fork, pipe, read */ /* Naprogramujte proceduru ‹slide›, která obdrží: * * • ‹dgram_fd›, popisovač datagramového socketu, * • ‹stream_fd›, popisovač výstupního proudu. * * Na ‹dgram_fd› budou přicházet datagramy, složené z: * * 1. dvoubajtové hlavičky, která obsahuje: * a. pořadové číslo fragmentu, * b. indikátor ukončení. * 2. obsahu (dat) fragmentu. * * Procedura ‹slide› bude přepisovat obsah fragmentů ve správném * pořadí do popisovače ‹stream_fd›. Fragment s nenulovým * indikátorem ukončení je poslední (žádné další již nebudou * následovat). * * Opakované fragmenty a fragmenty „za koncem“ proudu dat ignorujte. * * Procedura musí být paměťově efektivní – smí v paměti udržovat * pouze data, které prozatím není možné odeslat. * * Procedura skončí ve chvíli, kdy byl do výstupního proudu úspěšně * zapsán poslední datagram. Návratová hodnota: * * • ≥0 vše proběhlo v pořádku, přitom hodnota indikuje počet * chybných (ignorovaných) datagramů, * • -1 nastala systémová chyba. */ int slide( int sock_fd, int stream_fd ); /* ┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄┄┄┄ následují testy ┄┄┄┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄ */ static void close_or_warn( int fd, const char *name ) { if ( close( fd ) == -1 ) warn( "closing %s", name ); } static void send_or_die( int fd, const void *buffer, int nbytes ) { int bytes_sent = send( fd, buffer, nbytes, 0 ); if ( bytes_sent == -1 ) err( 1, "sending %d bytes", nbytes ); if ( bytes_sent != nbytes ) errx( 1, "unexpected short send: %d/%d sent", bytes_sent, nbytes ); } void read_or_die( int fd, const char *exp, int nbytes ) { assert( nbytes <= 256 ); char buff[ 256 ]; int off = 0; int rv = 0; while ( ( rv = read( fd, buff + off, nbytes - off ) ) > 0 ) off += rv; if ( rv == -1 && errno != EAGAIN ) err( 1, "read" ); assert(( strncmp( buff, exp, nbytes ) == 0 )); } void check_empty_pipe( int fd ) { int flags = fcntl( fd, F_GETFL ); if ( fcntl( fd, F_SETFL, flags | O_NONBLOCK ) == -1 ) err( 1, "fcntl" ); char buff; int rv = read( fd, &buff, 1 ); assert(( rv == -1 && errno == EAGAIN )); if ( fcntl( fd, F_SETFL, flags ) == -1 ) err( 1, "fcntl" ); } void test1( int sock_fd, int read_fd ) { const char *frags[] = { "\x01\x00Get a good job with more pay and you're okay", "\x00\x00Money, get away", "\x04\x00New car, caviar, four-star daydream", "\x02\x00Money, it's a gas", "\x03\x00Grab that cash with both hands and make a stash", "\x05\x42Think I'll buy me a football team" }; const char lens[] = { 46, 17, 37, 19, 49, 35 }; for ( int i = 0; i < 2; ++ i ) send_or_die( sock_fd, frags[ i ], lens[ i ] ); read_or_die( read_fd, frags[ 1 ] + 2, lens[ 1 ] - 2 ); read_or_die( read_fd, frags[ 0 ] + 2, lens[ 0 ] - 2 ); check_empty_pipe( read_fd ); for ( int i = 2; i < 5; ++ i ) send_or_die( sock_fd, frags[ i ], lens[ i ] ); read_or_die( read_fd, frags[ 3 ] + 2, lens[ 3 ] - 2 ); read_or_die( read_fd, frags[ 4 ] + 2, lens[ 4 ] - 2 ); read_or_die( read_fd, frags[ 2 ] + 2, lens[ 2 ] - 2 ); check_empty_pipe( read_fd ); send_or_die( sock_fd, frags[ 5 ], lens[ 5 ] ); read_or_die( read_fd, frags[ 5 ] + 2, lens[ 5 ] - 2 ); check_empty_pipe( read_fd ); } void test2( int sock_fd, int read_fd ) { const char *frags[] = { "\x80\000a mlady to neumi.", "\x00\000Pec nam spadla, pec nam spadla,", "\x7f\000kdopak nam ji postavi?", "\xff\000Pec nam spadla, pec nam spadla,", "\x02\000Stary pecar neni doma", "\x01\000kdopak nam ji postavi?", "\x01\000a mlady to neumi.", "\x09\000kdopak nam ji postavi?", "\x02\000Stary pecar neni doma", "\x05\000Stary pecar neni doma", "\x03\042a mlady to neumi." }; const char lens[] = { 19, 33, 24, 33, 23, 24, 19, 24, 23, 23, 19 }; for ( int i = 0; i < 2; ++ i ) send_or_die( sock_fd, frags[ i ], lens[ i ] ); read_or_die( read_fd, frags[ 1 ] + 2, lens[ 1 ] - 2 ); check_empty_pipe( read_fd ); for ( int i = 2; i < 6; ++ i ) send_or_die( sock_fd, frags[ i ], lens[ i ] ); read_or_die( read_fd, frags[ 5 ] + 2, lens[ 5 ] - 2 ); read_or_die( read_fd, frags[ 4 ] + 2, lens[ 4 ] - 2 ); check_empty_pipe( read_fd ); for ( int i = 6; i < 10; ++ i ) send_or_die( sock_fd, frags[ i ], lens[ i ] ); check_empty_pipe( read_fd ); send_or_die( sock_fd, frags[ 10 ], lens[ 10 ] ); read_or_die( read_fd, frags[ 10 ] + 2, lens[ 10 ] - 2 ); check_empty_pipe( read_fd ); } static int reap( pid_t pid ) { int status; if ( waitpid( pid, &status, 0 ) == -1 ) err( 2, "wait" ); return WIFEXITED( status ) ? WEXITSTATUS( status ) : -1; } static pid_t fork_server( int *sock_fd, int *pipe_fd, void( *test )( int, int ) ) { int sock_fds[ 2 ]; int pipe_fds[ 2 ]; if ( socketpair( AF_UNIX, SOCK_DGRAM, 0, sock_fds ) == -1 ) err( 1, "socketpair" ); if ( pipe( pipe_fds ) == -1 ) err( 1, "pipe" ); pid_t pid = fork(); if ( pid == -1 ) err( 1, "fork" ); if ( pid == 0 ) { close_or_warn( sock_fds[ 1 ], "client side of a socket" ); close_or_warn( pipe_fds[ 1 ], "write end of a pipe" ); alarm( 5 ); test( sock_fds[ 0 ], pipe_fds[ 0 ] ); close_or_warn( sock_fds[ 0 ], "server side of a socket" ); close_or_warn( pipe_fds[ 0 ], "read end of a pipe" ); exit( 0 ); } close_or_warn( sock_fds[ 0 ], "server side of a socket" ); close_or_warn( pipe_fds[ 0 ], "read end of a pipe" ); *sock_fd = sock_fds[ 1 ]; *pipe_fd = pipe_fds[ 1 ]; return pid; } int main( void ) { int sock_fd, write_fd; pid_t pid; /* Test case 1. */ pid = fork_server( &sock_fd, &write_fd, test1 ); assert(( slide( sock_fd, write_fd ) == 0 )); assert(( reap( pid ) == 0 )); close_or_warn( sock_fd, "client side of a socket" ); close_or_warn( write_fd, "write end of a pipe" ); /* Test case 2. */ pid = fork_server( &sock_fd, &write_fd, test2 ); assert(( slide( sock_fd, write_fd ) == 7 )); assert(( reap( pid ) == 0 )); close_or_warn( sock_fd, "client side of a socket" ); close_or_warn( write_fd, "write end of a pipe" ); /* Test case 3. */ assert(( slide( -1, -1 ) == -1 )); assert(( errno == EBADF )); return 0; }