nbd-server.c

Go to the documentation of this file.
00001 /*
00002  * Network Block Device - server
00003  *
00004  * Copyright 1996-1998 Pavel Machek, distribute under GPL
00005  *  <pavel@atrey.karlin.mff.cuni.cz>
00006  * Copyright 2001-2004 Wouter Verhelst <wouter@debian.org>
00007  * Copyright 2002 Anton Altaparmakov <aia21@cam.ac.uk>
00008  *
00009  * Version 1.0 - hopefully 64-bit-clean
00010  * Version 1.1 - merging enhancements from Josh Parsons, <josh@coombs.anu.edu.au>
00011  * Version 1.2 - autodetect size of block devices, thanx to Peter T. Breuer" <ptb@it.uc3m.es>
00012  * Version 1.5 - can compile on Unix systems that don't have 64 bit integer
00013  *      type, or don't have 64 bit file offsets by defining FS_32BIT
00014  *      in compile options for nbd-server *only*. This can be done
00015  *      with make FSCHOICE=-DFS_32BIT nbd-server. (I don't have the
00016  *      original autoconf input file, or I would make it a configure
00017  *      option.) Ken Yap <ken@nlc.net.au>.
00018  * Version 1.6 - fix autodetection of block device size and really make 64 bit
00019  *      clean on 32 bit machines. Anton Altaparmakov <aia21@cam.ac.uk>
00020  * Version 2.0 - Version synchronised with client
00021  * Version 2.1 - Reap zombie client processes when they exit. Removed
00022  *      (uncommented) the _IO magic, it's no longer necessary. Wouter
00023  *      Verhelst <wouter@debian.org>
00024  * Version 2.2 - Auto switch to read-only mode (usefull for floppies).
00025  * Version 2.3 - Fixed code so that Large File Support works. This
00026  *      removes the FS_32BIT compile-time directive; define
00027  *      _FILE_OFFSET_BITS=64 and _LARGEFILE_SOURCE if you used to be
00028  *      using FS_32BIT. This will allow you to use files >2GB instead of
00029  *      having to use the -m option. Wouter Verhelst <wouter@debian.org>
00030  * Version 2.4 - Added code to keep track of children, so that we can
00031  *      properly kill them from initscripts. Add a call to daemon(),
00032  *      so that processes don't think they have to wait for us, which is
00033  *      interesting for initscripts as well. Wouter Verhelst
00034  *      <wouter@debian.org>
00035  * Version 2.5 - Bugfix release: forgot to reset child_arraysize to
00036  *      zero after fork()ing, resulting in nbd-server going berserk
00037  *      when it receives a signal with at least one child open. Wouter
00038  *      Verhelst <wouter@debian.org>
00039  * 10/10/2003 - Added socket option SO_KEEPALIVE (sf.net bug 819235);
00040  *      rectified type of mainloop::size_host (sf.net bugs 814435 and
00041  *      817385); close the PID file after writing to it, so that the
00042  *      daemon can actually be found. Wouter Verhelst
00043  *      <wouter@debian.org>
00044  * 10/10/2003 - Size of the data "size_host" was wrong and so was not
00045  *      correctly put in network endianness. Many types were corrected
00046  *      (size_t and off_t instead of int).  <vspaceg@sourceforge.net>
00047  * Version 2.6 - Some code cleanup.
00048  * Version 2.7 - Better build system.
00049  * 11/02/2004 - Doxygenified the source, modularized it a bit. Needs a 
00050  *      lot more work, but this is a start. Wouter Verhelst
00051  *      <wouter@debian.org>
00052  */
00053 
00054 /* Includes LFS defines, which defines behaviours of some of the following
00055  * headers, so must come before those */
00056 #include "lfs.h"
00057 
00058 #include <sys/types.h>
00059 #include <sys/socket.h>
00060 #include <sys/stat.h>
00061 #include <sys/select.h>         /* select */
00062 #include <sys/wait.h>           /* wait */
00063 #ifdef HAVE_SYS_IOCTL_H
00064 #include <sys/ioctl.h>
00065 #endif
00066 #include <sys/param.h>
00067 #ifdef HAVE_SYS_MOUNT_H
00068 #include <sys/mount.h>          /* For BLKGETSIZE */
00069 #endif
00070 #include <signal.h>             /* sigaction */
00071 #include <netinet/tcp.h>
00072 #include <netinet/in.h>         /* sockaddr_in, htons, in_addr */
00073 #include <netdb.h>              /* hostent, gethostby*, getservby* */
00074 #include <syslog.h>
00075 #include <unistd.h>
00076 #include <stdio.h>
00077 #include <stdlib.h>
00078 #include <string.h>
00079 #include <fcntl.h>
00080 #include <arpa/inet.h>
00081 #include <strings.h>
00082 #include <dirent.h>
00083 #include <unistd.h>
00084 #include <getopt.h>
00085 #include <pwd.h>
00086 #include <grp.h>
00087 
00088 #include <glib.h>
00089 
00090 /* used in cliserv.h, so must come first */
00091 #define MY_NAME "nbd_server"
00092 #include "cliserv.h"
00093 
00094 /** Default position of the config file */
00095 #ifndef SYSCONFDIR
00096 #define SYSCONFDIR "/etc"
00097 #endif
00098 #define CFILE SYSCONFDIR "/nbd-server/config"
00099 
00100 /** Where our config file actually is */
00101 gchar* config_file_pos;
00102 
00103 /** What user we're running as */
00104 gchar* runuser=NULL;
00105 /** What group we're running as */
00106 gchar* rungroup=NULL;
00107 
00108 /** Logging macros, now nothing goes to syslog unless you say ISSERVER */
00109 #ifdef ISSERVER
00110 #define msg2(a,b) syslog(a,b)
00111 #define msg3(a,b,c) syslog(a,b,c)
00112 #define msg4(a,b,c,d) syslog(a,b,c,d)
00113 #else
00114 #define msg2(a,b) g_message(b)
00115 #define msg3(a,b,c) g_message(b,c)
00116 #define msg4(a,b,c,d) g_message(b,c,d)
00117 #endif
00118 
00119 /* Debugging macros */
00120 //#define DODBG
00121 #ifdef DODBG
00122 #define DEBUG( a ) printf( a )
00123 #define DEBUG2( a,b ) printf( a,b )
00124 #define DEBUG3( a,b,c ) printf( a,b,c )
00125 #define DEBUG4( a,b,c,d ) printf( a,b,c,d )
00126 #else
00127 #define DEBUG( a )
00128 #define DEBUG2( a,b ) 
00129 #define DEBUG3( a,b,c ) 
00130 #define DEBUG4( a,b,c,d ) 
00131 #endif
00132 #ifndef PACKAGE_VERSION
00133 #define PACKAGE_VERSION ""
00134 #endif
00135 /**
00136  * The highest value a variable of type off_t can reach. This is a signed
00137  * integer, so set all bits except for the leftmost one.
00138  **/
00139 #define OFFT_MAX ~((off_t)1<<(sizeof(off_t)*8-1))
00140 #define LINELEN 256       /**< Size of static buffer used to read the
00141                             authorization file (yuck) */
00142 #define BUFSIZE (1024*1024) /**< Size of buffer that can hold requests */
00143 #define DIFFPAGESIZE 4096 /**< diff file uses those chunks */
00144 #define F_READONLY 1      /**< flag to tell us a file is readonly */
00145 #define F_MULTIFILE 2     /**< flag to tell us a file is exported using -m */
00146 #define F_COPYONWRITE 4   /**< flag to tell us a file is exported using
00147                             copyonwrite */
00148 #define F_AUTOREADONLY 8  /**< flag to tell us a file is set to autoreadonly */
00149 #define F_SPARSE 16
00150 GHashTable *children;
00151 char pidfname[256]; /**< name of our PID file */
00152 char pidftemplate[256]; /**< template to be used for the filename of the PID file */
00153 char default_authname[] = SYSCONFDIR "/nbd-server/allow"; /**< default name of allow file */
00154 
00155 /**
00156  * Types of virtuatlization
00157  **/
00158 typedef enum {
00159         VIRT_NONE=0,    /**< No virtualization */
00160         VIRT_IPLIT,     /**< Literal IP address as part of the filename */
00161         VIRT_IPHASH,    /**< Replacing all dots in an ip address by a / before
00162                              doing the same as in IPLIT */
00163         VIRT_CIDR,      /**< Every subnet in its own directory */
00164 } VIRT_STYLE;
00165 
00166 /**
00167  * Variables associated with a server.
00168  **/
00169 typedef struct {
00170         gchar* exportname;    /**< (unprocessed) filename of the file we're exporting */
00171         off_t expected_size; /**< size of the exported file as it was told to
00172                                us through configuration */
00173         unsigned int port;   /**< port we're exporting this file at */
00174         char* authname;      /**< filename of the authorization file */
00175         int flags;           /**< flags associated with this exported file */
00176         unsigned int timeout;/**< how long a connection may be idle
00177                                (0=forever) */
00178         int socket;          /**< The socket of this server. */
00179         VIRT_STYLE virtstyle;/**< The style of virtualization, if any */
00180         uint8_t cidrlen;     /**< The length of the mask when we use
00181                                   CIDR-style virtualization */
00182 } SERVER;
00183 
00184 /**
00185  * Variables associated with a client socket.
00186  **/
00187 typedef struct {
00188         int fhandle;      /**< file descriptor */
00189         off_t startoff;   /**< starting offset of this file */
00190 } FILE_INFO;
00191 
00192 typedef struct {
00193         off_t exportsize;    /**< size of the file we're exporting */
00194         char *clientname;    /**< peer */
00195         char *exportname;    /**< (processed) filename of the file we're exporting */
00196         GArray *export;    /**< array of FILE_INFO of exported files;
00197                                array size is always 1 unless we're
00198                                doing the multiple file option */
00199         int net;             /**< The actual client socket */
00200         SERVER *server;      /**< The server this client is getting data from */
00201         char* difffilename;  /**< filename of the copy-on-write file, if any */
00202         int difffile;        /**< filedescriptor of copyonwrite file. @todo
00203                                shouldn't this be an array too? (cfr export) Or
00204                                make -m and -c mutually exclusive */
00205         u32 difffilelen;     /**< number of pages in difffile */
00206         u32 *difmap;         /**< see comment on the global difmap for this one */
00207 } CLIENT;
00208 
00209 /**
00210  * Type of configuration file values
00211  **/
00212 typedef enum {
00213         PARAM_INT,              /**< This parameter is an integer */
00214         PARAM_STRING,           /**< This parameter is a string */
00215         PARAM_BOOL,             /**< This parameter is a boolean */
00216 } PARAM_TYPE;
00217 
00218 /**
00219  * Configuration file values
00220  **/
00221 typedef struct {
00222         gchar *paramname;       /**< Name of the parameter, as it appears in
00223                                   the config file */
00224         gboolean required;      /**< Whether this is a required (as opposed to
00225                                   optional) parameter */
00226         PARAM_TYPE ptype;       /**< Type of the parameter. */
00227         gpointer target;        /**< Pointer to where the data of this
00228                                   parameter should be written. If ptype is
00229                                   PARAM_BOOL, the data is or'ed rather than
00230                                   overwritten. */
00231         gint flagval;           /**< Flag mask for this parameter in case ptype
00232                                   is PARAM_BOOL. */
00233 } PARAM;
00234 
00235 /**
00236  * Check whether a client is allowed to connect. Works with an authorization
00237  * file which contains one line per machine, no wildcards.
00238  *
00239  * @param opts The client who's trying to connect.
00240  * @return 0 - authorization refused, 1 - OK
00241  **/
00242 int authorized_client(CLIENT *opts) {
00243         const char *ERRMSG="Invalid entry '%s' in authfile '%s', so, refusing all connections.";
00244         FILE *f ;
00245         char line[LINELEN]; 
00246         char *tmp;
00247         struct in_addr addr;
00248         struct in_addr client;
00249         struct in_addr cltemp;
00250         int len;
00251 
00252         if ((f=fopen(opts->server->authname,"r"))==NULL) {
00253                 msg4(LOG_INFO,"Can't open authorization file %s (%s).",
00254                      opts->server->authname,strerror(errno)) ;
00255                 return 1 ; 
00256         }
00257   
00258         inet_aton(opts->clientname, &client);
00259         while (fgets(line,LINELEN,f)!=NULL) {
00260                 if((tmp=index(line, '/'))) {
00261                         if(strlen(line)<=tmp-line) {
00262                                 msg4(LOG_CRIT, ERRMSG, line, opts->server->authname);
00263                                 return 0;
00264                         }
00265                         *(tmp++)=0;
00266                         if(inet_aton(line,&addr)) {
00267                                 msg4(LOG_CRIT, ERRMSG, line, opts->server->authname);
00268                                 return 0;
00269                         }
00270                         len=strtol(tmp, NULL, 0);
00271                         addr.s_addr>>=32-len;
00272                         addr.s_addr<<=32-len;
00273                         memcpy(&cltemp,&client,sizeof(client));
00274                         cltemp.s_addr>>=32-len;
00275                         cltemp.s_addr<<=32-len;
00276                         if(addr.s_addr == cltemp.s_addr) {
00277                                 return 1;
00278                         }
00279                 }
00280                 if (strncmp(line,opts->clientname,strlen(opts->clientname))==0) {
00281                         fclose(f);
00282                         return 1;
00283                 }
00284         }
00285         fclose(f);
00286         return 0;
00287 }
00288 
00289 /**
00290  * Read data from a file descriptor into a buffer
00291  *
00292  * @param f a file descriptor
00293  * @param buf a buffer
00294  * @param len the number of bytes to be read
00295  **/
00296 inline void readit(int f, void *buf, size_t len) {
00297         ssize_t res;
00298         while (len > 0) {
00299                 DEBUG("*");
00300                 if ((res = read(f, buf, len)) <= 0)
00301                         err("Read failed: %m");
00302                 len -= res;
00303                 buf += res;
00304         }
00305 }
00306 
00307 /**
00308  * Write data from a buffer into a filedescriptor
00309  *
00310  * @param f a file descriptor
00311  * @param buf a buffer containing data
00312  * @param len the number of bytes to be written
00313  **/
00314 inline void writeit(int f, void *buf, size_t len) {
00315         ssize_t res;
00316         while (len > 0) {
00317                 DEBUG("+");
00318                 if ((res = write(f, buf, len)) <= 0)
00319                         err("Send failed: %m");
00320                 len -= res;
00321                 buf += res;
00322         }
00323 }
00324 
00325 /**
00326  * Print out a message about how to use nbd-server. Split out to a separate
00327  * function so that we can call it from multiple places
00328  */
00329 void usage() {
00330         printf("This is nbd-server version " VERSION "\n");
00331         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"
00332                "\t-r|--read-only\t\tread only\n"
00333                "\t-m|--multi-file\t\tmultiple file\n"
00334                "\t-c|--copy-on-write\tcopy on write\n"
00335                "\t-C|--config-file\tspecify an alternate configuration file\n"
00336                "\t-l|--authorize-file\tfile with list of hosts that are allowed to\n\t\t\t\tconnect.\n"
00337                "\t-a|--idle-time\t\tmaximum idle seconds; server terminates when\n\t\t\t\tidle time exceeded\n"
00338                "\t-p|--pid-file\t\tspecify a filename to write our PID to\n"
00339                "\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"
00340                "\tif port is set to 0, stdin is used (for running from inetd)\n"
00341                "\tif file_to_export contains '%%s', it is substituted with the IP\n"
00342                "\t\taddress of the machine trying to connect\n" );
00343         printf("Using configuration file %s\n", CFILE);
00344 }
00345 
00346 /* Dumps a config file section of the given SERVER*, and exits. */
00347 void dump_section(SERVER* serve, gchar* section_header) {
00348         printf("[%s]\n", section_header);
00349         printf("\texportname = %s\n", serve->exportname);
00350         printf("\tport = %d\n", serve->port);
00351         if(serve->flags & F_READONLY) {
00352                 printf("\treadonly = true\n");
00353         }
00354         if(serve->flags & F_MULTIFILE) {
00355                 printf("\tmultifile = true\n");
00356         }
00357         if(serve->flags & F_COPYONWRITE) {
00358                 printf("\tcopyonwrite = true\n");
00359         }
00360         if(serve->expected_size) {
00361                 printf("\tfilesize = %Ld\n", (long long int)serve->expected_size);
00362         }
00363         if(serve->authname) {
00364                 printf("\tauthfile = %s\n", serve->authname);
00365         }
00366         if(serve->timeout) {
00367                 printf("\ttimeout = %d\n", serve->timeout);
00368         }
00369         exit(EXIT_SUCCESS);
00370 }
00371 
00372 /**
00373  * Parse the command line.
00374  *
00375  * @param argc the argc argument to main()
00376  * @param argv the argv argument to main()
00377  **/
00378 SERVER* cmdline(int argc, char *argv[]) {
00379         int i=0;
00380         int nonspecial=0;
00381         int c;
00382         struct option long_options[] = {
00383                 {"read-only", no_argument, NULL, 'r'},
00384                 {"multi-file", no_argument, NULL, 'm'},
00385                 {"copy-on-write", no_argument, NULL, 'c'},
00386                 {"authorize-file", required_argument, NULL, 'l'},
00387                 {"idle-time", required_argument, NULL, 'a'},
00388                 {"config-file", required_argument, NULL, 'C'},
00389                 {"pid-file", required_argument, NULL, 'p'},
00390                 {"output-config", required_argument, NULL, 'o'},
00391                 {0,0,0,0}
00392         };
00393         SERVER *serve;
00394         off_t es;
00395         size_t last;
00396         char suffix;
00397         gboolean do_output=FALSE;
00398         gchar* section_header=NULL;
00399 
00400         if(argc==1) {
00401                 return NULL;
00402         }
00403         serve=g_new0(SERVER, 1);
00404         serve->authname = g_strdup(default_authname);
00405         while((c=getopt_long(argc, argv, "-a:C:cl:mo:rp:", long_options, &i))>=0) {
00406                 switch (c) {
00407                 case 1:
00408                         /* non-option argument */
00409                         switch(nonspecial++) {
00410                         case 0:
00411                                 serve->port=strtol(optarg, NULL, 0);
00412                                 break;
00413                         case 1:
00414                                 serve->exportname = g_strdup(optarg);
00415                                 if(serve->exportname[0] != '/') {
00416                                         fprintf(stderr, "E: The to be exported file needs to be an absolute filename!\n");
00417                                         exit(EXIT_FAILURE);
00418                                 }
00419                                 break;
00420                         case 2:
00421                                 last=strlen(optarg)-1;
00422                                 suffix=optarg[last];
00423                                 if (suffix == 'k' || suffix == 'K' ||
00424                                     suffix == 'm' || suffix == 'M')
00425                                         optarg[last] = '\0';
00426                                 es = (off_t)atol(optarg);
00427                                 switch (suffix) {
00428                                         case 'm':
00429                                         case 'M':  es <<= 10;
00430                                         case 'k':
00431                                         case 'K':  es <<= 10;
00432                                         default :  break;
00433                                 }
00434                                 serve->expected_size = es;
00435                                 break;
00436                         }
00437                         break;
00438                 case 'r':
00439                         serve->flags |= F_READONLY;
00440                         break;
00441                 case 'm':
00442                         serve->flags |= F_MULTIFILE;
00443                         break;
00444                 case 'o':
00445                         do_output = TRUE;
00446                         section_header = g_strdup(optarg);
00447                         break;
00448                 case 'p':
00449                         strncpy(pidftemplate, optarg, 256);
00450                         break;
00451                 case 'c': 
00452                         serve->flags |=F_COPYONWRITE;
00453                         break;
00454                 case 'C':
00455                         g_free(config_file_pos);
00456                         config_file_pos=g_strdup(optarg);
00457                         break;
00458                 case 'l':
00459                         g_free(serve->authname);
00460                         serve->authname=g_strdup(optarg);
00461                         break;
00462                 case 'a': 
00463                         serve->timeout=strtol(optarg, NULL, 0);
00464                         break;
00465                 default:
00466                         usage();
00467                         exit(EXIT_FAILURE);
00468                         break;
00469                 }
00470         }
00471         /* What's left: the port to export, the name of the to be exported
00472          * file, and, optionally, the size of the file, in that order. */
00473         if(nonspecial<2) {
00474                 g_free(serve);
00475                 serve=NULL;
00476         }
00477         if(do_output) {
00478                 if(!serve) {
00479                         g_critical("Need a complete configuration on the command line to output a config file section!");
00480                         exit(EXIT_FAILURE);
00481                 }
00482                 dump_section(serve, section_header);
00483         }
00484         return serve;
00485 }
00486 
00487 /**
00488  * Error codes for config file parsing
00489  **/
00490 typedef enum {
00491         CFILE_NOTFOUND,         /**< The configuration file is not found */
00492         CFILE_MISSING_GENERIC,  /**< The (required) group "generic" is missing */
00493         CFILE_KEY_MISSING,      /**< A (required) key is missing */
00494         CFILE_VALUE_INVALID,    /**< A value is syntactically invalid */
00495         CFILE_PROGERR           /**< Programmer error */
00496 } CFILE_ERRORS;
00497 
00498 /**
00499  * Remove a SERVER from memory. Used from the hash table
00500  **/
00501 void remove_server(gpointer s) {
00502         SERVER *server;
00503 
00504         server=(SERVER*)s;
00505         g_free(server->exportname);
00506         if(server->authname)
00507                 g_free(server->authname);
00508         g_free(server);
00509 }
00510 
00511 /**
00512  * Parse the config file.
00513  *
00514  * @param f the name of the config file
00515  * @param e a GError. @see CFILE_ERRORS for what error values this function can
00516  *      return.
00517  * @return a Array of SERVER* pointers, If the config file is empty or does not
00518  *      exist, returns an empty GHashTable; if the config file contains an
00519  *      error, returns NULL, and e is set appropriately
00520  **/
00521 GArray* parse_cfile(gchar* f, GError** e) {
00522         const char* DEFAULT_ERROR = "Could not parse %s in group %s: %s";
00523         const char* MISSING_REQUIRED_ERROR = "Could not find required value %s in group %s: %s";
00524         SERVER s;
00525         gchar *virtstyle=NULL;
00526         PARAM lp[] = {
00527                 { "exportname", TRUE,   PARAM_STRING,   NULL, 0 },
00528                 { "port",       TRUE,   PARAM_INT,      NULL, 0 },
00529                 { "authfile",   FALSE,  PARAM_STRING,   NULL, 0 },
00530                 { "timeout",    FALSE,  PARAM_INT,      NULL, 0 },
00531                 { "filesize",   FALSE,  PARAM_INT,      NULL, 0 },
00532                 { "virtstyle",  FALSE,  PARAM_STRING,   NULL, 0 },
00533                 { "readonly",   FALSE,  PARAM_BOOL,     NULL, F_READONLY },
00534                 { "multifile",  FALSE,  PARAM_BOOL,     NULL, F_MULTIFILE },
00535                 { "copyonwrite", FALSE, PARAM_BOOL,     NULL, F_COPYONWRITE },
00536                 { "autoreadonly", FALSE, PARAM_BOOL,    NULL, F_AUTOREADONLY },
00537                 { "sparse_cow", FALSE,  PARAM_BOOL,     NULL, F_SPARSE },
00538         };
00539         const int lp_size=11;
00540         PARAM gp[] = {
00541                 { "user",       FALSE, PARAM_STRING,    &runuser,       0 },
00542                 { "group",      FALSE, PARAM_STRING,    &rungroup,      0 },
00543         };
00544         PARAM* p=gp;
00545         int p_size=2;
00546         GKeyFile *cfile;
00547         GError *err = NULL;
00548         const char *err_msg=NULL;
00549         GQuark errdomain;
00550         GArray *retval=NULL;
00551         gchar **groups;
00552         gboolean value;
00553         gint i;
00554         gint j;
00555 
00556         errdomain = g_quark_from_string("parse_cfile");
00557         cfile = g_key_file_new();
00558         retval = g_array_new(FALSE, TRUE, sizeof(SERVER));
00559         if(!g_key_file_load_from_file(cfile, f, G_KEY_FILE_KEEP_COMMENTS |
00560                         G_KEY_FILE_KEEP_TRANSLATIONS, &err)) {
00561                 g_set_error(e, errdomain, CFILE_NOTFOUND, "Could not open config file.");
00562                 g_key_file_free(cfile);
00563                 return retval;
00564         }
00565         if(strcmp(g_key_file_get_start_group(cfile), "generic")) {
00566                 g_set_error(e, errdomain, CFILE_MISSING_GENERIC, "Config file does not contain the [generic] group!");
00567                 g_key_file_free(cfile);
00568                 return NULL;
00569         }
00570         groups = g_key_file_get_groups(cfile, NULL);
00571         for(i=0;groups[i];i++) {
00572                 memset(&s, '\0', sizeof(SERVER));
00573                 lp[0].target=&(s.exportname);
00574                 lp[1].target=&(s.port);
00575                 lp[2].target=&(s.authname);
00576                 lp[3].target=&(s.timeout);
00577                 lp[4].target=&(s.expected_size);
00578                 lp[5].target=&(virtstyle);
00579                 lp[6].target=lp[7].target=lp[8].target=
00580                                 lp[9].target=lp[10].target=&(s.flags);
00581                 /* After the [generic] group, start parsing exports */
00582                 if(i==1) {
00583                         p=lp;
00584                         p_size=lp_size;
00585                 } 
00586                 for(j=0;j<p_size;j++) {
00587                         g_assert(p[j].target != NULL);
00588                         g_assert(p[j].ptype==PARAM_INT||p[j].ptype==PARAM_STRING||p[j].ptype==PARAM_BOOL);
00589                         switch(p[j].ptype) {
00590                                 case PARAM_INT:
00591                                         *((gint*)p[j].target) =
00592                                                 g_key_file_get_integer(cfile,
00593                                                                 groups[i],
00594                                                                 p[j].paramname,
00595                                                                 &err);
00596                                         break;
00597                                 case PARAM_STRING:
00598                                         *((gchar**)p[j].target) =
00599                                                 g_key_file_get_string(cfile,
00600                                                                 groups[i],
00601                                                                 p[j].paramname,
00602                                                                 &err);
00603                                         break;
00604                                 case PARAM_BOOL:
00605                                         value = g_key_file_get_boolean(cfile,
00606                                                         groups[i],
00607                                                         p[j].paramname, &err);
00608                                         if(!err) {
00609                                                 if(value) {
00610                                                         *((gint*)p[j].target) |= p[j].flagval;
00611                                                 } else {
00612                                                         *((gint*)p[j].target) &= ~(p[j].flagval);
00613                                                 }
00614                                         }
00615                                         break;
00616                         }
00617                         if(err) {
00618                                 if(err->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND) {
00619                                         if(!p[j].required) {
00620                                                 /* Ignore not-found error for optional values */
00621                                                 g_clear_error(&err);
00622                                                 continue;
00623                                         } else {
00624                                                 err_msg = MISSING_REQUIRED_ERROR;
00625                                         }
00626                                 } else {
00627                                         err_msg = DEFAULT_ERROR;
00628                                 }
00629                                 g_set_error(e, errdomain, CFILE_VALUE_INVALID, err_msg, p[j].paramname, groups[i], err->message);
00630                                 g_array_free(retval, TRUE);
00631                                 g_error_free(err);
00632                                 g_key_file_free(cfile);
00633                                 return NULL;
00634                         }
00635                 }
00636                 if(virtstyle) {
00637                         if(!strncmp(virtstyle, "none", 4)) {
00638                                 s.virtstyle=VIRT_NONE;
00639                         } else if(!strncmp(virtstyle, "ipliteral", 9)) {
00640                                 s.virtstyle=VIRT_IPLIT;
00641                         } else if(!strncmp(virtstyle, "iphash", 6)) {
00642                                 s.virtstyle=VIRT_IPHASH;
00643                         } else if(!strncmp(virtstyle, "cidrhash", 8)) {
00644                                 s.virtstyle=VIRT_CIDR;
00645                                 if(strlen(virtstyle)<10) {
00646                                         g_set_error(e, errdomain, CFILE_VALUE_INVALID, "Invalid value %s for parameter virtstyle in group %s: missing length", virtstyle, groups[i]);
00647                                         g_array_free(retval, TRUE);
00648                                         g_key_file_free(cfile);
00649                                         return NULL;
00650                                 }
00651                                 s.cidrlen=strtol(virtstyle+8, NULL, 0);
00652                         } else {
00653                                 g_set_error(e, errdomain, CFILE_VALUE_INVALID, "Invalid value %s for parameter virtstyle in group %s", virtstyle, groups[i]);
00654                                 g_array_free(retval, TRUE);
00655                                 g_key_file_free(cfile);
00656                                 return NULL;
00657                         }
00658                 } else {
00659                         s.virtstyle=VIRT_IPLIT;
00660                 }
00661                 /* Don't need to free this, it's not our string */
00662                 virtstyle=NULL;
00663                 /* Don't append values for the [generic] group */
00664                 if(i>0) {
00665                         g_array_append_val(retval, s);
00666                 }
00667         }
00668         return retval;
00669 }
00670 
00671 /**
00672  * Signal handler for SIGCHLD
00673  * @param s the signal we're handling (must be SIGCHLD, or something
00674  * is severely wrong)
00675  **/
00676 void sigchld_handler(int s) {
00677         int status;
00678         int* i;
00679         pid_t pid;
00680 
00681         while((pid=waitpid(-1, &status, WNOHANG)) > 0) {
00682                 if(WIFEXITED(status)) {
00683                         msg3(LOG_INFO, "Child exited with %d", WEXITSTATUS(status));
00684                 }
00685                 i=g_hash_table_lookup(children, &pid);
00686                 if(!i) {
00687                         msg3(LOG_INFO, "SIGCHLD received for an unknown child with PID %ld", (long)pid);
00688                 } else {
00689                         DEBUG2("Removing %d from the list of children", pid);
00690                         g_hash_table_remove(children, &pid);
00691                 }
00692         }
00693 }
00694 
00695 /**
00696  * Kill a child. Called from sigterm_handler::g_hash_table_foreach.
00697  *
00698  * @param key the key
00699  * @param value the value corresponding to the above key
00700  * @param user_data a pointer which we always set to 1, so that we know what
00701  * will happen next.
00702  **/
00703 void killchild(gpointer key, gpointer value, gpointer user_data) {
00704         pid_t *pid=value;
00705         int *parent=user_data;
00706 
00707         kill(*pid, SIGTERM);
00708         *parent=1;
00709 }
00710 
00711 /**
00712  * Handle SIGTERM and dispatch it to our children
00713  * @param s the signal we're handling (must be SIGTERM, or something
00714  * is severely wrong).
00715  **/
00716 void sigterm_handler(int s) {
00717         int parent=0;
00718 
00719         g_hash_table_foreach(children, killchild, &parent);
00720 
00721         if(parent) {
00722                 unlink(pidfname);
00723         }
00724 
00725         exit(0);
00726 }
00727 
00728 /**
00729  * Detect the size of a file.
00730  *
00731  * @param fhandle An open filedescriptor
00732  * @return the size of the file, or OFFT_MAX if detection was
00733  * impossible.
00734  **/
00735 off_t size_autodetect(int fhandle) {
00736         off_t es;
00737         unsigned long sectors;
00738         struct stat stat_buf;
00739         int error;
00740 
00741 #ifdef HAVE_SYS_MOUNT_H
00742 #ifdef HAVE_SYS_IOCTL_H
00743 #ifdef BLKGETSIZE
00744         DEBUG("looking for export size with ioctl BLKGETSIZE\n");
00745         if (!ioctl(fhandle, BLKGETSIZE, &sectors) && sectors) {
00746                 es = (off_t)sectors * (off_t)512;
00747                 return es;
00748         }
00749 #endif /* BLKGETSIZE */
00750 #endif /* HAVE_SYS_IOCTL_H */
00751 #endif /* HAVE_SYS_MOUNT_H */
00752 
00753         DEBUG("looking for fhandle size with fstat\n");
00754         stat_buf.st_size = 0;
00755         error = fstat(fhandle, &stat_buf);
00756         if (!error) {
00757                 if(stat_buf.st_size > 0)
00758                         return (off_t)stat_buf.st_size;
00759         } else {
00760                 err("fstat failed: %m");
00761         }
00762 
00763         DEBUG("looking for fhandle size with lseek SEEK_END\n");
00764         es = lseek(fhandle, (off_t)0, SEEK_END);
00765         if (es > ((off_t)0)) {
00766                 return es;
00767         } else {
00768                 DEBUG2("lseek failed: %d", errno==EBADF?1:(errno==ESPIPE?2:(errno==EINVAL?3:4)));
00769         }
00770 
00771         err("Could not find size of exported block device: %m");
00772         return OFFT_MAX;
00773 }
00774 
00775 /**
00776  * Get the file handle and offset, given an export offset.
00777  *
00778  * @param export An array of export files
00779  * @param a The offset to get corresponding file/offset for
00780  * @param fhandle [out] File descriptor
00781  * @param foffset [out] Offset into fhandle
00782  * @param maxbytes [out] Tells how many bytes can be read/written
00783  * from fhandle starting at foffset (0 if there is no limit)
00784  * @return 0 on success, -1 on failure
00785  **/
00786 int get_filepos(GArray* export, off_t a, int* fhandle, off_t* foffset, size_t* maxbytes ) {
00787         /* Negative offset not allowed */
00788         if(a < 0)
00789                 return -1;
00790 
00791         /* Binary search for last file with starting offset <= a */
00792         FILE_INFO fi;
00793         int start = 0;
00794         int end = export->len - 1;
00795         while( start <= end ) {
00796                 int mid = (start + end) / 2;
00797                 fi = g_array_index(export, FILE_INFO, mid);
00798                 if( fi.startoff < a ) {
00799                         start = mid + 1;
00800                 } else if( fi.startoff > a ) {
00801                         end = mid - 1;
00802                 } else {
00803                         start = end = mid;
00804                         break;
00805                 }
00806         }
00807 
00808         /* end should never go negative, since first startoff is 0 and a >= 0 */
00809         g_assert(end >= 0);
00810 
00811         fi = g_array_index(export, FILE_INFO, end);
00812         *fhandle = fi.fhandle;
00813         *foffset = a - fi.startoff;
00814         *maxbytes = 0;
00815         if( end+1 < export->len ) {
00816                 FILE_INFO fi_next = g_array_index(export, FILE_INFO, end+1);
00817                 *maxbytes = fi_next.startoff - a;
00818         }
00819 
00820         return 0;
00821 }
00822 
00823 /**
00824  * seek to a position in a file, with error handling.
00825  * @param handle a filedescriptor
00826  * @param a position to seek to
00827  * @todo get rid of this; lastpoint is a global variable right now, but it
00828  * shouldn't be. If we pass it on as a parameter, that makes things a *lot*
00829  * easier.
00830  **/
00831 void myseek(int handle,off_t a) {
00832         if (lseek(handle, a, SEEK_SET) < 0) {
00833                 err("Can not seek locally!\n");
00834         }
00835 }
00836 
00837 /**
00838  * Write an amount of bytes at a given offset to the right file. This
00839  * abstracts the write-side of the multiple file option.
00840  *
00841  * @param a The offset where the write should start
00842  * @param buf The buffer to write from
00843  * @param len The length of buf
00844  * @param client The client we're serving for
00845  * @return The number of bytes actually written, or -1 in case of an error
00846  **/
00847 ssize_t rawexpwrite(off_t a, char *buf, size_t len, CLIENT *client) {
00848         int fhandle;
00849         off_t foffset;
00850         size_t maxbytes;
00851 
00852         if(get_filepos(client->export, a, &fhandle, &foffset, &maxbytes))
00853                 return -1;
00854         if(maxbytes && len > maxbytes)
00855                 len = maxbytes;
00856 
00857         DEBUG4("(WRITE to fd %d offset %Lu len %u), ", fhandle, foffset, len);
00858 
00859         myseek(fhandle, foffset);
00860         return write(fhandle, buf, len);
00861 }
00862 
00863 /**
00864  * Call rawexpwrite repeatedly until all data has been written.
00865  * @return 0 on success, nonzero on failure
00866  **/
00867 int rawexpwrite_fully(off_t a, char *buf, size_t len, CLIENT *client) {
00868         ssize_t ret = 0;
00869 
00870         while(len > 0 && (ret=rawexpwrite(a, buf, len, client)) > 0 ) {
00871                 a += ret;
00872                 buf += ret;
00873                 len -= ret;
00874         }
00875         return (ret < 0 || len != 0);
00876 }
00877 
00878 /**
00879  * Read an amount of bytes at a given offset from the right file. This
00880  * abstracts the read-side of the multiple files option.
00881  *
00882  * @param a The offset where the read should start
00883  * @param buf A buffer to read into
00884  * @param len The size of buf
00885  * @param client The client we're serving for
00886  * @return The number of bytes actually read, or -1 in case of an
00887  * error.
00888  **/
00889 ssize_t rawexpread(off_t a, char *buf, size_t len, CLIENT *client) {
00890         int fhandle;
00891         off_t foffset;
00892         size_t maxbytes;
00893 
00894         if(get_filepos(client->export, a, &fhandle, &foffset, &maxbytes))
00895                 return -1;
00896         if(maxbytes && len > maxbytes)
00897                 len = maxbytes;
00898 
00899         DEBUG4("(READ from fd %d offset %Lu len %u), ", fhandle, foffset, len);
00900 
00901         myseek(fhandle, foffset);
00902         return read(fhandle, buf, len);
00903 }
00904 
00905 /**
00906  * Call rawexpread repeatedly until all data has been read.
00907  * @return 0 on success, nonzero on failure
00908  **/
00909 int rawexpread_fully(off_t a, char *buf, size_t len, CLIENT *client) {
00910         ssize_t ret = 0;
00911 
00912         while(len > 0 && (ret=rawexpread(a, buf, len, client)) > 0 ) {
00913                 a += ret;
00914                 buf += ret;
00915                 len -= ret;
00916         }
00917         return (ret < 0 || len != 0);
00918 }
00919 
00920 /**
00921  * Read an amount of bytes at a given offset from the right file. This
00922  * abstracts the read-side of the copyonwrite stuff, and calls
00923  * rawexpread() with the right parameters to do the actual work.
00924  * @param a The offset where the read should start
00925  * @param buf A buffer to read into
00926  * @param len The size of buf
00927  * @param client The client we're going to read for
00928  * @return 0 on success, nonzero on failure
00929  **/
00930 int expread(off_t a, char *buf, size_t len, CLIENT *client) {
00931         off_t rdlen, offset;
00932         off_t mapcnt, mapl, maph, pagestart;
00933 
00934         if (!(client->server->flags & F_COPYONWRITE))
00935                 return(rawexpread_fully(a, buf, len, client));
00936         DEBUG3("Asked to read %d bytes at %Lu.\n", len, (unsigned long long)a);
00937 
00938         mapl=a/DIFFPAGESIZE; maph=(a+len-1)/DIFFPAGESIZE;
00939 
00940         for (mapcnt=mapl;mapcnt<=maph;mapcnt++) {
00941                 pagestart=mapcnt*DIFFPAGESIZE;
00942                 offset=a-pagestart;
00943                 rdlen=(0<DIFFPAGESIZE-offset && len<(size_t)(DIFFPAGESIZE-offset)) ?
00944                         len : (size_t)DIFFPAGESIZE-offset;
00945                 if (client->difmap[mapcnt]!=(u32)(-1)) { /* the block is already there */
00946                         DEBUG3("Page %Lu is at %lu\n", (unsigned long long)mapcnt,
00947                                (unsigned long)(client->difmap[mapcnt]));
00948                         myseek(client->difffile, client->difmap[mapcnt]*DIFFPAGESIZE+offset);
00949                         if (read(client->difffile, buf, rdlen) != rdlen) return -1;
00950                 } else { /* the block is not there */
00951                         DEBUG2("Page %Lu is not here, we read the original one\n",
00952                                (unsigned long long)mapcnt);
00953                         if(rawexpread_fully(a, buf, rdlen, client)) return -1;
00954                 }
00955                 len-=rdlen; a+=rdlen; buf+=rdlen;
00956         }
00957         return 0;
00958 }
00959 
00960 /**
00961  * Write an amount of bytes at a given offset to the right file. This
00962  * abstracts the write-side of the copyonwrite option, and calls
00963  * rawexpwrite() with the right parameters to do the actual work.
00964  *
00965  * @param a The offset where the write should start
00966  * @param buf The buffer to write from
00967  * @param len The length of buf
00968  * @param client The client we're going to write for.
00969  * @return 0 on success, nonzero on failure
00970  **/
00971 int expwrite(off_t a, char *buf, size_t len, CLIENT *client) {
00972         char pagebuf[DIFFPAGESIZE];
00973         off_t mapcnt,mapl,maph;
00974         off_t wrlen,rdlen; 
00975         off_t pagestart;
00976         off_t offset;
00977 
00978         if (!(client->server->flags & F_COPYONWRITE))
00979                 return(rawexpwrite_fully(a, buf, len, client)); 
00980         DEBUG3("Asked to write %d bytes at %Lu.\n", len, (unsigned long long)a);
00981 
00982         mapl=a/DIFFPAGESIZE ; maph=(a+len-1)/DIFFPAGESIZE ;
00983 
00984         for (mapcnt=mapl;mapcnt<=maph;mapcnt++) {
00985                 pagestart=mapcnt*DIFFPAGESIZE ;
00986                 offset=a-pagestart ;
00987                 wrlen=(0<DIFFPAGESIZE-offset && len<(size_t)(DIFFPAGESIZE-offset)) ?
00988                         len : (size_t)DIFFPAGESIZE-offset;
00989 
00990                 if (client->difmap[mapcnt]!=(u32)(-1)) { /* the block is already there */
00991                         DEBUG3("Page %Lu is at %lu\n", (unsigned long long)mapcnt,
00992                                (unsigned long)(client->difmap[mapcnt])) ;
00993                         myseek(client->difffile,
00994                                         client->difmap[mapcnt]*DIFFPAGESIZE+offset);
00995                         if (write(client->difffile, buf, wrlen) != wrlen) return -1 ;
00996                 } else { /* the block is not there */
00997                         myseek(client->difffile,client->difffilelen*DIFFPAGESIZE) ;
00998                         client->difmap[mapcnt]=(client->server->flags&F_SPARSE)?mapcnt:client->difffilelen++;
00999                         DEBUG3("Page %Lu is not here, we put it at %lu\n",
01000                                (unsigned long long)mapcnt,
01001                                (unsigned long)(client->difmap[mapcnt]));
01002                         rdlen=DIFFPAGESIZE ;
01003                         if (rawexpread_fully(pagestart, pagebuf, rdlen, client))
01004                                 return -1;
01005                         memcpy(pagebuf+offset,buf,wrlen) ;
01006                         if (write(client->difffile, pagebuf, DIFFPAGESIZE) !=
01007                                         DIFFPAGESIZE)
01008                                 return -1;
01009                 }                                                   
01010                 len-=wrlen ; a+=wrlen ; buf+=wrlen ;
01011         }
01012         return 0;
01013 }
01014 
01015 /**
01016  * Do the initial negotiation.
01017  *
01018  * @param client The client we're negotiating with.
01019  **/
01020 void negotiate(CLIENT *client) {
01021         char zeros[300];
01022         u64 size_host;
01023 
01024         memset(zeros, '\0', 290);
01025         if (write(client->net, INIT_PASSWD, 8) < 0)
01026                 err("Negotiation failed: %m");
01027         cliserv_magic = htonll(cliserv_magic);
01028         if (write(client->net, &cliserv_magic, sizeof(cliserv_magic)) < 0)
01029                 err("Negotiation failed: %m");
01030         size_host = htonll((u64)(client->exportsize));
01031         if (write(client->net, &size_host, 8) < 0)
01032                 err("Negotiation failed: %m");
01033         if (write(client->net, zeros, 128) < 0)
01034                 err("Negotiation failed: %m");
01035 }
01036 
01037 /** sending macro. */
01038 #define SEND(net,reply) writeit( net, &reply, sizeof( reply ));
01039 /** error macro. */
01040 #define ERROR(client,reply) { reply.error = htonl(-1); SEND(client->net,reply); reply.error = 0; }
01041 /**
01042  * Serve a file to a single client.
01043  *
01044  * @todo This beast needs to be split up in many tiny little manageable
01045  * pieces. Preferably with a chainsaw.
01046  *
01047  * @param client The client we're going to serve to.
01048  * @return never
01049  **/
01050 int mainloop(CLIENT *client) {
01051         struct nbd_request request;
01052         struct nbd_reply reply;
01053         gboolean go_on=TRUE;
01054 #ifdef DODBG
01055         int i = 0;
01056 #endif
01057         negotiate(client);
01058         DEBUG("Entering request loop!\n");
01059         reply.magic = htonl(NBD_REPLY_MAGIC);
01060         reply.error = 0;
01061         while (go_on) {
01062                 char buf[BUFSIZE];
01063                 size_t len;
01064 #ifdef DODBG
01065                 i++;
01066                 printf("%d: ", i);
01067 #endif
01068                 if (client->server->timeout) 
01069                         alarm(client->server->timeout);
01070                 readit(client->net, &request, sizeof(request));
01071                 request.from = ntohll(request.from);
01072                 request.type = ntohl(request.type);
01073 
01074                 if (request.type==NBD_CMD_DISC) {
01075                         msg2(LOG_INFO, "Disconnect request received.");
01076                         if (client->server->flags & F_COPYONWRITE) { 
01077                                 if (client->difmap) g_free(client->difmap) ;
01078                                 close(client->difffile);
01079                                 unlink(client->difffilename);
01080                                 free(client->difffilename);
01081                         }
01082                         go_on=FALSE;
01083                         continue;
01084                 }
01085 
01086                 len = ntohl(request.len);
01087 
01088                 if (request.magic != htonl(NBD_REQUEST_MAGIC))
01089                         err("Not enough magic.");
01090                 if (len > BUFSIZE + sizeof(struct nbd_reply))
01091                         err("Request too big!");
01092 #ifdef DODBG
01093                 printf("%s from %Lu (%Lu) len %d, ", request.type ? "WRITE" :
01094                                 "READ", (unsigned long long)request.from,
01095                                 (unsigned long long)request.from / 512, len);
01096 #endif
01097                 memcpy(reply.handle, request.handle, sizeof(reply.handle));
01098                 if ((request.from + len) > (OFFT_MAX)) {
01099                         DEBUG("[Number too large!]");
01100                         ERROR(client, reply);
01101                         continue;
01102                 }
01103 
01104                 if (((ssize_t)((off_t)request.from + len) > client->exportsize)) {
01105                         DEBUG("[RANGE!]");
01106                         ERROR(client, reply);
01107                         continue;
01108                 }
01109 
01110                 if (request.type==NBD_CMD_WRITE) {
01111                         DEBUG("wr: net->buf, ");
01112                         readit(client->net, buf, len);
01113                         DEBUG("buf->exp, ");
01114                         if ((client->server->flags & F_READONLY) ||
01115                             (client->server->flags & F_AUTOREADONLY)) {
01116                                 DEBUG("[WRITE to READONLY!]");
01117                                 ERROR(client, reply);
01118                                 continue;
01119                         }
01120                         if (expwrite(request.from, buf, len, client)) {
01121                                 DEBUG("Write failed: %m" );
01122                                 ERROR(client, reply);
01123                                 continue;
01124                         }
01125                         SEND(client->net, reply);
01126                         DEBUG("OK!\n");
01127                         continue;
01128                 }
01129                 /* READ */
01130 
01131                 DEBUG("exp->buf, ");
01132                 if (expread(request.from, buf + sizeof(struct nbd_reply), len, client)) {
01133                         DEBUG("Read failed: %m");
01134                         ERROR(client, reply);
01135                         continue;
01136                 }
01137 
01138                 DEBUG("buf->net, ");
01139                 memcpy(buf, &reply, sizeof(struct nbd_reply));
01140                 writeit(client->net, buf, len + sizeof(struct nbd_reply));
01141                 DEBUG("OK!\n");
01142         }
01143         return 0;
01144 }
01145 
01146 /**
01147  * Set up client export array, which is an array of FILE_INFO.
01148  * Also, split a single exportfile into multiple ones, if that was asked.
01149  * @param client information on the client which we want to setup export for
01150  **/
01151 void setupexport(CLIENT* client) {
01152         int i;
01153         off_t laststartoff = 0, lastsize = 0;
01154         int multifile = (client->server->flags & F_MULTIFILE);
01155 
01156         client->export = g_array_new(TRUE, TRUE, sizeof(FILE_INFO));
01157 
01158         /* If multi-file, open as many files as we can.
01159          * If not, open exactly one file.
01160          * Calculate file sizes as we go to get total size. */
01161         for(i=0; ; i++) {
01162                 FILE_INFO fi;
01163                 gchar *tmpname;
01164                 mode_t mode = (client->server->flags & F_READONLY) ? O_RDONLY : O_RDWR;
01165 
01166                 if(multifile) {
01167                         tmpname=g_strdup_printf("%s.%d", client->exportname, i);
01168                 } else {
01169                         tmpname=g_strdup(client->exportname);
01170                 }
01171                 DEBUG2( "Opening %s\n", tmpname );
01172                 fi.fhandle = open(tmpname, mode);
01173                 if(fi.fhandle == -1 && mode == O_RDWR) {
01174                         /* Try again because maybe media was read-only */
01175                         fi.fhandle = open(tmpname, O_RDONLY);
01176                         if(fi.fhandle != -1) {
01177                                 client->server->flags |= F_AUTOREADONLY;
01178                                 client->server->flags |= F_READONLY;
01179                         }
01180                 }
01181                 if(fi.fhandle == -1) {
01182                         if(multifile && i>0)
01183                                 break;
01184                         err("Could not open exported file: %m");
01185                 }
01186                 fi.startoff = laststartoff + lastsize;
01187                 g_array_append_val(client->export, fi);
01188                 g_free(tmpname);
01189 
01190                 /* Starting offset and size of this file will be used to
01191                  * calculate starting offset of next file */
01192                 laststartoff = fi.startoff;
01193                 lastsize = size_autodetect(fi.fhandle);
01194 
01195                 if(!multifile)
01196                         break;
01197         }
01198 
01199         /* Set export size to total calculated size */
01200         client->exportsize = laststartoff + lastsize;
01201 
01202         /* Export size may be overridden */
01203         if(client->server->expected_size) {
01204                 /* desired size must be <= total calculated size */
01205                 if(client->server->expected_size > client->exportsize) {
01206                         err("Size of exported file is too big\n");
01207                 }
01208 
01209                 client->exportsize = client->server->expected_size;
01210         }
01211 
01212         msg3(LOG_INFO, "Size of exported file/device is %Lu", (unsigned long long)client->exportsize);
01213         if(multifile) {
01214                 msg3(LOG_INFO, "Total number of files: %d", i);
01215         }
01216 }
01217 
01218 int copyonwrite_prepare(CLIENT* client) {
01219         off_t i;
01220         if ((client->difffilename = malloc(1024))==NULL)
01221                 err("Failed to allocate string for diff file name");
01222         snprintf(client->difffilename, 1024, "%s-%s-%d.diff",client->exportname,client->clientname,
01223                 (int)getpid()) ;
01224         client->difffilename[1023]='\0';
01225         msg3(LOG_INFO,"About to create map and diff file %s",client->difffilename) ;
01226         client->difffile=open(client->difffilename,O_RDWR | O_CREAT | O_TRUNC,0600) ;
01227         if (client->difffile<0) err("Could not create diff file (%m)") ;
01228         if ((client->difmap=calloc(client->exportsize/DIFFPAGESIZE,sizeof(u32)))==NULL)
01229                 err("Could not allocate memory") ;
01230         for (i=0;i<client->exportsize/DIFFPAGESIZE;i++) client->difmap[i]=(u32)-1 ;
01231 
01232         return 0;
01233 }
01234 
01235 /**
01236  * Serve a connection. 
01237  *
01238  * @todo allow for multithreading, perhaps use libevent. Not just yet, though;
01239  * follow the road map.
01240  *
01241  * @param client a connected client
01242  **/
01243 void serveconnection(CLIENT *client) {
01244         setupexport(client);
01245 
01246         if (client->server->flags & F_COPYONWRITE) {
01247                 copyonwrite_prepare(client);
01248         }
01249 
01250         setmysockopt(client->net);
01251 
01252         mainloop(client);
01253 }
01254 
01255 /**
01256  * Find the name of the file we have to serve. This will use g_strdup_printf
01257  * to put the IP address of the client inside a filename containing
01258  * "%s" (in the form as specified by the "virtstyle" option). That name
01259  * is then written to client->exportname.
01260  *
01261  * @param net A socket connected to an nbd client
01262  * @param client information about the client. The IP address in human-readable
01263  * format will be written to a new char* buffer, the address of which will be
01264  * stored in client->clientname.
01265  **/
01266 void set_peername(int net, CLIENT *client) {
01267         struct sockaddr_in addrin;
01268         struct sockaddr_in netaddr;
01269         socklen_t addrinlen = sizeof( addrin );
01270         char *peername;
01271         char *netname;
01272         char *tmp;
01273         int i;
01274 
01275         if (getpeername(net, (struct sockaddr *) &addrin, (socklen_t *)&addrinlen) < 0)
01276                 err("getsockname failed: %m");
01277         peername = g_strdup(inet_ntoa(addrin.sin_addr));
01278         switch(client->server->virtstyle) {
01279                 case VIRT_NONE:
01280                         client->exportname=g_strdup(client->server->exportname);
01281                         break;
01282                 case VIRT_IPHASH:
01283                         for(i=0;i<strlen(peername);i++) {
01284                                 if(peername[i]=='.') {
01285                                         peername[i]='/';
01286                                 }
01287                         }
01288                 case VIRT_IPLIT:
01289                         client->exportname=g_strdup_printf(client->server->exportname, peername);
01290                         break;
01291                 case VIRT_CIDR:
01292                         memcpy(&netaddr, &addrin, addrinlen);
01293                         netaddr.sin_addr.s_addr>>=32-(client->server->cidrlen);
01294                         netaddr.sin_addr.s_addr<<=32-(client->server->cidrlen);
01295                         netname = inet_ntoa(netaddr.sin_addr);
01296                         tmp=g_strdup_printf("%s/%s", netname, peername);
01297                         client->exportname=g_strdup_printf(client->server->exportname, tmp);
01298                         break;
01299         }
01300 
01301         msg4(LOG_INFO, "connect from %s, assigned file is %s", 
01302              peername, client->exportname);
01303         client->clientname=g_strdup(peername);
01304         g_free(peername);
01305 }
01306 
01307 /**
01308  * Destroy a pid_t*
01309  * @param data a pointer to pid_t which should be freed
01310  **/
01311 void destroy_pid_t(gpointer data) {
01312         g_free(data);
01313 }
01314 
01315 /**
01316  * Go daemon (unless we specified at compile time that we didn't want this)
01317  * @param serve the first server of our configuration. If its port is zero,
01318  *      then do not daemonize, because we're doing inetd then. This parameter
01319  *      is only used to create a PID file of the form
01320  *      /var/run/nbd-server.&lt;port&gt;.pid; it's not modified in any way.
01321  **/
01322 #if !defined(NODAEMON) && !defined(NOFORK)
01323 void daemonize(SERVER* serve) {
01324         FILE*pidf;
01325 
01326         //if (!serve) {
01327         //      return;
01328         //}
01329         if(daemon(0,0) < 0) {
01330                 err("daemon");
01331         }
01332         if(!*pidftemplate) {
01333                 if(serve) {
01334                         strncpy(pidftemplate, "/var/run/nbd-server.%d.pid", 255);
01335                 } else {
01336                         strncpy(pidftemplate, "/var/run/nbd-server.pid", 255);
01337                 }
01338         }
01339         snprintf(pidfname, 255, pidftemplate, serve ? serve->port : 0);
01340         pidf=fopen(pidfname, "w");
01341         if(pidf) {
01342                 fprintf(pidf,"%d\n", (int)getpid());
01343                 fclose(pidf);
01344         } else {
01345                 perror("fopen");
01346                 fprintf(stderr, "Not fatal; continuing");
01347         }
01348 }
01349 #else
01350 #define daemonize(serve)
01351 #endif /* !defined(NODAEMON) && !defined(NOFORK) */
01352 
01353 /**
01354  * Connect a server's socket.
01355  *
01356  * @param serve the server we want to connect.
01357  **/
01358 void setup_serve(SERVER *serve) {
01359         struct sockaddr_in addrin;
01360         struct sigaction sa;
01361         int addrinlen = sizeof(addrin);
01362         int sock_flags;
01363 #ifndef sun
01364         int yes=1;
01365 #else
01366         char yes='1';
01367 #endif /* sun */
01368         if ((serve->socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
01369                 err("socket: %m");
01370 
01371         /* lose the pesky "Address already in use" error message */
01372         if (setsockopt(serve->socket,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) {
01373                 err("setsockopt SO_REUSEADDR");
01374         }
01375         if (setsockopt(serve->socket,SOL_SOCKET,SO_KEEPALIVE,&yes,sizeof(int)) == -1) {
01376                 err("setsockopt SO_KEEPALIVE");
01377         }
01378 
01379         /* make the listening socket non-blocking */
01380         if ((sock_flags = fcntl(serve->socket, F_GETFL, 0)) == -1) {
01381                 err("fcntl F_GETFL");
01382         }
01383         if (fcntl(serve->socket, F_SETFL, sock_flags | O_NONBLOCK) == -1) {
01384                 err("fcntl F_SETFL O_NONBLOCK");
01385         }
01386 
01387         DEBUG("Waiting for connections... bind, ");
01388         addrin.sin_family = AF_INET;
01389         addrin.sin_port = htons(serve->port);
01390         addrin.sin_addr.s_addr = 0;
01391         if (bind(serve->socket, (struct sockaddr *) &addrin, addrinlen) < 0)
01392                 err("bind: %m");
01393         DEBUG("listen, ");
01394         if (listen(serve->socket, 1) < 0)
01395                 err("listen: %m");
01396         sa.sa_handler = sigchld_handler;
01397         sigemptyset(&sa.sa_mask);
01398         sa.sa_flags = SA_RESTART;
01399         if(sigaction(SIGCHLD, &sa, NULL) == -1)
01400                 err("sigaction: %m");
01401         sa.sa_handler = sigterm_handler;
01402         sigemptyset(&sa.sa_mask);
01403         sa.sa_flags = SA_RESTART;
01404         if(sigaction(SIGTERM, &sa, NULL) == -1)
01405                 err("sigaction: %m");
01406 }
01407 
01408 /**
01409  * Connect our servers.
01410  **/
01411 void setup_servers(GArray* servers) {
01412         int i;
01413 
01414         for(i=0;i<servers->len;i++) {
01415                 setup_serve(&(g_array_index(servers, SERVER, i)));
01416         }
01417         children=g_hash_table_new_full(g_int_hash, g_int_equal, NULL, destroy_pid_t);
01418 }
01419 
01420 /**
01421  * Loop through the available servers, and serve them.
01422  **/
01423 int serveloop(GArray* servers) {
01424         struct sockaddr_in addrin;
01425         socklen_t addrinlen=sizeof(addrin);
01426         SERVER *serve;
01427         int i;
01428         int max;
01429         int sock;
01430         fd_set mset;
01431         fd_set rset;
01432         struct timeval tv;
01433 
01434         /* 
01435          * Set up the master fd_set. The set of descriptors we need
01436          * to select() for never changes anyway and it buys us a *lot*
01437          * of time to only build this once. However, if we ever choose
01438          * to not fork() for clients anymore, we may have to revisit
01439          * this.
01440          */
01441         max=0;
01442         FD_ZERO(&mset);
01443         for(i=0;i<servers->len;i++) {
01444                 sock=(g_array_index(servers, SERVER, i)).socket;
01445                 FD_SET(sock, &mset);
01446                 max=sock>max?sock:max;
01447         }
01448         for(;;) {
01449                 CLIENT *client;
01450                 int net;
01451                 pid_t *pid;
01452 
01453                 memcpy(&rset, &mset, sizeof(fd_set));
01454                 tv.tv_sec=0;
01455                 tv.tv_usec=500;
01456                 if(select(max+1, &rset, NULL, NULL, &tv)>0) {
01457                         DEBUG("accept, ");
01458                         for(i=0;i<servers->len;i++) {
01459                                 serve=&(g_array_index(servers, SERVER, i));
01460                                 if(FD_ISSET(serve->socket, &rset)) {
01461                                         if ((net=accept(serve->socket, (struct sockaddr *) &addrin, &addrinlen)) < 0)
01462                                                 err("accept: %m");
01463 
01464                                         client = g_malloc(sizeof(CLIENT));
01465                                         client->server=serve;
01466                                         client->exportsize=OFFT_MAX;
01467                                         client->net=net;
01468                                         set_peername(net, client);
01469                                         if (!authorized_client(client)) {
01470                                                 msg2(LOG_INFO,"Unauthorized client") ;
01471                                                 close(net);
01472                                                 continue;
01473                                         }
01474                                         msg2(LOG_INFO,"Authorized client") ;
01475                                         pid=g_malloc(sizeof(pid_t));
01476 #ifndef NOFORK
01477                                         if ((*pid=fork())<0) {
01478                                                 msg3(LOG_INFO,"Could not fork (%s)",strerror(errno)) ;
01479                                                 close(net);
01480                                                 continue;
01481                                         }
01482                                         if (*pid>0) { /* parent */
01483                                                 close(net);
01484                                                 g_hash_table_insert(children, pid, pid);
01485                                                 continue;
01486                                         }
01487                                         /* child */
01488                                         g_hash_table_destroy(children);
01489                                         for(i=0; (i<servers->len && (serve=(g_array_index(servers, SERVER*, i)))); i++) {
01490                                                 close(serve->socket);
01491                                         }
01492                                         /* FALSE does not free the
01493                                         actual data. This is required,
01494                                         because the client has a
01495                                         direct reference into that
01496                                         data, and otherwise we get a
01497                                         segfault... */
01498                                         g_array_free(servers, FALSE);
01499 #endif // NOFORK
01500                                         msg2(LOG_INFO,"Starting to serve");
01501                                         serveconnection(client);
01502                                 }
01503                         }
01504                 }
01505         }
01506 }
01507 
01508 /**
01509  * Set up user-ID and/or group-ID
01510  **/
01511 void dousers(void) {
01512         struct passwd *pw;
01513         struct group *gr;
01514         if(runuser) {
01515                 pw=getpwnam(runuser);
01516                 if(setuid(pw->pw_uid)<0)
01517                         msg3(LOG_DEBUG, "Could not set UID: %s", strerror(errno));
01518         }
01519         if(rungroup) {
01520                 gr=getgrnam(rungroup);
01521                 if(setgid(gr->gr_gid)<0)
01522                         msg3(LOG_DEBUG, "Could not set GID: %s", strerror(errno));
01523         }
01524 }
01525 
01526 /**
01527  * Main entry point...
01528  **/
01529 int main(int argc, char *argv[]) {
01530         SERVER *serve;
01531         GArray *servers;
01532         GError *err=NULL;
01533 
01534         if (sizeof( struct nbd_request )!=28) {
01535                 fprintf(stderr,"Bad size of structure. Alignment problems?\n");
01536                 exit(-1) ;
01537         }
01538 
01539         memset(pidftemplate, '\0', 256);
01540 
01541         logging();
01542         config_file_pos = g_strdup(CFILE);
01543         serve=cmdline(argc, argv);
01544         servers = parse_cfile(config_file_pos, &err);
01545         if(!servers || !servers->len) {
01546                 g_warning("Could not parse config file: %s", err->message);
01547         }
01548         if(serve) {
01549                 g_array_append_val(servers, *serve);
01550      
01551                 if (!(serve->port)) {
01552                         CLIENT *client;
01553 #ifndef ISSERVER
01554                         /* You really should define ISSERVER if you're going to use
01555                          * inetd mode, but if you don't, closing stdout and stderr
01556                          * (which inetd had connected to the client socket) will let it
01557                          * work. */
01558                         close(1);
01559                         close(2);
01560                         open("/dev/null", O_WRONLY);
01561                         open("/dev/null", O_WRONLY);
01562 #endif
01563                         client=g_malloc(sizeof(CLIENT));
01564                         client->server=serve;
01565                         client->net=0;
01566                         client->exportsize=OFFT_MAX;
01567                         set_peername(0,client);
01568                         serveconnection(client);
01569                         return 0;
01570                 }
01571         }
01572         if((!serve) && (!servers||!servers->len)) {
01573                 g_message("Nothing to do! Bye!");
01574                 exit(EXIT_FAILURE);
01575         }
01576         daemonize(serve);
01577         setup_servers(servers);
01578         dousers();
01579         serveloop(servers);
01580         return 0 ;
01581 }

Generated on Fri Sep 21 19:49:46 2007 for Network Block Device by  doxygen 1.5.3