#include <sys/mount.h> /* For BLKGETSIZE */
#endif
#include <signal.h> /* sigaction */
+#include <errno.h>
#include <netinet/tcp.h>
#include <netinet/in.h> /* sockaddr_in, htons, in_addr */
#include <netdb.h> /* hostent, gethostby*, getservby* */
#include <dirent.h>
#include <unistd.h>
#include <getopt.h>
+#include <pwd.h>
+#include <grp.h>
#include <glib.h>
/** Where our config file actually is */
gchar* config_file_pos;
+/** What user we're running as */
+gchar* runuser=NULL;
+/** What group we're running as */
+gchar* rungroup=NULL;
+
/** Logging macros, now nothing goes to syslog unless you say ISSERVER */
#ifdef ISSERVER
#define msg2(a,b) syslog(a,b)
#define F_COPYONWRITE 4 /**< flag to tell us a file is exported using
copyonwrite */
#define F_AUTOREADONLY 8 /**< flag to tell us a file is set to autoreadonly */
+#define F_SPARSE 16
GHashTable *children;
char pidfname[256]; /**< name of our PID file */
-char default_authname[] = "/etc/nbd_server.allow"; /**< default name of allow file */
+char pidftemplate[256]; /**< template to be used for the filename of the PID file */
+char default_authname[] = SYSCONFDIR "/nbd-server/allow"; /**< default name of allow file */
+
+/**
+ * Types of virtuatlization
+ **/
+typedef enum {
+ VIRT_NONE=0, /**< No virtualization */
+ VIRT_IPLIT, /**< Literal IP address as part of the filename */
+ VIRT_IPHASH, /**< Replacing all dots in an ip address by a / before
+ doing the same as in IPLIT */
+ VIRT_CIDR, /**< Every subnet in its own directory */
+} VIRT_STYLE;
/**
* Variables associated with a server.
unsigned int timeout;/**< how long a connection may be idle
(0=forever) */
int socket; /**< The socket of this server. */
+ VIRT_STYLE virtstyle;/**< The style of virtualization, if any */
+ uint8_t cidrlen; /**< The length of the mask when we use
+ CIDR-style virtualization */
+ gchar* prerun; /**< command to be ran after connecting a client,
+ but before starting to serve */
+ gchar* postrun; /**< command that will be ran after the client
+ disconnects */
} SERVER;
/**
PARAM_STRING, /**< This parameter is a string */
PARAM_BOOL, /**< This parameter is a boolean */
} PARAM_TYPE;
+
/**
* Configuration file values
**/
* @return 0 - authorization refused, 1 - OK
**/
int authorized_client(CLIENT *opts) {
+ const char *ERRMSG="Invalid entry '%s' in authfile '%s', so, refusing all connections.";
FILE *f ;
-
char line[LINELEN];
+ char *tmp;
+ struct in_addr addr;
+ struct in_addr client;
+ struct in_addr cltemp;
+ int len;
if ((f=fopen(opts->server->authname,"r"))==NULL) {
msg4(LOG_INFO,"Can't open authorization file %s (%s).",
return 1 ;
}
+ inet_aton(opts->clientname, &client);
while (fgets(line,LINELEN,f)!=NULL) {
+ if((tmp=index(line, '/'))) {
+ if(strlen(line)<=tmp-line) {
+ msg4(LOG_CRIT, ERRMSG, line, opts->server->authname);
+ return 0;
+ }
+ *(tmp++)=0;
+ if(inet_aton(line,&addr)) {
+ msg4(LOG_CRIT, ERRMSG, line, opts->server->authname);
+ return 0;
+ }
+ len=strtol(tmp, NULL, 0);
+ addr.s_addr>>=32-len;
+ addr.s_addr<<=32-len;
+ memcpy(&cltemp,&client,sizeof(client));
+ cltemp.s_addr>>=32-len;
+ cltemp.s_addr<<=32-len;
+ if(addr.s_addr == cltemp.s_addr) {
+ return 1;
+ }
+ }
if (strncmp(line,opts->clientname,strlen(opts->clientname))==0) {
fclose(f);
return 1;
}
}
- fclose(f) ;
- return 0 ;
+ fclose(f);
+ return 0;
}
/**
*/
void usage() {
printf("This is nbd-server version " VERSION "\n");
- printf("Usage: port file_to_export [size][kKmM] [-l authorize_file] [-r] [-m] [-c] [-a timeout_sec] [-C configuration file]\n"
+ printf("Usage: port file_to_export [size][kKmM] [-l authorize_file] [-r] [-m] [-c] [-a timeout_sec] [-C configuration file] [-p PID file name] [-o section name]\n"
"\t-r|--read-only\t\tread only\n"
"\t-m|--multi-file\t\tmultiple file\n"
"\t-c|--copy-on-write\tcopy on write\n"
- "\t-C|--config-file\tspecify an alternat configuration file\n"
+ "\t-C|--config-file\tspecify an alternate configuration file\n"
"\t-l|--authorize-file\tfile with list of hosts that are allowed to\n\t\t\t\tconnect.\n"
- "\t-a|--idle-time\t\tmaximum idle seconds; server terminates when\n\t\t\t\tidle time exceeded\n\n"
+ "\t-a|--idle-time\t\tmaximum idle seconds; server terminates when\n\t\t\t\tidle time exceeded\n"
+ "\t-p|--pid-file\t\tspecify a filename to write our PID to\n"
+ "\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\n"
"\tif port is set to 0, stdin is used (for running from inetd)\n"
"\tif file_to_export contains '%%s', it is substituted with the IP\n"
"\t\taddress of the machine trying to connect\n" );
printf("Using configuration file %s\n", CFILE);
}
+/* Dumps a config file section of the given SERVER*, and exits. */
+void dump_section(SERVER* serve, gchar* section_header) {
+ printf("[%s]\n", section_header);
+ printf("\texportname = %s\n", serve->exportname);
+ printf("\tport = %d\n", serve->port);
+ if(serve->flags & F_READONLY) {
+ printf("\treadonly = true\n");
+ }
+ if(serve->flags & F_MULTIFILE) {
+ printf("\tmultifile = true\n");
+ }
+ if(serve->flags & F_COPYONWRITE) {
+ printf("\tcopyonwrite = true\n");
+ }
+ if(serve->expected_size) {
+ printf("\tfilesize = %Ld\n", (long long int)serve->expected_size);
+ }
+ if(serve->authname) {
+ printf("\tauthfile = %s\n", serve->authname);
+ }
+ if(serve->timeout) {
+ printf("\ttimeout = %d\n", serve->timeout);
+ }
+ exit(EXIT_SUCCESS);
+}
+
/**
* Parse the command line.
*
{"authorize-file", required_argument, NULL, 'l'},
{"idle-time", required_argument, NULL, 'a'},
{"config-file", required_argument, NULL, 'C'},
+ {"pid-file", required_argument, NULL, 'p'},
+ {"output-config", required_argument, NULL, 'o'},
{0,0,0,0}
};
SERVER *serve;
off_t es;
size_t last;
char suffix;
+ gboolean do_output=FALSE;
+ gchar* section_header;
if(argc==1) {
return NULL;
}
serve=g_new0(SERVER, 1);
serve->authname = g_strdup(default_authname);
- while((c=getopt_long(argc, argv, "-a:C:cl:mr", long_options, &i))>=0) {
+ serve->virtstyle=VIRT_IPLIT;
+ while((c=getopt_long(argc, argv, "-a:C:cl:mo:rp:", long_options, &i))>=0) {
switch (c) {
case 1:
/* non-option argument */
case 'm':
serve->flags |= F_MULTIFILE;
break;
+ case 'o':
+ do_output = TRUE;
+ section_header = g_strdup(optarg);
+ break;
+ case 'p':
+ strncpy(pidftemplate, optarg, 256);
+ break;
case 'c':
serve->flags |=F_COPYONWRITE;
break;
g_free(serve);
serve=NULL;
}
+ if(do_output) {
+ if(!serve) {
+ g_critical("Need a complete configuration on the command line to output a config file section!");
+ exit(EXIT_FAILURE);
+ }
+ dump_section(serve, section_header);
+ }
return serve;
}
* @param f the name of the config file
* @param e a GError. @see CFILE_ERRORS for what error values this function can
* return.
- * @return a GHashTable of SERVER* pointers, with the port number as the hash
- * key. If the config file is empty or does not exist, returns an empty
- * GHashTable; if the config file contains an error, returns NULL, and
- * e is set appropriately
+ * @return a Array of SERVER* pointers, If the config file is empty or does not
+ * exist, returns an empty GHashTable; if the config file contains an
+ * error, returns NULL, and e is set appropriately
**/
GArray* parse_cfile(gchar* f, GError** e) {
const char* DEFAULT_ERROR = "Could not parse %s in group %s: %s";
const char* MISSING_REQUIRED_ERROR = "Could not find required value %s in group %s: %s";
SERVER s;
- PARAM p[] = {
+ gchar *virtstyle=NULL;
+ PARAM lp[] = {
{ "exportname", TRUE, PARAM_STRING, NULL, 0 },
{ "port", TRUE, PARAM_INT, NULL, 0 },
{ "authfile", FALSE, PARAM_STRING, NULL, 0 },
{ "timeout", FALSE, PARAM_INT, 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 },
+ { "autoreadonly", FALSE, PARAM_BOOL, NULL, F_AUTOREADONLY },
+ { "sparse_cow", FALSE, PARAM_BOOL, NULL, F_SPARSE },
};
- const int p_size=8;
+ const int lp_size=11;
+ PARAM gp[] = {
+ { "user", FALSE, PARAM_STRING, &runuser, 0 },
+ { "group", FALSE, PARAM_STRING, &rungroup, 0 },
+ };
+ PARAM* p=gp;
+ int p_size=2;
GKeyFile *cfile;
GError *err = NULL;
const char *err_msg=NULL;
GArray *retval=NULL;
gchar **groups;
gboolean value;
- gint i,j;
+ gint i;
+ gint j;
- memset(&s, '\0', sizeof(SERVER));
errdomain = g_quark_from_string("parse_cfile");
cfile = g_key_file_new();
retval = g_array_new(FALSE, TRUE, sizeof(SERVER));
return NULL;
}
groups = g_key_file_get_groups(cfile, NULL);
- for(i=1;groups[i];i++) {
- p[0].target=&(s.exportname);
- p[1].target=&(s.port);
- p[2].target=&(s.authname);
- p[3].target=&(s.timeout);
- p[4].target=&(s.expected_size);
- p[5].target=p[6].target=p[7].target=&(s.flags);
+ 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.timeout);
+ lp[4].target=&(s.expected_size);
+ lp[5].target=&(virtstyle);
+ lp[6].target=&(s.prerun);
+ lp[7].target=&(s.postrun);
+ lp[8].target=lp[9].target=lp[10].target=
+ lp[11].target=lp[12].target=&(s.flags);
+
+ /* After the [generic] group, start parsing exports */
+ if(i==1) {
+ p=lp;
+ p_size=lp_size;
+ }
for(j=0;j<p_size;j++) {
g_assert(p[j].target != NULL);
g_assert(p[j].ptype==PARAM_INT||p[j].ptype==PARAM_STRING||p[j].ptype==PARAM_BOOL);
value = g_key_file_get_boolean(cfile,
groups[i],
p[j].paramname, &err);
- if(!err && value) {
- *((gint*)p[j].target) |= p[j].flagval;
+ if(!err) {
+ if(value) {
+ *((gint*)p[j].target) |= p[j].flagval;
+ } else {
+ *((gint*)p[j].target) &= ~(p[j].flagval);
+ }
}
break;
}
return NULL;
}
}
- g_array_append_val(retval, s);
+ if(virtstyle) {
+ if(!strncmp(virtstyle, "none", 4)) {
+ s.virtstyle=VIRT_NONE;
+ } else if(!strncmp(virtstyle, "ipliteral", 9)) {
+ s.virtstyle=VIRT_IPLIT;
+ } else if(!strncmp(virtstyle, "iphash", 6)) {
+ s.virtstyle=VIRT_IPHASH;
+ } else if(!strncmp(virtstyle, "cidrhash", 8)) {
+ s.virtstyle=VIRT_CIDR;
+ if(strlen(virtstyle)<10) {
+ g_set_error(e, errdomain, CFILE_VALUE_INVALID, "Invalid value %s for parameter virtstyle in group %s: missing length", virtstyle, groups[i]);
+ g_array_free(retval, TRUE);
+ g_key_file_free(cfile);
+ return NULL;
+ }
+ s.cidrlen=strtol(virtstyle+8, NULL, 0);
+ } else {
+ g_set_error(e, errdomain, CFILE_VALUE_INVALID, "Invalid value %s for parameter virtstyle in group %s", virtstyle, groups[i]);
+ g_array_free(retval, TRUE);
+ g_key_file_free(cfile);
+ return NULL;
+ }
+ } else {
+ s.virtstyle=VIRT_IPLIT;
+ }
+ /* Don't need to free this, it's not our string */
+ virtstyle=NULL;
+ /* Don't append values for the [generic] group */
+ if(i>0) {
+ g_array_append_val(retval, s);
+ }
}
return retval;
}
pid_t pid;
while((pid=waitpid(-1, &status, WNOHANG)) > 0) {
- if(WIFEXITED(&status)) {
+ if(WIFEXITED(status)) {
msg3(LOG_INFO, "Child exited with %d", WEXITSTATUS(status));
}
i=g_hash_table_lookup(children, &pid);
**/
off_t size_autodetect(int fhandle) {
off_t es;
- u32 es32;
+ unsigned long sectors;
struct stat stat_buf;
int error;
#ifdef HAVE_SYS_MOUNT_H
#ifdef HAVE_SYS_IOCTL_H
#ifdef BLKGETSIZE
- DEBUG("looking for fhandle size with ioctl BLKGETSIZE\n");
- if (!ioctl(fhandle, BLKGETSIZE, &es32) && es32) {
- es = (off_t)es32 * (off_t)512;
+ DEBUG("looking for export size with ioctl BLKGETSIZE\n");
+ if (!ioctl(fhandle, BLKGETSIZE, §ors) && sectors) {
+ es = (off_t)sectors * (off_t)512;
return es;
}
#endif /* BLKGETSIZE */
if (write(client->difffile, buf, wrlen) != wrlen) return -1 ;
} else { /* the block is not there */
myseek(client->difffile,client->difffilelen*DIFFPAGESIZE) ;
- client->difmap[mapcnt]=client->difffilelen++ ;
+ client->difmap[mapcnt]=(client->server->flags&F_SPARSE)?mapcnt:client->difffilelen++;
DEBUG3("Page %Lu is not here, we put it at %lu\n",
(unsigned long long)mapcnt,
(unsigned long)(client->difmap[mapcnt]));
* @param client The client we're negotiating with.
**/
void negotiate(CLIENT *client) {
- char zeros[300];
+ char zeros[128];
u64 size_host;
+ u32 flags = NBD_FLAG_HAS_FLAGS;
- memset(zeros, 0, 290);
+ memset(zeros, '\0', sizeof(zeros));
if (write(client->net, INIT_PASSWD, 8) < 0)
err("Negotiation failed: %m");
cliserv_magic = htonll(cliserv_magic);
size_host = htonll((u64)(client->exportsize));
if (write(client->net, &size_host, 8) < 0)
err("Negotiation failed: %m");
- if (write(client->net, zeros, 128) < 0)
+ if (client->server->flags & F_READONLY)
+ flags |= NBD_FLAG_READ_ONLY;
+ flags = htonl(flags);
+ if (write(client->net, &flags, 4) < 0)
+ err("Negotiation failed: %m");
+ if (write(client->net, zeros, 124) < 0)
err("Negotiation failed: %m");
}
/** sending macro. */
#define SEND(net,reply) writeit( net, &reply, sizeof( reply ));
/** error macro. */
-#define ERROR(client,reply) { reply.error = htonl(-1); SEND(client->net,reply); reply.error = 0; }
+#define ERROR(client,reply,errcode) { reply.error = htonl(errcode); SEND(client->net,reply); reply.error = 0; }
/**
* Serve a file to a single client.
*
* pieces. Preferably with a chainsaw.
*
* @param client The client we're going to serve to.
- * @return never
+ * @return when the client disconnects
**/
int mainloop(CLIENT *client) {
struct nbd_request request;
if (request.type==NBD_CMD_DISC) {
msg2(LOG_INFO, "Disconnect request received.");
- if (client->difmap) g_free(client->difmap) ;
if (client->server->flags & F_COPYONWRITE) {
+ if (client->difmap) g_free(client->difmap) ;
close(client->difffile);
unlink(client->difffilename);
free(client->difffilename);
memcpy(reply.handle, request.handle, sizeof(reply.handle));
if ((request.from + len) > (OFFT_MAX)) {
DEBUG("[Number too large!]");
- ERROR(client, reply);
+ ERROR(client, reply, EINVAL);
continue;
}
if (((ssize_t)((off_t)request.from + len) > client->exportsize)) {
DEBUG("[RANGE!]");
- ERROR(client, reply);
+ ERROR(client, reply, EINVAL);
continue;
}
if ((client->server->flags & F_READONLY) ||
(client->server->flags & F_AUTOREADONLY)) {
DEBUG("[WRITE to READONLY!]");
- ERROR(client, reply);
+ ERROR(client, reply, EPERM);
continue;
}
if (expwrite(request.from, buf, len, client)) {
DEBUG("Write failed: %m" );
- ERROR(client, reply);
+ ERROR(client, reply, errno);
continue;
}
SEND(client->net, reply);
DEBUG("exp->buf, ");
if (expread(request.from, buf + sizeof(struct nbd_reply), len, client)) {
DEBUG("Read failed: %m");
- ERROR(client, reply);
+ ERROR(client, reply, errno);
continue;
}
}
/**
+ * Run a command. This is used for the ``prerun'' and ``postrun'' config file
+ * options
+ *
+ * @param command the command to be ran. Read from the config file
+ * @param file the file name we're about to export
+ **/
+int do_run(gchar* command, gchar* file) {
+ gchar* cmd;
+ int retval=0;
+
+ if(command && *command) {
+ cmd = g_strdup_printf(command, file);
+ retval=system(cmd);
+ g_free(cmd);
+ }
+ return retval;
+}
+
+/**
* Serve a connection.
*
* @todo allow for multithreading, perhaps use libevent. Not just yet, though;
* @param client a connected client
**/
void serveconnection(CLIENT *client) {
+ if(do_run(client->server->prerun, client->exportname)) {
+ exit(EXIT_FAILURE);
+ }
setupexport(client);
if (client->server->flags & F_COPYONWRITE) {
setmysockopt(client->net);
mainloop(client);
+ do_run(client->server->postrun, client->exportname);
}
/**
* Find the name of the file we have to serve. This will use g_strdup_printf
* to put the IP address of the client inside a filename containing
- * "%s". That name is then written to client->exportname.
+ * "%s" (in the form as specified by the "virtstyle" option). That name
+ * is then written to client->exportname.
*
* @param net A socket connected to an nbd client
* @param client information about the client. The IP address in human-readable
**/
void set_peername(int net, CLIENT *client) {
struct sockaddr_in addrin;
- int addrinlen = sizeof( addrin );
- char *peername ;
+ struct sockaddr_in netaddr;
+ size_t addrinlen = sizeof( addrin );
+ char *peername;
+ char *netname;
+ char *tmp;
+ int i;
if (getpeername(net, (struct sockaddr *) &addrin, (socklen_t *)&addrinlen) < 0)
err("getsockname failed: %m");
- peername = inet_ntoa(addrin.sin_addr);
- client->exportname=g_strdup_printf(client->server->exportname, peername);
+ peername = g_strdup(inet_ntoa(addrin.sin_addr));
+ switch(client->server->virtstyle) {
+ case VIRT_NONE:
+ client->exportname=g_strdup(client->server->exportname);
+ break;
+ case VIRT_IPHASH:
+ for(i=0;i<strlen(peername);i++) {
+ if(peername[i]=='.') {
+ peername[i]='/';
+ }
+ }
+ case VIRT_IPLIT:
+ client->exportname=g_strdup_printf(client->server->exportname, peername);
+ break;
+ case VIRT_CIDR:
+ memcpy(&netaddr, &addrin, addrinlen);
+ netaddr.sin_addr.s_addr>>=32-(client->server->cidrlen);
+ netaddr.sin_addr.s_addr<<=32-(client->server->cidrlen);
+ netname = inet_ntoa(netaddr.sin_addr);
+ tmp=g_strdup_printf("%s/%s", netname, peername);
+ client->exportname=g_strdup_printf(client->server->exportname, tmp);
+ break;
+ }
msg4(LOG_INFO, "connect from %s, assigned file is %s",
peername, client->exportname);
client->clientname=g_strdup(peername);
+ g_free(peername);
}
/**
void daemonize(SERVER* serve) {
FILE*pidf;
+ if(serve && !(serve->port)) {
+ return;
+ }
if(daemon(0,0)<0) {
err("daemon");
}
- if(serve) {
- snprintf(pidfname, sizeof(char)*255, "/var/run/nbd-server.%d.pid", serve->port);
- } else {
- strncpy(pidfname, "/var/run/nbd-server.pid", sizeof(char)*255);
+ if(!*pidftemplate) {
+ if(serve) {
+ strncpy(pidftemplate, "/var/run/server.%d.pid", 255);
+ } else {
+ strncpy(pidftemplate, "/var/run/server.pid", 255);
+ }
}
+ snprintf(pidfname, 255, pidftemplate, serve ? serve->port : 0);
pidf=fopen(pidfname, "w");
if(pidf) {
fprintf(pidf,"%d\n", (int)getpid());
sa.sa_flags = SA_RESTART;
if(sigaction(SIGTERM, &sa, NULL) == -1)
err("sigaction: %m");
- children=g_hash_table_new_full(g_int_hash, g_int_equal, NULL, destroy_pid_t);
}
/**
for(i=0;i<servers->len;i++) {
setup_serve(&(g_array_index(servers, SERVER, i)));
}
+ children=g_hash_table_new_full(g_int_hash, g_int_equal, NULL, destroy_pid_t);
}
/**
}
/* child */
g_hash_table_destroy(children);
- for(i=0;i<servers->len,serve=(g_array_index(servers, SERVER*, i));i++) {
+ for(i=0;i<servers->len;i++) {
+ serve=g_array_index(servers, SERVER*, i);
close(serve->socket);
}
/* FALSE does not free the
#endif // NOFORK
msg2(LOG_INFO,"Starting to serve");
serveconnection(client);
+ exit(EXIT_SUCCESS);
}
}
}
}
/**
+ * Set up user-ID and/or group-ID
+ **/
+void dousers(void) {
+ struct passwd *pw;
+ struct group *gr;
+ if(rungroup) {
+ gr=getgrnam(rungroup);
+ if(setgid(gr->gr_gid)<0)
+ msg3(LOG_DEBUG, "Could not set GID: %s", strerror(errno));
+ }
+ if(runuser) {
+ pw=getpwnam(runuser);
+ if(setuid(pw->pw_uid)<0)
+ msg3(LOG_DEBUG, "Could not set UID: %s", strerror(errno));
+ }
+}
+
+/**
* Main entry point...
**/
int main(int argc, char *argv[]) {
exit(-1) ;
}
+ memset(pidftemplate, '\0', 256);
+
logging();
config_file_pos = g_strdup(CFILE);
serve=cmdline(argc, argv);
servers = parse_cfile(config_file_pos, &err);
if(!servers || !servers->len) {
- g_warning("Could not parse config file: %s", err->message);
+ g_warning("Could not parse config file: %s",
+ err ? err->message : "Unknown error");
}
if(serve) {
g_array_append_val(servers, *serve);
- }
-
-/* We don't support this at this time */
-#if 0
- if (!(serve->port)) {
- CLIENT *client;
+
+ if (!(serve->port)) {
+ CLIENT *client;
#ifndef ISSERVER
- /* You really should define ISSERVER if you're going to use
- * inetd mode, but if you don't, closing stdout and stderr
- * (which inetd had connected to the client socket) will let it
- * work. */
- close(1);
- close(2);
- open("/dev/null", O_WRONLY);
- open("/dev/null", O_WRONLY);
-#endif
- client=g_malloc(sizeof(CLIENT));
- client->server=serve;
- client->net=0;
- client->exportsize=OFFT_MAX;
- set_peername(0,client);
- serveconnection(client);
- return 0;
- }
+ /* You really should define ISSERVER if you're going to use
+ * inetd mode, but if you don't, closing stdout and stderr
+ * (which inetd had connected to the client socket) will let it
+ * work. */
+ close(1);
+ close(2);
+ open("/dev/null", O_WRONLY);
+ open("/dev/null", O_WRONLY);
#endif
+ client=g_malloc(sizeof(CLIENT));
+ client->server=serve;
+ client->net=0;
+ client->exportsize=OFFT_MAX;
+ set_peername(0,client);
+ serveconnection(client);
+ return 0;
+ }
+ }
if((!serve) && (!servers||!servers->len)) {
g_message("Nothing to do! Bye!");
exit(EXIT_FAILURE);
}
daemonize(serve);
setup_servers(servers);
+ dousers();
serveloop(servers);
return 0 ;
}