PERFORCE change 125368 for review
Andrew Turner
andrew at FreeBSD.org
Sun Aug 19 17:57:16 PDT 2007
http://perforce.freebsd.org/chv.cgi?CH=125368
Change 125368 by andrew at andrew_hermies on 2007/08/20 00:56:57
Add the get_services call to list the services avaliable to restart
Impement the restart_services call to restart services
Add a button to send the restart_services call to the backend
Add a services item to the computer view. When selected it will send a get_services and display the services returned on the ypdates area
Add the getType() method to facund objects
Spell True correctly for Python
Affected files ...
.. //depot/projects/soc2007/andrew-update/backend/facund-be.c#25 edit
.. //depot/projects/soc2007/andrew-update/frontend/facund-fe.glade#6 edit
.. //depot/projects/soc2007/andrew-update/frontend/facund/computer.py#14 edit
.. //depot/projects/soc2007/andrew-update/frontend/facund/controller.py#7 edit
.. //depot/projects/soc2007/andrew-update/frontend/facund/data.py#4 edit
.. //depot/projects/soc2007/andrew-update/frontend/facund/gui/main_window.py#12 edit
.. //depot/projects/soc2007/andrew-update/frontend/facund/gui/update_model.py#3 edit
.. //depot/projects/soc2007/andrew-update/frontend/facund/network/__init__.py#14 edit
Differences ...
==== //depot/projects/soc2007/andrew-update/backend/facund-be.c#25 (text+ko) ====
@@ -35,6 +35,7 @@
#include <assert.h>
#include <bsdxml.h>
+#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
@@ -85,6 +86,8 @@
struct facund_object *);
static struct facund_response *facund_call_rollback_patches(const char *,
struct facund_object *);
+static struct facund_response *facund_call_get_services(const char *,
+ struct facund_object *obj);
static struct facund_response *facund_call_restart_services(const char *,
struct facund_object *);
@@ -1093,10 +1096,158 @@
}
static struct facund_response *
-facund_call_restart_services(const char *id __unused, struct facund_object *obj __unused)
+facund_call_get_services(const char *id __unused, struct facund_object *obj __unused)
+{
+ struct facund_object *dirs, *cur_dir;
+ const char *base_dir;
+ struct dirent *de;
+ unsigned int pos;
+ DIR *d;
+
+ if (obj == NULL) {
+ /* TODO: Don't use magic numbers */
+ return facund_response_new(id, 1, "No data sent", NULL);
+ }
+
+ /* Read in the base dir to get the services for */
+ base_dir = NULL;
+ if (facund_object_get_type(obj) != FACUND_STRING) {
+ return facund_response_new(id, 1, "Incorrect data", NULL);
+ }
+ base_dir = facund_object_get_string(obj);
+ if (base_dir == NULL) {
+ return facund_response_new(id, 1, "Malloc failed", NULL);
+ }
+ if (strcmp(base_dir, "/") != 0) {
+ return facund_response_new(id, 1,
+ "Can only restart services in /", NULL);
+ }
+ for (pos = 0; pos < watched_db_count; pos++) {
+ if (strcmp(watched_db[pos].db_base, base_dir) == 0) {
+ break;
+ }
+ }
+ if (pos == watched_db_count) {
+ return facund_response_new(id, 1, "Unknown base dir", NULL);
+ }
+
+ d = opendir("/etc/rc.d/");
+ if (d == NULL) {
+ return facund_response_new(id, 1, "Could not open /etc/rc.d/",
+ NULL);
+ }
+
+ dirs = facund_object_new_array();
+ while ((de = readdir(d)) != NULL) {
+ /* Don't look at hidden files */
+ if (de->d_name[0] == '.')
+ continue;
+
+ cur_dir = facund_object_new_string();
+ facund_object_set_string(cur_dir, de->d_name);
+ facund_object_array_append(dirs, cur_dir);
+ }
+ if (facund_object_array_size(dirs) == 0) {
+ facund_object_free(dirs);
+ return facund_response_new(id, 1, "No services found", NULL);
+ }
+
+ return facund_response_new(id, 0, "Services found", dirs);
+}
+
+static struct facund_response *
+facund_call_restart_services(const char *id, struct facund_object *obj)
{
- fprintf(stderr, "STUB: %s\n", __func__);
- return NULL;
+ const char *base_dir, *service;
+ const struct facund_object *cur;
+ char service_script[PATH_MAX], *cmd;
+ unsigned int pos;
+ struct stat sb;
+
+ if (obj == NULL) {
+ /* TODO: Don't use magic numbers */
+ return facund_response_new(id, 1, "No data sent", NULL);
+ }
+
+ base_dir = NULL;
+ service = NULL;
+
+ if (facund_object_get_type(obj) != FACUND_ARRAY) {
+ return facund_response_new(id, 1, "Incorrect data", NULL);
+ }
+ if (facund_object_array_size(obj) != 2) {
+ return facund_response_new(id, 1, "Incorrect data", NULL);
+ }
+
+ /* Find the base dir */
+ cur = facund_object_get_array_item(obj, 0);
+ if (facund_object_get_type(cur) != FACUND_STRING) {
+ return facund_response_new(id, 1, "Incorrect data", NULL);
+ }
+ base_dir = facund_object_get_string(cur);
+ if (base_dir == NULL) {
+ return facund_response_new(id, 1, "Malloc failed", NULL);
+ }
+ /*
+ * We can only restart a service if the base dir
+ * is / as we don't know how to signal any other's.
+ * eg. if it is in a jail we will have to use jexec
+ * but we don't know if this base is in a jail
+ */
+ if (strcmp(base_dir, "/") != 0) {
+ return facund_response_new(id, 1,
+ "Can only restart services in /", NULL);
+ }
+ for (pos = 0; pos < watched_db_count; pos++) {
+ if (strcmp(watched_db[pos].db_base, base_dir) == 0) {
+ break;
+ }
+ }
+ if (pos == watched_db_count) {
+ return facund_response_new(id, 1, "Unknown base dir", NULL);
+ }
+
+ /* Find the service to restart */
+ cur = facund_object_get_array_item(obj, 1);
+ if (facund_object_get_type(cur) != FACUND_STRING) {
+ return facund_response_new(id, 1, "Incorrect data", NULL);
+ }
+ service = facund_object_get_string(cur);
+ if (service == NULL) {
+ return facund_response_new(id, 1, "Malloc failed", NULL);
+ }
+ do {
+ /* Try services in /etc/rc.d */
+ snprintf(service_script, PATH_MAX, "/etc/rc.d/%s", service);
+ if (stat(service_script, &sb) == 0) {
+ break;
+ }
+
+ /* Try services in /usr/local/etc/rc.d */
+ snprintf(service_script, PATH_MAX, "/usr/local/etc/rc.d/%s",
+ service);
+ if (stat(service_script, &sb) == 0) {
+ break;
+ }
+
+ return facund_response_new(id, 1, "Unknown service", NULL);
+ } while (0);
+
+ /* Attempt to restart the service */
+ asprintf(&cmd, "%s restart", service_script);
+ if (cmd == NULL) {
+ return facund_response_new(id, 1, "Malloc failed", NULL);
+ }
+ seteuid(0);
+ if (system(cmd) != 0) {
+ free(cmd);
+ seteuid(getuid());
+ return facund_response_new(id, 1, "Service restart failed",
+ NULL);
+ }
+ free(cmd);
+ seteuid(getuid());
+ return facund_response_new(id, 0, "Service restart successful", NULL);
}
static void
@@ -1203,6 +1354,7 @@
facund_server_add_call("list_installed", facund_call_list_installed);
facund_server_add_call("install_patches", facund_call_install_patches);
facund_server_add_call("rollback_patches",facund_call_rollback_patches);
+ facund_server_add_call("get_services", facund_call_get_services);
facund_server_add_call("restart_services",facund_call_restart_services);
pthread_create(&update_thread, NULL, look_for_updates, NULL);
==== //depot/projects/soc2007/andrew-update/frontend/facund-fe.glade#6 (text+ko) ====
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
-<!--Generated with glade3 3.2.2 on Thu Aug 16 12:34:07 2007 by andrew at hermies.int.fubar.geek.nz-->
+<!--Generated with glade3 3.2.2 on Mon Aug 20 10:28:13 2007 by andrew at hermies.int.fubar.geek.nz-->
<glade-interface>
<widget class="GtkWindow" id="facundWindow">
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
@@ -105,32 +105,39 @@
<widget class="GtkTable" id="table1">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="n_rows">2</property>
+ <property name="n_rows">3</property>
<property name="n_columns">2</property>
<property name="column_spacing">5</property>
<property name="row_spacing">5</property>
<child>
- <widget class="GtkButton" id="connectButton">
+ <widget class="GtkButton" id="restartButton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">Connect</property>
+ <property name="label" translatable="yes">Restart</property>
<property name="response_id">0</property>
</widget>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
</child>
<child>
- <widget class="GtkButton" id="disconnectButton">
+ <widget class="GtkButton" id="deinstallButton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">Disconnect</property>
+ <property name="label" translatable="yes">Remove</property>
<property name="response_id">0</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
</packing>
</child>
<child>
@@ -148,21 +155,29 @@
</packing>
</child>
<child>
- <widget class="GtkButton" id="deinstallButton">
+ <widget class="GtkButton" id="disconnectButton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">Remove</property>
+ <property name="label" translatable="yes">Disconnect</property>
<property name="response_id">0</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
</packing>
</child>
+ <child>
+ <widget class="GtkButton" id="connectButton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Connect</property>
+ <property name="response_id">0</property>
+ </widget>
+ </child>
</widget>
<packing>
<property name="expand">False</property>
@@ -261,34 +276,27 @@
<property name="column_spacing">3</property>
<property name="row_spacing">10</property>
<child>
- <widget class="GtkLabel" id="label3">
+ <widget class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">Computer's description
-This is a Human redable
-name for the computer</property>
+ <property name="label" translatable="yes">Socket location
+Leave blank for
+the default socket</property>
</widget>
- </child>
- <child>
- <widget class="GtkEntry" id="computerNameEntry">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- </widget>
<packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
</packing>
</child>
<child>
- <widget class="GtkEntry" id="computerEntry">
+ <widget class="GtkLabel" id="label1">
<property name="visible">True</property>
- <property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Computer's name
+leave blank for the
+local computer</property>
</widget>
<packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
@@ -307,31 +315,38 @@
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label1">
+ <widget class="GtkEntry" id="computerEntry">
<property name="visible">True</property>
+ <property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">Computer's name
-leave blank for the
-local computer</property>
</widget>
<packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label2">
+ <widget class="GtkEntry" id="computerNameEntry">
<property name="visible">True</property>
+ <property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">Socket location
-Leave blank for
-the default socket</property>
</widget>
<packing>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
</packing>
</child>
+ <child>
+ <widget class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Computer's description
+This is a Human redable
+name for the computer</property>
+ </widget>
+ </child>
</widget>
<packing>
<property name="position">1</property>
==== //depot/projects/soc2007/andrew-update/frontend/facund/computer.py#14 (text+ko) ====
@@ -53,7 +53,7 @@
self.__dirs = []
self.__connected = False
self.__connection = None
- self.__commands = ['Avaliable', 'Installed']
+ self.__commands = ['Avaliable', 'Installed', 'Services']
def __str__(self):
return self.__name + ": " + (self.__host or self.__socket)
@@ -91,6 +91,8 @@
return self.getUpdateList(dir)
elif self.__commands[command] == 'Installed':
return self.getInstalledList(dir)
+ elif self.__commands[command] == 'Services':
+ return self.getServicesList(dir)
else:
print 'TODO: Handle this command (%d)' % (command,);
@@ -119,8 +121,16 @@
# Wait for the response
call.acquireLock()
call.releaseLock()
- print call.getResponse()
-
+ return call.getResponse()
+
+ def restartService(self, dir, service):
+ args = self.buildInstallArg(dir, service)
+ call = facund.Call("restart_services", args)
+ self.__connection.doCall(call)
+ # Wait for the response
+ call.acquireLock()
+ call.releaseLock()
+ return call.getResponse()
def getUpdateList(self, dir = None):
if dir is None:
@@ -154,6 +164,16 @@
call.releaseLock()
return call.getResponse()
+ def getServicesList(self, dir):
+ arg = facund.String(dir)
+ call = facund.Call("get_services", arg)
+ self.__connection.doCall(call)
+ # Wait for the response
+ call.acquireLock()
+ call.releaseLock()
+ return call.getResponse()
+
+
def connect(self):
'''Connects to the remote computer'''
if self.__connection is not None:
==== //depot/projects/soc2007/andrew-update/frontend/facund/controller.py#7 (text+ko) ====
@@ -34,6 +34,8 @@
self.__currentDirectory = None
self.__updateModel = updateModel
self.__view.setUpdateViewModel(self.__updateModel)
+ self.__inServices = False
+ self.__selectedUpdate = None
def run(self):
self.__view.run()
@@ -41,12 +43,18 @@
def onComputerTreeSelect(self, position):
self.__currentDirectory = None
self.__updateModel.empty()
+ self.__inServices = False
+ self.__selectedUpdate = None
computer = self.__computersModel.getComputer(position[0])
self.__view.setConnected(computer.getConnectionStatus())
if computer.getConnectionStatus() is not True:
self.__view.setInstallable(False, False)
+ # We can disable the restart button as it will be
+ # enabled only when we select a service to start
+ self.__view.setRestartable(False)
+
self.__currentComputer = computer
if len(position) == 1:
@@ -65,21 +73,33 @@
# We can't to an install or remove when we have nothing
self.__view.setInstallable(False, False)
return
- item = data.getData()[0]
- # Each item will be a pair of <dir, update list>
- pair = item.getData()
- theDir = pair[0].getData()
+
+ if command is 'Services':
+ self.__inServices = True
+ for service in data.getData():
+ self.__updateModel.addUpdate(service)
+ else:
+ item = data.getData()[0]
+ # Each item will be a pair of <dir, update list>
+ pair = item.getData()
+ theDir = pair[0].getData()
+
+ for update in pair[1].getData():
+ self.__updateModel.addUpdate(update)
- for update in pair[1].getData():
- self.__updateModel.addUpdate(update)
+ if self.__updateModel.getSize() > 0:
+ if command == "Avaliable":
+ self.__view.setInstallable(True, False)
+ elif command == "Installed":
+ self.__view.setInstallable(False, True)
+ else:
+ self.__view.setInstallable(False, False)
- if self.__updateModel.getSize() > 0:
- if command == "Avaliable":
- self.__view.setInstallable(True, False)
- elif command == "Installed":
- self.__view.setInstallable(False, True)
- else:
- self.__view.setInstallable(False, False)
+ def onSelectUpdate(self, item):
+ if not self.__inServices:
+ return
+ self.__selectedUpdate = self.__updateModel.getUpdate(item)
+ self.__view.setRestartable(True)
def getCurrentComputer(self):
return self.__currentComputer
@@ -87,6 +107,9 @@
def getCurrentDirectory(self):
return self.__currentDirectory
+ def getCurrentService(self):
+ return self.__selectedUpdate
+
def installUpdates(self, updates):
computer = self.getCurrentComputer()
computer.installUpdates(True, updates)
@@ -94,3 +117,7 @@
def removeUpdates(self, updates):
computer = self.getCurrentComputer()
computer.installUpdates(False, updates)
+
+ def restartService(self, dir, service):
+ computer = self.getCurrentComputer()
+ computer.restartService(dir, service)
==== //depot/projects/soc2007/andrew-update/frontend/facund/data.py#4 (text+ko) ====
@@ -26,8 +26,6 @@
import struct
-#TODO: Create an exception class(es) for bad data, etc
-
class Object:
def __init__(self, type):
self.__parent = None
@@ -53,6 +51,9 @@
def getData(self):
return self.__data
+ def getType(self):
+ return self.__type
+
class Bool(Object):
def __init__(self, data = None):
Object.__init__(self, "bool")
==== //depot/projects/soc2007/andrew-update/frontend/facund/gui/main_window.py#12 (text+ko) ====
@@ -119,12 +119,16 @@
installButton.connect('clicked', self.onInstallClick)
removeButton = self.__xml.get_widget('deinstallButton')
removeButton.connect('clicked', self.onRemoveClick)
+ restartButton = self.__xml.get_widget('restartButton')
+ restartButton.connect('clicked', self.onRestartClick)
def setUpdateViewModel(self, model):
'''Sets the model to use to for the computer tree'''
self.__updateViewModel = model
treeView = self.__xml.get_widget('updateView')
treeView.set_model(model)
+ treeView.connect('cursor-changed', self.onSelectUpdate)
+
cell = gtk.CellRendererText()
column = gtk.TreeViewColumn("Update", cell, text=0)
treeView.append_column(column)
@@ -160,6 +164,9 @@
deinstallButton = self.__xml.get_widget('deinstallButton')
deinstallButton.set_sensitive(uninstallable)
+ def setRestartable(self, restartable):
+ restartButton = self.__xml.get_widget('restartButton')
+ restartButton.set_sensitive(restartable)
def onConnectClick(self, widget):
'''Signal handler for the connect button'''
@@ -190,11 +197,20 @@
dir = self.__controller.getCurrentDirectory()
self.__controller.removeUpdates((dir.getName(), 'base'))
+ def onRestartClick(self, widget):
+ dir = self.__controller.getCurrentDirectory()
+ service = self.__controller.getCurrentService()
+ self.__controller.restartService(dir.getName(), service)
+
def onSelectComputer(self, widget):
'''Signal handler for when the selected item is changed'''
cursor = widget.get_cursor()
self.__controller.onComputerTreeSelect(cursor[0])
+ def onSelectUpdate(self, widget):
+ cursor = widget.get_cursor()
+ self.__controller.onSelectUpdate(cursor[0][0])
+
def run(self):
'''Displays the main window. Does't return'''
self.__widget.show()
==== //depot/projects/soc2007/andrew-update/frontend/facund/gui/update_model.py#3 (text+ko) ====
@@ -39,6 +39,10 @@
self.set(iter, 0, name)
self.__size += 1
+ def getUpdate(self, item):
+ iter = self.get_iter((item,))
+ return self.get_value(iter, 0);
+
def empty(self):
self.__size = 0
self.clear()
==== //depot/projects/soc2007/andrew-update/frontend/facund/network/__init__.py#14 (text+ko) ====
@@ -54,7 +54,7 @@
self.socket.connect(server)
def isOpen(self):
- return true
+ return True
def read(self, len):
return self.socket.recv(len)
More information about the p4-projects
mailing list