xref: /inferno-os/appl/wm/logon.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1implement WmLogon;
2#
3# Logon program for Wm environment
4#
5include "sys.m";
6	sys: Sys;
7
8include "draw.m";
9	draw: Draw;
10	Screen, Display, Image, Context, Point, Rect: import draw;
11	ctxt: ref Context;
12
13include "tk.m";
14	tk: Tk;
15
16include "tkclient.m";
17	tkclient: Tkclient;
18
19include "readdir.m";
20
21include "arg.m";
22include "sh.m";
23include "newns.m";
24include "keyring.m";
25include "security.m";
26
27WmLogon: module
28{
29	init:	fn(ctxt: ref Draw->Context, argv: list of string);
30};
31
32cfg := array[] of {
33	"label .p -bitmap @/icons/inferno.bit -borderwidth 2 -relief raised",
34	"frame .l -bg red",
35	"label .l.u -fg black -bg silver -text {User Name:} -anchor w",
36	"pack .l.u -fill x",
37	"frame .e",
38	"entry .e.u -bg white",
39	"pack .e.u -fill x",
40	"frame .f -borderwidth 2 -relief raised",
41	"pack .l .e -side left -in .f",
42	"pack .p .f -fill x",
43	"bind .e.u <Key-\n> {send cmd ok}",
44	"focus .e.u"
45};
46
47listcfg := array[] of {
48	"frame .f",
49	"listbox .f.lb -yscrollcommand {.f.sb set}",
50	"scrollbar .f.sb -orient vertical -command {.f.lb yview}",
51	"button .login -text {Login} -command {send cmd login}",
52	"pack .f.sb .f.lb -in .f -side left -fill both -expand 1",
53	"pack .f -side top -anchor center -fill y -expand 1",
54	"pack .login -side top",
55#	"pack propagate . 0",
56};
57
58init(actxt: ref Draw->Context, args: list of string)
59{
60	sys = load Sys Sys->PATH;
61	draw = load Draw Draw->PATH;
62	tk = load Tk Tk->PATH;
63	tkclient = load Tkclient Tkclient->PATH;
64	if(tkclient == nil){
65		sys->fprint(stderr(), "logon: cannot load %s: %r\n", Tkclient->PATH);
66		raise "fail:bad module";
67	}
68	sys->pctl(Sys->NEWPGRP|Sys->FORKFD, nil);
69	tkclient->init();
70	ctxt = actxt;
71
72	dolist := 0;
73	usr := "";
74	nsfile := "namespace";
75	arg := load Arg Arg->PATH;
76	if(arg != nil){
77		arg->init(args);
78		arg->setusage("logon [-l] [-n namespace] [-u user]");
79		while((opt := arg->opt()) != 0){
80			case opt{
81			'u' =>
82				usr = arg->earg();
83			'l' =>
84				dolist = 1;
85			'n' =>
86				nsfile = arg->earg();
87			* =>
88				arg->usage();
89			}
90		}
91		args = arg->argv();
92		arg = nil;
93	} else
94		args = nil;
95	if(ctxt == nil)
96		sys->fprint(stderr(), "logon: must run under a window manager\n");
97
98	(ctlwin, nil) := tkclient->toplevel(ctxt, nil, nil, Tkclient->Plain);
99	if(sys->fprint(ctlwin.ctxt.connfd, "request") == -1){
100		sys->fprint(stderr(), "logon: must be run as principal wm application\n");
101		raise "fail:lack of control";
102	}
103
104	if(dolist)
105		usr = chooseuser(ctxt);
106
107	if (usr == nil || !logon(usr)) {
108		(panel, cmd) := makepanel(ctxt, cfg);
109		stop := chan of int;
110		spawn tkclient->handler(panel, stop);
111		for(;;) {
112			tk->cmd(panel, "focus .e.u; update");
113			<-cmd;
114			usr = tk->cmd(panel, ".e.u get");
115			if(usr == "") {
116				notice("You must supply a user name to login");
117				continue;
118			}
119			if(logon(usr)) {
120				panel = nil;
121				stop <-= 1;
122				break;
123			}
124			tk->cmd(panel, ".e.u delete 0 end");
125		}
126	}
127	ok: int;
128	if(nsfile != nil){
129		(ok, nil) = sys->stat(nsfile);
130		if(ok < 0){
131			nsfile = nil;
132			(ok, nil) = sys->stat("namespace");
133		}
134	}else
135		(ok, nil) = sys->stat("namespace");
136	if(ok >= 0) {
137		ns := load Newns Newns->PATH;
138		if(ns == nil)
139			notice("failed to load namespace builder");
140		else if ((nserr := ns->newns(nil, nsfile)) != nil)
141			notice("namespace error:\n"+nserr);
142	}
143	tkclient->wmctl(ctlwin, "endcontrol");
144	errch := chan of string;
145	spawn exec(ctxt, args, errch);
146	err := <-errch;
147	if (err != nil) {
148		sys->fprint(stderr(), "logon: %s\n", err);
149		raise "fail:exec failed";
150	}
151}
152
153makepanel(ctxt: ref Draw->Context, cmds: array of string): (ref Tk->Toplevel, chan of string)
154{
155	(t, nil) := tkclient->toplevel(ctxt, "-bg silver", nil, Tkclient->Plain);
156
157	cmd := chan of string;
158	tk->namechan(t, cmd, "cmd");
159
160	for(i := 0; i < len cmds; i++)
161		tk->cmd(t, cmds[i]);
162	err := tk->cmd(t, "variable lasterr");
163	if(err != nil) {
164		sys->fprint(stderr(), "logon: tk error: %s\n", err);
165		raise "fail:config error";
166	}
167	tk->cmd(t, "update");
168	centre(t);
169	tkclient->startinput(t, "kbd" :: "ptr" :: nil);
170	tkclient->onscreen(t, "onscreen");
171	return (t, cmd);
172}
173
174exec(ctxt: ref Draw->Context, argv: list of string, errch: chan of string)
175{
176	sys->pctl(sys->NEWFD, 0 :: 1 :: 2 :: nil);
177	{
178		argv = "/dis/wm/toolbar.dis" :: nil;
179		cmd := load Command hd argv;
180		if (cmd == nil) {
181			errch <-= sys->sprint("cannot load %s: %r", hd argv);
182		} else {
183			errch <-= nil;
184			spawn cmd->init(ctxt, argv);
185		}
186	}exception{
187	"fail:*" =>
188		exit;
189	}
190}
191
192logon(user: string): int
193{
194	userdir := "/usr/"+user;
195	if(sys->chdir(userdir) < 0) {
196		notice("There is no home directory for \""+
197			user+"\"\nmounted on this machine");
198		return 0;
199	}
200
201	chmod("/chan", Sys->DMDIR|8r777);
202	chmod("/chan/wmrect", 8r666);
203	chmod("/chan/wmctl", 8r666);
204
205	#
206	# Set the user id
207	#
208	fd := sys->open("/dev/user", sys->OWRITE);
209	if(fd == nil) {
210		notice(sys->sprint("failed to open /dev/user: %r"));
211		return 0;
212	}
213	b := array of byte user;
214	if(sys->write(fd, b, len b) < 0) {
215		notice("failed to write /dev/user\nwith error "+sys->sprint("%r"));
216		return 0;
217	}
218
219	return 1;
220}
221
222chmod(file: string, mode: int): int
223{
224	d := sys->nulldir;
225	d.mode = mode;
226	if(sys->wstat(file, d) < 0){
227		notice(sys->sprint("failed to chmod %s: %r", file));
228		return -1;
229	}
230	return 0;
231}
232
233chooseuser(ctxt: ref Draw->Context): string
234{
235	(t, cmd) := makepanel(ctxt, listcfg);
236	usrlist := getusers();
237	if(usrlist == nil)
238		usrlist = "inferno" :: nil;
239	for(; usrlist != nil; usrlist = tl usrlist)
240		tkcmd(t, ".f.lb insert end '" + hd usrlist);
241	tkcmd(t, "update");
242	stop := chan of int;
243	spawn tkclient->handler(t, stop);
244	u := "";
245	for(;;){
246		<-cmd;
247		sel := tkcmd(t, ".f.lb curselection");
248		if(sel == nil)
249			continue;
250		u = tkcmd(t, ".f.lb get " + sel);
251		if(u != nil)
252			break;
253	}
254	stop <-= 1;
255	return u;
256}
257
258getusers(): list of string
259{
260	readdir := load Readdir Readdir->PATH;
261	if(readdir == nil)
262		return nil;
263	(dirs, nil) := readdir->init("/usr", Readdir->NAME);
264	n: list of string;
265	for (i := len dirs -1; i >=0; i--)
266		if (dirs[i].qid.qtype & Sys->QTDIR)
267			n = dirs[i].name :: n;
268	return n;
269}
270
271notecmd := array[] of {
272	"frame .f",
273	"label .f.l -bitmap error -foreground red",
274	"button .b -text Continue -command {send cmd done}",
275	"focus .f",
276	"bind .f <Key-\n> {send cmd done}",
277	"pack .f.l .f.m -side left -expand 1",
278	"pack .f .b",
279	"pack propagate . 0",
280};
281
282centre(t: ref Tk->Toplevel)
283{
284	org: Point;
285	ir := tk->rect(t, ".", Tk->Border|Tk->Required);
286	org.x = t.screenr.dx() / 2 - ir.dx() / 2;
287	org.y = t.screenr.dy() / 3 - ir.dy() / 2;
288#sys->print("ir: %d %d %d %d\n", ir.min.x, ir.min.y, ir.max.x, ir.max.y);
289	if (org.y < 0)
290		org.y = 0;
291	tk->cmd(t, ". configure -x " + string org.x + " -y " + string org.y);
292}
293
294notice(message: string)
295{
296	(t, nil) := tkclient->toplevel(ctxt, "-borderwidth 2 -relief raised", nil, Tkclient->Plain);
297	cmd := chan of string;
298	tk->namechan(t, cmd, "cmd");
299	tk->cmd(t, "label .f.m -anchor nw -text '"+message);
300	for(i := 0; i < len notecmd; i++)
301		tk->cmd(t, notecmd[i]);
302	centre(t);
303	tkclient->onscreen(t, "onscreen");
304	tkclient->startinput(t, "kbd"::"ptr"::nil);
305	stop := chan of int;
306	spawn tkclient->handler(t, stop);
307	tk->cmd(t, "update; cursor -default");
308	<-cmd;
309	stop <-= 1;
310}
311
312tkcmd(t: ref Tk->Toplevel, cmd: string): string
313{
314	s := tk->cmd(t, cmd);
315	if (s != nil && s[0] == '!') {
316		sys->print("%s\n", cmd);
317		sys->print("tk error: %s\n", s);
318	}
319	return s;
320}
321
322stderr(): ref Sys->FD
323{
324	return sys->fildes(2);
325}
326
327rf(path: string) : string
328{
329	fd := sys->open(path, sys->OREAD);
330	if(fd == nil)
331		return nil;
332
333	buf := array[512] of byte;
334	n := sys->read(fd, buf, len buf);
335	if(n <= 0)
336		return nil;
337
338	return string buf[0:n];
339}
340