rough framework
[lemu] / common.c
diff --git a/common.c b/common.c
new file mode 100644 (file)
index 0000000..fb45589
--- /dev/null
+++ b/common.c
@@ -0,0 +1,150 @@
+/*
+ *  General utility functions which don't have better places to live.
+ *
+ */
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netdb.h>
+#include <string.h>
+#include <errno.h>
+
+#include "common.h"
+#include "notify.h"
+
+/** timeval_diff
+ *  Calculate the difference between two timevals.
+ *  Lifted wholesale from the GNU libc manual.
+**/
+int
+timeval_diff(struct timeval *result, struct timeval *x, struct timeval *y)
+{
+       /* Perform the carry for the later subtraction by updating y. */
+       if (x->tv_usec < y->tv_usec) {
+               int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
+               y->tv_usec -= 1000000 * nsec;
+               y->tv_sec += nsec;
+       }
+       if (x->tv_usec - y->tv_usec > 1000000) {
+               int nsec = (x->tv_usec - y->tv_usec) / 1000000;
+               y->tv_usec += 1000000 * nsec;
+               y->tv_sec -= nsec;
+       }
+
+       /* Compute the time remaining to wait.
+          tv_usec is certainly positive. */
+       result->tv_sec = x->tv_sec - y->tv_sec;
+       result->tv_usec = x->tv_usec - y->tv_usec;
+
+       /* Return 1 if result is negative. */
+       return x->tv_sec < y->tv_sec;
+}
+
+/** timeval_increment
+ *  Increment one timeval by another.
+**/
+void
+timeval_increment(struct timeval *result, struct timeval *diff)
+{
+       const long u_factor = 1000000;
+
+       result->tv_sec += diff->tv_sec;
+       result->tv_usec += diff->tv_usec;
+       if (result->tv_usec < 0) {
+               result->tv_usec += u_factor;
+               result->tv_sec--;
+       } else if (result->tv_usec > u_factor) {
+               result->tv_usec -= u_factor;
+               result->tv_sec++;
+       }
+}
+
+/** string_to_addrinfo_call
+ *  Attempt to parse a sockaddr ip and port out of a string,
+ *  calls ai_cb function with result (until one success, or
+ *  for all results depending on flag values).
+ *
+ *  IPv6 addresses must be [bracketed]. (Because of colons.)
+ *  Accepts:
+ *      [ipv6]:port
+ *      ipv4:port
+ *  Returns:
+ *      0 on valid parse
+ *      -1 on error
+**/
+#define _CB_ITER_ALL    (1<<0)
+int
+string_to_addrinfo_call(char *in, unsigned int flags, int (*ai_cb)(struct addrinfo *, void *), void *cb_data)
+{
+       char *full_string, *port_start, *addr_start, *tmp;
+       struct addrinfo hints, *res=NULL, *res_iter;
+       int r;
+
+       full_string = strdup(in);
+       if (!full_string) {
+               NOTIFY_ERROR("%s:%s", "strdup", strerror(errno) );
+               return -1;
+       }
+
+       addr_start = strchr(full_string, '[');
+       if (addr_start) {
+               *addr_start++ = '\0';
+               tmp = strchr(addr_start, ']');
+               if (!tmp) {
+                       NOTIFY_ERROR("invalid %saddress '%s': %s", "IPv6 ", in, "unmatched brackets");
+                       free(full_string);
+                       return -1;
+               }
+               *tmp++ = '\0';
+               port_start = tmp;
+       } else {
+               addr_start = full_string;
+               port_start = addr_start;
+       }
+
+       tmp = strrchr(port_start, ':');
+       if (!tmp) {
+               NOTIFY_ERROR("invalid %saddress '%s': %s", "", in, "no port specified");
+               free(full_string);
+               return -1;
+       }
+       *tmp++ = '\0';
+       port_start = tmp;
+
+       /* We now have address and port as separate strings */
+
+       memset(&hints, 0, sizeof hints);
+
+       hints.ai_family = AF_UNSPEC;
+       hints.ai_socktype = SOCK_STREAM;
+
+       /* bind to equiv of INADDR_ANY for any AF, if address is specified as '*' */
+       if (strcmp(addr_start, "*") == 0) {
+               hints.ai_flags |= AI_PASSIVE;
+               addr_start = NULL;
+       }
+
+       r = getaddrinfo(addr_start, port_start, &hints, &res);
+       if (r) {
+               NOTIFY_ERROR("%s:%s", "getaddrinfo", gai_strerror(r));
+               free(full_string);
+               return -1;
+       }
+
+       for (res_iter = res; res_iter; res_iter = res_iter->ai_next) {
+               r = ai_cb(res_iter, cb_data);
+               if (r) {
+                       NOTIFY_DEBUG("%s:%d", "(ai_cb)", r);
+                       continue;
+               }
+
+               if (! (flags & _CB_ITER_ALL)) {
+                       break;
+               }
+       }
+       freeaddrinfo(res);
+       free(full_string);
+
+       return 0;
+}