#define _POSIX_C_SOURCE 200809L #include /* assert */ #include /* err, warn, warnx */ #include /* exit */ #include /* alarm, close, fork, read, write */ /* Naprogramujte podprogram ‹bridge›, který obdrží dva popisovače, * přičemž data, která přichází na jeden z nich, přepošle na ten * opačný bez zbytečné prodlevy – komunikaci může blokovat pouze * situace, kdy některá strana nestíhá data přijímat. Pro účely * tohoto příkladu je ale v pořádku, budou-li v této situaci * blokovány oba směry.¹ Návratová hodnota budiž -1 při systémové * chybě a 0, pokud komunikace úspěšně skončí uzavřením spojení * na některém z popisovačů. */ int bridge( int fd_1, int fd_2 ); /* ¹ Můžete si samozřejmě zkusit naprogramovat řešení, které bude * schopno data ve volném směru přeposílat i když je ten druhý * zablokovaný. Budete k tomu potřebovat ‹POLLOUT› a ‹O_NONBLOCK› * – prostředky, o kterých se víc dozvíte v další kapitole. */ /* ┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄┄┄┄ následují testy ┄┄┄┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄ */ #include /* strcmp, strlen */ #include /* socketpair */ #include /* waitpid */ static void close_or_warn( int fd, const char *name ) { if ( close( fd ) == -1 ) warn( "closing %s", name ); } static int reap( pid_t pid ) { int status; if ( waitpid( pid, &status, 0 ) == -1 ) err( 2, "waitpid" ); if ( WIFEXITED( status ) ) return WEXITSTATUS( status ); else return -1; } static pid_t spawn_bridge( int *fd_1, int *fd_2 ) { int sock_1[ 2 ]; if ( socketpair( AF_UNIX, SOCK_STREAM, 0, sock_1 ) == -1 ) err( 1, "socketpair 1" ); int sock_2[ 2 ]; if ( socketpair( AF_UNIX, SOCK_STREAM, 0, sock_2 ) == -1 ) err( 1, "socketpair 2" ); pid_t pid = fork(); if ( pid == -1 ) err( 1, "fork" ); if ( pid > 0 ) { close_or_warn( sock_1[ 1 ], "child's socket 1 in parent" ); close_or_warn( sock_2[ 1 ], "child's socket 2 in parent" ); *fd_1 = sock_1[ 0 ]; *fd_2 = sock_2[ 0 ]; return pid; } alarm( 1 ); close_or_warn( sock_1[ 0 ], "parent's socket 1 in child" ); close_or_warn( sock_2[ 0 ], "parent's socket 2 in child" ); int rv = bridge( sock_1[ 1 ], sock_2[ 1 ] ); close_or_warn( sock_1[ 1 ], "child's socket 1" ); close_or_warn( sock_2[ 1 ], "child's socket 2" ); exit( rv ); } static int push( int fd, const char *msg ) { if ( write( fd, msg, strlen( msg ) ) == -1 ) return warn( "push write" ), 0; return 1; } static int pull( int fd, const char *msg ) { char buf[ 256 ]; int len = strlen( msg ); int read_total = 0; int bytes_read; while ( read_total < len && ( bytes_read = read( fd, buf + read_total, sizeof( buf ) - read_total - 1 ) ) > 0 ) { read_total += bytes_read; } if ( bytes_read == -1 ) return warn( "pull read" ), 0; buf[ read_total ] = '\0'; if ( strcmp( msg, buf ) != 0 ) return warnx( "pull unexpected message: %s", buf ), 0; return 1; } static int transfer( int fd_in, int fd_out, const char *msg ) { return push( fd_in, msg ) && pull( fd_out, msg ); } int main( void ) { int fd_1, fd_2; // Test case 1. pid_t pid = spawn_bridge( &fd_1, &fd_2 ); close_or_warn( fd_1, "parent's socket 1" ); close_or_warn( fd_2, "parent's socket 2" ); assert( reap( pid ) == 0 ); // Test case 2. pid = spawn_bridge( &fd_1, &fd_2 ); assert( transfer( fd_1, fd_2, "only one is speaking" ) ); assert( transfer( fd_1, fd_2, "and it is the first one" ) ); close_or_warn( fd_1, "parent's socket 1" ); close_or_warn( fd_2, "parent's socket 2" ); assert( reap( pid ) == 0 ); // Test case 3. pid = spawn_bridge( &fd_1, &fd_2 ); assert( transfer( fd_2, fd_1, "only one is speaking again" ) ); assert( transfer( fd_2, fd_1, "and it is the second one this time" ) ); close_or_warn( fd_1, "parent's socket 1" ); close_or_warn( fd_2, "parent's socket 2" ); assert( reap( pid ) == 0 ); // Test case 4. pid = spawn_bridge( &fd_1, &fd_2 ); assert( transfer( fd_1, fd_2, "sockets are wonderful" ) ); assert( transfer( fd_2, fd_1, "poll is great" ) ); assert( transfer( fd_1, fd_2, "select less so" ) ); close_or_warn( fd_1, "parent's socket 1" ); close_or_warn( fd_2, "parent's socket 2" ); assert( reap( pid ) == 0 ); // Test case 5. pid = spawn_bridge( &fd_1, &fd_2 ); assert( transfer( fd_2, fd_1, "hello from the other side" ) ); assert( transfer( fd_1, fd_2, "order should not matter" ) ); assert( transfer( fd_2, fd_1, "wooooo!" ) ); assert( transfer( fd_2, fd_1, "and another one in this direction" ) ); close_or_warn( fd_1, "parent's socket 1" ); close_or_warn( fd_2, "parent's socket 2" ); assert( reap( pid ) == 0 ); // Test case 6. pid = spawn_bridge( &fd_1, &fd_2 ); assert( push( fd_1, "lets talk " ) ); assert( push( fd_1, "over each other." ) ); assert( push( fd_2, "okay great idea." ) ); assert( push( fd_1, "okay!" ) ); assert( push( fd_2, "well?" ) ); assert( pull( fd_2, "lets talk over each other.okay!" ) ); assert( pull( fd_1, "okay great idea.well?" ) ); close_or_warn( fd_1, "parent's socket 1" ); close_or_warn( fd_2, "parent's socket 2" ); assert( reap( pid ) == 0 ); return 0; }