topic_monitor_t::topic_monitor_t() { // Set up our pipes. Assert it succeeds. auto pipes = make_autoclose_pipes({}); assert(pipes.has_value() && "Failed to make pubsub pipes"); pipes_ = pipes.acquire(); // Make sure that our write side doesn't block, else we risk hanging in a signal handler. // The read end must block to avoid spinning in await. DIE_ON_FAILURE(make_fd_nonblocking(pipes_.write.fd())); #if TOPIC_MONITOR_TSAN_WORKAROUND DIE_ON_FAILURE(make_fd_nonblocking(pipes_.read.fd())); #endif }
io_buffer_t *io_buffer_t::create(int fd) { bool success = true; assert(fd >= 0); io_buffer_t *buffer_redirect = new io_buffer_t(fd); if (exec_pipe(buffer_redirect->pipe_fd) == -1) { debug(1, PIPE_ERROR); wperror(L"pipe"); success = false; } else if (make_fd_nonblocking(buffer_redirect->pipe_fd[0]) != 0) { debug(1, PIPE_ERROR); wperror(L"fcntl"); success = false; } if (! success) { delete buffer_redirect; buffer_redirect = NULL; } else { //fprintf(stderr, "Created pipes {%d, %d} for %p\n", buffer_redirect->pipe_fd[0], buffer_redirect->pipe_fd[1], buffer_redirect); } return buffer_redirect; }
shared_ptr<io_bufferfill_t> io_bufferfill_t::create(const io_chain_t &conflicts, size_t buffer_limit) { // Construct our pipes. auto pipes = make_autoclose_pipes(conflicts); if (!pipes) { return nullptr; } // Our buffer will read from the read end of the pipe. This end must be non-blocking. This is // because our fillthread needs to poll to decide if it should shut down, and also accept input // from direct buffer transfers. if (make_fd_nonblocking(pipes->read.fd())) { debug(1, PIPE_ERROR); wperror(L"fcntl"); return nullptr; } // Our fillthread gets the read end of the pipe; out_pipe gets the write end. auto buffer = std::make_shared<io_buffer_t>(buffer_limit); buffer->begin_background_fillthread(std::move(pipes->read)); return std::make_shared<io_bufferfill_t>(std::move(pipes->write), buffer); }
shared_ptr<io_buffer_t> io_buffer_t::create(int fd, const io_chain_t &conflicts, size_t buffer_limit) { bool success = true; assert(fd >= 0); shared_ptr<io_buffer_t> buffer_redirect(new io_buffer_t(fd, buffer_limit)); if (exec_pipe(buffer_redirect->pipe_fd) == -1) { debug(1, PIPE_ERROR); wperror(L"pipe"); success = false; } else if (!buffer_redirect->avoid_conflicts_with_io_chain(conflicts)) { // The above call closes the fds on error. success = false; } else if (make_fd_nonblocking(buffer_redirect->pipe_fd[0]) != 0) { debug(1, PIPE_ERROR); wperror(L"fcntl"); success = false; } if (!success) { buffer_redirect.reset(); } return buffer_redirect; }
io_buffer_t *io_buffer_t::create(int fd, const io_chain_t &conflicts) { bool success = true; assert(fd >= 0); io_buffer_t *buffer_redirect = new io_buffer_t(fd); if (exec_pipe(buffer_redirect->pipe_fd) == -1) { debug(1, PIPE_ERROR); wperror(L"pipe"); success = false; } else if (!buffer_redirect->avoid_conflicts_with_io_chain(conflicts)) { // The above call closes the fds on error. success = false; } else if (make_fd_nonblocking(buffer_redirect->pipe_fd[0]) != 0) { debug(1, PIPE_ERROR); wperror(L"fcntl"); success = false; } if (!success) { delete buffer_redirect; buffer_redirect = NULL; } return buffer_redirect; }
/** Main function for fishd */ int main(int argc, char ** argv) { int child_socket; struct sockaddr_un remote; socklen_t t; uid_t sock_euid; gid_t sock_egid; int max_fd; int update_count=0; fd_set read_fd, write_fd; set_main_thread(); setup_fork_guards(); program_name=L"fishd"; wsetlocale(LC_ALL, L""); /* Parse options */ while (1) { static struct option long_options[] = { { "help", no_argument, 0, 'h' } , { "version", no_argument, 0, 'v' } , { 0, 0, 0, 0 } } ; int opt_index = 0; int opt = getopt_long(argc, argv, GETOPT_STRING, long_options, &opt_index); if (opt == -1) break; switch (opt) { case 0: break; case 'h': print_help(argv[0], 1); exit(0); case 'v': debug(0, L"%ls, version %s\n", program_name, FISH_BUILD_VERSION); exit(0); case '?': return 1; } } init(); while (1) { int res; t = sizeof(remote); FD_ZERO(&read_fd); FD_ZERO(&write_fd); FD_SET(sock, &read_fd); max_fd = sock+1; for (connection_list_t::const_iterator iter = connections.begin(); iter != connections.end(); ++iter) { const connection_t &c = *iter; FD_SET(c.fd, &read_fd); max_fd = maxi(max_fd, c.fd+1); if (! c.unsent.empty()) { FD_SET(c.fd, &write_fd); } } while (1) { res=select(max_fd, &read_fd, &write_fd, 0, 0); if (quit) { save(); exit(0); } if (res != -1) break; if (errno != EINTR) { wperror(L"select"); exit(1); } } if (FD_ISSET(sock, &read_fd)) { if ((child_socket = accept(sock, (struct sockaddr *)&remote, &t)) == -1) { wperror(L"accept"); exit(1); } else { debug(4, L"Connected with new child on fd %d", child_socket); if (((getpeereid(child_socket, &sock_euid, &sock_egid) != 0) || sock_euid != geteuid())) { debug(1, L"Wrong credentials for child on fd %d", child_socket); close(child_socket); } else if (make_fd_nonblocking(child_socket) != 0) { wperror(L"fcntl"); close(child_socket); } else { connections.push_front(connection_t(child_socket)); connection_t &newc = connections.front(); send(newc.fd, GREETING, strlen(GREETING), MSG_DONTWAIT); enqueue_all(&newc); } } } for (connection_list_t::iterator iter = connections.begin(); iter != connections.end(); ++iter) { if (FD_ISSET(iter->fd, &write_fd)) { try_send_all(&*iter); } } for (connection_list_t::iterator iter = connections.begin(); iter != connections.end(); ++iter) { if (FD_ISSET(iter->fd, &read_fd)) { read_message(&*iter); /* Occasionally we save during normal use, so that we won't lose everything on a system crash */ update_count++; if (update_count >= 64) { save(); update_count = 0; } } } for (connection_list_t::iterator iter = connections.begin(); iter != connections.end();) { if (iter->killme) { debug(4, L"Close connection %d", iter->fd); while (! iter->unsent.empty()) { message_t *msg = iter->unsent.front(); iter->unsent.pop(); msg->count--; if (! msg->count) free(msg); } connection_destroy(&*iter); iter = connections.erase(iter); } else { ++iter; } } if (connections.empty()) { debug(0, L"No more clients. Quitting"); save(); break; } } }
/** Connects to the fish socket and starts listening for connections */ static int get_socket(void) { // Cygwin has random problems involving sockets. When using Cygwin, // allow 20 attempts at making socket correctly. #ifdef __CYGWIN__ int attempts = 0; repeat: attempts += 1; #endif int s, len, doexit = 0; int exitcode = EXIT_FAILURE; struct sockaddr_un local; const std::string sock_name = get_socket_filename(); /* Start critical section protected by lock */ std::string lockfile; if (! acquire_socket_lock(sock_name, &lockfile)) { debug(0, L"Unable to obtain lock on socket, exiting"); exit(EXIT_FAILURE); } debug(4, L"Acquired lockfile: %s", lockfile.c_str()); local.sun_family = AF_UNIX; strcpy(local.sun_path, sock_name.c_str()); len = sizeof(local); debug(1, L"Connect to socket at %s", sock_name.c_str()); if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { wperror(L"socket"); doexit = 1; goto unlock; } /* First check whether the socket has been opened by another fishd; if so, exit with success status */ if (connect(s, (struct sockaddr *)&local, len) == 0) { debug(1, L"Socket already exists, exiting"); doexit = 1; exitcode = 0; goto unlock; } unlink(local.sun_path); if (bind(s, (struct sockaddr *)&local, len) == -1) { wperror(L"bind"); doexit = 1; goto unlock; } if (make_fd_nonblocking(s) != 0) { wperror(L"fcntl"); close(s); doexit = 1; } else if (listen(s, 64) == -1) { wperror(L"listen"); doexit = 1; } unlock: (void)unlink(lockfile.c_str()); debug(4, L"Released lockfile: %s", lockfile.c_str()); /* End critical section protected by lock */ if (doexit) { // If Cygwin, only allow normal quit when made lots of attempts. #ifdef __CYGWIN__ if (exitcode && attempts < 20) goto repeat; #endif exit_without_destructors(exitcode); } return s; }
static int try_get_socket_once(void) { int s; wchar_t *wdir; wchar_t *wuname; char *dir = 0; wdir = path; wuname = user; if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { wperror(L"socket"); return -1; } if (wdir) dir = wcs2str(wdir); else dir = strdup("/tmp"); std::string uname; if (wuname) { uname = wcs2string(wuname); } else { struct passwd *pw = getpwuid(getuid()); if (pw && pw->pw_name) { uname = pw->pw_name; } } std::string name; name.reserve(strlen(dir) + uname.size() + strlen(SOCK_FILENAME) + 2); name.append(dir); name.append("/"); name.append(SOCK_FILENAME); name.append(uname); free(dir); debug(3, L"Connect to socket %s at fd %d", name.c_str(), s); struct sockaddr_un local = {}; local.sun_family = AF_UNIX; strncpy(local.sun_path, name.c_str(), (sizeof local.sun_path) - 1); if (connect(s, (struct sockaddr *)&local, sizeof local) == -1) { close(s); /* If it fails on first try, it's probably no serious error, but fishd hasn't been launched yet. This happens (at least) on the first concurrent session. */ if (get_socket_count > 1) wperror(L"connect"); return -1; } if ((make_fd_nonblocking(s) != 0) || (fcntl(s, F_SETFD, FD_CLOEXEC) != 0)) { wperror(L"fcntl"); close(s); return -1; } debug(3, L"Connected to fd %d", s); return s; }