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 { "autoreadonly", FALSE, PARAM_BOOL, NULL, F_AUTOREADONLY },
00559 { "sparse_cow", FALSE, PARAM_BOOL, NULL, F_SPARSE },
00560 { "sdp", FALSE, PARAM_BOOL, NULL, F_SDP },
00561 { "listenaddr", FALSE, PARAM_STRING, NULL, 0 },
00562 };
00563 const int lp_size=15;
00564 PARAM gp[] = {
00565 { "user", FALSE, PARAM_STRING, &runuser, 0 },
00566 { "group", FALSE, PARAM_STRING, &rungroup, 0 },
00567 };
00568 PARAM* p=gp;
00569 int p_size=2;
00570 GKeyFile *cfile;
00571 GError *err = NULL;
00572 const char *err_msg=NULL;
00573 GQuark errdomain;
00574 GArray *retval=NULL;
00575 gchar **groups;
00576 gboolean value;
00577 gint i;
00578 gint j;
00579
00580 errdomain = g_quark_from_string("parse_cfile");
00581 cfile = g_key_file_new();
00582 retval = g_array_new(FALSE, TRUE, sizeof(SERVER));
00583 if(!g_key_file_load_from_file(cfile, f, G_KEY_FILE_KEEP_COMMENTS |
00584 G_KEY_FILE_KEEP_TRANSLATIONS, &err)) {
00585 g_set_error(e, errdomain, CFILE_NOTFOUND, "Could not open config file.");
00586 g_key_file_free(cfile);
00587 return retval;
00588 }
00589 if(strcmp(g_key_file_get_start_group(cfile), "generic")) {
00590 g_set_error(e, errdomain, CFILE_MISSING_GENERIC, "Config file does not contain the [generic] group!");
00591 g_key_file_free(cfile);
00592 return NULL;
00593 }
00594 groups = g_key_file_get_groups(cfile, NULL);
00595 for(i=0;groups[i];i++) {
00596 memset(&s, '\0', sizeof(SERVER));
00597 lp[0].target=&(s.exportname);
00598 lp[1].target=&(s.port);
00599 lp[2].target=&(s.authname);
00600 lp[3].target=&(s.timeout);
00601 lp[4].target=&(s.expected_size);
00602 lp[5].target=&(virtstyle);
00603 lp[6].target=&(s.prerun);
00604 lp[7].target=&(s.postrun);
00605 lp[8].target=lp[9].target=lp[10].target=
00606 lp[11].target=lp[12].target=
00607 lp[13].target=&(s.flags);
00608 lp[14].target=&(s.listenaddr);
00609
00610
00611 if(i==1) {
00612 p=lp;
00613 p_size=lp_size;
00614 }
00615 for(j=0;j<p_size;j++) {
00616 g_assert(p[j].target != NULL);
00617 g_assert(p[j].ptype==PARAM_INT||p[j].ptype==PARAM_STRING||p[j].ptype==PARAM_BOOL);
00618 switch(p[j].ptype) {
00619 case PARAM_INT:
00620 *((gint*)p[j].target) =
00621 g_key_file_get_integer(cfile,
00622 groups[i],
00623 p[j].paramname,
00624 &err);
00625 break;
00626 case PARAM_STRING:
00627 *((gchar**)p[j].target) =
00628 g_key_file_get_string(cfile,
00629 groups[i],
00630 p[j].paramname,
00631 &err);
00632 break;
00633 case PARAM_BOOL:
00634 value = g_key_file_get_boolean(cfile,
00635 groups[i],
00636 p[j].paramname, &err);
00637 if(!err) {
00638 if(value) {
00639 *((gint*)p[j].target) |= p[j].flagval;
00640 } else {
00641 *((gint*)p[j].target) &= ~(p[j].flagval);
00642 }
00643 }
00644 break;
00645 }
00646 if(err) {
00647 if(err->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND) {
00648 if(!p[j].required) {
00649
00650 g_clear_error(&err);
00651 continue;
00652 } else {
00653 err_msg = MISSING_REQUIRED_ERROR;
00654 }
00655 } else {
00656 err_msg = DEFAULT_ERROR;
00657 }
00658 g_set_error(e, errdomain, CFILE_VALUE_INVALID, err_msg, p[j].paramname, groups[i], err->message);
00659 g_array_free(retval, TRUE);
00660 g_error_free(err);
00661 g_key_file_free(cfile);
00662 return NULL;
00663 }
00664 }
00665 if(virtstyle) {
00666 if(!strncmp(virtstyle, "none", 4)) {
00667 s.virtstyle=VIRT_NONE;
00668 } else if(!strncmp(virtstyle, "ipliteral", 9)) {
00669 s.virtstyle=VIRT_IPLIT;
00670 } else if(!strncmp(virtstyle, "iphash", 6)) {
00671 s.virtstyle=VIRT_IPHASH;
00672 } else if(!strncmp(virtstyle, "cidrhash", 8)) {
00673 s.virtstyle=VIRT_CIDR;
00674 if(strlen(virtstyle)<10) {
00675 g_set_error(e, errdomain, CFILE_VALUE_INVALID, "Invalid value %s for parameter virtstyle in group %s: missing length", virtstyle, groups[i]);
00676 g_array_free(retval, TRUE);
00677 g_key_file_free(cfile);
00678 return NULL;
00679 }
00680 s.cidrlen=strtol(virtstyle+8, NULL, 0);
00681 } else {
00682 g_set_error(e, errdomain, CFILE_VALUE_INVALID, "Invalid value %s for parameter virtstyle in group %s", virtstyle, groups[i]);
00683 g_array_free(retval, TRUE);
00684 g_key_file_free(cfile);
00685 return NULL;
00686 }
00687 } else {
00688 s.virtstyle=VIRT_IPLIT;
00689 }
00690
00691 virtstyle=NULL;
00692
00693 if(i>0) {
00694 if(!s.listenaddr) {
00695 s.listenaddr = g_strdup("0.0.0.0");
00696 }
00697 g_array_append_val(retval, s);
00698 }
00699 #ifndef WITH_SDP
00700 if(s.flags & F_SDP) {
00701 g_set_error(e, errdomain, CFILE_VALUE_UNSUPPORTED, "This nbd-server was built without support for SDP, yet group %s uses it", groups[i]);
00702 g_array_free(retval, TRUE);
00703 g_key_file_free(cfile);
00704 return NULL;
00705 }
00706 #endif
00707 }
00708 return retval;
00709 }
00710
00711
00712
00713
00714
00715
00716 void sigchld_handler(int s) {
00717 int status;
00718 int* i;
00719 pid_t pid;
00720
00721 while((pid=waitpid(-1, &status, WNOHANG)) > 0) {
00722 if(WIFEXITED(status)) {
00723 msg3(LOG_INFO, "Child exited with %d", WEXITSTATUS(status));
00724 }
00725 i=g_hash_table_lookup(children, &pid);
00726 if(!i) {
00727 msg3(LOG_INFO, "SIGCHLD received for an unknown child with PID %ld", (long)pid);
00728 } else {
00729 DEBUG2("Removing %d from the list of children", pid);
00730 g_hash_table_remove(children, &pid);
00731 }
00732 }
00733 }
00734
00735
00736
00737
00738
00739
00740
00741
00742
00743 void killchild(gpointer key, gpointer value, gpointer user_data) {
00744 pid_t *pid=value;
00745 int *parent=user_data;
00746
00747 kill(*pid, SIGTERM);
00748 *parent=1;
00749 }
00750
00751
00752
00753
00754
00755
00756 void sigterm_handler(int s) {
00757 int parent=0;
00758
00759 g_hash_table_foreach(children, killchild, &parent);
00760
00761 if(parent) {
00762 unlink(pidfname);
00763 }
00764
00765 exit(EXIT_SUCCESS);
00766 }
00767
00768
00769
00770
00771
00772
00773
00774
00775 off_t size_autodetect(int fhandle) {
00776 off_t es;
00777 unsigned long sectors;
00778 struct stat stat_buf;
00779 int error;
00780
00781 #ifdef HAVE_SYS_MOUNT_H
00782 #ifdef HAVE_SYS_IOCTL_H
00783 #ifdef BLKGETSIZE
00784 DEBUG("looking for export size with ioctl BLKGETSIZE\n");
00785 if (!ioctl(fhandle, BLKGETSIZE, §ors) && sectors) {
00786 es = (off_t)sectors * (off_t)512;
00787 return es;
00788 }
00789 #endif
00790 #endif
00791 #endif
00792
00793 DEBUG("looking for fhandle size with fstat\n");
00794 stat_buf.st_size = 0;
00795 error = fstat(fhandle, &stat_buf);
00796 if (!error) {
00797 if(stat_buf.st_size > 0)
00798 return (off_t)stat_buf.st_size;
00799 } else {
00800 err("fstat failed: %m");
00801 }
00802
00803 DEBUG("looking for fhandle size with lseek SEEK_END\n");
00804 es = lseek(fhandle, (off_t)0, SEEK_END);
00805 if (es > ((off_t)0)) {
00806 return es;
00807 } else {
00808 DEBUG2("lseek failed: %d", errno==EBADF?1:(errno==ESPIPE?2:(errno==EINVAL?3:4)));
00809 }
00810
00811 err("Could not find size of exported block device: %m");
00812 return OFFT_MAX;
00813 }
00814
00815
00816
00817
00818
00819
00820
00821
00822
00823
00824
00825
00826 int get_filepos(GArray* export, off_t a, int* fhandle, off_t* foffset, size_t* maxbytes ) {
00827
00828 if(a < 0)
00829 return -1;
00830
00831
00832 FILE_INFO fi;
00833 int start = 0;
00834 int end = export->len - 1;
00835 while( start <= end ) {
00836 int mid = (start + end) / 2;
00837 fi = g_array_index(export, FILE_INFO, mid);
00838 if( fi.startoff < a ) {
00839 start = mid + 1;
00840 } else if( fi.startoff > a ) {
00841 end = mid - 1;
00842 } else {
00843 start = end = mid;
00844 break;
00845 }
00846 }
00847
00848
00849 g_assert(end >= 0);
00850
00851 fi = g_array_index(export, FILE_INFO, end);
00852 *fhandle = fi.fhandle;
00853 *foffset = a - fi.startoff;
00854 *maxbytes = 0;
00855 if( end+1 < export->len ) {
00856 FILE_INFO fi_next = g_array_index(export, FILE_INFO, end+1);
00857 *maxbytes = fi_next.startoff - a;
00858 }
00859
00860 return 0;
00861 }
00862
00863
00864
00865
00866
00867
00868
00869
00870
00871 void myseek(int handle,off_t a) {
00872 if (lseek(handle, a, SEEK_SET) < 0) {
00873 err("Can not seek locally!\n");
00874 }
00875 }
00876
00877
00878
00879
00880
00881
00882
00883
00884
00885
00886
00887 ssize_t rawexpwrite(off_t a, char *buf, size_t len, CLIENT *client) {
00888 int fhandle;
00889 off_t foffset;
00890 size_t maxbytes;
00891
00892 if(get_filepos(client->export, a, &fhandle, &foffset, &maxbytes))
00893 return -1;
00894 if(maxbytes && len > maxbytes)
00895 len = maxbytes;
00896
00897 DEBUG4("(WRITE to fd %d offset %Lu len %u), ", fhandle, foffset, len);
00898
00899 myseek(fhandle, foffset);
00900 return write(fhandle, buf, len);
00901 }
00902
00903
00904
00905
00906
00907 int rawexpwrite_fully(off_t a, char *buf, size_t len, CLIENT *client) {
00908 ssize_t ret=0;
00909
00910 while(len > 0 && (ret=rawexpwrite(a, buf, len, client)) > 0 ) {
00911 a += ret;
00912 buf += ret;
00913 len -= ret;
00914 }
00915 return (ret < 0 || len != 0);
00916 }
00917
00918
00919
00920
00921
00922
00923
00924
00925
00926
00927
00928
00929 ssize_t rawexpread(off_t a, char *buf, size_t len, CLIENT *client) {
00930 int fhandle;
00931 off_t foffset;
00932 size_t maxbytes;
00933
00934 if(get_filepos(client->export, a, &fhandle, &foffset, &maxbytes))
00935 return -1;
00936 if(maxbytes && len > maxbytes)
00937 len = maxbytes;
00938
00939 DEBUG4("(READ from fd %d offset %Lu len %u), ", fhandle, foffset, len);
00940
00941 myseek(fhandle, foffset);
00942 return read(fhandle, buf, len);
00943 }
00944
00945
00946
00947
00948
00949 int rawexpread_fully(off_t a, char *buf, size_t len, CLIENT *client) {
00950 ssize_t ret=0;
00951
00952 while(len > 0 && (ret=rawexpread(a, buf, len, client)) > 0 ) {
00953 a += ret;
00954 buf += ret;
00955 len -= ret;
00956 }
00957 return (ret < 0 || len != 0);
00958 }
00959
00960
00961
00962
00963
00964
00965
00966
00967
00968
00969
00970 int expread(off_t a, char *buf, size_t len, CLIENT *client) {
00971 off_t rdlen, offset;
00972 off_t mapcnt, mapl, maph, pagestart;
00973
00974 if (!(client->server->flags & F_COPYONWRITE))
00975 return(rawexpread_fully(a, buf, len, client));
00976 DEBUG3("Asked to read %d bytes at %Lu.\n", len, (unsigned long long)a);
00977
00978 mapl=a/DIFFPAGESIZE; maph=(a+len-1)/DIFFPAGESIZE;
00979
00980 for (mapcnt=mapl;mapcnt<=maph;mapcnt++) {
00981 pagestart=mapcnt*DIFFPAGESIZE;
00982 offset=a-pagestart;
00983 rdlen=(0<DIFFPAGESIZE-offset && len<(size_t)(DIFFPAGESIZE-offset)) ?
00984 len : (size_t)DIFFPAGESIZE-offset;
00985 if (client->difmap[mapcnt]!=(u32)(-1)) {
00986 DEBUG3("Page %Lu is at %lu\n", (unsigned long long)mapcnt,
00987 (unsigned long)(client->difmap[mapcnt]));
00988 myseek(client->difffile, client->difmap[mapcnt]*DIFFPAGESIZE+offset);
00989 if (read(client->difffile, buf, rdlen) != rdlen) return -1;
00990 } else {
00991 DEBUG2("Page %Lu is not here, we read the original one\n",
00992 (unsigned long long)mapcnt);
00993 if(rawexpread_fully(a, buf, rdlen, client)) return -1;
00994 }
00995 len-=rdlen; a+=rdlen; buf+=rdlen;
00996 }
00997 return 0;
00998 }
00999
01000
01001
01002
01003
01004
01005
01006
01007
01008
01009
01010
01011 int expwrite(off_t a, char *buf, size_t len, CLIENT *client) {
01012 char pagebuf[DIFFPAGESIZE];
01013 off_t mapcnt,mapl,maph;
01014 off_t wrlen,rdlen;
01015 off_t pagestart;
01016 off_t offset;
01017
01018 if (!(client->server->flags & F_COPYONWRITE))
01019 return(rawexpwrite_fully(a, buf, len, client));
01020 DEBUG3("Asked to write %d bytes at %Lu.\n", len, (unsigned long long)a);
01021
01022 mapl=a/DIFFPAGESIZE ; maph=(a+len-1)/DIFFPAGESIZE ;
01023
01024 for (mapcnt=mapl;mapcnt<=maph;mapcnt++) {
01025 pagestart=mapcnt*DIFFPAGESIZE ;
01026 offset=a-pagestart ;
01027 wrlen=(0<DIFFPAGESIZE-offset && len<(size_t)(DIFFPAGESIZE-offset)) ?
01028 len : (size_t)DIFFPAGESIZE-offset;
01029
01030 if (client->difmap[mapcnt]!=(u32)(-1)) {
01031 DEBUG3("Page %Lu is at %lu\n", (unsigned long long)mapcnt,
01032 (unsigned long)(client->difmap[mapcnt])) ;
01033 myseek(client->difffile,
01034 client->difmap[mapcnt]*DIFFPAGESIZE+offset);
01035 if (write(client->difffile, buf, wrlen) != wrlen) return -1 ;
01036 } else {
01037 myseek(client->difffile,client->difffilelen*DIFFPAGESIZE) ;
01038 client->difmap[mapcnt]=(client->server->flags&F_SPARSE)?mapcnt:client->difffilelen++;
01039 DEBUG3("Page %Lu is not here, we put it at %lu\n",
01040 (unsigned long long)mapcnt,
01041 (unsigned long)(client->difmap[mapcnt]));
01042 rdlen=DIFFPAGESIZE ;
01043 if (rawexpread_fully(pagestart, pagebuf, rdlen, client))
01044 return -1;
01045 memcpy(pagebuf+offset,buf,wrlen) ;
01046 if (write(client->difffile, pagebuf, DIFFPAGESIZE) !=
01047 DIFFPAGESIZE)
01048 return -1;
01049 }
01050 len-=wrlen ; a+=wrlen ; buf+=wrlen ;
01051 }
01052 return 0;
01053 }
01054
01055
01056
01057
01058
01059
01060 void negotiate(CLIENT *client) {
01061 char zeros[128];
01062 u64 size_host;
01063 u32 flags = NBD_FLAG_HAS_FLAGS;
01064
01065 memset(zeros, '\0', sizeof(zeros));
01066 if (write(client->net, INIT_PASSWD, 8) < 0)
01067 err("Negotiation failed: %m");
01068 cliserv_magic = htonll(cliserv_magic);
01069 if (write(client->net, &cliserv_magic, sizeof(cliserv_magic)) < 0)
01070 err("Negotiation failed: %m");
01071 size_host = htonll((u64)(client->exportsize));
01072 if (write(client->net, &size_host, 8) < 0)
01073 err("Negotiation failed: %m");
01074 if (client->server->flags & F_READONLY)
01075 flags |= NBD_FLAG_READ_ONLY;
01076 flags = htonl(flags);
01077 if (write(client->net, &flags, 4) < 0)
01078 err("Negotiation failed: %m");
01079 if (write(client->net, zeros, 124) < 0)
01080 err("Negotiation failed: %m");
01081 }
01082
01083
01084 #define SEND(net,reply) writeit( net, &reply, sizeof( reply ));
01085
01086 #define ERROR(client,reply,errcode) { reply.error = htonl(errcode); SEND(client->net,reply); reply.error = 0; }
01087
01088
01089
01090
01091
01092
01093
01094
01095
01096 int mainloop(CLIENT *client) {
01097 struct nbd_request request;
01098 struct nbd_reply reply;
01099 gboolean go_on=TRUE;
01100 #ifdef DODBG
01101 int i = 0;
01102 #endif
01103 negotiate(client);
01104 DEBUG("Entering request loop!\n");
01105 reply.magic = htonl(NBD_REPLY_MAGIC);
01106 reply.error = 0;
01107 while (go_on) {
01108 char buf[BUFSIZE];
01109 size_t len;
01110 #ifdef DODBG
01111 i++;
01112 printf("%d: ", i);
01113 #endif
01114 if (client->server->timeout)
01115 alarm(client->server->timeout);
01116 readit(client->net, &request, sizeof(request));
01117 request.from = ntohll(request.from);
01118 request.type = ntohl(request.type);
01119
01120 if (request.type==NBD_CMD_DISC) {
01121 msg2(LOG_INFO, "Disconnect request received.");
01122 if (client->server->flags & F_COPYONWRITE) {
01123 if (client->difmap) g_free(client->difmap) ;
01124 close(client->difffile);
01125 unlink(client->difffilename);
01126 free(client->difffilename);
01127 }
01128 go_on=FALSE;
01129 continue;
01130 }
01131
01132 len = ntohl(request.len);
01133
01134 if (request.magic != htonl(NBD_REQUEST_MAGIC))
01135 err("Not enough magic.");
01136 if (len > BUFSIZE + sizeof(struct nbd_reply))
01137 err("Request too big!");
01138 #ifdef DODBG
01139 printf("%s from %Lu (%Lu) len %d, ", request.type ? "WRITE" :
01140 "READ", (unsigned long long)request.from,
01141 (unsigned long long)request.from / 512, len);
01142 #endif
01143 memcpy(reply.handle, request.handle, sizeof(reply.handle));
01144 if ((request.from + len) > (OFFT_MAX)) {
01145 DEBUG("[Number too large!]");
01146 ERROR(client, reply, EINVAL);
01147 continue;
01148 }
01149
01150 if (((ssize_t)((off_t)request.from + len) > client->exportsize)) {
01151 DEBUG("[RANGE!]");
01152 ERROR(client, reply, EINVAL);
01153 continue;
01154 }
01155
01156 if (request.type==NBD_CMD_WRITE) {
01157 DEBUG("wr: net->buf, ");
01158 readit(client->net, buf, len);
01159 DEBUG("buf->exp, ");
01160 if ((client->server->flags & F_READONLY) ||
01161 (client->server->flags & F_AUTOREADONLY)) {
01162 DEBUG("[WRITE to READONLY!]");
01163 ERROR(client, reply, EPERM);
01164 continue;
01165 }
01166 if (expwrite(request.from, buf, len, client)) {
01167 DEBUG("Write failed: %m" );
01168 ERROR(client, reply, errno);
01169 continue;
01170 }
01171 SEND(client->net, reply);
01172 DEBUG("OK!\n");
01173 continue;
01174 }
01175
01176
01177 DEBUG("exp->buf, ");
01178 if (expread(request.from, buf + sizeof(struct nbd_reply), len, client)) {
01179 DEBUG("Read failed: %m");
01180 ERROR(client, reply, errno);
01181 continue;
01182 }
01183
01184 DEBUG("buf->net, ");
01185 memcpy(buf, &reply, sizeof(struct nbd_reply));
01186 writeit(client->net, buf, len + sizeof(struct nbd_reply));
01187 DEBUG("OK!\n");
01188 }
01189 return 0;
01190 }
01191
01192
01193
01194
01195
01196
01197 void setupexport(CLIENT* client) {
01198 int i;
01199 off_t laststartoff = 0, lastsize = 0;
01200 int multifile = (client->server->flags & F_MULTIFILE);
01201
01202 client->export = g_array_new(TRUE, TRUE, sizeof(FILE_INFO));
01203
01204
01205
01206
01207 for(i=0; ; i++) {
01208 FILE_INFO fi;
01209 gchar *tmpname;
01210 mode_t mode = (client->server->flags & F_READONLY) ? O_RDONLY : O_RDWR;
01211
01212 if(multifile) {
01213 tmpname=g_strdup_printf("%s.%d", client->exportname, i);
01214 } else {
01215 tmpname=g_strdup(client->exportname);
01216 }
01217 DEBUG2( "Opening %s\n", tmpname );
01218 fi.fhandle = open(tmpname, mode);
01219 if(fi.fhandle == -1 && mode == O_RDWR) {
01220
01221 fi.fhandle = open(tmpname, O_RDONLY);
01222 if(fi.fhandle != -1) {
01223 client->server->flags |= F_AUTOREADONLY;
01224 client->server->flags |= F_READONLY;
01225 }
01226 }
01227 if(fi.fhandle == -1) {
01228 if(multifile && i>0)
01229 break;
01230 err("Could not open exported file: %m");
01231 }
01232 fi.startoff = laststartoff + lastsize;
01233 g_array_append_val(client->export, fi);
01234 g_free(tmpname);
01235
01236
01237
01238 laststartoff = fi.startoff;
01239 lastsize = size_autodetect(fi.fhandle);
01240
01241 if(!multifile)
01242 break;
01243 }
01244
01245
01246 client->exportsize = laststartoff + lastsize;
01247
01248
01249 if(client->server->expected_size) {
01250
01251 if(client->server->expected_size > client->exportsize) {
01252 err("Size of exported file is too big\n");
01253 }
01254
01255 client->exportsize = client->server->expected_size;
01256 }
01257
01258 msg3(LOG_INFO, "Size of exported file/device is %Lu", (unsigned long long)client->exportsize);
01259 if(multifile) {
01260 msg3(LOG_INFO, "Total number of files: %d", i);
01261 }
01262 }
01263
01264 int copyonwrite_prepare(CLIENT* client) {
01265 off_t i;
01266 if ((client->difffilename = malloc(1024))==NULL)
01267 err("Failed to allocate string for diff file name");
01268 snprintf(client->difffilename, 1024, "%s-%s-%d.diff",client->exportname,client->clientname,
01269 (int)getpid()) ;
01270 client->difffilename[1023]='\0';
01271 msg3(LOG_INFO,"About to create map and diff file %s",client->difffilename) ;
01272 client->difffile=open(client->difffilename,O_RDWR | O_CREAT | O_TRUNC,0600) ;
01273 if (client->difffile<0) err("Could not create diff file (%m)") ;
01274 if ((client->difmap=calloc(client->exportsize/DIFFPAGESIZE,sizeof(u32)))==NULL)
01275 err("Could not allocate memory") ;
01276 for (i=0;i<client->exportsize/DIFFPAGESIZE;i++) client->difmap[i]=(u32)-1 ;
01277
01278 return 0;
01279 }
01280
01281
01282
01283
01284
01285
01286
01287
01288 int do_run(gchar* command, gchar* file) {
01289 gchar* cmd;
01290 int retval=0;
01291
01292 if(command && *command) {
01293 cmd = g_strdup_printf(command, file);
01294 retval=system(cmd);
01295 g_free(cmd);
01296 }
01297 return retval;
01298 }
01299
01300
01301
01302
01303
01304
01305
01306
01307
01308 void serveconnection(CLIENT *client) {
01309 if(do_run(client->server->prerun, client->exportname)) {
01310 exit(EXIT_FAILURE);
01311 }
01312 setupexport(client);
01313
01314 if (client->server->flags & F_COPYONWRITE) {
01315 copyonwrite_prepare(client);
01316 }
01317
01318 setmysockopt(client->net);
01319
01320 mainloop(client);
01321 do_run(client->server->postrun, client->exportname);
01322 }
01323
01324
01325
01326
01327
01328
01329
01330
01331
01332
01333
01334
01335 void set_peername(int net, CLIENT *client) {
01336 struct sockaddr_in addrin;
01337 struct sockaddr_in netaddr;
01338 socklen_t addrinlen = sizeof( addrin );
01339 char *peername;
01340 char *netname;
01341 char *tmp;
01342 int i;
01343
01344 if (getpeername(net, (struct sockaddr *) &addrin, (socklen_t *)&addrinlen) < 0)
01345 err("getsockname failed: %m");
01346 peername = g_strdup(inet_ntoa(addrin.sin_addr));
01347 switch(client->server->virtstyle) {
01348 case VIRT_NONE:
01349 client->exportname=g_strdup(client->server->exportname);
01350 break;
01351 case VIRT_IPHASH:
01352 for(i=0;i<strlen(peername);i++) {
01353 if(peername[i]=='.') {
01354 peername[i]='/';
01355 }
01356 }
01357 case VIRT_IPLIT:
01358 client->exportname=g_strdup_printf(client->server->exportname, peername);
01359 break;
01360 case VIRT_CIDR:
01361 memcpy(&netaddr, &addrin, addrinlen);
01362 netaddr.sin_addr.s_addr>>=32-(client->server->cidrlen);
01363 netaddr.sin_addr.s_addr<<=32-(client->server->cidrlen);
01364 netname = inet_ntoa(netaddr.sin_addr);
01365 tmp=g_strdup_printf("%s/%s", netname, peername);
01366 client->exportname=g_strdup_printf(client->server->exportname, tmp);
01367 break;
01368 }
01369
01370 msg4(LOG_INFO, "connect from %s, assigned file is %s",
01371 peername, client->exportname);
01372 client->clientname=g_strdup(peername);
01373 g_free(peername);
01374 }
01375
01376
01377
01378
01379
01380 void destroy_pid_t(gpointer data) {
01381 g_free(data);
01382 }
01383
01384
01385
01386
01387 int serveloop(GArray* servers) {
01388 struct sockaddr_in addrin;
01389 socklen_t addrinlen=sizeof(addrin);
01390 SERVER *serve;
01391 int i;
01392 int max;
01393 int sock;
01394 fd_set mset;
01395 fd_set rset;
01396 struct timeval tv;
01397
01398
01399
01400
01401
01402
01403
01404
01405 max=0;
01406 FD_ZERO(&mset);
01407 for(i=0;i<servers->len;i++) {
01408 sock=(g_array_index(servers, SERVER, i)).socket;
01409 FD_SET(sock, &mset);
01410 max=sock>max?sock:max;
01411 }
01412 for(;;) {
01413 CLIENT *client;
01414 int net;
01415 pid_t *pid;
01416
01417 memcpy(&rset, &mset, sizeof(fd_set));
01418 tv.tv_sec=0;
01419 tv.tv_usec=500;
01420 if(select(max+1, &rset, NULL, NULL, &tv)>0) {
01421 DEBUG("accept, ");
01422 for(i=0;i<servers->len;i++) {
01423 serve=&(g_array_index(servers, SERVER, i));
01424 if(FD_ISSET(serve->socket, &rset)) {
01425 if ((net=accept(serve->socket, (struct sockaddr *) &addrin, &addrinlen)) < 0)
01426 err("accept: %m");
01427
01428 client = g_malloc(sizeof(CLIENT));
01429 client->server=serve;
01430 client->exportsize=OFFT_MAX;
01431 client->net=net;
01432 set_peername(net, client);
01433 if (!authorized_client(client)) {
01434 msg2(LOG_INFO,"Unauthorized client") ;
01435 close(net);
01436 continue;
01437 }
01438 msg2(LOG_INFO,"Authorized client") ;
01439 pid=g_malloc(sizeof(pid_t));
01440 #ifndef NOFORK
01441 if ((*pid=fork())<0) {
01442 msg3(LOG_INFO,"Could not fork (%s)",strerror(errno)) ;
01443 close(net);
01444 continue;
01445 }
01446 if (*pid>0) {
01447 close(net);
01448 g_hash_table_insert(children, pid, pid);
01449 continue;
01450 }
01451
01452 g_hash_table_destroy(children);
01453 for(i=0;i<servers->len;i++) {
01454 serve=&g_array_index(servers, SERVER, i);
01455 close(serve->socket);
01456 }
01457
01458
01459
01460
01461
01462
01463 g_array_free(servers, FALSE);
01464 #endif // NOFORK
01465 msg2(LOG_INFO,"Starting to serve");
01466 serveconnection(client);
01467 exit(EXIT_SUCCESS);
01468 }
01469 }
01470 }
01471 }
01472 }
01473
01474
01475
01476
01477
01478
01479 void setup_serve(SERVER *serve) {
01480 struct sockaddr_in addrin;
01481 struct sigaction sa;
01482 int addrinlen = sizeof(addrin);
01483 int sock_flags;
01484 int af;
01485 #ifndef sun
01486 int yes=1;
01487 #else
01488 char yes='1';
01489 #endif
01490
01491 af = AF_INET;
01492 #ifdef WITH_SDP
01493 if ((serve->flags) && F_SDP) {
01494 af = AF_INET_SDP;
01495 }
01496 #endif
01497 if ((serve->socket = socket(af, SOCK_STREAM, IPPROTO_TCP)) < 0)
01498 err("socket: %m");
01499
01500
01501 if (setsockopt(serve->socket,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) {
01502 err("setsockopt SO_REUSEADDR");
01503 }
01504 if (setsockopt(serve->socket,SOL_SOCKET,SO_KEEPALIVE,&yes,sizeof(int)) == -1) {
01505 err("setsockopt SO_KEEPALIVE");
01506 }
01507
01508
01509 if ((sock_flags = fcntl(serve->socket, F_GETFL, 0)) == -1) {
01510 err("fcntl F_GETFL");
01511 }
01512 if (fcntl(serve->socket, F_SETFL, sock_flags | O_NONBLOCK) == -1) {
01513 err("fcntl F_SETFL O_NONBLOCK");
01514 }
01515
01516 DEBUG("Waiting for connections... bind, ");
01517 addrin.sin_family = AF_INET;
01518 #ifdef WITH_SDP
01519 if(serve->flags & F_SDP) {
01520 addrin.sin_family = AF_INET_SDP;
01521 }
01522 #endif
01523 addrin.sin_port = htons(serve->port);
01524 if(!inet_aton(serve->listenaddr, &(addrin.sin_addr)))
01525 err("could not parse listen address");
01526 if (bind(serve->socket, (struct sockaddr *) &addrin, addrinlen) < 0)
01527 err("bind: %m");
01528 DEBUG("listen, ");
01529 if (listen(serve->socket, 1) < 0)
01530 err("listen: %m");
01531 sa.sa_handler = sigchld_handler;
01532 sigemptyset(&sa.sa_mask);
01533 sa.sa_flags = SA_RESTART;
01534 if(sigaction(SIGCHLD, &sa, NULL) == -1)
01535 err("sigaction: %m");
01536 sa.sa_handler = sigterm_handler;
01537 sigemptyset(&sa.sa_mask);
01538 sa.sa_flags = SA_RESTART;
01539 if(sigaction(SIGTERM, &sa, NULL) == -1)
01540 err("sigaction: %m");
01541 }
01542
01543
01544
01545
01546 void setup_servers(GArray* servers) {
01547 int i;
01548
01549 for(i=0;i<servers->len;i++) {
01550 setup_serve(&(g_array_index(servers, SERVER, i)));
01551 }
01552 children=g_hash_table_new_full(g_int_hash, g_int_equal, NULL, destroy_pid_t);
01553 }
01554
01555
01556
01557
01558
01559
01560
01561
01562 #if !defined(NODAEMON) && !defined(NOFORK)
01563 void daemonize(SERVER* serve) {
01564 FILE*pidf;
01565
01566 if(serve && !(serve->port)) {
01567 return;
01568 }
01569 if(daemon(0,0)<0) {
01570 err("daemon");
01571 }
01572 if(!*pidftemplate) {
01573 if(serve) {
01574 strncpy(pidftemplate, "/var/run/nbd-server.%d.pid", 255);
01575 } else {
01576 strncpy(pidftemplate, "/var/run/nbd-server.pid", 255);
01577 }
01578 }
01579 snprintf(pidfname, 255, pidftemplate, serve ? serve->port : 0);
01580 pidf=fopen(pidfname, "w");
01581 if(pidf) {
01582 fprintf(pidf,"%d\n", (int)getpid());
01583 fclose(pidf);
01584 } else {
01585 perror("fopen");
01586 fprintf(stderr, "Not fatal; continuing");
01587 }
01588 }
01589 #else
01590 #define daemonize(serve)
01591 #endif
01592
01593
01594
01595
01596
01597
01598 void serve_err(SERVER* serve, const char* msg) G_GNUC_NORETURN;
01599
01600 void serve_err(SERVER* serve, const char* msg) {
01601 g_message("Export of %s on port %d failed:", serve->exportname,
01602 serve->port);
01603 err(msg);
01604 }
01605
01606
01607
01608
01609 void dousers(void) {
01610 struct passwd *pw;
01611 struct group *gr;
01612 if(rungroup) {
01613 gr=getgrnam(rungroup);
01614 if(!gr) {
01615 g_message("Invalid group name: %s", rungroup);
01616 exit(EXIT_FAILURE);
01617 }
01618 if(setgid(gr->gr_gid)<0) {
01619 g_message("Could not set GID: %s", strerror(errno));
01620 exit(EXIT_FAILURE);
01621 }
01622 }
01623 if(runuser) {
01624 pw=getpwnam(runuser);
01625 if(!pw) {
01626 g_message("Invalid user name: %s", runuser);
01627 exit(EXIT_FAILURE);
01628 }
01629 if(setuid(pw->pw_uid)<0) {
01630 g_message("Could not set UID: %s", strerror(errno));
01631 exit(EXIT_FAILURE);
01632 }
01633 }
01634 }
01635
01636
01637
01638
01639 int main(int argc, char *argv[]) {
01640 SERVER *serve;
01641 GArray *servers;
01642 GError *err=NULL;
01643
01644 if (sizeof( struct nbd_request )!=28) {
01645 fprintf(stderr,"Bad size of structure. Alignment problems?\n");
01646 exit(EXIT_FAILURE) ;
01647 }
01648
01649 memset(pidftemplate, '\0', 256);
01650
01651 logging();
01652 config_file_pos = g_strdup(CFILE);
01653 serve=cmdline(argc, argv);
01654 servers = parse_cfile(config_file_pos, &err);
01655 if(!servers || !servers->len) {
01656 g_warning("Could not parse config file: %s",
01657 err ? err->message : "Unknown error");
01658 }
01659 if(serve) {
01660 g_array_append_val(servers, *serve);
01661
01662 if (!(serve->port)) {
01663 CLIENT *client;
01664 #ifndef ISSERVER
01665
01666
01667
01668
01669 close(1);
01670 close(2);
01671 open("/dev/null", O_WRONLY);
01672 open("/dev/null", O_WRONLY);
01673 #endif
01674 client=g_malloc(sizeof(CLIENT));
01675 client->server=serve;
01676 client->net=0;
01677 client->exportsize=OFFT_MAX;
01678 set_peername(0,client);
01679 serveconnection(client);
01680 return 0;
01681 }
01682 }
01683 if((!serve) && (!servers||!servers->len)) {
01684 g_message("Nothing to do! Bye!");
01685 exit(EXIT_FAILURE);
01686 }
01687 daemonize(serve);
01688 setup_servers(servers);
01689 dousers();
01690 serveloop(servers);
01691 return 0 ;
01692 }