xref: /illumos-gate/usr/src/lib/libc/port/gen/setpriority.c (revision 002c70ff32f5df6f93c15f88d351ce26443e6ee7)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 /*
30  * University Copyright- Copyright (c) 1982, 1986, 1988
31  * The Regents of the University of California
32  * All Rights Reserved
33  *
34  * University Acknowledgment- Portions of this document are derived from
35  * software developed by the University of California, Berkeley, and its
36  * contributors.
37  */
38 
39 #pragma ident	"%Z%%M%	%I%	%E% SMI"
40 
41 #include "synonyms.h"
42 
43 #include <string.h>
44 #include <sys/types.h>
45 #include <sys/time.h>
46 #include <sys/resource.h>
47 #include <sys/procset.h>
48 #include <sys/priocntl.h>
49 #include <limits.h>
50 #include <errno.h>
51 #include <priv.h>
52 
53 static idtype_t
54 prio_to_idtype(int which)
55 {
56 	switch (which) {
57 
58 	case PRIO_PROCESS:
59 		return (P_PID);
60 
61 	case PRIO_PGRP:
62 		return (P_PGID);
63 
64 	case PRIO_USER:
65 		return (P_UID);
66 
67 	case PRIO_GROUP:
68 		return (P_GID);
69 
70 	case PRIO_SESSION:
71 		return (P_SID);
72 
73 	case PRIO_LWP:
74 		return (P_LWPID);
75 
76 	case PRIO_TASK:
77 		return (P_TASKID);
78 
79 	case PRIO_PROJECT:
80 		return (P_PROJID);
81 
82 	case PRIO_ZONE:
83 		return (P_ZONEID);
84 
85 	case PRIO_CONTRACT:
86 		return (P_CTID);
87 
88 	default:
89 		return (-1);
90 	}
91 }
92 
93 static int
94 old_idtype(int which)
95 {
96 	switch (which) {
97 	case PRIO_PROCESS:
98 	case PRIO_PGRP:
99 	case PRIO_USER:
100 		return (1);
101 	default:
102 		return (0);
103 	}
104 }
105 
106 int
107 getpriority(int which, id_t who)
108 {
109 	id_t id;
110 	idtype_t idtype;
111 	pcnice_t pcnice;
112 
113 	if ((idtype = prio_to_idtype(which)) == -1) {
114 		errno = EINVAL;
115 		return (-1);
116 	}
117 
118 	if (who < 0) {
119 		if (old_idtype(which)) {
120 			errno = EINVAL;
121 			return (-1);
122 		} else if (who != P_MYID) {
123 			errno = EINVAL;
124 			return (-1);
125 		}
126 	}
127 
128 	/*
129 	 * The POSIX standard requires that a 0 value for the who argument
130 	 * should specify the current process, process group, or user.
131 	 * For all other id types we can treat zero as normal id value.
132 	 */
133 	if (who == 0 && old_idtype(which))
134 		id = P_MYID;
135 	else
136 		id = who;
137 
138 	pcnice.pc_val = 0;
139 	pcnice.pc_op = PC_GETNICE;
140 
141 	if (priocntl(idtype, id, PC_DONICE, (caddr_t)&pcnice) == -1)
142 		return (-1);
143 	else
144 		return (pcnice.pc_val);
145 }
146 
147 int
148 setpriority(int which, id_t who, int prio)
149 {
150 	id_t id;
151 	idtype_t idtype;
152 	pcnice_t pcnice;
153 	int ret;
154 
155 	if ((idtype = prio_to_idtype(which)) == -1) {
156 		errno = EINVAL;
157 		return (-1);
158 	}
159 
160 	if (who < 0) {
161 		if (old_idtype(which)) {
162 			errno = EINVAL;
163 			return (-1);
164 		} else if (who != P_MYID) {
165 			errno = EINVAL;
166 			return (-1);
167 		}
168 	}
169 
170 	if (who == 0 && old_idtype(which))
171 		id = P_MYID;
172 	else
173 		id = who;
174 
175 	if (prio > 19)
176 		prio = 19;
177 	else if (prio < -20)
178 		prio = -20;
179 
180 	pcnice.pc_val = prio;
181 	pcnice.pc_op = PC_SETNICE;
182 
183 	ret = priocntl(idtype, id, PC_DONICE, (caddr_t)&pcnice);
184 
185 	if (ret != 0 && errno == EPERM) {
186 		int		incr;
187 		int		tmp;
188 		pcnice_t	gpcnice = { 0, PC_GETNICE };
189 		priv_set_t	*pset;
190 
191 		/*
192 		 * The priocntl PC_DONICE subcommand returns EPERM if we lack
193 		 * sufficient privileges to carry out the operation, but
194 		 * setpriority(3C) needs to return EACCES. We can't just change
195 		 * EPERM to EACCES, because there are other conditions which
196 		 * legitimately cause EPERM (such as an euid/ruid mismatch
197 		 * between the current process and the target.).
198 		 */
199 		if ((tmp = priocntl(idtype, id, PC_DONICE,
200 		    (caddr_t)&gpcnice)) != 0)
201 			return (tmp);
202 
203 		incr = prio - gpcnice.pc_val;
204 
205 		if ((pset = priv_allocset()) == NULL ||
206 		    getppriv(PRIV_EFFECTIVE, pset) != 0)
207 			return (-1);
208 
209 		/*
210 		 * setpriority(3C) must return EACCES if we lack the privilege
211 		 * checked for below and we are trying to increase the process
212 		 * priority (by lowering the numeric value of its priority).
213 		 */
214 		if ((incr < 0 || incr > 2 * NZERO) &&
215 		    !priv_ismember(pset, "proc_priocntl"))
216 			errno = EACCES;
217 
218 		priv_freeset(pset);
219 	}
220 
221 	return (ret);
222 }
223