]> git.saurik.com Git - apple/libc.git/blob - libdarwin/variant.c
684385671e1a01930471345b4bdf7cbe529b2250
[apple/libc.git] / libdarwin / variant.c
1 /*
2 * Copyright (c) 2016 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
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <sys/stat.h>
28 #include <sys/sysctl.h>
29 #include <sys/types.h>
30 #include <dispatch/dispatch.h>
31 #include <xpc/xpc.h>
32 #include <xpc/private.h>
33 #include <System/sys/csr.h>
34 #include <System/machine/cpu_capabilities.h>
35
36 #include <os/assumes.h>
37 #include <os/bsd.h>
38 #include <os/stdlib.h>
39 #include <os/variant_private.h>
40 #include <os/boot_mode_private.h>
41
42 /*
43 * Lists all properties overridden by an empty file
44 */
45 #define ALL_OVERRIDES_STR "content,diagnostics,ui,security"
46
47 enum variant_property {
48 VP_CONTENT,
49 VP_DIAGNOSTICS,
50 VP_UI,
51 VP_SECURITY,
52 VP_MAX
53 };
54
55 enum check_status {
56 S_UNKNOWN = 0,
57 S_NO = 2,
58 S_YES = 3
59 };
60
61 typedef struct {
62 const char *variant;
63 bool (*function)(const char*);
64 } variant_check_mapping;
65
66 static bool
67 status2bool(enum check_status status) {
68 switch (status) {
69 case S_NO:
70 return false;
71 case S_YES:
72 return true;
73 case S_UNKNOWN:
74 default:
75 os_crash("os_variant had unexpected status");
76 }
77 }
78
79 #define VAR_FILE_LEGACY "/var/db/disableAppleInternal"
80
81 #if TARGET_OS_OSX
82 #define VAR_FILE_OVERRIDE "/var/db/os_variant_override"
83 #else
84 #define VAR_FILE_OVERRIDE "/usr/share/misc/os_variant_override"
85 #endif
86
87 #if !TARGET_OS_SIMULATOR
88 #define INTERNAL_CONTENT_PATH "/System/Library/CoreServices/AppleInternalVariant.plist"
89 #else
90 #define INTERNAL_CONTENT_PATH "/AppleInternal"
91 #endif
92
93 #define SYSTEM_VERSION_PLIST_PATH "/System/Library/CoreServices/SystemVersion.plist"
94 #define SYSTEM_VERSION_PLIST_KEY "ReleaseType"
95
96 #if TARGET_OS_IPHONE
97 #define INTERNAL_SETTINGS_PATH "/AppleInternal/Library/PreferenceBundles/Internal Settings.bundle"
98 #else
99 #define INTERNAL_DIAGS_PROFILE_PATH "/var/db/ConfigurationProfiles/Settings/com.apple.InternalDiagnostics.plist"
100 #define FACTORY_CONTENT_PATH "/System/Library/CoreServices/AppleFactoryVariant.plist"
101 #define BASE_SYSTEM_CONTENT_PATH "/System/Library/BaseSystem"
102 #define DARWINOS_CONTENT_PATH "/System/Library/CoreServices/DarwinVariant.plist"
103 #endif
104
105 static void _check_all_statuses(void);
106
107 #if !TARGET_OS_SIMULATOR
108 #define CACHE_SYSCTL_NAME "kern.osvariant_status"
109
110 static void _restore_cached_check_status(uint64_t status);
111 static uint64_t _get_cached_check_status(void);
112
113 static char * _read_file(const char *path, size_t *size_out)
114 {
115 char *buf = NULL;
116
117 int fd = open(path, O_RDONLY);
118 if (fd == -1) return NULL;
119
120 struct stat sb;
121 int rc = fstat(fd, &sb);
122 if (rc != 0 || sb.st_size == 0) {
123 goto error;
124 }
125
126 size_t size_limit = (size_out && *size_out != 0) ? *size_out : 1024;
127 size_t size = (size_t)sb.st_size;
128 if (size_out) *size_out = (size_t)sb.st_size;
129 if (size > size_limit) {
130 goto error;
131 }
132
133 buf = malloc(size + 1);
134 if (!buf) {
135 goto error;
136 }
137
138 ssize_t bytes_read = read(fd, buf, size);
139 buf[size] = '\0';
140
141
142 if (bytes_read == (ssize_t)size) {
143 close(fd);
144 return buf;
145 }
146
147 error:
148 close(fd);
149 free(buf);
150 return NULL;
151 }
152
153 static xpc_object_t read_plist(const char *path)
154 {
155 size_t size = 16 * 1024;
156 uint8_t *buf = (uint8_t*)_read_file(path, &size);
157 if (!buf) return NULL;
158
159 xpc_object_t plist = xpc_create_from_plist(buf, size);
160 if (plist && xpc_get_type(plist) != XPC_TYPE_DICTIONARY) {
161 xpc_release(plist);
162 plist = NULL;
163 }
164
165 free(buf);
166
167 return plist;
168 }
169 #endif
170
171 #if !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
172 static enum check_status internal_content = S_UNKNOWN;
173 #endif
174 #if !TARGET_OS_SIMULATOR
175 static enum check_status can_has_debugger = S_UNKNOWN;
176 static enum check_status has_full_logging = S_UNKNOWN;
177 #if TARGET_OS_IPHONE
178 static enum check_status internal_release_type = S_UNKNOWN;
179 static enum check_status factory_release_type = S_UNKNOWN;
180 static enum check_status darwin_release_type = S_UNKNOWN;
181 static enum check_status recovery_release_type = S_UNKNOWN;
182 static enum check_status development_kernel = S_UNKNOWN;
183 #else // TARGET_OS_IPHONE
184 static enum check_status internal_diags_profile = S_UNKNOWN;
185 static enum check_status factory_content = S_UNKNOWN;
186 static enum check_status base_system_content = S_UNKNOWN;
187 static enum check_status darwinos_content = S_UNKNOWN;
188 #endif // TARGET_OS_IPHONE
189 #endif // !TARGET_OS_SIMULATOR
190 static enum check_status is_ephemeral = S_UNKNOWN;
191
192 static bool disabled_status[VP_MAX] = {};
193
194 static void _parse_disabled_status(char *test_string)
195 {
196 #if TARGET_OS_SIMULATOR
197 #pragma unused(test_string)
198 #else // TARGET_OS_SIMULATOR
199 char *override_str = NULL;
200
201 bzero(disabled_status, sizeof(disabled_status));
202
203 if (test_string != NULL) {
204 /* used for unit tests */
205 override_str = os_strdup(test_string);
206 } else {
207 if (access(VAR_FILE_LEGACY, F_OK) == 0) {
208 override_str = os_strdup(ALL_OVERRIDES_STR);
209 } else if (access(VAR_FILE_OVERRIDE, F_OK) != 0) {
210 return;
211 }
212
213 override_str = _read_file(VAR_FILE_OVERRIDE, NULL);
214 }
215
216 if (override_str == NULL) {
217 override_str = os_strdup(ALL_OVERRIDES_STR);
218 }
219
220 char *token, *string = override_str;
221 while ((token = strsep(&string, ",\n")) != NULL) {
222 if (strcmp(token, "content") == 0) {
223 disabled_status[VP_CONTENT] = true;
224 } else if (strcmp(token, "diagnostics") == 0) {
225 disabled_status[VP_DIAGNOSTICS] = true;
226 } else if (strcmp(token, "ui") == 0) {
227 disabled_status[VP_UI] = true;
228 } else if (strcmp(token, "security") == 0) {
229 disabled_status[VP_SECURITY] = true;
230 }
231 }
232
233 free(override_str);
234 return;
235 #endif //!TARGET_OS_SIMULATOR
236 }
237
238 #if !TARGET_OS_SIMULATOR
239 static bool _load_cached_status(void)
240 {
241 uint64_t status = 0;
242 size_t status_size = sizeof(status);
243 int ret = sysctlbyname(CACHE_SYSCTL_NAME, &status, &status_size, NULL, 0);
244 if (ret != 0) {
245 return false;
246 }
247
248 if (status) {
249 _restore_cached_check_status(status);
250 return true;
251 }
252
253 return false;
254 }
255 #endif
256
257 static void _initialize_status(void)
258 {
259 static dispatch_once_t once;
260 dispatch_once(&once, ^{
261 #if !TARGET_OS_SIMULATOR && !defined(VARIANT_SKIP_EXPORTED)
262 if (_load_cached_status() && !_os_xbs_chrooted) {
263 return;
264 }
265 #endif
266 _check_all_statuses();
267 });
268 }
269
270 static bool _check_disabled(enum variant_property variant_property)
271 {
272 _initialize_status();
273
274 return disabled_status[variant_property];
275 }
276
277 #if !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
278 static void _check_internal_content_impl(void)
279 {
280 if (_os_xbs_chrooted && internal_content != S_UNKNOWN) {
281 return;
282 } else {
283 os_assert(internal_content == S_UNKNOWN);
284 }
285
286 #if !TARGET_OS_SIMULATOR
287 const char * path = INTERNAL_CONTENT_PATH;
288 #else
289 char *simulator_root = getenv("IPHONE_SIMULATOR_ROOT");
290 char *to_free = NULL, *path = NULL;
291 if (simulator_root) {
292 asprintf(&path, "%s/%s", simulator_root, INTERNAL_CONTENT_PATH);
293 if (path == NULL) {
294 internal_content = S_NO;
295 return;
296 }
297 to_free = path;
298 }
299 #endif
300 internal_content = (access(path, F_OK) == 0) ? S_YES : S_NO;
301 #if TARGET_OS_SIMULATOR
302 free(to_free);
303 #endif
304 }
305
306 static bool _check_internal_content(void)
307 {
308 _initialize_status();
309 return status2bool(internal_content);
310 }
311 #endif // !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
312
313 #if TARGET_OS_OSX
314 static void _check_factory_content_impl(void)
315 {
316 if (_os_xbs_chrooted && factory_content != S_UNKNOWN) {
317 return;
318 } else {
319 os_assert(factory_content == S_UNKNOWN);
320 }
321
322 const char * path = FACTORY_CONTENT_PATH;
323 factory_content = (access(path, F_OK) == 0) ? S_YES : S_NO;
324 }
325
326 static bool _check_factory_content(void)
327 {
328 _initialize_status();
329
330 return status2bool(factory_content);
331 }
332 #endif // TARGET_OS_OSX
333
334 #if TARGET_OS_IPHONE
335
336 #if !TARGET_OS_SIMULATOR
337 static bool _parse_system_version_plist(void)
338 {
339 xpc_object_t system_version_plist = read_plist(SYSTEM_VERSION_PLIST_PATH);
340 if (!system_version_plist) {
341 return false;
342 }
343
344 const char *release_type =
345 xpc_dictionary_get_string(system_version_plist,
346 SYSTEM_VERSION_PLIST_KEY);
347
348 if (release_type == NULL) {
349 /*
350 * Confusingly, customer images are just completely missing this key.
351 */
352 internal_release_type = S_NO;
353 factory_release_type = S_NO;
354 darwin_release_type = S_NO;
355 recovery_release_type = S_NO;
356 } else if (strcmp(release_type, "NonUI") == 0) {
357 factory_release_type = S_YES;
358 internal_release_type = S_YES;
359 darwin_release_type = S_NO;
360 recovery_release_type = S_NO;
361 } else {
362 factory_release_type = S_NO;
363 internal_release_type = (strstr(release_type, "Internal") != NULL) ? S_YES : S_NO;
364 darwin_release_type = (strstr(release_type, "Darwin") != NULL) ? S_YES : S_NO;
365 recovery_release_type = (strstr(release_type, "Recovery") != NULL) ? S_YES : S_NO;
366 }
367
368 xpc_release(system_version_plist);
369
370 return true;
371 }
372
373 static void _check_system_version_plist_statuses_impl(void)
374 {
375 os_assert(internal_release_type == S_UNKNOWN);
376 os_assert(factory_release_type == S_UNKNOWN);
377 os_assert(darwin_release_type == S_UNKNOWN);
378 os_assert(recovery_release_type == S_UNKNOWN);
379
380 if (!_parse_system_version_plist()) {
381 internal_release_type = (access(INTERNAL_SETTINGS_PATH, F_OK) == 0) ? S_YES : S_NO;
382 factory_release_type = S_NO;
383 darwin_release_type = S_NO;
384 recovery_release_type = S_NO;
385 }
386 }
387 #endif //!TARGET_OS_SIMULATOR
388
389 static bool _check_internal_release_type(void)
390 {
391 #if TARGET_OS_SIMULATOR
392 return _check_internal_content();
393 #else // TARGET_OS_SIMULATOR
394 _initialize_status();
395
396 return status2bool(internal_release_type);
397 #endif // TARGET_OS_SIMULATOR
398 }
399
400 static bool _check_factory_release_type(void)
401 {
402 #if TARGET_OS_SIMULATOR
403 return false;
404 #else // TARGET_OS_SIMULATOR
405 _initialize_status();
406
407 return status2bool(factory_release_type);
408 #endif // TARGET_OS_SIMULATOR
409 }
410
411 static bool _check_darwin_release_type(void)
412 {
413 #if TARGET_OS_SIMULATOR
414 return false;
415 #else // TARGET_OS_SIMULATOR
416 _initialize_status();
417
418 return status2bool(darwin_release_type);
419 #endif // TARGET_OS_SIMULATOR
420 }
421
422 static bool _check_recovery_release_type(void)
423 {
424 #if TARGET_OS_SIMULATOR
425 return false;
426 #else // TARGET_OS_SIMULATOR
427 _initialize_status();
428
429 return status2bool(recovery_release_type);
430 #endif // TARGET_OS_SIMULATOR
431 }
432
433 #else // TARGET_OS_IPHONE
434
435 static void _check_internal_diags_profile_impl(void)
436 {
437 if (_os_xbs_chrooted && internal_diags_profile != S_UNKNOWN) {
438 return;
439 } else {
440 os_assert(internal_diags_profile == S_UNKNOWN);
441 }
442
443 xpc_object_t profile_settings = read_plist(INTERNAL_DIAGS_PROFILE_PATH);
444 if (profile_settings) {
445 internal_diags_profile = xpc_dictionary_get_bool(profile_settings, "AppleInternal") ? S_YES : S_NO;
446 xpc_release(profile_settings);
447 } else {
448 internal_diags_profile = S_NO;
449 }
450 }
451
452 static bool _check_internal_diags_profile(void)
453 {
454 _initialize_status();
455
456 return status2bool(internal_diags_profile);
457 }
458
459 static void _check_base_system_content_impl(void)
460 {
461 if (_os_xbs_chrooted && base_system_content != S_UNKNOWN) {
462 return;
463 } else {
464 os_assert(base_system_content == S_UNKNOWN);
465 }
466
467 const char * path = BASE_SYSTEM_CONTENT_PATH;
468 base_system_content = (access(path, F_OK) == 0) ? S_YES : S_NO;
469 }
470
471 static bool _check_base_system_content(void)
472 {
473 _initialize_status();
474
475 return status2bool(base_system_content);
476 }
477
478 static void _check_darwinos_content_impl(void)
479 {
480 if (_os_xbs_chrooted && darwinos_content != S_UNKNOWN) {
481 return;
482 } else {
483 os_assert(darwinos_content == S_UNKNOWN);
484 }
485
486 const char * path = DARWINOS_CONTENT_PATH;
487 darwinos_content = (access(path, F_OK) == 0) ? S_YES : S_NO;
488 }
489
490 static bool _check_darwinos_content(void)
491 {
492 _initialize_status();
493
494 return status2bool(darwinos_content);
495 }
496
497 #endif
498
499 #if !TARGET_OS_SIMULATOR
500 static void _check_can_has_debugger_impl(void)
501 {
502 if (_os_xbs_chrooted && can_has_debugger != S_UNKNOWN) {
503 return;
504 } else {
505 os_assert(can_has_debugger == S_UNKNOWN);
506 }
507
508 #if TARGET_OS_IPHONE
509 can_has_debugger = *((uint32_t *)_COMM_PAGE_DEV_FIRM) ? S_YES : S_NO;
510 #else
511 /*
512 * The comm page bit does exist on macOS, but also requires kernel
513 * debugging in the CSR configuration. We don't need to be that strict
514 * here.
515 */
516 can_has_debugger = (csr_check(CSR_ALLOW_APPLE_INTERNAL) == 0) ? S_YES : S_NO;
517 #endif
518 }
519
520 static bool _check_can_has_debugger(void)
521 {
522 _initialize_status();
523
524 return status2bool(can_has_debugger);
525 }
526 #endif // !TARGET_OS_SIMULATOR
527
528 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
529 static void _check_development_kernel_impl(void)
530 {
531 os_assert(development_kernel == S_UNKNOWN);
532 /*
533 * Whitelist values from SUPPORTED_KERNEL_CONFIGS.
534 */
535 char *osbuildconfig = NULL;
536 size_t osbuildconfig_sz = 0;
537 errno_t err = sysctlbyname_get_data_np("kern.osbuildconfig", (void **)&osbuildconfig, &osbuildconfig_sz);
538 if (err == 0) {
539 if (strcmp(osbuildconfig, "development") == 0 ||
540 strcmp(osbuildconfig, "debug") == 0 ||
541 strcmp(osbuildconfig, "profile") == 0 ||
542 strcmp(osbuildconfig, "kasan") == 0) {
543 development_kernel = S_YES;
544 }
545 }
546 free(osbuildconfig);
547
548 if (development_kernel == S_UNKNOWN) {
549 development_kernel = S_NO;
550 }
551 }
552
553 static bool _check_development_kernel(void)
554 {
555 _initialize_status();
556
557 return status2bool(development_kernel);
558 }
559 #endif // TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
560
561 static void _check_uses_ephemeral_storage_impl(void)
562 {
563 if (_os_xbs_chrooted && is_ephemeral != S_UNKNOWN) {
564 return;
565 } else {
566 os_assert(is_ephemeral == S_UNKNOWN);
567 }
568
569 uint32_t buffer = 0;
570 size_t buffer_size = sizeof(buffer);
571
572 sysctlbyname("hw.ephemeral_storage", (void *)&buffer, &buffer_size, NULL, 0);
573
574 is_ephemeral = (buffer != 0) ? S_YES : S_NO;
575 }
576
577 static bool _check_uses_ephemeral_storage(void)
578 {
579 _initialize_status();
580
581 return status2bool(is_ephemeral);
582 }
583
584 #if !TARGET_OS_SIMULATOR
585 // internal upcall into libtrace
586 extern bool
587 _os_trace_basesystem_storage_available(void);
588
589 static void
590 _init_has_full_logging(void)
591 {
592 #if TARGET_OS_OSX
593 if (_check_base_system_content() &&
594 !_os_trace_basesystem_storage_available()) {
595 has_full_logging = S_NO;
596 return;
597 }
598 #endif
599
600 has_full_logging = S_YES;
601 }
602
603 static bool _check_has_full_logging(void)
604 {
605 _initialize_status();
606
607 return status2bool(has_full_logging);
608 }
609 #endif // !TARGET_OS_SIMULATOR
610
611 static void _check_all_statuses(void)
612 {
613 #if !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
614 _check_internal_content_impl();
615 #endif
616
617 _check_uses_ephemeral_storage_impl();
618
619 #if !TARGET_OS_SIMULATOR
620 _check_can_has_debugger_impl();
621
622 #if TARGET_OS_IPHONE
623 _check_system_version_plist_statuses_impl();
624 _check_development_kernel_impl();
625 #else
626 _check_internal_diags_profile_impl();
627 _check_factory_content_impl();
628 _check_base_system_content_impl();
629 _check_darwinos_content_impl();
630 #endif
631
632 #endif // !TARGET_OS_SIMULUATOR
633
634 _parse_disabled_status(NULL);
635 }
636
637 static bool
638 os_variant_has_full_logging(const char * __unused subsystem)
639 {
640 #if TARGET_OS_SIMULATOR
641 return true;
642 #else
643 return _check_has_full_logging();
644 #endif
645 }
646
647 static const variant_check_mapping _variant_map[] = {
648 {.variant = "AllowsInternalSecurityPolicies", .function = os_variant_allows_internal_security_policies},
649 {.variant = "HasFactoryContent", .function = os_variant_has_factory_content},
650 {.variant = "HasFullLogging", .function = os_variant_has_full_logging},
651 {.variant = "HasInternalContent", .function = os_variant_has_internal_content},
652 {.variant = "HasInternalDiagnostics", .function = os_variant_has_internal_diagnostics},
653 {.variant = "HasInternalUI", .function = os_variant_has_internal_ui},
654 #if TARGET_OS_OSX
655 {.variant = "IsBaseSystem", .function = os_variant_is_basesystem},
656 #endif
657 {.variant = "IsDarwinOS", .function = os_variant_is_darwinos},
658 {.variant = "IsRecovery", .function = os_variant_is_recovery},
659 {.variant = "UsesEphemeralStorage", .function = os_variant_uses_ephemeral_storage},
660 {.variant = NULL, .function = NULL}
661 };
662
663 // For unit tests
664 #ifndef VARIANT_SKIP_EXPORTED
665
666 bool
667 os_variant_has_internal_content(const char * __unused subsystem)
668 {
669 if (_check_disabled(VP_CONTENT)) {
670 return false;
671 }
672
673 #if TARGET_OS_IPHONE
674 return _check_internal_release_type();
675 #else
676 return _check_internal_content();
677 #endif
678 }
679
680
681 bool
682 os_variant_has_internal_diagnostics(const char * __unused subsystem)
683 {
684 if (_check_disabled(VP_DIAGNOSTICS)) {
685 return false;
686 }
687
688 #if TARGET_OS_IPHONE
689 return _check_internal_release_type();
690 #else
691 return _check_internal_content() || _check_internal_diags_profile();
692 #endif
693 }
694
695 bool
696 os_variant_has_internal_ui(const char * __unused subsystem)
697 {
698 if (_check_disabled(VP_UI)) {
699 return false;
700 }
701
702 #if TARGET_OS_IPHONE
703 return _check_internal_release_type();
704 #else
705 return _check_internal_content();
706 #endif
707 }
708
709 bool
710 os_variant_allows_internal_security_policies(const char * __unused subsystem)
711 {
712 if (_check_disabled(VP_SECURITY)) {
713 return false;
714 }
715
716 #if TARGET_OS_SIMULATOR
717 return _check_internal_content();
718 #elif TARGET_OS_IPHONE
719 return _check_can_has_debugger() || _check_development_kernel();
720 #else
721 return _check_can_has_debugger();
722 #endif
723 }
724
725 bool
726 os_variant_has_factory_content(const char * __unused subsystem)
727 {
728 #if TARGET_OS_IPHONE
729 return _check_factory_release_type();
730 #else
731 return _check_factory_content();
732 #endif
733 }
734
735 bool
736 os_variant_is_darwinos(const char * __unused subsystem)
737 {
738 #if TARGET_OS_IPHONE
739 return _check_darwin_release_type();
740 #else
741 return _check_darwinos_content();
742 #endif
743 }
744
745 bool
746 os_variant_is_recovery(const char * __unused subsystem)
747 {
748 #if TARGET_OS_IPHONE
749 return _check_recovery_release_type();
750 #else
751 return _check_base_system_content();
752 #endif
753 }
754
755 #if TARGET_OS_OSX
756 bool
757 os_variant_is_basesystem(const char * __unused subsystem)
758 {
759 return _check_base_system_content();
760 }
761 #endif
762
763 bool
764 os_variant_uses_ephemeral_storage(const char * __unused subsystem)
765 {
766 return _check_uses_ephemeral_storage();
767 }
768
769 bool
770 os_variant_check(const char *subsystem, const char *variant)
771 {
772 variant_check_mapping *current = (variant_check_mapping *)_variant_map;
773
774 while (current->variant) {
775 if (0 == strncasecmp(current->variant, variant, strlen(current->variant))) {
776 return current->function(subsystem);
777 }
778 current ++;
779 }
780
781 return false;
782 }
783
784 char *
785 os_variant_copy_description(const char *subsystem)
786 {
787 variant_check_mapping *current = (variant_check_mapping *)_variant_map;
788
789 char *desc = NULL;
790 size_t desc_size = 0;
791 FILE *outstream = open_memstream(&desc, &desc_size);
792 if (!outstream) {
793 return NULL;
794 }
795
796 int error = 0;
797 bool needs_space = false;
798 while (current->variant) {
799 if (current->function(subsystem)) {
800 if (needs_space) {
801 int written = fputc(' ', outstream);
802 if (written == EOF) {
803 error = errno;
804 goto error_out;
805 }
806 }
807 int written = fputs(current->variant, outstream);
808 if (written == EOF) {
809 error = errno;
810 goto error_out;
811 }
812 needs_space = true;
813 }
814 current++;
815 }
816
817 int closed = fclose(outstream);
818 if (closed == EOF) {
819 error = errno;
820 goto close_error_out;
821 }
822 return desc;
823
824 error_out:
825 (void)fclose(outstream);
826 close_error_out:
827 free(desc);
828 errno = error;
829 return NULL;
830 }
831
832 #if TARGET_OS_OSX
833
834 // XXX As an implementation detail, os_boot_mode is piggy-backing on
835 // os_variant's infrastructure. This is not necessarily its long-term home,
836 // particularly after rdar://59966472
837
838 static enum boot_mode {
839 BOOTMODE_UNKNOWN = 0,
840 BOOTMODE_NONE,
841 BOOTMODE_FVUNLOCK,
842 BOOTMODE_KCGEN,
843 BOOTMODE_DIAGNOSTICS,
844 BOOTMODE_MIGRATION,
845 } os_boot_mode;
846
847 static void
848 _os_boot_mode_launchd_init(const char *boot_mode)
849 {
850 if (boot_mode == NULL) {
851 os_boot_mode = BOOTMODE_NONE;
852 } else if (strcmp(boot_mode, OS_BOOT_MODE_FVUNLOCK) == 0) {
853 os_boot_mode = BOOTMODE_FVUNLOCK;
854 } else if (strcmp(boot_mode, OS_BOOT_MODE_KCGEN) == 0) {
855 os_boot_mode = BOOTMODE_KCGEN;
856 } else if (strcmp(boot_mode, OS_BOOT_MODE_DIAGNOSTICS) == 0) {
857 os_boot_mode = BOOTMODE_DIAGNOSTICS;
858 } else if (strcmp(boot_mode, OS_BOOT_MODE_MIGRATION) == 0) {
859 os_boot_mode = BOOTMODE_MIGRATION;
860 }
861 }
862
863 bool
864 os_boot_mode_query(const char **boot_mode_out)
865 {
866 _initialize_status();
867
868 switch (os_boot_mode) {
869 case BOOTMODE_NONE:
870 *boot_mode_out = NULL;
871 return true;
872 case BOOTMODE_FVUNLOCK:
873 *boot_mode_out = OS_BOOT_MODE_FVUNLOCK;
874 return true;
875 case BOOTMODE_KCGEN:
876 *boot_mode_out = OS_BOOT_MODE_KCGEN;
877 return true;
878 case BOOTMODE_DIAGNOSTICS:
879 *boot_mode_out = OS_BOOT_MODE_DIAGNOSTICS;
880 return true;
881 case BOOTMODE_MIGRATION:
882 *boot_mode_out = OS_BOOT_MODE_MIGRATION;
883 return true;
884 default:
885 return false;
886 }
887 }
888
889 #endif // TARGET_OS_OSX
890
891 void
892 os_variant_init_4launchd(const char *boot_mode)
893 {
894 #if TARGET_OS_SIMULATOR
895 os_crash("simulator launchd does not initialize os_variant");
896 #else
897 os_assert(getpid() == 1);
898
899 _init_has_full_logging();
900
901 #if TARGET_OS_OSX
902 _os_boot_mode_launchd_init(boot_mode);
903 #endif
904
905 // re-initialize disabled status even if we've already initialized
906 // previously, as it's possible we may have initialized before the override
907 // file was available to read
908 _parse_disabled_status(NULL);
909
910 uint64_t status = _get_cached_check_status();
911 size_t status_size = sizeof(status);
912 // TODO: assert that this succeeds
913 sysctlbyname(CACHE_SYSCTL_NAME, NULL, 0, &status, status_size);
914 #endif
915 }
916
917 #endif // VARIANT_SKIP_EXPORTED
918
919 /*
920 * Bit allocation in kern.osvariant_status (all ranges inclusive):
921 * - [0-27] are 2-bit check_status values
922 * - [28-31] are 0xF
923 * - [32-32+VP_MAX-1] encode variant_property booleans
924 * - [48-51] encode the boot mode, if known
925 * - [60-62] are 0x7
926 */
927 #define STATUS_INITIAL_BITS 0x70000000F0000000ULL
928 #define STATUS_BIT_WIDTH 2
929 #define STATUS_SET 0x2
930 #define STATUS_MASK 0x3
931
932 enum status_flags_positions {
933 SFP_INTERNAL_CONTENT = 0,
934 SFP_CAN_HAS_DEBUGGER = 1,
935 SFP_INTERNAL_RELEASE_TYPE = 2,
936 SFP_INTERNAL_DIAGS_PROFILE = 3,
937 SFP_FACTORY_CONTENT = 4,
938 SFP_FACTORY_RELEASE_TYPE = 5,
939 SFP_DARWINOS_RELEASE_TYPE = 6,
940 SFP_EPHEMERAL_VOLUME = 7,
941 SFP_RECOVERY_RELEASE_TYPE = 8,
942 SFP_BASE_SYSTEM_CONTENT = 9,
943 SFP_DEVELOPMENT_KERNEL = 10,
944 SFP_DARWINOS_CONTENT = 11,
945 SFP_FULL_LOGGING = 12,
946 };
947
948 #define STATUS_BOOT_MODE_SHIFT 48
949 #define STATUS_BOOT_MODE_MASK 0x000F000000000000ULL
950
951 #if !TARGET_OS_SIMULATOR
952 static uint64_t _get_cached_check_status(void)
953 {
954 _initialize_status();
955
956 uint64_t res = STATUS_INITIAL_BITS;
957
958 #if !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
959 os_assert(internal_content != S_UNKNOWN);
960 res |= internal_content << SFP_INTERNAL_CONTENT * STATUS_BIT_WIDTH;
961 #endif
962
963 os_assert(can_has_debugger != S_UNKNOWN);
964 res |= can_has_debugger << SFP_CAN_HAS_DEBUGGER * STATUS_BIT_WIDTH;
965
966 os_assert(is_ephemeral != S_UNKNOWN);
967 res |= is_ephemeral << SFP_EPHEMERAL_VOLUME * STATUS_BIT_WIDTH;
968
969 #ifdef VARIANT_SKIP_EXPORTED
970 // has_full_logging can't be computed outside launchd, so in the tests/etc.
971 // cheat and use the value reported by libdarwin rather than re-computing
972 has_full_logging = os_variant_check("com.apple.Libc.tests", "HasFullLogging") ?
973 S_YES : S_NO;
974 #else
975 os_assert(has_full_logging != S_UNKNOWN);
976 #endif
977 res |= has_full_logging << SFP_FULL_LOGGING * STATUS_BIT_WIDTH;
978
979 #if TARGET_OS_IPHONE
980 os_assert(internal_release_type != S_UNKNOWN);
981 res |= internal_release_type << SFP_INTERNAL_RELEASE_TYPE * STATUS_BIT_WIDTH;
982
983 os_assert(factory_release_type != S_UNKNOWN);
984 res |= factory_release_type << SFP_FACTORY_RELEASE_TYPE * STATUS_BIT_WIDTH;
985
986 os_assert(darwin_release_type != S_UNKNOWN);
987 res |= darwin_release_type << SFP_DARWINOS_RELEASE_TYPE * STATUS_BIT_WIDTH;
988
989 os_assert(recovery_release_type != S_UNKNOWN);
990 res |= recovery_release_type << SFP_RECOVERY_RELEASE_TYPE * STATUS_BIT_WIDTH;
991
992 os_assert(development_kernel != S_UNKNOWN);
993 res |= development_kernel << SFP_DEVELOPMENT_KERNEL * STATUS_BIT_WIDTH;
994 #else
995 os_assert(internal_diags_profile != S_UNKNOWN);
996 res |= internal_diags_profile << SFP_INTERNAL_DIAGS_PROFILE * STATUS_BIT_WIDTH;
997
998 os_assert(factory_content != S_UNKNOWN);
999 res |= factory_content << SFP_FACTORY_CONTENT * STATUS_BIT_WIDTH;
1000
1001 os_assert(base_system_content != S_UNKNOWN);
1002 res |= base_system_content << SFP_BASE_SYSTEM_CONTENT * STATUS_BIT_WIDTH;
1003
1004 os_assert(darwinos_content != S_UNKNOWN);
1005 res |= darwinos_content << SFP_DARWINOS_CONTENT * STATUS_BIT_WIDTH;
1006 #endif
1007
1008 for (int i = 0; i < VP_MAX; i++) {
1009 if (disabled_status[i]) {
1010 res |= 0x1ULL << (i + 32);
1011 }
1012 }
1013
1014 #if !defined(VARIANT_SKIP_EXPORTED) && TARGET_OS_OSX
1015 res |= ((uint64_t)os_boot_mode) << STATUS_BOOT_MODE_SHIFT;
1016 #endif // TARGET_OS_OSX
1017
1018 return res;
1019 }
1020
1021 static void _restore_cached_check_status(uint64_t status)
1022 {
1023 #if !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
1024 if ((status >> (SFP_INTERNAL_CONTENT * STATUS_BIT_WIDTH)) & STATUS_SET)
1025 internal_content = (status >> (SFP_INTERNAL_CONTENT * STATUS_BIT_WIDTH)) & STATUS_MASK;
1026 #endif
1027
1028 if ((status >> (SFP_CAN_HAS_DEBUGGER * STATUS_BIT_WIDTH)) & STATUS_SET)
1029 can_has_debugger = (status >> (SFP_CAN_HAS_DEBUGGER * STATUS_BIT_WIDTH)) & STATUS_MASK;
1030
1031 if ((status >> (SFP_EPHEMERAL_VOLUME * STATUS_BIT_WIDTH)) & STATUS_SET)
1032 is_ephemeral = (status >> (SFP_EPHEMERAL_VOLUME * STATUS_BIT_WIDTH)) & STATUS_MASK;
1033
1034 if ((status >> (SFP_FULL_LOGGING * STATUS_BIT_WIDTH)) & STATUS_SET)
1035 has_full_logging = (status >> (SFP_FULL_LOGGING * STATUS_BIT_WIDTH)) & STATUS_MASK;
1036
1037 #if TARGET_OS_IPHONE
1038 if ((status >> (SFP_INTERNAL_RELEASE_TYPE * STATUS_BIT_WIDTH)) & STATUS_SET)
1039 internal_release_type = (status >> (SFP_INTERNAL_RELEASE_TYPE * STATUS_BIT_WIDTH)) & STATUS_MASK;
1040
1041 if ((status >> (SFP_FACTORY_RELEASE_TYPE * STATUS_BIT_WIDTH)) & STATUS_SET)
1042 factory_release_type = (status >> (SFP_FACTORY_RELEASE_TYPE * STATUS_BIT_WIDTH)) & STATUS_MASK;
1043
1044 if ((status >> (SFP_DARWINOS_RELEASE_TYPE * STATUS_BIT_WIDTH)) & STATUS_SET)
1045 darwin_release_type = (status >> (SFP_DARWINOS_RELEASE_TYPE * STATUS_BIT_WIDTH)) & STATUS_MASK;
1046
1047 if ((status >> (SFP_RECOVERY_RELEASE_TYPE * STATUS_BIT_WIDTH)) & STATUS_SET)
1048 recovery_release_type = (status >> (SFP_RECOVERY_RELEASE_TYPE * STATUS_BIT_WIDTH)) & STATUS_MASK;
1049
1050 if ((status >> (SFP_DEVELOPMENT_KERNEL * STATUS_BIT_WIDTH)) & STATUS_SET)
1051 development_kernel = (status >> (SFP_DEVELOPMENT_KERNEL * STATUS_BIT_WIDTH)) & STATUS_MASK;
1052 #else
1053 if ((status >> (SFP_INTERNAL_DIAGS_PROFILE * STATUS_BIT_WIDTH)) & STATUS_SET)
1054 internal_diags_profile = (status >> (SFP_INTERNAL_DIAGS_PROFILE * STATUS_BIT_WIDTH)) & STATUS_MASK;
1055
1056 if ((status >> (SFP_FACTORY_CONTENT * STATUS_BIT_WIDTH)) & STATUS_SET)
1057 factory_content = (status >> (SFP_FACTORY_CONTENT * STATUS_BIT_WIDTH)) & STATUS_MASK;
1058
1059 if ((status >> (SFP_BASE_SYSTEM_CONTENT * STATUS_BIT_WIDTH)) & STATUS_SET)
1060 base_system_content = (status >> (SFP_BASE_SYSTEM_CONTENT * STATUS_BIT_WIDTH)) & STATUS_MASK;
1061
1062 if ((status >> (SFP_DARWINOS_CONTENT * STATUS_BIT_WIDTH)) & STATUS_SET)
1063 darwinos_content = (status >> (SFP_DARWINOS_CONTENT * STATUS_BIT_WIDTH)) & STATUS_MASK;
1064 #endif
1065
1066 for (int i = 0; i < VP_MAX; i++) {
1067 disabled_status[i] = (status >> (32 + i)) & 0x1;
1068 }
1069
1070 #if !defined(VARIANT_SKIP_EXPORTED) && TARGET_OS_OSX
1071 os_boot_mode = (enum boot_mode)((status & STATUS_BOOT_MODE_MASK) >> STATUS_BOOT_MODE_SHIFT);
1072 #endif // TARGET_OS_OSX
1073 }
1074 #endif // !TARGET_OS_SIMULATOR