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$");
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>
42176732Sjeff#include <limits.h>
43176732Sjeff#include <stdio.h>
44176732Sjeff#include <stdlib.h>
45176732Sjeff#include <stdint.h>
46176732Sjeff#include <unistd.h>
47200462Sdelphij#include <string.h>
48176732Sjeff
49227160Sedstatic int Cflag;
50227160Sedstatic int cflag;
51227160Sedstatic int gflag;
52227160Sedstatic int iflag;
53227160Sedstatic int jflag;
54227160Sedstatic int lflag;
55227160Sedstatic int pflag;
56227160Sedstatic int rflag;
57227160Sedstatic int sflag;
58227160Sedstatic int tflag;
59227160Sedstatic int xflag;
60227160Sedstatic id_t id;
61227160Sedstatic cpulevel_t level;
62227160Sedstatic cpuwhich_t which;
63176732Sjeff
64227160Sedstatic void usage(void);
65176732Sjeff
66176732Sjeffstatic void printset(cpuset_t *mask);
67176732Sjeff
68176732Sjeffstatic void
69176732Sjeffparselist(char *list, cpuset_t *mask)
70176732Sjeff{
71176732Sjeff	enum { NONE, NUM, DASH } state;
72176732Sjeff	int lastnum;
73176732Sjeff	int curnum;
74176732Sjeff	char *l;
75176732Sjeff
76217416Sjhb	if (strcasecmp(list, "all") == 0) {
77217416Sjhb		if (cpuset_getaffinity(CPU_LEVEL_ROOT, CPU_WHICH_PID, -1,
78217416Sjhb		    sizeof(*mask), mask) != 0)
79217416Sjhb			err(EXIT_FAILURE, "getaffinity");
80217416Sjhb		return;
81217416Sjhb	}
82176732Sjeff	state = NONE;
83176732Sjeff	curnum = lastnum = 0;
84176732Sjeff	for (l = list; *l != '\0';) {
85176732Sjeff		if (isdigit(*l)) {
86176732Sjeff			curnum = atoi(l);
87176732Sjeff			if (curnum > CPU_SETSIZE)
88176732Sjeff				errx(EXIT_FAILURE,
89176732Sjeff				    "Only %d cpus supported", CPU_SETSIZE);
90176732Sjeff			while (isdigit(*l))
91176732Sjeff				l++;
92176732Sjeff			switch (state) {
93176732Sjeff			case NONE:
94176732Sjeff				lastnum = curnum;
95176732Sjeff				state = NUM;
96176732Sjeff				break;
97176732Sjeff			case DASH:
98176732Sjeff				for (; lastnum <= curnum; lastnum++)
99176732Sjeff					CPU_SET(lastnum, mask);
100176732Sjeff				state = NONE;
101176732Sjeff				break;
102176732Sjeff			case NUM:
103176732Sjeff			default:
104176732Sjeff				goto parserr;
105176732Sjeff			}
106176732Sjeff			continue;
107176732Sjeff		}
108176732Sjeff		switch (*l) {
109176732Sjeff		case ',':
110176732Sjeff			switch (state) {
111176732Sjeff			case NONE:
112176732Sjeff				break;
113176732Sjeff			case NUM:
114176732Sjeff				CPU_SET(curnum, mask);
115176732Sjeff				state = NONE;
116176732Sjeff				break;
117176732Sjeff			case DASH:
118176732Sjeff				goto parserr;
119176732Sjeff				break;
120176732Sjeff			}
121176732Sjeff			break;
122176732Sjeff		case '-':
123176732Sjeff			if (state != NUM)
124176732Sjeff				goto parserr;
125176732Sjeff			state = DASH;
126176732Sjeff			break;
127176732Sjeff		default:
128176732Sjeff			goto parserr;
129176732Sjeff		}
130176732Sjeff		l++;
131176732Sjeff	}
132176732Sjeff	switch (state) {
133176732Sjeff		case NONE:
134176732Sjeff			break;
135176732Sjeff		case NUM:
136176732Sjeff			CPU_SET(curnum, mask);
137176732Sjeff			break;
138176732Sjeff		case DASH:
139176732Sjeff			goto parserr;
140176732Sjeff	}
141176732Sjeff	return;
142176732Sjeffparserr:
143176811Sjeff	errx(EXIT_FAILURE, "Malformed cpu-list %s", list);
144176732Sjeff}
145176732Sjeff
146176732Sjeffstatic void
147176732Sjeffprintset(cpuset_t *mask)
148176732Sjeff{
149176732Sjeff	int once;
150176732Sjeff	int cpu;
151176732Sjeff
152176732Sjeff	for (once = 0, cpu = 0; cpu < CPU_SETSIZE; cpu++) {
153176732Sjeff		if (CPU_ISSET(cpu, mask)) {
154176732Sjeff			if (once == 0) {
155176732Sjeff				printf("%d", cpu);
156176732Sjeff				once = 1;
157176732Sjeff			} else
158176732Sjeff				printf(", %d", cpu);
159176732Sjeff		}
160176732Sjeff	}
161176732Sjeff	printf("\n");
162176732Sjeff}
163176732Sjeff
164227160Sedstatic const char *whichnames[] = { NULL, "tid", "pid", "cpuset", "irq", "jail" };
165227160Sedstatic const char *levelnames[] = { NULL, " root", " cpuset", "" };
166176732Sjeff
167176732Sjeffstatic void
168176732Sjeffprintaffinity(void)
169176732Sjeff{
170176732Sjeff	cpuset_t mask;
171176732Sjeff
172176811Sjeff	if (cpuset_getaffinity(level, which, id, sizeof(mask), &mask) != 0)
173176732Sjeff		err(EXIT_FAILURE, "getaffinity");
174176732Sjeff	printf("%s %jd%s mask: ", whichnames[which], (intmax_t)id,
175176732Sjeff	    levelnames[level]);
176176732Sjeff	printset(&mask);
177176732Sjeff	exit(EXIT_SUCCESS);
178176732Sjeff}
179176732Sjeff
180176732Sjeffstatic void
181176732Sjeffprintsetid(void)
182176732Sjeff{
183176732Sjeff	cpusetid_t setid;
184176732Sjeff
185176732Sjeff	/*
186176732Sjeff	 * Only LEVEL_WHICH && WHICH_CPUSET has a numbered id.
187176732Sjeff	 */
188176732Sjeff	if (level == CPU_LEVEL_WHICH && !sflag)
189176732Sjeff		level = CPU_LEVEL_CPUSET;
190176732Sjeff	if (cpuset_getid(level, which, id, &setid))
191176732Sjeff		err(errno, "getid");
192176732Sjeff	printf("%s %jd%s id: %d\n", whichnames[which], (intmax_t)id,
193176732Sjeff	    levelnames[level], setid);
194176732Sjeff}
195176732Sjeff
196176732Sjeffint
197176732Sjeffmain(int argc, char *argv[])
198176732Sjeff{
199176732Sjeff	cpusetid_t setid;
200176732Sjeff	cpuset_t mask;
201176732Sjeff	lwpid_t tid;
202176732Sjeff	pid_t pid;
203176732Sjeff	int ch;
204176732Sjeff
205176732Sjeff	CPU_ZERO(&mask);
206176732Sjeff	level = CPU_LEVEL_WHICH;
207176732Sjeff	which = CPU_WHICH_PID;
208176732Sjeff	id = pid = tid = setid = -1;
209217416Sjhb	while ((ch = getopt(argc, argv, "Ccgij:l:p:rs:t:x:")) != -1) {
210176732Sjeff		switch (ch) {
211217416Sjhb		case 'C':
212217416Sjhb			Cflag = 1;
213217416Sjhb			break;
214176732Sjeff		case 'c':
215176732Sjeff			if (rflag)
216176732Sjeff				usage();
217176732Sjeff			cflag = 1;
218176732Sjeff			level = CPU_LEVEL_CPUSET;
219176732Sjeff			break;
220176732Sjeff		case 'g':
221176732Sjeff			gflag = 1;
222176732Sjeff			break;
223176732Sjeff		case 'i':
224176732Sjeff			iflag = 1;
225176732Sjeff			break;
226185435Sbz		case 'j':
227185435Sbz			jflag = 1;
228185435Sbz			which = CPU_WHICH_JAIL;
229185435Sbz			id = atoi(optarg);
230185435Sbz			break;
231176732Sjeff		case 'l':
232176732Sjeff			lflag = 1;
233176732Sjeff			parselist(optarg, &mask);
234176732Sjeff			break;
235176732Sjeff		case 'p':
236176732Sjeff			pflag = 1;
237176732Sjeff			which = CPU_WHICH_PID;
238176732Sjeff			id = pid = atoi(optarg);
239176732Sjeff			break;
240176732Sjeff		case 'r':
241176732Sjeff			if (cflag)
242176732Sjeff				usage();
243176732Sjeff			level = CPU_LEVEL_ROOT;
244176732Sjeff			rflag = 1;
245176732Sjeff			break;
246176732Sjeff		case 's':
247176732Sjeff			sflag = 1;
248176732Sjeff			which = CPU_WHICH_CPUSET;
249176732Sjeff			id = setid = atoi(optarg);
250176732Sjeff			break;
251176732Sjeff		case 't':
252176732Sjeff			tflag = 1;
253176732Sjeff			which = CPU_WHICH_TID;
254176732Sjeff			id = tid = atoi(optarg);
255176732Sjeff			break;
256178093Sjeff		case 'x':
257178093Sjeff			xflag = 1;
258178093Sjeff			which = CPU_WHICH_IRQ;
259178093Sjeff			id = atoi(optarg);
260178093Sjeff			break;
261176732Sjeff		default:
262176732Sjeff			usage();
263176732Sjeff		}
264176732Sjeff	}
265176732Sjeff	argc -= optind;
266176732Sjeff	argv += optind;
267176732Sjeff	if (gflag) {
268217416Sjhb		if (argc || Cflag || lflag)
269176732Sjeff			usage();
270176732Sjeff		/* Only one identity specifier. */
271185435Sbz		if (jflag + xflag + sflag + pflag + tflag > 1)
272176732Sjeff			usage();
273176732Sjeff		if (iflag)
274176732Sjeff			printsetid();
275176732Sjeff		else
276176732Sjeff			printaffinity();
277176732Sjeff		exit(EXIT_SUCCESS);
278176732Sjeff	}
279176812Sjeff	if (iflag)
280176812Sjeff		usage();
281176732Sjeff	/*
282176732Sjeff	 * The user wants to run a command with a set and possibly cpumask.
283176732Sjeff	 */
284176732Sjeff	if (argc) {
285217416Sjhb		if (Cflag | pflag | rflag | tflag | xflag | jflag)
286176732Sjeff			usage();
287176732Sjeff		if (sflag) {
288176732Sjeff			if (cpuset_setid(CPU_WHICH_PID, -1, setid))
289176732Sjeff				err(argc, "setid");
290176812Sjeff		} else {
291176732Sjeff			if (cpuset(&setid))
292176732Sjeff				err(argc, "newid");
293176732Sjeff		}
294176732Sjeff		if (lflag) {
295177131Sjeff			if (cpuset_setaffinity(level, CPU_WHICH_PID,
296176812Sjeff			    -1, sizeof(mask), &mask) != 0)
297176732Sjeff				err(EXIT_FAILURE, "setaffinity");
298176732Sjeff		}
299176732Sjeff		errno = 0;
300176732Sjeff		execvp(*argv, argv);
301176732Sjeff		err(errno == ENOENT ? 127 : 126, "%s", *argv);
302176732Sjeff	}
303176732Sjeff	/*
304176732Sjeff	 * We're modifying something that presently exists.
305176732Sjeff	 */
306217416Sjhb	if (Cflag && (sflag || rflag || !pflag || tflag || xflag || jflag))
307217416Sjhb		usage();
308176732Sjeff	if (!lflag && (cflag || rflag))
309176732Sjeff		usage();
310217416Sjhb	if (!lflag && !(Cflag || sflag))
311176732Sjeff		usage();
312176732Sjeff	/* You can only set a mask on a thread. */
313185435Sbz	if (tflag && (sflag | pflag | xflag | jflag))
314176732Sjeff		usage();
315178093Sjeff	/* You can only set a mask on an irq. */
316185435Sbz	if (xflag && (jflag | pflag | sflag | tflag))
317178093Sjeff		usage();
318217416Sjhb	if (Cflag) {
319217416Sjhb		/*
320217416Sjhb		 * Create a new cpuset and move the specified process
321217416Sjhb		 * into the set.
322217416Sjhb		 */
323217416Sjhb		if (cpuset(&setid) < 0)
324217416Sjhb			err(EXIT_FAILURE, "newid");
325217416Sjhb		sflag = 1;
326217416Sjhb	}
327176732Sjeff	if (pflag && sflag) {
328176732Sjeff		if (cpuset_setid(CPU_WHICH_PID, pid, setid))
329176732Sjeff			err(EXIT_FAILURE, "setid");
330176732Sjeff		/*
331176732Sjeff		 * If the user specifies a set and a list we want the mask
332176732Sjeff		 * to effect the pid and not the set.
333176732Sjeff		 */
334176732Sjeff		which = CPU_WHICH_PID;
335176732Sjeff		id = pid;
336176732Sjeff	}
337176732Sjeff	if (lflag) {
338176811Sjeff		if (cpuset_setaffinity(level, which, id, sizeof(mask),
339176732Sjeff		    &mask) != 0)
340176732Sjeff			err(EXIT_FAILURE, "setaffinity");
341176732Sjeff	}
342176732Sjeff
343176732Sjeff	exit(EXIT_SUCCESS);
344176732Sjeff}
345176732Sjeff
346227160Sedstatic void
347176732Sjeffusage(void)
348176732Sjeff{
349176732Sjeff
350176732Sjeff	fprintf(stderr,
351176812Sjeff	    "usage: cpuset [-l cpu-list] [-s setid] cmd ...\n");
352176732Sjeff	fprintf(stderr,
353176811Sjeff	    "       cpuset [-l cpu-list] [-s setid] -p pid\n");
354176732Sjeff	fprintf(stderr,
355217416Sjhb	    "       cpuset [-c] [-l cpu-list] -C -p pid\n");
356217416Sjhb	fprintf(stderr,
357185435Sbz	    "       cpuset [-cr] [-l cpu-list] [-j jailid | -p pid | -t tid | -s setid | -x irq]\n");
358176732Sjeff	fprintf(stderr,
359185435Sbz	    "       cpuset [-cgir] [-j jailid | -p pid | -t tid | -s setid | -x irq]\n");
360176732Sjeff	exit(1);
361176732Sjeff}
362