rough framework
[lemu] / common.c
1 /*
2 * General utility functions which don't have better places to live.
3 *
4 */
5 #include <stdlib.h>
6 #include <sys/types.h>
7 #include <sys/socket.h>
8 #include <sys/time.h>
9 #include <netdb.h>
10 #include <string.h>
11 #include <errno.h>
12
13 #include "common.h"
14 #include "notify.h"
15
16 /** timeval_diff
17 * Calculate the difference between two timevals.
18 * Lifted wholesale from the GNU libc manual.
19 **/
20 int
21 timeval_diff(struct timeval *result, struct timeval *x, struct timeval *y)
22 {
23 /* Perform the carry for the later subtraction by updating y. */
24 if (x->tv_usec < y->tv_usec) {
25 int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
26 y->tv_usec -= 1000000 * nsec;
27 y->tv_sec += nsec;
28 }
29 if (x->tv_usec - y->tv_usec > 1000000) {
30 int nsec = (x->tv_usec - y->tv_usec) / 1000000;
31 y->tv_usec += 1000000 * nsec;
32 y->tv_sec -= nsec;
33 }
34
35 /* Compute the time remaining to wait.
36 tv_usec is certainly positive. */
37 result->tv_sec = x->tv_sec - y->tv_sec;
38 result->tv_usec = x->tv_usec - y->tv_usec;
39
40 /* Return 1 if result is negative. */
41 return x->tv_sec < y->tv_sec;
42 }
43
44 /** timeval_increment
45 * Increment one timeval by another.
46 **/
47 void
48 timeval_increment(struct timeval *result, struct timeval *diff)
49 {
50 const long u_factor = 1000000;
51
52 result->tv_sec += diff->tv_sec;
53 result->tv_usec += diff->tv_usec;
54 if (result->tv_usec < 0) {
55 result->tv_usec += u_factor;
56 result->tv_sec--;
57 } else if (result->tv_usec > u_factor) {
58 result->tv_usec -= u_factor;
59 result->tv_sec++;
60 }
61 }
62
63 /** string_to_addrinfo_call
64 * Attempt to parse a sockaddr ip and port out of a string,
65 * calls ai_cb function with result (until one success, or
66 * for all results depending on flag values).
67 *
68 * IPv6 addresses must be [bracketed]. (Because of colons.)
69 * Accepts:
70 * [ipv6]:port
71 * ipv4:port
72 * Returns:
73 * 0 on valid parse
74 * -1 on error
75 **/
76 #define _CB_ITER_ALL (1<<0)
77 int
78 string_to_addrinfo_call(char *in, unsigned int flags, int (*ai_cb)(struct addrinfo *, void *), void *cb_data)
79 {
80 char *full_string, *port_start, *addr_start, *tmp;
81 struct addrinfo hints, *res=NULL, *res_iter;
82 int r;
83
84 full_string = strdup(in);
85 if (!full_string) {
86 NOTIFY_ERROR("%s:%s", "strdup", strerror(errno) );
87 return -1;
88 }
89
90 addr_start = strchr(full_string, '[');
91 if (addr_start) {
92 *addr_start++ = '\0';
93 tmp = strchr(addr_start, ']');
94 if (!tmp) {
95 NOTIFY_ERROR("invalid %saddress '%s': %s", "IPv6 ", in, "unmatched brackets");
96 free(full_string);
97 return -1;
98 }
99 *tmp++ = '\0';
100 port_start = tmp;
101 } else {
102 addr_start = full_string;
103 port_start = addr_start;
104 }
105
106 tmp = strrchr(port_start, ':');
107 if (!tmp) {
108 NOTIFY_ERROR("invalid %saddress '%s': %s", "", in, "no port specified");
109 free(full_string);
110 return -1;
111 }
112 *tmp++ = '\0';
113 port_start = tmp;
114
115 /* We now have address and port as separate strings */
116
117 memset(&hints, 0, sizeof hints);
118
119 hints.ai_family = AF_UNSPEC;
120 hints.ai_socktype = SOCK_STREAM;
121
122 /* bind to equiv of INADDR_ANY for any AF, if address is specified as '*' */
123 if (strcmp(addr_start, "*") == 0) {
124 hints.ai_flags |= AI_PASSIVE;
125 addr_start = NULL;
126 }
127
128 r = getaddrinfo(addr_start, port_start, &hints, &res);
129 if (r) {
130 NOTIFY_ERROR("%s:%s", "getaddrinfo", gai_strerror(r));
131 free(full_string);
132 return -1;
133 }
134
135 for (res_iter = res; res_iter; res_iter = res_iter->ai_next) {
136 r = ai_cb(res_iter, cb_data);
137 if (r) {
138 NOTIFY_DEBUG("%s:%d", "(ai_cb)", r);
139 continue;
140 }
141
142 if (! (flags & _CB_ITER_ALL)) {
143 break;
144 }
145 }
146 freeaddrinfo(res);
147 free(full_string);
148
149 return 0;
150 }