534e1d5fedd24091887eca38e7d00755cc8e0e2d
[platform/upstream/dbus.git] / dbus / dbus-server-unix.c
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  */
23
24 #include <config.h>
25 #include "dbus-internals.h"
26 #include "dbus-server-unix.h"
27 #include "dbus-server-socket.h"
28 #include "dbus-server-launchd.h"
29 #include "dbus-transport-unix.h"
30 #include "dbus-connection-internal.h"
31 #include "dbus-sysdeps-unix.h"
32 #include "dbus-string.h"
33
34 /**
35  * @defgroup DBusServerUnix DBusServer implementations for UNIX
36  * @ingroup  DBusInternals
37  * @brief Implementation details of DBusServer on UNIX
38  *
39  * @{
40  */
41
42 /**
43  * Tries to interpret the address entry in a platform-specific
44  * way, creating a platform-specific server type if appropriate.
45  * Sets error if the result is not OK.
46  *
47  * @param entry an address entry
48  * @param server_p location to store a new DBusServer, or #NULL on failure.
49  * @param error location to store rationale for failure on bad address
50  * @returns the outcome
51  *
52  */
53 DBusServerListenResult
54 _dbus_server_listen_platform_specific (DBusAddressEntry *entry,
55                                        DBusServer      **server_p,
56                                        DBusError        *error)
57 {
58   const char *method;
59
60   *server_p = NULL;
61
62   method = dbus_address_entry_get_method (entry);
63
64   if (strcmp (method, "unix") == 0)
65     {
66       const char *path = dbus_address_entry_get_value (entry, "path");
67       const char *dir = dbus_address_entry_get_value (entry, "dir");
68       const char *tmpdir = dbus_address_entry_get_value (entry, "tmpdir");
69       const char *abstract = dbus_address_entry_get_value (entry, "abstract");
70       const char *runtime = dbus_address_entry_get_value (entry, "runtime");
71       int mutually_exclusive_modes = 0;
72
73       mutually_exclusive_modes = (path != NULL) + (tmpdir != NULL) +
74         (abstract != NULL) + (runtime != NULL) + (dir != NULL);
75
76       if (mutually_exclusive_modes < 1)
77         {
78           _dbus_set_bad_address(error, "unix",
79                                 "path or tmpdir or abstract or runtime or dir",
80                                 NULL);
81           return DBUS_SERVER_LISTEN_BAD_ADDRESS;
82         }
83
84       if (mutually_exclusive_modes > 1)
85         {
86           _dbus_set_bad_address(error, NULL, NULL,
87                                 "cannot specify two of \"path\", \"tmpdir\", \"abstract\", \"runtime\" and \"dir\" at the same time");
88           return DBUS_SERVER_LISTEN_BAD_ADDRESS;
89         }
90
91       if (runtime != NULL)
92         {
93           DBusString full_path;
94           DBusString filename;
95           const char *runtimedir;
96
97           if (strcmp (runtime, "yes") != 0)
98             {
99               _dbus_set_bad_address(error, NULL, NULL,
100                   "if given, the only value allowed for \"runtime\" is \"yes\"");
101               return DBUS_SERVER_LISTEN_BAD_ADDRESS;
102             }
103
104           runtimedir = _dbus_getenv ("XDG_RUNTIME_DIR");
105
106           if (runtimedir == NULL)
107             {
108               dbus_set_error (error,
109                   DBUS_ERROR_NOT_SUPPORTED, "\"XDG_RUNTIME_DIR\" is not set");
110               return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
111             }
112
113           _dbus_string_init_const (&filename, "bus");
114
115           if (!_dbus_string_init (&full_path))
116             {
117               _DBUS_SET_OOM (error);
118               return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
119             }
120
121           if (!_dbus_string_append (&full_path, runtimedir) ||
122               !_dbus_concat_dir_and_file (&full_path, &filename))
123             {
124               _dbus_string_free (&full_path);
125               _DBUS_SET_OOM (error);
126               return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
127             }
128
129           /* We can safely use filesystem sockets in the runtime directory,
130            * and they are preferred because they can be bind-mounted between
131            * Linux containers. */
132           *server_p = _dbus_server_new_for_domain_socket (
133               _dbus_string_get_const_data (&full_path),
134               FALSE, error);
135
136           _dbus_string_free (&full_path);
137         }
138       else if (tmpdir != NULL || dir != NULL)
139         {
140           DBusString full_path;
141           DBusString filename;
142           dbus_bool_t use_abstract = FALSE;
143
144           if (tmpdir != NULL)
145             {
146               dir = tmpdir;
147
148 #ifdef __linux__
149               /* Use abstract sockets for tmpdir if supported, so that it
150                * never needs to be cleaned up. Use dir instead if you want a
151                * path-based socket. */
152               use_abstract = TRUE;
153 #endif
154             }
155
156           if (!_dbus_string_init (&full_path))
157             {
158               dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
159               return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
160             }
161
162           if (!_dbus_string_init (&filename))
163             {
164               _dbus_string_free (&full_path);
165               dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
166               return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
167             }
168
169           if (!_dbus_string_append (&filename, "dbus-"))
170             {
171               _dbus_string_free (&full_path);
172               _dbus_string_free (&filename);
173               dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
174               return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
175             }
176
177           if (!_dbus_generate_random_ascii (&filename, 10, error))
178             {
179               _dbus_string_free (&full_path);
180               _dbus_string_free (&filename);
181               return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
182             }
183
184           if (!_dbus_string_append (&full_path, dir) ||
185               !_dbus_concat_dir_and_file (&full_path, &filename))
186             {
187               _dbus_string_free (&full_path);
188               _dbus_string_free (&filename);
189               dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
190               return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
191             }
192
193           *server_p =
194             _dbus_server_new_for_domain_socket (_dbus_string_get_const_data (&full_path),
195                                                 use_abstract,
196                                                 error);
197
198           _dbus_string_free (&full_path);
199           _dbus_string_free (&filename);
200         }
201       else
202         {
203           if (path)
204             *server_p = _dbus_server_new_for_domain_socket (path, FALSE, error);
205           else
206             *server_p = _dbus_server_new_for_domain_socket (abstract, TRUE, error);
207         }
208
209       if (*server_p != NULL)
210         {
211           _DBUS_ASSERT_ERROR_IS_CLEAR(error);
212           return DBUS_SERVER_LISTEN_OK;
213         }
214       else
215         {
216           _DBUS_ASSERT_ERROR_IS_SET(error);
217           return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
218         }
219     }
220   else if (strcmp (method, "systemd") == 0)
221     {
222       int i, n;
223       DBusSocket *fds;
224       DBusString address;
225
226       n = _dbus_listen_systemd_sockets (&fds, error);
227       if (n < 0)
228         {
229           _DBUS_ASSERT_ERROR_IS_SET (error);
230           return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
231         }
232
233       if (!_dbus_string_init (&address))
234           goto systemd_oom;
235
236       for (i = 0; i < n; i++)
237         {
238           if (i > 0)
239             {
240               if (!_dbus_string_append (&address, ";"))
241                 goto systemd_oom;
242             }
243           if (!_dbus_append_address_from_socket (fds[i], &address, error))
244             goto systemd_err;
245         }
246
247       *server_p = _dbus_server_new_for_socket (fds, n, &address, NULL, error);
248       if (*server_p == NULL)
249         goto systemd_err;
250
251       dbus_free (fds);
252
253       return DBUS_SERVER_LISTEN_OK;
254
255   systemd_oom:
256       _DBUS_SET_OOM (error);
257   systemd_err:
258       for (i = 0; i < n; i++)
259         {
260           _dbus_close_socket (fds[i], NULL);
261         }
262       dbus_free (fds);
263       _dbus_string_free (&address);
264
265       return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
266     }
267 #ifdef DBUS_ENABLE_LAUNCHD
268   else if (strcmp (method, "launchd") == 0)
269     {
270       const char *launchd_env_var = dbus_address_entry_get_value (entry, "env");
271       if (launchd_env_var == NULL)
272         {
273           _dbus_set_bad_address (error, "launchd", "env", NULL);
274           return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
275         }
276       *server_p = _dbus_server_new_for_launchd (launchd_env_var, error);
277
278       if (*server_p != NULL)
279         {
280           _DBUS_ASSERT_ERROR_IS_CLEAR(error);
281           return DBUS_SERVER_LISTEN_OK;
282         }
283       else
284         {
285           _DBUS_ASSERT_ERROR_IS_SET(error);
286           return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
287         }
288     }
289 #endif
290   else
291     {
292       /* If we don't handle the method, we return NULL with the
293        * error unset
294        */
295       _DBUS_ASSERT_ERROR_IS_CLEAR(error);
296       return DBUS_SERVER_LISTEN_NOT_HANDLED;
297     }
298 }
299
300 /**
301  * Creates a new server listening on the given Unix domain socket.
302  *
303  * @param path the path for the domain socket.
304  * @param abstract #TRUE to use abstract socket namespace
305  * @param error location to store reason for failure.
306  * @returns the new server, or #NULL on failure.
307  */
308 DBusServer*
309 _dbus_server_new_for_domain_socket (const char     *path,
310                                     dbus_bool_t     abstract,
311                                     DBusError      *error)
312 {
313   DBusServer *server;
314   DBusSocket listen_fd;
315   DBusString address;
316   char *path_copy;
317   DBusString path_str;
318
319   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
320
321   if (!_dbus_string_init (&address))
322     {
323       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
324       return NULL;
325     }
326
327   _dbus_string_init_const (&path_str, path);
328   if ((abstract &&
329        !_dbus_string_append (&address, "unix:abstract=")) ||
330       (!abstract &&
331        !_dbus_string_append (&address, "unix:path=")) ||
332       !_dbus_address_append_escaped (&address, &path_str))
333     {
334       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
335       goto failed_0;
336     }
337
338   if (abstract)
339     {
340       path_copy = NULL;
341     }
342   else
343     {
344       path_copy = _dbus_strdup (path);
345       if (path_copy == NULL)
346         {
347           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
348           goto failed_0;
349         }
350     }
351
352   listen_fd.fd = _dbus_listen_unix_socket (path, abstract, error);
353
354   if (listen_fd.fd < 0)
355     {
356       _DBUS_ASSERT_ERROR_IS_SET (error);
357       goto failed_1;
358     }
359
360   server = _dbus_server_new_for_socket (&listen_fd, 1, &address, 0, error);
361   if (server == NULL)
362     {
363       goto failed_2;
364     }
365
366   if (path_copy != NULL)
367     _dbus_server_socket_own_filename(server, path_copy);
368
369   _dbus_string_free (&address);
370
371   return server;
372
373  failed_2:
374   _dbus_close_socket (listen_fd, NULL);
375  failed_1:
376   dbus_free (path_copy);
377  failed_0:
378   _dbus_string_free (&address);
379
380   return NULL;
381 }
382
383 /** @} */