| /* Copyright 2000-2005 The Apache Software Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** UNIX AF_LOCAL network wrapper |
| * |
| * @author Mladen Turk |
| * @version $Revision$, $Date$ |
| */ |
| |
| |
| #include "tcn.h" |
| #include "apr_thread_mutex.h" |
| #include "apr_poll.h" |
| |
| /* ### should be tossed in favor of APR */ |
| #include <sys/stat.h> |
| #include <sys/un.h> /* for sockaddr_un */ |
| |
| #ifdef TCN_DO_STATISTICS |
| #include "apr_atomic.h" |
| |
| static volatile apr_uint32_t uxp_created = 0; |
| static volatile apr_uint32_t uxp_closed = 0; |
| static volatile apr_uint32_t uxp_cleared = 0; |
| static volatile apr_uint32_t uxp_accepted = 0; |
| |
| void uxp_network_dump_statistics() |
| { |
| fprintf(stderr, "NT Network Statistics ..\n"); |
| fprintf(stderr, "Sockets created : %d\n", uxp_created); |
| fprintf(stderr, "Sockets accepted : %d\n", uxp_accepted); |
| fprintf(stderr, "Sockets closed : %d\n", uxp_closed); |
| fprintf(stderr, "Sockets cleared : %d\n", uxp_cleared); |
| } |
| |
| #endif |
| |
| #define DEFNAME "/var/run/tomcatnativesock" |
| #define DEFNAME_FMT "/var/run/tomcatnativesock%08x%08x" |
| #define DEFSIZE 8192 |
| #define DEFTIMEOUT 60000 |
| |
| #define TCN_UXP_UNKNOWN 0 |
| #define TCN_UXP_CLIENT 1 |
| #define TCN_UXP_ACCEPTED 2 |
| #define TCN_UXP_SERVER 3 |
| |
| #define TCN_UNIX_MAXPATH 1024 |
| typedef struct { |
| apr_pool_t *pool; |
| apr_socket_t *sock; /* APR socket */ |
| int sd; |
| struct sockaddr_un uxaddr; |
| int timeout; |
| int mode; /* Client or server mode */ |
| char name[TCN_UNIX_MAXPATH+1]; |
| } tcn_uxp_conn_t; |
| |
| static apr_status_t APR_THREAD_FUNC |
| uxp_socket_timeout_set(apr_socket_t *sock, apr_interval_time_t t) |
| { |
| tcn_uxp_conn_t *con = (tcn_uxp_conn_t *)sock; |
| if (t < 0) |
| con->timeout = -1; |
| else |
| con->timeout = (int)(apr_time_as_msec(t)); |
| return APR_SUCCESS; |
| } |
| |
| static apr_status_t APR_THREAD_FUNC |
| uxp_socket_timeout_get(apr_socket_t *sock, apr_interval_time_t *t) |
| { |
| tcn_uxp_conn_t *con = (tcn_uxp_conn_t*)sock; |
| if (con->timeout < 0) |
| *t = -1; |
| else |
| *t = con->timeout * 1000; |
| return APR_SUCCESS; |
| } |
| |
| static APR_INLINE apr_status_t APR_THREAD_FUNC |
| uxp_socket_opt_set(apr_socket_t *sock, apr_int32_t opt, apr_int32_t on) |
| { |
| tcn_uxp_conn_t *con = (tcn_uxp_conn_t *)sock; |
| return apr_socket_opt_set(con->sock, opt, on); |
| } |
| |
| static APR_INLINE apr_status_t APR_THREAD_FUNC |
| uxp_socket_opt_get(apr_socket_t *sock, apr_int32_t opt, apr_int32_t *on) |
| { |
| tcn_uxp_conn_t *con = (tcn_uxp_conn_t *)sock; |
| return apr_socket_opt_get(con->sock, opt, on); |
| } |
| |
| static apr_status_t uxp_cleanup(void *data) |
| { |
| tcn_uxp_conn_t *con = (tcn_uxp_conn_t *)data; |
| |
| if (con) { |
| if (con->sock) { |
| apr_socket_close(con->sock); |
| con->sock = NULL; |
| } |
| if (con->mode == TCN_UXP_SERVER) { |
| unlink(con->name); |
| con->mode = TCN_UXP_UNKNOWN; |
| } |
| } |
| |
| #ifdef TCN_DO_STATISTICS |
| apr_atomic_inc32(&uxp_cleared); |
| #endif |
| return APR_SUCCESS; |
| } |
| |
| static apr_status_t APR_THREAD_FUNC |
| uxp_socket_shutdown(apr_socket_t *sock, apr_shutdown_how_e how) |
| { |
| tcn_uxp_conn_t *con = (tcn_uxp_conn_t *)sock; |
| return apr_socket_shutdown(con->sock, how); |
| } |
| |
| static apr_status_t APR_THREAD_FUNC |
| uxp_socket_close(apr_socket_t *sock) |
| { |
| #ifdef TCN_DO_STATISTICS |
| apr_atomic_inc32(&uxp_closed); |
| #endif |
| return uxp_cleanup(sock); |
| } |
| |
| static apr_status_t APR_THREAD_FUNC |
| uxp_socket_recv(apr_socket_t *sock, char *buf, apr_size_t *len) |
| { |
| tcn_uxp_conn_t *con = (tcn_uxp_conn_t *)sock; |
| return apr_socket_recv(con->sock, buf, len); |
| } |
| |
| |
| static apr_status_t APR_THREAD_FUNC |
| uxp_socket_send(apr_socket_t *sock, const char *buf, |
| apr_size_t *len) |
| { |
| tcn_uxp_conn_t *con = (tcn_uxp_conn_t *)sock; |
| return apr_socket_send(con->sock, buf, len); |
| } |
| |
| static apr_status_t APR_THREAD_FUNC |
| uxp_socket_sendv(apr_socket_t *sock, |
| const struct iovec *vec, |
| apr_int32_t nvec, apr_size_t *len) |
| { |
| tcn_uxp_conn_t *con = (tcn_uxp_conn_t *)sock; |
| return apr_socket_sendv(con->sock, vec, nvec, len); |
| } |
| |
| static apr_status_t uxp_socket_cleanup(void *data) |
| { |
| tcn_socket_t *s = (tcn_socket_t *)data; |
| |
| if (s->net->cleanup) { |
| (*s->net->cleanup)(s->opaque); |
| s->net->cleanup = NULL; |
| } |
| #ifdef TCN_DO_STATISTICS |
| apr_atomic_inc32(&uxp_cleared); |
| #endif |
| return APR_SUCCESS; |
| } |
| |
| static tcn_nlayer_t uxp_socket_layer = { |
| TCN_SOCKET_UNIX, |
| uxp_cleanup, |
| uxp_socket_close, |
| uxp_socket_shutdown, |
| uxp_socket_opt_get, |
| uxp_socket_opt_set, |
| uxp_socket_timeout_get, |
| uxp_socket_timeout_set, |
| uxp_socket_send, |
| uxp_socket_sendv, |
| uxp_socket_recv |
| }; |
| |
| TCN_IMPLEMENT_CALL(jlong, Local, create)(TCN_STDARGS, jstring name, |
| jlong pool) |
| { |
| apr_pool_t *p = J2P(pool, apr_pool_t *); |
| tcn_socket_t *s = NULL; |
| tcn_uxp_conn_t *con = NULL; |
| int sd; |
| TCN_ALLOC_CSTRING(name); |
| |
| UNREFERENCED(o); |
| TCN_ASSERT(pool != 0); |
| |
| if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { |
| tcn_ThrowAPRException(e, apr_get_netos_error()); |
| return 0; |
| } |
| #ifdef TCN_DO_STATISTICS |
| uxp_created++; |
| #endif |
| con = (tcn_uxp_conn_t *)apr_pcalloc(p, sizeof(tcn_uxp_conn_t)); |
| con->pool = p; |
| con->mode = TCN_UXP_UNKNOWN; |
| con->timeout = DEFTIMEOUT; |
| con->sd = sd; |
| con->uxaddr.sun_family = AF_UNIX; |
| if (J2S(name)) { |
| strcpy(con->uxaddr.sun_path, J2S(name)); |
| TCN_FREE_CSTRING(name); |
| } |
| else |
| strcpy(con->uxaddr.sun_path, DEFNAME); |
| s = (tcn_socket_t *)apr_pcalloc(p, sizeof(tcn_socket_t)); |
| s->pool = p; |
| s->net = &uxp_socket_layer; |
| s->opaque = con; |
| apr_pool_cleanup_register(p, (const void *)s, |
| uxp_socket_cleanup, |
| apr_pool_cleanup_null); |
| |
| apr_os_sock_put(&(con->sock), &(con->sd), p); |
| |
| return P2J(s); |
| |
| } |
| |
| TCN_IMPLEMENT_CALL(jint, Local, bind)(TCN_STDARGS, jlong sock, |
| jlong sa) |
| { |
| tcn_socket_t *s = J2P(sock, tcn_socket_t *); |
| UNREFERENCED_STDARGS; |
| UNREFERENCED(sa); |
| TCN_ASSERT(sock != 0); |
| if (s->net->type == TCN_SOCKET_UNIX) { |
| int rc; |
| tcn_uxp_conn_t *c = (tcn_uxp_conn_t *)s->opaque; |
| c->mode = TCN_UXP_SERVER; |
| rc = bind(c->sd, (struct sockaddr *)&(c->uxaddr), sizeof(c->uxaddr)); |
| if (rc < 0) |
| return errno; |
| else |
| return APR_SUCCESS; |
| } |
| else |
| return APR_EINVAL; |
| } |
| |
| TCN_IMPLEMENT_CALL(jint, Local, listen)(TCN_STDARGS, jlong sock, |
| jint backlog) |
| { |
| tcn_socket_t *s = J2P(sock, tcn_socket_t *); |
| UNREFERENCED_STDARGS; |
| |
| TCN_ASSERT(sock != 0); |
| if (s->net->type == TCN_SOCKET_UNIX) { |
| tcn_uxp_conn_t *c = (tcn_uxp_conn_t *)s->opaque; |
| c->mode = TCN_UXP_SERVER; |
| return apr_socket_listen(c->sock, (apr_int32_t)backlog); |
| } |
| else |
| return APR_EINVAL; |
| } |
| |
| TCN_IMPLEMENT_CALL(jlong, Local, accept)(TCN_STDARGS, jlong sock) |
| { |
| tcn_socket_t *s = J2P(sock, tcn_socket_t *); |
| apr_pool_t *p = NULL; |
| tcn_socket_t *a = NULL; |
| tcn_uxp_conn_t *con = NULL; |
| |
| UNREFERENCED(o); |
| TCN_ASSERT(sock != 0); |
| |
| TCN_THROW_IF_ERR(apr_pool_create(&p, s->pool), p); |
| if (s->net->type == TCN_SOCKET_UNIX) { |
| apr_socklen_t len; |
| tcn_uxp_conn_t *c = (tcn_uxp_conn_t *)s->opaque; |
| con = (tcn_uxp_conn_t *)apr_pcalloc(p, sizeof(tcn_uxp_conn_t)); |
| con->pool = p; |
| con->mode = TCN_UXP_ACCEPTED; |
| con->timeout = c->timeout; |
| len = sizeof(c->uxaddr); |
| /* Block until a client connects */ |
| con->sd = accept(c->sd, (struct sockaddr *)&(con->uxaddr), &len); |
| if (con->sd < 0) { |
| tcn_ThrowAPRException(e, apr_get_os_error()); |
| goto cleanup; |
| } |
| } |
| else { |
| tcn_ThrowAPRException(e, APR_ENOTIMPL); |
| goto cleanup; |
| } |
| if (con) { |
| #ifdef TCN_DO_STATISTICS |
| apr_atomic_inc32(&uxp_accepted); |
| #endif |
| a = (tcn_socket_t *)apr_pcalloc(p, sizeof(tcn_socket_t)); |
| a->pool = p; |
| a->net = &uxp_socket_layer; |
| a->opaque = con; |
| apr_pool_cleanup_register(p, (const void *)a, |
| uxp_socket_cleanup, |
| apr_pool_cleanup_null); |
| apr_os_sock_put(&(con->sock), &(con->sd), p); |
| } |
| return P2J(a); |
| cleanup: |
| if (p) |
| apr_pool_destroy(p); |
| return 0; |
| } |
| |
| TCN_IMPLEMENT_CALL(jint, Local, connect)(TCN_STDARGS, jlong sock, |
| jlong sa) |
| { |
| tcn_socket_t *s = J2P(sock, tcn_socket_t *); |
| tcn_uxp_conn_t *con = NULL; |
| int rc; |
| |
| UNREFERENCED(o); |
| UNREFERENCED(sa); |
| TCN_ASSERT(sock != 0); |
| if (s->net->type != TCN_SOCKET_UNIX) |
| return APR_ENOTSOCK; |
| con = (tcn_uxp_conn_t *)s->opaque; |
| if (con->mode != TCN_UXP_UNKNOWN) |
| return APR_EINVAL; |
| do { |
| rc = connect(con->sd, (const struct sockaddr *)&(con->uxaddr), |
| sizeof(con->uxaddr)); |
| } while (rc == -1 && errno == EINTR); |
| |
| if (rc == -1 && errno != EISCONN) |
| return errno; |
| con->mode = TCN_UXP_CLIENT; |
| |
| return APR_SUCCESS; |
| } |