]> git.saurik.com Git - apple/libc.git/blame - libdarwin/bsd.c
Libc-1272.200.26.tar.gz
[apple/libc.git] / libdarwin / bsd.c
CommitLineData
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
26static 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
96static 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
108static 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
161errno_t
162sysctl_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
199errno_t
200sysctlbyname_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
219bool
220os_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
245bool
246os_parse_boot_arg_string(const char *which, char *where, size_t maxlen)
247{
248 return _get_boot_arg_value(which, where, maxlen);
249}