/* * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include "utility.h" #include /******************************************************************************* * createCFString() *******************************************************************************/ CFStringRef createCFString(char * string) { return CFStringCreateWithCString(kCFAllocatorDefault, string, kCFStringEncodingUTF8); } /******************************************************************************* * check_dir() * * This function makes sure that a given directory exists, and is writeable. *******************************************************************************/ Boolean check_dir(const char * dirname, int writeable, int print_err) { int result = true; // assume success struct stat stat_buf; if (stat(dirname, &stat_buf) != 0) { if (print_err) { perror(dirname); } result = false; goto finish; } if ( !(stat_buf.st_mode & S_IFDIR) ) { // XXX This could be called on a kext, message should say such if (print_err) { qerror("%s is not a directory\n", dirname); } result = false; goto finish; } if (writeable) { if (access(dirname, W_OK) != 0) { if (print_err) { qerror("%s is not writeable\n", dirname); } result = false; goto finish; } } finish: return result; } /******************************************************************************* * qerror() * * Quick wrapper over printing that checks verbose level. Does not append a * newline like error_log() does. *******************************************************************************/ void qerror(const char * format, ...) { va_list ap; if (g_verbose_level <= kKXKextManagerLogLevelSilent) return; va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap); fflush(stderr); return; } /******************************************************************************* * basic_log() * * Print a log message prefixed with the name of the program. *******************************************************************************/ void basic_log(const char * format, ...) { va_list ap; fprintf(stdout, "%s: ", progname); va_start(ap, format); vfprintf(stdout, format, ap); va_end(ap); fprintf(stdout, "\n"); fflush(stdout); return; } /******************************************************************************* * verbose_log() * * Print a log message prefixed with the name of the program. *******************************************************************************/ void verbose_log(const char * format, ...) { va_list ap; if (g_verbose_level < kKXKextManagerLogLevelDefault) return; fprintf(stdout, "%s: ", progname); va_start(ap, format); vfprintf(stdout, format, ap); va_end(ap); fprintf(stdout, "\n"); fflush(stdout); return; } /******************************************************************************* * error_log() * * Print an error message prefixed with the name of the program. *******************************************************************************/ void error_log(const char * format, ...) { va_list ap; if (g_verbose_level <= kKXKextManagerLogLevelSilent) return; fprintf(stderr, "%s: ", progname); va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap); fprintf(stderr, "\n"); fflush(stderr); return; } /******************************************************************************* * addKextsToManager() * * Add the kexts named in the kextNames array to the given kext manager, and * put their names into the kextNamesToUse. * * Return values: * 1: all kexts added successfully * 0: one or more could not be added * -1: program-fatal error; exit as soon as possible *******************************************************************************/ int addKextsToManager( KXKextManagerRef aManager, CFArrayRef kextNames, CFMutableArrayRef kextNamesToUse, Boolean do_tests) { int result = 1; // assume success KXKextManagerError kxresult = kKXKextManagerErrorNone; CFIndex i, count; OSKextRef theKext = NULL; // don't release CFURLRef kextURL = NULL; // must release /***** * Add each kext named to the manager. */ count = CFArrayGetCount(kextNames); for (i = 0; i < count; i++) { char name_buffer[PATH_MAX]; CFStringRef kextName = (CFStringRef)CFArrayGetValueAtIndex( kextNames, i); if (kextURL) { CFRelease(kextURL); kextURL = NULL; } kextURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, kextName, kCFURLPOSIXPathStyle, true); if (!kextURL) { qerror("memory allocation failure\n"); result = -1; goto finish; } if (!CFStringGetCString(kextName, name_buffer, sizeof(name_buffer) - 1, kCFStringEncodingUTF8)) { qerror("memory allocation or string conversion error\n"); result = -1; goto finish; } kxresult = KXKextManagerAddKextWithURL(aManager, kextURL, true, &theKext); if (kxresult != kKXKextManagerErrorNone) { result = 0; qerror("can't add kernel extension %s (%s)", name_buffer, KXKextManagerErrorStaticCStringForError(kxresult)); qerror(" (run %s on this kext with -t for diagnostic output)\n", progname); #if 0 if (do_tests && theKext && g_verbose_level >= kKXKextManagerLogLevelErrorsOnly) { qerror("kernel extension problems:\n"); KXKextPrintDiagnostics(theKext, stderr); } continue; #endif 0 } if (kextNamesToUse && theKext && (kxresult == kKXKextManagerErrorNone || do_tests)) { CFArrayAppendValue(kextNamesToUse, kextName); } } finish: if (kextURL) CFRelease(kextURL); return result; } /******************************************************************************* * Fork a process after a specified delay, and either wait on it to exit or * leave it to run in the background. * * Returns -2 on fork() failure, -1 on other failure, and depending on wait: * wait:true - exit status of forked program * wait: false - pid of background process *******************************************************************************/ int fork_program(const char * argv0, char * const argv[], int delay, Boolean wait) { int result = -2; int status; pid_t pid; switch (pid = fork()) { case -1: // error goto finish; break; case 0: // child if (!wait) { // child forks for async/no zombies result = daemon(0, 0); if (result == -1) { goto finish; } // XX does this policy survive the exec below? setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_THROTTLE); } if (delay) { sleep(delay); } execv(argv0, argv); // if execv returns, we have an error but no clear way to log it exit(1); default: // parent waitpid(pid, &status, 0); status = WEXITSTATUS(status); if (wait) { result = status; } else if (status != 0) { result = -1; } else { result = pid; } break; } finish: return result; }