| 23 | rodolico | 1 | <?php
 | 
        
           |  |  | 2 |   | 
        
           |  |  | 3 | /*
 | 
        
           |  |  | 4 |  * Requires php-pear and libyaml under Debian Wheezy
 | 
        
           |  |  | 5 |  *
 | 
        
           |  |  | 6 |  * apt-get install php-pear php5-dev libyaml-dev libyaml-0-2 libyaml-0-2-dbg
 | 
        
           |  |  | 7 |  * pecl install yaml
 | 
        
           |  |  | 8 |  * echo 'extension=yaml.so' >> /etc/php5/cli/php.ini
 | 
        
           |  |  | 9 |  * echo 'extension=yaml.so' >> /etc/apache2/cli/php.ini
 | 
        
           |  |  | 10 |  * 
 | 
        
           | 25 | rodolico | 11 |  * truncate table backups_run
 | 
        
           | 24 | rodolico | 12 |  * alter table backups_run add skipped bigint(20),add file_name varchar(60),add notes text;
 | 
        
           |  |  | 13 |  * alter table backups_run modify end_time  datetime null, modify start_time datetime null;
 | 
        
           | 25 | rodolico | 14 |  * alter table backups_run add added_date datetime not null;
 | 
        
           | 23 | rodolico | 15 |  * 
 | 
        
           | 28 | rodolico | 16 |  * Version 0.1.1 20160713 RWR
 | 
        
           |  |  | 17 |  * Fixed issue where report had no transfers. In that case, header lines
 | 
        
           |  |  | 18 |  * are terminated by two blank lines, not the constant 'sending incremental file
 | 
        
           |  |  | 19 |  * list'. Simply modified to terminate on an empty line OR the constant
 | 
        
           |  |  | 20 |  * 
 | 
        
           |  |  | 21 |  * Also set it up to only report on one file at a time, ie accept
 | 
        
           |  |  | 22 |  * only one parameter, and that is assumed to be a file name
 | 
        
           | 35 | rodolico | 23 |  * 
 | 
        
           |  |  | 24 |  * Version 0.2.0 20160826 RWR
 | 
        
           |  |  | 25 |  * Fixed problem where file name has YYYYMMDD_HHMMSS_servername_backup.log instead of
 | 
        
           |  |  | 26 |  *    YYYYMMDD_servername_backup.log (it now checks for both)
 | 
        
           |  |  | 27 |  * Start and end time now accepted as YYYYMMDD_HHMMSS or YYYYMMDDHHMMSS
 | 
        
           | 95 | rodolico | 28 |  *
 | 
        
           |  |  | 29 |  * Version 0.2.1 20191208 RWR
 | 
        
           |  |  | 30 |  * When numeric strings are looked for, allow comma's in them and remove them before recording in the database
 | 
        
           |  |  | 31 |  * Fixes modifications made to rsync where reports now contain comma's.
 | 
        
           | 23 | rodolico | 32 | */
 | 
        
           |  |  | 33 |   | 
        
           | 25 | rodolico | 34 |    /*
 | 
        
           |  |  | 35 |     * This will return error codes
 | 
        
           |  |  | 36 |     * 1 - Invalid Data File Name
 | 
        
           |  |  | 37 |     * 2 - Could not open the file
 | 
        
           |  |  | 38 |     * 3 - Duplicate Report
 | 
        
           |  |  | 39 |     * 4 - Server Not Found
 | 
        
           |  |  | 40 |     */
 | 
        
           |  |  | 41 |   | 
        
           |  |  | 42 |   | 
        
           | 23 | rodolico | 43 | require 'library.php';
 | 
        
           | 95 | rodolico | 44 | $VERSION='0.2.1';
 | 
        
           | 23 | rodolico | 45 |   | 
        
           | 24 | rodolico | 46 | $results = array();
 | 
        
           | 23 | rodolico | 47 |   | 
        
           | 24 | rodolico | 48 | function parseFile( $filename, $path, $maxLineLength = 4096 ) {
 | 
        
           |  |  | 49 |    $data = array ( 
 | 
        
           |  |  | 50 |                      'server_name' => '',
 | 
        
           |  |  | 51 |                      'report_date' => '',
 | 
        
           |  |  | 52 |                      'start_time' => '',
 | 
        
           |  |  | 53 |                      'version' => '',
 | 
        
           |  |  | 54 |                      'end_time' => '',
 | 
        
           | 28 | rodolico | 55 |                      'files_count' => 0,
 | 
        
           |  |  | 56 |                      'files_size' => 0,
 | 
        
           | 24 | rodolico | 57 |                      'transferred_count' => 0,
 | 
        
           | 28 | rodolico | 58 |                      'transferred_size' => 0,
 | 
        
           | 24 | rodolico | 59 |                      'files_deleted' => 0,
 | 
        
           |  |  | 60 |                      'skipped' => 0,
 | 
        
           | 28 | rodolico | 61 |                      'data_sent' => 0,
 | 
        
           |  |  | 62 |                      'data_received' => 0,
 | 
        
           | 24 | rodolico | 63 |                      'disk_used' => '',
 | 
        
           |  |  | 64 |                      'notes' => '',
 | 
        
           |  |  | 65 |    );
 | 
        
           | 94 | rodolico | 66 |   | 
        
           |  |  | 67 |    function cleanNumber( $number ) {
 | 
        
           |  |  | 68 |       return preg_replace("/[^0-9.]/", "", $number );
 | 
        
           |  |  | 69 |    }
 | 
        
           | 23 | rodolico | 70 |   | 
        
           |  |  | 71 |   | 
        
           | 24 | rodolico | 72 |    $matches = array();
 | 
        
           | 23 | rodolico | 73 |   | 
        
           | 24 | rodolico | 74 |    $line;
 | 
        
           | 23 | rodolico | 75 |   | 
        
           | 24 | rodolico | 76 |    $data['file_name'] = $filename;
 | 
        
           |  |  | 77 |    # get server name from the file name, which should be
 | 
        
           | 35 | rodolico | 78 |    # date_time_servername.backup.log
 | 
        
           |  |  | 79 |    if ( preg_match( '/(\d+)_(\d+)_([a-z0-9_.-]+)\.backup\.log/', $filename, $matches ) ) {
 | 
        
           |  |  | 80 |       $data['report_date'] = $matches[1] . $matches[2];
 | 
        
           |  |  | 81 |       $data['server_name'] = $matches[3];
 | 
        
           |  |  | 82 |    } elseif ( preg_match( '/(\d+)_([a-z0-9_.-]+)\.backup\.log/', $filename, $matches ) ) {
 | 
        
           | 24 | rodolico | 83 |    # datetime_servername.backup.log
 | 
        
           |  |  | 84 |       $data['report_date'] = $matches[1];
 | 
        
           |  |  | 85 |       $data['server_name'] = $matches[2];
 | 
        
           |  |  | 86 |    } else {
 | 
        
           |  |  | 87 |       # ensure only three parts returned
 | 
        
           |  |  | 88 |       $data['error'] = "1\tInvalid Data File Name";
 | 
        
           |  |  | 89 |       return $data;
 | 
        
           | 23 | rodolico | 90 |    }
 | 
        
           |  |  | 91 |   | 
        
           | 24 | rodolico | 92 |    $log = fopen ( $path . '/' . $filename , 'rb' );
 | 
        
           |  |  | 93 |    if ( $log ) {
 | 
        
           | 28 | rodolico | 94 |       // get through the header lines, ended by either a blank line
 | 
        
           |  |  | 95 |       // or the constant 'sending incremental file list'
 | 
        
           | 24 | rodolico | 96 |       while (( $line = fgets( $log, $maxLineLength )) !== false ) {
 | 
        
           | 28 | rodolico | 97 |          $line = rtrim( $line );
 | 
        
           |  |  | 98 |          if ( $line ) {
 | 
        
           |  |  | 99 |             if ( preg_match( '/^backup\s+v?([\d.]+)$/', $line, $matches ) ) {
 | 
        
           |  |  | 100 |                $data['version'] = $matches[1];
 | 
        
           |  |  | 101 |             }
 | 
        
           |  |  | 102 |             // this line is the end of the header lines
 | 
        
           |  |  | 103 |             if ( preg_match( '/sending incremental file list/', $line, $matches ) ) {
 | 
        
           |  |  | 104 |                break;
 | 
        
           |  |  | 105 |             }
 | 
        
           |  |  | 106 |          } else {
 | 
        
           | 24 | rodolico | 107 |             break;
 | 
        
           |  |  | 108 |          }
 | 
        
           | 23 | rodolico | 109 |       }
 | 
        
           |  |  | 110 |   | 
        
           | 24 | rodolico | 111 |       // count line by line processing information. These are the actual lines
 | 
        
           |  |  | 112 |       // indicating files checked and how they were processed
 | 
        
           |  |  | 113 |       while (( $line = fgets( $log, $maxLineLength )) !== false ) {
 | 
        
           |  |  | 114 |          $line = rtrim($line);
 | 
        
           |  |  | 115 |          if ( $line ) {
 | 
        
           |  |  | 116 |             if ( ! preg_match( '~/$~', $line ) ) // count lines not ending in /, ie not directories
 | 
        
           |  |  | 117 |                $data['transferred_count']++;
 | 
        
           |  |  | 118 |             if ( preg_match( '/^deleting /', $line ) ) // count number deleted
 | 
        
           |  |  | 119 |                $data['files_deleted']++;
 | 
        
           |  |  | 120 |             if ( preg_match( '/^skipping /', $line ) )  // count number skipped
 | 
        
           |  |  | 121 |                $data['skipped']++ ;
 | 
        
           |  |  | 122 |          } else {
 | 
        
           |  |  | 123 |             break;
 | 
        
           |  |  | 124 |          }
 | 
        
           | 23 | rodolico | 125 |       }
 | 
        
           | 24 | rodolico | 126 |       $data['transferred_count'] -= $data['files_deleted'] + $data['skipped'];
 | 
        
           | 23 | rodolico | 127 |   | 
        
           | 24 | rodolico | 128 |       // we should have a stats summary at the end
 | 
        
           |  |  | 129 |       while (( $line = fgets( $log, $maxLineLength )) !== false ) {
 | 
        
           |  |  | 130 |          $data['notes'] .= $line; // everything here goes into notes
 | 
        
           | 94 | rodolico | 131 |          if ( preg_match( '/Number of files:\s+([0-9,]+)/', $line, $matches ) ) {
 | 
        
           |  |  | 132 |             $data['files_count'] = cleanNumber($matches[1]);
 | 
        
           |  |  | 133 |          } elseif ( preg_match( '/Number of files transferred:\s+([0-9,]+)/', $line, $matches ) ) {
 | 
        
           |  |  | 134 |             $data['transferred_count'] = cleanNumber($matches[1]);
 | 
        
           |  |  | 135 |          } elseif ( preg_match( '/Total file size:\s+([0-9,]+) bytes/', $line, $matches )  ) {
 | 
        
           |  |  | 136 |             $data['files_size'] = cleanNumber($matches[1]);
 | 
        
           |  |  | 137 |          } elseif ( preg_match( '/Total transferred file size:\s+([0-9,]+) bytes/', $line, $matches )  ) {
 | 
        
           |  |  | 138 |             $data['transferred_size'] = cleanNumber($matches[1]);
 | 
        
           |  |  | 139 |          } elseif ( preg_match( '/Total bytes sent:\s+([0-9,]+)/', $line, $matches )  ) {
 | 
        
           |  |  | 140 |             $data['data_sent'] = cleanNumber($matches[1]);
 | 
        
           |  |  | 141 |          } elseif ( preg_match( '/Total bytes received:\s+([0-9,]+)/', $line, $matches )  ) {
 | 
        
           |  |  | 142 |             $data['data_received'] = cleanNumber($matches[1]);
 | 
        
           | 24 | rodolico | 143 |          } elseif ( preg_match( '/Backup Version\s+v?([\d.]+)/', $line, $matches )  ) {
 | 
        
           | 94 | rodolico | 144 |          // this is the end of the rsync stats summary, and the
 | 
        
           | 24 | rodolico | 145 |             // beginning of the rsbackup summary
 | 
        
           |  |  | 146 |             $data['version'] = $matches[1];
 | 
        
           |  |  | 147 |             break;
 | 
        
           |  |  | 148 |          }
 | 
        
           | 23 | rodolico | 149 |       }
 | 
        
           |  |  | 150 |   | 
        
           | 24 | rodolico | 151 |       // process rsbackup summary
 | 
        
           |  |  | 152 |       while (( $line = fgets( $log, $maxLineLength )) !== false ) {
 | 
        
           |  |  | 153 |          $data['notes'] .= $line;
 | 
        
           | 35 | rodolico | 154 |          if ( preg_match( '/^Begin\s+([\d_]+)/', $line, $matches )   ) {
 | 
        
           |  |  | 155 |             $data['start_time'] = str_replace( '_', '', $matches[1] );
 | 
        
           |  |  | 156 |          } elseif ( preg_match( '/Complete\s+([\d_]+)/', $line, $matches )   ) {
 | 
        
           |  |  | 157 |             $data['end_time'] = str_replace( '_', '', $matches[1] );
 | 
        
           | 24 | rodolico | 158 |          } elseif ( preg_match( '/^Status:/', $line, $matches )   ) {
 | 
        
           |  |  | 159 |             break;
 | 
        
           | 23 | rodolico | 160 |          }
 | 
        
           |  |  | 161 |       }
 | 
        
           | 24 | rodolico | 162 |       // the rest of it just goes into notes
 | 
        
           |  |  | 163 |       while (( $line = fgets( $log, $maxLineLength )) !== false ) {
 | 
        
           |  |  | 164 |          $data['notes'] .= $line;
 | 
        
           | 23 | rodolico | 165 |       }
 | 
        
           | 24 | rodolico | 166 |       fclose ( $log );
 | 
        
           |  |  | 167 |    } else {
 | 
        
           |  |  | 168 |       $data['error'] = "2\tCould not open file name $filename for read";
 | 
        
           | 23 | rodolico | 169 |    }
 | 
        
           | 24 | rodolico | 170 |    return $data;
 | 
        
           | 23 | rodolico | 171 |   | 
        
           | 24 | rodolico | 172 | }
 | 
        
           | 23 | rodolico | 173 |   | 
        
           |  |  | 174 |   | 
        
           | 24 | rodolico | 175 | function recordMessage( $message ) {
 | 
        
           |  |  | 176 |    global $results;
 | 
        
           |  |  | 177 |    $results[] = $message;
 | 
        
           |  |  | 178 | }
 | 
        
           | 23 | rodolico | 179 |   | 
        
           |  |  | 180 |   | 
        
           | 24 | rodolico | 181 | /* 
 | 
        
           | 35 | rodolico | 182 |  * checks for a duplicate report, ie one that has already been run.q
 | 
        
           | 24 | rodolico | 183 |  * if this computer has a report already for this date/time
 | 
        
           |  |  | 184 |  */ 
 | 
        
           |  |  | 185 | function checkDuplicate( $backups_id, $report_date) {
 | 
        
           |  |  | 186 |    $sql = "select count(*) from backups_run where backups_id = $backups_id and report_date = $report_date";
 | 
        
           |  |  | 187 |    $count = getOneDBValue( $sql );
 | 
        
           |  |  | 188 |    return $count ? "3\tDuplicate Report for $backups_id on $report_date" : null;
 | 
        
           |  |  | 189 | } // recordReport
 | 
        
           | 23 | rodolico | 190 |   | 
        
           |  |  | 191 |   | 
        
           | 24 | rodolico | 192 | function findServer( $name ) {
 | 
        
           |  |  | 193 |    $sql = "select distinct( device_id ) from (select device_id from device where name = '$name' union select device_id from device_alias where alias = '$name') a";
 | 
        
           |  |  | 194 |    $device_id = getOneDBValue( $sql );
 | 
        
           |  |  | 195 |    if ( ! isset( $device_id ) ) {
 | 
        
           |  |  | 196 |       recordMessage( "Could not locate device $name, not processing record" );
 | 
        
           |  |  | 197 |       return null;
 | 
        
           | 23 | rodolico | 198 |    }
 | 
        
           | 24 | rodolico | 199 |    $sql = "select backups_id from backups where device_id = $device_id";
 | 
        
           |  |  | 200 |    $backups_id = getOneDBValue( $sql );
 | 
        
           |  |  | 201 |    if ( isset( $backups_id ) ) {
 | 
        
           |  |  | 202 |       return $backups_id;
 | 
        
           |  |  | 203 |    } else {
 | 
        
           |  |  | 204 |       recordMessage( "Adding server $name to backups" );
 | 
        
           |  |  | 205 |       $sql = "insert into backups ( device_id,backups_server_id,notes ) values ( $device_id, 1, 'Automatically Added' )";
 | 
        
           |  |  | 206 |       $results = queryDatabaseExtended( $sql );
 | 
        
           |  |  | 207 |       return $results['insert_id'];
 | 
        
           |  |  | 208 |    }
 | 
        
           |  |  | 209 | }
 | 
        
           | 23 | rodolico | 210 |   | 
        
           |  |  | 211 |   | 
        
           | 24 | rodolico | 212 | /* 
 | 
        
           |  |  | 213 |  * Creates an entry in the backups_run table
 | 
        
           |  |  | 214 |  */ 
 | 
        
           |  |  | 215 | function recordReport( $report ) {
 | 
        
           |  |  | 216 |   | 
        
           |  |  | 217 |    $keys = array();
 | 
        
           |  |  | 218 |    $values = array();
 | 
        
           |  |  | 219 |   | 
        
           |  |  | 220 |    foreach ( $report as $keyfield => $value ) {
 | 
        
           |  |  | 221 |       if ( $keyfield == 'server_name' ) {
 | 
        
           |  |  | 222 |          $keys[] = 'backups_id';
 | 
        
           |  |  | 223 |          $device_id = findServer( $value );
 | 
        
           |  |  | 224 |          if ( isset( $device_id ) ) {
 | 
        
           |  |  | 225 |             $values[] = $device_id;
 | 
        
           |  |  | 226 |             $duplicate = checkDuplicate( $device_id, $report['report_date'] );
 | 
        
           |  |  | 227 |             if ( $duplicate )
 | 
        
           |  |  | 228 |                return $report['file_name'] . "\t$duplicate";
 | 
        
           |  |  | 229 |          } else {
 | 
        
           |  |  | 230 |             return $report['file_name'] . "\t4\tServer not found";
 | 
        
           | 23 | rodolico | 231 |          }
 | 
        
           | 24 | rodolico | 232 |       } else {
 | 
        
           |  |  | 233 |          $keys[] = $keyfield;
 | 
        
           |  |  | 234 |          $values[] = makeSafeSQLValue( $value );
 | 
        
           | 23 | rodolico | 235 |       }
 | 
        
           | 24 | rodolico | 236 |    }
 | 
        
           | 25 | rodolico | 237 |    $keys[] = 'added_date'; $values[] = 'now()';
 | 
        
           | 24 | rodolico | 238 |    $sql = 'insert into backups_run (' . implode( ',', $keys ) . ') values (' . implode( ',', $values ) . ')';
 | 
        
           |  |  | 239 |    // if we made it this far, we are ok, so just add the report id
 | 
        
           |  |  | 240 |    $result = queryDatabaseExtended( $sql );
 | 
        
           |  |  | 241 |    return $report['file_name'] . "\t0\tok";
 | 
        
           |  |  | 242 | } // recordReport
 | 
        
           | 23 | rodolico | 243 |   | 
        
           |  |  | 244 |   | 
        
           |  |  | 245 | /*
 | 
        
           |  |  | 246 |  * we don't know where the configuration file will be, so we have a list
 | 
        
           |  |  | 247 |  * below (searchPaths) to look for it. It will load the first one it 
 | 
        
           |  |  | 248 |  * finds, then exit. THUS, you could have multiple configuration files
 | 
        
           |  |  | 249 |  * but only the first one in the list will be used
 | 
        
           |  |  | 250 |  */
 | 
        
           |  |  | 251 |   | 
        
           | 24 | rodolico | 252 | $confFileName = "rsbackupRead.conf.yaml";
 | 
        
           |  |  | 253 | $searchPaths = array( '/etc/camp', '/opt/camp', '/opt/camp/rsbackup', $_SERVER['SCRIPT_FILENAME'], getcwd() );
 | 
        
           | 23 | rodolico | 254 | $configuration = array();
 | 
        
           |  |  | 255 | foreach ( $searchPaths as $path ) {
 | 
        
           |  |  | 256 |    if ( file_exists( "$path/$confFileName" ) ) {
 | 
        
           |  |  | 257 |       $configuration = yaml_parse_file( "$path/$confFileName" );
 | 
        
           |  |  | 258 |       break; // exit out of the loop; we don't try to load it more than once
 | 
        
           |  |  | 259 |    } // if
 | 
        
           |  |  | 260 | } // foreach
 | 
        
           |  |  | 261 |   | 
        
           | 24 | rodolico | 262 |   | 
        
           | 23 | rodolico | 263 | mysql_connect( $configuration['database']['databaseServer'], $configuration['database']['databaseUsername'], $configuration['database']['databasePassword'] ) or die(mysql_error());
 | 
        
           |  |  | 264 | mysql_select_db( $configuration['database']['database'] ) or die(mysql_error());
 | 
        
           |  |  | 265 |   | 
        
           |  |  | 266 |   | 
        
           |  |  | 267 |   | 
        
           | 28 | rodolico | 268 | $filename = $argv[1];
 | 
        
           |  |  | 269 | $report = parseFile( $filename, $configuration['datapath'] . '/' . $configuration['unprocessed_path'] );
 | 
        
           | 35 | rodolico | 270 | # print_r( $report ); print "\n";
 | 
        
           | 28 | rodolico | 271 |   | 
        
           |  |  | 272 | if ( isset( $report['error'] ) ) { // we had an error processing the report
 | 
        
           |  |  | 273 |    $result[] = "$filename\t" . $report['error']; 
 | 
        
           |  |  | 274 | } else {
 | 
        
           |  |  | 275 |    $result[] = recordReport( $report );
 | 
        
           | 23 | rodolico | 276 | }
 | 
        
           |  |  | 277 |   | 
        
           | 24 | rodolico | 278 | print implode( "\n", $result );
 | 
        
           |  |  | 279 |   | 
        
           | 23 | rodolico | 280 | ?>
 |