gocage/cmd/list.go

335 lines
9.3 KiB
Go
Raw Normal View History

2021-12-18 13:13:25 +01:00
package cmd
import (
2022-04-24 16:49:54 +02:00
"encoding/json"
2021-12-18 13:13:25 +01:00
"fmt"
2022-04-24 16:49:54 +02:00
"gocage/jail"
"io/ioutil"
2021-12-18 13:13:25 +01:00
"log"
2022-04-24 16:49:54 +02:00
"os"
2021-12-18 15:22:55 +01:00
"reflect"
"strings"
2021-12-18 13:13:25 +01:00
)
2022-04-02 15:50:14 +02:00
/********************************************************************************
* List all properties a jail have, with their internal name
2022-04-03 14:27:26 +02:00
* Only print properties name. To get name & values, use GetJailProperties()
2022-04-02 15:50:14 +02:00
*******************************************************************************/
func ListJailsProps(args []string) {
var conf Jail
var result []string
// Mandatory constructor to init default values
jailconf, err := NewJailConfig()
if err != nil {
fmt.Printf("Error allocating JailConfig: %s\n", err.Error())
return
}
2022-04-24 16:49:54 +02:00
conf.Config = jailconf
2022-04-24 16:49:54 +02:00
result = getStructFieldNames(conf, result, "")
2022-04-24 16:49:54 +02:00
for _, f := range result {
fmt.Printf("%s\n", f)
}
}
2022-04-02 15:50:14 +02:00
/********************************************************************************
2022-04-24 16:49:54 +02:00
* Get Jails from datastores. Store config and running metadata
2022-04-02 15:50:14 +02:00
* into gJails global var
*******************************************************************************/
func ListJails(args []string, display bool) {
type uniqueJailName struct {
jail string
unique bool
uniqueName bool
}
var nameChecked []*uniqueJailName
var curCheck *uniqueJailName
var found bool
var skip bool
for _, ds := range gDatastores {
listJailsFromDatastore(ds, args, display)
}
// Only when displaying jails, we accept to process multiple same name jails
if false == display {
for _, j := range gJails {
// If already checked and was using distinctive name, skip this occurence
for i, n := range nameChecked {
if strings.EqualFold(n.jail, j.Name) {
found = true
if false == n.unique && true == n.uniqueName {
skip = true
} else {
curCheck = nameChecked[i]
}
break
}
}
if true == skip {
continue
}
// Initialize if not found in nameChecked
if false == found {
curCheck = &uniqueJailName{jail: j.Name,
unique: true,
uniqueName: false}
} else {
found = false
}
if countOfJailsWithThisName(j.Name) > 1 {
//fmt.Printf("DEBUG: Jail %s exist multiple times, now checking if specified with full name\n", j.Name)
curCheck.unique = false
for _, a := range args {
//fmt.Printf("DEBUG: comparing %s/%s with %s\n", j.Datastore, j.Name, a)
if strings.EqualFold(a, fmt.Sprintf("%s/%s", j.Datastore, j.Name)) {
//fmt.Printf("DEBUG: Was specified with full name, GG!\n")
curCheck.uniqueName = true
}
}
}
nameChecked = append(nameChecked, curCheck)
}
// Now check
for _, a := range args {
for _, n := range nameChecked {
if strings.EqualFold(n.jail, a) && false == n.unique && false == n.uniqueName {
fmt.Printf("There is more than one jail named \"%s\", please use datastore/jail format\n", n.jail)
os.Exit(1)
}
}
}
2021-12-18 13:13:25 +01:00
}
fields := strings.Split(gDisplayJColumns, ",")
2021-12-19 16:49:07 +01:00
// This is the structure we will filter, then display
var jails []Jail
2022-04-02 15:50:14 +02:00
/***************************************************************
2021-12-19 16:49:07 +01:00
/ Filter jails with "filter" options
2022-04-02 15:50:14 +02:00
/**************************************************************/
2021-12-19 16:49:07 +01:00
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-19 16:49:07 +01:00
} else {
jails = gJails
}
2022-04-02 15:50:14 +02:00
/***************************************************************
2021-12-19 16:49:07 +01:00
/ Filter jails by names given on command line
2022-04-02 15:50:14 +02:00
/**************************************************************/
2021-12-19 16:49:07 +01:00
if len(args) > 0 {
var js []Jail
for _, a := range args {
for _, j := range jails {
if j.Name == a {
js = append(js, j)
}
}
}
jails = js
}
2022-04-02 15:50:14 +02:00
/***************************************************************
2021-12-20 22:10:38 +01:00
/ Sort jails
/ We support 3 sort criteria max
2022-04-02 15:50:14 +02:00
/**************************************************************/
if len(gSortJailFields) > 0 && gSortJailFields != "none" {
2022-04-03 14:27:26 +02:00
js := initJailSortStruct()
2021-12-20 22:10:38 +01:00
// The way we manage criteria quantity is not very elegant...
var fct1, fct2, fct3 *reflect.Value
for i, c := range strings.Split(gSortJailFields, ",") {
2021-12-20 22:10:38 +01:00
var fctName string
if strings.HasPrefix(c, "-") {
fctName = fmt.Sprintf("%sDec", strings.Replace(c, "-", "", 1))
2022-04-24 16:49:54 +02:00
} else { // Par defaut (pas de prefix +/-) on considere un tri incremental
2021-12-20 22:10:38 +01:00
fctName = fmt.Sprintf("%sInc", strings.Replace(c, "+", "", 1))
}
2022-04-24 16:49:54 +02:00
2021-12-20 22:10:38 +01:00
// 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
}
2022-04-24 16:49:54 +02:00
switch i + 1 {
case 1:
fct1 = fct
case 2:
fct2 = fct
case 3:
fct3 = fct
2021-12-20 22:10:38 +01:00
}
}
switch len(strings.Split(gSortJailFields, ",")) {
2022-04-24 16:49:54 +02:00
case 1:
JailsOrderedBy(fct1.Interface().(jailLessFunc)).Sort(jails)
case 2:
JailsOrderedBy(fct1.Interface().(jailLessFunc), fct2.Interface().(jailLessFunc)).Sort(jails)
case 3:
JailsOrderedBy(fct1.Interface().(jailLessFunc), fct2.Interface().(jailLessFunc), fct3.Interface().(jailLessFunc)).Sort(jails)
2021-12-20 22:10:38 +01:00
}
}
2022-04-02 15:50:14 +02:00
/***************************************************************
2021-12-20 22:10:38 +01:00
/ Finally, display jails
2022-04-02 15:50:14 +02:00
/**************************************************************/
2021-12-19 16:49:07 +01:00
if display {
displayJailsFields(jails, fields)
}
2021-12-18 13:13:25 +01:00
}
func listJailsFromDatastore(ds Datastore, args []string, accept_multiple bool) {
fileInfo, err := os.Stat(ds.Mountpoint)
2022-04-24 16:49:54 +02:00
if err != nil {
log.Fatalln(fmt.Sprintf("Unable to access %s, check path and/or rights", ds.Mountpoint))
2022-04-24 16:49:54 +02:00
}
if fileInfo.IsDir() == false {
log.Fatalln(fmt.Sprintf("%s is not a directory", ds.Mountpoint))
2022-04-24 16:49:54 +02:00
}
2021-12-18 13:13:25 +01:00
// A datastore have to contain a "jails" directory
jailsDir := fmt.Sprintf("%s/jails", ds.Mountpoint)
2021-12-18 13:13:25 +01:00
fileInfo, err = os.Stat(jailsDir)
2022-04-24 16:49:54 +02:00
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))
}
2021-12-18 13:13:25 +01:00
listJailsFromDirectory(jailsDir, ds.Name)
2021-12-18 13:13:25 +01:00
}
func listJailsFromDirectory(dir string, dsname string) ([]Jail, error) {
2021-12-18 13:13:25 +01:00
files, err := ioutil.ReadDir(dir)
2022-04-24 16:49:54 +02:00
if err != nil {
log.Fatalln(fmt.Sprintf("Unable to browse %s, check path and/or rights", dir))
}
2021-12-18 13:13:25 +01:00
for _, fi := range files {
if fi.IsDir() == true {
2022-04-24 16:49:54 +02:00
// 1. Get conf from config.json
2021-12-18 13:13:25 +01:00
jailConfPath := fmt.Sprintf("%s/%s/%s", dir, fi.Name(), "config.json")
jailConf, err := getJailConfig(jailConfPath)
2022-04-24 16:49:54 +02:00
if err != nil {
2023-08-05 19:49:55 +02:00
fmt.Printf("ERROR reading jail config from %s\n", jailConfPath)
2022-04-24 16:49:54 +02:00
}
// 2. Build jail object from config
2021-12-18 13:13:25 +01:00
jailRootPath := fmt.Sprintf("%s/%s/%s", dir, fi.Name(), "root")
j := Jail{
2022-09-25 13:45:12 +02:00
Name: jailConf.Host_hostuuid,
2022-04-24 16:49:54 +02:00
Config: jailConf,
2021-12-18 13:13:25 +01:00
ConfigPath: jailConfPath,
Datastore: dsname,
2022-04-24 16:49:54 +02:00
RootPath: jailRootPath,
Running: false,
2021-12-18 13:13:25 +01:00
}
2022-04-24 16:49:54 +02:00
// 3. Add current running informations
2021-12-18 13:13:25 +01:00
rjails, err := jail.GetJails()
2022-04-24 16:49:54 +02:00
if err != nil {
log.Fatalln("Unable to list running jails")
}
2021-12-18 13:13:25 +01:00
for _, rj := range rjails {
if rj.Path == j.RootPath {
j.JID = rj.Jid
j.Running = true
2022-11-20 20:13:47 +01:00
// FIXME ??? Shouldn't be ioc-$Name ?
2021-12-18 13:13:25 +01:00
j.InternalName = rj.Name
j.Devfs_ruleset = rj.Devfs_ruleset
// Update release
r, err := getVersion(&j)
if err != nil {
fmt.Printf("ERROR getting jail %s version: %s\n", j.Name, err.Error())
} else {
j.Config.Release = r
}
break
2021-12-18 13:13:25 +01:00
}
}
2022-04-24 16:49:54 +02:00
// 4. Get Zpool
2022-04-24 16:49:54 +02: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
// Check if jail with the same name already exist on another DS
for _, jj := range gJails {
if strings.EqualFold(jj.Name, j.Name) {
/*fmt.Printf("Warning: A jail with name %s exist on datastore %s, use full name to interact!\n", j.Name, jj.Datastore)
fmt.Printf("(Ex: gocage start %s/%s)\n", jj.Datastore, j.Name)*/
// Add Datastore to avoid confusion
2022-07-10 21:26:54 +02:00
if false == strings.Contains(gDisplayJColumns, "Datastore") {
gDisplayJColumns += ",Datastore"
}
}
}
2021-12-18 13:13:25 +01:00
gJails = append(gJails, j)
}
}
return gJails, nil
2021-12-18 13:13:25 +01:00
}
func getJailConfig(jailConfigPath string) (JailConfig, error) {
content, err := ioutil.ReadFile(jailConfigPath)
2022-04-24 16:49:54 +02:00
if err != nil {
log.Fatalln(fmt.Sprintf("Unable to read %s, check path and/or rights", jailConfigPath))
}
2021-12-18 13:13:25 +01:00
// Mandatory constructor to init default values
jc, err := NewJailConfig()
if err != nil {
return jc, err
}
2021-12-18 13:13:25 +01:00
err = json.Unmarshal([]byte(content), &jc)
2022-04-24 16:49:54 +02:00
if err != nil {
log.Fatalln(fmt.Sprintf("Error occured during unmarshaling %s: %s", jailConfigPath, err.Error()))
}
2021-12-18 13:13:25 +01:00
return jc, err
}