diff options
Diffstat (limited to 'src/server/server.c')
-rw-r--r-- | src/server/server.c | 243 |
1 files changed, 206 insertions, 37 deletions
diff --git a/src/server/server.c b/src/server/server.c index 10ab208..0f75935 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -3,7 +3,7 @@ * * Copyright(c) 2011-2012 Johann Latocha <johann@latocha.de> * - * This file may be licensed under the terms of of the + * This file may be licensed under the terms of the * GNU General Public License Version 2 (the ``GPL''). * * Software distributed under the License is distributed @@ -29,14 +29,18 @@ #include "integrity.h" #include "threadpool.h" #include "rpc.h" +#include "fuse.h" -#include "../version.h" -#include "../shared/sockhelper.h" -#include "../shared/timing.h" +#include <dnbd3/version.h> +#include <dnbd3/build.h> +#include <dnbd3/shared/sockhelper.h> +#include <dnbd3/shared/timing.h> #include <signal.h> #include <getopt.h> #include <assert.h> +#include <sys/types.h> +#include <unistd.h> #define LONGOPT_CRC4 1000 #define LONGOPT_ASSERT 1001 @@ -45,6 +49,26 @@ #define LONGOPT_SIZE 1004 #define LONGOPT_ERRORMSG 1005 +typedef struct _job job_t; + +struct _job { + job_t *next; + void *(*startRoutine)(void *); + void *arg; + ticks dueDate; + int intervalSecs; +}; + +static job_t *jobHead; +static _Atomic(job_t *) newJob; +static bool hasTimerThread = false; +static pthread_t timerThread; + +static pid_t mainPid; +static pthread_t mainThread; + +#define DEFAULT_TIMER_TIMEOUT (60) + static poll_list_t *listeners = NULL; /** @@ -71,15 +95,25 @@ static void dnbd3_handleSignal2(int signum, siginfo_t *info, void *data); static void* server_asyncImageListLoad(void *data); +static void* timerMainloop(void*); + +static int handlePendingJobs(void); + +static void queueJobInternal(job_t *job); + /** * Print help text for usage instructions */ void dnbd3_printHelp(char *argv_0) { - printf( "Version: %s\n\n", VERSION_STRING ); + printf( "Version: %s\n\n", DNBD3_VERSION_LONG ); + printf( "Built: %s\n", DNBD3_BUILD_DATE ); printf( "Usage: %s [OPTIONS]...\n", argv_0 ); printf( "Start the DNBD3 server\n" ); printf( "-c or --config Configuration directory (default /etc/dnbd3-server/)\n" ); +#ifdef DNBD3_SERVER_FUSE + printf( "-m or --mount FUSE mount point\n"); +#endif printf( "-n or --nodaemon Start server in foreground\n" ); printf( "-b or --bind Local Address to bind to\n" ); printf( "-h or --help Show this help text and quit\n" ); @@ -98,21 +132,31 @@ void dnbd3_printHelp(char *argv_0) */ void dnbd3_printVersion() { - printf( "Version: %s\n", VERSION_STRING ); + printf( "dnbd3-server version: %s\n", DNBD3_VERSION_LONG ); + printf( "Built: %s\n", DNBD3_BUILD_DATE ); exit( 0 ); } /** * Clean up structs, connections, write out data, then exit */ -void dnbd3_cleanup() +_Noreturn static void dnbd3_cleanup() { int retries; _shutdown = true; logadd( LOG_INFO, "Cleanup..." ); - if ( listeners != NULL ) sock_destroyPollList( listeners ); + dfuse_shutdown(); + + if ( hasTimerThread ) { + pthread_kill( timerThread, SIGINT ); + thread_join( timerThread, NULL ); + } + + if ( listeners != NULL ) { + sock_destroyPollList( listeners ); + } listeners = NULL; // Kill connection to all clients @@ -121,9 +165,6 @@ void dnbd3_cleanup() // Disable threadpool threadpool_close(); - // Terminate the altserver checking thread - altservers_shutdown(); - // Terminate all uplinks image_killUplinks(); @@ -133,8 +174,7 @@ void dnbd3_cleanup() // Wait for clients to disconnect net_waitForAllDisconnected(); - // Watchdog not needed anymore - debug_locks_stop_watchdog(); + threadpool_waitEmpty(); // Clean up images retries = 5; @@ -159,11 +199,13 @@ int main(int argc, char *argv[]) char *paramCreate = NULL; char *bindAddress = NULL; char *errorMsg = NULL; + char *mountDir = NULL; int64_t paramSize = -1; int paramRevision = -1; - static const char *optString = "b:c:d:hnv?"; + static const char *optString = "b:c:m:d:hnv?"; static const struct option longOpts[] = { { "config", required_argument, NULL, 'c' }, + { "mount", required_argument, NULL, 'm' }, { "nodaemon", no_argument, NULL, 'n' }, { "reload", no_argument, NULL, 'r' }, { "help", no_argument, NULL, 'h' }, @@ -178,6 +220,18 @@ int main(int argc, char *argv[]) { 0, 0, 0, 0 } }; + log_init(); + + /* set proper output stream for AFL */ +#ifdef DNBD3_SERVER_AFL + if ( log_setConsoleOutputStream(stderr) < 0 ) { + logadd( LOG_ERROR, "Failed to set output stream for AFL to stderr" ); + exit( EXIT_FAILURE ); + } +#endif + + mainPid = getpid(); + mainThread = pthread_self(); opt = getopt_long( argc, argv, optString, longOpts, &longIndex ); while ( opt != -1 ) { @@ -185,6 +239,13 @@ int main(int argc, char *argv[]) case 'c': _configDir = strdup( optarg ); break; + case 'm': +#ifndef DNBD3_SERVER_FUSE + fprintf( stderr, "FUSE support not enabled at build time.\n" ); + return 8; +#endif + mountDir = strdup( optarg ); + break; case 'n': demonize = 0; break; @@ -201,6 +262,15 @@ int main(int argc, char *argv[]) case LONGOPT_CRC4: return image_generateCrcFile( optarg ) ? 0 : EXIT_FAILURE; case LONGOPT_ASSERT: + printf( "Now leaking memory:\n" ); + char *bla = malloc( 10 ); + bla[2] = 3; + bla = NULL; + printf( "Testing use after free:\n" ); + char *test = malloc( 10 ); + test[0] = 1; + free( (void*)test ); + test[1] = 2; printf( "Testing a failing assertion:\n" ); assert( 4 == 5 ); printf( "Assertion 4 == 5 seems to hold. ;-)\n" ); @@ -221,6 +291,7 @@ int main(int argc, char *argv[]) opt = getopt_long( argc, argv, optString, longOpts, &longIndex ); } + // Load general config if ( _configDir == NULL ) _configDir = strdup( "/etc/dnbd3-server" ); @@ -233,9 +304,7 @@ int main(int argc, char *argv[]) timing_setBase(); timing_get( &startupTime ); -#ifdef AFL_MODE - // ###### AFL - // +#ifdef DNBD3_SERVER_AFL image_serverStartup(); net_init(); uplink_globalsInit(); @@ -259,9 +328,7 @@ int main(int argc, char *argv[]) net_handleNewConnection( dnbd3_client ); exit( 0 ); } - // - // ###### AFL END -#endif +#endif /* DNBD3_SERVER_AFL */ // One-shots first: @@ -273,7 +340,10 @@ int main(int argc, char *argv[]) // No one-shot detected, normal server operation or errormsg serving if ( demonize ) { logadd( LOG_INFO, "Forking into background, see log file for further information" ); - daemon( 1, 0 ); + if ( daemon( 0, 0 ) == -1 ) { + logadd( LOG_ERROR, "Could not daemon(): errno=%d", errno ); + exit( 1 ); + } } if ( errorMsg != NULL ) { setupNetwork( bindAddress ); @@ -297,22 +367,25 @@ int main(int argc, char *argv[]) net_init(); uplink_globalsInit(); rpc_init(); - logadd( LOG_INFO, "DNBD3 server starting.... Machine type: " ENDIAN_MODE ); + if ( mountDir != NULL && !dfuse_init( "-oallow_other", mountDir ) ) { + logadd( LOG_ERROR, "Cannot mount fuse directory to %s", mountDir ); + dnbd3_cleanup(); + return EXIT_FAILURE; + } + logadd( LOG_INFO, "DNBD3 server starting...." ); + logadd( LOG_INFO, "Machine type: " DNBD3_ENDIAN_MODE ); + logadd( LOG_INFO, "Build Type: %s", DNBD3_BUILD ); + logadd( LOG_INFO, "Version: %s, built %s", DNBD3_VERSION_LONG, DNBD3_BUILD_DATE ); if ( altservers_load() < 0 ) { logadd( LOG_WARNING, "Could not load alt-servers. Does the file exist in %s?", _configDir ); } -#ifdef _DEBUG - debug_locks_start_watchdog(); -#endif - // setup signal handler - struct sigaction sa; - memset( &sa, 0, sizeof(sa) ); - sa.sa_sigaction = dnbd3_handleSignal2; - sa.sa_flags = SA_SIGINFO; - //sa.sa_mask = ; + struct sigaction sa = { + .sa_sigaction = dnbd3_handleSignal2, + .sa_flags = SA_SIGINFO, + }; sigaction( SIGTERM, &sa, NULL ); sigaction( SIGINT, &sa, NULL ); sigaction( SIGUSR1, &sa, NULL ); @@ -342,10 +415,15 @@ int main(int argc, char *argv[]) // Initialize thread pool if ( !threadpool_init( 8 ) ) { logadd( LOG_ERROR, "Could not init thread pool!\n" ); + dnbd3_cleanup(); exit( EXIT_FAILURE ); } - logadd( LOG_INFO, "Server is ready. (%s)", VERSION_STRING ); + logadd( LOG_INFO, "Server is ready." ); + + if ( thread_create( &timerThread, NULL, &timerMainloop, NULL ) == 0 ) { + hasTimerThread = true; + } // +++++++++++++++++++++++++++++++++++++++++++++++++++ main loop struct sockaddr_storage client; @@ -357,7 +435,7 @@ int main(int argc, char *argv[]) if ( sigReload ) { sigReload = false; logadd( LOG_INFO, "SIGHUP received, re-scanning image directory" ); - threadpool_run( &server_asyncImageListLoad, NULL ); + threadpool_run( &server_asyncImageListLoad, NULL, "IMAGE_RELOAD" ); } if ( sigLogCycle ) { sigLogCycle = false; @@ -370,7 +448,7 @@ int main(int argc, char *argv[]) // len = sizeof(client); fd = sock_accept( listeners, &client, &len ); - if ( fd < 0 ) { + if ( fd == -1 ) { const int err = errno; if ( err == EINTR || err == EAGAIN ) continue; logadd( LOG_ERROR, "Client accept failure (err=%d)", err ); @@ -384,7 +462,7 @@ int main(int argc, char *argv[]) continue; } - if ( !threadpool_run( &net_handleNewConnection, (void *)dnbd3_client ) ) { + if ( !threadpool_run( &net_handleNewConnection, (void *)dnbd3_client, "CLIENT" ) ) { logadd( LOG_ERROR, "Could not start thread for new connection." ); free( dnbd3_client ); continue; @@ -474,8 +552,17 @@ static void dnbd3_handleSignal(int signum) static void dnbd3_handleSignal2(int signum, siginfo_t *info, void *data UNUSED) { - memcpy( &lastSignal, info, sizeof(siginfo_t) ); - dnbd3_handleSignal( signum ); + if ( info->si_pid != mainPid ) { // Source is not this process + memcpy( &lastSignal, info, sizeof(siginfo_t) ); // Copy signal info + if ( info->si_pid != 0 && !pthread_equal( pthread_self(), mainThread ) ) { + pthread_kill( mainThread, info->si_signo ); // And relay signal if we're not the main thread + } + // Source is not this process -- only then do we honor signals + if ( pthread_equal( pthread_self(), mainThread ) ) { + // Signal received by main thread -- handle + dnbd3_handleSignal( signum ); + } + } } uint32_t dnbd3_serverUptime() @@ -493,3 +580,85 @@ static void* server_asyncImageListLoad(void *data UNUSED) return NULL; } +static void* timerMainloop(void* stuff UNUSED) +{ + setThreadName( "timer" ); + while ( !_shutdown ) { + // Handle jobs/timer events; returns timeout until next event + int to = handlePendingJobs(); + sleep( MIN( MAX( 1, to ), DEFAULT_TIMER_TIMEOUT ) ); + } + logadd( LOG_DEBUG1, "Timer thread done" ); + return NULL; +} + +static int handlePendingJobs(void) +{ + declare_now; + job_t *todo, **temp, *old; + int diff; + todo = jobHead; + for ( temp = &todo; *temp != NULL; temp = &(*temp)->next ) { + diff = (int)timing_diff( &now, &(*temp)->dueDate ); + if ( diff > 0 ) // Found one that's in the future + break; + } + jobHead = *temp; // Make it list head + *temp = NULL; // Split off part before that + while ( todo != NULL ) { + threadpool_run( todo->startRoutine, todo->arg, "TIMER_TASK" ); + old = todo; + todo = todo->next; + if ( old->intervalSecs == 0 ) { + free( old ); // oneshot + } else { + timing_set( &old->dueDate, &now, old->intervalSecs ); + queueJobInternal( old ); // repeated + } + } + // See if any new jobs have been queued + while ( newJob != NULL ) { + todo = newJob; + // NULL should never happen since we're the only consumer + assert( todo != NULL ); + if ( !atomic_compare_exchange_weak( &newJob, &todo, NULL ) ) + continue; + do { + old = todo; + todo = todo->next; + queueJobInternal( old ); + } while ( todo != NULL ); + } + // Return new timeout + if ( jobHead == NULL ) + return DEFAULT_TIMER_TIMEOUT; + return (int)timing_diff( &now, &jobHead->dueDate ); +} + +static void queueJobInternal(job_t *job) +{ + assert( job != NULL ); + job_t **it; + for ( it = &jobHead; *it != NULL; it = &(*it)->next ) { + if ( timing_1le2( &job->dueDate, &(*it)->dueDate ) ) + break; + } + job->next = *it; + *it = job; +} + +void server_addJob(void *(*startRoutine)(void *), void *arg, int delaySecs, int intervalSecs) +{ + declare_now; + job_t *new = malloc( sizeof(*new) ); + new->startRoutine = startRoutine; + new->arg = arg; + new->intervalSecs = intervalSecs; + timing_set( &new->dueDate, &now, delaySecs ); + for ( ;; ) { + new->next = newJob; + if ( atomic_compare_exchange_weak( &newJob, &new->next, new ) ) + break; + } +} + |