/*
openisis - an open implementation of the CDS/ISIS database
Version 0.8.x (patchlevel see file Version)
Copyright (C) 2001-2003 by Erik Grziwotz, erik@openisis.org
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
see README for more information
EOH */
/*
$Id: lcli.c,v 1.13 2003/05/15 19:17:17 mawag Exp $
OpenIsis client side of communication
*/
#ifndef WIN32
#include <sys/types.h>
#include <sys/socket.h> /* socket etc */
#include <netinet/in.h> /* IPPROTO_TCP, htons */
#include <netdb.h> /* gethostbyname */
#endif
#include <errno.h>
#include <memory.h>
#include <unistd.h>
#include "openisis.h"
#include "loi.h"
#include "lio.h"
#include "lcli.h"
/*
gcc -D_MAIN_ -I. -Lsto lcli.c -lopenisis && ./a.out 111 `find .`
gcc -D_MAIN_ -DWIN32 -I. lcli.c win/libopenisis.a /mingw/lib/libwsock32.a
*/
#define LF 10 /* LineFeed a.k.a. newline - '\n' isn't really well defined */
#define TAB 9 /* horizontal, that is */
#define VT 11 /* vertical, used as newline replacement */
#define STAT_CONT 0x01 /* not at beginning of line */
#define STAT_BIN 0x10 /* binary mode */
/* plain protocol
* @return done or -err
*/
int cliPlain (Ios *s, int *stat, Rec **rec) {
Field *f;
int l = s->b.fill - s->b.done;
unsigned char *b = s->b.c + s->b.done;
unsigned char *end = b+l, *v, *p;
if ( ! l ) { /* EOF: done */
if ( ! (STAT_CONT & *stat) ) /* ok */
return 1;
/* last field wasn't closed by LF */
return sMsg (ERR_INVAL, "cliPlain(%d): no EOL", LIO_FD & s->file);
}
if ( STAT_CONT & *stat )
RSPACE( *rec, l, !0 );
/* add text lines */
while ( b<end ) {
int conti = 0;
switch ( STAT_CONT & *stat ) {
case 0: /* at beginning of line -- start new field */
if ( LF == *b ) /* empty line */
return 1;
if ( TAB != *b || ! *rec || ! (*rec)->len ) {
RADD( *rec, 0,0,end-b, !0 );
}
else { /* binary mode continuation line */
conti = 1;
if ( !(STAT_BIN & *stat)) {
sMsg( LOG_INFO, "cliPlain(%d): detected binary mode",
LIO_FD & s->file);
*stat |= STAT_BIN;
}
RSPACE( *rec, end-b, !0 );
}
if ( ! *rec )
return -ERR_NOMEM;
*stat |= STAT_CONT;
case STAT_CONT: /* add to last field */
f = (*rec)->field + (*rec)->len-1;
v = (unsigned char*)f->val;
p = v + f->len;
if ( conti ) {
*p++ = LF;
b++;
}
if ( STAT_BIN & *stat ) {
for ( ; b<end && LF != (*p = *b++); p++ )
;
} else {
for ( ; b<end && LF != (*p = *b++); p++ )
if ( VT == *p ) /* convert VTABs */
*p = LF; /* back to newlines */
}
(*rec)->used += (p - v) - f->len;
f->len = p - v;
if ( LF == b[-1] ) {
int ret = a2il( f->val, f->len, &f->tag );
if ( ret ) {
if ( ret < f->len && TAB == v[ret] )
ret++;
if ( ret < f->len )
memmove( v, v+ret, f->len - ret );
f->len -= ret;
}
*stat &= ~STAT_CONT;
}
sMsg (LOG_VERBOSE, "cliPlain(%d): fld[%2d] %3d = '%.*s'",
LIO_FD & s->file, (*rec)->len-1, f->tag, f->len, f->val);
}
}
return 0;
}
Rec* cliRead (CliChnl *ch) {
Ios str;
Rec *rec;
int rt, stat;
if (!ch || 0 > ch->sd) {
return 0;
}
memset (&str, 0, sizeof (Ios));
str.file = ch->sd | LIO_IN;
rec = 0;
stat = 0;
ch->err = errno = 0;
while (1) {
str.b.done = str.b.fill;
rt = ioStdio (&str, LIO_SFILL);
if (0 > rt) {
ch->err = errno;
if (rec) {
mFree (rec);
}
if (!(LIO_IN & str.file)) {
ch->sd = -1;
}
return 0;
}
rt = cliPlain (&str, &stat, &rec);
if (rt) {
if (0 > rt) {
if (rec) {
mFree (rec);
}
return 0;
}
log_rec (LOG_VERBOSE, rec, "cliRead(%d): ", 0);
sMsg (LOG_INFO, "cliRead(%d): read #flds %d",
ch->sd, (rec ? rec->len : -1));
return rec;
}
}
}
int cliWrite (CliChnl *ch, Rec *rec) {
char buf [4096];
char *b;
int fd, len, sl, rl, rt;
if (!ch || 0 > ch->sd) {
return -ERR_BADF;
}
ch->err = 0;
if (!rec || !rec->len) {
return 0;
}
len = sizeof (buf);
b = rSerA (rec, buf, &len);
if (!b) {
return sMsg (ERR_NOMEM, "cliWrite(%d): write (%d)", ch->sd, rec->used);
}
fd = ch->sd | LIO_OUT;
for (sl = rt = errno = 0, rl = len; rl; sl += rt, rl -= rt) {
rt = lio_write (&fd, b + sl, (unsigned)rl);
ch->err = errno;
if (0 > rt) {
if (!(LIO_OUT & fd)) {
ch->sd = -1;
}
break;
}
}
if (b != buf) {
mFree (b);
}
if (0 > rt) {
return sMsg (ERR_IO, "cliWrite(%d): write (%d) = %d(%d)",
len, rt, ch->err);
}
sMsg (LOG_INFO, "cliWrite(%d): wrote %d,%d", ch->sd, rec->len, len);
return 0;
}
int cliConnect (CliChnl *ch, const char *hname, int port) {
#ifdef WIN32
return sMsg (ERR_TRASH, "cliConnect: operation not supported");
#else
struct sockaddr_in addr;
struct hostent *hostp;
int rt;
if (!ch) {
return sMsg (ERR_IDIOT, "cliConnect: null channel");
}
memset (ch, 0, sizeof (CliChnl));
rt = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (0 > rt) {
ch->err = errno;
return sMsg (ERR_IO, "cliConnect: cannot create socket: %d", errno);
}
ch->sd = rt;
memset (&addr, 0, sizeof (addr));
if (hname) {
hostp = gethostbyname (hname);
if (! hostp) {
ch->err = errno;
return sMsg (ERR_INVAL, "cliConnect: unknown host <%s>: %d",
hname, errno);
}
addr.sin_family = hostp->h_addrtype; /* already network-byte-order */
addr.sin_addr.s_addr = *(long*)(*(hostp->h_addr_list)); /* dto */
}
else {
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
}
addr.sin_port = htons (port);
rt = connect (ch->sd, &addr, sizeof (addr));
if (0 > rt) {
ch->err = errno;
return sMsg (ERR_IO, "cliConnect: cannot connect to %s:%d: %d",
(hname ? hname : "localhost"), port, errno);
}
return 0;
#endif
}
void cliClose (CliChnl *ch) {
if (ch && 0 <= ch->sd) {
close (ch->sd);
ch->sd = -1;
}
}
/* -----------------------------------------------------------------------
* test
*/
#ifdef _MAIN_
#include <stdio.h> /* printf */
#include <stdlib.h> /* atoi */
#include <string.h> /* strerror */
#include "luti.h" /* log_rec */
void cmp (Rec *in, Rec *out) {
Field *F1, *F2;
int l1 = in->len;
int l2 = out->len;
int j, k;
for (j = k = 0, F1 = in->field, F2 = out->field; l2 > k; ++k, ++F2) {
if (0 > F2->tag) {
continue;
}
if (l1 <= j) {
printf ("ERR too many answers %d %d\n", j, k);
log_rec (0, in, "ERR IN ", 0);
log_rec (0, out, "ERR OUT ", 0);
exit (1);
}
if (F1->tag != F2->tag) {
printf ("ERR tag %d mismatch %d %d\n", j, F1->tag, F2->tag);
log_rec (0, in, "ERR IN ", 0);
log_rec (0, out, "ERR OUT ", 0);
exit (1);
}
if (F1->len != F2->len) {
printf ("ERR len %d mismatch %d %d\n", j, F1->tag, F2->tag);
log_rec (0, in, "ERR IN ", 0);
log_rec (0, out, "ERR OUT ", 0);
exit (1);
}
if (memcmp (F1->val, F2->val, F1->len)) {
printf ("ERR val %d mismatch\n", j);
log_rec (0, in, "ERR IN ", 0);
log_rec (0, out, "ERR OUT ", 0);
exit (1);
}
++j;
++F1;
}
}
int loop (CliChnl *ch, Rec *demo, int fail) {
Rec *rsp;
int rt;
rt = cliWrite (ch, demo);
if (0 > rt) {
printf ("%s write %d %s\n", (fail ? "ERR":"WARN"),
ch->err, strerror (ch->err));
if (!fail) {
return -1;
}
exit (1);
}
rsp = cliRead (ch);
if (! rsp) {
printf ("%s read %d %s\n", (fail ? "ERR":"WARN"),
ch->err, strerror (ch->err));
if (!fail) {
return -1;
}
exit (1);
}
cmp (demo, rsp);
mFree (rsp);
return 0;
}
int main (int argc, char **argv) {
CliChnl ch;
char *hname = 0;
int port = 0;
Rec *demo = 0;
int num = 0;
int j;
for (j = 1; argc > j; ++j) {
if (*argv[j] == '-') {
if (argv[j][1] == 'h') {
hname = argv[j] + 2;
continue;
}
if (argv[j][1] == 'p') {
port = atoi (argv[j] + 2);
continue;
}
}
num = atoi (argv[j]);
break;
}
if (0 >= num) {
num = 1;
}
for (j = argc; 0 <= --j; ) {
RADDS (demo, j, argv[j], !0);
}
if (0 >= port) {
port = 8080;
}
if (cliConnect (&ch, hname, port)) {
printf ("ERR connect %d %s\n", ch.err, strerror (ch.err));
exit (1);
}
j = num;
while (1) {
if (loop (&ch, demo, 0)) {
sleep (15);
if (cliConnect (&ch, hname, port)) {
printf ("ERR reconnect %d %s\n", ch.err, strerror (ch.err));
exit (1);
}
loop (&ch, demo, !0);
}
if (! --j) {
break;
}
if (! (j % 10)) {
printf ("loop %d ...\n", j);
}
}
cliClose (&ch);
printf ("ok.\n");
return 0;
}
#endif /* _MAIN_ */