code: purgatorio

ref: a920c765f2b4130590fb5971a50690b21664957a
dir: /os/init/init.b/

View raw version
implement Init;

include "sys.m";
sys: Sys;
FD, Connection, sprint, Dir: import sys;
print, fprint, open, bind, mount, dial, sleep, read: import sys;

include "draw.m";
draw: Draw;
Context, Display, Font, Rect, Point, Image, Screen: import draw;

include "prefab.m";
prefab: Prefab;
Environ, Element, Compound, Style: import prefab;

include "mpeg.m";

include "ir.m";
tirc: chan of int;	# translated remote input (from irslave)
irstopc: chan of int;	# channel to irslave

include "keyring.m";
kr: Keyring;
IPint: import kr;

Init: module
{
	init:	fn();
};

Shell: module
{
	init:	fn(ctxt: ref Context, argv: list of string);
};

Signon: con "Dialing Local Service Provider\nWait a moment ...";
Login:  con "Connected to Service Provider";
Intro:	con "/mpeg/youwill2";
Garden:	con "The Garden of Delights\nHieronymus Bosch";

rootfs(server: string): int
{
	ok, n: int;
	c: Connection;
	err: string;

	(ok, c) = dial("tcp!" + server + "!6666", nil);
	if(ok < 0)
		return -1;

	if(kr != nil){
		ai := kr->readauthinfo("/nvfs/default");
		if(ai == nil){
			(ai, err) = register(server);
			if(err != nil){
				status("registration failed: "+err+"\nPress a key on your remote\ncontrol to continue.");
				# register() may have failed before Ir loaded.
				if(tirc!=nil){
					<-tirc;
					irstopc <-= 1;
				}
			}
			statusbox = nil;
		}
		(id_or_err, secret) := kr->auth(c.dfd, ai, 0);
		if(secret == nil){
			status("authentication failed: "+err);
			sys->sleep(2000);
			statusbox = nil;
			(ai, err) = register(server);
			if(err != nil){
				status("registration failed: "+err+"\nPress a key on your remote\ncontrol to continue.");
				# register() may have failed before Ir loaded.
				if(tirc!=nil){
					<-tirc;
					irstopc <-= 1;
				}
			}
			statusbox = nil;
		} else {
			# no line encryption
			algbuf := array of byte "none";
			kr->sendmsg(c.dfd, algbuf, len algbuf);
		}
	}

	c.cfd = nil;
	n = mount(c.dfd, nil, "/", sys->MREPL, "");
	if(n > 0)
		return 0;
	return -1;
}

ones: ref Image;
screen: ref Screen;
menuenv, tvenv: ref Environ;
Bootpreadlen: con 128;
textfont: ref Font;
disp: ref Display;
env: ref Environ;
statusbox: ref Compound;

init()
{
	shell: Shell;
	nr, ntok: int;
	c: ref Compound;
	ls: list of string;
	le, te, xe: ref Element;
	spec: string;

	sys = load Sys Sys->PATH;
	draw = load Draw Draw->PATH;
	prefab = load Prefab Prefab->PATH;
	kr = load Keyring Keyring->PATH;
	
	disp = Display.allocate(nil);
	ones = disp.ones;

	textfont = Font.open(disp, "*default*");
	screencolor := disp.rgb(161, 195, 209);

	menustyle := ref Style(
			textfont,			# titlefont
			textfont,			# textfont
			disp.color(16r55),		# elemcolor
			disp.color(draw->Black),	# edgecolor
			disp.color(draw->Yellow),	# titlecolor	
			disp.color(draw->Black),	# textcolor
			disp.color(draw->White));	# highlightcolor

	screen = Screen.allocate(disp.image, screencolor, 0);
	screen.image.draw(screen.image.r, screencolor, ones, (0, 0));
	menuenv = ref Environ(screen, menustyle);

	logo := disp.open("/lucent");
	phone := disp.open("/phone");
	if(phone == nil  || logo == nil) {
		print("open: /phone or /lucent: %r\n");
		exit;
	}

	#
	# Setup what we need to call a server and
	# Authenticate
	#
	bind("#l", "/net", sys->MREPL);
	bind("#I", "/net", sys->MAFTER);
	bind("#c", "/dev", sys->MAFTER);
	bind("#H", "/dev", sys->MAFTER);
	nvramfd := sys->open("#H/hd0nvram", sys->ORDWR);
	if(nvramfd != nil){
		spec = sys->sprint("#Fhd0nvram", nvramfd.fd);
		if(bind(spec, "/nvfs", sys->MAFTER|sys->MCREATE) < 0)
			print("init: bind %s: %r\n", spec);
	}

	setsysname();	# set up system name

	fd := open("/net/ipifc", sys->OWRITE);
	if(fd == nil) {
		print("init: open /net/ipifc: %r");
		exit;
	}
	fprint(fd, "bootp /net/ether0");

	fd = open("/net/bootp", sys->OREAD);
	if(fd == nil) {
		print("init: open /net/bootp: %r");
		exit;
	}

	buf := array[Bootpreadlen] of byte;
	nr = read(fd, buf, len buf);
	fd = nil;
	if(nr <= 0) {
		print("init: read /net/bootp: %r");
		exit;
	}

	(ntok, ls) = sys->tokenize(string buf, " \t\n");
	while(ls != nil) {
		if(hd ls == "fsip"){
			ls = tl ls;
			break;
		}
		ls = tl ls;
	}
	if(ls == nil) {
		print("init: server address not in bootp read");
		exit;
	}

	zr := Rect((0,0), (0,0));

	le = Element.icon(menuenv, logo.r, logo, ones);
	le = Element.elist(menuenv, le, Prefab->EVertical);
	xe = Element.icon(menuenv, phone.r, phone, ones);
	xe = Element.elist(menuenv, xe, Prefab->EHorizontal);
	te = Element.text(menuenv, Signon, zr, Prefab->EText);
	xe.append(te);
	xe.adjust(Prefab->Adjpack, Prefab->Adjleft);
	le.append(xe);
	le.adjust(Prefab->Adjpack, Prefab->Adjup);
	c = Compound.box(menuenv, (150, 100),
	Element.text(menuenv, "Inferno", zr, Prefab->ETitle), le);
	c.draw();

	while(rootfs(hd ls) < 0)
		sleep(1000);

	#
	# default namespace
	#
	bind("#c", "/dev", sys->MBEFORE);		# console
	bind("#H", "/dev", sys->MAFTER);
	if(spec != nil)
		bind(spec, "/nvfs", sys->MBEFORE|sys->MCREATE);	# our keys
	bind("#E", "/dev", sys->MBEFORE);		# mpeg
	bind("#l", "/net", sys->MBEFORE);		# ethernet
	bind("#I", "/net", sys->MBEFORE);		# TCP/IP
	bind("#V", "/dev", sys->MAFTER);		# hauppauge TV
	bind("#p", "/prog", sys->MREPL);		# prog device
	sys->bind("#d", "/fd", Sys->MREPL);

	setclock();

	le = Element.icon(menuenv, logo.r, logo, ones);
	le = Element.elist(menuenv, le, Prefab->EVertical);
	xe = Element.text(menuenv, Login, zr, Prefab->EText);
	le.append(xe);

	i := disp.newimage(Rect((0, 0), (320, 240)), 3, 0, 0);
	i.draw(i.r, menustyle.elemcolor, ones, i.r.min);
	xe = Element.icon(menuenv, i.r, i, ones);
	le.append(xe);

	le.adjust(Prefab->Adjpack, Prefab->Adjup);
	c = Compound.box(menuenv, (160, 50),
	Element.text(menuenv, "Inferno", zr, Prefab->ETitle), le);
	c.draw();

	xc: chan of string;
	mpeg := load Mpeg Mpeg->PATH;
	if(mpeg != nil) {
		xc = chan of string;
		r := (hd tl tl c.contents.kids).r;
		s := mpeg->play(disp, c.image, 1, r, Intro, xc);
		if(s != "") {
			print("mpeg: %s\n", s);
			xc = nil;
		}
	}

	i2 := disp.open("/icons/delight.bit");
	i.draw(i.r, i2, ones, i2.r.min);
	i2 = nil;
	if(xc != nil)
		<-xc;

	le.append(Element.text(menuenv, Garden, le.r, Prefab->EText));
	le.adjust(Prefab->Adjpack, Prefab->Adjup);
	c = Compound.box(menuenv, (160, 50),
	Element.text(menuenv, "Inferno", zr, Prefab->ETitle), le);
	c.draw();

	sleep(5000);

	# Do a bind to force applications to use IR module built
	# into the kernel.
	if(bind("#/./ir", Ir->PATH, sys->MREPL) < 0)
		print("init: bind ir: %r\n");
	# Uncomment the next line to load sh.dis.
#	shell = load Shell "/dis/sh.dis";
	dc : ref Context;
	# Comment the next 2 lines to load sh.dis.
	shell = load Shell "/dis/mux/mux.dis";
	dc = ref Context(screen, disp, nil, nil, nil, nil, nil);
	if(shell == nil) {
		print("init: load /dis/sh.dis: %r");
		exit;
	}
	shell->init(dc, nil);
}

setclock()
{
	(ok, dir) := sys->stat("/");
	if (ok < 0) {
		print("init: stat /: %r");
		return;
	}

	fd := sys->open("/dev/time", sys->OWRITE);
	if (fd == nil) {
		print("init: open /dev/time: %r");
		return;
	}

	# Time is kept as microsecs, atime is in secs
	b := array of byte sprint("%d000000", dir.atime);
	if (sys->write(fd, b, len b) != len b)
		print("init: write /dev/time: %r");
}

register(signer: string): (ref Keyring->Authinfo, string)
{

	# get box id
	fd := sys->open("/nvfs/ID", sys->OREAD);
	if(fd == nil){
		fd = sys->create("/nvfs/ID", sys->OWRITE, 8r664);
		if(fd == nil)
			return  (nil, "can't create /nvfs/ID");
		if(sys->fprint(fd, "LT%d", randomint()) < 0)
			return  (nil, "can't write /nvfs/ID");
		fd = sys->open("/nvfs/ID", sys->OREAD);
	}
	if(fd == nil)
		return  (nil, "can't open /nvfs/ID");

	buf := array[64] of byte;
	n := sys->read(fd, buf, (len buf) - 1);
	if(n <= 0)
		return (nil, "can't read /nvfs/ID");

	boxid := string buf[0:n];
	fd = nil;
	buf = nil;

	# Set-up for user input via remote control.
	tirc = chan of int;
	irstopc = chan of int;
	spawn irslave(tirc, irstopc);
	case dialogue("Register with your service provider?", "yes\nno") {
	0 =>
		;
	* =>
		return (nil, "registration not desired");
	}

	# a holder
	info := ref Keyring->Authinfo;

	# contact signer
#	status("looking for signer");
#	signer := virgil->virgil("$SIGNER");
#	if(signer == nil)
#		return (nil, "can't find signer");
	status("dialing tcp!"+signer+"!6671");
	(ok, c) := sys->dial("tcp!"+signer+"!6671", nil);
	if(!ok)
		return (nil, "can't contact signer");

	# get signer's public key and diffie helman parameters
	status("getting signer's key");
	spkbuf := kr->getmsg(c.dfd);
	if(spkbuf == nil)
		return (nil, "can't read signer's key");
	info.spk = kr->strtopk(string spkbuf);
	if(info.spk == nil)
		return (nil, "bad key from signer");
	alphabuf := kr->getmsg(c.dfd);
	if(alphabuf == nil)
		return (nil, "can't read dh alpha");
	info.alpha = IPint.b64toip(string alphabuf);
	pbuf := kr->getmsg(c.dfd);
	if(pbuf == nil)
		return (nil, "can't read dh mod");
	info.p = IPint.b64toip(string pbuf);

	# generate our key from system parameters
	status("generating our key");
	info.mysk = kr->genSKfromPK(info.spk, boxid);
	if(info.mysk == nil)
		return (nil, "can't generate our own key");
	info.mypk = kr->sktopk(info.mysk);

	# send signer our public key
	mypkbuf := array of byte kr->pktostr(info.mypk);
	kr->sendmsg(c.dfd, mypkbuf, len mypkbuf);

	# get blind certificate
	status("getting blinded certificate");
	certbuf := kr->getmsg(c.dfd);
	if(certbuf == nil)
		return (nil, "can't read signed key");

	# verify we've got the right stuff
	if(!verify(boxid, spkbuf, mypkbuf, certbuf))
		return (nil, "verification failed, try again");

	# contact counter signer
	status("dialing tcp!"+signer+"!6672");
	(ok, c) = sys->dial("tcp!"+signer+"!6672", nil);
	if(!ok)
		return (nil, "can't contact countersigner");

	# send boxid
	buf = array of byte boxid;
	kr->sendmsg(c.dfd, buf, len buf);

	# get blinding mask
	status("unblinding certificate");
	mask := kr->getmsg(c.dfd);
	if(len mask != len certbuf)
		return (nil, "bad mask length");
	for(i := 0; i < len mask; i++)
		certbuf[i] = certbuf[i] ^ mask[i];
	info.cert = kr->strtocert(string certbuf);

	status("verifying certificate");
	state := kr->sha(mypkbuf, len mypkbuf, nil, nil);
	if(kr->verify(info.spk, info.cert, state) == 0)
		return (nil, "bad certificate");

	status("storing keys");
	kr->writeauthinfo("/nvfs/default", info);
	
	status("Congratulations, you are registered.\nPress a key to continue.");
	<-tirc;
	irstopc <-= 1;

	return (info, nil);
}

dialogue(expl: string, selection: string): int
{
	c := Compound.textbox(menuenv, ((100, 100), (100, 100)), expl, selection);
	c.draw();
	for(;;){
		(key, index, nil) := c.select(c.contents, 0, tirc);
		case key {
		Ir->Select =>
			return index;
		Ir->Enter =>
			return -1;
		}
	}
}

status(expl: string)
{
#	title := Element.text(menuenv, "registration\nstatus", ((0,0),(0,0)), Prefab->ETitle);
#	msg := Element.text(menuenv, expl, ((0,0),(0,0)), Prefab->EText);
#	c := Compound.box(menuenv, (100, 100), title, msg);

	c := Compound.textbox(menuenv, ((100, 100),(100,100)), "Registration status", expl);
	c.draw();
	statusbox = c;
}

pro:= array[] of {
	"alpha", "bravo", "charlie", "delta", "echo", "foxtrot", "golf",
	"hotel", "india", "juliet", "kilo", "lima", "mike", "nancy", "oscar",
	"poppa", "quebec", "romeo", "sierra", "tango", "uniform",
	"victor", "whiskey", "xray", "yankee", "zulu"
};

#
#  prompt for acceptance
#
verify(boxid: string, hispk, mypk, cert: array of byte): int
{
	s: string;

	# hash the string
	state := kr->md5(hispk, len hispk, nil, nil);
	kr->md5(mypk, len mypk, nil, state);
	digest := array[Keyring->MD5dlen] of byte;
	kr->md5(cert, len cert, digest, state);

	title := Element.elist(menuenv, nil, Prefab->EVertical);
	subtitle := Element.text(menuenv, "Telephone your service provider\n to register.  You will need\nthe following:\n", ((0,0),(0,0)), Prefab->ETitle);
	title.append(subtitle);

	line := Element.text(menuenv, "boxid is '"+boxid+"'.", ((0,0),(0,0)), Prefab->ETitle);
	title.append(line);
	for(i := 0; i < len digest; i++){
		line = Element.elist(menuenv, nil, Prefab->EHorizontal);
		s = (string (2*i)) + ": " + pro[((int digest[i])>>4)%len pro];
		line.append(Element.text(menuenv, s, ((0,0),(0,0)), Prefab->ETitle));

		s = (string (2*i+1)) + ": " + pro[(int digest[i])%len pro] + "\n";
		line.append(Element.text(menuenv, s, ((0,0),(200,0)), Prefab->ETitle));

		line.adjust(Prefab->Adjequal, Prefab->Adjleft);
		title.append(line);
	}
	title.adjust(Prefab->Adjpack, Prefab->Adjleft);

	le := Element.elist(menuenv, nil, Prefab->EHorizontal);
	le.append(Element.text(menuenv, " accept ", ((0, 0), (0, 0)), Prefab->EText));
	le.append(Element.text(menuenv, " reject ", ((0, 0), (0, 0)), Prefab->EText));
	le.adjust(Prefab->Adjpack, Prefab->Adjleft);

	c := Compound.box(menuenv, (50, 50), title, le);
	c.draw();

	for(;;){
		(key, index, nil) := c.select(c.contents, 0, tirc);
		case key {
		Ir->Select =>
			if(index == 0)
				return 1;
			return 0;
		Ir->Enter =>
			return 0;
		}
	}

	return 0;
}

randomint(): int
{
	fd := sys->open("/dev/random", sys->OREAD);
	if(fd == nil)
		return 0;
	buf := array[4] of byte;
	sys->read(fd, buf, 4);
	rand := 0;
	for(i := 0; i < 4; i++)
		rand = (rand<<8) | int buf[i];
	return rand;
}

# Reads real (if possible) or simulated remote, returns Ir events on irc.
# Must be a separate thread to be able to 1) read raw Ir input channel
# and 2) write translated Ir input data on output channel.
irslave(irc, stopc: chan of int)
{
	in, irpid: int;
	buf: list of int;
	outc: chan of int;

	irchan := chan of int;	# Untranslated Ir input channel.
	irpidch := chan of int;	# Ir reader pid channel.
	irmod := load Ir "#/./ir";	# Module built into kernel.

	if(irmod==nil){
		print("irslave: failed to load #/./ir");
		return;
	}
	if(irmod->init(irchan, irpidch)<0){
		print("irslave: failed to initialize ir");
		return;
	}
	irpid =<-irpidch;

	hdbuf := 0;
	dummy := chan of int;
	for(;;){
		if(buf == nil){
			outc = dummy;
		}else{
			outc = irc;
			hdbuf = hd buf;
		}
		alt{
		in = <-irchan =>
			buf = append(buf, in);
		outc <-= irmod->translate(hdbuf) =>
			buf = tl buf;
		<-stopc =>{
			killir(irpid);
			return;
			}
		}
	}
}

append(l: list of int, i: int): list of int
{
	if(l == nil)
		return i :: nil;
	return hd l :: append(tl l, i);
}

killir(irpid: int)
{
        pid := sys->sprint("%d", irpid);
        fd := sys->open("#p/"+pid+"/ctl", sys->OWRITE);
        if(fd==nil) {
                print("init: process %s: %r\n", pid);
                return;
        }
 
        msg := array of byte "kill";
        n := sys->write(fd, msg, len msg);
        if(n < 0) {
                print("init: message for %s: %r\n", pid);
                return;
        }
}

#
# Set system name from nvram
#
setsysname()
{
	fd := open("/nvfs/ID", sys->OREAD);
	if(fd == nil)
		return;
	fds := open("/dev/sysname", sys->OWRITE);
	if(fds == nil)
		return;
	buf := array[128] of byte;
	nr := sys->read(fd, buf, len buf);
	if(nr <= 0)
		return;
	sys->write(fds, buf, nr);
}