r185: Build/documentation fixes from Phillip Hellewell
[nbd.git] / nbd-server.c
index 155998b..cadfb57 100644 (file)
@@ -58,6 +58,7 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
+#include <sys/select.h>                /* select */
 #include <sys/wait.h>          /* wait */
 #ifdef HAVE_SYS_IOCTL_H
 #include <sys/ioctl.h>
 /** Where our config file actually is */
 gchar* config_file_pos;
 
-/** how much space for child PIDs we have by default. Dynamically
-   allocated, and will be realloc()ed if out of space, so this should
-   probably be fair for most situations. */
-#define DEFAULT_CHILD_ARRAY 256
-
 /** Logging macros, now nothing goes to syslog unless you say ISSERVER */
 #ifdef ISSERVER
 #define msg2(a,b) syslog(a,b)
@@ -128,18 +124,10 @@ gchar* config_file_pos;
 #define PACKAGE_VERSION ""
 #endif
 /**
- * The highest value a variable of type off_t can reach.
+ * The highest value a variable of type off_t can reach. This is a signed
+ * integer, so set all bits except for the leftmost one.
  **/
-/* This is starting to get ugly. If someone knows a better way to find
- * the maximum value of a signed type *without* relying on overflow
- * (doing so breaks on 64bit architectures), that would be nice.
- *
- * Actually, do we need this at all? Can't we just say '0 is autodetect', and
- * live with it? Or better yet, use an extra flag, or so?
- * Answer: yes, we need it, as the hunksize is defined to this when the
- * multiple file thingy isn't used.
- */
-#define OFFT_MAX (((((off_t)1)<<((sizeof(off_t)-1)*8))-1)<<7)+127
+#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 */
@@ -287,10 +275,11 @@ inline void writeit(int f, void *buf, size_t len) {
  */
 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]\n"
+       printf("Usage: port file_to_export [size][kKmM] [-l authorize_file] [-r] [-m] [-c] [-a timeout_sec] [-C configuration file]\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-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"
               "\tif port is set to 0, stdin is used (for running from inetd)\n"
@@ -392,8 +381,8 @@ SERVER* cmdline(int argc, char *argv[]) {
        /* What's left: the port to export, the name of the to be exported
         * file, and, optionally, the size of the file, in that order. */
        if(nonspecial<2) {
-               usage();
-               exit(EXIT_FAILURE);
+               g_free(serve);
+               serve=NULL;
        }
        return serve;
 }
@@ -445,14 +434,16 @@ GArray* parse_cfile(gchar* f, GError** e) {
                { "multifile",  FALSE,  PARAM_BOOL,     NULL, F_MULTIFILE },
                { "copyonwrite", FALSE, PARAM_BOOL,     NULL, F_COPYONWRITE },
        };
+       const int p_size=8;
        GKeyFile *cfile;
        GError *err = NULL;
        GQuark errdomain;
-       GArray *retval;
+       GArray *retval=NULL;
        gchar **groups;
        gboolean value;
        gint i,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));
@@ -468,25 +459,35 @@ GArray* parse_cfile(gchar* f, GError** e) {
                return NULL;
        }
        groups = g_key_file_get_groups(cfile, NULL);
-       for(i=0;groups[i];i++) {
+       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=p[8].target=&(s.flags);
-               for(j=0;j<9;j++) {
+               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);
                        switch(p[j].ptype) {
                                case PARAM_INT:
-                                       *((gint*)p[j].target) = g_key_file_get_integer(cfile, groups[i], p[j].paramname, &err);
+                                       *((gint*)p[j].target) =
+                                               g_key_file_get_integer(cfile,
+                                                               groups[i],
+                                                               p[j].paramname,
+                                                               &err);
                                        break;
                                case PARAM_STRING:
-                                       *((gchar**)p[j].target) = g_key_file_get_string(cfile, groups[i], p[j].paramname, &err);
+                                       *((gchar**)p[j].target) =
+                                               g_key_file_get_string(cfile,
+                                                               groups[i],
+                                                               p[j].paramname,
+                                                               &err);
                                        break;
                                case PARAM_BOOL:
-                                       value = g_key_file_get_boolean(cfile, groups[i], p[j].paramname, &err);
+                                       value = g_key_file_get_boolean(cfile,
+                                                       groups[i],
+                                                       p[j].paramname, &err);
                                        if(!err) {
                                                *((gint*)p[j].target) |= value;
                                        }
@@ -501,7 +502,7 @@ GArray* parse_cfile(gchar* f, GError** e) {
                                                g_key_file_free(cfile);
                                                return NULL;
                                        } else {
-                                               g_error_free(err);
+                                               g_clear_error(&err);
                                                continue;
                                        }
                                        g_set_error(e, errdomain, CFILE_VALUE_INVALID, "Could not parse %s in group %s: %s", p[j].paramname, groups[i], err->message);
@@ -523,12 +524,12 @@ GArray* parse_cfile(gchar* f, GError** e) {
  * is severely wrong)
  **/
 void sigchld_handler(int s) {
-        int* status=NULL;
+        int status;
        int* i;
        pid_t pid;
 
-       while((pid=wait(status)) > 0) {
-               if(WIFEXITED(status)) {
+       while((pid=waitpid(-1, &status, WNOHANG)) > 0) {
+               if(WIFEXITED(&status)) {
                        msg3(LOG_INFO, "Child exited with %d", WEXITSTATUS(status));
                }
                i=g_hash_table_lookup(children, &pid);
@@ -933,8 +934,8 @@ int splitexport(CLIENT* client) {
        }
        return 0;
 }
-int copyonwrite_prepare(CLIENT* client)
-{
+
+int copyonwrite_prepare(CLIENT* client) {
        off_t i;
        if ((client->difffilename = malloc(1024))==NULL)
                err("Failed to allocate string for diff file name");
@@ -1060,6 +1061,7 @@ void setup_serve(SERVER *serve) {
        struct sockaddr_in addrin;
        struct sigaction sa;
        int addrinlen = sizeof(addrin);
+       int sock_flags;
 #ifndef sun
        int yes=1;
 #else
@@ -1076,6 +1078,14 @@ void setup_serve(SERVER *serve) {
                err("setsockopt SO_KEEPALIVE");
        }
 
+       /* make the listening socket non-blocking */
+       if ((sock_flags = fcntl(serve->socket, F_GETFL, 0)) == -1) {
+               err("fcntl F_GETFL");
+       }
+       if (fcntl(serve->socket, F_SETFL, sock_flags | O_NONBLOCK) == -1) {
+               err("fcntl F_SETFL O_NONBLOCK");
+       }
+
        DEBUG("Waiting for connections... bind, ");
        addrin.sin_family = AF_INET;
        addrin.sin_port = htons(serve->port);
@@ -1116,8 +1126,11 @@ int serveloop(GArray* servers) {
        struct sockaddr_in addrin;
        socklen_t addrinlen=sizeof(addrin);
        SERVER *serve;
-       int i, max, sock;
-       fd_set mset, rset;
+       int i;
+       int max;
+       int sock;
+       fd_set mset;
+       fd_set rset;
        struct timeval tv;
 
        /* 
@@ -1175,7 +1188,7 @@ int serveloop(GArray* servers) {
                                        }
                                        /* 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,serve=(g_array_index(servers, SERVER*, i));i++) {
                                                close(serve->socket);
                                        }
                                        /* FALSE does not free the
@@ -1211,12 +1224,11 @@ int main(int argc, char *argv[]) {
        config_file_pos = g_strdup(CFILE);
        serve=cmdline(argc, argv);
        servers = parse_cfile(config_file_pos, &err);
-       if(!servers) {
-               g_critical("Could not parse command file: %s", err->message);
+       if(!servers || !servers->len) {
+               g_warning("Could not parse config file: %s", err->message);
        }
        if(serve) {
                g_array_append_val(servers, *serve);
-               g_free(serve);
        }
 
 /* We don't support this at this time */
@@ -1242,7 +1254,7 @@ int main(int argc, char *argv[]) {
                return 0;
         }
 #endif
-       if((!serve) && (!servers)) {
+       if((!serve) && (!servers||!servers->len)) {
                g_message("Nothing to do! Bye!");
                exit(EXIT_FAILURE);
        }