|  | /* Copyright (c) 2014 The Regents of the University of California | 
|  | * Copyright (c) 2015 Google, Inc. | 
|  | * Barret Rhoden <brho@cs.berkeley.edu> | 
|  | * See LICENSE for details. | 
|  | * | 
|  | * Echo server using epoll, runs on port 23.  Main purpose is epoll testing. | 
|  | * | 
|  | * If you want to build the BSD sockets version, you need to comment out the | 
|  | * #define for PLAN9NET. */ | 
|  |  | 
|  | /* Comment this out for BSD sockets */ | 
|  | //#define PLAN9NET | 
|  |  | 
|  | #include <stdlib.h> | 
|  | #include <stdio.h> | 
|  | #include <parlib/parlib.h> | 
|  | #include <unistd.h> | 
|  | #include <parlib/event.h> | 
|  | #include <benchutil/measure.h> | 
|  | #include <parlib/uthread.h> | 
|  | #include <parlib/timing.h> | 
|  |  | 
|  | #include <sys/types.h> | 
|  | #include <sys/stat.h> | 
|  | #include <fcntl.h> | 
|  |  | 
|  | #include <sys/epoll.h> | 
|  |  | 
|  | #ifdef PLAN9NET | 
|  |  | 
|  | #include <iplib/iplib.h> | 
|  |  | 
|  | #else | 
|  |  | 
|  | #include <sys/types.h> | 
|  | #include <sys/socket.h> | 
|  | #include <netinet/in.h> | 
|  | #include <arpa/inet.h> | 
|  |  | 
|  | #endif | 
|  |  | 
|  | int main() | 
|  | { | 
|  | int ret; | 
|  | int afd, dfd, lcfd, listen_fd; | 
|  | char adir[40], ldir[40]; | 
|  | int n; | 
|  | char buf[256]; | 
|  | /* We'll use this to see if we actually did epoll_waits instead of blocking | 
|  | * calls.  It's not 100%, but with a human on the other end, it should be | 
|  | * fine. */ | 
|  | bool has_epolled = FALSE; | 
|  |  | 
|  | #ifdef PLAN9NET | 
|  | printf("Using Plan 9's networking stack\n"); | 
|  | /* This clones a conversation (opens /net/tcp/clone), then reads the cloned | 
|  | * fd (which is the ctl) to givure out the conv number (the line), then | 
|  | * writes "announce [addr]" into ctl.  This "announce" command often has a | 
|  | * "bind" in it too.  plan9 bind just sets the local addr/port.  TCP | 
|  | * announce also does this.  Returns the ctlfd. */ | 
|  | afd = announce9("tcp!*!23", adir, 0); | 
|  |  | 
|  | if (afd < 0) { | 
|  | perror("Announce failure"); | 
|  | return -1; | 
|  | } | 
|  | printf("Announced on line %s\n", adir); | 
|  | #else | 
|  | printf("Using the BSD socket shims over Plan 9's networking stack\n"); | 
|  | int srv_socket, con_socket; | 
|  | struct sockaddr_in dest, srv = {0}; | 
|  | srv.sin_family = AF_INET; | 
|  | srv.sin_addr.s_addr = htonl(INADDR_ANY); | 
|  | srv.sin_port = htons(23); | 
|  | socklen_t socksize = sizeof(struct sockaddr_in); | 
|  |  | 
|  | /* Equiv to cloning a converstation in plan 9.  The shim returns the data FD | 
|  | * for the conversation. */ | 
|  | srv_socket = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); | 
|  | if (srv_socket < 0) { | 
|  | perror("Socket failure"); | 
|  | return -1; | 
|  | } | 
|  | /* bind + listen is equiv to announce() in plan 9.  Note that the "bind" | 
|  | * command is used, unlike in the plan9 announce. */ | 
|  | /* Binds our socket to the given addr/port in srv. */ | 
|  | ret = bind(srv_socket, (struct sockaddr*)&srv, sizeof(struct sockaddr_in)); | 
|  | if (ret < 0) { | 
|  | perror("Bind failure"); | 
|  | return -1; | 
|  | } | 
|  | /* marks the socket as a listener/server */ | 
|  | ret = listen(srv_socket, 1); | 
|  | if (ret < 0) { | 
|  | perror("Listen failure"); | 
|  | return -1; | 
|  | } | 
|  | printf("Listened on port %d\n", ntohs(srv.sin_port)); | 
|  | #endif | 
|  |  | 
|  | /* at this point, the server has done all the prep necessary to be able to | 
|  | * sleep/block/wait on an incoming connection. */ | 
|  | #define EP_SET_SZ 10	/* this is actually the ID of the largest FD */ | 
|  | int epfd = epoll_create(EP_SET_SZ); | 
|  | struct epoll_event ep_ev; | 
|  | struct epoll_event results[EP_SET_SZ]; | 
|  |  | 
|  | if (epfd < 0) { | 
|  | perror("epoll_create"); | 
|  | exit(-1); | 
|  | } | 
|  | ep_ev.events = EPOLLIN | EPOLLET; | 
|  |  | 
|  | #ifdef PLAN9NET | 
|  |  | 
|  | snprintf(buf, sizeof(buf), "%s/listen", adir); | 
|  | listen_fd = open(buf, O_PATH); | 
|  | if (listen_fd < 0){ | 
|  | perror("listen fd"); | 
|  | return -1; | 
|  | } | 
|  | /* This is a little subtle.  We're putting a tap on the listen file / | 
|  | * listen_fd.  When this fires, we get an event because of that listen_fd. | 
|  | * But we don't actually listen or do anything to that listen_fd.  It's | 
|  | * solely for monitoring.  We open a path, below, and we'll reattempt to do | 
|  | * *that* operation when someone tells us that our listen tap fires. */ | 
|  | ep_ev.data.fd = listen_fd; | 
|  | if (epoll_ctl(epfd, EPOLL_CTL_ADD, listen_fd, &ep_ev)) { | 
|  | perror("epoll_ctl_add listen"); | 
|  | exit(-1); | 
|  | } | 
|  | has_epolled = FALSE; | 
|  | while (1) { | 
|  | /* Opens the conversation's listen file.  This blocks til someone | 
|  | * connects.  When they do, a new conversation is created, and that open | 
|  | * returned an FD for the new conv's ctl.  listen() reads that to find | 
|  | * out the conv number (the line) for this new conv.  listen() returns | 
|  | * the ctl for this new conv. | 
|  | * | 
|  | * Non-block is for the act of listening, and applies to lcfd. */ | 
|  | lcfd = listen9(adir, ldir, O_NONBLOCK); | 
|  | if (lcfd >= 0) | 
|  | break; | 
|  | if (errno != EAGAIN) { | 
|  | perror("Listen failure"); | 
|  | return -1; | 
|  | } | 
|  | if (epoll_wait(epfd, results, EP_SET_SZ, -1) != 1) { | 
|  | perror("epoll_wait"); | 
|  | exit(-1); | 
|  | } | 
|  | has_epolled = TRUE; | 
|  | assert(results[0].data.fd == listen_fd); | 
|  | assert(results[0].events == EPOLLIN); | 
|  | } | 
|  | printf("Listened and got line %s\n", ldir); | 
|  | assert(has_epolled); | 
|  |  | 
|  | /* No longer need listen_fd.  You should CTL_DEL before closing. */ | 
|  | if (epoll_ctl(epfd, EPOLL_CTL_DEL, listen_fd, &ep_ev)) { | 
|  | perror("epoll_ctl_del"); | 
|  | exit(-1); | 
|  | } | 
|  | close(listen_fd); | 
|  |  | 
|  | /* Writes "accept [NUM]" into the ctlfd, then opens the conv's data file and | 
|  | * returns that fd.  Writing "accept" is a noop for most of our protocols. | 
|  | * */ | 
|  | dfd = accept9(lcfd, ldir); | 
|  | if (dfd < 0) { | 
|  | perror("Accept failure"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | #else | 
|  |  | 
|  | ep_ev.data.fd = srv_socket; | 
|  | if (epoll_ctl(epfd, EPOLL_CTL_ADD, srv_socket, &ep_ev)) { | 
|  | perror("epoll_ctl_add srv_socket"); | 
|  | exit(-1); | 
|  | } | 
|  | has_epolled = FALSE; | 
|  | while (1) { | 
|  | /* returns an FD for a new socket. */ | 
|  | dfd = accept(srv_socket, (struct sockaddr*)&dest, &socksize); | 
|  | if (dfd >= 0) | 
|  | break; | 
|  | if (errno != EAGAIN) { | 
|  | perror("Accept failure"); | 
|  | return -1; | 
|  | } | 
|  | if (epoll_wait(epfd, results, EP_SET_SZ, -1) != 1) { | 
|  | perror("epoll_wait"); | 
|  | exit(-1); | 
|  | } | 
|  | has_epolled = TRUE; | 
|  | assert(results[0].data.fd == srv_socket); | 
|  | assert(results[0].events == EPOLLIN); | 
|  | } | 
|  | printf("Accepted and got dfd %d\n", dfd); | 
|  | assert(has_epolled); | 
|  | if (epoll_ctl(epfd, EPOLL_CTL_DEL, srv_socket, &ep_ev)) { | 
|  | perror("epoll_ctl_del"); | 
|  | while (1); | 
|  | exit(-1); | 
|  | } | 
|  |  | 
|  | #endif | 
|  |  | 
|  | /* In lieu of accept4, we set the new socket's nonblock status manually. | 
|  | * Both OSs do this.  */ | 
|  | ret = fcntl(dfd, F_SETFL, O_NONBLOCK); | 
|  | if (ret < 0) { | 
|  | perror("setfl dfd"); | 
|  | exit(-1); | 
|  | } | 
|  | ep_ev.data.fd = dfd; | 
|  | if (epoll_ctl(epfd, EPOLL_CTL_ADD, dfd, &ep_ev)) { | 
|  | perror("epoll_ctl_add dvd"); | 
|  | exit(-1); | 
|  | } | 
|  | /* echo until EOF */ | 
|  | has_epolled = FALSE; | 
|  | printf("Server read: "); | 
|  | while (1) { | 
|  | while ((n = read(dfd, buf, sizeof(buf))) > 0) { | 
|  | for (int i = 0; i < n; i++) | 
|  | printf("%c", buf[i]); | 
|  | fflush(stdout); | 
|  | /* Should epoll on this direction too. */ | 
|  | if (write(dfd, buf, n) < 0) { | 
|  | perror("writing"); | 
|  | exit(-1); | 
|  | } | 
|  | } | 
|  | if (n == 0) | 
|  | break; | 
|  | if (epoll_wait(epfd, results, EP_SET_SZ, -1) != 1) { | 
|  | perror("epoll_wait 2"); | 
|  | exit(-1); | 
|  | } | 
|  | has_epolled = TRUE; | 
|  | assert(results[0].data.fd == dfd); | 
|  | /* you might get a HUP, but keep on reading! */ | 
|  | } | 
|  | assert(has_epolled); | 
|  | if (epoll_ctl(epfd, EPOLL_CTL_DEL, dfd, &ep_ev)) { | 
|  | perror("epoll_ctl_del dfd"); | 
|  | exit(-1); | 
|  | } | 
|  |  | 
|  | #ifdef PLAN9NET | 
|  | close(dfd);		/* data fd for the new conv, from listen */ | 
|  | close(lcfd);	/* ctl fd for the new conv, from listen */ | 
|  | close(afd);		/* ctl fd for the listening conv */ | 
|  | #else | 
|  | close(dfd);		/* new connection socket, from accept */ | 
|  | close(srv_socket); | 
|  | #endif | 
|  | } |