2021-12-18 21:34:43 +01:00
package cmd
import (
2022-06-05 14:09:55 +02:00
"io"
"os"
2022-04-24 16:49:54 +02:00
"fmt"
2022-10-16 15:19:03 +02:00
//"log"
2022-06-05 14:09:55 +02:00
"sort"
"bufio"
"errors"
2021-12-18 21:34:43 +01:00
"os/exec"
2022-04-02 14:17:10 +02:00
"reflect"
"strconv"
2022-04-24 16:49:54 +02:00
"strings"
2022-06-05 14:09:55 +02:00
"io/ioutil"
2023-07-23 15:52:21 +02:00
"encoding/json"
2022-06-19 17:43:48 +02:00
"github.com/google/shlex"
2022-06-18 16:09:22 +02:00
"github.com/c2h5oh/datasize"
2022-10-16 15:19:03 +02:00
log "github.com/sirupsen/logrus"
2021-12-18 21:34:43 +01:00
)
2022-04-24 16:49:54 +02:00
const (
ipv4re = ` [0-9] { 1,3}\.[0-9] { 1,3}\.[0-9] { 1,3}\.[0-9] { 1,3} `
ifconfigipv4re = ` inet[[:space:]]( ` + ipv4re + ` ) `
2023-06-25 23:07:53 +02:00
// Maximum thread qty for start/stop
gMaxThreads = 4
2023-08-05 19:49:55 +02:00
gDefaultsJson = ` {
"CONFIG_VERSION" : "27" ,
"allow_chflags" : 0 ,
"allow_mlock" : 0 ,
"allow_mount" : 0 ,
"allow_mount_devfs" : 0 ,
"allow_mount_fusefs" : 0 ,
"allow_mount_nullfs" : 0 ,
"allow_mount_procfs" : 0 ,
"allow_mount_tmpfs" : 0 ,
"allow_mount_zfs" : 0 ,
"allow_quotas" : 0 ,
"allow_raw_sockets" : 0 ,
"allow_set_hostname" : 1 ,
"allow_socket_af" : 0 ,
"allow_sysvipc" : 0 ,
"allow_tun" : 0 ,
"allow_vmm" : 0 ,
"assign_localhost" : 0 ,
"available" : "readonly" ,
"basejail" : 0 ,
"boot" : 0 ,
"bpf" : 0 ,
"children_max" : "0" ,
"comment" : "none" ,
"compression" : "lz4" ,
"compressratio" : "readonly" ,
"coredumpsize" : "off" ,
"count" : "1" ,
"cpuset" : "off" ,
"cputime" : "off" ,
"datasize" : "off" ,
"dedup" : "off" ,
"defaultrouter" : "auto" ,
"defaultrouter6" : "auto" ,
"depends" : "none" ,
"devfs_ruleset" : "4" ,
"dhcp" : 0 ,
"enforce_statfs" : "2" ,
"exec_clean" : 1 ,
"exec_created" : "/usr/bin/true" ,
"exec_fib" : "0" ,
"exec_jail_user" : "root" ,
"exec_poststart" : "/usr/bin/true" ,
"exec_poststop" : "/usr/bin/true" ,
"exec_prestart" : "/usr/bin/true" ,
"exec_prestop" : "/usr/bin/true" ,
"exec_start" : "/bin/sh /etc/rc" ,
"exec_stop" : "/bin/sh /etc/rc.shutdown" ,
"exec_system_jail_user" : "0" ,
"exec_system_user" : "root" ,
"exec_timeout" : "60" ,
"host_domainname" : "none" ,
"host_time" : 1 ,
"hostid" : "36353536-3135-5a43-4a34-313130315a56" ,
"hostid_strict_check" : 0 ,
"interfaces" : "vnet0:bridge0" ,
"ip4" : "new" ,
"ip4_addr" : "none" ,
"ip4_saddrsel" : 1 ,
"ip6" : "new" ,
"ip6_addr" : "none" ,
"ip6_saddrsel" : 1 ,
"ip_hostname" : 0 ,
"jail_zfs" : 0 ,
"jail_zfs_mountpoint" : "none" ,
"last_started" : "none" ,
"localhost_ip" : "none" ,
"login_flags" : "-f root" ,
"mac_prefix" : "2c44fd" ,
"maxproc" : "off" ,
"memorylocked" : "off" ,
"memoryuse" : "off" ,
"min_dyn_devfs_ruleset" : "1000" ,
"mount_devfs" : 1 ,
"mount_fdescfs" : 1 ,
"mount_linprocfs" : 0 ,
"mount_procfs" : 0 ,
"mountpoint" : "readonly" ,
"msgqqueued" : "off" ,
"msgqsize" : "off" ,
"nat" : 0 ,
"nat_backend" : "ipfw" ,
"nat_forwards" : "none" ,
"nat_interface" : "none" ,
"nat_prefix" : "172.16" ,
"nmsgq" : "off" ,
"notes" : "none" ,
"nsem" : "off" ,
"nsemop" : "off" ,
"nshm" : "off" ,
"nthr" : "off" ,
"openfiles" : "off" ,
"origin" : "readonly" ,
"owner" : "root" ,
"pcpu" : "off" ,
"plugin_name" : "none" ,
"plugin_repository" : "none" ,
"priority" : "99" ,
"pseudoterminals" : "off" ,
"quota" : "none" ,
"readbps" : "off" ,
"readiops" : "off" ,
"reservation" : "none" ,
"resolver" : "/etc/resolv.conf" ,
"rlimits" : "off" ,
"rtsold" : 0 ,
"securelevel" : "2" ,
"shmsize" : "off" ,
"stacksize" : "off" ,
"stop_timeout" : "30" ,
"swapuse" : "off" ,
"sync_state" : "none" ,
"sync_target" : "none" ,
"sync_tgt_zpool" : "none" ,
"sysvmsg" : "new" ,
"sysvsem" : "new" ,
"sysvshm" : "new" ,
"template" : 0 ,
"type" : "jail" ,
"used" : "readonly" ,
"vmemoryuse" : "off" ,
"vnet" : 0 ,
"vnet0_mac" : "none" ,
"vnet1_mac" : "none" ,
"vnet2_mac" : "none" ,
"vnet3_mac" : "none" ,
"vnet_default_interface" : "auto" ,
"vnet_interfaces" : "none" ,
"wallclock" : "off" ,
"writebps" : "off" ,
"writeiops" : "off"
}
`
2022-04-24 16:49:54 +02:00
)
2022-04-05 20:58:11 +02:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Mandatory constructor for JailConfig type . It set default values
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
2022-04-05 22:21:04 +02:00
func NewJailConfig ( ) ( JailConfig , error ) {
2022-04-05 20:58:11 +02:00
var jc JailConfig
2022-04-24 16:49:54 +02:00
2022-04-05 20:58:11 +02:00
hostid , err := ioutil . ReadFile ( "/etc/hostid" )
if err != nil {
2022-04-18 13:36:33 +02:00
return jc , err
2022-04-05 21:43:11 +02:00
} else {
hostid = [ ] byte ( strings . Replace ( string ( hostid ) , "\n" , "" , - 1 ) )
2022-04-05 20:58:11 +02:00
}
2022-04-24 16:49:54 +02:00
jc . Allow_chflags = 0
jc . Allow_mlock = 0
jc . Allow_mount = 0
jc . Allow_mount_devfs = 0
jc . Allow_mount_fusefs = 0
jc . Allow_mount_nullfs = 0
jc . Allow_mount_procfs = 0
jc . Allow_mount_tmpfs = 0
jc . Allow_mount_zfs = 0
jc . Allow_quotas = 0
jc . Allow_raw_sockets = 0
jc . Allow_socket_af = 0
jc . Allow_set_hostname = 1
jc . Allow_sysvipc = 0
jc . Allow_tun = 0
jc . Allow_vmm = 0
jc . Assign_localhost = 0
jc . Available = "readonly"
jc . Basejail = 0
jc . Bpf = 0
jc . Boot = 0
jc . Children_max = "0"
jc . Comment = "none"
jc . Compression = "lz4"
jc . Compressratio = "readonly"
jc . Coredumpsize = "off"
jc . Count = "1"
jc . Cpuset = "off"
jc . Cputime = "off"
jc . Datasize = "off"
jc . Dedup = "off"
jc . Defaultrouter = "auto"
jc . Defaultrouter6 = "auto"
jc . Depends = "none"
jc . Devfs_ruleset = "4"
jc . Dhcp = 0
jc . Enforce_statfs = "2"
jc . Exec_clean = 1
jc . Exec_created = "/usr/bin/true"
jc . Exec_jail_user = "root"
jc . Exec_fib = "0"
jc . Exec_poststart = "/usr/bin/true"
jc . Exec_poststop = "/usr/bin/true"
jc . Exec_prestart = "/usr/bin/true"
jc . Exec_prestop = "/usr/bin/true"
jc . Exec_system_jail_user = "0"
jc . Exec_system_user = "root"
jc . Exec_start = "/bin/sh /etc/rc"
jc . Exec_stop = "/bin/sh /etc/rc.shutdown"
jc . Exec_timeout = "60"
jc . Hostid = string ( hostid )
jc . Hostid_strict_check = 0
jc . Host_time = 1
jc . Interfaces = "vnet0:bridge0"
jc . Ip4_addr = "none"
jc . Ip4_saddrsel = "1"
jc . Ip4 = "new"
jc . Ip6_addr = "none"
jc . Ip6_saddrsel = "1"
jc . Ip6 = "new"
jc . Ip_hostname = 0
jc . Jailtype = "jail"
jc . Jail_zfs = 0
jc . Jail_zfs_mountpoint = "none"
jc . Last_started = "none"
jc . Localhost_ip = "none"
jc . Login_flags = "-f root"
jc . Maxproc = "off"
jc . Min_dyn_devfs_ruleset = "1000"
jc . Memoryuse = "off"
jc . Memorylocked = "off"
jc . Mountpoint = "readonly"
jc . Mount_devfs = 1
jc . Mount_fdescfs = 1
jc . Mount_procfs = 0
jc . Mount_linprocfs = 0
jc . Msgqqueued = "off"
jc . Msgqsize = "off"
jc . Nat = 0
jc . Nat_backend = "ipfw"
jc . Nat_forwards = "none"
jc . Nat_interface = "none"
jc . Nat_prefix = "172.16"
jc . Nmsgq = "off"
jc . Notes = "none"
jc . Nsem = "off"
jc . Nsemop = "off"
jc . Nshm = "off"
jc . Nthr = "off"
jc . Openfiles = "off"
jc . Origin = "readonly"
jc . Owner = "root"
jc . Pcpu = "off"
jc . Plugin_name = "none"
jc . Plugin_repository = "none"
jc . Priority = "99"
jc . Pseudoterminals = "off"
jc . Quota = "none"
jc . Readbps = "off"
jc . Readiops = "off"
jc . Reservation = "none"
jc . Resolver = "/etc/resolv.conf"
jc . Rlimits = "off"
jc . Rtsold = 0
jc . Securelevel = "2"
jc . Shmsize = "off"
jc . Stacksize = "off"
jc . Stop_timeout = "30"
jc . Sync_state = "none"
jc . Sync_target = "none"
jc . Sync_tgt_zpool = "none"
jc . Sysvmsg = "new"
jc . Sysvsem = "new"
jc . Sysvshm = "new"
jc . Swapuse = "off"
jc . Template = 0
jc . Used = "readonly"
jc . Vmemoryuse = "off"
jc . Vnet = 0
jc . Vnet0_mac = "none"
jc . Vnet1_mac = "none"
jc . Vnet2_mac = "none"
jc . Vnet3_mac = "none"
2022-04-05 20:58:11 +02:00
jc . Vnet_default_interface = "auto"
2022-04-24 16:49:54 +02:00
jc . Vnet_interfaces = "none"
jc . Wallclock = "off"
jc . Writebps = "off"
jc . Writeiops = "off"
2022-04-18 13:36:33 +02:00
return jc , nil
2022-04-05 20:58:11 +02:00
}
2021-12-20 22:10:38 +01:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Command execution
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
2021-12-18 21:34:43 +01:00
func executeCommand ( cmdline string ) ( string , error ) {
2022-04-24 16:49:54 +02:00
var cmd [ ] string
var out [ ] byte
var err error
2021-12-18 21:34:43 +01:00
2022-04-24 16:49:54 +02:00
if gUseSudo {
cmd = append ( cmd , "sudo" )
}
2022-06-26 20:02:28 +02:00
var word string
var in_escaped bool
// Split by words, or " enclosed words
for i , c := range ( cmdline ) {
if string ( c ) == "\"" {
if in_escaped {
// This is the closing "
cmd = append ( cmd , word )
in_escaped = false
} else {
in_escaped = true
}
continue
}
if string ( c ) == " " {
if in_escaped {
word = word + string ( c )
continue
} else {
cmd = append ( cmd , word )
word = ""
continue
}
}
if i == ( len ( cmdline ) - 1 ) {
word = word + string ( c )
cmd = append ( cmd , word )
break
}
// else
word = word + string ( c )
}
2023-08-05 19:49:55 +02:00
log . Debugf ( "executeCommand: %s\n" , strings . Join ( cmd , " " ) )
2023-06-10 14:12:53 +02:00
2022-04-24 16:49:54 +02:00
if len ( cmd ) > 1 {
2022-06-04 22:28:38 +02:00
out , err = exec . Command ( cmd [ 0 ] , cmd [ 1 : ] ... ) . CombinedOutput ( )
2022-04-24 16:49:54 +02:00
} else {
2022-06-04 22:28:38 +02:00
out , err = exec . Command ( cmd [ 0 ] ) . CombinedOutput ( )
2022-04-24 16:49:54 +02:00
}
2021-12-18 21:34:43 +01:00
2022-04-24 16:49:54 +02:00
return string ( out ) , err
2021-12-18 21:34:43 +01:00
}
2023-06-10 14:12:53 +02:00
/ * From iocage :
* # Courtesy of @ william - gr
* # service ( 8 ) and some rc . d scripts have the bad h * abit of
* # exec ' ing and never closing stdout / stderr . This makes
* # sure we read only enough until the command exits and do
* # not wait on the pipe to close on the other end .
* So this function executes process without waiting after completion
* /
func executeCommandNonBlocking ( cmdline string ) ( error ) {
var cmd [ ] string
var oCmd * exec . Cmd
var err error
if gUseSudo {
cmd = append ( cmd , "sudo" )
}
var word string
var in_escaped bool
// Split by words, or " enclosed words
for i , c := range ( cmdline ) {
if string ( c ) == "\"" {
if in_escaped {
// This is the closing "
cmd = append ( cmd , word )
in_escaped = false
} else {
in_escaped = true
}
continue
}
if string ( c ) == " " {
if in_escaped {
word = word + string ( c )
continue
} else {
cmd = append ( cmd , word )
word = ""
continue
}
}
if i == ( len ( cmdline ) - 1 ) {
word = word + string ( c )
cmd = append ( cmd , word )
break
}
// else
word = word + string ( c )
}
if len ( cmd ) > 1 {
oCmd = exec . Command ( cmd [ 0 ] , cmd [ 1 : ] ... )
} else {
oCmd = exec . Command ( cmd [ 0 ] )
}
if err = oCmd . Start ( ) ; err != nil {
return err
}
err = oCmd . Wait ( )
return err
}
2022-11-20 20:16:23 +01:00
// Executed command outputs to stdout in realtime
func executeCommandWithOutputToStdout ( cmdline string ) ( error ) {
var cmd [ ] string
var err error
if gUseSudo {
cmd = append ( cmd , "sudo" )
}
var word string
var in_escaped bool
// Split by words, or " enclosed words
for i , c := range ( cmdline ) {
if string ( c ) == "\"" {
if in_escaped {
// This is the closing "
cmd = append ( cmd , word )
in_escaped = false
} else {
in_escaped = true
}
continue
}
if string ( c ) == " " {
if in_escaped {
word = word + string ( c )
continue
} else {
cmd = append ( cmd , word )
word = ""
continue
}
}
if i == ( len ( cmdline ) - 1 ) {
word = word + string ( c )
cmd = append ( cmd , word )
break
}
// else
word = word + string ( c )
}
2023-08-06 14:50:32 +02:00
log . Debugf ( "executeCommandWithOutputToStdout: will execute \"%s\"\n" , strings . Join ( cmd , " " ) )
2022-11-20 20:16:23 +01:00
var execHandle * exec . Cmd
if len ( cmd ) > 1 {
execHandle = exec . Command ( cmd [ 0 ] , cmd [ 1 : ] ... )
} else {
execHandle = exec . Command ( cmd [ 0 ] )
}
stdout , err := execHandle . StdoutPipe ( )
if err != nil {
return err
}
execHandle . Start ( )
buf := bufio . NewReader ( stdout )
for {
line , _ , err := buf . ReadLine ( )
if err != nil {
if err . Error ( ) == "EOF" {
return nil
} else {
return err
}
}
fmt . Println ( string ( line ) )
}
return fmt . Errorf ( "Unknown error: you shouldn't be here!\n" )
}
2023-07-23 15:11:29 +02:00
/ * Execute command plugging stdin and stdout to those of the running command .
* Blocking while the command run
* /
func executeCommandWithStdinStdoutStderr ( cmdline string ) ( error ) {
var cmd [ ] string
if gUseSudo {
cmd = append ( cmd , "sudo" )
}
var word string
var in_escaped bool
// Split by words, or " enclosed words
for i , c := range ( cmdline ) {
if string ( c ) == "\"" {
if in_escaped {
// This is the closing "
cmd = append ( cmd , word )
in_escaped = false
} else {
in_escaped = true
}
continue
}
if string ( c ) == " " {
if in_escaped {
word = word + string ( c )
continue
} else {
cmd = append ( cmd , word )
word = ""
continue
}
}
if i == ( len ( cmdline ) - 1 ) {
word = word + string ( c )
cmd = append ( cmd , word )
break
}
// else
word = word + string ( c )
}
var command * exec . Cmd
if len ( cmd ) > 1 {
command = exec . Command ( cmd [ 0 ] , cmd [ 1 : ] ... )
} else {
command = exec . Command ( cmd [ 0 ] )
}
// Get environment
command . Env = os . Environ ( )
// Connect command to current stdin/out/err
command . Stdin = os . Stdin
command . Stdout = os . Stdout
command . Stderr = os . Stderr
if err := command . Start ( ) ; err != nil {
return err
}
err := command . Wait ( )
return err
}
2021-12-18 21:34:43 +01:00
func executeCommandInJail ( jail * Jail , cmdline string ) ( string , error ) {
2022-04-24 16:49:54 +02:00
var cmd [ ] string
2021-12-18 21:34:43 +01:00
2023-07-23 15:11:29 +02:00
// We can't execute on non-running jail
if jail . Running == false {
return "" , errors . New ( "Can't execute command on stopped jail" )
}
2022-04-24 16:49:54 +02:00
if gUseSudo {
cmd = append ( cmd , "sudo" )
}
2021-12-18 21:34:43 +01:00
2022-04-24 16:49:54 +02:00
cmd = append ( cmd , "setfib" )
if len ( jail . Config . Exec_fib ) > 0 {
cmd = append ( cmd , jail . Config . Exec_fib )
} else {
cmd = append ( cmd , "0" )
}
2021-12-18 21:34:43 +01:00
2022-04-24 16:49:54 +02:00
cmd = append ( cmd , "jexec" , jail . InternalName )
2021-12-18 21:34:43 +01:00
2022-06-26 20:02:28 +02:00
var word string
var in_escaped bool
// Split by words, or " enclosed words
for i , c := range ( cmdline ) {
if string ( c ) == "\"" {
if in_escaped {
// This is the closing "
cmd = append ( cmd , word )
in_escaped = false
} else {
in_escaped = true
}
continue
}
if string ( c ) == " " {
if in_escaped {
word = word + string ( c )
continue
} else {
cmd = append ( cmd , word )
word = ""
continue
}
}
if i == ( len ( cmdline ) - 1 ) {
word = word + string ( c )
cmd = append ( cmd , word )
break
}
// else
word = word + string ( c )
}
2023-07-23 15:11:29 +02:00
if gDebug {
fmt . Printf ( "DEBUG: executeCommandInJail: prepare to execute \"%s\"\n" , cmd )
}
2021-12-18 21:34:43 +01:00
2022-06-05 11:20:16 +02:00
out , err := exec . Command ( cmd [ 0 ] , cmd [ 1 : ] ... ) . CombinedOutput ( )
2021-12-18 21:34:43 +01:00
2022-04-24 16:49:54 +02:00
return string ( out ) , err
2021-12-18 21:34:43 +01:00
}
2022-06-19 17:43:48 +02:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Execute a script , or shell command . Need to double escape special characters
* Ex . : executeScript ( "echo '\\\\_o\\< \\~ COINCOIN' > /tmp/coincoin.txt" )
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
func executeScript ( script string ) ( string , error ) {
var cmd [ ] string
var out [ ] byte
var err error
if gUseSudo {
cmd = append ( cmd , "sudo" )
}
cmd = append ( cmd , [ ] string { "/bin/sh" , "-c" } ... )
s , err := shlex . Split ( script )
if err != nil {
return "" , err
}
cmd = append ( cmd , strings . Join ( s , " " ) )
//fmt.Printf("DEBUG: Prepare to execute %v\n", cmd)
if len ( cmd ) > 1 {
out , err = exec . Command ( cmd [ 0 ] , cmd [ 1 : ] ... ) . CombinedOutput ( )
} else {
out , err = exec . Command ( cmd [ 0 ] ) . CombinedOutput ( )
}
return string ( out ) , err
}
2022-06-18 16:09:22 +02:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* ZFS datasets / pools operations
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
2022-06-05 14:09:55 +02:00
func zfsSnapshot ( dataset string , snapname string ) error {
cmd := fmt . Sprintf ( "zfs snapshot %s@%s" , dataset , snapname )
out , err := executeCommand ( cmd )
if err != nil {
return errors . New ( fmt . Sprintf ( "%v; command returned \"%s\"" , err , out ) )
}
return nil
}
2023-08-05 19:49:55 +02:00
// Copy snapshot to a new dataset
// TODO : Intercept death of sending process, then kill receiving
2022-06-05 14:09:55 +02:00
func zfsCopy ( src string , dest string ) error {
// First, declare sending process & pipe
2023-08-05 19:49:55 +02:00
log . Debugf ( "Execute: zfs send %s\n" , src )
2022-06-05 14:09:55 +02:00
cmd_send := exec . Command ( "zfs" , "send" , src )
stdout_send , err := cmd_send . StdoutPipe ( )
if err != nil {
//fmt.Printf("Error executing command \"zfs send %s\": %v\n", fmt.Sprintf("%s@gocage_mig_init", dsconf), err)
return errors . New ( fmt . Sprintf ( "Error: %v\n" , err ) )
}
// then declare receiving process & pipe
2023-08-05 19:49:55 +02:00
log . Debugf ( "Execute: zfs receive %s\n" , dest )
2022-06-05 14:09:55 +02:00
cmd_recv := exec . Command ( "zfs" , "receive" , dest )
stdin_recv , err := cmd_recv . StdinPipe ( )
if err != nil {
//fmt.Printf("Error executing command \"zfs receive %s\": %v\n", dest, err)
return errors . New ( fmt . Sprintf ( "Error: %v\n" , err ) )
}
// Copy data in a go routine
go io . Copy ( stdin_recv , stdout_send )
// then start processes and wait for finish
if err := cmd_recv . Start ( ) ; err != nil {
//fmt.Printf("Error: %v\n", err)
2023-08-05 19:49:55 +02:00
log . Debugf ( "zfs receive %s started: %v" , dest , err )
2022-06-05 14:09:55 +02:00
return errors . New ( fmt . Sprintf ( "Error starting receive process: %v\n" , err ) )
}
//fmt.Printf("DEBUG: Start \"zfs send %s\"\n", dsconf)
if err := cmd_send . Start ( ) ; err != nil {
//fmt.Printf("Error: %v\n", err)
2023-08-05 19:49:55 +02:00
log . Debugf ( "zfs send %s started: %v" , src , err )
2022-06-05 14:09:55 +02:00
return errors . New ( fmt . Sprintf ( "Error starting send process: %v\n" , err ) )
}
//fmt.Printf("DEBUG: Wait for zfs send to finish\n")
if err := cmd_send . Wait ( ) ; err != nil {
2023-08-05 19:49:55 +02:00
log . Debugf ( "zfs send %s stopped with %v" , err )
2022-06-05 14:09:55 +02:00
//fmt.Printf("Error: zfs send halted with %v\n", err)
return errors . New ( fmt . Sprintf ( "send halted with: %v\n" , err ) )
}
//fmt.Printf("DEBUG: Wait for zfs recv to finish\n")
if err := cmd_recv . Wait ( ) ; err != nil {
//fmt.Printf("Error: zfs recv halted with %v\n", err)
return errors . New ( fmt . Sprintf ( "receive halted with: %v\n" , err ) )
}
return nil
}
2022-06-05 18:42:20 +02:00
2022-06-18 11:09:06 +02:00
// Copy incremental snasphot to destination
func zfsCopyIncremental ( firstsnap string , secondsnap string , dest string ) error {
2022-06-05 18:42:20 +02:00
// First, declare sending process & pipe
2022-06-18 11:09:06 +02:00
cmd_send := exec . Command ( "zfs" , "send" , "-i" , firstsnap , secondsnap )
2022-06-05 18:42:20 +02:00
stdout_send , err := cmd_send . StdoutPipe ( )
if err != nil {
return errors . New ( fmt . Sprintf ( "Error: %v\n" , err ) )
}
// then declare receiving process & pipe
2022-06-18 16:34:06 +02:00
/ * "-Fu" flags to avoid " cannot receive incremental stream : destination qstore / iocage / jails / kibana has been modified
* since most recent snapshot " error message * /
2022-06-18 11:09:06 +02:00
cmd_recv := exec . Command ( "zfs" , "receive" , "-Fu" , dest )
2022-06-05 18:42:20 +02:00
stdin_recv , err := cmd_recv . StdinPipe ( )
if err != nil {
return errors . New ( fmt . Sprintf ( "Error: %v\n" , err ) )
}
// Copy data in a go routine
go io . Copy ( stdin_recv , stdout_send )
// then start processes and wait for finish
if err := cmd_recv . Start ( ) ; err != nil {
return errors . New ( fmt . Sprintf ( "Error starting receive process: %v\n" , err ) )
}
//fmt.Printf("DEBUG: Start \"zfs send %s\"\n", dsconf)
if err := cmd_send . Start ( ) ; err != nil {
return errors . New ( fmt . Sprintf ( "Error starting send process: %v\n" , err ) )
}
//fmt.Printf("DEBUG: Wait for zfs send to finish\n")
if err := cmd_send . Wait ( ) ; err != nil {
return errors . New ( fmt . Sprintf ( "send halted with: %v\n" , err ) )
}
//fmt.Printf("DEBUG: Wait for zfs recv to finish\n")
if err := cmd_recv . Wait ( ) ; err != nil {
return errors . New ( fmt . Sprintf ( "receive halted with: %v\n" , err ) )
}
return nil
}
2022-10-16 15:19:03 +02:00
// Return true if dataset exist, false if not, false & error if anything else
func doZfsDatasetExist ( dataset string ) ( bool , error ) {
cmd := fmt . Sprintf ( "zfs list %s" , dataset )
out , err := executeCommand ( cmd )
if err != nil {
if strings . HasSuffix ( strings . TrimSuffix ( out , "\n" ) , "dataset does not exist" ) {
return false , nil
} else {
return false , errors . New ( fmt . Sprintf ( "%v; command returned \"%s\"" , err , out ) )
}
}
return true , nil
}
2023-08-06 11:15:49 +02:00
/ * Create ZFS dataset
* mountpoint can be "none" , then the dataset won ' t be mounted
* mountpoint can be "" , then it will be inherited
* compression can be "" , then it wil be inherited
* /
2023-07-09 13:40:40 +02:00
func zfsCreateDataset ( dataset , mountpoint , compression string ) error {
2023-08-06 11:15:49 +02:00
cmd := "zfs create"
if len ( mountpoint ) > 0 {
cmd = fmt . Sprintf ( "%s -o mountpoint=%s" , cmd , mountpoint )
}
if len ( compression ) > 0 {
cmd = fmt . Sprintf ( "%s -o compression=%s" , cmd , compression )
}
cmd = fmt . Sprintf ( "%s %s" , cmd , dataset )
2022-10-16 15:19:03 +02:00
out , err := executeCommand ( cmd )
if err != nil {
return errors . New ( fmt . Sprintf ( "%v; command returned \"%s\"" , err , out ) )
}
return nil
}
2023-07-09 13:40:40 +02:00
// Return dataset name for a given mountpoint
func zfsGetDatasetByMountpoint ( mountpoint string ) ( string , error ) {
cmd := fmt . Sprintf ( "zfs list -p -r -H -o name %s" , mountpoint )
out , err := executeCommand ( cmd )
if err != nil {
return "" , errors . New ( fmt . Sprintf ( "%v; command returned \"%s\"" , err , out ) )
}
return strings . TrimSuffix ( out , "\n" ) , nil
}
// Delete a ZFS Dataset by name
func zfsDestroy ( dataset string ) error {
2023-08-05 19:49:55 +02:00
log . Debugf ( "execute \"zfs destroy -r %s\"\n" , dataset )
2023-07-09 13:40:40 +02:00
cmd := fmt . Sprintf ( "zfs destroy -r %s" , dataset )
out , err := executeCommand ( cmd )
if err != nil {
return errors . New ( fmt . Sprintf ( "%v; command returned \"%s\"" , err , out ) )
}
return nil
}
2022-10-16 15:19:03 +02:00
2023-08-06 11:15:49 +02:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Filesystem operations
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
2023-08-05 19:49:55 +02:00
/* Copy file */
func copyFile ( src , dst string ) error {
srcfinfo , err := os . Stat ( src )
if err != nil {
return fmt . Errorf ( "Cannot find source file: %s" , err . Error ( ) )
}
if ! srcfinfo . Mode ( ) . IsRegular ( ) {
return fmt . Errorf ( "%s is not a regular file" , src )
}
srcHandle , err := os . Open ( src )
if err != nil {
return fmt . Errorf ( "Cannot open source file: %s" , err . Error ( ) )
}
defer srcHandle . Close ( )
dstHandle , err := os . Create ( dst )
if err != nil {
return fmt . Errorf ( "Cannot create destination file: %s" , err . Error ( ) )
}
defer dstHandle . Close ( )
_ , err = io . Copy ( dstHandle , srcHandle )
return err
}
2023-08-06 11:15:49 +02:00
// Get permissions of file or folder
func getPermissions ( path string ) ( os . FileInfo , error ) {
return os . Stat ( path )
}
2022-04-24 16:55:33 +02:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* rc . conf management
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
func enableRcKeyValue ( rcconfpath string , key string , value string ) error {
cmd := fmt . Sprintf ( "/usr/sbin/sysrc -f %s %s=%s" , rcconfpath , key , value )
_ , err := executeCommand ( cmd )
if err != nil {
return err
}
return nil
}
func disableRcKey ( rcconfpath string , key string ) error {
2022-06-19 19:14:58 +02:00
// First check if key exist
cmd := fmt . Sprintf ( "/usr/sbin/sysrc -f %s %s" , rcconfpath , key )
out , err := executeCommand ( cmd )
if err != nil {
if strings . HasPrefix ( out , "sysrc: unknown variable" ) {
return nil
} else {
return err
}
}
cmd = fmt . Sprintf ( "/usr/sbin/sysrc -f %s -x %s" , rcconfpath , key )
_ , err = executeCommand ( cmd )
2022-04-24 16:55:33 +02:00
if err != nil {
return err
}
return nil
}
2022-04-02 14:17:10 +02:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Parse an fstab file , returning an array of Mount
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
func getFstab ( path string ) ( [ ] Mount , error ) {
var mounts [ ] Mount
f , err := os . Open ( path )
if err != nil {
return mounts , err
}
defer f . Close ( )
scan := bufio . NewScanner ( f )
for scan . Scan ( ) {
res := strings . Fields ( scan . Text ( ) )
2023-11-09 19:16:53 +01:00
// iocage create lines like that : "/iocage/releases/13.2-RELEASE/root/bin /iocage/jails/smtp-router-02/root/bin nullfs ro 0 0 # Added by iocage on 2023-10-10 17:20:51"
if ( len ( res ) > 6 && ! strings . EqualFold ( res [ 6 ] , "#" ) ) || len ( res ) < 6 {
2022-04-02 14:17:10 +02:00
return mounts , fmt . Errorf ( "Incorrect format for fstab line %s" , scan . Text ( ) )
}
freq , err := strconv . Atoi ( res [ 4 ] )
if err != nil {
return mounts , fmt . Errorf ( "Incorrect format for fstab line %s: Dump is not an integer\n" , scan . Text ( ) )
}
pass , err := strconv . Atoi ( res [ 5 ] )
if err != nil {
return mounts , fmt . Errorf ( "Incorrect format for fstab line %s: Pass is not an integer\n" , scan . Text ( ) )
}
m := Mount {
2022-04-24 16:49:54 +02:00
Device : res [ 0 ] ,
Mountpoint : res [ 1 ] ,
Type : res [ 2 ] ,
Options : strings . Split ( res [ 3 ] , "," ) ,
Fs_Freq : freq ,
Fs_Passno : pass ,
2022-04-02 14:17:10 +02:00
}
mounts = append ( mounts , m )
}
2022-04-24 16:49:54 +02:00
2022-04-02 14:17:10 +02:00
return mounts , nil
}
2022-06-19 19:45:07 +02:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2022-06-18 16:09:22 +02:00
*
2022-06-19 19:45:07 +02:00
* devfs rules management
2022-06-18 16:09:22 +02:00
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
2022-06-18 20:07:57 +02:00
func getDevfsRuleset ( ruleset int ) [ ] string {
cmd := fmt . Sprintf ( "/sbin/devfs rule -s %d show" , ruleset )
out , err := executeCommand ( cmd )
if err != nil {
return [ ] string { }
}
// Get rid of the last "\n"
return strings . Split ( out , "\n" ) [ : len ( strings . Split ( out , "\n" ) ) - 1 ]
}
func copyDevfsRuleset ( ruleset int , srcrs int ) error {
// Resulting ruleset as an array of line
//var result []string
out := getDevfsRuleset ( srcrs )
for _ , line := range out {
//fields := strings.Fields(line)
cmd := fmt . Sprintf ( "/sbin/devfs rule -s %d add %s" , ruleset , line )
out , err := executeCommand ( cmd )
if err != nil {
return errors . New ( fmt . Sprintf ( "Error adding rule \"%s\" to ruleset %d: %s" , line , ruleset , out ) )
}
}
return nil
}
2023-07-23 15:11:29 +02:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Add a rule to specified ruleset
* Ex . : addDevfsRuleToRuleset ( "path bpf* unhide" , 1002 )
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
func addDevfsRuleToRuleset ( rule string , ruleset int ) error {
// TODO: Check if rule not already enabled. We will need to recurse into includes.
// Get last rule index
rules := getDevfsRuleset ( ruleset )
if len ( rules ) == 0 {
fmt . Printf ( "Error listing ruleset %d\n" , ruleset )
return errors . New ( fmt . Sprintf ( "Error listing rueset %d\n" , ruleset ) )
}
f := strings . Fields ( rules [ ( len ( rules ) - 1 ) ] )
//fmt.Printf("Dernier index du ruleset %d: %s\n", ruleset, f[0])
index , _ := strconv . Atoi ( f [ 0 ] )
index += 100
cmd := fmt . Sprintf ( "/sbin/devfs rule -s %d add %d %s" , ruleset , index , rule )
out , err := executeCommand ( cmd )
if err != nil {
return errors . New ( fmt . Sprintf ( "Error adding rule \"%s\" to ruleset %d: %s" , rule , ruleset , out ) )
}
return nil
}
2022-06-26 20:02:28 +02:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Returns value of parameter as read in / var / run / jail . $ InternalName . conf
2023-06-10 14:12:53 +02:00
* Directives without value will return "true" if found
2022-06-26 20:02:28 +02:00
* Returns an error if parameter not found in file
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
func getValueFromRunningConfig ( jname string , param string ) ( string , error ) {
content , err := ioutil . ReadFile ( fmt . Sprintf ( "/var/run/jail.%s.conf" , jname ) )
if err != nil {
return "" , err
}
2023-08-05 19:49:55 +02:00
2022-06-26 20:02:28 +02:00
for _ , line := range strings . Split ( string ( content ) , "\n" ) {
if strings . Contains ( line , fmt . Sprintf ( "%s = " , param ) ) {
split := strings . Split ( line , "=" )
switch len ( split ) {
// directives without value
case 0 :
return "true" , nil
case 1 :
return "" , fmt . Errorf ( "Invalid format: %s" , line )
case 2 :
return strings . Trim ( split [ 1 ] , " ;\"" ) , nil
}
}
}
2023-08-05 19:49:55 +02:00
2022-06-26 20:02:28 +02:00
return "" , fmt . Errorf ( "Parameter not found: %s" , param )
}
2022-06-18 20:07:57 +02:00
2022-06-19 19:45:07 +02:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Generic utilities
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
func isStringInArray ( strarr [ ] string , searched string ) bool {
for _ , s := range strarr {
if strings . EqualFold ( s , searched ) {
return true
}
}
return false
}
2023-08-05 19:49:55 +02:00
func ( j Jail ) isFirstNetDhcp ( ) bool {
for _ , n := range strings . Split ( j . Config . Ip4_addr , "," ) {
splitd := strings . Split ( n , "|" )
if len ( splitd ) > 1 && strings . EqualFold ( splitd [ 1 ] , "dhcp" ) {
return true
}
}
return false
}
2022-06-26 20:02:28 +02:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Get a specific jail reference , to update properties after a range loop
2022-07-10 14:15:02 +02:00
* Name can be short or long form ( "myjail" vs "mystore/myjail" )
2023-08-05 19:49:55 +02:00
* An empty jailtype means "all types"
2022-06-26 20:02:28 +02:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
2023-08-05 19:49:55 +02:00
func getJailFromArray ( name string , jailtypes [ ] string , jarray [ ] Jail ) ( * Jail , error ) {
2022-07-10 14:15:02 +02:00
var ds , jail string
var jails [ ] Jail
2023-08-05 19:49:55 +02:00
if ( len ( jailtypes ) == 1 && len ( jailtypes [ 0 ] ) == 0 ) || len ( jailtypes ) == 0 {
2023-08-06 14:50:32 +02:00
jailtypes = [ ] string { "basejail" , "jail" , "template" }
2023-08-05 19:49:55 +02:00
}
2022-07-10 14:15:02 +02:00
if strings . Contains ( name , "/" ) {
split := strings . Split ( name , "/" )
if len ( split ) != 2 {
return & Jail { } , errors . New ( "Invalid format for jail name" )
}
ds = split [ 0 ]
jail = split [ 1 ]
} else {
jail = name
}
2022-06-26 20:02:28 +02:00
for i , j := range jarray {
2023-07-09 10:38:00 +02:00
if strings . HasPrefix ( j . Name , jail ) {
2023-08-05 19:49:55 +02:00
if isStringInArray ( jailtypes , j . Config . Jailtype ) {
if len ( ds ) > 0 {
if strings . EqualFold ( ds , j . Datastore ) {
return & jarray [ i ] , nil
} else {
continue
}
2022-07-10 14:15:02 +02:00
} else {
2023-08-05 19:49:55 +02:00
jails = append ( jails , j )
2022-07-10 14:15:02 +02:00
}
}
}
}
if len ( jails ) > 0 {
if len ( jails ) > 1 {
2023-07-09 10:38:00 +02:00
return & Jail { } , errors . New ( "More than one jail matching, please use datastore/jail format or full name" )
2022-07-10 14:15:02 +02:00
} else {
return & jails [ 0 ] , nil
2022-06-26 20:02:28 +02:00
}
}
2022-07-10 14:15:02 +02:00
2022-06-26 20:02:28 +02:00
return & Jail { } , errors . New ( "Jail not found" )
}
2022-06-19 19:45:07 +02:00
func getDatastoreFromArray ( name string , dsa [ ] Datastore ) ( * Datastore , error ) {
for _ , d := range dsa {
if name == d . Name {
return & d , nil
}
}
return & Datastore { } , errors . New ( "Datastore not found" )
}
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Flag a jail so config on disk will be updated when calling WriteConfigToDisk
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
func setJailConfigUpdated ( jail * Jail ) error {
if len ( jail . ConfigPath ) == 0 {
return errors . New ( fmt . Sprintf ( "No config path for jail %s" , jail . Name ) )
}
2023-08-05 19:49:55 +02:00
j , err := getJailFromArray ( jail . Name , [ ] string { "" } , gJails )
2022-06-19 19:45:07 +02:00
if err != nil {
return err
}
j . ConfigUpdated = true
2022-11-20 20:16:23 +01:00
2022-06-19 19:45:07 +02:00
return nil
}
2023-08-06 14:50:32 +02:00
func getVersion ( jail * Jail ) ( string , error ) {
cvers , err := executeCommand ( fmt . Sprintf ( "%s/bin/freebsd-version" , jail . RootPath ) )
if err != nil {
fmt . Printf ( "ERROR: %s\n" , err . Error ( ) )
return "" , err
}
return strings . TrimRight ( cvers , "\n" ) , nil
}
2023-07-23 15:52:21 +02:00
func updateVersion ( jail * Jail ) error {
2023-08-06 14:50:32 +02:00
cvers , err := executeCommand ( fmt . Sprintf ( "%s/bin/freebsd-version" , jail . RootPath ) )
2023-07-23 15:52:21 +02:00
if err != nil {
fmt . Printf ( "ERROR: %s\n" , err . Error ( ) )
return err
}
cvers = strings . TrimRight ( cvers , "\n" )
jail . Config . Release = cvers
2023-08-06 14:50:32 +02:00
jail . WriteConfigToDisk ( false )
2023-07-23 15:52:21 +02:00
return nil
}
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Write jail ( s ) config which been updated to disk .
* If name is specified , work on the jail . If name is empty string , work on all .
* If changeauto not set , values which are in "auto" mode on disk
* won ' t be overwritten ( p . ex defaultrouter wont be overwritten with current
* default route , so if route change on jailhost this will reflect on jail next
* start )
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
func writeConfigToDisk ( j * Jail , changeauto bool ) {
// we will manipulate properties so get a copy
jc := j . Config
if changeauto == false {
// Overwrite "auto" properties
ondiskjc , err := getJailConfig ( j . ConfigPath )
if err != nil {
panic ( err )
}
// TODO : List all fields, then call getStructFieldValue to compare value with "auto"
// If "auto" then keep it that way before writing ondiskjc to disk
var properties [ ] string
properties = getStructFieldNames ( ondiskjc , properties , "" )
for _ , p := range properties {
v , _ , err := getStructFieldValue ( ondiskjc , p )
if err != nil {
panic ( err )
}
if v . String ( ) == "auto" {
err = setStructFieldValue ( & jc , p , "auto" )
if err != nil {
fmt . Printf ( "ERROR sanitizing config: %s\n" , err . Error ( ) )
os . Exit ( 1 )
}
}
}
}
marshaled , err := json . MarshalIndent ( jc , "" , " " )
if err != nil {
fmt . Printf ( "ERROR marshaling config: %s\n" , err . Error ( ) )
}
//fmt.Printf("DEBUG: Will write config to disk, with content:\n")
//fmt.Printf(string(marshaled))
if os . WriteFile ( j . ConfigPath , [ ] byte ( marshaled ) , 0644 ) ; err != nil {
fmt . Printf ( "Error writing config file %s: %v\n" , j . ConfigPath , err )
os . Exit ( 1 )
}
}
2023-08-05 19:49:55 +02:00
func ( j Jail ) WriteConfigToDisk ( changeauto bool ) {
// we will manipulate properties so get a copy
jc := j . Config
if changeauto == false {
// Overwrite "auto" properties
ondiskjc , err := getJailConfig ( j . ConfigPath )
if err != nil {
panic ( err )
}
// TODO : List all fields, then call getStructFieldValue to compare value with "auto"
// If "auto" then keep it that way before writing ondiskjc to disk
var properties [ ] string
properties = getStructFieldNames ( ondiskjc , properties , "" )
for _ , p := range properties {
v , _ , err := getStructFieldValue ( ondiskjc , p )
if err != nil {
panic ( err )
}
if v . String ( ) == "auto" {
err = setStructFieldValue ( & jc , p , "auto" )
if err != nil {
fmt . Printf ( "ERROR sanitizing config: %s\n" , err . Error ( ) )
os . Exit ( 1 )
}
}
}
}
marshaled , err := json . MarshalIndent ( jc , "" , " " )
if err != nil {
fmt . Printf ( "ERROR marshaling config: %s\n" , err . Error ( ) )
}
if os . WriteFile ( j . ConfigPath , [ ] byte ( marshaled ) , 0644 ) ; err != nil {
fmt . Printf ( "Error writing config file %s: %v\n" , j . ConfigPath , err )
os . Exit ( 1 )
}
}
2022-06-18 18:24:09 +02:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Return the quantity of jails with the name passed as parameter
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
func countOfJailsWithThisName ( name string ) int {
count := 0
for _ , j := range gJails {
if strings . EqualFold ( j . Name , name ) {
count ++
}
}
return count
}
2022-07-10 14:15:02 +02:00
func isNameDistinctive ( name string , jails [ ] Jail ) bool {
2023-08-05 19:49:55 +02:00
_ , err := getJailFromArray ( name , [ ] string { "" } , jails )
2022-07-10 14:15:02 +02:00
if err != nil {
return false
} else {
return true
}
}
2022-04-02 15:50:14 +02:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Recurse into structure , returning reflect . Kind of named field .
* Nested fields are named with a dot ( ex "MyStruct.MyField" )
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
2022-04-02 14:17:10 +02:00
func getStructFieldKind ( parentStruct interface { } , fieldName string ) ( reflect . Kind , string , error ) {
2022-04-24 16:49:54 +02:00
v := reflect . ValueOf ( parentStruct )
2022-04-02 14:17:10 +02:00
2022-04-24 16:49:54 +02:00
if v . Kind ( ) == reflect . Ptr {
v = v . Elem ( )
}
2022-04-02 14:17:10 +02:00
// For debugging
2022-04-24 16:49:54 +02:00
if false {
for i := 0 ; i < v . NumField ( ) ; i ++ {
f := v . Field ( i )
if f . Kind ( ) == reflect . String {
fmt . Printf ( "%v=%v\n" , v . Type ( ) . Field ( i ) . Name , f . Interface ( ) )
}
}
}
if strings . Contains ( fieldName , "." ) {
fs := strings . Split ( fieldName , "." )
f := v . FieldByName ( fs [ 0 ] )
if f . Kind ( ) == reflect . Struct {
return getStructFieldKind ( f . Interface ( ) , strings . Join ( fs [ 1 : ] , "." ) )
} else {
return reflect . Kind ( 0 ) , fieldName , errors . New ( fmt . Sprintf ( "%s is not a struct: %s\n" , fs [ 0 ] , f . Kind ( ) . String ( ) ) )
}
} else {
f := v . FieldByName ( fieldName )
if f . IsValid ( ) {
return f . Kind ( ) , fieldName , nil
} else {
return reflect . Kind ( 0 ) , fieldName , errors . New ( fmt . Sprintf ( "Field not found: %s" , fieldName ) )
}
}
return reflect . Kind ( 0 ) , fieldName , errors . New ( fmt . Sprintf ( "Field not found: %s" , fieldName ) )
2022-04-02 14:17:10 +02:00
}
2022-04-02 15:50:14 +02:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Display struct attributes name for a given struct .
* Recurse into struct attributes of type struct
* Used to show user jail properties
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
func getStructFieldNames ( parentStruct interface { } , result [ ] string , prefix string ) [ ] string {
v := reflect . ValueOf ( parentStruct )
2022-04-24 16:49:54 +02:00
for i := 0 ; i < v . NumField ( ) ; i ++ {
2022-04-02 15:50:14 +02:00
if v . Type ( ) . Field ( i ) . Type . Kind ( ) == reflect . Struct {
result = getStructFieldNames ( v . Field ( i ) . Interface ( ) , result , v . Type ( ) . Field ( i ) . Name )
} else {
if len ( prefix ) > 0 {
result = append ( result , fmt . Sprintf ( "%s.%s" , prefix , v . Type ( ) . Field ( i ) . Name ) )
} else {
result = append ( result , v . Type ( ) . Field ( i ) . Name )
}
}
}
2022-04-24 16:49:54 +02:00
2022-04-02 15:50:14 +02:00
return result
}
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Recurse into structure , returning reflect . Value of wanted field .
* Nested fields are named with a dot ( ex "MyStruct.MyField" )
2022-04-18 13:36:33 +02:00
* Returns ( value , field_name , error )
2022-04-02 15:50:14 +02:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
func getStructFieldValue ( parentStruct interface { } , fieldName string ) ( * reflect . Value , string , error ) {
v := reflect . ValueOf ( parentStruct )
2022-04-24 16:49:54 +02:00
2022-04-02 21:15:06 +02:00
// Get value while we're dealing with pointers
2022-04-24 16:49:54 +02:00
for ; v . Kind ( ) == reflect . Ptr ; v = v . Elem ( ) {
}
2022-04-02 21:15:06 +02:00
if v . Kind ( ) != reflect . Struct {
return & v , fieldName , errors . New ( fmt . Sprintf ( "parentStruct is not a struct! Kind: %s" , v . Kind ( ) . String ( ) ) )
2022-04-02 17:12:51 +02:00
}
2022-04-24 16:49:54 +02:00
2022-04-02 21:15:06 +02:00
typeOfV := v . Type ( )
2022-04-02 17:12:51 +02:00
2022-04-02 15:50:14 +02:00
if false {
2022-04-24 16:49:54 +02:00
for i := 0 ; i < v . NumField ( ) ; i ++ {
2022-04-02 15:50:14 +02:00
f := v . Field ( i )
if f . Kind ( ) == reflect . String {
fmt . Printf ( "%v=%v\n" , v . Type ( ) . Field ( i ) . Name , f . Interface ( ) )
}
}
}
2022-04-24 16:49:54 +02:00
2022-04-02 21:38:54 +02:00
var f reflect . Value
var found bool
2022-04-24 16:49:54 +02:00
2022-04-02 21:38:54 +02:00
fs := strings . Split ( fieldName , "." )
// Loop through properties
for i , curF := range fs {
found = false
2022-04-24 16:49:54 +02:00
for j := 0 ; j < v . NumField ( ) ; j ++ {
2022-04-02 21:38:54 +02:00
if typeOfV . Field ( j ) . Name == curF {
f = v . Field ( j )
found = true
break
}
}
if ! found {
return & v , fieldName , errors . New ( fmt . Sprintf ( "Field not found: %s" , fieldName ) )
}
2022-04-24 16:49:54 +02:00
for ; f . Kind ( ) == reflect . Ptr ; f = f . Elem ( ) {
}
2022-04-02 21:38:54 +02:00
/ * fmt . Printf ( "v.kind() = %v\n" , v . Kind ( ) . String ( ) )
fmt . Printf ( "v = %v\n" , v )
fmt . Printf ( "f.kind() = %v\n" , f . Kind ( ) . String ( ) )
fmt . Printf ( "f = %v\n" , f ) * /
2022-04-24 16:49:54 +02:00
2022-04-02 21:38:54 +02:00
// If this is the last loop, return result even if it's a struct
// FIXME : What if we got interface?
if f . Kind ( ) != reflect . Struct && i < len ( fs ) - 1 {
if f . IsValid ( ) {
//fmt.Printf("Return f = %v of Kind %s\n", f, f.Kind().String())
return & f , fieldName , nil
2022-04-02 21:15:06 +02:00
} else {
2022-04-02 21:38:54 +02:00
return & v , fieldName , errors . New ( fmt . Sprintf ( "Field not found: %s" , fieldName ) )
2022-04-02 21:15:06 +02:00
}
2022-04-02 15:50:14 +02:00
} else {
2022-04-02 21:38:54 +02:00
v = f
typeOfV = v . Type ( )
2022-04-02 15:50:14 +02:00
}
}
2022-04-24 16:49:54 +02:00
2022-04-02 15:50:14 +02:00
return & v , fieldName , nil
}
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* TODO : Replace by getStructFieldValue
* Recurse into structure , returning reflect . Value of wanted field .
* Nested fields are named with a dot ( ex "MyStruct.MyField" )
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
func getStructField ( parentStruct interface { } , fieldName string ) ( reflect . Value , string ) {
v := reflect . ValueOf ( parentStruct )
2022-04-24 16:49:54 +02:00
2022-04-02 15:50:14 +02:00
if v . Kind ( ) == reflect . Ptr {
v = v . Elem ( )
}
2022-04-24 16:49:54 +02:00
2022-04-02 15:50:14 +02:00
if false {
2022-04-24 16:49:54 +02:00
for i := 0 ; i < v . NumField ( ) ; i ++ {
2022-04-02 15:50:14 +02:00
f := v . Field ( i )
if f . Kind ( ) == reflect . String {
fmt . Printf ( "%v=%v\n" , v . Type ( ) . Field ( i ) . Name , f . Interface ( ) )
}
}
}
2022-04-24 16:49:54 +02:00
2022-04-02 15:50:14 +02:00
if strings . Contains ( fieldName , "." ) {
fs := strings . Split ( fieldName , "." )
f := v . FieldByName ( fs [ 0 ] )
if f . Kind ( ) == reflect . Struct {
return getStructField ( f . Interface ( ) , strings . Join ( fs [ 1 : ] , "." ) )
} else {
log . Fatalln ( fmt . Sprintf ( "%s is not a struct: %s\n" , fs [ 0 ] , f . Kind ( ) . String ( ) ) )
}
}
2022-04-24 16:49:54 +02:00
2022-04-02 15:50:14 +02:00
return v , fieldName
}
2022-04-18 13:36:33 +02:00
// setStructFieldValue takes a string as propValue, whatever the real property type is.
// It will be converted.
func setStructFieldValue ( parentStruct interface { } , propName string , propValue string ) error {
val , _ , err := getStructFieldValue ( parentStruct , propName )
if err != nil {
return err
}
2022-04-24 16:49:54 +02:00
2022-04-18 13:36:33 +02:00
if val . CanSet ( ) {
switch val . Kind ( ) {
2022-04-24 16:49:54 +02:00
case reflect . String :
val . SetString ( propValue )
case reflect . Int :
ival , err := strconv . ParseInt ( propValue , 10 , 64 )
if err != nil {
return err
}
val . SetInt ( ival )
case reflect . Bool :
bval , err := strconv . ParseBool ( propValue )
if err != nil {
return err
}
val . SetBool ( bval )
default :
return errors . New ( fmt . Sprintf ( "Field is an unkown type: %s: %s" , propName , val . Kind ( ) . String ( ) ) )
2022-04-18 13:36:33 +02:00
}
} else {
return errors . New ( fmt . Sprintf ( "Field is not writable : %s" , propName ) )
}
2022-04-24 16:49:54 +02:00
2022-04-18 13:36:33 +02:00
return nil
}
2022-04-02 15:50:14 +02:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Pretty display of jails field
* Fields to show are given in a string array parameter
2022-06-18 16:09:22 +02:00
* Ex . : displayJailsFields ( gJails , [ "Name" , "JID" , "RootPath" ] )
2022-04-02 15:50:14 +02:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
2022-04-04 19:47:15 +02:00
func displayJailsFields ( jails [ ] Jail , valsToDisplay [ ] string ) {
2022-04-02 15:50:14 +02:00
/ * A line is defined by :
* Nr of fields
* For each field :
* Its name
* Its max length
* Its value
* /
type Field struct {
2022-04-24 16:49:54 +02:00
Name string
MaxLen int
Value string
2022-04-02 15:50:14 +02:00
}
2022-04-24 16:49:54 +02:00
2022-04-02 15:50:14 +02:00
type Line [ ] Field
2022-04-24 16:49:54 +02:00
2022-04-02 15:50:14 +02:00
type Output [ ] Line
var out Output
2022-04-24 16:49:54 +02:00
2022-04-02 15:50:14 +02:00
//fmt.Printf("%d fields in a %d items structure\n", len(valsToDisplay), len(jails))
2022-04-24 16:49:54 +02:00
2022-04-02 15:50:14 +02:00
// Browse structure to get max length and values
for _ , j := range jails {
// Have to use a pointer, else reflect.Value.Elem() will panic : https://pkg.go.dev/reflect#Value.Elem
tj := & j
2022-04-24 16:49:54 +02:00
2022-04-02 15:50:14 +02:00
line := make ( [ ] Field , len ( valsToDisplay ) )
for i , f := range valsToDisplay {
a , f := getStructField ( tj , f )
2022-04-24 16:49:54 +02:00
field := Field {
2022-04-02 15:50:14 +02:00
Name : f ,
}
if a . FieldByName ( f ) . IsValid ( ) {
// For now this just contains this item length, will adjust later
// We need to divide length by number of items in string fields (because we can have more than 1 ip4_addr)
if reflect . TypeOf ( a . FieldByName ( f ) . Interface ( ) ) . Kind ( ) == reflect . String {
itnr := len ( strings . Split ( string ( a . FieldByName ( f ) . Interface ( ) . ( string ) ) , "," ) )
field . MaxLen = len ( fmt . Sprintf ( "%v" , a . FieldByName ( f ) . Interface ( ) ) ) / itnr
} else {
field . MaxLen = len ( fmt . Sprintf ( "%v" , a . FieldByName ( f ) . Interface ( ) ) )
}
field . Value = fmt . Sprintf ( "%v" , a . FieldByName ( f ) . Interface ( ) )
} else {
fmt . Printf ( "Invalid field name: %s\n" , f )
}
line [ i ] = field
2022-04-24 16:49:54 +02:00
2022-04-02 15:50:14 +02:00
}
out = append ( out , line )
}
2022-04-24 16:49:54 +02:00
2022-04-02 15:50:14 +02:00
if len ( out ) == 0 {
fmt . Printf ( "Nothing to see here!\n" )
return
}
2022-04-24 16:49:54 +02:00
2022-04-02 15:50:14 +02:00
// Get real maximum length
maxlen := make ( [ ] int , len ( valsToDisplay ) )
2022-04-24 16:49:54 +02:00
for i := 0 ; i < len ( valsToDisplay ) ; i ++ {
2022-04-02 15:50:14 +02:00
maxlen [ i ] = len ( valsToDisplay [ i ] )
}
for _ , l := range out {
for i , f := range l {
if f . MaxLen > maxlen [ i ] {
maxlen [ i ] = f . MaxLen
}
}
}
2022-04-24 16:49:54 +02:00
2022-04-02 15:50:14 +02:00
// Align maxlen to the real maximum length
for io , l := range out {
for ii , _ := range l {
// We need to access slice by index if we want to modify content
out [ io ] [ ii ] . MaxLen = maxlen [ ii ]
}
}
2022-04-24 16:49:54 +02:00
2022-04-02 15:50:14 +02:00
totalLen := 0
2022-04-24 16:49:54 +02:00
// For each field, add separator and 2 blank spaces
2022-04-02 15:50:14 +02:00
for _ , f := range out [ 0 ] {
totalLen += f . MaxLen + 3
}
2022-04-24 16:49:54 +02:00
// Then add starting delimiter
2022-04-02 15:50:14 +02:00
totalLen += 1
2022-04-24 16:49:54 +02:00
2022-04-02 15:50:14 +02:00
Debug := false
if Debug == true {
for _ , f := range out [ 0 ] {
fmt . Printf ( "%s max length : %d\n" , f . Name , f . MaxLen )
}
}
2022-04-24 16:49:54 +02:00
2022-04-02 15:50:14 +02:00
// Lets draw things on the screen!
// First, headers: 1st separator line
for i , f := range out [ 0 ] {
2022-04-24 16:49:54 +02:00
if i == 0 {
fmt . Printf ( "+" )
}
for i := 0 ; i < f . MaxLen + 2 ; i ++ {
fmt . Printf ( "=" )
}
2022-04-02 15:50:14 +02:00
fmt . Printf ( "+" )
}
fmt . Printf ( "\n" )
2022-04-24 16:49:54 +02:00
2022-04-02 15:50:14 +02:00
// Column names
for i , f := range out [ 0 ] {
if i == 0 {
fmt . Printf ( "|" )
}
/ * Use vlsToDisplay to get real name ( with "Config." )
* fmt . Printf ( " %s" , f . Name )
* for i := len ( f . Name ) + 1 ; i < f . MaxLen + 1 ; i ++ { * /
fmt . Printf ( " %s" , valsToDisplay [ i ] )
2022-04-24 16:49:54 +02:00
for i := len ( valsToDisplay [ i ] ) + 1 ; i < f . MaxLen + 1 ; i ++ {
2022-04-02 15:50:14 +02:00
fmt . Printf ( " " )
}
fmt . Printf ( " |" )
}
// Finally separator line
fmt . Printf ( "\n" )
for i , f := range out [ 0 ] {
2022-04-24 16:49:54 +02:00
if i == 0 {
fmt . Printf ( "+" )
}
for i := 0 ; i < f . MaxLen + 2 ; i ++ {
fmt . Printf ( "=" )
}
2022-04-02 15:50:14 +02:00
fmt . Printf ( "+" )
}
fmt . Printf ( "\n" )
2022-04-24 16:49:54 +02:00
2022-04-02 15:50:14 +02:00
// Then display data
// Loop through lines
for _ , l := range out {
2022-04-24 16:49:54 +02:00
2022-04-02 15:50:14 +02:00
// Loop through fields
// In case we need to add a line for a 2nd IP, or whatever object
var supplines = make ( map [ string ] string )
for i , f := range l {
if i == 0 {
fmt . Printf ( "|" )
}
// Special cases of value displaying
if f . Name == "JID" && f . Value == "0" {
fmt . Printf ( " " )
} else if f . Name == "Ip4_addr" {
ia := strings . Split ( f . Value , "," )
// If we have more than 1 value we need to finish this line, and store value for writing at the end of line loop
for i , inter := range ia {
if i > 0 {
supplines [ f . Name ] = inter
} else {
fmt . Printf ( " %s" , inter )
}
}
//fmt.Printf(" %s", strings.Split(strings.Split(f.Value, "|")[1], "/")[0])
} else {
fmt . Printf ( " %s" , f . Value )
}
// Complete with spaces to the max length
2022-04-24 16:49:54 +02:00
for i := len ( f . Value ) + 1 ; i < f . MaxLen + 1 ; i ++ {
2022-04-02 15:50:14 +02:00
fmt . Printf ( " " )
}
fmt . Printf ( " |" )
}
// Draw supplementary lines
if len ( supplines ) > 0 {
for i , f := range l {
if i == 0 {
fmt . Printf ( "\n|" )
}
// Overwrite value in this scope
v , exists := supplines [ f . Name ]
if exists {
fmt . Printf ( " %s" , v )
} else {
// 1st option : Do not redisplay precedent line values
fmt . Printf ( " " )
// 2nd option : Redisplay precedenet line values
//fmt.Printf(" %s", f.Value)
}
// Complete with spaces to the max length
2022-04-24 16:49:54 +02:00
for i := len ( v ) + 1 ; i < f . MaxLen + 1 ; i ++ {
2022-04-02 15:50:14 +02:00
fmt . Printf ( " " )
}
fmt . Printf ( " |" )
}
}
// Draw line separator between jails
2022-06-18 16:09:22 +02:00
if ! gNoJailLineSep {
2022-04-02 15:50:14 +02:00
fmt . Printf ( "\n" )
for i , f := range out [ 0 ] {
2022-04-24 16:49:54 +02:00
if i == 0 {
fmt . Printf ( "+" )
}
for i := 0 ; i < f . MaxLen + 2 ; i ++ {
fmt . Printf ( "-" )
}
2022-04-02 15:50:14 +02:00
fmt . Printf ( "+" )
}
}
fmt . Printf ( "\n" )
}
2022-06-18 16:09:22 +02:00
if gNoJailLineSep {
2022-04-02 15:50:14 +02:00
for i , f := range out [ 0 ] {
2022-04-24 16:49:54 +02:00
if i == 0 {
fmt . Printf ( "+" )
}
for i := 0 ; i < f . MaxLen + 2 ; i ++ {
fmt . Printf ( "-" )
}
2022-04-02 15:50:14 +02:00
fmt . Printf ( "+" )
}
}
2022-04-24 16:49:54 +02:00
2022-04-02 15:50:14 +02:00
fmt . Printf ( "\n" )
}
2022-04-04 19:47:15 +02:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Pretty display of snapshots field
* Fields to show are given in a string array parameter
2022-06-18 18:24:09 +02:00
* Ex . : displaySnapshotsFields ( snapshots , [ "Name" , "Datastore" , "Used" ] )
2022-04-04 19:47:15 +02:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
func displaySnapshotsFields ( snaps [ ] Snapshot , valsToDisplay [ ] string ) {
/ * A line is defined by :
* Nr of fields
* For each field :
* Its name
* Its max length
* Its value
* /
type Field struct {
2022-04-24 16:49:54 +02:00
Name string
MaxLen int
Value string
2022-04-04 19:47:15 +02:00
}
2022-04-24 16:49:54 +02:00
2022-04-04 19:47:15 +02:00
type Line [ ] Field
2022-04-24 16:49:54 +02:00
2022-04-04 19:47:15 +02:00
type Output [ ] Line
var out Output
2022-04-24 16:49:54 +02:00
2022-04-04 19:47:15 +02:00
//fmt.Printf("%d fields in a %d items structure\n", len(valsToDisplay), len(jails))
2022-04-24 16:49:54 +02:00
2022-04-04 19:47:15 +02:00
// Browse structure to get max length and values
for _ , s := range snaps {
// Have to use a pointer, else reflect.Value.Elem() will panic : https://pkg.go.dev/reflect#Value.Elem
tj := & s
2022-04-24 16:49:54 +02:00
2022-04-04 19:47:15 +02:00
line := make ( [ ] Field , len ( valsToDisplay ) )
for i , f := range valsToDisplay {
a , f := getStructField ( tj , f )
2022-04-24 16:49:54 +02:00
field := Field {
2022-04-04 19:47:15 +02:00
Name : f ,
}
if a . FieldByName ( f ) . IsValid ( ) {
// For now this just contains this item length, will adjust later
// We need to divide length by number of items in string fields (because we can have more than 1 ip4_addr)
if reflect . TypeOf ( a . FieldByName ( f ) . Interface ( ) ) . Kind ( ) == reflect . String {
itnr := len ( strings . Split ( string ( a . FieldByName ( f ) . Interface ( ) . ( string ) ) , "," ) )
field . MaxLen = len ( fmt . Sprintf ( "%v" , a . FieldByName ( f ) . Interface ( ) ) ) / itnr
} else {
2022-06-18 18:24:09 +02:00
// Special case of disk size : We will print human readable values
if field . Name == "Used" || field . Name == "Referenced" || field . Name == "Available" {
var v datasize . ByteSize
v . UnmarshalText ( [ ] byte ( fmt . Sprintf ( "%v" , a . FieldByName ( f ) . Interface ( ) ) ) )
field . MaxLen = len ( fmt . Sprintf ( "%v" , v . HumanReadable ( ) ) )
} else {
field . MaxLen = len ( fmt . Sprintf ( "%v" , a . FieldByName ( f ) . Interface ( ) ) )
}
2022-04-04 19:47:15 +02:00
}
field . Value = fmt . Sprintf ( "%v" , a . FieldByName ( f ) . Interface ( ) )
} else {
fmt . Printf ( "Invalid field name: %s\n" , f )
}
line [ i ] = field
2022-04-24 16:49:54 +02:00
2022-04-04 19:47:15 +02:00
}
out = append ( out , line )
}
2022-04-24 16:49:54 +02:00
2022-04-04 19:47:15 +02:00
if len ( out ) == 0 {
fmt . Printf ( "Nothing to see here!\n" )
return
}
2022-04-24 16:49:54 +02:00
2022-04-04 19:47:15 +02:00
// Get real maximum length
maxlen := make ( [ ] int , len ( valsToDisplay ) )
2022-04-24 16:49:54 +02:00
for i := 0 ; i < len ( valsToDisplay ) ; i ++ {
2022-04-04 19:47:15 +02:00
maxlen [ i ] = len ( valsToDisplay [ i ] )
}
for _ , l := range out {
for i , f := range l {
if f . MaxLen > maxlen [ i ] {
maxlen [ i ] = f . MaxLen
}
}
}
2022-04-24 16:49:54 +02:00
2022-04-04 19:47:15 +02:00
// Align maxlen to the real maximum length
for io , l := range out {
for ii , _ := range l {
// We need to access slice by index if we want to modify content
out [ io ] [ ii ] . MaxLen = maxlen [ ii ]
}
}
2022-04-24 16:49:54 +02:00
2022-04-04 19:47:15 +02:00
totalLen := 0
2022-04-24 16:49:54 +02:00
// For each field, add separator and 2 blank spaces
2022-04-04 19:47:15 +02:00
for _ , f := range out [ 0 ] {
totalLen += f . MaxLen + 3
}
2022-04-24 16:49:54 +02:00
// Then add starting delimiter
2022-04-04 19:47:15 +02:00
totalLen += 1
2022-04-24 16:49:54 +02:00
2022-04-04 19:47:15 +02:00
Debug := false
if Debug == true {
for _ , f := range out [ 0 ] {
fmt . Printf ( "%s max length : %d\n" , f . Name , f . MaxLen )
}
}
2022-04-24 16:49:54 +02:00
2022-04-04 19:47:15 +02:00
// Lets draw things on the screen!
// First, headers: 1st separator line
for i , f := range out [ 0 ] {
2022-04-24 16:49:54 +02:00
if i == 0 {
fmt . Printf ( "+" )
}
for i := 0 ; i < f . MaxLen + 2 ; i ++ {
fmt . Printf ( "=" )
}
2022-04-04 19:47:15 +02:00
fmt . Printf ( "+" )
}
fmt . Printf ( "\n" )
2022-04-24 16:49:54 +02:00
2022-04-04 19:47:15 +02:00
// Column names
for i , f := range out [ 0 ] {
if i == 0 {
fmt . Printf ( "|" )
}
/ * Use vlsToDisplay to get real name ( with "Config." )
* fmt . Printf ( " %s" , f . Name )
* for i := len ( f . Name ) + 1 ; i < f . MaxLen + 1 ; i ++ { * /
fmt . Printf ( " %s" , valsToDisplay [ i ] )
2022-04-24 16:49:54 +02:00
for i := len ( valsToDisplay [ i ] ) + 1 ; i < f . MaxLen + 1 ; i ++ {
2022-04-04 19:47:15 +02:00
fmt . Printf ( " " )
}
fmt . Printf ( " |" )
}
// Finally separator line
fmt . Printf ( "\n" )
for i , f := range out [ 0 ] {
2022-04-24 16:49:54 +02:00
if i == 0 {
fmt . Printf ( "+" )
}
for i := 0 ; i < f . MaxLen + 2 ; i ++ {
fmt . Printf ( "=" )
}
2022-04-04 19:47:15 +02:00
fmt . Printf ( "+" )
}
fmt . Printf ( "\n" )
2022-04-24 16:49:54 +02:00
2022-04-04 19:47:15 +02:00
// Then display data
// Loop through lines
for _ , l := range out {
2022-04-24 16:49:54 +02:00
2022-04-04 19:47:15 +02:00
// Loop through fields
// In case we need to add a line for a 2nd IP, or whatever object
var supplines = make ( map [ string ] string )
for i , f := range l {
if i == 0 {
fmt . Printf ( "|" )
}
// Special cases of value displaying
2022-06-18 18:24:09 +02:00
// Pretty print of sizes
if f . Name == "Used" || f . Name == "Referenced" || f . Name == "Available" {
var v datasize . ByteSize
err := v . UnmarshalText ( [ ] byte ( f . Value ) )
if err != nil {
return
}
fmt . Printf ( " %s" , v . HumanReadable ( ) )
// Complete with spaces to the max length
for i := len ( v . HumanReadable ( ) ) + 1 ; i < f . MaxLen + 1 ; i ++ {
fmt . Printf ( " " )
}
} else {
fmt . Printf ( " %s" , f . Value )
// Complete with spaces to the max length
for i := len ( f . Value ) + 1 ; i < f . MaxLen + 1 ; i ++ {
fmt . Printf ( " " )
}
2022-04-04 19:47:15 +02:00
}
fmt . Printf ( " |" )
}
// Draw supplementary lines
if len ( supplines ) > 0 {
for i , f := range l {
if i == 0 {
fmt . Printf ( "\n|" )
}
// Overwrite value in this scope
v , exists := supplines [ f . Name ]
if exists {
fmt . Printf ( " %s" , v )
} else {
// 1st option : Do not redisplay precedent line values
fmt . Printf ( " " )
// 2nd option : Redisplay precedenet line values
//fmt.Printf(" %s", f.Value)
}
// Complete with spaces to the max length
2022-04-24 16:49:54 +02:00
for i := len ( v ) + 1 ; i < f . MaxLen + 1 ; i ++ {
2022-04-04 19:47:15 +02:00
fmt . Printf ( " " )
}
fmt . Printf ( " |" )
}
}
// Draw line separator between jails
2022-06-18 16:09:22 +02:00
if ! gNoSnapLineSep {
2022-04-04 19:47:15 +02:00
fmt . Printf ( "\n" )
for i , f := range out [ 0 ] {
2022-04-24 16:49:54 +02:00
if i == 0 {
fmt . Printf ( "+" )
}
for i := 0 ; i < f . MaxLen + 2 ; i ++ {
fmt . Printf ( "-" )
}
2022-04-04 19:47:15 +02:00
fmt . Printf ( "+" )
}
}
fmt . Printf ( "\n" )
}
2022-06-18 16:09:22 +02:00
if gNoSnapLineSep {
for i , f := range out [ 0 ] {
if i == 0 {
fmt . Printf ( "+" )
}
for i := 0 ; i < f . MaxLen + 2 ; i ++ {
fmt . Printf ( "-" )
}
fmt . Printf ( "+" )
}
}
fmt . Printf ( "\n" )
}
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Pretty display of datastores field
* Fields to show are given in a string array parameter
* Ex . : displaySnapshotsFields ( datastores , [ "Name" , "ZFSDataset" , "Available" ] )
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
func displayDatastoresFields ( datastores [ ] Datastore , valsToDisplay [ ] string ) {
/ * A line is defined by :
* Nr of fields
* For each field :
* Its name
* Its max length
* Its value
* /
type Field struct {
Name string
MaxLen int
Value string
}
type Line [ ] Field
type Output [ ] Line
var out Output
//fmt.Printf("%d fields in a %d items structure\n", len(valsToDisplay), len(jails))
// Browse structure to get max length and values
for _ , d := range datastores {
// Have to use a pointer, else reflect.Value.Elem() will panic : https://pkg.go.dev/reflect#Value.Elem
td := & d
line := make ( [ ] Field , len ( valsToDisplay ) )
for i , f := range valsToDisplay {
a , f := getStructField ( td , f )
field := Field {
Name : f ,
}
if a . FieldByName ( f ) . IsValid ( ) {
// For now this just contains this item length, will adjust later
// We need to divide length by number of items in string fields (because we can have more than 1 ip4_addr)
if reflect . TypeOf ( a . FieldByName ( f ) . Interface ( ) ) . Kind ( ) == reflect . String {
itnr := len ( strings . Split ( string ( a . FieldByName ( f ) . Interface ( ) . ( string ) ) , "," ) )
field . MaxLen = len ( fmt . Sprintf ( "%v" , a . FieldByName ( f ) . Interface ( ) ) ) / itnr
} else {
// Special case of disk size : We will print human readable values
if field . Name == "Used" || field . Name == "Referenced" || field . Name == "Available" {
var v datasize . ByteSize
v . UnmarshalText ( [ ] byte ( fmt . Sprintf ( "%v" , a . FieldByName ( f ) . Interface ( ) ) ) )
field . MaxLen = len ( fmt . Sprintf ( "%v" , v . HumanReadable ( ) ) )
} else {
field . MaxLen = len ( fmt . Sprintf ( "%v" , a . FieldByName ( f ) . Interface ( ) ) )
}
}
field . Value = fmt . Sprintf ( "%v" , a . FieldByName ( f ) . Interface ( ) )
} else {
fmt . Printf ( "Invalid field name: %s\n" , f )
}
line [ i ] = field
}
out = append ( out , line )
}
if len ( out ) == 0 {
fmt . Printf ( "Nothing to see here!\n" )
return
}
// Get real maximum length
maxlen := make ( [ ] int , len ( valsToDisplay ) )
for i := 0 ; i < len ( valsToDisplay ) ; i ++ {
maxlen [ i ] = len ( valsToDisplay [ i ] )
}
for _ , l := range out {
for i , f := range l {
if f . MaxLen > maxlen [ i ] {
maxlen [ i ] = f . MaxLen
}
}
}
// Align maxlen to the real maximum length
for io , l := range out {
for ii , _ := range l {
// We need to access slice by index if we want to modify content
out [ io ] [ ii ] . MaxLen = maxlen [ ii ]
}
}
totalLen := 0
// For each field, add separator and 2 blank spaces
for _ , f := range out [ 0 ] {
totalLen += f . MaxLen + 3
}
// Then add starting delimiter
totalLen += 1
Debug := false
if Debug == true {
for _ , f := range out [ 0 ] {
fmt . Printf ( "%s max length : %d\n" , f . Name , f . MaxLen )
}
}
// Lets draw things on the screen!
// First, headers: 1st separator line
for i , f := range out [ 0 ] {
if i == 0 {
fmt . Printf ( "+" )
}
for i := 0 ; i < f . MaxLen + 2 ; i ++ {
fmt . Printf ( "=" )
}
fmt . Printf ( "+" )
}
fmt . Printf ( "\n" )
// Column names
for i , f := range out [ 0 ] {
if i == 0 {
fmt . Printf ( "|" )
}
/ * Use vlsToDisplay to get real name ( with "Config." )
* fmt . Printf ( " %s" , f . Name )
* for i := len ( f . Name ) + 1 ; i < f . MaxLen + 1 ; i ++ { * /
fmt . Printf ( " %s" , valsToDisplay [ i ] )
for i := len ( valsToDisplay [ i ] ) + 1 ; i < f . MaxLen + 1 ; i ++ {
fmt . Printf ( " " )
}
fmt . Printf ( " |" )
}
// Finally separator line
fmt . Printf ( "\n" )
for i , f := range out [ 0 ] {
if i == 0 {
fmt . Printf ( "+" )
}
for i := 0 ; i < f . MaxLen + 2 ; i ++ {
fmt . Printf ( "=" )
}
fmt . Printf ( "+" )
}
fmt . Printf ( "\n" )
// Then display data
// Loop through lines
for _ , l := range out {
// Loop through fields
// In case we need to add a line for a 2nd IP, or whatever object
var supplines = make ( map [ string ] string )
for i , f := range l {
if i == 0 {
fmt . Printf ( "|" )
}
// Special cases of value displaying
// Pretty print of sizes
if f . Name == "Used" || f . Name == "Referenced" || f . Name == "Available" {
var v datasize . ByteSize
err := v . UnmarshalText ( [ ] byte ( f . Value ) )
if err != nil {
return
}
fmt . Printf ( " %s" , v . HumanReadable ( ) )
// Complete with spaces to the max length
for i := len ( v . HumanReadable ( ) ) + 1 ; i < f . MaxLen + 1 ; i ++ {
fmt . Printf ( " " )
}
} else {
fmt . Printf ( " %s" , f . Value )
// Complete with spaces to the max length
for i := len ( f . Value ) + 1 ; i < f . MaxLen + 1 ; i ++ {
fmt . Printf ( " " )
}
}
fmt . Printf ( " |" )
}
// Draw supplementary lines
if len ( supplines ) > 0 {
for i , f := range l {
if i == 0 {
fmt . Printf ( "\n|" )
}
// Overwrite value in this scope
v , exists := supplines [ f . Name ]
if exists {
fmt . Printf ( " %s" , v )
} else {
// 1st option : Do not redisplay precedent line values
fmt . Printf ( " " )
// 2nd option : Redisplay precedenet line values
//fmt.Printf(" %s", f.Value)
}
// Complete with spaces to the max length
for i := len ( v ) + 1 ; i < f . MaxLen + 1 ; i ++ {
fmt . Printf ( " " )
}
fmt . Printf ( " |" )
}
}
// Draw line separator between jails
if ! gNoDSLineSep {
fmt . Printf ( "\n" )
for i , f := range out [ 0 ] {
if i == 0 {
fmt . Printf ( "+" )
}
for i := 0 ; i < f . MaxLen + 2 ; i ++ {
fmt . Printf ( "-" )
}
fmt . Printf ( "+" )
}
}
fmt . Printf ( "\n" )
}
if gNoDSLineSep {
2022-04-04 19:47:15 +02:00
for i , f := range out [ 0 ] {
2022-04-24 16:49:54 +02:00
if i == 0 {
fmt . Printf ( "+" )
}
for i := 0 ; i < f . MaxLen + 2 ; i ++ {
fmt . Printf ( "-" )
}
2022-04-04 19:47:15 +02:00
fmt . Printf ( "+" )
}
}
2022-04-24 16:49:54 +02:00
2022-04-04 19:47:15 +02:00
fmt . Printf ( "\n" )
}
2022-04-02 14:17:10 +02:00
2021-12-20 22:10:38 +01:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Sorting jails
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
2022-04-03 14:27:26 +02:00
// This struct hold "sort by jail fields" functions
type jailLessFunc func ( j1 * Jail , j2 * Jail ) bool
func initJailSortStruct ( ) JailSort {
2021-12-20 22:10:38 +01:00
jcs := JailConfigSort {
Allow_chflagsInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_chflags < j2 . Config . Allow_chflags
} ,
Allow_chflagsDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_chflags > j2 . Config . Allow_chflags
} ,
Allow_mlockInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_mlock < j2 . Config . Allow_mlock
} ,
Allow_mlockDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_mlock > j2 . Config . Allow_mlock
} ,
Allow_mountInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_mount < j2 . Config . Allow_mount
} ,
Allow_mountDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_mount > j2 . Config . Allow_mount
} ,
Allow_mount_devfsInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_mount_devfs < j2 . Config . Allow_mount_devfs
} ,
Allow_mount_devfsDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_mount_devfs > j2 . Config . Allow_mount_devfs
} ,
Allow_mount_fusefsInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_mount_fusefs < j2 . Config . Allow_mount_fusefs
} ,
Allow_mount_fusefsDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_mount_fusefs > j2 . Config . Allow_mount_fusefs
} ,
Allow_mount_nullfsInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_mount_nullfs < j2 . Config . Allow_mount_nullfs
} ,
Allow_mount_nullfsDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_mount_nullfs > j2 . Config . Allow_mount_nullfs
} ,
Allow_mount_procfsInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_mount_procfs < j2 . Config . Allow_mount_procfs
} ,
Allow_mount_procfsDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_mount_procfs > j2 . Config . Allow_mount_procfs
} ,
Allow_mount_tmpfsInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_mount_tmpfs < j2 . Config . Allow_mount_tmpfs
} ,
Allow_mount_tmpfsDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_mount_tmpfs > j2 . Config . Allow_mount_tmpfs
} ,
Allow_mount_zfsInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_mount_zfs < j2 . Config . Allow_mount_zfs
} ,
Allow_mount_zfsDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_mount_zfs > j2 . Config . Allow_mount_zfs
} ,
Allow_quotasInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_quotas < j2 . Config . Allow_quotas
} ,
Allow_quotasDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_quotas > j2 . Config . Allow_quotas
} ,
Allow_raw_socketsInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_raw_sockets < j2 . Config . Allow_raw_sockets
} ,
Allow_raw_socketsDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_raw_sockets > j2 . Config . Allow_raw_sockets
} ,
Allow_set_hostnameInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_set_hostname < j2 . Config . Allow_set_hostname
} ,
Allow_set_hostnameDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_set_hostname > j2 . Config . Allow_set_hostname
} ,
Allow_socket_afInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_socket_af < j2 . Config . Allow_socket_af
} ,
Allow_socket_afDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_socket_af > j2 . Config . Allow_socket_af
} ,
Allow_sysvipcInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_sysvipc < j2 . Config . Allow_sysvipc
} ,
Allow_sysvipcDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_sysvipc > j2 . Config . Allow_sysvipc
} ,
Allow_tunInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_tun < j2 . Config . Allow_tun
} ,
Allow_tunDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_tun > j2 . Config . Allow_tun
} ,
Allow_vmmInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_vmm < j2 . Config . Allow_vmm
} ,
Allow_vmmDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Allow_vmm > j2 . Config . Allow_vmm
} ,
Assign_localhostInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Assign_localhost < j2 . Config . Assign_localhost
} ,
Assign_localhostDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Assign_localhost > j2 . Config . Assign_localhost
} ,
AvailableInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Available < j2 . Config . Available
} ,
AvailableDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Available > j2 . Config . Available
} ,
BasejailInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Basejail < j2 . Config . Basejail
} ,
BasejailDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Basejail > j2 . Config . Basejail
} ,
BootInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Boot < j2 . Config . Boot
} ,
BootDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Boot > j2 . Config . Boot
} ,
BpfInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Bpf < j2 . Config . Bpf
} ,
BpfDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Bpf > j2 . Config . Bpf
} ,
Children_maxInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Children_max < j2 . Config . Children_max
} ,
Children_maxDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Children_max > j2 . Config . Children_max
} ,
Cloned_releaseInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Cloned_release < j2 . Config . Cloned_release
} ,
Cloned_releaseDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Cloned_release > j2 . Config . Cloned_release
} ,
CommentInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Comment < j2 . Config . Comment
} ,
CommentDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Comment > j2 . Config . Comment
} ,
CompressionInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Compression < j2 . Config . Compression
} ,
CompressionDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Compression > j2 . Config . Compression
} ,
CompressratioInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Compressratio < j2 . Config . Compressratio
} ,
CompressratioDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Compressratio > j2 . Config . Compressratio
} ,
Config_versionInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Config_version < j2 . Config . Config_version
} ,
Config_versionDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Config_version > j2 . Config . Config_version
} ,
CoredumpsizeInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Coredumpsize < j2 . Config . Coredumpsize
} ,
CoredumpsizeDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Coredumpsize > j2 . Config . Coredumpsize
} ,
CountInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Count < j2 . Config . Count
} ,
CountDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Count > j2 . Config . Count
} ,
CpusetInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Cpuset < j2 . Config . Cpuset
} ,
CpusetDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Cpuset > j2 . Config . Cpuset
} ,
CputimeInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Cputime < j2 . Config . Cputime
} ,
CputimeDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Cputime > j2 . Config . Cputime
} ,
DatasizeInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Datasize < j2 . Config . Datasize
} ,
DatasizeDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Datasize > j2 . Config . Datasize
} ,
DedupInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Dedup < j2 . Config . Dedup
} ,
DedupDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Dedup > j2 . Config . Dedup
} ,
DefaultrouterInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Defaultrouter < j2 . Config . Defaultrouter
} ,
DefaultrouterDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Defaultrouter > j2 . Config . Defaultrouter
} ,
Defaultrouter6Inc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Defaultrouter6 < j2 . Config . Defaultrouter6
} ,
Defaultrouter6Dec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Defaultrouter6 > j2 . Config . Defaultrouter6
} ,
DependsInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Depends < j2 . Config . Depends
} ,
DependsDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Depends > j2 . Config . Depends
} ,
Devfs_rulesetInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Devfs_ruleset < j2 . Config . Devfs_ruleset
} ,
Devfs_rulesetDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Devfs_ruleset > j2 . Config . Devfs_ruleset
} ,
DhcpInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Dhcp < j2 . Config . Dhcp
} ,
DhcpDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Dhcp > j2 . Config . Dhcp
} ,
Enforce_statfsInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Enforce_statfs < j2 . Config . Enforce_statfs
} ,
Enforce_statfsDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Enforce_statfs > j2 . Config . Enforce_statfs
} ,
Exec_cleanInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Exec_clean < j2 . Config . Exec_clean
} ,
Exec_cleanDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Exec_clean > j2 . Config . Exec_clean
} ,
Exec_createdInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Exec_created < j2 . Config . Exec_created
} ,
Exec_createdDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Exec_created > j2 . Config . Exec_created
} ,
Exec_fibInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Exec_fib < j2 . Config . Exec_fib
} ,
Exec_fibDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Exec_fib > j2 . Config . Exec_fib
} ,
Exec_jail_userInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Exec_jail_user < j2 . Config . Exec_jail_user
} ,
Exec_jail_userDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Exec_jail_user > j2 . Config . Exec_jail_user
} ,
Exec_poststartInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Exec_poststart < j2 . Config . Exec_poststart
} ,
Exec_poststartDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Exec_poststart > j2 . Config . Exec_poststart
} ,
Exec_poststopInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Exec_poststop < j2 . Config . Exec_poststop
} ,
Exec_poststopDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Exec_poststop > j2 . Config . Exec_poststop
} ,
Exec_prestartInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Exec_prestart < j2 . Config . Exec_prestart
} ,
Exec_prestartDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Exec_prestart > j2 . Config . Exec_prestart
} ,
Exec_prestopInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Exec_prestop < j2 . Config . Exec_prestop
} ,
Exec_prestopDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Exec_prestop > j2 . Config . Exec_prestop
} ,
Exec_startInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Exec_start < j2 . Config . Exec_start
} ,
Exec_startDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Exec_start > j2 . Config . Exec_start
} ,
Exec_stopInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Exec_stop < j2 . Config . Exec_stop
} ,
Exec_stopDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Exec_stop > j2 . Config . Exec_stop
} ,
Exec_system_jail_userInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Exec_system_jail_user < j2 . Config . Exec_system_jail_user
} ,
Exec_system_jail_userDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Exec_system_jail_user > j2 . Config . Exec_system_jail_user
} ,
Exec_system_userInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Exec_system_user < j2 . Config . Exec_system_user
} ,
Exec_system_userDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Exec_system_user > j2 . Config . Exec_system_user
} ,
Exec_timeoutInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Exec_timeout < j2 . Config . Exec_timeout
} ,
Exec_timeoutDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Exec_timeout > j2 . Config . Exec_timeout
} ,
Host_domainnameInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Host_domainname < j2 . Config . Host_domainname
} ,
Host_domainnameDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Host_domainname > j2 . Config . Host_domainname
} ,
Host_hostnameInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Host_hostname < j2 . Config . Host_hostname
} ,
Host_hostnameDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Host_hostname > j2 . Config . Host_hostname
} ,
Host_hostuuidInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Host_hostuuid < j2 . Config . Host_hostuuid
} ,
Host_hostuuidDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Host_hostuuid > j2 . Config . Host_hostuuid
} ,
Host_timeInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Host_time < j2 . Config . Host_time
} ,
Host_timeDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Host_time > j2 . Config . Host_time
} ,
HostidInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Hostid < j2 . Config . Hostid
} ,
HostidDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Hostid > j2 . Config . Hostid
} ,
Hostid_strict_checkInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Hostid_strict_check < j2 . Config . Hostid_strict_check
} ,
Hostid_strict_checkDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Hostid_strict_check > j2 . Config . Hostid_strict_check
} ,
InterfacesInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Interfaces < j2 . Config . Interfaces
} ,
InterfacesDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Interfaces > j2 . Config . Interfaces
} ,
Ip_hostnameInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Ip_hostname < j2 . Config . Ip_hostname
} ,
Ip_hostnameDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Ip_hostname > j2 . Config . Ip_hostname
} ,
Ip4Inc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Ip4 < j2 . Config . Ip4
} ,
Ip4Dec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Ip4 > j2 . Config . Ip4
} ,
Ip4_addrInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Ip4_addr < j2 . Config . Ip4_addr
} ,
Ip4_addrDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Ip4_addr > j2 . Config . Ip4_addr
} ,
Ip4_saddrselInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Ip4_saddrsel < j2 . Config . Ip4_saddrsel
} ,
Ip4_saddrselDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Ip4_saddrsel > j2 . Config . Ip4_saddrsel
} ,
Ip6Inc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Ip6 < j2 . Config . Ip6
} ,
Ip6Dec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Ip6 > j2 . Config . Ip6
} ,
Ip6_addrInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Ip6_addr < j2 . Config . Ip6_addr
} ,
Ip6_addrDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Ip6_addr > j2 . Config . Ip6_addr
} ,
Ip6_saddrselInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Ip6_saddrsel < j2 . Config . Ip6_saddrsel
} ,
Ip6_saddrselDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Ip6_saddrsel > j2 . Config . Ip6_saddrsel
} ,
Jail_zfsInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Jail_zfs < j2 . Config . Jail_zfs
} ,
Jail_zfsDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Jail_zfs > j2 . Config . Jail_zfs
} ,
Jail_zfs_datasetInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Jail_zfs_dataset < j2 . Config . Jail_zfs_dataset
} ,
Jail_zfs_datasetDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Jail_zfs_dataset > j2 . Config . Jail_zfs_dataset
} ,
Jail_zfs_mountpointInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Jail_zfs_mountpoint < j2 . Config . Jail_zfs_mountpoint
} ,
Jail_zfs_mountpointDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Jail_zfs_mountpoint > j2 . Config . Jail_zfs_mountpoint
} ,
JailtypeInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Jailtype < j2 . Config . Jailtype
} ,
JailtypeDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Jailtype > j2 . Config . Jailtype
} ,
Last_startedInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Last_started < j2 . Config . Last_started
} ,
Last_startedDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Last_started > j2 . Config . Last_started
} ,
Localhost_ipInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Localhost_ip < j2 . Config . Localhost_ip
} ,
Localhost_ipDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Localhost_ip > j2 . Config . Localhost_ip
} ,
Login_flagsInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Login_flags < j2 . Config . Login_flags
} ,
Login_flagsDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Login_flags > j2 . Config . Login_flags
} ,
Mac_prefixInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Mac_prefix < j2 . Config . Mac_prefix
} ,
Mac_prefixDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Mac_prefix > j2 . Config . Mac_prefix
} ,
MaxprocInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Maxproc < j2 . Config . Maxproc
} ,
MaxprocDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Maxproc > j2 . Config . Maxproc
} ,
MemorylockedInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Memorylocked < j2 . Config . Memorylocked
} ,
MemorylockedDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Memorylocked > j2 . Config . Memorylocked
} ,
MemoryuseInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Memoryuse < j2 . Config . Memoryuse
} ,
MemoryuseDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Memoryuse > j2 . Config . Memoryuse
} ,
Min_dyn_devfs_rulesetInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Min_dyn_devfs_ruleset < j2 . Config . Min_dyn_devfs_ruleset
} ,
Min_dyn_devfs_rulesetDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Min_dyn_devfs_ruleset > j2 . Config . Min_dyn_devfs_ruleset
} ,
Mount_devfsInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Mount_devfs < j2 . Config . Mount_devfs
} ,
Mount_devfsDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Mount_devfs > j2 . Config . Mount_devfs
} ,
Mount_fdescfsInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Mount_fdescfs < j2 . Config . Mount_fdescfs
} ,
Mount_fdescfsDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Mount_fdescfs > j2 . Config . Mount_fdescfs
} ,
Mount_linprocfsInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Mount_linprocfs < j2 . Config . Mount_linprocfs
} ,
Mount_linprocfsDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Mount_linprocfs > j2 . Config . Mount_linprocfs
} ,
Mount_procfsInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Mount_procfs < j2 . Config . Mount_procfs
} ,
Mount_procfsDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Mount_procfs > j2 . Config . Mount_procfs
} ,
MountpointInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Mountpoint < j2 . Config . Mountpoint
} ,
MountpointDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Mountpoint > j2 . Config . Mountpoint
} ,
MsgqqueuedInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Msgqqueued < j2 . Config . Msgqqueued
} ,
MsgqqueuedDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Msgqqueued > j2 . Config . Msgqqueued
} ,
MsgqsizeInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Msgqsize < j2 . Config . Msgqsize
} ,
MsgqsizeDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Msgqsize > j2 . Config . Msgqsize
} ,
NatInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Nat < j2 . Config . Nat
} ,
NatDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Nat > j2 . Config . Nat
} ,
Nat_backendInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Nat_backend < j2 . Config . Nat_backend
} ,
Nat_backendDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Nat_backend > j2 . Config . Nat_backend
} ,
Nat_forwardsInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Nat_forwards < j2 . Config . Nat_forwards
} ,
Nat_forwardsDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Nat_forwards > j2 . Config . Nat_forwards
} ,
Nat_interfaceInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Nat_interface < j2 . Config . Nat_interface
} ,
Nat_interfaceDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Nat_interface > j2 . Config . Nat_interface
} ,
Nat_prefixInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Nat_prefix < j2 . Config . Nat_prefix
} ,
Nat_prefixDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Nat_prefix > j2 . Config . Nat_prefix
} ,
NmsgqInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Nmsgq < j2 . Config . Nmsgq
} ,
NmsgqDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Nmsgq > j2 . Config . Nmsgq
} ,
NotesInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Notes < j2 . Config . Notes
} ,
NotesDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Notes > j2 . Config . Notes
} ,
NsemInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Nsem < j2 . Config . Nsem
} ,
NsemDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Nsem > j2 . Config . Nsem
} ,
NsemopInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Nsemop < j2 . Config . Nsemop
} ,
NsemopDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Nsemop > j2 . Config . Nsemop
} ,
NshmInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Nshm < j2 . Config . Nshm
} ,
NshmDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Nshm > j2 . Config . Nshm
} ,
NthrInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Nthr < j2 . Config . Nthr
} ,
NthrDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Nthr > j2 . Config . Nthr
} ,
OpenfilesInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Openfiles < j2 . Config . Openfiles
} ,
OpenfilesDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Openfiles > j2 . Config . Openfiles
} ,
OriginInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Origin < j2 . Config . Origin
} ,
OriginDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Origin > j2 . Config . Origin
} ,
OwnerInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Owner < j2 . Config . Owner
} ,
OwnerDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Owner > j2 . Config . Owner
} ,
PcpuInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Pcpu < j2 . Config . Pcpu
} ,
PcpuDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Pcpu > j2 . Config . Pcpu
} ,
Plugin_nameInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Plugin_name < j2 . Config . Plugin_name
} ,
Plugin_nameDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Plugin_name > j2 . Config . Plugin_name
} ,
Plugin_repositoryInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Plugin_repository < j2 . Config . Plugin_repository
} ,
Plugin_repositoryDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Plugin_repository > j2 . Config . Plugin_repository
} ,
PriorityInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Priority < j2 . Config . Priority
} ,
PriorityDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Priority > j2 . Config . Priority
} ,
PseudoterminalsInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Pseudoterminals < j2 . Config . Pseudoterminals
} ,
PseudoterminalsDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Pseudoterminals > j2 . Config . Pseudoterminals
} ,
QuotaInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Quota < j2 . Config . Quota
} ,
QuotaDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Quota > j2 . Config . Quota
} ,
ReadbpsInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Readbps < j2 . Config . Readbps
} ,
ReadbpsDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Readbps > j2 . Config . Readbps
} ,
ReadiopsInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Readiops < j2 . Config . Readiops
} ,
ReadiopsDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Readiops > j2 . Config . Readiops
} ,
ReleaseInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Release < j2 . Config . Release
} ,
ReleaseDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Release > j2 . Config . Release
} ,
ReservationInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Reservation < j2 . Config . Reservation
} ,
ReservationDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Reservation > j2 . Config . Reservation
} ,
ResolverInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Resolver < j2 . Config . Resolver
} ,
ResolverDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Resolver > j2 . Config . Resolver
} ,
RlimitsInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Rlimits < j2 . Config . Rlimits
} ,
RlimitsDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Rlimits > j2 . Config . Rlimits
} ,
RtsoldInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Rtsold < j2 . Config . Rtsold
} ,
RtsoldDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Rtsold > j2 . Config . Rtsold
} ,
SecurelevelInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Securelevel < j2 . Config . Securelevel
} ,
SecurelevelDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Securelevel > j2 . Config . Securelevel
} ,
ShmsizeInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Shmsize < j2 . Config . Shmsize
} ,
ShmsizeDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Shmsize > j2 . Config . Shmsize
} ,
StacksizeInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Stacksize < j2 . Config . Stacksize
} ,
StacksizeDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Stacksize > j2 . Config . Stacksize
} ,
Stop_timeoutInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Stop_timeout < j2 . Config . Stop_timeout
} ,
Stop_timeoutDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Stop_timeout > j2 . Config . Stop_timeout
} ,
SwapuseInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Swapuse < j2 . Config . Swapuse
} ,
SwapuseDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Swapuse > j2 . Config . Swapuse
} ,
Sync_stateInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Sync_state < j2 . Config . Sync_state
} ,
Sync_stateDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Sync_state > j2 . Config . Sync_state
} ,
Sync_targetInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Sync_target < j2 . Config . Sync_target
} ,
Sync_targetDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Sync_target > j2 . Config . Sync_target
} ,
Sync_tgt_zpoolInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Sync_tgt_zpool < j2 . Config . Sync_tgt_zpool
} ,
Sync_tgt_zpoolDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Sync_tgt_zpool > j2 . Config . Sync_tgt_zpool
} ,
SysvmsgInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Sysvmsg < j2 . Config . Sysvmsg
} ,
SysvmsgDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Sysvmsg > j2 . Config . Sysvmsg
} ,
SysvsemInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Sysvsem < j2 . Config . Sysvsem
} ,
SysvsemDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Sysvsem > j2 . Config . Sysvsem
} ,
SysvshmInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Sysvshm < j2 . Config . Sysvshm
} ,
SysvshmDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Sysvshm > j2 . Config . Sysvshm
} ,
TemplateInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Template < j2 . Config . Template
} ,
TemplateDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Template > j2 . Config . Template
} ,
UsedInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Used < j2 . Config . Used
} ,
UsedDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Used > j2 . Config . Used
} ,
VmemoryuseInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Vmemoryuse < j2 . Config . Vmemoryuse
} ,
VmemoryuseDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Vmemoryuse > j2 . Config . Vmemoryuse
} ,
VnetInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Vnet < j2 . Config . Vnet
} ,
VnetDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Vnet > j2 . Config . Vnet
} ,
Vnet_default_interfaceInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Vnet_default_interface < j2 . Config . Vnet_default_interface
} ,
Vnet_default_interfaceDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Vnet_default_interface > j2 . Config . Vnet_default_interface
} ,
Vnet_interfacesInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Vnet_interfaces < j2 . Config . Vnet_interfaces
} ,
Vnet_interfacesDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Vnet_interfaces > j2 . Config . Vnet_interfaces
} ,
Vnet0_macInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Vnet0_mac < j2 . Config . Vnet0_mac
} ,
Vnet0_macDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Vnet0_mac > j2 . Config . Vnet0_mac
} ,
Vnet1_macInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Vnet1_mac < j2 . Config . Vnet1_mac
} ,
Vnet1_macDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Vnet1_mac > j2 . Config . Vnet1_mac
} ,
Vnet2_macInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Vnet2_mac < j2 . Config . Vnet2_mac
} ,
Vnet2_macDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Vnet2_mac > j2 . Config . Vnet2_mac
} ,
Vnet3_macInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Vnet3_mac < j2 . Config . Vnet3_mac
} ,
Vnet3_macDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Vnet3_mac > j2 . Config . Vnet3_mac
} ,
WallclockInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Wallclock < j2 . Config . Wallclock
} ,
WallclockDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Wallclock > j2 . Config . Wallclock
} ,
WritebpsInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Writebps < j2 . Config . Writebps
} ,
WritebpsDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Writebps > j2 . Config . Writebps
} ,
WriteiopsInc : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Writeiops < j2 . Config . Writeiops
} ,
WriteiopsDec : func ( j1 , j2 * Jail ) bool {
return j1 . Config . Writeiops > j2 . Config . Writeiops
} ,
}
2022-04-24 16:49:54 +02:00
2021-12-20 22:10:38 +01:00
js := JailSort {
ConfigPathInc : func ( j1 , j2 * Jail ) bool {
return j1 . ConfigPath < j2 . ConfigPath
} ,
ConfigPathDec : func ( j1 , j2 * Jail ) bool {
return j1 . ConfigPath > j2 . ConfigPath
} ,
2022-06-18 18:24:09 +02:00
DatastoreInc : func ( j1 , j2 * Jail ) bool {
return j1 . Datastore < j2 . Datastore
} ,
DatastoreDec : func ( j1 , j2 * Jail ) bool {
return j1 . Datastore > j2 . Datastore
} ,
2021-12-20 22:10:38 +01:00
InternalNameInc : func ( j1 , j2 * Jail ) bool {
return j1 . InternalName < j2 . InternalName
} ,
InternalNameDec : func ( j1 , j2 * Jail ) bool {
return j1 . InternalName > j2 . InternalName
} ,
JIDInc : func ( j1 , j2 * Jail ) bool {
return j1 . JID < j2 . JID
} ,
JIDDec : func ( j1 , j2 * Jail ) bool {
return j1 . JID > j2 . JID
} ,
NameInc : func ( j1 , j2 * Jail ) bool {
return j1 . Name < j2 . Name
} ,
NameDec : func ( j1 , j2 * Jail ) bool {
return j1 . Name > j2 . Name
} ,
RootPathInc : func ( j1 , j2 * Jail ) bool {
return j1 . RootPath < j2 . RootPath
} ,
RootPathDec : func ( j1 , j2 * Jail ) bool {
return j1 . RootPath > j2 . RootPath
} ,
RunningInc : func ( j1 , j2 * Jail ) bool {
return ! j1 . Running && j2 . Running
} ,
RunningDec : func ( j1 , j2 * Jail ) bool {
return j1 . Running && ! j2 . Running
} ,
ZpoolInc : func ( j1 , j2 * Jail ) bool {
return j1 . Zpool < j2 . Zpool
} ,
ZpoolDec : func ( j1 , j2 * Jail ) bool {
return j1 . Zpool > j2 . Zpool
} ,
Config : jcs ,
}
return js
}
2022-04-03 14:27:26 +02:00
// jailSorter implements the Sort interface, sorting the jails within.
type jailSorter struct {
2022-04-24 16:49:54 +02:00
jails [ ] Jail
less [ ] jailLessFunc
2021-12-20 22:10:38 +01:00
}
2022-04-03 14:27:26 +02:00
// Sort sorts the argument slice according to the less functions passed to JailsOrderedBy.
func ( js * jailSorter ) Sort ( jails [ ] Jail ) {
js . jails = jails
sort . Sort ( js )
2021-12-20 22:10:38 +01:00
}
2022-04-03 14:27:26 +02:00
// JailsOrderedBy returns a Sorter that sorts using the less functions, in order.
2021-12-20 22:10:38 +01:00
// Call its Sort method to sort the data.
2022-04-03 14:27:26 +02:00
func JailsOrderedBy ( less ... jailLessFunc ) * jailSorter {
return & jailSorter {
2021-12-20 22:10:38 +01:00
less : less ,
}
}
// Len is part of sort.Interface.
2022-04-03 14:27:26 +02:00
func ( js * jailSorter ) Len ( ) int {
return len ( js . jails )
2021-12-20 22:10:38 +01:00
}
// Swap is part of sort.Interface.
2022-04-03 14:27:26 +02:00
func ( js * jailSorter ) Swap ( i , j int ) {
js . jails [ i ] , js . jails [ j ] = js . jails [ j ] , js . jails [ i ]
2021-12-20 22:10:38 +01:00
}
// Less is part of sort.Interface. It is implemented by looping along the
// less functions until it finds a comparison that discriminates between
// the two items (one is less than the other). Note that it can call the
// less functions twice per call. We could change the functions to return
// -1, 0, 1 and reduce the number of calls for greater efficiency: an
// exercise for the reader.
2022-04-03 14:27:26 +02:00
func ( js * jailSorter ) Less ( i , j int ) bool {
p , q := & js . jails [ i ] , & js . jails [ j ]
2021-12-20 22:10:38 +01:00
// Try all but the last comparison.
var k int
2022-04-03 14:27:26 +02:00
for k = 0 ; k < len ( js . less ) - 1 ; k ++ {
less := js . less [ k ]
2021-12-20 22:10:38 +01:00
switch {
case less ( p , q ) :
// p < q, so we have a decision.
return true
case less ( q , p ) :
// p > q, so we have a decision.
return false
}
// p == q; try the next comparison.
}
// All comparisons to here said "equal", so just return whatever
// the final comparison reports.
2022-04-03 14:27:26 +02:00
return js . less [ k ] ( p , q )
}
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2022-04-24 16:49:54 +02:00
*
2022-04-03 14:27:26 +02:00
* Sorting snapshots
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
// This struct hold "sort by jail fields" functions
type snapshotLessFunc func ( s1 * Snapshot , s2 * Snapshot ) bool
func initSnapshotSortStruct ( ) SnapshotSort {
ss := SnapshotSort {
NameInc : func ( s1 , s2 * Snapshot ) bool {
return s1 . Name < s2 . Name
} ,
NameDec : func ( s1 , s2 * Snapshot ) bool {
return s1 . Name > s2 . Name
} ,
2022-06-18 18:24:09 +02:00
DatastoreInc : func ( s1 , s2 * Snapshot ) bool {
return s1 . Datastore < s2 . Datastore
2022-04-03 14:27:26 +02:00
} ,
2022-06-18 18:24:09 +02:00
DatastoreDec : func ( s1 , s2 * Snapshot ) bool {
return s1 . Datastore > s2 . Datastore
2022-04-03 14:27:26 +02:00
} ,
2022-04-04 20:10:42 +02:00
JailnameInc : func ( s1 , s2 * Snapshot ) bool {
return s1 . Jailname < s2 . Jailname
} ,
JailnameDec : func ( s1 , s2 * Snapshot ) bool {
return s1 . Jailname > s2 . Jailname
} ,
2022-04-03 14:27:26 +02:00
MountpointInc : func ( s1 , s2 * Snapshot ) bool {
return s1 . Mountpoint < s2 . Mountpoint
} ,
MountpointDec : func ( s1 , s2 * Snapshot ) bool {
return s1 . Mountpoint > s2 . Mountpoint
} ,
UsedInc : func ( s1 , s2 * Snapshot ) bool {
return s1 . Used < s2 . Used
} ,
UsedDec : func ( s1 , s2 * Snapshot ) bool {
return s1 . Used > s2 . Used
} ,
ReferencedInc : func ( s1 , s2 * Snapshot ) bool {
return s1 . Referenced < s2 . Referenced
} ,
ReferencedDec : func ( s1 , s2 * Snapshot ) bool {
return s1 . Referenced > s2 . Referenced
} ,
CreationInc : func ( s1 , s2 * Snapshot ) bool {
return s1 . Creation . Unix ( ) < s2 . Creation . Unix ( )
} ,
CreationDec : func ( s1 , s2 * Snapshot ) bool {
return s1 . Creation . Unix ( ) > s2 . Creation . Unix ( )
} ,
}
return ss
}
// snapshotSorter implements the Sort interface, sorting the jails within.
type snapshotSorter struct {
2022-04-24 16:49:54 +02:00
snapshots [ ] Snapshot
less [ ] snapshotLessFunc
2022-04-03 14:27:26 +02:00
}
// Sort sorts the argument slice according to the less functions passed to OrderedBy.
func ( ss * snapshotSorter ) Sort ( snapshots [ ] Snapshot ) {
ss . snapshots = snapshots
sort . Sort ( ss )
}
// OrderedBy returns a Sorter that sorts using the less functions, in order.
// Call its Sort method to sort the data.
func SnapshotsOrderedBy ( less ... snapshotLessFunc ) * snapshotSorter {
return & snapshotSorter {
less : less ,
}
}
// Len is part of sort.Interface.
func ( ss * snapshotSorter ) Len ( ) int {
return len ( ss . snapshots )
}
// Swap is part of sort.Interface.
func ( ss * snapshotSorter ) Swap ( i , j int ) {
ss . snapshots [ i ] , ss . snapshots [ j ] = ss . snapshots [ j ] , ss . snapshots [ i ]
}
// Less is part of sort.Interface. It is implemented by looping along the
// less functions until it finds a comparison that discriminates between
// the two items (one is less than the other). Note that it can call the
// less functions twice per call. We could change the functions to return
// -1, 0, 1 and reduce the number of calls for greater efficiency: an
// exercise for the reader.
func ( ss * snapshotSorter ) Less ( i , j int ) bool {
p , q := & ss . snapshots [ i ] , & ss . snapshots [ j ]
// Try all but the last comparison.
var k int
for k = 0 ; k < len ( ss . less ) - 1 ; k ++ {
less := ss . less [ k ]
switch {
2022-04-24 16:49:54 +02:00
case less ( p , q ) :
// p < q, so we have a decision.
return true
case less ( q , p ) :
// p > q, so we have a decision.
return false
2022-04-03 14:27:26 +02:00
}
// p == q; try the next comparison.
}
// All comparisons to here said "equal", so just return whatever
// the final comparison reports.
return ss . less [ k ] ( p , q )
2021-12-20 22:10:38 +01:00
}
2022-04-03 11:04:01 +02:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
2022-06-18 16:09:22 +02:00
* Sorting datastores
2022-04-03 11:04:01 +02:00
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
2022-06-18 16:09:22 +02:00
// This struct hold "sort by datastores fields" functions
type datastoreLessFunc func ( s1 * Datastore , s2 * Datastore ) bool
func initDatastoreSortStruct ( ) DatastoreSort {
ss := DatastoreSort {
NameInc : func ( s1 , s2 * Datastore ) bool {
return s1 . Name < s2 . Name
} ,
NameDec : func ( s1 , s2 * Datastore ) bool {
return s1 . Name > s2 . Name
} ,
MountpointInc : func ( s1 , s2 * Datastore ) bool {
return s1 . Mountpoint < s2 . Mountpoint
} ,
MountpointDec : func ( s1 , s2 * Datastore ) bool {
return s1 . Mountpoint > s2 . Mountpoint
} ,
ZFSDatasetInc : func ( s1 , s2 * Datastore ) bool {
return s1 . ZFSDataset < s2 . ZFSDataset
} ,
ZFSDatasetDec : func ( s1 , s2 * Datastore ) bool {
return s1 . ZFSDataset > s2 . ZFSDataset
} ,
UsedInc : func ( s1 , s2 * Datastore ) bool {
return s1 . Used < s2 . Used
} ,
UsedDec : func ( s1 , s2 * Datastore ) bool {
return s1 . Used > s2 . Used
} ,
ReferencedInc : func ( s1 , s2 * Datastore ) bool {
return s1 . Referenced < s2 . Referenced
} ,
ReferencedDec : func ( s1 , s2 * Datastore ) bool {
return s1 . Referenced > s2 . Referenced
} ,
AvailableInc : func ( s1 , s2 * Datastore ) bool {
return s1 . Available < s2 . Available
} ,
AvailableDec : func ( s1 , s2 * Datastore ) bool {
return s1 . Available > s2 . Available
} ,
}
return ss
}
// DatastoreSorter implements the Sort interface, sorting the jails within.
type DatastoreSorter struct {
Datastores [ ] Datastore
less [ ] datastoreLessFunc
}
// Sort sorts the argument slice according to the less functions passed to OrderedBy.
func ( ss * DatastoreSorter ) Sort ( Datastores [ ] Datastore ) {
ss . Datastores = Datastores
sort . Sort ( ss )
}
// OrderedBy returns a Sorter that sorts using the less functions, in order.
// Call its Sort method to sort the data.
func DatastoresOrderedBy ( less ... datastoreLessFunc ) * DatastoreSorter {
return & DatastoreSorter {
less : less ,
}
}
// Len is part of sort.Interface.
func ( ss * DatastoreSorter ) Len ( ) int {
return len ( ss . Datastores )
}
// Swap is part of sort.Interface.
func ( ss * DatastoreSorter ) Swap ( i , j int ) {
ss . Datastores [ i ] , ss . Datastores [ j ] = ss . Datastores [ j ] , ss . Datastores [ i ]
}
// Less is part of sort.Interface. It is implemented by looping along the
// less functions until it finds a comparison that discriminates between
// the two items (one is less than the other). Note that it can call the
// less functions twice per call. We could change the functions to return
// -1, 0, 1 and reduce the number of calls for greater efficiency: an
// exercise for the reader.
func ( ss * DatastoreSorter ) Less ( i , j int ) bool {
p , q := & ss . Datastores [ i ] , & ss . Datastores [ j ]
// Try all but the last comparison.
var k int
for k = 0 ; k < len ( ss . less ) - 1 ; k ++ {
less := ss . less [ k ]
switch {
case less ( p , q ) :
// p < q, so we have a decision.
2022-04-03 11:04:01 +02:00
return true
2022-06-18 16:09:22 +02:00
case less ( q , p ) :
// p > q, so we have a decision.
return false
2022-04-03 11:04:01 +02:00
}
2022-06-18 16:09:22 +02:00
// p == q; try the next comparison.
2022-04-03 11:04:01 +02:00
}
2022-06-18 16:09:22 +02:00
// All comparisons to here said "equal", so just return whatever
// the final comparison reports.
return ss . less [ k ] ( p , q )
2022-04-03 11:04:01 +02:00
}