63 #include <sys/types.h>
64 #include <sys/socket.h>
66 #include <sys/select.h>
68 #ifdef HAVE_SYS_IOCTL_H
69 #include <sys/ioctl.h>
71 #include <sys/param.h>
72 #ifdef HAVE_SYS_MOUNT_H
73 #include <sys/mount.h>
77 #include <netinet/tcp.h>
78 #include <netinet/in.h>
88 #include <linux/falloc.h>
90 #include <arpa/inet.h>
102 #define MY_NAME "nbd_server"
107 #include <sdp_inet.h>
112 #define SYSCONFDIR "/etc"
114 #define CFILE SYSCONFDIR "/nbd-server/config"
127 #define msg(prio, ...) syslog(prio, __VA_ARGS__)
129 #define msg(prio, ...) g_log(G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, __VA_ARGS__)
135 #define DEBUG(...) printf(__VA_ARGS__)
139 #ifndef PACKAGE_VERSION
140 #define PACKAGE_VERSION ""
146 #define OFFT_MAX ~((off_t)1<<(sizeof(off_t)*8-1))
149 #define BUFSIZE ((1024*1024)+sizeof(struct nbd_reply))
150 #define DIFFPAGESIZE 4096
154 #define F_MULTIFILE 2
155 #define F_COPYONWRITE 4
157 #define F_AUTOREADONLY 8
163 #define F_ROTATIONAL 512
164 #define F_TEMPORARY 1024
176 #define NEG_INIT (1 << 0)
177 #define NEG_OLD (1 << 1)
178 #define NEG_MODERN (1 << 2)
267 int transactionlogfd;
318 return "NBD_CMD_READ";
320 return "NBD_CMD_WRITE";
322 return "NBD_CMD_DISC";
324 return "NBD_CMD_FLUSH";
326 return "NBD_CMD_TRIM";
340 const char *ERRMSG=
"Invalid entry '%s' in authfile '%s', so, refusing all connections.";
345 struct in_addr client;
346 struct in_addr cltemp;
350 msg(LOG_INFO,
"Can't open authorization file %s (%s).",
356 while (fgets(line,
LINELEN,f)!=NULL) {
357 if((tmp=strchr(line,
'/'))) {
358 if(strlen(line)<=tmp-line) {
363 if(!inet_aton(line,&addr)) {
367 len=strtol(tmp, NULL, 0);
368 addr.s_addr>>=32-
len;
369 addr.s_addr<<=32-
len;
370 memcpy(&cltemp,&client,
sizeof(client));
371 cltemp.s_addr>>=32-
len;
372 cltemp.s_addr<<=32-
len;
373 if(addr.s_addr == cltemp.s_addr) {
393 static inline void readit(
int f,
void *buf,
size_t len) {
397 if ((res = read(f, buf, len)) <= 0) {
398 if(errno != EAGAIN) {
399 err(
"Read failed: %m");
416 static inline void consume(
int f,
void * buf,
size_t len,
size_t bufsiz) {
419 curlen = (len>bufsiz)?bufsiz:len;
433 static inline void writeit(
int f,
void *buf,
size_t len) {
437 if ((res = write(f, buf, len)) <= 0)
438 err(
"Send failed: %m");
449 printf(
"This is nbd-server version " VERSION "\n");
450 printf(
"Usage: [ip:|ip6@]port file_to_export [size][kKmM] [-l authorize_file] [-r] [-m] [-c] [-C configuration file] [-p PID file name] [-o section name] [-M max connections]\n"
451 "\t-r|--read-only\t\tread only\n"
452 "\t-m|--multi-file\t\tmultiple file\n"
453 "\t-c|--copy-on-write\tcopy on write\n"
454 "\t-C|--config-file\tspecify an alternate configuration file\n"
455 "\t-l|--authorize-file\tfile with list of hosts that are allowed to\n\t\t\t\tconnect.\n"
456 "\t-p|--pid-file\t\tspecify a filename to write our PID to\n"
457 "\t-o|--output-config\toutput a config file section for what you\n\t\t\t\tspecified on the command line, with the\n\t\t\t\tspecified section name\n"
458 "\t-M|--max-connections\tspecify the maximum number of opened connections\n\n"
459 "\tif port is set to 0, stdin is used (for running from inetd).\n"
460 "\tif file_to_export contains '%%s', it is substituted with the IP\n"
461 "\t\taddress of the machine trying to connect\n"
462 "\tif ip is set, it contains the local IP address on which we're listening.\n\tif not, the server will listen on all local IP addresses\n");
463 printf(
"Using configuration file %s\n",
CFILE);
468 printf(
"[%s]\n", section_header);
470 printf(
"\tlistenaddr = %s\n", serve->
listenaddr);
471 printf(
"\tport = %d\n", serve->
port);
473 printf(
"\treadonly = true\n");
476 printf(
"\tmultifile = true\n");
479 printf(
"\tcopyonwrite = true\n");
482 printf(
"\tfilesize = %lld\n", (
long long int)serve->
expected_size);
485 printf(
"\tauthfile = %s\n", serve->
authname);
500 struct option long_options[] = {
501 {
"read-only", no_argument, NULL,
'r'},
502 {
"multi-file", no_argument, NULL,
'm'},
503 {
"copy-on-write", no_argument, NULL,
'c'},
504 {
"dont-fork", no_argument, NULL,
'd'},
505 {
"authorize-file", required_argument, NULL,
'l'},
506 {
"config-file", required_argument, NULL,
'C'},
507 {
"pid-file", required_argument, NULL,
'p'},
508 {
"output-config", required_argument, NULL,
'o'},
509 {
"max-connection", required_argument, NULL,
'M'},
516 gboolean do_output=FALSE;
517 gchar* section_header=
"";
526 while((c=getopt_long(argc, argv,
"-C:cdl:mo:rp:M:", long_options, &i))>=0) {
530 switch(nonspecial++) {
532 if(strchr(optarg,
':') == strrchr(optarg,
':')) {
533 addr_port=g_strsplit(optarg,
":", 2);
538 g_strfreev(addr_port);
539 addr_port=g_strsplit(optarg,
"@", 2);
542 addr_port=g_strsplit(optarg,
"@", 2);
546 serve->
port=strtol(addr_port[1], NULL, 0);
550 serve->
port=strtol(addr_port[0], NULL, 0);
552 g_strfreev(addr_port);
557 fprintf(stderr,
"E: The to be exported file needs to be an absolute filename!\n");
562 last=strlen(optarg)-1;
564 if (suffix ==
'k' || suffix ==
'K' ||
565 suffix ==
'm' || suffix ==
'M')
567 es = (off_t)atoll(optarg);
587 section_header = g_strdup(optarg);
625 g_critical(
"Need a complete configuration on the command line to output a config file section!");
636 #define NBDS_ERR g_quark_from_static_string("server-error-quark")
719 struct addrinfo hints;
720 struct addrinfo *ai = NULL;
721 struct addrinfo *rp = NULL;
722 char host[NI_MAXHOST];
729 port = g_strdup_printf(
"%d", s->
port);
731 memset(&hints,
'\0',
sizeof(hints));
732 hints.ai_family = AF_UNSPEC;
733 hints.ai_socktype = SOCK_STREAM;
734 hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE;
735 hints.ai_protocol = IPPROTO_TCP;
737 e = getaddrinfo(s->
listenaddr, port, &hints, &ai);
743 for (rp = ai; rp != NULL; rp = rp->ai_next) {
744 e = getnameinfo(rp->ai_addr, rp->ai_addrlen, host,
sizeof(host), NULL, 0, NI_NUMERICHOST);
747 fprintf(stderr,
"getnameinfo: %s\n", gai_strerror(e));
756 g_array_append_val(a, *ns);
764 fprintf(stderr,
"getaddrinfo failed on listen host/address: %s (%s)\n", s->
listenaddr ? s->
listenaddr :
"any", gai_strerror(e));
783 DIR* dirh = opendir(dir);
786 GArray* retval = NULL;
795 while((de = readdir(dirh))) {
796 int saved_errno=errno;
797 fname = g_build_filename(dir, de->d_name, NULL);
803 if(stat(fname, &stbuf)) {
807 if (!S_ISREG(stbuf.st_mode)) {
812 if(strcmp((de->d_name + strlen(de->d_name) - 5),
".conf")) {
821 retval = g_array_new(FALSE, TRUE,
sizeof(
SERVER));
822 retval = g_array_append_vals(retval, tmp->data, tmp->len);
823 g_array_free(tmp, TRUE);
834 g_array_free(retval, TRUE);
859 const char* DEFAULT_ERROR =
"Could not parse %s in group %s: %s";
860 const char* MISSING_REQUIRED_ERROR =
"Could not find required value %s in group %s: %s";
863 gchar *virtstyle=NULL;
887 const int lp_size=
sizeof(lp)/
sizeof(
PARAM);
899 int p_size=
sizeof(gp)/
sizeof(
PARAM);
902 const char *err_msg=NULL;
919 memcpy(&genconftmp, genconf,
sizeof(
struct generic_conf));
922 cfile = g_key_file_new();
923 retval = g_array_new(FALSE, TRUE,
sizeof(
SERVER));
924 if(!g_key_file_load_from_file(cfile, f, G_KEY_FILE_KEEP_COMMENTS |
925 G_KEY_FILE_KEEP_TRANSLATIONS, &err)) {
928 g_key_file_free(cfile);
931 startgroup = g_key_file_get_start_group(cfile);
932 if((!startgroup || strcmp(startgroup,
"generic")) && genconf) {
934 g_key_file_free(cfile);
937 groups = g_key_file_get_groups(cfile, NULL);
938 for(i=0;groups[i];i++) {
939 memset(&s,
'\0',
sizeof(
SERVER));
943 if(i==1 || !genconf) {
950 for(j=0;j<p_size;j++) {
951 assert(p[j].target != NULL);
952 assert(p[j].ptype==PARAM_INT||p[j].ptype==PARAM_STRING||p[j].ptype==PARAM_BOOL||p[j].ptype==
PARAM_INT64);
955 ival = g_key_file_get_integer(cfile,
960 *((gint*)p[j].target) = ival;
964 i64val = g_key_file_get_int64(cfile,
969 *((gint64*)p[j].target) = i64val;
973 sval = g_key_file_get_string(cfile,
978 *((gchar**)p[j].target) = sval;
982 bval = g_key_file_get_boolean(cfile,
984 p[j].paramname, &err);
987 *((gint*)p[j].target) |= p[j].flagval;
989 *((gint*)p[j].target) &= ~(p[j].flagval);
995 if(err->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND) {
1001 err_msg = MISSING_REQUIRED_ERROR;
1004 err_msg = DEFAULT_ERROR;
1007 g_array_free(retval, TRUE);
1009 g_key_file_free(cfile);
1014 if(!strncmp(virtstyle,
"none", 4)) {
1016 }
else if(!strncmp(virtstyle,
"ipliteral", 9)) {
1018 }
else if(!strncmp(virtstyle,
"iphash", 6)) {
1020 }
else if(!strncmp(virtstyle,
"cidrhash", 8)) {
1022 if(strlen(virtstyle)<10) {
1024 g_array_free(retval, TRUE);
1025 g_key_file_free(cfile);
1028 s.
cidrlen=strtol(virtstyle+8, NULL, 0);
1031 g_array_free(retval, TRUE);
1032 g_key_file_free(cfile);
1039 g_warning(
"A port was specified, but oldstyle exports were not requested. This may not do what you expect.");
1040 g_warning(
"Please read 'man 5 nbd-server' and search for oldstyle for more info");
1045 if(i>0 || !genconf) {
1054 g_array_free(retval, TRUE);
1055 g_key_file_free(cfile);
1060 g_key_file_free(cfile);
1064 retval = g_array_append_vals(retval, extra->data, extra->len);
1066 g_array_free(extra, TRUE);
1069 g_array_free(retval, TRUE);
1074 if(i==1 && genconf) {
1081 memcpy(genconf, &genconftmp,
sizeof(
struct generic_conf));
1108 void killchild(gpointer key, gpointer value, gpointer user_data) {
1111 kill(*pid, SIGTERM);
1150 struct stat stat_buf;
1153 #ifdef HAVE_SYS_MOUNT_H
1154 #ifdef HAVE_SYS_IOCTL_H
1156 DEBUG(
"looking for export size with ioctl BLKGETSIZE64\n");
1157 if (!ioctl(fhandle, BLKGETSIZE64, &bytes) && bytes) {
1158 return (off_t)bytes;
1164 DEBUG(
"looking for fhandle size with fstat\n");
1165 stat_buf.st_size = 0;
1166 error = fstat(fhandle, &stat_buf);
1170 if (S_ISREG(stat_buf.st_mode) || (stat_buf.st_size > 0))
1171 return (off_t)stat_buf.st_size;
1173 err(
"fstat failed: %m");
1176 DEBUG(
"looking for fhandle size with lseek SEEK_END\n");
1177 es = lseek(fhandle, (off_t)0, SEEK_END);
1178 if (es > ((off_t)0)) {
1181 DEBUG(
"lseek failed: %d", errno==EBADF?1:(errno==ESPIPE?2:(errno==EINVAL?3:4)));
1184 err(
"Could not find size of exported block device: %m");
1198 int get_filepos(GArray* export, off_t a,
int* fhandle, off_t* foffset,
size_t* maxbytes ) {
1206 int end = export->len - 1;
1207 while( start <= end ) {
1208 int mid = (start + end) / 2;
1209 fi = g_array_index(export,
FILE_INFO, mid);
1223 fi = g_array_index(export,
FILE_INFO, end);
1227 if( end+1 < export->len ) {
1244 if (lseek(handle, a, SEEK_SET) < 0) {
1245 err(
"Can not seek locally!\n");
1268 if(maxbytes && len > maxbytes)
1271 DEBUG(
"(WRITE to fd %d offset %llu len %u fua %d), ", fhandle, (
long long unsigned)foffset, (
unsigned int)len, fua);
1273 myseek(fhandle, foffset);
1274 retval = write(fhandle, buf, len);
1310 sync_file_range(fhandle, foffset, len,
1311 SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE |
1312 SYNC_FILE_RANGE_WAIT_AFTER);
1333 while(len > 0 && (ret=
rawexpwrite(a, buf, len, client, fua)) > 0 ) {
1338 return (ret < 0 || len != 0);
1359 if(maxbytes && len > maxbytes)
1362 DEBUG(
"(READ from fd %d offset %llu len %u), ", fhandle, (
long long unsigned int)foffset, (
unsigned int)len);
1364 myseek(fhandle, foffset);
1365 return read(fhandle, buf, len);
1375 while(len > 0 && (ret=
rawexpread(a, buf, len, client)) > 0 ) {
1380 return (ret < 0 || len != 0);
1393 int expread(off_t a,
char *buf,
size_t len,
CLIENT *client) {
1394 off_t rdlen, offset;
1395 off_t mapcnt, mapl, maph, pagestart;
1399 DEBUG(
"Asked to read %u bytes at %llu.\n", (
unsigned int)len, (
unsigned long long)a);
1403 for (mapcnt=mapl;mapcnt<=maph;mapcnt++) {
1406 rdlen=(0<DIFFPAGESIZE-offset && len<(size_t)(DIFFPAGESIZE-offset)) ?
1407 len : (
size_t)DIFFPAGESIZE-offset;
1408 if (client->
difmap[mapcnt]!=(u32)(-1)) {
1409 DEBUG(
"Page %llu is at %lu\n", (
unsigned long long)mapcnt,
1410 (
unsigned long)(client->
difmap[mapcnt]));
1412 if (read(client->
difffile, buf, rdlen) != rdlen)
return -1;
1414 DEBUG(
"Page %llu is not here, we read the original one\n",
1415 (
unsigned long long)mapcnt);
1418 len-=rdlen; a+=rdlen; buf+=rdlen;
1435 int expwrite(off_t a,
char *buf,
size_t len,
CLIENT *client,
int fua) {
1437 off_t mapcnt,mapl,maph;
1444 DEBUG(
"Asked to write %u bytes at %llu.\n", (
unsigned int)len, (
unsigned long long)a);
1448 for (mapcnt=mapl;mapcnt<=maph;mapcnt++) {
1450 offset=a-pagestart ;
1451 wrlen=(0<DIFFPAGESIZE-offset && len<(size_t)(DIFFPAGESIZE-offset)) ?
1452 len : (
size_t)DIFFPAGESIZE-offset;
1454 if (client->
difmap[mapcnt]!=(u32)(-1)) {
1455 DEBUG(
"Page %llu is at %lu\n", (
unsigned long long)mapcnt,
1456 (
unsigned long)(client->
difmap[mapcnt])) ;
1458 client->
difmap[mapcnt]*DIFFPAGESIZE+offset);
1459 if (write(client->
difffile, buf, wrlen) != wrlen)
return -1 ;
1463 DEBUG(
"Page %llu is not here, we put it at %lu\n",
1464 (
unsigned long long)mapcnt,
1465 (
unsigned long)(client->
difmap[mapcnt]));
1469 memcpy(pagebuf+offset,buf,wrlen) ;
1470 if (write(client->
difffile, pagebuf, DIFFPAGESIZE) !=
1474 len-=wrlen ; a+=wrlen ; buf+=wrlen ;
1500 for (i = 0; i < client->
export->len; i++) {
1521 if(i<client->export->len) {
1527 fallocate(prev.
fhandle, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, curoff, curlen);
1530 }
while(i < client->export->len && cur.
startoff < (req->
from + req->
len));
1531 DEBUG(
"Performed TRIM request from %llu to %llu", (
unsigned long long) req->
from, (
unsigned long long) req->
len);
1533 DEBUG(
"Ignoring TRIM request (not supported on current platform");
1538 static void send_reply(uint32_t opt,
int net, uint32_t reply_type,
size_t datasize,
void* data) {
1540 reply_type = htonl(reply_type);
1541 uint32_t datsize = htonl(datasize);
1542 struct iovec v_data[] = {
1544 { &opt,
sizeof(opt) },
1545 { &reply_type,
sizeof(reply_type) },
1546 { &datsize,
sizeof(datsize) },
1549 writev(net, v_data, 5);
1557 if (read(net, &namelen,
sizeof(namelen)) < 0) {
1558 err(
"Negotiation failed/7: %m");
1561 namelen = ntohl(namelen);
1562 name = malloc(namelen+1);
1564 if (read(net, name, namelen) < 0) {
1565 err(
"Negotiation failed/8: %m");
1569 for(i=0; i<servers->len; i++) {
1583 err(
"Negotiation failed/8a: Requested export not found");
1588 static void handle_list(uint32_t opt,
int net, GArray* servers, uint32_t cflags) {
1592 char *ptr = buf +
sizeof(
len);
1594 if (read(net, &len,
sizeof(len)) < 0)
1595 err(
"Negotiation failed/8: %m");
1605 for(i=0; i<servers->len; i++) {
1608 memcpy(buf, &len,
sizeof(len));
1624 uint16_t smallflags = 0;
1627 memset(zeros,
'\0',
sizeof(zeros));
1632 if(phase & NEG_INIT) {
1639 if(phase & NEG_MODERN) {
1646 if (write(net, &magic,
sizeof(magic)) < 0) {
1652 if ((phase & NEG_MODERN) && (phase & NEG_INIT)) {
1658 err(
"programmer error");
1659 smallflags = htons(smallflags);
1660 if (write(net, &smallflags,
sizeof(uint16_t)) < 0)
1662 if (read(net, &cflags,
sizeof(cflags)) < 0)
1664 cflags = htonl(cflags);
1666 if (read(net, &magic,
sizeof(magic)) < 0)
1673 if (read(net, &opt,
sizeof(opt)) < 0)
1701 if (write(net, &size_host, 8) < 0)
1702 err(
"Negotiation failed/9: %m");
1715 flags = htonl(flags);
1716 if (write(client->
net, &flags, 4) < 0)
1717 err(
"Negotiation failed/10: %m");
1720 smallflags = (uint16_t)(flags & ~((uint16_t)0));
1721 smallflags = htons(smallflags);
1722 if (write(client->
net, &smallflags,
sizeof(smallflags)) < 0) {
1723 err(
"Negotiation failed/11: %m");
1727 if (write(client->
net, zeros, 124) < 0)
1728 err(
"Negotiation failed/12: %m");
1733 #define SEND(net,reply) { writeit( net, &reply, sizeof( reply )); \
1734 if (client->transactionlogfd != -1) \
1735 writeit(client->transactionlogfd, &reply, sizeof(reply)); }
1737 #define ERROR(client,reply,errcode) { reply.error = htonl(errcode); SEND(client->net,reply); reply.error = 0; }
1750 gboolean go_on=TRUE;
1754 negotiate(client->
net, client, NULL, client->
modern ? NEG_MODERN : (NEG_OLD | NEG_INIT));
1755 DEBUG(
"Entering request loop!\n");
1769 readit(client->
net, &request,
sizeof(request));
1774 request.
type = ntohl(request.
type);
1776 len = ntohl(request.
len);
1779 (
unsigned long long)request.
from,
1780 (
unsigned long long)request.
from / 512, len);
1783 err(
"Not enough magic.");
1785 memcpy(reply.handle, request.
handle,
sizeof(reply.handle));
1788 if (request.
from + len < request.
from) {
1789 DEBUG(
"[Number too large!]");
1790 ERROR(client, reply, EINVAL);
1796 ERROR(client, reply, EINVAL);
1804 msg(LOG_DEBUG,
"oversized request (this is not a problem)");
1813 msg(LOG_INFO,
"Disconnect request received.");
1824 DEBUG(
"wr: net->buf, ");
1827 DEBUG(
"buf->exp, ");
1830 DEBUG(
"[WRITE to READONLY!]");
1831 ERROR(client, reply, EPERM);
1837 DEBUG(
"Write failed: %m" );
1838 ERROR(client, reply, errno);
1843 request.
from += currlen;
1853 DEBUG(
"Flush failed: %m");
1854 ERROR(client, reply, errno);
1862 DEBUG(
"exp->buf, ");
1870 DEBUG(
"Read failed: %m");
1871 ERROR(client, reply, errno);
1875 DEBUG(
"buf->net, ");
1878 request.
from += currlen;
1889 if (
exptrim(&request, client)) {
1890 DEBUG(
"Trim failed: %m");
1891 ERROR(client, reply, errno);
1898 DEBUG (
"Ignoring unknown command\n");
1912 off_t laststartoff = 0, lastsize = 0;
1925 gchar* error_string;
1931 O_RDONLY : (O_RDWR | (cancreate?O_CREAT:0));
1934 tmpname=g_strdup_printf(
"%s.%d-XXXXXX", client->
exportname, i);
1935 DEBUG(
"Opening %s\n", tmpname );
1936 fi.
fhandle = mkstemp(tmpname);
1939 tmpname=g_strdup_printf(
"%s.%d", client->
exportname, i);
1943 DEBUG(
"Opening %s\n", tmpname );
1944 fi.
fhandle = open(tmpname, mode, 0x600);
1945 if(fi.
fhandle == -1 && mode == O_RDWR) {
1947 fi.
fhandle = open(tmpname, O_RDONLY);
1959 if(multifile && i>0)
1961 error_string=g_strdup_printf(
1962 "Could not open exported file %s: %%m",
1970 fi.
startoff = laststartoff + lastsize;
1971 g_array_append_val(client->
export, fi);
1980 if (!lastsize && cancreate) {
1983 err(
"Could not expand file: %m");
1989 if(!multifile || temporary)
1994 client->
exportsize = laststartoff + lastsize;
2000 err(
"Size of exported file is too big\n");
2006 msg(LOG_INFO,
"Size of exported file/device is %llu", (
unsigned long long)client->
exportsize);
2008 msg(LOG_INFO,
"Total number of files: %d", i);
2015 err(
"Failed to allocate string for diff file name");
2019 msg(LOG_INFO,
"About to create map and diff file %s", client->
difffilename) ;
2021 if (client->
difffile<0)
err(
"Could not create diff file (%m)") ;
2022 if ((client->
difmap=calloc(client->
exportsize/DIFFPAGESIZE,
sizeof(u32)))==NULL)
2023 err(
"Could not allocate memory") ;
2036 int do_run(gchar* command, gchar* file) {
2040 if(command && *command) {
2041 cmd = g_strdup_printf(command, file);
2061 S_IRUSR | S_IWUSR)))
2062 g_warning(
"Could not open transaction log %s",
2100 struct sockaddr_storage addrin;
2101 struct sockaddr_storage netaddr;
2102 struct sockaddr_in *netaddr4 = NULL;
2103 struct sockaddr_in6 *netaddr6 = NULL;
2104 socklen_t addrinlen =
sizeof( addrin );
2105 struct addrinfo hints;
2106 struct addrinfo *ai = NULL;
2107 char peername[NI_MAXHOST];
2108 char netname[NI_MAXHOST];
2114 if (getpeername(net, (
struct sockaddr *) &addrin, &addrinlen) < 0) {
2115 msg(LOG_INFO,
"getpeername failed: %m");
2119 if((e = getnameinfo((
struct sockaddr *)&addrin, addrinlen,
2120 peername,
sizeof (peername), NULL, 0, NI_NUMERICHOST))) {
2121 msg(LOG_INFO,
"getnameinfo failed: %s", gai_strerror(e));
2125 memset(&hints,
'\0',
sizeof (hints));
2126 hints.ai_flags = AI_ADDRCONFIG;
2127 e = getaddrinfo(peername, NULL, &hints, &ai);
2130 msg(LOG_INFO,
"getaddrinfo failed: %s", gai_strerror(e));
2137 msg(LOG_DEBUG,
"virtualization is off");
2141 msg(LOG_DEBUG,
"virtstyle iphash");
2142 for(i=0;i<strlen(peername);i++) {
2143 if(peername[i]==
'.') {
2148 msg(LOG_DEBUG,
"virststyle ipliteral");
2153 memcpy(&netaddr, &addrin, addrinlen);
2154 if(ai->ai_family == AF_INET) {
2155 netaddr4 = (
struct sockaddr_in *)&netaddr;
2159 getnameinfo((
struct sockaddr *) netaddr4, addrinlen,
2160 netname,
sizeof (netname), NULL, 0, NI_NUMERICHOST);
2161 tmp=g_strdup_printf(
"%s/%s", netname, peername);
2162 }
else if(ai->ai_family == AF_INET6) {
2163 netaddr6 = (
struct sockaddr_in6 *)&netaddr;
2168 ((netaddr6->sin6_addr).s6_addr[i])=0;
2172 (netaddr6->sin6_addr).s6_addr[i]>>=shift;
2173 (netaddr6->sin6_addr).s6_addr[i]<<=shift;
2175 getnameinfo((
struct sockaddr *)netaddr6, addrinlen,
2176 netname,
sizeof(netname), NULL, 0, NI_NUMERICHOST);
2177 tmp=g_strdup_printf(
"%s/%s", netname, peername);
2187 msg(LOG_INFO,
"connect from %s, assigned file is %s",
2208 sigemptyset(&newset);
2209 sigaddset(&newset, SIGCHLD);
2210 sigaddset(&newset, SIGTERM);
2211 sigprocmask(SIG_BLOCK, &newset, &oldset);
2214 msg(LOG_ERR,
"Could not fork (%s)", strerror(errno));
2220 pidp = g_malloc(
sizeof(pid_t));
2222 g_hash_table_insert(
children, pidp, pidp);
2226 signal(SIGCHLD, SIG_DFL);
2227 signal(SIGTERM, SIG_DFL);
2228 signal(SIGHUP, SIG_DFL);
2230 sigprocmask(SIG_SETMASK, &oldset, NULL);
2237 struct sockaddr_storage addrin;
2238 socklen_t addrinlen =
sizeof(addrin);
2241 net = accept(sock, (
struct sockaddr *) &addrin, &addrinlen);
2243 err_nonfatal(
"Failed to accept socket connection: %m");
2266 msg(LOG_INFO,
"Spawned a child process");
2268 msg(LOG_ERR,
"Failed to spawn a child process");
2275 client =
negotiate(net, NULL, servers, NEG_INIT | NEG_MODERN);
2277 msg(LOG_ERR,
"Modern initial negotiation failed");
2283 msg(LOG_ERR,
"Max connections (%d) reached",
2288 sock_flags_old = fcntl(net, F_GETFL, 0);
2289 if (sock_flags_old == -1) {
2290 msg(LOG_ERR,
"Failed to get socket flags");
2294 sock_flags_new = sock_flags_old & ~O_NONBLOCK;
2295 if (sock_flags_new != sock_flags_old &&
2296 fcntl(net, F_SETFL, sock_flags_new) == -1) {
2297 msg(LOG_ERR,
"Failed to set socket to blocking mode");
2302 msg(LOG_ERR,
"Failed to set peername");
2307 msg(LOG_INFO,
"Client '%s' is not authorized to access",
2329 for (i = 0; i < servers->len; i++) {
2330 const SERVER *
const server = &g_array_index(servers,
SERVER, i);
2340 g_array_free(servers, FALSE);
2343 msg(LOG_INFO,
"Starting to serve");
2364 msg(LOG_INFO,
"Max connections reached");
2365 goto handle_connection_out;
2367 if((sock_flags_old = fcntl(net, F_GETFL, 0)) == -1) {
2368 err(
"fcntl F_GETFL");
2370 sock_flags_new = sock_flags_old & ~O_NONBLOCK;
2371 if (sock_flags_new != sock_flags_old &&
2372 fcntl(net, F_SETFL, sock_flags_new) == -1) {
2373 err(
"fcntl F_SETFL ~O_NONBLOCK");
2376 client = g_new0(
CLIENT, 1);
2383 goto handle_connection_out;
2386 msg(LOG_INFO,
"Unauthorized client");
2387 goto handle_connection_out;
2389 msg(LOG_INFO,
"Authorized client");
2397 sigemptyset(&newset);
2398 sigaddset(&newset, SIGCHLD);
2399 sigaddset(&newset, SIGTERM);
2400 sigprocmask(SIG_BLOCK, &newset, &oldset);
2401 if ((pid = fork()) < 0) {
2402 msg(LOG_INFO,
"Could not fork (%s)", strerror(errno));
2403 sigprocmask(SIG_SETMASK, &oldset, NULL);
2404 goto handle_connection_out;
2409 pidp = g_malloc(
sizeof(pid_t));
2411 g_hash_table_insert(
children, pidp, pidp);
2412 sigprocmask(SIG_SETMASK, &oldset, NULL);
2413 goto handle_connection_out;
2418 signal(SIGCHLD, SIG_DFL);
2419 signal(SIGTERM, SIG_DFL);
2420 signal(SIGHUP, SIG_DFL);
2421 sigemptyset(&oldset);
2422 sigprocmask(SIG_SETMASK, &oldset, NULL);
2426 for(i=0;i<servers->len;i++) {
2427 serve=&g_array_index(servers,
SERVER, i);
2436 g_array_free(servers, FALSE);
2443 msg(LOG_INFO,
"Starting to serve");
2447 handle_connection_out:
2462 const GArray *
const servers) {
2465 for (i = 0; i < servers->len; ++i) {
2466 const SERVER server = g_array_index(servers,
SERVER, i);
2468 if (strcmp(servename, server.
servename) == 0)
2489 GArray *new_servers;
2490 const int old_len = servers->len;
2498 for (i = 0; i < new_servers->len; ++i) {
2499 SERVER new_server = g_array_index(new_servers,
SERVER, i);
2511 retval = servers->len - old_len;
2513 g_array_free(new_servers, TRUE);
2522 struct sockaddr_storage addrin;
2523 socklen_t addrinlen=
sizeof(addrin);
2529 sigset_t blocking_mask;
2530 sigset_t original_mask;
2541 for(i=0;i<servers->len;i++) {
2542 if((sock=(g_array_index(servers,
SERVER, i)).socket) >= 0) {
2543 FD_SET(sock, &mset);
2544 max=sock>max?sock:max;
2549 FD_SET(sock, &mset);
2550 max=sock>max?sock:max;
2556 if (sigemptyset(&blocking_mask) == -1)
2557 err(
"failed to initialize blocking_mask: %m");
2559 if (sigaddset(&blocking_mask, SIGCHLD) == -1)
2560 err(
"failed to add SIGCHLD to blocking_mask: %m");
2562 if (sigaddset(&blocking_mask, SIGHUP) == -1)
2563 err(
"failed to add SIGHUP to blocking_mask: %m");
2565 if (sigaddset(&blocking_mask, SIGTERM) == -1)
2566 err(
"failed to add SIGTERM to blocking_mask: %m");
2568 if (sigprocmask(SIG_BLOCK, &blocking_mask, &original_mask) == -1)
2569 err(
"failed to block signals: %m");
2588 while ((pid=waitpid(-1, &status, WNOHANG)) > 0) {
2589 if (WIFEXITED(status)) {
2590 msg(LOG_INFO,
"Child exited with %d", WEXITSTATUS(status));
2592 i = g_hash_table_lookup(
children, &pid);
2594 msg(LOG_INFO,
"SIGCHLD received for an unknown child with PID %ld", (
long)pid);
2596 DEBUG(
"Removing %d from the list of children", pid);
2597 g_hash_table_remove(
children, &pid);
2610 GError *gerror = NULL;
2612 msg(LOG_INFO,
"reconfiguration request received");
2618 msg(LOG_ERR,
"failed to append new servers: %s",
2621 for (i = servers->len - n; i < servers->len; ++i) {
2622 const SERVER server = g_array_index(servers,
2625 if (server.
socket >= 0) {
2626 FD_SET(server.
socket, &mset);
2630 msg(LOG_INFO,
"reconfigured new server: %s",
2635 memcpy(&rset, &mset,
sizeof(fd_set));
2636 if (pselect(max + 1, &rset, NULL, NULL, NULL, &original_mask) > 0) {
2641 if(!FD_ISSET(sock, &rset)) {
2647 for(i=0; i < servers->len; i++) {
2651 serve=&(g_array_index(servers,
SERVER, i));
2655 if(FD_ISSET(serve->
socket, &rset)) {
2656 if ((net=accept(serve->
socket, (
struct sockaddr *) &addrin, &addrinlen)) < 0) {
2679 int dosockopts(
const int socket, GError **
const gerror) {
2688 if (setsockopt(socket,SOL_SOCKET,SO_REUSEADDR,&yes,
sizeof(
int)) == -1) {
2690 "failed to set socket option SO_REUSEADDR: %s",
2696 if (setsockopt(socket,SOL_SOCKET,SO_LINGER,&l,
sizeof(l)) == -1) {
2698 "failed to set socket option SO_LINGER: %s",
2702 if (setsockopt(socket,SOL_SOCKET,SO_KEEPALIVE,&yes,
sizeof(
int)) == -1) {
2704 "failed to set socket option SO_KEEPALIVE: %s",
2718 struct addrinfo hints;
2719 struct addrinfo *ai = NULL;
2743 memset(&hints,
'\0',
sizeof(hints));
2745 hints.ai_socktype = SOCK_STREAM;
2748 port = g_strdup_printf(
"%d", serve->
port);
2751 "failed to open an export socket: "
2752 "failed to convert a port number to a string: %s",
2757 e = getaddrinfo(serve->
listenaddr,port,&hints,&ai);
2763 "failed to open an export socket: "
2764 "failed to get address info: %s",
2774 if (ai->ai_family == AF_INET)
2775 ai->ai_family = AF_INET_SDP;
2776 else (ai->ai_family == AF_INET6)
2777 ai->ai_family = AF_INET6_SDP;
2780 if ((serve->
socket = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
2782 "failed to open an export socket: "
2783 "failed to create a socket: %s",
2789 g_prefix_error(gerror,
"failed to open an export socket: ");
2793 DEBUG(
"Waiting for connections... bind, ");
2794 e = bind(serve->
socket, ai->ai_addr, ai->ai_addrlen);
2795 if (e != 0 && errno != EADDRINUSE) {
2797 "failed to open an export socket: "
2798 "failed to bind an address to a socket: %s",
2803 if (listen(serve->
socket, 1) < 0) {
2805 "failed to open an export socket: "
2806 "failed to start listening on a socket: %s",
2814 if (retval == -1 && serve->
socket >= 0) {
2823 int open_modern(
const gchar *
const addr,
const gchar *
const port,
2824 GError **
const gerror) {
2825 struct addrinfo hints;
2826 struct addrinfo* ai = NULL;
2827 struct addrinfo* ai_bak;
2834 memset(&hints,
'\0',
sizeof(hints));
2835 hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
2836 hints.ai_socktype = SOCK_STREAM;
2837 hints.ai_family = AF_UNSPEC;
2838 hints.ai_protocol = IPPROTO_TCP;
2843 "failed to open a modern socket: "
2844 "failed to get address info: %s",
2852 if((sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol))<0) {
2854 "failed to open a modern socket: "
2855 "failed to create a socket: %s",
2861 g_prefix_error(gerror,
"failed to open a modern socket: ");
2865 if(bind(sock, ai->ai_addr, ai->ai_addrlen)) {
2882 "failed to open a modern socket: "
2883 "failed to bind an address to a socket: %s",
2888 if(listen(sock, 10) <0) {
2890 "failed to open a modern socket: "
2891 "failed to start listening on a socket: %s",
2903 if (retval == -1 && sock >= 0) {
2907 freeaddrinfo(ai_bak);
2915 void setup_servers(GArray *
const servers,
const gchar *
const modernaddr,
2916 const gchar *
const modernport) {
2918 struct sigaction sa;
2921 for(i=0;i<servers->len;i++) {
2922 GError *gerror = NULL;
2928 msg(LOG_ERR,
"failed to setup servers: %s",
2930 g_clear_error(&gerror);
2936 GError *gerror = NULL;
2937 if (
open_modern(modernaddr, modernport, &gerror) == -1) {
2938 msg(LOG_ERR,
"failed to setup servers: %s",
2940 g_clear_error(&gerror);
2947 sigemptyset(&sa.sa_mask);
2948 sigaddset(&sa.sa_mask, SIGTERM);
2949 sa.sa_flags = SA_RESTART;
2950 if(sigaction(SIGCHLD, &sa, NULL) == -1)
2951 err(
"sigaction: %m");
2954 sigemptyset(&sa.sa_mask);
2955 sigaddset(&sa.sa_mask, SIGCHLD);
2956 sa.sa_flags = SA_RESTART;
2957 if(sigaction(SIGTERM, &sa, NULL) == -1)
2958 err(
"sigaction: %m");
2961 sigemptyset(&sa.sa_mask);
2962 sa.sa_flags = SA_RESTART;
2963 if(sigaction(SIGHUP, &sa, NULL) == -1)
2964 err(
"sigaction: %m");
2974 #if !defined(NODAEMON)
2978 if(serve && !(serve->
port)) {
2986 strncpy(
pidftemplate,
"/var/run/nbd-server.%d.pid", 255);
2994 fprintf(pidf,
"%d\n", (
int)getpid());
2998 fprintf(stderr,
"Not fatal; continuing");
3002 #define daemonize(serve)
3013 void dousers(
const gchar *
const username,
const gchar *
const groupname) {
3018 gr = getgrnam(groupname);
3020 str = g_strdup_printf(
"Invalid group name: %s", groupname);
3023 if(setgid(gr->gr_gid)<0) {
3024 err(
"Could not set GID: %m");
3028 pw = getpwnam(username);
3030 str = g_strdup_printf(
"Invalid user name: %s", username);
3033 if(setuid(pw->pw_uid)<0) {
3034 err(
"Could not set UID: %m");
3041 GLogLevelFlags log_level,
3042 const gchar *message,
3045 int level=LOG_DEBUG;
3049 case G_LOG_FLAG_FATAL:
3050 case G_LOG_LEVEL_CRITICAL:
3051 case G_LOG_LEVEL_ERROR:
3054 case G_LOG_LEVEL_WARNING:
3057 case G_LOG_LEVEL_MESSAGE:
3058 case G_LOG_LEVEL_INFO:
3061 case G_LOG_LEVEL_DEBUG:
3067 syslog(level,
"%s", message);
3074 int main(
int argc,
char *argv[]) {
3083 fprintf(stderr,
"Bad size of structure. Alignment problems?\n");
3084 exit(EXIT_FAILURE) ;
3089 modernsocks = g_array_new(FALSE, FALSE,
sizeof(
int));
3106 if (!(serve->
port)) {
3115 open(
"/dev/null", O_WRONLY);
3116 open(
"/dev/null", O_WRONLY);
3119 client=g_malloc(
sizeof(
CLIENT));
3130 if(!servers || !servers->len) {
3131 if(err && !(err->domain ==
NBDS_ERR
3133 g_warning(
"Could not parse config file: %s",
3134 err ? err->message :
"Unknown error");
3138 g_warning(
"Specifying an export on the command line is deprecated.");
3139 g_warning(
"Please use a configuration file instead.");
3142 if((!serve) && (!servers||!servers->len)) {
3144 g_message(
"No configured exports; quitting.");
int expread(off_t a, char *buf, size_t len, CLIENT *client)
Read an amount of bytes at a given offset from the right file.
int get_filepos(GArray *export, off_t a, int *fhandle, off_t *foffset, size_t *maxbytes)
Get the file handle and offset, given an export offset.
int setup_serve(SERVER *const serve, GError **const gerror)
Connect a server's socket.
static void consume(int f, void *buf, size_t len, size_t bufsiz)
Consume data from an FD that we don't want.
This parameter is a string.
gchar * servename
name of the export as selected by nbd-client
void glib_message_syslog_redirect(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data)
GArray * export
array of FILE_INFO of exported files; array size is always 1 unless we're doing the multiple file opt...
int append_serve(const SERVER *const s, GArray *const a)
append new server to array
Variables associated with a server.
void destroy_pid_t(gpointer data)
Destroy a pid_t*.
Failed to set SO_KEEPALIVE to a socket.
Replacing all dots in an ip address by a / before doing the same as in IPLIT.
void setup_servers(GArray *const servers, const gchar *const modernaddr, const gchar *const modernport)
Connect our servers.
void usage()
Print out a message about how to use nbd-server.
#define NBD_FLAG_SEND_FUA
#define SEND(net, reply)
sending macro.
Underlying system call or library error.
int rawexpread_fully(off_t a, char *buf, size_t len, CLIENT *client)
Call rawexpread repeatedly until all data has been read.
int glob_flags
global flags
gchar * config_file_pos
Where our config file actually is.
#define SYSCONFDIR
Default position of the config file.
#define F_COPYONWRITE
flag to tell us a file is exported using copyonwrite
A value is not supported in this build.
SERVER * server
The server this client is getting data from.
#define F_FLUSH
Whether server wants FLUSH to be sent by the client.
int copyonwrite_prepare(CLIENT *client)
static gchar * transactionlog
gchar * postrun
command that will be ran after the client disconnects
int clientfeats
Features supported by this client.
int rawexpwrite_fully(off_t a, char *buf, size_t len, CLIENT *client, int fua)
Call rawexpwrite repeatedly until all data has been written.
Failed to bind an address to socket.
void dump_section(SERVER *serve, gchar *section_header)
u32 difffilelen
number of pages in difffile
static int socket_accept(const int sock)
This parameter is a boolean.
#define F_SDP
flag to tell us the export should be done using the Socket Direct Protocol for RDMA ...
A value is syntactically invalid.
void serveloop(GArray *servers)
Loop through the available servers, and serve them.
static void handle_modern_connection(GArray *const servers, const int sock)
int fhandle
file descriptor
int dosockopts(const int socket, GError **const gerror)
Set server socket options.
static int get_index_by_servename(const gchar *const servename, const GArray *const servers)
Return the index of the server whose servename matches the given name.
gchar * modernport
port of the modern socket
gchar * user
user we run the server as
#define NBD_REP_ERR_INVALID
void setmysockopt(int sock)
off_t startoff
starting offset of this file
Failed to start listening on a socket.
Failed to create a socket.
A (required) key is missing.
int authorized_client(CLIENT *opts)
Check whether a client is allowed to connect.
#define F_TEMPORARY
Whether the backing file is temporary and should be created then unlinked.
char pidftemplate[256]
template to be used for the filename of the PID file
#define NBD_REP_ERR_POLICY
static pid_t spawn_child()
static volatile sig_atomic_t is_sigchld_caught
Flag set by SIGCHLD handler to mark a child exit.
static int append_new_servers(GArray *const servers, GError **const gerror)
Parse configuration files and add servers to the array if they don't already exist there...
int expflush(CLIENT *client)
Flush data to a client.
Literal IP address as part of the filename.
int flags
flags associated with this exported file
This parameter is an integer.
#define F_LIST
Allow clients to list the exports on a server.
void serveconnection(CLIENT *client)
Serve a connection.
CLIENT * negotiate(int net, CLIENT *client, GArray *servers, int phase)
Do the initial negotiation.
GArray * modernsocks
Sockets for the modern handler.
A directory requested does not exist.
int exptrim(struct nbd_request *req, CLIENT *client)
void killchild(gpointer key, gpointer value, gpointer user_data)
Kill a child.
gchar * exportname
(unprocessed) filename of the file we're exporting
int net
The actual client socket.
#define NBD_FLAG_SEND_FLUSH
static void sigchld_handler(const int s G_GNUC_UNUSED)
Handle SIGCHLD by setting atomically a flag which will be evaluated in the main loop of the root serv...
void err_nonfatal(const char *s)
#define F_AUTOREADONLY
flag to tell us a file is set to autoreadonly
int do_run(gchar *command, gchar *file)
Run a command.
static volatile sig_atomic_t is_sigterm_caught
Flag set by SIGTERM handler to mark a exit request.
Every subnet in its own directory.
#define NBD_OPT_EXPORT_NAME
int open_modern(const gchar *const addr, const gchar *const port, GError **const gerror)
unsigned int port
port we're exporting this file at
gboolean required
Whether this is a required (as opposed to optional) parameter.
#define F_TRIM
Whether server wants TRIM (discard) to be sent by the client.
gchar * transactionlog
filename for transaction log
A config file was specified that does not define any exports.
gchar * listenaddr
The IP address we're listening on.
#define F_OLDSTYLE
Global flags:
static void send_reply(uint32_t opt, int net, uint32_t reply_type, size_t datasize, void *data)
off_t expected_size
size of the exported file as it was told to us through configuration
The reserved port was specified for an old-style export.
static const char * getcommandname(uint64_t command)
Translate a command name into human readable form.
int socket
The socket of this server.
Error occurred during readdir()
gchar * modernaddr
address of the modern socket
bool logged_oversized
whether we logged oversized requests already
int difffile
filedescriptor of copyonwrite file.
SERVER * cmdline(int argc, char *argv[])
Parse the command line.
#define OFFT_MAX
The highest value a variable of type off_t can reach.
int set_peername(int net, CLIENT *client)
Find the name of the file we have to serve.
#define NBD_FLAG_HAS_FLAGS
struct nbd_reply __attribute__
static void readit(int f, void *buf, size_t len)
Read data from a file descriptor into a buffer.
char pidfname[256]
name of our PID file
Variables associated with a client socket.
Failed to set SO_REUSEADDR to a socket.
The configuration file is not found.
static void sighup_handler(const int s G_GNUC_UNUSED)
Handle SIGHUP by setting atomically a flag which will be evaluated in the main loop of the root serve...
u32 * difmap
see comment on the global difmap for this one
NBDS_ERRS
NBD server error codes.
static void handle_list(uint32_t opt, int net, GArray *servers, uint32_t cflags)
void daemonize(SERVER *serve)
Go daemon (unless we specified at compile time that we didn't want this)
static CLIENT * handle_export_name(uint32_t opt, int net, GArray *servers, uint32_t cflags)
#define F_SPARSE
flag to tell us copyronwrite should use a sparse file
PARAM_TYPE
Type of configuration file values.
The (required) group "generic" is missing.
ssize_t rawexpread(off_t a, char *buf, size_t len, CLIENT *client)
Read an amount of bytes at a given offset from the right file.
#define NBDS_ERR
Error domain common for all NBD server errors.
GArray * do_cfile_dir(gchar *dir, GError **e)
Parse config file snippets in a directory.
int transactionlogfd
fd for transaction log
void myseek(int handle, off_t a)
seek to a position in a file, with error handling.
void err(const char *s) G_GNUC_NORETURN
off_t exportsize
size of the file we're exporting
int max_connections
maximum number of opened connections
VIRT_STYLE virtstyle
The style of virtualization, if any.
#define NBD_FLAG_READ_ONLY
Failed to get address info.
static volatile sig_atomic_t is_sighup_caught
Flag set by SIGHUP handler to mark a reconfiguration request.
VIRT_STYLE
Types of virtuatlization.
ssize_t rawexpwrite(off_t a, char *buf, size_t len, CLIENT *client, int fua)
Write an amount of bytes at a given offset to the right file.
#define BUFSIZE
Size of buffer that can hold requests.
#define LINELEN
Size of static buffer used to read the authorization file (yuck)
SERVER * dup_serve(const SERVER *const s)
duplicate server
int socket_family
family of the socket
gchar * prerun
command to be ran after connecting a client, but before starting to serve
static void handle_connection(GArray *servers, int net, SERVER *serve, CLIENT *client)
#define NBD_FLAG_SEND_TRIM
This parameter is an integer.
#define NBD_CMD_MASK_COMMAND
gchar * group
group we run running as
#define F_SYNC
Whether to fsync() after a write.
char * authname
filename of the authorization file
int main(int argc, char *argv[])
Main entry point...
static void writeit(int f, void *buf, size_t len)
Write data from a buffer into a filedescriptor.
off_t size_autodetect(int fhandle)
Detect the size of a file.
gboolean modern
client was negotiated using modern negotiation protocol
#define F_FUA
Whether server wants FUA to be sent by the client.
void setupexport(CLIENT *client)
Set up client export array, which is an array of FILE_INFO.
#define NBD_REQUEST_MAGIC
char * difffilename
filename of the copy-on-write file, if any
void dousers(const gchar *const username, const gchar *const groupname)
Set up user-ID and/or group-ID.
Failed to set SO_LINGER to a socket.
char default_authname[]
default name of allow file
#define DIFFPAGESIZE
diff file uses those chunks
#define msg(prio,...)
Logging macros, now nothing goes to syslog unless you say ISSERVER.
uint8_t cidrlen
The length of the mask when we use CIDR-style virtualization.
static void sigterm_handler(const int s G_GNUC_UNUSED)
Handle SIGTERM by setting atomically a flag which will be evaluated in the main loop of the root serv...
#define NBD_FLAG_FIXED_NEWSTYLE
#define F_READONLY
Per-export flags:
#define NBD_FLAG_ROTATIONAL
char * exportname
(processed) filename of the file we're exporting
int expwrite(off_t a, char *buf, size_t len, CLIENT *client, int fua)
Write an amount of bytes at a given offset to the right file.
#define ERROR(client, reply, errcode)
error macro.
Configuration file values of the "generic" section.
Configuration file values.
#define F_ROTATIONAL
Whether server wants the client to implement the elevator algorithm.
GArray * parse_cfile(gchar *f, struct generic_conf *genconf, GError **e)
Parse the config file.
int mainloop(CLIENT *client)
Serve a file to a single client.
#define F_MULTIFILE
flag to tell us a file is exported using -m
#define NBD_REP_ERR_UNSUP