[Initng-svn] r3189 - initng/trunk/plugins/ngcs

svn at initng.thinktux.net svn at initng.thinktux.net
Sat Mar 4 03:11:37 CET 2006


Author: makomk
Date: Sat Mar  4 03:11:37 2006
New Revision: 3189

Added:
   initng/trunk/plugins/ngcs/initng_ngcs_cmds.h
Modified:
   initng/trunk/plugins/ngcs/Makefile.am
   initng/trunk/plugins/ngcs/idef.py
   initng/trunk/plugins/ngcs/initng_ngcs.c
   initng/trunk/plugins/ngcs/initng_ngcs.h
   initng/trunk/plugins/ngcs/initng_ngcs_cmds.c
   initng/trunk/plugins/ngcs/ngcs.py
   initng/trunk/plugins/ngcs/ngcs_common.c
   initng/trunk/plugins/ngcs/ngcs_common.h
Log:
Various ngcs improvements - in particular, "ngcs.py -u service-name" can now be used 
to start a service


Modified: initng/trunk/plugins/ngcs/Makefile.am
==============================================================================
--- initng/trunk/plugins/ngcs/Makefile.am	(original)
+++ initng/trunk/plugins/ngcs/Makefile.am	Sat Mar  4 03:11:37 2006
@@ -6,7 +6,9 @@
 	initng_ngcs.c \
 	initng_ngcs.h \
 	ngcs_common.c \
+	ngcs_common.h \
 	initng_ngcs_cmds.c \
+	initng_ngcs_cmds.h \
         ngcs_marshal.c
 libngcs_la_CFLAGS = $(AM_CFLAGS)
 
@@ -15,14 +17,16 @@
 
 BUILT_SOURCES = ngcs_marshal.h
 
+EXTRA_DIST = ngcs_marshal.ngci idef.py ngcs.py
+
 # ngcs_SOURCES = ngcs.c ngcs_common.c
 # ngdcs_SOURCES = ngcs.c ngcs_common.c
 
 install-data-hook:
 	rm -f $(DESTDIR)$(plugindir)/$(plugin_LTLIBRARIES)
 
-ngcs_marshal.c: ngcs_marshal.ngci
+ngcs_marshal.c: ngcs_marshal.ngci idef.py
 	./idef.py ngcs_marshal
 
-ngcs_marshal.h: ngcs_marshal.ngci
+ngcs_marshal.h: ngcs_marshal.ngci idef.py
 	./idef.py ngcs_marshal

Modified: initng/trunk/plugins/ngcs/idef.py
==============================================================================
--- initng/trunk/plugins/ngcs/idef.py	(original)
+++ initng/trunk/plugins/ngcs/idef.py	Sat Mar  4 03:11:37 2006
@@ -1,4 +1,23 @@
 #!/usr/bin/python
+
+# Initng, a next generation sysvinit replacement.
+# Copyright (C) 2005-6 Aidan Thornton <makomk at lycos.co.uk>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General
+# Public License along with this library; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+# Boston, MA  02110-1301, USA.
+
 import re,sys
 
 def mkmarshaler(name, members,sname=None):
@@ -80,13 +99,18 @@
     if(s->@m[1]@)
     {
 #endif
+    @sp at if(buf) *(int*)buf = NGCS_TYPE_STRUCT;
 #if isptr
     @sp at n = ngcs_marshal_ at typename@(s->@m[1]@, (buf?buf+2*sizeof(int):NULL));
 #else
     @sp at n = ngcs_marshal_ at typename@(&s->@m[1]@, (buf?buf+2*sizeof(int):NULL));
 #endif
     @sp at if(n < 0) return n;
-    @sp at if(buf) buf += 2*sizeof(int) + n;
+    @sp at if(buf)
+    @sp@{
+    @sp@    *(int*)(buf+sizeof(int)) = n;
+    @sp@    buf += 2*sizeof(int) + n;
+    @sp@}
     @sp at len += 2*sizeof(int) + n;
 #if isptr
     }

Modified: initng/trunk/plugins/ngcs/initng_ngcs.c
==============================================================================
--- initng/trunk/plugins/ngcs/initng_ngcs.c	(original)
+++ initng/trunk/plugins/ngcs/initng_ngcs.c	Sat Mar  4 03:11:37 2006
@@ -2,6 +2,7 @@
 /* Initng, a next generation sysvinit replacement.
  * Copyright (C) 2005 neuron <neuron at hollowtube.mine.nu>
  * Copyright (C) 2005 Jimmy Wennlund <jimmy.wennlund at gmail.com>
+ * Copyright (C) 2005-6 Aidan Thornton <makomk at lycos.co.uk>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -175,7 +176,7 @@
     return;
 }
 
-void data_ready(f_module_h * from)      /* TODO */
+void data_ready(f_module_h * from)  
 {
     int sock = from->fds;
     int chanid, len, type, cnt;
@@ -193,7 +194,7 @@
         closeconn(conn);
         return;
     }
-    if (chanid == 0)                        /* channel 0 is the command channel */
+    if (chanid == 0)   /* channel 0 is the command channel */
     {
         /* sanity checks on message */
         if (len < 0)
@@ -280,7 +281,7 @@
         ngcs_free_unpack(cnt, udata);
         return;
     }
-    else                                    /* other channels are handled by whoever opened them */
+    else     /* other channels are handled by whoever opened them */
     {
         while_ngcs_chans(chan, conn) if (chan->id == chanid)
         {
@@ -409,6 +410,12 @@
     list_add(&cmd->list, &ngcs_cmds.list);
 }
 
+void ngcs_unreg_cmd(ngcs_cmd* cmd)
+{
+  assert(cmd); assert(cmd->list.prev); assert(cmd->list.next);
+  list_del(&cmd->list);
+}
+
 #if 0
 /* Send a ping to ourselves to check if we're 100% ok. */
 static int sendping()

Modified: initng/trunk/plugins/ngcs/initng_ngcs.h
==============================================================================
--- initng/trunk/plugins/ngcs/initng_ngcs.h	(original)
+++ initng/trunk/plugins/ngcs/initng_ngcs.h	Sat Mar  4 03:11:37 2006
@@ -18,6 +18,9 @@
  * Boston, MA  02110-1301, USA.
  */
 
+#ifndef INITNG_NGCS_H
+#define INITNG_NGCS_H
+
 #include "../../src/initng_active_db.h"
 #include "../../src/initng_system_states.h"
 #include "../../src/initng_list.h"
@@ -36,8 +39,33 @@
 int service_status(active_db_h * service);
 void is_system_halt(h_sys_state state);
 
+/*! \brief Close the specified channel
+ *  Called to close a channel. Sends a message with length -1 on the channel to 
+ *  notify the client (if the connection is still open) and frees the ngcs_channel
+ *  structure, calling the ngcs_channel.free callback first if there is one
+ *
+ *  \param chan the channel to be closed
+ */
 void ngcs_close_channel(ngcs_channel * chan);
+
+/* \brief Register an ngcs command
+ * Registers an ngcs command. ngcs clients can then call it by sending a 
+ * suitably-structured message on channel 0, specifying the command name. If 
+ * more than one registered command has the same name, the results are undefined.
+ *
+ * \param cmd structure describing the command - must not be freed, modified 
+ *        or passed to ngcs_reg_cmd again until ngcs_unreg_cmd(cmd) has been called
+ * \sa ngcs_unreg_cmd() ngcs_cmd
+ */
 void ngcs_reg_cmd(ngcs_cmd * cmd);
+
+/* \brief Unregister an ngcs command
+ * Unregisters a command registered with ngcs_reg_cmd.
+ *
+ * \param cmd the ngcs_cmd structure passed to ngcs_reg_cmd
+ * \sa ngcs_reg_cmd()
+ */
+void ngcs_unreg_cmd(ngcs_cmd* cmd);
 int ngcs_channel_send(ngcs_channel * chan, int type, int len, char *data);
 ngcs_channel *ngcs_open_channel(ngcs_conn * conn,
                                 void (*gotdata) (ngcs_channel *, int, int,
@@ -45,10 +73,27 @@
                                 void (*chanfree) (ngcs_channel *));
 int ngcs_send_response(ngcs_request * req, int type, int len, char *data);
 
+/*! \brief Ngcs command handler
+ *
+ *  Represents an ngcs command handler. Ngcs commands are sent on channel 0 of
+ *  a connection and are identified by a (case-sensitive) string, their name 
+ *
+ *  \sa ngcs_reg_cmd() ngcs_unreg_cmd()
+ */
 struct ngcs_cmd_s
 {
+    /*! \brief Name of the command */
     const char *name;
+    
+    /*! \brief Callback function to handle the request
+     *
+     *  The callback function to handle the request. It should call  ngcs_send_response
+     *  exactly once to send a response to the request.
+     *  \sa ngcs_send_response()
+     */
     void (*func) (ngcs_request * req);
+
+    /* \brief List head - internal use */
     struct list_head list;
 };
 
@@ -95,3 +140,5 @@
 
 #define while_ngcs_cmds(current) list_for_each_entry_prev(current, &ngcs_cmds.list, list)
 #define while_ngcs_cmds_safe(current, safe) list_for_each_entry_prev_safe(current, safe, &ngcs_cmds.list, list)
+
+#endif /* !INITNG_NGCS_H */

Modified: initng/trunk/plugins/ngcs/initng_ngcs_cmds.c
==============================================================================
--- initng/trunk/plugins/ngcs/initng_ngcs_cmds.c	(original)
+++ initng/trunk/plugins/ngcs/initng_ngcs_cmds.c	Sat Mar  4 03:11:37 2006
@@ -1,3 +1,24 @@
+/* Initng, a next generation sysvinit replacement.
+ * Copyright (C) 2005 Jimmy Wennlund <jimmy.wennlund at gmail.com>
+ * Copyright (C) 2005-6 Aidan Thornton <makomk at lycos.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+
 #include "../../src/initng.h"
 
 #include <stdio.h>
@@ -34,6 +55,15 @@
 #include "../../initng-paths.h"
 #include "initng_ngcs.h"
 #include "ngcs_marshal.h"
+#include "initng_ngcs_cmds.h"
+
+typedef struct ngcs_watch_s
+{
+    ngcs_channel *chan;
+    char *name;
+    int flags;
+    struct list_head list;
+} ngcs_watch;
 
 void register_ngcs_cmds(void);
 void unregister_ngcs_cmds(void);
@@ -42,7 +72,10 @@
 static int service_status_watch(active_db_h * service);
 static void ngcs_cmd_watch(ngcs_request * req);
 static void ngcs_free_watch(ngcs_channel * chan);
-static int service_output_watch(active_db_h * service, process_h * x);
+static int service_output_watch(active_db_h * service, process_h * x, char *buffer_pos);
+static ngcs_watch* ngcs_add_watch(ngcs_conn* conn, char* svcname, int flags);
+static void ngcs_cmd_start(ngcs_request * req);
+static int ngcs_watch_initial(ngcs_watch *watch);
 
 ngcs_cmd ngcs_halt_cmd = {
     "halt",
@@ -51,27 +84,27 @@
 };
 
 ngcs_cmd ngcs_reboot_cmd = {
-    "halt",
+    "reboot",
     ngcs_cmd_reboot,
     {0, 0}
 };
 
+ngcs_cmd ngcs_start_cmd = {
+    "start",
+    ngcs_cmd_start,
+    {0, 0}
+};
+
 ngcs_cmd ngcs_watch_cmd = {
     "watch",
     ngcs_cmd_watch,
     {0, 0}
 };
 
-typedef struct ngcs_watch_s
-{
-    ngcs_channel *chan;
-    char *name;
-    struct list_head list;
-} ngcs_watch;
 
 ngcs_watch watches;
 
-int service_status_watch(active_db_h * service)
+static int service_status_watch(active_db_h * service)
 {
     ngcs_watch *watch, *nextwatch;
     int len = 0;
@@ -93,14 +126,52 @@
                     return TRUE;
                 }
             }
-            ngcs_channel_send(watch->chan, NGCS_TYPE_STRUCT, len, buf);
-        }
+	    if(ngcs_channel_send(watch->chan, NGCS_TYPE_STRUCT, len, buf))
+	    {
+		free(buf); return TRUE;
+	    };
+       }
     }
     if (buf)
         free(buf);
     return TRUE;
 }
 
+static int ngcs_watch_initial(ngcs_watch *watch)
+{
+    if(watch->flags & NGCS_CURRENT_STATUS)
+    {
+	active_db_h* current;
+	current = NULL;
+	while_active_db(current)
+	{
+	    if(watch->name == NULL || strcmp(watch->name, current->name) == 0)
+	    {
+		int len = 0; char *buf = NULL;
+		len = ngcs_marshal_active_db_h(current, NULL);
+		buf = i_calloc(1, len);
+		len = ngcs_marshal_active_db_h(current, buf);
+		if (len < 0)
+                {
+                    F_("ngcs_marshal_active_db_h() failed!\n");
+                    free(buf);
+                    return 1;
+                }
+		if(ngcs_channel_send(watch->chan, NGCS_TYPE_STRUCT, len, buf))
+		{
+		    free(buf); return 1;
+		};
+		free(buf);
+	    }
+	}
+    }
+	
+    if(ngcs_channel_send(watch->chan, NGCS_TYPE_NULL, 0, NULL))
+	return 1;
+
+    return 0;
+}
+
 static int service_output_watch(active_db_h * service, process_h * x, char *buffer_pos)
 {
     ngcs_watch *watch, *nextwatch;
@@ -123,10 +194,13 @@
                 len = ngcs_pack(dat, 2, NULL);
                 assert(len >= 0);
                 buf = i_calloc(1, len);
-                len = ngcs_pack(dat, 2, NULL);
+                len = ngcs_pack(dat, 2, buf);
                 assert(len >= 0);
             }
-            ngcs_channel_send(watch->chan, NGCS_TYPE_STRUCT, len, buf);
+            if(ngcs_channel_send(watch->chan, NGCS_TYPE_STRUCT, len, buf))
+	    {
+		free(buf); return FALSE;
+	    };
         }
     }
     if (buf)
@@ -134,36 +208,137 @@
     return FALSE;
 }
 
-void ngcs_cmd_watch(ngcs_request * req)
+static void ngcs_cmd_start(ngcs_request * req)
+{
+    int i = 0; ngcs_watch* watch;
+    active_db_h *serv = NULL; char* svcname = NULL;
+    if(req->type != NGCS_TYPE_STRING || req->data == NULL || req->len <= 0)
+    {
+	F_("Bad call to ngcs command 'start'\n");
+	ngcs_send_response(req, NGCS_TYPE_STRING, 8, (char*)"BAD_CALL");
+	return;
+    }
+    
+    svcname = i_calloc(1, req->len+1);
+    memcpy(svcname, req->data, req->len);
+
+    serv = initng_active_db_find_in_name(svcname);
+    if (serv)
+    {
+	watch = ngcs_add_watch(req->conn, serv->name, NGCS_WATCH_STATUS | NGCS_WATCH_OUTPUT | 
+			       NGCS_CURRENT_STATUS);
+	if(watch) i = watch->chan->id;
+	ngcs_send_response(req, NGCS_TYPE_INT, sizeof(int), (char *) &i);
+	ngcs_watch_initial(watch);
+	if (!IS_UP(serv))
+            initng_handler_start_service(serv);
+	free(svcname); return;
+    }
+    
+    serv = initng_handler_start_new_service_named(svcname);
+    if (!serv)
+    {
+	ngcs_send_response(req, NGCS_TYPE_STRING, 9, (char*)"NOT_FOUND");
+	free(svcname); return;
+    }
+
+    watch = ngcs_add_watch(req->conn, serv->name, NGCS_WATCH_STATUS | NGCS_WATCH_OUTPUT);
+    if(watch) i = watch->chan->id;
+    ngcs_send_response(req, NGCS_TYPE_INT, sizeof(int), (char *) &i);
+    ngcs_watch_initial(watch); free(svcname); return;
+}
+
+ngcs_watch* ngcs_add_watch(ngcs_conn* conn, char* svcname, int flags) 
 {
+    assert(conn);
     ngcs_channel *chan;
     ngcs_watch *watch;
-    int i = 0;
-
-    chan = ngcs_open_channel(req->conn, NULL, ngcs_free_watch);
-    if (!chan)
-        return;
     watch = i_calloc(1, sizeof(ngcs_watch));
 
-    watch->chan = chan;
-    if (req->type != NGCS_TYPE_STRING || req->len <= 0)
-    {
-        watch->name = NULL;
-    }
-    else
+    chan = ngcs_open_channel(conn, NULL, ngcs_free_watch);
+    if (!chan)
     {
-        watch->name = i_calloc(1, req->len + 1);
-        memcpy(watch->name, req->data, req->len);
+	F_("ngcs_open_channel failed!\n");
+        free(watch); return 0;
     }
+    if(svcname)
+	watch->name = i_strdup(svcname);
+    else watch->name = NULL;
+    watch->flags = flags;
+    watch->chan = chan;
     watch->list.prev = 0;
     watch->list.next = 0;
     list_add(&watch->list, &watches.list);
     chan->user_data = watch;
-    i = chan->id;
+    return watch;
+}
+
+static void ngcs_cmd_watch(ngcs_request * req)
+{
+    int i = 0, cnt;
+    ngcs_data* data; ngcs_watch *watch;
+    char* name = NULL; int flags;
+
+    if(req->type == NGCS_TYPE_STRING)       
+    {
+	W_("Use of watch(STRING) depreciated - please update client code\n");
+       if(req->len <= 0 || !req->data) 
+       {
+	   name = NULL;
+       }
+       else
+       {
+	  name = i_calloc(1, req->len + 1);
+	  memcpy(name, req->data, req->len);
+       }
+       flags = NGCS_WATCH_STATUS | NGCS_WATCH_OUTPUT;
+    }
+    else if(req->type == NGCS_TYPE_STRUCT)
+    {
+	cnt = ngcs_unpack(req->data, req->len, &data);
+	if(cnt < 0)
+	{
+	    F_("Bad watch command\n"); 
+	    ngcs_send_response(req, NGCS_TYPE_INT, sizeof(int), (char *) &i);
+	    return;
+	}
+	if(cnt != 2 || data[1].type != NGCS_TYPE_INT)
+	{
+	    F_("Bad watch command\n"); free(data); 
+	    ngcs_send_response(req, NGCS_TYPE_INT, sizeof(int), (char *) &i);
+	    return;
+	}
+	flags = data[1].d.i;
+	if(data[0].type == NGCS_TYPE_NULL || (data[0].type = NGCS_TYPE_STRING &&
+					     data[0].len <= 0))
+	{
+	    name = NULL;
+	}
+	else if(data[0].type == NGCS_TYPE_STRING)
+	{
+	    name = i_strdup(data[0].d.s);
+	}
+    }
+    else if(req->type == NGCS_TYPE_NULL)
+    {
+	W_("Use of watch(NULL) depreciated - please update client code\n");
+        name = NULL;
+	flags = NGCS_WATCH_STATUS | NGCS_WATCH_OUTPUT;
+    }
+    else
+    {
+	F_("Bad watch command\n");
+	ngcs_send_response(req, NGCS_TYPE_INT, sizeof(int), (char *) &i);
+	return;
+    }
+    watch = ngcs_add_watch(req->conn, name, flags);
+    if(watch) i = watch->chan->id;
     ngcs_send_response(req, NGCS_TYPE_INT, sizeof(int), (char *) &i);
+    if(name) free(name);
+    ngcs_watch_initial(watch);
 }
 
-void ngcs_free_watch(ngcs_channel * chan)
+static void ngcs_free_watch(ngcs_channel * chan)
 {
     ngcs_watch *watch = chan->user_data;
 
@@ -179,6 +354,7 @@
 void register_ngcs_cmds(void)
 {
     ngcs_reg_cmd(&ngcs_halt_cmd);
+    ngcs_reg_cmd(&ngcs_start_cmd);
     ngcs_reg_cmd(&ngcs_reboot_cmd);
     ngcs_reg_cmd(&ngcs_watch_cmd);
     initng_plugin_hook_add(&g.ASTATUS_CHANGE, 50, &service_status_watch);
@@ -190,7 +366,11 @@
 {
     initng_plugin_hook_del(&g.ASTATUS_CHANGE, &service_status_watch);
     initng_plugin_hook_del(&g.PIPEWATCHERS, &service_output_watch);
-    /* TODO */
+    ngcs_unreg_cmd(&ngcs_halt_cmd);
+    ngcs_unreg_cmd(&ngcs_start_cmd);
+    ngcs_unreg_cmd(&ngcs_reboot_cmd);
+    ngcs_unreg_cmd(&ngcs_watch_cmd);
+    /* TODO - need to free watches */
 }
 
 static void ngcs_cmd_halt(ngcs_request * req)

Modified: initng/trunk/plugins/ngcs/ngcs.py
==============================================================================
--- initng/trunk/plugins/ngcs/ngcs.py	(original)
+++ initng/trunk/plugins/ngcs/ngcs.py	Sat Mar  4 03:11:37 2006
@@ -1,5 +1,27 @@
+#!/usr/bin/python
+
+# Initng, a next generation sysvinit replacement.
+# Copyright (C) 2005-6 Aidan Thornton <makomk at lycos.co.uk>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General
+# Public License along with this library; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+# Boston, MA  02110-1301, USA.
+
 import socket,sys,struct
+from select import select, poll
 from types import *
+from errno import EAGAIN, EWOULDBLOCK
 
 NGCS_TYPE_NONE = 0
 NGCS_TYPE_INT = 1
@@ -10,6 +32,15 @@
 
 SIZEOF_INT = struct.calcsize("@i")
 
+IS_UNKNOWN = 0
+IS_UP = 1
+IS_DOWN = 2
+IS_FAILED = 3
+IS_STARTING = 4
+IS_STOPPING = 5
+IS_WAITING = 6
+
+
 sock = 0;
 
 class NgcsData:
@@ -23,7 +54,192 @@
     def __init__(self,i):
         self.val = i
     def __repr__(self):
-        return "NgcsError("+repr(self.val)+")"
+        return "NgcsEOF("+repr(self.val)+")"
+
+class AsyncConnect:
+    def __init__(self):
+        self.sock = socket.socket(socket.AF_UNIX,socket.SOCK_STREAM)
+        self.sock.connect("/dev/initng/initng.ngcs")
+        self.sock.setblocking(0)
+        self._recvbuf = ""
+        self._sendbuf = ""
+        self._head = None
+        self._chans = { }
+        self.reg_channel(0, self._chan_0)
+        self._pending_req = [ ]
+
+    def close(self):
+        self.sock.close()
+        self.sock = None
+
+    def _chan_0(self, conn, chan, data):
+        if len(self._pending_req) == 0:
+            sys.stderr.write("Unexpected response on channel 0\n")
+            return
+        self._pending_req.pop(0)(data)
+
+    def send_cmd(self, cmd, callback):
+        self._pending_req.append(callback)
+        self.send(0, cmd)
+    
+    def send(self, chan, msg):
+        msg = ngcs_pack(msg)
+        self._sendbuf += struct.pack("@iii",chan,msg.typecode,len(msg.data)) + msg.data
+        self.try_send()
+        
+    def try_send(self):
+        try:
+            ret = self.sock.send(self._sendbuf)
+            self._sendbuf = self._sendbuf[ret:]
+        except socket.error, e:
+            if e[0] != EAGAIN and e[0] != EWOULDBLOCK:
+                raise
+
+    def unhandled_data_hook(self, chan, data):
+        pass
+
+    def reg_channel(self, chan, hook):
+        self._chans[chan] = hook
+
+    def unreg_channel(self, chan):
+        del self._chans[chan]
+
+    def try_recv(self):
+        while True:
+            try:
+                ret = self.sock.recv(4096)
+            except socket.error, e:
+                if e[0] == EAGAIN:
+                    break
+                else:
+                    raise
+            if len(ret) == 0: break
+            self._recvbuf += ret
+            
+        while True:
+            if self._head == None:
+                if len(self._recvbuf) < 3*SIZEOF_INT: break
+                self._head = struct.unpack("@iii",self._recvbuf[:3*SIZEOF_INT])
+                self._recvbuf = self._recvbuf[3*SIZEOF_INT:]
+            else:
+                if len(self._recvbuf) < self._head[2]: break
+                (chan, type, l) = self._head; self._head = None
+                if l < 0:
+                    data = NgcsEOF(l)
+                else:
+                    data = NgcsData(type, self._recvbuf[:l])
+                    self._recvbuf = self._recvbuf[l:]
+                if(self._chans.has_key(chan)):
+                    self._chans[chan](self, chan, data)
+                else:
+                    self.unhandled_data_hook(chan, data)
+
+class _NgcsCmds:
+    def __init__(self, conn):
+        self.conn = conn
+        self.cmds = [ ]
+        self.success = True
+
+    def add(self, cmd):
+        self.cmds.append(cmd)
+
+    def rem(self, cmd):
+        self.cmds.remove(cmd)
+
+    def fail(self):
+        self.success = False
+
+    def mainloop(self):
+        while len(self.cmds) > 0:
+            res = select((self.conn.sock,),(self.conn.sock,),(self.conn.sock,))
+            if self.conn.sock in res[0]:
+                self.conn.try_recv()
+            if self.conn.sock in res[1]:
+                self.conn.try_send()
+            if self.conn.sock in res[2]:
+                return False
+        return self.success
+
+class _NgcsCmd:
+    def __init__(self, cmds, cmd, txt):
+        if not isinstance(cmds,  _NgcsCmds): raise TypeError
+        self._cmds = cmds
+        self._txt = txt
+        cmds.add(self)
+        cmds.conn.send_cmd(cmd, self._resp_cb)
+
+    def _resp_cb(self, resp):
+        self._cmds.rem(self)
+        if isinstance(resp, NgcsEOF):
+            sys.stderr.write("FAILED " + self._txt + ": unknown server error\n")
+            self._cmds.fail(); return
+        resp = ngcs_unpack(resp)
+        if resp == None:
+            sys.stderr.write("FAILED " + self._txt + ": couldn't run command\n")
+            self._cmds.fail(); return
+        elif isinstance(resp, StringType):
+            sys.stderr.write("FAILED " + self._txt + ": " + resp + "\n")
+            self._cmds.fail(); return
+        elif isinstance(resp, IntType):
+            sys.stderr.write("SUCCESS " + self._txt + "\n")
+            return
+        else:
+            sys.stderr.write("FAILED " + self._txt + ": unknown response\n")
+            self._cmds.fail(); return            
+
+class _NgcsStartStopCmd (_NgcsCmd):
+    def __init__(self, cmds, cmd, svc):
+        _NgcsCmd.__init__(self, cmds, (cmd,svc), cmd + " " + svc)
+        self._svc = svc
+        self._cmd = cmd
+        self._rtmark = False
+
+    def _resp_cb(self, resp):
+        resp = ngcs_unpack(resp)
+        if isinstance(resp, IntType):
+            if resp == 0:
+                sys.stderr.write("FAILED " + self._txt + ": didn't get channel\n")
+                self._cmds.fail(); return
+            self._chan = resp
+            self._cmds.conn.reg_channel(resp, self._chan_cb)
+        else:
+            return _NgcsCmd._resp_cb(self, resp)
+
+    def _state_check(self, state):
+        pass
+
+    def _chan_cb(self, conn, chan, data):
+        data = ngcs_unpack(data)
+        if data == None:
+            self._rtmark = True
+        if not isinstance(data, TupleType) or len(data) < 2 or not isinstance(data[0], str):
+            return
+        self._svc = data[0]
+        self._text = "start %s" % data[0]
+        if isinstance(data[1], str):
+            sys.stderr.write(self._svc + " output:\n" + data[1])
+        elif isinstance(data[1], tuple):
+            state = data[2]
+            if self._rtmark:
+                sys.stderr.write("%s: %s\n" % (self._svc, state[0]));
+            self._state_check(state)
+
+    def _done(self):
+        self._cmds.rem(self); self._cmds.conn.unreg_channel(self._chan);
+
+class _NgcsStartCmd(_NgcsStartStopCmd):
+    def __init__(self, cmds, svc):
+        _NgcsStartStopCmd.__init__(self, cmds, "start", svc)
+        
+    def _state_check(self, state):
+        if state[1] == IS_STARTING:
+            pass
+        elif state[1] in (IS_UP, IS_WAITING):
+            sys.stderr.write("Service %s started succesfully (%s)\n" % (self._svc, state[0]))
+            self._done()
+        elif self._rtmark:
+            sys.stderr.write("Service %s failed to start (%s)\n" % (self._svc, state[0]))
+            self._done(); self._cmds.fail()
 
 def connect():
     global sock;
@@ -38,7 +254,9 @@
     sock.sendall(msg.data)
 
 def ngcs_pack(datum):
-    if isinstance(datum, IntType):
+    if isinstance(datum, NoneType):
+         return NgcsData(NGCS_TYPE_NONE,"");
+    elif isinstance(datum, IntType):
         return NgcsData(NGCS_TYPE_INT,struct.pack("@i",datum));
     elif isinstance(datum, StringType):
         return NgcsData(NGCS_TYPE_STRING,datum);
@@ -56,6 +274,8 @@
 def ngcs_unpack(data):
     if not isinstance(data, NgcsData):
         return data
+    elif data.typecode == NGCS_TYPE_NONE:
+        return None
     elif data.typecode == NGCS_TYPE_INT:
         return struct.unpack("@i", data.data)[0]
     elif data.typecode == NGCS_TYPE_LONG:
@@ -109,6 +329,51 @@
         elif chan != 0 and msg[0] == chan and isinstance(msg[1],NgcsEOF):
             print "---End---"; break
 
+_simple_cmds = { "-6": ("reboot", False, "initiate reboot"), "-0": ("halt", False, "initiate shutdown") }
+_simple_cmds["--halt"] = _simple_cmds["-0"]
+_simple_cmds["--reboot"] = _simple_cmds["-6"]
+
+
+
+def _main():
+    cmd = None; cmdlist = []; cmdargs = []; success = True
+    for a in sys.argv[1:]:
+        if len(a) < 1: continue
+        if a[0] == '-':
+            if cmd != None:
+                cmdlist.append((cmd, cmdargs))
+            cmd = a; cmdargs = []
+        else:
+            cmdargs.append(a)
+    if cmd != None:
+        cmdlist.append((cmd, cmdargs))
+        
+    conn = AsyncConnect()
+    wcmds = _NgcsCmds(conn)
+    for c in cmdlist:
+        if _simple_cmds.has_key(c[0]):
+            cd = _simple_cmds[c[0]]
+            if len(c[1]) > 0 and not cd[1]:
+                sys.stderr.write("Error: " + c[0] + " takes no options\n")
+                success = False
+            if cd[1]:
+                for arg in c[1]:
+                    _NgcsCmd(wcmds, (cd[0],arg), cd[2])
+            else:
+                _NgcsCmd(wcmds, (cd[0],), cd[2])
+        elif cmd in ("-u","--start"):
+            for arg in c[1]:
+                _NgcsStartCmd(wcmds,arg)
+        else:
+            sys.stderr.write("Error: unknown command "+c[0]+"\n")
+            
+            success = False
+    if not wcmds.mainloop(): success = False
+    conn.close()
+    if success:
+        sys.exit(0)
+    else:
+        sys.exit(1)
+
 if __name__=='__main__':
-    connect()
-    ez_cmd("watch","daemon/agetty/5")
+    _main()

Modified: initng/trunk/plugins/ngcs/ngcs_common.c
==============================================================================
--- initng/trunk/plugins/ngcs/ngcs_common.c	(original)
+++ initng/trunk/plugins/ngcs/ngcs_common.c	Sat Mar  4 03:11:37 2006
@@ -1,3 +1,22 @@
+/* Initng, a next generation sysvinit replacement.
+ * Copyright (C) 2005-6 Aidan Thornton <makomk at lycos.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
 #include <stdlib.h>
 #include "ngcs_common.h"
 #include <sys/socket.h>
@@ -16,7 +35,7 @@
  *  \param chan the channel number the message is marked with
  *  \param type the data type of the message (typically one of the NGCS_TYPE
  *         constants)
- *  \param len the length of the message (in bytes)
+ *  \param len the length of the message body (in bytes)
  *  \param data the message body
  *  \return Zero on success, non-zero on failure
  *  \sa ngcs_recvmsg(), ngcs_pack()
@@ -50,9 +69,9 @@
  *        should be stored
  * \param type a pointer to a location where the data type of the message
  *        should be stored
- * \param len a pointer to a location where the length of the message should be 
- *        stored. On return, *len may be less than zero, in which case *data is 
- *        set to NULL
+ * \param len a pointer to a location where the length of the message body (in bytes)
+ *        should be stored. On return, *len may be less than zero, in which case 
+ *        *data is set to NULL
  * \param data a location where a pointer to the message body is stored. *data is
  *        valid on return if ngcs_recvmsg() succeeded with *len>=0, and in this case
  *        must be free()d by the caller after use.
@@ -85,10 +104,29 @@
     return 0;
 }
 
+
+/*! \brief Pack an ngcs stucture for transmission
+ * 
+ * This function packs a sequence of items into a buffer, which can be transmitted
+ * down a socket or otherwise transferred, then unpacked by ngcs_unpack()
+ * The resulting packed data strcuture should be transmitted marked as type
+ * NGCS_TYPE_STRUCT.
+ *
+ * Since the caller must ensure that the buffer is large enough, your code should
+ * call ngcs_pack with buf=NULL to get the required size, then allocate a buffer
+ * and call it again with the same data an the newly-allocated buffer
+ *
+ * \param data an array of structures describing the items of data to pack
+ * \param cnt the number of items to pack (the length of data[])
+ * \param buf the buffer into which to pack the data. May be null, in which case
+ *            the required buffer size is returned
+ *
+ * \return the total packed length of the data, in bytes
+ */
 int ngcs_pack(ngcs_data * data, int cnt, char *buf)
 {
     int n;
-    int outcnt;
+    int outcnt = 0;
 
     for (n = 0; n < cnt; n++)
     {
@@ -147,9 +185,26 @@
                 break;
         }
     }
-    return 0;
+    return outcnt;
 }
 
+/*! /brief Unpack a received structure
+ *
+ *  This is the reverse of ngcs_pack() - given packed data representing a
+ *  structure, it creates an array of ngcs_data structures representing the
+ *  contents of the structure. If you receive data marked as type 
+ *  NGCS_TYPE_STRUCT, you should be able to unpack it using this function.
+ *
+ *  Note that this function allocates memory, which you must call
+ *  ngcs_free_unpack(*res) to free once you're done. However, you must *not*
+ *  do so if ngcs_unpack() fails (ie. if return value is < 0).
+ *
+ *  /param data the packed data representing the structure
+ *  /param len the length of data, in bytes
+ *  /param res a location where a pointer to an array of ngcs_data structures,
+ *             representing the result of unpacking the data, is returned.
+ *  /return the number of items unpacked, or -1 on error
+ */
 int ngcs_unpack(const char *data, int len, ngcs_data ** res)
 {
     const char *d = data;
@@ -238,6 +293,15 @@
     return cnt;
 }
 
+
+/*! \brief Frees the memory allocated by ngcs_unpack()
+ *  
+ *  Once you're done with the results of ngcs_unpack(), you should
+ *  call this method to free the memory allocated by it.
+ *
+ * /param len the number of data items unpacked (that is, the return value of ngcs_unpack())
+ * /param res the array of ngcs_data structures returned by ngcs_unpack in its *res parameter
+ */ 
 void ngcs_free_unpack(int len, ngcs_data * res)
 {
     int n;

Modified: initng/trunk/plugins/ngcs/ngcs_common.h
==============================================================================
--- initng/trunk/plugins/ngcs/ngcs_common.h	(original)
+++ initng/trunk/plugins/ngcs/ngcs_common.h	Sat Mar  4 03:11:37 2006
@@ -1,3 +1,22 @@
+/* Initng, a next generation sysvinit replacement.
+ * Copyright (C) 2005-6 Aidan Thornton <makomk at lycos.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
 #ifndef NGCS_COMMON_H
 #define NGCS_COMMON_H
 #include "../../initng-paths.h"


More information about the Initng-svn mailing list