]> git.saurik.com Git - apple/launchd.git/blob - launchd/src/ConsoleMessage.c
a07f4ab113aa3108ab16bf648aa0ea831361d3c3
[apple/launchd.git] / launchd / src / ConsoleMessage.c
1 /**
2 * ConsoleMessage.c - ConsoleMessage main
3 * Wilfredo Sanchez | wsanchez@opensource.apple.com
4 * Kevin Van Vechten | kevinvv@uclink4.berkeley.edu
5 * $Apple$
6 **
7 * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved.
8 *
9 * @APPLE_LICENSE_HEADER_START@
10 *
11 * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
12 * Reserved. This file contains Original Code and/or Modifications of
13 * Original Code as defined in and that are subject to the Apple Public
14 * Source License Version 1.1 (the "License"). You may not use this file
15 * except in compliance with the License. Please obtain a copy of the
16 * License at http://www.apple.com/publicsource and read it before using
17 * this file.
18 *
19 * The Original Code and all software distributed under the License are
20 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
21 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
22 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the
24 * License for the specific language governing rights and limitations
25 * under the License.
26 *
27 * @APPLE_LICENSE_HEADER_END@
28 **
29 * The ConsoleMessage utility sends an IPC message to SystemStarter
30 * containing the message specified on the command line. SystemStarter
31 * will perform the localization. The message is also printed to
32 * the system log.
33 **/
34
35 #include <unistd.h>
36 #include <crt_externs.h>
37 #include <syslog.h>
38 #include <mach/mach.h>
39 #include <mach/message.h>
40 #include <mach/mach_error.h>
41 #include <servers/bootstrap.h>
42 #include <CoreFoundation/CoreFoundation.h>
43 #include "SystemStarterIPC.h"
44
45 static CFDataRef sendIPCMessage(CFStringRef aPortName, CFDataRef aData, CFStringRef aRunLoopMode);
46
47 static void usage() __attribute__((__noreturn__));
48 static void
49 usage()
50 {
51 /* char* aProgram = **_NSGetArgv(); */
52 fprintf(stderr, "usage:\n"
53 "\tConsoleMessage [-v] <message>\n"
54 "\tConsoleMessage [-v] -S\n"
55 "\tConsoleMessage [-v] -F\n"
56 "\tConsoleMessage [-v] -s <service>\n"
57 "\tConsoleMessage [-v] -f <service>\n"
58 "\tConsoleMessage [-v] -q <setting>\n"
59 "\tConsoleMessage [-v] -b <path>\n"
60 "\tConsoleMessage [-v] -u\n"
61 "\noptions:\n"
62 "\t-v: verbose (prints errors to stdout)\n"
63 "\t-S: mark all services as successful\n"
64 "\t-F: mark all services as failed\n"
65 "\t-s: mark a specific service as successful\n"
66 "\t-f: mark a specific service as failed\n"
67 "\t-q: query a configuration setting\n"
68 "\t-b: (ignored)\n"
69 "\t-u: (ignored)\n");
70 exit(1);
71 }
72
73 enum {
74 kActionConsoleMessage,
75 kActionSuccess,
76 kActionFailure,
77 kActionQuery,
78 };
79
80 int
81 main(int argc, char *argv[])
82 {
83 int anExitCode = 0;
84 int aVerboseFlag = 0;
85 int anAction = kActionConsoleMessage;
86 char *aProgram = argv[0];
87 char *anArgCStr = NULL;
88 char c;
89 pid_t w4lw_pid = 0;
90 FILE *w4lw_f;
91
92 /**
93 * Handle command line.
94 **/
95 while ((c = getopt(argc, argv, "?vSFs:f:q:b:u")) != -1) {
96 switch (c) {
97 case '?':
98 usage();
99 break;
100 case 'v':
101 aVerboseFlag = 1;
102 break;
103 case 'S':
104 anAction = kActionSuccess;
105 anArgCStr = NULL;
106 break;
107 case 'F':
108 anAction = kActionFailure;
109 anArgCStr = NULL;
110 break;
111 case 's':
112 anAction = kActionSuccess;
113 anArgCStr = optarg;
114 break;
115 case 'f':
116 anAction = kActionFailure;
117 anArgCStr = optarg;
118 break;
119 case 'q':
120 anAction = kActionQuery;
121 anArgCStr = optarg;
122 break;
123 case 'b':
124 exit(EXIT_SUCCESS);
125 break;
126 case 'u':
127 w4lw_f = fopen("/var/run/waiting4loginwindow.pid", "r");
128 if (w4lw_f) {
129 fscanf(w4lw_f, "%d\n", &w4lw_pid);
130 if (w4lw_pid)
131 kill(w4lw_pid, SIGTERM);
132 }
133 exit(EXIT_SUCCESS);
134 break;
135 default:
136 fprintf(stderr, "ignoring unknown option '-%c'\n", c);
137 break;
138 }
139 }
140 argc -= optind;
141 argv += optind;
142
143 if ((anAction == kActionConsoleMessage && argc != 1) ||
144 (anAction == kActionSuccess && argc != 0) ||
145 (anAction == kActionFailure && argc != 0) ||
146 (anAction == kActionQuery && argc != 0)) {
147 usage();
148 }
149 if (getuid() != 0) {
150 fprintf(stderr, "you must be root to run %s\n", aProgram);
151 exit(1);
152 } else {
153 CFMutableDictionaryRef anIPCMessage = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks,
154 &kCFTypeDictionaryValueCallBacks);
155
156 if (anIPCMessage) {
157 CFStringRef anArg = NULL;
158 CFIndex aPID = getppid();
159 CFNumberRef aPIDNumber = CFNumberCreate(NULL, kCFNumberCFIndexType, &aPID);
160
161 /*
162 * Parent process id is the process id of the startup
163 * item that called ConsoleMessage.
164 */
165 CFDictionarySetValue(anIPCMessage, kIPCProcessIDKey, aPIDNumber);
166 CFRelease(aPIDNumber);
167
168 if (anArgCStr) {
169 anArg = CFStringCreateWithCString(NULL, anArgCStr, kCFStringEncodingUTF8);
170 }
171 if (anAction == kActionSuccess || anAction == kActionFailure) {
172 CFBooleanRef aStatus = (anAction == kActionSuccess) ? kCFBooleanTrue : kCFBooleanFalse;
173 CFDictionarySetValue(anIPCMessage, kIPCMessageKey, kIPCStatusMessage);
174 CFDictionarySetValue(anIPCMessage, kIPCStatusKey, aStatus);
175 if (anArg)
176 CFDictionarySetValue(anIPCMessage, kIPCServiceNameKey, anArg);
177 } else if (anAction == kActionQuery && anArg) {
178 CFDictionarySetValue(anIPCMessage, kIPCMessageKey, kIPCQueryMessage);
179 CFDictionarySetValue(anIPCMessage, kIPCConfigSettingKey, anArg);
180 } else if (anAction == kActionConsoleMessage) {
181 char *aConsoleMessageCStr = argv[0];
182 CFStringRef aConsoleMessage = CFStringCreateWithCString(NULL, aConsoleMessageCStr, kCFStringEncodingUTF8);
183
184 syslog(LOG_INFO, "%s", aConsoleMessageCStr);
185
186 CFDictionarySetValue(anIPCMessage, kIPCMessageKey, kIPCConsoleMessage);
187 CFDictionarySetValue(anIPCMessage, kIPCConsoleMessageKey, aConsoleMessage);
188 CFRelease(aConsoleMessage);
189 }
190 if (anArg)
191 CFRelease(anArg);
192
193 {
194 CFDataRef aData = CFPropertyListCreateXMLData(NULL, anIPCMessage);
195 if (aData) {
196 CFDataRef aResultData = sendIPCMessage(CFSTR(kSystemStarterMessagePort), aData, kCFRunLoopDefaultMode);
197
198 /* aResultData should be ASCIZ */
199 if (aResultData) {
200 fprintf(stdout, "%s", CFDataGetBytePtr(aResultData));
201 CFRelease(aResultData);
202 } else {
203 char *aConsoleMessageCStr = argv[0];
204 fprintf(stdout, "%s\n", aConsoleMessageCStr);
205
206 if (aVerboseFlag)
207 fprintf(stderr, "%s could not connect to SystemStarter.\n", aProgram);
208 anExitCode = 0;
209 }
210 CFRelease(aData);
211 } else {
212 if (aVerboseFlag)
213 fprintf(stderr, "%s: not enough memory to create IPC message.\n", aProgram);
214 anExitCode = 1;
215 }
216 }
217 } else {
218 if (aVerboseFlag)
219 fprintf(stderr, "%s: not enough memory to create IPC message.\n", aProgram);
220 anExitCode = 1;
221 }
222 }
223 exit(anExitCode);
224 }
225
226
227 static void
228 dummyCallback(void)
229 {
230 }
231
232 static void replyCallback(CFMachPortRef port __attribute__((unused)), void *aPtr, CFIndex aSize __attribute__((unused)), CFDataRef * aReply) {
233 SystemStarterIPCMessage *aMessage = (SystemStarterIPCMessage *) aPtr;
234
235 if (aReply != NULL &&
236 aMessage->aProtocol == kIPCProtocolVersion &&
237 aMessage->aByteLength >= 0) {
238 *aReply = CFDataCreate(NULL, (char *) aMessage + aMessage->aByteLength, aMessage->aByteLength);
239 } else if (aReply != NULL) {
240 *aReply = NULL;
241 }
242 }
243
244
245 static CFDataRef
246 sendIPCMessage(CFStringRef aPortName, CFDataRef aData, CFStringRef aRunLoopMode)
247 {
248 SystemStarterIPCMessage *aMessage = NULL;
249 CFRunLoopSourceRef aSource = NULL;
250 CFMachPortRef aMachPort = NULL, aReplyPort = NULL;
251 CFMachPortContext aContext;
252 kern_return_t aKernReturn = KERN_FAILURE;
253 mach_port_t aBootstrapPort, aNativePort;
254 char *aPortNameUTF8String;
255 CFDataRef aReply = NULL;
256 SInt32 aStrLen;
257
258 aContext.version = 0;
259 aContext.info = (void *) NULL;
260 aContext.retain = 0;
261 aContext.release = 0;
262 aContext.copyDescription = 0;
263
264 /* Look up the remote port by name */
265
266 aStrLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(aPortName) + 1, kCFStringEncodingUTF8);
267 aPortNameUTF8String = malloc(aStrLen);
268 if (aPortNameUTF8String) {
269 CFStringGetCString(aPortName, aPortNameUTF8String, aStrLen, kCFStringEncodingUTF8);
270 task_get_bootstrap_port(mach_task_self(), &aBootstrapPort);
271 aKernReturn = bootstrap_look_up(aBootstrapPort, aPortNameUTF8String, &aNativePort);
272 aMachPort = (KERN_SUCCESS == aKernReturn) ? CFMachPortCreateWithPort(NULL, aNativePort, (CFMachPortCallBack) dummyCallback, &aContext, NULL) : NULL;
273 free(aPortNameUTF8String);
274 }
275 /* Create a reply port and associated run loop source */
276 aContext.info = &aReply;
277 aReplyPort = CFMachPortCreate(NULL, (CFMachPortCallBack) replyCallback, &aContext, NULL);
278 if (aReplyPort) {
279 aSource = CFMachPortCreateRunLoopSource(NULL, aReplyPort, 0);
280 if (aSource) {
281 CFRunLoopAddSource(CFRunLoopGetCurrent(), aSource, aRunLoopMode);
282 }
283 }
284 /* Allocate a buffer for the message */
285 if (aData && aMachPort && aReplyPort) {
286 SInt32 aSize = (sizeof(SystemStarterIPCMessage) + (CFDataGetLength(aData) + 3)) & ~0x3;
287 aMessage = (SystemStarterIPCMessage *) malloc(aSize);
288 if (aMessage) {
289 aMessage->aHeader.msgh_id = 1;
290 aMessage->aHeader.msgh_size = aSize;
291 aMessage->aHeader.msgh_remote_port = CFMachPortGetPort(aMachPort);
292 aMessage->aHeader.msgh_local_port = CFMachPortGetPort(aReplyPort);
293 aMessage->aHeader.msgh_reserved = 0;
294 aMessage->aHeader.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
295 aMessage->aBody.msgh_descriptor_count = 0;
296 aMessage->aProtocol = kIPCProtocolVersion;
297 aMessage->aByteLength = CFDataGetLength(aData);
298 memmove((uint8_t *) aMessage + sizeof(SystemStarterIPCMessage),
299 CFDataGetBytePtr(aData),
300 CFDataGetLength(aData));
301 }
302 }
303 /* Wait up to 1 second to send the message */
304 if (aMessage) {
305 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);
306 free(aMessage);
307 }
308 /* Wait up to 30 seconds for the reply */
309 if (aSource && aKernReturn == MACH_MSG_SUCCESS) {
310 CFRetain(aReplyPort);
311 CFRunLoopRunInMode(aRunLoopMode, 30.0, true);
312 /*
313 * aReplyPort's replyCallback will set the local aReply
314 * variable
315 */
316 CFRelease(aReplyPort);
317 }
318 if (aMachPort)
319 CFRelease(aMachPort);
320 if (aReplyPort)
321 CFRelease(aReplyPort);
322 if (aSource) {
323 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), aSource, aRunLoopMode);
324 CFRelease(aSource);
325 }
326 return aReply;
327 }