Finally got some time to debug this, and it now actually works. nbd 1.2.3.4 9876 /dev/nbd0 mount /dev/nbd0 /blah
It's not busyboxified at all (standalone, no external dependencies). If a server exports multiple NBDs it only supports selecting them by port number (there's an extention to select them by name; I didn't need it so didn't implement it). Its command line option parsing is somewhere between "pathetic" and "nonexistent", so there's no if() around the call to daemon() and such, and the 4k block size is hardwired (which means with an MTU of 1500 the packets will be split, but just about all modern filesystems are using a 4k block size anyway and gigabit ethernet with a 1500 byte mtu is silly). But it Worked For Me. Denys: you wanna busyboxify it, or wait for me to get around to it now that my immediate need for something working is satisfied? :) Does anybody care deeply about nbd-server? Rob P.S. My motivation for writing this is documented at http://landley.net/notes-2010.html#26-08-2010 and yes, it really was easier for me to write a new one than try to configure and build the old one from source. It's THAT pointlessly overcomplicated. They autoconfed it within an inch of its life, for NO REASON... Sigh. -- GPLv3: as worthy a successor as The Phantom Menace, as timely as Duke Nukem Forever, and as welcome as New Coke.
/* nbd-client Copyright 2010 Rob Landley <[email protected]> Licensed under GPLv2 Usage: nbd-client HOST PORT DEVICE [-sSpn] [-b BLOCK_SIZE] [-t TIMEOUT] [-N name] -s swap -S sdp -p persist -n nofork -d DEVICE -c DEVICE */ #include <errno.h> #include <fcntl.h> #include <limits.h> #include <netdb.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <netinet/tcp.h> #include <sys/ioctl.h> #include <sys/mount.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/stat.h> #define NBD_SET_SOCK _IO(0xab, 0) #define NBD_SET_BLKSIZE _IO(0xab, 1) #define NBD_SET_SIZE _IO(0xab, 2) #define NBD_DO_IT _IO(0xab, 3) #define NBD_CLEAR_SOCK _IO(0xab, 4) #define NBD_CLEAR_QUEUE _IO(0xab, 5) #define NBD_PRINT_DEBUG _IO(0xab, 6) #define NBD_SET_SIZE_BLOCKS _IO(0xab, 7) #define NBD_DISCONNECT _IO(0xab, 8) #define NBD_SET_TIMEOUT _IO(0xab, 9) #if __BYTE_ORDER == __LITTLE_ENDIAN #include <byteswap.h> #define SWAP_BE32(x) __bswap_32(x) #define SWAP_BE64(x) __bswap_64(x) #else #define SWAP_BE32(x) (x) #define SWAP_BE64(x) (x) #endif int main(int argc, char *argv[]) { int sock, nbd, flags; unsigned long timeout = 0; struct addrinfo *addr, *p; char *host=argv[1], *port=argv[2], *device=argv[3]; uint64_t devsize; char data[124]; // Parse command line stuff (just a stub now) if (argc != 4) { fprintf(stderr, "Usage: nbd-client HOST PORT DEVICE\n"); exit(1); } // Make sure the /dev/nbd exists. if (0>(nbd = open(device, O_RDWR))) { fprintf(stderr, "Can't open '%s'\n", device); exit(1); } // Repeat until spanked for (;;) { int temp; struct addrinfo hints; // Find and connect to server memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if (getaddrinfo(host, port, &hints, &addr)) addr = 0; for (p = addr; p; p = p->ai_next) { sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol); if (-1 != connect(sock, p->ai_addr, p->ai_addrlen)) break; } freeaddrinfo(addr); if (!p) { fprintf(stderr, "Can't connect '%s' port '%s'\n", host, port); exit(1); } temp = 1; setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &temp, sizeof(int)); // Log on to the server. (Todo: one big 8+8+8+4+124=152 read) if (read(sock, data, 8) != 8 || memcmp(data, "NBDMAGIC", 8) || read(sock, &devsize, 8) != 8 || SWAP_BE64(devsize) != 0x420281861253LL || read(sock, &devsize, 8) != 8 || read(sock, &flags, 4) != 4 || read(sock, data, 124) != 124) { fprintf(stderr, "Login fail\n"); exit(1); } devsize = SWAP_BE64(devsize); flags = SWAP_BE32(flags); // Set 4k block size. Everything uses that these days. ioctl(nbd, NBD_SET_BLKSIZE, 4096); ioctl(nbd, NBD_SET_SIZE_BLOCKS, devsize/4096); ioctl(nbd, NBD_CLEAR_SOCK); // If the sucker was exported read only, respect that locally. temp = (flags & 2) ? 1 : 0; if (ioctl(nbd, BLKROSET, &temp)<0) { fprintf(stderr, "Login fail\n"); exit(1); } if (timeout && ioctl(nbd, NBD_SET_TIMEOUT, timeout)<0) break; if (ioctl(nbd, NBD_SET_SOCK, sock) < 0) break; // if (swap) mlockall(MCL_CURRENT|MCL_FUTURE); // Open the device to force reread of the partition table. if (!fork()) { char *s = strrchr(device, '/'); sprintf(data, "/sys/block/%.32s/pid", s ? s+1 : device); // Is it up yet? for (;;) { temp = open(data, O_RDONLY); if (temp == -1) sleep(1); else { close(temp); break; } } close(open(device, O_RDONLY)); exit(0); } // Daemonize here. daemon(0,0); // Process NBD requests until further notice. if (ioctl(nbd, NBD_DO_IT)>=0 || errno==EBADR) break; close(sock); close(nbd); } // Flush queue and exit. ioctl(nbd, NBD_CLEAR_QUEUE); ioctl(nbd, NBD_CLEAR_SOCK); exit(0); }
_______________________________________________ busybox mailing list [email protected] http://lists.busybox.net/mailman/listinfo/busybox
