uname: use wider integer for option bits
[oweals/busybox.git] / miscutils / taskset.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * taskset - retrieve or set a processes' CPU affinity
4  * Copyright (c) 2006 Bernhard Reutner-Fischer
5  *
6  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
7  */
8
9 #include <sched.h>
10 #include "libbb.h"
11
12 #if ENABLE_FEATURE_TASKSET_FANCY
13 #define TASKSET_PRINTF_MASK "%s"
14 /* craft a string from the mask */
15 static char *from_cpuset(cpu_set_t *mask)
16 {
17         int i;
18         char *ret = NULL;
19         char *str = xzalloc((CPU_SETSIZE / 4) + 1); /* we will leak it */
20
21         for (i = CPU_SETSIZE - 4; i >= 0; i -= 4) {
22                 int val = 0;
23                 int off;
24                 for (off = 0; off <= 3; ++off)
25                         if (CPU_ISSET(i + off, mask))
26                                 val |= 1 << off;
27                 if (!ret && val)
28                         ret = str;
29                 *str++ = bb_hexdigits_upcase[val] | 0x20;
30         }
31         return ret;
32 }
33 #else
34 #define TASKSET_PRINTF_MASK "%llx"
35 static unsigned long long from_cpuset(cpu_set_t *mask)
36 {
37         struct BUG_CPU_SETSIZE_is_too_small {
38                 char BUG_CPU_SETSIZE_is_too_small[
39                         CPU_SETSIZE < sizeof(int) ? -1 : 1];
40         };
41         char *p = (void*)mask;
42
43         /* Take the least significant bits. Careful!
44          * Consider both CPU_SETSIZE=4 and CPU_SETSIZE=1024 cases
45          */
46 #if BB_BIG_ENDIAN
47         /* For big endian, it means LAST bits */
48         if (CPU_SETSIZE < sizeof(long))
49                 p += CPU_SETSIZE - sizeof(int);
50         else if (CPU_SETSIZE < sizeof(long long))
51                 p += CPU_SETSIZE - sizeof(long);
52         else
53                 p += CPU_SETSIZE - sizeof(long long);
54 #endif
55         if (CPU_SETSIZE < sizeof(long))
56                 return *(unsigned*)p;
57         if (CPU_SETSIZE < sizeof(long long))
58                 return *(unsigned long*)p;
59         return *(unsigned long long*)p;
60 }
61 #endif
62
63
64 int taskset_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
65 int taskset_main(int argc UNUSED_PARAM, char **argv)
66 {
67         cpu_set_t mask;
68         pid_t pid = 0;
69         unsigned opt_p;
70         const char *current_new;
71         char *pid_str;
72         char *aff = aff; /* for compiler */
73
74         /* NB: we mimic util-linux's taskset: -p does not take
75          * an argument, i.e., "-pN" is NOT valid, only "-p N"!
76          * Indeed, util-linux-2.13-pre7 uses:
77          * getopt_long(argc, argv, "+pchV", ...), not "...p:..." */
78
79         opt_complementary = "-1"; /* at least 1 arg */
80         opt_p = getopt32(argv, "+p");
81         argv += optind;
82
83         if (opt_p) {
84                 pid_str = *argv++;
85                 if (*argv) { /* "-p <aff> <pid> ...rest.is.ignored..." */
86                         aff = pid_str;
87                         pid_str = *argv; /* NB: *argv != NULL in this case */
88                 }
89                 /* else it was just "-p <pid>", and *argv == NULL */
90                 pid = xatoul_range(pid_str, 1, ((unsigned)(pid_t)ULONG_MAX) >> 1);
91         } else {
92                 aff = *argv++; /* <aff> <cmd...> */
93                 if (!*argv)
94                         bb_show_usage();
95         }
96
97         current_new = "current\0new";
98         if (opt_p) {
99  print_aff:
100                 if (sched_getaffinity(pid, sizeof(mask), &mask) < 0)
101                         bb_perror_msg_and_die("can't %cet pid %d's affinity", 'g', pid);
102                 printf("pid %d's %s affinity mask: "TASKSET_PRINTF_MASK"\n",
103                                 pid, current_new, from_cpuset(&mask));
104                 if (!*argv) {
105                         /* Either it was just "-p <pid>",
106                          * or it was "-p <aff> <pid>" and we came here
107                          * for the second time (see goto below) */
108                         return EXIT_SUCCESS;
109                 }
110                 *argv = NULL;
111                 current_new += 8; /* "new" */
112         }
113
114         { /* Affinity was specified, translate it into cpu_set_t */
115                 unsigned i;
116                 /* Do not allow zero mask: */
117                 unsigned long long m = xstrtoull_range(aff, 0, 1, ULLONG_MAX);
118                 enum { CNT_BIT = CPU_SETSIZE < sizeof(m)*8 ? CPU_SETSIZE : sizeof(m)*8 };
119
120                 CPU_ZERO(&mask);
121                 for (i = 0; i < CNT_BIT; i++) {
122                         unsigned long long bit = (1ULL << i);
123                         if (bit & m)
124                                 CPU_SET(i, &mask);
125                 }
126         }
127
128         /* Set pid's or our own (pid==0) affinity */
129         if (sched_setaffinity(pid, sizeof(mask), &mask))
130                 bb_perror_msg_and_die("can't %cet pid %d's affinity", 's', pid);
131
132         if (!*argv) /* "-p <aff> <pid> [...ignored...]" */
133                 goto print_aff; /* print new affinity and exit */
134
135         BB_EXECVP(*argv, argv);
136         bb_simple_perror_msg_and_die(*argv);
137 }