diff src/lwwire.c @ 10:36c4cda4b6c4

Add extension support with the PINGPONG extension Add extension support. There are two ways to add extensions: 1. as a shared object which will be loaded with ext=<filename> as a parameter to lwwire. See the lwwire_pingpong.c file for details. 2. By doing basically the same thing as a shared object but linking it into the main binary and calling lwwire_register_extension() appropriately.
author William Astle <lost@l-w.ca>
date Sat, 30 Jul 2016 13:16:39 -0600
parents a11b330771e0
children 38184ef1296d
line wrap: on
line diff
--- a/src/lwwire.c	Sat Jul 30 10:35:14 2016 -0600
+++ b/src/lwwire.c	Sat Jul 30 13:16:39 2016 -0600
@@ -39,6 +39,7 @@
 // for nanosleep
 #define _POSIX_C_SOURCE 199309L
 
+#include <dlfcn.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
@@ -61,6 +62,17 @@
 	int isconst;
 };
 
+struct lwwire_extension_data
+{
+	int enabled;
+	int (*handler)(int op);	// handle op, return -1 if not supported
+	int (*disable)(void);	// disable extension negotiated
+	int (*enable)(void);	// enable extension negotiated (return -1 if failed to init)
+	void (*reset)(void);	// on server reset opcode; extension will be disabled automatically here
+};
+
+struct lwwire_extension_data lwwire_extension_list[256];
+
 struct lwwire_driveinfo drivedata[256];
 
 void lwwire_protoerror(void);
@@ -74,7 +86,7 @@
 int lwwire_save_sector(int dn, int lsn, void *);
 int nonblock(int);
 int lwwire_drive_readononly(int dn);
-
+int lwwire_register_extension(int, int (*handler)(int), int (*enable)(void), int (*disable)(void), void (*reset)(void));
 void lwwire_proto_read(void);
 void lwwire_proto_write(void);
 void lwwire_proto_readex(void);
@@ -103,10 +115,43 @@
 	}
 
 	memset(&drivedata, 0, sizeof(drivedata));
+	memset(lwwire_extension_list, 0, sizeof(struct lwwire_extension_data) * 256);
 
+	// call lwwire_register_extension() for each extension you define
+	// statically. Add those calls here, or calls to the equivalent
+	// of the "lwwire_register" function from a DSO type extension.
+	
 	for (i = 1; i < argc; i++)
 	{
-		if (strncmp("drive=", argv[i], 6) == 0)
+		if (strncmp("ext=", argv[i], 4) == 0)
+		{
+			// SO name is at argv[i]+4
+			void *dlhandle;
+			void *initfn;
+			int (*rinitfn)(void);
+			
+			dlhandle = dlopen(argv[i] + 4, RTLD_NOW | RTLD_LOCAL);
+			if (!dlhandle)
+			{
+				fprintf(stderr, "Cannot load extension %s: %s\n", argv[i] + 4, dlerror());
+				continue;
+			}
+			initfn = dlsym(dlhandle, "lwwire_register");
+			if (!initfn)
+			{
+				dlclose(dlhandle);
+				fprintf(stderr, "Extension '%s' is not valid.\n", argv[i] + 4);
+				continue;
+			}
+			rinitfn = initfn;
+			if ((*rinitfn)() != 0)
+			{
+				dlclose(dlhandle);
+				fprintf(stderr, "Initialization of extension '%s' failed.\n", argv[i] + 4);
+				continue;
+			}
+		}
+		else if (strncmp("drive=", argv[i], 6) == 0)
 		{
 			int dn=0;
 			int isconst = 0;
@@ -331,31 +376,68 @@
 void lwwire_proto_requestextension(void)
 {
 	unsigned char buf[1];
-	
+	int ext;
+		
 	if (lwwire_read(buf, 1) < 0)
 		return;
-	// NAK the request
-	buf[0] = 0x55;
+	ext = buf[0];
+	buf[0] = 0x55;	// default to NAK
+	if (lwwire_extension_list[ext].enable)
+	{
+		if ((*(lwwire_extension_list[ext].enable))() == 0)
+		{
+			// enable succeeded; enable it
+			buf[0] = 0x42;
+			lwwire_extension_list[ext].enabled = 1;
+		}
+	}
 	lwwire_write(buf, 1);
 }
 
 void lwwire_proto_disableextension(void)
 {
 	unsigned char buf[1];
+	int ext;
 	
 	if (lwwire_read(buf, 1) < 0)
 		return;
-	// ACK disabling any unsupported extensions
-	buf[0] = 0x42;
+	ext = buf[0];
+	
+	buf[0] = 0x42;	// default to ACK
+	if (lwwire_extension_list[ext].disable)
+	{
+		if ((*(lwwire_extension_list[ext].disable))() != 0)
+		{
+			// extension says it can't be disabled; NAK response
+			buf[0] = 0x55;
+		}
+	}
+	if (buf[0] == 0x42)
+		lwwire_extension_list[ext].enabled = 0;
 	lwwire_write(buf, 1);
 }
 
 void lwwire_proto_extensionop(void)
 {
-	unsigned char buf[1];
-	if (lwwire_read(buf, 1) < 0)
+	unsigned char buf[2];
+	int ext;
+	int op;
+	
+	if (lwwire_read(buf, 2) < 0)
 		return;
-	// we don't currently support any extensions so treat as unknown
+	ext = buf[0];
+	op = buf[1];
+	if (lwwire_extension_list[ext].enabled == 1)
+	{
+		if (lwwire_extension_list[ext].handler)
+		{
+			if ((*(lwwire_extension_list[ext].handler))(op) == 0)
+				return;
+		}
+		lwwire_protoerror();
+		return;
+	}
+	// extension not enabled; do a protocol error
 	lwwire_protoerror();
 }
 
@@ -675,4 +757,34 @@
 */
 void lwwire_reset(void)
 {
+	int i;
+	
+	// tell all extensions to reset and disable them
+	for (i = 0; i < 256; i++)
+	{
+		if (lwwire_extension_list[i].handler)
+		{
+			(*(lwwire_extension_list[i].reset))();
+		}
+		lwwire_extension_list[i].enabled = 0;
+	}
 }
+
+/*
+Register an extension. This will replace the registration if the same
+extension number is registered twice. Returns -1 on parameter error.
+ALL function arguments are required. num must be between 0 and 255 inclusive
+currently.  Returns 0 if the extension is registered.
+*/
+int lwwire_register_extension(int num, int (*handler)(int), int (*enable)(void), int (*disable)(void), void (*reset)(void))
+{
+	if (!handler || !enable || !disable || !reset)
+		return -1;
+	if (num < 0 || num > 255)
+		return -1;
+	lwwire_extension_list[num].handler = handler;
+	lwwire_extension_list[num].enable = enable;
+	lwwire_extension_list[num].disable = disable;
+	lwwire_extension_list[num].reset = reset;
+	return 0;
+}