X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fsocket-util.c;h=961e00b4a19b23a4a77e09ca335db11deaccebfc;hb=refs%2Fheads%2Flts-1.0a;hp=3fcd5a1e8e79f73210c0d2ec70feeb33b370a1dd;hpb=4d678233e981fa319a338f6b0949e9dc625941a4;p=sliver-openvswitch.git diff --git a/lib/socket-util.c b/lib/socket-util.c index 3fcd5a1e8..961e00b4a 100644 --- a/lib/socket-util.c +++ b/lib/socket-util.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009 Nicira Networks. + * Copyright (c) 2008, 2009, 2010 Nicira Networks. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,13 +26,15 @@ #include #include #include +#include +#include #include #include #include "fatal-signal.h" #include "util.h" - #include "vlog.h" -#define THIS_MODULE VLM_socket_util + +VLOG_DEFINE_THIS_MODULE(socket_util) /* Sets 'fd' to non-blocking mode. Returns 0 if successful, otherwise a * positive errno value. */ @@ -53,6 +55,28 @@ set_nonblocking(int fd) } } +static bool +rlim_is_finite(rlim_t limit) +{ + if (limit == RLIM_INFINITY) { + return false; + } + +#ifdef RLIM_SAVED_CUR /* FreeBSD 8.0 lacks RLIM_SAVED_CUR. */ + if (limit == RLIM_SAVED_CUR) { + return false; + } +#endif + +#ifdef RLIM_SAVED_MAX /* FreeBSD 8.0 lacks RLIM_SAVED_MAX. */ + if (limit == RLIM_SAVED_MAX) { + return false; + } +#endif + + return true; +} + /* Returns the maximum valid FD value, plus 1. */ int get_max_fds(void) @@ -60,10 +84,7 @@ get_max_fds(void) static int max_fds = -1; if (max_fds < 0) { struct rlimit r; - if (!getrlimit(RLIMIT_NOFILE, &r) - && r.rlim_cur != RLIM_INFINITY - && r.rlim_cur != RLIM_SAVED_MAX - && r.rlim_cur != RLIM_SAVED_CUR) { + if (!getrlimit(RLIMIT_NOFILE, &r) && rlim_is_finite(r.rlim_cur)) { max_fds = r.rlim_cur; } else { VLOG_WARN("failed to obtain fd limit, defaulting to 1024"); @@ -77,7 +98,7 @@ get_max_fds(void) * address, into a numeric IP address in '*addr'. Returns 0 if successful, * otherwise a positive errno value. */ int -lookup_ip(const char *host_name, struct in_addr *addr) +lookup_ip(const char *host_name, struct in_addr *addr) { if (!inet_aton(host_name, addr)) { struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); @@ -90,7 +111,7 @@ lookup_ip(const char *host_name, struct in_addr *addr) /* Returns the error condition associated with socket 'fd' and resets the * socket's error status. */ int -get_socket_error(int fd) +get_socket_error(int fd) { int error; socklen_t len = sizeof(error); @@ -103,7 +124,7 @@ get_socket_error(int fd) } int -check_connection_completion(int fd) +check_connection_completion(int fd) { struct pollfd pfd; int retval; @@ -204,7 +225,7 @@ make_sockaddr_un(const char *name, struct sockaddr_un* un, socklen_t *un_len) * * Returns the socket's fd if successful, otherwise a negative errno value. */ int -make_unix_socket(int style, bool nonblock, bool passcred UNUSED, +make_unix_socket(int style, bool nonblock, bool passcred OVS_UNUSED, const char *bind_path, const char *connect_path) { int error; @@ -265,10 +286,10 @@ make_unix_socket(int style, bool nonblock, bool passcred UNUSED, return fd; error: + error = errno == EAGAIN ? EPROTO : errno; if (bind_path) { fatal_signal_remove_file_to_unlink(bind_path); } - error = errno; close(fd); return -error; } @@ -291,56 +312,87 @@ guess_netmask(uint32_t ip) : htonl(0)); /* ??? */ } -/* Opens a non-blocking TCP socket and connects to 'target', which should be a - * string in the format "[:]", where is required and - * is optional, with 'default_port' assumed if is omitted. - * - * On success, returns 0 (indicating connection complete) or EAGAIN (indicating - * connection in progress), in which case the new file descriptor is stored - * into '*fdp'. On failure, returns a positive errno value other than EAGAIN - * and stores -1 into '*fdp'. +/* Parses 'target', which should be a string in the format "[:]". + * is required. If 'default_port' is nonzero then is optional + * and defaults to 'default_port'. * - * If 'sinp' is non-null, then on success the target address is stored into - * '*sinp'. */ -int -tcp_open_active(const char *target_, uint16_t default_port, - struct sockaddr_in *sinp, int *fdp) + * On success, returns true and stores the parsed remote address into '*sinp'. + * On failure, logs an error, stores zeros into '*sinp', and returns false. */ +bool +inet_parse_active(const char *target_, uint16_t default_port, + struct sockaddr_in *sinp) { char *target = xstrdup(target_); char *save_ptr = NULL; const char *host_name; const char *port_string; - struct sockaddr_in sin; - int fd = -1; - int error; + bool ok = false; /* Defaults. */ - memset(&sin, 0, sizeof sin); - sin.sin_family = AF_INET; - sin.sin_port = htons(default_port); + sinp->sin_family = AF_INET; + sinp->sin_port = htons(default_port); /* Tokenize. */ host_name = strtok_r(target, ":", &save_ptr); port_string = strtok_r(NULL, ":", &save_ptr); if (!host_name) { - ovs_error(0, "%s: bad peer name format", target_); - error = EAFNOSUPPORT; + VLOG_ERR("%s: bad peer name format", target_); goto exit; } /* Look up IP, port. */ - error = lookup_ip(host_name, &sin.sin_addr); - if (error) { + if (lookup_ip(host_name, &sinp->sin_addr)) { goto exit; } if (port_string && atoi(port_string)) { - sin.sin_port = htons(atoi(port_string)); + sinp->sin_port = htons(atoi(port_string)); + } else if (!default_port) { + VLOG_ERR("%s: port number must be specified", target_); + goto exit; + } + + ok = true; + +exit: + if (!ok) { + memset(sinp, 0, sizeof *sinp); + } + free(target); + return ok; +} + +/* Opens a non-blocking IPv4 socket of the specified 'style' and connects to + * 'target', which should be a string in the format "[:]". + * is required. If 'default_port' is nonzero then is optional and + * defaults to 'default_port'. + * + * 'style' should be SOCK_STREAM (for TCP) or SOCK_DGRAM (for UDP). + * + * On success, returns 0 (indicating connection complete) or EAGAIN (indicating + * connection in progress), in which case the new file descriptor is stored + * into '*fdp'. On failure, returns a positive errno value other than EAGAIN + * and stores -1 into '*fdp'. + * + * If 'sinp' is non-null, then on success the target address is stored into + * '*sinp'. */ +int +inet_open_active(int style, const char *target, uint16_t default_port, + struct sockaddr_in *sinp, int *fdp) +{ + struct sockaddr_in sin; + int fd = -1; + int error; + + /* Parse. */ + if (!inet_parse_active(target, default_port, &sin)) { + error = EAFNOSUPPORT; + goto exit; } /* Create non-blocking socket. */ - fd = socket(AF_INET, SOCK_STREAM, 0); + fd = socket(AF_INET, style, 0); if (fd < 0) { - VLOG_ERR("%s: socket: %s", target_, strerror(errno)); + VLOG_ERR("%s: socket: %s", target, strerror(errno)); error = errno; goto exit; } @@ -371,29 +423,40 @@ exit: } else { *fdp = -1; } - free(target); return error; } -/* Opens a non-blocking TCP socket, binds to 'target', and listens for incoming - * connections. 'target' should be a string in the format "[][:]", - * where both and are optional. If is omitted, it defaults - * to 'default_port'; if is omitted it defaults to the wildcard IP - * address. +/* Opens a non-blocking IPv4 socket of the specified 'style', binds to + * 'target', and listens for incoming connections. 'target' should be a string + * in the format "[][:]": + * + * - If 'default_port' is -1, then is required. Otherwise, if + * is omitted, then 'default_port' is used instead. + * + * - If (or 'default_port', if used) is 0, then no port is bound + * and the TCP/IP stack will select a port. + * + * - If is omitted then the IP address is wildcarded. + * + * 'style' should be SOCK_STREAM (for TCP) or SOCK_DGRAM (for UDP). * - * The socket will have SO_REUSEADDR turned on. + * For TCP, the socket will have SO_REUSEADDR turned on. * * On success, returns a non-negative file descriptor. On failure, returns a - * negative errno value. */ + * negative errno value. + * + * If 'sinp' is non-null, then on success the bound address is stored into + * '*sinp'. */ int -tcp_open_passive(const char *target_, uint16_t default_port) +inet_open_passive(int style, const char *target_, int default_port, + struct sockaddr_in *sinp) { char *target = xstrdup(target_); char *string_ptr = target; struct sockaddr_in sin; const char *host_name; const char *port_string; - int fd, error; + int fd = 0, error, port; unsigned int yes = 1; /* Address defaults. */ @@ -404,8 +467,12 @@ tcp_open_passive(const char *target_, uint16_t default_port) /* Parse optional port number. */ port_string = strsep(&string_ptr, ":"); - if (port_string && atoi(port_string)) { - sin.sin_port = htons(atoi(port_string)); + if (port_string && str_to_int(port_string, 10, &port)) { + sin.sin_port = htons(port); + } else if (default_port < 0) { + VLOG_ERR("%s: port number must be specified", target_); + error = EAFNOSUPPORT; + goto exit; } /* Parse optional bind IP. */ @@ -418,7 +485,7 @@ tcp_open_passive(const char *target_, uint16_t default_port) } /* Create non-blocking socket, set SO_REUSEADDR. */ - fd = socket(AF_INET, SOCK_STREAM, 0); + fd = socket(AF_INET, style, 0); if (fd < 0) { error = errno; VLOG_ERR("%s: socket: %s", target_, strerror(error)); @@ -428,7 +495,8 @@ tcp_open_passive(const char *target_, uint16_t default_port) if (error) { goto exit_close; } - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) < 0) { + if (style == SOCK_STREAM + && setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) < 0) { error = errno; VLOG_ERR("%s: setsockopt(SO_REUSEADDR): %s", target_, strerror(error)); goto exit_close; @@ -447,6 +515,21 @@ tcp_open_passive(const char *target_, uint16_t default_port) VLOG_ERR("%s: listen: %s", target_, strerror(error)); goto exit_close; } + + if (sinp) { + socklen_t sin_len = sizeof sin; + if (getsockname(fd, (struct sockaddr *) &sin, &sin_len) < 0){ + error = errno; + VLOG_ERR("%s: getsockname: %s", target_, strerror(error)); + goto exit_close; + } + if (sin.sin_family != AF_INET || sin_len != sizeof sin) { + VLOG_ERR("%s: getsockname: invalid socket name", target_); + goto exit_close; + } + *sinp = sin; + } + error = 0; goto exit; @@ -517,3 +600,62 @@ write_fully(int fd, const void *p_, size_t size, size_t *bytes_written) } return 0; } + +/* Given file name 'file_name', fsyncs the directory in which it is contained. + * Returns 0 if successful, otherwise a positive errno value. */ +int +fsync_parent_dir(const char *file_name) +{ + int error = 0; + char *dir; + int fd; + + dir = dir_name(file_name); + fd = open(dir, O_RDONLY); + if (fd >= 0) { + if (fsync(fd)) { + if (errno == EINVAL || errno == EROFS) { + /* This directory does not support synchronization. Not + * really an error. */ + } else { + error = errno; + VLOG_ERR("%s: fsync failed (%s)", dir, strerror(error)); + } + } + close(fd); + } else { + error = errno; + VLOG_ERR("%s: open failed (%s)", dir, strerror(error)); + } + free(dir); + + return error; +} + +/* Obtains the modification time of the file named 'file_name' to the greatest + * supported precision. If successful, stores the mtime in '*mtime' and + * returns 0. On error, returns a positive errno value and stores zeros in + * '*mtime'. */ +int +get_mtime(const char *file_name, struct timespec *mtime) +{ + struct stat s; + + if (!stat(file_name, &s)) { + mtime->tv_sec = s.st_mtime; + +#if HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC + mtime->tv_nsec = s.st_mtim.tv_nsec; +#elif HAVE_STRUCT_STAT_ST_MTIMENSEC + mtime->tv_nsec = s.st_mtimensec; +#else + mtime->tv_nsec = 0; +#endif + + return 0; + } else { + mtime->tv_sec = mtime->tv_nsec = 0; + return errno; + } +} +