xref: /plan9-contrib/sys/src/9/boot/boot.c (revision 906943f9f6b8411972abb5e3a03ed19f74be7ccc)
1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include <fcall.h>
5 #include "../boot/boot.h"
6 
7 char	cputype[64];
8 char	sys[2*64];
9 char 	reply[256];
10 int	printcol;
11 int	mflag;
12 int	fflag;
13 int	kflag;
14 
15 char	*bargv[Nbarg];
16 int	bargc;
17 
18 static void	swapproc(void);
19 static Method	*rootserver(char*);
20 static void	usbinit(void);
21 static void	kbmap(void);
22 
23 void
24 boot(int argc, char *argv[])
25 {
26 	int fd, afd;
27 	Method *mp;
28 	char *cmd, cmdbuf[64], *iargv[16];
29 	char rootbuf[64];
30 	int islocal, ishybrid;
31 	char *rp, *rsp;
32 	int iargc, n;
33 	char buf[32];
34 	AuthInfo *ai;
35 
36 	fmtinstall('r', errfmt);
37 
38 	bind("#c", "/dev", MBEFORE);
39 	open("/dev/cons", OREAD);
40 	open("/dev/cons", OWRITE);
41 	open("/dev/cons", OWRITE);
42 	/*
43 	 * init will reinitialize its namespace.
44 	 * #ec gets us plan9.ini settings (*var variables).
45 	 */
46 	bind("#ec", "/env", MREPL);
47 	bind("#e", "/env", MBEFORE|MCREATE);
48 	bind("#s", "/srv", MREPL|MCREATE);
49 #ifdef DEBUG
50 	print("argc=%d\n", argc);
51 	for(fd = 0; fd < argc; fd++)
52 		print("%lux %s ", argv[fd], argv[fd]);
53 	print("\n");
54 #endif DEBUG
55 
56 	ARGBEGIN{
57 	case 'k':
58 		kflag = 1;
59 		break;
60 	case 'm':
61 		mflag = 1;
62 		break;
63 	case 'f':
64 		fflag = 1;
65 		break;
66 	}ARGEND
67 
68 	readfile("#e/cputype", cputype, sizeof(cputype));
69 
70 	/*
71 	 *  set up usb keyboard, mouse and disk, if any.
72 	 */
73 	usbinit();
74 
75 	/*
76 	 *  pick a method and initialize it
77 	 */
78 	if(method[0].name == nil)
79 		fatal("no boot methods");
80 	mp = rootserver(argc ? *argv : 0);
81 	(*mp->config)(mp);
82 	islocal = strcmp(mp->name, "local") == 0;
83 	ishybrid = strcmp(mp->name, "hybrid") == 0;
84 
85 	/*
86 	 *  load keymap if it's there.
87 	 */
88 	kbmap();
89 
90 	/*
91  	 *  authentication agent
92 	 */
93 	authentication(cpuflag);
94 
95 	/*
96 	 *  connect to the root file system
97 	 */
98 	fd = (*mp->connect)();
99 	if(fd < 0)
100 		fatal("can't connect to file server");
101 	if(getenv("srvold9p"))
102 		fd = old9p(fd);
103 	if(!islocal && !ishybrid){
104 		if(cfs)
105 			fd = (*cfs)(fd);
106 	}
107 	print("version...");
108 	buf[0] = '\0';
109 	n = fversion(fd, 0, buf, sizeof buf);
110 	if(n < 0)
111 		fatal("can't init 9P");
112 	srvcreate("boot", fd);
113 
114 	/*
115 	 *  create the name space, mount the root fs
116 	 */
117 	if(bind("/", "/", MREPL) < 0)
118 		fatal("bind /");
119 	rp = getenv("rootspec");
120 	if(rp == nil)
121 		rp = "";
122 
123 	afd = fauth(fd, rp);
124 	if(afd >= 0){
125 		ai = auth_proxy(afd, auth_getkey, "proto=p9any role=client");
126 		if(ai == nil)
127 			print("authentication failed (%r), trying mount anyways\n");
128 	}
129 	if(mount(fd, afd, "/root", MREPL|MCREATE, rp) < 0)
130 		fatal("mount /");
131 	rsp = rp;
132 	rp = getenv("rootdir");
133 	if(rp == nil)
134 		rp = rootdir;
135 	if(bind(rp, "/", MAFTER|MCREATE) < 0){
136 		if(strncmp(rp, "/root", 5) == 0){
137 			fprint(2, "boot: couldn't bind $rootdir=%s to root: %r\n", rp);
138 			fatal("second bind /");
139 		}
140 		snprint(rootbuf, sizeof rootbuf, "/root/%s", rp);
141 		rp = rootbuf;
142 		if(bind(rp, "/", MAFTER|MCREATE) < 0){
143 			fprint(2, "boot: couldn't bind $rootdir=%s to root: %r\n", rp);
144 			if(strcmp(rootbuf, "/root//plan9") == 0){
145 				fprint(2, "**** warning: remove rootdir=/plan9 entry from plan9.ini\n");
146 				rp = "/root";
147 				if(bind(rp, "/", MAFTER|MCREATE) < 0)
148 					fatal("second bind /");
149 			}else
150 				fatal("second bind /");
151 		}
152 	}
153 	close(fd);
154 	setenv("rootdir", rp);
155 
156 	settime(islocal, afd, rsp);
157 	if(afd > 0)
158 		close(afd);
159 	swapproc();
160 
161 	cmd = getenv("init");
162 	if(cmd == nil){
163 		sprint(cmdbuf, "/%s/init -%s%s", cputype,
164 			cpuflag ? "c" : "t", mflag ? "m" : "");
165 		cmd = cmdbuf;
166 	}
167 	iargc = tokenize(cmd, iargv, nelem(iargv)-1);
168 	cmd = iargv[0];
169 
170 	/* make iargv[0] basename(iargv[0]) */
171 	if(iargv[0] = strrchr(iargv[0], '/'))
172 		iargv[0]++;
173 	else
174 		iargv[0] = cmd;
175 
176 	iargv[iargc] = nil;
177 
178 	exec(cmd, iargv);
179 	fatal(cmd);
180 }
181 
182 static Method*
183 findmethod(char *a)
184 {
185 	Method *mp;
186 	int i, j;
187 	char *cp;
188 
189 	if((i = strlen(a)) == 0)
190 		return nil;
191 	cp = strchr(a, '!');
192 	if(cp)
193 		i = cp - a;
194 	for(mp = method; mp->name; mp++){
195 		j = strlen(mp->name);
196 		if(j > i)
197 			j = i;
198 		if(strncmp(a, mp->name, j) == 0)
199 			break;
200 	}
201 	if(mp->name)
202 		return mp;
203 	return nil;
204 }
205 
206 /*
207  *  ask user from whence cometh the root file system
208  */
209 static Method*
210 rootserver(char *arg)
211 {
212 	char prompt[256];
213 	Method *mp;
214 	char *cp;
215 	int n;
216 
217 	/* look for required reply */
218 	readfile("#e/nobootprompt", reply, sizeof(reply));
219 	if(reply[0]){
220 		mp = findmethod(reply);
221 		if(mp)
222 			goto HaveMethod;
223 		print("boot method %s not found\n", reply);
224 		reply[0] = 0;
225 	}
226 
227 	/* make list of methods */
228 	mp = method;
229 	n = sprint(prompt, "root is from (%s", mp->name);
230 	for(mp++; mp->name; mp++)
231 		n += sprint(prompt+n, ", %s", mp->name);
232 	sprint(prompt+n, ")");
233 
234 	/* create default reply */
235 	readfile("#e/bootargs", reply, sizeof(reply));
236 	if(reply[0] == 0 && arg != 0)
237 		strcpy(reply, arg);
238 	if(reply[0]){
239 		mp = findmethod(reply);
240 		if(mp == 0)
241 			reply[0] = 0;
242 	}
243 	if(reply[0] == 0)
244 		strcpy(reply, method->name);
245 
246 	/* parse replies */
247 	do{
248 		outin(prompt, reply, sizeof(reply));
249 		mp = findmethod(reply);
250 	}while(mp == nil);
251 
252 HaveMethod:
253 	bargc = tokenize(reply, bargv, Nbarg-2);
254 	bargv[bargc] = nil;
255 	cp = strchr(reply, '!');
256 	if(cp)
257 		strcpy(sys, cp+1);
258 	return mp;
259 }
260 
261 static void
262 swapproc(void)
263 {
264 	int fd;
265 
266 	fd = open("#c/swap", OWRITE);
267 	if(fd < 0){
268 		warning("opening #c/swap");
269 		return;
270 	}
271 	if(write(fd, "start", 5) <= 0)
272 		warning("starting swap kproc");
273 	close(fd);
274 }
275 
276 int
277 old9p(int fd)
278 {
279 	int p[2];
280 
281 	if(pipe(p) < 0)
282 		fatal("pipe");
283 
284 	print("srvold9p...");
285 	switch(fork()) {
286 	case -1:
287 		fatal("rfork srvold9p");
288 	case 0:
289 		dup(fd, 1);
290 		close(fd);
291 		dup(p[0], 0);
292 		close(p[0]);
293 		close(p[1]);
294 		execl("/srvold9p", "srvold9p", "-s", 0);
295 		fatal("exec srvold9p");
296 	default:
297 		close(fd);
298 		close(p[0]);
299 	}
300 	return p[1];
301 }
302 
303 static void
304 run(char *prog, char **args)
305 {
306 	int i, pid;
307 
308 	if (access(args[0], AEXIST) < 0)
309 		return;			/* avoid error messages */
310 	print("%s...", prog);
311 	switch(pid = fork()){
312 	case -1:
313 		fatal("fork");
314 	case 0:
315 		exec(args[0], args);
316 		fatal(smprint("can't exec %s: %r", args[0]));
317 	}
318 	while ((i = waitpid()) != pid && i != -1)
319 		;
320 	if(i == -1)
321 		fatal(smprint("waitpid for %s failed", args[0]));
322 }
323 
324 static void
325 usbinit(void)
326 {
327 	static char *darg[] = { "/boot/usbd", nil };
328 
329 	if(bind("#u", "/dev", MAFTER) >= 0 && access("/dev/usb", 0) >= 0)
330 		run("usbd", darg);
331 }
332 
333 static void
334 kbmap(void)
335 {
336 	char *f;
337 	int n, in, out;
338 	char buf[1024];
339 
340 	f = getenv("kbmap");
341 	if(f == nil)
342 		return;
343 	if(bind("#κ", "/dev", MAFTER) < 0){
344 		warning("can't bind #κ");
345 		return;
346 	}
347 
348 	in = open(f, OREAD);
349 	if(in < 0){
350 		warning("can't open kbd map: %r");
351 		return;
352 	}
353 	out = open("/dev/kbmap", OWRITE);
354 	if(out < 0) {
355 		warning("can't open /dev/kbmap: %r");
356 		close(in);
357 		return;
358 	}
359 	while((n = read(in, buf, sizeof(buf))) > 0)
360 		if(write(out, buf, n) != n){
361 			warning("write to /dev/kbmap failed");
362 			break;
363 		}
364 	close(in);
365 	close(out);
366 }
367