Network Block Device 2.9.20

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  * 16/03/2010 - Add IPv6 support.
00053  *      Kitt Tientanopajai <kitt@kitty.in.th>
00054  *      Neutron Soutmun <neo.neutron@gmail.com>
00055  *      Suriya Soutmun <darksolar@gmail.com>
00056  */
00057 
00058 /* Includes LFS defines, which defines behaviours of some of the following
00059  * headers, so must come before those */
00060 #include "lfs.h"
00061 
00062 #include <sys/types.h>
00063 #include <sys/socket.h>
00064 #include <sys/stat.h>
00065 #include <sys/select.h>         /* select */
00066 #include <sys/wait.h>           /* wait */
00067 #ifdef HAVE_SYS_IOCTL_H
00068 #include <sys/ioctl.h>
00069 #endif
00070 #include <sys/param.h>
00071 #ifdef HAVE_SYS_MOUNT_H
00072 #include <sys/mount.h>          /* For BLKGETSIZE */
00073 #endif
00074 #include <signal.h>             /* sigaction */
00075 #include <errno.h>
00076 #include <netinet/tcp.h>
00077 #include <netinet/in.h>
00078 #include <netdb.h>
00079 #include <syslog.h>
00080 #include <unistd.h>
00081 #include <stdio.h>
00082 #include <stdlib.h>
00083 #include <string.h>
00084 #include <fcntl.h>
00085 #include <arpa/inet.h>
00086 #include <strings.h>
00087 #include <dirent.h>
00088 #include <unistd.h>
00089 #include <getopt.h>
00090 #include <pwd.h>
00091 #include <grp.h>
00092 
00093 #include <glib.h>
00094 
00095 /* used in cliserv.h, so must come first */
00096 #define MY_NAME "nbd_server"
00097 #include "cliserv.h"
00098 
00099 #ifdef WITH_SDP
00100 #include <sdp_inet.h>
00101 #endif
00102 
00103 /** Default position of the config file */
00104 #ifndef SYSCONFDIR
00105 #define SYSCONFDIR "/etc"
00106 #endif
00107 #define CFILE SYSCONFDIR "/nbd-server/config"
00108 
00109 /** Where our config file actually is */
00110 gchar* config_file_pos;
00111 
00112 /** What user we're running as */
00113 gchar* runuser=NULL;
00114 /** What group we're running as */
00115 gchar* rungroup=NULL;
00116 /** whether to export using the old negotiation protocol (port-based) */
00117 gboolean do_oldstyle=FALSE;
00118 
00119 /** Logging macros, now nothing goes to syslog unless you say ISSERVER */
00120 #ifdef ISSERVER
00121 #define msg2(a,b) syslog(a,b)
00122 #define msg3(a,b,c) syslog(a,b,c)
00123 #define msg4(a,b,c,d) syslog(a,b,c,d)
00124 #else
00125 #define msg2(a,b) g_message(b)
00126 #define msg3(a,b,c) g_message(b,c)
00127 #define msg4(a,b,c,d) g_message(b,c,d)
00128 #endif
00129 
00130 /* Debugging macros */
00131 //#define DODBG
00132 #ifdef DODBG
00133 #define DEBUG( a ) printf( a )
00134 #define DEBUG2( a,b ) printf( a,b )
00135 #define DEBUG3( a,b,c ) printf( a,b,c )
00136 #define DEBUG4( a,b,c,d ) printf( a,b,c,d )
00137 #else
00138 #define DEBUG( a )
00139 #define DEBUG2( a,b ) 
00140 #define DEBUG3( a,b,c ) 
00141 #define DEBUG4( a,b,c,d ) 
00142 #endif
00143 #ifndef PACKAGE_VERSION
00144 #define PACKAGE_VERSION ""
00145 #endif
00146 /**
00147  * The highest value a variable of type off_t can reach. This is a signed
00148  * integer, so set all bits except for the leftmost one.
00149  **/
00150 #define OFFT_MAX ~((off_t)1<<(sizeof(off_t)*8-1))
00151 #define LINELEN 256       /**< Size of static buffer used to read the
00152                                authorization file (yuck) */
00153 #define BUFSIZE ((1024*1024)+sizeof(struct nbd_reply)) /**< Size of buffer that can hold requests */
00154 #define DIFFPAGESIZE 4096 /**< diff file uses those chunks */
00155 #define F_READONLY 1      /**< flag to tell us a file is readonly */
00156 #define F_MULTIFILE 2     /**< flag to tell us a file is exported using -m */
00157 #define F_COPYONWRITE 4   /**< flag to tell us a file is exported using
00158                             copyonwrite */
00159 #define F_AUTOREADONLY 8  /**< flag to tell us a file is set to autoreadonly */
00160 #define F_SPARSE 16       /**< flag to tell us copyronwrite should use a sparse file */
00161 #define F_SDP 32          /**< flag to tell us the export should be done using the Socket Direct Protocol for RDMA */
00162 #define F_SYNC 64         /**< Whether to fsync() after a write */
00163 GHashTable *children;
00164 char pidfname[256]; /**< name of our PID file */
00165 char pidftemplate[256]; /**< template to be used for the filename of the PID file */
00166 char default_authname[] = SYSCONFDIR "/nbd-server/allow"; /**< default name of allow file */
00167 
00168 int modernsock=0;         /**< Socket for the modern handler. Not used
00169                                if a client was only specified on the
00170                                command line; only port used if
00171                                oldstyle is set to false (and then the
00172                                command-line client isn't used, gna gna) */
00173 char* modern_listen;      /**< listenaddr value for modernsock */
00174 
00175 /**
00176  * Types of virtuatlization
00177  **/
00178 typedef enum {
00179         VIRT_NONE=0,    /**< No virtualization */
00180         VIRT_IPLIT,     /**< Literal IP address as part of the filename */
00181         VIRT_IPHASH,    /**< Replacing all dots in an ip address by a / before
00182                              doing the same as in IPLIT */
00183         VIRT_CIDR,      /**< Every subnet in its own directory */
00184 } VIRT_STYLE;
00185 
00186 /**
00187  * Variables associated with a server.
00188  **/
00189 typedef struct {
00190         gchar* exportname;    /**< (unprocessed) filename of the file we're exporting */
00191         off_t expected_size; /**< size of the exported file as it was told to
00192                                us through configuration */
00193         gchar* listenaddr;   /**< The IP address we're listening on */
00194         unsigned int port;   /**< port we're exporting this file at */
00195         char* authname;      /**< filename of the authorization file */
00196         int flags;           /**< flags associated with this exported file */
00197         int socket;          /**< The socket of this server. */
00198         int socket_family;   /**< family of the socket */
00199         VIRT_STYLE virtstyle;/**< The style of virtualization, if any */
00200         uint8_t cidrlen;     /**< The length of the mask when we use
00201                                   CIDR-style virtualization */
00202         gchar* prerun;       /**< command to be ran after connecting a client,
00203                                   but before starting to serve */
00204         gchar* postrun;      /**< command that will be ran after the client
00205                                   disconnects */
00206         gchar* servename;    /**< name of the export as selected by nbd-client */
00207         int max_connections; /**< maximum number of opened connections */
00208 } SERVER;
00209 
00210 /**
00211  * Variables associated with a client socket.
00212  **/
00213 typedef struct {
00214         int fhandle;      /**< file descriptor */
00215         off_t startoff;   /**< starting offset of this file */
00216 } FILE_INFO;
00217 
00218 typedef struct {
00219         off_t exportsize;    /**< size of the file we're exporting */
00220         char *clientname;    /**< peer */
00221         char *exportname;    /**< (processed) filename of the file we're exporting */
00222         GArray *export;    /**< array of FILE_INFO of exported files;
00223                                array size is always 1 unless we're
00224                                doing the multiple file option */
00225         int net;             /**< The actual client socket */
00226         SERVER *server;      /**< The server this client is getting data from */
00227         char* difffilename;  /**< filename of the copy-on-write file, if any */
00228         int difffile;        /**< filedescriptor of copyonwrite file. @todo
00229                                shouldn't this be an array too? (cfr export) Or
00230                                make -m and -c mutually exclusive */
00231         u32 difffilelen;     /**< number of pages in difffile */
00232         u32 *difmap;         /**< see comment on the global difmap for this one */
00233         gboolean modern;     /**< client was negotiated using modern negotiation protocol */
00234 } CLIENT;
00235 
00236 /**
00237  * Type of configuration file values
00238  **/
00239 typedef enum {
00240         PARAM_INT,              /**< This parameter is an integer */
00241         PARAM_STRING,           /**< This parameter is a string */
00242         PARAM_BOOL,             /**< This parameter is a boolean */
00243 } PARAM_TYPE;
00244 
00245 /**
00246  * Configuration file values
00247  **/
00248 typedef struct {
00249         gchar *paramname;       /**< Name of the parameter, as it appears in
00250                                   the config file */
00251         gboolean required;      /**< Whether this is a required (as opposed to
00252                                   optional) parameter */
00253         PARAM_TYPE ptype;       /**< Type of the parameter. */
00254         gpointer target;        /**< Pointer to where the data of this
00255                                   parameter should be written. If ptype is
00256                                   PARAM_BOOL, the data is or'ed rather than
00257                                   overwritten. */
00258         gint flagval;           /**< Flag mask for this parameter in case ptype
00259                                   is PARAM_BOOL. */
00260 } PARAM;
00261 
00262 /**
00263  * Check whether a client is allowed to connect. Works with an authorization
00264  * file which contains one line per machine, no wildcards.
00265  *
00266  * @param opts The client who's trying to connect.
00267  * @return 0 - authorization refused, 1 - OK
00268  **/
00269 int authorized_client(CLIENT *opts) {
00270         const char *ERRMSG="Invalid entry '%s' in authfile '%s', so, refusing all connections.";
00271         FILE *f ;
00272         char line[LINELEN]; 
00273         char *tmp;
00274         struct in_addr addr;
00275         struct in_addr client;
00276         struct in_addr cltemp;
00277         int len;
00278 
00279         if ((f=fopen(opts->server->authname,"r"))==NULL) {
00280                 msg4(LOG_INFO,"Can't open authorization file %s (%s).",
00281                      opts->server->authname,strerror(errno)) ;
00282                 return 1 ; 
00283         }
00284   
00285         inet_aton(opts->clientname, &client);
00286         while (fgets(line,LINELEN,f)!=NULL) {
00287                 if((tmp=index(line, '/'))) {
00288                         if(strlen(line)<=tmp-line) {
00289                                 msg4(LOG_CRIT, ERRMSG, line, opts->server->authname);
00290                                 return 0;
00291                         }
00292                         *(tmp++)=0;
00293                         if(!inet_aton(line,&addr)) {
00294                                 msg4(LOG_CRIT, ERRMSG, line, opts->server->authname);
00295                                 return 0;
00296                         }
00297                         len=strtol(tmp, NULL, 0);
00298                         addr.s_addr>>=32-len;
00299                         addr.s_addr<<=32-len;
00300                         memcpy(&cltemp,&client,sizeof(client));
00301                         cltemp.s_addr>>=32-len;
00302                         cltemp.s_addr<<=32-len;
00303                         if(addr.s_addr == cltemp.s_addr) {
00304                                 return 1;
00305                         }
00306                 }
00307                 if (strncmp(line,opts->clientname,strlen(opts->clientname))==0) {
00308                         fclose(f);
00309                         return 1;
00310                 }
00311         }
00312         fclose(f);
00313         return 0;
00314 }
00315 
00316 /**
00317  * Read data from a file descriptor into a buffer
00318  *
00319  * @param f a file descriptor
00320  * @param buf a buffer
00321  * @param len the number of bytes to be read
00322  **/
00323 inline void readit(int f, void *buf, size_t len) {
00324         ssize_t res;
00325         while (len > 0) {
00326                 DEBUG("*");
00327                 if ((res = read(f, buf, len)) <= 0) {
00328                         if(errno != EAGAIN) {
00329                                 err("Read failed: %m");
00330                         }
00331                 } else {
00332                         len -= res;
00333                         buf += res;
00334                 }
00335         }
00336 }
00337 
00338 /**
00339  * Write data from a buffer into a filedescriptor
00340  *
00341  * @param f a file descriptor
00342  * @param buf a buffer containing data
00343  * @param len the number of bytes to be written
00344  **/
00345 inline void writeit(int f, void *buf, size_t len) {
00346         ssize_t res;
00347         while (len > 0) {
00348                 DEBUG("+");
00349                 if ((res = write(f, buf, len)) <= 0)
00350                         err("Send failed: %m");
00351                 len -= res;
00352                 buf += res;
00353         }
00354 }
00355 
00356 /**
00357  * Print out a message about how to use nbd-server. Split out to a separate
00358  * function so that we can call it from multiple places
00359  */
00360 void usage() {
00361         printf("This is nbd-server version " VERSION "\n");
00362         printf("Usage: [ip:|ip6@]port file_to_export [size][kKmM] [-l authorize_file] [-r] [-m] [-c] [-C configuration file] [-p PID file name] [-o section name] [-M max connections]\n"
00363                "\t-r|--read-only\t\tread only\n"
00364                "\t-m|--multi-file\t\tmultiple file\n"
00365                "\t-c|--copy-on-write\tcopy on write\n"
00366                "\t-C|--config-file\tspecify an alternate configuration file\n"
00367                "\t-l|--authorize-file\tfile with list of hosts that are allowed to\n\t\t\t\tconnect.\n"
00368                "\t-p|--pid-file\t\tspecify a filename to write our PID to\n"
00369                "\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"
00370                "\t-M|--max-connections\tspecify the maximum number of opened connections\n\n"
00371                "\tif port is set to 0, stdin is used (for running from inetd)\n"
00372                "\tif file_to_export contains '%%s', it is substituted with the IP\n"
00373                "\t\taddress of the machine trying to connect\n" 
00374                "\tif ip is set, it contains the local IP address on which we're listening.\n\tif not, the server will listen on all local IP addresses\n");
00375         printf("Using configuration file %s\n", CFILE);
00376 }
00377 
00378 /* Dumps a config file section of the given SERVER*, and exits. */
00379 void dump_section(SERVER* serve, gchar* section_header) {
00380         printf("[%s]\n", section_header);
00381         printf("\texportname = %s\n", serve->exportname);
00382         printf("\tlistenaddr = %s\n", serve->listenaddr);
00383         printf("\tport = %d\n", serve->port);
00384         if(serve->flags & F_READONLY) {
00385                 printf("\treadonly = true\n");
00386         }
00387         if(serve->flags & F_MULTIFILE) {
00388                 printf("\tmultifile = true\n");
00389         }
00390         if(serve->flags & F_COPYONWRITE) {
00391                 printf("\tcopyonwrite = true\n");
00392         }
00393         if(serve->expected_size) {
00394                 printf("\tfilesize = %lld\n", (long long int)serve->expected_size);
00395         }
00396         if(serve->authname) {
00397                 printf("\tauthfile = %s\n", serve->authname);
00398         }
00399         exit(EXIT_SUCCESS);
00400 }
00401 
00402 /**
00403  * Parse the command line.
00404  *
00405  * @param argc the argc argument to main()
00406  * @param argv the argv argument to main()
00407  **/
00408 SERVER* cmdline(int argc, char *argv[]) {
00409         int i=0;
00410         int nonspecial=0;
00411         int c;
00412         struct option long_options[] = {
00413                 {"read-only", no_argument, NULL, 'r'},
00414                 {"multi-file", no_argument, NULL, 'm'},
00415                 {"copy-on-write", no_argument, NULL, 'c'},
00416                 {"authorize-file", required_argument, NULL, 'l'},
00417                 {"config-file", required_argument, NULL, 'C'},
00418                 {"pid-file", required_argument, NULL, 'p'},
00419                 {"output-config", required_argument, NULL, 'o'},
00420                 {"max-connection", required_argument, NULL, 'M'},
00421                 {0,0,0,0}
00422         };
00423         SERVER *serve;
00424         off_t es;
00425         size_t last;
00426         char suffix;
00427         gboolean do_output=FALSE;
00428         gchar* section_header="";
00429         gchar** addr_port;
00430 
00431         if(argc==1) {
00432                 return NULL;
00433         }
00434         serve=g_new0(SERVER, 1);
00435         serve->authname = g_strdup(default_authname);
00436         serve->virtstyle=VIRT_IPLIT;
00437         while((c=getopt_long(argc, argv, "-C:cl:mo:rp:M:", long_options, &i))>=0) {
00438                 switch (c) {
00439                 case 1:
00440                         /* non-option argument */
00441                         switch(nonspecial++) {
00442                         case 0:
00443                                 if(strchr(optarg, ':') == strrchr(optarg, ':')) {
00444                                         addr_port=g_strsplit(optarg, ":", 2);
00445 
00446                                         /* Check for "@" - maybe user using this separator
00447                                                  for IPv4 address */
00448                                         if(!addr_port[1]) {
00449                                                 g_strfreev(addr_port);
00450                                                 addr_port=g_strsplit(optarg, "@", 2);
00451                                         }
00452                                 } else {
00453                                         addr_port=g_strsplit(optarg, "@", 2);
00454                                 }
00455 
00456                                 if(addr_port[1]) {
00457                                         serve->port=strtol(addr_port[1], NULL, 0);
00458                                         serve->listenaddr=g_strdup(addr_port[0]);
00459                                 } else {
00460                                         serve->listenaddr=NULL;
00461                                         serve->port=strtol(addr_port[0], NULL, 0);
00462                                 }
00463                                 g_strfreev(addr_port);
00464                                 break;
00465                         case 1:
00466                                 serve->exportname = g_strdup(optarg);
00467                                 if(serve->exportname[0] != '/') {
00468                                         fprintf(stderr, "E: The to be exported file needs to be an absolute filename!\n");
00469                                         exit(EXIT_FAILURE);
00470                                 }
00471                                 break;
00472                         case 2:
00473                                 last=strlen(optarg)-1;
00474                                 suffix=optarg[last];
00475                                 if (suffix == 'k' || suffix == 'K' ||
00476                                     suffix == 'm' || suffix == 'M')
00477                                         optarg[last] = '\0';
00478                                 es = (off_t)atoll(optarg);
00479                                 switch (suffix) {
00480                                         case 'm':
00481                                         case 'M':  es <<= 10;
00482                                         case 'k':
00483                                         case 'K':  es <<= 10;
00484                                         default :  break;
00485                                 }
00486                                 serve->expected_size = es;
00487                                 break;
00488                         }
00489                         break;
00490                 case 'r':
00491                         serve->flags |= F_READONLY;
00492                         break;
00493                 case 'm':
00494                         serve->flags |= F_MULTIFILE;
00495                         break;
00496                 case 'o':
00497                         do_output = TRUE;
00498                         section_header = g_strdup(optarg);
00499                         break;
00500                 case 'p':
00501                         strncpy(pidftemplate, optarg, 256);
00502                         break;
00503                 case 'c': 
00504                         serve->flags |=F_COPYONWRITE;
00505                         break;
00506                 case 'C':
00507                         g_free(config_file_pos);
00508                         config_file_pos=g_strdup(optarg);
00509                         break;
00510                 case 'l':
00511                         g_free(serve->authname);
00512                         serve->authname=g_strdup(optarg);
00513                         break;
00514                 case 'M':
00515                         serve->max_connections = strtol(optarg, NULL, 0);
00516                         break;
00517                 default:
00518                         usage();
00519                         exit(EXIT_FAILURE);
00520                         break;
00521                 }
00522         }
00523         /* What's left: the port to export, the name of the to be exported
00524          * file, and, optionally, the size of the file, in that order. */
00525         if(nonspecial<2) {
00526                 g_free(serve);
00527                 serve=NULL;
00528         } else {
00529                 do_oldstyle = TRUE;
00530         }
00531         if(do_output) {
00532                 if(!serve) {
00533                         g_critical("Need a complete configuration on the command line to output a config file section!");
00534                         exit(EXIT_FAILURE);
00535                 }
00536                 dump_section(serve, section_header);
00537         }
00538         return serve;
00539 }
00540 
00541 /**
00542  * Error codes for config file parsing
00543  **/
00544 typedef enum {
00545         CFILE_NOTFOUND,         /**< The configuration file is not found */
00546         CFILE_MISSING_GENERIC,  /**< The (required) group "generic" is missing */
00547         CFILE_KEY_MISSING,      /**< A (required) key is missing */
00548         CFILE_VALUE_INVALID,    /**< A value is syntactically invalid */
00549         CFILE_VALUE_UNSUPPORTED,/**< A value is not supported in this build */
00550         CFILE_PROGERR,          /**< Programmer error */
00551         CFILE_NO_EXPORTS,       /**< A config file was specified that does not
00552                                      define any exports */
00553         CFILE_INCORRECT_PORT,   /**< The reserved port was specified for an
00554                                      old-style export. */
00555 } CFILE_ERRORS;
00556 
00557 /**
00558  * Remove a SERVER from memory. Used from the hash table
00559  **/
00560 void remove_server(gpointer s) {
00561         SERVER *server;
00562 
00563         server=(SERVER*)s;
00564         g_free(server->exportname);
00565         if(server->authname)
00566                 g_free(server->authname);
00567         if(server->listenaddr)
00568                 g_free(server->listenaddr);
00569         if(server->prerun)
00570                 g_free(server->prerun);
00571         if(server->postrun)
00572                 g_free(server->postrun);
00573         g_free(server);
00574 }
00575 
00576 /**
00577  * duplicate server
00578  * @param s the old server we want to duplicate
00579  * @return new duplicated server
00580  **/
00581 SERVER* dup_serve(SERVER *s) {
00582         SERVER *serve = NULL;
00583 
00584         serve=g_new0(SERVER, 1);
00585         if(serve == NULL)
00586                 return NULL;
00587 
00588         if(s->exportname)
00589                 serve->exportname = g_strdup(s->exportname);
00590 
00591         serve->expected_size = s->expected_size;
00592 
00593         if(s->listenaddr)
00594                 serve->listenaddr = g_strdup(s->listenaddr);
00595 
00596         serve->port = s->port;
00597 
00598         if(s->authname)
00599                 serve->authname = strdup(s->authname);
00600 
00601         serve->flags = s->flags;
00602         serve->socket = serve->socket;
00603         serve->socket_family = serve->socket_family;
00604         serve->cidrlen = s->cidrlen;
00605 
00606         if(s->prerun)
00607                 serve->prerun = g_strdup(s->prerun);
00608 
00609         if(s->postrun)
00610                 serve->postrun = g_strdup(s->postrun);
00611         
00612         if(s->servename)
00613                 serve->servename = g_strdup(s->servename);
00614 
00615         serve->max_connections = s->max_connections;
00616 
00617         return serve;
00618 }
00619 
00620 /**
00621  * append new server to array
00622  * @param s server
00623  * @param a server array
00624  * @return 0 success, -1 error
00625  */
00626 int append_serve(SERVER *s, GArray *a) {
00627         SERVER *ns = NULL;
00628         struct addrinfo hints;
00629         struct addrinfo *ai = NULL;
00630         struct addrinfo *rp = NULL;
00631         char   host[NI_MAXHOST];
00632         gchar  *port = NULL;
00633         int e;
00634         int ret;
00635 
00636         if(!s) {
00637                 err("Invalid parsing server");
00638                 return -1;
00639         }
00640 
00641         port = g_strdup_printf("%d", s->port);
00642 
00643         memset(&hints,'\0',sizeof(hints));
00644         hints.ai_family = AF_UNSPEC;
00645         hints.ai_socktype = SOCK_STREAM;
00646         hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE;
00647         hints.ai_protocol = IPPROTO_TCP;
00648 
00649         e = getaddrinfo(s->listenaddr, port, &hints, &ai);
00650 
00651         if (port)
00652                 g_free(port);
00653 
00654         if(e == 0) {
00655                 for (rp = ai; rp != NULL; rp = rp->ai_next) {
00656                         e = getnameinfo(rp->ai_addr, rp->ai_addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST);
00657 
00658                         if (e != 0) { // error
00659                                 fprintf(stderr, "getnameinfo: %s\n", gai_strerror(e));
00660                                 continue;
00661                         }
00662 
00663                         // duplicate server and set listenaddr to resolved IP address
00664                         ns = dup_serve (s);
00665                         if (ns) {
00666                                 ns->listenaddr = g_strdup(host);
00667                                 ns->socket_family = rp->ai_family;
00668                                 g_array_append_val(a, *ns);
00669                                 free(ns);
00670                                 ns = NULL;
00671                         }
00672                 }
00673 
00674                 ret = 0;
00675         } else {
00676                 fprintf(stderr, "getaddrinfo failed on listen host/address: %s (%s)\n", s->listenaddr ? s->listenaddr : "any", gai_strerror(e));
00677                 ret = -1;
00678         }
00679 
00680         if (ai)
00681                 freeaddrinfo(ai);
00682 
00683         return ret;
00684 }
00685 
00686 /**
00687  * Parse the config file.
00688  *
00689  * @param f the name of the config file
00690  * @param e a GError. @see CFILE_ERRORS for what error values this function can
00691  *      return.
00692  * @return a Array of SERVER* pointers, If the config file is empty or does not
00693  *      exist, returns an empty GHashTable; if the config file contains an
00694  *      error, returns NULL, and e is set appropriately
00695  **/
00696 GArray* parse_cfile(gchar* f, GError** e) {
00697         const char* DEFAULT_ERROR = "Could not parse %s in group %s: %s";
00698         const char* MISSING_REQUIRED_ERROR = "Could not find required value %s in group %s: %s";
00699         SERVER s;
00700         gchar *virtstyle=NULL;
00701         PARAM lp[] = {
00702                 { "exportname", TRUE,   PARAM_STRING,   NULL, 0 },
00703                 { "port",       TRUE,   PARAM_INT,      NULL, 0 },
00704                 { "authfile",   FALSE,  PARAM_STRING,   NULL, 0 },
00705                 { "filesize",   FALSE,  PARAM_INT,      NULL, 0 },
00706                 { "virtstyle",  FALSE,  PARAM_STRING,   NULL, 0 },
00707                 { "prerun",     FALSE,  PARAM_STRING,   NULL, 0 },
00708                 { "postrun",    FALSE,  PARAM_STRING,   NULL, 0 },
00709                 { "readonly",   FALSE,  PARAM_BOOL,     NULL, F_READONLY },
00710                 { "multifile",  FALSE,  PARAM_BOOL,     NULL, F_MULTIFILE },
00711                 { "copyonwrite", FALSE, PARAM_BOOL,     NULL, F_COPYONWRITE },
00712                 { "sparse_cow", FALSE,  PARAM_BOOL,     NULL, F_SPARSE },
00713                 { "sdp",        FALSE,  PARAM_BOOL,     NULL, F_SDP },
00714                 { "sync",       FALSE,  PARAM_BOOL,     NULL, F_SYNC },
00715                 { "listenaddr", FALSE,  PARAM_STRING,   NULL, 0 },
00716                 { "maxconnections", FALSE, PARAM_INT,   NULL, 0 },
00717         };
00718         const int lp_size=sizeof(lp)/sizeof(PARAM);
00719         PARAM gp[] = {
00720                 { "user",       FALSE, PARAM_STRING,    &runuser,       0 },
00721                 { "group",      FALSE, PARAM_STRING,    &rungroup,      0 },
00722                 { "oldstyle",   FALSE, PARAM_BOOL,      &do_oldstyle,   1 },
00723                 { "listenaddr", FALSE, PARAM_STRING,    &modern_listen, 0 },
00724         };
00725         PARAM* p=gp;
00726         int p_size=sizeof(gp)/sizeof(PARAM);
00727         GKeyFile *cfile;
00728         GError *err = NULL;
00729         const char *err_msg=NULL;
00730         GQuark errdomain;
00731         GArray *retval=NULL;
00732         gchar **groups;
00733         gboolean value;
00734         gchar* startgroup;
00735         gint i;
00736         gint j;
00737 
00738         errdomain = g_quark_from_string("parse_cfile");
00739         cfile = g_key_file_new();
00740         retval = g_array_new(FALSE, TRUE, sizeof(SERVER));
00741         if(!g_key_file_load_from_file(cfile, f, G_KEY_FILE_KEEP_COMMENTS |
00742                         G_KEY_FILE_KEEP_TRANSLATIONS, &err)) {
00743                 g_set_error(e, errdomain, CFILE_NOTFOUND, "Could not open config file %s.", f);
00744                 g_key_file_free(cfile);
00745                 return retval;
00746         }
00747         startgroup = g_key_file_get_start_group(cfile);
00748         if(!startgroup || strcmp(startgroup, "generic")) {
00749                 g_set_error(e, errdomain, CFILE_MISSING_GENERIC, "Config file does not contain the [generic] group!");
00750                 g_key_file_free(cfile);
00751                 return NULL;
00752         }
00753         groups = g_key_file_get_groups(cfile, NULL);
00754         for(i=0;groups[i];i++) {
00755                 memset(&s, '\0', sizeof(SERVER));
00756                 lp[0].target=&(s.exportname);
00757                 lp[1].target=&(s.port);
00758                 lp[2].target=&(s.authname);
00759                 lp[3].target=&(s.expected_size);
00760                 lp[4].target=&(virtstyle);
00761                 lp[5].target=&(s.prerun);
00762                 lp[6].target=&(s.postrun);
00763                 lp[7].target=lp[8].target=lp[9].target=
00764                                 lp[10].target=lp[11].target=
00765                                 lp[12].target=&(s.flags);
00766                 lp[13].target=&(s.listenaddr);
00767                 lp[14].target=&(s.max_connections);
00768 
00769                 /* After the [generic] group, start parsing exports */
00770                 if(i==1) {
00771                         p=lp;
00772                         p_size=lp_size;
00773                 } 
00774                 for(j=0;j<p_size;j++) {
00775                         g_assert(p[j].target != NULL);
00776                         g_assert(p[j].ptype==PARAM_INT||p[j].ptype==PARAM_STRING||p[j].ptype==PARAM_BOOL);
00777                         switch(p[j].ptype) {
00778                                 case PARAM_INT:
00779                                         *((gint*)p[j].target) =
00780                                                 g_key_file_get_integer(cfile,
00781                                                                 groups[i],
00782                                                                 p[j].paramname,
00783                                                                 &err);
00784                                         break;
00785                                 case PARAM_STRING:
00786                                         *((gchar**)p[j].target) =
00787                                                 g_key_file_get_string(cfile,
00788                                                                 groups[i],
00789                                                                 p[j].paramname,
00790                                                                 &err);
00791                                         break;
00792                                 case PARAM_BOOL:
00793                                         value = g_key_file_get_boolean(cfile,
00794                                                         groups[i],
00795                                                         p[j].paramname, &err);
00796                                         if(!err) {
00797                                                 if(value) {
00798                                                         *((gint*)p[j].target) |= p[j].flagval;
00799                                                 } else {
00800                                                         *((gint*)p[j].target) &= ~(p[j].flagval);
00801                                                 }
00802                                         }
00803                                         break;
00804                         }
00805                         if(!strcmp(p[j].paramname, "port") && !strcmp(p[j].target, NBD_DEFAULT_PORT)) {
00806                                 g_set_error(e, errdomain, CFILE_INCORRECT_PORT, "Config file specifies default port for oldstyle export");
00807                                 g_key_file_free(cfile);
00808                                 return NULL;
00809                         }
00810                         if(err) {
00811                                 if(err->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND) {
00812                                         if(!p[j].required) {
00813                                                 /* Ignore not-found error for optional values */
00814                                                 g_clear_error(&err);
00815                                                 continue;
00816                                         } else {
00817                                                 err_msg = MISSING_REQUIRED_ERROR;
00818                                         }
00819                                 } else {
00820                                         err_msg = DEFAULT_ERROR;
00821                                 }
00822                                 g_set_error(e, errdomain, CFILE_VALUE_INVALID, err_msg, p[j].paramname, groups[i], err->message);
00823                                 g_array_free(retval, TRUE);
00824                                 g_error_free(err);
00825                                 g_key_file_free(cfile);
00826                                 return NULL;
00827                         }
00828                 }
00829                 if(virtstyle) {
00830                         if(!strncmp(virtstyle, "none", 4)) {
00831                                 s.virtstyle=VIRT_NONE;
00832                         } else if(!strncmp(virtstyle, "ipliteral", 9)) {
00833                                 s.virtstyle=VIRT_IPLIT;
00834                         } else if(!strncmp(virtstyle, "iphash", 6)) {
00835                                 s.virtstyle=VIRT_IPHASH;
00836                         } else if(!strncmp(virtstyle, "cidrhash", 8)) {
00837                                 s.virtstyle=VIRT_CIDR;
00838                                 if(strlen(virtstyle)<10) {
00839                                         g_set_error(e, errdomain, CFILE_VALUE_INVALID, "Invalid value %s for parameter virtstyle in group %s: missing length", virtstyle, groups[i]);
00840                                         g_array_free(retval, TRUE);
00841                                         g_key_file_free(cfile);
00842                                         return NULL;
00843                                 }
00844                                 s.cidrlen=strtol(virtstyle+8, NULL, 0);
00845                         } else {
00846                                 g_set_error(e, errdomain, CFILE_VALUE_INVALID, "Invalid value %s for parameter virtstyle in group %s", virtstyle, groups[i]);
00847                                 g_array_free(retval, TRUE);
00848                                 g_key_file_free(cfile);
00849                                 return NULL;
00850                         }
00851                         if(s.port && !do_oldstyle) {
00852                                 g_warning("A port was specified, but oldstyle exports were not requested. This may not do what you expect.");
00853                                 g_warning("Please read 'man 5 nbd-server' and search for oldstyle for more info");
00854                         }
00855                 } else {
00856                         s.virtstyle=VIRT_IPLIT;
00857                 }
00858                 /* Don't need to free this, it's not our string */
00859                 virtstyle=NULL;
00860                 /* Don't append values for the [generic] group */
00861                 if(i>0) {
00862                         s.socket_family = AF_UNSPEC;
00863                         s.servename = groups[i];
00864 
00865                         append_serve(&s, retval);
00866                 } else {
00867                         if(!do_oldstyle) {
00868                                 lp[1].required = 0;
00869                         }
00870                 }
00871 #ifndef WITH_SDP
00872                 if(s.flags & F_SDP) {
00873                         g_set_error(e, errdomain, CFILE_VALUE_UNSUPPORTED, "This nbd-server was built without support for SDP, yet group %s uses it", groups[i]);
00874                         g_array_free(retval, TRUE);
00875                         g_key_file_free(cfile);
00876                         return NULL;
00877                 }
00878 #endif
00879         }
00880         if(i==1) {
00881                 g_set_error(e, errdomain, CFILE_NO_EXPORTS, "The config file does not specify any exports");
00882         }
00883         g_key_file_free(cfile);
00884         return retval;
00885 }
00886 
00887 /**
00888  * Signal handler for SIGCHLD
00889  * @param s the signal we're handling (must be SIGCHLD, or something
00890  * is severely wrong)
00891  **/
00892 void sigchld_handler(int s) {
00893         int status;
00894         int* i;
00895         pid_t pid;
00896 
00897         while((pid=waitpid(-1, &status, WNOHANG)) > 0) {
00898                 if(WIFEXITED(status)) {
00899                         msg3(LOG_INFO, "Child exited with %d", WEXITSTATUS(status));
00900                 }
00901                 i=g_hash_table_lookup(children, &pid);
00902                 if(!i) {
00903                         msg3(LOG_INFO, "SIGCHLD received for an unknown child with PID %ld", (long)pid);
00904                 } else {
00905                         DEBUG2("Removing %d from the list of children", pid);
00906                         g_hash_table_remove(children, &pid);
00907                 }
00908         }
00909 }
00910 
00911 /**
00912  * Kill a child. Called from sigterm_handler::g_hash_table_foreach.
00913  *
00914  * @param key the key
00915  * @param value the value corresponding to the above key
00916  * @param user_data a pointer which we always set to 1, so that we know what
00917  * will happen next.
00918  **/
00919 void killchild(gpointer key, gpointer value, gpointer user_data) {
00920         pid_t *pid=value;
00921         int *parent=user_data;
00922 
00923         kill(*pid, SIGTERM);
00924         *parent=1;
00925 }
00926 
00927 /**
00928  * Handle SIGTERM and dispatch it to our children
00929  * @param s the signal we're handling (must be SIGTERM, or something
00930  * is severely wrong).
00931  **/
00932 void sigterm_handler(int s) {
00933         int parent=0;
00934 
00935         g_hash_table_foreach(children, killchild, &parent);
00936 
00937         if(parent) {
00938                 unlink(pidfname);
00939         }
00940 
00941         exit(EXIT_SUCCESS);
00942 }
00943 
00944 /**
00945  * Detect the size of a file.
00946  *
00947  * @param fhandle An open filedescriptor
00948  * @return the size of the file, or OFFT_MAX if detection was
00949  * impossible.
00950  **/
00951 off_t size_autodetect(int fhandle) {
00952         off_t es;
00953         u64 bytes;
00954         struct stat stat_buf;
00955         int error;
00956 
00957 #ifdef HAVE_SYS_MOUNT_H
00958 #ifdef HAVE_SYS_IOCTL_H
00959 #ifdef BLKGETSIZE64
00960         DEBUG("looking for export size with ioctl BLKGETSIZE64\n");
00961         if (!ioctl(fhandle, BLKGETSIZE64, &bytes) && bytes) {
00962                 return (off_t)bytes;
00963         }
00964 #endif /* BLKGETSIZE64 */
00965 #endif /* HAVE_SYS_IOCTL_H */
00966 #endif /* HAVE_SYS_MOUNT_H */
00967 
00968         DEBUG("looking for fhandle size with fstat\n");
00969         stat_buf.st_size = 0;
00970         error = fstat(fhandle, &stat_buf);
00971         if (!error) {
00972                 if(stat_buf.st_size > 0)
00973                         return (off_t)stat_buf.st_size;
00974         } else {
00975                 err("fstat failed: %m");
00976         }
00977 
00978         DEBUG("looking for fhandle size with lseek SEEK_END\n");
00979         es = lseek(fhandle, (off_t)0, SEEK_END);
00980         if (es > ((off_t)0)) {
00981                 return es;
00982         } else {
00983                 DEBUG2("lseek failed: %d", errno==EBADF?1:(errno==ESPIPE?2:(errno==EINVAL?3:4)));
00984         }
00985 
00986         err("Could not find size of exported block device: %m");
00987         return OFFT_MAX;
00988 }
00989 
00990 /**
00991  * Get the file handle and offset, given an export offset.
00992  *
00993  * @param export An array of export files
00994  * @param a The offset to get corresponding file/offset for
00995  * @param fhandle [out] File descriptor
00996  * @param foffset [out] Offset into fhandle
00997  * @param maxbytes [out] Tells how many bytes can be read/written
00998  * from fhandle starting at foffset (0 if there is no limit)
00999  * @return 0 on success, -1 on failure
01000  **/
01001 int get_filepos(GArray* export, off_t a, int* fhandle, off_t* foffset, size_t* maxbytes ) {
01002         /* Negative offset not allowed */
01003         if(a < 0)
01004                 return -1;
01005 
01006         /* Binary search for last file with starting offset <= a */
01007         FILE_INFO fi;
01008         int start = 0;
01009         int end = export->len - 1;
01010         while( start <= end ) {
01011                 int mid = (start + end) / 2;
01012                 fi = g_array_index(export, FILE_INFO, mid);
01013                 if( fi.startoff < a ) {
01014                         start = mid + 1;
01015                 } else if( fi.startoff > a ) {
01016                         end = mid - 1;
01017                 } else {
01018                         start = end = mid;
01019                         break;
01020                 }
01021         }
01022 
01023         /* end should never go negative, since first startoff is 0 and a >= 0 */
01024         g_assert(end >= 0);
01025 
01026         fi = g_array_index(export, FILE_INFO, end);
01027         *fhandle = fi.fhandle;
01028         *foffset = a - fi.startoff;
01029         *maxbytes = 0;
01030         if( end+1 < export->len ) {
01031                 FILE_INFO fi_next = g_array_index(export, FILE_INFO, end+1);
01032                 *maxbytes = fi_next.startoff - a;
01033         }
01034 
01035         return 0;
01036 }
01037 
01038 /**
01039  * seek to a position in a file, with error handling.
01040  * @param handle a filedescriptor
01041  * @param a position to seek to
01042  * @todo get rid of this; lastpoint is a global variable right now, but it
01043  * shouldn't be. If we pass it on as a parameter, that makes things a *lot*
01044  * easier.
01045  **/
01046 void myseek(int handle,off_t a) {
01047         if (lseek(handle, a, SEEK_SET) < 0) {
01048                 err("Can not seek locally!\n");
01049         }
01050 }
01051 
01052 /**
01053  * Write an amount of bytes at a given offset to the right file. This
01054  * abstracts the write-side of the multiple file option.
01055  *
01056  * @param a The offset where the write should start
01057  * @param buf The buffer to write from
01058  * @param len The length of buf
01059  * @param client The client we're serving for
01060  * @return The number of bytes actually written, or -1 in case of an error
01061  **/
01062 ssize_t rawexpwrite(off_t a, char *buf, size_t len, CLIENT *client) {
01063         int fhandle;
01064         off_t foffset;
01065         size_t maxbytes;
01066         ssize_t retval;
01067 
01068         if(get_filepos(client->export, a, &fhandle, &foffset, &maxbytes))
01069                 return -1;
01070         if(maxbytes && len > maxbytes)
01071                 len = maxbytes;
01072 
01073         DEBUG4("(WRITE to fd %d offset %llu len %u), ", fhandle, foffset, len);
01074 
01075         myseek(fhandle, foffset);
01076         retval = write(fhandle, buf, len);
01077         if(client->server->flags & F_SYNC) {
01078                 fsync(fhandle);
01079         }
01080         return retval;
01081 }
01082 
01083 /**
01084  * Call rawexpwrite repeatedly until all data has been written.
01085  * @return 0 on success, nonzero on failure
01086  **/
01087 int rawexpwrite_fully(off_t a, char *buf, size_t len, CLIENT *client) {
01088         ssize_t ret=0;
01089 
01090         while(len > 0 && (ret=rawexpwrite(a, buf, len, client)) > 0 ) {
01091                 a += ret;
01092                 buf += ret;
01093                 len -= ret;
01094         }
01095         return (ret < 0 || len != 0);
01096 }
01097 
01098 /**
01099  * Read an amount of bytes at a given offset from the right file. This
01100  * abstracts the read-side of the multiple files option.
01101  *
01102  * @param a The offset where the read should start
01103  * @param buf A buffer to read into
01104  * @param len The size of buf
01105  * @param client The client we're serving for
01106  * @return The number of bytes actually read, or -1 in case of an
01107  * error.
01108  **/
01109 ssize_t rawexpread(off_t a, char *buf, size_t len, CLIENT *client) {
01110         int fhandle;
01111         off_t foffset;
01112         size_t maxbytes;
01113 
01114         if(get_filepos(client->export, a, &fhandle, &foffset, &maxbytes))
01115                 return -1;
01116         if(maxbytes && len > maxbytes)
01117                 len = maxbytes;
01118 
01119         DEBUG4("(READ from fd %d offset %llu len %u), ", fhandle, foffset, len);
01120 
01121         myseek(fhandle, foffset);
01122         return read(fhandle, buf, len);
01123 }
01124 
01125 /**
01126  * Call rawexpread repeatedly until all data has been read.
01127  * @return 0 on success, nonzero on failure
01128  **/
01129 int rawexpread_fully(off_t a, char *buf, size_t len, CLIENT *client) {
01130         ssize_t ret=0;
01131 
01132         while(len > 0 && (ret=rawexpread(a, buf, len, client)) > 0 ) {
01133                 a += ret;
01134                 buf += ret;
01135                 len -= ret;
01136         }
01137         return (ret < 0 || len != 0);
01138 }
01139 
01140 /**
01141  * Read an amount of bytes at a given offset from the right file. This
01142  * abstracts the read-side of the copyonwrite stuff, and calls
01143  * rawexpread() with the right parameters to do the actual work.
01144  * @param a The offset where the read should start
01145  * @param buf A buffer to read into
01146  * @param len The size of buf
01147  * @param client The client we're going to read for
01148  * @return 0 on success, nonzero on failure
01149  **/
01150 int expread(off_t a, char *buf, size_t len, CLIENT *client) {
01151         off_t rdlen, offset;
01152         off_t mapcnt, mapl, maph, pagestart;
01153 
01154         if (!(client->server->flags & F_COPYONWRITE))
01155                 return(rawexpread_fully(a, buf, len, client));
01156         DEBUG3("Asked to read %d bytes at %llu.\n", len, (unsigned long long)a);
01157 
01158         mapl=a/DIFFPAGESIZE; maph=(a+len-1)/DIFFPAGESIZE;
01159 
01160         for (mapcnt=mapl;mapcnt<=maph;mapcnt++) {
01161                 pagestart=mapcnt*DIFFPAGESIZE;
01162                 offset=a-pagestart;
01163                 rdlen=(0<DIFFPAGESIZE-offset && len<(size_t)(DIFFPAGESIZE-offset)) ?
01164                         len : (size_t)DIFFPAGESIZE-offset;
01165                 if (client->difmap[mapcnt]!=(u32)(-1)) { /* the block is already there */
01166                         DEBUG3("Page %llu is at %lu\n", (unsigned long long)mapcnt,
01167                                (unsigned long)(client->difmap[mapcnt]));
01168                         myseek(client->difffile, client->difmap[mapcnt]*DIFFPAGESIZE+offset);
01169                         if (read(client->difffile, buf, rdlen) != rdlen) return -1;
01170                 } else { /* the block is not there */
01171                         DEBUG2("Page %llu is not here, we read the original one\n",
01172                                (unsigned long long)mapcnt);
01173                         if(rawexpread_fully(a, buf, rdlen, client)) return -1;
01174                 }
01175                 len-=rdlen; a+=rdlen; buf+=rdlen;
01176         }
01177         return 0;
01178 }
01179 
01180 /**
01181  * Write an amount of bytes at a given offset to the right file. This
01182  * abstracts the write-side of the copyonwrite option, and calls
01183  * rawexpwrite() with the right parameters to do the actual work.
01184  *
01185  * @param a The offset where the write should start
01186  * @param buf The buffer to write from
01187  * @param len The length of buf
01188  * @param client The client we're going to write for.
01189  * @return 0 on success, nonzero on failure
01190  **/
01191 int expwrite(off_t a, char *buf, size_t len, CLIENT *client) {
01192         char pagebuf[DIFFPAGESIZE];
01193         off_t mapcnt,mapl,maph;
01194         off_t wrlen,rdlen; 
01195         off_t pagestart;
01196         off_t offset;
01197 
01198         if (!(client->server->flags & F_COPYONWRITE))
01199                 return(rawexpwrite_fully(a, buf, len, client)); 
01200         DEBUG3("Asked to write %d bytes at %llu.\n", len, (unsigned long long)a);
01201 
01202         mapl=a/DIFFPAGESIZE ; maph=(a+len-1)/DIFFPAGESIZE ;
01203 
01204         for (mapcnt=mapl;mapcnt<=maph;mapcnt++) {
01205                 pagestart=mapcnt*DIFFPAGESIZE ;
01206                 offset=a-pagestart ;
01207                 wrlen=(0<DIFFPAGESIZE-offset && len<(size_t)(DIFFPAGESIZE-offset)) ?
01208                         len : (size_t)DIFFPAGESIZE-offset;
01209 
01210                 if (client->difmap[mapcnt]!=(u32)(-1)) { /* the block is already there */
01211                         DEBUG3("Page %llu is at %lu\n", (unsigned long long)mapcnt,
01212                                (unsigned long)(client->difmap[mapcnt])) ;
01213                         myseek(client->difffile,
01214                                         client->difmap[mapcnt]*DIFFPAGESIZE+offset);
01215                         if (write(client->difffile, buf, wrlen) != wrlen) return -1 ;
01216                 } else { /* the block is not there */
01217                         myseek(client->difffile,client->difffilelen*DIFFPAGESIZE) ;
01218                         client->difmap[mapcnt]=(client->server->flags&F_SPARSE)?mapcnt:client->difffilelen++;
01219                         DEBUG3("Page %llu is not here, we put it at %lu\n",
01220                                (unsigned long long)mapcnt,
01221                                (unsigned long)(client->difmap[mapcnt]));
01222                         rdlen=DIFFPAGESIZE ;
01223                         if (rawexpread_fully(pagestart, pagebuf, rdlen, client))
01224                                 return -1;
01225                         memcpy(pagebuf+offset,buf,wrlen) ;
01226                         if (write(client->difffile, pagebuf, DIFFPAGESIZE) !=
01227                                         DIFFPAGESIZE)
01228                                 return -1;
01229                 }                                                   
01230                 len-=wrlen ; a+=wrlen ; buf+=wrlen ;
01231         }
01232         return 0;
01233 }
01234 
01235 /**
01236  * Do the initial negotiation.
01237  *
01238  * @param client The client we're negotiating with.
01239  **/
01240 CLIENT* negotiate(int net, CLIENT *client, GArray* servers) {
01241         char zeros[128];
01242         uint64_t size_host;
01243         uint32_t flags = NBD_FLAG_HAS_FLAGS;
01244         uint16_t smallflags = 0;
01245         uint64_t magic;
01246 
01247         memset(zeros, '\0', sizeof(zeros));
01248         if(!client || !client->modern) {
01249                 /* common */
01250                 if (write(net, INIT_PASSWD, 8) < 0) {
01251                         err_nonfatal("Negotiation failed: %m");
01252                         if(client)
01253                                 exit(EXIT_FAILURE);
01254                 }
01255                 if(!client || client->modern) {
01256                         /* modern */
01257                         magic = htonll(opts_magic);
01258                 } else {
01259                         /* oldstyle */
01260                         magic = htonll(cliserv_magic);
01261                 }
01262                 if (write(net, &magic, sizeof(magic)) < 0) {
01263                         err_nonfatal("Negotiation failed: %m");
01264                         if(client)
01265                                 exit(EXIT_FAILURE);
01266                 }
01267         }
01268         if(!client) {
01269                 /* modern */
01270                 uint32_t reserved;
01271                 uint32_t opt;
01272                 uint32_t namelen;
01273                 char* name;
01274                 int i;
01275 
01276                 if(!servers)
01277                         err("programmer error");
01278                 if (write(net, &smallflags, sizeof(uint16_t)) < 0)
01279                         err("Negotiation failed: %m");
01280                 if (read(net, &reserved, sizeof(reserved)) < 0)
01281                         err("Negotiation failed: %m");
01282                 if (read(net, &magic, sizeof(magic)) < 0)
01283                         err("Negotiation failed: %m");
01284                 magic = ntohll(magic);
01285                 if(magic != opts_magic) {
01286                         close(net);
01287                         return NULL;
01288                 }
01289                 if (read(net, &opt, sizeof(opt)) < 0)
01290                         err("Negotiation failed: %m");
01291                 opt = ntohl(opt);
01292                 if(opt != NBD_OPT_EXPORT_NAME) {
01293                         close(net);
01294                         return NULL;
01295                 }
01296                 if (read(net, &namelen, sizeof(namelen)) < 0)
01297                         err("Negotiation failed: %m");
01298                 namelen = ntohl(namelen);
01299                 name = malloc(namelen+1);
01300                 name[namelen]=0;
01301                 if (read(net, name, namelen) < 0)
01302                         err("Negotiation failed: %m");
01303                 for(i=0; i<servers->len; i++) {
01304                         SERVER* serve = &(g_array_index(servers, SERVER, i));
01305                         if(!strcmp(serve->servename, name)) {
01306                                 CLIENT* client = g_new0(CLIENT, 1);
01307                                 client->server = serve;
01308                                 client->exportsize = OFFT_MAX;
01309                                 client->net = net;
01310                                 client->modern = TRUE;
01311                                 return client;
01312                         }
01313                 }
01314                 return NULL;
01315         }
01316         /* common */
01317         size_host = htonll((u64)(client->exportsize));
01318         if (write(net, &size_host, 8) < 0)
01319                 err("Negotiation failed: %m");
01320         if (client->server->flags & F_READONLY)
01321                 flags |= NBD_FLAG_READ_ONLY;
01322         if (!client->modern) {
01323                 /* oldstyle */
01324                 flags = htonl(flags);
01325                 if (write(client->net, &flags, 4) < 0)
01326                         err("Negotiation failed: %m");
01327         } else {
01328                 /* modern */
01329                 smallflags = (uint16_t)(flags & ~((uint16_t)0));
01330                 smallflags = htons(smallflags);
01331                 if (write(client->net, &smallflags, sizeof(smallflags)) < 0) {
01332                         err("Negotiation failed: %m");
01333                 }
01334         }
01335         /* common */
01336         if (write(client->net, zeros, 124) < 0)
01337                 err("Negotiation failed: %m");
01338         return NULL;
01339 }
01340 
01341 /** sending macro. */
01342 #define SEND(net,reply) writeit( net, &reply, sizeof( reply ));
01343 /** error macro. */
01344 #define ERROR(client,reply,errcode) { reply.error = htonl(errcode); SEND(client->net,reply); reply.error = 0; }
01345 /**
01346  * Serve a file to a single client.
01347  *
01348  * @todo This beast needs to be split up in many tiny little manageable
01349  * pieces. Preferably with a chainsaw.
01350  *
01351  * @param client The client we're going to serve to.
01352  * @return when the client disconnects
01353  **/
01354 int mainloop(CLIENT *client) {
01355         struct nbd_request request;
01356         struct nbd_reply reply;
01357         gboolean go_on=TRUE;
01358 #ifdef DODBG
01359         int i = 0;
01360 #endif
01361         negotiate(client->net, client, NULL);
01362         DEBUG("Entering request loop!\n");
01363         reply.magic = htonl(NBD_REPLY_MAGIC);
01364         reply.error = 0;
01365         while (go_on) {
01366                 char buf[BUFSIZE];
01367                 char* p;
01368                 size_t len;
01369                 size_t currlen;
01370                 size_t writelen;
01371 #ifdef DODBG
01372                 i++;
01373                 printf("%d: ", i);
01374 #endif
01375                 readit(client->net, &request, sizeof(request));
01376                 request.from = ntohll(request.from);
01377                 request.type = ntohl(request.type);
01378 
01379                 if (request.type==NBD_CMD_DISC) {
01380                         msg2(LOG_INFO, "Disconnect request received.");
01381                         if (client->server->flags & F_COPYONWRITE) { 
01382                                 if (client->difmap) g_free(client->difmap) ;
01383                                 close(client->difffile);
01384                                 unlink(client->difffilename);
01385                                 free(client->difffilename);
01386                         }
01387                         go_on=FALSE;
01388                         continue;
01389                 }
01390 
01391                 len = ntohl(request.len);
01392 
01393                 if (request.magic != htonl(NBD_REQUEST_MAGIC))
01394                         err("Not enough magic.");
01395                 if (len > BUFSIZE - sizeof(struct nbd_reply)) {
01396                         currlen = BUFSIZE - sizeof(struct nbd_reply);
01397                         msg2(LOG_INFO, "oversized request (this is not a problem)");
01398                 } else {
01399                         currlen = len;
01400                 }
01401 #ifdef DODBG
01402                 printf("%s from %llu (%llu) len %d, ", request.type ? "WRITE" :
01403                                 "READ", (unsigned long long)request.from,
01404                                 (unsigned long long)request.from / 512, len);
01405 #endif
01406                 memcpy(reply.handle, request.handle, sizeof(reply.handle));
01407                 if ((request.from + len) > (OFFT_MAX)) {
01408                         DEBUG("[Number too large!]");
01409                         ERROR(client, reply, EINVAL);
01410                         continue;
01411                 }
01412 
01413                 if (((ssize_t)((off_t)request.from + len) > client->exportsize)) {
01414                         DEBUG("[RANGE!]");
01415                         ERROR(client, reply, EINVAL);
01416                         continue;
01417                 }
01418 
01419                 if (request.type==NBD_CMD_WRITE) {
01420                         DEBUG("wr: net->buf, ");
01421                         while(len > 0) {
01422                                 readit(client->net, buf, currlen);
01423                                 DEBUG("buf->exp, ");
01424                                 if ((client->server->flags & F_READONLY) ||
01425                                     (client->server->flags & F_AUTOREADONLY)) {
01426                                         DEBUG("[WRITE to READONLY!]");
01427                                         ERROR(client, reply, EPERM);
01428                                         continue;
01429                                 }
01430                                 if (expwrite(request.from, buf, len, client)) {
01431                                         DEBUG("Write failed: %m" );
01432                                         ERROR(client, reply, errno);
01433                                         continue;
01434                                 }
01435                                 SEND(client->net, reply);
01436                                 DEBUG("OK!\n");
01437                                 len -= currlen;
01438                                 currlen = (len < BUFSIZE) ? len : BUFSIZE;
01439                         }
01440                         continue;
01441                 }
01442                 /* READ */
01443 
01444                 DEBUG("exp->buf, ");
01445                 memcpy(buf, &reply, sizeof(struct nbd_reply));
01446                 p = buf + sizeof(struct nbd_reply);
01447                 writelen = currlen + sizeof(struct nbd_reply);
01448                 while(len > 0) {
01449                         if (expread(request.from, p, currlen, client)) {
01450                                 DEBUG("Read failed: %m");
01451                                 ERROR(client, reply, errno);
01452                                 continue;
01453                         }
01454 
01455                         DEBUG("buf->net, ");
01456                         writeit(client->net, buf, writelen);
01457                         len -= currlen;
01458                         currlen = (len < BUFSIZE) ? len : BUFSIZE;
01459                         p = buf;
01460                         writelen = currlen;
01461                 }
01462                 DEBUG("OK!\n");
01463         }
01464         return 0;
01465 }
01466 
01467 /**
01468  * Set up client export array, which is an array of FILE_INFO.
01469  * Also, split a single exportfile into multiple ones, if that was asked.
01470  * @param client information on the client which we want to setup export for
01471  **/
01472 void setupexport(CLIENT* client) {
01473         int i;
01474         off_t laststartoff = 0, lastsize = 0;
01475         int multifile = (client->server->flags & F_MULTIFILE);
01476 
01477         client->export = g_array_new(TRUE, TRUE, sizeof(FILE_INFO));
01478 
01479         /* If multi-file, open as many files as we can.
01480          * If not, open exactly one file.
01481          * Calculate file sizes as we go to get total size. */
01482         for(i=0; ; i++) {
01483                 FILE_INFO fi;
01484                 gchar *tmpname;
01485                 gchar* error_string;
01486                 mode_t mode = (client->server->flags & F_READONLY) ? O_RDONLY : O_RDWR;
01487 
01488                 if(multifile) {
01489                         tmpname=g_strdup_printf("%s.%d", client->exportname, i);
01490                 } else {
01491                         tmpname=g_strdup(client->exportname);
01492                 }
01493                 DEBUG2( "Opening %s\n", tmpname );
01494                 fi.fhandle = open(tmpname, mode);
01495                 if(fi.fhandle == -1 && mode == O_RDWR) {
01496                         /* Try again because maybe media was read-only */
01497                         fi.fhandle = open(tmpname, O_RDONLY);
01498                         if(fi.fhandle != -1) {
01499                                 /* Opening the base file in copyonwrite mode is
01500                                  * okay */
01501                                 if(!(client->server->flags & F_COPYONWRITE)) {
01502                                         client->server->flags |= F_AUTOREADONLY;
01503                                         client->server->flags |= F_READONLY;
01504                                 }
01505                         }
01506                 }
01507                 if(fi.fhandle == -1) {
01508                         if(multifile && i>0)
01509                                 break;
01510                         error_string=g_strdup_printf(
01511                                 "Could not open exported file %s: %%m",
01512                                 tmpname);
01513                         err(error_string);
01514                 }
01515                 fi.startoff = laststartoff + lastsize;
01516                 g_array_append_val(client->export, fi);
01517                 g_free(tmpname);
01518 
01519                 /* Starting offset and size of this file will be used to
01520                  * calculate starting offset of next file */
01521                 laststartoff = fi.startoff;
01522                 lastsize = size_autodetect(fi.fhandle);
01523 
01524                 if(!multifile)
01525                         break;
01526         }
01527 
01528         /* Set export size to total calculated size */
01529         client->exportsize = laststartoff + lastsize;
01530 
01531         /* Export size may be overridden */
01532         if(client->server->expected_size) {
01533                 /* desired size must be <= total calculated size */
01534                 if(client->server->expected_size > client->exportsize) {
01535                         err("Size of exported file is too big\n");
01536                 }
01537 
01538                 client->exportsize = client->server->expected_size;
01539         }
01540 
01541         msg3(LOG_INFO, "Size of exported file/device is %llu", (unsigned long long)client->exportsize);
01542         if(multifile) {
01543                 msg3(LOG_INFO, "Total number of files: %d", i);
01544         }
01545 }
01546 
01547 int copyonwrite_prepare(CLIENT* client) {
01548         off_t i;
01549         if ((client->difffilename = malloc(1024))==NULL)
01550                 err("Failed to allocate string for diff file name");
01551         snprintf(client->difffilename, 1024, "%s-%s-%d.diff",client->exportname,client->clientname,
01552                 (int)getpid()) ;
01553         client->difffilename[1023]='\0';
01554         msg3(LOG_INFO,"About to create map and diff file %s",client->difffilename) ;
01555         client->difffile=open(client->difffilename,O_RDWR | O_CREAT | O_TRUNC,0600) ;
01556         if (client->difffile<0) err("Could not create diff file (%m)") ;
01557         if ((client->difmap=calloc(client->exportsize/DIFFPAGESIZE,sizeof(u32)))==NULL)
01558                 err("Could not allocate memory") ;
01559         for (i=0;i<client->exportsize/DIFFPAGESIZE;i++) client->difmap[i]=(u32)-1 ;
01560 
01561         return 0;
01562 }
01563 
01564 /**
01565  * Run a command. This is used for the ``prerun'' and ``postrun'' config file
01566  * options
01567  *
01568  * @param command the command to be ran. Read from the config file
01569  * @param file the file name we're about to export
01570  **/
01571 int do_run(gchar* command, gchar* file) {
01572         gchar* cmd;
01573         int retval=0;
01574 
01575         if(command && *command) {
01576                 cmd = g_strdup_printf(command, file);
01577                 retval=system(cmd);
01578                 g_free(cmd);
01579         }
01580         return retval;
01581 }
01582 
01583 /**
01584  * Serve a connection. 
01585  *
01586  * @todo allow for multithreading, perhaps use libevent. Not just yet, though;
01587  * follow the road map.
01588  *
01589  * @param client a connected client
01590  **/
01591 void serveconnection(CLIENT *client) {
01592         if(do_run(client->server->prerun, client->exportname)) {
01593                 exit(EXIT_FAILURE);
01594         }
01595         setupexport(client);
01596 
01597         if (client->server->flags & F_COPYONWRITE) {
01598                 copyonwrite_prepare(client);
01599         }
01600 
01601         setmysockopt(client->net);
01602 
01603         mainloop(client);
01604         do_run(client->server->postrun, client->exportname);
01605 }
01606 
01607 /**
01608  * Find the name of the file we have to serve. This will use g_strdup_printf
01609  * to put the IP address of the client inside a filename containing
01610  * "%s" (in the form as specified by the "virtstyle" option). That name
01611  * is then written to client->exportname.
01612  *
01613  * @param net A socket connected to an nbd client
01614  * @param client information about the client. The IP address in human-readable
01615  * format will be written to a new char* buffer, the address of which will be
01616  * stored in client->clientname.
01617  **/
01618 void set_peername(int net, CLIENT *client) {
01619         struct sockaddr_storage addrin;
01620         struct sockaddr_storage netaddr;
01621         struct sockaddr_in  *netaddr4 = NULL;
01622         struct sockaddr_in6 *netaddr6 = NULL;
01623         socklen_t addrinlen = sizeof( addrin );
01624         struct addrinfo hints;
01625         struct addrinfo *ai = NULL;
01626         char peername[NI_MAXHOST];
01627         char netname[NI_MAXHOST];
01628         char *tmp = NULL;
01629         int i;
01630         int e;
01631         int shift;
01632 
01633         if (getpeername(net, (struct sockaddr *) &addrin, &addrinlen) < 0)
01634                 err("getsockname failed: %m");
01635 
01636         getnameinfo((struct sockaddr *)&addrin, addrinlen,
01637                 peername, sizeof (peername), NULL, 0, NI_NUMERICHOST);
01638 
01639         memset(&hints, '\0', sizeof (hints));
01640         hints.ai_flags = AI_ADDRCONFIG;
01641         e = getaddrinfo(peername, NULL, &hints, &ai);
01642 
01643         if(e != 0) {
01644                 fprintf(stderr, "getaddrinfo failed: %s\n", gai_strerror(e));
01645                 freeaddrinfo(ai);
01646                 return;
01647         }
01648 
01649         switch(client->server->virtstyle) {
01650                 case VIRT_NONE:
01651                         client->exportname=g_strdup(client->server->exportname);
01652                         break;
01653                 case VIRT_IPHASH:
01654                         for(i=0;i<strlen(peername);i++) {
01655                                 if(peername[i]=='.') {
01656                                         peername[i]='/';
01657                                 }
01658                         }
01659                 case VIRT_IPLIT:
01660                         client->exportname=g_strdup_printf(client->server->exportname, peername);
01661                         break;
01662                 case VIRT_CIDR:
01663                         memcpy(&netaddr, &addrin, addrinlen);
01664                         if(ai->ai_family == AF_INET) {
01665                                 netaddr4 = (struct sockaddr_in *)&netaddr;
01666                                 (netaddr4->sin_addr).s_addr>>=32-(client->server->cidrlen);
01667                                 (netaddr4->sin_addr).s_addr<<=32-(client->server->cidrlen);
01668 
01669                                 getnameinfo((struct sockaddr *) netaddr4, addrinlen,
01670                                                         netname, sizeof (netname), NULL, 0, NI_NUMERICHOST);
01671                                 tmp=g_strdup_printf("%s/%s", netname, peername);
01672                         }else if(ai->ai_family == AF_INET6) {
01673                                 netaddr6 = (struct sockaddr_in6 *)&netaddr;
01674 
01675                                 shift = 128-(client->server->cidrlen);
01676                                 i = 3;
01677                                 while(shift >= 32) {
01678                                         ((netaddr6->sin6_addr).s6_addr32[i])=0;
01679                                         shift-=32;
01680                                         i--;
01681                                 }
01682                                 (netaddr6->sin6_addr).s6_addr32[i]>>=shift;
01683                                 (netaddr6->sin6_addr).s6_addr32[i]<<=shift;
01684 
01685                                 getnameinfo((struct sockaddr *)netaddr6, addrinlen,
01686                                             netname, sizeof(netname), NULL, 0, NI_NUMERICHOST);
01687                                 tmp=g_strdup_printf("%s/%s", netname, peername);
01688                         }
01689 
01690                         if(tmp != NULL)
01691                           client->exportname=g_strdup_printf(client->server->exportname, tmp);
01692 
01693                         break;
01694         }
01695 
01696         freeaddrinfo(ai);
01697         msg4(LOG_INFO, "connect from %s, assigned file is %s", 
01698              peername, client->exportname);
01699         client->clientname=g_strdup(peername);
01700 }
01701 
01702 /**
01703  * Destroy a pid_t*
01704  * @param data a pointer to pid_t which should be freed
01705  **/
01706 void destroy_pid_t(gpointer data) {
01707         g_free(data);
01708 }
01709 
01710 /**
01711  * Loop through the available servers, and serve them. Never returns.
01712  **/
01713 int serveloop(GArray* servers) {
01714         struct sockaddr_storage addrin;
01715         socklen_t addrinlen=sizeof(addrin);
01716         int i;
01717         int max;
01718         int sock;
01719         fd_set mset;
01720         fd_set rset;
01721 
01722         /* 
01723          * Set up the master fd_set. The set of descriptors we need
01724          * to select() for never changes anyway and it buys us a *lot*
01725          * of time to only build this once. However, if we ever choose
01726          * to not fork() for clients anymore, we may have to revisit
01727          * this.
01728          */
01729         max=0;
01730         FD_ZERO(&mset);
01731         for(i=0;i<servers->len;i++) {
01732                 if((sock=(g_array_index(servers, SERVER, i)).socket)) {
01733                         FD_SET(sock, &mset);
01734                         max=sock>max?sock:max;
01735                 }
01736         }
01737         if(modernsock) {
01738                 FD_SET(modernsock, &mset);
01739                 max=modernsock>max?modernsock:max;
01740         }
01741         for(;;) {
01742                 CLIENT *client = NULL;
01743                 pid_t *pid;
01744 
01745                 memcpy(&rset, &mset, sizeof(fd_set));
01746                 if(select(max+1, &rset, NULL, NULL, NULL)>0) {
01747                         int net = 0;
01748                         SERVER* serve;
01749 
01750                         DEBUG("accept, ");
01751                         if(FD_ISSET(modernsock, &rset)) {
01752                                 if((net=accept(modernsock, (struct sockaddr *) &addrin, &addrinlen)) < 0)
01753                                         err("accept: %m");
01754                                 client = negotiate(net, NULL, servers);
01755                                 if(!client) {
01756                                         err_nonfatal("negotiation failed");
01757                                         close(net);
01758                                         net=0;
01759                                 }
01760                         }
01761                         for(i=0;i<servers->len && !net;i++) {
01762                                 serve=&(g_array_index(servers, SERVER, i));
01763                                 if(FD_ISSET(serve->socket, &rset)) {
01764                                         if ((net=accept(serve->socket, (struct sockaddr *) &addrin, &addrinlen)) < 0)
01765                                                 err("accept: %m");
01766                                 }
01767                         }
01768                         if(net) {
01769                                 int sock_flags;
01770 
01771                                 if(serve->max_connections > 0 &&
01772                                    g_hash_table_size(children) >= serve->max_connections) {
01773                                         msg2(LOG_INFO, "Max connections reached");
01774                                         close(net);
01775                                         continue;
01776                                 }
01777                                 if((sock_flags = fcntl(net, F_GETFL, 0))==-1) {
01778                                         err("fcntl F_GETFL");
01779                                 }
01780                                 if(fcntl(net, F_SETFL, sock_flags &~O_NONBLOCK)==-1) {
01781                                         err("fcntl F_SETFL ~O_NONBLOCK");
01782                                 }
01783                                 if(!client) {
01784                                         client = g_new0(CLIENT, 1);
01785                                         client->server=serve;
01786                                         client->exportsize=OFFT_MAX;
01787                                         client->net=net;
01788                                 }
01789                                 set_peername(net, client);
01790                                 if (!authorized_client(client)) {
01791                                         msg2(LOG_INFO,"Unauthorized client") ;
01792                                         close(net);
01793                                         continue;
01794                                 }
01795                                 msg2(LOG_INFO,"Authorized client") ;
01796                                 pid=g_malloc(sizeof(pid_t));
01797 #ifndef NOFORK
01798                                 if ((*pid=fork())<0) {
01799                                         msg3(LOG_INFO,"Could not fork (%s)",strerror(errno)) ;
01800                                         close(net);
01801                                         continue;
01802                                 }
01803                                 if (*pid>0) { /* parent */
01804                                         close(net);
01805                                         g_hash_table_insert(children, pid, pid);
01806                                         continue;
01807                                 }
01808                                 /* child */
01809                                 g_hash_table_destroy(children);
01810                                 for(i=0;i<servers->len;i++) {
01811                                         serve=&g_array_index(servers, SERVER, i);
01812                                         close(serve->socket);
01813                                 }
01814                                 /* FALSE does not free the
01815                                 actual data. This is required,
01816                                 because the client has a
01817                                 direct reference into that
01818                                 data, and otherwise we get a
01819                                 segfault... */
01820                                 g_array_free(servers, FALSE);
01821 #endif // NOFORK
01822                                 msg2(LOG_INFO,"Starting to serve");
01823                                 serveconnection(client);
01824                                 exit(EXIT_SUCCESS);
01825                         }
01826                 }
01827         }
01828 }
01829 
01830 void dosockopts(int socket) {
01831 #ifndef sun
01832         int yes=1;
01833 #else
01834         char yes='1';
01835 #endif /* sun */
01836         int sock_flags;
01837 
01838         /* lose the pesky "Address already in use" error message */
01839         if (setsockopt(socket,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) {
01840                 err("setsockopt SO_REUSEADDR");
01841         }
01842         if (setsockopt(socket,SOL_SOCKET,SO_KEEPALIVE,&yes,sizeof(int)) == -1) {
01843                 err("setsockopt SO_KEEPALIVE");
01844         }
01845 
01846         /* make the listening socket non-blocking */
01847         if ((sock_flags = fcntl(socket, F_GETFL, 0)) == -1) {
01848                 err("fcntl F_GETFL");
01849         }
01850         if (fcntl(socket, F_SETFL, sock_flags | O_NONBLOCK) == -1) {
01851                 err("fcntl F_SETFL O_NONBLOCK");
01852         }
01853 }
01854 
01855 /**
01856  * Connect a server's socket.
01857  *
01858  * @param serve the server we want to connect.
01859  **/
01860 int setup_serve(SERVER *serve) {
01861         struct addrinfo hints;
01862         struct addrinfo *ai = NULL;
01863         gchar *port = NULL;
01864         int e;
01865 
01866         if(!do_oldstyle) {
01867                 return serve->servename ? 1 : 0;
01868         }
01869         memset(&hints,'\0',sizeof(hints));
01870         hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG | AI_NUMERICSERV;
01871         hints.ai_socktype = SOCK_STREAM;
01872         hints.ai_family = serve->socket_family;
01873 
01874         port = g_strdup_printf ("%d", serve->port);
01875         if (port == NULL)
01876                 return 0;
01877 
01878         e = getaddrinfo(serve->listenaddr,port,&hints,&ai);
01879 
01880         g_free(port);
01881 
01882         if(e != 0) {
01883                 fprintf(stderr, "getaddrinfo failed: %s\n", gai_strerror(e));
01884                 serve->socket = -1;
01885                 freeaddrinfo(ai);
01886                 exit(EXIT_FAILURE);
01887         }
01888 
01889         if(serve->socket_family == AF_UNSPEC)
01890                 serve->socket_family = ai->ai_family;
01891 
01892 #ifdef WITH_SDP
01893         if ((serve->flags) && F_SDP) {
01894                 if (ai->ai_family == AF_INET)
01895                         ai->ai_family = AF_INET_SDP;
01896                 else (ai->ai_family == AF_INET6)
01897                         ai->ai_family = AF_INET6_SDP;
01898         }
01899 #endif
01900         if ((serve->socket = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0)
01901                 err("socket: %m");
01902 
01903         dosockopts(serve->socket);
01904 
01905         DEBUG("Waiting for connections... bind, ");
01906         e = bind(serve->socket, ai->ai_addr, ai->ai_addrlen);
01907         if (e != 0 && errno != EADDRINUSE)
01908                 err("bind: %m");
01909         DEBUG("listen, ");
01910         if (listen(serve->socket, 1) < 0)
01911                 err("listen: %m");
01912 
01913         freeaddrinfo (ai);
01914         if(serve->servename) {
01915                 return 1;
01916         } else {
01917                 return 0;
01918         }
01919 }
01920 
01921 void open_modern(void) {
01922         struct addrinfo hints;
01923         struct addrinfo* ai = NULL;
01924         struct sock_flags;
01925         int e;
01926 
01927         memset(&hints, '\0', sizeof(hints));
01928         hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
01929         hints.ai_socktype = SOCK_STREAM;
01930         hints.ai_family = AF_UNSPEC;
01931         hints.ai_protocol = IPPROTO_TCP;
01932         e = getaddrinfo(modern_listen, NBD_DEFAULT_PORT, &hints, &ai);
01933         if(e != 0) {
01934                 fprintf(stderr, "getaddrinfo failed: %s\n", gai_strerror(e));
01935                 exit(EXIT_FAILURE);
01936         }
01937         if((modernsock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol))<0) {
01938                 err("socket: %m");
01939         }
01940 
01941         dosockopts(modernsock);
01942 
01943         if(bind(modernsock, ai->ai_addr, ai->ai_addrlen)) {
01944                 err("bind: %m");
01945         }
01946         if(listen(modernsock, 10) <0) {
01947                 err("listen: %m");
01948         }
01949 
01950         freeaddrinfo(ai);
01951 }
01952 
01953 /**
01954  * Connect our servers.
01955  **/
01956 void setup_servers(GArray* servers) {
01957         int i;
01958         struct sigaction sa;
01959         int want_modern=0;
01960 
01961         for(i=0;i<servers->len;i++) {
01962                 want_modern |= setup_serve(&(g_array_index(servers, SERVER, i)));
01963         }
01964         if(want_modern) {
01965                 open_modern();
01966         }
01967         children=g_hash_table_new_full(g_int_hash, g_int_equal, NULL, destroy_pid_t);
01968 
01969         sa.sa_handler = sigchld_handler;
01970         sigemptyset(&sa.sa_mask);
01971         sa.sa_flags = SA_RESTART;
01972         if(sigaction(SIGCHLD, &sa, NULL) == -1)
01973                 err("sigaction: %m");
01974         sa.sa_handler = sigterm_handler;
01975         sigemptyset(&sa.sa_mask);
01976         sa.sa_flags = SA_RESTART;
01977         if(sigaction(SIGTERM, &sa, NULL) == -1)
01978                 err("sigaction: %m");
01979 }
01980 
01981 /**
01982  * Go daemon (unless we specified at compile time that we didn't want this)
01983  * @param serve the first server of our configuration. If its port is zero,
01984  *      then do not daemonize, because we're doing inetd then. This parameter
01985  *      is only used to create a PID file of the form
01986  *      /var/run/nbd-server.&lt;port&gt;.pid; it's not modified in any way.
01987  **/
01988 #if !defined(NODAEMON) && !defined(NOFORK)
01989 void daemonize(SERVER* serve) {
01990         FILE*pidf;
01991 
01992         if(serve && !(serve->port)) {
01993                 return;
01994         }
01995         if(daemon(0,0)<0) {
01996                 err("daemon");
01997         }
01998         if(!*pidftemplate) {
01999                 if(serve) {
02000                         strncpy(pidftemplate, "/var/run/nbd-server.%d.pid", 255);
02001                 } else {
02002                         strncpy(pidftemplate, "/var/run/nbd-server.pid", 255);
02003                 }
02004         }
02005         snprintf(pidfname, 255, pidftemplate, serve ? serve->port : 0);
02006         pidf=fopen(pidfname, "w");
02007         if(pidf) {
02008                 fprintf(pidf,"%d\n", (int)getpid());
02009                 fclose(pidf);
02010         } else {
02011                 perror("fopen");
02012                 fprintf(stderr, "Not fatal; continuing");
02013         }
02014 }
02015 #else
02016 #define daemonize(serve)
02017 #endif /* !defined(NODAEMON) && !defined(NOFORK) */
02018 
02019 /*
02020  * Everything beyond this point (in the file) is run in non-daemon mode.
02021  * The stuff above daemonize() isn't.
02022  */
02023 
02024 void serve_err(SERVER* serve, const char* msg) G_GNUC_NORETURN;
02025 
02026 void serve_err(SERVER* serve, const char* msg) {
02027         g_message("Export of %s on port %d failed:", serve->exportname,
02028                         serve->port);
02029         err(msg);
02030 }
02031 
02032 /**
02033  * Set up user-ID and/or group-ID
02034  **/
02035 void dousers(void) {
02036         struct passwd *pw;
02037         struct group *gr;
02038         gchar* str;
02039         if(rungroup) {
02040                 gr=getgrnam(rungroup);
02041                 if(!gr) {
02042                         str = g_strdup_printf("Invalid group name: %s", rungroup);
02043                         err(str);
02044                 }
02045                 if(setgid(gr->gr_gid)<0) {
02046                         err("Could not set GID: %m"); 
02047                 }
02048         }
02049         if(runuser) {
02050                 pw=getpwnam(runuser);
02051                 if(!pw) {
02052                         str = g_strdup_printf("Invalid user name: %s", runuser);
02053                         err(str);
02054                 }
02055                 if(setuid(pw->pw_uid)<0) {
02056                         err("Could not set UID: %m");
02057                 }
02058         }
02059 }
02060 
02061 #ifndef ISSERVER
02062 void glib_message_syslog_redirect(const gchar *log_domain,
02063                                   GLogLevelFlags log_level,
02064                                   const gchar *message,
02065                                   gpointer user_data)
02066 {
02067     int level=LOG_DEBUG;
02068     
02069     switch( log_level )
02070     {
02071       case G_LOG_FLAG_FATAL:
02072       case G_LOG_LEVEL_CRITICAL:
02073       case G_LOG_LEVEL_ERROR:    
02074         level=LOG_ERR; 
02075         break;
02076       case G_LOG_LEVEL_WARNING:
02077         level=LOG_WARNING;
02078         break;
02079       case G_LOG_LEVEL_MESSAGE:
02080       case G_LOG_LEVEL_INFO:
02081         level=LOG_INFO;
02082         break;
02083       case G_LOG_LEVEL_DEBUG:
02084         level=LOG_DEBUG;
02085       default:
02086         level=LOG_ERR;
02087     }
02088     syslog(level, "%s", message);
02089 }
02090 #endif
02091 
02092 /**
02093  * Main entry point...
02094  **/
02095 int main(int argc, char *argv[]) {
02096         SERVER *serve;
02097         GArray *servers;
02098         GError *err=NULL;
02099 
02100         if (sizeof( struct nbd_request )!=28) {
02101                 fprintf(stderr,"Bad size of structure. Alignment problems?\n");
02102                 exit(EXIT_FAILURE) ;
02103         }
02104 
02105         memset(pidftemplate, '\0', 256);
02106 
02107         logging();
02108         config_file_pos = g_strdup(CFILE);
02109         serve=cmdline(argc, argv);
02110         servers = parse_cfile(config_file_pos, &err);
02111         
02112         if(serve) {
02113                 serve->socket_family = AF_UNSPEC;
02114 
02115                 append_serve(serve, servers);
02116      
02117                 if (!(serve->port)) {
02118                         CLIENT *client;
02119 #ifndef ISSERVER
02120                         /* You really should define ISSERVER if you're going to use
02121                          * inetd mode, but if you don't, closing stdout and stderr
02122                          * (which inetd had connected to the client socket) will let it
02123                          * work. */
02124                         close(1);
02125                         close(2);
02126                         open("/dev/null", O_WRONLY);
02127                         open("/dev/null", O_WRONLY);
02128                         g_log_set_default_handler( glib_message_syslog_redirect, NULL );
02129 #endif
02130                         client=g_malloc(sizeof(CLIENT));
02131                         client->server=serve;
02132                         client->net=0;
02133                         client->exportsize=OFFT_MAX;
02134                         set_peername(0,client);
02135                         serveconnection(client);
02136                         return 0;
02137                 }
02138         }
02139     
02140         if(!servers || !servers->len) {
02141                 if(err && !(err->domain == g_quark_from_string("parse_cfile")
02142                                 && err->code == CFILE_NOTFOUND)) {
02143                         g_warning("Could not parse config file: %s", 
02144                                         err ? err->message : "Unknown error");
02145                 }
02146         }
02147         if(serve) {
02148                 g_warning("Specifying an export on the command line is deprecated.");
02149                 g_warning("Please use a configuration file instead.");
02150         }
02151 
02152         if((!serve) && (!servers||!servers->len)) {
02153                 g_message("No configured exports; quitting.");
02154                 exit(EXIT_FAILURE);
02155         }
02156         daemonize(serve);
02157         setup_servers(servers);
02158         dousers();
02159         serveloop(servers);
02160         return 0 ;
02161 }