[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: