xref: /plan9/sys/src/cmd/auth/factotum/fgui.c (revision 9ef7517a50e613e265e62b53dd1110fd021da569)
1 #include "dat.h"
2 #include <draw.h>
3 #include <mouse.h>
4 #include <keyboard.h>
5 #include <control.h>
6 
7 int ctldeletequits = 1;
8 
9 typedef struct RequestType RequestType;
10 typedef struct Request Request;
11 typedef struct Memory Memory;
12 
13 struct RequestType
14 {
15 	char		*file;			/* file to read requests from */
16 	void		(*f)(Request*);		/* request handler */
17 	void		(*r)(Controlset*);	/* resize handler */
18 	int		fd;			/* fd = open(file, ORDWR) */
19 	Channel		*rc;			/* channel requests are multiplexed to */
20 	Controlset	*cs;
21 };
22 
23 struct Request
24 {
25 	RequestType	*rt;
26 	Attr		*a;
27 	Attr		*tag;
28 };
29 
30 struct Memory
31 {
32 	Memory	*next;
33 	Attr	*a;
34 	Attr	*val;
35 };
36 Memory *mem;
37 
38 static void	readreq(void*);
39 static void	hide(void);
40 static void	unhide(void);
41 static void	openkmr(void);
42 static void	closekmr(void);
43 static Memory*	searchmem(Attr*);
44 static void	addmem(Attr*, Attr*);
45 
46 static void	confirm(Request*);
47 static void	resizeconfirm(Controlset*);
48 static void	needkey(Request*);
49 static void	resizeneedkey(Controlset*);
50 
51 Control *b_remember;
52 Control *b_accept;
53 Control *b_refuse;
54 
55 RequestType rt[] =
56 {
57 	{ "/mnt/factotum/confirm",	confirm,	resizeconfirm, },
58 	{ "/mnt/factotum/needkey",	needkey,	resizeneedkey, },
59 	{ 0 },
60 };
61 
62 enum
63 {
64 	ButtonDim=	15,
65 };
66 
67 void
threadmain(int argc,char * argv[])68 threadmain(int argc, char *argv[])
69 {
70 	Request r;
71 	Channel *rc;
72 	RequestType *p;
73 	Font *invis;
74 
75 	ARGBEGIN{
76 	}ARGEND;
77 
78 	if(newwindow("-hide") < 0)
79 		sysfatal("newwindow: %r");
80 
81 	fmtinstall('A', _attrfmt);
82 
83 	/* create the proc's that read */
84 	rc = chancreate(sizeof(Request), 0);
85 	for(p = rt;  p->file != 0; p++){
86 		p->fd = -1;
87 		p->rc = rc;
88 		proccreate(readreq, p, 16*1024);
89 	}
90 
91 	/* gui initialization */
92 	if(initdraw(0, 0, "auth/fgui") < 0)
93 		sysfatal("initdraw failed: %r");
94 	initcontrols();
95 	hide();
96 
97 	/* get an invisible font for passwords */
98 	invis = openfont(display, "/lib/font/bit/lucm/passwd.9.font");
99 	if (invis == nil)
100 		sysfatal("fgui: %s: %r", "/lib/font/bit/lucm/passwd.9.font");
101 	namectlfont(invis, "invisible");
102 
103 	/* serialize all requests */
104 	for(;;){
105 		if(recv(rc, &r) < 0)
106 			break;
107 		(*r.rt->f)(&r);
108 		_freeattr(r.a);
109 		_freeattr(r.tag);
110 	}
111 
112 	threadexitsall(nil);
113 }
114 
115 /*
116  *  read requests and pass them to the main loop
117  */
118 enum
119 {
120 	Requestlen=4096,
121 };
122 static void
readreq(void * a)123 readreq(void *a)
124 {
125 	RequestType *rt = a;
126 	char *buf, *p;
127 	int n;
128 	Request r;
129 	Attr **l;
130 
131 	rt->fd = open(rt->file, ORDWR);
132 	if(rt->fd < 0)
133 		sysfatal("opening %s: %r", rt->file);
134 	rt->cs = nil;
135 
136 	buf = malloc(Requestlen);
137 	if(buf == nil)
138 		sysfatal("allocating read buffer: %r");
139 	r.rt = rt;
140 
141 	for(;;){
142 		n = read(rt->fd, buf, Requestlen-1);
143 		if(n < 0)
144 			break;
145 		buf[n] = 0;
146 
147 		/* skip verb, parse attributes, and remove tag */
148 		p = strchr(buf, ' ');
149 		if(p != nil)
150 			p++;
151 		else
152 			p = buf;
153 		r.a = _parseattr(p);
154 
155 		/* separate out the tag */
156 		r.tag = nil;
157 		for(l = &r.a; *l != nil; l = &(*l)->next)
158 			if(strcmp((*l)->name, "tag") == 0){
159 				r.tag = *l;
160 				*l = r.tag->next;
161 				r.tag->next = nil;
162 				break;
163 			}
164 
165 		/* if no tag, forget it */
166 		if(r.tag == nil){
167 			_freeattr(r.a);
168 			continue;
169 		}
170 
171 		send(rt->rc, &r);
172 	}
173 }
174 #ifdef asdf
175 static void
readreq(void * a)176 readreq(void *a)
177 {
178 	RequestType *rt = a;
179 	char *buf, *p;
180 	int n;
181 	Request r;
182 
183 	rt->fd = -1;
184 	rt->cs = nil;
185 
186 	buf = malloc(Requestlen);
187 	if(buf == nil)
188 		sysfatal("allocating read buffer: %r");
189 	r.rt = rt;
190 
191 	for(;;){
192 		strcpy(buf, "adfasdf=afdasdf asdfasdf=asdfasdf");
193 		r.a = _parseattr(buf);
194 		send(rt->rc, &r);
195 		sleep(5000);
196 	}
197 }
198 #endif asdf
199 
200 /*
201  *  open/close the keyboard, mouse and resize channels
202  */
203 static Channel *kbdc;
204 static Channel *mousec;
205 static Channel *resizec;
206 static Keyboardctl *kctl;
207 static Mousectl *mctl;
208 
209 static void
openkmr(void)210 openkmr(void)
211 {
212 	/* get channels for subsequent newcontrolset calls  */
213 	kctl = initkeyboard(nil);
214 	if(kctl == nil)
215 		sysfatal("can't initialize keyboard: %r");
216 	kbdc = kctl->c;
217 	mctl = initmouse(nil, screen);
218 	if(mctl == nil)
219 		sysfatal("can't initialize mouse: %r");
220 	mousec = mctl->c;
221 	resizec = mctl->resizec;
222 }
223 static void
closekmr(void)224 closekmr(void)
225 {
226 	Mouse m;
227 
228 	while(nbrecv(kbdc, &m) > 0)
229 		;
230 	closekeyboard(kctl);
231 	while(nbrecv(mousec, &m) > 0)
232 		;
233 	closemouse(mctl);
234 }
235 
236 
237 /*
238  *  called when the window is resized
239  */
240 void
resizecontrolset(Controlset * cs)241 resizecontrolset(Controlset *cs)
242 {
243 	RequestType *p;
244 
245 	for(p = rt;  p->file != 0; p++){
246 		if(p->cs == cs){
247 			(*p->r)(cs);
248 			break;
249 		}
250 	}
251 }
252 
253 /*
254  *  hide window when not in use
255  */
256 static void
unhide(void)257 unhide(void)
258 {
259 	int wctl;
260 
261 	wctl = open("/dev/wctl", OWRITE);
262 	if(wctl < 0)
263 		return;
264 	fprint(wctl, "unhide");
265 	close(wctl);
266 }
267 static void
hide(void)268 hide(void)
269 {
270 	int wctl;
271 	int tries;
272 
273 	wctl = open("/dev/wctl", OWRITE);
274 	if(wctl < 0)
275 		return;
276 	for(tries = 0; tries < 10; tries++){
277 		if(fprint(wctl, "hide") >= 0)
278 			break;
279 		sleep(100);
280 	}
281 	close(wctl);
282 }
283 
284 /*
285  *  set up the controls for the confirmation window
286  */
287 static Channel*
setupconfirm(Request * r)288 setupconfirm(Request *r)
289 {
290 	Controlset *cs;
291 	Channel *c;
292 	Attr *a;
293 
294 	/* create a new control set for the confirmation */
295 	openkmr();
296 	cs = newcontrolset(screen, kbdc, mousec, resizec);
297 
298 	createtext(cs, "msg");
299 	chanprint(cs->ctl, "msg image paleyellow");
300 	chanprint(cs->ctl, "msg border 1");
301 	chanprint(cs->ctl, "msg add 'The following key is being used:'");
302 	for(a = r->a; a != nil; a = a->next)
303 		chanprint(cs->ctl, "msg add '    %s = %s'", a->name,
304 				a->val);
305 
306 	namectlimage(display->white, "i_white");
307 	namectlimage(display->black, "i_black");
308 
309 	b_remember = createbutton(cs, "b_remember");
310 	chanprint(cs->ctl, "b_remember border 1");
311 	chanprint(cs->ctl, "b_remember mask i_white");
312 	chanprint(cs->ctl, "b_remember image i_white");
313 	chanprint(cs->ctl, "b_remember light i_black");
314 
315 	createtext(cs, "t_remember");
316 	chanprint(cs->ctl, "t_remember image white");
317 	chanprint(cs->ctl, "t_remember bordercolor white");
318 	chanprint(cs->ctl, "t_remember add 'Remember this answer for future confirmations'");
319 
320 	b_accept = createtextbutton(cs, "b_accept");
321 	chanprint(cs->ctl, "b_accept border 1");
322 	chanprint(cs->ctl, "b_accept align center");
323 	chanprint(cs->ctl, "b_accept text Accept");
324 	chanprint(cs->ctl, "b_accept image i_white");
325 	chanprint(cs->ctl, "b_accept light i_black");
326 
327 	b_refuse = createtextbutton(cs, "b_refuse");
328 	chanprint(cs->ctl, "b_refuse border 1");
329 	chanprint(cs->ctl, "b_refuse align center");
330 	chanprint(cs->ctl, "b_refuse text Refuse");
331 	chanprint(cs->ctl, "b_refuse image i_white");
332 	chanprint(cs->ctl, "b_refuse light i_black");
333 
334 	c = chancreate(sizeof(char*), 0);
335 	controlwire(b_remember, "event", c);
336 	controlwire(b_accept, "event", c);
337 	controlwire(b_refuse, "event", c);
338 
339 	/* make the controls interactive */
340 	activate(b_remember);
341 	activate(b_accept);
342 	activate(b_refuse);
343 	r->rt->cs = cs;
344 	unhide();
345 	resizecontrolset(cs);
346 
347 	return c;
348 }
349 
350 /*
351  *  resize the controls for the confirmation window
352  */
353 static void
resizeconfirm(Controlset * cs)354 resizeconfirm(Controlset *cs)
355 {
356 	Rectangle r, mr, nr, ntr, ar, rr;
357 	int fontwidth;
358 
359 	fontwidth = font->height;
360 
361 	/* get usable window rectangle */
362 	if(getwindow(display, Refnone) < 0)
363 		ctlerror("resize failed: %r");
364 	r = insetrect(screen->r, 10);
365 
366 	/* message box fills everything not needed for buttons */
367 	mr = r;
368 	mr.max.y = mr.min.y + font->height*((Dy(mr)-3*ButtonDim-font->height-4)/font->height);
369 
370 	/* remember button */
371 	nr.min = Pt(mr.min.x, mr.max.y+ButtonDim);
372 	nr.max = Pt(mr.max.x, r.max.y);
373 	if(Dx(nr) > ButtonDim)
374 		nr.max.x = nr.min.x+ButtonDim;
375 	if(Dy(nr) > ButtonDim)
376 		nr.max.y = nr.min.y+ButtonDim;
377 	ntr.min = Pt(nr.max.x+ButtonDim, nr.min.y);
378 	ntr.max = Pt(r.max.x, nr.min.y+font->height);
379 
380 	/* accept/refuse buttons */
381 	ar.min = Pt(r.min.x+Dx(r)/2-ButtonDim-6*fontwidth, nr.max.y+ButtonDim);
382 	ar.max = Pt(ar.min.x+6*fontwidth, ar.min.y+font->height+4);
383 	rr.min = Pt(r.min.x+Dx(r)/2+ButtonDim, nr.max.y+ButtonDim);
384 	rr.max = Pt(rr.min.x+6*fontwidth, rr.min.y+font->height+4);
385 
386 	/* make the controls visible */
387 	chanprint(cs->ctl, "msg rect %R\nmsg show", mr);
388 	chanprint(cs->ctl, "b_remember rect %R\nb_remember show", nr);
389 	chanprint(cs->ctl, "t_remember rect %R\nt_remember show", ntr);
390 	chanprint(cs->ctl, "b_accept rect %R\nb_accept show", ar);
391 	chanprint(cs->ctl, "b_refuse rect %R\nb_refuse show", rr);
392 }
393 
394 /*
395  *  free the controls for the confirmation window
396  */
397 static void
teardownconfirm(Request * r)398 teardownconfirm(Request *r)
399 {
400 	Controlset *cs;
401 
402 	cs = r->rt->cs;
403 	r->rt->cs = nil;
404 	hide();
405 	closecontrolset(cs);
406 	closekmr();
407 }
408 
409 /*
410  *  get user confirmation of a key
411  */
412 static void
confirm(Request * r)413 confirm(Request *r)
414 {
415 	Channel *c;
416 	char *s;
417 	int n;
418 	char *args[3];
419 	int remember;
420 	Attr *val;
421 	Memory *m;
422 
423 	/* if it's something that the user wanted us not to ask again about */
424 	m = searchmem(r->a);
425 	if(m != nil){
426 		fprint(r->rt->fd, "%A %A", r->tag, m->val);
427 		return;
428 	}
429 
430 	/* set up the controls */
431 	c = setupconfirm(r);
432 
433 	/* wait for user to reply */
434 	remember = 0;
435 	for(;;){
436 		s = recvp(c);
437 		n = tokenize(s, args, nelem(args));
438 		if(n==3 && strcmp(args[1], "value")==0){
439 			if(strcmp(args[0], "b_remember:") == 0){
440 				remember = atoi(args[2]);
441 			}
442 			if(strcmp(args[0], "b_accept:") == 0){
443 				val = _mkattr(AttrNameval, "answer", "yes", nil);
444 				free(s);
445 				break;
446 			}
447 			if(strcmp(args[0], "b_refuse:") == 0){
448 				val = _mkattr(AttrNameval, "answer", "no", nil);
449 				free(s);
450 				break;
451 			}
452 		}
453 		free(s);
454 	}
455 	teardownconfirm(r);
456 	fprint(r->rt->fd, "%A %A", r->tag, val);
457 	if(remember)
458 		addmem(_copyattr(r->a), val);
459 	else
460 		_freeattr(val);
461 }
462 
463 /*
464  *  confirmations that are remembered
465  */
466 static int
match(Attr * a,Attr * b)467 match(Attr *a, Attr *b)
468 {
469 	Attr *x;
470 
471 	for(; a != nil; a = a->next){
472 		x = _findattr(b, a->name);
473 		if(x == nil || strcmp(a->val, x->val) != 0)
474 			return 0;
475 	}
476 	return 1;
477 }
478 static Memory*
searchmem(Attr * a)479 searchmem(Attr *a)
480 {
481 	Memory *m;
482 
483 	for(m = mem; m != nil; m = m->next){
484 		if(match(a, m->a))
485 			break;
486 	}
487 	return m;
488 }
489 static void
addmem(Attr * a,Attr * val)490 addmem(Attr *a, Attr *val)
491 {
492 	Memory *m;
493 
494 	m = malloc(sizeof *m);
495 	if(m == nil)
496 		return;
497 	m->a = a;
498 	m->val = val;
499 	m->next = mem;
500 	mem = m;
501 }
502 
503 /* controls for needkey */
504 Control *msg;
505 Control *b_done;
506 enum {
507 	Pprivate=	1<<0,
508 	Pneed=		1<<1,
509 };
510 typedef struct Entry Entry;
511 struct Entry {
512 	Control *name;
513 	Control *val;
514 	Control *eq;
515 	Attr *a;
516 };
517 static Entry *entry;
518 static int entries;
519 
520 /*
521  *  set up the controls for the confirmation window
522  */
523 static Channel*
setupneedkey(Request * r)524 setupneedkey(Request *r)
525 {
526 	Controlset *cs;
527 	Channel *c;
528 	Attr *a;
529 	Attr **l;
530 	char cn[10];
531 	int i;
532 
533 	/* create a new control set for the confirmation */
534 	openkmr();
535 	cs = newcontrolset(screen, kbdc, mousec, resizec);
536 
537 	/* count attributes and allocate entry controls */
538 	entries = 0;
539 	for(l = &r->a; *l; l = &(*l)->next)
540 		entries++;
541 	if(entries == 0){
542 		closecontrolset(cs);
543 		closekmr();
544 		return nil;
545 	}
546 	*l = a = mallocz(sizeof *a, 1);
547 	a->type = AttrQuery;
548 	entries++;
549 	l = &(*l)->next;
550 	*l = a = mallocz(sizeof *a, 1);
551 	a->type = AttrQuery;
552 	entries++;
553 	entry = malloc(entries*sizeof(Entry));
554 	if(entry == nil){
555 		closecontrolset(cs);
556 		closekmr();
557 		return nil;
558 	}
559 
560 	namectlimage(display->white, "i_white");
561 	namectlimage(display->black, "i_black");
562 
563 	/* create controls */
564 	msg = createtext(cs, "msg");
565 	chanprint(cs->ctl, "msg image white");
566 	chanprint(cs->ctl, "msg bordercolor white");
567 	chanprint(cs->ctl, "msg add 'You need the following key.  Fill in the blanks'");
568 	chanprint(cs->ctl, "msg add 'and click on the DONE button.'");
569 
570 	for(i = 0, a = r->a; a != nil; i++, a = a->next){
571 		entry[i].a = a;
572 		snprint(cn, sizeof cn, "name_%d", i);
573 		if(entry[i].a->name == nil){
574 			entry[i].name = createentry(cs, cn);
575 			chanprint(cs->ctl, "%s image yellow", cn);
576 			chanprint(cs->ctl, "%s border 1", cn);
577 		} else {
578 			entry[i].name = createtext(cs, cn);
579 			chanprint(cs->ctl, "%s image white", cn);
580 			chanprint(cs->ctl, "%s bordercolor white", cn);
581 			chanprint(cs->ctl, "%s add '%s'", cn, a->name);
582 		}
583 
584 		snprint(cn, sizeof cn, "val_%d", i);
585 		if(a->type == AttrQuery){
586 			entry[i].val = createentry(cs, cn);
587 			chanprint(cs->ctl, "%s image yellow", cn);
588 			chanprint(cs->ctl, "%s border 1", cn);
589 			if(a->name != nil){
590 				if(strcmp(a->name, "user") == 0)
591 					chanprint(cs->ctl, "%s value %q", cn, getuser());
592 				if(*a->name == '!')
593 					chanprint(cs->ctl, "%s font invisible", cn);
594 			}
595 		} else {
596 			entry[i].val = createtext(cs, cn);
597 			chanprint(cs->ctl, "%s image white", cn);
598 			chanprint(cs->ctl, "%s add %q", cn, a->val);
599 		}
600 
601 		snprint(cn, sizeof cn, "eq_%d", i);
602 		entry[i].eq = createtext(cs, cn);
603 		chanprint(cs->ctl, "%s image white", cn);
604 		chanprint(cs->ctl, "%s add ' = '", cn);
605 	}
606 
607 	b_done = createtextbutton(cs, "b_done");
608 	chanprint(cs->ctl, "b_done border 1");
609 	chanprint(cs->ctl, "b_done align center");
610 	chanprint(cs->ctl, "b_done text DONE");
611 	chanprint(cs->ctl, "b_done image green");
612 	chanprint(cs->ctl, "b_done light green");
613 
614 	/* wire controls for input */
615 	c = chancreate(sizeof(char*), 0);
616 	controlwire(b_done, "event", c);
617 	for(i = 0; i < entries; i++)
618 		if(entry[i].a->type == AttrQuery)
619 			controlwire(entry[i].val, "event", c);
620 
621 	/* make the controls interactive */
622 	activate(msg);
623 	activate(b_done);
624 	for(i = 0; i < entries; i++){
625 		if(entry[i].a->type != AttrQuery)
626 			continue;
627 		if(entry[i].a->name == nil)
628 			activate(entry[i].name);
629 		activate(entry[i].val);
630 	}
631 
632 	/* change the display */
633 	r->rt->cs = cs;
634 	unhide();
635 	resizecontrolset(cs);
636 
637 	return c;
638 }
639 
640 /*
641  *  resize the controls for the confirmation window
642  */
643 static void
resizeneedkey(Controlset * cs)644 resizeneedkey(Controlset *cs)
645 {
646 	Rectangle r, mr;
647 	int mid, i, n, lasty;
648 
649 	/* get usable window rectangle */
650 	if(getwindow(display, Refnone) < 0)
651 		ctlerror("resize failed: %r");
652 	r = insetrect(screen->r, 10);
653 
654 	/* find largest name */
655 	mid = 0;
656 	for(i = 0; i < entries; i++){
657 		if(entry[i].a->name == nil)
658 			continue;
659 		n = strlen(entry[i].a->name);
660 		if(n > mid)
661 			mid = n;
662 	}
663 	mid = (mid+2) * font->height;
664 
665 	/* top line is the message */
666 	mr = r;
667 	mr.max.y = mr.min.y + 2*font->height + 2;
668 	chanprint(cs->ctl, "msg rect %R\nmsg show", mr);
669 
670 	/* one line per attribute */
671 	mr.min.x += 2*font->height;
672 	lasty = mr.max.y;
673 	for(i = 0; i < entries; i++){
674 		r.min.x = mr.min.x;
675 		r.min.y = lasty+2;
676 		r.max.x = r.min.x + mid - 3*stringwidth(font, "=");
677 		r.max.y = r.min.y + font->height;
678 		chanprint(cs->ctl, "name_%d rect %R\nname_%d show", i, r, i);
679 
680 		r.min.x = r.max.x;
681 		r.max.x = r.min.x + 3*stringwidth(font, "=");
682 		chanprint(cs->ctl, "eq_%d rect %R\neq_%d show", i, r, i);
683 
684 		r.min.x = r.max.x;
685 		r.max.x = mr.max.x;
686 		if(Dx(r) > 32*font->height)
687 			r.max.x = r.min.x + 32*font->height;
688 		chanprint(cs->ctl, "val_%d rect %R\nval_%d show", i, r, i);
689 		lasty = r.max.y;
690 	}
691 
692 	/* done button */
693 	mr.min.x -= 2*font->height;
694 	r.min.x = mr.min.x + mid - 3*font->height;
695 	r.min.y = lasty+10;
696 	r.max.x = r.min.x + 6*font->height;
697 	r.max.y = r.min.y + font->height + 2;
698 	chanprint(cs->ctl, "b_done rect %R\nb_done show", r);
699 }
700 
701 /*
702  *  free the controls for the confirmation window
703  */
704 static void
teardownneedkey(Request * r)705 teardownneedkey(Request *r)
706 {
707 	Controlset *cs;
708 
709 	cs = r->rt->cs;
710 	r->rt->cs = nil;
711 	hide();
712 	closecontrolset(cs);
713 	closekmr();
714 
715 	if(entry != nil)
716 		free(entry);
717 	entry = nil;
718 }
719 
720 static void
needkey(Request * r)721 needkey(Request *r)
722 {
723 	Channel *c;
724 	char *nam, *val;
725 	int i, n;
726 	int fd;
727 	char *args[3];
728 
729 	/* set up the controls */
730 	c = setupneedkey(r);
731 	if(c == nil)
732 		goto out;
733 
734 	/* wait for user to reply */
735 	for(;;){
736 		val = recvp(c);
737 		n = tokenize(val, args, nelem(args));
738 		if(n==3 && strcmp(args[1], "value")==0){	/* user hit 'enter' */
739 			free(val);
740 			break;
741 		}
742 		free(val);
743 	}
744 
745 	/* get entry values */
746 	for(i = 0; i < entries; i++){
747 		if(entry[i].a->type != AttrQuery)
748 			continue;
749 
750 		chanprint(r->rt->cs->ctl, "val_%d data", i);
751 		val = recvp(entry[i].val->data);
752 		if(entry[i].a->name == nil){
753 			chanprint(r->rt->cs->ctl, "name_%d data", i);
754 			nam = recvp(entry[i].name->data);
755 			if(nam == nil || *nam == 0){
756 				free(val);
757 				continue;
758 			}
759 			entry[i].a->val = estrdup(val);
760 			free(val);
761 			entry[i].a->name = estrdup(nam);
762 			free(nam);
763 		} else {
764 			if(val != nil){
765 				entry[i].a->val = estrdup(val);
766 				free(val);
767 			}
768 		}
769 		entry[i].a->type = AttrNameval;
770 	}
771 
772 	/* enter the new key !!!!need to do something in case of error!!!! */
773 	fd = open("/mnt/factotum/ctl", OWRITE);
774 	fprint(fd, "key %A", r->a);
775 	close(fd);
776 
777 	teardownneedkey(r);
778 out:
779 	fprint(r->rt->fd, "%A", r->tag);
780 }
781