1 | /* GIO - GLib Input, Output and Streaming Library |
2 | * |
3 | * Copyright © 2009 Codethink Limited |
4 | * Copyright © 2009 Red Hat, Inc |
5 | * |
6 | * This library is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Lesser General Public |
8 | * License as published by the Free Software Foundation; either |
9 | * version 2.1 of the License, or (at your option) any later version. |
10 | * |
11 | * This library is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | * Lesser General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Lesser General |
17 | * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. |
18 | * |
19 | * Authors: Ryan Lortie <desrt@desrt.ca> |
20 | * Alexander Larsson <alexl@redhat.com> |
21 | */ |
22 | |
23 | /** |
24 | * SECTION:gsocketservice |
25 | * @title: GSocketService |
26 | * @short_description: Make it easy to implement a network service |
27 | * @include: gio/gio.h |
28 | * @see_also: #GThreadedSocketService, #GSocketListener. |
29 | * |
30 | * A #GSocketService is an object that represents a service that |
31 | * is provided to the network or over local sockets. When a new |
32 | * connection is made to the service the #GSocketService::incoming |
33 | * signal is emitted. |
34 | * |
35 | * A #GSocketService is a subclass of #GSocketListener and you need |
36 | * to add the addresses you want to accept connections on with the |
37 | * #GSocketListener APIs. |
38 | * |
39 | * There are two options for implementing a network service based on |
40 | * #GSocketService. The first is to create the service using |
41 | * g_socket_service_new() and to connect to the #GSocketService::incoming |
42 | * signal. The second is to subclass #GSocketService and override the |
43 | * default signal handler implementation. |
44 | * |
45 | * In either case, the handler must immediately return, or else it |
46 | * will block additional incoming connections from being serviced. |
47 | * If you are interested in writing connection handlers that contain |
48 | * blocking code then see #GThreadedSocketService. |
49 | * |
50 | * The socket service runs on the main loop of the |
51 | * [thread-default context][g-main-context-push-thread-default-context] |
52 | * of the thread it is created in, and is not |
53 | * threadsafe in general. However, the calls to start and stop the |
54 | * service are thread-safe so these can be used from threads that |
55 | * handle incoming clients. |
56 | * |
57 | * Since: 2.22 |
58 | */ |
59 | |
60 | #include "config.h" |
61 | #include "gsocketservice.h" |
62 | |
63 | #include <gio/gio.h> |
64 | #include "gsocketlistener.h" |
65 | #include "gsocketconnection.h" |
66 | #include "glibintl.h" |
67 | #include "gmarshal-internal.h" |
68 | |
69 | struct _GSocketServicePrivate |
70 | { |
71 | GCancellable *cancellable; |
72 | guint active : 1; |
73 | guint outstanding_accept : 1; |
74 | }; |
75 | |
76 | static guint g_socket_service_incoming_signal; |
77 | |
78 | G_LOCK_DEFINE_STATIC(active); |
79 | |
80 | G_DEFINE_TYPE_WITH_PRIVATE (GSocketService, g_socket_service, G_TYPE_SOCKET_LISTENER) |
81 | |
82 | enum |
83 | { |
84 | PROP_0, |
85 | PROP_ACTIVE |
86 | }; |
87 | |
88 | static void g_socket_service_ready (GObject *object, |
89 | GAsyncResult *result, |
90 | gpointer user_data); |
91 | |
92 | static gboolean |
93 | g_socket_service_real_incoming (GSocketService *service, |
94 | GSocketConnection *connection, |
95 | GObject *source_object) |
96 | { |
97 | return FALSE; |
98 | } |
99 | |
100 | static void |
101 | g_socket_service_init (GSocketService *service) |
102 | { |
103 | service->priv = g_socket_service_get_instance_private (self: service); |
104 | service->priv->cancellable = g_cancellable_new (); |
105 | service->priv->active = TRUE; |
106 | } |
107 | |
108 | static void |
109 | g_socket_service_finalize (GObject *object) |
110 | { |
111 | GSocketService *service = G_SOCKET_SERVICE (object); |
112 | |
113 | g_object_unref (object: service->priv->cancellable); |
114 | |
115 | G_OBJECT_CLASS (g_socket_service_parent_class) |
116 | ->finalize (object); |
117 | } |
118 | |
119 | static void |
120 | do_accept (GSocketService *service) |
121 | { |
122 | g_socket_listener_accept_async (G_SOCKET_LISTENER (service), |
123 | cancellable: service->priv->cancellable, |
124 | callback: g_socket_service_ready, NULL); |
125 | service->priv->outstanding_accept = TRUE; |
126 | } |
127 | |
128 | static gboolean |
129 | get_active (GSocketService *service) |
130 | { |
131 | gboolean active; |
132 | |
133 | G_LOCK (active); |
134 | active = service->priv->active; |
135 | G_UNLOCK (active); |
136 | |
137 | return active; |
138 | } |
139 | |
140 | static void |
141 | set_active (GSocketService *service, gboolean active) |
142 | { |
143 | gboolean notify = FALSE; |
144 | |
145 | active = !!active; |
146 | |
147 | G_LOCK (active); |
148 | |
149 | if (active != service->priv->active) |
150 | { |
151 | service->priv->active = active; |
152 | notify = TRUE; |
153 | |
154 | if (active) |
155 | { |
156 | if (service->priv->outstanding_accept) |
157 | g_cancellable_cancel (cancellable: service->priv->cancellable); |
158 | else |
159 | do_accept (service); |
160 | } |
161 | else |
162 | { |
163 | if (service->priv->outstanding_accept) |
164 | g_cancellable_cancel (cancellable: service->priv->cancellable); |
165 | } |
166 | } |
167 | |
168 | G_UNLOCK (active); |
169 | |
170 | if (notify) |
171 | g_object_notify (G_OBJECT (service), property_name: "active" ); |
172 | } |
173 | |
174 | static void |
175 | g_socket_service_get_property (GObject *object, |
176 | guint prop_id, |
177 | GValue *value, |
178 | GParamSpec *pspec) |
179 | { |
180 | GSocketService *service = G_SOCKET_SERVICE (object); |
181 | |
182 | switch (prop_id) |
183 | { |
184 | case PROP_ACTIVE: |
185 | g_value_set_boolean (value, v_boolean: get_active (service)); |
186 | break; |
187 | default: |
188 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
189 | break; |
190 | } |
191 | } |
192 | |
193 | static void |
194 | g_socket_service_set_property (GObject *object, |
195 | guint prop_id, |
196 | const GValue *value, |
197 | GParamSpec *pspec) |
198 | { |
199 | GSocketService *service = G_SOCKET_SERVICE (object); |
200 | |
201 | switch (prop_id) |
202 | { |
203 | case PROP_ACTIVE: |
204 | set_active (service, active: g_value_get_boolean (value)); |
205 | break; |
206 | default: |
207 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
208 | break; |
209 | } |
210 | } |
211 | |
212 | static void |
213 | g_socket_service_changed (GSocketListener *listener) |
214 | { |
215 | GSocketService *service = G_SOCKET_SERVICE (listener); |
216 | |
217 | G_LOCK (active); |
218 | |
219 | if (service->priv->active) |
220 | { |
221 | if (service->priv->outstanding_accept) |
222 | g_cancellable_cancel (cancellable: service->priv->cancellable); |
223 | else |
224 | do_accept (service); |
225 | } |
226 | |
227 | G_UNLOCK (active); |
228 | } |
229 | |
230 | /** |
231 | * g_socket_service_is_active: |
232 | * @service: a #GSocketService |
233 | * |
234 | * Check whether the service is active or not. An active |
235 | * service will accept new clients that connect, while |
236 | * a non-active service will let connecting clients queue |
237 | * up until the service is started. |
238 | * |
239 | * Returns: %TRUE if the service is active, %FALSE otherwise |
240 | * |
241 | * Since: 2.22 |
242 | */ |
243 | gboolean |
244 | g_socket_service_is_active (GSocketService *service) |
245 | { |
246 | g_return_val_if_fail (G_IS_SOCKET_SERVICE (service), FALSE); |
247 | |
248 | return get_active (service); |
249 | } |
250 | |
251 | /** |
252 | * g_socket_service_start: |
253 | * @service: a #GSocketService |
254 | * |
255 | * Restarts the service, i.e. start accepting connections |
256 | * from the added sockets when the mainloop runs. This only needs |
257 | * to be called after the service has been stopped from |
258 | * g_socket_service_stop(). |
259 | * |
260 | * This call is thread-safe, so it may be called from a thread |
261 | * handling an incoming client request. |
262 | * |
263 | * Since: 2.22 |
264 | */ |
265 | void |
266 | g_socket_service_start (GSocketService *service) |
267 | { |
268 | g_return_if_fail (G_IS_SOCKET_SERVICE (service)); |
269 | |
270 | set_active (service, TRUE); |
271 | } |
272 | |
273 | /** |
274 | * g_socket_service_stop: |
275 | * @service: a #GSocketService |
276 | * |
277 | * Stops the service, i.e. stops accepting connections |
278 | * from the added sockets when the mainloop runs. |
279 | * |
280 | * This call is thread-safe, so it may be called from a thread |
281 | * handling an incoming client request. |
282 | * |
283 | * Note that this only stops accepting new connections; it does not |
284 | * close the listening sockets, and you can call |
285 | * g_socket_service_start() again later to begin listening again. To |
286 | * close the listening sockets, call g_socket_listener_close(). (This |
287 | * will happen automatically when the #GSocketService is finalized.) |
288 | * |
289 | * This must be called before calling g_socket_listener_close() as |
290 | * the socket service will start accepting connections immediately |
291 | * when a new socket is added. |
292 | * |
293 | * Since: 2.22 |
294 | */ |
295 | void |
296 | g_socket_service_stop (GSocketService *service) |
297 | { |
298 | g_return_if_fail (G_IS_SOCKET_SERVICE (service)); |
299 | |
300 | set_active (service, FALSE); |
301 | } |
302 | |
303 | static gboolean |
304 | g_socket_service_incoming (GSocketService *service, |
305 | GSocketConnection *connection, |
306 | GObject *source_object) |
307 | { |
308 | gboolean result; |
309 | |
310 | g_signal_emit (instance: service, signal_id: g_socket_service_incoming_signal, |
311 | detail: 0, connection, source_object, &result); |
312 | return result; |
313 | } |
314 | |
315 | static void |
316 | g_socket_service_class_init (GSocketServiceClass *class) |
317 | { |
318 | GObjectClass *gobject_class = G_OBJECT_CLASS (class); |
319 | GSocketListenerClass *listener_class = G_SOCKET_LISTENER_CLASS (class); |
320 | |
321 | gobject_class->finalize = g_socket_service_finalize; |
322 | gobject_class->set_property = g_socket_service_set_property; |
323 | gobject_class->get_property = g_socket_service_get_property; |
324 | listener_class->changed = g_socket_service_changed; |
325 | class->incoming = g_socket_service_real_incoming; |
326 | |
327 | /** |
328 | * GSocketService::incoming: |
329 | * @service: the #GSocketService |
330 | * @connection: a new #GSocketConnection object |
331 | * @source_object: (nullable): the source_object passed to |
332 | * g_socket_listener_add_address() |
333 | * |
334 | * The ::incoming signal is emitted when a new incoming connection |
335 | * to @service needs to be handled. The handler must initiate the |
336 | * handling of @connection, but may not block; in essence, |
337 | * asynchronous operations must be used. |
338 | * |
339 | * @connection will be unreffed once the signal handler returns, |
340 | * so you need to ref it yourself if you are planning to use it. |
341 | * |
342 | * Returns: %TRUE to stop other handlers from being called |
343 | * |
344 | * Since: 2.22 |
345 | */ |
346 | g_socket_service_incoming_signal = |
347 | g_signal_new (I_("incoming" ), G_TYPE_FROM_CLASS (class), signal_flags: G_SIGNAL_RUN_LAST, |
348 | G_STRUCT_OFFSET (GSocketServiceClass, incoming), |
349 | accumulator: g_signal_accumulator_true_handled, NULL, |
350 | c_marshaller: _g_cclosure_marshal_BOOLEAN__OBJECT_OBJECT, |
351 | G_TYPE_BOOLEAN, |
352 | n_params: 2, G_TYPE_SOCKET_CONNECTION, G_TYPE_OBJECT); |
353 | g_signal_set_va_marshaller (signal_id: g_socket_service_incoming_signal, |
354 | G_TYPE_FROM_CLASS (class), |
355 | va_marshaller: _g_cclosure_marshal_BOOLEAN__OBJECT_OBJECTv); |
356 | |
357 | /** |
358 | * GSocketService:active: |
359 | * |
360 | * Whether the service is currently accepting connections. |
361 | * |
362 | * Since: 2.46 |
363 | */ |
364 | g_object_class_install_property (oclass: gobject_class, property_id: PROP_ACTIVE, |
365 | pspec: g_param_spec_boolean (name: "active" , |
366 | P_("Active" ), |
367 | P_("Whether the service is currently accepting connections" ), |
368 | TRUE, |
369 | flags: G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
370 | } |
371 | |
372 | static void |
373 | g_socket_service_ready (GObject *object, |
374 | GAsyncResult *result, |
375 | gpointer user_data) |
376 | { |
377 | GSocketListener *listener = G_SOCKET_LISTENER (object); |
378 | GSocketService *service = G_SOCKET_SERVICE (object); |
379 | GSocketConnection *connection; |
380 | GObject *source_object; |
381 | GError *error = NULL; |
382 | |
383 | connection = g_socket_listener_accept_finish (listener, result, source_object: &source_object, error: &error); |
384 | if (error) |
385 | { |
386 | if (!g_error_matches (error, G_IO_ERROR, code: G_IO_ERROR_CANCELLED)) |
387 | g_warning ("fail: %s" , error->message); |
388 | g_error_free (error); |
389 | } |
390 | else |
391 | { |
392 | g_socket_service_incoming (service, connection, source_object); |
393 | g_object_unref (object: connection); |
394 | } |
395 | |
396 | G_LOCK (active); |
397 | |
398 | g_cancellable_reset (cancellable: service->priv->cancellable); |
399 | |
400 | /* requeue */ |
401 | service->priv->outstanding_accept = FALSE; |
402 | if (service->priv->active) |
403 | do_accept (service); |
404 | |
405 | G_UNLOCK (active); |
406 | } |
407 | |
408 | /** |
409 | * g_socket_service_new: |
410 | * |
411 | * Creates a new #GSocketService with no sockets to listen for. |
412 | * New listeners can be added with e.g. g_socket_listener_add_address() |
413 | * or g_socket_listener_add_inet_port(). |
414 | * |
415 | * New services are created active, there is no need to call |
416 | * g_socket_service_start(), unless g_socket_service_stop() has been |
417 | * called before. |
418 | * |
419 | * Returns: a new #GSocketService. |
420 | * |
421 | * Since: 2.22 |
422 | */ |
423 | GSocketService * |
424 | g_socket_service_new (void) |
425 | { |
426 | return g_object_new (G_TYPE_SOCKET_SERVICE, NULL); |
427 | } |
428 | |