source: trunk/Cocoa/F-Script Anywhere/Source/SCPatch/Common/SCPatchMessenger.cp@ 400

Last change on this file since 400 was 342, checked in by rchin, 17 years ago

Leopard compatibility changes:

  • Removed some icon caching code that was causing crashes (not sure we really needed those optimizations anyway -- doesn't appear to affect performance)
  • Added code to automatically try to add certificate to keychain, for new code signing behavior (replaces previous procmod nonesense).
  • Note that the enclosed public certificate is mine, and so it will need to be signed by me. In the case that someone else wants to distribute this binary, please replace Certficiate.cer with your own public certificate, and then make sure o code sign the binary after it is built.
File size: 12.7 KB
Line 
1#include <Carbon/Carbon.h>
2#include <sys/time.h>
3#include <syslog.h>
4#include <unistd.h>
5#include "SCPatchPrivate.h"
6#include "SCPatchMessenger.h"
7
8//-------------------------------------------------------------------------------------------------------------
9SCPatchMessenger::SCPatchMessenger(void)
10{
11 mThreaded = false;
12 mMessagePort = NULL;
13 mMessageSource = NULL;
14 pthread_mutex_init(&mMessageQueueMutex, NULL);
15 mMessageRetryInterval = DEFAULT_MESSAGE_RETRY_INTERVAL;
16 mMessageRetryLimit = DEFAULT_MESSAGE_RETRY_LIMIT;
17
18 mMessageQueueDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, NULL);
19 mMessageThreadDict = CFDictionaryCreateMutable(NULL, 0, NULL, &kCFTypeDictionaryValueCallBacks);
20}
21
22//-------------------------------------------------------------------------------------------------------------
23SCPatchMessenger::~SCPatchMessenger(void)
24{
25 CFRelease(mMessageQueueDict);
26 CFRelease(mMessageThreadDict);
27}
28
29//-------------------------------------------------------------------------------------------------------------
30OSErr SCPatchMessenger::StartListening(CFStringRef bundleIdentifier,
31 Boolean isPatch, Boolean threaded)
32{
33 OSErr err = noErr;
34 CFStringRef messagePortName;
35 CFMessagePortContext messageContext = { 0 };
36 Boolean messageShouldFree;
37 EventLoopRef mainLoop;
38
39 mThreaded = threaded;
40
41 // Create a port to receive messages
42 messageContext.info = this;
43
44 if(isPatch)
45 messagePortName = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@.%ld"), bundleIdentifier, getpid());
46 else
47 messagePortName = CFStringCreateCopy(NULL, bundleIdentifier);
48
49 if(messagePortName == NULL)
50 return memFullErr;
51
52 if(messagePortName)
53 mMessagePort = CFMessagePortCreateLocal(NULL, messagePortName, ReceiveCallback, &messageContext, &messageShouldFree);
54
55 CFRelease(messagePortName);
56
57 if(mMessagePort == NULL)
58 return destPortErr;
59
60 // Set up to receive messages from the controller
61 if((mMessageSource = CFMessagePortCreateRunLoopSource(NULL, mMessagePort, 0)) != NULL &&
62 (mainLoop = GetMainEventLoop()) != NULL)
63 {
64 OSErr err;
65 AEDesc desc;
66 OSType sig = 'SCPM';
67
68 CFRunLoopAddSource((CFRunLoopRef)GetCFRunLoopFromEventLoop(mainLoop), mMessageSource, kCFRunLoopCommonModes);
69
70 // Tell the patch code that it has officially been started.
71 err = AEBuildAppleEvent(kEventClassSCPatchMessenger, kEventSCPatchStart,
72 typeApplSignature, &sig, sizeof(sig),
73 kAutoGenerateReturnID, kAnyTransactionID,
74 &desc, NULL, "");
75 ReceiveMessage(&desc);
76 }
77 else
78 {
79 CFRelease(mMessagePort);
80 mMessagePort = NULL;
81 }
82
83 return err;
84}
85
86//-------------------------------------------------------------------------------------------------------------
87OSErr SCPatchMessenger::StopListening(void)
88{
89 EventLoopRef mainLoop;
90 OSErr err;
91 AEDesc desc;
92 OSType sig = 'SCPM';
93
94 if(mMessageSource && (mainLoop = GetMainEventLoop()) != NULL)
95 {
96 // Tell the patch code that it's about to be stopped.
97 err = AEBuildAppleEvent(kEventClassSCPatchMessenger, kEventSCPatchStop,
98 typeApplSignature, &sig, sizeof(sig),
99 kAutoGenerateReturnID, kAnyTransactionID,
100 &desc, NULL, "");
101 ReceiveMessage(&desc);
102
103 CFRunLoopRemoveSource((CFRunLoopRef)GetCFRunLoopFromEventLoop(mainLoop), mMessageSource, kCFRunLoopCommonModes);
104 CFRelease(mMessageSource);
105 mMessageSource = NULL;
106 }
107
108 if(mMessagePort)
109 {
110 CFRelease(mMessagePort);
111 mMessagePort = NULL;
112 }
113 return noErr;
114}
115
116//-------------------------------------------------------------------------------------------------------------
117OSErr SCPatchMessenger::SendPing(CFStringRef bundleIdentifier, ProcessSerialNumber *psn)
118{
119 OSErr err;
120 AEDesc desc;
121 OSType sig = 'SCPM';
122
123 err = AEBuildAppleEvent(kSCMessageClass, kSCPing,
124 typeApplSignature, &sig, sizeof(sig),
125 kAutoGenerateReturnID, kAnyTransactionID,
126 &desc, NULL, "");
127
128 if(err == noErr)
129 err = SendMessage(bundleIdentifier, psn, &desc, false);
130
131 return err;
132}
133
134//-------------------------------------------------------------------------------------------------------------
135OSErr SCPatchMessenger::SendMessage(CFStringRef bundleIdentifier,
136 ProcessSerialNumber *psn,
137 AEEventClass evtClass, AEEventID evtID,
138 const char *paramsFmt, ...)
139{
140 OSErr err;
141 AEDesc desc;
142 va_list args;
143 OSType sig = 'SCPM';
144
145 va_start(args, paramsFmt);
146 // fprintf(stderr, "Sending message %.4s:%.4s\n", &evtClass, &evtID);
147 err = vAEBuildAppleEvent(evtClass, evtID,
148 typeApplSignature, &sig, sizeof(sig),
149 kAutoGenerateReturnID, kAnyTransactionID,
150 &desc, NULL, paramsFmt, args);
151 va_end(args);
152
153 if(err == noErr)
154 err = SendMessage(bundleIdentifier, psn, &desc, mThreaded);
155
156 return err;
157}
158
159
160//-------------------------------------------------------------------------------------------------------------
161OSErr SCPatchMessenger::SendMessage(CFStringRef bundleIdentifier,
162 ProcessSerialNumber *psn,
163 const AppleEvent *theAE,
164 Boolean threaded)
165{
166 OSErr err;
167 pid_t pid = 0;
168 AEMessageInfo *info;
169
170 if(psn && (err = GetProcessPID(psn, &pid)) != noErr)
171 return err;
172
173 // If we don't succeed or get an error, there's not enough memory
174 err = memFullErr;
175
176 if((info = (AEMessageInfo *)NewPtrClear(sizeof(AEMessageInfo))) != NULL &&
177 (info->descSize = AEGetDescDataSize(theAE)) > 0 &&
178 (info->descData = NewPtr(info->descSize)) != NULL &&
179 AEGetDescData(theAE, info->descData, info->descSize) == noErr)
180 {
181 if(pid)
182 info->portName = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@.%ld"), bundleIdentifier, pid);
183 else
184 info->portName = CFStringCreateCopy(NULL, bundleIdentifier);
185
186 if(info->portName)
187 {
188 info->threaded = threaded;
189
190 if(!threaded)
191 {
192 err = SendMessage(info);
193 DisposePtr(info->descData);
194 DisposePtr((Ptr)info);
195 }
196 else if((err = QueueMessage(info)) != noErr)
197 {
198 err = queueFull;
199 }
200
201 }
202 }
203
204 return err;
205}
206
207//-------------------------------------------------------------------------------------------------------------
208OSErr SCPatchMessenger::SendMessage(AEMessageInfo *info)
209{
210 SInt32 result = kCFMessagePortTransportError;
211 OSErr err = noErr;
212 CFDataRef dataRef;
213 CFMessagePortRef portRef;
214
215 if((dataRef = CFDataCreate(NULL, (unsigned char*)info->descData, info->descSize)) != NULL)
216 {
217 if((portRef = CFMessagePortCreateRemote(NULL, info->portName)) != NULL)
218 {
219 result = CFMessagePortSendRequest(portRef, kSCMessageClass, dataRef, info->threaded ? 0.01 : 5, 0, NULL, NULL);
220 CFRelease(portRef);
221 }
222 else
223 {
224 err = destPortErr;
225 }
226 CFRelease(dataRef);
227 }
228 else
229 {
230 err = memFullErr;
231 }
232
233 // If we're not going to retry, if we've retried too many times, if the message was sent successfully,
234 // or there was some unrecoverable error like destPortErr (no one's listening) give up and let the
235 // error pass to the thread loop so it can clean up.
236 if(!info->threaded || info->retryCount >= mMessageRetryLimit ||
237 (err == noErr && result == kCFMessagePortSuccess) ||
238 err == destPortErr || err == memFullErr)
239 {
240 if(err == noErr && result != kCFMessagePortSuccess)
241 err = (result == kCFMessagePortSendTimeout ? noResponseErr : networkErr);
242#if 0
243 if(err != noErr || result != kCFMessagePortSuccess)
244 syslog(LOG_ALERT, "%d: Message to %s failed (err = %d result = %d)\n", getpid(), CFStringGetCStringPtr(info->portName, kCFStringEncodingMacRoman), err, result);
245 else
246 syslog(LOG_ALERT, "%d: Message sent to %s\n", getpid(), CFStringGetCStringPtr(info->portName, kCFStringEncodingMacRoman));
247
248#endif
249 }
250 else if(info->threaded) // drop the sending thread's priority and hang around for mMessageRetryInterval
251 {
252 int policy;
253 struct sched_param param;
254 struct timeval startTime, endTime;
255
256 info->retryCount++;
257 pthread_getschedparam(pthread_self(), &policy, &param);
258 param.sched_priority = sched_get_priority_min(policy);
259 pthread_setschedparam(pthread_self(), policy, &param);
260 gettimeofday(&startTime, NULL);
261 for(;;)
262 {
263 gettimeofday(&endTime, NULL);
264 if((endTime.tv_sec - startTime.tv_sec) * 1000000L + (endTime.tv_usec - startTime.tv_usec) >
265 mMessageRetryInterval * 1000000)
266 {
267 // syslog(LOG_ALERT, "%d: waiting to resend on %s (%d)\n",
268 // getpid(), CFStringGetCStringPtr(info->portName, kCFStringEncodingMacRoman), info->retryCount);
269 break;
270 }
271 }
272
273 err = channelBusy;
274 }
275
276 return err;
277}
278
279//-------------------------------------------------------------------------------------------------------------
280void *SCPatchMessenger::SendThreadEntryPoint(void *infoPtr)
281{
282 SCPatchMessenger *self = (SCPatchMessenger *)infoPtr;
283 AEMessageInfo *info;
284 OSStatus err;
285
286 while((info = self->GetNextMessage(pthread_self())) != NULL)
287 {
288 // This will block for mMessageRetryInterval if the send fails
289 err = self->SendMessage(info);
290
291 // If we've retried too many times, if the message was sent successfully, or if there
292 // was some unrecoverable error like destPortErr (no one's listening) give up.
293 if(err == noErr || err == destPortErr || err == memFullErr || err == noResponseErr)
294 {
295 if((info = self->DequeueMessage(pthread_self())) != NULL)
296 {
297 CFRelease(info->portName);
298 DisposePtr(info->descData);
299 DisposePtr((Ptr)info);
300 }
301 }
302 }
303
304 return (void *)err;
305}
306
307//-------------------------------------------------------------------------------------------------------------
308OSErr SCPatchMessenger::QueueMessage(AEMessageInfo *info)
309{
310 CFMutableArrayRef queue;
311 OSErr result = noErr;
312
313 pthread_mutex_lock(&mMessageQueueMutex);
314
315 if((queue = (CFMutableArrayRef)CFDictionaryGetValue(mMessageQueueDict, info->portName)) == NULL)
316 {
317 if((queue = CFArrayCreateMutable(NULL, 0, NULL)) != NULL)
318 {
319 pthread_t messageThread;
320
321 CFDictionaryAddValue(mMessageQueueDict, info->portName, queue);
322 pthread_create(&messageThread, NULL, SendThreadEntryPoint, (void *)this);
323 pthread_detach(messageThread);
324 CFDictionaryAddValue(mMessageThreadDict, messageThread, info->portName);
325 }
326 }
327
328 if(queue)
329 CFArrayAppendValue(queue, info);
330 else
331 result = queueFull;
332
333 pthread_mutex_unlock(&mMessageQueueMutex);
334 return result;
335}
336
337//-------------------------------------------------------------------------------------------------------------
338AEMessageInfo *SCPatchMessenger::GetNextMessage(pthread_t threadID)
339{
340 CFMutableArrayRef queue;
341 CFStringRef queueName;
342 AEMessageInfo *result = NULL;
343
344 pthread_mutex_lock(&mMessageQueueMutex);
345
346 if((queueName = (CFStringRef)CFDictionaryGetValue(mMessageThreadDict, threadID)) != NULL)
347 if((queue = (CFMutableArrayRef)CFDictionaryGetValue(mMessageQueueDict, queueName)) != NULL)
348 if(CFArrayGetCount(queue) > 0)
349 result = (AEMessageInfo *)CFArrayGetValueAtIndex(queue, 0);
350
351 pthread_mutex_unlock(&mMessageQueueMutex);
352
353 return result;
354}
355
356//-------------------------------------------------------------------------------------------------------------
357AEMessageInfo *SCPatchMessenger::DequeueMessage(pthread_t threadID)
358{
359 CFMutableArrayRef queue;
360 CFStringRef queueName;
361 Boolean stopThread = false;
362 AEMessageInfo *result = NULL;
363
364 pthread_mutex_lock(&mMessageQueueMutex);
365
366 if((queueName = (CFStringRef)CFDictionaryGetValue(mMessageThreadDict, threadID)) != NULL &&
367 (queue = (CFMutableArrayRef)CFDictionaryGetValue(mMessageQueueDict, queueName)) != NULL)
368 {
369 if(CFArrayGetCount(queue) > 0)
370 {
371 result = (AEMessageInfo *)CFArrayGetValueAtIndex(queue, 0);
372 CFArrayRemoveValueAtIndex(queue, 0);
373 }
374 if(CFArrayGetCount(queue) == 0)
375 {
376 CFDictionaryRemoveValue(mMessageThreadDict, threadID);
377 CFDictionaryRemoveValue(mMessageQueueDict, queueName);
378 stopThread = true;
379 }
380 }
381
382 pthread_mutex_unlock(&mMessageQueueMutex);
383
384 if(stopThread)
385 pthread_cancel(threadID);
386
387 return result;
388}
389
390//-------------------------------------------------------------------------------------------------------------
391CFDataRef SCPatchMessenger::ReceiveCallback(CFMessagePortRef local,
392 SInt32 message,
393 CFDataRef dataRef,
394 void *info)
395{
396 #pragma unused(local)
397
398 SCPatchMessenger *self = (SCPatchMessenger *)info;
399 Ptr descData = NULL;
400 AEDesc desc;
401 long length;
402
403 // fprintf(stderr, "%d: Receiving started\n", getpid());
404 if(message == kSCMessageClass)
405 {
406 if((length = CFDataGetLength(dataRef)) > 0 && (descData = NewPtr(length)) != NULL)
407 {
408 CFDataGetBytes(dataRef, CFRangeMake(0, length), (unsigned char *)descData);
409
410 if(AECreateDesc(typeAppleEvent, descData, length, &desc) == noErr)
411 {
412 // fprintf(stderr, "%d: handling message\n", getpid());
413 if (self->HandleMessage(&desc) != noErr)
414 self->ReceiveMessage(&desc);
415 // fprintf(stderr, "%d: done handling message\n", getpid());
416 AEDisposeDesc(&desc);
417 }
418 else
419 {
420 fprintf(stderr, "%d: Error %d building AppleEvent", getpid(), memFullErr);
421 }
422 }
423 }
424
425 if(descData)
426 DisposePtr(descData);
427
428 // fprintf(stderr, "%d: Receiving finished\n", getpid());
429 return NULL;
430}
431
Note: See TracBrowser for help on using the repository browser.