ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /os/init/init.b/
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);
}