machined-dbus.c revision bd16acf35e13a19cd2ded0a0c2ef774a98f73808
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2011 Lennart Poettering
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <pwd.h>
#include <systemd/sd-id128.h>
#include <systemd/sd-messages.h>
#include "machined.h"
#include "dbus-common.h"
#include "strv.h"
#include "mkdir.h"
#include "path-util.h"
#include "special.h"
#include "sleep-config.h"
#include "fileio-label.h"
#include "label.h"
#include "utf8.h"
#include "unit-name.h"
#include "bus-errors.h"
#include "virt.h"
#include "cgroup-util.h"
#define BUS_MANAGER_INTERFACE \
" <interface name=\"org.freedesktop.machine1.Manager\">\n" \
" <method name=\"GetMachine\">\n" \
" <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"machine\" type=\"o\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"GetMachineByPID\">\n" \
" <arg name=\"pid\" type=\"u\" direction=\"in\"/>\n" \
" <arg name=\"machine\" type=\"o\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"ListMachines\">\n" \
" <arg name=\"machines\" type=\"a(ssso)\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"CreateMachine\">\n" \
" <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"id\" type=\"ay\" direction=\"in\"/>\n" \
" <arg name=\"service\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"class\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"leader\" type=\"u\" direction=\"in\"/>\n" \
" <arg name=\"root_directory\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"scope_properties\" type=\"a(sv)\" direction=\"in\"/>\n" \
" <arg name=\"path\" type=\"o\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"KillMachine\">\n" \
" <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"who\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"signal\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"TerminateMachine\">\n" \
" <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
" <signal name=\"MachineNew\">\n" \
" <arg name=\"machine\" type=\"s\"/>\n" \
" <arg name=\"path\" type=\"o\"/>\n" \
" </signal>\n" \
" <signal name=\"MachineRemoved\">\n" \
" <arg name=\"machine\" type=\"s\"/>\n" \
" <arg name=\"path\" type=\"o\"/>\n" \
" </signal>\n" \
" </interface>\n"
#define INTROSPECTION_BEGIN \
"<node>\n" \
#define INTROSPECTION_END \
"</node>\n"
#define INTERFACES_LIST \
"org.freedesktop.machine1.Manager\0"
static bool valid_machine_name(const char *p) {
size_t l;
if (!filename_is_safe(p))
return false;
if (!ascii_is_valid(p))
return false;
l = strlen(p);
if (l < 1 || l> 64)
return false;
return true;
}
MachineClass c;
Machine *m;
int n, r;
void *v;
return -EINVAL;
if (!valid_machine_name(name) ||
!dbus_message_iter_next(&iter) ||
return -EINVAL;
dbus_message_iter_get_fixed_array(&sub, &v, &n);
if (n == 0)
id = SD_ID128_NULL;
else if (n == 16)
else
return -EINVAL;
if (!dbus_message_iter_next(&iter) ||
return -EINVAL;
if (!dbus_message_iter_next(&iter) ||
return -EINVAL;
else {
if (c < 0)
return -EINVAL;
}
if (!dbus_message_iter_next(&iter) ||
return -EINVAL;
if (!dbus_message_iter_next(&iter) ||
return -EINVAL;
return -EINVAL;
if (!dbus_message_iter_next(&iter) ||
return -EINVAL;
return -EEXIST;
if (leader <= 0) {
if (leader == 0)
return -EINVAL;
}
if (r < 0)
goto fail;
m->class = c;
if (!m->service) {
r = -ENOMEM;
goto fail;
}
}
if (!isempty(root_directory)) {
if (!m->root_directory) {
r = -ENOMEM;
goto fail;
}
}
r = machine_start(m, &sub);
if (r < 0)
goto fail;
return 0;
fail:
if (m)
return r;
}
void *userdata) {
int r;
assert(m);
const char *name;
char *p;
bool b;
if (!dbus_message_get_args(
&error,
if (!machine)
if (!reply)
goto oom;
p = machine_bus_path(machine);
if (!p)
goto oom;
free(p);
if (!b)
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "GetMachineByPID")) {
char *p;
bool b;
if (!dbus_message_get_args(
&error,
if (r <= 0)
if (!reply)
goto oom;
p = machine_bus_path(machine);
if (!p)
goto oom;
free(p);
if (!b)
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "ListMachines")) {
Iterator i;
if (!reply)
goto oom;
goto oom;
_cleanup_free_ char *p = NULL;
const char *class;
goto oom;
p = machine_bus_path(machine);
if (!p)
goto oom;
free(p);
goto oom;
}
goto oom;
}
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "CreateMachine")) {
r = bus_manager_create_machine(m, message);
if (r < 0)
} else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "KillMachine")) {
const char *swho;
const char *name;
if (!dbus_message_get_args(
&error,
else {
if (who < 0)
}
if (!machine)
if (r < 0)
if (!reply)
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "TerminateMachine")) {
const char *name;
if (!dbus_message_get_args(
&error,
if (!machine)
r = machine_stop(machine);
if (r < 0)
if (!reply)
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
char *introspection = NULL;
FILE *f;
Iterator i;
char *p;
if (!reply)
goto oom;
/* We roll our own introspection code here, instead of
* relying on bus_default_message_handler() because we
* need to generate our introspection string
* dynamically. */
if (!f)
goto oom;
fputs(INTROSPECTION_BEGIN, f);
if (p) {
fprintf(f, "<node name=\"machine/%s\"/>", p);
free(p);
}
}
fputs(INTROSPECTION_END, f);
if (ferror(f)) {
fclose(f);
goto oom;
}
fclose(f);
if (!introspection)
goto oom;
goto oom;
}
} else
if (reply) {
goto oom;
}
return DBUS_HANDLER_RESULT_HANDLED;
oom:
return DBUS_HANDLER_RESULT_NEED_MEMORY;
}
const DBusObjectPathVTable bus_manager_vtable = {
};
void *userdata) {
assert(m);
log_debug("Got message: %s %s %s", strna(dbus_message_get_sender(message)), strna(dbus_message_get_interface(message)), strna(dbus_message_get_member(message)));
goto finish;
}
if (mm) {
else {
dbus_set_error(&error, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
}
} else
}
}
} else if (dbus_message_is_signal(message, "org.freedesktop.DBus.Properties", "PropertiesChanged")) {
const char *path;
if (!path)
goto finish;
if (unit) {
if (mm)
}
goto finish;
}
if (mm)
dbus_bool_t b;
DBUS_TYPE_BOOLEAN, &b,
goto finish;
}
/* systemd finished reloading, let's recheck all our machines */
if (!b) {
Iterator i;
log_debug("System manager has been reloaded, rechecking machines...");
}
}
}
int type, r;
switch (type) {
case DBUS_TYPE_STRUCT: {
return log_oom();
if (r < 0)
return r;
return log_oom();
return 0;
}
case DBUS_TYPE_ARRAY: {
if (!dbus_message_iter_open_container(dest, DBUS_TYPE_ARRAY, dbus_message_iter_get_signature(&src_sub), &dest_sub))
return log_oom();
if (r < 0)
return r;
return log_oom();
return 0;
}
case DBUS_TYPE_VARIANT: {
if (!dbus_message_iter_open_container(dest, DBUS_TYPE_VARIANT, dbus_message_iter_get_signature(&src_sub), &dest_sub))
return log_oom();
if (r < 0)
return r;
return log_oom();
return 0;
}
case DBUS_TYPE_STRING:
case DBUS_TYPE_OBJECT_PATH:
case DBUS_TYPE_BYTE:
case DBUS_TYPE_BOOLEAN:
case DBUS_TYPE_UINT16:
case DBUS_TYPE_INT16:
case DBUS_TYPE_UINT32:
case DBUS_TYPE_INT32:
case DBUS_TYPE_UINT64:
case DBUS_TYPE_INT64:
case DBUS_TYPE_DOUBLE:
case DBUS_TYPE_SIGNATURE: {
const void *p;
return 0;
}
default:
return -EINVAL;
}
}
int r;
if (r < 0)
return r;
}
return 0;
}
int manager_start_scope(
const char *scope,
const char *slice,
const char *description,
char **job) {
const char *timeout_stop_property = "TimeoutStopUSec";
const char *pids_property = "PIDs";
const char *fail = "fail";
uint32_t u;
int r;
if (!slice)
slice = "";
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"StartTransientUnit");
if (!m)
return log_oom();
return log_oom();
const char *slice_property = "Slice";
return log_oom();
}
if (!isempty(description)) {
const char *description_property = "Description";
return log_oom();
}
/* cgroup empty notification is not available in containers
* currently. To make this less problematic, let's shorten the
* stop timeout for sessions, so that we don't wait
* forever. */
return log_oom();
u = pid;
return log_oom();
if (more_properties) {
if (r < 0)
return r;
}
return log_oom();
if (!reply)
return -EIO;
if (job) {
const char *j;
char *copy;
return -EIO;
if (!copy)
return -ENOMEM;
}
return 0;
}
const char *fail = "fail";
int r;
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"StopUnit",
&reply,
if (r < 0) {
if (job)
return 0;
}
return r;
}
if (job) {
const char *j;
char *copy;
log_error("Failed to parse reply.");
return -EIO;
}
if (!copy)
return -ENOMEM;
}
return 1;
}
int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, DBusError *error) {
const char *w;
int r;
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"KillUnit",
&reply,
DBUS_TYPE_STRING, &w,
if (r < 0) {
return r;
}
return 0;
}
const char *interface = "org.freedesktop.systemd1.Unit";
const char *property = "ActiveState";
const char *state;
int r;
if (!path)
return -ENOMEM;
"org.freedesktop.systemd1",
path,
"org.freedesktop.DBus.Properties",
"Get",
&reply,
&error,
if (r < 0) {
return true;
}
return false;
}
return r;
}
log_error("Failed to parse reply.");
return -EINVAL;
}
log_error("Failed to parse reply.");
return -EINVAL;
}
}
assert(m);
if (machine) {
if (_machine)
return 0;
}
if (!machine)
return -ENOMEM;
if (_machine)
return 0;
}
int r;
assert(m);
if (r < 0)
return r;
if (!mm)
return 0;
return 1;
}