/ libdarwin / bsd.c
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  }