1/*	$OpenBSD: renice.c,v 1.22 2022/08/12 00:24:07 cheloha Exp $	*/
2
3/*
4 * Copyright (c) 2009, 2015 Todd C. Miller <millert@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/types.h>
20#include <sys/time.h>
21#include <sys/resource.h>
22
23#include <ctype.h>
24#include <err.h>
25#include <errno.h>
26#include <limits.h>
27#include <pwd.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <unistd.h>
32
33#define	RENICE_NONE		0
34#define	RENICE_ABSOLUTE		1
35#define	RENICE_INCREMENT	2
36
37struct renice_param {
38	int pri;
39	short pri_type;
40	short id_type;
41	id_t id;
42};
43
44int main(int, char **);
45static int renice(struct renice_param *, struct renice_param *);
46__dead void usage(void);
47
48int
49main(int argc, char **argv)
50{
51	struct renice_param *params, *p;
52	struct passwd *pw;
53	int ch, id_type = PRIO_PROCESS;
54	int pri = 0, pri_type = RENICE_NONE;
55	char *ep, *idstr;
56	const char *errstr;
57
58	if (pledge("stdio getpw proc", NULL) == -1)
59		err(1, "pledge");
60
61	if (argc < 3)
62		usage();
63
64	/* Allocate enough space for the worst case. */
65	params = p = reallocarray(NULL, argc - 1, sizeof(*params));
66	if (params == NULL)
67		err(1, NULL);
68
69	/* Backwards compatibility: first arg may be priority. */
70	if (isdigit((unsigned char)argv[1][0]) ||
71	    ((argv[1][0] == '+' || argv[1][0] == '-') &&
72	    isdigit((unsigned char)argv[1][1]))) {
73		pri = (int)strtol(argv[1], &ep, 10);
74		if (*ep != '\0' || ep == argv[1]) {
75			warnx("invalid priority %s", argv[1]);
76			usage();
77		}
78		pri_type = RENICE_ABSOLUTE;
79		optind = 2;
80	}
81
82	/*
83	 * Slightly tricky getopt() usage since it is legal to have
84	 * option flags interleaved with arguments.
85	 */
86	for (;;) {
87		if ((ch = getopt(argc, argv, "g:n:p:u:")) != -1) {
88			switch (ch) {
89			case 'g':
90				id_type = PRIO_PGRP;
91				idstr = optarg;
92				break;
93			case 'n':
94				pri = (int)strtol(optarg, &ep, 10);
95				if (*ep != '\0' || ep == optarg) {
96					warnx("invalid increment %s", optarg);
97					usage();
98				}
99
100				/* Set priority for previous entries? */
101				if (pri_type == RENICE_NONE) {
102					struct renice_param *pp;
103					for (pp = params; pp != p; pp++) {
104						pp->pri = pri;
105						pp->pri_type = RENICE_INCREMENT;
106					}
107				}
108				pri_type = RENICE_INCREMENT;
109				continue;
110			case 'p':
111				id_type = PRIO_PROCESS;
112				idstr = optarg;
113				break;
114			case 'u':
115				id_type = PRIO_USER;
116				idstr = optarg;
117				break;
118			default:
119				usage();
120				break;
121			}
122		} else {
123			idstr = argv[optind++];
124			if (idstr == NULL)
125				break;
126		}
127		p->id_type = id_type;
128		p->pri = pri;
129		p->pri_type = pri_type;
130		if (id_type == PRIO_USER) {
131			if ((pw = getpwnam(idstr)) == NULL) {
132				uid_t id = strtonum(idstr, 0, UID_MAX, &errstr);
133				if (!errstr)
134					pw = getpwuid(id);
135			}
136			if (pw == NULL) {
137				warnx("unknown user %s", idstr);
138				continue;
139			}
140			p->id = pw->pw_uid;
141		} else {
142			p->id = strtonum(idstr, 0, UINT_MAX, &errstr);
143			if (errstr) {
144				warnx("%s is %s", idstr, errstr);
145				continue;
146			}
147		}
148		p++;
149	}
150	if (pri_type == RENICE_NONE)
151		usage();
152	return(renice(params, p));
153}
154
155static int
156renice(struct renice_param *p, struct renice_param *end)
157{
158	int new, old, error = 0;
159
160	for (; p < end; p++) {
161		errno = 0;
162		old = getpriority(p->id_type, p->id);
163		if (errno) {
164			warn("getpriority: %d", p->id);
165			error = 1;
166			continue;
167		}
168		if (p->pri_type == RENICE_INCREMENT)
169			p->pri += old;
170		new = p->pri > PRIO_MAX ? PRIO_MAX :
171		    p->pri < PRIO_MIN ? PRIO_MIN : p->pri;
172		if (setpriority(p->id_type, p->id, new) == -1) {
173			warn("setpriority: %d", p->id);
174			error = 1;
175			continue;
176		}
177		printf("%d: old priority %d, new priority %d\n",
178		    p->id, old, new);
179	}
180	return error;
181}
182
183__dead void
184usage(void)
185{
186	fprintf(stderr, "usage: renice [-n] increment [-gpu] id\n");
187	exit(1);
188}
189