]> git.cameronkatri.com Git - apple_cmds.git/blob - system_cmds/taskpolicy.tproj/taskpolicy.c
5bc96844ba2ef55e41b0c666b55bbb69e678b4bf
[apple_cmds.git] / system_cmds / taskpolicy.tproj / taskpolicy.c
1 /*
2 * Copyright (c) 2013-2016 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License'). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
20 * under the License."
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25 #include <System/sys/proc.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <string.h>
30 #include <sys/resource.h>
31 #include <err.h>
32 #include <sys/errno.h>
33 #include <stdbool.h>
34 #include <sysexits.h>
35 #include <mach/mach.h>
36 #include <mach/task_policy.h>
37
38 #include <spawn.h>
39 #include <spawn_private.h>
40 #include <sys/spawn_internal.h>
41
42 #define QOS_PARAMETER_LATENCY 0
43 #define QOS_PARAMETER_THROUGHPUT 1
44
45 extern char **environ;
46
47 static void usage(void);
48 static int parse_disk_policy(const char *strpolicy);
49 static int parse_qos_tier(const char *strpolicy, int parameter);
50 static uint64_t parse_qos_clamp(const char *qos_string);
51
52 int main(int argc, char * argv[])
53 {
54 int ch, ret;
55 pid_t pid = 0;
56 posix_spawnattr_t attr;
57 extern char **environ;
58 bool flagx = false, flagX = false, flagb = false, flagB = false, flaga = false;
59 int flagd = -1, flagg = -1;
60 struct task_qos_policy qosinfo = { LATENCY_QOS_TIER_UNSPECIFIED, THROUGHPUT_QOS_TIER_UNSPECIFIED };
61 uint64_t qos_clamp = POSIX_SPAWN_PROC_CLAMP_NONE;
62
63 while ((ch = getopt(argc, argv, "xXbBd:g:c:t:l:p:a")) != -1) {
64 switch (ch) {
65 case 'x':
66 flagx = true;
67 break;
68 case 'X':
69 flagX = true;
70 break;
71 case 'b':
72 flagb = true;
73 break;
74 case 'B':
75 flagB = true;
76 break;
77 case 'd':
78 flagd = parse_disk_policy(optarg);
79 if (flagd == -1) {
80 warnx("Could not parse '%s' as a disk policy", optarg);
81 usage();
82 }
83 break;
84 case 'g':
85 flagg = parse_disk_policy(optarg);
86 if (flagg == -1) {
87 warnx("Could not parse '%s' as a disk policy", optarg);
88 usage();
89 }
90 break;
91 case 'c':
92 qos_clamp = parse_qos_clamp(optarg);
93 if (qos_clamp == POSIX_SPAWN_PROC_CLAMP_NONE) {
94 warnx("Could not parse '%s' as a QoS clamp", optarg);
95 usage();
96 }
97 break;
98 case 't':
99 qosinfo.task_throughput_qos_tier = parse_qos_tier(optarg, QOS_PARAMETER_THROUGHPUT);
100 if (qosinfo.task_throughput_qos_tier == -1) {
101 warnx("Could not parse '%s' as a qos tier", optarg);
102 usage();
103 }
104 break;
105 case 'l':
106 qosinfo.task_latency_qos_tier = parse_qos_tier(optarg, QOS_PARAMETER_LATENCY);
107 if (qosinfo.task_latency_qos_tier == -1) {
108 warnx("Could not parse '%s' as a qos tier", optarg);
109 usage();
110 }
111 break;
112 case 'p':
113 pid = atoi(optarg);
114 if (pid == 0) {
115 warnx("Invalid pid '%s' specified", optarg);
116 usage();
117 }
118 break;
119 case 'a':
120 flaga = true;
121 break;
122 case '?':
123 default:
124 usage();
125 }
126 }
127 argc -= optind;
128 argv += optind;
129
130 if (pid == 0 && argc == 0) {
131 usage();
132 }
133
134 if (pid != 0 && (flagx || flagX || flagg != -1 || flagd != -1)) {
135 warnx("Incompatible option(s) used with -p");
136 usage();
137 }
138
139 if (flagx && flagX){
140 warnx("Incompatible options -x, -X");
141 usage();
142 }
143
144 if (flagb && flagB) {
145 warnx("Incompatible options -b, -B");
146 usage();
147 }
148
149 if (flagB && pid == 0) {
150 warnx("The -B option can only be used with the -p option");
151 usage();
152 }
153
154 if (flagx) {
155 ret = setiopolicy_np(IOPOL_TYPE_VFS_HFS_CASE_SENSITIVITY, IOPOL_SCOPE_PROCESS, IOPOL_VFS_HFS_CASE_SENSITIVITY_FORCE_CASE_SENSITIVE);
156 if (ret == -1) {
157 err(EX_SOFTWARE, "setiopolicy_np(IOPOL_TYPE_VFS_HFS_CASE_SENSITIVITY...)");
158 }
159 }
160
161 if (flagX) {
162 ret = setiopolicy_np(IOPOL_TYPE_VFS_HFS_CASE_SENSITIVITY, IOPOL_SCOPE_PROCESS, IOPOL_VFS_HFS_CASE_SENSITIVITY_DEFAULT);
163 if (ret == -1) {
164 err(EX_SOFTWARE, "setiopolicy_np(IOPOL_TYPE_VFS_HFS_CASE_SENSITIVITY...)");
165 }
166 }
167
168 if (flagb) {
169 ret = setpriority(PRIO_DARWIN_PROCESS, pid, PRIO_DARWIN_BG);
170 if (ret == -1) {
171 err(EX_SOFTWARE, "setpriority()");
172 }
173 }
174
175 if (flagB) {
176 ret = setpriority(PRIO_DARWIN_PROCESS, pid, 0);
177 if (ret == -1) {
178 err(EX_SOFTWARE, "setpriority()");
179 }
180 }
181
182 if (flagd >= 0) {
183 ret = setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, flagd);
184 if (ret == -1) {
185 err(EX_SOFTWARE, "setiopolicy_np(...IOPOL_SCOPE_PROCESS...)");
186 }
187 }
188
189 if (flagg >= 0){
190 ret = setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_DARWIN_BG, flagg);
191 if (ret == -1) {
192 err(EX_SOFTWARE, "setiopolicy_np(...IOPOL_SCOPE_DARWIN_BG...)");
193 }
194 }
195
196 if (qosinfo.task_latency_qos_tier != LATENCY_QOS_TIER_UNSPECIFIED ||
197 qosinfo.task_throughput_qos_tier != THROUGHPUT_QOS_TIER_UNSPECIFIED){
198 mach_port_t task;
199 if (pid) {
200 ret = task_name_for_pid(mach_task_self(), pid, &task);
201 if (ret != KERN_SUCCESS) {
202 err(EX_SOFTWARE, "task_name_for_pid(%d) failed", pid);
203 return EX_OSERR;
204 }
205 } else {
206 task = mach_task_self();
207 }
208 ret = task_policy_set((task_t)task, TASK_OVERRIDE_QOS_POLICY, (task_policy_t)&qosinfo, TASK_QOS_POLICY_COUNT);
209 if (ret != KERN_SUCCESS){
210 err(EX_SOFTWARE, "task_policy_set(...TASK_OVERRIDE_QOS_POLICY...)");
211 }
212 }
213
214 if (pid != 0)
215 return 0;
216
217 ret = posix_spawnattr_init(&attr);
218 if (ret != 0) errc(EX_NOINPUT, ret, "posix_spawnattr_init");
219
220 ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETEXEC);
221 if (ret != 0) errc(EX_NOINPUT, ret, "posix_spawnattr_setflags");
222
223 if (qos_clamp != POSIX_SPAWN_PROC_CLAMP_NONE) {
224 ret = posix_spawnattr_set_qos_clamp_np(&attr, qos_clamp);
225 if (ret != 0) errc(EX_NOINPUT, ret, "posix_spawnattr_set_qos_clamp_np");
226 }
227
228 if (flaga) {
229 ret = posix_spawnattr_setprocesstype_np(&attr, POSIX_SPAWN_PROC_TYPE_APP_DEFAULT);
230 if (ret != 0) errc(EX_NOINPUT, ret, "posix_spawnattr_setprocesstype_np");
231
232 ret = posix_spawnattr_set_darwin_role_np(&attr, PRIO_DARWIN_ROLE_UI);
233 if (ret != 0) errc(EX_NOINPUT, ret, "posix_spawnattr_set_darwin_role_np");
234 }
235
236 ret = posix_spawnp(&pid, argv[0], NULL, &attr, argv, environ);
237 if (ret != 0) errc(EX_NOINPUT, ret, "posix_spawn");
238
239 return EX_OSERR;
240 }
241
242 static void usage(void)
243 {
244 fprintf(stderr, "Usage: %s [-x|-X] [-d <policy>] [-g policy] [-c clamp] [-b] [-t <tier>]\n"
245 " [-l <tier>] [-a] <program> [<pargs> [...]]\n", getprogname());
246 fprintf(stderr, " %s [-b|-B] [-t <tier>] [-l <tier>] -p pid\n", getprogname());
247 exit(EX_USAGE);
248 }
249
250 static int parse_disk_policy(const char *strpolicy)
251 {
252 long policy;
253 char *endptr = NULL;
254
255 /* first try as an integer */
256 policy = strtol(strpolicy, &endptr, 0);
257 if (endptr && (endptr[0] == '\0') && (strpolicy[0] != '\0')) {
258 /* parsed complete string as a number */
259 return (int)policy;
260 }
261
262 if (0 == strcasecmp(strpolicy, "DEFAULT") ) {
263 return IOPOL_DEFAULT;
264 } else if (0 == strcasecmp(strpolicy, "IMPORTANT")) {
265 return IOPOL_IMPORTANT;
266 } else if (0 == strcasecmp(strpolicy, "PASSIVE")) {
267 return IOPOL_PASSIVE;
268 } else if (0 == strcasecmp(strpolicy, "THROTTLE")) {
269 return IOPOL_THROTTLE;
270 } else if (0 == strcasecmp(strpolicy, "UTILITY")) {
271 return IOPOL_UTILITY;
272 } else if (0 == strcasecmp(strpolicy, "STANDARD")) {
273 return IOPOL_STANDARD;
274 } else {
275 return -1;
276 }
277 }
278
279 static int parse_qos_tier(const char *strtier, int parameter){
280 long policy;
281 char *endptr = NULL;
282
283 /* first try as an integer */
284 policy = strtol(strtier, &endptr, 0);
285 if (endptr && (endptr[0] == '\0') && (strtier[0] != '\0')) {
286 switch (policy) {
287 case 0:
288 return parameter ? THROUGHPUT_QOS_TIER_0 : LATENCY_QOS_TIER_0;
289 break;
290 case 1:
291 return parameter ? THROUGHPUT_QOS_TIER_1 : LATENCY_QOS_TIER_1;
292 break;
293 case 2:
294 return parameter ? THROUGHPUT_QOS_TIER_2 : LATENCY_QOS_TIER_2;
295 break;
296 case 3:
297 return parameter ? THROUGHPUT_QOS_TIER_3 : LATENCY_QOS_TIER_3;
298 break;
299 case 4:
300 return parameter ? THROUGHPUT_QOS_TIER_4 : LATENCY_QOS_TIER_4;
301 break;
302 case 5:
303 return parameter ? THROUGHPUT_QOS_TIER_5 : LATENCY_QOS_TIER_5;
304 break;
305 default:
306 return -1;
307 break;
308 }
309 }
310
311 return -1;
312 }
313
314 static uint64_t parse_qos_clamp(const char *qos_string) {
315
316 if (0 == strcasecmp(qos_string, "utility") ) {
317 return POSIX_SPAWN_PROC_CLAMP_UTILITY;
318 } else if (0 == strcasecmp(qos_string, "background")) {
319 return POSIX_SPAWN_PROC_CLAMP_BACKGROUND;
320 } else if (0 == strcasecmp(qos_string, "maintenance")) {
321 return POSIX_SPAWN_PROC_CLAMP_MAINTENANCE;
322 } else {
323 return POSIX_SPAWN_PROC_CLAMP_NONE;
324 }
325 }