+
+/* 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;
+ }
+}
+
+void
+xpipe(int fds[2])
+{
+ if (pipe(fds)) {
+ VLOG_FATAL("failed to create pipe (%s)", strerror(errno));
+ }
+}
+
+void
+xpipe_nonblocking(int fds[2])
+{
+ xpipe(fds);
+ xset_nonblocking(fds[0]);
+ xset_nonblocking(fds[1]);
+}
+
+void
+xsocketpair(int domain, int type, int protocol, int fds[2])
+{
+ if (socketpair(domain, type, protocol, fds)) {
+ VLOG_FATAL("failed to create socketpair (%s)", strerror(errno));
+ }
+}
+
+static int
+getsockopt_int(int fd, int level, int option, const char *optname, int *valuep)
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 10);
+ socklen_t len;
+ int value;
+ int error;
+
+ len = sizeof value;
+ if (getsockopt(fd, level, option, &value, &len)) {
+ error = errno;
+ VLOG_ERR_RL(&rl, "getsockopt(%s): %s", optname, strerror(error));
+ } else if (len != sizeof value) {
+ error = EINVAL;
+ VLOG_ERR_RL(&rl, "getsockopt(%s): value is %u bytes (expected %zu)",
+ optname, (unsigned int) len, sizeof value);
+ } else {
+ error = 0;
+ }
+
+ *valuep = error ? 0 : value;
+ return error;
+}
+
+static void
+describe_sockaddr(struct ds *string, int fd,
+ int (*getaddr)(int, struct sockaddr *, socklen_t *))
+{
+ struct sockaddr_storage ss;
+ socklen_t len = sizeof ss;
+
+ if (!getaddr(fd, (struct sockaddr *) &ss, &len)) {
+ if (ss.ss_family == AF_INET) {
+ struct sockaddr_in sin;
+
+ memcpy(&sin, &ss, sizeof sin);
+ ds_put_format(string, IP_FMT":%"PRIu16,
+ IP_ARGS(sin.sin_addr.s_addr), ntohs(sin.sin_port));
+ } else if (ss.ss_family == AF_UNIX) {
+ struct sockaddr_un sun;
+ const char *null;
+ size_t maxlen;
+
+ memcpy(&sun, &ss, sizeof sun);
+ maxlen = len - offsetof(struct sockaddr_un, sun_path);
+ null = memchr(sun.sun_path, '\0', maxlen);
+ ds_put_buffer(string, sun.sun_path,
+ null ? null - sun.sun_path : maxlen);
+ }
+#ifdef HAVE_NETLINK
+ else if (ss.ss_family == AF_NETLINK) {
+ int protocol;
+
+/* SO_PROTOCOL was introduced in 2.6.32. Support it regardless of the version
+ * of the Linux kernel headers in use at build time. */
+#ifndef SO_PROTOCOL
+#define SO_PROTOCOL 38
+#endif
+
+ if (!getsockopt_int(fd, SOL_SOCKET, SO_PROTOCOL, "SO_PROTOCOL",
+ &protocol)) {
+ switch (protocol) {
+ case NETLINK_ROUTE:
+ ds_put_cstr(string, "NETLINK_ROUTE");
+ break;
+
+ case NETLINK_GENERIC:
+ ds_put_cstr(string, "NETLINK_GENERIC");
+ break;
+
+ default:
+ ds_put_format(string, "AF_NETLINK family %d", protocol);
+ break;
+ }
+ } else {
+ ds_put_cstr(string, "AF_NETLINK");
+ }
+ }
+#endif
+#if AF_PACKET && LINUX_DATAPATH
+ else if (ss.ss_family == AF_PACKET) {
+ struct sockaddr_ll sll;
+
+ memcpy(&sll, &ss, sizeof sll);
+ ds_put_cstr(string, "AF_PACKET");
+ if (sll.sll_ifindex) {
+ char name[IFNAMSIZ];
+
+ if (if_indextoname(sll.sll_ifindex, name)) {
+ ds_put_format(string, "(%s)", name);
+ } else {
+ ds_put_format(string, "(ifindex=%d)", sll.sll_ifindex);
+ }
+ }
+ if (sll.sll_protocol) {
+ ds_put_format(string, "(protocol=0x%"PRIu16")",
+ ntohs(sll.sll_protocol));
+ }
+ }
+#endif
+ else if (ss.ss_family == AF_UNSPEC) {
+ ds_put_cstr(string, "AF_UNSPEC");
+ } else {
+ ds_put_format(string, "AF_%d", (int) ss.ss_family);
+ }
+ }
+}
+
+
+#ifdef LINUX_DATAPATH
+static void
+put_fd_filename(struct ds *string, int fd)
+{
+ char buf[1024];
+ char *linkname;
+ int n;
+
+ linkname = xasprintf("/proc/self/fd/%d", fd);
+ n = readlink(linkname, buf, sizeof buf);
+ if (n > 0) {
+ ds_put_char(string, ' ');
+ ds_put_buffer(string, buf, n);
+ if (n > sizeof buf) {
+ ds_put_cstr(string, "...");
+ }
+ }
+ free(linkname);
+}
+#endif
+
+/* Returns a malloc()'d string describing 'fd', for use in logging. */
+char *
+describe_fd(int fd)
+{
+ struct ds string;
+ struct stat s;
+
+ ds_init(&string);
+ if (fstat(fd, &s)) {
+ ds_put_format(&string, "fstat failed (%s)", strerror(errno));
+ } else if (S_ISSOCK(s.st_mode)) {
+ describe_sockaddr(&string, fd, getsockname);
+ ds_put_cstr(&string, "<->");
+ describe_sockaddr(&string, fd, getpeername);
+ } else {
+ ds_put_cstr(&string, (isatty(fd) ? "tty"
+ : S_ISDIR(s.st_mode) ? "directory"
+ : S_ISCHR(s.st_mode) ? "character device"
+ : S_ISBLK(s.st_mode) ? "block device"
+ : S_ISREG(s.st_mode) ? "file"
+ : S_ISFIFO(s.st_mode) ? "FIFO"
+ : S_ISLNK(s.st_mode) ? "symbolic link"
+ : "unknown"));
+#ifdef LINUX_DATAPATH
+ put_fd_filename(&string, fd);
+#endif
+ }
+ return ds_steal_cstr(&string);
+}
+
+/* Returns the total of the 'iov_len' members of the 'n_iovs' in 'iovs'.
+ * The caller must ensure that the total does not exceed SIZE_MAX. */
+size_t
+iovec_len(const struct iovec iovs[], size_t n_iovs)
+{
+ size_t len = 0;
+ size_t i;
+
+ for (i = 0; i < n_iovs; i++) {
+ len += iovs[i].iov_len;
+ }
+ return len;
+}
+
+/* Returns true if all of the 'n_iovs' iovecs in 'iovs' have length zero. */
+bool
+iovec_is_empty(const struct iovec iovs[], size_t n_iovs)
+{
+ size_t i;
+
+ for (i = 0; i < n_iovs; i++) {
+ if (iovs[i].iov_len) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/* Sends the 'n_iovs' iovecs of data in 'iovs' and the 'n_fds' file descriptors
+ * in 'fds' on Unix domain socket 'sock'. Returns the number of bytes
+ * successfully sent or -1 if an error occurred. On error, sets errno
+ * appropriately. */
+int
+send_iovec_and_fds(int sock,
+ const struct iovec *iovs, size_t n_iovs,
+ const int fds[], size_t n_fds)
+{
+ assert(sock >= 0);
+ if (n_fds > 0) {
+ union {
+ struct cmsghdr cm;
+ char control[CMSG_SPACE(SOUTIL_MAX_FDS * sizeof *fds)];
+ } cmsg;
+ struct msghdr msg;
+
+ assert(!iovec_is_empty(iovs, n_iovs));
+ assert(n_fds <= SOUTIL_MAX_FDS);
+
+ memset(&cmsg, 0, sizeof cmsg);
+ cmsg.cm.cmsg_len = CMSG_LEN(n_fds * sizeof *fds);
+ cmsg.cm.cmsg_level = SOL_SOCKET;
+ cmsg.cm.cmsg_type = SCM_RIGHTS;
+ memcpy(CMSG_DATA(&cmsg.cm), fds, n_fds * sizeof *fds);
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = CONST_CAST(struct iovec *, iovs);
+ msg.msg_iovlen = n_iovs;
+ msg.msg_control = &cmsg.cm;
+ msg.msg_controllen = CMSG_SPACE(n_fds * sizeof *fds);
+ msg.msg_flags = 0;
+
+ return sendmsg(sock, &msg, 0);
+ } else {
+ return writev(sock, iovs, n_iovs);
+ }
+}
+
+/* Sends the 'n_iovs' iovecs of data in 'iovs' and the 'n_fds' file descriptors
+ * in 'fds' on Unix domain socket 'sock'. If 'skip_bytes' is nonzero, then the
+ * first 'skip_bytes' of data in the iovecs are not sent, and none of the file
+ * descriptors are sent. The function continues to retry sending until an
+ * error (other than EINTR) occurs or all the data and fds are sent.
+ *
+ * Returns 0 if all the data and fds were successfully sent, otherwise a
+ * positive errno value. Regardless of success, stores the number of bytes
+ * sent (always at least 'skip_bytes') in '*bytes_sent'. (If at least one byte
+ * is sent, then all the fds have been sent.)
+ *
+ * 'skip_bytes' must be less than or equal to iovec_len(iovs, n_iovs). */
+int
+send_iovec_and_fds_fully(int sock,
+ const struct iovec iovs[], size_t n_iovs,
+ const int fds[], size_t n_fds,
+ size_t skip_bytes, size_t *bytes_sent)
+{
+ *bytes_sent = 0;
+ while (n_iovs > 0) {
+ int retval;
+
+ if (skip_bytes) {
+ retval = skip_bytes;
+ skip_bytes = 0;
+ } else if (!*bytes_sent) {
+ retval = send_iovec_and_fds(sock, iovs, n_iovs, fds, n_fds);
+ } else {
+ retval = writev(sock, iovs, n_iovs);
+ }
+
+ if (retval > 0) {
+ *bytes_sent += retval;
+ while (retval > 0) {
+ const uint8_t *base = iovs->iov_base;
+ size_t len = iovs->iov_len;
+
+ if (retval < len) {
+ size_t sent;
+ int error;
+
+ error = write_fully(sock, base + retval, len - retval,
+ &sent);
+ *bytes_sent += sent;
+ retval += sent;
+ if (error) {
+ return error;
+ }
+ }
+ retval -= len;
+ iovs++;
+ n_iovs--;
+ }
+ } else if (retval == 0) {
+ if (iovec_is_empty(iovs, n_iovs)) {
+ break;
+ }
+ VLOG_WARN("send returned 0");
+ return EPROTO;
+ } else if (errno != EINTR) {
+ return errno;
+ }
+ }
+
+ return 0;
+}
+
+/* Sends the 'n_iovs' iovecs of data in 'iovs' and the 'n_fds' file descriptors
+ * in 'fds' on Unix domain socket 'sock'. The function continues to retry
+ * sending until an error (other than EAGAIN or EINTR) occurs or all the data
+ * and fds are sent. Upon EAGAIN, the function blocks until the socket is
+ * ready for more data.
+ *
+ * Returns 0 if all the data and fds were successfully sent, otherwise a
+ * positive errno value. */
+int
+send_iovec_and_fds_fully_block(int sock,
+ const struct iovec iovs[], size_t n_iovs,
+ const int fds[], size_t n_fds)
+{
+ size_t sent = 0;
+
+ for (;;) {
+ int error;
+
+ error = send_iovec_and_fds_fully(sock, iovs, n_iovs,
+ fds, n_fds, sent, &sent);
+ if (error != EAGAIN) {
+ return error;
+ }
+ poll_fd_wait(sock, POLLOUT);
+ poll_block();
+ }
+}
+
+/* Attempts to receive from Unix domain socket 'sock' up to 'size' bytes of
+ * data into 'data' and up to SOUTIL_MAX_FDS file descriptors into 'fds'.
+ *
+ * - Upon success, returns the number of bytes of data copied into 'data'
+ * and stores the number of received file descriptors into '*n_fdsp'.
+ *
+ * - On failure, returns a negative errno value and stores 0 in
+ * '*n_fdsp'.
+ *
+ * - On EOF, returns 0 and stores 0 in '*n_fdsp'. */
+int
+recv_data_and_fds(int sock,
+ void *data, size_t size,
+ int fds[SOUTIL_MAX_FDS], size_t *n_fdsp)
+{
+ union {
+ struct cmsghdr cm;
+ char control[CMSG_SPACE(SOUTIL_MAX_FDS * sizeof *fds)];
+ } cmsg;
+ struct msghdr msg;
+ int retval;
+ struct cmsghdr *p;
+ size_t i;
+
+ *n_fdsp = 0;
+
+ do {
+ struct iovec iov;
+
+ iov.iov_base = data;
+ iov.iov_len = size;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = &cmsg.cm;
+ msg.msg_controllen = sizeof cmsg.control;
+ msg.msg_flags = 0;
+
+ retval = recvmsg(sock, &msg, 0);
+ } while (retval < 0 && errno == EINTR);
+ if (retval <= 0) {
+ return retval < 0 ? -errno : 0;
+ }
+
+ for (p = CMSG_FIRSTHDR(&msg); p; p = CMSG_NXTHDR(&msg, p)) {
+ if (p->cmsg_level != SOL_SOCKET || p->cmsg_type != SCM_RIGHTS) {
+ VLOG_ERR("unexpected control message %d:%d",
+ p->cmsg_level, p->cmsg_type);
+ goto error;
+ } else if (*n_fdsp) {
+ VLOG_ERR("multiple SCM_RIGHTS received");
+ goto error;
+ } else {
+ size_t n_fds = (p->cmsg_len - CMSG_LEN(0)) / sizeof *fds;
+ const int *fds_data = (const int *) CMSG_DATA(p);
+
+ assert(n_fds > 0);
+ if (n_fds > SOUTIL_MAX_FDS) {
+ VLOG_ERR("%zu fds received but only %d supported",
+ n_fds, SOUTIL_MAX_FDS);
+ for (i = 0; i < n_fds; i++) {
+ close(fds_data[i]);
+ }
+ goto error;
+ }
+
+ *n_fdsp = n_fds;
+ memcpy(fds, fds_data, n_fds * sizeof *fds);
+ }
+ }
+
+ return retval;
+
+error:
+ for (i = 0; i < *n_fdsp; i++) {
+ close(fds[i]);
+ }
+ *n_fdsp = 0;
+ return EPROTO;
+}