[libvirt] [PATCH v2 0/4] Support for sending signals to guest processes

A follow up to https://www.redhat.com/archives/libvir-list/2012-November/msg01283.html Changed in this version - Switch pid to long long to allow process groups & 64-bit pids - Add explicit enum entries for RT signals - Do case insensitive parsing in virsh - Fix version numbers - Improve docs

From: "Daniel P. Berrange" <berrange@redhat.com> Add an API for sending signals to arbitrary processes in the guest OS. This is primarily useful for container based virt, but can be used for machine virt too, if there is a suitable guest agent, * include/libvirt/libvirt.h.in: Add virDomainSendProcessSignal and virDomainProcessSignal enum * src/driver.h: Driver entry point * src/libvirt.c, src/libvirt_public.syms: Impl for new API --- include/libvirt/libvirt.h.in | 97 ++++++++++++++++++++++++++++++++++++++++++++ src/driver.h | 7 ++++ src/libvirt.c | 84 ++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 1 + 4 files changed, 189 insertions(+) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index bd03315..0f6ff4c 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2989,6 +2989,103 @@ int virDomainSendKey(virDomainPtr domain, unsigned int flags); /* + * These just happen to match Linux signal numbers. The numbers + * will be mapped to whatever the SIGNUM is in the guest OS in + * question by the agent delivering the signal. The names are + * based on the POSIX / XSI signal standard though. + * + * Do not rely on all values matching Linux though. It is possible + * this enum might be extended with new signals which have no + * mapping in Linux. + */ +typedef enum { + VIR_DOMAIN_PROCESS_SIGNAL_NOP = 0, /* No constant in POSIX/Linux */ + VIR_DOMAIN_PROCESS_SIGNAL_HUP = 1, /* SIGHUP */ + VIR_DOMAIN_PROCESS_SIGNAL_INT = 2, /* SIGINT */ + VIR_DOMAIN_PROCESS_SIGNAL_QUIT = 3, /* SIGQUIT */ + VIR_DOMAIN_PROCESS_SIGNAL_ILL = 4, /* SIGILL */ + VIR_DOMAIN_PROCESS_SIGNAL_TRAP = 5, /* SIGTRAP */ + VIR_DOMAIN_PROCESS_SIGNAL_ABRT = 6, /* SIGABRT */ + VIR_DOMAIN_PROCESS_SIGNAL_BUS = 7, /* SIGBUS */ + VIR_DOMAIN_PROCESS_SIGNAL_FPE = 8, /* SIGFPE */ + VIR_DOMAIN_PROCESS_SIGNAL_KILL = 9, /* SIGKILL */ + + VIR_DOMAIN_PROCESS_SIGNAL_USR1 = 10, /* SIGUSR1 */ + VIR_DOMAIN_PROCESS_SIGNAL_SEGV = 11, /* SIGSEGV */ + VIR_DOMAIN_PROCESS_SIGNAL_USR2 = 12, /* SIGUSR2 */ + VIR_DOMAIN_PROCESS_SIGNAL_PIPE = 13, /* SIGPIPE */ + VIR_DOMAIN_PROCESS_SIGNAL_ALRM = 14, /* SIGALRM */ + VIR_DOMAIN_PROCESS_SIGNAL_TERM = 15, /* SIGTERM */ + VIR_DOMAIN_PROCESS_SIGNAL_STKFLT = 16, /* Not in POSIX (SIGSTKFLT on Linux )*/ + VIR_DOMAIN_PROCESS_SIGNAL_CHLD = 17, /* SIGCHLD */ + VIR_DOMAIN_PROCESS_SIGNAL_CONT = 18, /* SIGCONT */ + VIR_DOMAIN_PROCESS_SIGNAL_STOP = 19, /* SIGSTOP */ + + VIR_DOMAIN_PROCESS_SIGNAL_TSTP = 20, /* SIGTSTP */ + VIR_DOMAIN_PROCESS_SIGNAL_TTIN = 21, /* SIGTTIN */ + VIR_DOMAIN_PROCESS_SIGNAL_TTOU = 22, /* SIGTTOU */ + VIR_DOMAIN_PROCESS_SIGNAL_URG = 23, /* SIGURG */ + VIR_DOMAIN_PROCESS_SIGNAL_XCPU = 24, /* SIGXCPU */ + VIR_DOMAIN_PROCESS_SIGNAL_XFSZ = 25, /* SIGXFSZ */ + VIR_DOMAIN_PROCESS_SIGNAL_VTALRM = 26, /* SIGVTALRM */ + VIR_DOMAIN_PROCESS_SIGNAL_PROF = 27, /* SIGPROF */ + VIR_DOMAIN_PROCESS_SIGNAL_WINCH = 28, /* Not in POSIX (SIGWINCH on Linux) */ + VIR_DOMAIN_PROCESS_SIGNAL_POLL = 29, /* SIGPOLL (also known as SIGIO on Linux) */ + + VIR_DOMAIN_PROCESS_SIGNAL_PWR = 30, /* Not in POSIX (SIGPWR on Linux */ + VIR_DOMAIN_PROCESS_SIGNAL_SYS = 31, /* SIGSYS (also known as SIGUNUSED on Linux ) */ + VIR_DOMAIN_PROCESS_SIGNAL_RT0 = 32, /* SIGRTMIN */ + VIR_DOMAIN_PROCESS_SIGNAL_RT1 = 33, /* SIGRTMIN + 1 */ + VIR_DOMAIN_PROCESS_SIGNAL_RT2 = 34, /* SIGRTMIN + 2 */ + VIR_DOMAIN_PROCESS_SIGNAL_RT3 = 35, /* SIGRTMIN + 3 */ + VIR_DOMAIN_PROCESS_SIGNAL_RT4 = 36, /* SIGRTMIN + 4 */ + VIR_DOMAIN_PROCESS_SIGNAL_RT5 = 37, /* SIGRTMIN + 5 */ + VIR_DOMAIN_PROCESS_SIGNAL_RT6 = 38, /* SIGRTMIN + 6 */ + VIR_DOMAIN_PROCESS_SIGNAL_RT7 = 39, /* SIGRTMIN + 7 */ + + VIR_DOMAIN_PROCESS_SIGNAL_RT8 = 40, /* SIGRTMIN + 8 */ + VIR_DOMAIN_PROCESS_SIGNAL_RT9 = 41, /* SIGRTMIN + 9 */ + VIR_DOMAIN_PROCESS_SIGNAL_RT10 = 42, /* SIGRTMIN + 10 */ + VIR_DOMAIN_PROCESS_SIGNAL_RT11 = 43, /* SIGRTMIN + 11 */ + VIR_DOMAIN_PROCESS_SIGNAL_RT12 = 44, /* SIGRTMIN + 12 */ + VIR_DOMAIN_PROCESS_SIGNAL_RT13 = 45, /* SIGRTMIN + 13 */ + VIR_DOMAIN_PROCESS_SIGNAL_RT14 = 46, /* SIGRTMIN + 14 */ + VIR_DOMAIN_PROCESS_SIGNAL_RT15 = 47, /* SIGRTMIN + 15 */ + VIR_DOMAIN_PROCESS_SIGNAL_RT16 = 48, /* SIGRTMIN + 16 */ + VIR_DOMAIN_PROCESS_SIGNAL_RT17 = 49, /* SIGRTMIN + 17 */ + + VIR_DOMAIN_PROCESS_SIGNAL_RT18 = 50, /* SIGRTMIN + 18 */ + VIR_DOMAIN_PROCESS_SIGNAL_RT19 = 51, /* SIGRTMIN + 19 */ + VIR_DOMAIN_PROCESS_SIGNAL_RT20 = 52, /* SIGRTMIN + 20 */ + VIR_DOMAIN_PROCESS_SIGNAL_RT21 = 53, /* SIGRTMIN + 21 */ + VIR_DOMAIN_PROCESS_SIGNAL_RT22 = 54, /* SIGRTMIN + 22 */ + VIR_DOMAIN_PROCESS_SIGNAL_RT23 = 55, /* SIGRTMIN + 23 */ + VIR_DOMAIN_PROCESS_SIGNAL_RT24 = 56, /* SIGRTMIN + 24 */ + VIR_DOMAIN_PROCESS_SIGNAL_RT25 = 57, /* SIGRTMIN + 25 */ + VIR_DOMAIN_PROCESS_SIGNAL_RT26 = 58, /* SIGRTMIN + 26 */ + VIR_DOMAIN_PROCESS_SIGNAL_RT27 = 59, /* SIGRTMIN + 27 */ + + VIR_DOMAIN_PROCESS_SIGNAL_RT28 = 60, /* SIGRTMIN + 28 */ + VIR_DOMAIN_PROCESS_SIGNAL_RT29 = 61, /* SIGRTMIN + 29 */ + VIR_DOMAIN_PROCESS_SIGNAL_RT30 = 62, /* SIGRTMIN + 30 */ + VIR_DOMAIN_PROCESS_SIGNAL_RT31 = 63, /* SIGRTMIN + 31 */ + VIR_DOMAIN_PROCESS_SIGNAL_RT32 = 64, /* SIGRTMIN + 32 / SIGRTMAX */ + +#ifdef VIR_ENUM_SENTINELS + /* This is the "last" signal only in so much as none + * of the latter ones have named constants. They are + * just numeric offsets from RTMIN upwards + */ + VIR_DOMAIN_PROCESS_SIGNAL_LAST +#endif +} virDomainProcessSignal; + +int virDomainSendProcessSignal(virDomainPtr domain, + long long pid_value, + unsigned int signum, + unsigned int flags); + +/* * Deprecated calls */ virDomainPtr virDomainCreateLinux (virConnectPtr conn, diff --git a/src/driver.h b/src/driver.h index 5163840..9add96c 100644 --- a/src/driver.h +++ b/src/driver.h @@ -732,6 +732,12 @@ typedef int int nkeycodes, unsigned int flags); +typedef int + (*virDrvDomainSendProcessSignal)(virDomainPtr dom, + long long pid_value, + unsigned int signum, + unsigned int flags); + typedef char * (*virDrvDomainMigrateBegin3) (virDomainPtr domain, @@ -1100,6 +1106,7 @@ struct _virDriver { virDrvNodeSetMemoryParameters nodeSetMemoryParameters; virDrvNodeGetCPUMap nodeGetCPUMap; virDrvDomainFSTrim domainFSTrim; + virDrvDomainSendProcessSignal domainSendProcessSignal; }; typedef int diff --git a/src/libvirt.c b/src/libvirt.c index 4581394..fa3d58f 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -8590,6 +8590,90 @@ error: return -1; } + +/** + * virDomainSendProcessSignal: + * @domain: pointer to domain object + * @pid_value: a positive integer process ID, or negative integer process group ID + * @signum: a signal from the virDomainProcessSignal enum + * @flags: one of the virDomainProcessSignalFlag values + * + * Send a signal to the designated process in the guest + * + * The signal numbers must be taken from the virDomainProcessSignal + * enum. These will be translated to the corresponding signal + * number for the guest OS, by the guest agent delivering the + * signal. If there is no mapping from virDomainProcessSignal to + * the native OS signals, this API will report an error + * + * If @pid_value is an integer greater than zero, it is + * treated as a process ID. If @pid_value is an integer + * less than zero, it is treated as a process group ID. + * All the @pid_value numbers are from the container/guest + * namespace. The value zero is not valid. + * + * Not all hypervisors will support sending signals to + * arbitrary processes or process groups. If this API is + * implemented the minimum requirement is to be able to + * use @pid_value==1 (ie kill init). No other value is + * required to be supported. + * + * If the @signum is VIR_DOMAIN_PROCESS_SIGNAL_NOP then this + * API will simply report whether the process is running in + * the container/guest. + * + * Returns 0 in case of success, -1 in case of failure. + */ +int virDomainSendProcessSignal(virDomainPtr domain, + long long pid_value, + unsigned int signum, + unsigned int flags) +{ + virConnectPtr conn; + VIR_DOMAIN_DEBUG(domain, "pid=%lld, signum=%u flags=%x", + pid_value, signum, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + virCheckNonZeroArgGoto(pid_value, error); + + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (signum > VIR_DOMAIN_PROCESS_SIGNAL_LAST) { + virLibDomainError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + conn = domain->conn; + + if (conn->driver->domainSendProcessSignal) { + int ret; + ret = conn->driver->domainSendProcessSignal(domain, + pid_value, + signum, + flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(domain->conn); + return -1; +} + + /** * virDomainSetVcpus: * @domain: pointer to domain object, or NULL for Domain0 diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index f699e93..e3d63d3 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -577,6 +577,7 @@ LIBVIRT_1.0.0 { LIBVIRT_1.0.1 { global: virDomainFSTrim; + virDomainSendProcessSignal; } LIBVIRT_1.0.0; # .... define new API here using predicted next version number .... -- 1.7.11.7

Add an API for sending signals to arbitrary processes in the guest OS. This is primarily useful for container based virt, but can be used for machine virt too, if there is a suitable guest agent,
* include/libvirt/libvirt.h.in: Add virDomainSendProcessSignal and virDomainProcessSignal enum * src/driver.h: Driver entry point * src/libvirt.c, src/libvirt_public.syms: Impl for new API --- include/libvirt/libvirt.h.in | 97 ++++++++++++++++++++++++++++++++++++++++++++ src/driver.h | 7 ++++ src/libvirt.c | 84 ++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 1 + 4 files changed, 189 insertions(+)
+ * + * Do not rely on all values matching Linux though. It is possible
Why two spaces mid-sentence?
+ VIR_DOMAIN_PROCESS_SIGNAL_RT32 = 64, /* SIGRTMIN + 32 / SIGRTMAX */
Yes, this works better for me.
+ +#ifdef VIR_ENUM_SENTINELS + /* This is the "last" signal only in so much as none + * of the latter ones have named constants. They are + * just numeric offsets from RTMIN upwards
This comment is now out of date.
+/** + * virDomainSendProcessSignal: + * @domain: pointer to domain object + * @pid_value: a positive integer process ID, or negative integer process group ID + * @signum: a signal from the virDomainProcessSignal enum + * @flags: one of the virDomainProcessSignalFlag values + * + * Send a signal to the designated process in the guest + * + * The signal numbers must be taken from the virDomainProcessSignal + * enum. These will be translated to the corresponding signal + * number for the guest OS, by the guest agent delivering the + * signal. If there is no mapping from virDomainProcessSignal to + * the native OS signals, this API will report an error
s/$/./
+ * + * If @pid_value is an integer greater than zero, it is + * treated as a process ID. If @pid_value is an integer + * less than zero, it is treated as a process group ID. + * All the @pid_value numbers are from the container/guest + * namespace. The value zero is not valid. + * + * Not all hypervisors will support sending signals to + * arbitrary processes or process groups. If this API is + * implemented the minimum requirement is to be able to + * use @pid_value==1 (ie kill init). No other value is
s/ie/i.e./
+ + if (signum > VIR_DOMAIN_PROCESS_SIGNAL_LAST) { + virLibDomainError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + }
Drop this hunk (rather, move it to patch 4/4). Such a check should be done in the hypervisor driver, not in the entry point. Otherwise, if we add a new signal later, this hunk would prevent an older virsh from sending the new signal. ACK with those changes.

Add an API for sending signals to arbitrary processes in the guest OS. This is primarily useful for container based virt, but can be used for machine virt too, if there is a suitable guest agent,
+ + if (signum > VIR_DOMAIN_PROCESS_SIGNAL_LAST) { + virLibDomainError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + }
Drop this hunk (rather, move it to patch 4/4). Such a check should be done in the hypervisor driver, not in the entry point. Otherwise, if we add a new signal later, this hunk would prevent an older virsh from sending the new signal.
On the other hand, you _should_ add a check here that prevents signum==0, since we have documented that signum must be non-zero as part of the API contract.

Drop this hunk (rather, move it to patch 4/4). Such a check should be done in the hypervisor driver, not in the entry point. Otherwise, if we add a new signal later, this hunk would prevent an older virsh from sending the new signal.
On the other hand, you _should_ add a check here that prevents signum==0, since we have documented that signum must be non-zero as part of the API contract.
Arg. Can't type. signum==0 is valid, pid_value==0 is the forbidden item.

From: "Daniel P. Berrange" <berrange@redhat.com> * src/remote/remote_protocol.x: message definition * src/remote/remote_driver.c: Register driver function * src/remote_protocol-structs: Test case Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 10 +++++++++- src/remote_protocol-structs | 7 +++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 49d89be..25f20fc 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -6124,6 +6124,7 @@ static virDriver remote_driver = { .domainMigrateFinish3 = remoteDomainMigrateFinish3, /* 0.9.2 */ .domainMigrateConfirm3 = remoteDomainMigrateConfirm3, /* 0.9.2 */ .domainSendKey = remoteDomainSendKey, /* 0.9.3 */ + .domainSendProcessSignal = remoteDomainSendProcessSignal, /* 1.0.1 */ .domainBlockJobAbort = remoteDomainBlockJobAbort, /* 0.9.4 */ .domainGetBlockJobInfo = remoteDomainGetBlockJobInfo, /* 0.9.4 */ .domainBlockJobSetSpeed = remoteDomainBlockJobSetSpeed, /* 0.9.4 */ diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 31567e2..bdad9f0 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -1019,6 +1019,13 @@ struct remote_domain_send_key_args { unsigned int flags; }; +struct remote_domain_send_process_signal_args { + remote_nonnull_domain dom; + hyper pid_value; + unsigned int signum; + unsigned int flags; +}; + struct remote_domain_set_vcpus_args { remote_nonnull_domain dom; unsigned int nvcpus; @@ -3034,7 +3041,8 @@ enum remote_procedure { REMOTE_PROC_NETWORK_UPDATE = 291, /* autogen autogen priority:high */ REMOTE_PROC_DOMAIN_EVENT_PMSUSPEND_DISK = 292, /* autogen autogen */ REMOTE_PROC_NODE_GET_CPU_MAP = 293, /* skipgen skipgen */ - REMOTE_PROC_DOMAIN_FSTRIM = 294 /* autogen autogen */ + REMOTE_PROC_DOMAIN_FSTRIM = 294, /* autogen autogen */ + REMOTE_PROC_DOMAIN_SEND_PROCESS_SIGNAL = 295 /* autogen autogen */ /* * Notice how the entries are grouped in sets of 10 ? diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index d0d4f53..e7d05b8 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -672,6 +672,12 @@ struct remote_domain_send_key_args { } keycodes; u_int flags; }; +struct remote_domain_send_process_signal_args { + remote_nonnull_domain dom; + int64_t pid_value; + u_int signum; + u_int flags; +}; struct remote_domain_set_vcpus_args { remote_nonnull_domain dom; u_int nvcpus; @@ -2440,4 +2446,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_EVENT_PMSUSPEND_DISK = 292, REMOTE_PROC_NODE_GET_CPU_MAP = 293, REMOTE_PROC_DOMAIN_FSTRIM = 294, + REMOTE_PROC_DOMAIN_SEND_PROCESS_SIGNAL = 295, }; -- 1.7.11.7

* src/remote/remote_protocol.x: message definition * src/remote/remote_driver.c: Register driver function * src/remote_protocol-structs: Test case
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 10 +++++++++- src/remote_protocol-structs | 7 +++++++ 3 files changed, 17 insertions(+), 1 deletion(-)
ACK

From: "Daniel P. Berrange" <berrange@redhat.com> * tools/virsh.c: Add send-process-signal * tools/virsh.pod: Document new command Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- tools/virsh-domain.c | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 27 ++++++++++++++ 2 files changed, 130 insertions(+) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 6d5a0ec..e22255a 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -5901,6 +5901,108 @@ cleanup: } /* + * "send-process-signal" command + */ +static const vshCmdInfo info_send_process_signal[] = { + {"help", N_("Send signals to processes") }, + {"desc", N_("Send signals to processes in the guest") }, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_send_process_signal[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"pid", VSH_OT_DATA, VSH_OFLAG_REQ, N_("the process ID") }, + {"signame", VSH_OT_DATA, VSH_OFLAG_REQ, N_("the signal number or name") }, + {NULL, 0, 0, NULL} +}; + +VIR_ENUM_DECL(virDomainProcessSignal) +VIR_ENUM_IMPL(virDomainProcessSignal, + VIR_DOMAIN_PROCESS_SIGNAL_LAST, + "nop", "hup", "int", "quit", "ill", /* 0-4 */ + "trap", "abrt", "bus", "fpe", "kill", /* 5-9 */ + "usr1", "segv", "usr2", "pipe", "alrm", /* 10-14 */ + "term", "stkflt", "chld", "cont", "stop", /* 15-19 */ + "tstp", "ttin", "ttou", "urg", "xcpu", /* 20-24 */ + "xfsz", "vtalrm", "prof", "winch", "poll", /* 25-29 */ + "pwr", "sys", "rt0","rt1", "rt2", /* 30-34 */ + "rt3", "rt4", "rt5", "rt6", "rt7", /* 35-39 */ + "rt8", "rt9", "rt10", "rt11", "rt12", /* 40-44 */ + "rt13", "rt14", "rt15", "rt16", "rt17", /* 45-49 */ + "rt18", "rt19", "rt20", "rt21", "rt22", /* 50-54 */ + "rt23", "rt24", "rt25", "rt26", "rt27", /* 55-59 */ + "rt28", "rt29", "rt30", "rt31", "rt32") /* 60-64 */ + +static int getSignalNumber(vshControl *ctl, const char *signame) +{ + size_t i; + int signum; + char *lower = vshStrdup(ctl, signame); + + for (i = 0 ; signame[i] ; i++) + lower[i] = c_tolower(signame[i]); + + if (virStrToLong_i(signame, NULL, 10, &signum) >= 0) + goto cleanup; + + if (STRPREFIX(signame, "sig")) + signame += 3; + else if (STRPREFIX(signame, "sig_")) + signame += 4; + + if ((signum = virDomainProcessSignalTypeFromString(signame)) >= 0) + goto cleanup; + + signum = -1; +cleanup: + VIR_FREE(lower); + return signum; +} + +static bool +cmdSendProcessSignal(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom; + int ret = false; + const char *pidstr; + const char *signame; + long long pid_value; + int signum; + + if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + return false; + + if (vshCommandOptString(cmd, "pid", &pidstr) <= 0) { + vshError(ctl, "%s", _("missing argument")); + return false; + } + + if (vshCommandOptString(cmd, "signame", &signame) <= 0) { + vshError(ctl, "%s", _("missing argument")); + return false; + } + + if (virStrToLong_ll(pidstr, NULL, 10, &pid_value) < 0) { + vshError(ctl, _("malformed PID value: %s"), pidstr); + goto cleanup; + } + + if ((signum = getSignalNumber(ctl, signame)) < 0) { + vshError(ctl, _("malformed signal name: %s"), signame); + goto cleanup; + } + + if (virDomainSendProcessSignal(dom, pid_value, signum, 0) < 0) + goto cleanup; + + ret = true; + +cleanup: + virDomainFree(dom); + return ret; +} + +/* * "setmem" command */ static const vshCmdInfo info_setmem[] = { @@ -8398,6 +8500,7 @@ const vshCmdDef domManagementCmds[] = { {"edit", cmdEdit, opts_edit, info_edit, 0}, {"inject-nmi", cmdInjectNMI, opts_inject_nmi, info_inject_nmi, 0}, {"send-key", cmdSendKey, opts_send_key, info_send_key, 0}, + {"send-process-signal", cmdSendProcessSignal, opts_send_process_signal, info_send_process_signal, 0}, {"managedsave", cmdManagedSave, opts_managedsave, info_managedsave, 0}, {"managedsave-remove", cmdManagedSaveRemove, opts_managedsaveremove, info_managedsaveremove, 0}, diff --git a/tools/virsh.pod b/tools/virsh.pod index 353af66..1708cc4 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -1393,6 +1393,33 @@ B<Examples> # send a tab, held for 1 second virsh send-key --holdtime 1000 0xf +=item B<send-process-signal> I<domain-id> [I<--host-pid>] I<pid> I<signame> + +Send a signal I<signame> to the process identified by I<pid> running in +the virtual domain I<domain-id>. The I<pid> is a process ID in the virtual +domain namespace. + +The I<signame> argument may be either an integer signal constant number, +or one of the symbolic names: + + "nop", "hup", "int", "quit", "ill", + "trap", "abrt", "bus", "fpe", "kill", + "usr1", "segv", "usr2", "pipe", "alrm", + "term", "stkflt", "chld", "cont", "stop", + "tstp", "ttin", "ttou", "urg", "xcpu", + "xfsz", "vtalrm", "prof", "winch", "poll", + "pwr", "sys", "rtmin" + +The symbol name may optionally be prefixed with 'sig'. The 'rtmin' signal +also allows an optional suffix "+<number>" to refer to other real time +signals. + +B<Examples> + virsh send-process-signal myguest 1 15 + virsh send-process-signal myguest 1 term + virsh send-process-signal myguest 1 sigterm + virsh send-process-signal myguest 1 rtmin+12 + =item B<setmem> I<domain> B<size> [[I<--config>] [I<--live>] | [I<--current>]] -- 1.7.11.7

* tools/virsh.c: Add send-process-signal * tools/virsh.pod: Document new command
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- tools/virsh-domain.c | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 27 ++++++++++++++ 2 files changed, 130 insertions(+)
+VIR_ENUM_DECL(virDomainProcessSignal) +VIR_ENUM_IMPL(virDomainProcessSignal, + VIR_DOMAIN_PROCESS_SIGNAL_LAST, + "nop", "hup", "int", "quit", "ill", /* 0-4 */ + "trap", "abrt", "bus", "fpe", "kill", /* 5-9 */ + "usr1", "segv", "usr2", "pipe", "alrm", /* 10-14 */ + "term", "stkflt", "chld", "cont", "stop", /* 15-19 */ + "tstp", "ttin", "ttou", "urg", "xcpu", /* 20-24 */ + "xfsz", "vtalrm", "prof", "winch", "poll", /* 25-29 */ + "pwr", "sys", "rt0","rt1", "rt2", /* 30-34 */
This doesn't include rtmin...
+ + if (STRPREFIX(signame, "sig")) + signame += 3; + else if (STRPREFIX(signame, "sig_")) + signame += 4;
The else will never be reached. Swap these two conditions.
+ + if ((signum = virDomainProcessSignalTypeFromString(signame)) >= 0) + goto cleanup; +
...and you lost your handling of rtmin+1 here...
+++ b/tools/virsh.pod @@ -1393,6 +1393,33 @@ B<Examples> # send a tab, held for 1 second virsh send-key --holdtime 1000 0xf
+=item B<send-process-signal> I<domain-id> [I<--host-pid>] I<pid> I<signame>
--host-pid is unsupported; drop it from the synopsis.
+The I<signame> argument may be either an integer signal constant number, +or one of the symbolic names:
Maybe mention case-insensitive.
+ + "nop", "hup", "int", "quit", "ill", + "trap", "abrt", "bus", "fpe", "kill", + "usr1", "segv", "usr2", "pipe", "alrm", + "term", "stkflt", "chld", "cont", "stop", + "tstp", "ttin", "ttou", "urg", "xcpu", + "xfsz", "vtalrm", "prof", "winch", "poll", + "pwr", "sys", "rtmin"
This list doesn't mention rt0 and friends.
+ +The symbol name may optionally be prefixed with 'sig'. The 'rtmin' signal +also allows an optional suffix "+<number>" to refer to other real time +signals.
...so this part of the docs no longer matches the code...
+ +B<Examples> + virsh send-process-signal myguest 1 15 + virsh send-process-signal myguest 1 term + virsh send-process-signal myguest 1 sigterm + virsh send-process-signal myguest 1 rtmin+12
...and this last example needs fixing. But I'm okay giving ACK; I trust you to clean things up appropriately, and this patch only touches virsh.

From: "Daniel P. Berrange" <berrange@redhat.com> Implement the new API for sending signals to processes in a guest for the LXC driver. Only support sending signals to the init process for now, because - The kernel does not appear to expose the mapping between container PID numbers and host PID numbers anywhere in the host OS namespace - There is no race-free validate whether a host PID corresponds to a process in a container. * src/lxc/lxc_driver.c: Allow sending processes signals --- src/lxc/lxc_driver.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 991b593..96d5c3e 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -2666,6 +2666,78 @@ cleanup: return ret; } + +static int +lxcDomainSendProcessSignal(virDomainPtr dom, + long long pid_value, + unsigned int signum, + unsigned int flags) +{ + virLXCDriverPtr driver = dom->conn->privateData; + virDomainObjPtr vm = NULL; + virLXCDomainObjPrivatePtr priv; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + pid_t victim; + int ret = -1; + + virCheckFlags(0, -1); + + lxcDriverLock(driver); + virUUIDFormat(dom->uuid, uuidstr); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + lxcDriverUnlock(driver); + if (!vm) { + virReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + priv = vm->privateData; + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto cleanup; + } + + /* + * XXX if the kernel has /proc/$PID/ns/pid we can + * switch into container namespace & that way be + * able to kill any PID. Alternatively if there + * is a way to find a mapping of guest<->host PIDs + * we can kill that way. + */ + if (pid_value != 1) { + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", + _("Only the init process may be killed")); + goto cleanup; + } + + if (!priv->initpid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Init pid is not yet available")); + goto cleanup; + } + victim = priv->initpid; + + /* We're relying on fact libvirt header signal numbers + * are taken from Linux, to avoid mapping + */ + if (kill(victim, signum) < 0) { + virReportSystemError(errno, + _("Unable to send %d signal to process %d"), + signum, victim); + goto cleanup; + } + + ret = 0; + +cleanup: + if (vm) + virDomainObjUnlock(vm); + return ret; +} + + static int lxcListAllDomains(virConnectPtr conn, virDomainPtr **domains, @@ -2751,6 +2823,7 @@ static virDriver lxcDriver = { .nodeSuspendForDuration = nodeSuspendForDuration, /* 0.9.8 */ .nodeGetMemoryParameters = nodeGetMemoryParameters, /* 0.10.2 */ .nodeSetMemoryParameters = nodeSetMemoryParameters, /* 0.10.2 */ + .domainSendProcessSignal = lxcDomainSendProcessSignal, /* 1.0.1 */ }; static virStateDriver lxcStateDriver = { -- 1.7.11.7

Implement the new API for sending signals to processes in a guest for the LXC driver. Only support sending signals to the init process for now, because
- The kernel does not appear to expose the mapping between container PID numbers and host PID numbers anywhere in the host OS namespace - There is no race-free validate whether a host PID corresponds
s/validate/way to validate/ ACK
participants (2)
-
Daniel P. Berrange
-
Eric Blake