xref: /netbsd-src/usr.bin/rump_allserver/rump_allserver.c (revision daf6c4152fcddc27c445489775ed1f66ab4ea9a9)
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