aboutsummaryrefslogtreecommitdiff
path: root/toys/pending/nbd_client.c
blob: 86972303dcd6c7d4782c3a30d4cb855cb2053ffe (plain)
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
137
/* 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"
#include "toynet.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);
}