const char *EventMachine_t::Popen (const char *cmd, const char *mode) { #ifdef OS_WIN32 throw std::runtime_error ("popen is currently unavailable on this platform"); #endif // The whole rest of this function is only compiled on Unix systems. // Eventually we need this functionality (or a full-duplex equivalent) on Windows. #ifdef OS_UNIX const char *output_binding = NULL; FILE *fp = popen (cmd, mode); if (!fp) return NULL; // From here, all early returns must pclose the stream. // According to the pipe(2) manpage, descriptors returned from pipe have both // CLOEXEC and NONBLOCK clear. Do NOT set CLOEXEC. DO set nonblocking. if (!SetSocketNonblocking (fileno (fp))) { pclose (fp); return NULL; } { // Looking good. PipeDescriptor *pd = new PipeDescriptor (fp, this); if (!pd) throw std::runtime_error ("unable to allocate pipe"); Add (pd); output_binding = pd->GetBinding().c_str(); } return output_binding; #endif }
const char *EventMachine_t::Socketpair (char * const*cmd_strings) { #ifdef OS_WIN32 throw std::runtime_error ("socketpair is currently unavailable on this platform"); #endif // The whole rest of this function is only compiled on Unix systems. // Eventually we need this functionality (or a full-duplex equivalent) on Windows. #ifdef OS_UNIX // Make sure the incoming array of command strings is sane. if (!cmd_strings) return NULL; int j; for (j=0; j < 100 && cmd_strings[j]; j++) ; if ((j==0) || (j==100)) return NULL; const char *output_binding = NULL; int sv[2]; if (socketpair (AF_LOCAL, SOCK_STREAM, 0, sv) < 0) return NULL; // from here, all early returns must close the pair of sockets. // Set the parent side of the socketpair nonblocking. // We don't care about the child side, and most child processes will expect their // stdout to be blocking. Thanks to Duane Johnson and Bill Kelly for pointing this out. // Obviously DON'T set CLOEXEC. if (!SetSocketNonblocking (sv[0])) { close (sv[0]); close (sv[1]); return NULL; } pid_t f = fork(); if (f > 0) { close (sv[1]); PipeDescriptor *pd = new PipeDescriptor (sv[0], f, this); if (!pd) throw std::runtime_error ("unable to allocate pipe"); Add (pd); output_binding = pd->GetBinding().c_str(); } else if (f == 0) { close (sv[0]); dup2 (sv[1], STDIN_FILENO); close (sv[1]); dup2 (STDIN_FILENO, STDOUT_FILENO); execvp (cmd_strings[0], cmd_strings+1); exit (-1); // end the child process if the exec doesn't work. } else throw std::runtime_error ("no fork"); return output_binding; #endif }
EventSocketServer(const std::string &addr, const std::string &port, boost::shared_ptr<RequestHandler> handler, boost::shared_ptr<EventLoop> el): SocketServer(addr, port), request_handler_(handler), eventloop_(el) { SetSocketNonblocking(listener_fd_); }
const char *EventMachine_t::OpenDatagramSocket (const char *address, int port) { const char *output_binding = NULL; int sd = socket (AF_INET, SOCK_DGRAM, 0); if (sd == INVALID_SOCKET) goto fail; // from here on, early returns must close the socket! struct sockaddr_in sin; memset (&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons (port); if (address && *address) { sin.sin_addr.s_addr = inet_addr (address); if (sin.sin_addr.s_addr == INADDR_NONE) { hostent *hp = gethostbyname ((char*)address); // Windows requires the cast. if (hp == NULL) goto fail; sin.sin_addr.s_addr = ((in_addr*)(hp->h_addr))->s_addr; } } else sin.sin_addr.s_addr = htonl (INADDR_ANY); // Set the new socket nonblocking. { if (!SetSocketNonblocking (sd)) //int val = fcntl (sd, F_GETFL, 0); //if (fcntl (sd, F_SETFL, val | O_NONBLOCK) == -1) goto fail; } if (bind (sd, (struct sockaddr*)&sin, sizeof(sin)) != 0) goto fail; { // Looking good. DatagramDescriptor *ds = new DatagramDescriptor (sd, this); if (!ds) throw std::runtime_error ("unable to allocate datagram-socket"); Add (ds); output_binding = ds->GetBinding().c_str(); } return output_binding; fail: if (sd != INVALID_SOCKET) closesocket (sd); return NULL; }
bool SocketClient::Open(bool force_open) { if (!force_open && IsConnected()) { LOG(WARNING) << "already connected"; return true; } Close(); fd_ = CreateClientSocket(peer_address_, peer_port_); if (nonblocking_) SetSocketNonblocking(fd_); return fd_ >= 0; }
void EventMachine_t::_InitializeLoopBreaker() { /* A "loop-breaker" is a socket-descriptor that we can write to in order * to break the main select loop. Primarily useful for things running on * threads other than the main EM thread, so they can trigger processing * of events that arise exogenously to the EM. * Keep the loop-breaker pipe out of the main descriptor set, otherwise * its events will get passed on to user code. */ #ifdef OS_UNIX int fd[2]; if (pipe (fd)) throw std::runtime_error ("no loop breaker"); LoopBreakerWriter = fd[1]; LoopBreakerReader = fd[0]; #endif #ifdef OS_WIN32 int sd = socket (AF_INET, SOCK_DGRAM, 0); if (sd == INVALID_SOCKET) throw std::runtime_error ("no loop breaker socket"); SetSocketNonblocking (sd); memset (&LoopBreakerTarget, 0, sizeof(LoopBreakerTarget)); LoopBreakerTarget.sin_family = AF_INET; LoopBreakerTarget.sin_addr.s_addr = inet_addr ("127.0.0.1"); srand ((int)time(NULL)); int i; for (i=0; i < 100; i++) { int r = (rand() % 10000) + 20000; LoopBreakerTarget.sin_port = htons (r); if (bind (sd, (struct sockaddr*)&LoopBreakerTarget, sizeof(LoopBreakerTarget)) == 0) break; } if (i == 100) throw std::runtime_error ("no loop breaker"); LoopBreakerReader = sd; #endif }
InotifyDescriptor::InotifyDescriptor (EventMachine_t *em): EventableDescriptor(0, em) { bCallbackUnbind = false; #ifndef HAVE_INOTIFY throw std::runtime_error("no inotify support on this system"); #else int fd = inotify_init(); if (fd == -1) { char buf[200]; snprintf (buf, sizeof(buf)-1, "unable to create inotify descriptor: %s", strerror(errno)); throw std::runtime_error (buf); } MySocket = fd; SetSocketNonblocking(MySocket); #ifdef HAVE_EPOLL EpollEvent.events = EPOLLIN; #endif #endif }
void AcceptorDescriptor::Read() { /* Accept up to a certain number of sockets on the listening connection. * Don't try to accept all that are present, because this would allow a DoS attack * in which no data were ever read or written. We should accept more than one, * if available, to keep the partially accepted sockets from backing up in the kernel. */ /* Make sure we use non-blocking i/o on the acceptor socket, since we're selecting it * for readability. According to Stevens UNP, it's possible for an acceptor to select readable * and then block when we call accept. For example, the other end resets the connection after * the socket selects readable and before we call accept. The kernel will remove the dead * socket from the accept queue. If the accept queue is now empty, accept will block. */ struct sockaddr_in pin; socklen_t addrlen = sizeof (pin); for (int i=0; i < 10; i++) { int sd = accept (GetSocket(), (struct sockaddr*)&pin, &addrlen); if (sd == INVALID_SOCKET) { // This breaks the loop when we've accepted everything on the kernel queue, // up to 10 new connections. But what if the *first* accept fails? // Does that mean anything serious is happening, beyond the situation // described in the note above? break; } // Set the newly-accepted socket non-blocking. // On Windows, this may fail because, weirdly, Windows inherits the non-blocking // attribute that we applied to the acceptor socket into the accepted one. if (!SetSocketNonblocking (sd)) { //int val = fcntl (sd, F_GETFL, 0); //if (fcntl (sd, F_SETFL, val | O_NONBLOCK) == -1) { shutdown (sd, 1); closesocket (sd); continue; } // Disable slow-start (Nagle algorithm). Eventually make this configurable. int one = 1; setsockopt (sd, IPPROTO_TCP, TCP_NODELAY, (char*) &one, sizeof(one)); ConnectionDescriptor *cd = new ConnectionDescriptor (sd, MyEventMachine); if (!cd) throw std::runtime_error ("no newly accepted connection"); cd->SetServerMode(); if (EventCallback) { (*EventCallback) (GetBinding().c_str(), EM_CONNECTION_ACCEPTED, cd->GetBinding().c_str(), cd->GetBinding().size()); } #ifdef HAVE_EPOLL cd->GetEpollEvent()->events = EPOLLIN | (cd->SelectForWrite() ? EPOLLOUT : 0); #endif assert (MyEventMachine); MyEventMachine->Add (cd); #ifdef HAVE_KQUEUE if (cd->SelectForWrite()) MyEventMachine->ArmKqueueWriter (cd); MyEventMachine->ArmKqueueReader (cd); #endif } }
const char *EventMachine_t::ConnectToServer (const char *server, int port) { /* We want to spend no more than a few seconds waiting for a connection * to a remote host. So we use a nonblocking connect. * Linux disobeys the usual rules for nonblocking connects. * Per Stevens (UNP p.410), you expect a nonblocking connect to select * both readable and writable on error, and not to return EINPROGRESS * if the connect can be fulfilled immediately. Linux violates both * of these expectations. * Any kind of nonblocking connect on Linux returns EINPROGRESS. * The socket will then return writable when the disposition of the * connect is known, but it will not also be readable in case of * error! Weirdly, it will be readable in case there is data to read!!! * (Which can happen with protocols like SSH and SMTP.) * I suppose if you were so inclined you could consider this logical, * but it's not the way Unix has historically done it. * So we ignore the readable flag and read getsockopt to see if there * was an error connecting. A select timeout works as expected. * In regard to getsockopt: Linux does the Berkeley-style thing, * not the Solaris-style, and returns zero with the error code in * the error parameter. * Return the binding-text of the newly-created pending connection, * or NULL if there was a problem. */ if (!server || !*server || !port) return NULL; int family, bind_size; struct sockaddr *bind_as = name2address (server, port, &family, &bind_size); if (!bind_as) return NULL; int sd = socket (family, SOCK_STREAM, 0); if (sd == INVALID_SOCKET) return NULL; /* sockaddr_in pin; unsigned long HostAddr; HostAddr = inet_addr (server); if (HostAddr == INADDR_NONE) { hostent *hp = gethostbyname ((char*)server); // Windows requires (char*) if (!hp) { // TODO: This gives the caller a fatal error. Not good. // They can respond by catching RuntimeError (blecch). // Possibly we need to fire an unbind event and provide // a status code so user code can detect the cause of the // failure. return NULL; } HostAddr = ((in_addr*)(hp->h_addr))->s_addr; } memset (&pin, 0, sizeof(pin)); pin.sin_family = AF_INET; pin.sin_addr.s_addr = HostAddr; pin.sin_port = htons (port); int sd = socket (AF_INET, SOCK_STREAM, 0); if (sd == INVALID_SOCKET) return NULL; */ // From here on, ALL error returns must close the socket. // Set the new socket nonblocking. if (!SetSocketNonblocking (sd)) { closesocket (sd); return NULL; } // Disable slow-start (Nagle algorithm). int one = 1; setsockopt (sd, IPPROTO_TCP, TCP_NODELAY, (char*) &one, sizeof(one)); const char *out = NULL; #ifdef OS_UNIX //if (connect (sd, (sockaddr*)&pin, sizeof pin) == 0) { if (connect (sd, bind_as, bind_size) == 0) { // This is a connect success, which Linux appears // never to give when the socket is nonblocking, // even if the connection is intramachine or to // localhost. /* Changed this branch 08Aug06. Evidently some kernels * (FreeBSD for example) will actually return success from * a nonblocking connect. This is a pretty simple case, * just set up the new connection and clear the pending flag. * Thanks to Chris Ochs for helping track this down. * This branch never gets taken on Linux or (oddly) OSX. * The original behavior was to throw an unimplemented, * which the user saw as a fatal exception. Very unfriendly. * * Tweaked 10Aug06. Even though the connect disposition is * known, we still set the connect-pending flag. That way * some needed initialization will happen in the ConnectionDescriptor. * (To wit, the ConnectionCompleted event gets sent to the client.) */ ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this); if (!cd) throw std::runtime_error ("no connection allocated"); cd->SetConnectPending (true); Add (cd); out = cd->GetBinding().c_str(); } else if (errno == EINPROGRESS) { // Errno will generally always be EINPROGRESS, but on Linux // we have to look at getsockopt to be sure what really happened. int error; socklen_t len; len = sizeof(error); int o = getsockopt (sd, SOL_SOCKET, SO_ERROR, &error, &len); if ((o == 0) && (error == 0)) { // Here, there's no disposition. // Put the connection on the stack and wait for it to complete // or time out. ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this); if (!cd) throw std::runtime_error ("no connection allocated"); cd->SetConnectPending (true); Add (cd); out = cd->GetBinding().c_str(); } else { /* This could be connection refused or some such thing. * We will come here on Linux if a localhost connection fails. * Changed 16Jul06: Originally this branch was a no-op, and * we'd drop down to the end of the method, close the socket, * and return NULL, which would cause the caller to GET A * FATAL EXCEPTION. Now we keep the socket around but schedule an * immediate close on it, so the caller will get a close-event * scheduled on it. This was only an issue for localhost connections * to non-listening ports. We may eventually need to revise this * revised behavior, in case it causes problems like making it hard * for people to know that a failure occurred. */ ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this); if (!cd) throw std::runtime_error ("no connection allocated"); cd->ScheduleClose (false); Add (cd); out = cd->GetBinding().c_str(); } } else { // The error from connect was something other then EINPROGRESS. } #endif #ifdef OS_WIN32 //if (connect (sd, (sockaddr*)&pin, sizeof pin) == 0) { if (connect (sd, bind_as, bind_size) == 0) { // This is a connect success, which Windows appears // never to give when the socket is nonblocking, // even if the connection is intramachine or to // localhost. throw std::runtime_error ("unimplemented"); } else if (WSAGetLastError() == WSAEWOULDBLOCK) { // Here, there's no disposition. // Windows appears not to surface refused connections or // such stuff at this point. // Put the connection on the stack and wait for it to complete // or time out. ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this); if (!cd) throw std::runtime_error ("no connection allocated"); cd->SetConnectPending (true); Add (cd); out = cd->GetBinding().c_str(); } else { // The error from connect was something other then WSAEWOULDBLOCK. } #endif if (out == NULL) closesocket (sd); return out; }
const char *EventMachine_t::CreateUnixDomainServer (const char *filename) { /* Create a UNIX-domain acceptor (server) socket and add it to the event machine. * Return the binding of the new acceptor to the caller. * This binding will be referenced when the new acceptor sends events * to indicate accepted connections. * THERE IS NO MEANINGFUL IMPLEMENTATION ON WINDOWS. */ #ifdef OS_WIN32 throw std::runtime_error ("unix-domain server unavailable on this platform"); #endif // The whole rest of this function is only compiled on Unix systems. #ifdef OS_UNIX const char *output_binding = NULL; struct sockaddr_un s_sun; int sd_accept = socket (AF_LOCAL, SOCK_STREAM, 0); if (sd_accept == INVALID_SOCKET) { goto fail; } if (!filename || !*filename) goto fail; unlink (filename); bzero (&s_sun, sizeof(s_sun)); s_sun.sun_family = AF_LOCAL; strncpy (s_sun.sun_path, filename, sizeof(s_sun.sun_path)-1); // don't bother with reuseaddr for a local socket. { // set CLOEXEC. Only makes sense on Unix #ifdef OS_UNIX int cloexec = fcntl (sd_accept, F_GETFD, 0); assert (cloexec >= 0); cloexec |= FD_CLOEXEC; fcntl (sd_accept, F_SETFD, cloexec); #endif } if (bind (sd_accept, (struct sockaddr*)&s_sun, sizeof(s_sun))) { //__warning ("binding failed"); goto fail; } if (listen (sd_accept, 100)) { //__warning ("listen failed"); goto fail; } { // Set the acceptor non-blocking. // THIS IS CRUCIALLY IMPORTANT because we read it in a select loop. if (!SetSocketNonblocking (sd_accept)) { //int val = fcntl (sd_accept, F_GETFL, 0); //if (fcntl (sd_accept, F_SETFL, val | O_NONBLOCK) == -1) { goto fail; } } { // Looking good. AcceptorDescriptor *ad = new AcceptorDescriptor (sd_accept, this); if (!ad) throw std::runtime_error ("unable to allocate acceptor"); Add (ad); output_binding = ad->GetBinding().c_str(); } return output_binding; fail: if (sd_accept != INVALID_SOCKET) closesocket (sd_accept); return NULL; #endif // OS_UNIX }
const char *EventMachine_t::CreateTcpServer (const char *server, int port) { /* Create a TCP-acceptor (server) socket and add it to the event machine. * Return the binding of the new acceptor to the caller. * This binding will be referenced when the new acceptor sends events * to indicate accepted connections. */ int family, bind_size; struct sockaddr *bind_here = name2address (server, port, &family, &bind_size); if (!bind_here) return NULL; const char *output_binding = NULL; //struct sockaddr_in sin; int sd_accept = socket (family, SOCK_STREAM, 0); if (sd_accept == INVALID_SOCKET) { goto fail; } /* memset (&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = htons (port); if (server && *server) { sin.sin_addr.s_addr = inet_addr (server); if (sin.sin_addr.s_addr == INADDR_NONE) { hostent *hp = gethostbyname ((char*)server); // Windows requires the cast. if (hp == NULL) { //__warning ("hostname not resolved: ", server); goto fail; } sin.sin_addr.s_addr = ((in_addr*)(hp->h_addr))->s_addr; } } */ { // set reuseaddr to improve performance on restarts. int oval = 1; if (setsockopt (sd_accept, SOL_SOCKET, SO_REUSEADDR, (char*)&oval, sizeof(oval)) < 0) { //__warning ("setsockopt failed while creating listener",""); goto fail; } } { // set CLOEXEC. Only makes sense on Unix #ifdef OS_UNIX int cloexec = fcntl (sd_accept, F_GETFD, 0); assert (cloexec >= 0); cloexec |= FD_CLOEXEC; fcntl (sd_accept, F_SETFD, cloexec); #endif } //if (bind (sd_accept, (struct sockaddr*)&sin, sizeof(sin))) { if (bind (sd_accept, bind_here, bind_size)) { //__warning ("binding failed"); goto fail; } if (listen (sd_accept, 100)) { //__warning ("listen failed"); goto fail; } { // Set the acceptor non-blocking. // THIS IS CRUCIALLY IMPORTANT because we read it in a select loop. if (!SetSocketNonblocking (sd_accept)) { //int val = fcntl (sd_accept, F_GETFL, 0); //if (fcntl (sd_accept, F_SETFL, val | O_NONBLOCK) == -1) { goto fail; } } { // Looking good. AcceptorDescriptor *ad = new AcceptorDescriptor (sd_accept, this); if (!ad) throw std::runtime_error ("unable to allocate acceptor"); Add (ad); output_binding = ad->GetBinding().c_str(); } return output_binding; fail: if (sd_accept != INVALID_SOCKET) closesocket (sd_accept); return NULL; }
const char *EventMachine_t::ConnectToUnixServer (const char *server) { /* Connect to a Unix-domain server, which by definition is running * on the same host. * There is no meaningful implementation on Windows. * There's no need to do a nonblocking connect, since the connection * is always local and can always be fulfilled immediately. */ #ifdef OS_WIN32 throw std::runtime_error ("unix-domain connection unavailable on this platform"); return NULL; #endif // The whole rest of this function is only compiled on Unix systems. #ifdef OS_UNIX const char *out = NULL; if (!server || !*server) return NULL; sockaddr_un pun; memset (&pun, 0, sizeof(pun)); pun.sun_family = AF_LOCAL; // You ordinarily expect the server name field to be at least 1024 bytes long, // but on Linux it can be MUCH shorter. if (strlen(server) >= sizeof(pun.sun_path)) throw std::runtime_error ("unix-domain server name is too long"); strcpy (pun.sun_path, server); int fd = socket (AF_LOCAL, SOCK_STREAM, 0); if (fd == INVALID_SOCKET) return NULL; // From here on, ALL error returns must close the socket. // NOTE: At this point, the socket is still a blocking socket. if (connect (fd, (struct sockaddr*)&pun, sizeof(pun)) != 0) { closesocket (fd); return NULL; } // Set the newly-connected socket nonblocking. if (!SetSocketNonblocking (fd)) { closesocket (fd); return NULL; } // Set up a connection descriptor and add it to the event-machine. // Observe, even though we know the connection status is connect-success, // we still set the "pending" flag, so some needed initializations take // place. ConnectionDescriptor *cd = new ConnectionDescriptor (fd, this); if (!cd) throw std::runtime_error ("no connection allocated"); cd->SetConnectPending (true); Add (cd); out = cd->GetBinding().c_str(); if (out == NULL) closesocket (fd); return out; #endif }