dbus-daemon: Don't create /tmp/dbus_launch
[platform/upstream/dbus.git] / tools / dbus-update-activation-environment.c
1 /*
2  * dbus-update-activation-environment - update D-Bus, and optionally
3  * systemd, activation environment
4  *
5  * Copyright © 2014-2015 Collabora Ltd.
6  *
7  * Permission is hereby granted, free of charge, to any person
8  * obtaining a copy of this software and associated documentation files
9  * (the "Software"), to deal in the Software without restriction,
10  * including without limitation the rights to use, copy, modify, merge,
11  * publish, distribute, sublicense, and/or sell copies of the Software,
12  * and to permit persons to whom the Software is furnished to do so,
13  * 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 HOLDERS
22  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
23  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25  * SOFTWARE.
26  */
27
28 #include <config.h>
29
30 #include <errno.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #ifdef HAVE_SYSEXITS_H
35 # include <sysexits.h>
36 #endif
37
38 #include <dbus/dbus.h>
39
40 #ifdef DBUS_UNIX
41 # include <unistd.h>
42 # include <sys/stat.h>
43 # include <sys/types.h>
44 #endif
45
46 #include "tool-common.h"
47
48 #define PROGNAME "dbus-update-activation-environment"
49
50 #ifndef EX_USAGE
51 # define EX_USAGE 64
52 #endif
53
54 #ifndef EX_UNAVAILABLE
55 # define EX_UNAVAILABLE 69
56 #endif
57
58 #ifndef EX_OSERR
59 # define EX_OSERR 71
60 #endif
61
62 #ifdef DBUS_WIN
63 /* The Windows C runtime uses a different name */
64 #define environ _environ
65 #else
66 /* apparently this is the portable way to get the entire environment...
67  * GNU platforms also put it in unistd.h but that's not portable */
68 extern char **environ;
69 #endif
70
71 /* we don't really have anything useful to say about the stage at which we
72  * failed */
73 #define oom() tool_oom ("updating environment")
74
75 static dbus_bool_t verbose = FALSE;
76
77 static void say (const char *format, ...) _DBUS_GNUC_PRINTF (1, 2);
78
79 static void
80 say (const char *format,
81     ...)
82 {
83   va_list ap;
84
85   if (!verbose)
86     return;
87
88   fprintf (stderr, "%s: ", PROGNAME);
89   va_start (ap, format);
90   vfprintf (stderr, format, ap);
91   fputc ('\n', stderr);
92   va_end (ap);
93 }
94
95 #ifdef __linux__
96 static dbus_bool_t
97 systemd_user_running (void)
98 {
99   char *xdg_runtime_dir = getenv ("XDG_RUNTIME_DIR");
100   char *path;
101   struct stat buf;
102   dbus_bool_t ret = FALSE;
103
104   if (xdg_runtime_dir == NULL)
105     return FALSE;
106
107   /* Assume that XDG_RUNTIME_DIR/systemd exists if and only if
108    * "systemd --user" is running. It's OK to use asprintf() here
109    * because we know we're on Linux. */
110   if (asprintf (&path, "%s/systemd", xdg_runtime_dir) < 0)
111     oom ();
112
113   if (stat (path, &buf) == 0)
114     ret = TRUE;
115
116   free (path);
117   return ret;
118 }
119 #endif
120
121 int
122 main (int argc, char **argv)
123 {
124   DBusConnection *conn;
125   DBusMessage *msg;
126   DBusMessage *reply;
127   DBusError error = DBUS_ERROR_INIT;
128   DBusMessageIter msg_iter;
129   DBusMessageIter array_iter;
130   int i;
131   int first_non_option = argc;
132   dbus_bool_t all = FALSE;
133 #ifdef __linux__
134   DBusMessage *sd_msg = NULL;
135   DBusMessageIter sd_msg_iter;
136   DBusMessageIter sd_array_iter;
137   dbus_bool_t systemd = FALSE;
138 #endif
139
140   for (i = 1; i < argc; i++)
141     {
142       if (argv[i][0] != '-')
143         {
144           first_non_option = i;
145           break;
146         }
147       else if (strcmp (argv[i], "--") == 0)
148         {
149           first_non_option = i + 1;
150           break;
151         }
152       else if (strcmp (argv[i], "--all") == 0)
153         {
154           all = TRUE;
155         }
156       else if (strcmp (argv[i], "--systemd") == 0)
157         {
158 #ifdef __linux__
159           systemd = TRUE;
160 #else
161           say ("not on Linux, ignoring --systemd argument");
162 #endif
163         }
164       else if (strcmp (argv[i], "--verbose") == 0)
165         {
166           verbose = TRUE;
167         }
168       else
169         {
170           fprintf (stderr,
171               "%1$s: update environment variables that will be set for D-Bus\n"
172               "    session services\n"
173               "\n"
174               "%1$s [options] VAR[=VAL] [VAR2[=VAL2] ...]\n"
175               "    Add specified variables to D-Bus activation environment.\n"
176               "    If omitted, values are taken from current environment;\n"
177               "    variables not found in the environment are ignored.\n"
178               "%1$s --all\n"
179               "    Add entire current environment to D-Bus activation\n"
180               "    environment.\n"
181               "\n"
182               "Options:\n"
183               "\n"
184               "--all\n"
185               "    Upload all environment variables.\n"
186               "--systemd\n"
187               "    Also update the 'systemd --user' environment\n"
188               "    if possible.\n"
189               "--verbose\n"
190               "    Talk about it.\n"
191               ,
192               PROGNAME);
193           exit (EX_USAGE);
194         }
195     }
196
197   if (all && first_non_option < argc)
198     {
199       fprintf (stderr, "%s: error: --all cannot be used with VAR or "
200                "VAR=VAL arguments\n", PROGNAME);
201       exit (EX_USAGE);
202     }
203
204   conn = dbus_bus_get (DBUS_BUS_SESSION, &error);
205
206   if (conn == NULL)
207     {
208       fprintf (stderr,
209           "%s: error: unable to connect to D-Bus: %s\n", PROGNAME,
210           error.message);
211       exit (EX_OSERR);
212     }
213
214   msg = dbus_message_new_method_call (DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
215       DBUS_INTERFACE_DBUS, "UpdateActivationEnvironment");
216
217   if (msg == NULL)
218     oom ();
219
220   dbus_message_iter_init_append (msg, &msg_iter);
221
222   if (!dbus_message_iter_open_container (&msg_iter, DBUS_TYPE_ARRAY,
223       "{ss}", &array_iter))
224     oom ();
225
226 #ifdef __linux__
227   if (systemd)
228     {
229       if (!systemd_user_running ())
230         {
231           /* This is only best-effort. */
232           say ("systemd --user not found, ignoring --systemd argument");
233           systemd = FALSE;
234         }
235     }
236
237   if (systemd)
238     {
239       sd_msg = dbus_message_new_method_call ("org.freedesktop.systemd1",
240           "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager",
241           "SetEnvironment");
242
243       if (sd_msg == NULL)
244         oom ();
245
246       dbus_message_iter_init_append (sd_msg, &sd_msg_iter);
247
248       if (!dbus_message_iter_open_container (&sd_msg_iter, DBUS_TYPE_ARRAY,
249           "s", &sd_array_iter))
250         oom ();
251     }
252 #endif
253
254   for (i = all ? 0 : first_non_option;
255       all ? environ[i] != NULL : i < argc;
256       i++)
257     {
258       const char *var;
259       char *copy;
260       char *eq;
261       const char *val;
262       DBusMessageIter pair_iter;
263
264       if (all)
265         var = environ[i];
266       else
267         var = argv[i];
268
269       copy = strdup (var);
270
271       if (copy == NULL)
272         oom ();
273
274       if (!dbus_validate_utf8 (var, NULL))
275         {
276           /* var is either of the form VAR or VAR=VAL */
277           fprintf (stderr,
278               "%s: warning: environment variable not UTF-8: %s\n",
279               PROGNAME, var);
280           goto next;
281         }
282
283       eq = strchr (copy, '=');
284
285       if (eq == NULL)
286         {
287           if (all)
288             {
289               /* items in the environment block should be of the form
290                * VAR=VAL */
291               fprintf (stderr,
292                   "%s: warning: environment variable without '=': %s\n",
293                   PROGNAME, var);
294               goto next;
295             }
296           else
297             {
298               /* items on the command-line may be of the form VAR
299                * in which case we infer the value from the environment */
300               val = getenv (var);
301
302               if (val == NULL)
303                 {
304                   /* nothing to be done here */
305                   goto next;
306                 }
307
308               if (!dbus_validate_utf8 (val, NULL))
309                 {
310                   fprintf (stderr,
311                       "%s: warning: environment variable not UTF-8: %s=%s\n",
312                       PROGNAME, var, val);
313                   goto next;
314                 }
315             }
316         }
317       else
318         {
319           /* split VAR=VAL into VAR and VAL */
320           *eq = '\0';
321           val = eq + 1;
322         }
323
324 #ifdef __linux__
325       if (systemd)
326         {
327           char *combined;
328
329           /* recombine if necessary */
330           if (asprintf (&combined, "%s=%s", copy, val) < 0)
331             oom ();
332
333           if (!dbus_message_iter_append_basic (&sd_array_iter,
334                 DBUS_TYPE_STRING, &combined))
335             oom ();
336
337           free (combined);
338         }
339 #endif
340
341       if (!dbus_message_iter_open_container (&array_iter,
342               DBUS_TYPE_DICT_ENTRY, NULL, &pair_iter))
343         oom ();
344
345       say ("setting %s=%s", copy, val);
346
347       if (!dbus_message_iter_append_basic (&pair_iter, DBUS_TYPE_STRING,
348               &copy))
349         oom ();
350
351       if (!dbus_message_iter_append_basic (&pair_iter, DBUS_TYPE_STRING,
352               &val))
353         oom ();
354
355       if (!dbus_message_iter_close_container (&array_iter, &pair_iter))
356         oom ();
357
358 next:
359       free (copy);
360     }
361
362   if (!dbus_message_iter_close_container (&msg_iter, &array_iter))
363     oom ();
364
365 #ifdef __linux__
366   if (systemd &&
367       !dbus_message_iter_close_container (&sd_msg_iter, &sd_array_iter))
368     oom ();
369 #endif
370
371   reply = dbus_connection_send_with_reply_and_block (conn, msg, -1, &error);
372
373   if (reply == NULL)
374     {
375       fprintf (stderr,
376           "%s: error sending to dbus-daemon: %s: %s\n",
377           PROGNAME, error.name, error.message);
378       exit (EX_UNAVAILABLE);
379     }
380
381   if (!dbus_message_get_args (msg, &error, DBUS_TYPE_INVALID))
382     {
383       fprintf (stderr,
384           "%s: error from dbus-daemon: %s: %s\n",
385           PROGNAME, error.name, error.message);
386       exit (EX_UNAVAILABLE);
387     }
388
389   dbus_message_unref (reply);
390
391 #ifdef __linux__
392   if (systemd)
393     {
394       reply = dbus_connection_send_with_reply_and_block (conn, sd_msg, -1,
395           &error);
396
397       /* non-fatal, the main purpose of this thing is to communicate
398        * with dbus-daemon */
399       if (reply == NULL)
400         {
401           fprintf (stderr,
402               "%s: warning: error sending to systemd: %s: %s\n",
403               PROGNAME, error.name, error.message);
404         }
405       else if (!dbus_message_get_args (msg, &error, DBUS_TYPE_INVALID))
406         {
407           fprintf (stderr,
408               "%s: warning: error from systemd: %s: %s\n",
409               PROGNAME, error.name, error.message);
410         }
411
412       if (reply != NULL)
413         dbus_message_unref (reply);
414
415       dbus_message_unref (sd_msg);
416       dbus_error_free (&error);
417     }
418 #endif
419
420   dbus_message_unref (msg);
421   dbus_connection_unref (conn);
422   return 0;
423 }