diff options
Diffstat (limited to 'src/client/client.c')
-rw-r--r-- | src/client/client.c | 284 |
1 files changed, 181 insertions, 103 deletions
diff --git a/src/client/client.c b/src/client/client.c index 37f0558..0cf222e 100644 --- a/src/client/client.c +++ b/src/client/client.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 @@ -18,9 +18,10 @@ * */ -#include "../clientconfig.h" -#include "../types.h" -#include "../version.h" +#include <dnbd3/config/client.h> +#include <dnbd3/types.h> +#include <dnbd3/version.h> +#include <dnbd3/build.h> #include <stdio.h> #include <stdlib.h> @@ -33,19 +34,19 @@ #include <arpa/inet.h> #include <string.h> #include <sys/stat.h> +#include <sys/socket.h> #include <sys/un.h> #include <errno.h> -#define SOCK_PATH "/var/run/dnbd3.socket" +#define SOCK_PATH "/run/dnbd3.socket" #define SOCK_BUFFER 1000 #define DEV_LEN 15 #define MAX_DEVS 50 - +#define TMP_STR_LEN 100 static int openDevices[MAX_DEVS]; -static const char *optString = "f:h:i:r:d:a:cs:HV?k"; +static const char *optString = "h:i:r:d:a:cs:SA:R:HV?k"; static const struct option longOpts[] = { - { "file", required_argument, NULL, 'f' }, { "host", required_argument, NULL, 'h' }, { "image", required_argument, NULL, 'i' }, { "rid", required_argument, NULL, 'r' }, @@ -53,8 +54,9 @@ static const struct option longOpts[] = { { "ahead", required_argument, NULL, 'a' }, { "close", no_argument, NULL, 'c' }, { "switch", required_argument, NULL, 's' }, - { "add", required_argument, NULL, 'adds' }, - { "remove", required_argument, NULL, 'rems' }, + { "sticky", no_argument, NULL, 'S' }, + { "add", required_argument, NULL, 'A' }, + { "remove", required_argument, NULL, 'R' }, { "help", no_argument, NULL, 'H' }, { "version", no_argument, NULL, 'V' }, { "daemon", no_argument, NULL, 'D' }, @@ -66,9 +68,9 @@ static const struct option longOpts[] = { static int dnbd3_ioctl(const char *dev, const int command, dnbd3_ioctl_t * const msg); static void dnbd3_client_daemon(); -static void dnbd3_daemon_action(int client, int argc, char **argv); +static void dnbd3_daemon_action(int client, int uid, int argc, char **argv); static int dnbd3_daemon_ioctl(int uid, char *device, int action, const char *actionName, char *host); -static char* dnbd3_daemon_open(int uid, char *host, char *image, int rid, int readAhead); +static char* dnbd3_daemon_open(int uid, char *host, char *image, int rid, int readAhead, const bool doLearnNewServers); static int dnbd3_daemon_send(int argc, char **argv); static void dnbd3_print_help(char *argv_0); static void dnbd3_print_version(); @@ -84,11 +86,11 @@ static char host_to_string(const dnbd3_host_t *host, char *target, size_t target if ( targetlen < 10 ) return false; if ( host->type == HOST_IP6 ) { *target++ = '['; - inet_ntop( AF_INET6, host->addr, target, targetlen - 10 ); + inet_ntop( AF_INET6, host->addr, target, (socklen_t)targetlen - 10 ); target += strlen( target ); *target++ = ']'; } else if ( host->type == HOST_IP4 ) { - inet_ntop( AF_INET, host->addr, target, targetlen - 8 ); + inet_ntop( AF_INET, host->addr, target, (socklen_t)targetlen - 8 ); target += strlen( target ); } else { snprintf( target, targetlen, "<?addrtype=%d>", (int)host->type ); @@ -135,8 +137,9 @@ static char parse_address(char *string, dnbd3_host_t *host) // Scan for port char *portpos = NULL, *ptr = string; while ( *ptr ) { - if ( *ptr == ':' ) - portpos = ptr; + if ( *ptr == ':' ) { + portpos = ptr; + } ++ptr; } if ( portpos == NULL ) return 0; // No port in string @@ -192,33 +195,77 @@ static int dnbd3_get_ip(char *hostname, dnbd3_host_t *host) return true; } +/* parses hosts from space separated cmdln string, resolves them and saves them into hosts */ +static int dnbd3_get_resolved_hosts(char *hosts_str, dnbd3_host_t *hosts, const size_t hosts_len) +{ + char *hosts_current_token = hosts_str; + char *hosts_last_host; + int hosts_index = 0; + char host_str[TMP_STR_LEN]; + size_t host_str_len = 0; + + do { + /* get next host from string */ + while ( *hosts_current_token == ' ' ) { + hosts_current_token++; + } + + /* buffer substring of host to get ip from it */ + hosts_last_host = strchr( hosts_current_token, ' ' ); + host_str_len = (hosts_last_host == NULL ? TMP_STR_LEN : (size_t)(hosts_last_host - hosts_current_token) + 1); + if ( host_str_len > TMP_STR_LEN ) { + host_str_len = TMP_STR_LEN; + } + + snprintf( host_str, host_str_len, "%s", hosts_current_token ); + + if ( !dnbd3_get_ip( host_str, &hosts[hosts_index] ) ) + return false; + + hosts_index++; + + /* continue processing of hosts */ + hosts_current_token = hosts_last_host + 1; + + } while ( hosts_last_host != NULL && hosts_index < hosts_len ); + + return hosts_index; +} + int main(int argc, char *argv[]) { char *dev = NULL; char host[50]; int action = -1; + bool learnNewServers = true; + int active_device_num = 0; - dnbd3_ioctl_t msg; - memset( &msg, 0, sizeof(dnbd3_ioctl_t) ); - msg.len = (uint16_t)sizeof(dnbd3_ioctl_t); + dnbd3_ioctl_t msg = { .len = (uint16_t)sizeof(msg) }; + msg.hosts_num = 0; msg.read_ahead_kb = DEFAULT_READ_AHEAD_KB; - msg.host.port = htons( PORT ); - msg.host.type = 0; msg.imgname = NULL; - msg.use_server_provided_alts = true; int opt = 0; int longIndex = 0; + // In case the client was invoked as a suid binary, change uid back to original user + // and warn the user as this was legacy mode + if ( geteuid() == 0 && getuid() != 0 ) { + fprintf( stderr, "Warning! %s is a setuid binary. This is deprecated and not needed anymore.\n", argv[0] ); + fprintf( stderr, "Switching back o user %d\n", (int)getuid() ); + setgid( getgid() ); + setuid( getuid() ); + } + opt = getopt_long( argc, argv, optString, longOpts, &longIndex ); while ( opt != -1 ) { switch ( opt ) { - case 'f': - break; case 'h': - if ( !dnbd3_get_ip( optarg, &msg.host ) ) exit( EXIT_FAILURE ); + msg.hosts_num = (uint8_t)dnbd3_get_resolved_hosts( optarg, msg.hosts, MAX_HOSTS_PER_IOCTL ); + if ( !msg.hosts_num ) + exit( EXIT_FAILURE ); break; case 'i': action = IOCTL_OPEN; @@ -238,25 +285,34 @@ int main(int argc, char *argv[]) action = IOCTL_CLOSE; break; case 's': - dnbd3_get_ip( optarg, &msg.host ); + dnbd3_get_ip( optarg, &msg.hosts[0] ); + msg.hosts_num = 1; action = IOCTL_SWITCH; break; - case 'adds': - dnbd3_get_ip( optarg, &msg.host ); + case 'S': + learnNewServers = false; + break; + case 'A': + dnbd3_get_ip( optarg, &msg.hosts[0] ); + msg.hosts_num = 1; action = IOCTL_ADD_SRV; break; - case 'rems': - dnbd3_get_ip( optarg, &msg.host ); + case 'R': + dnbd3_get_ip( optarg, &msg.hosts[0] ); + msg.hosts_num = 1; action = IOCTL_REM_SRV; break; case 'H': dnbd3_print_help( argv[0] ); + exit( EXIT_SUCCESS ); break; case 'V': - dnbd3_print_version(); + dnbd3_print_version( argv[0] ); + exit( EXIT_SUCCESS ); break; case '?': dnbd3_print_help( argv[0] ); + exit( EXIT_SUCCESS ); break; case 'D': dnbd3_client_daemon(); @@ -265,6 +321,14 @@ int main(int argc, char *argv[]) opt = getopt_long( argc, argv, optString, longOpts, &longIndex ); } + /* abort if sticky mode is set and image will not be opened */ + if ( !learnNewServers && action != IOCTL_OPEN ) { + printf( "ERROR: sticky mode can only be set if image will be opened.\n" ); + exit( EXIT_FAILURE ); + } + + msg.use_server_provided_alts = learnNewServers; + // See if socket exists, if so, try to send to daemon struct stat st; if ( stat( SOCK_PATH, &st ) == 0 ) { @@ -275,39 +339,37 @@ int main(int argc, char *argv[]) // Direct requests - // In case the client was invoked as a suid binary, change uid back to original user - // when being used for direct ioctl, so that the device's permissions are taken into account - if ( geteuid() == 0 ) { - setgid( getgid() ); - setuid( getuid() ); - } - - host_to_string( &msg.host, host, 50 ); - // close device - if ( action == IOCTL_CLOSE && msg.host.type == 0 && dev && (msg.imgname == NULL )) { + if ( action == IOCTL_CLOSE && msg.hosts_num == 0 && dev && (msg.imgname == NULL )) { printf( "INFO: Closing device %s\n", dev ); - if ( dnbd3_ioctl( dev, IOCTL_CLOSE, &msg ) ) exit( EXIT_SUCCESS ); + if ( dnbd3_ioctl( dev, IOCTL_CLOSE, &msg ) == 0 ) exit( EXIT_SUCCESS ); printf( "Couldn't close device.\n" ); exit( EXIT_FAILURE ); } // switch host - if ( (action == IOCTL_SWITCH || action == IOCTL_ADD_SRV || action == IOCTL_REM_SRV) && msg.host.type != 0 && dev && (msg.imgname == NULL )) { + if ( (action == IOCTL_SWITCH || action == IOCTL_ADD_SRV || action == IOCTL_REM_SRV) && msg.hosts_num == 1 && dev && (msg.imgname == NULL )) { + host_to_string( &msg.hosts[0], host, 50 ); if ( action == IOCTL_SWITCH ) printf( "INFO: Switching device %s to %s\n", dev, host ); if ( action == IOCTL_ADD_SRV ) printf( "INFO: %s: adding %s\n", dev, host ); if ( action == IOCTL_REM_SRV ) printf( "INFO: %s: removing %s\n", dev, host ); - if ( dnbd3_ioctl( dev, action, &msg ) ) exit( EXIT_SUCCESS ); + if ( dnbd3_ioctl( dev, action, &msg ) == 0 ) exit( EXIT_SUCCESS ); printf( "Failed! Maybe the device is not connected?\n" ); exit( EXIT_FAILURE ); } // connect - if ( action == IOCTL_OPEN && msg.host.type != 0 && dev && (msg.imgname != NULL )) { - printf( "INFO: Connecting device %s to %s for image %s\n", dev, host, msg.imgname ); - if ( dnbd3_ioctl( dev, IOCTL_OPEN, &msg ) ) exit( EXIT_SUCCESS ); - printf( "ERROR: connecting device failed. Maybe it's already connected?\n" ); - exit( EXIT_FAILURE ); + if ( action == IOCTL_OPEN && msg.hosts_num > 0 && dev && (msg.imgname != NULL )) { + printf( "INFO: Connecting device %s for image %s\n", dev, msg.imgname ); + active_device_num = dnbd3_ioctl( dev, IOCTL_OPEN, &msg ); + if ( active_device_num >= 0 ) { + host_to_string( &msg.hosts[active_device_num], host, 50 ); + printf( "INFO: Device %s for image %s is connected to server %s\n", dev, msg.imgname, host); + exit( EXIT_SUCCESS ); + } else { + printf( "ERROR: connecting device failed. Maybe it's already connected?\n" ); + exit( EXIT_FAILURE ); + } } dnbd3_print_help( argv[0] ); @@ -317,17 +379,19 @@ int main(int argc, char *argv[]) static int dnbd3_ioctl(const char *dev, const int command, dnbd3_ioctl_t * const msg) { const int fd = open( dev, O_WRONLY ); - if ( fd < 0 ) { - printf( "open() for %s failed.\n", dev ); - return false; + if ( fd == -1 ) { + perror( "open() failed" ); + return -ENODEV; + } + if ( msg != NULL && msg->imgname != NULL ) { + msg->imgnamelen = (uint16_t)strlen( msg->imgname ); } - if ( msg != NULL && msg->imgname != NULL ) msg->imgnamelen = (uint16_t)strlen( msg->imgname ); const int ret = ioctl( fd, command, msg ); if ( ret < 0 ) { - printf( "ioctl() failed.\n" ); + perror( "ioctl() failed" ); } close( fd ); - return ret >= 0; + return ret; } static void dnbd3_client_daemon() @@ -338,11 +402,8 @@ static void dnbd3_client_daemon() struct timeval tv; int done, ret, len; socklen_t socklen; - - if ( geteuid() != 0 ) { - printf( "Only root can run the dnbd3-client in daemon mode!\n" ); - exit( 1 ); - } + struct ucred ucred; + int fdTest; if ( (listener = socket( AF_UNIX, SOCK_STREAM, 0 )) == -1 ) { perror( "socket" ); @@ -356,12 +417,21 @@ static void dnbd3_client_daemon() perror( "bind" ); exit( 1 ); } - chmod( addrLocal.sun_path, 0600 ); + fchmod( listener, 0666 ); + chmod( SOCK_PATH, 0666 ); if ( listen( listener, 5 ) == -1 ) { perror( "listen" ); + unlink( addrLocal.sun_path ); exit( 1 ); } + fdTest = open( "/dev/dnbd0", O_RDWR ); + if ( fdTest == -1 ) { + perror( "Opening /dev/dnbd0 failed. Daemon will probably not work" ); + } else { + close( fdTest ); + } + memset( openDevices, -1, sizeof(openDevices) ); for (;;) { @@ -372,6 +442,14 @@ static void dnbd3_client_daemon() continue; } + socklen = sizeof(ucred); + if ( getsockopt( client, SOL_SOCKET, SO_PEERCRED, &ucred, &socklen ) == -1 ) { + perror( "Could not get credentials of connection" ); + close( client ); + continue; + } + printf("Call from user %d\n", (int)ucred.uid ); + tv.tv_sec = 1; tv.tv_usec = 0; setsockopt( client, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv) ); @@ -398,27 +476,28 @@ static void dnbd3_client_daemon() } if ( pos >= end ) break; argv[argc++] = pos; - printf("Arg %d: '%s'\n", argc, pos); + //printf("Arg %d: '%s'\n", argc, pos); while ( *pos != '\0' ) { // This will always be in bounds because of -4 above if ( ++pos >= end ) break; } } - dnbd3_daemon_action( client, argc, argv ); + dnbd3_daemon_action( client, (int)ucred.uid, argc, argv ); } close( client ); } } -static void dnbd3_daemon_action(int client, int argc, char **argv) +static void dnbd3_daemon_action(int client, int uid, int argc, char **argv) { int opt = 0; int longIndex = 0; char *host = NULL, *image = NULL, *device = NULL; - int rid = 0, uid = 0, killMe = false, ahead = 512; + int rid = 0, killMe = false, ahead = 512; int len; int action = -1; const char *actionName = NULL; + bool learnNewServers = true; optind = 1; opt = getopt_long( argc, argv, optString, longOpts, &longIndex ); @@ -439,18 +518,18 @@ static void dnbd3_daemon_action(int client, int argc, char **argv) case 'r': rid = atoi( optarg ); break; - case 'U': - uid = atoi( optarg ); - break; case 'c': action = IOCTL_CLOSE; actionName = "Close"; break; - case 'adds': + case 'S': + learnNewServers = false; + break; + case 'A': action = IOCTL_ADD_SRV; actionName = "Add Server"; break; - case 'rems': + case 'R': action = IOCTL_REM_SRV; actionName = "Remove Server"; break; @@ -465,14 +544,14 @@ static void dnbd3_daemon_action(int client, int argc, char **argv) } if ( killMe ) { - if ( uid != 0 ) { + if ( uid != geteuid() ) { printf( "Ignoring kill request by user %d\n", uid ); close( client ); return; } printf( "Received kill request; exiting.\n" ); - close( client ); unlink( SOCK_PATH ); + close( client ); exit( 0 ); } @@ -486,7 +565,7 @@ static void dnbd3_daemon_action(int client, int argc, char **argv) return; } if ( action == IOCTL_OPEN && host != NULL && image != NULL && rid >= 0 ) { - device = dnbd3_daemon_open( uid, host, image, rid, ahead ); + device = dnbd3_daemon_open( uid, host, image, rid, ahead, learnNewServers); if ( device != NULL ) { len = strlen( device ); send( client, &len, sizeof(len), 0 ); @@ -509,11 +588,9 @@ static int dnbd3_daemon_ioctl(int uid, char *device, int action, const char *act } else { index = atoi( device ); } - dnbd3_ioctl_t msg; - memset( &msg, 0, sizeof(msg) ); - msg.len = (uint16_t)sizeof(msg); + dnbd3_ioctl_t msg = { .len = (uint16_t)sizeof(msg) }; if ( host != NULL ) { - dnbd3_get_ip( host, &msg.host ); + dnbd3_get_ip( host, &msg.hosts[0] ); } if ( index < 0 || index >= MAX_DEVS ) { printf( "%s request with invalid device id %d\n", actionName, index ); @@ -528,7 +605,7 @@ static int dnbd3_daemon_ioctl(int uid, char *device, int action, const char *act printf( "%s: User %d cannot access %s owned by %d\n", actionName, uid, dev, openDevices[index] ); return false; } - if ( dnbd3_ioctl( dev, action, &msg ) ) { + if ( dnbd3_ioctl( dev, action, &msg ) == 0 ) { printf( "%s request for device %s of user %d successful\n", actionName, dev, uid ); openDevices[index] = -1; return true; @@ -537,23 +614,26 @@ static int dnbd3_daemon_ioctl(int uid, char *device, int action, const char *act return false; } -static char* dnbd3_daemon_open(int uid, char *host, char *image, int rid, int readAhead) +static char* dnbd3_daemon_open(int uid, char *host, char *image, int rid, int readAhead, const bool doLearnNewServers) { int i, sameUser = 0; struct stat st; static char dev[DEV_LEN]; printf( "Opening a device for %s on %s\n", image, host ); // Check number of open devices - for (i = 0; i < MAX_DEVS; ++i) { - if ( openDevices[i] == uid ) sameUser++; - } - if ( sameUser > 1 ) { - printf( "Ignoring request by %d as there are already %d open devices for that user.\n", uid, sameUser ); - return NULL ; + if ( uid != 0 ) { + for ( i = 0; i < MAX_DEVS; ++i ) { + if ( openDevices[i] == uid ) sameUser++; + } + if ( sameUser > 1 ) { + printf( "Ignoring request by %d as there are already %d open devices for that user.\n", uid, sameUser ); + return NULL; + } } // Find free device - for (i = 0; i < MAX_DEVS; ++i) { - if ( openDevices[i] != -1 ) continue; + for ( i = 0; i < MAX_DEVS; ++i ) { + if ( openDevices[i] != -1 ) + continue; snprintf( dev, DEV_LEN, "/dev/dnbd%d", i ); if ( stat( dev, &st ) == -1 ) { break; @@ -561,16 +641,16 @@ static char* dnbd3_daemon_open(int uid, char *host, char *image, int rid, int re // Open dnbd3_ioctl_t msg; msg.len = (uint16_t)sizeof(msg); - if ( !dnbd3_get_ip( host, &msg.host ) ) { + if ( !dnbd3_get_ip( host, &msg.hosts[0] ) ) { printf( "Cannot parse host address %s\n", host ); return NULL ; } msg.imgname = image; msg.imgnamelen = strlen( image ); msg.rid = rid; - msg.use_server_provided_alts = true; + msg.use_server_provided_alts = doLearnNewServers; msg.read_ahead_kb = readAhead; - if ( dnbd3_ioctl( dev, IOCTL_OPEN, &msg ) ) { + if ( dnbd3_ioctl( dev, IOCTL_OPEN, &msg ) >= 0 ) { openDevices[i] = uid; printf( "Device %s now occupied by %d\n", dev, uid ); return dev; @@ -584,7 +664,6 @@ static char* dnbd3_daemon_open(int uid, char *host, char *image, int rid, int re static int dnbd3_daemon_send(int argc, char **argv) { - const int uid = getuid(); int s, i, len; struct sockaddr_un remote; char buffer[SOCK_BUFFER]; @@ -604,7 +683,6 @@ static int dnbd3_daemon_send(int argc, char **argv) // (Re)build argument string into a single one, arguments separated by null chars char *pos = buffer; char *end = buffer + SOCK_BUFFER; - pos += snprintf( pos, end - pos, "--user%c%d", (int)'\0', uid ) + 1; for (i = 1; i < argc && pos < end; ++i) { pos += snprintf( pos, end - pos, "%s", argv[i] ) + 1; } @@ -643,28 +721,28 @@ static int dnbd3_daemon_send(int argc, char **argv) static void dnbd3_print_help(char *argv_0) { - printf( "Version: %s\n\n", VERSION_STRING ); - printf( "\nUsage: %s\n" - "\t-h <host> -i <image name> [-r <rid>] -d <device> [-a <KB>] || -c -d <device>\n\n", argv_0 ); - printf( "Start the DNBD3 client.\n" ); - //printf("-f or --file \t\t Configuration file (default /etc/dnbd3-client.conf)\n"); - printf( "-h or --host \t\t Host running dnbd3-server.\n" ); + printf( "Usage: %s\n", argv_0 ); + printf( " -h <host> -i <image name> [-r <rid>] -d <device> [-a <KB>] || -c -d <device>\n\n" ); + printf( "Start the DNBD3 client.\n\n" ); + printf( "-h or --host \t\t List of space separated hosts to use.\n" ); printf( "-i or --image \t\t Image name of exported image.\n" ); printf( "-r or --rid \t\t Release-ID of exported image (default 0, latest).\n" ); printf( "-d or --device \t\t DNBD3 device name.\n" ); printf( "-a or --ahead \t\t Read ahead in KByte (default %i).\n", DEFAULT_READ_AHEAD_KB ); printf( "-c or --close \t\t Disconnect and close device.\n" ); printf( "-s or --switch \t\t Switch dnbd3-server on device (DEBUG).\n" ); + printf( "-S or --sticky \t\t Use only servers from command line (no learning from servers)\n" ); + printf( "-A or --add \t\t Add given dnbd3-server on device.\n"); + printf( "-R or --remove \t\t Remove given dnbd3-server on device.\n"); printf( "-H or --help \t\t Show this help text and quit.\n" ); printf( "-V or --version \t Show version and quit.\n\n" ); - printf( "\t--daemon \t Run as helper daemon\n" ); - printf( "\t--kill \t Kill running helper daemon\n" ); + printf( " --daemon \t\t Run as helper daemon\n" ); + printf( " --kill \t\t Kill running helper daemon\n\n" ); printf( "The helper daemon makes it possible for normal users to connect dnbd3 devices.\n" ); - printf( "The client binary needs to be a setuid program for this to work!\n\n" ); } void dnbd3_print_version() { - printf( "Version: %s\n", VERSION_STRING ); - exit( EXIT_SUCCESS ); + printf( "dnbd3-client version: %s\n", DNBD3_VERSION_LONG ); + printf( "Built: %s\n", DNBD3_BUILD_DATE ); } |