Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 24 Jun 2007 12:58:54 GMT
From:      Matus Harvan <mharvan@FreeBSD.org>
To:        Perforce Change Reviews <perforce@FreeBSD.org>
Subject:   PERFORCE change 122228 for review
Message-ID:  <200706241258.l5OCws3V030993@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=122228

Change 122228 by mharvan@mharvan_home on 2007/06/24 12:58:32

	rewrite to use libevent
	to report errors plugins now call a special function in the daemon

Affected files ...

.. //depot/projects/soc2007/mharvan-mtund/mtund.src/Makefile#2 edit
.. //depot/projects/soc2007/mharvan-mtund/mtund.src/plugin.h#2 edit
.. //depot/projects/soc2007/mharvan-mtund/mtund.src/plugin_tcp.c#2 edit
.. //depot/projects/soc2007/mharvan-mtund/mtund.src/plugin_udp.c#2 edit
.. //depot/projects/soc2007/mharvan-mtund/mtund.src/tunneld.c#2 edit
.. //depot/projects/soc2007/mharvan-mtund/mtund.src/tunneld.h#2 edit

Differences ...

==== //depot/projects/soc2007/mharvan-mtund/mtund.src/Makefile#2 (text+ko) ====

@@ -1,6 +1,10 @@
 # Linux needs -ldl
-#LIBS= -ldl
-CFLAGS=-Wall -rdynamic -g 
+#LIBS+= -ldl
+
+LIBS=-L/usr/local/lib -levent
+CFLAGS=-Wall -rdynamic
+CFLAGS+=-g
+CFLAGS+=-I/usr/local/include
 
 all: tunneld plugin_tcp.so plugin_udp.so
 

==== //depot/projects/soc2007/mharvan-mtund/mtund.src/plugin.h#2 (text+ko) ====

@@ -57,7 +57,7 @@
  * from the client was accepted, return >0 so that it becomes the
  * current_pl!
  */
-int plugin_receive(plugint *pl);
+void plugin_receive(int fd, short ev_type, void *arg);
 
 /*
  * Send the data.

==== //depot/projects/soc2007/mharvan-mtund/mtund.src/plugin_tcp.c#2 (text+ko) ====

@@ -228,7 +228,7 @@
     }
 
     if (data->fd != -1) {
-	register_select_fd(data->fd);
+	register_select_fd(data->fd, plugin_receive, pl);
 	return 0;
     } else return -1;
 }
@@ -240,7 +240,8 @@
     data->state = PLUGIN_STATE_UNINITIALIZED;
 }
 
-int plugin_receive(plugint *pl) {
+void plugin_receive(int fd, short ev_type, void *arg) {
+    plugint *pl = (plugint *) arg;
     plugin_tcp_datat *data = (plugin_tcp_datat*) pl->data;
     int n = 0;
     char packet[PACKETLEN];
@@ -249,41 +250,38 @@
     //fprintf(stderr, "state: %d\n", data->state);
     if (! (data->state == PLUGIN_STATE_CONNECTED
 	   || data->state == PLUGIN_STATE_INITIALIZED)) {
-	return -1;
+	report_plugin_error(pl, PLUGIN_ERROR_RECEIVE);
     }
 
-    if (fd_isset(data->fd)) {
-	//fprintf(stderr, "data on plugin fd\n");
-
-	if (data->state != PLUGIN_STATE_CONNECTED) {
-            new_fd = tcp_accept(data->fd);
-            if (new_fd == -1) {
-		fprintf(stderr, "accept failed\n");
-                return -1;
-            } else {
-		unregister_select_fd(data->fd);
-		tcp_close(data->fd);
-		data->fd = new_fd;
-		register_select_fd(data->fd);
-		data->state = PLUGIN_STATE_CONNECTED;
-		fprintf(stderr, "new client connection accepted\n");
-		return 1;
-	    }
+    //fprintf(stderr, "data on plugin fd\n");
+    if (data->state != PLUGIN_STATE_CONNECTED) {
+	new_fd = tcp_accept(fd);
+	if (new_fd == -1) {
+	    fprintf(stderr, "accept failed\n");
+	    report_plugin_error(pl, PLUGIN_ERROR_RECEIVE);
 	} else {
-	    //n = read(data->fd, packet, sizeof(packet));
-	    n = recv(data->fd, packet, sizeof(packet),0);
-	    fprintf(stderr, "n: %d\n", n);
-	    if (n <= 0) {
-		/* client disconnected */
-		unregister_select_fd(data->fd);
-		data->state = PLUGIN_STATE_DISCONNECTED;
-		return -1;
-	    } else if (n > 0) {
-		return tun_send(packet, n);
-	    }
+	    unregister_select_fd(data->fd);
+	    tcp_close(data->fd);
+	    data->fd = new_fd;
+	    register_select_fd(data->fd, plugin_receive, pl);
+	    data->state = PLUGIN_STATE_CONNECTED;
+	    fprintf(stderr, "new client connection accepted\n");
+	    //report_plugin_error(pl, PLUGIN_ERROR_SUCCESS);
+	}
+    } else {
+	//n = read(data->fd, packet, sizeof(packet));
+	n = recv(fd, packet, sizeof(packet),0);
+	//fprintf(stderr, "n: %d\n", n);
+	if (n <= 0) {
+	    /* client disconnected */
+	    unregister_select_fd(fd);
+	    data->state = PLUGIN_STATE_DISCONNECTED;
+	    report_plugin_error(pl, PLUGIN_ERROR_RECEIVE);
+	} else if (n > 0) {
+	    process_data_from_plugin(pl, packet, n);
 	}
     }
-    return 0;
+    //report_plugin_error(pl, PLUGIN_ERROR_SUCCESS);
 }
 
 /*
@@ -299,8 +297,11 @@
     } else {
 	//n = write(datapl->fd, data, len);
 	n = send(datapl->fd, data, len, 0);
-	fprintf(stderr, "plugin_send: fd: %d\n", datapl->fd);
-	fprintf(stderr, "plugin_send: write returned %d\n", n);
+	//fprintf(stderr, "plugin_send: fd: %d\n", datapl->fd);
+	//fprintf(stderr, "plugin_send: write returned %d\n", n);
+    }
+    if (n < 0 || (len != 0 && n == 0) ) {
+	report_plugin_error(pl, PLUGIN_ERROR_RECEIVE);
     }
     return n;
 }

==== //depot/projects/soc2007/mharvan-mtund/mtund.src/plugin_udp.c#2 (text+ko) ====

@@ -165,7 +165,7 @@
     if (server) {
 	data->fd = udp_open(port);
 	if (data->fd != -1) {
-	    register_select_fd(data->fd);
+	    register_select_fd(data->fd, plugin_receive, pl);
 	    data->state = PLUGIN_STATE_INITIALIZED;
 	    return 0;
 	}
@@ -174,7 +174,7 @@
 	if (data->fd != -1) {
 	    //n = send(fd,"test",5,0);
 	    //if (n == 5) {
-		register_select_fd(data->fd);
+		register_select_fd(data->fd, plugin_receive, pl);
 		data->state = PLUGIN_STATE_CONNECTED;
 		return 0;
 		//}
@@ -190,7 +190,8 @@
     data->state = PLUGIN_STATE_UNINITIALIZED;
 }
 
-int plugin_receive(plugint *pl) {
+void plugin_receive(int fd, short ev_type, void *arg) {
+    plugint *pl = (plugint *) arg;
     plugin_udp_datat *data = (plugin_udp_datat*) pl->data;
     int n = 0;
     char packet[PACKETLEN];
@@ -201,46 +202,45 @@
 
     if (! (data->state == PLUGIN_STATE_CONNECTED
 	   || data->state == PLUGIN_STATE_INITIALIZED)) {
-	return -1;
+	//return -1;
     }
 
-    if (fd_isset(data->fd)) {
-	fprintf(stderr, "data on plugin fd\n");
+    fprintf(stderr, "data on plugin fd\n");
 
-	if (data->state != PLUGIN_STATE_CONNECTED) {
-	    /* client connecting for the first time, connect back */
-	    n = recvfrom(data->fd, packet, sizeof(packet), 0,
-			 (struct sockaddr *) &from, &fromlen);
-	    
-	    if (getnameinfo((struct sockaddr *) &from, fromlen,
-			    host, sizeof(host), serv, sizeof(serv),
-			    NI_NUMERICHOST | NI_DGRAM) ) {
-		fprintf(stderr, "getnameinfo failed: %s\n",
-			gai_strerror(errno));
-	    } else {
-		fprintf(stderr, "received traffic from client %s:%s\n",
-			host, serv);
-	    }
-
-	    if (connect(data->fd, (struct sockaddr *) &from, fromlen) == 0) {
-		data->state = PLUGIN_STATE_CONNECTED;
-		fprintf(stderr, "connected to client %s:%s\n", host, serv);
+    if (data->state != PLUGIN_STATE_CONNECTED) {
+	/* client connecting for the first time, connect back */
+	n = recvfrom(data->fd, packet, sizeof(packet), 0,
+		     (struct sockaddr *) &from, &fromlen);
+	
+	if (getnameinfo((struct sockaddr *) &from, fromlen,
+			host, sizeof(host), serv, sizeof(serv),
+			NI_NUMERICHOST | NI_DGRAM) ) {
+	    fprintf(stderr, "getnameinfo failed: %s\n",
+		    gai_strerror(errno));
+	} else {
+	    fprintf(stderr, "received traffic from client %s:%s\n",
+		    host, serv);
+	}
+	
+	if (connect(data->fd, (struct sockaddr *) &from, fromlen) == 0) {
+	    data->state = PLUGIN_STATE_CONNECTED;
+	    fprintf(stderr, "connected to client %s:%s\n", host, serv);
 	    } else {
 		fprintf(stderr, "failed to connect to client %s:%s\n",
 			host, serv);
 	    }
-	} else {
-	    n = recv(data->fd, packet, sizeof(packet), 0);
-	}
-	
-	if (n == -1) {
-	    return -1;
-	} else if (n > 0) {
-	    return tun_send(packet, n);
-	}
-	fprintf(stderr, "plugin_receive: recv return 0\n");
+    } else {
+	n = recv(data->fd, packet, sizeof(packet), 0);
+    }
+    
+    if (n == -1) {
+	//return -1;
+    } else if (n > 0) {
+	process_data_from_plugin(pl, packet, n);
+	//return tun_send(packet, n);
     }
-    return 0;
+    fprintf(stderr, "plugin_receive: recv return 0\n");
+    //return 0;
 }
 
 int plugin_send(plugint *pl, char *data, int len) {

==== //depot/projects/soc2007/mharvan-mtund/mtund.src/tunneld.c#2 (text+ko) ====

@@ -16,8 +16,11 @@
 #include <stdlib.h>
 #include <getopt.h>
 
-#include <sys/select.h>
+//#include <sys/select.h>
 #include <dlfcn.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <event.h>
 
 #include "tun_dev.h"
 
@@ -28,11 +31,17 @@
 
 int server = 0; /* are we a server or a client? */
 plugint *plugins = NULL; /* linked list of loaded plugins */
-int tun_fd = 0; /* tunnel device */
 int pl_fd = 0; /* plugin socket */
 plugint *current_pl = NULL; /* currently active plugin if we are a client */
 fd_set fdset;
+char tun_dev[16];
+int tun_fd = 0; /* tunnel device */
+struct event tun_ev;
 
+char *host;
+char *port = "12345";
+    
+
 /* 
  * helper function to register/unregisted/check file descriptors to be
  * watched by select
@@ -41,20 +50,14 @@
 /* helper structure to put file descriptors into a linked list */
 typedef struct _fdlt {
     int fd;
+    struct event ev;
     struct _fdlt *next;
 } fdlt;
 fdlt *fdl;
 
-static void update_fdset() {
-    fdlt *p;
-
-    FD_ZERO(&fdset);
-    for(p = fdl; p; p=p->next) {
-	FD_SET(p->fd, &fdset);
-    }
-}
-
-void register_select_fd(int fd) {
+void register_select_fd(int fd,
+			void (*ev_callback)(int, short, void *arg), void *arg)
+{
     fdlt* nfdl = malloc(sizeof(fdlt));
     if (!nfdl) {
 	fprintf(stderr, "failed to malloc an fdlt: out of mem!\n");
@@ -63,7 +66,11 @@
     nfdl->fd = fd;
     nfdl->next = fdl;
     fdl = nfdl;
+
     //update_fdset();
+
+    event_set(&nfdl->ev, fd, EV_PERSIST | EV_READ, ev_callback, arg);
+    event_add(&nfdl->ev, NULL);
 }
 
 void unregister_select_fd(int fd) {
@@ -71,6 +78,11 @@
     for(q = &fdl, p = fdl; p; q = &(p->next), p=p->next) {
 	if (p->fd == fd) {
 	    *q = p->next;
+
+	    //event_set(&nfdl->ev, fd, EV_READ, ev_callback, arg);
+	    event_set(&p->ev, fd, EV_READ, NULL, NULL);
+	    event_del(&p->ev);
+
 	    free(p);
 	    break;
 	}
@@ -82,94 +94,55 @@
     return FD_ISSET(fd, &fdset);
 }
 
-static int max_fd() {
-    int m = -1;
-    fdlt *p;
-    for(p = fdl;p;p=p->next) {
-	fprintf(stderr, "p->fd: %d\n", p->fd);
-	if (p->fd > m)
-	    m = p->fd;
-    }
-    fprintf(stderr, "maxfd: %d\n", m);
-    return m;
+int
+ssystem(const char *fmt, ...)
+{
+  char cmd[128];
+  va_list ap;
+  va_start(ap, fmt);
+  vsnprintf(cmd, sizeof(cmd), fmt, ap);
+  va_end(ap);
+  fprintf(stderr, "%s\n", cmd);
+  //fflush(stderr);
+  //fflush(stdout);
+  return system(cmd);
 }
 
 /* tun functions - this could be turned into a plugin as well */
 
-int tun_receive() {
+void tun_receive(int fd, short ev_type, void *arg) {
     char packet[PACKETLEN];
     int result = 0;
     
-    if (fd_isset(tun_fd)) {
-	/* data available on tunnel device */
-	memset(packet, 0, sizeof(packet));
-	result = tun_read(tun_fd, packet, PACKETLEN);
-	if (!result) {
-	    return 0;
-	} else if (result==-1) {
-	    perror ("read");
-	    return -1;
-	}
-	fprintf(stderr, "data on tun interface\n");
-	if (current_pl == NULL) {
-	    fprintf(stderr, "no plugin connected yet, discarding tun data\n");
-	    return 0;
-	}
-	result = current_pl->send(current_pl, packet, result);
-	if (result==-1) {
-	    perror ("plugin_send returned -1");
-	}
+    /* data available on tunnel device */
+    memset(packet, 0, sizeof(packet));
+    result = tun_read(tun_fd, packet, PACKETLEN);
+
+    if (!result) {
+	//return 0;
+    } else if (result==-1) {
+	perror ("read");
+	//return -1;
     }
-    return result;
+    //fprintf(stderr, "data on tun interface\n");
+    process_data_from_tun(packet, result);
+    //return result;
 }
 
 int tun_send(char *data, int len) {
     int n;
     n = tun_write(tun_fd, data, len);
-    fprintf(stderr, "tun_send: send returned %d\n", n);
+    /*
+    if (n != len) {
+	fprintf(stderr, "tun_send: tun_write wrote less bytes (%d) than "
+		"requested (%d)\n", n, len);
+	// report error
+    }
+    */
     return n;
 }
 
 /* 
- * sit in select
- * call tun_receive and plugin_receive when select fires
- */
-static int tunnel_select() {
-    int result_tun = 0;
-    int result_plugin = 0;
-    int n = 0;
-    plugint *pl;
-
-    while (result_tun >= 0 && result_plugin >= 0) {
-	update_fdset();
-	select (max_fd()+1, &fdset, NULL, NULL, NULL);
-	fprintf(stderr, "tunnel_select: select fired\n");
-
-	fprintf(stderr, "tunnel_select: server: %d\n", server);
-
-	result_tun = tun_receive();
-	if (server) {
-	    /* check all plugins */
-	    result_plugin = -1;
-	    for (pl=plugins; pl; pl = pl->next) {
-		fprintf(stderr, "select - calling \"%s\"->receive()...",
-			pl->name);
-		n = pl->receive(pl);
-		fprintf(stderr, "%d\n", n);
-		if (n > 0) current_pl = pl;
-		if (n > result_plugin) result_plugin = n;
-	    }
-	} else {
-	    result_plugin = (current_pl->receive)(current_pl);
-	}
-
-	fprintf(stderr, "tunnel_select: result_tun: %d, result_plugin: %d\n",
-		result_tun, result_plugin);
-    }
-    return 0;
-}
-
-/* 
  * BUGS: if dlclose() is called, I get a segfault when trying to use
  * the loaded functions. Maybe I'm not passing the right flags to
  * dlopen()? Well, RTLD_NODELETE seems to help here, at least on
@@ -214,6 +187,87 @@
 	return 2;
 }
 
+/*
+ * Pass data received by the plugin to the daemon.
+ */
+void process_data_from_plugin(plugint *pl, char *data, int len)
+{
+    tun_send(data, len);
+}
+
+/*
+ * Pass data received from the tun interface to the daemon.
+ */
+void process_data_from_tun(char *data, int len)
+{
+    int n;
+
+    if (current_pl == NULL) {
+	fprintf(stderr, "no plugin connected yet, discarding tun data\n");
+	report_plugin_error(NULL, PLUGIN_ERROR_BOOTSTRAP);
+    }
+
+    n = current_pl->send(current_pl, data, len);
+
+    if (n < len) {
+	fprintf(stderr, "process_data_from_tun: plugind sent less "
+		"bytes (%d) than requested (%d)\n", n, len);
+    }
+}
+
+void report_plugin_error(plugint *pl, int err)
+{
+    if (err == PLUGIN_ERROR_SUCCESS) {
+	return;
+    }
+
+    if (!server) {
+	if (pl) {
+	    /* deinitialize the broken plugin */
+	    fprintf(stderr, "plugin failed: %s\n", pl->name);
+	    pl->deinitialize(pl);
+	}
+	
+	/* scan - find a working plugin */
+	//for (pl=plugins; pl; pl = pl->next) {
+	if (!pl) pl=plugins;
+	for (; pl; pl = pl->next) {
+	    /* try to initialize plugin */
+	    //if (((pl->initialize)(server, host, port)) == 0) {
+	    fprintf(stderr, "initalizing plugin: %s...\n", pl->name);
+	    if (((pl->initialize)(pl, server, host, pl->name+4)) == 0) {
+		fprintf(stderr, "found a working plugin: %s\n", pl->name);
+		current_pl = pl;
+		return;
+	    } else {
+		fprintf(stderr, "plugin %s failed to initialize\n", pl->name);
+	    }
+	}
+    }
+}
+
+void cleanup()
+{
+    plugint *pl;
+
+    event_del(&tun_ev);
+    tun_close(tun_fd, tun_dev);
+
+    /* deinitialize all plugins and free memory */
+    while(plugins) {
+	pl = plugins;
+	plugins=plugins->next;
+	pl->deinitialize(pl);
+	free(pl);
+    }
+    exit(0);
+}
+
+void sigcb(int sig)
+{
+    cleanup();
+}
+
 void usage() {
     fprintf(stderr, "usage:\n");
     fprintf(stderr, "server: tunneld -s -p port\n");
@@ -221,13 +275,9 @@
 }
 
 int main(int argc, char **argv) {
-    char dev[16];
     plugint *pl;
     char opt;
 
-    char *host;
-    char *port = "12345";
-    
     /* argument parsing */
     while((opt=getopt(argc, argv, "scp:")) != EOF) {
 	switch(opt) {
@@ -263,15 +313,33 @@
 	}
     }
 
+    /* Initalize the event library */
+    event_init();
+
     /* create the tunnel device */
-    dev[0] = 0;
-    tun_fd = tun_open(dev);
+    tun_dev[0] = 0;
+    tun_fd = tun_open(tun_dev);
     if (tun_fd < 1) {
 	printf("Could not create tunnel device\n");
 	return 1;
     }
-    register_select_fd(tun_fd);
-    printf("Created tunnel device: %s\n", dev);
+    /* Initalize one event */
+    event_set(&tun_ev, tun_fd, EV_PERSIST | EV_READ, tun_receive, NULL);
+    /* Add it to the active events, without a timeout */
+    event_add(&tun_ev, NULL);
+    //register_select_fd(tun_fd);
+    printf("Created tunnel device: %s\n", tun_dev);
+
+    /* setup the tun interface */
+    if (server) {
+	system("ifconfig tun0 mtu 1400 192.168.0.1 192.168.0.2");
+    } else {
+	system("ifconfig tun0 mtu 1400 192.168.0.2 192.168.0.1");
+    }
+
+    signal(SIGHUP, sigcb);
+    signal(SIGINT, sigcb);
+    signal(SIGTERM, sigcb);
     
     /* load plugins */
     load_plugin("./plugin_udp.so");
@@ -282,7 +350,7 @@
     plugins->name = "tcp_2222";
     load_plugin("./plugin_tcp.so");
     plugins->name = "tcp_3333";
-    
+
     if (server) {
 	/* initialize all plugins */
 	for (pl=plugins; pl; pl = pl->next) {
@@ -290,43 +358,14 @@
 	    (void) (pl->initialize)(pl, server, host, pl->name+4);
 	    /* we should unload plugins which fail to initialize */
 	}
-	/* 
-	 * start tunneling
-	 * - wait in select to see which plugin will be used (its fd will fire)
-	 */
-	tunnel_select();
-
-	/* deinitialize all plugins */
-	for (pl=plugins; pl; pl = pl->next) {
-	    pl->deinitialize(pl);
-	}
     } else {
-	/* scan - find a working plugin */
-	for (pl=plugins; pl; pl = pl->next) {
-	    /* try to initialize plugin */
-	    //if (((pl->initialize)(server, host, port)) == 0) {
-	    if (((pl->initialize)(pl, server, host, pl->name+4)) == 0) {
-		fprintf(stderr, "found a working plugin: %s\n", pl->name);
-		current_pl = pl;
-		
-		/* start tunneling */
-		tunnel_select();
-	    
-		fprintf(stderr, "plugin failed: %s\n", pl->name);
-		pl->deinitialize(pl);
-	    } else {
-		fprintf(stderr, "plugin %s failed to initialize\n", pl->name);
-	    }
-	}
+	/* initialize a working plugin */
+	report_plugin_error(NULL, PLUGIN_ERROR_BOOTSTRAP);
     }
 
-    tun_close(tun_fd, dev);
-
-    while(plugins) {
-	pl = plugins;
-	plugins=plugins->next;
-	free(pl);
-    }
-
-    return 0;
+    /* start tunneling */
+    event_dispatch();
+    
+    cleanup();
+    return 1;
 }

==== //depot/projects/soc2007/mharvan-mtund/mtund.src/tunneld.h#2 (text+ko) ====

@@ -18,16 +18,25 @@
     int (*initialize)(struct _plugint*, int, char*, char*);
     void (*deinitialize)(struct _plugint*);
     int (*send)(struct _plugint*, char*, int);
-    int (*receive)(struct _plugint*); /* select fired on some fd - check for data */
+    void (*receive)(int fd, short ev_type, void *arg); /* select fired on some fd - check for data */
     void* data;
     struct _plugint *next;
 } plugint;
 
+enum {
+    PLUGIN_ERROR_SUCCESS = 0,
+    PLUGIN_ERROR_BOOTSTRAP,
+    PLUGIN_ERROR_SEND,
+    PLUGIN_ERROR_RECEIVE,
+    PLUGIN_ERROR_TIMEOUT
+};
+
 /* --- select() --- */
 /*
  * Register file descriptor fd to be watched by the main select().
  */
-void register_select_fd(int fd);
+void register_select_fd(int fd,
+			void (*ev_callback)(int, short, void *arg), void *arg);
 /*
  * Unregister file descriptor fd to no longer be watched by the main
  * select().
@@ -36,12 +45,27 @@
 /*
  * Check whether select fired on the file descriptor - wrapper for FD_ISSET.
  */
-int fd_isset(int fd);
+//int fd_isset(int fd);
 
 /* --- tunnel --- */
 /*
  * Send data over the tun interface.
  */
-int tun_send(char *data, int len);
+//int tun_send(char *data, int len);
+
+/*
+ * Pass data received by the plugin to the daemon.
+ */
+void process_data_from_plugin(plugint *pl, char *data, int len);
+
+/*
+ * Pass data received from the tun interface to the daemon.
+ */
+void process_data_from_tun(char *data, int len);
+
+/*
+ * Report a problem in the plugin to the daemon.
+ */
+void report_plugin_error(plugint *pl, int err);
 
 #endif



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200706241258.l5OCws3V030993>