#define _POSIX_C_SOURCE 200809L #include /* write, read */ #include /* openat, O_* */ #include /* strlen */ #include /* close, open */ #include /* htonl, ntohl */ #include /* errno */ #include /* exit */ #include #include /* Vaším úkolem je tentokrát naprogramovat jednoduchý server, který * bude komunikovat prostřednictvím proudového socketu s jedním * klientem. Struktura protokolu je velmi jednoduchá: klient bude * odesílat „klíče“ ukončené nulovým bajtem. Ke každému klíči, který * server přečte, odešle odpověď, která bude obsahovat čtyřbajtovou * délku hodnoty, která klíči náleží, následovanou samotnou * hodnotou. Není-li klíč přítomen, odešle hodnotu 0xffffffff. * Nejvýznamnější bajt je vždy odesílán jako první. * * Krom nulového bajtu (který slouží jako oddělovač) nebudou klíče * obsahovat ani znak lomítka ‹/›. Klíče jsou navíc omezeny na 512 * bajtů – je-li klíč na vstupu delší, nebo obsahuje znak ‹/›, * server tuto skutečnost detekuje a odpoví hodnotou 0xfffffffe (bez * ohledu na to, existuje-li odpovídající klíč). Zpracování pak * pokračuje dalším klíčem. * * Klíče a hodnoty jsou uloženy v souborovém systému, ve složce * předané podprogramu ‹kvsd› popisovačem ‹root_fd›. Klíč je název * souboru, hodnota je pak jeho obsah. * * Podprogram ‹kvsd› vrátí hodnotu -1 v případě fatální chyby, jinak * hodnotu 0 (poté, co protistrana ukončí spojení). Neexistence * klíče není fatální chybou (viz výše), jiné chyby při otevírání * souboru ale ano. */ int kvsd( int root_fd, int client_fd ); /* ┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄┄┄┄ následují testy ┄┄┄┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄ */ #include /* mkdir */ #include /* socketpair */ #include /* waitpid */ static void close_or_warn( int fd, const char *name ) { if ( close( fd ) == -1 ) warn( "closing %s", name ); } static void unlink_if_exists( int dir_fd, const char *file ) { if ( unlinkat( dir_fd, file, 0 ) == -1 && errno != ENOENT ) err( 2, "unlink %s", file ); } static void mkdir_or_die( const char* dirname ) { if ( mkdir( dirname , 0755 ) == -1 && errno != EEXIST ) err( 2, "mkdir" ); } static void prepare_file( int dir_fd, const char* name, const char* content ) { unlink_if_exists( dir_fd, name ); int fd = openat( dir_fd, name, O_CREAT | O_WRONLY, 0755 ); if ( fd == -1 ) err( 2, "creating input file" ); if ( write( fd, content, strlen( content ) ) == -1 ) err( 2, "writing input file" ); close( fd ); } static int fork_client( const char *to_write, int w_bytes, const char *expect, int e_bytes, int *client_fd, int dir_fd ) { int fds[ 2 ]; if ( socketpair( AF_UNIX, SOCK_STREAM, 0, fds ) == -1 ) err( 1, "socketpair" ); pid_t pid = fork(); alarm( 5 ); if ( pid == -1 ) err( 2, "fork" ); if ( pid == 0 ) // child → client { close( fds[ 0 ] ); close( dir_fd ); if ( write( fds[ 1 ], to_write, w_bytes ) == -1 ) err( 2, "client write" ); char buf[ e_bytes ]; int total = 0, nread; while ( ( nread = read( fds[ 1 ], buf + total, e_bytes - total ) ) > 0 ) total += nread; if ( nread == -1 ) err( 2, "client read" ); assert( total == e_bytes ); assert( memcmp( buf, expect, e_bytes ) == 0 ); close( fds[ 1 ] ); exit( 0 ); } close( fds[ 1 ] ); *client_fd = fds[ 0 ]; return pid; } static int reap( pid_t pid ) { int status; if ( waitpid( pid, &status, 0 ) == -1 ) err( 2, "waiting for %d", pid ); if ( WIFEXITED( status ) ) return WEXITSTATUS( status ); else return -1; } int main() { const char* dir_path = "zt.p4_kvsd"; mkdir_or_die( dir_path ); int dir_fd = open( dir_path, O_DIRECTORY ); if ( dir_fd == -1 ) err( 2, "opening directory" ); prepare_file( dir_fd, "key1", "a" ); prepare_file( dir_fd, "key2", "bb" ); prepare_file( dir_fd, "key3", "ccc" ); prepare_file( dir_fd, "empty", "" ); int pid, sock_fd; pid = fork_client( "key1\0", 5, "\0\0\0\01a", 5, &sock_fd, dir_fd ); assert( kvsd( dir_fd, sock_fd ) == 0 ); assert( reap( pid ) == 0); close_or_warn( sock_fd, "server side of the socket" ); pid = fork_client( "key2\0key3\0", 10, "\0\0\0\02bb\0\0\0\03ccc", 13, &sock_fd, dir_fd ); assert( kvsd( dir_fd, sock_fd ) == 0 ); assert( reap( pid ) == 0); close_or_warn( sock_fd, "server side of the socket" ); pid = fork_client( "empty\0", 6, "\0\0\0\0", 4, &sock_fd, dir_fd ); assert( kvsd( dir_fd, sock_fd ) == 0 ); assert( reap( pid ) == 0); close_or_warn( sock_fd, "server side of the socket" ); pid = fork_client( "nope\0", 5, "\xff\xff\xff\xff", 4, &sock_fd, dir_fd ); assert( kvsd( dir_fd, sock_fd ) == 0 ); assert( reap( pid ) == 0); close_or_warn( sock_fd, "server side of the socket" ); pid = fork_client( "no/e\0", 5, "\xff\xff\xff\xfe", 4, &sock_fd, dir_fd ); assert( kvsd( dir_fd, sock_fd ) == 0 ); assert( reap( pid ) == 0); close_or_warn( sock_fd, "server side of the socket" ); close_or_warn( dir_fd, dir_path ); return 0; }