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