blob: 19a5798650a6e985ce997ec86a70618e693a8d63 [file] [log] [blame]
/* 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;
}