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 {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 {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]; }