summaryrefslogtreecommitdiffstats
path: root/build_tools/l7/larch0/gui/controller.py
diff options
context:
space:
mode:
Diffstat (limited to 'build_tools/l7/larch0/gui/controller.py')
-rw-r--r--build_tools/l7/larch0/gui/controller.py466
1 files changed, 466 insertions, 0 deletions
diff --git a/build_tools/l7/larch0/gui/controller.py b/build_tools/l7/larch0/gui/controller.py
new file mode 100644
index 0000000..2025301
--- /dev/null
+++ b/build_tools/l7/larch0/gui/controller.py
@@ -0,0 +1,466 @@
+#!/usr/bin/env python
+#
+# controller.py - Manages file-system access and calling of larch scripts
+#
+# (c) Copyright 2010 Michael Towers (larch42 at googlemail dot com)
+#
+# This file is part of the larch project.
+#
+# larch is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# larch is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with larch; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+#----------------------------------------------------------------------------
+#2010.07.19
+
+import sys, os, pwd, traceback, __builtin__
+try:
+ import json as serialize
+except:
+ import simplejson as serialize
+from subprocess import Popen, PIPE, call
+import threading
+import re
+import SocketServer
+
+from config import *
+start_translator(switchC=False)
+
+
+exports = {}
+def add_exports(elist):
+ for key, method in elist:
+ exports[key] = method
+
+__builtin__.add_exports = add_exports
+
+
+def error0(message):
+ sys.stderr.write('>>ERROR>>' + message + '\n')
+ sys.stderr.flush()
+__builtin__.error0 = error0
+
+
+class Fs:
+ """Collect file system access methods in one class.
+ """
+ def __init__(self):
+ add_exports( (
+ ('fetch_layout', self.fetch_layout),
+ ('isfile', self.isfile),
+ ('isdir', self.isdir),
+ ('rm_rf', self.rm_rf),
+ ('get_partitions', self.get_partitions),
+ ('readfile', self.readfile),
+ ('savefile', self.savefile),
+ ('get_docs_url', self.get_docs_url),
+ ('oldsqf', self.oldsqf),
+ ('oldlocales', self.oldlocales),
+# ('getpath', self.getpath),
+ ('browse', self.browse))
+ )
+
+ def fetch_layout(self, layout_file):
+ fh = open(base_dir + '/gui/layouts/' + layout_file)
+ r = fh.read()
+ fh.close()
+ return (True, eval(r))
+
+ def rm_rf(self, path):
+ call(['rm', '-rf', self._getpath(path)])
+ return (True, None)
+
+
+# def exists(self, path):
+# return os.path.exists(self._getpath(path))
+
+# def copy(self, src, dst):
+# shutil.copytree(self._getpath(src), self._getpath(dst))
+
+
+ def oldsqf(self):
+ return (True,
+ os.path.isfile(self._getpath('install:' + CHROOT_SYSTEMSQF))
+ or os.path.isfile(self._getpath('install:' + CHROOT_DIR_MEDIUM
+ + '/larch/system.sqf')))
+
+ def oldlocales(self):
+ return (True, os.path.isdir(self._getpath('install:%s/locale'
+ % CHROOT_DIR_BUILD)))
+
+ def isfile(self, path):
+ return (True, os.path.isfile(self._getpath(path)))
+
+ def isdir(self, path):
+ return (True, os.path.isdir(self._getpath(path)))
+
+
+ def browse(self, path):
+ fpath = self._getpath(path)
+ if call(['mkdir', '-p', fpath]) == 0:
+ # Start file browser at fpath
+ call(project_manager.appget('filebrowser').replace('$', fpath)
+ + ' &', shell=True)
+ return (True, None)
+ else:
+ return (False, None)
+
+
+# def makedirs(self, path):
+# os.makedirs(self._getpath(path))
+# return (True, None)
+
+ def readfile(self, f):
+ f = self._getpath(f)
+ try:
+ fh = open(f)
+ r = fh.read()
+ fh.close()
+ return (True, r)
+ except:
+ return (False, _("Couldn't read file '%s'") % f)
+
+ def savefile(self, f, d):
+ f = self._getpath(f)
+ dir = os.path.dirname(f)
+ if not os.path.isdir(dir):
+ os.makedirs(dir)
+ try:
+ fh = open(f, "w")
+ fh.write(d)
+ fh.close()
+ return (True, None)
+ except:
+ return (False, _("Couldn't save file '%s'") % f)
+
+ def _getpath(self, f):
+ if f[0] != "/":
+ base, f = f.split(':')
+ f = '/' + f
+ if base == 'base':
+ f = base_dir + f
+ elif base == 'profile':
+ f = project_manager.profile_path + f
+ elif base == 'working':
+ f = project_manager.project_dir + f
+ else:
+ f = project_manager.get_ipath()[1] + f
+ return f
+
+ def get_docs_url(self, page):
+ if lang and (len(lang) > 1):
+ p = base_dir + ('/docs/%s/html/' % lang[0:2]) + page
+ if os.path.isfile(p):
+ return (True, p)
+ return (True, base_dir + '/docs/html/' + page)
+
+
+ def get_partitions(self):
+ """Get a list of available partitions (only unmounted ones
+ are included).
+ """
+ # First get a list of mounted devices
+ mounteds = []
+ fh = open('/etc/mtab')
+ for l in fh:
+ dev = l.split()[0]
+ if dev.startswith('/dev/sd'):
+ mounteds.append(dev[5:])
+ fh.close()
+ # Get a list of partitions
+ partlist = []
+ fh = open('/proc/partitions')
+ for l in fh:
+ fields = l.split()
+ if len(fields) == 4:
+ dev = fields[3]
+ if dev.startswith('sd') and (dev[-1] in '0123456789'):
+ size = (int(fields[2]) + 512) / 1024
+ if (size > 200) and (dev not in mounteds):
+ # Keep a tuple (partition, size in MiB)
+ partlist.append("%-12s %12s MiB"
+ % ('/dev/' + dev, size))
+ fh.close()
+ return (True, partlist)
+
+
+
+class LarchScripts:
+ """This class deals with calling the larch scripts.
+ As they must be run as root a separate dispatcher process, running as
+ root, is used to call the actual scripts. The dispatcher is started
+ using 'sudo'.
+ A call will be initiated from the gui, and is sent to the dispatcher,
+ which starts the process and returns the output when it is available.
+ If the script is interactive, it might also require input, which can be
+ passed via the dispatcher.
+ While reading output from the dispatcher the gui must remain responsive,
+ so that the view can be switched and the subprocess aborted, if desired.
+ To achieve this a separate thread is used for reading input from the
+ dispatcher, together with a mechanism for activating a handler in a
+ thread-safe way, 'ui.idle_add'.
+ """
+ def __init__(self):
+ self.larch_dispatcher = None # dispatcher subprocess
+ self.progress_waiting = None # used by progress widget
+
+
+ def call(self, cmd, arg=[], atend=None):
+ self.cmd = cmd
+ self.arg = arg
+ # Callback on completion:
+ self.atend = atend # returns True for automatic return to normal view
+ if self.larch_dispatcher:
+ self.runcmd()
+
+ else:
+ # Start a socket server to handle password requests
+ # Use a unix domain server in the abstract namespace
+ port = '\0larch-sudopw'
+ # Create the server
+ self.sserver = SocketServer.UnixStreamServer(port, MyHandler)
+ self.sst = threading.Thread(target=self.sserver.serve_forever,
+ args=())
+ self.sst.start()
+ # Handle one request
+ #self.sserver.handle_request()
+
+ # Start the larch dispatcher script
+ os.environ['SUDO_ASKPASS'] = base_dir + '/gui/askpass.py'
+ self._sudo_wait()
+ dispatcher = Popen(['sudo', '-A', '-k',
+ base_dir + '/gui/dispatcher.py'],
+ stdin=PIPE,
+ stdout=PIPE,
+ stderr=PIPE)
+
+ # And a thread to read its output
+ self.istream = dispatcher.stdin
+ self.estream = dispatcher.stderr
+ self.t = threading.Thread(target=self.readinput, args=(dispatcher,))
+ self.t.start()
+
+
+ def runcmd(self):
+ progress.start() # initialize progress widget
+ ui.runningtab(1) # switch view to progress widget
+ # Run command:
+ cx = '%s %s:%s\n' % (self.cmd, project_manager.project_dir,
+ serialize.dumps(self.arg))
+ logger.addLine('++' + cx)
+ self.istream.write(cx)
+ self.istream.flush()
+ r = self.geterr()
+ if r:
+ ui.command('infoDialog', r, 'BUG')
+
+
+ def geterr(self):
+ r = ""
+ while True:
+ rx = self.estream.readline()
+ if rx:
+ rx = rx.strip()
+ else:
+ break
+ if rx == '!+':
+ break
+ if r:
+ r +='\n'
+ r += rx
+ return r
+
+
+ def readinput(self, dispatcher):
+ ostream = dispatcher.stdout
+ while True:
+ line = ostream.readline()
+ if not line:
+ break
+ id, line = line.rstrip().split(':', 1)
+ try:
+ if line[0] == '=':
+ self._stop_server()
+ # The dispatcher has just started, make it available
+ self.larch_dispatcher = dispatcher
+ # Reenable the gui, and queue the command
+ ui.idle_add(self._dispatcher_started, self.runcmd)
+ continue
+ elif line[0] != '/':
+ line = serialize.loads(line)
+ except:
+ line = '[[%s]]' % line
+ ui.idle_add(self.addline, line) # cross-thread call
+
+ if self.larch_dispatcher == None:
+ self._stop_server()
+ ui.idle_add(self._dispatcher_started, None)
+
+
+
+ def _sudo_wait(self):
+ ui.command(':larch.busy', [':larch'], True)
+
+
+ def _dispatcher_started(self, cmd):
+ if cmd:
+ cmd()
+ else:
+ ui.command('infoDialog',
+ ("%s:\n %s" % (ui.data('authfail'), self.geterr())),
+ 'sudo')
+ ui.command(':larch.busy', [':larch'], False)
+
+
+ def _stop_server(self):
+ # Stop the password socket-server
+ self.sserver.shutdown()
+ self.sserver = None
+
+
+ def close(self):
+ if self.larch_dispatcher:
+ self.istream.write('quit\n')
+ self.istream.flush()
+ self.larch_dispatcher.wait()
+
+
+ def interrupt(self):
+ logger.addLine('--Terminate--')
+ self.istream.write('kill\n')
+ self.istream.flush()
+
+
+ def addline(self, message):
+ """A line has been received from the script.
+ This must be handled in the main thread.
+ The input lines are filtered for pacman, mksquashfs and mkisofs
+ progress output so that appropriate progress reports can be given.
+ """
+ if 'pacman:' in message:
+ progress.set(message[2:])
+ if message.endswith('|100'):
+ progress.set()
+ message = message.rsplit('|', 1)[0].rstrip()
+ else:
+ return
+
+ if 'mksquashfs:' in message:
+ progress.set(message[2:])
+ self.progress_waiting = message
+ return
+
+ if 'mkisofs:' in message:
+ progress.set(message[2:])
+ self.progress_waiting = ">_mkisofs: completed"
+ return
+
+ if self.progress_waiting:
+ progress.set()
+ progress.addLine(self.progress_waiting)
+ self.progress_waiting = None
+
+ progress.addLine(message)
+ if message[0] == '/':
+ # process finished:
+ auto = False
+ if self.atend:
+ auto = self.atend(int(message[1:]))
+ progress.end(auto)
+ self.cmd = None
+ elif message.startswith('?>'):
+ # a query (yes/no)
+ # Pop up the query
+ reply = '??YES' if ui.command('confirmDialog',
+ message[2:], self.cmd) else '??NO'
+ self.istream.write(reply + '\n')
+ self.istream.flush()
+
+
+ def archin(self, cmd, installrepos):
+ args = ['-p', project_manager.profile_path,
+ '-i', project_manager.get_ipath()[1],
+ '-c', project_manager.get('pacman_cache')]
+ rf = project_manager.project_dir + '/pacman.conf.repos'
+ if installrepos and os.path.isfile(rf):
+ args += ['-r', rf]
+ self.call('archin', args + cmd.split())
+
+
+ def larchify(self, oldsyssqf, oldlocales):
+ args = ['-p', project_manager.profile_path,
+ '-i', project_manager.get_ipath()[1],]
+ if oldsyssqf:
+ args.append('-o')
+ if oldlocales:
+ args.append('-l')
+ self.call('larchify', args)
+
+
+ def testmedium(self, path, atend):
+ self.call('live_iso', ['-T', '-S', path,], atend=atend)
+
+
+ def writemedium(self, path, args, dest=None):
+ if dest == 'BOOTISO':
+ args.append(path)
+ cmd = 'boot_iso'
+ else:
+ if path:
+ args += ['-S', path]
+ else:
+ args += ['-p', project_manager.profile_path]
+ args += ['-i', project_manager.get_ipath()[1]]
+ if dest != None:
+ args.append(dest)
+ cmd = 'live_part'
+ else:
+ cmd = 'live_iso'
+ self.call(cmd, args)
+
+
+
+class MyHandler(SocketServer.StreamRequestHandler):
+ def handle(self):
+ self._event = threading.Event()
+ # self.rfile is a file-like object created by the handler;
+ # we can now use e.g. readline() instead of raw recv() calls
+ data = self.rfile.readline().strip()
+ if data == 'pw-get':
+ ui.idle_add(self.dialog)
+ self._event.wait()
+ # Likewise, self.wfile is a file-like object used to write back
+ # to the client
+ self.wfile.write(self.pw)
+
+ def dialog(self):
+ ok, self.pw = ui.command('textLineDialog',
+ ui.data('getpw'),
+ 'sudo', '', True)
+ if not ok:
+ self.pw = ''
+ self._event.set()
+
+
+
+fs = Fs()
+
+def filesystem(key, *args):
+ return exports[key](*args)
+
+__builtin__.filesystem = filesystem
+__builtin__.larchscripts = LarchScripts()
+
+import project
+project_manager.init()
+