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