r74: * Added checks for GLib (not used yet, but will be in this release)
[nbd.git] / nbd-server.c
index 7ae3415..58050bc 100644 (file)
  *     <wouter@debian.org>
  */
 
+/* Includes LFS defines, which defines behaviours of some of the following
+ * headers, so must come before those */
+#include "config.h"
+#include "lfs.h"
+
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/wait.h>          /* wait */
+#ifdef HAVE_SYS_IOCTL_H
 #include <sys/ioctl.h>
+#endif
 #include <sys/param.h>
+#ifdef HAVE_SYS_MOUNT_H
 #include <sys/mount.h>         /* For BLKGETSIZE */
+#endif
 #include <signal.h>            /* sigaction */
 #include <netinet/tcp.h>
 #include <netinet/in.h>                /* sockaddr_in, htons, in_addr */
@@ -72,6 +81,8 @@
 #include <strings.h>
 #include <dirent.h>
 
+#include <glib.h>
+
 /* used in cliserv.h, so must come first */
 #define MY_NAME "nbd_server"
 #include "cliserv.h"
 
 /** Logging macros, now nothing goes to syslog unless you say ISSERVER */
 #ifdef ISSERVER
-#define msg2(a,b) syslog(a,b)
-#define msg3(a,b,c) syslog(a,b,c)
-#define msg4(a,b,c,d) syslog(a,b,c,d)
+#define msg2(a,b) syslog(a,"%s", b)
+#define msg3(a,b,c) syslog(a,"%s %s", b,c)
+#define msg4(a,b,c,d) syslog(a,"%s %s %s", b,c,d)
 #else
-#define msg2(a,b) do { fprintf(stderr,b) ; fputs("\n",stderr) ; } while(0) 
-#define msg3(a,b,c) do { fprintf(stderr,b,c); fputs("\n",stderr) ; } while(0) 
-#define msg4(a,b,c,d) do { fprintf(stderr,b,c,d); fputs("\n",stderr) ; } while(0)
+#define msg2(a,b) do { fprintf(stderr,"%s\n", b) ; } while(0) 
+#define msg3(a,b,c) do { fprintf(stderr,"%s %s\n", b,c); } while(0) 
+#define msg4(a,b,c,d) do { fprintf(stderr,"%s %s %s\n", b,c,d); } while(0)
 #endif
 
 /* Debugging macros */
 #define DEBUG2( a,b ) 
 #define DEBUG3( a,b,c ) 
 #endif
-/** sending macro... not really required */
-#define SEND writeit( net, &reply, sizeof( reply ));
-/** error macro... not sure whether we really need this */
-#define ERROR { reply.error = htonl(-1); SEND; reply.error = 0; lastpoint = -1; }
 #ifndef PACKAGE_VERSION
 #define PACKAGE_VERSION ""
 #endif
 #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 */
 #define F_COPYONWRITE 4          /**< flag to tell us a file is exported using copyonwrite */
-char difffilename[256]; /**< filename of the copy-on-write file. Doesn't belong here! */
+char difffilename[1024]; /**< filename of the copy-on-write file. Doesn't belong here! */
 unsigned int timeout = 0; /**< disconnect timeout */
 int autoreadonly = 0; /**< 1 = switch to readonly if opening readwrite isn't
                        possible */
@@ -164,7 +171,7 @@ char pidfname[256]; /**< name of our PID file */
 /**
  * Variables associated with a copyonwrite server. Not yet used.
  **/
-typedef struct __cow_opts {
+typedef struct {
        char* difffilename;  /**< filename of the copy-on-write file */
        int difffile;        /**< filedescriptor of copyonwrite file. @todo
                               shouldn't this be an array too? (cfr
@@ -175,9 +182,10 @@ typedef struct __cow_opts {
 } cow_opts;
 
 /**
- * Variables associated with a server. Not yet used.
+ * Variables associated with a server. Not yet used. @todo modify the code to
+ * use an instance of this struct instead of the heap of global variables.
  **/
-typedef struct __nbd_server_opts {
+typedef struct {
        char* exportname;    /**< filename of the file we're exporting */
        unsigned int port;            /**< port we're exporting this file at */
        char* authname;      /**< filename of the authorization file */
@@ -190,13 +198,14 @@ typedef struct __nbd_server_opts {
        int export[1024];    /**< array of filedescriptors of exported files;
                               only the first is actually used unless we're
                               doing the multiple file option */
-       cow_opts* cow;       /**< only used if (flags | F_COPYONWRITE) */
+       cow_opts* cow;       /**< only used if (flags | F_COPYONWRITE) (NULL
+                              otherwise) */
 } nbd_server_opts;
 
 /**
- * Check whether a client is allowed to connect. Works with an
- * authorization file which contains one line per machine, no
- * wildcards.
+ * Check whether a client is allowed to connect. Works with an authorization
+ * file which contains one line per machine, no wildcards.
+ *
  * @param name IP address of client trying to connect (in human-readable form)
  * @return 0 - authorization refused, 1 - OK
  **/
@@ -264,7 +273,8 @@ inline void writeit(int f, void *buf, size_t len)
  * Parse the command line.
  *
  * @todo getopt() is a great thing, and easy to use. Also, we want to
- * create a configuration file which nbd-server will read.
+ * create a configuration file which nbd-server will read. Maybe do (as in,
+ * parse) that here.
  *
  * @param argc the argc argument to main()
  * @param argv the argv argument to main()
@@ -347,15 +357,20 @@ void sigchld_handler(int s)
 {
         int* status=NULL;
        int i;
+       char buf[80];
        pid_t pid;
 
        while((pid=wait(status)) > 0) {
                if(WIFEXITED(status)) {
-                       msg3(LOG_INFO, "Child exited with %d", WEXITSTATUS(status));
+                       memset(buf,'\0', 80);
+                       snprintf(buf, 79, "%d", WEXITSTATUS(status));
+                       msg3(LOG_INFO, "Child exited with ", buf);
                }
                for(i=0;children[i]!=pid&&i<child_arraysize;i++);
                if(i>=child_arraysize) {
-                       msg3(LOG_INFO, "SIGCHLD received for an unknown child with PID %ld",(long) pid);
+                       memset(buf, '\0', 80);
+                       snprintf(buf, 79, "%ld", (long)pid);
+                       msg3(LOG_INFO, "SIGCHLD received for an unknown child with PID ", buf);
                } else {
                        children[i]=(pid_t)0;
                        DEBUG2("Removing %d from the list of children", pid);
@@ -382,7 +397,7 @@ void sigterm_handler(int s) {
        if(parent) {
                unlink(pidfname);
        }
-               
+
        exit(0);
 }
 
@@ -399,15 +414,19 @@ off_t size_autodetect(int export)
        u32 es32;
        struct stat stat_buf;
        int error;
-       
-       DEBUG("looking for export size with lseek SEEK_END\n");
-       es = lseek(export, (off_t)0, SEEK_END);
-       if (es > ((off_t)0)) {
+
+#ifdef HAVE_SYS_MOUNT_H
+#ifdef HAVE_SYS_IOCTL_H
+#ifdef BLKGETSIZE
+       DEBUG("looking for export size with ioctl BLKGETSIZE\n");
+       if (!ioctl(export, BLKGETSIZE, &es32) && es32) {
+               es = (off_t)es32 * (off_t)512;
                return es;
-        } else {
-                DEBUG2("lseek failed: %d", errno==EBADF?1:(errno==ESPIPE?2:(errno==EINVAL?3:4)));
-        }
-       
+       }
+#endif /* BLKGETSIZE */
+#endif /* HAVE_SYS_IOCTL_H */
+#endif /* HAVE_SYS_MOUNT_H */
+
        DEBUG("looking for export size with fstat\n");
        stat_buf.st_size = 0;
        error = fstat(export, &stat_buf);
@@ -416,14 +435,15 @@ off_t size_autodetect(int export)
         } else {
                 err("fstat failed: %m");
         }
-       
-#ifdef BLKGETSIZE
-       DEBUG("looking for export size with ioctl BLKGETSIZE\n");
-       if (!ioctl(export, BLKGETSIZE, &es32) && es32) {
-               es = (off_t)es32 * (off_t)512;
+
+       DEBUG("looking for export size with lseek SEEK_END\n");
+       es = lseek(export, (off_t)0, SEEK_END);
+       if (es > ((off_t)0)) {
                return es;
-       }
-#endif
+        } else {
+                DEBUG2("lseek failed: %d", errno==EBADF?1:(errno==ESPIPE?2:(errno==EINVAL?3:4)));
+        }
+
        err("Could not find size of exported block device: %m");
        return OFFT_MAX;
 }
@@ -468,9 +488,12 @@ int rawexpwrite(off_t a, char *buf, size_t len)
 /**
  * seek to a position in a file, no matter what. Used when using maybeseek is a
  * bad idea (for instance, because we're reading the copyonwrite file instead
- * of the exported file)
+ * of the exported file).
  * @param handle a filedescriptor
  * @param a position to seek to
+ * @todo get rid of this; lastpoint is a global variable right now, but it
+ * shouldn't be. If we pass it on as a parameter, that makes things a *lot*
+ * easier.
  **/
 void myseek(int handle,off_t a) {
        if (lseek(handle, a, SEEK_SET) < 0) {
@@ -611,6 +634,12 @@ void negotiate(int net) {
                err("Negotiation failed: %m");
 }
 
+/** sending macro; not really required. Uses variables in the local
+ * scope of mainloop(). Get rid of it. */
+#define SEND writeit( net, &reply, sizeof( reply ));
+/** error macro; not sure whether we really need this. Uses variables
+ * in the local scope of mainloop(). Get rid of this beast. */
+#define ERROR { reply.error = htonl(-1); SEND; reply.error = 0; lastpoint = -1; }
 /**
  * Serve a file to a single client.
  *
@@ -714,11 +743,16 @@ int mainloop(int net)
  **/
 int splitexport(void) {
        off_t i ;
-       
+
        for (i=0; i<exportsize; i+=hunksize) {
                char exportname3[1024];
-               
-               sprintf(exportname3, exportname2, i/hunksize);
+
+               if(flags & F_MULTIFILE) {
+                       snprintf(exportname3, 1024, "%s.%d", exportname2, (int)(i/hunksize));
+               } else {
+                       strncpy(exportname3, exportname2, 1024);
+               }
+               exportname3[1023]='\0';
                printf( "Opening %s\n", exportname3 );
                if ((export[i/hunksize] = open(exportname3, (flags & F_READONLY) ? O_RDONLY : O_RDWR)) == -1) {
                        /* Read WRITE ACCESS was requested by media is only read only */
@@ -730,8 +764,9 @@ int splitexport(void) {
        }
 
        if (flags & F_COPYONWRITE) {
-               sprintf(difffilename,"%s-%s-%d.diff",exportname2,clientname,
+               snprintf(difffilename, 1024, "%s-%s-%d.diff",exportname2,clientname,
                        (int)getpid()) ;
+               difffilename[1023]='\0';
                msg3(LOG_INFO,"About to create map and diff file %s",difffilename) ;
                difffile=open(difffilename,O_RDWR | O_CREAT | O_TRUNC,0600) ;
                if (difffile<0) err("Could not create diff file (%m)") ;
@@ -750,7 +785,8 @@ int splitexport(void) {
  *
  * @param net A network socket connected to an nbd client
  **/
-void serveconnection(int net) {   
+void serveconnection(int net) {
+       char buf[80];
        splitexport();
        if (exportsize == OFFT_MAX) {
                exportsize = size_autodetect(export[0]);
@@ -758,9 +794,11 @@ void serveconnection(int net) {
        if (exportsize > OFFT_MAX) {
                err("Size of exported file is too big\n");
        }
-       else
-               msg3(LOG_INFO, "size of exported file/device is %Lu",
-                    (unsigned long long)exportsize);
+       else {
+               memset(buf, '\0', 80);
+               snprintf(buf, 79, "%Lu", (unsigned long long)exportsize);
+               msg3(LOG_INFO, "size of exported file/device is ", buf);
+       }
 
        setmysockopt(net);
 
@@ -768,7 +806,7 @@ void serveconnection(int net) {
 }
 
 /**
- * Find the name of the file we have to serve. This will use sprintf()
+ * Find the name of the file we have to serve. This will use snprintf()
  * to put the IP address of the client inside a filename containing
  * "%s". That name is then written to exportname2
  *
@@ -785,7 +823,8 @@ void set_peername(int net,char *clientname)
        if (getpeername( net, (struct sockaddr *) &addrin, &addrinlen ) < 0)
                err("getsockname failed: %m");
        peername = inet_ntoa(addrin.sin_addr);
-       sprintf(exportname2, exportname, peername);
+       snprintf(exportname2, 1024, exportname, peername);
+       exportname2[1023]='\0';
 
        msg4(LOG_INFO, "connect from %s, assigned file is %s", 
             peername, exportname2);