xref: /plan9/sys/src/9/boot/boot.c (revision 217e9e83c7f9cc6fb27d97dda90c8339b6f98728)
1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include <fcall.h>
5 #include "../boot/boot.h"
6 
7 #define PARTSRV "partfs.sdXX"
8 
9 enum {
10 	Dontpost,
11 	Post,
12 };
13 
14 char	cputype[64];
15 char	sys[2*64];
16 char 	reply[256];
17 int	printcol;
18 int	mflag;
19 int	fflag;
20 int	kflag;
21 int	debugboot;
22 int	nousbboot;
23 
24 char	*bargv[Nbarg];
25 int	bargc;
26 
27 static void	swapproc(void);
28 static Method	*rootserver(char*);
29 static void	kbmap(void);
30 
31 /*
32  * we should inherit the standard fds all referring to /dev/cons,
33  * but we're being paranoid.
34  */
35 static void
opencons(void)36 opencons(void)
37 {
38 	close(0);
39 	close(1);
40 	close(2);
41 	bind("#c", "/dev", MBEFORE);
42 	open("/dev/cons", OREAD);
43 	open("/dev/cons", OWRITE);
44 	open("/dev/cons", OWRITE);
45 }
46 
47 /*
48  * init will reinitialize its namespace.
49  * #ec gets us plan9.ini settings (*var variables).
50  */
51 static void
bindenvsrv(void)52 bindenvsrv(void)
53 {
54 	bind("#ec", "/env", MREPL);
55 	bind("#e", "/env", MBEFORE|MCREATE);
56 	bind("#s", "/srv/", MREPL|MCREATE);
57 }
58 
59 static void
debuginit(int argc,char ** argv)60 debuginit(int argc, char **argv)
61 {
62 	int fd;
63 
64 	if(getenv("debugboot"))
65 		debugboot = 1;
66 	if(getenv("nousbboot"))
67 		nousbboot = 1;
68 #ifdef DEBUG
69 	print("argc=%d\n", argc);
70 	for(fd = 0; fd < argc; fd++)
71 		print("%#p %s ", argv[fd], argv[fd]);
72 	print("\n");
73 #endif	/* DEBUG */
74 	SET(fd);
75 	USED(argc, argv, fd);
76 }
77 
78 /*
79  * read disk partition tables here so that readnvram via factotum
80  * can see them.  ideally we would have this information in
81  * environment variables before attaching #S, which would then
82  * parse them and create partitions.
83  */
84 static void
partinit(void)85 partinit(void)
86 {
87 	char *rdparts;
88 
89 	rdparts = getenv("readparts");
90 	if(rdparts)
91 		readparts();
92 	free(rdparts);
93 }
94 
95 /*
96  *  pick a method and initialize it
97  */
98 static Method *
pickmethod(int argc,char ** argv)99 pickmethod(int argc, char **argv)
100 {
101 	Method *mp;
102 
103 	if(method[0].name == nil)
104 		fatal("no boot methods");
105 	mp = rootserver(argc ? *argv : 0);
106 	(*mp->config)(mp);
107 	return mp;
108 }
109 
110 /*
111  *  authentication agent
112  *  sets hostowner, creating an auth discontinuity
113  */
114 static void
doauth(int cpuflag)115 doauth(int cpuflag)
116 {
117 	dprint("auth...");
118 	authentication(cpuflag);
119 }
120 
121 /*
122  *  connect to the root file system
123  */
124 static int
connectroot(Method * mp,int islocal,int ishybrid)125 connectroot(Method *mp, int islocal, int ishybrid)
126 {
127 	int fd, n;
128 	char buf[32];
129 
130 	fd = (*mp->connect)();
131 	if(fd < 0)
132 		fatal("can't connect to file server");
133 	if(getenv("srvold9p"))
134 		fd = old9p(fd);
135 	if(!islocal && !ishybrid){
136 		if(cfs)
137 			fd = (*cfs)(fd);
138 	}
139 	print("version...");
140 	buf[0] = '\0';
141 	n = fversion(fd, 0, buf, sizeof buf);
142 	if(n < 0)
143 		fatal("can't init 9P");
144 	srvcreate("boot", fd);
145 	return fd;
146 }
147 
148 /*
149  *  create the name space, mount the root fs
150  */
151 static int
nsinit(int fd,char ** rspp)152 nsinit(int fd, char **rspp)
153 {
154 	int afd;
155 	char *rp, *rsp;
156 	AuthInfo *ai;
157 	static char rootbuf[64];
158 
159 	if(bind("/", "/", MREPL) < 0)
160 		fatal("bind /");
161 	rp = getenv("rootspec");
162 	if(rp == nil)
163 		rp = "";
164 
165 	afd = fauth(fd, rp);
166 	if(afd >= 0){
167 		ai = auth_proxy(afd, auth_getkey, "proto=p9any role=client");
168 		if(ai == nil)
169 			print("authentication failed (%r), trying mount anyways\n");
170 	}
171 	if(mount(fd, afd, "/root", MREPL|MCREATE, rp) < 0)
172 		fatal("mount /");
173 	rsp = rp;
174 	rp = getenv("rootdir");
175 	if(rp == nil)
176 		rp = rootdir;
177 	if(bind(rp, "/", MAFTER|MCREATE) < 0){
178 		if(strncmp(rp, "/root", 5) == 0){
179 			fprint(2, "boot: couldn't bind $rootdir=%s to root: %r\n", rp);
180 			fatal("second bind /");
181 		}
182 		snprint(rootbuf, sizeof rootbuf, "/root/%s", rp);
183 		rp = rootbuf;
184 		if(bind(rp, "/", MAFTER|MCREATE) < 0){
185 			fprint(2, "boot: couldn't bind $rootdir=%s to root: %r\n", rp);
186 			if(strcmp(rootbuf, "/root//plan9") != 0)
187 				fatal("second bind /");
188 			/* undo installer's work */
189 			fprint(2, "**** warning: remove rootdir=/plan9 "
190 				"entry from plan9.ini\n");
191 			rp = "/root";
192 			if(bind(rp, "/", MAFTER|MCREATE) < 0)
193 				fatal("second bind /");
194 		}
195 	}
196 	setenv("rootdir", rp);
197 	*rspp = rsp;
198 	return afd;
199 }
200 
201 static void
execinit(void)202 execinit(void)
203 {
204 	int iargc;
205 	char *cmd, cmdbuf[64], *iargv[16];
206 
207 	/* exec init */
208 	cmd = getenv("init");
209 	if(cmd == nil){
210 		sprint(cmdbuf, "/%s/init -%s%s", cputype,
211 			cpuflag ? "c" : "t", mflag ? "m" : "");
212 		cmd = cmdbuf;
213 	}
214 	iargc = tokenize(cmd, iargv, nelem(iargv)-1);
215 	cmd = iargv[0];
216 
217 	/* make iargv[0] basename(iargv[0]) */
218 	if(iargv[0] = strrchr(iargv[0], '/'))
219 		iargv[0]++;
220 	else
221 		iargv[0] = cmd;
222 
223 	iargv[iargc] = nil;
224 
225 	chmod("/srv/" PARTSRV, 0600);
226 	exec(cmd, iargv);
227 	fatal(cmd);
228 }
229 
230 void
boot(int argc,char * argv[])231 boot(int argc, char *argv[])
232 {
233 	int fd, afd, islocal, ishybrid;
234 	char *rsp;
235 	Method *mp;
236 
237 	fmtinstall('r', errfmt);
238 	opencons();
239 	bindenvsrv();
240 	debuginit(argc, argv);
241 
242 	ARGBEGIN{
243 	case 'k':
244 		kflag = 1;
245 		break;
246 	case 'm':
247 		mflag = 1;
248 		break;
249 	case 'f':
250 		fflag = 1;
251 		break;
252 	}ARGEND
253 
254 	readfile("#e/cputype", cputype, sizeof(cputype));
255 
256 	/*
257 	 *  set up usb keyboard & mouse, if any.
258 	 *  starts partfs on first disk, if any, to permit nvram on usb.
259 	 */
260 	if (!nousbboot)
261 		usbinit(Dontpost);
262 
263 	dprint("pickmethod...");
264 	mp = pickmethod(argc, argv);
265 	islocal = strcmp(mp->name, "local") == 0;
266 	ishybrid = strcmp(mp->name, "hybrid") == 0;
267 
268 	kbmap();			/*  load keymap if it's there. */
269 
270 	/* don't trigger aoe until the network has been configured */
271 	dprint("bind #æ...");
272 	bind("#æ", "/dev", MAFTER);	/* nvram could be here */
273 	dprint("bind #S...");
274 	bind("#S", "/dev", MAFTER);	/* nvram could be here */
275 	dprint("partinit...");
276 	partinit();
277 
278 	doauth(cpuflag);	/* authentication usually changes hostowner */
279 	rfork(RFNAMEG);		/* leave existing subprocs in own namespace */
280 	if (!nousbboot)
281 		usbinit(Post);	/* restart partfs under the new hostowner id */
282 	fd = connectroot(mp, islocal, ishybrid);
283 	afd = nsinit(fd, &rsp);
284 	close(fd);
285 
286 	settime(islocal, afd, rsp);
287 	if(afd > 0)
288 		close(afd);
289 	swapproc();
290 	execinit();
291 	exits("failed to exec init");
292 }
293 
294 static Method*
findmethod(char * a)295 findmethod(char *a)
296 {
297 	Method *mp;
298 	int i, j;
299 	char *cp;
300 
301 	if((i = strlen(a)) == 0)
302 		return nil;
303 	cp = strchr(a, '!');
304 	if(cp)
305 		i = cp - a;
306 	for(mp = method; mp->name; mp++){
307 		j = strlen(mp->name);
308 		if(j > i)
309 			j = i;
310 		if(strncmp(a, mp->name, j) == 0)
311 			break;
312 	}
313 	if(mp->name)
314 		return mp;
315 	return nil;
316 }
317 
318 /*
319  *  ask user from whence cometh the root file system
320  */
321 static Method*
rootserver(char * arg)322 rootserver(char *arg)
323 {
324 	char prompt[256];
325 	Method *mp;
326 	char *cp;
327 	int n;
328 
329 	/* look for required reply */
330 	dprint("read #e/nobootprompt...");
331 	readfile("#e/nobootprompt", reply, sizeof(reply));
332 	if(reply[0]){
333 		mp = findmethod(reply);
334 		if(mp)
335 			goto HaveMethod;
336 		print("boot method %s not found\n", reply);
337 		reply[0] = 0;
338 	}
339 
340 	/* make list of methods */
341 	mp = method;
342 	n = sprint(prompt, "root is from (%s", mp->name);
343 	for(mp++; mp->name; mp++)
344 		n += sprint(prompt+n, ", %s", mp->name);
345 	sprint(prompt+n, ")");
346 
347 	/* create default reply */
348 	dprint("read #e/bootargs...");
349 	readfile("#e/bootargs", reply, sizeof(reply));
350 	if(reply[0] == 0 && arg != 0)
351 		strcpy(reply, arg);
352 	if(reply[0]){
353 		mp = findmethod(reply);
354 		if(mp == 0)
355 			reply[0] = 0;
356 	}
357 	if(reply[0] == 0)
358 		strcpy(reply, method->name);
359 
360 	/* parse replies */
361 	do{
362 		dprint("outin...");
363 		outin(prompt, reply, sizeof(reply));
364 		mp = findmethod(reply);
365 	}while(mp == nil);
366 
367 HaveMethod:
368 	bargc = tokenize(reply, bargv, Nbarg-2);
369 	bargv[bargc] = nil;
370 	cp = strchr(reply, '!');
371 	if(cp)
372 		strcpy(sys, cp+1);
373 	dprint("pickmethod done\n");
374 	return mp;
375 }
376 
377 static void
swapproc(void)378 swapproc(void)
379 {
380 	int fd;
381 
382 	fd = open("#c/swap", OWRITE);
383 	if(fd < 0){
384 		warning("opening #c/swap");
385 		return;
386 	}
387 	if(write(fd, "start", 5) <= 0)
388 		warning("starting swap kproc");
389 	close(fd);
390 }
391 
392 int
old9p(int fd)393 old9p(int fd)
394 {
395 	int p[2];
396 
397 	if(pipe(p) < 0)
398 		fatal("pipe");
399 
400 	print("srvold9p...");
401 	switch(fork()) {
402 	case -1:
403 		fatal("rfork srvold9p");
404 	case 0:
405 		dup(fd, 1);
406 		close(fd);
407 		dup(p[0], 0);
408 		close(p[0]);
409 		close(p[1]);
410 		execl("/srvold9p", "srvold9p", "-s", 0);
411 		fatal("exec srvold9p");
412 	default:
413 		close(fd);
414 		close(p[0]);
415 	}
416 	return p[1];
417 }
418 
419 static void
kbmap(void)420 kbmap(void)
421 {
422 	char *f;
423 	int n, in, out;
424 	char buf[1024];
425 
426 	f = getenv("kbmap");
427 	if(f == nil)
428 		return;
429 	if(bind("#κ", "/dev", MAFTER) < 0){
430 		warning("can't bind #κ");
431 		return;
432 	}
433 
434 	in = open(f, OREAD);
435 	if(in < 0){
436 		warning("can't open kbd map");
437 		return;
438 	}
439 	out = open("/dev/kbmap", OWRITE);
440 	if(out < 0) {
441 		warning("can't open /dev/kbmap");
442 		close(in);
443 		return;
444 	}
445 	while((n = read(in, buf, sizeof(buf))) > 0)
446 		if(write(out, buf, n) != n){
447 			warning("write to /dev/kbmap failed");
448 			break;
449 		}
450 	close(in);
451 	close(out);
452 }
453