1/*
2 * Copyright (c) 2000-2006 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_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. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <mach/port.h>
30#include <mach/message.h>
31#include <mach/kern_return.h>
32#include <mach/host_priv.h>
33
34#include <kern/kern_types.h>
35#include <kern/kalloc.h>
36#include <kern/host.h>
37#include <kern/ipc_kobject.h>
38
39#include <ipc/ipc_port.h>
40
41#include <UserNotification/UNDTypes.h>
42#include <UserNotification/UNDRequest.h>
43#include <UserNotification/UNDReplyServer.h>
44#include <UserNotification/KUNCUserNotifications.h>
45
46#ifdef KERNEL_CF
47// external
48#include <IOKit/IOCFSerialize.h>
49#include <IOKit/IOCFUnserialize.h>
50#endif
51
52/*
53 * DEFINES AND STRUCTURES
54 */
55
56struct UNDReply {
57	decl_lck_mtx_data(,lock)				/* UNDReply lock */
58	int				userLandNotificationKey;
59	KUNCUserNotificationCallBack 	callback;
60	boolean_t			inprogress;
61	ipc_port_t			self_port;	/* Our port */
62};
63
64#define UNDReply_lock(reply)		lck_mtx_lock(&reply->lock)
65#define UNDReply_unlock(reply)		lck_mtx_lock(&reply->lock)
66
67extern lck_grp_t LockCompatGroup;
68
69/* forward declarations */
70void UNDReply_deallocate(
71	UNDReplyRef		reply);
72
73
74void
75UNDReply_deallocate(
76	UNDReplyRef		reply)
77{
78	ipc_port_t port;
79
80	UNDReply_lock(reply);
81	port = reply->self_port;
82	assert(IP_VALID(port));
83	ipc_kobject_set(port, IKO_NULL, IKOT_NONE);
84	reply->self_port = IP_NULL;
85	UNDReply_unlock(reply);
86
87	ipc_port_dealloc_kernel(port);
88	lck_mtx_destroy(&reply->lock, &LockCompatGroup);
89	kfree(reply, sizeof(struct UNDReply));
90	return;
91}
92
93static UNDServerRef
94UNDServer_reference(void)
95{
96	UNDServerRef UNDServer;
97	kern_return_t kr;
98
99	kr = host_get_user_notification_port(host_priv_self(), &UNDServer);
100	assert(kr == KERN_SUCCESS);
101	return UNDServer;
102}
103
104static void
105UNDServer_deallocate(
106	UNDServerRef	UNDServer)
107{
108	if (IP_VALID(UNDServer))
109		ipc_port_release_send(UNDServer);
110}
111
112/*
113 * UND Mig Callbacks
114*/
115
116kern_return_t
117UNDAlertCompletedWithResult_rpc (
118        UNDReplyRef 		reply,
119        int 			result,
120        xmlData_t		keyRef,		/* raw XML bytes */
121#ifdef KERNEL_CF
122        mach_msg_type_number_t	keyLen)
123#else
124        __unused mach_msg_type_number_t	keyLen)
125#endif
126{
127#ifdef KERNEL_CF
128	CFStringRef		xmlError = NULL;
129	CFDictionaryRef 	dict = NULL;
130#else
131	const void *dict = (const void *)keyRef;
132#endif
133
134	if (reply == UND_REPLY_NULL || !reply->inprogress)
135		return KERN_INVALID_ARGUMENT;
136
137	/*
138	 * JMM - No C vesion of the Unserialize code in-kernel
139	 * and no C type for a CFDictionary either.  For now,
140	 * just pass the raw keyRef through.
141	 */
142#ifdef KERNEL_CF
143	if (keyRef && keyLen) {
144		dict = IOCFUnserialize(keyRef, NULL, NULL, &xmlError);
145	}
146
147	if (xmlError) {
148		CFShow(xmlError);
149		CFRelease(xmlError);
150	}
151#endif /* KERNEL_CF */
152
153	if (reply->callback) {
154		(reply->callback)((int)(KUNCUserNotificationID)reply, result, dict);
155	}
156
157	UNDReply_lock(reply);
158	reply->inprogress = FALSE;
159	reply->userLandNotificationKey = -1;
160	UNDReply_unlock(reply);
161	UNDReply_deallocate(reply);
162	return KERN_SUCCESS;
163}
164
165/*
166 *	Routine: UNDNotificationCreated_rpc
167 *
168 *		Intermediate routine.  Allows the kernel mechanism
169 *		to be informed that the notification request IS
170 *		being processed by the user-level daemon, and how
171 *		to identify that request.
172 */
173kern_return_t
174UNDNotificationCreated_rpc (
175        UNDReplyRef	reply,
176        int		userLandNotificationKey)
177{
178	if (reply == UND_REPLY_NULL)
179		return KERN_INVALID_ARGUMENT;
180
181	UNDReply_lock(reply);
182	if (reply->inprogress || reply->userLandNotificationKey != -1) {
183		UNDReply_unlock(reply);
184		return KERN_INVALID_ARGUMENT;
185	}
186	reply->userLandNotificationKey = userLandNotificationKey;
187	UNDReply_unlock(reply);
188	return KERN_SUCCESS;
189}
190
191/*
192 * KUNC Functions
193*/
194
195
196KUNCUserNotificationID
197KUNCGetNotificationID(void)
198{
199	UNDReplyRef reply;
200
201	reply = (UNDReplyRef) kalloc(sizeof(struct UNDReply));
202	if (reply != UND_REPLY_NULL) {
203		reply->self_port = ipc_port_alloc_kernel();
204		if (reply->self_port == IP_NULL) {
205			kfree(reply, sizeof(struct UNDReply));
206			reply = UND_REPLY_NULL;
207		} else {
208			lck_mtx_init(&reply->lock, &LockCompatGroup, LCK_ATTR_NULL);
209			reply->userLandNotificationKey = -1;
210			reply->inprogress = FALSE;
211			ipc_kobject_set(reply->self_port,
212					(ipc_kobject_t)reply,
213					IKOT_UND_REPLY);
214		}
215	}
216	return (KUNCUserNotificationID) reply;
217}
218
219
220kern_return_t KUNCExecute(char executionPath[1024], int uid, int gid)
221{
222
223	UNDServerRef UNDServer;
224
225	UNDServer = UNDServer_reference();
226	if (IP_VALID(UNDServer)) {
227		kern_return_t kr;
228		kr = UNDExecute_rpc(UNDServer, executionPath, uid, gid);
229		UNDServer_deallocate(UNDServer);
230		return kr;
231	}
232	return MACH_SEND_INVALID_DEST;
233}
234
235kern_return_t KUNCUserNotificationCancel(
236	KUNCUserNotificationID id)
237{
238	UNDReplyRef reply = (UNDReplyRef)id;
239	kern_return_t kr;
240	int ulkey;
241
242	if (reply == UND_REPLY_NULL)
243		return KERN_INVALID_ARGUMENT;
244
245	UNDReply_lock(reply);
246	if (!reply->inprogress) {
247		UNDReply_unlock(reply);
248		return KERN_INVALID_ARGUMENT;
249	}
250
251	reply->inprogress = FALSE;
252	if ((ulkey = reply->userLandNotificationKey) != 0) {
253		UNDServerRef UNDServer;
254
255		reply->userLandNotificationKey = 0;
256		UNDReply_unlock(reply);
257
258		UNDServer = UNDServer_reference();
259		if (IP_VALID(UNDServer)) {
260			kr = UNDCancelNotification_rpc(UNDServer,ulkey);
261			UNDServer_deallocate(UNDServer);
262		} else
263			kr = MACH_SEND_INVALID_DEST;
264	} else {
265		UNDReply_unlock(reply);
266		kr = KERN_SUCCESS;
267	}
268	UNDReply_deallocate(reply);
269	return kr;
270}
271
272kern_return_t
273KUNCUserNotificationDisplayNotice(
274	int		noticeTimeout,
275	unsigned	flags,
276	char		*iconPath,
277	char		*soundPath,
278	char		*localizationPath,
279	char		*alertHeader,
280	char		*alertMessage,
281	char		*defaultButtonTitle)
282{
283	UNDServerRef UNDServer;
284
285	UNDServer = UNDServer_reference();
286	if (IP_VALID(UNDServer)) {
287		kern_return_t kr;
288		kr = UNDDisplayNoticeSimple_rpc(UNDServer,
289					noticeTimeout,
290					flags,
291					iconPath,
292					soundPath,
293					localizationPath,
294					alertHeader,
295					alertMessage,
296					defaultButtonTitle);
297		UNDServer_deallocate(UNDServer);
298		return kr;
299	}
300	return MACH_SEND_INVALID_DEST;
301}
302
303kern_return_t
304KUNCUserNotificationDisplayAlert(
305	int		alertTimeout,
306	unsigned	flags,
307	char		*iconPath,
308	char		*soundPath,
309	char		*localizationPath,
310	char		*alertHeader,
311	char		*alertMessage,
312	char		*defaultButtonTitle,
313	char		*alternateButtonTitle,
314	char		*otherButtonTitle,
315	unsigned	*responseFlags)
316{
317	UNDServerRef	UNDServer;
318
319	UNDServer = UNDServer_reference();
320	if (IP_VALID(UNDServer)) {
321		kern_return_t	kr;
322		kr = UNDDisplayAlertSimple_rpc(UNDServer,
323				       alertTimeout,
324				       flags,
325				       iconPath,
326				       soundPath,
327				       localizationPath,
328				       alertHeader,
329				       alertMessage,
330				       defaultButtonTitle,
331				       alternateButtonTitle,
332				       otherButtonTitle,
333				       responseFlags);
334		UNDServer_deallocate(UNDServer);
335		return kr;
336	}
337	return MACH_SEND_INVALID_DEST;
338}
339
340kern_return_t
341KUNCUserNotificationDisplayFromBundle(
342	KUNCUserNotificationID	     id,
343	char 			     *bundlePath,
344	char			     *fileName,
345	char			     *fileExtension,
346	char			     *messageKey,
347	char			     *tokenString,
348	KUNCUserNotificationCallBack callback,
349	__unused int			contextKey)
350{
351	UNDReplyRef reply = (UNDReplyRef)id;
352	UNDServerRef UNDServer;
353	ipc_port_t reply_port;
354
355	if (reply == UND_REPLY_NULL)
356		return KERN_INVALID_ARGUMENT;
357	UNDReply_lock(reply);
358	if (reply->inprogress == TRUE || reply->userLandNotificationKey != -1) {
359		UNDReply_unlock(reply);
360		return KERN_INVALID_ARGUMENT;
361	}
362	reply->inprogress = TRUE;
363	reply->callback = callback;
364	reply_port = ipc_port_make_send(reply->self_port);
365	UNDReply_unlock(reply);
366
367	UNDServer = UNDServer_reference();
368	if (IP_VALID(UNDServer)) {
369		kern_return_t kr;
370
371		kr = UNDDisplayCustomFromBundle_rpc(UNDServer,
372					    reply_port,
373					    bundlePath,
374					    fileName,
375					    fileExtension,
376					    messageKey,
377					    tokenString);
378		UNDServer_deallocate(UNDServer);
379		return kr;
380	}
381	return MACH_SEND_INVALID_DEST;
382}
383
384/*
385 *	Routine: convert_port_to_UNDReply
386 *
387 *		MIG helper routine to convert from a mach port to a
388 *		UNDReply object.
389 *
390 *	Assumptions:
391 *		Nothing locked.
392 */
393UNDReplyRef
394convert_port_to_UNDReply(
395	ipc_port_t port)
396{
397	if (IP_VALID(port)) {
398		UNDReplyRef reply;
399
400		ip_lock(port);
401		if (!ip_active(port) || (ip_kotype(port) != IKOT_UND_REPLY)) {
402			ip_unlock(port);
403			return UND_REPLY_NULL;
404		}
405		reply = (UNDReplyRef) port->ip_kobject;
406		assert(reply != UND_REPLY_NULL);
407		ip_unlock(port);
408		return reply;
409	}
410	return UND_REPLY_NULL;
411}
412
413/*
414 *      User interface for setting the host UserNotification Daemon port.
415 */
416
417kern_return_t
418host_set_UNDServer(
419        host_priv_t     host_priv,
420        UNDServerRef    server)
421{
422	return (host_set_user_notification_port(host_priv, server));
423}
424
425/*
426 *      User interface for retrieving the UserNotification Daemon port.
427 */
428
429kern_return_t
430host_get_UNDServer(
431	host_priv_t     host_priv,
432	UNDServerRef	*serverp)
433{
434	return (host_get_user_notification_port(host_priv, serverp));
435}
436