1 /* $NetBSD: rump_allserver.c,v 1.18 2011/02/17 16:59:46 pooka Exp $ */ 2 3 /*- 4 * Copyright (c) 2010, 2011 Antti Kantee. All Rights Reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 #ifndef lint 30 __RCSID("$NetBSD: rump_allserver.c,v 1.18 2011/02/17 16:59:46 pooka Exp $"); 31 #endif /* !lint */ 32 33 #include <sys/types.h> 34 #include <sys/disklabel.h> 35 #include <sys/signal.h> 36 #include <sys/module.h> 37 38 #include <rump/rump.h> 39 #include <rump/rump_syscalls.h> 40 41 #include <dlfcn.h> 42 #include <err.h> 43 #include <errno.h> 44 #include <fcntl.h> 45 #include <semaphore.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <unistd.h> 50 #include <util.h> 51 52 static void 53 usage(void) 54 { 55 56 fprintf(stderr, "usage: %s [-s] [-c ncpu] [-d drivespec] [-l libs] " 57 "[-m modules] bindurl\n", getprogname()); 58 exit(1); 59 } 60 61 static void 62 die(int sflag, int error, const char *reason) 63 { 64 65 warnx("%s: %s", reason, strerror(error)); 66 if (!sflag) 67 rump_daemonize_done(error); 68 exit(1); 69 } 70 71 static sem_t sigsem; 72 static void 73 sigreboot(int sig) 74 { 75 76 sem_post(&sigsem); 77 } 78 79 static const char *const disktokens[] = { 80 #define DKEY 0 81 "key", 82 #define DFILE 1 83 "hostpath", 84 #define DSIZE 2 85 #define DSIZE_E -1 86 "size", 87 #define DOFFSET 3 88 "offset", 89 #define DLABEL 4 90 "disklabel", 91 #define DTYPE 5 92 "type", 93 NULL 94 }; 95 96 struct etfsreg { 97 const char *key; 98 const char *hostpath; 99 off_t flen; 100 off_t foffset; 101 char partition; 102 enum rump_etfs_type type; 103 }; 104 105 struct etfstype { 106 const char *name; 107 enum rump_etfs_type type; 108 } etfstypes[] = { 109 { "blk", RUMP_ETFS_BLK }, 110 { "chr", RUMP_ETFS_CHR }, 111 { "reg", RUMP_ETFS_REG }, 112 }; 113 114 int 115 main(int argc, char *argv[]) 116 { 117 const char *serverurl; 118 char **modarray = NULL; 119 unsigned nmods = 0, curmod = 0, i; 120 struct etfsreg *etfs = NULL; 121 unsigned netfs = 0, curetfs = 0; 122 int error; 123 int ch, sflag; 124 int ncpu; 125 126 setprogname(argv[0]); 127 128 sflag = 0; 129 while ((ch = getopt(argc, argv, "c:d:l:m:s")) != -1) { 130 switch (ch) { 131 case 'c': 132 ncpu = atoi(optarg); 133 /* XXX: MAXCPUS is from host, not from kernel */ 134 if (ncpu < 1 || ncpu > MAXCPUS) 135 err(1, "CPU count needs to be between " 136 "1 and %d\n", MAXCPUS); 137 setenv("RUMP_NCPU", optarg, 1); 138 break; 139 case 'd': { 140 char *options, *value; 141 char *key, *hostpath; 142 long long flen, foffset; 143 char partition; 144 int ftype; 145 146 flen = foffset = 0; 147 partition = 0; 148 key = hostpath = NULL; 149 ftype = -1; 150 options = optarg; 151 while (*options) { 152 switch (getsubopt(&options, 153 __UNCONST(disktokens), &value)) { 154 case DKEY: 155 if (key != NULL) { 156 fprintf(stderr, 157 "key already given\n"); 158 usage(); 159 } 160 key = value; 161 break; 162 163 case DFILE: 164 if (hostpath != NULL) { 165 fprintf(stderr, 166 "hostpath already given\n"); 167 usage(); 168 } 169 hostpath = value; 170 break; 171 172 case DSIZE: 173 if (flen != 0) { 174 fprintf(stderr, 175 "size already given\n"); 176 usage(); 177 } 178 if (strcmp(value, "e") == 0) { 179 if (foffset != 0) { 180 fprintf(stderr, 181 "cannot specify " 182 "offset with " 183 "size=e\n"); 184 usage(); 185 } 186 flen = DSIZE_E; 187 } else { 188 /* XXX: off_t max? */ 189 flen = strsuftoll("-d size", 190 value, 0, LLONG_MAX); 191 } 192 break; 193 case DOFFSET: 194 if (foffset != 0) { 195 fprintf(stderr, 196 "offset already given\n"); 197 usage(); 198 } 199 if (flen == DSIZE_E) { 200 fprintf(stderr, "cannot " 201 "specify offset with " 202 "size=e\n"); 203 usage(); 204 } 205 /* XXX: off_t max? */ 206 foffset = strsuftoll("-d offset", value, 207 0, LLONG_MAX); 208 break; 209 210 case DLABEL: 211 if (foffset != 0 || flen != 0) { 212 fprintf(stderr, 213 "disklabel needs to be " 214 "used alone\n"); 215 usage(); 216 } 217 if (strlen(value) != 1 || 218 *value < 'a' || *value > 'z') { 219 fprintf(stderr, 220 "invalid label part\n"); 221 usage(); 222 } 223 partition = *value; 224 break; 225 226 case DTYPE: 227 if (ftype != -1) { 228 fprintf(stderr, 229 "type already specified\n"); 230 usage(); 231 } 232 233 for (i = 0; 234 i < __arraycount(etfstypes); 235 i++) { 236 if (strcmp(etfstypes[i].name, 237 value) == 0) 238 break; 239 } 240 if (i == __arraycount(etfstypes)) { 241 fprintf(stderr, 242 "invalid type %s\n", value); 243 usage(); 244 } 245 ftype = etfstypes[i].type; 246 break; 247 248 default: 249 fprintf(stderr, "invalid dtoken\n"); 250 usage(); 251 break; 252 } 253 } 254 255 if (key == NULL || hostpath == NULL || 256 (flen == 0 && partition == 0)) { 257 fprintf(stderr, "incomplete drivespec\n"); 258 usage(); 259 } 260 if (ftype == -1) 261 ftype = RUMP_ETFS_BLK; 262 263 if (netfs - curetfs == 0) { 264 etfs = realloc(etfs, (netfs+16)*sizeof(*etfs)); 265 if (etfs == NULL) 266 err(1, "realloc etfs"); 267 netfs += 16; 268 } 269 270 etfs[curetfs].key = key; 271 etfs[curetfs].hostpath = hostpath; 272 etfs[curetfs].flen = flen; 273 etfs[curetfs].foffset = foffset; 274 etfs[curetfs].partition = partition; 275 etfs[curetfs].type = ftype; 276 curetfs++; 277 278 break; 279 } 280 case 'l': 281 if (dlopen(optarg, RTLD_LAZY|RTLD_GLOBAL) == NULL) { 282 char pb[MAXPATHLEN]; 283 /* try to mimic linker -l syntax */ 284 285 snprintf(pb, sizeof(pb), "lib%s.so", optarg); 286 if (dlopen(pb, RTLD_LAZY|RTLD_GLOBAL) == NULL) { 287 errx(1, "dlopen %s failed: %s", 288 pb, dlerror()); 289 } 290 } 291 break; 292 case 'm': 293 if (nmods - curmod == 0) { 294 modarray = realloc(modarray, 295 (nmods+16) * sizeof(char *)); 296 if (modarray == NULL) 297 err(1, "realloc"); 298 nmods += 16; 299 } 300 modarray[curmod++] = optarg; 301 break; 302 case 's': 303 sflag = 1; 304 break; 305 default: 306 usage(); 307 /*NOTREACHED*/ 308 } 309 } 310 311 argc -= optind; 312 argv += optind; 313 314 if (argc != 1) 315 usage(); 316 317 serverurl = argv[0]; 318 319 if (!sflag) { 320 error = rump_daemonize_begin(); 321 if (error) 322 errx(1, "rump daemonize: %s", strerror(error)); 323 } 324 325 error = rump_init(); 326 if (error) 327 die(sflag, error, "rump init failed"); 328 329 /* load modules */ 330 for (i = 0; i < curmod; i++) { 331 struct modctl_load ml; 332 333 #define ETFSKEY "/module.mod" 334 if ((error = rump_pub_etfs_register(ETFSKEY, 335 modarray[0], RUMP_ETFS_REG)) != 0) 336 die(sflag, error, "module etfs register failed"); 337 memset(&ml, 0, sizeof(ml)); 338 ml.ml_filename = ETFSKEY; 339 if (rump_sys_modctl(MODCTL_LOAD, &ml) == -1) 340 die(sflag, errno, "module load failed"); 341 rump_pub_etfs_remove(ETFSKEY); 342 #undef ETFSKEY 343 } 344 345 /* register host drives */ 346 for (i = 0; i < curetfs; i++) { 347 char buf[1<<16]; 348 struct disklabel dl; 349 struct stat sb; 350 off_t foffset, flen, fendoff; 351 int fd, oflags; 352 353 oflags = etfs[i].flen == DSIZE_E ? 0 : O_CREAT; 354 fd = open(etfs[i].hostpath, O_RDWR | oflags, 0644); 355 if (fd == -1) 356 die(sflag, errno, "etfs hostpath open"); 357 358 if (etfs[i].partition) { 359 int partition = etfs[i].partition - 'a'; 360 361 pread(fd, buf, sizeof(buf), 0); 362 if (disklabel_scan(&dl, buf, sizeof(buf))) 363 die(sflag, ENOENT, "disklabel not found"); 364 365 if (partition >= dl.d_npartitions) 366 die(sflag, ENOENT, "partition not available"); 367 368 foffset = dl.d_partitions[partition].p_offset 369 << DEV_BSHIFT; 370 flen = dl.d_partitions[partition].p_size 371 << DEV_BSHIFT; 372 } else { 373 foffset = etfs[i].foffset; 374 flen = etfs[i].flen; 375 } 376 377 if (fstat(fd, &sb) == -1) 378 die(sflag, errno, "fstat etfs hostpath"); 379 if (flen == DSIZE_E) { 380 if (!S_ISREG(sb.st_mode)) 381 die(sflag, EINVAL, "size=e requires reg file"); 382 flen = sb.st_size; 383 } 384 fendoff = foffset + flen; 385 if (S_ISREG(sb.st_mode) && sb.st_size < fendoff) { 386 if (ftruncate(fd, fendoff) == -1) 387 die(sflag, errno, "truncate"); 388 } 389 close(fd); 390 391 if ((error = rump_pub_etfs_register_withsize(etfs[i].key, 392 etfs[i].hostpath, etfs[i].type, foffset, flen)) != 0) 393 die(sflag, error, "etfs register"); 394 } 395 396 error = rump_init_server(serverurl); 397 if (error) 398 die(sflag, error, "rump server init failed"); 399 400 if (!sflag) 401 rump_daemonize_done(RUMP_DAEMONIZE_SUCCESS); 402 403 sem_init(&sigsem, 0, 0); 404 signal(SIGTERM, sigreboot); 405 signal(SIGINT, sigreboot); 406 sem_wait(&sigsem); 407 408 rump_sys_reboot(0, NULL); 409 /*NOTREACHED*/ 410 411 return 0; 412 } 413