#include "serve.h" #include "util.h" #include "self_pipe.h" #include "client.h" #include "flexnbd.h" #include #include #include #include #include #include #include #include #ifdef DEBUG # define LOG_LEVEL 0 #else # define LOG_LEVEL 2 #endif /* Need these because libcheck is braindead and doesn't * run teardown after a failing test */ #define myfail( msg ) do { teardown(); fail(msg); } while (0) #define myfail_if( tst, msg ) do { if( tst ) { myfail( msg ); } } while (0) #define myfail_unless( tst, msg ) myfail_if( !(tst), msg ) char * dummy_file; char *make_tmpfile(void) { FILE *fp; char *fn_buf; char leader[] = "/tmp/check_serve"; fn_buf = (char *)malloc( 1024 ); strncpy( fn_buf, leader, sizeof( leader ) - 1); snprintf( &fn_buf[sizeof( leader ) - 1], 10, "%d", getpid() ); fp = fopen( fn_buf, "w" ); fwrite( fn_buf, 1024, 1, fp ); fclose( fp ); return fn_buf; } void setup( void ) { dummy_file = make_tmpfile(); } void teardown( void ) { if( dummy_file ){ unlink( dummy_file ); } free( dummy_file ); dummy_file = NULL; } START_TEST( test_replaces_acl ) { struct flexnbd flexnbd; flexnbd.signal_fd = -1; struct server * s = server_create( &flexnbd, "127.0.0.1", "0", dummy_file, 0, 0, NULL, 1, 0, 1 ); struct acl * new_acl = acl_create( 0, NULL, 0 ); server_replace_acl( s, new_acl ); myfail_unless( s->acl == new_acl, "ACL wasn't replaced." ); server_destroy( s ); } END_TEST START_TEST( test_signals_acl_updated ) { struct flexnbd flexnbd; flexnbd.signal_fd = -1; struct server * s = server_create( &flexnbd, "127.0.0.1", "0", dummy_file, 0, 0, NULL, 1, 0, 1 ); struct acl * new_acl = acl_create( 0, NULL, 0 ); server_replace_acl( s, new_acl ); myfail_unless( 1 == self_pipe_signal_clear( s->acl_updated_signal ), "No signal sent." ); server_destroy( s ); } END_TEST int connect_client( char *addr, int actual_port, char *source_addr ) { int client_fd = -1; struct addrinfo hint; struct addrinfo *ailist, *aip; memset( &hint, '\0', sizeof( struct addrinfo ) ); hint.ai_socktype = SOCK_STREAM; myfail_if( getaddrinfo( addr, NULL, &hint, &ailist ) != 0, "getaddrinfo failed." ); int connected = 0; for( aip = ailist; aip; aip = aip->ai_next ) { ((struct sockaddr_in *)aip->ai_addr)->sin_port = htons( actual_port ); client_fd = socket( aip->ai_family, aip->ai_socktype, aip->ai_protocol ); if (source_addr) { struct sockaddr src; if( !parse_ip_to_sockaddr(&src, source_addr)) { close(client_fd); continue; } bind(client_fd, &src, sizeof(struct sockaddr_in6)); } if( client_fd == -1) { continue; } if( connect( client_fd, aip->ai_addr, aip->ai_addrlen) == 0 ) { connected = 1; break; } close( client_fd ); } myfail_unless( connected, "Didn't connect." ); return client_fd; } /* These are "internal" functions we need for the following test. We * shouldn't need them but there's no other way at the moment. */ void serve_open_server_socket( struct server * ); int server_port( struct server * ); void server_accept( struct server * ); int fd_is_closed( int ); void server_close_clients( struct server * ); START_TEST( test_acl_update_closes_bad_client ) { /* This is the wrong way round. Rather than pulling the thread * and socket out of the server structure, we should be testing * a client socket. */ struct flexnbd flexnbd; flexnbd.signal_fd = -1; struct server * s = server_create( &flexnbd, "127.0.0.7", "0", dummy_file, 0, 0, NULL, 1, 0, 1 ); struct acl * new_acl = acl_create( 0, NULL, 1 ); struct client * c; struct client_tbl_entry * entry; int actual_port; int client_fd; int server_fd; serve_open_server_socket( s ); actual_port = server_port( s ); client_fd = connect_client( "127.0.0.7", actual_port, "127.0.0.1" ); server_accept( s ); entry = &s->nbd_client[0]; c = entry->client; /* At this point there should be an entry in the nbd_clients * table and a background thread to run the client loop */ myfail_if( entry->thread == 0, "No client thread was started." ); server_fd = c->socket; myfail_if( fd_is_closed(server_fd), "Sanity check failed - client socket wasn't open." ); server_replace_acl( s, new_acl ); /* accept again, so that we can react to the acl replacement signal */ server_accept( s ); /* Fail if we time out here */ while( !fd_is_closed( server_fd ) ); close( client_fd ); server_close_clients( s ); server_destroy( s ); } END_TEST START_TEST( test_acl_update_leaves_good_client ) { struct flexnbd flexnbd; flexnbd.signal_fd = -1; struct server * s = server_create( &flexnbd, "127.0.0.7", "0", dummy_file, 0, 0, NULL, 1, 0, 1 ); char *lines[] = {"127.0.0.1"}; struct acl * new_acl = acl_create( 1, lines, 1 ); struct client * c; struct client_tbl_entry * entry; int actual_port; int client_fd; int server_fd; serve_open_server_socket( s ); actual_port = server_port( s ); client_fd = connect_client( "127.0.0.7", actual_port, "127.0.0.1" ); server_accept( s ); entry = &s->nbd_client[0]; c = entry->client; /* At this point there should be an entry in the nbd_clients * table and a background thread to run the client loop */ myfail_if( entry->thread == 0, "No client thread was started." ); server_fd = c->socket; myfail_if( fd_is_closed(server_fd), "Sanity check failed - client socket wasn't open." ); server_replace_acl( s, new_acl ); server_accept( s ); myfail_if( self_pipe_signal_clear( c->stop_signal ), "Client was told to stop." ); close( client_fd ); server_close_clients( s ); server_destroy( s ); } END_TEST Suite* serve_suite(void) { Suite *s = suite_create("serve"); TCase *tc_acl_update = tcase_create("acl_update"); tcase_add_checked_fixture( tc_acl_update, setup, NULL ); tcase_add_test(tc_acl_update, test_replaces_acl); tcase_add_test(tc_acl_update, test_signals_acl_updated); tcase_add_exit_test(tc_acl_update, test_acl_update_closes_bad_client, 0); tcase_add_exit_test(tc_acl_update, test_acl_update_leaves_good_client, 0); suite_add_tcase(s, tc_acl_update); return s; } int main(void) { log_level = LOG_LEVEL; error_init(); int number_failed; Suite *s = serve_suite(); SRunner *sr = srunner_create(s); srunner_run_all(sr, CK_NORMAL); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0) ? 0 : 1; }