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/*
23 * Copyright 2008 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 * University Copyright- Copyright (c) 1982, 1986, 1988
32 * The Regents of the University of California
33 * All Rights Reserved
34 *
35 * University Acknowledgment- Portions of this document are derived from
36 * software developed by the University of California, Berkeley, and its
37 * contributors.
38 */
39
40#pragma ident	"%Z%%M%	%I%	%E% SMI"
41
42#include "lint.h"
43#include <string.h>
44#include <limits.h>
45#include <sys/types.h>
46#include <sys/time.h>
47#include <sys/resource.h>
48#include <sys/procset.h>
49#include <sys/priocntl.h>
50#include <limits.h>
51#include <errno.h>
52#include <priv.h>
53
54static idtype_t
55prio_to_idtype(int which)
56{
57	switch (which) {
58
59	case PRIO_PROCESS:
60		return (P_PID);
61
62	case PRIO_PGRP:
63		return (P_PGID);
64
65	case PRIO_USER:
66		return (P_UID);
67
68	case PRIO_GROUP:
69		return (P_GID);
70
71	case PRIO_SESSION:
72		return (P_SID);
73
74	case PRIO_LWP:
75		return (P_LWPID);
76
77	case PRIO_TASK:
78		return (P_TASKID);
79
80	case PRIO_PROJECT:
81		return (P_PROJID);
82
83	case PRIO_ZONE:
84		return (P_ZONEID);
85
86	case PRIO_CONTRACT:
87		return (P_CTID);
88
89	default:
90		return (-1);
91	}
92}
93
94static int
95old_idtype(int which)
96{
97	switch (which) {
98	case PRIO_PROCESS:
99	case PRIO_PGRP:
100	case PRIO_USER:
101		return (1);
102	default:
103		return (0);
104	}
105}
106
107int
108getpriority(int which, id_t who)
109{
110	id_t id;
111	idtype_t idtype;
112	pcnice_t pcnice;
113
114	if ((idtype = prio_to_idtype(which)) == -1) {
115		errno = EINVAL;
116		return (-1);
117	}
118
119	if (who < 0) {
120		if (old_idtype(which)) {
121			errno = EINVAL;
122			return (-1);
123		} else if (who != P_MYID) {
124			errno = EINVAL;
125			return (-1);
126		}
127	}
128
129	/*
130	 * The POSIX standard requires that a 0 value for the who argument
131	 * should specify the current process, process group, or user.
132	 * For all other id types we can treat zero as normal id value.
133	 */
134	if (who == 0 && old_idtype(which))
135		id = P_MYID;
136	else
137		id = who;
138
139	pcnice.pc_val = 0;
140	pcnice.pc_op = PC_GETNICE;
141
142	if (priocntl(idtype, id, PC_DONICE, &pcnice) == -1)
143		return (-1);
144	else
145		return (pcnice.pc_val);
146}
147
148int
149setpriority(int which, id_t who, int prio)
150{
151	id_t id;
152	idtype_t idtype;
153	pcnice_t pcnice;
154	int ret;
155
156	if ((idtype = prio_to_idtype(which)) == -1) {
157		errno = EINVAL;
158		return (-1);
159	}
160
161	if (who < 0) {
162		if (old_idtype(which)) {
163			errno = EINVAL;
164			return (-1);
165		} else if (who != P_MYID) {
166			errno = EINVAL;
167			return (-1);
168		}
169	}
170
171	if (who == 0 && old_idtype(which))
172		id = P_MYID;
173	else
174		id = who;
175
176	if (prio > NZERO - 1)
177		prio = NZERO - 1;
178	else if (prio < -NZERO)
179		prio = -NZERO;
180
181	pcnice.pc_val = prio;
182	pcnice.pc_op = PC_SETNICE;
183
184	ret = priocntl(idtype, id, PC_DONICE, &pcnice);
185
186	if (ret != 0 && errno == EPERM) {
187		pcnice_t	gpcnice = { 0, PC_GETNICE };
188		priv_set_t	*pset = NULL;
189
190		/*
191		 * The priocntl PC_DONICE subcommand returns EPERM if we lack
192		 * sufficient privileges to carry out the operation, but
193		 * setpriority(3C) may need to return EACCES.  We can't just
194		 * change EPERM to EACCES, because there are other conditions
195		 * which legitimately cause EPERM (such as an euid/ruid mismatch
196		 * between the current process and the target.).
197		 *
198		 * setpriority(3C) must return EACCES if we lack the privilege
199		 * checked for below and we are trying to increase the process
200		 * priority (by lowering the numeric value of its priority).
201		 */
202		if (priocntl(idtype, id, PC_DONICE, &gpcnice) == 0 &&
203		    prio < gpcnice.pc_val) {
204			if ((pset = priv_allocset()) != NULL &&
205			    getppriv(PRIV_EFFECTIVE, pset) == 0 &&
206			    !priv_ismember(pset, "proc_priocntl"))
207				errno = EACCES;
208			if (pset != NULL)
209				priv_freeset(pset);
210		}
211	}
212
213	return (ret);
214}
215