def main(): common.check_filesystem() message.sub_info('Detecting available sessions') xsession = None for sfile in misc.list_files(misc.join_paths(config.FILESYSTEM_DIR, \ 'usr/share/xsessions')): if sfile.endswith('.desktop'): xsession = common.get_value(sfile, 'Exec=') message.sub_debug('Session detected', xsession) if not xsession: raise (message.exception('No session avaialable')) # FIXME: race condition between session and Xephyr - if session # starts before Xephyr it fails saying it does not find the DISPLAY message.sub_info('Starting Xephyr') x = subprocess.Popen((misc.whereis('Xephyr'), '-ac', '-screen', \ config.RESOLUTION, '-br', ':13')) x.poll() if x.returncode > 0: raise (message.exception('Failed to start Xephyr', x.returncode)) try: message.sub_info('Allowing local access to X-server') misc.system_command((misc.whereis('xhost'), '+local:13')) message.sub_info('Starting nested X session', xsession) misc.chroot_exec((xsession), xnest=True) message.sub_info('Blocking local access to X-server') misc.system_command((misc.whereis('xhost'), '-local:13')) finally: message.sub_info('Terminating Xephyr') x.terminate()
def main(): common.check_filesystem() message.sub_info('Detecting available sessions') xsession = None for sfile in misc.list_files(misc.join_paths(config.FILESYSTEM_DIR, \ 'usr/share/xsessions')): if sfile.endswith('.desktop'): xsession = common.get_value(sfile, 'Exec=') message.sub_debug('Session detected', xsession) if not xsession: raise(message.exception('No session available')) # FIXME: race condition between session and Xephyr - if session # starts before Xephyr it fails saying it does not find the DISPLAY message.sub_info('Starting Xephyr') x = subprocess.Popen((misc.whereis('Xephyr'), '-ac', '-screen', \ config.RESOLUTION, '-br', ':13')) x.poll() if x.returncode is not None and x.returncode > 0: raise(message.exception('Failed to start Xephyr', x.returncode)) try: message.sub_info('Allowing local access to X-server') misc.system_command((misc.whereis('xhost'), '+local:13')) message.sub_info('Starting nested X session', xsession) misc.chroot_exec((xsession), xnest=True) message.sub_info('Blocking local access to X-server') misc.system_command((misc.whereis('xhost'), '-local:13')) finally: message.sub_info('Terminating Xephyr') x.terminate()
def main(): common.check_filesystem() message.sub_info('Gathering information') arch = misc.chroot_exec(('dpkg', '--print-architecture'), prepare=False, \ mount=False, output=True) distrib = common.get_value(config.FILESYSTEM_DIR + '/etc/lsb-release', \ 'DISTRIB_ID=') release = common.get_value(config.FILESYSTEM_DIR + '/etc/lsb-release', \ 'DISTRIB_RELEASE=') message.sub_debug('Architecture', arch) message.sub_debug('Distribution (DISTRIB_ID)', distrib) message.sub_debug('Release (DISTRIB_RELEASE)', release) if isinstance(arch, bytes): # For some reason this is of type 'bytes'. if int(sys.version_info[0]) >= 3: # If we're running under python3 arch = str(arch, 'utf-8') else: # Otherwise just cast it to a str without the 'utf-8' option. arch = str(arch) iso_file = '%s/%s-%s-%s.iso' % (config.WORK_DIR, distrib, arch, release) if not os.path.exists(iso_file): raise(message.exception('ISO image does not exist', iso_file)) message.sub_info('Running QEMU with ISO image', iso_file) host_arch = os.uname()[4] if host_arch == 'x86_64': qemu = misc.whereis('qemu-system-x86_64') else: qemu = misc.whereis('qemu-system-i386') if not qemu: raise(message.exception('QEMU is not installed')) qemu_kvm = False command = [qemu, '-m', config.VRAM, '-cdrom', iso_file] if misc.search_string('-enable-kvm', misc.get_output((qemu, '-h'))): qemu_kvm = True # CPU flag: "vmx" for Intel processors, "svm" for AMD processors # These flags are hidden in Xen environment # https://wiki.xenproject.org/wiki/Xen_Common_Problems host_kvm = False if os.path.exists('/dev/kvm') and os.path.exists('/proc/cpuinfo') and \ misc.search_file('(?:\\s|^)flags.*(?:\\s)(vmx|svm)(?:\\s|$)', \ '/proc/cpuinfo', escape=False): host_kvm = True if qemu_kvm and host_kvm: command.append('-enable-kvm') message.sub_debug('Host architecture', host_arch) message.sub_debug('QEMU KVM', qemu_kvm) message.sub_debug('Host KVM', host_kvm) misc.system_command(command)
def main(): common.check_filesystem() pkgmngr = None for sfile in ('aptitude', 'aptitude-curses', 'synaptic', 'apper'): for sdir in ('bin', 'sbin', 'usr/bin', 'usr/sbin'): full_file = misc.join_paths(config.FILESYSTEM_DIR, sdir, sfile) if os.path.exists(full_file) and os.access(full_file, os.X_OK): pkgmngr = misc.join_paths(sdir, sfile) message.sub_debug('Package manager detected', sfile) if not pkgmngr: raise(message.exception('No package manager available')) if not pkgmngr.startswith('aptitude'): try: message.sub_info('Allowing local access to X-server') misc.system_command((misc.whereis('xhost'), '+local:')) message.sub_info('Executing package manager') misc.chroot_exec((pkgmngr)) finally: message.sub_info('Blocking local access to X-server') misc.system_command((misc.whereis('xhost'), '-local:')) else: message.sub_info('Executing package manager') misc.chroot_exec((pkgmngr))
def main(): common.check_filesystem() pkgmngr = None for sfile in ('aptitude', 'aptitude-curses', 'synaptic', 'apper'): for sdir in ('bin', 'sbin', 'usr/bin', 'usr/sbin'): full_file = misc.join_paths(config.FILESYSTEM_DIR, sdir, sfile) if os.path.exists(full_file) and os.access(full_file, os.X_OK): pkgmngr = misc.join_paths(sdir, sfile) message.sub_debug('Package manager detected', sfile) if not pkgmngr: raise (message.exception('No package manager available')) if not pkgmngr.startswith('aptitude'): try: message.sub_info('Allowing local access to X-server') misc.system_command((misc.whereis('xhost'), '+local:')) message.sub_info('Executing package manager') misc.chroot_exec((pkgmngr)) finally: message.sub_info('Blocking local access to X-server') misc.system_command((misc.whereis('xhost'), '-local:')) else: message.sub_info('Executing package manager') misc.chroot_exec((pkgmngr))
def main(): common.check_filesystem() message.sub_info('Gathering information') arch = misc.chroot_exec(('dpkg', '--print-architecture'), prepare=False, \ mount=False, output=True) distrib = common.get_value(config.FILESYSTEM_DIR + '/etc/lsb-release', \ 'DISTRIB_ID=') release = common.get_value(config.FILESYSTEM_DIR + '/etc/lsb-release', \ 'DISTRIB_RELEASE=') message.sub_debug('Architecture', arch) message.sub_debug('Distribution (DISTRIB_ID)', distrib) message.sub_debug('Release (DISTRIB_RELEASE)', release) iso_file = '%s/%s-%s-%s.iso' % (config.WORK_DIR, distrib, arch, release) if not os.path.exists(iso_file): raise(message.exception('ISO image does not exist', iso_file)) message.sub_info('Running QEMU with ISO image', iso_file) host_arch = os.uname()[4] if host_arch == 'x86_64': qemu = misc.whereis('qemu-system-x86_64') else: qemu = misc.whereis('qemu-system-i386') if not qemu: raise(message.exception('QEMU is not installed')) qemu_kvm = False command = [qemu, '-m', config.VRAM, '-cdrom', iso_file] if misc.search_string('-enable-kvm', misc.get_output((qemu, '-h'))): qemu_kvm = True # CPU flag: "vmx" for Intel processors, "svm" for AMD processors # These flags are hidden in Xen environment # https://wiki.xenproject.org/wiki/Xen_Common_Problems host_kvm = False if os.path.exists('/dev/kvm') and os.path.exists('/proc/cpuinfo') and \ misc.search_file('(?:\\s|^)flags.*(?:\\s)(vmx|svm)(?:\\s|$)', \ '/proc/cpuinfo', escape=False): host_kvm = True if qemu_kvm and host_kvm: command.append('-enable-kvm') message.sub_debug('Host architecture', host_arch) message.sub_debug('QEMU KVM', qemu_kvm) message.sub_debug('Host KVM', host_kvm) misc.system_command(command)
def main(): common.check_filesystem() message.sub_info('Gathering information') arch = misc.chroot_exec(('dpkg', '--print-architecture'), prepare=False, \ mount=False, output=True) distrib = common.get_value(config.FILESYSTEM_DIR + '/etc/lsb-release', \ 'DISTRIB_ID=') release = common.get_value(config.FILESYSTEM_DIR + '/etc/lsb-release', \ 'DISTRIB_RELEASE=') message.sub_debug('Architecture', arch) message.sub_debug('Distribution (DISTRIB_ID)', distrib) message.sub_debug('Release (DISTRIB_RELEASE)', release) iso_file = '%s/%s-%s-%s.iso' % (config.WORK_DIR, distrib, arch, release) if not os.path.exists(iso_file): raise (message.exception('ISO image does not exist', iso_file)) message.sub_info('Running QEMU with ISO image', iso_file) host_arch = os.uname()[4] if host_arch == 'x86_64': qemu = misc.whereis('qemu-system-x86_64') else: qemu = misc.whereis('qemu-system-i386') if not qemu: raise (message.exception('QEMU is not installed')) qemu_kvm = False command = [qemu, '-m', config.VRAM, '-cdrom', iso_file] if misc.search_string('-enable-kvm', misc.get_output((qemu, '-h'))): qemu_kvm = True # vmx for intel, svm for amd processors. it will most likely not work in # XEN environment host_kvm = False if os.path.exists('/dev/kvm') and os.path.exists('/proc/cpuinfo') and \ misc.search_file('(?:\\s|^)flags.*(?:\\s)(vme|vmx)(?:\\s|$)', \ '/proc/cpuinfo', escape=False): host_kvm = True if qemu_kvm and host_kvm: command.append('-enable-kvm') message.sub_debug('Host architecture', host_arch) message.sub_debug('QEMU KVM', qemu_kvm) message.sub_debug('Host KVM', host_kvm) misc.system_command(command)
def main(): common.check_filesystem() if not os.path.isfile(config.DEB): raise(message.exception('DEB does not exists', config.DEB)) elif not config.DEB.endswith('.deb'): raise(message.exception('File is not DEB', config.DEB)) message.sub_info('Copying DEB file') deb_file = misc.join_paths(config.FILESYSTEM_DIR, 'temp.deb') if os.path.isfile(deb_file): message.sub_debug('Removing', deb_file) os.unlink(deb_file) misc.copy_file(config.DEB, deb_file) message.sub_info('Installing DEB') misc.chroot_exec(('dpkg', '-i', '/temp.deb')) message.sub_info('Installing dependencies') misc.chroot_exec(('apt-get', 'install', '-f', '-y'))
def main(): common.check_filesystem() if not os.path.isfile(config.DEB): raise (message.exception('DEB does not exists', config.DEB)) elif not config.DEB.endswith('.deb'): raise (message.exception('File is not DEB', config.DEB)) message.sub_info('Copying DEB file') deb_file = misc.join_paths(config.FILESYSTEM_DIR, 'temp.deb') if os.path.isfile(deb_file): message.sub_debug('Removing', deb_file) os.unlink(deb_file) misc.copy_file(config.DEB, deb_file) message.sub_info('Installing DEB') misc.chroot_exec(('dpkg', '-i', '/temp.deb')) message.sub_info('Installing dependencies') misc.chroot_exec(('apt-get', 'install', '-f', '-y'))
def check_filesystem(): message.sub_info('Checking') corrupted = False for sdir in ('bin', 'sbin', 'usr/bin', 'usr/sbin', 'etc', 'lib', 'usr/lib'): full_dir = misc.join_paths(config.FILESYSTEM_DIR, sdir) if not os.path.isdir(full_dir): message.sub_debug('Non-existing path', full_dir) corrupted = True break if corrupted: raise(message.exception('Filesystem is missing or corrupted'))
def check_filesystem(): message.sub_info('Checking') corrupted = False for sdir in ('bin', 'sbin', 'usr/bin', 'usr/sbin', 'etc', 'lib', 'usr/lib'): full_dir = misc.join_paths(config.FILESYSTEM_DIR, sdir) if not os.path.isdir(full_dir): message.sub_debug('Non-existing path', full_dir) corrupted = True break if corrupted: raise (message.exception('Filesystem is missing or corrupted'))
def main(): common.check_filesystem() if not os.path.isfile(config.HOOK): raise(message.exception('HOOK does not exists', config.HOOK)) message.sub_info('Copying HOOK file') hook_file = misc.join_paths(config.FILESYSTEM_DIR, 'hook') if os.path.isfile(hook_file): message.sub_debug('Removing', hook_file) os.unlink(hook_file) misc.copy_file(config.HOOK, hook_file) message.sub_info('Making HOOK executable') os.chmod(hook_file, stat.S_IEXEC) message.sub_info('Running HOOK') misc.chroot_exec(('/hook'))
def main(): common.check_filesystem() if not os.path.isfile(config.HOOK): raise (message.exception("HOOK does not exists", config.HOOK)) message.sub_info("Copying HOOK file") hook_file = misc.join_paths(config.FILESYSTEM_DIR, "hook") if os.path.isfile(hook_file): message.sub_debug("Removing", hook_file) os.unlink(hook_file) misc.copy_file(config.HOOK, hook_file) try: message.sub_info("Making HOOK executable") os.chmod(hook_file, stat.S_IEXEC) message.sub_info("Running HOOK") misc.chroot_exec(("/hook")) finally: if os.path.isfile(hook_file): os.unlink(hook_file)
def main(): common.check_filesystem() # Basic sanity checks of files and paths that absolutely need to exist. message.sub_info('Doing sanity checks') lsb_file = misc.join_paths(config.FILESYSTEM_DIR, 'etc/lsb-release') if not os.path.isfile(lsb_file): raise(message.exception(lsb_file + ' does not exist')) isolinux_dir = misc.join_paths(config.ISO_DIR, 'isolinux') if not os.path.isdir(isolinux_dir): raise(message.exception(isolinux_dir + ' does not exist')) if misc.search_file('999:999', misc.join_paths(config.FILESYSTEM_DIR, 'etc/passwd')): raise(message.exception('User with UID 999 exists, this means automatic login will fail')) elif misc.search_file('999:999', misc.join_paths(config.FILESYSTEM_DIR, 'etc/group')): raise(message.exception('Group with GID 999 exists, this means automatic login will fail')) casper_dir = misc.join_paths(config.ISO_DIR, 'casper') if not os.path.isdir(casper_dir): message.sub_debug('Creating', casper_dir) os.makedirs(casper_dir) base_file = misc.join_paths(config.ISO_DIR, '.disk/base_installable') if os.path.isfile(misc.join_paths(config.FILESYSTEM_DIR, 'usr/bin/ubiquity')): if not os.path.isfile(base_file): message.sub_debug('Creating', base_file) misc.write_file(base_file, '') elif os.path.isfile(base_file): message.sub_debug('Removing', base_file) os.unlink(base_file) # Acquire distribution information from the FileSystem message.sub_info('Gathering information') arch = misc.chroot_exec(('dpkg', '--print-architecture'), prepare=False, \ mount=False, output=True) distrib = common.get_value(config.FILESYSTEM_DIR + '/etc/lsb-release', \ 'DISTRIB_ID=') release = common.get_value(config.FILESYSTEM_DIR + '/etc/lsb-release', \ 'DISTRIB_RELEASE=') message.sub_debug('Architecture', arch) message.sub_debug('Distribution (DISTRIB_ID)', distrib) message.sub_debug('Release (DISTRIB_RELEASE)', release) # Remove files, by name, that we know we must repopulate if they exist. message.sub_info('Cleaning up') cleanup_files = ['casper/filesystem.squashfs', 'casper/initrd.lz', \ 'casper/vmlinuz', 'casper/vmlinuz.efi', 'casper/filesystem.manifest', \ 'casper/filesystem.size'] cleanup_files.extend(glob.glob('.disk/casper-uuid-*')) for sfile in cleanup_files: full_file = misc.join_paths(config.ISO_DIR, sfile) if os.path.exists(full_file): message.sub_debug('Removing', full_file) os.unlink(full_file) # Define the checksum files, and the ISO filename. md5sum_iso_file = misc.join_paths(config.WORK_DIR, 'md5sum') sha1sum_iso_file = misc.join_paths(config.WORK_DIR, 'sha1sum') sha256sum_iso_file = misc.join_paths(config.WORK_DIR, 'sha256sum') iso_file = '%s/%s-%s-%s.iso' % (config.WORK_DIR, distrib, arch, release) if os.path.exists(iso_file): message.sub_debug('Removing', iso_file) os.unlink(iso_file) if os.path.exists(md5sum_iso_file): message.sub_debug('Removing', md5sum_iso_file) os.unlink(md5sum_iso_file) if os.path.exists(sha1sum_iso_file): message.sub_debug('Removing', sha1sum_iso_file) os.unlink(sha1sum_iso_file) if os.path.exists(sha256sum_iso_file): message.sub_debug('Removing', sha256sum_iso_file) os.unlink(sha256sum_iso_file) # Detect files needed for booting, the kernel, initramfs, xen and anything else. detect_boot() if not vmlinuz: message.sub_info('Re-installing kernel') misc.chroot_exec(('apt-get', 'purge', '--yes', 'linux-image*', '-q')) misc.chroot_exec(('apt-get', 'install', '--yes', \ 'linux-image-generic', '-q')) misc.chroot_exec(('apt-get', 'clean')) else: message.sub_info('Updating initramfs') misc.chroot_exec(('update-initramfs', '-k', 'all', '-t', '-u')) detect_boot() if not initrd or not vmlinuz: raise(message.exception('Missing boot file (initrd or vmlinuz)')) else: message.sub_info('Copying boot files') message.sub_debug('Initrd', initrd) message.sub_debug('Vmlinuz', vmlinuz) misc.copy_file(initrd, misc.join_paths(config.ISO_DIR, 'casper/initrd.lz')) # FIXME: extend to support grub efi_boot_entry = False isolinux_dir = config.ISO_DIR + '/isolinux' if os.path.isdir(isolinux_dir): for sfile in os.listdir(isolinux_dir): if sfile.endswith('.cfg') and misc.search_file('vmlinuz.efi', isolinux_dir + '/' + sfile): message.sub_debug('Found EFI entry in isolinux conf', sfile) efi_boot_entry = True if os.path.isdir(misc.join_paths(config.ISO_DIR, 'efi/boot')) or \ efi_boot_entry: message.sub_debug('Copying EFI vmlinuz') misc.copy_file(vmlinuz, misc.join_paths(config.ISO_DIR, \ 'casper/vmlinuz.efi')) os.link(misc.join_paths(config.ISO_DIR, \ 'casper/vmlinuz.efi'), misc.join_paths(config.ISO_DIR, \ 'casper/vmlinuz')) # EFI Kernels are still loadable by grub, modern ISOs lack a bare vmlinuz. # mkisofs/genisoimage -cache-inodes reuses hard linked inodes. else: misc.copy_file(vmlinuz, misc.join_paths(config.ISO_DIR, 'casper/vmlinuz')) # We only need to copy the bare kernel if we're not using EFI at all. # Copy optional boot-enablement packages onto the ISO, if found. if mt86plus: message.sub_debug('Memtest86+ kernel', mt86plus) misc.copy_file(mt86plus, misc.join_paths(config.ISO_DIR, 'install/mt86plus')) if xen_kernel: message.sub_debug('Xen kernel', xen_kernel) misc.copy_file(xen_kernel, \ misc.join_paths(config.ISO_DIR, 'casper/' + os.path.basename(xen_kernel))) if xen_efi: message.sub_debug('Xen EFI kernel', xen_efi) misc.copy_file(xen_efi, \ misc.join_paths(config.ISO_DIR, 'casper/' + os.path.basename(xen_efi))) if ipxe_kernel: message.sub_debug('iPXE kernel', ipxe_kernel) misc.copy_file(ipxe_kernel, \ misc.join_paths(config.ISO_DIR, 'casper/' + os.path.basename(ipxe_kernel))) if ipxe_efi: message.sub_debug('iPXE EFI kernel', ipxe_efi) misc.copy_file(ipxe_efi, \ misc.join_paths(config.ISO_DIR, 'casper/' + os.path.basename(ipxe_efi))) message.sub_info('Extracting casper UUID') confdir = config.FILESYSTEM_DIR + '/conf' if os.path.isdir(confdir): shutil.rmtree(confdir) os.makedirs(confdir) try: misc.chroot_exec('zcat ' + initrd.replace(config.FILESYSTEM_DIR, '') + ' | cpio --quiet -id conf/uuid.conf', \ shell=True, cwd=config.FILESYSTEM_DIR) kernel = re.search('initrd.img-*.*.*-*-(.*)', initrd).group(1) message.sub_debug('Kernel', kernel) misc.copy_file(confdir + '/uuid.conf', misc.join_paths(config.ISO_DIR, \ '.disk/casper-uuid-' + kernel)) finally: shutil.rmtree(confdir) # Define some default compression parameters, including a 1MB blocksize for all compressors. compression_parameters = ('-b', '1048576', '-comp', config.COMPRESSION) if config.COMPRESSION == 'xz': # Append additional compression parameters for xz. # Using the branch-call-jump filter provides a compression boost with executable code. # This can save a hundred megabytes easily, on an 800MB ISO. The dictionary size must # match the block size, and it's advisable to use larger block sizes, like 1MB or 4MB. compression_parameters += ('-Xbcj', 'x86', '-Xdict-size', '100%') message.sub_info('SquashFS Compression parameters', compression_parameters) # Create the compressed filesystem message.sub_info('Creating SquashFS compressed filesystem') make_squash_fs = ('mksquashfs', config.FILESYSTEM_DIR, \ misc.join_paths(config.ISO_DIR, 'casper/filesystem.squashfs'), \ '-wildcards', '-no-recovery', '-noappend', \ '-ef', os.path.join(sys.prefix, 'share/customizer/exclude.list')) misc.system_command(make_squash_fs + compression_parameters) message.sub_info('Checking SquashFS filesystem size') sfs_size = os.path.getsize(misc.join_paths(config.ISO_DIR, \ 'casper/filesystem.squashfs')) message.sub_debug('SquashFS filesystem size', sfs_size) if sfs_size > 4000000000: raise(message.exception('The SquashFS filesystem size is greater than 4GB')) message.sub_info('Creating filesystem.size') fs_size = 0 for root, subdirs, files in os.walk(config.FILESYSTEM_DIR): for sfile in files: sfull = os.path.join(root, sfile) if os.path.islink(sfull): continue # FIXME: respect ignored files from exclude.list fs_size += os.path.getsize(sfull) message.sub_debug('Root filesystem size', fs_size) misc.write_file(misc.join_paths(config.ISO_DIR, \ 'casper/filesystem.size'), str(fs_size)) message.sub_info('Creating filesystem.manifest') lpackages = misc.chroot_exec(('dpkg-query', '-W', \ '--showformat=${Package} ${Version}\\n'), prepare=False, mount=False, \ output=True) message.sub_debug('Packages', lpackages) misc.write_file(misc.join_paths(config.ISO_DIR, \ 'casper/filesystem.manifest'), lpackages) # FIXME: do some kung-fu to check if packages are installed # and remove them from filesystem.manifest-remove if they are not # Creating a md5sum.txt file fixes lubuntu's integrity check. md5sums_file = misc.join_paths(config.ISO_DIR, 'md5sum.txt') if os.path.isfile(md5sums_file): message.sub_info('Creating md5sum.txt') misc.write_file(md5sums_file, '') for sfile in misc.list_files(config.ISO_DIR): if sfile.endswith('md5sum.txt'): continue if sfile.endswith('SHA256SUMS'): continue message.sub_debug('MD5 Checksumming', sfile) checksum = misc.generate_hash_for_file('md5', sfile) misc.append_file(md5sums_file, checksum + ' .' + \ sfile.replace(config.ISO_DIR, '') +'\n') # Creating a SHA256SUMS file fixes ubuntu-mini-remix's integrity check. shasums_file = misc.join_paths(config.ISO_DIR, 'SHA256SUMS') if os.path.isfile(shasums_file): message.sub_info('Creating SHA256SUMS') misc.write_file(shasums_file, '') for sfile in misc.list_files(config.ISO_DIR): if sfile.endswith('md5sum.txt'): continue if sfile.endswith('SHA256SUMS'): continue message.sub_debug('SHA256 Checksumming', sfile) checksum = misc.generate_hash_for_file('sha256', sfile) misc.append_file(shasums_file, checksum + ' .' + \ sfile.replace(config.ISO_DIR, '') +'\n') # Create the ISO filesystem message.sub_info('Creating ISO') os.chdir(config.ISO_DIR) misc.system_command(('xorriso', '-as', 'mkisofs', '-r', '-V', \ distrib + '-' + arch + '-' + release, '-b', 'isolinux/isolinux.bin', \ '-c', 'isolinux/boot.cat', '-J', '-l', '-no-emul-boot', \ '-boot-load-size', '4', '-boot-info-table', '-o', iso_file, \ '-cache-inodes', '-input-charset', 'utf-8', '.')) message.sub_info('Creating ISO checksums') md5checksum = misc.generate_hash_for_file('md5', iso_file) message.sub_info('ISO md5 checksum', md5checksum) misc.append_file(md5sum_iso_file, md5checksum + ' .' + \ iso_file.replace(config.WORK_DIR, '') +'\n') sha1checksum = misc.generate_hash_for_file('sha1', iso_file) message.sub_info('ISO sha1 checksum', sha1checksum) misc.append_file(sha1sum_iso_file, sha1checksum + ' .' + \ iso_file.replace(config.WORK_DIR, '') +'\n') sha256checksum = misc.generate_hash_for_file('sha256', iso_file) message.sub_info('ISO sha256 checksum', sha256checksum) misc.append_file(sha256sum_iso_file, sha256checksum + ' .' + \ iso_file.replace(config.WORK_DIR, '') +'\n') message.sub_info('Successfuly created ISO image', iso_file)
def main(): global mount_dir if not os.path.isfile(config.ISO): raise(message.exception('ISO does not exist', config.ISO)) elif not config.ISO.endswith('.iso'): raise(message.exception('File is not ISO', config.ISO)) common.clean_work_dirs() common.create_work_dirs() message.sub_info('Creating mount directory') mount_dir = tempfile.mkdtemp(prefix=config.MOUNT_DIR + '/') message.sub_debug('Mount directory is', mount_dir) message.sub_info('Mounting ISO', config.ISO) try: # load the required kernel modules in cases the are not, if they are # builtin the command fails so ignoring that. the "-b" argument is to # ensure blacklisted modules are respected try: misc.system_command((misc.whereis('modprobe'), '-ba', 'loop', 'iso9660')) except: pass misc.system_command((misc.whereis('mount'), '-t', 'iso9660', '-o', \ 'ro,loop', config.ISO, mount_dir)) except: message.sub_info('Removing', mount_dir) os.rmdir(mount_dir) common.clean_work_dirs() raise message.sub_info('Checking ISO') for spath in (mount_dir + '/casper/filesystem.squashfs', \ mount_dir + '/casper/filesystem.manifest', \ mount_dir + '/casper/filesystem.manifest-remove', \ mount_dir + '/.disk', mount_dir + '/isolinux', ): if not os.path.exists(spath): message.sub_debug('Non-existing path', spath) common.clean_work_dirs() unmount_iso() raise(message.exception('Invalid ISO', config.ISO)) message.sub_info('Unsquashing filesystem') try: misc.system_command((misc.whereis('unsquashfs'), '-f', '-d', \ config.FILESYSTEM_DIR, mount_dir + '/casper/filesystem.squashfs')) except: unmount_iso() raise message.sub_info('Checking architecture') fs_arch = misc.chroot_exec(('dpkg', '--print-architecture'), \ prepare=False, mount=False, output=True) host_arch = os.uname()[4] message.sub_debug('Filesystem architecture', fs_arch) message.sub_debug('Host architecture', host_arch) if fs_arch == 'amd64' and not host_arch == 'x86_64': message.sub_debug('The ISO architecture is amd64 and yours is not') common.clean_work_dirs() unmount_iso() raise(message.exception('The ISO architecture is amd64 and yours is not')) message.sub_info('Copying ISO files') for sfile in misc.list_files(mount_dir): if sfile.endswith('casper/filesystem.squashfs'): continue else: message.sub_debug('Copying', sfile) misc.copy_file(sfile, sfile.replace(mount_dir, config.ISO_DIR)) unmount_iso()
def main(): global mount_dir if not os.path.isfile(config.ISO): raise (message.exception('ISO does not exists', config.ISO)) elif not config.ISO.endswith('.iso'): raise (message.exception('File is not ISO', config.ISO)) common.clean_work_dirs() common.create_work_dirs() message.sub_info('Creating mount directory') mount_dir = tempfile.mkdtemp(prefix=config.MOUNT_DIR + '/') message.sub_debug('Mount directory is', mount_dir) message.sub_info('Mounting ISO', config.ISO) try: # load the required kernel modules in cases the are not, if they are # builtin the command fails so ignoring that. the "-b" argument is to # ensure blacklisted modules are respected try: misc.system_command( (misc.whereis('modprobe'), '-ba', 'loop', 'iso9660')) except: pass misc.system_command((misc.whereis('mount'), '-t', 'iso9660', '-o', \ 'ro,loop', config.ISO, mount_dir)) except: message.sub_info('Removing', mount_dir) os.rmdir(mount_dir) common.clean_work_dirs() raise message.sub_info('Checking ISO') for spath in (mount_dir + '/casper/filesystem.squashfs', \ mount_dir + '/casper/filesystem.manifest', \ mount_dir + '/casper/filesystem.manifest-remove', \ mount_dir + '/.disk', mount_dir + '/isolinux', ): if not os.path.exists(spath): message.sub_debug('Non-existing path', spath) common.clean_work_dirs() unmount_iso() raise (message.exception('Invalid ISO', config.ISO)) message.sub_info('Unsquashing filesystem') try: misc.system_command((misc.whereis('unsquashfs'), '-f', '-d', \ config.FILESYSTEM_DIR, mount_dir + '/casper/filesystem.squashfs')) except: unmount_iso() raise message.sub_info('Checking architecture') fs_arch = misc.chroot_exec(('dpkg', '--print-architecture'), \ prepare=False, mount=False, output=True) host_arch = os.uname()[4] message.sub_debug('Filesystem architecture', fs_arch) message.sub_debug('Host architecture', host_arch) if fs_arch == 'amd64' and not host_arch == 'x86_64': message.sub_debug('The ISO architecture is amd64 and yours is not') common.clean_work_dirs() unmount_iso() raise (message.exception( 'The ISO architecture is amd64 and yours is not')) message.sub_info('Copying ISO files') for sfile in misc.list_files(mount_dir): if sfile.endswith('casper/filesystem.squashfs'): continue else: message.sub_debug('Copying', sfile) misc.copy_file(sfile, sfile.replace(mount_dir, config.ISO_DIR)) unmount_iso()
def main(): common.check_filesystem() # Basic sanity checks of files and paths that absolutely need to exist. message.sub_info('Doing sanity checks') lsb_file = misc.join_paths(config.FILESYSTEM_DIR, 'etc/lsb-release') if not os.path.isfile(lsb_file): raise(message.exception(lsb_file + ' does not exist')) isolinux_dir = misc.join_paths(config.ISO_DIR, 'isolinux') if not os.path.isdir(isolinux_dir): raise(message.exception(isolinux_dir + ' does not exist')) if misc.search_file('999:999', misc.join_paths(config.FILESYSTEM_DIR, 'etc/passwd')): raise(message.exception('User with UID 999 exists, this means automatic login will fail')) elif misc.search_file('999:999', misc.join_paths(config.FILESYSTEM_DIR, 'etc/group')): raise(message.exception('Group with GID 999 exists, this means automatic login will fail')) casper_dir = misc.join_paths(config.ISO_DIR, 'casper') if not os.path.isdir(casper_dir): message.sub_debug('Creating', casper_dir) os.makedirs(casper_dir) base_file = misc.join_paths(config.ISO_DIR, '.disk/base_installable') if os.path.isfile(misc.join_paths(config.FILESYSTEM_DIR, 'usr/bin/ubiquity')): if not os.path.isfile(base_file): message.sub_debug('Creating', base_file) misc.write_file(base_file, '') elif os.path.isfile(base_file): message.sub_debug('Removing', base_file) os.unlink(base_file) # Acquire distribution information from the FileSystem message.sub_info('Gathering information') arch = misc.chroot_exec(('dpkg', '--print-architecture'), prepare=False, \ mount=False, output=True) distrib = common.get_value(config.FILESYSTEM_DIR + '/etc/lsb-release', \ 'DISTRIB_ID=') release = common.get_value(config.FILESYSTEM_DIR + '/etc/lsb-release', \ 'DISTRIB_RELEASE=') if isinstance(arch, bytes): # For some reason this is of type 'bytes'. if int(sys.version_info[0]) >= 3: # If we're running under python3 arch = str(arch, 'utf-8') else: # Otherwise just cast it to a str without the 'utf-8' option. arch = str(arch) # It's really annoying to override the output filename other ways. # If you actually try to change lsb-release, you end up breaking apt. livecd_file = misc.join_paths(config.FILESYSTEM_DIR, 'etc/livecd-release') if os.path.isfile(livecd_file): distrib = common.get_value(config.FILESYSTEM_DIR + '/etc/livecd-release', \ 'DISTRIB_ID=') release = common.get_value(config.FILESYSTEM_DIR + '/etc/livecd-release', \ 'DISTRIB_RELEASE=') message.sub_debug('Distribution and Release overriden by /etc/livecd-release') # Just overwrite the variables if the file actually exists, it's cleaner. message.sub_debug('Architecture', arch) message.sub_debug('Distribution (DISTRIB_ID)', distrib) message.sub_debug('Release (DISTRIB_RELEASE)', release) # Remove files, by name, that we know we must repopulate if they exist. message.sub_info('Cleaning up') cleanup_files = ['casper/filesystem.squashfs', 'casper/initrd.lz', \ 'casper/vmlinuz', 'casper/vmlinuz.efi', 'casper/filesystem.manifest', \ 'casper/filesystem.size'] cleanup_files.extend(glob.glob('.disk/casper-uuid-*')) for sfile in cleanup_files: full_file = misc.join_paths(config.ISO_DIR, sfile) if os.path.exists(full_file): message.sub_debug('Removing', full_file) os.unlink(full_file) # Define the checksum files, and the ISO filename. md5sum_iso_file = misc.join_paths(config.WORK_DIR, 'md5sum') sha1sum_iso_file = misc.join_paths(config.WORK_DIR, 'sha1sum') sha256sum_iso_file = misc.join_paths(config.WORK_DIR, 'sha256sum') iso_file = '%s/%s-%s-%s.iso' % (config.WORK_DIR, distrib, arch, release) if os.path.exists(iso_file): message.sub_debug('Removing', iso_file) os.unlink(iso_file) if os.path.exists(md5sum_iso_file): message.sub_debug('Removing', md5sum_iso_file) os.unlink(md5sum_iso_file) if os.path.exists(sha1sum_iso_file): message.sub_debug('Removing', sha1sum_iso_file) os.unlink(sha1sum_iso_file) if os.path.exists(sha256sum_iso_file): message.sub_debug('Removing', sha256sum_iso_file) os.unlink(sha256sum_iso_file) # Detect files needed for booting, the kernel, initramfs, xen and anything else. detect_boot() if not vmlinuz: if config.PURGE_KERNEL: message.sub_info('Re-installing kernel') misc.chroot_exec(('apt-get', 'purge', '--yes', 'linux-image*', '-q')) message.sub_debug("Kernel Selection Debug: {0}, {1}".format(type(config.KERNEL), config.KERNEL)) if config.KERNEL is not "default" or None: misc.chroot_exec(('apt-get', 'install', '--yes', \ config.KERNEL, '-q')) else: # use the kernel the user specified in the config. if arch is not "amd64": # then use the 32bit 'linux-image-generic' misc.chroot_exec(('apt-get', 'install', '--yes', \ 'linux-image-generic', '-q')) else: # use the amd64 'linux-signed-generic' for uEFI misc.chroot_exec(('apt-get', 'install', '--yes', \ 'linux-signed-generic', '-q')) misc.chroot_exec(('apt-get', 'clean')) else: message.sub_info('Updating initramfs') misc.chroot_exec(('update-initramfs', '-k', 'all', '-t', '-u')) detect_boot() if not initrd or not vmlinuz: raise(message.exception('Missing boot file (initrd or vmlinuz)')) else: message.sub_info('Copying boot files') message.sub_debug('Initrd', initrd) message.sub_debug('Vmlinuz', vmlinuz) misc.copy_file(initrd, misc.join_paths(config.ISO_DIR, 'casper/initrd.lz')) # FIXME: extend to support grub efi_boot_entry = False isolinux_dir = config.ISO_DIR + '/isolinux' if os.path.isdir(isolinux_dir): for sfile in os.listdir(isolinux_dir): if sfile.endswith('.cfg') and misc.search_file('vmlinuz.efi', isolinux_dir + '/' + sfile): message.sub_debug('Found EFI entry in isolinux conf', sfile) efi_boot_entry = True if os.path.isdir(misc.join_paths(config.ISO_DIR, 'efi/boot')) or \ efi_boot_entry: message.sub_debug('Copying EFI vmlinuz') misc.copy_file(vmlinuz, misc.join_paths(config.ISO_DIR, \ 'casper/vmlinuz.efi')) os.link(misc.join_paths(config.ISO_DIR, \ 'casper/vmlinuz.efi'), misc.join_paths(config.ISO_DIR, \ 'casper/vmlinuz')) # EFI Kernels are still loadable by grub, modern ISOs lack a bare vmlinuz. # mkisofs/genisoimage -cache-inodes reuses hard linked inodes. else: misc.copy_file(vmlinuz, misc.join_paths(config.ISO_DIR, 'casper/vmlinuz')) # We only need to copy the bare kernel if we're not using EFI at all. # Copy optional boot-enablement packages onto the ISO, if found. if mt86plus: message.sub_debug('Memtest86+ kernel', mt86plus) misc.copy_file(mt86plus, misc.join_paths(config.ISO_DIR, 'install/mt86plus')) if xen_kernel: message.sub_debug('Xen kernel', xen_kernel) misc.copy_file(xen_kernel, \ misc.join_paths(config.ISO_DIR, 'casper/' + os.path.basename(xen_kernel))) if xen_efi: message.sub_debug('Xen EFI kernel', xen_efi) misc.copy_file(xen_efi, \ misc.join_paths(config.ISO_DIR, 'casper/' + os.path.basename(xen_efi))) if ipxe_kernel: message.sub_debug('iPXE kernel', ipxe_kernel) misc.copy_file(ipxe_kernel, \ misc.join_paths(config.ISO_DIR, 'casper/' + os.path.basename(ipxe_kernel))) if ipxe_efi: message.sub_debug('iPXE EFI kernel', ipxe_efi) misc.copy_file(ipxe_efi, \ misc.join_paths(config.ISO_DIR, 'casper/' + os.path.basename(ipxe_efi))) kernel_type = re.search('initrd.img-*.*.*-*-(.*)', initrd).group(1) message.sub_debug('Kernel Variant Type', kernel_type) message.sub_info('Extracting casper UUID') confdir = config.FILESYSTEM_DIR + '/conf' if os.path.isdir(confdir): shutil.rmtree(confdir) os.makedirs(confdir) if common.is_gz_file(initrd): message.sub_debug('Kernel Compression', "gzip without early microcode") try: misc.chroot_exec('zcat ' + initrd.replace(config.FILESYSTEM_DIR, '') + ' | cpio --quiet -id conf/uuid.conf', \ shell=True, cwd=config.FILESYSTEM_DIR) misc.copy_file(confdir + '/uuid.conf', misc.join_paths(config.ISO_DIR, \ '.disk/casper-uuid-' + kernel_type)) finally: shutil.rmtree(confdir) else: message.sub_debug('Kernel Compression', "gzip with early microcode") try: misc.chroot_exec('unmkinitramfs ' + initrd.replace(config.FILESYSTEM_DIR, '') + ' conf/', \ shell=True, cwd=config.FILESYSTEM_DIR) misc.copy_file(confdir + '/main/conf/uuid.conf', misc.join_paths(config.ISO_DIR, \ '.disk/casper-uuid-' + kernel_type)) finally: shutil.rmtree(confdir) # Define some default compression parameters, including a 1MB blocksize for all compressors. compression_parameters = ('-b', '1048576', '-comp', config.COMPRESSION) if config.COMPRESSION == 'xz': # Append additional compression parameters for xz. # Using the branch-call-jump filter provides a compression boost with executable code. # This can save a hundred megabytes easily, on an 800MB ISO. The dictionary size must # match the block size, and it's advisable to use larger block sizes, like 1MB or 4MB. compression_parameters += ('-Xbcj', 'x86', '-Xdict-size', '100%') message.sub_info('SquashFS Compression parameters', compression_parameters) # Create the compressed filesystem message.sub_info('Creating SquashFS compressed filesystem') make_squash_fs = ('mksquashfs', config.FILESYSTEM_DIR, \ misc.join_paths(config.ISO_DIR, 'casper/filesystem.squashfs'), \ '-wildcards', '-no-recovery', '-noappend', \ '-ef', os.path.join(sys.prefix, 'share/customizer/exclude.list')) misc.system_command(make_squash_fs + compression_parameters) message.sub_info('Checking SquashFS filesystem size') sfs_size = os.path.getsize(misc.join_paths(config.ISO_DIR, \ 'casper/filesystem.squashfs')) message.sub_debug('SquashFS filesystem size', sfs_size) if sfs_size > 4000000000: raise(message.exception('The SquashFS filesystem size is greater than 4GB')) message.sub_info('Creating filesystem.size') fs_size = 0 for root, subdirs, files in os.walk(config.FILESYSTEM_DIR): for sfile in files: sfull = os.path.join(root, sfile) if os.path.islink(sfull): continue # FIXME: respect ignored files from exclude.list fs_size += os.path.getsize(sfull) message.sub_debug('Root filesystem size', fs_size) misc.write_file(misc.join_paths(config.ISO_DIR, \ 'casper/filesystem.size'), str(fs_size)) message.sub_info('Creating filesystem.manifest') lpackages = misc.chroot_exec(('dpkg-query', '-W', \ '--showformat=${Package} ${Version}\\n'), prepare=False, mount=False, \ output=True) if isinstance(lpackages, bytes): # For some reason this is of type 'bytes'. if int(sys.version_info[0]) >= 3: # If we're running under python3 lpackages = str(lpackages, 'utf-8') else: # Otherwise just cast it to a str without the 'utf-8' option. lpackages = str(lpackages) message.sub_debug('Packages', lpackages) misc.write_file(misc.join_paths(config.ISO_DIR, \ 'casper/filesystem.manifest'), lpackages) # FIXME: do some kung-fu to check if packages are installed # and remove them from filesystem.manifest-remove if they are not # Creating a md5sum.txt file fixes lubuntu's integrity check. md5sums_file = misc.join_paths(config.ISO_DIR, 'md5sum.txt') if os.path.isfile(md5sums_file): message.sub_info('Creating md5sum.txt') misc.write_file(md5sums_file, '') for sfile in misc.list_files(config.ISO_DIR): if sfile.endswith('md5sum.txt'): continue if sfile.endswith('SHA256SUMS'): continue message.sub_debug('MD5 Checksumming', sfile) checksum = misc.generate_hash_for_file('md5', sfile) misc.append_file(md5sums_file, checksum + ' .' + \ sfile.replace(config.ISO_DIR, '') +'\n') # Creating a SHA256SUMS file fixes ubuntu-mini-remix's integrity check. shasums_file = misc.join_paths(config.ISO_DIR, 'SHA256SUMS') if os.path.isfile(shasums_file): message.sub_info('Creating SHA256SUMS') misc.write_file(shasums_file, '') for sfile in misc.list_files(config.ISO_DIR): if sfile.endswith('md5sum.txt'): continue if sfile.endswith('SHA256SUMS'): continue message.sub_debug('SHA256 Checksumming', sfile) checksum = misc.generate_hash_for_file('sha256', sfile) misc.append_file(shasums_file, checksum + ' .' + \ sfile.replace(config.ISO_DIR, '') +'\n') # Create the ISO filesystem message.sub_info('Creating ISO') os.chdir(config.ISO_DIR) misc.system_command(('xorriso', '-as', 'mkisofs', '-r', '-V', \ distrib + '-' + arch + '-' + release, '-isohybrid-mbr',\ '/usr/lib/ISOLINUX/isohdpfx.bin', '-b', 'isolinux/isolinux.bin', \ '-c', 'isolinux/boot.cat', '-J', '-l', '-no-emul-boot', \ '-boot-load-size', '4', '-boot-info-table', '-o', iso_file, \ '-cache-inodes', '-input-charset', 'utf-8', '.')) message.sub_info('Creating ISO checksums') md5checksum = misc.generate_hash_for_file('md5', iso_file) message.sub_info('ISO md5 checksum', md5checksum) misc.append_file(md5sum_iso_file, md5checksum + ' .' + \ iso_file.replace(config.WORK_DIR, '') +'\n') sha1checksum = misc.generate_hash_for_file('sha1', iso_file) message.sub_info('ISO sha1 checksum', sha1checksum) misc.append_file(sha1sum_iso_file, sha1checksum + ' .' + \ iso_file.replace(config.WORK_DIR, '') +'\n') sha256checksum = misc.generate_hash_for_file('sha256', iso_file) message.sub_info('ISO sha256 checksum', sha256checksum) misc.append_file(sha256sum_iso_file, sha256checksum + ' .' + \ iso_file.replace(config.WORK_DIR, '') +'\n') message.sub_info('Successfuly created ISO image', iso_file)
def main(): common.check_filesystem() message.sub_info('Doing sanity checks') lsb_file = misc.join_paths(config.FILESYSTEM_DIR, 'etc/lsb-release') if not os.path.isfile(lsb_file): raise(message.exception(lsb_file + ' does not exists')) isolinux_dir = misc.join_paths(config.ISO_DIR, 'isolinux') if not os.path.isdir(isolinux_dir): raise(message.exception(isolinux_dir + ' does not exist')) if misc.search_file('999:999', misc.join_paths(config.FILESYSTEM_DIR, 'etc/passwd')): raise(message.exception('User with UID 999 exists, this mean that automatic login will fail')) elif misc.search_file('999:999', misc.join_paths(config.FILESYSTEM_DIR, 'etc/group')): raise(message.exception('Group with GID 999 exists, this mean that automatic login will fail')) casper_dir = misc.join_paths(config.ISO_DIR, 'casper') if not os.path.isdir(casper_dir): message.sub_debug('Creating', casper_dir) os.makedirs(casper_dir) base_file = misc.join_paths(config.ISO_DIR, '.disk/base_installable') if os.path.isfile(misc.join_paths(config.FILESYSTEM_DIR, 'usr/bin/ubiquity')): if not os.path.isfile(base_file): message.sub_debug('Creating', base_file) misc.write_file(base_file, '') elif os.path.isfile(base_file): message.sub_debug('Removing', base_file) os.unlink(base_file) message.sub_info('Gathering information') arch = misc.chroot_exec(('dpkg', '--print-architecture'), prepare=False, \ mount=False, output=True) distrib = common.get_value(config.FILESYSTEM_DIR + '/etc/lsb-release', \ 'DISTRIB_ID=') release = common.get_value(config.FILESYSTEM_DIR + '/etc/lsb-release', \ 'DISTRIB_RELEASE=') message.sub_debug('Architecture', arch) message.sub_debug('Distribution (DISTRIB_ID)', distrib) message.sub_debug('Release (DISTRIB_RELEASE)', release) message.sub_info('Cleaning up') cleanup_files = ['casper/filesystem.squashfs', 'casper/initrd.lz', \ 'casper/vmlinuz', 'casper/vmlinuz.efi', 'casper/filesystem.manifest', \ 'casper/filesystem.size'] cleanup_files.extend(glob.glob('.disk/casper-uuid-*')) for sfile in cleanup_files: full_file = misc.join_paths(config.ISO_DIR, sfile) if os.path.exists(full_file): message.sub_debug('Removing', full_file) os.unlink(full_file) iso_file = '%s/%s-%s-%s.iso' % (config.WORK_DIR, distrib, arch, release) if os.path.exists(iso_file): message.sub_debug('Removing', iso_file) os.unlink(iso_file) detect_boot() if not vmlinuz: message.sub_info('Re-installing kernel') misc.chroot_exec(('apt-get', 'purge', '--yes', 'linux-image*', '-q')) misc.chroot_exec(('apt-get', 'install', '--yes', \ 'linux-image-generic', '-q')) misc.chroot_exec(('apt-get', 'clean')) else: message.sub_info('Updating initramfs') misc.chroot_exec(('update-initramfs', '-k', 'all', '-t', '-u')) detect_boot() if not initrd or not vmlinuz: raise(message.exception('Missing boot file (initrd or vmlinuz)')) else: message.sub_info('Copying boot files') message.sub_debug('Initrd', initrd) message.sub_debug('Vmlinuz', vmlinuz) misc.copy_file(initrd, misc.join_paths(config.ISO_DIR, 'casper/initrd.lz')) misc.copy_file(vmlinuz, misc.join_paths(config.ISO_DIR, 'casper/vmlinuz')) # FIXME: extend to support grub efi_boot_entry = False isolinux_dir = config.ISO_DIR + '/isolinux' if os.path.isdir(isolinux_dir): for sfile in os.listdir(isolinux_dir): if sfile.endswith('.cfg') and misc.search_file('vmlinuz.efi', isolinux_dir + '/' + sfile): message.sub_debug('Found EFI entry in isolinux conf', sfile) efi_boot_entry = True if os.path.isdir(misc.join_paths(config.ISO_DIR, 'efi/boot')) or \ efi_boot_entry: message.sub_debug('Copying EFI vmlinuz') misc.copy_file(vmlinuz, misc.join_paths(config.ISO_DIR, \ 'casper/vmlinuz.efi')) message.sub_info('Extracting casper UUID') confdir = config.FILESYSTEM_DIR + '/conf' if os.path.isdir(confdir): shutil.rmtree(confdir) os.makedirs(confdir) try: misc.chroot_exec('zcat ' + initrd.lstrip(config.FILESYSTEM_DIR) + ' | ' + ' cpio --quiet -id conf/uuid.conf', shell=True) kernel = re.search('initrd.img-*.*.*-*-(.*)', initrd).group(1) message.sub_debug('Kernel', kernel) misc.copy_file(confdir + '/uuid.conf', misc.join_paths(config.ISO_DIR, \ '.disk/casper-uuid-' + kernel)) finally: shutil.rmtree(confdir) message.sub_info('Creating squashed FileSystem') misc.system_command(('mksquashfs', config.FILESYSTEM_DIR, \ misc.join_paths(config.ISO_DIR, 'casper/filesystem.squashfs'), \ '-wildcards', '-ef', os.path.join(sys.prefix, 'share/customizer/exclude.list'), \ '-comp', config.COMPRESSION)) message.sub_info('Checking SquashFS filesystem size') sfs_size = os.path.getsize(misc.join_paths(config.ISO_DIR, \ 'casper/filesystem.squashfs')) message.sub_debug('SquashFS filesystem size', sfs_size) if sfs_size > 4000000000: raise(message.exception('The SquashFS filesystem size is greater than 4GB')) message.sub_info('Creating filesystem.size') fs_size = 0 for root, subdirs, files in os.walk(config.FILESYSTEM_DIR): for sfile in files: sfull = os.path.join(root, sfile) if os.path.islink(sfull): continue # FIXME: respect ignored files from exclude.list fs_size += os.path.getsize(sfull) message.sub_debug('Root filesystem size', fs_size) misc.write_file(misc.join_paths(config.ISO_DIR, \ 'casper/filesystem.size'), str(fs_size)) message.sub_info('Creating filesystem.manifest') lpackages = misc.chroot_exec(('dpkg-query', '-W', \ '--showformat=${Package} ${Version}\\n'), prepare=False, mount=False, \ output=True) message.sub_debug('Packages', lpackages) misc.write_file(misc.join_paths(config.ISO_DIR, \ 'casper/filesystem.manifest'), lpackages) # FIXME: do some kung-fu to check if packages are installed # and remove them from filesystem.manifest-remove if they are not md5sums_file = misc.join_paths(config.ISO_DIR, 'md5sum.txt') if os.path.isfile(md5sums_file): message.sub_info('Creating md5sum.txt') misc.write_file(md5sums_file, '') for sfile in misc.list_files(config.ISO_DIR): if sfile.endswith('md5sum.txt'): continue # FIXME: read in chunks message.sub_debug('Checksuming', sfile) checksum = hashlib.md5(misc.read_file(sfile)).hexdigest() misc.append_file(md5sums_file, checksum + ' .' + \ sfile.replace(config.ISO_DIR, '') +'\n') message.sub_info('Creating ISO') os.chdir(config.ISO_DIR) misc.system_command(('xorriso', '-as', 'mkisofs', '-r', '-V', \ distrib + '-' + arch + '-' + release, '-b', 'isolinux/isolinux.bin', \ '-c', 'isolinux/boot.cat', '-J', '-l', '-no-emul-boot', \ '-boot-load-size', '4', '-boot-info-table', '-o', iso_file, \ '-input-charset', 'utf-8', '.')) message.sub_info('Successfuly created ISO image', iso_file)
def main(): common.check_filesystem() # Basic sanity checks of files and paths that absolutely need to exist. message.sub_info("Doing sanity checks") lsb_file = misc.join_paths(config.FILESYSTEM_DIR, "etc/lsb-release") if not os.path.isfile(lsb_file): raise (message.exception(lsb_file + " does not exist")) isolinux_dir = misc.join_paths(config.ISO_DIR, "isolinux") if not os.path.isdir(isolinux_dir): raise (message.exception(isolinux_dir + " does not exist")) if misc.search_file("999:999", misc.join_paths(config.FILESYSTEM_DIR, "etc/passwd")): raise (message.exception("User with UID 999 exists, this mean that automatic login will fail")) elif misc.search_file("999:999", misc.join_paths(config.FILESYSTEM_DIR, "etc/group")): raise (message.exception("Group with GID 999 exists, this mean that automatic login will fail")) casper_dir = misc.join_paths(config.ISO_DIR, "casper") if not os.path.isdir(casper_dir): message.sub_debug("Creating", casper_dir) os.makedirs(casper_dir) base_file = misc.join_paths(config.ISO_DIR, ".disk/base_installable") if os.path.isfile(misc.join_paths(config.FILESYSTEM_DIR, "usr/bin/ubiquity")): if not os.path.isfile(base_file): message.sub_debug("Creating", base_file) misc.write_file(base_file, "") elif os.path.isfile(base_file): message.sub_debug("Removing", base_file) os.unlink(base_file) # Acquire distribution information from the FileSystem message.sub_info("Gathering information") arch = misc.chroot_exec(("dpkg", "--print-architecture"), prepare=False, mount=False, output=True) distrib = common.get_value(config.FILESYSTEM_DIR + "/etc/lsb-release", "DISTRIB_ID=") release = common.get_value(config.FILESYSTEM_DIR + "/etc/lsb-release", "DISTRIB_RELEASE=") # It's really annoying to override the output filename other ways. # If you actually try to change lsb-release, you end up breaking apt. livecd_file = misc.join_paths(config.FILESYSTEM_DIR, "etc/livecd-release") if os.path.isfile(livecd_file): distrib = common.get_value(config.FILESYSTEM_DIR + "/etc/livecd-release", "DISTRIB_ID=") release = common.get_value(config.FILESYSTEM_DIR + "/etc/livecd-release", "DISTRIB_RELEASE=") message.sub_debug("Distribution and Release overriden by /etc/livecd-release") # Just overwrite the variables if the file actually exists, it's cleaner. message.sub_debug("Architecture", arch) message.sub_debug("Distribution (DISTRIB_ID)", distrib) message.sub_debug("Release (DISTRIB_RELEASE)", release) # Remove files, by name, that we know we must repopulate if they exist. message.sub_info("Cleaning up") cleanup_files = [ "casper/filesystem.squashfs", "casper/initrd.lz", "casper/vmlinuz", "casper/vmlinuz.efi", "casper/filesystem.manifest", "casper/filesystem.size", ] cleanup_files.extend(glob.glob(".disk/casper-uuid-*")) for sfile in cleanup_files: full_file = misc.join_paths(config.ISO_DIR, sfile) if os.path.exists(full_file): message.sub_debug("Removing", full_file) os.unlink(full_file) # Define the checksum files, and the ISO filename. md5sum_iso_file = misc.join_paths(config.WORK_DIR, "md5sum") sha1sum_iso_file = misc.join_paths(config.WORK_DIR, "sha1sum") sha256sum_iso_file = misc.join_paths(config.WORK_DIR, "sha256sum") iso_file = "%s/%s-%s-%s.iso" % (config.WORK_DIR, distrib, arch, release) if os.path.exists(iso_file): message.sub_debug("Removing", iso_file) os.unlink(iso_file) if os.path.exists(md5sum_iso_file): message.sub_debug("Removing", md5sum_iso_file) os.unlink(md5sum_iso_file) if os.path.exists(sha1sum_iso_file): message.sub_debug("Removing", sha1sum_iso_file) os.unlink(sha1sum_iso_file) if os.path.exists(sha256sum_iso_file): message.sub_debug("Removing", sha256sum_iso_file) os.unlink(sha256sum_iso_file) # Detect files needed for booting, the kernel, initramfs, xen and anything else. detect_boot() if not vmlinuz: message.sub_info("Re-installing kernel") misc.chroot_exec(("apt-get", "purge", "--yes", "linux-image*", "-q")) misc.chroot_exec(("apt-get", "install", "--yes", "linux-signed-generic", "-q")) misc.chroot_exec(("apt-get", "clean")) else: message.sub_info("Updating initramfs") misc.chroot_exec(("update-initramfs", "-k", "all", "-t", "-u")) detect_boot() if not initrd or not vmlinuz: raise (message.exception("Missing boot file (initrd or vmlinuz)")) else: message.sub_info("Copying boot files") message.sub_debug("Initrd", initrd) message.sub_debug("Vmlinuz", vmlinuz) misc.copy_file(initrd, misc.join_paths(config.ISO_DIR, "casper/initrd.lz")) # FIXME: extend to support grub efi_boot_entry = False isolinux_dir = config.ISO_DIR + "/isolinux" if os.path.isdir(isolinux_dir): for sfile in os.listdir(isolinux_dir): if sfile.endswith(".cfg") and misc.search_file("vmlinuz.efi", isolinux_dir + "/" + sfile): message.sub_debug("Found EFI entry in isolinux conf", sfile) efi_boot_entry = True if os.path.isdir(misc.join_paths(config.ISO_DIR, "efi/boot")) or efi_boot_entry: message.sub_debug("Copying EFI vmlinuz") misc.copy_file(vmlinuz, misc.join_paths(config.ISO_DIR, "casper/vmlinuz.efi")) os.link( misc.join_paths(config.ISO_DIR, "casper/vmlinuz.efi"), misc.join_paths(config.ISO_DIR, "casper/vmlinuz") ) # EFI Kernels are still loadable by grub, modern ISOs lack a bare vmlinuz. # mkisofs/genisoimage -cache-inodes reuses hard linked inodes. else: misc.copy_file(vmlinuz, misc.join_paths(config.ISO_DIR, "casper/vmlinuz")) # We only need to copy the bare kernel if we're not using EFI at all. # Copy optional boot-enablement packages onto the ISO, if found. if mt86plus: message.sub_debug("Memtest86+ kernel", mt86plus) misc.copy_file(mt86plus, misc.join_paths(config.ISO_DIR, "install/mt86plus")) if xen_kernel: message.sub_debug("Xen kernel", xen_kernel) misc.copy_file(xen_kernel, misc.join_paths(config.ISO_DIR, "casper/" + os.path.basename(xen_kernel))) if xen_efi: message.sub_debug("Xen EFI kernel", xen_efi) misc.copy_file(xen_efi, misc.join_paths(config.ISO_DIR, "casper/" + os.path.basename(xen_efi))) if ipxe_kernel: message.sub_debug("iPXE kernel", ipxe_kernel) misc.copy_file(ipxe_kernel, misc.join_paths(config.ISO_DIR, "casper/" + os.path.basename(ipxe_kernel))) if ipxe_efi: message.sub_debug("iPXE EFI kernel", ipxe_efi) misc.copy_file(ipxe_efi, misc.join_paths(config.ISO_DIR, "casper/" + os.path.basename(ipxe_efi))) message.sub_info("Extracting casper UUID") confdir = config.FILESYSTEM_DIR + "/conf" if os.path.isdir(confdir): shutil.rmtree(confdir) os.makedirs(confdir) try: misc.chroot_exec( "zcat " + initrd.replace(config.FILESYSTEM_DIR, "") + " | cpio --quiet -id conf/uuid.conf", shell=True, cwd=config.FILESYSTEM_DIR, ) kernel = re.search("initrd.img-*.*.*-*-(.*)", initrd).group(1) message.sub_debug("Kernel", kernel) misc.copy_file(confdir + "/uuid.conf", misc.join_paths(config.ISO_DIR, ".disk/casper-uuid-" + kernel)) finally: shutil.rmtree(confdir) # Define some default compression parameters, including a 1MB blocksize for all compressors. compression_parameters = ("-b", "1048576", "-comp", config.COMPRESSION) if config.COMPRESSION == "xz": # Append additional compression parameters for xz. # Using the branch-call-jump filter provides a compression boost with executable code. # This can save a hundred megabytes easily, on an 800MB ISO. The dictionary size must # match the block size, and it's advisable to use larger block sizes, like 1MB or 4MB. compression_parameters += ("-Xbcj", "x86", "-Xdict-size", "100%") message.sub_info("SquashFS Compression parameters", compression_parameters) # Create the compressed filesystem message.sub_info("Creating SquashFS Compressed Filesystem") make_squash_fs = ( "mksquashfs", config.FILESYSTEM_DIR, misc.join_paths(config.ISO_DIR, "casper/filesystem.squashfs"), "-wildcards", "-no-recovery", "-noappend", "-ef", os.path.join(sys.prefix, "share/customizer/exclude.list"), ) misc.system_command(make_squash_fs + compression_parameters) message.sub_info("Checking SquashFS filesystem size") sfs_size = os.path.getsize(misc.join_paths(config.ISO_DIR, "casper/filesystem.squashfs")) message.sub_debug("SquashFS filesystem size", sfs_size) if sfs_size > 4000000000: raise (message.exception("The SquashFS filesystem size is greater than 4GB")) message.sub_info("Creating filesystem.size") fs_size = 0 for root, subdirs, files in os.walk(config.FILESYSTEM_DIR): for sfile in files: sfull = os.path.join(root, sfile) if os.path.islink(sfull): continue # FIXME: respect ignored files from exclude.list fs_size += os.path.getsize(sfull) message.sub_debug("Root filesystem size", fs_size) misc.write_file(misc.join_paths(config.ISO_DIR, "casper/filesystem.size"), str(fs_size)) message.sub_info("Creating filesystem.manifest") lpackages = misc.chroot_exec( ("dpkg-query", "-W", "--showformat=${Package} ${Version}\\n"), prepare=False, mount=False, output=True ) message.sub_debug("Packages", lpackages) misc.write_file(misc.join_paths(config.ISO_DIR, "casper/filesystem.manifest"), lpackages) # FIXME: do some kung-fu to check if packages are installed # and remove them from filesystem.manifest-remove if they are not # Creating a md5sum.txt file fixes lubuntu's integrity check. md5sums_file = misc.join_paths(config.ISO_DIR, "md5sum.txt") if os.path.isfile(md5sums_file): message.sub_info("Creating md5sum.txt") misc.write_file(md5sums_file, "") for sfile in misc.list_files(config.ISO_DIR): if sfile.endswith("md5sum.txt"): continue if sfile.endswith("SHA256SUMS"): continue message.sub_debug("MD5 Checksumming", sfile) checksum = misc.generate_hash_for_file("md5", sfile) misc.append_file(md5sums_file, checksum + " ." + sfile.replace(config.ISO_DIR, "") + "\n") # Creating a SHA256SUMS file fixes ubuntu-mini-remix's integrity check. shasums_file = misc.join_paths(config.ISO_DIR, "SHA256SUMS") if os.path.isfile(shasums_file): message.sub_info("Creating SHA256SUMS") misc.write_file(shasums_file, "") for sfile in misc.list_files(config.ISO_DIR): if sfile.endswith("md5sum.txt"): continue if sfile.endswith("SHA256SUMS"): continue message.sub_debug("SHA256 Checksumming", sfile) checksum = misc.generate_hash_for_file("sha256", sfile) misc.append_file(shasums_file, checksum + " ." + sfile.replace(config.ISO_DIR, "") + "\n") # Create the ISO filesystem message.sub_info("Creating ISO") os.chdir(config.ISO_DIR) misc.system_command( ( "xorriso", "-as", "mkisofs", "-r", "-V", distrib + "-" + arch + "-" + release, "-b", "isolinux/isolinux.bin", "-c", "isolinux/boot.cat", "-J", "-l", "-no-emul-boot", "-boot-load-size", "4", "-boot-info-table", "-o", iso_file, "-cache-inodes", "-input-charset", "utf-8", ".", ) ) message.sub_info("Creating ISO checksums") md5checksum = misc.generate_hash_for_file("md5", iso_file) message.sub_info("ISO md5 checksum", md5checksum) misc.append_file(md5sum_iso_file, md5checksum + " ." + iso_file.replace(config.WORK_DIR, "") + "\n") sha1checksum = misc.generate_hash_for_file("sha1", iso_file) message.sub_info("ISO sha1 checksum", sha1checksum) misc.append_file(sha1sum_iso_file, sha1checksum + " ." + iso_file.replace(config.WORK_DIR, "") + "\n") sha256checksum = misc.generate_hash_for_file("sha256", iso_file) message.sub_info("ISO sha256 checksum", sha256checksum) misc.append_file(sha256sum_iso_file, sha256checksum + " ." + iso_file.replace(config.WORK_DIR, "") + "\n") message.sub_info("Successfuly created ISO image", iso_file)