]> git.saurik.com Git - apple/system_cmds.git/blame - taskpolicy.tproj/taskpolicy.c
system_cmds-735.tar.gz
[apple/system_cmds.git] / taskpolicy.tproj / taskpolicy.c
CommitLineData
fc6d9e4b 1/*
cf37c299 2 * Copyright (c) 2013-2016 Apple Inc. All rights reserved.
fc6d9e4b
A
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 <stdio.h>
26#include <stdlib.h>
27#include <unistd.h>
28#include <string.h>
29#include <sys/resource.h>
30#include <err.h>
31#include <sys/errno.h>
32#include <stdbool.h>
33#include <sysexits.h>
34#include <mach/mach.h>
35#include <mach/task_policy.h>
36
1a7e3f61
A
37#include <spawn.h>
38#include <spawn_private.h>
39#include <sys/spawn_internal.h>
40
fc6d9e4b
A
41#define QOS_PARAMETER_LATENCY 0
42#define QOS_PARAMETER_THROUGHPUT 1
43
1a7e3f61
A
44extern char **environ;
45
fc6d9e4b
A
46static void usage(void);
47static int parse_disk_policy(const char *strpolicy);
48static int parse_qos_tier(const char *strpolicy, int parameter);
1a7e3f61 49static uint64_t parse_qos_clamp(const char *qos_string);
fc6d9e4b
A
50
51int main(int argc, char * argv[])
52{
53 int ch, ret;
1a7e3f61
A
54 pid_t pid = 0;
55 posix_spawnattr_t attr;
56 extern char **environ;
57 bool flagx = false, flagX = false, flagb = false, flagB = false;
fc6d9e4b
A
58 int flagd = -1, flagg = -1;
59 struct task_qos_policy qosinfo = { LATENCY_QOS_TIER_UNSPECIFIED, THROUGHPUT_QOS_TIER_UNSPECIFIED };
1a7e3f61 60 uint64_t qos_clamp = POSIX_SPAWN_PROC_CLAMP_NONE;
cf37c299 61
1a7e3f61 62 while ((ch = getopt(argc, argv, "xXbBd:g:c:t:l:p:")) != -1) {
fc6d9e4b
A
63 switch (ch) {
64 case 'x':
65 flagx = true;
66 break;
67 case 'X':
68 flagX = true;
69 break;
70 case 'b':
71 flagb = true;
72 break;
1a7e3f61
A
73 case 'B':
74 flagB = true;
75 break;
fc6d9e4b
A
76 case 'd':
77 flagd = parse_disk_policy(optarg);
78 if (flagd == -1) {
79 warnx("Could not parse '%s' as a disk policy", optarg);
80 usage();
81 }
82 break;
83 case 'g':
84 flagg = parse_disk_policy(optarg);
85 if (flagg == -1) {
86 warnx("Could not parse '%s' as a disk policy", optarg);
87 usage();
88 }
89 break;
1a7e3f61
A
90 case 'c':
91 qos_clamp = parse_qos_clamp(optarg);
92 if (qos_clamp == POSIX_SPAWN_PROC_CLAMP_NONE) {
93 warnx("Could not parse '%s' as a QoS clamp", optarg);
94 usage();
95 }
96 break;
fc6d9e4b
A
97 case 't':
98 qosinfo.task_throughput_qos_tier = parse_qos_tier(optarg, QOS_PARAMETER_THROUGHPUT);
99 if (qosinfo.task_throughput_qos_tier == -1) {
100 warnx("Could not parse '%s' as a qos tier", optarg);
101 usage();
102 }
103 break;
104 case 'l':
105 qosinfo.task_latency_qos_tier = parse_qos_tier(optarg, QOS_PARAMETER_LATENCY);
106 if (qosinfo.task_latency_qos_tier == -1) {
107 warnx("Could not parse '%s' as a qos tier", optarg);
108 usage();
109 }
110 break;
1a7e3f61
A
111 case 'p':
112 pid = atoi(optarg);
113 if (pid == 0) {
114 warnx("Invalid pid '%s' specified", optarg);
115 usage();
116 }
117 break;
fc6d9e4b
A
118 case '?':
119 default:
120 usage();
121 }
122 }
123 argc -= optind;
124 argv += optind;
125
1a7e3f61
A
126 if (pid == 0 && argc == 0) {
127 usage();
128 }
129
130 if (pid != 0 && (flagx || flagX || flagg != -1 || flagd != -1)) {
131 warnx("Incompatible option(s) used with -p");
fc6d9e4b
A
132 usage();
133 }
cf37c299 134
fc6d9e4b
A
135 if (flagx && flagX){
136 warnx("Incompatible options -x, -X");
137 usage();
138 }
139
1a7e3f61
A
140 if (flagb && flagB) {
141 warnx("Incompatible options -b, -B");
142 usage();
143 }
144
145 if (flagB && pid == 0) {
146 warnx("The -B option can only be used with the -p option");
147 usage();
148 }
149
fc6d9e4b
A
150 if (flagx) {
151 ret = setiopolicy_np(IOPOL_TYPE_VFS_HFS_CASE_SENSITIVITY, IOPOL_SCOPE_PROCESS, IOPOL_VFS_HFS_CASE_SENSITIVITY_FORCE_CASE_SENSITIVE);
152 if (ret == -1) {
153 err(EX_SOFTWARE, "setiopolicy_np(IOPOL_TYPE_VFS_HFS_CASE_SENSITIVITY...)");
154 }
155 }
156
157 if (flagX) {
158 ret = setiopolicy_np(IOPOL_TYPE_VFS_HFS_CASE_SENSITIVITY, IOPOL_SCOPE_PROCESS, IOPOL_VFS_HFS_CASE_SENSITIVITY_DEFAULT);
159 if (ret == -1) {
160 err(EX_SOFTWARE, "setiopolicy_np(IOPOL_TYPE_VFS_HFS_CASE_SENSITIVITY...)");
161 }
162 }
163
164 if (flagb) {
1a7e3f61
A
165 ret = setpriority(PRIO_DARWIN_PROCESS, pid, PRIO_DARWIN_BG);
166 if (ret == -1) {
167 err(EX_SOFTWARE, "setpriority()");
168 }
169 }
170
171 if (flagB) {
172 ret = setpriority(PRIO_DARWIN_PROCESS, pid, 0);
fc6d9e4b
A
173 if (ret == -1) {
174 err(EX_SOFTWARE, "setpriority()");
175 }
176 }
177
178 if (flagd >= 0) {
179 ret = setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, flagd);
180 if (ret == -1) {
181 err(EX_SOFTWARE, "setiopolicy_np(...IOPOL_SCOPE_PROCESS...)");
182 }
183 }
184
185 if (flagg >= 0){
186 ret = setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_DARWIN_BG, flagg);
187 if (ret == -1) {
188 err(EX_SOFTWARE, "setiopolicy_np(...IOPOL_SCOPE_DARWIN_BG...)");
189 }
190 }
191
192 if (qosinfo.task_latency_qos_tier != LATENCY_QOS_TIER_UNSPECIFIED ||
193 qosinfo.task_throughput_qos_tier != THROUGHPUT_QOS_TIER_UNSPECIFIED){
1a7e3f61
A
194 mach_port_t task;
195 if (pid) {
196 ret = task_for_pid(mach_task_self(), pid, &task);
197 if (ret != KERN_SUCCESS) {
198 err(EX_SOFTWARE, "task_for_pid(%d) failed", pid);
199 return EX_OSERR;
200 }
201 } else {
202 task = mach_task_self();
203 }
204 ret = task_policy_set((task_t)task, TASK_OVERRIDE_QOS_POLICY, (task_policy_t)&qosinfo, TASK_QOS_POLICY_COUNT);
fc6d9e4b
A
205 if (ret != KERN_SUCCESS){
206 err(EX_SOFTWARE, "task_policy_set(...TASK_OVERRIDE_QOS_POLICY...)");
207 }
208 }
cf37c299 209
1a7e3f61
A
210 if (pid != 0)
211 return 0;
cf37c299 212
1a7e3f61
A
213 ret = posix_spawnattr_init(&attr);
214 if (ret != 0) errc(EX_NOINPUT, ret, "posix_spawnattr_init");
cf37c299 215
1a7e3f61
A
216 ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETEXEC);
217 if (ret != 0) errc(EX_NOINPUT, ret, "posix_spawnattr_setflags");
cf37c299 218
1a7e3f61
A
219 if (qos_clamp != POSIX_SPAWN_PROC_CLAMP_NONE) {
220 ret = posix_spawnattr_set_qos_clamp_np(&attr, qos_clamp);
221 if (ret != 0) errc(EX_NOINPUT, ret, "posix_spawnattr_set_qos_clamp_np");
222 }
cf37c299 223
1a7e3f61
A
224 ret = posix_spawnp(&pid, argv[0], NULL, &attr, argv, environ);
225 if (ret != 0) errc(EX_NOINPUT, ret, "posix_spawn");
cf37c299 226
fc6d9e4b
A
227 return EX_OSERR;
228}
229
230static void usage(void)
231{
1a7e3f61
A
232 fprintf(stderr, "Usage: %s [-x|-X] [-d <policy>] [-g policy] [-c clamp] [-b] [-t <tier>]\n"
233 " [-l <tier>] <program> [<pargs> [...]]\n", getprogname());
234 fprintf(stderr, " %s [-b|-B] [-t <tier>] [-l <tier>] -p pid\n", getprogname());
fc6d9e4b
A
235 exit(EX_USAGE);
236}
237
238static int parse_disk_policy(const char *strpolicy)
239{
240 long policy;
241 char *endptr = NULL;
cf37c299 242
fc6d9e4b
A
243 /* first try as an integer */
244 policy = strtol(strpolicy, &endptr, 0);
245 if (endptr && (endptr[0] == '\0') && (strpolicy[0] != '\0')) {
246 /* parsed complete string as a number */
247 return (int)policy;
248 }
cf37c299 249
fc6d9e4b
A
250 if (0 == strcasecmp(strpolicy, "DEFAULT") ) {
251 return IOPOL_DEFAULT;
252 } else if (0 == strcasecmp(strpolicy, "IMPORTANT")) {
253 return IOPOL_IMPORTANT;
254 } else if (0 == strcasecmp(strpolicy, "PASSIVE")) {
255 return IOPOL_PASSIVE;
256 } else if (0 == strcasecmp(strpolicy, "THROTTLE")) {
257 return IOPOL_THROTTLE;
258 } else if (0 == strcasecmp(strpolicy, "UTILITY")) {
259 return IOPOL_UTILITY;
260 } else if (0 == strcasecmp(strpolicy, "STANDARD")) {
261 return IOPOL_STANDARD;
262 } else {
263 return -1;
264 }
265}
266
267static int parse_qos_tier(const char *strtier, int parameter){
268 long policy;
269 char *endptr = NULL;
cf37c299 270
fc6d9e4b
A
271 /* first try as an integer */
272 policy = strtol(strtier, &endptr, 0);
273 if (endptr && (endptr[0] == '\0') && (strtier[0] != '\0')) {
274 switch (policy) {
275 case 0:
276 return parameter ? THROUGHPUT_QOS_TIER_0 : LATENCY_QOS_TIER_0;
277 break;
278 case 1:
279 return parameter ? THROUGHPUT_QOS_TIER_1 : LATENCY_QOS_TIER_1;
280 break;
281 case 2:
282 return parameter ? THROUGHPUT_QOS_TIER_2 : LATENCY_QOS_TIER_2;
283 break;
284 case 3:
285 return parameter ? THROUGHPUT_QOS_TIER_3 : LATENCY_QOS_TIER_3;
286 break;
287 case 4:
288 return parameter ? THROUGHPUT_QOS_TIER_4 : LATENCY_QOS_TIER_4;
289 break;
290 case 5:
291 return parameter ? THROUGHPUT_QOS_TIER_5 : LATENCY_QOS_TIER_5;
292 break;
293 default:
294 return -1;
295 break;
296 }
297 }
298
299 return -1;
300}
1a7e3f61
A
301
302static uint64_t parse_qos_clamp(const char *qos_string) {
cf37c299 303
1a7e3f61
A
304 if (0 == strcasecmp(qos_string, "utility") ) {
305 return POSIX_SPAWN_PROC_CLAMP_UTILITY;
306 } else if (0 == strcasecmp(qos_string, "background")) {
307 return POSIX_SPAWN_PROC_CLAMP_BACKGROUND;
308 } else if (0 == strcasecmp(qos_string, "maintenance")) {
309 return POSIX_SPAWN_PROC_CLAMP_MAINTENANCE;
310 } else {
311 return POSIX_SPAWN_PROC_CLAMP_NONE;
312 }
313}