349 lines
9.6 KiB
Go
349 lines
9.6 KiB
Go
package cmd
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"gocage/jail"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"reflect"
|
|
"strings"
|
|
)
|
|
|
|
/********************************************************************************
|
|
* List all properties a jail have, with their internal name
|
|
* Only print properties name. To get name & values, use GetJailProperties()
|
|
*******************************************************************************/
|
|
func ListJailsProps(format string) (string, error) {
|
|
var conf Jail
|
|
var props []string
|
|
var out string
|
|
// Mandatory constructor to init default values
|
|
jailconf, err := NewJailConfig()
|
|
if err != nil {
|
|
fmt.Printf("Error allocating JailConfig: %s\n", err.Error())
|
|
return out, err
|
|
}
|
|
|
|
conf.Config = jailconf
|
|
|
|
props = getStructFieldNames(conf, props, "")
|
|
|
|
if format == "json" {
|
|
out = "{\"properties\":["
|
|
for i, p := range props {
|
|
out += fmt.Sprintf("\"%s\"", p)
|
|
if i < len(props) - 1 {
|
|
out += ","
|
|
}
|
|
}
|
|
out += "]}"
|
|
} else {
|
|
for _, p := range props {
|
|
out += fmt.Sprintf("%s\n", p)
|
|
}
|
|
}
|
|
|
|
return out, nil
|
|
}
|
|
|
|
/********************************************************************************
|
|
* Get Jails from datastores. Store config and running metadata
|
|
* 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)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fields := strings.Split(gDisplayJColumns, ",")
|
|
|
|
// 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)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} 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)
|
|
}
|
|
}
|
|
}
|
|
jails = js
|
|
}
|
|
|
|
/***************************************************************
|
|
/ Sort jails
|
|
/ We support 3 sort criteria max
|
|
/**************************************************************/
|
|
if len(gSortJailFields) > 0 && gSortJailFields != "none" {
|
|
js := initJailSortStruct()
|
|
|
|
// The way we manage criteria quantity is not very elegant...
|
|
var fct1, fct2, fct3 *reflect.Value
|
|
for i, c := range strings.Split(gSortJailFields, ",") {
|
|
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(gSortJailFields, ",")) {
|
|
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)
|
|
}
|
|
}
|
|
|
|
/***************************************************************
|
|
/ Finally, display jails
|
|
/**************************************************************/
|
|
if display {
|
|
displayJailsFields(jails, fields)
|
|
}
|
|
}
|
|
|
|
func listJailsFromDatastore(ds Datastore, args []string, accept_multiple bool) {
|
|
|
|
fileInfo, err := os.Stat(ds.Mountpoint)
|
|
if err != nil {
|
|
log.Fatalln(fmt.Sprintf("Unable to access %s, check path and/or rights", ds.Mountpoint))
|
|
}
|
|
if fileInfo.IsDir() == false {
|
|
log.Fatalln(fmt.Sprintf("%s is not a directory", ds.Mountpoint))
|
|
}
|
|
|
|
// A datastore have to contain a "jails" directory
|
|
jailsDir := fmt.Sprintf("%s/jails", ds.Mountpoint)
|
|
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, ds.Name)
|
|
}
|
|
|
|
func listJailsFromDirectory(dir string, dsname string) ([]Jail, error) {
|
|
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 {
|
|
fmt.Printf("ERROR reading jail config from %s\n", jailConfPath)
|
|
}
|
|
|
|
// 2. Build jail object from config
|
|
jailRootPath := fmt.Sprintf("%s/%s/%s", dir, fi.Name(), "root")
|
|
j := Jail{
|
|
Name: jailConf.Host_hostuuid,
|
|
Config: jailConf,
|
|
ConfigPath: jailConfPath,
|
|
Datastore: dsname,
|
|
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
|
|
// FIXME ??? Shouldn't be ioc-$Name ?
|
|
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
|
|
}
|
|
}
|
|
|
|
// 4. Get Zpool
|
|
/* 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]
|
|
}
|
|
|
|
// 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
|
|
if false == strings.Contains(gDisplayJColumns, "Datastore") {
|
|
gDisplayJColumns += ",Datastore"
|
|
}
|
|
}
|
|
}
|
|
|
|
gJails = append(gJails, j)
|
|
}
|
|
}
|
|
|
|
return gJails, nil
|
|
}
|
|
|
|
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))
|
|
}
|
|
|
|
// Mandatory constructor to init default values
|
|
jc, err := NewJailConfig()
|
|
if err != nil {
|
|
return jc, err
|
|
}
|
|
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
|
|
}
|