[SCM] GUI front-end for Debian Live. branch, master, updated. 72023d3200637edc6b81b8cc8d240b376a0d14df

Chris Lamb chris at chris-lamb.co.uk
Wed Jul 9 23:14:01 UTC 2008


The following commit has been merged in the master branch:
commit c4bf2b42dbd1abf975d723e498f48a44b1653f5f
Author: Chris Lamb <chris at chris-lamb.co.uk>
Date:   Wed Jul 9 23:54:05 2008 +0100

    Add support for the entire build window running as root, chown-ing the build contents post-build, etc.
    
    Signed-off-by: Chris Lamb <chris at chris-lamb.co.uk>

diff --git a/LiveMagic/__init__.py b/LiveMagic/__init__.py
index 7f60e50..ed5d1d9 100644
--- a/LiveMagic/__init__.py
+++ b/LiveMagic/__init__.py
@@ -17,13 +17,22 @@ class LiveMagic(object):
         except RuntimeError, e:
             sys.exit('E: %s. Exiting.' % e)
 
-        optparser = optparse.OptionParser(version=__version__)
-        _, args = optparser.parse_args()
-
-        c = controllers.Controller(args)
+        parser = optparse.OptionParser(version=__version__)
+        parser.add_option('--build-for', dest='build_for',
+            metavar='owner:group', help="Perform build on behalf of owner:group. "
+            "This option is used internally.")
+        parser.add_option('--kde-full-session', dest='kde_full_session',
+            metavar='', help="Value of KDE_FULL_SESSION. "
+            "This option is used internally.")
+        parser.add_option('--gnome-desktop-session-id', dest='gnome_desktop_session_id',
+            metavar='', help="Value of GNOME_DESKTOP_SESSION_ID. "
+            "This option is used internally.")
+        options, args = parser.parse_args()
+
+        c = controllers.Controller()
         v = views.View(c)
 
-        c.ready()
+        c.ready(options, args)
 
         gtk.gdk.threads_init()
         gtk.main()
diff --git a/LiveMagic/controllers/__init__.py b/LiveMagic/controllers/__init__.py
index b999b85..a469efa 100644
--- a/LiveMagic/controllers/__init__.py
+++ b/LiveMagic/controllers/__init__.py
@@ -4,10 +4,10 @@ from hooks import HooksController as Hooks
 from wizard import WizardController as Wizard
 
 class Controller(Main, Build, Hooks, Wizard):
-    def __init__(self, args):
+    def __init__(self):
         self.model = None
 
-        Main.__init__(self, args)
+        Main.__init__(self)
         Build.__init__(self)
         Hooks.__init__(self)
         Wizard.__init__(self)
diff --git a/LiveMagic/controllers/build.py b/LiveMagic/controllers/build.py
index 0442b7a..f773fed 100644
--- a/LiveMagic/controllers/build.py
+++ b/LiveMagic/controllers/build.py
@@ -1,28 +1,27 @@
 import os
+import pwd
 import sys
 import shutil
 import gobject
 import subprocess
 
-BUILDING, CANCELLED, CANCELLED_CLEAN, GKSU_ERROR, FAILED, \
-    FAILED_CLEAN, OK, OK_CLEAN, DONE = range(9)
+BUILDING, CANCELLED, CANCELLED_CLEAN, FAILED, \
+    FAILED_CLEAN, OK, OK_CLEAN, DONE = range(8)
 
 from LiveMagic.utils import find_resource
 
 class BuildController(object):
 
-    # GTK callbacks
-
-    def on_build_activate(self, *_):
-        # Save model if necessary
-        if self.model.altered():
-            self.model.save()
-        self.do_show_build_window(self.set_window_main_sensitive)
-
     def do_show_build_window(self, build_close_callback):
         self.state = BUILDING
         self.build_successful = False
 
+        self.uid, self.gid = [int(x) for x in self.options.build_for.split(':', 2)]
+
+        f = open(os.path.join(os.getcwd(), 'build-log.txt'), 'w')
+        f.write('I: live-magic respawned as root')
+        f.close()
+
         # Set initial titles and state
         self.view.set_build_status_change(initial=True)
 
@@ -30,11 +29,11 @@ class BuildController(object):
         self.view.do_show_window_build(build_close_callback)
 
         # Start pulsing
-        gobject.timeout_add(100, self.do_pulse_cb)
+        gobject.timeout_add(80, self.do_pulse_cb)
 
         # Fork command
-        cmd = ['/usr/bin/gksu', '--', find_resource('live-magic-builder')]
-        self.pid = self.view.vte_terminal.fork_command(cmd[0], cmd, None, self.model.dir)
+        cmd = [find_resource('live-magic-builder')]
+        self.pid = self.view.vte_terminal.fork_command(cmd[0], cmd, None, os.getcwd())
 
         if self.pid < 0:
             self.view.set_build_titles("Error creating Debian Live system!", \
@@ -48,12 +47,11 @@ class BuildController(object):
         return True
 
     def on_vte_child_exited(self, *_):
-        self.pid = -1
-        status_filename = os.path.join(self.model.dir, '.status')
+        status_filename = os.path.join(os.getcwd(), '.status')
 
         def _exec(*cmds):
             args = ['/bin/sh', '-c', '; '.join(cmds)]
-            self.view.vte_terminal.fork_command(args[0], args, None, self.model.dir)
+            self.view.vte_terminal.fork_command(args[0], args, None, os.getcwd())
 
         def set_cleaning_status():
             os.remove(status_filename)
@@ -64,13 +62,21 @@ class BuildController(object):
         def ok():
             self.view.set_build_titles("Build process finished",
                 "Your Debian Live system has been created successfully.")
-            subprocess.call(['xdg-open', '%s' % self.model.dir])
+
+            if self.options.kde_full_session != '-':
+                os.environ['KDE_FULL_SESSION'] = self.options.kde_full_session
+            if self.options.gnome_desktop_session_id != '-':
+                os.environ['GNOME_DESKTOP_SESSION_ID'] = self.options.gnome_desktop_session_id
+
+            cmd = ['su', pwd.getpwuid(self.uid)[0], '-c', 'xdg-open .']
+            subprocess.call(cmd)
             return DONE
 
         def ok_clean():
             set_cleaning_status()
-            _exec('lh_clean --chroot --stage --source --cache', 'rm -rf config/ binary/', \
-                'gzip build-log.txt')
+            _exec('lh_clean --chroot --stage --source --cache',
+                'rm -rf config/ binary/',
+                'chown -R %d:%d .' % (self.uid, self.gid))
             return OK
 
         def failed():
@@ -80,7 +86,8 @@ class BuildController(object):
 
         def failed_clean():
             set_cleaning_status()
-            _exec('lh_clean --purge', 'rm -rf config/', 'gzip-build.log.txt')
+            _exec('lh_clean --purge', 'rm -rf config/',
+                'chown -R . %d:%d' % (self.uid, self.gid))
             return FAILED
 
         def cancelled():
@@ -93,12 +100,6 @@ class BuildController(object):
             _exec('lh_clean --purge', 'rm -rf $(pwd)')
             return CANCELLED
 
-        def gksu_error():
-            self.view.set_build_titles("Error gaining superuser priviledges",
-                "There was an error when trying to gain superuser priviledges.")
-            shutil.rmtree(self.model.dir)
-            return DONE
-
         if self.state == BUILDING:
             self.state = FAILED_CLEAN
             try:
@@ -110,20 +111,20 @@ class BuildController(object):
                 finally:
                     f.close()
             except:
-                self.state = GKSU_ERROR
+                pass
 
         self.state = {
             CANCELLED: cancelled,
             CANCELLED_CLEAN: cancelled_clean,
             FAILED: failed,
             FAILED_CLEAN: failed_clean,
-            GKSU_ERROR: gksu_error,
             OK: ok,
             OK_CLEAN: ok_clean,
         }[self.state]()
 
         if self.state == DONE:
             self.view.set_build_status_change(initial=False)
+            self.pid = -1
 
         # Auto-close if requested
         if self.view.get_build_auto_close() and self.build_successful:
@@ -139,4 +140,4 @@ class BuildController(object):
 
     def on_button_build_cancel_clicked(self, *_):
         self.state = CANCELLED_CLEAN
-        subprocess.call(['gksu', '--', '/bin/kill', '-s', 'KILL', '-%d' % self.pid])
+        subprocess.call(['/bin/kill', '-s', 'KILL', '-%d' % self.pid])
diff --git a/LiveMagic/controllers/main.py b/LiveMagic/controllers/main.py
index 7dfe51d..5584873 100644
--- a/LiveMagic/controllers/main.py
+++ b/LiveMagic/controllers/main.py
@@ -1,89 +1,22 @@
+import os
 import gtk
-
-import commands
+import sys
 
 class MainController(object):
-    def __init__(self, args):
-        #self.model.attach_load_observer(self.notify_load)
-        self.args = args
-
-    def ready(self):
-        sections = ('common', 'chroot', 'binary', 'bootstrap', 'source', 'hooks')
-        self.view.setup_sections(sections)
-
-        # Notify all the observers that depend on the model
-        if len(self.args) == 0:
-            self.view.do_show_wizard()
-        else:
-            self.view.do_show_main_window()
-            self.model.notify_load_observers()
-
-    # GTK callbacks
-    def on_live_helper_value_changed(self, widget):
-        namespace, key = widget.name.split("/")
-        value = widget.get_text()
-
-        # Call self.model.namespace.key = vaulue
-        ns = getattr(self.model, namespace)
-        setattr(ns, key, value)
-
-        # Variables have been tainted, allow saving
-        self.view.set_save_enabled(True)
 
-    def on_choose_section(self, button):
-        self.view.do_select_section(button)
+    def ready(self, options, args):
+        self.options = options
 
-    def on_new(self, *_):
-        if self.model.altered():
-            print "Not newing as not saved"
+        if options.build_for:
+            self.do_show_build_window(lambda: gtk.main_quit())
         else:
-            self.model.new()
-        self.view.set_save_enabled(True)
-
-    def on_open(self, *_):
-        def open_dialog(self):
-            res, filename = self.view.do_folder_open()
-            if res == gtk.RESPONSE_ACCEPT:
-                try:
-                    self.model.open(filename)
-                    self.view.set_save_enabled(False)
-                except IOError:
-                    self.view.do_show_loading_error()
-        return self._confirm_save(lambda: open_dialog(self), quit_dialog=False)
-
-    def on_save(self, *_):
-        self.view.set_save_enabled(False)
-        self.view.set_status_bar("Saving configuration to %s ..." % self.model.dir)
-        self.model.save()
-        self.view.set_status_bar("Saved configuration to %s" % self.model.dir)
-
-    def on_save_as(self, *_):
-        raise NotImplemented
-
-    def _confirm_save(self, fn, **kw):
-        if self.model.altered():
-            res = self.view.do_confirm_save(**kw)
-            if res == gtk.RESPONSE_ACCEPT: # Save
-                self.on_save(None)
-                fn()
-            elif res == gtk.RESPONSE_REJECT: # Don't save
-                fn()
-            else:
-                return True # Cancel
-        else:
-            # We are up to date
-            fn()
+            self.view.do_show_wizard()
 
     def on_quit_request(self, *_):
         return self._confirm_save(lambda: gtk.main_quit(), quit_dialog=True)
 
-    def on_about_activate(self, *_):
-        self.view.do_show_about_dialog()
-
-    def set_window_main_sensitive(self):
-        self.view.do_show_main_window(True)
-
     def get_host_architecture(self):
+        import commands
         status, output = commands.getstatusoutput('dpkg --print-architecture')
         assert status == 0
         return output
diff --git a/LiveMagic/controllers/wizard.py b/LiveMagic/controllers/wizard.py
index 9e4fd23..454400d 100644
--- a/LiveMagic/controllers/wizard.py
+++ b/LiveMagic/controllers/wizard.py
@@ -1,17 +1,23 @@
+import os
 import gtk
-import os.path
+import time
+import popen2
+import shutil
+import threading
+import subprocess
 
-from DebianLive import Config, utils
+from LiveMagic import utils
+from DebianLive import Config
+from DebianLive.utils import SourcesList, get_build_dir
 
 class WizardController(object):
-
     def on_wizard_apply(self, _):
-        build_dir = utils.get_build_dir()
+        build_dir = get_build_dir()
         data = self.view.get_wizard_completed_details()
 
         # Use cdebootstrap in some situations
         if os.path.exists('/usr/bin/cdebootstrap') and \
-                            data['distribution' in ('etch', 'lenny'):
+                                data['distribution'] in ('etch', 'lenny'):
             data['bootstrap'] = 'cdebootstrap'
 
         # Disabling caching
@@ -21,15 +27,39 @@ class WizardController(object):
         self.model = Config(build_dir, **data)
 
         self.view.do_dim_wizard()
-        self.do_show_build_window(lambda: gtk.main_quit())
 
-    def on_wizard_expert_mode_selected(self, _):
-        self.view.do_hide_wizard()
-        self.view.do_show_main_window()
+        os.chdir(build_dir)
+        build_for = '%d:%d' % (os.geteuid(), os.getegid())
+
+        def gain_superuser():
+            title = "Enter your password to continue"
+            text = "Live-magic requires superuser capabilities to build your Debian Live system."
+
+            for _ in range(3):
+                cmd = ['gksu', '--disable-grab', '--preserve-env',
+                    '--message', '<big><b>%s</b></big>\n\n%s' % (title, text), '--',
+                    utils.find_resource('live-magic'), '--build-for', build_for,
+                    '--kde-full-session', os.environ.get('KDE_FULL_SESSION', '-'),
+                    '--gnome-desktop-session-id', os.environ.get('GNOME_DESKTOP_SESSION_ID', '-')]
+                p = subprocess.Popen(cmd)
+
+                os.waitpid(p.pid, 0)
+                try:
+                    os.stat(os.path.join(self.model.dir, 'build-log.txt'))
+                    gtk.main_quit()
+                    return
+                except:
+                    pass
+
+            self.view.do_undim_wizard()
+            os.chdir('..')
+            shutil.rmtree(self.model.dir)
+
+        threading.Thread(target=gain_superuser).start()
 
     def get_suggested_mirror(self):
-        s = utils.SourcesList()
-        return s.get_mirror()
+        return SourcesList().get_mirror()
 
     def on_wizard_cancel(self, *args):
         if self.view.do_show_wizard_cancel_confirm_window():
+            gtk.main_quit()
diff --git a/LiveMagic/utils.py b/LiveMagic/utils.py
index b192eb5..d996cb7 100644
--- a/LiveMagic/utils.py
+++ b/LiveMagic/utils.py
@@ -3,6 +3,7 @@ import os
 def find_resource(resource):
     dirs = (
         os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'misc'),
+        os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
         '/usr/share/live-magic',
         '/usr/local/share/live-magic',
     )
diff --git a/LiveMagic/views/__init__.py b/LiveMagic/views/__init__.py
index 19346b3..3353670 100644
--- a/LiveMagic/views/__init__.py
+++ b/LiveMagic/views/__init__.py
@@ -11,7 +11,7 @@ class View(Main, Build, Hooks, Wizard):
     def __init__(self, controller):
         self.controller = controller
 
-        self.xml = gtk.glade.XML(find_resource('main.glade'))
+        self.xml = gtk.glade.XML(find_resource('live-magic.glade'))
         self.xml.signal_autoconnect(self.controller)
 
         Main.__init__(self)
diff --git a/LiveMagic/views/wizard.py b/LiveMagic/views/wizard.py
index 7a73c71..0ae89ef 100644
--- a/LiveMagic/views/wizard.py
+++ b/LiveMagic/views/wizard.py
@@ -60,6 +60,9 @@ class WizardView(object):
     def do_dim_wizard(self):
         self.asst.set_sensitive(False)
 
+    def do_undim_wizard(self):
+        self.asst.set_sensitive(True)
+
     def do_hide_wizard(self):
         self.asst.hide()
 
diff --git a/TODO b/TODO
index d7cf67f..61b34d1 100644
--- a/TODO
+++ b/TODO
@@ -6,10 +6,6 @@ Release blockers
 
  * Build process needs to be cancellable
 
- * Password entry problems
-   - Run the entire "build" process as root, passing original uid/gid
-     as arguments so that results can be used by the original user.
-
  * Ability to select location of build in the wizard
 
 Other

-- 
GUI front-end for Debian Live.



More information about the debian-live-changes mailing list