/** whether to export using the old negotiation protocol (port-based) */
gboolean do_oldstyle=FALSE;
+/* Whether we should avoid forking */
+int dontfork = 0;
+
/** Logging macros, now nothing goes to syslog unless you say ISSERVER */
#ifdef ISSERVER
#define msg2(a,b) syslog(a,b)
#define OFFT_MAX ~((off_t)1<<(sizeof(off_t)*8-1))
#define LINELEN 256 /**< Size of static buffer used to read the
authorization file (yuck) */
-#define BUFSIZE (1024*1024) /**< Size of buffer that can hold requests */
+#define BUFSIZE ((1024*1024)+sizeof(struct nbd_reply)) /**< Size of buffer that can hold requests */
#define DIFFPAGESIZE 4096 /**< diff file uses those chunks */
#define F_READONLY 1 /**< flag to tell us a file is readonly */
#define F_MULTIFILE 2 /**< flag to tell us a file is exported using -m */
* @param buf a buffer
* @param len the number of bytes to be read
**/
-inline void readit(int f, void *buf, size_t len) {
+static inline void readit(int f, void *buf, size_t len) {
ssize_t res;
while (len > 0) {
DEBUG("*");
* @param buf a buffer containing data
* @param len the number of bytes to be written
**/
-inline void writeit(int f, void *buf, size_t len) {
+static inline void writeit(int f, void *buf, size_t len) {
ssize_t res;
while (len > 0) {
DEBUG("+");
{"read-only", no_argument, NULL, 'r'},
{"multi-file", no_argument, NULL, 'm'},
{"copy-on-write", no_argument, NULL, 'c'},
+ {"dont-fork", no_argument, NULL, 'd'},
{"authorize-file", required_argument, NULL, 'l'},
{"config-file", required_argument, NULL, 'C'},
{"pid-file", required_argument, NULL, 'p'},
serve=g_new0(SERVER, 1);
serve->authname = g_strdup(default_authname);
serve->virtstyle=VIRT_IPLIT;
- while((c=getopt_long(argc, argv, "-C:cl:mo:rp:M:", long_options, &i))>=0) {
+ while((c=getopt_long(argc, argv, "-C:cdl:mo:rp:M:", long_options, &i))>=0) {
switch (c) {
case 1:
/* non-option argument */
case 'c':
serve->flags |=F_COPYONWRITE;
break;
+ case 'd':
+ dontfork = 1;
+ break;
case 'C':
g_free(config_file_pos);
config_file_pos=g_strdup(optarg);
serve->authname = strdup(s->authname);
serve->flags = s->flags;
- serve->socket = serve->socket;
- serve->socket_family = serve->socket_family;
+ serve->socket = s->socket;
+ serve->socket_family = s->socket_family;
+ serve->virtstyle = s->virtstyle;
serve->cidrlen = s->cidrlen;
if(s->prerun)
SERVER s;
gchar *virtstyle=NULL;
PARAM lp[] = {
- { "exportname", TRUE, PARAM_STRING, NULL, 0 },
- { "port", TRUE, PARAM_INT, NULL, 0 },
- { "authfile", FALSE, PARAM_STRING, NULL, 0 },
- { "filesize", FALSE, PARAM_INT, NULL, 0 },
- { "virtstyle", FALSE, PARAM_STRING, NULL, 0 },
- { "prerun", FALSE, PARAM_STRING, NULL, 0 },
- { "postrun", FALSE, PARAM_STRING, NULL, 0 },
- { "readonly", FALSE, PARAM_BOOL, NULL, F_READONLY },
- { "multifile", FALSE, PARAM_BOOL, NULL, F_MULTIFILE },
- { "copyonwrite", FALSE, PARAM_BOOL, NULL, F_COPYONWRITE },
- { "sparse_cow", FALSE, PARAM_BOOL, NULL, F_SPARSE },
- { "sdp", FALSE, PARAM_BOOL, NULL, F_SDP },
- { "sync", FALSE, PARAM_BOOL, NULL, F_SYNC },
- { "listenaddr", FALSE, PARAM_STRING, NULL, 0 },
- { "maxconnections", FALSE, PARAM_INT, NULL, 0 },
+ { "exportname", TRUE, PARAM_STRING, &(s.exportname), 0 },
+ { "port", TRUE, PARAM_INT, &(s.port), 0 },
+ { "authfile", FALSE, PARAM_STRING, &(s.authname), 0 },
+ { "filesize", FALSE, PARAM_INT, &(s.expected_size), 0 },
+ { "virtstyle", FALSE, PARAM_STRING, &(virtstyle), 0 },
+ { "prerun", FALSE, PARAM_STRING, &(s.prerun), 0 },
+ { "postrun", FALSE, PARAM_STRING, &(s.postrun), 0 },
+ { "readonly", FALSE, PARAM_BOOL, &(s.flags), F_READONLY },
+ { "multifile", FALSE, PARAM_BOOL, &(s.flags), F_MULTIFILE },
+ { "copyonwrite", FALSE, PARAM_BOOL, &(s.flags), F_COPYONWRITE },
+ { "sparse_cow", FALSE, PARAM_BOOL, &(s.flags), F_SPARSE },
+ { "sdp", FALSE, PARAM_BOOL, &(s.flags), F_SDP },
+ { "sync", FALSE, PARAM_BOOL, &(s.flags), F_SYNC },
+ { "listenaddr", FALSE, PARAM_STRING, &(s.listenaddr), 0 },
+ { "maxconnections", FALSE, PARAM_INT, &(s.max_connections), 0 },
};
const int lp_size=sizeof(lp)/sizeof(PARAM);
PARAM gp[] = {
groups = g_key_file_get_groups(cfile, NULL);
for(i=0;groups[i];i++) {
memset(&s, '\0', sizeof(SERVER));
- lp[0].target=&(s.exportname);
- lp[1].target=&(s.port);
- lp[2].target=&(s.authname);
- lp[3].target=&(s.expected_size);
- lp[4].target=&(virtstyle);
- lp[5].target=&(s.prerun);
- lp[6].target=&(s.postrun);
- lp[7].target=lp[8].target=lp[9].target=
- lp[10].target=lp[11].target=
- lp[12].target=&(s.flags);
- lp[13].target=&(s.listenaddr);
- lp[14].target=&(s.max_connections);
/* After the [generic] group, start parsing exports */
if(i==1) {
client->exportsize = OFFT_MAX;
client->net = net;
client->modern = TRUE;
+ free(name);
return client;
}
}
+ free(name);
return NULL;
}
/* common */
reply.error = 0;
while (go_on) {
char buf[BUFSIZE];
+ char* p;
size_t len;
+ size_t currlen;
+ size_t writelen;
#ifdef DODBG
i++;
printf("%d: ", i);
if (request.magic != htonl(NBD_REQUEST_MAGIC))
err("Not enough magic.");
- if (len > BUFSIZE + sizeof(struct nbd_reply))
- err("Request too big!");
+ if (len > BUFSIZE - sizeof(struct nbd_reply)) {
+ currlen = BUFSIZE - sizeof(struct nbd_reply);
+ msg2(LOG_INFO, "oversized request (this is not a problem)");
+ } else {
+ currlen = len;
+ }
#ifdef DODBG
printf("%s from %llu (%llu) len %d, ", request.type ? "WRITE" :
"READ", (unsigned long long)request.from,
if (request.type==NBD_CMD_WRITE) {
DEBUG("wr: net->buf, ");
- readit(client->net, buf, len);
- DEBUG("buf->exp, ");
- if ((client->server->flags & F_READONLY) ||
- (client->server->flags & F_AUTOREADONLY)) {
- DEBUG("[WRITE to READONLY!]");
- ERROR(client, reply, EPERM);
- continue;
- }
- if (expwrite(request.from, buf, len, client)) {
- DEBUG("Write failed: %m" );
- ERROR(client, reply, errno);
- continue;
+ while(len > 0) {
+ readit(client->net, buf, currlen);
+ DEBUG("buf->exp, ");
+ if ((client->server->flags & F_READONLY) ||
+ (client->server->flags & F_AUTOREADONLY)) {
+ DEBUG("[WRITE to READONLY!]");
+ ERROR(client, reply, EPERM);
+ continue;
+ }
+ if (expwrite(request.from, buf, len, client)) {
+ DEBUG("Write failed: %m" );
+ ERROR(client, reply, errno);
+ continue;
+ }
+ SEND(client->net, reply);
+ DEBUG("OK!\n");
+ len -= currlen;
+ currlen = (len < BUFSIZE) ? len : BUFSIZE;
}
- SEND(client->net, reply);
- DEBUG("OK!\n");
continue;
}
/* READ */
DEBUG("exp->buf, ");
- if (expread(request.from, buf + sizeof(struct nbd_reply), len, client)) {
- DEBUG("Read failed: %m");
- ERROR(client, reply, errno);
- continue;
- }
-
- DEBUG("buf->net, ");
memcpy(buf, &reply, sizeof(struct nbd_reply));
- writeit(client->net, buf, len + sizeof(struct nbd_reply));
+ p = buf + sizeof(struct nbd_reply);
+ writelen = currlen + sizeof(struct nbd_reply);
+ while(len > 0) {
+ if (expread(request.from, p, currlen, client)) {
+ DEBUG("Read failed: %m");
+ ERROR(client, reply, errno);
+ continue;
+ }
+
+ DEBUG("buf->net, ");
+ writeit(client->net, buf, writelen);
+ len -= currlen;
+ currlen = (len < BUFSIZE) ? len : BUFSIZE;
+ p = buf;
+ writelen = currlen;
+ }
DEBUG("OK!\n");
}
return 0;
memcpy(&rset, &mset, sizeof(fd_set));
if(select(max+1, &rset, NULL, NULL, NULL)>0) {
int net = 0;
- SERVER* serve;
+ SERVER* serve=NULL;
DEBUG("accept, ");
if(FD_ISSET(modernsock, &rset)) {
err_nonfatal("negotiation failed");
close(net);
net=0;
+ continue;
}
+ serve = client->server;
}
for(i=0;i<servers->len && !net;i++) {
serve=&(g_array_index(servers, SERVER, i));
}
msg2(LOG_INFO,"Authorized client") ;
pid=g_malloc(sizeof(pid_t));
-#ifndef NOFORK
- if ((*pid=fork())<0) {
- msg3(LOG_INFO,"Could not fork (%s)",strerror(errno)) ;
- close(net);
- continue;
- }
- if (*pid>0) { /* parent */
- close(net);
- g_hash_table_insert(children, pid, pid);
- continue;
- }
- /* child */
- g_hash_table_destroy(children);
- for(i=0;i<servers->len;i++) {
- serve=&g_array_index(servers, SERVER, i);
- close(serve->socket);
+
+ if (!dontfork) {
+ if ((*pid=fork())<0) {
+ msg3(LOG_INFO,"Could not fork (%s)",strerror(errno)) ;
+ close(net);
+ continue;
+ }
+ if (*pid>0) { /* parent */
+ close(net);
+ g_hash_table_insert(children, pid, pid);
+ continue;
+ }
+ /* child */
+ g_hash_table_destroy(children);
+ for(i=0;i<servers->len;i++) {
+ serve=&g_array_index(servers, SERVER, i);
+ close(serve->socket);
+ }
+ /* FALSE does not free the
+ actual data. This is required,
+ because the client has a
+ direct reference into that
+ data, and otherwise we get a
+ segfault... */
+ g_array_free(servers, FALSE);
}
- /* FALSE does not free the
- actual data. This is required,
- because the client has a
- direct reference into that
- data, and otherwise we get a
- segfault... */
- g_array_free(servers, FALSE);
-#endif // NOFORK
+
msg2(LOG_INFO,"Starting to serve");
serveconnection(client);
exit(EXIT_SUCCESS);
* is only used to create a PID file of the form
* /var/run/nbd-server.<port>.pid; it's not modified in any way.
**/
-#if !defined(NODAEMON) && !defined(NOFORK)
+#if !defined(NODAEMON)
void daemonize(SERVER* serve) {
FILE*pidf;
}
#else
#define daemonize(serve)
-#endif /* !defined(NODAEMON) && !defined(NOFORK) */
+#endif /* !defined(NODAEMON) */
/*
* Everything beyond this point (in the file) is run in non-daemon mode.
g_message("No configured exports; quitting.");
exit(EXIT_FAILURE);
}
- daemonize(serve);
+ if (!dontfork)
+ daemonize(serve);
setup_servers(servers);
dousers();
serveloop(servers);