bus: add ConnectionOverflow signal API
[platform/upstream/dbus.git] / dbus / dbus-transport-unix.c
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /* dbus-transport-unix.c UNIX socket subclasses of DBusTransport
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
26 #include <stdio.h>
27
28 #include "dbus-internals.h"
29 #include "dbus-connection-internal.h"
30 #include "dbus-transport-unix.h"
31 #include "dbus-transport-socket.h"
32 #include "dbus-transport-protected.h"
33 #include "dbus-watch.h"
34 #include "dbus-sysdeps-unix.h"
35 #include "dbus-test.h"
36
37 /**
38  * @defgroup DBusTransportUnix DBusTransport implementations for UNIX
39  * @ingroup  DBusInternals
40  * @brief Implementation details of DBusTransport on UNIX
41  *
42  * @{
43  */
44
45 /**
46  * Creates a new transport for the given Unix domain socket
47  * path. This creates a client-side of a transport.
48  *
49  * @todo once we add a way to escape paths in a dbus
50  * address, this function needs to do escaping.
51  *
52  * @param path the path to the domain socket.
53  * @param abstract #TRUE to use abstract socket namespace
54  * @param error address where an error can be returned.
55  * @returns a new transport, or #NULL on failure.
56  */
57 DBusTransport*
58 _dbus_transport_new_for_domain_socket (const char     *path,
59                                        dbus_bool_t     abstract,
60                                        DBusError      *error)
61 {
62   DBusSocket fd = DBUS_SOCKET_INIT;
63   DBusTransport *transport;
64   DBusString address;
65   
66   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
67
68   if (!_dbus_string_init (&address))
69     {
70       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
71       return NULL;
72     }
73
74   if ((abstract &&
75        !_dbus_string_append (&address, "unix:abstract=")) ||
76       (!abstract &&
77        !_dbus_string_append (&address, "unix:path=")) ||
78       !_dbus_string_append (&address, path))
79     {
80       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
81       goto failed_0;
82     }
83   
84   fd.fd = _dbus_connect_unix_socket (path, abstract, error);
85   if (fd.fd < 0)
86     {
87       _DBUS_ASSERT_ERROR_IS_SET (error);
88       goto failed_0;
89     }
90
91   _dbus_verbose ("Successfully connected to unix socket %s\n",
92                  path);
93
94   transport = _dbus_transport_new_for_socket (fd, NULL, &address);
95   if (transport == NULL)
96     {
97       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
98       goto failed_1;
99     }
100   
101   _dbus_string_free (&address);
102   
103   return transport;
104
105  failed_1:
106   _dbus_close_socket (fd, NULL);
107  failed_0:
108   _dbus_string_free (&address);
109   return NULL;
110 }
111
112 /**
113  * Creates a new transport for the given binary and arguments. This
114  * creates a client-side of a transport. The process will be forked
115  * off and executed with stdin/stdout connected to a local AF_UNIX
116  * socket.
117  *
118  * @param path the path to the domain socket.
119  * @param argv Parameters list
120  * @param error address where an error can be returned.
121  * @returns a new transport, or #NULL on failure.
122  */
123 static DBusTransport*
124 _dbus_transport_new_for_exec (const char     *path,
125                               char *const     argv[],
126                               DBusError      *error)
127 {
128   DBusSocket fd = DBUS_SOCKET_INIT;
129   DBusTransport *transport;
130   DBusString address;
131   unsigned i;
132   char *escaped;
133
134   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
135
136   if (!_dbus_string_init (&address))
137     {
138       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
139       return NULL;
140     }
141
142   escaped = dbus_address_escape_value (path);
143   if (!escaped)
144     {
145       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
146       goto failed;
147     }
148
149   if (!_dbus_string_append (&address, "unixexec:path=") ||
150       !_dbus_string_append (&address, escaped))
151     {
152       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
153       dbus_free (escaped);
154       goto failed;
155     }
156
157   dbus_free (escaped);
158
159   if (argv)
160     {
161       for (i = 0; argv[i]; i++)
162         {
163           dbus_bool_t success;
164
165           escaped = dbus_address_escape_value (argv[i]);
166           if (!escaped)
167             {
168               dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
169               goto failed;
170             }
171
172           success = _dbus_string_append_printf (&address, ",argv%u=%s", i, escaped);
173           dbus_free (escaped);
174
175           if (!success)
176             {
177               dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
178               goto failed;
179             }
180         }
181     }
182
183   fd.fd = _dbus_connect_exec (path, argv, error);
184   if (fd.fd < 0)
185     {
186       _DBUS_ASSERT_ERROR_IS_SET (error);
187       goto failed;
188     }
189
190   _dbus_verbose ("Successfully connected to process %s\n",
191                  path);
192
193   transport = _dbus_transport_new_for_socket (fd, NULL, &address);
194   if (transport == NULL)
195     {
196       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
197       goto failed;
198     }
199
200   _dbus_string_free (&address);
201
202   return transport;
203
204  failed:
205   if (fd.fd >= 0)
206     _dbus_close_socket (fd, NULL);
207
208   _dbus_string_free (&address);
209   return NULL;
210 }
211
212 /**
213  * Opens platform specific transport types.
214  * 
215  * @param entry the address entry to try opening
216  * @param transport_p return location for the opened transport
217  * @param error error to be set
218  * @returns result of the attempt
219  */
220 DBusTransportOpenResult
221 _dbus_transport_open_platform_specific (DBusAddressEntry  *entry,
222                                         DBusTransport    **transport_p,
223                                         DBusError         *error)
224 {
225   const char *method;
226   
227   method = dbus_address_entry_get_method (entry);
228   _dbus_assert (method != NULL);
229
230   if (strcmp (method, "unix") == 0)
231     {
232       const char *path = dbus_address_entry_get_value (entry, "path");
233       const char *tmpdir = dbus_address_entry_get_value (entry, "tmpdir");
234       const char *abstract = dbus_address_entry_get_value (entry, "abstract");
235           
236       if (tmpdir != NULL)
237         {
238           _dbus_set_bad_address (error, NULL, NULL,
239                                  "cannot use the \"tmpdir\" option for an address to connect to, only in an address to listen on");
240           return DBUS_TRANSPORT_OPEN_BAD_ADDRESS;
241         }
242           
243       if (path == NULL && abstract == NULL)
244         {
245           _dbus_set_bad_address (error, "unix",
246                                  "path or abstract",
247                                  NULL);
248           return DBUS_TRANSPORT_OPEN_BAD_ADDRESS;
249         }
250
251       if (path != NULL && abstract != NULL)
252         {
253           _dbus_set_bad_address (error, NULL, NULL,
254                                  "can't specify both \"path\" and \"abstract\" options in an address");
255           return DBUS_TRANSPORT_OPEN_BAD_ADDRESS;
256         }
257
258       if (path)
259         *transport_p = _dbus_transport_new_for_domain_socket (path, FALSE,
260                                                            error);
261       else
262         *transport_p = _dbus_transport_new_for_domain_socket (abstract, TRUE,
263                                                            error);
264       if (*transport_p == NULL)
265         {
266           _DBUS_ASSERT_ERROR_IS_SET (error);
267           return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT;
268         }
269       else
270         {
271           _DBUS_ASSERT_ERROR_IS_CLEAR (error);
272           return DBUS_TRANSPORT_OPEN_OK;
273         }
274     }
275   else if (strcmp (method, "unixexec") == 0)
276     {
277       const char *path;
278       unsigned i;
279       char **argv;
280
281       path = dbus_address_entry_get_value (entry, "path");
282       if (path == NULL)
283         {
284           _dbus_set_bad_address (error, NULL, NULL,
285                                  "No process path specified");
286           return DBUS_TRANSPORT_OPEN_BAD_ADDRESS;
287         }
288
289       /* First count argv arguments */
290       for (i = 1; ; i++)
291         {
292           char t[4+20+1]; /* "argv" plus space for a formatted base 10 64bit integer, plus NUL */
293
294           snprintf (t, sizeof(t), "argv%u", i);
295
296           if (!dbus_address_entry_get_value (entry, t))
297             break;
298         }
299
300       /* Allocate string array */
301       argv = dbus_new0 (char*, i+1);
302       if (!argv)
303         {
304           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
305           return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT;
306         }
307
308       /* Fill in string array */
309       for (i = 0; ; i++)
310         {
311           char t[4+20+1];
312           const char *p;
313
314           snprintf (t, sizeof(t), "argv%u", i);
315
316           p = dbus_address_entry_get_value (entry, t);
317           if (!p)
318             {
319               if (i == 0)
320                 /* If argv0 isn't specified, fill in the path instead */
321                 p = path;
322               else
323                 break;
324             }
325
326           argv[i] = _dbus_strdup (p);
327           if (!argv[i])
328             {
329               dbus_free_string_array (argv);
330               dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
331               return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT;
332             }
333         }
334
335       *transport_p = _dbus_transport_new_for_exec (path, argv, error);
336       dbus_free_string_array (argv);
337
338       if (*transport_p == NULL)
339         {
340           _DBUS_ASSERT_ERROR_IS_SET (error);
341           return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT;
342         }
343       else
344         {
345           _DBUS_ASSERT_ERROR_IS_CLEAR (error);
346           return DBUS_TRANSPORT_OPEN_OK;
347         }
348     }
349 #ifdef DBUS_ENABLE_LAUNCHD
350   else if (strcmp (method, "launchd") == 0)
351     {
352       DBusError tmp_error = DBUS_ERROR_INIT;
353       const char *launchd_env_var = dbus_address_entry_get_value (entry, "env");
354       const char *launchd_socket;
355       DBusString socket_path;
356       dbus_bool_t valid_socket;
357
358       if (!_dbus_string_init (&socket_path))
359         {
360           _DBUS_SET_OOM (error);
361           return FALSE;
362         }
363
364       if (launchd_env_var == NULL)
365         {
366           _dbus_set_bad_address (error, "launchd", "env", NULL);
367           return DBUS_TRANSPORT_OPEN_BAD_ADDRESS;
368         }
369
370       valid_socket = _dbus_lookup_launchd_socket (&socket_path, launchd_env_var, error);
371
372       if (dbus_error_is_set(error))
373         {
374           _dbus_string_free(&socket_path);
375           return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT;
376         }
377
378       if (!valid_socket)
379         {
380           dbus_set_error(&tmp_error, DBUS_ERROR_BAD_ADDRESS,
381                          "launchd's env var %s does not exist", launchd_env_var);
382           dbus_error_free(error);
383           dbus_move_error(&tmp_error, error);
384           return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT;
385         }
386
387       launchd_socket = _dbus_string_get_const_data(&socket_path);
388       *transport_p = _dbus_transport_new_for_domain_socket (launchd_socket, FALSE, error);
389
390       if (*transport_p == NULL)
391         {
392           _DBUS_ASSERT_ERROR_IS_SET (error);
393           return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT;
394         }
395       else
396         {
397           _DBUS_ASSERT_ERROR_IS_CLEAR (error);
398           return DBUS_TRANSPORT_OPEN_OK;
399         }
400     }
401 #endif
402   else
403     {
404       _DBUS_ASSERT_ERROR_IS_CLEAR (error);
405       return DBUS_TRANSPORT_OPEN_NOT_HANDLED;
406     }
407 }
408
409 /** @} */
410
411 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
412
413 dbus_bool_t
414 _dbus_transport_unix_test (void)
415 {
416   DBusConnection *c;
417   DBusError error;
418   dbus_bool_t ret;
419   const char *address;
420
421   dbus_error_init (&error);
422
423   c = dbus_connection_open ("unixexec:argv0=false,argv1=foobar,path=/bin/false", &error);
424   _dbus_assert (c != NULL);
425   _dbus_assert (!dbus_error_is_set (&error));
426
427   address = _dbus_connection_get_address (c);
428   _dbus_assert (address != NULL);
429
430   /* Let's see if the address got parsed, reordered and formatted correctly */
431   ret = strcmp (address, "unixexec:path=/bin/false,argv0=false,argv1=foobar") == 0;
432
433   dbus_connection_unref (c);
434
435   return ret;
436 }
437
438 #endif