1/*
2 * Copyright (c) 2009-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/*
25 * InterestNotification.c
26 * - register for IOKit interest notification on a particular BSD name
27 */
28
29/*
30 * Modification History
31 *
32 * March 10, 2009	Dieter Siegmund (dieter@apple)
33 * - created
34 */
35
36#include <IOKit/IOKitLib.h>
37#include <IOKit/IOMessage.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <unistd.h>
41#include <string.h>
42#include "EAPLog.h"
43#include "InterestNotification.h"
44
45struct InterestNotification {
46    IONotificationPortRef 		notify;
47    io_object_t				if_change;
48    InterestNotificationCallbackRef	callback;
49    const void *			arg;
50};
51
52static void
53InterestNotificationHandleMessage(void * refcon, io_service_t service,
54				  natural_t message_type,
55				  void * messageArgument)
56{
57    InterestNotificationRef	interest_p = (InterestNotificationRef)refcon;
58
59    if (message_type == kIOMessageServicePropertyChange) {
60	InterestNotificationCallbackRef	callback = interest_p->callback;
61	const void *			arg = interest_p->arg;
62
63	(*callback)(interest_p, arg);
64    }
65    return;
66}
67
68static void
69InterestNotificationInit(InterestNotificationRef interest_p)
70{
71    bzero(interest_p, sizeof(*interest_p));
72    return;
73}
74
75void
76InterestNotificationRelease(InterestNotificationRef interest_p)
77{
78    if (interest_p->if_change != MACH_PORT_NULL) {
79	IOObjectRelease(interest_p->if_change);
80    }
81    if (interest_p->notify != MACH_PORT_NULL) {
82	CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
83			      IONotificationPortGetRunLoopSource(interest_p->notify),
84			      kCFRunLoopDefaultMode);
85	IONotificationPortDestroy(interest_p->notify);
86    }
87    InterestNotificationInit(interest_p);
88    free(interest_p);
89    return;
90}
91
92static boolean_t
93InterestNotificationStart(InterestNotificationRef interest_p,
94			  const char * if_name,
95			  InterestNotificationCallbackRef callback,
96			  const void * arg)
97{
98    io_object_t			if_change = MACH_PORT_NULL;
99    kern_return_t		kr;
100    io_iterator_t		list = MACH_PORT_NULL;
101    CFDictionaryRef		matching;
102    IONotificationPortRef 	notify = NULL;
103    io_object_t			obj = MACH_PORT_NULL;
104    boolean_t			ok = FALSE;
105
106    matching = IOBSDNameMatching(kIOMasterPortDefault, 0, if_name);
107    kr = IOServiceGetMatchingServices(kIOMasterPortDefault,
108				      matching, &list);
109    if (kr != KERN_SUCCESS) {
110	EAPLOG_FL(LOG_NOTICE, "No such interface %s\n", if_name);
111	goto done;
112    }
113    notify = IONotificationPortCreate(kIOMasterPortDefault);
114    if (notify == NULL) {
115	EAPLOG_FL(LOG_NOTICE, "IONotificationPortCreate failed\n");
116	goto done;
117    }
118    obj = IOIteratorNext(list);
119    if (obj == MACH_PORT_NULL) {
120	EAPLOG_FL(LOG_NOTICE, "IOIteratorNext no object\n");
121	goto done;
122    }
123    kr = IOServiceAddInterestNotification(notify,
124					  obj,
125					  kIOGeneralInterest,
126					  &InterestNotificationHandleMessage,
127					  (void *)interest_p,
128					  &if_change);
129    if (kr != KERN_SUCCESS) {
130	EAPLOG_FL(LOG_NOTICE, "IOServiceAddInterestNotification failed, 0x%x\n",
131		  kr);
132	goto done;
133    }
134    CFRunLoopAddSource(CFRunLoopGetCurrent(),
135		       IONotificationPortGetRunLoopSource(notify),
136		       kCFRunLoopDefaultMode);
137    interest_p->notify = notify;
138    interest_p->if_change = if_change;
139    interest_p->callback = callback;
140    interest_p->arg = arg;
141    ok = TRUE;
142
143 done:
144    if (list != MACH_PORT_NULL) {
145	IOObjectRelease(list);
146    }
147    if (obj != MACH_PORT_NULL) {
148	IOObjectRelease(obj);
149    }
150    if (ok == FALSE) {
151	if (notify != NULL) {
152	    IONotificationPortDestroy(notify);
153	}
154	if (if_change != MACH_PORT_NULL) {
155	    IOObjectRelease(if_change);
156	}
157    }
158    return (ok);
159}
160
161InterestNotificationRef
162InterestNotificationCreate(const char * if_name,
163			   InterestNotificationCallbackRef callback,
164			   const void * arg)
165{
166    InterestNotificationRef	interest_p;
167
168    if (callback == NULL) {
169	return (NULL);
170    }
171    interest_p = (InterestNotificationRef)malloc(sizeof(*interest_p));
172    InterestNotificationInit(interest_p);
173    if (InterestNotificationStart(interest_p, if_name, callback, arg)
174	== FALSE) {
175	free(interest_p);
176	return (NULL);
177    }
178    return (interest_p);
179}
180
181#ifdef TEST_INTERESTNOTIFICATION
182
183static void
184change_callback(InterestNotificationRef interest_p,
185		const void * arg)
186{
187    printf("Change, all done\n");
188    InterestNotificationRelease(interest_p);
189    return;
190}
191
192int
193main(int argc, char * argv[])
194{
195    InterestNotificationRef	interest_p;
196
197    if (argc < 2) {
198	fprintf(stderr, "usage: ioregwatch <ifname>\n");
199	exit(1);
200    }
201    interest_p = InterestNotificationCreate(argv[1],
202					    change_callback, NULL);
203    if (interest_p == NULL) {
204	fprintf(stderr, "Create failed\n");
205	exit(2);
206    }
207    CFRunLoopRun();
208    exit(0);
209    return (0);
210}
211
212#endif /* TEST_INTERESTNOTIFICATION */
213