xref: /NextBSD/lib/libxpc/xpc_connection.c (revision 33da5adc555b3bc29986eeadca03829e4ad06b1e)
1 /*
2  * Copyright 2014-2015 iXsystems, Inc.
3  * All rights reserved
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted providing that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
23  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24  * POSSIBILITY OF SUCH DAMAGE.
25  *
26  */
27 
28 #include <errno.h>
29 #include <mach/mach.h>
30 #include <servers/bootstrap.h>
31 #include <xpc/xpc.h>
32 #include <machine/atomic.h>
33 #include <Block.h>
34 #include "xpc_internal.h"
35 
36 #define XPC_CONNECTION_NEXT_ID(conn) (atomic_fetchadd_int(&conn->xc_last_id, 1))
37 
38 static void xpc_connection_recv_message();
39 static void xpc_send(xpc_connection_t xconn, xpc_object_t message, uint64_t id);
40 
41 xpc_connection_t
xpc_connection_create(const char * name,dispatch_queue_t targetq)42 xpc_connection_create(const char *name, dispatch_queue_t targetq)
43 {
44 	kern_return_t kr;
45 	char *qname;
46 	struct xpc_connection *conn;
47 
48 	if ((conn = malloc(sizeof(struct xpc_connection))) == NULL) {
49 		errno = ENOMEM;
50 		return (NULL);
51 	}
52 
53 	memset(conn, 0, sizeof(struct xpc_connection));
54 	conn->xc_last_id = 1;
55 	TAILQ_INIT(&conn->xc_peers);
56 	TAILQ_INIT(&conn->xc_pending);
57 
58 	/* Create send queue */
59 	asprintf(&qname, "com.ixsystems.xpc.connection.sendq.%p", conn);
60 	conn->xc_send_queue = dispatch_queue_create(qname, NULL);
61 
62 	/* Create recv queue */
63 	asprintf(&qname, "com.ixsystems.xpc.connection.recvq.%p", conn);
64 	conn->xc_recv_queue = dispatch_queue_create(qname, NULL);
65 
66 	/* Create target queue */
67 	conn->xc_target_queue = targetq ? targetq : dispatch_get_main_queue();
68 
69 	/* Receive queue is initially suspended */
70 	dispatch_suspend(conn->xc_recv_queue);
71 
72 	/* Create local port */
73 	kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
74 	    &conn->xc_local_port);
75 	if (kr != KERN_SUCCESS) {
76 		errno = EPERM;
77 		return (NULL);
78 	}
79 
80 	kr = mach_port_insert_right(mach_task_self(), conn->xc_local_port,
81 	    conn->xc_local_port, MACH_MSG_TYPE_MAKE_SEND);
82 	if (kr != KERN_SUCCESS) {
83 		errno = EPERM;
84 		return (NULL);
85 	}
86 
87 	return (conn);
88 }
89 
90 xpc_connection_t
xpc_connection_create_mach_service(const char * name,dispatch_queue_t targetq,uint64_t flags)91 xpc_connection_create_mach_service(const char *name, dispatch_queue_t targetq,
92     uint64_t flags)
93 {
94 	kern_return_t kr;
95 	struct xpc_connection *conn;
96 
97 	conn = xpc_connection_create(name, targetq);
98 	if (conn == NULL)
99 		return (NULL);
100 
101 	conn->xc_flags = flags;
102 
103 	if (flags & XPC_CONNECTION_MACH_SERVICE_LISTENER) {
104 		kr = bootstrap_check_in(bootstrap_port, name,
105 		    &conn->xc_local_port);
106 		if (kr != KERN_SUCCESS) {
107 			errno = EBUSY;
108 			free(conn);
109 			return (NULL);
110 		}
111 
112 		return (conn);
113 	}
114 
115 	if (!strcmp(name, "bootstrap")) {
116 		conn->xc_remote_port = bootstrap_port;
117 		return (conn);
118 	}
119 
120 	/* Look up named mach service */
121 	kr = bootstrap_look_up(bootstrap_port, name, &conn->xc_remote_port);
122 	if (kr != KERN_SUCCESS) {
123 		errno = ENOENT;
124 		free(conn);
125 		return (NULL);
126 	}
127 
128 	return (conn);
129 }
130 
131 xpc_connection_t
xpc_connection_create_from_endpoint(xpc_endpoint_t endpoint)132 xpc_connection_create_from_endpoint(xpc_endpoint_t endpoint)
133 {
134 	kern_return_t kr;
135 	struct xpc_connection *conn;
136 
137 	conn = xpc_connection_create("anonymous", NULL);
138 	if (conn == NULL)
139 		return (NULL);
140 
141 	conn->xc_remote_port = (mach_port_t)endpoint;
142 	return (conn);
143 }
144 
145 void
xpc_connection_set_target_queue(xpc_connection_t xconn,dispatch_queue_t targetq)146 xpc_connection_set_target_queue(xpc_connection_t xconn,
147     dispatch_queue_t targetq)
148 {
149 	struct xpc_connection *conn;
150 
151 	debugf("connection=%p", xconn);
152 	conn = xconn;
153 	conn->xc_target_queue = targetq;
154 }
155 
156 void
xpc_connection_set_event_handler(xpc_connection_t xconn,xpc_handler_t handler)157 xpc_connection_set_event_handler(xpc_connection_t xconn,
158     xpc_handler_t handler)
159 {
160 	struct xpc_connection *conn;
161 
162 	debugf("connection=%p", xconn);
163 	conn = xconn;
164 	conn->xc_handler = (xpc_handler_t)Block_copy(handler);
165 }
166 
167 void
xpc_connection_suspend(xpc_connection_t xconn)168 xpc_connection_suspend(xpc_connection_t xconn)
169 {
170 	struct xpc_connection *conn;
171 
172 	conn = xconn;
173 	dispatch_suspend(conn->xc_recv_source);
174 }
175 
176 void
xpc_connection_resume(xpc_connection_t xconn)177 xpc_connection_resume(xpc_connection_t xconn)
178 {
179 	struct xpc_connection *conn;
180 
181 	debugf("connection=%p", xconn);
182 	conn = xconn;
183 
184 	/* Create dispatch source for top-level connection */
185 	if (conn->xc_parent == NULL) {
186 		conn->xc_recv_source = dispatch_source_create(
187 		    DISPATCH_SOURCE_TYPE_MACH_RECV, conn->xc_local_port, 0,
188 		    conn->xc_recv_queue);
189 		dispatch_set_context(conn->xc_recv_source, conn);
190 		dispatch_source_set_event_handler_f(conn->xc_recv_source,
191 		    xpc_connection_recv_message);
192 		dispatch_resume(conn->xc_recv_source);
193 	}
194 
195 	dispatch_resume(conn->xc_recv_queue);
196 }
197 
198 void
xpc_connection_send_message(xpc_connection_t xconn,xpc_object_t message)199 xpc_connection_send_message(xpc_connection_t xconn,
200     xpc_object_t message)
201 {
202 	struct xpc_connection *conn;
203 	uint64_t id;
204 
205 	conn = xconn;
206 	id = xpc_dictionary_get_uint64(message, XPC_SEQID);
207 
208 	if (id == 0)
209 		id = XPC_CONNECTION_NEXT_ID(conn);
210 
211 	dispatch_async(conn->xc_send_queue, ^{
212 		xpc_send(conn, message, id);
213 	});
214 }
215 
216 void
xpc_connection_send_message_with_reply(xpc_connection_t xconn,xpc_object_t message,dispatch_queue_t targetq,xpc_handler_t handler)217 xpc_connection_send_message_with_reply(xpc_connection_t xconn,
218     xpc_object_t message, dispatch_queue_t targetq, xpc_handler_t handler)
219 {
220 	struct xpc_connection *conn;
221 	struct xpc_pending_call *call;
222 
223 	conn = xconn;
224 	call = malloc(sizeof(struct xpc_pending_call));
225 	call->xp_id = XPC_CONNECTION_NEXT_ID(conn);
226 	call->xp_handler = handler;
227 	call->xp_queue = targetq;
228 	TAILQ_INSERT_TAIL(&conn->xc_pending, call, xp_link);
229 
230 	dispatch_async(conn->xc_send_queue, ^{
231 		xpc_send(conn, message, call->xp_id);
232 	});
233 
234 }
235 
236 xpc_object_t
xpc_connection_send_message_with_reply_sync(xpc_connection_t conn,xpc_object_t message)237 xpc_connection_send_message_with_reply_sync(xpc_connection_t conn,
238     xpc_object_t message)
239 {
240 	__block xpc_object_t result;
241 	dispatch_semaphore_t sem = dispatch_semaphore_create(0);
242 
243 	xpc_connection_send_message_with_reply(conn, message, NULL,
244 	    ^(xpc_object_t o) {
245 		result = o;
246 		dispatch_semaphore_signal(sem);
247 	});
248 
249 	dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
250 	return (result);
251 }
252 
253 void
xpc_connection_send_barrier(xpc_connection_t xconn,dispatch_block_t barrier)254 xpc_connection_send_barrier(xpc_connection_t xconn, dispatch_block_t barrier)
255 {
256 	struct xpc_connection *conn;
257 
258 	conn = xconn;
259 	dispatch_sync(conn->xc_send_queue, barrier);
260 }
261 
262 void
xpc_connection_cancel(xpc_connection_t connection)263 xpc_connection_cancel(xpc_connection_t connection)
264 {
265 
266 }
267 
268 const char *
xpc_connection_get_name(xpc_connection_t connection)269 xpc_connection_get_name(xpc_connection_t connection)
270 {
271 
272 	return ("unknown"); /* ??? */
273 }
274 
275 uid_t
xpc_connection_get_euid(xpc_connection_t xconn)276 xpc_connection_get_euid(xpc_connection_t xconn)
277 {
278 	struct xpc_connection *conn;
279 
280 	conn = xconn;
281 	return (conn->xc_remote_euid);
282 }
283 
284 gid_t
xpc_connection_get_guid(xpc_connection_t xconn)285 xpc_connection_get_guid(xpc_connection_t xconn)
286 {
287 	struct xpc_connection *conn;
288 
289 	conn = xconn;
290 	return (conn->xc_remote_guid);
291 }
292 
293 pid_t
xpc_connection_get_pid(xpc_connection_t xconn)294 xpc_connection_get_pid(xpc_connection_t xconn)
295 {
296 	struct xpc_connection *conn;
297 
298 	conn = xconn;
299 	return (conn->xc_remote_pid);
300 }
301 
302 au_asid_t
xpc_connection_get_asid(xpc_connection_t xconn)303 xpc_connection_get_asid(xpc_connection_t xconn)
304 {
305 	struct xpc_connection *conn;
306 
307 	conn = xconn;
308 	return (conn->xc_remote_asid);
309 }
310 
311 void
xpc_connection_set_context(xpc_connection_t xconn,void * ctx)312 xpc_connection_set_context(xpc_connection_t xconn, void *ctx)
313 {
314 	struct xpc_connection *conn;
315 
316 	conn = xconn;
317 	conn->xc_context = ctx;
318 }
319 
320 void *
xpc_connection_get_context(xpc_connection_t xconn)321 xpc_connection_get_context(xpc_connection_t xconn)
322 {
323 	struct xpc_connection *conn;
324 
325 	conn = xconn;
326 	return (conn->xc_context);
327 }
328 
329 void
xpc_connection_set_finalizer_f(xpc_connection_t connection,xpc_finalizer_t finalizer)330 xpc_connection_set_finalizer_f(xpc_connection_t connection,
331     xpc_finalizer_t finalizer)
332 {
333 
334 }
335 
336 xpc_endpoint_t
xpc_endpoint_create(xpc_connection_t connection)337 xpc_endpoint_create(xpc_connection_t connection)
338 {
339 
340 }
341 
342 void
xpc_main(xpc_connection_handler_t handler)343 xpc_main(xpc_connection_handler_t handler)
344 {
345 
346 	dispatch_main();
347 }
348 
349 void
xpc_transaction_begin(void)350 xpc_transaction_begin(void)
351 {
352 
353 }
354 
355 void
xpc_transaction_end(void)356 xpc_transaction_end(void)
357 {
358 
359 }
360 
361 static void
xpc_send(xpc_connection_t xconn,xpc_object_t message,uint64_t id)362 xpc_send(xpc_connection_t xconn, xpc_object_t message, uint64_t id)
363 {
364 	struct xpc_connection *conn;
365 	kern_return_t kr;
366 
367 	debugf("connection=%p, message=%p, id=%d", xconn, message, id);
368 
369 	conn = xconn;
370 	kr = xpc_pipe_send(message, conn->xc_remote_port,
371 	    conn->xc_local_port, id);
372 
373 	if (kr != KERN_SUCCESS)
374 		debugf("send failed, kr=%d", kr);
375 }
376 
377 static void
xpc_connection_set_credentials(struct xpc_connection * conn,audit_token_t * tok)378 xpc_connection_set_credentials(struct xpc_connection *conn, audit_token_t *tok)
379 {
380 	uid_t uid;
381 	gid_t gid;
382 	pid_t pid;
383 	au_asid_t asid;
384 
385 	if (tok == NULL)
386 		return;
387 
388 	audit_token_to_au32(*tok, NULL, &uid, &gid, NULL, NULL, &pid, &asid,
389 	    NULL);
390 
391 	conn->xc_remote_euid = uid;
392 	conn->xc_remote_guid = gid;
393 	conn->xc_remote_pid = pid;
394 	conn->xc_remote_asid = asid;
395 }
396 
397 static void
xpc_connection_recv_message(void * context)398 xpc_connection_recv_message(void *context)
399 {
400 	struct xpc_pending_call *call;
401 	struct xpc_connection *conn, *peer;
402 	xpc_object_t result;
403 	mach_port_t remote;
404 	kern_return_t kr;
405 	uint64_t id;
406 
407 	debugf("connection=%p", context);
408 
409 	conn = context;
410 	kr = xpc_pipe_receive(conn->xc_local_port, &remote, &result, &id);
411 	if (kr != KERN_SUCCESS)
412 		return;
413 
414 	debugf("message=%p, id=%d, remote=<%d>", result, id, remote);
415 
416 	if (conn->xc_flags & XPC_CONNECTION_MACH_SERVICE_LISTENER) {
417 		TAILQ_FOREACH(peer, &conn->xc_peers, xc_link) {
418 			if (remote == peer->xc_remote_port) {
419 				dispatch_async(peer->xc_target_queue, ^{
420 					peer->xc_handler(result);
421 				});
422 				return;
423 			}
424 		}
425 
426 		debugf("new peer on port <%u>", remote);
427 
428 		/* New peer */
429 		peer = xpc_connection_create(NULL, NULL);
430 		peer->xc_parent = conn;
431 		peer->xc_remote_port = remote;
432 		xpc_connection_set_credentials(peer,
433 		    ((struct xpc_object *)result)->xo_audit_token);
434 
435 		TAILQ_INSERT_TAIL(&conn->xc_peers, peer, xc_link);
436 
437 		dispatch_async(conn->xc_target_queue, ^{
438 			conn->xc_handler(peer);
439 		});
440 
441 		dispatch_async(peer->xc_target_queue, ^{
442 			peer->xc_handler(result);
443 		});
444 
445 	} else {
446 		xpc_connection_set_credentials(conn,
447 		    ((struct xpc_object *)result)->xo_audit_token);
448 
449 		TAILQ_FOREACH(call, &conn->xc_pending, xp_link) {
450 			if (call->xp_id == id) {
451 				dispatch_async(conn->xc_target_queue, ^{
452 					call->xp_handler(result);
453 					TAILQ_REMOVE(&conn->xc_pending, call,
454 					    xp_link);
455 					free(call);
456 				});
457 				return;
458 			}
459 		}
460 
461 		if (conn->xc_handler) {
462 			dispatch_async(conn->xc_target_queue, ^{
463 			    conn->xc_handler(result);
464 			});
465 		}
466 	}
467 }
468