1/*
2 *  kextload_main.c
3 *  kext_tools
4 *
5 *  Created by Nik Gervae on 11/08/08.
6 *  Copyright 2008 Apple Inc. All rights reserved.
7 *
8 */
9#include "kextload_main.h"
10#include "kext_tools_util.h"
11#include "security.h"
12
13#include <libc.h>
14#include <servers/bootstrap.h>
15#include <sysexits.h>
16#include <Security/SecKeychainPriv.h>
17
18#include <IOKit/kext/KextManager.h>
19#include <IOKit/kext/KextManagerPriv.h>
20#include <IOKit/kext/kextmanager_types.h>
21#include <IOKit/kext/OSKextPrivate.h>
22
23#pragma mark Constants
24/*******************************************************************************
25* Constants
26*******************************************************************************/
27
28#pragma mark Global/Static Variables
29/*******************************************************************************
30* Global/Static Variables
31*******************************************************************************/
32const char      * progname     = "(unknown)";
33static Boolean    sKextdActive = FALSE;
34
35#pragma mark Main Routine
36/*******************************************************************************
37* Global variables.
38*******************************************************************************/
39ExitStatus
40main(int argc, char * const * argv)
41{
42    ExitStatus   result = EX_SOFTWARE;
43    KextloadArgs toolArgs;
44
45   /*****
46    * Find out what the program was invoked as.
47    */
48    progname = rindex(argv[0], '/');
49    if (progname) {
50        progname++;   // go past the '/'
51    } else {
52        progname = (char *)argv[0];
53    }
54
55   /* Set the OSKext log callback right away.
56    */
57    OSKextSetLogOutputFunction(&tool_log);
58
59   /*****
60    * Process args & check for permission to load.
61    */
62    result = readArgs(argc, argv, &toolArgs);
63    if (result != EX_OK) {
64        if (result == kKextloadExitHelp) {
65            result = EX_OK;
66        }
67        goto finish;
68    }
69
70    result = checkArgs(&toolArgs);
71    if (result != EX_OK) {
72        goto finish;
73    }
74
75    result = checkAccess();
76    if (result != EX_OK) {
77        goto finish;
78    }
79
80   /*****
81    * Assemble the list of URLs to scan, in this order (the OSKext lib inverts it
82    * for last-opened-wins semantics):
83    * 1. System repository directories (if not asking kextd to load).
84    * 2. Named kexts (always given after -repository & -dependency on command line).
85    * 3. Named repository directories (-repository/-r).
86    * 4. Named dependencies get priority (-dependency/-d).
87    *
88    * #2 is necessary since one might try to run kextload on two kexts,
89    * one of which depends on the other.
90    */
91    if (!sKextdActive) {
92        CFArrayRef sysExtFolders = OSKextGetSystemExtensionsFolderURLs();
93        if (!sysExtFolders) {
94            OSKextLog(/* kext */ NULL,
95                kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
96                "Can't get system extensions folders.");
97            result = EX_OSERR;
98            goto finish;
99        }
100        CFArrayAppendArray(toolArgs.scanURLs,
101            sysExtFolders, RANGE_ALL(sysExtFolders));
102    }
103    CFArrayAppendArray(toolArgs.scanURLs, toolArgs.kextURLs,
104        RANGE_ALL(toolArgs.kextURLs));
105    CFArrayAppendArray(toolArgs.scanURLs, toolArgs.repositoryURLs,
106        RANGE_ALL(toolArgs.repositoryURLs));
107    CFArrayAppendArray(toolArgs.scanURLs, toolArgs.dependencyURLs,
108        RANGE_ALL(toolArgs.dependencyURLs));
109
110    if (sKextdActive) {
111        result = loadKextsViaKextd(&toolArgs);
112    } else {
113        result = loadKextsIntoKernel(&toolArgs);
114    }
115
116finish:
117
118   /* We're actually not going to free anything else because we're exiting!
119    */
120    exit(result);
121
122    SAFE_RELEASE(toolArgs.kextIDs);
123    SAFE_RELEASE(toolArgs.dependencyURLs);
124    SAFE_RELEASE(toolArgs.repositoryURLs);
125    SAFE_RELEASE(toolArgs.kextURLs);
126    SAFE_RELEASE(toolArgs.scanURLs);
127    SAFE_RELEASE(toolArgs.allKexts);
128
129    return result;
130}
131
132#pragma mark Major Subroutines
133
134/*******************************************************************************
135* Major Subroutines
136*******************************************************************************/
137ExitStatus
138readArgs(
139    int            argc,
140    char * const * argv,
141    KextloadArgs * toolArgs)
142{
143    ExitStatus   result          = EX_USAGE;
144    ExitStatus   scratchResult   = EX_USAGE;
145    int          optchar;
146    int          longindex;
147    CFStringRef  scratchString   = NULL;  // must release
148    CFURLRef     scratchURL      = NULL;  // must release
149    uint32_t     i;
150
151   /* Set up default arg values.
152    */
153    bzero(toolArgs, sizeof(*toolArgs));
154
155   /*****
156    * Allocate collection objects needed for reading args.
157    */
158    if (!createCFMutableArray(&toolArgs->kextIDs, &kCFTypeArrayCallBacks)         ||
159        !createCFMutableArray(&toolArgs->dependencyURLs, &kCFTypeArrayCallBacks)  ||
160        !createCFMutableArray(&toolArgs->repositoryURLs, &kCFTypeArrayCallBacks)  ||
161        !createCFMutableArray(&toolArgs->kextURLs, &kCFTypeArrayCallBacks)        ||
162        !createCFMutableArray(&toolArgs->scanURLs, &kCFTypeArrayCallBacks)) {
163
164        result = EX_OSERR;
165        OSKextLogMemError();
166        exit(result);
167    }
168
169    while ((optchar = getopt_long_only(argc, (char * const *)argv,
170        kOptChars, sOptInfo, &longindex)) != -1) {
171
172        SAFE_RELEASE_NULL(scratchString);
173        SAFE_RELEASE_NULL(scratchURL);
174
175        switch (optchar) {
176            case kOptHelp:
177                usage(kUsageLevelFull);
178                result = kKextloadExitHelp;
179                goto finish;
180                break;
181
182            case kOptBundleIdentifier:
183                scratchString = CFStringCreateWithCString(kCFAllocatorDefault,
184                    optarg, kCFStringEncodingUTF8);
185                if (!scratchString) {
186                    OSKextLogMemError();
187                    result = EX_OSERR;
188                    goto finish;
189                }
190                CFArrayAppendValue(toolArgs->kextIDs, scratchString);
191                break;
192
193            case kOptDependency:
194            case kOptRepository:
195                scratchURL = CFURLCreateFromFileSystemRepresentation(
196                    kCFAllocatorDefault,
197                    (const UInt8 *)optarg, strlen(optarg), true);
198                if (!scratchURL) {
199                    OSKextLogStringError(/* kext */ NULL);
200                    result = EX_OSERR;
201                    goto finish;
202                }
203                CFArrayAppendValue((optchar == kOptDependency) ?
204                    toolArgs->dependencyURLs : toolArgs->repositoryURLs,
205                    scratchURL);
206                break;
207
208            case kOptQuiet:
209                beQuiet();
210                break;
211
212            case kOptVerbose:
213                scratchResult = setLogFilterForOpt(argc, argv, /* forceOnFlags */ 0);
214                if (scratchResult != EX_OK) {
215                    result = scratchResult;
216                    goto finish;
217                }
218                break;
219
220            case kOptNoCaches:
221                OSKextLog(/* kext */ NULL,
222                    kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
223                    "Notice: -%s (-%c) ignored; use kextutil(8) to test kexts.",
224                    kOptNameNoCaches, kOptNoCaches);
225                break;
226
227            case kOptNoLoadedCheck:
228                OSKextLog(/* kext */ NULL,
229                    kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
230                    "Notice: -%s (-%c) ignored.",
231                    kOptNameNoLoadedCheck, kOptNoLoadedCheck);
232                break;
233
234            case kOptTests:
235                OSKextLog(/* kext */ NULL,
236                    kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
237                    "Notice: -%s (-%c) ignored; use kextutil(8) to test kexts.",
238                    kOptNameTests, kOptTests);
239                break;
240
241            case 0:
242                switch (longopt) {
243                   default:
244                        OSKextLog(/* kext */ NULL,
245                            kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
246                            "Use kextutil(8) for development loading of kexts.");
247                        goto finish;
248                        break;
249                }
250                break;
251
252            default:
253                OSKextLog(/* kext */ NULL,
254                    kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
255                    "Use kextutil(8) for development loading of kexts.");
256                goto finish;
257                break;
258
259        } /* switch (optchar) */
260    } /* while (optchar = getopt_long_only(...) */
261
262   /*****
263    * Record the kext names from the command line.
264    */
265    for (i = optind; (int)i < argc; i++) {
266        SAFE_RELEASE_NULL(scratchURL);
267        scratchURL = CFURLCreateFromFileSystemRepresentation(
268            kCFAllocatorDefault,
269            (const UInt8 *)argv[i], strlen(argv[i]), true);
270        if (!scratchURL) {
271            result = EX_OSERR;
272            OSKextLogMemError();
273            goto finish;
274        }
275        CFArrayAppendValue(toolArgs->kextURLs, scratchURL);
276    }
277
278    result = EX_OK;
279
280finish:
281    SAFE_RELEASE(scratchString);
282    SAFE_RELEASE(scratchURL);
283
284    if (result == EX_USAGE) {
285        usage(kUsageLevelBrief);
286    }
287    return result;
288}
289
290/*******************************************************************************
291*******************************************************************************/
292ExitStatus
293checkArgs(KextloadArgs * toolArgs)
294{
295    ExitStatus         result         = EX_USAGE;
296
297    if (!CFArrayGetCount(toolArgs->kextURLs) &&
298        !CFArrayGetCount(toolArgs->kextIDs)) {
299
300        OSKextLog(/* kext */ NULL,
301            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
302            "No kernel extensions specified; name kernel extension bundles\n"
303            "    following options, or use -%s (-%c).",
304            kOptNameBundleIdentifier, kOptBundleIdentifier);
305        goto finish;
306    }
307
308    result = EX_OK;
309
310finish:
311    if (result == EX_USAGE) {
312        usage(kUsageLevelBrief);
313    }
314    return result;
315}
316
317/*******************************************************************************
318*******************************************************************************/
319ExitStatus checkAccess(void)
320{
321    ExitStatus    result         = EX_OK;
322#if !TARGET_OS_EMBEDDED
323    kern_return_t kern_result    = kOSReturnError;
324    mach_port_t   kextd_port     = MACH_PORT_NULL;
325
326    kern_result = bootstrap_look_up(bootstrap_port,
327        (char *)KEXTD_SERVER_NAME, &kextd_port);
328
329    if (kern_result == kOSReturnSuccess) {
330        sKextdActive = TRUE;
331    } else {
332        if (geteuid() == 0) {
333            OSKextLog(/* kext */ NULL,
334                kOSKextLogBasicLevel | kOSKextLogGeneralFlag |
335                kOSKextLogLoadFlag | kOSKextLogIPCFlag,
336                "Can't contact kextd; attempting to load directly into kernel.");
337        } else {
338            OSKextLog(/* kext */ NULL,
339                kOSKextLogErrorLevel | kOSKextLogGeneralFlag |
340                kOSKextLogLoadFlag | kOSKextLogIPCFlag,
341                "Can't contact kextd; must run as root to load kexts.");
342            result = EX_NOPERM;
343            goto finish;
344        }
345    }
346
347#else
348
349    if (geteuid() != 0) {
350        OSKextLog(/* kext */ NULL,
351            kOSKextLogErrorLevel | kOSKextLogGeneralFlag |
352            kOSKextLogLoadFlag | kOSKextLogIPCFlag,
353            "You must be running as root to load kexts.");
354        result = EX_NOPERM;
355        goto finish;
356    }
357
358#endif /* !TARGET_OS_EMBEDDED */
359
360finish:
361
362#if !TARGET_OS_EMBEDDED
363    if (kextd_port != MACH_PORT_NULL) {
364        mach_port_deallocate(mach_task_self(), kextd_port);
365    }
366#endif /* !TARGET_OS_EMBEDDED */
367
368    return result;
369}
370
371/*******************************************************************************
372*******************************************************************************/
373ExitStatus loadKextsViaKextd(KextloadArgs * toolArgs)
374{
375    ExitStatus result     = EX_OK;
376    OSReturn   loadResult = kOSReturnError;
377    char       scratchCString[PATH_MAX];
378    CFIndex    count, index;
379
380    count = CFArrayGetCount(toolArgs->kextIDs);
381    for (index = 0; index < count; index++) {
382        CFStringRef kextID  = CFArrayGetValueAtIndex(toolArgs->kextIDs, index);
383
384        if (!CFStringGetCString(kextID, scratchCString, sizeof(scratchCString),
385            kCFStringEncodingUTF8)) {
386
387            strlcpy(scratchCString, "unknown", sizeof(scratchCString));
388        }
389
390        OSKextLog(/* kext */ NULL,
391            kOSKextLogBasicLevel | kOSKextLogGeneralFlag |
392            kOSKextLogLoadFlag | kOSKextLogIPCFlag,
393            "Requesting load of %s.",
394            scratchCString);
395
396        loadResult = KextManagerLoadKextWithIdentifier(kextID,
397            toolArgs->scanURLs);
398        if (loadResult != kOSReturnSuccess) {
399            OSKextLog(/* kext */ NULL,
400                kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
401                "%s failed to load - %s; "
402                "check the system/kernel logs for errors or try kextutil(8).",
403                scratchCString, safe_mach_error_string(loadResult));
404            if (result == EX_OK) {
405                result = exitStatusForOSReturn(loadResult);
406                // keep trying other kexts though
407            }
408        } else {
409            OSKextLog(/* kext */ NULL,
410                kOSKextLogBasicLevel | kOSKextLogGeneralFlag | kOSKextLogLoadFlag,
411                "%s loaded successfully (or already loaded).",
412                scratchCString);
413        }
414    }
415
416    count = CFArrayGetCount(toolArgs->kextURLs);
417    for (index = 0; index < count; index++) {
418        CFURLRef kextURL = CFArrayGetValueAtIndex(toolArgs->kextURLs, index);
419        if (!CFURLGetFileSystemRepresentation(kextURL, /* resolveToBase */ true,
420            (UInt8 *)scratchCString, sizeof(scratchCString))) {
421
422            strlcpy(scratchCString, "unknown", sizeof(scratchCString));
423        }
424
425        OSKextLog(/* kext */ NULL,
426            kOSKextLogBasicLevel | kOSKextLogGeneralFlag |
427            kOSKextLogLoadFlag | kOSKextLogIPCFlag,
428            "Requesting load of %s.",
429            scratchCString);
430
431        loadResult = KextManagerLoadKextWithURL(kextURL,
432            toolArgs->scanURLs);
433        if (loadResult != kOSReturnSuccess) {
434            OSKextLog(/* kext */ NULL,
435                kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
436                "%s failed to load - %s; "
437                "check the system/kernel logs for errors or try kextutil(8).",
438                scratchCString, safe_mach_error_string(loadResult));
439            if (result == EX_OK) {
440                result = exitStatusForOSReturn(loadResult);
441                // keep trying other kexts though
442            }
443        } else {
444            OSKextLog(/* kext */ NULL,
445                kOSKextLogBasicLevel | kOSKextLogGeneralFlag | kOSKextLogLoadFlag,
446                "%s loaded successfully (or already loaded).",
447                scratchCString);
448        }
449    }
450
451    return result;
452}
453
454/*******************************************************************************
455*******************************************************************************/
456ExitStatus loadKextsIntoKernel(KextloadArgs * toolArgs)
457{
458    ExitStatus result     = EX_OK;
459    OSReturn   loadResult = kOSReturnError;
460    char       scratchCString[PATH_MAX];
461    CFIndex    count, index;
462#if !TARGET_OS_EMBEDDED
463    Boolean     earlyBoot = false;
464    Boolean     readOnly = false;
465#endif
466
467    OSKextLog(/* kext */ NULL,
468        kOSKextLogProgressLevel | kOSKextLogGeneralFlag,
469        "Reading extensions.");
470    toolArgs->allKexts = OSKextCreateKextsFromURLs(kCFAllocatorDefault,
471        toolArgs->scanURLs);
472    if (!toolArgs->allKexts) {
473        OSKextLog(/* kext */ NULL,
474            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
475            "Can't read kexts from disk.");
476        result = EX_OSERR;
477        goto finish;
478    }
479#if !TARGET_OS_EMBEDDED
480    // not perfect, but we check to see if kextd is running to determine
481    // if we are in early boot.
482    int     skc_result;
483
484    earlyBoot = (isKextdRunning() == false);
485    skc_result = callSecKeychainMDSInstall();
486    if (skc_result != 0) {
487        // SecKeychainMDSInstall should never fail, except on read only FS.
488        // We get -67674 when SecKeychainMDSInstall can't write to its DB.
489        // see 18367703
490        if (earlyBoot && skc_result == -67674) {
491           struct statfs statfsBuffer;
492            if (statfs("/System/Library", &statfsBuffer) == 0) {
493                if (statfsBuffer.f_flags & MNT_RDONLY) {
494                    readOnly = true;
495                }
496            }
497        }
498        else {
499            goto finish;
500        }
501    }
502#endif
503    count = CFArrayGetCount(toolArgs->kextIDs);
504    for (index = 0; index < count; index++) {
505        OSKextRef     theKext = NULL;  // do not release
506        CFStringRef   kextID  = CFArrayGetValueAtIndex(
507                                                       toolArgs->kextIDs,
508                                                       index);
509
510        if (!CFStringGetCString(kextID, scratchCString, sizeof(scratchCString),
511            kCFStringEncodingUTF8)) {
512
513            strlcpy(scratchCString, "unknown", sizeof(scratchCString));
514        }
515
516        OSKextLog(/* kext */ NULL,
517            kOSKextLogBasicLevel | kOSKextLogGeneralFlag |
518            kOSKextLogLoadFlag | kOSKextLogIPCFlag,
519            "Loading %s.",
520            scratchCString);
521
522        theKext = OSKextGetKextWithIdentifier(kextID);
523        if (!theKext) {
524            OSKextLog(/* kext */ NULL,
525                kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
526                "Error: Kext %s - not found/unable to create.", scratchCString);
527            result = kOSKextReturnNotFound;
528            goto finish;
529        }
530
531#if !TARGET_OS_EMBEDDED
532        // temp change for 18367703
533        if (readOnly &&
534            (CFStringCompare(kextID, CFSTR("com.apple.nke.asp-tcp"), 0) == kCFCompareEqualTo
535             ||
536             CFStringCompare(kextID, CFSTR("com.apple.filesystems.afpfs"), 0) == kCFCompareEqualTo)) {
537                OSKextLogCFString(NULL,
538                                  kOSKextLogErrorLevel | kOSKextLogLoadFlag,
539                                  CFSTR("Netboot, loading '%@'"),
540                                  kextID);
541        }
542        else {
543            OSStatus  sigResult = checkKextSignature(theKext, true, earlyBoot);
544            if ( sigResult != 0 ) {
545                if ( isInvalidSignatureAllowed() ) {
546                    OSKextLogCFString(NULL,
547                                      kOSKextLogErrorLevel | kOSKextLogLoadFlag,
548                                      CFSTR("kext-dev-mode allowing invalid signature %ld 0x%02lX for kext '%s'"),
549                                      (long)sigResult, (long)sigResult,
550                                      scratchCString);
551                }
552                else {
553                    OSKextLogCFString(NULL,
554                                      kOSKextLogErrorLevel |
555                                      kOSKextLogLoadFlag | kOSKextLogIPCFlag,
556                                      CFSTR("ERROR: invalid signature for '%s', will not load"),
557                                      scratchCString);
558                    result = sigResult;
559                    goto finish;
560                }
561            }
562        }
563#endif // not TARGET_OS_EMBEDDED
564
565        /* The codepath from this function will do any error logging
566        * and cleanup needed.
567        */
568        loadResult = OSKextLoadWithOptions(theKext,
569            /* statExclusion */ kOSKextExcludeNone,
570            /* addPersonalitiesExclusion */ kOSKextExcludeNone,
571            /* personalityNames */ NULL,
572            /* delayAutounloadFlag */ false);
573
574        if (loadResult != kOSReturnSuccess) {
575            OSKextLog(/* kext */ NULL,
576                kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
577                "%s failed to load - %s.",
578                scratchCString, safe_mach_error_string(loadResult));
579            if (result == EX_OK) {
580                result = exitStatusForOSReturn(loadResult);
581                // keep trying other kexts though
582            }
583        } else {
584            OSKextLog(/* kext */ NULL,
585                kOSKextLogBasicLevel | kOSKextLogGeneralFlag | kOSKextLogLoadFlag,
586                "%s loaded successfully (or already loaded).",
587                scratchCString);
588        }
589    }
590
591    count = CFArrayGetCount(toolArgs->kextURLs);
592    for (index = 0; index < count; index++) {
593        CFURLRef      kextURL        = CFArrayGetValueAtIndex(
594            toolArgs->kextURLs,
595            index);
596        if (!CFURLGetFileSystemRepresentation(kextURL, /* resolveToBase */ true,
597            (UInt8 *)scratchCString, sizeof(scratchCString))) {
598
599            strlcpy(scratchCString, "unknown", sizeof(scratchCString));
600        }
601
602        OSKextLog(/* kext */ NULL,
603            kOSKextLogBasicLevel | kOSKextLogGeneralFlag |
604            kOSKextLogLoadFlag | kOSKextLogIPCFlag,
605            "Loading %s.",
606            scratchCString);
607
608        OSKextRef theKext = NULL;  // do not release
609
610       /* Use OSKextGetKextWithURL() to avoid double open error messages,
611        * because we already tried to open all kexts above.
612        * That means we don't log here if we don't find the kext.
613        */
614        loadResult = kOSKextReturnNotFound;
615        theKext = OSKextGetKextWithURL(kextURL);
616        if (theKext) {
617            /* The codepath from OSKextLoadWithOptions will do any error logging
618             * and cleanup needed.
619             */
620#if !TARGET_OS_EMBEDDED
621            OSStatus        sigResult = 0;
622            CFStringRef     myBundleID = NULL;         // do not release
623
624            myBundleID = OSKextGetIdentifier(theKext);
625
626            // temp change for 18367703
627            if (readOnly && myBundleID &&
628                (CFStringCompare(myBundleID, CFSTR("com.apple.nke.asp-tcp"), 0) == kCFCompareEqualTo
629                 ||
630                 CFStringCompare(myBundleID, CFSTR("com.apple.filesystems.afpfs"), 0) == kCFCompareEqualTo)) {
631                    OSKextLogCFString(NULL,
632                                      kOSKextLogErrorLevel | kOSKextLogLoadFlag,
633                                      CFSTR("Netboot, loading '%@'"),
634                                      myBundleID);
635            }
636            else {
637                sigResult = checkKextSignature(theKext, true, earlyBoot);
638                if ( sigResult != 0 ) {
639                    if ( isInvalidSignatureAllowed() ) {
640                        OSKextLogCFString(NULL,
641                                          kOSKextLogErrorLevel | kOSKextLogLoadFlag,
642                                          CFSTR("kext-dev-mode allowing invalid signature %ld 0x%02lX for kext '%s'"),
643                                          (long)sigResult, (long)sigResult,
644                                          scratchCString);
645                        sigResult = 0;
646                    }
647                    else {
648                        OSKextLogCFString(NULL,
649                                          kOSKextLogErrorLevel |
650                                          kOSKextLogLoadFlag | kOSKextLogIPCFlag,
651                                          CFSTR("ERROR: invalid signature for '%s', will not load"),
652                                          scratchCString);
653                        loadResult = sigResult;
654                    }
655                }
656            }
657
658
659            if (sigResult == 0) {
660                loadResult = OSKextLoadWithOptions(theKext,
661                                                   kOSKextExcludeNone,
662                                                   kOSKextExcludeNone,
663                                                   NULL,
664                                                   false);
665            }
666#else
667            loadResult = OSKextLoadWithOptions(theKext,
668                                               kOSKextExcludeNone,
669                                               kOSKextExcludeNone,
670                                               NULL,
671                                               false);
672#endif // not TARGET_OS_EMBEDDED
673        }
674        if (loadResult != kOSReturnSuccess) {
675            OSKextLog(/* kext */ NULL,
676                kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
677                "%s failed to load - %s.",
678                scratchCString, safe_mach_error_string(loadResult));
679            if (result == EX_OK) {
680                result = exitStatusForOSReturn(loadResult);
681                // keep trying other kexts though
682            }
683        } else {
684            OSKextLog(/* kext */ NULL,
685                kOSKextLogBasicLevel | kOSKextLogGeneralFlag | kOSKextLogLoadFlag,
686                "%s loaded successfully (or already loaded).",
687                scratchCString);
688        }
689    }
690
691finish:
692    return result;
693}
694
695/*******************************************************************************
696*******************************************************************************/
697ExitStatus exitStatusForOSReturn(OSReturn osReturn)
698{
699    ExitStatus result = EX_OSERR;
700
701    switch (osReturn) {
702    case kOSKextReturnNotPrivileged:
703        result = EX_NOPERM;
704        break;
705    default:
706        result = EX_OSERR;
707        break;
708    }
709    return result;
710}
711
712/*******************************************************************************
713* usage()
714*******************************************************************************/
715void usage(UsageLevel usageLevel)
716{
717    fprintf(stderr, "usage: %s [options] [--] [kext] ...\n"
718      "\n", progname);
719
720    if (usageLevel == kUsageLevelBrief) {
721        fprintf(stderr, "use %s -%s for an explanation of each option\n",
722            progname, kOptNameHelp);
723        return;
724    }
725
726    fprintf(stderr, "kext: a kext bundle to load or examine\n");
727    fprintf(stderr, "\n");
728
729    fprintf(stderr, "-%s <bundle_id> (-%c):\n"
730        "        load/use the kext whose CFBundleIdentifier is <bundle_id>\n",
731        kOptNameBundleIdentifier, kOptBundleIdentifier);
732    fprintf(stderr, "-%s <kext> (-%c):\n"
733        "        consider <kext> as a candidate dependency\n",
734        kOptNameDependency, kOptDependency);
735    fprintf(stderr, "-%s <directory> (-%c):\n"
736        "        look in <directory> for kexts\n",
737        kOptNameRepository, kOptRepository);
738    fprintf(stderr, "\n");
739
740    fprintf(stderr, "-%s (-%c):\n"
741        "        quiet mode: print no informational or error messages\n",
742        kOptNameQuiet, kOptQuiet);
743    fprintf(stderr, "-%s [ 0-6 | 0x<flags> ] (-%c):\n"
744        "        verbose mode; print info about analysis & loading\n",
745        kOptNameVerbose, kOptVerbose);
746    fprintf(stderr, "\n");
747
748    fprintf(stderr, "-%s (-%c): print this message and exit\n",
749        kOptNameHelp, kOptHelp);
750    fprintf(stderr, "\n");
751
752    fprintf(stderr, "--: end of options\n");
753    return;
754}
755