diff --git a/cmd/init.go b/cmd/init.go new file mode 100644 index 0000000..5e17159 --- /dev/null +++ b/cmd/init.go @@ -0,0 +1,153 @@ +package cmd + +import ( + "os" + "fmt" + "strings" + + "github.com/spf13/viper" + log "github.com/sirupsen/logrus" +) + +/******************************************************************************** + * Initialize datastore(s) /iocage, /iocage/jails + * Put defaults.json, + * Update it with hostid, interfaces, and maybe other necessary fields + * Initialize bridge + *******************************************************************************/ +func InitGoCage(args []string) { + // Create datastores + for _, dstore := range viper.GetStringSlice("datastore") { + log.Debugf("Ranging over %v\n", dstore) + dset, err := zfsGetDatasetByMountpoint(dstore) + if err != nil && strings.HasSuffix(err.Error(), "No such file or directory\"") { + if len(gZPool) == 0 { + log.Errorf("Datastore mountpoint \"%s\" does not exist. Specify a pool if you want to create it.", dstore) + return + } + // Create dataset /iocage + rootDSName := fmt.Sprintf("%s%s", gZPool, dstore) + log.Debugf("Creating dataset %s mounted on %s\n", rootDSName, dstore) + if err = zfsCreateDataset(rootDSName, dstore, ""); err != nil { + log.Errorf("Error creating dataset %s: %v\n", rootDSName, err) + return + } + // Create /iocage/jail, releases, templates + for _, l := range []string{"jails","releases","templates"} { + cds := fmt.Sprintf("%s/%s", rootDSName, l) + cmp := fmt.Sprintf("%s/%s", dstore, l) + log.Debugf("Creating dataset %s mounted on %s\n", cds, cmp) + if err = zfsCreateDataset(cds, cmp, ""); err != nil { + log.Errorf("Error creating dataset %s: %v\n", cds, err) + return + } + } + + // Create /iocage/defaults.json + exists, err := doFileExist(fmt.Sprintf("%s/defaults.json", dstore)) + if err != nil { + log.Errorf("Error checking defaults.json: %v\n", err) + return + } + if !exists { + if err = createDefaultsJson(dstore, gBridge); err != nil { + log.Errorf("%v\n", err) + } + } + } else if err != nil { + log.Errorf("Error checking datastore existence: %v\n", err) + return + } else { + log.Debugf("Datastore dataset exist: %s\n", dset) + } + } + // Check and create bridge + // FIXME: What if bridge name is invalid, as we already wrote it in defaults.json in dstore loop? + if len(gBridge) > 0 && len(gInterface) > 0 { + if err := initBridge(); err != nil { + log.Errorf("%v\n", err) + } + } +} + +func createDefaultsJson(rootDirectory string, bridge string) error { + hostid, err := os.ReadFile("/etc/hostid") + if err != nil { + log.Fatalf("Unable to read /etc/hostid: %v\n", err) + } + json := strings.Replace(gDefaultsJson, "TO-BE-REPLACED-WITH-HOSTID", strings.Trim(string(hostid), "\n"), 1) + json = strings.Replace(json, "TO-BE-REPLACED-WITH-BRIDGE", bridge, 1) + if err := os.WriteFile(fmt.Sprintf("%s/defaults.json", rootDirectory), []byte(json), 0640); err != nil { + log.Fatal(err) + } + + return nil +} + +func createInterface(iface string) error { + log.Debugf("creating interface \"%s\"\n", iface) + cmd := fmt.Sprintf("/sbin/ifconfig %s create", iface) + _, err := executeCommand(cmd) + if err != nil { + return err + } + return nil +} + +func bringUpInterface(iface string) error { + log.Debugf("bringing up interface \"%s\"\n", iface) + cmd := fmt.Sprintf("/sbin/ifconfig %s up", iface) + _, err := executeCommand(cmd) + if err != nil { + return err + } + return nil +} + +func addMemberToBridge(bridge string, iface string) error { + log.Debugf("adding member interface \"%s\" to bridge \"%s\"\n", iface, bridge) + cmd := fmt.Sprintf("/sbin/ifconfig %s addm %s", bridge, iface) + _, err := executeCommand(cmd) + if err != nil { + return err + } + return nil +} + +func initBridge() error { + hostInt, err := gJailHost.GetInterfaces() + if err != nil { + return fmt.Errorf("Error listing interfaces: %v\n", err) + } + if !isStringInArray(hostInt, gInterface) { + return fmt.Errorf("Interface not found: %s\n", gInterface) + } + if !isStringInArray(hostInt, gBridge) { + if err := createInterface(gBridge); err != nil { + return fmt.Errorf("Error creating bridge: %v\n", err) + } + if err := bringUpInterface(gBridge); err != nil { + return fmt.Errorf("Error bringing up bridge: %v\n", err) + } + log.Infof("bridge was created, but it won't persist reboot. Configure rc.conf to persist. See https://docs.freebsd.org/en/books/handbook/advanced-networking/#network-bridging\n") + log.Infof("It is strongly suggested you move interface %s IP to bridge %s\n", gInterface, gBridge) + } + // FIXME: Need to check if not already member + members, err := getBridgeMembers(gBridge) + if err != nil { + return fmt.Errorf("Error getting bridge members: %v\n", err) + } + + // Return if interface already member of the bridge + for _, m := range members { + log.Debugf("Bridge member: %s\n", m) + if strings.EqualFold(m, gInterface) { + return nil + } + } + if err := addMemberToBridge(gBridge, gInterface); err != nil { + return fmt.Errorf("Error adding interface to bridge: %v\n", err) + } + + return nil +}