summaryrefslogtreecommitdiffstats
path: root/src/server/server.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/server.c')
-rw-r--r--src/server/server.c243
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;
+ }
+}
+