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)
257 int transactionlogfd;
308 return "NBD_CMD_READ";
310 return "NBD_CMD_WRITE";
312 return "NBD_CMD_DISC";
314 return "NBD_CMD_FLUSH";
316 return "NBD_CMD_TRIM";
330 const char *ERRMSG=
"Invalid entry '%s' in authfile '%s', so, refusing all connections.";
335 struct in_addr client;
336 struct in_addr cltemp;
340 msg(LOG_INFO,
"Can't open authorization file %s (%s).",
346 while (fgets(line,
LINELEN,f)!=NULL) {
347 if((tmp=strchr(line,
'/'))) {
348 if(strlen(line)<=tmp-line) {
353 if(!inet_aton(line,&addr)) {
357 len=strtol(tmp, NULL, 0);
358 addr.s_addr>>=32-
len;
359 addr.s_addr<<=32-
len;
360 memcpy(&cltemp,&client,
sizeof(client));
361 cltemp.s_addr>>=32-
len;
362 cltemp.s_addr<<=32-
len;
363 if(addr.s_addr == cltemp.s_addr) {
383 static inline void readit(
int f,
void *buf,
size_t len) {
387 if ((res = read(f, buf, len)) <= 0) {
388 if(errno != EAGAIN) {
389 err(
"Read failed: %m");
406 static inline void consume(
int f,
void * buf,
size_t len,
size_t bufsiz) {
409 curlen = (len>bufsiz)?bufsiz:len;
423 static inline void writeit(
int f,
void *buf,
size_t len) {
427 if ((res = write(f, buf, len)) <= 0)
428 err(
"Send failed: %m");
439 printf(
"This is nbd-server version " VERSION "\n");
440 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"
441 "\t-r|--read-only\t\tread only\n"
442 "\t-m|--multi-file\t\tmultiple file\n"
443 "\t-c|--copy-on-write\tcopy on write\n"
444 "\t-C|--config-file\tspecify an alternate configuration file\n"
445 "\t-l|--authorize-file\tfile with list of hosts that are allowed to\n\t\t\t\tconnect.\n"
446 "\t-p|--pid-file\t\tspecify a filename to write our PID to\n"
447 "\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"
448 "\t-M|--max-connections\tspecify the maximum number of opened connections\n\n"
449 "\tif port is set to 0, stdin is used (for running from inetd).\n"
450 "\tif file_to_export contains '%%s', it is substituted with the IP\n"
451 "\t\taddress of the machine trying to connect\n"
452 "\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");
453 printf(
"Using configuration file %s\n",
CFILE);
458 printf(
"[%s]\n", section_header);
460 printf(
"\tlistenaddr = %s\n", serve->
listenaddr);
461 printf(
"\tport = %d\n", serve->
port);
463 printf(
"\treadonly = true\n");
466 printf(
"\tmultifile = true\n");
469 printf(
"\tcopyonwrite = true\n");
472 printf(
"\tfilesize = %lld\n", (
long long int)serve->
expected_size);
475 printf(
"\tauthfile = %s\n", serve->
authname);
490 struct option long_options[] = {
491 {
"read-only", no_argument, NULL,
'r'},
492 {
"multi-file", no_argument, NULL,
'm'},
493 {
"copy-on-write", no_argument, NULL,
'c'},
494 {
"dont-fork", no_argument, NULL,
'd'},
495 {
"authorize-file", required_argument, NULL,
'l'},
496 {
"config-file", required_argument, NULL,
'C'},
497 {
"pid-file", required_argument, NULL,
'p'},
498 {
"output-config", required_argument, NULL,
'o'},
499 {
"max-connection", required_argument, NULL,
'M'},
506 gboolean do_output=FALSE;
507 gchar* section_header=
"";
516 while((c=getopt_long(argc, argv,
"-C:cdl:mo:rp:M:", long_options, &i))>=0) {
520 switch(nonspecial++) {
522 if(strchr(optarg,
':') == strrchr(optarg,
':')) {
523 addr_port=g_strsplit(optarg,
":", 2);
528 g_strfreev(addr_port);
529 addr_port=g_strsplit(optarg,
"@", 2);
532 addr_port=g_strsplit(optarg,
"@", 2);
536 serve->
port=strtol(addr_port[1], NULL, 0);
540 serve->
port=strtol(addr_port[0], NULL, 0);
542 g_strfreev(addr_port);
547 fprintf(stderr,
"E: The to be exported file needs to be an absolute filename!\n");
552 last=strlen(optarg)-1;
554 if (suffix ==
'k' || suffix ==
'K' ||
555 suffix ==
'm' || suffix ==
'M')
557 es = (off_t)atoll(optarg);
577 section_header = g_strdup(optarg);
615 g_critical(
"Need a complete configuration on the command line to output a config file section!");
626 #define NBDS_ERR g_quark_from_static_string("server-error-quark")
709 struct addrinfo hints;
710 struct addrinfo *ai = NULL;
711 struct addrinfo *rp = NULL;
712 char host[NI_MAXHOST];
719 port = g_strdup_printf(
"%d", s->
port);
721 memset(&hints,
'\0',
sizeof(hints));
722 hints.ai_family = AF_UNSPEC;
723 hints.ai_socktype = SOCK_STREAM;
724 hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE;
725 hints.ai_protocol = IPPROTO_TCP;
727 e = getaddrinfo(s->
listenaddr, port, &hints, &ai);
733 for (rp = ai; rp != NULL; rp = rp->ai_next) {
734 e = getnameinfo(rp->ai_addr, rp->ai_addrlen, host,
sizeof(host), NULL, 0, NI_NUMERICHOST);
737 fprintf(stderr,
"getnameinfo: %s\n", gai_strerror(e));
746 g_array_append_val(a, *ns);
754 fprintf(stderr,
"getaddrinfo failed on listen host/address: %s (%s)\n", s->
listenaddr ? s->
listenaddr :
"any", gai_strerror(e));
773 DIR* dirh = opendir(dir);
776 GArray* retval = NULL;
785 while((de = readdir(dirh))) {
786 int saved_errno=errno;
787 fname = g_build_filename(dir, de->d_name, NULL);
793 if(stat(fname, &stbuf)) {
797 if (!S_ISREG(stbuf.st_mode)) {
802 if(strcmp((de->d_name + strlen(de->d_name) - 5),
".conf")) {
811 retval = g_array_new(FALSE, TRUE,
sizeof(
SERVER));
812 retval = g_array_append_vals(retval, tmp->data, tmp->len);
813 g_array_free(tmp, TRUE);
824 g_array_free(retval, TRUE);
849 const char* DEFAULT_ERROR =
"Could not parse %s in group %s: %s";
850 const char* MISSING_REQUIRED_ERROR =
"Could not find required value %s in group %s: %s";
853 gchar *virtstyle=NULL;
877 const int lp_size=
sizeof(lp)/
sizeof(
PARAM);
889 int p_size=
sizeof(gp)/
sizeof(
PARAM);
892 const char *err_msg=NULL;
909 memcpy(&genconftmp, genconf,
sizeof(
struct generic_conf));
912 cfile = g_key_file_new();
913 retval = g_array_new(FALSE, TRUE,
sizeof(
SERVER));
914 if(!g_key_file_load_from_file(cfile, f, G_KEY_FILE_KEEP_COMMENTS |
915 G_KEY_FILE_KEEP_TRANSLATIONS, &err)) {
918 g_key_file_free(cfile);
921 startgroup = g_key_file_get_start_group(cfile);
922 if((!startgroup || strcmp(startgroup,
"generic")) && genconf) {
924 g_key_file_free(cfile);
927 groups = g_key_file_get_groups(cfile, NULL);
928 for(i=0;groups[i];i++) {
929 memset(&s,
'\0',
sizeof(
SERVER));
933 if(i==1 || !genconf) {
940 for(j=0;j<p_size;j++) {
941 assert(p[j].target != NULL);
942 assert(p[j].ptype==PARAM_INT||p[j].ptype==PARAM_STRING||p[j].ptype==PARAM_BOOL||p[j].ptype==
PARAM_INT64);
945 ival = g_key_file_get_integer(cfile,
950 *((gint*)p[j].target) = ival;
954 i64val = g_key_file_get_int64(cfile,
959 *((gint64*)p[j].target) = i64val;
963 sval = g_key_file_get_string(cfile,
968 *((gchar**)p[j].target) = sval;
972 bval = g_key_file_get_boolean(cfile,
974 p[j].paramname, &err);
977 *((gint*)p[j].target) |= p[j].flagval;
979 *((gint*)p[j].target) &= ~(p[j].flagval);
985 if(err->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND) {
991 err_msg = MISSING_REQUIRED_ERROR;
994 err_msg = DEFAULT_ERROR;
997 g_array_free(retval, TRUE);
999 g_key_file_free(cfile);
1004 if(!strncmp(virtstyle,
"none", 4)) {
1006 }
else if(!strncmp(virtstyle,
"ipliteral", 9)) {
1008 }
else if(!strncmp(virtstyle,
"iphash", 6)) {
1010 }
else if(!strncmp(virtstyle,
"cidrhash", 8)) {
1012 if(strlen(virtstyle)<10) {
1014 g_array_free(retval, TRUE);
1015 g_key_file_free(cfile);
1018 s.
cidrlen=strtol(virtstyle+8, NULL, 0);
1021 g_array_free(retval, TRUE);
1022 g_key_file_free(cfile);
1029 g_warning(
"A port was specified, but oldstyle exports were not requested. This may not do what you expect.");
1030 g_warning(
"Please read 'man 5 nbd-server' and search for oldstyle for more info");
1035 if(i>0 || !genconf) {
1044 g_array_free(retval, TRUE);
1045 g_key_file_free(cfile);
1050 g_key_file_free(cfile);
1054 retval = g_array_append_vals(retval, extra->data, extra->len);
1056 g_array_free(extra, TRUE);
1059 g_array_free(retval, TRUE);
1064 if(i==1 && genconf) {
1071 memcpy(genconf, &genconftmp,
sizeof(
struct generic_conf));
1087 while((pid=waitpid(-1, &status, WNOHANG)) > 0) {
1088 if(WIFEXITED(status)) {
1089 msg(LOG_INFO,
"Child exited with %d", WEXITSTATUS(status));
1091 i=g_hash_table_lookup(
children, &pid);
1093 msg(LOG_INFO,
"SIGCHLD received for an unknown child with PID %ld", (
long)pid);
1095 DEBUG(
"Removing %d from the list of children", pid);
1096 g_hash_table_remove(
children, &pid);
1109 void killchild(gpointer key, gpointer value, gpointer user_data) {
1112 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",
2209 msg(LOG_INFO,
"Max connections reached");
2210 goto handle_connection_out;
2212 if((sock_flags_old = fcntl(net, F_GETFL, 0)) == -1) {
2213 err(
"fcntl F_GETFL");
2215 sock_flags_new = sock_flags_old & ~O_NONBLOCK;
2216 if (sock_flags_new != sock_flags_old &&
2217 fcntl(net, F_SETFL, sock_flags_new) == -1) {
2218 err(
"fcntl F_SETFL ~O_NONBLOCK");
2221 client = g_new0(
CLIENT, 1);
2228 goto handle_connection_out;
2231 msg(LOG_INFO,
"Unauthorized client");
2232 goto handle_connection_out;
2234 msg(LOG_INFO,
"Authorized client");
2242 sigemptyset(&newset);
2243 sigaddset(&newset, SIGCHLD);
2244 sigaddset(&newset, SIGTERM);
2245 sigprocmask(SIG_BLOCK, &newset, &oldset);
2246 if ((pid = fork()) < 0) {
2247 msg(LOG_INFO,
"Could not fork (%s)", strerror(errno));
2248 sigprocmask(SIG_SETMASK, &oldset, NULL);
2249 goto handle_connection_out;
2254 pidp = g_malloc(
sizeof(pid_t));
2256 g_hash_table_insert(
children, pidp, pidp);
2257 sigprocmask(SIG_SETMASK, &oldset, NULL);
2258 goto handle_connection_out;
2261 signal(SIGCHLD, SIG_DFL);
2262 signal(SIGTERM, SIG_DFL);
2263 signal(SIGHUP, SIG_DFL);
2264 sigprocmask(SIG_SETMASK, &oldset, NULL);
2268 for(i=0;i<servers->len;i++) {
2269 serve=&g_array_index(servers,
SERVER, i);
2278 g_array_free(servers, FALSE);
2285 msg(LOG_INFO,
"Starting to serve");
2289 handle_connection_out:
2304 const GArray *
const servers) {
2307 for (i = 0; i < servers->len; ++i) {
2308 const SERVER server = g_array_index(servers,
SERVER, i);
2310 if (strcmp(servename, server.
servename) == 0)
2331 GArray *new_servers;
2332 const int old_len = servers->len;
2340 for (i = 0; i < new_servers->len; ++i) {
2341 SERVER new_server = g_array_index(new_servers,
SERVER, i);
2353 retval = servers->len - old_len;
2355 g_array_free(new_servers, TRUE);
2364 struct sockaddr_storage addrin;
2365 socklen_t addrinlen=
sizeof(addrin);
2381 for(i=0;i<servers->len;i++) {
2382 if((sock=(g_array_index(servers,
SERVER, i)).socket) >= 0) {
2383 FD_SET(sock, &mset);
2384 max=sock>max?sock:max;
2389 FD_SET(sock, &mset);
2390 max=sock>max?sock:max;
2401 GError *gerror = NULL;
2403 msg(LOG_INFO,
"reconfiguration request received");
2409 msg(LOG_ERR,
"failed to append new servers: %s",
2412 for (i = servers->len - n; i < servers->len; ++i) {
2413 const SERVER server = g_array_index(servers,
2416 if (server.
socket >= 0) {
2417 FD_SET(server.
socket, &mset);
2421 msg(LOG_INFO,
"reconfigured new server: %s",
2426 memcpy(&rset, &mset,
sizeof(fd_set));
2427 if(select(max+1, &rset, NULL, NULL, NULL)>0) {
2433 if(!FD_ISSET(sock, &rset)) {
2438 if((net=accept(sock, (
struct sockaddr *) &addrin, &addrinlen)) < 0) {
2442 client =
negotiate(net, NULL, servers, NEG_INIT | NEG_MODERN);
2449 for(i=0; i < servers->len; i++) {
2452 serve=&(g_array_index(servers,
SERVER, i));
2456 if(FD_ISSET(serve->
socket, &rset)) {
2457 if ((net=accept(serve->
socket, (
struct sockaddr *) &addrin, &addrinlen)) < 0) {
2480 int dosockopts(
const int socket, GError **
const gerror) {
2489 if (setsockopt(socket,SOL_SOCKET,SO_REUSEADDR,&yes,
sizeof(
int)) == -1) {
2491 "failed to set socket option SO_REUSEADDR: %s",
2497 if (setsockopt(socket,SOL_SOCKET,SO_LINGER,&l,
sizeof(l)) == -1) {
2499 "failed to set socket option SO_LINGER: %s",
2503 if (setsockopt(socket,SOL_SOCKET,SO_KEEPALIVE,&yes,
sizeof(
int)) == -1) {
2505 "failed to set socket option SO_KEEPALIVE: %s",
2519 struct addrinfo hints;
2520 struct addrinfo *ai = NULL;
2544 memset(&hints,
'\0',
sizeof(hints));
2546 hints.ai_socktype = SOCK_STREAM;
2549 port = g_strdup_printf(
"%d", serve->
port);
2552 "failed to open an export socket: "
2553 "failed to convert a port number to a string: %s",
2558 e = getaddrinfo(serve->
listenaddr,port,&hints,&ai);
2564 "failed to open an export socket: "
2565 "failed to get address info: %s",
2575 if (ai->ai_family == AF_INET)
2576 ai->ai_family = AF_INET_SDP;
2577 else (ai->ai_family == AF_INET6)
2578 ai->ai_family = AF_INET6_SDP;
2581 if ((serve->
socket = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
2583 "failed to open an export socket: "
2584 "failed to create a socket: %s",
2590 g_prefix_error(gerror,
"failed to open an export socket: ");
2594 DEBUG(
"Waiting for connections... bind, ");
2595 e = bind(serve->
socket, ai->ai_addr, ai->ai_addrlen);
2596 if (e != 0 && errno != EADDRINUSE) {
2598 "failed to open an export socket: "
2599 "failed to bind an address to a socket: %s",
2604 if (listen(serve->
socket, 1) < 0) {
2606 "failed to open an export socket: "
2607 "failed to start listening on a socket: %s",
2615 if (retval == -1 && serve->
socket >= 0) {
2624 int open_modern(
const gchar *
const addr,
const gchar *
const port,
2625 GError **
const gerror) {
2626 struct addrinfo hints;
2627 struct addrinfo* ai = NULL;
2628 struct addrinfo* ai_bak;
2635 memset(&hints,
'\0',
sizeof(hints));
2636 hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
2637 hints.ai_socktype = SOCK_STREAM;
2638 hints.ai_family = AF_UNSPEC;
2639 hints.ai_protocol = IPPROTO_TCP;
2644 "failed to open a modern socket: "
2645 "failed to get address info: %s",
2653 if((sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol))<0) {
2655 "failed to open a modern socket: "
2656 "failed to create a socket: %s",
2662 g_prefix_error(gerror,
"failed to open a modern socket: ");
2666 if(bind(sock, ai->ai_addr, ai->ai_addrlen)) {
2683 "failed to open a modern socket: "
2684 "failed to bind an address to a socket: %s",
2689 if(listen(sock, 10) <0) {
2691 "failed to open a modern socket: "
2692 "failed to start listening on a socket: %s",
2704 if (retval == -1 && sock >= 0) {
2708 freeaddrinfo(ai_bak);
2716 void setup_servers(GArray *
const servers,
const gchar *
const modernaddr,
2717 const gchar *
const modernport) {
2719 struct sigaction sa;
2722 for(i=0;i<servers->len;i++) {
2723 GError *gerror = NULL;
2729 msg(LOG_ERR,
"failed to setup servers: %s",
2731 g_clear_error(&gerror);
2737 GError *gerror = NULL;
2738 if (
open_modern(modernaddr, modernport, &gerror) == -1) {
2739 msg(LOG_ERR,
"failed to setup servers: %s",
2741 g_clear_error(&gerror);
2748 sigemptyset(&sa.sa_mask);
2749 sigaddset(&sa.sa_mask, SIGTERM);
2750 sa.sa_flags = SA_RESTART;
2751 if(sigaction(SIGCHLD, &sa, NULL) == -1)
2752 err(
"sigaction: %m");
2755 sigemptyset(&sa.sa_mask);
2756 sigaddset(&sa.sa_mask, SIGCHLD);
2757 sa.sa_flags = SA_RESTART;
2758 if(sigaction(SIGTERM, &sa, NULL) == -1)
2759 err(
"sigaction: %m");
2762 sigemptyset(&sa.sa_mask);
2763 sa.sa_flags = SA_RESTART;
2764 if(sigaction(SIGHUP, &sa, NULL) == -1)
2765 err(
"sigaction: %m");
2775 #if !defined(NODAEMON)
2779 if(serve && !(serve->
port)) {
2787 strncpy(
pidftemplate,
"/var/run/nbd-server.%d.pid", 255);
2795 fprintf(pidf,
"%d\n", (
int)getpid());
2799 fprintf(stderr,
"Not fatal; continuing");
2803 #define daemonize(serve)
2814 void dousers(
const gchar *
const username,
const gchar *
const groupname) {
2819 gr = getgrnam(groupname);
2821 str = g_strdup_printf(
"Invalid group name: %s", groupname);
2824 if(setgid(gr->gr_gid)<0) {
2825 err(
"Could not set GID: %m");
2829 pw = getpwnam(username);
2831 str = g_strdup_printf(
"Invalid user name: %s", username);
2834 if(setuid(pw->pw_uid)<0) {
2835 err(
"Could not set UID: %m");
2842 GLogLevelFlags log_level,
2843 const gchar *message,
2846 int level=LOG_DEBUG;
2850 case G_LOG_FLAG_FATAL:
2851 case G_LOG_LEVEL_CRITICAL:
2852 case G_LOG_LEVEL_ERROR:
2855 case G_LOG_LEVEL_WARNING:
2858 case G_LOG_LEVEL_MESSAGE:
2859 case G_LOG_LEVEL_INFO:
2862 case G_LOG_LEVEL_DEBUG:
2868 syslog(level,
"%s", message);
2875 int main(
int argc,
char *argv[]) {
2884 fprintf(stderr,
"Bad size of structure. Alignment problems?\n");
2885 exit(EXIT_FAILURE) ;
2890 modernsocks = g_array_new(FALSE, FALSE,
sizeof(
int));
2907 if (!(serve->
port)) {
2916 open(
"/dev/null", O_WRONLY);
2917 open(
"/dev/null", O_WRONLY);
2920 client=g_malloc(
sizeof(
CLIENT));
2931 if(!servers || !servers->len) {
2932 if(err && !(err->domain ==
NBDS_ERR
2934 g_warning(
"Could not parse config file: %s",
2935 err ? err->message :
"Unknown error");
2939 g_warning(
"Specifying an export on the command line is deprecated.");
2940 g_warning(
"Please use a configuration file instead.");
2943 if((!serve) && (!servers||!servers->len)) {
2945 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)
void sigterm_handler(int s)
Handle SIGTERM and dispatch it to our children.
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.
#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
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.
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 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.
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
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.
void usage(char *errmsg,...)
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.
void sigchld_handler(int s)
Signal handler for SIGCHLD.
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
int main(int argc, char **argv)
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
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.
#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.
void negotiate(int sock, u64 *rsize64, u32 *flags, char *name, uint32_t needed_flags, uint32_t client_flags, uint32_t do_opts)
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