1/* dbus-server-launchd.c Server methods for interacting with launchd.
2 * Copyright (C) 2007, Tanner Lovelace <lovelace@wayfarer.org>
3 * Copyright (C) 2008, Colin Walters <walters@verbum.org>
4 * Copyright (C) 2008-2009, Benjamin Reed <rangerrick@befunk.com>
5 * Copyright (C) 2009, Jonas B��hr <jonas.baehr@web.de>
6 *
7 * Permission is hereby granted, free of charge, to any person
8 * obtaining a copy of this software and associated documentation
9 * files (the "Software"), to deal in the Software without
10 * restriction, including without limitation the rights to use, copy,
11 * modify, merge, publish, distribute, sublicense, and/or sell copies
12 * of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 * DEALINGS IN THE SOFTWARE.
26 */
27
28#include <config.h>
29#include "dbus-server-launchd.h"
30
31/**
32 * @defgroup DBusServerLaunchd DBusServer implementations for Launchd
33 * @ingroup  DBusInternals
34 * @brief Implementation details of DBusServer with Launchd support
35 *
36 * @{
37 */
38
39#ifdef DBUS_ENABLE_LAUNCHD
40#include <launch.h>
41#include <errno.h>
42
43#include "dbus-server-socket.h"
44
45/* put other private launchd functions here */
46
47#endif /* DBUS_ENABLE_LAUNCHD */
48
49/**
50 * @brief Creates a new server from launchd.
51 *
52 * launchd has allocaed a socket for us. We now query launchd for the
53 * file descriptor of this socket and create a server on it.
54 * In addition we inherit launchd's environment which holds a variable
55 * containing the path to the socket. This is used to init the server's
56 * address which is passed to autolaunched services.
57 *
58 * @param launchd_env_var the environment variable which holds the unix path to the socket
59 * @param error location to store reason for failure.
60 * @returns the new server, or #NULL on failure.
61 */
62
63DBusServer *
64_dbus_server_new_for_launchd (const char *launchd_env_var, DBusError * error)
65  {
66#ifdef DBUS_ENABLE_LAUNCHD
67    DBusServer *server;
68    DBusString address;
69    int launchd_fd;
70    launch_data_t sockets_dict, checkin_response;
71    launch_data_t checkin_request;
72    launch_data_t listening_fd_array, listening_fd;
73    launch_data_t environment_dict, environment_param;
74    const char *launchd_socket_path, *display;
75
76    launchd_socket_path = _dbus_getenv (launchd_env_var);
77    display = _dbus_getenv ("DISPLAY");
78
79    _DBUS_ASSERT_ERROR_IS_CLEAR (error);
80
81    if (launchd_socket_path == NULL || *launchd_socket_path == '\0')
82      {
83        dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
84                        "launchd's environment variable %s is empty, but should contain a socket path.\n", launchd_env_var);
85        return NULL;
86      }
87
88    if (!_dbus_string_init (&address))
89      {
90        dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
91        return NULL;
92      }
93    if (!_dbus_string_append (&address, "unix:path="))
94      {
95        dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
96        goto l_failed_0;
97      }
98    if (!_dbus_string_append (&address, launchd_socket_path))
99      {
100        dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
101        goto l_failed_0;
102      }
103
104    if ((checkin_request = launch_data_new_string (LAUNCH_KEY_CHECKIN)) == NULL)
105      {
106        dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
107                        "launch_data_new_string(\"%s\") Unable to create string.\n",
108                        LAUNCH_KEY_CHECKIN);
109        goto l_failed_0;
110      }
111
112    if ((checkin_response = launch_msg (checkin_request)) == NULL)
113      {
114        dbus_set_error (error, DBUS_ERROR_IO_ERROR,
115                        "launch_msg(\"%s\") IPC failure: %s\n",
116                        LAUNCH_KEY_CHECKIN, strerror (errno));
117        goto l_failed_0;
118      }
119
120    if (LAUNCH_DATA_ERRNO == launch_data_get_type (checkin_response))
121      {
122        dbus_set_error (error, DBUS_ERROR_FAILED, "Check-in failed: %s\n",
123                        strerror (launch_data_get_errno (checkin_response)));
124        goto l_failed_0;
125      }
126
127    sockets_dict =
128      launch_data_dict_lookup (checkin_response, LAUNCH_JOBKEY_SOCKETS);
129    if (NULL == sockets_dict)
130      {
131        dbus_set_error (error, DBUS_ERROR_IO_ERROR,
132                        "No sockets found to answer requests on!\n");
133        goto l_failed_0;
134      }
135
136    listening_fd_array =
137      launch_data_dict_lookup (sockets_dict, "unix_domain_listener");
138    if (NULL == listening_fd_array)
139      {
140        dbus_set_error (error, DBUS_ERROR_IO_ERROR,
141                        "No known sockets found to answer requests on!\n");
142        goto l_failed_0;
143      }
144
145    if (launch_data_array_get_count (listening_fd_array) != 1)
146      {
147        dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
148                        "Expected 1 socket from launchd, got %d.\n",
149                        launch_data_array_get_count (listening_fd_array));
150        goto l_failed_0;
151      }
152
153    listening_fd = launch_data_array_get_index (listening_fd_array, 0);
154    launchd_fd = launch_data_get_fd (listening_fd);
155
156    _dbus_fd_set_close_on_exec (launchd_fd);
157
158    if (launchd_fd < 0)
159      {
160        _DBUS_ASSERT_ERROR_IS_SET (error);
161        goto l_failed_0;
162  if (display == NULL || *display == '\0')
163    {
164      environment_dict = launch_data_dict_lookup (checkin_response, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES);
165      if (NULL == environment_dict)
166        {
167          _dbus_warn ("Unable to retrieve user environment from launchd.");
168        }
169      else
170        {
171          environment_param = launch_data_dict_lookup (environment_dict, "DISPLAY");
172          if (NULL == environment_param)
173            {
174              _dbus_warn ("Unable to retrieve DISPLAY from launchd.");
175            }
176          else
177            {
178              display = launch_data_get_string(environment_param);
179              _dbus_setenv ("DISPLAY", display);
180            }
181        }
182    }
183
184      }
185
186    server = _dbus_server_new_for_socket (&launchd_fd, 1, &address, 0);
187    if (server == NULL)
188      {
189        dbus_set_error (error, DBUS_ERROR_NO_SERVER,
190                        "Unable to listen on launchd fd %d.", launchd_fd);
191        goto l_failed_0;
192      }
193
194    _dbus_string_free (&address);
195
196    return server;
197
198  l_failed_0:
199    _dbus_string_free (&address);
200
201    return NULL;
202#else /* DBUS_ENABLE_LAUNCHD */
203    dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
204                    "address type 'launchd' requested, but launchd support not compiled in");
205    return NULL;
206#endif
207  }
208
209/** @} */
210