/* * 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_file() * * This function makes sure that a given file exists, is a regular file, and * is readable. *******************************************************************************/ Boolean check_file(const char * filename) { Boolean result = true; // assume success struct stat stat_buf; if (stat(filename, &stat_buf) != 0) { perror(filename); result = false; goto finish; } if ( !(stat_buf.st_mode & S_IFREG) ) { qerror("%s is not a regular file\n", filename); result = false; goto finish; } if (access(filename, R_OK) != 0) { qerror("%s is not readable\n", filename); result = false; goto finish; } finish: return result; } /******************************************************************************* * 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; } /******************************************************************************* * 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; } /******************************************************************************* * user_approve() * * Ask the user a question and wait for a yes/no answer. *******************************************************************************/ int user_approve(int default_answer, const char * format, ...) { int result = 1; va_list ap; char fake_buffer[2]; int output_length; char * output_string; char * prompt_string = NULL; int c, x; va_start(ap, format); output_length = vsnprintf(fake_buffer, 1, format, ap); va_end(ap); output_string = (char *)malloc(output_length + 1); if (!output_string) { qerror("memory allocation failure\n"); result = -1; goto finish; } va_start(ap, format); vsprintf(output_string, format, ap); va_end(ap); prompt_string = default_answer ? " [Y/n]" : " [y/N]"; while ( 1 ) { fprintf(stdout, "%s%s%s", output_string, prompt_string, "? "); fflush(stdout); c = fgetc(stdin); if (c == EOF) { result = -1; goto finish; } /* Make sure we get a newline. */ if ( c != '\n' ) { do { x = fgetc(stdin); } while (x != '\n' && x != EOF); if (x == EOF) { result = -1; goto finish; } } if (c == '\n') { result = default_answer ? 1 : 0; goto finish; } else if (tolower(c) == 'y') { result = 1; goto finish; } else if (tolower(c) == 'n') { result = 0; goto finish; } } finish: if (output_string) free(output_string); return result; } /******************************************************************************* * user_input() * * Ask the user for input. *******************************************************************************/ const char * user_input(const char * format, ...) { char * result = NULL; // return value va_list ap; char fake_buffer[2]; int output_length; char * output_string = NULL; unsigned index; size_t size = 80; // more than enough to input a hex address int c; result = (char *)malloc(size); if (!result) { goto finish; } index = 0; va_start(ap, format); output_length = vsnprintf(fake_buffer, 1, format, ap); va_end(ap); output_string = (char *)malloc(output_length + 1); if (!output_string) { qerror("memory allocation failure\n"); result = NULL; goto finish; } va_start(ap, format); vsprintf(output_string, format, ap); va_end(ap); fprintf(stdout, "%s ", output_string); fflush(stdout); c = fgetc(stdin); while (c != '\n' && c != EOF) { if (index >= (size - 1)) { qerror("input line too long\n"); if (result) free(result); result = NULL; goto finish; } result[index++] = (char)c; c = fgetc(stdin); } result[index] = '\0'; if (c == EOF) { if (result) free(result); result = NULL; goto finish; } finish: if (output_string) free(output_string); return result; } /******************************************************************************* * 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; KXKextRef 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[MAXPATHLEN]; 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; }