mkarchiso 9.63 KB
Newer Older
1
#! /bin/bash
2
3
4
5

PKGLIST=""
QUIET="y"
FORCE="n"
6
PACCONFIG="/etc/pacman.conf"
7
8
9
10
11
12
13
14
15

APPNAME=$(basename "${0}")

# usage: usage <exitvalue>
usage ()
{
    echo "usage ${APPNAME} [options] command <command options>"
    echo " general options:"
    echo "    -f               Force overwrite of working files/squashfs image/bootable image"
16
    echo "    -p PACKAGE(S)    Additional package(s) to install, can be used multiple times"
17
    echo "    -C <file>        Config file for pacman. Default $PACCONFIG"
18
19
20
    echo "    -v               Enable verbose output."
    echo "    -h               This message."
    echo " commands:"
21
22
23
24
25
26
27
    echo "   create <dir>"
    echo "      create a base directory layout to work with"
    echo "      includes all specified packages"
    echo "   iso -p <bootloader> <dir> <image name>"
    echo "      build an iso image from the working dir"
    echo "   usb -p <bootloader> <dir> <image name>"
    echo "      build an iso image from the working dir"
28
29
30
    exit $1
}

31
while getopts 'C:i:P:p:a:t:fvh' arg; do
32
33
    case "${arg}" in
        p) PKGLIST="${PKGLIST} ${OPTARG}" ;;
34
        C) PACCONFIG="${OPTARG}" ;;
35
36
37
38
39
40
41
        f) FORCE="y" ;;
        v) QUIET="n" ;;
        h|?) usage 0 ;;
        *) echo "invalid argument '${arg}'"; usage 1 ;;
    esac
done

42
43
44
#trim spaces
PKGLIST="$(echo $PKGLIST)"

45
46
47
48
49
50
51
52
shift $(($OPTIND - 1))

# do UID checking here so someone can at least get usage instructions
if [ "$EUID" != "0" ]; then
    echo "error: This script must be run as root."
    exit 1
fi

53
54
55
56
57
if [ ! -f "$PACCONFIG" ]; then
    echo "error: pacman config file '$PACCONFIG' does not exist"
    exit 1
fi

58
command_name="${1}"
59
60
61
work_dir=""
imgname=""

62
case "${command_name}" in
63
64
65
    create) work_dir="${2}"; imgname="none" ;;
    iso) work_dir="${2}"; imgname="${3}" ;;
    usb) work_dir="${2}"; imgname="${3}" ;;
66
67
68
    *) echo "invalid command name '${command_name}'"; usage 1 ;;
esac

69
70
[ "x${imgname}" = "x" ] && echo "Image name must be specified" && usage 1
[ "x${work_dir}" = "x" ] && echo "Please specify a working directory" && usage 1
71

72
73
74
75
echo "${APPNAME} : Configuration Settings"
echo "        working directory:   ${work_dir}"
echo "               image name:   ${imgname}"

76
77
78
79
80
# usage: _pacman <packages>...
_pacman ()
{
    local ret
    if [ "${QUIET}" = "y" ]; then
81
        mkarchroot -C "$PACCONFIG" -f "${work_dir}/root-image" $* 2>&1 >/dev/null
82
83
        ret=$?
    else
84
        mkarchroot -C "$PACCONFIG" -f "${work_dir}/root-image" $*
85
86
        ret=$?
    fi
87
88
89
90

    # Cleanup
    find "${work_dir}" -name *.pacnew -name *.pacsave -name *.pacorig -delete

91
92
93
94
95
    if [ $ret -ne 0 ]; then
        exit 1
    fi
}

96
97
98
99
100
101
102
103
104
command_create () {
    echo "====> Creating working directory: ${work_dir}"
    mkdir -p "${work_dir}/iso/"
    mkdir -p "${work_dir}/root-image/"
    echo "# archiso isomounts file
# img - location of image/directory to mount relative to addons directory
# arch - architecture of this image
# mount point - absolute location on the post-initrd root
# type - either 'bind' or 'squashfs' for now
105

106
# syntax: <img> <arch> <mount point> <type>
107

108
109
110
# NOTE: Order matters. If the same file exists in multiple
#  images, the FIRST one mounted, top-down, will take precedence

111
112
root-image.sqfs i686 / squashfs
#root-image-x86_64.sqfs x86_64 / squashfs" > "${work_dir}/isomounts"
113

114
    echo "README for this archiso created directory
115

116
117
118
All directories in this dir, except for 'iso' will be squashed
with squashfs and put into the iso dir as iso/<dirname>.sqfs
This should be reflected in the isomounts file
119

120
The iso dir is later used to build the actual bootable iso.
121
122
Please ensure the proper bootloader is installed or copied
to the iso/ directory.
123
124

...TODO: Write more..." > "${work_dir}/README"
125

126
127
128
    if [ "${PKGLIST}" != "" ]; then
        echo "====> Installing packages to '${work_dir}/root-image/'"
        _pacman "${PKGLIST}"
129

130
131
132
        echo "Cleaning up what we can"
        if [ -d "${work_dir}/root-image/boot/" ]; then
            # remove the initcpio images that were generated for the host system
133
            find "${work_dir}/root-image/boot" -name '*.img' -delete
134
135
        fi

136
137
138
139
140
141
142
143
144
145
146
147
148
149
        #TODO is this needed? do it at the Makefile level?
        if [ -d "${work_dir}/root-image/home/" ]; then
            echo "Creating default home directory"
            install -d -o1000 -g100 -m0755 "${work_dir}/root-image/home/arch"
        fi

        # delete a lot of unnecessary cache/log files
        kill_dirs="var/abs var/cache/man var/cache/pacman var/lib/pacman/sync var/log/* var/mail tmp/* initrd"
        for x in ${kill_dirs}; do
            if [ -e "${work_dir}/root-image/${x}" ]; then
                rm -rf "${work_dir}/root-image/${x}"
            fi
        done
    fi
150
}
151

152
153
154
155
156
157
158
159
160
161
# _mksquash dirname
_mksquash () {
    if [ ! -d "$1" ]; then
        echo "Error: '$1' is not a directory"
        return 1
    fi

    sqimg="${work_dir}/iso/$(basename ${1}).sqfs"
    echo "====> Generating SquashFS image for '${1}'"
    if [ -e "${sqimg}" ]; then
162
163
164
        dirhaschanged=$(find ${1} -newer ${sqimg})
        if [ "${dirhaschanged}" != "" ]; then
            echo "SquashFS image '${sqimg}' is not up to date, rebuilding..."
165
            rm "${sqimg}"
166
        else
167
168
            echo "SquashFS image '${sqimg}' is up to date, skipping."
            return
169
170
171
        fi
    fi

172
    echo "Creating SquashFS image. This may take some time..."
173
174
    start=$(date +%s)
    if [ "${QUIET}" = "y" ]; then
175
        mksquashfs "${1}" "${sqimg}" -noappend >/dev/null
176
    else
177
        mksquashfs "${1}" "${sqimg}" -noappend
178
179
180
    fi
    minutes=$(echo $start $(date +%s) | awk '{ printf "%0.2f",($2-$1)/60 }')
    echo "Image creation done in $minutes minutes."
181
}
182

183
184
185
186
187
188
189
190
191
_imgcommon () {
    for d in $(find "${work_dir}" -maxdepth 1 -type d -name '[^.]*'); do
        if [ "$d" != "${work_dir}/iso" -a \
             "$(basename "$d")" != "iso" -a \
             "$d" != "${work_dir}" ]; then
            _mksquash "$d"
        fi
    done

192
    echo "====> Making bootable image"
193
194
195
196
197
198
199
200
201
202
203
204
205

    # Sanity checks
    if [ ! -d "${work_dir}/iso" ]; then
        echo "Error: '${work_dir}/iso' doesn't exist. What did you do?!"
        exit 1
    fi

    if [ ! -f "${work_dir}/isomounts" ]; then
        echo "Error: the isomounts file doesn't exist. This image won't do anything"
        echo "  Protecting you from yourself and erroring out here..."
        exit 1
    fi

206
207
208
209
210
211
212
213
214
215
    if [ -e "${imgname}" ]; then
        if [ "${FORCE}" = "y" ]; then
            echo "Removing existing bootable image..."
            rm -rf "${imgname}"
        else
            echo "error: Image '${imgname}' already exists, aborting."
            exit 1
        fi
    fi

216
    cp "${work_dir}/isomounts" "${work_dir}/iso/"
217
218
219
220

    export LABEL="ARCHISO_$(pwgen -n 8 1 | tr [a-z] [A-Z])"
    [ -f ${work_dir}/iso/boot/grub/menu.lst ] && sed "s|archisolabel=[^ ]*|archisolabel=${LABEL}|" -i ${work_dir}/iso/boot/grub/menu.lst
    [ -f ${work_dir}/iso/boot/isolinux/isolinux.cfg ] && sed "s|archisolabel=[^ ]*|archisolabel=${LABEL}|" -i ${work_dir}/iso/boot/isolinux/isolinux.cfg
221
222
223
224
}

command_iso () {
    _imgcommon
Aaron Griffin's avatar
Aaron Griffin committed
225

226
    bootflags=""
227
    if [ "$PKGLIST" = "grub" -o "$PKGLIST" = "grub-gfx" ]; then
228
229
        if [ ! -e "${work_dir}/iso/boot/grub/stage2_eltorito" ]; then
            echo "error: grub stage files not found in '${work_dir}/iso/boot/grub'"
230
231
            exit 1
        fi
Aaron Griffin's avatar
Aaron Griffin committed
232
233

        bootflags="-b boot/grub/stage2_eltorito"
234
    elif [ "$PKGLIST" = "syslinux" ]; then
235
236
        if [ ! -e "${work_dir}/iso/boot/isolinux/isolinux.bin" ]; then
            echo "error: isolinux bin file not found in '${work_dir}/iso/boot/isolinux'"
237
238
            exit 1
        fi
Aaron Griffin's avatar
Aaron Griffin committed
239
240

        bootflags="-b boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat"
241
    else
Aaron Griffin's avatar
Aaron Griffin committed
242
243
244
        echo "No bootloader specified. Use the -p flag to specify"
        echo "  Supported Bootloaders:"
        echo "     grub"
245
        echo "     grub-gfx"
246
        echo "     syslinux"
Aaron Griffin's avatar
Aaron Griffin committed
247
        exit 1
248
    fi
249

250
251
    echo "Creating ISO image..."
    qflag=""
Gerhard Brauer's avatar
Gerhard Brauer committed
252
    [ "${QUIET}" = "y" ] && qflag="-quiet"
253
254
255
    mkisofs ${qflag} -r -l $bootflags -uid 0 -gid 0 \
        -input-charset utf-8 -p "prepared by mkarchiso" \
        -no-emul-boot -boot-load-size 4 -boot-info-table \
256
257
        -publisher "Arch Linux <http://www.archlinux.org>" \
        -A "Arch Linux Live/Rescue CD" \
258
        -V "${LABEL}" \
259
260
261
262
263
        -o "${imgname}" "${work_dir}/iso/"
}

command_usb () {
    _imgcommon
264

265
    modprobe -q loop > /dev/null 2>&1
266
267
268
269
270
271

    # Calculate cylinder size in bytes
    CYL_SIZE=$((255*63*512))

    # First partition offset
    PART_OFFSET=$((63*512))
272
273

    # ext2 overhead's upper bound is 6%, empirically tested up to 1GB
Aaron Griffin's avatar
Aaron Griffin committed
274
    rootsize=$(du -bs "${work_dir}/iso" | cut -f1)
275
276
277
278
    imgsz=$(( (${rootsize}*106)/100/${CYL_SIZE} + 1 )) # image size in cylinders

    # Get next free loop device
    devloop=$(losetup -f)
279
280

    # create the filesystem image file
281
282
283
284
    dd if=/dev/zero of="$imgname" bs="$CYL_SIZE" count="$imgsz"

    # Setup a loop device, and skip the first 63 sectors
    losetup "$devloop" -o "$PART_OFFSET" "$imgname"
285
286

    # create a filesystem on the image
287
    mke2fs -m 0 -F -L "${LABEL}" "$devloop"
288
289
290

    # mount the filesystem and copy data
    TMPDIR=$(mktemp -d archiso-usbXXXXXX)
291
    mount "$devloop" "$TMPDIR"
292
    cp -a "${work_dir}"/iso/* "$TMPDIR"
293
    umount -d "$TMPDIR"
294
    rm -rf "$TMPDIR"
295
296

    # create a partition table
297
298
299
300
301
302
303
304
305
306
    fdisk -C "$imgsz" -H 255 -S 63 "$imgname" << EOF
n
p
1


a
1
p
w
307
308
309
310
EOF

    # install grub on the image
    grub --no-floppy --batch << EOF
311
312
313
device (hd0) ${imgname}
root (hd0,0)
setup (hd0)
314
315
316
317
EOF

}

318
319
# Go through the main commands in order. If 'all' was specified, then we want
# to do everything. Start with 'install'.
320
321
if [ "${command_name}" = "create"  ]; then
    command_create
322
fi
323
324
if [ "${command_name}" = "iso" ]; then
    command_iso
325
fi
326
327
if [ "${command_name}" = "usb" ]; then
    command_usb
328
329
330
fi

# vim:ts=4:sw=4:et: