grostoon
2006-12-05 21:58:47 UTC
Hi all,
I'm currently adding support for Unix domain socket to firebird 1.5.3. My goal is to be able
to make the super server listen on a unix socket rather than a TCP socket. This way, no TCP port is reserved on the system, making the database access purely local. And compare to binding to 127.0.0.1, the unix socket has (I think) at least 2 advantages :
=> you can really control who (in term of UID/GIDs) can open (so connect) the unix socket thanks to the file permissions set on the socket path whereas with a TCP socket on 127.0.0.1, all local users can connect whatever their UID/GIDs are.
=> unix sockets are often twice as fast as TCP sockets.
Well, I really think unix socket is a valuable feature...
At this time, what I've done is just a proof of concept or a prototype. If you feel it an interesting feature, I would appreciate your suggestions.
I use the '@' character in the connection string to separate the socket path from the database path. The connection string then looks like socket-***@database-file-path.
For example, /opt/firebird/.fbsock@/opt/firebird/examples/v5/employees.fdb.
For example, to make the fbserver bind on the unix socket /tmp/.fbsock, one have to edit firedird.conf and define RemoteServicePort and RemoteServiceName variables this way :
RemoteServicePort = 0
RemoteServiceName = /tmp/.fbsock
Notice: it could be also interesting to have the fbserver able to listen on both TCP and unix socket at the same time. It such a case, using RemoteServiceName to specified the unix socket is probably not a good idea. Adding a new configuration variable (LocalServiceName for example) is probably a better solution.
Following are the places where I added code to firebird to implement this feature. It works fine at least with isql, gfix, gbak.
However, I've got a little problem with server startup through fbguard : /etc/init.d/firebird start takes much more time to complete when I activate unix socket than when using classic TCP socket. Moreover, fbguard fails to start fbserver if the unix socket already exists even if /etc/init.d/firebird start report an 'OK' status !
/etc/init.d/firebird stop works fine.
Help would be appreciate.
In src/jrd/isc_file.cpp :
At the beginning of function ISC_analyze_tcp :
// First of all detect unix socket connection
if (*file_name == '/' && (p = strchr(file_name, '@')) != NULL) {
*p = '\0';
strcpy(node_name, file_name);
while (*file_name++ = *++p);
return TRUE;
}
if (!(p = strchr(file_name, INET_FLAG))) {
return FALSE;
}
...
In src/remote/inet.cpp :
I rewrite the INET_connect function the follwing way (it's not perfect, it's just a proof of concept at this time : there should be some #ifdef UNIX to make the unix socket code invisible when compiling for windows) :
PORT DLL_EXPORT INET_connect(TEXT * name,
PACKET * packet,
ISC_STATUS * status_vector,
USHORT flag, SCHAR * dpb, SSHORT dpb_length)
{
/**************************************
*
* I N E T _ c o n n e c t
*
**************************************
*
* Functional description
* Establish half of a communication link. If a connect packet is given,
* the connection is on behalf of a remote interface. Otherwise the connect
* is for a server process.
*
**************************************/
socklen_t l;
int n;
int ux = 0; // set to 1 when unix socket detected
SOCKET s;
PORT port;
TEXT temp[1024];
TEXT *p;
struct sockaddr_in address;
struct sockaddr_un uxaddress; // unix domain socket support (AF_UNIX)
#ifndef VMS
struct servent *service;
TEXT msg[64];
#endif
int optval;
#ifdef DEBUG
{
UCHAR *p;
if (INET_trace & TRACE_operations) {
ib_fprintf(ib_stdout, "INET_connect\n");
ib_fflush(ib_stdout);
};
INET_start_time = inet_debug_timer();
if ((p = (UCHAR*) getenv("INET_force_error")) != NULL) {
INET_force_error = atoi((const char*)p);
}
}
#endif
port = alloc_port(0);
port->port_status_vector = status_vector;
REMOTE_get_timeout_params(port, reinterpret_cast < UCHAR * >(dpb),
dpb_length);
status_vector[0] = gds_arg_gds;
status_vector[1] = 0;
status_vector[2] = gds_arg_end;
#ifdef VMS
ISC_tcp_setup(ISC_wait, gds__completion_ast);
#endif
const TEXT* protocol = NULL;
if (name) {
strcpy(temp, name);
if (*temp == '/') {
/* Unix domain socket (/socket-path@/database-file-path) */
ux = 1;
name = temp;
}
else {
for (p = temp; *p;) {
if (*p++ == '/') {
p[-1] = 0;
name = temp;
protocol = p;
break;
}
}
}
}
if (name && *name) {
if (port->port_connection) {
ALLR_free(port->port_connection);
}
port->port_connection = REMOTE_make_string(name);
}
else {
name = port->port_host->str_data;
}
if (!protocol) {
/*
* For unix socket, RemoteServicePort have to be set to 0 and RemoteServiceName
* have to be defined as the unix socket path. Example :
* RemoteServicePort = 0
* RemoteServiceName = /tmp/.fbsock
*/
const int portno = Config::getRemoteServicePort();
const char* svc = Config::getRemoteServiceName();
if (portno) {
snprintf(temp, sizeof(temp), "%d", portno);
protocol = temp;
}
else {
protocol = svc;
}
if (*protocol == '/') {
// Unix domain socket
ux = 1;
strcpy(temp, protocol);
name = temp;
if (port->port_connection) {
ALLR_free(port->port_connection);
}
port->port_connection = REMOTE_make_string(name);
}
}
/* Set up Inter-Net socket address */
inet_zero((SCHAR *) &address, sizeof(address));
if (ux) {
inet_zero((SCHAR *) &uxaddress, sizeof(uxaddress));
}
#ifdef VMS
/* V M S */
if (!ux && getservport(protocol, "tcp", &address.sin_port) == -1) {
inet_error(port, "getservbyname", isc_net_connect_err, 0);
disconnect(port);
return NULL;
}
if (packet) {
if (!ux && getaddr(name, &address) == -1) {
inet_error(port, "gethostbyname", isc_net_connect_err, 0);
disconnect(port);
return NULL;
}
}
else {
if (!ux) {
address.sin_addr.s_addr = INADDR_ANY;
}
}
#else
/* U N I X style sockets */
if (ux) {
uxaddress.sun_family = AF_LOCAL;
strcpy(uxaddress.sun_path, name);
}
else {
address.sin_family = AF_INET;
}
in_addr host_addr;
if (!ux) {
if (packet) {
// client connection
host_addr = get_host_address(name);
if (host_addr.s_addr == INADDR_NONE) {
sprintf(msg,
"INET/INET_connect: gethostbyname failed, error code = %d",
H_ERRNO);
gds__log(msg, 0);
inet_gen_error(port,
isc_network_error,
isc_arg_string,
port->port_connection->str_data,
isc_arg_gds,
isc_net_lookup_err, isc_arg_gds, isc_host_unknown, 0);
disconnect(port);
return NULL;
}
}
else {
// server connection
host_addr = get_bind_address();
}
inet_copy((SCHAR*) &host_addr, (SCHAR *) &address.sin_addr,
sizeof(address.sin_addr));
}
THREAD_EXIT;
if (!ux) {
service = getservbyname(protocol, "tcp");
}
#ifdef WIN_NT
/* On Windows NT/9x, getservbyname can only accomodate
* 1 call at a time. In this case it returns the error
* WSAEINPROGRESS.
* If this happens, retry the operation a few times.
* NOTE: This still does not guarantee success, but helps.
*/
if (!service) {
if (H_ERRNO == INET_RETRY_ERRNO) {
for (int retry = 0; retry < INET_RETRY_CALL; retry++) {
if ( (service = getservbyname(protocol, "tcp")) )
break;
}
}
}
#endif /* WIN_NT */
THREAD_ENTER;
/* Modification by luz (slightly modified by FSG)
instead of failing here, try applying hard-wired
translation of "gds_db" into "3050"
This way, a connection to a remote FB server
works even from clients with missing "gds_db"
entry in "services" file, which is important
for zero-installation clients.
*/
if (!ux) {
if (!service) {
if (strcmp(protocol, FB_SERVICE_NAME) == 0) {
/* apply hardwired translation */
address.sin_port = htons(FB_SERVICE_PORT);
}
/* modification by FSG 23.MAR.2001 */
else {
/* modification by FSG 23.MAR.2001 */
/* The user has supplied something as protocol
* let's see whether this is a port number
* instead of a service name
*/
address.sin_port = htons(atoi(protocol));
}
if (address.sin_port == 0) {
/* end of modification by FSG */
/* this is the original code */
sprintf(msg,
"INET/INET_connect: getservbyname failed, error code = %d",
H_ERRNO);
gds__log(msg, 0);
inet_gen_error(port,
isc_network_error,
isc_arg_string,
port->port_connection->str_data,
isc_arg_gds,
isc_net_lookup_err,
isc_arg_gds,
isc_service_unknown,
isc_arg_string,
protocol, isc_arg_string, "tcp", 0);
return NULL;
}
}
else {
/* if we have got a service-struct, get port number from there
* (in case of hardwired gds_db to 3050 translation, address.sin_port was
* already set above */
address.sin_port = service->s_port;
}
}
/* end of modifications by luz */
#endif /* VMS */
/* Allocate a port block and initialize a socket for communications */
port->port_handle = (HANDLE) socket(ux ? AF_LOCAL : AF_INET, SOCK_STREAM, 0);
if ((SOCKET) port->port_handle == INVALID_SOCKET)
{
inet_error(port, "socket", isc_net_connect_err, ERRNO);
disconnect(port);
return NULL;
}
/* If we're a host, just make the connection */
if (packet) {
THREAD_EXIT;
if (ux) {
n = connect((SOCKET) port->port_handle,
(struct sockaddr *) &uxaddress, SUN_LEN(&uxaddress));
}
else {
n = connect((SOCKET) port->port_handle,
(struct sockaddr *) &address, sizeof(address));
}
THREAD_ENTER;
if (n != -1 && send_full(port, packet))
return port;
else {
inet_error(port, "connect", isc_net_connect_err, ERRNO);
disconnect(port);
return NULL;
}
}
/* We're a server, so wait for a host to show up */
if (flag & SRVR_multi_client) {
if (!ux) {
socklen_t optlen;
struct linger lingerInfo;
lingerInfo.l_onoff = 0;
lingerInfo.l_linger = 0;
optval = TRUE;
n = setsockopt((SOCKET) port->port_handle, SOL_SOCKET, SO_REUSEADDR,
(SCHAR *) & optval, sizeof(optval));
if (n == -1) {
inet_error(port, "setsockopt REUSE", isc_net_connect_listen_err,
ERRNO);
disconnect(port);
return NULL;
}
/* Get any values for SO_LINGER so that they can be reset during
* disconnect. SO_LINGER should be set by default on the socket
*/
optlen = sizeof(port->port_linger);
n = getsockopt((SOCKET) port->port_handle, SOL_SOCKET, SO_LINGER,
(SCHAR *) & port->port_linger, &optlen);
if (n != 0) /* getsockopt failed */
port->port_linger.l_onoff = 0;
n = setsockopt((SOCKET) port->port_handle, SOL_SOCKET, SO_LINGER,
(SCHAR *) & lingerInfo, sizeof(lingerInfo));
if (n == -1) {
inet_error(port, "setsockopt LINGER", isc_net_connect_listen_err,
ERRNO);
disconnect(port);
return NULL;
}
#ifdef SET_TCP_NO_DELAY
if (Config::getTcpNoNagle()) {
optval = TRUE;
n =
setsockopt((SOCKET) port->port_handle, SOL_SOCKET,
TCP_NODELAY, (SCHAR *) & optval, sizeof(optval));
gds__log("inet log: disabled Nagle algorithm \n");
if (n == -1) {
inet_error(port, "setsockopt TCP_NODELAY",
isc_net_connect_listen_err, ERRNO);
disconnect(port);
return NULL;
}
}
#endif
} /* !ux */
}
if (ux) {
struct stat st;
if (stat(uxaddress.sun_path, &st) == 0 && S_ISSOCK(st.st_mode)) {
// Kind of REUSEADDR for unix socket ...
unlink(uxaddress.sun_path);
}
n = bind((SOCKET) port->port_handle,
(struct sockaddr *) &uxaddress, SUN_LEN(&uxaddress));
}
else {
n = bind((SOCKET) port->port_handle,
(struct sockaddr *) &address, sizeof(address));
}
if (n == -1) {
/* On Linux platform, when the server dies the system holds a port
for some time. */
if (!ux && ERRNO == INET_ADDR_IN_USE) {
int retry;
for (retry = 0; retry < INET_RETRY_CALL; retry++) {
sleep(10);
n = bind((SOCKET) port->port_handle,
(struct sockaddr *) &address, sizeof(address));
if (n == 0)
break;
}
}
else {
inet_error(port, "bind", isc_net_connect_listen_err, ERRNO);
disconnect(port);
return NULL;
}
}
n = listen((SOCKET) port->port_handle, 5);
if (n == -1) {
inet_error(port, "listen", isc_net_connect_listen_err, ERRNO);
return NULL;
}
if (flag & SRVR_multi_client) {
/* Prevent the generation of dummy keepalive packets on the
connect port. */
port->port_dummy_packet_interval = 0;
port->port_dummy_timeout = 0;
port->port_server_flags |= (SRVR_server | SRVR_multi_client);
gds__register_cleanup(exit_handler, (void *) port);
return port;
}
while (true) {
THREAD_EXIT;
if (ux) {
l = sizeof(uxaddress);
s = accept((SOCKET) port->port_handle,
(struct sockaddr *) &uxaddress, &l);
}
else {
l = sizeof(address);
s = accept((SOCKET) port->port_handle,
(struct sockaddr *) &address, &l);
}
if (s == INVALID_SOCKET) {
THREAD_ENTER;
inet_error(port, "accept", isc_net_connect_err, ERRNO);
disconnect(port);
return NULL;
}
#ifdef WIN_NT
if ((flag & SRVR_debug) || !fork(s, flag))
#else
if ((flag & SRVR_debug) || !fork())
#endif
{
THREAD_ENTER;
SOCLOSE((SOCKET) port->port_handle);
port->port_handle = (HANDLE) s;
port->port_server_flags |= SRVR_server;
return port;
}
THREAD_ENTER;
SOCLOSE(s);
}
}
Best regards,
Toon.
__________________________________________________
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around
http://mail.yahoo.com
I'm currently adding support for Unix domain socket to firebird 1.5.3. My goal is to be able
to make the super server listen on a unix socket rather than a TCP socket. This way, no TCP port is reserved on the system, making the database access purely local. And compare to binding to 127.0.0.1, the unix socket has (I think) at least 2 advantages :
=> you can really control who (in term of UID/GIDs) can open (so connect) the unix socket thanks to the file permissions set on the socket path whereas with a TCP socket on 127.0.0.1, all local users can connect whatever their UID/GIDs are.
=> unix sockets are often twice as fast as TCP sockets.
Well, I really think unix socket is a valuable feature...
At this time, what I've done is just a proof of concept or a prototype. If you feel it an interesting feature, I would appreciate your suggestions.
I use the '@' character in the connection string to separate the socket path from the database path. The connection string then looks like socket-***@database-file-path.
For example, /opt/firebird/.fbsock@/opt/firebird/examples/v5/employees.fdb.
For example, to make the fbserver bind on the unix socket /tmp/.fbsock, one have to edit firedird.conf and define RemoteServicePort and RemoteServiceName variables this way :
RemoteServicePort = 0
RemoteServiceName = /tmp/.fbsock
Notice: it could be also interesting to have the fbserver able to listen on both TCP and unix socket at the same time. It such a case, using RemoteServiceName to specified the unix socket is probably not a good idea. Adding a new configuration variable (LocalServiceName for example) is probably a better solution.
Following are the places where I added code to firebird to implement this feature. It works fine at least with isql, gfix, gbak.
However, I've got a little problem with server startup through fbguard : /etc/init.d/firebird start takes much more time to complete when I activate unix socket than when using classic TCP socket. Moreover, fbguard fails to start fbserver if the unix socket already exists even if /etc/init.d/firebird start report an 'OK' status !
/etc/init.d/firebird stop works fine.
Help would be appreciate.
In src/jrd/isc_file.cpp :
At the beginning of function ISC_analyze_tcp :
// First of all detect unix socket connection
if (*file_name == '/' && (p = strchr(file_name, '@')) != NULL) {
*p = '\0';
strcpy(node_name, file_name);
while (*file_name++ = *++p);
return TRUE;
}
if (!(p = strchr(file_name, INET_FLAG))) {
return FALSE;
}
...
In src/remote/inet.cpp :
I rewrite the INET_connect function the follwing way (it's not perfect, it's just a proof of concept at this time : there should be some #ifdef UNIX to make the unix socket code invisible when compiling for windows) :
PORT DLL_EXPORT INET_connect(TEXT * name,
PACKET * packet,
ISC_STATUS * status_vector,
USHORT flag, SCHAR * dpb, SSHORT dpb_length)
{
/**************************************
*
* I N E T _ c o n n e c t
*
**************************************
*
* Functional description
* Establish half of a communication link. If a connect packet is given,
* the connection is on behalf of a remote interface. Otherwise the connect
* is for a server process.
*
**************************************/
socklen_t l;
int n;
int ux = 0; // set to 1 when unix socket detected
SOCKET s;
PORT port;
TEXT temp[1024];
TEXT *p;
struct sockaddr_in address;
struct sockaddr_un uxaddress; // unix domain socket support (AF_UNIX)
#ifndef VMS
struct servent *service;
TEXT msg[64];
#endif
int optval;
#ifdef DEBUG
{
UCHAR *p;
if (INET_trace & TRACE_operations) {
ib_fprintf(ib_stdout, "INET_connect\n");
ib_fflush(ib_stdout);
};
INET_start_time = inet_debug_timer();
if ((p = (UCHAR*) getenv("INET_force_error")) != NULL) {
INET_force_error = atoi((const char*)p);
}
}
#endif
port = alloc_port(0);
port->port_status_vector = status_vector;
REMOTE_get_timeout_params(port, reinterpret_cast < UCHAR * >(dpb),
dpb_length);
status_vector[0] = gds_arg_gds;
status_vector[1] = 0;
status_vector[2] = gds_arg_end;
#ifdef VMS
ISC_tcp_setup(ISC_wait, gds__completion_ast);
#endif
const TEXT* protocol = NULL;
if (name) {
strcpy(temp, name);
if (*temp == '/') {
/* Unix domain socket (/socket-path@/database-file-path) */
ux = 1;
name = temp;
}
else {
for (p = temp; *p;) {
if (*p++ == '/') {
p[-1] = 0;
name = temp;
protocol = p;
break;
}
}
}
}
if (name && *name) {
if (port->port_connection) {
ALLR_free(port->port_connection);
}
port->port_connection = REMOTE_make_string(name);
}
else {
name = port->port_host->str_data;
}
if (!protocol) {
/*
* For unix socket, RemoteServicePort have to be set to 0 and RemoteServiceName
* have to be defined as the unix socket path. Example :
* RemoteServicePort = 0
* RemoteServiceName = /tmp/.fbsock
*/
const int portno = Config::getRemoteServicePort();
const char* svc = Config::getRemoteServiceName();
if (portno) {
snprintf(temp, sizeof(temp), "%d", portno);
protocol = temp;
}
else {
protocol = svc;
}
if (*protocol == '/') {
// Unix domain socket
ux = 1;
strcpy(temp, protocol);
name = temp;
if (port->port_connection) {
ALLR_free(port->port_connection);
}
port->port_connection = REMOTE_make_string(name);
}
}
/* Set up Inter-Net socket address */
inet_zero((SCHAR *) &address, sizeof(address));
if (ux) {
inet_zero((SCHAR *) &uxaddress, sizeof(uxaddress));
}
#ifdef VMS
/* V M S */
if (!ux && getservport(protocol, "tcp", &address.sin_port) == -1) {
inet_error(port, "getservbyname", isc_net_connect_err, 0);
disconnect(port);
return NULL;
}
if (packet) {
if (!ux && getaddr(name, &address) == -1) {
inet_error(port, "gethostbyname", isc_net_connect_err, 0);
disconnect(port);
return NULL;
}
}
else {
if (!ux) {
address.sin_addr.s_addr = INADDR_ANY;
}
}
#else
/* U N I X style sockets */
if (ux) {
uxaddress.sun_family = AF_LOCAL;
strcpy(uxaddress.sun_path, name);
}
else {
address.sin_family = AF_INET;
}
in_addr host_addr;
if (!ux) {
if (packet) {
// client connection
host_addr = get_host_address(name);
if (host_addr.s_addr == INADDR_NONE) {
sprintf(msg,
"INET/INET_connect: gethostbyname failed, error code = %d",
H_ERRNO);
gds__log(msg, 0);
inet_gen_error(port,
isc_network_error,
isc_arg_string,
port->port_connection->str_data,
isc_arg_gds,
isc_net_lookup_err, isc_arg_gds, isc_host_unknown, 0);
disconnect(port);
return NULL;
}
}
else {
// server connection
host_addr = get_bind_address();
}
inet_copy((SCHAR*) &host_addr, (SCHAR *) &address.sin_addr,
sizeof(address.sin_addr));
}
THREAD_EXIT;
if (!ux) {
service = getservbyname(protocol, "tcp");
}
#ifdef WIN_NT
/* On Windows NT/9x, getservbyname can only accomodate
* 1 call at a time. In this case it returns the error
* WSAEINPROGRESS.
* If this happens, retry the operation a few times.
* NOTE: This still does not guarantee success, but helps.
*/
if (!service) {
if (H_ERRNO == INET_RETRY_ERRNO) {
for (int retry = 0; retry < INET_RETRY_CALL; retry++) {
if ( (service = getservbyname(protocol, "tcp")) )
break;
}
}
}
#endif /* WIN_NT */
THREAD_ENTER;
/* Modification by luz (slightly modified by FSG)
instead of failing here, try applying hard-wired
translation of "gds_db" into "3050"
This way, a connection to a remote FB server
works even from clients with missing "gds_db"
entry in "services" file, which is important
for zero-installation clients.
*/
if (!ux) {
if (!service) {
if (strcmp(protocol, FB_SERVICE_NAME) == 0) {
/* apply hardwired translation */
address.sin_port = htons(FB_SERVICE_PORT);
}
/* modification by FSG 23.MAR.2001 */
else {
/* modification by FSG 23.MAR.2001 */
/* The user has supplied something as protocol
* let's see whether this is a port number
* instead of a service name
*/
address.sin_port = htons(atoi(protocol));
}
if (address.sin_port == 0) {
/* end of modification by FSG */
/* this is the original code */
sprintf(msg,
"INET/INET_connect: getservbyname failed, error code = %d",
H_ERRNO);
gds__log(msg, 0);
inet_gen_error(port,
isc_network_error,
isc_arg_string,
port->port_connection->str_data,
isc_arg_gds,
isc_net_lookup_err,
isc_arg_gds,
isc_service_unknown,
isc_arg_string,
protocol, isc_arg_string, "tcp", 0);
return NULL;
}
}
else {
/* if we have got a service-struct, get port number from there
* (in case of hardwired gds_db to 3050 translation, address.sin_port was
* already set above */
address.sin_port = service->s_port;
}
}
/* end of modifications by luz */
#endif /* VMS */
/* Allocate a port block and initialize a socket for communications */
port->port_handle = (HANDLE) socket(ux ? AF_LOCAL : AF_INET, SOCK_STREAM, 0);
if ((SOCKET) port->port_handle == INVALID_SOCKET)
{
inet_error(port, "socket", isc_net_connect_err, ERRNO);
disconnect(port);
return NULL;
}
/* If we're a host, just make the connection */
if (packet) {
THREAD_EXIT;
if (ux) {
n = connect((SOCKET) port->port_handle,
(struct sockaddr *) &uxaddress, SUN_LEN(&uxaddress));
}
else {
n = connect((SOCKET) port->port_handle,
(struct sockaddr *) &address, sizeof(address));
}
THREAD_ENTER;
if (n != -1 && send_full(port, packet))
return port;
else {
inet_error(port, "connect", isc_net_connect_err, ERRNO);
disconnect(port);
return NULL;
}
}
/* We're a server, so wait for a host to show up */
if (flag & SRVR_multi_client) {
if (!ux) {
socklen_t optlen;
struct linger lingerInfo;
lingerInfo.l_onoff = 0;
lingerInfo.l_linger = 0;
optval = TRUE;
n = setsockopt((SOCKET) port->port_handle, SOL_SOCKET, SO_REUSEADDR,
(SCHAR *) & optval, sizeof(optval));
if (n == -1) {
inet_error(port, "setsockopt REUSE", isc_net_connect_listen_err,
ERRNO);
disconnect(port);
return NULL;
}
/* Get any values for SO_LINGER so that they can be reset during
* disconnect. SO_LINGER should be set by default on the socket
*/
optlen = sizeof(port->port_linger);
n = getsockopt((SOCKET) port->port_handle, SOL_SOCKET, SO_LINGER,
(SCHAR *) & port->port_linger, &optlen);
if (n != 0) /* getsockopt failed */
port->port_linger.l_onoff = 0;
n = setsockopt((SOCKET) port->port_handle, SOL_SOCKET, SO_LINGER,
(SCHAR *) & lingerInfo, sizeof(lingerInfo));
if (n == -1) {
inet_error(port, "setsockopt LINGER", isc_net_connect_listen_err,
ERRNO);
disconnect(port);
return NULL;
}
#ifdef SET_TCP_NO_DELAY
if (Config::getTcpNoNagle()) {
optval = TRUE;
n =
setsockopt((SOCKET) port->port_handle, SOL_SOCKET,
TCP_NODELAY, (SCHAR *) & optval, sizeof(optval));
gds__log("inet log: disabled Nagle algorithm \n");
if (n == -1) {
inet_error(port, "setsockopt TCP_NODELAY",
isc_net_connect_listen_err, ERRNO);
disconnect(port);
return NULL;
}
}
#endif
} /* !ux */
}
if (ux) {
struct stat st;
if (stat(uxaddress.sun_path, &st) == 0 && S_ISSOCK(st.st_mode)) {
// Kind of REUSEADDR for unix socket ...
unlink(uxaddress.sun_path);
}
n = bind((SOCKET) port->port_handle,
(struct sockaddr *) &uxaddress, SUN_LEN(&uxaddress));
}
else {
n = bind((SOCKET) port->port_handle,
(struct sockaddr *) &address, sizeof(address));
}
if (n == -1) {
/* On Linux platform, when the server dies the system holds a port
for some time. */
if (!ux && ERRNO == INET_ADDR_IN_USE) {
int retry;
for (retry = 0; retry < INET_RETRY_CALL; retry++) {
sleep(10);
n = bind((SOCKET) port->port_handle,
(struct sockaddr *) &address, sizeof(address));
if (n == 0)
break;
}
}
else {
inet_error(port, "bind", isc_net_connect_listen_err, ERRNO);
disconnect(port);
return NULL;
}
}
n = listen((SOCKET) port->port_handle, 5);
if (n == -1) {
inet_error(port, "listen", isc_net_connect_listen_err, ERRNO);
return NULL;
}
if (flag & SRVR_multi_client) {
/* Prevent the generation of dummy keepalive packets on the
connect port. */
port->port_dummy_packet_interval = 0;
port->port_dummy_timeout = 0;
port->port_server_flags |= (SRVR_server | SRVR_multi_client);
gds__register_cleanup(exit_handler, (void *) port);
return port;
}
while (true) {
THREAD_EXIT;
if (ux) {
l = sizeof(uxaddress);
s = accept((SOCKET) port->port_handle,
(struct sockaddr *) &uxaddress, &l);
}
else {
l = sizeof(address);
s = accept((SOCKET) port->port_handle,
(struct sockaddr *) &address, &l);
}
if (s == INVALID_SOCKET) {
THREAD_ENTER;
inet_error(port, "accept", isc_net_connect_err, ERRNO);
disconnect(port);
return NULL;
}
#ifdef WIN_NT
if ((flag & SRVR_debug) || !fork(s, flag))
#else
if ((flag & SRVR_debug) || !fork())
#endif
{
THREAD_ENTER;
SOCLOSE((SOCKET) port->port_handle);
port->port_handle = (HANDLE) s;
port->port_server_flags |= SRVR_server;
return port;
}
THREAD_ENTER;
SOCLOSE(s);
}
}
Best regards,
Toon.
__________________________________________________
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around
http://mail.yahoo.com