347 lines
8.5 KiB
Go
347 lines
8.5 KiB
Go
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 = ""
|
|
}
|
|
}
|