package cmd import ( "os" "fmt" //"log" "time" "errors" "strings" cp "github.com/otiai10/copy" log "github.com/sirupsen/logrus" ) // TODO : Add a flag to specify which parts of freebsd base we want : Slim jail only need base.txz, neither lib32 nor src.txz func CreateJail(args []string) { var err error var jtype []string if len(gCreateArgs.JailType) > 0 { jtype = []string{gCreateArgs.JailType} } for _, jname := range args { // Check if jail exist and is distinctly named _, err = getJailFromArray(jname, jtype, gJails) if err != nil { if strings.EqualFold(err.Error(), "Jail not found") { } else { fmt.Printf("ERROR: %s\n", err.Error()) return } } else { fmt.Printf("Jail exist: %s\n", jname) continue } fmt.Printf(" > create jail %s\n", jname) var ds *Datastore if len(gCreateArgs.Datastore) > 0 { fmt.Printf("DEBUG: Use %s datastore\n", gCreateArgs.Datastore) ds, err = getDatastoreFromArray(gCreateArgs.Datastore, gDatastores) if err != nil { fmt.Printf("ERROR Getting datastore: %s\n", gCreateArgs.Datastore, err.Error()) return } } else { ds = &gDatastores[0] } // Get base template if specified if len(gCreateArgs.BaseTemplate) > 0 { /************************************************************************** * Create based jail from a template */ log.Debugf("Jail will be created from a base template\n") bj, err := getJailFromArray(gCreateArgs.BaseTemplate, []string{"template"}, gJails) if err != nil { if strings.EqualFold(err.Error(), "Jail not found") { fmt.Printf("Template not found\n") return } else { fmt.Printf("ERROR: %s\n", err.Error()) return } } // Create jail datasets dstDset := fmt.Sprintf("%s/jails/%s", ds.ZFSDataset, jname) fmt.Printf(" > Initialize dataset %s\n", dstDset) err = zfsCreateDataset(dstDset, "", "") if err != nil { fmt.Printf("ERROR creating dataset %s: %s\n", dstDset, err.Error()) return } // Create jail root datasets dstRootDset := fmt.Sprintf("%s/jails/%s/root", ds.ZFSDataset, jname) fmt.Printf(" > Initialize dataset %s\n", dstRootDset) err = zfsCreateDataset(dstRootDset, "", "") if err != nil { fmt.Printf("ERROR creating dataset %s: %s\n", dstRootDset, err.Error()) return } // Create needed directories with basejail permissions fmt.Printf(" > Create base read-only directories\n") dstRootDir := fmt.Sprintf("%s/jails/%s/root", ds.Mountpoint, jname) for _, d := range append(gBaseDirs, gEmptyDirs...) { dstPath := dstRootDir srcPath := bj.RootPath for _, cd := range strings.Split(d, "/") { srcPath = fmt.Sprintf("%s/%s", srcPath, cd) dstPath = fmt.Sprintf("%s/%s", dstPath, cd) _, err := os.Stat(dstPath) if errors.Is(err, os.ErrNotExist) { srcPerm, err := getPermissions(srcPath) if err != nil { fmt.Printf("ERROR getting permissions of %s: %s\n", srcPath, err.Error()) return } err = os.Mkdir(dstPath, srcPerm.Mode().Perm()) if err != nil { fmt.Printf("ERROR creating directory %s: %s\n", dstPath, err.Error()) return } } } } // Copy these from basejail fmt.Printf(" > Create base writable directories\n") for _, d := range gCopyDirs { err := cp.Copy(fmt.Sprintf("%s/%s", bj.RootPath, d), fmt.Sprintf("%s/%s", dstRootDir, d)) if err != nil { fmt.Printf("ERROR copying %s to %s: %s\n", fmt.Sprintf("%s/%s", bj.RootPath, d), fmt.Sprintf("%s/%s", dstRootDir, d), err.Error()) return } } /////////////////////////////////////////////////////////////////////// // Copy defaults.json... jailConfPath := fmt.Sprintf("%s/jails/%s/config.json", ds.Mountpoint, jname) err = copyFile(fmt.Sprintf("%s/defaults.json", ds.Mountpoint), jailConfPath) if err != nil { fmt.Printf("ERROR creating config.json: %s\n", err.Error()) return } /////////////////////////////////////////////////////////////////////// // ... and update it jailConf, err := getJailConfig(jailConfPath) if err != nil { log.Println("ERROR reading jail config from %s", jailConfPath) } // Build jail object from config jailRootPath := fmt.Sprintf("%s/jails/%s/%s", ds.Mountpoint, jname, "root") j := Jail{ Name: jailConf.Host_hostuuid, Config: jailConf, ConfigPath: jailConfPath, Datastore: ds.Name, RootPath: jailRootPath, Running: false, } // We need to store the basejail template. We could : // 1. Use "origin" ? // 2. Add a json item to config ("basejail_template" p.e.), but iocage would delete it once jail is started from iocage // 3. Add a gocage specific config ("config.gocage.json" p.e.) j.Config.Jailtype = "basejail" j.Config.Origin = bj.Name j.Config.Host_hostname = jname j.Config.Host_hostuuid = jname j.WriteConfigToDisk(false) /////////////////////////////////////////////////////////////////////// // Create fstab fstabHandle, err := os.Create(fmt.Sprintf("%s/jails/%s/fstab", ds.Mountpoint, jname)) if err != nil { fmt.Printf("ERROR creating fstab: %s", err.Error()) return } defer fstabHandle.Close() for _, d := range gBaseDirs { fmt.Fprintf(fstabHandle, "%s\t%s\tnullfs\tro\t0\t0\n", fmt.Sprintf("%s/%s", bj.RootPath, d), fmt.Sprintf("%s/%s", dstRootDir, d)) } fmt.Printf(" > Jail created!\n") } else { /************************************************************************** * Create normal jail with its own freebsd base */ log.Debugf("Creating jail with its own freebsd base\n") // First check if we got release on the same datastore _, err := os.Stat(fmt.Sprintf("%s/releases/%s/root", ds.Mountpoint, gCreateArgs.Release)) if os.IsNotExist(err) { fmt.Printf("ERROR: Release locally not available. Run \"gocage fetch\"\n") return } /////////////////////////////////////////////////////////////////////// // Create and populate jail filesystem from release dstDset := fmt.Sprintf("%s/jails/%s", ds.ZFSDataset, jname) fmt.Printf(" > Initialize dataset %s\n", dstDset) sNow := time.Now().Format("20060102150405") reldset := fmt.Sprintf("%s/releases/%s", ds.ZFSDataset, gCreateArgs.Release) err = zfsSnapshot(reldset, sNow) if err != nil { fmt.Printf("ERROR Creating snapshot of %s: %s\n", reldset, err.Error()) return } err = zfsCopy(fmt.Sprintf("%s@%s", reldset, sNow), dstDset) if err != nil { fmt.Printf("ERROR sending snapshot to %s: %s\n", dstDset, err.Error()) return } // Remove snapshot of release, then snapshot of destination dataset err = zfsDestroy(fmt.Sprintf("%s@%s", reldset, sNow)) if err != nil { fmt.Printf("ERROR destroying snapshot %s: %s\n", reldset, err.Error()) return } err = zfsDestroy(fmt.Sprintf("%s@%s", dstDset, sNow)) if err != nil { fmt.Printf("ERROR destroying snapshot %s: %s\n", dstDset, err.Error()) return } dstRootDset := fmt.Sprintf("%s/jails/%s/root", ds.ZFSDataset, jname) fmt.Printf(" > Initialize dataset %s\n", dstRootDset) relrootdset := fmt.Sprintf("%s/releases/%s/root", ds.ZFSDataset, gCreateArgs.Release) err = zfsSnapshot(relrootdset, sNow) if err != nil { fmt.Printf("ERROR Creating snapshot of %s: %s\n", relrootdset, err.Error()) return } err = zfsCopy(fmt.Sprintf("%s@%s", relrootdset, sNow), dstRootDset) if err != nil { fmt.Printf("ERROR sending snapshot to %s: %s\n", dstRootDset, err.Error()) return } // Remove snapshot of release, then snapshot of destination dataset err = zfsDestroy(fmt.Sprintf("%s@%s", relrootdset, sNow)) if err != nil { fmt.Printf("ERROR destroying snapshot %s: %s\n", relrootdset, err.Error()) return } err = zfsDestroy(fmt.Sprintf("%s@%s", dstRootDset, sNow)) if err != nil { fmt.Printf("ERROR destroying snapshot %s: %s\n", dstRootDset, err.Error()) return } /////////////////////////////////////////////////////////////////////// // Copy defaults.json... jailConfPath := fmt.Sprintf("%s/jails/%s/config.json", ds.Mountpoint, jname) err = copyFile(fmt.Sprintf("%s/defaults.json", ds.Mountpoint), jailConfPath) if err != nil { fmt.Printf("ERROR creating config.json: %s\n", err.Error()) return } /////////////////////////////////////////////////////////////////////// // ... and update it jailConf, err := getJailConfig(jailConfPath) if err != nil { log.Println("ERROR reading jail config from %s", jailConfPath) } // Build jail object from config jailRootPath := fmt.Sprintf("%s/jails/%s/%s", ds.Mountpoint, jname, "root") j := Jail{ Name: jailConf.Host_hostuuid, Config: jailConf, ConfigPath: jailConfPath, Datastore: ds.Name, RootPath: jailRootPath, Running: false, } j.Config.Release = gCreateArgs.Release j.Config.Host_hostname = jname j.Config.Host_hostuuid = jname j.Config.Jailtype = "jail" j.WriteConfigToDisk(false) /////////////////////////////////////////////////////////////////////// // Create fstab fstabHandle, err := os.Create(fmt.Sprintf("%s/jails/%s/fstab", ds.Mountpoint, jname)) if err != nil { fmt.Printf("ERROR creating fstab: %s", err.Error()) return } defer fstabHandle.Close() fmt.Printf(" > Jail created!\n") } } }