$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp'; $var = parse_ini_file('state/var.ini'); extract(parse_ini_file('state/network.ini',true)); ignore_user_abort(true); require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php"; $DockerClient = new DockerClient(); $DockerUpdate = new DockerUpdate(); $DockerTemplates = new DockerTemplates(); # ███████╗██╗ ██╗███╗ ██╗ ██████╗████████╗██╗ ██████╗ ███╗ ██╗███████╗ # ██╔════╝██║ ██║████╗ ██║██╔════╝╚══██╔══╝██║██╔═══██╗████╗ ██║██╔════╝ # █████╗ ██║ ██║██╔██╗ ██║██║ ██║ ██║██║ ██║██╔██╗ ██║███████╗ # ██╔══╝ ██║ ██║██║╚██╗██║██║ ██║ ██║██║ ██║██║╚██╗██║╚════██║ # ██║ ╚██████╔╝██║ ╚████║╚██████╗ ██║ ██║╚██████╔╝██║ ╚████║███████║ # ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚══════╝ $echo = function($m){echo "
".print_r($m, true)."";}; $custom=[]; docker("network ls --filter driver='macvlan' --format='{{.Name}}'", $custom); $subnet = ['bridge'=>'', 'host'=>'', 'none'=>'']; foreach ($custom as $network) $subnet[$network] = substr(docker("network inspect --format='{{range .IPAM.Config}}{{.Subnet}}, {{end}}' $network"),0,-1); function stopContainer($name) { global $DockerClient; $waitID = mt_rand(); echo ""; echo "\n"; @flush(); $retval = $DockerClient->stopContainer($name); $out = ($retval === true) ? "Successfully stopped container '$name'" : "Error: ".$retval; echo "\n"; @flush(); } function removeContainer($name) { global $DockerClient; $waitID = mt_rand(); echo ""; echo "\n"; @flush(); $retval = $DockerClient->removeContainer($name); $out = ($retval === true) ? "Successfully removed container '$name'" : "Error: ".$retval; echo "\n"; @flush(); } function removeImage($image) { global $DockerClient; $waitID = mt_rand(); echo ""; echo "\n"; @flush(); $retval = $DockerClient->removeImage($image); $out = ($retval === true) ? "Successfully removed image '$image'" : "Error: ".$retval; echo "\n"; @flush(); } function pullImage($name, $image) { global $DockerClient, $DockerTemplates, $DockerUpdate; $waitID = mt_rand(); if (!preg_match("/:\S+$/", $image)) $image .= ":latest"; echo ""; echo "\n"; @flush(); $alltotals = []; $laststatus = []; $strError = ''; $DockerClient->pullImage($image, function ($line) use (&$alltotals, &$laststatus, &$waitID, &$strError, $image, $DockerClient, $DockerUpdate) { $cnt = json_decode($line, true); $id = (isset($cnt['id'])) ? trim($cnt['id']) : ''; $status = (isset($cnt['status'])) ? trim($cnt['status']) : ''; if (isset($cnt['error'])) { $strError = $cnt['error']; } if ($waitID !== false) { echo "\n"; @flush(); $waitID = false; } if (empty($status)) return; if (!empty($id)) { if (!empty($cnt['progressDetail']) && !empty($cnt['progressDetail']['total'])) { $alltotals[$id] = $cnt['progressDetail']['total']; } if (empty($laststatus[$id])) { $laststatus[$id] = ''; } switch ($status) { case 'Waiting': // Omit break; case 'Downloading': if ($laststatus[$id] != $status) { echo "\n"; } $total = $cnt['progressDetail']['total']; $current = $cnt['progressDetail']['current']; if ($total > 0) { $percentage = round(($current / $total) * 100); echo "\n"; } else { // Docker must not know the total download size (http-chunked or something?) // just show the current download progress without the percentage $alltotals[$id] = $current; echo "\n"; } break; default: if ($laststatus[$id] == "Downloading") { echo "\n"; } if ($laststatus[$id] != $status) { echo "\n"; } break; } $laststatus[$id] = $status; } else { if (strpos($status, 'Status: ') === 0) { echo "\n"; } if (strpos($status, 'Digest: ') === 0) { $DockerUpdate->setUpdateStatus($image, substr($status, 8)); } } @flush(); }); echo "\n"; @flush(); if (!empty($strError)) { echo "\n"; @flush(); return false; } return true; } function xml_encode($string) { return htmlspecialchars($string, ENT_XML1, 'UTF-8'); } function xml_decode($string) { return strval(html_entity_decode($string, ENT_XML1, 'UTF-8')); } function postToXML($post, $setOwnership=false) { $dom = new domDocument; $dom->appendChild($dom->createElement("Container")); $xml = simplexml_import_dom($dom); $xml['version'] = 2; $xml->Name = xml_encode(preg_replace('/\s+/', '', $post['contName'])); $xml->Repository = xml_encode(trim($post['contRepository'])); $xml->Registry = xml_encode(trim($post['contRegistry'])); $xml->Network = xml_encode($post['contNetwork']); $xml->MyIP = xml_encode($post['contMyIP']); $xml->Privileged = strtolower($post['contPrivileged'])=='on' ? 'true' : 'false'; $xml->Support = xml_encode($post['contSupport']); $xml->Project = xml_encode($post['contProject']); $xml->Overview = xml_encode($post['contOverview']); $xml->Category = xml_encode($post['contCategory']); $xml->WebUI = xml_encode(trim($post['contWebUI'])); $xml->TemplateURL = xml_encode($post['contTemplateURL']); $xml->Icon = xml_encode(trim($post['contIcon'])); $xml->ExtraParams = xml_encode($post['contExtraParams']); $xml->PostArgs = xml_encode($post['contPostArgs']); $xml->DateInstalled = xml_encode(time()); $xml->DonateText = xml_encode($post['contDonateText']); $xml->DonateLink = xml_encode($post['contDonateLink']); $xml->DonateImg = xml_encode($post['contDonateImg']); $xml->MinVer = xml_encode($post['contMinVer']); # V1 compatibility $xml->Description = xml_encode($post['contOverview']); $xml->Networking->Mode = xml_encode($post['contNetwork']); $xml->Networking->addChild("Publish"); $xml->addChild("Data"); $xml->addChild("Environment"); for ($i = 0; $i < count($post['confName']); $i++) { $Type = $post['confType'][$i]; $config = $xml->addChild('Config', xml_encode($post['confValue'][$i])); $config['Name'] = xml_encode($post['confName'][$i]); $config['Target'] = xml_encode($post['confTarget'][$i]); $config['Default'] = xml_encode($post['confDefault'][$i]); $config['Mode'] = xml_encode($post['confMode'][$i]); $config['Description'] = xml_encode($post['confDescription'][$i]); $config['Type'] = xml_encode($post['confType'][$i]); $config['Display'] = xml_encode($post['confDisplay'][$i]); $config['Required'] = xml_encode($post['confRequired'][$i]); $config['Mask'] = xml_encode($post['confMask'][$i]); # V1 compatibility if ($Type == "Port") { $port = $xml->Networking->Publish->addChild("Port"); $port->HostPort = $post['confValue'][$i]; $port->ContainerPort = $post['confTarget'][$i]; $port->Protocol = $post['confMode'][$i]; } elseif ($Type == "Path") { $path = $xml->Data->addChild("Volume"); $path->HostDir = $post['confValue'][$i]; $path->ContainerDir = $post['confTarget'][$i]; $path->Mode = $post['confMode'][$i]; } elseif ($Type == "Variable") { $variable = $xml->Environment->addChild("Variable"); $variable->Value = $post['confValue'][$i]; $variable->Name = $post['confTarget'][$i]; $variable->Mode = $post['confMode'][$i]; } } $dom = new DOMDocument('1.0'); $dom->preserveWhiteSpace = false; $dom->formatOutput = true; $dom->loadXML($xml->asXML()); return $dom->saveXML(); } function xmlToVar($xml) { global $subnet; $xml = is_file($xml) ? simplexml_load_file($xml) : simplexml_load_string($xml); $out = []; $out['Name'] = preg_replace('/\s+/', '', xml_decode($xml->Name)); $out['Repository'] = xml_decode($xml->Repository); $out['Registry'] = xml_decode($xml->Registry); $out['Network'] = xml_decode($xml->Network); $out['MyIP'] = xml_decode($xml->MyIP ?? ''); $out['Privileged'] = xml_decode($xml->Privileged); $out['Support'] = xml_decode($xml->Support); $out['Project'] = xml_decode($xml->Project); $out['Overview'] = stripslashes(xml_decode($xml->Overview)); $out['Category'] = xml_decode($xml->Category); $out['WebUI'] = xml_decode($xml->WebUI); $out['TemplateURL'] = xml_decode($xml->TemplateURL); $out['Icon'] = xml_decode($xml->Icon); $out['ExtraParams'] = xml_decode($xml->ExtraParams); $out['PostArgs'] = xml_decode($xml->PostArgs); $out['DonateText'] = xml_decode($xml->DonateText); $out['DonateLink'] = xml_decode($xml->DonateLink); $out['DonateImg'] = xml_decode($xml->DonateImg ?? $xml->DonateImage); # Various authors use different tags. DonateImg is the official spec $out['MinVer'] = xml_decode($xml->MinVer); $out['Config'] = []; if (isset($xml->Config)) { foreach ($xml->Config as $config) { $c = []; $c['Value'] = strlen(xml_decode($config)) ? xml_decode($config) : xml_decode($config['Default']); foreach ($config->attributes() as $key => $value) { $value = xml_decode($value); $val = strtolower($value); if ($key == 'Mode') { switch (xml_decode($config['Type'])) { case 'Path': $value = ($val=='rw'||$val=='rw,slave'||$val=='rw,shared'||$val=='ro'||$val=='ro,slave'||$val=='ro,shared') ? $value : "rw"; break; case 'Port': $value = ($val=='tcp'||$val=='udp') ? $value : "tcp"; break; } } $c[$key] = $value; } $out['Config'][] = $c; } } # some xml templates advertise as V2 but omit the new
".htmlspecialchars($postXML).""; echo "
".htmlspecialchars($cmd).""; echo "