gdp / apps / gdp-reader.c @ master
History | View | Annotate | Download (16.7 KB)
1 |
/* vim: set ai sw=4 sts=4 ts=4 : */
|
---|---|
2 |
|
3 |
/*
|
4 |
** GDP-READER --- read and print records from a GDP log
|
5 |
**
|
6 |
** Unfortunately it isn't that simple, since it is possible to read
|
7 |
** using all the internal mechanisms. The -c, -m, and -s flags
|
8 |
** control which approach is being used.
|
9 |
**
|
10 |
** There are two ways of reading. The first is to get individual
|
11 |
** records in a loop (as implemented in do_simpleread), and the
|
12 |
** second is to request a batch of records (as implemented in
|
13 |
** do_async_read or do_subscribe); these are returned as events
|
14 |
** that are collected after the initial command completes or as
|
15 |
** callbacks that are invoked in a separate thread. There are two
|
16 |
** interfaces for the event/callback techniques; one only reads
|
17 |
** existing data, and the other ("subscriptions") will wait for
|
18 |
** data to be appended by another client.
|
19 |
**
|
20 |
** ----- BEGIN LICENSE BLOCK -----
|
21 |
** Applications for the Global Data Plane
|
22 |
** From the Ubiquitous Swarm Lab, 490 Cory Hall, U.C. Berkeley.
|
23 |
**
|
24 |
** Copyright (c) 2015-2019, Regents of the University of California.
|
25 |
** All rights reserved.
|
26 |
**
|
27 |
** Permission is hereby granted, without written agreement and without
|
28 |
** license or royalty fees, to use, copy, modify, and distribute this
|
29 |
** software and its documentation for any purpose, provided that the above
|
30 |
** copyright notice and the following two paragraphs appear in all copies
|
31 |
** of this software.
|
32 |
**
|
33 |
** IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
|
34 |
** SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST
|
35 |
** PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
|
36 |
** EVEN IF REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
37 |
**
|
38 |
** REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
|
39 |
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
40 |
** FOR A PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION,
|
41 |
** IF ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO
|
42 |
** OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
|
43 |
** OR MODIFICATIONS.
|
44 |
** ----- END LICENSE BLOCK -----
|
45 |
*/
|
46 |
|
47 |
#include <ep/ep.h> |
48 |
#include <ep/ep_dbg.h> |
49 |
#include <ep/ep_app.h> |
50 |
#include <ep/ep_time.h> |
51 |
#include <gdp/gdp.h> |
52 |
#include <event2/buffer.h> |
53 |
|
54 |
#include <unistd.h> |
55 |
#include <errno.h> |
56 |
#include <errno.h> |
57 |
#include <getopt.h> |
58 |
#include <string.h> |
59 |
#include <sysexits.h> |
60 |
|
61 |
static EP_DBG Dbg = EP_DBG_INIT("gdp-reader", "GDP Reader Application"); |
62 |
|
63 |
#ifndef USE_GETDATE
|
64 |
# define USE_GETDATE 1 |
65 |
#endif
|
66 |
|
67 |
|
68 |
/*
|
69 |
** DO_LOG --- log a timestamp (for performance checking).
|
70 |
*/
|
71 |
|
72 |
FILE *LogFile; |
73 |
bool TextData = false; // set if data should be displayed as text |
74 |
bool BinaryData = false; // set if data should be output as binary |
75 |
bool PrintSig = false; // set if signature should be printed |
76 |
bool Quiet = false; // don't print metadata |
77 |
int NRead = 0; // number of datums read |
78 |
FILE *OutFile; // data output file
|
79 |
|
80 |
void
|
81 |
do_log(const char *tag) |
82 |
{ |
83 |
struct timeval tv;
|
84 |
|
85 |
if (LogFile == NULL) |
86 |
return;
|
87 |
gettimeofday(&tv, NULL);
|
88 |
fprintf(LogFile, "%s %ld.%06ld\n", tag, tv.tv_sec, (long) tv.tv_usec); |
89 |
} |
90 |
|
91 |
#define LOG(tag) { if (LogFile != NULL) do_log(tag); } |
92 |
|
93 |
/*
|
94 |
** PRINTDATUM --- just print out a datum
|
95 |
*/
|
96 |
|
97 |
void
|
98 |
printdatum(gdp_datum_t *datum, FILE *fp) |
99 |
{ |
100 |
uint32_t prflags = 0;
|
101 |
|
102 |
// logging for simple performance testing
|
103 |
LOG("R");
|
104 |
|
105 |
if (TextData)
|
106 |
prflags |= GDP_DATUM_PRTEXT; |
107 |
if (BinaryData)
|
108 |
prflags |= GDP_DATUM_PRBINARY; |
109 |
if (PrintSig)
|
110 |
prflags |= GDP_DATUM_PRSIG; |
111 |
if (Quiet)
|
112 |
prflags |= GDP_DATUM_PRQUIET; |
113 |
flockfile(fp); |
114 |
if (!Quiet)
|
115 |
fprintf(fp, " >>> ");
|
116 |
gdp_datum_print(datum, fp, prflags); |
117 |
funlockfile(fp); |
118 |
NRead++; |
119 |
} |
120 |
|
121 |
|
122 |
/*
|
123 |
** DO_SIMPLEREAD --- read from a log using the one-record-at-a-time call
|
124 |
*/
|
125 |
|
126 |
EP_STAT |
127 |
do_simpleread(gdp_gin_t *gin, |
128 |
gdp_recno_t firstrec, |
129 |
const char *dtstr, |
130 |
int numrecs)
|
131 |
{ |
132 |
EP_STAT estat = EP_STAT_OK; |
133 |
gdp_datum_t *datum = gdp_datum_new(); |
134 |
|
135 |
// change the "infinity" sentinel to make the loop easier
|
136 |
if (numrecs == 0) |
137 |
numrecs = -1;
|
138 |
|
139 |
// can't start reading before first record (but negative makes sense)
|
140 |
if (firstrec == 0) |
141 |
firstrec = 1;
|
142 |
|
143 |
// are we reading by record number or by timestamp?
|
144 |
if (dtstr == NULL) |
145 |
{ |
146 |
// record number
|
147 |
estat = gdp_gin_read_by_recno(gin, firstrec, datum); |
148 |
} |
149 |
else
|
150 |
{ |
151 |
// timestamp
|
152 |
EP_TIME_SPEC ts; |
153 |
|
154 |
estat = ep_time_parse(dtstr, &ts, EP_TIME_USE_LOCALTIME); |
155 |
if (!EP_STAT_ISOK(estat))
|
156 |
{ |
157 |
ep_app_message(estat, |
158 |
"Cannot convert date/time string \"%s\"",
|
159 |
dtstr); |
160 |
goto done;
|
161 |
} |
162 |
|
163 |
estat = gdp_gin_read_by_ts(gin, &ts, datum); |
164 |
} |
165 |
|
166 |
// start reading data, one record at a time
|
167 |
while (EP_STAT_ISOK(estat) && (numrecs < 0 || --numrecs > 0)) |
168 |
{ |
169 |
gdp_recno_t recno; |
170 |
|
171 |
// print the previous value
|
172 |
printdatum(datum, OutFile); |
173 |
|
174 |
// flush any left over data
|
175 |
if (gdp_buf_reset(gdp_datum_getbuf(datum)) < 0) |
176 |
{ |
177 |
char nbuf[40]; |
178 |
|
179 |
(void) (0 == strerror_r(errno, nbuf, sizeof nbuf)); |
180 |
ep_app_warn("buffer reset failed: %s", nbuf);
|
181 |
} |
182 |
|
183 |
// move to next record
|
184 |
recno = gdp_datum_getrecno(datum) + 1;
|
185 |
estat = gdp_gin_read_by_recno(gin, recno, datum); |
186 |
} |
187 |
|
188 |
// print the final value
|
189 |
if (EP_STAT_ISOK(estat))
|
190 |
printdatum(datum, OutFile); |
191 |
|
192 |
// end of data is returned as a "not found" error: turn it into a warning
|
193 |
// to avoid scaring the unsuspecting user
|
194 |
if (EP_STAT_IS_SAME(estat, GDP_STAT_NAK_NOTFOUND))
|
195 |
estat = EP_STAT_END_OF_FILE; |
196 |
|
197 |
done:
|
198 |
gdp_datum_free(datum); |
199 |
return estat;
|
200 |
} |
201 |
|
202 |
|
203 |
/*
|
204 |
** Async Read and Subscriptions.
|
205 |
*/
|
206 |
|
207 |
EP_STAT |
208 |
print_event(gdp_event_t *gev) |
209 |
{ |
210 |
EP_STAT estat = gdp_event_getstat(gev); |
211 |
const char *op = gdp_event_getudata(gev); |
212 |
|
213 |
if (op == NULL) |
214 |
op = "Unknown";
|
215 |
|
216 |
// decode it
|
217 |
switch (gdp_event_gettype(gev))
|
218 |
{ |
219 |
case GDP_EVENT_DATA:
|
220 |
// this event contains a data return
|
221 |
LOG("S");
|
222 |
printdatum(gdp_event_getdatum(gev), OutFile); |
223 |
break;
|
224 |
|
225 |
case GDP_EVENT_DONE:
|
226 |
// "end of subscription": no more data will be returned
|
227 |
if (!Quiet)
|
228 |
{ |
229 |
ep_app_info("End of %s", op);
|
230 |
} |
231 |
estat = EP_STAT_END_OF_FILE; |
232 |
break;
|
233 |
|
234 |
case GDP_EVENT_SHUTDOWN:
|
235 |
// log daemon has shut down, meaning we lose our subscription
|
236 |
estat = GDP_STAT_DEAD_DAEMON; |
237 |
ep_app_message(estat, "%s terminating because of log daemon shutdown", op);
|
238 |
break;
|
239 |
|
240 |
case GDP_EVENT_CREATED:
|
241 |
ep_app_info("Successful append, create, or similar");
|
242 |
break;
|
243 |
|
244 |
default:
|
245 |
// let the library handle this
|
246 |
gdp_event_print(gev, stderr); |
247 |
break;
|
248 |
} |
249 |
|
250 |
if (EP_STAT_ISFAIL(estat)) // ERROR or higher severity |
251 |
{ |
252 |
char ebuf[100]; |
253 |
fprintf(stderr, " STATUS: %s\n",
|
254 |
ep_stat_tostr(estat, ebuf, sizeof ebuf));
|
255 |
} |
256 |
return estat;
|
257 |
} |
258 |
|
259 |
|
260 |
void
|
261 |
read_cb(gdp_event_t *gev) |
262 |
{ |
263 |
(void) print_event(gev);
|
264 |
gdp_event_free(gev); |
265 |
} |
266 |
|
267 |
|
268 |
/*
|
269 |
** DO_SUBSCRIBE --- subscribe to a log, possibly using callbacks
|
270 |
**
|
271 |
** This routine handles calls that return multiple values via the
|
272 |
** event interface. They might include subscriptions.
|
273 |
*/
|
274 |
|
275 |
EP_STAT |
276 |
do_subscribe(gdp_gin_t *gin, |
277 |
gdp_recno_t firstrec, |
278 |
const char *dtstr, |
279 |
int32_t numrecs, |
280 |
bool use_callbacks)
|
281 |
{ |
282 |
EP_STAT estat; |
283 |
void (*cbfunc)(gdp_event_t *) = NULL; |
284 |
EP_TIME_SPEC ts; |
285 |
|
286 |
if (use_callbacks)
|
287 |
cbfunc = read_cb; |
288 |
|
289 |
// are we reading by record number or by timestamp?
|
290 |
if (dtstr != NULL) |
291 |
{ |
292 |
// timestamp
|
293 |
estat = ep_time_parse(dtstr, &ts, EP_TIME_USE_LOCALTIME); |
294 |
if (!EP_STAT_ISOK(estat))
|
295 |
{ |
296 |
ep_app_message(estat, "Cannot convert date/time string \"%s\"",
|
297 |
dtstr); |
298 |
return estat;
|
299 |
} |
300 |
} |
301 |
|
302 |
// start up a subscription
|
303 |
if (dtstr == NULL) |
304 |
estat = gdp_gin_subscribe_by_recno(gin, firstrec, numrecs, |
305 |
NULL, cbfunc, "Subscription"); |
306 |
else
|
307 |
estat = gdp_gin_subscribe_by_ts(gin, &ts, numrecs, |
308 |
NULL, cbfunc, "Subscription"); |
309 |
|
310 |
// check to make sure the subscribe succeeded; if not, bail
|
311 |
if (!EP_STAT_ISOK(estat))
|
312 |
{ |
313 |
char ebuf[200]; |
314 |
|
315 |
ep_app_fatal("Cannot subscribe: %s",
|
316 |
ep_stat_tostr(estat, ebuf, sizeof ebuf));
|
317 |
} |
318 |
|
319 |
// this sleep will allow multiple results to appear before we start reading
|
320 |
if (ep_dbg_test(Dbg, 100)) |
321 |
ep_time_nanosleep(500000000); //DEBUG: one half second |
322 |
|
323 |
// now start reading the events that will be generated
|
324 |
if (!use_callbacks)
|
325 |
{ |
326 |
int32_t ndone = 0;
|
327 |
for (;;)
|
328 |
{ |
329 |
// for testing: force early termination and close
|
330 |
if (ep_dbg_test(Dbg, 127) && ++ndone >= numrecs) |
331 |
break;
|
332 |
|
333 |
// get the next incoming event
|
334 |
gdp_event_t *gev = gdp_event_next(NULL, 0); |
335 |
|
336 |
// print it
|
337 |
estat = print_event(gev); |
338 |
|
339 |
// don't forget to free the event!
|
340 |
gdp_event_free(gev); |
341 |
|
342 |
EP_STAT_CHECK(estat, break);
|
343 |
} |
344 |
} |
345 |
else
|
346 |
{ |
347 |
// hang for an hour waiting for events
|
348 |
sleep(3600);
|
349 |
} |
350 |
|
351 |
return estat;
|
352 |
} |
353 |
|
354 |
|
355 |
/*
|
356 |
** DO_ASYNC_READ --- read asynchronously
|
357 |
*/
|
358 |
|
359 |
EP_STAT |
360 |
do_async_read(gdp_gin_t *gin, |
361 |
gdp_recno_t firstrec, |
362 |
int32_t numrecs, |
363 |
bool use_callbacks)
|
364 |
{ |
365 |
EP_STAT estat = EP_STAT_OK; |
366 |
gdp_event_cbfunc_t cbfunc = NULL;
|
367 |
|
368 |
if (use_callbacks)
|
369 |
cbfunc = read_cb; |
370 |
|
371 |
// make the flags more user-friendly
|
372 |
if (firstrec == 0) |
373 |
firstrec = 1;
|
374 |
if (numrecs <= 0) |
375 |
numrecs = gdp_gin_getnrecs(gin); |
376 |
if (firstrec < 0) |
377 |
{ |
378 |
firstrec += numrecs + 1;
|
379 |
numrecs -= firstrec - 1;
|
380 |
} |
381 |
|
382 |
// issue the async read command without reading results yet
|
383 |
estat = gdp_gin_read_by_recno_async(gin, firstrec, numrecs, |
384 |
cbfunc, "Async Read");
|
385 |
if (!EP_STAT_ISOK(estat))
|
386 |
{ |
387 |
char ebuf[100]; |
388 |
ep_app_error("async_read: gdp_gin_read_by_recno_async error:\n\t%s",
|
389 |
ep_stat_tostr(estat, ebuf, sizeof ebuf));
|
390 |
} |
391 |
|
392 |
// this sleep will allow multiple results to appear before we start reading
|
393 |
if (ep_dbg_test(Dbg, 100)) |
394 |
ep_time_nanosleep(500000000); //DEBUG: one half second |
395 |
|
396 |
// now start reading the events that will be generated
|
397 |
if (!use_callbacks)
|
398 |
{ |
399 |
do
|
400 |
{ |
401 |
// get the next incoming event
|
402 |
gdp_event_t *gev = gdp_event_next(NULL, 0); |
403 |
|
404 |
// print it
|
405 |
estat = print_event(gev); |
406 |
|
407 |
// don't forget to free the event!
|
408 |
gdp_event_free(gev); |
409 |
|
410 |
//EP_STAT_CHECK(estat, break);
|
411 |
} while (EP_STAT_ISOK(estat));
|
412 |
} |
413 |
else
|
414 |
{ |
415 |
// hang for 60 seconds waiting for events
|
416 |
sleep(60);
|
417 |
} |
418 |
|
419 |
return estat;
|
420 |
} |
421 |
|
422 |
|
423 |
/*
|
424 |
** PRINT_METADATA --- get and print the metadata
|
425 |
*/
|
426 |
|
427 |
void
|
428 |
print_metadata(gdp_gin_t *gin) |
429 |
{ |
430 |
EP_STAT estat; |
431 |
gdp_md_t *gmd; |
432 |
gdp_recno_t nrecs; |
433 |
|
434 |
nrecs = gdp_gin_getnrecs(gin); |
435 |
printf("Number of records: %" PRIgdp_recno "\n", nrecs); |
436 |
estat = gdp_gin_getmetadata(gin, &gmd); |
437 |
EP_STAT_CHECK(estat, goto fail0);
|
438 |
|
439 |
gdp_md_dump(gmd, stdout, 5, 0); |
440 |
gdp_md_free(gmd); |
441 |
return;
|
442 |
|
443 |
fail0:
|
444 |
ep_app_message(estat, "Could not read metadata!");
|
445 |
} |
446 |
|
447 |
void
|
448 |
usage(void)
|
449 |
{ |
450 |
fprintf(stderr, |
451 |
"Usage: %s [-a] [-b] [-c] [-d datetime] [-D dbgspec] [-f firstrec]\n"
|
452 |
" [-G router_addr] [-L logfile] [-M] [-n nrecs] [-o outfile]\n"
|
453 |
" [-s] [-t] [-v] [-V] log_name\n"
|
454 |
" -a read asynchronously\n"
|
455 |
" -b output data in binary\n"
|
456 |
" -c use callbacks\n"
|
457 |
" -d first date/time to read from\n"
|
458 |
" -D turn on debugging flags\n"
|
459 |
" -f first record number to read (from 1)\n"
|
460 |
" -G IP host to contact for gdp_router\n"
|
461 |
" -L set logging file name (for debugging)\n"
|
462 |
" -M show log metadata\n"
|
463 |
" -n set number of records to read (default all)\n"
|
464 |
" -o set output file name\n"
|
465 |
" -q be quiet (don't print any metadata)\n"
|
466 |
" -s subscribe to this log\n"
|
467 |
" -t print data as text (instead of hexdump)\n"
|
468 |
" -v print verbose output (include signature)\n"
|
469 |
" -V verify proof on returned data\n",
|
470 |
ep_app_getprogname()); |
471 |
exit(EX_USAGE); |
472 |
} |
473 |
|
474 |
/*
|
475 |
** MAIN --- the name says it all
|
476 |
*/
|
477 |
|
478 |
int
|
479 |
main(int argc, char **argv) |
480 |
{ |
481 |
gdp_gin_t *gin = NULL;
|
482 |
EP_STAT estat; |
483 |
gdp_name_t gobname; |
484 |
int opt;
|
485 |
char *gdpd_addr = NULL; |
486 |
bool subscribe = false; |
487 |
bool async = false; |
488 |
bool use_callbacks = false; |
489 |
bool showmetadata = false; |
490 |
bool vrfy_proof = false; |
491 |
int32_t numrecs = 0;
|
492 |
gdp_recno_t firstrec = 0;
|
493 |
bool show_usage = false; |
494 |
char *log_file_name = NULL; |
495 |
gdp_iomode_t open_mode = GDP_MODE_RO; |
496 |
const char *dtstr = NULL; |
497 |
|
498 |
ep_lib_init(EP_LIB_USEPTHREADS); // initialize app errors, etc.
|
499 |
|
500 |
// parse command-line options
|
501 |
while ((opt = getopt(argc, argv, "abAcd:D:f:G:L:mMn:o:qstvV")) > 0) |
502 |
{ |
503 |
errno = 0; // avoid misleading messages |
504 |
switch (opt)
|
505 |
{ |
506 |
case 'A': // hidden flag for debugging only |
507 |
open_mode = GDP_MODE_RA; |
508 |
break;
|
509 |
|
510 |
case 'a': |
511 |
// do asynchronous read
|
512 |
async = true;
|
513 |
break;
|
514 |
|
515 |
case 'b': |
516 |
BinaryData = true;
|
517 |
break;
|
518 |
|
519 |
case 'c': |
520 |
// use callbacks
|
521 |
use_callbacks = true;
|
522 |
break;
|
523 |
|
524 |
case 'd': |
525 |
dtstr = optarg; |
526 |
break;
|
527 |
|
528 |
case 'D': |
529 |
// turn on debugging
|
530 |
ep_dbg_set(optarg); |
531 |
break;
|
532 |
|
533 |
case 'f': |
534 |
// select the first record
|
535 |
firstrec = atol(optarg); |
536 |
break;
|
537 |
|
538 |
case 'G': |
539 |
// set the port for connecting to the GDP daemon
|
540 |
gdpd_addr = optarg; |
541 |
break;
|
542 |
|
543 |
case 'L': |
544 |
log_file_name = optarg; |
545 |
break;
|
546 |
|
547 |
case 'm': |
548 |
// multiread: obsolete
|
549 |
ep_app_warn("Multiread (-m) is deprecated; using Async (-a)");
|
550 |
async = true;
|
551 |
break;
|
552 |
|
553 |
case 'M': |
554 |
showmetadata = true;
|
555 |
break;
|
556 |
|
557 |
case 'n': |
558 |
// select the number of records to be returned
|
559 |
numrecs = atol(optarg); |
560 |
break;
|
561 |
|
562 |
case 'o': |
563 |
OutFile = fopen(optarg, "w");
|
564 |
if (OutFile == NULL) |
565 |
{ |
566 |
ep_app_error("Cannot open output file %s", optarg);
|
567 |
exit(EX_CANTCREAT); |
568 |
} |
569 |
break;
|
570 |
|
571 |
case 'q': |
572 |
// be quiet (don't print metadata)
|
573 |
Quiet = true;
|
574 |
break;
|
575 |
|
576 |
case 's': |
577 |
// subscribe to this log
|
578 |
subscribe = true;
|
579 |
break;
|
580 |
|
581 |
case 't': |
582 |
// print data as text
|
583 |
TextData = true;
|
584 |
break;
|
585 |
|
586 |
case 'v': |
587 |
PrintSig = true;
|
588 |
|
589 |
case 'V': |
590 |
vrfy_proof = true;
|
591 |
break;
|
592 |
|
593 |
default:
|
594 |
show_usage = true;
|
595 |
break;
|
596 |
} |
597 |
} |
598 |
argc -= optind; |
599 |
argv += optind; |
600 |
|
601 |
if (firstrec != 0 && dtstr != NULL) |
602 |
{ |
603 |
ep_app_error("Cannot specify -f and -d");
|
604 |
exit(EX_USAGE); |
605 |
} |
606 |
|
607 |
int ntypes = 0; |
608 |
if (async)
|
609 |
ntypes++; |
610 |
if (subscribe)
|
611 |
ntypes++; |
612 |
if (ntypes > 1) |
613 |
{ |
614 |
ep_app_error("Can only specify at most one of -a and -s");
|
615 |
usage(); |
616 |
} |
617 |
else if (ntypes < 1 && use_callbacks) |
618 |
{ |
619 |
ep_app_warn("UseCallbacks (-c) flag does nothing without -a or -s");
|
620 |
} |
621 |
|
622 |
ntypes = 0;
|
623 |
if (TextData)
|
624 |
ntypes++; |
625 |
if (BinaryData)
|
626 |
ntypes++; |
627 |
if (ntypes > 1) |
628 |
{ |
629 |
ep_app_error("Can only specify at most one of -b and -t");
|
630 |
usage(); |
631 |
} |
632 |
|
633 |
// we require a log name
|
634 |
if (show_usage || argc <= 0) |
635 |
usage(); |
636 |
|
637 |
if (log_file_name != NULL) |
638 |
{ |
639 |
// open a log file (for timing measurements)
|
640 |
LogFile = fopen(log_file_name, "a");
|
641 |
if (LogFile == NULL) |
642 |
ep_app_warn("Cannot open log file %s: %s",
|
643 |
log_file_name, strerror(errno)); |
644 |
else
|
645 |
setlinebuf(LogFile); |
646 |
} |
647 |
|
648 |
if (OutFile == NULL) |
649 |
OutFile = stdout; |
650 |
|
651 |
// initialize the GDP library
|
652 |
estat = gdp_init(gdpd_addr); |
653 |
if (!EP_STAT_ISOK(estat))
|
654 |
{ |
655 |
ep_app_error("GDP Initialization failed");
|
656 |
goto fail0;
|
657 |
} |
658 |
|
659 |
// allow thread to settle to avoid interspersed debug output
|
660 |
ep_time_nanosleep(INT64_C(100000000)); // 100 msec |
661 |
|
662 |
// parse the name (either base64-encoded or symbolic)
|
663 |
estat = gdp_parse_name(argv[0], gobname);
|
664 |
if (EP_STAT_ISFAIL(estat))
|
665 |
{ |
666 |
ep_app_message(estat, "illegal log name syntax:\n\t%s", argv[0]); |
667 |
exit(EX_USAGE); |
668 |
} |
669 |
|
670 |
// convert it to printable format and tell the user what we are doing
|
671 |
if (!Quiet)
|
672 |
{ |
673 |
gdp_pname_t gobpname; |
674 |
|
675 |
gdp_printable_name(gobname, gobpname); |
676 |
fprintf(stderr, "Reading log %s\n", gobpname);
|
677 |
} |
678 |
|
679 |
// set up any special open parameters
|
680 |
gdp_open_info_t *open_info = gdp_open_info_new(); |
681 |
if (vrfy_proof)
|
682 |
gdp_open_info_set_vrfy(open_info, true);
|
683 |
|
684 |
// open the log; arguably this shouldn't be necessary
|
685 |
estat = gdp_gin_open(gobname, open_mode, open_info, &gin); |
686 |
gdp_open_info_free(open_info); |
687 |
if (!EP_STAT_ISOK(estat))
|
688 |
{ |
689 |
ep_app_message(estat, "Cannot open log %s", argv[0]); |
690 |
exit(EX_NOINPUT); |
691 |
} |
692 |
|
693 |
// if we are converting a date/time string, set the local timezone
|
694 |
if (dtstr != NULL) |
695 |
tzset(); |
696 |
|
697 |
if (showmetadata)
|
698 |
print_metadata(gin); |
699 |
|
700 |
// arrange to do the reading via one of the helper routines
|
701 |
if (async)
|
702 |
estat = do_async_read(gin, firstrec, numrecs, use_callbacks); |
703 |
else if (subscribe) |
704 |
estat = do_subscribe(gin, firstrec, dtstr, numrecs, use_callbacks); |
705 |
else
|
706 |
estat = do_simpleread(gin, firstrec, dtstr, numrecs); |
707 |
|
708 |
fail0:
|
709 |
; // silly compiler grammar
|
710 |
int exitstat;
|
711 |
|
712 |
if (!EP_STAT_ISFAIL(estat)) // WARN or OK |
713 |
exitstat = EX_OK; |
714 |
else if (EP_STAT_IS_SAME(estat, GDP_STAT_NAK_NOROUTE)) |
715 |
exitstat = EX_NOINPUT; |
716 |
else if (EP_STAT_ISABORT(estat)) |
717 |
exitstat = EX_SOFTWARE; |
718 |
else
|
719 |
exitstat = EX_UNAVAILABLE; |
720 |
|
721 |
if (ep_dbg_test(Dbg, 9)) |
722 |
{ |
723 |
char ebuf[100]; |
724 |
ep_dbg_printf("Cleaning up, exitstat %d, estat %s\n",
|
725 |
exitstat, ep_stat_tostr(estat, ebuf, sizeof ebuf));
|
726 |
} |
727 |
|
728 |
// might as well let the GDP know we're going away
|
729 |
if (gin != NULL) |
730 |
{ |
731 |
EP_STAT close_stat = gdp_gin_close(gin); |
732 |
if (!EP_STAT_ISOK(close_stat))
|
733 |
ep_app_message(close_stat, "cannot close log");
|
734 |
} |
735 |
|
736 |
// this sleep is to watch for any extraneous results coming back
|
737 |
if (ep_dbg_test(Dbg, 126)) |
738 |
{ |
739 |
int sleep_time = 40; |
740 |
ep_dbg_printf("Sleeping for %d seconds\n", sleep_time);
|
741 |
while (sleep_time-- > 0) |
742 |
ep_time_nanosleep(INT64_C(1000000000)); // one second |
743 |
} |
744 |
|
745 |
// might as well let the user know what's going on....
|
746 |
if (EP_STAT_ISFAIL(estat))
|
747 |
ep_app_message(estat, "exiting after %d records", NRead);
|
748 |
else if (!Quiet) |
749 |
fprintf(stderr, "Exiting after %d records\n", NRead);
|
750 |
return exitstat;
|
751 |
} |