mirror of
https://git.proxmox.com/git/pve-manager
synced 2025-05-01 06:08:11 +00:00
new html formatter PVE::API2::Formatter::HTML
This one provides a login page and uses bootstrap for html.
This commit is contained in:
parent
4833787409
commit
7e73c93e55
@ -6,7 +6,6 @@ use warnings;
|
|||||||
use PVE::pvecfg;
|
use PVE::pvecfg;
|
||||||
use PVE::RESTHandler;
|
use PVE::RESTHandler;
|
||||||
use PVE::JSONSchema;
|
use PVE::JSONSchema;
|
||||||
use PVE::API2::Formatter::Standard;
|
|
||||||
|
|
||||||
use base qw(PVE::RESTHandler);
|
use base qw(PVE::RESTHandler);
|
||||||
|
|
||||||
|
@ -94,16 +94,22 @@ sub body {
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>Proxmox VE Portal at '$hostname'</title>
|
<title>Proxmox VE API</title>
|
||||||
|
|
||||||
<!-- Bootstrap -->
|
<!-- Bootstrap -->
|
||||||
<link href="/css/bootstrap.min.css" rel="stylesheet">
|
<link href="/pve2/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$jssrc
|
$jssrc
|
||||||
$jssetup
|
$jssetup
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
padding-top: 70px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
||||||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
||||||
<!--[if lt IE 9]>
|
<!--[if lt IE 9]>
|
||||||
@ -114,7 +120,7 @@ sub body {
|
|||||||
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
|
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
|
||||||
<!-- Include all compiled plugins (below), or include individual files as needed -->
|
<!-- Include all compiled plugins (below), or include individual files as needed -->
|
||||||
<script src="/js/bootstrap.min.js"></script>
|
<script src="/pve2/js/bootstrap.min.js"></script>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
277
PVE/API2/Formatter/HTML.pm
Normal file
277
PVE/API2/Formatter/HTML.pm
Normal file
@ -0,0 +1,277 @@
|
|||||||
|
package PVE::API2::Formatter::HTML;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
use PVE::REST;
|
||||||
|
use PVE::HTTPServer;
|
||||||
|
use HTTP::Status;
|
||||||
|
use JSON;
|
||||||
|
use HTML::Entities;
|
||||||
|
use PVE::JSONSchema;
|
||||||
|
use PVE::API2::Formatter::Bootstrap;
|
||||||
|
use PVE::API2::Formatter::Standard;
|
||||||
|
|
||||||
|
my $portal_format = 'html';
|
||||||
|
my $portal_ct = 'text/html;charset=UTF-8';
|
||||||
|
|
||||||
|
my $baseurl = "/api2/$portal_format";
|
||||||
|
my $login_url = "$baseurl/access/ticket";
|
||||||
|
|
||||||
|
sub render_page {
|
||||||
|
my ($doc, $html) = @_;
|
||||||
|
|
||||||
|
my $items = [];
|
||||||
|
|
||||||
|
push @$items, {
|
||||||
|
tag => 'li',
|
||||||
|
cn => {
|
||||||
|
tag => 'a',
|
||||||
|
href => $login_url,
|
||||||
|
onClick => "PVE.delete_auth_cookie();",
|
||||||
|
text => "Logout",
|
||||||
|
}};
|
||||||
|
|
||||||
|
|
||||||
|
my $title = "Proxmox VE";
|
||||||
|
|
||||||
|
my $nav = $doc->el(
|
||||||
|
class => "navbar navbar-inverse navbar-fixed-top",
|
||||||
|
role => "navigation", cn => {
|
||||||
|
class => "container", cn => [
|
||||||
|
{
|
||||||
|
class => "navbar-header", cn => [
|
||||||
|
{
|
||||||
|
tag => 'button',
|
||||||
|
type => 'button',
|
||||||
|
class => "navbar-toggle",
|
||||||
|
'data-toggle' => "collapse",
|
||||||
|
'data-target' => ".navbar-collapse",
|
||||||
|
cn => [
|
||||||
|
{ tag => 'span', class => 'sr-only', text => "Toggle navigation" },
|
||||||
|
{ tag => 'span', class => 'icon-bar' },
|
||||||
|
{ tag => 'span', class => 'icon-bar' },
|
||||||
|
{ tag => 'span', class => 'icon-bar' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag => 'a',
|
||||||
|
class => "navbar-brand",
|
||||||
|
href => $baseurl,
|
||||||
|
text => $title,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
class => "collapse navbar-collapse",
|
||||||
|
cn => {
|
||||||
|
tag => 'ul',
|
||||||
|
class => "nav navbar-nav",
|
||||||
|
cn => $items,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
$items = [];
|
||||||
|
my @pcomp = split('/', $doc->{url});
|
||||||
|
shift @pcomp; # empty
|
||||||
|
shift @pcomp; # api2
|
||||||
|
shift @pcomp; # $format
|
||||||
|
|
||||||
|
my $href = $baseurl;
|
||||||
|
push @$items, { tag => 'li', cn => {
|
||||||
|
tag => 'a',
|
||||||
|
href => $href,
|
||||||
|
text => 'Home'}};
|
||||||
|
|
||||||
|
foreach my $comp (@pcomp) {
|
||||||
|
$href .= "/$comp";
|
||||||
|
push @$items, { tag => 'li', cn => {
|
||||||
|
tag => 'a',
|
||||||
|
href => $href,
|
||||||
|
text => $comp}};
|
||||||
|
}
|
||||||
|
|
||||||
|
my $breadcrumbs = $doc->el(tag => 'ol', class => 'breadcrumb container', cn => $items);
|
||||||
|
|
||||||
|
return $doc->body($nav . $breadcrumbs . $html);
|
||||||
|
}
|
||||||
|
|
||||||
|
my $login_form = sub {
|
||||||
|
my ($doc, $param, $errmsg) = @_;
|
||||||
|
|
||||||
|
$param = {} if !$param;
|
||||||
|
|
||||||
|
my $username = $param->{username} || '';
|
||||||
|
my $password = $param->{password} || '';
|
||||||
|
|
||||||
|
my $items = [
|
||||||
|
{
|
||||||
|
tag => 'label',
|
||||||
|
text => "Please sign in",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag => 'input',
|
||||||
|
type => 'text',
|
||||||
|
class => 'form-control',
|
||||||
|
name => 'username',
|
||||||
|
value => $username,
|
||||||
|
placeholder => "Enter user name",
|
||||||
|
required => 1,
|
||||||
|
autofocus => 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag => 'input',
|
||||||
|
type => 'password',
|
||||||
|
class => 'form-control',
|
||||||
|
name => 'password',
|
||||||
|
value => $password,
|
||||||
|
placeholder => 'Password',
|
||||||
|
required => 1,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
my $html = '';
|
||||||
|
|
||||||
|
$html .= $doc->alert(text => $errmsg) if ($errmsg);
|
||||||
|
|
||||||
|
$html .= $doc->el(
|
||||||
|
class => 'container',
|
||||||
|
cn => {
|
||||||
|
tag => 'form',
|
||||||
|
role => 'form',
|
||||||
|
method => 'POST',
|
||||||
|
action => $login_url,
|
||||||
|
cn => [
|
||||||
|
{
|
||||||
|
class => 'form-group',
|
||||||
|
cn => $items,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag => 'button',
|
||||||
|
type => 'submit',
|
||||||
|
class => 'btn btn-lg btn-primary btn-block',
|
||||||
|
text => "Sign in",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
};
|
||||||
|
|
||||||
|
PVE::HTTPServer::register_login_formatter($portal_format, sub {
|
||||||
|
my ($path, $auth) = @_;
|
||||||
|
|
||||||
|
my $headers = HTTP::Headers->new(Location => $login_url);
|
||||||
|
return HTTP::Response->new(301, "Moved", $headers);
|
||||||
|
});
|
||||||
|
|
||||||
|
PVE::HTTPServer::register_formatter($portal_format, sub {
|
||||||
|
my ($res, $data, $param, $path, $auth) = @_;
|
||||||
|
|
||||||
|
# fixme: clumsy!
|
||||||
|
PVE::API2::Formatter::Standard::prepare_response_data($portal_format, $res);
|
||||||
|
$data = $res->{data};
|
||||||
|
|
||||||
|
my $html = '';
|
||||||
|
my $doc = PVE::API2::Formatter::Bootstrap->new($res, $path);
|
||||||
|
|
||||||
|
if (!HTTP::Status::is_success($res->{status})) {
|
||||||
|
$html .= $doc->alert(text => "Error $res->{status}: $res->{message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
my $info = $res->{info};
|
||||||
|
my $lnk = PVE::JSONSchema::method_get_child_link($info);
|
||||||
|
|
||||||
|
if ($lnk && $data && $data->{data} && HTTP::Status::is_success($res->{status})) {
|
||||||
|
|
||||||
|
my $href = $lnk->{href};
|
||||||
|
if ($href =~ m/^\{(\S+)\}$/) {
|
||||||
|
|
||||||
|
my $items = [];
|
||||||
|
|
||||||
|
my $prop = $1;
|
||||||
|
$path =~ s/\/+$//; # remove trailing slash
|
||||||
|
|
||||||
|
foreach my $elem (sort {$a->{$prop} cmp $b->{$prop}} @{$data->{data}}) {
|
||||||
|
next if !ref($elem);
|
||||||
|
|
||||||
|
if (defined(my $value = $elem->{$prop})) {
|
||||||
|
if ($value ne '') {
|
||||||
|
my $text = $value;
|
||||||
|
if (scalar(keys %$elem) > 1) {
|
||||||
|
my $tv = to_json($elem, {allow_nonref => 1, canonical => 1});
|
||||||
|
$text = "$value $tv";
|
||||||
|
}
|
||||||
|
push @$items, {
|
||||||
|
tag => 'a',
|
||||||
|
class => 'list-group-item',
|
||||||
|
href => "$path/$value",
|
||||||
|
text => $text,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .= $doc->el(class => 'list-group', cn => $items);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
my $json = to_json($data, {allow_nonref => 1, pretty => 1});
|
||||||
|
$html .= $doc->el(tag => 'pre', text => $json);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
my $json = to_json($data, {allow_nonref => 1, pretty => 1});
|
||||||
|
$html .= $doc->el(tag => 'pre', text => $json);
|
||||||
|
}
|
||||||
|
|
||||||
|
$html = $doc->el(class => 'container', html => $html);
|
||||||
|
|
||||||
|
my $raw = render_page($doc, $html);
|
||||||
|
return ($raw, $portal_ct);
|
||||||
|
});
|
||||||
|
|
||||||
|
PVE::API2->register_page_formatter(
|
||||||
|
'format' => $portal_format,
|
||||||
|
method => 'GET',
|
||||||
|
path => "/access/ticket",
|
||||||
|
code => sub {
|
||||||
|
my ($res, $data, $param, $path, $auth) = @_;
|
||||||
|
|
||||||
|
my $doc = PVE::API2::Formatter::Bootstrap->new($res, $path);
|
||||||
|
|
||||||
|
my $html = &$login_form($doc);
|
||||||
|
|
||||||
|
my $raw = render_page($doc, $html);
|
||||||
|
return ($raw, $portal_ct);
|
||||||
|
});
|
||||||
|
|
||||||
|
PVE::API2->register_page_formatter(
|
||||||
|
'format' => $portal_format,
|
||||||
|
method => 'POST',
|
||||||
|
path => "/access/ticket",
|
||||||
|
code => sub {
|
||||||
|
my ($res, $data, $param, $path, $auth) = @_;
|
||||||
|
|
||||||
|
if (HTTP::Status::is_success($res->{status})) {
|
||||||
|
my $cookie = PVE::REST::create_auth_cookie($data->{ticket});
|
||||||
|
my $headers = HTTP::Headers->new(Location => $baseurl,
|
||||||
|
'Set-Cookie' => $cookie);
|
||||||
|
return HTTP::Response->new(301, "Moved", $headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
# Note: HTTP server redirects to 'GET /access/ticket', so below
|
||||||
|
# output is not really visible.
|
||||||
|
|
||||||
|
my $doc = PVE::API2::Formatter::Bootstrap->new($res, $path);
|
||||||
|
|
||||||
|
my $html = &$login_form($doc);
|
||||||
|
|
||||||
|
my $raw = render_page($doc, $html);
|
||||||
|
return ($raw, $portal_ct);
|
||||||
|
});
|
||||||
|
|
||||||
|
1;
|
@ -1,6 +1,8 @@
|
|||||||
include ../../../defines.mk
|
include ../../../defines.mk
|
||||||
|
|
||||||
PERLSOURCE = \
|
PERLSOURCE = \
|
||||||
|
Bootstrap.pm \
|
||||||
|
HTML.pm \
|
||||||
Standard.pm
|
Standard.pm
|
||||||
|
|
||||||
all:
|
all:
|
||||||
|
@ -11,7 +11,7 @@ use PVE::JSONSchema;
|
|||||||
|
|
||||||
# register result formatters
|
# register result formatters
|
||||||
|
|
||||||
my $prepare_response_data = sub {
|
sub prepare_response_data {
|
||||||
my ($format, $res) = @_;
|
my ($format, $res) = @_;
|
||||||
|
|
||||||
my $success = 1;
|
my $success = 1;
|
||||||
@ -44,7 +44,7 @@ my $prepare_response_data = sub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$res->{data} = $new;
|
$res->{data} = $new;
|
||||||
};
|
}
|
||||||
|
|
||||||
PVE::HTTPServer::register_formatter('json', sub {
|
PVE::HTTPServer::register_formatter('json', sub {
|
||||||
my ($res, $data, $param, $path, $auth) = @_;
|
my ($res, $data, $param, $path, $auth) = @_;
|
||||||
@ -53,7 +53,7 @@ PVE::HTTPServer::register_formatter('json', sub {
|
|||||||
|
|
||||||
my $ct = 'application/json;charset=UTF-8';
|
my $ct = 'application/json;charset=UTF-8';
|
||||||
|
|
||||||
&$prepare_response_data('json', $res);
|
prepare_response_data('json', $res);
|
||||||
|
|
||||||
my $raw = to_json($res->{data}, {utf8 => 1, allow_nonref => 1});
|
my $raw = to_json($res->{data}, {utf8 => 1, allow_nonref => 1});
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ PVE::HTTPServer::register_formatter('extjs', sub {
|
|||||||
|
|
||||||
my $ct = 'application/json;charset=UTF-8';
|
my $ct = 'application/json;charset=UTF-8';
|
||||||
|
|
||||||
&$prepare_response_data('extjs', $res);
|
prepare_response_data('extjs', $res);
|
||||||
|
|
||||||
my $raw = to_json($res->{data}, {utf8 => 1, allow_nonref => 1});
|
my $raw = to_json($res->{data}, {utf8 => 1, allow_nonref => 1});
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ PVE::HTTPServer::register_formatter('htmljs', sub {
|
|||||||
|
|
||||||
my $ct = 'text/html;charset=UTF-8';
|
my $ct = 'text/html;charset=UTF-8';
|
||||||
|
|
||||||
&$prepare_response_data('htmljs', $res);
|
prepare_response_data('htmljs', $res);
|
||||||
|
|
||||||
my $raw = encode_entities(to_json($res->{data}, {allow_nonref => 1}));
|
my $raw = encode_entities(to_json($res->{data}, {allow_nonref => 1}));
|
||||||
|
|
||||||
@ -99,7 +99,7 @@ PVE::HTTPServer::register_formatter('spiceconfig', sub {
|
|||||||
|
|
||||||
my $ct = 'application/x-virt-viewer;charset=UTF-8';
|
my $ct = 'application/x-virt-viewer;charset=UTF-8';
|
||||||
|
|
||||||
&$prepare_response_data('spiceconfig', $res);
|
prepare_response_data('spiceconfig', $res);
|
||||||
|
|
||||||
$data = $res->{data};
|
$data = $res->{data};
|
||||||
|
|
||||||
@ -122,7 +122,7 @@ PVE::HTTPServer::register_formatter('png', sub {
|
|||||||
|
|
||||||
my $ct = 'image/png';
|
my $ct = 'image/png';
|
||||||
|
|
||||||
&$prepare_response_data('png', $res);
|
prepare_response_data('png', $res);
|
||||||
|
|
||||||
$data = $res->{data};
|
$data = $res->{data};
|
||||||
|
|
||||||
@ -140,53 +140,3 @@ PVE::HTTPServer::register_formatter('png', sub {
|
|||||||
return ($raw, $ct, $nocomp);
|
return ($raw, $ct, $nocomp);
|
||||||
});
|
});
|
||||||
|
|
||||||
PVE::HTTPServer::register_formatter('html', sub {
|
|
||||||
my ($res, $data, $param, $path, $auth) = @_;
|
|
||||||
|
|
||||||
my $nocomp = 0;
|
|
||||||
|
|
||||||
my $ct = 'text/html;charset=UTF-8';
|
|
||||||
|
|
||||||
&$prepare_response_data('html', $res);
|
|
||||||
|
|
||||||
$data = $res->{data};
|
|
||||||
|
|
||||||
my $info = $res->{info};
|
|
||||||
|
|
||||||
my $raw = "<html><body>";
|
|
||||||
if (!HTTP::Status::is_success($res->{status})) {
|
|
||||||
my $msg = $res->{message} || '';
|
|
||||||
$raw .= "<h1>ERROR $res->{status} $msg</h1>";
|
|
||||||
}
|
|
||||||
my $lnk = PVE::JSONSchema::method_get_child_link($info);
|
|
||||||
|
|
||||||
if ($lnk && $data && $data->{data} && HTTP::Status::is_success($res->{status})) {
|
|
||||||
|
|
||||||
my $href = $lnk->{href};
|
|
||||||
if ($href =~ m/^\{(\S+)\}$/) {
|
|
||||||
my $prop = $1;
|
|
||||||
$path =~ s/\/+$//; # remove trailing slash
|
|
||||||
foreach my $elem (sort {$a->{$prop} cmp $b->{$prop}} @{$data->{data}}) {
|
|
||||||
next if !ref($elem);
|
|
||||||
|
|
||||||
if (defined(my $value = $elem->{$prop})) {
|
|
||||||
if ($value ne '') {
|
|
||||||
if (scalar(keys %$elem) > 1) {
|
|
||||||
my $tv = to_json($elem, {allow_nonref => 1, canonical => 1});
|
|
||||||
$raw .= "<a href='$path/$value'>$value</a> <pre>$tv</pre><br>";
|
|
||||||
} else {
|
|
||||||
$raw .= "<a href='$path/$value'>$value</a><br>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$raw .= "<pre>";
|
|
||||||
$raw .= encode_entities(to_json($data, {allow_nonref => 1, pretty => 1}));
|
|
||||||
$raw .= "</pre>";
|
|
||||||
}
|
|
||||||
$raw .= "</body></html>";
|
|
||||||
|
|
||||||
return ($raw, $ct, $nocomp);
|
|
||||||
});
|
|
||||||
|
@ -434,6 +434,10 @@ sub proxy_request {
|
|||||||
delete $hdr->{URL};
|
delete $hdr->{URL};
|
||||||
delete $hdr->{HTTPVersion};
|
delete $hdr->{HTTPVersion};
|
||||||
my $header = HTTP::Headers->new(%$hdr);
|
my $header = HTTP::Headers->new(%$hdr);
|
||||||
|
if (my $location = $header->header('Location')) {
|
||||||
|
$location =~ s|^http://localhost:85||;
|
||||||
|
$header->header(Location => $location);
|
||||||
|
}
|
||||||
my $resp = HTTP::Response->new($code, $msg, $header, $body);
|
my $resp = HTTP::Response->new($code, $msg, $header, $body);
|
||||||
# Note: disable compression, because body is already compressed
|
# Note: disable compression, because body is already compressed
|
||||||
$self->response($reqstate, $resp, undef, 1);
|
$self->response($reqstate, $resp, undef, 1);
|
||||||
@ -560,7 +564,7 @@ sub handle_api2_request {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
my ($raw, $ct, $nocomp) = &$formatter($res, $res->{data}, $path, $auth);
|
my ($raw, $ct, $nocomp) = &$formatter($res, $res->{data}, $params, $path, $auth);
|
||||||
|
|
||||||
my $resp;
|
my $resp;
|
||||||
if (ref($raw) && (ref($raw) eq 'HTTP::Response')) {
|
if (ref($raw) && (ref($raw) eq 'HTTP::Response')) {
|
||||||
|
@ -12,6 +12,8 @@ use Socket;
|
|||||||
use PVE::SafeSyslog;
|
use PVE::SafeSyslog;
|
||||||
use PVE::APIDaemon;
|
use PVE::APIDaemon;
|
||||||
use PVE::API2;
|
use PVE::API2;
|
||||||
|
use PVE::API2::Formatter::Standard;
|
||||||
|
use PVE::API2::Formatter::HTML;
|
||||||
|
|
||||||
my $pidfile = "/var/run/pvedaemon.pid";
|
my $pidfile = "/var/run/pvedaemon.pid";
|
||||||
my $lockfile = "/var/lock/pvedaemon.lck";
|
my $lockfile = "/var/lock/pvedaemon.lck";
|
||||||
|
@ -20,6 +20,8 @@ use URI::QueryParam;
|
|||||||
use File::Find;
|
use File::Find;
|
||||||
use Data::Dumper;
|
use Data::Dumper;
|
||||||
use PVE::API2;
|
use PVE::API2;
|
||||||
|
use PVE::API2::Formatter::Standard;
|
||||||
|
use PVE::API2::Formatter::HTML;
|
||||||
|
|
||||||
my $pidfile = "/var/run/pveproxy/pveproxy.pid";
|
my $pidfile = "/var/run/pveproxy/pveproxy.pid";
|
||||||
my $lockfile = "/var/lock/pveproxy.lck";
|
my $lockfile = "/var/lock/pveproxy.lck";
|
||||||
|
Loading…
Reference in New Issue
Block a user