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 27 /* 28 * Factored out from _get_parse_boot_arg_value for unit testing purposes 29 */ 30 static bool 31 _parse_boot_arg_value(char *argsbuff, const char *which, char *where, size_t max) 32 { 33 bool found = false; 34 35 char *token = NULL; 36 char *argsstr = argsbuff; 37 static const char seps[] = { ' ', '\t', 0 }; 38 while ((token = strsep(&argsstr, seps)) != NULL) { 39 bool is_boolean = false; 40 41 char *value = NULL; 42 char *equals = strchr(token, '='); 43 if (token[0] == '-') { 44 /* 45 * Arguments whose names begins with "-" are booleans, so don't get 46 * key=value splitting. Though I'd still discourage you from 47 * naming your option "-edge=case". 48 */ 49 is_boolean = true; 50 } else if (equals) { 51 equals[0] = '\0'; 52 value = &equals[1]; 53 } else { 54 is_boolean = true; 55 } 56 57 if (strcmp(which, token) == 0) { 58 /* 59 * Found it! Copy out the value as required. 60 */ 61 found = true; 62 63 if (!where) { 64 // Caller just wants to know whether the boot-arg exists. 65 } else if (is_boolean || value == NULL) { 66 strlcpy(where, "", max); 67 } else { 68 strlcpy(where, value, max); 69 } 70 71 break; 72 } 73 } 74 75 return found; 76 } 77 78 /* 79 * This is (very) loosely based on the implementation of 80 * PE_parse_boot_argn() (or at least the parts where I was able to easily 81 * decipher the policy). 82 */ 83 static bool 84 _get_boot_arg_value(const char *which, char *where, size_t max) 85 { 86 bool found = false; 87 __os_free char *argsbuff = NULL; 88 size_t argsbuff_len = 0; 89 errno_t error = sysctlbyname_get_data_np("kern.bootargs", 90 (void **)&argsbuff, &argsbuff_len); 91 92 if (!error) { 93 found = _parse_boot_arg_value(argsbuff, which, where, max); 94 } 95 96 return found; 97 } 98 99 #pragma mark API 100 errno_t 101 sysctl_get_data_np(int mib[4], size_t mib_cnt, void **buff, size_t *buff_len) 102 { 103 errno_t error = 0; 104 int ret = 0; 105 size_t needed = 0; 106 void *mybuff = NULL; 107 108 // We need to get the length of the parameter so we can allocate a buffer 109 // that's large enough. 110 ret = sysctl(mib, (unsigned int)mib_cnt, NULL, &needed, NULL, 0); 111 if (ret) { 112 error = errno; 113 goto __out; 114 } 115 116 mybuff = malloc(needed); 117 if (!mybuff) { 118 error = errno; 119 goto __out; 120 } 121 122 ret = sysctl(mib, (unsigned int)mib_cnt, mybuff, &needed, NULL, 0); 123 if (ret) { 124 // It's conceivable that some other process came along within this 125 // window and modified the variable to be even larger than we'd 126 // previously been told, but if that's the case, just give up. 127 error = errno; 128 goto __out; 129 } 130 131 *buff = mybuff; 132 *buff_len = needed; 133 134 __out: 135 if (error) { 136 free(mybuff); 137 } 138 return error; 139 } 140 141 errno_t 142 sysctlbyname_get_data_np(const char *mibdesc, void **buff, size_t *buff_len) 143 { 144 int ret = -1; 145 int error = -1; 146 int mib[4]; 147 size_t mib_cnt = countof(mib); 148 149 ret = sysctlnametomib(mibdesc, mib, &mib_cnt); 150 if (ret) { 151 error = errno; 152 goto __out; 153 } 154 155 error = sysctl_get_data_np(mib, mib_cnt, buff, buff_len); 156 157 __out: 158 return error; 159 } 160 161 bool 162 os_parse_boot_arg_int(const char *which, int64_t *where) 163 { 164 bool found = false; 165 char buff[24] = {0}; 166 char *endptr = NULL; 167 int64_t val = 0; 168 169 found = _get_boot_arg_value(which, buff, sizeof(buff)); 170 if (!found || !where) { 171 goto __out; 172 } 173 174 // A base of zero handles bases 8, 10, and 16. 175 val = strtoll(buff, &endptr, 0); 176 if (*endptr == 0) { 177 *where = val; 178 } else { 179 // The boot-arg value was invalid, so say we didn't find it. 180 found = false; 181 } 182 183 __out: 184 return found; 185 } 186 187 bool 188 os_parse_boot_arg_string(const char *which, char *where, size_t maxlen) 189 { 190 return _get_boot_arg_value(which, where, maxlen); 191 }