1/*
2 * Copyright (c) 1998-2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include "DASession.h"
25
26#include "DACallback.h"
27#include "DAServer.h"
28
29#include <mach/mach.h>
30#include <CoreFoundation/CoreFoundation.h>
31#include <CoreFoundation/CFRuntime.h>
32
33struct __DASession
34{
35    CFRuntimeBase      _base;
36
37    AuthorizationRef   _authorization;
38    mach_port_t        _client;
39    char *             _name;
40    pid_t              _pid;
41    DASessionOptions   _options;
42    CFMutableArrayRef  _queue;
43    CFMutableArrayRef  _register;
44    CFMachPortRef      _server;
45    CFRunLoopSourceRef _source;
46    DASessionState     _state;
47};
48
49typedef struct __DASession __DASession;
50
51static CFStringRef  __DASessionCopyDescription( CFTypeRef object );
52static CFStringRef  __DASessionCopyFormattingDescription( CFTypeRef object, CFDictionaryRef options );
53static void         __DASessionDeallocate( CFTypeRef object );
54static Boolean      __DASessionEqual( CFTypeRef object1, CFTypeRef object2 );
55static CFHashCode   __DASessionHash( CFTypeRef object );
56
57static const CFRuntimeClass __DASessionClass =
58{
59    0,
60    "DASession",
61    NULL,
62    NULL,
63    __DASessionDeallocate,
64    __DASessionEqual,
65    __DASessionHash,
66    __DASessionCopyFormattingDescription,
67    __DASessionCopyDescription
68};
69
70static CFTypeID __kDASessionTypeID = _kCFRuntimeNotATypeID;
71
72static CFStringRef __DASessionCopyDescription( CFTypeRef object )
73{
74    DASessionRef session = ( DASessionRef ) object;
75
76    return CFStringCreateWithFormat( CFGetAllocator( object ),
77                                     NULL,
78                                     CFSTR( "<DASession %p [%p]>{id = %s [%d]:%d}" ),
79                                     object,
80                                     CFGetAllocator( object ),
81                                     session->_name,
82                                     session->_pid,
83                                     CFMachPortGetPort( session->_server ) );
84}
85
86static CFStringRef __DASessionCopyFormattingDescription( CFTypeRef object, CFDictionaryRef options )
87{
88    DASessionRef session = ( DASessionRef ) object;
89
90    return CFStringCreateWithFormat( CFGetAllocator( object ),
91                                     NULL,
92                                     CFSTR( "%s [%d]:%d" ),
93                                     session->_name,
94                                     session->_pid,
95                                     CFMachPortGetPort( session->_server ) );
96}
97
98static DASessionRef __DASessionCreate( CFAllocatorRef allocator )
99{
100    __DASession * session;
101
102    session = ( void * ) _CFRuntimeCreateInstance( allocator, __kDASessionTypeID, sizeof( __DASession ) - sizeof( CFRuntimeBase ), NULL );
103
104    if ( session )
105    {
106        session->_authorization = NULL;
107        session->_client        = MACH_PORT_NULL;
108        session->_name          = NULL;
109        session->_pid           = 0;
110        session->_options       = 0;
111        session->_queue         = CFArrayCreateMutable( allocator, 0, &kCFTypeArrayCallBacks );
112        session->_register      = CFArrayCreateMutable( allocator, 0, &kCFTypeArrayCallBacks );
113        session->_server        = NULL;
114        session->_source        = NULL;
115        session->_state         = 0;
116
117        assert( session->_queue    );
118        assert( session->_register );
119    }
120
121    return session;
122}
123
124static void __DASessionDeallocate( CFTypeRef object )
125{
126    DASessionRef session = ( DASessionRef ) object;
127
128    if ( session->_authorization )  AuthorizationFree( session->_authorization, kAuthorizationFlagDefaults );
129    if ( session->_client        )  mach_port_deallocate( mach_task_self( ), session->_client );
130    if ( session->_name          )  free( session->_name );
131    if ( session->_queue         )  CFRelease( session->_queue );
132    if ( session->_register      )  CFRelease( session->_register );
133
134    if ( session->_source )
135    {
136        CFRunLoopSourceInvalidate( session->_source );
137
138        CFRelease( session->_source );
139    }
140
141    if ( session->_server )
142    {
143        mach_port_t serverPort;
144
145        serverPort = CFMachPortGetPort( session->_server );
146
147        CFMachPortInvalidate( session->_server );
148
149        CFRelease( session->_server );
150
151        mach_port_mod_refs( mach_task_self( ), serverPort, MACH_PORT_RIGHT_RECEIVE, -1 );
152    }
153}
154
155static Boolean __DASessionEqual( CFTypeRef object1, CFTypeRef object2 )
156{
157    DASessionRef session1 = ( DASessionRef ) object1;
158    DASessionRef session2 = ( DASessionRef ) object2;
159
160    return ( session1->_server == session2->_server ) ? TRUE : FALSE;
161}
162
163static CFHashCode __DASessionHash( CFTypeRef object )
164{
165    DASessionRef session = ( DASessionRef ) object;
166
167    return ( CFHashCode ) CFMachPortGetPort( session->_server );
168}
169
170///w:start
171const char * _DASessionGetName( DASessionRef session )
172{
173    return session->_name;
174}
175///w:stop
176DASessionRef DASessionCreate( CFAllocatorRef allocator, const char * _name, pid_t _pid )
177{
178    DASessionRef session;
179
180    /*
181     * Create the session.
182     */
183
184    session = __DASessionCreate( allocator );
185
186    if ( session )
187    {
188        mach_port_t   serverPort;
189        kern_return_t status;
190
191        status = mach_port_allocate( mach_task_self( ), MACH_PORT_RIGHT_RECEIVE, &serverPort );
192
193        if ( status == KERN_SUCCESS )
194        {
195            CFMachPortRef     server;
196            CFMachPortContext serverContext;
197
198            serverContext.version         = 0;
199            serverContext.info            = session;
200            serverContext.retain          = NULL;
201            serverContext.release         = NULL;
202            serverContext.copyDescription = NULL;
203
204            /*
205             * Create the session's server port.
206             */
207
208            server = CFMachPortCreateWithPort( allocator, serverPort, _DAServerCallback, &serverContext, NULL );
209
210            if ( server )
211            {
212                CFRunLoopSourceRef source;
213
214                /*
215                 * Create the session's server port run loop source.
216                 */
217
218                source = CFMachPortCreateRunLoopSource( allocator, server, 0 );
219
220                if ( source )
221                {
222                    mach_port_t port;
223
224                    /*
225                     * Set up the session's server port.
226                     */
227
228                    status = mach_port_request_notification( mach_task_self( ),
229                                                             CFMachPortGetPort( server ),
230                                                             MACH_NOTIFY_NO_SENDERS,
231                                                             1,
232                                                             CFMachPortGetPort( server ),
233                                                             MACH_MSG_TYPE_MAKE_SEND_ONCE,
234                                                             &port );
235
236                    if ( status == KERN_SUCCESS )
237                    {
238                        assert( port == MACH_PORT_NULL );
239
240                        session->_name   = strdup( _name );
241                        session->_pid    = _pid;
242                        session->_server = server;
243                        session->_source = source;
244
245                        return session;
246                    }
247
248                    CFRunLoopSourceInvalidate( source );
249
250                    CFRelease( source );
251                }
252
253                CFMachPortInvalidate( server );
254
255                CFRelease( server );
256            }
257
258            mach_port_mod_refs( mach_task_self( ), serverPort, MACH_PORT_RIGHT_RECEIVE, -1 );
259        }
260
261        CFRelease( session );
262    }
263
264    return NULL;
265}
266
267AuthorizationRef DASessionGetAuthorization( DASessionRef session )
268{
269    return session->_authorization;
270}
271
272CFMutableArrayRef DASessionGetCallbackQueue( DASessionRef session )
273{
274    return session->_queue;
275}
276
277CFMutableArrayRef DASessionGetCallbackRegister( DASessionRef session )
278{
279    return session->_register;
280}
281
282mach_port_t DASessionGetID( DASessionRef session )
283{
284    return CFMachPortGetPort( session->_server );
285}
286
287Boolean DASessionGetOption( DASessionRef session, DASessionOption option )
288{
289    return ( session->_options & option ) ? TRUE : FALSE;
290}
291
292DASessionOptions DASessionGetOptions( DASessionRef session )
293{
294    return session->_options;
295}
296
297mach_port_t DASessionGetServerPort( DASessionRef session )
298{
299    return CFMachPortGetPort( session->_server );
300}
301
302Boolean DASessionGetState( DASessionRef session, DASessionState state )
303{
304    return ( session->_state & state ) ? TRUE : FALSE;
305}
306
307CFTypeID DASessionGetTypeID( void )
308{
309    return __kDASessionTypeID;
310}
311
312void DASessionInitialize( void )
313{
314    __kDASessionTypeID = _CFRuntimeRegisterClass( &__DASessionClass );
315}
316
317void DASessionQueueCallback( DASessionRef session, DACallbackRef callback )
318{
319    session->_state &= ~kDASessionStateIdle;
320
321    CFArrayAppendValue( session->_queue, callback );
322
323    if ( CFArrayGetCount( session->_queue ) == 1 )
324    {
325        if ( session->_client )
326        {
327            mach_msg_header_t message;
328            kern_return_t     status;
329
330            message.msgh_bits        = MACH_MSGH_BITS( MACH_MSG_TYPE_COPY_SEND, 0 );
331            message.msgh_id          = 0;
332            message.msgh_local_port  = MACH_PORT_NULL;
333            message.msgh_remote_port = session->_client;
334            message.msgh_reserved    = 0;
335            message.msgh_size        = sizeof( message );
336
337            status = mach_msg( &message, MACH_SEND_MSG | MACH_SEND_TIMEOUT, message.msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL );
338
339            if ( status == MACH_SEND_TIMED_OUT )
340            {
341                mach_msg_destroy( &message );
342            }
343        }
344    }
345}
346
347void DASessionRegisterCallback( DASessionRef session, DACallbackRef callback )
348{
349    CFArrayAppendValue( session->_register, callback );
350}
351
352void DASessionScheduleWithRunLoop( DASessionRef session, CFRunLoopRef runLoop, CFStringRef runLoopMode )
353{
354    CFRunLoopAddSource( runLoop, session->_source, runLoopMode );
355}
356
357void DASessionSetAuthorization( DASessionRef session, AuthorizationRef authorization )
358{
359    if ( session->_authorization )
360    {
361        AuthorizationFree( session->_authorization, kAuthorizationFlagDefaults );
362    }
363
364    session->_authorization = authorization;
365}
366
367void DASessionSetClientPort( DASessionRef session, mach_port_t client )
368{
369    if ( session->_client )
370    {
371        mach_port_deallocate( mach_task_self( ), session->_client );
372    }
373
374    session->_client = client;
375
376    if ( CFArrayGetCount( session->_queue ) )
377    {
378        if ( session->_client )
379        {
380            mach_msg_header_t message;
381            kern_return_t     status;
382
383            message.msgh_bits        = MACH_MSGH_BITS( MACH_MSG_TYPE_COPY_SEND, 0 );
384            message.msgh_id          = 0;
385            message.msgh_local_port  = MACH_PORT_NULL;
386            message.msgh_remote_port = session->_client;
387            message.msgh_reserved    = 0;
388            message.msgh_size        = sizeof( message );
389
390            status = mach_msg( &message, MACH_SEND_MSG | MACH_SEND_TIMEOUT, message.msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL );
391
392            if ( status == MACH_SEND_TIMED_OUT )
393            {
394                mach_msg_destroy( &message );
395            }
396        }
397    }
398}
399
400void DASessionSetOption( DASessionRef session, DASessionOption option, Boolean value )
401{
402    DASessionSetOptions( session, option, value );
403}
404
405void DASessionSetOptions( DASessionRef session, DASessionOptions options, Boolean value )
406{
407    session->_options &= ~options;
408    session->_options |= value ? options : 0;
409}
410
411void DASessionSetState( DASessionRef session, DASessionState state, Boolean value )
412{
413    session->_state &= ~state;
414    session->_state |= value ? state : 0;
415}
416
417void DASessionUnregisterCallback( DASessionRef session, DACallbackRef callback )
418{
419    CFIndex count;
420    CFIndex index;
421
422    count = CFArrayGetCount( session->_register );
423
424    for ( index = count - 1; index > -1; index-- )
425    {
426        DACallbackRef item;
427
428        item = ( void * ) CFArrayGetValueAtIndex( session->_register, index );
429
430        if ( DACallbackGetAddress( item ) == DACallbackGetAddress( callback ) )
431        {
432            if ( DACallbackGetContext( item ) == DACallbackGetContext( callback ) )
433            {
434                CFArrayRemoveValueAtIndex( session->_register, index );
435            }
436        }
437    }
438
439    count = CFArrayGetCount( session->_queue );
440
441    for ( index = count - 1; index > -1; index-- )
442    {
443        DACallbackRef item;
444
445        item = ( void * ) CFArrayGetValueAtIndex( session->_queue, index );
446
447        if ( DACallbackGetAddress( item ) == DACallbackGetAddress( callback ) )
448        {
449            if ( DACallbackGetContext( item ) == DACallbackGetContext( callback ) )
450            {
451                CFArrayRemoveValueAtIndex( session->_queue, index );
452            }
453        }
454    }
455}
456
457void DASessionUnscheduleFromRunLoop( DASessionRef session, CFRunLoopRef runLoop, CFStringRef runLoopMode )
458{
459    CFRunLoopRemoveSource( runLoop, session->_source, runLoopMode );
460}
461