]> git.saurik.com Git - apple/libc.git/blob - libdarwin/bsd.c
30d915fad4e2fa1affcb47e68cf8c18f01dc94f4
[apple/libc.git] / libdarwin / bsd.c
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 }