package cmd import ( "fmt" "os" // "log" "errors" "os/exec" "regexp" // "reflect" "strings" ) // TODO : Use SYS_RCTL_GET_RACCT syscall func removeRctlRules(jail string, rules []string) error { var cmd []string if len(rules) == 0 { rules[0] = "" } for _, r := range rules { if gUseSudo { cmd = append(cmd, "sudo") } cmd = append(cmd, "/usr/bin/rctl") cmd = append(cmd, "-r") cmd = append(cmd, fmt.Sprintf("jail:%s:%s", jail, r)) // TODO : Log in another channel than stdout (will scramble display) //log.Println(fmt.Sprintf("Removing all rules for jail %s: %s", jail, cmd)) //out, err := exec.Command(cmd[0], cmd[1:]...).Output() _, err := exec.Command(cmd[0], cmd[1:]...).Output() return err } return nil } // TODO: Validate with >1 dataset func umountAndUnjailZFS(jail *Jail) error { var ds []string // Make sure we have a string array ds = append(ds, jail.Config.Jail_zfs_dataset) for _, zd := range ds { // 1. Get dataset and childs cmd := fmt.Sprintf("zfs list -H -r -o name -S name %s/%s", jail.Zpool, zd) out, err := executeCommand(cmd) if err != nil { fmt.Printf(fmt.Sprintf("ERROR listing dataset %s/%s\n", jail.Zpool, zd)) os.Exit(1) } for _, c := range strings.Split(out, "\n") { if len(c) == 0 { continue } fmt.Printf("Unmounting dataset %s: ", c) cmd := fmt.Sprintf("zfs umount %s", c) _, err := executeCommandInJail(jail, cmd) if err != nil { return err } fmt.Printf("OK\n") } } // 2. Unjail dataset from the host cmd := fmt.Sprintf("zfs unjail %s %s/%s", jail.InternalName, jail.Zpool, ds[len(ds)-1]) _, err := executeCommand(cmd) if err != nil { fmt.Printf("ERROR unjailing %s/%s: %s\n", jail.Zpool, ds[len(ds)-1], err.Error()) return err } return nil } func destroyVNetInterfaces(jail *Jail) error { for _, i := range strings.Split(jail.Config.Ip4_addr, ",") { iname := fmt.Sprintf("%s.%d", strings.Split(i, "|")[0], jail.JID) fmt.Printf("%s: ", iname) _, err := executeCommand(fmt.Sprintf("ifconfig %s destroy", iname)) if err != nil { return err } else { fmt.Printf("OK\n") } } return nil } // Jails copy the ruleset referenced as "devfs_ruleset" when starting, getting a new devsf_ruleset ID. // This new ID can be obtained with 'jls -j $JID devfs_ruleset' // This is the ID which needs to be removed. Original ID referenced is json should not be deleted // or else it will require a restart of "devfs" service. // But, stoppign the jail already removes this >1000 ID. // So no need to call this function. func deleteDevfsRuleset(jail *Jail) error { cmd := "devfs rule showsets" out, err := executeCommand(cmd) if err != nil { return errors.New(fmt.Sprintf("ERROR listing rulesets: %s", err.Error())) } for _, r := range strings.Split(out, "\n") { if r == jail.Config.Devfs_ruleset { cmd := fmt.Sprintf("devfs rule -s %s delset", jail.Config.Devfs_ruleset) _, err := executeCommand(cmd) return err } } return nil } func umountJailFsFromHost(jail *Jail, mountpoint string) error { cmd := "mount -p" out, err := executeCommand(cmd) if err != nil { return errors.New(fmt.Sprintf("Error executing mount: %s", err.Error())) } remSpPtrn := regexp.MustCompile(`\s+`) for _, l := range strings.Split(out, "\n") { f := strings.Split(remSpPtrn.ReplaceAllString(l, " "), " ") if len(f) > 2 { if strings.EqualFold(f[1], fmt.Sprintf("%s%s", jail.RootPath, mountpoint)) { cmd = fmt.Sprintf("umount %s%s", jail.RootPath, mountpoint) _, err := executeCommand(cmd) if err != nil { return errors.New(fmt.Sprintf("Error umounting %s%s: %s", jail.RootPath, mountpoint, err.Error())) } return nil } } } return nil } // Internal usage only func stopJail(jail *Jail) error { cmd := "jail -q" // Test if conf file exist (iocage created) cf := fmt.Sprintf("/var/run/jail.%s.conf", jail.InternalName) file, err := os.Open(cf) if err != nil { file.Close() cmd = fmt.Sprintf("%s -f %s", cmd, cf) } cmd = fmt.Sprintf("%s -r %s", cmd, jail.InternalName) _, err = executeCommand(cmd) if err != nil { return err } return nil } /* Stop jail: Remove rctl rules Execute prestop if set (jailhost perimeter) Execute stop if set (inside jail) Umount ZFS dataset from inside jail Unjail ZFS dataset from jailhost If VNet Delete VNet interface on host Delete devfs ruleset Effectively stop jail process Umount all mountpoints from $jail/fstab Umount proc if set Umount linprocfs if set Umount fdescfs if set Umount devfs if set Use setfib for each command Shouldnt rctl rules be removed last, when jail is stopped? */ func StopJail(args []string) { // Current jail were stopping var cj *Jail for _, j := range args { fmt.Printf("> Stopping jail %s\n", j) for _, rj := range gJails { if rj.Name == j { cj = &rj break } } if cj == nil { fmt.Printf("Jail not found: %s\n", j) continue } if cj.Running == false { fmt.Printf("Jail %s is not running!\n", cj.Name) continue } out, err := executeCommand(fmt.Sprintf("rctl jail:%s", cj.InternalName)) if err == nil && len(out) > 0 { fmt.Printf(" > Remove RCTL rules:\n") err := removeRctlRules(cj.InternalName, []string{""}) if err != nil { fmt.Printf("ERROR: %s\n", err.Error()) } else { fmt.Printf(" > Remove RCTL rules: OK\n") } } if len(cj.Config.Exec_prestop) > 0 { fmt.Printf(" > Execute prestop:\n") _, err := executeCommand(cj.Config.Exec_prestop) if err != nil { fmt.Printf("ERROR: %s\n", err.Error()) } else { fmt.Printf(" > Execute prestop: OK\n") } } if len(cj.Config.Exec_stop) > 0 { fmt.Printf(" > Execute stop:\n") _, err := executeCommandInJail(cj, cj.Config.Exec_stop) if err != nil { fmt.Printf("ERROR: %s\n", err.Error()) } else { fmt.Printf(" > Execute stop: OK\n") } } if cj.Config.Jail_zfs > 0 { fmt.Printf(" > Umount jailed ZFS:\n") err := umountAndUnjailZFS(cj) if err != nil { fmt.Printf("ERROR: %s\n", err.Error()) } else { fmt.Printf(" > Umount jailed ZFS: OK\n") } } if cj.Config.Vnet > 0 && len(cj.Config.Ip4_addr) > 0 { fmt.Printf(" > Destroy VNet interfaces:\n") err := destroyVNetInterfaces(cj) if err != nil { fmt.Printf("ERROR: %s\n", err.Error()) } else { fmt.Printf(" > Destroy VNet interfaces: OK\n") } } /*fmt.Printf(" > Remove devfsruleset %s:\n", cj.Config.Devfs_ruleset) err = deleteDevfsRuleset(cj) if err != nil { fmt.Printf("ERROR: %s\n", err.Error()) } else { fmt.Printf(" > Remove devfsruleset %s: OK\n", cj.Config.Devfs_ruleset) }*/ fmt.Printf(" > Stop jail %s:\n", cj.Name) err = stopJail(cj) if err != nil { fmt.Printf("ERROR: %s\n", err.Error()) } else { fmt.Printf(" > Stop jail %s: OK\n", cj.Name) } if cj.Config.Mount_procfs > 0 { fmt.Printf(" > Umount procfs:\n") err := umountJailFsFromHost(cj, "/proc") if err != nil { fmt.Printf("ERROR: %s\n", err.Error()) } else { fmt.Printf(" > Umount procfs: OK\n") } } if cj.Config.Mount_linprocfs > 0 { fmt.Printf(" > Umount linprocfs:\n") err := umountJailFsFromHost(cj, "/compat/linux/proc") if err != nil { fmt.Printf("ERROR: %s\n", err.Error()) } else { fmt.Printf(" > Umount linprocfs: OK\n") } } // FIXME: /dev/fd is mounted even with Mount_fdescfs = 0 ?! //if cj.Config.Mount_fdescfs > 0 { fmt.Printf(" > Umount fdescfs:\n") err = umountJailFsFromHost(cj, "/dev/fd") if err != nil { fmt.Printf("ERROR: %s\n", err.Error()) } else { fmt.Printf(" > Umount fdescfs: OK\n") } //} if cj.Config.Mount_devfs > 0 { fmt.Printf(" > Umount devfs:\n") err := umountJailFsFromHost(cj, "/dev") if err != nil { fmt.Printf("ERROR: %s\n", err.Error()) } else { fmt.Printf(" > Umount devfs: OK\n") } } // Remove local mounts from $JAIL/fstab fstab := strings.Replace(cj.ConfigPath, "config.json", "fstab", 1) mounts, err := getFstab(fstab) if err != nil { fmt.Printf("ERROR: %s\n", err.Error()) } if len(mounts) > 0 { fmt.Printf(" > Umount mountpoints from %s\n", fstab) errs := 0 for _, m := range mounts { err = umountJailFsFromHost(cj, m.Mountpoint) if err != nil { fmt.Printf("ERROR: %s\n", err.Error()) errs += 1 } } if errs == 0 { fmt.Printf(" > Umount mountpoints from %s: OK\n", fstab) } } // Remove parameter file pfile := fmt.Sprintf("/var/run/jail.%s.conf", cj.InternalName) if err = os.Remove(pfile); err != nil { fmt.Printf("Error deleting parameter file %s\n", pfile) } cj.InternalName = "" } }