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