#!/bin/php
<?php
// TODO Locking if porate-ensure-downstream-running

$CMD_OPTS = parse_opts();
check_reqs();
if($CMD_OPTS["help"]=="yes"){
	display_usage();
}
//TODO if in --cron mode, dont lock Claim flock -> max wait 30min (bei –cron) – damit updater prozess nicht zu lange verzögert wird
echo "Claim flock for update_ensure_downstream... ";
$fp_lock = fopen("/var/run/update_ensure_downstream_oneshot.lock","w+");
flock($fp_lock,LOCK_EX);
echo "done\r\n";
lock($CMD_OPTS["flock_wait"]);

task_main();
fclose($fp_lock);
echo "The following tasks are not implemented yes:\r\n";
echo "RESTARTING?\r\n";
echo "\r\n";
echo "lostfiles?\r\n";
echo "system-check?\r\n";
echo "- dmesg?\r\n";
echo "- btrfs fi usage -t /?\r\n";
echo "- df -m?\r\n";
echo "- tail -f /var/log/beesdaemon?\r\n";
echo "- snapper list?\r\n";
echo "\r\n";


function task_main(){
	task_sync();
	task_maintain_deps(true);
    save_state();
    if(!get_state_todos()) {
        echo "Nothing todo!\r\n";
        display_infos();
        echo "\r\nThank you for using Gentoo update_system.php-Updater!\r\n";
        exit;
    }
    task_buildhost_compile(true);
    task_prefetch_binpkgs(true);
    task_update_portage();
	
	save_state();
	update_subroutine();
	if(get_state_todos()){
		echo "Something went terrible wrong, we have TODOs, but we cant solve it :(\r\n";
		exit(1);
	}
	display_infos();
	echo "\r\nThank you for using Gentoo update_system.php-Updater!\r\n";
}
function display_infos(){
	passthru_call("Get final informations","update_status.php force_update");
}
function update_subroutine(){
	if(get_state_todos()){
		$maxtimes=2;
	}
	else{
		$maxtimes=1;
	}
	$dontskipprepare=false;
	for($i=1;$i<=$maxtimes;$i++){
		display_state("MAIN-PHASE ".$i."/".$maxtimes." Now starting real update with changes on system...");
		if($dontskipprepare){
			task_maintain_deps();
			task_buildhost_compile();
			task_prefetch_binpkgs();
		}
		$dontskipprepare=true;
		if(get_state_todos()){
			update_world($i."/".$maxtimes);
		}
		passthru_call("MAIN-PHASE ".$i."/".$maxtimes,"portage-reinstalls.php");
		passthru_call("MAIN-PHASE ".$i."/".$maxtimes,"etc-autoupdate.php");
		update_depclean();
		
		if(get_state_changes()){
			if($maxtimes<10) $maxtimes++;
		}
	}
}
function update_depclean(){
	$exec = shell_exec("emerge -pv --depclean 2>&1");
	if(stripos($exec,"Dependencies could not be completely resolved")!==false && stripos($exec,"emerge --update --newuse --deep --with-bdeps=y")!==false){
        echo "Doing safety check... ";
        $safety_check = shell_exec("emerge --depclean --with-bdeps=n --dynamic-deps=n -p 2>&1");
        $split_lines = explode("\n",$safety_check);
        $break_emerg=false;
        foreach($split_lines as $line){
            if(stripos($line,"app-portage/pw-cfg-portage-systemandprofile")!==false && stripos($line,"All selected pack")!==false){
                $break_emerg=true;
            }
        }
        if($break_emerg){
            echo "ERROR! - depclean would 1 kill app-portage/pw-cfg-portage-systemandprofile :(\r\n";
            echo "Details: ".$safety_check."\r\n";
            echo "There goes something very wrong with depclean :(\r\n";
            exit(1);
        }
        echo "\r\n";
		$resultcode = passthru_call("DEPCLEAN and remove build deps","emerge --depclean --with-bdeps=n --dynamic-deps=n");
        if($resultcode==1){
            echo "Error while remove deps - start again with update world bdeps=y\r\n";
            passthru_call("Update World with bdeps=y","emerge --update --newuse --deep --with-bdeps=y @world");

            echo "Doing safety check... ";
            $safety_check = shell_exec("emerge --depclean --with-bdeps=n --dynamic-deps=n -p 2>&1");
            $split_lines = explode("\n",$safety_check);
            $break_emerg=false;
            foreach($split_lines as $line){
                if(stripos($line,"app-portage/pw-cfg-portage-systemandprofile")!==false && stripos($line,"All selected pack")!==false){
                    $break_emerg=true;
                }
            }
            if($break_emerg){
                echo "ERROR! - depclean would 1 kill app-portage/pw-cfg-portage-systemandprofile :(\r\n";
                echo "Details: ".$safety_check."\r\n";
                echo "There goes something very wrong with depclean :(\r\n";
                exit(1);
            }
            echo "\r\n";
            passthru_call("DEPCLEAN and remove build deps","emerge --depclean --with-bdeps=n --dynamic-deps=n");
        }
	}
	else{
        echo "Doing safety check... ";
        $safety_check = shell_exec("emerge --depclean --with-bdeps=y --dynamic-deps=n -p 2>&1");
        $split_lines = explode("\n",$safety_check);
        $break_emerg=false;
        foreach($split_lines as $line){
            if(stripos($line,"app-portage/pw-cfg-portage-systemandprofile")!==false && stripos($line,"All selected pack")!==false){
                $break_emerg=true;
            }
        }
        if($break_emerg){
            echo "ERROR! - depclean would 2 kill app-portage/pw-cfg-portage-systemandprofile :(\r\n";
            echo "Details: ".$safety_check."\r\n";
            echo "There goes something very wrong with depclean :(\r\n";
            exit(1);
        }
        echo "\r\n";
		passthru_call("DEPCLEAN and pretend build deps","emerge --depclean --with-bdeps=y --dynamic-deps=n");
	}
}
function update_world($mainphase){
	if(get_state_todos()){
		$maxtimes=2;
	}
	else{
		$maxtimes=1;
	}
	$dontskipprepare=false;
	for($i=1;$i<=$maxtimes;$i++){
		display_state("SUB-PHASE ".$i."/".$maxtimes." (MAIN: ".$mainphase.") Now starting real update with changes on system...");
		if($dontskipprepare){
			task_buildhost_compile();
			task_prefetch_binpkgs();
		}
		$dontskipprepare=true;
		
		$increase_allowed=true;
		$ret = passthru_call("Update World","emerge --autounmask --autounmask-continue --backtrack=100 --update --deep --newuse --rebuilt-binaries world");
		if($ret!=0){
			echo "Issuing emerge_with_fixes.php...\r\n";
			$ret = passthru_call("Update World","emerge_with_fixes.php --autounmask --autounmask-continue --backtrack=100 --update --deep --newuse --rebuilt-binaries world");
			if($ret==2){
				echo "decrease maxtimes because of retcode 2\r\n";
				$increase_allowed=false;
			}
		}
		if(get_state_changes() && $increase_allowed){
			if($maxtimes<10) $maxtimes++;
		}
	}
}

function get_state_todos(){
	global $_STATE_CACHE_1;
	if(stripos($_STATE_CACHE_1,"Total: 0 packages")===false){
		return(true);
	}
	else{
		return(false);
	}
}
function get_state_changes(){
	// true: something changed, repeat could have luck
	// false: nothing changed, repeat will not have luck
	save_state();
	global $_STATE_CACHE_1,$_STATE_CACHE_2;
	if($_STATE_CACHE_1!=$_STATE_CACHE_2){
		return(true);
	}
	else{
		return(false);
	}
}
function save_state(){
	$state = shell_exec("emerge --autounmask --autounmask-continue --backtrack=100 --update --deep --newuse --rebuilt-binaries -pv world 2>&1");
	$lines = explode("\n",$state);
	$stateline="";
	foreach($lines as $line){
		$line = trim($line);
		if(substr($line,0,7)=="Total: "){
			$stateline=$line;
		}
	}
	if(empty($stateline)){
		echo "We cant get a state from portage :(\r\n";
		echo "Details:\r\n";
		echo $state;
		exit(1);
	}
	echo "Current Portage state: ".$stateline."\r\n";
	global $_STATE_CACHE_1,$_STATE_CACHE_2;
	if(!isset($_STATE_CACHE_1)) $_STATE_CACHE_1="";
	if(!isset($_STATE_CACHE_2)) $_STATE_CACHE_2="";
	$_STATE_CACHE_2=$_STATE_CACHE_1;
	$_STATE_CACHE_1=$stateline;
	
	global $_MOUNTED_RW;
	if(!isset($_MOUNTED_RW)){
		$_MOUNTED_RW=false;
	}

    $mount_req=false;
    if(stripos($state,"sys-kernel/pw-kernel")!==false) $mount_req=true;
    if(stripos($state,"sys-boot/pw-do-grub-install")!==false) $mount_req=true;
    if(stripos($state,"sys-kernel/linux-firmware")!==false) $mount_req=true;
    if(stripos($state,"sys-firmware/intel-microcode")!==false) $mount_req=true;
    if(stripos($state,"sys-boot/pw-rpi-fat-txt")!==false) $mount_req=true;
	
	if(!$_MOUNTED_RW && $mount_req){
        $printed_info_mountboot=false;
        if(file_exists("/boot/efi")){
            if(!$printed_info_mountboot){
                echo "sys-kernel/pw-kernel detected: mounting boot partitions rw...\r\n";
                $printed_info_mountboot=true;
            }
            shell_exec("mount -o remount,rw /boot/efi");
            @mkdir("/run/needrestart");
            @mkdir("/run/needrestart/system");
            @touch("/run/needrestart/system/mount-boot-efi");
        }
        if(file_exists("/boot/firmware")){
            if(!$printed_info_mountboot){
                echo "sys-kernel/pw-kernel detected: mounting boot partitions rw...\r\n";
                $printed_info_mountboot=true;
            }
            shell_exec("mount -o remount,rw /boot/firmware");
            @mkdir("/run/needrestart");
            @mkdir("/run/needrestart/system");
            @touch("/run/needrestart/system/mount-boot-firmware");

        }
		$_MOUNTED_RW=true;
	}
}
function task_update_portage($emerg=false){
	$cur_version=shell_exec("equery list app-portage/portage-updater");
	$ret = passthru_call("Update portage-updater","emerge --oneshot --autounmask --autounmask-continue --backtrack=100 --update app-portage/portage-updater");
	$new_version=shell_exec("equery list app-portage/portage-updater");
	
	if($emerg){
		return;
	}
	
	if($cur_version!=$new_version){
		echo "We updated portage-updater, so repeat important tasks...\r\n";
		task_maintain_deps();
		task_buildhost_compile();
		task_prefetch_binpkgs();
	}
	else{
		echo "No portage-updater update detected!\r\n";
	}
}

function task_prefetch_binpkgs($firstrun=false){
	display_state("Prefetch GPKGs as deltas");
	global $CMD_OPTS;
	echo "\r\n\r\ntask_prefetch_binpkgs() started...\r\n";
	
	if($CMD_OPTS["dedup_gpkgs"]=="no"){
		echo "\r\ndedup_gpkgs=no -> skip\r\n";
		return;
	}
	
	if($CMD_OPTS["cron"]=="yes" && $firstrun){
		echo "\r\ncron=yes and firstrun -> skip - only check that we dont have downloads left\r\n";
		
		echo "now checking if we dont have longer downloads...\r\n";
		$exec = shell_exec("emerge --autounmask --backtrack=100 --autounmask-backtrack=y --update --deep --newuse --rebuilt-binaries -pv world");
		if(strpos($exec,"Size of downloads: 0 KiB")===false){
		        echo "There are downloads left (emerge --autounmask --backtrack=100 --autounmask-backtrack=y --update --deep --newuse --rebuilt-binaries -pv world), dont go further... break!\r\n";
		        echo "Details:\r\n";
		        echo $exec;
		        echo "This was the first run, it is not allowed that we have downloads left from update_ensure_downstream, while we run this task as cron\r\n";
		        echo "\r\n";
		        echo "But additionally trying to update portage-updater for the next run...\r\n";
			task_update_portage(true);
			echo "RUN INCOMPLETE - We distributed, because of update_ensure_downstream works not correctly, but we have updated portage to be sure for the next run!\r\n";
		        exit(1);
		}
		else{
		        echo "No downloads left (emerge --autounmask --backtrack=100 --autounmask-backtrack=y --update --deep --newuse --rebuilt-binaries -pv world)... go further...\r\n";
		}
		return;
	}
	
	
	$ret = passthru_call("Downloading gkpg-deltas from buildhost","update_prefetch_binpkgs_delta.php");
	if($ret==0){
		echo "download from buildhost successfully!\r\n";
	}
	else{
		echo "unknown error\r\n";
		exit(1);
	}
	
	if($CMD_OPTS["dedup_gpkgs"]=="yes"){
		echo "now checking if we dont have longer downloads...\r\n";
		$exec = shell_exec("emerge --autounmask --backtrack=100 --autounmask-backtrack=y --update --deep --newuse --rebuilt-binaries -pv world");
		if(strpos($exec,"Size of downloads: 0 KiB")===false){
		        echo "There are downloads left (emerge --autounmask --backtrack=100 --autounmask-backtrack=y --update --deep --newuse --rebuilt-binaries -pv world), dont go further... break!\r\n";
		        echo "Details:\r\n";
		        echo $exec;
		        exit(1);
		}
		else{
		        echo "No downloads left (emerge --autounmask --backtrack=100 --autounmask-backtrack=y --update --deep --newuse --rebuilt-binaries -pv world)... go further...\r\n";
		}
		return;
	}
	
	
}

function task_buildhost_compile($firstrun=false){
	display_state("Buildhost Compile");
	global $CMD_OPTS;
	echo "\r\n\r\ntask_buildhost_compile() started...\r\n";
	if($CMD_OPTS["usebuildhost"]!="yes"){
		echo "\r\nusebuildhost=no -> skip\r\n";
		return;
	}
	
	if($CMD_OPTS["cron"]=="yes" && $firstrun){
		echo "\r\ncron=yes and firstrun -> skip - only check that we dont have ebuils left\r\n";
		
		echo "now checking if we dont have longer ebuilds...\r\n";
		$exec = shell_exec("emerge --autounmask --backtrack=100 --autounmask-backtrack=y --update --deep --newuse -pv world");
		if(strpos($exec,"[ebuild")!==false){
		        echo "There are ebuilds left (emerge --autounmask --backtrack=100 --autounmask-backtrack=y --update --deep --newuse -pv world), dont go further... break!\r\n";
		        echo "Details:\r\n";
		        echo $exec;
		        echo "This was the first run, it is not allowed that we have ebuilds left from update_ensure_downstream, while we run this task as cron\r\n";
		        echo "\r\n";
		        echo "But additionally trying to update portage-updater for the next run...\r\n";
			task_update_portage(true);
			echo "RUN INCOMPLETE - We distributed, because of update_ensure_downstream works not correctly, but we have updated portage to be sure for the next run!\r\n";
		        exit(1);
		}
		else{
		        echo "No ebuilds left (emerge --autounmask --backtrack=100 --autounmask-backtrack=y --update --deep --newuse -pv world)... go further...\r\n";
		}
		return;
	}

	$ret = passthru_call("Compiling ebuilds on buildhost","update_buildhost_compile.php");
	if($ret==5 || $ret==6){
		echo "buildhost compile successfully!\r\n";
	}
	else{
		echo "unknown error\r\n";
		exit(1);
	}
	
	if($CMD_OPTS["usebuildhost"]=="yes"){
		echo "now checking if we dont have longer ebuilds...\r\n";
		$exec = shell_exec("emerge --autounmask --backtrack=100 --autounmask-backtrack=y --update --deep --newuse -pv world");
		if(strpos($exec,"[ebuild")!==false){
		        echo "There are ebuilds left (emerge --autounmask --backtrack=100 --autounmask-backtrack=y --update --deep --newuse -pv world), dont go further... break!\r\n";
		        echo "Details:\r\n";
		        echo $exec;
                exit(1);
		}
		else{
		        echo "No ebuilds left (emerge --autounmask --backtrack=100 --autounmask-backtrack=y --update --deep --newuse -pv world)... go further...\r\n";
		}
	}
}
function task_maintain_deps($cache_allowed=false){
	display_state("Maintain Deps (package.use/mask/accept_keywords)");
	echo "\r\n\r\ntask_maintain_deps() started...\r\n";
	$rev_current=get_cur_rev();

	if($cache_allowed){
		if(file_exists("/run/portage-updater/cli/maintain_deps")){
			$last_maintain_deps_time=filemtime("/run/portage-updater/cli/maintain_deps");
			$last_maintain_deps_rev=file_get_contents("/run/portage-updater/cli/maintain_deps");
		}
		else{
			$last_maintain_deps_time=-1;
			$last_maintain_deps_rev=-1;
		}
		
		if($rev_current==$last_maintain_deps_rev && ($last_maintain_deps_time+3600)>time() && filemtime("/var/lib/portage/world")<$last_maintain_deps_time){
			echo "task_maintain_deps(): use cached results, no update!\r\n";
			return;
		}
        else{
            echo "task_maintain_deps(): cant use cache, details follows:\r\n";
            echo "- Rev.  ".$rev_current."!=".$last_maintain_deps_rev."\r\n";
            echo "- MTIME ".($last_maintain_deps_time)."+3600<=".time()."\r\n";
        }
	}

    @mkdir("/var/log/portage-updater/update_system", 0700, true);
    $filename="/var/log/portage-updater/update_system/update_maintain_deps_".time().".log";
    display_state("maintain package.use/mask/accept_keywords","update_maintain_deps.php");
    echo "starting, log to: ".$filename." ... ";
    $starttime=time();
    $fp = fopen($filename,"w");
    $exec = shell_exec("update_maintain_deps.php 2>&1");
    fwrite($fp,$exec);
    fclose($fp);
    file_put_contents("/run/portage-updater/cli/maintain_deps",$rev_current);
    $stoptime=time();
    $duration = $stoptime-$starttime;
    echo "done in ".$duration." seconds\r\n";
}
function task_sync(){
	global $CMD_OPTS;
	if($CMD_OPTS["revision"]=="target"){
		$ret = passthru_call("sync Repo","emergesync.php target");
		if($ret!=0 && $CMD_OPTS["breakonwrongrev"]=="yes"){
			echo "DIE, because of breakonwrongrev\r\n";
			exit(1);
		}
		
		$rev_age=-1;
		$rev_target=get_target_rev();
		$rev_current=get_cur_rev($rev_age);
		$allowed_revs=get_allowed_revs();
		if($rev_target!=$rev_current){
			echo "Mismatch of current vs. target rev:\r\n";
			echo "- My target-rev: ".$rev_target."\r\n";
			echo "- All target-revs: ".join(", ",$allowed_revs)."\r\n";
			echo "- Current-rev: ".$rev_current."\r\n";
			echo "- Current-rev-date: ".date("d.m.Y H:i:s T",$rev_age)."\r\n";
		}
		if($rev_target!=$rev_current && $CMD_OPTS["breakonwrongrev"]=="yes"){
			echo "DIE, because of sync error\r\n";
			exit(1);
		}
	}
	elseif($CMD_OPTS["revision"]=="head"){
		$ret = passthru_call("sync Repo","emerge --sync");
		if($ret!=0 && $CMD_OPTS["breakonfailure"]=="yes"){
			echo "DIE, because of breakonfailure\r\n";
			exit(1);
		}
	}
	elseif($CMD_OPTS["revision"]=="current"){
		$revage=-1;
		$rev_target=get_target_rev();
		$rev_current=get_cur_rev($revage);
		$allowed_revs=get_allowed_revs();
		if($rev_target!=$rev_current){
			echo "Mismatch of current vs. target rev:\r\n";
			echo "- My target-rev: ".$rev_target."\r\n";
			echo "- All target-revs: ".join(", ",$allowed_revs)."\r\n";
			echo "- Current-rev: ".$rev_current."\r\n";
			echo "- Current-rev-date: ".date("d.m.Y H:i:s T",$revage)."\r\n";
		}
		$exit=false;
		if(!in_array($rev_current,$allowed_revs) && $CMD_OPTS["breakonwrongrev"]=="yes"){
			echo "rev=current needs --breakonwrongrev=no\r\n";
			echo "break because of --breakonwrongrev (likely through --cron)\r\n";
			echo "if you manually invoked \"emerge --sync\" this is normal, till target-revs get higher - CANCEL!\r\n";
			$exit=true;
		}
		if($CMD_OPTS["breakonwrongrev"]=="yes" && ($revage+(3600*24*90))<time()){
			$days = floor((time()-$revage)/24/3600);
			echo "oldest repository is very old -> ".date("d.m.Y H:i:s T",$revage)." | ".$days." days - CANCEL!\r\n";
			$exit=true;
		}
		if($exit){
			exit(1);
		}
		echo "Leave repositories on current state, go forward...\r\n";
	}
	else{
		echo "Unknown revision ".$CMD_OPTS["revision"]."\r\n";
		exit(1);
	}
	
}
function get_target_rev(){
    $exec=shell_exec("update_getmytargetrev.php");
    $exec=explode("\n",$exec);
    echo "Calling update_getmytargetrev.php...\r\n";
    $infodata=array();
    foreach($exec as $line){
	$line=trim($line);
	echo "\r".$line."\r\n";
	$exp = explode(": ",$line);
	if(count($exp)==2){
	    $infodata[$exp[0]]=$exp[1];
	}
    }
    if($infodata["Target Revision is"]<1){
	echo "Invalid Target Revision :(\r\n";
	exit(1);
    }
    if(strlen($infodata["Target Revision is from"])!=43 && strlen($infodata["Target Revision is from"])!=44){
	echo "Invalid Target Revision date (".strlen($infodata["Target Revision is from"]).") :(\r\n";
	exit(1);
    }
    $rev=$infodata["Target Revision is"];
    return($rev);
}
function get_allowed_revs(){
	passthru_call("sync target-revisions...","update_getmytargetrev.php");
	$lines=file("/var/db/pw-update-instructions/main_target_revs/cascade.txt");
	$revs=array();
	foreach($lines as $line){
		$line=trim($line);
		$cells = explode(":",$line);
		if(count($cells)<2)continue;
		$rev=trim($cells[1]);
		if(!in_array($rev,$revs)){
			$revs[]=$rev;
		}
	}
	return($revs);
}
function get_cur_rev(&$revage=NULL){
	$currev=-1;
	$repos = scandir("/var/db/repos");
	$revage=-1;
	foreach($repos as $repo){
		if($repo=="." || $repo=="..") continue;
		if(is_dir("/var/db/repos/".$repo."/.svn")){
			$exec = shell_exec("svn info /var/db/repos/".$repo);
			$exec=explode("\n",$exec);
			foreach($exec as $line){
			    if(substr($line,0,19)=="Last Changed Date: "){
			    	$dtstr = trim(substr($line,19,19));
			    	$dtdt = strtotime($dtstr);
			    	if($dtdt>$revage || $revage==-1){
			    		$revage=$dtdt;
			    	}
			    }
			    if(substr($line,0,10)=="Revision: "){
				$thisrev=trim(substr($line,10));
				if($currev==$thisrev){
				    // all ok
				}
				elseif($currev==-1){
				    $currev=$thisrev;
				}
				else{
				    echo "Repos have different revs? (".$currev."/".$thisrev.") :( use highest rev...\r\n";
				    if($currev<$thisrev){
					$currev=$thisrev;
				    }
				}
			    }
			}
		}
	}
	return($currev);
}
function passthru_call($stage,$cmd){
	display_state($stage,$cmd);
	$resultcode=-1;

	$ret = passthru($cmd,$resultcode);
	if($ret){
		echo "\r\npassthru: true\r\n";
	}
	else{
		echo "\r\npassthru: false\r\n";
	}
	echo "resultcode: ".$resultcode."\r\n";
	return($resultcode);
}


function display_usage(){
	echo "Usage: update_system.php [options]\r\n";
	echo "Options:\r\n";
	echo "--help|-h:                        Display help\r\n";
	echo "--head/--sync                     Sync to current head revision and dont break\r\n";
	echo "--rev=target/head/current         SVN Repo Revision to take\r\n";
	echo "--breakonfailure[=yes/no]         Cancel on failure\r\n";
	echo "--breakonwrongrev[=yes/no]        Cancel on wrong rev\r\n";
	echo "--usebuildhost[=yes/no]           Dont compile locally\r\n";
	echo "--nobuildhost                     Compily locally\r\n";
	echo "--dedup_gpkgs[=yes/no]            Fetch binaries with dedup (slowly)\r\n";
	echo "--nodedup/noprefetch              Fetch binaries without dedup\r\n";
	echo "--cron[=yes/no]                   current revision, dedup, buildhost, break on everything, wait on lock\r\n";
	exit(0);
}

function display_state($stage,$cmd=""){
	$lines=array();
	$lines[]="Date: ".date("d.m.Y H:i:s T");
	$lines[]="Stage: ".$stage;
	if(!empty($cmd)){
		$lines[]="CMD: ".$cmd;
	}
	
	$maxlen=0;
	foreach($lines as $line){
		$len = strlen($line);
		if($len>$maxlen){
			$maxlen=$len;
		}
	}

	$raute="";
	$space="";
	for($i=0;$i<($maxlen+20);$i++){
		$raute.="#";
		$space.=" ";
	}

	echo substr($raute,0,$maxlen+10)."\r\n";
	foreach($lines as $line){
		echo "#### ";
		echo $line;
		echo substr($space,0,$maxlen-strlen($line));
		echo " ####\r\n";
	}
	echo substr($raute,0,$maxlen+10)."\r\n";

}
function lock($wait){
	global $LOCKFILE;
	@mkdir("/run/portage-updater");
	@mkdir("/run/portage-updater/cli");
	$LOCKFILE=fopen("/run/portage-updater/cli/updater_system.php.lock","w+");
	if($wait=="no"){
		if(!flock($LOCKFILE,LOCK_EX | LOCK_NB)){
			echo "this process can only be run once, close other prcess!\r\n";
			exit(1);
		}
	}
	else{
		if(!flock($LOCKFILE,LOCK_EX | LOCK_NB)){
			echo "update_system.php currently running... waiting for lock... ";
			if(!flock($LOCKFILE,LOCK_EX)){
				echo "error!\r\n";
				exit(1);
			}
			else{
				echo "locked!\r\n";
			}
			
		}
	}
}
function check_reqs(){
	$uid = posix_getpwuid(posix_geteuid());
	if($uid["name"]!="root"){
		echo "update_system.php must be startet as root (currently: ".$uid["name"].")\r\n";
		exit(1);
	}
}
function parse_opts(){
	$opts = cmdopts();
	$options=array();
	
	// Set defaults
	$options["revision"]="target";
	$options["breakonfailure"]="yes";
	$options["breakonwrongrev"]="no";
	$options["usebuildhost"]="yes";
	$options["dedup_gpkgs"]="yes";
	$options["cron"]="no";
	$options["flock_wait"]="no";
	$options["help"]="no";

	foreach($opts as $var=>$val){
		if($var=="head" || $var=="sync"){
			$options["revision"]="head";
			$options["breakonwrongrev"]="no";
		}
		if($var=="help" || $var=="h"){
			$options["help"]="yes";
		}
		if($var=="rev"){
			if($val=="target" || $val=="head" || $val=="current"){
				$options["revision"]=$val;
			}
			else{
				echo "Invalid value ".$val." for ".$var."\r\n";
				exit(1);
			}
		}
		if($var=="breakonfailure"){
			if($val=="" || $val=="y" || $val=="yes"){
				$options["breakonfailure"]="yes";
			}
			elseif($val=="n" || $val=="no"){
				$options["breakonfailure"]="no";
			}
			else{
				echo "Invalid value ".$val." for ".$var."\r\n";
				exit(1);
			}
		}
		if($var=="breakonwrongrev"){
			if($val=="" || $val=="y" || $val=="yes"){
				$options["breakonwrongrev"]="yes";
			}
			elseif($val=="n" || $val=="no"){
				$options["breakonwrongrev"]="no";
			}
			else{
				echo "Invalid value ".$val." for ".$var."\r\n";
				exit(1);
			}
		}
		if($var=="usebuildhost"){
			if($val=="" || $val=="y" || $val=="yes"){
				$options["usebuildhost"]="yes";
			}
			elseif($val=="n" || $val=="no"){
				$options["usebuildhost"]="no";
				$options["dedup_gpkgs"]="no";
			}
			else{
				echo "Invalid value ".$val." for ".$var."\r\n";
				exit(1);
			}
		}
		if($var=="nobuildhost"){
			$options["usebuildhost"]="no";
			$options["dedup_gpkgs"]="no";
		}
		if($var=="dedup_gpkgs"){
			if($val=="" || $val=="y" || $val=="yes"){
				$options["dedup_gpkgs"]="yes";
			}
			elseif($val=="n" || $val=="no"){
				$options["dedup_gpkgs"]="no";
			}
			else{
				echo "Invalid value ".$val." for ".$var."\r\n";
				exit(1);
			}
		}
		if($var=="nodedup"){
			$options["dedup_gpkgs"]="no";
		}
		if($var=="noprefetch"){
			$options["dedup_gpkgs"]="no";
		}
		if($var=="cron"){
			if($val=="" || $val=="y" || $val=="yes"){
				$options["cron"]="yes";
				
				$options["revision"]="current";
				$options["breakonfailure"]="yes";
				$options["breakonwrongrev"]="yes";
				$options["usebuildhost"]="yes";
				$options["dedup_gpkgs"]="yes";
				$options["flock_wait"]="yes";
			}
			elseif($val=="n" || $val=="no"){
				$options["cron"]="no";
			}
			else{
				echo "Invalid value ".$val." for ".$var."\r\n";
				exit(1);
			}
		}
	}
	echo "update_system.php from app-portage/portage-updater started on ".date("d.m.Y H:i:s T")." with flags:\r\n";
	foreach($options as $var=>$val){
		echo "- ".$var." => ".$val."\r\n";
	}
	return($options);
}
function cmdopts(){
	$rest="";
	$opts = getopt("h",
		array(	"rev::",
			"breakonfailure::",
			"breakonwrongrev::",
			"usebuildhost::",
			"nobuildhost",
			"dedup_gpkgs::",
			"nodedup",
			"noprefetch",
			"cron::",
			"sync",
			"head",
			"help"
			));

	return($opts);	
}
