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