From ade10e0b8e5571e45cdd4800927c24597e2f7315 Mon Sep 17 00:00:00 2001 From: necoro <> Date: Sun, 12 Aug 2007 04:21:38 +0000 Subject: added listener/notify --- portato/__init__.py | 3 + portato/constants.py | 7 + portato/gui/gtk/__init__.py | 3 + portato/gui/gtk/windows.py | 111 +++++----- portato/gui/templates/portato.glade | 420 ++++++++++++++++++------------------ portato/helper.py | 17 +- portato/plistener.py | 127 +++++++++++ portato/plugin.py | 3 + portato/plugins/etc_proposals.py | 3 +- portato/plugins/noroot.py | 15 -- portato/plugins/notify.py | 23 ++ 11 files changed, 427 insertions(+), 305 deletions(-) create mode 100644 portato/plistener.py delete mode 100644 portato/plugins/noroot.py create mode 100644 portato/plugins/notify.py (limited to 'portato') diff --git a/portato/__init__.py b/portato/__init__.py index f10b9ef..57dd691 100644 --- a/portato/__init__.py +++ b/portato/__init__.py @@ -55,3 +55,6 @@ handler.setFormatter(formatter) logging.getLogger("portatoLogger").addHandler(handler) logging.getLogger("portatoLogger").setLevel(logging.DEBUG) logging.getLogger("portatoLogger").propagate = False + +from plistener import PListener +listener = PListener() diff --git a/portato/constants.py b/portato/constants.py index c21b0a5..309b774 100644 --- a/portato/constants.py +++ b/portato/constants.py @@ -38,6 +38,10 @@ These should be set during the installation. @type FRONTENDS: string[] @var STD_FRONTEND: the frontend uses as the default, i.e. if no other one is given on the cmdline @type STD_FRONTEND: string +@var SU_COMMAND: command to execute to "su" +@type SU_COMMAND: string +@var SOCKET: path to socket for communication between listener and GUI +@type SOCKET: string """ from os.path import join as pjoin @@ -60,3 +64,6 @@ LOCALE_DIR = "i18n/" FRONTENDS = ["gtk" ,"qt"] STD_FRONTEND = "gtk" + +SU_COMMAND = "gksu -D 'Portato'" +SOCKET = "/tmp/portato.socket" diff --git a/portato/gui/gtk/__init__.py b/portato/gui/gtk/__init__.py index 0714f39..ba49652 100644 --- a/portato/gui/gtk/__init__.py +++ b/portato/gui/gtk/__init__.py @@ -12,6 +12,7 @@ from gettext import lgettext as _ +from portato import listener from exception_handling import register_ex_handler def run (): @@ -27,6 +28,8 @@ def run (): except KeyboardInterrupt: pass + listener.close() + def show_ebuild (pkg): import gtk from portato import plugin diff --git a/portato/gui/gtk/windows.py b/portato/gui/gtk/windows.py index 74f7058..f0b7f1b 100644 --- a/portato/gui/gtk/windows.py +++ b/portato/gui/gtk/windows.py @@ -20,6 +20,7 @@ from subprocess import Popen from gettext import lgettext as _ # our backend stuff +from portato import listener from portato.helper import * from portato.constants import CONFIG_LOCATION, VERSION, APP_ICON from portato.backend import flags, system @@ -335,7 +336,7 @@ class PreferenceWindow (AbstractDialog): self.cfg.set("consolefont", font, section = "GTK") self.set_console_font(font) - gtk.link_button_set_uri_hook(lambda btn, x: Popen([self.cfg.get("browserCmd", section = "GUI"), btn.get_uri()])) + gtk.link_button_set_uri_hook(lambda btn, x: listener.send_cmd([self.cfg.get("browserCmd", section = "GUI"), btn.get_uri()])) def cb_ok_clicked(self, button): """Saves, writes to config-file and closes the window.""" @@ -717,20 +718,14 @@ class PackageTable: def cb_package_emerge_clicked (self, button): """Callback for pressed emerge-button. Adds the package to the EmergeQueue.""" - if not am_i_root(): - not_root_dialog() - else: - self._update_keywords(True) - self.main.notebook.set_current_page(self.main.QUEUE_PAGE) + self._update_keywords(True) + self.main.notebook.set_current_page(self.main.QUEUE_PAGE) return True def cb_package_unmerge_clicked (self, button): """Callback for pressed unmerge-button clicked. Adds the package to the UnmergeQueue.""" - if not am_i_root(): - not_root_dialog() - else: - self._update_keywords(False) - self.main.notebook.set_current_page(self.main.QUEUE_PAGE) + self._update_keywords(False) + self.main.notebook.set_current_page(self.main.QUEUE_PAGE) return True def cb_package_ebuild_clicked(self, button): @@ -900,7 +895,7 @@ class MainWindow (Window): raise self.cfg.modify_external_configs() - gtk.link_button_set_uri_hook(lambda btn, x: Popen([self.cfg.get("browserCmd", section = "GUI"), btn.get_uri()])) + gtk.link_button_set_uri_hook(lambda btn, x: listener.send_cmd([self.cfg.get("browserCmd", section = "GUI"), btn.get_uri()])) # set plugins and plugin-menu splash(_("Loading Plugins")) @@ -1177,47 +1172,43 @@ class MainWindow (Window): return True def cb_update_clicked (self, action): - if not am_i_root(): - not_root_dialog() - else: + def __update(): - def __update(): - - def cb_idle_append (pkg, unmask): - self.queue.append(pkg.get_cpv(), unmask = unmask) - return False + def cb_idle_append (pkg, unmask): + self.queue.append(pkg.get_cpv(), unmask = unmask) + return False - def cb_idle_unmask_dialog (e, updating): - if unmask_dialog(e[0]) == gtk.RESPONSE_YES: - for pkg, old_pkg in updating: - self.queue.append(pkg.get_cpv(), unmask = True) - return False + def cb_idle_unmask_dialog (e, updating): + if unmask_dialog(e[0]) == gtk.RESPONSE_YES: + for pkg, old_pkg in updating: + self.queue.append(pkg.get_cpv(), unmask = True) + return False - def cb_idle_blocked(e): - blocked_dialog(e[0], e[1]) - self.queue.remove_children(self.queue.emergeIt) - return False + def cb_idle_blocked(e): + blocked_dialog(e[0], e[1]) + self.queue.remove_children(self.queue.emergeIt) + return False - watch = gtk.gdk.Cursor(gtk.gdk.WATCH) - self.window.window.set_cursor(watch) + watch = gtk.gdk.Cursor(gtk.gdk.WATCH) + self.window.window.set_cursor(watch) + try: + updating = system.update_world(newuse = self.cfg.get_boolean("newuse"), deep = self.cfg.get_boolean("deep")) + debug("updating list: %s --> length: %s", [(x.get_cpv(), y.get_cpv()) for x,y in updating], len(updating)) try: - updating = system.update_world(newuse = self.cfg.get_boolean("newuse"), deep = self.cfg.get_boolean("deep")) - debug("updating list: %s --> length: %s", [(x.get_cpv(), y.get_cpv()) for x,y in updating], len(updating)) try: - try: - for pkg, old_pkg in updating: - gobject.idle_add(cb_idle_append, pkg, False) - except PackageNotFoundException, e: - gobject.idle_add(cb_idle_unmask_dialog, e, updating) - - except BlockedException, e: - gobject.idle_add(cb_idle_blocked(e)) - - if len(updating): self.doUpdate = True - finally: - self.window.window.set_cursor(None) + for pkg, old_pkg in updating: + gobject.idle_add(cb_idle_append, pkg, False) + except PackageNotFoundException, e: + gobject.idle_add(cb_idle_unmask_dialog, e, updating) - GtkThread(name="Update-Thread", target=__update).start() + except BlockedException, e: + gobject.idle_add(cb_idle_blocked(e)) + + if len(updating): self.doUpdate = True + finally: + self.window.window.set_cursor(None) + + GtkThread(name="Update-Thread", target=__update).start() return True @@ -1245,26 +1236,20 @@ class MainWindow (Window): return True def cb_sync_clicked (self, action): - if not am_i_root(): - not_root_dialog() - else: - self.notebook.set_current_page(self.CONSOLE_PAGE) - cmd = self.cfg.get("syncCmd") + self.notebook.set_current_page(self.CONSOLE_PAGE) + cmd = self.cfg.get("syncCmd") - if cmd != "emerge --sync": - cmd = cmd.split() - self.queue.sync(cmd) - else: - self.queue.sync() + if cmd != "emerge --sync": + cmd = cmd.split() + self.queue.sync(cmd) + else: + self.queue.sync() def cb_save_flags_clicked (self, action): - if not am_i_root(): - not_root_dialog() - else: - flags.write_use_flags() - flags.write_testing() - flags.write_masked() - + flags.write_use_flags() + flags.write_testing() + flags.write_masked() + @Window.watch_cursor def cb_reload_clicked (self, action): """Reloads the portage settings and the database.""" diff --git a/portato/gui/templates/portato.glade b/portato/gui/templates/portato.glade index fe57c9a..77f8a8f 100644 --- a/portato/gui/templates/portato.glade +++ b/portato/gui/templates/portato.glade @@ -450,107 +450,52 @@ 5 3 - + True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 1 + True - + + True + True + Installed + 0 + True + + + + False + - - - 3 - 1 - 2 - GTK_EXPAND - - - - - - True - True - <b>Installed, but not in portage anymore</b> - True - - - 3 - 2 - 3 - GTK_FILL - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 5 - + True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - GTK_POLICY_NEVER - GTK_POLICY_AUTOMATIC - - - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - True - False - - - + True + Masked + 0 + True + - False + False + 1 - + True - False - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - - - True - - + True + Testing + 0 + True + - 1 + False + 2 - - 3 - 3 - 4 - 5 - 5 - - - - - True - GTK_JUSTIFY_CENTER - True - - - 3 - GTK_FILL - - 10 - - - - - True - True - <span foreground='red'><b>MISSING KEYWORD</b></span> - True - 3 2 @@ -617,51 +562,90 @@ - + True - 1 - True + True + <span foreground='red'><b>MISSING KEYWORD</b></span> + True + + + 3 + 2 + 3 + GTK_FILL + + + + + True + GTK_JUSTIFY_CENTER + True + + + 3 + GTK_FILL + + 10 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 5 - + True - True - Installed - 0 - True - + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + GTK_POLICY_NEVER + GTK_POLICY_AUTOMATIC + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + False + + + - False + False - + True - True - Masked - 0 - True - + False + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + True + + - False 1 - - - True - True - Testing - 0 - True - - - - False - 2 - - + + + 3 + 3 + 4 + 5 + 5 + + + + + True + True + <b>Installed, but not in portage anymore</b> + True 3 @@ -670,6 +654,22 @@ GTK_FILL + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + + + + 3 + 1 + 2 + GTK_EXPAND + + + False @@ -1147,189 +1147,189 @@ - + True + 0 + 5 + <u><i>Masking Keywords</i></u> + True + True - 1 - 2 - 3 - 4 + 7 + 8 + 5 - + True 0 - File name to use, if package.use is a directory: + 5 + <u><i>Testing Keywords</i></u> + True True - 3 - 4 + 4 + 5 + 5 - + True - Add only exact version to package.use - 0 - True + 0 + 5 + <u><i>Use-Flags</i></u> + True + True + + + 1 + 2 + 6 + + + + + True + + + True + 0 + GTK_SHADOW_OUT + + + True + 0 + <u>You may use the following placeholders:</u> + +<i>$(cat)</i>: category +<i>$(pkg)</i>: package name +<i>$(cat-1)/$(cat-2)</i>: first/second part of the category + True + + + + + + label_item + + + + 2 - 2 - 3 - + True - Add only exact version to package.keywords + Add only exact version to package.mask/package.unmask 0 True 2 - 5 - 6 + 8 + 9 - + True 0 - File name to use, if package.keywords is a directory: + File name to use, if package.mask/package.unmask is a directory: True - 6 - 7 + 9 + 10 - + True 1 2 - 6 - 7 + 9 + 10 - + True 1 2 - 9 - 10 + 6 + 7 - + True 0 - File name to use, if package.mask/package.unmask is a directory: + File name to use, if package.keywords is a directory: True - 9 - 10 + 6 + 7 - + True - Add only exact version to package.mask/package.unmask + Add only exact version to package.keywords 0 True 2 - 8 - 9 + 5 + 6 - + True - - - True - 0 - GTK_SHADOW_OUT - - - True - 0 - <u>You may use the following placeholders:</u> - -<i>$(cat)</i>: category -<i>$(pkg)</i>: package name -<i>$(cat-1)/$(cat-2)</i>: first/second part of the category - True - - - - - - label_item - - - - + Add only exact version to package.use + 0 + True 2 + 2 + 3 - - True - 0 - 5 - <u><i>Use-Flags</i></u> - True - True - - - 1 - 2 - 6 - - - - + True 0 - 5 - <u><i>Testing Keywords</i></u> - True + File name to use, if package.use is a directory: True - 4 - 5 - 5 + 3 + 4 - + True - 0 - 5 - <u><i>Masking Keywords</i></u> - True - True - 7 - 8 - 5 + 1 + 2 + 3 + 4 diff --git a/portato/helper.py b/portato/helper.py index 754f566..cb3289c 100644 --- a/portato/helper.py +++ b/portato/helper.py @@ -14,7 +14,7 @@ Some nice functions used in the program. """ -import os, signal, logging +import os, signal, logging, grp debug = logging.getLogger("portatoLogger").debug info = logging.getLogger("portatoLogger").info @@ -43,21 +43,6 @@ def send_signal_to_group (sig): pgid = os.getpgrp() os.killpg(pgid, sig) -def am_i_root (): - """Returns True if the current user is root, False otherwise. - @rtype: boolean""" - - from plugin import hook - - @hook("am_i_root") - def __am_i_root(): - if os.getuid() == 0: - return True - else: - return False - - return __am_i_root() - def flatten (listOfLists): """Flattens the given list of lists. diff --git a/portato/plistener.py b/portato/plistener.py new file mode 100644 index 0000000..29054b2 --- /dev/null +++ b/portato/plistener.py @@ -0,0 +1,127 @@ +# -*- coding: utf-8 -*- +# +# File: portato/gui/gtk/plistener.py +# This file is part of the Portato-Project, a graphical portage-frontend. +# +# Copyright (C) 2007 René 'Necoro' Neumann +# This is free software. You may redistribute copies of it under the terms of +# the GNU General Public License version 2. +# There is NO WARRANTY, to the extent permitted by law. +# +# Written by René 'Necoro' Neumann + +import socket, os +from subprocess import Popen +from gettext import lgettext as _ + +try: + import pynotify +except ImportError: + pynotify = None + +from constants import SOCKET, APP +from helper import debug, warning + +class PListener (object): + """This class handles the communication between the "listener" and the GUI. + This listener starts programs as the user while the GUI runs as root. + + @ivar _recv: listener socket + @type _recv: socket.socket + @ivar _send: sender socket + @type _send: socket.socket""" + + def set_recv (self): + self._recv = socket.socket(socket.AF_UNIX) + + try: + self._recv.bind(SOCKET) + except socket.error, e: + if int(e[0]) == 98: # already existing - delete + os.unlink(SOCKET) + self._recv.bind(SOCKET) + else: + raise + + self._recv.listen(1) + con, addr = self._recv.accept() + + while True: + try: + len = con.recv(4) + string = con.recv(int(len)) + + data = string.split("\0") + debug(data) + + if data[0] == "notify": + self.do_notify(*data[1:]) + elif data[0] == "cmd": + self.do_cmd(data[1:]) + elif data[0] == "close": + break + except KeyboardInterrupt: + pass + + con.close() + self._recv.close() + + def do_cmd (self, cmdlist): + """Starts a command as the user. + + @param cmdlist: list of command (options) + @type cmdlist: string[]""" + + Popen(cmdlist) + + def do_notify(self, base, descr, icon, urgency): + """Displays a notify. + This will do nothing if pynotify is not present and/or root is running the listener.""" + + if pynotify and not os.getuid == 0: + if not pynotify.is_initted(): + pynotify.init(APP) + + n = pynotify.Notification(base, descr, icon) + n.set_urgency(int(urgency)) + n.show() + + def set_send (self): + self._send = socket.socket(socket.AF_UNIX) + try: + self._send.connect(SOCKET) + except socket.error, e: + if e[0] in [111, 2]: # can't connect + warning(_("Listener has not been started.")) + self._send = None + else: + raise + + def __send (self, string): + self._send.sendall("%4d" % len(string)) + self._send.sendall(string) + + def send_notify (self, base = "", descr = "", icon = "", urgency = None): + if self._send is None: + self.do_notify(base, descr, icon, urgency) + else: + string = "\0".join(["notify", base, descr, icon]) + + if urgency is not None: + string += "\0%d" % urgency + else: + string += "\0" + + self.__send(string) + + def send_cmd (self, cmdlist): + if self._send is None: + self.do_cmd(cmdlist) + else: + self.__send("\0".join(["cmd"] +cmdlist)) + + def close (self): + if self._send is not None: + self.__send("close") + self._send.close() + os.unlink(SOCKET) diff --git a/portato/plugin.py b/portato/plugin.py index 3b065b9..baa5d0c 100644 --- a/portato/plugin.py +++ b/portato/plugin.py @@ -133,6 +133,9 @@ class Hook: @param connects: the list of 's @type connects: NodeList""" + if not connects: # no connects - assume "before" connect + self.connects.append(Connect(self, "before", None)) + for c in connects: type = c.getAttribute("type") if type == '': diff --git a/portato/plugins/etc_proposals.py b/portato/plugins/etc_proposals.py index 81370e0..f094599 100644 --- a/portato/plugins/etc_proposals.py +++ b/portato/plugins/etc_proposals.py @@ -13,6 +13,7 @@ from portato.helper import * from portato.backend import system +import os from subprocess import Popen from gettext import lgettext as _ from etcproposals.etcproposals_lib import EtcProposals, __version__ @@ -64,7 +65,7 @@ def etc_prop (*args, **kwargs): error(_("Cannot start etc-proposals. No graphical frontend installed!")) def etc_prop_menu (*args, **kwargs): - if am_i_root(): + if os.getuid() == 0: if float(__version__) < 1.1: Popen(PROG) else: diff --git a/portato/plugins/noroot.py b/portato/plugins/noroot.py deleted file mode 100644 index a28ef85..0000000 --- a/portato/plugins/noroot.py +++ /dev/null @@ -1,15 +0,0 @@ -# -*- coding: utf-8 -*- -# -# File: portato/plugins/noroot.py -# This file is part of the Portato-Project, a graphical portage-frontend. -# -# Copyright (C) 2007 René 'Necoro' Neumann -# This is free software. You may redistribute copies of it under the terms of -# the GNU General Public License version 2. -# There is NO WARRANTY, to the extent permitted by law. -# -# Written by René 'Necoro' Neumann - -def i_am_root (*args): - """Pretend we are root.""" - return True diff --git a/portato/plugins/notify.py b/portato/plugins/notify.py new file mode 100644 index 0000000..5e4a577 --- /dev/null +++ b/portato/plugins/notify.py @@ -0,0 +1,23 @@ +from gettext import lgettext as _ +import pynotify + +from portato import listener + +from portato.helper import warning, error, debug +from portato.constants import APP_ICON, APP + +def notify (retcode, **kwargs): + if retcode is None: + warning(_("Notify called while process is still running!")) + else: + icon = APP_ICON + if retcode == 0: + text = "Emerge finished!" + descr = "" + urgency = pynotify.URGENCY_NORMAL + else: + text = "Emerge failed!" + descr = "Error Code: %d" % retcode + urgency = pynotify.URGENCY_CRITICAL + + listener.send_notify(base = text, descr = descr, icon = icon, urgency = urgency) -- cgit v1.2.3-54-g00ecf