expand: add commented-out code to handle NULs
[oweals/busybox.git] / networking / nbd-client.c
1 /*
2  * Copyright 2010 Rob Landley <rob@landley.net>
3  *
4  * Licensed under GPLv2, see file LICENSE in this source tree.
5  */
6 //config:config NBDCLIENT
7 //config:       bool "nbd-client (6 kb)"
8 //config:       default y
9 //config:       help
10 //config:       Network block device client
11
12 //applet:IF_NBDCLIENT(APPLET_NOEXEC(nbd-client, nbdclient, BB_DIR_USR_SBIN, BB_SUID_DROP, nbdclient))
13
14 //kbuild:lib-$(CONFIG_NBDCLIENT) += nbd-client.o
15
16 #include "libbb.h"
17 #include <netinet/tcp.h>
18 #include <linux/fs.h>
19 #include <getopt.h>
20
21 #define NBD_SET_SOCK          _IO(0xab, 0)
22 #define NBD_SET_BLKSIZE       _IO(0xab, 1)
23 #define NBD_SET_SIZE          _IO(0xab, 2)
24 #define NBD_DO_IT             _IO(0xab, 3)
25 #define NBD_CLEAR_SOCK        _IO(0xab, 4)
26 #define NBD_CLEAR_QUEUE       _IO(0xab, 5)
27 #define NBD_PRINT_DEBUG       _IO(0xab, 6)
28 #define NBD_SET_SIZE_BLOCKS   _IO(0xab, 7)
29 #define NBD_DISCONNECT        _IO(0xab, 8)
30 #define NBD_SET_TIMEOUT       _IO(0xab, 9)
31 #define NBD_SET_FLAGS         _IO(0xab, 10)
32
33 //usage:#define nbdclient_trivial_usage
34 //usage:       "{ [-b BLKSIZE] [-N NAME] [-t SEC] [-p] HOST [PORT] | -d } BLOCKDEV"
35 //usage:#define nbdclient_full_usage "\n\n"
36 //usage:       "Connect to HOST and provide network block device on BLOCKDEV"
37
38 //TODO: more compat with nbd-client version 3.17 -
39 //nbd-client host [ port ] nbd-device [ -connections num ] [ -sdp ] [ -swap ]
40 //      [ -persist ] [ -nofork ] [ -nonetlink ] [ -systemd-mark ]
41 //      [ -block-size block size ] [ -timeout seconds ] [ -name name ]
42 //      [ -certfile certfile ] [ -keyfile keyfile ] [ -cacertfile cacertfile ]
43 //      [ -tlshostname hostname ]
44 //nbd-client -unix path nbd-device [ -connections num ] [ -sdp ] [ -swap ]
45 //      [ -persist ] [ -nofork ] [ -nonetlink ] [ -systemd-mark ]
46 //      [ -block-size block size ] [ -timeout seconds ] [ -name name ]
47 //nbd-client nbd-device
48 //nbd-client -d nbd-device
49 //nbd-client -c nbd-device
50 //nbd-client -l host [ port ]
51 //nbd-client [ -netlink ] -l host
52 //
53 //Default value for blocksize is 4096
54 //Allowed values for blocksize are 512,1024,2048,4096
55
56 int nbdclient_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
57 int nbdclient_main(int argc, char **argv)
58 {
59 #if BB_MMU
60         bool nofork;
61 #endif
62         bool opt_d;
63         bool opt_p;
64         const char *host, *port, *device;
65         const char *name;
66         unsigned blksize, size_blocks;
67         unsigned timeout;
68         int ch;
69         struct nbd_header_t {
70                 uint64_t magic1; // "NBDMAGIC"
71                 uint64_t magic2; // old style: 0x420281861253 big endian
72                 //               // new style: 0x49484156454F5054 (IHAVEOPT)
73         } nbd_header;
74         struct old_nbd_header_t {
75                 uint64_t devsize;
76                 uint32_t flags;
77                 char data[124];
78         } old_nbd_header;
79         struct new_nbd_header_t {
80                 uint64_t devsize;
81                 uint16_t transmission_flags;
82                 char data[124];
83         } new_nbd_header;
84         struct nbd_opt_t {
85                 uint64_t magic;
86                 uint32_t opt;
87                 uint32_t len;
88         } nbd_opts;
89
90         static const struct option long_options[] = {
91                 { "block-size", required_argument, NULL, 'b' },
92                 { "timeout"   , required_argument, NULL, 't' },
93                 { "name"      , required_argument, NULL, 'n' },
94                 { "persist"   , no_argument      , NULL, 'p' },
95                 { NULL }
96         };
97
98         BUILD_BUG_ON(offsetof(struct old_nbd_header_t, data) != 8 + 4);
99         BUILD_BUG_ON(offsetof(struct new_nbd_header_t, data) != 8 + 2);
100
101 #if !BB_MMU
102         bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
103 #endif
104
105         // Parse args. nbd-client uses stupid "one-dash long options" style :(
106         // Even though short forms (-b,-t,-N,-p) exist for all long opts,
107         // older manpages only contained long forms, which probably resulted
108         // in many scripts using them.
109         blksize = 4096;
110         timeout = 0;
111         name = ""; // use of "" instead of NULL simplifies strlen() later
112         opt_d = opt_p = 0;
113         while ((ch = getopt_long_only(argc, argv, "dN:", long_options, NULL)) != -1) {
114                 switch (ch) {
115                 case 'p':       // -persist
116                         opt_p = 1;
117                         break;
118                 case 'd':       // -d
119                         opt_d = 1;
120                         break;
121                 case 'b':       // -block-size
122                         blksize = xatou(optarg);
123                         break;
124                 case 't':       // -timeout
125                         timeout = xatou(optarg);
126                         break;
127                 case 'N':       // -N
128                 case 'n':       // -name
129                         name = optarg;
130                         break;
131                 default:
132                         bb_show_usage();
133                 }
134         }
135         argv += optind;
136
137         if (opt_d) { // -d
138                 if (argv[0] && !argv[1]) {
139                         int nbd = xopen(argv[0], O_RDWR);
140                         ioctl(nbd, NBD_DISCONNECT);
141                         ioctl(nbd, NBD_CLEAR_SOCK);
142                         if (ENABLE_FEATURE_CLEAN_UP)
143                                 close(nbd);
144                         return 0;
145                 }
146                 bb_show_usage();
147         }
148
149         // Allow only argv[] of: HOST [PORT] BLOCKDEV
150         if (!argv[0] || !argv[1] || (argv[2] && argv[3])) {
151                 bb_show_usage();
152         }
153
154         host = argv[0];
155         port = argv[2] ? argv[1] : "10809";
156         device = argv[2] ? argv[2] : argv[1];
157
158         // Repeat until spanked if -persist
159 #if BB_MMU
160         nofork = 0;
161 #endif
162         do {
163                 int sock, nbd;
164                 int ro;
165                 int proto_new; // 0 for old, 1 for new
166 #if BB_MMU
167                 char *data;
168 #endif
169
170                 // Make sure BLOCKDEV exists
171                 nbd = xopen(device, O_RDWR);
172
173                 // Find and connect to server
174                 sock = create_and_connect_stream_or_die(host, xatou16(port));
175                 setsockopt_1(sock, IPPROTO_TCP, TCP_NODELAY);
176
177                 // Log on to the server
178                 xread(sock, &nbd_header, 8 + 8);
179                 if (memcmp(&nbd_header.magic1, "NBDMAGIC",
180                                 sizeof(nbd_header.magic1)) != 0
181                 ) {
182                         bb_error_msg_and_die("login failed");
183                 }
184                 if (memcmp(&nbd_header.magic2,
185                                 "\x00\x00\x42\x02\x81\x86\x12\x53",
186                                 sizeof(nbd_header.magic2)) == 0
187                 ) {
188                         proto_new = 0;
189                 } else if (memcmp(&nbd_header.magic2, "IHAVEOPT", 8) == 0) {
190                         proto_new = 1;
191                 } else {
192                         bb_error_msg_and_die("login failed");
193                 }
194
195                 if (!proto_new) {
196                         xread(sock, &old_nbd_header,
197                                         sizeof(old_nbd_header.devsize) +
198                                         sizeof(old_nbd_header.flags) +
199                                         sizeof(old_nbd_header.data));
200                         size_blocks = SWAP_BE64(old_nbd_header.devsize) / blksize;
201                         ioctl(nbd, NBD_SET_BLKSIZE, (unsigned long) blksize);
202                         ioctl(nbd, NBD_SET_SIZE_BLOCKS, size_blocks);
203                         ioctl(nbd, NBD_CLEAR_SOCK);
204                         ro = !!(old_nbd_header.flags & htons(2));
205 #if BB_MMU
206                         data = old_nbd_header.data;
207 #endif
208                 } else {
209                         unsigned namelen;
210                         uint16_t handshake_flags;
211
212                         xread(sock, &handshake_flags, sizeof(handshake_flags));
213                         xwrite(sock, &const_int_0, sizeof(const_int_0)); // client_flags
214
215                         memcpy(&nbd_opts.magic, "IHAVEOPT",
216                                         sizeof(nbd_opts.magic));
217                         nbd_opts.opt = htonl(1); // NBD_OPT_EXPORT_NAME
218                         namelen = strlen(name);
219                         nbd_opts.len = htonl(namelen);
220                         xwrite(sock, &nbd_opts,
221                                         sizeof(nbd_opts.magic) +
222                                         sizeof(nbd_opts.opt) +
223                                         sizeof(nbd_opts.len));
224                         xwrite(sock, name, namelen);
225
226                         xread(sock, &new_nbd_header,
227                                         sizeof(new_nbd_header.devsize) +
228                                         sizeof(new_nbd_header.transmission_flags) +
229                                         sizeof(new_nbd_header.data));
230                         size_blocks = SWAP_BE64(new_nbd_header.devsize) / blksize;
231                         ioctl(nbd, NBD_SET_BLKSIZE, (unsigned long) blksize);
232                         ioctl(nbd, NBD_SET_SIZE_BLOCKS, size_blocks);
233                         ioctl(nbd, NBD_CLEAR_SOCK);
234                         ioctl(nbd, NBD_SET_FLAGS,
235                                         ntohs(new_nbd_header.transmission_flags));
236                         ro = !!(new_nbd_header.transmission_flags & htons(2));
237 #if BB_MMU
238                         data = new_nbd_header.data;
239 #endif
240                 }
241
242                 if (ioctl(nbd, BLKROSET, &ro) < 0) {
243                         bb_perror_msg_and_die("BLKROSET");
244                 }
245
246                 if (timeout) {
247                         if (ioctl(nbd, NBD_SET_TIMEOUT, (unsigned long) timeout)) {
248                                 bb_perror_msg_and_die("NBD_SET_TIMEOUT");
249                         }
250                 }
251
252                 if (ioctl(nbd, NBD_SET_SOCK, sock)) {
253                         bb_perror_msg_and_die("NBD_SET_SOCK");
254                 }
255
256                 //if (swap) mlockall(MCL_CURRENT|MCL_FUTURE);
257 #if BB_MMU
258                 // Open the device to force reread of the partition table.
259                 // Need to do it in a separate process, since open(device)
260                 // needs some other process to sit in ioctl(nbd, NBD_DO_IT).
261                 if (fork() == 0) {
262                         /* child */
263                         char *s = strrchr(device, '/');
264                         sprintf(data, "/sys/block/%.32s/pid", s ? s + 1 : device);
265                         // Is it up yet?
266                         for (;;) {
267                                 int fd = open(data, O_RDONLY);
268                                 if (fd >= 0) {
269                                         if (ENABLE_FEATURE_CLEAN_UP)
270                                                 close(fd);
271                                         break;
272                                 }
273                                 sleep(1);
274                         }
275                         open(device, O_RDONLY);
276                         return 0;
277                 }
278
279                 // Daemonize here
280                 if (!nofork) {
281                         daemon(0, 0);
282                         nofork = 1;
283                 }
284 #endif
285                 // This turns us (the process that calls this ioctl)
286                 // into a dedicated NBD request handler.
287                 // We block here for a long time.
288                 // When exactly ioctl returns? On a signal,
289                 // or if someone does ioctl(NBD_DISCONNECT) [nbd-client -d].
290                 if (ioctl(nbd, NBD_DO_IT) >= 0 || errno == EBADR) {
291                         // Flush queue and exit
292                         ioctl(nbd, NBD_CLEAR_QUEUE);
293                         ioctl(nbd, NBD_CLEAR_SOCK);
294                         break;
295                 }
296
297                 close(sock);
298                 close(nbd);
299         } while (opt_p);
300
301         return 0;
302 }