#define _POSIX_C_SOURCE 200809L #include /* assert */ /* Uvažme proudový protokol z předchozí přípravy. Vaším úkolem bude * naprogramovat klient, který ověří, zda je hodnota klíče na * serveru rovna nějaké očekávané. Protože hodnoty mohou být velké, * je očekávaná hodnota klientu předána jako soubor (resp. * popisovač, který odkazuje na obyčejný soubor). * * Pro připomenutí, požadavek klienta sestává z nulou ukončeného * klíče (budeme odesílat pouze jeden), odpověď serveru pak * z čtyřbajtové velikosti hodnoty a pak samotné hodnoty. * Nejvýznamnější bajt velikosti je odeslán jako první. * * Návratová hodnota bude: * * • 0 je-li vše v pořádku (hodnota odpovídá očekávání), * • -1 došlo-li k chybě komunikace nebo jiné fatální chybě, * • -2 nebyl-li požadovaný klíč na serveru přítomen, * • -3 jestli byla hodnota přijata, ale neodpovídá očekávání. */ int kvchk( int server_fd, const char *key, int data_fd ); /* ┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄┄┄┄ následují testy ┄┄┄┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄ */ #include #include #include #include #include #include #include #include #include #include #include static void close_or_warn( int fd, const char *name ) { if ( close( fd ) == -1 ) warn( "closing %s", name ); } static void unlink_if_exists( const char *file ) { if ( unlink( file ) == -1 && errno != ENOENT ) err( 2, "unlink" ); } static void write_or_die( int fd, const uint8_t *buffer, int nbytes ) { int bytes_written = write( fd, buffer, nbytes ); if ( bytes_written == -1 && errno == EPIPE ) return; if ( bytes_written == -1 ) err( 1, "writing %d bytes", nbytes ); if ( bytes_written != nbytes ) errx( 1, "unexpected short write: %d/%d written", bytes_written, nbytes ); } static int reap( pid_t pid ) { int status; if ( waitpid( pid, &status, 0 ) == -1 ) err( 2, "wait" ); if ( WIFEXITED( status ) ) return WEXITSTATUS( status ); else return -1; } static int create_file( const char *name ) { unlink_if_exists( name ); int fd; if ( ( fd = open( name, O_CREAT | O_TRUNC | O_RDWR, 0666 ) ) == -1 ) err( 2, "creating %s", name ); return fd; } static int fork_server( int *client_fd, void( *server )( int ), int close_fd ) { int fds[ 2 ]; if ( socketpair( AF_UNIX, SOCK_STREAM, 0, fds ) == -1 ) err( 1, "socketpair" ); int server_fd = fds[ 0 ]; *client_fd = fds[ 1 ]; int server_pid = fork(); if ( server_pid == -1 ) err( 1, "fork" ); if ( server_pid == 0 ) { close_or_warn( close_fd, "client state file" ); close_or_warn( *client_fd, "client end of the socket" ); server( server_fd ); close_or_warn( server_fd, "server end of the socket" ); exit( 0 ); } close_or_warn( server_fd, "server end of the socket" ); return server_pid; } static void expect( int fd, const char *str ) { char buffer[ 128 ]; do { int len = strlen( str ) > 128 ? 128 : strlen( str ) + 1; int bytes = read( fd, buffer, len ); if ( bytes == -1 ) err( 1, "reading data from client" ); assert( memcmp( str, buffer, bytes ) == 0 ); str += bytes; } while ( str[ -1 ] != 0 ); } static void server_1( int fd ) { expect( fd, "key_1" ); uint8_t buffer[ 512 ] = { 0 }; uint32_t size = htonl( 512 ); write_or_die( fd, ( uint8_t * ) &size, 4 ); write_or_die( fd, buffer, 512 ); assert( read( fd, buffer, 1 ) == 0 ); } static void server_2( int fd ) { expect( fd, "key_2" ); uint8_t buffer[ 300 ] = { 0, 0, 0, 0, 7, 3 }; uint32_t size = htonl( 296 ); memcpy( buffer, &size, 4 ); for ( int i = 0; i < 300; i += 9 ) { write_or_die( fd, buffer + i, 9 ); sched_yield(); } assert( read( fd, buffer, 1 ) == 0 ); } static int file_rewind( int fd ) { int size = lseek( fd, 0, SEEK_SET ); if ( size == -1 ) err( 1, "seek" ); return size; } int main() { const char *expect_name = "zt.p5_expect.bin"; uint8_t buffer[ 2048 ] = { 0 }; int client_fd, server_pid; int file_fd; signal( SIGPIPE, SIG_IGN ); file_fd = create_file( expect_name ); /* case 1 */ write_or_die( file_fd, buffer, 512 ); file_rewind( file_fd ); server_pid = fork_server( &client_fd, server_1, file_fd ); assert( kvchk( client_fd, "key_1", file_fd ) == 0 ); close_or_warn( client_fd, "client side of the socket" ); assert( reap( server_pid ) == 0 ); if ( ftruncate( file_fd, 0 ) != 0 ) err( 1, "truncating %s", expect_name ); /* case 2 */ buffer[ 0 ] = 7; buffer[ 1 ] = 3; file_rewind( file_fd ); write_or_die( file_fd, buffer, 296 ); file_rewind( file_fd ); server_pid = fork_server( &client_fd, server_2, file_fd ); assert( kvchk( client_fd, "key_2", file_fd ) == 0 ); close_or_warn( client_fd, "client side of the socket" ); assert( reap( server_pid ) == 0 ); if ( ftruncate( file_fd, 0 ) != 0 ) err( 1, "truncating %s", expect_name ); /* case 3 */ file_rewind( file_fd ); write_or_die( file_fd, buffer, 305 ); file_rewind( file_fd ); server_pid = fork_server( &client_fd, server_2, file_fd ); assert( kvchk( client_fd, "key_2", file_fd ) == -3 ); close_or_warn( client_fd, "client side of the socket" ); assert( reap( server_pid ) == 0 ); if ( ftruncate( file_fd, 0 ) != 0 ) err( 1, "truncating %s", expect_name ); /* case 4 */ file_rewind( file_fd ); write_or_die( file_fd, buffer, 200 ); file_rewind( file_fd ); server_pid = fork_server( &client_fd, server_2, file_fd ); assert( kvchk( client_fd, "key_2", file_fd ) == -3 ); close_or_warn( client_fd, "client side of the socket" ); assert( reap( server_pid ) == 0 ); close_or_warn( file_fd, expect_name ); unlink_if_exists( expect_name ); return 0; }