/* * Try to setup dnotify on the state directory. This returns the * descriptor of a pipe in @p dummy_fd. Every time the state changes, * a single byte is written to this pipe. A caller who select()s on * the pipe will therefore be woken every time there is a change. * * If we can do dnotify, create the dummy pipe and turn it on. * * @fixme One problem here is that if the state directory is deleted * and recreated, then we'll never notice and find the new one. I * don't know of any good fix, other than perhaps polling every so * often. So just don't do that. * * @fixme If this function is called repeatedly it will leak FDs. * * @todo Reimplement this on top of kevent for BSD. */ int dcc_mon_setup_notify (int *dummy_fd) { #ifdef F_NOTIFY char *state_dir; int ret; int fd; if (signal (SIGIO, dcc_mon_siginfo_handler) == SIG_ERR) { rs_log_error ("signal(SIGINFO) failed: %s", strerror(errno)); return EXIT_IO_ERROR; } if (pipe ((int *) pipe_fd) == -1) { rs_log_error ("pipe failed: %s", strerror (errno)); return EXIT_IO_ERROR; } *dummy_fd = pipe_fd[0]; /* read end */ dcc_set_nonblocking (pipe_fd[0]); dcc_set_nonblocking (pipe_fd[1]); if ((ret = dcc_get_state_dir (&state_dir))) return ret; if ((fd = open (state_dir, O_RDONLY)) == -1) { rs_log_error ("failed to open %s: %s", state_dir, strerror (errno)); free (state_dir); return EXIT_IO_ERROR; } /* CAUTION! Signals can start arriving immediately. Be ready. */ if (fcntl (fd, F_NOTIFY, DN_RENAME|DN_DELETE|DN_MULTISHOT) == -1) { rs_log_warning ("setting F_NOTIFY failed: %s", strerror (errno)); free (state_dir); return EXIT_IO_ERROR; } return 0; #else /* F_NOTIFY */ return EXIT_IO_ERROR; #endif /* F_NOTIFY */ }
/* * Connect to a host given its binary address, with a timeout. * * host and port are only here to aid printing debug messages. */ static int dcc_connect_by_addr(struct sockaddr *sa, size_t salen, int *p_fd) { int fd; int ret; char *s; int failed; dcc_sockaddr_to_string(sa, salen, &s); rs_trace("started connecting to %s", s); if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) == -1) { rs_log_error("failed to create socket: %s", strerror(errno)); ret = EXIT_CONNECT_FAILED; goto out_failed; } dcc_set_nonblocking(fd); /* start the nonblocking connect... */ do failed = connect(fd, sa, salen); while (failed == -1 && errno == EINTR); if (failed == -1 && errno != EINPROGRESS) { rs_log(RS_LOG_ERR|RS_LOG_NONAME, "failed to connect to %s: %s", s, strerror(errno)); ret = EXIT_CONNECT_FAILED; goto out_failed; } if ((ret = dcc_select_for_write(fd, dcc_connect_timeout))) { rs_log(RS_LOG_ERR|RS_LOG_NONAME, "timeout while connecting to %s", s); goto out_failed; } *p_fd = fd; free(s); return 0; out_failed: free(s); return ret; }
/* * Connect to a host given its binary address, with a timeout. * * host and port are only here to aid printing debug messages. */ int dcc_connect_by_addr(struct sockaddr *sa, size_t salen, int *p_fd) { int fd; int ret; char *s; int failed; int connecterr; int tries = 3; dcc_sockaddr_to_string(sa, salen, &s); if (s == NULL) return EXIT_OUT_OF_MEMORY; rs_trace("started connecting to %s", s); if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) == -1) { rs_log_error("failed to create socket: %s", strerror(errno)); ret = EXIT_CONNECT_FAILED; goto out_failed; } dcc_set_nonblocking(fd); /* start the nonblocking connect... */ do failed = connect(fd, sa, salen); while (failed == -1 && (errno == EINTR || (errno == EAGAIN && tries-- && poll(NULL, 0, 500) == 0))); if (failed == -1 && errno != EINPROGRESS) { rs_log(RS_LOG_ERR|RS_LOG_NONAME, "failed to connect to %s: %s", s, strerror(errno)); ret = EXIT_CONNECT_FAILED; goto out_failed; } do { socklen_t len; if ((ret = dcc_select_for_write(fd, dcc_connect_timeout))) { rs_log(RS_LOG_ERR|RS_LOG_NONAME, "timeout while connecting to %s", s); goto out_failed; } connecterr = -1; len = sizeof(connecterr); if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *)&connecterr, &len) < 0) { rs_log_error("getsockopt SO_ERROR failed?!"); ret = EXIT_CONNECT_FAILED; goto out_failed; } /* looping is unlikely, but I believe I needed this in dkftpbench */ /* fixme: should reduce timeout on each time around this loop */ } while (connecterr == EINPROGRESS); if (connecterr) { rs_log(RS_LOG_ERR|RS_LOG_NONAME, "nonblocking connect to %s failed: %s", s, strerror(connecterr)); ret = EXIT_CONNECT_FAILED; goto out_failed; } *p_fd = fd; free(s); return 0; out_failed: free(s); return ret; }