| 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 |