1 /*
2 * Copyright (c) 2003-2020 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/mach_types.h>
30 #include <mach/mach_host.h>
31
32 #include <kern/kern_types.h>
33 #include <kern/ipc_kobject.h>
34 #include <kern/host_notify.h>
35
36 #include <kern/queue.h>
37
38 #include "mach/host_notify_reply.h"
39
40 struct host_notify_entry {
41 queue_chain_t entries;
42 ipc_port_t port;
43 };
44 typedef struct host_notify_entry *host_notify_t;
45
46 LCK_GRP_DECLARE(host_notify_lock_grp, "host_notify");
47 LCK_MTX_EARLY_DECLARE(host_notify_lock, &host_notify_lock_grp);
48
49 static ZONE_DECLARE(host_notify_zone, "host_notify",
50 sizeof(struct host_notify_entry), ZC_NONE);
51
52 static queue_head_t host_notify_queue[HOST_NOTIFY_TYPE_MAX + 1];
53
54 static mach_msg_id_t host_notify_replyid[HOST_NOTIFY_TYPE_MAX + 1] =
55 { HOST_CALENDAR_CHANGED_REPLYID,
56 HOST_CALENDAR_SET_REPLYID };
57
58 __startup_func
59 static void
host_notify_init(void)60 host_notify_init(void)
61 {
62 for (int i = 0; i <= HOST_NOTIFY_TYPE_MAX; i++) {
63 queue_init(&host_notify_queue[i]);
64 }
65 }
66 STARTUP(MACH_IPC, STARTUP_RANK_FIRST, host_notify_init);
67
68 kern_return_t
host_request_notification(host_t host,host_flavor_t notify_type,ipc_port_t port)69 host_request_notification(
70 host_t host,
71 host_flavor_t notify_type,
72 ipc_port_t port)
73 {
74 host_notify_t entry;
75 kern_return_t kr;
76
77 if (host == HOST_NULL) {
78 return KERN_INVALID_ARGUMENT;
79 }
80
81 if (!IP_VALID(port)) {
82 return KERN_INVALID_CAPABILITY;
83 }
84
85 if (notify_type > HOST_NOTIFY_TYPE_MAX || notify_type < 0) {
86 return KERN_INVALID_ARGUMENT;
87 }
88
89 entry = zalloc_flags(host_notify_zone, Z_WAITOK | Z_ZERO | Z_NOFAIL);
90 entry->port = port;
91
92 lck_mtx_lock(&host_notify_lock);
93 kr = ipc_kobject_upgrade(port, entry, IKOT_HOST_NOTIFY);
94 if (kr == KERN_SUCCESS) {
95 enqueue_tail(&host_notify_queue[notify_type], (queue_entry_t)entry);
96 }
97 lck_mtx_unlock(&host_notify_lock);
98
99 if (kr != KERN_SUCCESS) {
100 zfree(host_notify_zone, entry);
101 }
102
103 return kr;
104 }
105
106 static void
host_notify_port_destroy(ipc_port_t port)107 host_notify_port_destroy(
108 ipc_port_t port)
109 {
110 host_notify_t entry;
111
112 lck_mtx_lock(&host_notify_lock);
113
114 entry = ipc_kobject_downgrade_host_notify(port);
115 if (entry) {
116 assert(entry->port == port);
117 remqueue((queue_entry_t)entry);
118 }
119
120 lck_mtx_unlock(&host_notify_lock);
121
122 if (entry) {
123 zfree(host_notify_zone, entry);
124 ipc_port_release_sonce(port);
125 }
126 }
127
128 IPC_KOBJECT_DEFINE(IKOT_HOST_NOTIFY,
129 .iko_op_allow_upgrade = true,
130 .iko_op_destroy = host_notify_port_destroy);
131
132 static void
host_notify_all(host_flavor_t notify_type,mach_msg_header_t * msg,mach_msg_size_t msg_size)133 host_notify_all(
134 host_flavor_t notify_type,
135 mach_msg_header_t *msg,
136 mach_msg_size_t msg_size)
137 {
138 queue_t notify_queue = &host_notify_queue[notify_type];
139
140 lck_mtx_lock(&host_notify_lock);
141
142 if (!queue_empty(notify_queue)) {
143 queue_head_t send_queue;
144 host_notify_t entry;
145
146 send_queue = *notify_queue;
147 queue_init(notify_queue);
148
149 send_queue.next->prev = &send_queue;
150 send_queue.prev->next = &send_queue;
151
152 msg->msgh_bits =
153 MACH_MSGH_BITS_SET(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0, 0, 0);
154 msg->msgh_local_port = MACH_PORT_NULL;
155 msg->msgh_voucher_port = MACH_PORT_NULL;
156 msg->msgh_id = host_notify_replyid[notify_type];
157
158 while ((entry = (host_notify_t)dequeue(&send_queue)) != NULL) {
159 ipc_port_t port = entry->port;
160
161 ipc_kobject_downgrade_host_notify(port);
162
163 lck_mtx_unlock(&host_notify_lock);
164
165 zfree(host_notify_zone, entry);
166
167 msg->msgh_remote_port = port;
168
169 (void)mach_msg_send_from_kernel_proper(msg, msg_size);
170
171 lck_mtx_lock(&host_notify_lock);
172 }
173 }
174
175 lck_mtx_unlock(&host_notify_lock);
176 }
177
178 void
host_notify_calendar_change(void)179 host_notify_calendar_change(void)
180 {
181 __Request__host_calendar_changed_t msg;
182
183 host_notify_all(HOST_NOTIFY_CALENDAR_CHANGE, &msg.Head, sizeof(msg));
184 }
185
186 void
host_notify_calendar_set(void)187 host_notify_calendar_set(void)
188 {
189 __Request__host_calendar_set_t msg;
190
191 host_notify_all(HOST_NOTIFY_CALENDAR_SET, &msg.Head, sizeof(msg));
192 }
193