create-iso.sh 11.1 KB
Newer Older
1
#!/bin/bash
2
# shellcheck disable=SC1090
3 4 5 6 7 8
#
# Copyright (C) 2017 Centro de Computacao Cientifica e Software Livre
# Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
#
# This file is part of create-iso
#
9
# create-iso is free software; you can redistribute it and/or
10 11 12 13 14 15 16 17 18 19 20 21 22 23
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
# USA.

24
export PREFIX="${PWD}"
sulzbach's avatar
sulzbach committed
25
source "${PREFIX}/conf/opts.conf"
26

27
while true; do
28
    case "${1}" in
sulzbach's avatar
sulzbach committed
29 30 31 32 33 34 35 36 37 38
        -h | --help          ) _HELP=true; break ;;
        -p | --profile       ) _PROF="$2"; shift; shift ;;
        -v | --version       ) _VERSION="$2"; shift; shift ;;
        -i | --input         ) _INPUT="$2"; shift; shift ;;
        -o | --output        ) _OUTPUT="$2"; shift; shift ;;
        -r | --repo          ) _REPO="$2"; shift; shift ;;
        -d | --debug         ) _DEBUG=true; shift ;;
        -*                   ) echo "Unrecognized option. Try with --help."; \
                               exit 1 ;;
        *                    ) break ;;
39 40 41
    esac
done

42
if [ "${_HELP}" = true ]; then
sulzbach's avatar
sulzbach committed
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
    echo    "Usage: $0 [OPTION] [ARGUMENT]..."
    echo    ""
    echo    "Examples:"
    echo    "    sudo $0 --profile le6/testing  # Generate le6 iso image"
    echo    ""
    echo    "Options:"
    echo    "    -h, --help                               Show this help list"
    echo -n "    -p, --profile [PROF]                     Select profile to "
    echo    "load configurations and run enabled scripts from"
    echo -n "    -v, --version [VERSION]                  Select version of "
    echo    "the distro"
    echo -n "    -i, --input [PATH]                       Select ISO file "
    echo    "which contains the filesystem to be used as base"
    echo -n "    -o, --output [PATH]                      Select path to "
    echo    "output the created ISO image"
    echo -n "    -r, --repo [stable | testing | unstable] Select remote "
    echo    "repository codename to download packages from"
    echo    "    -d, --debug                              Cache from last"
    echo    "execution"

    exit 0
64
fi
65

sulzbach's avatar
sulzbach committed
66 67 68
if [ "$(id -u)" != "0" ]; then
    echo "You must have more power to run this script. Are you root?"
    exit -1
69
fi
70

sulzbach's avatar
sulzbach committed
71 72
if [ -z "${_PROF}" ]; then
    echo "No profile specified. Try with --help or --list-profiles."
73
    exit 1
74 75
fi

sulzbach's avatar
sulzbach committed
76
if ! source "${PREFIX}/conf/${_PROF}.conf"; then
sulzbach's avatar
sulzbach committed
77
    echo "Unavailable profile. Try with --help or --list-profiles." 
78 79 80
    exit 1
fi

sulzbach's avatar
sulzbach committed
81 82
source "${PREFIX}/conf/general.conf"

sulzbach's avatar
sulzbach committed
83 84 85 86
if [ -z "${_VERSION}" ]; then
    echo "No version specified. Ommiting."
else
    NAME="${NAME} ${_VERSION}"
87
fi
88

sulzbach's avatar
sulzbach committed
89 90 91 92 93 94 95 96 97
if [ -z "${_OUTPUT}" ]; then
    _OUTPUT="${ISODIR}/output"
fi

if [ "${_REPO}" != "stable" ] && [ "${_REPO}" != "testing" ] && \
[ "${_REPO}" != "unstable" ]; then
    echo "Unrecognized remote repository codename. Try with --help."
    exit 1
fi
98

99
TIME_STAMP="$(date +'%Y-%m-%dT%H:%M:%S')"
sulzbach's avatar
sulzbach committed
100

101
export LOG_FILE="${LOG_PATH}/create-iso-${TIME_STAMP}.log"
102

103 104
exec 1<&-
exec 2<&-
105
exec 1<> "${LOG_FILE}"
106
exec 1>&1
107 108

source "${PREFIX}/helper/logging.incsh"
sulzbach's avatar
sulzbach committed
109 110 111

# Disable debug logging if not in debug mode
# [ "${_DEBUG}" == false ] && let VERBOSE_FLAGS="${ALL} ^ ${DEBUG}"
112 113

# Installation of create-iso's dependencies
114 115 116
apt-get install isolinux syslinux squashfs-tools genisoimage xorriso reprepro \
binwalk --yes || createIsoLogger --message "Failed to install basic tools" \
--tag "apt-get"
117

sulzbach's avatar
sulzbach committed
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 146 147 148 149 150 151 152 153 154 155 156 157 158
if [ -z "${_INPUT}" ]; then
    NEWMD5="$(wget "${ISOURL}/MD5SUMS" -O- | grep "amd64" | sort \
    --version-sort | tail --lines=1)"
    ISONAME="$(cut --delimiter='*' --fields=2 <<< "${NEWMD5}")"
    NEWMD5="$(cut --delimiter=' ' --fields=1 <<< "${NEWMD5}")"
    OLDMD5="$(cat "${ISODIR}/input/MD5SUMS")"
    if [ "${OLDMD5}" != "${NEWMD5}" ]; then
        mkdir --parents "${ISODIR}/input"
        pushd "${ISODIR}/input" || createIsoLogger --message \
        "Unable to change directory" --tag "pushd"
        wget "${ISOURL}/${ISONAME}" || createIsoLogger --message \
        "Unable to download image" --tag "wget"
        popd || createIsoLogger --message "Unable to change directory" --tag \
        "popd"
        echo "${NEWMD5}" > "${ISODIR}/input/MD5SUMS"
    fi

    mkdir --parents "${PREFIX}/isobase"
    # Mount if it isn't already mounted
    (mountpoint --quiet "${PREFIX}/isobase" || mount \
    "${ISODIR}/input/${ISONAME}" "${PREFIX}/isobase" --types iso9660 --options \
    ro,loop) || createIsoLogger --message "Unable to mount iso9660 fs" --tag \
    "mount"

    SQUASHFSPATH="${PREFIX}/isobase/casper/filesystem.squashfs"
else
    mkdir --parents "${PREFIX}/isobase"
    # Mount if it isn't already mounted
    (mountpoint --quiet "${PREFIX}/isobase" || mount "${_INPUT}" \
    "${PREFIX}/isobase" --types iso9660 --options ro,loop) || createIsoLogger \
    --message "Unable to mount iso9660 fs" --tag "mount"

    SQUASHFSPATH="${PREFIX}/isobase/casper/filesystem.squashfs"
fi

mkdir --parents "${TMPFS}"
# Mount if it isn't already mounted
(mountpoint --quiet "${TMPFS}" || mount tmpfs "${TMPFS}" --types tmpfs \
--options "suid,dev,size=${TMPFS_SIZE}") || createIsoLogger --message \
"Unable to mount tmpfs" --tag "mount"

159
# TODO: Decide what to do when generating for production. Cache is being currently used here, but the generated entry is being discarded.
sulzbach's avatar
sulzbach committed
160 161
mkdir --parents "${CACHEDIR}"
find="$(find "${CACHEDIR}" -mindepth 1 -maxdepth 1 -type d | sort --numeric)"
sulzbach's avatar
sulzbach committed
162
if [ -z "${find}" ]; then
sulzbach's avatar
sulzbach committed
163
    # TODO: Mount iso in place
sulzbach's avatar
sulzbach committed
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
    mkdir --parents "${TMP}/lower"
    lowerdir="${TMP}/lower"
else
    lowerdir="$(head --lines=1 <<< "${find}")"
    find="$(grep --invert-match "${lowerdir}" <<< "${find}")"
    lowerdir="$(sed 's/:/\\:/g' <<< "${lowerdir}")/image"
    for dir in ${find}; do
        lowerdir="$(sed 's/:/\\:/g' <<< "${dir}")/image:${lowerdir}"
    done
fi

upperdir="${TMP}/upper"
workdir="${TMP}/work"

mkdir --parents "${upperdir}"
mkdir --parents "${workdir}"

mkdir --parents "${TMP}/image"
# Mount if it isn't already mounted
(mountpoint --quiet "${TMP}/image" || mount overlay "${TMP}/image" --types \
overlay --options \
rw,lowerdir="${lowerdir}",upperdir="${upperdir}",workdir="${workdir}") || \
createIsoLogger --message "Unable to mount overlay fs" --tag "mount"

# TODO: Fix this mess
[ "${lowerdir}" != "${TMP}/lower" ] && SQUASHFSPATH="${TMP}/image/casper/filesystem.squashfs"

mkdir --parents "${PREFIX}/squashfsbase"
# Mount if it isn't already mounted
(mountpoint --quiet "${PREFIX}/squashfsbase" || mount "${SQUASHFSPATH}" \
"${PREFIX}/squashfsbase" --types squashfs --options ro,loop) || \
createIsoLogger --message "Unable to mount squashfs" --tag "mount"

lowerdir="${PREFIX}/squashfsbase"
upperdir="${TMPFS}/upper"
workdir="${TMPFS}/work"

mkdir --parents "${upperdir}"
mkdir --parents "${workdir}"
mkdir --parents "${CHROOTDIR}"

# Mount if it isn't already mounted
(mountpoint --quiet "${CHROOTDIR}" || mount overlay "${CHROOTDIR}" --types \
overlay --options \
rw,lowerdir="${lowerdir}",upperdir="${upperdir}",workdir="${workdir}") || \
createIsoLogger --message "Unable to mount overlay fs" --tag "mount"

source "${PREFIX}/common.sh"
212

sulzbach's avatar
sulzbach committed
213
# TODO: Organize variables
214 215
# Each one of the available scripts is executed in numeric order
for script in "${SCRIPTSDIR}"/*; do
sulzbach's avatar
sulzbach committed
216 217 218 219
    if [ -x "${script}" ] && grep -E --quiet '^[0-9]{2}-[A-Za-z]+' \
    <<< "$(basename "${script}")"; then
        createIsoLogger --message "Running $(basename "${script}")..." --type \
        info
220
        if ! "${script}"\
sulzbach's avatar
sulzbach committed
221 222 223 224 225
             "${_PROF}" \
             "${NAME}" \
             "${WEBSITE}" \
             "${TMPFS}" \
             "${TMP}" \
226 227
             "${CHROOTDIR}" \
             "${SCRIPTSDIR}" \
sulzbach's avatar
sulzbach committed
228 229 230
             "${PKGLIST}" \
             "${SOURCESLIST}" \
             "${_REPO}" \
sulzbach's avatar
sulzbach committed
231
             "${KEYURL}" \
232 233
             "${CACHEDIR}" \
             "${_DEBUG}"; then
sulzbach's avatar
sulzbach committed
234
			cleanup "${CHROOTDIR}"
sulzbach's avatar
sulzbach committed
235
            createIsoLogger --message "Failed to run script"
236 237 238
            exit 1
        fi

sulzbach's avatar
sulzbach committed
239 240 241 242
        # If the script is marked as 'chroot', it means it performs changes in 
        # the overlaid chroot filesystem,
        # therefore, the 'chroot script', filled with the required commands by
        # the marked script, is executed
243 244 245
        # This warning is disabled because the literal '-' is actually needed:
        # shellcheck disable=SC1001
        if [[ "$(basename "${script}")" =~ [0-9]+\-chroot\.* ]]; then
246
            initChroot "${CHROOTDIR}" "${LOG_FILE}"
247
            execChroot "${CHROOTDIR}" "${SCRIPT_NAME}"
248
            closeChroot "${CHROOTDIR}" "${LOG_FILE}"
249 250 251 252
        fi
    fi
done

sulzbach's avatar
sulzbach committed
253 254 255 256 257
# The name of the generated ISO is a concatenation of lowered-cased 
# hyphen-delimited distribution version, repo codename and timestamp
ISONAME="$(sed -e 's/ /-/g' -e 's/\(.*\)/\L\1/g' \
<<< "${NAME}")-${_REPO}-${TIME_STAMP}.iso"

258
# Finally generates the resulting iso image
sulzbach's avatar
sulzbach committed
259 260 261 262 263 264 265 266
mkdir --parents "${_OUTPUT}"
pushd "${TMP}/image" || createIsoLogger --message "Unable to change directory" \
--tag "pushd"
xorriso -as mkisofs -isohybrid-mbr /usr/lib/ISOLINUX/isohdpfx.bin -c \
isolinux/boot.cat -b isolinux/isolinux.bin -no-emul-boot -boot-load-size 4 \
-boot-info-table -eltorito-alt-boot -no-emul-boot -isohybrid-gpt-basdat -o \
"${_OUTPUT}/${ISONAME}" . || createIsoLogger --message \
"Failed to generate iso image." --tag "xorriso"
Lucas Sulzbach's avatar
Lucas Sulzbach committed
267
popd || createIsoLogger --message "Unable to change directory" --tag "popd"
268

sulzbach's avatar
sulzbach committed
269 270 271 272 273 274 275 276
cleanup "${CHROOTDIR}"

# Umount if it is mounted
(! mountpoint --quiet "${CHROOTDIR}" || umount "${CHROOTDIR}") || \
createIsoLogger --message "Unable to umount overlay fs" --tag "umount"

rm --recursive --force "${CHROOTDIR:?}" "${upperdir:?}" "${workdir:?}"

sulzbach's avatar
sulzbach committed
277 278 279 280 281 282 283
# Umount if it is mounted
(! mountpoint --quiet "${PREFIX}/squashfsbase" || umount \
"${PREFIX}/squashfsbase") || createIsoLogger --message \
"Unable to umount squashfs" --tag "umount"

rm --recursive --force "${PREFIX:?}/squashfsbase"

sulzbach's avatar
sulzbach committed
284 285 286 287 288
# Umount if it is mounted
(! mountpoint --quiet "${TMP}/image" || umount "${TMP}/image") || \
createIsoLogger --message "Unable to umount overlay fs" --tag "umount"

if [ "${_DEBUG}" == true ]; then
sulzbach's avatar
sulzbach committed
289 290
    mkdir --parents "${CACHEDIR}/${TIME_STAMP}"
    mv "${TMP}/MD5SUMS" "${CACHEDIR}/${TIME_STAMP}/" || createIsoLogger \
sulzbach's avatar
sulzbach committed
291
    --message "Unable to move file" --tag "mv"
sulzbach's avatar
sulzbach committed
292
    mv "${TMP}/upper" "${CACHEDIR}/${TIME_STAMP}/image" || createIsoLogger \
sulzbach's avatar
sulzbach committed
293 294 295 296
    --message "Unable to move directory" --tag "mv"
fi

# Umount if it is mounted
sulzbach's avatar
sulzbach committed
297 298
(! mountpoint --quiet "${TMPFS}" || umount "${TMPFS}") || createIsoLogger \
--message "Unable to umount tmpfs" --tag "umount"
sulzbach's avatar
sulzbach committed
299

sulzbach's avatar
sulzbach committed
300
rm --recursive --force "${TMPFS:?}"
sulzbach's avatar
sulzbach committed
301 302 303 304 305 306

# Umount if it is mounted
(! mountpoint --quiet "${PREFIX}/isobase" || umount "${PREFIX}/isobase") || \
createIsoLogger --message "Unable to umount iso9660 fs" --tag "umount"

rm --recursive --force "${PREFIX:?}/isobase"
307 308

exit 0