/*
* tcp.c: TCP communicating code
*
* $Id: tcp.c,v 1.4 2000/07/19 06:26:22 sc Exp $
*/
/* Copyright (C) 1999-2000 Sergey Chernikov (sc@ivvs.ul.ru)
*
* Authors: Sergey Chernikov <sc@ivvs.ul.ru>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
*/
#include "grn_consts.h"
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <strings.h>
#define _POSIX_SOURCE
#include <signal.h>
#include <sys/signal.h>
#undef _POSIX_SOURCE
#include <glib.h>
#include "tcp.h"
#include "grn_util.h"
#define BUFSIZE 4096
void grn_socket_init(grn_socket *socket)
{
socket->fd = -1;
socket->error = FALSE;
socket->closed = TRUE;
socket->addr = NULL;
socket->service = NULL;
socket->err_msg = NULL;
socket->buf = g_malloc(BUFSIZE);
socket->endbuf = socket->buf;
socket->inptr = socket->buf;
socket->bytes_rx = 0L;
socket->bytes_tx = 0L;
}
grn_socket *grn_socket_new(void)
{
grn_socket *ret = g_new(grn_socket, 1);
grn_socket_init(ret);
return ret;
}
void grn_socket_free(grn_socket **socket)
{
if (! socket) return;
if (! *socket) return;
g_print("%s:%s\tRX:%lu\tTX:%lu\n", (*socket)->addr, (*socket)->service,
(*socket)->bytes_rx, (*socket)->bytes_tx);
if (! (*socket)->closed) grn_socket_close(*socket);
if ((*socket)->addr) str_free(&((*socket)->addr));
if ((*socket)->service) str_free(&((*socket)->service));
if ((*socket)->buf) str_free(&((*socket)->buf));
if ((*socket)->err_msg) str_free(&((*socket)->err_msg));
g_free(*socket); *socket = NULL;
}
void grn_socket_destroy(grn_socket *socket)
{ grn_socket_free(&socket); }
void grn_socket_open(grn_socket *sock)
{
struct sockaddr_in serv_addr;
struct servent serv_info, *sp;
struct hostent host_info, *hp;
struct sigaction act, oact;
int on;
unsigned long inaddr;
g_return_if_fail(sock != NULL);
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
sock->error = FALSE;
if ((! str_check(sock->addr)) || (! str_check(sock->service)))
{
grn_socket_set_error(sock, _("Invalid input parameters"));
return;
}
if ((sp = getservbyname(sock->service, "tcp")) == NULL)
{
grn_socket_set_error(sock, _("tcp_open(): getservbyname() failed"));
return;
}
serv_info = *sp;
serv_addr.sin_port = sp->s_port;
if ((inaddr = inet_addr(sock->addr)) != -1)
{
bcopy((char *) &inaddr, (char *) &serv_addr.sin_addr, sizeof(inaddr));
host_info.h_name = NULL;
}
else {
while ((hp = gethostbyname(sock->addr)) == NULL)
{
grn_socket_set_error(sock, _("tcp_open(): gethostbyname() failed"));
return;
}
host_info = *hp;
bcopy(hp->h_addr, (char *) &serv_addr.sin_addr, hp->h_length);
}
if ((sock->fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
grn_socket_set_error(sock, _("tcp_open(): socket() failed"));
return;
}
if (connect(sock->fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
{
grn_socket_close(sock);
grn_socket_set_error(sock, _("tcp_open(): connect() failed"));
return;
}
on = 1;
if (setsockopt(sock->fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on)) < 0)
{
grn_socket_close(sock);
grn_socket_set_error(sock, _("tcp_open(): setsockopt() failed"));
return;
}
act.sa_handler = SIG_IGN;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGPIPE, &act, &oact);
sock->closed = FALSE;
}
void grn_socket_close(grn_socket *socket)
{
int rc;
g_return_if_fail(socket != NULL);
socket->error = FALSE;
if (socket->closed)
{
grn_socket_set_error(socket, _("Already closed"));
return;
}
rc = close(socket->fd);
if (rc == 0) socket->closed = TRUE;
if (rc == -1)
{
switch (errno)
{
case EBADF:
grn_socket_set_error(socket, _("Invalid descriptor"));
break;
case EINTR:
grn_socket_set_error(socket, _("Interrupted by user"));
break;
default: grn_socket_set_error(socket, _("Undefined error"));
}
}
}
gint grn_socket_read_char(grn_socket *socket, gchar *c)
{
socket->error = FALSE;
if (socket->closed)
{
grn_socket_set_error(socket, _("The socket is closed"));
return (-1);
}
if (socket->inptr == socket->endbuf)
{
int read_count=0, retval=0;
struct timeval tv;
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(socket->fd, &rfds);
tv.tv_sec = 120; tv.tv_usec = 0;
retval = select(socket->fd + 1, &rfds, NULL, NULL, &tv);
if (retval <=0)
{
grn_socket_set_error(socket, _("select() timeout"));
return (-1);
}
read_count = read(socket->fd, socket->buf, BUFSIZE);
socket->endbuf = socket->buf + read_count;
socket->inptr = socket->buf;
if (read_count == 0) return 0;
if (read_count < 0)
{
switch (errno)
{
case EBADF:
grn_socket_set_error(socket, _("Invalid descriptor"));
break;
case EINTR:
grn_socket_set_error(socket, _("Interrupted by user"));
break;
case EFAULT:
case EINVAL:
grn_socket_set_error(socket, _("Invalid pointer"));
break;
case EIO:
case EAGAIN:
grn_socket_set_error(socket, _("I/O error"));
break;
default: grn_socket_set_error(socket, _("Undefined error"));
}
return read_count;
}
}
*c = *(socket->inptr)++;
socket->bytes_rx++;
return 1;
}
gchar *grn_socket_get(grn_socket *socket)
{
gchar *line_buffer;
int maxline = 128;
char c;
int i, done=0;
g_return_val_if_fail(socket != NULL, NULL);
socket->error = FALSE;
if (socket->closed)
{
grn_socket_set_error(socket, _("The socket is closed"));
return NULL;
}
line_buffer = (gchar *) g_malloc(maxline);
for (i=0; !done; i++)
{
if (grn_socket_read_char(socket, &c) != 1)
{
g_free(line_buffer);
return NULL;
}
if (i >= (maxline - 1))
{
maxline *= 2;
line_buffer = (gchar *) g_realloc(line_buffer, maxline);
}
line_buffer[i] = c;
if (c == '\n') done++;
}
line_buffer[i] = '\0';
if (i == 0 || line_buffer[i-1] != '\n')
{
g_free(line_buffer);
return NULL;
}
return line_buffer;
}
gint grn_socket_put(grn_socket *socket, gchar *line)
{
gint nleft, nwritten, nbytes;
g_return_val_if_fail(socket != NULL, (-1));
socket->error = FALSE;
if (socket->closed)
{
grn_socket_set_error(socket, _("The socket is closed"));
return (-1);
}
nbytes = strlen(line); nleft = nbytes;
while (nleft > 0)
{
nwritten = write(socket->fd, line, nleft);
if (nwritten < 0)
{
switch (errno)
{
case EBADF:
grn_socket_set_error(socket, _("Invalid descriptor"));
break;
case EPIPE:
grn_socket_set_error(socket, _("Invalid pipe/socket"));
break;
case EFBIG:
grn_socket_set_error(socket, _("File size limit exceeded"));
break;
case EFAULT:
case EINVAL:
grn_socket_set_error(socket, _("Invalid pointer"));
break;
case ENOSPC:
grn_socket_set_error(socket, _("No free space on FS"));
break;
case EIO:
case EAGAIN:
grn_socket_set_error(socket, _("I/O error"));
break;
default: grn_socket_set_error(socket, _("Undefined error"));
}
return (-1);
}
nleft -= nwritten;
line += nwritten;
socket->bytes_tx += nwritten;
}
return (nbytes - nleft);
}
gint grn_socket_put_va(grn_socket *sock, const gchar *format, ...)
{
va_list args;
gchar *ptr;
gint nwritten;
g_return_val_if_fail(format != NULL, (-1));
va_start(args, format);
ptr = g_strdup_vprintf(format, args);
va_end(args);
nwritten = grn_socket_put(sock, ptr);
str_free(&ptr);
return nwritten;
}
void grn_socket_set_error(grn_socket *socket, gchar *msg)
{
g_return_if_fail(socket != NULL);
if (socket->err_msg) str_free(&(socket->err_msg));
socket->error = TRUE;
if (str_check(msg)) socket->err_msg = g_strdup(msg);
else socket->err_msg = NULL;
}
syntax highlighted by Code2HTML, v. 0.9.1