2012-06-11 10:08:24 +01:00
|
|
|
#include "serve.h"
|
2012-06-11 10:04:31 +01:00
|
|
|
#include "util.h"
|
|
|
|
|
2012-06-08 10:32:33 +01:00
|
|
|
#include "self_pipe.h"
|
2012-06-08 18:03:41 +01:00
|
|
|
#include "client.h"
|
2012-06-08 10:32:33 +01:00
|
|
|
|
2012-06-08 18:03:41 +01:00
|
|
|
#include <stdlib.h>
|
2012-06-08 10:32:33 +01:00
|
|
|
#include <check.h>
|
|
|
|
#include <stdio.h>
|
2012-06-08 18:03:41 +01:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <netdb.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
2012-06-08 10:32:33 +01:00
|
|
|
|
|
|
|
|
2012-06-08 18:03:41 +01:00
|
|
|
char * dummy_file;
|
|
|
|
|
2012-06-11 13:57:03 +01:00
|
|
|
char *make_tmpfile(void)
|
2012-06-08 10:32:33 +01:00
|
|
|
{
|
2012-06-08 18:03:41 +01:00
|
|
|
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;
|
|
|
|
}
|
2012-06-08 11:02:40 +01:00
|
|
|
|
2012-06-08 10:32:33 +01:00
|
|
|
|
2012-06-08 18:03:41 +01:00
|
|
|
void setup( void )
|
|
|
|
{
|
|
|
|
dummy_file = make_tmpfile();
|
|
|
|
}
|
2012-06-08 10:32:33 +01:00
|
|
|
|
2012-06-08 18:03:41 +01:00
|
|
|
void teardown( void )
|
|
|
|
{
|
|
|
|
if( dummy_file ){ unlink( dummy_file ); }
|
|
|
|
free( dummy_file );
|
|
|
|
dummy_file = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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 )
|
|
|
|
|
|
|
|
|
|
|
|
START_TEST( test_replaces_acl )
|
|
|
|
{
|
|
|
|
struct server * s = server_create( "127.0.0.1", "0", dummy_file, NULL, 0, 0, NULL );
|
|
|
|
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 );
|
2012-06-08 10:32:33 +01:00
|
|
|
}
|
|
|
|
END_TEST
|
|
|
|
|
|
|
|
|
|
|
|
START_TEST( test_signals_acl_updated )
|
|
|
|
{
|
2012-06-08 18:03:41 +01:00
|
|
|
struct server * s = server_create( "127.0.0.1", "0", dummy_file, NULL, 0, 0, NULL );
|
2012-06-08 10:32:33 +01:00
|
|
|
struct acl * new_acl = acl_create( 0, NULL, 0 );
|
|
|
|
|
2012-06-08 18:03:41 +01:00
|
|
|
server_replace_acl( s, new_acl );
|
2012-06-08 10:32:33 +01:00
|
|
|
|
2012-06-11 10:04:31 +01:00
|
|
|
myfail_unless( 1 == self_pipe_signal_clear( s->acl_updated_signal ),
|
2012-06-08 18:03:41 +01:00
|
|
|
"No signal sent." );
|
|
|
|
server_destroy( s );
|
|
|
|
}
|
|
|
|
END_TEST
|
2012-06-08 10:32:33 +01:00
|
|
|
|
|
|
|
|
2012-06-11 14:40:41 +01:00
|
|
|
int connect_client( char *addr, int actual_port, char *source_addr )
|
2012-06-08 18:03:41 +01:00
|
|
|
{
|
|
|
|
int client_fd;
|
|
|
|
|
2012-06-11 13:57:03 +01:00
|
|
|
struct addrinfo hint;
|
2012-06-08 18:03:41 +01:00
|
|
|
struct addrinfo *ailist, *aip;
|
2012-06-11 13:57:03 +01:00
|
|
|
|
2012-06-11 14:40:41 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
2012-06-11 13:57:03 +01:00
|
|
|
memset( &hint, '\0', sizeof( struct addrinfo ) );
|
2012-06-08 18:03:41 +01:00
|
|
|
hint.ai_socktype = SOCK_STREAM;
|
|
|
|
|
2012-06-11 13:57:03 +01:00
|
|
|
myfail_if( getaddrinfo( addr, NULL, &hint, &ailist ) != 0, "getaddrinfo failed." );
|
2012-06-11 10:04:31 +01:00
|
|
|
|
2012-06-08 18:03:41 +01:00
|
|
|
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 );
|
2012-06-11 14:40:41 +01:00
|
|
|
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2012-06-08 18:03:41 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-06-11 13:57:03 +01:00
|
|
|
/* 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 * );
|
2012-06-08 18:03:41 +01:00
|
|
|
|
|
|
|
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 server * s = server_create( "127.0.0.7", "0", dummy_file, NULL, 0, 0, NULL );
|
|
|
|
struct acl * new_acl = acl_create( 0, NULL, 1 );
|
|
|
|
struct client * c;
|
|
|
|
struct client_tbl_entry * entry;
|
|
|
|
|
2012-06-11 10:04:31 +01:00
|
|
|
int actual_port;
|
2012-06-08 18:03:41 +01:00
|
|
|
int client_fd;
|
|
|
|
int server_fd;
|
|
|
|
|
|
|
|
|
|
|
|
serve_open_server_socket( s );
|
|
|
|
actual_port = server_port( s );
|
|
|
|
|
2012-06-11 14:40:41 +01:00
|
|
|
client_fd = connect_client( "127.0.0.7", actual_port, "127.0.0.1" );
|
2012-06-08 18:03:41 +01:00
|
|
|
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;
|
2012-06-11 10:04:31 +01:00
|
|
|
myfail_if( fd_is_closed(server_fd),
|
2012-06-08 18:03:41 +01:00
|
|
|
"Sanity check failed - client socket wasn't open." );
|
|
|
|
|
|
|
|
server_replace_acl( s, new_acl );
|
|
|
|
|
|
|
|
server_accept( s );
|
|
|
|
|
2012-06-11 10:04:31 +01:00
|
|
|
pthread_join( entry->thread, NULL );
|
2012-06-08 18:03:41 +01:00
|
|
|
|
2012-06-11 10:04:31 +01:00
|
|
|
myfail_unless( fd_is_closed(server_fd),
|
2012-06-08 18:03:41 +01:00
|
|
|
"Client socket wasn't closed." );
|
|
|
|
close( client_fd );
|
|
|
|
server_close_clients( s );
|
|
|
|
server_destroy( s );
|
|
|
|
}
|
|
|
|
END_TEST
|
|
|
|
|
|
|
|
|
|
|
|
START_TEST( test_acl_update_leaves_good_client )
|
|
|
|
{
|
|
|
|
struct server * s = server_create( "127.0.0.7", "0", dummy_file, NULL, 0, 0, NULL );
|
2012-06-11 12:56:45 +01:00
|
|
|
|
2012-06-11 14:40:41 +01:00
|
|
|
char *lines[] = {"127.0.0.1"};
|
|
|
|
struct acl * new_acl = acl_create( 1, lines, 1 );
|
2012-06-08 18:03:41 +01:00
|
|
|
struct client * c;
|
|
|
|
struct client_tbl_entry * entry;
|
|
|
|
|
2012-06-11 10:04:31 +01:00
|
|
|
int actual_port;
|
2012-06-08 18:03:41 +01:00
|
|
|
int client_fd;
|
|
|
|
int server_fd;
|
|
|
|
|
|
|
|
serve_open_server_socket( s );
|
|
|
|
actual_port = server_port( s );
|
|
|
|
|
2012-06-11 14:40:41 +01:00
|
|
|
client_fd = connect_client( "127.0.0.7", actual_port, "127.0.0.1" );
|
2012-06-08 18:03:41 +01:00
|
|
|
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;
|
2012-06-11 10:04:31 +01:00
|
|
|
myfail_if( fd_is_closed(server_fd),
|
2012-06-08 18:03:41 +01:00
|
|
|
"Sanity check failed - client socket wasn't open." );
|
|
|
|
|
|
|
|
server_replace_acl( s, new_acl );
|
|
|
|
server_accept( s );
|
|
|
|
|
2012-06-11 10:04:31 +01:00
|
|
|
myfail_if( self_pipe_signal_clear( c->stop_signal ),
|
2012-06-08 18:03:41 +01:00
|
|
|
"Client was told to stop." );
|
|
|
|
|
|
|
|
close( client_fd );
|
|
|
|
server_close_clients( s );
|
|
|
|
server_destroy( s );
|
2012-06-08 10:32:33 +01:00
|
|
|
}
|
|
|
|
END_TEST
|
|
|
|
|
|
|
|
|
2012-06-11 13:57:03 +01:00
|
|
|
Suite* serve_suite(void)
|
2012-06-08 10:32:33 +01:00
|
|
|
{
|
|
|
|
Suite *s = suite_create("serve");
|
|
|
|
TCase *tc_acl_update = tcase_create("acl_update");
|
2012-06-11 10:04:31 +01:00
|
|
|
|
2012-06-08 18:03:41 +01:00
|
|
|
tcase_add_checked_fixture( tc_acl_update, setup, teardown );
|
2012-06-08 10:32:33 +01:00
|
|
|
tcase_add_test(tc_acl_update, test_replaces_acl);
|
|
|
|
tcase_add_test(tc_acl_update, test_signals_acl_updated);
|
2012-06-08 18:03:41 +01:00
|
|
|
tcase_add_test(tc_acl_update, test_acl_update_closes_bad_client);
|
|
|
|
|
|
|
|
tcase_add_test(tc_acl_update, test_acl_update_leaves_good_client);
|
2012-06-08 10:32:33 +01:00
|
|
|
|
|
|
|
suite_add_tcase(s, tc_acl_update);
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int main(void)
|
|
|
|
{
|
2012-06-11 14:59:26 +01:00
|
|
|
log_level = 2;
|
2012-06-08 10:32:33 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|