00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056 #include "lfs.h"
00057
00058 #include <sys/types.h>
00059 #include <sys/socket.h>
00060 #include <sys/stat.h>
00061 #include <sys/select.h>
00062 #include <sys/wait.h>
00063 #ifdef HAVE_SYS_IOCTL_H
00064 #include <sys/ioctl.h>
00065 #endif
00066 #include <sys/param.h>
00067 #ifdef HAVE_SYS_MOUNT_H
00068 #include <sys/mount.h>
00069 #endif
00070 #include <signal.h>
00071 #include <errno.h>
00072 #include <netinet/tcp.h>
00073 #include <netinet/in.h>
00074 #include <netdb.h>
00075 #include <syslog.h>
00076 #include <unistd.h>
00077 #include <stdio.h>
00078 #include <stdlib.h>
00079 #include <string.h>
00080 #include <fcntl.h>
00081 #include <arpa/inet.h>
00082 #include <strings.h>
00083 #include <dirent.h>
00084 #include <unistd.h>
00085 #include <getopt.h>
00086 #include <pwd.h>
00087 #include <grp.h>
00088
00089 #include <glib.h>
00090
00091
00092 #define MY_NAME "nbd_server"
00093 #include "cliserv.h"
00094
00095
00096 #ifndef SYSCONFDIR
00097 #define SYSCONFDIR "/etc"
00098 #endif
00099 #define CFILE SYSCONFDIR "/nbd-server/config"
00100
00101
00102 gchar* config_file_pos;
00103
00104
00105 gchar* runuser=NULL;
00106
00107 gchar* rungroup=NULL;
00108
00109
00110 #ifdef ISSERVER
00111 #define msg2(a,b) syslog(a,b)
00112 #define msg3(a,b,c) syslog(a,b,c)
00113 #define msg4(a,b,c,d) syslog(a,b,c,d)
00114 #else
00115 #define msg2(a,b) g_message(b)
00116 #define msg3(a,b,c) g_message(b,c)
00117 #define msg4(a,b,c,d) g_message(b,c,d)
00118 #endif
00119
00120
00121
00122 #ifdef DODBG
00123 #define DEBUG( a ) printf( a )
00124 #define DEBUG2( a,b ) printf( a,b )
00125 #define DEBUG3( a,b,c ) printf( a,b,c )
00126 #define DEBUG4( a,b,c,d ) printf( a,b,c,d )
00127 #else
00128 #define DEBUG( a )
00129 #define DEBUG2( a,b )
00130 #define DEBUG3( a,b,c )
00131 #define DEBUG4( a,b,c,d )
00132 #endif
00133 #ifndef PACKAGE_VERSION
00134 #define PACKAGE_VERSION ""
00135 #endif
00136
00137
00138
00139
00140 #define OFFT_MAX ~((off_t)1<<(sizeof(off_t)*8-1))
00141 #define LINELEN 256
00142
00143 #define BUFSIZE (1024*1024)
00144 #define DIFFPAGESIZE 4096
00145 #define F_READONLY 1
00146 #define F_MULTIFILE 2
00147 #define F_COPYONWRITE 4
00148
00149 #define F_AUTOREADONLY 8
00150 #define F_SPARSE 16
00151 #define F_SDP 32
00152 GHashTable *children;
00153 char pidfname[256];
00154 char pidftemplate[256];
00155 char default_authname[] = SYSCONFDIR "/nbd-server/allow";
00156
00157
00158
00159
00160 typedef enum {
00161 VIRT_NONE=0,
00162 VIRT_IPLIT,
00163 VIRT_IPHASH,
00164
00165 VIRT_CIDR,
00166 } VIRT_STYLE;
00167
00168
00169
00170
00171 typedef struct {
00172 gchar* exportname;
00173 off_t expected_size;
00174
00175 gchar* listenaddr;
00176 unsigned int port;
00177 char* authname;
00178 int flags;
00179 unsigned int timeout;
00180
00181 int socket;
00182 VIRT_STYLE virtstyle;
00183 uint8_t cidrlen;
00184
00185 gchar* prerun;
00186
00187 gchar* postrun;
00188
00189 } SERVER;
00190
00191
00192
00193
00194 typedef struct {
00195 int fhandle;
00196 off_t startoff;
00197 } FILE_INFO;
00198
00199 typedef struct {
00200 off_t exportsize;
00201 char *clientname;
00202 char *exportname;
00203 GArray *export;
00204
00205
00206 int net;
00207 SERVER *server;
00208 char* difffilename;
00209 int difffile;
00210
00211
00212 u32 difffilelen;
00213 u32 *difmap;
00214 } CLIENT;
00215
00216
00217
00218
00219 typedef enum {
00220 PARAM_INT,
00221 PARAM_STRING,
00222 PARAM_BOOL,
00223 } PARAM_TYPE;
00224
00225
00226
00227
00228 typedef struct {
00229 gchar *paramname;
00230
00231 gboolean required;
00232
00233 PARAM_TYPE ptype;
00234 gpointer target;
00235
00236
00237
00238 gint flagval;
00239
00240 } PARAM;
00241
00242
00243
00244
00245
00246
00247
00248
00249 int authorized_client(CLIENT *opts) {
00250 const char *ERRMSG="Invalid entry '%s' in authfile '%s', so, refusing all connections.";
00251 FILE *f ;
00252 char line[LINELEN];
00253 char *tmp;
00254 struct in_addr addr;
00255 struct in_addr client;
00256 struct in_addr cltemp;
00257 int len;
00258
00259 if ((f=fopen(opts->server->authname,"r"))==NULL) {
00260 msg4(LOG_INFO,"Can't open authorization file %s (%s).",
00261 opts->server->authname,strerror(errno)) ;
00262 return 1 ;
00263 }
00264
00265 inet_aton(opts->clientname, &client);
00266 while (fgets(line,LINELEN,f)!=NULL) {
00267 if((tmp=index(line, '/'))) {
00268 if(strlen(line)<=tmp-line) {
00269 msg4(LOG_CRIT, ERRMSG, line, opts->server->authname);
00270 return 0;
00271 }
00272 *(tmp++)=0;
00273 if(inet_aton(line,&addr)) {
00274 msg4(LOG_CRIT, ERRMSG, line, opts->server->authname);
00275 return 0;
00276 }
00277 len=strtol(tmp, NULL, 0);
00278 addr.s_addr>>=32-len;
00279 addr.s_addr<<=32-len;
00280 memcpy(&cltemp,&client,sizeof(client));
00281 cltemp.s_addr>>=32-len;
00282 cltemp.s_addr<<=32-len;
00283 if(addr.s_addr == cltemp.s_addr) {
00284 return 1;
00285 }
00286 }
00287 if (strncmp(line,opts->clientname,strlen(opts->clientname))==0) {
00288 fclose(f);
00289 return 1;
00290 }
00291 }
00292 fclose(f);
00293 return 0;
00294 }
00295
00296
00297
00298
00299
00300
00301
00302
00303 inline void readit(int f, void *buf, size_t len) {
00304 ssize_t res;
00305 while (len > 0) {
00306 DEBUG("*");
00307 if ((res = read(f, buf, len)) <= 0)
00308 err("Read failed: %m");
00309 len -= res;
00310 buf += res;
00311 }
00312 }
00313
00314
00315
00316
00317
00318
00319
00320
00321 inline void writeit(int f, void *buf, size_t len) {
00322 ssize_t res;
00323 while (len > 0) {
00324 DEBUG("+");
00325 if ((res = write(f, buf, len)) <= 0)
00326 err("Send failed: %m");
00327 len -= res;
00328 buf += res;
00329 }
00330 }
00331
00332
00333
00334
00335
00336 void usage() {
00337 printf("This is nbd-server version " VERSION "\n");
00338 printf("Usage: [ip:]port file_to_export [size][kKmM] [-l authorize_file] [-r] [-m] [-c] [-a timeout_sec] [-C configuration file] [-p PID file name] [-o section name]\n"
00339 "\t-r|--read-only\t\tread only\n"
00340 "\t-m|--multi-file\t\tmultiple file\n"
00341 "\t-c|--copy-on-write\tcopy on write\n"
00342 "\t-C|--config-file\tspecify an alternate configuration file\n"
00343 "\t-l|--authorize-file\tfile with list of hosts that are allowed to\n\t\t\t\tconnect.\n"
00344 "\t-a|--idle-time\t\tmaximum idle seconds; server terminates when\n\t\t\t\tidle time exceeded\n"
00345 "\t-p|--pid-file\t\tspecify a filename to write our PID to\n"
00346 "\t-o|--output-config\toutput a config file section for what you\n\t\t\t\tspecified on the command line, with the\n\t\t\t\tspecified section name\n\n"
00347 "\tif port is set to 0, stdin is used (for running from inetd)\n"
00348 "\tif file_to_export contains '%%s', it is substituted with the IP\n"
00349 "\t\taddress of the machine trying to connect\n"
00350 "\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");
00351 printf("Using configuration file %s\n", CFILE);
00352 }
00353
00354
00355 void dump_section(SERVER* serve, gchar* section_header) {
00356 printf("[%s]\n", section_header);
00357 printf("\texportname = %s\n", serve->exportname);
00358 printf("\tlistenaddr = %s\n", serve->listenaddr);
00359 printf("\tport = %d\n", serve->port);
00360 if(serve->flags & F_READONLY) {
00361 printf("\treadonly = true\n");
00362 }
00363 if(serve->flags & F_MULTIFILE) {
00364 printf("\tmultifile = true\n");
00365 }
00366 if(serve->flags & F_COPYONWRITE) {
00367 printf("\tcopyonwrite = true\n");
00368 }
00369 if(serve->expected_size) {
00370 printf("\tfilesize = %Ld\n", (long long int)serve->expected_size);
00371 }
00372 if(serve->authname) {
00373 printf("\tauthfile = %s\n", serve->authname);
00374 }
00375 if(serve->timeout) {
00376 printf("\ttimeout = %d\n", serve->timeout);
00377 }
00378 exit(EXIT_SUCCESS);
00379 }
00380
00381
00382
00383
00384
00385
00386
00387 SERVER* cmdline(int argc, char *argv[]) {
00388 int i=0;
00389 int nonspecial=0;
00390 int c;
00391 struct option long_options[] = {
00392 {"read-only", no_argument, NULL, 'r'},
00393 {"multi-file", no_argument, NULL, 'm'},
00394 {"copy-on-write", no_argument, NULL, 'c'},
00395 {"authorize-file", required_argument, NULL, 'l'},
00396 {"idle-time", required_argument, NULL, 'a'},
00397 {"config-file", required_argument, NULL, 'C'},
00398 {"pid-file", required_argument, NULL, 'p'},
00399 {"output-config", required_argument, NULL, 'o'},
00400 {0,0,0,0}
00401 };
00402 SERVER *serve;
00403 off_t es;
00404 size_t last;
00405 char suffix;
00406 gboolean do_output=FALSE;
00407 gchar* section_header="";
00408 gchar** addr_port;
00409
00410 if(argc==1) {
00411 return NULL;
00412 }
00413 serve=g_new0(SERVER, 1);
00414 serve->authname = g_strdup(default_authname);
00415 serve->virtstyle=VIRT_IPLIT;
00416 while((c=getopt_long(argc, argv, "-a:C:cl:mo:rp:", long_options, &i))>=0) {
00417 switch (c) {
00418 case 1:
00419
00420 switch(nonspecial++) {
00421 case 0:
00422 addr_port=g_strsplit(optarg, ":", 2);
00423 if(addr_port[1]) {
00424 serve->port=strtol(addr_port[1], NULL, 0);
00425 serve->listenaddr=g_strdup(addr_port[0]);
00426 } else {
00427 serve->listenaddr=g_strdup("0.0.0.0");
00428 serve->port=strtol(addr_port[0], NULL, 0);
00429 }
00430 g_strfreev(addr_port);
00431 break;
00432 case 1:
00433 serve->exportname = g_strdup(optarg);
00434 if(serve->exportname[0] != '/') {
00435 fprintf(stderr, "E: The to be exported file needs to be an absolute filename!\n");
00436 exit(EXIT_FAILURE);
00437 }
00438 break;
00439 case 2:
00440 last=strlen(optarg)-1;
00441 suffix=optarg[last];
00442 if (suffix == 'k' || suffix == 'K' ||
00443 suffix == 'm' || suffix == 'M')
00444 optarg[last] = '\0';
00445 es = (off_t)atol(optarg);
00446 switch (suffix) {
00447 case 'm':
00448 case 'M': es <<= 10;
00449 case 'k':
00450 case 'K': es <<= 10;
00451 default : break;
00452 }
00453 serve->expected_size = es;
00454 break;
00455 }
00456 break;
00457 case 'r':
00458 serve->flags |= F_READONLY;
00459 break;
00460 case 'm':
00461 serve->flags |= F_MULTIFILE;
00462 break;
00463 case 'o':
00464 do_output = TRUE;
00465 section_header = g_strdup(optarg);
00466 break;
00467 case 'p':
00468 strncpy(pidftemplate, optarg, 256);
00469 break;
00470 case 'c':
00471 serve->flags |=F_COPYONWRITE;
00472 break;
00473 case 'C':
00474 g_free(config_file_pos);
00475 config_file_pos=g_strdup(optarg);
00476 break;
00477 case 'l':
00478 g_free(serve->authname);
00479 serve->authname=g_strdup(optarg);
00480 break;
00481 case 'a':
00482 serve->timeout=strtol(optarg, NULL, 0);
00483 break;
00484 default:
00485 usage();
00486 exit(EXIT_FAILURE);
00487 break;
00488 }
00489 }
00490
00491
00492 if(nonspecial<2) {
00493 g_free(serve);
00494 serve=NULL;
00495 }
00496 if(do_output) {
00497 if(!serve) {
00498 g_critical("Need a complete configuration on the command line to output a config file section!");
00499 exit(EXIT_FAILURE);
00500 }
00501 dump_section(serve, section_header);
00502 }
00503 return serve;
00504 }
00505
00506
00507
00508
00509 typedef enum {
00510 CFILE_NOTFOUND,
00511 CFILE_MISSING_GENERIC,
00512 CFILE_KEY_MISSING,
00513 CFILE_VALUE_INVALID,
00514 CFILE_VALUE_UNSUPPORTED,
00515 CFILE_PROGERR
00516 } CFILE_ERRORS;
00517
00518
00519
00520
00521 void remove_server(gpointer s) {
00522 SERVER *server;
00523
00524 server=(SERVER*)s;
00525 g_free(server->exportname);
00526 if(server->authname)
00527 g_free(server->authname);
00528 g_free(server);
00529 }
00530
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541 GArray* parse_cfile(gchar* f, GError** e) {
00542 const char* DEFAULT_ERROR = "Could not parse %s in group %s: %s";
00543 const char* MISSING_REQUIRED_ERROR = "Could not find required value %s in group %s: %s";
00544 SERVER s;
00545 gchar *virtstyle=NULL;
00546 PARAM lp[] = {
00547 { "exportname", TRUE, PARAM_STRING, NULL, 0 },
00548 { "port", TRUE, PARAM_INT, NULL, 0 },
00549 { "authfile", FALSE, PARAM_STRING, NULL, 0 },
00550 { "timeout", FALSE, PARAM_INT, NULL, 0 },
00551 { "filesize", FALSE, PARAM_INT, NULL, 0 },
00552 { "virtstyle", FALSE, PARAM_STRING, NULL, 0 },
00553 { "prerun", FALSE, PARAM_STRING, NULL, 0 },
00554 { "postrun", FALSE, PARAM_STRING, NULL, 0 },
00555 { "readonly", FALSE, PARAM_BOOL, NULL, F_READONLY },
00556 { "multifile", FALSE, PARAM_BOOL, NULL, F_MULTIFILE },
00557 { "copyonwrite", FALSE, PARAM_BOOL, NULL, F_COPYONWRITE },
00558 { "sparse_cow", FALSE, PARAM_BOOL, NULL, F_SPARSE },
00559 { "sdp", FALSE, PARAM_BOOL, NULL, F_SDP },
00560 { "listenaddr", FALSE, PARAM_STRING, NULL, 0 },
00561 };
00562 const int lp_size=sizeof(lp)/sizeof(PARAM);
00563 PARAM gp[] = {
00564 { "user", FALSE, PARAM_STRING, &runuser, 0 },
00565 { "group", FALSE, PARAM_STRING, &rungroup, 0 },
00566 };
00567 PARAM* p=gp;
00568 int p_size=sizeof(gp)/sizeof(PARAM);
00569 GKeyFile *cfile;
00570 GError *err = NULL;
00571 const char *err_msg=NULL;
00572 GQuark errdomain;
00573 GArray *retval=NULL;
00574 gchar **groups;
00575 gboolean value;
00576 gint i;
00577 gint j;
00578
00579 errdomain = g_quark_from_string("parse_cfile");
00580 cfile = g_key_file_new();
00581 retval = g_array_new(FALSE, TRUE, sizeof(SERVER));
00582 if(!g_key_file_load_from_file(cfile, f, G_KEY_FILE_KEEP_COMMENTS |
00583 G_KEY_FILE_KEEP_TRANSLATIONS, &err)) {
00584 g_set_error(e, errdomain, CFILE_NOTFOUND, "Could not open config file.");
00585 g_key_file_free(cfile);
00586 return retval;
00587 }
00588 if(strcmp(g_key_file_get_start_group(cfile), "generic")) {
00589 g_set_error(e, errdomain, CFILE_MISSING_GENERIC, "Config file does not contain the [generic] group!");
00590 g_key_file_free(cfile);
00591 return NULL;
00592 }
00593 groups = g_key_file_get_groups(cfile, NULL);
00594 for(i=0;groups[i];i++) {
00595 memset(&s, '\0', sizeof(SERVER));
00596 lp[0].target=&(s.exportname);
00597 lp[1].target=&(s.port);
00598 lp[2].target=&(s.authname);
00599 lp[3].target=&(s.timeout);
00600 lp[4].target=&(s.expected_size);
00601 lp[5].target=&(virtstyle);
00602 lp[6].target=&(s.prerun);
00603 lp[7].target=&(s.postrun);
00604 lp[8].target=lp[9].target=lp[10].target=
00605 lp[11].target=lp[12].target=&(s.flags);
00606 lp[13].target=&(s.listenaddr);
00607
00608
00609 if(i==1) {
00610 p=lp;
00611 p_size=lp_size;
00612 }
00613 for(j=0;j<p_size;j++) {
00614 g_assert(p[j].target != NULL);
00615 g_assert(p[j].ptype==PARAM_INT||p[j].ptype==PARAM_STRING||p[j].ptype==PARAM_BOOL);
00616 switch(p[j].ptype) {
00617 case PARAM_INT:
00618 *((gint*)p[j].target) =
00619 g_key_file_get_integer(cfile,
00620 groups[i],
00621 p[j].paramname,
00622 &err);
00623 break;
00624 case PARAM_STRING:
00625 *((gchar**)p[j].target) =
00626 g_key_file_get_string(cfile,
00627 groups[i],
00628 p[j].paramname,
00629 &err);
00630 break;
00631 case PARAM_BOOL:
00632 value = g_key_file_get_boolean(cfile,
00633 groups[i],
00634 p[j].paramname, &err);
00635 if(!err) {
00636 if(value) {
00637 *((gint*)p[j].target) |= p[j].flagval;
00638 } else {
00639 *((gint*)p[j].target) &= ~(p[j].flagval);
00640 }
00641 }
00642 break;
00643 }
00644 if(err) {
00645 if(err->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND) {
00646 if(!p[j].required) {
00647
00648 g_clear_error(&err);
00649 continue;
00650 } else {
00651 err_msg = MISSING_REQUIRED_ERROR;
00652 }
00653 } else {
00654 err_msg = DEFAULT_ERROR;
00655 }
00656 g_set_error(e, errdomain, CFILE_VALUE_INVALID, err_msg, p[j].paramname, groups[i], err->message);
00657 g_array_free(retval, TRUE);
00658 g_error_free(err);
00659 g_key_file_free(cfile);
00660 return NULL;
00661 }
00662 }
00663 if(virtstyle) {
00664 if(!strncmp(virtstyle, "none", 4)) {
00665 s.virtstyle=VIRT_NONE;
00666 } else if(!strncmp(virtstyle, "ipliteral", 9)) {
00667 s.virtstyle=VIRT_IPLIT;
00668 } else if(!strncmp(virtstyle, "iphash", 6)) {
00669 s.virtstyle=VIRT_IPHASH;
00670 } else if(!strncmp(virtstyle, "cidrhash", 8)) {
00671 s.virtstyle=VIRT_CIDR;
00672 if(strlen(virtstyle)<10) {
00673 g_set_error(e, errdomain, CFILE_VALUE_INVALID, "Invalid value %s for parameter virtstyle in group %s: missing length", virtstyle, groups[i]);
00674 g_array_free(retval, TRUE);
00675 g_key_file_free(cfile);
00676 return NULL;
00677 }
00678 s.cidrlen=strtol(virtstyle+8, NULL, 0);
00679 } else {
00680 g_set_error(e, errdomain, CFILE_VALUE_INVALID, "Invalid value %s for parameter virtstyle in group %s", virtstyle, groups[i]);
00681 g_array_free(retval, TRUE);
00682 g_key_file_free(cfile);
00683 return NULL;
00684 }
00685 } else {
00686 s.virtstyle=VIRT_IPLIT;
00687 }
00688
00689 virtstyle=NULL;
00690
00691 if(i>0) {
00692 if(!s.listenaddr) {
00693 s.listenaddr = g_strdup("0.0.0.0");
00694 }
00695 g_array_append_val(retval, s);
00696 }
00697 #ifndef WITH_SDP
00698 if(s.flags & F_SDP) {
00699 g_set_error(e, errdomain, CFILE_VALUE_UNSUPPORTED, "This nbd-server was built without support for SDP, yet group %s uses it", groups[i]);
00700 g_array_free(retval, TRUE);
00701 g_key_file_free(cfile);
00702 return NULL;
00703 }
00704 #endif
00705 }
00706 return retval;
00707 }
00708
00709
00710
00711
00712
00713
00714 void sigchld_handler(int s) {
00715 int status;
00716 int* i;
00717 pid_t pid;
00718
00719 while((pid=waitpid(-1, &status, WNOHANG)) > 0) {
00720 if(WIFEXITED(status)) {
00721 msg3(LOG_INFO, "Child exited with %d", WEXITSTATUS(status));
00722 }
00723 i=g_hash_table_lookup(children, &pid);
00724 if(!i) {
00725 msg3(LOG_INFO, "SIGCHLD received for an unknown child with PID %ld", (long)pid);
00726 } else {
00727 DEBUG2("Removing %d from the list of children", pid);
00728 g_hash_table_remove(children, &pid);
00729 }
00730 }
00731 }
00732
00733
00734
00735
00736
00737
00738
00739
00740
00741 void killchild(gpointer key, gpointer value, gpointer user_data) {
00742 pid_t *pid=value;
00743 int *parent=user_data;
00744
00745 kill(*pid, SIGTERM);
00746 *parent=1;
00747 }
00748
00749
00750
00751
00752
00753
00754 void sigterm_handler(int s) {
00755 int parent=0;
00756
00757 g_hash_table_foreach(children, killchild, &parent);
00758
00759 if(parent) {
00760 unlink(pidfname);
00761 }
00762
00763 exit(EXIT_SUCCESS);
00764 }
00765
00766
00767
00768
00769
00770
00771
00772
00773 off_t size_autodetect(int fhandle) {
00774 off_t es;
00775 unsigned long sectors;
00776 struct stat stat_buf;
00777 int error;
00778
00779 #ifdef HAVE_SYS_MOUNT_H
00780 #ifdef HAVE_SYS_IOCTL_H
00781 #ifdef BLKGETSIZE
00782 DEBUG("looking for export size with ioctl BLKGETSIZE\n");
00783 if (!ioctl(fhandle, BLKGETSIZE, §ors) && sectors) {
00784 es = (off_t)sectors * (off_t)512;
00785 return es;
00786 }
00787 #endif
00788 #endif
00789 #endif
00790
00791 DEBUG("looking for fhandle size with fstat\n");
00792 stat_buf.st_size = 0;
00793 error = fstat(fhandle, &stat_buf);
00794 if (!error) {
00795 if(stat_buf.st_size > 0)
00796 return (off_t)stat_buf.st_size;
00797 } else {
00798 err("fstat failed: %m");
00799 }
00800
00801 DEBUG("looking for fhandle size with lseek SEEK_END\n");
00802 es = lseek(fhandle, (off_t)0, SEEK_END);
00803 if (es > ((off_t)0)) {
00804 return es;
00805 } else {
00806 DEBUG2("lseek failed: %d", errno==EBADF?1:(errno==ESPIPE?2:(errno==EINVAL?3:4)));
00807 }
00808
00809 err("Could not find size of exported block device: %m");
00810 return OFFT_MAX;
00811 }
00812
00813
00814
00815
00816
00817
00818
00819
00820
00821
00822
00823
00824 int get_filepos(GArray* export, off_t a, int* fhandle, off_t* foffset, size_t* maxbytes ) {
00825
00826 if(a < 0)
00827 return -1;
00828
00829
00830 FILE_INFO fi;
00831 int start = 0;
00832 int end = export->len - 1;
00833 while( start <= end ) {
00834 int mid = (start + end) / 2;
00835 fi = g_array_index(export, FILE_INFO, mid);
00836 if( fi.startoff < a ) {
00837 start = mid + 1;
00838 } else if( fi.startoff > a ) {
00839 end = mid - 1;
00840 } else {
00841 start = end = mid;
00842 break;
00843 }
00844 }
00845
00846
00847 g_assert(end >= 0);
00848
00849 fi = g_array_index(export, FILE_INFO, end);
00850 *fhandle = fi.fhandle;
00851 *foffset = a - fi.startoff;
00852 *maxbytes = 0;
00853 if( end+1 < export->len ) {
00854 FILE_INFO fi_next = g_array_index(export, FILE_INFO, end+1);
00855 *maxbytes = fi_next.startoff - a;
00856 }
00857
00858 return 0;
00859 }
00860
00861
00862
00863
00864
00865
00866
00867
00868
00869 void myseek(int handle,off_t a) {
00870 if (lseek(handle, a, SEEK_SET) < 0) {
00871 err("Can not seek locally!\n");
00872 }
00873 }
00874
00875
00876
00877
00878
00879
00880
00881
00882
00883
00884
00885 ssize_t rawexpwrite(off_t a, char *buf, size_t len, CLIENT *client) {
00886 int fhandle;
00887 off_t foffset;
00888 size_t maxbytes;
00889
00890 if(get_filepos(client->export, a, &fhandle, &foffset, &maxbytes))
00891 return -1;
00892 if(maxbytes && len > maxbytes)
00893 len = maxbytes;
00894
00895 DEBUG4("(WRITE to fd %d offset %Lu len %u), ", fhandle, foffset, len);
00896
00897 myseek(fhandle, foffset);
00898 return write(fhandle, buf, len);
00899 }
00900
00901
00902
00903
00904
00905 int rawexpwrite_fully(off_t a, char *buf, size_t len, CLIENT *client) {
00906 ssize_t ret=0;
00907
00908 while(len > 0 && (ret=rawexpwrite(a, buf, len, client)) > 0 ) {
00909 a += ret;
00910 buf += ret;
00911 len -= ret;
00912 }
00913 return (ret < 0 || len != 0);
00914 }
00915
00916
00917
00918
00919
00920
00921
00922
00923
00924
00925
00926
00927 ssize_t rawexpread(off_t a, char *buf, size_t len, CLIENT *client) {
00928 int fhandle;
00929 off_t foffset;
00930 size_t maxbytes;
00931
00932 if(get_filepos(client->export, a, &fhandle, &foffset, &maxbytes))
00933 return -1;
00934 if(maxbytes && len > maxbytes)
00935 len = maxbytes;
00936
00937 DEBUG4("(READ from fd %d offset %Lu len %u), ", fhandle, foffset, len);
00938
00939 myseek(fhandle, foffset);
00940 return read(fhandle, buf, len);
00941 }
00942
00943
00944
00945
00946
00947 int rawexpread_fully(off_t a, char *buf, size_t len, CLIENT *client) {
00948 ssize_t ret=0;
00949
00950 while(len > 0 && (ret=rawexpread(a, buf, len, client)) > 0 ) {
00951 a += ret;
00952 buf += ret;
00953 len -= ret;
00954 }
00955 return (ret < 0 || len != 0);
00956 }
00957
00958
00959
00960
00961
00962
00963
00964
00965
00966
00967
00968 int expread(off_t a, char *buf, size_t len, CLIENT *client) {
00969 off_t rdlen, offset;
00970 off_t mapcnt, mapl, maph, pagestart;
00971
00972 if (!(client->server->flags & F_COPYONWRITE))
00973 return(rawexpread_fully(a, buf, len, client));
00974 DEBUG3("Asked to read %d bytes at %Lu.\n", len, (unsigned long long)a);
00975
00976 mapl=a/DIFFPAGESIZE; maph=(a+len-1)/DIFFPAGESIZE;
00977
00978 for (mapcnt=mapl;mapcnt<=maph;mapcnt++) {
00979 pagestart=mapcnt*DIFFPAGESIZE;
00980 offset=a-pagestart;
00981 rdlen=(0<DIFFPAGESIZE-offset && len<(size_t)(DIFFPAGESIZE-offset)) ?
00982 len : (size_t)DIFFPAGESIZE-offset;
00983 if (client->difmap[mapcnt]!=(u32)(-1)) {
00984 DEBUG3("Page %Lu is at %lu\n", (unsigned long long)mapcnt,
00985 (unsigned long)(client->difmap[mapcnt]));
00986 myseek(client->difffile, client->difmap[mapcnt]*DIFFPAGESIZE+offset);
00987 if (read(client->difffile, buf, rdlen) != rdlen) return -1;
00988 } else {
00989 DEBUG2("Page %Lu is not here, we read the original one\n",
00990 (unsigned long long)mapcnt);
00991 if(rawexpread_fully(a, buf, rdlen, client)) return -1;
00992 }
00993 len-=rdlen; a+=rdlen; buf+=rdlen;
00994 }
00995 return 0;
00996 }
00997
00998
00999
01000
01001
01002
01003
01004
01005
01006
01007
01008
01009 int expwrite(off_t a, char *buf, size_t len, CLIENT *client) {
01010 char pagebuf[DIFFPAGESIZE];
01011 off_t mapcnt,mapl,maph;
01012 off_t wrlen,rdlen;
01013 off_t pagestart;
01014 off_t offset;
01015
01016 if (!(client->server->flags & F_COPYONWRITE))
01017 return(rawexpwrite_fully(a, buf, len, client));
01018 DEBUG3("Asked to write %d bytes at %Lu.\n", len, (unsigned long long)a);
01019
01020 mapl=a/DIFFPAGESIZE ; maph=(a+len-1)/DIFFPAGESIZE ;
01021
01022 for (mapcnt=mapl;mapcnt<=maph;mapcnt++) {
01023 pagestart=mapcnt*DIFFPAGESIZE ;
01024 offset=a-pagestart ;
01025 wrlen=(0<DIFFPAGESIZE-offset && len<(size_t)(DIFFPAGESIZE-offset)) ?
01026 len : (size_t)DIFFPAGESIZE-offset;
01027
01028 if (client->difmap[mapcnt]!=(u32)(-1)) {
01029 DEBUG3("Page %Lu is at %lu\n", (unsigned long long)mapcnt,
01030 (unsigned long)(client->difmap[mapcnt])) ;
01031 myseek(client->difffile,
01032 client->difmap[mapcnt]*DIFFPAGESIZE+offset);
01033 if (write(client->difffile, buf, wrlen) != wrlen) return -1 ;
01034 } else {
01035 myseek(client->difffile,client->difffilelen*DIFFPAGESIZE) ;
01036 client->difmap[mapcnt]=(client->server->flags&F_SPARSE)?mapcnt:client->difffilelen++;
01037 DEBUG3("Page %Lu is not here, we put it at %lu\n",
01038 (unsigned long long)mapcnt,
01039 (unsigned long)(client->difmap[mapcnt]));
01040 rdlen=DIFFPAGESIZE ;
01041 if (rawexpread_fully(pagestart, pagebuf, rdlen, client))
01042 return -1;
01043 memcpy(pagebuf+offset,buf,wrlen) ;
01044 if (write(client->difffile, pagebuf, DIFFPAGESIZE) !=
01045 DIFFPAGESIZE)
01046 return -1;
01047 }
01048 len-=wrlen ; a+=wrlen ; buf+=wrlen ;
01049 }
01050 return 0;
01051 }
01052
01053
01054
01055
01056
01057
01058 void negotiate(CLIENT *client) {
01059 char zeros[128];
01060 u64 size_host;
01061 u32 flags = NBD_FLAG_HAS_FLAGS;
01062
01063 memset(zeros, '\0', sizeof(zeros));
01064 if (write(client->net, INIT_PASSWD, 8) < 0)
01065 err("Negotiation failed: %m");
01066 cliserv_magic = htonll(cliserv_magic);
01067 if (write(client->net, &cliserv_magic, sizeof(cliserv_magic)) < 0)
01068 err("Negotiation failed: %m");
01069 size_host = htonll((u64)(client->exportsize));
01070 if (write(client->net, &size_host, 8) < 0)
01071 err("Negotiation failed: %m");
01072 if (client->server->flags & F_READONLY)
01073 flags |= NBD_FLAG_READ_ONLY;
01074 flags = htonl(flags);
01075 if (write(client->net, &flags, 4) < 0)
01076 err("Negotiation failed: %m");
01077 if (write(client->net, zeros, 124) < 0)
01078 err("Negotiation failed: %m");
01079 }
01080
01081
01082 #define SEND(net,reply) writeit( net, &reply, sizeof( reply ));
01083
01084 #define ERROR(client,reply,errcode) { reply.error = htonl(errcode); SEND(client->net,reply); reply.error = 0; }
01085
01086
01087
01088
01089
01090
01091
01092
01093
01094 int mainloop(CLIENT *client) {
01095 struct nbd_request request;
01096 struct nbd_reply reply;
01097 gboolean go_on=TRUE;
01098 #ifdef DODBG
01099 int i = 0;
01100 #endif
01101 negotiate(client);
01102 DEBUG("Entering request loop!\n");
01103 reply.magic = htonl(NBD_REPLY_MAGIC);
01104 reply.error = 0;
01105 while (go_on) {
01106 char buf[BUFSIZE];
01107 size_t len;
01108 #ifdef DODBG
01109 i++;
01110 printf("%d: ", i);
01111 #endif
01112 if (client->server->timeout)
01113 alarm(client->server->timeout);
01114 readit(client->net, &request, sizeof(request));
01115 request.from = ntohll(request.from);
01116 request.type = ntohl(request.type);
01117
01118 if (request.type==NBD_CMD_DISC) {
01119 msg2(LOG_INFO, "Disconnect request received.");
01120 if (client->server->flags & F_COPYONWRITE) {
01121 if (client->difmap) g_free(client->difmap) ;
01122 close(client->difffile);
01123 unlink(client->difffilename);
01124 free(client->difffilename);
01125 }
01126 go_on=FALSE;
01127 continue;
01128 }
01129
01130 len = ntohl(request.len);
01131
01132 if (request.magic != htonl(NBD_REQUEST_MAGIC))
01133 err("Not enough magic.");
01134 if (len > BUFSIZE + sizeof(struct nbd_reply))
01135 err("Request too big!");
01136 #ifdef DODBG
01137 printf("%s from %Lu (%Lu) len %d, ", request.type ? "WRITE" :
01138 "READ", (unsigned long long)request.from,
01139 (unsigned long long)request.from / 512, len);
01140 #endif
01141 memcpy(reply.handle, request.handle, sizeof(reply.handle));
01142 if ((request.from + len) > (OFFT_MAX)) {
01143 DEBUG("[Number too large!]");
01144 ERROR(client, reply, EINVAL);
01145 continue;
01146 }
01147
01148 if (((ssize_t)((off_t)request.from + len) > client->exportsize)) {
01149 DEBUG("[RANGE!]");
01150 ERROR(client, reply, EINVAL);
01151 continue;
01152 }
01153
01154 if (request.type==NBD_CMD_WRITE) {
01155 DEBUG("wr: net->buf, ");
01156 readit(client->net, buf, len);
01157 DEBUG("buf->exp, ");
01158 if ((client->server->flags & F_READONLY) ||
01159 (client->server->flags & F_AUTOREADONLY)) {
01160 DEBUG("[WRITE to READONLY!]");
01161 ERROR(client, reply, EPERM);
01162 continue;
01163 }
01164 if (expwrite(request.from, buf, len, client)) {
01165 DEBUG("Write failed: %m" );
01166 ERROR(client, reply, errno);
01167 continue;
01168 }
01169 SEND(client->net, reply);
01170 DEBUG("OK!\n");
01171 continue;
01172 }
01173
01174
01175 DEBUG("exp->buf, ");
01176 if (expread(request.from, buf + sizeof(struct nbd_reply), len, client)) {
01177 DEBUG("Read failed: %m");
01178 ERROR(client, reply, errno);
01179 continue;
01180 }
01181
01182 DEBUG("buf->net, ");
01183 memcpy(buf, &reply, sizeof(struct nbd_reply));
01184 writeit(client->net, buf, len + sizeof(struct nbd_reply));
01185 DEBUG("OK!\n");
01186 }
01187 return 0;
01188 }
01189
01190
01191
01192
01193
01194
01195 void setupexport(CLIENT* client) {
01196 int i;
01197 off_t laststartoff = 0, lastsize = 0;
01198 int multifile = (client->server->flags & F_MULTIFILE);
01199
01200 client->export = g_array_new(TRUE, TRUE, sizeof(FILE_INFO));
01201
01202
01203
01204
01205 for(i=0; ; i++) {
01206 FILE_INFO fi;
01207 gchar *tmpname;
01208 mode_t mode = (client->server->flags & F_READONLY) ? O_RDONLY : O_RDWR;
01209
01210 if(multifile) {
01211 tmpname=g_strdup_printf("%s.%d", client->exportname, i);
01212 } else {
01213 tmpname=g_strdup(client->exportname);
01214 }
01215 DEBUG2( "Opening %s\n", tmpname );
01216 fi.fhandle = open(tmpname, mode);
01217 if(fi.fhandle == -1 && mode == O_RDWR) {
01218
01219 fi.fhandle = open(tmpname, O_RDONLY);
01220 if(fi.fhandle != -1) {
01221
01222
01223 if(!(client->server->flags & F_COPYONWRITE)) {
01224 client->server->flags |= F_AUTOREADONLY;
01225 client->server->flags |= F_READONLY;
01226 }
01227 }
01228 }
01229 if(fi.fhandle == -1) {
01230 if(multifile && i>0)
01231 break;
01232 err("Could not open exported file: %m");
01233 }
01234 fi.startoff = laststartoff + lastsize;
01235 g_array_append_val(client->export, fi);
01236 g_free(tmpname);
01237
01238
01239
01240 laststartoff = fi.startoff;
01241 lastsize = size_autodetect(fi.fhandle);
01242
01243 if(!multifile)
01244 break;
01245 }
01246
01247
01248 client->exportsize = laststartoff + lastsize;
01249
01250
01251 if(client->server->expected_size) {
01252
01253 if(client->server->expected_size > client->exportsize) {
01254 err("Size of exported file is too big\n");
01255 }
01256
01257 client->exportsize = client->server->expected_size;
01258 }
01259
01260 msg3(LOG_INFO, "Size of exported file/device is %Lu", (unsigned long long)client->exportsize);
01261 if(multifile) {
01262 msg3(LOG_INFO, "Total number of files: %d", i);
01263 }
01264 }
01265
01266 int copyonwrite_prepare(CLIENT* client) {
01267 off_t i;
01268 if ((client->difffilename = malloc(1024))==NULL)
01269 err("Failed to allocate string for diff file name");
01270 snprintf(client->difffilename, 1024, "%s-%s-%d.diff",client->exportname,client->clientname,
01271 (int)getpid()) ;
01272 client->difffilename[1023]='\0';
01273 msg3(LOG_INFO,"About to create map and diff file %s",client->difffilename) ;
01274 client->difffile=open(client->difffilename,O_RDWR | O_CREAT | O_TRUNC,0600) ;
01275 if (client->difffile<0) err("Could not create diff file (%m)") ;
01276 if ((client->difmap=calloc(client->exportsize/DIFFPAGESIZE,sizeof(u32)))==NULL)
01277 err("Could not allocate memory") ;
01278 for (i=0;i<client->exportsize/DIFFPAGESIZE;i++) client->difmap[i]=(u32)-1 ;
01279
01280 return 0;
01281 }
01282
01283
01284
01285
01286
01287
01288
01289
01290 int do_run(gchar* command, gchar* file) {
01291 gchar* cmd;
01292 int retval=0;
01293
01294 if(command && *command) {
01295 cmd = g_strdup_printf(command, file);
01296 retval=system(cmd);
01297 g_free(cmd);
01298 }
01299 return retval;
01300 }
01301
01302
01303
01304
01305
01306
01307
01308
01309
01310 void serveconnection(CLIENT *client) {
01311 if(do_run(client->server->prerun, client->exportname)) {
01312 exit(EXIT_FAILURE);
01313 }
01314 setupexport(client);
01315
01316 if (client->server->flags & F_COPYONWRITE) {
01317 copyonwrite_prepare(client);
01318 }
01319
01320 setmysockopt(client->net);
01321
01322 mainloop(client);
01323 do_run(client->server->postrun, client->exportname);
01324 }
01325
01326
01327
01328
01329
01330
01331
01332
01333
01334
01335
01336
01337 void set_peername(int net, CLIENT *client) {
01338 struct sockaddr_in addrin;
01339 struct sockaddr_in netaddr;
01340 socklen_t addrinlen = sizeof( addrin );
01341 char *peername;
01342 char *netname;
01343 char *tmp;
01344 int i;
01345
01346 if (getpeername(net, (struct sockaddr *) &addrin, (socklen_t *)&addrinlen) < 0)
01347 err("getsockname failed: %m");
01348 peername = g_strdup(inet_ntoa(addrin.sin_addr));
01349 switch(client->server->virtstyle) {
01350 case VIRT_NONE:
01351 client->exportname=g_strdup(client->server->exportname);
01352 break;
01353 case VIRT_IPHASH:
01354 for(i=0;i<strlen(peername);i++) {
01355 if(peername[i]=='.') {
01356 peername[i]='/';
01357 }
01358 }
01359 case VIRT_IPLIT:
01360 client->exportname=g_strdup_printf(client->server->exportname, peername);
01361 break;
01362 case VIRT_CIDR:
01363 memcpy(&netaddr, &addrin, addrinlen);
01364 netaddr.sin_addr.s_addr>>=32-(client->server->cidrlen);
01365 netaddr.sin_addr.s_addr<<=32-(client->server->cidrlen);
01366 netname = inet_ntoa(netaddr.sin_addr);
01367 tmp=g_strdup_printf("%s/%s", netname, peername);
01368 client->exportname=g_strdup_printf(client->server->exportname, tmp);
01369 break;
01370 }
01371
01372 msg4(LOG_INFO, "connect from %s, assigned file is %s",
01373 peername, client->exportname);
01374 client->clientname=g_strdup(peername);
01375 g_free(peername);
01376 }
01377
01378
01379
01380
01381
01382 void destroy_pid_t(gpointer data) {
01383 g_free(data);
01384 }
01385
01386
01387
01388
01389 int serveloop(GArray* servers) {
01390 struct sockaddr_in addrin;
01391 socklen_t addrinlen=sizeof(addrin);
01392 SERVER *serve;
01393 int i;
01394 int max;
01395 int sock;
01396 fd_set mset;
01397 fd_set rset;
01398
01399
01400
01401
01402
01403
01404
01405
01406 max=0;
01407 FD_ZERO(&mset);
01408 for(i=0;i<servers->len;i++) {
01409 sock=(g_array_index(servers, SERVER, i)).socket;
01410 FD_SET(sock, &mset);
01411 max=sock>max?sock:max;
01412 }
01413 for(;;) {
01414 CLIENT *client;
01415 int net;
01416 pid_t *pid;
01417
01418 memcpy(&rset, &mset, sizeof(fd_set));
01419 if(select(max+1, &rset, NULL, NULL, NULL)>0) {
01420 DEBUG("accept, ");
01421 for(i=0;i<servers->len;i++) {
01422 serve=&(g_array_index(servers, SERVER, i));
01423 if(FD_ISSET(serve->socket, &rset)) {
01424 if ((net=accept(serve->socket, (struct sockaddr *) &addrin, &addrinlen)) < 0)
01425 err("accept: %m");
01426
01427 client = g_malloc(sizeof(CLIENT));
01428 client->server=serve;
01429 client->exportsize=OFFT_MAX;
01430 client->net=net;
01431 set_peername(net, client);
01432 if (!authorized_client(client)) {
01433 msg2(LOG_INFO,"Unauthorized client") ;
01434 close(net);
01435 continue;
01436 }
01437 msg2(LOG_INFO,"Authorized client") ;
01438 pid=g_malloc(sizeof(pid_t));
01439 #ifndef NOFORK
01440 if ((*pid=fork())<0) {
01441 msg3(LOG_INFO,"Could not fork (%s)",strerror(errno)) ;
01442 close(net);
01443 continue;
01444 }
01445 if (*pid>0) {
01446 close(net);
01447 g_hash_table_insert(children, pid, pid);
01448 continue;
01449 }
01450
01451 g_hash_table_destroy(children);
01452 for(i=0;i<servers->len;i++) {
01453 serve=&g_array_index(servers, SERVER, i);
01454 close(serve->socket);
01455 }
01456
01457
01458
01459
01460
01461
01462 g_array_free(servers, FALSE);
01463 #endif // NOFORK
01464 msg2(LOG_INFO,"Starting to serve");
01465 serveconnection(client);
01466 exit(EXIT_SUCCESS);
01467 }
01468 }
01469 }
01470 }
01471 }
01472
01473
01474
01475
01476
01477
01478 void setup_serve(SERVER *serve) {
01479 struct sockaddr_in addrin;
01480 struct sigaction sa;
01481 int addrinlen = sizeof(addrin);
01482 int sock_flags;
01483 int af;
01484 #ifndef sun
01485 int yes=1;
01486 #else
01487 char yes='1';
01488 #endif
01489
01490 af = AF_INET;
01491 #ifdef WITH_SDP
01492 if ((serve->flags) && F_SDP) {
01493 af = AF_INET_SDP;
01494 }
01495 #endif
01496 if ((serve->socket = socket(af, SOCK_STREAM, IPPROTO_TCP)) < 0)
01497 err("socket: %m");
01498
01499
01500 if (setsockopt(serve->socket,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) {
01501 err("setsockopt SO_REUSEADDR");
01502 }
01503 if (setsockopt(serve->socket,SOL_SOCKET,SO_KEEPALIVE,&yes,sizeof(int)) == -1) {
01504 err("setsockopt SO_KEEPALIVE");
01505 }
01506
01507
01508 if ((sock_flags = fcntl(serve->socket, F_GETFL, 0)) == -1) {
01509 err("fcntl F_GETFL");
01510 }
01511 if (fcntl(serve->socket, F_SETFL, sock_flags | O_NONBLOCK) == -1) {
01512 err("fcntl F_SETFL O_NONBLOCK");
01513 }
01514
01515 DEBUG("Waiting for connections... bind, ");
01516 addrin.sin_family = AF_INET;
01517 #ifdef WITH_SDP
01518 if(serve->flags & F_SDP) {
01519 addrin.sin_family = AF_INET_SDP;
01520 }
01521 #endif
01522 addrin.sin_port = htons(serve->port);
01523 if(!inet_aton(serve->listenaddr, &(addrin.sin_addr)))
01524 err("could not parse listen address");
01525 if (bind(serve->socket, (struct sockaddr *) &addrin, addrinlen) < 0)
01526 err("bind: %m");
01527 DEBUG("listen, ");
01528 if (listen(serve->socket, 1) < 0)
01529 err("listen: %m");
01530 sa.sa_handler = sigchld_handler;
01531 sigemptyset(&sa.sa_mask);
01532 sa.sa_flags = SA_RESTART;
01533 if(sigaction(SIGCHLD, &sa, NULL) == -1)
01534 err("sigaction: %m");
01535 sa.sa_handler = sigterm_handler;
01536 sigemptyset(&sa.sa_mask);
01537 sa.sa_flags = SA_RESTART;
01538 if(sigaction(SIGTERM, &sa, NULL) == -1)
01539 err("sigaction: %m");
01540 }
01541
01542
01543
01544
01545 void setup_servers(GArray* servers) {
01546 int i;
01547
01548 for(i=0;i<servers->len;i++) {
01549 setup_serve(&(g_array_index(servers, SERVER, i)));
01550 }
01551 children=g_hash_table_new_full(g_int_hash, g_int_equal, NULL, destroy_pid_t);
01552 }
01553
01554
01555
01556
01557
01558
01559
01560
01561 #if !defined(NODAEMON) && !defined(NOFORK)
01562 void daemonize(SERVER* serve) {
01563 FILE*pidf;
01564
01565 if(serve && !(serve->port)) {
01566 return;
01567 }
01568 if(daemon(0,0)<0) {
01569 err("daemon");
01570 }
01571 if(!*pidftemplate) {
01572 if(serve) {
01573 strncpy(pidftemplate, "/var/run/nbd-server.%d.pid", 255);
01574 } else {
01575 strncpy(pidftemplate, "/var/run/nbd-server.pid", 255);
01576 }
01577 }
01578 snprintf(pidfname, 255, pidftemplate, serve ? serve->port : 0);
01579 pidf=fopen(pidfname, "w");
01580 if(pidf) {
01581 fprintf(pidf,"%d\n", (int)getpid());
01582 fclose(pidf);
01583 } else {
01584 perror("fopen");
01585 fprintf(stderr, "Not fatal; continuing");
01586 }
01587 }
01588 #else
01589 #define daemonize(serve)
01590 #endif
01591
01592
01593
01594
01595
01596
01597 void serve_err(SERVER* serve, const char* msg) G_GNUC_NORETURN;
01598
01599 void serve_err(SERVER* serve, const char* msg) {
01600 g_message("Export of %s on port %d failed:", serve->exportname,
01601 serve->port);
01602 err(msg);
01603 }
01604
01605
01606
01607
01608 void dousers(void) {
01609 struct passwd *pw;
01610 struct group *gr;
01611 if(rungroup) {
01612 gr=getgrnam(rungroup);
01613 if(!gr) {
01614 g_message("Invalid group name: %s", rungroup);
01615 exit(EXIT_FAILURE);
01616 }
01617 if(setgid(gr->gr_gid)<0) {
01618 g_message("Could not set GID: %s", strerror(errno));
01619 exit(EXIT_FAILURE);
01620 }
01621 }
01622 if(runuser) {
01623 pw=getpwnam(runuser);
01624 if(!pw) {
01625 g_message("Invalid user name: %s", runuser);
01626 exit(EXIT_FAILURE);
01627 }
01628 if(setuid(pw->pw_uid)<0) {
01629 g_message("Could not set UID: %s", strerror(errno));
01630 exit(EXIT_FAILURE);
01631 }
01632 }
01633 }
01634
01635
01636
01637
01638 int main(int argc, char *argv[]) {
01639 SERVER *serve;
01640 GArray *servers;
01641 GError *err=NULL;
01642
01643 if (sizeof( struct nbd_request )!=28) {
01644 fprintf(stderr,"Bad size of structure. Alignment problems?\n");
01645 exit(EXIT_FAILURE) ;
01646 }
01647
01648 memset(pidftemplate, '\0', 256);
01649
01650 logging();
01651 config_file_pos = g_strdup(CFILE);
01652 serve=cmdline(argc, argv);
01653 servers = parse_cfile(config_file_pos, &err);
01654 if(!servers || !servers->len) {
01655 g_warning("Could not parse config file: %s",
01656 err ? err->message : "Unknown error");
01657 }
01658 if(serve) {
01659 g_array_append_val(servers, *serve);
01660
01661 if (!(serve->port)) {
01662 CLIENT *client;
01663 #ifndef ISSERVER
01664
01665
01666
01667
01668 close(1);
01669 close(2);
01670 open("/dev/null", O_WRONLY);
01671 open("/dev/null", O_WRONLY);
01672 #endif
01673 client=g_malloc(sizeof(CLIENT));
01674 client->server=serve;
01675 client->net=0;
01676 client->exportsize=OFFT_MAX;
01677 set_peername(0,client);
01678 serveconnection(client);
01679 return 0;
01680 }
01681 }
01682 if((!serve) && (!servers||!servers->len)) {
01683 g_message("Nothing to do! Bye!");
01684 exit(EXIT_FAILURE);
01685 }
01686 daemonize(serve);
01687 setup_servers(servers);
01688 dousers();
01689 serveloop(servers);
01690 return 0 ;
01691 }