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