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