ref: d0ab3a0dd8f6355b3603d0fb04043a9ae867639b
dir: /appl/wm/logon.b/
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];
}