package cmd import ( "os" "fmt" "log" "reflect" "strings" "io/ioutil" "gocage/jail" "encoding/json" "github.com/spf13/viper" ) /******************************************************************************** * List all properties a jail have, with their internal name *******************************************************************************/ func ListJailsProps(args []string) { var conf Jail var jailconf JailConfig var result []string conf.Config = jailconf result = getStructFieldNames(conf, result, "") for _, f := range result { fmt.Printf("%s\n", f) } } /******************************************************************************** * Get Jails from datastores. Store config and running metadata * into gJails global var *******************************************************************************/ func ListJails(args []string, display bool) { fields := strings.Split(gDisplayColumns, ",") for _, d := range viper.GetStringSlice("datastore") { listJailsFromDatastore(d) } // 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) break } } } jails = js } /*************************************************************** / Sort jails / We support 3 sort criteria max /**************************************************************/ if len(gSortFields) > 0 && gSortFields != "none" { 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 /**************************************************************/ if display { displayStructFields(jails, fields) } } 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 break } } /* 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] } 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 }