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