1176732Sjeff/*
2176732Sjeff * Copyright (c) 2007, 2008 	Jeffrey Roberson <jeff@freebsd.org>
3176732Sjeff * All rights reserved.
4176732Sjeff *
5178093Sjeff * Copyright (c) 2008 Nokia Corporation
6178093Sjeff * All rights reserved.
7178093Sjeff *
8176732Sjeff * Redistribution and use in source and binary forms, with or without
9176732Sjeff * modification, are permitted provided that the following conditions
10176732Sjeff * are met:
11176732Sjeff * 1. Redistributions of source code must retain the above copyright
12176732Sjeff *    notice, this list of conditions and the following disclaimer.
13176732Sjeff * 2. Redistributions in binary form must reproduce the above copyright
14176732Sjeff *    notice, this list of conditions and the following disclaimer in the
15176732Sjeff *    documentation and/or other materials provided with the distribution.
16176732Sjeff *
17176732Sjeff * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18176732Sjeff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19176732Sjeff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20176732Sjeff * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21176732Sjeff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22176732Sjeff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23176732Sjeff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24176732Sjeff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25176732Sjeff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26176732Sjeff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27176732Sjeff * SUCH DAMAGE.
28176732Sjeff */
29176732Sjeff
30176732Sjeff#include <sys/cdefs.h>
31176732Sjeff__FBSDID("$FreeBSD: stable/10/usr.bin/cpuset/cpuset.c 336039 2018-07-06 19:10:07Z jamie $");
32176732Sjeff
33176732Sjeff#include <sys/param.h>
34176732Sjeff#include <sys/types.h>
35176732Sjeff#include <sys/time.h>
36176732Sjeff#include <sys/resource.h>
37176732Sjeff#include <sys/cpuset.h>
38176732Sjeff
39176732Sjeff#include <ctype.h>
40176732Sjeff#include <err.h>
41176732Sjeff#include <errno.h>
42336039Sjamie#include <jail.h>
43176732Sjeff#include <limits.h>
44176732Sjeff#include <stdio.h>
45176732Sjeff#include <stdlib.h>
46176732Sjeff#include <stdint.h>
47176732Sjeff#include <unistd.h>
48200462Sdelphij#include <string.h>
49176732Sjeff
50227160Sedstatic int Cflag;
51227160Sedstatic int cflag;
52227160Sedstatic int gflag;
53227160Sedstatic int iflag;
54227160Sedstatic int jflag;
55227160Sedstatic int lflag;
56227160Sedstatic int pflag;
57227160Sedstatic int rflag;
58227160Sedstatic int sflag;
59227160Sedstatic int tflag;
60227160Sedstatic int xflag;
61227160Sedstatic id_t id;
62227160Sedstatic cpulevel_t level;
63227160Sedstatic cpuwhich_t which;
64176732Sjeff
65227160Sedstatic void usage(void);
66176732Sjeff
67176732Sjeffstatic void printset(cpuset_t *mask);
68176732Sjeff
69176732Sjeffstatic void
70176732Sjeffparselist(char *list, cpuset_t *mask)
71176732Sjeff{
72176732Sjeff	enum { NONE, NUM, DASH } state;
73176732Sjeff	int lastnum;
74176732Sjeff	int curnum;
75176732Sjeff	char *l;
76176732Sjeff
77217416Sjhb	if (strcasecmp(list, "all") == 0) {
78217416Sjhb		if (cpuset_getaffinity(CPU_LEVEL_ROOT, CPU_WHICH_PID, -1,
79217416Sjhb		    sizeof(*mask), mask) != 0)
80217416Sjhb			err(EXIT_FAILURE, "getaffinity");
81217416Sjhb		return;
82217416Sjhb	}
83176732Sjeff	state = NONE;
84176732Sjeff	curnum = lastnum = 0;
85176732Sjeff	for (l = list; *l != '\0';) {
86176732Sjeff		if (isdigit(*l)) {
87176732Sjeff			curnum = atoi(l);
88176732Sjeff			if (curnum > CPU_SETSIZE)
89176732Sjeff				errx(EXIT_FAILURE,
90176732Sjeff				    "Only %d cpus supported", CPU_SETSIZE);
91176732Sjeff			while (isdigit(*l))
92176732Sjeff				l++;
93176732Sjeff			switch (state) {
94176732Sjeff			case NONE:
95176732Sjeff				lastnum = curnum;
96176732Sjeff				state = NUM;
97176732Sjeff				break;
98176732Sjeff			case DASH:
99176732Sjeff				for (; lastnum <= curnum; lastnum++)
100176732Sjeff					CPU_SET(lastnum, mask);
101176732Sjeff				state = NONE;
102176732Sjeff				break;
103176732Sjeff			case NUM:
104176732Sjeff			default:
105176732Sjeff				goto parserr;
106176732Sjeff			}
107176732Sjeff			continue;
108176732Sjeff		}
109176732Sjeff		switch (*l) {
110176732Sjeff		case ',':
111176732Sjeff			switch (state) {
112176732Sjeff			case NONE:
113176732Sjeff				break;
114176732Sjeff			case NUM:
115176732Sjeff				CPU_SET(curnum, mask);
116176732Sjeff				state = NONE;
117176732Sjeff				break;
118176732Sjeff			case DASH:
119176732Sjeff				goto parserr;
120176732Sjeff				break;
121176732Sjeff			}
122176732Sjeff			break;
123176732Sjeff		case '-':
124176732Sjeff			if (state != NUM)
125176732Sjeff				goto parserr;
126176732Sjeff			state = DASH;
127176732Sjeff			break;
128176732Sjeff		default:
129176732Sjeff			goto parserr;
130176732Sjeff		}
131176732Sjeff		l++;
132176732Sjeff	}
133176732Sjeff	switch (state) {
134176732Sjeff		case NONE:
135176732Sjeff			break;
136176732Sjeff		case NUM:
137176732Sjeff			CPU_SET(curnum, mask);
138176732Sjeff			break;
139176732Sjeff		case DASH:
140176732Sjeff			goto parserr;
141176732Sjeff	}
142176732Sjeff	return;
143176732Sjeffparserr:
144176811Sjeff	errx(EXIT_FAILURE, "Malformed cpu-list %s", list);
145176732Sjeff}
146176732Sjeff
147176732Sjeffstatic void
148176732Sjeffprintset(cpuset_t *mask)
149176732Sjeff{
150176732Sjeff	int once;
151176732Sjeff	int cpu;
152176732Sjeff
153176732Sjeff	for (once = 0, cpu = 0; cpu < CPU_SETSIZE; cpu++) {
154176732Sjeff		if (CPU_ISSET(cpu, mask)) {
155176732Sjeff			if (once == 0) {
156176732Sjeff				printf("%d", cpu);
157176732Sjeff				once = 1;
158176732Sjeff			} else
159176732Sjeff				printf(", %d", cpu);
160176732Sjeff		}
161176732Sjeff	}
162176732Sjeff	printf("\n");
163176732Sjeff}
164176732Sjeff
165227160Sedstatic const char *whichnames[] = { NULL, "tid", "pid", "cpuset", "irq", "jail" };
166227160Sedstatic const char *levelnames[] = { NULL, " root", " cpuset", "" };
167176732Sjeff
168176732Sjeffstatic void
169176732Sjeffprintaffinity(void)
170176732Sjeff{
171176732Sjeff	cpuset_t mask;
172176732Sjeff
173176811Sjeff	if (cpuset_getaffinity(level, which, id, sizeof(mask), &mask) != 0)
174176732Sjeff		err(EXIT_FAILURE, "getaffinity");
175176732Sjeff	printf("%s %jd%s mask: ", whichnames[which], (intmax_t)id,
176176732Sjeff	    levelnames[level]);
177176732Sjeff	printset(&mask);
178176732Sjeff	exit(EXIT_SUCCESS);
179176732Sjeff}
180176732Sjeff
181176732Sjeffstatic void
182176732Sjeffprintsetid(void)
183176732Sjeff{
184176732Sjeff	cpusetid_t setid;
185176732Sjeff
186176732Sjeff	/*
187176732Sjeff	 * Only LEVEL_WHICH && WHICH_CPUSET has a numbered id.
188176732Sjeff	 */
189176732Sjeff	if (level == CPU_LEVEL_WHICH && !sflag)
190176732Sjeff		level = CPU_LEVEL_CPUSET;
191176732Sjeff	if (cpuset_getid(level, which, id, &setid))
192176732Sjeff		err(errno, "getid");
193176732Sjeff	printf("%s %jd%s id: %d\n", whichnames[which], (intmax_t)id,
194176732Sjeff	    levelnames[level], setid);
195176732Sjeff}
196176732Sjeff
197176732Sjeffint
198176732Sjeffmain(int argc, char *argv[])
199176732Sjeff{
200176732Sjeff	cpusetid_t setid;
201176732Sjeff	cpuset_t mask;
202176732Sjeff	lwpid_t tid;
203176732Sjeff	pid_t pid;
204176732Sjeff	int ch;
205176732Sjeff
206176732Sjeff	CPU_ZERO(&mask);
207176732Sjeff	level = CPU_LEVEL_WHICH;
208176732Sjeff	which = CPU_WHICH_PID;
209176732Sjeff	id = pid = tid = setid = -1;
210217416Sjhb	while ((ch = getopt(argc, argv, "Ccgij:l:p:rs:t:x:")) != -1) {
211176732Sjeff		switch (ch) {
212217416Sjhb		case 'C':
213217416Sjhb			Cflag = 1;
214217416Sjhb			break;
215176732Sjeff		case 'c':
216176732Sjeff			if (rflag)
217176732Sjeff				usage();
218176732Sjeff			cflag = 1;
219176732Sjeff			level = CPU_LEVEL_CPUSET;
220176732Sjeff			break;
221176732Sjeff		case 'g':
222176732Sjeff			gflag = 1;
223176732Sjeff			break;
224176732Sjeff		case 'i':
225176732Sjeff			iflag = 1;
226176732Sjeff			break;
227185435Sbz		case 'j':
228185435Sbz			jflag = 1;
229185435Sbz			which = CPU_WHICH_JAIL;
230336039Sjamie			id = jail_getid(optarg);
231336039Sjamie			if (id < 0)
232336039Sjamie				errx(EXIT_FAILURE, "%s", jail_errmsg);
233185435Sbz			break;
234176732Sjeff		case 'l':
235176732Sjeff			lflag = 1;
236176732Sjeff			parselist(optarg, &mask);
237176732Sjeff			break;
238176732Sjeff		case 'p':
239176732Sjeff			pflag = 1;
240176732Sjeff			which = CPU_WHICH_PID;
241176732Sjeff			id = pid = atoi(optarg);
242176732Sjeff			break;
243176732Sjeff		case 'r':
244176732Sjeff			if (cflag)
245176732Sjeff				usage();
246176732Sjeff			level = CPU_LEVEL_ROOT;
247176732Sjeff			rflag = 1;
248176732Sjeff			break;
249176732Sjeff		case 's':
250176732Sjeff			sflag = 1;
251176732Sjeff			which = CPU_WHICH_CPUSET;
252176732Sjeff			id = setid = atoi(optarg);
253176732Sjeff			break;
254176732Sjeff		case 't':
255176732Sjeff			tflag = 1;
256176732Sjeff			which = CPU_WHICH_TID;
257176732Sjeff			id = tid = atoi(optarg);
258176732Sjeff			break;
259178093Sjeff		case 'x':
260178093Sjeff			xflag = 1;
261178093Sjeff			which = CPU_WHICH_IRQ;
262178093Sjeff			id = atoi(optarg);
263178093Sjeff			break;
264176732Sjeff		default:
265176732Sjeff			usage();
266176732Sjeff		}
267176732Sjeff	}
268176732Sjeff	argc -= optind;
269176732Sjeff	argv += optind;
270176732Sjeff	if (gflag) {
271217416Sjhb		if (argc || Cflag || lflag)
272176732Sjeff			usage();
273176732Sjeff		/* Only one identity specifier. */
274185435Sbz		if (jflag + xflag + sflag + pflag + tflag > 1)
275176732Sjeff			usage();
276176732Sjeff		if (iflag)
277176732Sjeff			printsetid();
278176732Sjeff		else
279176732Sjeff			printaffinity();
280176732Sjeff		exit(EXIT_SUCCESS);
281176732Sjeff	}
282176812Sjeff	if (iflag)
283176812Sjeff		usage();
284176732Sjeff	/*
285176732Sjeff	 * The user wants to run a command with a set and possibly cpumask.
286176732Sjeff	 */
287176732Sjeff	if (argc) {
288217416Sjhb		if (Cflag | pflag | rflag | tflag | xflag | jflag)
289176732Sjeff			usage();
290176732Sjeff		if (sflag) {
291176732Sjeff			if (cpuset_setid(CPU_WHICH_PID, -1, setid))
292176732Sjeff				err(argc, "setid");
293176812Sjeff		} else {
294176732Sjeff			if (cpuset(&setid))
295176732Sjeff				err(argc, "newid");
296176732Sjeff		}
297176732Sjeff		if (lflag) {
298177131Sjeff			if (cpuset_setaffinity(level, CPU_WHICH_PID,
299176812Sjeff			    -1, sizeof(mask), &mask) != 0)
300176732Sjeff				err(EXIT_FAILURE, "setaffinity");
301176732Sjeff		}
302176732Sjeff		errno = 0;
303176732Sjeff		execvp(*argv, argv);
304176732Sjeff		err(errno == ENOENT ? 127 : 126, "%s", *argv);
305176732Sjeff	}
306176732Sjeff	/*
307176732Sjeff	 * We're modifying something that presently exists.
308176732Sjeff	 */
309217416Sjhb	if (Cflag && (sflag || rflag || !pflag || tflag || xflag || jflag))
310217416Sjhb		usage();
311176732Sjeff	if (!lflag && (cflag || rflag))
312176732Sjeff		usage();
313217416Sjhb	if (!lflag && !(Cflag || sflag))
314176732Sjeff		usage();
315176732Sjeff	/* You can only set a mask on a thread. */
316185435Sbz	if (tflag && (sflag | pflag | xflag | jflag))
317176732Sjeff		usage();
318178093Sjeff	/* You can only set a mask on an irq. */
319185435Sbz	if (xflag && (jflag | pflag | sflag | tflag))
320178093Sjeff		usage();
321217416Sjhb	if (Cflag) {
322217416Sjhb		/*
323217416Sjhb		 * Create a new cpuset and move the specified process
324217416Sjhb		 * into the set.
325217416Sjhb		 */
326217416Sjhb		if (cpuset(&setid) < 0)
327217416Sjhb			err(EXIT_FAILURE, "newid");
328217416Sjhb		sflag = 1;
329217416Sjhb	}
330176732Sjeff	if (pflag && sflag) {
331176732Sjeff		if (cpuset_setid(CPU_WHICH_PID, pid, setid))
332176732Sjeff			err(EXIT_FAILURE, "setid");
333176732Sjeff		/*
334176732Sjeff		 * If the user specifies a set and a list we want the mask
335176732Sjeff		 * to effect the pid and not the set.
336176732Sjeff		 */
337176732Sjeff		which = CPU_WHICH_PID;
338176732Sjeff		id = pid;
339176732Sjeff	}
340176732Sjeff	if (lflag) {
341176811Sjeff		if (cpuset_setaffinity(level, which, id, sizeof(mask),
342176732Sjeff		    &mask) != 0)
343176732Sjeff			err(EXIT_FAILURE, "setaffinity");
344176732Sjeff	}
345176732Sjeff
346176732Sjeff	exit(EXIT_SUCCESS);
347176732Sjeff}
348176732Sjeff
349227160Sedstatic void
350176732Sjeffusage(void)
351176732Sjeff{
352176732Sjeff
353176732Sjeff	fprintf(stderr,
354176812Sjeff	    "usage: cpuset [-l cpu-list] [-s setid] cmd ...\n");
355176732Sjeff	fprintf(stderr,
356176811Sjeff	    "       cpuset [-l cpu-list] [-s setid] -p pid\n");
357176732Sjeff	fprintf(stderr,
358217416Sjhb	    "       cpuset [-c] [-l cpu-list] -C -p pid\n");
359217416Sjhb	fprintf(stderr,
360185435Sbz	    "       cpuset [-cr] [-l cpu-list] [-j jailid | -p pid | -t tid | -s setid | -x irq]\n");
361176732Sjeff	fprintf(stderr,
362185435Sbz	    "       cpuset [-cgir] [-j jailid | -p pid | -t tid | -s setid | -x irq]\n");
363176732Sjeff	exit(1);
364176732Sjeff}
365