[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

[Nbd] ndb-client 2.7.3 patch



I've partially re-written the nbd-client from nbd-2.7.3 (sorry it's not against the 2.8.x or CVS, I needed this working quickly) to be more of a daemon and not exit if the connection goes away.

I use this for my software RAID of network block devices so I want it to reconnect once the host becomes available again without intervention.

Feel free to license it however you wish.

Most changes only affect nbd-client.c, but I did update cliserv.h to include "err_noexit()" that behaves the same as err() but does not call exit().
diff -uNr nbd-2.7.3-orig/cliserv.h nbd-2.7.3-rsk/cliserv.h
--- nbd-2.7.3-orig/cliserv.h	2004-05-16 15:21:35.000000000 -0500
+++ nbd-2.7.3-rsk/cliserv.h	2005-04-28 14:29:19.000000000 -0500
@@ -74,7 +74,7 @@
 #endif
 }
 
-void err(const char *s)
+void err_noexit(const char *s)
 {
 	const int maxlen = 150;
 	char s1[maxlen], *s2;
@@ -101,6 +101,10 @@
 #else
 	fprintf(stderr, "Error: %s\n", s1);
 #endif
+}
+
+void err(const char *s) {
+	err_noexit(s);
 	exit(1);
 }
 
diff -uNr nbd-2.7.3-orig/nbd-client.c nbd-2.7.3-rsk/nbd-client.c
--- nbd-2.7.3-orig/nbd-client.c	2004-05-23 04:55:43.000000000 -0500
+++ nbd-2.7.3-rsk/nbd-client.c	2005-04-28 14:33:53.000000000 -0500
@@ -1,18 +1,3 @@
-/*
- * Open connection for network block device
- *
- * Copyright 1997,1998 Pavel Machek, distribute under GPL
- *  <pavel@...27...>
- *
- * Version 1.0 - 64bit issues should be fixed, now
- * Version 1.1 - added bs (blocksize) option (Alexey Guzeev, aga@...52...)
- * Version 1.2 - I added new option '-d' to send the disconnect request
- * Version 2.0 - Version synchronised with server
- * Version 2.1 - Check for disconnection before INIT_PASSWD is received
- * 	to make errormsg a bit more helpful in case the server can't
- * 	open the exported file.
- */
-
 #include "config.h"
 #include "lfs.h"
 
@@ -20,205 +5,365 @@
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <sys/types.h>
+#include <stdint.h>
+#include <inttypes.h>
 #include <unistd.h>
 #include <netinet/tcp.h>
-#include <netinet/in.h>		/* sockaddr_in, htons, in_addr */
-#include <netdb.h>		/* hostent, gethostby*, getservby* */
+#include <netinet/in.h>         /* sockaddr_in, htons, in_addr */
+#include <netdb.h>              /* hostent, gethostby*, getservby* */
 #include <stdio.h>
 #include <fcntl.h>
 #include <syslog.h>
 #include <stdlib.h>
-
-#ifndef __GNUC__
-#error I need GCC to work
-#endif
-
 #include <linux/ioctl.h>
 #define MY_NAME "nbd_client"
 #include "cliserv.h"
 
-int opennet(char *name, int port)
-{
+void print_help(void) {
+	fprintf(stderr, "nbd-client version %s\n", PACKAGE_VERSION);
+	fprintf(stderr, "Usage:  nbd-client <host> <port> <nbd_device>\n");
+	fprintf(stderr, "        nbd-client -d <nbd_device>\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "  host        Host to connect to.\n");
+	fprintf(stderr, "  port        Port to connect to nbd server on.\n");
+	fprintf(stderr, "  nbd_device  Device to connect NBD server specified to.\n");
+	return;
+}
+
+/*
+ * SYNOPSIS:
+ *   void daeomize(void);
+ *
+ * NOTES:
+ *   This function accomplishes everything needed to become a daemon.
+ *   Including closing standard in/out/err and forking.
+ *   It returns nothing, on failure the program must abort.
+ *
+ */
+void daemonize(void) {
+	pid_t pid;
+
+	chdir("/");
+
+	setsid();
+
+	pid = fork();
+
+	if (pid > 0) {
+		exit(EXIT_SUCCESS);
+	}
+	if (pid < 0) {
+		err_noexit("fork() failed.");
+		exit(EXIT_FAILURE);
+	}
+
+	return;
+}
+
+int opennet(const char *name, int port) {
 	int sock;
 	struct sockaddr_in xaddrin;
 	int xaddrinlen = sizeof(xaddrin);
-	struct hostent *hostn;
+	struct hostent *hostn = NULL;
 
 	hostn = gethostbyname(name);
-	if (!hostn)
-		err("Gethostname failed: %h\n");
+	if (!hostn) {
+		err_noexit("gethostname() failed: %h\n");
+		return(EXIT_FAILURE);
+	}
 
-	if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
-		err("Socket failed: %m");
+	if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
+		err_noexit("socket() failed: %m");
+		return(EXIT_FAILURE);
+	}
 
 	xaddrin.sin_family = AF_INET;
 	xaddrin.sin_port = htons(port);
 	xaddrin.sin_addr.s_addr = *((int *) hostn->h_addr);
-	if ((connect(sock, (struct sockaddr *) &xaddrin, xaddrinlen) < 0))
-		err("Connect: %m");
+	if ((connect(sock, (struct sockaddr *) &xaddrin, xaddrinlen) < 0)) {
+		err_noexit("Connect: %m");
+	}
 
 	setmysockopt(sock);
-	return sock;
+
+	return(sock);
 }
 
-int main(int argc, char *argv[])
-{
-	int port, sock, nbd, one = 1;
-	u64 magic, size64;
-	unsigned long size;
-	char buf[256] = "\0\0\0\0\0\0\0\0\0";
-	int blocksize=1024;
-	char *hostname;
-	int swap=0;
+int nbd_disable(const char *nbddev) {
+	int nbd_fd;
+	int retval = EXIT_SUCCESS;
+
+	nbd_fd = open(nbddev, O_RDWR);
+	if (nbd_fd < 0) {
+		fprintf(stderr, "Can not open NBD: %s\n", strerror(errno));
+		return(EXIT_FAILURE);
+	}
 
-	logging();
+	printf("Disconnecting: ");
+	if (ioctl(nbd_fd, NBD_CLEAR_QUE) < 0) {
+		printf("queue [FAILED], ");
+		retval = EXIT_FAILURE;
+	} else {
+		printf("queue, ");
+	}
 
-	if (argc < 3) {
-	errmsg:
-		fprintf(stderr, "nbd-client version %s\n", PACKAGE_VERSION);
-		fprintf(stderr, "Usage: nbd-client [bs=blocksize] host port nbd_device [-swap]\n");
-		fprintf(stderr, "Or   : nbd-client -d nbd_device\n");
-		fprintf(stderr, "Default value for blocksize is 1024 (recommended for ethernet)\n");
-		fprintf(stderr, "Allowed values for blocksize are 512,1024,2048,4096\n"); /* will be checked in kernel :) */
-		fprintf(stderr, "Note, that kernel 2.4.2 and older ones do not work correctly with\n");
-		fprintf(stderr, "blocksizes other than 1024 without patches\n");
-		return 1;
-	}
-
-	++argv; --argc; /* skip programname */
-	
-	if (strcmp(argv[0], "-d")==0) {
-		nbd = open(argv[1], O_RDWR);
-		if (nbd < 0)
-			err("Can not open NBD: %m");
-		printf("Disconnecting: que, ");
-		if (ioctl(nbd, NBD_CLEAR_QUE)< 0)
-			err("Ioctl failed: %m\n");
-		printf("disconnect, ");
 #ifdef NBD_DISCONNECT
-		if (ioctl(nbd, NBD_DISCONNECT)<0)
-			err("Ioctl failed: %m\n");
-		printf("sock, ");
-#else
-		fprintf(stderr, "Can't disconnect: I was not compiled with disconnect support!\n" );
-		exit(1);
+	if (ioctl(nbd_fd, NBD_DISCONNECT) < 0) {
+		printf("disconnect [FAILED], ");
+		retval = EXIT_FAILURE;
+	} else {
+		printf("disconnect, ");
+	}
 #endif
-		if (ioctl(nbd, NBD_CLEAR_SOCK)<0)
-			err("Ioctl failed: %m\n");
-		printf("done\n");
-		return 0;
-	}
-	
-	if (strncmp(argv[0], "bs=", 3)==0) {
-		blocksize=atoi(argv[0]+3);
-		++argv; --argc; /* skip blocksize */
-	}
-	
-	if (argc==0) goto errmsg;
-	hostname=argv[0];
-	++argv; --argc; /* skip hostname */
-	
-	if (argc==0) goto errmsg;
-	port = atoi(argv[0]);
-	++argv; --argc; /* skip port */
-
-	if (argc==0) goto errmsg;
-	sock = opennet(hostname, port);
-	nbd = open(argv[0], O_RDWR);
-	if (nbd < 0)
-	  err("Can not open NBD: %m");
-	++argv; --argc; /* skip device */
-
-	if (argc>1) goto errmsg;
-	if (argc!=0) swap=1;
-	argv=NULL; argc=0; /* don't use it later suddenly */
-
-	printf("Negotiation: ");
-	if (read(sock, buf, 8) < 0)
-		err("Failed/1: %m");
-	if (strlen(buf)==0)
-		err("Server closed connection");
-	if (strcmp(buf, INIT_PASSWD))
-		err("INIT_PASSWD bad");
-	printf(".");
-	if (read(sock, &magic, sizeof(magic)) < 0)
-		err("Failed/2: %m");
+
+	if (ioctl(nbd_fd, NBD_CLEAR_SOCK) < 0) {
+		printf("clear [FAILED], ");
+		retval = EXIT_FAILURE;
+	} else {
+		printf("clear, ");
+	}
+
+	printf("done.\n");
+
+	close(nbd_fd);
+
+	return(retval);
+}
+
+int nbd_enable(const char *host, int port, const char *nbddev, unsigned long blocksize) {
+	uint64_t magic, size64;
+	unsigned long size;
+	ssize_t read_ret;
+	char buf[256] = {0};
+	int nbd_fd, sock_fd;
+	int ioctl_ret;
+
+	nbd_fd = open(nbddev, O_RDWR);
+	if (nbd_fd < 0) {
+		err_noexit("Could not open NBD: %m");
+		return(EXIT_FAILURE);
+	}
+
+	sock_fd = opennet(host, port);
+	if (sock_fd < 0) {
+		/* opennet() returns its own error messages. */
+		close(nbd_fd);
+		return(EXIT_FAILURE);
+	}
+
+	read_ret = read(sock_fd, buf, 8);
+	if (read_ret != 8) {
+		close(sock_fd);
+		close(nbd_fd);
+		if (read_ret < 0) {
+			err_noexit("Error reading initial password, aborting: %m");
+		} else {
+			err_noexit("Error reading initial password, aborting.");
+		}
+		return(EXIT_FAILURE);
+	}
+
+	if (strcmp(buf, INIT_PASSWD) != 0) {
+		close(sock_fd);
+		close(nbd_fd);
+		err_noexit("Bad initial password, aborting.");
+		return(EXIT_FAILURE);
+	}
+
+	read_ret = read(sock_fd, &magic, sizeof(magic));
+	if (read_ret != sizeof(magic)) {
+		close(sock_fd);
+		close(nbd_fd);
+		if (read_ret < 0) {
+			err_noexit("Error reading magic, aborting: %m");
+		} else {
+			err_noexit("Error reading magic, aborting.");
+		}
+		return(EXIT_FAILURE);
+	}
+
 	magic = ntohll(magic);
-	if (magic != cliserv_magic)
-		err("Not enough cliserv_magic");
-	printf(".");
 
-	if (read(sock, &size64, sizeof(size64)) < 0)
-		err("Failed/3: %m\n");
+	if (magic != cliserv_magic) {
+		close(sock_fd);
+		close(nbd_fd);
+		err_noexit("Bad magic, aborting.");
+		return(EXIT_FAILURE);
+	}
+
+	read_ret = read(sock_fd, &size64, sizeof(size64));
+	if (read_ret != sizeof(size64)) {
+		close(sock_fd);
+		close(nbd_fd);
+		if (read_ret < 0) {
+			err_noexit("Invalid size, aborting: %m");
+		} else {
+			err_noexit("Invalid size, aborting.");
+		}
+		return(EXIT_FAILURE);
+	}
+
 	size64 = ntohll(size64);
 
 #ifdef NBD_SET_SIZE_BLOCKS
 	if ((size64>>10) > (~0UL >> 1)) {
-		printf("size = %luMB", (unsigned long)(size64>>20));
-		err("Exported device is too big for me. Get 64-bit machine :-(\n");
-	} else
-		printf("size = %luKB", (unsigned long)(size64>>10));
+//		printf("size = %luMB\n", (unsigned long)(size64>>20));
+
+		close(sock_fd);
+		close(nbd_fd);
+
+		err_noexit("Exported device is too big for me. Get 64-bit machine :-(");
+
+		return(EXIT_FAILURE);
+	} else {
+//		printf("size = %luKB\n", (unsigned long)(size64>>10));
+	}
 #else
 	if (size64 > (~0UL >> 1)) {
-		printf("size = %luKB", (unsigned long)(size64>>10));
-		err("Exported device is too big. Get 64-bit machine or newer kernel :-(\n");
-	} else
-		printf("size = %lu", (unsigned long)(size64));
+//		printf("size = %luKB\n", (unsigned long)(size64>>10));
+
+		close(sock_fd);
+		close(nbd_fd);
+
+		err_noexit("Exported device is too big. Get 64-bit machine or newer kernel :-(");
+
+		return(EXIT_FAILURE);
+	} else {
+//		printf("size = %lu\n", (unsigned long)(size64));
+	}
 #endif
 
-	if (read(sock, &buf, 128) < 0)
-		err("Failed/4: %m\n");
-	printf("\n");
+	read_ret = read(sock_fd, buf, 128);
+	if (read_ret != 128) {
+		close(sock_fd);
+		close(nbd_fd);
+		if (read_ret < 0) {
+			err_noexit("Error reading data, aborting: %m");
+		} else {
+			err_noexit("Error reading data, aborting.");
+		}
+		return(EXIT_FAILURE);
+	}
 
 #ifdef NBD_SET_SIZE_BLOCKS
-	if (size64/blocksize > (~0UL >> 1))
-		err("Device too large.\n");
-	else {
-		int er;
-		if (ioctl(nbd, NBD_SET_BLKSIZE, (unsigned long)blocksize) < 0)
-			err("Ioctl/1.1a failed: %m\n");
-		size = (unsigned long)(size64/blocksize);
-		if ((er = ioctl(nbd, NBD_SET_SIZE_BLOCKS, size)) < 0)
-			err("Ioctl/1.1b failed: %m\n");
-fprintf(stderr, "bs=%d, sz=%lu\n", blocksize, size);
+	if ((size64 / blocksize) > (~0UL >> 1)) {
+		close(sock_fd);
+		close(nbd_fd);
+
+		err_noexit("Device too large.\n");
+
+		return(EXIT_FAILURE);
+	} else {
+		if (ioctl(nbd_fd, NBD_SET_BLKSIZE, blocksize) < 0) {
+			close(sock_fd);
+			close(nbd_fd);
+
+			err_noexit("Ioctl/1.1a failed: %m");
+
+			return(EXIT_FAILURE);
+		}
+		size = size64 / blocksize;
+
+		ioctl_ret = ioctl(nbd_fd, NBD_SET_SIZE_BLOCKS, size);
+		if (ioctl_ret < 0) {
+			close(sock_fd);
+			close(nbd_fd);
+
+			err_noexit("Ioctl/1.1b failed: %m");
+
+			return(EXIT_FAILURE);
+		}
+//		printf("bs=%lu, sz=%lu\n", blocksize, size);
 	}
 #else
 	if (size64 > (~0UL >> 1)) {
-		err("Device too large.\n");
+		close(sock_fd);
+		close(nbd_fd);
+
+		err_noexit("Device too large.\n");
+
+		return(EXIT_FAILURE);
 	} else {
-		size = (unsigned long)size64;
-		if (ioctl(nbd, NBD_SET_SIZE, size) < 0)
-			err("Ioctl/1 failed: %m\n");
+		size = size64;
+
+		if (ioctl(nbd_fd, NBD_SET_SIZE, size) < 0) {
+			close(sock_fd);
+			close(nbd_fd);
+
+			err_noexit("Ioctl/1 failed: %m\n");
+
+			return(EXIT_FAILURE);
+		}
 	}
 #endif
 
-	ioctl(nbd, NBD_CLEAR_SOCK);
-	if (ioctl(nbd, NBD_SET_SOCK, sock) < 0)
-		err("Ioctl/2 failed: %m\n");
-
-#ifndef SO_SWAPPING
-	if (swap)
-		err("You have to compile me on machine with swapping patch enabled in order to use it later.");
-#else
-	if (swap)
-		if (setsockopt(sock, SOL_SOCKET, SO_SWAPPING, &one, sizeof(int)) < 0)
-			err("Could not enable swapping: %m");
+	ioctl_ret = ioctl(nbd_fd, NBD_CLEAR_SOCK);
+	if (ioctl_ret < 0) {
+		close(sock_fd);
+		close(nbd_fd);
+
+		err_noexit("ioctl(nbd_fd, NBD_CLEAR_SOCK) failed, aborting: %m");
+
+		return(EXIT_FAILURE);
+	}
+
+	ioctl_ret = ioctl(nbd_fd, NBD_SET_SOCK, sock_fd);
+	if (ioctl_ret < 0) {
+		close(sock_fd);
+		close(nbd_fd);
+
+		err_noexit("ioctl(nbd_fd, NBD_SET_SOCK, sock_fd) failed, aborting: %m");
+
+		return(EXIT_FAILURE);
+	}
+
+	/* This ioctl() call only returns when the connection is terminated by the other end closing the socket. */
+	ioctl_ret = ioctl(nbd_fd, NBD_DO_IT);
+	if (ioctl_ret < 0) {
+		err_noexit("Connection terminated: %m");
+	} else {
+		err_noexit("Connection terminated.");
+	}
+
+	ioctl(nbd_fd, NBD_CLEAR_QUE);
+#ifdef NBD_DISCONNECT
+	ioctl(nbd_fd, NBD_DISCONNECT);
 #endif
-	
-	/* Go daemon */
-	
-	chdir("/");
-	if (fork())
-		exit(0);
-	
-	if (ioctl(nbd, NBD_DO_IT) < 0)
-		fprintf(stderr, "Kernel call returned: %m");
-	else
-		fprintf(stderr, "Kernel call returned.");
-	printf("Closing: que, ");
-	ioctl(nbd, NBD_CLEAR_QUE);
-	printf("sock, ");
-	ioctl(nbd, NBD_CLEAR_SOCK);
-	printf("done\n");
-	return 0;
+	ioctl(nbd_fd, NBD_CLEAR_SOCK);
+
+	close(sock_fd);
+	close(nbd_fd);
+
+	return(EXIT_FAILURE);
+}
+
+int main(int argc, char **argv) {
+	uint16_t port = 2000;
+	uint32_t bs = 1024;
+	char *host, *nbddev, *bs_str;
+
+	if (argc < 3) {
+		print_help();
+		return(EXIT_FAILURE);
+	}
+
+	if (strcmp(argv[1], "-d") == 0) {
+		nbddev = argv[2];
+		return(nbd_disable(nbddev));
+	}
+
+	host = argv[1];
+	port = atoi(argv[2]);
+	nbddev = argv[3];
+
+	logging();
+
+	daemonize();
+
+	while (1) {
+		nbd_enable(host, port, nbddev, bs);
+		sleep(30);
+	}
+
+	return(EXIT_FAILURE);
 }

Reply to: