1/**
2 * \file
3 * \brief Simple Server to test Barrelfish's implementation of and conformance
4 *        to the BSD/POSIX sockets API.
5 *
6 * The server waits until it receives BUFFER_SIZE characters from the client
7 * and then echos these back to the client, waits again, ...
8 *
9 * The code it written so that it compiles on Barrelfish as well as on a
10 * POSIX system.
11 */
12
13/*
14 * Copyright (c) 2012, ETH Zurich.
15 * All rights reserved.
16 *
17 * This file is distributed under the terms in the attached LICENSE file.
18 * If you do not find this file, copies can be found by writing to:
19 * ETH Zurich D-INFK, CAB F.78, Universitaetstr. 6, CH-8092 Zurich,
20 * Attn: Systems Group.
21 */
22
23#include <assert.h>
24#include <inttypes.h>
25#include <stdbool.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <unistd.h>
30
31#include <arpa/inet.h>
32#include <sys/select.h>
33#include <sys/socket.h>
34#include <sys/types.h>
35
36#ifdef BARRELFISH
37# include <barrelfish/barrelfish.h>
38# include <lwip/tcpip.h>
39#endif /* BARRELFISH */
40
41#define DEFAULT_PORT        4242
42#define BACKLOG             10
43#define BUFFER_SIZE         32
44
45#ifndef MAX
46#define	MAX(a,b) (((a)>(b))?(a):(b))
47#endif
48#ifndef MIN
49#define MIN(a,b) (((a)<(b))?(a):(b))
50#endif
51
52enum client_state {
53    READING,
54    WRITING,
55    FINISHED
56};
57
58struct client {
59    int socket;
60    char buffer[BUFFER_SIZE + 1];
61    int buffer_index;
62    enum client_state state;
63    struct client *next;
64};
65
66struct client *clients = NULL;
67
68#ifdef BARRELFISH
69extern void network_polling_loop(void);
70
71static int poll_loop(void *args)
72{
73    network_polling_loop();
74
75    // should never be reached
76    return EXIT_FAILURE;
77}
78#endif /* BARRELFISH */
79
80static void start_server(uint16_t port, int *serversocket)
81{
82    int ret = 0;
83    int listenfd = 0;
84    struct sockaddr_in listen_addr;
85
86    listenfd = socket(AF_INET, SOCK_STREAM, 0);
87    if (listenfd < 0) {
88        perror("Failed to open socket");
89        exit(EXIT_FAILURE);
90    }
91
92    memset(&listen_addr, 0, sizeof(struct sockaddr_in));
93    listen_addr.sin_family      = AF_INET;
94    listen_addr.sin_port        = htons(port);
95    listen_addr.sin_addr.s_addr = htonl(INADDR_ANY);
96
97    ret = bind(listenfd, (struct sockaddr *)&listen_addr,
98               sizeof(struct sockaddr_in));
99    if (ret < 0) {
100        perror("Failed to bind address to socket");
101        exit(EXIT_FAILURE);
102    }
103
104    ret = listen(listenfd, BACKLOG);
105    if (ret < 0) {
106        perror("Failed to put socket into listening mode");
107        exit(EXIT_FAILURE);
108    }
109
110    *serversocket = listenfd;
111}
112
113static void handle_new_client(int serversocket)
114{
115    struct client *client;
116    int clientfd = 0;
117    struct sockaddr_in client_addr;
118    socklen_t client_addrlen = sizeof(struct sockaddr_in);
119
120    clientfd = accept(serversocket, (struct sockaddr *) &client_addr,
121                      &client_addrlen);
122    if (clientfd < 0) {
123        perror("Failed to accept new client.");
124        exit(EXIT_FAILURE);
125    }
126
127    /* allocate client struct */
128    client = calloc(1, sizeof(struct client));
129    assert(client != NULL);
130
131    client->socket       = clientfd;
132    client->buffer_index = 0;
133    client->state        = READING;
134
135    /* insert new client at head of the list */
136    if (clients == NULL) {
137        client->next = NULL;
138        clients = client;
139    } else {
140        client->next = clients;
141        clients = client;
142    }
143}
144
145static void handle_client_read(struct client *client)
146{
147    assert(client != NULL);
148    assert(client->state == READING);
149
150    ssize_t ret = 0;
151
152    ret = read(client->socket, &client->buffer[client->buffer_index],
153               BUFFER_SIZE - client->buffer_index);
154    assert(read > 0);
155
156    client->buffer_index += ret;
157
158    if (client->buffer_index == BUFFER_SIZE) {
159        client->buffer[BUFFER_SIZE] = '\0';
160        client->state = WRITING;
161        client->buffer_index = 0;
162    }
163}
164
165static void handle_client_write(struct client *client)
166{
167    assert(client != NULL);
168    assert(client->state == WRITING);
169
170    ssize_t ret = 0;
171
172    ret = write(client->socket, &client->buffer[client->buffer_index],
173                (BUFFER_SIZE + 1) - client->buffer_index);
174    assert(write > 0);
175
176    client->buffer_index +=ret;
177
178    if (client->buffer_index == BUFFER_SIZE + 1) {
179        client->state = READING;
180        client->buffer_index = 0;
181        memset(client->buffer, '\0', BUFFER_SIZE + 1);
182    }
183}
184
185static void server_accept_loop(int serversocket)
186{
187    int ret = 0;
188    struct client *client;
189    fd_set readfds;
190    fd_set writefds;
191    int maxfd = 0;
192
193    while (true) {
194
195        /* build select sets */
196        FD_ZERO(&readfds);
197        FD_ZERO(&writefds);
198
199        FD_SET(serversocket, &readfds);
200        maxfd = MAX(maxfd, serversocket);
201
202        for (client = clients; client != NULL; client = client->next) {
203            if (client->state == READING) {
204                FD_SET(client->socket, &readfds);
205            } else if (client->state == WRITING) {
206                FD_SET(client->socket, &writefds);
207            }
208            maxfd = MAX(maxfd, client->socket);
209        }
210        ret = select(maxfd + 1, &readfds, &writefds, NULL, NULL);
211        assert(ret > 0);
212
213        /* new client */
214        if (FD_ISSET(serversocket, &readfds)) {
215            handle_new_client(serversocket);
216        }
217
218        /* process clients */
219        for (client = clients; client != NULL; client = client->next) {
220            if (client->state == READING || client->state == WRITING) {
221                if (FD_ISSET(client->socket, &readfds)) {
222                    handle_client_read(client);
223                }
224                if (FD_ISSET(client->socket, &writefds)) {
225                    handle_client_write(client);
226                }
227            } else {
228                /* TODO remove finished clients from linked list. */
229            }
230        }
231    }
232}
233
234int main(int argc, char *argv[])
235{
236    uint16_t port = 0;
237    int serversocket = 0;
238#ifdef BARRELFISH
239    struct thread *t;
240#endif /* BARRELFISH */
241
242    // Parse command line arguments
243    for (int i = 0; i < argc; i++) {
244        if (strncmp(argv[i], "port=", strlen("port=")) == 0) {
245            port = atoi(argv[i] + strlen("port="));
246            printf("port=%" PRIu16 "\n", port);
247        }
248    }
249
250    // Set default values
251    if (port == 0) {
252        port = DEFAULT_PORT;
253    }
254
255#ifdef BARRELFISH
256    /*
257     * Start the main lwIP thread, that has exclusive access to the lwip core
258     * functions. Other threads communicate with this thread using message
259     * boxes.
260     *
261     * Note that tcpip_init() calls lwip_init_auto().
262     */
263    tcpip_init(NULL, NULL);
264    lwip_socket_init();
265
266    t = thread_create(poll_loop, NULL);
267#endif /* BARRELFISH */
268
269    start_server(port, &serversocket);
270    printf("Using server socket: %d\n", serversocket);
271    server_accept_loop(serversocket);
272
273    return EXIT_SUCCESS;
274}
275