int virSystemdTerminateMachine(const char *name) { int ret; DBusConnection *conn; virError error; if (!name) return 0; memset(&error, 0, sizeof(error)); if ((ret = virSystemdHasMachined()) < 0) goto cleanup; ret = -1; if (!(conn = virDBusGetSystemBus())) goto cleanup; /* * The systemd DBus API we're invoking has the * following signature * * TerminateMachine(in s name); * * @name a host unique name for the machine. shows up * in 'ps' listing & similar */ VIR_DEBUG("Attempting to terminate machine via systemd"); if (virDBusCallMethod(conn, NULL, &error, "org.freedesktop.machine1", "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", "TerminateMachine", "s", name) < 0) goto cleanup; if (error.level == VIR_ERR_ERROR && STRNEQ_NULLABLE("org.freedesktop.machine1.NoSuchMachine", error.str1)) { virReportErrorObject(&error); goto cleanup; } ret = 0; cleanup: virResetError(&error); return ret; }
char * virSystemdGetMachineNameByPID(pid_t pid) { DBusConnection *conn; DBusMessage *reply = NULL; char *name = NULL, *object = NULL; if (virSystemdHasMachined() < 0) goto cleanup; if (!(conn = virDBusGetSystemBus())) goto cleanup; if (virDBusCallMethod(conn, &reply, NULL, "org.freedesktop.machine1", "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", "GetMachineByPID", "u", pid) < 0) goto cleanup; if (virDBusMessageDecode(reply, "o", &object) < 0) goto cleanup; virDBusMessageUnref(reply); reply = NULL; VIR_DEBUG("Domain with pid %lld has object path '%s'", (long long) pid, object); if (virDBusCallMethod(conn, &reply, NULL, "org.freedesktop.machine1", object, "org.freedesktop.DBus.Properties", "Get", "ss", "org.freedesktop.machine1.Machine", "Name") < 0) goto cleanup; if (virDBusMessageDecode(reply, "v", "s", &name) < 0) goto cleanup; VIR_DEBUG("Domain with pid %lld has machine name '%s'", (long long) pid, name); cleanup: VIR_FREE(object); virDBusMessageUnref(reply); return name; }
/* As per: http://www.freedesktop.org/wiki/Software/systemd/inhibit */ static void virNetDaemonCallInhibit(virNetDaemonPtr dmn, const char *what, const char *who, const char *why, const char *mode) { DBusMessage *message; DBusPendingCall *pendingReply; DBusConnection *systemBus; VIR_DEBUG("dmn=%p what=%s who=%s why=%s mode=%s", dmn, NULLSTR(what), NULLSTR(who), NULLSTR(why), NULLSTR(mode)); if (!(systemBus = virDBusGetSystemBus())) return; /* Only one outstanding call at a time */ if (dmn->autoShutdownCallingInhibit) return; message = dbus_message_new_method_call("org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "Inhibit"); if (message == NULL) return; dbus_message_append_args(message, DBUS_TYPE_STRING, &what, DBUS_TYPE_STRING, &who, DBUS_TYPE_STRING, &why, DBUS_TYPE_STRING, &mode, DBUS_TYPE_INVALID); pendingReply = NULL; if (dbus_connection_send_with_reply(systemBus, message, &pendingReply, 25*1000)) { dbus_pending_call_set_notify(pendingReply, virNetDaemonGotInhibitReply, dmn, NULL); dmn->autoShutdownCallingInhibit = true; } dbus_message_unref(message); }
static int virSystemdPMSupportTarget(const char *methodName, bool *result) { int ret; DBusConnection *conn; DBusMessage *message = NULL; char *response; ret = virDBusIsServiceEnabled("org.freedesktop.login1"); if (ret < 0) return ret; if ((ret = virDBusIsServiceRegistered("org.freedesktop.login1")) < 0) return ret; if (!(conn = virDBusGetSystemBus())) return -1; ret = -1; if (virDBusCallMethod(conn, &message, NULL, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", methodName, NULL) < 0) return ret; if ((ret = virDBusMessageRead(message, "s", &response)) < 0) goto cleanup; *result = STREQ("yes", response) || STREQ("challenge", response); ret = 0; cleanup: virDBusMessageUnref(message); VIR_FREE(response); return ret; }
int virSystemdTerminateMachine(const char *name, const char *drivername, bool privileged) { int ret; DBusConnection *conn; char *machinename = NULL; virError error; memset(&error, 0, sizeof(error)); ret = virDBusIsServiceEnabled("org.freedesktop.machine1"); if (ret < 0) goto cleanup; if ((ret = virDBusIsServiceRegistered("org.freedesktop.systemd1")) < 0) goto cleanup; ret = -1; if (!(conn = virDBusGetSystemBus())) goto cleanup; if (!(machinename = virSystemdMakeMachineName(name, drivername, privileged))) goto cleanup; /* * The systemd DBus API we're invoking has the * following signature * * TerminateMachine(in s name); * * @name a host unique name for the machine. shows up * in 'ps' listing & similar */ VIR_DEBUG("Attempting to terminate machine via systemd"); if (virDBusCallMethod(conn, NULL, &error, "org.freedesktop.machine1", "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", "TerminateMachine", "s", machinename) < 0) goto cleanup; if (error.code == VIR_ERR_ERROR && !STREQ_NULLABLE("org.freedesktop.machine1.NoSuchMachine", error.str1)) { virReportErrorObject(&error); goto cleanup; } ret = 0; cleanup: virResetError(&error); VIR_FREE(machinename); return ret; }
/** * virSystemdCreateMachine: * @name: driver unique name of the machine * @drivername: name of the virt driver * @privileged: whether driver is running privileged or per user * @uuid: globally unique UUID of the machine * @rootdir: root directory of machine filesystem * @pidleader: PID of the leader process * @iscontainer: true if a container, false if a VM * @nnicindexes: number of network interface indexes in list * @nicindexes: list of network interface indexes * @partition: name of the slice to place the machine in * * Returns 0 on success, -1 on fatal error, or -2 if systemd-machine is not available */ int virSystemdCreateMachine(const char *name, const char *drivername, bool privileged, const unsigned char *uuid, const char *rootdir, pid_t pidleader, bool iscontainer, size_t nnicindexes, int *nicindexes, const char *partition) { int ret; DBusConnection *conn; char *machinename = NULL; char *creatorname = NULL; char *slicename = NULL; static int hasCreateWithNetwork = 1; ret = virDBusIsServiceEnabled("org.freedesktop.machine1"); if (ret < 0) return ret; if ((ret = virDBusIsServiceRegistered("org.freedesktop.systemd1")) < 0) return ret; if (!(conn = virDBusGetSystemBus())) return -1; ret = -1; if (!(machinename = virSystemdMakeMachineName(name, drivername, privileged))) goto cleanup; if (virAsprintf(&creatorname, "libvirt-%s", drivername) < 0) goto cleanup; if (partition) { if (!(slicename = virSystemdMakeSliceName(partition))) goto cleanup; } else { if (VIR_STRDUP(slicename, "") < 0) goto cleanup; } /* * The systemd DBus APIs we're invoking have the * following signature(s) * * CreateMachineWithNetwork(in s name, * in ay id, * in s service, * in s class, * in u leader, * in s root_directory, * in ai nicindexes * in a(sv) scope_properties, * out o path); * * CreateMachine(in s name, * in ay id, * in s service, * in s class, * in u leader, * in s root_directory, * in a(sv) scope_properties, * out o path); * * @name a host unique name for the machine. shows up * in 'ps' listing & similar * * @id: a UUID of the machine, ideally matching /etc/machine-id * for containers * * @service: identifier of the client ie "libvirt-lxc" * * @class: either the string "container" or "vm" depending * on the type of machine * * @leader: main PID of the machine, either the host emulator * process, or the 'init' PID of the container * * @root_directory: the root directory of the container, if * this is known & visible in the host filesystem, or empty string * * @nicindexes: list of network interface indexes for the * host end of the VETH device pairs. * * @scope_properties:an array (not a dict!) of properties that are * passed on to PID 1 when creating a scope unit for your machine. * Will allow initial settings for the cgroup & similar. * * @path: a bus path returned for the machine object created, to * allow further API calls to be made against the object. * */ VIR_DEBUG("Attempting to create machine via systemd"); if (virAtomicIntGet(&hasCreateWithNetwork)) { virError error; memset(&error, 0, sizeof(error)); if (virDBusCallMethod(conn, NULL, &error, "org.freedesktop.machine1", "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", "CreateMachineWithNetwork", "sayssusa&ia(sv)", machinename, 16, uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15], creatorname, iscontainer ? "container" : "vm", (unsigned int)pidleader, rootdir ? rootdir : "", nnicindexes, nicindexes, 3, "Slice", "s", slicename, "After", "as", 1, "libvirtd.service", "Before", "as", 1, "libvirt-guests.service") < 0) goto cleanup; if (error.level == VIR_ERR_ERROR) { if (virDBusErrorIsUnknownMethod(&error)) { VIR_INFO("CreateMachineWithNetwork isn't supported, switching " "to legacy CreateMachine method for systemd-machined"); virResetError(&error); virAtomicIntSet(&hasCreateWithNetwork, 0); /* Could re-structure without Using goto, but this * avoids another atomic read which would trigger * another memory barrier */ goto fallback; } virReportErrorObject(&error); virResetError(&error); goto cleanup; } } else { fallback: if (virDBusCallMethod(conn, NULL, NULL, "org.freedesktop.machine1", "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", "CreateMachine", "sayssusa(sv)", machinename, 16, uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15], creatorname, iscontainer ? "container" : "vm", (unsigned int)pidleader, rootdir ? rootdir : "", 3, "Slice", "s", slicename, "After", "as", 1, "libvirtd.service", "Before", "as", 1, "libvirt-guests.service") < 0) goto cleanup; } ret = 0; cleanup: VIR_FREE(creatorname); VIR_FREE(machinename); VIR_FREE(slicename); return ret; }
/* * virPolkitCheckAuth: * @actionid: permission to check * @pid: client process ID * @startTime: process start time, or 0 * @uid: client process user ID * @details: NULL terminated (key, value) pair list * @allowInteraction: true if auth prompts are allowed * * Check if a client is authenticated with polkit * * Returns 0 on success, -1 on failure, -2 on auth denied */ int virPolkitCheckAuth(const char *actionid, pid_t pid, unsigned long long startTime, uid_t uid, const char **details, bool allowInteraction) { DBusConnection *sysbus; DBusMessage *reply = NULL; char **retdetails = NULL; size_t nretdetails = 0; bool is_authorized; bool is_challenge; bool is_dismissed = false; size_t i; int ret = -1; if (!(sysbus = virDBusGetSystemBus())) goto cleanup; VIR_INFO("Checking PID %lld running as %d", (long long) pid, uid); if (virDBusCallMethod(sysbus, &reply, NULL, "org.freedesktop.PolicyKit1", "/org/freedesktop/PolicyKit1/Authority", "org.freedesktop.PolicyKit1.Authority", "CheckAuthorization", "(sa{sv})sa&{ss}us", "unix-process", 3, "pid", "u", (unsigned int)pid, "start-time", "t", startTime, "uid", "i", (int)uid, actionid, virStringListLength(details) / 2, details, allowInteraction, "" /* cancellation ID */) < 0) goto cleanup; if (virDBusMessageRead(reply, "(bba&{ss})", &is_authorized, &is_challenge, &nretdetails, &retdetails) < 0) goto cleanup; for (i = 0; i < (nretdetails / 2); i++) { if (STREQ(retdetails[(i * 2)], "polkit.dismissed") && STREQ(retdetails[(i * 2) + 1], "true")) is_dismissed = true; } VIR_DEBUG("is auth %d is challenge %d", is_authorized, is_challenge); if (is_authorized) { ret = 0; } else { ret = -2; if (is_dismissed) virReportError(VIR_ERR_AUTH_CANCELLED, "%s", _("user cancelled authentication process")); else if (is_challenge) virReportError(VIR_ERR_AUTH_UNAVAILABLE, _("no polkit agent available to authenticate " "action '%s'"), actionid); else virReportError(VIR_ERR_AUTH_FAILED, "%s", _("access denied by policy")); } cleanup: virStringFreeListCount(retdetails, nretdetails); return ret; }
int virPolkitCheckAuth(const char *actionid, pid_t pid, unsigned long long startTime ATTRIBUTE_UNUSED, uid_t uid, const char **details, bool allowInteraction ATTRIBUTE_UNUSED) { PolKitCaller *pkcaller = NULL; PolKitAction *pkaction = NULL; PolKitContext *pkcontext = NULL; PolKitError *pkerr = NULL; PolKitResult pkresult; DBusError err; DBusConnection *sysbus; int ret = -1; if (details) { virReportError(VIR_ERR_AUTH_FAILED, "%s", _("Details not supported with polkit v0")); return -1; } if (!(sysbus = virDBusGetSystemBus())) goto cleanup; VIR_INFO("Checking PID %lld running as %d", (long long) pid, uid); dbus_error_init(&err); if (!(pkcaller = polkit_caller_new_from_pid(sysbus, pid, &err))) { VIR_DEBUG("Failed to lookup policy kit caller: %s", err.message); dbus_error_free(&err); goto cleanup; } if (!(pkaction = polkit_action_new())) { char ebuf[1024]; VIR_DEBUG("Failed to create polkit action %s", virStrerror(errno, ebuf, sizeof(ebuf))); goto cleanup; } polkit_action_set_action_id(pkaction, actionid); if (!(pkcontext = polkit_context_new()) || !polkit_context_init(pkcontext, &pkerr)) { char ebuf[1024]; VIR_DEBUG("Failed to create polkit context %s", (pkerr ? polkit_error_get_error_message(pkerr) : virStrerror(errno, ebuf, sizeof(ebuf)))); if (pkerr) polkit_error_free(pkerr); dbus_error_free(&err); goto cleanup; } # if HAVE_POLKIT_CONTEXT_IS_CALLER_AUTHORIZED pkresult = polkit_context_is_caller_authorized(pkcontext, pkaction, pkcaller, 0, &pkerr); if (pkerr && polkit_error_is_set(pkerr)) { VIR_DEBUG("Policy kit failed to check authorization %d %s", polkit_error_get_error_code(pkerr), polkit_error_get_error_message(pkerr)); goto cleanup; } # else pkresult = polkit_context_can_caller_do_action(pkcontext, pkaction, pkcaller); # endif if (pkresult != POLKIT_RESULT_YES) { VIR_DEBUG("Policy kit denied action %s from pid %lld, uid %d, result: %s", actionid, (long long) pid, uid, polkit_result_to_string_representation(pkresult)); ret = -2; goto cleanup; } VIR_DEBUG("Policy allowed action %s from pid %lld, uid %d", actionid, (long long)pid, (int)uid); ret = 0; cleanup: if (ret < 0) { virResetLastError(); virReportError(VIR_ERR_AUTH_FAILED, "%s", _("authentication failed")); } if (pkcontext) polkit_context_unref(pkcontext); if (pkcaller) polkit_caller_unref(pkcaller); if (pkaction) polkit_action_unref(pkaction); return ret; }
void virSystemdNotifyStartup(void) { #ifdef HAVE_SYS_UN_H const char *path; const char *msg = "READY=1"; int fd; struct sockaddr_un un = { .sun_family = AF_UNIX, }; struct iovec iov = { .iov_base = (char *)msg, .iov_len = strlen(msg), }; struct msghdr mh = { .msg_name = &un, .msg_iov = &iov, .msg_iovlen = 1, }; if (!(path = virGetEnvBlockSUID("NOTIFY_SOCKET"))) { VIR_DEBUG("Skipping systemd notify, not requested"); return; } /* NB sun_path field is *not* NUL-terminated, hence >, not >= */ if (strlen(path) > sizeof(un.sun_path)) { VIR_WARN("Systemd notify socket path '%s' too long", path); return; } memcpy(un.sun_path, path, strlen(path)); if (un.sun_path[0] == '@') un.sun_path[0] = '\0'; mh.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(path); fd = socket(AF_UNIX, SOCK_DGRAM, 0); if (fd < 0) { VIR_WARN("Unable to create socket FD"); return; } if (sendmsg(fd, &mh, MSG_NOSIGNAL) < 0) VIR_WARN("Failed to notify systemd"); VIR_FORCE_CLOSE(fd); #endif /* HAVE_SYS_UN_H */ } static int virSystemdPMSupportTarget(const char *methodName, bool *result) { int ret; DBusConnection *conn; DBusMessage *message = NULL; char *response; ret = virDBusIsServiceEnabled("org.freedesktop.login1"); if (ret < 0) return ret; if ((ret = virDBusIsServiceRegistered("org.freedesktop.login1")) < 0) return ret; if (!(conn = virDBusGetSystemBus())) return -1; ret = -1; if (virDBusCallMethod(conn, &message, NULL, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", methodName, NULL) < 0) return ret; if ((ret = virDBusMessageDecode(message, "s", &response)) < 0) goto cleanup; *result = STREQ("yes", response) || STREQ("challenge", response); ret = 0; cleanup: virDBusMessageUnref(message); VIR_FREE(response); return ret; } int virSystemdCanSuspend(bool *result) { return virSystemdPMSupportTarget("CanSuspend", result); } int virSystemdCanHibernate(bool *result) { return virSystemdPMSupportTarget("CanHibernate", result); } int virSystemdCanHybridSleep(bool *result) { return virSystemdPMSupportTarget("CanHybridSleep", result); }