2005-02-26 Havoc Pennington <hp@redhat.com>
[platform/upstream/dbus.git] / dbus / dbus-server-unix.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-server-unix.c Server implementation for Unix network protocols.
3  *
4  * Copyright (C) 2002, 2003, 2004  Red Hat Inc.
5  *
6  * Licensed under the Academic Free License version 2.1
7  * 
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program 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
16  * GNU General Public License for more details.
17  * 
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23
24 #include "dbus-internals.h"
25 #include "dbus-server-unix.h"
26 #include "dbus-transport-unix.h"
27 #include "dbus-connection-internal.h"
28 #include "dbus-string.h"
29 #include <sys/types.h>
30 #include <unistd.h>
31
32 /**
33  * @defgroup DBusServerUnix DBusServer implementations for UNIX
34  * @ingroup  DBusInternals
35  * @brief Implementation details of DBusServer on UNIX
36  *
37  * @{
38  */
39 /**
40  * 
41  * Opaque object representing a Unix server implementation.
42  */
43 typedef struct DBusServerUnix DBusServerUnix;
44
45 /**
46  * Implementation details of DBusServerUnix. All members
47  * are private.
48  */
49 struct DBusServerUnix
50 {
51   DBusServer base;   /**< Parent class members. */
52   int fd;            /**< File descriptor or -1 if disconnected. */
53   DBusWatch *watch;  /**< File descriptor watch. */
54   char *socket_name; /**< Name of domain socket, to unlink if appropriate */
55 };
56
57 static void
58 unix_finalize (DBusServer *server)
59 {
60   DBusServerUnix *unix_server = (DBusServerUnix*) server;
61   
62   _dbus_server_finalize_base (server);
63
64   if (unix_server->watch)
65     {
66       _dbus_watch_unref (unix_server->watch);
67       unix_server->watch = NULL;
68     }
69   
70   dbus_free (unix_server->socket_name);
71   dbus_free (server);
72 }
73
74 /**
75  * @todo unreffing the connection at the end may cause
76  * us to drop the last ref to the connection before
77  * disconnecting it. That is invalid.
78  *
79  * @todo doesn't this leak a server refcount if
80  * new_connection_function is NULL?
81  */
82 /* Return value is just for memory, not other failures. */
83 static dbus_bool_t
84 handle_new_client_fd_and_unlock (DBusServer *server,
85                                  int         client_fd)
86 {
87   DBusConnection *connection;
88   DBusTransport *transport;
89   DBusNewConnectionFunction new_connection_function;
90   void *new_connection_data;
91   
92   _dbus_verbose ("Creating new client connection with fd %d\n", client_fd);
93
94   HAVE_LOCK_CHECK (server);
95   
96   if (!_dbus_set_fd_nonblocking (client_fd, NULL))
97     {
98       SERVER_UNLOCK (server);
99       return TRUE;
100     }
101   
102   transport = _dbus_transport_new_for_fd (client_fd, &server->guid_hex, NULL);
103   if (transport == NULL)
104     {
105       close (client_fd);
106       SERVER_UNLOCK (server);
107       return FALSE;
108     }
109
110   if (!_dbus_transport_set_auth_mechanisms (transport,
111                                             (const char **) server->auth_mechanisms))
112     {
113       _dbus_transport_unref (transport);
114       SERVER_UNLOCK (server);
115       return FALSE;
116     }
117   
118   /* note that client_fd is now owned by the transport, and will be
119    * closed on transport disconnection/finalization
120    */
121   
122   connection = _dbus_connection_new_for_transport (transport);
123   _dbus_transport_unref (transport);
124   transport = NULL; /* now under the connection lock */
125   
126   if (connection == NULL)
127     {
128       SERVER_UNLOCK (server);
129       return FALSE;
130     }
131   
132   /* See if someone wants to handle this new connection, self-referencing
133    * for paranoia.
134    */
135   new_connection_function = server->new_connection_function;
136   new_connection_data = server->new_connection_data;
137
138   _dbus_server_ref_unlocked (server);
139   SERVER_UNLOCK (server);
140   
141   if (new_connection_function)
142     {
143       (* new_connection_function) (server, connection,
144                                    new_connection_data);
145       dbus_server_unref (server);
146     }
147   
148   /* If no one grabbed a reference, the connection will die. */
149   dbus_connection_unref (connection);
150
151   return TRUE;
152 }
153
154 static dbus_bool_t
155 unix_handle_watch (DBusWatch    *watch,
156                    unsigned int  flags,
157                    void         *data)
158 {
159   DBusServer *server = data;
160   DBusServerUnix *unix_server = data;
161
162   SERVER_LOCK (server);
163   
164   _dbus_assert (watch == unix_server->watch);
165
166   _dbus_verbose ("Handling client connection, flags 0x%x\n", flags);
167   
168   if (flags & DBUS_WATCH_READABLE)
169     {
170       int client_fd;
171       int listen_fd;
172       
173       listen_fd = dbus_watch_get_fd (watch);
174
175       client_fd = _dbus_accept (listen_fd);
176       
177       if (client_fd < 0)
178         {
179           /* EINTR handled for us */
180           
181           if (errno == EAGAIN || errno == EWOULDBLOCK)
182             _dbus_verbose ("No client available to accept after all\n");
183           else
184             _dbus_verbose ("Failed to accept a client connection: %s\n",
185                            _dbus_strerror (errno));
186
187           SERVER_UNLOCK (server);
188         }
189       else
190         {
191           _dbus_fd_set_close_on_exec (client_fd);         
192
193           if (!handle_new_client_fd_and_unlock (server, client_fd))
194             _dbus_verbose ("Rejected client connection due to lack of memory\n");
195         }
196     }
197
198   if (flags & DBUS_WATCH_ERROR)
199     _dbus_verbose ("Error on server listening socket\n");
200
201   if (flags & DBUS_WATCH_HANGUP)
202     _dbus_verbose ("Hangup on server listening socket\n");
203
204   return TRUE;
205 }
206   
207 static void
208 unix_disconnect (DBusServer *server)
209 {
210   DBusServerUnix *unix_server = (DBusServerUnix*) server;
211
212   HAVE_LOCK_CHECK (server);
213   
214   if (unix_server->watch)
215     {
216       _dbus_server_remove_watch (server,
217                                  unix_server->watch);
218       _dbus_watch_unref (unix_server->watch);
219       unix_server->watch = NULL;
220     }
221   
222   close (unix_server->fd);
223   unix_server->fd = -1;
224
225   if (unix_server->socket_name != NULL)
226     {
227       DBusString tmp;
228       _dbus_string_init_const (&tmp, unix_server->socket_name);
229       _dbus_delete_file (&tmp, NULL);
230     }
231
232   HAVE_LOCK_CHECK (server);
233 }
234
235 static DBusServerVTable unix_vtable = {
236   unix_finalize,
237   unix_disconnect
238 };
239
240 /**
241  * Creates a new server listening on the given file descriptor.  The
242  * file descriptor should be nonblocking (use
243  * _dbus_set_fd_nonblocking() to make it so). The file descriptor
244  * should be listening for connections, that is, listen() should have
245  * been successfully invoked on it. The server will use accept() to
246  * accept new client connections.
247  *
248  * @param fd the file descriptor.
249  * @param address the server's address
250  * @returns the new server, or #NULL if no memory.
251  * 
252  */
253 DBusServer*
254 _dbus_server_new_for_fd (int               fd,
255                          const DBusString *address)
256 {
257   DBusServerUnix *unix_server;
258   DBusServer *server;
259   DBusWatch *watch;
260   
261   unix_server = dbus_new0 (DBusServerUnix, 1);
262   if (unix_server == NULL)
263     return NULL;
264   
265   watch = _dbus_watch_new (fd,
266                            DBUS_WATCH_READABLE,
267                            TRUE,
268                            unix_handle_watch, unix_server,
269                            NULL);
270   if (watch == NULL)
271     {
272       dbus_free (unix_server);
273       return NULL;
274     }
275   
276   if (!_dbus_server_init_base (&unix_server->base,
277                                &unix_vtable, address))
278     {
279       _dbus_watch_unref (watch);
280       dbus_free (unix_server);
281       return NULL;
282     }
283
284   server = (DBusServer*) unix_server;
285
286   SERVER_LOCK (server);
287   
288   if (!_dbus_server_add_watch (&unix_server->base,
289                                watch))
290     {
291       SERVER_UNLOCK (server);
292       _dbus_server_finalize_base (&unix_server->base);
293       _dbus_watch_unref (watch);
294       dbus_free (unix_server);
295       return NULL;
296     }
297   
298   unix_server->fd = fd;
299   unix_server->watch = watch;
300
301   SERVER_UNLOCK (server);
302   
303   return (DBusServer*) unix_server;
304 }
305
306 /**
307  * Creates a new server listening on the given Unix domain socket.
308  *
309  * @param path the path for the domain socket.
310  * @param abstract #TRUE to use abstract socket namespace
311  * @param error location to store reason for failure.
312  * @returns the new server, or #NULL on failure.
313  */
314 DBusServer*
315 _dbus_server_new_for_domain_socket (const char     *path,
316                                     dbus_bool_t     abstract,
317                                     DBusError      *error)
318 {
319   DBusServer *server;
320   DBusServerUnix *unix_server;
321   int listen_fd;
322   DBusString address;
323   char *path_copy;
324   DBusString path_str;
325   
326   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
327
328   if (!_dbus_string_init (&address))
329     {
330       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
331       return NULL;
332     }
333
334   _dbus_string_init_const (&path_str, path);
335   if ((abstract &&
336        !_dbus_string_append (&address, "unix:abstract=")) ||
337       (!abstract &&
338        !_dbus_string_append (&address, "unix:path=")) ||
339       !_dbus_address_append_escaped (&address, &path_str))
340     {
341       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
342       goto failed_0;
343     }
344
345   path_copy = _dbus_strdup (path);
346   if (path_copy == NULL)
347     {
348       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
349       goto failed_0;
350     }
351   
352   listen_fd = _dbus_listen_unix_socket (path, abstract, error);
353   _dbus_fd_set_close_on_exec (listen_fd);
354   
355   if (listen_fd < 0)
356     {
357       _DBUS_ASSERT_ERROR_IS_SET (error);
358       goto failed_1;
359     }
360   
361   server = _dbus_server_new_for_fd (listen_fd, &address);
362   if (server == NULL)
363     {
364       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
365       goto failed_2;
366     }
367
368   unix_server = (DBusServerUnix*) server;
369   unix_server->socket_name = path_copy;
370   
371   _dbus_string_free (&address);
372   
373   return server;
374
375  failed_2:
376   _dbus_close (listen_fd, NULL);
377  failed_1:
378   dbus_free (path_copy);
379  failed_0:
380   _dbus_string_free (&address);
381
382   return NULL;
383 }
384
385 /**
386  * Creates a new server listening on the given hostname and port.
387  * If the hostname is NULL, listens on localhost.
388  *
389  * @param host the hostname to listen on.
390  * @param port the port to listen on.
391  * @param error location to store reason for failure.
392  * @returns the new server, or #NULL on failure.
393  */
394 DBusServer*
395 _dbus_server_new_for_tcp_socket (const char     *host,
396                                  dbus_uint32_t   port,
397                                  DBusError      *error)
398 {
399   DBusServer *server;
400   int listen_fd;
401   DBusString address;
402   DBusString host_str;
403   
404   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
405
406   if (!_dbus_string_init (&address))
407     {
408       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
409       return NULL;
410     }
411
412   if (host == NULL)
413     host = "localhost";
414
415   _dbus_string_init_const (&host_str, host);
416   if (!_dbus_string_append (&address, "tcp:host=") ||
417       !_dbus_address_append_escaped (&address, &host_str) ||
418       !_dbus_string_append (&address, ",port=") ||
419       !_dbus_string_append_int (&address, port))
420     {
421       _dbus_string_free (&address);
422       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
423       return NULL;
424     }
425   
426   listen_fd = _dbus_listen_tcp_socket (host, port, error);
427   _dbus_fd_set_close_on_exec (listen_fd);
428   
429   if (listen_fd < 0)
430     {
431       _dbus_string_free (&address);
432       return NULL;
433     }
434   
435   server = _dbus_server_new_for_fd (listen_fd, &address);
436   if (server == NULL)
437     {
438       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
439       close (listen_fd);
440       _dbus_string_free (&address);
441       return NULL;
442     }
443
444   _dbus_string_free (&address);
445   
446   return server;
447
448
449 }
450
451 /** @} */
452