aboutsummaryrefslogtreecommitdiff
path: root/toys/pending/nbd_client.c
blob: 0465eea1621ac64f43d8ea3cbd40ba96da2fdfe6 (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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/* 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>3", TOYFLAG_USR|TOYFLAG_BIN))

config NBD_CLIENT
  bool "nbd-client"
  default n
  help
    Usage: nbd-client [-sSpn] [-b BLKSZ] [-t SECS] [-N name] HOST PORT DEVICE

    -b	block size
    -t	timeout in seconds
    -s	swap
    -S	sdp
    -p	persist
    -n	nofork
    -d	DEVICE
    -c	DEVICE
*/

#define FOR_nbd_client
#include "toys.h"

//#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)

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;
  char data[124];

  // Parse command line stuff (just a stub now)

  // 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);
}