1 |
/* |
2 |
openisis - an open implementation of the CDS/ISIS database |
3 |
Version 0.8.x (patchlevel see file Version) |
4 |
Copyright (C) 2001-2003 by Erik Grziwotz, erik@openisis.org |
5 |
|
6 |
This library is free software; you can redistribute it and/or |
7 |
modify it under the terms of the GNU Lesser General Public |
8 |
License as published by the Free Software Foundation; either |
9 |
version 2.1 of the License, or (at your option) any later version. |
10 |
|
11 |
This library is distributed in the hope that it will be useful, |
12 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 |
Lesser General Public License for more details. |
15 |
|
16 |
You should have received a copy of the GNU Lesser General Public |
17 |
License along with this library; if not, write to the Free Software |
18 |
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
19 |
|
20 |
see README for more information |
21 |
EOH */ |
22 |
|
23 |
/* |
24 |
$Id: lcli.c,v 1.13 2003/05/15 19:17:17 mawag Exp $ |
25 |
OpenIsis client side of communication |
26 |
*/ |
27 |
|
28 |
#ifndef WIN32 |
29 |
#include <sys/types.h> |
30 |
#include <sys/socket.h> /* socket etc */ |
31 |
#include <netinet/in.h> /* IPPROTO_TCP, htons */ |
32 |
#include <netdb.h> /* gethostbyname */ |
33 |
#endif |
34 |
|
35 |
#include <errno.h> |
36 |
#include <memory.h> |
37 |
#include <unistd.h> |
38 |
|
39 |
#include "openisis.h" |
40 |
#include "loi.h" |
41 |
#include "lio.h" |
42 |
#include "lcli.h" |
43 |
|
44 |
/* |
45 |
gcc -D_MAIN_ -I. -Lsto lcli.c -lopenisis && ./a.out 111 `find .` |
46 |
gcc -D_MAIN_ -DWIN32 -I. lcli.c win/libopenisis.a /mingw/lib/libwsock32.a |
47 |
*/ |
48 |
|
49 |
#define LF 10 /* LineFeed a.k.a. newline - '\n' isn't really well defined */ |
50 |
#define TAB 9 /* horizontal, that is */ |
51 |
#define VT 11 /* vertical, used as newline replacement */ |
52 |
|
53 |
#define STAT_CONT 0x01 /* not at beginning of line */ |
54 |
#define STAT_BIN 0x10 /* binary mode */ |
55 |
|
56 |
/* plain protocol |
57 |
* @return done or -err |
58 |
*/ |
59 |
int cliPlain (Ios *s, int *stat, Rec **rec) { |
60 |
Field *f; |
61 |
int l = s->b.fill - s->b.done; |
62 |
unsigned char *b = s->b.c + s->b.done; |
63 |
unsigned char *end = b+l, *v, *p; |
64 |
if ( ! l ) { /* EOF: done */ |
65 |
if ( ! (STAT_CONT & *stat) ) /* ok */ |
66 |
return 1; |
67 |
/* last field wasn't closed by LF */ |
68 |
return sMsg (ERR_INVAL, "cliPlain(%d): no EOL", LIO_FD & s->file); |
69 |
} |
70 |
if ( STAT_CONT & *stat ) |
71 |
RSPACE( *rec, l, !0 ); |
72 |
/* add text lines */ |
73 |
while ( b<end ) { |
74 |
int conti = 0; |
75 |
switch ( STAT_CONT & *stat ) { |
76 |
case 0: /* at beginning of line -- start new field */ |
77 |
if ( LF == *b ) /* empty line */ |
78 |
return 1; |
79 |
if ( TAB != *b || ! *rec || ! (*rec)->len ) { |
80 |
RADD( *rec, 0,0,end-b, !0 ); |
81 |
} |
82 |
else { /* binary mode continuation line */ |
83 |
conti = 1; |
84 |
if ( !(STAT_BIN & *stat)) { |
85 |
sMsg( LOG_INFO, "cliPlain(%d): detected binary mode", |
86 |
LIO_FD & s->file); |
87 |
*stat |= STAT_BIN; |
88 |
} |
89 |
RSPACE( *rec, end-b, !0 ); |
90 |
} |
91 |
if ( ! *rec ) |
92 |
return -ERR_NOMEM; |
93 |
*stat |= STAT_CONT; |
94 |
case STAT_CONT: /* add to last field */ |
95 |
f = (*rec)->field + (*rec)->len-1; |
96 |
v = (unsigned char*)f->val; |
97 |
p = v + f->len; |
98 |
if ( conti ) { |
99 |
*p++ = LF; |
100 |
b++; |
101 |
} |
102 |
if ( STAT_BIN & *stat ) { |
103 |
for ( ; b<end && LF != (*p = *b++); p++ ) |
104 |
; |
105 |
} else { |
106 |
for ( ; b<end && LF != (*p = *b++); p++ ) |
107 |
if ( VT == *p ) /* convert VTABs */ |
108 |
*p = LF; /* back to newlines */ |
109 |
} |
110 |
(*rec)->used += (p - v) - f->len; |
111 |
f->len = p - v; |
112 |
if ( LF == b[-1] ) { |
113 |
int ret = a2il( f->val, f->len, &f->tag ); |
114 |
if ( ret ) { |
115 |
if ( ret < f->len && TAB == v[ret] ) |
116 |
ret++; |
117 |
if ( ret < f->len ) |
118 |
memmove( v, v+ret, f->len - ret ); |
119 |
f->len -= ret; |
120 |
} |
121 |
*stat &= ~STAT_CONT; |
122 |
} |
123 |
sMsg (LOG_VERBOSE, "cliPlain(%d): fld[%2d] %3d = '%.*s'", |
124 |
LIO_FD & s->file, (*rec)->len-1, f->tag, f->len, f->val); |
125 |
} |
126 |
} |
127 |
return 0; |
128 |
} |
129 |
|
130 |
Rec* cliRead (CliChnl *ch) { |
131 |
Ios str; |
132 |
Rec *rec; |
133 |
int rt, stat; |
134 |
|
135 |
if (!ch || 0 > ch->sd) { |
136 |
return 0; |
137 |
} |
138 |
memset (&str, 0, sizeof (Ios)); |
139 |
str.file = ch->sd | LIO_IN; |
140 |
rec = 0; |
141 |
stat = 0; |
142 |
ch->err = errno = 0; |
143 |
|
144 |
while (1) { |
145 |
str.b.done = str.b.fill; |
146 |
rt = ioStdio (&str, LIO_SFILL); |
147 |
if (0 > rt) { |
148 |
ch->err = errno; |
149 |
if (rec) { |
150 |
mFree (rec); |
151 |
} |
152 |
if (!(LIO_IN & str.file)) { |
153 |
ch->sd = -1; |
154 |
} |
155 |
return 0; |
156 |
} |
157 |
rt = cliPlain (&str, &stat, &rec); |
158 |
if (rt) { |
159 |
if (0 > rt) { |
160 |
if (rec) { |
161 |
mFree (rec); |
162 |
} |
163 |
return 0; |
164 |
} |
165 |
log_rec (LOG_VERBOSE, rec, "cliRead(%d): ", 0); |
166 |
sMsg (LOG_INFO, "cliRead(%d): read #flds %d", |
167 |
ch->sd, (rec ? rec->len : -1)); |
168 |
return rec; |
169 |
} |
170 |
} |
171 |
} |
172 |
|
173 |
int cliWrite (CliChnl *ch, Rec *rec) { |
174 |
char buf [4096]; |
175 |
char *b; |
176 |
int fd, len, sl, rl, rt; |
177 |
if (!ch || 0 > ch->sd) { |
178 |
return -ERR_BADF; |
179 |
} |
180 |
ch->err = 0; |
181 |
if (!rec || !rec->len) { |
182 |
return 0; |
183 |
} |
184 |
len = sizeof (buf); |
185 |
b = rSerA (rec, buf, &len); |
186 |
if (!b) { |
187 |
return sMsg (ERR_NOMEM, "cliWrite(%d): write (%d)", ch->sd, rec->used); |
188 |
} |
189 |
fd = ch->sd | LIO_OUT; |
190 |
for (sl = rt = errno = 0, rl = len; rl; sl += rt, rl -= rt) { |
191 |
rt = lio_write (&fd, b + sl, (unsigned)rl); |
192 |
ch->err = errno; |
193 |
if (0 > rt) { |
194 |
if (!(LIO_OUT & fd)) { |
195 |
ch->sd = -1; |
196 |
} |
197 |
break; |
198 |
} |
199 |
} |
200 |
if (b != buf) { |
201 |
mFree (b); |
202 |
} |
203 |
if (0 > rt) { |
204 |
return sMsg (ERR_IO, "cliWrite(%d): write (%d) = %d(%d)", |
205 |
len, rt, ch->err); |
206 |
} |
207 |
sMsg (LOG_INFO, "cliWrite(%d): wrote %d,%d", ch->sd, rec->len, len); |
208 |
return 0; |
209 |
} |
210 |
|
211 |
int cliConnect (CliChnl *ch, const char *hname, int port) { |
212 |
#ifdef WIN32 |
213 |
return sMsg (ERR_TRASH, "cliConnect: operation not supported"); |
214 |
#else |
215 |
struct sockaddr_in addr; |
216 |
struct hostent *hostp; |
217 |
int rt; |
218 |
if (!ch) { |
219 |
return sMsg (ERR_IDIOT, "cliConnect: null channel"); |
220 |
} |
221 |
memset (ch, 0, sizeof (CliChnl)); |
222 |
rt = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); |
223 |
if (0 > rt) { |
224 |
ch->err = errno; |
225 |
return sMsg (ERR_IO, "cliConnect: cannot create socket: %d", errno); |
226 |
} |
227 |
ch->sd = rt; |
228 |
memset (&addr, 0, sizeof (addr)); |
229 |
if (hname) { |
230 |
hostp = gethostbyname (hname); |
231 |
if (! hostp) { |
232 |
ch->err = errno; |
233 |
return sMsg (ERR_INVAL, "cliConnect: unknown host <%s>: %d", |
234 |
hname, errno); |
235 |
} |
236 |
addr.sin_family = hostp->h_addrtype; /* already network-byte-order */ |
237 |
addr.sin_addr.s_addr = *(long*)(*(hostp->h_addr_list)); /* dto */ |
238 |
} |
239 |
else { |
240 |
addr.sin_family = AF_INET; |
241 |
addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); |
242 |
} |
243 |
addr.sin_port = htons (port); |
244 |
rt = connect (ch->sd, &addr, sizeof (addr)); |
245 |
if (0 > rt) { |
246 |
ch->err = errno; |
247 |
return sMsg (ERR_IO, "cliConnect: cannot connect to %s:%d: %d", |
248 |
(hname ? hname : "localhost"), port, errno); |
249 |
} |
250 |
return 0; |
251 |
#endif |
252 |
} |
253 |
|
254 |
void cliClose (CliChnl *ch) { |
255 |
if (ch && 0 <= ch->sd) { |
256 |
close (ch->sd); |
257 |
ch->sd = -1; |
258 |
} |
259 |
} |
260 |
|
261 |
/* ----------------------------------------------------------------------- |
262 |
* test |
263 |
*/ |
264 |
#ifdef _MAIN_ |
265 |
|
266 |
#include <stdio.h> /* printf */ |
267 |
#include <stdlib.h> /* atoi */ |
268 |
#include <string.h> /* strerror */ |
269 |
#include "luti.h" /* log_rec */ |
270 |
|
271 |
void cmp (Rec *in, Rec *out) { |
272 |
Field *F1, *F2; |
273 |
int l1 = in->len; |
274 |
int l2 = out->len; |
275 |
int j, k; |
276 |
for (j = k = 0, F1 = in->field, F2 = out->field; l2 > k; ++k, ++F2) { |
277 |
if (0 > F2->tag) { |
278 |
continue; |
279 |
} |
280 |
if (l1 <= j) { |
281 |
printf ("ERR too many answers %d %d\n", j, k); |
282 |
log_rec (0, in, "ERR IN ", 0); |
283 |
log_rec (0, out, "ERR OUT ", 0); |
284 |
exit (1); |
285 |
} |
286 |
if (F1->tag != F2->tag) { |
287 |
printf ("ERR tag %d mismatch %d %d\n", j, F1->tag, F2->tag); |
288 |
log_rec (0, in, "ERR IN ", 0); |
289 |
log_rec (0, out, "ERR OUT ", 0); |
290 |
exit (1); |
291 |
} |
292 |
if (F1->len != F2->len) { |
293 |
printf ("ERR len %d mismatch %d %d\n", j, F1->tag, F2->tag); |
294 |
log_rec (0, in, "ERR IN ", 0); |
295 |
log_rec (0, out, "ERR OUT ", 0); |
296 |
exit (1); |
297 |
} |
298 |
if (memcmp (F1->val, F2->val, F1->len)) { |
299 |
printf ("ERR val %d mismatch\n", j); |
300 |
log_rec (0, in, "ERR IN ", 0); |
301 |
log_rec (0, out, "ERR OUT ", 0); |
302 |
exit (1); |
303 |
} |
304 |
++j; |
305 |
++F1; |
306 |
} |
307 |
} |
308 |
|
309 |
int loop (CliChnl *ch, Rec *demo, int fail) { |
310 |
Rec *rsp; |
311 |
int rt; |
312 |
rt = cliWrite (ch, demo); |
313 |
if (0 > rt) { |
314 |
printf ("%s write %d %s\n", (fail ? "ERR":"WARN"), |
315 |
ch->err, strerror (ch->err)); |
316 |
if (!fail) { |
317 |
return -1; |
318 |
} |
319 |
exit (1); |
320 |
} |
321 |
rsp = cliRead (ch); |
322 |
if (! rsp) { |
323 |
printf ("%s read %d %s\n", (fail ? "ERR":"WARN"), |
324 |
ch->err, strerror (ch->err)); |
325 |
if (!fail) { |
326 |
return -1; |
327 |
} |
328 |
exit (1); |
329 |
} |
330 |
cmp (demo, rsp); |
331 |
mFree (rsp); |
332 |
return 0; |
333 |
} |
334 |
|
335 |
int main (int argc, char **argv) { |
336 |
CliChnl ch; |
337 |
char *hname = 0; |
338 |
int port = 0; |
339 |
Rec *demo = 0; |
340 |
int num = 0; |
341 |
int j; |
342 |
|
343 |
for (j = 1; argc > j; ++j) { |
344 |
if (*argv[j] == '-') { |
345 |
if (argv[j][1] == 'h') { |
346 |
hname = argv[j] + 2; |
347 |
continue; |
348 |
} |
349 |
if (argv[j][1] == 'p') { |
350 |
port = atoi (argv[j] + 2); |
351 |
continue; |
352 |
} |
353 |
} |
354 |
num = atoi (argv[j]); |
355 |
break; |
356 |
} |
357 |
|
358 |
if (0 >= num) { |
359 |
num = 1; |
360 |
} |
361 |
|
362 |
for (j = argc; 0 <= --j; ) { |
363 |
RADDS (demo, j, argv[j], !0); |
364 |
} |
365 |
|
366 |
if (0 >= port) { |
367 |
port = 8080; |
368 |
} |
369 |
if (cliConnect (&ch, hname, port)) { |
370 |
printf ("ERR connect %d %s\n", ch.err, strerror (ch.err)); |
371 |
exit (1); |
372 |
} |
373 |
|
374 |
j = num; |
375 |
while (1) { |
376 |
if (loop (&ch, demo, 0)) { |
377 |
sleep (15); |
378 |
if (cliConnect (&ch, hname, port)) { |
379 |
printf ("ERR reconnect %d %s\n", ch.err, strerror (ch.err)); |
380 |
exit (1); |
381 |
} |
382 |
loop (&ch, demo, !0); |
383 |
} |
384 |
if (! --j) { |
385 |
break; |
386 |
} |
387 |
if (! (j % 10)) { |
388 |
printf ("loop %d ...\n", j); |
389 |
} |
390 |
} |
391 |
|
392 |
cliClose (&ch); |
393 |
printf ("ok.\n"); |
394 |
return 0; |
395 |
} |
396 |
|
397 |
#endif /* _MAIN_ */ |
398 |
|