xref: /illumos-gate/usr/src/cmd/priocntl/subr.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
30 /*	  All Rights Reserved  	*/
31 
32 
33 #include	<stdio.h>
34 #include	<string.h>
35 #include	<strings.h>
36 #include	<stdlib.h>
37 #include	<unistd.h>
38 #include	<sys/types.h>
39 #include	<limits.h>
40 #include	<dirent.h>
41 #include	<fcntl.h>
42 #include	<sys/time.h>
43 #include	<sys/procset.h>
44 #include	<sys/priocntl.h>
45 #include	<sys/task.h>
46 #include	<procfs.h>
47 #include	<project.h>
48 #include	<errno.h>
49 #include	<zone.h>
50 #include	<libcontract_priv.h>
51 
52 #include "priocntl.h"
53 
54 /*LINTLIBRARY*/
55 
56 /*
57  * Utility functions for priocntl command.
58  */
59 
60 static char	*procdir = "/proc";
61 
62 /*PRINTFLIKE1*/
63 void
64 fatalerr(format, a1, a2, a3, a4, a5)
65 char	*format;
66 int	a1, a2, a3, a4, a5;
67 {
68 	(void) fprintf(stderr, format, a1, a2, a3, a4, a5);
69 	exit(1);
70 }
71 
72 
73 /*
74  * Structure defining idtypes known to the priocntl command
75  * along with the corresponding names and a liberal guess
76  * of the max number of procs sharing any given ID of that type.
77  * The idtype values themselves are defined in <sys/procset.h>.
78  */
79 static struct idtypes {
80 	idtype_t	idtype;
81 	char		*idtypnm;
82 } idtypes [] = {
83 	{ P_PID,	"pid"	},
84 	{ P_PPID,	"ppid"	},
85 	{ P_PGID,	"pgid"	},
86 	{ P_SID,	"sid"	},
87 	{ P_CID,	"class"	},
88 	{ P_UID,	"uid"	},
89 	{ P_GID,	"gid"	},
90 	{ P_PROJID,	"projid" },
91 	{ P_TASKID,	"taskid" },
92 	{ P_ZONEID,	"zoneid" },
93 	{ P_CTID,	"ctid" },
94 	{ P_ALL,	"all"	}
95 };
96 
97 #define	IDCNT	(sizeof (idtypes) / sizeof (struct idtypes))
98 
99 
100 int
101 str2idtyp(idtypnm, idtypep)
102 char		*idtypnm;
103 idtype_t	*idtypep;
104 {
105 	register struct idtypes	*curp;
106 	register struct idtypes	*endp;
107 
108 	for (curp = idtypes, endp = &idtypes[IDCNT]; curp < endp; curp++) {
109 		if (strcmp(curp->idtypnm, idtypnm) == 0) {
110 			*idtypep = curp->idtype;
111 			return (0);
112 		}
113 	}
114 	return (-1);
115 }
116 
117 
118 int
119 idtyp2str(idtype, idtypnm)
120 idtype_t	idtype;
121 char		*idtypnm;
122 {
123 	register struct idtypes	*curp;
124 	register struct idtypes	*endp;
125 
126 	for (curp = idtypes, endp = &idtypes[IDCNT]; curp < endp; curp++) {
127 		if (idtype == curp->idtype) {
128 			(void) strncpy(idtypnm, curp->idtypnm, PC_IDTYPNMSZ);
129 			return (0);
130 		}
131 	}
132 	return (-1);
133 }
134 
135 
136 /*
137  * Compare two IDs for equality.
138  */
139 int
140 idcompar(id1p, id2p)
141 id_t	*id1p;
142 id_t	*id2p;
143 {
144 	if (*id1p == *id2p)
145 		return (0);
146 	else
147 		return (-1);
148 }
149 
150 
151 id_t
152 clname2cid(clname)
153 char	*clname;
154 {
155 	pcinfo_t	pcinfo;
156 
157 	(void) strncpy(pcinfo.pc_clname, clname, PC_CLNMSZ);
158 	if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1)
159 		return ((id_t)-1);
160 	return (pcinfo.pc_cid);
161 }
162 
163 
164 int
165 getmyid(idtype, idptr)
166 idtype_t	idtype;
167 id_t		*idptr;
168 {
169 	pcinfo_t	pcinfo;
170 
171 	switch (idtype) {
172 
173 	case P_PID:
174 		*idptr = (id_t)getpid();
175 		break;
176 
177 	case P_PPID:
178 		*idptr = (id_t)getppid();
179 		break;
180 
181 	case P_PGID:
182 		*idptr = (id_t)getpgrp();
183 		break;
184 
185 	case P_SID:
186 		*idptr = (id_t)getsid(getpid());
187 		break;
188 
189 	case P_CID:
190 		if (priocntl(P_PID, P_MYID, PC_GETXPARMS, NULL,
191 		    PC_KY_CLNAME, pcinfo.pc_clname, 0) == -1 ||
192 		    priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1)
193 			return (-1);
194 
195 		*idptr = pcinfo.pc_cid;
196 		break;
197 
198 	case P_UID:
199 		*idptr = (id_t)getuid();
200 		break;
201 
202 	case P_GID:
203 		*idptr = (id_t)getgid();
204 		break;
205 
206 	case P_PROJID:
207 		*idptr = (id_t)getprojid();
208 		break;
209 
210 	case P_TASKID:
211 		*idptr = (id_t)gettaskid();
212 		break;
213 
214 	case P_ZONEID:
215 		*idptr = (id_t)getzoneid();
216 		break;
217 
218 	case P_CTID: {
219 		ctid_t id = getctid();
220 		if (id == -1)
221 			return (-1);
222 		*idptr = id;
223 		break;
224 	}
225 
226 	default:
227 		return (-1);
228 	}
229 	return (0);
230 }
231 
232 
233 int
234 getmyidstr(idtype, idstr)
235 idtype_t	idtype;
236 char		*idstr;
237 {
238 	char		clname[PC_CLNMSZ];
239 
240 	switch (idtype) {
241 
242 	case P_PID:
243 		itoa((long)getpid(), idstr);
244 		break;
245 
246 	case P_PPID:
247 		itoa((long)getppid(), idstr);
248 		break;
249 
250 	case P_PGID:
251 		itoa((long)getpgrp(), idstr);
252 		break;
253 	case P_SID:
254 		itoa((long)getsid(getpid()), idstr);
255 		break;
256 
257 	case P_CID:
258 		if (priocntl(P_PID, P_MYID, PC_GETXPARMS, NULL,
259 		    PC_KY_CLNAME, clname, 0) == -1)
260 			return (-1);
261 		(void) strncpy(idstr, clname, PC_CLNMSZ);
262 		break;
263 
264 	case P_UID:
265 		itoa((long)getuid(), idstr);
266 		break;
267 
268 	case P_GID:
269 		itoa((long)getgid(), idstr);
270 		break;
271 
272 	case P_PROJID:
273 		itoa((long)getprojid(), idstr);
274 		break;
275 
276 	case P_TASKID:
277 		itoa((long)gettaskid(), idstr);
278 		break;
279 
280 	case P_ZONEID:
281 		itoa((long)getzoneid(), idstr);
282 		break;
283 
284 	case P_CTID: {
285 		id_t id;
286 		if ((id = getctid()) == -1)
287 			return (-1);
288 		itoa((long)id, idstr);
289 		break;
290 	}
291 
292 	default:
293 		return (-1);
294 	}
295 	return (0);
296 }
297 
298 /*
299  * Look for pids with "upri > uprilim" in the set specified by idtype/id.
300  * If upri exceeds uprilim then print a warning.
301  */
302 int
303 verifyupri(idtype_t idtype, id_t id, char *clname, int key,
304 	pri_t upri, char *basenm)
305 {
306 	psinfo_t		prinfo;
307 	prcred_t		prcred;
308 	DIR			*dirp;
309 	struct dirent		*dentp;
310 	char			pname[MAXNAMLEN];
311 	char			*fname;
312 	int			procfd;
313 	int			saverr;
314 	pri_t			uprilim;
315 	int			verify;
316 	int			error = 0;
317 
318 	if (idtype == P_PID) {
319 		if (priocntl(P_PID, id, PC_GETXPARMS, clname, key,
320 		    &uprilim, 0) == -1)
321 			error = -1;
322 		else if (upri > uprilim)
323 			(void) fprintf(stderr,
324 			    "%s: Specified user priority %d exceeds"
325 			    " limit %d; set to %d (pid %d)\n",
326 			    basenm, upri, uprilim, uprilim, (int)id);
327 
328 		return (error);
329 	}
330 
331 	/*
332 	 * Look for the processes in the set specified by idtype/id.
333 	 * We read the /proc/<pid>/psinfo file to get the necessary
334 	 * process information.
335 	 */
336 
337 	if ((dirp = opendir(procdir)) == NULL)
338 		fatalerr("%s: Can't open PROC directory %s\n",
339 		    basenm, procdir);
340 
341 	while ((dentp = readdir(dirp)) != NULL) {
342 		if (dentp->d_name[0] == '.')	/* skip . and .. */
343 			continue;
344 
345 		(void) snprintf(pname, MAXNAMLEN, "%s/%s/",
346 		    procdir, dentp->d_name);
347 		fname = pname + strlen(pname);
348 retry:
349 		(void) strncpy(fname, "psinfo", strlen("psinfo") + 1);
350 		if ((procfd = open(pname, O_RDONLY)) < 0)
351 			continue;
352 		if (read(procfd, &prinfo, sizeof (prinfo)) != sizeof (prinfo)) {
353 			saverr = errno;
354 			(void) close(procfd);
355 			if (saverr == EAGAIN)
356 				goto retry;
357 			continue;
358 		}
359 		(void) close(procfd);
360 
361 		if (idtype == P_UID || idtype == P_GID) {
362 			(void) strncpy(fname, "cred", strlen("cred") + 1);
363 			if ((procfd = open(pname, O_RDONLY)) < 0 ||
364 			    read(procfd, &prcred, sizeof (prcred)) !=
365 			    sizeof (prcred)) {
366 				saverr = errno;
367 				(void) close(procfd);
368 				if (saverr == EAGAIN)
369 					goto retry;
370 				continue;
371 			}
372 			(void) close(procfd);
373 		}
374 
375 		if (prinfo.pr_lwp.pr_state == 0 || prinfo.pr_nlwp == 0)
376 			continue;
377 
378 		/*
379 		 * The lwp must be in the correct class.
380 		 */
381 		if (strncmp(clname, prinfo.pr_lwp.pr_clname, PC_CLNMSZ) != 0)
382 			continue;
383 
384 		verify = 0;
385 		switch (idtype) {
386 
387 		case P_PPID:
388 			if (id == (id_t)prinfo.pr_ppid)
389 				verify++;
390 			break;
391 
392 		case P_PGID:
393 			if (id == (id_t)prinfo.pr_pgid)
394 				verify++;
395 			break;
396 
397 		case P_SID:
398 			if (id == (id_t)prinfo.pr_sid)
399 				verify++;
400 			break;
401 
402 		case P_UID:
403 			if (id == (id_t)prcred.pr_euid)
404 				verify++;
405 			break;
406 
407 		case P_GID:
408 			if (id == (id_t)prcred.pr_egid)
409 				verify++;
410 			break;
411 
412 		case P_PROJID:
413 			if (id == (id_t)prinfo.pr_projid)
414 				verify++;
415 			break;
416 
417 		case P_TASKID:
418 			if (id == (id_t)prinfo.pr_taskid)
419 				verify++;
420 			break;
421 
422 		case P_ZONEID:
423 			if (id == (id_t)prinfo.pr_zoneid)
424 				verify++;
425 			break;
426 
427 		case P_CTID:
428 			if (id == (id_t)prinfo.pr_contract)
429 				verify++;
430 			break;
431 
432 		case P_CID:
433 		case P_ALL:
434 			verify++;
435 			break;
436 
437 		default:
438 			fatalerr("%s: Bad idtype %d in verifyupri()\n",
439 			    basenm, idtype);
440 		}
441 
442 		if (verify) {
443 			if (priocntl(P_PID, prinfo.pr_pid, PC_GETXPARMS,
444 			    clname, key, &uprilim, 0) == -1)
445 				error = -1;
446 			else if (upri > uprilim)
447 				(void) fprintf(stderr,
448 				    "%s: Specified user priority %d exceeds"
449 				    " limit %d; set to %d (pid %d)\n",
450 				    basenm, upri, uprilim, uprilim,
451 				    (int)prinfo.pr_pid);
452 		}
453 	}
454 	(void) closedir(dirp);
455 
456 	return (error);
457 }
458 
459 
460 /*
461  * Read a list of pids from a stream.
462  */
463 pid_t *
464 read_pidlist(size_t *npidsp, FILE *filep)
465 {
466 	size_t	nitems;
467 	pid_t	*pidlist = NULL;
468 
469 	*npidsp = 0;
470 
471 	do {
472 		if ((pidlist = (pid_t *)realloc(pidlist,
473 		    (*npidsp + NPIDS) * sizeof (pid_t))) == NULL)
474 			return (NULL);
475 
476 		nitems = fread(pidlist + *npidsp, sizeof (pid_t), NPIDS, filep);
477 		if (ferror(filep))
478 			return (NULL);
479 
480 		*npidsp += nitems;
481 	} while (nitems == NPIDS);
482 
483 	return (pidlist);
484 }
485 
486 
487 void
488 free_pidlist(pid_t *pidlist)
489 {
490 	free(pidlist);
491 }
492 
493 
494 long
495 str2num(char *p, long min, long max)
496 {
497 	long val;
498 	char *q;
499 	errno = 0;
500 
501 	val = strtol(p, &q, 10);
502 	if (errno != 0 || q == p || *q != '\0' || val < min || val > max)
503 		errno = EINVAL;
504 
505 	return (val);
506 }
507 
508 
509 /*
510  * itoa() and reverse() taken almost verbatim from K & R Chapter 3.
511  */
512 static void	reverse();
513 
514 /*
515  * itoa(): Convert n to characters in s.
516  */
517 void
518 itoa(n, s)
519 long	n;
520 char	*s;
521 {
522 	long	i, sign;
523 
524 	if ((sign = n) < 0)	/* record sign */
525 		n = -n;		/* make sign positive */
526 	i = 0;
527 	do {	/* generate digits in reverse order */
528 		s[i++] = n % 10 + '0';	/* get next digit */
529 	} while ((n /= 10) > 0);	/* delete it */
530 	if (sign < 0)
531 		s[i++] = '-';
532 	s[i] = '\0';
533 	reverse(s);
534 }
535 
536 
537 /*
538  * reverse(): Reverse string s in place.
539  */
540 static void
541 reverse(s)
542 char	*s;
543 {
544 	int	c, i, j;
545 
546 	for (i = 0, j = strlen(s) - 1; i < j; i++, j--) {
547 		c = s[i];
548 		s[i] = s[j];
549 		s[j] = (char)c;
550 	}
551 }
552 
553 
554 /*
555  * The following routine was removed from libc (libc/port/gen/hrtnewres.c).
556  * It has also been added to disadmin, so if you fix it here, you should
557  * also probably fix it there. In the long term, this should be recoded to
558  * not be hrt'ish.
559  */
560 
561 /*
562  *	Convert interval expressed in htp->hrt_res to new_res.
563  *
564  *	Calculate: (interval * new_res) / htp->hrt_res  rounding off as
565  *		specified by round.
566  *
567  *	Note:	All args are assumed to be positive.  If
568  *	the last divide results in something bigger than
569  *	a long, then -1 is returned instead.
570  */
571 
572 int
573 _hrtnewres(htp, new_res, round)
574 register hrtimer_t *htp;
575 register ulong_t new_res;
576 long round;
577 {
578 	register long  interval;
579 	longlong_t	dint;
580 	longlong_t	dto_res;
581 	longlong_t	drem;
582 	longlong_t	dfrom_res;
583 	longlong_t	prod;
584 	longlong_t	quot;
585 	register long	numerator;
586 	register long	result;
587 	ulong_t		modulus;
588 	ulong_t		twomodulus;
589 	long		temp;
590 
591 	if (new_res > NANOSEC || htp->hrt_rem < 0)
592 		return (-1);
593 
594 	if (htp->hrt_rem >= htp->hrt_res) {
595 		htp->hrt_secs += htp->hrt_rem / htp->hrt_res;
596 		htp->hrt_rem = htp->hrt_rem % htp->hrt_res;
597 	}
598 
599 	interval = htp->hrt_rem;
600 	if (interval == 0) {
601 		htp->hrt_res = new_res;
602 		return (0);
603 	}
604 
605 	/*
606 	 *	Try to do the calculations in single precision first
607 	 *	(for speed).  If they overflow, use double precision.
608 	 *	What we want to compute is:
609 	 *
610 	 *		(interval * new_res) / hrt->hrt_res
611 	 */
612 
613 	numerator = interval * new_res;
614 
615 	if (numerator / new_res  ==  interval) {
616 
617 		/*
618 		 *	The above multiply didn't give overflow since
619 		 *	the division got back the original number.  Go
620 		 *	ahead and compute the result.
621 		 */
622 
623 		result = numerator / htp->hrt_res;
624 
625 		/*
626 		 *	For HRT_RND, compute the value of:
627 		 *
628 		 *		(interval * new_res) % htp->hrt_res
629 		 *
630 		 *	If it is greater than half of the htp->hrt_res,
631 		 *	then rounding increases the result by 1.
632 		 *
633 		 *	For HRT_RNDUP, we increase the result by 1 if:
634 		 *
635 		 *		result * htp->hrt_res != numerator
636 		 *
637 		 *	because this tells us we truncated when calculating
638 		 *	result above.
639 		 *
640 		 *	We also check for overflow when incrementing result
641 		 *	although this is extremely rare.
642 		 */
643 
644 		if (round == HRT_RND) {
645 			modulus = numerator - result * htp->hrt_res;
646 			if ((twomodulus = 2 * modulus) / 2 == modulus) {
647 
648 				/*
649 				 * No overflow (if we overflow in calculation
650 				 * of twomodulus we fall through and use
651 				 * double precision).
652 				 */
653 				if (twomodulus >= htp->hrt_res) {
654 					temp = result + 1;
655 					if (temp - 1 == result)
656 						result++;
657 					else
658 						return (-1);
659 				}
660 				htp->hrt_res = new_res;
661 				htp->hrt_rem = result;
662 				return (0);
663 			}
664 		} else if (round == HRT_RNDUP) {
665 			if (result * htp->hrt_res != numerator) {
666 				temp = result + 1;
667 				if (temp - 1 == result)
668 					result++;
669 				else
670 					return (-1);
671 			}
672 			htp->hrt_res = new_res;
673 			htp->hrt_rem = result;
674 			return (0);
675 		} else {	/* round == HRT_TRUNC */
676 			htp->hrt_res = new_res;
677 			htp->hrt_rem = result;
678 			return (0);
679 		}
680 	}
681 
682 	/*
683 	 *	We would get overflow doing the calculation is
684 	 *	single precision so do it the slow but careful way.
685 	 *
686 	 *	Compute the interval times the resolution we are
687 	 *	going to.
688 	 */
689 
690 	dint = interval;
691 	dto_res = new_res;
692 	prod = dint * dto_res;
693 
694 	/*
695 	 *	For HRT_RND the result will be equal to:
696 	 *
697 	 *		((interval * new_res) + htp->hrt_res / 2) / htp->hrt_res
698 	 *
699 	 *	and for HRT_RNDUP we use:
700 	 *
701 	 *		((interval * new_res) + htp->hrt_res - 1) / htp->hrt_res
702 	 *
703 	 * 	This is a different but equivalent way of rounding.
704 	 */
705 
706 	if (round == HRT_RND) {
707 		drem = htp->hrt_res / 2;
708 		prod = prod + drem;
709 	} else if (round == HRT_RNDUP) {
710 		drem = htp->hrt_res - 1;
711 		prod = prod + drem;
712 	}
713 
714 	dfrom_res = htp->hrt_res;
715 	quot = prod / dfrom_res;
716 
717 	/*
718 	 *	If the quotient won't fit in a long, then we have
719 	 *	overflow.  Otherwise, return the result.
720 	 */
721 
722 	if (quot > UINT_MAX) {
723 		return (-1);
724 	} else {
725 		htp->hrt_res = new_res;
726 		htp->hrt_rem = (int)quot;
727 		return (0);
728 	}
729 }
730