diff options
-rw-r--r-- | inc/abrtlib.h | 51 | ||||
-rw-r--r-- | lib/Utils/Makefile.am | 3 | ||||
-rw-r--r-- | lib/Utils/skip_whitespace.cpp | 22 | ||||
-rw-r--r-- | lib/Utils/time.cpp | 65 | ||||
-rw-r--r-- | lib/Utils/xconnect.cpp | 416 | ||||
-rw-r--r-- | lib/Utils/xfuncs.cpp | 16 |
6 files changed, 573 insertions, 0 deletions
diff --git a/inc/abrtlib.h b/inc/abrtlib.h index 02906f2f..d98be0b6 100644 --- a/inc/abrtlib.h +++ b/inc/abrtlib.h @@ -25,6 +25,7 @@ #include <sys/time.h> #include <sys/types.h> #include <sys/wait.h> +#include <arpa/inet.h> /* sockaddr_in, sockaddr_in6 etc */ #include <termios.h> #include <time.h> #include <unistd.h> @@ -100,6 +101,9 @@ void* xzalloc(size_t size); char* xstrdup(const char *s); char* xstrndup(const char *s, int n); +char* skip_whitespace(const char *s); +char* skip_non_whitespace(const char *s); + extern ssize_t safe_read(int fd, void *buf, size_t count); // NB: will return short read on error, not -1, // if some data was read before error occurred @@ -131,6 +135,9 @@ void xstat(const char *name, struct stat *stat_buf); int is_regular_file(struct dirent *dent, const char *dirname); void xmove_fd(int from, int to); +int ndelay_on(int fd); +int ndelay_off(int fd); +int close_on_exec_on(int fd); char* xasprintf(const char *format, ...); int xopen(const char *pathname, int flags); @@ -142,6 +149,50 @@ off_t copyfd_eof(int src_fd, int dst_fd); off_t copyfd_size(int src_fd, int dst_fd, off_t size); void copyfd_exact_size(int src_fd, int dst_fd, off_t size); +unsigned long long monotonic_ns(void); +unsigned long long monotonic_us(void); +unsigned monotonic_sec(void); + +/* networking helpers */ +typedef struct len_and_sockaddr { + socklen_t len; + union { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + } u; +} len_and_sockaddr; +enum { + LSA_LEN_SIZE = offsetof(len_and_sockaddr, u), + LSA_SIZEOF_SA = sizeof(struct sockaddr) > sizeof(struct sockaddr_in6) ? + sizeof(struct sockaddr) : sizeof(struct sockaddr_in6), +}; +void setsockopt_reuseaddr(int fd); +int setsockopt_broadcast(int fd); +int setsockopt_bindtodevice(int fd, const char *iface); +len_and_sockaddr* get_sock_lsa(int fd); +void xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen); +unsigned lookup_port(const char *port, const char *protocol, unsigned default_port); +int get_nport(const struct sockaddr *sa); +void set_nport(len_and_sockaddr *lsa, unsigned port); +len_and_sockaddr* host_and_af2sockaddr(const char *host, int port, sa_family_t af); +len_and_sockaddr* xhost_and_af2sockaddr(const char *host, int port, sa_family_t af); +len_and_sockaddr* host2sockaddr(const char *host, int port); +len_and_sockaddr* xhost2sockaddr(const char *host, int port); +len_and_sockaddr* xdotted2sockaddr(const char *host, int port); +int xsocket_type(len_and_sockaddr **lsap, int family, int sock_type); +int xsocket_stream(len_and_sockaddr **lsap); +int create_and_bind_stream_or_die(const char *bindaddr, int port); +int create_and_bind_dgram_or_die(const char *bindaddr, int port); +int create_and_connect_stream_or_die(const char *peer, int port); +int xconnect_stream(const len_and_sockaddr *lsa); +char* xmalloc_sockaddr2host(const struct sockaddr *sa); +char* xmalloc_sockaddr2host_noport(const struct sockaddr *sa); +char* xmalloc_sockaddr2hostonly_noport(const struct sockaddr *sa); +char* xmalloc_sockaddr2dotted(const struct sockaddr *sa); +char* xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa); + + /* C++ style stuff */ std::string ssprintf(const char *format, ...); diff --git a/lib/Utils/Makefile.am b/lib/Utils/Makefile.am index 713fe868..d24afa72 100644 --- a/lib/Utils/Makefile.am +++ b/lib/Utils/Makefile.am @@ -1,7 +1,10 @@ lib_LTLIBRARIES = libABRTUtils.la libABRTUtils_la_SOURCES = \ + time.cpp \ xfuncs.cpp \ + xconnect.cpp \ + skip_whitespace.cpp \ read_write.cpp \ logging.cpp \ copyfd.cpp \ diff --git a/lib/Utils/skip_whitespace.cpp b/lib/Utils/skip_whitespace.cpp new file mode 100644 index 00000000..816928bf --- /dev/null +++ b/lib/Utils/skip_whitespace.cpp @@ -0,0 +1,22 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ +#include "abrtlib.h" + +char* skip_whitespace(const char *s) +{ + /* NB: isspace('\0') returns 0 */ + while (isspace(*s)) ++s; + + return (char *) s; +} + +char* skip_non_whitespace(const char *s) +{ + while (*s && !isspace(*s)) ++s; + + return (char *) s; +} diff --git a/lib/Utils/time.cpp b/lib/Utils/time.cpp new file mode 100644 index 00000000..37ade2cc --- /dev/null +++ b/lib/Utils/time.cpp @@ -0,0 +1,65 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Licensed under GPL version 2, see file LICENSE in this tarball for details. + */ +#include "abrtlib.h" + +#define ENABLE_MONOTONIC_SYSCALL 1 + +#if ENABLE_MONOTONIC_SYSCALL + +#include <sys/syscall.h> +/* Old glibc (< 2.3.4) does not provide this constant. We use syscall + * directly so this definition is safe. */ +#ifndef CLOCK_MONOTONIC +#define CLOCK_MONOTONIC 1 +#endif + +/* libc has incredibly messy way of doing this, + * typically requiring -lrt. We just skip all this mess */ +static void get_mono(struct timespec *ts) +{ + if (syscall(__NR_clock_gettime, CLOCK_MONOTONIC, ts)) + error_msg_and_die("clock_gettime(MONOTONIC) failed"); +} +unsigned long long monotonic_ns(void) +{ + struct timespec ts; + get_mono(&ts); + return ts.tv_sec * 1000000000ULL + ts.tv_nsec; +} +unsigned long long monotonic_us(void) +{ + struct timespec ts; + get_mono(&ts); + return ts.tv_sec * 1000000ULL + ts.tv_nsec/1000; +} +unsigned monotonic_sec(void) +{ + struct timespec ts; + get_mono(&ts); + return ts.tv_sec; +} + +#else + +unsigned long long monotonic_ns(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000000000ULL + tv.tv_usec * 1000; +} +unsigned long long monotonic_us(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000000ULL + tv.tv_usec; +} +unsigned monotonic_sec(void) +{ + return time(NULL); +} + +#endif diff --git a/lib/Utils/xconnect.cpp b/lib/Utils/xconnect.cpp new file mode 100644 index 00000000..746edd63 --- /dev/null +++ b/lib/Utils/xconnect.cpp @@ -0,0 +1,416 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Connect to host at port using address resolution from getaddrinfo + * + * Licensed under GPLv2, see file LICENSE in this tarball for details. + */ + +#include "abrtlib.h" +#include <sys/socket.h> /* netinet/in.h needs it */ +#include <netinet/in.h> +#include <net/if.h> +#include <sys/un.h> +#include <netdb.h> + +#define ENABLE_FEATURE_IPV6 1 +#define ENABLE_FEATURE_PREFER_IPV4_ADDRESS 1 + +static const int const_int_1 = 1; + +void setsockopt_reuseaddr(int fd) +{ + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &const_int_1, sizeof(const_int_1)); +} +int setsockopt_broadcast(int fd) +{ + return setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &const_int_1, sizeof(const_int_1)); +} +int setsockopt_bindtodevice(int fd, const char *iface) +{ + int r; + struct ifreq ifr; + strncpy(ifr.ifr_name, iface, IFNAMSIZ); + /* NB: passing (iface, strlen(iface) + 1) does not work! + * (maybe it works on _some_ kernels, but not on 2.6.26) + * Actually, ifr_name is at offset 0, and in practice + * just giving char[IFNAMSIZ] instead of struct ifreq works too. + * But just in case it's not true on some obscure arch... */ + r = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)); + if (r) + perror_msg("can't bind to interface %s", iface); + return r; +} + +len_and_sockaddr* get_sock_lsa(int fd) +{ + len_and_sockaddr lsa; + len_and_sockaddr *lsa_ptr; + + lsa.len = LSA_SIZEOF_SA; + if (getsockname(fd, &lsa.u.sa, &lsa.len) != 0) + return NULL; + + lsa_ptr = (len_and_sockaddr *)xzalloc(LSA_LEN_SIZE + lsa.len); + if (lsa.len > LSA_SIZEOF_SA) { /* rarely (if ever) happens */ + lsa_ptr->len = lsa.len; + getsockname(fd, &lsa_ptr->u.sa, &lsa_ptr->len); + } else { + memcpy(lsa_ptr, &lsa, LSA_LEN_SIZE + lsa.len); + } + return lsa_ptr; +} + +void xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen) +{ + if (connect(s, s_addr, addrlen) < 0) { + close(s); + if (s_addr->sa_family == AF_INET) + perror_msg_and_die("%s (%s)", + "cannot connect to remote host", + inet_ntoa(((struct sockaddr_in *)s_addr)->sin_addr)); + perror_msg_and_die("cannot connect to remote host"); + } +} + +/* Return port number for a service. + * If "port" is a number use it as the port. + * If "port" is a name it is looked up in /etc/services, + * if it isnt found return default_port + */ +unsigned lookup_port(const char *port, const char *protocol, unsigned default_port) +{ + unsigned port_nr = default_port; + if (port) { + int old_errno; + char *end; + + /* Since this is a lib function, we're not allowed to reset errno to 0. + * Doing so could break an app that is deferring checking of errno. */ + old_errno = errno; + errno = 0; + port_nr = strtoul(port, &end, 10); + if (errno || *end || port_nr > 65535) { + struct servent *tserv = getservbyname(port, protocol); + port_nr = default_port; + if (tserv) + port_nr = ntohs(tserv->s_port); + } + errno = old_errno; + } + return (uint16_t)port_nr; +} + +int get_nport(const struct sockaddr *sa) +{ +#if ENABLE_FEATURE_IPV6 + if (sa->sa_family == AF_INET6) { + return ((struct sockaddr_in6*)sa)->sin6_port; + } +#endif + if (sa->sa_family == AF_INET) { + return ((struct sockaddr_in*)sa)->sin_port; + } + /* What? UNIX socket? IPX?? :) */ + return -1; +} + +void set_nport(len_and_sockaddr *lsa, unsigned port) +{ +#if ENABLE_FEATURE_IPV6 + if (lsa->u.sa.sa_family == AF_INET6) { + lsa->u.sin6.sin6_port = port; + return; + } +#endif + if (lsa->u.sa.sa_family == AF_INET) { + lsa->u.sin.sin_port = port; + return; + } + /* What? UNIX socket? IPX?? :) */ +} + +/* We hijack this constant to mean something else */ +/* It doesn't hurt because we will remove this bit anyway */ +#define DIE_ON_ERROR AI_CANONNAME + +/* host: "1.2.3.4[:port]", "www.google.com[:port]" + * port: if neither of above specifies port # */ +static len_and_sockaddr* str2sockaddr( + const char *host, int port, + sa_family_t af, + int ai_flags) +{ + int rc; + len_and_sockaddr *r; + struct addrinfo *result = NULL; + struct addrinfo *used_res; + const char *org_host = host; /* only for error msg */ + const char *cp; + struct addrinfo hint; + + r = NULL; + + /* Ugly parsing of host:addr */ + if (ENABLE_FEATURE_IPV6 && host[0] == '[') { + /* Even uglier parsing of [xx]:nn */ + host++; + cp = strchr(host, ']'); + if (!cp || (cp[1] != ':' && cp[1] != '\0')) { + /* Malformed: must be [xx]:nn or [xx] */ + error_msg("bad address '%s'", org_host); + if (ai_flags & DIE_ON_ERROR) + xfunc_die(); + return NULL; + } + } else { + cp = strrchr(host, ':'); + if (ENABLE_FEATURE_IPV6 && cp && strchr(host, ':') != cp) { + /* There is more than one ':' (e.g. "::1") */ + cp = NULL; /* it's not a port spec */ + } + } + if (cp) { /* points to ":" or "]:" */ + int sz = cp - host + 1; + char *hbuf = (char*)alloca(sz); + hbuf[--sz] = '\0'; + host = strncpy(hbuf, host, sz); + if (ENABLE_FEATURE_IPV6 && *cp != ':') { + cp++; /* skip ']' */ + if (*cp == '\0') /* [xx] without port */ + goto skip; + } + cp++; /* skip ':' */ + char *end; + errno = 0; + port = strtoul(cp, &end, 10); + if (errno || *end || (unsigned)port > 0xffff) { + error_msg("bad port spec '%s'", org_host); + if (ai_flags & DIE_ON_ERROR) + xfunc_die(); + return NULL; + } + skip: ; + } + + memset(&hint, 0 , sizeof(hint)); +#if !ENABLE_FEATURE_IPV6 + hint.ai_family = AF_INET; /* do not try to find IPv6 */ +#else + hint.ai_family = af; +#endif + /* Needed. Or else we will get each address thrice (or more) + * for each possible socket type (tcp,udp,raw...): */ + hint.ai_socktype = SOCK_STREAM; + hint.ai_flags = ai_flags & ~DIE_ON_ERROR; + rc = getaddrinfo(host, NULL, &hint, &result); + if (rc || !result) { + error_msg("bad address '%s'", org_host); + if (ai_flags & DIE_ON_ERROR) + xfunc_die(); + goto ret; + } + used_res = result; +#if ENABLE_FEATURE_PREFER_IPV4_ADDRESS + while (1) { + if (used_res->ai_family == AF_INET) + break; + used_res = used_res->ai_next; + if (!used_res) { + used_res = result; + break; + } + } +#endif + r = (len_and_sockaddr *)xmalloc(offsetof(len_and_sockaddr, u.sa) + used_res->ai_addrlen); + r->len = used_res->ai_addrlen; + memcpy(&r->u.sa, used_res->ai_addr, used_res->ai_addrlen); + set_nport(r, htons(port)); + ret: + freeaddrinfo(result); + return r; +} +#if !ENABLE_FEATURE_IPV6 +#define str2sockaddr(host, port, af, ai_flags) str2sockaddr(host, port, ai_flags) +#endif + +#if ENABLE_FEATURE_IPV6 +len_and_sockaddr* host_and_af2sockaddr(const char *host, int port, sa_family_t af) +{ + return str2sockaddr(host, port, af, 0); +} + +len_and_sockaddr* xhost_and_af2sockaddr(const char *host, int port, sa_family_t af) +{ + return str2sockaddr(host, port, af, DIE_ON_ERROR); +} +#endif + +len_and_sockaddr* host2sockaddr(const char *host, int port) +{ + return str2sockaddr(host, port, AF_UNSPEC, 0); +} + +len_and_sockaddr* xhost2sockaddr(const char *host, int port) +{ + return str2sockaddr(host, port, AF_UNSPEC, DIE_ON_ERROR); +} + +len_and_sockaddr* xdotted2sockaddr(const char *host, int port) +{ + return str2sockaddr(host, port, AF_UNSPEC, AI_NUMERICHOST | DIE_ON_ERROR); +} + +#undef xsocket_type +int xsocket_type(len_and_sockaddr **lsap, int family, int sock_type) +{ + len_and_sockaddr *lsa; + int fd; + int len; + +#if ENABLE_FEATURE_IPV6 + if (family == AF_UNSPEC) { + fd = socket(AF_INET6, sock_type, 0); + if (fd >= 0) { + family = AF_INET6; + goto done; + } + family = AF_INET; + } +#endif + fd = xsocket(family, sock_type, 0); + len = sizeof(struct sockaddr_in); +#if ENABLE_FEATURE_IPV6 + if (family == AF_INET6) { + done: + len = sizeof(struct sockaddr_in6); + } +#endif + lsa = (len_and_sockaddr *)xzalloc(offsetof(len_and_sockaddr, u.sa) + len); + lsa->len = len; + lsa->u.sa.sa_family = family; + *lsap = lsa; + return fd; +} + +int xsocket_stream(len_and_sockaddr **lsap) +{ + return xsocket_type(lsap, AF_UNSPEC, SOCK_STREAM); +} + +static int create_and_bind_or_die(const char *bindaddr, int port, int sock_type) +{ + int fd; + len_and_sockaddr *lsa; + + if (bindaddr && bindaddr[0]) { + lsa = xdotted2sockaddr(bindaddr, port); + /* user specified bind addr dictates family */ + fd = xsocket(lsa->u.sa.sa_family, sock_type, 0); + } else { + fd = xsocket_type(&lsa, AF_UNSPEC, sock_type); + set_nport(lsa, htons(port)); + } + setsockopt_reuseaddr(fd); + xbind(fd, &lsa->u.sa, lsa->len); + free(lsa); + return fd; +} + +int create_and_bind_stream_or_die(const char *bindaddr, int port) +{ + return create_and_bind_or_die(bindaddr, port, SOCK_STREAM); +} + +int create_and_bind_dgram_or_die(const char *bindaddr, int port) +{ + return create_and_bind_or_die(bindaddr, port, SOCK_DGRAM); +} + + +int create_and_connect_stream_or_die(const char *peer, int port) +{ + int fd; + len_and_sockaddr *lsa; + + lsa = xhost2sockaddr(peer, port); + fd = xsocket(lsa->u.sa.sa_family, SOCK_STREAM, 0); + setsockopt_reuseaddr(fd); + xconnect(fd, &lsa->u.sa, lsa->len); + free(lsa); + return fd; +} + +int xconnect_stream(const len_and_sockaddr *lsa) +{ + int fd = xsocket(lsa->u.sa.sa_family, SOCK_STREAM, 0); + xconnect(fd, &lsa->u.sa, lsa->len); + return fd; +} + +/* We hijack this constant to mean something else */ +/* It doesn't hurt because we will add this bit anyway */ +#define IGNORE_PORT NI_NUMERICSERV +static char* sockaddr2str(const struct sockaddr *sa, int flags) +{ + char host[128]; + char serv[16]; + int rc; + socklen_t salen; + + salen = LSA_SIZEOF_SA; +#if ENABLE_FEATURE_IPV6 + if (sa->sa_family == AF_INET) + salen = sizeof(struct sockaddr_in); + if (sa->sa_family == AF_INET6) + salen = sizeof(struct sockaddr_in6); +#endif + rc = getnameinfo(sa, salen, + host, sizeof(host), + /* can do ((flags & IGNORE_PORT) ? NULL : serv) but why bother? */ + serv, sizeof(serv), + /* do not resolve port# into service _name_ */ + flags | NI_NUMERICSERV + ); + if (rc) + return NULL; + if (flags & IGNORE_PORT) + return xstrdup(host); +#if ENABLE_FEATURE_IPV6 + if (sa->sa_family == AF_INET6) { + if (strchr(host, ':')) /* heh, it's not a resolved hostname */ + return xasprintf("[%s]:%s", host, serv); + /*return xasprintf("%s:%s", host, serv);*/ + /* - fall through instead */ + } +#endif + /* For now we don't support anything else, so it has to be INET */ + /*if (sa->sa_family == AF_INET)*/ + return xasprintf("%s:%s", host, serv); + /*return xstrdup(host);*/ +} + +char* xmalloc_sockaddr2host(const struct sockaddr *sa) +{ + return sockaddr2str(sa, 0); +} + +char* xmalloc_sockaddr2host_noport(const struct sockaddr *sa) +{ + return sockaddr2str(sa, IGNORE_PORT); +} + +char* xmalloc_sockaddr2hostonly_noport(const struct sockaddr *sa) +{ + return sockaddr2str(sa, NI_NAMEREQD | IGNORE_PORT); +} +char* xmalloc_sockaddr2dotted(const struct sockaddr *sa) +{ + return sockaddr2str(sa, NI_NUMERICHOST); +} + +char* xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa) +{ + return sockaddr2str(sa, NI_NUMERICHOST | IGNORE_PORT); +} diff --git a/lib/Utils/xfuncs.cpp b/lib/Utils/xfuncs.cpp index d256c195..9c8b0f12 100644 --- a/lib/Utils/xfuncs.cpp +++ b/lib/Utils/xfuncs.cpp @@ -5,6 +5,22 @@ */ #include "abrtlib.h" +/* Turn on nonblocking I/O on a fd */ +int ndelay_on(int fd) +{ + return fcntl(fd, F_SETFL, fcntl(fd,F_GETFL) | O_NONBLOCK); +} + +int ndelay_off(int fd) +{ + return fcntl(fd, F_SETFL, fcntl(fd,F_GETFL) & ~O_NONBLOCK); +} + +int close_on_exec_on(int fd) +{ + return fcntl(fd, F_SETFD, FD_CLOEXEC); +} + // Die if we can't allocate size bytes of memory. void* xmalloc(size_t size) { |