File Coverage

blib/lib/BGPmon/Fetch/File.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             package BGPmon::Fetch::File;
2             our $VERSION = '2.0';
3              
4 1     1   48995 use 5.14.0;
  1         4  
  1         46  
5 1     1   5 use strict;
  1         2  
  1         29  
6 1     1   5 use warnings;
  1         1  
  1         28  
7 1     1   893 use POSIX qw/strftime/;
  1         7501  
  1         9  
8 1     1   740226 use IO::Uncompress::AnyUncompress qw(anyuncompress $AnyUncompressError);
  1         1904630  
  1         189  
9 1     1   12 use File::Path qw/mkpath rmtree/;
  1         3  
  1         66  
10 1     1   2342 use XML::LibXML::Reader;
  0            
  0            
11              
12             BEGIN{
13             require Exporter;
14             our $AUTOLOAD;
15             our @ISA = qw(Exporter);
16             our %EXPORT_TAGS = ( 'all' => [ qw(init_bgpdata connect_file
17             read_xml_message close_connection is_connected messages_read uptime
18             connection_endtime connection_duration get_error_code get_error_message
19             get_error_msg) ] );
20             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
21             }
22              
23             # connection status
24             my $msgs_read = 0;
25             my $connection_start;
26             my $connection_stop;
27             my $connected = 0;
28             my $saw_start = 0; #saw_start and saw_end track tags
29             my $saw_end = 0;
30              
31             #Set these to 1 to skip errors and/or incomplete data errors
32             my $ignore_data_errors = 0;
33             my $ignore_incomplete_data = 0;
34              
35             #state variables to maintain state between calls to read_xml_message
36             my $upd_fh; #filehandle for update files
37             my $upd_filename; #Filename
38             my $xml_reader; #variable to use as the XML reader
39              
40             #scratch directory and default filename
41             my $scratch_dir = "/tmp/";
42             my $output_file = "extract_bgp.$$";
43              
44             #Error codes and messages
45             my %error_code;
46             my %error_msg;
47             my @function_names = ('init_bgpdata', 'connect_file', 'read_xml_message',
48             'close_connection', 'is_connected', 'uptime', 'connection_endtime',
49             'connection_duration');
50              
51             use constant NO_ERROR_CODE => 0;
52             use constant NO_ERROR_MSG => 'No Error';
53             use constant UNDEFINED_ARGUMENT_CODE => 301;
54             use constant UNDEFINED_ARGUMENT_MSG => 'Undefined Argument(s)';
55             use constant UNCONNECTED_CODE => 302;
56             use constant UNCONNECTED_MSG => 'Not connected to a file';
57             use constant ALREADY_CONNECTED_CODE => 303;
58             use constant ALREADY_CONNECTED_MSG => 'Already connected to a file';
59             use constant NO_SUCH_FILE_CODE => 304;
60             use constant NO_SUCH_FILE_MSG => 'Specified file/directory does not exist';
61             use constant SYSCALL_FAIL_CODE => 305;
62             use constant SYSCALL_FAIL_MSG => 'System call failed';
63             use constant DECOMPRESS_FAIL_CODE => 306;
64             use constant DECOMPRESS_FAIL_MSG => 'Failed to decompress file';
65             use constant OPEN_FAIL_CODE => 307;
66             use constant OPEN_FAIL_MSG => 'Failed to open file';
67             use constant PARSER_INIT_FAIL_CODE => 308;
68             use constant PARSER_INIT_FAIL_MSG => 'Failed to initialize XML Reader';
69             use constant PARSER_FATAL_ERROR_CODE => 309;
70             use constant PARSER_FATAL_ERROR_MSG => 'XML Parser Error';
71             use constant FILE_FORMAT_ERROR_CODE => 310;
72             use constant FILE_FORMAT_ERROR_MSG => 'File must begin with tag';
73             use constant INVALID_FUNCTION_SPECIFIED_CODE => 311;
74             use constant INVALID_FUNCTION_SPECIFIED_MSG => 'Invalid function specified';
75             use constant INIT_FAIL_CODE => 312;
76             use constant INIT_FAIL_MSG => 'Failed to initialize file connection';
77             use constant INCOMPLETE_DATA_CODE => 313;
78             use constant INCOMPLETE_DATA_MSG =>
79             'File is missing expected ARCHIVER messages';
80             use constant DATA_GAP_CODE => 314;
81             use constant DATA_GAP_MSG => 'File may be missing data';
82             use constant IGNORE_ERROR_CODE => 315;
83             use constant IGNORE_ERROR_MSG =>
84             'Cannot have ignore_incomplete_data off with ignore_data_errors on';
85              
86             for my $function_name (@function_names) {
87             $error_code{$function_name} = NO_ERROR_CODE;
88             $error_msg{$function_name} = NO_ERROR_MSG;
89             }
90              
91             END{
92             my $errs;
93             rmtree($scratch_dir,{keep_root => 1, safe => 1, error => \$errs});
94             }
95              
96             =head1 NAME
97              
98             BGPmon::Fetch::File
99              
100             The BGPmon::Fetch::File module, to connect to a local XML file and read
101             XML messages one at a time.
102              
103              
104             =head1 SYNOPSIS
105              
106             The BGPmon::Fetch::File module provides functionality to connect
107             to an XML file and read message at a time.
108              
109             use BGPmon::Fetch::File;
110             my $ret = init_bgpdata('scratch_dir'=>'path/to/temp/dir',
111             'ignore_incomplete_data' => 0, 'ignore_data_errors' => 0);
112             my $ret = connect_file('path/to/file');
113             my $xml_msg = read_xml_message();
114             my $ret = is_connected();
115             my $num_read = messages_read();
116             my $uptime = uptime();
117             my $ret = close_connection();
118             my $downtime = connection_endtime();
119             my $duration = connection_duration();
120              
121             =head1 EXPORT
122              
123             init_bgpdata
124             connect_file
125             read_xml_message
126             close_connection
127             is_connected
128             messages_read
129             uptime
130             connection_endtime
131             connection_duration
132             get_error_code
133             get_error_message
134             get_error_msg
135              
136             =cut
137              
138             =head1 SUBROUTINES/METHODS
139              
140             =head2 init_bgpdata
141              
142             Initializes the scratch directory and error-checking flags for the next
143             file connection.
144              
145             Input: The location to create a scratch directory in (default is /tmp)
146             Whether to ignore potentially incomplete data (default is off)
147             Whether to ignore all data errors (must also specify ignore
148             incomplete data flag as well) (default is off)
149              
150             Output: 0 if initialization fails
151             1 if initialization succeeds
152              
153             Usage: my $ret = init_bgpdata('scratch_dir' => '/tmp',
154             'ignore_incomplete_data' => 1, 'ignore_data_errors' => 0);
155              
156             =cut
157              
158             sub init_bgpdata{
159             my %args = @_;
160             my $fname = "init_bgpdata";
161              
162             #Extract the specified scratch directory if specified, otherwise
163             #use the default directory (/tmp).
164             my $new_dir = $args{'scratch_dir'};
165             if( !defined($new_dir) ){
166             eval{
167             $scratch_dir .= "/BGP.File.$$";
168             $scratch_dir =~ s/\/\//\//;
169             mkpath $scratch_dir;
170             1;
171             } or do {
172             $error_code{$fname} = SYSCALL_FAIL_CODE;
173             $error_msg{$fname} = SYSCALL_FAIL_MSG.": $@";
174             return 0;
175             };
176             }
177             else{
178             eval{
179             $new_dir .= "/BGP.File.$$";
180             $new_dir =~ s/\/\//\//;
181             mkpath $new_dir;
182             $scratch_dir = $new_dir;
183             1;
184             } or do {
185             $error_code{$fname} = SYSCALL_FAIL_CODE;
186             $error_msg{$fname} = SYSCALL_FAIL_MSG.": $@";
187             return 0;
188             };
189             }
190              
191             #Get whether the user wants to ignore incomplete data or all data errors
192             #It is an error to ignore all data errors but not incomplete data
193             $ignore_incomplete_data = $args{'ignore_incomplete_data'}
194             if defined $args{'ignore_incomplete_data'};
195             $ignore_data_errors = $args{'ignore_data_errors'}
196             if defined $args{'ignore_data_errors'};
197              
198             if( $ignore_data_errors && !$ignore_incomplete_data ){
199             $error_code{$fname} = IGNORE_ERROR_CODE;
200             $error_msg{$fname} = IGNORE_ERROR_MSG;
201             return 0;
202             }
203             #Reset some module state variables, including error codes
204             $saw_start = 0;
205             $saw_end = 0;
206             for my $function_name (@function_names) {
207             $error_code{$function_name} = NO_ERROR_CODE;
208             $error_msg{$function_name} = NO_ERROR_MSG;
209             }
210             $connection_stop = undef;
211             return 1;
212             }
213              
214             =head2 connect_file
215              
216             This function connects to a local file.
217              
218             Input: the filename to read XML from
219              
220             Output: 0 on success, 1 on failure
221              
222             Usage: my $ret = connect_file("path/to/file");
223              
224             =cut
225              
226             sub connect_file {
227              
228             #Store arguments in state variables
229             my $filename = shift;
230              
231             my $fname = "connect_file";
232              
233             #Check for correct number of variables
234             if( !defined($filename) ){
235             $error_code{$fname} = UNDEFINED_ARGUMENT_CODE;
236             $error_msg{$fname} = UNDEFINED_ARGUMENT_MSG;
237             return 1;
238             }
239              
240             #We cannot connect to a file while already connected to one
241             if(is_connected()){
242             $error_code{$fname} = ALREADY_CONNECTED_CODE;
243             $error_msg{$fname} = ALREADY_CONNECTED_MSG;
244             return 1;
245             }
246              
247             #The file must exist before we can connect to it
248             if(!file_check($filename) ){
249             $error_code{$fname} = NO_SUCH_FILE_CODE;
250             $error_msg{$fname} = NO_SUCH_FILE_MSG;
251             return 1;
252             }
253             #Create the scratch directory if it has not already been created
254             #This call will create the default scratch directory, and will NOT
255             #ignore any data errors
256             if( $scratch_dir !~ m/BGP.File/ ){
257             if(!init_bgpdata('ignore_incomplete_data' => 0,
258             'ignore_data_errors' => 0)){
259             $error_code{$fname} = INIT_FAIL_CODE;
260             $error_msg{$fname} = INIT_FAIL_MSG;
261             close_connection();
262             return 1;
263             }
264             }
265             #decompress_file never fails to return
266             my $ret = decompress_file($filename);
267             $filename = $ret if defined $ret;
268              
269             #Now open the now-uncompressed file
270             unless( open($upd_fh, "<", "$scratch_dir/$filename") ){
271             $error_code{$fname} = OPEN_FAIL_CODE;
272             $error_msg{$fname} = OPEN_FAIL_MSG.": $@";
273             return 1;
274             }
275              
276             #Instantiate the XML parser
277             unless($xml_reader =
278             XML::LibXML::Reader->new(IO => $upd_fh, recover => 1)){
279             $error_code{$fname} = PARSER_INIT_FAIL_CODE;
280             $error_msg{$fname} = PARSER_INIT_FAIL_MSG;
281             close($upd_fh);
282             return 1;
283             }
284              
285             #We require all files to start w/ an tag; fail if it is not present.
286             eval{
287             unless( $xml_reader->read() == 1 && $xml_reader->localName() eq "xml"){
288             $error_code{$fname} = FILE_FORMAT_ERROR_CODE;
289             $error_msg{$fname} = FILE_FORMAT_ERROR_MSG;
290             close($upd_fh);
291             return 1;
292             }
293             1;
294             } or do{
295             $error_code{$fname} = PARSER_FATAL_ERROR_CODE;
296             $error_msg{$fname} = PARSER_FATAL_ERROR_MSG.": $@";
297             close($upd_fh);
298             return 1;
299             };
300              
301             #Now that we have successfully connected, set remaining state variables
302             #for this connection
303             $msgs_read = 0;
304             $upd_filename = "$scratch_dir/$filename";
305             $connected = 1;
306             $connection_start = time;
307              
308             $error_code{$fname} = NO_ERROR_CODE;
309             $error_msg{$fname} = NO_ERROR_MSG;
310             return 0;
311             }
312              
313             =head2 read_xml_message
314              
315             This function reads one XML message at a time from an open file connection.
316              
317             Input: None, but assumes connect_file has been called
318              
319             Output: The next available XML message from the file, or undef if the
320             messages are exhausted or some other failure is encountered
321              
322             Usage: my $next_msg = read_xml_message();
323              
324             =cut
325              
326             sub read_xml_message {
327             my $fname = "read_xml_message";
328              
329             #There must be an open file connection to read from
330             if( !is_connected() ){
331             $error_code{$fname} = UNCONNECTED_CODE;
332             $error_msg{$fname} = UNCONNECTED_MSG;
333             return undef;
334             }
335              
336             #The file that we're supposed to be connected to had better still be there
337             if( !file_check($upd_filename) ){
338             $error_code{$fname} = NO_SUCH_FILE_CODE;
339             $error_msg{$fname} = NO_SUCH_FILE_MSG;
340             close_connection();
341             return undef;
342             }
343             my $complete_xml_msg = "";
344              
345             #For archive files, the root node (depth 0) is the tag
346             #Therefore we want every XML message right under that (at depth 1)
347             #These are mostly s, but could be anything
348             while( $xml_reader->depth() != 1 || $xml_reader->nodeType() ==
349             XML_READER_TYPE_SIGNIFICANT_WHITESPACE){
350             #First try to read the next node in the XML stream
351             #If the XML parser itself fails, close the connection and return
352             eval{
353             #If read() returns 0, then we are at the end of the file
354             #and we need to check for any data error. However, the XML parser
355             #can fail too, so this eval will catch both cases which we need
356             #to differentiate in the do block.
357             return undef if !$xml_reader->read();
358             1;
359             } or do {
360             #This case catches any XML parser error.
361             if($?){
362             $error_code{$fname} = PARSER_FATAL_ERROR_CODE;
363             $error_msg{$fname} = PARSER_FATAL_ERROR_MSG.": $@";
364             }
365             #Since the only other way to get into this block is for the file
366             #to be done, this checks to make sure the file ended with an
367             #ARCHIVER/CLOSED message (if we're looking for such things).
368             elsif( $ignore_incomplete_data || $saw_end == 1 ){
369             $error_code{$fname} = NO_ERROR_CODE;
370             $error_msg{$fname} = NO_ERROR_MSG;
371             }
372             #If the ARCHIVER/CLOSED message was not there or there were
373             #more than 1, there is an error.
374             else{
375             $error_code{$fname} = INCOMPLETE_DATA_CODE;
376             $error_msg{$fname} = INCOMPLETE_DATA_MSG;
377             }
378             #All 3 cases terminate this connection.
379             close_connection();
380             return undef;
381             };
382             }
383             eval{
384             $complete_xml_msg .= $xml_reader->readOuterXml();
385             #There are a couple of error cases to check w.r.t. ARCHIVER messages.
386             #We must start off with an ARCHIVER/OPENED message
387             #Any additional ARCHIVER/OPENED messages indicate missing data
388             #We must see an ARCHIVER/END message
389             if( !$ignore_data_errors && $xml_reader->localName() eq "ARCHIVER" &&
390             $xml_reader->nextElement("EVENT") == 1 ){
391             #This block examines any ARCHIVER messages we encounter.
392             #If the current message is an ARCHIVER/OPENED and we have not seen
393             #any messages yet, then it is expected and we can set the flag
394             #to indicate it.
395             if($xml_reader->readInnerXml() eq "OPENED" && $msgs_read == 0){
396             $saw_start = 1;
397             $error_code{$fname} = NO_ERROR_CODE;
398             $error_msg{$fname} = NO_ERROR_MSG;
399             }
400             #If the current message is an ARCHIVER/CLOSED message, then we
401             #increment the count of such messages we've seen. This will be
402             #used later in error-detection.
403             elsif( $xml_reader->readInnerXml() eq "CLOSED" ){
404             $saw_end++;
405             }
406             #This case implies that the current message is an ARCHIVER/OPENED
407             #message, but we've already seen one, so this is an error
408             else{ return undef;}
409             #Read to the beginning of the next XML message
410             $xml_reader->read() while( $xml_reader->depth() != 1 ||
411             $xml_reader->nodeType() == XML_READER_TYPE_SIGNIFICANT_WHITESPACE );
412             }
413             1;
414             #This block handles the error cases that can arise from the previous
415             #block. This can happen in two ways: either we've seen a duplicate
416             #ARCHIVER/OPENED message or the XML parser itself failed.
417             } or do {
418             if( $msgs_read != 0 ){
419             $error_code{$fname} = DATA_GAP_CODE;
420             $error_msg{$fname} = DATA_GAP_MSG;
421             #We only want to throw this error if we care about
422             #incomplete data.
423             if( !$ignore_incomplete_data ){
424             close_connection();
425             return undef;
426             }
427             }
428             else{
429             $error_code{$fname} = PARSER_FATAL_ERROR_CODE;
430             $error_msg{$fname} = PARSER_FATAL_ERROR_MSG.": $@";
431             close_connection();
432             return undef;
433             }
434             };
435             if( !defined($complete_xml_msg) ){
436             $error_code{$fname} = FILE_FORMAT_ERROR_CODE;
437             $error_msg{$fname} = FILE_FORMAT_ERROR_MSG.": $@";
438             close_connection();
439             return undef;
440             }
441             $xml_reader->next();
442             #If we are checking data errors, then the first message MUST
443             #be an ARCHIVER/OPENED message, otherwise throw the error and quit.
444             if( $msgs_read == 0 && !$saw_start && !$ignore_data_errors){
445             $error_code{$fname} = INCOMPLETE_DATA_CODE;
446             $error_msg{$fname} = INCOMPLETE_DATA_MSG;
447             close_connection();
448             return undef;
449             }
450             $msgs_read++;
451             $error_code{$fname} = NO_ERROR_CODE;
452             $error_msg{$fname} = NO_ERROR_MSG;
453             return $complete_xml_msg;
454             }
455              
456              
457             =head2 close_connection
458              
459             Function to close and delete any files and reset the module's state variables
460              
461             Usage: close_connection();
462              
463             =cut
464              
465             sub close_connection {
466             my $fname = "close_connection";
467             #Can't close a connection that isn't there...
468             if( !is_connected() ){
469             $error_code{$fname} = UNCONNECTED_CODE;
470             $error_msg{$fname} = UNCONNECTED_MSG;
471             return 1;
472             }
473             #Close the XML reader
474             $xml_reader->close();
475             #Close the open file handle
476             close($upd_fh) if defined(fileno $upd_fh);
477             #Track the end of this connection
478             $connection_stop = time;
479             #Now try to delete the scratch directory; set an error if it fails
480             #but do not return an error if the system call fails
481             eval{
482             my $errs;
483             rmtree($scratch_dir,{keep_root => 1, safe => 1, error => \$errs});
484             1;
485             } or do {
486             $error_code{$fname} = SYSCALL_FAIL_CODE;
487             $error_msg{$fname} = SYSCALL_FAIL_MSG.": $@";
488             };
489             ($upd_fh,$upd_filename) = undef;
490             $error_code{$fname} = NO_ERROR_CODE;
491             $error_msg{$fname} = NO_ERROR_MSG;
492             $connected = 0;
493             }
494              
495             =head2 is_connected
496              
497             Function to report whether currently connected to an archive.
498              
499             =cut
500              
501             sub is_connected {
502             return $connected;
503             }
504              
505             =head2 messages_read
506              
507             Get number of messages read.
508              
509             Usage: my $msgs_read = messages_read();
510              
511             =cut
512              
513             sub messages_read {
514             return $msgs_read;
515             }
516              
517             =head2 uptime
518              
519             Returns number of seconds the connection has been up.
520             If the connection is down, return 0.
521              
522             Usage: my $uptime = uptime();
523              
524             =cut
525              
526             sub uptime {
527             if ($connected) {
528             return time() - $connection_start;
529             }
530             return 0;
531              
532             }
533              
534             =head2 connection_endtime
535              
536             Returns the time the connection ended .
537             If the connection is up, return 0.
538              
539             Usage: my $endtime = connection_endtime();
540              
541             =cut
542              
543             sub connection_endtime {
544             my $fname = "connection_endtime";
545             if ($connected) {
546             $error_code{$fname} = ALREADY_CONNECTED_CODE;
547             $error_msg{$fname} = ALREADY_CONNECTED_MSG;
548             return 0;
549             }
550             $error_code{$fname} = NO_ERROR_CODE;
551             $error_msg{$fname} = NO_ERROR_MSG;
552             return $connection_stop;
553              
554             }
555              
556             =head2 connection_duration
557              
558             Returns the total time the last connection was up for.
559             If the connection is up, returns 0.
560             NOTE: If a connection is currently established, call uptime().
561              
562             Usage: my $dur = connection_duration();
563              
564             =cut
565             sub connection_duration{
566             my $fname = "connection_duration";
567             if( $connected) {
568             $error_code{$fname} = ALREADY_CONNECTED_CODE;
569             $error_msg{$fname} = ALREADY_CONNECTED_MSG;
570             return 0;
571             }
572             $error_code{$fname} = NO_ERROR_CODE;
573             $error_msg{$fname} = NO_ERROR_MSG;
574             return $connection_stop - $connection_start;
575             }
576              
577             =head2 get_error_code
578              
579             Get the error code
580              
581             Input : the name of the function whose error code we should report
582              
583             Output: the function's error code
584             or UNDEFINED_ARGUMENT if the user did not supply a function
585             or INVALID_FUNCTION_SPECIFIED if the user provided an invalid
586             function name
587              
588             Usage: my $err_code = get_error_code("connect_file");
589             =cut
590              
591             sub get_error_code {
592             my $function = shift;
593              
594             # check we got a function name
595             if (!defined($function)) {
596             return UNDEFINED_ARGUMENT_CODE;
597             }
598              
599             return $error_code{$function} if defined($error_code{$function});
600             return INVALID_FUNCTION_SPECIFIED_CODE;
601             }
602              
603             =head2 get_error_message
604              
605             Get the error message
606              
607             Input : the name of the function whose error message we should report
608              
609             Output: the function's error message
610             or UNDEFINED_ARGUMENT if the user did not supply a function
611             or INVALID_FUNCTION_SPECIFIED if the user provided an invalid
612             function name
613              
614             Usage: my $err_msg = get_error_message("read_xml_message");
615             =cut
616              
617             sub get_error_message {
618             my $function = shift;
619              
620             # check we got a function name
621             if (!defined($function)) {
622             return UNDEFINED_ARGUMENT_MSG;
623             }
624              
625             return $error_msg{$function} if defined($error_msg{$function});
626             return INVALID_FUNCTION_SPECIFIED_MSG."$function";
627             }
628              
629             =head2 get_error_msg
630              
631             Shorthand call for get_error_message
632              
633             =cut
634             sub get_error_msg{
635             my $fname = shift;
636             return get_error_message($fname);
637             }
638              
639              
640             ########################## END EXPORTED FUNCTIONS #############################
641             ########################## BEGIN UNEXPORTED FUNCTIONS #########################
642              
643             # file_check
644              
645             #This function checks whether or not the currently-open file exists
646             #Input: A filename to check
647             #Output: 1 if the file exists, 0 otherwise
648              
649             sub file_check{
650             my $file = shift;
651             my $fname = "file_check";
652             if( !defined($file) ){
653             $error_code{$fname} = UNDEFINED_ARGUMENT_CODE;
654             $error_msg{$fname} = UNDEFINED_ARGUMENT_MSG;
655             return 0;
656             }
657             if( -e $file ){
658             return 1;
659             }
660             else{
661             $error_code{$fname} = NO_SUCH_FILE_CODE;
662             $error_msg{$fname} = NO_SUCH_FILE_MSG;
663             return 0;
664             }
665             }
666              
667             # decompress_file
668              
669             #This function decompresses a file using Perl's IO::Uncompress library.
670             #Currently supports gzip, bzip2, RFC 1950/1951, zip,lzop,lzf,lzma,xz
671             #Input: The filename to uncompress
672             #Returns: undef on failure, the name of the uncompressed file on success
673              
674             sub decompress_file{
675             my $file = shift;
676             my $fname = "decompress_file";
677             if( !defined($file) ){
678             $error_code{$fname} = UNDEFINED_ARGUMENT_CODE;
679             $error_msg{$fname} = UNDEFINED_ARGUMENT_MSG;
680             return undef;
681             }
682             if( is_connected() ){
683             $error_code{$fname} = ALREADY_CONNECTED_CODE;
684             $error_msg{$fname} = ALREADY_CONNECTED_MSG;
685             return undef;
686             }
687             if( !file_check($file) || !file_check($scratch_dir) ){
688             $error_code{$fname} = NO_SUCH_FILE_CODE;
689             $error_msg{$fname} = NO_SUCH_FILE_MSG;
690             return undef;
691             }
692             unless( anyuncompress $file => "$scratch_dir/$output_file" ){
693             $error_code{$fname} = DECOMPRESS_FAIL_CODE;
694             $error_msg{$fname} = DECOMPRESS_FAIL_MSG.": $@";
695             return undef;
696             }
697              
698             return $output_file;
699             }
700              
701             ########################## END UNEXPORTED #####################################
702              
703             =head1 ERROR CODES AND MESSAGES
704              
705             The following error codes and messages are defined:
706              
707             0: No Error
708             'No Error'
709              
710             301: An argument to a function was undefined
711             'Undefined Argument(s)'
712              
713             302: There is no active connection to a file
714             'Not connected to a file'
715              
716             303: There is a currently-active connection to a file
717             'Already connected to a file'
718              
719             304: The filename or directory given does not exist
720             'Specified file/directory does not exist'
721              
722             305: A system call failed
723             'System call failed'
724              
725             306: Decompressing the file failed
726             'Failed to decompress file'
727              
728             307: The file was not opened successfully
729             'Failed to open file'
730              
731             308: Initializing the XML Reader failed
732             'Failed to initialize XML Reader'
733              
734             309: The XML Reader encountered a fatal error
735             'XML Parser Error'
736              
737             310: The XML file passed in did not begin with an tag
738             'File must begin with tag'
739              
740             311: An invalid function name was passed to get_error_[code/message/msg]
741             'Invalid function specified'
742              
743             312: There was an error initializing one or more of the options to init_bgpdata
744             'Failed to initialize file connection'
745              
746             313: At least one of the beginning ARCHIVER/OPENING or ARCHIVER/CLOSE
747             messages were missing from the file
748             NOTE: Setting the ignore_data_errors flag will suppress this
749             'File is missing expected ARCHIVER messages'
750              
751             314: An additional ARCHIVER/OPENING message was encountered during file
752             processing. This indicates a likely gap in the data.
753             NOTE: Setting the ignore_incomplete_data flag will suppress this
754             'File may be missing data'
755              
756             315: User tried to ignore all data errors, but was checking for incomplete data
757             'Cannot have ignore_incomplete_data off with ignore_data_errors on'
758              
759             =head1 AUTHOR
760              
761             Jason Bartlett, C<< >>
762              
763             =head1 BUGS
764              
765             Please report any bugs or feature requests to
766             C, or through
767             the web interface at L.
768              
769              
770             =head1 SUPPORT
771              
772             You can find documentation for this module with the perldoc command.
773              
774             perldoc BGPmon::Fetch::File
775              
776             =cut
777              
778             =head1 LICENSE AND COPYRIGHT
779              
780             Copyright (c) 2012 Colorado State University
781              
782             Permission is hereby granted, free of charge, to any person
783             obtaining a copy of this software and associated documentation
784             files (the "Software"), to deal in the Software without
785             restriction, including without limitation the rights to use,
786             copy, modify, merge, publish, distribute, sublicense, and/or
787             sell copies of the Software, and to permit persons to whom
788             the Software is furnished to do so, subject to the following
789             conditions:
790              
791             The above copyright notice and this permission notice shall be
792             included in all copies or substantial portions of the Software.
793              
794             THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
795             EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
796             OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
797             NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
798             HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
799             WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
800             FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
801             OTHER DEALINGS IN THE SOFTWARE.\
802              
803             File: File.pm
804              
805             Authors: Jason Bartlett, Kaustubh Gadkari, Dan Massey, Cathie Olschanowsky
806             Date: 13 October 2013
807              
808             =cut
809              
810             1; # End of BGPmon::Fetch::File