From bdd80cef5b70e702db135df02fa26e5cb60753fd Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Fri, 3 Jan 2014 14:04:58 +0200 Subject: [PATCH] Implement setup-ivi-clone Change-Id: I88c149fe892a25a0a3f03216d1c1c7c34d7e5478 Signed-off-by: Artem Bityutskiy --- installerfw-sh-functions | 2 +- ivi-clone.service | 9 + packaging/setup-ivi.changes | 4 +- packaging/setup-ivi.spec | 31 +++ setup-extlinux-conf | 2 +- setup-gummiboot-conf | 2 +- setup-ivi-boot | 24 +- setup-ivi-bootloader-conf | 2 +- setup-ivi-clone | 541 ++++++++++++++++++++++++++++++++++++++++++++ setup-ivi-clone-service | 180 +++++++++++++++ setup-ivi-fstab | 2 +- setup-ivi-sh-functions | 12 +- 12 files changed, 793 insertions(+), 18 deletions(-) create mode 100644 ivi-clone.service create mode 100755 setup-ivi-clone create mode 100755 setup-ivi-clone-service diff --git a/installerfw-sh-functions b/installerfw-sh-functions index 7d8a9b3..1316f8e 100644 --- a/installerfw-sh-functions +++ b/installerfw-sh-functions @@ -1,4 +1,4 @@ -# Copyright 2013 Intel Corporation +# Copyright 2013-2014 Intel Corporation # Author: Artem Bityutskiy # License: GPLv2 diff --git a/ivi-clone.service b/ivi-clone.service new file mode 100644 index 0000000..10a8d6f --- /dev/null +++ b/ivi-clone.service @@ -0,0 +1,9 @@ +[Unit] +Description=Cloning of the Tizen IVI OS to a non-removable media + +[Service] +Type=oneshot +StandardOutput=journal+console +StandardInput=tty +ExecStart=/usr/sbin/setup-ivi-clone-service +ExecStartPost=/usr/sbin/reboot diff --git a/packaging/setup-ivi.changes b/packaging/setup-ivi.changes index 94f588c..6d1cdb1 100644 --- a/packaging/setup-ivi.changes +++ b/packaging/setup-ivi.changes @@ -1,2 +1,2 @@ -* Thu Dec 19 14:29:23 UTC 2013 Artem Bityutskiy 1.0 -- Initial implementation +* Wed Jan 15 10:15:26 UTC 2014 Artem Bityutskiy 1.0 +- Initial implementation. diff --git a/packaging/setup-ivi.spec b/packaging/setup-ivi.spec index aa95539..d74a028 100644 --- a/packaging/setup-ivi.spec +++ b/packaging/setup-ivi.spec @@ -11,6 +11,7 @@ Requires: /usr/bin/grep Requires: /usr/bin/printf Requires: /usr/bin/printenv Requires: /usr/bin/sort +Requires: /usr/bin/tr Requires: virtual-setup-ivi-bootloader BuildArchitectures: noarch @@ -26,6 +27,20 @@ Provides: virtual-setup-ivi-bootloader Requires: %{name} Requires: syslinux-extlinux +%package -n setup-ivi-clone +Summary: A tool for cloning a Tizen IVI system +Requires: %{name} +Requires: /usr/bin/mount +Requires: /usr/bin/udevadm +Requires: /usr/bin/uuidgen +Requires: /usr/bin/sync +Requires: /usr/bin/tail +Requires: systemd +Requires: gptfdisk +Requires: e2fsprogs +Requires: dosfstools +Requires: rsync + %description This package provides various early system setup programs @@ -37,6 +52,10 @@ configuration files. This package provides a command-line tool for changing the extlinux bootloader configuration file. +%description -n setup-ivi-clone +This package provides a command line tool for cloning a Tizen IVI system to a +different disk. + ### ### PREP ### @@ -51,10 +70,14 @@ configuration file. %install install -d %{buildroot}/%{_sbindir} install -d %{buildroot}/%{_prefix}/share/setup-ivi +install -d %{buildroot}/%{_unitdir} install -m755 setup-ivi-boot %{buildroot}/%{_sbindir} install -m755 setup-ivi-fstab %{buildroot}/%{_sbindir} install -m755 setup-ivi-bootloader-conf %{buildroot}/%{_sbindir} +install -m755 setup-ivi-clone %{buildroot}/%{_sbindir} +install -m755 setup-ivi-clone-service %{buildroot}/%{_sbindir} +install -m644 ivi-clone.service %{buildroot}/%{_unitdir} install -m755 setup-gummiboot-conf %{buildroot}/%{_sbindir} install -m755 setup-extlinux-conf %{buildroot}/%{_sbindir} install -m644 setup-ivi-sh-functions %{buildroot}/%{_prefix}/share/setup-ivi @@ -84,3 +107,11 @@ rm -rf %{buildroot} %files -n setup-extlinux %defattr(-,root,root) %{_sbindir}/setup-extlinux-conf + +%files -n setup-ivi-clone +%defattr(-,root,root) +%{_sbindir}/setup-ivi-clone +%{_sbindir}/setup-ivi-clone-service +# Note, we do not need to run 'systemctl enable ivi-clone' for this one because +# it is activated by the 'systemd.unit=ivi-clone.service' kernel parameter. +%{_unitdir}/ivi-clone.service diff --git a/setup-extlinux-conf b/setup-extlinux-conf index adf5969..4a45152 100755 --- a/setup-extlinux-conf +++ b/setup-extlinux-conf @@ -1,6 +1,6 @@ #!/bin/sh -euf -# Copyright 2013 Intel Corporation +# Copyright 2013-2014 Intel Corporation # Author: Artem Bityutskiy # License: GPLv2 diff --git a/setup-gummiboot-conf b/setup-gummiboot-conf index 4fa7fbe..1fac081 100755 --- a/setup-gummiboot-conf +++ b/setup-gummiboot-conf @@ -1,6 +1,6 @@ #!/bin/sh -euf -# Copyright 2013 Intel Corporation +# Copyright 2013-2014 Intel Corporation # Author: Artem Bityutskiy # License: GPLv2 diff --git a/setup-ivi-boot b/setup-ivi-boot index ba167fc..428443b 100755 --- a/setup-ivi-boot +++ b/setup-ivi-boot @@ -1,6 +1,6 @@ #!/bin/sh -euf -# Copyright 2013 Intel Corporation +# Copyright 2013-2014 Intel Corporation # Author: Artem Bityutskiy # License: GPLv2 @@ -52,10 +52,9 @@ install_gummiboot() install_extlinux() { - verbose "installing extlinux to $bootdir" - local installdir="$bootdir/extlinux" local extlinux="extlinux" + local output # Check if extlinux is available if ! command -v "extlinux" >/dev/null 2>&1; then @@ -73,11 +72,13 @@ install_extlinux() "variable is not defined" # Install extlinux + verbose "installing extlinux to $bootdir, boot device node is" \ + "\"$boot_devnode\"" mkdir -p $verbose -- "$installdir" >&2 - "$extlinux" --device "$boot_devnode" -i "$installdir" || \ - fatal "cannot install extlinux to \"$installdir\"" \ - "(requires extlinux version 5 or greater)" - + output="$("$extlinux" --device "$boot_devnode" -i "$installdir" 2>&1)" \ + || fatal "cannot install extlinux to \"$installdir\" (note," \ + "extlinux version 5 or greater is required)" \ + "${br}${output}" # Get device node name for the boot disk local mbr_devnode @@ -88,9 +89,12 @@ install_extlinux() "variable is not defined" # Install the MBR part of extlinux - local mbr_bin="$(installerfw_mnt_prefix "/usr/share/syslinux/gptmbr.bin")" - dd if="$mbr_bin" of="$mbr_devnode" count=1 || \ - fatal "cannot install MBR: dd if=$mbr_bin of=$mbr_devnode" + local mbr_bin="$(installerfw_mnt_prefix \ + "/usr/share/syslinux/gptmbr.bin")" + verbose "setting up MBR, writing \"$mbr_bin\" to \"$mbr_devnode\"" + output="$(dd if="$mbr_bin" of="$mbr_devnode" count=1 2>&1)" || \ + fatal "cannot install MBR, dd if=$mbr_bin of=$mbr_devnode" \ + "failed${br}${output}" verbose "installed extlinux to $bootdir" } diff --git a/setup-ivi-bootloader-conf b/setup-ivi-bootloader-conf index 2b32595..b5b19f1 100755 --- a/setup-ivi-bootloader-conf +++ b/setup-ivi-bootloader-conf @@ -1,6 +1,6 @@ #!/bin/sh -euf -# Copyright 2013 Intel Corporation +# Copyright 2013-2014 Intel Corporation # Author: Artem Bityutskiy # License: GPLv2 diff --git a/setup-ivi-clone b/setup-ivi-clone new file mode 100755 index 0000000..e36c313 --- /dev/null +++ b/setup-ivi-clone @@ -0,0 +1,541 @@ +#!/bin/sh -euf + +# Copyright 2013-2014 Intel Corporation +# Author: Artem Bityutskiy +# License: GPLv2 + +PROG="setup-ivi-clone" +VER="1.0" + +srcdir="$(readlink -ev -- ${0%/*})" + +if [ -f "$srcdir/setup-ivi-sh-functions" ]; then + . "$srcdir/setup-ivi-sh-functions" + . "$srcdir/installerfw-sh-functions" +else + . /usr/share/setup-ivi/setup-ivi-sh-functions + . /usr/share/setup-ivi/installerfw-sh-functions +fi + +# This is a small trick which I use to make sure my scripts are portable - +# check if 'dash' is present, and if yes - use it. +if can_switch_to_dash; then + exec dash -euf -- "$srcdir/$PROG" "$@" + exit $? +fi + +# These are used in the cleanup handler, so have to be defined before the +# function +tmpdir= +unmount_list= +automount_status= + +# Unmount all partitions which we possibly mounted +unmount_partitions() +{ + printf "%s\n" "$unmount_list" | while IFS= read -r dir; do + if [ -n "$dir" ]; then + verbose "unmount \"$dir\"" + umount $verbose -- "$dir" >&2 ||: + fi + done +} + +# This function is called on exit, crashes, interrupts, etc. We clean up +# everything here before the program is terminated +cleanup_handler() +{ + local exitcode="$1" + + # Restore the default exit handler + trap - EXIT + + verbose "cleanup and exit with code $exitcode" + + unmount_partitions + rm -r $verbose -- "$tmpdir" >&2 + + if [ "$automount_status" = "active" ]; then + message "re-enabling udisks-automount-agent" + systemctl --user start udisks-automount-agent || \ + warning "cannot start udisks-automount-agent" + fi + + exit "$exitcode" +} + +# Append a line to a list, which is just text where each line is considered to +# be an element of the list. The first parameter is the line to append, the +# second parameter is the variable name to append to (the variable is defined +# outside of this function and contains the text to prepend the line to). +list_append() +{ + local __value="$1"; shift + local __list_name="$1" + eval local "__list=\$$__list_name" + + [ -z "$__list" ] && eval "$__list_name=\"\$__value\"" || \ + eval "$__list_name=\"\${__list}\${br}\${__value}\"" +} + +# Similar to 'list_append()', but prepends a list with a line. +list_prepend() +{ + local __value="$1"; shift + local __list_name="$1" + eval local "__list=\$$__list_name" + + [ -z "$__list" ] && eval "$__list_name=\"\$__value\"" || \ + eval "$__list_name=\"\${__value}\${br}\${__list}\"" +} + +# Create partitions on the destination disk. This function also updates the +# following installer framework environment variables: +# o INSTALLERFW_PARTx_PARTUUID +# o INSTALLERFW_PARTx_TYPE_ID +create_partitions() +{ + # The GPT partition type GUID for Linux partitions + local linuxfs_type_id="0fc63daf-8483-4772-8e79-3d69d8477de4" + + verbose "destroying all partitions at \"$dstdisk\"" + sgdisk -z "$dstdisk" -- > /dev/null 2>&1 ||: + sgdisk -Z "$dstdisk" -- > /dev/null 2>&1 ||: + + # Create all partitions one-by-one + local pnum=0 + local gdisk_pnum=1 + while [ "$pnum" -lt "$part_count" ]; do + installerfw_verify_defined "INSTALLERFW_PART${pnum}_SIZE" + + eval local size="\$INSTALLERFW_PART${pnum}_SIZE" + eval local bootflag="\${INSTALLERFW_PART${pnum}_BOOTFLAG:-}" + eval local type_id="\${INSTALLERFW_PART${pnum}_TYPE_ID:-$linuxfs_type_id}" + + if [ "$gdisk_pnum" -eq "$part_count" ]; then + # Make the last partition take the rest of the space + verbose "creating the last partition $pnum" + sgdisk -n "$gdisk_pnum:+0:-1s" -- "$dstdisk" > /dev/null + else + verbose "creating partition $pnum of size $size MiB" + sgdisk -n "$gdisk_pnum:+0:+${size}M" -- "$dstdisk" > /dev/null + fi + + # Re-set the UUID + local partuuid="$(uuidgen)" + verbose "setting partition $pnum PARTUUID to $partuuid" + sgdisk -u "$gdisk_pnum:$partuuid" -- "$dstdisk" > /dev/null + + # Take care of the boot flag + if [ "$bootflag" = "True" ]; then + verbose "setting the boot flag for partition $pnum" + sgdisk -A "$gdisk_pnum:set:2" -- "$dstdisk" > /dev/null + fi + + verbose "setting partition $pnum type ID to $type_id" + sgdisk -t "$gdisk_pnum:$type_id" -- "$dstdisk" > /dev/null + + # Re-define some installer framework variables + eval "export INSTALLERFW_PART${pnum}_PARTUUID=$partuuid" + eval "export INSTALLERFW_PART${pnum}_TYPE_ID=$type_id" + + pnum="$(($pnum + 1))" + gdisk_pnum="$(($gdisk_pnum + 1))" + done +} + +# Find the device node name by its major:minor numbers ($1:$2). +find_devnode_by_nums() +{ + local major="$1"; shift + local minor="$1" + + ls -1 /dev | while IFS= read -r node; do + local ma="$(printf "%d" "0x$(stat -c '%t' -- "/dev/$node")")" + local mi="$(printf "%d" "0x$(stat -c '%T' -- "/dev/$node")")" + + if [ -b "/dev/$node" ] && [ "$ma" -eq "$major" ] && \ + [ "$mi" -eq "$minor" ]; then + printf "%s" "/dev/$node" + break + fi + done +} + +# Find the device node name by a file name. In other words, for a given file, +# fine the device node of the partition this file resides on. +find_devnode_by_file() +{ + local file="$1" + local devnum="$(stat -c '%d' -- "$file")" + local major=$((($devnum / 256) % 256)) + local minor=$(($devnum % 256)) + + find_devnode_by_nums "$major" "$minor" +} + +# Find all the source and destination device nodes and declare change the +# following installer framework environment variables: +# o INSTALLERFW_PARTx_DEVNODE_NOW +# o INSTALLERFW_PARTx_DISK_DEVNODE_NOW +# And for the source partitions, the device node names are stored in +# "srcpartX_devnode" variables (X is the partition number). +find_devnodes() +{ + # Find source device nodes + + local pnum=0 + while [ "$pnum" -lt "$part_count" ]; do + installerfw_verify_defined "INSTALLERFW_PART${pnum}_MOUNTPOINT" + + eval local mountpoint="\$INSTALLERFW_PART${pnum}_MOUNTPOINT" + local devnode="$(find_devnode_by_file "$mountpoint")" + + [ -n "$devnode" ] || fatal "cannot find device node for" \ + "source partition $mountpoint" + + verbose "source partition $mountpoint device node is: $devnode" + + eval "srcpart${pnum}_devnode=$devnode" + + pnum="$(($pnum + 1))" + done + + # Find destination device nodes + + local major="$(printf "%d" "0x$(stat -c '%t' -- "$dstdisk")")" + local minor="$(printf "%d" "0x$(stat -c '%T' -- "$dstdisk")")" + + pnum=0 + while [ "$pnum" -lt "$part_count" ]; do + local part_minor="$(($minor + $pnum + 1))" + devnode="$(find_devnode_by_nums "$major" "$part_minor")" + + [ -n "$devnode" ] || fatal "cannot find device node for" \ + "destination partition $pnum" + + verbose "destination partition $pnum device node is: $devnode" + + eval "export INSTALLERFW_PART${pnum}_DEVNODE_NOW=$devnode" + eval "export INSTALLERFW_PART${pnum}_DISK_DEVNODE_NOW=$dstdisk" + + pnum="$(($pnum + 1))" + done + +} + +# Format all the destination partitions and changes the +# "INSTALLERFW_PARTx_UUID" installer framework environment variables. +format_dst_partitions() +{ + local pnum=0 + while [ "$pnum" -lt "$part_count" ]; do + installerfw_verify_defined "INSTALLERFW_PART${pnum}_FSTYPE" + eval local fstype="\$INSTALLERFW_PART${pnum}_FSTYPE" + eval local label="\${INSTALLERFW_PART${pnum}_LABEL:-}" + eval local devnode="\$INSTALLERFW_PART${pnum}_DEVNODE_NOW" + + local uuid="$(uuidgen)" + + verbose "format partition $pnum ($devnode) with mkfs.$fstype" + + if [ "$fstype" != "vfat" ]; then + verbose "partition $pnum label is \"$label\", UUID is \"$uuid\"" + + [ -z "$label" ] || label="-L $label" + + mkfs.$fstype $verbose $quiet $label -U "$uuid" -- "$devnode" >&2 + else + # FATFS Volume ID is an XXXXXXXX (32-bit) string, where + # X is are uppercase hex digits. + uuid="$(printf "%s" "$uuid" | head -c8 | \ + tr "[:lower:]" "[:upper:]")" + + verbose "partition $pnum label is \"$label\", UUID is \"$uuid\"" + + [ -z "$label" ] || label="-n $label" + + if [ -n "$verbose" ]; then + mkfs.vfat $verbose $label -i "$uuid" -- "$devnode" >&2 + else + # mkfs.vfat does not accept -q + mkfs.vfat $label -i "$uuid" -- "$devnode" > /dev/null + fi + + # However, the installer framework variable has to have + # the XXXX-XXXX format, not XXXXXXXX. Unfortunately, + # mkfs.vfat does not like the "-", while 'mount' + # requires the "-". + uuid="$(printf "%s" "$uuid" | LC_ALL=C sed -e 's/.\{4\}/&-/')" + fi + + eval "export INSTALLERFW_PART${pnum}_UUID=$uuid" + + pnum="$(($pnum+1))" + done +} + +# Mount source and destination partition +mount_partitions() +{ + local pnum=0 + while [ "$pnum" -lt "$part_count" ]; do + eval local mountpoint="\$INSTALLERFW_PART${pnum}_MOUNTPOINT" + + # Mount the source partition + eval local devnode="\$srcpart${pnum}_devnode" + local mntdir="$srcbase/$pnum" + + verbose "mounting source partition $pnum" \ + "($devnode, $mountpoint) to $mntdir" + + mkdir -p $verbose -- "$mntdir" >&2 + mount $verbose -- "$devnode" "$mntdir" >&2 + list_prepend "$mntdir" "unmount_list" + + # Mount the destination partition + eval devnode="\$INSTALLERFW_PART${pnum}_DEVNODE_NOW" + mntdir="$dstbase/$pnum" + + verbose "mounting destination partition $pnum" \ + "($devnode, $mountpoint) to $mntdir" + + mkdir -p $verbose -- "$mntdir" >&2 + mount $verbose -- "$devnode" "$mntdir" >&2 + list_prepend "$mntdir" "unmount_list" + + pnum="$(($pnum + 1))" + done +} + +# Copy data from all the source partitions to destination partitions +copy_the_data() +{ + local pnum=0 + while [ "$pnum" -lt "$part_count" ]; do + local src_mntdir="$srcbase/$pnum" + local dst_mntdir="$dstbase/$pnum" + + verbose "copy partition $pnum:" \ + "\"$src_mntdir\" -> \"$dst_mntdir\"" + + rsync -WaHXSh --exclude "${tmpdir}" "$src_mntdir/" "$dst_mntdir/" + + pnum="$(($pnum + 1))" + done +} + +# This function creates proper target hierarchy by mounting destination +# partition under the right mount points. By the time this function is called, +# all the destination partitions are mounted under "$dstbase/$pnum", so "/boot" +# is not under "/", etc. This function mounts them under "$dstbase/root" +create_dst_hierarchy() +{ + # Create a sorted list of mount points + local list= + local mountpoint= + local pnum=0 + while [ "$pnum" -lt "$part_count" ]; do + eval mountpoint="\$INSTALLERFW_PART${pnum}_MOUNTPOINT" + list_append "$mountpoint" "list" + pnum="$(($pnum+1))" + done + + list="$(printf "%s" "$list" | sort)" + + mkdir $verbose -- "$dstbase/root" >&2 + + local devnode= + local mntdir= + local IFS="$br" + for mountpoint in $list; do + installerfw_get_part_info "$mountpoint" "DEVNODE_NOW" "devnode" + mntdir="$dstbase/root$mountpoint" + + verbose "mounting \"$devnode\" under \"$mntdir\"" + mount $verbose -- "$devnode" "$mntdir" >&2 + + list_prepend "$mntdir" "unmount_list" + done +} + +# Amend the /etc/installerfw-environment file at the destination. +amend_installerfw_environment() +{ + # Most of the variables were already changed, let's take care about the + # rest. + export INSTALLERFW_INSTALLER_NAME="$PROG" + export INSTALLERFW_MOUNT_PREFIX="$dstbase/root" + + local pnum=0 + while [ "$pnum" -lt "$part_count" ]; do + eval "unset INSTALLERFW_PART${pnum}_DEVNODE" + eval "unset INSTALLERFW_PART${pnum}_DISK_DEVNODE" + pnum="$(($pnum+1))" + done + + installerfw_save_env +} + +# Check if the block device is mounted +# TODO: the much better way to do this is to use the open(2) "O_EXCL" flag +# which has special meaning for block devices. This would need to write a +# C helper program, something like setup-ivi-open-helper. +is_mounted() +{ + local devnode="$(esc_regexp "$1")" + + grep -q -e "^$devnode[p0-9]* " /proc/mounts +} + +show_usage() +{ + cat <<-EOF +Usage: $PROG [options] + +Clone a running Tizen IVI system to a different disk. The mandatory argument +"" is the device node name to clone to (e.g., /dev/sda). Be careful, +this program will destroy all the "" data! + +The program roughly works like this. + +1. Reads the "/etc/installerfw-environment" file to get information about the + partitions of the running system. +2. Partitions "" according to "/etc/installerfw-environment" (the last + partition gets re-sized to consume all the space on ""). +3. Formats all the newly created partitions on "". +4. Copies all the data from the disk Tizen IVI currently runs from to + "", partition-by-partition. +5. Configures the cloned system on "" by "setup-ivi-boot". +6. Reboots the system. + +Options: + -v, --verbose be verbose + --version show the program version and exit + -h, --help show this text and exit +EOF +} + +show_usage_fail() +{ + IFS= printf "%s\n\n" "$PROG: error: $*" >&2 + show_usage >&2 + exit 1 +} + +# Parse the options +tmp=`getopt -n $PROG -o v,h --long verbose,version,help -- "$@"` || + show_usage_fail "cannot parse command-line options" +eval set -- "$tmp" + +dstdisk= +verbose= +quiet="-q" +while true; do + case "$1" in + -v|--verbose) + verbose="-v" + quiet= + ;; + --version) + printf "%s\n" "$VER" + exit 0 + ;; + -h|--help) + show_usage + exit 0 + ;; + --) shift; break + ;; + *) show_usage_fail "unrecognized option \"$1\"" + ;; + esac + shift +done + +[ "$#" -eq 1 ] || \ + show_usage_fail "please, provide exactly one \"disk\" argument" + +dstdisk="$1" +[ -b "$dstdisk" ] || \ + fatal "\"$dstdisk\" does not exist or is not a block device node" + +is_mounted "$dstdisk" && fatal "\"$dstdisk\" is mounted" + +# Load the installer framework data +if ! installerfw_available; then + installerfw_restore_env +fi + +# Install the cleanup handler (the first parameter will be our exit code) +trap 'cleanup_handler $?' EXIT +trap 'cleanup_handler 1' HUP PIPE INT QUIT TERM + +# Create our work directory +tmpdir="$(mktemp --tmpdir="/tmp" -d -t -- "$PROG.XXXX")" +verbose "tmpdir is \"$tmpdir\"" + +srcbase="$tmpdir/src" +dstbase="$tmpdir/dst" +mkdir $verbose -- "$srcbase" >&2 +mkdir $verbose -- "$dstbase" >&2 + +# Fetch some installer framework variables +installerfw_verify_defined "INSTALLERFW_PTABLE_FORMAT" +installerfw_verify_defined "INSTALLERFW_PART_COUNT" + +ptable_format="$INSTALLERFW_PTABLE_FORMAT" +part_count="$INSTALLERFW_PART_COUNT" + +# Some general checks +if [ "$ptable_format" != "gpt" ]; then + fatal "partition table type \"$ptable_format\" is not supported" +fi + +# Disable the Tizen IVI automount agent, otherwise it may start mounting the +# $dstdisk partitions that we are going to create +automount_status="$(systemctl --user is-active udisks-automount-agent 2>/dev/null ||:)" +if [ "$automount_status" = "active" ]; then + message "disabling udisks-automount-agent" + if ! systemctl --user stop udisks-automount-agent; then + warning "cannot stop udisks-automount-agent" + automount_status="dunno" + fi +fi + +# Create partitions on the destination disk +message "partitioning $dstdisk" +create_partitions + +# Make sure the device nodes are created +udevadm settle > /dev/null 2>&1 ||: + +# Find device nodes for source and destination partitions +find_devnodes + +# Format the destination partitions +message "formatting $dstdisk" +format_dst_partitions + +# Mount the source and destination partitions +mount_partitions + +# Copy all the data +message "copying all the data to $dstdisk" +copy_the_data + +# Create proper target file-system hierarchy in order to be able to execute +# installer framework scripts for the target. +create_dst_hierarchy + +message "finished copying, configuring the cloned OS" + +# Amend the /etc/installerfw-environment file on the destination +amend_installerfw_environment + +# Configure the target system +$srcdir/setup-ivi-boot $verbose + +# Note, unmount happens in 'cleanup_handler()' +message "done, synchronizing the data" +sync diff --git a/setup-ivi-clone-service b/setup-ivi-clone-service new file mode 100755 index 0000000..775f382 --- /dev/null +++ b/setup-ivi-clone-service @@ -0,0 +1,180 @@ +#!/bin/sh -euf + +# Copyright 2013-2014 Intel Corporation +# Author: Artem Bityutskiy +# License: GPLv2 + +PROG="setup-ivi-clone-service" +VER="1.0" + +srcdir="$(readlink -ev -- ${0%/*})" +PATH="/usr/share/setup-ivi:$srcdir:$PATH" + +if [ -f "$srcdir/setup-ivi-sh-functions" ]; then + . "$srcdir/setup-ivi-sh-functions" + . "$srcdir/installerfw-sh-functions" +else + . /usr/share/setup-ivi/setup-ivi-sh-functions + . /usr/share/setup-ivi/installerfw-sh-functions +fi + +# This is a small trick which I use to make sure my scripts are portable - +# check if 'dash' is present, and if yes - use it. +if can_switch_to_dash; then + exec dash -euf -- "$srcdir/$PROG" "$@" + exit $? +fi + +# Find the first non-removable device and prints its device node name. +find_an_internal_disk() +{ + local dir + + ls -1 "/sys/block" | while IFS= read -r name; do + local removable="$(cat "/sys/block/$name/removable")" + local size="$(cat "/sys/block/$name/size")" + + if [ "$removable" -eq "0" ] && [ "$size" -ne "0" ]; then + verbose "found removable disk \"$name\"" + printf "%s" "/dev/$name" + break + fi + done +} + +# Get user-readable information about a disk. +get_disk_info() +{ + local name="${1##*/}" + local path="/sys/block/$name" + + local size="$(LC_ALL=C sed -n -e "s/[[:blank:]]*$//p" -- "$path/size")" + local info="size $(($size/2048))MiB" + + if [ -f "$path/device/vendor" ]; then + local vendor="$(LC_ALL=C sed -n -e "s/[[:blank:]]*$//p" -- \ + "$path/device/vendor")" + info="${info}, $vendor" + fi + + if [ -f "$path/device/model" ]; then + local model="$(LC_ALL=C sed -n -e "s/[[:blank:]]*$//p" -- \ + "$path/device/model")" + info="${info} $model" + fi + + printf "%s" "$info" +} + +# Get user confirmation about whether it is OK to proceed. +get_confirmation() +{ + printf "%s\n" "WARNING! All the disk data will be destroyed!" + + while true; do + printf "%s\n" "Type \"yes\" to proceed, type \"no\" to exit" + + local input + read input + + if printf "%s" "$input" | grep -q -I -e "^yes$"; then + return + elif printf "%s" "$input" | grep -q -I -e "^no$"; then + exit 0 + fi + done +} + +show_usage() +{ + cat <<-EOF +Usage: $PROG [options] + +This program is a wrapper over the 'setup-ivi-clone' program and it is intended +to be executed from a systemd service when the system boots up. + +By default, this program finds the first non-removable device in the system and +installs the OS there. However, if the 'ivi-clone-target=' kernel boot +option is present (see "/proc/cmdline"), then this program installs the OS to +the disk represented by "" (e.g., "/dev/sda"). + +The 'ivi-clone-target=autodetect' kernel option causes the default behaviour. + +Options: + -f, --force do not ask for confirmation, just proceed with cloning the OS + --version show the program version and exit + -v, --verbose be verbose + -h, --help show this text and exit +EOF +} + +show_usage_fail() +{ + IFS= printf "%s\n\n" "$PROG: error: $*" >&2 + show_usage >&2 + exit 1 +} + +# Parse the options +tmp=`getopt -n $PROG -o f,v,h --long force,verbose,version,help -- "$@"` || + show_usage_fail "cannot parse command-line options" +eval set -- "$tmp" + +verbose= +force= +while true; do + case "$1" in + -f|--force) + force="-f" + ;; + --version) + printf "%s\n" "$VER" + exit 0 + ;; + -v|--verbose) + verbose="-v" + ;; + -h|--help) + show_usage + exit 0 + ;; + --) shift; break + ;; + *) show_usage_fail "unrecognized option \"$1\"" + ;; + esac + shift +done + +[ "$#" -eq 0 ] || \ + show_usage_fail "this program does not take any arguments" + +# Sleep for a while to make sure other tasks finish and our messages appear the +# last on the console. +sleep 2 + +devnode="$(sed -n -e "s/^.*ivi-clone-target=\([^ ]\+\).*$/\1/p" /proc/cmdline)" +if [ -z "$devnode" ] || [ "$devnode" = "autodetect" ]; then + devnode="$(find_an_internal_disk)" + + [ -n "$devnode" ] || fatal "cannot find an internal disk" +fi + +# Make sure all device nodes are created +udevadm settle > /dev/null 2>&1 ||: + +[ -b "$devnode" ] || fatal "intended to clone the OS to \"$devnode\"," \ + "but it does not exist" + +disk_info="$(get_disk_info "$devnode")" +if [ -n "$disk_info" ]; then + disk_info="$devnode ($disk_info)" +else + disk_info="$devnode" +fi + +printf "%s\n" "cloning the Tizen IVI OS to \"$disk_info\"" +[ -z "$force" ] && get_confirmation +printf "%s\n" "this may take a while, be patient" + +setup-ivi-clone $verbose "$devnode" || fatal "failed to clone to $devnode" diff --git a/setup-ivi-fstab b/setup-ivi-fstab index a91c19c..c130e5f 100755 --- a/setup-ivi-fstab +++ b/setup-ivi-fstab @@ -1,6 +1,6 @@ #!/bin/sh -euf -# Copyright 2013 Intel Corporation +# Copyright 2013-2014 Intel Corporation # Author: Artem Bityutskiy # License: GPLv2 diff --git a/setup-ivi-sh-functions b/setup-ivi-sh-functions index 4831048..f1aa2c6 100644 --- a/setup-ivi-sh-functions +++ b/setup-ivi-sh-functions @@ -1,4 +1,4 @@ -# Copyright 2013 Intel Corporation +# Copyright 2013-2014 Intel Corporation # Author: Artem Bityutskiy # License: GPLv2 @@ -17,6 +17,16 @@ fatal() exit 1 } +warning() +{ + IFS= printf "%s\n" "$PROG: Warning!: $*" >&2 +} + +message() +{ + IFS= printf "%s\n" "$PROG: $*" +} + verbose() { if [ -n "$verbose" ]; then -- 2.7.4