Date: Mon, 20 Aug 2007 00:57:14 GMT From: Andrew Turner <andrew@FreeBSD.org> To: Perforce Change Reviews <perforce@freebsd.org> Subject: PERFORCE change 125368 for review Message-ID: <200708200057.l7K0vEVP028697@repoman.freebsd.org>
next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=125368 Change 125368 by andrew@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@hermies.int.fubar.geek.nz--> +<!--Generated with glade3 3.2.2 on Mon Aug 20 10:28:13 2007 by andrew@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)
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200708200057.l7K0vEVP028697>