xref: /netbsd-src/usr.bin/rump_allserver/rump_allserver.c (revision bfcefd56cfc74946ba0f251734a8426a0b91e115)
1 /*	$NetBSD: rump_allserver.c,v 1.26 2013/09/10 20:36:08 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 <rump/rumpuser_port.h>
29 
30 #ifndef lint
31 __RCSID("$NetBSD: rump_allserver.c,v 1.26 2013/09/10 20:36:08 pooka Exp $");
32 #endif /* !lint */
33 
34 #include <sys/types.h>
35 #include <sys/signal.h>
36 #include <sys/stat.h>
37 
38 #ifdef PLATFORM_HAS_NBMODULES
39 #include <sys/module.h>
40 #endif
41 #ifdef PLATFORM_HAS_DISKLABEL
42 #include <sys/disklabel.h>
43 #include <util.h>
44 #endif
45 
46 #include <dlfcn.h>
47 #include <errno.h>
48 #include <fcntl.h>
49 #include <semaphore.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <stdint.h>
53 #include <string.h>
54 #include <unistd.h>
55 
56 #include <rump/rump.h>
57 #include <rump/rump_syscalls.h>
58 
59 __dead static void
60 usage(void)
61 {
62 
63 #ifndef PLATFORM_HAS_SETGETPROGNAME
64 #define getprogname() "rump_server"
65 #endif
66 	fprintf(stderr, "usage: %s [-s] [-c ncpu] [-d drivespec] [-l libs] "
67 	    "[-m modules] bindurl\n", getprogname());
68 	exit(1);
69 }
70 
71 __dead static void
72 die(int sflag, int error, const char *reason)
73 {
74 
75 	fprintf(stderr, "%s: %s", reason, strerror(error));
76 	if (!sflag)
77 		rump_daemonize_done(error);
78 	exit(1);
79 }
80 
81 static sem_t sigsem;
82 static void
83 sigreboot(int sig)
84 {
85 
86 	sem_post(&sigsem);
87 }
88 
89 static const char *const disktokens[] = {
90 #define DKEY 0
91 	"key",
92 #define DFILE 1
93 	"hostpath",
94 #define DSIZE 2
95 #define DSIZE_E -1
96 	"size",
97 #define DOFFSET 3
98 	"offset",
99 #define DLABEL 4
100 	"disklabel",
101 #define DTYPE 5
102 	"type",
103 	NULL
104 };
105 
106 struct etfsreg {
107 	const char *key;
108 	const char *hostpath;
109 	off_t flen;
110 	off_t foffset;
111 	char partition;
112 	enum rump_etfs_type type;
113 };
114 
115 struct etfstype {
116 	const char *name;
117 	enum rump_etfs_type type;
118 } etfstypes[] = {
119 	{ "blk", RUMP_ETFS_BLK },
120 	{ "chr", RUMP_ETFS_CHR },
121 	{ "reg", RUMP_ETFS_REG },
122 };
123 
124 int
125 main(int argc, char *argv[])
126 {
127 	const char *serverurl;
128 	struct etfsreg *etfs = NULL;
129 	unsigned netfs = 0, curetfs = 0;
130 	int error;
131 	int ch, sflag;
132 	unsigned i;
133 
134 #ifdef PLATFORM_HAS_NBMODULES
135 	char **modarray = NULL;
136 	unsigned nmods = 0, curmod = 0;
137 #endif
138 
139 #ifdef PLATFORM_HAS_SETGETPROGNAME
140 	setprogname(argv[0]);
141 #endif
142 
143 	sflag = 0;
144 	while ((ch = getopt(argc, argv, "c:d:l:m:r:sv")) != -1) {
145 		switch (ch) {
146 		case 'c':
147 			setenv("RUMP_NCPU", optarg, 1);
148 			break;
149 		case 'd': {
150 			char *options, *value;
151 			char *key, *hostpath;
152 			long long flen, foffset;
153 			char partition;
154 			int ftype;
155 
156 			flen = foffset = 0;
157 			partition = 0;
158 			key = hostpath = NULL;
159 			ftype = -1;
160 			options = optarg;
161 			while (*options) {
162 				switch (getsubopt(&options,
163 				    __UNCONST(disktokens), &value)) {
164 				case DKEY:
165 					if (key != NULL) {
166 						fprintf(stderr,
167 						    "key already given\n");
168 						usage();
169 					}
170 					key = value;
171 					break;
172 
173 				case DFILE:
174 					if (hostpath != NULL) {
175 						fprintf(stderr,
176 						    "hostpath already given\n");
177 						usage();
178 					}
179 					hostpath = value;
180 					break;
181 
182 				case DSIZE:
183 					if (flen != 0) {
184 						fprintf(stderr,
185 						    "size already given\n");
186 						usage();
187 					}
188 					if (strcmp(value, "host") == 0) {
189 						if (foffset != 0) {
190 							fprintf(stderr,
191 							    "cannot specify "
192 							    "offset with "
193 							    "size=host\n");
194 							usage();
195 						}
196 						flen = DSIZE_E;
197 					} else {
198 #ifdef PLATFORM_HAS_STRSUFTOLL
199 						/* XXX: off_t max? */
200 						flen = strsuftoll("-d size",
201 						    value, 0, LLONG_MAX);
202 #else
203 						flen = strtoull(value,
204 						    NULL, 10);
205 #endif
206 					}
207 					break;
208 				case DOFFSET:
209 					if (foffset != 0) {
210 						fprintf(stderr,
211 						    "offset already given\n");
212 						usage();
213 					}
214 					if (flen == DSIZE_E) {
215 						fprintf(stderr, "cannot "
216 						    "specify offset with "
217 						    "size=host\n");
218 						usage();
219 					}
220 #ifdef PLATFORM_HAS_STRSUFTOLL
221 					/* XXX: off_t max? */
222 					foffset = strsuftoll("-d offset", value,
223 					    0, LLONG_MAX);
224 #else
225 					foffset = strtoull(value, NULL, 10);
226 #endif
227 					break;
228 
229 				case DLABEL:
230 					if (foffset != 0 || flen != 0) {
231 						fprintf(stderr,
232 						    "disklabel needs to be "
233 						    "used alone\n");
234 						usage();
235 					}
236 					if (strlen(value) != 1 ||
237 					    *value < 'a' || *value > 'z') {
238 						fprintf(stderr,
239 						    "invalid label part\n");
240 						usage();
241 					}
242 					partition = *value;
243 					break;
244 
245 				case DTYPE:
246 					if (ftype != -1) {
247 						fprintf(stderr,
248 						    "type already specified\n");
249 						usage();
250 					}
251 
252 					for (i = 0;
253 					    i < __arraycount(etfstypes);
254 					    i++) {
255 						if (strcmp(etfstypes[i].name,
256 						    value) == 0)
257 							break;
258 					}
259 					if (i == __arraycount(etfstypes)) {
260 						fprintf(stderr,
261 						    "invalid type %s\n", value);
262 						usage();
263 					}
264 					ftype = etfstypes[i].type;
265 					break;
266 
267 				default:
268 					fprintf(stderr, "invalid dtoken\n");
269 					usage();
270 					break;
271 				}
272 			}
273 
274 			if (key == NULL || hostpath == NULL ||
275 			    (flen == 0 && partition == 0)) {
276 				fprintf(stderr, "incomplete drivespec\n");
277 				usage();
278 			}
279 			if (ftype == -1)
280 				ftype = RUMP_ETFS_BLK;
281 
282 			if (netfs - curetfs == 0) {
283 				etfs = realloc(etfs, (netfs+16)*sizeof(*etfs));
284 				if (etfs == NULL)
285 					die(1, errno, "realloc etfs");
286 				netfs += 16;
287 			}
288 
289 			etfs[curetfs].key = key;
290 			etfs[curetfs].hostpath = hostpath;
291 			etfs[curetfs].flen = flen;
292 			etfs[curetfs].foffset = foffset;
293 			etfs[curetfs].partition = partition;
294 			etfs[curetfs].type = ftype;
295 			curetfs++;
296 
297 			break;
298 		}
299 		case 'l':
300 			if (dlopen(optarg, RTLD_LAZY|RTLD_GLOBAL) == NULL) {
301 				char pb[MAXPATHLEN];
302 				/* try to mimic linker -l syntax */
303 
304 				snprintf(pb, sizeof(pb), "lib%s.so", optarg);
305 				if (dlopen(pb, RTLD_LAZY|RTLD_GLOBAL) == NULL) {
306 					die(1, 0, "dlopen lib");
307 				}
308 			}
309 			break;
310 #ifdef PLATFORM_HAS_NBMODULES
311 		case 'm': {
312 
313 			if (nmods - curmod == 0) {
314 				modarray = realloc(modarray,
315 				    (nmods+16) * sizeof(char *));
316 				if (modarray == NULL)
317 					die(1, errno, "realloc");
318 				nmods += 16;
319 			}
320 			modarray[curmod++] = optarg;
321 			break; }
322 #endif
323 		case 'r':
324 			setenv("RUMP_MEMLIMIT", optarg, 1);
325 			break;
326 		case 's':
327 			sflag = 1;
328 			break;
329 		case 'v':
330 			setenv("RUMP_VERBOSE", "1", 1);
331 			break;
332 		default:
333 			usage();
334 			/*NOTREACHED*/
335 		}
336 	}
337 
338 	argc -= optind;
339 	argv += optind;
340 
341 	if (argc != 1)
342 		usage();
343 
344 	serverurl = argv[0];
345 
346 	if (!sflag) {
347 		error = rump_daemonize_begin();
348 		if (error)
349 			die(1, error, "rump daemonize");
350 	}
351 
352 	error = rump_init();
353 	if (error)
354 		die(sflag, error, "rump init failed");
355 
356 #ifdef PLATFORM_HAS_NBMODULES
357 	/* load modules */
358 	for (i = 0; i < curmod; i++) {
359 		struct modctl_load ml;
360 
361 #define ETFSKEY "/module.mod"
362 		if ((error = rump_pub_etfs_register(ETFSKEY,
363 		    modarray[0], RUMP_ETFS_REG)) != 0)
364 			die(sflag, error, "module etfs register failed");
365 		memset(&ml, 0, sizeof(ml));
366 		ml.ml_filename = ETFSKEY;
367 		if (rump_sys_modctl(MODCTL_LOAD, &ml) == -1)
368 			die(sflag, errno, "module load failed");
369 		rump_pub_etfs_remove(ETFSKEY);
370 #undef ETFSKEY
371 	}
372 #endif /* PLATFORM_HAS_NBMODULES */
373 
374 	/* register host drives */
375 	for (i = 0; i < curetfs; i++) {
376 		struct stat sb;
377 		off_t foffset, flen, fendoff;
378 		int fd, oflags;
379 
380 		oflags = etfs[i].flen == DSIZE_E ? 0 : O_CREAT;
381 		fd = open(etfs[i].hostpath, O_RDWR | oflags, 0644);
382 		if (fd == -1)
383 			die(sflag, errno, "etfs hostpath open");
384 
385 #ifdef PLATFORM_HAS_DISKLABEL
386 		if (etfs[i].partition) {
387 			struct disklabel dl;
388 			char buf[1<<16];
389 			int partition = etfs[i].partition - 'a';
390 
391 			pread(fd, buf, sizeof(buf), 0);
392 			if (disklabel_scan(&dl, buf, sizeof(buf)))
393 				die(sflag, ENOENT, "disklabel not found");
394 
395 			if (partition >= dl.d_npartitions)
396 				die(sflag, ENOENT, "partition not available");
397 
398 			foffset = dl.d_partitions[partition].p_offset
399 			    << DEV_BSHIFT;
400 			flen = dl.d_partitions[partition].p_size
401 			    << DEV_BSHIFT;
402 		} else {
403 #else
404 		{
405 #endif
406 			foffset = etfs[i].foffset;
407 			flen = etfs[i].flen;
408 		}
409 
410 		if (fstat(fd, &sb) == -1)
411 			die(sflag, errno, "fstat etfs hostpath");
412 		if (flen == DSIZE_E) {
413 			if (sb.st_size == 0)
414 				die(sflag, EINVAL, "size=host, but cannot "
415 				    "query non-zero size");
416 			flen = sb.st_size;
417 		}
418 		fendoff = foffset + flen;
419 		if (S_ISREG(sb.st_mode) && sb.st_size < fendoff) {
420 			if (ftruncate(fd, fendoff) == -1)
421 				die(sflag, errno, "truncate");
422 		}
423 		close(fd);
424 
425 		if ((error = rump_pub_etfs_register_withsize(etfs[i].key,
426 		    etfs[i].hostpath, etfs[i].type, foffset, flen)) != 0)
427 			die(sflag, error, "etfs register");
428 	}
429 
430 	error = rump_init_server(serverurl);
431 	if (error)
432 		die(sflag, error, "rump server init failed");
433 
434 	if (!sflag)
435 		rump_daemonize_done(RUMP_DAEMONIZE_SUCCESS);
436 
437 	sem_init(&sigsem, 0, 0);
438 	signal(SIGTERM, sigreboot);
439 	signal(SIGINT, sigreboot);
440 	sem_wait(&sigsem);
441 
442 	rump_sys_reboot(0, NULL);
443 	/*NOTREACHED*/
444 
445 	return 0;
446 }
447