1 | /* |
---|
2 | * linc-server.c: This file is part of the linc library. |
---|
3 | * |
---|
4 | * Authors: |
---|
5 | * Elliot Lee (sopwith@redhat.com) |
---|
6 | * Michael Meeks (michael@ximian.com) |
---|
7 | * Mark McLouglin (mark@skynet.ie) & others |
---|
8 | * |
---|
9 | * Copyright 2001, Red Hat, Inc., Ximian, Inc., |
---|
10 | * Sun Microsystems, Inc. |
---|
11 | */ |
---|
12 | #include <config.h> |
---|
13 | #include <stdio.h> |
---|
14 | #include <netdb.h> |
---|
15 | #include <unistd.h> |
---|
16 | #include <string.h> |
---|
17 | #include <fcntl.h> |
---|
18 | #include <errno.h> |
---|
19 | |
---|
20 | #include <linc/linc.h> |
---|
21 | #include <linc/linc-server.h> |
---|
22 | #include <linc/linc-connection.h> |
---|
23 | |
---|
24 | #include "linc-debug.h" |
---|
25 | #include "linc-private.h" |
---|
26 | #include "linc-compat.h" |
---|
27 | |
---|
28 | enum { |
---|
29 | NEW_CONNECTION, |
---|
30 | LAST_SIGNAL |
---|
31 | }; |
---|
32 | static guint server_signals [LAST_SIGNAL] = { 0 }; |
---|
33 | |
---|
34 | static GObjectClass *parent_class = NULL; |
---|
35 | |
---|
36 | static void |
---|
37 | my_cclosure_marshal_VOID__OBJECT (GClosure *closure, |
---|
38 | GValue *return_value, |
---|
39 | guint n_param_values, |
---|
40 | const GValue *param_values, |
---|
41 | gpointer invocation_hint, |
---|
42 | gpointer marshal_data) |
---|
43 | { |
---|
44 | typedef void (*GSignalFunc_VOID__OBJECT) (gpointer data1, |
---|
45 | GObject *arg_1, |
---|
46 | gpointer data2); |
---|
47 | register GSignalFunc_VOID__OBJECT callback; |
---|
48 | register GCClosure *cc = (GCClosure*) closure; |
---|
49 | register gpointer data1, data2; |
---|
50 | |
---|
51 | g_return_if_fail (n_param_values >= 2); |
---|
52 | |
---|
53 | if (G_CCLOSURE_SWAP_DATA (closure)) { |
---|
54 | data1 = closure->data; |
---|
55 | data2 = g_value_peek_pointer (param_values + 0); |
---|
56 | } else { |
---|
57 | data1 = g_value_peek_pointer (param_values + 0); |
---|
58 | data2 = closure->data; |
---|
59 | } |
---|
60 | callback = (GSignalFunc_VOID__OBJECT) ( |
---|
61 | marshal_data ? marshal_data : cc->callback); |
---|
62 | |
---|
63 | callback (data1, |
---|
64 | g_value_peek_pointer (param_values + 1), |
---|
65 | data2); |
---|
66 | } |
---|
67 | |
---|
68 | static void |
---|
69 | linc_server_init (LINCServer *cnx) |
---|
70 | { |
---|
71 | cnx->priv = g_new0 (LINCServerPrivate, 1); |
---|
72 | |
---|
73 | cnx->priv->mutex = linc_mutex_new (); |
---|
74 | cnx->priv->fd = -1; |
---|
75 | } |
---|
76 | |
---|
77 | static void |
---|
78 | linc_server_dispose (GObject *obj) |
---|
79 | { |
---|
80 | GSList *l; |
---|
81 | LINCServer *cnx = (LINCServer *) obj; |
---|
82 | |
---|
83 | d_printf ("Dispose / close server fd %d\n", cnx->priv->fd); |
---|
84 | |
---|
85 | #ifdef G_THREADS_ENABLED |
---|
86 | if (cnx->priv->mutex) { |
---|
87 | g_mutex_free (cnx->priv->mutex); |
---|
88 | cnx->priv->mutex = NULL; |
---|
89 | } |
---|
90 | #endif |
---|
91 | if (cnx->priv->tag) { |
---|
92 | LincWatch *thewatch = cnx->priv->tag; |
---|
93 | cnx->priv->tag = NULL; |
---|
94 | linc_io_remove_watch (thewatch); |
---|
95 | } |
---|
96 | |
---|
97 | linc_protocol_destroy_cnx (cnx->proto, |
---|
98 | cnx->priv->fd, |
---|
99 | cnx->local_host_info, |
---|
100 | cnx->local_serv_info); |
---|
101 | cnx->priv->fd = -1; |
---|
102 | |
---|
103 | while ((l = cnx->priv->connections)) { |
---|
104 | GObject *o = l->data; |
---|
105 | |
---|
106 | cnx->priv->connections = l->next; |
---|
107 | g_slist_free_1 (l); |
---|
108 | g_object_unref (o); |
---|
109 | } |
---|
110 | |
---|
111 | parent_class->dispose (obj); |
---|
112 | } |
---|
113 | |
---|
114 | static void |
---|
115 | linc_server_finalize (GObject *obj) |
---|
116 | { |
---|
117 | LINCServer *cnx = (LINCServer *)obj; |
---|
118 | |
---|
119 | g_free (cnx->local_host_info); |
---|
120 | g_free (cnx->local_serv_info); |
---|
121 | |
---|
122 | g_free (cnx->priv); |
---|
123 | |
---|
124 | parent_class->finalize (obj); |
---|
125 | } |
---|
126 | |
---|
127 | static LINCConnection * |
---|
128 | linc_server_create_connection (LINCServer *cnx) |
---|
129 | { |
---|
130 | return g_object_new (linc_connection_get_type (), NULL); |
---|
131 | } |
---|
132 | |
---|
133 | static gboolean |
---|
134 | linc_server_accept_connection (LINCServer *server, |
---|
135 | LINCConnection **connection) |
---|
136 | { |
---|
137 | LINCServerClass *klass; |
---|
138 | struct sockaddr *saddr; |
---|
139 | int addrlen, fd; |
---|
140 | |
---|
141 | g_return_val_if_fail (connection != NULL, FALSE); |
---|
142 | |
---|
143 | *connection = NULL; |
---|
144 | |
---|
145 | addrlen = server->proto->addr_len; |
---|
146 | saddr = g_alloca (addrlen); |
---|
147 | |
---|
148 | fd = accept (server->priv->fd, saddr, &addrlen); |
---|
149 | if (fd < 0) { |
---|
150 | d_printf ("accept on %d failed %d", server->priv->fd, errno); |
---|
151 | return FALSE; /* error */ |
---|
152 | } |
---|
153 | |
---|
154 | if (server->create_options & LINC_CONNECTION_LOCAL_ONLY && |
---|
155 | !linc_protocol_is_local (server->proto, saddr, addrlen)) { |
---|
156 | LINC_CLOSE (fd); |
---|
157 | return FALSE; |
---|
158 | } |
---|
159 | |
---|
160 | if (server->create_options & LINC_CONNECTION_NONBLOCKING) |
---|
161 | if (fcntl (fd, F_SETFL, O_NONBLOCK) < 0) { |
---|
162 | d_printf ("failed to set O_NONBLOCK on %d", fd); |
---|
163 | LINC_CLOSE (fd); |
---|
164 | return FALSE; |
---|
165 | } |
---|
166 | |
---|
167 | if (fcntl (fd, F_SETFD, FD_CLOEXEC) < 0) { |
---|
168 | d_printf ("failed to set cloexec on %d", fd); |
---|
169 | LINC_CLOSE (fd); |
---|
170 | return FALSE; |
---|
171 | } |
---|
172 | |
---|
173 | klass = (LINCServerClass *) G_OBJECT_GET_CLASS (server); |
---|
174 | |
---|
175 | g_assert (klass->create_connection); |
---|
176 | *connection = klass->create_connection (server); |
---|
177 | |
---|
178 | g_return_val_if_fail (*connection != NULL, FALSE); |
---|
179 | |
---|
180 | d_printf ("accepted a new connection (%d) on server %d\n", |
---|
181 | fd, server->priv->fd); |
---|
182 | |
---|
183 | if (!linc_connection_from_fd ( |
---|
184 | *connection, fd, server->proto, NULL, NULL, |
---|
185 | FALSE, LINC_CONNECTED, server->create_options)) { |
---|
186 | |
---|
187 | g_object_unref (G_OBJECT (*connection)); |
---|
188 | *connection = NULL; |
---|
189 | LINC_CLOSE (fd); |
---|
190 | return FALSE; |
---|
191 | } |
---|
192 | |
---|
193 | server->priv->connections = g_slist_prepend ( |
---|
194 | server->priv->connections, *connection); |
---|
195 | |
---|
196 | return TRUE; |
---|
197 | } |
---|
198 | |
---|
199 | static gboolean |
---|
200 | linc_server_handle_io (GIOChannel *gioc, |
---|
201 | GIOCondition condition, |
---|
202 | gpointer data) |
---|
203 | { |
---|
204 | gboolean accepted; |
---|
205 | LINCServer *server = data; |
---|
206 | LINCConnection *connection = NULL; |
---|
207 | |
---|
208 | if (!(condition & LINC_IN_CONDS)) |
---|
209 | g_error ("error condition on server fd is %#x", condition); |
---|
210 | |
---|
211 | LINC_MUTEX_LOCK (server->priv->mutex); |
---|
212 | |
---|
213 | accepted = linc_server_accept_connection (server, &connection); |
---|
214 | |
---|
215 | LINC_MUTEX_UNLOCK (server->priv->mutex); |
---|
216 | |
---|
217 | if (!accepted) { |
---|
218 | GValue parms[2]; |
---|
219 | |
---|
220 | memset (parms, 0, sizeof (parms)); |
---|
221 | g_value_init (parms, G_OBJECT_TYPE (server)); |
---|
222 | g_value_set_object (parms, G_OBJECT (server)); |
---|
223 | g_value_init (parms + 1, G_TYPE_OBJECT); |
---|
224 | |
---|
225 | /* FIXME: this connection is always NULL */ |
---|
226 | g_value_set_object (parms + 1, (GObject *) connection); |
---|
227 | |
---|
228 | d_printf ("p %d, Non-accepted input on fd %d", |
---|
229 | getpid (), server->priv->fd); |
---|
230 | |
---|
231 | g_signal_emitv (parms, server_signals [NEW_CONNECTION], 0, NULL); |
---|
232 | |
---|
233 | g_value_unset (parms); |
---|
234 | g_value_unset (parms + 1); |
---|
235 | } |
---|
236 | |
---|
237 | return TRUE; |
---|
238 | } |
---|
239 | |
---|
240 | /** |
---|
241 | * linc_server_setup: |
---|
242 | * @cnx: the connection to setup |
---|
243 | * @proto_name: the protocol to use |
---|
244 | * @local_host_info: the local hsot |
---|
245 | * @local_serv_info: remote server info |
---|
246 | * @create_options: various create options |
---|
247 | * |
---|
248 | * Setup the server object. You should create a server object |
---|
249 | * via g_object_new and then set it up, using this method. |
---|
250 | * |
---|
251 | * Return value: the initialized server |
---|
252 | **/ |
---|
253 | gboolean |
---|
254 | linc_server_setup (LINCServer *cnx, |
---|
255 | const char *proto_name, |
---|
256 | const char *local_host_info, |
---|
257 | const char *local_serv_info, |
---|
258 | LINCConnectionOptions create_options) |
---|
259 | { |
---|
260 | const LINCProtocolInfo *proto; |
---|
261 | int fd, n; |
---|
262 | struct sockaddr *saddr; |
---|
263 | LincSockLen saddr_len; |
---|
264 | const char *local_host; |
---|
265 | char *service, *hostname; |
---|
266 | |
---|
267 | #if !LINC_SSL_SUPPORT |
---|
268 | if (create_options & LINC_CONNECTION_SSL) |
---|
269 | return FALSE; |
---|
270 | #endif |
---|
271 | |
---|
272 | proto = linc_protocol_find (proto_name); |
---|
273 | if (!proto) { |
---|
274 | d_printf ("Can't find proto '%s'\n", proto_name); |
---|
275 | return FALSE; |
---|
276 | } |
---|
277 | |
---|
278 | if (local_host_info) |
---|
279 | local_host = local_host_info; |
---|
280 | else |
---|
281 | local_host = linc_get_local_hostname (); |
---|
282 | |
---|
283 | address_in_use: |
---|
284 | |
---|
285 | saddr = linc_protocol_get_sockaddr ( |
---|
286 | proto, local_host, local_serv_info, &saddr_len); |
---|
287 | |
---|
288 | if (!saddr) { |
---|
289 | d_printf ("Can't get_sockaddr proto '%s' '%s'\n", |
---|
290 | local_host, local_serv_info ? local_serv_info : "(null)"); |
---|
291 | return FALSE; |
---|
292 | } |
---|
293 | |
---|
294 | fd = socket (proto->family, SOCK_STREAM, |
---|
295 | proto->stream_proto_num); |
---|
296 | if (fd < 0) { |
---|
297 | g_free (saddr); |
---|
298 | d_printf ("socket (%d, %d, %d) failed\n", |
---|
299 | proto->family, SOCK_STREAM, |
---|
300 | proto->stream_proto_num); |
---|
301 | return FALSE; |
---|
302 | } |
---|
303 | |
---|
304 | { |
---|
305 | static const int oneval = 1; |
---|
306 | |
---|
307 | setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &oneval, sizeof (oneval)); |
---|
308 | } |
---|
309 | |
---|
310 | n = 0; |
---|
311 | errno = 0; |
---|
312 | |
---|
313 | if ((proto->flags & LINC_PROTOCOL_NEEDS_BIND) || local_serv_info) |
---|
314 | n = bind (fd, saddr, saddr_len); |
---|
315 | |
---|
316 | if (n && errno == EADDRINUSE) { |
---|
317 | d_printf ("bind failed; retrying"); |
---|
318 | goto address_in_use; |
---|
319 | } |
---|
320 | |
---|
321 | if (!n) |
---|
322 | n = listen (fd, 10); |
---|
323 | else |
---|
324 | d_printf ("bind really failed errno: %d\n", errno); |
---|
325 | |
---|
326 | |
---|
327 | if (!n && |
---|
328 | create_options & LINC_CONNECTION_NONBLOCKING) |
---|
329 | n = fcntl (fd, F_SETFL, O_NONBLOCK); |
---|
330 | else |
---|
331 | d_printf ("listen failed errno: %d\n", errno); |
---|
332 | |
---|
333 | if (!n) |
---|
334 | n = fcntl (fd, F_SETFD, FD_CLOEXEC); |
---|
335 | else |
---|
336 | d_printf ("failed to set nonblock on %d", fd); |
---|
337 | |
---|
338 | if (!n) |
---|
339 | n = getsockname (fd, saddr, &saddr_len); |
---|
340 | else |
---|
341 | d_printf ("failed to set cloexec on %d", fd); |
---|
342 | |
---|
343 | if (n) { |
---|
344 | linc_protocol_destroy_addr (proto, fd, saddr); |
---|
345 | d_printf ("get_sockname failed errno: %d\n", errno); |
---|
346 | return FALSE; |
---|
347 | } |
---|
348 | |
---|
349 | if (!linc_protocol_get_sockinfo (proto, saddr, &hostname, &service)) { |
---|
350 | linc_protocol_destroy_addr (proto, fd, saddr); |
---|
351 | d_printf ("linc_getsockinfo failed.\n"); |
---|
352 | return FALSE; |
---|
353 | } |
---|
354 | |
---|
355 | g_free (saddr); |
---|
356 | |
---|
357 | cnx->proto = proto; |
---|
358 | cnx->priv->fd = fd; |
---|
359 | |
---|
360 | if (create_options & LINC_CONNECTION_NONBLOCKING) { |
---|
361 | g_assert (cnx->priv->tag == NULL); |
---|
362 | |
---|
363 | cnx->priv->tag = linc_io_add_watch_fd ( |
---|
364 | fd, LINC_IN_CONDS | LINC_ERR_CONDS, |
---|
365 | linc_server_handle_io, cnx); |
---|
366 | } |
---|
367 | |
---|
368 | cnx->create_options = create_options; |
---|
369 | |
---|
370 | if (local_host_info) { |
---|
371 | g_free (hostname); |
---|
372 | cnx->local_host_info = g_strdup (local_host_info); |
---|
373 | } else |
---|
374 | cnx->local_host_info = hostname; |
---|
375 | |
---|
376 | cnx->local_serv_info = service; |
---|
377 | |
---|
378 | d_printf ("Created a new server fd (%d) '%s', '%s', '%s'\n", |
---|
379 | fd, proto->name, |
---|
380 | hostname ? hostname : "<Null>", |
---|
381 | service ? service : "<Null>"); |
---|
382 | |
---|
383 | return TRUE; |
---|
384 | } |
---|
385 | |
---|
386 | static void |
---|
387 | linc_server_class_init (LINCServerClass *klass) |
---|
388 | { |
---|
389 | GType ptype; |
---|
390 | GClosure *closure; |
---|
391 | GObjectClass *object_class = (GObjectClass *) klass; |
---|
392 | |
---|
393 | object_class->dispose = linc_server_dispose; |
---|
394 | object_class->finalize = linc_server_finalize; |
---|
395 | klass->create_connection = linc_server_create_connection; |
---|
396 | |
---|
397 | parent_class = g_type_class_peek_parent (klass); |
---|
398 | closure = g_signal_type_cclosure_new ( |
---|
399 | G_OBJECT_CLASS_TYPE (klass), |
---|
400 | G_STRUCT_OFFSET (LINCServerClass, new_connection)); |
---|
401 | |
---|
402 | ptype = G_TYPE_OBJECT; |
---|
403 | server_signals [NEW_CONNECTION] = g_signal_newv ( |
---|
404 | "new_connection", |
---|
405 | G_OBJECT_CLASS_TYPE (klass), |
---|
406 | G_SIGNAL_RUN_LAST, closure, |
---|
407 | NULL, NULL, |
---|
408 | my_cclosure_marshal_VOID__OBJECT, |
---|
409 | G_TYPE_NONE, |
---|
410 | 1, &ptype); |
---|
411 | } |
---|
412 | |
---|
413 | GType |
---|
414 | linc_server_get_type (void) |
---|
415 | { |
---|
416 | static GType object_type = 0; |
---|
417 | |
---|
418 | if (!object_type) { |
---|
419 | static const GTypeInfo object_info = { |
---|
420 | sizeof (LINCServerClass), |
---|
421 | (GBaseInitFunc) NULL, |
---|
422 | (GBaseFinalizeFunc) NULL, |
---|
423 | (GClassInitFunc) linc_server_class_init, |
---|
424 | NULL, /* class_finalize */ |
---|
425 | NULL, /* class_data */ |
---|
426 | sizeof (LINCServer), |
---|
427 | 0, /* n_preallocs */ |
---|
428 | (GInstanceInitFunc) linc_server_init, |
---|
429 | }; |
---|
430 | |
---|
431 | object_type = g_type_register_static ( |
---|
432 | G_TYPE_OBJECT, "LINCServer", |
---|
433 | &object_info, 0); |
---|
434 | } |
---|
435 | |
---|
436 | return object_type; |
---|
437 | } |
---|