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