1dbed73cbSSangeeta Misra /*
2dbed73cbSSangeeta Misra  * CDDL HEADER START
3dbed73cbSSangeeta Misra  *
4dbed73cbSSangeeta Misra  * The contents of this file are subject to the terms of the
5dbed73cbSSangeeta Misra  * Common Development and Distribution License (the "License").
6dbed73cbSSangeeta Misra  * You may not use this file except in compliance with the License.
7dbed73cbSSangeeta Misra  *
8dbed73cbSSangeeta Misra  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9dbed73cbSSangeeta Misra  * or http://www.opensolaris.org/os/licensing.
10dbed73cbSSangeeta Misra  * See the License for the specific language governing permissions
11dbed73cbSSangeeta Misra  * and limitations under the License.
12dbed73cbSSangeeta Misra  *
13dbed73cbSSangeeta Misra  * When distributing Covered Code, include this CDDL HEADER in each
14dbed73cbSSangeeta Misra  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15dbed73cbSSangeeta Misra  * If applicable, add the following below this CDDL HEADER, with the
16dbed73cbSSangeeta Misra  * fields enclosed by brackets "[]" replaced with your own identifying
17dbed73cbSSangeeta Misra  * information: Portions Copyright [yyyy] [name of copyright owner]
18dbed73cbSSangeeta Misra  *
19dbed73cbSSangeeta Misra  * CDDL HEADER END
20dbed73cbSSangeeta Misra  */
21dbed73cbSSangeeta Misra 
22dbed73cbSSangeeta Misra /*
23dbed73cbSSangeeta Misra  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24dbed73cbSSangeeta Misra  * Use is subject to license terms.
25*1b58875aSJohn Levon  *
26*1b58875aSJohn Levon  * Copyright (c) 2018, Joyent, Inc.
27dbed73cbSSangeeta Misra  */
28dbed73cbSSangeeta Misra 
29dbed73cbSSangeeta Misra #include <stdlib.h>
30dbed73cbSSangeeta Misra #include <strings.h>
31dbed73cbSSangeeta Misra #include <unistd.h>
32dbed73cbSSangeeta Misra #include <stddef.h>
33dbed73cbSSangeeta Misra #include <assert.h>
34dbed73cbSSangeeta Misra #include <sys/types.h>
35dbed73cbSSangeeta Misra #include <sys/socket.h>
36dbed73cbSSangeeta Misra #include <thread.h>
37dbed73cbSSangeeta Misra #include <synch.h>
38dbed73cbSSangeeta Misra #include <libilb_impl.h>
39dbed73cbSSangeeta Misra #include <libilb.h>
40dbed73cbSSangeeta Misra 
41dbed73cbSSangeeta Misra /* Assertion: the calling thread has a hold on the handle */
42dbed73cbSSangeeta Misra static void
i_ilb_socket_set_err(ilb_handle_t h,ilb_status_t err)43dbed73cbSSangeeta Misra i_ilb_socket_set_err(ilb_handle_t h, ilb_status_t err)
44dbed73cbSSangeeta Misra {
45dbed73cbSSangeeta Misra 	ilb_handle_impl_t	*hi = (ilb_handle_impl_t *)h;
46dbed73cbSSangeeta Misra 
47dbed73cbSSangeeta Misra 	if (h == ILB_INVALID_HANDLE)
48dbed73cbSSangeeta Misra 		return;
49dbed73cbSSangeeta Misra 	hi->h_valid = B_FALSE;
50dbed73cbSSangeeta Misra 	hi->h_error = err;
51dbed73cbSSangeeta Misra }
52dbed73cbSSangeeta Misra 
53dbed73cbSSangeeta Misra ilb_status_t
ilb_open(ilb_handle_t * hp)54dbed73cbSSangeeta Misra ilb_open(ilb_handle_t *hp)
55dbed73cbSSangeeta Misra {
56dbed73cbSSangeeta Misra 	ilb_handle_impl_t	*hi = NULL;
57dbed73cbSSangeeta Misra 	int			s = -1;
58dbed73cbSSangeeta Misra 	struct sockaddr_un sa = {AF_UNIX, SOCKET_PATH};
59dbed73cbSSangeeta Misra 	ilb_status_t		rc = ILB_STATUS_OK;
60dbed73cbSSangeeta Misra 	int			sobufsz;
61dbed73cbSSangeeta Misra 
62dbed73cbSSangeeta Misra 	if (hp == NULL)
63dbed73cbSSangeeta Misra 		return (ILB_STATUS_EINVAL);
64dbed73cbSSangeeta Misra 
65*1b58875aSJohn Levon 	hi = calloc(1, sizeof (*hi));
66dbed73cbSSangeeta Misra 	if (hi == NULL)
67dbed73cbSSangeeta Misra 		return (ILB_STATUS_ENOMEM);
68dbed73cbSSangeeta Misra 
69dbed73cbSSangeeta Misra 	if (cond_init(&hi->h_cv, USYNC_THREAD, NULL) != 0) {
70dbed73cbSSangeeta Misra 		rc = ILB_STATUS_INTERNAL;
71dbed73cbSSangeeta Misra 		goto out;
72dbed73cbSSangeeta Misra 	}
73dbed73cbSSangeeta Misra 
74dbed73cbSSangeeta Misra 	if (mutex_init(&hi->h_lock, USYNC_THREAD | LOCK_ERRORCHECK, NULL)
75dbed73cbSSangeeta Misra 	    != 0) {
76dbed73cbSSangeeta Misra 		rc = ILB_STATUS_INTERNAL;
77dbed73cbSSangeeta Misra 		goto out;
78dbed73cbSSangeeta Misra 	}
79dbed73cbSSangeeta Misra 
80dbed73cbSSangeeta Misra 	hi->h_busy = B_FALSE;
81dbed73cbSSangeeta Misra 
82dbed73cbSSangeeta Misra 	if ((s = socket(PF_UNIX, SOCK_SEQPACKET, 0)) == -1 ||
83dbed73cbSSangeeta Misra 	    connect(s, (struct sockaddr *)&sa, sizeof (sa.sun_path))
84dbed73cbSSangeeta Misra 	    == -1) {
85dbed73cbSSangeeta Misra 		rc = ILB_STATUS_SOCKET;
86dbed73cbSSangeeta Misra 		goto out;
87dbed73cbSSangeeta Misra 	}
88dbed73cbSSangeeta Misra 
89dbed73cbSSangeeta Misra 	/* The socket buffer must be at least the max size of a message */
90dbed73cbSSangeeta Misra 	sobufsz = ILBD_MSG_SIZE;
91dbed73cbSSangeeta Misra 	if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sobufsz,
92dbed73cbSSangeeta Misra 	    sizeof (sobufsz)) != 0) {
93dbed73cbSSangeeta Misra 		rc = ILB_STATUS_SOCKET;
94dbed73cbSSangeeta Misra 		(void) close(s);
95dbed73cbSSangeeta Misra 		goto out;
96dbed73cbSSangeeta Misra 	}
97dbed73cbSSangeeta Misra 	if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &sobufsz,
98dbed73cbSSangeeta Misra 	    sizeof (sobufsz)) != 0) {
99dbed73cbSSangeeta Misra 		rc = ILB_STATUS_SOCKET;
100dbed73cbSSangeeta Misra 		(void) close(s);
101dbed73cbSSangeeta Misra 		goto out;
102dbed73cbSSangeeta Misra 	}
103dbed73cbSSangeeta Misra 
104dbed73cbSSangeeta Misra 	hi->h_socket = s;
105dbed73cbSSangeeta Misra 	hi->h_valid = B_TRUE;
106dbed73cbSSangeeta Misra 
107dbed73cbSSangeeta Misra out:
108dbed73cbSSangeeta Misra 	if (rc != ILB_STATUS_OK && s != -1)
109dbed73cbSSangeeta Misra 		(void) close(s);
110dbed73cbSSangeeta Misra 
111dbed73cbSSangeeta Misra 	if (rc == ILB_STATUS_OK) {
112dbed73cbSSangeeta Misra 		*hp = (ilb_handle_t)hi;
113dbed73cbSSangeeta Misra 	} else {
114dbed73cbSSangeeta Misra 		free(hi);
115dbed73cbSSangeeta Misra 		*hp = ILB_INVALID_HANDLE;
116dbed73cbSSangeeta Misra 	}
117dbed73cbSSangeeta Misra 	return (rc);
118dbed73cbSSangeeta Misra }
119dbed73cbSSangeeta Misra 
120dbed73cbSSangeeta Misra ilb_status_t
ilb_close(ilb_handle_t h)121dbed73cbSSangeeta Misra ilb_close(ilb_handle_t h)
122dbed73cbSSangeeta Misra {
123dbed73cbSSangeeta Misra 	ilb_handle_impl_t	*hi = (ilb_handle_impl_t *)h;
124dbed73cbSSangeeta Misra 
125dbed73cbSSangeeta Misra 	if (h == ILB_INVALID_HANDLE)
126dbed73cbSSangeeta Misra 		return (ILB_STATUS_EINVAL);
127dbed73cbSSangeeta Misra 
128dbed73cbSSangeeta Misra 	if (mutex_lock(&hi->h_lock) != 0)
129dbed73cbSSangeeta Misra 		return (ILB_STATUS_INTERNAL);
130dbed73cbSSangeeta Misra 
131dbed73cbSSangeeta Misra 	/* Somebody has done a close, no need to do anything. */
132dbed73cbSSangeeta Misra 	if (hi->h_closing) {
133dbed73cbSSangeeta Misra 		return (ILB_STATUS_OK);
134dbed73cbSSangeeta Misra 	} else {
135dbed73cbSSangeeta Misra 		hi->h_closing = B_TRUE;
136dbed73cbSSangeeta Misra 		hi->h_error = ILB_STATUS_HANDLE_CLOSING;
137dbed73cbSSangeeta Misra 	}
138dbed73cbSSangeeta Misra 
139dbed73cbSSangeeta Misra 	/* Wait until there is nobody waiting. */
140dbed73cbSSangeeta Misra 	while (hi->h_waiter > 0) {
141dbed73cbSSangeeta Misra 		if (cond_wait(&hi->h_cv, &hi->h_lock) != 0) {
142dbed73cbSSangeeta Misra 			(void) mutex_unlock(&hi->h_lock);
143dbed73cbSSangeeta Misra 			return (ILB_STATUS_INTERNAL);
144dbed73cbSSangeeta Misra 		}
145dbed73cbSSangeeta Misra 	}
146dbed73cbSSangeeta Misra 	/* No one is waiting, proceed to free the handle. */
147dbed73cbSSangeeta Misra 
148dbed73cbSSangeeta Misra 	(void) close(hi->h_socket);
149dbed73cbSSangeeta Misra 	(void) mutex_destroy(&hi->h_lock);
150dbed73cbSSangeeta Misra 	(void) cond_destroy(&hi->h_cv);
151dbed73cbSSangeeta Misra 	free(hi);
152dbed73cbSSangeeta Misra 	return (ILB_STATUS_OK);
153dbed73cbSSangeeta Misra }
154dbed73cbSSangeeta Misra 
155dbed73cbSSangeeta Misra /*
156dbed73cbSSangeeta Misra  * Unified routine to communicate with ilbd.
157dbed73cbSSangeeta Misra  *
158dbed73cbSSangeeta Misra  * If ic is non-NULL, it means that the caller wants to send something
159dbed73cbSSangeeta Misra  * to ilbd and expects a reply.  If ic is NULL, it means that the caller
160dbed73cbSSangeeta Misra  * only expects to receive from ilbd.
161dbed73cbSSangeeta Misra  *
162dbed73cbSSangeeta Misra  * The rbuf is the buffer supplied by the caller for receiving.  If it
163dbed73cbSSangeeta Misra  * is NULL, it means that there is no reply expected.
164dbed73cbSSangeeta Misra  *
165dbed73cbSSangeeta Misra  * This function will not close() the socket to kernel unless there is
166dbed73cbSSangeeta Misra  * an error.  If the transaction only consists of one exchange, the caller
167dbed73cbSSangeeta Misra  * can use i_ilb_close_comm() to close() the socket when done.
168dbed73cbSSangeeta Misra  */
169dbed73cbSSangeeta Misra ilb_status_t
i_ilb_do_comm(ilb_handle_t h,ilb_comm_t * ic,size_t ic_sz,ilb_comm_t * rbuf,size_t * rbufsz)170dbed73cbSSangeeta Misra i_ilb_do_comm(ilb_handle_t h, ilb_comm_t *ic, size_t ic_sz, ilb_comm_t *rbuf,
171dbed73cbSSangeeta Misra     size_t *rbufsz)
172dbed73cbSSangeeta Misra {
173dbed73cbSSangeeta Misra 	ilb_status_t		rc = ILB_STATUS_OK;
174dbed73cbSSangeeta Misra 	int			r, s;
175dbed73cbSSangeeta Misra 	ilb_handle_impl_t	*hi = (ilb_handle_impl_t *)h;
176dbed73cbSSangeeta Misra 
177dbed73cbSSangeeta Misra 	assert(rbuf != NULL);
178dbed73cbSSangeeta Misra 	if (h == ILB_INVALID_HANDLE)
179dbed73cbSSangeeta Misra 		return (ILB_STATUS_EINVAL);
180dbed73cbSSangeeta Misra 
181dbed73cbSSangeeta Misra 	if (mutex_lock(&hi->h_lock) != 0)
182dbed73cbSSangeeta Misra 		return (ILB_STATUS_INTERNAL);
183dbed73cbSSangeeta Misra 
184dbed73cbSSangeeta Misra 	hi->h_waiter++;
185dbed73cbSSangeeta Misra 	while (hi->h_busy) {
186dbed73cbSSangeeta Misra 		if (cond_wait(&hi->h_cv, &hi->h_lock) != 0) {
187dbed73cbSSangeeta Misra 			hi->h_waiter--;
188dbed73cbSSangeeta Misra 			(void) cond_signal(&hi->h_cv);
189dbed73cbSSangeeta Misra 			(void) mutex_unlock(&hi->h_lock);
190dbed73cbSSangeeta Misra 			return (ILB_STATUS_INTERNAL);
191dbed73cbSSangeeta Misra 		}
192dbed73cbSSangeeta Misra 	}
193dbed73cbSSangeeta Misra 
194dbed73cbSSangeeta Misra 	if (!hi->h_valid || hi->h_closing) {
195dbed73cbSSangeeta Misra 		hi->h_waiter--;
196dbed73cbSSangeeta Misra 		(void) cond_signal(&hi->h_cv);
197dbed73cbSSangeeta Misra 		(void) mutex_unlock(&hi->h_lock);
198dbed73cbSSangeeta Misra 		return (hi->h_error);
199dbed73cbSSangeeta Misra 	}
200dbed73cbSSangeeta Misra 
201dbed73cbSSangeeta Misra 	hi->h_busy = B_TRUE;
202dbed73cbSSangeeta Misra 	(void) mutex_unlock(&hi->h_lock);
203dbed73cbSSangeeta Misra 
204dbed73cbSSangeeta Misra 	s = hi->h_socket;
205dbed73cbSSangeeta Misra 
206dbed73cbSSangeeta Misra 	r = send(s, ic, ic_sz, 0);
207dbed73cbSSangeeta Misra 	if (r < ic_sz) {
208dbed73cbSSangeeta Misra 		rc = ILB_STATUS_WRITE;
209dbed73cbSSangeeta Misra 		goto socket_error;
210dbed73cbSSangeeta Misra 	}
211dbed73cbSSangeeta Misra 	rc = ILB_STATUS_OK;
212dbed73cbSSangeeta Misra 
213dbed73cbSSangeeta Misra 	if ((r = recv(s, rbuf, *rbufsz, 0)) <= 0) {
214dbed73cbSSangeeta Misra 		rc = ILB_STATUS_READ;
215dbed73cbSSangeeta Misra 	} else {
216dbed73cbSSangeeta Misra 		*rbufsz = r;
217dbed73cbSSangeeta Misra 		goto out;
218dbed73cbSSangeeta Misra 	}
219dbed73cbSSangeeta Misra 
220dbed73cbSSangeeta Misra socket_error:
221dbed73cbSSangeeta Misra 	i_ilb_socket_set_err(h, rc);
222dbed73cbSSangeeta Misra 
223dbed73cbSSangeeta Misra out:
224dbed73cbSSangeeta Misra 	(void) mutex_lock(&hi->h_lock);
225dbed73cbSSangeeta Misra 	hi->h_busy = B_FALSE;
226dbed73cbSSangeeta Misra 	hi->h_waiter--;
227dbed73cbSSangeeta Misra 	(void) cond_signal(&hi->h_cv);
228dbed73cbSSangeeta Misra 	(void) mutex_unlock(&hi->h_lock);
229dbed73cbSSangeeta Misra 
230dbed73cbSSangeeta Misra 	return (rc);
231dbed73cbSSangeeta Misra }
232dbed73cbSSangeeta Misra 
233dbed73cbSSangeeta Misra void
i_ilb_close_comm(ilb_handle_t h)234dbed73cbSSangeeta Misra i_ilb_close_comm(ilb_handle_t h)
235dbed73cbSSangeeta Misra {
236dbed73cbSSangeeta Misra 	(void) ilb_close(h);
237dbed73cbSSangeeta Misra }
238