/** * ConsoleMessage.c - ConsoleMessage main * Wilfredo Sanchez | wsanchez@opensource.apple.com * Kevin Van Vechten | kevinvv@uclink4.berkeley.edu * $Apple$ ** * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights * Reserved. 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 1.1 (the "License"). You may not use this file * except in compliance with the License. Please obtain a copy of the * License at http://www.apple.com/publicsource 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 OR NON- INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ ** * The ConsoleMessage utility sends an IPC message to SystemStarter * containing the message specified on the command line. SystemStarter * will perform the localization. The message is also printed to * the system log. **/ #include #include #include #include #include #include #include #include #include "SystemStarterIPC.h" static CFDataRef sendIPCMessage(CFStringRef aPortName, CFDataRef aData, CFStringRef aRunLoopMode); static void usage() __attribute__((__noreturn__)); static void usage() { /* char* aProgram = **_NSGetArgv(); */ fprintf(stderr, "usage:\n" "\tConsoleMessage [-v] \n" "\tConsoleMessage [-v] -S\n" "\tConsoleMessage [-v] -F\n" "\tConsoleMessage [-v] -s \n" "\tConsoleMessage [-v] -f \n" "\tConsoleMessage [-v] -q \n" "\tConsoleMessage [-v] -b \n" "\tConsoleMessage [-v] -u\n" "\noptions:\n" "\t-v: verbose (prints errors to stdout)\n" "\t-S: mark all services as successful\n" "\t-F: mark all services as failed\n" "\t-s: mark a specific service as successful\n" "\t-f: mark a specific service as failed\n" "\t-q: query a configuration setting\n" "\t-b: (ignored)\n" "\t-u: (ignored)\n"); exit(1); } enum { kActionConsoleMessage, kActionSuccess, kActionFailure, kActionQuery, }; int main(int argc, char *argv[]) { int anExitCode = 0; int aVerboseFlag = 0; int anAction = kActionConsoleMessage; char *aProgram = argv[0]; char *anArgCStr = NULL; char c; pid_t w4lw_pid = 0; FILE *w4lw_f; /** * Handle command line. **/ while ((c = getopt(argc, argv, "?vSFs:f:q:b:u")) != -1) { switch (c) { case '?': usage(); break; case 'v': aVerboseFlag = 1; break; case 'S': anAction = kActionSuccess; anArgCStr = NULL; break; case 'F': anAction = kActionFailure; anArgCStr = NULL; break; case 's': anAction = kActionSuccess; anArgCStr = optarg; break; case 'f': anAction = kActionFailure; anArgCStr = optarg; break; case 'q': anAction = kActionQuery; anArgCStr = optarg; break; case 'b': exit(EXIT_SUCCESS); break; case 'u': w4lw_f = fopen("/var/run/waiting4loginwindow.pid", "r"); if (w4lw_f) { fscanf(w4lw_f, "%d\n", &w4lw_pid); if (w4lw_pid) kill(w4lw_pid, SIGTERM); } exit(EXIT_SUCCESS); break; default: fprintf(stderr, "ignoring unknown option '-%c'\n", c); break; } } argc -= optind; argv += optind; if ((anAction == kActionConsoleMessage && argc != 1) || (anAction == kActionSuccess && argc != 0) || (anAction == kActionFailure && argc != 0) || (anAction == kActionQuery && argc != 0)) { usage(); } if (getuid() != 0) { fprintf(stderr, "you must be root to run %s\n", aProgram); exit(1); } else { CFMutableDictionaryRef anIPCMessage = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (anIPCMessage) { CFStringRef anArg = NULL; CFIndex aPID = getppid(); CFNumberRef aPIDNumber = CFNumberCreate(NULL, kCFNumberCFIndexType, &aPID); /* * Parent process id is the process id of the startup * item that called ConsoleMessage. */ CFDictionarySetValue(anIPCMessage, kIPCProcessIDKey, aPIDNumber); CFRelease(aPIDNumber); if (anArgCStr) { anArg = CFStringCreateWithCString(NULL, anArgCStr, kCFStringEncodingUTF8); } if (anAction == kActionSuccess || anAction == kActionFailure) { CFBooleanRef aStatus = (anAction == kActionSuccess) ? kCFBooleanTrue : kCFBooleanFalse; CFDictionarySetValue(anIPCMessage, kIPCMessageKey, kIPCStatusMessage); CFDictionarySetValue(anIPCMessage, kIPCStatusKey, aStatus); if (anArg) CFDictionarySetValue(anIPCMessage, kIPCServiceNameKey, anArg); } else if (anAction == kActionQuery && anArg) { CFDictionarySetValue(anIPCMessage, kIPCMessageKey, kIPCQueryMessage); CFDictionarySetValue(anIPCMessage, kIPCConfigSettingKey, anArg); } else if (anAction == kActionConsoleMessage) { char *aConsoleMessageCStr = argv[0]; CFStringRef aConsoleMessage = CFStringCreateWithCString(NULL, aConsoleMessageCStr, kCFStringEncodingUTF8); syslog(LOG_INFO, "%s", aConsoleMessageCStr); CFDictionarySetValue(anIPCMessage, kIPCMessageKey, kIPCConsoleMessage); CFDictionarySetValue(anIPCMessage, kIPCConsoleMessageKey, aConsoleMessage); CFRelease(aConsoleMessage); } if (anArg) CFRelease(anArg); { CFDataRef aData = CFPropertyListCreateXMLData(NULL, anIPCMessage); if (aData) { CFDataRef aResultData = sendIPCMessage(CFSTR(kSystemStarterMessagePort), aData, kCFRunLoopDefaultMode); /* aResultData should be ASCIZ */ if (aResultData) { fprintf(stdout, "%s", CFDataGetBytePtr(aResultData)); CFRelease(aResultData); } else { char *aConsoleMessageCStr = argv[0]; fprintf(stdout, "%s\n", aConsoleMessageCStr); if (aVerboseFlag) fprintf(stderr, "%s could not connect to SystemStarter.\n", aProgram); anExitCode = 0; } CFRelease(aData); } else { if (aVerboseFlag) fprintf(stderr, "%s: not enough memory to create IPC message.\n", aProgram); anExitCode = 1; } } } else { if (aVerboseFlag) fprintf(stderr, "%s: not enough memory to create IPC message.\n", aProgram); anExitCode = 1; } } exit(anExitCode); } static void dummyCallback(void) { } static void replyCallback(CFMachPortRef port __attribute__((unused)), void *aPtr, CFIndex aSize __attribute__((unused)), CFDataRef * aReply) { SystemStarterIPCMessage *aMessage = (SystemStarterIPCMessage *) aPtr; if (aReply != NULL && aMessage->aProtocol == kIPCProtocolVersion && aMessage->aByteLength >= 0) { *aReply = CFDataCreate(NULL, (UInt8 *) aMessage + aMessage->aByteLength, aMessage->aByteLength); } else if (aReply != NULL) { *aReply = NULL; } } static CFDataRef sendIPCMessage(CFStringRef aPortName, CFDataRef aData, CFStringRef aRunLoopMode) { SystemStarterIPCMessage *aMessage = NULL; CFRunLoopSourceRef aSource = NULL; CFMachPortRef aMachPort = NULL, aReplyPort = NULL; CFMachPortContext aContext; kern_return_t aKernReturn = KERN_FAILURE; mach_port_t aBootstrapPort, aNativePort; char *aPortNameUTF8String; CFDataRef aReply = NULL; SInt32 aStrLen; aContext.version = 0; aContext.info = (void *) NULL; aContext.retain = 0; aContext.release = 0; aContext.copyDescription = 0; /* Look up the remote port by name */ aStrLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(aPortName) + 1, kCFStringEncodingUTF8); aPortNameUTF8String = malloc(aStrLen); if (aPortNameUTF8String) { CFStringGetCString(aPortName, aPortNameUTF8String, aStrLen, kCFStringEncodingUTF8); task_get_bootstrap_port(mach_task_self(), &aBootstrapPort); aKernReturn = bootstrap_look_up(aBootstrapPort, aPortNameUTF8String, &aNativePort); aMachPort = (KERN_SUCCESS == aKernReturn) ? CFMachPortCreateWithPort(NULL, aNativePort, (CFMachPortCallBack) dummyCallback, &aContext, NULL) : NULL; free(aPortNameUTF8String); } /* Create a reply port and associated run loop source */ aContext.info = &aReply; aReplyPort = CFMachPortCreate(NULL, (CFMachPortCallBack) replyCallback, &aContext, NULL); if (aReplyPort) { aSource = CFMachPortCreateRunLoopSource(NULL, aReplyPort, 0); if (aSource) { CFRunLoopAddSource(CFRunLoopGetCurrent(), aSource, aRunLoopMode); } } /* Allocate a buffer for the message */ if (aData && aMachPort && aReplyPort) { SInt32 aSize = (sizeof(SystemStarterIPCMessage) + (CFDataGetLength(aData) + 3)) & ~0x3; aMessage = (SystemStarterIPCMessage *) malloc(aSize); if (aMessage) { aMessage->aHeader.msgh_id = 1; aMessage->aHeader.msgh_size = aSize; aMessage->aHeader.msgh_remote_port = CFMachPortGetPort(aMachPort); aMessage->aHeader.msgh_local_port = CFMachPortGetPort(aReplyPort); aMessage->aHeader.msgh_reserved = 0; aMessage->aHeader.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE); aMessage->aBody.msgh_descriptor_count = 0; aMessage->aProtocol = kIPCProtocolVersion; aMessage->aByteLength = CFDataGetLength(aData); memmove((uint8_t *) aMessage + sizeof(SystemStarterIPCMessage), CFDataGetBytePtr(aData), CFDataGetLength(aData)); } } /* Wait up to 1 second to send the message */ if (aMessage) { aKernReturn = mach_msg((mach_msg_header_t *) aMessage, MACH_SEND_MSG | MACH_SEND_TIMEOUT, aMessage->aHeader.msgh_size, 0, MACH_PORT_NULL, 1000.0, MACH_PORT_NULL); free(aMessage); } /* Wait up to 30 seconds for the reply */ if (aSource && aKernReturn == MACH_MSG_SUCCESS) { CFRetain(aReplyPort); CFRunLoopRunInMode(aRunLoopMode, 30.0, true); /* * aReplyPort's replyCallback will set the local aReply * variable */ CFRelease(aReplyPort); } if (aMachPort) CFRelease(aMachPort); if (aReplyPort) CFRelease(aReplyPort); if (aSource) { CFRunLoopRemoveSource(CFRunLoopGetCurrent(), aSource, aRunLoopMode); CFRelease(aSource); } return aReply; }