As already discussed in: rsnapshot backup solution I am using rsnapshot as my main backup solutions for a variety of servers.
But I am lazy and often forget about the correct settings, commands, etc. For this reason I started building up some web front ends for several common daily tasks. Just to remember what to do and how and of course to have some fun with programming.
And here is another part of this series: The Rsnapshot Web Front End:
The complete webpage is enclosed into one file – this was my first internal order, because I wanted something that may be installed very easily. There is also a file structure as follows:
main folder:
- default – the base for new config files
- pid – the process id file
- logs – the folder with log files
- rsnapshot – the folder holding the config files
- backup.php – the web front end
The default file is cloned to get the base for an new config file and as such is the master. All files in folder rsnapshot are shown in a matrix to access the action.
The web page itself is self explanatory and allows to:
- view the config and logfiles,
- create and delete a config file/server entry
- start a backup process and
- edit the config file
Any new entry made in the config file is checked against a regular expression already within the backup.php file to avoid misconfigurations. The editing is further devided into normal and expert mode whereas normal mode is only showing the most important options and expert mode is showing all options.
The styling/design of the page is very poor and might be improved later on. Also the security of the page is lacking some attention and might change.
But now to the code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 | <?php ############# HEAD ############################ echo "<!DOCTYPE html>\n" ; echo "<html>\n<head>\n <meta charset='utf-8'><meta name='viewport' content='width=device-width, initial-scale=1.0'>\n" ; ############# style sheets ######################## echo "<title>Backup Admin Page</title>\n</head>\n<body>\n\n" ; echo "<header><h1>Backup Admin - Main Page</h1>" ; echo "<h4>created to configure my own backup system based on rsnapshot</h4></header>\n" ; echo "<style>.red { color: red;} .blue {color: blue;} .hide {display: none;} .comment { width: 100%;}</style>" ; ############# Configuration ########################## $config [ 'configfile' ] = "rsnapshot/" ;# folder for config files $config [ 'logfile' ] = "log/" ;# folder for logfiles $config [ 'command' ] = "ls -l" ;# command to be executed to start backup ##################################### no editing below here ########################### $PHP_SELF = htmlspecialchars( $_SERVER [ 'PHP_SELF' ]); # use one file only which will be reload with new parameters all the time $general_menu = array ( "Cancel" , "NewHost" );# general menue options not associated with host/server $menue_items = array ( "ShowConfigfile" , "StartBackup" , "ShowLogfile" , "EditConfigfile" , "Delete" );# menue items for each host/server $config [ 'columns' ] = count ( $menue_items );# the number of columns in total $config [ 'values' ] = array ( "snapshot_root" , "interval" , "backup" , "no_create_root" , "cmd_cp" , "cmd_rm" , "cmd_rsync" , "cmd_ssh" , "cmd_logger" , "cmd_du" , "verbose" , "loglevel" , "rsync_long_args" , "ssh_args" , "du_args" , "logfile" , "lockfile" , "config_version" ); $config [ "inputtype" ][ "interval" ] = array ( "weekly" , "hourly" , "daily" , "monthly" ); $config [ "inputtype" ][ "verbose" ] = array ( "1" , "2" , "3" , "4" , "5" ); $config [ "inputtype" ][ "loglevel" ] = array ( "1" , "2" , "3" , "4" , "5" ); ##################################### function definitions ######################################## ####### clear html variables from malicious code function clean_html( $variable ){ return trim(htmlspecialchars( $variable )); } ###### debug output of POST function debug_output(){ echo "<pre><br><h1>Hier kommt der schamass!</h1>" ; print_r( $_POST ); echo "</pre>" ; return ; } ###### parse contents of configfile and store everything in two arrays function parse_file_contents( $filename ){ global $inputvalues ; # one matrix for all values already filled with defaults if ( $data = file_get_contents ( $filename )){ $contents = explode ( "\n" , $data ); $comment = "" ; foreach ( $contents as $line ) { # parse each line of the input file seperately $line = trim( $line ); $value = "" ; # and override default values with config file values if ( strlen ( $line ) && substr ( $line , 0, 1) == '#' ) # a comment line $comment .= "\n" . $line ; elseif ( strlen ( $line )){ # non empty line found $key = trim( strtok ( $line , " \t" )); # name of the specific option is first parameter while ( $inputpart = strtok ( " \t" )) $value = $value .trim( $inputpart ). "\t" ; # rest of the line is combined as value if ( $key == "exclude" ) # special treatement for excluded directories $inputvalues [ "exclude" ][] = trim( $value ); else # normal value will override default values in array $inputvalues [ "values" ][ $key ] = trim( $value ); if ( $comment != "" ) {# non enpty comment will be stored $inputvalues [ 'comment' ][ $key ] = $comment ; $comment = "" ; } } } $return = "File: " . $filename . " successfully paresed!" ; } else # could not get file contents via function so die $return = "Could not open file: " . $filename ; return $return ; } ####### save config file function save_conf_file( $inputvalues , $filename ){ global $config ; $output = "" ; foreach ( $config [ 'values' ] as $key ){# iterate over all config file parameters if (isset( $inputvalues [ 'comment' ][ $key ])) $output .= $inputvalues [ 'comment' ][ $key ]. "\n" ; if (isset( $inputvalues [ 'values' ][ $key ])) $output .= $key . "\t" . $inputvalues [ 'values' ][ $key ]. "\n" ; } if (isset( $inputvalues [ 'comment' ][ 'exclude' ])) $output .= $inputvalues [ 'comment' ][ 'exclude' ]. "\n" ; foreach ( $inputvalues [ 'exclude' ] as $key => $value ) if ( $inputvalues [ 'exclude' ][ $key ] != "" ) $output .= "exclude" . "\t" . $value . "\n" ; /* if ($handle = fopen($filename, "w")){ # open configfile for write fwrite($handle, $output); fclose($handle); } else $return = "Could not open file: ".$filename; */ return "<pre>" . $output . "</pre>" ; # return $return ; } ####### edit values using defaults or values from config file function edit_conf_file( $inputvalues , $config , $mode ){ $class = "show" ; $return = "<table border='1' width='100%'>\n<tr><td colspan='4' align='right'>" ; $return .= "<input type='submit' name='action[Expert]' value='" . $mode . "'></td></tr>\n" ; foreach ( $config [ 'values' ] as $key ){ if ( $key == "no_create_root" && $mode == "Normal" ) $class = "hide" ;# show other entries only in expert mnode if (isset( $inputvalues [ 'comment' ][ $key ])){# any comments for the forthcoming option? $rows = substr_count( $inputvalues [ 'comment' ][ $key ], "\n" ) + 2; $return .= "<tr class='" . $class . "'><td colspan='3'><textarea cols='100%' rows='" . $rows ; $return .= "' name='inputvalues[comment][" . $key . "]'> " . $inputvalues ['comment'][ $key ]; $return .= " </textarea></td></tr>\n" ; }# and now the entry $return .= "<tr class='" . $class . "'><td>" . $key . "</td>" ; if (isset( $config [ 'inputtype' ][ $key ])) {# no simple input field but a select box $return .= "<td><select name='inputvalues[values][" . $key . "]' size='1'>" ; foreach ( $config [ 'inputtype' ][ $key ] as $value ){ $return .= "<option" ; if (isset( $inputvalues [ 'values' ][ $key ]) && strtok ( $inputvalues [ 'values' ][ $key ], " " ) == $value ) $return .= " selected " ; $return .= ">" . $value . "</option>\n" ; } $return .= "</select></td>" ; } else {# simple input field $return .= "<td><input type='input' name='inputvalues[values][" . $key . "]' value='" ; if (isset( $inputvalues [ 'values' ][ $key ])) $return .= $inputvalues [ 'values' ][ $key ]; $return .= "'></td>" ; } $return .= "<td></td></tr>\n" ; }################# Exclude directories ################## $return .= "<tr class='show'><td colspan='4'>Exclude Directories:</td></tr>\n" ; if (isset( $inputvalues [ 'comment' ][ 'exclude' ])){ $rows = substr_count( $inputvalues [ 'comment' ][ 'exclude' ], "\n" ) + 2; $return .= "<tr class='show'><td colspan='3'><textarea cols='100%' rows='" . $rows ; $return .= "' name='inputvalues[comment][exclude]'> " . $inputvalues ['comment '][' exclude']; $return .= " </textarea></td></tr>\n" ; } if (isset( $inputvalues [ 'exclude' ])){ foreach ( $inputvalues [ 'exclude' ] as $subkey => $value ){# show excludes as checkbox list if ( $value != "" ) {# only if there is an entry and not en empty line $return .= "<tr class='show'><td colspan='2'>" . $value . "</td><td><input type='checkbox' name='inputvalues[exclude]" ; $return .= "[" . $subkey . "]' value='" . $value . "' checked></td></tr>\n" ; } } } for ( $i =0; $i < 5; $i ++)# add 5 extra rows for exclude input $return .= "<tr class='show'><td>Add exclude folder</td><td colspan='2'><input type='input' value='' name='inputvalues[exclude][]'></td></tr>\n" ; $return .= "<tr class='show'><td align='center' colspan='2'><input type='submit' value='SaveConfigfile' " ; $return .= "name='action[SaveConfigfile]'></td><td align='center' colspan='2'>" ; $return .= "<input type='submit' name='action[Cancel]' value='Cancel'></td></tr>\n</table>\n" ; return $return ; } ####### function generate menu function generate_menue( $menue_items , $configfile ){ $return = "<tr><thead align='center'>" ; foreach ( $menue_items as $key ) # table headers $return .= "<th>" . $key . "</th>" ; $return .= "</thead>\n<tbody align='center'>\n" ; foreach (scandir( $configfile ) as $file ){# iterate over complete directory contents if ( substr ( $file , 0, 1) != "." ) { # dotfiles are ignored $file = htmlspecialchars( $file ); $return .= "<tr>" ; foreach ( $menue_items as $key ) { $return .= "<td><input type='submit' value=' " . $file . " ' name='action[" . $key . "]'></td>" ; } $return .= "</tr>\n" ; } } return $return ;} ###################################################################### ############################## MAIN ################################## ###################################################################### echo "<body><form method='POST' action='" . $PHP_SELF . "'>\n" ;# page header with headline and table headers echo "<table border=1 cellspacing='10'>\n" ;# everything will be bound to one table echo generate_menue( $menue_items , $config [ 'configfile' ]); $cols = round ( $config [ 'columns' ] / 2); $cols_remain = $config [ 'columns' ] - $cols ; echo "<tr><td colspan='" . $cols . "'><input type='submit' name='action[Cancel]' value='Cancel'></td>" ; echo "<td colspan='" . $cols_remain . "'><input type='submit' name='action[NewHost]' value='NewHost'></td></tr>" ; ######################### Main Menue ##################################### if (isset( $_POST [ 'expertMode' ]) && $_POST [ 'expertMode' ] == "Expert" ) $mode = "Expert" ; else $mode = "Normal" ; if (isset( $_POST [ 'action' ])) { $menue_entry = array_keys ( $_POST [ 'action' ])[0];# action[<menue_entry>] -> <host>/<menue_entry> $host = clean_html( $_POST [ 'action' ][ $menue_entry ]); switch ( $menue_entry ){ case "EditConfigfile" :########################################### if ( file_exists ( $config [ 'configfile' ]. $host )) { $return = "<h3>Edit (" . $host . ")</h3>" ;# show headline $return .= parse_file_contents( $config [ 'configfile' ]. $host ); $return .= "<input type='hidden' name='hostname' value='" . $host . "'>" ; $return .= "<pre class='blue'>" .edit_conf_file( $inputvalues , $config , $mode ). "</pre>" ; } else { $return = "<h3>Error (" . $host . ")</h3>" ;# show headline $return .= "<pre class='red'>File: " . $config [ 'configfile' ]. $host . " does not exist!</pre>\n" ;# show error message } break ; case "Expert" :################################################### if (isset( $_POST [ 'hostname' ])){ $return = "<h3>Edit (" . $_POST [ 'hostname' ]. ")</h3>" ;# show headline if ( $host == "Expert" ) $mode = "Normal" ; else $mode = "Expert" ; $return .= "<input type='hidden' name='hostname' value='" . $_POST ['hostname ']."' >"; $return .= "<pre class='blue'>" .edit_conf_file( $_POST [ 'inputvalues' ], $config , $mode ). "</pre>" ; } else { $return = "<h3>Error (" . $host . ")</h3>" ;# show headline $return .= "<pre class='red'>Error! No host defined!</pre>\n" ;# show error message } break ; case "SaveConfigfile" :############################################ if (isset( $_POST [ 'inputvalues' ])){ $return = "<h3>Save Configfile (" . $host . ")</h3>" ;# show headline $return .= "<pre class='blue'>" .save_conf_file( $_POST [ 'inputvalues' ], $config [ 'configfile' ]. $host ). "</pre>" ;# write file contents } else { $return = "<h3>Error (" . $host . ")</h3>" ;# show headline $return .= "<pre class='red'>Error! No host defined!</pre>\n" ;# show error message } break ; case "ShowConfigfile" :############################################ if ( file_exists ( $config [ 'configfile' ]. $host )){# check if file exists in folder $return = "<h3>Show Configfile (" . $host . ")</h3>" ;# show headline $return .= "<pre class='blue'>" . file_get_contents ( $config [ 'configfile' ]. $host ). "</pre>" ;# get file contents } else { $return = "<h3>Error (" . $host . ")</h3>" ;# show headline $return .= "<pre class='red'>File: " . $config [ 'configfile' ]. $host . " does not exist!</pre>" ;# show error } break ; case "StartBackup" :############################################### $befehl = $config [ 'command' ]. " " . $config [ 'configfile' ]. $host . " 2>&1" ;# create backup command if ( file_exists ( $config [ 'configfile' ]. $host )){# check if file exists $return = "<h3>Start Backup (" . $host . ")</h3>" ; $return .= "<pre class='blue'>Execute Command: (" . $befehl . ")</pre>" ; $return .= exec ( $befehl , $output , $return_val );# execute backup command # $return .= $output . " " . $return_val ; } else { $return = "<h3>Error (" . $host . ")</h3>" ;# show headline $return .= "<pre class='red'>File: " . $config [ 'configfile' ]. $host . " does not exist!</pre>" ;# show error message } break ; case "ShowLogfile" :############################################## if ( file_exists ( $config [ 'logfile' ]. $host )){ # check if file exists $return = "<h3>Show Logfile (" . $host . ")</h3>" ;# show headline $return .= "<pre class='blue'>" . file_get_contents ( $config [ 'logfile' ]. $host ). "</pre>" ;# get file contents } else { $return = "<h3>Error (" . $host . ")</h3>" ;# show headline $return .= "<pre class='red'>File: " . $config [ 'logfile' ]. $host . " does not exist!</pre>" ;# show error message } break ; case "Delete" :################################################### $return = "<h3>Delete (" . $host . ")</h3>" ;# show headline $return .= "<div class='red'>You really want to delete:" ; $return .= "<input type='submit' name='action[ReallyDelete]' value='" . $host . "'></div>" ; break ; case "ReallyDelete" :############################################# $return = "<h3>Delete (" . $host . ")</h3>" ;# show headline if ( file_exists ( $config [ 'configfile' ]. $host )){ if (unlink( $config [ 'configfile' ]. $host )){ unlink( $config [ 'configfile' ]. $host ); $return .= "<pre class='blue'>Host: " . $host . " successfully deleted!</pre>" ; } else $return .= "<pre class='red'>An error occured while deleting Host: " . $host . "</pre>" ; } else $return .= "<pre class='red'>File: " . $config [ 'configfile' ]. $host . " does not exist!</pre>\n" ;# show error message break ; case "NewHost" :################################################## $return = "<h3>Create (New Host)</h3>" ;# show headline $return .= "<div class='blue'>Please name new host entry:" ; $return .= "<input type='input' name='hostname' value='New Host'>" ; $return .= "<input type='submit' name='action[ReallyNewHost]' value='Create'></div>" ; break ; case "ReallyNewHost" :############################################ $return = "<h3>Create (" . $_POST [ 'hostname' ]. ")</h3>" ;# show headline if ( file_exists ( $config [ 'configfile' ]. $_POST [ 'hostname' ])) $return .= "<pre class='red'>Host: <em>" . $_POST [ 'hostname' ]. "</em> already exists!</pre>" ; else { if ( copy ( 'default' , $config [ 'configfile' ]. $_POST [ 'hostname' ])) { $return .= "<pre class='red'>New Host: <em>" . $_POST [ 'hostname' ]. "</em> created!</pre>" ; $return .= parse_file_contents( $config [ 'configfile' ]. $_POST [ 'hostname' ]); $return .= "<input type='hidden' name='hostname' value='" . $_POST ['hostname ']."' >"; $return .= "<pre class='blue'>" .edit_conf_file( $inputvalues , $config , $mode ). "</pre>" ; } else $return .= "<pre class='red'>Error while copying!</pre>" ; } break ; default :######################################################### $return = "<h3>Error (" . $menue_entry . ")</h3>" ;# show headline $return .= "<pre class='red'>Please choose one of the options above!</pre>" ; break ; } } elseif (isset( $_POST [ 'Comment' ])){# comment button pressed $menue_entry = clean_html( array_keys ( $_POST [ 'Comment' ])[0]);# Comment[<config file option>][<line no>] -> <+/-> $line = clean_html( array_keys ( $_POST [ 'Comment' ][ $menue_entry ])[0]); if (isset( $_POST [ 'hostname' ])){ $return = "<h3>Edit (" . $_POST [ 'hostname' ]. ")</h3>" ;# show headline $return .= "<input type='hidden' name='hostname' value='" . $_POST ['hostname ']."' >"; $inputvalues = $_POST [ 'inputvalues' ]; unset( $inputvalues [ 'comment' ][ $menue_entry ][ $line ]);# delete specific line from variable $return .= "<pre class='blue'>" .edit_conf_file( $inputvalues , $config , $mode ). "</pre>" ; } else $return = "<pre class='red'>Error! No host defined!</pre>\n" ;# show error message } else $return = "" ; $return .= "<input type='hidden' name='expertMode' value='" . $mode . "'>" ; ################# Main Menue End ######################################## echo "<tr><td colspan='" . $config ['columns ']."' align= 'left' >". $return . "</td></tr>" ; echo "</tr>\n</tbody></table>\n" ; echo "<pre>" ;print_r( $config ); echo "</pre>" ; echo "<pre> " ;print_r( $inputvalues ); echo "</pre>" ; debug_output(); ############################# END ################################################################# echo "<br><br>" ; echo "</form>\n<footer id='footer'>My personal page hosted on my own server © olkn</footer></body></HTML>\n" ; ########### END ###################################?> |