2003-04-24 Havoc Pennington <hp@redhat.com>
[platform/upstream/dbus.git] / dbus / dbus-server-unix.c
index 277e00a..fe4dbaa 100644 (file)
@@ -1,7 +1,7 @@
 /* -*- mode: C; c-file-style: "gnu" -*- */
 /* dbus-server-unix.c Server implementation for Unix network protocols.
  *
- * Copyright (C) 2002  Red Hat Inc.
+ * Copyright (C) 2002, 2003  Red Hat Inc.
  *
  * Licensed under the Academic Free License version 1.2
  * 
 #include "dbus-connection-internal.h"
 #include <sys/types.h>
 #include <unistd.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <errno.h>
-#include <fcntl.h>
 
 /**
  * @defgroup DBusServerUnix DBusServer implementations for UNIX
@@ -51,25 +47,34 @@ typedef struct DBusServerUnix DBusServerUnix;
  */
 struct DBusServerUnix
 {
-  DBusServer base;  /**< Parent class members. */
-  int fd;           /**< File descriptor or -1 if disconnected. */
-  DBusWatch *watch; /**< File descriptor watch. */
+  DBusServer base;   /**< Parent class members. */
+  int fd;            /**< File descriptor or -1 if disconnected. */
+  DBusWatch *watch;  /**< File descriptor watch. */
+  char *socket_name; /**< Name of domain socket, to unlink if appropriate */
 };
 
 static void
 unix_finalize (DBusServer *server)
 {
   DBusServerUnix *unix_server = (DBusServerUnix*) server;
-  
-  _dbus_server_finalize_base (server);
 
   if (unix_server->watch)
     _dbus_watch_unref (unix_server->watch);
+
+  dbus_free (unix_server->socket_name);
+  
+  _dbus_server_finalize_base (server);
   
   dbus_free (server);
 }
 
-static void
+/**
+ * @todo unreffing the connection at the end may cause
+ * us to drop the last ref to the connection before
+ * disconnecting it. That is invalid.
+ */
+/* Return value is just for memory, not other failures. */
+static dbus_bool_t
 handle_new_client_fd (DBusServer *server,
                       int         client_fd)
 {
@@ -79,15 +84,22 @@ handle_new_client_fd (DBusServer *server,
   _dbus_verbose ("Creating new client connection with fd %d\n", client_fd);
           
   if (!_dbus_set_fd_nonblocking (client_fd, NULL))
-    return;
+    return TRUE;
   
-  transport = _dbus_transport_new_for_fd (client_fd);
+  transport = _dbus_transport_new_for_fd (client_fd, TRUE, NULL);
   if (transport == NULL)
     {
       close (client_fd);
-      return;
+      return FALSE;
     }
 
+  if (!_dbus_transport_set_auth_mechanisms (transport,
+                                            (const char **) server->auth_mechanisms))
+    {
+      _dbus_transport_unref (transport);
+      return FALSE;
+    }
+  
   /* note that client_fd is now owned by the transport, and will be
    * closed on transport disconnection/finalization
    */
@@ -96,8 +108,8 @@ handle_new_client_fd (DBusServer *server,
   _dbus_transport_unref (transport);
   
   if (connection == NULL)
-    return;
-
+    return FALSE;
+  
   /* See if someone wants to handle this new connection,
    * self-referencing for paranoia
    */
@@ -112,14 +124,17 @@ handle_new_client_fd (DBusServer *server,
   
   /* If no one grabbed a reference, the connection will die. */
   dbus_connection_unref (connection);
+
+  return TRUE;
 }
 
-static void
-unix_handle_watch (DBusServer  *server,
-                   DBusWatch   *watch,
-                   unsigned int flags)
+static dbus_bool_t
+unix_handle_watch (DBusWatch    *watch,
+                   unsigned int  flags,
+                   void         *data)
 {
-  DBusServerUnix *unix_server = (DBusServerUnix*) server;
+  DBusServer *server = data;
+  DBusServerUnix *unix_server = data;
 
   _dbus_assert (watch == unix_server->watch);
 
@@ -132,14 +147,13 @@ unix_handle_watch (DBusServer  *server,
       
       listen_fd = dbus_watch_get_fd (watch);
 
-    retry:
-      client_fd = accept (listen_fd, NULL, NULL);
+      client_fd = _dbus_accept (listen_fd);
       
       if (client_fd < 0)
         {
-          if (errno == EINTR)
-            goto retry;
-          else if (errno == EAGAIN || errno == EWOULDBLOCK)
+          /* EINTR handled for us */
+          
+          if (errno == EAGAIN || errno == EWOULDBLOCK)
             _dbus_verbose ("No client available to accept after all\n");
           else
             _dbus_verbose ("Failed to accept a client connection: %s\n",
@@ -147,7 +161,10 @@ unix_handle_watch (DBusServer  *server,
         }
       else
         {
-          handle_new_client_fd (server, client_fd);
+         _dbus_fd_set_close_on_exec (client_fd);         
+
+          if (!handle_new_client_fd (server, client_fd))
+            _dbus_verbose ("Rejected client connection due to lack of memory\n");
         }
     }
 
@@ -156,6 +173,8 @@ unix_handle_watch (DBusServer  *server,
 
   if (flags & DBUS_WATCH_HANGUP)
     _dbus_verbose ("Hangup on server listening socket\n");
+
+  return TRUE;
 }
   
 static void
@@ -173,11 +192,17 @@ unix_disconnect (DBusServer *server)
   
   close (unix_server->fd);
   unix_server->fd = -1;
+
+  if (unix_server->socket_name != NULL)
+    {
+      DBusString tmp;
+      _dbus_string_init_const (&tmp, unix_server->socket_name);
+      _dbus_delete_file (&tmp, NULL);
+    }
 }
 
 static DBusServerVTable unix_vtable = {
   unix_finalize,
-  unix_handle_watch,
   unix_disconnect
 };
 
@@ -190,29 +215,34 @@ static DBusServerVTable unix_vtable = {
  * accept new client connections.
  *
  * @param fd the file descriptor.
+ * @param address the server's address
  * @returns the new server, or #NULL if no memory.
  * 
  */
 DBusServer*
-_dbus_server_new_for_fd (int fd)
+_dbus_server_new_for_fd (int               fd,
+                         const DBusString *address)
 {
   DBusServerUnix *unix_server;
   DBusWatch *watch;
-
-  watch = _dbus_watch_new (fd,
-                           DBUS_WATCH_READABLE);
-  if (watch == NULL)
-    return NULL;
   
   unix_server = dbus_new0 (DBusServerUnix, 1);
   if (unix_server == NULL)
+    return NULL;
+
+  watch = _dbus_watch_new (fd,
+                           DBUS_WATCH_READABLE,
+                           TRUE,
+                           unix_handle_watch, unix_server,
+                           NULL);
+  if (watch == NULL)
     {
-      _dbus_watch_unref (watch);
+      dbus_free (unix_server);
       return NULL;
     }
   
   if (!_dbus_server_init_base (&unix_server->base,
-                               &unix_vtable))
+                               &unix_vtable, address))
     {
       _dbus_watch_unref (watch);
       dbus_free (unix_server);
@@ -238,65 +268,133 @@ _dbus_server_new_for_fd (int fd)
  * Creates a new server listening on the given Unix domain socket.
  *
  * @param path the path for the domain socket.
- * @param result location to store reason for failure.
+ * @param error location to store reason for failure.
  * @returns the new server, or #NULL on failure.
  */
 DBusServer*
 _dbus_server_new_for_domain_socket (const char     *path,
-                                    DBusResultCode *result)
+                                    DBusError      *error)
 {
   DBusServer *server;
+  DBusServerUnix *unix_server;
   int listen_fd;
-  struct sockaddr_un addr;
-
-  listen_fd = socket (AF_LOCAL, SOCK_STREAM, 0);
+  DBusString address;
+  char *path_copy;
+  
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
 
-  if (listen_fd < 0)
+  if (!_dbus_string_init (&address))
     {
-      dbus_set_result (result, _dbus_result_from_errno (errno));
-      _dbus_verbose ("Failed to create socket \"%s\": %s\n",
-                     path, _dbus_strerror (errno));
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
       return NULL;
     }
 
-  if (!_dbus_set_fd_nonblocking (listen_fd, result))
+  if (!_dbus_string_append (&address, "unix:path=") ||
+      !_dbus_string_append (&address, path))
     {
-      close (listen_fd);
-      return NULL;
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      goto failed_0;
     }
 
-  _DBUS_ZERO (addr);
-  addr.sun_family = AF_LOCAL;
-  strncpy (addr.sun_path, path, _DBUS_MAX_SUN_PATH_LENGTH);
-  addr.sun_path[_DBUS_MAX_SUN_PATH_LENGTH] = '\0';
+  path_copy = _dbus_strdup (path);
+  if (path_copy == NULL)
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      goto failed_0;
+    }
+  
+  listen_fd = _dbus_listen_unix_socket (path, error);
+  _dbus_fd_set_close_on_exec (listen_fd);
   
-  if (bind (listen_fd, (struct sockaddr*) &addr, SUN_LEN (&addr)) < 0)
+  if (listen_fd < 0)
     {
-      dbus_set_result (result, _dbus_result_from_errno (errno));
-      _dbus_verbose ("Failed to bind socket \"%s\": %s\n",
-                     path, _dbus_strerror (errno));
-      close (listen_fd);
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      goto failed_1;
+    }
+  
+  server = _dbus_server_new_for_fd (listen_fd, &address);
+  if (server == NULL)
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      goto failed_2;
+    }
+
+  unix_server = (DBusServerUnix*) server;
+  unix_server->socket_name = path_copy;
+  
+  _dbus_string_free (&address);
+  
+  return server;
+
+ failed_2:
+  _dbus_close (listen_fd, NULL);
+ failed_1:
+  dbus_free (path_copy);
+ failed_0:
+  _dbus_string_free (&address);
+
+  return NULL;
+}
+
+/**
+ * Creates a new server listening on the given hostname and port.
+ * If the hostname is NULL, listens on localhost.
+ *
+ * @param host the hostname to listen on.
+ * @param port the port to listen on.
+ * @param error location to store reason for failure.
+ * @returns the new server, or #NULL on failure.
+ */
+DBusServer*
+_dbus_server_new_for_tcp_socket (const char     *host,
+                                 dbus_uint32_t   port,
+                                 DBusError      *error)
+{
+  DBusServer *server;
+  int listen_fd;
+  DBusString address;
+  
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+  if (!_dbus_string_init (&address))
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
       return NULL;
     }
 
-  if (listen (listen_fd, 30 /* backlog */) < 0)
+  if (!_dbus_string_append (&address, "tcp:host=") ||
+      !_dbus_string_append (&address, host) ||
+      !_dbus_string_append (&address, ",port=") ||
+      !_dbus_string_append_int (&address, port))
     {
-      dbus_set_result (result, _dbus_result_from_errno (errno));      
-      _dbus_verbose ("Failed to listen on socket \"%s\": %s\n",
-                     path, _dbus_strerror (errno));
-      close (listen_fd);
+      _dbus_string_free (&address);
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
       return NULL;
     }
   
-  server = _dbus_server_new_for_fd (listen_fd);
+  listen_fd = _dbus_listen_tcp_socket (host, port, error);
+  _dbus_fd_set_close_on_exec (listen_fd);
+  
+  if (listen_fd < 0)
+    {
+      _dbus_string_free (&address);
+      return NULL;
+    }
+  
+  server = _dbus_server_new_for_fd (listen_fd, &address);
   if (server == NULL)
     {
-      dbus_set_result (result, DBUS_RESULT_NO_MEMORY);
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
       close (listen_fd);
+      _dbus_string_free (&address);
       return NULL;
     }
 
+  _dbus_string_free (&address);
+  
   return server;
+
+
 }
 
 /** @} */