1/*
2 * Copyright (c) 2008 Apple Computer, 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 <TargetConditionals.h>
25#if !TARGET_OS_EMBEDDED
26    #include <bless.h>
27#endif  // !TARGET_OS_EMBEDDED
28
29#include <libc.h>
30#include <sysexits.h>
31#include <asl.h>
32#include <syslog.h>
33#include <sys/resource.h>
34#include <IOKit/kext/OSKext.h>
35#include <IOKit/kext/OSKextPrivate.h>
36
37#include "kext_tools_util.h"
38
39
40#if PRAGMA_MARK
41#pragma mark Basic Utility
42#endif /* PRAGMA_MARK */
43/*********************************************************************
44*********************************************************************/
45char * createUTF8CStringForCFString(CFStringRef aString)
46{
47    char     * result = NULL;
48    CFIndex    bufferLength = 0;
49
50    if (!aString) {
51        goto finish;
52    }
53
54    bufferLength = sizeof('\0') +
55        CFStringGetMaximumSizeForEncoding(CFStringGetLength(aString),
56        kCFStringEncodingUTF8);
57
58    result = (char *)malloc(bufferLength * sizeof(char));
59    if (!result) {
60        goto finish;
61    }
62    if (!CFStringGetCString(aString, result, bufferLength,
63        kCFStringEncodingUTF8)) {
64
65        SAFE_FREE_NULL(result);
66        goto finish;
67    }
68
69finish:
70    return result;
71}
72
73/*******************************************************************************
74* createCFMutableArray()
75*******************************************************************************/
76Boolean createCFMutableArray(CFMutableArrayRef * array,
77    const CFArrayCallBacks * callbacks)
78{
79    Boolean result = true;
80
81    *array = CFArrayCreateMutable(kCFAllocatorDefault, 0,
82        callbacks);
83    if (!*array) {
84        result = false;
85    }
86    return result;
87}
88
89/*******************************************************************************
90* createCFMutableDictionary()
91*******************************************************************************/
92Boolean createCFMutableDictionary(CFMutableDictionaryRef * dict)
93{
94    Boolean result = true;
95
96    *dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
97        &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
98    if (!*dict) {
99        result = false;
100    }
101    return result;
102}
103
104/*******************************************************************************
105* createCFMutableSet()
106*******************************************************************************/
107Boolean createCFMutableSet(CFMutableSetRef * setOut,
108    const CFSetCallBacks * callbacks)
109{
110    Boolean result = true;
111
112    *setOut = CFSetCreateMutable(kCFAllocatorDefault, 0,
113        callbacks);
114    if (!*setOut) {
115        result = false;
116    }
117    return result;
118}
119
120/*******************************************************************************
121*******************************************************************************/
122void addToArrayIfAbsent(CFMutableArrayRef array, const void * value)
123{
124    if (kCFNotFound == CFArrayGetFirstIndexOfValue(array, RANGE_ALL(array),
125        value)) {
126
127        CFArrayAppendValue(array, value);
128    }
129    return;
130}
131
132/*******************************************************************************
133 * createCFDataFromFile()
134 *******************************************************************************/
135Boolean createCFDataFromFile(CFDataRef  *dataRefOut,
136                             const char *filePath)
137{
138    int             fd = -1;
139    Boolean         result = false;
140    struct stat     statBuf;
141    void            *buffer;
142    CFIndex         length;
143
144    *dataRefOut = NULL;
145    fd = open(filePath, O_RDONLY, 0);
146    if (fd < 0) {
147        goto finish;
148    }
149    if (fstat(fd, &statBuf) != 0) {
150        goto finish;
151    }
152    if ((statBuf.st_mode & S_IFMT) != S_IFREG) {
153        goto finish;
154    }
155    if (statBuf.st_size == 0) {
156        goto finish;
157    }
158
159    // fill buffer used for CFData passed to caller
160    length = (CFIndex) statBuf.st_size;
161    buffer = CFAllocatorAllocate(kCFAllocatorDefault, length, 0);
162    if (read(fd, buffer, length) < 0) {
163        goto finish;
164    }
165
166    *dataRefOut = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
167                                (const UInt8 *)buffer,
168                                length,
169                                kCFAllocatorDefault);
170    if (*dataRefOut == NULL) {
171        CFAllocatorDeallocate(kCFAllocatorDefault, buffer);
172        goto finish;
173    }
174    result = true;
175finish:
176    if (fd != -1) {
177        close(fd);
178    }
179    if (result == false) {
180        OSKextLog(/* kext */ NULL,
181                  kOSKextLogErrorLevel,
182                  "%s: failed for '%s'", __func__, filePath);
183    }
184    return result;
185}
186
187
188/*******************************************************************************
189 *******************************************************************************/
190ExitStatus writeToFile(
191                       int           fileDescriptor,
192                       const UInt8 * data,
193                       CFIndex       length)
194{
195    ExitStatus result = EX_OSERR;
196    ssize_t bytesWritten = 0;
197    ssize_t totalBytesWritten = 0;
198
199    while (totalBytesWritten < length) {
200        bytesWritten = write(fileDescriptor, data + totalBytesWritten,
201                             length - totalBytesWritten);
202        if (bytesWritten < 0) {
203            OSKextLog(/* kext */ NULL,
204                      kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
205                      "Write failed - %s", strerror(errno));
206            goto finish;
207        }
208        totalBytesWritten += bytesWritten;
209    }
210
211    result = EX_OK;
212finish:
213    return result;
214}
215
216#if !TARGET_OS_EMBEDDED
217/******************************************************************************
218 ******************************************************************************/
219
220void postNoteAboutKexts( CFStringRef theNotificationCenterName,
221                         CFMutableDictionaryRef theDict )
222{
223    CFNotificationCenterRef     myCenter    = NULL;
224
225    if (theDict == NULL || theNotificationCenterName == NULL)
226        return;
227
228    myCenter = CFNotificationCenterGetDistributedCenter();
229    CFRetain(theDict);
230
231    CFNotificationCenterPostNotificationWithOptions(
232        myCenter,
233        theNotificationCenterName,
234        NULL,
235        theDict,
236        kCFNotificationDeliverImmediately | kCFNotificationPostToAllSessions );
237
238    SAFE_RELEASE(theDict);
239
240    return;
241}
242
243/******************************************************************************
244 * postNoteAboutKextLoadsMT will use CFNotificationCenter to post a notification.
245 * The notification center is named by theNotificationCenterName.  This routine
246 * is used to notify kextd about a kext that is getting loaded for message
247 * tracing.
248 ******************************************************************************/
249
250void postNoteAboutKextLoadsMT(CFStringRef theNotificationCenterName,
251                              CFMutableArrayRef theKextPathArray)
252{
253    CFMutableDictionaryRef      myInfoDict  = NULL; // must release
254    CFNotificationCenterRef     myCenter    = NULL;
255
256    if (theKextPathArray == NULL || theNotificationCenterName == NULL)
257        return;
258
259    myCenter = CFNotificationCenterGetDistributedCenter();
260    myInfoDict = CFDictionaryCreateMutable(
261                                           kCFAllocatorDefault, 0,
262                                           &kCFCopyStringDictionaryKeyCallBacks,
263                                           &kCFTypeDictionaryValueCallBacks);
264
265    if (myInfoDict && myCenter) {
266        CFDictionaryAddValue(myInfoDict,
267                             CFSTR("KextArrayKey"),
268                             theKextPathArray);
269
270        CFNotificationCenterPostNotificationWithOptions(
271                                                        myCenter,
272                                                        theNotificationCenterName,
273                                                        NULL,
274                                                        myInfoDict,
275                                                        kCFNotificationDeliverImmediately |
276                                                        kCFNotificationPostToAllSessions );
277    }
278
279    SAFE_RELEASE(myInfoDict);
280
281    return;
282}
283
284/*******************************************************************************
285 ******************************************************************************/
286void addKextToAlertDict( CFMutableDictionaryRef *theDictPtr, OSKextRef theKext )
287{
288    CFStringRef         myBundleID;                 // do NOT release
289    CFStringRef         myBundleVersion;            // do NOT release
290    CFMutableArrayRef   myKextArray;                // do NOT release
291    CFURLRef            myKextURL = NULL;           // must release
292    CFStringRef         myKextPath = NULL;          // must release
293    CFMutableDictionaryRef  myKextInfoDict = NULL;  // must release
294    CFMutableDictionaryRef  myAlertInfoDict = NULL; // do NOT release
295    CFIndex                myCount, i;
296
297    if ( theDictPtr == NULL || theKext == NULL ) {
298        return;
299    }
300
301    myAlertInfoDict = *theDictPtr;
302    if (myAlertInfoDict == NULL) {
303        /* caller wants us to create Alert Info Dictionary */
304        myAlertInfoDict = CFDictionaryCreateMutable(
305                                        kCFAllocatorDefault, 0,
306                                        &kCFCopyStringDictionaryKeyCallBacks,
307                                        &kCFTypeDictionaryValueCallBacks );
308        if (myAlertInfoDict == NULL) {
309            return;
310        }
311        *theDictPtr = myAlertInfoDict;
312    }
313
314    myBundleID = OSKextGetIdentifier(theKext);
315    if ( myBundleID == NULL ) {
316        goto finish;
317    }
318
319    /* We never alert about Apple Kexts */
320    if ( CFStringHasPrefix(myBundleID, __kOSKextApplePrefix) == true) {
321        goto finish;
322    }
323
324    myBundleVersion = OSKextGetValueForInfoDictionaryKey(theKext,
325                                                         kCFBundleVersionKey);
326    if (myBundleVersion == NULL) {
327        goto finish;
328    }
329
330    myKextURL = CFURLCopyAbsoluteURL(OSKextGetURL(theKext));
331    if (myKextURL == NULL) {
332        goto finish;
333    }
334
335    myKextPath = CFURLCopyFileSystemPath(myKextURL, kCFURLPOSIXPathStyle);
336    if (myKextPath == NULL) {
337        goto finish;
338    }
339
340    /* add kext info to the Alert Dictionary.
341     * We want BundleID, Version and full path to the kext
342     */
343    myKextArray = (CFMutableArrayRef)
344        CFDictionaryGetValue(myAlertInfoDict, CFSTR("KextInfoArrayKey"));
345    if (myKextArray == NULL) {
346        /* first kext info so create the kext info array */
347        myKextArray = CFArrayCreateMutable(kCFAllocatorDefault,
348                                           0,
349                                           &kCFTypeArrayCallBacks);
350        if (myKextArray == NULL) {
351            goto finish;
352        }
353        CFDictionarySetValue(myAlertInfoDict,
354                             CFSTR("KextInfoArrayKey"),
355                             myKextArray);
356    }
357
358    /* check for dup of this kext */
359    myCount = CFArrayGetCount(myKextArray);
360    if (myCount > 0) {
361        for (i = 0; i < myCount; i++) {
362            CFMutableDictionaryRef myDict;
363            myDict = (CFMutableDictionaryRef)
364            CFArrayGetValueAtIndex(myKextArray, i);
365            if (myDict == NULL)   continue;
366
367            if ( !CFDictionaryContainsValue(myDict, myBundleID) ) {
368                continue;
369            }
370            if ( !CFDictionaryContainsValue(myDict,
371                                            myBundleVersion) ) {
372                continue;
373            }
374            /* already have this one so bail */
375            goto finish;
376        }
377    }
378
379    /* new kext info to add */
380    myKextInfoDict = CFDictionaryCreateMutable(
381                                        kCFAllocatorDefault, 0,
382                                        &kCFCopyStringDictionaryKeyCallBacks,
383                                        &kCFTypeDictionaryValueCallBacks);
384    if (myKextInfoDict == NULL) {
385        goto finish;
386    }
387    CFDictionaryAddValue(myKextInfoDict,
388                         kCFBundleIdentifierKey,
389                         myBundleID);
390    CFDictionaryAddValue(myKextInfoDict,
391                         kCFBundleVersionKey,
392                         myBundleVersion);
393    CFDictionaryAddValue(myKextInfoDict,
394                         CFSTR("KextPathKey"),
395                         myKextPath);
396
397    CFArrayAppendValue(myKextArray,
398                       myKextInfoDict);
399
400finish:
401    SAFE_RELEASE(myKextURL);
402    SAFE_RELEASE(myKextPath);
403    SAFE_RELEASE(myKextInfoDict);
404
405    return;
406}
407
408#endif  // !TARGET_OS_EMBEDDED
409
410#if PRAGMA_MARK
411#pragma mark Path & File
412#endif /* PRAGMA_MARK */
413/*******************************************************************************
414*******************************************************************************/
415ExitStatus checkPath(
416    const char * path,
417    const char * suffix,  // w/o the dot
418    Boolean      directoryRequired,
419    Boolean      writableRequired)
420{
421    Boolean result  = EX_USAGE;
422    Boolean nameBad = FALSE;
423    struct  stat statBuffer;
424
425    if (!path) {
426        OSKextLog(/* kext */ NULL,
427            kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
428            "Internal error - %s - NULL path.",
429                __FUNCTION__);
430        result = EX_SOFTWARE;
431        goto finish;
432    }
433
434    result = EX_USAGE;
435    if (suffix) {
436        size_t pathLength   = strlen(path);
437        size_t suffixLength = strlen(suffix);
438        size_t suffixIndex = 0;
439        size_t periodIndex = 0;
440
441        nameBad = TRUE;
442        if (!pathLength || !suffixLength) {
443            OSKextLog(/* kext */ NULL,
444                kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
445                "Internal error - %s - empty string.",
446                __FUNCTION__);
447            result = EX_SOFTWARE;
448            goto finish;
449        }
450
451       /* Peel off any trailing '/' characters (silly shell completion),
452        * then advance the length back to point to the character past
453        * the real end (which will be a slash or '\0').
454        */
455        while (pathLength-- && path[pathLength] == '/') {
456            /* just scanning for last non-slash */
457            if (!pathLength) {
458                goto finish;
459            }
460        }
461        pathLength++;
462
463        if (suffixLength >= pathLength) {
464            goto finish;
465        }
466        suffixIndex = pathLength - suffixLength;
467        periodIndex = suffixIndex - 1;
468        if (path[periodIndex] != '.' ||
469            strncmp(path + suffixIndex, suffix, suffixLength)) {
470            goto finish;
471        }
472        nameBad = FALSE;
473    }
474
475    result = EX_NOINPUT;
476    if (0 != stat(path, &statBuffer)) {
477        OSKextLog(/* kext */ NULL,
478            kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
479            "Can't stat %s - %s.", path,
480            strerror(errno));
481        goto finish;
482    }
483
484    if (directoryRequired && ((statBuffer.st_mode & S_IFMT) != S_IFDIR) ) {
485        OSKextLog(/* kext */ NULL,
486            kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
487            "%s is not a directory.",
488            path);
489        goto finish;
490    }
491
492    result = EX_NOPERM;
493    if (writableRequired && access(path, W_OK) == -1) {
494        OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel,
495            "%s is not writable.", path);
496        goto finish;
497    }
498
499    result = EX_OK;
500
501finish:
502    if (nameBad) {
503        OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel,
504            "%s not of type '%s'.", path, suffix);
505    }
506    return result;
507}
508
509/*******************************************************************************
510*******************************************************************************/
511void
512saveFile(const void * vKey, const void * vValue, void * vContext)
513{
514    CFStringRef       key      = (CFStringRef)vKey;
515    CFDataRef         fileData = (CFDataRef)vValue;
516    SaveFileContext * context  = (SaveFileContext *)vContext;
517
518    long              length;
519    int               fd = -1;
520    mode_t            mode = 0666;
521    struct  stat      statBuf;
522    CFURLRef          saveURL = NULL;     // must release
523    Boolean           fileExists = false;
524    char              savePath[PATH_MAX];
525
526    if (context->fatal) {
527        goto finish;
528    }
529
530    saveURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault,
531        context->saveDirURL, key, /* isDirectory */ false);
532    if (!saveURL) {
533        context->fatal = true;
534        goto finish;
535    }
536
537    if (!CFURLGetFileSystemRepresentation(saveURL, /* resolveToBase */ true,
538        (u_char *)savePath, sizeof(savePath))) {
539        context->fatal = true;
540        goto finish;
541    }
542
543    if (!context->overwrite) {
544        fileExists = CFURLResourceIsReachable(saveURL, NULL);
545        if (fileExists) {
546           switch (user_approve(/* ask_all */ TRUE, /* default_answer */ REPLY_YES,
547                "%s exists, overwrite", savePath)) {
548
549                case REPLY_YES:
550                    // go ahead and overwrite.
551                    break;
552                case REPLY_ALL:
553                    // go ahead and overwrite this and all following.
554                    fprintf(stderr,
555                        "Overwriting all symbol files for kexts in dependency graph.\n");
556                    context->overwrite = TRUE;
557                    break;
558                case REPLY_NO:
559                    goto finish;
560                    break;
561                default:
562                    context->fatal = true;
563                    goto finish;
564                    break;
565            }
566        }
567        else {
568            OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel,
569                      "%s missing '%s'", __func__, savePath);
570            context->fatal = true;
571            goto finish;
572        }
573    }
574
575    /* Write data.
576     */
577    length = CFDataGetLength(fileData);
578    if (0 == stat(savePath, &statBuf)) {
579        mode = statBuf.st_mode;
580    }
581    fd = open(savePath, O_WRONLY|O_CREAT|O_TRUNC, mode);
582    if (fd != -1 && length) {
583        ExitStatus result;
584        result = writeToFile(fd, CFDataGetBytePtr(fileData), length);
585        if (result != EX_OK) {
586            OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel,
587                      "%s write failed for '%s'", __func__, savePath);
588        }
589    }
590    else {
591        /* Is this fatal to the whole program? I'd rather soldier on.
592         */
593        OSKextLog(/* kext */ NULL,
594                  kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
595                  "%s Failed to save '%s'", __func__, savePath);
596    }
597
598finish:
599    if (fd != -1) {
600        fsync(fd);
601        close(fd);
602    }
603    SAFE_RELEASE(saveURL);
604    return;
605}
606
607/*******************************************************************************
608*******************************************************************************/
609CFStringRef copyKextPath(OSKextRef aKext)
610{
611    CFStringRef result = NULL;
612    CFURLRef    absURL = NULL;  // must release
613
614    if (!OSKextGetURL(aKext)) {
615        goto finish;
616    }
617
618    absURL = CFURLCopyAbsoluteURL(OSKextGetURL(aKext));
619    if (!absURL) {
620        goto finish;
621    }
622    result = CFURLCopyFileSystemPath(absURL, kCFURLPOSIXPathStyle);
623finish:
624    SAFE_RELEASE(absURL);
625    return result;
626}
627
628
629/*******************************************************************************
630 * Returns the access and mod times from the file in the given array of
631 * fileURLs with the latest mod time.
632 * 11860417 - support multiple extensions directories
633 *******************************************************************************/
634ExitStatus
635getLatestTimesFromCFURLArray(
636                             CFArrayRef       dirURLArray,
637                             struct timeval   dirTimeVals[2])
638{
639    ExitStatus      result     = EX_SOFTWARE;
640    int             i;
641    CFURLRef        myURL;
642    struct stat     myStatBuf;
643    struct timeval  myTempModTime;
644    struct timeval  myTempAccessTime;
645
646    if (dirURLArray == NULL) {
647        goto finish;
648    }
649    bzero(dirTimeVals, (sizeof(struct timeval) * 2));
650
651    for (i = 0; i < CFArrayGetCount(dirURLArray); i++) {
652        myURL = (CFURLRef) CFArrayGetValueAtIndex(dirURLArray, i);
653        if (myURL == NULL) {
654            OSKextLog(NULL,
655                      kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
656                      "%s: NO fileURL at index %d!!!! ", __FUNCTION__, i);
657            goto finish;
658        }
659
660        result = statURL(myURL, &myStatBuf);
661        if (result != EX_OK) {
662            goto finish;
663        }
664        TIMESPEC_TO_TIMEVAL(&myTempAccessTime, &myStatBuf.st_atimespec);
665        TIMESPEC_TO_TIMEVAL(&myTempModTime, &myStatBuf.st_mtimespec);
666
667        if (timercmp(&myTempModTime, &dirTimeVals[1], >)) {
668            dirTimeVals[0].tv_sec = myTempAccessTime.tv_sec;
669            dirTimeVals[0].tv_usec = myTempAccessTime.tv_usec;
670            dirTimeVals[1].tv_sec = myTempModTime.tv_sec;
671            dirTimeVals[1].tv_usec = myTempModTime.tv_usec;
672        }
673    }
674
675    result = EX_OK;
676finish:
677    return result;
678}
679
680/*******************************************************************************
681 *******************************************************************************/
682ExitStatus
683getFilePathTimes(
684                 const char        * filePath,
685                 struct timeval      cacheFileTimes[2])
686{
687    struct stat         statBuffer;
688    ExitStatus          result          = EX_SOFTWARE;
689
690    result = statPath(filePath, &statBuffer);
691    if (result != EX_OK) {
692        goto finish;
693    }
694
695    TIMESPEC_TO_TIMEVAL(&cacheFileTimes[0], &statBuffer.st_atimespec);
696    TIMESPEC_TO_TIMEVAL(&cacheFileTimes[1], &statBuffer.st_mtimespec);
697
698    result = EX_OK;
699finish:
700    return result;
701}
702
703
704/*******************************************************************************
705 *******************************************************************************/
706ExitStatus
707statURL(CFURLRef anURL, struct stat * statBuffer)
708{
709    ExitStatus result = EX_OSERR;
710    char path[PATH_MAX];
711
712    if (!CFURLGetFileSystemRepresentation(anURL, /* resolveToBase */ true,
713                                          (UInt8 *)path, sizeof(path)))
714    {
715        OSKextLogStringError(/* kext */ NULL);
716        goto finish;
717    }
718
719    result = statPath(path, statBuffer);
720    if (!result) {
721        goto finish;
722    }
723
724    result = EX_OK;
725finish:
726    return result;
727}
728
729/*******************************************************************************
730 *******************************************************************************/
731ExitStatus
732statPath(const char *path, struct stat *statBuffer)
733{
734    ExitStatus result = EX_OSERR;
735
736    if (stat(path, statBuffer)) {
737        OSKextLog(/* kext */ NULL,
738                  kOSKextLogDebugLevel | kOSKextLogGeneralFlag,
739                  "Can't stat %s - %s.", path, strerror(errno));
740        goto finish;
741    }
742
743    result = EX_OK;
744
745finish:
746    return result;
747}
748
749#if PRAGMA_MARK
750#pragma mark Logging
751#endif /* PRAGMA_MARK */
752/*******************************************************************************
753*******************************************************************************/
754OSKextLogSpec _sLogSpecsForVerboseLevels[] = {
755    kOSKextLogErrorLevel    | kOSKextLogVerboseFlagsMask,   // [0xff1] -v 0
756    kOSKextLogBasicLevel    | kOSKextLogVerboseFlagsMask,   // [0xff3] -v 1
757    kOSKextLogProgressLevel | kOSKextLogVerboseFlagsMask,   // [0xff4] -v 2
758    kOSKextLogStepLevel     | kOSKextLogVerboseFlagsMask,   // [0xff5] -v 3
759    kOSKextLogDetailLevel   | kOSKextLogVerboseFlagsMask,   // [0xff6] -v 4
760    kOSKextLogDebugLevel    | kOSKextLogVerboseFlagsMask,   // [0xff7] -v 5
761    kOSKextLogDebugLevel    | kOSKextLogVerboseFlagsMask |  // [0xfff] -v 6
762        kOSKextLogKextOrGlobalMask
763};
764
765/*******************************************************************************
766* getopt_long_only() doesn't actually handle optional args very well. So, we
767* jump through some hoops here to handle all six possibilities:
768*
769*   cmd line      optarg  argv[optind]
770*   ----------------------------------------------
771*   -v            (null)  (following arg or null)
772*   -v arg        (null)  arg
773*   -v=arg        (null)  -v=arg -- ILLEGAL
774*   -verbose      (null)  (following arg or null)
775*   -verbose arg  (null)  arg
776*   -verbose=arg  arg     (following arg or null)
777*
778* Note that only in the -verbose=arg case does optarg actually get set
779* correctly!
780*
781* If we have not optarg but a following argv[optind], we check it to see if
782* it looks like a legal arg to -v/-verbose; if it matches we increment optind.
783* -v has never allowed the argument to immediately follow (as in -v2), so
784* we still don't handle that.
785*******************************************************************************/
786#define kBadVerboseOptPrefix  "-v="
787
788ExitStatus setLogFilterForOpt(
789    int            argc,
790    char * const * argv,
791    OSKextLogSpec  forceOnFlags)
792{
793    ExitStatus      result       = EX_USAGE;
794    OSKextLogSpec   logFilter    = 0;
795    const char    * localOptarg  = NULL;
796
797   /* Must be a bare -v; just use the extra flags.
798    */
799    if (!optarg && optind >= argc) {
800        logFilter = _sLogSpecsForVerboseLevels[1];
801
802    } else {
803
804        if (optarg) {
805            localOptarg = optarg;
806        } else {
807            localOptarg = argv[optind];
808        }
809
810        if (!strncmp(localOptarg, kBadVerboseOptPrefix,
811            sizeof(kBadVerboseOptPrefix) - 1)) {
812
813            OSKextLog(/* kext */ NULL,
814                kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
815                "%s - syntax error (don't use = with single-letter option args).",
816                localOptarg);
817            goto finish;
818        }
819
820       /* Look for '-v0x####' with no space and advance to the 0x part.
821        */
822        if (localOptarg[0] == '-' && localOptarg[1] == kOptVerbose &&
823            localOptarg[2] == '0' && (localOptarg[3] == 'x' || localOptarg[3] == 'X')) {
824
825            localOptarg += 2;
826        }
827
828       /* Look for a 0x#### style verbose arg.
829        */
830        if (localOptarg[0] == '0' && (localOptarg[1] == 'x' || localOptarg[1] == 'X')) {
831            char          * endptr      = NULL;
832            OSKextLogSpec   parsedFlags = (unsigned)strtoul(localOptarg, &endptr, 16);
833
834            if (endptr[0]) {
835                OSKextLog(/* kext */ NULL,
836                    kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
837                    "Can't parse verbose argument %s.", localOptarg);
838                goto finish;
839            }
840            logFilter = parsedFlags;
841
842            if (!optarg) {
843                optind++;
844            }
845
846       /* Now a 0-6 style verbose arg.
847        */
848        } else if (((localOptarg[0] >= '0') || (localOptarg[0] <= '6')) &&
849            (localOptarg[1] == '\0')) {
850
851            logFilter = _sLogSpecsForVerboseLevels[localOptarg[0] - '0'];
852            if (!optarg) {
853                optind++;
854            }
855
856       /* Must be a -v with command args following; just use the extra flag.
857        */
858        } else {
859            logFilter = _sLogSpecsForVerboseLevels[1];
860        }
861    }
862
863    logFilter = logFilter | forceOnFlags;
864
865    OSKextSetLogFilter(logFilter, /* kernel? */ false);
866    OSKextSetLogFilter(logFilter, /* kernel? */ true);
867
868    result = EX_OK;
869
870finish:
871    return result;
872}
873
874/*******************************************************************************
875*******************************************************************************/
876void beQuiet(void)
877{
878    fclose(stdout);
879    fclose(stderr);
880    close(1);
881    close(2);
882    OSKextSetLogFilter(kOSKextLogSilentFilter, /* kernel? */ false);
883    OSKextSetLogFilter(kOSKextLogSilentFilter, /* kernel? */ true);
884    return;
885}
886
887/*******************************************************************************
888*******************************************************************************/
889FILE * g_log_stream = NULL;
890aslclient gASLClientHandle = NULL;
891aslmsg    gASLMessage      = NULL;  // reused
892// xxx - need to aslclose()
893
894void tool_openlog(const char * name)
895{
896    // xxx - do we want separate name & facility?
897    gASLClientHandle = asl_open(/* ident */ name, /* facility*/ name,
898        /* options */ 0);
899    gASLMessage     = asl_new(ASL_TYPE_MSG);
900    return;
901}
902
903/*******************************************************************************
904* Basic log function. If any log flags are set, log the message
905* to syslog/stderr.  Note: exported as SPI in bootroot.h.
906*******************************************************************************/
907
908void tool_log(
909    OSKextRef       aKext __unused,
910    OSKextLogSpec   msgLogSpec,
911    const char    * format, ...)
912{
913    va_list ap;
914
915    if (gASLClientHandle) {
916        int            aslLevel = ASL_LEVEL_ERR;
917        OSKextLogSpec  kextLogLevel = msgLogSpec & kOSKextLogLevelMask;
918        char           messageLogSpec[16];
919
920        if (kextLogLevel == kOSKextLogErrorLevel) {
921            aslLevel = ASL_LEVEL_ERR;
922        } else if (kextLogLevel == kOSKextLogWarningLevel) {
923            aslLevel = ASL_LEVEL_WARNING;
924        } else if (kextLogLevel == kOSKextLogBasicLevel) {
925            aslLevel = ASL_LEVEL_NOTICE;
926        } else if (kextLogLevel < kOSKextLogDebugLevel) {
927            aslLevel = ASL_LEVEL_INFO;
928        } else {
929            aslLevel = ASL_LEVEL_DEBUG;
930        }
931
932        snprintf(messageLogSpec, sizeof(messageLogSpec), "0x%x", msgLogSpec);
933        asl_set(gASLMessage, "OSKextLogSpec", messageLogSpec);
934
935        va_start(ap, format);
936        asl_vlog(gASLClientHandle, gASLMessage, aslLevel, format, ap);
937        va_end(ap);
938
939    } else {
940        // xxx - change to pick log stream based on log level
941        // xxx - (0 == stdout, all others stderr)
942
943        if (!g_log_stream) {
944            g_log_stream = stderr;
945        }
946
947        va_start(ap, format);
948        vfprintf(g_log_stream, format, ap);
949        va_end(ap);
950
951        fprintf(g_log_stream, "\n");
952        fflush(g_log_stream);
953    }
954
955    return;
956}
957
958/*******************************************************************************
959*******************************************************************************/
960void log_CFError(
961    OSKextRef     aKext __unused,
962    OSKextLogSpec msgLogSpec,
963    CFErrorRef    error)
964{
965    CFStringRef   errorString = NULL;  // must release
966    char        * cstring     = NULL;  // must release
967
968    if (!error) {
969        return;
970    }
971    errorString = CFErrorCopyDescription(error);
972    if (errorString) {
973        cstring = createUTF8CStringForCFString(errorString);
974        OSKextLog(/* kext */ NULL, msgLogSpec,
975            "CFError descripton: %s.", cstring);
976        SAFE_RELEASE_NULL(errorString);
977        SAFE_FREE_NULL(cstring);
978    }
979
980    errorString = CFErrorCopyFailureReason(error);
981    if (errorString) {
982        cstring = createUTF8CStringForCFString(errorString);
983        OSKextLog(/* kext */ NULL, msgLogSpec,
984            "CFError reason: %s.", cstring);
985        SAFE_RELEASE_NULL(errorString);
986        SAFE_FREE_NULL(cstring);
987    }
988
989    return;
990}
991
992#if !TARGET_OS_EMBEDDED
993// log helper for libbless, exported as SPI via bootroot.h
994int32_t
995BRBLLogFunc(void *refcon __unused, int32_t level, const char *string)
996{
997    OSKextLogSpec logSpec = kOSKextLogGeneralFlag;
998    switch (level) {
999    case kBLLogLevelVerbose:
1000        logSpec |= kOSKextLogDebugLevel;
1001        break;
1002    case kBLLogLevelError:
1003        logSpec |= kOSKextLogErrorLevel;
1004        break;
1005    default:
1006        logSpec |= kOSKextLogWarningLevel;
1007    }
1008    OSKextLog(NULL, logSpec, "%s", string);
1009    return 0;
1010}
1011#endif   // !TARGET_OS_EMBEDDED
1012
1013/*******************************************************************************
1014* safe_mach_error_string()
1015*******************************************************************************/
1016const char * safe_mach_error_string(mach_error_t error_code)
1017{
1018    const char * result = mach_error_string(error_code);
1019    if (!result) {
1020        result = "(unknown)";
1021    }
1022    return result;
1023}
1024
1025#if PRAGMA_MARK
1026#pragma mark User Input
1027#endif /* PRAGMA_MARK */
1028/*******************************************************************************
1029* user_approve()
1030*
1031* Ask the user a question and wait for a yes/no answer.
1032*******************************************************************************/
1033int user_approve(Boolean ask_all, int default_answer, const char * format, ...)
1034{
1035    int     result = REPLY_YES;
1036    va_list ap;
1037    char    fake_buffer[2];
1038    int     output_length;
1039    char  * output_string;
1040    int     c, x;
1041
1042    va_start(ap, format);
1043    output_length = vsnprintf(fake_buffer, 1, format, ap);
1044    va_end(ap);
1045
1046    output_string = (char *)malloc(output_length + 1);
1047    if (!output_string) {
1048        result = REPLY_ERROR;
1049        goto finish;
1050    }
1051
1052    va_start(ap, format);
1053    vsnprintf(output_string, output_length + 1, format, ap);
1054    va_end(ap);
1055
1056    while ( 1 ) {
1057        fprintf(stderr, "%s [%s/%s", output_string,
1058            (default_answer == REPLY_YES) ? "Y" : "y",
1059            (default_answer == REPLY_NO)  ? "N" : "n");
1060        if (ask_all) {
1061            fprintf(stderr, "/%s",
1062                (default_answer == REPLY_ALL) ? "A" : "a");
1063        }
1064        fprintf(stderr, "]? ");
1065        fflush(stderr);
1066
1067        c = fgetc(stdin);
1068
1069        if (c == EOF) {
1070            result = REPLY_ERROR;
1071            goto finish;
1072        }
1073
1074       /* Make sure we get a newline.
1075        */
1076        if ( c != '\n' ) {
1077            do {
1078                x = fgetc(stdin);
1079            } while (x != '\n' && x != EOF);
1080
1081            if (x == EOF) {
1082                result = REPLY_ERROR;
1083                goto finish;
1084            }
1085        }
1086
1087        if (c == '\n') {
1088            result = default_answer;
1089            goto finish;
1090        } else if (tolower(c) == 'y') {
1091            result = REPLY_YES;
1092            goto finish;
1093        } else if (tolower(c) == 'n') {
1094            result = REPLY_NO;
1095            goto finish;
1096        } else if (ask_all && tolower(c) == 'a') {
1097            result = REPLY_ALL;
1098            goto finish;
1099        } else {
1100            fprintf(stderr, "Please answer 'y' or 'n'%s.\n",
1101                ask_all ? " or 'a'" : "");
1102        }
1103    }
1104
1105finish:
1106    if (output_string) free(output_string);
1107
1108    return result;
1109}
1110
1111/*******************************************************************************
1112* user_input()
1113*
1114* Ask the user for input.
1115*******************************************************************************/
1116const char * user_input(Boolean * eof, const char * format, ...)
1117{
1118    char * result = NULL;  // return value
1119    va_list ap;
1120    char fake_buffer[2];
1121    int output_length;
1122    char * output_string = NULL;
1123    unsigned index;
1124    size_t size = 80;  // more than enough to input a hex address
1125    int c;
1126
1127    if (eof) {
1128        *eof = false;
1129    }
1130
1131    result = (char *)malloc(size);
1132    if (!result) {
1133        goto finish;
1134    }
1135    index = 0;
1136
1137    va_start(ap, format);
1138    output_length = vsnprintf(fake_buffer, 1, format, ap);
1139    va_end(ap);
1140
1141    output_string = (char *)malloc(output_length + 1);
1142    if (!output_string) {
1143        if (result) free(result);
1144        result = NULL;
1145        goto finish;
1146    }
1147
1148    va_start(ap, format);
1149    vsnprintf(output_string, output_length + 1, format, ap);
1150    va_end(ap);
1151
1152    fprintf(stderr, "%s ", output_string);
1153    fflush(stderr);
1154
1155    c = fgetc(stdin);
1156    while (c != '\n' && c != EOF) {
1157        if (index >= (size - 1)) {
1158            fprintf(stderr, "input line too long\n");
1159            if (result) free(result);
1160            result = NULL;
1161            goto finish;
1162        }
1163        result[index++] = (char)c;
1164        c = fgetc(stdin);
1165    }
1166
1167    result[index] = '\0';
1168
1169    if (c == EOF) {
1170        if (result) free(result);
1171        result = NULL;
1172        if (eof) {
1173            *eof = true;
1174        }
1175        goto finish;
1176    }
1177
1178finish:
1179    if (output_string) free(output_string);
1180
1181    return result;
1182}
1183
1184#if PRAGMA_MARK
1185#pragma mark Caches
1186#endif /* PRAGMA_MARK */
1187/*******************************************************************************
1188*******************************************************************************/
1189Boolean readSystemKextPropertyValues(
1190    CFStringRef        propertyKey,
1191    const NXArchInfo * arch,
1192    Boolean            forceUpdateFlag,
1193    CFArrayRef       * valuesOut)
1194{
1195    Boolean                result                  = false;
1196    CFArrayRef             sysExtensionsFolderURLs = OSKextGetSystemExtensionsFolderURLs();
1197    CFMutableArrayRef      values                  = NULL;  // must release
1198    CFStringRef            cacheBasename           = NULL;  // must release
1199    CFArrayRef             kexts                   = NULL;  // must release
1200    CFMutableDictionaryRef newDict                 = NULL;  // must release
1201    CFStringRef            kextPath                = NULL;  // must release
1202    CFTypeRef              value                   = NULL;  // do not release
1203    CFStringRef            kextVersion             = NULL;  // do not release
1204    CFIndex                count, i;
1205
1206    cacheBasename = CFStringCreateWithFormat(kCFAllocatorDefault,
1207        /* formatOptions */ NULL, CFSTR("%s%@"),
1208        _kKextPropertyValuesCacheBasename,
1209        propertyKey);
1210    if (!cacheBasename) {
1211        OSKextLogMemError();
1212        goto finish;
1213    }
1214
1215    if (OSKextGetUsesCaches() && !forceUpdateFlag) {
1216
1217       /* See if we have an up-to-date cache containing an array, and return
1218        * that if we have one.
1219        */
1220        if (_OSKextReadCache(sysExtensionsFolderURLs, cacheBasename,
1221            arch, _kOSKextCacheFormatCFXML, /* parseXML? */ true,
1222            (CFPropertyListRef *)&values)) {
1223
1224            if (values && CFGetTypeID(values) == CFArrayGetTypeID()) {
1225                result = true;
1226                goto finish;
1227            }
1228        }
1229    }
1230
1231    values = CFArrayCreateMutable(kCFAllocatorDefault, /* capacity */ 0,
1232        &kCFTypeArrayCallBacks);
1233    if (!values) {
1234        OSKextLogMemError();
1235        goto finish;
1236    }
1237
1238    kexts = OSKextCreateKextsFromURLs(kCFAllocatorDefault,
1239    sysExtensionsFolderURLs);
1240
1241    if (!kexts) {
1242        // Create function should log error
1243        goto finish;
1244    }
1245
1246    count = CFArrayGetCount(kexts);
1247
1248    for (i = 0; i < count; i++) {
1249        OSKextRef aKext = (OSKextRef)CFArrayGetValueAtIndex(kexts, i);
1250
1251        SAFE_RELEASE_NULL(newDict);
1252        SAFE_RELEASE_NULL(kextPath);
1253        // do not release kextVersion
1254        kextVersion = NULL;
1255
1256        if ((OSKextGetSimulatedSafeBoot() || OSKextGetActualSafeBoot()) &&
1257            !OSKextIsLoadableInSafeBoot(aKext)) {
1258
1259            continue;
1260        }
1261        //??? if (OSKextGetLoadFailed(aKext)) continue;  -- don't have in OSKext
1262
1263        value = OSKextGetValueForInfoDictionaryKey(aKext, propertyKey);
1264        if (!value) {
1265            continue;
1266        }
1267
1268        newDict = CFDictionaryCreateMutable(
1269            kCFAllocatorDefault, 0,
1270            &kCFTypeDictionaryKeyCallBacks,
1271            &kCFTypeDictionaryValueCallBacks);
1272        if (!newDict) {
1273            goto finish;
1274        }
1275
1276        CFDictionarySetValue(newDict, CFSTR("Data"), value);
1277
1278        CFDictionarySetValue(newDict, CFSTR("CFBundleIdentifier"),
1279            OSKextGetIdentifier(aKext));
1280
1281        kextPath = copyKextPath(aKext);
1282        if (!kextPath) {
1283            goto finish;
1284        }
1285        CFDictionarySetValue(newDict, CFSTR("OSBundlePath"), kextPath);
1286
1287        kextVersion = OSKextGetValueForInfoDictionaryKey(aKext,
1288            CFSTR("CFBundleVersion"));
1289        if (!kextVersion) {
1290            goto finish;
1291        }
1292        CFDictionarySetValue(newDict, CFSTR("CFBundleVersion"),
1293            kextVersion);
1294
1295        CFArrayAppendValue(values, newDict);
1296    }
1297
1298    if (OSKextGetUsesCaches() || forceUpdateFlag) {
1299        _OSKextWriteCache(sysExtensionsFolderURLs, cacheBasename,
1300            arch, _kOSKextCacheFormatCFXML, values);
1301    }
1302
1303    result = true;
1304
1305finish:
1306    if (result && valuesOut && values) {
1307        *valuesOut = (CFArrayRef)CFRetain(values);
1308    }
1309
1310    SAFE_RELEASE(values);
1311    SAFE_RELEASE(cacheBasename);
1312    SAFE_RELEASE(kexts);
1313    SAFE_RELEASE(newDict);
1314    SAFE_RELEASE(kextPath);
1315
1316    return result;
1317}
1318
1319