implement WmLogon;## Logon program for Wm environment#include "sys.m"; sys: Sys; include "draw.m"; draw: Draw; Screen, Display, Image, Context, Point, Rect: import draw; ctxt: ref Context; include "tk.m"; tk: Tk; include "tkclient.m"; tkclient: Tkclient; include "readdir.m"; include "arg.m";include "sh.m";include "newns.m";include "keyring.m";include "security.m"; WmLogon: module{ init: fn(ctxt: ref Draw->Context, argv: list of string);}; cfg := array[] of { "label .p -bitmap @/icons/inferno.bit -borderwidth 2 -relief raised", "frame .l -bg red", "label .l.u -fg black -bg silver -text {User Name:} -anchor w", "pack .l.u -fill x", "frame .e", "entry .e.u -bg white", "pack .e.u -fill x", "frame .f -borderwidth 2 -relief raised", "pack .l .e -side left -in .f", "pack .p .f -fill x", "bind .e.u <Key-\n> {send cmd ok}", "focus .e.u"}; listcfg := array[] of { "frame .f", "listbox .f.lb -yscrollcommand {.f.sb set}", "scrollbar .f.sb -orient vertical -command {.f.lb yview}", "button .login -text {Login} -command {send cmd login}", "pack .f.sb .f.lb -in .f -side left -fill both -expand 1", "pack .f -side top -anchor center -fill y -expand 1", "pack .login -side top",# "pack propagate . 0",}; init(actxt: ref Draw->Context, args: list of string){ sys = load Sys Sys->PATH; draw = load Draw Draw->PATH; tk = load Tk Tk->PATH; tkclient = load Tkclient Tkclient->PATH; if(tkclient == nil){ sys->fprint(stderr(), "logon: cannot load %s: %r\n", Tkclient->PATH); raise "fail:bad module"; } sys->pctl(Sys->NEWPGRP|Sys->FORKFD, nil); tkclient->init(); ctxt = actxt; dolist := 0; usr := ""; nsfile := "namespace"; arg := load Arg Arg->PATH; if(arg != nil){ arg->init(args); arg->setusage("logon [-l] [-n namespace] [-u user]"); while((opt := arg->opt()) != 0){ case opt{ 'u' => usr = arg->earg(); 'l' => dolist = 1; 'n' => nsfile = arg->earg(); * => arg->usage(); } } args = arg->argv(); arg = nil; } else args = nil; if(ctxt == nil) sys->fprint(stderr(), "logon: must run under a window manager\n"); (ctlwin, nil) := tkclient->toplevel(ctxt, nil, nil, Tkclient->Plain); if(sys->fprint(ctlwin.ctxt.connfd, "request") == -1){ sys->fprint(stderr(), "logon: must be run as principal wm application\n"); raise "fail:lack of control"; } if(dolist) usr = chooseuser(ctxt); if (usr == nil || !logon(usr)) { (panel, cmd) := makepanel(ctxt, cfg); stop := chan of int; spawn tkclient->handler(panel, stop); for(;;) { tk->cmd(panel, "focus .e.u; update"); <-cmd; usr = tk->cmd(panel, ".e.u get"); if(usr == "") { notice("You must supply a user name to login"); continue; } if(logon(usr)) { panel = nil; stop <-= 1; break; } tk->cmd(panel, ".e.u delete 0 end"); } } ok: int; if(nsfile != nil){ (ok, nil) = sys->stat(nsfile); if(ok < 0){ nsfile = nil; (ok, nil) = sys->stat("namespace"); } }else (ok, nil) = sys->stat("namespace"); if(ok >= 0) { ns := load Newns Newns->PATH; if(ns == nil) notice("failed to load namespace builder"); else if ((nserr := ns->newns(nil, nsfile)) != nil) notice("namespace error:\n"+nserr); } tkclient->wmctl(ctlwin, "endcontrol"); errch := chan of string; spawn exec(ctxt, args, errch); err := <-errch; if (err != nil) { sys->fprint(stderr(), "logon: %s\n", err); raise "fail:exec failed"; }} makepanel(ctxt: ref Draw->Context, cmds: array of string): (ref Tk->Toplevel, chan of string){ (t, nil) := tkclient->toplevel(ctxt, "-bg silver", nil, Tkclient->Plain); cmd := chan of string; tk->namechan(t, cmd, "cmd"); for(i := 0; i < len cmds; i++) tk->cmd(t, cmds[i]); err := tk->cmd(t, "variable lasterr"); if(err != nil) { sys->fprint(stderr(), "logon: tk error: %s\n", err); raise "fail:config error"; } tk->cmd(t, "update"); centre(t); tkclient->startinput(t, "kbd" :: "ptr" :: nil); tkclient->onscreen(t, "onscreen"); return (t, cmd);} exec(ctxt: ref Draw->Context, argv: list of string, errch: chan of string){ sys->pctl(sys->NEWFD, 0 :: 1 :: 2 :: nil); { argv = "/dis/wm/toolbar.dis" :: nil; cmd := load Command hd argv; if (cmd == nil) { errch <-= sys->sprint("cannot load %s: %r", hd argv); } else { errch <-= nil; spawn cmd->init(ctxt, argv); } }exception{ "fail:*" => exit; }} logon(user: string): int{ userdir := "/usr/"+user; if(sys->chdir(userdir) < 0) { notice("There is no home directory for \""+ user+"\"\nmounted on this machine"); return 0; } chmod("/chan", Sys->DMDIR|8r777); chmod("/chan/wmrect", 8r666); chmod("/chan/wmctl", 8r666); # # Set the user id # fd := sys->open("/dev/user", sys->OWRITE); if(fd == nil) { notice(sys->sprint("failed to open /dev/user: %r")); return 0; } b := array of byte user; if(sys->write(fd, b, len b) < 0) { notice("failed to write /dev/user\nwith error "+sys->sprint("%r")); return 0; } return 1;} chmod(file: string, mode: int): int{ d := sys->nulldir; d.mode = mode; if(sys->wstat(file, d) < 0){ notice(sys->sprint("failed to chmod %s: %r", file)); return -1; } return 0;} chooseuser(ctxt: ref Draw->Context): string{ (t, cmd) := makepanel(ctxt, listcfg); usrlist := getusers(); if(usrlist == nil) usrlist = "inferno" :: nil; for(; usrlist != nil; usrlist = tl usrlist) tkcmd(t, ".f.lb insert end '" + hd usrlist); tkcmd(t, "update"); stop := chan of int; spawn tkclient->handler(t, stop); u := ""; for(;;){ <-cmd; sel := tkcmd(t, ".f.lb curselection"); if(sel == nil) continue; u = tkcmd(t, ".f.lb get " + sel); if(u != nil) break; } stop <-= 1; return u;} getusers(): list of string{ readdir := load Readdir Readdir->PATH; if(readdir == nil) return nil; (dirs, nil) := readdir->init("/usr", Readdir->NAME); n: list of string; for (i := len dirs -1; i >=0; i--) if (dirs[i].qid.qtype & Sys->QTDIR) n = dirs[i].name :: n; return n;} notecmd := array[] of { "frame .f", "label .f.l -bitmap error -foreground red", "button .b -text Continue -command {send cmd done}", "focus .f", "bind .f <Key-\n> {send cmd done}", "pack .f.l .f.m -side left -expand 1", "pack .f .b", "pack propagate . 0",}; centre(t: ref Tk->Toplevel){ org: Point; ir := tk->rect(t, ".", Tk->Border|Tk->Required); org.x = t.screenr.dx() / 2 - ir.dx() / 2; org.y = t.screenr.dy() / 3 - ir.dy() / 2;#sys->print("ir: %d %d %d %d\n", ir.min.x, ir.min.y, ir.max.x, ir.max.y); if (org.y < 0) org.y = 0; tk->cmd(t, ". configure -x " + string org.x + " -y " + string org.y);} notice(message: string){ (t, nil) := tkclient->toplevel(ctxt, "-borderwidth 2 -relief raised", nil, Tkclient->Plain); cmd := chan of string; tk->namechan(t, cmd, "cmd"); tk->cmd(t, "label .f.m -anchor nw -text '"+message); for(i := 0; i < len notecmd; i++) tk->cmd(t, notecmd[i]); centre(t); tkclient->onscreen(t, "onscreen"); tkclient->startinput(t, "kbd"::"ptr"::nil); stop := chan of int; spawn tkclient->handler(t, stop); tk->cmd(t, "update; cursor -default"); <-cmd; stop <-= 1;} tkcmd(t: ref Tk->Toplevel, cmd: string): string{ s := tk->cmd(t, cmd); if (s != nil && s[0] == '!') { sys->print("%s\n", cmd); sys->print("tk error: %s\n", s); } return s;} stderr(): ref Sys->FD{ return sys->fildes(2);} rf(path: string) : string{ fd := sys->open(path, sys->OREAD); if(fd == nil) return nil; buf := array[512] of byte; n := sys->read(fd, buf, len buf); if(n <= 0) return nil; return string buf[0:n];}
implement WmLogon;
#
# Logon program for Wm environment
#
include "sys.m";
sys: Sys;
include "draw.m";
draw: Draw;
Screen, Display, Image, Context, Point, Rect: import draw;
ctxt: ref Context;
include "tk.m";
tk: Tk;
include "tkclient.m";
tkclient: Tkclient;
include "readdir.m";
include "arg.m";
include "sh.m";
include "newns.m";
include "keyring.m";
include "security.m";
WmLogon: module
{
init: fn(ctxt: ref Draw->Context, argv: list of string);
};
cfg := array[] of {
"label .p -bitmap @/icons/inferno.bit -borderwidth 2 -relief raised",
"frame .l -bg red",
"label .l.u -fg black -bg silver -text {User Name:} -anchor w",
"pack .l.u -fill x",
"frame .e",
"entry .e.u -bg white",
"pack .e.u -fill x",
"frame .f -borderwidth 2 -relief raised",
"pack .l .e -side left -in .f",
"pack .p .f -fill x",
"bind .e.u <Key-\n> {send cmd ok}",
"focus .e.u"
};
listcfg := array[] of {
"frame .f",
"listbox .f.lb -yscrollcommand {.f.sb set}",
"scrollbar .f.sb -orient vertical -command {.f.lb yview}",
"button .login -text {Login} -command {send cmd login}",
"pack .f.sb .f.lb -in .f -side left -fill both -expand 1",
"pack .f -side top -anchor center -fill y -expand 1",
"pack .login -side top",
# "pack propagate . 0",
};
init(actxt: ref Draw->Context, args: list of string)
{
sys = load Sys Sys->PATH;
draw = load Draw Draw->PATH;
tk = load Tk Tk->PATH;
tkclient = load Tkclient Tkclient->PATH;
if(tkclient == nil){
sys->fprint(stderr(), "logon: cannot load %s: %r\n", Tkclient->PATH);
raise "fail:bad module";
}
sys->pctl(Sys->NEWPGRP|Sys->FORKFD, nil);
tkclient->init();
ctxt = actxt;
dolist := 0;
usr := "";
nsfile := "namespace";
arg := load Arg Arg->PATH;
if(arg != nil){
arg->init(args);
arg->setusage("logon [-l] [-n namespace] [-u user]");
while((opt := arg->opt()) != 0){
case opt{
'u' =>
usr = arg->earg();
'l' =>
dolist = 1;
'n' =>
nsfile = arg->earg();
* =>
arg->usage();
}
}
args = arg->argv();
arg = nil;
} else
args = nil;
if(ctxt == nil)
sys->fprint(stderr(), "logon: must run under a window manager\n");
(ctlwin, nil) := tkclient->toplevel(ctxt, nil, nil, Tkclient->Plain);
if(sys->fprint(ctlwin.ctxt.connfd, "request") == -1){
sys->fprint(stderr(), "logon: must be run as principal wm application\n");
raise "fail:lack of control";
}
if(dolist)
usr = chooseuser(ctxt);
if (usr == nil || !logon(usr)) {
(panel, cmd) := makepanel(ctxt, cfg);
stop := chan of int;
spawn tkclient->handler(panel, stop);
for(;;) {
tk->cmd(panel, "focus .e.u; update");
<-cmd;
usr = tk->cmd(panel, ".e.u get");
if(usr == "") {
notice("You must supply a user name to login");
continue;
}
if(logon(usr)) {
panel = nil;
stop <-= 1;
break;
}
tk->cmd(panel, ".e.u delete 0 end");
}
}
ok: int;
if(nsfile != nil){
(ok, nil) = sys->stat(nsfile);
if(ok < 0){
nsfile = nil;
(ok, nil) = sys->stat("namespace");
}
}else
(ok, nil) = sys->stat("namespace");
if(ok >= 0) {
ns := load Newns Newns->PATH;
if(ns == nil)
notice("failed to load namespace builder");
else if ((nserr := ns->newns(nil, nsfile)) != nil)
notice("namespace error:\n"+nserr);
}
tkclient->wmctl(ctlwin, "endcontrol");
errch := chan of string;
spawn exec(ctxt, args, errch);
err := <-errch;
if (err != nil) {
sys->fprint(stderr(), "logon: %s\n", err);
raise "fail:exec failed";
}
}
makepanel(ctxt: ref Draw->Context, cmds: array of string): (ref Tk->Toplevel, chan of string)
{
(t, nil) := tkclient->toplevel(ctxt, "-bg silver", nil, Tkclient->Plain);
cmd := chan of string;
tk->namechan(t, cmd, "cmd");
for(i := 0; i < len cmds; i++)
tk->cmd(t, cmds[i]);
err := tk->cmd(t, "variable lasterr");
if(err != nil) {
sys->fprint(stderr(), "logon: tk error: %s\n", err);
raise "fail:config error";
}
tk->cmd(t, "update");
centre(t);
tkclient->startinput(t, "kbd" :: "ptr" :: nil);
tkclient->onscreen(t, "onscreen");
return (t, cmd);
}
exec(ctxt: ref Draw->Context, argv: list of string, errch: chan of string)
{
sys->pctl(sys->NEWFD, 0 :: 1 :: 2 :: nil);
{
argv = "/dis/wm/toolbar.dis" :: nil;
cmd := load Command hd argv;
if (cmd == nil) {
errch <-= sys->sprint("cannot load %s: %r", hd argv);
} else {
errch <-= nil;
spawn cmd->init(ctxt, argv);
}
}exception{
"fail:*" =>
exit;
}
}
logon(user: string): int
{
userdir := "/usr/"+user;
if(sys->chdir(userdir) < 0) {
notice("There is no home directory for \""+
user+"\"\nmounted on this machine");
return 0;
}
chmod("/chan", Sys->DMDIR|8r777);
chmod("/chan/wmrect", 8r666);
chmod("/chan/wmctl", 8r666);
#
# Set the user id
#
fd := sys->open("/dev/user", sys->OWRITE);
if(fd == nil) {
notice(sys->sprint("failed to open /dev/user: %r"));
return 0;
}
b := array of byte user;
if(sys->write(fd, b, len b) < 0) {
notice("failed to write /dev/user\nwith error "+sys->sprint("%r"));
return 0;
}
return 1;
}
chmod(file: string, mode: int): int
{
d := sys->nulldir;
d.mode = mode;
if(sys->wstat(file, d) < 0){
notice(sys->sprint("failed to chmod %s: %r", file));
return -1;
}
return 0;
}
chooseuser(ctxt: ref Draw->Context): string
{
(t, cmd) := makepanel(ctxt, listcfg);
usrlist := getusers();
if(usrlist == nil)
usrlist = "inferno" :: nil;
for(; usrlist != nil; usrlist = tl usrlist)
tkcmd(t, ".f.lb insert end '" + hd usrlist);
tkcmd(t, "update");
stop := chan of int;
spawn tkclient->handler(t, stop);
u := "";
for(;;){
<-cmd;
sel := tkcmd(t, ".f.lb curselection");
if(sel == nil)
continue;
u = tkcmd(t, ".f.lb get " + sel);
if(u != nil)
break;
}
stop <-= 1;
return u;
}
getusers(): list of string
{
readdir := load Readdir Readdir->PATH;
if(readdir == nil)
return nil;
(dirs, nil) := readdir->init("/usr", Readdir->NAME);
n: list of string;
for (i := len dirs -1; i >=0; i--)
if (dirs[i].qid.qtype & Sys->QTDIR)
n = dirs[i].name :: n;
return n;
}
notecmd := array[] of {
"frame .f",
"label .f.l -bitmap error -foreground red",
"button .b -text Continue -command {send cmd done}",
"focus .f",
"bind .f <Key-\n> {send cmd done}",
"pack .f.l .f.m -side left -expand 1",
"pack .f .b",
"pack propagate . 0",
};
centre(t: ref Tk->Toplevel)
{
org: Point;
ir := tk->rect(t, ".", Tk->Border|Tk->Required);
org.x = t.screenr.dx() / 2 - ir.dx() / 2;
org.y = t.screenr.dy() / 3 - ir.dy() / 2;
#sys->print("ir: %d %d %d %d\n", ir.min.x, ir.min.y, ir.max.x, ir.max.y);
if (org.y < 0)
org.y = 0;
tk->cmd(t, ". configure -x " + string org.x + " -y " + string org.y);
}
notice(message: string)
{
(t, nil) := tkclient->toplevel(ctxt, "-borderwidth 2 -relief raised", nil, Tkclient->Plain);
cmd := chan of string;
tk->namechan(t, cmd, "cmd");
tk->cmd(t, "label .f.m -anchor nw -text '"+message);
for(i := 0; i < len notecmd; i++)
tk->cmd(t, notecmd[i]);
centre(t);
tkclient->onscreen(t, "onscreen");
tkclient->startinput(t, "kbd"::"ptr"::nil);
stop := chan of int;
spawn tkclient->handler(t, stop);
tk->cmd(t, "update; cursor -default");
<-cmd;
stop <-= 1;
}
tkcmd(t: ref Tk->Toplevel, cmd: string): string
{
s := tk->cmd(t, cmd);
if (s != nil && s[0] == '!') {
sys->print("%s\n", cmd);
sys->print("tk error: %s\n", s);
}
return s;
}
stderr(): ref Sys->FD
{
return sys->fildes(2);
}
rf(path: string) : string
{
fd := sys->open(path, sys->OREAD);
if(fd == nil)
return nil;
buf := array[512] of byte;
n := sys->read(fd, buf, len buf);
if(n <= 0)
return nil;
return string buf[0:n];
}