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