1 /* Unix socket Echo server
2 * Copyright (C) 2001 Mark Ferlatte
3 * Adapted from echoserver.c, Copyright (C) 2000 David Helder
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301 USA
19 */
20
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/types.h>
26 #include <unistd.h>
27 #include <signal.h>
28 #include <glib.h>
29 #include <gnet.h>
30
31 typedef enum { NORMAL, ASYNC} ServerType;
32
33 void cleanup_on_sig(int signum);
34
35 static void usage(int status);
36 static void normal_echoserver(gchar *path, gboolean abstract);
37 static void async_echoserver(gchar *path, gboolean abstract);
38
39 GUnixSocket *server;
40
41 int
main(int argc,char ** argv)42 main(int argc, char** argv)
43 {
44 gchar *path = NULL;
45 ServerType server_type = NORMAL;
46 gboolean abstract = FALSE;
47
48 gnet_init ();
49
50 if (argc < 2) {
51 usage(EXIT_FAILURE);
52 }
53 if (argc > 2) {
54 if (strcmp(argv[1], "--async") == 0 || strcmp(argv[2], "--async") == 0)
55 server_type = ASYNC;
56 if (strcmp(argv[1], "--abstract") == 0 ||
57 strcmp(argv[2], "--abstract") == 0)
58 abstract = TRUE;
59 }
60
61 path = g_strdup (argv[argc - 1]);
62
63 signal(SIGINT, cleanup_on_sig);
64 signal(SIGTERM, cleanup_on_sig);
65
66 switch (server_type) {
67 case NORMAL:
68 g_print("Normal echo server running\n");
69 normal_echoserver(path, abstract);
70 break;
71 case ASYNC:
72 g_print("Async echo server running\n");
73 async_echoserver(path, abstract);
74 break;
75 default:
76 g_assert_not_reached();
77 }
78 return 0;
79 }
80
81 static void
usage(int status)82 usage(int status)
83 {
84 g_print("usage: echoserver-unix [(nothing)|--async| --abstract] <path>\n");
85 exit(status);
86 }
87
88 void
cleanup_on_sig(int signum)89 cleanup_on_sig(int signum)
90 {
91 gnet_unix_socket_delete(server);
92 exit(EXIT_SUCCESS);
93 }
94
95 static void
normal_echoserver(gchar * path,gboolean abstract)96 normal_echoserver(gchar *path, gboolean abstract)
97 {
98 GUnixSocket *client = NULL;
99 gchar buffer[1024];
100 gsize n;
101 GIOChannel *ioclient = NULL;
102 GIOError e;
103
104 g_assert(path != NULL);
105 /* Create the server */
106 if (abstract)
107 server = gnet_unix_socket_server_new_abstract(path);
108 else
109 server = gnet_unix_socket_server_new(path);
110 g_assert(server != NULL);
111
112 while ((client = gnet_unix_socket_server_accept(server)) != NULL) {
113 ioclient = gnet_unix_socket_get_io_channel(client);
114 g_assert(ioclient != NULL);
115
116 while ((e = gnet_io_channel_readline(ioclient, buffer,
117 sizeof(buffer), &n))
118 == G_IO_ERROR_NONE && (n > 0)) {
119 e = gnet_io_channel_writen(ioclient, buffer, n, &n);
120 if (e != G_IO_ERROR_NONE)
121 break;
122 fwrite(buffer, n, 1, stdout);
123 }
124 if (e != G_IO_ERROR_NONE)
125 fprintf(stderr,
126 "\nRecieved error %d (closing socket).\n",
127 e);
128 gnet_unix_socket_delete (client);
129 }
130 }
131
132 typedef struct
133 {
134 GUnixSocket *socket;
135 guint out_watch;
136 gchar buffer[1024];
137 guint n;
138 } client_state;
139
140 static void clientstate_delete(client_state *state);
141
142 static gboolean async_server_iofunc(GIOChannel *server,
143 GIOCondition c, gpointer data);
144 static gboolean async_client_iofunc(GIOChannel *client,
145 GIOCondition c, gpointer data);
146
147 static void
clientstate_delete(client_state * state)148 clientstate_delete(client_state *state)
149 {
150 if (state->out_watch)
151 g_source_remove(state->out_watch);
152 gnet_unix_socket_delete(state->socket);
153 g_free(state);
154 }
155
156 static void
async_echoserver(gchar * path,gboolean abstract)157 async_echoserver(gchar *path, gboolean abstract)
158 {
159 GIOChannel *iochannel = NULL;
160 GMainLoop *main_loop = NULL;
161
162 g_assert(path != NULL);
163 if (abstract)
164 server = gnet_unix_socket_server_new_abstract(path);
165 else
166 server = gnet_unix_socket_server_new(path);
167 g_assert(server != NULL);
168
169 main_loop = g_main_new(FALSE);
170
171 /* Add a watch for incoming clients */
172 iochannel = gnet_unix_socket_get_io_channel(server);
173 g_io_add_watch(iochannel,
174 G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
175 async_server_iofunc, server);
176
177 /* Start the main loop */
178 g_main_run(main_loop);
179 }
180
181 static gboolean
async_server_iofunc(GIOChannel * iochannel,GIOCondition c,gpointer data)182 async_server_iofunc(GIOChannel *iochannel, GIOCondition c, gpointer data)
183 {
184 GUnixSocket *server = (GUnixSocket *) data;
185 g_assert(server != NULL);
186
187 if (c & G_IO_IN) {
188 GUnixSocket *client = NULL;
189 GIOChannel *client_iochannel = NULL;
190 client_state *cs = NULL;
191
192 client = gnet_unix_socket_server_accept(server);
193 g_assert(client != NULL);
194
195 client_iochannel = gnet_unix_socket_get_io_channel(client);
196 g_assert(client_iochannel != NULL);
197
198 cs = g_new0(client_state, 1);
199 cs->socket = client;
200
201 g_io_add_watch(client_iochannel,
202 G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
203 async_client_iofunc, cs);
204 } else {
205 fprintf(stderr, "Server error %d\n", c);
206 return FALSE;
207 }
208 return TRUE;
209 }
210
211 /*
212
213 This callback is called for (IN|ERR) or OUT.
214
215 We add the watch for (IN|ERR) when the client connects in
216 async_server_iofunc(). We remove it if the connection is closed
217 (i.e. we read 0 bytes) or if there's an error.
218
219 We add the watch for OUT when we have something to write and remove
220 it when we're done writing or when the connection is closed.
221 */
222 static gboolean
async_client_iofunc(GIOChannel * iochannel,GIOCondition c,gpointer data)223 async_client_iofunc(GIOChannel *iochannel, GIOCondition c,
224 gpointer data)
225 {
226 client_state *cs = (client_state *) data;
227
228 g_assert(cs != NULL);
229
230 /* Check for socket error */
231 if (c & G_IO_ERR) {
232 fprintf(stderr, "Client socket error!\n");
233 goto error;
234 }
235 /* Check for data to be read (or if the socket was closed) */
236 if (c & G_IO_IN) {
237 GIOError e;
238 gsize bytes_read;
239
240 /* Read the data into our buffer */
241 e = g_io_channel_read(iochannel,
242 &cs->buffer[cs->n],
243 sizeof(cs->buffer) - cs->n,
244 &bytes_read);
245 /* Check for socket error */
246 if (e != G_IO_ERROR_NONE) {
247 fprintf(stderr, "Client read error: %d\n", e);
248 goto error;
249 } else if (bytes_read == 0) {
250 /* Check if we read 0 bytes, which means the connection
251 is closed */
252 goto error;
253 } else {
254 g_assert(bytes_read > 0);
255 /* If there isn't an out_watch, add one */
256 if (cs->out_watch == 0) {
257 cs->out_watch =
258 g_io_add_watch(iochannel,
259 G_IO_OUT,
260 async_client_iofunc,
261 cs);
262 }
263 fwrite(&cs->buffer[cs->n], bytes_read, 1, stdout);
264 cs->n += bytes_read;
265 }
266 }
267 if (c & G_IO_OUT) {
268 GIOError e;
269 gsize bytes_written;
270 /* Write the data out */
271 e = g_io_channel_write(iochannel, cs->buffer, cs->n,
272 &bytes_written);
273 if (e != G_IO_ERROR_NONE) {
274 fprintf(stderr, "Client write error: %d\n", e);
275 clientstate_delete(cs);
276 return FALSE;
277 } else if (bytes_written > 0) {
278 /* Move the memory down some (you wouldn't do this
279 in a performance server because it's slow) */
280 g_memmove(cs->buffer,
281 &cs->buffer[bytes_written],
282 bytes_written);
283 cs->n -= bytes_written;
284 }
285 /* Check if done */
286 if (cs->n == 0) {
287 cs->out_watch = 0;
288 return FALSE;
289 }
290 }
291 return TRUE;
292
293 error:
294 clientstate_delete(cs);
295 return FALSE;
296 }
297