1 |
dpavlin |
237 |
/* |
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 |
|
|
|