dbus-marshal-byteswap: Byte-swap Unix fd indexes if needed
[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       _dbus_string_free (&address);
253
254       return DBUS_SERVER_LISTEN_OK;
255
256   systemd_oom:
257       _DBUS_SET_OOM (error);
258   systemd_err:
259       for (i = 0; i < n; i++)
260         {
261           _dbus_close_socket (fds[i], NULL);
262         }
263       dbus_free (fds);
264       _dbus_string_free (&address);
265
266       return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
267     }
268 #ifdef DBUS_ENABLE_LAUNCHD
269   else if (strcmp (method, "launchd") == 0)
270     {
271       const char *launchd_env_var = dbus_address_entry_get_value (entry, "env");
272       if (launchd_env_var == NULL)
273         {
274           _dbus_set_bad_address (error, "launchd", "env", NULL);
275           return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
276         }
277       *server_p = _dbus_server_new_for_launchd (launchd_env_var, error);
278
279       if (*server_p != NULL)
280         {
281           _DBUS_ASSERT_ERROR_IS_CLEAR(error);
282           return DBUS_SERVER_LISTEN_OK;
283         }
284       else
285         {
286           _DBUS_ASSERT_ERROR_IS_SET(error);
287           return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
288         }
289     }
290 #endif
291   else
292     {
293       /* If we don't handle the method, we return NULL with the
294        * error unset
295        */
296       _DBUS_ASSERT_ERROR_IS_CLEAR(error);
297       return DBUS_SERVER_LISTEN_NOT_HANDLED;
298     }
299 }
300
301 /**
302  * Creates a new server listening on the given Unix domain socket.
303  *
304  * @param path the path for the domain socket.
305  * @param abstract #TRUE to use abstract socket namespace
306  * @param error location to store reason for failure.
307  * @returns the new server, or #NULL on failure.
308  */
309 DBusServer*
310 _dbus_server_new_for_domain_socket (const char     *path,
311                                     dbus_bool_t     abstract,
312                                     DBusError      *error)
313 {
314   DBusServer *server;
315   DBusSocket listen_fd;
316   DBusString address;
317   char *path_copy;
318   DBusString path_str;
319
320   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
321
322   if (!_dbus_string_init (&address))
323     {
324       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
325       return NULL;
326     }
327
328   _dbus_string_init_const (&path_str, path);
329   if ((abstract &&
330        !_dbus_string_append (&address, "unix:abstract=")) ||
331       (!abstract &&
332        !_dbus_string_append (&address, "unix:path=")) ||
333       !_dbus_address_append_escaped (&address, &path_str))
334     {
335       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
336       goto failed_0;
337     }
338
339   if (abstract)
340     {
341       path_copy = NULL;
342     }
343   else
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
353   listen_fd.fd = _dbus_listen_unix_socket (path, abstract, error);
354
355   if (listen_fd.fd < 0)
356     {
357       _DBUS_ASSERT_ERROR_IS_SET (error);
358       goto failed_1;
359     }
360
361   server = _dbus_server_new_for_socket (&listen_fd, 1, &address, 0, error);
362   if (server == NULL)
363     {
364       goto failed_2;
365     }
366
367   if (path_copy != NULL)
368     _dbus_server_socket_own_filename(server, path_copy);
369
370   _dbus_string_free (&address);
371
372   return server;
373
374  failed_2:
375   _dbus_close_socket (listen_fd, NULL);
376  failed_1:
377   dbus_free (path_copy);
378  failed_0:
379   _dbus_string_free (&address);
380
381   return NULL;
382 }
383
384 /** @} */