#!/usr/bin/python # -*- coding: utf-8 -*- """ system-config-services: This module contains the Gui class which contains the methods pertaining to the gui only """ # serviceconf.py # Copyright © 2002-2006 Red Hat, Inc. # Authors: Tim Powers # Bill Nottingham # Dan Walsh # Nils Philippsen # Florian Festi # # This program 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. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import signal import sys try: import gtk except RuntimeError,e: print "Unable to initialize graphical environment. Most likely cause of failure" print "is that the tool was not run using a graphical environment. Please either" print "start your graphical user interface or set your DISPLAY variable." print "Caught exception: %s" % e sys.exit(-1) domain = "system-config-services" appPath="/usr/share/%s" % domain if not appPath in sys.path: sys.path.append(appPath) rhplPath="/usr/lib/python%d.%d/site-packages/rhpl" % (sys.version_info[0], sys.version_info[1]) if not rhplPath in sys.path: sys.path.append(rhplPath) rhplPath="/usr/lib64/python%d.%d/site-packages/rhpl" % (sys.version_info[0], sys.version_info[1]) if not rhplPath in sys.path: sys.path.append(rhplPath) import gtk.glade import os import string import checklist from servicemethods import * from rhpl.translate import _, N_, cat ## ## I18N ## import gettext gettext.bindtextdomain(domain, "/usr/share/locale") gettext.textdomain(domain) try: gettext.install(domain, "/usr/share/locale", 1) except IOError: import __builtin__ __builtin__.__dict__['_'] = unicode _=gettext.gettext quitting = 0 VERSION = "0.9.4" def verify_delete(arg): message=_("Are you sure you want to delete the %s?") % arg dlg = gtk.MessageDialog(None, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_YES_NO, message) dlg.set_position(gtk.WIN_POS_MOUSE) dlg.show_all() rc = dlg.run() dlg.destroy() return rc def error_dialog(message, dialog_type=gtk.MESSAGE_WARNING): dialog = gtk.MessageDialog(None, gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL, dialog_type, gtk.BUTTONS_OK, message) dialog.set_position(gtk.WIN_POS_MOUSE) dialog.run() dialog.destroy() def find_browser(): try: path = '/usr/bin/mozilla' os.stat(path) return path except: try: path = '/usr/bin/galeon' os.stat(path) return path except: try: path = '/usr/bin/konqueror' os.stat(path) return path except: return None def idle_func(): while gtk.events_pending(): if gtk.__dict__.has_key("main_iteration"): gtk.main_iteration() else: gtk.mainiteration() class RunlevelCheckList(checklist.CheckList): def __init__(self): checklist.CheckList.__init__(self, 7) self.set_column_visible(0, True) def set_column_visible(self, column, visible): checklist.CheckList.set_column_visible(self, column, visible) not_visible_before = True for i in range(0, 7): acol = self.get_column(i) if acol: if not_visible_before: acol.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE) acol.set_alignment(1.0) for cr in acol.get_cell_renderers(): cr.set_property('xalign', 0.7) self.set_column_title(i, _('Runlevel %d') % i) else: acol.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) acol.set_alignment(0.5) for cr in acol.get_cell_renderers(): cr.set_property('xalign', 0.5) self.set_column_title(i, '%d' % i) if acol.get_visible(): not_visible_before = False class Gui: def destroy(self, args): if gtk.__dict__.has_key("main_quit"): gtk.main_quit() else: gtk.mainquit() def uicallback(self): while gtk.events_pending(): if gtk.__dict__.has_key("main_iteration"): gtk.main_iteration() else: gtk.mainiteration() """This class handles everything gui for the system-config-services application""" def __init__(self): signal.signal(signal.SIGINT, signal.SIG_DFL) # make sure threading is disabled try: from gtk import _disable_gdk_threading _disable_gdk_threading() except ImportError: pass self.dirty=0 self.previous=None self.services = Services(self.uicallback) gtk.glade.bindtextdomain(domain) if os.access("serviceconf.glade", os.R_OK) == 1: self.xml = gtk.glade.XML("serviceconf.glade", domain=domain) else: self.xml = gtk.glade.XML("/usr/share/system-config-services/serviceconf.glade", domain=domain) # map the event signals to specific methods self.xml.signal_autoconnect( { 'on_winMain_delete_event' : self.quit, "on_mnuRescan_activate" : self.on_mnuRescan_activate, "on_mnuSave_activate" : self.on_mnuSave_clicked, "on_mnuRevert_activate" : self.on_mnuRevert_clicked, "on_mnuExit_activate" : self.quit, "on_add_service_activate" : self.on_add_service_clicked, "on_delete_service_activate" : self.on_delete_service_clicked, "on_mnuStart_activate" : self.on_btnStart_clicked, "on_mnuStop_activate" : self.on_btnStop_clicked, "on_mnuRestart_activate" : self.on_btnRestart_clicked, "on_pmnStart_activate" : self.on_btnStart_clicked, "on_pmnStop_activate" : self.on_btnStop_clicked, "on_pmnRestart_activate" : self.on_btnRestart_clicked, "on_mnuManual_activate" : self.on_mnuManual_activate, "on_edit_runlevel" : self.on_edit_runlevel, "on_optRL3_toggled" : self.on_optRL3_toggled, "on_optRL4_toggled" : self.on_optRL4_toggled, "on_optRL5_toggled" : self.on_optRL5_toggled , "on_optRLA_toggled" : self.on_optRLA_toggled , "on_pmnStart_activate" : self.on_btnStart_clicked, "on_pmnStop_activate" : self.on_btnStop_clicked, "on_pmnRestart_activate" : self.on_btnRestart_clicked } ) # main window self.winMain = self.xml.get_widget("winMain") self.winMain.connect("destroy", self.destroy) self.winMain.realize () # menu items self.mnuRescan = self.xml.get_widget("mnuRescan") self.mnuSave = self.xml.get_widget("mnuSave") self.mnuRevert = self.xml.get_widget("mnuRevert") self.mnuExit = self.xml.get_widget("mnuExit") self.mnuAbout = self.xml.get_widget("mnuAbout") self.mnuStart = self.xml.get_widget("mnuStart") self.mnuStop = self.xml.get_widget("mnuStop") self.mnuRestart = self.xml.get_widget("mnuRestart") self.optRL3 = self.xml.get_widget("optRL3") self.optRL4 = self.xml.get_widget("optRL4") self.optRL5 = self.xml.get_widget("optRL5") self.optRLA = self.xml.get_widget("optRLA") #toolbars #self.tbrSave = self.xml.get_widget("tbrSave") self.btnStart = self.xml.get_widget('btnStart') self.xml.signal_connect('on_btnStart_clicked', self.on_btnStart_clicked) self.btnStop = self.xml.get_widget('btnStop') self.xml.signal_connect('on_btnStop_clicked', self.on_btnStop_clicked) self.btnRestart = self.xml.get_widget('btnRestart') self.xml.signal_connect('on_btnRestart_clicked', self.on_btnRestart_clicked) self.btnSave = self.xml.get_widget('btnSave') self.xml.signal_connect('on_btnSave_clicked', self.on_mnuSave_clicked) self.btnSave.get_children()[0].get_children()[0].get_children()[1].set_use_underline(True) self.btnRevert = self.xml.get_widget('btnRevert') self.xml.signal_connect('on_btnRevert_clicked', self.on_mnuRevert_clicked) self.btnRevert.get_children()[0].get_children()[0].get_children()[1].set_use_underline(True) self.srvTypeNotebook = self.xml.get_widget('srvTypeNotebook') self.srvTypeNotebook.connect("switch-page", self.on_srvTypeNotebook_switch_page) #### background services #### # the textbox self.bgTxtDesc = self.xml.get_widget("bgTxtDesc") self.bgTxtDescBuffer = gtk.TextBuffer(None) self.bgTxtDesc.set_buffer(self.bgTxtDescBuffer) # the statusbox self.bgTxtStatus = self.xml.get_widget("bgTxtStatus") self.bgTxtStatusBuffer = gtk.TextBuffer(None) self.bgTxtStatus.set_buffer(self.bgTxtStatusBuffer) self.lblRunlevel = self.xml.get_widget("lblRunlevel") self.lblEditing = self.xml.get_widget("lblEditing") self.editing_runlevel = self.services.get_runlevel() self.pmnStart = self.xml.get_widget("pmnStart") self.pmnStop = self.xml.get_widget("pmnStop") self.pmnRestart = self.xml.get_widget("pmnRestart") # the CheckList which will display whether the service # is enabled, and the service names self.bgListServices = RunlevelCheckList() self.xml.get_widget("bgScrolledWindow").add(self.bgListServices) self.bgListServices.show() # initialize this to the runlevel we are running in. It will # change when one of the optRL[3-6] are selected self.lblRunlevel.set_text(_("Currently Running in Runlevel: ") + str (self.editing_runlevel)) self.bgListServices.get_selection().connect("changed", self.changed, self.bgListServices) self.bgListServices.connect("button_press_event", self.local_button_press_cb) self.popup_menu = gtk.MenuItem() self.optRL3.set_active(0) self.optRL4.set_active(0) self.optRL5.set_active(0) self.optRLA.set_active(0) self.save_revert_sensitive(0) #### xinetd/"on demand" services #### # the textbox self.odTxtDesc = self.xml.get_widget("odTxtDesc") self.odTxtDescBuffer = gtk.TextBuffer(None) self.odTxtDesc.set_buffer(self.odTxtDescBuffer) # the statusbox self.odTxtStatus = self.xml.get_widget("odTxtStatus") self.odTxtStatusBuffer = gtk.TextBuffer(None) self.odTxtStatus.set_buffer(self.odTxtStatusBuffer) # the CheckList which will display whether the service # is enabled, and the service names self.odListServices = checklist.CheckList() self.xml.get_widget("odScrolledWindow").add(self.odListServices) self.odListServices.get_selection().connect("changed", self.changed, self.odListServices) self.odListServices.show() # This is a sentry self.already_init = 0 self.populateList() # XXX self.toggled_service(0, 0, self.bgListServices) #self.toggled_service(0, 0, self.odListServices) self.bgListServices.get_selection().select_path((0,)) self.changed(self.bgListServices.get_selection(), self.bgListServices) #### select runlevel #### map(lambda x: self.bgListServices.set_column_visible(x, 0), range(0, 7)) if self.editing_runlevel == "3" or self.editing_runlevel == "2" or self.editing_runlevel == "1": self.bgListServices.set_column_visible(3, 1) self.optRL3.set_active(1) elif self.editing_runlevel == "4" : self.bgListServices.set_column_visible(4, 1) self.optRL4.set_active(1) else: self.bgListServices.set_column_visible(5, 1) self.optRL5.set_active(1) for s in (self.bgListServices, self.odListServices): for i in range(0, s.num_checkboxes): s.checkboxrenderer[i].connect("toggled", self.toggled_service, i, s) self.winMain.show() #----------------------------------------------------------------------------- # Methods assiciated with populating the checklist #----------------------------------------------------------------------------- def populateList(self): """Populates {bg,od}ListServices with the service names, whether it is configured, and service descriptions""" # sentinel so that we don't have selected rows while we are updating bgpath = (0,) odpath = (0,) result = self.bgListServices.get_selection().get_selected() try: (model, iter) = result bgpath = model.get_path(iter) except: pass result = self.odListServices.get_selection().get_selected() try: (model, iter) = result odpath = model.get_path(iter) except: pass self.am_updating = 1 self.winMain.get_toplevel().window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH)) self.winMain.set_sensitive(0) self.bgListServices.clear() self.odListServices.clear() if self.already_init == 0: self.services.get_service_lists() self.lblEditing.set_text(_("Editing Runlevel: ") + str (self.editing_runlevel)) self.already_init = 1 for service in self.services: if service.hide: continue self.bgListServices.append_row(service.name, service.get_runlevels()) for service in self.services.xinetd_services(): if service.hide: continue self.odListServices.append_row(service.name, service.get_runlevels()) self.winMain.set_sensitive(1) self.winMain.get_toplevel().window.set_cursor(gtk.gdk.Cursor(gtk.gdk.LEFT_PTR)) self.am_updating = 0 self.bgListServices.get_selection().select_path(bgpath) self.odListServices.get_selection().select_path(odpath) def changed(self, selection, list): result = selection.get_selected() if result != None: (model, iter) = result if iter != None: row = model.get_path(iter)[0] self.text_in_row = list.get_text(int(row), list.num_checkboxes) if list == self.bgListServices: desc = self.bgTxtDescBuffer status = self.bgTxtStatusBuffer elif list == self.odListServices: desc = self.odTxtDescBuffer status = self.odTxtStatusBuffer self.set_desc_buffer(desc) self.set_status_buffer(status) def set_desc_buffer(self, buf): # set the text in {bg,od}TxtDesc service = self.services[self.text_in_row] buf.set_text(service.description) def set_status_buffer(self, buf): # set the text in {bg,od}TxtStatus print self.text_in_row if not self.services.has_key(self.text_in_row): status = UNKNOWN message = _("Unknown") else: service = self.services[self.text_in_row] message="" if not service.is_xinetd_service(): # background service/daemon status = service.status message = service.status_message else: # on demand/xinetd service status = UNKNOWN message = self.services["xinetd"].status_message print message buf.set_text(message) def toggled_service(self, toggle, row, column, list): """Updates self.services if buttons are toggled in {bg,od}ListServices""" #make sure we aren't updating if self.am_updating != 1: row = int(row) self.text_in_row = list.get_text(row, list.num_checkboxes) service = self.services[self.text_in_row] enabled = not toggle.get_active() # we are before the change? service.set_in_runlevels(enabled, column) changed = self.services.is_changed() self.save_revert_sensitive(changed) return self.text_in_row #---------------------------------------------------------------------------- # Methods pertaining to the "File" menu items # The "Save Changes", "Revert to Last Save" and "Quit" menu items are handled # elsewhere by on_mnuSave_clicked(), on_mnuRevert_clicked(), and # "Exit" just calls gtk.main_quit() #---------------------------------------------------------------------------- def on_mnuRescan_activate(self,args): """clears bgListServices, and reruns populateList ()""" # set the cursor while we are busy self.already_init = 0 self.populateList() #---------------------------------------------------------------------------- # Methods pertaining to the "Edit Runlevel" menu items #---------------------------------------------------------------------------- def set_editing_runlevels(self, button, title, runlevels): if self.previous==button: return if button.get_active() != True: self.previous=button return if self.check_dirty() == gtk.RESPONSE_CANCEL: self.previous.set_active(1) return True self.editing_runlevel = title self.lblEditing.set_text(_("Editing Runlevel: ") + title) if isinstance(runlevels, int): runlevels = [runlevels] self.bgListServices.set_headers_visible(0) else: self.bgListServices.set_headers_visible(1) rl_map = map(lambda x: 0, range(0,7)) for rl in runlevels: rl_map[rl] = 1 for i in range(0,7): if self.bgListServices.get_column_visible(i) != rl_map[i]: self.bgListServices.set_column_visible(i, rl_map[i]) self.populateList() def on_optRL3_toggled(self, button): """calls populateList() to repopulate the checklist for runlevel 3""" return self.set_editing_runlevels(button, "3", 3) def on_optRL4_toggled(self, button): """calls populateList() to repopulate the checklist for runlevel 4""" return self.set_editing_runlevels(button, "4", 4) def on_optRL5_toggled(self, button): """calls populateList() to repopulate the checklist for runlevel 5""" return self.set_editing_runlevels(button, "5", 5) def on_optRLA_toggled(self, button): """calls populateList() to repopulate the checklist for all runlevels""" return self.set_editing_runlevels(button, _("All"), [3, 4, 5]) def on_edit_runlevel(self, button): self.optRL3.set_sensitive(not self.dirty) self.optRL4.set_sensitive(not self.dirty) self.optRL5.set_sensitive(not self.dirty) self.optRLA.set_sensitive(not self.dirty) def check_dirty(self): rc=gtk.RESPONSE_YES """Check to see if the user has any unsaved changes.""" if self.dirty: dlg = self.xml.get_widget("saveDialog") rc = dlg.run() dlg.hide() if rc==gtk.RESPONSE_YES: try: self.on_mnuSave_clicked(None) except IOError: pass if rc!=gtk.RESPONSE_CANCEL: self.dirty=0 return rc #---------------------------------------------------------------------------- # Methods pertaining to the "Help" menu items, there will be more here # once I have "real" help :) #---------------------------------------------------------------------------- def on_mnuAbout_activate(self,Dummy): """Just a silly about dialog""" dlg = gtk.MessageDialog(None, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, _("System Services Configuration Tool VERSION\n Copyright © 2002-2006 " "Red Hat, Inc.\n Tim Powers \n " "Bill Nottingham \n Dan Walsh \n " "Brent Fox \n Nils Philippsen \n ")) dlg.set_title(_("About")) dlg.set_default_size(100, 100) dlg.set_position(gtk.WIN_POS_CENTER) dlg.set_border_width(2) dlg.set_modal(True) dlg.set_transient_for(self.winMain) iconPixbuf = None try: iconPixbuf = gtk.gdk.pixbuf_new_from_file("/usr/share/system-config-services/system-config-services.png") except: pass if iconPixbuf: dlg.set_icon(iconPixbuf) rc = dlg.run() dlg.destroy() #---------------------------------------------------------------------------- # Methods pertaining to the "Help" menu items, there will be more here # once I have "real" help :) #---------------------------------------------------------------------------- def on_mnuManual_activate(self,args): help_page = "file:///usr/share/doc/system-config-services-" + VERSION + "/system-config-services.xml" path = "/usr/bin/yelp" if path == None: dlg = gtk.MessageDialog(None, 0, gtk.MESSAGE_WARNING, gtk.BUTTONS_OK, (_("Help is not available."))) dlg.set_position(gtk.WIN_POS_CENTER) dlg.run() dlg.destroy() return pid = os.fork() if not pid: os.execv(path, [path, help_page]) def on_selectCursor(self,args): """calls get_service_action_results to start the selected initscript""" print "on_selectCursor" #---------------------------------------------------------------------------- # Methods pertaining to the toolbar items # The menu and popup menu items for "Start", "Stop", and "Restart" are # handled by on_btnStart_clicked(), on_btnStop_clicked(), and # on_btnRestart_clicked() #---------------------------------------------------------------------------- def on_mnuSave_clicked(self, args): """Commits the changes made for each service""" self.services.save_changes() self.save_revert_sensitive(0) def quit(self,arg1=None,arg2=None): global quitting if self.check_dirty() == gtk.RESPONSE_CANCEL: return True quitting=1 if gtk.__dict__.has_key("main_quit"): gtk.main_quit() else: gtk.mainquit() def on_mnuRevert_clicked(self, args): """calls populateList() to repopulate the checklist""" self.services.revert () self.populateList () self.save_revert_sensitive (0) def get_service_action_results(self, servicename, action_type): """calls services[servicename].action and displays the results in a dialog box""" self.winMain.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH)) results = self.services[servicename].action(action_type) if int(results[0]) != 0: dlg = gtk.MessageDialog(self.winMain, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, results[1]) else: dlg = gtk.MessageDialog(self.winMain, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, results[1]) dlg.set_position(gtk.WIN_POS_CENTER_ON_PARENT) dlg.show_all() rc = dlg.run() dlg.destroy() self.set_desc_buffer(self.bgTxtDescBuffer) self.set_status_buffer(self.bgTxtStatusBuffer) self.winMain.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.LEFT_PTR)) def on_add_service_clicked(self,args): dlg = self.xml.get_widget("serviceNameDialog") entry = self.xml.get_widget("serviceNameEntry") entry.set_text("") rc = dlg.run() dlg.hide() if rc==gtk.RESPONSE_OK: service=entry.get_text() response=self.services.add_service(service) if response[0]!=0: error_dialog(response[1]) else: self.on_mnuRescan_activate(None) def on_delete_service_clicked(self,args): if verify_delete(self.text_in_row) == gtk.RESPONSE_YES: response=self.services.delete_service(self.text_in_row) if response[0]!=0: error_dialog(response[1]) else: self.on_mnuRescan_activate(None) def on_btnStart_clicked(self, *args): """calls get_service_action_results to start the selected initscript""" self.get_service_action_results(self.text_in_row, "start") def on_btnStop_clicked(self, *args): """calls get_service_action_results to stop the selected initscript""" self.get_service_action_results(self.text_in_row, "stop") def on_btnRestart_clicked(self,args): """calls get_service_action_results to restart the selected initscript""" self.get_service_action_results(self.text_in_row, "restart") #---------------------------------------------------------------------------- # Methods for everything else #---------------------------------------------------------------------------- def local_button_press_cb(self, clist, event): """checks to see if the third mouse button was clicked. If it was, then bring up the popup menu""" row=clist.get_path_at_pos(int(event.x),int(event.y))[0][0] self.text_in_row = self.bgListServices.get_text(int(row),7) self.set_desc_buffer(self.bgTxtDescBuffer) self.set_status_buffer(self.bgTxtStatusBuffer) if (event.button == 3): self.popupMenu = self.xml.get_widget("popup_menu") # get rid of the tearoff from the popup menu self.pmnBlank = self.xml.get_widget("pmnBlank") # set the label text to be the current selected servicename for pmnBlank self.pmnBlank.get_children()[0].set_text(self.text_in_row) self.popupMenu.popup(None, None, None, event.button, event.time) def save_revert_sensitive(self, sensitive): """sets the save and revert buttons and menus to be sensitive or not, sensitive is a 1 or 0""" self.mnuSave.set_sensitive(sensitive) self.mnuRevert.set_sensitive(sensitive) self.btnSave.set_sensitive(sensitive) self.btnRevert.set_sensitive(sensitive) self.dirty=sensitive def on_srvTypeNotebook_switch_page(self, notebook, page, page_num): assert(page_num in (0, 1)) if page_num == 0: list_ = self.bgListServices else: list_ = self.odListServices selection = list_.get_selection() self.changed(selection, list_) def main(): if os.geteuid() == 0: #try: Gui() if gtk.__dict__.has_key("main"): gtk.main() else: gtk.mainloop() #except: # # if you quit during initialization exceptions can be raised # if not quitting: # raise else: print _("You must run system-config-services as root.") sys.exit(-1) if __name__ == "__main__": main()