2021-12-18 13:13:25 +01:00
|
|
|
package cmd
|
|
|
|
|
|
|
|
import (
|
|
|
|
"os"
|
|
|
|
"fmt"
|
|
|
|
"log"
|
2021-12-19 16:49:07 +01:00
|
|
|
"errors"
|
2021-12-18 15:22:55 +01:00
|
|
|
"reflect"
|
2021-12-18 17:15:06 +01:00
|
|
|
"strings"
|
2021-12-18 13:13:25 +01:00
|
|
|
"io/ioutil"
|
|
|
|
"gocage/jail"
|
|
|
|
"encoding/json"
|
|
|
|
"github.com/spf13/viper"
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2021-12-19 16:49:07 +01:00
|
|
|
// Recurse into structure, returning reflect.Value of wanted field.
|
|
|
|
// Nested fields are named with a dot (ex "MyStruct.MyField")
|
|
|
|
func getStructFieldValue(parentStruct interface{}, fieldName string) (reflect.Value, string, error) {
|
|
|
|
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 getStructFieldValue(f.Interface(), strings.Join(fs[1:], "."))
|
|
|
|
} else {
|
|
|
|
log.Fatalln(fmt.Sprintf("%s is not a struct: %s\n", fs[0], f.Kind().String()))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
f := v.FieldByName(fieldName)
|
|
|
|
if f.IsValid() {
|
|
|
|
return f, fieldName, nil
|
|
|
|
} else {
|
|
|
|
return v, fieldName, errors.New(fmt.Sprintf("Field not found: %s", fieldName))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return v, fieldName, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TODO : Replace by getStructFieldValue
|
2021-12-18 21:34:11 +01:00
|
|
|
// Recurse into structure, returning reflect.Value of wanted field.
|
|
|
|
// Nested fields are named with a dot (ex "MyStruct.MyField")
|
2021-12-18 17:15:06 +01:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-12-18 15:22:55 +01:00
|
|
|
/* Pretty display of jails field
|
|
|
|
Fields to show are given in a string array parameter
|
|
|
|
Ex. : displayJails(["Name", "JID", "RootPath"])
|
|
|
|
*/
|
|
|
|
func displayStructFields(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 {
|
2021-12-18 17:15:06 +01:00
|
|
|
a, f := getStructField(tj, f)
|
|
|
|
|
2021-12-18 15:22:55 +01:00
|
|
|
field := Field {
|
|
|
|
Name: f,
|
2021-12-18 15:37:05 +01:00
|
|
|
}
|
2021-12-18 17:15:06 +01:00
|
|
|
if a.FieldByName(f).IsValid() {
|
2021-12-18 15:22:55 +01:00
|
|
|
// For now this just contains this item length, will adjust later
|
2021-12-19 14:31:51 +01:00
|
|
|
// 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()))
|
|
|
|
}
|
2021-12-18 17:15:06 +01:00
|
|
|
field.Value = fmt.Sprintf("%v", a.FieldByName(f).Interface())
|
|
|
|
} else {
|
|
|
|
fmt.Printf("Invalid field name: %s\n", f)
|
2021-12-18 15:22:55 +01:00
|
|
|
}
|
|
|
|
line[i] = field
|
|
|
|
|
|
|
|
}
|
|
|
|
out = append(out, line)
|
|
|
|
}
|
|
|
|
|
2021-12-19 16:49:07 +01:00
|
|
|
if len(out) == 0 {
|
|
|
|
fmt.Printf("Nothing to see here!\n")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-12-18 15:22:55 +01:00
|
|
|
// Get real maximum length
|
|
|
|
maxlen := make([]int, len(valsToDisplay))
|
|
|
|
for i := 0; i< len(valsToDisplay); i++ {
|
2021-12-18 15:37:05 +01:00
|
|
|
maxlen[i] = len(valsToDisplay[i])
|
2021-12-18 15:22:55 +01:00
|
|
|
}
|
|
|
|
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
|
2021-12-18 15:37:05 +01:00
|
|
|
// For each field, add separator and 2 blank spaces
|
2021-12-18 15:22:55 +01:00
|
|
|
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!
|
2021-12-19 14:31:51 +01:00
|
|
|
// 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("+")
|
2021-12-18 15:22:55 +01:00
|
|
|
}
|
|
|
|
fmt.Printf("\n")
|
|
|
|
|
2021-12-19 14:31:51 +01:00
|
|
|
|
|
|
|
// Column names
|
2021-12-18 15:22:55 +01:00
|
|
|
for i, f := range out[0] {
|
|
|
|
if i == 0 {
|
|
|
|
fmt.Printf("|")
|
|
|
|
}
|
2021-12-18 21:34:11 +01:00
|
|
|
/* Use vlsToDisplay to get real name (with "Config.")
|
2021-12-18 15:22:55 +01:00
|
|
|
fmt.Printf(" %s", f.Name)
|
2021-12-18 21:34:11 +01:00
|
|
|
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++ {
|
2021-12-18 15:22:55 +01:00
|
|
|
fmt.Printf(" ")
|
|
|
|
}
|
|
|
|
fmt.Printf(" |")
|
|
|
|
}
|
2021-12-19 14:31:51 +01:00
|
|
|
// Finally separator line
|
2021-12-18 15:22:55 +01:00
|
|
|
fmt.Printf("\n")
|
2021-12-19 14:31:51 +01:00
|
|
|
for i, f := range out[0] {
|
|
|
|
if i == 0 { fmt.Printf("+") }
|
|
|
|
for i := 0 ; i < f.MaxLen+2 ; i++ { fmt.Printf("=") }
|
|
|
|
fmt.Printf("+")
|
2021-12-18 15:22:55 +01:00
|
|
|
}
|
|
|
|
fmt.Printf("\n")
|
|
|
|
|
|
|
|
// Then display data
|
2021-12-19 14:31:51 +01:00
|
|
|
// Loop through lines
|
2021-12-18 15:22:55 +01:00
|
|
|
for _, l := range out {
|
2021-12-19 16:49:07 +01:00
|
|
|
|
2021-12-19 14:31:51 +01: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)
|
2021-12-18 15:22:55 +01:00
|
|
|
for i, f := range l {
|
|
|
|
if i == 0 {
|
|
|
|
fmt.Printf("|")
|
|
|
|
}
|
2021-12-19 12:41:16 +01:00
|
|
|
// Special cases of value displaying
|
|
|
|
if f.Name == "JID" && f.Value == "0" {
|
|
|
|
fmt.Printf(" ")
|
2021-12-19 14:31:51 +01:00
|
|
|
} 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])
|
2021-12-19 12:41:16 +01:00
|
|
|
} else {
|
|
|
|
fmt.Printf(" %s", f.Value)
|
|
|
|
}
|
2021-12-19 14:31:51 +01:00
|
|
|
// Complete with spaces to the max length
|
2021-12-18 15:22:55 +01:00
|
|
|
for i := len(f.Value)+1 ; i < f.MaxLen+1 ; i++ {
|
|
|
|
fmt.Printf(" ")
|
|
|
|
}
|
|
|
|
fmt.Printf(" |")
|
|
|
|
}
|
2021-12-19 14:31:51 +01:00
|
|
|
// 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 !gNoLineSep {
|
|
|
|
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("+")
|
|
|
|
}
|
|
|
|
}
|
2021-12-18 15:22:55 +01:00
|
|
|
fmt.Printf("\n")
|
|
|
|
}
|
2021-12-19 14:31:51 +01:00
|
|
|
if gNoLineSep {
|
|
|
|
for i, f := range out[0] {
|
|
|
|
if i == 0 { fmt.Printf("+") }
|
|
|
|
for i := 0 ; i < f.MaxLen+2 ; i++ { fmt.Printf("-") }
|
|
|
|
fmt.Printf("+")
|
|
|
|
}
|
2021-12-18 15:22:55 +01:00
|
|
|
}
|
2021-12-19 14:31:51 +01:00
|
|
|
|
2021-12-18 15:22:55 +01:00
|
|
|
fmt.Printf("\n")
|
|
|
|
}
|
2021-12-18 13:13:25 +01:00
|
|
|
|
2021-12-18 21:34:11 +01:00
|
|
|
|
|
|
|
/* Get Jails from datastores. Store config and running metadata into gJails global var */
|
2021-12-19 12:41:16 +01:00
|
|
|
func ListJails(args []string, display bool) {
|
2021-12-19 13:05:30 +01:00
|
|
|
fields := strings.Split(gDisplayColumns, ",")
|
2021-12-18 21:34:11 +01:00
|
|
|
|
2021-12-18 13:13:25 +01:00
|
|
|
for _, d := range viper.GetStringSlice("datastore") {
|
|
|
|
listJailsFromDatastore(d)
|
|
|
|
}
|
|
|
|
|
2021-12-19 16:49:07 +01:00
|
|
|
// This is the structure we will filter, then display
|
|
|
|
var jails []Jail
|
|
|
|
|
|
|
|
/************************************************************************************
|
|
|
|
/ Filter jails with "filter" options
|
|
|
|
/***********************************************************************************/
|
|
|
|
if len(gFilterJails) > 0 && gFilterJails != "none" {
|
|
|
|
flts := make(map[string]string)
|
|
|
|
for _, flt := range strings.Split(gFilterJails, ",") {
|
|
|
|
sa := strings.Split(flt, "=")
|
|
|
|
if len(sa) != 2 {
|
|
|
|
fmt.Printf("Invalid format for filter %s\n", gFilterJails)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
flts[sa[0]] = sa[1]
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(flts) > 0 {
|
|
|
|
// Browse global gJails array to get filtered results
|
|
|
|
for _, j := range gJails {
|
|
|
|
for key, val := range flts {
|
|
|
|
v, _, err := getStructFieldValue(&j, key)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("ERROR: Field %s not found\n", key)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if strings.EqualFold(fmt.Sprintf("%v", v.Interface()), val) {
|
|
|
|
jails = append(jails, j)
|
2021-12-18 21:34:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-12-19 16:49:07 +01:00
|
|
|
} else {
|
|
|
|
jails = gJails
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
|
|
|
/ Filter jails by names given on command line
|
|
|
|
/***********************************************************************************/
|
|
|
|
if len(args) > 0 {
|
|
|
|
var js []Jail
|
|
|
|
for _, a := range args {
|
|
|
|
for _, j := range jails {
|
|
|
|
if j.Name == a {
|
|
|
|
js = append(js, j)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
jails = js
|
|
|
|
}
|
|
|
|
|
2021-12-20 22:10:38 +01:00
|
|
|
/************************************************************************************
|
|
|
|
/ Sort jails
|
|
|
|
/ We support 3 sort criteria max
|
|
|
|
/***********************************************************************************/
|
2021-12-21 20:47:46 +01:00
|
|
|
if len(gSortFields) > 0 && gSortFields != "none" {
|
2021-12-20 22:10:38 +01:00
|
|
|
js := initSortStruct()
|
|
|
|
|
|
|
|
// The way we manage criteria quantity is not very elegant...
|
|
|
|
var fct1, fct2, fct3 reflect.Value
|
|
|
|
for i, c := range strings.Split(gSortFields, ",") {
|
|
|
|
var fctName string
|
|
|
|
if strings.HasPrefix(c, "-") {
|
|
|
|
fctName = fmt.Sprintf("%sDec", strings.Replace(c, "-", "", 1))
|
|
|
|
} else { // Par defaut (pas de prefix +/-) on considere un tri incremental
|
|
|
|
fctName = fmt.Sprintf("%sInc", strings.Replace(c, "+", "", 1))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get function by its name
|
|
|
|
fct, _, err := getStructFieldValue(js, fctName)
|
|
|
|
if err != nil {
|
|
|
|
fieldName := strings.Replace(strings.Replace(c, "-", "", 1), "+", "", 1)
|
|
|
|
fmt.Printf("ERROR getting JailSort struct field %s. Please check the field name: %s\n", fctName, fieldName)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
switch i+1 {
|
|
|
|
case 1: fct1 = fct
|
|
|
|
case 2: fct2 = fct
|
|
|
|
case 3: fct3 = fct
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch len(strings.Split(gSortFields, ",")) {
|
|
|
|
case 1:
|
|
|
|
OrderedBy(fct1.Interface().(lessFunc)).Sort(jails)
|
|
|
|
case 2:
|
|
|
|
OrderedBy(fct1.Interface().(lessFunc), fct2.Interface().(lessFunc)).Sort(jails)
|
|
|
|
case 3:
|
|
|
|
OrderedBy(fct1.Interface().(lessFunc), fct2.Interface().(lessFunc), fct3.Interface().(lessFunc)).Sort(jails)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************************
|
|
|
|
/ Finally, display jails
|
|
|
|
/***********************************************************************************/
|
2021-12-19 16:49:07 +01:00
|
|
|
if display {
|
|
|
|
displayStructFields(jails, fields)
|
2021-12-18 21:34:11 +01:00
|
|
|
}
|
2021-12-18 13:13:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func listJailsFromDatastore(datastore string) {
|
|
|
|
fileInfo, err := os.Stat(datastore)
|
|
|
|
if err != nil { log.Fatalln(fmt.Sprintf("Unable to access %s, check path and/or rights", datastore)) }
|
|
|
|
if fileInfo.IsDir() == false { log.Fatalln(fmt.Sprintf("%s is not a directory", datastore)) }
|
|
|
|
|
|
|
|
// A datastore have to contain a "jails" directory
|
|
|
|
jailsDir := fmt.Sprintf("%s/jails", datastore)
|
|
|
|
fileInfo, err = os.Stat(jailsDir)
|
|
|
|
if err != nil { log.Fatalln(fmt.Sprintf("Unable to access %s, check path and/or rights", jailsDir)) }
|
|
|
|
if fileInfo.IsDir() == false { log.Fatalln(fmt.Sprintf("%s is not a directory", jailsDir)) }
|
|
|
|
|
|
|
|
listJailsFromDirectory(jailsDir)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func listJailsFromDirectory(dir string) ([]Jail) {
|
|
|
|
files, err := ioutil.ReadDir(dir)
|
|
|
|
if err != nil { log.Fatalln(fmt.Sprintf("Unable to browse %s, check path and/or rights", dir)) }
|
|
|
|
|
|
|
|
for _, fi := range files {
|
|
|
|
if fi.IsDir() == true {
|
|
|
|
|
|
|
|
// 1. Get conf from config.json
|
|
|
|
jailConfPath := fmt.Sprintf("%s/%s/%s", dir, fi.Name(), "config.json")
|
|
|
|
jailConf, err := getJailConfig(jailConfPath)
|
|
|
|
if err != nil { log.Println("ERROR reading jail config for %s", jailConfPath) }
|
|
|
|
|
|
|
|
// 2. Build jail object from config
|
|
|
|
jailRootPath := fmt.Sprintf("%s/%s/%s", dir, fi.Name(), "root")
|
|
|
|
j := Jail{
|
|
|
|
Name: jailConf.Host_hostname,
|
|
|
|
Config: jailConf,
|
|
|
|
ConfigPath: jailConfPath,
|
|
|
|
RootPath: jailRootPath,
|
|
|
|
Running: false,
|
|
|
|
}
|
|
|
|
|
|
|
|
// 3. Add current running informations
|
|
|
|
rjails, err := jail.GetJails()
|
|
|
|
if err != nil { log.Fatalln("Unable to list running jails") }
|
|
|
|
for _, rj := range rjails {
|
|
|
|
if rj.Path == j.RootPath {
|
|
|
|
j.JID = rj.Jid
|
|
|
|
j.Running = true
|
|
|
|
j.InternalName = rj.Name
|
2021-12-19 12:41:16 +01:00
|
|
|
break
|
2021-12-18 13:13:25 +01:00
|
|
|
}
|
|
|
|
}
|
2021-12-19 12:41:16 +01:00
|
|
|
|
|
|
|
/* This op take some 600ms for ~40 jails :^( */
|
|
|
|
out, err := executeCommand(fmt.Sprintf("zfs list -H -o name %s", j.RootPath))
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("ERROR getting dataset from %s: %s\n", j.RootPath, err.Error())
|
|
|
|
} else {
|
|
|
|
j.Zpool = strings.Split(strings.TrimSuffix(out, "\n"), "/")[0]
|
|
|
|
}
|
2021-12-18 13:13:25 +01:00
|
|
|
|
|
|
|
gJails = append(gJails, j)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return gJails
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func getJailConfig(jailConfigPath string) (JailConfig, error) {
|
|
|
|
content, err := ioutil.ReadFile(jailConfigPath)
|
|
|
|
if err != nil { log.Fatalln(fmt.Sprintf("Unable to read %s, check path and/or rights", jailConfigPath)) }
|
|
|
|
|
|
|
|
var jc JailConfig
|
|
|
|
err = json.Unmarshal([]byte(content), &jc)
|
|
|
|
if err != nil { log.Fatalln(fmt.Sprintf("Error occured during unmarshaling %s: %s", jailConfigPath, err.Error())) }
|
|
|
|
|
|
|
|
return jc, err
|
|
|
|
}
|