1/*
2 * Copyright (c) 2000-2010 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/*
25 * FDSet.c
26 * - contains FDCallout, a thin wrapper on CFSocketRef/CFFileDescriptorRef
27 */
28/*
29 * Modification History
30 *
31 * May 11, 2000		Dieter Siegmund (dieter@apple.com)
32 * - created
33 * June 12, 2000	Dieter Siegmund (dieter@apple.com)
34 * - converted to use CFRunLoop
35 * January 27, 2010	Dieter Siegmund (dieter@apple.com)
36 * - use CFFileDescriptorRef for non-sockets
37 */
38
39#include <stdlib.h>
40#include <unistd.h>
41#include <string.h>
42#include <stdio.h>
43#include <sys/types.h>
44#include <sys/stat.h>
45#include <sys/errno.h>
46#include <sys/socket.h>
47#include <net/if_types.h>
48#include <syslog.h>
49#include <CoreFoundation/CFRunLoop.h>
50#include <CoreFoundation/CFSocket.h>
51#include <CoreFoundation/CFFileDescriptor.h>
52
53#include "dynarray.h"
54#include "FDSet.h"
55#include "symbol_scope.h"
56
57struct FDCallout {
58    Boolean			is_socket;
59    union {
60	CFSocketRef		socket;
61	CFFileDescriptorRef 	fdesc;
62    } u;
63    FDCalloutFuncRef		func;
64    void *			arg1;
65    void *			arg2;
66};
67
68STATIC void
69FDCalloutSocketReceive(CFSocketRef s, CFSocketCallBackType type,
70		       CFDataRef address, const void *data, void *info);
71
72STATIC void
73FDCalloutFileDescriptorReceive(CFFileDescriptorRef f,
74			       CFOptionFlags callBackTypes, void *info);
75
76
77STATIC void
78FDCalloutCreateSocket(FDCalloutRef callout, int fd)
79{
80    CFSocketContext	context = { 0, NULL, NULL, NULL, NULL };
81    CFRunLoopSourceRef	rls;
82
83    context.info = callout;
84    callout->is_socket = TRUE;
85    callout->u.socket
86	= CFSocketCreateWithNative(NULL, fd, kCFSocketReadCallBack,
87				   FDCalloutSocketReceive, &context);
88    rls = CFSocketCreateRunLoopSource(NULL, callout->u.socket, 0);
89    CFRunLoopAddSource(CFRunLoopGetCurrent(), rls,
90		       kCFRunLoopDefaultMode);
91    CFRelease(rls);
92    return;
93}
94
95STATIC void
96FDCalloutCreateFileDescriptor(FDCalloutRef callout, int fd)
97{
98    CFFileDescriptorContext	context = { 0, NULL, NULL, NULL, NULL };
99    CFRunLoopSourceRef	rls;
100
101    context.info = callout;
102    callout->is_socket = FALSE;
103    callout->u.fdesc
104	= CFFileDescriptorCreate(NULL, fd, TRUE,
105				 FDCalloutFileDescriptorReceive, &context);
106    CFFileDescriptorEnableCallBacks(callout->u.fdesc,
107				    kCFFileDescriptorReadCallBack);
108    rls = CFFileDescriptorCreateRunLoopSource(NULL, callout->u.fdesc, 0);
109    CFRunLoopAddSource(CFRunLoopGetCurrent(), rls,
110		       kCFRunLoopDefaultMode);
111    CFRelease(rls);
112    return;
113}
114
115PRIVATE_EXTERN FDCalloutRef
116FDCalloutCreate(int fd, FDCalloutFuncRef func,
117		void * arg1, void * arg2)
118{
119    FDCalloutRef	callout;
120    struct stat		sb;
121
122    if (fstat(fd, &sb) < 0) {
123	return (NULL);
124    }
125    callout = malloc(sizeof(*callout));
126    if (callout == NULL) {
127	return (NULL);
128    }
129    bzero(callout, sizeof(*callout));
130    if (S_ISSOCK(sb.st_mode)) {
131	FDCalloutCreateSocket(callout, fd);
132    }
133    else {
134	FDCalloutCreateFileDescriptor(callout, fd);
135    }
136    callout->func = func;
137    callout->arg1 = arg1;
138    callout->arg2 = arg2;
139    return (callout);
140}
141
142STATIC void
143FDCalloutReleaseSocket(FDCalloutRef callout)
144{
145    if (callout->u.socket != NULL) {
146	CFSocketInvalidate(callout->u.socket);
147	CFRelease(callout->u.socket);
148	callout->u.socket = NULL;
149    }
150    return;
151}
152
153STATIC void
154FDCalloutReleaseFileDescriptor(FDCalloutRef callout)
155{
156    if (callout->u.fdesc != NULL) {
157	CFFileDescriptorInvalidate(callout->u.fdesc);
158	CFRelease(callout->u.fdesc);
159	callout->u.fdesc = NULL;
160    }
161    return;
162}
163
164PRIVATE_EXTERN void
165FDCalloutRelease(FDCalloutRef * callout_p)
166{
167    FDCalloutRef callout = *callout_p;
168
169    if (callout == NULL) {
170	return;
171    }
172    if (callout->is_socket) {
173	FDCalloutReleaseSocket(callout);
174    }
175    else {
176	FDCalloutReleaseFileDescriptor(callout);
177    }
178    free(callout);
179    *callout_p = NULL;
180    return;
181}
182
183STATIC void
184FDCalloutSocketReceive(CFSocketRef s, CFSocketCallBackType type,
185		       CFDataRef address, const void *data, void *info)
186{
187    FDCalloutRef 	callout = (FDCalloutRef)info;
188
189    if (callout->func) {
190	(*callout->func)(callout->arg1, callout->arg2);
191    }
192    return;
193}
194
195STATIC void
196FDCalloutFileDescriptorReceive(CFFileDescriptorRef f,
197			       CFOptionFlags callBackTypes, void *info)
198{
199    FDCalloutRef 	callout = (FDCalloutRef)info;
200
201    if (callout->func) {
202	(*callout->func)(callout->arg1, callout->arg2);
203    }
204    /* each callback is one-shot, so we need to re-arm */
205    CFFileDescriptorEnableCallBacks(callout->u.fdesc,
206				    kCFFileDescriptorReadCallBack);
207    return;
208}
209
210PRIVATE_EXTERN int
211FDCalloutGetFD(FDCalloutRef callout)
212{
213    if (callout->is_socket) {
214	return (CFSocketGetNative(callout->u.socket));
215    }
216    else {
217	return (CFFileDescriptorGetNativeDescriptor(callout->u.fdesc));
218    }
219}
220