]> git.proxmox.com Git - pve-common.git/blobdiff - src/PVE/SectionConfig.pm
fix #1819: fork_worker: ensure sync'ed workers control terminal
[pve-common.git] / src / PVE / SectionConfig.pm
index d4d2ed57e43437db4a73b231862f25452d07d2fc..cc03aeaed638db1a4f6c4a736ade97ea90633649 100644 (file)
@@ -26,6 +26,9 @@ sub register {
     my $type = $class->type();
     my $pdata = $class->private();
 
+    die "duplicate plugin registration (type = $type)"
+       if defined($pdata->{plugins}->{$type});
+
     my $plugindata = $class->plugindata();
     $pdata->{plugindata}->{$type} = $plugindata;
     $pdata->{plugins}->{$type} = $class;
@@ -48,20 +51,62 @@ sub plugindata {
 }   
 
 sub createSchema {
-    my ($class) = @_;
+    my ($class, $skip_type) = @_;
 
     my $pdata = $class->private();
     my $propertyList = $pdata->{propertyList};
+    my $plugins = $pdata->{plugins};
+
+    my $props = {};
+
+    my $copy_property = sub {
+       my ($src) = @_;
+
+       my $res = {};
+       foreach my $k (keys %$src) {
+           $res->{$k} = $src->{$k};
+       }
+
+       return $res;
+    };
+
+    foreach my $p (keys %$propertyList) {
+       next if $skip_type && $p eq 'type';
+
+       if (!$propertyList->{$p}->{optional}) {
+           $props->{$p} = $propertyList->{$p};
+           next;
+       }
+
+       my $required = 1;
+
+       my $copts = $class->options();
+       $required = 0 if defined($copts->{$p}) && $copts->{$p}->{optional};
+
+       foreach my $t (keys %$plugins) {
+           my $opts = $pdata->{options}->{$t} || {};
+           $required = 0 if !defined($opts->{$p}) || $opts->{$p}->{optional};
+       }
+
+       if ($required) {
+           # make a copy, because we modify the optional property
+           my $res = &$copy_property($propertyList->{$p});
+           $res->{optional} = 0;
+           $props->{$p} = $res;
+       } else {
+           $props->{$p} = $propertyList->{$p};
+       }
+    }
 
     return {
        type => "object",
        additionalProperties => 0,
-       properties => $propertyList,
+       properties => $props,
     };
 }
 
 sub updateSchema {
-    my ($class) = @_;
+    my ($class, $single_class) = @_;
 
     my $pdata = $class->private();
     my $propertyList = $pdata->{propertyList};
@@ -69,8 +114,15 @@ sub updateSchema {
 
     my $props = {};
 
+    my $filter_type = $class->type() if $single_class;
+
     foreach my $p (keys %$propertyList) {
        next if $p eq 'type';
+
+       my $copts = $class->options();
+
+       next if defined($filter_type) && !defined($copts->{$p});
+
        if (!$propertyList->{$p}->{optional}) {
            $props->{$p} = $propertyList->{$p};
            next;
@@ -78,6 +130,8 @@ sub updateSchema {
 
        my $modifyable = 0;
 
+       $modifyable = 1 if defined($copts->{$p}) && !$copts->{$p}->{fixed};
+
        foreach my $t (keys %$plugins) {
            my $opts = $pdata->{options}->{$t} || {};
            next if !defined($opts->{$p});
@@ -138,7 +192,7 @@ sub init {
     }
 
     $propertyList->{type}->{type} = 'string';
-    $propertyList->{type}->{enum} = [keys %$plugins];
+    $propertyList->{type}->{enum} = [sort keys %$plugins];
 }
 
 sub lookup {
@@ -157,7 +211,7 @@ sub lookup_types {
 
     my $pdata = $class->private();
     
-    return [ keys %{$pdata->{plugins}} ];
+    return [ sort keys %{$pdata->{plugins}} ];
 }
 
 sub decode_value {
@@ -205,6 +259,10 @@ sub check_value {
        }
     }
 
+    if ($ct eq 'boolean' || $ct eq 'integer' || $ct eq 'number') {
+       return $value + 0; # convert to number
+    }
+
     return $value;
 }
 
@@ -221,7 +279,7 @@ sub parse_section_header {
 }
 
 sub format_section_header {
-    my ($class, $type, $sectionId) = @_;
+    my ($class, $type, $sectionId, $scfg, $done_hash) = @_;
 
     return "$type: $sectionId\n";
 }
@@ -350,8 +408,12 @@ my $format_config_line = sub {
 
     my $ct = $schema->{type};
 
+    die "property '$key' contains a line feed\n"
+       if ($key =~ m/[\n\r]/) || ($value =~ m/[\n\r]/);
+
     if ($ct eq 'boolean') {
-       return $value ? "\t$key\n" : '';
+       return "\t$key " . ($value ? 1 : 0) . "\n"
+           if defined($value);
     } else {
        return "\t$key $value\n" if "$value" ne '';
     }
@@ -386,18 +448,23 @@ sub write_config {
 
        die "unknown section type '$type'\n" if !$opts;
 
-       my $data = $class->format_section_header($type, $sectionId);
-       if ($scfg->{comment}) {
+       my $done_hash = {};
+
+       my $data = $class->format_section_header($type, $sectionId, $scfg, $done_hash);
+       if ($scfg->{comment} && !$done_hash->{comment}) {
            my $k = 'comment';
            my $v = $class->encode_value($type, $k, $scfg->{$k});
            $data .= &$format_config_line($propertyList->{$k}, $k, $v);
        }
 
-       $data .= "\tdisable\n" if $scfg->{disable};
+       $data .= "\tdisable\n" if $scfg->{disable} && !$done_hash->{disable};
 
-       my $done_hash = { comment => 1, disable => 1};
+       $done_hash->{comment} = 1;
+       $done_hash->{disable} = 1;
 
-       foreach my $k (keys %$opts) {
+       my @option_keys = sort keys %$opts;
+       foreach my $k (@option_keys) {
+           next if defined($done_hash->{$k});
            next if $opts->{$k}->{optional};
            $done_hash->{$k} = 1;
            my $v = $scfg->{$k};
@@ -407,7 +474,7 @@ sub write_config {
            $data .= &$format_config_line($propertyList->{$k}, $k, $v);
        }
 
-       foreach my $k (keys %$opts) {
+       foreach my $k (@option_keys) {
            next if defined($done_hash->{$k});
            my $v = $scfg->{$k};
            next if !defined($v);