1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
/* vi: set sw=4 ts=4:
*
* nbd-client.c - network block device client
*
* Copyright 2010 Rob Landley <rob@landley.net>
*
* Not in SUSv4.
USE_NBD_CLIENT(NEWTOY(nbd_client, "<3>3ns", TOYFLAG_USR|TOYFLAG_BIN))
config NBD_CLIENT
bool "nbd-client"
default n
help
usage: nbd-client [-ns] HOST PORT DEVICE
-n Do not fork into background
-s nbd swap support (lock server into memory)
*/
/*
Usage: nbd-client [-sSpn] [-b BLKSZ] [-t SECS] [-N name] HOST PORT DEVICE
-b block size
-t timeout in seconds
-S sdp
-p persist
-n nofork
-d DEVICE
-c DEVICE
*/
#define FOR_nbd_client
#include "toys.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)
void nbd_client_main(void)
{
int sock = -1, nbd, flags;
unsigned long timeout = 0;
struct addrinfo *addr, *p;
char *host=toys.optargs[0], *port=toys.optargs[1], *device=toys.optargs[2];
uint64_t devsize;
// Repeat until spanked
nbd = xopen(device, O_RDWR);
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;
close(sock);
}
freeaddrinfo(addr);
if (!p) perror_exit("%s:%s", host, port);
temp = 1;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &temp, sizeof(int));
// Read login data
xreadall(sock, toybuf, 152);
if (memcmp(toybuf, "NBDMAGIC\x00\x00\x42\x02\x81\x86\x12\x53", 16))
error_exit("bad login %s:%s", host, port);
devsize = SWAP_BE64(*(uint64_t *)(toybuf+16));
flags = SWAP_BE32(*(int *)(toybuf+24));
// 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;
xioctl(nbd, BLKROSET, &temp);
if (timeout && ioctl(nbd, NBD_SET_TIMEOUT, timeout)<0) break;
if (ioctl(nbd, NBD_SET_SOCK, sock) < 0) break;
if (toys.optflags & FLAG_s) mlockall(MCL_CURRENT|MCL_FUTURE);
// Open the device to force reread of the partition table.
if ((toys.optflags & FLAG_n) || !fork()) {
char *s = strrchr(device, '/');
int i;
sprintf(toybuf, "/sys/block/%.32s/pid", s ? s+1 : device);
// Is it up yet? (Give it 10 seconds.)
for (i=0; i<100; i++) {
temp = open(toybuf, O_RDONLY);
if (temp == -1) msleep(100);
else {
close(temp);
break;
}
}
close(open(device, O_RDONLY));
if (!(toys.optflags & FLAG_n)) 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);
}
|