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