diff options
Diffstat (limited to 'scripts/lib/wic/utils')
-rw-r--r-- | scripts/lib/wic/utils/__init__.py | 0 | ||||
-rw-r--r-- | scripts/lib/wic/utils/errors.py | 29 | ||||
-rw-r--r-- | scripts/lib/wic/utils/fs_related.py | 84 | ||||
-rw-r--r-- | scripts/lib/wic/utils/misc.py | 95 | ||||
-rw-r--r-- | scripts/lib/wic/utils/oe/__init__.py | 22 | ||||
-rw-r--r-- | scripts/lib/wic/utils/oe/misc.py | 250 | ||||
-rw-r--r-- | scripts/lib/wic/utils/partitionedfs.py | 360 | ||||
-rw-r--r-- | scripts/lib/wic/utils/runner.py | 110 | ||||
-rw-r--r-- | scripts/lib/wic/utils/syslinux.py | 58 |
9 files changed, 1008 insertions, 0 deletions
diff --git a/scripts/lib/wic/utils/__init__.py b/scripts/lib/wic/utils/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/scripts/lib/wic/utils/__init__.py diff --git a/scripts/lib/wic/utils/errors.py b/scripts/lib/wic/utils/errors.py new file mode 100644 index 0000000..d1b514d --- /dev/null +++ b/scripts/lib/wic/utils/errors.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python -tt +# +# Copyright (c) 2007 Red Hat, Inc. +# Copyright (c) 2011 Intel, Inc. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the Free +# Software Foundation; version 2 of the License +# +# 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., 59 +# Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +class WicError(Exception): + pass + +class CreatorError(WicError): + pass + +class Usage(WicError): + pass + +class ImageError(WicError): + pass diff --git a/scripts/lib/wic/utils/fs_related.py b/scripts/lib/wic/utils/fs_related.py new file mode 100644 index 0000000..2e74461 --- /dev/null +++ b/scripts/lib/wic/utils/fs_related.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python -tt +# +# Copyright (c) 2007, Red Hat, Inc. +# Copyright (c) 2009, 2010, 2011 Intel, Inc. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the Free +# Software Foundation; version 2 of the License +# +# 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., 59 +# Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +from __future__ import with_statement +import os +import errno + +from wic.utils.oe.misc import exec_cmd + +def makedirs(dirname): + """A version of os.makedirs() that doesn't throw an + exception if the leaf directory already exists. + """ + try: + os.makedirs(dirname) + except OSError, err: + if err.errno != errno.EEXIST: + raise + +class Disk: + """ + Generic base object for a disk. + """ + def __init__(self, size, device=None): + self._device = device + self._size = size + + def create(self): + pass + + def cleanup(self): + pass + + def get_device(self): + return self._device + def set_device(self, path): + self._device = path + device = property(get_device, set_device) + + def get_size(self): + return self._size + size = property(get_size) + + +class DiskImage(Disk): + """ + A Disk backed by a file. + """ + def __init__(self, image_file, size): + Disk.__init__(self, size) + self.image_file = image_file + + def exists(self): + return os.path.exists(self.image_file) + + def create(self): + if self.device is not None: + return + + blocks = self.size / 1024 + if self.size - blocks * 1024: + blocks += 1 + + # create disk image + dd_cmd = "dd if=/dev/zero of=%s bs=1024 seek=%d count=1" % \ + (self.image_file, blocks) + exec_cmd(dd_cmd) + + self.device = self.image_file diff --git a/scripts/lib/wic/utils/misc.py b/scripts/lib/wic/utils/misc.py new file mode 100644 index 0000000..1415ae9 --- /dev/null +++ b/scripts/lib/wic/utils/misc.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python -tt +# +# Copyright (c) 2010, 2011 Intel Inc. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the Free +# Software Foundation; version 2 of the License +# +# 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., 59 +# Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +import os +import time +import wic.engine + +def build_name(kscfg, release=None, prefix=None, suffix=None): + """Construct and return an image name string. + + This is a utility function to help create sensible name and fslabel + strings. The name is constructed using the sans-prefix-and-extension + kickstart filename and the supplied prefix and suffix. + + kscfg -- a path to a kickstart file + release -- a replacement to suffix for image release + prefix -- a prefix to prepend to the name; defaults to None, which causes + no prefix to be used + suffix -- a suffix to append to the name; defaults to None, which causes + a YYYYMMDDHHMM suffix to be used + + Note, if maxlen is less then the len(suffix), you get to keep both pieces. + + """ + name = os.path.basename(kscfg) + idx = name.rfind('.') + if idx >= 0: + name = name[:idx] + + if release is not None: + suffix = "" + if prefix is None: + prefix = "" + if suffix is None: + suffix = time.strftime("%Y%m%d%H%M") + + if name.startswith(prefix): + name = name[len(prefix):] + + prefix = "%s-" % prefix if prefix else "" + suffix = "-%s" % suffix if suffix else "" + + ret = prefix + name + suffix + + return ret + +def find_canned(scripts_path, file_name): + """ + Find a file either by its path or by name in the canned files dir. + + Return None if not found + """ + if os.path.exists(file_name): + return file_name + + layers_canned_wks_dir = wic.engine.build_canned_image_list(scripts_path) + for canned_wks_dir in layers_canned_wks_dir: + for root, dirs, files in os.walk(canned_wks_dir): + for fname in files: + if fname == file_name: + fullpath = os.path.join(canned_wks_dir, fname) + return fullpath + +def get_custom_config(boot_file): + """ + Get the custom configuration to be used for the bootloader. + + Return None if the file can't be found. + """ + scripts_path = os.path.abspath(os.path.dirname(__file__)) + # Get the scripts path of poky + for x in range(0, 3): + scripts_path = os.path.dirname(scripts_path) + + cfg_file = find_canned(scripts_path, boot_file) + if cfg_file: + with open(cfg_file, "r") as f: + config = f.read() + return config + + return None diff --git a/scripts/lib/wic/utils/oe/__init__.py b/scripts/lib/wic/utils/oe/__init__.py new file mode 100644 index 0000000..0a81575 --- /dev/null +++ b/scripts/lib/wic/utils/oe/__init__.py @@ -0,0 +1,22 @@ +# +# OpenEmbedded wic utils library +# +# Copyright (c) 2013, Intel Corporation. +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# 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. +# +# AUTHORS +# Tom Zanussi <tom.zanussi (at] linux.intel.com> +# diff --git a/scripts/lib/wic/utils/oe/misc.py b/scripts/lib/wic/utils/oe/misc.py new file mode 100644 index 0000000..81239ac --- /dev/null +++ b/scripts/lib/wic/utils/oe/misc.py @@ -0,0 +1,250 @@ +# ex:ts=4:sw=4:sts=4:et +# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +# +# Copyright (c) 2013, Intel Corporation. +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# 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. +# +# DESCRIPTION +# This module provides a place to collect various wic-related utils +# for the OpenEmbedded Image Tools. +# +# AUTHORS +# Tom Zanussi <tom.zanussi (at] linux.intel.com> +# +"""Miscellaneous functions.""" + +import os +from collections import defaultdict + +from wic import msger +from wic.utils import runner + +# executable -> recipe pairs for exec_native_cmd +NATIVE_RECIPES = {"mcopy": "mtools", + "mkdosfs": "dosfstools", + "mkfs.btrfs": "btrfs-tools", + "mkfs.ext2": "e2fsprogs", + "mkfs.ext3": "e2fsprogs", + "mkfs.ext4": "e2fsprogs", + "mkfs.vfat": "dosfstools", + "mksquashfs": "squashfs-tools", + "mkswap": "util-linux", + "parted": "parted", + "sgdisk": "gptfdisk", + "syslinux": "syslinux" + } + +def _exec_cmd(cmd_and_args, as_shell=False, catch=3): + """ + Execute command, catching stderr, stdout + + Need to execute as_shell if the command uses wildcards + """ + msger.debug("_exec_cmd: %s" % cmd_and_args) + args = cmd_and_args.split() + msger.debug(args) + + if as_shell: + ret, out = runner.runtool(cmd_and_args, catch) + else: + ret, out = runner.runtool(args, catch) + out = out.strip() + msger.debug("_exec_cmd: output for %s (rc = %d): %s" % \ + (cmd_and_args, ret, out)) + + return (ret, out) + + +def exec_cmd(cmd_and_args, as_shell=False, catch=3): + """ + Execute command, catching stderr, stdout + + Exits if rc non-zero + """ + ret, out = _exec_cmd(cmd_and_args, as_shell, catch) + + if ret != 0: + msger.error("exec_cmd: %s returned '%s' instead of 0" % \ + (cmd_and_args, ret)) + + return out + +def cmd_in_path(cmd, path): + import scriptpath + + scriptpath.add_bitbake_lib_path() + + return bb.utils.which(path, cmd) != "" or False + +def exec_native_cmd(cmd_and_args, native_sysroot, catch=3, pseudo=""): + """ + Execute native command, catching stderr, stdout + + Need to execute as_shell if the command uses wildcards + + Always need to execute native commands as_shell + """ + # The reason -1 is used is because there may be "export" commands. + args = cmd_and_args.split(';')[-1].split() + msger.debug(args) + + if pseudo: + cmd_and_args = pseudo + cmd_and_args + native_paths = \ + "%s/sbin:%s/usr/sbin:%s/usr/bin" % \ + (native_sysroot, native_sysroot, native_sysroot) + native_cmd_and_args = "export PATH=%s:$PATH;%s" % \ + (native_paths, cmd_and_args) + msger.debug("exec_native_cmd: %s" % cmd_and_args) + + # If the command isn't in the native sysroot say we failed. + if cmd_in_path(args[0], native_paths): + ret, out = _exec_cmd(native_cmd_and_args, True, catch) + else: + ret = 127 + + prog = args[0] + # shell command-not-found + if ret == 127 \ + or (pseudo and ret == 1 and out == "Can't find '%s' in $PATH." % prog): + msg = "A native program %s required to build the image "\ + "was not found (see details above).\n\n" % prog + recipe = NATIVE_RECIPES.get(prog) + if recipe: + msg += "Please bake it with 'bitbake %s-native' "\ + "and try again.\n" % recipe + else: + msg += "Wic failed to find a recipe to build native %s. Please "\ + "file a bug against wic.\n" % prog + msger.error(msg) + if out: + msger.debug('"%s" output: %s' % (args[0], out)) + + if ret != 0: + msger.error("exec_cmd: '%s' returned '%s' instead of 0" % \ + (cmd_and_args, ret)) + + return ret, out + +BOOTDD_EXTRA_SPACE = 16384 + +class BitbakeVars(defaultdict): + """ + Container for Bitbake variables. + """ + def __init__(self): + defaultdict.__init__(self, dict) + + # default_image and vars_dir attributes should be set from outside + self.default_image = None + self.vars_dir = None + + def _parse_line(self, line, image): + """ + Parse one line from bitbake -e output or from .env file. + Put result key-value pair into the storage. + """ + if "=" not in line: + return + try: + key, val = line.split("=") + except ValueError: + return + key = key.strip() + val = val.strip() + if key.replace('_', '').isalnum(): + self[image][key] = val.strip('"') + + def get_var(self, var, image=None): + """ + Get bitbake variable from 'bitbake -e' output or from .env file. + This is a lazy method, i.e. it runs bitbake or parses file only when + only when variable is requested. It also caches results. + """ + if not image: + image = self.default_image + + if image not in self: + if image and self.vars_dir: + fname = os.path.join(self.vars_dir, image + '.env') + if os.path.isfile(fname): + # parse .env file + with open(fname) as varsfile: + for line in varsfile: + self._parse_line(line, image) + else: + print "Couldn't get bitbake variable from %s." % fname + print "File %s doesn't exist." % fname + return + else: + # Get bitbake -e output + cmd = "bitbake -e" + if image: + cmd += " %s" % image + + log_level = msger.get_loglevel() + msger.set_loglevel('normal') + ret, lines = _exec_cmd(cmd) + msger.set_loglevel(log_level) + + if ret: + print "Couldn't get '%s' output." % cmd + print "Bitbake failed with error:\n%s\n" % lines + return + + # Parse bitbake -e output + for line in lines.split('\n'): + self._parse_line(line, image) + + # Make first image a default set of variables + images = [key for key in self if key] + if len(images) == 1: + self[None] = self[image] + + return self[image].get(var) + +# Create BB_VARS singleton +BB_VARS = BitbakeVars() + +def get_bitbake_var(var, image=None): + """ + Provide old get_bitbake_var API by wrapping + get_var method of BB_VARS singleton. + """ + return BB_VARS.get_var(var, image) + +def parse_sourceparams(sourceparams): + """ + Split sourceparams string of the form key1=val1[,key2=val2,...] + into a dict. Also accepts valueless keys i.e. without =. + + Returns dict of param key/val pairs (note that val may be None). + """ + params_dict = {} + + params = sourceparams.split(',') + if params: + for par in params: + if not par: + continue + if not '=' in par: + key = par + val = None + else: + key, val = par.split('=') + params_dict[key] = val + + return params_dict diff --git a/scripts/lib/wic/utils/partitionedfs.py b/scripts/lib/wic/utils/partitionedfs.py new file mode 100644 index 0000000..ad596d2 --- /dev/null +++ b/scripts/lib/wic/utils/partitionedfs.py @@ -0,0 +1,360 @@ +#!/usr/bin/env python -tt +# +# Copyright (c) 2009, 2010, 2011 Intel, Inc. +# Copyright (c) 2007, 2008 Red Hat, Inc. +# Copyright (c) 2008 Daniel P. Berrange +# Copyright (c) 2008 David P. Huff +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the Free +# Software Foundation; version 2 of the License +# +# 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., 59 +# Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +import os +from wic import msger +from wic.utils.errors import ImageError +from wic.utils.oe.misc import exec_cmd, exec_native_cmd + +# Overhead of the MBR partitioning scheme (just one sector) +MBR_OVERHEAD = 1 + +# Overhead of the GPT partitioning scheme +GPT_OVERHEAD = 34 + +# Size of a sector in bytes +SECTOR_SIZE = 512 + +class Image(object): + """ + Generic base object for an image. + + An Image is a container for a set of DiskImages and associated + partitions. + """ + def __init__(self, native_sysroot=None): + self.disks = {} + self.partitions = [] + # Size of a sector used in calculations + self.sector_size = SECTOR_SIZE + self._partitions_layed_out = False + self.native_sysroot = native_sysroot + + def __add_disk(self, disk_name): + """ Add a disk 'disk_name' to the internal list of disks. Note, + 'disk_name' is the name of the disk in the target system + (e.g., sdb). """ + + if disk_name in self.disks: + # We already have this disk + return + + assert not self._partitions_layed_out + + self.disks[disk_name] = \ + {'disk': None, # Disk object + 'numpart': 0, # Number of allocate partitions + 'realpart': 0, # Number of partitions in the partition table + 'partitions': [], # Indexes to self.partitions + 'offset': 0, # Offset of next partition (in sectors) + # Minimum required disk size to fit all partitions (in bytes) + 'min_size': 0, + 'ptable_format': "msdos"} # Partition table format + + def add_disk(self, disk_name, disk_obj): + """ Add a disk object which have to be partitioned. More than one disk + can be added. In case of multiple disks, disk partitions have to be + added for each disk separately with 'add_partition()". """ + + self.__add_disk(disk_name) + self.disks[disk_name]['disk'] = disk_obj + + def __add_partition(self, part): + """ This is a helper function for 'add_partition()' which adds a + partition to the internal list of partitions. """ + + assert not self._partitions_layed_out + + self.partitions.append(part) + self.__add_disk(part['disk_name']) + + def add_partition(self, size, disk_name, mountpoint, source_file=None, fstype=None, + label=None, fsopts=None, boot=False, align=None, no_table=False, + part_type=None, uuid=None): + """ Add the next partition. Prtitions have to be added in the + first-to-last order. """ + + ks_pnum = len(self.partitions) + + # Converting kB to sectors for parted + size = size * 1024 / self.sector_size + + part = {'ks_pnum': ks_pnum, # Partition number in the KS file + 'size': size, # In sectors + 'mountpoint': mountpoint, # Mount relative to chroot + 'source_file': source_file, # partition contents + 'fstype': fstype, # Filesystem type + 'fsopts': fsopts, # Filesystem mount options + 'label': label, # Partition label + 'disk_name': disk_name, # physical disk name holding partition + 'device': None, # kpartx device node for partition + 'num': None, # Partition number + 'boot': boot, # Bootable flag + 'align': align, # Partition alignment + 'no_table' : no_table, # Partition does not appear in partition table + 'part_type' : part_type, # Partition type + 'uuid': uuid} # Partition UUID + + self.__add_partition(part) + + def layout_partitions(self, ptable_format="msdos"): + """ Layout the partitions, meaning calculate the position of every + partition on the disk. The 'ptable_format' parameter defines the + partition table format and may be "msdos". """ + + msger.debug("Assigning %s partitions to disks" % ptable_format) + + if self._partitions_layed_out: + return + + self._partitions_layed_out = True + + # Go through partitions in the order they are added in .ks file + for num in range(len(self.partitions)): + part = self.partitions[num] + + if not self.disks.has_key(part['disk_name']): + raise ImageError("No disk %s for partition %s" \ + % (part['disk_name'], part['mountpoint'])) + + if ptable_format == 'msdos' and part['part_type']: + # The --part-type can also be implemented for MBR partitions, + # in which case it would map to the 1-byte "partition type" + # filed at offset 3 of the partition entry. + raise ImageError("setting custom partition type is not " \ + "implemented for msdos partitions") + + # Get the disk where the partition is located + disk = self.disks[part['disk_name']] + disk['numpart'] += 1 + if not part['no_table']: + disk['realpart'] += 1 + disk['ptable_format'] = ptable_format + + if disk['numpart'] == 1: + if ptable_format == "msdos": + overhead = MBR_OVERHEAD + elif ptable_format == "gpt": + overhead = GPT_OVERHEAD + + # Skip one sector required for the partitioning scheme overhead + disk['offset'] += overhead + + if disk['realpart'] > 3: + # Reserve a sector for EBR for every logical partition + # before alignment is performed. + if ptable_format == "msdos": + disk['offset'] += 1 + + + if part['align']: + # If not first partition and we do have alignment set we need + # to align the partition. + # FIXME: This leaves a empty spaces to the disk. To fill the + # gaps we could enlargea the previous partition? + + # Calc how much the alignment is off. + align_sectors = disk['offset'] % (part['align'] * 1024 / self.sector_size) + + if align_sectors: + # If partition is not aligned as required, we need + # to move forward to the next alignment point + align_sectors = (part['align'] * 1024 / self.sector_size) - align_sectors + + msger.debug("Realignment for %s%s with %s sectors, original" + " offset %s, target alignment is %sK." % + (part['disk_name'], disk['numpart'], align_sectors, + disk['offset'], part['align'])) + + # increase the offset so we actually start the partition on right alignment + disk['offset'] += align_sectors + + part['start'] = disk['offset'] + disk['offset'] += part['size'] + + part['type'] = 'primary' + if not part['no_table']: + part['num'] = disk['realpart'] + else: + part['num'] = 0 + + if disk['ptable_format'] == "msdos": + if disk['realpart'] > 3: + part['type'] = 'logical' + part['num'] = disk['realpart'] + 1 + + disk['partitions'].append(num) + msger.debug("Assigned %s to %s%d, sectors range %d-%d size %d " + "sectors (%d bytes)." \ + % (part['mountpoint'], part['disk_name'], part['num'], + part['start'], part['start'] + part['size'] - 1, + part['size'], part['size'] * self.sector_size)) + + # Once all the partitions have been layed out, we can calculate the + # minumim disk sizes. + for disk in self.disks.values(): + disk['min_size'] = disk['offset'] + if disk['ptable_format'] == "gpt": + disk['min_size'] += GPT_OVERHEAD + + disk['min_size'] *= self.sector_size + + def __create_partition(self, device, parttype, fstype, start, size): + """ Create a partition on an image described by the 'device' object. """ + + # Start is included to the size so we need to substract one from the end. + end = start + size - 1 + msger.debug("Added '%s' partition, sectors %d-%d, size %d sectors" % + (parttype, start, end, size)) + + cmd = "parted -s %s unit s mkpart %s" % (device, parttype) + if fstype: + cmd += " %s" % fstype + cmd += " %d %d" % (start, end) + + return exec_native_cmd(cmd, self.native_sysroot) + + def __format_disks(self): + self.layout_partitions() + + for dev in self.disks.keys(): + disk = self.disks[dev] + msger.debug("Initializing partition table for %s" % \ + (disk['disk'].device)) + exec_native_cmd("parted -s %s mklabel %s" % \ + (disk['disk'].device, disk['ptable_format']), + self.native_sysroot) + + msger.debug("Creating partitions") + + for part in self.partitions: + if part['num'] == 0: + continue + + disk = self.disks[part['disk_name']] + if disk['ptable_format'] == "msdos" and part['num'] == 5: + # Create an extended partition (note: extended + # partition is described in MBR and contains all + # logical partitions). The logical partitions save a + # sector for an EBR just before the start of a + # partition. The extended partition must start one + # sector before the start of the first logical + # partition. This way the first EBR is inside of the + # extended partition. Since the extended partitions + # starts a sector before the first logical partition, + # add a sector at the back, so that there is enough + # room for all logical partitions. + self.__create_partition(disk['disk'].device, "extended", + None, part['start'] - 1, + disk['offset'] - part['start'] + 1) + + if part['fstype'] == "swap": + parted_fs_type = "linux-swap" + elif part['fstype'] == "vfat": + parted_fs_type = "fat32" + elif part['fstype'] == "msdos": + parted_fs_type = "fat16" + elif part['fstype'] == "ontrackdm6aux3": + parted_fs_type = "ontrackdm6aux3" + else: + # Type for ext2/ext3/ext4/btrfs + parted_fs_type = "ext2" + + # Boot ROM of OMAP boards require vfat boot partition to have an + # even number of sectors. + if part['mountpoint'] == "/boot" and part['fstype'] in ["vfat", "msdos"] \ + and part['size'] % 2: + msger.debug("Substracting one sector from '%s' partition to " \ + "get even number of sectors for the partition" % \ + part['mountpoint']) + part['size'] -= 1 + + self.__create_partition(disk['disk'].device, part['type'], + parted_fs_type, part['start'], part['size']) + + if part['part_type']: + msger.debug("partition %d: set type UID to %s" % \ + (part['num'], part['part_type'])) + exec_native_cmd("sgdisk --typecode=%d:%s %s" % \ + (part['num'], part['part_type'], + disk['disk'].device), self.native_sysroot) + + if part['uuid']: + msger.debug("partition %d: set UUID to %s" % \ + (part['num'], part['uuid'])) + exec_native_cmd("sgdisk --partition-guid=%d:%s %s" % \ + (part['num'], part['uuid'], disk['disk'].device), + self.native_sysroot) + + if part['boot']: + flag_name = "legacy_boot" if disk['ptable_format'] == 'gpt' else "boot" + msger.debug("Set '%s' flag for partition '%s' on disk '%s'" % \ + (flag_name, part['num'], disk['disk'].device)) + exec_native_cmd("parted -s %s set %d %s on" % \ + (disk['disk'].device, part['num'], flag_name), + self.native_sysroot) + + # Parted defaults to enabling the lba flag for fat16 partitions, + # which causes compatibility issues with some firmware (and really + # isn't necessary). + if parted_fs_type == "fat16": + if disk['ptable_format'] == 'msdos': + msger.debug("Disable 'lba' flag for partition '%s' on disk '%s'" % \ + (part['num'], disk['disk'].device)) + exec_native_cmd("parted -s %s set %d lba off" % \ + (disk['disk'].device, part['num']), + self.native_sysroot) + + def cleanup(self): + if self.disks: + for dev in self.disks: + disk = self.disks[dev] + try: + disk['disk'].cleanup() + except: + pass + + def assemble(self, image_file): + msger.debug("Installing partitions") + + for part in self.partitions: + source = part['source_file'] + if source: + # install source_file contents into a partition + cmd = "dd if=%s of=%s bs=%d seek=%d count=%d conv=notrunc" % \ + (source, image_file, self.sector_size, + part['start'], part['size']) + exec_cmd(cmd) + + msger.debug("Installed %s in partition %d, sectors %d-%d, " + "size %d sectors" % \ + (source, part['num'], part['start'], + part['start'] + part['size'] - 1, part['size'])) + + os.rename(source, image_file + '.p%d' % part['num']) + + def create(self): + for dev in self.disks.keys(): + disk = self.disks[dev] + disk['disk'].create() + + self.__format_disks() + + return diff --git a/scripts/lib/wic/utils/runner.py b/scripts/lib/wic/utils/runner.py new file mode 100644 index 0000000..7431917 --- /dev/null +++ b/scripts/lib/wic/utils/runner.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python -tt +# +# Copyright (c) 2011 Intel, Inc. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the Free +# Software Foundation; version 2 of the License +# +# 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., 59 +# Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +import os +import subprocess + +from wic import msger + +def runtool(cmdln_or_args, catch=1): + """ wrapper for most of the subprocess calls + input: + cmdln_or_args: can be both args and cmdln str (shell=True) + catch: 0, quitely run + 1, only STDOUT + 2, only STDERR + 3, both STDOUT and STDERR + return: + (rc, output) + if catch==0: the output will always None + """ + + if catch not in (0, 1, 2, 3): + # invalid catch selection, will cause exception, that's good + return None + + if isinstance(cmdln_or_args, list): + cmd = cmdln_or_args[0] + shell = False + else: + import shlex + cmd = shlex.split(cmdln_or_args)[0] + shell = True + + if catch != 3: + dev_null = os.open("/dev/null", os.O_WRONLY) + + if catch == 0: + sout = dev_null + serr = dev_null + elif catch == 1: + sout = subprocess.PIPE + serr = dev_null + elif catch == 2: + sout = dev_null + serr = subprocess.PIPE + elif catch == 3: + sout = subprocess.PIPE + serr = subprocess.STDOUT + + try: + process = subprocess.Popen(cmdln_or_args, stdout=sout, + stderr=serr, shell=shell) + (sout, serr) = process.communicate() + # combine stdout and stderr, filter None out + out = ''.join(filter(None, [sout, serr])) + except OSError, err: + if err.errno == 2: + # [Errno 2] No such file or directory + msger.error('Cannot run command: %s, lost dependency?' % cmd) + else: + raise # relay + finally: + if catch != 3: + os.close(dev_null) + + return (process.returncode, out) + +def show(cmdln_or_args): + # show all the message using msger.verbose + + rcode, out = runtool(cmdln_or_args, catch=3) + + if isinstance(cmdln_or_args, list): + cmd = ' '.join(cmdln_or_args) + else: + cmd = cmdln_or_args + + msg = 'running command: "%s"' % cmd + if out: + out = out.strip() + if out: + msg += ', with output::' + msg += '\n +----------------' + for line in out.splitlines(): + msg += '\n | %s' % line + msg += '\n +----------------' + + msger.verbose(msg) + return rcode + +def outs(cmdln_or_args, catch=1): + # get the outputs of tools + return runtool(cmdln_or_args, catch)[1].strip() + +def quiet(cmdln_or_args): + return runtool(cmdln_or_args, catch=0)[0] diff --git a/scripts/lib/wic/utils/syslinux.py b/scripts/lib/wic/utils/syslinux.py new file mode 100644 index 0000000..aace286 --- /dev/null +++ b/scripts/lib/wic/utils/syslinux.py @@ -0,0 +1,58 @@ +# ex:ts=4:sw=4:sts=4:et +# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the Free +# Software Foundation; version 2 of the License +# +# 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., 59 +# Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# AUTHOR +# Adrian Freihofer <adrian.freihofer (at] neratec.com> + + +import re +from wic import msger + + +def serial_console_form_kargs(kernel_args): + """ + Create SERIAL... line from kernel parameters + + syslinux needs a line SERIAL port [baudrate [flowcontrol]] + in the syslinux.cfg file. The config line is generated based + on kernel boot parameters. The the parameters of the first + ttyS console are considered for syslinux config. + @param kernel_args kernel command line + @return line for syslinux config file e.g. "SERIAL 0 115200" + """ + syslinux_conf = "" + for param in kernel_args.split(): + param_match = re.match("console=ttyS([0-9]+),?([0-9]*)([noe]?)([0-9]?)(r?)", param) + if param_match: + syslinux_conf += "SERIAL " + param_match.group(1) + # baudrate + if param_match.group(2): + syslinux_conf += " " + param_match.group(2) + # parity + if param_match.group(3) and param_match.group(3) != 'n': + msger.warning("syslinux does not support parity for console. {} is ignored." + .format(param_match.group(3))) + # number of bits + if param_match.group(4) and param_match.group(4) != '8': + msger.warning("syslinux supports 8 bit console configuration only. {} is ignored." + .format(param_match.group(4))) + # flow control + if param_match.group(5) and param_match.group(5) != '': + msger.warning("syslinux console flowcontrol configuration. {} is ignored." + .format(param_match.group(5))) + break + + return syslinux_conf |