mirror of
https://gitlab.freedesktop.org/dbus/dbus.git
synced 2025-12-20 15:00:08 +01:00
On 32 bit systems long will overflow in 2038, causing complete breakage. This is confirmed by running dbus's test suite on a 32 bit system with system time set to 2040 (and configured to use 64 bit time_t of course). Note that both timespec and timeval are specified with time_t for the seconds component. This should propagate everywhere where that data is passed and stored, but previously _dbus_get_monotonic_time() and _dbus_get_monotonic_time() would truncate it to long. Also add a function for parsing dbus_int64_t from files, as existing functions can only handle long. Signed-off-by: Alexander Kanavin <alex@linutronix.de>
705 lines
19 KiB
C
705 lines
19 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
/* dbus-run-session.c - run a child process in its own session
|
|
*
|
|
* Copyright © 2003-2006 Red Hat, Inc.
|
|
* Copyright © 2006 Thiago Macieira <thiago@kde.org>
|
|
* Copyright © 2011-2012 Nokia Corporation
|
|
* Copyright © 2018, 2021 Ralf Habacker
|
|
*
|
|
* SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
|
|
*
|
|
* Licensed under the Academic Free License version 2.1
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#ifndef _MSC_VER
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include <sys/types.h>
|
|
#ifdef DBUS_UNIX
|
|
#include <sys/wait.h>
|
|
#include <signal.h>
|
|
#include <dbus/dbus-sysdeps-unix.h>
|
|
#else
|
|
#include <dbus/dbus-internals.h>
|
|
#include <dbus/dbus-sysdeps-win.h>
|
|
#endif
|
|
#include "dbus/dbus.h"
|
|
#include "dbus/dbus-internals.h"
|
|
|
|
#include "tool-common.h"
|
|
|
|
#define MAX_ADDR_LEN 512
|
|
#define PIPE_READ_END 0
|
|
#define PIPE_WRITE_END 1
|
|
|
|
/* PROCESSES
|
|
*
|
|
* If you are in a shell and run "dbus-run-session myapp", here is what
|
|
* happens (compare and contrast with dbus-launch):
|
|
*
|
|
* shell
|
|
* \- dbus-run-session myapp
|
|
* \- dbus-daemon --nofork --print-address --session
|
|
* \- myapp
|
|
*
|
|
* All processes are long-running.
|
|
*
|
|
* When myapp exits, dbus-run-session kills dbus-daemon and terminates.
|
|
*
|
|
* If dbus-daemon exits, dbus-run-session warns and continues to run.
|
|
*
|
|
* PIPES
|
|
*
|
|
* dbus-daemon --print-address -> bus_address_pipe -> d-r-s
|
|
*/
|
|
|
|
static const char me[] = "dbus-run-session";
|
|
|
|
static void usage (int ecode) _DBUS_GNUC_NORETURN;
|
|
|
|
static void
|
|
usage (int ecode)
|
|
{
|
|
fprintf (stderr,
|
|
"%s [OPTIONS] [--] PROGRAM [ARGUMENTS]\n"
|
|
"%s --version\n"
|
|
"%s --help\n"
|
|
"\n"
|
|
"Options:\n"
|
|
"--dbus-daemon=BINARY run BINARY instead of dbus-daemon\n"
|
|
"--config-file=FILENAME pass to dbus-daemon instead of --session\n"
|
|
"\n",
|
|
me, me, me);
|
|
exit (ecode);
|
|
}
|
|
|
|
static void version (void) _DBUS_GNUC_NORETURN;
|
|
|
|
static void
|
|
version (void)
|
|
{
|
|
printf ("%s %s\n"
|
|
"Copyright (C) 2003-2006 Red Hat, Inc.\n"
|
|
"Copyright (C) 2006 Thiago Macieira\n"
|
|
"Copyright © 2011-2012 Nokia Corporation\n"
|
|
"Copyright © 2018, 2021 Ralf Habacker\n"
|
|
"\n"
|
|
"This is free software; see the source for copying conditions.\n"
|
|
"There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
|
|
me, VERSION);
|
|
exit (0);
|
|
}
|
|
|
|
#ifndef DBUS_WIN
|
|
static void oom (void) _DBUS_GNUC_NORETURN;
|
|
|
|
static void
|
|
oom (void)
|
|
{
|
|
fprintf (stderr, "%s: out of memory\n", me);
|
|
exit (1);
|
|
}
|
|
|
|
typedef enum
|
|
{
|
|
READ_STATUS_OK, /**< Read succeeded */
|
|
READ_STATUS_ERROR, /**< Some kind of error */
|
|
READ_STATUS_EOF /**< EOF returned */
|
|
} ReadStatus;
|
|
|
|
static ReadStatus
|
|
read_line (int fd,
|
|
char *buf,
|
|
size_t maxlen)
|
|
{
|
|
size_t bytes = 0;
|
|
ReadStatus retval;
|
|
|
|
memset (buf, '\0', maxlen);
|
|
maxlen -= 1; /* ensure nul term */
|
|
|
|
retval = READ_STATUS_OK;
|
|
|
|
while (1)
|
|
{
|
|
ssize_t chunk;
|
|
size_t to_read;
|
|
|
|
again:
|
|
to_read = maxlen - bytes;
|
|
|
|
if (to_read == 0)
|
|
break;
|
|
|
|
chunk = read (fd,
|
|
buf + bytes,
|
|
to_read);
|
|
if (chunk < 0 && errno == EINTR)
|
|
goto again;
|
|
|
|
if (chunk < 0)
|
|
{
|
|
retval = READ_STATUS_ERROR;
|
|
break;
|
|
}
|
|
else if (chunk == 0)
|
|
{
|
|
retval = READ_STATUS_EOF;
|
|
break; /* EOF */
|
|
}
|
|
else /* chunk > 0 */
|
|
bytes += chunk;
|
|
}
|
|
|
|
if (retval == READ_STATUS_EOF &&
|
|
bytes > 0)
|
|
retval = READ_STATUS_OK;
|
|
|
|
/* whack newline */
|
|
if (retval != READ_STATUS_ERROR &&
|
|
bytes > 0 &&
|
|
buf[bytes-1] == '\n')
|
|
buf[bytes-1] = '\0';
|
|
|
|
return retval;
|
|
}
|
|
|
|
static void
|
|
exec_dbus_daemon (const char *dbus_daemon,
|
|
int bus_address_pipe[2],
|
|
const char *config_file)
|
|
{
|
|
/* Child process, which execs dbus-daemon or dies trying */
|
|
#define MAX_FD_LEN 64
|
|
char write_address_fd_as_string[MAX_FD_LEN];
|
|
|
|
close (bus_address_pipe[PIPE_READ_END]);
|
|
|
|
/* Set all fds >= 3 close-on-execute, except for the one that can't be.
|
|
* We don't want dbus-daemon to inherit random fds we might have
|
|
* inherited from our caller. (Note that we *do* let the wrapped process
|
|
* inherit them in exec_app(), in an attempt to be as close as possible
|
|
* to being a transparent wrapper.) */
|
|
_dbus_fd_set_all_close_on_exec ();
|
|
_dbus_fd_clear_close_on_exec (bus_address_pipe[PIPE_WRITE_END]);
|
|
|
|
sprintf (write_address_fd_as_string, "%d", bus_address_pipe[PIPE_WRITE_END]);
|
|
|
|
execlp (dbus_daemon,
|
|
dbus_daemon,
|
|
"--nofork",
|
|
"--print-address", write_address_fd_as_string,
|
|
config_file ? "--config-file" : "--session",
|
|
config_file, /* has to be last in this varargs list */
|
|
NULL);
|
|
|
|
fprintf (stderr, "%s: failed to execute message bus daemon '%s': %s\n",
|
|
me, dbus_daemon, strerror (errno));
|
|
}
|
|
|
|
static void exec_app (int prog_arg, char **argv) _DBUS_GNUC_NORETURN;
|
|
|
|
static void
|
|
exec_app (int prog_arg, char **argv)
|
|
{
|
|
execvp (argv[prog_arg], argv + prog_arg);
|
|
|
|
fprintf (stderr, "%s: failed to exec '%s': %s\n", me, argv[prog_arg],
|
|
strerror (errno));
|
|
exit (1);
|
|
}
|
|
|
|
static int
|
|
run_session (const char *dbus_daemon,
|
|
const char *config_file,
|
|
char *bus_address,
|
|
char **argv,
|
|
int prog_arg)
|
|
{
|
|
pid_t bus_pid;
|
|
pid_t app_pid;
|
|
int bus_address_pipe[2] = { 0, 0 };
|
|
|
|
if (pipe (bus_address_pipe) < 0)
|
|
{
|
|
fprintf (stderr, "%s: failed to create pipe: %s\n", me, strerror (errno));
|
|
return 127;
|
|
}
|
|
|
|
/* Make sure our output buffers aren't redundantly printed by both the
|
|
* parent and the child */
|
|
fflush (stdout);
|
|
fflush (stderr);
|
|
|
|
bus_pid = fork ();
|
|
|
|
if (bus_pid < 0)
|
|
{
|
|
fprintf (stderr, "%s: failed to fork: %s\n", me, strerror (errno));
|
|
return 127;
|
|
}
|
|
|
|
if (bus_pid == 0)
|
|
{
|
|
/* child */
|
|
exec_dbus_daemon (dbus_daemon, bus_address_pipe, config_file);
|
|
/* not reached */
|
|
return 127;
|
|
}
|
|
|
|
close (bus_address_pipe[PIPE_WRITE_END]);
|
|
|
|
switch (read_line (bus_address_pipe[PIPE_READ_END], bus_address, MAX_ADDR_LEN))
|
|
{
|
|
case READ_STATUS_OK:
|
|
break;
|
|
|
|
case READ_STATUS_EOF:
|
|
fprintf (stderr, "%s: EOF reading address from bus daemon\n", me);
|
|
return 127;
|
|
break;
|
|
|
|
case READ_STATUS_ERROR:
|
|
fprintf (stderr, "%s: error reading address from bus daemon: %s\n",
|
|
me, strerror (errno));
|
|
return 127;
|
|
break;
|
|
|
|
default:
|
|
_dbus_assert_not_reached ("invalid read result");
|
|
}
|
|
|
|
close (bus_address_pipe[PIPE_READ_END]);
|
|
|
|
if (!dbus_setenv ("DBUS_SESSION_BUS_ADDRESS", bus_address) ||
|
|
!dbus_setenv ("DBUS_SESSION_BUS_PID", NULL) ||
|
|
!dbus_setenv ("DBUS_SESSION_BUS_WINDOWID", NULL) ||
|
|
!dbus_setenv ("DBUS_STARTER_ADDRESS", NULL) ||
|
|
!dbus_setenv ("DBUS_STARTER_BUS_TYPE", NULL))
|
|
oom ();
|
|
|
|
fflush (stdout);
|
|
fflush (stderr);
|
|
|
|
app_pid = fork ();
|
|
|
|
if (app_pid < 0)
|
|
{
|
|
fprintf (stderr, "%s: failed to fork: %s\n", me, strerror (errno));
|
|
return 127;
|
|
}
|
|
|
|
if (app_pid == 0)
|
|
{
|
|
/* child */
|
|
exec_app (prog_arg, argv);
|
|
/* not reached */
|
|
return 127;
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
int child_status;
|
|
pid_t child_pid = waitpid (-1, &child_status, 0);
|
|
|
|
if (child_pid == (pid_t) -1)
|
|
{
|
|
int errsv = errno;
|
|
|
|
if (errsv == EINTR)
|
|
continue;
|
|
|
|
/* shouldn't happen: the only other documented errors are ECHILD,
|
|
* which shouldn't happen because we terminate when all our children
|
|
* have died, and EINVAL, which would indicate programming error */
|
|
fprintf (stderr, "%s: waitpid() failed: %s\n", me, strerror (errsv));
|
|
return 127;
|
|
}
|
|
else if (child_pid == bus_pid)
|
|
{
|
|
/* no need to kill it, now */
|
|
bus_pid = 0;
|
|
|
|
if (WIFEXITED (child_status))
|
|
fprintf (stderr, "%s: dbus-daemon exited with code %d\n",
|
|
me, WEXITSTATUS (child_status));
|
|
else if (WIFSIGNALED (child_status))
|
|
fprintf (stderr, "%s: dbus-daemon terminated by signal %d\n",
|
|
me, WTERMSIG (child_status));
|
|
else
|
|
fprintf (stderr, "%s: dbus-daemon died or something\n", me);
|
|
}
|
|
else if (child_pid == app_pid)
|
|
{
|
|
if (bus_pid != 0)
|
|
kill (bus_pid, SIGTERM);
|
|
|
|
if (WIFEXITED (child_status))
|
|
return WEXITSTATUS (child_status);
|
|
|
|
/* if it died from a signal, behave like sh(1) */
|
|
if (WIFSIGNALED (child_status))
|
|
return 128 + WTERMSIG (child_status);
|
|
|
|
/* I give up (this should never be reached) */
|
|
fprintf (stderr, "%s: child process died or something\n", me);
|
|
return 127;
|
|
}
|
|
else
|
|
{
|
|
fprintf (stderr, "%s: ignoring unknown child process %ld\n", me,
|
|
(long) child_pid);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
static int
|
|
run_session (const char *dbus_daemon,
|
|
const char *config_file,
|
|
char *bus_address,
|
|
char **argv,
|
|
int prog_arg)
|
|
{
|
|
char *dbus_daemon_argv[5];
|
|
int ret = 127;
|
|
HANDLE server_handle = NULL;
|
|
HANDLE app_handle = NULL;
|
|
HANDLE ready_event_handle = NULL;
|
|
DWORD exit_code;
|
|
DBusString argv_strings[4];
|
|
DBusString address;
|
|
char **env = NULL;
|
|
DBusHashTable *env_table = NULL;
|
|
dbus_int64_t sec;
|
|
long usec;
|
|
dbus_bool_t result = TRUE;
|
|
char *key = NULL;
|
|
char *value = NULL;
|
|
DBusError error;
|
|
|
|
if (!_dbus_string_init (&argv_strings[0]))
|
|
result = FALSE;
|
|
if (!_dbus_string_init (&argv_strings[1]))
|
|
result = FALSE;
|
|
if (!_dbus_string_init (&argv_strings[2]))
|
|
result = FALSE;
|
|
if (!_dbus_string_init (&argv_strings[3]))
|
|
result = FALSE;
|
|
if (!_dbus_string_init (&address))
|
|
result = FALSE;
|
|
if (!result)
|
|
goto out;
|
|
|
|
/* The handle of this event is used by the dbus daemon
|
|
* to signal that connections are ready. */
|
|
dbus_error_init (&error);
|
|
ready_event_handle = _dbus_win_event_create_inheritable (&error);
|
|
if (ready_event_handle == NULL)
|
|
goto out;
|
|
|
|
/* run dbus daemon */
|
|
_dbus_get_real_time (&sec, &usec);
|
|
/* On Windows it's difficult to make use of --print-address to
|
|
* convert a listenable address into a connectable address, so instead
|
|
* we tell the temporary dbus-daemon to use the Windows autolaunch
|
|
* mechanism, with a unique scope that is shared by this dbus-daemon,
|
|
* the app process that defines its lifetime, and any other child
|
|
* processes they might have. */
|
|
_dbus_string_append_printf (&address, "autolaunch:scope=dbus-tmp-session-%" DBUS_INT64_MODIFIER "d%ld-" DBUS_PID_FORMAT, sec, usec, _dbus_getpid ());
|
|
_dbus_string_append_printf (&argv_strings[0], "%s", dbus_daemon);
|
|
if (config_file != NULL)
|
|
_dbus_string_append_printf (&argv_strings[1], "--config-file=%s", config_file);
|
|
else
|
|
_dbus_string_append_printf (&argv_strings[1], "--session");
|
|
_dbus_string_append_printf (&argv_strings[2], "--address=%s", _dbus_string_get_const_data (&address));
|
|
_dbus_string_append_printf (&argv_strings[3], "--ready-event-handle=%p", ready_event_handle);
|
|
dbus_daemon_argv[0] = _dbus_string_get_data (&argv_strings[0]);
|
|
dbus_daemon_argv[1] = _dbus_string_get_data (&argv_strings[1]);
|
|
dbus_daemon_argv[2] = _dbus_string_get_data (&argv_strings[2]);
|
|
dbus_daemon_argv[3] = _dbus_string_get_data (&argv_strings[3]);
|
|
dbus_daemon_argv[4] = NULL;
|
|
|
|
server_handle = _dbus_spawn_program (dbus_daemon, dbus_daemon_argv, NULL, TRUE, &error);
|
|
if (server_handle == NULL)
|
|
goto out;
|
|
|
|
/* wait until dbus-daemon is ready for connections */
|
|
if (ready_event_handle != NULL)
|
|
{
|
|
DWORD status;
|
|
HANDLE events[2];
|
|
|
|
_dbus_verbose ("Wait until dbus-daemon is ready for connections (event handle %p)\n", ready_event_handle);
|
|
|
|
events[0] = ready_event_handle;
|
|
events[1] = server_handle;
|
|
status = WaitForMultipleObjects (2, events, FALSE, 30000);
|
|
|
|
switch (status)
|
|
{
|
|
case WAIT_OBJECT_0:
|
|
/* ready event signalled, everything is okay */
|
|
break;
|
|
|
|
case WAIT_OBJECT_0 + 1:
|
|
/* dbus-daemon process has exited */
|
|
dbus_set_error (&error, DBUS_ERROR_SPAWN_CHILD_EXITED, "dbus-daemon exited before signalling ready");
|
|
goto out;
|
|
|
|
case WAIT_FAILED:
|
|
_dbus_win_set_error_from_last_error (&error, "Unable to wait for server readiness (handle %p)", ready_event_handle);
|
|
goto out;
|
|
|
|
case WAIT_TIMEOUT:
|
|
/* GetLastError() is not set */
|
|
dbus_set_error (&error, DBUS_ERROR_TIMEOUT, "Timed out waiting for server readiness or exit (handle %p)", ready_event_handle);
|
|
goto out;
|
|
|
|
default:
|
|
/* GetLastError() is probably not set? */
|
|
dbus_set_error (&error, DBUS_ERROR_FAILED, "Unknown result '%lu' while waiting for server readiness (handle %p)", status, ready_event_handle);
|
|
goto out;
|
|
}
|
|
_dbus_verbose ("Got signal that dbus-daemon is ready for connections\n");
|
|
}
|
|
|
|
/* run app */
|
|
env = _dbus_get_environment ();
|
|
env_table = _dbus_hash_table_new (DBUS_HASH_STRING,
|
|
dbus_free,
|
|
dbus_free);
|
|
if (!_dbus_hash_table_from_array (env_table, env, '='))
|
|
{
|
|
goto out;
|
|
}
|
|
|
|
/* replace DBUS_SESSION_BUS_ADDRESS in environment */
|
|
if (!_dbus_string_steal_data (&address, &value))
|
|
goto out;
|
|
|
|
key = _dbus_strdup ("DBUS_SESSION_BUS_ADDRESS");
|
|
|
|
if (key == NULL)
|
|
goto out;
|
|
|
|
if (_dbus_hash_table_insert_string (env_table, key, value))
|
|
{
|
|
/* env_table took ownership, do not free separately */
|
|
key = NULL;
|
|
value = NULL;
|
|
}
|
|
else
|
|
{
|
|
/* we still own key and value, the cleanup code will free them */
|
|
goto out;
|
|
}
|
|
|
|
_dbus_hash_table_remove_string (env_table, "DBUS_STARTER_ADDRESS");
|
|
_dbus_hash_table_remove_string (env_table, "DBUS_STARTER_BUS_TYPE");
|
|
_dbus_hash_table_remove_string (env_table, "DBUS_SESSION_BUS_PID");
|
|
_dbus_hash_table_remove_string (env_table, "DBUS_SESSION_BUS_WINDOWID");
|
|
|
|
dbus_free_string_array (env);
|
|
env = _dbus_hash_table_to_array (env_table, '=');
|
|
if (!env)
|
|
goto out;
|
|
|
|
app_handle = _dbus_spawn_program (argv[prog_arg], argv + prog_arg, env, FALSE, &error);
|
|
if (app_handle == NULL)
|
|
goto out;
|
|
|
|
WaitForSingleObject (app_handle, INFINITE);
|
|
if (!GetExitCodeProcess (app_handle, &exit_code))
|
|
{
|
|
_dbus_win_set_error_from_last_error (&error, "Could not fetch exit code");
|
|
goto out;
|
|
}
|
|
ret = exit_code;
|
|
|
|
out:
|
|
if (dbus_error_is_set (&error))
|
|
tool_stderr_error (me, &error);
|
|
dbus_error_free (&error);
|
|
TerminateProcess (server_handle, 0);
|
|
if (server_handle != NULL)
|
|
CloseHandle (server_handle);
|
|
if (app_handle != NULL)
|
|
CloseHandle (app_handle);
|
|
if (ready_event_handle != NULL)
|
|
_dbus_win_event_free (ready_event_handle, NULL);
|
|
_dbus_string_free (&argv_strings[0]);
|
|
_dbus_string_free (&argv_strings[1]);
|
|
_dbus_string_free (&argv_strings[2]);
|
|
_dbus_string_free (&argv_strings[3]);
|
|
_dbus_string_free (&address);
|
|
dbus_free_string_array (env);
|
|
if (env_table != NULL)
|
|
_dbus_hash_table_unref (env_table);
|
|
dbus_free (key);
|
|
dbus_free (value);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
int
|
|
main (int argc, char **argv)
|
|
{
|
|
int prog_arg = 0;
|
|
const char *config_file = NULL;
|
|
const char *dbus_daemon = NULL;
|
|
char bus_address[MAX_ADDR_LEN] = { 0 };
|
|
const char *prev_arg = NULL;
|
|
int i = 1;
|
|
int requires_arg = 0;
|
|
|
|
while (i < argc)
|
|
{
|
|
const char *arg = argv[i];
|
|
|
|
if (requires_arg)
|
|
{
|
|
const char **arg_dest;
|
|
|
|
assert (prev_arg != NULL);
|
|
|
|
if (strcmp (prev_arg, "--config-file") == 0)
|
|
{
|
|
arg_dest = &config_file;
|
|
}
|
|
else if (strcmp (prev_arg, "--dbus-daemon") == 0)
|
|
{
|
|
arg_dest = &dbus_daemon;
|
|
}
|
|
else
|
|
{
|
|
/* shouldn't happen */
|
|
fprintf (stderr, "%s: internal error: %s not fully implemented\n",
|
|
me, prev_arg);
|
|
return 127;
|
|
}
|
|
|
|
if (*arg_dest != NULL)
|
|
{
|
|
fprintf (stderr, "%s: %s given twice\n", me, prev_arg);
|
|
return 127;
|
|
}
|
|
|
|
*arg_dest = arg;
|
|
requires_arg = 0;
|
|
prev_arg = arg;
|
|
++i;
|
|
continue;
|
|
}
|
|
|
|
if (strcmp (arg, "--help") == 0 ||
|
|
strcmp (arg, "-h") == 0 ||
|
|
strcmp (arg, "-?") == 0)
|
|
{
|
|
usage (0);
|
|
}
|
|
else if (strcmp (arg, "--version") == 0)
|
|
{
|
|
version ();
|
|
}
|
|
else if (strstr (arg, "--config-file=") == arg)
|
|
{
|
|
const char *file;
|
|
|
|
if (config_file != NULL)
|
|
{
|
|
fprintf (stderr, "%s: --config-file given twice\n", me);
|
|
return 127;
|
|
}
|
|
|
|
file = strchr (arg, '=');
|
|
++file;
|
|
|
|
config_file = file;
|
|
}
|
|
else if (strstr (arg, "--dbus-daemon=") == arg)
|
|
{
|
|
const char *file;
|
|
|
|
if (dbus_daemon != NULL)
|
|
{
|
|
fprintf (stderr, "%s: --dbus-daemon given twice\n", me);
|
|
return 127;
|
|
}
|
|
|
|
file = strchr (arg, '=');
|
|
++file;
|
|
|
|
dbus_daemon = file;
|
|
}
|
|
else if (strcmp (arg, "--config-file") == 0 ||
|
|
strcmp (arg, "--dbus-daemon") == 0)
|
|
{
|
|
requires_arg = 1;
|
|
}
|
|
else if (arg[0] == '-')
|
|
{
|
|
if (strcmp (arg, "--") != 0)
|
|
{
|
|
fprintf (stderr, "%s: option '%s' is unknown\n", me, arg);
|
|
return 127;
|
|
}
|
|
else
|
|
{
|
|
prog_arg = i + 1;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
prog_arg = i;
|
|
break;
|
|
}
|
|
|
|
prev_arg = arg;
|
|
++i;
|
|
}
|
|
|
|
/* "dbus-run-session" and "dbus-run-session ... --" are not allowed:
|
|
* there must be something to run */
|
|
if (prog_arg < 1 || prog_arg >= argc)
|
|
{
|
|
fprintf (stderr, "%s: a non-option argument is required\n", me);
|
|
return 127;
|
|
}
|
|
|
|
if (requires_arg)
|
|
{
|
|
fprintf (stderr, "%s: option '%s' requires an argument\n", me, prev_arg);
|
|
return 127;
|
|
}
|
|
|
|
if (dbus_daemon == NULL)
|
|
dbus_daemon = "dbus-daemon";
|
|
|
|
return run_session (dbus_daemon, config_file, bus_address, argv, prog_arg);
|
|
}
|