aboutsummaryrefslogtreecommitdiff
path: root/contrib/cpt-chroot
blob: a2ea1515980c135b1145cb520addc0b7c387778b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#!/bin/sh -e
# Enter a chroot
# shellcheck disable=2004

## SYNOPSIS:
## .Nm cpt-chroot
## .Op Fl m
## .Op Ar dir

## DESCRIPTION:
## .Nm
## is a wrapper script to chroot inside other root filesystems. It automatically
## mounts important filesystems in to the chroot directory, and unmounts them
## when the user exits the chroot and cleans up leftover host files. If the flag
## .Fl m
## is given,
## .Nm
## does not try to mount or unmount any filesystems.

# We generate the parser into the script, because we don't want this script to
# depend on cpt-lib.
nomount=''
REST=''
parse() {
    OPTIND=$(($#+1))
    while OPTARG= && [ $# -gt 0 ]; do
        case $1 in
            --?*=*) OPTARG=$1; shift
                    eval 'set -- "${OPTARG%%\=*}" "${OPTARG#*\=}"' ${1+'"$@"'}
                    ;;
            --no-*) unset OPTARG ;;
            -[mh]?*) OPTARG=$1; shift
                     eval 'set -- "${OPTARG%"${OPTARG#??}"}" -"${OPTARG#??}"' ${1+'"$@"'}
                     OPTARG= ;;
        esac
        case $1 in
            '-m')
                [ "${OPTARG:-}" ] && OPTARG=${OPTARG#*\=} && set "noarg" "$1" && break
                eval '[ ${OPTARG+x} ] &&:' && OPTARG='1' || OPTARG=''
                nomount="$OPTARG"
                ;;
            '-h'|'--help')
                usage
                exit 0 ;;
            --)
                shift
                while [ $# -gt 0 ]; do
                    REST="${REST} \"\${$(($OPTIND-$#))}\""
                    shift
                done
                break ;;
            [-]?*)
                set "unknown" "$1"; break ;;
            *)
                REST="${REST} \"\${$(($OPTIND-$#))}\""
        esac
        shift
    done
    [ $# -eq 0 ] && { OPTIND=1; unset OPTARG; return 0; }
    case $1 in
        unknown) set "Unrecognized option: $2" "$@" ;;
        noarg) set "Does not allow an argument: $2" "$@" ;;
        required) set "Requires an argument: $2" "$@" ;;
        pattern:*) set "Does not match the pattern (${1#*:}): $2" "$@" ;;
        notcmd) set "Not a command: $2" "$@" ;;
        *) set "Validation error ($1): $2" "$@"
    esac
    echo "$1" >&2
    exit 1
}
usage() { printf '%s\n' "usage: ${0##*/} [-m] [dir]" "" "Options:" \
    "  -m                          Don't mount or unmount directories"
}

parser_definition() {
    setup REST help:usage -- "usage: ${0##*/} [-m] [dir]"
    msg -- '' 'Options:'
    flag nomount -m        -- "Don't mount or unmount directories"
    disp :usage  -h --help hidden:1
}

log() {
    printf '\033[32m->\033[m %s.\n' "$*"
}

die() {
    log "$*" >&2
    exit 1
}

clean() {
    log Unmounting /dev, /proc and /sys from chroot; {
        umount "$1/dev"  ||:
        umount "$1/proc" ||:
        umount "$1/sys"  ||:
    }

    log Cleaning leftover host files; {
        rm -f "$1/root/.ash_history"
    }
}

main() {
    parse "$@" && eval set -- "$REST"
    [ -d "$1" ]        || die Given path does not exist
    [ "$(id -u)" = 0 ] || die Script needs to be run as root

    [ "$2" ] || {
        march=$(uname -m 2>/dev/null) ||:
        case "$march" in
            '')     march=native ;;
            x86_64) march=x86-64 ;;
            i*86)   march=i686 ;;
        esac
    }

    [ -z "$nomount" ] && {
        trap 'clean "$1"' EXIT INT

        log Mounting /dev, /proc and /sys from host; {
            mountpoint -q "$1/dev"  || mount -o bind /dev "$1/dev"
            mountpoint -q "$1/proc" || mount -t proc proc "$1/proc"
            mountpoint -q "$1/sys"  || mount -t sysfs sys "$1/sys"

        }

        log Copying /etc/resolv.conf from host; {
            [ -f "$1/etc/resolv.conf" ] || cp /etc/resolv.conf "$1/etc"
        }
    }

    log Entering chroot; {
        chroot "$1" /usr/bin/env -i \
            HOME=/root \
            TERM="$TERM" \
            SHELL=/bin/sh \
            USER=root \
            CFLAGS="${CFLAGS:--march=$march -mtune=generic -pipe -Os}" \
            CXXFLAGS="${CXXFLAGS:--march=$march -mtune=generic -pipe -Os}" \
            MAKEFLAGS="${MAKFLAGS:--j$(nproc 2>/dev/null || echo 1)}" \
            /bin/sh -l
    }
}

main "$@"