/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include <stdlib.h> #include <strings.h> #include <unistd.h> #include <stddef.h> #include <assert.h> #include <sys/types.h> #include <sys/socket.h> #include <thread.h> #include <synch.h> #include <libilb_impl.h> #include <libilb.h> /* Assertion: the calling thread has a hold on the handle */ static void i_ilb_socket_set_err(ilb_handle_t h, ilb_status_t err) { ilb_handle_impl_t *hi = (ilb_handle_impl_t *)h; if (h == ILB_INVALID_HANDLE) return; hi->h_valid = B_FALSE; hi->h_error = err; } ilb_status_t ilb_open(ilb_handle_t *hp) { ilb_handle_impl_t *hi = NULL; int s = -1; struct sockaddr_un sa = {AF_UNIX, SOCKET_PATH}; ilb_status_t rc = ILB_STATUS_OK; int sobufsz; if (hp == NULL) return (ILB_STATUS_EINVAL); hi = calloc(sizeof (*hi), 1); if (hi == NULL) return (ILB_STATUS_ENOMEM); if (cond_init(&hi->h_cv, USYNC_THREAD, NULL) != 0) { rc = ILB_STATUS_INTERNAL; goto out; } if (mutex_init(&hi->h_lock, USYNC_THREAD | LOCK_ERRORCHECK, NULL) != 0) { rc = ILB_STATUS_INTERNAL; goto out; } hi->h_busy = B_FALSE; if ((s = socket(PF_UNIX, SOCK_SEQPACKET, 0)) == -1 || connect(s, (struct sockaddr *)&sa, sizeof (sa.sun_path)) == -1) { rc = ILB_STATUS_SOCKET; goto out; } /* The socket buffer must be at least the max size of a message */ sobufsz = ILBD_MSG_SIZE; if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sobufsz, sizeof (sobufsz)) != 0) { rc = ILB_STATUS_SOCKET; (void) close(s); goto out; } if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &sobufsz, sizeof (sobufsz)) != 0) { rc = ILB_STATUS_SOCKET; (void) close(s); goto out; } hi->h_socket = s; hi->h_valid = B_TRUE; out: if (rc != ILB_STATUS_OK && s != -1) (void) close(s); if (rc == ILB_STATUS_OK) { *hp = (ilb_handle_t)hi; } else { free(hi); *hp = ILB_INVALID_HANDLE; } return (rc); } ilb_status_t ilb_close(ilb_handle_t h) { ilb_handle_impl_t *hi = (ilb_handle_impl_t *)h; if (h == ILB_INVALID_HANDLE) return (ILB_STATUS_EINVAL); if (mutex_lock(&hi->h_lock) != 0) return (ILB_STATUS_INTERNAL); /* Somebody has done a close, no need to do anything. */ if (hi->h_closing) { return (ILB_STATUS_OK); } else { hi->h_closing = B_TRUE; hi->h_error = ILB_STATUS_HANDLE_CLOSING; } /* Wait until there is nobody waiting. */ while (hi->h_waiter > 0) { if (cond_wait(&hi->h_cv, &hi->h_lock) != 0) { (void) mutex_unlock(&hi->h_lock); return (ILB_STATUS_INTERNAL); } } /* No one is waiting, proceed to free the handle. */ (void) close(hi->h_socket); (void) mutex_destroy(&hi->h_lock); (void) cond_destroy(&hi->h_cv); free(hi); return (ILB_STATUS_OK); } /* * Unified routine to communicate with ilbd. * * If ic is non-NULL, it means that the caller wants to send something * to ilbd and expects a reply. If ic is NULL, it means that the caller * only expects to receive from ilbd. * * The rbuf is the buffer supplied by the caller for receiving. If it * is NULL, it means that there is no reply expected. * * This function will not close() the socket to kernel unless there is * an error. If the transaction only consists of one exchange, the caller * can use i_ilb_close_comm() to close() the socket when done. */ 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) { ilb_status_t rc = ILB_STATUS_OK; int r, s; ilb_handle_impl_t *hi = (ilb_handle_impl_t *)h; assert(rbuf != NULL); if (h == ILB_INVALID_HANDLE) return (ILB_STATUS_EINVAL); if (mutex_lock(&hi->h_lock) != 0) return (ILB_STATUS_INTERNAL); hi->h_waiter++; while (hi->h_busy) { if (cond_wait(&hi->h_cv, &hi->h_lock) != 0) { hi->h_waiter--; (void) cond_signal(&hi->h_cv); (void) mutex_unlock(&hi->h_lock); return (ILB_STATUS_INTERNAL); } } if (!hi->h_valid || hi->h_closing) { hi->h_waiter--; (void) cond_signal(&hi->h_cv); (void) mutex_unlock(&hi->h_lock); return (hi->h_error); } hi->h_busy = B_TRUE; (void) mutex_unlock(&hi->h_lock); s = hi->h_socket; r = send(s, ic, ic_sz, 0); if (r < ic_sz) { rc = ILB_STATUS_WRITE; goto socket_error; } rc = ILB_STATUS_OK; if ((r = recv(s, rbuf, *rbufsz, 0)) <= 0) { rc = ILB_STATUS_READ; } else { *rbufsz = r; goto out; } socket_error: i_ilb_socket_set_err(h, rc); out: (void) mutex_lock(&hi->h_lock); hi->h_busy = B_FALSE; hi->h_waiter--; (void) cond_signal(&hi->h_cv); (void) mutex_unlock(&hi->h_lock); return (rc); } void i_ilb_close_comm(ilb_handle_t h) { (void) ilb_close(h); }