]>
Commit | Line | Data |
---|---|---|
70ad1dc8 A |
1 | /* |
2 | * Copyright (c) 2018 Apple Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | #include "internal.h" | |
24 | ||
25 | #pragma mark Utilities | |
26 | static int | |
27 | _sysctl_12809455(int mib[4], size_t mib_cnt, void *old, size_t *old_len, | |
28 | void *new, size_t new_len) | |
29 | { | |
30 | int error = -1; | |
31 | int ret = -1; | |
32 | size_t mylen = 0; | |
33 | size_t *mylenp = NULL; | |
34 | #if RDAR_12809455 | |
35 | bool workaround_12809455 = false; | |
36 | #endif | |
37 | ||
38 | if (old_len) { | |
39 | mylen = *old_len; | |
40 | mylenp = &mylen; | |
41 | } | |
42 | ||
43 | // sysctl(3) doesn't behave anything like its documentation leads you to | |
44 | // believe. If the given buffer is too small to hold the requested data, | |
45 | // what's supposed to happen is: | |
46 | // | |
47 | // - as much data as possible is copied into the buffer | |
48 | // - the number of bytes copied is written to the len parameter | |
49 | // - errno is set to ENOMEM | |
50 | // - -1 is returned (to indicate that you should check errno) | |
51 | // | |
52 | // What actually happens: | |
53 | // | |
54 | // - no data is copied | |
55 | // - len is set to zero | |
56 | // - errno is undefined | |
57 | // - zero is returned | |
58 | // | |
59 | // So... swing and a miss. | |
60 | // | |
61 | // Since it returns success in this case our only indication that something | |
62 | // went wrong is if mylen is set to zero. | |
63 | // | |
64 | // So we do our best to sniff out the misbehavior and emulate sysctl(3)'s | |
65 | // API contract until it's fixed. | |
66 | // | |
67 | // <rdar://problem/12809455> | |
68 | #if RDAR_12809455 | |
69 | if (old_len && *old_len > 0) { | |
70 | // We can only work around the bug if the passed-in length was non-zero. | |
71 | workaround_12809455 = true; | |
72 | } | |
73 | #endif | |
74 | ||
75 | ret = sysctl(mib, (u_int)mib_cnt, old, mylenp, new, new_len); | |
76 | #if RDAR_12809455 | |
77 | if (workaround_12809455 && old && ret == 0 && mylen == 0) { | |
78 | ret = -1; | |
79 | errno = ENOMEM; | |
80 | } | |
81 | #endif // RDAR_12809455 | |
82 | ||
83 | if (ret == 0) { | |
84 | error = 0; | |
85 | } else { | |
86 | error = errno; | |
87 | } | |
88 | ||
89 | if (old_len) { | |
90 | *old_len = mylen; | |
91 | } | |
92 | ||
93 | return error; | |
94 | } | |
95 | ||
96 | static char * | |
97 | _strblk(const char *str) | |
98 | { | |
99 | const char *cur = str; | |
100 | ||
101 | while (*cur && !isblank(*cur)) { | |
102 | cur++; | |
103 | } | |
104 | ||
105 | return (char *)cur; | |
106 | } | |
107 | ||
108 | static bool | |
109 | _get_boot_arg_value(const char *which, char *where, size_t max) | |
110 | { | |
111 | // This is (very) loosely based on the implementation of | |
112 | // PE_parse_boot_argn() (or at least the parts where I was able to easily | |
113 | // decipher the policy). | |
114 | bool found = false; | |
115 | errno_t error = -1; | |
116 | char *buff = NULL; | |
117 | size_t buff_len = 0; | |
118 | char *theone = NULL; | |
119 | char *equals = NULL; | |
120 | ||
121 | error = sysctlbyname_get_data_np("kern.bootargs", (void **)&buff, | |
122 | &buff_len); | |
123 | if (error) { | |
124 | goto __out; | |
125 | } | |
126 | ||
127 | theone = strstr(buff, which); | |
128 | if (!theone) { | |
129 | goto __out; | |
130 | } | |
131 | ||
132 | found = true; | |
133 | if (!where) { | |
134 | // Caller just wants to know whether the boot-arg exists. | |
135 | goto __out; | |
136 | } | |
137 | ||
138 | equals = strchr(theone, '='); | |
139 | if (!equals || isblank(equals[1])) { | |
140 | strlcpy(where, "", max); | |
141 | } else { | |
142 | char *nextsep = NULL; | |
143 | char nextsep_old = 0; | |
144 | ||
145 | // Find the next separator and nerf it temporarily for the benefit of | |
146 | // the underlying strcpy(3). | |
147 | nextsep = _strblk(theone); | |
148 | nextsep_old = *nextsep; | |
149 | *nextsep = 0; | |
150 | strlcpy(where, &equals[1], max); | |
151 | ||
152 | *nextsep = nextsep_old; | |
153 | } | |
154 | ||
155 | __out: | |
156 | free(buff); | |
157 | return found; | |
158 | } | |
159 | ||
160 | #pragma mark API | |
161 | errno_t | |
162 | sysctl_get_data_np(int mib[4], size_t mib_cnt, void **buff, size_t *buff_len) | |
163 | { | |
164 | errno_t error = -1; | |
165 | size_t needed = 0; | |
166 | void *mybuff = NULL; | |
167 | ||
168 | // We need to get the length of the parameter so we can allocate a buffer | |
169 | // that's large enough. | |
170 | error = _sysctl_12809455(mib, mib_cnt, NULL, &needed, NULL, 0); | |
171 | if (error) { | |
172 | goto __out; | |
173 | } | |
174 | ||
175 | mybuff = malloc(needed); | |
176 | if (!mybuff) { | |
177 | error = errno; | |
178 | goto __out; | |
179 | } | |
180 | ||
181 | error = _sysctl_12809455(mib, mib_cnt, mybuff, &needed, NULL, 0); | |
182 | if (error) { | |
183 | // It's conceivable that some other process came along within this | |
184 | // window and modified the variable to be even larger than we'd | |
185 | // previously been told, but if that's the case, just give up. | |
186 | goto __out; | |
187 | } | |
188 | ||
189 | *buff = mybuff; | |
190 | *buff_len = needed; | |
191 | ||
192 | __out: | |
193 | if (error) { | |
194 | free(mybuff); | |
195 | } | |
196 | return error; | |
197 | } | |
198 | ||
199 | errno_t | |
200 | sysctlbyname_get_data_np(const char *mibdesc, void **buff, size_t *buff_len) | |
201 | { | |
202 | int ret = -1; | |
203 | int error = -1; | |
204 | int mib[4]; | |
205 | size_t mib_cnt = countof(mib); | |
206 | ||
207 | ret = sysctlnametomib(mibdesc, mib, &mib_cnt); | |
208 | if (ret) { | |
209 | error = errno; | |
210 | goto __out; | |
211 | } | |
212 | ||
213 | error = sysctl_get_data_np(mib, mib_cnt, buff, buff_len); | |
214 | ||
215 | __out: | |
216 | return error; | |
217 | } | |
218 | ||
219 | bool | |
220 | os_parse_boot_arg_int(const char *which, int64_t *where) | |
221 | { | |
222 | bool found = false; | |
223 | char buff[24] = {0}; | |
224 | char *endptr = NULL; | |
225 | int64_t val = 0; | |
226 | ||
227 | found = _get_boot_arg_value(which, buff, sizeof(buff)); | |
228 | if (!found || !where) { | |
229 | goto __out; | |
230 | } | |
231 | ||
232 | // A base of zero handles bases 8, 10, and 16. | |
233 | val = strtoll(buff, &endptr, 0); | |
234 | if (*endptr == 0) { | |
235 | *where = val; | |
236 | } else { | |
237 | // The boot-arg value was invalid, so say we didn't find it. | |
238 | found = false; | |
239 | } | |
240 | ||
241 | __out: | |
242 | return found; | |
243 | } | |
244 | ||
245 | bool | |
246 | os_parse_boot_arg_string(const char *which, char *where, size_t maxlen) | |
247 | { | |
248 | return _get_boot_arg_value(which, where, maxlen); | |
249 | } |