From patchwork Thu Nov 14 13:06:53 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: "Wieczorkiewicz, Pawel" X-Patchwork-Id: 11243711 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 3F53014BC for ; Thu, 14 Nov 2019 13:09:41 +0000 (UTC) Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 1051D206DC for ; Thu, 14 Nov 2019 13:09:41 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=amazon.de header.i=@amazon.de header.b="r1xzZGNR" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 1051D206DC Authentication-Results: mail.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=amazon.de Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=xen-devel-bounces@lists.xenproject.org Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.89) (envelope-from ) id 1iVErI-0007gV-W7; Thu, 14 Nov 2019 13:08:25 +0000 Received: from all-amaz-eas1.inumbo.com ([34.197.232.57] helo=us1-amaz-eas2.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.89) (envelope-from ) id 1iVErH-0007fL-GO for xen-devel@lists.xenproject.org; Thu, 14 Nov 2019 13:08:23 +0000 X-Inumbo-ID: cfa7aa49-06df-11ea-a24c-12813bfff9fa Received: from smtp-fw-9102.amazon.com (unknown [207.171.184.29]) by us1-amaz-eas2.inumbo.com (Halon) with ESMTPS id cfa7aa49-06df-11ea-a24c-12813bfff9fa; Thu, 14 Nov 2019 13:08:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=amazon.de; i=@amazon.de; q=dns/txt; s=amazon201209; t=1573736893; x=1605272893; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=S5wJZkZNgEvgR4OD1RjH57qgoUjEHOe2x5Y16wwBsd0=; b=r1xzZGNRc/Vqf1qoXuKDYif1NXeGFZLlNNhXn0BcCeqIe6G2AFsJ9Q2I gpEhwxkYgd7RGgBxH6GonslVBbANLFQGOpMNKu2bSCzmiKmqTzYH6u+pM S0KTgZ7WkMgnXRfsXIh/3fDVBmQBcC+AJXoltpS3BkjS6JJ+R9qG0r3iM c=; IronPort-SDR: uM2wHZxNmBYXZiwOQtvGwh4BHsMoxOHe1FDmWY5x0SntbisQry6x5rCT/+Tqkk1ldQlcqixqVu qcnuTNUZrsFA== X-IronPort-AV: E=Sophos;i="5.68,304,1569283200"; d="scan'208";a="7280397" Received: from sea32-co-svc-lb4-vlan3.sea.corp.amazon.com (HELO email-inbound-relay-1a-807d4a99.us-east-1.amazon.com) ([10.47.23.38]) by smtp-border-fw-out-9102.sea19.amazon.com with ESMTP; 14 Nov 2019 13:08:11 +0000 Received: from EX13MTAUEA001.ant.amazon.com (iad55-ws-svc-p15-lb9-vlan3.iad.amazon.com [10.40.159.166]) by email-inbound-relay-1a-807d4a99.us-east-1.amazon.com (Postfix) with ESMTPS id 4A110A2B28; Thu, 14 Nov 2019 13:08:09 +0000 (UTC) Received: from EX13D03EUA002.ant.amazon.com (10.43.165.166) by EX13MTAUEA001.ant.amazon.com (10.43.61.82) with Microsoft SMTP Server (TLS) id 15.0.1367.3; Thu, 14 Nov 2019 13:07:59 +0000 Received: from EX13MTAUWA001.ant.amazon.com (10.43.160.58) by EX13D03EUA002.ant.amazon.com (10.43.165.166) with Microsoft SMTP Server (TLS) id 15.0.1367.3; Thu, 14 Nov 2019 13:07:58 +0000 Received: from dev-dsk-wipawel-1a-0c4e6d58.eu-west-1.amazon.com (10.4.134.33) by mail-relay.amazon.com (10.43.160.118) with Microsoft SMTP Server id 15.0.1367.3 via Frontend Transport; Thu, 14 Nov 2019 13:07:56 +0000 From: Pawel Wieczorkiewicz To: Date: Thu, 14 Nov 2019 13:06:53 +0000 Message-ID: <20191114130653.51185-13-wipawel@amazon.de> X-Mailer: git-send-email 2.16.5 In-Reply-To: <20191114130653.51185-1-wipawel@amazon.de> References: <20191114130653.51185-1-wipawel@amazon.de> MIME-Version: 1.0 Precedence: Bulk Subject: [Xen-devel] [PATCH v5 12/12] livepatch: Add python bindings for livepatch operations X-BeenThere: xen-devel@lists.xenproject.org X-Mailman-Version: 2.1.23 List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Cc: Pawel Wieczorkiewicz , Ian Jackson , mpohlack@amazon.com, Wei Liu , =?utf-8?q?Marek_Marczykowski-G=C3=B3recki?= Errors-To: xen-devel-bounces@lists.xenproject.org Sender: "Xen-devel" Extend the XC python bindings library to support also all common livepatch operations and actions. Add the python bindings for the following operations: - status (pyxc_livepatch_status): Requires a payload name as an input. Returns a status dict containing a state string and a return code integer. - action (pyxc_livepatch_action): Requires a payload name and an action id as an input. Timeout and flags are optional parameters. Returns None or throws an exception. - upload (pyxc_livepatch_upload): Requires a payload name and a module's filename as an input. Returns None or throws an exception. - list (pyxc_livepatch_list): Takes no parameters. Returns a list of dicts containing each payload's: * name as a string * state as a string * return code as an integer * list of metadata key=value strings Each functions throws an exception error based on the errno value received from its corresponding libxc function call. Signed-off-by: Pawel Wieczorkiewicz Reviewed-by: Martin Mazein Reviewed-by: Andra-Irina Paraschiv Reviewed-by: Leonard Foerster Reviewed-by: Norbert Manthey Acked-by: Marek Marczykowski-Górecki Reviewed-by: Ross Lagerwall --- Changed since v4: * changed flags field type from uint64_t to uint32_t * fixed leaking fd in pyxc_livepatch_upload() Changed since v3: * return None instead of integer 0 from pyxc_livepatch_action() and pyxc_livepatch_upload() * use fstat() instead of stat() * simplify error condition handling code for pyxc_livepatch_upload and also save and restore errno value * check done and left values to handle errors in pyxc_livepatch_list() * use PyList_SET_ITEM() to avoid the need for PyDECREF Changed since v1: * changed PyList_Append() with PyList_SetItem() as requested by Marek --- tools/python/xen/lowlevel/xc/xc.c | 268 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 268 insertions(+) diff --git a/tools/python/xen/lowlevel/xc/xc.c b/tools/python/xen/lowlevel/xc/xc.c index 44d3606141..9a1d3b6b92 100644 --- a/tools/python/xen/lowlevel/xc/xc.c +++ b/tools/python/xen/lowlevel/xc/xc.c @@ -1979,6 +1979,225 @@ static PyObject *pyflask_access(PyObject *self, PyObject *args, return Py_BuildValue("i",ret); } +static PyObject *pyxc_livepatch_status(XcObject *self, + PyObject *args, + PyObject *kwds) +{ + xen_livepatch_status_t status; + PyObject *info_dict = NULL; + char *name; + int rc; + + static char *kwd_list[] = { "name", NULL }; + + if ( !PyArg_ParseTupleAndKeywords(args, kwds, "s", kwd_list, &name) ) + goto error; + + rc = xc_livepatch_get(self->xc_handle, name, &status); + if ( rc ) + goto error; + + info_dict = Py_BuildValue( + "{s:i,s:i}", + "state", status.state, + "rc", status.rc); + +error: + return info_dict ?: pyxc_error_to_exception(self->xc_handle); +} + +static PyObject *pyxc_livepatch_action(XcObject *self, + PyObject *args, + PyObject *kwds) +{ + int (*action_func)(xc_interface *xch, char *name, uint32_t timeout, uint32_t flags); + char *name; + unsigned int action; + uint32_t timeout; + uint32_t flags; + int rc = -1; + + static char *kwd_list[] = { "name", "action", "timeout", "flags", NULL }; + + if ( !PyArg_ParseTupleAndKeywords(args, kwds, "sI|Ik", kwd_list, + &name, &action, &timeout, &flags) ) + goto error; + + switch (action) + { + case LIVEPATCH_ACTION_UNLOAD: + action_func = xc_livepatch_unload; + break; + case LIVEPATCH_ACTION_REVERT: + action_func = xc_livepatch_revert; + break; + case LIVEPATCH_ACTION_APPLY: + action_func = xc_livepatch_apply; + break; + case LIVEPATCH_ACTION_REPLACE: + action_func = xc_livepatch_replace; + break; + default: + goto error; + } + + rc = action_func(self->xc_handle, name, timeout, flags); + +error: + return rc ? pyxc_error_to_exception(self->xc_handle) : Py_None; +} + +static PyObject *pyxc_livepatch_upload(XcObject *self, + PyObject *args, + PyObject *kwds) +{ + unsigned char *fbuf = MAP_FAILED; + char *name, *filename; + struct stat buf; + int fd = 0, rc = -1, saved_errno; + ssize_t len; + + static char *kwd_list[] = { "name", "filename", NULL }; + + if ( !PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwd_list, + &name, &filename)) + goto error; + + fd = open(filename, O_RDONLY); + if ( fd < 0 ) + goto error; + + if ( fstat(fd, &buf) != 0 ) + goto error_fd; + + len = buf.st_size; + fbuf = mmap(0, len, PROT_READ, MAP_PRIVATE, fd, 0); + if ( fbuf == MAP_FAILED ) + goto error_fd; + + rc = xc_livepatch_upload(self->xc_handle, name, fbuf, len); + + saved_errno = errno; + munmap(fbuf, len); + errno = saved_errno; + +error_fd: + close(fd); +error: + return rc ? pyxc_error_to_exception(self->xc_handle) : Py_None; +} + +static PyObject *pyxc_livepatch_list(XcObject *self) +{ + PyObject *list = Py_None; + unsigned int nr, done, left, i; + xen_livepatch_status_t *info = NULL; + char *name = NULL; + char *metadata = NULL; + uint32_t *len = NULL; + uint32_t *metadata_len = NULL; + uint32_t name_total_size, metadata_total_size; + uint32_t name_off, metadata_off; + int rc; + + done = left = 0; + rc = xc_livepatch_list_get_sizes(self->xc_handle, &nr, + &name_total_size, &metadata_total_size); + if ( rc ) + goto error; + + if ( nr == 0 ) + return PyList_New(0); + + rc = ENOMEM; + info = malloc(nr * sizeof(*info)); + if ( !info ) + goto error; + + name = malloc(name_total_size * sizeof(*name)); + if ( !name ) + goto error; + + len = malloc(nr * sizeof(*len)); + if ( !len ) + goto error; + + metadata = malloc(metadata_total_size * sizeof(*metadata)); + if ( !metadata ) + goto error; + + metadata_len = malloc(nr * sizeof(*metadata_len)); + if ( !metadata_len ) + goto error; + + rc = xc_livepatch_list(self->xc_handle, nr, 0, info, + name, len, name_total_size, + metadata, metadata_len, metadata_total_size, + &done, &left); + if ( rc ) + goto error; + + if ( done != nr || left > 0 ) + { + rc = EFAULT; + goto error; + } + + list = PyList_New(done); + name_off = metadata_off = 0; + for ( i = 0; i < done; i++ ) + { + PyObject *info_dict, *metadata_list; + char *name_str, *metadata_str; + + name_str = name + name_off; + metadata_str = metadata + metadata_off; + + metadata_list = PyList_New(0); + for ( char *s = metadata_str; s < metadata_str + metadata_len[i]; s += strlen(s) + 1 ) + { + PyObject *field = Py_BuildValue("s", s); + if ( field == NULL ) + { + Py_DECREF(list); + Py_DECREF(metadata_list); + rc = EFAULT; + goto error; + } + + PyList_Append(metadata_list, field); + Py_DECREF(field); + } + + info_dict = Py_BuildValue( + "{s:s,s:i,s:i,s:N}", + "name", name_str, + "state", info[i].state, + "rc", info[i].rc, + "metadata", metadata_list); + + if ( info_dict == NULL ) + { + Py_DECREF(list); + Py_DECREF(metadata_list); + rc = EFAULT; + goto error; + } + PyList_SET_ITEM(list, i, info_dict); + + name_off += len[i]; + metadata_off += metadata_len[i]; + } + +error: + free(info); + free(name); + free(len); + free(metadata); + free(metadata_len); + return rc ? pyxc_error_to_exception(self->xc_handle) : list; +} + static PyMethodDef pyxc_methods[] = { { "domain_create", (PyCFunction)pyxc_domain_create, @@ -2542,6 +2761,44 @@ static PyMethodDef pyxc_methods[] = { "Returns: [int]: 0 on all permission granted; -1 if any permissions are \ denied\n" }, + { "livepatch_status", + (PyCFunction)pyxc_livepatch_status, + METH_KEYWORDS, "\n" + "Gets current state and return code for a specified module.\n" + " name [str]: Module name to be used\n" + "Returns: [dict] on success; throwing an exception on error\n" + " state [int]: Module current state: CHECKED or APPLIED\n" + " rc [int]: Return code of last module's operation\n" }, + + { "livepatch_upload", + (PyCFunction)pyxc_livepatch_upload, + METH_KEYWORDS, "\n" + "Uploads a module with specified name from filename.\n" + " name [str]: Module name to be used\n" + " filename [str]: Filename of a module to be uploaded\n" + "Returns: None on success; throwing an exception on error\n" }, + + { "livepatch_action", + (PyCFunction)pyxc_livepatch_action, + METH_KEYWORDS, "\n" + "Performs an action (unload, revert, apply or replace) on a specified \ + module.\n" + " name [str]: Module name to be used\n" + " action [uint]: Action enum id\n" + " timeout [uint]: Action scheduled execution timeout\n" + " flags [ulong]: Flags specifying action's extra parameters\n" + "Returns: None on success; throwing an exception on error\n" }, + + { "livepatch_list", + (PyCFunction)pyxc_livepatch_list, + METH_NOARGS, "\n" + "List all uploaded livepatch modules with their current state and metadata.\n" + "Returns: [list of dicts] on success; throwing an exception on error\n" + " name [str]: Module name\n" + " state [int]: Module current state: CHECKED or APPLIED\n" + " rc [int]: Return code of last module's operation\n" + " metadata [list]: List of module's metadata 'key=value' strings\n" }, + { NULL, NULL, 0, NULL } }; @@ -2653,6 +2910,17 @@ PyMODINIT_FUNC initxc(void) PyModule_AddIntConstant(m, "XEN_SCHEDULER_CREDIT", XEN_SCHEDULER_CREDIT); PyModule_AddIntConstant(m, "XEN_SCHEDULER_CREDIT2", XEN_SCHEDULER_CREDIT2); + /* Expose livepatch constants to Python */ + PyModule_AddIntConstant(m, "LIVEPATCH_ACTION_UNLOAD", LIVEPATCH_ACTION_UNLOAD); + PyModule_AddIntConstant(m, "LIVEPATCH_ACTION_REVERT", LIVEPATCH_ACTION_REVERT); + PyModule_AddIntConstant(m, "LIVEPATCH_ACTION_APPLY", LIVEPATCH_ACTION_APPLY); + PyModule_AddIntConstant(m, "LIVEPATCH_ACTION_REPLACE", LIVEPATCH_ACTION_REPLACE); + + PyModule_AddIntConstant(m, "LIVEPATCH_ACTION_APPLY_NODEPS", LIVEPATCH_ACTION_APPLY_NODEPS); + + PyModule_AddIntConstant(m, "LIVEPATCH_STATE_APPLIED", LIVEPATCH_STATE_APPLIED); + PyModule_AddIntConstant(m, "LIVEPATCH_STATE_CHECKED", LIVEPATCH_STATE_CHECKED); + #if PY_MAJOR_VERSION >= 3 return m; #endif