1/*
2 * Copyright (c) 2013 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 <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
37#define QOS_PARAMETER_LATENCY 0
38#define QOS_PARAMETER_THROUGHPUT 1
39
40static void usage(void);
41static int parse_disk_policy(const char *strpolicy);
42static int parse_qos_tier(const char *strpolicy, int parameter);
43
44int main(int argc, char * argv[])
45{
46	int ch, ret;
47	bool flagx = false, flagX = false, flagb = false;
48	int flagd = -1, flagg = -1;
49	struct task_qos_policy qosinfo = { LATENCY_QOS_TIER_UNSPECIFIED, THROUGHPUT_QOS_TIER_UNSPECIFIED };
50
51	while ((ch = getopt(argc, argv, "xXbd:g:t:l:")) != -1) {
52		switch (ch) {
53			case 'x':
54				flagx = true;
55				break;
56			case 'X':
57				flagX = true;
58				break;
59			case 'b':
60				flagb = true;
61				break;
62			case 'd':
63				flagd = parse_disk_policy(optarg);
64				if (flagd == -1) {
65					warnx("Could not parse '%s' as a disk policy", optarg);
66					usage();
67				}
68				break;
69			case 'g':
70				flagg = parse_disk_policy(optarg);
71				if (flagg == -1) {
72					warnx("Could not parse '%s' as a disk policy", optarg);
73					usage();
74				}
75				break;
76			case 't':
77				qosinfo.task_throughput_qos_tier = parse_qos_tier(optarg, QOS_PARAMETER_THROUGHPUT);
78				if (qosinfo.task_throughput_qos_tier == -1) {
79					warnx("Could not parse '%s' as a qos tier", optarg);
80					usage();
81				}
82				break;
83			case 'l':
84				qosinfo.task_latency_qos_tier = parse_qos_tier(optarg, QOS_PARAMETER_LATENCY);
85				if (qosinfo.task_latency_qos_tier == -1) {
86					warnx("Could not parse '%s' as a qos tier", optarg);
87					usage();
88				}
89				break;
90			case '?':
91			default:
92				usage();
93		}
94	}
95	argc -= optind;
96	argv += optind;
97
98	if (argc == 0) {
99		usage();
100	}
101
102	if (flagx && flagX){
103		warnx("Incompatible options -x, -X");
104		usage();
105	}
106
107	if (flagx) {
108		ret = setiopolicy_np(IOPOL_TYPE_VFS_HFS_CASE_SENSITIVITY, IOPOL_SCOPE_PROCESS, IOPOL_VFS_HFS_CASE_SENSITIVITY_FORCE_CASE_SENSITIVE);
109		if (ret == -1) {
110			err(EX_SOFTWARE, "setiopolicy_np(IOPOL_TYPE_VFS_HFS_CASE_SENSITIVITY...)");
111		}
112	}
113
114	if (flagX) {
115		ret = setiopolicy_np(IOPOL_TYPE_VFS_HFS_CASE_SENSITIVITY, IOPOL_SCOPE_PROCESS, IOPOL_VFS_HFS_CASE_SENSITIVITY_DEFAULT);
116		if (ret == -1) {
117			err(EX_SOFTWARE, "setiopolicy_np(IOPOL_TYPE_VFS_HFS_CASE_SENSITIVITY...)");
118		}
119	}
120
121	if (flagb) {
122		ret = setpriority(PRIO_DARWIN_PROCESS, 0, PRIO_DARWIN_BG);
123		if (ret == -1) {
124			err(EX_SOFTWARE, "setpriority()");
125		}
126	}
127
128	if (flagd >= 0) {
129		ret = setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, flagd);
130		if (ret == -1) {
131			err(EX_SOFTWARE, "setiopolicy_np(...IOPOL_SCOPE_PROCESS...)");
132		}
133	}
134
135	if (flagg >= 0){
136		ret = setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_DARWIN_BG, flagg);
137		if (ret == -1) {
138			err(EX_SOFTWARE, "setiopolicy_np(...IOPOL_SCOPE_DARWIN_BG...)");
139		}
140	}
141
142	if (qosinfo.task_latency_qos_tier != LATENCY_QOS_TIER_UNSPECIFIED ||
143	    qosinfo.task_throughput_qos_tier != THROUGHPUT_QOS_TIER_UNSPECIFIED){
144		ret = task_policy_set(mach_task_self(), TASK_OVERRIDE_QOS_POLICY, (task_policy_t)&qosinfo, TASK_QOS_POLICY_COUNT);
145		if (ret != KERN_SUCCESS){
146			err(EX_SOFTWARE, "task_policy_set(...TASK_OVERRIDE_QOS_POLICY...)");
147		}
148	}
149
150	ret = execvp(argv[0], argv);
151	if (ret == -1) {
152		err(EX_NOINPUT, "Could not execute %s", argv[0]);
153	}
154
155	return EX_OSERR;
156}
157
158static void usage(void)
159{
160	fprintf(stderr, "Usage: %s [-x|-X] [-d <policy>] [-g policy] [-b] [-t <tier>] [-l <tier>] <program> [<pargs> [...]]\n", getprogname());
161	exit(EX_USAGE);
162}
163
164static int parse_disk_policy(const char *strpolicy)
165{
166	long policy;
167	char *endptr = NULL;
168
169	/* first try as an integer */
170	policy = strtol(strpolicy, &endptr, 0);
171	if (endptr && (endptr[0] == '\0') && (strpolicy[0] != '\0')) {
172		/* parsed complete string as a number */
173		return (int)policy;
174	}
175
176	if (0 == strcasecmp(strpolicy, "DEFAULT") ) {
177		return IOPOL_DEFAULT;
178	} else if (0 == strcasecmp(strpolicy, "IMPORTANT")) {
179		return IOPOL_IMPORTANT;
180	} else if (0 == strcasecmp(strpolicy, "PASSIVE")) {
181		return IOPOL_PASSIVE;
182	} else if (0 == strcasecmp(strpolicy, "THROTTLE")) {
183		return IOPOL_THROTTLE;
184	} else if (0 == strcasecmp(strpolicy, "UTILITY")) {
185		return IOPOL_UTILITY;
186	} else if (0 == strcasecmp(strpolicy, "STANDARD")) {
187		return IOPOL_STANDARD;
188	} else {
189		return -1;
190	}
191}
192
193static int parse_qos_tier(const char *strtier, int parameter){
194	long policy;
195	char *endptr = NULL;
196
197	/* first try as an integer */
198	policy = strtol(strtier, &endptr, 0);
199	if (endptr && (endptr[0] == '\0') && (strtier[0] != '\0')) {
200		switch (policy) {
201			case 0:
202				return parameter ? THROUGHPUT_QOS_TIER_0 : LATENCY_QOS_TIER_0;
203				break;
204			case 1:
205				return parameter ? THROUGHPUT_QOS_TIER_1 : LATENCY_QOS_TIER_1;
206				break;
207			case 2:
208				return parameter ? THROUGHPUT_QOS_TIER_2 : LATENCY_QOS_TIER_2;
209				break;
210			case 3:
211				return parameter ? THROUGHPUT_QOS_TIER_3 : LATENCY_QOS_TIER_3;
212				break;
213			case 4:
214				return parameter ? THROUGHPUT_QOS_TIER_4 : LATENCY_QOS_TIER_4;
215				break;
216			case 5:
217				return parameter ? THROUGHPUT_QOS_TIER_5 : LATENCY_QOS_TIER_5;
218				break;
219			default:
220				return -1;
221				break;
222		}
223	}
224
225	return -1;
226}
227