xref: /plan9/sys/src/9/port/devproc.c (revision 6d2133e0d4be943e9ce2e416f00a3b046838334e)
1 #include	"u.h"
2 #include	<trace.h>
3 #include	"tos.h"
4 #include	"../port/lib.h"
5 #include	"mem.h"
6 #include	"dat.h"
7 #include	"fns.h"
8 #include	"../port/error.h"
9 #include	"ureg.h"
10 #include	"edf.h"
11 
12 enum
13 {
14 	Qdir,
15 	Qtrace,
16 	Qargs,
17 	Qctl,
18 	Qfd,
19 	Qfpregs,
20 	Qkregs,
21 	Qmem,
22 	Qnote,
23 	Qnoteid,
24 	Qnotepg,
25 	Qns,
26 	Qproc,
27 	Qregs,
28 	Qsegment,
29 	Qstatus,
30 	Qtext,
31 	Qwait,
32 	Qprofile,
33 	Qsyscall,
34 };
35 
36 enum
37 {
38 	CMclose,
39 	CMclosefiles,
40 	CMfixedpri,
41 	CMhang,
42 	CMkill,
43 	CMnohang,
44 	CMnoswap,
45 	CMpri,
46 	CMprivate,
47 	CMprofile,
48 	CMstart,
49 	CMstartstop,
50 	CMstartsyscall,
51 	CMstop,
52 	CMwaitstop,
53 	CMwired,
54 	CMtrace,
55 	/* real time */
56 	CMperiod,
57 	CMdeadline,
58 	CMcost,
59 	CMsporadic,
60 	CMdeadlinenotes,
61 	CMadmit,
62 	CMextra,
63 	CMexpel,
64 	CMevent,
65 };
66 
67 enum{
68 	Nevents = 0x4000,
69 	Emask = Nevents - 1,
70 };
71 
72 #define	STATSIZE	(2*KNAMELEN+12+9*12)
73 /*
74  * Status, fd, and ns are left fully readable (0444) because of their use in debugging,
75  * particularly on shared servers.
76  * Arguably, ns and fd shouldn't be readable; if you'd prefer, change them to 0000
77  */
78 Dirtab procdir[] =
79 {
80 	"args",		{Qargs},	0,			0660,
81 	"ctl",		{Qctl},		0,			0000,
82 	"fd",		{Qfd},		0,			0444,
83 	"fpregs",	{Qfpregs},	sizeof(FPsave),		0000,
84 	"kregs",	{Qkregs},	sizeof(Ureg),		0400,
85 	"mem",		{Qmem},		0,			0000,
86 	"note",		{Qnote},	0,			0000,
87 	"noteid",	{Qnoteid},	0,			0664,
88 	"notepg",	{Qnotepg},	0,			0000,
89 	"ns",		{Qns},		0,			0444,
90 	"proc",		{Qproc},	0,			0400,
91 	"regs",		{Qregs},	sizeof(Ureg),		0000,
92 	"segment",	{Qsegment},	0,			0444,
93 	"status",	{Qstatus},	STATSIZE,		0444,
94 	"text",		{Qtext},	0,			0000,
95 	"wait",		{Qwait},	0,			0400,
96 	"profile",	{Qprofile},	0,			0400,
97 	"syscall",	{Qsyscall},	0,			0400,
98 };
99 
100 static
101 Cmdtab proccmd[] = {
102 	CMclose,		"close",		2,
103 	CMclosefiles,		"closefiles",		1,
104 	CMfixedpri,		"fixedpri",		2,
105 	CMhang,			"hang",			1,
106 	CMnohang,		"nohang",		1,
107 	CMnoswap,		"noswap",		1,
108 	CMkill,			"kill",			1,
109 	CMpri,			"pri",			2,
110 	CMprivate,		"private",		1,
111 	CMprofile,		"profile",		1,
112 	CMstart,		"start",		1,
113 	CMstartstop,		"startstop",		1,
114 	CMstartsyscall,		"startsyscall",		1,
115 	CMstop,			"stop",			1,
116 	CMwaitstop,		"waitstop",		1,
117 	CMwired,		"wired",		2,
118 	CMtrace,		"trace",		0,
119 	CMperiod,		"period",		2,
120 	CMdeadline,		"deadline",		2,
121 	CMcost,			"cost",			2,
122 	CMsporadic,		"sporadic",		1,
123 	CMdeadlinenotes,	"deadlinenotes",	1,
124 	CMadmit,		"admit",		1,
125 	CMextra,		"extra",		1,
126 	CMexpel,		"expel",		1,
127 	CMevent,		"event",		1,
128 };
129 
130 /* Segment type from portdat.h */
131 static char *sname[]={ "Text", "Data", "Bss", "Stack", "Shared", "Phys", };
132 
133 /*
134  * Qids are, in path:
135  *	 4 bits of file type (qids above)
136  *	23 bits of process slot number + 1
137  *	     in vers,
138  *	32 bits of pid, for consistency checking
139  * If notepg, c->pgrpid.path is pgrp slot, .vers is noteid.
140  */
141 #define	QSHIFT	5	/* location in qid of proc slot # */
142 
143 #define	QID(q)		((((ulong)(q).path)&0x0000001F)>>0)
144 #define	SLOT(q)		(((((ulong)(q).path)&0x07FFFFFE0)>>QSHIFT)-1)
145 #define	PID(q)		((q).vers)
146 #define	NOTEID(q)	((q).vers)
147 
148 void	procctlreq(Proc*, char*, int);
149 int	procctlmemio(Proc*, ulong, int, void*, int);
150 Chan*	proctext(Chan*, Proc*);
151 Segment* txt2data(Proc*, Segment*);
152 int	procstopped(void*);
153 void	mntscan(Mntwalk*, Proc*);
154 
155 static Traceevent *tevents;
156 static Lock tlock;
157 static int topens;
158 static int tproduced, tconsumed;
159 void (*proctrace)(Proc*, int, vlong);
160 
161 extern int unfair;
162 
163 static void
164 profclock(Ureg *ur, Timer *)
165 {
166 	Tos *tos;
167 
168 	if(up == 0 || up->state != Running)
169 		return;
170 
171 	/* user profiling clock */
172 	if(userureg(ur)){
173 		tos = (Tos*)(USTKTOP-sizeof(Tos));
174 		tos->clock += TK2MS(1);
175 		segclock(ur->pc);
176 	}
177 }
178 
179 static int
180 procgen(Chan *c, char *name, Dirtab *tab, int, int s, Dir *dp)
181 {
182 	Qid qid;
183 	Proc *p;
184 	char *ename;
185 	Segment *q;
186 	ulong pid, path, perm, len;
187 
188 	if(s == DEVDOTDOT){
189 		mkqid(&qid, Qdir, 0, QTDIR);
190 		devdir(c, qid, "#p", 0, eve, 0555, dp);
191 		return 1;
192 	}
193 
194 	if(c->qid.path == Qdir){
195 		if(s == 0){
196 			strcpy(up->genbuf, "trace");
197 			mkqid(&qid, Qtrace, -1, QTFILE);
198 			devdir(c, qid, up->genbuf, 0, eve, 0444, dp);
199 			return 1;
200 		}
201 
202 		if(name != nil){
203 			/* ignore s and use name to find pid */
204 			pid = strtol(name, &ename, 10);
205 			if(pid==0 || ename[0]!='\0')
206 				return -1;
207 			s = procindex(pid);
208 			if(s < 0)
209 				return -1;
210 		}
211 		else if(--s >= conf.nproc)
212 			return -1;
213 
214 		p = proctab(s);
215 		pid = p->pid;
216 		if(pid == 0)
217 			return 0;
218 		sprint(up->genbuf, "%lud", pid);
219 		/*
220 		 * String comparison is done in devwalk so name must match its formatted pid
221 		*/
222 		if(name != nil && strcmp(name, up->genbuf) != 0)
223 			return -1;
224 		mkqid(&qid, (s+1)<<QSHIFT, pid, QTDIR);
225 		devdir(c, qid, up->genbuf, 0, p->user, DMDIR|0555, dp);
226 		return 1;
227 	}
228 	if(c->qid.path == Qtrace){
229 		strcpy(up->genbuf, "trace");
230 		mkqid(&qid, Qtrace, -1, QTFILE);
231 		devdir(c, qid, up->genbuf, 0, eve, 0444, dp);
232 		return 1;
233 	}
234 	if(s >= nelem(procdir))
235 		return -1;
236 	if(tab)
237 		panic("procgen");
238 
239 	tab = &procdir[s];
240 	path = c->qid.path&~(((1<<QSHIFT)-1));	/* slot component */
241 
242 	/* p->procmode determines default mode for files in /proc */
243 	p = proctab(SLOT(c->qid));
244 	perm = tab->perm;
245 	if(perm == 0)
246 		perm = p->procmode;
247 	else	/* just copy read bits */
248 		perm |= p->procmode & 0444;
249 
250 	len = tab->length;
251 	switch(QID(c->qid)) {
252 	case Qwait:
253 		len = p->nwait;	/* incorrect size, but >0 means there's something to read */
254 		break;
255 	case Qprofile:
256 		q = p->seg[TSEG];
257 		if(q && q->profile) {
258 			len = (q->top-q->base)>>LRESPROF;
259 			len *= sizeof(*q->profile);
260 		}
261 		break;
262 	}
263 
264 	mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE);
265 	devdir(c, qid, tab->name, len, p->user, perm, dp);
266 	return 1;
267 }
268 
269 static void
270 _proctrace(Proc* p, Tevent etype, vlong ts)
271 {
272 	Traceevent *te;
273 
274 	if (p->trace == 0 || topens == 0 ||
275 		tproduced - tconsumed >= Nevents)
276 		return;
277 
278 	te = &tevents[tproduced&Emask];
279 	te->pid = p->pid;
280 	te->etype = etype;
281 	if (ts == 0)
282 		te->time = todget(nil);
283 	else
284 		te->time = ts;
285 	tproduced++;
286 }
287 
288 static void
289 procinit(void)
290 {
291 	if(conf.nproc >= (1<<(16-QSHIFT))-1)
292 		print("warning: too many procs for devproc\n");
293 	addclock0link((void (*)(void))profclock, 113);	/* Relative prime to HZ */
294 }
295 
296 static Chan*
297 procattach(char *spec)
298 {
299 	return devattach('p', spec);
300 }
301 
302 static Walkqid*
303 procwalk(Chan *c, Chan *nc, char **name, int nname)
304 {
305 	return devwalk(c, nc, name, nname, 0, 0, procgen);
306 }
307 
308 static int
309 procstat(Chan *c, uchar *db, int n)
310 {
311 	return devstat(c, db, n, 0, 0, procgen);
312 }
313 
314 /*
315  *  none can't read or write state on other
316  *  processes.  This is to contain access of
317  *  servers running as none should they be
318  *  subverted by, for example, a stack attack.
319  */
320 static void
321 nonone(Proc *p)
322 {
323 	if(p == up)
324 		return;
325 	if(strcmp(up->user, "none") != 0)
326 		return;
327 	if(iseve())
328 		return;
329 	error(Eperm);
330 }
331 
332 static Chan*
333 procopen(Chan *c, int omode)
334 {
335 	Proc *p;
336 	Pgrp *pg;
337 	Chan *tc;
338 	int pid;
339 
340 	if(c->qid.type & QTDIR)
341 		return devopen(c, omode, 0, 0, procgen);
342 
343 	if(QID(c->qid) == Qtrace){
344 		if (omode != OREAD)
345 			error(Eperm);
346 		lock(&tlock);
347 		if (waserror()){
348 			unlock(&tlock);
349 			nexterror();
350 		}
351 		if (topens > 0)
352 			error("already open");
353 		topens++;
354 		if (tevents == nil){
355 			tevents = (Traceevent*)malloc(sizeof(Traceevent) * Nevents);
356 			if(tevents == nil)
357 				error(Enomem);
358 			tproduced = tconsumed = 0;
359 		}
360 		proctrace = _proctrace;
361 		unlock(&tlock);
362 		poperror();
363 
364 		c->mode = openmode(omode);
365 		c->flag |= COPEN;
366 		c->offset = 0;
367 		return c;
368 	}
369 
370 	p = proctab(SLOT(c->qid));
371 	qlock(&p->debug);
372 	if(waserror()){
373 		qunlock(&p->debug);
374 		nexterror();
375 	}
376 	pid = PID(c->qid);
377 	if(p->pid != pid)
378 		error(Eprocdied);
379 
380 	omode = openmode(omode);
381 
382 	switch(QID(c->qid)){
383 	case Qtext:
384 		if(omode != OREAD)
385 			error(Eperm);
386 		tc = proctext(c, p);
387 		tc->offset = 0;
388 		qunlock(&p->debug);
389 		poperror();
390 		return tc;
391 
392 	case Qproc:
393 	case Qkregs:
394 	case Qsegment:
395 	case Qprofile:
396 	case Qfd:
397 		if(omode != OREAD)
398 			error(Eperm);
399 		break;
400 
401 	case Qnote:
402 		if(p->privatemem)
403 			error(Eperm);
404 		break;
405 
406 	case Qmem:
407 	case Qctl:
408 		if(p->privatemem)
409 			error(Eperm);
410 		nonone(p);
411 		break;
412 
413 	case Qargs:
414 	case Qnoteid:
415 	case Qstatus:
416 	case Qwait:
417 	case Qregs:
418 	case Qfpregs:
419 	case Qsyscall:
420 		nonone(p);
421 		break;
422 
423 	case Qns:
424 		if(omode != OREAD)
425 			error(Eperm);
426 		c->aux = malloc(sizeof(Mntwalk));
427 		break;
428 
429 	case Qnotepg:
430 		nonone(p);
431 		pg = p->pgrp;
432 		if(pg == nil)
433 			error(Eprocdied);
434 		if(omode!=OWRITE || pg->pgrpid == 1)
435 			error(Eperm);
436 		c->pgrpid.path = pg->pgrpid+1;
437 		c->pgrpid.vers = p->noteid;
438 		break;
439 
440 	default:
441 		pprint("procopen %#lux\n", QID(c->qid));
442 		error(Egreg);
443 	}
444 
445 	/* Affix pid to qid */
446 	if(p->state != Dead)
447 		c->qid.vers = p->pid;
448 
449 	/* make sure the process slot didn't get reallocated while we were playing */
450 	coherence();
451 	if(p->pid != pid)
452 		error(Eprocdied);
453 
454 	tc = devopen(c, omode, 0, 0, procgen);
455 	qunlock(&p->debug);
456 	poperror();
457 
458 	return tc;
459 }
460 
461 static int
462 procwstat(Chan *c, uchar *db, int n)
463 {
464 	Proc *p;
465 	Dir *d;
466 
467 	if(c->qid.type&QTDIR)
468 		error(Eperm);
469 
470 	if(QID(c->qid) == Qtrace)
471 		return devwstat(c, db, n);
472 
473 	p = proctab(SLOT(c->qid));
474 	nonone(p);
475 	d = nil;
476 	if(waserror()){
477 		free(d);
478 		qunlock(&p->debug);
479 		nexterror();
480 	}
481 	qlock(&p->debug);
482 
483 	if(p->pid != PID(c->qid))
484 		error(Eprocdied);
485 
486 	if(strcmp(up->user, p->user) != 0 && strcmp(up->user, eve) != 0)
487 		error(Eperm);
488 
489 	d = smalloc(sizeof(Dir)+n);
490 	n = convM2D(db, n, &d[0], (char*)&d[1]);
491 	if(n == 0)
492 		error(Eshortstat);
493 	if(!emptystr(d->uid) && strcmp(d->uid, p->user) != 0){
494 		if(strcmp(up->user, eve) != 0)
495 			error(Eperm);
496 		else
497 			kstrdup(&p->user, d->uid);
498 	}
499 	/* p->procmode determines default mode for files in /proc */
500 	if(d->mode != ~0UL)
501 		p->procmode = d->mode&0777;
502 
503 	poperror();
504 	free(d);
505 	qunlock(&p->debug);
506 	return n;
507 }
508 
509 
510 static long
511 procoffset(long offset, char *va, int *np)
512 {
513 	if(offset > 0) {
514 		offset -= *np;
515 		if(offset < 0) {
516 			memmove(va, va+*np+offset, -offset);
517 			*np = -offset;
518 		}
519 		else
520 			*np = 0;
521 	}
522 	return offset;
523 }
524 
525 static int
526 procqidwidth(Chan *c)
527 {
528 	char buf[32];
529 
530 	return sprint(buf, "%lud", c->qid.vers);
531 }
532 
533 int
534 procfdprint(Chan *c, int fd, int w, char *s, int ns)
535 {
536 	int n;
537 
538 	if(w == 0)
539 		w = procqidwidth(c);
540 	n = snprint(s, ns, "%3d %.2s %C %4ld (%.16llux %*lud %.2ux) %5ld %8lld %s\n",
541 		fd,
542 		&"r w rw"[(c->mode&3)<<1],
543 		devtab[c->type]->dc, c->dev,
544 		c->qid.path, w, c->qid.vers, c->qid.type,
545 		c->iounit, c->offset, c->path->s);
546 	return n;
547 }
548 
549 static int
550 procfds(Proc *p, char *va, int count, long offset)
551 {
552 	Fgrp *f;
553 	Chan *c;
554 	char buf[256];
555 	int n, i, w, ww;
556 	char *a;
557 
558 	/* print to buf to avoid holding fgrp lock while writing to user space */
559 	if(count > sizeof buf)
560 		count = sizeof buf;
561 	a = buf;
562 
563 	qlock(&p->debug);
564 	f = p->fgrp;
565 	if(f == nil){
566 		qunlock(&p->debug);
567 		return 0;
568 	}
569 	lock(f);
570 	if(waserror()){
571 		unlock(f);
572 		qunlock(&p->debug);
573 		nexterror();
574 	}
575 
576 	n = readstr(0, a, count, p->dot->path->s);
577 	n += snprint(a+n, count-n, "\n");
578 	offset = procoffset(offset, a, &n);
579 	/* compute width of qid.path */
580 	w = 0;
581 	for(i = 0; i <= f->maxfd; i++) {
582 		c = f->fd[i];
583 		if(c == nil)
584 			continue;
585 		ww = procqidwidth(c);
586 		if(ww > w)
587 			w = ww;
588 	}
589 	for(i = 0; i <= f->maxfd; i++) {
590 		c = f->fd[i];
591 		if(c == nil)
592 			continue;
593 		n += procfdprint(c, i, w, a+n, count-n);
594 		offset = procoffset(offset, a, &n);
595 	}
596 	unlock(f);
597 	qunlock(&p->debug);
598 	poperror();
599 
600 	/* copy result to user space, now that locks are released */
601 	memmove(va, buf, n);
602 
603 	return n;
604 }
605 
606 static void
607 procclose(Chan * c)
608 {
609 	if(QID(c->qid) == Qtrace){
610 		lock(&tlock);
611 		if(topens > 0)
612 			topens--;
613 		if(topens == 0)
614 			proctrace = nil;
615 		unlock(&tlock);
616 	}
617 	if(QID(c->qid) == Qns && c->aux != 0)
618 		free(c->aux);
619 }
620 
621 static void
622 int2flag(int flag, char *s)
623 {
624 	if(flag == 0){
625 		*s = '\0';
626 		return;
627 	}
628 	*s++ = '-';
629 	if(flag & MAFTER)
630 		*s++ = 'a';
631 	if(flag & MBEFORE)
632 		*s++ = 'b';
633 	if(flag & MCREATE)
634 		*s++ = 'c';
635 	if(flag & MCACHE)
636 		*s++ = 'C';
637 	*s = '\0';
638 }
639 
640 static int
641 procargs(Proc *p, char *buf, int nbuf)
642 {
643 	int j, k, m;
644 	char *a;
645 	int n;
646 
647 	a = p->args;
648 	if(p->setargs){
649 		snprint(buf, nbuf, "%s [%s]", p->text, p->args);
650 		return strlen(buf);
651 	}
652 	n = p->nargs;
653 	for(j = 0; j < nbuf - 1; j += m){
654 		if(n <= 0)
655 			break;
656 		if(j != 0)
657 			buf[j++] = ' ';
658 		m = snprint(buf+j, nbuf-j, "%q",  a);
659 		k = strlen(a) + 1;
660 		a += k;
661 		n -= k;
662 	}
663 	return j;
664 }
665 
666 static int
667 eventsavailable(void *)
668 {
669 	return tproduced > tconsumed;
670 }
671 
672 static long
673 procread(Chan *c, void *va, long n, vlong off)
674 {
675 	/* NSEG*32 was too small for worst cases */
676 	char *a, flag[10], *sps, *srv, statbuf[NSEG*64];
677 	int i, j, m, navail, ne, pid, rsize;
678 	long l;
679 	uchar *rptr;
680 	ulong offset;
681 	Confmem *cm;
682 	Mntwalk *mw;
683 	Proc *p;
684 	Segment *sg, *s;
685 	Ureg kur;
686 	Waitq *wq;
687 
688 	a = va;
689 	offset = off;
690 
691 	if(c->qid.type & QTDIR)
692 		return devdirread(c, a, n, 0, 0, procgen);
693 
694 	if(QID(c->qid) == Qtrace){
695 		if(!eventsavailable(nil))
696 			return 0;
697 
698 		rptr = (uchar*)va;
699 		navail = tproduced - tconsumed;
700 		if(navail > n / sizeof(Traceevent))
701 			navail = n / sizeof(Traceevent);
702 		while(navail > 0) {
703 			ne = ((tconsumed & Emask) + navail > Nevents)?
704 					Nevents - (tconsumed & Emask): navail;
705 			memmove(rptr, &tevents[tconsumed & Emask],
706 					ne * sizeof(Traceevent));
707 
708 			tconsumed += ne;
709 			rptr += ne * sizeof(Traceevent);
710 			navail -= ne;
711 		}
712 		return rptr - (uchar*)va;
713 	}
714 
715 	p = proctab(SLOT(c->qid));
716 	if(p->pid != PID(c->qid))
717 		error(Eprocdied);
718 
719 	switch(QID(c->qid)){
720 	case Qargs:
721 		qlock(&p->debug);
722 		j = procargs(p, up->genbuf, sizeof up->genbuf);
723 		qunlock(&p->debug);
724 		if(offset >= j)
725 			return 0;
726 		if(offset+n > j)
727 			n = j-offset;
728 		memmove(a, &up->genbuf[offset], n);
729 		return n;
730 	case Qsyscall:
731 		if(!p->syscalltrace)
732 			return 0;
733 		n = readstr(offset, a, n, p->syscalltrace);
734 		return n;
735 
736 	case Qmem:
737 		if(offset < KZERO)
738 			return procctlmemio(p, offset, n, va, 1);
739 
740 		if(!iseve())
741 			error(Eperm);
742 
743 		/* validate kernel addresses */
744 		if(offset < (ulong)end) {
745 			if(offset+n > (ulong)end)
746 				n = (ulong)end - offset;
747 			memmove(a, (char*)offset, n);
748 			return n;
749 		}
750 		for(i=0; i<nelem(conf.mem); i++){
751 			cm = &conf.mem[i];
752 			/* klimit-1 because klimit might be zero! */
753 			if(cm->kbase <= offset && offset <= cm->klimit-1){
754 				if(offset+n >= cm->klimit-1)
755 					n = cm->klimit - offset;
756 				memmove(a, (char*)offset, n);
757 				return n;
758 			}
759 		}
760 		error(Ebadarg);
761 
762 	case Qprofile:
763 		s = p->seg[TSEG];
764 		if(s == 0 || s->profile == 0)
765 			error("profile is off");
766 		i = (s->top-s->base)>>LRESPROF;
767 		i *= sizeof(*s->profile);
768 		if(offset >= i)
769 			return 0;
770 		if(offset+n > i)
771 			n = i - offset;
772 		memmove(a, ((char*)s->profile)+offset, n);
773 		return n;
774 
775 	case Qnote:
776 		qlock(&p->debug);
777 		if(waserror()){
778 			qunlock(&p->debug);
779 			nexterror();
780 		}
781 		if(p->pid != PID(c->qid))
782 			error(Eprocdied);
783 		if(n < 1)	/* must accept at least the '\0' */
784 			error(Etoosmall);
785 		if(p->nnote == 0)
786 			n = 0;
787 		else {
788 			m = strlen(p->note[0].msg) + 1;
789 			if(m > n)
790 				m = n;
791 			memmove(va, p->note[0].msg, m);
792 			((char*)va)[m-1] = '\0';
793 			p->nnote--;
794 			memmove(p->note, p->note+1, p->nnote*sizeof(Note));
795 			n = m;
796 		}
797 		if(p->nnote == 0)
798 			p->notepending = 0;
799 		poperror();
800 		qunlock(&p->debug);
801 		return n;
802 
803 	case Qproc:
804 		if(offset >= sizeof(Proc))
805 			return 0;
806 		if(offset+n > sizeof(Proc))
807 			n = sizeof(Proc) - offset;
808 		memmove(a, ((char*)p)+offset, n);
809 		return n;
810 
811 	case Qregs:
812 		rptr = (uchar*)p->dbgreg;
813 		rsize = sizeof(Ureg);
814 		goto regread;
815 
816 	case Qkregs:
817 		memset(&kur, 0, sizeof(Ureg));
818 		setkernur(&kur, p);
819 		rptr = (uchar*)&kur;
820 		rsize = sizeof(Ureg);
821 		goto regread;
822 
823 	case Qfpregs:
824 		rptr = (uchar*)&p->fpsave;
825 		rsize = sizeof(FPsave);
826 	regread:
827 		if(rptr == 0)
828 			error(Enoreg);
829 		if(offset >= rsize)
830 			return 0;
831 		if(offset+n > rsize)
832 			n = rsize - offset;
833 		memmove(a, rptr+offset, n);
834 		return n;
835 
836 	case Qstatus:
837 		if(offset >= STATSIZE)
838 			return 0;
839 		if(offset+n > STATSIZE)
840 			n = STATSIZE - offset;
841 
842 		sps = p->psstate;
843 		if(sps == 0)
844 			sps = statename[p->state];
845 		memset(statbuf, ' ', sizeof statbuf);
846 		memmove(statbuf+0*KNAMELEN, p->text, strlen(p->text));
847 		memmove(statbuf+1*KNAMELEN, p->user, strlen(p->user));
848 		memmove(statbuf+2*KNAMELEN, sps, strlen(sps));
849 		j = 2*KNAMELEN + 12;
850 
851 		for(i = 0; i < 6; i++) {
852 			l = p->time[i];
853 			if(i == TReal)
854 				l = MACHP(0)->ticks - l;
855 			l = TK2MS(l);
856 			readnum(0, statbuf+j+NUMSIZE*i, NUMSIZE, l, NUMSIZE);
857 		}
858 		/* ignore stack, which is mostly non-existent */
859 		l = 0;
860 		for(i=1; i<NSEG; i++){
861 			s = p->seg[i];
862 			if(s)
863 				l += s->top - s->base;
864 		}
865 		readnum(0, statbuf+j+NUMSIZE*6, NUMSIZE, l>>10, NUMSIZE);
866 		readnum(0, statbuf+j+NUMSIZE*7, NUMSIZE, p->basepri, NUMSIZE);
867 		readnum(0, statbuf+j+NUMSIZE*8, NUMSIZE, p->priority, NUMSIZE);
868 		memmove(a, statbuf+offset, n);
869 		return n;
870 
871 	case Qsegment:
872 		j = 0;
873 		for(i = 0; i < NSEG; i++) {
874 			sg = p->seg[i];
875 			if(sg == 0)
876 				continue;
877 			j += sprint(statbuf+j, "%-6s %c%c %.8lux %.8lux %4ld\n",
878 				sname[sg->type&SG_TYPE],
879 				sg->type&SG_RONLY ? 'R' : ' ',
880 				sg->profile ? 'P' : ' ',
881 				sg->base, sg->top, sg->ref);
882 		}
883 		if(offset >= j)
884 			return 0;
885 		if(offset+n > j)
886 			n = j-offset;
887 		if(n == 0 && offset == 0)
888 			exhausted("segments");
889 		memmove(a, &statbuf[offset], n);
890 		return n;
891 
892 	case Qwait:
893 		if(!canqlock(&p->qwaitr))
894 			error(Einuse);
895 
896 		if(waserror()) {
897 			qunlock(&p->qwaitr);
898 			nexterror();
899 		}
900 
901 		lock(&p->exl);
902 		if(up == p && p->nchild == 0 && p->waitq == 0) {
903 			unlock(&p->exl);
904 			error(Enochild);
905 		}
906 		pid = p->pid;
907 		while(p->waitq == 0) {
908 			unlock(&p->exl);
909 			sleep(&p->waitr, haswaitq, p);
910 			if(p->pid != pid)
911 				error(Eprocdied);
912 			lock(&p->exl);
913 		}
914 		wq = p->waitq;
915 		p->waitq = wq->next;
916 		p->nwait--;
917 		unlock(&p->exl);
918 
919 		qunlock(&p->qwaitr);
920 		poperror();
921 		n = snprint(a, n, "%d %lud %lud %lud %q",
922 			wq->w.pid,
923 			wq->w.time[TUser], wq->w.time[TSys], wq->w.time[TReal],
924 			wq->w.msg);
925 		free(wq);
926 		return n;
927 
928 	case Qns:
929 		qlock(&p->debug);
930 		if(waserror()){
931 			qunlock(&p->debug);
932 			nexterror();
933 		}
934 		if(p->pgrp == nil || p->pid != PID(c->qid))
935 			error(Eprocdied);
936 		mw = c->aux;
937 		if(mw->cddone){
938 			qunlock(&p->debug);
939 			poperror();
940 			return 0;
941 		}
942 		mntscan(mw, p);
943 		if(mw->mh == 0){
944 			mw->cddone = 1;
945 			i = snprint(a, n, "cd %s\n", p->dot->path->s);
946 			qunlock(&p->debug);
947 			poperror();
948 			return i;
949 		}
950 		int2flag(mw->cm->mflag, flag);
951 		if(strcmp(mw->cm->to->path->s, "#M") == 0){
952 			srv = srvname(mw->cm->to->mchan);
953 			i = snprint(a, n, "mount %s %s %s %s\n", flag,
954 				srv==nil? mw->cm->to->mchan->path->s : srv,
955 				mw->mh->from->path->s, mw->cm->spec? mw->cm->spec : "");
956 			free(srv);
957 		}else
958 			i = snprint(a, n, "bind %s %s %s\n", flag,
959 				mw->cm->to->path->s, mw->mh->from->path->s);
960 		qunlock(&p->debug);
961 		poperror();
962 		return i;
963 
964 	case Qnoteid:
965 		return readnum(offset, va, n, p->noteid, NUMSIZE);
966 	case Qfd:
967 		return procfds(p, va, n, offset);
968 	}
969 	error(Egreg);
970 	return 0;		/* not reached */
971 }
972 
973 void
974 mntscan(Mntwalk *mw, Proc *p)
975 {
976 	Pgrp *pg;
977 	Mount *t;
978 	Mhead *f;
979 	int nxt, i;
980 	ulong last, bestmid;
981 
982 	pg = p->pgrp;
983 	rlock(&pg->ns);
984 
985 	nxt = 0;
986 	bestmid = ~0;
987 
988 	last = 0;
989 	if(mw->mh)
990 		last = mw->cm->mountid;
991 
992 	for(i = 0; i < MNTHASH; i++) {
993 		for(f = pg->mnthash[i]; f; f = f->hash) {
994 			for(t = f->mount; t; t = t->next) {
995 				if(mw->mh == 0 ||
996 				  (t->mountid > last && t->mountid < bestmid)) {
997 					mw->cm = t;
998 					mw->mh = f;
999 					bestmid = mw->cm->mountid;
1000 					nxt = 1;
1001 				}
1002 			}
1003 		}
1004 	}
1005 	if(nxt == 0)
1006 		mw->mh = 0;
1007 
1008 	runlock(&pg->ns);
1009 }
1010 
1011 static long
1012 procwrite(Chan *c, void *va, long n, vlong off)
1013 {
1014 	int id, m;
1015 	Proc *p, *t, *et;
1016 	char *a, *arg, buf[ERRMAX];
1017 	ulong offset = off;
1018 
1019 	a = va;
1020 	if(c->qid.type & QTDIR)
1021 		error(Eisdir);
1022 
1023 	p = proctab(SLOT(c->qid));
1024 
1025 	/* Use the remembered noteid in the channel rather
1026 	 * than the process pgrpid
1027 	 */
1028 	if(QID(c->qid) == Qnotepg) {
1029 		pgrpnote(NOTEID(c->pgrpid), va, n, NUser);
1030 		return n;
1031 	}
1032 
1033 	qlock(&p->debug);
1034 	if(waserror()){
1035 		qunlock(&p->debug);
1036 		nexterror();
1037 	}
1038 	if(p->pid != PID(c->qid))
1039 		error(Eprocdied);
1040 
1041 	switch(QID(c->qid)){
1042 	case Qargs:
1043 		if(n == 0)
1044 			error(Eshort);
1045 		if(n >= ERRMAX)
1046 			error(Etoobig);
1047 		arg = malloc(n+1);
1048 		if(arg == nil)
1049 			error(Enomem);
1050 		memmove(arg, va, n);
1051 		m = n;
1052 		if(arg[m-1] != 0)
1053 			arg[m++] = 0;
1054 		free(p->args);
1055 		p->nargs = m;
1056 		p->args = arg;
1057 		p->setargs = 1;
1058 		break;
1059 
1060 	case Qmem:
1061 		if(p->state != Stopped)
1062 			error(Ebadctl);
1063 
1064 		n = procctlmemio(p, offset, n, va, 0);
1065 		break;
1066 
1067 	case Qregs:
1068 		if(offset >= sizeof(Ureg))
1069 			n = 0;
1070 		else if(offset+n > sizeof(Ureg))
1071 			n = sizeof(Ureg) - offset;
1072 		if(p->dbgreg == 0)
1073 			error(Enoreg);
1074 		setregisters(p->dbgreg, (char*)(p->dbgreg)+offset, va, n);
1075 		break;
1076 
1077 	case Qfpregs:
1078 		if(offset >= sizeof(FPsave))
1079 			n = 0;
1080 		else if(offset+n > sizeof(FPsave))
1081 			n = sizeof(FPsave) - offset;
1082 		memmove((uchar*)&p->fpsave+offset, va, n);
1083 		break;
1084 
1085 	case Qctl:
1086 		procctlreq(p, va, n);
1087 		break;
1088 
1089 	case Qnote:
1090 		if(p->kp)
1091 			error(Eperm);
1092 		if(n >= ERRMAX-1)
1093 			error(Etoobig);
1094 		memmove(buf, va, n);
1095 		buf[n] = 0;
1096 		if(!postnote(p, 0, buf, NUser))
1097 			error("note not posted");
1098 		break;
1099 	case Qnoteid:
1100 		id = atoi(a);
1101 		if(id == p->pid) {
1102 			p->noteid = id;
1103 			break;
1104 		}
1105 		t = proctab(0);
1106 		for(et = t+conf.nproc; t < et; t++) {
1107 			if(t->state == Dead)
1108 				continue;
1109 			if(id == t->noteid) {
1110 				if(strcmp(p->user, t->user) != 0)
1111 					error(Eperm);
1112 				p->noteid = id;
1113 				break;
1114 			}
1115 		}
1116 		if(p->noteid != id)
1117 			error(Ebadarg);
1118 		break;
1119 	default:
1120 		pprint("unknown qid in procwrite\n");
1121 		error(Egreg);
1122 	}
1123 	poperror();
1124 	qunlock(&p->debug);
1125 	return n;
1126 }
1127 
1128 Dev procdevtab = {
1129 	'p',
1130 	"proc",
1131 
1132 	devreset,
1133 	procinit,
1134 	devshutdown,
1135 	procattach,
1136 	procwalk,
1137 	procstat,
1138 	procopen,
1139 	devcreate,
1140 	procclose,
1141 	procread,
1142 	devbread,
1143 	procwrite,
1144 	devbwrite,
1145 	devremove,
1146 	procwstat,
1147 };
1148 
1149 Chan*
1150 proctext(Chan *c, Proc *p)
1151 {
1152 	Chan *tc;
1153 	Image *i;
1154 	Segment *s;
1155 
1156 	s = p->seg[TSEG];
1157 	if(s == 0)
1158 		error(Enonexist);
1159 	if(p->state==Dead)
1160 		error(Eprocdied);
1161 
1162 	lock(s);
1163 	i = s->image;
1164 	if(i == 0) {
1165 		unlock(s);
1166 		error(Eprocdied);
1167 	}
1168 	unlock(s);
1169 
1170 	lock(i);
1171 	if(waserror()) {
1172 		unlock(i);
1173 		nexterror();
1174 	}
1175 
1176 	tc = i->c;
1177 	if(tc == 0)
1178 		error(Eprocdied);
1179 
1180 	if(incref(tc) == 1 || (tc->flag&COPEN) == 0 || tc->mode!=OREAD) {
1181 		cclose(tc);
1182 		error(Eprocdied);
1183 	}
1184 
1185 	if(p->pid != PID(c->qid))
1186 		error(Eprocdied);
1187 
1188 	unlock(i);
1189 	poperror();
1190 
1191 	return tc;
1192 }
1193 
1194 void
1195 procstopwait(Proc *p, int ctl)
1196 {
1197 	int pid;
1198 
1199 	if(p->pdbg)
1200 		error(Einuse);
1201 	if(procstopped(p) || p->state == Broken)
1202 		return;
1203 
1204 	if(ctl != 0)
1205 		p->procctl = ctl;
1206 	p->pdbg = up;
1207 	pid = p->pid;
1208 	qunlock(&p->debug);
1209 	up->psstate = "Stopwait";
1210 	if(waserror()) {
1211 		p->pdbg = 0;
1212 		qlock(&p->debug);
1213 		nexterror();
1214 	}
1215 	sleep(&up->sleep, procstopped, p);
1216 	poperror();
1217 	qlock(&p->debug);
1218 	if(p->pid != pid)
1219 		error(Eprocdied);
1220 }
1221 
1222 static void
1223 procctlcloseone(Proc *p, Fgrp *f, int fd)
1224 {
1225 	Chan *c;
1226 
1227 	c = f->fd[fd];
1228 	if(c == nil)
1229 		return;
1230 	f->fd[fd] = nil;
1231 	unlock(f);
1232 	qunlock(&p->debug);
1233 	cclose(c);
1234 	qlock(&p->debug);
1235 	lock(f);
1236 }
1237 
1238 void
1239 procctlclosefiles(Proc *p, int all, int fd)
1240 {
1241 	int i;
1242 	Fgrp *f;
1243 
1244 	f = p->fgrp;
1245 	if(f == nil)
1246 		error(Eprocdied);
1247 
1248 	lock(f);
1249 	f->ref++;
1250 	if(all)
1251 		for(i = 0; i < f->maxfd; i++)
1252 			procctlcloseone(p, f, i);
1253 	else
1254 		procctlcloseone(p, f, fd);
1255 	unlock(f);
1256 	closefgrp(f);
1257 }
1258 
1259 static char *
1260 parsetime(vlong *rt, char *s)
1261 {
1262 	uvlong ticks;
1263 	ulong l;
1264 	char *e, *p;
1265 	static int p10[] = {100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1};
1266 
1267 	if (s == nil)
1268 		return("missing value");
1269 	ticks=strtoul(s, &e, 10);
1270 	if (*e == '.'){
1271 		p = e+1;
1272 		l = strtoul(p, &e, 10);
1273 		if(e-p > nelem(p10))
1274 			return "too many digits after decimal point";
1275 		if(e-p == 0)
1276 			return "ill-formed number";
1277 		l *= p10[e-p-1];
1278 	}else
1279 		l = 0;
1280 	if (*e == '\0' || strcmp(e, "s") == 0){
1281 		ticks = 1000000000 * ticks + l;
1282 	}else if (strcmp(e, "ms") == 0){
1283 		ticks = 1000000 * ticks + l/1000;
1284 	}else if (strcmp(e, "µs") == 0 || strcmp(e, "us") == 0){
1285 		ticks = 1000 * ticks + l/1000000;
1286 	}else if (strcmp(e, "ns") != 0)
1287 		return "unrecognized unit";
1288 	*rt = ticks;
1289 	return nil;
1290 }
1291 
1292 void
1293 procctlreq(Proc *p, char *va, int n)
1294 {
1295 	Segment *s;
1296 	int npc, pri;
1297 	Cmdbuf *cb;
1298 	Cmdtab *ct;
1299 	vlong time;
1300 	char *e;
1301 	void (*pt)(Proc*, int, vlong);
1302 
1303 	if(p->kp)	/* no ctl requests to kprocs */
1304 		error(Eperm);
1305 
1306 	cb = parsecmd(va, n);
1307 	if(waserror()){
1308 		free(cb);
1309 		nexterror();
1310 	}
1311 
1312 	ct = lookupcmd(cb, proccmd, nelem(proccmd));
1313 
1314 	switch(ct->index){
1315 	case CMclose:
1316 		procctlclosefiles(p, 0, atoi(cb->f[1]));
1317 		break;
1318 	case CMclosefiles:
1319 		procctlclosefiles(p, 1, 0);
1320 		break;
1321 	case CMhang:
1322 		p->hang = 1;
1323 		break;
1324 	case CMkill:
1325 		switch(p->state) {
1326 		case Broken:
1327 			unbreak(p);
1328 			break;
1329 		case Stopped:
1330 			p->procctl = Proc_exitme;
1331 			postnote(p, 0, "sys: killed", NExit);
1332 			ready(p);
1333 			break;
1334 		default:
1335 			p->procctl = Proc_exitme;
1336 			postnote(p, 0, "sys: killed", NExit);
1337 		}
1338 		break;
1339 	case CMnohang:
1340 		p->hang = 0;
1341 		break;
1342 	case CMnoswap:
1343 		p->noswap = 1;
1344 		break;
1345 	case CMpri:
1346 		pri = atoi(cb->f[1]);
1347 		if(pri > PriNormal && !iseve())
1348 			error(Eperm);
1349 		procpriority(p, pri, 0);
1350 		break;
1351 	case CMfixedpri:
1352 		pri = atoi(cb->f[1]);
1353 		if(pri > PriNormal && !iseve())
1354 			error(Eperm);
1355 		procpriority(p, pri, 1);
1356 		break;
1357 	case CMprivate:
1358 		p->privatemem = 1;
1359 		break;
1360 	case CMprofile:
1361 		s = p->seg[TSEG];
1362 		if(s == 0 || (s->type&SG_TYPE) != SG_TEXT)
1363 			error(Ebadctl);
1364 		if(s->profile != 0)
1365 			free(s->profile);
1366 		npc = (s->top-s->base)>>LRESPROF;
1367 		s->profile = malloc(npc*sizeof(*s->profile));
1368 		if(s->profile == 0)
1369 			error(Enomem);
1370 		break;
1371 	case CMstart:
1372 		if(p->state != Stopped)
1373 			error(Ebadctl);
1374 		ready(p);
1375 		break;
1376 	case CMstartstop:
1377 		if(p->state != Stopped)
1378 			error(Ebadctl);
1379 		p->procctl = Proc_traceme;
1380 		ready(p);
1381 		procstopwait(p, Proc_traceme);
1382 		break;
1383 	case CMstartsyscall:
1384 		if(p->state != Stopped)
1385 			error(Ebadctl);
1386 		p->procctl = Proc_tracesyscall;
1387 		ready(p);
1388 		procstopwait(p, Proc_tracesyscall);
1389 		break;
1390 	case CMstop:
1391 		procstopwait(p, Proc_stopme);
1392 		break;
1393 	case CMwaitstop:
1394 		procstopwait(p, 0);
1395 		break;
1396 	case CMwired:
1397 		procwired(p, atoi(cb->f[1]));
1398 		break;
1399 	case CMtrace:
1400 		switch(cb->nf){
1401 		case 1:
1402 			p->trace ^= 1;
1403 			break;
1404 		case 2:
1405 			p->trace = (atoi(cb->f[1]) != 0);
1406 			break;
1407 		default:
1408 			error("args");
1409 		}
1410 		break;
1411 	/* real time */
1412 	case CMperiod:
1413 		if(p->edf == nil)
1414 			edfinit(p);
1415 		if(e=parsetime(&time, cb->f[1]))	/* time in ns */
1416 			error(e);
1417 		edfstop(p);
1418 		p->edf->T = time/1000;	/* Edf times are in µs */
1419 		break;
1420 	case CMdeadline:
1421 		if(p->edf == nil)
1422 			edfinit(p);
1423 		if(e=parsetime(&time, cb->f[1]))
1424 			error(e);
1425 		edfstop(p);
1426 		p->edf->D = time/1000;
1427 		break;
1428 	case CMcost:
1429 		if(p->edf == nil)
1430 			edfinit(p);
1431 		if(e=parsetime(&time, cb->f[1]))
1432 			error(e);
1433 		edfstop(p);
1434 		p->edf->C = time/1000;
1435 		break;
1436 	case CMsporadic:
1437 		if(p->edf == nil)
1438 			edfinit(p);
1439 		p->edf->flags |= Sporadic;
1440 		break;
1441 	case CMdeadlinenotes:
1442 		if(p->edf == nil)
1443 			edfinit(p);
1444 		p->edf->flags |= Sendnotes;
1445 		break;
1446 	case CMadmit:
1447 		if(p->edf == 0)
1448 			error("edf params");
1449 		if(e = edfadmit(p))
1450 			error(e);
1451 		break;
1452 	case CMextra:
1453 		if(p->edf == nil)
1454 			edfinit(p);
1455 		p->edf->flags |= Extratime;
1456 		break;
1457 	case CMexpel:
1458 		if(p->edf)
1459 			edfstop(p);
1460 		break;
1461 	case CMevent:
1462 		pt = proctrace;
1463 		if(up->trace && pt)
1464 			pt(up, SUser, 0);
1465 		break;
1466 	}
1467 
1468 	poperror();
1469 	free(cb);
1470 }
1471 
1472 int
1473 procstopped(void *a)
1474 {
1475 	Proc *p = a;
1476 	return p->state == Stopped;
1477 }
1478 
1479 int
1480 procctlmemio(Proc *p, ulong offset, int n, void *va, int read)
1481 {
1482 	KMap *k;
1483 	Pte *pte;
1484 	Page *pg;
1485 	Segment *s;
1486 	ulong soff, l;
1487 	char *a = va, *b;
1488 
1489 	for(;;) {
1490 		s = seg(p, offset, 1);
1491 		if(s == 0)
1492 			error(Ebadarg);
1493 
1494 		if(offset+n >= s->top)
1495 			n = s->top-offset;
1496 
1497 		if(!read && (s->type&SG_TYPE) == SG_TEXT)
1498 			s = txt2data(p, s);
1499 
1500 		s->steal++;
1501 		soff = offset-s->base;
1502 		if(waserror()) {
1503 			s->steal--;
1504 			nexterror();
1505 		}
1506 		if(fixfault(s, offset, read, 0) == 0)
1507 			break;
1508 		poperror();
1509 		s->steal--;
1510 	}
1511 	poperror();
1512 	pte = s->map[soff/PTEMAPMEM];
1513 	if(pte == 0)
1514 		panic("procctlmemio");
1515 	pg = pte->pages[(soff&(PTEMAPMEM-1))/BY2PG];
1516 	if(pagedout(pg))
1517 		panic("procctlmemio1");
1518 
1519 	l = BY2PG - (offset&(BY2PG-1));
1520 	if(n > l)
1521 		n = l;
1522 
1523 	k = kmap(pg);
1524 	if(waserror()) {
1525 		s->steal--;
1526 		kunmap(k);
1527 		nexterror();
1528 	}
1529 	b = (char*)VA(k);
1530 	b += offset&(BY2PG-1);
1531 	if(read == 1)
1532 		memmove(a, b, n);	/* This can fault */
1533 	else
1534 		memmove(b, a, n);
1535 	kunmap(k);
1536 	poperror();
1537 
1538 	/* Ensure the process sees text page changes */
1539 	if(s->flushme)
1540 		memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl));
1541 
1542 	s->steal--;
1543 
1544 	if(read == 0)
1545 		p->newtlb = 1;
1546 
1547 	return n;
1548 }
1549 
1550 Segment*
1551 txt2data(Proc *p, Segment *s)
1552 {
1553 	int i;
1554 	Segment *ps;
1555 
1556 	ps = newseg(SG_DATA, s->base, s->size);
1557 	ps->image = s->image;
1558 	incref(ps->image);
1559 	ps->fstart = s->fstart;
1560 	ps->flen = s->flen;
1561 	ps->flushme = 1;
1562 
1563 	qlock(&p->seglock);
1564 	for(i = 0; i < NSEG; i++)
1565 		if(p->seg[i] == s)
1566 			break;
1567 	if(i == NSEG)
1568 		panic("segment gone");
1569 
1570 	qunlock(&s->lk);
1571 	putseg(s);
1572 	qlock(&ps->lk);
1573 	p->seg[i] = ps;
1574 	qunlock(&p->seglock);
1575 
1576 	return ps;
1577 }
1578 
1579 Segment*
1580 data2txt(Segment *s)
1581 {
1582 	Segment *ps;
1583 
1584 	ps = newseg(SG_TEXT, s->base, s->size);
1585 	ps->image = s->image;
1586 	incref(ps->image);
1587 	ps->fstart = s->fstart;
1588 	ps->flen = s->flen;
1589 	ps->flushme = 1;
1590 
1591 	return ps;
1592 }
1593