xref: /inferno-os/os/init/init.b (revision 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a)
1implement Init;
2
3include "sys.m";
4sys: Sys;
5FD, Connection, sprint, Dir: import sys;
6print, fprint, open, bind, mount, dial, sleep, read: import sys;
7
8include "draw.m";
9draw: Draw;
10Context, Display, Font, Rect, Point, Image, Screen: import draw;
11
12include "prefab.m";
13prefab: Prefab;
14Environ, Element, Compound, Style: import prefab;
15
16include "mpeg.m";
17
18include "ir.m";
19tirc: chan of int;	# translated remote input (from irslave)
20irstopc: chan of int;	# channel to irslave
21
22include "keyring.m";
23kr: Keyring;
24IPint: import kr;
25
26Init: module
27{
28	init:	fn();
29};
30
31Shell: module
32{
33	init:	fn(ctxt: ref Context, argv: list of string);
34};
35
36Signon: con "Dialing Local Service Provider\nWait a moment ...";
37Login:  con "Connected to Service Provider";
38Intro:	con "/mpeg/youwill2";
39Garden:	con "The Garden of Delights\nHieronymus Bosch";
40
41rootfs(server: string): int
42{
43	ok, n: int;
44	c: Connection;
45	err: string;
46
47	(ok, c) = dial("tcp!" + server + "!6666", nil);
48	if(ok < 0)
49		return -1;
50
51	if(kr != nil){
52		ai := kr->readauthinfo("/nvfs/default");
53		if(ai == nil){
54			(ai, err) = register(server);
55			if(err != nil){
56				status("registration failed: "+err+"\nPress a key on your remote\ncontrol to continue.");
57				# register() may have failed before Ir loaded.
58				if(tirc!=nil){
59					<-tirc;
60					irstopc <-= 1;
61				}
62			}
63			statusbox = nil;
64		}
65		(id_or_err, secret) := kr->auth(c.dfd, ai, 0);
66		if(secret == nil){
67			status("authentication failed: "+err);
68			sys->sleep(2000);
69			statusbox = nil;
70			(ai, err) = register(server);
71			if(err != nil){
72				status("registration failed: "+err+"\nPress a key on your remote\ncontrol to continue.");
73				# register() may have failed before Ir loaded.
74				if(tirc!=nil){
75					<-tirc;
76					irstopc <-= 1;
77				}
78			}
79			statusbox = nil;
80		} else {
81			# no line encryption
82			algbuf := array of byte "none";
83			kr->sendmsg(c.dfd, algbuf, len algbuf);
84		}
85	}
86
87	c.cfd = nil;
88	n = mount(c.dfd, nil, "/", sys->MREPL, "");
89	if(n > 0)
90		return 0;
91	return -1;
92}
93
94ones: ref Image;
95screen: ref Screen;
96menuenv, tvenv: ref Environ;
97Bootpreadlen: con 128;
98textfont: ref Font;
99disp: ref Display;
100env: ref Environ;
101statusbox: ref Compound;
102
103init()
104{
105	shell: Shell;
106	nr, ntok: int;
107	c: ref Compound;
108	ls: list of string;
109	le, te, xe: ref Element;
110	spec: string;
111
112	sys = load Sys Sys->PATH;
113	draw = load Draw Draw->PATH;
114	prefab = load Prefab Prefab->PATH;
115	kr = load Keyring Keyring->PATH;
116
117	disp = Display.allocate(nil);
118	ones = disp.ones;
119
120	textfont = Font.open(disp, "*default*");
121	screencolor := disp.rgb(161, 195, 209);
122
123	menustyle := ref Style(
124			textfont,			# titlefont
125			textfont,			# textfont
126			disp.color(16r55),		# elemcolor
127			disp.color(draw->Black),	# edgecolor
128			disp.color(draw->Yellow),	# titlecolor
129			disp.color(draw->Black),	# textcolor
130			disp.color(draw->White));	# highlightcolor
131
132	screen = Screen.allocate(disp.image, screencolor, 0);
133	screen.image.draw(screen.image.r, screencolor, ones, (0, 0));
134	menuenv = ref Environ(screen, menustyle);
135
136	logo := disp.open("/lucent");
137	phone := disp.open("/phone");
138	if(phone == nil  || logo == nil) {
139		print("open: /phone or /lucent: %r\n");
140		exit;
141	}
142
143	#
144	# Setup what we need to call a server and
145	# Authenticate
146	#
147	bind("#l", "/net", sys->MREPL);
148	bind("#I", "/net", sys->MAFTER);
149	bind("#c", "/dev", sys->MAFTER);
150	bind("#H", "/dev", sys->MAFTER);
151	nvramfd := sys->open("#H/hd0nvram", sys->ORDWR);
152	if(nvramfd != nil){
153		spec = sys->sprint("#Fhd0nvram", nvramfd.fd);
154		if(bind(spec, "/nvfs", sys->MAFTER|sys->MCREATE) < 0)
155			print("init: bind %s: %r\n", spec);
156	}
157
158	setsysname();	# set up system name
159
160	fd := open("/net/ipifc", sys->OWRITE);
161	if(fd == nil) {
162		print("init: open /net/ipifc: %r");
163		exit;
164	}
165	fprint(fd, "bootp /net/ether0");
166
167	fd = open("/net/bootp", sys->OREAD);
168	if(fd == nil) {
169		print("init: open /net/bootp: %r");
170		exit;
171	}
172
173	buf := array[Bootpreadlen] of byte;
174	nr = read(fd, buf, len buf);
175	fd = nil;
176	if(nr <= 0) {
177		print("init: read /net/bootp: %r");
178		exit;
179	}
180
181	(ntok, ls) = sys->tokenize(string buf, " \t\n");
182	while(ls != nil) {
183		if(hd ls == "fsip"){
184			ls = tl ls;
185			break;
186		}
187		ls = tl ls;
188	}
189	if(ls == nil) {
190		print("init: server address not in bootp read");
191		exit;
192	}
193
194	zr := Rect((0,0), (0,0));
195
196	le = Element.icon(menuenv, logo.r, logo, ones);
197	le = Element.elist(menuenv, le, Prefab->EVertical);
198	xe = Element.icon(menuenv, phone.r, phone, ones);
199	xe = Element.elist(menuenv, xe, Prefab->EHorizontal);
200	te = Element.text(menuenv, Signon, zr, Prefab->EText);
201	xe.append(te);
202	xe.adjust(Prefab->Adjpack, Prefab->Adjleft);
203	le.append(xe);
204	le.adjust(Prefab->Adjpack, Prefab->Adjup);
205	c = Compound.box(menuenv, (150, 100),
206	Element.text(menuenv, "Inferno", zr, Prefab->ETitle), le);
207	c.draw();
208
209	while(rootfs(hd ls) < 0)
210		sleep(1000);
211
212	#
213	# default namespace
214	#
215	bind("#c", "/dev", sys->MBEFORE);		# console
216	bind("#H", "/dev", sys->MAFTER);
217	if(spec != nil)
218		bind(spec, "/nvfs", sys->MBEFORE|sys->MCREATE);	# our keys
219	bind("#E", "/dev", sys->MBEFORE);		# mpeg
220	bind("#l", "/net", sys->MBEFORE);		# ethernet
221	bind("#I", "/net", sys->MBEFORE);		# TCP/IP
222	bind("#V", "/dev", sys->MAFTER);		# hauppauge TV
223	bind("#p", "/prog", sys->MREPL);		# prog device
224	sys->bind("#d", "/fd", Sys->MREPL);
225
226	setclock();
227
228	le = Element.icon(menuenv, logo.r, logo, ones);
229	le = Element.elist(menuenv, le, Prefab->EVertical);
230	xe = Element.text(menuenv, Login, zr, Prefab->EText);
231	le.append(xe);
232
233	i := disp.newimage(Rect((0, 0), (320, 240)), 3, 0, 0);
234	i.draw(i.r, menustyle.elemcolor, ones, i.r.min);
235	xe = Element.icon(menuenv, i.r, i, ones);
236	le.append(xe);
237
238	le.adjust(Prefab->Adjpack, Prefab->Adjup);
239	c = Compound.box(menuenv, (160, 50),
240	Element.text(menuenv, "Inferno", zr, Prefab->ETitle), le);
241	c.draw();
242
243	xc: chan of string;
244	mpeg := load Mpeg Mpeg->PATH;
245	if(mpeg != nil) {
246		xc = chan of string;
247		r := (hd tl tl c.contents.kids).r;
248		s := mpeg->play(disp, c.image, 1, r, Intro, xc);
249		if(s != "") {
250			print("mpeg: %s\n", s);
251			xc = nil;
252		}
253	}
254
255	i2 := disp.open("/icons/delight.bit");
256	i.draw(i.r, i2, ones, i2.r.min);
257	i2 = nil;
258	if(xc != nil)
259		<-xc;
260
261	le.append(Element.text(menuenv, Garden, le.r, Prefab->EText));
262	le.adjust(Prefab->Adjpack, Prefab->Adjup);
263	c = Compound.box(menuenv, (160, 50),
264	Element.text(menuenv, "Inferno", zr, Prefab->ETitle), le);
265	c.draw();
266
267	sleep(5000);
268
269	# Do a bind to force applications to use IR module built
270	# into the kernel.
271	if(bind("#/./ir", Ir->PATH, sys->MREPL) < 0)
272		print("init: bind ir: %r\n");
273	# Uncomment the next line to load sh.dis.
274#	shell = load Shell "/dis/sh.dis";
275	dc : ref Context;
276	# Comment the next 2 lines to load sh.dis.
277	shell = load Shell "/dis/mux/mux.dis";
278	dc = ref Context(screen, disp, nil, nil, nil, nil, nil);
279	if(shell == nil) {
280		print("init: load /dis/sh.dis: %r");
281		exit;
282	}
283	shell->init(dc, nil);
284}
285
286setclock()
287{
288	(ok, dir) := sys->stat("/");
289	if (ok < 0) {
290		print("init: stat /: %r");
291		return;
292	}
293
294	fd := sys->open("/dev/time", sys->OWRITE);
295	if (fd == nil) {
296		print("init: open /dev/time: %r");
297		return;
298	}
299
300	# Time is kept as microsecs, atime is in secs
301	b := array of byte sprint("%d000000", dir.atime);
302	if (sys->write(fd, b, len b) != len b)
303		print("init: write /dev/time: %r");
304}
305
306register(signer: string): (ref Keyring->Authinfo, string)
307{
308
309	# get box id
310	fd := sys->open("/nvfs/ID", sys->OREAD);
311	if(fd == nil){
312		fd = sys->create("/nvfs/ID", sys->OWRITE, 8r664);
313		if(fd == nil)
314			return  (nil, "can't create /nvfs/ID");
315		if(sys->fprint(fd, "LT%d", randomint()) < 0)
316			return  (nil, "can't write /nvfs/ID");
317		fd = sys->open("/nvfs/ID", sys->OREAD);
318	}
319	if(fd == nil)
320		return  (nil, "can't open /nvfs/ID");
321
322	buf := array[64] of byte;
323	n := sys->read(fd, buf, (len buf) - 1);
324	if(n <= 0)
325		return (nil, "can't read /nvfs/ID");
326
327	boxid := string buf[0:n];
328	fd = nil;
329	buf = nil;
330
331	# Set-up for user input via remote control.
332	tirc = chan of int;
333	irstopc = chan of int;
334	spawn irslave(tirc, irstopc);
335	case dialogue("Register with your service provider?", "yes\nno") {
336	0 =>
337		;
338	* =>
339		return (nil, "registration not desired");
340	}
341
342	# a holder
343	info := ref Keyring->Authinfo;
344
345	# contact signer
346#	status("looking for signer");
347#	signer := virgil->virgil("$SIGNER");
348#	if(signer == nil)
349#		return (nil, "can't find signer");
350	status("dialing tcp!"+signer+"!6671");
351	(ok, c) := sys->dial("tcp!"+signer+"!6671", nil);
352	if(!ok)
353		return (nil, "can't contact signer");
354
355	# get signer's public key and diffie helman parameters
356	status("getting signer's key");
357	spkbuf := kr->getmsg(c.dfd);
358	if(spkbuf == nil)
359		return (nil, "can't read signer's key");
360	info.spk = kr->strtopk(string spkbuf);
361	if(info.spk == nil)
362		return (nil, "bad key from signer");
363	alphabuf := kr->getmsg(c.dfd);
364	if(alphabuf == nil)
365		return (nil, "can't read dh alpha");
366	info.alpha = IPint.b64toip(string alphabuf);
367	pbuf := kr->getmsg(c.dfd);
368	if(pbuf == nil)
369		return (nil, "can't read dh mod");
370	info.p = IPint.b64toip(string pbuf);
371
372	# generate our key from system parameters
373	status("generating our key");
374	info.mysk = kr->genSKfromPK(info.spk, boxid);
375	if(info.mysk == nil)
376		return (nil, "can't generate our own key");
377	info.mypk = kr->sktopk(info.mysk);
378
379	# send signer our public key
380	mypkbuf := array of byte kr->pktostr(info.mypk);
381	kr->sendmsg(c.dfd, mypkbuf, len mypkbuf);
382
383	# get blind certificate
384	status("getting blinded certificate");
385	certbuf := kr->getmsg(c.dfd);
386	if(certbuf == nil)
387		return (nil, "can't read signed key");
388
389	# verify we've got the right stuff
390	if(!verify(boxid, spkbuf, mypkbuf, certbuf))
391		return (nil, "verification failed, try again");
392
393	# contact counter signer
394	status("dialing tcp!"+signer+"!6672");
395	(ok, c) = sys->dial("tcp!"+signer+"!6672", nil);
396	if(!ok)
397		return (nil, "can't contact countersigner");
398
399	# send boxid
400	buf = array of byte boxid;
401	kr->sendmsg(c.dfd, buf, len buf);
402
403	# get blinding mask
404	status("unblinding certificate");
405	mask := kr->getmsg(c.dfd);
406	if(len mask != len certbuf)
407		return (nil, "bad mask length");
408	for(i := 0; i < len mask; i++)
409		certbuf[i] = certbuf[i] ^ mask[i];
410	info.cert = kr->strtocert(string certbuf);
411
412	status("verifying certificate");
413	state := kr->sha(mypkbuf, len mypkbuf, nil, nil);
414	if(kr->verify(info.spk, info.cert, state) == 0)
415		return (nil, "bad certificate");
416
417	status("storing keys");
418	kr->writeauthinfo("/nvfs/default", info);
419
420	status("Congratulations, you are registered.\nPress a key to continue.");
421	<-tirc;
422	irstopc <-= 1;
423
424	return (info, nil);
425}
426
427dialogue(expl: string, selection: string): int
428{
429	c := Compound.textbox(menuenv, ((100, 100), (100, 100)), expl, selection);
430	c.draw();
431	for(;;){
432		(key, index, nil) := c.select(c.contents, 0, tirc);
433		case key {
434		Ir->Select =>
435			return index;
436		Ir->Enter =>
437			return -1;
438		}
439	}
440}
441
442status(expl: string)
443{
444#	title := Element.text(menuenv, "registration\nstatus", ((0,0),(0,0)), Prefab->ETitle);
445#	msg := Element.text(menuenv, expl, ((0,0),(0,0)), Prefab->EText);
446#	c := Compound.box(menuenv, (100, 100), title, msg);
447
448	c := Compound.textbox(menuenv, ((100, 100),(100,100)), "Registration status", expl);
449	c.draw();
450	statusbox = c;
451}
452
453pro:= array[] of {
454	"alpha", "bravo", "charlie", "delta", "echo", "foxtrot", "golf",
455	"hotel", "india", "juliet", "kilo", "lima", "mike", "nancy", "oscar",
456	"poppa", "quebec", "romeo", "sierra", "tango", "uniform",
457	"victor", "whiskey", "xray", "yankee", "zulu"
458};
459
460#
461#  prompt for acceptance
462#
463verify(boxid: string, hispk, mypk, cert: array of byte): int
464{
465	s: string;
466
467	# hash the string
468	state := kr->md5(hispk, len hispk, nil, nil);
469	kr->md5(mypk, len mypk, nil, state);
470	digest := array[Keyring->MD5dlen] of byte;
471	kr->md5(cert, len cert, digest, state);
472
473	title := Element.elist(menuenv, nil, Prefab->EVertical);
474	subtitle := Element.text(menuenv, "Telephone your service provider\n to register.  You will need\nthe following:\n", ((0,0),(0,0)), Prefab->ETitle);
475	title.append(subtitle);
476
477	line := Element.text(menuenv, "boxid is '"+boxid+"'.", ((0,0),(0,0)), Prefab->ETitle);
478	title.append(line);
479	for(i := 0; i < len digest; i++){
480		line = Element.elist(menuenv, nil, Prefab->EHorizontal);
481		s = (string (2*i)) + ": " + pro[((int digest[i])>>4)%len pro];
482		line.append(Element.text(menuenv, s, ((0,0),(0,0)), Prefab->ETitle));
483
484		s = (string (2*i+1)) + ": " + pro[(int digest[i])%len pro] + "\n";
485		line.append(Element.text(menuenv, s, ((0,0),(200,0)), Prefab->ETitle));
486
487		line.adjust(Prefab->Adjequal, Prefab->Adjleft);
488		title.append(line);
489	}
490	title.adjust(Prefab->Adjpack, Prefab->Adjleft);
491
492	le := Element.elist(menuenv, nil, Prefab->EHorizontal);
493	le.append(Element.text(menuenv, " accept ", ((0, 0), (0, 0)), Prefab->EText));
494	le.append(Element.text(menuenv, " reject ", ((0, 0), (0, 0)), Prefab->EText));
495	le.adjust(Prefab->Adjpack, Prefab->Adjleft);
496
497	c := Compound.box(menuenv, (50, 50), title, le);
498	c.draw();
499
500	for(;;){
501		(key, index, nil) := c.select(c.contents, 0, tirc);
502		case key {
503		Ir->Select =>
504			if(index == 0)
505				return 1;
506			return 0;
507		Ir->Enter =>
508			return 0;
509		}
510	}
511
512	return 0;
513}
514
515randomint(): int
516{
517	fd := sys->open("/dev/random", sys->OREAD);
518	if(fd == nil)
519		return 0;
520	buf := array[4] of byte;
521	sys->read(fd, buf, 4);
522	rand := 0;
523	for(i := 0; i < 4; i++)
524		rand = (rand<<8) | int buf[i];
525	return rand;
526}
527
528# Reads real (if possible) or simulated remote, returns Ir events on irc.
529# Must be a separate thread to be able to 1) read raw Ir input channel
530# and 2) write translated Ir input data on output channel.
531irslave(irc, stopc: chan of int)
532{
533	in, irpid: int;
534	buf: list of int;
535	outc: chan of int;
536
537	irchan := chan of int;	# Untranslated Ir input channel.
538	irpidch := chan of int;	# Ir reader pid channel.
539	irmod := load Ir "#/./ir";	# Module built into kernel.
540
541	if(irmod==nil){
542		print("irslave: failed to load #/./ir");
543		return;
544	}
545	if(irmod->init(irchan, irpidch)<0){
546		print("irslave: failed to initialize ir");
547		return;
548	}
549	irpid =<-irpidch;
550
551	hdbuf := 0;
552	dummy := chan of int;
553	for(;;){
554		if(buf == nil){
555			outc = dummy;
556		}else{
557			outc = irc;
558			hdbuf = hd buf;
559		}
560		alt{
561		in = <-irchan =>
562			buf = append(buf, in);
563		outc <-= irmod->translate(hdbuf) =>
564			buf = tl buf;
565		<-stopc =>{
566			killir(irpid);
567			return;
568			}
569		}
570	}
571}
572
573append(l: list of int, i: int): list of int
574{
575	if(l == nil)
576		return i :: nil;
577	return hd l :: append(tl l, i);
578}
579
580killir(irpid: int)
581{
582        pid := sys->sprint("%d", irpid);
583        fd := sys->open("#p/"+pid+"/ctl", sys->OWRITE);
584        if(fd==nil) {
585                print("init: process %s: %r\n", pid);
586                return;
587        }
588
589        msg := array of byte "kill";
590        n := sys->write(fd, msg, len msg);
591        if(n < 0) {
592                print("init: message for %s: %r\n", pid);
593                return;
594        }
595}
596
597#
598# Set system name from nvram
599#
600setsysname()
601{
602	fd := open("/nvfs/ID", sys->OREAD);
603	if(fd == nil)
604		return;
605	fds := open("/dev/sysname", sys->OWRITE);
606	if(fds == nil)
607		return;
608	buf := array[128] of byte;
609	nr := sys->read(fd, buf, len buf);
610	if(nr <= 0)
611		return;
612	sys->write(fds, buf, nr);
613}
614