]>
Commit | Line | Data |
---|---|---|
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 | ||
00bf83c0 | 25 | #include <System/sys/proc.h> |
fc6d9e4b A |
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 | ||
1a7e3f61 A |
38 | #include <spawn.h> |
39 | #include <spawn_private.h> | |
40 | #include <sys/spawn_internal.h> | |
41 | ||
fc6d9e4b A |
42 | #define QOS_PARAMETER_LATENCY 0 |
43 | #define QOS_PARAMETER_THROUGHPUT 1 | |
44 | ||
1a7e3f61 A |
45 | extern char **environ; |
46 | ||
fc6d9e4b A |
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); | |
1a7e3f61 | 50 | static uint64_t parse_qos_clamp(const char *qos_string); |
fc6d9e4b A |
51 | |
52 | int main(int argc, char * argv[]) | |
53 | { | |
54 | int ch, ret; | |
1a7e3f61 A |
55 | pid_t pid = 0; |
56 | posix_spawnattr_t attr; | |
57 | extern char **environ; | |
887d5eed | 58 | bool flagx = false, flagX = false, flagb = false, flagB = false, flaga = false; |
fc6d9e4b A |
59 | int flagd = -1, flagg = -1; |
60 | struct task_qos_policy qosinfo = { LATENCY_QOS_TIER_UNSPECIFIED, THROUGHPUT_QOS_TIER_UNSPECIFIED }; | |
1a7e3f61 | 61 | uint64_t qos_clamp = POSIX_SPAWN_PROC_CLAMP_NONE; |
cf37c299 | 62 | |
887d5eed | 63 | while ((ch = getopt(argc, argv, "xXbBd:g:c:t:l:p:a")) != -1) { |
fc6d9e4b A |
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; | |
1a7e3f61 A |
74 | case 'B': |
75 | flagB = true; | |
76 | break; | |
fc6d9e4b A |
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; | |
1a7e3f61 A |
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; | |
fc6d9e4b A |
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; | |
1a7e3f61 A |
112 | case 'p': |
113 | pid = atoi(optarg); | |
114 | if (pid == 0) { | |
115 | warnx("Invalid pid '%s' specified", optarg); | |
116 | usage(); | |
117 | } | |
118 | break; | |
887d5eed A |
119 | case 'a': |
120 | flaga = true; | |
121 | break; | |
fc6d9e4b A |
122 | case '?': |
123 | default: | |
124 | usage(); | |
125 | } | |
126 | } | |
127 | argc -= optind; | |
128 | argv += optind; | |
129 | ||
1a7e3f61 A |
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"); | |
fc6d9e4b A |
136 | usage(); |
137 | } | |
cf37c299 | 138 | |
fc6d9e4b A |
139 | if (flagx && flagX){ |
140 | warnx("Incompatible options -x, -X"); | |
141 | usage(); | |
142 | } | |
143 | ||
1a7e3f61 A |
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 | ||
fc6d9e4b A |
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) { | |
1a7e3f61 A |
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); | |
fc6d9e4b A |
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){ | |
1a7e3f61 A |
198 | mach_port_t task; |
199 | if (pid) { | |
00bf83c0 | 200 | ret = task_name_for_pid(mach_task_self(), pid, &task); |
1a7e3f61 | 201 | if (ret != KERN_SUCCESS) { |
00bf83c0 | 202 | err(EX_SOFTWARE, "task_name_for_pid(%d) failed", pid); |
1a7e3f61 A |
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); | |
fc6d9e4b A |
209 | if (ret != KERN_SUCCESS){ |
210 | err(EX_SOFTWARE, "task_policy_set(...TASK_OVERRIDE_QOS_POLICY...)"); | |
211 | } | |
212 | } | |
cf37c299 | 213 | |
1a7e3f61 A |
214 | if (pid != 0) |
215 | return 0; | |
cf37c299 | 216 | |
1a7e3f61 A |
217 | ret = posix_spawnattr_init(&attr); |
218 | if (ret != 0) errc(EX_NOINPUT, ret, "posix_spawnattr_init"); | |
cf37c299 | 219 | |
1a7e3f61 A |
220 | ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETEXEC); |
221 | if (ret != 0) errc(EX_NOINPUT, ret, "posix_spawnattr_setflags"); | |
cf37c299 | 222 | |
1a7e3f61 A |
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 | } | |
cf37c299 | 227 | |
887d5eed A |
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 | ||
1a7e3f61 A |
236 | ret = posix_spawnp(&pid, argv[0], NULL, &attr, argv, environ); |
237 | if (ret != 0) errc(EX_NOINPUT, ret, "posix_spawn"); | |
cf37c299 | 238 | |
fc6d9e4b A |
239 | return EX_OSERR; |
240 | } | |
241 | ||
242 | static void usage(void) | |
243 | { | |
1a7e3f61 | 244 | fprintf(stderr, "Usage: %s [-x|-X] [-d <policy>] [-g policy] [-c clamp] [-b] [-t <tier>]\n" |
887d5eed | 245 | " [-l <tier>] [-a] <program> [<pargs> [...]]\n", getprogname()); |
1a7e3f61 | 246 | fprintf(stderr, " %s [-b|-B] [-t <tier>] [-l <tier>] -p pid\n", getprogname()); |
fc6d9e4b A |
247 | exit(EX_USAGE); |
248 | } | |
249 | ||
250 | static int parse_disk_policy(const char *strpolicy) | |
251 | { | |
252 | long policy; | |
253 | char *endptr = NULL; | |
cf37c299 | 254 | |
fc6d9e4b A |
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 | } | |
cf37c299 | 261 | |
fc6d9e4b A |
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; | |
cf37c299 | 282 | |
fc6d9e4b A |
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 | } | |
1a7e3f61 A |
313 | |
314 | static uint64_t parse_qos_clamp(const char *qos_string) { | |
cf37c299 | 315 | |
1a7e3f61 A |
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 | } |