Network Block Device 2.9.20
|
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 size_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, (socklen_t *)&addrinlen) < 0) 01634 err("getsockname failed: %m"); 01635 01636 getnameinfo((struct sockaddr *)&addrin, (socklen_t)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, (socklen_t) 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, (socklen_t)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.<port>.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 }