gocage/cmd/utils.go

2535 lines
73 KiB
Go

package cmd
import (
"io"
"os"
"fmt"
"log"
"sort"
"bufio"
"errors"
"os/exec"
"reflect"
"strconv"
"strings"
"io/ioutil"
"github.com/c2h5oh/datasize"
)
const (
ipv4re = `[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}`
ifconfigipv4re = `inet[[:space:]](` + ipv4re + `)`
)
/*****************************************************************************
* Mandatory constructor for JailConfig type. It set default values
*****************************************************************************/
func NewJailConfig() (JailConfig, error) {
var jc JailConfig
hostid, err := ioutil.ReadFile("/etc/hostid")
if err != nil {
return jc, err
} else {
hostid = []byte(strings.Replace(string(hostid), "\n", "", -1))
}
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"
jc.Vnet_default_interface = "auto"
jc.Vnet_interfaces = "none"
jc.Wallclock = "off"
jc.Writebps = "off"
jc.Writeiops = "off"
return jc, nil
}
/*****************************************************************************
*
* Command execution
*
*****************************************************************************/
func executeCommand(cmdline string) (string, error) {
var cmd []string
var out []byte
var err error
if gUseSudo {
cmd = append(cmd, "sudo")
}
cs := strings.Split(cmdline, " ")
cmd = append(cmd, cs...)
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
}
func executeCommandInJail(jail *Jail, cmdline string) (string, error) {
var cmd []string
if gUseSudo {
cmd = append(cmd, "sudo")
}
cmd = append(cmd, "setfib")
if len(jail.Config.Exec_fib) > 0 {
cmd = append(cmd, jail.Config.Exec_fib)
} else {
cmd = append(cmd, "0")
}
cmd = append(cmd, "jexec", jail.InternalName)
cs := strings.Split(cmdline, " ")
cmd = append(cmd, cs...)
out, err := exec.Command(cmd[0], cmd[1:]...).CombinedOutput()
return string(out), err
}
/*****************************************************************************
*
* ZFS datasets/pools operations
*
*****************************************************************************/
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
}
func zfsCopy(src string, dest string) error {
// First, declare sending process & pipe
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
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)
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)
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 {
//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
}
// Copy incremental snasphot to destination
func zfsCopyIncremental(firstsnap string, secondsnap string, dest string) error {
// First, declare sending process & pipe
cmd_send := exec.Command("zfs", "send", "-i", firstsnap, secondsnap)
stdout_send, err := cmd_send.StdoutPipe()
if err != nil {
return errors.New(fmt.Sprintf("Error: %v\n", err))
}
// then declare receiving process & pipe
/* "-Fu" flags to avoid "cannot receive incremental stream: destination qstore/iocage/jails/kibana has been modified
* since most recent snapshot" error message */
cmd_recv := exec.Command("zfs", "receive", "-Fu", dest)
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
}
/*****************************************************************************
*
* 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 {
cmd := fmt.Sprintf("/usr/sbin/sysrc -f %s -x %s", rcconfpath, key)
_, err := executeCommand(cmd)
if err != nil {
return err
}
return nil
}
/*****************************************************************************
* 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())
if len(res) != 6 {
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{
Device: res[0],
Mountpoint: res[1],
Type: res[2],
Options: strings.Split(res[3], ","),
Fs_Freq: freq,
Fs_Passno: pass,
}
mounts = append(mounts, m)
}
return mounts, nil
}
/********************************************************************************
* Get a specific jail reference, to update properties after a range loop
*******************************************************************************/
func getJailFromArray(name string, jarray []Jail) (*Jail, error) {
for _, j := range jarray {
if name == j.Name {
return &j, nil
}
}
return &Jail{}, errors.New("Jail not found")
}
/******************************************************************************
*
* Generic utilities
*
*****************************************************************************/
func isStringInArray(strarr []string, searched string) bool {
for _, s := range strarr {
if strings.EqualFold(s, searched) {
return true
}
}
return false
}
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")
}
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
}
/********************************************************************************
* 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
}
/******************************************************************************
* 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
}
/********************************************************************************
* Recurse into structure, returning reflect.Kind of named field.
* Nested fields are named with a dot (ex "MyStruct.MyField")
*******************************************************************************/
func getStructFieldKind(parentStruct interface{}, fieldName string) (reflect.Kind, string, error) {
v := reflect.ValueOf(parentStruct)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
// For debugging
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))
}
/********************************************************************************
* 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)
for i := 0; i < v.NumField(); i++ {
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)
}
}
}
return result
}
/********************************************************************************
* Recurse into structure, returning reflect.Value of wanted field.
* Nested fields are named with a dot (ex "MyStruct.MyField")
* Returns (value, field_name, error)
*******************************************************************************/
func getStructFieldValue(parentStruct interface{}, fieldName string) (*reflect.Value, string, error) {
v := reflect.ValueOf(parentStruct)
// Get value while we're dealing with pointers
for ; v.Kind() == reflect.Ptr; v = v.Elem() {
}
if v.Kind() != reflect.Struct {
return &v, fieldName, errors.New(fmt.Sprintf("parentStruct is not a struct! Kind: %s", v.Kind().String()))
}
typeOfV := v.Type()
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())
}
}
}
var f reflect.Value
var found bool
fs := strings.Split(fieldName, ".")
// Loop through properties
for i, curF := range fs {
found = false
for j := 0; j < v.NumField(); j++ {
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))
}
for ; f.Kind() == reflect.Ptr; f = f.Elem() {
}
/*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)*/
// 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
} else {
return &v, fieldName, errors.New(fmt.Sprintf("Field not found: %s", fieldName))
}
} else {
v = f
typeOfV = v.Type()
}
}
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)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
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 getStructField(f.Interface(), strings.Join(fs[1:], "."))
} else {
log.Fatalln(fmt.Sprintf("%s is not a struct: %s\n", fs[0], f.Kind().String()))
}
}
return v, fieldName
}
// 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
}
if val.CanSet() {
switch val.Kind() {
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()))
}
} else {
return errors.New(fmt.Sprintf("Field is not writable : %s", propName))
}
return nil
}
/********************************************************************************
* Pretty display of jails field
* Fields to show are given in a string array parameter
* Ex. : displayJailsFields(gJails, ["Name", "JID", "RootPath"])
*******************************************************************************/
func displayJailsFields(jails []Jail, 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 _, j := range jails {
// Have to use a pointer, else reflect.Value.Elem() will panic : https://pkg.go.dev/reflect#Value.Elem
tj := &j
line := make([]Field, len(valsToDisplay))
for i, f := range valsToDisplay {
a, f := getStructField(tj, 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 {
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
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
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 !gNoJailLineSep {
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 gNoJailLineSep {
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 snapshots field
* Fields to show are given in a string array parameter
* Ex. : displaySnapshotsFields(snapshots, ["Name", "Datastore", "Used"])
*******************************************************************************/
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 {
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 _, s := range snaps {
// Have to use a pointer, else reflect.Value.Elem() will panic : https://pkg.go.dev/reflect#Value.Elem
tj := &s
line := make([]Field, len(valsToDisplay))
for i, f := range valsToDisplay {
a, f := getStructField(tj, 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 !gNoSnapLineSep {
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 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 {
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")
}
/*****************************************************************************
*
* Sorting jails
*
*****************************************************************************/
// This struct hold "sort by jail fields" functions
type jailLessFunc func(j1 *Jail, j2 *Jail) bool
func initJailSortStruct() JailSort {
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
},
}
js := JailSort{
ConfigPathInc: func(j1, j2 *Jail) bool {
return j1.ConfigPath < j2.ConfigPath
},
ConfigPathDec: func(j1, j2 *Jail) bool {
return j1.ConfigPath > j2.ConfigPath
},
DatastoreInc: func(j1, j2 *Jail) bool {
return j1.Datastore < j2.Datastore
},
DatastoreDec: func(j1, j2 *Jail) bool {
return j1.Datastore > j2.Datastore
},
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
}
// jailSorter implements the Sort interface, sorting the jails within.
type jailSorter struct {
jails []Jail
less []jailLessFunc
}
// 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)
}
// JailsOrderedBy returns a Sorter that sorts using the less functions, in order.
// Call its Sort method to sort the data.
func JailsOrderedBy(less ...jailLessFunc) *jailSorter {
return &jailSorter{
less: less,
}
}
// Len is part of sort.Interface.
func (js *jailSorter) Len() int {
return len(js.jails)
}
// Swap is part of sort.Interface.
func (js *jailSorter) Swap(i, j int) {
js.jails[i], js.jails[j] = js.jails[j], js.jails[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 (js *jailSorter) Less(i, j int) bool {
p, q := &js.jails[i], &js.jails[j]
// Try all but the last comparison.
var k int
for k = 0; k < len(js.less)-1; k++ {
less := js.less[k]
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.
return js.less[k](p, q)
}
/*****************************************************************************
*
* 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
},
DatastoreInc: func(s1, s2 *Snapshot) bool {
return s1.Datastore < s2.Datastore
},
DatastoreDec: func(s1, s2 *Snapshot) bool {
return s1.Datastore > s2.Datastore
},
JailnameInc: func(s1, s2 *Snapshot) bool {
return s1.Jailname < s2.Jailname
},
JailnameDec: func(s1, s2 *Snapshot) bool {
return s1.Jailname > s2.Jailname
},
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 {
snapshots []Snapshot
less []snapshotLessFunc
}
// 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 {
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.
return ss.less[k](p, q)
}
/*****************************************************************************
*
* Sorting datastores
*
*****************************************************************************/
// 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.
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.
return ss.less[k](p, q)
}