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
27 #include <stdlib.h>
28 #include <strings.h>
29 #include <unistd.h>
30 #include <stddef.h>
31 #include <assert.h>
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <thread.h>
35 #include <synch.h>
36 #include <libilb_impl.h>
37 #include <libilb.h>
38
39 /* Assertion: the calling thread has a hold on the handle */
40 static void
i_ilb_socket_set_err(ilb_handle_t h,ilb_status_t err)41 i_ilb_socket_set_err(ilb_handle_t h, ilb_status_t err)
42 {
43 ilb_handle_impl_t *hi = (ilb_handle_impl_t *)h;
44
45 if (h == ILB_INVALID_HANDLE)
46 return;
47 hi->h_valid = B_FALSE;
48 hi->h_error = err;
49 }
50
51 ilb_status_t
ilb_open(ilb_handle_t * hp)52 ilb_open(ilb_handle_t *hp)
53 {
54 ilb_handle_impl_t *hi = NULL;
55 int s = -1;
56 struct sockaddr_un sa = {AF_UNIX, SOCKET_PATH};
57 ilb_status_t rc = ILB_STATUS_OK;
58 int sobufsz;
59
60 if (hp == NULL)
61 return (ILB_STATUS_EINVAL);
62
63 hi = calloc(sizeof (*hi), 1);
64 if (hi == NULL)
65 return (ILB_STATUS_ENOMEM);
66
67 if (cond_init(&hi->h_cv, USYNC_THREAD, NULL) != 0) {
68 rc = ILB_STATUS_INTERNAL;
69 goto out;
70 }
71
72 if (mutex_init(&hi->h_lock, USYNC_THREAD | LOCK_ERRORCHECK, NULL)
73 != 0) {
74 rc = ILB_STATUS_INTERNAL;
75 goto out;
76 }
77
78 hi->h_busy = B_FALSE;
79
80 if ((s = socket(PF_UNIX, SOCK_SEQPACKET, 0)) == -1 ||
81 connect(s, (struct sockaddr *)&sa, sizeof (sa.sun_path))
82 == -1) {
83 rc = ILB_STATUS_SOCKET;
84 goto out;
85 }
86
87 /* The socket buffer must be at least the max size of a message */
88 sobufsz = ILBD_MSG_SIZE;
89 if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sobufsz,
90 sizeof (sobufsz)) != 0) {
91 rc = ILB_STATUS_SOCKET;
92 (void) close(s);
93 goto out;
94 }
95 if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &sobufsz,
96 sizeof (sobufsz)) != 0) {
97 rc = ILB_STATUS_SOCKET;
98 (void) close(s);
99 goto out;
100 }
101
102 hi->h_socket = s;
103 hi->h_valid = B_TRUE;
104
105 out:
106 if (rc != ILB_STATUS_OK && s != -1)
107 (void) close(s);
108
109 if (rc == ILB_STATUS_OK) {
110 *hp = (ilb_handle_t)hi;
111 } else {
112 free(hi);
113 *hp = ILB_INVALID_HANDLE;
114 }
115 return (rc);
116 }
117
118 ilb_status_t
ilb_close(ilb_handle_t h)119 ilb_close(ilb_handle_t h)
120 {
121 ilb_handle_impl_t *hi = (ilb_handle_impl_t *)h;
122
123 if (h == ILB_INVALID_HANDLE)
124 return (ILB_STATUS_EINVAL);
125
126 if (mutex_lock(&hi->h_lock) != 0)
127 return (ILB_STATUS_INTERNAL);
128
129 /* Somebody has done a close, no need to do anything. */
130 if (hi->h_closing) {
131 return (ILB_STATUS_OK);
132 } else {
133 hi->h_closing = B_TRUE;
134 hi->h_error = ILB_STATUS_HANDLE_CLOSING;
135 }
136
137 /* Wait until there is nobody waiting. */
138 while (hi->h_waiter > 0) {
139 if (cond_wait(&hi->h_cv, &hi->h_lock) != 0) {
140 (void) mutex_unlock(&hi->h_lock);
141 return (ILB_STATUS_INTERNAL);
142 }
143 }
144 /* No one is waiting, proceed to free the handle. */
145
146 (void) close(hi->h_socket);
147 (void) mutex_destroy(&hi->h_lock);
148 (void) cond_destroy(&hi->h_cv);
149 free(hi);
150 return (ILB_STATUS_OK);
151 }
152
153 /*
154 * Unified routine to communicate with ilbd.
155 *
156 * If ic is non-NULL, it means that the caller wants to send something
157 * to ilbd and expects a reply. If ic is NULL, it means that the caller
158 * only expects to receive from ilbd.
159 *
160 * The rbuf is the buffer supplied by the caller for receiving. If it
161 * is NULL, it means that there is no reply expected.
162 *
163 * This function will not close() the socket to kernel unless there is
164 * an error. If the transaction only consists of one exchange, the caller
165 * can use i_ilb_close_comm() to close() the socket when done.
166 */
167 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)168 i_ilb_do_comm(ilb_handle_t h, ilb_comm_t *ic, size_t ic_sz, ilb_comm_t *rbuf,
169 size_t *rbufsz)
170 {
171 ilb_status_t rc = ILB_STATUS_OK;
172 int r, s;
173 ilb_handle_impl_t *hi = (ilb_handle_impl_t *)h;
174
175 assert(rbuf != NULL);
176 if (h == ILB_INVALID_HANDLE)
177 return (ILB_STATUS_EINVAL);
178
179 if (mutex_lock(&hi->h_lock) != 0)
180 return (ILB_STATUS_INTERNAL);
181
182 hi->h_waiter++;
183 while (hi->h_busy) {
184 if (cond_wait(&hi->h_cv, &hi->h_lock) != 0) {
185 hi->h_waiter--;
186 (void) cond_signal(&hi->h_cv);
187 (void) mutex_unlock(&hi->h_lock);
188 return (ILB_STATUS_INTERNAL);
189 }
190 }
191
192 if (!hi->h_valid || hi->h_closing) {
193 hi->h_waiter--;
194 (void) cond_signal(&hi->h_cv);
195 (void) mutex_unlock(&hi->h_lock);
196 return (hi->h_error);
197 }
198
199 hi->h_busy = B_TRUE;
200 (void) mutex_unlock(&hi->h_lock);
201
202 s = hi->h_socket;
203
204 r = send(s, ic, ic_sz, 0);
205 if (r < ic_sz) {
206 rc = ILB_STATUS_WRITE;
207 goto socket_error;
208 }
209 rc = ILB_STATUS_OK;
210
211 if ((r = recv(s, rbuf, *rbufsz, 0)) <= 0) {
212 rc = ILB_STATUS_READ;
213 } else {
214 *rbufsz = r;
215 goto out;
216 }
217
218 socket_error:
219 i_ilb_socket_set_err(h, rc);
220
221 out:
222 (void) mutex_lock(&hi->h_lock);
223 hi->h_busy = B_FALSE;
224 hi->h_waiter--;
225 (void) cond_signal(&hi->h_cv);
226 (void) mutex_unlock(&hi->h_lock);
227
228 return (rc);
229 }
230
231 void
i_ilb_close_comm(ilb_handle_t h)232 i_ilb_close_comm(ilb_handle_t h)
233 {
234 (void) ilb_close(h);
235 }
236