xref: /illumos-gate/usr/src/lib/libilb/common/ilb_comm.c (revision dbed73cbda2229fd1aa6dc5743993cae7f0a7ee9)
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
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
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
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
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
232 i_ilb_close_comm(ilb_handle_t h)
233 {
234 	(void) ilb_close(h);
235 }
236