r270: Implement prerun and postrun options
[nbd.git] / nbd-server.c
index cc9cefe..26c8f59 100644 (file)
@@ -179,6 +179,10 @@ typedef struct {
        VIRT_STYLE virtstyle;/**< The style of virtualization, if any */
        uint8_t cidrlen;     /**< The length of the mask when we use
                                  CIDR-style virtualization */
+       gchar* prerun;       /**< command to be ran after connecting a client,
+                                 but before starting to serve */
+       gchar* postrun;      /**< command that will be ran after the client
+                                 disconnects */
 } SERVER;
 
 /**
@@ -530,6 +534,8 @@ GArray* parse_cfile(gchar* f, GError** e) {
                { "timeout",    FALSE,  PARAM_INT,      NULL, 0 },
                { "filesize",   FALSE,  PARAM_INT,      NULL, 0 },
                { "virtstyle",  FALSE,  PARAM_STRING,   NULL, 0 },
+               { "prerun",     FALSE,  PARAM_STRING,   NULL, 0 },
+               { "postrun",    FALSE,  PARAM_STRING,   NULL, 0 },
                { "readonly",   FALSE,  PARAM_BOOL,     NULL, F_READONLY },
                { "multifile",  FALSE,  PARAM_BOOL,     NULL, F_MULTIFILE },
                { "copyonwrite", FALSE, PARAM_BOOL,     NULL, F_COPYONWRITE },
@@ -576,8 +582,11 @@ GArray* parse_cfile(gchar* f, GError** e) {
                lp[3].target=&(s.timeout);
                lp[4].target=&(s.expected_size);
                lp[5].target=&(virtstyle);
-               lp[6].target=lp[7].target=lp[8].target=
-                               lp[9].target=lp[10].target=&(s.flags);
+               lp[6].target=&(s.prerun);
+               lp[7].target=&(s.postrun);
+               lp[8].target=lp[9].target=lp[10].target=
+                               lp[11].target=lp[12].target=&(s.flags);
+               
                /* After the [generic] group, start parsing exports */
                if(i==1) {
                        p=lp;
@@ -1045,7 +1054,7 @@ void negotiate(CLIENT *client) {
  * pieces. Preferably with a chainsaw.
  *
  * @param client The client we're going to serve to.
- * @return never
+ * @return when the client disconnects
  **/
 int mainloop(CLIENT *client) {
        struct nbd_request request;
@@ -1233,6 +1242,25 @@ int copyonwrite_prepare(CLIENT* client) {
 }
 
 /**
+ * Run a command. This is used for the ``prerun'' and ``postrun'' config file
+ * options
+ *
+ * @param command the command to be ran. Read from the config file
+ * @param file the file name we're about to export
+ **/
+int do_run(gchar* command, gchar* file) {
+       gchar* cmd;
+       int retval=0;
+
+       if(*command) {
+               cmd = g_strdup_printf(command, file);
+               retval=system(cmd);
+               g_free(cmd);
+       }
+       return retval;
+}
+
+/**
  * Serve a connection. 
  *
  * @todo allow for multithreading, perhaps use libevent. Not just yet, though;
@@ -1249,7 +1277,10 @@ void serveconnection(CLIENT *client) {
 
        setmysockopt(client->net);
 
-       mainloop(client);
+       if(!do_run(client->server->prerun, client->exportname)) {
+               mainloop(client);
+       }
+       do_run(client->server->postrun, client->exportname);
 }
 
 /**
@@ -1298,10 +1329,10 @@ void set_peername(int net, CLIENT *client) {
                        break;
        }
 
-       g_free(peername);
        msg4(LOG_INFO, "connect from %s, assigned file is %s", 
             peername, client->exportname);
        client->clientname=g_strdup(peername);
+       g_free(peername);
 }
 
 /**
@@ -1323,6 +1354,9 @@ void destroy_pid_t(gpointer data) {
 void daemonize(SERVER* serve) {
        FILE*pidf;
 
+       if(serve && !(serve->port)) {
+               return;
+       }
        if(daemon(0,0)<0) {
                err("daemon");
        }
@@ -1496,6 +1530,7 @@ int serveloop(GArray* servers) {
 #endif // NOFORK
                                        msg2(LOG_INFO,"Starting to serve");
                                        serveconnection(client);
+                                       exit(EXIT_SUCCESS);
                                }
                        }
                }
@@ -1544,31 +1579,28 @@ int main(int argc, char *argv[]) {
        }
        if(serve) {
                g_array_append_val(servers, *serve);
-       }
-
-/* We don't support this at this time */
-#if 0
-       if (!(serve->port)) {
-               CLIENT *client;
+     
+               if (!(serve->port)) {
+                       CLIENT *client;
 #ifndef ISSERVER
-               /* You really should define ISSERVER if you're going to use
-                * inetd mode, but if you don't, closing stdout and stderr
-                * (which inetd had connected to the client socket) will let it
-                * work. */
-               close(1);
-               close(2);
-               open("/dev/null", O_WRONLY);
-               open("/dev/null", O_WRONLY);
-#endif
-               client=g_malloc(sizeof(CLIENT));
-               client->server=serve;
-               client->net=0;
-               client->exportsize=OFFT_MAX;
-               set_peername(0,client);
-               serveconnection(client);
-               return 0;
-        }
+                       /* You really should define ISSERVER if you're going to use
+                        * inetd mode, but if you don't, closing stdout and stderr
+                        * (which inetd had connected to the client socket) will let it
+                        * work. */
+                       close(1);
+                       close(2);
+                       open("/dev/null", O_WRONLY);
+                       open("/dev/null", O_WRONLY);
 #endif
+                       client=g_malloc(sizeof(CLIENT));
+                       client->server=serve;
+                       client->net=0;
+                       client->exportsize=OFFT_MAX;
+                       set_peername(0,client);
+                       serveconnection(client);
+                       return 0;
+               }
+       }
        if((!serve) && (!servers||!servers->len)) {
                g_message("Nothing to do! Bye!");
                exit(EXIT_FAILURE);