Add nocleanup option to retain temp files
[livecd.git] / tools / edit-livecd
blob16702288e0e2e378aa0848b6d51dddcea4ed2edd
1 #!/usr/bin/python -tt
3 # edit-liveos: Edit a LiveOS to insert files or to clone an instance onto a new
4 # iso image file.
6 # Copyright 2009, Red Hat Inc.
7 # Written by Perry Myers <pmyers at redhat.com> & David Huff <dhuff at redhat.com>
8 # Cloning code added by Frederick Grose <fgrose at sugarlabs.org>
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; version 2 of the License.
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU Library General Public License for more details.
20 # You should have received a copy of the GNU General Public License
21 # along with this program; if not, write to the Free Software
22 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 import os
25 import sys
26 import stat
27 import tempfile
28 import shutil
29 import subprocess
30 import optparse
31 import logging
32 import rpm
33 import glob
35 from imgcreate.debug import *
36 from imgcreate.errors import *
37 from imgcreate.fs import *
38 from imgcreate.live import *
39 from imgcreate.creator import *
40 import imgcreate.kickstart as kickstart
41 from imgcreate import read_kickstart
43 class ExistingSparseLoopbackDisk(SparseLoopbackDisk):
44 """don't want to expand the disk"""
45 def __init__(self, lofile, size):
46 SparseLoopbackDisk.__init__(self, lofile, size)
48 def create(self):
49 #self.expand(create = True)
50 LoopbackDisk.create(self)
52 class LiveImageEditor(LiveImageCreator):
53 """class for editing LiveOS images.
55 We need an instance of LiveImageCreator, however, we do not have a kickstart
56 file and we may not need to create a new image. We just want to reuse some
57 of LiveImageCreators methods on an existing LiveOS image.
59 """
61 def __init__(self, name):
62 """Initialize a LiveImageEditor instance.
64 creates a dummy instance of LiveImageCreator
65 We do not initialize any sub classes b/c we have no ks file.
67 """
68 self.name = name
70 self.tmpdir = "/var/tmp"
71 """The directory in which all temporary files will be created."""
73 self.clone = False
74 """Signals when to copy a running LiveOS image as base."""
76 self._include = None
77 """A string of file or directory paths to include in __copy_img_root."""
79 self._builder = os.getlogin()
80 """The name of the Remix builder for _branding.
81 Default = os.getlogin()"""
83 self.compress_type = None
84 """mksquashfs compressor to use. Use 'None' to force reading of the
85 existing image, or enter a -p --compress_type value to override the
86 current compression or lack thereof. Compression type options vary with
87 the version of the kernel and SquashFS used."""
89 self.skip_compression = False
90 """Controls whether to use squashfs to compress the image."""
92 self.skip_minimize = False
93 """Controls whether an image minimizing snapshot should be created."""
95 self._isofstype = "iso9660"
96 self.__isodir = None
98 self._ImageCreator__builddir = None
99 """working directory"""
101 self._ImageCreator_outdir = None
102 """where final iso gets written"""
104 self._ImageCreator__bindmounts = []
106 self._LoopImageCreator__blocksize = 4096
107 self._LoopImageCreator__fslabel = None
108 self._LoopImageCreator__instloop = None
109 self._LoopImageCreator__fstype = None
110 self._LoopImageCreator__image_size = None
112 self.__instroot = None
114 self._LiveImageCreatorBase__isodir = None
115 """directory where the iso is staged"""
117 self.ks = None
118 """optional kickstart file as a recipe for editing the image"""
120 self._ImageCreator__selinux_mountpoint = "/sys/fs/selinux"
121 with open("/proc/self/mountinfo", "r") as f:
122 for line in f.readlines():
123 fields = line.split()
124 if fields[-2] == "selinuxfs":
125 self.__ImageCreator__selinux_mountpoint = fields[4]
126 break
128 # properties
129 def __get_image(self):
130 if self._LoopImageCreator__imagedir is None:
131 self.__ensure_builddir()
132 self._LoopImageCreator__imagedir = \
133 tempfile.mkdtemp(dir = os.path.abspath(self.tmpdir),
134 prefix = self.name + "-")
135 rtn = self._LoopImageCreator__imagedir + "/ext3fs.img"
136 return rtn
137 _image = property(__get_image)
138 """The location of the filesystem image file."""
140 def _get_fslabel(self):
141 dev_null = os.open("/dev/null", os.O_WRONLY)
142 try:
143 out = subprocess.Popen(["/sbin/e2label", self._image],
144 stdout = subprocess.PIPE,
145 stderr = dev_null).communicate()[0]
147 self._LoopImageCreator__fslabel = out.strip()
149 except IOError, e:
150 raise CreatorError("Failed to determine fsimage LABEL: %s" % e )
151 finally:
152 os.close(dev_null)
154 def __ensure_builddir(self):
155 if not self._ImageCreator__builddir is None:
156 return
158 try:
159 self._ImageCreator__builddir = tempfile.mkdtemp(dir = os.path.abspath(self.tmpdir),
160 prefix = "edit-liveos-")
161 except OSError, (err, msg):
162 raise CreatorError("Failed create build directory in %s: %s" %
163 (self.tmpdir, msg))
165 def _run_script(self, script):
167 (fd, path) = tempfile.mkstemp(prefix = "script-",
168 dir = self._instroot + "/tmp")
170 logging.debug("copying script to install root: %s" % path)
171 shutil.copy(os.path.abspath(script), path)
172 os.close(fd)
173 os.chmod(path, 0700)
175 script = "/tmp/" + os.path.basename(path)
177 try:
178 subprocess.call([script], preexec_fn = self._chroot)
179 except OSError, e:
180 raise CreatorError("Failed to execute script %s, %s " % (script, e))
181 finally:
182 os.unlink(path)
184 def mount(self, base_on, cachedir = None):
185 """mount existing file system.
187 We have to override mount b/c we many not be creating an new install
188 root nor do we need to setup the file system, i.e., makedirs(/etc/,
189 /boot, ...), nor do we want to overwrite fstab, or create selinuxfs.
191 We also need to get some info about the image before we can mount it.
193 base_on -- the <LIVEIMG.src> a LiveOS.iso file or an attached LiveOS
194 device, such as, /dev/live for a currently running image.
196 cachedir -- a directory in which to store a Yum cache;
197 Not used in edit-liveos.
201 if not base_on:
202 raise CreatorError("No base LiveOS image specified.")
204 self.__ensure_builddir()
206 self._ImageCreator_instroot = self._ImageCreator__builddir + "/install_root"
207 self._LoopImageCreator__imagedir = self._ImageCreator__builddir + "/ex"
208 self._ImageCreator_outdir = self._ImageCreator__builddir + "/out"
210 makedirs(self._ImageCreator_instroot)
211 makedirs(self._LoopImageCreator__imagedir)
212 makedirs(self._ImageCreator_outdir)
214 if self.clone:
215 # Need to clone base_on into ext3fs.img at this point
216 self._LoopImageCreator__fslabel = self.name
217 self._base_on(base_on)
218 else:
219 LiveImageCreator._base_on(self, base_on)
220 self._LoopImageCreator__fstype = get_fsvalue(self._image, 'TYPE')
221 self._get_fslabel()
223 self.fslabel = self._LoopImageCreator__fslabel
224 if self._LoopImageCreator__image_size == None:
225 self._LoopImageCreator__image_size = os.stat(self._image)[stat.ST_SIZE]
227 self._LoopImageCreator__instloop = ExtDiskMount(
228 ExistingSparseLoopbackDisk(self._image,
229 self._LoopImageCreator__image_size),
230 self._ImageCreator_instroot,
231 self._fstype,
232 self._LoopImageCreator__blocksize,
233 self.fslabel,
234 self.tmpdir)
235 try:
236 self._LoopImageCreator__instloop.mount()
237 except MountError, e:
238 raise CreatorError("Failed to loopback mount '%s' : %s" %
239 (self._image, e))
241 cachesrc = cachedir or (self._ImageCreator__builddir + "/yum-cache")
242 makedirs(cachesrc)
244 for (f, dest) in [("/sys", None), ("/proc", None),
245 ("/dev/pts", None), ("/dev/shm", None),
246 (cachesrc, "/var/cache/yum")]:
247 self._ImageCreator__bindmounts.append(BindChrootMount(f, self._instroot, dest))
249 self._do_bindmounts()
251 os.symlink("../proc/mounts", self._instroot + "/etc/mtab")
253 self.__copy_img_root(base_on)
254 self._brand(self._builder)
256 def _base_on(self, base_on):
257 """Clone the running LiveOS image as the basis for the new image."""
259 self.__fstype = 'ext4'
260 self.__image_size = 4096L * 1024 * 1024
261 self.__blocksize = 4096
263 self.__instloop = ExtDiskMount(SparseLoopbackDisk(self._image,
264 self.__image_size),
265 self._instroot,
266 self.__fstype,
267 self.__blocksize,
268 self.fslabel,
269 self.tmpdir)
270 try:
271 self.__instloop.mount()
272 except MountError, e:
273 raise CreatorError("Failed to loopback mount '%s' : %s" %
274 (self._image, e))
276 subprocess.call(['rsync', '-ptgorlHASx', '--specials', '--progress',
277 '--include', '/*/',
278 '--exclude', '/etc/mtab',
279 '--exclude', '/etc/blkid/*',
280 '--exclude', '/dev/*',
281 '--exclude', '/proc/*',
282 '--exclude', '/home/*',
283 '--exclude', '/media/*',
284 '--exclude', '/mnt/live',
285 '--exclude', '/sys/*',
286 '--exclude', '/tmp/*',
287 '--exclude', '/.liveimg*',
288 '--exclude', '/.autofsck',
289 '/', self._instroot])
290 subprocess.call(['sync'])
292 self._ImageCreator__create_minimal_dev()
294 self.__instloop.cleanup()
297 def __copy_img_root(self, base_on):
298 """helper function to copy root content of the base LiveIMG to
299 ISOdir"""
301 ignore_list = ['ext3fs.img', 'squashfs.img', 'osmin.img', 'home.img',
302 'overlay-*']
304 if self.clone:
305 ignore_list.remove('home.img')
306 includes = 'boot, /EFI, /syslinux, /LiveOS'
307 if self._include:
308 includes += ", " + self._include
310 imgmnt = DiskMount(RawDisk(0, base_on), self._mkdtemp())
311 else:
312 imgmnt = DiskMount(LoopbackDisk(base_on, 0), self._mkdtemp())
314 self._LiveImageCreatorBase__isodir = self._ImageCreator__builddir + "/iso"
316 try:
317 imgmnt.mount()
318 except MountError, e:
319 raise CreatorError("Failed to mount '%s' : %s" % (base_on, e))
320 else:
321 # include specified files or directories
322 if self.clone:
323 baseimg = os.path.join(imgmnt.mountdir, 'LiveOS',
324 'squashfs.img')
325 # 'self.compress_type = None' will force reading it from
326 # base_on.
327 if self.compress_type is None:
328 self.compress_type = squashfs_compression_type(baseimg)
329 if self.compress_type == 'undetermined':
330 # 'gzip' for compatibility with older versions.
331 self.compress_type = 'gzip'
333 dst = self._LiveImageCreatorBase__isodir
334 print includes
335 for fd in includes.split(', /'):
336 src = os.path.join(imgmnt.mountdir, fd)
337 if os.path.isfile(src):
338 shutil.copy2(src, os.path.join(dst, fd))
339 elif os.path.isdir(src):
340 shutil.copytree(src, os.path.join(dst, fd),
341 symlinks=True,
342 ignore=shutil.ignore_patterns(
343 *ignore_list))
344 else:
345 #copy over everything but squashfs.img or ext3fs.img
346 shutil.copytree(imgmnt.mountdir,
347 self._LiveImageCreatorBase__isodir,
348 ignore=shutil.ignore_patterns(*ignore_list))
349 subprocess.call(['sync'])
350 finally:
351 imgmnt.cleanup()
354 def _brand (self, _builder):
355 """Adjust the image branding to show its variation from original
356 source by builder and build date."""
358 self.fslabel = self.name
359 dt = time.strftime('%d-%b-%Y')
361 lst = ['isolinux/isolinux.cfg', 'syslinux/syslinux.cfg',
362 'syslinux/extlinux.conf']
363 for f in lst:
364 fpath = os.path.join(self._LiveImageCreatorBase__isodir, f)
365 if os.path.exists(fpath):
366 break
368 # Get build name from boot configuration file.
369 try:
370 cfgf = open(fpath, 'r')
371 except IOError, e:
372 raise CreatorError("Failed to open '%s' : %s" % (fpath, e))
373 else:
374 release = None
375 for line in cfgf:
376 i = line.find('Welcome to ')
377 if i > -1:
378 release = line[i+11:-2]
379 break
380 cfgf.close()
381 if not release:
382 return
384 ntext = dt.translate(None, '-') + '-' + _builder + '-Remix-' + release
386 # Update fedora-release message with Remix details.
387 releasefiles = '/etc/fedora-release, /etc/generic-release'
388 if self._releasefile:
389 releasefiles += ', ' + self._releasefile
390 for fn in releasefiles.split(', '):
391 if os.path.exists(fn):
392 try:
393 with open(self._instroot + fn, 'r') as f:
394 text = ntext + '\n' + f.read()
395 open(f.name, 'w').write(text)
396 except IOError, e:
397 raise CreatorError("Failed to open or write '%s' : %s" %
398 (f.name, e))
400 self._releasefile = ntext
401 self.name += '-' + os.uname()[4] + '-' + time.strftime('%Y%m%d.%H%M')
404 def _configure_bootloader(self, isodir):
405 """Restore the boot configuration files for an iso image boot."""
407 bootfolder = os.path.join(isodir, 'isolinux')
408 oldpath = os.path.join(isodir, 'syslinux')
409 if os.path.exists(oldpath):
410 os.rename(oldpath, bootfolder)
412 cfgf = os.path.join(bootfolder, 'isolinux.cfg')
413 for f in ['syslinux.cfg', 'extlinux.conf']:
414 src = os.path.join(bootfolder, f)
415 if os.path.exists(src):
416 os.rename(src, cfgf)
418 args = ['/bin/sed', '-i']
419 if self._releasefile:
420 args.append('-e')
421 args.append('s/Welcome to .*/Welcome to ' + self._releasefile + '!/')
422 if self.clone:
423 args.append('-e')
424 args.append('s/rootfstype=[^ ]* [^ ]*/rootfstype=auto ro/')
425 args.append('-e')
426 args.append('s/\(r*d*.*live.*ima*ge*\) .* quiet/\1 quiet/')
427 args.append('-e')
428 args.append('s/root=[^ ]*/root=live:CDLABEL=' + self.name + '/')
429 if self.ks:
430 # bootloader --append "!opt-to-remove opt-to-add"
431 for param in kickstart.get_kernel_args(self.ks,"").split():
432 if param.startswith('!'):
433 param=param[1:]
434 # remove parameter prefixed with !
435 args.append('-e')
436 args.append("/^ append/s/%s //" % param)
437 # special case for last parameter
438 args.append('-e')
439 args.append("/^ append/s/%s$//" % param)
440 else:
441 # append parameter
442 args.append('-e')
443 args.append("/^ append/s/$/ %s/" % param)
444 args.append(cfgf)
445 dev_null = os.open("/dev/null", os.O_WRONLY)
446 try:
447 subprocess.Popen(args,
448 stdout = subprocess.PIPE,
449 stderr = dev_null).communicate()[0]
450 return 0
452 except IOError, e:
453 raise CreatorError("Failed to configure bootloader file: %s" % e)
454 return 1
455 finally:
456 os.close(dev_null)
458 def _run_pre_scripts(self):
459 for s in kickstart.get_pre_scripts(self.ks):
460 (fd, path) = tempfile.mkstemp(prefix = "ks-script-",
461 dir = self._instroot + "/tmp")
463 os.write(fd, s.script)
464 os.close(fd)
465 os.chmod(path, 0700)
467 env = self._get_post_scripts_env(s.inChroot)
469 if not s.inChroot:
470 env["INSTALL_ROOT"] = self._instroot
471 preexec = None
472 script = path
473 else:
474 preexec = self._chroot
475 script = "/tmp/" + os.path.basename(path)
477 try:
478 subprocess.check_call([s.interp, script],
479 preexec_fn = preexec, env = env)
480 except OSError, e:
481 raise CreatorError("Failed to execute %%post script "
482 "with '%s' : %s" % (s.interp, e.strerror))
483 except subprocess.CalledProcessError, err:
484 if s.errorOnFail:
485 raise CreatorError("%%post script failed with code %d "
486 % err.returncode)
487 logging.warning("ignoring %%post failure (code %d)"
488 % err.returncode)
489 finally:
490 os.unlink(path)
492 class simpleCallback:
493 def __init__(self):
494 self.fdnos = {}
496 def callback(self, what, amount, total, mydata, wibble):
497 if what == rpm.RPMCALLBACK_TRANS_START:
498 pass
500 elif what == rpm.RPMCALLBACK_INST_OPEN_FILE:
501 hdr, path = mydata
502 print "Installing %s\r" % (hdr["name"])
503 fd = os.open(path, os.O_RDONLY)
504 nvr = '%s-%s-%s' % ( hdr['name'], hdr['version'], hdr['release'] )
505 self.fdnos[nvr] = fd
506 return fd
508 elif what == rpm.RPMCALLBACK_INST_CLOSE_FILE:
509 hdr, path = mydata
510 nvr = '%s-%s-%s' % ( hdr['name'], hdr['version'], hdr['release'] )
511 os.close(self.fdnos[nvr])
513 elif what == rpm.RPMCALLBACK_INST_PROGRESS:
514 hdr, path = mydata
515 print "%s: %.5s%% done\r" % (hdr["name"], (float(amount) / total) * 100),
517 def install_rpms(self):
518 if kickstart.exclude_docs(self.ks):
519 rpm.addMacro("_excludedocs", "1")
520 if not kickstart.selinux_enabled(self.ks):
521 rpm.addMacro("__file_context_path", "%{nil}")
522 if kickstart.inst_langs(self.ks) != None:
523 rpm.addMacro("_install_langs", kickstart.inst_langs(self.ks))
524 # start RPM transaction
525 ts=rpm.TransactionSet(self._instroot)
526 for repo in kickstart.get_repos(self.ks):
527 (name, baseurl, mirrorlist, proxy, inc, exc, cost) = repo
528 if baseurl.startswith("file://"):
529 baseurl=baseurl[7:]
530 elif not baseurl.startswith("/"):
531 raise CreatorError("edit-livecd accepts only --baseurl pointing to a local folder with RPMs (not YUM repo)")
532 if not baseurl.endswith("/"):
533 baseurl+="/"
534 for pkg_from_list in kickstart.get_packages(self.ks):
535 # TODO report if package listed in ks is missing
536 for pkg in glob.glob(baseurl+pkg_from_list+"-[0-9]*.rpm"):
537 fdno = os.open(pkg, os.O_RDONLY)
538 hdr = ts.hdrFromFdno(fdno)
539 os.close(fdno)
540 ts.addInstall(hdr,(hdr,pkg), "u")
541 ts.run(self.simpleCallback().callback,'')
543 def parse_options(args):
544 parser = optparse.OptionParser(usage = """
545 %prog [-n=<name>]
546 [-o <output>]
547 [-k <kickstart-file>]
548 [-s <script.sh>]
549 [-t <tmpdir>]
550 [-e <excludes>]
551 [-f <exclude-file>]
552 [-i <includes>]
553 [-r <releasefile>]
554 [-b <builder>]
555 [--clone]
556 [-c <compress_type>]
557 [--skip-compression]
558 [--skip-minimize]
559 <LIVEIMG.src>""")
561 parser.add_option("-n", "--name", type="string", dest="name",
562 help="name of new LiveOS (don't include .iso, it will "
563 "be added)")
565 parser.add_option("-o", "--output", type="string", dest="output",
566 help="specify directory for new iso file.")
568 parser.add_option("-k", "--kickstart", type="string", dest="kscfg",
569 help="Path or url to kickstart config file")
571 parser.add_option("-s", "--script", type="string", dest="script",
572 help="specify script to run chrooted in the LiveOS "
573 "fsimage")
575 parser.add_option("-t", "--tmpdir", type="string",
576 dest="tmpdir", default="/var/tmp",
577 help="Temporary directory to use (default: /var/tmp)")
579 parser.add_option("-e", "--exclude", type="string", dest="exclude",
580 help="Specify directory or file patterns to be excluded "
581 "from the rsync copy of the filesystem.")
583 parser.add_option("-f", "--exclude-file", type="string",
584 dest="exclude_file",
585 help="Specify a file containing file patterns to be "
586 "excluded from the rsync copy of the filesystem.")
588 parser.add_option("-i", "--include", type="string", dest="include",
589 help="Specify directory or file patterns to be included "
590 "in copy_img_root.")
592 parser.add_option("-r", "--releasefile", type="string", dest="releasefile",
593 help="Specify release file/s for branding.")
595 parser.add_option("-b", "--builder", type="string",
596 dest="builder", default=os.getlogin(),
597 help="Specify the builder of a Remix.")
599 parser.add_option("", "--clone", action="store_true", dest="clone",
600 help="Specify that source image is LiveOS block device.")
602 parser.add_option("-c", "--compress_type", type="string",
603 dest="compress_type",
604 help="Specify the compression type for SquashFS. Will "
605 "override the current compression or lack thereof.")
607 parser.add_option("", "--skip-compression", action="store_true",
608 dest="skip_compression", default=False,
609 help="Specify no compression of filesystem, ext3fs.img")
611 parser.add_option("", "--skip-minimize", action="store_true",
612 dest="skip_minimize", default=False,
613 help="Specify no osmin.img minimal snapshot.")
615 setup_logging(parser)
617 (options, args) = parser.parse_args()
619 if len(args) != 1:
620 parser.print_usage()
621 sys.exit(1)
623 print args[0]
625 return (args[0], options)
627 def get_fsvalue(filesystem, tag):
628 dev_null = os.open('/dev/null', os.O_WRONLY)
629 args = ['/sbin/blkid', '-s', tag, '-o', 'value', filesystem]
630 try:
631 fs_type = subprocess.Popen(args,
632 stdout=subprocess.PIPE,
633 stderr=dev_null).communicate()[0]
634 except IOError, e:
635 raise CreatorError("Failed to determine fs %s: %s" % value, e )
636 finally:
637 os.close(dev_null)
639 return fs_type.rstrip()
641 def rebuild_iso_symlinks(isodir):
642 # remove duplicate files and rebuild symlinks to reduce iso size
643 efi_vmlinuz = "%s/EFI/BOOT/vmlinuz0" % isodir
644 isolinux_vmlinuz = "%s/isolinux/vmlinuz0" % isodir
645 efi_initrd = "%s/EFI/BOOT/initrd0.img" % isodir
646 isolinux_initrd = "%s/isolinux/initrd0.img" % isodir
648 if os.path.exists(efi_vmlinuz):
649 os.remove(efi_vmlinuz)
650 os.remove(efi_initrd)
651 os.symlink(isolinux_vmlinuz,efi_vmlinuz)
652 os.symlink(isolinux_initrd,efi_initrd)
654 def main():
655 # LiveOS set to <LIVEIMG.src>
656 (LiveOS, options) = parse_options(sys.argv[1:])
658 if os.geteuid () != 0:
659 print >> sys.stderr, "You must run edit-liveos as root"
660 return 1
662 if options.name:
663 name = options.name
664 elif stat.S_ISBLK(os.stat(LiveOS).st_mode):
665 name = get_fsvalue(LiveOS, 'LABEL') + '.edited'
666 else:
667 name = os.path.basename(LiveOS) + ".edited"
669 if options.output:
670 output = options.output
671 else:
672 output = os.path.dirname(LiveOS)
673 if output == '/dev':
674 output = options.tmpdir
676 editor = LiveImageEditor(name)
677 editor._exclude = options.exclude
678 editor._exclude_file = options.exclude_file
679 editor._include = options.include
680 editor.clone = options.clone
681 editor.tmpdir = options.tmpdir
682 editor._builder = options.builder
683 editor._releasefile = options.releasefile
684 editor.compress_type = options.compress_type
685 editor.skip_compression = options.skip_compression
686 editor.skip_minimize = options.skip_minimize
688 try:
689 if options.kscfg:
690 editor.ks = read_kickstart(options.kscfg)
691 # part / --size <new rootfs size to be resized to>
692 editor._LoopImageCreator__image_size = kickstart.get_image_size(editor.ks)
693 if options.script:
694 if not os.path.exists(options.script):
695 print "Invalid Script Path '%s'" % options.script
696 return 1
697 editor.mount(LiveOS, cachedir = None)
698 editor._configure_bootloader(editor._LiveImageCreatorBase__isodir)
699 if editor.ks:
700 editor._run_pre_scripts()
701 editor.install_rpms()
702 editor._run_post_scripts()
703 elif options.script:
704 print "Running edit script '%s'" % options.script
705 editor._run_script(options.script)
706 else:
707 print "Launching shell. Exit to continue."
708 print "----------------------------------"
709 editor.launch_shell()
710 rebuild_iso_symlinks(editor._LiveImageCreatorBase__isodir)
711 editor.unmount()
712 editor.package(output)
713 logging.info("%s.iso saved to %s" % (editor.name, output))
714 except CreatorError, e:
715 logging.error(u"Error editing LiveOS : %s" % e)
716 return 1
717 finally:
718 editor.cleanup()
720 return 0
722 if __name__ == "__main__":
723 sys.exit(main())
725 arch = rpmUtils.arch.getBaseArch()
726 if arch in ("i386", "x86_64"):
727 LiveImageCreator = x86LiveImageCreator
728 elif arch in ("ppc",):
729 LiveImageCreator = ppcLiveImageCreator
730 elif arch in ("ppc64",):
731 LiveImageCreator = ppc64LiveImageCreator
732 else:
733 raise CreatorError("Architecture not supported!")