Generic socket-level options added

Signed-off-by: Martin Sustrik <sustrik@250bpm.com>
diff --git a/doc/sp_getsockopt.3.txt b/doc/sp_getsockopt.3.txt
index 110c9a1..a8323d9 100644
--- a/doc/sp_getsockopt.3.txt
+++ b/doc/sp_getsockopt.3.txt
@@ -15,7 +15,10 @@
 DESCRIPTION
 -----------
 Retrieves the value for the 'option'. The 'level' argument specifies
-the protocol level at which the option resides.
+the protocol level at which the option resides. For generic socket-level options
+use _SP_SOL_SOCKET_ level. For socket-type-specific options use socket type
+for 'level' argument (e.g. _SP_SUB_). For transport-specific options use ID of
+the transport as the 'level' argument (e.g. _SP_TCP_).
 
 The value is stored in the buffer pointed to by 'optval' argument. Size of the
 buffer is specified by the 'optvallen' argument. If the size of the option is
@@ -23,6 +26,24 @@
 Otherwise, the 'optvallen' will be modified to indicate the actual length of
 the option.
 
+_<sp/sp.h>_ header defines generic socket-level options. The options are as
+follows:
+
+*SP_LINGER*::
+    Option type is int. Default is 1000.
+*SP_SNDBUF*::
+    Option type is int. Default is 128kB.
+*SP_RCVBUF*::
+    Option type is int. Default is 128kB.
+*SP_SNDTIMEO*::
+    Option type is int. Default is 0 (no timeout).
+*SP_RCVTIMEO*::
+    Option type is int. Default is 0 (no timeout).
+*SP_RECONNECT_IVL*::
+    Option type is int. Default is 100.
+*SP_RECONNECT_IVL_MAX*::
+    Option type is int. Default is 0.
+
 
 RETURN VALUE
 ------------
@@ -37,7 +58,7 @@
 *EBADF*::
 The provided socket is invalid.
 *ENOPROTOOPT*::
-The option is not supported by the protocol.
+The option is unknown at the level indicated.
 *ETERM*::
 The library is terminating.
 
diff --git a/doc/sp_setsockopt.3.txt b/doc/sp_setsockopt.3.txt
index e8bca72..10749d2 100644
--- a/doc/sp_setsockopt.3.txt
+++ b/doc/sp_setsockopt.3.txt
@@ -16,11 +16,32 @@
 DESCRIPTION
 -----------
 Sets the value of the 'option'. The 'level' argument specifies the protocol
-level at which the option resides.
+level at which the option resides. For generic socket-level options use
+_SP_SOL_SOCKET_ level. For socket-type-specific options use socket type
+for 'level' argument (e.g. _SP_SUB_). For transport-specific options use ID of
+the transport as the 'level' argument (e.g. _SP_TCP_).
 
 The new value is poited to by 'optval' argument. Size of the option is specified
 by the 'optvallen' argument.
 
+_<sp/sp.h>_ header defines generic socket-level options. The options are as
+follows:
+
+*SP_LINGER*::
+    Option type is int. Default is 1000.
+*SP_SNDBUF*::
+    Option type is int. Default is 128kB.
+*SP_RCVBUF*::
+    Option type is int. Default is 128kB.
+*SP_SNDTIMEO*::
+    Option type is int. Default is 0 (no timeout).
+*SP_RCVTIMEO*::
+    Option type is int. Default is 0 (no timeout).
+*SP_RECONNECT_IVL*::
+    Option type is int. Default is 100.
+*SP_RECONNECT_IVL_MAX*::
+    Option type is int. Default is 0.
+
 
 RETURN VALUE
 ------------
@@ -35,9 +56,9 @@
 *EBADF*::
 The provided socket is invalid.
 *ENOPROTOOPT*::
-The option is not supported by the protocol.
+The option is unknown at the level indicated.
 *EINVAL*::
-The specified value is invalid.
+The specified option value is invalid.
 *ETERM*::
 The library is terminating.
 
@@ -47,6 +68,7 @@
 ----
 int linger = 1000;
 sp_setsockopt (s, SOL_SP, SP_LINGER, &linger, sizeof (linger));
+sp_setsockopt (s, SP_SUB, SP_SUBSCRIBE, "ABC", 3);
 ----
 
 
diff --git a/src/core/sock.c b/src/core/sock.c
index 7e4e0c2..806bab2 100644
--- a/src/core/sock.c
+++ b/src/core/sock.c
@@ -39,6 +39,14 @@
     sp_cp_init (&self->cp);
     sp_cond_init (&self->cond);
     self->fd = fd;
+
+    /*  Default values for SP_SOL_SOCKET options. */
+    self->linger = 1000;
+    self->sndbuf = 128 * 1024;
+    self->rcvbuf = 128 * 1024;
+    self->sndtimeo = 0;
+    self->rcvtimeo = 0;
+    self->reconnect_ivl = 100;
 }
 
 void sp_sock_zombify (struct sp_sock *self)
@@ -91,6 +99,7 @@
 {
     int rc;
     struct sp_sockbase *sockbase;
+    int *dst;
 
     sockbase = (struct sp_sockbase*) self;
 
@@ -104,8 +113,39 @@
 
     /*  Generic socket-level options. */
     if (level == SP_SOL_SOCKET) {
+        switch (option) {
+        case SP_LINGER:
+            dst = &sockbase->linger;
+            break;
+        case SP_SNDBUF:
+            dst = &sockbase->sndbuf;
+            break;
+        case SP_RCVBUF:
+            dst = &sockbase->rcvbuf;
+            break;
+        case SP_SNDTIMEO:
+            dst = &sockbase->sndtimeo;
+            break;
+        case SP_RCVTIMEO:
+            dst = &sockbase->rcvtimeo;
+            break;
+        case SP_RECONNECT_IVL:
+            dst = &sockbase->reconnect_ivl;
+            break;
+        case SP_RECONNECT_IVL_MAX:
+            dst = &sockbase->reconnect_ivl_max;
+            break;
+        default:
+            sp_cp_unlock (&sockbase->cp);
+            return -ENOPROTOOPT;
+        }
+        if (optvallen != sizeof (int)) {
+            sp_cp_unlock (&sockbase->cp);
+            return -EINVAL;
+        }
+        *dst = *(int*) optval;
         sp_cp_unlock (&sockbase->cp);
-        return -ENOPROTOOPT;
+        return 0;
     }
 
     /*  Pattern-specific socket options. */
@@ -130,6 +170,7 @@
 {
     int rc;
     struct sp_sockbase *sockbase;
+    int *src;
 
     sockbase = (struct sp_sockbase*) self;
 
@@ -143,8 +184,37 @@
 
     /*  Generic socket-level options. */
     if (level == SP_SOL_SOCKET) {
+        switch (option) {
+        case SP_LINGER:
+            src = &sockbase->linger;
+            break;
+        case SP_SNDBUF:
+            src = &sockbase->sndbuf;
+            break;
+        case SP_RCVBUF:
+            src = &sockbase->rcvbuf;
+            break;
+        case SP_SNDTIMEO:
+            src = &sockbase->sndtimeo;
+            break;
+        case SP_RCVTIMEO:
+            src = &sockbase->rcvtimeo;
+            break;
+        case SP_RECONNECT_IVL:
+            src = &sockbase->reconnect_ivl;
+            break;
+        case SP_RECONNECT_IVL_MAX:
+            src = &sockbase->reconnect_ivl_max;
+            break;
+        default:
+            sp_cp_unlock (&sockbase->cp);
+            return -ENOPROTOOPT;
+        }
+        memcpy (optval, src,
+            *optvallen < sizeof (int) ? *optvallen : sizeof (int));
+        *optvallen = sizeof (int);
         sp_cp_unlock (&sockbase->cp);
-        return -ENOPROTOOPT;
+        return 0;
     }
 
     /*  Pattern-specific socket options. */
diff --git a/src/pattern.h b/src/pattern.h
index 1e654c4..777338e 100644
--- a/src/pattern.h
+++ b/src/pattern.h
@@ -81,6 +81,15 @@
 
     /*  File descriptor for this socket. */
     int fd;
+
+    /*  SP_SOL_SOCKET level options. */
+    int linger;
+    int sndbuf;
+    int rcvbuf;
+    int sndtimeo;
+    int rcvtimeo;
+    int reconnect_ivl;
+    int reconnect_ivl_max;
 };
 
 /*  Initialise the socket. */
diff --git a/src/sp.h b/src/sp.h
index 6ee3d14..8bce28a 100644
--- a/src/sp.h
+++ b/src/sp.h
@@ -150,7 +150,13 @@
 #define SP_SOL_SOCKET 0
 
 /*  Generic socket options (SP_SOL_SOCKET level).                             */
-/*  TODO */
+#define SP_LINGER 1
+#define SP_SNDBUF 2
+#define SP_RCVBUF 3
+#define SP_SNDTIMEO 4
+#define SP_RCVTIMEO 5
+#define SP_RECONNECT_IVL 6
+#define SP_RECONNECT_IVL_MAX 7
 
 /*  Send/recv options.                                                        */
 #define SP_DONTWAIT 1