From 43e57ce5e11e9052f5a7db66f2e8613f1784f919 Mon Sep 17 00:00:00 2001 From: Frederic Robra Date: Tue, 25 Jun 2019 17:03:28 +0200 Subject: first version of dnbd3-ng --- src/server/server.c | 495 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 495 insertions(+) create mode 100644 src/server/server.c (limited to 'src/server/server.c') diff --git a/src/server/server.c b/src/server/server.c new file mode 100644 index 0000000..10ab208 --- /dev/null +++ b/src/server/server.c @@ -0,0 +1,495 @@ + /* + * This file is part of the Distributed Network Block Device 3 + * + * Copyright(c) 2011-2012 Johann Latocha + * + * This file may be licensed under the terms of of the + * GNU General Public License Version 2 (the ``GPL''). + * + * Software distributed under the License is distributed + * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the GPL for the specific language + * governing rights and limitations. + * + * You should have received a copy of the GPL along with this + * program. If not, go to http://www.gnu.org/licenses/gpl.html + * or write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "server.h" +#include "helper.h" + +#include "locks.h" +#include "image.h" +#include "uplink.h" +#include "net.h" +#include "altservers.h" +#include "integrity.h" +#include "threadpool.h" +#include "rpc.h" + +#include "../version.h" +#include "../shared/sockhelper.h" +#include "../shared/timing.h" + +#include +#include +#include + +#define LONGOPT_CRC4 1000 +#define LONGOPT_ASSERT 1001 +#define LONGOPT_CREATE 1002 +#define LONGOPT_REVISION 1003 +#define LONGOPT_SIZE 1004 +#define LONGOPT_ERRORMSG 1005 + +static poll_list_t *listeners = NULL; + +/** + * Time the server was started + */ +static ticks startupTime; +static bool sigReload = false, sigLogCycle = false; + +/** + * Copied to in signal handler so we can print info + * later on + */ +static siginfo_t lastSignal; + +void printSignal(); + +static poll_list_t* setupNetwork(char *bindAddress); + +static dnbd3_client_t* dnbd3_prepareClient(struct sockaddr_storage *client, int fd); + +static void dnbd3_handleSignal(int signum); + +static void dnbd3_handleSignal2(int signum, siginfo_t *info, void *data); + +static void* server_asyncImageListLoad(void *data); + +/** + * Print help text for usage instructions + */ +void dnbd3_printHelp(char *argv_0) +{ + printf( "Version: %s\n\n", VERSION_STRING ); + printf( "Usage: %s [OPTIONS]...\n", argv_0 ); + printf( "Start the DNBD3 server\n" ); + printf( "-c or --config Configuration directory (default /etc/dnbd3-server/)\n" ); + 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" ); + printf( "-v or --version Show version and quit\n" ); + printf( "\nManagement functions:\n" ); + printf( "--crc [image-file] Generate crc block list for given image\n" ); + printf( "--create [image-name] --revision [rid] --size [filesize]\n" + "\tCreate a local empty image file with a zeroed cache-map for the specified image\n" ); + printf( "--errormsg [text] Just serve given error message via HTTP, no service otherwise\n" ); + printf( "\n" ); + exit( 0 ); +} + +/** + * Print version information + */ +void dnbd3_printVersion() +{ + printf( "Version: %s\n", VERSION_STRING ); + exit( 0 ); +} + +/** + * Clean up structs, connections, write out data, then exit + */ +void dnbd3_cleanup() +{ + int retries; + + _shutdown = true; + logadd( LOG_INFO, "Cleanup..." ); + + if ( listeners != NULL ) sock_destroyPollList( listeners ); + listeners = NULL; + + // Kill connection to all clients + net_disconnectAll(); + + // Disable threadpool + threadpool_close(); + + // Terminate the altserver checking thread + altservers_shutdown(); + + // Terminate all uplinks + image_killUplinks(); + + // Terminate integrity checker + integrity_shutdown(); + + // Wait for clients to disconnect + net_waitForAllDisconnected(); + + // Watchdog not needed anymore + debug_locks_stop_watchdog(); + + // Clean up images + retries = 5; + while ( !image_tryFreeAll() && --retries > 0 ) { + logadd( LOG_INFO, "Waiting for images to free...\n" ); + sleep( 1 ); + } + + free( _basePath ); + free( _configDir ); + exit( EXIT_SUCCESS ); +} + +/** + * Program entry point + */ +int main(int argc, char *argv[]) +{ + int demonize = 1; + int opt = 0; + int longIndex = 0; + char *paramCreate = NULL; + char *bindAddress = NULL; + char *errorMsg = NULL; + int64_t paramSize = -1; + int paramRevision = -1; + static const char *optString = "b:c:d:hnv?"; + static const struct option longOpts[] = { + { "config", required_argument, NULL, 'c' }, + { "nodaemon", no_argument, NULL, 'n' }, + { "reload", no_argument, NULL, 'r' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "bind", required_argument, NULL, 'b' }, + { "crc", required_argument, NULL, LONGOPT_CRC4 }, + { "assert", no_argument, NULL, LONGOPT_ASSERT }, + { "create", required_argument, NULL, LONGOPT_CREATE }, + { "revision", required_argument, NULL, LONGOPT_REVISION }, + { "size", required_argument, NULL, LONGOPT_SIZE }, + { "errormsg", required_argument, NULL, LONGOPT_ERRORMSG }, + { 0, 0, 0, 0 } + }; + + opt = getopt_long( argc, argv, optString, longOpts, &longIndex ); + + while ( opt != -1 ) { + switch ( opt ) { + case 'c': + _configDir = strdup( optarg ); + break; + case 'n': + demonize = 0; + break; + case 'h': + case '?': + dnbd3_printHelp( argv[0] ); + break; + case 'v': + dnbd3_printVersion(); + break; + case 'b': + bindAddress = strdup( optarg ); + break; + case LONGOPT_CRC4: + return image_generateCrcFile( optarg ) ? 0 : EXIT_FAILURE; + case LONGOPT_ASSERT: + printf( "Testing a failing assertion:\n" ); + assert( 4 == 5 ); + printf( "Assertion 4 == 5 seems to hold. ;-)\n" ); + return EXIT_SUCCESS; + case LONGOPT_CREATE: + paramCreate = strdup( optarg ); + break; + case LONGOPT_REVISION: + paramRevision = atoi( optarg ); + break; + case LONGOPT_SIZE: + paramSize = strtoll( optarg, NULL, 10 ); + break; + case LONGOPT_ERRORMSG: + errorMsg = strdup( optarg ); + break; + } + opt = getopt_long( argc, argv, optString, longOpts, &longIndex ); + } + + // Load general config + + if ( _configDir == NULL ) _configDir = strdup( "/etc/dnbd3-server" ); + globals_loadConfig(); + if ( _basePath == NULL && errorMsg == NULL ) { + logadd( LOG_ERROR, "Aborting, set proper basePath in %s/%s", _configDir, CONFIG_FILENAME ); + exit( EXIT_FAILURE ); + } + + timing_setBase(); + timing_get( &startupTime ); + +#ifdef AFL_MODE + // ###### AFL + // + image_serverStartup(); + net_init(); + uplink_globalsInit(); + rpc_init(); + if ( !image_loadAll( NULL ) || _shutdown ) { + fprintf( stderr, "Error loading images\n" ); + exit( 3 ); + } + { + struct sockaddr_storage client; + memset( &client, 0, sizeof client ); + client.ss_family = AF_INET; + dnbd3_client_t *dnbd3_client = dnbd3_prepareClient( &client, 1 ); + if ( dnbd3_client == NULL ) { + fprintf( stderr, "New client failed\n" ); + exit( 1 ); + } +#ifdef __AFL_HAVE_MANUAL_CONTROL + __AFL_INIT(); +#endif + net_handleNewConnection( dnbd3_client ); + exit( 0 ); + } + // + // ###### AFL END +#endif + + + // One-shots first: + + if ( paramCreate != NULL ) { + return image_create( paramCreate, paramRevision, paramSize ) ? 0 : EXIT_FAILURE; + } + + // 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 ( errorMsg != NULL ) { + setupNetwork( bindAddress ); + logadd( LOG_INFO, "Running errormsg server" ); + while ( true ) { + const int fd = sock_accept( listeners, NULL, NULL ); + if ( fd >= 0 ) { + rpc_sendErrorMessage( fd, errorMsg ); + } else { + const int err = errno; + if ( err == EINTR || err == EAGAIN ) continue; + logadd( LOG_ERROR, "Client accept failure (err=%d)", err ); + usleep( 10000 ); // 10ms + } + } + exit( 0 ); + } + image_serverStartup(); + altservers_init(); + integrity_init(); + net_init(); + uplink_globalsInit(); + rpc_init(); + logadd( LOG_INFO, "DNBD3 server starting.... Machine type: " ENDIAN_MODE ); + + 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 = ; + sigaction( SIGTERM, &sa, NULL ); + sigaction( SIGINT, &sa, NULL ); + sigaction( SIGUSR1, &sa, NULL ); + sigaction( SIGHUP, &sa, NULL ); + sigaction( SIGUSR2, &sa, NULL ); + signal( SIGPIPE, SIG_IGN ); + + logadd( LOG_INFO, "Loading images...." ); + // Load all images in base path + if ( !image_loadAll( NULL ) || _shutdown ) { + if ( _shutdown ) { + logadd( LOG_ERROR, "Received shutdown request while loading images." ); + } else { + logadd( LOG_ERROR, "Could not load images." ); + } + free( bindAddress ); + dnbd3_cleanup(); + return _shutdown ? 0 : 1; + } + + // Give other threads some time to start up before accepting connections + usleep( 100000 ); + + // setup network + listeners = setupNetwork( bindAddress ); + + // Initialize thread pool + if ( !threadpool_init( 8 ) ) { + logadd( LOG_ERROR, "Could not init thread pool!\n" ); + exit( EXIT_FAILURE ); + } + + logadd( LOG_INFO, "Server is ready. (%s)", VERSION_STRING ); + + // +++++++++++++++++++++++++++++++++++++++++++++++++++ main loop + struct sockaddr_storage client; + socklen_t len; + int fd; + while ( !_shutdown ) { + // Handle signals + printSignal(); + if ( sigReload ) { + sigReload = false; + logadd( LOG_INFO, "SIGHUP received, re-scanning image directory" ); + threadpool_run( &server_asyncImageListLoad, NULL ); + } + if ( sigLogCycle ) { + sigLogCycle = false; + logadd( LOG_INFO, "SIGUSR2 received, reopening log file..." ); + if ( log_openLogFile( NULL ) ) + logadd( LOG_INFO, "Log file has been reopened." ); + else + logadd( LOG_WARNING, "Could not cycle log file." ); + } + // + len = sizeof(client); + fd = sock_accept( listeners, &client, &len ); + if ( fd < 0 ) { + const int err = errno; + if ( err == EINTR || err == EAGAIN ) continue; + logadd( LOG_ERROR, "Client accept failure (err=%d)", err ); + usleep( 10000 ); // 10ms + continue; + } + + dnbd3_client_t *dnbd3_client = dnbd3_prepareClient( &client, fd ); + if ( dnbd3_client == NULL ) { + close( fd ); + continue; + } + + if ( !threadpool_run( &net_handleNewConnection, (void *)dnbd3_client ) ) { + logadd( LOG_ERROR, "Could not start thread for new connection." ); + free( dnbd3_client ); + continue; + } + } + printSignal(); + free( bindAddress ); + dnbd3_cleanup(); + return 0; +} + +void printSignal() +{ + if ( lastSignal.si_signo != 0 ) { + logadd( LOG_INFO, "Signal %d (via %d) by pid %u, uid %u", + lastSignal.si_signo, lastSignal.si_code, + (unsigned int)lastSignal.si_pid, (unsigned int)lastSignal.si_uid ); + if ( lastSignal.si_pid != 0 ) { + char buffer[500], path[100]; + snprintf( path, sizeof(path), "/proc/%u/exe", (unsigned int)lastSignal.si_pid ); + ssize_t len = readlink( path, buffer, sizeof(buffer) ); + if ( len > 0 ) { + logadd( LOG_INFO, "%u is %.*s", (unsigned int)lastSignal.si_pid, (int)len, buffer ); + } + } + lastSignal.si_signo = 0; + } +} + +static poll_list_t* setupNetwork(char *bindAddress) +{ + listeners = sock_newPollList(); + if ( listeners == NULL ) { + logadd( LOG_ERROR, "Didnt get a poll list!" ); + exit( EXIT_FAILURE ); + } + if ( !sock_listen( listeners, bindAddress, (uint16_t)_listenPort ) ) { + logadd( LOG_ERROR, "Could not listen on any local interface." ); + exit( EXIT_FAILURE ); + } + return listeners; +} + +/** + * Initialize and partially populate the client struct - called when an incoming + * connection is accepted. As this might be an HTTP request we don't initialize the + * locks, that would happen later once we know. + */ +static dnbd3_client_t* dnbd3_prepareClient(struct sockaddr_storage *client, int fd) +{ + dnbd3_client_t *dnbd3_client = calloc( 1, sizeof(dnbd3_client_t) ); + if ( dnbd3_client == NULL ) { // This will never happen thanks to memory overcommit + logadd( LOG_ERROR, "Could not alloc dnbd3_client_t for new client." ); + return NULL; + } + + if ( client->ss_family == AF_INET ) { + struct sockaddr_in *v4 = (struct sockaddr_in *)client; + dnbd3_client->host.type = HOST_IP4; + memcpy( dnbd3_client->host.addr, &(v4->sin_addr), 4 ); + dnbd3_client->host.port = v4->sin_port; + } else if ( client->ss_family == AF_INET6 ) { + struct sockaddr_in6 *v6 = (struct sockaddr_in6 *)client; + dnbd3_client->host.type = HOST_IP6; + memcpy( dnbd3_client->host.addr, &(v6->sin6_addr), 16 ); + dnbd3_client->host.port = v6->sin6_port; + } else { + logadd( LOG_ERROR, "New client has unknown address family %d, disconnecting...", (int)client->ss_family ); + free( dnbd3_client ); + return NULL; + } + dnbd3_client->sock = fd; + return dnbd3_client; +} + +static void dnbd3_handleSignal(int signum) +{ + if ( _shutdown ) return; + if ( signum == SIGINT || signum == SIGTERM ) { + _shutdown = true; + } else if ( signum == SIGUSR1 || signum == SIGHUP ) { + sigReload = true; + } else if ( signum == SIGUSR2 ) { + sigLogCycle = true; + } +} + +static void dnbd3_handleSignal2(int signum, siginfo_t *info, void *data UNUSED) +{ + memcpy( &lastSignal, info, sizeof(siginfo_t) ); + dnbd3_handleSignal( signum ); +} + +uint32_t dnbd3_serverUptime() +{ + ticks now; + timing_get( &now ); + return timing_diff( &startupTime, &now ); +} + +static void* server_asyncImageListLoad(void *data UNUSED) +{ + setThreadName( "img-list-loader" ); + globals_loadConfig(); + image_loadAll( NULL ); + return NULL; +} + -- cgit v1.2.3-55-g7522