Full Code of pizzamig/pot for AI

master 5ef4db1cb181 cached
189 files
922.2 KB
311.3k tokens
1 symbols
1 requests
Download .txt
Showing preview only (975K chars total). Download the full file or copy to clipboard to get everything.
Repository: pizzamig/pot
Branch: master
Commit: 5ef4db1cb181
Files: 189
Total size: 922.2 KB

Directory structure:
gitextract_cytad9_s/

├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   └── workflows/
│       ├── ci.yml
│       ├── main.yml
│       └── shellcheck.yml
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── Jenkinsfile
├── LICENSE
├── README.md
├── bin/
│   └── pot
├── etc/
│   ├── pot/
│   │   ├── flavours/
│   │   │   ├── dns
│   │   │   ├── dns.sh
│   │   │   ├── fbsd-update.sh
│   │   │   └── slim.sh
│   │   ├── pot.conf.sample
│   │   └── pot.default.conf
│   └── rc.d/
│       ├── pot
│       └── pot_early
├── release.sh
├── share/
│   ├── doc/
│   │   └── pot/
│   │       ├── .gitignore
│   │       ├── Description.md
│   │       ├── Images.md
│   │       ├── Installation.md
│   │       ├── Makefile
│   │       ├── QuickStart.md
│   │       ├── Synopsis.md
│   │       ├── conf.py
│   │       ├── index.md
│   │       └── migration.md
│   ├── pot/
│   │   ├── add-dep.sh
│   │   ├── clone-fscomp.sh
│   │   ├── clone.sh
│   │   ├── common-flv.sh
│   │   ├── common.sh
│   │   ├── config.sh
│   │   ├── copy-in.sh
│   │   ├── copy-out.sh
│   │   ├── create-base.sh
│   │   ├── create-fscomp.sh
│   │   ├── create-private-bridge.sh
│   │   ├── create.sh
│   │   ├── de-init.sh
│   │   ├── destroy.sh
│   │   ├── exec.sh
│   │   ├── export-ports.sh
│   │   ├── export.sh
│   │   ├── get-attribute.sh
│   │   ├── get-rss.sh
│   │   ├── help.sh
│   │   ├── import.sh
│   │   ├── info.sh
│   │   ├── init.sh
│   │   ├── last-run-stats.sh
│   │   ├── list.sh
│   │   ├── mount-in.sh
│   │   ├── mount-out.sh
│   │   ├── network.sh
│   │   ├── prepare.sh
│   │   ├── prune.sh
│   │   ├── ps.sh
│   │   ├── purge-snapshots.sh
│   │   ├── rename.sh
│   │   ├── revert.sh
│   │   ├── set-attribute.sh
│   │   ├── set-cmd.sh
│   │   ├── set-env.sh
│   │   ├── set-hook.sh
│   │   ├── set-hosts.sh
│   │   ├── set-rss.sh
│   │   ├── set-status.sh
│   │   ├── show.sh
│   │   ├── signal.sh
│   │   ├── snapshot.sh
│   │   ├── start.sh
│   │   ├── stop.sh
│   │   ├── term.sh
│   │   ├── top.sh
│   │   ├── update-config.sh
│   │   ├── version.sh
│   │   └── vnet-start.sh
│   └── zsh/
│       └── site-functions/
│           └── _pot
└── tests/
    ├── CI/
    │   ├── resolv.conf-dual
    │   ├── resolv.conf-ipv4
    │   ├── resolv.conf-ipv6
    │   └── run.sh
    ├── add-dep1.sh
    ├── clone-fscomp1.sh
    ├── clone1.sh
    ├── clone2.sh
    ├── common-flv1.sh
    ├── common-stub.sh
    ├── common-zfs1.sh
    ├── common-zfs2.sh
    ├── common1.sh
    ├── common2.sh
    ├── common4.sh
    ├── common5.sh
    ├── common6.sh
    ├── common7.sh
    ├── common8.sh
    ├── conf-stub.sh
    ├── config1.sh
    ├── copy-in1.sh
    ├── copy-out1.sh
    ├── create-base1.sh
    ├── create-fscomp1.sh
    ├── create-private-bridge1.sh
    ├── create1.sh
    ├── create2.sh
    ├── create3.sh
    ├── destroy1.sh
    ├── export-ports1.sh
    ├── export1.sh
    ├── get-rss1.sh
    ├── import1.sh
    ├── info1.sh
    ├── info2.sh
    ├── list1.sh
    ├── list2.sh
    ├── monitor.sh
    ├── mount-in1.sh
    ├── mount-out1.sh
    ├── network1.sh
    ├── pipefail-stub.sh
    ├── ps1.sh
    ├── rename1.sh
    ├── rename2.sh
    ├── rename3.sh
    ├── rename4.sh
    ├── revert1.sh
    ├── set-attr1.sh
    ├── set-attr2.sh
    ├── set-cmd1.sh
    ├── set-env1.sh
    ├── set-env2.sh
    ├── set-hook1.sh
    ├── set-hosts1.sh
    ├── set-rss1.sh
    ├── show1.sh
    ├── shunit/
    │   ├── .gitignore
    │   ├── .travis.yml
    │   ├── CODE_OF_CONDUCT.md
    │   ├── LICENSE
    │   ├── README.md
    │   ├── doc/
    │   │   ├── CHANGES-2.1.md
    │   │   ├── RELEASE_NOTES-2.1.0.txt
    │   │   ├── RELEASE_NOTES-2.1.1.txt
    │   │   ├── RELEASE_NOTES-2.1.2.txt
    │   │   ├── RELEASE_NOTES-2.1.3.txt
    │   │   ├── RELEASE_NOTES-2.1.4.txt
    │   │   ├── RELEASE_NOTES-2.1.5.txt
    │   │   ├── RELEASE_NOTES-2.1.6.txt
    │   │   ├── RELEASE_NOTES-2.1.7.md
    │   │   ├── TODO.txt
    │   │   ├── contributors.md
    │   │   └── design_doc.txt
    │   ├── examples/
    │   │   ├── equality_test.sh
    │   │   ├── lineno_test.sh
    │   │   ├── math.inc
    │   │   ├── math_test.sh
    │   │   ├── mkdir_test.sh
    │   │   ├── mock_file.sh
    │   │   ├── mock_file_test.sh
    │   │   ├── party_test.sh
    │   │   └── suite_test.sh
    │   ├── lib/
    │   │   ├── shflags
    │   │   └── versions
    │   ├── shunit2
    │   ├── shunit2_args_test.sh
    │   ├── shunit2_asserts_test.sh
    │   ├── shunit2_failures_test.sh
    │   ├── shunit2_macros_test.sh
    │   ├── shunit2_misc_test.sh
    │   ├── shunit2_standalone_test.sh
    │   ├── shunit2_test_helpers
    │   └── test_runner
    ├── signal1.sh
    ├── signal2.sh
    ├── snapshot1.sh
    ├── start2.sh
    ├── start3.sh
    ├── start4.sh
    ├── stop1.sh
    ├── term1.sh
    ├── test-suite.sh
    ├── top1.sh
    └── version1.sh

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Report a bug you found
title: "[BUG]"
labels: bug
assignees: ''

---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. Create the `pot` with this command: '...'
2. Run this command: '...'
3. See error

**Expected behavior**
A clear and concise description of what you expected to happen.

**System configuration - if possible**
 - `/usr/local/etc/pot/pot.conf`

** If network related **
 - `cat /etc/pf.conf`
 - `potnet show -v`

**Additional context**
Add any other context about the problem here.


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: A nice to have
title: ''
labels: feature
assignees: ''

---

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the feature you'd like to have**
A clear and concise description of what you want to happen.

**Describe potential alternatives or workaround you've considered (if any)**
A clear and concise description of any alternative solutions or workaround you've considered.


================================================
FILE: .github/workflows/ci.yml
================================================
name: Run CI
on:
  workflow_dispatch:
  push:
    branches: [ github-ci-action ]
#  pull_request:
#    branches: [ master ]

jobs:
  ci_test_suite:
    name: CI test suite
    runs-on: ${{ matrix.job.os }}
    strategy:
      fail-fast: false
      matrix:
        job:
          - { os: macos-12 }
        release: [ "13.1" ]
    steps:
    - uses: actions/checkout@v3
    - name: Run tests/CI/run.sh
      uses: vmactions/freebsd-vm@v0
      with:
        mem: 8192
        usesh: true
        copyback: false
        prepare: pkg install -y curl gmake potnet freebsd-release-manifests nmap
        run: |
          #####################################################################################
          ###  Prepare, build, and test
          #####################################################################################
          ###  based on ref: <https://github.com/rust-lang/rustup/pull/2783>
          ###  and on ref: <https://github.com/uutils/coreutils/commit/86c610a84b8b6c>
          ###  * NOTE: All steps need to be run in this block, otherwise, we are operating back
          ###    on the mac host
          set -exo pipefail
          #
          ### Basic user setup ################################################################
          TEST_USER=tester
          TEST_USER_HOME="/opt/$TEST_USER"
          REPO_NAME=${GITHUB_WORKSPACE##*/}
          WORKSPACE_PARENT="/Users/runner/work/${REPO_NAME}"
          WORKSPACE="${WORKSPACE_PARENT}/${REPO_NAME}"
          OS_VERSION="$(freebsd-version | awk -F- '{print $1}')"
          PUB_INTF="$(netstat -4rn | grep default | awk '{ print $4}')"
          ifconfig
          #
          mkdir -p "$TEST_USER_HOME"
          pw adduser -n "$TEST_USER" -d "$TEST_USER_HOME" -c "Tester" -h -
          chown -R "$TEST_USER":"$TEST_USER" "$TEST_USER_HOME"
          chown -R "$TEST_USER":"$TEST_USER" "/$WORKSPACE_PARENT"/
          whoami
          #
          ### Output some information about the environment  ##################################
          # environment
          echo "## environment"
          env | sort
          # tooling info
          echo "## installed packages"
          pkg info
          #
          ### Create zpool ####################################################################
          dd if=/dev/zero of=/zfs1 bs=1 count=1 seek=2G
          zdev=$(mdconfig -a -t vnode -S 4096 -f /zfs1)
          zpool create -f zroot "$zdev"
          #
          ### Configure pf and pot ############################################################
          echo "set skip on lo0" >/etc/pf.conf
          echo pass >>/etc/pf.conf
          service pf enable
          service pf start
          pw groupadd pot
          bin/pot init
          #
          ### Run CI tests ################################################
          cd "$WORKSPACE"
          cp -f etc/pot/pot.default.conf etc/pot/pot.conf
          cd tests/CI
          set +e
          FAULT=0
          ./run.sh || FAULT=1
          echo "Log output:"
          cat pot-ci-*
          if [ $FAULT -ne 0 ]; then exit 1; fi
          #
          ### Finished ########################################################################
          echo "Done"


================================================
FILE: .github/workflows/main.yml
================================================
# This is a basic workflow to help you get started with Actions

name: unit-test

# Controls when the action will run. Triggers the workflow on push or pull request
# events but only for the master branch
on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  build:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
    # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
    - uses: actions/checkout@v2

    # Runs a single command using the runners shell
    - name: Run a one-line script
      run: cd tests && ./test-suite.sh


================================================
FILE: .github/workflows/shellcheck.yml
================================================
name: Shellcheck

on: [ pull_request ]

jobs:
  shellcheck:
    name: Shellcheck
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Run ShellCheck
      uses: ludeeus/action-shellcheck@master
      with:
        scandir: share/pot
        additional_files: bin/pot


================================================
FILE: .gitignore
================================================
/doc
*.swp
*~
/etc/pot/pot.conf
/etc/pot/flavours/*
!/etc/pot/flavours/dns.sh
!/etc/pot/flavours/dns
!/etc/pot/flavours/slim.sh
!/etc/pot/flavours/fbsd-update.sh
devenv.sh
/tests/CI/pot-ci*


================================================
FILE: .travis.yml
================================================
language: bash
script: cd tests && ./test-suite.sh


================================================
FILE: CHANGELOG.md
================================================
# Changelog
All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## Unreleased
### Added
- import/prepare: Support setting a default signature public key to verify pot signature (#296)

### Fixed
- prune: Only attempt to stop pot if it is running (#295)

## [0.16.0] 2023-12-30
### Added
- tinirc: Write tinirc's pid to /tmp/tinirc.pid (#277)
- set-attr/stop: Add attributes exec_stop and stop_timeout (#275)
- init/de-init: Add flag "-m" to be minimally intrusive, add flag -p to specify pf file (#284)
- init: Add flag -s to not alter syslogd settings, deprecate flag -f pf_file, as it is replaced by -p (#284)
- vnet: Add global configuration POT_ISOLATE_VNET_POTS to prevent direct traffic between VNET pots (#283)

### Fixed
- tinirc: Overwrite tinirc on start instead of appending to an existing file (#277)
- start: Fix setting of nullfs attribute
- set-status: Ignore status files that predate system boot (#278)
- set-status: Forward verbosity flags (#279)
- network: Find bridge interfaces by interface group, this allows custom bridge names (#282)

## [0.15.6] 2023-09-29
### Added
- start: Add custom pf rule configuration hook, POT_EXPORT_PORTS_PF_RULES_HOOK (#273)
- Remove basepath from mountpoint, make mount-in/out errors more visible (#259)

## [0.15.5] 2023-06-29
### Added
- set-attr: Add support for setting devfs_ruleset (#270)
- set-attr: Add support for setting mlock, sysvshm, sysvsem, sysvmsg, retire sysvipc attribute, which was always a noop (#263)

### Fixed
- pot-cmd: Output problems with pot root to stderr (#254)
- version: Don't require pot root to exist to run version command (#253)
- mount-in: Skip empty lines in fscomp.conf during mount process (#258)

## [0.15.4] 2022-12-15
### Added
- set-attr: add jail attributes "raw_sockets", "sysvipc" (#247, #248)
- import/export/prepare: support signing pots (#221)

### Changed
- flavours: scripts are made executable when loading
- destroy: remove status file when destroying
- vnet: use unique epaira interface names (#232)
- Add pot group to protect pot root (#240)

### Fixed
- Reverted the change of permissions of pot root mountpoint to fix a regression (#233)
- set-attr: fix no-etc-hosts attribute handling
- Remove leftover mount points on destroy (#236)
- set-attr/get-attr: fix help output (#245)
- Fix running flavour script on non-persistent pot (#238)

## [0.15.3] 2022-09-17
### Fixed
- stop: Destroy epair interface if stop is not called from start (#229)

## [0.15.2] 2022-09-17
### Fixed
- start: fix pot getting stuck in state "starting" on pot start failure (#227)

## [0.15.1] 2022-09-16
### Fixed
- set-status: fix a bug that deletes the status (#224)

## [0.15.0] 2022-09-11
### Added
- mount-out: new command to remove or unmount a previously mount-in folder or fs
- attribute no-tmpfs: an attribute, for single dataset only, to not use tmpfs for /tmp
- create/import: inherit ZFS encryption property from parent filesystem (#196)
- attribute no-etchosts: an attribute, to not inject additional /etc/hosts entries from `potnet`
- last-run-stats: new command to get statistics on the last run of a pot, currently contains "ExitCode", which is the exit code of pot.cmd (#200)
- start: return with code 125 in case pot.cmd of a non-persistent pot failed (#200)
- tinirc: wait for epair interface, exit early if it doesn't become available (#204)
- ifconfig: label and group interfaces created by pot (#206)
- clone: add dns option, to customize DNS configuration while cloning (#199)
- prepare: add -d option to change dns configuration during clone (#192)
- signal: send signals to processes running inside a pot (#216)
- exec: command to execute programs inside a running pot (#217)

### Changed
- Stop logging trivial commands like get-rss to syslog by default (#190)
- get-rss: test if the pot is running, instead of it only exists during input validation
- mount-in: mountpoint cannot contain spaces anymore (#187)
- start: allow pots to run for less than 5 seconds (#200)
- start: always stop and cleanup non-persistent pots once pot.cmd finished, prevents stray background tasks from keeping them alive (#200)
- prune: add flag "-g" to delay pruning of pots that just stopped, so users have a chance to inspect last-run-stats (#200)
- help: rework usage screens (#209)
- prepare: enable attribute no-tmpfs and no-etc-hosts (#192)
- tests: improved monitoring of tests, requires sysutils/flock on FreeBSD (#220)
- Change permissions of pot root mount point to be only accessible by root user (#218)

### Fixed
- start: correct invocation of prestart and poststart hooks (#200)
- tinirc: configure address selection policy (#205)
- fdescfs/procfs: fixed the correcte behavior of those attribute, such as mount them at start
- mount-out: fix it
- clone: fix cleanup after failed clone (#214)
- start/stop: heavy rework to fix concurrency (#202)

## [0.14.0] 2021-10-31
### Added
- copy-in: -c option to create missing dirs on copy-in (#172)
- create: New command copy-in-flv, which is the same as copy-in, but always relative to flavourdir (#173)
- init: -f option to specify pf file to patch on init (#181)

### Changed
- start: do not write jid files to POT_TMP (#178)
- start/stop: remove pot_stopped files from TMP_DIR after stopping non-persistent jails (#179)

### Fixed
- prepare: fix -N option to allow network-type "host" as used by nomad-pot-driver (#177)
- copy-in: fix tmp source directory creation

## [0.13.0] 2021-09-21
### Added
- import-export: add support for layered images (#151)
- POT_TMP: add a parameter to select the folder used to create temporary files
- flavour: -f option support a full pathname (#161)
- copy-out: new command to copy file or folder out from a pot (#162)

### Changed
- start: simplify startup, use jexec to run pot.cmd (#150)
- flavour: the current directory is added to the flavour search path (#161)

### Fixed
- start/stop: prevent stopping non-persistent jails twice (#152)
- stop: garbage collect POSIX shared memory (#150)
- start: fix ncat failing to start due to argv handling (#167)

## [0.12.0] 2021-05-22
### Added
- pot.conf: add parameter to control max hostname length inside the pot (#118)
- CI: add shellcheck as hard requirements in the PR flow
- export-ports: add UDP support, as -e udp:53:53 (#115)
- create: dns custom allows to statically provide a resolv.conf
- POT_EXTIF_ADDR: new parameter to force which IP of EXTIF should be used for NAT and RDR
- clone: add support for applying flavors to cloned pots
- clone: add -k flag to keep the cloned pot for debug when the process failed
- info: -s to list available snapshots of a pot
- clone: -s flag to explicitly choose the snapshot to clone
- architecture: remove limitation of amd64 as the only architecture supported (#143 by jmg@)
- start/stop/term/run: add support to -p potname on those commands, the only one not supporting it

### Changed
- hostname: max default length for hostname set to 64 (#118)
- create: adopt the new hostname length parameter (#118)
- clone: adopt the new hostname length parameter (#118)
- ext-if: do not include interface aliases in the bridges network if EXTIF has them (#120)
- start: add support for custom dns resolver
- init: create backup of rc.conf and pf.conf before to apply pot related changes
- info: -B instead of -b for private bridge information
- copy-in: copy is executed in the jail environment, to avoid soft-link related issues in the destination path
- copy-in: with running pots, a -F flag is needed to force the copy, an operation that is discouraged for security reasons
- fetch base.txz: the base FreeBSD tarball used to be temporarily stored in /tmp. While a POT_CACHE folder is available, use that instead.

### Removed
- create-dns: remove this already deprecated command, leaving the user to create a dns for the public bridge

### Fixed
- zsh: fix autocompletion for set-hook (#139 by urosgruber)

## [0.11.6] 2020-12-14
### Fixed
- stop: remove resolv.conf only if dns is not off (#117)

## [0.11.5] 2020-11-21
### Added
- create: dns off allows to skip the resolv.conf configuration

### Fixed
- start: (FreeBSD 12.2) pf fails to load rdr rule in some cases

## [0.11.4] 2020-09-12
### Added
- set-attr: add many jails attributes: enforce_stats mount fdescfs libprocfs nullfs procfs tmpfs zfs children

### Fixed
- localhost-tunnel: fix multiple port support (#108)

## [0.11.3] 2020-08-03
### Changed
- start: remove temporary files (#91 #92)

### Fixes
- clone: fix a typo refactoring the grep that remove network parameters (#90)
- mount-in: fix mountpoint validation when pot is stopped and -v is passed (#93)
- clone: hooks have been ignored by clone (#94)
- info: fix withespace quoting with -E flag (#95)
- prepare: fix -i command to allow multiple IP addresses (#97)
- ifconfig: force IFCONFIG_FORMAT to avoid conflicting user setting (#99)

## [0.11.2] 2020-05-01
### Added
- prepare: the -S option is now used to specify the network stack

### Changed
- prepare: the -S flag to start the imported pot changed in -s

### Fixed
- fbsd-update: don't assume there is a tty (#86)
- clone: duplicate the entry pot.stack (#88)

## [0.11.1] 2020-04-19
### Fixed
- set-attr: attribute early-start-at-boot is now correctly recognized

## [0.11.0] 2020-04-19
### Added
- create-base: automatically call freebsd-update when a base is created (#83)
- attribute early-start-at-boot: for pot needed to start early at boot (REQUIRE: NETWORKING syslogd pf)
- create: add a -k flag to keep the pot, even if it's creation process failed
- network stack: add network stack as framework concept (ipv4, ipv6 or dual)
- CI: import the run.sh script, with regression system tests
- alias: the new notation -i can be repeated to assign multiple IPs to different NICs
- hooks: added variables to provide the full new alias network configuration

### Changed
- osrelease: detect it from freebsd-version, deprecating the osrelease field in pot.conf (#83)
- start-at-boot: the pot rc.d service will be executed late, with jail
- create: if create fails, the partially created pot is automatically destroyed
- create: -P will use send/receive from a snapshot, to cut the dependency with the snapshot
- create-multi: usr.local and custom dataset are send/received instead of cloned
- inherit: it inherits the stack configured in pot.conf
- alias: extend -i option to accept netif|ipaddr
- alias: -i option can be repeated more than once to add more ip addresses to the same instance
- syslogd: initial removal of syslogd forwarding

### Removed
- alias: remove option -I, in favour of a more flexible and powerful -i
- export: remove option -s, to select a specific snapshot (already deprecated)
- snapshot: remove option -n, to specify a snapshot name (already deprecated)
- snapshot: remove flag -a, to snapshot external ZFS datasets (already deprecated)
- revert: remove flag -a, to restore external ZFS datasets (already deprecated)

### Fixed
- mount-in: compute the realpath of the mount-point
- create: use pipefail only where implemented
- ipv6: rtsold doesn't start in a jail on 11.3

## [0.10.4] 2020-02-23
### Added
- alias: add ability to use a different network interface for alias network type (#80)
- env: add pot info -E output to environment or tinirc
- log: add the ability to log activites in syslog

### Fixed
- prepare: fix multiple export port support
- rc script: extend PATH to make potnet accessible
- etc/hosts: add full hostname to localhost

## [0.10.3] 2020-01-07
### Changed
- export-ports: relax the check bout the pot's network type
- list: print a message, if there are no pot yet

## [0.10.2] 2019-12-17
### Added
- fbsd-update flavour: add a flavour to run freebsd update

### Changed
- slim flavour: remove a bounch of other directories

### Fixed
- flavor: set-cmd can cause issue if it has quotes or double quotes in the command string
- tinirc: lo1 initialization needed only for public or private bridge
- start: background tasks now check if the pot is running
- init: fix bridge folder creation
- prepare: fix prepare when no command is provided (-c is optional)

## [0.10.1] 2019-12-04
### Added
- set-hooks: add support for pre/post start/stop hooks. Script are executed in the host environment (#61)

### Changed
- home-usr/home: those link are not always available. Change create and crate-base to have them always (reported by Philip Jocks)

### Fixed
- create: permission of /tmp in single type are wrong (#72)
- create: if FreeBSD base fetch is interrupted, a broken file is left and the checksum will always fail (#73)
- import: if the image fetch is interrupted, a broken file is left and the checksum will always fail
- import: fix hostname rename
- localhost-tunnel: fix kill of ncat tunnel, when the pot has a long name

## [0.10.0] 2019-11-01
### Added
- info: support for bridge
- set-hosts: new command to add custom etc/hosts entries to a pot
- set-env: new command to add environment variable to a pot
- network-type private-bridge: add a new network layout, to provide private bridges for a group of pots
- create-private-bridge: new subcommand to define and create a private-bridge
- create: add option -B, to provide the bridge name if network-type is private-bridge
- clone: add option -B, to provide the bridge name if network-type is private-bridge
- prepare: add option -B, to provide the bridge name if network-type is private-bridge
- destroy: add option -B, to provide a way to destroy a bridge
- Image Guide: added a guide about how to create an Image of a pot
- POT_EXTRA_EXTIF: add addition network interfaces support

### Changed
- start: overwrite /etc/hosts of a pot, adding all pots on the same bridge and custom entries added via set-hosts
- flavorable commands: extend support to set-cmd and set-env
- pot-rdr anchor: the name of the anchor is now a truncated pot name (the last 54 characters)
- export: it's executed only if one snapshot is available. -F force execution, -A fix the number of snapshots, via purge-snapshots or taking a shapshot automagically
- start: using exec.start instead of command (it seems more predictable)

### Deprecated
- snapshot: -n option to provide custom name to snapshots
- support to full pot snapshot (external zfs dataset) in snapshot and start, as well as the _pot_zfs_snap_full function
- support to full pot revert (external zfs dataset) in revert/rollback (option -a)
- create-dns: undocumented and too hard to maintain
- export: -s option, to specify a snapshot. It's misleading, because zfs send -R will send all the previous snapshots anyway

### Fixed
- flavorable commands: they cannot exit, but return. create can stop flavour execution otherwise
- show: fix single type support and directory in fscomp.conf
- start: if the command has arguments with equals, it would have been truncated

## [0.9.2] 2019-08-25
### Added
- prune: invoke a stop, before the destroy, even if the pot is not runing
- lockf: introducing lockf to run create, import or clone one at a time

### Fixed
- stop: make the pkill on ncat more robust
- stop: add a workaround for a race condition in the epair driver

## [0.9.1] 2019-08-20
### Added
- localhost-tunnel attribute: new attribute to create a tunnet to redirect traffic from localhost (consul feature)

### Changed
- prepare: allow network type "host" as alias for "inherit" (nomad-friendly feature)
- prepare: set localhost-tunnel automatically (consul-friendly feature)

### Fixed
- get-rss: fix return code when the pot is not valid
- set-cmd: fix potential double quotes surrouding the command
- get-rss: TotalTicks is now expressed in Mhz (pcpu * max cpu Frequency)
- get-rss: add swap usage statistic
- start: fix tinirc permission if no-rc-script is used with network type different than public-bridge
- destroy: use -f to remove the 'Device busy' issue

## [0.9.0] 2019-08-13
### Added
- Installation Guide: a more detailed guide, that better explains the installation of pot
- fdescfs attribute: new attribute to mount fdescfs inside the pot
- init: add a network configuration validation step

### Changed
- POT_CACHE: the cache used by import is now a dataset, child of POT_ZFS_ROOT
- set-rss: CPU limits is set as maximum amount of CPU. At start, pot decide where to allocate using potcpu
- prunable: if a pot is prunable, it has to be started at least once to be pruned (flavour counts)

## [0.8.0] 2019-07-30
### Added
- update-config: implemented -a flag to update all pot configurations in one run
- get-rss: show the current resources usage (output available in json)
- procfs attribute: new attribute to mount a procfs inside the pot
- prunable attribute: new attribute prunable, to automatically delete not running pots (prune)
- prune: new command to automatically destro yinactive prunable pots

### Changed
- create: rework how to configure the network type of a pot
- Quickstart Guide: rework the guide using mount-in and copy-in, listing all possibilities
- README: remove the introduction and pointing to the Quickstart guide instead
- import: removed -a option, not really needed during import
- prepare: optimized, importing once and using clone instead of import+rename every time
- clone: add -N option, to change network type while cloning
- prepare: add -N option, to change network type while preparing

### Removed
- promote: after a long deprecation time, promote has been deleted
- add-fscomp: removed, mount-in is its more generic replacement
- add-file: removed, copy-in is its more generic replacement
- execute: remove this alias of prepare

### Fixed
- start: if the start command doesn't go in background, rss and persist weren't managed

## [0.7.0] 2019-07-04
### Added
- update-config: new command that will update a pot configuration
- execute: an orchestration oriented command that imports and automatically set several settings on a pot
- prepare: new command, taking the place of execute
- copy-in: new command, to copy files or directory inside a pot (generalized replacement of add-file)
- mount-in: new command, to mount a directory, a zfs dataset or a fscomp inside a pot (replacement for add-fscomp)

### Changed
- export-ports: removed -S for static port export
- export-ports: add the ability to associate any host port to a pot port to be exported using pot_port:host_port format
- execute: an alias for prepare

### Deprecated
- add-file: deprecated, replaced by the more general new copy-in command
- add-fscomp: deprecated, replaces by the more general new mount-in command

## [0.6.1] 2019-06-25
### Fixed
- init: make pf.conf more robust
- vnet-start: make pf start more robust, in case pf as service is not up

## [0.6.0] 2019-06-23
### Added
- add-fscomp: add option -d, to allow to mount generic directories into a pot (-d and -f are mutual)
- show: add -q flag, to only show pot names
- set-attribute: to set pot attributes (options/flags/configurations)
- get-attribute: to get pot attributes (options/flags/configurations)
- FreeBSD version usable to create a pot are all the ones listed in the FreeBSD MANIFEST
- attributes: add persistent attribute to jail
- attributes: add no-rc-script attribute to start a pot without a rc script
- add-file: new command to copy a single file inside a pot

### Changed
- inherit network: added ipv6 support (automatic)
- static IP network: added ipv6 support
- pf: adopt anchor with relevant changes in nat rules management

### Deprecated
- pot_list: in rc.conf pot_list is not supported anymore. Please use the start-at-boot attribute

### Fixed
- syntax error in zsh autocompletion
- ls fscomp: using zfs instead of ls (if a fscomp is re-mounted, the mountpoint is not in /opt/pot/fscomp anymore)
- static-ip: Fix invocation to potnet to validate ip addresses

## [0.5.11] 2019-03-04
### Added
- export: new command. export generates a compressed file with the entire pot in it. It works only for single type pot
- export: -D option to change export directory and -l option to change compression level
- POT_CACHE: a place to cache pot images. It's a variable of pot.conf
- import: new command. import create a new pot based on an image generated via export
- import-export: add skein has verification support

### Changed
- rc.d: changed order, to start pot before ntpdate
- destroy: extend the usage of -F to be able to destroy corrupted pots

### Fixed
- destroy: fix return code
- rename: fix single pot support
- get_conf_var: using a better RE to avoid to detect variables value in pot names (Thanks to Johan Hendriks to report)

## [0.5.10] 2019-01-15
### Added
- destroy: option -f can be used to delete/destroy a fscomp (no recursive support yet)
- vnet-start: add support to VPN and custom pf configuration

### Changed
- destroy: option -f (force) is now -F

### Fixed
- is_pot: improve support to single pots, that don't have fscomp.conf file
- create-base: fix regression introduced on 0.5.9 (RC support and create -F removal)

## [0.5.9] 2018-12-16
### Added
- Add support to RC FreeBSD version
- config: add pot_prefix and fscomp_prefix as possible values
- snapshot: add option -n to give a name to the snapshot; valid for fscomp only
- Add support to FreeBSD 12.0

### Changed
- create: removed -F option and silent default flavor invocation. Default flavor has to be explicitely selected via -f
- create-base: removed support for undocumented default-base flavor

## [0.5.8] 2018-11-29
### Added
- QuickStart.md : a markdown quick guide, for new users
- create : -i auto (based on potnet) to get automatically a valid IP address
- clone : -i auto (based on potnet) to get automatically a valid IP address
- clone : add support to single type pot
- export-port : new command that allow pot ports to be exposed outside (vnet case)
- slim flavor, designed to be used with single dataset pot types
- purge-snapshot : new command that will remove all snapshots, except the last one
- export-port : static option, to add statically exported ports

### Changed
- init : it takes care of syslogd configuration in the host system
- create : type=single will install plain FreeBSD and run the default flavour
- create : multiple flavour support, executed in sequentially; option -f can be repeated
- add-fscomp : exploit the new internal refactorized mount and umount function to avoid to start the pot
- add-fscomp : if the pot is running, mount the new fscomp right away

### Fixed
- clone : fix a misleading/false positive error message
- clone : fix syslogd configuration in the cloned pot
- destroy : fix if pot is a single dataset one
- start : fix hostname warning
- start (#046) : run the jail in a clean enviroment
- term (#046) : spawn the shell in a jail using the jailed user environment
- add-fscomp (#045) : check the mount point and create it, if missing
- list (#052) : fixing xargs invocation

### Deprecated
- promote : mark promot as deprecated, so we can remove it in the next major release

## [0.5.7] 2018-06-28
### Added
- create-base : add support to FreeBSD 11.2

### Fixed
- version (#038) : fix the version number showed

## [0.5.6] 2018-05-18
### Added
- create-base : add option -b, to provide a specific name to a base and support multiple bases with the same FreeBSD version
- create: add support to single dataset pots.
- set-cmd: add the command to manage the command line that starts the container
- top: add a new command, to spawn a top only on a pot

## [0.5.5] 2018-04-18
### Added
- ps : add ps subcommand, to show information about which pot is running
- config : add config subcommand, to easily access configuration values
- zsh autocompletion (#013): pot autocompletion support for zsh
- syslogd log unification (#032): when possible, syslogd autoconfigured to log in the host instead of in the pot

### Changed
- list : keep it more simple, leaving more information under -v
- show : add -r (all running pots) option and made it the new default
- show : -a greatly improved show all relevant information per pot

### Removed
- fs.conf is not supported anymore

## [0.5.0] 2018-03-16
### Added
- add-fscomp : add the ability to remount a fscomp (-w), instead of mount it via nullfs
- add-fscomp : add the ability to mount a fscomp in read-only (-r)
- info : new command, to get information about a specific pot
- rc.d script (#022)
- create (#020) : add option -s, to configure static ip address (alias to external network interface)
- create-base (#030) : add sha verification using freebsd-release-manifests
- version : add a subcommand the print the current version of the utility
- fscomp.conf : new fs component description file
- clone : -f option to automatically get a snapshot of a dataset, if needed
- promote (#008) : add promote command

### Changed
- create: the new wiki page shows the slightly different behavior of all use cases
- create: fscomp.conf is generated instead of fs.conf, using dataset instead of mountpoint as first column
- private datasets are now mounted changing the mountpoint of the dataset, instead of relying on nullfs
  For that reasons, an option called "zfs-remount" is added to the fs.conf file

### Deprecated
- fs.conf: fs.conf is deprecated. Support will be removed in the next release.

### Removed
- create: -S option, not needed anymore

## [0.4.0] 2018-02-13
### Added
- rollback : added rollback as alias of revert
- help : add support for aliases
- add-fscomp (#027): add support for external ZFS dataset via the option -e
- de-init (0#23): new command to completely remove (de-install) pot from your system

### Fixed
- stop (#024): check the pot existance
- stop : fixed error messages referring to "jail"
- list : fix help, option -s never implemented
- destroy (0#25): fix pot name validation and add dependency detection

## [0.3.1] 2018-01-26
### Added
- pot.conf.sample : a documentated pot.conf example

## [0.3.0] 2018-01-26
### Added
- Every command that need root privileges perform a check of the user id
- create (#010): add a validation of command types executable in a pot flavour
- Checks about VIMAGE and rctl usability (#018)

### Changed
- set-rss : rename command (it was add-rss)
- vnet-start (#019): automatic activation of ip forwarding

### Fixed
- create-base (#016): do not re-create the base pot
- rename (#017): apply the rename also in dependents pots (level 2 or runtime dependency)
- create (#021): -b argument validation

## [0.2.0] 2018-01-23
### Added
- create-dns : new command, to create the dns pot
- create : add the option -d to choose the type of dns (inherit or pot)
- start : add the support to dns types

### Fixed
- rename : remove a misleading error message
- start : proper stop, if one mount fails
- vnet-start (#009): pf module loaded and firewall enabled

## [0.1.0] 2017-12-14
### Added
- Add resource constraints, via add-rss command
- Add clone-fscomp command, to clone a fscomp
- Add clone command, to clone a pot
- Add rename command, to completely rename a pot
- Add dependency support, and related command add-dep
- Add support to destroy bases and their related level 0 pot
- Add support to recursive destroy
- Add support to default flavour with create-base
- Add -F option to create, to disable the default flavour
- More tests

### Changed
- Move packages db to the usr.local dataset - it's a breaking change

## [0.0.2] 2017-12-03
### Fixed
- Fixed start, a typo prevents the correct behavior

## [0.0.1] 2017-12-03
### Added
- Add a revert command, to rollback a pot snapshot
- Add snaposhot information in list command
- Add a test framework
- Add travis-ci support

### Changed
- snapshot options has changed to be more consistent and to support fscomp
- revert options has changed to be more consistent and to support fscomp

### Fixed
- If a start fails, it tries to clean up (umount)

## [0.0.1-rc.1] 2017-11-28
### Added
- Add a vnet-start command, to properly init the vnet network configuration (bridge+pf)
- Add an option to show, to show all pot resource usage
- Add some command alias: ls is an alias of list
- Auto-start of vnet, when needed
- Add level 2 pot support, adding special option -P and -S in create command
- Add a run command, to start and enter in a pot
- Add a -f option to destroy and term, to fix/force the operation
- Add -F for flavor and -a for all in list command

### Changed
- Changed pot configuration. No jail.conf anymore, but pot.conf
- Pots with an ip are now based on epair/vnet/VIMAGE technology
- Command show now shows resource usage of all pots by default
- Add more information in show and list command

### Deprecated
- jail.conf files are currently ignored, please destroy and recreate pots

### Removed
- scripts directory and j\* commands in bin. None of them could really works
## [0.0.1-beta] 2017-11-07
### Added
- Add option -b to list, to list available bases
- Add option -f to list, to list available fs components
- Add a destroy command, to destroy a pot
- Add a term command, to start a pot
- First implementation of show, that shows memory used by running pots
- Add support to flavour- pot subcommand and shell script

### Changed
- Remove jail references and use pot instead (not in zfs)
- jstart command is now start
- jstop command is now stop
- create-jail command is not create
- create-base creates the related level 0 pot automatically
- start doesn't invoke exit if succeeds

### Fixed
- Fix add-fscomp that can introduce valid, but imprecise mount-point
- Fix create-base that created a wrong usr.local.etc link

## [0.0.1-alpha] 2017-10-20
### Added
- Add the central utility called 'pot'
- Add a central configuration file
- Add a init command, to initialize the zfs layout
- Add a create-base command, to create a new base
- Add a create-fscomp command, to create a new base
- Add a create-jail command, to create a new base
- Add a jstart command, to start a pot
- Add a jstop command, to stop a pot
- Add an option to jstart, to take a snapshot of the pot before the start
- Add a help command, to show subcommand helps
- Add a list command, to list of pots
- Add a add-fscomp command, to add fscomponents to pots
- Add a snapshot command, to take a snapshot of a pot

### Changed
- After long time spent on thinking, the project has a new nice name, pot.


================================================
FILE: Jenkinsfile
================================================
pipeline {
	agent any

	environment {
		TERM = 'rxvt'
	}

	stages {
		stage('Test') {
			steps {
				sh 'echo "Hello World"'
				sh '''
					cd tests
					./test-suite.sh
				'''
			}
		}
	}
}


================================================
FILE: LICENSE
================================================
BSD 3-Clause License

Copyright (c) 2017, Luca Pizzamiglio
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


================================================
FILE: README.md
================================================
# THIS REPOSITORY IS NO LONGER ACTIVELY MAINTAINED, IT HAS MOVED TO https://codeberg.org/bsdpot/pot
# pot

[![build-badge](https://github.com/pizzamig/pot/workflows/unit-test/badge.svg)](https://github.com/pizzamig/pot/actions) [![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)

Another container framework based on jails, to run FreeBSD containers on FreeBSD.
Every running instance is called `pot`, like the one that I use to cook all the different type of pasta.
It's heavily based on FreeBSD, in particular on jails, ZFS, pf and rctl.

The project's initial goal was to prove that FreeBSD has all the technologies to have a container-alike environment.
The project then evolved into something more robust and feature-rich.

The project was presented for the first time at FOSDEM 2018: ([talk page](https://archive.fosdem.org/2018/schedule/event/pot_container_framework/))

If you are more interested in jail orchestration, a nomad driver is provided to interact with `pot` and this work has been presented at FOSDEM 2020 ([talk page](https://archive.fosdem.org/2020/schedule/event/orchestrating_jails/))

### Documentation
The project's documentation is available at [https://pot.pizzamig.dev](https://pot.pizzamig.dev)

More in details:
* A Getting started guide is available [here](https://pot.pizzamig.dev/Getting)
* An installation guide, with detailed description is available [here](https://pot.pizzamig.dev/Installation)

### Nomad pot driver integration
A driver to allow [nomad](https://www.nomadproject.io) to interact with `pot` has been developed and available [here](https://github.com/trivago/nomad-pot-driver)

### Ansible Collection

There is pot collection for Ansible provided by github user @zilti available at https://galaxy.ansible.com/zilti/pot.

### Online help
`pot` provide an online help:
```
# pot help
Usage: pot command [options]

Commands:
	help	-- Show help
	version -- Show the pot version
	config  -- Show pot framework configuration
	ls/list	-- List of the installed pots
	show	-- Show pot information
	info    -- Print minimal information on a pot
	top     -- Run the unix top in the pot
	ps      -- Show running pots
	init	-- Initialize the ZFS layout
	de-init	-- Deinstall pot from your system
	vnet-start -- Start the vnet configuration
	create-base	-- Create a new base image
	create-fscomp -- Create a new fs component
	create-private-bridge -- Create a new private bridge
	create -- Create a new pot (jail)
	clone -- Clone a pot creating a new one
	clone-fscomp - Clone a fscomp
	rename -- Rename a pot
	destroy -- Destroy a pot
	prune   -- Destroy not running prunable pots
	copy-in -- Copy a file or a directory into a pot
	mount-in -- Mount a directory, a zfs dataset or a fscomp into a pot
	add-dep -- Add a dependency
	set-rss -- Set a resource constraint
	get-rss -- Get the current resource usage
	set-cmd -- Set the command to start the pot
	set-env -- Set environment variabls inside a pot
	set-hosts -- Set etc/hosts entries inside a pot
	set-hook -- Set hook scripts for a pot
	set-attr -- Set a pot's attribute
	get-attr -- Get a pot's attribute
	export-ports -- Let export tcp ports
	start -- Start a jail (pot)
	stop -- Stop a jail (pot)
	term -- Start a terminal in a pot
	run -- Start and open a terminal in a pot
	snap/snapshot -- Take a snapshot of a pot
	rollback/revert -- Restore the last snapshot
	purge-snapshots -- Remove old/all snapshots
	export -- Export a pot to a file
	import -- Import a pot from a file or a URL
	prepare -- Import and prepare a pot - designed for jail orchestrator
	update-config -- Update the configuration of a pot
```

Every command has its own online help as well. For instance:
```
pot create [-hv] -p potname [-N network-type] [-i ipaddr] [-l lvl] [-f flavour]
  [-b base | -P basepot ] [-d dns] [-t type]
  -h print this help
  -v verbose
  -k keep the pot, if create fails
  -p potname : the pot name (mandatory)
  -l lvl : pot level (only for type multi)
  -b base : the base pot
  -P pot : the pot to be used as reference
  -d dns : one between inherit(default), pot, off or custom:filename
  -f flavour : flavour to be used
  -t type: single or multi (default multi)
         single: the pot is based on a unique ZFS dataset
         multi: the pot is composed by a classical collection of 3 ZFS dataset
  -N network-type: one of those
         inherit: inherit the host network stack (default)
         alias: use a static ip as alias configured directly to the host NIC
         public-bridge: use the internal commonly public bridge
         private-bridge: use an internal private bridge (with option -B)
  -i ipaddr : an ip address or the keyword auto (if compatible with the network-type)
         auto: usable with public-bridge and private-bridge (default)
         ipaddr: mandatory with alias, usable with public-bridge and private-bridge
  -B bridge-name : the name of the bridge to be used (private-bridge only)
  -S network-stack : the network stack (ipv4, ipv6 or dual)
```


================================================
FILE: bin/pot
================================================
#!/bin/sh

# Copyright (c) 2017, Luca Pizzamiglio <pizzamig@FreeBSD.org>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
#
# * Neither the name of the copyright holder nor the names of its
#   contributors may be used to endorse or promote products derived from
#   this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

# Force ifconfig into expected format
export IFCONFIG_FORMAT=addr:default

# Environment initialization and initial checks

# shellcheck disable=SC2034
_POT_VERSION=0.16.0
_POT_PATHNAME="$(realpath "$0")"
_POT_PREFIX="$(dirname "${_POT_PATHNAME}")"
_POT_INCLUDE="$( realpath "${_POT_PREFIX}/../share/pot")"
_POT_ETC="$( realpath "${_POT_PREFIX}/../etc/pot")"
if [ -d "${_POT_ETC}/flavours" ]; then
	_POT_FLAVOUR_DIR="$( realpath "${_POT_ETC}/flavours")"
else
	# shellcheck disable=SC2034
	_POT_FLAVOUR_DIR=
fi

if [ ! -d "${_POT_INCLUDE}" ]; then
	echo "Fatal error! Not able to find the subroutines directory as ${_POT_INCLUDE}!"
	exit 1
fi

# loading subroutines

if [ ! -r "${_POT_INCLUDE}/common.sh" ]; then
	echo "Fatal error! Not able to find common subroutines in ${_POT_INCLUDE}!"
	exit 1
fi
# shellcheck disable=SC1090
. "${_POT_INCLUDE}/common.sh"

if [ ! -r "${_POT_INCLUDE}/common-flv.sh" ]; then
	echo "Fatal error! Not able to find flavor subroutines in ${_POT_INCLUDE}!"
	exit 1
fi
# shellcheck disable=SC1090
. "${_POT_INCLUDE}/common-flv.sh"

if [ ! -r "${_POT_INCLUDE}/network.sh" ]; then
	echo "Fatal error! Not able to find network subroutines in ${_POT_INCLUDE}!"
	exit 1
fi
# shellcheck disable=SC1090
. "${_POT_INCLUDE}/network.sh"

# loading configuration
if [ -r "$_POT_ETC/pot.default.conf" ]; then
	# shellcheck disable=SC1090
	. "$_POT_ETC/pot.default.conf"
else
	_error "Fatal error! Not able to find default configuration file on $_POT_ETC"
	exit 1
fi

if [ -r "$_POT_ETC/pot.conf" ]; then
	# shellcheck disable=SC1090
	. "$_POT_ETC/pot.conf"
fi

usage() {
	cat <<-"EOF"
	Usage: pot command [options]

	Commands:
	    help    -- Show help
	    version -- Show version of the pot command
	    config  -- Show pot framework configuration
	    ls/list -- List of the installed pots
	    show    -- Show information on pots
	    info    -- Print minimal information on a pot
	    top     -- Run top(1) inside the pot
	    ps      -- Show running pots
	    init    -- Initialize the ZFS layout
	    de-init -- Remove pot from your system
	    vnet-start -- Start vnet configuration
	    create-base -- Create a new base image
	    create-fscomp -- Create a new fs component
	    create-private-bridge -- Create a new private bridge
	    create -- Create a new pot (jail)
	    clone -- Clone a pot, creating a new one
	    clone-fscomp -- Clone an fscomp
	    rename -- Rename a pot
	    destroy -- Destroy a pot
	    prune   -- Destroy non-running, prunable pots
	    copy-in -- Copy a file or a directory into a pot
	    copy-out -- Copy a file or a directory out of a pot
	    mount-in -- Mount directory, ZFS dataset, or fscomp into a pot
	    mount-out -- Unmount directory, ZFS dataset, or fscomp from a pot
	    add-dep -- Add a dependency
	    set-rss -- Set a resource constraint
	    get-rss -- Get the current resource usage
	    set-cmd -- Set the command to start the pot
	    set-env -- Set environment variables inside a pot
	    set-hosts -- Set /etc/hosts entries inside a pot
	    set-hook -- Set hook scripts for a pot
	    set-attribute/set-attr -- Set an attribute on a pot
	    get-attribute/set-attr -- Get an attribute from a pot
	    export-ports -- Allows exposing tcp and udp ports
	    start -- Start a pot (jail)
	    stop -- Stop a pot (jail)
	    term -- Open terminal inside of a pot
	    run -- Same as term, but start pot if it is not running
	    snap/snapshot -- Take a snapshot of a pot
	    rollback/revert -- Restore the latest snapshot
	    purge-snapshots -- Remove old/all snapshots
	    export -- Export a pot to a file
	    import -- Import a pot from a file or a URL
	    prepare -- Import and prepare a pot, used by orchestrators
	    update-config -- Update the configuration of a pot
	    last-run-stats -- Get statistics about a pot's last run
	    signal -- Send signal to pot
	    exec -- Execute a progam inside of a pot
	EOF
}

# shellcheck disable=SC2034
# variable initialization
_POT_VERBOSITY=1

# parsing command line subcommand
if [ $# -lt 1 ]; then
	usage
	exit 1
fi
CMD="$1"
shift

case "${CMD}" in
	ls)
		CMD=list
		;;
	rollback)
		CMD=revert
		;;
	snap)
		CMD=snapshot
		;;
	set-attr)
		CMD=set-attribute
		;;
	get-attr)
		CMD=get-attribute
		;;
esac

case "${CMD}" in
	help)
		if [ -n "$1" ]; then
			pot-cmd "${CMD}" "$1"
			exit 0
		else
			usage
			exit 0
		fi
		;;
	show|version|config|\
	list|info|ps|top|\
	init|de-init|vnet-start|\
	create-base|create-fscomp|create|\
	create-private-bridge|\
	copy-in|copy-out|mount-in|mount-out|prune|set-hook|\
	destroy|add-dep|set-rss|get-rss|set-cmd|set-env|set-hosts|\
	export|import|prepare|\
	export-ports|set-attribute|get-attribute|\
	start|stop|term|\
	rename|clone|clone-fscomp|promote|\
	snapshot|revert|purge-snapshots|update-config|\
	last-run-stats|signal|exec|set-status)
		pot-cmd "${CMD}" "$@"
		exit $?
		;;
	run)
		pot-cmd term -f "$@"
		exit $?
		;;
	*)
		usage
		exit 1
		;;
esac


================================================
FILE: etc/pot/flavours/dns
================================================
set-attribute -A start-at-boot -V YES


================================================
FILE: etc/pot/flavours/dns.sh
================================================
#!/bin/sh

pkg install -y dnsmasq
pkg install -y consul
pkg clean -ayq

if [ ! -d /usr/local/etc/consul.d ]; then
	mkdir -p /usr/local/etc/consul.d
fi

_epair="$(ifconfig | egrep 'epair.*b' | cut -f 1 -d':')"
_ip="$( ifconfig $_epair inet | awk '/inet/ { print $2; }' )"
_ip1="$( echo $_ip | cut -f 1 -d'.' )"
_ip2="$( echo $_ip | cut -f 2 -d'.' )"
_domain="$( hostname | cut -f 2 -d'.' )"
echo "server=/${_domain}/127.0.0.1#8600" >> /usr/local/etc/dnsmasq.conf
echo "server=/0.${_ip2}.${_ip1}.in-addr.arpa/127.0.0.1#8600" >> /usr/local/etc/dnsmasq.conf
echo "listen-address=${_ip}" >> /usr/local/etc/dnsmasq.conf

sysrc dnsmasq_enable="YES"
sysrc consul_enable="YES"
sysrc consul_args="-server -dev -domain=${_domain} -bind=${_ip}"


================================================
FILE: etc/pot/flavours/fbsd-update.sh
================================================
#!/bin/sh

export PAGER=/bin/cat
freebsd-update --not-running-from-cron fetch install


================================================
FILE: etc/pot/flavours/slim.sh
================================================
#!/bin/sh

dirs="/usr/share/bsdconfig /usr/share/doc /usr/share/dtrace /usr/share/examples /usr/share/man /usr/share/openssl /usr/share/sendmail /usr/share/pc-sysinstall /usr/libexec/bsdinstall /usr/libexec/bsdconfig /rescue /usr/tests /usr/lib32 /usr/lib/clang /usr/include /var/db/freebsd-update /var/db/etcupdate /boot"
usr_bin="c++ c++filt c89 c99 cc CC cpp clang clang-cpp clang-tblgen clang++ gdb gdbtui gdbserver ld ld.bfd ld.lld lldb llvm-objdump llvm-tblgen nm objcopy objdump strings strip"
usr_bin_glob="svnlite yp"

usr_sbin="dtrace"
usr_sbin_glob="bhyve boot yp"
rm -f /usr/lib/*.a
## Remove pkg stuff
rm -rf /var/db/pkg/*
rm -rf /usr/sbin/pkg
rm -rf /usr/local/sbin/pkg

for d in $dirs ; do
	rm -rf ${d}
done
(
	cd /usr/bin
	for f in $usr_bin ; do
		rm -f $f
	done
	for g in $usr_bin_glob ; do
		rm -rf ${g}*
	done
)
(
	cd /usr/sbin
	for g in $usr_sbin_glob ; do
		rm -rf ${g}*
	done
	rm -f $usr_sbin
)


================================================
FILE: etc/pot/pot.conf.sample
================================================
# pot configuration file

# All datasets related to pot use the some zfs dataset as parent
# With this variable, you can choose which dataset has to be used
# POT_ZFS_ROOT=zroot/pot

# It is also important to know where the root dataset is mounted
# POT_FS_ROOT=/opt/pot

# This is the cache used to import/export pots
# POT_CACHE=/var/cache/pot

# This is where pot is going to store temporary files
# POT_TMP=/tmp

# This is the group owning POT_FS_ROOT
# POT_GROUP=pot

# This is the suffix added to temporary files created using mktemp,
# X is a placeholder for a random character, see mktemp(1)
# POT_MKTEMP_SUFFIX=.XXXXXXXX

# Define the max length of the hostname inside the pot
# POT_HOSTNAME_MAX_LENGTH=64

# Internal Virtual Network configuration

# IPv4 Internal Virtual network
# POT_NETWORK=10.192.0.0/10

# Internal Virtual Network netmask
# POT_NETMASK=255.192.0.0

# The default gateway of the Internal Virtual Network
# POT_GATEWAY=10.192.0.1

# The name of the network physical interface, to be used as default gateway
# POT_EXTIF=em0

# The list of extra network interface, to make other network segments accessible
# POT_EXTRA_EXTIF=vlan20 vlan50
# for each extra interface, a variable is used to sepcify its network segment
# POT_NETWORK_vlan20=192.168.100.0/24
# POT_NETWORK_vlan50=10.50.50.0/24

# Do not allow bridge-based pots to forward traffic to each other
# POT_ISOLATE_VNET_POTS=true

# DNS on the Internal Virtual Network

# name of the pot running the DNS
# POT_DNS_NAME=dns

# IP of the DNS
# POT_DNS_IP=10.192.0.2

# Path to default public key to verify pot signatures using signify(1)
# on import/prepare - can be overridden using `-C pubkey`.
# POT_DEFAULT_SIGNATURE_PUBKEY=/usr/local/etc/pot/sign_key.pub

# VPN support

# name of the tunnel network interface
# POT_VPN_EXTIF=tun0
# POT_VPN_NETWORKS=192.168.0.0/24 192.168.10.0/24


================================================
FILE: etc/pot/pot.default.conf
================================================
# pot configuration file - default values

# All datasets related to pot use the some zfs dataset as parent
# With this variable, you can choose which dataset has to be used
POT_ZFS_ROOT=zroot/pot

# It is also important to know where the root dataset is mounted
POT_FS_ROOT=/opt/pot

# This is the cache used to import/export pots
POT_CACHE=/var/cache/pot

# This is where pot is going to store temporary files
POT_TMP=/tmp

# This is the group owning POT_FS_ROOT
POT_GROUP=pot

# This is the suffix added to temporary files created using mktemp,
# X is a placeholder for a random character, see mktemp(1)
POT_MKTEMP_SUFFIX=.XXXXXXXX

# Define the max length of the hostname inside the pot
POT_HOSTNAME_MAX_LENGTH=64

# Internal Virtual Network configuration
# IPv4 Internal Virtual network
POT_NETWORK=10.192.0.0/10
# Internal Virtual Network netmask
POT_NETMASK=255.192.0.0
# The default gateway of the Internal Virtual Network
POT_GATEWAY=10.192.0.1
# The name of the network physical interface, to be used as default gateway
POT_EXTIF=em0
# Additional network interfaces
POT_EXTRA_EXTIF=
# If not empty, it will use this IPv4 on POT_EXTIF as defualt gateway
POT_EXTIF_ADDR=

# Three possible values: ipv4, ipv6, dual
POT_NETWORK_STACK=ipv4

# DNS on the Internal Virtual Network
# name of the pot running the DNS
POT_DNS_NAME=dns
# IP of the DNS
POT_DNS_IP=10.192.0.2

# If set to true, isolate pot vnet bridge members
# (by using `ifconfig <bridgeif> private <memberif>`, see ifconfig(8))
POT_ISOLATE_VNET_POTS=false

# If not empty, this script will be called by pot and the pf rules
# returned on stdout will be loaded into "pot-rdr/anchor" instead
# of those which pot would usually create. This also skips
# creation of netcat-based localhost-tunnels.
# Only works with IPv4 at the moment.
#
# Parameters sent to the script are:
# POT_EXTIF BRIDGE POT_NETWORK POT_GATEWAY proto host_port pot_ip pot_port
# Example:
# igb0 bridge1 10.192.0.0/10 10.192.0.1 tcp 32732 10.192.0.10 80
POT_EXPORT_PORTS_PF_RULES_HOOK=
# VPN support

# Path to default public key to verify pot signatures using signify(1)
# on import/prepare - can be overridden using `-C pubkey`.
POT_DEFAULT_SIGNATURE_PUBKEY=

# name of the tunnel network interface
POT_VPN_EXTIF=
POT_VPN_NETWORKS=

# POT log facility
POT_LOG_FACILITY=local2


================================================
FILE: etc/rc.d/pot
================================================
#!/bin/sh

# PROVIDE: pot
# REQUIRE: NETWORKING LOGIN FILESYSTEM
# BEFORE: securelevel
# KEYWORD: shutdown nojail

. /etc/rc.subr

PATH=$PATH:/usr/local/bin
name="pot"
desc="Pot containers"
procname="pot"
rcvar=pot_enable
start_cmd="pot_start"
stop_cmd="pot_stop"
restart_cmd="pot_restart"
status_cmd="pot_status"
start_precmd="pot_deprecated_start"
stop_postcmd="pot_deprecated_stop"

load_rc_config $name
: ${pot_enable:=NO}

pot_start()
{
	local _pname _dyn_pot_list _start
	_dyn_pot_list=$(/usr/local/bin/pot ls -q)
	for _pname in $_dyn_pot_list ; do
		if [ "$( /usr/local/bin/pot get-attr -p "$_pname" -A start-at-boot -q )" = "YES" ]; then
			/usr/local/bin/pot start "$_pname"
		fi
	done
}

pot_stop()
{
	local _pname _dyn_pot_list _start
	_dyn_pot_list=$(/usr/local/bin/pot ls -q)
	for _pname in $_dyn_pot_list ; do
		if [ "$( /usr/local/bin/pot get-attr -p "$_pname" -A start-at-boot -q )" = "YES" ]; then
			/usr/local/bin/pot stop "$_pname"
		fi
	done
}

pot_early_start()
{
	local _pname _dyn_pot_list _start
	_dyn_pot_list=$(/usr/local/bin/pot ls -q)
	for _pname in $_dyn_pot_list ; do
		if [ "$( /usr/local/bin/pot get-attr -p "$_pname" -A early-start-at-boot -q )" = "YES" ]; then
			/usr/local/bin/pot start "$_pname"
		fi
	done
}

pot_early_stop()
{
	local _pname _dyn_pot_list _start
	_dyn_pot_list=$(/usr/local/bin/pot ls -q)
	for _pname in $_dyn_pot_list ; do
		if [ "$( /usr/local/bin/pot get-attr -p "$_pname" -A early-start-at-boot -q )" = "YES" ]; then
			/usr/local/bin/pot stop "$_pname"
		fi
	done
}

pot_restart()
{
	pot_stop
	pot_early_stop
	sleep 5
	pot_early_start
	pot_start
}

pot_status()
{
	local _pname _dyn_pot_list
	_dyn_pot_list=$(/usr/local/bin/pot ls -q)
	for _pname in $_dyn_pot_list ; do
		if [ "$( /usr/local/bin/pot get-attr -p "$_pname" -A early-start-at-boot -q )" = "YES" ] ||
		   [ "$( /usr/local/bin/pot get-attr -p "$_pname" -A start-at-boot -q )" = "YES" ]; then
			if /usr/local/bin/pot info -qrp "$_pname" ; then
				echo "pot $_pname is up and running"
			else
				echo "pot $_pname is not running"
			fi
		fi
	done
}

run_rc_command "$1"


================================================
FILE: etc/rc.d/pot_early
================================================
#!/bin/sh

# PROVIDE: pot_early
# REQUIRE: NETWORKING syslogd pf
# BEFORE: ntpdate
# KEYWORD: shutdown nojail

. /etc/rc.subr

PATH=$PATH:/usr/local/bin
name="pot_early"
desc="Pot containers - early start"
procname="pot"
rcvar=pot_enable
start_cmd="pot_early_start"
stop_cmd="pot_early_stop"

load_rc_config $name
: ${pot_enable:=NO}

pot_early_start()
{
	local _pname _dyn_pot_list _start
	_dyn_pot_list=$(/usr/local/bin/pot ls -q)
	for _pname in $_dyn_pot_list ; do
		if [ "$( /usr/local/bin/pot get-attr -p "$_pname" -A early-start-at-boot -q )" = "YES" ]; then
			/usr/local/bin/pot start "$_pname"
		fi
	done
}

pot_early_stop()
{
	local _pname _dyn_pot_list _start
	_dyn_pot_list=$(/usr/local/bin/pot ls -q)
	for _pname in $_dyn_pot_list ; do
		if [ "$( /usr/local/bin/pot get-attr -p "$_pname" -A early-start-at-boot -q )" = "YES" ]; then
			/usr/local/bin/pot stop "$_pname"
		fi
	done
}

run_rc_command "$1"


================================================
FILE: release.sh
================================================
#!/bin/sh

print_syntax() { echo "$0" X.Y.Z ; exit "${1:-1}"; }

if [ -z "$1" ]; then
	print_syntax
fi

if [ "$1" = "$(echo "$1" | sed -E 's/[0-9]+(\.[0-9]+)+//')" ]; then
	echo invalid verion number $1
	print_syntax
	exit
fi

version="$1"
tag_date="$(date +%Y-%m-%d)"
echo applying new version "$version" with date "$tag_date"

sed -i '' "s/^_POT_VERSION=.*$/_POT_VERSION=$version/" 'bin/pot'
sed -i '' 's/^## \[Unreleased\]/## \[Unreleased\]\
\
### NEWVERSION/' CHANGELOG.md

sed -i '' "s/### NEWVERSION/## \[$version\] $tag_date/" CHANGELOG.md

sed -i '' "s/^version = .*$/version = \"$version\"/" 'share/doc/pot/conf.py'
sed -i '' "s/^release = .*$/release = \"$version\"/" 'share/doc/pot/conf.py'

git diff -p --stat


================================================
FILE: share/doc/pot/.gitignore
================================================
_build


================================================
FILE: share/doc/pot/Description.md
================================================
DESCRIPTION
-----------
Another container framework based on jails, to run FreeBSD containers on
FreeBSD.  Every running instance is called "pot", but less flexible than a
VM.  It's heavily based on FreeBSD, in particular on jails, ZFS, pf and
rctl.


================================================
FILE: share/doc/pot/Images.md
================================================
# Using `pot` images

This guide has the ambitious goal of explain how to create `pot` images, a feature that allows `pot` to be used with nomad, but it can be useful in other use cases.
This guide assumes that you have already installed `pot` and you are already familiar with it (installation and quickstart guide).

## What is a `pot` image?

A `pot` image is a binary blob representing the `pot` configuration file and it's file system.
In more detail, it's a compressed archive containing a ZFS snapshot of a file system.

## Create an images `pot`

The fundamental steps to create an image of a `pot` container are:
* create a `pot`
* "customize" the `pot` as needed
* take a snapshot
* export the image

**NOTE** `export` and `import` support `single` type `pot`s. Multi-dataset `pot` are not supported yet.

#### Create a `pot`

A `pot` named `test` can be created using the command `pot create`:
```console
# pot create -p test -b 12.0 -N public-bridge -t single`
```
* `-p test` : the `pot` name
* `-b 12.0` : the FreeBSD release version to be used
* `-t single` : the `pot` type. Only `single` type are supported at the moment for `import`/`export`
* `-N public-bridge` : the network type (it can be changed during `import`, but it can be useful to use a network type that is relevant for the use case)

Once the `pot` named `test` is created, it's possible to :
* customize its configuration (attributes, starting command, and so on)
* enter in it and do whatever is needed, for instance install `nginx`
```console
# pot run test
[...]
root@test:~ # pkg install nginx
[...]
root@test:~ # exit
exit
# pot set-cmd -p test -c "nginx -g 'daemon off;'"
# pot set-attr -p test -A no-rc-script -V ON
# pot set-attr -p test -A persistent -V NO
# pot set-rss -p test -C 1
```

Once you're satisfied with your `pot`, you can stop it and take a snapshot:
```console
# pot stop test
# pot snapshot -p test
```

The snapshot can be now exported as an image, with the command `export`
```console
# pot export -p test -t 1.0
```
* `-t 1.0`: the same image can have multiple version. The option `-t` allows to provide a tag to the image

The `export` command can take quite some time, because of the compression step.
Once the `export` command ends, it generates 2 files:
```console
test_1.0.xz
test_1.0.xz.skein
```
The first file is the image, the second file is a hash file, used by the `import` command to verify the integrity.

#### Snapshots management

The `export` command will create an image if and only if one snapshot is available.
The flag `-A` try to automatically fix the number of available snapshots:
* if 0 snapshots are available, `-A` will automatically invoke `pot snapshot`
* if 2+ snapshots are available, `-A` will automatically invoke `pot purge-snapshots`

The command `pot purge-snapshots` deletes all snapshots, except the last one.

### Images creation automated with flavours

Flavour is the way we currently provide to automate the customization of a `pot`.
With flavour, it's possible to automatically:
* apply configuration parameters to your `pot`
* execute a bootstrap script

just putting some files in flavour folder (`/usr/local/etc/pot/flavours`).

In the example above, we customized the `pot` `test` via:
```console
# pot run test
[...]
root@test:~ # pkg install nginx
[...]
root@test:~ # exit
exit
# pot set-cmd -p test -c "nginx -g 'daemon off;'"
# pot set-attr -p test -A no-rc-script -V ON
# pot set-attr -p test -A persistent -V NO
# pot set-rss -p test -C 1
```

Now we automate those commands in a flavour, called `nginx-test`.
In the flavour folder we create one file for the `pot` configuration:
```console
# cat /usr/local/etc/pot/flavours/nginx-test
set-cmd -c "nginx -g 'daemon off;'"
set-attribute -A no-rc-script -V YES
set-attribute -A persistent -V NO
set-rss -C 1
```

The bootstrap script, that will be executed inside the jail, would look like this:
```console
# chmode a+x /usr/local/etc/pot/flavours/nginx-test.sh
# cat /usr/local/etc/pot/flavours/nginx-test.sh
#!/bin/sh

[ -w /etc/pkg/FreeBSD.conf ] && sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf
ASSUME_ALWAYS_YES=yes pkg bootstrap
touch /etc/rc.conf
sysrc sendmail_enable="NONE"
pkg install -y nginx
pkg clean -y
```

The flavour `nginx-test` can now be used with the `create` command:
```console
# pot create -p test-flavour -b 12.0 -N public-bridge -t single -f nginx-test
```

An important note: while the bootstrap script is a shell script, where you can do whatever you want, the `pot` command usable in a flavour are a small subset:
* `add-dep`
* `export-ports`
* `copy-in`
* `mount-in`
* `set-attribute` (the abbreviated form `set-attr` is not recognized here)
* `set-cmd`
* `set-env`
* `set-rss`

**NOTE** the `mount-in` command has to be used carefully. If the `pot` will be migrated to a different machine, the folders or the ZFS datasets has to be manually migrated as well

### Images registry and import
Once an image is created, we've seen it can be exported:
```console
# pot export -p test -t 1.0
# ls
test_1.0.xz  
test_1.0.xz.skein
```
The image freshly created can now be used to create new `pot` via the command `import`:
```console
# pot import -p test -t 1.0 -U file:///path/to/images
===>  importing test @ 1.0 as test_1_0
/var/cache/pot/test_1.0.xz                     174 MB  527 MBps    00s
/var/cache/pot/test_1.0.xz.skein               257  B  598 kBps    00s
===>  Assigning new IP: 10.192.0.15
```
* `-p potname` : the name of the pot to be imported
* `-t tag`: the version of the image to be imported
* `-U URL`: the base URL to be used to download the `pot` image

The command, when executed, will download the image from the URL (caching them to `/var/cache/pot` and create a new `pot` called `test_1_0` using that image as file system:
```console
# pot info -vp test_1_0
pot name : test_1_0
	type : single
	base : 12.0
	level : 0
	network_type : public-bridge
	ip : 10.192.0.15
		no ports exported
	active : false
	datasets:
		test_1_0/m
	snapshots:
		zroot/pot/jails/test_1_0@1569922467
		zroot/pot/jails/test_1_0/m@1569922467
	attributes:
		start-at-boot: NO
		persistent: NO
		no-rc-script: YES
		procfs: NO
		fdescfs: NO
		prunable: NO
		localhost-tunnel: NO
		to-be-pruned: NO
	resource limits:
		max amount cpus: 1
```
The `import` process automatically recognizes that the `pot` uses the `prublic-bridge` and assigns a new available IP to the imported `pot`




================================================
FILE: share/doc/pot/Installation.md
================================================
# `pot` installation guide 

This is a guide to prepare your FreeBSD installation to use the `pot` jail framework.

**NOTE**: 99% of the operations needs `root` privileges. In this guide, we consider to be logged in as `root`

**NOTE2**: ZFS is mandatory, so if you don't know what it is or you don't have a ZFS pool, please consider to read this [quick guide](https://www.freebsd.org/doc/handbook/zfs-quickstart.html).

## FreeBSD version
`pot` is mainly developed on CURRENT, but it's tested and used on 12.0.
Those two are the versions suggested.
It should work also on 11.3, even if the kernel has to be rebuild, to activate VNET(9), via the VIMAGE option.
If you want to use FreeBSD 11.3, please follow the instruction reported [here](https://www.freebsd.org/doc/handbook/kernelconfig.html) to build a custom kernel with the VIMAGE option enabled.

## Install `pot`
`pot` is available as package or port.

The suggested way is to install it using the packages:
```console
# pkg install -y pot
```
All dependencies will be automatically installed (if not yet present)

If you want to install it using ports, you can
```console
# cd /usr/ports/sysutils/pot
# make install clean
```
**NOTE** A dependency of `pot`, called `potnet` is written in Rust. If you install `potnet` via ports, the build dependencies will be built as well, and it can take really long time (depending on the power of you system, it could be several hours).

## Enable the resource limit database
One really useful feature, needed to improve the isolation between jail, is the resource limit database.
This feature is normally disabled (it seems it causes a performance penalty), and it can be enabled only at boot.
To do so:
```console
# echo kern.racct.enable=1 >> /boot/loader.conf
# reboot
```
This settings will take effect at the next reboot.

#### Known issue
We have found a performance issue with the `vtnet` driver.
If you are installing `pot` on a VM using `vtnet`, probably you want to add this line to your `/boot/loader.conf`:
```console
echo hw.vtnet.lro_disable=1 >> /boot/loader.conf
```
This settings will take effect at the next reboot.
## `pot` framework configuration

Under the folder `/usr/local/etc/pot` you'll find two files:
* `pot.default.conf`
* `pot.conf`

The `pot.default.conf` contains all the default values and it shouldn't be touched.

All needed changes can be made in the `pot.conf` file. 
This configuration file provide already a brief explanation for all paramaters, but here we go deep, explaining them one by one

### File system parameters
`pot` is based on ZFS. In the configuration file, 2 parameters are used to let `pot` use your ZFS pool correctly.
#### `POT_ZFS_ROOT` (default `zroot/pot`)
This paramater is the ZFS dataset that will be used by `pot` to store whatever will be needed: jails file systems, bases, and so on.
If the dataset doesn't exist, it will be created by the initialization command (See the last chapter).
#### `POT_FS_ROOT` (default `/opt/pot`)
This parameter is the mountpoint for the `POT_ZFS_ROOT` dataset. You shouldn't use a mountpoint that exists and contains file, otherwise the content will become unreachable.
#### `POT_CACHE` (default `/var/cache/pot`)
This parameter specifies the mountpoint of the dataset `POT_ZFS_ROOT/cache`. This dataset is used only to store `pot` images for the `import` and the `prepare`command. The default value is the suggested one.

### Network parameters
In order to use network types like `alias` or `public-bridge`, some configuration parameters are needed.

#### `POT_EXTIF` (default `em0`)
Currently, `pot` assumes that all the network traffic is going through one physical network interface.
This parameter configures `pot` to use the specified network interface.
It's relevant for `alias`, `public-bridge` and `private-bridge` network type.

#### `POT_NETWORK` (default `10.192.0.0/10`)
This parameter specifies the IPv4 address of you internal virtual network and is used by the `public-bridge` network type only.
It's wise to choose a private network segment that doesn't conflict with your current network setup.
The default address space is huge, however you can choose the network range that match your needs.

#### `POT_NETMASK` (default `255.192.0.0`)
This parameter specifies the netmask relative to the `POT_NETWORK`.
Theoretically, the netmask can be derived by the `POT_NETWORK`. For now, this is not the case, so you have to provide a netmask consistent with the network specified in `POT_NETWORK`

#### `POT_GATEWAY` (default `10.192.0.1`)
This parameter specifies the IP address that will be used as default gateway in your internal virtual network. It has to be part of the network specified in `POT_NETWORK` and it will be used as default gateway for all `pot`s attached to the internal virtual network (`public-bridge` network type).

#### `POT_EXTRA_EXTIF` (default empty)
In case your host has multiple network interfaces connected to multiple network segments, this option allows your `pot`s to access those network segments.
For example, let's say that you have 2 vlan interfaces, called `vlan20` and `vlan30`.
`vlan20` is configured as 10.0.20.4/24
`vlan30` is configured as 10.0.30.8/24
To make those segments accessible, the configuration file should look like:
```
POT_EXTRA_EXTIF=vlan20 vlan30
POT_NETWORK_vlan20=10.0.20.0/24
POT_NETWORK_vlan30=10.0.30.0/24
```
Currently there is no way to use additional external interface for the network type `alias`.
All other network types are supported

#### Network validation
If you want to check that your network configuration is valid, you can use the utility `potnet`:

```console
# potnet config-check
```
This command will show only the errors.

### Experimental parameters
There are other parameters that are used by some experimental features.

#### dns `pot`
An experimental feature is to provide an internal dns service running in a `pot` attached to the internal virtual network.
The dns is still a work in progress, however two parameters are already present for this feature:
* `POT_DNS_NAME`: this parameter specifies the name of the `pot` that will run the dns; default => `dns`
* `POT_DNS_IP`: this parameter specifies the IP (internal to the `POT_NETWORK` that the "dns `pot`" will have; default => `10.192.0.2`

#### VPN support
If your host system is using a VPN to reach some network segments, you can add some parameters in order to be able to connect your internal virtual network to those networks

* `POT_VPN_EXTIF`: the name of the network interface of the VPN software tunnel; default: `tun0`
* `POT_VPN_NETWORKS`: a list of all network segments served by the VPN; default: `192.168.0.0/16`

If you have multiple network segments, you have to list them all. For instance:
```sh
POT_VPN_NETWORKS="192.168.0.0/24 192.168.10.0/24 10.10.0.0/16"
```

## Initialize the environment
The initialization of the environment will:
* Create the ZFS datasets
* Validate the network parameters
* Configure `pf(4)` to be aware of the internal virtual network

If you are already using `pf`, I suggest to make a backup of you `pf` configuration file.

When ready, you can initialize the environment with the command (use the flag `-v` if you want a bit more of verbosity):
```console
# pot init
```

### Initialize and test the internal virtual network
The internal virtual network is not always active, but it's automatically activated if a `pot` configured to use it get started.
However, a command is provided to activate the virtual network:

```console
# pot vnet-start
```

From your host, you can now ping the virtual network default gateway (always reachable from the host):
```console
# ping 10.192.0.1
```

## Remove the `pot` environment
In order to remove the `pot` from your system, a command is provided to make it easy:
```console
# pot de-init
```
This powerful command will remove everything related to `pot` and it cannot be undone.

Even if not mandatory, it would be nice to know why you removed it.
Please, consider to write a feedback email to pizzamig at FreeBSD dot org
* What's wrong with `pot`?
* What's the missing feature I really need?
* How bad is to use it? How can it be more user-friendly?


================================================
FILE: share/doc/pot/Makefile
================================================
# Minimal makefile for Sphinx documentation
#

# You can set these variables from the command line.
SPHINXOPTS    =
SPHINXBUILD   = sphinx-build
SPHINXPROJ    = pot
SOURCEDIR     = .
BUILDDIR      = _build

# Put it first so that "make" without argument is like "make help".
help:
	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

================================================
FILE: share/doc/pot/QuickStart.md
================================================
# QuickStart Guide on `pot`

This is an introduction at the usage of `pot`, a `jail(8)` wrapper based on ZFS and `pf(4)` that naively tries to emulate containerization on FreeBSD.

`pot` uses FreeBSD specific technologies, so you need a FreeBSD machine to run it.

**NOTE**: 99% of the operations needs `root` privileges. In this guide, we consider to be logged in as `root`

**NOTE2**: ZFS is mandatory, so if you don't know what it is or you don't have a ZFS pool, please consider to read this [quick guide](https://www.freebsd.org/doc/handbook/zfs-quickstart.html).

**NOTE3**: Some features, like memory limits and memory usage, rely on the resources limit framework, normally disabled. Even if it's not mandatory, it's suggested to enable it, with the following steps:
```console
# echo kern.racct.enable=1 >> /boot/loader.conf
```
This settings will take effect at the next reboot.

**NOTE4**: One of the 3 network configuration need `VNET(9)`, the network subsystem virtualization infrastructure, enabled in the kernel.
On FreeBSD 12 and later, this kernel feature is already enabled and you don't need to do anything.
On FreeBSD 11.x, you have to rebuild the kernel, enabling the VIMAGE options, following the instruction reported [here](https://www.freebsd.org/doc/handbook/kernelconfig.html)
## Install `pot`
The installation process is pretty straightforward:
```console
# pkg install -y pot
```
That's it, `pot` and its dependencies are installed, but we're not yet ready.
#### Configuration [Optional]
Under the folder `/usr/local/etc/pot` you'll find two files:
* `pot.default.conf`
* `pot.conf`

The `pot.default.conf` contains all the default values and it shouldn't be touched.

All needed changes have to be stored in the `pot.conf` file. Please take your time to give a look to this file and to change configuration accordingly to your system

### Initialization
When you are happy with your configuration file, especially with the location of `POT_ZFS_ROOT`, you can run:
```console
# pot init
```
This command will just create the needed ZFS datasets.
## Create a simple `pot`
We can now create the simplest `pot`
```console
# pot create -p mypot -t single -b 11.3
```
**NOTE** The FreeBSD machine doesn't have to be the same version of your `pot` (jail). However, the hosting machine's version has to be greater or equal than the `pot`'s one.
For instance, you can run a FreeBSD 10.4 `pot` on a FreeBSD 11.3 host. You **cannot** run a FreeBSD 12 `pot` on a FreeBSD 11.3 host.

So, we created a `pot`, named `mypot`, based on FreeBSD 11.3 consisting of one ZFS dataset.

Now you can start it or stop it, via:
```console
# pot start mypot
# pot stop mypot
```
If you want to have a shell inside your pot:
```console
# pot term mypot
# pot run mypot # an alias for start+term
```
## A bit of diagnostic
Via the command:
```console
# pot ls
# pot ls -v # more information
```
You can see a list of the `pot`s available on you local machine. The verbose output would look like this:
```console
pot name : mypot
	ip4 : inherit
	active : true
	base : 11.3
	level : 0
	datasets:
	snapshot:
```
If you want to get some information on a specific `pot`, this command is more useful:
```console
# pot info -v -p mypot
pot name : mypot
	type : single
	base : 11.3
	level : 0
	ip4 : inherit
	active : true
	datasets:
		mypot/m
	snapshot:
	attributes:
		start-at-boot: NO
		persistent: YES
		no-rc-script: NO
		procfs: NO
		prunable: NO
```
Some explanation of this output:
* `type`: currently two types of `pot` are supported: `single`, based on one ZFS dataset, and `multi`, based on multiple ZFS dataset.
* `base`: the FreeBSD version used to build this `pot`.
* `level`: for single type `pot` the level is always `0`. Levels are explained for the multi type `pot`.
* `ip4`: the IPv4 address of the `pot` or the keyword `inherit`. By default, `inherit` is chosen, that means that this `pot` is sharing the same network stack of the running machine.
* `active`: it's a boolean value, that tells you if your `pot` is running or not.
* `datasets`: single type `pot`s have only one dataset.
* `snapshot`: the list of snapshots of this `pot`; currently empty.
* `attributes`: attributes/properties of this this `pot`

If your `pot` is running, runtime information can be obtained via:
```console
# pot start mypot
# pot show -p mypot
pot mypot
	disk usage      : 274M
	virtual memory  : 13M
	physical memory : 4820K
```
This command will show the current amount of resources used by this `pot`
## Take a snapshot of your `pot`
Thanks to ZFS, taking a snapshot of your stopped `pot` is easy and super fast:
```console
# pot stop mypot
# pot snap mypot
# pot info -v -p mypot
[..]
	snapshot:
		zroot/pot/jails/mypot@1539804703
		zroot/pot/jails/mypot/m@1539804703
```
The snapshot's name is the Unix epoch and it's used to automatically determine the snapshot's chronological sequence. 

Now you can restart it and do some real damage:
```console
# pot run mypot
root@mypot:~ # rm -rf /*
[..]
root@mypot:~ # exit
# pot stop mypot
```
We have deleted almost every file in the `pot`, the pot cannot start again (feel free to try!)
The snapshot can be used to revert all the modifications occurred between the time that the snapshot was taken and now, using the following command:
```console
# pot revert -p mypot
# pot run mypot
```
The revert command will automatically select the newest snapshot available.
## Attach a "volume" to your pot
Let's say that you want to attach a pre-existent "volume" to your `pot`.
There are several way to do that, depending on what your volume is.

### First volume type: fscomp
To support users managing ZFS datasets for `pot`, the concept of `fscomp` (AKA file system component) is introduced.
You can create a file system component in the `pot` ecosystem, that can be attached to one or more `pot`s.

When a `fscomp` is created, the underlaying ZFS dataset is created as well.

To create a `fscomp`, you can run:
```console
# pot create-fscomp -f myfscomp
```
With this command, you have created an empty ZFS dataset, a "volume", that can be attached to one or more `pot`s

A list of available `fscomp`s can be obtained with the command:
```console
# pot ls -f
```
To mount your new `fscomp` to a `pot`, you can use the command:
```console
# pot mount-in -p mypot -f myfscomp -m /mnt
# pot info -p mypot -v
```
The `-m` mandatory option represents the mountpoint (absolute pathname) inside the `pot`.

The advantage of this approach, is that `fscomp` are recognized by the `pot` framework, and a set of features is provided, like snapshot, rollback and clone.

### Second volume type: an already existent dataset
It could happen that you want to attach to a `pot` a pre-existing ZFS dataset and you don't want to create an emtpy `fscomp` and move all data there.

To add and external ZFS dataset, the command would be:
```console
# pot mount-in -p mypot -m /mnt -z zroot/mydataset
```
The only difference is the different option used (`-z` instead of `-f`)  and the argument of the option is not a `fscomp` name, but a generic valid ZFS dataset.

### Third volume type: a generic directory
There two ways to make external directories available in a `pot`: mount them or copy them.
The decision to mount or to copy is to the user to take, with obvious pros and cons.

To mount a directory, the command would be:
```console
# pot mount-in -p mypot -m /mnt -d mydir
```
The directory `mydir` will be mounted at `/mnt`

To copy a directory, the command would be:
```console
# pot copy-in -p mypot -s mydir -d /mnt
```
The directory `mydir` (and all its file) will be copied in `/mnt`, creating the directory `/mnt/mydir`

### Forth volume type: a single file
For single files, only the copy option is available.
```console
# pot copy-in -p mypot -s myfile -d /mnt
```
The file `myfile` will be copied in `/mnt`.

### Common consideration
The `mount-in` command will change the configuration of the `pot`; the "volume" will be automatically mounted when the `pot` starts and unmounted when the `pot` stops.
If you run `mount-in` when the `pot` is already running, the "volume" is mounted on the fly.
A "volume" can be used with multiple `pot`s. Potential problems, like concurrent access to the same files, cannot be managed by `pot` and are left to the user.

In order to mitigate concurrency access to the same `fscomp`, the option `-r` is introduced:
```console
# pot mount-in -p mypot-ro -f myfscomp -m /mnt -r
# pot mount-in -p mypot-rw -f myfscomp -m /mnt
```
This option will inform the framework to mount `myfscomp` in `mypot-ro` in read-only mode, while in `mypot-rw` that same `myfscomp` is mounted in read-write mode.

## Network configuration
During the creation phase, it's possible to specify which type of network our `pot` should use.
`pot` supports three different type of network configurations:
* inherit
* alias (IPv4 or IPv6) n the host network interface
* IPv4 address on the public internal virtual network
* IPv4 address on a private internal virtual network

By default, `inherit` is the chosen one.
### Network configuration: inherit
To use the `inherit` network type, a `pot` can be created with the following command:
```console
# pot create -p mypot -t single -b 11.3 -N inherit
```
The option `-N` can be omitted, because `inherit` is the default value.
The `inherit` type means that `mypot` will reuse the same network stack of the host machine.
This network type works pretty well when your `pot` doesn't provide/export any network services, but it uses the network's host as client, like a `pot` created to build applications.

### Network configuration: IPv4 or IPv6 alias
If your host is a network that support static IPs, you can assign one static IP address to your `pot` via this network configuration type.
**NOTE** Be sure that in the `pot` configuration file (`/usr/local/etc/pot/pot.conf`) you have correctly set the variable `POT_EXTIF`; this network interface is the one used to route the network traffic and to assign the IP address.
For example, your system has 192.168.178.20/24 as IP address and your network administrator reserved you the additional IP address 192.168.178.200.
To assing the latter IP address to your `pot` you can create it with the following command:
```console
# pot create -p mypot -t single -b 11.3 -N alias -i 192.168.178.200
# pot start mypot
# pot info -vp mypot
```
The alias 192.168.178.200 will be assigned to the network interface during the start phase.
Now, your `pot` is bound to the address 192.168.178.200
When the `pot` is stopped, the alias will be automatically removed from the inferface.
More information about alias addresses on network interfaces are available in the `man` page of `ifconfig(8)`

### Network configuration: public virtual network bridge
Thanks to `VNET(9)`, `pot` supports an IPv4 virtual network. This network is configured in configuration file (`/usr/local/etc/pot/pot.conf`), so be sure you have it properly configured.
This network type refers to a shared bridge where the public virtual network lives. All `pot`s with this network type will share it. The virtual internal network is connected with the ouside via NAT.

To help the `pot` framework and all users to manage the public virtual network, an additional package is required, normally automatically installed as dependency of the package `pot`. It's also manually installable via:
```console
# pkg install potnet
```
To verify you virtual network configuration, this command can be used:
```console
# potnet show
Network topology:
	network : 10.192.0.0
	min addr: 10.192.0.0
	max addr: 10.255.255.255

Addresses already taken:
	10.192.0.0	
	10.192.0.1	default gateway
	10.192.0.2	dns
```
The output is from my configuration (and also the default one), however your address' range can differ, depending on the configuration values you have adopted.

Optionally, you can start the virtual network via the command:
```console
# pot vnet-start
```
This command will create and configure the network interfaces properly and will activate `pf` to perform NAT on the virtual network.

**NOTE** This command is automatically executed when a `pot` is configured to use the public virtual network. There is no need to run it manually.

The following command will create a `pot` running on the internal network:
```console
# pot create -p mypot -t single -b 11.3 -N public-bridge -i auto
# pot run mypot
root@mypot:~ # ping 1.1.1.1
[..]
root@mypot:~ # exit
# pot stop mypot
```
The `auto` keyword will automatically select an available address in the internal virtual network and it's the default value, hence the `-i` option can be omitted.
Commands like `pot info -p mypot` and `potnet show` will show you exactly which address has been assigned to your `pot`

If you prefer to assign a specific IP address of your virtual network to your `pot`, you can just do:
```console
# pot create -p mypot2 -t single -b 11.3 -N public-bridge -i 10.192.0.10
```
`pot` will verify if the IP address is available and free to be used.

### Network configuration: private virtual network bridge
The public virtual network has the downside that all `pot`s share the same bridge, affecting isolation.
To mitigate this issue, private virtual network has been introduced.
A private virtual network is just a different bridge, that can be used to connect multiple `pot`s, but it's not automatically shared with all `pot`s.

First of all, to use a private virtual network a private bridge has to be created:
```console
# pot create-private-bridge -B mybridge -S 4
```
This command will create a new private bridge, called `mybridge`, with a network segment big enough to connect 4 `pot`s.
Using `potnet` it's possible to check the details of the private bridge via the command:
```console
# potnet show -b mybridge
	10.192.0.16	mybridge bridge - network
	10.192.0.17	mybridge bridge - gateway
	10.192.0.23	mybridge bridge - broadcast
```
The output is from my configuration, however your address' range can differ, depending on the configuration values you have adopted and the network segment available when the bridge is created.

To activate a specific bridge, you can use the command:
```console
# pot vnet-start -B mybridge
```
This command will create and configure the network interfaces properly and will activate `pf` to perform NAT on the virtual network.

**NOTE** This command is automatically executed when a `pot` is configured to use the public virtual network. There is no need to run it manually.

The following command will create a `pot` running on the private internal network:
```console
# pot create -p mypot -t single -b 11.3 -N private-bridge -B mybridge -i auto
# pot run mypot
root@mypot:~ # ping 1.1.1.1
[..]
root@mypot:~ # exit
# pot stop mypot
```
The `auto` keyword will automatically select an available address in the internal virtual network and it's the default value, hence the `-i` option can be omitted.
Commands like `pot info -p mypot` and `potnet show -b mybridge` will show you exactly which address has been assigned to your `pot`

If you prefer to assign a specific IP address of your virtual network to your `pot`, you can just do:
```console
# pot create -p mypot2 -t single -b 11.3 -N private-bridge -B mybridge -i 10.192.0.19
```
`pot` will verify if the IP address is available and free to be used.
### Export network services with the internal network
The virtual network is not visible outside the host machine, becuase it's based on NAT of the pf's NAT.
To make your network services running in your `pot` visible outside the TCP/UDP, desired ports have to be exported/redirected.
`pot` provides a command to tell which port has to be exported.
```console
# pot export-ports -p mypot -e 80 -e 443
```
The `export-ports` command will make available the port 80 and 443 outside the virtual network. At start, `pot` look for an available host port that can be used to redirect the traffic from the host to the virtual network.

To know which port is used, you can use the `show` command:
```console
# pot start mypot
# pot show -p mypot
pot mypot
	disk usage      : 274M
	virtual memory  : 13M
	physical memory : 4824K

	Network port redirection
		192.168.178.20 port 1024 -> 10.192.0.3 port 80
		192.168.178.20 port 1025 -> 10.192.0.3 port 443
```

To map the network services to a specific port, instead of leaving the decision to `pot`, the following syntax can be used:
```console
# pot export-ports -p mypot -e 80:30080 -e 443:30443
# pot start mypot
# pot show -p mypot
pot mypot
	disk usage      : 266M
	virtual memory  : 33M
	physical memory : 17M

	Network port redirection
		192.168.178.20 port 30080 -> 10.192.0.11 port 80
		192.168.178.20 port 30443 -> 10.192.0.11 port 443

```
However, there is no guarantee that the choosen ports are available.


================================================
FILE: share/doc/pot/Synopsis.md
================================================
SYNOPSIS
--------

`pot` SUBCOMMAND


================================================
FILE: share/doc/pot/conf.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# pot documentation build configuration file, created by
# sphinx-quickstart on Thu Nov 28 15:41:27 2019.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))

from recommonmark.transform import AutoStructify

# -- General configuration ------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['sphinx.ext.githubpages', 'recommonmark']

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.md'

# The master toctree document.
master_doc = 'index'

# General information about the project.
project = 'pot'
copyright = '2019, Luca Pizzamiglio'
author = 'Luca Pizzamiglio'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = "0.16.0"
# The full version, including alpha/beta/rc tags.
release = "0.16.0"

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False


# -- Options for HTML output ----------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
# This is required for the alabaster theme
# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars
html_sidebars = {
    '**': [
        'relations.html',  # needs 'show_related': True theme option to display
        'searchbox.html',
    ]
}


# -- Options for HTMLHelp output ------------------------------------------

# Output file base name for HTML help builder.
htmlhelp_basename = 'potdoc'


# -- Options for LaTeX output ---------------------------------------------

latex_elements = {
    # The paper size ('letterpaper' or 'a4paper').
    #
    # 'papersize': 'letterpaper',

    # The font size ('10pt', '11pt' or '12pt').
    #
    # 'pointsize': '10pt',

    # Additional stuff for the LaTeX preamble.
    #
    # 'preamble': '',

    # Latex figure (float) alignment
    #
    # 'figure_align': 'htbp',
}

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
#  author, documentclass [howto, manual, or own class]).
latex_documents = [
    (master_doc, 'pot.tex', 'pot Documentation',
     'Luca Pizzamiglio', 'manual'),
]


# -- Options for manual page output ---------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    (master_doc, 'pot', 'Another container framework based on jails',
     [author], 8)
]


# -- Options for Texinfo output -------------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
#  dir menu entry, description, category)
texinfo_documents = [
    (master_doc, 'pot', 'pot Documentation',
     author, 'pot', 'One line description of project.',
     'Miscellaneous'),
]

# app setup hook
def setup(app):
    app.add_config_value('recommonmark_config', {
        'auto_toc_tree_section': 'Contents',
        'enable_math': False,
        'enable_inline_math': False,
        'enable_eval_rst': True,
    }, True)
    app.add_transform(AutoStructify)


================================================
FILE: share/doc/pot/index.md
================================================
Contents
--------

* [Synopsis](Synopsis.md)
* [Description](Description.md)
* [QuickStart](QuickStart.md)
* [Installation](Installation.md)
* [migration](migration.md)
* [Images](Images.md)


================================================
FILE: share/doc/pot/migration.md
================================================
## Manual migration handbook

### Prerequisite

* testing with single type pot
* a snapshot is already present

### A xz archive of the datasets

* `zfs send -R zroot/pot/jails/mypot@1539804703 | xz > mypot.1539804703.xz`

Some statistics on FreeBSD 12.0
The file systems are accounted for 801MB, lz4 providing 2.23 as compression ration, leaving 383MB on the disk.
xz -9 is extremely slow, using +800MB of RAM and producing an output of 132MB
xz -6 (default) is quite slow, producing an output of 148MB
xz -3 is decently fast, producing an output of 164MB
xz -0 is quite fast, producing an output of 182MB

# Speculation

### A migration process

send the first xz file and combine with the receive
* `xzcat mypot.1539804703.xz | zfs receive ${POT_ZFS_ROOT}/jails/mypot@1539804703

depending on the content of pot.conf and fscomp.conf, get and extract the other datasets propery
In this case:
* xzcat mypot_m.1539804703.xz | zfs receive ${POT_ZFS_ROOT}/jails/mypot/m@1539804703


================================================
FILE: share/pot/add-dep.sh
================================================
#!/bin/sh
# shellcheck disable=SC3033,SC3040,SC3043
:

add-dep-help()
{
	cat <<-"EOH"
	pot add-dep [-hv] -p potname -P depPot
	  -h print this help
	  -v verbose
	  -p potname : the working pot
	  -P depPot : the pot to depend on. Will be started automatically
	              before starting the working pot "potname".
	EOH
}

# $1 pot
# $2 depPot
_add_dependency()
{
	local _depPot _pname _cdir
	_pname="$1"
	_depPot="$2"
	_cdir=$POT_FS_ROOT/jails/$_pname/conf
	echo "pot.depend=$_depPot" >> "$_cdir"/pot.conf
}

pot-add-dep()
{
	local _pname _depPot
	_depPot=
	_pname=
	OPTIND=1
	while getopts "hvp:P:" _o ; do
		case "$_o" in
		h)
			add-dep-help
			return 0
			;;
		v)
			_POT_VERBOSITY=$(( _POT_VERBOSITY + 1))
			;;
		P)
			_depPot="$OPTARG"
			;;
		p)
			_pname="$OPTARG"
			;;
		*)
			add-dep-help
			return 1
			;;
		esac
	done

	if [ -z "$_pname" ]; then
		_error "A pot name is mandatory"
		add-dep-help
		return 1
	fi
	if [ -z "$_depPot" ]; then
		_error "A dependency pot is mandatory"
		add-dep-help
		return 1
	fi
	if [ "$_pname" = "$_depPot" ]; then
		_error "a pot cannot be run time dependecy of itself"
		add-dep-help
		return 1
	fi
	if ! _is_pot "$_pname" ; then
		_error "pot $_pname is not valid"
		add-dep-help
		return 1
	fi
	if ! _is_pot "$_depPot" ; then
		_error "dependency pot $_depPot is not valid"
		add-dep-help
		return 1
	fi
	if ! _is_uid0 ; then
		return 1
	fi
	_add_dependency "$_pname" "$_depPot"
}


================================================
FILE: share/pot/clone-fscomp.sh
================================================
#!/bin/sh
# shellcheck disable=SC3033,SC3040,SC3043
:

clone-fscomp-help()
{
	cat <<-"EOH"
	pot clone-fscomp [-hv] -f fscomp -F fscomp
	  -h print this help
	  -v verbose
	  -F fscomp : the fscomp to be cloned (mandatory)
	  -f fscomp : the fscomp name (mandatory)
	EOH
}

# $1 new fscomp name
# $2 old fscomp name
_cf_zfs()
{
	local _fscomp _cfscomp _fsdset _fsdir _snap
	_fscomp=$1
	_cfscomp=$2
	_fsdset=${POT_ZFS_ROOT}/fscomp
	_fsdir=${POT_FS_ROOT}/fscomp
	_snap=$( _zfs_last_snap "$_fsdset/$_cfscomp" )
	if [ -z "$_snap" ]; then
		_error "$_fsdset/$_cfscomp has no snapshots - please take one"
		return 1
	else
		_debug "Cloning $_cfscomp@$_snap into $_fsdset/$_fscomp"
		zfs clone -o mountpoint="$_fsdir/$_fscomp" "$_fsdset/$_cfscomp@$_snap" "$_fsdset/$_fscomp"
	fi
	return 0 # true
}

pot-clone-fscomp()
{
	local _fscomp _cfscomp
	_fscomp=
	_cfscomp=
	OPTIND=1
	while getopts "hvf:F:" _o ; do
		case "$_o" in
		h)
			clone-fscomp-help
			${EXIT} 0
			;;
		v)
			_POT_VERBOSITY=$(( _POT_VERBOSITY + 1))
			;;
		f)
			_fscomp=$OPTARG
			;;
		F)
			_cfscomp=$OPTARG
			;;
		*)
			clone-fscomp-help
			${EXIT} 1
			;;
		esac
	done
	# parameter validation
	if [ -z "$_fscomp" ]; then
		_error "fscomp name is missing (option -f)"
		clone-fscomp-help
		${EXIT} 1
	fi
	if [ -z "$_cfscomp" ]; then
		_error "clonable fscomp name is missing (option -F)"
		clone-fscomp-help
		${EXIT} 1
	fi
	if _zfs_dataset_valid "${POT_ZFS_ROOT}/fscomp/$_fscomp" ; then
		_error "fscomp $_fscomp already exists"
		${EXIT} 1
	fi
	if ! _zfs_dataset_valid "${POT_ZFS_ROOT}/fscomp/$_cfscomp" ; then
		_error "fscomp $_cfscomp doesn't exist"
		${EXIT} 1
	fi
	if ! _is_uid0 ; then
		${EXIT} 1
	fi
	if ! _cf_zfs "$_fscomp" "$_cfscomp" ; then
		${EXIT} 1
	fi
}


================================================
FILE: share/pot/clone.sh
================================================
#!/bin/sh
# shellcheck disable=SC3033,SC3040,SC3043
:

trap _cj_undo_clone TERM INT
_set_pipefail

clone-help()
{
	cat <<-"EOH"
	pot clone [-hvF] -p potname -P basepot [-i ipaddr]
	  -h print this help
	  -v verbose
	  -k keep the pot, if clone fails
	  -P potname : the pot to be cloned (template)
	  -s snapshot : the snapshot to be used to clone
	  -p potname : the name of the pot that is created
	  -f flavour : flavour to be used
	  -N network-type : new network type of the cloned pot
	  -i ipaddr : an ip address or the keyword auto (if applicable)
	  -B bridge-name : the name of the private bridge to be used
	  -S network-stack : the network stack (ipv4, ipv6 or dual)
	  -d dns : change pot dns resolver configuration, one of
	           inherit       - inherit from jailhost
	           pot           - the pot configured in POT_DNS_NAME
	           custom:<file> - copy <file> into pot configuration
	           off           - leave resolver config unaltered
	  -F : automatically take snapshots of dataset that has none
	EOH
}

# $1 pot name
_cj_undo_clone()
{
	_POT_VERBOSITY=0
	if [ -z "$_cleanup_pname" ]; then
		${EXIT} 1
	fi
	# stop in subshell, so it won't end script execution
	(
		pot-cmd stop "$_cleanup_pname" >/dev/null 2>&1
	)
	if [ "$_cleanup_keep" != "YES" ]; then
		pot-cmd destroy -Fp "$_cleanup_pname" -q
	fi
	unset _cleanup_pname
	unset _cleanup_keep
	${EXIT} 1
}

# $1 pot name
# $2 pot-base name
# $3 auto-snapshot
# $4 custom snapshot tag
_cj_zfs()
{
	local _pname _potbase _jdset _pdir _pbdir _pbdset _mnt_p _opt _autosnap _snaptag _pb_type _snap __last_snap
	_pname=$1
	_potbase=$2
	_autosnap="$3"
	_snap="$4"
	__last_snap=
	_jdset=${POT_ZFS_ROOT}/jails/$_pname
	_pbdset=${POT_ZFS_ROOT}/jails/$_potbase
	_pdir=${POT_FS_ROOT}/jails/$_pname
	_pbdir=${POT_FS_ROOT}/jails/$_potbase
	_pb_type="$( _get_conf_var "$_potbase" pot.type )"
	# Create the main jail zfs dataset
	if ! _zfs_dataset_valid "$_jdset" ; then
		zfs create "$_jdset"
	else
		_info "$_jdset exists already"
	fi
	# Create the conf directory
	if [ ! -d "$_pdir/conf" ]; then
		_debug "Create conf dir ($_pdir/conf)"
		mkdir -p "$_pdir/conf"
	fi
	if [ -e "$_pdir/conf/fscomp.conf" ]; then
		rm -f "$_pdir/conf/fscomp.conf"
	fi
	_debug "Cloning $_potbase with snap $_snap"
	_update_fscomp "$_potbase"
	if [ "$_pb_type" = "single" ]; then
		_dset="${_pbdset}/m"
		if [ -z "$_snap" ]; then
			_snap=$( _zfs_last_snap "$_dset" )
		fi
		if [ -z "$_snap" ]; then
			if [ "$_autosnap" = "YES" ]; then
				_snaptag="$(date +%s)"
				_info "$_dset has no snap - taking a snapshot on the fly with tag $_snaptag"
				zfs snapshot "${_dset}@${_snaptag}"
				_snap=$_snaptag
			else
				_error "$_dset has no snap - please take a snapshot of $_potbase"
				_cj_undo_clone
				return 1 # error
			fi
		fi
		_debug "clone $_dset@$_snap into $_jdset/m"
		zfs clone -o mountpoint="$_pdir/m" "$_dset@$_snap" "$_jdset/m"
		cp "${_pbdir}/conf/fscomp.conf" "$_pdir/conf/fscomp.conf"
	elif [ "$_pb_type" = "multi" ]; then
		# Create the root mountpoint
		_create_pot_mountpoint "$_pdir/m"
		if [ -z "$_snap" ]; then
			__last_snap="YES"
		fi
		while read -r line ; do
			_dset=$( echo "$line" | awk '{print $1}' )
			_mnt_p=$( echo "$line" | awk '{print $2}' )
			_opt=$( echo "$line" | awk '{print $3}' )
			# ro components are replicated "as is"
			if [ "$_opt" = ro ] ; then
				_debug "$_dset ${_mnt_p} $_opt"
				echo "$_dset ${_mnt_p} $_opt" >> "$_pdir/conf/fscomp.conf"
			else
				# managing potbase datasets
				if [ "$_dset" != "${_dset##"${_pbdset}"}" ]; then
					_dname="${_dset##"${_pbdset}"/}"
					if [ "$__last_snap" = "YES" ]; then
						_snap=$( _zfs_last_snap "$_dset" )
					fi
					if [ -z "$_snap" ]; then
						if [ "$_autosnap" = "YES" ]; then
							_snaptag="$(date +%s)"
							_info "$_dset has no snap - taking a snapshot on the fly with tag $_snaptag"
							zfs snapshot "${_dset}@${_snaptag}"
							_snap=$_snaptag
						else
							_error "$_dset has no snap - please take a snapshot of $_potbase"
							_cj_undo_clone "$_pname"
							return 1
						fi
					fi
					if _zfs_exist "$_jdset/$_dname" "$_pdir/$_dname" ; then
						_debug "$_dname dataset already cloned"
					else
						_debug "clone $_dset@$_snap into $_jdset/$_dname"
						zfs clone -o mountpoint="$_pdir/$_dname" "$_dset@$_snap" "$_jdset/$_dname"
						if [ -z "$_opt" ]; then
							_debug "$_jdset/$_dname ${_mnt_p}"
							echo "$_jdset/$_dname ${_mnt_p}" >> "$_pdir/conf/fscomp.conf"
						else
							_debug "$_jdset/$_dname ${_mnt_p} $_opt"
							echo "$_jdset/$_dname ${_mnt_p} $_opt" >> "$_pdir/conf/fscomp.conf"
						fi
					fi
				# managing fscomp datasets - the simple way - no clone support for fscomp
				elif [ "$_dset" != "${_dset##"${POT_ZFS_ROOT}"/fscomp}" ]; then
					_debug "$_dset ${_mnt_p}"
					echo "$_dset ${_mnt_p}" >> "$_pdir/conf/fscomp.conf"
				else
					_error "not able to manage $_dset"
				fi
			fi
		done < "${POT_FS_ROOT}/jails/$_potbase/conf/fscomp.conf"
	fi
	return 0 # true
}

# $1 pot name
# $2 pot-base name
# $3 network type
# $4 ip
# $5 bridge name
# $6 network stack
# $7 dns
_cj_conf()
{
	local _pname _potbase _ip _network_type _bridge_name _stack _dns _potdns
	_pname=$1
	_potbase=$2
	_network_type=$3
	_ip=$4
	_bridge_name=$5
	_stack=$6
	_dns=$7
	_pdir=${POT_FS_ROOT}/jails/$_pname
	_pbdir=${POT_FS_ROOT}/jails/$_potbase
	if [ ! -d "$_pdir/conf" ]; then
		mkdir -p "$_pdir/conf"
	fi
	if [ "$_dns" != "${_dns##custom:}" ]; then # if dns is custom:filename
		cp "${_dns##custom:}" "$_pdir/conf/resolv.conf" # copy custom dns config to pot config
		_potdns=custom
	else
		_potdns="$_dns"
	fi
	grep -vE '^(host.hostname|bridge|ip|vnet|network_type|pot.stack|pot.dns)' "$_pbdir/conf/pot.conf" > "$_pdir/conf/pot.conf"
	{
		echo "host.hostname=\"$( _get_usable_hostname "${_pname}" )\""
		echo "pot.stack=$_stack"
		echo "network_type=$_network_type"
		case "$_network_type" in
		"inherit")
			echo "vnet=false"
			;;
		"alias")
			echo "vnet=false"
			echo "ip=$_ip"
			;;
		"public-bridge")
			echo "vnet=true"
			echo "ip=$_ip"
			;;
		"private-bridge")
			echo "vnet=true"
			echo "ip=$_ip"
			echo "bridge=$_bridge_name"
			;;
		esac
		echo "pot.dns=$_potdns"
	} >> "$_pdir/conf/pot.conf"
	if [ -e "$_pbdir/conf/prestart.sh" ]; then
		cp "$_pbdir/conf/prestart.sh" "$_pdir/conf/prestart.sh"
	fi
	if [ -e "$_pbdir/conf/prestop.sh" ]; then
		cp "$_pbdir/conf/prestop.sh" "$_pdir/conf/prestop.sh"
	fi
	if [ -e "$_pbdir/conf/poststart.sh" ]; then
		cp "$_pbdir/conf/poststart.sh" "$_pdir/conf/poststart.sh"
	fi
	if [ -e "$_pbdir/conf/poststop.sh" ]; then
		cp "$_pbdir/conf/poststop.sh" "$_pdir/conf/poststop.sh"
	fi
	if [ -e "$_pbdir/conf/resolv.conf" ]; then
		cp "$_pbdir/conf/resolv.conf" "$_pdir/conf/resolv.conf"
	fi
}

pot-clone()
{
	local _pname _ipaddr _potbase _pblvl _autosnap _pb_type _pb_network_type _network_type _bridge_name _network_stack _snap _flv _dns
	_pname=
	_ipaddr=
	_potbase=
	_pblvl=0
	_autosnap="NO"
	_bridge_name=
	_network_stack=
	_cleanup_keep="NO"
	_snap=
	_flv=
	_dns=
	OPTIND=1
	while getopts "hvp:i:P:FN:B:S:f:ks:d:" _o ; do
		case "$_o" in
			h)
				clone-help
				${EXIT} 0
				;;
			v)
				_POT_VERBOSITY=$(( _POT_VERBOSITY + 1))
				;;
			k)
				_cleanup_keep="YES"
				;;
			p)
				if ! _is_valid_potname "$OPTARG" ; then
					_error "$OPTARG is not a valid name for a pot ('.' is the only character not allowed)"
					${EXIT} 1
				fi
				_pname=$OPTARG
				;;
			N)
				# shellcheck disable=SC2086
				if ! _is_in_list "$OPTARG" $_POT_NETWORK_TYPES ; then
					_error "Network type $OPTARG not recognized"
					clone-help
					${EXIT} 1
				fi
				_network_type="$OPTARG"
				;;
			i)
				if [ -z "$_ipaddr" ]; then
					_ipaddr="$OPTARG"
				else
					_ipaddr="$_ipaddr $OPTARG"
				fi
				;;
			P)
				_potbase=$OPTARG
				;;
			s)
				_snap=$OPTARG
				;;
			B)
				_bridge_name=$OPTARG
				;;
			S)
				if ! _is_in_list "$OPTARG" "ipv4" "ipv6" "dual" ; then
					_error "Network stack $OPTARG not valid"
					create-help
					${EXIT} 1
				fi
				_network_stack="$OPTARG"
				;;
			F)
				_autosnap="YES"
				;;
			f)
				if _is_flavour "$OPTARG" ; then
					if [ -z "$_flv" ]; then
						_flv="$OPTARG"
					else
						_flv="$_flv $OPTARG"
					fi
				else
					_error "Flavour $OPTARG not found"
					_debug "Looking in the flavour dir ${_POT_FLAVOUR_DIR}"
					${EXIT} 1
				fi
				;;
			d)
				case $OPTARG in
					inherit|pot|off)
						_dns=$OPTARG
						;;
					custom:*)
						if [ -r "${OPTARG##custom:}" ]; then
							_dns=$OPTARG
						else
							_error "The file ${OPTARG##custom:} is not valid or readable"
							${EXIT} 1
						fi
						;;
					*)
							_error "'${OPTARG}' is not a valid dns option"
							clone-help
							${EXIT} 1
				esac
				;;

			*)
				clone-help
				${EXIT} 1
				;;
		esac
	done

	if [ -z "$_pname" ]; then
		_error "pot name is missing (option -p)"
		clone-help
		${EXIT} 1
	fi
	if [ -z "$_potbase" ]; then
		_error "reference pot name is missing (option -P)"
		clone-help
		${EXIT} 1
	fi
	if ! _is_pot "$_potbase" quiet ; then
		_error "reference pot $_potbase not found"
		clone-help
		${EXIT} 1
	fi
	if _is_pot "$_pname" quiet ; then
		_error "pot $_pname already exists"
		clone-help
		${EXIT} 1
	fi
	if [ -n "$_snap" ]; then
		if [ "$_autosnap" = "YES" ]; then
			_error "-s and -F are incompatible - which snapshot should I use"
			clone-help
			${EXIT} 1
		fi
		# shellcheck disable=SC2046
		if ! _is_in_list "$_snap" $(_get_pot_snaps "$_potbase" | tr '\n' ' ') ; then
			_error "snapshot $_snap not found"
			_debug "snapshots available $(_get_pot_snaps "$_potbase" | tr '\n' ' ')"
			clone-help
			${EXIT} 1
		fi
	fi
	if [ -z "$_network_type" ]; then
		_pb_network_type="$( _get_pot_network_type "$_potbase" )"
		if [ -z "$_pb_network_type" ] ; then
			_error "Configuration file for $_potbase contains obsolete elements"
			_error "Please run pot update-config -p $_potbase to fix"
			${EXIT} 1
		fi
		_network_type="$_pb_network_type"
	fi
	if [ -z "$_network_stack" ]; then
		_network_stack="$( _get_pot_network_stack "$_potbase" )"
	fi
	if ! _ipaddr="$( _validate_network_param "$_network_type" "$_ipaddr" "$_bridge_name" "$_network_stack" )" ; then
		echo "$_ipaddr"
		clone-help
		${EXIT} 1
	fi
	if [ -z "$_dns" ]; then
		_dns="$(_get_conf_var "$_potbase" pot.dns)"
	fi
	_pblvl="$( _get_conf_var "$_potbase" pot.level )"
	_pb_type="$( _get_conf_var "$_potbase" pot.type )"
	if [ "$_pblvl" = "0" ] && [ "$_pb_type" != "single" ]; then
		_error "Level 0 pots cannot be cloned"
		clone-help
		${EXIT} 1
	fi
	if ! _is_uid0 ; then
		${EXIT} 1
	fi
	export _cleanup_pname="$_pname"
	export _cleanup_keep
	if ! _cj_zfs "$_pname" "$_potbase" "$_autosnap" "$_snap" ; then
		${EXIT} 1
	fi
	if ! _cj_conf "$_pname" "$_potbase" "$_network_type" "$_ipaddr" "$_bridge_name" "$_network_stack" "$_dns"; then
		${EXIT} 1
	fi
	if [ -n "$_flv" ]; then
		for _f in $_flv ; do
			if ! _exec_flv "$_pname" "$_f" ; then
				_cj_undo_clone
				${EXIT} 1
			fi
		done
	fi
	unset _cleanup_pname
	unset _cleanup_keep
}


================================================
FILE: share/pot/common-flv.sh
================================================
#!/bin/sh
# shellcheck disable=SC3033,SC3040,SC3043

# $1 flavour name
_is_flavour()
{
	local _flv_name
	_flv_name="$1"
	if [ -n "$( _get_flavour_script "$_flv_name" )" ] ||
		[ -n "$( _get_flavour_cmd_file "$_flv_name" )" ]; then
		return 0 # true
	fi
	return 1 # false
}

_get_flavour_script()
{
	local _flv_name
	_flv_name="$1"
	if [ -f "$_flv_name" ] && [ "$_flv_name" != "${_flv_name%%.sh}" ]; then ## it's a script path name
		echo "$_flv_name"
	elif [ -f "$_flv_name.sh" ];  then ## it's a path name
		echo "$_flv_name.sh"
	elif [ -f "./$_flv_name.sh" ]; then
		echo "./$_flv_name.sh"
	elif [ -f "${_POT_FLAVOUR_DIR}/$_flv_name.sh" ]; then
		echo "${_POT_FLAVOUR_DIR}/$_flv_name.sh"
	fi
}

_get_flavour_cmd_file()
{
	local _flv_name
	_flv_name="$1"
	# if the flavor name ends with .sh return immediately
	if [ "$_flv_name" != "${_flv_name%%.sh}" ]; then
		return
	fi
	if [ -f "$_flv_name" ] && [ -r "$_flv_name" ]; then ## it's a cmd file path name
		echo "$_flv_name"
	elif [ -f "./$_flv_name" ] && [ -r "./$_flv_name" ]; then
		echo "./$_flv_name"
	elif [ -f "${_POT_FLAVOUR_DIR}/$_flv_name" ] || [ -r "${_POT_FLAVOUR_DIR}/$_flv_name" ]; then
		echo "${_POT_FLAVOUR_DIR}/$_flv_name"
	fi
}

# $1 the cmd
# all other parameter will be ignored
# tested
_is_cmd_flavorable()
{
	local _cmd
	_cmd=$1
	case $_cmd in
		add-dep|set-attribute|\
		copy-in|copy-in-flv|mount-in|\
		set-rss|export-ports|\
		set-cmd|set-env)
		return 0
		;;
	esac
	return 1 # false
}

# Special version of set-cmd usable only for flavours
# $1 : pot name
# $2 : the set-cmd line in the file
_flv_set_cmd()
{
	local _pname _line _cmd
	_pname="$1"
	_line="$2"
	_cmd="${_line#set-cmd -c }"
	if [ "$_line" = "$_cmd" ]; then
		_error "In flavour only 'set-cmd -c ' is supported"
		return 1
	fi
	_set_command "$_pname" "$_cmd"
}

_exec_flv()
{
	local _pname _flv _pdir _flv_cmd_file _flv_script _flv_dir _previous_pwd _persist
	_pname=$1
	_flv=$2
	_pdir=${POT_FS_ROOT}/jails/$_pname
	_debug "Flavour: $_flv"
	_flv_cmd_file="$( _get_flavour_cmd_file "$_flv" )"
	_flv_dir=$(dirname "${_flv_cmd_file}")
	if [ -n "${_flv_cmd_file}" ]; then
		_debug "Executing $_flv pot commands on $_pname"
		while read -r line ; do
			# shellcheck disable=SC2086
			if _is_cmd_flavorable $line ; then
				if [ "$line" != "${line#set-cmd}" ]; then
					# workaround for set-cmd / damn quoting and shell scripts
					if ! _flv_set_cmd "$_pname" "$line" ; then
						return 1
					fi
				elif [ "$line" != "${line#copy-in-flv}" ]; then
					# copy-in relative to flavour dir
					_previous_pwd=$PWD
					if ! cd "$_flv_dir"; then
						_error "Can't chdir to flavour dir $_flv_dir"
						return 1
					fi
					line=$(echo "$line" | sed "s/^copy-in-flv/copy-in/")
					if ! pot-cmd $line -p "$_pname" ; then
						return 1
					fi
					if ! cd "$_previous_pwd"; then
						_error "Can't chdir to previous pwd $_previous_pwd"
						return 1
					fi
				else
					# shellcheck disable=SC2086
					if ! pot-cmd $line -p "$_pname" ; then
						return 1
					fi
				fi
			else
				_error "Flavor $_flv: line $line not valid - ignoring"
			fi
		done < "${_flv_cmd_file}"
	fi
	_flv_script="$( _get_flavour_script "$_flv" )"
	if [ -n "${_flv_script}" ]; then
		_debug "Starting $_pname pot for the initial bootstrap"

		_persist="$(_get_conf_var "$_pname" "pot.attr.persistent")"
		if [ "$_persist" = "NO" ]; then
			_debug "Setting pot $_pname temporarily to persistent"
			if ! pot-cmd set-attribute -A persistent -V YES -p "$_pname" ; then
				return 1
			fi
		fi

		pot-cmd start "$_pname"
		cp -v "${_flv_script}" "$_pdir/m/tmp"
		chmod a+x "$_pdir/m/tmp/$(basename "${_flv_script}" )"
		_debug "Executing $_flv script on $_pname"
		if ! jexec "$_pname" "/tmp/$(basename "${_flv_script}")" "$_pname" ; then
			_error "create: flavour $_flv failed (script)"
			return 1
		fi
		pot-cmd stop "$_pname"
		if [ "$_persist" = "NO" ]; then
			_debug "Reverting pot $_pname to non-persistent"
			if ! pot-cmd set-attribute -A persistent -V NO -p "$_pname" ; then
				return 1
			fi
		fi
	else
		_debug "No shell script available for the flavour $_flv"
	fi

}


================================================
FILE: share/pot/common.sh
================================================
#!/bin/sh
# shellcheck disable=SC2034,SC3033,SC3040,SC3043

: "${EXIT:=exit}"
: "${ECHO:=echo}"
: "${SED:=sed}"

_POT_RW_ATTRIBUTES="start-at-boot early-start-at-boot persistent no-rc-script prunable localhost-tunnel no-tmpfs no-etc-hosts"
_POT_RO_ATTRIBUTES="to-be-pruned"
_POT_NETWORK_TYPES="inherit alias public-bridge private-bridge"

# not devfs handles separately
_POT_JAIL_RW_ATTRIBUTES='enforce_statfs mount fdescfs linprocfs nullfs procfs tmpfs zfs raw_sockets sysvshm sysvsem sysvmsg children mlock devfs_ruleset exec_stop stop_timeout'

# N: arg name jail command, T: type of data, D: deafult value
# devfs is always mounted
_POT_DEFAULT_mount_N='allow.mount'
_POT_DEFAULT_mount_T='bool'
_POT_DEFAULT_mount_D='NO'
_POT_DEFAULT_fdescfs_N='mount.fdescfs'
_POT_DEFAULT_fdescfs_T='bool'
_POT_DEFAULT_fdescfs_D='NO'
_POT_DEFAULT_linprocfs_N='allow.mount.linprocfs'
_POT_DEFAULT_linprocfs_T='bool'
_POT_DEFAULT_linprocfs_D='NO'
_POT_DEFAULT_nullfs_N='allow.mount.nullfs'
_POT_DEFAULT_nullfs_T='bool'
_POT_DEFAULT_nullfs_D='NO'
_POT_DEFAULT_procfs_N='mount.procfs'
_POT_DEFAULT_procfs_T='bool'
_POT_DEFAULT_procfs_D='NO'
_POT_DEFAULT_tmpfs_N='allow.mount.tmpfs'
_POT_DEFAULT_tmpfs_T='bool'
_POT_DEFAULT_tmpfs_D='NO'
_POT_DEFAULT_zfs_N='allow.mount.zfs'
_POT_DEFAULT_zfs_T='bool'
_POT_DEFAULT_zfs_D='NO'
_POT_DEFAULT_raw_sockets_N='allow.raw_sockets'
_POT_DEFAULT_raw_sockets_T='bool'
_POT_DEFAULT_raw_sockets_D='NO'
_POT_DEFAULT_sysvshm_N='sysvshm'
_POT_DEFAULT_sysvshm_T='sysvopt'
_POT_DEFAULT_sysvshm_D='new'
_POT_DEFAULT_sysvsem_N='sysvsem'
_POT_DEFAULT_sysvsem_T='sysvopt'
_POT_DEFAULT_sysvsem_D='new'
_POT_DEFAULT_sysvmsg_N='sysvmsg'
_POT_DEFAULT_sysvmsg_T='sysvopt'
_POT_DEFAULT_sysvmsg_D='new'
_POT_DEFAULT_children_N='children.max'
_POT_DEFAULT_children_T='uint'
_POT_DEFAULT_children_D='0'
_POT_DEFAULT_devfs_ruleset_N='devfs_ruleset'
_POT_DEFAULT_devfs_ruleset_T='uint'
_POT_DEFAULT_devfs_ruleset_D='4'
_POT_DEFAULT_mlock_N='allow.mlock'
_POT_DEFAULT_mlock_T='bool'
_POT_DEFAULT_mlock_D='NO'
_POT_DEFAULT_exec_stop_N='exec.stop'
_POT_DEFAULT_exec_stop_T='string'
_POT_DEFAULT_exec_stop_D=''
_POT_DEFAULT_stop_timeout_N='stop.timeout'
_POT_DEFAULT_stop_timeout_T='uint'
_POT_DEFAULT_stop_timeout_D='10'
# 0:everything, 1:chroot+below(poudriere), 2:just chroot(normal jail)
_POT_DEFAULT_enforce_statfs_N='enforce_statfs'
_POT_DEFAULT_enforce_statfs_T='uint'
_POT_DEFAULT_enforce_statfs_D='2'


__POT_MSG_ERR=0
__POT_MSG_INFO=1
__POT_MSG_DBG=2
# $1 severity
_msg()
{
	local _sev
	_sev=$1
	shift
	if [ "$_sev" -gt "${_POT_VERBOSITY:-0}" ]; then
		return
	fi
	case $_sev in
		"$__POT_MSG_ERR")
			echo "###> " "$@"
			;;
		"$__POT_MSG_INFO")
			echo "===> " "$@"
			;;
		"$__POT_MSG_DBG")
			echo "=====> " "$@"
			;;
		*)
			;;
	esac
}

_error()
{
	_msg $__POT_MSG_ERR "$@"
}

_info()
{
	_msg $__POT_MSG_INFO "$@"
}

_debug()
{
	_msg $__POT_MSG_DBG "$@"
}

# $1 quiet / no _error message is emitted
_qerror()
{
	if [ "$1" != "quiet" ]; then
		_error "$@"
	fi
}

_set_pipefail()
{
	local _major _version
	_major="$(sysctl -n kern.osrelease | cut -f 1 -d '.')"
	if [ "$_major" -ge "13" ]; then
		# shellcheck disable=SC3040
		set -o pipefail
		return
	fi
	_version="$(sysctl -n kern.osrelease | cut -f 1 -d '-')"
	case "$_version" in
		"12.1"|"12.2")
			# shellcheck disable=SC3040
			set -o pipefail
			;;
	esac
}

# tested
_is_verbose()
{
	if [ "$_POT_VERBOSITY" -gt $__POT_MSG_INFO ]; then
		return 0 # true
	else
		return 1 # false
	fi
}

# $1 quiet / no _error messages are emitted (sometimes useful)
_is_uid0()
{
	if [ "$(id -u)" = "0" ]; then
		return 0 # true
	else
		_qerror "$1" "This operation needs 'root' privilegies"
		return 1 # false
	fi
}

# tested
# check if the argument is an absolute pathname
_is_absolute_path()
{
	if [ "$1" = "${1#/}" ]; then
		return 1 # false
	else
		return 0 # true
	fi
}

# save encoded parameters suitable for use in `set --`
_save_params () {
	for i; do
		printf %s\\n "$i" |\
		sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/"
	done
	echo " "
}

# get system boot time in seconds since the epoch
_get_system_uptime() {
	sysctl -n kern.boottime | sed -e 's/.*[^u]sec = \([0-9]*\).*$/\1/'
}

# check if the argument is a valid boolean value
# if valid, it returns true and it echo a normalized version of the boolean value (YES/NO)
# if not valid, it return false
_normalize_true_false() {
	case $1 in
		[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn])
			echo YES
			return 0 # true
			;;
		[Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff])
			echo NO
			return 0 # true
			;;
		*)
			return 1 # false
	esac
}

# validate some values of the configuration files
# $1 quiet / no _error messages are emitted
_conf_check()
{
	if [ -z "${POT_ZFS_ROOT}" ]; then
		_qerror "$1" "POT_ZFS_ROOT is mandatory"
		return 1 # false
	fi
	if [ -z "${POT_FS_ROOT}" ]; then
		_qerror "$1" "POT_FS_ROOT is mandatory"
		return 1 # false
	fi
	if ! getent group "${POT_GROUP:-pot}" >/dev/null 2>&1; then
		_qerror "$1" "Group '${POT_GROUP:-pot}' is missing, create it or change POT_GROUP"
		return 1 # false
	fi
	return 0 # true
}

# it checkes that the pot environment is initialized
# $1 quiet / no _error messages are emitted
_is_init()
{
	if ! _conf_check "$1" ; then
		_qerror "$1" "Configuration not valid, please verify it"
		return 1 # false
	fi
	if ! _zfs_exist "${POT_ZFS_ROOT}" "${POT_FS_ROOT}" ; then
		_qerror "$1" "Your system is not initialized, please run pot init"
		return 1 # false
	fi
	if ! _zfs_dataset_valid "${POT_ZFS_ROOT}/bases" || \
	   ! _zfs_dataset_valid "${POT_ZFS_ROOT}/jails" || \
	   ! _zfs_dataset_valid "${POT_ZFS_ROOT}/fscomp" ; then
		_qerror "$1" "Your system is not propery initialized, please run pot init to fix it"
	fi
}

# check the POT_TMP directory
# if missing, it will initialize it
_is_pot_tmp_dir()
{
	local _pot_tmp
	_pot_tmp="${POT_TMP:-/tmp}"
	if [ ! -d "$_pot_tmp" ]; then
		mkdir -p "$_pot_tmp"
	fi
	if [ ! -d "$_pot_tmp" ]; then
		return 1 # false
	fi
}

# set status of pot, locks properly
# $1 pot name
# $2 status to set
# $3 interfaces for pot (epaira)
_set_pot_status()
{
	local _pname _status _interfaces _verbose _param
	_pname=$1
	_status=$2
	_interfaces=$3
	_param=$(_save_params "-p" "$_pname" "-s" "$_status")
	if [ -n "$_interfaces" ]; then
		_param="$_param"$(_save_params "-i" "$_interfaces")
	fi
	if [ "$_POT_VERBOSITY" -gt 1 ]; then
		_verbose=$(printf -- "-%$(( _POT_VERBOSITY - 1 ))s" |\
		  tr " " "v")
		_param="$_param"$(_save_params "$_verbose")
	fi
	eval "set -- $_param"
	lockf "${POT_TMP:-/tmp}/pot-lock-$_pname" "${_POT_PATHNAME}"\
	  set-status "$@"
}

# check if the dataset is a dataset name
# $1 the dataset NAME
# tested
_zfs_dataset_valid()
{
	[ -z "$1" ] && return 1 # return false
	if [ "$1" = "$( zfs list -o name -H "$1" 2> /dev/null)" ]; then
		return 0 # true
	else
		return 1 # false
	fi
}

# check if the dataset $1 with the mountpoint $2 exists
# $1 the dataset NAME
# $2 the mountpoint
# tested
_zfs_exist()
{
	local _mnt_
	[ -z "$2" ] && return 1 # false
	if ! _zfs_dataset_valid "$1" ; then
		return 1 # false
	fi
	_mnt_="$(zfs list -H -o mountpoint "$1" 2> /dev/null )"
	if [ "$_mnt_" != "$2" ]; then
		return 1 # false
	fi
	return 0 # true
}

# check if the dataset $1 is mounted
# $1 the dataset NAME
_zfs_mounted()
{
	if [ "$(zfs get -Ho value mounted "$1")" != "yes" ]; then
		return 1; # false
	fi
	return 0 # true
}

# given a dataset, look for the corresponding mountpoint
# $1 the dataset
_get_zfs_mountpoint()
{
	local _mnt_p _dset
	_dset=$1
	_mnt_p="$( zfs list -o mountpoint -H "$_dset" 2> /dev/null )"
	echo "$_mnt_p"
}

# given a mountpoint, look for the corresponding dataset
# $1 the mountpoint
_get_zfs_dataset()
{
	local _mnt_p _dset
	_mnt_p=$1
	_dset=$(zfs list -o name,mountpoint -H 2>/dev/null | awk -v "mntp=${_mnt_p}" '{ if ($2 == mntp) print $1 }')
	echo "$_dset"
}

# check if POT_ZFS_ROOT supports given property
# $1 the property to check
_is_zfs_property_supported()
{
	local _properties _ret

	_properties=$(zfs get -o property all "${POT_ZFS_ROOT}")
	_ret=$?
	if [ $_ret -ne 0 ]; then
		_error "Could not determine if $POT_ZFS_ROOT supports encryption"
		return 2 # error
	fi
	if echo "$_properties" | grep -xq "$1" ; then
		return 0 # true
	else
		return 1 # false
	fi
}

# get extra arguments to be added to the zfs receive command.
# Output can be used without addional escaping.
# In case of errors, the returned string will make sure
# the zfs receive command fails in case the called
# didn't check the return value.
#
# Usage:
#     zfs receive $(_get_zfs_receive_extra_args) pool/fs
_get_zfs_receive_extra_args()
{
	local _ret

	_is_zfs_property_supported "encryption"
	_ret=$?
	case $_ret in
		0)	# encryption supported
			echo "-x encryption"
			return 0
			;;
		1)	# encryption not supported
			echo ""
			return 0
			;;
		*)	# communicate error
			echo "-x therewasanerror"
			return 1
			;;
	esac
}

# take a zfs recursive snapshot of a pot
# $1 pot name
_pot_zfs_snap()
{
	local _pname _snaptag _dset
	_pname=$1
	_snaptag="$(date +%s)"
	_debug "Take snapshot of $_pname"
	zfs snapshot -r "${POT_ZFS_ROOT}/jails/${_pname}@${_snaptag}"
}

# recursively remove the oldest snapshot of a pot
# $1 pot name
_remove_oldest_pot_snap()
{
	local _pname _snap _pdset
	_pname=$1
	_pdset="${POT_ZFS_ROOT}/jails/${_pname}"
	_snap="$( _zfs_oldest_snap "$_pdset" )"
	if [ -n "$_snap" ]; then
		zfs destroy -r "$_pdset@${_snap}"
	fi
}

# take a zfs snapshot of all rw dataset found in the fscomp.conf of a pot
# $1 pot name
# DEPRECATED - but still used by create-base
_pot_zfs_snap_full()
{
	local _pname _node _opt _snaptag _dset
	_pname=$1
	_snaptag="$(date +%s)"
	_debug "Take snapshot of the full $_pname"
	while read -r line ; do
		_dset=$( echo "$line" | awk '{print $1}' )
		_opt=$( echo "$line" | awk '{print $3}' )
		if [ "$_opt" = "ro" ]; then
			continue
		fi
		if _is_absolute_path "$_dset" ; then
			_debug "Skip $_dset, it's not a dataset"
		else
			_debug "snapshot of $_dset"
			zfs snapshot "${_dset}@${_snaptag}"
		fi
	done < "${POT_FS_ROOT}/jails/$_pname/conf/fscomp.conf"
}

# recursively remove the oldest snapshot of a pot
# $1 pot name
_remove_oldest_fscomp_snap()
{
	local _fscomp _snap _fdset
	_fscomp=$1
	_fdset="${POT_ZFS_ROOT}/fscomp/${_fscomp}"
	_snap="$( _zfs_oldest_snap "$_fdset" )"
	if [ -n "$_snap" ]; then
		zfs destroy -r "$_fdset@${_snap}"
	fi
}
# take a zfs snapshot of a fscomp
# $1 fscomp name
_fscomp_zfs_snap()
{
	local _fscomp _snaptag _dset
	_fscomp=$1
	_snaptag="$(date +%s)"
	_debug "Take snapshot of $_fscomp"
	zfs snapshot "${POT_ZFS_ROOT}/fscomp/${_fscomp}@${_snaptag}"
}

# get the last available snapshot of a given dataset
# $1 the dataset name
_zfs_last_snap()
{
	local _dset _output
	_dset="$1"
	if [ -z "$_dset" ]; then
		return 1 # false
	fi
	_output="$(zfs list -d 1 -H -t snapshot "$_dset" | sort -r | cut -d'@' -f2 | cut -f1 | head -n1)"
	if [ -z "$_output" ]; then
		return 1 # false
	fi
	echo "${_output}"
	return 0 # true
}

# get the oldest available snapshot of a given dataset
# $1 the dataset name
_zfs_oldest_snap()
{
	local _dset _output
	_dset="$1"
	if [ -z "$_dset" ]; then
		return 1 # false
	fi
	_output="$(zfs list -d 1 -H -t snapshot "$_dset" | sort -r | cut -d'@' -f2 | cut -f1 | tail -n1)"
	if [ -z "$_output" ]; then
		return 1 # false
	fi
	echo "${_output}"
	return 0 # true
}

# get the amount of available snapshots of a given dataset
# $1 the dataset name
_zfs_count_snap()
{
	local _dset _output
	_dset="$1"
	if [ -z "$_dset" ]; then
		return 1 # false
	fi
	_output="$(zfs list -d 1 -H -t snapshot "$_dset" | grep -c . )"
	if [ -z "$_output" ]; then
		 echo 0
	fi
	echo "${_output}"
}

# check if the snapshot of the pot does exist
# $1 pot name
# $2 snapshot name
_is_zfs_pot_snap()
{
	local _pname _snap _dset
	_pname=$1
	_snap=$2
	if zfs list -t snap "${POT_ZFS_ROOT}/jails/${_pname}@${_snap}" 2>/dev/null ; then
		return 0 # true
	else
		return 1 # false
	fi
}

# $1 pot name
# tested (common.sh 7)
_get_usable_hostname() {
	local _pname _hname _phname
	_pname="$1"
	_hname="$(hostname)"
	if [ ${#_pname} -gt "${POT_HOSTNAME_MAX_LENGTH:-64}" ]; then
		echo "$_pname" | awk -v len="${POT_HOSTNAME_MAX_LENGTH:-64}" '{ truncated = substr($1, 1, len); printf("%s", truncated); }'
	else
		_phname="${_pname}.$_hname"
		if [ ${#_phname} -gt "${POT_HOSTNAME_MAX_LENGTH:-64}" ]; then
			echo "$_pname"
		else
			echo "$_phname"
		fi
	fi
}

# $1 bridge name
# $2 var name
_get_bridge_var()
{
	local _Bname _cfile _var _value
	_Bname="$1"
	_cfile="${POT_FS_ROOT}/bridges/$_Bname"
	_var="$2"
	_value="$( grep "^$_var=" "$_cfile" | tr -d ' \t"' | cut -f2 -d'=' )"
	echo "$_value"
}

# $1 pot name
# $2 var name
_get_conf_var()
{
	local _pname _cdir _var _value
	_pname="$1"
	_cdir="${POT_FS_ROOT}/jails/$_pname/conf"
	_var="$2"
	_value="$( grep "^$_var=" "$_cdir/pot.conf" | tr -d ' \t"' | cut -f2 -d'=' )"
	echo "$_value"
}

# $1 pot name
# $2 var name
_get_conf_var_string()
{
	local _pname _cdir _var _value
	_pname="$1"
	_cdir="${POT_FS_ROOT}/jails/$_pname/conf"
	_var="$2"
	_value="$( grep "^$_var=" "$_cdir/pot.conf" | cut -f2 -d'=' )"
	echo "$_value"
}

# $1 pot name
# $2 var name
_get_ip_var()
{
	local _pname _cdir _var _value
	_pname="$1"
	_cdir="${POT_FS_ROOT}/jails/$_pname/conf"
	_value="$( grep "^ip=" "$_cdir/pot.conf" | sed 's/^ip=//' )"
	echo "$_value"
}

_get_pot_export_ports()
{
	local _pname _cdir _var _value
	_pname="$1"
	_cdir="${POT_FS_ROOT}/jails/$_pname/conf"
	_value="$(awk '/pot.export.ports/ { n=split($0,array,"="); if (n==2) { print array[2]; } }' "$_cdir/pot.conf" )"
	echo "$_value"
}

# $1 pot name
_get_pot_base()
{
	_get_conf_var "$1" pot.base
}

# $1 pot name
_get_pot_lvl()
{
	_get_conf_var "$1" pot.level
}

# $1 pot name
_get_pot_type()
{
	local _type
	_type="$( _get_conf_var "$1" pot.type )"
	if [ -z "$_type" ]; then
		_type="multi"
	fi
	echo "$_type"
}

# $1 pot name
_get_pot_network_type()
{
	_get_conf_var "$1" network_type
}

# $1 pot name
_is_ip_inherit()
{
	local _pname _val
	_pname="$1"
	if [ "$(_get_pot_network_type "$_pname" )" = "inherit" ]; then
		return 0 # true
	else
		return 1 # false
	fi
}

# $1 pot name
_is_pot_vnet()
{
	local _pname _val
	_pname="$1"
	_val="$( _get_conf_var "$_pname" vnet )"
	if [ "$_val" = "true" ]; then
		return 0 # true
	else
		return 1 # false
	fi
}

# $1 pot name
_is_pot_prunable()
{
	local _pname
	_pname="$1"
	if [ "$( _get_conf_var "$_pname" "pot.attr.prunable" )" = "YES" ]; then
		return 0 # true
	else
		return 1
	fi
}

# $1 bridge name
# $2 quiet / no _error messages are emitted (sometimes useful)
_is_bridge()
{
	local _bname _bconf
	_bname="$1"
	_bconf="${POT_FS_ROOT}/bridges/$_bname"
	if [ ! -e "$_bconf" ]; then
		_qerror "$2" "bridge $_bname not found"
		return 1 # false
	fi
	return 0 # true
}

# $1 fscomp name
# $2 quiet / no _error messages are emitted (sometimes useful)
# tested
_is_fscomp()
{
	local _fscomp _fdir _fdset
	_fscomp="$1"
	_fdir="${POT_FS_ROOT}/fscomp/$_fscomp"
	_fdset="${POT_ZFS_ROOT}/fscomp/$_fscomp"
	if [ ! -d "$_fdir" ]; then
		_qerror "$2" "fscomp $_fscomp not found"
		return 1
	fi
	if ! _zfs_dataset_valid "$_fdset" ; then
		_qerror "$2" "dataset $_fdset for fscomp $_fscomp not found"
		return 2
	fi
	return 0
}

# $1 base name
# $2 quiet / no _error messages are emitted (sometimes useful)
# tested
_is_base()
{
	local _base _bdir _bdset
	_base="$1"
	_bdir="${POT_FS_ROOT}/bases/$_base"
	_bdset="${POT_ZFS_ROOT}/bases/$_base"
	if [ ! -d "$_bdir" ]; then
		if [ "$2" != "quiet" ]; then
			_error "Base $_base not found"
		fi
		return 1 # false
	fi
	if ! _zfs_dataset_valid "$_bdset" ; then
		if [ "$2" != "quiet" ]; then
			_error "zfs dataset $_bdset not found"
		fi
		return 2 #false
	fi
	return 0 # true
}

# $1 pot name
# $2 quiet / no _error messages are emitted (sometimes useful)
# tested
_is_pot()
{
	local _pname _pdir
	_pname="$1"
	_pdir="${POT_FS_ROOT}/jails/$_pname"
	if [ ! -d "$_pdir" ]; then
		_qerror "$2" "Pot $_pname not found"
		return 1 # false
	fi
	if ! _zfs_dataset_valid "${POT_ZFS_ROOT}/jails/$_pname" ; then
		_qerror "$2" "zfs dataset $_pname not found"
		return 2 # false
	fi

	if [ ! -d "$_pdir/m" ] || [ ! -r "$_pdir/conf/pot.conf" ] ; then
		_qerror "$2" "Some component of the pot $_pname is missing"
		return 3 # false
	fi
	if [ "$( _get_pot_type "$_pname" )" = "multi" ] && [ ! -r "$_pdir/conf/fscomp.conf" ]; then
		_qerror "$2" "Some component of the pot $_pname is missing"
		return 4 # false
	fi
	return 0 # true
}

# $1 pot name
# tested
_is_pot_running()
{
	if [ -z "$1" ]; then
		return 1 ## false
	fi
	jls -j "$1" >/dev/null 2>/dev/null
	return $?
}

# $1 pot name
# tested (common1)
_is_valid_potname()
{
	if echo "$1" | grep -Fq '.' ; then
		return 1 # false
	else
		return 0 # true
	fi
}

# $1 the element to search
# $2.. the list
# tested
_is_in_list()
{
	local _e
	if [ $# -lt 2 ]; then
		return 1 # false
	fi
	_e="$1"
	shift
	# shellcheck disable=SC2068
	for e in $@ ; do
		if [ "$_e" = "$e" ]; then
			return 0 # true
		fi
	done
	return 1 # false
}

# $1 the number to test
# tested ( common8 )
_is_natural_number()
{
	case "$1" in
		''|*[!0-9]*)
			return 1 # false
			;;
		*)
			return 0 # true
			;;
	esac
}

# $1 a string
# tested ( common8 )
_contains_spaces()
{
	echo "$1" | grep -q "[[:space:]]"
}

# $1 mountpoint
# tested
_is_mounted()
{
	local _mnt_p _mounted
	_mnt_p=$1
	if [ -z "$_mnt_p" ]; then
		return 1 # false
	fi
	_mounted=$( mount | grep -F "$_mnt_p" | awk '{print $3}')
	for m in $_mounted ; do
		if [ "$m" = "$_mnt_p" ]; then
			return 0 # true
		fi
	done
	return 1 # false
}

# $1 mountpoint
# tested
_umount()
{
	local _mnt_p
	_mnt_p=$1
	if _is_mounted "$_mnt_p" ; then
		_debug "unmount $_mnt_p"
		umount -f "$_mnt_p"
	else
		_debug "$_mnt_p is already unmounted"
	fi
}

# $1 pot
# $2 cmd
_set_command()
{
	local _pname _cmd _cdir _cmd1 _cmd2
	_pname="$1"
	_cmd="$2"
	_cdir=$POT_FS_ROOT/jails/$_pname/conf
	sed -i '' -e "/^pot.cmd=.*/d" "$_cdir/pot.conf"
	_cmd1="$( echo "$_cmd" | sed 's/^"//' )"
	if [ "$_cmd" = "$_cmd1" ]; then
		echo "pot.cmd=$_cmd" >> "$_cdir"/pot.conf
	else
		_cmd2="$( echo "$_cmd1" | sed 's/"$//' )"
		echo "pot.cmd=$_cmd2" >> "$_cdir/pot.conf"
	fi
}

# tested
_is_rctl_available()
{
	local _racct
	_racct="$(sysctl -qn kern.racct.enable)"
	if [ "$_racct" = "1" ]; then
		return 0 # true
	fi
	return 1 # false
}

_is_vnet_available()
{
	if [ "$(sysctl -n kern.features.vimage 2>/dev/null)" = "1" ]; then
		return 0 # true
	else
		return 1 # false
	fi
}

_is_potnet_available()
{
	if which potnet 2> /dev/null > /dev/null ; then
		return 0 # true
	else
		return 1 # false
	fi
}

# tested (common7)
_get_arch()
{

	echo "$(sysctl -n hw.machine)-$(sysctl -n hw.machine_arch)"
}

# tested (common7)
_get_valid_releases()
{
	local _arch _file_prefix
	_file_prefix="$(_get_arch)"
	if [ -z "$_file_prefix" ]; then
		echo
	fi
	releases="$( find /usr/local/share/freebsd/MANIFESTS -type f -name "${_file_prefix}-*" | sed s%/usr/local/share/freebsd/MANIFESTS/"${_file_prefix}"-%% | sort -V | sed 's/-RELEASE//' | tr '\n' ' ' )"
	echo "$releases"
}

# tested (common7)
_is_valid_release()
{
	local _rel _releases
	if [ -z "$1" ]; then
		return 1 # false
	fi
	_rel="$1"
	_releases="$( _get_valid_releases )"
	# shellcheck disable=SC2086
	if _is_in_list "$_rel" $_releases ; then
		return 0 # true
	else
		return 1 # false
	fi
}

# $1 potname
# it's required to have all the file-system mounted to access /bin/freebsd-version
_get_os_release()
{
	local _pname
	_pname="$1"
	if [ -r "${POT_FS_ROOT}/jails/$_pname/m/bin/freebsd-version" ]; then
		grep ^USERLAND "${POT_FS_ROOT}/jails/$_pname/m/bin/freebsd-version" | cut -f 2 -d"=" | tr -d \"
	else
		_get_conf_var "$_pname" osrelease
	fi
}

# $1 FreeBSD release.
# for instance 12.0 or 13.0-RC1
_get_freebsd_release_name()
{
	if echo "$1" | grep -q "RC" ; then
		echo "$1"
	else
		echo "$1-RELEASE"
	fi
}

_fetch_freebsd()
{
	local _archpath _rel
	_archpath="$(_get_arch)"

	if ! _fetch_freebsd_internal "$1" "$_archpath"; then
		# remove artifact and retry only once
		_rel="$( _get_freebsd_release_name "$1" )"
		rm -f "${POT_CACHE}/${_rel}"_base.txz
		if ! _fetch_freebsd_internal "$1" "$_archpath"; then
			return 1 # false
		fi
		return 0 # true
	fi
	return 0 # true
}

# $1 release, in short format, major.minor or major.minor-RC#
_fetch_freebsd_internal()
{
	local _rel _sha _sha_m _archpath
	_rel="$( _get_freebsd_release_name "$1" )"
	_archpath="$( echo "$2" | sed -e 's:-:/:' )"

	if [ ! -r "${POT_CACHE}/${_rel}"_base.txz ]; then
		fetch -m https://ftp.freebsd.org/pub/FreeBSD/releases/"$_archpath"/"${_rel}"/base.txz -o "${POT_CACHE}/${_rel}"_base.txz
	fi

	if [ ! -r "${POT_CACHE}/${_rel}"_base.txz ]; then
		return 1 # false
	fi
	if [ -r /usr/local/share/freebsd/MANIFESTS/"$2"-"${_rel}" ]; then
		_sha=$( sha256 -q "${POT_CACHE}/${_rel}"_base.txz )
		# shellcheck disable=SC2002
		_sha_m=$( cat /usr/local/share/freebsd/MANIFESTS/"$2"-"${_rel}" | awk '/^base.txz/ { print $2 }' )
		# This version would remove the useless cat, but the testability of this function is compromised
		#_sha_m=$( awk '/^base.txz/ { print $2 }' < /usr/local/share/freebsd/MANIFESTS/"$2"-"${_rel}")
		if [ "$_sha" != "$_sha_m" ]; then
			_error "sha256 doesn't match! Aborting"
			return 1 # false
		fi
	else
		_error "No manifests found - please install the package freebsd-release-manifests"
		return 1 # false
	fi
	return 0 # true
}

# $1 fscomp.conf absolute pathname
_print_pot_fscomp()
{
	local _dset _mnt_p
	while read -r line ; do
		_dset=$( echo "$line" | awk '{print $1}' )
		_mnt_p=$( echo "$line" | awk '{print $2}' )
		printf "\\t\\t%s => %s\\n" "${_mnt_p##"${POT_FS_ROOT}"/jails/}" "${_dset##"${POT_ZFS_ROOT}"/}"
	done < "$1"
}

# $1 pot name
_print_pot_snaps()
{
	if [ -z "$( zfs list -t snapshot -o name -Hr "${POT_ZFS_ROOT}/jails/$1")" ]; then
		printf "\t\tno snapshots\n"
	else
		for _s in $( zfs list -t snapshot -o name -Hr "${POT_ZFS_ROOT}/jails/$1" | tr '\n' ' ' ) ; do
			printf "\\t\\t%s\\n" "$_s"
		done
	fi
}

#1 pot name
_get_pot_snaps()
{
	for _s in $( zfs list -t snapshot -o name -H "${POT_ZFS_ROOT}/jails/$1" | tr '\n' ' ' ) ; do
		echo "${_s##*@}"
	done
}

# $1 mountpoint to create (proper permissions are applied)
_create_pot_mountpoint()
{
	local _mp
	_mp="$1"

	if [ ! -d "$_mp" ]; then
		_debug "Creating mountpoint $_mp"
		mkdir -p "$_mp" || exit 1
	fi
}

# $1 pot name
_is_fscomp_old()
{
	local _pname _fsconf _mnt_p _stripped_mnt_p
	_pname="$1"
	_fsconf="${POT_FS_ROOT}/jails/$_pname/conf/fscomp.conf"
	while read -r line; do
		_mnt_p=$( echo "$line" | awk '{print $2}' )
		_stripped_mnt_p="${_mnt_p##"${POT_FS_ROOT}/jails/$_pname/m"}"
		if [ "$_stripped_mnt_p" != "$_mnt_p" ]; then
			return 0 # true
		fi
	done < "$_fsconf"
	return 1 # false
}

# $1 pot name
_update_fscomp()
{
	local _pname _fsconf _mnt_p _stripped_mnt_p _dset _opt _tmpfile
	_pname="$1"
	if _is_fscomp_old "$_pname" ; then
		_fsconf="${POT_FS_ROOT}/jails/$_pname/conf/fscomp.conf"
		_tmpfile=$(mktemp "${POT_TMP:-/tmp}/fscomp.conf.${_pname}${POT_MKTEMP_SUFFIX}")
		while read -r line; do
			_dset=$( echo "$line" | awk '{print $1}' )
			_mnt_p=$( echo "$line" | awk '{print $2}' )
			_opt=$( echo "$line" | awk '{print $3}' )
			_stripped_mnt_p="${_mnt_p##"${POT_FS_ROOT}/jails/$_pname/m"}"
			if [ -z "$_stripped_mnt_p" ]; then
				_stripped_mnt_p="/"
			fi
			${ECHO} "$_dset $_stripped_mnt_p $_opt" >> "$_tmpfile"
		done < "$_fsconf"
		mv "$_tmpfile" "$_fsconf"
	fi
}

# $1 pot name
_pot_mount()
{
	local _pname _dset _mnt_p _opt _node
	_pname="$1"
	if ! _is_pot "$_pname" ; then
		return 1 # false
	fi
	_update_fscomp "$_pname"
	while read -r line ; do
		if [ -z "$line" ]; then
			_debug "Empty line found. Skipping."
			continue
		fi
		_dset=$( echo "$line" | awk '{print $1}' )
		_mnt_p=$( echo "$line" | awk '{print $2}' )
		_mnt_p="${POT_FS_ROOT}/jails/$_pname/m$_mnt_p"
		_mnt_p="${_mnt_p%/}"
		_opt=$( echo "$line" | awk '{print $3}' )
		if [ "$_opt" = "zfs-remount" ]; then
			# if the mountpoint doesn't exist, zfs will create it
			zfs set mountpoint="$_mnt_p" "$_dset"
			_node=$( _get_zfs_mountpoint "$_dset" )
			if _zfs_exist "$_dset" "$_node" ; then
				# the information are correct - move the mountpoint
				_debug "_pot_mount: the dataset $_dset is mounted at $_node"
			else
				# mountpoint already moved ?
				_error "_pot_mount: Dataset $_dset not mounted at $_mnt_p! Aborting"
				return 1 # false
			fi
		else
			if _is_absolute_path "$_dset" ; then
				if ! mount_nullfs -o "${_opt:-rw}" "$_dset" "$_mnt_p" ; then
					_error "Error mounting $_dset on $_mnt_p"
					return 1 # false
				else
					_debug "mount $_mnt_p"
				fi
			else
				_node=$( _get_zfs_mountpoint "$_dset" )
				if [ ! -d "$_mnt_p" ]; then
					_debug "start: creating the missing mountpoint $_mnt_p"
					if ! mkdir "$_mnt_p" ; then
						_error "Error creating the missing mountpoint $_mnt_p"
						return 1
					fi
				fi
				if ! mount_nullfs -o "${_opt:-rw}" "$_node" "$_mnt_p" ; then
					_error "Error mounting $_node"
					return 1 # false
				else
					_debug "mount $_mnt_p"
				fi
			fi
		fi
	done < "${POT_FS_ROOT}/jails/$_pname/conf/fscomp.conf"
	if [ "$(_get_conf_var "$_pname" pot.attr.no-tmpfs)" = "YES" ]; then
		_debug "Not mounting tmpfs because of no-tmppfs attribure"
	else
		if ! mount -t tmpfs tmpfs "${POT_FS_ROOT}/jails/$_pname/m/tmp" ; then
			_error "Error mounting tmpfs"
			return 1
		else
			_debug "mount ${POT_FS_ROOT}/jails/$_pname/m/tmp"
		fi
	fi
	return 0 # true
}

# $1 pot name
_pot_umount()
{
	local _pname _tmpfile _jdir _node _mnt_p _opt _dset
	_pname="$1"
	if ! _tmpfile=$(mktemp -t "${_pname}.XXXXXX") ; then
		_error "not able to create temporary file - umount failed"
		return 1 # false
	fi
	_jdir="${POT_FS_ROOT}/jails/$_pname"

	if [ "$(_get_conf_var "$_pname" pot.attr.no-tmpfs)" = "YES" ]; then
		_debug "Not umounting tmpfs because of no-tmppfs attribure"
	else
		_umount "$_jdir/m/tmp"
	fi
	if [ "$(_get_conf_var "$_pname" "pot.attr.fdescfs")" = "YES" ]; then
		_umount "$_jdir/m/dev/fs"
	fi
	_umount "$_jdir/m/dev"
	if [ "$(_get_conf_var "$_pname" "pot.attr.procfs")" = "YES" ]; then
		_umount "$_jdir/m/proc"
	fi
	if [ -e "$_jdir/conf/fscomp.conf" ]; then
		_update_fscomp "$_pname"
		tail -r "$_jdir/conf/fscomp.conf" > "$_tmpfile"
		while read -r line ; do
			_dset=$( echo "$line" | awk '{print $1}' )
			_mnt_p=$( echo "$line" | awk '{print $2}' )
			_mnt_p="${POT_FS_ROOT}/jails/$_pname/m$_mnt_p"
			_mnt_p=${_mnt_p%/}
			_opt=$( echo "$line" | awk '{print $3}' )
			if [ "$_opt" = "zfs-remount" ]; then
				_node=${POT_FS_ROOT}/jails/$_pname/$(basename "$_dset")
				zfs set mountpoint="$_node" "$_dset"
				if _zfs_exist "$_dset" "$_node" ; then
					# the information are correct - move the mountpoint
					_debug "stop: the dataset $_dset is mounted at $_node"
				else
					# mountpoint not moved
					_error "Dataset $_dset moved to $_node (Fix it manually)"
				fi
			else
				_umount "$_mnt_p"
			fi
		done < "$_tmpfile"
		rm "$_tmpfile"
	fi
}

_get_pot_list()
{
	# shellcheck disable=SC2011
	ls -d "${POT_FS_ROOT}/jails/"*/ 2>/dev/null | xargs -I {} basename {} | tr '\n' ' '
}

_get_bridge_list()
{
	# shellcheck disable=SC2038
	find "${POT_FS_ROOT}/bridges" -type f 2>/dev/null | xargs -I {} basename {} | tr '\n' ' '
}

pot-cmd()
{
	local _cmd _func
	_cmd=$1
	shift
	if [ ! -r "${_POT_INCLUDE}/${_cmd}.sh" ]; then
		_error "Fatal error! $_cmd implementation not found!"
		${EXIT} 1
	fi

	if [ "$_cmd" != "init" ] && [ "$_cmd" != "de-init" ] && [ "$_cmd" != "version" ] ; then
		if [ ! -d "$POT_FS_ROOT" ]; then
			>&2 _error "$POT_FS_ROOT does not exist, please run 'pot init'"
			${EXIT} 1
		fi
		if [ ! -r "$POT_FS_ROOT" ]; then
			>&2 _error "Current user has no read access to $POT_FS_ROOT"
			${EXIT} 1
		fi
	fi

	# shellcheck disable=SC1090
	. "${_POT_INCLUDE}/${_cmd}.sh"
	_func=pot-${_cmd}
	case "$_cmd" in
		create|import|clone|create-private-bridge|prepare|copy-in)
			if [ "$_POT_RECURSIVE" = "1" ]; then
				logger -p "${POT_LOG_FACILITY}".info -t pot "$_func $*"
				$_func "$@"
			else
				export _POT_RECURSIVE=1
				lockf -k /tmp/pot-lock-file "$_POT_PATHNAME" "$_cmd" "$@"
			fi
			;;
		config|get-attr|get-rss|info|last-run-stats|list|ps|show|top)
			if _is_verbose ; then
				logger -p "${POT_LOG_FACILITY}".debug -t pot "$_func $*"
			fi
			$_func "$@"
			;;
		*)
			logger -p "${POT_LOG_FACILITY}".info -t pot "$_func $*"
			$_func "$@"
			;;
	esac
}


================================================
FILE: share/pot/config.sh
================================================
#!/bin/sh
# shellcheck disable=SC3033,SC3040,SC3043

: "${_config_names:="fs_root zfs_root gateway syslogd pot_prefix fscomp_prefix network_stack"}"

config-help()
{
	cat <<-EOH
	pot config [-hvq] [-g name]
	  -h print this help
	  -v verbose
	  -q quiet
	  -g name : get value of config item "name", one of:
	$(echo "$_config_names" | xargs -n1 echo "     +" | sort)
	EOH
}

# $1 quiet
# $2 name
# $3 value
_config_echo()
{
	if [ "$1" = "quiet" ]; then
		echo "$3"
	else
		echo "$2 = $3"
	fi
}

pot-config()
{
	local _quiet
	_quiet="NO"
	_get=
	OPTIND=1

	while getopts "hvqg:" _o ; do
		case "$_o" in
		h)
			config-help
			${EXIT} 0
			;;
		v)
			_POT_VERBOSITY=$(( _POT_VERBOSITY + 1))
			;;
		q)
			_quiet="quiet"
			;;
		g)
			if _is_in_list "$OPTARG" "$_config_names" ; then
				_get="$OPTARG"
			else
				_qerror $_quiet "$OPTARG is not a valid name"
				[ "quiet" != "$_quiet" ] && config-help
				${EXIT} 1
			fi
			;;
		?)
			config-help
			${EXIT} 1
			;;
		esac
	done

	if [ -z "$_get" ]; then
		_qerror $_quiet "option -g is mandatory"
		[ "quiet" != "$_quiet" ] && config-help
		${EXIT} 1
	fi
	case $_get in
		fs_root)
			_config_echo $_quiet "fs_root" "$POT_FS_ROOT"
			;;
		zfs_root)
			_config_echo $_quiet "zfs_root" "$POT_ZFS_ROOT"
			;;
		gateway)
			_config_echo $_quiet "gateway" "$POT_GATEWAY"
			;;
		syslogd)
			_config_echo $_quiet "syslogd flags" "-b 127.0.0.1 -b $POT_GATEWAY -a $POT_NETWORK"
			;;
		pot_prefix)
			_config_echo $_quiet "pot prefix" "$POT_FS_ROOT/jails"
			;;
		fscomp_prefix)
			_config_echo $_quiet "fscomp prefix" "$POT_FS_ROOT/fscomp"
			;;
		network_stack)
			_config_echo $_quiet "network stack" "$( _get_network_stack )"
			;;
	esac
}


================================================
FILE: share/pot/copy-in.sh
================================================
#!/bin/sh
# shellcheck disable=SC3033,SC3040,SC3043
:

copy-in-help()
{
	cat <<-"EOH"
	pot copy-in [-hv] -p pot -s source -d dest
	  -h print this help
	  -v verbose
	  -F force copy operation for running jails
	     (WARNING: can expose parts of the host file system)
	  -p pot : the working pot
	  -s source : the file or directory to be copied in
	  -d dest : the final location inside the pot
	  -c create any missing path components to `dirname destination`
	     inside the pot
	EOH
}

# $1 source
_source_validation()
{
	local _source
	_source="$1"
	if [ -f "$_source" ] || [ -d "$_source" ]; then
		if [ -r "$_source" ]; then
			return 0 # true
		else
			_error "$_source not readable"
		fi
	else
		_error "$_source not valid"
		return 1 # false
	fi
}

_make_temp_source()
{
	local _proot
	_proot="$1"
	mktemp -d "$_proot/tmp/copy-in${POT_MKTEMP_SUFFIX}"
}

_mount_source_into_potroot()
{
	local _source _mountpoint _source_mnt
	_source="$1"
	_mountpoint="$2"
	if [ -f "$_source" ]; then
		_source_mnt="$( dirname "$_source" )"
	else
		_source_mnt="$_source"
	fi
	if ! mount_nullfs -o ro "$_source_mnt" "$_mountpoint" ; then
		_error "Failed to mount source inside the pot"
		return 1
	fi
}

pot-copy-in()
{
	local _pname _source _destination _to_be_umount _rc _force _proot _cp_opt _create_dirs
	OPTIND=1
	_pname=
	_destination=
	_force=
	_cp_opt="-a"
	_create_dirs=
	while getopts "hvs:d:p:Fc" _o ; do
		case "$_o" in
		h)
			copy-in-help
			return 0
			;;
		F)
			_force="YES"
			;;
		v)
			_POT_VERBOSITY=$(( _POT_VERBOSITY + 1))
			_cp_opt="-va"
			;;
		s)
			_source="$OPTARG"
			;;
		p)
			_pname="$OPTARG"
			;;
		d)
			_destination="$OPTARG"
			;;
		c)
			_create_dirs="YES"
			;;
		*)
			copy-in-help
			return 1
			;;
		esac
	done

	if [ -z "$_pname" ]; then
		_error "A pot name is mandatory"
		copy-in-help
		return 1
	fi
	if [ -z "$_source" ]; then
		_error "A source is mandatory"
		copy-in-help
		return 1
	fi
	if [ -z "$_destination" ]; then
		_error "A destination is mandatory"
		copy-in-help
		return 1
	fi
	if ! _is_absolute_path "$_destination" ; then
		_error "The destination has to be an absolute pathname"
		return 1
	fi

	if ! _is_pot "$_pname" ; then
		_error "pot $_pname is not valid"
		copy-in-help
		return 1
	fi
	if ! _is_uid0 ; then
		return 1
	fi
	if ! _source_validation "$_source" ; then
		copy-in-help
		return 1
	fi
	if _is_pot_running "$_pname" ; then
		if [ "$_force" != "YES" ]; then
			_error "Copying files on a running pot is discouraged, it can partially expose the host file system to the jail"
			_info "Using the -F flag, the operation can be executed anyway, but we disagree"
			return 1
		else
			_debug "Copying files on a running pot allowed, because of the -F flag"
		fi
	else
		_pot_mount "$_pname"
		_to_be_umount=1
	fi
	_proot=${POT_FS_ROOT}/jails/$_pname/m
	if [ "$_create_dirs" = "YES" ]; then
		if _is_pot_running "$_pname" ; then
			if jexec "$_pname" /bin/mkdir -p "$(dirname "$_destination")" ; then
				_debug "Destination path $_destination created in the pot $_pname"
			else
				_error "Destination path $_destination NOT created because of an error"
				if [ "$_to_be_umount" = "1" ]; then
					_pot_umount "$_pname"
				fi
				return 1
			fi
		else
			if jail -c path="$_proot" command=/bin/mkdir -p "$(dirname "$_destination")" ; then
				_debug "Destination path $_destination created in the pot $_pname"
			else
				_error "Destination path $_destination NOT created because of an error"
				if [ "$_to_be_umount" = "1" ]; then
					_pot_umount "$_pname"
				fi
				return 1
			fi
		fi
	fi
	if ! _source_mountpoint="$( _make_temp_source "$_proot" )" ; then
		_error "Failed to build a temporary folder in the pot /tmp"
		if [ "$_to_be_umount" = "1" ]; then
			_pot_umount "$_pname"
		fi
		return 1
	fi
	if ! _mount_source_into_potroot "$_source" "$_source_mountpoint" ; then
		if [ "$_to_be_umount" = "1" ]; then
			_pot_umount "$_pname"
		fi
		return 1
	fi
	if [ -f "$_source" ]; then
		_cp_source="/tmp/$( basename "$_source_mountpoint" )/$( basename "$_source" )"
	else
		_cp_source="/tmp/$( basename "$_source_mountpoint" )"
	fi
	if _is_pot_running "$_pname" ; then
		if jexec "$_pname" /bin/cp "$_cp_opt" "$_cp_source" "$_destination" ; then
			_debug "Source $_source copied in the pot $_pname"
			_rc=0
		else
			_error "Source $_source NOT copied because of an error"
			_rc=1
		fi
	else
		if jail -c path="$_proot" command=/bin/cp "$_cp_opt" "$_cp_source" "$_destination" ; then
			_debug "Source $_source copied in the pot $_pname"
			_rc=0
		else
			_error "Source $_source NOT copied because of an error"
			_rc=1
		fi
	fi

	if ! umount -f "$_source_mountpoint" ; then
		_error "Failed to unmount the source tmp folder from the pot"
		_rc=1
	fi
	if [ "$_to_be_umount" = "1" ]; then
		_pot_umount "$_pname"
	else
		rmdir "$_source_mountpoint"
	fi
	return $_rc
}


================================================
FILE: share/pot/copy-out.sh
================================================
#!/bin/sh
# shellcheck disable=SC3033,SC3040,SC3043
:

copy-out-help()
{
	cat <<-"EOH"
	pot copy-out [-hv] -p pot -s source -d dest
	  -h print this help
	  -v verbose
	  -F force copy operation for running jails
	     (warning: can expose parts of the host file system)
	  -p pot : the working pot
	  -s source : the file or directory inside the pot
	  -d dest : the location (directory) on the host to copy-out to
	EOH
}

# $1 source
_destination_validation()
{
	local _destination
	_destination="$1"
	if [ -r "$_destination" ] && [ -d "$_destination" ] && [ -x "$_destination" ]; then
		return 0 # true
	else
		_error "$_destination not valid"
	fi
}

_make_temp_destination()
{
	local _proot
	_proot="$1"
	mktemp -d "$_proot/tmp/copy-out${POT_MKTEMP_SUFFIX}"
}

_mount_destination_into_potroot()
{
	local _destination _mountpoint
	_destination="$1"
	_mountpoint="$2"
	if ! mount_nullfs "$_destination" "$_mountpoint" ; then
		_error "Failed to mount destination inside the pot"
		return 1
	fi
}

pot-copy-out()
{
	local _pname _source _destination _to_be_umount _rc _force _proot _cp_opt _destination_mountpoint
	OPTIND=1
	_pname=
	_destination=
	_force=
	_cp_opt="-a"
	while getopts "hvs:d:p:F" _o ; do
		case "$_o" in
		h)
			copy-out-help
			return 0
			;;
		F)
			_force="YES"
			;;
		v)
			_POT_VERBOSITY=$(( _POT_VERBOSITY + 1))
			_cp_opt="-va"
			;;
		s)
			_source="$OPTARG"
			;;
		p)
			_pname="$OPTARG"
			;;
		d)
			_destination="$OPTARG"
			;;
		*)
			copy-out-help
			return 1
			;;
		esac
	done

	if [ -z "$_pname" ]; then
		_error "A pot name is mandatory"
		copy-out-help
		return 1
	fi
	if [ -z "$_source" ]; then
		_error "A source is mandatory"
		copy-out-help
		return 1
	fi
	if [ -z "$_destination" ]; then
		_error "A destination is mandatory"
		copy-out-help
		return 1
	fi
	if ! _is_absolute_path "$_source" ; then
		_error "The source has to be an absolute pathname"
		return 1
	fi

	if ! _is_pot "$_pname" ; then
		_error "pot $_pname is not valid"
		copy-out-help
		return 1
	fi
	if ! _is_uid0 ; then
		return 1
	fi
	if ! _destination_validation "$_destination" ; then
		copy-out-help
		return 1
	fi
	if _is_pot_running "$_pname" ; then
		if [ "$_force" != "YES" ]; then
			_error "Copying files from a running pot is discouraged, it can partially expose the host file system to the jail"
			_info "Using the -F flag, the operation can be executed anyway, but we disagree"
			return 1
		else
			_debug "Copying files from a running pot allowed, because of the -F flag"
		fi
	else
		_pot_mount "$_pname"
		_to_be_umount=1
	fi
	_proot=${POT_FS_ROOT}/jails/$_pname/m
	if ! _destination_mountpoint="$( _make_temp_destination "$_proot" )" ; then
		_error "Failed to build a temporary folder in the pot /tmp"
		if [ "$_to_be_umount" = "1" ]; then
			_pot_umount "$_pname"
		fi
		return 1
	fi
	if ! _mount_destination_into_potroot "$_destination" "$_destination_mountpoint" ; then
		if [ "$_to_be_umount" = "1" ]; then
			_pot_umount "$_pname"
		fi
		return 1
	fi
	_cp_destination="/tmp/$( basename "$_destination_mountpoint" )"
	if _is_pot_running "$_pname" ; then
		if jexec "$_pname" /bin/cp "$_cp_opt" "$_source" "$_cp_destination" ; then
			_debug "Source $_source copied from the pot $_pname"
			_rc=0
		else
			_error "Source $_source NOT copied because of an error"
			_rc=1
		fi
	else
		if jail -c path="$_proot" command=/bin/cp "$_cp_opt" "$_source" "$_cp_destination" ; then
			_debug "Source $_source copied from the pot $_pname"
			_rc=0
		else
			_error "Source $_source NOT copied because of an error"
			_rc=1
		fi
	fi

	if ! umount -f "$_destination_mountpoint" ; then
		_error "Failed to unmount the source tmp folder from the pot"
		_rc=1
	fi
	if [ "$_to_be_umount" = "1" ]; then
		_pot_umount "$_pname"
	else
		rmdir "$_destination_mountpoint"
	fi
	return $_rc
}


================================================
FILE: share/pot/create-base.sh
================================================
#!/bin/sh
# shellcheck disable=SC3033,SC3040,SC3043
:

# supported releases are defined in common.sh

create-base-help()
{
	cat <<-EOH
	pot create-base [-h] [-r RELEASE] [-b basename]
	  -h print this help
	  -v verbose
	  -r RELEASE : supported release are: $( _get_valid_releases )
	$( _get_valid_releases | xargs -n1 echo "     +" | sort)
	  -b basename : optional, (default: the release)
	EOH
}

# $1 base name
_cb_zfs()
{
	local _bname _dset _mnt
	_bname=$1
	_dset="${POT_ZFS_ROOT}/bases/${_bname}"
	_mnt="${POT_FS_ROOT}/bases/${_bname}"
	_info "Create the zfs datasets for base release $_dset"
	if ! _zfs_exist "${_dset}" "${_mnt}" ; then
		if ! zfs create "$_dset" ; then
			return 1
		fi
	fi

	if ! _zfs_exist "${_dset}/usr.local" "${_mnt}/usr/local" ; then
		if ! zfs create -o mountpoint="${_mnt}/usr/local" "$_dset/usr.local" ; then
			return 1
		fi
	fi

	if ! _zfs_exist "${_dset}/custom" "${_mnt}/opt/custom" ; then
		if ! zfs create -o mountpoint="${_mnt}/opt/custom" "$_dset/custom" ; then
			return 1
		fi
	fi
	return 0
}

# $1 release
# $2 base name
_cb_tar_dir()
{
	local _rel _bname _mnt
	if echo "$1" | grep -q "RC" ; then
		_rel="$1"
	else
		_rel="$1"-RELEASE
	fi
	_bname=$2
	_mnt="${POT_FS_ROOT}/bases/${_bname}"
	(
		set -e
		cd "$_mnt"
		tar xkf "${POT_CACHE}/${_rel}"_base.txz
		# add release information
		echo "$_rel" > .osrelease
		cp -a root opt/custom/
		cp -a etc opt/custom/
		cp -a var opt/custom/
		mkdir -p opt/custom/usr.local.etc
		mkdir -p opt/custom/usr.home
		# they could be part of flavor
		mkdir -p usr/ports

		# remove duplicated dirs
		chflags -R noschg var/empty
		rm -rf etc/ root/ var/

		# create links
		ln -s opt/custom/etc etc
		ln -s opt/custom/root root
		ln -s opt/custom/var var
		if [ ! -e home ]; then
			ln -s opt/custom/usr.home home
		fi
		cd usr
		ln -s ../opt/custom/usr.home home
		cd local
		ln -s ../../opt/custom/usr.local.etc etc
		mkdir -p var.db.pkg
		cd ../../opt/custom/var/db
		rm -rf pkg
		ln -s ../../../../usr/local/var.db.pkg pkg
	)
}

# $1 base name
_cb_base_pot()
{
	local _bname _pname _tmp
	_bname=$1
	_tmp=$(echo "$_bname" | sed 's/\./_/')
	_pname="base-$_tmp"
	_info "Create the related pot [$_pname]"
	if ! _is_pot "$_pname" quiet ; then
		pot-cmd create -l 0 -b "$_bname" -p "$_pname" -f fbsd-update
	fi
	_debug "Taking a snapshot fo $_pname"
	_pot_zfs_snap_full "$_pname"
}

pot-create-base()
{
	local _rel _bname
	OPTIND=1
	while getopts "hr:b:v" _o ; do
		case "$_o" in
		h)
			create-base-help
			${EXIT} 0
			;;
		v)
			_POT_VERBOSITY=$(( _POT_VERBOSITY + 1))
			;;
		r)
			if ! _is_valid_release "$OPTARG" ; then
				_error "$OPTARG is not a supported release"
				create-base-help
				${EXIT} 1
			fi
			_rel=$OPTARG
			;;
		b)
			if _is_base "$OPTARG" quiet ; then
				_error "$OPTARG is already a base"
				${EXIT} 1
			fi
			_bname="$OPTARG"
			;;
		*)
			create-base-help
			${EXIT} 1
			;;
		esac

	done

	if [ -z "$_rel" ]; then
		_error "option -r is mandatory"
		create-base-help
		${EXIT} 1
	fi
	if [ -z "$_bname" ]; then
		_bname=$_rel
		_info "Automatically use $_rel as base name"
	fi
	if [ "$_bname" != "$_rel" ] && _is_valid_release "$_bname" ; then
		_error "$_bname has the name of another valid release and that's forbidden"
		create-base-help
		${EXIT} 1
	fi
	if _is_base "$_bname" quiet ; then
		_error "$_bname already exist"
		${EXIT} 1
	fi
	if ! _is_uid0 ; then
		${EXIT} 1
	fi
	if ! _is_init ; then
		${EXIT} 1
	fi
	_info "Create a base with release $_rel"
	# fetch binaries
	if ! _fetch_freebsd "${_rel}" ; then
		_error "fetch of ${_rel} RELEASE failed"
		${EXIT} 1
	fi
	# create zfs dataset
	if ! _cb_zfs "${_bname}" ; then
		_error "zfs dataset of ${_bname} failed"
		${EXIT} 1
	fi
	# move binaries to the dataset and create linkx
	_cb_tar_dir "${_rel}" "${_bname}"
	# create jail level 0
	_cb_base_pot "${_bname}"
}


================================================
FILE: share/pot/create-fscomp.sh
================================================
#!/bin/sh
# shellcheck disable=SC3033,SC3040,SC3043
:

create-fscomp-help()
{
	cat <<-"EOH"
	pot create-fscomp [-hv] -f name
	  -h print this help
	  -v verbose
	  -f name : the fs component name (mandatory)
	EOH
}

pot-create-fscomp()
{
	local _dset
	_dset=
	OPTIND=1
	while getopts "hvf:" _o ; do
		case "$_o" in
		h)
			create-fscomp-help
			${EXIT} 0
			;;
		v)
			_POT_VERBOSITY=$(( _POT_VERBOSITY + 1))
			;;
		f)
			_dset="${POT_ZFS_ROOT}/fscomp/$OPTARG"
			;;
		?)
			create-fscomp-help
			${EXIT} 1
		esac
	done

	if [ -z "$_dset" ]; then
		_error "fs component name is missing"
		create-fscomp-help
		${EXIT} 1
	fi
	if ! _is_init ; then
		${EXIT} 1
	fi
	if ! _zfs_dataset_valid "$_dset" ; then
		if ! _is_uid0 ; then
			${EXIT} 1
		fi
		if ! zfs create "$_dset" ; then
			_error "fs component $_dset creation failed"
			${EXIT} 1
		fi
	else
		_info "fs component $_dset already exists"
	fi
	return 0
}


================================================
FILE: share/pot/create-private-bridge.sh
================================================
#!/bin/sh
# shellcheck disable=SC3033,SC3040,SC3043
:

create-private-bridge-help()
{
	cat <<-"EOH"
	pot create-private-bridge [-hv] [-B name] [-S size]
	  -h print this help
	  -v verbose
	  -B bridge name
	  -S bridge size (number of host expected)
	EOH
}

# $1 bridge-name
# $2 bridge-size
create-bridge()
{
	local _bconf _bname _bsize
	_bname=$1
	_bsize=$2
	_bconf="${POT_FS_ROOT}/bridges/$_bname"
	if potnet new-net -s "$_bsize" > "$_bconf" ; then
		echo "name=$_bname" >> "$_bconf"
	else
		rm -f "$_bconf"
		_error "Not able to get a valid network with size $_bsize"
		${EXIT} 1
	fi
}

pot-create-private-bridge()
{
	local _bname _host_amount
	OPTIND=1
	while getopts "hvB:S:" _o ; do
		case "$_o" in
		h)
			create-private-bridge-help
			${EXIT} 0
			;;
		v)
			_POT_VERBOSITY=$(( _POT_VERBOSITY + 1))
			;;
		B)
			_bname="$OPTARG"
			;;
		S)
			_host_amount="$OPTARG"
			;;
		?)
			create-private-bridge-help
			${EXIT} 1
		esac
	done

	if [ -z "$_bname" ]; then
		_error "A bridge name is mandatory (-B option)"
		${EXIT} 1
	fi
	if [ -z "$_host_amount" ]; then
		_error "The amount of host is mandatory (-S option)"
		${EXIT} 1
	fi
	if ! _is_potnet_available ; then
	   _error "potnet is not available! It's needed - cannot proceed"
		${EXIT} 1
	fi
	if _is_bridge "$_bname" quiet ; then
		_error "A bridge with name $_bname already exists"
		${EXIT} 1
	fi
	if ! _is_uid0 ; then
		${EXIT} 1
	fi
	mkdir -p "${POT_FS_ROOT}/bridges"
	create-bridge "$_bname" "$_host_amount"
}


================================================
FILE: share/pot/create.sh
================================================
#!/bin/sh
# shellcheck disable=SC3033,SC3040,SC3043
:

trap _cj_undo_create TERM INT
_set_pipefail

create-help()
{
	cat <<-"EOH"
	pot create [-hv] -p potname [-N network-type] [-i ipaddr]
	           [-l lvl] [-f flavour] [-b base|-P basepot]
	           [-d dns] [-t type]
	  -h print this help
	  -v verbose
	  -k keep the pot, if create fails
	  -p potname : the pot name (mandatory)
	  -l lvl : pot level (only for type multi)
	  -b base : the base pot
	  -P pot : the pot to be used as reference
	  -d dns : pot dns resolver configuration, one of
	           inherit*       - inherit from jailhost (*default)
	           pot            - the pot configured in POT_DNS_NAME
	           custom:<file>  - copy <file> into pot configuration
	           off            - leave resolver config unaltered
	  -f flavour : flavour to be used
	  -t type: pot file system layout, one of
	           single         - the pot is based on a unique ZFS dataset
	           multi*         - the pot is composed by a classical
	                            collection of 3 ZFS datasets (*default)
	  -N network-type: pot network layout, one of
	           inherit*       - inherit the host network stack (*default)
	           alias          - configure a static ip alias directly on
	                            the host's NIC
	           public-bridge  - use the common internal public bridge
	           private-bridge - use an internal private bridge (with -B)
	  -i ipaddr : IP address selection, one of
	           auto*          - automatically assign address (*default),
	                            only works with (public|private)-bridge
	           ipaddr         - mandatory with alias,
	                            also works with (public|private)-bridge
	  -B bridge-name : the name of the private bridge to be used
	  -S network-stack : the network stack (ipv4, ipv6 or dual)
	EOH
}

_cj_undo_create()
{
	_POT_VERBOSITY=0
	if [ -z "$_cleanup_pname" ]; then
		${EXIT} 1
	fi
	pot-cmd stop "$_cleanup_pname"
	if [ "$_cleanup_keep" != "YES" ]; then
		pot-cmd destroy -Fp "$_cleanup_pname" -q
	fi
	unset _cleanup_pname
	unset _cleanup_keep
	${EXIT} 1
}

# $1 pot name
# $2 pot-base name
_c_zfs_single()
{
	local _pname _base _potbase _jdset _snap _dset
	_pname=$1
	_potbase=$2
	_pdset=${POT_ZFS_ROOT}/jails/$_pname
	_pdir=${POT_FS_ROOT}/jails/$_pname
	# Create the main jail zfs dataset
	if ! _zfs_dataset_valid "$_pdset" ; then
		if ! zfs create "$_pdset" ; then
			_error "create: failed to create $_pdset dataset"
			_cj_undo_create
			return 1
		fi
	else
		_info "$_pdset exists already"
	fi

	if [ -z "$_potbase" ]; then
		# create an empty dataset
		if ! zfs create "$_pdset/m" ; then
			_error "create: failed to create $_pdset/m dataset"
			_cj_undo_create
			return 1
		fi
		# create the minimum needed tree
		mkdir -p "$_pdir/m/tmp"
		chmod 1777 "$_pdir/m/tmp"
		mkdir -p "$_pdir/m/dev"
	else
		# send/receive the last snapshot of _potbase
		_dset=${POT_ZFS_ROOT}/jails/$_potbase/m
		_snap=$(_zfs_last_snap "$_dset")
		if [ -n "$_snap" ]; then
			_debug "Clone zfs snapshot $_dset@$_snap"
			# shellcheck disable=SC2046
			if ! zfs send "$_dset@$_snap" | zfs receive \
			    $(_get_zfs_receive_extra_args) "$_pdset/m" ; then
				_error "create: zfs send/receive failed"
				_cj_undo_create
				return 1 # false
			fi
		else
			# TODO - autofix
			_error "no snapshot found for $_dset/m"
			_cj_undo_create
			return 1 # false
		fi
	fi
	return 0
}

# $1 pot name
# $2 level
# $3 base name
# $4 pot-base name
_c_zfs_multi()
{
	local _pname _base _potbase _jdset _snap _dset
	_pname=$1
	_lvl=$2
	_base=$3
	_potbase=$4
	_pdset="${POT_ZFS_ROOT}/jails/$_pname"
	_pdir="${POT_FS_ROOT}/jails/$_pname"

	# Create the main jail zfs dataset
	if ! _zfs_dataset_valid "$_pdset" ; then
		if ! zfs create "$_pdset" ; then
			_error "create: failed to create $_pdset dataset"
			_cj_undo_create
			return 1
		fi
	else
		_info "$_pdset exists already"
	fi
	# Create the root mountpoint
	_create_pot_mountpoint "$_pdir/m"
	# lvl 0 images mount directly usr.local and custom
	if [ "$_lvl" = "0" ]; then
		return 0
	fi
	# usr.local
	if [ "$_lvl" -eq 1 ]; then
		# lvl 1 images send/receive the usr.local dataset
		if ! _zfs_dataset_valid "$_pdset/usr.local" ; then
			if [ -n "$_potbase" ]; then
				_dset=${POT_ZFS_ROOT}/jails/$_potbase
			else
				_dset=${POT_ZFS_ROOT}/bases/$_base
			fi
			_snap=$(_zfs_last_snap "$_dset/usr.local")
			if [ -n "$_snap" ]; then
				_debug "Import zfs snapshot $_dset/usr.local@$_snap"
				# shellcheck disable=SC2046
				if ! zfs send "$_dset/usr.local@$_snap" | zfs receive \
				    $(_get_zfs_receive_extra_args) "$_pdset/usr.local" ; then
					_error "create: zfs send/receive failed"
					_cj_undo_create
					return 1 # false
				fi
				if ! zfs destroy "$_pdset/usr.local@$_snap" ; then
					_info "create: not able to destroy the snapshot of usr.local - please, do it manually"
				fi
			else
				# TODO - autofix
				_error "no snapshot found for $_dset/usr.local"
				return 1 # false
			fi
		else
			_info "$_pdset/usr.local exists already"
		fi
	fi

	# custom dataset
	if ! _zfs_dataset_valid "$_pdset/custom" ; then
		if [ -n "$_potbase" ]; then
			_dset=${POT_ZFS_ROOT}/jails/$_potbase/custom
		else
			_dset=${POT_ZFS_ROOT}/bases/$_base/custom
		fi
		_snap=$(_zfs_last_snap "$_dset")
		if [ -n "$_snap" ]; then
			_debug "Clone zfs snapshot $_dset@$_snap"
			# shellcheck disable=SC2046
			if ! zfs send "$_dset@$_snap" | zfs receive \
			    $(_get_zfs_receive_extra_args) "$_pdset/custom" ; then
				_error "create: zfs send/receive failed"
				_cj_undo_create
				return 1 # false
			fi
			if ! zfs destroy "$_pdset/custom@$_snap" ; then
				_info "create: not able to destroy the snapshot of custom - please, do it manually"
			fi
		else
			# TODO - autofix
			_error "no snapshot found for $_dset"
			return 1 # false
		fi
	else
		_info "$_pdset/custom exists already"
	fi
	return 0 # true
}
# $1 pot name
# $2 type
# $3 level
# $4 base name
# $5 pot-base name
_cj_zfs()
{
	local _pname _base _type _potbase _jdset _snap _dset
	_pname=$1
	_type=$2
	_lvl=$3
	_base=$4
	_potbase=$5
	case "$_type" in
		"multi")
			_c_zfs_multi "$_pname" "$_lvl" "$_base" "$_potbase"
			;;
		"single")
			_c_zfs_single "$_pname" "$_potbase"
			;;
	esac
}

# $1 pot name
# $2 base name
# $3 network type
# $4 ip
# $5 level
# $6 dns
# $7 type
# $8 private bridge (if network type is private_bridge)
# $9 pot-base name
# $10 network-stack
_cj_conf()
{
	local _pname _base _ip _network_type _lvl _jdir _bdir _potbase _dns _type _pblvl _pbpb
	local _jdset _bdset _pbdset _baseos _bridge_name _stack
	_pname=$1
	_base=$2
	_network_type=$3
	_ip=$4
	_lvl=$5
	_dns=$6
	_type=$7
	_bridge_name=$8
	_potbase=$9
	_stack=${10}

	_jdir=${POT_FS_ROOT}/jails/$_pname
	_bdir=${POT_FS_ROOT}/bases/$_base

	_jdset=${POT_ZFS_ROOT}/jails/$_pname
	_bdset=${POT_ZFS_ROOT}/bases/$_base
	if [ -n "$_potbase" ]; then
		_pblvl=$( _get_conf_var "$_potbase" pot.level )
		_pbdset=${POT_ZFS_ROOT}/jails/$_potbase
	else
		_pblvl=
	fi
	if [ ! -d "$_jdir/conf" ]; then
		mkdir -p "$_jdir/conf"
	fi
	(
	if [ "$_type" = "multi" ]; then
		case $_lvl in
		0)
			echo "$_bdset /"
			echo "$_bdset/usr.local /usr/local"
			echo "$_bdset/custom /opt/custom"
			;;
		1)
			echo "$_bdset / ro"
			echo "$_jdset/usr.local /usr/local zfs-remount"
			echo "$_jdset/custom /opt/custom zfs-remount"
			;;
		2)
			echo "$_bdset / ro"
			if [ "$_pblvl" -eq 1 ]; then
				echo "$_pbdset/usr.local /usr/local ro"
			else
				_pbpb="$( _get_conf_var "$_potbase" pot.potbase )"
				echo "${POT_ZFS_ROOT}/jails/$_pbpb/usr.local /usr/local ro"
			fi
			echo "$_jdset/custom /opt/custom zfs-remount"
			;;
		esac
	fi
	) > "$_jdir/conf/fscomp.conf"
	(
		if [ "$_type" = "multi" ]; then
			_baseos="$( cat "$_bdir/.osrelease" )"
		else
			_baseos="${_base}"
		fi
		echo "pot.level=${_lvl}"
		echo "pot.type=${_type}"
		echo "pot.base=${_base}"
		echo "pot.potbase=${_potbase}"
		if [ "$_dns" = "${_dns##custom:}" ]; then
			echo "pot.dns=${_dns}"
		else
			echo "pot.dns=custom"
		fi
		echo "pot.cmd=sh /etc/rc"
		echo "host.hostname=\"$( _get_usable_hostname "${_pname}" )\""
		if echo "$_baseos" | grep -q "RC" ; then
			echo "osrelease=\"${_baseos}\""
		elif echo "$_baseos" | grep -q "RELEASE" ; then
			echo "osrelease=\"${_baseos}\""
		else
			echo "osrelease=\"${_baseos}-RELEASE\""
		fi
		# pot attributes
		echo "pot.attr.no-rc-script=NO"
		echo "pot.attr.persistent=YES"
		echo "pot.attr.start-at-boot=NO"
		echo "pot.attr.prunable=NO"
		echo "pot.attr.no-tmpfs=NO"
		# jail attributes
		for _attr in ${_POT_JAIL_RW_ATTRIBUTES} ; do
			if [ -z "$(_get_conf_var "$_pname" "pot.attr.${_attr}")" ]; then
				# shellcheck disable=SC1083,SC2086
				eval _value=\"\${_POT_DEFAULT_${_attr}_D}\"
				# shellcheck disable=SC2154
				echo "pot.attr.${_attr}=${_value}"
			fi
		done

		echo "pot.stack=$_stack"
		echo "network_type=$_network_type"
		case $_network_type in
		"inherit")
			echo "vnet=false"
			;;
		"alias")
			echo "vnet=false"
			echo "ip=${_ip}"
			;;
		"public-bridge")
			echo "vnet=true"
			echo "ip=${_ip}"
			;;
		"private-bridge")
			echo "vnet=true"
			echo "ip=${_ip}"
			echo "bridge=${_bridge_name}"
			;;
		esac
		if [ "${_dns}" = "pot" ]; then
			echo "pot.depend=${POT_DNS_NAME}"
		fi
	) > "$_jdir/conf/pot.conf"
	if [ "$_dns" != "${_dns##custom:}" ]; then
		cp "${_dns##custom:}" "$_jdir/conf/resolv.conf"
	fi
	if [ "$_lvl" -eq 2 ]; then
		if [ "$_pblvl" -eq 1 ]; then
			# CHANGE the potbase usr.local to be not zfs-remount
			# Add an info here would be nice
			if [ -w "${POT_FS_ROOT}/jails/$_potbase/conf/fscomp.conf" ]; then
				_info "${POT_FS_ROOT}/jails/$_potbase/conf/fscomp.conf fix (${POT_FS_ROOT}/jails/$_potbase/m/usr/local zfs-remount)"
				# shellcheck disable=SC2086
				${SED} -i '' s%${POT_FS_ROOT}/jails/$_potbase/m/usr/local\ zfs-remount%${POT_FS_ROOT}/jails/$_potbase/m/usr/local% ${POT_FS_ROOT}/jails/$_potbase/conf/fscomp.conf
			else
				_info "$_potbase fscomp.conf has not fscomp.conf"
			fi
		fi
	fi
	if [ "$_type" = "multi" ]; then
		_cj_internal_conf "$_pname" "$_type" "$_lvl" "$_ip"
	fi
}

# $1 pot name
# $2 type
# $3 level
# $4 ip
_cj_internal_conf()
{
	local _pname _type _lvl _ip _jdir
	_pname=$1
	_type=$2
	_lvl=$3
	_ip=$4
	_jdir=${POT_FS_ROOT}/jails/$_pname
	if [ "$_type" = "multi" ]; then
		_etcdir="${POT_FS_ROOT}/jails/$_pname/custom/etc"
	else
		_etcdir="${POT_FS_ROOT}/jails/$_pname/m/etc"
	fi

	# disable some cron jobs, not relevant in a jail
	if [ "$_type" = "single" ] || [ "$_lvl" -ne 0 ]; then
		${SED} -i '' 's/^.*save-entropy$/# &/g' "${_etcdir}/crontab"
		${SED} -i '' 's/^.*adjkerntz.*$/# &/g' "${_etcdir}/crontab"
	fi

	# TODO: to be verified
	# add remote syslogd capability, if not inherit
#	if [ -n "$_ip" ]; then
#		# configure syslog in the pot
#		${SED} -i '' 's%^[^#].*/var/log.*$%# &%g' "${_etcdir}/syslog.conf"
#		echo "*.*  @${POT_GATEWAY}:514" > "${_etcdir}/syslog.d/pot.conf"
#		if [ ! -r "${_etcdir}/rc.conf" ]; then
#			touch "${_etcdir}/rc.conf"
#		fi
#		sysrc -f "${_etcdir}/rc.conf" "syslogd_flags=-vv -s -b $_ip" > /dev/null
#		# configure syslogd in the host
#		(
#			echo +"$_ip"
#			echo '*.*		'"/var/log/pot/${_pname}.log"
#		) > /usr/local/etc/syslog.d/"${_pname}".conf
#		touch /var/log/pot/"${_pname}".log
#		(
#			echo "# log rotation for pot ${_pname}"
#			echo "/var/log/pot/${_pname}.log 644 7 * @T00 CX"
#		) > /usr/local/etc/newsyslog.conf.d/"${_pname}".conf
#		service syslogd reload
#	fi
}

# $1 pot name
# $2 freebsd version
_cj_single_install()
{
	local _pname _base _proot _rel
	_pname=$1
	_base=$2
	_proot=${POT_FS_ROOT}/jails/$_pname/m
	_info "Fetching FreeBSD $_base"
	if ! _fetch_freebsd "$_base" ; then
		_error "FreeBSD $_base fetch failed - try to continue"
		_cj_undo_create
		return 1 # false
	fi
	if echo "$_base" | grep -q "RC" ; then
		_rel="$_base"
	else
		_rel="$_base"-RELEASE
	fi
	if [ ! -r "${POT_CACHE}/${_rel}_base.txz" ]; then
		_error "FreeBSD base tarball ${POT_CACHE}/${_rel}_base.txz is missing"
		_cj_undo_create
		return 1 # falase
	fi
	(
	  set -e
	  cd "$_proot"
	  _info "Extract the tarball"
	  tar xkf "${POT_CACHE}/${_rel}_base.txz"
	  if [ ! -d usr/home ]; then
		  mkdir -p usr/home
	  fi
	  if [ ! -e home ]; then
		  ln -s usr/home home
	  fi
	)
	return 0
}

pot-create()
{
	local _pname _ipaddr _lvl _base _flv _potbase _dns _type _new_lvl _network_type _private_bridge _network_stack
	OPTIND=1
	_type="multi"
	_network_type="inherit"
	_pname=
	_base=
	_ipaddr=
	_lvl=1
	_new_lvl=
	_flv=
	_potbase=
	_dns=inherit
	_private_bridge=
	_network_stack="$( _get_network_stack )"
	_cleanup_keep="NO"
	while getopts "hvp:t:N:i:l:b:f:P:d:B:S:k" _o ; do
		case "$_o" in
		h)
			create-help
			${EXIT} 0
			;;
		v)
			_POT_VERBOSITY=$(( _POT_VERBOSITY + 1))
			;;
		k)
			_cleanup_keep="YES"
			;;
		p)
			if ! _is_valid_potname "$OPTARG" ; then
				_error "$OPTARG is not a valid name for a pot ('.' is the only character not allowed)"
				${EXIT} 1
			fi
			_pname="$OPTARG"
			;;
		t)
			if [ "$OPTARG" = "multi" ] || [ "$OPTARG" = "single" ]; then
				_type="$OPTARG"
			else
				_error "Type $OPTARG not supported"
				create-help
				${EXIT} 1
			fi
			;;
		N)
			# shellcheck disable=SC2086
			if ! _is_in_list "$OPTARG" $_POT_NETWORK_TYPES ; then
				_error "Network type $OPTARG not recognized"
				create-help
				${EXIT} 1
			fi
			_network_type="$OPTARG"
			;;
		B)
			_private_bridge="$OPTARG"
			;;
		S)
			if ! _is_in_list "$OPTARG" "ipv4" "ipv6" "dual" ; then
				_error "Network stack $OPTARG not valid"
				create-help
				${EXIT} 1
			fi
			_network_stack="$OPTARG"
			;;
		i)
			if [ -z "$_ipaddr" ]; then
				_ipaddr="$OPTARG"
			else
				_ipaddr="$_ipaddr $OPTARG"
			fi
			;;
		l)
			_lvl="$OPTARG"
			_new_lvl="$OPTARG"
			;;
		b)
			_base=$OPTARG
			;;
		P)
			_potbase=$OPTARG
			;;
		d)
			case $OPTARG in
				"inherit"|"pot"|"off")
					_dns=$OPTARG
					;;
				custom:*)
					if [ -r "${OPTARG##custom:}" ]; then
						_dns=$OPTARG
					else
						_error "The file ${OPTARG##custom:} is not valid or readable"
						${EXIT} 1
					fi
					;;
				*)
					_error "'${OPTARG}' is not a valid dns option"
					create-help
					${EXIT} 1
			esac
			;;
		f)
			if _is_flavour "$OPTARG" ; then
				if [ -z "$_flv" ]; then
					_flv=$OPTARG
				else
					_flv="$_flv $OPTARG"
				fi
			else
				_error "Flavour $OPTARG not found"
				_debug "Looking in the flavour dir ${_POT_FLAVOUR_DIR}"
				${EXIT} 1
			fi
			;;
		*)
			create-help
			${EXIT} 1
			;;
		esac
	done
	# check options consitency
	if [ "$_type" = "single" ]; then
		if [ -n "$_new_lvl" ] && [ "$_new_lvl" != "0" ]; then
			_error "single pot level can only be zero (omit -l option)"
			create-help
			${EXIT} 1
		fi
		_lvl=0
		if [ -n "$_potbase" ]; then
			if ! _is_pot "$_potbase" quiet ; then
				_error "pot $_potbase not found"
				create-help
				${EXIT} 1
			fi
			if [ "$( _get_pot_type "$_potbase" )" != "single" ]; then
				_error "pot $_potbase has the wrong type, it has to be of type single"
				create-help
				${EXIT} 1
			fi
			if [ -z "$_base" ]; then
				_base="$( _get_pot_base "$_potbase" )"
			elif [ "$( _get_pot_base "$_potbase" )" != "$_base" ]; then
				_error "-b $_base and -P $_potbase are not compatible"
				create-help
				${EXIT} 1
			fi
		else
		   	if [ -z "$_base" ]; then
				_error "at least one of -b and -P has to be used"
				create-help
				${EXIT} 1
			fi
			if ! _is_valid_release "$_base" ; then
				_error "$_base is not a valid release"
				create-help
				${EXIT} 1
			fi
		fi
	else
		case $_lvl in
				0)
				if [ -z "$_base" ]; then
					_error "level $_lvl needs option -b"
					create-help
					${EXIT} 1
				fi
				if [ -n "$_potbase" ]; then
					_error "-P option is not allowed with level $_lvl"
					create-help
					${EXIT} 1
				fi
				if ! _is_base "$_base" quiet ; then
					_error "$_base is not a valid base"
					create-help
					${EXIT} 1
				fi
				;;
			1)
				if [ -z "$_base" ] && [ -z "$_potbase" ]; then
					_error "at least one of -b and -P has to be used"
					create-help
					${EXIT} 1
				fi
				if [ -n "$_base" ] && [ -n "$_potbase" ]; then
					if [ "$( _get_pot_base "$_potbase" )" != "$_base" ]; then
						_error "-b $_base and -P $_potbase are not compatible"
						create-help
						${EXIT} 1
					fi
					# TODO: an info or debug message che be showned
				fi
				if [ -n "$_potbase" ]; then
					if ! _is_pot "$_potbase" ; then
						_error "-P $_potbase : is not a pot"
						create-help
						${EXIT} 1
					fi
					if [ "$( _get_conf_var "$_potbase" pot.level )" != "1" ]; then
						_error "-P $_potbase : it has to be of level 1"
						create-help
						${EXIT} 1
					fi
				fi
				if [ -z "$_base" ]; then
					_base=$( _get_pot_base "$_potbase" )
					if [ -z "$_base" ]; then
						_error "-P $_potbase has no base??"
						${EXIT} 1
					fi
					_debug "-P $_potbase induced -b $_base"
				fi
				if ! _is_base "$_base" quiet ; then
					_error "$_base is not a valid base"
					create-help
					${EXIT} 1
				fi
				;;
			2)
				if [ -z "$_potbase" ]; then
					_error "level $_lvl pots need another pot as reference"
					create-help
					${EXIT} 1
				fi
				if [ "$( _get_conf_var "$_potbase" pot.level )" -lt 1 ]; then
					_error "-P $_potbase : it has to be at least of level 1"
					create-help
					${EXIT} 1
				fi
				if ! _is_pot "$_potbase" ; then
					_error "-P $_potbase : is not a pot"
					create-help
					${EXIT} 1
				fi
				if [ -n "$_base" ]; then
					if ! _is_base "$_base" quiet ; then
						_error "$_base is not a valid base"
						create-help
						${EXIT} 1
					fi
					if [ "$( _get_pot_base "$_potbase" )" != "$_base" ]; then
						_error "-b $_base and -P $_potbase are not compatible"
						${EXIT} 1
					fi
				else
					_base=$( _get_pot_base "$_potbase" )
					if [ -z "$_base" ]; then
						_error "-P $_potbase has no base??"
						${EXIT} 1
					fi
					if ! _is_base "$_base" quiet ; then
						_error "$_base (induced by the pot $_potbase) is not a valid base"
						create-help
						${EXIT} 1
					fi
				fi
				;;
			*)
				_error "level $_lvl is not supported"
				${EXIT} 1
				;;
		esac
	fi
	if [ -z "$_pname" ]; then
		_error "pot name is missing"
		create-help
		${EXIT} 1
	fi
	if _is_pot "$_pname" quiet ; then
		_error "pot $_pname already exists"
		${EXIT} 1
	fi
	if ! _is_uid0 ; then
		${EXIT} 1
	fi
	if ! _ipaddr="$( _validate_network_param "$_network_type" "$_ipaddr" "$_private_bridge" "$_network_stack" )" ; then
		echo "$_ipaddr"
		${EXIT} 1
	fi
	if [ "$_dns" = "pot" ]; then
		if ! _is_vnet_available ; then
			_error "This kernel doesn't support VIMAGE! No vnet possible (needed by the dns)"
			${EXIT} 1
		fi
		if ! _is_pot "${POT_DNS_NAME}" quiet ; then
			_error "dns pot not found ($POT_DNS_NAME) - please fix it"
			${EXIT} 1
		fi
	fi
	_info "Creating a new pot"
	_info "pot name     : $_pname"
	_info "type         : $_type"
	_info "base         : $_base"
	_info "pot_base     : $_potbase"
	_info "level        : $_lvl"
	_info "network-type : $_network_type"
	_info "network-stack: $_network_stack"
	_info "ip           : $_ipaddr"
	_info "bridge       : $_private_bridge"
	_info "dns          : $_dns"
	_info "flavours     : $_flv"
	export _cleanup_pname="$_pname" # for the cleanup function
	export _cleanup_keep
	if ! _cj_zfs "$_pname" "$_type" "$_lvl" "$_base" "$_potbase" ; then
		${EXIT} 1
	fi
	if ! _cj_conf "$_pname" "$_base" "$_network_type" "$_ipaddr" "$_lvl" "$_dns" "$_type" "$_private_bridge" "$_potbase" "$_network_stack" ; then
		${EXIT} 1
	fi
	if [ "$_type" = "single" ]; then
		if [ -z "$_potbase" ]; then
			_cj_single_install "$_pname" "$_base"
		fi
		_cj_internal_conf "$_pname" "$_type" "0" "$_ipaddr"
	fi
	if [ -n "$_flv" ]; then
		for _f in $_flv ; do
			if ! _exec_flv "$_pname" "$_f" ; then
				_cj_undo_create
				${EXIT} 1
			fi
		done
	fi
	unset _cleanup_pname
	unset _cleanup_keep
}


================================================
FILE: share/pot/de-init.sh
================================================
#!/bin/sh
# shellcheck disable=SC3033,SC3040,SC3043
:

de-init-help()
{
	cat <<-"EOH"
	pot de-init [-hmvf] [-p pf_file]
	  -f force : stop all running pots
	  -p pf_file : remove anchors to this file (empty to skip),
	               defaults to result of `sysrc -n pf_rules`
	  -h print this help
	  -m minimal modifications (alias for `-p ''`)
	     WARNING: Still destroys POT_ZFS_ROOT
	  -v verbose
	EOH
}

pot-de-init()
{
	local _pots _p _force _zopt _pf_file
	_force=
	_zopt=
	_pf_file="$(sysrc -n pf_rules)"
	OPTIND=1
	while getopts "fhmvp:" _o ; do
		case "$_o" in
		f)
			_force="force"
			;;
		h)
			de-init-help
			${EXIT} 0
			;;
		m)
			_pf_file=""
			;;
		p)
			_pf_file="$OPTARG"
			;;
		v)
			_POT_VERBOSITY=$(( _POT_VERBOSITY + 1))
			_zopt="-v"
			;;
		?)
			de-init-help
			${EXIT} 1
			;;
		esac
	done
	_pots=_get_pot_list
	if ! _is_uid0 ; then
		${EXIT} 1
	fi
	for _p in $_pots ; do
		if _is_pot_running "$_p" ; then
			if [ -n "$_force" ]; then
				_debug "Stop the pot $_p"
				pot-cmd stop "$_p"
			else
				_error "At least on pot is still running. Use -f to force the stop of all pots"
				${EXIT} 1
			fi
		fi
	done
	# Remove pot dataset
	if ! _zfs_dataset_valid "${POT_ZFS_ROOT}" ; then
		_info "no root dataset found ($POT_ZFS_ROOT)"
	else
		_info "Deinstall pot ($POT_ZFS_ROOT)"
		zfs destroy -r $_zopt "${POT_ZFS_ROOT}"
	fi
	echo "zfs datasets have been removed"
	# Remove pf entries if needed
	if [ -n "$_pf_file" ]; then
		sed -i '' '/^nat-anchor pot-nat$/d' "$_pf_file"
		sed -i '' '/^rdr-anchor "pot-rdr\/\*"$/d' "$_pf_file"
		echo "pf configuration file should be clean"
		echo "  - please check $_pf_file and reload it"
	fi
	# Final message
	echo "check your rc.conf for potential leftovers variable like:"
	echo '  syslogd_flags'
	echo '  pot_enable'
	echo "Please, consider to write a feedback email to pizzamig at FreeBSD dot org"
	echo "It gives us the opportunity to learn and improve"
}


================================================
FILE: share/pot/destroy.sh
================================================
#!/bin/sh
# shellcheck disable=SC3033,SC3040,SC3043
:

destroy-help()
{
	cat <<-"EOH"
	pot destroy [-hvFr] [-p potname|-b basename|-f fscomp|-B bridge]
	  -h print this help
	  -v verbose
	  -q quiet
	  -F force the stop and destroy
	  -p potname : the pot name (mandatory)
	  -b basename : the base name (mandatory)
	  -f fscomp : the fscomp name (mandatory)
	  -B bridge-name : the name of the bridge to be deleted (mandatory)
	  -r : destroy recursively all pots based on this base/pot
	EOH
}

# $1 zfs dataset
_zfs_dataset_destroy()
{
	local _dset _zopt
	_dset=$1
	_zopt=
	if _is_verbose ; then
		_zopt="-v"
	fi
	zfs destroy -f -r $_zopt "$_dset"
	return $?
}

# $1 pot name
# $2 force parameter
_pot_zfs_destroy()
{
	local _pname _zopt _jdset _force
	_pname=$1
	_force=$2
	_jdset=${POT_ZFS_ROOT}/jails/$_pname
	if ! _zfs_dataset_valid "$_jdset" ; then
		## if a directory is found, just remove if
		if [ -d "${POT_FS_ROOT}/jails/$_pname" ]; then
			_debug "Dataset of $_pname not found, but removing the directory anyway"
			rm -rf "${POT_FS_ROOT}/jails/$_pname"
			return 0 # true
		fi
		_error "$_pname not found"
		return 1 # false
	fi
	if _is_pot_running "$_pname" ; then
		if [ "$_force" = "YES" ]; then
			pot-cmd stop "$_pname"
		else
			_error "pot $_pname is running"
			${EXIT} 1
		fi
	fi
	if ! _zfs_dataset_destroy "$_jdset" ; then
		_error "zfs failed to destroy the dataset $_jdset"
		return 1 # false
	fi
	if [ -d "${POT_FS_ROOT}/jails/$_pname" ]; then
		_debug "Removing leftover mountpoint ${POT_FS_ROOT}/jails/$_pname"
		rm -rf "${POT_FS_ROOT}/jails/$_pname"
	fi
	rm -f "/usr/local/etc/syslog.d/${_pname}.conf" "/usr/local/etc/newsyslog.conf.d/${_pname}.conf" "/var/log/pot/${_pname}.log"
	return $?
}

# $1 base name
_base_zfs_destroy()
{
	local _bname _bdset
	_bname=$1
	_bdset=${POT_ZFS_ROOT}/bases/$_bname
	if ! _zfs_dataset_destroy "$_bdset" ; then
		_error "zfs failed to destroy the dataset $_bdset"
		return 1 # false
	fi
	if [ -d "${POT_FS_ROOT}/bases/$_bname" ]; then
		_debug "Removing leftover mountpoint ${POT_FS_ROOT}/bases/$_bname"
		rm -rf "${POT_FS_ROOT}/bases/$_bname"
	fi
	return 0
}

# $1 base name
_fscomp_zfs_destroy()
{
	local _fname _fdset
	_fname=$1
	_fdset=${POT_ZFS_ROOT}/fscomp/$_fname
	if ! _zfs_dataset_destroy "$_fdset" ; then
		_error "zfs failed to destroy the dataset $_fdset"
		return 1 # false
	fi
	if [ -d "${POT_FS_ROOT}/fscomp/$_fname" ]; then
		_debug "Removing leftover mountpoint ${POT_FS_ROOT}/fscomp/$_fname"
		rm -rf "${POT_FS_ROOT}/fscomp/$_fname"
	fi
	return 0
}

pot-destroy()
{
	local _pname _bname _fname _force _recursive _pots _depPot _bridge_name
	_pname=
	_bname=
	_fname=
	_bridge_name=
	_force=
	_recursive="NO"
	OPTIND=1
	while getopts "hvrf:p:b:FB:q" _o ; do
		case "$_o" in
		h)
			destroy-help
			${EXIT} 0
			;;
		v)
			_POT_VERBOSITY=$(( _POT_VERBOSITY + 1))
			;;
		q)
			_POT_VERBOSITY=0
			;;
		F)
			_force="YES"
			;;
		r)
			_recursive="YES"
			;;
		p)
			_pname=$OPTARG
			;;
		b)
			_bname=$OPTARG
			;;
		f)
			_fname=$OPTARG
			;;
		B)
			_bridge_name=$OPTARG
			;;
		*)
			destroy-help
			${EXIT} 1
			;;
		esac
	done

	if [ -z "$_pname" ] && [ -z "$_bname" ] && [ -z "$_fname" ] && [ -z "$_bridge_name" ]; then
		_error "-b or -p or -f or -B are missing"
		destroy-help
		${EXIT} 1
	fi
	if [ -n "$_pname" ]; then
		if [ -n "$_bname" ] || [ -n "$_fname" ] || [ -n "$_bridge_name" ] ; then
			_error "-b, -p, -f and -B cannot be used at the same time"
			destroy-help
			${EXIT} 1
		fi
	fi
	if [ -n "$_bname" ]; then
		if [ -n "$_fname" ] || [ -n "$_bridge_name" ] ; then
			_error "-b, -p, -f and -B cannot be used at the same time"
			destroy-help
			${EXIT} 1
		fi
	fi
	if [ -n "$_fname" ] && [ -n "$_bridge_name" ] ; then
		_error "-b, -p, -f and -B cannot be used at the same time"
		destroy-help
		${EXIT} 1
	fi

	if ! _is_uid0 ; then
		${EXIT} 1
	fi
	if [ -n "$_bridge_name" ]; then
		if [ "$_force" = "YES" ] || [ "$_recursive" = "YES" ] ; then
			_info "Destroy bridge will ignore force and recursive"
		fi
		if ! _is_bridge "$_bridge_name" ; then
			_error "$_bridge_name is not a valid bridge name"
			${EXIT} 1
		fi
		_pots=$( _get_pot_list )
		for _p in $_pots ; do
			_bridge="$( _get_conf_var "$_p" bridge)"
			if [ "$_bridge" = "$_bridge_name" ]; then
				_error "the bridge $_bridge_name is still used by the pot $_p - unable to delete"
				${EXIT} 1
			fi
		done
		_info "Destroying bridge $_bridge_name"
		rm -f "${POT_FS_ROOT}/bridges/$_bridge_name"
		${EXIT} $?
	fi

	if [ -n "$_fname" ]; then
		if [ "$_force" = "YES" ] || [ "$_recursive" = "YES" ] ; then
			_info "Destroy fscomps will ignore force and recursive"
		fi
		if ! _zfs_dataset_valid "${POT_ZFS_ROOT}/fscomp/$_fname" ; then
			_error "$_fname is not a fscomp"
			${EXIT} 1
		fi
		_info "Destroying fscomp $_fname"
		_fscomp_zfs_destroy "$_fname"
		${EXIT} $?
	fi

	if [ -n "$_bname" ]; then
		# check the base
		if ! _zfs_dataset_valid "${POT_ZFS_ROOT}/bases/$_bname" ; then
			_error "$_bname is not a base"
			${EXIT} 1
		fi
		if [ "$_recursive" = "YES" ]; then
			for _lvl in 2 1 0 ; do
				_pots=$( _get_pot_list )
				for _p in $_pots ; do
					if [ "$( _get_conf_var "$_p" pot.level )" = "$_lvl" ]; then
						if [ "$( _get_conf_var "$_p" pot.base )" = "$_bname" ]; then
							_info "Destroying recursively pot $_p based on $_bname"
							_pot_zfs_destroy "$_p" $_force
						fi
					fi
				done
			done
		else
			# check if some pot depends on it
			_pots=$( _get_pot_list )
			_pname="base-$(echo "$_bname" | sed 's/\./_/')"
			for _p in $_pots ; do
				if [ "$_p" = "$_pname" ]; then
					continue
				fi
				if [ "$( _get_conf_var "$_p" pot.base )" = "$_bname" ]; then
					_error "base $_bname is used at least by one pot - use option -r to destroy it recursively"
					${EXIT} 1
				fi
			done
			# if present, destroy the lvl 0 pot
			_debug "Destroying lvl 0 pot $_pname"
			_pot_zfs_destroy "$_pname" "$_force"
		fi
		_info "Destroying base $_bname"
		_base_zfs_destroy "$_bname"
		${EXIT} $?
	fi
	if [ -n "$_pname" ]; then
		if ! _is_pot "$_pname" quiet ; then
			if _zfs_dataset_valid "${POT_ZFS_ROOT}/jails/$_pname" && [ "$_force" = "YES" ] ; then
				# we can destroy forcibly
				if ! _pot_zfs_destroy "$_pname" "$_force" ; then
					_error "Failed to destroy pot $_pname"
					${EXIT} 1
				else
					_info "Forcibly destroyed pot $_pname"
					${EXIT} 0
				fi
			else
				_is_pot "$_pname"
				_error "pot $_pname not found or corrupted. Try to use the -F flag"
				${EXIT} 1 # false
			fi
		fi
		if [ "$( _get_conf_var "$_pname" pot.level )" = "0" ]; then
			# if single we can remove a level 0 pot
			if [ "$( _get_conf_var "$_pname" pot.type )" = "single" ]; then
				_pots=$( _get_pot_list )
				for _p in $_pots ; do
					if [ "$( _get_conf_var "$_p" pot.potbase )" = "$_pname" ]; then
						if [ "$_recursive" = "YES" ]; then
							_debug "Destroying recursively pot $_p based on $_pname"
							_pot_zfs_destroy "$_p" $_force
						else
							_error "$_pname is used at least by another pot ($_p) - use option -r to destroy it recursively"
							${EXIT} 1
						fi
					fi
				done
			else
				_error "The pot $_pname has level 0. Please destroy the related base insted"
				${EXIT} 1
			fi
		fi
		if [ "$( _get_conf_var "$_pname" pot.level )" = "1" ]; then
			_pots=$( _get_pot_list )
			for _p in $_pots ; do
				if [ "$( _get_conf_var "$_p" pot.potbase )" = "$_pname" ]; then
					if [ "$_recursive" = "YES" ]; then
						_debug "Destroying recursively pot $_p based on $_pname"
						_pot_zfs_destroy "$_p" $_force
					else
						_error "$_pname is used at least by one level 2 pot - use option -r to destroy it recursively"
						${EXIT} 1
					fi
				fi
			done
		fi
		if [ "$( _get_conf_var "$_pname" pot.level )" = "2" ] && [ "$_recursive" = "YES" ]; then
			_debug "$_pname has level 2. No recursive destroy possible (ignored)"
		fi

		## dependency detection
		_pots=$( _get_pot_list )
		for _p in $_pots ; do
			_depPot="$( _get_conf_var "$_p" pot.depend )"
			if [ -z "$_depPot" ]; then
				continue
			fi
			for _d in $_depPot ; do
				if [ "$_d" = "$_pname" ]; then
					_info "pot $_p is losing his dependency $_pname"
					continue
				fi
			done
		done

		_info "Destroying pot $_pname"
		if ! _pot_zfs_destroy "$_pname" $_force ; then
			_error "$_pname destruction failed"
			${EXIT} 1
		fi
	fi
	if [ -f "${POT_TMP:-/tmp}/pot_status_${_pname}" ]; then
		rm -f "${POT_TMP:-/tmp}/pot_status_${_pname}"
	fi
}


================================================
FILE: share/pot/exec.sh
================================================
#!/bin/sh
# shellcheck disable=SC3033,SC3040,SC3043
:

exec-help()
{
	cat <<-"EOH"
	pot exec [-hvdt] [-e var=value] [-u username] [-U username]
	         -p pot COMMAND
	  -h print this help
	  -v verbose
	  -d detach, run in background
	  -t allocate pty
	  -e var=value : set environment variable, can be repeated
	  -u username: The username from the host environment as whom the
	               command should run
	  -U username: The username from the pot environment as whom the
	               command should run
	  -p pot : the pot image

	  COMMAND is the command to execute and will be executed in the
	  username's $HOME
	EOH
}

# Actually send signal to process inside pot
# $1 pot name
# $2 detach
# $3 env (encoded with save_params)
# $4 alloc_pty
# $5 user_host
# $6 user_pot
# $7-$n command/args
_exec_cmd()
{
	local _pname _detach _env _alloc_pty _user_host _user_pot
	local _cmd

	_pname=$1
	_detach=$2
	_env=$3
	_alloc_pty=$4
	_user_host=$5
	_user_pot=$6
	shift 6  # $@ contains command/args now

	_debug "Exec in $_pname, cmd: $*"

	# assemble command
	_cmd=
	if [ "$_alloc_pty" = "YES" ]; then
		_cmd="$_cmd"$(_save_params "script" "-q" "/dev/null")
	fi
	_cmd="$_cmd"$(_save_params "jexec" "-l")
	if [ -n "$_user_host" ]; then
		_cmd="$_cmd"$(_save_params "-u" "$_user_host")
	elif [ -n "$_user_pot" ]; then
		_cmd="$_cmd"$(_save_params "-U" "$_user_pot")
	fi
	_cmd="$_cmd"$(_save_params "$_pname")
	_cmd="$_cmd$_env"$(_save_params "$@")

	# execute command
	eval "set -- $_cmd"
	if [ "$_detach" = "YES" ]; then
		nohup "$@" >/dev/null 2>&1 &
	else
		"$@"
	fi
	_ret=$?

	return "$_ret"
}

pot-exec()
{
	local _pname _detach _env _alloc_pty _user_host _user_pot _ret
	_pname=
	_detach="NO"
	_env=$(_save_params "env")
	_alloc_pty="NO"
	_user_host=
	_user_pot=
	OPTIND=1
	while getopts "hvdtp:e:u:U:" _o ; do
		case "$_o" in
		h)
			exec-help
			${EXIT} 0
			;;
		v)
			_POT_VERBOSITY=$(( _POT_VERBOSITY + 1))
			;;

		d)
			_detach="YES"
			;;
		t)
			_alloc_pty="YES"
			;;
		e)
			if [ "$OPTARG" = "${OPTARG#*=}" ]; then
				# the argument doesn't have an equal sign
				_error "$OPTARG not in a valid form"
				_error "VARIABLE=value is accetped"
				exec-help
				${EXIT} 1
			fi
			_env="$_env"$(_save_params "$OPTARG")
			;;
		u)
			_user_host="$OPTARG"
			;;
		U)
			_user_pot="$OPTARG"
			;;
		p)
			_pname="$OPTARG"
			;;
		*)
			exec-help
			${EXIT} 1
		esac
	done

	shift $((OPTIND-1))

	if [ "$#" -eq 0 ]; then
		_error "A command is mandatory"
		${EXIT} 1
	fi

	if [ -z "$_pname" ]; then
		_error "A pot name is mandatory"
		exec-help
		${EXIT} 1
	fi

	if [ -n "$_user_host" ] && [ -n "$_user_pot" ]; then
		_error "Parameters -u and -U are mutually exclu
Download .txt
gitextract_cytad9_s/

├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   └── workflows/
│       ├── ci.yml
│       ├── main.yml
│       └── shellcheck.yml
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── Jenkinsfile
├── LICENSE
├── README.md
├── bin/
│   └── pot
├── etc/
│   ├── pot/
│   │   ├── flavours/
│   │   │   ├── dns
│   │   │   ├── dns.sh
│   │   │   ├── fbsd-update.sh
│   │   │   └── slim.sh
│   │   ├── pot.conf.sample
│   │   └── pot.default.conf
│   └── rc.d/
│       ├── pot
│       └── pot_early
├── release.sh
├── share/
│   ├── doc/
│   │   └── pot/
│   │       ├── .gitignore
│   │       ├── Description.md
│   │       ├── Images.md
│   │       ├── Installation.md
│   │       ├── Makefile
│   │       ├── QuickStart.md
│   │       ├── Synopsis.md
│   │       ├── conf.py
│   │       ├── index.md
│   │       └── migration.md
│   ├── pot/
│   │   ├── add-dep.sh
│   │   ├── clone-fscomp.sh
│   │   ├── clone.sh
│   │   ├── common-flv.sh
│   │   ├── common.sh
│   │   ├── config.sh
│   │   ├── copy-in.sh
│   │   ├── copy-out.sh
│   │   ├── create-base.sh
│   │   ├── create-fscomp.sh
│   │   ├── create-private-bridge.sh
│   │   ├── create.sh
│   │   ├── de-init.sh
│   │   ├── destroy.sh
│   │   ├── exec.sh
│   │   ├── export-ports.sh
│   │   ├── export.sh
│   │   ├── get-attribute.sh
│   │   ├── get-rss.sh
│   │   ├── help.sh
│   │   ├── import.sh
│   │   ├── info.sh
│   │   ├── init.sh
│   │   ├── last-run-stats.sh
│   │   ├── list.sh
│   │   ├── mount-in.sh
│   │   ├── mount-out.sh
│   │   ├── network.sh
│   │   ├── prepare.sh
│   │   ├── prune.sh
│   │   ├── ps.sh
│   │   ├── purge-snapshots.sh
│   │   ├── rename.sh
│   │   ├── revert.sh
│   │   ├── set-attribute.sh
│   │   ├── set-cmd.sh
│   │   ├── set-env.sh
│   │   ├── set-hook.sh
│   │   ├── set-hosts.sh
│   │   ├── set-rss.sh
│   │   ├── set-status.sh
│   │   ├── show.sh
│   │   ├── signal.sh
│   │   ├── snapshot.sh
│   │   ├── start.sh
│   │   ├── stop.sh
│   │   ├── term.sh
│   │   ├── top.sh
│   │   ├── update-config.sh
│   │   ├── version.sh
│   │   └── vnet-start.sh
│   └── zsh/
│       └── site-functions/
│           └── _pot
└── tests/
    ├── CI/
    │   ├── resolv.conf-dual
    │   ├── resolv.conf-ipv4
    │   ├── resolv.conf-ipv6
    │   └── run.sh
    ├── add-dep1.sh
    ├── clone-fscomp1.sh
    ├── clone1.sh
    ├── clone2.sh
    ├── common-flv1.sh
    ├── common-stub.sh
    ├── common-zfs1.sh
    ├── common-zfs2.sh
    ├── common1.sh
    ├── common2.sh
    ├── common4.sh
    ├── common5.sh
    ├── common6.sh
    ├── common7.sh
    ├── common8.sh
    ├── conf-stub.sh
    ├── config1.sh
    ├── copy-in1.sh
    ├── copy-out1.sh
    ├── create-base1.sh
    ├── create-fscomp1.sh
    ├── create-private-bridge1.sh
    ├── create1.sh
    ├── create2.sh
    ├── create3.sh
    ├── destroy1.sh
    ├── export-ports1.sh
    ├── export1.sh
    ├── get-rss1.sh
    ├── import1.sh
    ├── info1.sh
    ├── info2.sh
    ├── list1.sh
    ├── list2.sh
    ├── monitor.sh
    ├── mount-in1.sh
    ├── mount-out1.sh
    ├── network1.sh
    ├── pipefail-stub.sh
    ├── ps1.sh
    ├── rename1.sh
    ├── rename2.sh
    ├── rename3.sh
    ├── rename4.sh
    ├── revert1.sh
    ├── set-attr1.sh
    ├── set-attr2.sh
    ├── set-cmd1.sh
    ├── set-env1.sh
    ├── set-env2.sh
    ├── set-hook1.sh
    ├── set-hosts1.sh
    ├── set-rss1.sh
    ├── show1.sh
    ├── shunit/
    │   ├── .gitignore
    │   ├── .travis.yml
    │   ├── CODE_OF_CONDUCT.md
    │   ├── LICENSE
    │   ├── README.md
    │   ├── doc/
    │   │   ├── CHANGES-2.1.md
    │   │   ├── RELEASE_NOTES-2.1.0.txt
    │   │   ├── RELEASE_NOTES-2.1.1.txt
    │   │   ├── RELEASE_NOTES-2.1.2.txt
    │   │   ├── RELEASE_NOTES-2.1.3.txt
    │   │   ├── RELEASE_NOTES-2.1.4.txt
    │   │   ├── RELEASE_NOTES-2.1.5.txt
    │   │   ├── RELEASE_NOTES-2.1.6.txt
    │   │   ├── RELEASE_NOTES-2.1.7.md
    │   │   ├── TODO.txt
    │   │   ├── contributors.md
    │   │   └── design_doc.txt
    │   ├── examples/
    │   │   ├── equality_test.sh
    │   │   ├── lineno_test.sh
    │   │   ├── math.inc
    │   │   ├── math_test.sh
    │   │   ├── mkdir_test.sh
    │   │   ├── mock_file.sh
    │   │   ├── mock_file_test.sh
    │   │   ├── party_test.sh
    │   │   └── suite_test.sh
    │   ├── lib/
    │   │   ├── shflags
    │   │   └── versions
    │   ├── shunit2
    │   ├── shunit2_args_test.sh
    │   ├── shunit2_asserts_test.sh
    │   ├── shunit2_failures_test.sh
    │   ├── shunit2_macros_test.sh
    │   ├── shunit2_misc_test.sh
    │   ├── shunit2_standalone_test.sh
    │   ├── shunit2_test_helpers
    │   └── test_runner
    ├── signal1.sh
    ├── signal2.sh
    ├── snapshot1.sh
    ├── start2.sh
    ├── start3.sh
    ├── start4.sh
    ├── stop1.sh
    ├── term1.sh
    ├── test-suite.sh
    ├── top1.sh
    └── version1.sh
Download .txt
SYMBOL INDEX (1 symbols across 1 files)

FILE: share/doc/pot/conf.py
  function setup (line 170) | def setup(app):
Condensed preview — 189 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,054K chars).
[
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 605,
    "preview": "---\nname: Bug report\nabout: Report a bug you found\ntitle: \"[BUG]\"\nlabels: bug\nassignees: ''\n\n---\n\n**Describe the bug**\nA"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 531,
    "preview": "---\nname: Feature request\nabout: A nice to have\ntitle: ''\nlabels: feature\nassignees: ''\n\n---\n\n**Is your feature request "
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 3236,
    "preview": "name: Run CI\non:\n  workflow_dispatch:\n  push:\n    branches: [ github-ci-action ]\n#  pull_request:\n#    branches: [ maste"
  },
  {
    "path": ".github/workflows/main.yml",
    "chars": 857,
    "preview": "# This is a basic workflow to help you get started with Actions\n\nname: unit-test\n\n# Controls when the action will run. T"
  },
  {
    "path": ".github/workflows/shellcheck.yml",
    "chars": 296,
    "preview": "name: Shellcheck\n\non: [ pull_request ]\n\njobs:\n  shellcheck:\n    name: Shellcheck\n    runs-on: ubuntu-latest\n    steps:\n "
  },
  {
    "path": ".gitignore",
    "chars": 190,
    "preview": "/doc\n*.swp\n*~\n/etc/pot/pot.conf\n/etc/pot/flavours/*\n!/etc/pot/flavours/dns.sh\n!/etc/pot/flavours/dns\n!/etc/pot/flavours/"
  },
  {
    "path": ".travis.yml",
    "chars": 51,
    "preview": "language: bash\nscript: cd tests && ./test-suite.sh\n"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 30317,
    "preview": "# Changelog\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changel"
  },
  {
    "path": "Jenkinsfile",
    "chars": 193,
    "preview": "pipeline {\n\tagent any\n\n\tenvironment {\n\t\tTERM = 'rxvt'\n\t}\n\n\tstages {\n\t\tstage('Test') {\n\t\t\tsteps {\n\t\t\t\tsh 'echo \"Hello Wor"
  },
  {
    "path": "LICENSE",
    "chars": 1516,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2017, Luca Pizzamiglio\nAll rights reserved.\n\nRedistribution and use in source and bi"
  },
  {
    "path": "README.md",
    "chars": 5064,
    "preview": "# THIS REPOSITORY IS NO LONGER ACTIVELY MAINTAINED, IT HAS MOVED TO https://codeberg.org/bsdpot/pot\n# pot\n\n[![build-badg"
  },
  {
    "path": "bin/pot",
    "chars": 6509,
    "preview": "#!/bin/sh\n\n# Copyright (c) 2017, Luca Pizzamiglio <pizzamig@FreeBSD.org>\n# All rights reserved.\n#\n# Redistribution and u"
  },
  {
    "path": "etc/pot/flavours/dns",
    "chars": 38,
    "preview": "set-attribute -A start-at-boot -V YES\n"
  },
  {
    "path": "etc/pot/flavours/dns.sh",
    "chars": 733,
    "preview": "#!/bin/sh\n\npkg install -y dnsmasq\npkg install -y consul\npkg clean -ayq\n\nif [ ! -d /usr/local/etc/consul.d ]; then\n\tmkdir"
  },
  {
    "path": "etc/pot/flavours/fbsd-update.sh",
    "chars": 86,
    "preview": "#!/bin/sh\n\nexport PAGER=/bin/cat\nfreebsd-update --not-running-from-cron fetch install\n"
  },
  {
    "path": "etc/pot/flavours/slim.sh",
    "chars": 917,
    "preview": "#!/bin/sh\n\ndirs=\"/usr/share/bsdconfig /usr/share/doc /usr/share/dtrace /usr/share/examples /usr/share/man /usr/share/ope"
  },
  {
    "path": "etc/pot/pot.conf.sample",
    "chars": 1868,
    "preview": "# pot configuration file\n\n# All datasets related to pot use the some zfs dataset as parent\n# With this variable, you can"
  },
  {
    "path": "etc/pot/pot.default.conf",
    "chars": 2314,
    "preview": "# pot configuration file - default values\n\n# All datasets related to pot use the some zfs dataset as parent\n# With this "
  },
  {
    "path": "etc/rc.d/pot",
    "chars": 2095,
    "preview": "#!/bin/sh\n\n# PROVIDE: pot\n# REQUIRE: NETWORKING LOGIN FILESYSTEM\n# BEFORE: securelevel\n# KEYWORD: shutdown nojail\n\n. /et"
  },
  {
    "path": "etc/rc.d/pot_early",
    "chars": 917,
    "preview": "#!/bin/sh\n\n# PROVIDE: pot_early\n# REQUIRE: NETWORKING syslogd pf\n# BEFORE: ntpdate\n# KEYWORD: shutdown nojail\n\n. /etc/rc"
  },
  {
    "path": "release.sh",
    "chars": 722,
    "preview": "#!/bin/sh\n\nprint_syntax() { echo \"$0\" X.Y.Z ; exit \"${1:-1}\"; }\n\nif [ -z \"$1\" ]; then\n\tprint_syntax\nfi\n\nif [ \"$1\" = \"$(e"
  },
  {
    "path": "share/doc/pot/.gitignore",
    "chars": 7,
    "preview": "_build\n"
  },
  {
    "path": "share/doc/pot/Description.md",
    "chars": 250,
    "preview": "DESCRIPTION\n-----------\nAnother container framework based on jails, to run FreeBSD containers on\nFreeBSD.  Every running"
  },
  {
    "path": "share/doc/pot/Images.md",
    "chars": 6423,
    "preview": "# Using `pot` images\n\nThis guide has the ambitious goal of explain how to create `pot` images, a feature that allows `po"
  },
  {
    "path": "share/doc/pot/Installation.md",
    "chars": 8193,
    "preview": "# `pot` installation guide \n\nThis is a guide to prepare your FreeBSD installation to use the `pot` jail framework.\n\n**NO"
  },
  {
    "path": "share/doc/pot/Makefile",
    "chars": 600,
    "preview": "# Minimal makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS    =\nSPHI"
  },
  {
    "path": "share/doc/pot/QuickStart.md",
    "chars": 16783,
    "preview": "# QuickStart Guide on `pot`\n\nThis is an introduction at the usage of `pot`, a `jail(8)` wrapper based on ZFS and `pf(4)`"
  },
  {
    "path": "share/doc/pot/Synopsis.md",
    "chars": 36,
    "preview": "SYNOPSIS\n--------\n\n`pot` SUBCOMMAND\n"
  },
  {
    "path": "share/doc/pot/conf.py",
    "chars": 5445,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n#\n# pot documentation build configuration file, created by\n# sphinx-quick"
  },
  {
    "path": "share/doc/pot/index.md",
    "chars": 191,
    "preview": "Contents\n--------\n\n* [Synopsis](Synopsis.md)\n* [Description](Description.md)\n* [QuickStart](QuickStart.md)\n* [Installati"
  },
  {
    "path": "share/doc/pot/migration.md",
    "chars": 979,
    "preview": "## Manual migration handbook\n\n### Prerequisite\n\n* testing with single type pot\n* a snapshot is already present\n\n### A xz"
  },
  {
    "path": "share/pot/add-dep.sh",
    "chars": 1436,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\nadd-dep-help()\n{\n\tcat <<-\"EOH\"\n\tpot add-dep [-hv] -p potname -P d"
  },
  {
    "path": "share/pot/clone-fscomp.sh",
    "chars": 1734,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\nclone-fscomp-help()\n{\n\tcat <<-\"EOH\"\n\tpot clone-fscomp [-hv] -f fs"
  },
  {
    "path": "share/pot/clone.sh",
    "chars": 11027,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\ntrap _cj_undo_clone TERM INT\n_set_pipefail\n\nclone-help()\n{\n\tcat <"
  },
  {
    "path": "share/pot/common-flv.sh",
    "chars": 4098,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n\n# $1 flavour name\n_is_flavour()\n{\n\tlocal _flv_name\n\t_flv_name=\"$1\"\n"
  },
  {
    "path": "share/pot/common.sh",
    "chars": 28326,
    "preview": "#!/bin/sh\n# shellcheck disable=SC2034,SC3033,SC3040,SC3043\n\n: \"${EXIT:=exit}\"\n: \"${ECHO:=echo}\"\n: \"${SED:=sed}\"\n\n_POT_RW"
  },
  {
    "path": "share/pot/config.sh",
    "chars": 1688,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n\n: \"${_config_names:=\"fs_root zfs_root gateway syslogd pot_prefix fs"
  },
  {
    "path": "share/pot/copy-in.sh",
    "chars": 4853,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\ncopy-in-help()\n{\n\tcat <<-\"EOH\"\n\tpot copy-in [-hv] -p pot -s sourc"
  },
  {
    "path": "share/pot/copy-out.sh",
    "chars": 3808,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\ncopy-out-help()\n{\n\tcat <<-\"EOH\"\n\tpot copy-out [-hv] -p pot -s sou"
  },
  {
    "path": "share/pot/create-base.sh",
    "chars": 3837,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\n# supported releases are defined in common.sh\n\ncreate-base-help()"
  },
  {
    "path": "share/pot/create-fscomp.sh",
    "chars": 912,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\ncreate-fscomp-help()\n{\n\tcat <<-\"EOH\"\n\tpot create-fscomp [-hv] -f "
  },
  {
    "path": "share/pot/create-private-bridge.sh",
    "chars": 1482,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\ncreate-private-bridge-help()\n{\n\tcat <<-\"EOH\"\n\tpot create-private-"
  },
  {
    "path": "share/pot/create.sh",
    "chars": 19879,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\ntrap _cj_undo_create TERM INT\n_set_pipefail\n\ncreate-help()\n{\n\tcat"
  },
  {
    "path": "share/pot/de-init.sh",
    "chars": 1929,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\nde-init-help()\n{\n\tcat <<-\"EOH\"\n\tpot de-init [-hmvf] [-p pf_file]\n"
  },
  {
    "path": "share/pot/destroy.sh",
    "chars": 8439,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\ndestroy-help()\n{\n\tcat <<-\"EOH\"\n\tpot destroy [-hvFr] [-p potname|-"
  },
  {
    "path": "share/pot/exec.sh",
    "chars": 2932,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\nexec-help()\n{\n\tcat <<-\"EOH\"\n\tpot exec [-hvdt] [-e var=value] [-u "
  },
  {
    "path": "share/pot/export-ports.sh",
    "chars": 2578,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\nexport-ports-help()\n{\n\tcat <<-\"EOH\"\n\tpot export-ports configure e"
  },
  {
    "path": "share/pot/export.sh",
    "chars": 6369,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\n# TODO\n# Add a way to directly upload the compressed file\n# Add a"
  },
  {
    "path": "share/pot/get-attribute.sh",
    "chars": 1602,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\nget-attribute-help()\n{\n\tcat <<-EOH\n\tpot get-attribute [-hvq] -p p"
  },
  {
    "path": "share/pot/get-rss.sh",
    "chars": 2102,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\nget-rss-help()\n{\n\tcat <<-\"EOH\"\n\tpot get-rss [-h] -p pot\n\t  -h pri"
  },
  {
    "path": "share/pot/help.sh",
    "chars": 510,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n\npot-help()\n{\n\tlocal _cmd _func\n\t_cmd=$1\n\tshift\n\tcase \"${_cmd}\" in\n\t"
  },
  {
    "path": "share/pot/import.sh",
    "chars": 6977,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\n# TODO\n# add sha256 check on fetch pot and option to disable it\n#"
  },
  {
    "path": "share/pot/info.sh",
    "chars": 5949,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\ninfo-help()\n{\n\tcat <<-\"EOH\"\n\tpot info [-hvqr] [-p pname|-B bname]"
  },
  {
    "path": "share/pot/init.sh",
    "chars": 4977,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\n# TODO\n# check the return code of all commands\n\ninit-help()\n{\n\tca"
  },
  {
    "path": "share/pot/last-run-stats.sh",
    "chars": 777,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\nlast-run-stats-help()\n{\n\tcat <<-\"EOH\"\n\tpot last-run-stats [-hv] ["
  },
  {
    "path": "share/pot/list.sh",
    "chars": 4527,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\nlist-help()\n{\n\tcat <<-\"EOH\"\n\tpot list [-hpbfFa] [-qv]\n\t  -h print"
  },
  {
    "path": "share/pot/mount-in.sh",
    "chars": 7469,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\nmount-in-help()\n{\n\tcat <<-\"EOH\"\n\tpot mount-in [-hvwr] -p pot -m m"
  },
  {
    "path": "share/pot/mount-out.sh",
    "chars": 3381,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\nmount-out-help()\n{\n\tcat <<-\"EOH\"\n\tpot mount-out [-hvwr] -p pot -m"
  },
  {
    "path": "share/pot/network.sh",
    "chars": 8260,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n\n# tested\n_pot_bridge()\n{\n\t_pot_bridge_ipv4\n}\n\n_pot_bridge_ipv4()\n{\n"
  },
  {
    "path": "share/pot/prepare.sh",
    "chars": 6933,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\nprepare-help()\n{\n\tcat <<-\"EOH\"\n\tpot prepare [-hvS] -p pot -U URL "
  },
  {
    "path": "share/pot/prune.sh",
    "chars": 2095,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n\nprune-help()\n{\n\tcat <<-\"EOH\"\n\tpot prune [-hvq]\n\t  -h print this hel"
  },
  {
    "path": "share/pot/ps.sh",
    "chars": 1004,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n\nps-help()\n{\n\tcat <<-\"EOH\"\n\tpot ps [-hvq]\n\t  -h print this help\n\t  -"
  },
  {
    "path": "share/pot/purge-snapshots.sh",
    "chars": 2385,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\npurge-snapshots-help()\n{\n\tcat <<-\"EOH\"\n\tpot purge-snapshots [-hva"
  },
  {
    "path": "share/pot/rename.sh",
    "chars": 4504,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\nrename-help()\n{\n\tcat <<-\"EOH\"\n\tpot rename [-hv] -p oldname -n new"
  },
  {
    "path": "share/pot/revert.sh",
    "chars": 2366,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\nrevert-help()\n{\n\tcat <<-\"EOH\"\n\tpot revert [-hv] -p potname|-f fsc"
  },
  {
    "path": "share/pot/set-attribute.sh",
    "chars": 4123,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\nset-attribute-help()\n{\n\tcat <<-EOH\n\tpot set-attribute [-hv] -p po"
  },
  {
    "path": "share/pot/set-cmd.sh",
    "chars": 901,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\nset-cmd-help() {\n\tcat <<-\"EOH\"\n\tpot set-cmd [-hv] -p pot -c cmd\n\t"
  },
  {
    "path": "share/pot/set-env.sh",
    "chars": 1835,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\nset-env-help() {\n\tcat <<-\"EOH\"\n\tpot set-env [-hv] -p pot -E env\n\t"
  },
  {
    "path": "share/pot/set-hook.sh",
    "chars": 2084,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\nset-hook-help() {\n\tcat <<-\"EOH\"\n\tpot set-hook [-hv] -p pot [-s ho"
  },
  {
    "path": "share/pot/set-hosts.sh",
    "chars": 2092,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\nset-hosts-help() {\n\tcat <<-\"EOH\"\n\tpot set-hosts [-hv] -p pot -H h"
  },
  {
    "path": "share/pot/set-rss.sh",
    "chars": 2430,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\nset-rss-help()\n{\n\tcat <<-\"EOH\"\n\tpot set-rss [-hv] -p pot [-C cpus"
  },
  {
    "path": "share/pot/set-status.sh",
    "chars": 3410,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\n: \"${_POT_INTERNAL_STATUS:=\"starting doa started stopping stopped"
  },
  {
    "path": "share/pot/show.sh",
    "chars": 4096,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\nshow-help()\n{\n\tcat <<-\"EOH\"\n\tpot show [-hvq] [-a|-r|-p potname]\n\t"
  },
  {
    "path": "share/pot/signal.sh",
    "chars": 4042,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\nsignal-help()\n{\n\tcat <<-\"EOH\"\n\tpot signal [-hvflC] [-s signal] [-"
  },
  {
    "path": "share/pot/snapshot.sh",
    "chars": 2046,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\nsnapshot-help()\n{\n\tcat <<-\"EOH\"\n\tpot snapshot [-hv] -p potname|-f"
  },
  {
    "path": "share/pot/start.sh",
    "chars": 25032,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\nstart-help()\n{\n\tcat <<-\"EOH\"\n\tpot start [-h] -p potname [pname]\n\t"
  },
  {
    "path": "share/pot/stop.sh",
    "chars": 6246,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\nstop-help()\n{\n\tcat <<-\"EOH\"\n\tpot stop [-hv] -p potname | potname\n"
  },
  {
    "path": "share/pot/term.sh",
    "chars": 1436,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\nterm-help()\n{\n\tcat <<-\"EOH\"\n\tpot term [-hvf] -p potname [pname]\n\t"
  },
  {
    "path": "share/pot/top.sh",
    "chars": 677,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\ntop-help()\n{\n\tcat <<-\"EOH\"\n\tpot top [-h] -p pot\n\t  -h print this "
  },
  {
    "path": "share/pot/update-config.sh",
    "chars": 5124,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\nupdate-config-help()\n{\n\tcat <<-\"EOH\"\n\tpot update-config [-h] -p p"
  },
  {
    "path": "share/pot/version.sh",
    "chars": 560,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\nversion-help()\n{\n\tcat <<-\"EOH\"\n\tpot version [-hvq]\n\t  -h print th"
  },
  {
    "path": "share/pot/vnet-start.sh",
    "chars": 5258,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3033,SC3040,SC3043\n:\n\nvnet-start-help()\n{\n\tcat <<-\"EOH\"\n\tpot vnet-start [-hv] [-B bridg"
  },
  {
    "path": "share/zsh/site-functions/_pot",
    "chars": 12799,
    "preview": "#compdef pot\n\n: ${POT_FS_ROOT:=$( pot config -qg fs_root )}\n\n_pot_pots() {\n        _values \"pot pots\" ${${(f)\"$(${servic"
  },
  {
    "path": "tests/CI/resolv.conf-dual",
    "chars": 103,
    "preview": "nameserver 1.1.1.1\nnameserver 1.0.0.1\nnameserver 2606:4700:4700::1111\nnameserver 2606:4700:4700::1001\n\n"
  },
  {
    "path": "tests/CI/resolv.conf-ipv4",
    "chars": 39,
    "preview": "nameserver 1.1.1.1\nnameserver 1.0.0.1\n\n"
  },
  {
    "path": "tests/CI/resolv.conf-ipv6",
    "chars": 65,
    "preview": "nameserver 2606:4700:4700::1111\nnameserver 2606:4700:4700::1001\n\n"
  },
  {
    "path": "tests/CI/run.sh",
    "chars": 11215,
    "preview": "#!/bin/sh\n\nexport POT_PATH=$( realpath $( dirname $(realpath $0))/../..)\nexport PATH=$POT_PATH/bin:$PATH\ntimestamp=\"$(da"
  },
  {
    "path": "tests/add-dep1.sh",
    "chars": 3114,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\n# UUT\n. ../share/pot/add-dep.sh\n\n# common stubs\n. common-stub.sh\n\n# app specific st"
  },
  {
    "path": "tests/clone-fscomp1.sh",
    "chars": 3424,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\n# UUT\n. ../share/pot/clone-fscomp.sh\n\n# common stubs\n. common-stub.sh\n\n_zfs_dataset"
  },
  {
    "path": "tests/clone1.sh",
    "chars": 15408,
    "preview": "#!/bin/sh\n\n# system utilities stubs\npotnet()\n{\n\t# no monitor, potnet is called in a subshell\n\tif [ \"$1\" = \"next\" ]; then"
  },
  {
    "path": "tests/clone2.sh",
    "chars": 10004,
    "preview": "#!/bin/sh\n\n# system utilities stubs\nzfs()\n{\n\t__monitor ZFS \"$@\"\n}\n\nECHO=echo_stub\necho_stub()\n{\n\t__monitor ECHO \"$@\"\n}\n\n"
  },
  {
    "path": "tests/common-flv1.sh",
    "chars": 1288,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n. monitor.sh\n\n# UUT\n. ../share/pot/common-flv.sh\n\n# app specific stubs\n\n_get_flavour"
  },
  {
    "path": "tests/common-stub.sh",
    "chars": 5822,
    "preview": "#!/bin/sh\n. ../share/pot/common.sh\n. ../share/pot/common-flv.sh\n. ../share/pot/network.sh\nEXIT=\"return\"\n\n# common stubs\n"
  },
  {
    "path": "tests/common-zfs1.sh",
    "chars": 1118,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\nzfs()\n{\n\tif [ \"$2\" = \"zfs-dataset\" ]; then\n\t\treturn 0 # true\n\tfi\n\tif [ \"$2\" = \"-H\" "
  },
  {
    "path": "tests/common-zfs2.sh",
    "chars": 733,
    "preview": "#!/bin/sh\n\n. monitor.sh\n# system utilities stubs\n\nzfs()\n{\n\t__monitor ZFS \"$@\"\n}\n\ndate()\n{\n\techo \"123454321\"\n}\n\n# UUT\n. ."
  },
  {
    "path": "tests/common1.sh",
    "chars": 3978,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n. monitor.sh\n\nmount()\n{\n\tcat << EOF--\nzroot on /zroot (zfs, local, noatime, nfsv4acl"
  },
  {
    "path": "tests/common2.sh",
    "chars": 2735,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\nif [ \"$(uname)\" = \"Linux\" ]; then\n\tTEST=/usr/bin/[\nelse\n\tTEST=/bin/[\nfi\n\n[()\n{\n\tif "
  },
  {
    "path": "tests/common4.sh",
    "chars": 817,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\n# UUT\n. ../share/pot/common.sh\n\n# common stubs\n. ./monitor.sh\n\n_qerror()\n{\n\t__monit"
  },
  {
    "path": "tests/common5.sh",
    "chars": 5472,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\nif [ \"$(uname)\" = \"Linux\" ]; then\n\tTEST=/usr/bin/[\nelse\n\tTEST=/bin/[\nfi\n\n[()\n{\n\t#ec"
  },
  {
    "path": "tests/common6.sh",
    "chars": 388,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\n# UUT\n. ../share/pot/common.sh\n\n# common stubs\n. conf-stub.sh\n\ntest_get_conf_vnet_0"
  },
  {
    "path": "tests/common7.sh",
    "chars": 2814,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\nfind()\n{\n\tcat << MANIFEST-EOF\n/usr/local/share/freebsd/MANIFESTS/amd64-amd64-12.0-R"
  },
  {
    "path": "tests/common8.sh",
    "chars": 621,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\n# UUT\n. ../share/pot/common.sh\n\ntest_is_natural_number_001()\n{\n\t_is_natural_number "
  },
  {
    "path": "tests/conf-stub.sh",
    "chars": 2467,
    "preview": "#!/bin/sh\n\nconf_setUp()\n{\n\tPOT_FS_ROOT=/tmp\n\tPOT_ZFS_ROOT=zpot\n\n\t/bin/mkdir -p /tmp/jails/test-pot/conf\n\t{\n\t\techo \"zpot/"
  },
  {
    "path": "tests/config1.sh",
    "chars": 1807,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\n# UUT\n. ../share/pot/config.sh\n\n. ../share/pot/common.sh\n# common stubs\n. common-st"
  },
  {
    "path": "tests/copy-in1.sh",
    "chars": 7943,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\nif [ \"$(uname)\" = \"Linux\" ]; then\n\tTEST=/usr/bin/[\nelse\n\tTEST=/bin/[\nfi\n\n[()\n{\n\tif "
  },
  {
    "path": "tests/copy-out1.sh",
    "chars": 7938,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\nif [ \"$(uname)\" = \"Linux\" ]; then\n\tTEST=/usr/bin/[\nelse\n\tTEST=/bin/[\nfi\n\n[()\n{\n\tif "
  },
  {
    "path": "tests/create-base1.sh",
    "chars": 6151,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\n# UUT\n. ../share/pot/create-base.sh\n\n# common stubs\n. common-stub.sh\n\n_is_init()\n{\n"
  },
  {
    "path": "tests/create-fscomp1.sh",
    "chars": 2332,
    "preview": "#!/bin/sh\n\n# system utilities stubs\nzfs()\n{\n\t__monitor ZFS \"$@\"\n\treturn 0 # true\n}\n\n# UUT\n. ../share/pot/create-fscomp.s"
  },
  {
    "path": "tests/create-private-bridge1.sh",
    "chars": 2542,
    "preview": "#!/bin/sh\n\n# system utilities stubs\nmkdir()\n{\n\t__monitor MKDIR \"$@\"\n}\n\n# UUT\n. ../share/pot/create-private-bridge.sh\n\n# "
  },
  {
    "path": "tests/create1.sh",
    "chars": 42311,
    "preview": "#!/bin/sh\n\n# system utilities stubs\npotnet()\n{\n\t__monitor POTNET \"$@\"\n\tif [ \"$1\" = \"next\" ]; then\n\t\techo \"10.192.123.123"
  },
  {
    "path": "tests/create2.sh",
    "chars": 9858,
    "preview": "#!/bin/sh\n\n# system utilities stubs\nzfs()\n{\n\t__monitor ZFS \"$@\"\n}\n\nECHO=echo_stub\necho_stub()\n{\n\t__monitor ECHO \"$@\"\n}\n\n"
  },
  {
    "path": "tests/create3.sh",
    "chars": 20247,
    "preview": "#!/bin/sh\n\n# system utilities stubs\nmkdir()\n{\n\t__monitor MKDIR \"$@\"\n\t/bin/mkdir \"$@\"\n}\n\nSED=sed_stub\nsed_stub()\n{\n\t__mon"
  },
  {
    "path": "tests/destroy1.sh",
    "chars": 8793,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\nls()\n{\n\tcat << LS_EOL\n/opt/pot/jails/base-11_1/\n/opt/pot/jails/test-pot/\n/opt/pot/j"
  },
  {
    "path": "tests/export-ports1.sh",
    "chars": 6736,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\n# UUT\n. ../share/pot/export-ports.sh\n\n# common stubs\n. common-stub.sh\n\n# app specif"
  },
  {
    "path": "tests/export1.sh",
    "chars": 8990,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\n# UUT\n. ../share/pot/export.sh\n\n. ../share/pot/common.sh\n# common stubs\n. common-st"
  },
  {
    "path": "tests/get-rss1.sh",
    "chars": 1884,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\n# UUT\n. ../share/pot/get-rss.sh\n\n. ../share/pot/common.sh\n# common stubs\n. common-s"
  },
  {
    "path": "tests/import1.sh",
    "chars": 6219,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\n# UUT\n. ../share/pot/import.sh\n\n. ../share/pot/common.sh\n\n# common stubs\n. common-s"
  },
  {
    "path": "tests/info1.sh",
    "chars": 5105,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\n# UUT\n. ../share/pot/info.sh\n\n. ../share/pot/common.sh\n# common stubs\n. common-stub"
  },
  {
    "path": "tests/info2.sh",
    "chars": 3203,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\n# UUT\n. ../share/pot/info.sh\n\n. ../share/pot/common.sh\n# common stubs\n\n_get_pot_net"
  },
  {
    "path": "tests/list1.sh",
    "chars": 7978,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\n# UUT\n. ../share/pot/list.sh\n\n. ../share/pot/common.sh\n# common stubs\n. common-stub"
  },
  {
    "path": "tests/list2.sh",
    "chars": 1146,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\n# UUT\n. ../share/pot/list.sh\n\n. ../share/pot/common.sh\n# common stubs\n. common-stub"
  },
  {
    "path": "tests/monitor.sh",
    "chars": 2060,
    "preview": "#!/bin/sh\n# shellcheck disable=SC3043\n\nif [ -z \"$POT_MONITOR_TMP\" ]; then\n\tif [ \"$(command uname)\" = \"Linux\" ]; then\n\t\tP"
  },
  {
    "path": "tests/mount-in1.sh",
    "chars": 20896,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\nif [ \"$(uname)\" = \"Linux\" ]; then\n\tTEST=/usr/bin/[\nelse\n\tTEST=/bin/[\nfi\n\n[()\n{\n\tif "
  },
  {
    "path": "tests/mount-out1.sh",
    "chars": 3131,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\nif [ \"$(uname)\" = \"Linux\" ]; then\n\tTEST=/usr/bin/[\nelse\n\tTEST=/bin/[\nfi\n\n[()\n{\n\tif "
  },
  {
    "path": "tests/network1.sh",
    "chars": 26635,
    "preview": "#!/bin/sh\n\n# system utilities stubs\nifconfig() {\n\tif [ -z \"$1\" ]; then\n\t\tcat << EOF--\nem0: flags=8843<UP,BROADCAST,RUNNI"
  },
  {
    "path": "tests/pipefail-stub.sh",
    "chars": 361,
    "preview": "#!/bin/sh\n\n_set_pipefail()\n{\n\tlocal _major _version\n\tif [ \"$(uname)\" = \"Linux\" ]; then\n\t\tset -o pipefail\n\t\treturn\n\tfi\n\t_"
  },
  {
    "path": "tests/ps1.sh",
    "chars": 1231,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\n# UUT\n. ../share/pot/ps.sh\n\n. ../share/pot/common.sh\n# common stubs\n. common-stub.s"
  },
  {
    "path": "tests/rename1.sh",
    "chars": 4717,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\n# UUT\n. ../share/pot/rename.sh\n\n# common stubs\n. common-stub.sh\n\n# app specific stu"
  },
  {
    "path": "tests/rename2.sh",
    "chars": 984,
    "preview": "#!/bin/sh\n\n# system utilities stubs\nSED=sed_stub\nsed_stub()\n{\n\tif [ \"$(uname)\" = \"Linux\" ]; then\n\t\tsed -i'' \"$3\" \"$4\" \"$"
  },
  {
    "path": "tests/rename3.sh",
    "chars": 5036,
    "preview": "#!/bin/sh\n\n# system utilities stubs\nzfs()\n{\n\t__monitor ZFS \"$@\"\n}\n\n# UUT\n. ../share/pot/rename.sh\n\n# common stubs\n. comm"
  },
  {
    "path": "tests/rename4.sh",
    "chars": 1207,
    "preview": "#!/bin/sh\n\n# system utilities stubs\nls()\n{\n\tcat << LS_EOL\n/opt/pot/jails/test-pot/\n/opt/pot/jails/test-pot-2/\n/opt/pot/j"
  },
  {
    "path": "tests/revert1.sh",
    "chars": 7736,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\n# UUT\n. ../share/pot/revert.sh\n\n# common stubs\n. common-stub.sh\n\n# app specific stu"
  },
  {
    "path": "tests/set-attr1.sh",
    "chars": 7233,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\n# UUT\n. ../share/pot/set-attribute.sh\n\n# common stubs\n. common-stub.sh\n\n# app speci"
  },
  {
    "path": "tests/set-attr2.sh",
    "chars": 686,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\n# UUT\n. ../share/pot/set-attribute.sh\n\n# common stubs\n. common-stub.sh\n\n# app speci"
  },
  {
    "path": "tests/set-cmd1.sh",
    "chars": 2570,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\n# UUT\n. ../share/pot/set-cmd.sh\n\n# common stubs\n. common-stub.sh\n\n# app specific st"
  },
  {
    "path": "tests/set-env1.sh",
    "chars": 10013,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\n# UUT\n. ../share/pot/set-env.sh\n\n# common stubs\n. common-stub.sh\n\n# app specific st"
  },
  {
    "path": "tests/set-env2.sh",
    "chars": 3476,
    "preview": "#!/bin/sh\n\n# system utilities stubs\nSED=sed_stub\nsed_stub()\n{\n\tif [ \"$(uname)\" = \"Linux\" ]; then\n\t\tsed -i'' \"$3\" \"$4\"\n\te"
  },
  {
    "path": "tests/set-hook1.sh",
    "chars": 6376,
    "preview": "#!/bin/sh\n\n# system utilities stubs\npotnet()\n{\n\tcase \"$4\" in\n\t\t\"10.1.2.3\"|\"10.1.2.4\"|\\\n\t\t\"fe00::2\"|\"::1\")\n\t\treturn 0 # t"
  },
  {
    "path": "tests/set-hosts1.sh",
    "chars": 6958,
    "preview": "#!/bin/sh\n\n# system utilities stubs\npotnet()\n{\n\tcase \"$4\" in\n\t\t\"10.1.2.3\"|\"10.1.2.4\"|\\\n\t\t\"fe00::2\"|\"::1\")\n\t\treturn 0 # t"
  },
  {
    "path": "tests/set-rss1.sh",
    "chars": 4868,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\n# UUT\n. ../share/pot/set-rss.sh\n\n# common stubs\n. common-stub.sh\n\n# app specific st"
  },
  {
    "path": "tests/show1.sh",
    "chars": 4877,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\n# UUT\n. ../share/pot/show.sh\n\n. ../share/pot/common.sh\n# common stubs\n. common-stub"
  },
  {
    "path": "tests/shunit/.gitignore",
    "chars": 49,
    "preview": "# Hidden files generated by macOS.\n.DS_Store\n._*\n"
  },
  {
    "path": "tests/shunit/.travis.yml",
    "chars": 472,
    "preview": "language: bash\n\nenv:\n  - SHUNIT_COLOR='always'\n\nscript:\n  # Execute the unit tests.\n  - ./test_runner\n\nos:\n  - linux\n  -"
  },
  {
    "path": "tests/shunit/CODE_OF_CONDUCT.md",
    "chars": 3220,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
  },
  {
    "path": "tests/shunit/LICENSE",
    "chars": 11357,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "tests/shunit/README.md",
    "chars": 22955,
    "preview": "# shUnit2\n\nshUnit2 is a [xUnit](http://en.wikipedia.org/wiki/XUnit) unit test framework for\nBourne based shell scripts, "
  },
  {
    "path": "tests/shunit/doc/CHANGES-2.1.md",
    "chars": 8558,
    "preview": "# shUnit2 2.1.x Changes\n\n## Changes with 2.1.8\n\n### New\n\nIssue #29. Add support for user defined prefix for test names. "
  },
  {
    "path": "tests/shunit/doc/RELEASE_NOTES-2.1.0.txt",
    "chars": 2247,
    "preview": "Release Notes for shUnit2 2.1.0\n===============================\n\nThis release was branched from shUnit2 2.0.1. It mostly"
  },
  {
    "path": "tests/shunit/doc/RELEASE_NOTES-2.1.1.txt",
    "chars": 1634,
    "preview": "Release Notes for shUnit2 2.1.1\n===============================\n\nThis is mainly a bug fix release, but it also incorpora"
  },
  {
    "path": "tests/shunit/doc/RELEASE_NOTES-2.1.2.txt",
    "chars": 1696,
    "preview": "Release Notes for shUnit2 2.1.2\n===============================\n\nThis release adds initial support for the zsh shell. Du"
  },
  {
    "path": "tests/shunit/doc/RELEASE_NOTES-2.1.3.txt",
    "chars": 1570,
    "preview": "Release Notes for shUnit2 2.1.3\n===============================\n\nThis release is minor feature release. It improves supp"
  },
  {
    "path": "tests/shunit/doc/RELEASE_NOTES-2.1.4.txt",
    "chars": 1716,
    "preview": "Release Notes for shUnit2 2.1.4\n===============================\n\nThis release contains lots of bug fixes and changes. Mo"
  },
  {
    "path": "tests/shunit/doc/RELEASE_NOTES-2.1.5.txt",
    "chars": 2862,
    "preview": "Release Notes for shUnit2 2.1.5\n===============================\n\nThis release contains several bug fixes and changes. Ad"
  },
  {
    "path": "tests/shunit/doc/RELEASE_NOTES-2.1.6.txt",
    "chars": 2392,
    "preview": "Release Notes for shUnit2 2.1.6\n===============================\n\nThis release contains bug fixes and changes. It is also"
  },
  {
    "path": "tests/shunit/doc/RELEASE_NOTES-2.1.7.md",
    "chars": 1579,
    "preview": "# shUnit2 2.1.7 Release Notes\n\nhttps://github.com/kward/shunit2\n\nThis release contains bug fixes and enhancements. It is"
  },
  {
    "path": "tests/shunit/doc/TODO.txt",
    "chars": 486,
    "preview": "Make it possible to execute a single test by passing the name of the test on\nthe command line\n\nAdd support for '--random"
  },
  {
    "path": "tests/shunit/doc/contributors.md",
    "chars": 447,
    "preview": "The original author of shunit2 is Kate Ward. The following people have\ncontributed in some way or another to shunit2.\n\n-"
  },
  {
    "path": "tests/shunit/doc/design_doc.txt",
    "chars": 1454,
    "preview": "Design Doc for shUnit\n\nshUnit is based upon JUnit. The initial ideas for the script came from the book\n\"Pragmatic Unit T"
  },
  {
    "path": "tests/shunit/examples/equality_test.sh",
    "chars": 122,
    "preview": "#! /bin/sh\n# file: examples/equality_test.sh\n\ntestEquality() {\n  assertEquals 1 1\n}\n\n# Load and run shUnit2.\n. ../shunit"
  },
  {
    "path": "tests/shunit/examples/lineno_test.sh",
    "chars": 402,
    "preview": "#! /bin/sh\n# file: examples/lineno_test.sh\n\ntestLineNo() {\n  # This assert will have line numbers included (e.g. \"ASSERT"
  },
  {
    "path": "tests/shunit/examples/math.inc",
    "chars": 149,
    "preview": "# available as examples/math.inc\n\nadd_generic()\n{\n  num_a=$1\n  num_b=$2\n\n  expr $1 + $2\n}\n\nadd_bash()\n{\n  num_a=$1\n  num"
  },
  {
    "path": "tests/shunit/examples/math_test.sh",
    "chars": 454,
    "preview": "#! /bin/sh\n# file: examples/math_test.sh\n\ntestAdding() {\n  result=`add_generic 1 2`\n  assertEquals \\\n      \"the result o"
  },
  {
    "path": "tests/shunit/examples/mkdir_test.sh",
    "chars": 2264,
    "preview": "#!/bin/sh\n# vim:et:ft=sh:sts=2:sw=2\n#\n# Copyright 2008-2019 Kate Ward. All Rights Reserved.\n# Released under the Apache "
  },
  {
    "path": "tests/shunit/examples/mock_file.sh",
    "chars": 2498,
    "preview": "#!/bin/sh\n#\n# shUnit2 example for mocking files.\n#\n# This example demonstrates two different mechanisms for mocking file"
  },
  {
    "path": "tests/shunit/examples/mock_file_test.sh",
    "chars": 897,
    "preview": "#!/bin/sh\n#\n# shUnit2 example for mocking files.\n\nMOCK_PASSWD=''  # This will be overridden in oneTimeSetUp().\n\ntest_roo"
  },
  {
    "path": "tests/shunit/examples/party_test.sh",
    "chars": 472,
    "preview": "#! /bin/sh\n# file: examples/party_test.sh\n#\n# This test is mostly for fun. Technically, it is a bad example of a unit te"
  },
  {
    "path": "tests/shunit/examples/suite_test.sh",
    "chars": 986,
    "preview": "#!/bin/sh\n# file: examples/suite_test.sh\n#\n# This test demonstrates the use of suites. Note: the suite functionality is\n"
  },
  {
    "path": "tests/shunit/lib/shflags",
    "chars": 38349,
    "preview": "# vim:et:ft=sh:sts=2:sw=2\n#\n# Copyright 2008-2017 Kate Ward. All Rights Reserved.\n# Released under the Apache License 2."
  },
  {
    "path": "tests/shunit/lib/versions",
    "chars": 8025,
    "preview": "#! /bin/sh\n# vim:et:ft=sh:sts=2:sw=2\n#\n# Versions determines the versions of all installed shells.\n#\n# Copyright 2008-20"
  },
  {
    "path": "tests/shunit/shunit2",
    "chars": 39509,
    "preview": "#! /bin/sh\n# vim:et:ft=sh:sts=2:sw=2\n#\n# Copyright 2008-2019 Kate Ward. All Rights Reserved.\n# Released under the Apache"
  },
  {
    "path": "tests/shunit/shunit2_args_test.sh",
    "chars": 2055,
    "preview": "#!/bin/sh\n#\n# shunit2 unit test for running subset(s) of tests based upon command line arguments.\n# Also shows how non-d"
  },
  {
    "path": "tests/shunit/shunit2_asserts_test.sh",
    "chars": 9280,
    "preview": "#! /bin/sh\n# vim:et:ft=sh:sts=2:sw=2\n#\n# shunit2 unit test for assert functions.\n#\n# Copyright 2008-2017 Kate Ward. All "
  },
  {
    "path": "tests/shunit/shunit2_failures_test.sh",
    "chars": 2734,
    "preview": "#! /bin/sh\n# vim:et:ft=sh:sts=2:sw=2\n#\n# Copyright 2008-2019 Kate Ward. All Rights Reserved.\n# Released under the Apache"
  },
  {
    "path": "tests/shunit/shunit2_macros_test.sh",
    "chars": 8111,
    "preview": "#! /bin/sh\n# vim:et:ft=sh:sts=2:sw=2\n#\n# shunit2 unit test for macros.\n#\n# Copyright 2008-2017 Kate Ward. All Rights Res"
  },
  {
    "path": "tests/shunit/shunit2_misc_test.sh",
    "chars": 9395,
    "preview": "#! /bin/sh\n# vim:et:ft=sh:sts=2:sw=2\n#\n# shUnit2 unit tests of miscellaneous things\n#\n# Copyright 2008-2018 Kate Ward. A"
  },
  {
    "path": "tests/shunit/shunit2_standalone_test.sh",
    "chars": 985,
    "preview": "#! /bin/sh\n# vim:et:ft=sh:sts=2:sw=2\n#\n# shUnit2 unit test for standalone operation.\n#\n# Copyright 2010-2017 Kate Ward. "
  },
  {
    "path": "tests/shunit/shunit2_test_helpers",
    "chars": 6806,
    "preview": "# vim:et:ft=sh:sts=2:sw=2\n#\n# shUnit2 unit test common functions\n#\n# Copyright 2008 Kate Ward. All Rights Reserved.\n# Re"
  },
  {
    "path": "tests/shunit/test_runner",
    "chars": 4649,
    "preview": "#! /bin/sh\n# vim:et:ft=sh:sts=2:sw=2\n#\n# Unit test suite runner.\n#\n# Copyright 2008-2018 Kate Ward. All Rights Reserved."
  },
  {
    "path": "tests/signal1.sh",
    "chars": 5261,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\n# UUT\n. ../share/pot/signal.sh\n\n. ../share/pot/common.sh\n# common stubs\n. common-st"
  },
  {
    "path": "tests/signal2.sh",
    "chars": 2711,
    "preview": "#!/bin/sh\n\n# system utilities stubs\npkill()\n{\n\t__monitor PKILL \"$@\"\n}\n\npgrep()\n{\n\t__monitor PGREP \"$@\"\n}\n\nmktemp()\n{\n\t__"
  },
  {
    "path": "tests/snapshot1.sh",
    "chars": 12994,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\n# UUT\n. ../share/pot/snapshot.sh\n\n# common stubs\n. common-stub.sh\n\n# app specific s"
  },
  {
    "path": "tests/start2.sh",
    "chars": 2471,
    "preview": "#!/bin/sh\n\n: ${MKTEMP_FILE:=/tmp/pot_pfrules_test}\n# system utilities stubs\npfctl()\n{\n\t__monitor PFCTL \"$@\"\n}\n\nmktemp()\n"
  },
  {
    "path": "tests/start3.sh",
    "chars": 5794,
    "preview": "#!/bin/sh\n\n# system utilities stubs\npfctl()\n{\n\t__monitor PFCTL \"$@\"\n}\n\nSED=sed_stub\nsed_stub()\n{\n\tif [ \"$(uname)\" = \"Lin"
  },
  {
    "path": "tests/start4.sh",
    "chars": 5713,
    "preview": "#!/bin/sh\n\n# system utilities stubs\npotnet()\n{\n\tif [ -z \"$3\" ]; then\n\t\t# public bridge\n\t\techo \"10.1.2.3 test-pot-2\"\n\t\tec"
  },
  {
    "path": "tests/stop1.sh",
    "chars": 2788,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\nlockf()\n{\n\treturn 0\n}\n# UUT\n. ../share/pot/stop.sh\n\n. ../share/pot/common.sh\n# comm"
  },
  {
    "path": "tests/term1.sh",
    "chars": 2757,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\n# UUT\n. ../share/pot/term.sh\n\n. ../share/pot/common.sh\n# common stubs\n. common-stub"
  },
  {
    "path": "tests/test-suite.sh",
    "chars": 846,
    "preview": "#!/bin/sh\n\nDOIT=\nSHL=\"sh\"\nif [ \"$(uname)\" = \"Linux\" ]; then\n\t# using bash explicitely because of travis unkowns about /b"
  },
  {
    "path": "tests/top1.sh",
    "chars": 1994,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\ntop()\n{\n\t__monitor TOP \"$@\"\n}\n\n# UUT\n. ../share/pot/top.sh\n\n. ../share/pot/common.s"
  },
  {
    "path": "tests/version1.sh",
    "chars": 968,
    "preview": "#!/bin/sh\n\n# system utilities stubs\n\n# UUT\n. ../share/pot/version.sh\n\n. ../share/pot/common.sh\n# common stubs\n. common-s"
  }
]

About this extraction

This page contains the full source code of the pizzamig/pot GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 189 files (922.2 KB), approximately 311.3k tokens, and a symbol index with 1 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!