release version 0.9.37
Signed-off-by: zhangsong234 <zhangsong34@huawei.com>
This commit is contained in:
parent
2d51190752
commit
adbb969f8b
130
CHANGELOG
130
CHANGELOG
@ -1,130 +0,0 @@
|
|||||||
# CHANGELOG
|
|
||||||
|
|
||||||
## v0.9 (2017-9-1)
|
|
||||||
|
|
||||||
* Add list-nic command and change remove-nic --name paramer form (MR: #93)
|
|
||||||
* Update config only when container isn't running (MR: #94)
|
|
||||||
* Add new parameters (--pretty/--filter) about nic/route commands (MR: #95)
|
|
||||||
* Add list route command (MR: #95)
|
|
||||||
|
|
||||||
## v0.8 (2017-8-16)
|
|
||||||
|
|
||||||
### general
|
|
||||||
|
|
||||||
* change Mtu range (MR: #80)
|
|
||||||
* remove 'add-path/remove-path' commands from syscontainer-tools (MR: #82)
|
|
||||||
* network: config net device 'tx/sg/tso' on (MR: #81)
|
|
||||||
* fix some minor issue(security check, file mode...) (MR: #84)
|
|
||||||
* fix when supply empty mac address, `add-nic` will panic (MR: #85)
|
|
||||||
* add '--update-config-only' option for add resources (MR: #89)
|
|
||||||
* add 'follow-partition' for add/remove device (MR: #86)
|
|
||||||
|
|
||||||
## v0.7 (2017-6-29)
|
|
||||||
|
|
||||||
### general
|
|
||||||
|
|
||||||
* reorginzed the syscontainer-tools code (MR: #75)
|
|
||||||
* use`syscontainer-tools` in `syscontainer-tools_wrapper` instead of `isula exec` (MR: #76)
|
|
||||||
* fix filelock can't lock accross the process issue (MR: #78)
|
|
||||||
|
|
||||||
### hooks
|
|
||||||
|
|
||||||
* combine 3 hooks binary to `syscontainer-hooks` (MR: #75)
|
|
||||||
|
|
||||||
## v0.6 (2017-6-23)
|
|
||||||
|
|
||||||
### general
|
|
||||||
|
|
||||||
* syscontainer-tools add `add-route & del-nic & del-route` command line. (MR: #71, #68)
|
|
||||||
* syscontainer-tools add-nic supports network interface config. (MR: #68)
|
|
||||||
* syscontainer-tools supports fidsk in container. (MR: #70)
|
|
||||||
* syscontainer-tools could only remove the devices added by syscontainer-tools (MR: #66)
|
|
||||||
|
|
||||||
### network-hook
|
|
||||||
|
|
||||||
* supports persistent network config for both interfaces and route rules. (MR: #69)
|
|
||||||
|
|
||||||
## v0.5 (2017-2-15)
|
|
||||||
|
|
||||||
### general
|
|
||||||
|
|
||||||
* fix some dir permissions for potential security risk. (MR: #56, #57, #62)
|
|
||||||
* move empty_systemd.sh to iSulad project. (MR: #60)
|
|
||||||
|
|
||||||
### syscontainer-tools
|
|
||||||
|
|
||||||
* fix syscontainer-tools subcommand display orders. (MR: #55)
|
|
||||||
* when iSulad daemon and syscontainer-tools are not in the same cgroup path, device-hook will fail to update cgroup permissions. (MR: #58)
|
|
||||||
* when containers specify cgroup folder (with `--cgroup-parent` flag), syscontainer-tools will fail to update device cgroup permissions. (MR: #64)
|
|
||||||
|
|
||||||
### oci-relabel-hook
|
|
||||||
|
|
||||||
* Fix systemd's context type if host use iSulad image's SELinux policy. (MR: #61)
|
|
||||||
|
|
||||||
## v0.4 (2017-1-24)
|
|
||||||
|
|
||||||
### general
|
|
||||||
|
|
||||||
* fix some golang security problems based on review comments
|
|
||||||
|
|
||||||
### syscontainer-tools
|
|
||||||
|
|
||||||
* Fix find cgroup path issue when syscontainer-tools run in container
|
|
||||||
* Add upstart support for system container
|
|
||||||
|
|
||||||
## v0.3 (2017-1-9)
|
|
||||||
|
|
||||||
### general
|
|
||||||
|
|
||||||
* use "go+c" function call to take over "exec nsenter"
|
|
||||||
* add golang 1.5.4 support
|
|
||||||
|
|
||||||
### hooks
|
|
||||||
|
|
||||||
* device-hook: Update cgroup permission in prestart hook not poststart
|
|
||||||
* oci-relabel-hook: Use golang to refact oci-relabel-hook
|
|
||||||
|
|
||||||
### syscontainer-tools
|
|
||||||
|
|
||||||
* Add "--force" flag for `syscontainer-tools add-device`
|
|
||||||
|
|
||||||
* can not use fdisk to partition when add a device to a container
|
|
||||||
|
|
||||||
## v1.11.2.8.it
|
|
||||||
|
|
||||||
### hooks
|
|
||||||
|
|
||||||
* device-hook: newly introduced device hook to save config for new added devices.
|
|
||||||
* oci-relabel-hook: use container's SELinux policy to adapt various images.
|
|
||||||
|
|
||||||
### syscontainer-tools
|
|
||||||
|
|
||||||
* use device-hook instead of "isula update" to keep resource persistent in container.
|
|
||||||
* add syslog support, all log will be written to syslog.
|
|
||||||
* add "make rpm" support in Makefile.
|
|
||||||
|
|
||||||
### bug fix
|
|
||||||
|
|
||||||
* can not add read-only mount path to container.
|
|
||||||
* add resource to system container, then use reboot to restart container, then resource can not be found after next reboot.
|
|
||||||
* validate network interface name in container, error out if it's empty.
|
|
||||||
* error out when insert network interface to non-existing container.
|
|
||||||
|
|
||||||
## v1.11.2.7.it
|
|
||||||
|
|
||||||
This is first initial release with lots of new features:
|
|
||||||
|
|
||||||
### hooks
|
|
||||||
|
|
||||||
* network-hook: move network interface from host to container when container starts.
|
|
||||||
* oci-relabel-hook: relabel container's rootfs when container starts.
|
|
||||||
|
|
||||||
### syscontainer-tools
|
|
||||||
|
|
||||||
New Commands:
|
|
||||||
|
|
||||||
* add-device: dynamically add new device into running container.
|
|
||||||
* remove-device: remove device from running container.
|
|
||||||
* add-path: dynamically bind mount host path to container path.
|
|
||||||
* remove-path: remove bind mount path from container.
|
|
||||||
* add-nic: dynamically move physical/virtual network interfaces into container.
|
|
||||||
127
License/LICENSE
127
License/LICENSE
@ -1,127 +0,0 @@
|
|||||||
木兰宽松许可证, 第2版
|
|
||||||
|
|
||||||
木兰宽松许可证, 第2版
|
|
||||||
2020年1月 http://license.coscl.org.cn/MulanPSL2
|
|
||||||
|
|
||||||
|
|
||||||
您对“软件”的复制、使用、修改及分发受木兰宽松许可证,第2版(“本许可证”)的如下条款的约束:
|
|
||||||
|
|
||||||
0. 定义
|
|
||||||
|
|
||||||
“软件”是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。
|
|
||||||
|
|
||||||
“贡献”是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。
|
|
||||||
|
|
||||||
“贡献者”是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。
|
|
||||||
|
|
||||||
“法人实体”是指提交贡献的机构及其“关联实体”。
|
|
||||||
|
|
||||||
“关联实体”是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。
|
|
||||||
|
|
||||||
1. 授予版权许可
|
|
||||||
|
|
||||||
每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。
|
|
||||||
|
|
||||||
2. 授予专利许可
|
|
||||||
|
|
||||||
每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。
|
|
||||||
|
|
||||||
3. 无商标许可
|
|
||||||
|
|
||||||
“本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用除外。
|
|
||||||
|
|
||||||
4. 分发限制
|
|
||||||
|
|
||||||
您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。
|
|
||||||
|
|
||||||
5. 免责声明与责任限制
|
|
||||||
|
|
||||||
“软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。
|
|
||||||
|
|
||||||
6. 语言
|
|
||||||
“本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。
|
|
||||||
|
|
||||||
条款结束
|
|
||||||
|
|
||||||
如何将木兰宽松许可证,第2版,应用到您的软件
|
|
||||||
|
|
||||||
如果您希望将木兰宽松许可证,第2版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步:
|
|
||||||
|
|
||||||
1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字;
|
|
||||||
|
|
||||||
2, 请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中;
|
|
||||||
|
|
||||||
3, 请将如下声明文本放入每个源文件的头部注释中。
|
|
||||||
|
|
||||||
Copyright (c) [Year] [name of copyright holder]
|
|
||||||
[Software Name] is licensed under Mulan PSL v2.
|
|
||||||
You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
http://license.coscl.org.cn/MulanPSL2
|
|
||||||
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
||||||
See the Mulan PSL v2 for more details.
|
|
||||||
|
|
||||||
|
|
||||||
Mulan Permissive Software License,Version 2
|
|
||||||
|
|
||||||
Mulan Permissive Software License,Version 2 (Mulan PSL v2)
|
|
||||||
January 2020 http://license.coscl.org.cn/MulanPSL2
|
|
||||||
|
|
||||||
Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions:
|
|
||||||
|
|
||||||
0. Definition
|
|
||||||
|
|
||||||
Software means the program and related documents which are licensed under this License and comprise all Contribution(s).
|
|
||||||
|
|
||||||
Contribution means the copyrightable work licensed by a particular Contributor under this License.
|
|
||||||
|
|
||||||
Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License.
|
|
||||||
|
|
||||||
Legal Entity means the entity making a Contribution and all its Affiliates.
|
|
||||||
|
|
||||||
Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, ‘control’ means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity.
|
|
||||||
|
|
||||||
1. Grant of Copyright License
|
|
||||||
|
|
||||||
Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not.
|
|
||||||
|
|
||||||
2. Grant of Patent License
|
|
||||||
|
|
||||||
Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken.
|
|
||||||
|
|
||||||
3. No Trademark License
|
|
||||||
|
|
||||||
No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in Section 4.
|
|
||||||
|
|
||||||
4. Distribution Restriction
|
|
||||||
|
|
||||||
You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software.
|
|
||||||
|
|
||||||
5. Disclaimer of Warranty and Limitation of Liability
|
|
||||||
|
|
||||||
THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO MATTER HOW IT’S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
|
||||||
|
|
||||||
6. Language
|
|
||||||
|
|
||||||
THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL.
|
|
||||||
|
|
||||||
END OF THE TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
How to Apply the Mulan Permissive Software License,Version 2 (Mulan PSL v2) to Your Software
|
|
||||||
|
|
||||||
To apply the Mulan PSL v2 to your work, for easy identification by recipients, you are suggested to complete following three steps:
|
|
||||||
|
|
||||||
i Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner;
|
|
||||||
|
|
||||||
ii Create a file named “LICENSE” which contains the whole context of this License in the first directory of your software package;
|
|
||||||
|
|
||||||
iii Attach the statement to the appropriate annotated syntax at the beginning of each source file.
|
|
||||||
|
|
||||||
|
|
||||||
Copyright (c) [Year] [name of copyright holder]
|
|
||||||
[Software Name] is licensed under Mulan PSL v2.
|
|
||||||
You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
http://license.coscl.org.cn/MulanPSL2
|
|
||||||
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
||||||
See the Mulan PSL v2 for more details.
|
|
||||||
@ -1,373 +0,0 @@
|
|||||||
OPEN SOURCE SOFTWARE NOTICE
|
|
||||||
|
|
||||||
Please note we provide an open source software notice along with this product and/or this product firmware (in the following just “this product”). The open source software licenses are granted by the respective right holders. And the open source licenses prevail all other license information with regard to the respective open source software contained in the product, including but not limited to End User Software Licensing Agreement. This notice is provided on behalf of Huawei Technologies Co. Ltd. and any of its local subsidiaries which may have provided this product to you in your local country.
|
|
||||||
|
|
||||||
Warranty Disclaimer
|
|
||||||
|
|
||||||
THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIEDWARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
|
|
||||||
|
|
||||||
Copyright Notice and License Texts
|
|
||||||
|
|
||||||
Software: vendor/github.com/Microsoft/go-winio v0.4.14
|
|
||||||
Copyright notice:
|
|
||||||
Copyright (c) 2015 Microsoft
|
|
||||||
License: The MIT License (MIT)
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
||||||
Software: vendor/github.com/Sirupsen/logrus v1.4.2
|
|
||||||
Copyright notice:
|
|
||||||
Copyright (c) 2014 Simon Eskildsen
|
|
||||||
License: The MIT License (MIT)
|
|
||||||
Please see above
|
|
||||||
|
|
||||||
Software: vendor/github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b
|
|
||||||
Copyright notice:
|
|
||||||
Copyright 2018 CoreOS, Inc
|
|
||||||
License: Apache License Version 2.0
|
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction, and
|
|
||||||
distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright
|
|
||||||
owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all other entities
|
|
||||||
that control, are controlled by, or are under common control with that entity.
|
|
||||||
For the purposes of this definition, "control" means (i) the power, direct or
|
|
||||||
indirect, to cause the direction or management of such entity, whether by
|
|
||||||
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity exercising
|
|
||||||
permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications, including
|
|
||||||
but not limited to software source code, documentation source, and configuration
|
|
||||||
files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical transformation or
|
|
||||||
translation of a Source form, including but not limited to compiled object code,
|
|
||||||
generated documentation, and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or Object form, made
|
|
||||||
available under the License, as indicated by a copyright notice that is included
|
|
||||||
in or attached to the work (an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object form, that
|
|
||||||
is based on (or derived from) the Work and for which the editorial revisions,
|
|
||||||
annotations, elaborations, or other modifications represent, as a whole, an
|
|
||||||
original work of authorship. For the purposes of this License, Derivative Works
|
|
||||||
shall not include works that remain separable from, or merely link (or bind by
|
|
||||||
name) to the interfaces of, the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including the original version
|
|
||||||
of the Work and any modifications or additions to that Work or Derivative Works
|
|
||||||
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
|
||||||
by the copyright owner or by an individual or Legal Entity authorized to submit
|
|
||||||
on behalf of the copyright owner. For the purposes of this definition,
|
|
||||||
"submitted" means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems, and
|
|
||||||
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
|
||||||
the purpose of discussing and improving the Work, but excluding communication
|
|
||||||
that is conspicuously marked or otherwise designated in writing by the copyright
|
|
||||||
owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
|
|
||||||
of whom a Contribution has been received by Licensor and subsequently
|
|
||||||
incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License.
|
|
||||||
|
|
||||||
Subject to the terms and conditions of this License, each Contributor hereby
|
|
||||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
|
||||||
irrevocable copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the Work and such
|
|
||||||
Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License.
|
|
||||||
|
|
||||||
Subject to the terms and conditions of this License, each Contributor hereby
|
|
||||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
|
||||||
irrevocable (except as stated in this section) patent license to make, have
|
|
||||||
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
|
|
||||||
such license applies only to those patent claims licensable by such Contributor
|
|
||||||
that are necessarily infringed by their Contribution(s) alone or by combination
|
|
||||||
of their Contribution(s) with the Work to which such Contribution(s) was
|
|
||||||
submitted. If You institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
|
||||||
Contribution incorporated within the Work constitutes direct or contributory
|
|
||||||
patent infringement, then any patent licenses granted to You under this License
|
|
||||||
for that Work shall terminate as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution.
|
|
||||||
|
|
||||||
You may reproduce and distribute copies of the Work or Derivative Works thereof
|
|
||||||
in any medium, with or without modifications, and in Source or Object form,
|
|
||||||
provided that You meet the following conditions:
|
|
||||||
|
|
||||||
You must give any other recipients of the Work or Derivative Works a copy of
|
|
||||||
this License; and
|
|
||||||
You must cause any modified files to carry prominent notices stating that You
|
|
||||||
changed the files; and
|
|
||||||
You must retain, in the Source form of any Derivative Works that You distribute,
|
|
||||||
all copyright, patent, trademark, and attribution notices from the Source form
|
|
||||||
of the Work, excluding those notices that do not pertain to any part of the
|
|
||||||
Derivative Works; and
|
|
||||||
If the Work includes a "NOTICE" text file as part of its distribution, then any
|
|
||||||
Derivative Works that You distribute must include a readable copy of the
|
|
||||||
attribution notices contained within such NOTICE file, excluding those notices
|
|
||||||
that do not pertain to any part of the Derivative Works, in at least one of the
|
|
||||||
following places: within a NOTICE text file distributed as part of the
|
|
||||||
Derivative Works; within the Source form or documentation, if provided along
|
|
||||||
with the Derivative Works; or, within a display generated by the Derivative
|
|
||||||
Works, if and wherever such third-party notices normally appear. The contents of
|
|
||||||
the NOTICE file are for informational purposes only and do not modify the
|
|
||||||
License. You may add Your own attribution notices within Derivative Works that
|
|
||||||
You distribute, alongside or as an addendum to the NOTICE text from the Work,
|
|
||||||
provided that such additional attribution notices cannot be construed as
|
|
||||||
modifying the License.
|
|
||||||
You may add Your own copyright statement to Your modifications and may provide
|
|
||||||
additional or different license terms and conditions for use, reproduction, or
|
|
||||||
distribution of Your modifications, or for any such Derivative Works as a whole,
|
|
||||||
provided Your use, reproduction, and distribution of the Work otherwise complies
|
|
||||||
with the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions.
|
|
||||||
|
|
||||||
Unless You explicitly state otherwise, any Contribution intentionally submitted
|
|
||||||
for inclusion in the Work by You to the Licensor shall be under the terms and
|
|
||||||
conditions of this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify the terms of
|
|
||||||
any separate license agreement you may have executed with Licensor regarding
|
|
||||||
such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks.
|
|
||||||
|
|
||||||
This License does not grant permission to use the trade names, trademarks,
|
|
||||||
service marks, or product names of the Licensor, except as required for
|
|
||||||
reasonable and customary use in describing the origin of the Work and
|
|
||||||
reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty.
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, Licensor provides the
|
|
||||||
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
|
||||||
including, without limitation, any warranties or conditions of TITLE,
|
|
||||||
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
|
|
||||||
solely responsible for determining the appropriateness of using or
|
|
||||||
redistributing the Work and assume any risks associated with Your exercise of
|
|
||||||
permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability.
|
|
||||||
|
|
||||||
In no event and under no legal theory, whether in tort (including negligence),
|
|
||||||
contract, or otherwise, unless required by applicable law (such as deliberate
|
|
||||||
and grossly negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special, incidental,
|
|
||||||
or consequential damages of any character arising as a result of this License or
|
|
||||||
out of the use or inability to use the Work (including but not limited to
|
|
||||||
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
|
|
||||||
any and all other commercial damages or losses), even if such Contributor has
|
|
||||||
been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability.
|
|
||||||
|
|
||||||
While redistributing the Work or Derivative Works thereof, You may choose to
|
|
||||||
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
|
|
||||||
other liability obligations and/or rights consistent with this License. However,
|
|
||||||
in accepting such obligations, You may act only on Your own behalf and on Your
|
|
||||||
sole responsibility, not on behalf of any other Contributor, and only if You
|
|
||||||
agree to indemnify, defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason of your
|
|
||||||
accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following boilerplate
|
|
||||||
notice, with the fields enclosed by brackets "[]" replaced with your own
|
|
||||||
identifying information. (Don't include the brackets!) The text should be
|
|
||||||
enclosed in the appropriate comment syntax for the file format. We also
|
|
||||||
recommend that a file or class name and description of purpose be included on
|
|
||||||
the same "printed page" as the copyright notice for easier identification within
|
|
||||||
third-party archives.
|
|
||||||
|
|
||||||
Copyright [yyyy] [name of copyright owner]
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
Software: vendor/github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f
|
|
||||||
Copyright notice:
|
|
||||||
Copyright 2018 CoreOS, Inc
|
|
||||||
License: Apache License Version 2.0
|
|
||||||
Please see above
|
|
||||||
|
|
||||||
Software: vendor/github.com/docker/docker v1.13.1
|
|
||||||
Copyright notice:
|
|
||||||
Copyright 2012-2016 Docker, Inc.
|
|
||||||
License: Apache License Version 2.0
|
|
||||||
Please see above
|
|
||||||
|
|
||||||
Software: vendor/github.com/docker/go-units v0.4.0
|
|
||||||
Copyright notice:
|
|
||||||
Copyright 2015 Docker, Inc.
|
|
||||||
License: Apache License Version 2.0
|
|
||||||
Please see above
|
|
||||||
|
|
||||||
Software: vendor/github.com/docker/libnetwork v0.5.6
|
|
||||||
Copyright notice:
|
|
||||||
Copyright 2015 Docker, Inc.
|
|
||||||
License: Apache License Version 2.0
|
|
||||||
Please see above
|
|
||||||
|
|
||||||
Software: vendor/github.com/godbus/dbus v4.1.0+incompatible
|
|
||||||
Copyright notice:
|
|
||||||
Copyright (c) 2013, Georg Reinke (<guelfey at gmail dot com>), Google
|
|
||||||
All rights reserved.
|
|
||||||
License: BSD 2-Clause
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions
|
|
||||||
are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in the
|
|
||||||
documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
|
||||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
Software: vendor/github.com/golang/protobuf v1.3.2
|
|
||||||
Copyright notice:
|
|
||||||
Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
License: BSD 2-Clause
|
|
||||||
Please see above
|
|
||||||
|
|
||||||
Software: vendor/github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618
|
|
||||||
Copyright notice:
|
|
||||||
Copyright 2014 Docker, Inc.
|
|
||||||
License: Apache License Version 2.0
|
|
||||||
Please see above
|
|
||||||
|
|
||||||
Software: vendor/github.com/opencontainers/runc v1.0.0-rc3
|
|
||||||
Copyright notice:
|
|
||||||
Copyright 2015 The Linux Foundation.
|
|
||||||
License: Apache License Version 2.0
|
|
||||||
Please see above
|
|
||||||
|
|
||||||
Software: vendor/github.com/opencontainers/runtime-spec v1.0.0-rc5
|
|
||||||
Copyright notice:
|
|
||||||
Copyright 2012-2015 Docker, Inc.
|
|
||||||
License: Apache License Version 2.0
|
|
||||||
Please see above
|
|
||||||
|
|
||||||
Software: vendor/github.com/seccomp/libseccomp-golang v0.9.1
|
|
||||||
Copyright notice:
|
|
||||||
Copyright (c) 2015 Matthew Heon <mheon@redhat.com>
|
|
||||||
Copyright (c) 2015 Paul Moore <pmoore@redhat.com>
|
|
||||||
All rights reserved.
|
|
||||||
License: BSD 2-Clause
|
|
||||||
Please see above
|
|
||||||
|
|
||||||
Software: vendor/github.com/sirupsen/logrus v1.4.2
|
|
||||||
Copyright notice:
|
|
||||||
Copyright (c) 2014 Simon Eskildsen
|
|
||||||
License: MIT License
|
|
||||||
Please see above
|
|
||||||
|
|
||||||
Software: vendor/github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2
|
|
||||||
Copyright notice:
|
|
||||||
Copyright 2013 Suryandaru Triandana <syndtr@gmail.com>
|
|
||||||
All rights reserved.
|
|
||||||
License: BSD 2-Clause
|
|
||||||
Please see above
|
|
||||||
|
|
||||||
Software: vendor/github.com/urfave/cli v1.21.0
|
|
||||||
Copyright notice:
|
|
||||||
Copyright (c) 2016 Jeremy Saenz & Contributors
|
|
||||||
License: MIT License
|
|
||||||
Please see above
|
|
||||||
|
|
||||||
Software: vendor/github.com/vishvananda/netlink v1.0.0
|
|
||||||
Copyright notice:
|
|
||||||
Copyright 2014 Vishvananda Ishaya.
|
|
||||||
Copyright 2014 Docker, Inc.
|
|
||||||
License: Apache License Version 2.0
|
|
||||||
Please see above
|
|
||||||
|
|
||||||
Software: vendor/github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f
|
|
||||||
Copyright notice:
|
|
||||||
Copyright 2014 Vishvananda Ishaya.
|
|
||||||
Copyright 2014 Docker, Inc.
|
|
||||||
License: Apache License Version 2.0
|
|
||||||
Please see above
|
|
||||||
|
|
||||||
Software: vendor/golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b
|
|
||||||
Copyright notice:
|
|
||||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
|
||||||
License: BSD
|
|
||||||
Please see above
|
|
||||||
|
|
||||||
Software: vendor/github.com/go-yaml/yaml v2.1.0+incompatible
|
|
||||||
Copyright notice:
|
|
||||||
Copyright 2011-2016 Canonical Ltd.
|
|
||||||
License: Apache License Version 2.0
|
|
||||||
Please see above
|
|
||||||
|
|
||||||
Written Offer
|
|
||||||
|
|
||||||
This product contains software whose rights holders license it on the terms of the GNU General Public License, version 2 (GPLv2) and/or other open source software licenses. We will provide you and any third party with the source code of the software licensed under an open source software license if you send us a written request by mail or email to the following addresses:
|
|
||||||
foss@huawei.com
|
|
||||||
|
|
||||||
detailing the name of the product and the firmware version for which you need the source code and indicating how we can contact you.
|
|
||||||
Please note you need to make a payment before you obtain the complete Corresponding Source Code from us. For how much you will pay and how we will deliver the complete Corresponding Source Code to you, we will further discuss it by mail or email.
|
|
||||||
This offer is valid to anyone in receipt of this information.
|
|
||||||
|
|
||||||
THIS OFFER IS VALID FOR THREE YEARS FROM THE MOMENT WE DISTRIBUTED THE PRODUCT OR FIRMWARE.
|
|
||||||
54
Makefile
54
Makefile
@ -1,54 +0,0 @@
|
|||||||
# Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
# syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
# You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
# http://license.coscl.org.cn/MulanPSL2
|
|
||||||
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
# PURPOSE.
|
|
||||||
# See the Mulan PSL v2 for more details.
|
|
||||||
# Description: makefile for syscontainer-tools
|
|
||||||
# Author: zhangwei
|
|
||||||
# Create: 2018-01-18
|
|
||||||
|
|
||||||
COMMIT=$(shell git rev-parse HEAD 2> /dev/null || true)
|
|
||||||
SOURCES := $(shell find . 2>&1 | grep -E '.*\.(c|h|go)$$')
|
|
||||||
DEPS_LINK := $(CURDIR)/vendor/
|
|
||||||
TAGS="cgo static_build"
|
|
||||||
VERSION := $(shell cat ./VERSION)
|
|
||||||
|
|
||||||
BEP_DIR=/tmp/syscontainer-tools-build-bep
|
|
||||||
BEP_FLAGS=-tmpdir=/tmp/syscontainer-tools-build-bep
|
|
||||||
|
|
||||||
GO_LDFLAGS="-w -buildid=IdByiSula -extldflags -static $(BEP_FLAGS) -X main.gitCommit=${COMMIT} -X main.version=${VERSION}"
|
|
||||||
ENV = GOPATH=${GOPATH} CGO_ENABLED=1
|
|
||||||
|
|
||||||
## PLEASE be noticed that the vendor dir can only work with golang > 1.6 !!
|
|
||||||
|
|
||||||
all: dep syscontainer-tools syscontainer-hooks
|
|
||||||
dep:
|
|
||||||
mkdir -p $(BEP_DIR)
|
|
||||||
|
|
||||||
init:
|
|
||||||
sh -x apply-patch
|
|
||||||
|
|
||||||
syscontainer-tools: $(SOURCES) | $(DEPS_LINK)
|
|
||||||
@echo "Making syscontainer-tools..."
|
|
||||||
${ENV} go build -mod=vendor -tags ${TAGS} -ldflags ${GO_LDFLAGS} -o build/syscontainer-tools .
|
|
||||||
@echo "Done!"
|
|
||||||
|
|
||||||
syscontainer-hooks: $(SOURCES) | $(DEPS_LINK)
|
|
||||||
@echo "Making syscontainer-hooks..."
|
|
||||||
${ENV} go build -mod=vendor -tags ${TAGS} -ldflags ${GO_LDFLAGS} -o build/syscontainer-hooks ./hooks/syscontainer-hooks
|
|
||||||
@echo "Done!"
|
|
||||||
|
|
||||||
localtest:
|
|
||||||
go test -tags ${TAGS} -ldflags ${GO_LDFLAGS} -v ./...
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -rf build
|
|
||||||
|
|
||||||
install:
|
|
||||||
cd hack && ./install.sh
|
|
||||||
|
|
||||||
.PHONY: test
|
|
||||||
67
README.md
67
README.md
@ -1,67 +0,0 @@
|
|||||||
# syscontainer-tools
|
|
||||||
|
|
||||||
## Introduction
|
|
||||||
|
|
||||||
**syscontainer-tools** is a fully customized tool,
|
|
||||||
it is a small auxiliary tool which is expected to work with isulad with hook support,
|
|
||||||
and provides enhanced functions which is inappropriate to be included in isulad itself.
|
|
||||||
|
|
||||||
The project includes two main parts: `syscontainer-tools` and `hooks`.
|
|
||||||
`syscontainer-tools` is used for dynamically operating on running containers,
|
|
||||||
and `hooks` is used for executing user defined program at some special timepoint of container's lifecycle.
|
|
||||||
|
|
||||||
## Hooks
|
|
||||||
|
|
||||||
We provide syscontainer hooks function.
|
|
||||||
|
|
||||||
* syscontainer-hooks:
|
|
||||||
1. insert block devices added by syscontainer-tools into container when container restarts(prestart state).
|
|
||||||
2. insert network interfaces and route rules added by syscontainer-tools into container when container restarts(prestart state).
|
|
||||||
3. remove udev rules and leaking network interfaces when container stops(post-stop state).
|
|
||||||
4. handling oci relabel for container in prestart and post stop state.
|
|
||||||
|
|
||||||
You could use hook spec to customise your hooks.
|
|
||||||
For detailed information, See [introduction of syscontainer-hooks](hooks/syscontainer-hooks/README.md)
|
|
||||||
|
|
||||||
## syscontainer-tools
|
|
||||||
|
|
||||||
Basic usage of `syscontainer-tools`:
|
|
||||||
|
|
||||||
```
|
|
||||||
NAME:
|
|
||||||
syscontainer-tools - Enhanced tools for IT isulad
|
|
||||||
|
|
||||||
USAGE:
|
|
||||||
syscontainer-tools [global options] command [command options] [arguments...]
|
|
||||||
|
|
||||||
VERSION:
|
|
||||||
v0.9
|
|
||||||
commit: e39c47b1d0403fd133c49db13ab6df7e5d53a21b
|
|
||||||
|
|
||||||
COMMANDS:
|
|
||||||
add-device add one or more host devices to container
|
|
||||||
add-nic create a new network interfaces for container
|
|
||||||
add-path add one or more host paths to container
|
|
||||||
add-route add a new network route rule into container
|
|
||||||
relabel relabel rootfs for running SELinux in system container
|
|
||||||
remove-device remove one or more devices from container
|
|
||||||
remove-nic remove a network interface from container
|
|
||||||
remove-path remove one or more paths from container
|
|
||||||
remove-route remove a network route rule from container
|
|
||||||
|
|
||||||
GLOBAL OPTIONS:
|
|
||||||
--debug enable debug output for logging
|
|
||||||
--log "/dev/null" set the log file path where internal debug information is written
|
|
||||||
--log-format "text" set the format used by logs ('text' (default), or 'json')
|
|
||||||
--syslog-service "unix:///dev/log" set syslog service
|
|
||||||
--help, -h show help
|
|
||||||
--version, -v print the version
|
|
||||||
```
|
|
||||||
|
|
||||||
For usage of each command, you can check with `--help`, e.g. `syscontainer-tools add-device --help`
|
|
||||||
|
|
||||||
## Contributions
|
|
||||||
|
|
||||||
As this is a fully customized tool, I don't think anyone will be interested in contributing to this project,
|
|
||||||
but we welcome your contributions. Before contributing, please make sure you understand our needs and
|
|
||||||
make a communication with us.
|
|
||||||
22
apply-patch
22
apply-patch
@ -1,22 +0,0 @@
|
|||||||
# Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
# syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
# You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
# http://license.coscl.org.cn/MulanPSL2
|
|
||||||
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
# PURPOSE.
|
|
||||||
# See the Mulan PSL v2 for more details.
|
|
||||||
# Description: apply patch
|
|
||||||
# Author: zhangwei
|
|
||||||
# Create: 2018-01-18
|
|
||||||
|
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
cat series.conf | while read line
|
|
||||||
do
|
|
||||||
if [[ $line == '' || $line =~ ^\s*# ]]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
patch -p1 -F1 -s < $line
|
|
||||||
done
|
|
||||||
225
config/config.go
225
config/config.go
@ -1,225 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: device hook config
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"isula.org/syscontainer-tools/container"
|
|
||||||
"isula.org/syscontainer-tools/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
defaultConfigFile = "device_hook.json"
|
|
||||||
// IsuladToolsDir is syscontainer-tools run dir
|
|
||||||
IsuladToolsDir = "/run/syscontainer-tools"
|
|
||||||
)
|
|
||||||
|
|
||||||
// QosType defines the qos type by int
|
|
||||||
type QosType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
// QosReadIOPS defines the read device iops type
|
|
||||||
QosReadIOPS QosType = iota
|
|
||||||
// QosWriteIOPS defines the write device iops type
|
|
||||||
QosWriteIOPS
|
|
||||||
// QosReadBps defines the read device bps type
|
|
||||||
QosReadBps
|
|
||||||
// QosWriteBps defines the write device bps type
|
|
||||||
QosWriteBps
|
|
||||||
// QosBlkioWeight defines the device blkio weight type
|
|
||||||
QosBlkioWeight
|
|
||||||
)
|
|
||||||
|
|
||||||
// ContainerConfig is the interface of container config handler
|
|
||||||
type ContainerConfig interface {
|
|
||||||
FindDeviceByMapping(dev *types.Device) *types.Device
|
|
||||||
FindSubPartition(dev *types.Device) []*types.Device
|
|
||||||
UpdateDevice(device *types.Device, isAddDevice bool) error
|
|
||||||
UpdateDeviceNode(device string, major, minor int64)
|
|
||||||
|
|
||||||
IsBindInConfig(bind *types.Bind) bool
|
|
||||||
UpdateBind(bind *types.Bind, isAddBind bool) (bool, error)
|
|
||||||
GetBinds() []string
|
|
||||||
GetBindInConfig(bind *types.Bind) (*HostMapping, error)
|
|
||||||
GetAllDevices() []*DeviceMapping
|
|
||||||
DeviceIndexInArray(device *types.Device) int
|
|
||||||
UpdateDeviceQos(qos *types.Qos, qType QosType) error
|
|
||||||
RemoveDeviceQos(device *types.Device, qType QosType) (bool, error)
|
|
||||||
|
|
||||||
FindInterfaceByName(config *types.InterfaceConf) *types.InterfaceConf
|
|
||||||
IsConflictInterface(nic *types.InterfaceConf) error
|
|
||||||
IsSameInterface(nic *types.InterfaceConf) bool
|
|
||||||
UpdateNetworkInterface(nic *types.InterfaceConf, isAdd bool) error
|
|
||||||
GetNics(filter *types.InterfaceConf) []*types.InterfaceConf
|
|
||||||
|
|
||||||
IsRouteExist(route *types.Route) bool
|
|
||||||
IsConflictRoute(route *types.Route) error
|
|
||||||
UpdateNetworkRoutes(route *types.Route, isAdd bool) error
|
|
||||||
GetRoutes(filter *types.Route) []*types.Route
|
|
||||||
|
|
||||||
Flush() error
|
|
||||||
CheckPathNum() error
|
|
||||||
CheckNicNum() error
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewContainerConfig will create the container config handler by name
|
|
||||||
func NewContainerConfig(c *container.Container) (ContainerConfig, error) {
|
|
||||||
configfile := filepath.Join(c.ContainerPath(), defaultConfigFile)
|
|
||||||
hConfig, err := LoadContainerHookConfig(configfile)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
hConfig.configPath = configfile
|
|
||||||
return hConfig, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeviceMapping represents the device mapping between the host and the container.
|
|
||||||
type DeviceMapping struct {
|
|
||||||
Type string
|
|
||||||
Minor int64
|
|
||||||
Major int64
|
|
||||||
PathOnHost string
|
|
||||||
PathInContainer string
|
|
||||||
CgroupPermissions string
|
|
||||||
Parent string
|
|
||||||
}
|
|
||||||
|
|
||||||
type info struct {
|
|
||||||
count int
|
|
||||||
perm string
|
|
||||||
}
|
|
||||||
type bindsInfo struct {
|
|
||||||
pathInHost map[string]*info
|
|
||||||
pathInContainer map[string]int
|
|
||||||
l *sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkEuqal(old, new string) bool {
|
|
||||||
oldp := strings.Split(strings.Replace(old, " ", "", -1), ",")
|
|
||||||
newp := strings.Split(strings.Replace(new, " ", "", -1), ",")
|
|
||||||
return reflect.DeepEqual(oldp, newp)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// we do not allow mount more than on host paths to a single path in container
|
|
||||||
// if we mount a single host path to multi paths in contiainer, return true,nil
|
|
||||||
func (bi *bindsInfo) add(bindstr string) (bool, error) {
|
|
||||||
|
|
||||||
bi.l.Lock()
|
|
||||||
defer bi.l.Unlock()
|
|
||||||
|
|
||||||
hostPathExist := false
|
|
||||||
|
|
||||||
mp, err := parseMapping(bindstr)
|
|
||||||
if err != nil {
|
|
||||||
return hostPathExist, fmt.Errorf("Wrong bind format: %s,err %s", bindstr, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, exist := bi.pathInContainer[mp.PathInContainer]; exist == true {
|
|
||||||
return hostPathExist, fmt.Errorf("Mount more than one host paths to a single path in container")
|
|
||||||
}
|
|
||||||
|
|
||||||
bi.pathInContainer[mp.PathInContainer] = 1
|
|
||||||
|
|
||||||
if _, exist := bi.pathInHost[mp.PathOnHost]; exist == true {
|
|
||||||
if checkEuqal(mp.Permission, bi.pathInHost[mp.PathOnHost].perm) == false {
|
|
||||||
return hostPathExist, fmt.Errorf("Mount one host path with different permissions, old: %s, new: %s", bi.pathInHost[mp.PathOnHost].perm, mp.Permission)
|
|
||||||
}
|
|
||||||
bi.pathInHost[mp.PathOnHost].count++
|
|
||||||
hostPathExist = true
|
|
||||||
return hostPathExist, nil
|
|
||||||
}
|
|
||||||
bi.pathInHost[mp.PathOnHost] = &info{count: 1, perm: mp.Permission}
|
|
||||||
return hostPathExist, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bi *bindsInfo) remove(bindstr string) (bool, error) {
|
|
||||||
bi.l.Lock()
|
|
||||||
defer bi.l.Unlock()
|
|
||||||
mp, err := parseMapping(bindstr)
|
|
||||||
if err != nil {
|
|
||||||
return true, fmt.Errorf("Wrong bind format: %s,err %s", bindstr, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// always delete the item of container path
|
|
||||||
delete(bi.pathInContainer, mp.PathInContainer)
|
|
||||||
|
|
||||||
if _, exist := bi.pathInHost[mp.PathOnHost]; exist == true {
|
|
||||||
bi.pathInHost[mp.PathOnHost].count--
|
|
||||||
if bi.pathInHost[mp.PathOnHost].count <= 0 {
|
|
||||||
delete(bi.pathInHost, mp.PathOnHost)
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
return true, fmt.Errorf("%s not in memory datebase", mp.PathOnHost)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerHookConfig is the data config structure for device hook storage file.
|
|
||||||
type ContainerHookConfig struct {
|
|
||||||
Binds []string `json:"bindToAdd,omitempty"`
|
|
||||||
Devices []*DeviceMapping `json:"deviceToAdd,omitempty"`
|
|
||||||
ReadIOPS []*types.Qos `json:"readIops,omitempty"`
|
|
||||||
WriteIOPS []*types.Qos `json:"writeIops,omitempty"`
|
|
||||||
ReadBps []*types.Qos `json:"readBps,omitempty"`
|
|
||||||
WriteBps []*types.Qos `json:"writeBps,omitempty"`
|
|
||||||
BlkioWeight []*types.Qos `json:"blkioWeight,omitempty"`
|
|
||||||
NetworkInterfaces []*types.InterfaceConf `json:"networkInterfaces,omitempty"`
|
|
||||||
NetworkRoutes []*types.Route `json:"networkRoute,omitempty"`
|
|
||||||
configPath string
|
|
||||||
dirty bool
|
|
||||||
bi *bindsInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadContainerHookConfig will parse and unmarshal ContainerHookConfig
|
|
||||||
func LoadContainerHookConfig(path string) (*ContainerHookConfig, error) {
|
|
||||||
// if config file do not exist, just return empty DevieHookConfig.
|
|
||||||
bi := bindsInfo{
|
|
||||||
pathInHost: make(map[string]*info),
|
|
||||||
pathInContainer: make(map[string]int),
|
|
||||||
l: &sync.Mutex{},
|
|
||||||
}
|
|
||||||
if _, err := os.Stat(path); err != nil {
|
|
||||||
return &ContainerHookConfig{bi: &bi}, nil
|
|
||||||
}
|
|
||||||
bytes, err := ioutil.ReadFile(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
config := &ContainerHookConfig{}
|
|
||||||
|
|
||||||
if err := json.Unmarshal(bytes, &config); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
config.bi = &bi
|
|
||||||
|
|
||||||
for _, bindstr := range config.Binds {
|
|
||||||
if _, err := config.bi.add(bindstr); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
config.configPath = path
|
|
||||||
|
|
||||||
return config, nil
|
|
||||||
}
|
|
||||||
@ -1,370 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: device config operation
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"isula.org/syscontainer-tools/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// HostMapping host path mapping to container path
|
|
||||||
type HostMapping struct {
|
|
||||||
PathOnHost string
|
|
||||||
PathInContainer string
|
|
||||||
Permission string
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
// MaxPathNum is max path number for devices
|
|
||||||
MaxPathNum = 128
|
|
||||||
// ArrayLen is host bind split array len
|
|
||||||
ArrayLen = 3
|
|
||||||
)
|
|
||||||
|
|
||||||
func parseMapping(bind string) (*HostMapping, error) {
|
|
||||||
array := strings.SplitN(bind, ":", 3)
|
|
||||||
if len(array) < ArrayLen {
|
|
||||||
// this function is used for host bind.
|
|
||||||
// should not get here, in case won't crash.
|
|
||||||
return nil, fmt.Errorf("bind must have two : in string")
|
|
||||||
}
|
|
||||||
mp := &HostMapping{
|
|
||||||
PathOnHost: array[0],
|
|
||||||
PathInContainer: array[1],
|
|
||||||
Permission: array[2],
|
|
||||||
}
|
|
||||||
return mp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flush will flush the config to filesystem
|
|
||||||
func (config *ContainerHookConfig) Flush() error {
|
|
||||||
if !config.dirty {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
file, err := os.Create(config.configPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
if err := file.Chmod(0600); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := json.NewEncoder(file).Encode(config); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (config *ContainerHookConfig) bindIndexInArray(bind *types.Bind, array []string) int {
|
|
||||||
for index, bindstr := range array {
|
|
||||||
mp, err := parseMapping(bindstr)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if mp.PathInContainer == bind.ContainerPath && mp.PathOnHost == bind.HostPath {
|
|
||||||
return index
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeviceIndexInArray get device index in array
|
|
||||||
func (config *ContainerHookConfig) DeviceIndexInArray(device *types.Device) int {
|
|
||||||
|
|
||||||
for index, dev := range config.Devices {
|
|
||||||
if (device.PathOnHost == "" && dev.PathInContainer == device.Path) ||
|
|
||||||
(device.Path == "" && dev.PathOnHost == device.PathOnHost) ||
|
|
||||||
(dev.PathInContainer == device.Path && dev.PathOnHost == device.PathOnHost) {
|
|
||||||
return index
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (config *ContainerHookConfig) getConflictIndex(device *types.Device) int {
|
|
||||||
|
|
||||||
for index, dev := range config.Devices {
|
|
||||||
if dev.PathInContainer == device.Path || dev.PathOnHost == device.PathOnHost {
|
|
||||||
return index
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindDeviceByMapping returns if a device in DeviceToAdd Config.
|
|
||||||
func (config *ContainerHookConfig) FindDeviceByMapping(device *types.Device) *types.Device {
|
|
||||||
for _, eDevice := range config.Devices {
|
|
||||||
if eDevice.PathOnHost == device.PathOnHost && eDevice.PathInContainer == device.Path {
|
|
||||||
return &types.Device{
|
|
||||||
Path: eDevice.PathInContainer,
|
|
||||||
PathOnHost: eDevice.PathOnHost,
|
|
||||||
Permissions: eDevice.CgroupPermissions,
|
|
||||||
Major: eDevice.Major,
|
|
||||||
Minor: eDevice.Minor,
|
|
||||||
Type: eDevice.Type,
|
|
||||||
Parent: eDevice.Parent,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindSubPartition returns a set of sub devices of a device by config.
|
|
||||||
func (config *ContainerHookConfig) FindSubPartition(device *types.Device) []*types.Device {
|
|
||||||
var ret []*types.Device
|
|
||||||
for _, eDevice := range config.Devices {
|
|
||||||
if eDevice.Parent == device.PathOnHost {
|
|
||||||
ret = append(ret, &types.Device{
|
|
||||||
Path: eDevice.PathInContainer,
|
|
||||||
PathOnHost: eDevice.PathOnHost,
|
|
||||||
Permissions: eDevice.CgroupPermissions,
|
|
||||||
Major: eDevice.Major,
|
|
||||||
Minor: eDevice.Minor,
|
|
||||||
Type: eDevice.Type,
|
|
||||||
Parent: eDevice.Parent,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateDevice will update hook config of devices
|
|
||||||
func (config *ContainerHookConfig) UpdateDevice(device *types.Device, isAddDevice bool) error {
|
|
||||||
dev := &DeviceMapping{
|
|
||||||
Type: device.Type,
|
|
||||||
Major: device.Major,
|
|
||||||
Minor: device.Minor,
|
|
||||||
PathOnHost: device.PathOnHost,
|
|
||||||
PathInContainer: device.Path,
|
|
||||||
CgroupPermissions: device.Permissions,
|
|
||||||
Parent: device.Parent,
|
|
||||||
}
|
|
||||||
|
|
||||||
// add device action:
|
|
||||||
if isAddDevice {
|
|
||||||
// if not exist in Add array, add it.
|
|
||||||
index := config.getConflictIndex(device)
|
|
||||||
if index != -1 {
|
|
||||||
conflictDev := config.Devices[index]
|
|
||||||
return fmt.Errorf("device %s:%s has been already added into container", conflictDev.PathOnHost, conflictDev.PathInContainer)
|
|
||||||
}
|
|
||||||
config.dirty = true
|
|
||||||
config.Devices = append(config.Devices, dev)
|
|
||||||
} else {
|
|
||||||
if index := config.DeviceIndexInArray(device); index != -1 {
|
|
||||||
config.dirty = true
|
|
||||||
config.Devices = append(config.Devices[:index], config.Devices[index+1:]...)
|
|
||||||
} else {
|
|
||||||
return fmt.Errorf("device %s:%s has not been added into container", device.PathOnHost, device.Path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsBindInConfig returns if a device in DeviceToAdd Config.
|
|
||||||
func (config *ContainerHookConfig) IsBindInConfig(bind *types.Bind) bool {
|
|
||||||
if index := config.bindIndexInArray(bind, config.Binds); index != -1 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBindInConfig returns device in DeviceToAdd Config.
|
|
||||||
func (config *ContainerHookConfig) GetBindInConfig(bind *types.Bind) (*HostMapping, error) {
|
|
||||||
if index := config.bindIndexInArray(bind, config.Binds); index != -1 {
|
|
||||||
return parseMapping(config.Binds[index])
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("fail to find bind: %v", bind)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (config *ContainerHookConfig) addBind(bind *types.Bind) (bool, error) {
|
|
||||||
// if not in Add array, add it.
|
|
||||||
exist, err := config.bi.add(bind.ToString())
|
|
||||||
if err != nil {
|
|
||||||
return exist, err
|
|
||||||
}
|
|
||||||
if index := config.bindIndexInArray(bind, config.Binds); index == -1 {
|
|
||||||
config.dirty = true
|
|
||||||
config.Binds = append(config.Binds, bind.ToString())
|
|
||||||
}
|
|
||||||
return exist, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
func (config *ContainerHookConfig) removeBind(bind *types.Bind) (bool, error) {
|
|
||||||
// if in add array, remove it, will not restore to Rm array, as it is added by syscontainer-tools.
|
|
||||||
if index := config.bindIndexInArray(bind, config.Binds); index != -1 {
|
|
||||||
config.dirty = true
|
|
||||||
config.Binds = append(config.Binds[:index], config.Binds[index+1:]...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return config.bi.remove(bind.ToString())
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateBind will update binds of hook config
|
|
||||||
func (config *ContainerHookConfig) UpdateBind(bind *types.Bind, isAddBind bool) (bool, error) {
|
|
||||||
if isAddBind {
|
|
||||||
return config.addBind(bind)
|
|
||||||
}
|
|
||||||
return config.removeBind(bind)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBinds get binds of hook config
|
|
||||||
func (config *ContainerHookConfig) GetBinds() []string {
|
|
||||||
return config.Binds[:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAllDevices get all devices of hook config
|
|
||||||
func (config *ContainerHookConfig) GetAllDevices() []*DeviceMapping {
|
|
||||||
return config.Devices[:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateDeviceQos will update the qos for device
|
|
||||||
func (config *ContainerHookConfig) UpdateDeviceQos(qos *types.Qos, qType QosType) error {
|
|
||||||
update := func(qosArr []*types.Qos, qos *types.Qos) []*types.Qos {
|
|
||||||
for _, q := range qosArr {
|
|
||||||
if q.Major == qos.Major && q.Minor == qos.Minor {
|
|
||||||
if q.Value != qos.Value {
|
|
||||||
config.dirty = true
|
|
||||||
q.Value = qos.Value
|
|
||||||
}
|
|
||||||
return qosArr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
config.dirty = true
|
|
||||||
qosArr = append(qosArr, qos)
|
|
||||||
return qosArr
|
|
||||||
}
|
|
||||||
switch qType {
|
|
||||||
case QosReadIOPS:
|
|
||||||
config.ReadIOPS = update(config.ReadIOPS, qos)
|
|
||||||
case QosWriteIOPS:
|
|
||||||
config.WriteIOPS = update(config.WriteIOPS, qos)
|
|
||||||
case QosReadBps:
|
|
||||||
config.ReadBps = update(config.ReadBps, qos)
|
|
||||||
case QosWriteBps:
|
|
||||||
config.WriteBps = update(config.WriteBps, qos)
|
|
||||||
case QosBlkioWeight:
|
|
||||||
config.BlkioWeight = update(config.BlkioWeight, qos)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveDeviceQos remove qos for device
|
|
||||||
func (config *ContainerHookConfig) RemoveDeviceQos(device *types.Device, qType QosType) (bool, error) {
|
|
||||||
remove := func(qosArr []*types.Qos, device *types.Device) ([]*types.Qos, bool) {
|
|
||||||
for index, q := range qosArr {
|
|
||||||
if q.Major == device.Major && q.Minor == device.Minor {
|
|
||||||
config.dirty = true
|
|
||||||
qosArr = append(qosArr[:index], qosArr[index+1:]...)
|
|
||||||
return qosArr, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return qosArr, false
|
|
||||||
}
|
|
||||||
var ret bool
|
|
||||||
switch qType {
|
|
||||||
case QosReadIOPS:
|
|
||||||
config.ReadIOPS, ret = remove(config.ReadIOPS, device)
|
|
||||||
case QosWriteIOPS:
|
|
||||||
config.WriteIOPS, ret = remove(config.WriteIOPS, device)
|
|
||||||
case QosReadBps:
|
|
||||||
config.ReadBps, ret = remove(config.ReadBps, device)
|
|
||||||
case QosWriteBps:
|
|
||||||
config.WriteBps, ret = remove(config.WriteBps, device)
|
|
||||||
case QosBlkioWeight:
|
|
||||||
config.BlkioWeight, ret = remove(config.BlkioWeight, device)
|
|
||||||
}
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CheckPathNum check path num reach max limit or not
|
|
||||||
func (config *ContainerHookConfig) CheckPathNum() error {
|
|
||||||
if len(config.Binds) > MaxPathNum {
|
|
||||||
return fmt.Errorf("Path already reach max limit")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetConfigDirty set config dir dirty
|
|
||||||
func (config *ContainerHookConfig) SetConfigDirty() {
|
|
||||||
config.dirty = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateQosDevNum update qos major and minor for device
|
|
||||||
func (config *ContainerHookConfig) UpdateQosDevNum(qos *types.Qos, major int64, minor int64) {
|
|
||||||
if qos.Major != major || qos.Minor != minor {
|
|
||||||
qos.Major = major
|
|
||||||
qos.Minor = minor
|
|
||||||
config.dirty = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateDeviceNode update device node
|
|
||||||
func (config *ContainerHookConfig) UpdateDeviceNode(device string, major, minor int64) {
|
|
||||||
for index, dev := range config.Devices {
|
|
||||||
if dev.PathOnHost == device {
|
|
||||||
config.Devices[index].Major = major
|
|
||||||
config.Devices[index].Minor = minor
|
|
||||||
config.dirty = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for index, qos := range config.ReadIOPS {
|
|
||||||
if qos.Path == device {
|
|
||||||
config.ReadIOPS[index].Major = major
|
|
||||||
config.ReadIOPS[index].Minor = minor
|
|
||||||
config.dirty = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for index, qos := range config.WriteIOPS {
|
|
||||||
if qos.Path == device {
|
|
||||||
config.WriteIOPS[index].Major = major
|
|
||||||
config.WriteIOPS[index].Minor = minor
|
|
||||||
config.dirty = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for index, qos := range config.ReadBps {
|
|
||||||
if qos.Path == device {
|
|
||||||
config.ReadBps[index].Major = major
|
|
||||||
config.ReadBps[index].Minor = minor
|
|
||||||
config.dirty = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for index, qos := range config.WriteBps {
|
|
||||||
if qos.Path == device {
|
|
||||||
config.WriteBps[index].Major = major
|
|
||||||
config.WriteBps[index].Minor = minor
|
|
||||||
config.dirty = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for index, qos := range config.BlkioWeight {
|
|
||||||
if qos.Path == device {
|
|
||||||
config.BlkioWeight[index].Major = major
|
|
||||||
config.BlkioWeight[index].Minor = minor
|
|
||||||
config.dirty = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,156 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: network config operation
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"isula.org/syscontainer-tools/types"
|
|
||||||
"path/filepath"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// IsuladToolsDirNetns is syscontainer-tools netns dir
|
|
||||||
IsuladToolsDirNetns = filepath.Join(IsuladToolsDir, "netns")
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// MaxNicNum is max nic number
|
|
||||||
MaxNicNum = 128
|
|
||||||
)
|
|
||||||
|
|
||||||
// FindInterfaceByName will find the full config for nic by name
|
|
||||||
func (config *ContainerHookConfig) FindInterfaceByName(nic *types.InterfaceConf) *types.InterfaceConf {
|
|
||||||
for _, eNic := range config.NetworkInterfaces {
|
|
||||||
if nic.Type != eNic.Type && nic.Type != "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if nic.CtrNicName == eNic.CtrNicName && (nic.HostNicName == "" || nic.HostNicName == eNic.HostNicName) {
|
|
||||||
return eNic
|
|
||||||
}
|
|
||||||
if nic.HostNicName == eNic.HostNicName && (nic.CtrNicName == "" || nic.CtrNicName == eNic.CtrNicName) {
|
|
||||||
return eNic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetNics will list all nics in config
|
|
||||||
func (config *ContainerHookConfig) GetNics(filter *types.InterfaceConf) []*types.InterfaceConf {
|
|
||||||
interfaces := make([]*types.InterfaceConf, 0)
|
|
||||||
for _, intf := range config.NetworkInterfaces {
|
|
||||||
if types.IsSameNic(filter, intf) {
|
|
||||||
interfaces = append(interfaces, intf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return interfaces
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsConflictInterface will check if the new interface config is conflict with the existing ones.
|
|
||||||
func (config *ContainerHookConfig) IsConflictInterface(nic *types.InterfaceConf) error {
|
|
||||||
for _, eNic := range config.NetworkInterfaces {
|
|
||||||
if err := types.IsConflictNic(nic, eNic); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsSameInterface will check if the new interface config is same with the existing ones.
|
|
||||||
func (config *ContainerHookConfig) IsSameInterface(nic *types.InterfaceConf) bool {
|
|
||||||
for _, eNic := range config.NetworkInterfaces {
|
|
||||||
if types.IsSameNic(nic, eNic) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateNetworkInterface will add network interface to config
|
|
||||||
func (config *ContainerHookConfig) UpdateNetworkInterface(nic *types.InterfaceConf, isAdd bool) error {
|
|
||||||
if isAdd {
|
|
||||||
config.dirty = true
|
|
||||||
config.NetworkInterfaces = append(config.NetworkInterfaces, nic)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for index, eNic := range config.NetworkInterfaces {
|
|
||||||
if types.IsSameNic(nic, eNic) {
|
|
||||||
config.dirty = true
|
|
||||||
config.NetworkInterfaces = append(config.NetworkInterfaces[:index], config.NetworkInterfaces[index+1:]...)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsRouteExist will check if the route is added by syscontainer-tools
|
|
||||||
func (config *ContainerHookConfig) IsRouteExist(route *types.Route) bool {
|
|
||||||
for _, eRoute := range config.NetworkRoutes {
|
|
||||||
if types.IsSameRoute(route, eRoute) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsConflictRoute will check if the new route config is conflict with the existing ones.
|
|
||||||
func (config *ContainerHookConfig) IsConflictRoute(route *types.Route) error {
|
|
||||||
for _, eRoute := range config.NetworkRoutes {
|
|
||||||
if err := types.IsConflictRoute(route, eRoute); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRoutes will get all filterd routes
|
|
||||||
func (config *ContainerHookConfig) GetRoutes(filter *types.Route) []*types.Route {
|
|
||||||
routes := make([]*types.Route, 0)
|
|
||||||
for _, eRoute := range config.NetworkRoutes {
|
|
||||||
if types.IsSameRoute(filter, eRoute) {
|
|
||||||
routes = append(routes, eRoute)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return routes
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateNetworkRoutes will add route to config
|
|
||||||
func (config *ContainerHookConfig) UpdateNetworkRoutes(route *types.Route, isAdd bool) error {
|
|
||||||
if isAdd {
|
|
||||||
config.dirty = true
|
|
||||||
config.NetworkRoutes = append(config.NetworkRoutes, route)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for index, eRoute := range config.NetworkRoutes {
|
|
||||||
if types.IsSameRoute(route, eRoute) {
|
|
||||||
config.dirty = true
|
|
||||||
config.NetworkRoutes = append(config.NetworkRoutes[:index], config.NetworkRoutes[index+1:]...)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CheckNicNum check nic num reach max limit or not
|
|
||||||
func (config *ContainerHookConfig) CheckNicNum() error {
|
|
||||||
if len(config.NetworkInterfaces) > MaxNicNum {
|
|
||||||
return fmt.Errorf("Nic already reach max limit")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@ -1,249 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: container config operation
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package container
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/opencontainers/runtime-spec/specs-go"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// deviceHookLock is the default isulad container file lock name
|
|
||||||
deviceHookLock = ".device_hook.lock"
|
|
||||||
|
|
||||||
// restrictedNameChars collects the characters allowed to represent a name, normally used to validate container and volume names.
|
|
||||||
restrictedNameChars = `[a-zA-Z0-9][a-zA-Z0-9_.-]`
|
|
||||||
|
|
||||||
// restrictedNamePattern is a regular expression to validate names against the collection of restricted characters.
|
|
||||||
restrictedNamePattern = regexp.MustCompile(`^/?` + restrictedNameChars + `+$`)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Container is a structure which contains the basic config of isulad containers
|
|
||||||
type Container struct {
|
|
||||||
pid int
|
|
||||||
containerID string
|
|
||||||
containerPath string
|
|
||||||
name string
|
|
||||||
spec *specs.Spec
|
|
||||||
lock *os.File
|
|
||||||
}
|
|
||||||
|
|
||||||
// New will create a container via a container name
|
|
||||||
func New(name string) (*Container, error) {
|
|
||||||
if !restrictedNamePattern.MatchString(name) {
|
|
||||||
return nil, fmt.Errorf("Invalid container name (%s), only %s are allowed", name, restrictedNameChars)
|
|
||||||
}
|
|
||||||
|
|
||||||
graphDriverPath, err := getIsuladGraphDriverPath()
|
|
||||||
|
|
||||||
var id, storagePath string
|
|
||||||
var pid int
|
|
||||||
var spec *specs.Spec
|
|
||||||
storagePath = filepath.Join(graphDriverPath, "engines", "lcr")
|
|
||||||
id, err = getIsuladContainerID(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
pid, err = getIsuladContainerPid(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
spec, err = getIsuladContainerSpec(id)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Warnf("fail to get isulad container %v spec: %v", id, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
container := &Container{
|
|
||||||
pid: pid,
|
|
||||||
name: name,
|
|
||||||
containerID: id,
|
|
||||||
spec: spec,
|
|
||||||
containerPath: filepath.Join(storagePath, id),
|
|
||||||
}
|
|
||||||
return container, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pid returns the pid of the container
|
|
||||||
func (c *Container) Pid() int {
|
|
||||||
return c.pid
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerID returns the container id
|
|
||||||
func (c *Container) ContainerID() string {
|
|
||||||
return c.containerID
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name returns the container name input by user.
|
|
||||||
func (c *Container) Name() string {
|
|
||||||
return c.name
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerPath returns the container config storage path.
|
|
||||||
func (c *Container) ContainerPath() string {
|
|
||||||
return c.containerPath
|
|
||||||
}
|
|
||||||
|
|
||||||
// NetNsPath returns the net namespace path of the container
|
|
||||||
func (c *Container) NetNsPath() string {
|
|
||||||
return fmt.Sprintf("/proc/%d/ns/net", c.pid)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lock uses file lock to lock the container
|
|
||||||
// to make sure only one handler could access the container resource
|
|
||||||
func (c *Container) Lock() error {
|
|
||||||
fileName := filepath.Join(c.ContainerPath(), deviceHookLock)
|
|
||||||
f, err := os.OpenFile(fileName, os.O_RDONLY|os.O_CREATE, 0600)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// FileLock will be released at 3 conditions:
|
|
||||||
// 1. process to unlock manully.
|
|
||||||
// 2. Close the opened fd.
|
|
||||||
// 3. process died without call unlock. kernel will close the file and release the lock.
|
|
||||||
// LOCK_EX means only one process could lock it at one time.
|
|
||||||
// LOCK_NB is not set, using block mode.
|
|
||||||
if err := unix.Flock(int(f.Fd()), unix.LOCK_EX); err != nil {
|
|
||||||
f.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.lock = f
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unlock will release the file lock
|
|
||||||
func (c *Container) Unlock() error {
|
|
||||||
defer c.lock.Close()
|
|
||||||
return unix.Flock(int(c.lock.Fd()), unix.LOCK_UN)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCgroupPath returns the cgroup-parent segment of the container.
|
|
||||||
// For isulad container, it is a configurable segment.
|
|
||||||
func (c *Container) GetCgroupPath() (string, error) {
|
|
||||||
cmd := exec.Command("isula", "inspect", "-f", "{{json .HostConfig.CgroupParent}}", c.name)
|
|
||||||
out, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("%s: %v", string(out), err)
|
|
||||||
}
|
|
||||||
cgroupPath := strings.Trim(string(out), "\n")
|
|
||||||
if len(cgroupPath) >= 2 {
|
|
||||||
cgroupPath = cgroupPath[1 : len(cgroupPath)-1]
|
|
||||||
}
|
|
||||||
if cgroupPath == "" {
|
|
||||||
// by default, the cgroup path is "/isulad/<id>"
|
|
||||||
cgroupPath = "/isulad"
|
|
||||||
}
|
|
||||||
cgroupPath = filepath.Join(cgroupPath, c.containerID)
|
|
||||||
return cgroupPath, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSpec get container spec
|
|
||||||
func (c *Container) GetSpec() *specs.Spec {
|
|
||||||
return c.spec
|
|
||||||
}
|
|
||||||
|
|
||||||
// getIsuladContainerID returns the isulad container ID via the container name
|
|
||||||
func getIsuladContainerID(name string) (string, error) {
|
|
||||||
cmd := exec.Command("isula", "inspect", "-f", "{{json .Id}}", name)
|
|
||||||
out, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("%s: %v", string(out), err)
|
|
||||||
}
|
|
||||||
return strings.Trim(strings.Trim(string(out), "\n"), "\""), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getIsuladContainerPid returns the isulad container process id via the container name
|
|
||||||
func getIsuladContainerPid(name string) (int, error) {
|
|
||||||
cmd := exec.Command("isula", "inspect", "-f", "{{json .State.Pid}}", name)
|
|
||||||
out, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
return -1, fmt.Errorf("%s: %v", string(out), err)
|
|
||||||
}
|
|
||||||
strPid := strings.Trim(string(out), "\n")
|
|
||||||
pid, err := strconv.Atoi(strPid)
|
|
||||||
if err != nil {
|
|
||||||
return -1, fmt.Errorf("failed to convert %q to int: %v", strPid, err)
|
|
||||||
}
|
|
||||||
return pid, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getIsuladContainerSpec(id string) (spec *specs.Spec, err error) {
|
|
||||||
graphDriverPath, err := getIsuladGraphDriverPath()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
configPath := fmt.Sprintf("%s/engines/lcr/%s/config.json", graphDriverPath, id)
|
|
||||||
config, err := os.Open(configPath)
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return nil, fmt.Errorf("config file %s not found", configPath)
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if config != nil {
|
|
||||||
config.Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
if err := json.NewDecoder(config).Decode(&spec); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return spec, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getIsuladGraphDriverPath() (string, error) {
|
|
||||||
cmd := exec.Command("isula", "info")
|
|
||||||
out, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("Exec isula info failed: %v", err)
|
|
||||||
}
|
|
||||||
// Find "iSulad Root Dir: /xx/xx" line. and out is still has the rest characters.
|
|
||||||
if index := strings.Index(string(out), "iSulad Root Dir:"); index != -1 {
|
|
||||||
// Split to array, and the first line is the "iSulad Root Dir"
|
|
||||||
arr := strings.Split(string(out)[index:], "\n")
|
|
||||||
// Split to find " /xxx/xxx"
|
|
||||||
array := strings.Split(arr[0], ":")
|
|
||||||
if len(array) > 1 {
|
|
||||||
// Trim all the spaces.
|
|
||||||
rootdir := strings.Trim(array[1], " ")
|
|
||||||
return rootdir, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", fmt.Errorf("Faild to parse isula info, no \"iSulad Root Dir:\" found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetContainerPath set container path
|
|
||||||
func (c *Container) SetContainerPath(path string) {
|
|
||||||
c.containerPath = path
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// CheckPidExist check pid exist or not
|
|
||||||
func (c *Container) CheckPidExist() bool {
|
|
||||||
if _, err := os.Stat(fmt.Sprintf("/proc/%d", c.Pid())); err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
404
device.go
404
device.go
@ -1,404 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: device commands
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
// go base main package
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
hconfig "isula.org/syscontainer-tools/config"
|
|
||||||
"isula.org/syscontainer-tools/container"
|
|
||||||
"isula.org/syscontainer-tools/libdevice"
|
|
||||||
"isula.org/syscontainer-tools/types"
|
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
var addDevCommand = cli.Command{
|
|
||||||
Name: "add-device",
|
|
||||||
Usage: "add one or more host devices to container",
|
|
||||||
ArgsUsage: `<container_id> hostdevice[:containerdevice][:permission] [hostdevice[:containerdevice][:permission] ...]`,
|
|
||||||
Description: `You can add mutiple host devices to container.
|
|
||||||
The program will error out when the host device is not a device or the container device already exists.`,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.StringSliceFlag{
|
|
||||||
Name: "blkio-weight-device",
|
|
||||||
Usage: "Set Block IO weight (relative device weight, between 10 and 1000)",
|
|
||||||
},
|
|
||||||
cli.StringSliceFlag{
|
|
||||||
Name: "device-read-bps",
|
|
||||||
Usage: "Limit read rate (bytes per second) from a device",
|
|
||||||
},
|
|
||||||
cli.StringSliceFlag{
|
|
||||||
Name: "device-read-iops",
|
|
||||||
Usage: "Limit read rate (IO per second) from a device",
|
|
||||||
},
|
|
||||||
cli.StringSliceFlag{
|
|
||||||
Name: "device-write-bps",
|
|
||||||
Usage: "Limit write rate (bytes per second) to a device",
|
|
||||||
},
|
|
||||||
cli.StringSliceFlag{
|
|
||||||
Name: "device-write-iops",
|
|
||||||
Usage: "Limit write rate (IO per second) to a device",
|
|
||||||
},
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "follow-partition",
|
|
||||||
Usage: "If disk is a base device, add all the sub partitions to container",
|
|
||||||
},
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "force",
|
|
||||||
Usage: "If device exists in container, will cover the old file.",
|
|
||||||
},
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "update-config-only",
|
|
||||||
Usage: "If this flag is set, will not add device to container but update config only",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Action: func(context *cli.Context) {
|
|
||||||
if context.NArg() < 2 {
|
|
||||||
fatalf("%s: %q requires a minimum of 2 args", os.Args[0], context.Command.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
blkioWeight, err := libdevice.ParseAddDeviceBlkioWeight(context.StringSlice("blkio-weight-device"))
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
readBps, err := libdevice.ParseAddDeviceQosOption(context.StringSlice("device-read-bps"))
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
writeBps, err := libdevice.ParseAddDeviceQosOption(context.StringSlice("device-write-bps"))
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
readIOPS, err := libdevice.ParseAddDeviceQosOption(context.StringSlice("device-read-iops"))
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
writeIOPS, err := libdevice.ParseAddDeviceQosOption(context.StringSlice("device-write-iops"))
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
devices, err := getDevices(context)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
name := context.Args()[0]
|
|
||||||
c, err := container.New(name)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := setDevicesPath(c, devices); err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
opts := &types.AddDeviceOptions{
|
|
||||||
Force: context.Bool("force"),
|
|
||||||
UpdateConfigOnly: context.Bool("update-config-only"),
|
|
||||||
ReadBps: readBps,
|
|
||||||
WriteBps: writeBps,
|
|
||||||
ReadIOPS: readIOPS,
|
|
||||||
WriteIOPS: writeIOPS,
|
|
||||||
BlkioWeight: blkioWeight,
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle add device here
|
|
||||||
if err = libdevice.AddDevice(c, devices, opts); err != nil {
|
|
||||||
fatalf("Failed to add device: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
logrus.Infof("add device to container %q successfully", name)
|
|
||||||
return
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var rmDevCommand = cli.Command{
|
|
||||||
Name: "remove-device",
|
|
||||||
Usage: "remove one or more devices from container",
|
|
||||||
ArgsUsage: `<container_id> hostdevice[:containerdevice] [hostdevice[:containerdevice] ...]`,
|
|
||||||
Description: `You can remove mutiple host devices from container.
|
|
||||||
You can assign hostdevice an empty value, though either of hostdevice and containerdevice should be assigned.
|
|
||||||
The program will error out when the container device does not exist.`,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "follow-partition",
|
|
||||||
Usage: "If disk is a base device, will remove all the sub partitions from container",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Action: func(context *cli.Context) {
|
|
||||||
if context.NArg() < 2 {
|
|
||||||
fatalf("%s: %q requires a minimum of 2 args", os.Args[0], context.Command.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
name := context.Args()[0]
|
|
||||||
c, err := container.New(name)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
devices, err := getMappings(context)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
if err := setDevicesPath(c, devices); err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle remove device here
|
|
||||||
if err = libdevice.RemoveDevice(c, devices, context.Bool("follow-partition")); err != nil {
|
|
||||||
fatalf("Failed to remove device: %v", err)
|
|
||||||
}
|
|
||||||
logrus.Infof("remove device from container %q successfully", name)
|
|
||||||
return
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var listDevCommand = cli.Command{
|
|
||||||
Name: "list-device",
|
|
||||||
Usage: "list all devices in container",
|
|
||||||
ArgsUsage: `<container_id>`,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "pretty, p",
|
|
||||||
Usage: "If this flag is set, list pathes in pretty json form",
|
|
||||||
},
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "sub-partition",
|
|
||||||
Usage: "If disk is a base device, list all the sub partitions by the base disk",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Action: func(context *cli.Context) {
|
|
||||||
if context.NArg() < 1 {
|
|
||||||
fatalf("%s: %q must accept a container-id", os.Args[0], context.Command.Name)
|
|
||||||
}
|
|
||||||
if context.NArg() > 1 {
|
|
||||||
fatalf("Don't put container-id in the middle of options")
|
|
||||||
}
|
|
||||||
|
|
||||||
name := context.Args()[0]
|
|
||||||
c, err := container.New(name)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
allDevices, majorDevices, err := libdevice.ListDevice(c)
|
|
||||||
if err != nil {
|
|
||||||
fatalf("Failed to list device in container: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var outputDevices []*hconfig.DeviceMapping
|
|
||||||
|
|
||||||
if context.Bool("sub-partition") {
|
|
||||||
outputDevices = allDevices
|
|
||||||
} else {
|
|
||||||
outputDevices = majorDevices
|
|
||||||
}
|
|
||||||
|
|
||||||
if outputDevices == nil || len(outputDevices) == 0 {
|
|
||||||
logrus.Infof("list device in container %q successfully", name)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
devicesData, err := json.Marshal(outputDevices)
|
|
||||||
if err != nil {
|
|
||||||
fatalf("failed to Marshal device config: %v", err)
|
|
||||||
}
|
|
||||||
devicesBuffer := new(bytes.Buffer)
|
|
||||||
if _, err = devicesBuffer.Write(devicesData); err != nil {
|
|
||||||
fatalf("Buffer Write error %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if context.Bool("pretty") {
|
|
||||||
devicesBuffer.Truncate(0)
|
|
||||||
if json.Indent(devicesBuffer, devicesData, "", "\t") != nil {
|
|
||||||
fatalf("failed to Indent device data: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = devicesBuffer.WriteString("\n"); err != nil {
|
|
||||||
fatalf("Buffer WriteString error %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = os.Stdout.Write(devicesBuffer.Bytes()); err != nil {
|
|
||||||
logrus.Errorf("os.Stdout.Write error : %v", err)
|
|
||||||
}
|
|
||||||
logrus.Infof("list devices in container %q successfully", name)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var updateDevCommand = cli.Command{
|
|
||||||
Name: "update-device",
|
|
||||||
Usage: "update configuration of device",
|
|
||||||
ArgsUsage: `<container_id>`,
|
|
||||||
Description: `You can update configuration of container devices.`,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.StringSliceFlag{
|
|
||||||
Name: "device-read-bps",
|
|
||||||
Usage: "Limit read rate (bytes per second) from a device",
|
|
||||||
},
|
|
||||||
cli.StringSliceFlag{
|
|
||||||
Name: "device-read-iops",
|
|
||||||
Usage: "Limit read rate (IO per second) from a device",
|
|
||||||
},
|
|
||||||
cli.StringSliceFlag{
|
|
||||||
Name: "device-write-bps",
|
|
||||||
Usage: "Limit write rate (bytes per second) to a device",
|
|
||||||
},
|
|
||||||
cli.StringSliceFlag{
|
|
||||||
Name: "device-write-iops",
|
|
||||||
Usage: "Limit write rate (IO per second) to a device",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Action: func(context *cli.Context) {
|
|
||||||
if context.NArg() < 1 {
|
|
||||||
fatalf("%s: %q requires a minimum of 1 args", os.Args[0], context.Command.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
readBps, err := libdevice.ParseAddDeviceQosOption(context.StringSlice("device-read-bps"))
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
writeBps, err := libdevice.ParseAddDeviceQosOption(context.StringSlice("device-write-bps"))
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
readIOPS, err := libdevice.ParseAddDeviceQosOption(context.StringSlice("device-read-iops"))
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
writeIOPS, err := libdevice.ParseAddDeviceQosOption(context.StringSlice("device-write-iops"))
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(readBps) == 0 && len(writeBps) == 0 && len(readIOPS) == 0 && len(writeIOPS) == 0 {
|
|
||||||
fatalf("update device should specify at least one device QOS configuration")
|
|
||||||
}
|
|
||||||
|
|
||||||
name := context.Args()[0]
|
|
||||||
c, err := container.New(name)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
opts := &types.AddDeviceOptions{
|
|
||||||
ReadBps: readBps,
|
|
||||||
WriteBps: writeBps,
|
|
||||||
ReadIOPS: readIOPS,
|
|
||||||
WriteIOPS: writeIOPS,
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle add device here
|
|
||||||
if err = libdevice.UpdateDevice(c, opts); err != nil {
|
|
||||||
fatalf("Failed to update device: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
logrus.Infof("update device configure in container %q successfully", name)
|
|
||||||
return
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDevices(context *cli.Context) ([]*types.Device, error) {
|
|
||||||
var devices []*types.Device
|
|
||||||
followPartition := context.Bool("follow-partition")
|
|
||||||
for k := 1; k < context.NArg(); k++ {
|
|
||||||
v := context.Args()[k]
|
|
||||||
device, err := libdevice.ParseDevice(context.Args()[k])
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to parse device: %s, %v", v, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if device.Type == "c" {
|
|
||||||
if followPartition {
|
|
||||||
return nil, fmt.Errorf("Char device %s not support follow partition", v)
|
|
||||||
}
|
|
||||||
devices = append(devices, device)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
basedev, err := types.GetBaseDevName(device.PathOnHost)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
devType, err := types.GetDeviceType(device.PathOnHost)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if devType != "lvm" {
|
|
||||||
device.Parent = basedev
|
|
||||||
}
|
|
||||||
|
|
||||||
devices = append(devices, device)
|
|
||||||
|
|
||||||
if followPartition && devType == "disk" {
|
|
||||||
// Add sub-partition here
|
|
||||||
subDevices := libdevice.FindSubPartition(device)
|
|
||||||
for _, dev := range subDevices {
|
|
||||||
found := false
|
|
||||||
for _, eDev := range devices {
|
|
||||||
if dev.PathOnHost == eDev.PathOnHost {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
devices = append(devices, dev)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return devices, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getMappings(context *cli.Context) ([]*types.Device, error) {
|
|
||||||
var devices []*types.Device
|
|
||||||
for k := 1; k < context.NArg(); k++ {
|
|
||||||
v := context.Args()[k]
|
|
||||||
device, err := libdevice.ParseMapping(context.Args()[k])
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to parse device mapping: %s, %v", v, err)
|
|
||||||
}
|
|
||||||
devices = append(devices, device)
|
|
||||||
}
|
|
||||||
return devices, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func setDevicesPath(c *container.Container, devices []*types.Device) error {
|
|
||||||
if err := c.Lock(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer c.Unlock()
|
|
||||||
config, err := hconfig.NewContainerConfig(c)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, dev := range devices {
|
|
||||||
if index := config.DeviceIndexInArray(dev); index != -1 {
|
|
||||||
found := config.GetAllDevices()[index]
|
|
||||||
dev.Path = found.PathInContainer
|
|
||||||
dev.PathOnHost = found.PathOnHost
|
|
||||||
}
|
|
||||||
libdevice.SetDefaultPath(dev)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
33
go.mod
33
go.mod
@ -1,33 +0,0 @@
|
|||||||
module isula.org/syscontainer-tools
|
|
||||||
|
|
||||||
replace (
|
|
||||||
golang.org/x/crypto => github.com/golang/crypto v0.0.0-20190701094942-4def268fd1a4
|
|
||||||
golang.org/x/net => github.com/golang/net v0.0.0-20190813141303-74dc4d7220e7
|
|
||||||
golang.org/x/sync => github.com/golang/sync v0.0.0-20190423024810-112230192c58
|
|
||||||
golang.org/x/sys => github.com/golang/sys v0.0.0-20190813064441-fde4db37ae7a
|
|
||||||
golang.org/x/text => github.com/golang/text v0.3.2
|
|
||||||
golang.org/x/tools => github.com/golang/tools v0.0.0-20190815235612-5b08f89bfc0c
|
|
||||||
golang.org/x/xerrors => github.com/golang/xerrors v0.0.0-20190717185122-a985d3407aa7
|
|
||||||
gopkg.in/yaml.v2 => github.com/go-yaml/yaml v2.1.0+incompatible
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/Microsoft/go-winio v0.4.14 // indirect
|
|
||||||
github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b // indirect
|
|
||||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
|
|
||||||
github.com/docker/docker v1.13.1
|
|
||||||
github.com/docker/go-units v0.4.0 // indirect
|
|
||||||
github.com/docker/libnetwork v0.5.6
|
|
||||||
github.com/godbus/dbus v4.1.0+incompatible // indirect
|
|
||||||
github.com/golang/protobuf v1.3.2 // indirect
|
|
||||||
github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618 // indirect
|
|
||||||
github.com/opencontainers/runc v1.0.0-rc3
|
|
||||||
github.com/opencontainers/runtime-spec v1.0.0-rc5
|
|
||||||
github.com/seccomp/libseccomp-golang v0.9.1 // indirect
|
|
||||||
github.com/sirupsen/logrus v1.4.2
|
|
||||||
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 // indirect
|
|
||||||
github.com/urfave/cli v1.21.0
|
|
||||||
github.com/vishvananda/netlink v1.0.0
|
|
||||||
github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f
|
|
||||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b
|
|
||||||
)
|
|
||||||
46
go.sum
46
go.sum
@ -1,46 +0,0 @@
|
|||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
|
||||||
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
|
||||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
|
||||||
github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b h1:+mtZ0WjVZwTX0RVrXMXDwuYVaNeHGvWBW1UwJeMR+2M=
|
|
||||||
github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
|
||||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
|
|
||||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo=
|
|
||||||
github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
|
||||||
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
|
||||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
|
||||||
github.com/docker/libnetwork v0.5.6 h1:hnGiypBsZR6PW1I8lqaBHh06U6LCJbI3IhOvfsZiymY=
|
|
||||||
github.com/docker/libnetwork v0.5.6/go.mod h1:93m0aTqz6z+g32wla4l4WxTrdtvBRmVzYRkYvasA5Z8=
|
|
||||||
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
|
|
||||||
github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4=
|
|
||||||
github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
|
||||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/sys v0.0.0-20190813064441-fde4db37ae7a h1:hVLU4+cxX4r89gounKarktyMqZ2cx/5Y2jeGLtWqzUE=
|
|
||||||
github.com/golang/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
|
||||||
github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618 h1:7InQ7/zrOh6SlFjaXFubv0xX0HsuC9qJsdqm7bNQpYM=
|
|
||||||
github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0=
|
|
||||||
github.com/opencontainers/runc v1.0.0-rc3 h1:tGkPg19g46ZCB9eiKd4Jd0uJ0K17lpsA3ya26UiQFLE=
|
|
||||||
github.com/opencontainers/runc v1.0.0-rc3/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
|
||||||
github.com/opencontainers/runtime-spec v1.0.0-rc5 h1:gUJ82jaA7l+A8tWYQL9Pzr5kh4IbzOP6Qe50sKUzgP0=
|
|
||||||
github.com/opencontainers/runtime-spec v1.0.0-rc5/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
||||||
github.com/seccomp/libseccomp-golang v0.9.1 h1:NJjM5DNFOs0s3kYE1WUOr6G8V97sdt46rlXTMfXGWBo=
|
|
||||||
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
|
||||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
|
||||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
|
||||||
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 h1:b6uOv7YOFK0TYG7HtkIgExQo+2RdLuwRft63jn2HWj8=
|
|
||||||
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
|
||||||
github.com/urfave/cli v1.21.0 h1:wYSSj06510qPIzGSua9ZqsncMmWE3Zr55KBERygyrxE=
|
|
||||||
github.com/urfave/cli v1.21.0/go.mod h1:lxDj6qX9Q6lWQxIrbrT0nwecwUtRnhVZAJjJZrVUZZQ=
|
|
||||||
github.com/vishvananda/netlink v1.0.0 h1:bqNY2lgheFIu1meHUFSH3d7vG93AFyqg3oGbJCOJgSM=
|
|
||||||
github.com/vishvananda/netlink v1.0.0/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
|
|
||||||
github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f h1:nBX3nTcmxEtHSERBJaIo1Qa26VwRaopnZmfDQUXsF4I=
|
|
||||||
github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
|
|
||||||
@ -1,57 +0,0 @@
|
|||||||
# Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
# syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
# You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
# http://license.coscl.org.cn/MulanPSL2
|
|
||||||
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
# PURPOSE.
|
|
||||||
# See the Mulan PSL v2 for more details.
|
|
||||||
# Description: make install
|
|
||||||
# Author: zhangwei
|
|
||||||
# Create: 2018-01-18
|
|
||||||
|
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
HOOK_DIR=/var/lib/isulad/hooks
|
|
||||||
ISULAD_TOOLS_DIR=/usr/local/bin
|
|
||||||
ISULAD_TOOLS_WRAPPER="/lib/udev"
|
|
||||||
HOOK_SPEC="/etc/syscontainer-tools"
|
|
||||||
|
|
||||||
echo "Hooks will be installed to $HOOK_DIR"
|
|
||||||
echo "syscontainer_tools will be installed to $ISULAD_TOOLS_DIR"
|
|
||||||
|
|
||||||
mkdir -p -m 0700 ${HOOK_DIR}
|
|
||||||
mkdir -p -m 0750 ${ISULAD_TOOLS_DIR}
|
|
||||||
mkdir -p -m 0750 ${ISULAD_TOOLS_WRAPPER}
|
|
||||||
mkdir -p -m 0750 ${HOOK_SPEC}
|
|
||||||
|
|
||||||
install -m 0755 -p ../build/*-hooks ${HOOK_DIR}
|
|
||||||
install -m 0755 -p ../build/syscontainer-tools ${ISULAD_TOOLS_DIR}
|
|
||||||
install -m 0750 syscontainer-tools_wrapper ${ISULAD_TOOLS_WRAPPER}/syscontainer-tools_wrapper
|
|
||||||
|
|
||||||
cat << EOF > ${HOOK_SPEC}/hookspec.json
|
|
||||||
{
|
|
||||||
"prestart": [
|
|
||||||
{
|
|
||||||
"path": "${HOOK_DIR}/syscontainer-hooks",
|
|
||||||
"args": ["syscontainer-hooks", "--state", "prestart"],
|
|
||||||
"env": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"poststart":[
|
|
||||||
{
|
|
||||||
"path": "${HOOK_DIR}/syscontainer-hooks",
|
|
||||||
"args": ["syscontainer-hooks", "--state", "poststart"],
|
|
||||||
"env": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"poststop":[
|
|
||||||
{
|
|
||||||
"path": "${HOOK_DIR}/syscontainer-hooks",
|
|
||||||
"args": ["syscontainer-hooks", "--state", "poststop"],
|
|
||||||
"env": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
# syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
# You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
# http://license.coscl.org.cn/MulanPSL2
|
|
||||||
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
# PURPOSE.
|
|
||||||
# See the Mulan PSL v2 for more details.
|
|
||||||
# Description: syscontainer tools wrapper
|
|
||||||
# Author: zhangsong234
|
|
||||||
# Create: 2020-01-17
|
|
||||||
|
|
||||||
LOG_DIR=/var/log/hyperagent
|
|
||||||
LOG_FILE=${LOG_DIR}/syscontainer-tools.log
|
|
||||||
|
|
||||||
## by default, isulad is installed in /usr/bin,
|
|
||||||
## but udevd do not have this path in PATH env
|
|
||||||
export PATH=$PATH:/usr/bin:/usr/local/bin:/usr/sbin
|
|
||||||
[ -d "$LOG_DIR" ] || mkdir -p $LOG_DIR
|
|
||||||
|
|
||||||
run_cmd() {
|
|
||||||
echo [$(date)]: $@ >> $LOG_FILE
|
|
||||||
$@
|
|
||||||
}
|
|
||||||
add_node() {
|
|
||||||
run_cmd syscontainer-tools --log $LOG_FILE add-device $id /dev/$o_dev:$devname
|
|
||||||
}
|
|
||||||
remove_node() {
|
|
||||||
run_cmd syscontainer-tools --log $LOG_FILE remove-device $id /dev/$o_dev:$devname
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
## $1 : Udev Action: (add|remove)
|
|
||||||
## $2 : Container ID
|
|
||||||
## $3 : Devname to mknod on host, like: sdc1
|
|
||||||
## $4 : Contianer device basename, like /dev/sdx
|
|
||||||
## $5 : Basename of the device on host and kernel
|
|
||||||
##
|
|
||||||
## We need to get the mknod number and compose with container device name together.
|
|
||||||
## eg: $3=sdc1, $4=/dev/sdx $5=sdc
|
|
||||||
## ==> $4 + ($3-$5) = /dev/sdx1
|
|
||||||
action=$1
|
|
||||||
id=$2
|
|
||||||
o_dev=$3
|
|
||||||
devname=$4$(echo $3 | sed "s/$5//g")
|
|
||||||
|
|
||||||
${action}_node
|
|
||||||
@ -1,94 +0,0 @@
|
|||||||
# syscontainer-hooks
|
|
||||||
|
|
||||||
This is a simple custom syscontainer hook for our own need,
|
|
||||||
it interacts with isulad as a multifunctional hook.
|
|
||||||
|
|
||||||
1. allow user to add your own devices or binds into the container and update device Qos for container(device hook in prestart state).
|
|
||||||
2. allow user to remove udev rule which added by syscontainer-tools when container is exiting(device hook in post-stop state).
|
|
||||||
3. allow user to add network interface and route rule to container(network hook in prestart state).
|
|
||||||
4. allow user to remove network interface on host when container is exiting(network hook in post-stop state).
|
|
||||||
5. allow user to do oci relabel for container in both prestart and post-stop state for container.
|
|
||||||
|
|
||||||
Actually, this hook only handles the container restart process, we use syscontainer-tools to
|
|
||||||
add device/binds/network interface/route rule to container. And syscontainer-tools will save the device/network config to disk.
|
|
||||||
And the hook will make sure the resources you added to container will be persistent after restart.
|
|
||||||
|
|
||||||
Rename it to your favourite name afterwards.
|
|
||||||
|
|
||||||
## build
|
|
||||||
|
|
||||||
To build the binary, you need to download it then run
|
|
||||||
|
|
||||||
```
|
|
||||||
# make
|
|
||||||
# sudo make install
|
|
||||||
```
|
|
||||||
|
|
||||||
Note: make install will install the binary into your "/usr/bin",
|
|
||||||
it's not a mandatory step, make your own choice for your convenience :)
|
|
||||||
|
|
||||||
|
|
||||||
## customise hook service
|
|
||||||
|
|
||||||
We could use `syscontainer-hooks` to customise the hook service.
|
|
||||||
```
|
|
||||||
Usage of syscontainer-hooks:
|
|
||||||
-log string
|
|
||||||
set output log file
|
|
||||||
-state string
|
|
||||||
set syscontainer hook state mode: prestart or poststop
|
|
||||||
-with-relabel
|
|
||||||
syscontainer hook enable oci relabel hook function
|
|
||||||
```
|
|
||||||
|
|
||||||
As block device and network interface are both in our requirement, so these two function are mandantory.
|
|
||||||
We could use `--with-relabel=true` to add oci-relabel hook service for container.
|
|
||||||
We could use `--state` to specify which state the hook will be running in.
|
|
||||||
|
|
||||||
Full hook config:
|
|
||||||
[hook spec example of syscontainer-hooks](hooks/syscontainer-hooks/example/hookspec.json)
|
|
||||||
|
|
||||||
## Try it!
|
|
||||||
|
|
||||||
First you need an enhanced `isula` with newly added `--hook-spec` flag,
|
|
||||||
after that, you can run it like this:
|
|
||||||
|
|
||||||
1.run isulad container with hook spec in `example` directory
|
|
||||||
|
|
||||||
```
|
|
||||||
$ isula run -d --name test_device --hook-spec $PWD/example/hookspec.json busybox sleep 20000
|
|
||||||
```
|
|
||||||
2.use syscontainer-tools to add device or binds to container
|
|
||||||
|
|
||||||
```
|
|
||||||
syscontainer-tools add-device test_device /dev/zero:/dev/test_zero:rwm /dev/zero:/dev/test_zero2:rwm
|
|
||||||
```
|
|
||||||
|
|
||||||
3.restart the container. to check the device is still in container.
|
|
||||||
|
|
||||||
```
|
|
||||||
isula restart test_device
|
|
||||||
```
|
|
||||||
|
|
||||||
Let's check the [`hookspec.json`](example/hookspec.json) file:
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"prestart": [
|
|
||||||
{
|
|
||||||
"path": "/var/lib/isulad/hooks/device-hook",
|
|
||||||
"args": ["device-hook"],
|
|
||||||
"env": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"poststart":[],
|
|
||||||
"poststop":[]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
# Contact me
|
|
||||||
|
|
||||||
If you have any question or suggestion, contact me!
|
|
||||||
Also welcome for any issue or MR! Thanks!
|
|
||||||
|
|
||||||
Author: Zhang Wentao <zhangwentao234@huawei.com>
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"prestart": [
|
|
||||||
{
|
|
||||||
"path": "/var/lib/isulad/hooks/syscontainer-hooks",
|
|
||||||
"args": ["syscontainer-hooks", "--state", "prestart", "--log", "/var/log/device-hook.log"],
|
|
||||||
"env": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"poststart":[
|
|
||||||
{
|
|
||||||
"path": "/var/lib/isulad/hooks/syscontainer-hooks",
|
|
||||||
"args": ["syscontainer-hooks", "--state", "prestart", "--log", "/var/log/device-hook.log"],
|
|
||||||
"env": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"poststop":[
|
|
||||||
{
|
|
||||||
"path": "/var/lib/isulad/hooks/syscontainer-hooks",
|
|
||||||
"args": ["syscontainer-hooks", "--state", "poststop", "--log", "/var/log/device-hook.log"],
|
|
||||||
"env": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -1,231 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: syscontainer hook main function
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
// go base main package
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"path/filepath"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/reexec"
|
|
||||||
"github.com/opencontainers/runc/libcontainer/configs"
|
|
||||||
_ "github.com/opencontainers/runc/libcontainer/nsenter"
|
|
||||||
"github.com/opencontainers/runtime-spec/specs-go"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
hconfig "isula.org/syscontainer-tools/config"
|
|
||||||
"isula.org/syscontainer-tools/container"
|
|
||||||
"isula.org/syscontainer-tools/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
defaultHookConfigFile = "device_hook.json"
|
|
||||||
syslogTag = "hook "
|
|
||||||
bundleConfigFile = "config.json"
|
|
||||||
)
|
|
||||||
|
|
||||||
type hookData struct {
|
|
||||||
spec *specs.Spec
|
|
||||||
state *configs.HookState
|
|
||||||
hookConfig *hconfig.ContainerHookConfig
|
|
||||||
storagePath string
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupLog(logfile string) {
|
|
||||||
logrus.SetLevel(logrus.DebugLevel)
|
|
||||||
logrus.SetOutput(os.Stdout)
|
|
||||||
if logfile != "" {
|
|
||||||
f, err := os.OpenFile(logfile, os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_SYNC, 0600)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logrus.SetOutput(f)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fm := &logrus.TextFormatter{DisableTimestamp: true, DisableColors: true}
|
|
||||||
logrus.SetFormatter(fm)
|
|
||||||
if err := utils.HookSyslog("", syslogTag); err != nil {
|
|
||||||
fmt.Fprintf(os.Stdout, "%v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func fatal(err error) {
|
|
||||||
if err != nil {
|
|
||||||
logrus.Error(err)
|
|
||||||
fmt.Fprintln(os.Stderr, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func prepareHookData() (*hookData, error) {
|
|
||||||
var (
|
|
||||||
err error
|
|
||||||
spec *specs.Spec
|
|
||||||
compatSpec *specs.CompatSpec
|
|
||||||
state *configs.HookState
|
|
||||||
hookConfig *hconfig.ContainerHookConfig
|
|
||||||
containerStoragePath = ""
|
|
||||||
)
|
|
||||||
|
|
||||||
if state, err = utils.ParseHookState(os.Stdin); err != nil {
|
|
||||||
logrus.Errorf("Parse Hook State Failed: %v", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load container OCI spec from config
|
|
||||||
configFile := bundleConfigFile
|
|
||||||
if spec, err = utils.LoadSpec(filepath.Join(state.Bundle, configFile)); err != nil {
|
|
||||||
if compatSpec, err = utils.LoadCompatSpec(filepath.Join(state.Bundle, configFile)); err != nil {
|
|
||||||
logrus.Errorf("Failed to load spec for contianer %s: %v", state.ID, err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if compatSpec != nil {
|
|
||||||
capabilities := compatSpec.Process.Capabilities
|
|
||||||
spec = &compatSpec.Spec
|
|
||||||
spec.Process = compatSpec.Process.Process
|
|
||||||
spec.Process.Capabilities = &specs.LinuxCapabilities{
|
|
||||||
Bounding: capabilities,
|
|
||||||
Effective: capabilities,
|
|
||||||
Inheritable: capabilities,
|
|
||||||
Permitted: capabilities,
|
|
||||||
Ambient: capabilities,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if containerStoragePath, err = utils.GetContainerStoragePath(); err != nil {
|
|
||||||
logrus.Errorf("Failed to get container storage path: %v", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
configPath := filepath.Join(containerStoragePath, state.ID, defaultHookConfigFile)
|
|
||||||
if _, err := os.Stat(configPath); err != nil {
|
|
||||||
// not an error, user do not add/remove device to/from this container.
|
|
||||||
return &hookData{
|
|
||||||
spec: spec,
|
|
||||||
state: state,
|
|
||||||
hookConfig: &hconfig.ContainerHookConfig{},
|
|
||||||
storagePath: containerStoragePath,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load devices and binds config for container.
|
|
||||||
if hookConfig, err = hconfig.LoadContainerHookConfig(configPath); err != nil {
|
|
||||||
logrus.Errorf("Failed to parse Config File for container %s: %v", state.ID, err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &hookData{
|
|
||||||
spec: spec,
|
|
||||||
state: state,
|
|
||||||
hookConfig: hookConfig,
|
|
||||||
storagePath: containerStoragePath,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
if reexec.Init() {
|
|
||||||
// `reexec routine` was registered in syscontainer-tools/libdevice
|
|
||||||
// Sub nsenter process will come here.
|
|
||||||
// Isulad reexec package do not handle errors.
|
|
||||||
// And sub device-hook nsenter init process will send back the error message to parenet through pipe.
|
|
||||||
// So here do not need to handle errors.
|
|
||||||
return
|
|
||||||
}
|
|
||||||
signal.Ignore(syscall.SIGINT, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM)
|
|
||||||
|
|
||||||
flLogfile := flag.String("log", "", "set output log file")
|
|
||||||
flMode := flag.String("state", "", "set syscontainer hook state mode: prestart or poststop")
|
|
||||||
// No requirements at present, by default don't enable this function.
|
|
||||||
flWithRelabel := flag.Bool("with-relabel", false, "syscontainer hook enable oci relabel hook function")
|
|
||||||
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
setupLog(*flLogfile)
|
|
||||||
hData, err := prepareHookData()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := os.MkdirAll(hconfig.IsuladToolsDir, 0666); err != nil {
|
|
||||||
logrus.Errorf("failed to set syscontainer-tools dir: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch *flMode {
|
|
||||||
case "prestart":
|
|
||||||
if hData.state.Pid <= 0 {
|
|
||||||
logrus.Errorf("can't get correct pid of container: %d", hData.state.Pid)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := prestartHook(hData, *flWithRelabel); err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := updateHookData(hData); err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
case "poststart":
|
|
||||||
if hData.state.Pid <= 0 {
|
|
||||||
logrus.Errorf("can't get correct pid of container: %d", hData.state.Pid)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := poststartHook(hData, *flWithRelabel); err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := updateHookData(hData); err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case "poststop":
|
|
||||||
postStopHook(hData, *flWithRelabel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateHookData(data *hookData) error {
|
|
||||||
|
|
||||||
var (
|
|
||||||
err error
|
|
||||||
containerStoragePath = ""
|
|
||||||
)
|
|
||||||
|
|
||||||
c := &container.Container{}
|
|
||||||
if data.storagePath != "" {
|
|
||||||
containerStoragePath = data.storagePath
|
|
||||||
} else {
|
|
||||||
containerStoragePath, err = utils.GetContainerStoragePath()
|
|
||||||
if err != nil {
|
|
||||||
logrus.Errorf("Failed to get container storage path: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c.SetContainerPath(filepath.Join(containerStoragePath, data.state.ID))
|
|
||||||
|
|
||||||
if err := c.Lock(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer c.Unlock()
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if err := data.hookConfig.Flush(); err != nil {
|
|
||||||
logrus.Infof("config Flush error:%v", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: poststart hook
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
// go base main package
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "github.com/opencontainers/runc/libcontainer/nsenter"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
// prestartHook is the main logic of device hook
|
|
||||||
func poststartHook(data *hookData, withRelabel bool) error {
|
|
||||||
var actions []HookAction
|
|
||||||
actions = []HookAction{}
|
|
||||||
for _, ac := range actions {
|
|
||||||
if err := ac(data.state, data.hookConfig, data.spec); err != nil {
|
|
||||||
logrus.Errorf("Failed with err: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@ -1,156 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: poststop hook
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
// go base main package
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/opencontainers/runc/libcontainer/configs"
|
|
||||||
hconfig "isula.org/syscontainer-tools/config"
|
|
||||||
"isula.org/syscontainer-tools/libdevice"
|
|
||||||
"isula.org/syscontainer-tools/libnetwork"
|
|
||||||
"isula.org/syscontainer-tools/pkg/udevd"
|
|
||||||
"isula.org/syscontainer-tools/types"
|
|
||||||
"isula.org/syscontainer-tools/utils"
|
|
||||||
|
|
||||||
"github.com/opencontainers/runtime-spec/specs-go"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
_ "github.com/vishvananda/netlink"
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RemoveUdevRule will remove device udev rule for the stopped container
|
|
||||||
func RemoveUdevRule(state *configs.HookState, hookConfig *hconfig.ContainerHookConfig, spec *specs.Spec) error {
|
|
||||||
udevdCtrl := udevd.NewUdevdController()
|
|
||||||
if err := udevdCtrl.Lock(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer udevdCtrl.Unlock()
|
|
||||||
|
|
||||||
if err := udevdCtrl.LoadRules(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer udevdCtrl.ToDisk()
|
|
||||||
for _, dev := range hookConfig.Devices {
|
|
||||||
// re-calc the dest path of device.
|
|
||||||
resolvDev := calcPathForDevice(state.Root, dev)
|
|
||||||
device, err := libdevice.ParseDevice(resolvDev)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Errorf("[device-hook] Add device (%s), parse device failed: %v", resolvDev, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if device.Type == "c" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
devType, err := types.GetDeviceType(device.PathOnHost)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if devType == "disk" {
|
|
||||||
udevdCtrl.RemoveRule(&udevd.Rule{
|
|
||||||
Name: dev.PathOnHost,
|
|
||||||
CtrDevName: dev.PathInContainer,
|
|
||||||
Container: state.ID,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveNetworkDevices will remove network device after container stop.
|
|
||||||
func RemoveNetworkDevices(state *configs.HookState, hookConfig *hconfig.ContainerHookConfig, spec *specs.Spec) error {
|
|
||||||
|
|
||||||
file, err := os.Open(filepath.Join(hconfig.IsuladToolsDirNetns, state.ID))
|
|
||||||
if err != nil {
|
|
||||||
logrus.Errorf("[device-hook] Failed to Open netns file %v", err)
|
|
||||||
return fmt.Errorf("[device-hook] Failed to Open netns file %v", err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := os.Remove(file.Name()); err != nil {
|
|
||||||
logrus.Errorf("Failed to remove fileName err: %v", err)
|
|
||||||
}
|
|
||||||
file.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
for _, nic := range hookConfig.NetworkInterfaces {
|
|
||||||
err := libnetwork.DelNicFromContainer(filepath.Join(hconfig.IsuladToolsDirNetns, state.ID), nic)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Errorf("[device-hook] Failed to del network interface (%s) from container %s: %v", nic.String(), state.ID, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
logrus.Debugf("Removed %s interface: (%s,%s)", nic.Type, nic.HostNicName, nic.CtrNicName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = unix.Unmount(file.Name(), unix.MNT_DETACH); err != nil {
|
|
||||||
err = fmt.Errorf("[device-hook] Failed to Unmount netns file %v", err)
|
|
||||||
logrus.Errorf("%v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func stringToBind(containerRoot, bindstr string, spec *specs.Spec, isCreate bool) (*types.Bind, error) {
|
|
||||||
// we have done the chroot
|
|
||||||
resolvBind, err := calcPathForBind(containerRoot, bindstr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Re-Calculate bind(%s) failed: %v", bindstr, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
bind, err := libdevice.ParseBind(resolvBind, spec, isCreate)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Parse bind(%s) failed: %v", bindstr, err)
|
|
||||||
}
|
|
||||||
return bind, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveSharedPath will remove shared path after container stop.
|
|
||||||
func RemoveSharedPath(state *configs.HookState, hookConfig *hconfig.ContainerHookConfig, spec *specs.Spec) error {
|
|
||||||
|
|
||||||
for _, bindstr := range hookConfig.Binds {
|
|
||||||
// we have do the chroot
|
|
||||||
bind, err := stringToBind("/", bindstr, spec, false)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Errorf("RemoveSharedPath failed: %s", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := utils.RemoveTransferPath(state.ID, bind); err != nil {
|
|
||||||
logrus.Errorf("RemoveSharedPath failed: Path: %v failed: %s", bind, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
utils.RemoveContainerSpecPath(state.ID)
|
|
||||||
return nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// prestartHook is the main logic of device hook
|
|
||||||
func postStopHook(data *hookData, withRelabel bool) {
|
|
||||||
var actions []HookAction
|
|
||||||
actions = []HookAction{RemoveUdevRule, RemoveNetworkDevices, RemoveSharedPath}
|
|
||||||
if withRelabel {
|
|
||||||
actions = append(actions, PostStopRelabel)
|
|
||||||
}
|
|
||||||
for _, ac := range actions {
|
|
||||||
if err := ac(data.state, data.hookConfig, data.spec); err != nil {
|
|
||||||
logrus.Errorf("Failed with err: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,481 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: prestart hook
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
// go base main package
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/opencontainers/runc/libcontainer/configs"
|
|
||||||
_ "github.com/opencontainers/runc/libcontainer/nsenter"
|
|
||||||
"github.com/opencontainers/runtime-spec/specs-go"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
hconfig "isula.org/syscontainer-tools/config"
|
|
||||||
"isula.org/syscontainer-tools/libdevice"
|
|
||||||
"isula.org/syscontainer-tools/libdevice/nsexec"
|
|
||||||
"isula.org/syscontainer-tools/libnetwork"
|
|
||||||
"isula.org/syscontainer-tools/pkg/udevd"
|
|
||||||
"isula.org/syscontainer-tools/types"
|
|
||||||
"isula.org/syscontainer-tools/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
arrayLen = 3 // calc path for bind array len
|
|
||||||
minor = 7 // runcDevice Minor
|
|
||||||
major = 5 // runcDevice Major
|
|
||||||
)
|
|
||||||
|
|
||||||
// HookAction is the definition of hook action callback
|
|
||||||
type HookAction func(state *configs.HookState, hookConfig *hconfig.ContainerHookConfig, spec *specs.Spec) error
|
|
||||||
|
|
||||||
func fmPtr(mode int64) *os.FileMode {
|
|
||||||
fm := os.FileMode(mode)
|
|
||||||
return &fm
|
|
||||||
}
|
|
||||||
|
|
||||||
// re-parse device
|
|
||||||
func calcPathForDevice(rootfs string, device *hconfig.DeviceMapping) string {
|
|
||||||
paths := []string{device.PathOnHost, filepath.Join(rootfs, device.PathInContainer), device.CgroupPermissions}
|
|
||||||
return strings.Join(paths, ":")
|
|
||||||
}
|
|
||||||
|
|
||||||
func calcPathForBind(rootfs string, bind string) (string, error) {
|
|
||||||
array := strings.SplitN(bind, ":", 3)
|
|
||||||
if len(array) < arrayLen {
|
|
||||||
// this should not happen.
|
|
||||||
// if happen, parseBind will print error
|
|
||||||
return "", fmt.Errorf("Error: bind lack of \":\"")
|
|
||||||
}
|
|
||||||
paths := []string{array[0], filepath.Join(rootfs, array[1]), array[2]}
|
|
||||||
return strings.Join(paths, ":"), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddDevices will add devices to the container
|
|
||||||
func AddDevices(state *configs.HookState, hookConfig *hconfig.ContainerHookConfig, spec *specs.Spec) error {
|
|
||||||
pid := strconv.Itoa(state.Pid)
|
|
||||||
driver := nsexec.NewDefaultNsDriver()
|
|
||||||
|
|
||||||
cgroupPath, err := libdevice.FindCgroupPath(pid, "devices", spec.Linux.CgroupsPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
udevdCtrl := udevd.NewUdevdController()
|
|
||||||
if err := udevdCtrl.Lock(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer udevdCtrl.Unlock()
|
|
||||||
|
|
||||||
if err := udevdCtrl.LoadRules(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
logrus.Infof("Start sync rules to disk")
|
|
||||||
udevdCtrl.ToDisk()
|
|
||||||
logrus.Infof("Finish sync rules to disk")
|
|
||||||
}()
|
|
||||||
|
|
||||||
for index, dev := range hookConfig.Devices {
|
|
||||||
// re-calc the dest path of device.
|
|
||||||
resolvDev := calcPathForDevice(state.Root, dev)
|
|
||||||
device, err := libdevice.ParseDevice(resolvDev)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Errorf("[device-hook] Add device (%s), parse device failed: %v", resolvDev, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// update config here
|
|
||||||
if dev.Major != device.Major || dev.Minor != device.Minor {
|
|
||||||
hookConfig.Devices[index].Major = device.Major
|
|
||||||
hookConfig.Devices[index].Minor = device.Minor
|
|
||||||
hookConfig.SetConfigDirty()
|
|
||||||
}
|
|
||||||
|
|
||||||
if device.Type != "c" {
|
|
||||||
devType, err := types.GetDeviceType(device.PathOnHost)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if devType == "disk" {
|
|
||||||
udevdCtrl.AddRule(&udevd.Rule{
|
|
||||||
Name: dev.PathOnHost,
|
|
||||||
CtrDevName: dev.PathInContainer,
|
|
||||||
Container: state.ID,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// use exec driver to add device.
|
|
||||||
libdevice.UpdateDeviceOwner(spec, device)
|
|
||||||
if err = driver.AddDevice(pid, device, true); err != nil {
|
|
||||||
logrus.Errorf("[device-hook] Add device (%s) failed: %v", resolvDev, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// update cgroup access permission.
|
|
||||||
if err = libdevice.UpdateCgroupPermission(cgroupPath, device, true); err != nil {
|
|
||||||
logrus.Errorf("[device-hook] Update add device (%s) cgroup failed: %v", resolvDev, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddBinds will add the binds to the container
|
|
||||||
func AddBinds(state *configs.HookState, hookConfig *hconfig.ContainerHookConfig, spec *specs.Spec) error {
|
|
||||||
pid := strconv.Itoa(state.Pid)
|
|
||||||
driver := nsexec.NewDefaultNsDriver()
|
|
||||||
|
|
||||||
for _, bindstr := range hookConfig.Binds {
|
|
||||||
bind, err := stringToBind(state.Root, bindstr, spec, true)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Errorf("[device-hook] parse bind error, %s, skipping", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// re-calc the bind dest path, because we have not done the chroot
|
|
||||||
if err := utils.PrepareTransferPath(state.Root, state.ID, bind, true); err != nil {
|
|
||||||
logrus.Errorf("[device-hook] Prepare tansfer path (%s) failed, prepare tansfer path failed: %v", bindstr, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = driver.AddBind(pid, bind); err != nil {
|
|
||||||
logrus.Errorf("[device-hook] Add bind (%s) failed: %v", bindstr, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SharePath will add the binds to the container
|
|
||||||
func SharePath(state *configs.HookState, hookConfig *hconfig.ContainerHookConfig, spec *specs.Spec) error {
|
|
||||||
pid := strconv.Itoa(state.Pid)
|
|
||||||
driver := nsexec.NewDefaultNsDriver()
|
|
||||||
if err := utils.PrepareHostPath(state.ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
bind := &types.Bind{
|
|
||||||
HostPath: utils.GetContainerSpecDir(state.ID),
|
|
||||||
IsDir: true,
|
|
||||||
ContainerPath: filepath.Join(state.Root, utils.GetSlavePath()),
|
|
||||||
}
|
|
||||||
if err := driver.AddTransferBase(pid, bind); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateQos will update the Qos config for container
|
|
||||||
func UpdateQos(state *configs.HookState, hookConfig *hconfig.ContainerHookConfig, spec *specs.Spec) error {
|
|
||||||
pid := strconv.Itoa(state.Pid)
|
|
||||||
innerPath := spec.Linux.CgroupsPath
|
|
||||||
|
|
||||||
// update device read iops
|
|
||||||
for _, devReadIOPS := range hookConfig.ReadIOPS {
|
|
||||||
if err := updateQosDeviceNum(hookConfig, devReadIOPS); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := libdevice.UpdateCgroupDeviceReadIOPS(pid, innerPath, devReadIOPS.String()); err != nil {
|
|
||||||
logrus.Errorf("[device-hook] Failed to update device read iops (%s) for container %s: %v", devReadIOPS.String(), state.ID, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// update device write iops
|
|
||||||
for _, devWriteIOPS := range hookConfig.WriteIOPS {
|
|
||||||
if err := updateQosDeviceNum(hookConfig, devWriteIOPS); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := libdevice.UpdateCgroupDeviceWriteIOPS(pid, innerPath, devWriteIOPS.String()); err != nil {
|
|
||||||
logrus.Errorf("[device-hook] Failed to update device write iops (%s) for container %s: %v", devWriteIOPS, state.ID, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// update device read bps
|
|
||||||
for _, devReadBps := range hookConfig.ReadBps {
|
|
||||||
if err := updateQosDeviceNum(hookConfig, devReadBps); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := libdevice.UpdateCgroupDeviceReadBps(pid, innerPath, devReadBps.String()); err != nil {
|
|
||||||
logrus.Errorf("[device-hook] Failed to update device read bps (%s) for container %s: %v", devReadBps.String(), state.ID, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// update device write bps
|
|
||||||
for _, devWriteBps := range hookConfig.WriteBps {
|
|
||||||
if err := updateQosDeviceNum(hookConfig, devWriteBps); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := libdevice.UpdateCgroupDeviceWriteBps(pid, innerPath, devWriteBps.String()); err != nil {
|
|
||||||
logrus.Errorf("[device-hook] Failed to update device write bps (%s) for container %s: %v", devWriteBps.String(), state.ID, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// update device blkio weight
|
|
||||||
for _, devBlkioWeight := range hookConfig.BlkioWeight {
|
|
||||||
if err := updateQosDeviceNum(hookConfig, devBlkioWeight); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := libdevice.UpdateCgroupDeviceWeight(pid, innerPath, devBlkioWeight.String()); err != nil {
|
|
||||||
logrus.Errorf("[device-hook] Failed to update device weight %s for container %s : %v", devBlkioWeight.String(), state.ID, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateNetwork will update the network interface for container
|
|
||||||
func UpdateNetwork(state *configs.HookState, hookConfig *hconfig.ContainerHookConfig, spec *specs.Spec) error {
|
|
||||||
nsPath := fmt.Sprintf("/proc/%d/ns/net", state.Pid)
|
|
||||||
if err := os.MkdirAll(hconfig.IsuladToolsDirNetns, 0600); err != nil {
|
|
||||||
logrus.Errorf("[device-hook] Failed to Create netns dir %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
file, err := os.Create(filepath.Join(hconfig.IsuladToolsDirNetns, state.ID))
|
|
||||||
if err != nil {
|
|
||||||
logrus.Errorf("[device-hook] Failed to Create netns file %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
if err := file.Chmod(0600); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := unix.Mount(nsPath, file.Name(), "bind", unix.MS_BIND, ""); err != nil {
|
|
||||||
logrus.Errorf("[device-hook] Failed to Mount netns file %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, nic := range hookConfig.NetworkInterfaces {
|
|
||||||
if err := libnetwork.AddNicToContainer(nsPath, nic); err != nil {
|
|
||||||
logrus.Errorf("[device-hook] Failed to add network interface (%s) to container %s: %v", nic.String(), state.ID, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, route := range hookConfig.NetworkRoutes {
|
|
||||||
if err := libnetwork.AddRouteToContainer(nsPath, route); err != nil {
|
|
||||||
logrus.Errorf("[device-hook] Failed to add route rule (%s) to container %s: %v", route.String(), state.ID, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DynLoadModule dynamic load kernel modules
|
|
||||||
func DynLoadModule(state *configs.HookState, hookConfig *hconfig.ContainerHookConfig, spec *specs.Spec) error {
|
|
||||||
kernelModules := []string{"-a"}
|
|
||||||
|
|
||||||
for _, env := range spec.Process.Env {
|
|
||||||
if strings.Contains(env, "KERNEL_MODULES=") {
|
|
||||||
envValue := strings.Split(env, "=")
|
|
||||||
|
|
||||||
if envValue[0] == "KERNEL_MODULES" {
|
|
||||||
envModules := strings.Split(envValue[1], ",")
|
|
||||||
|
|
||||||
for _, module := range envModules {
|
|
||||||
if module != "" {
|
|
||||||
if isValidModuleName(module) {
|
|
||||||
kernelModules = append(kernelModules, module)
|
|
||||||
} else {
|
|
||||||
return fmt.Errorf("[module-hook] Failed to modprobe modules by module name is incorrect:%s", module)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(kernelModules) == 1 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := exec.Command("modprobe", kernelModules...)
|
|
||||||
|
|
||||||
err := cmd.Run()
|
|
||||||
if err != nil {
|
|
||||||
logrus.Errorf("[module-hook] Failed to modprobe modules (%q) to host : %v", kernelModules, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func isValidModuleName(input string) bool {
|
|
||||||
pattern := `^[A-Za-z0-9\-\_]*$`
|
|
||||||
reg := regexp.MustCompile(pattern)
|
|
||||||
return reg.MatchString(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AdjustUserns ajust user namespace for container hooks
|
|
||||||
func AdjustUserns(state *configs.HookState, hookConfig *hconfig.ContainerHookConfig, spec *specs.Spec) error {
|
|
||||||
driver := nsexec.NewDefaultNsDriver()
|
|
||||||
pid := strconv.Itoa(state.Pid)
|
|
||||||
|
|
||||||
if len(spec.Linux.UIDMappings) == 0 && len(spec.Linux.GIDMappings) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, value := range spec.Linux.Sysctl {
|
|
||||||
if err := driver.UpdateSysctl(pid, &types.Sysctl{key, value}); err != nil {
|
|
||||||
logrus.Errorf("[device-hook] Update sysctl %s:%s failed: %v", key, value, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
containerStoragePath, err := utils.GetContainerStoragePath()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if strings.Contains(containerStoragePath, "isulad") {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, mount := range spec.Mounts {
|
|
||||||
if mount.Destination == "/dev" && mount.Type == "tmpfs" && mount.Source == "tmpfs" {
|
|
||||||
// remount to allow dev
|
|
||||||
rootfsDev := filepath.Join(state.Root, "/dev")
|
|
||||||
rootfsBakDev := filepath.Join(state.Root, "/.dev")
|
|
||||||
// move /dev to /.dev
|
|
||||||
if err := driver.Mount(pid, &types.Mount{Source: rootfsDev, Destination: rootfsBakDev, Type: "move", Options: ""}); err != nil {
|
|
||||||
logrus.Errorf("[device-hook] Move /dev to /.dev failed: %v", err)
|
|
||||||
}
|
|
||||||
// remount /dev whit dev option
|
|
||||||
options := append(mount.Options, "dev")
|
|
||||||
uid, gid := utils.GetUIDGid(spec)
|
|
||||||
if uid != -1 {
|
|
||||||
uidStr := fmt.Sprintf("uid=%d", uid)
|
|
||||||
options = append(options, uidStr)
|
|
||||||
}
|
|
||||||
if gid != -1 {
|
|
||||||
gidStr := fmt.Sprintf("gid=%d", gid)
|
|
||||||
options = append(options, gidStr)
|
|
||||||
}
|
|
||||||
opts := strings.Join(options, ",")
|
|
||||||
if err := driver.Mount(pid, &types.Mount{Source: mount.Type, Destination: rootfsDev, Type: mount.Type, Options: opts}); err != nil {
|
|
||||||
logrus.Errorf("[device-hook] mount /dev failed: %v", err)
|
|
||||||
}
|
|
||||||
options = append(mount.Options, "remount")
|
|
||||||
opts = strings.Join(options, ",")
|
|
||||||
if err := driver.Mount(pid, &types.Mount{Source: rootfsDev, Destination: rootfsDev, Type: mount.Type, Options: opts}); err != nil {
|
|
||||||
logrus.Errorf("[device-hook] remount /dev failed: %v", err)
|
|
||||||
}
|
|
||||||
if uid == -1 {
|
|
||||||
uid = 0
|
|
||||||
}
|
|
||||||
if gid == -1 {
|
|
||||||
gid = 0
|
|
||||||
}
|
|
||||||
// bind mountpoints in /dev
|
|
||||||
for _, mnt := range spec.Mounts {
|
|
||||||
if mnt.Destination != "/dev" && strings.Contains(mnt.Destination, "/dev") {
|
|
||||||
source := filepath.Join(rootfsBakDev, filepath.Base(mnt.Destination))
|
|
||||||
dest := filepath.Join(rootfsDev, filepath.Base(mnt.Destination))
|
|
||||||
if err := driver.Mount(pid, &types.Mount{source, dest, "bind", "bind", uid, gid}); err != nil {
|
|
||||||
logrus.Errorf("[device-hook] bindmount %s failed: %v", mnt.Destination, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// re-add runc's devices
|
|
||||||
runcDevices := []specs.LinuxDevice{
|
|
||||||
{
|
|
||||||
Type: "c",
|
|
||||||
Path: "/dev/full",
|
|
||||||
Major: 1,
|
|
||||||
Minor: minor,
|
|
||||||
FileMode: fmPtr(0666),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Type: "c",
|
|
||||||
Path: "/dev/tty",
|
|
||||||
Major: major,
|
|
||||||
Minor: 0,
|
|
||||||
FileMode: fmPtr(0666),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
spec.Linux.Devices = append(spec.Linux.Devices, runcDevices...)
|
|
||||||
for _, mnt := range spec.Linux.Devices {
|
|
||||||
if mnt.Path != "/dev" && strings.Contains(mnt.Path, "/dev") {
|
|
||||||
source := filepath.Join(rootfsBakDev, filepath.Base(mnt.Path))
|
|
||||||
dest := filepath.Join(rootfsDev, filepath.Base(mnt.Path))
|
|
||||||
if err := driver.Mount(pid, &types.Mount{source, dest, "bind", "bind", uid, gid}); err != nil {
|
|
||||||
logrus.Errorf("[device-hook] bindmount %s failed: %v", mnt.Path, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
links := [][2]string{
|
|
||||||
{"/proc/self/fd", "/dev/fd"},
|
|
||||||
{"/proc/self/fd/0", "/dev/stdin"},
|
|
||||||
{"/proc/self/fd/1", "/dev/stdout"},
|
|
||||||
{"/proc/self/fd/2", "/dev/stderr"},
|
|
||||||
{"pts/ptmx", "/dev/ptmx"},
|
|
||||||
{"/proc/kcore", "/dev/core"},
|
|
||||||
}
|
|
||||||
for _, mnt := range links {
|
|
||||||
source := mnt[0]
|
|
||||||
dest := filepath.Join(state.Root, mnt[1])
|
|
||||||
if err := driver.Mount(pid, &types.Mount{source, dest, "link", "", uid, gid}); err != nil {
|
|
||||||
logrus.Errorf("[device-hook] link %s failed: %v", mnt[0], err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// prestartHook is the main logic of device hook
|
|
||||||
func prestartHook(data *hookData, withRelabel bool) error {
|
|
||||||
var actions []HookAction
|
|
||||||
actions = []HookAction{
|
|
||||||
SharePath,
|
|
||||||
AdjustUserns,
|
|
||||||
AddDevices,
|
|
||||||
AddBinds,
|
|
||||||
UpdateQos,
|
|
||||||
UpdateNetwork,
|
|
||||||
DynLoadModule,
|
|
||||||
}
|
|
||||||
if withRelabel {
|
|
||||||
actions = append(actions, PrestartRelabel)
|
|
||||||
}
|
|
||||||
for _, ac := range actions {
|
|
||||||
if err := ac(data.state, data.hookConfig, data.spec); err != nil {
|
|
||||||
logrus.Errorf("Failed with err: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateQosDeviceNum(hookConfig *hconfig.ContainerHookConfig, qos *types.Qos) error {
|
|
||||||
devMajor, devMinor, err := libdevice.GetDeviceNum(qos.Path)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Errorf("[device-hook] Failed to update device num (%s) for container %v", qos.String(), err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
hookConfig.UpdateQosDevNum(qos, devMajor, devMinor)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@ -1,284 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: selinux relabel operation
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
// go base main package
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/reexec"
|
|
||||||
"github.com/opencontainers/runc/libcontainer"
|
|
||||||
"github.com/opencontainers/runc/libcontainer/configs"
|
|
||||||
_ "github.com/opencontainers/runc/libcontainer/nsenter"
|
|
||||||
"github.com/opencontainers/runc/libcontainer/selinux"
|
|
||||||
"github.com/opencontainers/runtime-spec/specs-go"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/vishvananda/netlink/nl"
|
|
||||||
hconfig "isula.org/syscontainer-tools/config"
|
|
||||||
"isula.org/syscontainer-tools/libdevice/nsexec"
|
|
||||||
"isula.org/syscontainer-tools/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
autoRelabel = "/.autorelabel"
|
|
||||||
autoRelabelInContainer = "/.autorelabel_in_container"
|
|
||||||
containerautoRelabel = "/.container_autorelabel"
|
|
||||||
relabelBin = "/usr/bin/autorelabel_container"
|
|
||||||
systemdServiceFile = "/etc/systemd/system/multi-user.target.wants/autorelabel.service"
|
|
||||||
upstartServiceFile = "/etc/init/autorelabel.conf"
|
|
||||||
systemdInit = "systemd"
|
|
||||||
appName = "oci-relabel-hook"
|
|
||||||
usage = "oci-relabel-hook poststart|poststop"
|
|
||||||
relabelRexec = "reexec-relabel"
|
|
||||||
|
|
||||||
autoRelabelService = `#!/bin/bash
|
|
||||||
. /etc/selinux/config
|
|
||||||
setenforce 0
|
|
||||||
semodule -R
|
|
||||||
if [ -f "%s" ]; then
|
|
||||||
restorecon -R /
|
|
||||||
rm -rf %s
|
|
||||||
reboot -f
|
|
||||||
else
|
|
||||||
if [ "$SELINUX" = "enforcing" ]; then
|
|
||||||
setenforce 1
|
|
||||||
else
|
|
||||||
setenforce 0
|
|
||||||
fi
|
|
||||||
fi`
|
|
||||||
|
|
||||||
upstartService = `start on startup
|
|
||||||
task
|
|
||||||
console output
|
|
||||||
script
|
|
||||||
logger "upstart-autorelabel start"
|
|
||||||
exec %s
|
|
||||||
end script`
|
|
||||||
|
|
||||||
systemdService = `[Unit]
|
|
||||||
Description=Relabel all container's filesystems, if necessary
|
|
||||||
DefaultDependencies=no
|
|
||||||
Requires=local-fs.target
|
|
||||||
Conflicts=shutdown.target
|
|
||||||
After=local-fs.target
|
|
||||||
Before=sysinit.target shutdown.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
ExecStart=%s
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
|
|
||||||
`
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
reexec.Register(relabelRexec, RelabelInMntNs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RelabelInMntNs relabel in container mount namespace
|
|
||||||
func RelabelInMntNs() {
|
|
||||||
var s configs.HookState
|
|
||||||
if err := json.NewDecoder(os.Stdin).Decode(&s); err != nil {
|
|
||||||
logrus.Errorf("[oci relabel] Failed to decode stdin: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := preStartNs(&s); err != nil {
|
|
||||||
logrus.Errorf("[oci relabel] Failed to relabel in mnt ns: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func relabelSystemd(rootfs string) error {
|
|
||||||
logrus.Info("systemd autorelable")
|
|
||||||
autoRel := fmt.Sprintf(autoRelabelService, autoRelabelInContainer, autoRelabelInContainer)
|
|
||||||
if err := ioutil.WriteFile(filepath.Join(rootfs, relabelBin), []byte(autoRel), 0700); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
systemdService := fmt.Sprintf(systemdService, relabelBin)
|
|
||||||
if err := ioutil.WriteFile(filepath.Join(rootfs, systemdServiceFile), []byte(systemdService), 0600); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func relabelUpstart(rootfs string) error {
|
|
||||||
logrus.Info("upstart autorelable")
|
|
||||||
autoRel := fmt.Sprintf(autoRelabelService, autoRelabelInContainer, autoRelabelInContainer)
|
|
||||||
if err := ioutil.WriteFile(filepath.Join(rootfs, relabelBin), []byte(autoRel), 0700); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
upstartService := fmt.Sprintf(upstartService, relabelBin)
|
|
||||||
if err := ioutil.WriteFile(filepath.Join(rootfs, upstartServiceFile), []byte(upstartService), 0600); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func relabel(rootfs string) error {
|
|
||||||
if utils.IsSystemdInit(rootfs) {
|
|
||||||
return relabelSystemd(rootfs)
|
|
||||||
}
|
|
||||||
return relabelUpstart(rootfs)
|
|
||||||
}
|
|
||||||
|
|
||||||
func preStartNs(s *configs.HookState) error {
|
|
||||||
var (
|
|
||||||
se string
|
|
||||||
err error
|
|
||||||
attr string
|
|
||||||
seconfig = "/etc/selinux/config"
|
|
||||||
seconfigContainer = s.Root + "/etc/selinux/config"
|
|
||||||
)
|
|
||||||
|
|
||||||
if se, err = utils.SeconfigGet(seconfig, "SELINUX"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// don't exec hook's function if SELinux is disabled in host
|
|
||||||
if se == "disabled" {
|
|
||||||
logrus.Infof("Host SELinux disabled")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// set permissive to host /etc/selinux/config
|
|
||||||
if err = utils.SeconfigSet(seconfig, "SELINUX", "permissive"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if se, err = utils.SeconfigGet(seconfigContainer, "SELINUX"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// proposal from it
|
|
||||||
// don't exec hook's function if SELinux is disabled in container
|
|
||||||
if se == "disabled" {
|
|
||||||
logrus.Infof("Container SELinux disabled")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// mount selinuxfs
|
|
||||||
if err = syscall.Mount("none", s.Root+utils.GetSelinuxMountPount(s.Root), "selinuxfs", 0, ""); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// start a container in the first time, it need relabel, so create a /.autorelabel file
|
|
||||||
if !utils.IsExist(s.Root + containerautoRelabel) {
|
|
||||||
if err := ioutil.WriteFile(filepath.Join(s.Root, autoRelabel), []byte(""), 0600); err != nil {
|
|
||||||
logrus.Errorf("WriteFile err: %v", err)
|
|
||||||
}
|
|
||||||
if err := ioutil.WriteFile(filepath.Join(s.Root, containerautoRelabel), []byte(""), 0600); err != nil {
|
|
||||||
logrus.Errorf("WriteFile err: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// relabel container' rootfs, just create a systemd service file, and the relabel process is executed in container.
|
|
||||||
if err = relabel(s.Root); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure relabelBin can execute exactly
|
|
||||||
hostRelabelBin := filepath.Join(s.Root, relabelBin)
|
|
||||||
if attr, err = selinux.Getfilecon(hostRelabelBin); err != nil {
|
|
||||||
logrus.Errorf("Getfilecon %s err", hostRelabelBin)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
con := utils.NewContext(attr)
|
|
||||||
con.SetType("init_exec_t")
|
|
||||||
selinux.Setfilecon(hostRelabelBin, con.Get())
|
|
||||||
logrus.Infof("%s [%s]", hostRelabelBin, con.Get())
|
|
||||||
|
|
||||||
if utils.IsExist(s.Root + autoRelabel) {
|
|
||||||
if err := ioutil.WriteFile(filepath.Join(s.Root, autoRelabelInContainer), []byte(""), 0600); err != nil {
|
|
||||||
logrus.Errorf("WriteFile err: %v", err)
|
|
||||||
}
|
|
||||||
if err := syscall.Unlink(filepath.Join(s.Root, autoRelabel)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func preStartClone(s *configs.HookState) error {
|
|
||||||
parent, child, err := utils.NewPipe()
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
b, err := json.Marshal(s)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
env := os.Environ()
|
|
||||||
env = append(env, fmt.Sprintf("%s=3", nsexec.InitPipe))
|
|
||||||
cmd := &exec.Cmd{
|
|
||||||
Path: "/proc/self/exe",
|
|
||||||
Args: []string{relabelRexec},
|
|
||||||
ExtraFiles: []*os.File{child},
|
|
||||||
Env: env,
|
|
||||||
Stdin: bytes.NewReader(b),
|
|
||||||
Stdout: os.Stdout,
|
|
||||||
Stderr: os.Stderr,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := cmd.Start(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
namespaces := []string{
|
|
||||||
fmt.Sprintf("mnt:/proc/%d/ns/mnt", s.Pid),
|
|
||||||
}
|
|
||||||
r := nl.NewNetlinkRequest(int(libcontainer.InitMsg), 0)
|
|
||||||
r.AddData(&libcontainer.Bytemsg{
|
|
||||||
Type: libcontainer.NsPathsAttr,
|
|
||||||
Value: []byte(strings.Join(namespaces, ",")),
|
|
||||||
})
|
|
||||||
if _, err := io.Copy(parent, bytes.NewReader(r.Serialize())); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := cmd.Wait(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrestartRelabel handles oci relabel for prestart state
|
|
||||||
func PrestartRelabel(state *configs.HookState, hookConfig *hconfig.ContainerHookConfig, spec *specs.Spec) error {
|
|
||||||
if err := preStartClone(state); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// PostStopRelabel handles oci relabel for post-stop state
|
|
||||||
func PostStopRelabel(state *configs.HookState, hookConfig *hconfig.ContainerHookConfig, spec *specs.Spec) error {
|
|
||||||
if utils.IsSystemdInit(state.Root) {
|
|
||||||
if err := syscall.Unlink(filepath.Join(state.Root + systemdServiceFile)); err != nil {
|
|
||||||
logrus.Errorf("syscall.Unlink state.Root err: %v", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err := syscall.Unlink(filepath.Join(state.Root + upstartServiceFile)); err != nil {
|
|
||||||
logrus.Errorf("syscall.Unlink not state.Root err: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@ -1,196 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: bind operation for device
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package libdevice
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
"github.com/opencontainers/runtime-spec/specs-go"
|
|
||||||
"isula.org/syscontainer-tools/types"
|
|
||||||
"isula.org/syscontainer-tools/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ParseBind will parse host path to Bind structure
|
|
||||||
func ParseBind(bindstr string, spec *specs.Spec, create bool) (*types.Bind, error) {
|
|
||||||
var src, dst string
|
|
||||||
var isDir bool
|
|
||||||
|
|
||||||
permissions := "rw,rslave"
|
|
||||||
arr := strings.Split(bindstr, ":")
|
|
||||||
switch len(arr) {
|
|
||||||
case 3:
|
|
||||||
if validMountOption(arr[2]) == false {
|
|
||||||
return nil, fmt.Errorf("invalid permissions: %s", arr[2])
|
|
||||||
}
|
|
||||||
permissions = arr[2]
|
|
||||||
fallthrough
|
|
||||||
case 2:
|
|
||||||
src = path.Clean(arr[0])
|
|
||||||
dst = path.Clean(arr[1])
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("invalid path specification: %s", bindstr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if path.IsAbs(src) == false || path.IsAbs(dst) == false {
|
|
||||||
return nil, fmt.Errorf("invalid path specification:%s, only absolute path is allowed", bindstr)
|
|
||||||
}
|
|
||||||
|
|
||||||
bind := &types.Bind{
|
|
||||||
HostPath: src,
|
|
||||||
ContainerPath: dst,
|
|
||||||
MountOption: permissions,
|
|
||||||
}
|
|
||||||
|
|
||||||
symlinkinfo, err := os.Lstat(src)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Errorf("Lstat returns a FileInfo describing the named file error: %v", err)
|
|
||||||
}
|
|
||||||
info, err := os.Stat(src)
|
|
||||||
if symlinkinfo != nil {
|
|
||||||
if ((symlinkinfo.Mode() & os.ModeSymlink) == os.ModeSymlink) && (err != nil) {
|
|
||||||
return nil, fmt.Errorf("Parsebind get symlibk source file error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if create {
|
|
||||||
if spec != nil {
|
|
||||||
uid, gid := utils.GetUIDGid(spec)
|
|
||||||
if uid == -1 {
|
|
||||||
uid = 0
|
|
||||||
}
|
|
||||||
if gid == -1 {
|
|
||||||
gid = 0
|
|
||||||
}
|
|
||||||
bind.UID = uid
|
|
||||||
bind.GID = gid
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
isDir = info.IsDir()
|
|
||||||
} else if os.IsNotExist(err) {
|
|
||||||
isDir = true
|
|
||||||
if spec != nil {
|
|
||||||
if err := os.MkdirAll(src, os.FileMode(0755)); err != nil {
|
|
||||||
return nil, fmt.Errorf("ParseBind mkdir error: %v", err)
|
|
||||||
}
|
|
||||||
if err := os.Chown(src, bind.UID, bind.GID); err != nil {
|
|
||||||
return nil, fmt.Errorf("ParseBind chown error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("invalid path specification: %s", bindstr)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err == nil {
|
|
||||||
isDir = info.IsDir()
|
|
||||||
} else if os.IsNotExist(err) {
|
|
||||||
return bind, nil
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("invalid path specification: %s", bindstr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bind.IsDir = isDir
|
|
||||||
|
|
||||||
return bind, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func findPathDevice(path string) (*types.Device, string, error) {
|
|
||||||
|
|
||||||
// find path mount entry point.
|
|
||||||
cmd := exec.Command("df", "-P", path)
|
|
||||||
stdout, err := cmd.StdoutPipe()
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
if err := cmd.Start(); err != nil {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer cmd.Wait()
|
|
||||||
|
|
||||||
reader := bufio.NewReader(stdout)
|
|
||||||
|
|
||||||
// ignore first line.
|
|
||||||
reader.ReadString('\n')
|
|
||||||
line, err := reader.ReadString('\n')
|
|
||||||
if err != nil {
|
|
||||||
logrus.Errorf("reader.ReadString error: %v", err)
|
|
||||||
}
|
|
||||||
line = strings.Trim(line, "\n")
|
|
||||||
|
|
||||||
devs := strings.Split(line, " ")
|
|
||||||
device, err := DeviceFromPath(devs[0], "rwm")
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
return device, devs[len(devs)-1], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func findDeviceMountEntryPoint(device, mp string) (string, string, string, error) {
|
|
||||||
f, err := os.Open("/proc/mounts")
|
|
||||||
if err != nil {
|
|
||||||
return "", "", "", err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(f)
|
|
||||||
procMntCols := 6
|
|
||||||
for scanner.Scan() {
|
|
||||||
line := scanner.Text()
|
|
||||||
array := strings.Split(line, " ")
|
|
||||||
if len(array) < procMntCols {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// the one we wanted.
|
|
||||||
if array[0] == device && array[1] == mp {
|
|
||||||
entry := array[1]
|
|
||||||
fstype := array[2]
|
|
||||||
mountOption := array[3]
|
|
||||||
return entry, fstype, mountOption, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := scanner.Err(); err != nil {
|
|
||||||
return "", "", "", err
|
|
||||||
}
|
|
||||||
return "", "", "", fmt.Errorf("Device Not Found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// validMountOption will validate the mount option for user input
|
|
||||||
func validMountOption(option string) bool {
|
|
||||||
validOp := map[string]bool{
|
|
||||||
"ro": true,
|
|
||||||
"rw": true,
|
|
||||||
// "shared": true,
|
|
||||||
"private": true,
|
|
||||||
// "slave": true,
|
|
||||||
// "rshared": true,
|
|
||||||
"rprivate": true,
|
|
||||||
"rslave": true,
|
|
||||||
}
|
|
||||||
|
|
||||||
arr := strings.Split(option, ",")
|
|
||||||
for _, op := range arr {
|
|
||||||
if !validOp[op] {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
validOp[op] = false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
@ -1,176 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: cgroup operation for container
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package libdevice
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
|
||||||
"isula.org/syscontainer-tools/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
cgroupNamePrefix = "name="
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetCgroupDir returns the cgorup mount directory from pid
|
|
||||||
func GetCgroupDir(pid, subsystem string) (string, error) {
|
|
||||||
path := filepath.Join("/proc", pid, "cgroup")
|
|
||||||
cgroupmap, err := cgroups.ParseCgroupFile(path)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if path, ok := cgroupmap[subsystem]; ok {
|
|
||||||
return path, nil
|
|
||||||
}
|
|
||||||
if path, ok := cgroupmap[cgroupNamePrefix+subsystem]; ok {
|
|
||||||
return path, nil
|
|
||||||
}
|
|
||||||
return "", fmt.Errorf("Error: ControllerPath of %s is not found", subsystem)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindCgroupPath will search the subsystem cgroup path for target process
|
|
||||||
func FindCgroupPath(pid, subsystem, innerPath string) (string, error) {
|
|
||||||
cgroupRoot, err := cgroups.FindCgroupMountpointDir()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
mnt, root, err := cgroups.FindCgroupMountpointAndRoot(subsystem)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if filepath.IsAbs(innerPath) {
|
|
||||||
return filepath.Join(cgroupRoot, filepath.Base(mnt), innerPath), nil
|
|
||||||
}
|
|
||||||
initPath, err := GetCgroupDir(pid, subsystem)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is needed for nested containers, because in /proc/pid/cgroup we
|
|
||||||
// see pathes from host, which don't exist in container.
|
|
||||||
relDir, err := filepath.Rel(root, initPath)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return filepath.Join(mnt, relDir), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateCgroupPermission will update the cgroup permissions for specified device
|
|
||||||
func UpdateCgroupPermission(CgroupBase string, device *types.Device, isAddDevice bool) error {
|
|
||||||
var path string
|
|
||||||
|
|
||||||
if isAddDevice {
|
|
||||||
path = filepath.Join(CgroupBase, "devices.allow")
|
|
||||||
} else {
|
|
||||||
path = filepath.Join(CgroupBase, "devices.deny")
|
|
||||||
}
|
|
||||||
value := device.CgroupString()
|
|
||||||
if err := ioutil.WriteFile(path, []byte(value), 0600); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateCgroupDeviceReadIOPS updates the read device iops for container/pid
|
|
||||||
func UpdateCgroupDeviceReadIOPS(pid, innerPath, value string) error {
|
|
||||||
if pid == "0" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
cgroupPath, err := FindCgroupPath(pid, "blkio", innerPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
path := filepath.Join(cgroupPath, "blkio.throttle.read_iops_device")
|
|
||||||
if err := ioutil.WriteFile(path, []byte(value), 0600); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateCgroupDeviceWriteIOPS updates the write device iops for container/pid
|
|
||||||
func UpdateCgroupDeviceWriteIOPS(pid, innerPath, value string) error {
|
|
||||||
if pid == "0" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
cgroupPath, err := FindCgroupPath(pid, "blkio", innerPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
path := filepath.Join(cgroupPath, "blkio.throttle.write_iops_device")
|
|
||||||
if err := ioutil.WriteFile(path, []byte(value), 0600); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateCgroupDeviceReadBps updates the read device bps for container/pid
|
|
||||||
func UpdateCgroupDeviceReadBps(pid, innerPath, value string) error {
|
|
||||||
if pid == "0" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
cgroupPath, err := FindCgroupPath(pid, "blkio", innerPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
path := filepath.Join(cgroupPath, "blkio.throttle.read_bps_device")
|
|
||||||
if err := ioutil.WriteFile(path, []byte(value), 0600); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateCgroupDeviceWriteBps updates the write device bps for container/pid
|
|
||||||
func UpdateCgroupDeviceWriteBps(pid, innerPath, value string) error {
|
|
||||||
if pid == "0" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
cgroupPath, err := FindCgroupPath(pid, "blkio", innerPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
path := filepath.Join(cgroupPath, "blkio.throttle.write_bps_device")
|
|
||||||
if err := ioutil.WriteFile(path, []byte(value), 0600); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateCgroupDeviceWeight updates the write device weight for container/pid
|
|
||||||
func UpdateCgroupDeviceWeight(pid, innerPath, value string) error {
|
|
||||||
if pid == "0" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
cgroupPath, err := FindCgroupPath(pid, "blkio", innerPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
path := filepath.Join(cgroupPath, "blkio.weight_device")
|
|
||||||
if err := ioutil.WriteFile(path, []byte(value), 0600); err != nil {
|
|
||||||
return fmt.Errorf("%s, please check whether current OS support blkio weight device configuration for bfq scheduler", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@ -1,332 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: bind and device operation in container namespace
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package libdevice
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/reexec"
|
|
||||||
"isula.org/syscontainer-tools/libdevice/nsexec"
|
|
||||||
"isula.org/syscontainer-tools/pkg/mount"
|
|
||||||
"isula.org/syscontainer-tools/types"
|
|
||||||
"isula.org/syscontainer-tools/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
reexec.Register(nsexec.NsEnterReexecName, WorkInContainer)
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupPipe(name string) (*os.File, error) {
|
|
||||||
v := os.Getenv(name)
|
|
||||||
|
|
||||||
fd, err := strconv.Atoi(v)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to convert %s=%s to int", name, v)
|
|
||||||
}
|
|
||||||
return os.NewFile(uintptr(fd), "pipe"), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupWorkType(name string) (int, error) {
|
|
||||||
v := os.Getenv(name)
|
|
||||||
|
|
||||||
worktype, err := strconv.Atoi(v)
|
|
||||||
if err != nil {
|
|
||||||
return -1, fmt.Errorf("unable to convert %s=%s to int", name, v)
|
|
||||||
}
|
|
||||||
return worktype, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WorkInContainer will handle command in new namespace(container).
|
|
||||||
func WorkInContainer() {
|
|
||||||
var err error
|
|
||||||
var worktype int
|
|
||||||
var pipe *os.File
|
|
||||||
pipe, err = setupPipe(nsexec.InitPipe)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "%v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// when pipe setup, should always send back the errors.
|
|
||||||
defer func() {
|
|
||||||
var msg types.ErrMsg
|
|
||||||
if err != nil {
|
|
||||||
msg.Error = fmt.Sprintf("%s", err.Error())
|
|
||||||
}
|
|
||||||
if err := utils.WriteJSON(pipe, msg); err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
worktype, err = setupWorkType(nsexec.WorkType)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle work here:
|
|
||||||
switch worktype {
|
|
||||||
case nsexec.AddDeviceMsg:
|
|
||||||
err = doAddDevice(pipe)
|
|
||||||
case nsexec.RemoveDeviceMsg:
|
|
||||||
err = doRemoveDevice(pipe)
|
|
||||||
case nsexec.AddBindMsg:
|
|
||||||
err = doAddBind(pipe)
|
|
||||||
case nsexec.RemoveBindMsg:
|
|
||||||
err = doRemoveBind(pipe)
|
|
||||||
case nsexec.AddTransferBaseMsg:
|
|
||||||
err = doAddTransferBase(pipe)
|
|
||||||
case nsexec.UpdateSysctlMsg:
|
|
||||||
err = doUpdateSysctl(pipe)
|
|
||||||
case nsexec.MountMsg:
|
|
||||||
err = doMount(pipe)
|
|
||||||
default:
|
|
||||||
err = fmt.Errorf("unkown worktype=(%d)", worktype)
|
|
||||||
}
|
|
||||||
// do not need to check err here because we have check in defer
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeSystemProperty writes the value to a path under /proc/sys as determined from the key.
|
|
||||||
// For e.g. net.ipv4.ip_forward translated to /proc/sys/net/ipv4/ip_forward.
|
|
||||||
func writeSystemProperty(key, value string) error {
|
|
||||||
keyPath := strings.Replace(key, ".", "/", -1)
|
|
||||||
return ioutil.WriteFile(path.Join("/proc/sys", keyPath), []byte(value), 0644)
|
|
||||||
}
|
|
||||||
|
|
||||||
func doUpdateSysctl(pipe *os.File) error {
|
|
||||||
var sysctl types.Sysctl
|
|
||||||
if err := json.NewDecoder(pipe).Decode(&sysctl); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return writeSystemProperty(sysctl.Key, sysctl.Value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func doMount(pipe *os.File) error {
|
|
||||||
var mnt types.Mount
|
|
||||||
if err := json.NewDecoder(pipe).Decode(&mnt); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if mnt.Type == "move" {
|
|
||||||
_, err := os.Stat(mnt.Destination)
|
|
||||||
if err != nil && !os.IsNotExist(err) {
|
|
||||||
return fmt.Errorf("doMount: stat %s in container failed, err: %s", mnt.Destination, err)
|
|
||||||
}
|
|
||||||
if err := os.MkdirAll(mnt.Destination, 0600); err != nil {
|
|
||||||
return fmt.Errorf("doMount: create mount destination in container failed, err: %s", err)
|
|
||||||
}
|
|
||||||
return syscall.Mount(mnt.Source, mnt.Destination, "", syscall.MS_MOVE, "")
|
|
||||||
}
|
|
||||||
if mnt.Type == "bind" {
|
|
||||||
_, err := os.Stat(mnt.Destination)
|
|
||||||
if err != nil && !os.IsNotExist(err) {
|
|
||||||
return fmt.Errorf("doMount: stat %s in container failed, err: %s", mnt.Destination, err)
|
|
||||||
}
|
|
||||||
fi, err := os.Stat(mnt.Source)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("doMount: stat %s in container failed, err: %s", mnt.Source, err)
|
|
||||||
}
|
|
||||||
if err := os.Chown(mnt.Source, mnt.UID, mnt.GID); err != nil {
|
|
||||||
return fmt.Errorf("chown changes the numeric uid and gid of the name file, err: %s", err)
|
|
||||||
}
|
|
||||||
if fi.Mode().IsDir() {
|
|
||||||
if err := os.MkdirAll(mnt.Destination, 0600); err != nil {
|
|
||||||
return fmt.Errorf("doMount: create mount destination in container failed, err: %s", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
f, err := os.OpenFile(mnt.Destination, os.O_RDWR|os.O_CREATE, 0600)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("fail to create %s, err: %s", mnt.Destination, err)
|
|
||||||
}
|
|
||||||
f.Close()
|
|
||||||
}
|
|
||||||
return mount.Mount(mnt.Source, mnt.Destination, "none", mnt.Options)
|
|
||||||
}
|
|
||||||
if mnt.Type == "link" {
|
|
||||||
if err := os.Symlink(mnt.Source, mnt.Destination); err != nil && !os.IsNotExist(err) {
|
|
||||||
return fmt.Errorf("doMount: symlink %s %s %s", mnt.Source, mnt.Destination, err)
|
|
||||||
}
|
|
||||||
if err := os.Lchown(mnt.Destination, mnt.UID, mnt.GID); err != nil {
|
|
||||||
logrus.Errorf("os.Lchown error: %s", err)
|
|
||||||
}
|
|
||||||
if err := os.Remove(strings.Replace(mnt.Destination, "/dev", "/.dev", -1)); err != nil {
|
|
||||||
logrus.Errorf("os.Remove error: %s", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return mount.Mount(mnt.Source, mnt.Destination, mnt.Type, mnt.Options)
|
|
||||||
}
|
|
||||||
|
|
||||||
func doAddDevice(pipe *os.File) error {
|
|
||||||
msg := types.AddDeviceMsg{}
|
|
||||||
if err := json.NewDecoder(pipe).Decode(&msg); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
force := msg.Force
|
|
||||||
device := msg.Device
|
|
||||||
|
|
||||||
existDev, err := DeviceFromPath(device.Path, "")
|
|
||||||
// if device exists and device is the one we wantted, just return.
|
|
||||||
if err == nil && existDev.Major == device.Major && existDev.Minor == device.Minor && existDev.Type == device.Type {
|
|
||||||
// change filemode, uid and gid
|
|
||||||
if err := os.Chmod(device.Path, device.FileMode); err != nil {
|
|
||||||
logrus.Errorf("os.Chmod error: %v", err)
|
|
||||||
}
|
|
||||||
if err := os.Chown(device.Path, int(device.UID), int(device.GID)); err != nil {
|
|
||||||
logrus.Errorf("os.Chown error: %v", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if force {
|
|
||||||
// if force, the target file is not the one we wantted, just remove it.
|
|
||||||
fmt.Printf("path %s in container already exists. removing it.\n", device.Path)
|
|
||||||
if err := os.Remove(device.Path); err != nil {
|
|
||||||
logrus.Errorf("os.Remove error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// change umask
|
|
||||||
oldMask := syscall.Umask(0000)
|
|
||||||
defer syscall.Umask(oldMask)
|
|
||||||
|
|
||||||
var needPermission []string
|
|
||||||
dir := filepath.Dir(device.Path)
|
|
||||||
for {
|
|
||||||
_, err := os.Stat(dir)
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
needPermission = append(needPermission, dir)
|
|
||||||
dir = filepath.Dir(dir)
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dir = filepath.Dir(device.Path)
|
|
||||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, dirname := range needPermission {
|
|
||||||
if err := os.Chown(dirname, int(device.UID), int(device.GID)); err != nil {
|
|
||||||
logrus.Errorf("os.Chown error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := MknodDevice(device.Path, device); err != nil {
|
|
||||||
return fmt.Errorf("Current OS kernel do not support mknod in container user namespace for root, err: %s", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func doRemoveDevice(pipe *os.File) error {
|
|
||||||
var device types.Device
|
|
||||||
if err := json.NewDecoder(pipe).Decode(&device); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// As add-device supports `update-config-only` flag, it will update the config only.
|
|
||||||
// So the device we wantted to remove maybe not exist in container at all, that's fine, just return OK.
|
|
||||||
if _, err := os.Stat(device.Path); os.IsNotExist(err) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// if not a device.
|
|
||||||
if _, err := DeviceFromPath(device.Path, ""); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// need strict check here?
|
|
||||||
return os.Remove(device.Path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func doAddBind(pipe *os.File) error {
|
|
||||||
var bind types.Bind
|
|
||||||
if err := json.NewDecoder(pipe).Decode(&bind); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var needPermission []string
|
|
||||||
dir := filepath.Dir(bind.ContainerPath)
|
|
||||||
for {
|
|
||||||
_, err := os.Stat(dir)
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
needPermission = append(needPermission, dir)
|
|
||||||
dir = filepath.Dir(dir)
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if bind.IsDir {
|
|
||||||
if err := os.MkdirAll(bind.ContainerPath, 0600); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err := os.MkdirAll(filepath.Dir(bind.ContainerPath), 0600); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
f, err := os.OpenFile(bind.ContainerPath, os.O_RDWR|os.O_CREATE, 0600)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("fail to create transfer path,err: %s", err)
|
|
||||||
}
|
|
||||||
f.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := os.Chown(bind.ContainerPath, bind.UID, bind.GID); err != nil {
|
|
||||||
logrus.Errorf("os.Chown error: %s", err)
|
|
||||||
}
|
|
||||||
for _, dirname := range needPermission {
|
|
||||||
if err := os.Chown(dirname, bind.UID, bind.GID); err != nil {
|
|
||||||
logrus.Errorf("os.Chown error: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := mount.Mount(bind.ResolvPath, bind.ContainerPath, "none", bind.MountOption); err != nil {
|
|
||||||
return fmt.Errorf("fail to mount via transfer path: %+v, err: %s", bind, err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func doRemoveBind(pipe *os.File) error {
|
|
||||||
var bind types.Bind
|
|
||||||
if err := json.NewDecoder(pipe).Decode(&bind); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return mount.Unmount(bind.ContainerPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
func doAddTransferBase(pipe *os.File) error {
|
|
||||||
var bind types.Bind
|
|
||||||
if err := json.NewDecoder(pipe).Decode(&bind); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := os.MkdirAll(bind.ContainerPath, 0600); err != nil {
|
|
||||||
return fmt.Errorf("doAddTransferBase: create transfer dir in container failed, err: %s", err)
|
|
||||||
}
|
|
||||||
if err := mount.Mount(bind.HostPath, bind.ContainerPath, "none", "ro,bind,rslave"); err != nil {
|
|
||||||
return fmt.Errorf("doAddTransferBase: mount transfer dir in container failed, err:%s, %+v", err, bind)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@ -1,254 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: device operation
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
// +build linux freebsd
|
|
||||||
|
|
||||||
package libdevice
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"isula.org/syscontainer-tools/types"
|
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// ErrNotADevice not a device error
|
|
||||||
ErrNotADevice = errors.New("not a device")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Testing dependencies
|
|
||||||
var (
|
|
||||||
osLstat = os.Lstat
|
|
||||||
)
|
|
||||||
|
|
||||||
// ParseMapping will return a device with mapping segment only.
|
|
||||||
func ParseMapping(device string) (*types.Device, error) {
|
|
||||||
var src, dst, permissions string
|
|
||||||
arr := strings.Split(device, ":")
|
|
||||||
permissions = "rwm"
|
|
||||||
|
|
||||||
// According to the length of device specifications
|
|
||||||
if len(arr) < 1 || len(arr) > 3 {
|
|
||||||
return nil, fmt.Errorf("invalid device specification: %s", device)
|
|
||||||
}
|
|
||||||
src = arr[0]
|
|
||||||
if len(arr) == 3 {
|
|
||||||
if arr[2] != "" {
|
|
||||||
permissions = arr[2]
|
|
||||||
}
|
|
||||||
dst = arr[1]
|
|
||||||
}
|
|
||||||
if len(arr) == 2 {
|
|
||||||
if CheckDeviceMode(arr[1]) {
|
|
||||||
permissions = arr[1]
|
|
||||||
} else {
|
|
||||||
dst = arr[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !CheckDeviceMode(permissions) {
|
|
||||||
return nil, fmt.Errorf("invalid permission: %s", permissions)
|
|
||||||
}
|
|
||||||
if src != "" {
|
|
||||||
if !filepath.IsAbs(src) {
|
|
||||||
return nil, fmt.Errorf("hostpath should be an absolute path: %s", src)
|
|
||||||
}
|
|
||||||
src = filepath.Clean(src)
|
|
||||||
}
|
|
||||||
if dst != "" {
|
|
||||||
if !filepath.IsAbs(dst) {
|
|
||||||
return nil, fmt.Errorf("containerpath should be an absolute path: %s", dst)
|
|
||||||
}
|
|
||||||
dst = filepath.Clean(dst)
|
|
||||||
}
|
|
||||||
if src == "" && dst == "" {
|
|
||||||
return nil, fmt.Errorf("either of host path and container path should be assigned")
|
|
||||||
}
|
|
||||||
|
|
||||||
ret := &types.Device{
|
|
||||||
Path: dst,
|
|
||||||
PathOnHost: src,
|
|
||||||
Permissions: permissions,
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CheckDeviceMode checks if the mode is ilegal.
|
|
||||||
func CheckDeviceMode(mode string) bool {
|
|
||||||
var DeviceMode = map[rune]bool{
|
|
||||||
'r': true,
|
|
||||||
'w': true,
|
|
||||||
'm': true,
|
|
||||||
}
|
|
||||||
if mode == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, md := range mode {
|
|
||||||
if !DeviceMode[md] {
|
|
||||||
// Device Mode is ilegal
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
DeviceMode[md] = false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseDevice parses the device from file path and returns the device structure
|
|
||||||
func ParseDevice(device string) (*types.Device, error) {
|
|
||||||
mapDevice, err := ParseMapping(device)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
dev, err := DeviceFromPath(mapDevice.PathOnHost, mapDevice.Permissions)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
dev.Path = mapDevice.Path
|
|
||||||
return dev, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDeviceRealPath get real path of device
|
|
||||||
func GetDeviceRealPath(path string) string {
|
|
||||||
resolvedPathOnHost := path
|
|
||||||
|
|
||||||
for {
|
|
||||||
linkedPathOnHost, err := os.Readlink(resolvedPathOnHost)
|
|
||||||
// regular file will return error
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
base := filepath.Dir(resolvedPathOnHost)
|
|
||||||
resolvedPathOnHost = linkedPathOnHost
|
|
||||||
if !filepath.IsAbs(resolvedPathOnHost) {
|
|
||||||
resolvedPathOnHost = filepath.Join(base, linkedPathOnHost)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return resolvedPathOnHost
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDeviceNum get device major and minor number
|
|
||||||
func GetDeviceNum(path string) (int64, int64, error) {
|
|
||||||
dev, err := DeviceFromPath(path, "")
|
|
||||||
if err != nil {
|
|
||||||
return 0, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return dev.Major, dev.Minor, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeviceFromPath parses the given device path to a device structure
|
|
||||||
func DeviceFromPath(path, permissions string) (*types.Device, error) {
|
|
||||||
resolvedPathOnHost := GetDeviceRealPath(path)
|
|
||||||
|
|
||||||
fileInfo, err := osLstat(resolvedPathOnHost)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var (
|
|
||||||
devType string
|
|
||||||
mode = fileInfo.Mode()
|
|
||||||
fileModePermissionBits = os.FileMode.Perm(mode)
|
|
||||||
)
|
|
||||||
switch {
|
|
||||||
case mode&os.ModeDevice == 0:
|
|
||||||
return nil, ErrNotADevice
|
|
||||||
case mode&os.ModeCharDevice != 0:
|
|
||||||
fileModePermissionBits |= syscall.S_IFCHR
|
|
||||||
devType = "c"
|
|
||||||
default:
|
|
||||||
fileModePermissionBits |= syscall.S_IFBLK
|
|
||||||
devType = "b"
|
|
||||||
}
|
|
||||||
stat, ok := fileInfo.Sys().(*syscall.Stat_t)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("cannot determine the device number for device %s", path)
|
|
||||||
}
|
|
||||||
devNumber := int(stat.Rdev)
|
|
||||||
return &types.Device{
|
|
||||||
Type: devType,
|
|
||||||
PathOnHost: path,
|
|
||||||
Major: Major(devNumber),
|
|
||||||
Minor: Minor(devNumber),
|
|
||||||
Permissions: permissions,
|
|
||||||
FileMode: fileModePermissionBits,
|
|
||||||
UID: stat.Uid,
|
|
||||||
GID: stat.Gid,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindSubPartition will find all the sub-partitions for a base device.
|
|
||||||
func FindSubPartition(device *types.Device) []*types.Device {
|
|
||||||
var subDevices []*types.Device
|
|
||||||
|
|
||||||
cmd := exec.Command("lsblk", "-n", "-p", "-r", "-o", "NAME", device.PathOnHost)
|
|
||||||
out, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
logrus.Errorf("Failed to lsblk %s : %v", string(out), err)
|
|
||||||
return subDevices
|
|
||||||
}
|
|
||||||
rawString := strings.Split(string(out), "\n")
|
|
||||||
subDevNames := rawString[1 : len(rawString)-1]
|
|
||||||
for _, devName := range subDevNames {
|
|
||||||
tryDevice := devName
|
|
||||||
if device.Path != "" {
|
|
||||||
tryDevice = tryDevice + ":" + device.Path + string(devName[len(devName)-1])
|
|
||||||
}
|
|
||||||
|
|
||||||
tryDevice = tryDevice + ":" + device.Permissions
|
|
||||||
|
|
||||||
dev, err := ParseDevice(tryDevice)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
dev.Parent = device.PathOnHost
|
|
||||||
subDevices = append(subDevices, dev)
|
|
||||||
}
|
|
||||||
return subDevices
|
|
||||||
}
|
|
||||||
|
|
||||||
// MknodDevice will create device in container by calling mknod system call
|
|
||||||
func MknodDevice(dest string, node *types.Device) error {
|
|
||||||
fileMode := node.FileMode
|
|
||||||
switch node.Type {
|
|
||||||
case "c":
|
|
||||||
fileMode |= syscall.S_IFCHR
|
|
||||||
case "b":
|
|
||||||
fileMode |= syscall.S_IFBLK
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("%s is not a valid device type for device %s", node.Type, node.Path)
|
|
||||||
}
|
|
||||||
if err := syscall.Mknod(dest, uint32(fileMode), node.Mkdev()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return syscall.Chown(dest, int(node.UID), int(node.GID))
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetDefaultPath set default path for device
|
|
||||||
func SetDefaultPath(dev *types.Device) {
|
|
||||||
if dev.Path == "" {
|
|
||||||
dev.Path = dev.PathOnHost
|
|
||||||
}
|
|
||||||
if dev.PathOnHost == "" {
|
|
||||||
dev.PathOnHost = dev.Path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,651 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: device operation lib
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package libdevice
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/opencontainers/runtime-spec/specs-go"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
hconfig "isula.org/syscontainer-tools/config"
|
|
||||||
"isula.org/syscontainer-tools/container"
|
|
||||||
"isula.org/syscontainer-tools/libdevice/nsexec"
|
|
||||||
"isula.org/syscontainer-tools/pkg/udevd"
|
|
||||||
"isula.org/syscontainer-tools/types"
|
|
||||||
"isula.org/syscontainer-tools/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
func checkDevice(config hconfig.ContainerConfig, devs []*types.Device, opts *types.AddDeviceOptions) error {
|
|
||||||
devices := devs
|
|
||||||
// check all devices, config updated
|
|
||||||
if len(devs) == 0 {
|
|
||||||
for _, dm := range config.GetAllDevices() {
|
|
||||||
devices = append(devices, &types.Device{
|
|
||||||
Type: dm.Type,
|
|
||||||
Major: dm.Major,
|
|
||||||
Minor: dm.Minor,
|
|
||||||
PathOnHost: dm.PathOnHost,
|
|
||||||
Path: dm.PathInContainer,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
checkDeviceQos := func(qos *types.Qos) bool {
|
|
||||||
for _, device := range devices {
|
|
||||||
if device.Major == qos.Major && device.Minor == qos.Minor {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
qosOpts := append(opts.ReadBps, opts.WriteBps...)
|
|
||||||
qosOpts = append(qosOpts, opts.ReadIOPS...)
|
|
||||||
qosOpts = append(qosOpts, opts.WriteIOPS...)
|
|
||||||
for _, opt := range qosOpts {
|
|
||||||
if !checkDeviceQos(opt) {
|
|
||||||
return fmt.Errorf("device %v was not added to container or not in add-device args", opt.Path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateDeviceOwner update device owner
|
|
||||||
func UpdateDeviceOwner(spec *specs.Spec, device *types.Device) {
|
|
||||||
if spec == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
uid, gid := utils.GetUIDGid(spec)
|
|
||||||
|
|
||||||
if uid != -1 {
|
|
||||||
device.UID = uint32(uid)
|
|
||||||
}
|
|
||||||
if gid != -1 {
|
|
||||||
device.GID = uint32(gid)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddDevice will add devices to a container.
|
|
||||||
func AddDevice(c *container.Container, devices []*types.Device, opts *types.AddDeviceOptions) error {
|
|
||||||
driver := nsexec.NewDefaultNsDriver()
|
|
||||||
pid := strconv.Itoa(c.Pid())
|
|
||||||
|
|
||||||
innerPath, err := c.GetCgroupPath()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
cgroupPath, err := FindCgroupPath(pid, "devices", innerPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, device := range devices {
|
|
||||||
UpdateDeviceOwner(c.GetSpec(), device)
|
|
||||||
}
|
|
||||||
|
|
||||||
udevdCtrl := udevd.NewUdevdController()
|
|
||||||
|
|
||||||
// lockFile := <container config path>/lock
|
|
||||||
// 1. use file lock, to make sure only one process to access this config file.
|
|
||||||
// 2. different container has different lock, will not block other container.
|
|
||||||
if err := c.Lock(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer c.Unlock()
|
|
||||||
|
|
||||||
// create config file handler.
|
|
||||||
config, err := hconfig.NewContainerConfig(c)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := checkDevice(config, devices, opts); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if err := config.Flush(); err != nil {
|
|
||||||
logrus.Infof("config Flush error:%v", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err := udevdCtrl.Lock(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer udevdCtrl.Unlock()
|
|
||||||
|
|
||||||
if err := udevdCtrl.LoadRules(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer udevdCtrl.ToDisk()
|
|
||||||
|
|
||||||
var retErr []error
|
|
||||||
// add device and udpate cgroup here
|
|
||||||
for _, device := range devices {
|
|
||||||
// update config here
|
|
||||||
if err = config.UpdateDevice(device, true); err != nil {
|
|
||||||
retErr = append(retErr, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
r := &udevd.Rule{
|
|
||||||
Name: device.PathOnHost,
|
|
||||||
Container: c.ContainerID(),
|
|
||||||
CtrDevName: device.Path,
|
|
||||||
}
|
|
||||||
if device.Type != "c" {
|
|
||||||
devType, err := types.GetDeviceType(device.PathOnHost)
|
|
||||||
if err != nil {
|
|
||||||
retErr = append(retErr, err)
|
|
||||||
config.UpdateDevice(device, false)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if devType == "disk" {
|
|
||||||
udevdCtrl.AddRule(r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Do not insert device and update cgroup when:
|
|
||||||
// 1. update-config-only flag is set
|
|
||||||
// 2. container isn't running (pid==0)
|
|
||||||
if !opts.UpdateConfigOnly && c.Pid() > 0 && c.CheckPidExist() {
|
|
||||||
// add device to container.
|
|
||||||
if err = driver.AddDevice(pid, device, opts.Force); err != nil {
|
|
||||||
retErr = append(retErr, err)
|
|
||||||
// roll back config and udev rules
|
|
||||||
config.UpdateDevice(device, false)
|
|
||||||
udevdCtrl.RemoveRule(r)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// update cgroup access permission.
|
|
||||||
if err = UpdateCgroupPermission(cgroupPath, device, true); err != nil {
|
|
||||||
retErr = append(retErr, err)
|
|
||||||
// roll back config and udev rules and remove device
|
|
||||||
driver.RemoveDevice(pid, device)
|
|
||||||
config.UpdateDevice(device, false)
|
|
||||||
udevdCtrl.RemoveRule(r)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(os.Stdout, "Add device (%s) to container(%s,%s) done.\n", device.PathOnHost, c.Name(), device.Path)
|
|
||||||
logrus.Infof("Add device (%s) to container(%s,%s) done", device.PathOnHost, c.Name(), device.Path)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := updateQos(config, pid, innerPath, opts); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(retErr) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
for i := 0; i < len(retErr); i++ {
|
|
||||||
retErr[i] = fmt.Errorf("%s", retErr[i].Error())
|
|
||||||
}
|
|
||||||
return errors.New(strings.Trim(fmt.Sprint(retErr), "[]"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateDevice will update device for container.
|
|
||||||
func UpdateDevice(c *container.Container, opts *types.AddDeviceOptions) error {
|
|
||||||
pid := strconv.Itoa(c.Pid())
|
|
||||||
|
|
||||||
innerPath, err := c.GetCgroupPath()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.Lock(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer c.Unlock()
|
|
||||||
|
|
||||||
// create config file handler.
|
|
||||||
config, err := hconfig.NewContainerConfig(c)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := checkDevice(config, []*types.Device{}, opts); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if err := config.Flush(); err != nil {
|
|
||||||
logrus.Infof("config Flush error:%v", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err := updateQos(config, pid, innerPath, opts); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveDevice will remove devices from container
|
|
||||||
func RemoveDevice(c *container.Container, devices []*types.Device, followPartition bool) error {
|
|
||||||
driver := nsexec.NewDefaultNsDriver()
|
|
||||||
pid := strconv.Itoa(c.Pid())
|
|
||||||
|
|
||||||
innerPath, err := c.GetCgroupPath()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
cgroupPath, err := FindCgroupPath(pid, "devices", innerPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.Lock(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer c.Unlock()
|
|
||||||
|
|
||||||
config, err := hconfig.NewContainerConfig(c)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer config.Flush()
|
|
||||||
|
|
||||||
udevdCtrl := udevd.NewUdevdController()
|
|
||||||
if err := udevdCtrl.Lock(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer udevdCtrl.Unlock()
|
|
||||||
|
|
||||||
if err := udevdCtrl.LoadRules(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer udevdCtrl.ToDisk()
|
|
||||||
|
|
||||||
var retErr []error
|
|
||||||
var newDevices []*types.Device
|
|
||||||
for _, device := range devices {
|
|
||||||
newDevice := config.FindDeviceByMapping(device)
|
|
||||||
if newDevice == nil {
|
|
||||||
errinfo := fmt.Sprint("Device pair(", device.PathOnHost, ":", device.Path, ") is not added by syscontainer-tools, can not remove it, please check input parameter.")
|
|
||||||
retErr = append(retErr, errors.New(errinfo))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
newDevices = append(newDevices, newDevice)
|
|
||||||
|
|
||||||
if followPartition {
|
|
||||||
subDevices := config.FindSubPartition(newDevice)
|
|
||||||
for _, subDev := range subDevices {
|
|
||||||
// check the sub partition is added by syscontainer-tools
|
|
||||||
if found := config.FindDeviceByMapping(subDev); found == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
found := false
|
|
||||||
for _, eDev := range newDevices {
|
|
||||||
if subDev.Path == eDev.Path && subDev.PathOnHost == eDev.PathOnHost {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
newDevices = append(newDevices, subDev)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, device := range newDevices {
|
|
||||||
// update config.
|
|
||||||
if err = config.UpdateDevice(device, false); err != nil {
|
|
||||||
retErr = append(retErr, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
r := &udevd.Rule{
|
|
||||||
Name: device.PathOnHost,
|
|
||||||
CtrDevName: device.Path,
|
|
||||||
Container: c.ContainerID(),
|
|
||||||
}
|
|
||||||
udevdCtrl.RemoveRule(r)
|
|
||||||
|
|
||||||
// only update for running container
|
|
||||||
if c.Pid() > 0 && c.CheckPidExist() {
|
|
||||||
if err = driver.RemoveDevice(pid, device); err != nil {
|
|
||||||
config.UpdateDevice(device, true)
|
|
||||||
if device.Type != "c" {
|
|
||||||
devType, err := types.GetDeviceType(device.PathOnHost)
|
|
||||||
if err != nil {
|
|
||||||
retErr = append(retErr, err)
|
|
||||||
config.UpdateDevice(device, true)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if devType == "disk" {
|
|
||||||
udevdCtrl.AddRule(r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
retErr = append(retErr, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// update cgroup access permission.
|
|
||||||
if err = UpdateCgroupPermission(cgroupPath, device, false); err != nil {
|
|
||||||
// TODO: also need a roll back?
|
|
||||||
retErr = append(retErr, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(os.Stdout, "Remove device (%s) from container(%s,%s) done.\n", device.PathOnHost, c.Name(), device.Path)
|
|
||||||
logrus.Infof("Remove device (%s) from container(%s,%s) done.\n", device.PathOnHost, c.Name(), device.Path)
|
|
||||||
|
|
||||||
if err := removeQos(config, pid, innerPath, device); err != nil {
|
|
||||||
retErr = append(retErr, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(retErr) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
for i := 0; i < len(retErr); i++ {
|
|
||||||
retErr[i] = fmt.Errorf("%s", retErr[i].Error())
|
|
||||||
}
|
|
||||||
return errors.New(strings.Trim(fmt.Sprint(retErr), "[]"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListDevice list container devices
|
|
||||||
func ListDevice(c *container.Container) ([]*hconfig.DeviceMapping, []*hconfig.DeviceMapping, error) {
|
|
||||||
if err := c.Lock(); err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
defer c.Unlock()
|
|
||||||
hConfig, err := hconfig.NewContainerConfig(c)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
allDevice := hConfig.GetAllDevices()
|
|
||||||
var majorDevices []*hconfig.DeviceMapping
|
|
||||||
|
|
||||||
for _, sDevice := range allDevice {
|
|
||||||
if sDevice.Parent == "" {
|
|
||||||
majorDevices = append(majorDevices, sDevice)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return allDevice, majorDevices, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddPath will add paths from host to container
|
|
||||||
func AddPath(c *container.Container, binds []*types.Bind) error {
|
|
||||||
driver := nsexec.NewDefaultNsDriver()
|
|
||||||
pid := strconv.Itoa(c.Pid())
|
|
||||||
|
|
||||||
if err := c.Lock(); err != nil {
|
|
||||||
return fmt.Errorf("AddPath: failed to get lock, err: %s", err)
|
|
||||||
}
|
|
||||||
defer c.Unlock()
|
|
||||||
|
|
||||||
config, err := hconfig.NewContainerConfig(c)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("AddPath: failed to create config, err: %s", err)
|
|
||||||
}
|
|
||||||
defer config.Flush()
|
|
||||||
|
|
||||||
if err := config.CheckPathNum(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var retErr []error
|
|
||||||
for _, bind := range binds {
|
|
||||||
logrus.Debugf("Adding path: %+v", bind)
|
|
||||||
// 1. update config.
|
|
||||||
hostPathExist, err := config.UpdateBind(bind, true)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("AddPath: failed to UpdateBind, err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Pid() > 0 && c.CheckPidExist() {
|
|
||||||
// 2. prepare transferpath if needed
|
|
||||||
if err := utils.PrepareTransferPath("/", c.ContainerID(), bind, !hostPathExist); err != nil {
|
|
||||||
config.UpdateBind(bind, false)
|
|
||||||
|
|
||||||
// if no existed, do unmount
|
|
||||||
if !hostPathExist {
|
|
||||||
utils.RemoveTransferPath(c.ContainerID(), bind)
|
|
||||||
}
|
|
||||||
return fmt.Errorf("AddPath: failed to prepare transfer base, err: %s", err)
|
|
||||||
}
|
|
||||||
if err = driver.AddBind(pid, bind); err != nil {
|
|
||||||
retErr = append(retErr, err)
|
|
||||||
config.UpdateBind(bind, false)
|
|
||||||
if !hostPathExist {
|
|
||||||
utils.RemoveTransferPath(c.ContainerID(), bind)
|
|
||||||
}
|
|
||||||
return fmt.Errorf("AddPath: failed to add bind, err: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
msg := fmt.Sprintf("Add path (%s) to container(%s,%s) done.", bind.HostPath, c.Name(), bind.ContainerPath)
|
|
||||||
fmt.Fprintln(os.Stdout, msg)
|
|
||||||
logrus.Info(msg)
|
|
||||||
|
|
||||||
}
|
|
||||||
if len(retErr) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
for i := 0; i < len(retErr); i++ {
|
|
||||||
retErr[i] = fmt.Errorf("%s", retErr[i].Error())
|
|
||||||
}
|
|
||||||
return errors.New(strings.Trim(fmt.Sprint(retErr), "[]"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemovePath will remove paths from container
|
|
||||||
func RemovePath(c *container.Container, binds []*types.Bind) error {
|
|
||||||
|
|
||||||
driver := nsexec.NewDefaultNsDriver()
|
|
||||||
pid := strconv.Itoa(c.Pid())
|
|
||||||
if err := c.Lock(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer c.Unlock()
|
|
||||||
|
|
||||||
config, err := hconfig.NewContainerConfig(c)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer config.Flush()
|
|
||||||
|
|
||||||
var retErr []error
|
|
||||||
for _, bind := range binds {
|
|
||||||
mp, err := config.GetBindInConfig(bind)
|
|
||||||
if err != nil {
|
|
||||||
retErr = append(retErr, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if mp == nil {
|
|
||||||
errinfo := fmt.Sprint("Path pair(", bind.HostPath, ":", bind.ContainerPath, ") is not added by syscontainer-tools, can not remove it, please check input parameter")
|
|
||||||
retErr = append(retErr, errors.New(errinfo))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
bind.MountOption = mp.Permission
|
|
||||||
|
|
||||||
// update config.
|
|
||||||
removeHostPath, err := config.UpdateBind(bind, false)
|
|
||||||
if err != nil {
|
|
||||||
retErr = append(retErr, fmt.Errorf("Failed to update bind(%v), error: %s, still try to remove it", bind, err))
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove from container.
|
|
||||||
if c.Pid() > 0 && c.CheckPidExist() {
|
|
||||||
if err := driver.RemoveBind(pid, bind); err != nil {
|
|
||||||
retErr = append(retErr, fmt.Errorf("Failed to remove bind(%v),error: %s", bind, err))
|
|
||||||
config.UpdateBind(bind, true)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if removeHostPath == true {
|
|
||||||
if err := utils.RemoveTransferPath(c.ContainerID(), bind); err != nil {
|
|
||||||
retErr = append(retErr, fmt.Errorf("Remove path (%s) from %s failed, err: %s", bind.HostPath, c.Name(), err))
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
msg := fmt.Sprintf("Remove path (%s) from container(%s,%s) done", bind.HostPath, c.Name(), bind.ContainerPath)
|
|
||||||
fmt.Fprintln(os.Stdout, msg)
|
|
||||||
logrus.Info(msg)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(retErr) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
for i := 0; i < len(retErr); i++ {
|
|
||||||
retErr[i] = fmt.Errorf("%s", retErr[i].Error())
|
|
||||||
}
|
|
||||||
return errors.New(strings.Trim(fmt.Sprint(retErr), "[]"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListPath list container paths
|
|
||||||
func ListPath(ctr *container.Container) ([]string, error) {
|
|
||||||
if err := ctr.Lock(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer ctr.Unlock()
|
|
||||||
hConfig, err := hconfig.NewContainerConfig(ctr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return hConfig.GetBinds(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateQos(config hconfig.ContainerConfig, pid, innerPath string, opts *types.AddDeviceOptions) error {
|
|
||||||
// update device read iops
|
|
||||||
for _, devReadIOPS := range opts.ReadIOPS {
|
|
||||||
if err := UpdateCgroupDeviceReadIOPS(pid, innerPath, devReadIOPS.String()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := config.UpdateDeviceQos(devReadIOPS, hconfig.QosReadIOPS); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
msg := fmt.Sprintf("Update read iops for device (%s,%s) done.", devReadIOPS.Path, devReadIOPS.Value)
|
|
||||||
fmt.Fprintln(os.Stdout, msg)
|
|
||||||
logrus.Info(msg)
|
|
||||||
|
|
||||||
}
|
|
||||||
// update device write iops
|
|
||||||
for _, devWriteIOPS := range opts.WriteIOPS {
|
|
||||||
if err := UpdateCgroupDeviceWriteIOPS(pid, innerPath, devWriteIOPS.String()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := config.UpdateDeviceQos(devWriteIOPS, hconfig.QosWriteIOPS); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
msg := fmt.Sprintf("Update write iops for device (%s,%s) done.", devWriteIOPS.Path, devWriteIOPS.Value)
|
|
||||||
fmt.Fprintln(os.Stdout, msg)
|
|
||||||
logrus.Info(msg)
|
|
||||||
}
|
|
||||||
// update device read bps
|
|
||||||
for _, devReadBps := range opts.ReadBps {
|
|
||||||
if err := UpdateCgroupDeviceReadBps(pid, innerPath, devReadBps.String()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := config.UpdateDeviceQos(devReadBps, hconfig.QosReadBps); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
msg := fmt.Sprintf("Update read bps for device (%s,%s) done.", devReadBps.Path, devReadBps.Value)
|
|
||||||
fmt.Fprintln(os.Stdout, msg)
|
|
||||||
logrus.Info(msg)
|
|
||||||
|
|
||||||
}
|
|
||||||
// update device write bps
|
|
||||||
for _, devWriteBps := range opts.WriteBps {
|
|
||||||
if err := UpdateCgroupDeviceWriteBps(pid, innerPath, devWriteBps.String()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := config.UpdateDeviceQos(devWriteBps, hconfig.QosWriteBps); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
msg := fmt.Sprintf("Update write bps for device (%s,%s) done.", devWriteBps.Path, devWriteBps.Value)
|
|
||||||
fmt.Fprintln(os.Stdout, msg)
|
|
||||||
logrus.Info(msg)
|
|
||||||
|
|
||||||
}
|
|
||||||
// update device blkio weight
|
|
||||||
for _, devBlkioWeight := range opts.BlkioWeight {
|
|
||||||
cfqEnable, err := devBlkioWeight.GetCfqAbility()
|
|
||||||
if err == nil && cfqEnable {
|
|
||||||
if err := UpdateCgroupDeviceWeight(pid, innerPath, devBlkioWeight.String()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := config.UpdateDeviceQos(devBlkioWeight, hconfig.QosBlkioWeight); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
msg := fmt.Sprintf("Update blkio weight for device (%s,%s) done.", devBlkioWeight.Path, devBlkioWeight.Value)
|
|
||||||
fmt.Fprintln(os.Stdout, msg)
|
|
||||||
logrus.Info(msg)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
msg := fmt.Sprintf("device not support cfq:%s", devBlkioWeight.Path)
|
|
||||||
fmt.Fprintln(os.Stdout, msg)
|
|
||||||
logrus.Info(msg)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeQos(config hconfig.ContainerConfig, pid, innerPath string, device *types.Device) error {
|
|
||||||
cleanString := fmt.Sprintf("%d:%d 0", device.Major, device.Minor)
|
|
||||||
if exist, err := config.RemoveDeviceQos(device, hconfig.QosReadIOPS); err != nil {
|
|
||||||
return err
|
|
||||||
} else if exist {
|
|
||||||
if err := UpdateCgroupDeviceReadIOPS(pid, innerPath, cleanString); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Fprintf(os.Stdout, "Remove read iops for device (%s) done.\n", device.PathOnHost)
|
|
||||||
}
|
|
||||||
if exist, err := config.RemoveDeviceQos(device, hconfig.QosWriteIOPS); err != nil {
|
|
||||||
return err
|
|
||||||
} else if exist {
|
|
||||||
if err := UpdateCgroupDeviceWriteIOPS(pid, innerPath, cleanString); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Fprintf(os.Stdout, "Remove write iops for device (%s) done.\n", device.PathOnHost)
|
|
||||||
}
|
|
||||||
if exist, err := config.RemoveDeviceQos(device, hconfig.QosReadBps); err != nil {
|
|
||||||
return err
|
|
||||||
} else if exist {
|
|
||||||
if err := UpdateCgroupDeviceReadBps(pid, innerPath, cleanString); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Fprintf(os.Stdout, "Remove read bps for device (%s) done.\n", device.PathOnHost)
|
|
||||||
}
|
|
||||||
if exist, err := config.RemoveDeviceQos(device, hconfig.QosWriteBps); err != nil {
|
|
||||||
return err
|
|
||||||
} else if exist {
|
|
||||||
if err := UpdateCgroupDeviceWriteBps(pid, innerPath, cleanString); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Fprintf(os.Stdout, "Remove write bps for device (%s) done.\n", device.PathOnHost)
|
|
||||||
}
|
|
||||||
if exist, err := config.RemoveDeviceQos(device, hconfig.QosBlkioWeight); err != nil {
|
|
||||||
return err
|
|
||||||
} else if exist {
|
|
||||||
if err := UpdateCgroupDeviceWeight(pid, innerPath, cleanString); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Fprintf(os.Stdout, "Remove blkio weight for device (%s) done.\n", device.PathOnHost)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@ -1,64 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: ns exec driver
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package nsexec
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"isula.org/syscontainer-tools/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// NSExecDriver is the nsexec driver which will use "c+go" to enter namespace
|
|
||||||
NSExecDriver = "nsexec"
|
|
||||||
// DefaultNSDriver is the default namespace driver name.
|
|
||||||
DefaultNSDriver = NSExecDriver
|
|
||||||
)
|
|
||||||
|
|
||||||
// NsDriver is the namespace driver interface
|
|
||||||
type NsDriver interface {
|
|
||||||
// Add device to container.
|
|
||||||
AddDevice(pid string, device *types.Device, force bool) error
|
|
||||||
// Remove device from container.
|
|
||||||
RemoveDevice(pid string, device *types.Device) error
|
|
||||||
// Add a bind to container.
|
|
||||||
AddBind(pid string, bind *types.Bind) error
|
|
||||||
// Remove a bind from container.
|
|
||||||
RemoveBind(pid string, bind *types.Bind) error
|
|
||||||
// Add a transfer base for sharing
|
|
||||||
AddTransferBase(pid string, bind *types.Bind) error
|
|
||||||
// Update sysctl for userns enabled container
|
|
||||||
UpdateSysctl(pid string, sysctl *types.Sysctl) error
|
|
||||||
// Mount remount /dev to remove nodev option for userns enabled container
|
|
||||||
Mount(pid string, mount *types.Mount) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewNsDriver creates the namespace driver by name
|
|
||||||
func NewNsDriver(name string) (NsDriver, error) {
|
|
||||||
switch name {
|
|
||||||
case NSExecDriver:
|
|
||||||
return NewNSExecDriver(), nil
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("Ns device driver (%s) not supported", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDefaultNsDriver creates the default namespace driver
|
|
||||||
func NewDefaultNsDriver() NsDriver {
|
|
||||||
drv, err := NewNsDriver(DefaultNSDriver)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, err)
|
|
||||||
}
|
|
||||||
return drv
|
|
||||||
}
|
|
||||||
@ -1,192 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: ns exec in container namespace
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package nsexec
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
|
|
||||||
"isula.org/syscontainer-tools/types"
|
|
||||||
"isula.org/syscontainer-tools/utils"
|
|
||||||
|
|
||||||
"github.com/opencontainers/runc/libcontainer"
|
|
||||||
"github.com/vishvananda/netlink/nl"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// AddDeviceMsg is a parent and child process message type, for adding device operation
|
|
||||||
AddDeviceMsg = 1
|
|
||||||
// RemoveDeviceMsg is a parent and child process message type, for removing device operation
|
|
||||||
RemoveDeviceMsg = 2
|
|
||||||
// AddBindMsg is a parent and child process message type, for adding bind operation
|
|
||||||
AddBindMsg = 3
|
|
||||||
// RemoveBindMsg is a parent and child process message type, for removing bind operation
|
|
||||||
RemoveBindMsg = 4
|
|
||||||
// AddTransferBaseMsg is a parent and child process message type, for adding sharing
|
|
||||||
AddTransferBaseMsg = 5
|
|
||||||
// UpdateSysctlMsg is a parent and child process message type, for updateing sysctl
|
|
||||||
UpdateSysctlMsg = 6
|
|
||||||
// MountMsg is a parent and child process message type, for remount /dev/ to remove nodev
|
|
||||||
MountMsg = 7
|
|
||||||
// InitPipe is a parent and child process env name, used to pass the init pipe number to child process
|
|
||||||
InitPipe = "_LIBCONTAINER_INITPIPE"
|
|
||||||
// WorkType is a parent and child process env name, used to pass the work type to child process
|
|
||||||
WorkType = "_ISULAD_TOOLS_WORKTYPE"
|
|
||||||
// NsEnterReexecName is the reexec name, see reexec package
|
|
||||||
NsEnterReexecName = "nsenter-init"
|
|
||||||
)
|
|
||||||
|
|
||||||
type nsexecDriver struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
type pid struct {
|
|
||||||
Pid int `json:"Pid"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewNSExecDriver creates the nsexecDriver
|
|
||||||
func NewNSExecDriver() NsDriver {
|
|
||||||
return &nsexecDriver{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *nsexecDriver) exec(nsPaths string, worktype int, data interface{}) error {
|
|
||||||
parent, child, err := utils.NewPipe()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
cmd := &exec.Cmd{
|
|
||||||
Path: "/proc/self/exe",
|
|
||||||
Args: []string{NsEnterReexecName},
|
|
||||||
ExtraFiles: []*os.File{child},
|
|
||||||
Env: []string{fmt.Sprintf("%s=3", InitPipe),
|
|
||||||
fmt.Sprintf("%s=%d", WorkType, worktype)},
|
|
||||||
Stdout: os.Stdout,
|
|
||||||
Stderr: os.Stderr,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := cmd.Start(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
r := nl.NewNetlinkRequest(int(libcontainer.InitMsg), 0)
|
|
||||||
|
|
||||||
r.AddData(&libcontainer.Bytemsg{
|
|
||||||
Type: libcontainer.NsPathsAttr,
|
|
||||||
Value: []byte(nsPaths),
|
|
||||||
})
|
|
||||||
|
|
||||||
// send nspath to child process through _ISULAD_TOOLS_INITPIPE, to join container ns.
|
|
||||||
if _, err := io.Copy(parent, bytes.NewReader(r.Serialize())); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// send the config to child
|
|
||||||
if err := utils.WriteJSON(parent, data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for command
|
|
||||||
if err := cmd.Wait(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
decoder := json.NewDecoder(parent)
|
|
||||||
var pid *pid
|
|
||||||
if err := decoder.Decode(&pid); err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "fail to decode pid:%v, but it may not affect later process", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// read error message
|
|
||||||
var msg types.ErrMsg
|
|
||||||
if err := decoder.Decode(&msg); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if msg.Error != "" {
|
|
||||||
return fmt.Errorf("%s", msg.Error)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddDevice is a low level function which implements how to add devices to a container.
|
|
||||||
func (ns *nsexecDriver) AddDevice(pid string, device *types.Device, force bool) error {
|
|
||||||
namespaces := []string{"mnt"}
|
|
||||||
nsPaths := buildNSString(pid, namespaces)
|
|
||||||
|
|
||||||
msg := &types.AddDeviceMsg{
|
|
||||||
Force: force,
|
|
||||||
Device: device,
|
|
||||||
}
|
|
||||||
|
|
||||||
return ns.exec(nsPaths, AddDeviceMsg, msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveDevice is a low level function which implements how to remove devices from a container.
|
|
||||||
func (ns *nsexecDriver) RemoveDevice(pid string, device *types.Device) error {
|
|
||||||
namespaces := []string{"mnt"}
|
|
||||||
nsPaths := buildNSString(pid, namespaces)
|
|
||||||
|
|
||||||
return ns.exec(nsPaths, RemoveDeviceMsg, device)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddTransferBase adds transfer path between container and host for sharing files
|
|
||||||
func (ns *nsexecDriver) AddTransferBase(pid string, bind *types.Bind) error {
|
|
||||||
namespaces := []string{"mnt"}
|
|
||||||
nsPaths := buildNSString(pid, namespaces)
|
|
||||||
|
|
||||||
return ns.exec(nsPaths, AddTransferBaseMsg, bind)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddBind is a low level function which implements how to add binds to a container.
|
|
||||||
func (ns *nsexecDriver) AddBind(pid string, bind *types.Bind) error {
|
|
||||||
namespaces := []string{"mnt"}
|
|
||||||
nsPaths := buildNSString(pid, namespaces)
|
|
||||||
|
|
||||||
return ns.exec(nsPaths, AddBindMsg, bind)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveBind is a low level function which implements how to remove binds from a container.
|
|
||||||
func (ns *nsexecDriver) RemoveBind(pid string, bind *types.Bind) error {
|
|
||||||
namespaces := []string{"mnt"}
|
|
||||||
nsPaths := buildNSString(pid, namespaces)
|
|
||||||
|
|
||||||
return ns.exec(nsPaths, RemoveBindMsg, bind)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateSysctl is a low level function which implements how to update sysctl for a userns enabled contianer
|
|
||||||
func (ns *nsexecDriver) UpdateSysctl(pid string, sysctl *types.Sysctl) error {
|
|
||||||
namespaces := []string{"ipc", "net", "mnt"}
|
|
||||||
nsPaths := buildNSString(pid, namespaces)
|
|
||||||
|
|
||||||
return ns.exec(nsPaths, UpdateSysctlMsg, sysctl)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *nsexecDriver) Mount(pid string, mount *types.Mount) error {
|
|
||||||
namespaces := []string{"mnt"}
|
|
||||||
nsPaths := buildNSString(pid, namespaces)
|
|
||||||
|
|
||||||
return ns.exec(nsPaths, MountMsg, mount)
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildNSString(pid string, namespaces []string) string {
|
|
||||||
var nsPaths string
|
|
||||||
for _, ns := range namespaces {
|
|
||||||
if nsPaths != "" {
|
|
||||||
nsPaths += ","
|
|
||||||
}
|
|
||||||
nsPaths += fmt.Sprintf("%s:/proc/%s/ns/%s", ns, pid, ns)
|
|
||||||
}
|
|
||||||
return nsPaths
|
|
||||||
}
|
|
||||||
@ -1,37 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: parse device major and minor number
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package libdevice
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
This code provides support for manipulating linux device numbers. It should be replaced by normal syscall functions once http://code.google.com/p/go/issues/detail?id=8106 is solved.
|
|
||||||
|
|
||||||
You can read what they are here:
|
|
||||||
|
|
||||||
- http://www.makelinux.net/ldd3/chp-3-sect-2
|
|
||||||
- http://www.linux-tutorial.info/modules.php?name=MContent&pageid=94
|
|
||||||
|
|
||||||
Note! These are NOT the same as the MAJOR(dev_t device);, MINOR(dev_t device); and MKDEV(int major, int minor); functions as defined in <linux/kdev_t.h> as the representation of device numbers used by go is different than the one used internally to the kernel! - https://github.com/torvalds/linux/blob/master/include/linux/kdev_t.h#L9
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Major returns the major number of a device
|
|
||||||
func Major(devNumber int) int64 {
|
|
||||||
return int64((devNumber >> 8) & 0xfff)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Minor returns the minor number of a device
|
|
||||||
func Minor(devNumber int) int64 {
|
|
||||||
return int64((devNumber & 0xff) | ((devNumber >> 12) & 0xfff00))
|
|
||||||
}
|
|
||||||
@ -1,137 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: parse device option
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package libdevice
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"isula.org/syscontainer-tools/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// KB equals 1024Byte
|
|
||||||
KB float64 = 1024
|
|
||||||
// MB equals 1024KB
|
|
||||||
MB float64 = 1024 * KB
|
|
||||||
// GB equals 1024MB
|
|
||||||
GB float64 = 1024 * MB
|
|
||||||
// TB equals 1024GB
|
|
||||||
TB float64 = 1024 * GB
|
|
||||||
// PB equals 1024TB
|
|
||||||
PB float64 = 1024 * TB
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
unitMap = map[string]float64{"k": KB, "m": MB, "g": GB, "t": TB, "p": PB}
|
|
||||||
reg = regexp.MustCompile(`^(\d+(?:\.\d+)*)?([kKmMgGtTpP])?[bB]?$`)
|
|
||||||
)
|
|
||||||
|
|
||||||
func parseSize(sizeString string) (int64, error) {
|
|
||||||
matches := reg.FindStringSubmatch(sizeString)
|
|
||||||
if len(matches) != 3 { // valid values for parse string len
|
|
||||||
return -1, fmt.Errorf("Invalid size: '%s'", sizeString)
|
|
||||||
}
|
|
||||||
size, err := strconv.ParseFloat(matches[1], 64)
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
if unit, ok := unitMap[strings.ToLower(matches[2])]; ok {
|
|
||||||
size *= unit
|
|
||||||
}
|
|
||||||
return int64(size), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseAddDeviceQosOption parse the inpt blkio options into []types.Qos for add device command.
|
|
||||||
func ParseAddDeviceQosOption(vals []string) ([]*types.Qos, error) {
|
|
||||||
var devQos []*types.Qos
|
|
||||||
|
|
||||||
for _, val := range vals {
|
|
||||||
split := strings.SplitN(val, ":", 2)
|
|
||||||
if len(split) < 2 { // the number of substrings to return
|
|
||||||
return nil, fmt.Errorf("Bad format: %s", val)
|
|
||||||
}
|
|
||||||
dev, err := ParseDevice(split[0])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if dev.Type == "c" {
|
|
||||||
return nil, fmt.Errorf("cannot set Qos of a char device")
|
|
||||||
}
|
|
||||||
devType, err := types.GetDeviceType(dev.PathOnHost)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if devType == "part" {
|
|
||||||
return nil, fmt.Errorf("cannot set Qos of a child device")
|
|
||||||
}
|
|
||||||
rate, err := parseSize(split[1])
|
|
||||||
if err != nil || rate < 0 {
|
|
||||||
return nil, fmt.Errorf("Invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val)
|
|
||||||
}
|
|
||||||
devQos = append(devQos, &types.Qos{
|
|
||||||
Path: split[0],
|
|
||||||
Major: dev.Major,
|
|
||||||
Minor: dev.Minor,
|
|
||||||
Value: fmt.Sprintf("%d", rate),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return devQos, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseAddDeviceBlkioWeight parse the inpt blkio weight into []types.Qos for add device command.
|
|
||||||
func ParseAddDeviceBlkioWeight(vals []string) ([]*types.Qos, error) {
|
|
||||||
var devQos []*types.Qos
|
|
||||||
|
|
||||||
for _, val := range vals {
|
|
||||||
split := strings.SplitN(val, ":", 2)
|
|
||||||
if len(split) < 2 { // the number of substrings to return
|
|
||||||
return nil, fmt.Errorf("Bad format: %s", val)
|
|
||||||
}
|
|
||||||
dev, err := ParseDevice(split[0])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if dev.Type == "c" {
|
|
||||||
return nil, fmt.Errorf("cannot set Qos of a char device")
|
|
||||||
}
|
|
||||||
devType, err := types.GetDeviceType(dev.PathOnHost)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if devType == "part" {
|
|
||||||
return nil, fmt.Errorf("cannot set Qos of a child device")
|
|
||||||
}
|
|
||||||
|
|
||||||
weight, err := strconv.ParseUint(split[1], 10, 0)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Invalid weight for device: %s", val)
|
|
||||||
}
|
|
||||||
if weight > 0 && (weight < 10 || weight > 1000) { // Invalid weight for device interval value
|
|
||||||
return nil, fmt.Errorf("Invalid weight for device: %s", val)
|
|
||||||
}
|
|
||||||
devQos = append(devQos, &types.Qos{
|
|
||||||
Path: split[0],
|
|
||||||
Major: dev.Major,
|
|
||||||
Minor: dev.Minor,
|
|
||||||
Value: fmt.Sprintf("%d", weight),
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
return devQos, nil
|
|
||||||
}
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: bridge driver
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package api
|
|
||||||
|
|
||||||
// BridgeDriver defines the bridge driver interface
|
|
||||||
type BridgeDriver interface {
|
|
||||||
Name() string
|
|
||||||
AddToBridge(netif, bridge string) error
|
|
||||||
}
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: init bridge driver
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package bridge
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os/exec"
|
|
||||||
|
|
||||||
"isula.org/syscontainer-tools/libnetwork/bridge/api"
|
|
||||||
"isula.org/syscontainer-tools/libnetwork/bridge/linux"
|
|
||||||
"isula.org/syscontainer-tools/libnetwork/bridge/ovs"
|
|
||||||
)
|
|
||||||
|
|
||||||
var supportedDrivers map[string]api.BridgeDriver = make(map[string]api.BridgeDriver)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
type initFunction func() api.BridgeDriver
|
|
||||||
for name, initFunc := range map[string]initFunction{
|
|
||||||
"linux": linux.Init,
|
|
||||||
"ovs": ovs.Init,
|
|
||||||
} {
|
|
||||||
supportedDrivers[name] = initFunc()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDriver will return the bridge driver by name
|
|
||||||
func GetDriver(bridgeName string) api.BridgeDriver {
|
|
||||||
_, err := exec.Command("ovs-vsctl", "br-exists", bridgeName).CombinedOutput()
|
|
||||||
if err == nil {
|
|
||||||
// bridgeName is detected as an ovs bridge, return ovs driver
|
|
||||||
return supportedDrivers["ovs"]
|
|
||||||
}
|
|
||||||
// error happens, use linux bridge as default:
|
|
||||||
// 1. ovs-vsctl doesn't exist, or ovs not supported
|
|
||||||
// 2. bridgeName isn't ovs bridge
|
|
||||||
// whatever, fallthrough to default linux driver
|
|
||||||
|
|
||||||
return supportedDrivers["linux"]
|
|
||||||
}
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: linux bridge driver implement
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package linux
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"isula.org/syscontainer-tools/libnetwork/bridge/api"
|
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
|
||||||
)
|
|
||||||
|
|
||||||
type linuxBridgeDriver struct {
|
|
||||||
bridge string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init returns the linux bridge driver instance
|
|
||||||
func Init() api.BridgeDriver {
|
|
||||||
return &linuxBridgeDriver{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name returns the linux bridge driver name
|
|
||||||
func (d *linuxBridgeDriver) Name() string {
|
|
||||||
return "linux"
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddToBridge will add an interface to bridge
|
|
||||||
func (d *linuxBridgeDriver) AddToBridge(netif, bridge string) error {
|
|
||||||
if len(netif) == 0 || len(bridge) == 0 {
|
|
||||||
return fmt.Errorf("bridge or network interface can't be empty")
|
|
||||||
}
|
|
||||||
netl, err := netlink.LinkByName(netif)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to get link by name %q: %v", netif, err)
|
|
||||||
}
|
|
||||||
return netlink.LinkSetMaster(netl,
|
|
||||||
&netlink.Bridge{LinkAttrs: netlink.LinkAttrs{Name: bridge}})
|
|
||||||
}
|
|
||||||
@ -1,58 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: ovs bridge driver implement
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package ovs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os/exec"
|
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
|
||||||
"isula.org/syscontainer-tools/libnetwork/bridge/api"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ovsBridgeDriver struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init returns the ovs bridge driver instance
|
|
||||||
func Init() api.BridgeDriver {
|
|
||||||
return &ovsBridgeDriver{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name returns the linux bridge driver name
|
|
||||||
func (d *ovsBridgeDriver) Name() string {
|
|
||||||
return "ovs"
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddToBridge will add an interface to bridge
|
|
||||||
func (d *ovsBridgeDriver) AddToBridge(netif, bridge string) error {
|
|
||||||
if len(netif) == 0 || len(bridge) == 0 {
|
|
||||||
return fmt.Errorf("bridge or network interface can't be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := exec.Command("ovs-vsctl", "br-exists", bridge).CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("can't get ovs bridge %q: %v", bridge, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = netlink.LinkByName(netif)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to get link by name %q: %v", netif, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
out, err := exec.Command("ovs-vsctl", "add-port", bridge, netif).CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to add port %q to ovs bridge %q, out: %s, err: %v", netif, bridge, out, err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@ -1,120 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: common network driver
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"isula.org/syscontainer-tools/libnetwork/bridge"
|
|
||||||
"isula.org/syscontainer-tools/libnetwork/bridge/api"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Driver implement the network driver common options
|
|
||||||
type Driver struct {
|
|
||||||
nsPath string
|
|
||||||
ctrName string
|
|
||||||
hostName string
|
|
||||||
mac *net.HardwareAddr
|
|
||||||
ip *net.IPNet
|
|
||||||
bridge string
|
|
||||||
bridgeDriver api.BridgeDriver
|
|
||||||
mtu int
|
|
||||||
qlen int
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetCtrNicName will set the network interface name in container
|
|
||||||
func (d *Driver) SetCtrNicName(name string) {
|
|
||||||
d.ctrName = name
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCtrNicName will return the network interface name in container
|
|
||||||
func (d *Driver) GetCtrNicName() string {
|
|
||||||
return d.ctrName
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetHostNicName will set the network interface name on host
|
|
||||||
func (d *Driver) SetHostNicName(name string) {
|
|
||||||
d.hostName = name
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetHostNicName will return the network interface name on host
|
|
||||||
func (d *Driver) GetHostNicName() string {
|
|
||||||
return d.hostName
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetNsPath will set the network namespace path
|
|
||||||
func (d *Driver) SetNsPath(path string) {
|
|
||||||
d.nsPath = path
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetNsPath will return the network namespace path
|
|
||||||
func (d *Driver) GetNsPath() string {
|
|
||||||
return d.nsPath
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetIP will set the network interface ip
|
|
||||||
func (d *Driver) SetIP(addr *net.IPNet) {
|
|
||||||
d.ip = addr
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetIP will set the network interface ip
|
|
||||||
func (d *Driver) GetIP() *net.IPNet {
|
|
||||||
return d.ip
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetMac will set the network interface mac
|
|
||||||
func (d *Driver) SetMac(mac *net.HardwareAddr) {
|
|
||||||
d.mac = mac
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMac will return the network interface mac
|
|
||||||
func (d *Driver) GetMac() *net.HardwareAddr {
|
|
||||||
return d.mac
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetMtu will set the network interface mtu
|
|
||||||
func (d *Driver) SetMtu(mtu int) {
|
|
||||||
d.mtu = mtu
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMtu will return the network interface mtu
|
|
||||||
func (d *Driver) GetMtu() int {
|
|
||||||
return d.mtu
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetQlen will set the network interface qlen
|
|
||||||
func (d *Driver) SetQlen(qlen int) {
|
|
||||||
d.qlen = qlen
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetQlen will return the network interface qlen
|
|
||||||
func (d *Driver) GetQlen() int {
|
|
||||||
return d.qlen
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetBridge will set the bridge name which the nic connected to
|
|
||||||
func (d *Driver) SetBridge(bridgeName string) {
|
|
||||||
d.bridge = bridgeName
|
|
||||||
d.bridgeDriver = bridge.GetDriver(bridgeName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBridge will return the bridge name which the nic connected to
|
|
||||||
func (d *Driver) GetBridge() string {
|
|
||||||
return d.bridge
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBridgeDriver will return bridge driver interface
|
|
||||||
func (d *Driver) GetBridgeDriver() api.BridgeDriver {
|
|
||||||
return d.bridgeDriver
|
|
||||||
}
|
|
||||||
@ -1,167 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: network interface driver
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package drivers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
// "github.com/Sirupsen/logrus"
|
|
||||||
"github.com/vishvananda/netlink"
|
|
||||||
"isula.org/syscontainer-tools/libnetwork/drivers/common"
|
|
||||||
"isula.org/syscontainer-tools/libnetwork/drivers/eth"
|
|
||||||
"isula.org/syscontainer-tools/libnetwork/drivers/veth"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// ErrTypeNotSupported is the interface type not supported error
|
|
||||||
ErrTypeNotSupported = fmt.Errorf("network interface type not supported")
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// OptionMtu mtu must not be less than 68
|
|
||||||
OptionMtu = 68
|
|
||||||
)
|
|
||||||
|
|
||||||
// Driver defines the network driver function interface
|
|
||||||
type Driver interface {
|
|
||||||
// CreateNic create interface according to type
|
|
||||||
CreateIf() error
|
|
||||||
// DeleteIf delete interface from container
|
|
||||||
DeleteIf() error
|
|
||||||
// JoinAndConfigure join network interface into namespace and configure it
|
|
||||||
JoinAndConfigure() error
|
|
||||||
// Configure update network interface in a container
|
|
||||||
Configure() error
|
|
||||||
// AddToBridge adds interface to bridge
|
|
||||||
AddToBridge() error
|
|
||||||
}
|
|
||||||
|
|
||||||
// New will crate a network driver by type and options
|
|
||||||
func New(driverType string, options ...DriverOptions) (Driver, error) {
|
|
||||||
d := &common.Driver{}
|
|
||||||
if err := processOptions(d, options...); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch driverType {
|
|
||||||
case "", "veth":
|
|
||||||
return veth.New(d)
|
|
||||||
case "eth":
|
|
||||||
return eth.New(d)
|
|
||||||
case "sriov", "dpdk":
|
|
||||||
fallthrough
|
|
||||||
default:
|
|
||||||
return nil, ErrTypeNotSupported
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DriverOptions define a callback function to handle driver option
|
|
||||||
type DriverOptions func(d *common.Driver) error
|
|
||||||
|
|
||||||
func processOptions(d *common.Driver, options ...DriverOptions) error {
|
|
||||||
for _, op := range options {
|
|
||||||
if err := op(d); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NicOptionNsPath handles network namespace path option
|
|
||||||
func NicOptionNsPath(nsPath string) DriverOptions {
|
|
||||||
return func(d *common.Driver) error {
|
|
||||||
nsPath = strings.TrimSpace(nsPath)
|
|
||||||
if len(nsPath) != 0 {
|
|
||||||
d.SetNsPath(nsPath)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NicOptionCtrNicName handles interface name in container option
|
|
||||||
func NicOptionCtrNicName(name string) DriverOptions {
|
|
||||||
return func(d *common.Driver) error {
|
|
||||||
d.SetCtrNicName(strings.TrimSpace(name))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NicOptionHostNicName handles the network interface name on host opstion
|
|
||||||
func NicOptionHostNicName(name string) DriverOptions {
|
|
||||||
return func(d *common.Driver) error {
|
|
||||||
d.SetHostNicName(strings.TrimSpace(name))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NicOptionIP handles network interface ip option
|
|
||||||
func NicOptionIP(ip string) DriverOptions {
|
|
||||||
return func(d *common.Driver) error {
|
|
||||||
ip = strings.TrimSpace(ip)
|
|
||||||
ipnet, err := netlink.ParseIPNet(ip)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
d.SetIP(ipnet)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NicOptionMac handles network interface mac option
|
|
||||||
func NicOptionMac(mac string) DriverOptions {
|
|
||||||
return func(d *common.Driver) error {
|
|
||||||
if len(strings.TrimSpace(mac)) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
hw, err := net.ParseMAC(strings.TrimSpace(mac))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
d.SetMac(&hw)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NicOptionMtu handles interface mtu option
|
|
||||||
func NicOptionMtu(mtu int) DriverOptions {
|
|
||||||
return func(d *common.Driver) error {
|
|
||||||
if mtu < OptionMtu {
|
|
||||||
return fmt.Errorf("Mtu must not be less than 68")
|
|
||||||
}
|
|
||||||
d.SetMtu(mtu)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NicOptionQlen handles interface Qlen option
|
|
||||||
func NicOptionQlen(qlen int) DriverOptions {
|
|
||||||
return func(d *common.Driver) error {
|
|
||||||
if qlen < 0 {
|
|
||||||
return fmt.Errorf("Qlen must not be less than 0")
|
|
||||||
}
|
|
||||||
d.SetQlen(qlen)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NicOptionBridge handles brigde name option
|
|
||||||
func NicOptionBridge(bridge string) DriverOptions {
|
|
||||||
return func(d *common.Driver) error {
|
|
||||||
d.SetBridge(strings.TrimSpace(bridge))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,358 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: ethetic network driver
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package eth
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/docker/libnetwork/netutils"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/vishvananda/netlink"
|
|
||||||
|
|
||||||
"isula.org/syscontainer-tools/libnetwork/drivers/common"
|
|
||||||
"isula.org/syscontainer-tools/libnetwork/nsutils"
|
|
||||||
"isula.org/syscontainer-tools/pkg/ethtool"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ethDriver struct {
|
|
||||||
*common.Driver
|
|
||||||
}
|
|
||||||
|
|
||||||
// New will create a eth driver
|
|
||||||
func New(d *common.Driver) (*ethDriver, error) {
|
|
||||||
driver := ðDriver{
|
|
||||||
Driver: d,
|
|
||||||
}
|
|
||||||
return driver, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *ethDriver) setDefaultEthFeature(name string) error {
|
|
||||||
etool, err := ethtool.NewEthtool(name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer etool.Close()
|
|
||||||
if err := etool.SetNetDeviceTSO(true); err != nil {
|
|
||||||
logrus.Errorf("Failed to set device %s tso on with error: %v", name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := etool.SetNetDeviceSG(true); err != nil {
|
|
||||||
logrus.Errorf("Failed to set device %s sg on with error: %v", name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := etool.SetNetDeviceTX(true); err != nil {
|
|
||||||
logrus.Errorf("Failed to set device %s tx on with error: %v", name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *ethDriver) CreateIf() error {
|
|
||||||
logrus.Debugf("creating eth device: %s:%s", d.GetHostNicName(), d.GetCtrNicName())
|
|
||||||
if d.GetHostNicName() == "" || d.GetCtrNicName() == "" {
|
|
||||||
return fmt.Errorf("Link name error %s:%s", d.GetHostNicName(), d.GetCtrNicName())
|
|
||||||
}
|
|
||||||
err := d.setDefaultEthFeature(d.GetHostNicName())
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to set default eth feature: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
hostNic, err := netlink.LinkByName(d.GetHostNicName())
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to get link by host name %q: %v", d.GetHostNicName(), err)
|
|
||||||
}
|
|
||||||
if err := netlink.LinkSetTxQLen(hostNic, d.GetQlen()); err != nil {
|
|
||||||
return fmt.Errorf("failed to set qlen(%d) for nic(%s)", d.GetQlen(), d.GetHostNicName())
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *ethDriver) DeleteIf() (rErr error) {
|
|
||||||
originalCtrNicName := d.GetCtrNicName()
|
|
||||||
defer func() {
|
|
||||||
if rErr != nil {
|
|
||||||
ctrNic, err := netlink.LinkByName(d.GetCtrNicName())
|
|
||||||
if err != nil {
|
|
||||||
logrus.Errorf("failed to get link by name %q: %v", d.GetCtrNicName(), err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := netlink.LinkSetName(ctrNic, originalCtrNicName); err != nil {
|
|
||||||
logrus.Errorf("failed to rename link: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
d.Driver.SetCtrNicName(originalCtrNicName)
|
|
||||||
logrus.Debugf("Rename link back %s -> %s", ctrNic.Attrs().Name, originalCtrNicName)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
err := nsutils.NsInvoke(
|
|
||||||
d.GetNsPath(), func(nsFD int) error {
|
|
||||||
return nil
|
|
||||||
}, func(nsFD int) error {
|
|
||||||
// post function is executed in container
|
|
||||||
ctrNic, err := netlink.LinkByName(d.GetCtrNicName())
|
|
||||||
if err != nil {
|
|
||||||
logrus.Warnf("%s not found", d.GetCtrNicName())
|
|
||||||
return fmt.Errorf("failed to get link by name %q: %v", d.GetCtrNicName(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
logrus.Debugf("Down device %s", d.GetCtrNicName())
|
|
||||||
// Down the interface.
|
|
||||||
if err := netlink.LinkSetDown(ctrNic); err != nil {
|
|
||||||
return fmt.Errorf("failed to set link down: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
initnsFD, err := os.OpenFile("/proc/1/ns/net", os.O_RDONLY, 0)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed get network namespace %q: %v", "/proc/1/ns/net", err)
|
|
||||||
}
|
|
||||||
defer initnsFD.Close()
|
|
||||||
|
|
||||||
randomName, err := netutils.GenerateRandomName("tmp", 10)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed generate random name: %v", err)
|
|
||||||
}
|
|
||||||
if err := netlink.LinkSetName(ctrNic, randomName); err != nil {
|
|
||||||
return fmt.Errorf("failed to rename link: %v", err)
|
|
||||||
}
|
|
||||||
d.Driver.SetCtrNicName(randomName)
|
|
||||||
logrus.Debugf("Rename link %s -> %s", ctrNic.Attrs().Name, randomName)
|
|
||||||
|
|
||||||
// move the network interface to the host namespace
|
|
||||||
if err := netlink.LinkSetNsFd(ctrNic, int(initnsFD.Fd())); err != nil {
|
|
||||||
return fmt.Errorf("failed to set namespace on link %q: %v", d.GetCtrNicName(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
if strings.Contains(err.Error(), "Link not found") {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
hostNic, err := netlink.LinkByName(d.GetCtrNicName())
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to get host link by name %q: %v", d.GetCtrNicName(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if rErr != nil {
|
|
||||||
// nsFD is used to recover on failure
|
|
||||||
nsFD, err := os.OpenFile(d.GetNsPath(), os.O_RDONLY, 0)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Errorf("Recover on failure: failed get network namespace %s: %v", d.GetNsPath(), err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// move the network interface back to the container
|
|
||||||
if err := netlink.LinkSetNsFd(hostNic, int(nsFD.Fd())); err != nil {
|
|
||||||
logrus.Errorf("Recover on failure: failed to move nic(%s) back to container: %v", d.GetHostNicName(), err)
|
|
||||||
nsFD.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
nsFD.Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
// set iface to user desired name
|
|
||||||
if err := netlink.LinkSetName(hostNic, d.GetHostNicName()); err != nil {
|
|
||||||
return fmt.Errorf("failed to rename link: %v", err)
|
|
||||||
}
|
|
||||||
logrus.Debugf("Rename link %s -> %s", hostNic.Attrs().Name, d.GetHostNicName())
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *ethDriver) setNicConfigure(nic netlink.Link) (rErr error) {
|
|
||||||
// set MAC
|
|
||||||
if d.GetMac() != nil {
|
|
||||||
// set hardware address for interface
|
|
||||||
if err := netlink.LinkSetHardwareAddr(nic, *(d.GetMac())); err != nil {
|
|
||||||
return fmt.Errorf("failed to set hardware: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// set mtu
|
|
||||||
if err := netlink.LinkSetMTU(nic, d.GetMtu()); err != nil {
|
|
||||||
return fmt.Errorf("failed to set mtu: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// set qlen
|
|
||||||
if err := netlink.LinkSetTxQLen(nic, d.GetQlen()); err != nil {
|
|
||||||
return fmt.Errorf("failed to set qlen(%d) for nic(%s)", d.GetQlen(), d.GetCtrNicName())
|
|
||||||
}
|
|
||||||
|
|
||||||
// set ipv4 address (TODO: ipv6 support?)
|
|
||||||
oldAddr, _ := netlink.AddrList(nic, netlink.FAMILY_V4)
|
|
||||||
if oldAddr != nil {
|
|
||||||
// we only have on IP set for the interface
|
|
||||||
if err := netlink.AddrDel(nic, &oldAddr[0]); err != nil {
|
|
||||||
return fmt.Errorf("failed to delete old ip address: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// set ipv4 address (TODO: ipv6 support?)
|
|
||||||
ipAddr := &netlink.Addr{IPNet: d.GetIP(), Label: ""}
|
|
||||||
if err := netlink.AddrAdd(nic, ipAddr); err != nil {
|
|
||||||
return fmt.Errorf("failed to configure ip address: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *ethDriver) JoinAndConfigure() (rErr error) {
|
|
||||||
oldName := d.GetHostNicName()
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if rErr != nil {
|
|
||||||
nic, err := netlink.LinkByName(d.GetHostNicName())
|
|
||||||
if err != nil {
|
|
||||||
logrus.Errorf("Recover on failure: failed to get host link by name %q: %v", d.GetHostNicName(), err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := netlink.LinkSetName(nic, oldName); err != nil {
|
|
||||||
logrus.Errorf("Recover on failure: failed to rename link back to %s: %v", oldName, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return nsutils.NsInvoke(
|
|
||||||
d.GetNsPath(), func(nsFD int) error {
|
|
||||||
// pre function is executed in host
|
|
||||||
hostNic, err := netlink.LinkByName(d.GetHostNicName())
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to get link by host name %q: %v", d.GetHostNicName(), err)
|
|
||||||
}
|
|
||||||
randomName, err := netutils.GenerateRandomName("tmp", 10)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed generate random name: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// down the interface before configuring
|
|
||||||
if err := netlink.LinkSetDown(hostNic); err != nil {
|
|
||||||
return fmt.Errorf("failed to set link down: %v", err)
|
|
||||||
}
|
|
||||||
logrus.Debugf("Rename link %s -> %s", hostNic.Attrs().Name, randomName)
|
|
||||||
|
|
||||||
// set iface to user desired name
|
|
||||||
if err := netlink.LinkSetName(hostNic, randomName); err != nil {
|
|
||||||
return fmt.Errorf("failed to rename link: %v", err)
|
|
||||||
}
|
|
||||||
d.SetHostNicName(randomName)
|
|
||||||
|
|
||||||
// move the network interface to the destination
|
|
||||||
if err := netlink.LinkSetNsFd(hostNic, nsFD); err != nil {
|
|
||||||
return fmt.Errorf("failed to set namespace on link %q: %v", d.GetHostNicName(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}, func(nsFD int) (rErr error) {
|
|
||||||
// post function is executed in container
|
|
||||||
ctrNic, err := netlink.LinkByName(d.GetHostNicName())
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to get link by name %q: %v", d.GetHostNicName(), err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if rErr != nil {
|
|
||||||
// initnsFD is used to recover on failure
|
|
||||||
initnsFD, err := os.OpenFile("/proc/1/ns/net", os.O_RDONLY, 0)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Errorf("Recover on failure: failed get network namespace /proc/1/ns/net: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// move the network interface back to the host
|
|
||||||
if err := netlink.LinkSetNsFd(ctrNic, int(initnsFD.Fd())); err != nil {
|
|
||||||
logrus.Errorf("Recover on failure: failed to move nic(%s) back to host: %v", d.GetHostNicName(), err)
|
|
||||||
initnsFD.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
initnsFD.Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
// set iface to user desired name
|
|
||||||
if err := netlink.LinkSetName(ctrNic, d.GetCtrNicName()); err != nil {
|
|
||||||
return fmt.Errorf("failed to rename link: %v", err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if rErr != nil {
|
|
||||||
// still try to move this nic back to host even if recoverNicName failed
|
|
||||||
logrus.Debugf("Recover on failure: try to rename nic name back to %s", d.GetHostNicName())
|
|
||||||
if err := netlink.LinkSetName(ctrNic, d.GetHostNicName()); err != nil {
|
|
||||||
d.SetHostNicName(d.GetCtrNicName())
|
|
||||||
logrus.Errorf("Recover on failure: failed to rename nic back: %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
d.SetHostNicName(d.GetHostNicName())
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err = d.setNicConfigure(ctrNic); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Up the interface.
|
|
||||||
if err := netlink.LinkSetUp(ctrNic); err != nil {
|
|
||||||
return fmt.Errorf("failed to set link up: %v", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *ethDriver) Configure() (rErr error) {
|
|
||||||
return nsutils.NsInvoke(
|
|
||||||
d.GetNsPath(), func(nsFD int) error {
|
|
||||||
return nil
|
|
||||||
}, func(nsFD int) error {
|
|
||||||
// post function is executed in container
|
|
||||||
ctrNic, err := netlink.LinkByName(d.GetCtrNicName())
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to get link by name %q: %v", d.GetCtrNicName(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
linkUp := false
|
|
||||||
if ctrNic.Attrs().Flags&(1<<uint(0)) == 1 {
|
|
||||||
linkUp = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Down the interface before configure.
|
|
||||||
if err := netlink.LinkSetDown(ctrNic); err != nil {
|
|
||||||
return fmt.Errorf("failed to set link down: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = d.setNicConfigure(ctrNic); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if linkUp {
|
|
||||||
// Up the interface.
|
|
||||||
if err := netlink.LinkSetUp(ctrNic); err != nil {
|
|
||||||
return fmt.Errorf("failed to set link up: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddTOBridge will add the eth to bridge
|
|
||||||
func (d *ethDriver) AddToBridge() error {
|
|
||||||
if len(d.GetBridge()) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return fmt.Errorf("can't add eth in container to bridge in host")
|
|
||||||
}
|
|
||||||
@ -1,304 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: virtual ethetic network driver
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package veth
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/docker/libnetwork/netutils"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/vishvananda/netlink"
|
|
||||||
|
|
||||||
"isula.org/syscontainer-tools/libnetwork/drivers/common"
|
|
||||||
"isula.org/syscontainer-tools/libnetwork/nsutils"
|
|
||||||
"isula.org/syscontainer-tools/pkg/ethtool"
|
|
||||||
)
|
|
||||||
|
|
||||||
type vethDriver struct {
|
|
||||||
*common.Driver
|
|
||||||
veth *netlink.Veth
|
|
||||||
mutex sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// New will create a veth driver
|
|
||||||
func New(d *common.Driver) (*vethDriver, error) {
|
|
||||||
driver := &vethDriver{
|
|
||||||
Driver: d,
|
|
||||||
}
|
|
||||||
return driver, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *vethDriver) setDefaultVethFeature(name string) error {
|
|
||||||
etool, err := ethtool.NewEthtool(name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer etool.Close()
|
|
||||||
if err := etool.SetNetDeviceTSO(true); err != nil {
|
|
||||||
logrus.Errorf("Failed to set device %s tso on with error: %v", name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := etool.SetNetDeviceSG(true); err != nil {
|
|
||||||
logrus.Errorf("Failed to set device %s sg on with error: %v", name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := etool.SetNetDeviceTX(true); err != nil {
|
|
||||||
logrus.Errorf("Failed to set device %s tx on with error: %v", name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *vethDriver) CreateIf() error {
|
|
||||||
logrus.Debugf("creating veth pairs")
|
|
||||||
hostIfName, err := netutils.GenerateIfaceName("veth", 10)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
guestIfName, err := netutils.GenerateIfaceName("veth", 10)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Generate and add the interface pipe host <-> sandbox
|
|
||||||
d.mutex.Lock()
|
|
||||||
d.veth = &netlink.Veth{
|
|
||||||
LinkAttrs: netlink.LinkAttrs{Name: hostIfName, TxQLen: d.GetQlen()},
|
|
||||||
PeerName: guestIfName,
|
|
||||||
}
|
|
||||||
d.mutex.Unlock()
|
|
||||||
if err = netlink.LinkAdd(d.veth); err != nil {
|
|
||||||
return fmt.Errorf("failed to create veth pairs: %v", err)
|
|
||||||
}
|
|
||||||
if err := d.setDefaultVethFeature(guestIfName); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := d.setDefaultVethFeature(hostIfName); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logrus.Debugf("veth pair (%s, %s) created", hostIfName, guestIfName)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *vethDriver) DeleteIf() error {
|
|
||||||
veth, err := netlink.LinkByName(d.GetHostNicName())
|
|
||||||
if err != nil {
|
|
||||||
// As add-nic supports 'update-config-only' option,
|
|
||||||
// With this flag, syscontainer-tools will update config only, don't add device to container.
|
|
||||||
// So if device dose not exist on host, ignore it.
|
|
||||||
if strings.Contains(err.Error(), "Link not found") {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return netlink.LinkDel(veth)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *vethDriver) setNicConfigure(nic netlink.Link) (rErr error) {
|
|
||||||
// set MAC
|
|
||||||
if d.GetMac() != nil {
|
|
||||||
// set hardware address for interface
|
|
||||||
if err := netlink.LinkSetHardwareAddr(nic, *(d.GetMac())); err != nil {
|
|
||||||
return fmt.Errorf("failed to set hardware: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// set mtu
|
|
||||||
if err := netlink.LinkSetMTU(nic, d.GetMtu()); err != nil {
|
|
||||||
return fmt.Errorf("failed to set mtu: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// set qlen
|
|
||||||
if err := netlink.LinkSetTxQLen(nic, d.GetQlen()); err != nil {
|
|
||||||
return fmt.Errorf("failed to set qlen(%d) for nic(%s)", d.GetQlen(), d.GetCtrNicName())
|
|
||||||
}
|
|
||||||
|
|
||||||
// set ipv4 address (TODO: ipv6 support?)
|
|
||||||
oldAddr, _ := netlink.AddrList(nic, netlink.FAMILY_V4)
|
|
||||||
if oldAddr != nil {
|
|
||||||
// we only have on IP set for the interface
|
|
||||||
if err := netlink.AddrDel(nic, &oldAddr[0]); err != nil {
|
|
||||||
return fmt.Errorf("failed to delete old ip address: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ipAddr := &netlink.Addr{IPNet: d.GetIP(), Label: ""}
|
|
||||||
if err := netlink.AddrAdd(nic, ipAddr); err != nil {
|
|
||||||
return fmt.Errorf("failed to configure ip address: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *vethDriver) JoinAndConfigure() (rErr error) {
|
|
||||||
if d.veth == nil || d.veth.Attrs() == nil {
|
|
||||||
return fmt.Errorf("can't find veth interface")
|
|
||||||
}
|
|
||||||
// peerName does not matter, since we can delete veth pare via one end of it
|
|
||||||
hostNicName := d.veth.Attrs().Name
|
|
||||||
defer func() {
|
|
||||||
if rErr != nil {
|
|
||||||
logrus.Infof("Recover on failure: delete veth(%s)", hostNicName)
|
|
||||||
nic, err := netlink.LinkByName(hostNicName)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Errorf("Recover on failure: failed to get link by name(%q): %v", hostNicName, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := netlink.LinkDel(nic); err != nil {
|
|
||||||
logrus.Errorf("Recover on failure: failed to remove nic(%s): %v", hostNicName, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
err := nsutils.NsInvoke(
|
|
||||||
d.GetNsPath(), func(nsFD int) error {
|
|
||||||
// pre function is executed in host
|
|
||||||
hostNic, err := netlink.LinkByName(d.veth.Attrs().Name)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to get link by name %q: %v", d.veth.Attrs().Name, err)
|
|
||||||
}
|
|
||||||
ctrNic, err := netlink.LinkByName(d.veth.PeerName)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to get link by name %q: %v", d.veth.PeerName, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// down the interface before configuring
|
|
||||||
if err := netlink.LinkSetDown(hostNic); err != nil {
|
|
||||||
return fmt.Errorf("failed to set link down: %v", err)
|
|
||||||
}
|
|
||||||
if err := netlink.LinkSetDown(ctrNic); err != nil {
|
|
||||||
return fmt.Errorf("failed to set link down: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// move the network interface to the destination
|
|
||||||
if err := netlink.LinkSetNsFd(ctrNic, nsFD); err != nil {
|
|
||||||
return fmt.Errorf("failed to set namespace on link %q: %v", d.veth.PeerName, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// attach host nic to bridge and configure mtu
|
|
||||||
if err = netlink.LinkSetMTU(hostNic, d.GetMtu()); err != nil {
|
|
||||||
return fmt.Errorf("failed to set mtu: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if d.GetHostNicName() != "" {
|
|
||||||
// set iface to user desired name
|
|
||||||
if err := netlink.LinkSetName(hostNic, d.GetHostNicName()); err != nil {
|
|
||||||
return fmt.Errorf("failed to rename link %s -> %s: %v", d.veth.Attrs().Name, d.GetHostNicName(), err)
|
|
||||||
}
|
|
||||||
hostNicName = d.GetHostNicName()
|
|
||||||
logrus.Debugf("Rename host link %s -> %s", d.veth.Attrs().Name, d.GetHostNicName())
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = d.AddToBridge(); err != nil {
|
|
||||||
return fmt.Errorf("failed to add to bridge: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := netlink.LinkSetUp(hostNic); err != nil {
|
|
||||||
return fmt.Errorf("failed to set link up: %v", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}, func(nsFD int) error {
|
|
||||||
// post function is executed in container
|
|
||||||
ctrNic, err := netlink.LinkByName(d.veth.PeerName)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to get link by name %q: %v", d.veth.PeerName, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// set iface to user desired name
|
|
||||||
if err := netlink.LinkSetName(ctrNic, d.GetCtrNicName()); err != nil {
|
|
||||||
return fmt.Errorf("failed to rename link: %v", err)
|
|
||||||
}
|
|
||||||
logrus.Debugf("Rename container link %s -> %s", d.veth.PeerName, d.GetCtrNicName())
|
|
||||||
|
|
||||||
if err := d.setNicConfigure(ctrNic); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Up the interface.
|
|
||||||
if err := netlink.LinkSetUp(ctrNic); err != nil {
|
|
||||||
return fmt.Errorf("failed to set link up: %v", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *vethDriver) Configure() (rErr error) {
|
|
||||||
return nsutils.NsInvoke(
|
|
||||||
d.GetNsPath(), func(nsFD int) error {
|
|
||||||
// pre function is executed in host
|
|
||||||
hostNic, err := netlink.LinkByName(d.GetHostNicName())
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to get link by name %q: %v", d.GetHostNicName(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// down the interface before configuring
|
|
||||||
if err := netlink.LinkSetDown(hostNic); err != nil {
|
|
||||||
return fmt.Errorf("failed to set link down: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// attach host nic to bridge and configure mtu
|
|
||||||
if err = netlink.LinkSetMTU(hostNic, d.GetMtu()); err != nil {
|
|
||||||
return fmt.Errorf("failed to set mtu: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := netlink.LinkSetTxQLen(hostNic, d.GetQlen()); err != nil {
|
|
||||||
return fmt.Errorf("failed to set qlen: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = d.AddToBridge(); err != nil {
|
|
||||||
return fmt.Errorf("failed to add to bridge: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := netlink.LinkSetUp(hostNic); err != nil {
|
|
||||||
return fmt.Errorf("failed to set link up: %v", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}, func(nsFD int) error {
|
|
||||||
// post function is executed in container
|
|
||||||
ctrNic, err := netlink.LinkByName(d.GetCtrNicName())
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to get link by name %q: %v", d.GetCtrNicName(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// down the interface before configuring
|
|
||||||
if err := netlink.LinkSetDown(ctrNic); err != nil {
|
|
||||||
return fmt.Errorf("failed to set link down: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := d.setNicConfigure(ctrNic); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Up the interface.
|
|
||||||
if err := netlink.LinkSetUp(ctrNic); err != nil {
|
|
||||||
return fmt.Errorf("failed to set link up: %v", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddTOBridge will add the veth to bridge
|
|
||||||
func (d *vethDriver) AddToBridge() error {
|
|
||||||
if len(d.GetBridge()) == 0 {
|
|
||||||
return fmt.Errorf("bridge can't be empty")
|
|
||||||
}
|
|
||||||
bd := d.GetBridgeDriver()
|
|
||||||
if bd == nil {
|
|
||||||
return fmt.Errorf("can't get bridge driver")
|
|
||||||
}
|
|
||||||
return bd.AddToBridge(d.GetHostNicName(), d.GetBridge())
|
|
||||||
}
|
|
||||||
@ -1,275 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: network interface operation
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package libnetwork
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
hconfig "isula.org/syscontainer-tools/config"
|
|
||||||
"isula.org/syscontainer-tools/container"
|
|
||||||
"isula.org/syscontainer-tools/libnetwork/drivers"
|
|
||||||
"isula.org/syscontainer-tools/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AddNic will add a network interface to container, it will update the config for container
|
|
||||||
func AddNic(ctr *container.Container, config *types.InterfaceConf, updateConfigOnly bool) error {
|
|
||||||
if err := ctr.Lock(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer ctr.Unlock()
|
|
||||||
// create config file handler.
|
|
||||||
hConfig, err := hconfig.NewContainerConfig(ctr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer hConfig.Flush()
|
|
||||||
|
|
||||||
if err := hConfig.CheckNicNum(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := hConfig.IsConflictInterface(config); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := hConfig.UpdateNetworkInterface(config, true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// don't insert net interface when:
|
|
||||||
// 1. update-config-only flag is set
|
|
||||||
// 2. container isn't running(pid==0)
|
|
||||||
if !updateConfigOnly && ctr.Pid() > 0 && ctr.CheckPidExist() {
|
|
||||||
if err := AddNicToContainer(ctr.NetNsPath(), config); err != nil {
|
|
||||||
// roll back
|
|
||||||
hConfig.UpdateNetworkInterface(config, false)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Fprintf(os.Stdout, "Add network interface (%s) to container (%s,%s) done\n", config.HostNicName, ctr.Name(), config.CtrNicName)
|
|
||||||
logrus.Infof("Add network interface (%s) to container (%s,%s) done", config.HostNicName, ctr.Name(), config.CtrNicName)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddNicToContainer will add a network interface to container only.
|
|
||||||
// It will be called by network-hook
|
|
||||||
func AddNicToContainer(nsPath string, config *types.InterfaceConf) (rErr error) {
|
|
||||||
driver, err := drivers.New(config.Type,
|
|
||||||
drivers.NicOptionCtrNicName(config.CtrNicName),
|
|
||||||
drivers.NicOptionHostNicName(config.HostNicName),
|
|
||||||
drivers.NicOptionNsPath(nsPath),
|
|
||||||
drivers.NicOptionIP(config.IP),
|
|
||||||
drivers.NicOptionMac(config.Mac),
|
|
||||||
drivers.NicOptionMtu(config.Mtu),
|
|
||||||
drivers.NicOptionQlen(config.Qlen),
|
|
||||||
drivers.NicOptionBridge(config.Bridge))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := driver.CreateIf(); err != nil {
|
|
||||||
return fmt.Errorf("failed to create interface: %v", err)
|
|
||||||
}
|
|
||||||
// do not need to DeleteIf here, if CreateIf failed, there are no ifs.
|
|
||||||
// JoinAndConfigure is doing cleanup within itself
|
|
||||||
return driver.JoinAndConfigure()
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateNicInContainer will update an existing network interface in container.
|
|
||||||
func UpdateNicInContainer(nsPath string, config *types.InterfaceConf) (rErr error) {
|
|
||||||
driver, err := drivers.New(config.Type,
|
|
||||||
drivers.NicOptionCtrNicName(config.CtrNicName),
|
|
||||||
drivers.NicOptionHostNicName(config.HostNicName),
|
|
||||||
drivers.NicOptionNsPath(nsPath),
|
|
||||||
drivers.NicOptionIP(config.IP),
|
|
||||||
drivers.NicOptionMac(config.Mac),
|
|
||||||
drivers.NicOptionMtu(config.Mtu),
|
|
||||||
drivers.NicOptionQlen(config.Qlen),
|
|
||||||
drivers.NicOptionBridge(config.Bridge))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure is doing cleanup within itself
|
|
||||||
return driver.Configure()
|
|
||||||
}
|
|
||||||
|
|
||||||
// DelNic will remove a network interface from container and update the config
|
|
||||||
func DelNic(ctr *container.Container, config *types.InterfaceConf) error {
|
|
||||||
if err := ctr.Lock(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer ctr.Unlock()
|
|
||||||
// create config file handler.
|
|
||||||
hConfig, err := hconfig.NewContainerConfig(ctr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer hConfig.Flush()
|
|
||||||
|
|
||||||
var newConfig *types.InterfaceConf
|
|
||||||
if newConfig = hConfig.FindInterfaceByName(config); newConfig == nil {
|
|
||||||
return fmt.Errorf("Network interface %s,%s with type %s not exist in container %s", config.HostNicName, config.CtrNicName, config.Type, ctr.Name())
|
|
||||||
}
|
|
||||||
if err := hConfig.UpdateNetworkInterface(newConfig, false); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// only work for running container
|
|
||||||
if ctr.Pid() > 0 && ctr.CheckPidExist() {
|
|
||||||
if err := DelNicFromContainer(ctr.NetNsPath(), newConfig); err != nil {
|
|
||||||
if !strings.Contains(err.Error(), "failed to get host link by name") {
|
|
||||||
// roll back
|
|
||||||
hConfig.UpdateNetworkInterface(newConfig, true)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logrus.Errorf("Remove network interface error: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(os.Stdout, "Remove network interface (%s) from container (%s,%s) done\n", newConfig.HostNicName, ctr.Name(), newConfig.CtrNicName)
|
|
||||||
logrus.Infof("Remove network interface (%s) from container (%s,%s) done", newConfig.HostNicName, ctr.Name(), newConfig.CtrNicName)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DelNicFromContainer will remove a network interface from container only
|
|
||||||
func DelNicFromContainer(nsPath string, config *types.InterfaceConf) error {
|
|
||||||
driver, err := drivers.New(config.Type,
|
|
||||||
drivers.NicOptionCtrNicName(config.CtrNicName),
|
|
||||||
drivers.NicOptionHostNicName(config.HostNicName),
|
|
||||||
drivers.NicOptionNsPath(nsPath),
|
|
||||||
drivers.NicOptionIP(config.IP),
|
|
||||||
drivers.NicOptionMac(config.Mac),
|
|
||||||
drivers.NicOptionMtu(config.Mtu),
|
|
||||||
drivers.NicOptionBridge(config.Bridge))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return driver.DeleteIf()
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateNic will reconfigure network interface for a container
|
|
||||||
func UpdateNic(ctr *container.Container, config *types.InterfaceConf, updateConfigOnly bool) error {
|
|
||||||
if err := ctr.Lock(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer ctr.Unlock()
|
|
||||||
hConfig, err := hconfig.NewContainerConfig(ctr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var tmpConfig = new(types.InterfaceConf)
|
|
||||||
tmpConfig.CtrNicName = config.CtrNicName
|
|
||||||
|
|
||||||
var newConfig *types.InterfaceConf
|
|
||||||
if newConfig = hConfig.FindInterfaceByName(tmpConfig); newConfig == nil {
|
|
||||||
return fmt.Errorf("Network interface %s,%s with type %s not exist in container %s", config.HostNicName, config.CtrNicName, config.Type, ctr.Name())
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.IP == "" {
|
|
||||||
tmpConfig.IP = newConfig.IP
|
|
||||||
} else {
|
|
||||||
tmpConfig.IP = config.IP
|
|
||||||
msg := fmt.Sprintf("Update IP address for network interface (%s,%v) done", config.CtrNicName, config.IP)
|
|
||||||
fmt.Fprintln(os.Stdout, msg)
|
|
||||||
logrus.Info(msg)
|
|
||||||
}
|
|
||||||
if config.Mac == "" {
|
|
||||||
tmpConfig.Mac = newConfig.Mac
|
|
||||||
} else {
|
|
||||||
tmpConfig.Mac = config.Mac
|
|
||||||
msg := fmt.Sprintf("Update MAC address for network interface (%s,%v) done", config.CtrNicName, config.Mac)
|
|
||||||
fmt.Fprintln(os.Stdout, msg)
|
|
||||||
logrus.Info(msg)
|
|
||||||
|
|
||||||
}
|
|
||||||
if config.Bridge == "" {
|
|
||||||
tmpConfig.Bridge = newConfig.Bridge
|
|
||||||
} else {
|
|
||||||
tmpConfig.Bridge = config.Bridge
|
|
||||||
msg := fmt.Sprintf("Update Bridge for network interface (%s,%v) done", config.CtrNicName, config.Bridge)
|
|
||||||
fmt.Fprintln(os.Stdout, msg)
|
|
||||||
logrus.Info(msg)
|
|
||||||
|
|
||||||
}
|
|
||||||
if config.Mtu == 0 {
|
|
||||||
tmpConfig.Mtu = newConfig.Mtu
|
|
||||||
} else {
|
|
||||||
tmpConfig.Mtu = config.Mtu
|
|
||||||
msg := fmt.Sprintf("Update Mtu for network interface (%s,%v) done", config.CtrNicName, config.Mtu)
|
|
||||||
fmt.Fprintln(os.Stdout, msg)
|
|
||||||
logrus.Info(msg)
|
|
||||||
|
|
||||||
}
|
|
||||||
// we use qlen < 0 to check if the user has set parameter qlen or not
|
|
||||||
if config.Qlen < 0 {
|
|
||||||
tmpConfig.Qlen = newConfig.Qlen
|
|
||||||
} else {
|
|
||||||
tmpConfig.Qlen = config.Qlen
|
|
||||||
msg := fmt.Sprintf("Update Qlen for network interface (%s,%v)", config.CtrNicName, config.Qlen)
|
|
||||||
fmt.Fprintln(os.Stdout, msg)
|
|
||||||
logrus.Info(msg)
|
|
||||||
}
|
|
||||||
tmpConfig.Type = newConfig.Type
|
|
||||||
tmpConfig.HostNicName = newConfig.HostNicName
|
|
||||||
|
|
||||||
if hConfig.IsSameInterface(tmpConfig) {
|
|
||||||
logrus.Infof("Network interface in container %s: Identical setting, nothing to change", config.CtrNicName, ctr.Name())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err := hConfig.UpdateNetworkInterface(newConfig, false); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := hConfig.IsConflictInterface(tmpConfig); err != nil {
|
|
||||||
if err := hConfig.UpdateNetworkInterface(newConfig, true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !updateConfigOnly && ctr.Pid() > 0 && ctr.CheckPidExist() {
|
|
||||||
if err := UpdateNicInContainer(ctr.NetNsPath(), tmpConfig); err != nil {
|
|
||||||
if err := hConfig.UpdateNetworkInterface(newConfig, true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// update the config file.
|
|
||||||
if err := hConfig.UpdateNetworkInterface(tmpConfig, true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
hConfig.Flush()
|
|
||||||
logrus.Infof("Network interface %s in container %s update successfully", config.CtrNicName, ctr.Name())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListNic will list all network interfaces in a container
|
|
||||||
func ListNic(ctr *container.Container, filter *types.InterfaceConf) ([]*types.InterfaceConf, error) {
|
|
||||||
if err := ctr.Lock(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer ctr.Unlock()
|
|
||||||
hConfig, err := hconfig.NewContainerConfig(ctr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return hConfig.GetNics(filter), nil
|
|
||||||
}
|
|
||||||
@ -1,57 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: netns invoke
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package nsutils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"github.com/vishvananda/netns"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NsInvoke function is used for setting network outside/inside the container/netns
|
|
||||||
// prefunc is called in the host, and postfunc is used in container
|
|
||||||
func NsInvoke(path string, prefunc func(nsFD int) error, postfunc func(callerFD int) error) error {
|
|
||||||
initns, err := netns.Get()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed get network namespace %v", err)
|
|
||||||
}
|
|
||||||
defer initns.Close()
|
|
||||||
|
|
||||||
ns, err := netns.GetFromPath(path)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed get network namespace %s: %v", path, err)
|
|
||||||
}
|
|
||||||
defer ns.Close()
|
|
||||||
|
|
||||||
// Invoked before the namespace switch happens but after the namespace file
|
|
||||||
// handle is obtained.
|
|
||||||
if err := prefunc(int(ns)); err != nil {
|
|
||||||
return fmt.Errorf("failed in prefunc: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
runtime.LockOSThread()
|
|
||||||
defer runtime.UnlockOSThread()
|
|
||||||
|
|
||||||
if err = netns.Set(ns); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Invoked after the namespace switch.
|
|
||||||
err = postfunc(int(initns))
|
|
||||||
if err1 := netns.Set(initns); err1 != nil {
|
|
||||||
return fmt.Errorf("failed to set to initial namespace: %v: %v", err1, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
@ -1,247 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: network routes operation
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package libnetwork
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
hconfig "isula.org/syscontainer-tools/config"
|
|
||||||
"isula.org/syscontainer-tools/container"
|
|
||||||
"isula.org/syscontainer-tools/libnetwork/nsutils"
|
|
||||||
"isula.org/syscontainer-tools/types"
|
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AddRoutes will add network routes to contianer and update container config.
|
|
||||||
func AddRoutes(ctr *container.Container, routes []*types.Route, updateConfigOnly bool) error {
|
|
||||||
if err := ctr.Lock(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer ctr.Unlock()
|
|
||||||
|
|
||||||
// create config file handler.
|
|
||||||
hConfig, err := hconfig.NewContainerConfig(ctr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer hConfig.Flush()
|
|
||||||
|
|
||||||
for _, route := range routes {
|
|
||||||
if err := hConfig.IsConflictRoute(route); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := hConfig.UpdateNetworkRoutes(route, true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Don't insert real route rules when:
|
|
||||||
// 1. update-config-only flag is set
|
|
||||||
// 2. or container isn't running(pid=0)
|
|
||||||
if !updateConfigOnly && ctr.Pid() > 0 && ctr.CheckPidExist() {
|
|
||||||
if err := AddRouteToContainer(ctr.NetNsPath(), route); err != nil {
|
|
||||||
// roll back
|
|
||||||
hConfig.UpdateNetworkRoutes(route, false)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
msg := fmt.Sprintf("Add route to container %s, route: %s done", ctr.Name(), route.String())
|
|
||||||
fmt.Fprintln(os.Stdout, msg)
|
|
||||||
logrus.Info(msg)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddRouteToContainer will add one route to container.
|
|
||||||
// It will be called by network-hook too.
|
|
||||||
func AddRouteToContainer(nsPath string, route *types.Route) error {
|
|
||||||
var err error
|
|
||||||
src := strings.TrimSpace(route.Src)
|
|
||||||
dest := strings.TrimSpace(route.Dest)
|
|
||||||
gw := strings.TrimSpace(route.Gw)
|
|
||||||
dev := strings.TrimSpace(route.Dev)
|
|
||||||
if len(src) == 0 && len(gw) == 0 && len(dev) == 0 {
|
|
||||||
return fmt.Errorf("src or gw or dev name is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
rule := &netlink.Route{}
|
|
||||||
if dest == "default" {
|
|
||||||
dest = ""
|
|
||||||
}
|
|
||||||
if len(dest) != 0 {
|
|
||||||
rule.Dst, err = netlink.ParseIPNet(dest)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to parse dest %q of route rule", dest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(src) != 0 {
|
|
||||||
rule.Src = net.ParseIP(src)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to parse src ip")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(gw) != 0 {
|
|
||||||
rule.Gw = net.ParseIP(gw)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to parse gw ip")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nsutils.NsInvoke(nsPath,
|
|
||||||
func(nsFD int) error { return nil },
|
|
||||||
func(nsFD int) error {
|
|
||||||
// executed in container
|
|
||||||
if len(dev) != 0 {
|
|
||||||
ctrNic, err := netlink.LinkByName(dev)
|
|
||||||
if err != nil || ctrNic == nil {
|
|
||||||
return fmt.Errorf("failed to get link by name %s: %v", dev, err)
|
|
||||||
}
|
|
||||||
rule.LinkIndex = ctrNic.Attrs().Index
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := netlink.RouteAdd(rule); err != nil {
|
|
||||||
return fmt.Errorf("failed to add route: %v", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// DelRoutes will remove network routes from contianer and update container config.
|
|
||||||
func DelRoutes(ctr *container.Container, routes []*types.Route, updateConfigOnly bool) error {
|
|
||||||
if err := ctr.Lock(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer ctr.Unlock()
|
|
||||||
|
|
||||||
// create config file handler.
|
|
||||||
hConfig, err := hconfig.NewContainerConfig(ctr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer hConfig.Flush()
|
|
||||||
|
|
||||||
var retErr []error
|
|
||||||
for _, r := range routes {
|
|
||||||
if exist := hConfig.IsRouteExist(r); !exist {
|
|
||||||
errinfo := fmt.Sprint("Route(", r, ") is not added by syscontainer-tools, can not remove it, please check input parameter.")
|
|
||||||
retErr = append(retErr, errors.New(errinfo))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, route := range hConfig.GetRoutes(r) {
|
|
||||||
if err := hConfig.UpdateNetworkRoutes(route, false); err != nil {
|
|
||||||
retErr = append(retErr, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// for running container only
|
|
||||||
if !updateConfigOnly && ctr.Pid() > 0 && ctr.CheckPidExist() {
|
|
||||||
if err := DelRouteFromContainer(ctr.NetNsPath(), route); err != nil {
|
|
||||||
// roll back
|
|
||||||
hConfig.UpdateNetworkRoutes(route, true)
|
|
||||||
retErr = append(retErr, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Fprintf(os.Stdout, "Remove route from container %s, route: %s done\n", ctr.Name(), route.String())
|
|
||||||
logrus.Infof("Remove route from container %s, route: %s done", ctr.Name(), route.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(retErr) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
for i := 0; i < len(retErr); i++ {
|
|
||||||
retErr[i] = fmt.Errorf("%s", retErr[i].Error())
|
|
||||||
}
|
|
||||||
return errors.New(strings.Trim(fmt.Sprint(retErr), "[]"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// DelRouteFromContainer will add one route to container.
|
|
||||||
func DelRouteFromContainer(nsPath string, route *types.Route) error {
|
|
||||||
var err error
|
|
||||||
src := strings.TrimSpace(route.Src)
|
|
||||||
dest := strings.TrimSpace(route.Dest)
|
|
||||||
gw := strings.TrimSpace(route.Gw)
|
|
||||||
dev := strings.TrimSpace(route.Dev)
|
|
||||||
if len(src) == 0 && len(gw) == 0 && len(dev) == 0 {
|
|
||||||
return fmt.Errorf("src or gw or dev name is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
rule := &netlink.Route{}
|
|
||||||
if dest == "default" {
|
|
||||||
dest = ""
|
|
||||||
}
|
|
||||||
if len(dest) != 0 {
|
|
||||||
rule.Dst, err = netlink.ParseIPNet(dest)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to parse dest %q of route rule", dest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(src) != 0 {
|
|
||||||
rule.Src = net.ParseIP(src)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to parse src ip")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(gw) != 0 {
|
|
||||||
rule.Gw = net.ParseIP(gw)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to parse gw ip")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nsutils.NsInvoke(nsPath,
|
|
||||||
func(nsFD int) error { return nil },
|
|
||||||
func(nsFD int) error {
|
|
||||||
// executed in container
|
|
||||||
if len(dev) != 0 {
|
|
||||||
ctrNic, err := netlink.LinkByName(dev)
|
|
||||||
if err != nil || ctrNic == nil {
|
|
||||||
return fmt.Errorf("failed to get link by name %q: %v", ctrNic, err)
|
|
||||||
}
|
|
||||||
rule.LinkIndex = ctrNic.Attrs().Index
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := netlink.RouteDel(rule); err != nil {
|
|
||||||
if strings.Contains(err.Error(), "no such process") {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return fmt.Errorf("failed to remove route: %v", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListRoutes will list all filterd network routes in contianer
|
|
||||||
func ListRoutes(ctr *container.Container, filter *types.Route) ([]*types.Route, error) {
|
|
||||||
if err := ctr.Lock(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer ctr.Unlock()
|
|
||||||
|
|
||||||
// create config file handler.
|
|
||||||
hConfig, err := hconfig.NewContainerConfig(ctr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer hConfig.Flush()
|
|
||||||
|
|
||||||
return hConfig.GetRoutes(filter), nil
|
|
||||||
}
|
|
||||||
167
main.go
167
main.go
@ -1,167 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: main funtion
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
// go base main package
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/reexec"
|
|
||||||
"isula.org/syscontainer-tools/config"
|
|
||||||
"isula.org/syscontainer-tools/utils"
|
|
||||||
|
|
||||||
_ "github.com/opencontainers/runc/libcontainer/nsenter"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
gitCommit = ""
|
|
||||||
version = ""
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
usage = `Enhanced tools for isulad`
|
|
||||||
syslogTag = "tools "
|
|
||||||
)
|
|
||||||
|
|
||||||
// fatal prints the error's details
|
|
||||||
// then exits the program with an exit status of 1.
|
|
||||||
func fatal(err error) {
|
|
||||||
// make sure the error is written to the logger
|
|
||||||
logrus.Error(err)
|
|
||||||
fmt.Fprintln(os.Stderr, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func fatalf(t string, v ...interface{}) {
|
|
||||||
fatal(fmt.Errorf(t, v...))
|
|
||||||
}
|
|
||||||
|
|
||||||
func mainWork() {
|
|
||||||
app := cli.NewApp()
|
|
||||||
app.Name = "syscontainer-tools"
|
|
||||||
app.Usage = usage
|
|
||||||
v := []string{
|
|
||||||
version,
|
|
||||||
}
|
|
||||||
if gitCommit != "" {
|
|
||||||
v = append(v, fmt.Sprintf("commit: %s", gitCommit))
|
|
||||||
}
|
|
||||||
app.Version = strings.Join(v, "\n")
|
|
||||||
app.Flags = []cli.Flag{
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "log",
|
|
||||||
Value: "/dev/null",
|
|
||||||
Usage: "set the log file path('.' stands for stdout)",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "log-level",
|
|
||||||
Value: "info",
|
|
||||||
Usage: "set the level for logging(debug, info, warn, error, fatal, panic)",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "log-format",
|
|
||||||
Value: "text",
|
|
||||||
Usage: "set the format used by logs ('text' or 'json')",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "syslog-service",
|
|
||||||
Value: "unix:///dev/log",
|
|
||||||
Usage: "set syslog service",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
app.Commands = []cli.Command{
|
|
||||||
addDevCommand,
|
|
||||||
addNicCommand,
|
|
||||||
addPathCommand,
|
|
||||||
addRouteCommand,
|
|
||||||
relabelCommand,
|
|
||||||
rmDevCommand,
|
|
||||||
rmNicCommand,
|
|
||||||
rmPathCommand,
|
|
||||||
rmRouteCommand,
|
|
||||||
listNicCommand,
|
|
||||||
listPathCommand,
|
|
||||||
listRouteCommand,
|
|
||||||
listDevCommand,
|
|
||||||
updateDevCommand,
|
|
||||||
updateNicCommand,
|
|
||||||
}
|
|
||||||
|
|
||||||
app.CommandNotFound = func(context *cli.Context, command string) {
|
|
||||||
fatalf("unknown subcommand %v", command)
|
|
||||||
}
|
|
||||||
|
|
||||||
app.Before = func(context *cli.Context) error {
|
|
||||||
if err := os.MkdirAll(config.IsuladToolsDir, 0666); err != nil {
|
|
||||||
logrus.Errorf("failed to set syscontainer-tools dir: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if logpath := context.GlobalString("log"); logpath != "" {
|
|
||||||
var logfile *os.File
|
|
||||||
var err error
|
|
||||||
if logpath == "." {
|
|
||||||
logfile = os.Stdout
|
|
||||||
} else {
|
|
||||||
logfile, err = os.OpenFile(logpath, os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_SYNC, 0600)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logrus.SetOutput(logfile)
|
|
||||||
}
|
|
||||||
|
|
||||||
lvl, err := logrus.ParseLevel(context.GlobalString("log-level"))
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatalf("unknown log-level %v", err)
|
|
||||||
}
|
|
||||||
logrus.SetLevel(lvl)
|
|
||||||
|
|
||||||
switch context.GlobalString("log-format") {
|
|
||||||
case "text":
|
|
||||||
logrus.SetFormatter(&logrus.TextFormatter{DisableColors: true})
|
|
||||||
case "json":
|
|
||||||
logrus.SetFormatter(new(logrus.JSONFormatter))
|
|
||||||
default:
|
|
||||||
fatalf("unknown log-format %s", context.GlobalString("log-format"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := utils.HookSyslog(context.GlobalString("syslog-service"), syslogTag); err != nil {
|
|
||||||
logrus.Errorf("failed to set syslog: %v", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := app.Run(os.Args); err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func main() {
|
|
||||||
if reexec.Init() {
|
|
||||||
// `reexec routine` was registered in syscontainer-tools/libdevice
|
|
||||||
// Sub nsenter process will come here.
|
|
||||||
// Isulad reexec package do not handle errors.
|
|
||||||
// And sub syscontainer-tools nsenter init process will send back the error message to parenet through pipe.
|
|
||||||
// So here do not need to handle errors.
|
|
||||||
return
|
|
||||||
}
|
|
||||||
signal.Ignore(syscall.SIGINT, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM)
|
|
||||||
mainWork()
|
|
||||||
}
|
|
||||||
358
network.go
358
network.go
@ -1,358 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: network interface commands
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
// go base main package
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
|
|
||||||
"github.com/docker/libnetwork/netutils"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"isula.org/syscontainer-tools/container"
|
|
||||||
"isula.org/syscontainer-tools/libnetwork"
|
|
||||||
"isula.org/syscontainer-tools/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
var addNicCommand = cli.Command{
|
|
||||||
Name: "add-nic",
|
|
||||||
Usage: "create a new network interface for container",
|
|
||||||
ArgsUsage: `<container_id>`,
|
|
||||||
Description: `This command is used to create a new network interface in an existing container,
|
|
||||||
and configure it as you wanted, then attach to specified bridge.
|
|
||||||
`,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "type",
|
|
||||||
Usage: "set network interface type (veth/eth)",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "name",
|
|
||||||
Usage: "set network interface name: [host:]<container>. for veth type, host could be unset and is random by default. for eth type, host is required",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "ip",
|
|
||||||
Usage: "set ip address. E.g. 172.17.28.2/24",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "mac",
|
|
||||||
Usage: "set mac address. E.g. 00:ff:48:23:e2:bb",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "bridge",
|
|
||||||
Usage: "set bridge name the network interface will attach to, for eth type, bridge cannot be set",
|
|
||||||
},
|
|
||||||
cli.IntFlag{
|
|
||||||
Name: "mtu",
|
|
||||||
Value: 1500,
|
|
||||||
Usage: "set mtu",
|
|
||||||
},
|
|
||||||
cli.IntFlag{
|
|
||||||
Name: "qlen",
|
|
||||||
Value: 1000,
|
|
||||||
Usage: "set qlen, 1000 by default",
|
|
||||||
},
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "update-config-only",
|
|
||||||
Usage: "If this flag is set, will not add network interface to container but update config only",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Action: func(context *cli.Context) {
|
|
||||||
if context.NArg() < 1 {
|
|
||||||
fatalf("%s: %q must accept a container-id", os.Args[0], context.Command.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctrName := strings.TrimSpace(context.Args()[0])
|
|
||||||
if len(ctrName) == 0 {
|
|
||||||
fatalf("container-id can't be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
if context.NArg() > 1 {
|
|
||||||
fatalf("Don't put container-id in the middle of options")
|
|
||||||
}
|
|
||||||
|
|
||||||
hostNicName, ctrNicName, err := parseNicName(context.String("name"))
|
|
||||||
if err != nil {
|
|
||||||
fatalf("failed to parse network name: %s", context.String("name"))
|
|
||||||
}
|
|
||||||
|
|
||||||
ctr, err := container.New(ctrName)
|
|
||||||
if err != nil {
|
|
||||||
fatalf("failed to get container info: %v", err)
|
|
||||||
}
|
|
||||||
if ctrNicName == "" {
|
|
||||||
fatalf("failed to get container nic name")
|
|
||||||
}
|
|
||||||
|
|
||||||
nicConf := &types.InterfaceConf{
|
|
||||||
IP: context.String("ip"),
|
|
||||||
Mac: context.String("mac"),
|
|
||||||
Mtu: context.Int("mtu"),
|
|
||||||
Type: context.String("type"),
|
|
||||||
Bridge: context.String("bridge"),
|
|
||||||
Qlen: context.Int("qlen"),
|
|
||||||
CtrNicName: ctrNicName,
|
|
||||||
HostNicName: hostNicName,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := types.ValidNetworkConfig(nicConf); err != nil {
|
|
||||||
fatalf("invalid network option: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if nicConf.HostNicName == "" {
|
|
||||||
nicConf.HostNicName = nicConf.CtrNicName
|
|
||||||
// interface name length must be less than 15
|
|
||||||
if len(nicConf.CtrNicName) > 9 {
|
|
||||||
nicConf.HostNicName = nicConf.CtrNicName[:9]
|
|
||||||
}
|
|
||||||
nicConf.HostNicName, err = netutils.GenerateRandomName(nicConf.HostNicName+"_", 5)
|
|
||||||
if err != nil {
|
|
||||||
fatalf("failed set host nic name %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := libnetwork.AddNic(ctr, nicConf, context.Bool("update-config-only")); err != nil {
|
|
||||||
fatalf("failed to add nic into container: %v", err)
|
|
||||||
}
|
|
||||||
logrus.Infof("add network interface to container %s successfully", ctrName)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var rmNicCommand = cli.Command{
|
|
||||||
Name: "remove-nic",
|
|
||||||
Usage: "remove a network interface from container",
|
|
||||||
ArgsUsage: `<container_id>`,
|
|
||||||
Description: `This command is used to remove a network interface from an existing container.
|
|
||||||
`,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "type",
|
|
||||||
Usage: "set network interface type (veth/eth)",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "name",
|
|
||||||
Usage: "set network interface name: [host:]<container>",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Action: func(context *cli.Context) {
|
|
||||||
if context.NArg() < 1 {
|
|
||||||
fatalf("%s: %q must accept a container-id", os.Args[0], context.Command.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctrName := strings.TrimSpace(context.Args()[0])
|
|
||||||
if len(ctrName) == 0 {
|
|
||||||
fatalf("container-id can't be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
if context.NArg() > 1 {
|
|
||||||
fatalf("Don't put container-id in the middle of options")
|
|
||||||
}
|
|
||||||
|
|
||||||
hostNicName, ctrNicName, err := parseNicName(context.String("name"))
|
|
||||||
if err != nil {
|
|
||||||
fatalf("failed to parse network name: %s", context.String("name"))
|
|
||||||
}
|
|
||||||
|
|
||||||
ctr, err := container.New(ctrName)
|
|
||||||
if err != nil {
|
|
||||||
fatalf("failed to get container info: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
nicConf := &types.InterfaceConf{
|
|
||||||
CtrNicName: ctrNicName,
|
|
||||||
HostNicName: hostNicName,
|
|
||||||
Type: context.String("type"),
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := libnetwork.DelNic(ctr, nicConf); err != nil {
|
|
||||||
fatalf("failed to remove nic from container: %v", err)
|
|
||||||
}
|
|
||||||
logrus.Infof("remove network interface from container %v successfully", ctrName)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseNicName(name string) (string, string, error) {
|
|
||||||
names := strings.SplitN(name, ":", 2)
|
|
||||||
if len(names) == 1 {
|
|
||||||
return "", name, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return names[0], names[1], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var updateNicCommand = cli.Command{
|
|
||||||
Name: "update-nic",
|
|
||||||
Usage: "update network interfaces in a container",
|
|
||||||
ArgsUsage: `<container_id>`,
|
|
||||||
Description: `This command is used to update network interfaces in an existing container.
|
|
||||||
`,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "name",
|
|
||||||
Usage: "network interface name in container will be updated, must be required",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "ip",
|
|
||||||
Usage: "set ip address. E.g. 172.17.28.2/24",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "mac",
|
|
||||||
Usage: "set mac address. E.g. 00:ff:48:23:e2:bb",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "bridge",
|
|
||||||
Usage: "set bridge name the network interface will attach to",
|
|
||||||
},
|
|
||||||
cli.IntFlag{
|
|
||||||
Name: "mtu",
|
|
||||||
Usage: "set mtu. 0 means keeping old value",
|
|
||||||
},
|
|
||||||
cli.IntFlag{
|
|
||||||
Name: "qlen",
|
|
||||||
Usage: "set qlen, works only on veth type. 0 means keeping old value",
|
|
||||||
},
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "update-config-only",
|
|
||||||
Usage: "If this flag is set, will not add network interface to container but update config only",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Action: func(context *cli.Context) {
|
|
||||||
if context.NArg() < 1 {
|
|
||||||
fatalf("%s: %q must accept a container-id", os.Args[0], context.Command.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctrName := strings.TrimSpace(context.Args()[0])
|
|
||||||
if len(ctrName) == 0 {
|
|
||||||
fatalf("container-id can't be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
if context.NArg() > 1 {
|
|
||||||
fatalf("Don't put container-id in the middle of options")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctr, err := container.New(ctrName)
|
|
||||||
if err != nil {
|
|
||||||
fatalf("failed to get container info: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctrNicName := context.String("name")
|
|
||||||
if ctrNicName == "" {
|
|
||||||
fatalf("Network interface name in container must be provided")
|
|
||||||
}
|
|
||||||
|
|
||||||
// we use qlen < 0 to tell qlen is not set by user, so set it to -1 here
|
|
||||||
var qlen int
|
|
||||||
if !context.IsSet("qlen") {
|
|
||||||
qlen = -1
|
|
||||||
} else {
|
|
||||||
qlen = context.Int("qlen")
|
|
||||||
}
|
|
||||||
|
|
||||||
nicConf := &types.InterfaceConf{
|
|
||||||
IP: context.String("ip"),
|
|
||||||
Mac: context.String("mac"),
|
|
||||||
Mtu: context.Int("mtu"),
|
|
||||||
Bridge: context.String("bridge"),
|
|
||||||
Qlen: qlen,
|
|
||||||
CtrNicName: ctrNicName,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := libnetwork.UpdateNic(ctr, nicConf, context.Bool("update-config-only")); err != nil {
|
|
||||||
fatalf("failed to upadte nic in container: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
logrus.Infof("update network interface in container %v successfully", ctrName)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var listNicCommand = cli.Command{
|
|
||||||
Name: "list-nic",
|
|
||||||
Usage: "list all network interfaces in a container",
|
|
||||||
ArgsUsage: `<container_id>`,
|
|
||||||
Description: `This command is used to list all network interfaces in an existing container.
|
|
||||||
`,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "pretty, p",
|
|
||||||
Usage: "If this flag is set, list nics in pretty json form",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "filter, f",
|
|
||||||
Usage: "Filter output based on conditions provided. E.g. '{\"ip\":\"1.2.3.4/24\", \"Mtu\":1500}'",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Action: func(context *cli.Context) {
|
|
||||||
if context.NArg() < 1 {
|
|
||||||
fatalf("%s: %q must accept a container-id", os.Args[0], context.Command.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctrName := strings.TrimSpace(context.Args()[0])
|
|
||||||
if len(ctrName) == 0 {
|
|
||||||
fatalf("container-id can't be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
if context.NArg() > 1 {
|
|
||||||
fatalf("Don't put container-id in the middle of options")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctr, err := container.New(ctrName)
|
|
||||||
if err != nil {
|
|
||||||
fatalf("failed to get container info: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
filterString := context.String("filter")
|
|
||||||
if filterString == "" {
|
|
||||||
filterString = "{}"
|
|
||||||
}
|
|
||||||
var filter = new(types.InterfaceConf)
|
|
||||||
if err = json.Unmarshal([]byte(filterString), filter); err != nil {
|
|
||||||
fatalf("failed to parse filter: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
nics, err := libnetwork.ListNic(ctr, filter)
|
|
||||||
if err != nil {
|
|
||||||
fatalf("failed to list nic in container: %v", err)
|
|
||||||
}
|
|
||||||
if nics == nil || len(nics) == 0 {
|
|
||||||
logrus.Infof("list network interface in container %q successfully", ctrName)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
nicData, err := json.Marshal(nics)
|
|
||||||
if err != nil {
|
|
||||||
fatalf("failed to Marshal nic config: %v", err)
|
|
||||||
}
|
|
||||||
nicBuffer := new(bytes.Buffer)
|
|
||||||
if _, err = nicBuffer.Write(nicData); err != nil {
|
|
||||||
fatalf("Buffer Write error %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if context.Bool("pretty") {
|
|
||||||
nicBuffer.Truncate(0)
|
|
||||||
if json.Indent(nicBuffer, nicData, "", "\t") != nil {
|
|
||||||
fatalf("failed to Indent nic data: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if _, err = nicBuffer.WriteString("\n"); err != nil {
|
|
||||||
fatalf("Buffer WriteString error %v", err)
|
|
||||||
}
|
|
||||||
if _, err = os.Stdout.Write(nicBuffer.Bytes()); err != nil {
|
|
||||||
logrus.Errorf("Write nicBuffer.Bytes error: %v", err)
|
|
||||||
}
|
|
||||||
logrus.Infof("list network interface in container %v successfully", ctrName)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
diff --git a/vendor/github.com/docker/docker/pkg/mount/flags.go b/vendor/github.com/docker/docker/pkg/mount/flags.go
|
|
||||||
index d2fb1fb..0f6fd61 100644
|
|
||||||
--- a/vendor/github.com/docker/docker/pkg/mount/flags.go
|
|
||||||
+++ b/vendor/github.com/docker/docker/pkg/mount/flags.go
|
|
||||||
@@ -90,3 +90,7 @@ func ParseTmpfsOptions(options string) (int, string, error) {
|
|
||||||
}
|
|
||||||
return flags, data, nil
|
|
||||||
}
|
|
||||||
+
|
|
||||||
+func ParseOptions(options string) (int, string) {
|
|
||||||
+ return parseOptions(options)
|
|
||||||
+}
|
|
||||||
@ -1,119 +0,0 @@
|
|||||||
diff --git a/vendor/github.com/opencontainers/runc/libcontainer/configs/config.go b/vendor/github.com/opencontainers/runc/libcontainer/configs/config.go
|
|
||||||
index 890cd7d..87cddc3 100644
|
|
||||||
--- a/vendor/github.com/opencontainers/runc/libcontainer/configs/config.go
|
|
||||||
+++ b/vendor/github.com/opencontainers/runc/libcontainer/configs/config.go
|
|
||||||
@@ -256,8 +256,12 @@ func (hooks Hooks) MarshalJSON() ([]byte, error) {
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
-// HookState is the payload provided to a hook on execution.
|
|
||||||
-type HookState specs.State
|
|
||||||
+type SpecState specs.State
|
|
||||||
+
|
|
||||||
+type HookState struct {
|
|
||||||
+ SpecState
|
|
||||||
+ Root string `json:"root"`
|
|
||||||
+}
|
|
||||||
|
|
||||||
type Hook interface {
|
|
||||||
// Run executes the hook with the provided state.
|
|
||||||
diff --git a/vendor/github.com/opencontainers/runc/libcontainer/container_linux.go b/vendor/github.com/opencontainers/runc/libcontainer/container_linux.go
|
|
||||||
index 28dff86..f4855f4 100644
|
|
||||||
--- a/vendor/github.com/opencontainers/runc/libcontainer/container_linux.go
|
|
||||||
+++ b/vendor/github.com/opencontainers/runc/libcontainer/container_linux.go
|
|
||||||
@@ -275,10 +275,13 @@ func (c *linuxContainer) start(process *Process, isInit bool) error {
|
|
||||||
|
|
||||||
if c.config.Hooks != nil {
|
|
||||||
s := configs.HookState{
|
|
||||||
- Version: c.config.Version,
|
|
||||||
- ID: c.id,
|
|
||||||
- Pid: parent.pid(),
|
|
||||||
- Bundle: utils.SearchLabels(c.config.Labels, "bundle"),
|
|
||||||
+ SpecState: configs.SpecState{
|
|
||||||
+ Version: c.config.Version,
|
|
||||||
+ ID: c.id,
|
|
||||||
+ Pid: parent.pid(),
|
|
||||||
+ Bundle: utils.SearchLabels(c.config.Labels, "bundle"),
|
|
||||||
+ },
|
|
||||||
+ Root: c.config.Rootfs,
|
|
||||||
}
|
|
||||||
for i, hook := range c.config.Hooks.Poststart {
|
|
||||||
if err := hook.Run(s); err != nil {
|
|
||||||
@@ -1144,10 +1147,13 @@ func (c *linuxContainer) criuNotifications(resp *criurpc.CriuResp, process *Proc
|
|
||||||
case notify.GetScript() == "setup-namespaces":
|
|
||||||
if c.config.Hooks != nil {
|
|
||||||
s := configs.HookState{
|
|
||||||
- Version: c.config.Version,
|
|
||||||
- ID: c.id,
|
|
||||||
- Pid: int(notify.GetPid()),
|
|
||||||
- Bundle: utils.SearchLabels(c.config.Labels, "bundle"),
|
|
||||||
+ SpecState: configs.SpecState{
|
|
||||||
+ Version: c.config.Version,
|
|
||||||
+ ID: c.id,
|
|
||||||
+ Pid: int(notify.GetPid()),
|
|
||||||
+ Bundle: utils.SearchLabels(c.config.Labels, "bundle"),
|
|
||||||
+ },
|
|
||||||
+ Root: c.config.Rootfs,
|
|
||||||
}
|
|
||||||
for i, hook := range c.config.Hooks.Prestart {
|
|
||||||
if err := hook.Run(s); err != nil {
|
|
||||||
diff --git a/vendor/github.com/opencontainers/runc/libcontainer/process_linux.go b/vendor/github.com/opencontainers/runc/libcontainer/process_linux.go
|
|
||||||
index 0f79a38..131e8e6 100644
|
|
||||||
--- a/vendor/github.com/opencontainers/runc/libcontainer/process_linux.go
|
|
||||||
+++ b/vendor/github.com/opencontainers/runc/libcontainer/process_linux.go
|
|
||||||
@@ -298,10 +298,13 @@ func (p *initProcess) start() error {
|
|
||||||
if !p.config.Config.Namespaces.Contains(configs.NEWNS) {
|
|
||||||
if p.config.Config.Hooks != nil {
|
|
||||||
s := configs.HookState{
|
|
||||||
- Version: p.container.config.Version,
|
|
||||||
- ID: p.container.id,
|
|
||||||
- Pid: p.pid(),
|
|
||||||
- Bundle: utils.SearchLabels(p.config.Config.Labels, "bundle"),
|
|
||||||
+ SpecState: configs.SpecState{
|
|
||||||
+ Version: p.container.config.Version,
|
|
||||||
+ ID: p.container.id,
|
|
||||||
+ Pid: p.pid(),
|
|
||||||
+ Bundle: utils.SearchLabels(p.config.Config.Labels, "bundle"),
|
|
||||||
+ },
|
|
||||||
+ Root: p.config.Config.Rootfs,
|
|
||||||
}
|
|
||||||
for i, hook := range p.config.Config.Hooks.Prestart {
|
|
||||||
if err := hook.Run(s); err != nil {
|
|
||||||
@@ -318,10 +321,13 @@ func (p *initProcess) start() error {
|
|
||||||
case procHooks:
|
|
||||||
if p.config.Config.Hooks != nil {
|
|
||||||
s := configs.HookState{
|
|
||||||
- Version: p.container.config.Version,
|
|
||||||
- ID: p.container.id,
|
|
||||||
- Pid: p.pid(),
|
|
||||||
- Bundle: utils.SearchLabels(p.config.Config.Labels, "bundle"),
|
|
||||||
+ SpecState: configs.SpecState{
|
|
||||||
+ Version: p.container.config.Version,
|
|
||||||
+ ID: p.container.id,
|
|
||||||
+ Pid: p.pid(),
|
|
||||||
+ Bundle: utils.SearchLabels(p.config.Config.Labels, "bundle"),
|
|
||||||
+ },
|
|
||||||
+ Root: p.config.Config.Rootfs,
|
|
||||||
}
|
|
||||||
for i, hook := range p.config.Config.Hooks.Prestart {
|
|
||||||
if err := hook.Run(s); err != nil {
|
|
||||||
diff --git a/vendor/github.com/opencontainers/runc/libcontainer/state_linux.go b/vendor/github.com/opencontainers/runc/libcontainer/state_linux.go
|
|
||||||
index 62878ac..9f8def2 100644
|
|
||||||
--- a/vendor/github.com/opencontainers/runc/libcontainer/state_linux.go
|
|
||||||
+++ b/vendor/github.com/opencontainers/runc/libcontainer/state_linux.go
|
|
||||||
@@ -58,9 +58,12 @@ func destroy(c *linuxContainer) error {
|
|
||||||
func runPoststopHooks(c *linuxContainer) error {
|
|
||||||
if c.config.Hooks != nil {
|
|
||||||
s := configs.HookState{
|
|
||||||
- Version: c.config.Version,
|
|
||||||
- ID: c.id,
|
|
||||||
- Bundle: utils.SearchLabels(c.config.Labels, "bundle"),
|
|
||||||
+ SpecState: configs.SpecState{
|
|
||||||
+ Version: c.config.Version,
|
|
||||||
+ ID: c.id,
|
|
||||||
+ Bundle: utils.SearchLabels(c.config.Labels, "bundle"),
|
|
||||||
+ },
|
|
||||||
+ Root: c.config.Rootfs,
|
|
||||||
}
|
|
||||||
for _, hook := range c.config.Hooks.Poststop {
|
|
||||||
if err := hook.Run(s); err != nil {
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
diff --git a/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go b/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go
|
|
||||||
index bd8e96a..68839d3 100644
|
|
||||||
--- a/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go
|
|
||||||
+++ b/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go
|
|
||||||
@@ -29,6 +29,16 @@ type Spec struct {
|
|
||||||
Windows *Windows `json:"windows,omitempty" platform:"windows"`
|
|
||||||
}
|
|
||||||
|
|
||||||
+type CompatSpec struct {
|
|
||||||
+ Spec
|
|
||||||
+ Process CompatProcess `json:"process"`
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+type CompatProcess struct {
|
|
||||||
+ Process
|
|
||||||
+ Capabilities []string `json:"capabilities,omitempty" platform:"linux"`
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
// Process contains information to start a specific application inside the container.
|
|
||||||
type Process struct {
|
|
||||||
// Terminal creates an interactive terminal for the container.
|
|
||||||
@ -1,65 +0,0 @@
|
|||||||
diff --git a/vendor/github.com/opencontainers/runc/libcontainer/configs/config.go b/vendor/github.com/opencontainers/runc/libcontainer/configs/config.go
|
|
||||||
index 87cddc3..c4e89ac 100644
|
|
||||||
--- a/vendor/github.com/opencontainers/runc/libcontainer/configs/config.go
|
|
||||||
+++ b/vendor/github.com/opencontainers/runc/libcontainer/configs/config.go
|
|
||||||
@@ -7,7 +7,7 @@ import (
|
|
||||||
"os/exec"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
- "github.com/Sirupsen/logrus"
|
|
||||||
+ "github.com/sirupsen/logrus"
|
|
||||||
"github.com/opencontainers/runtime-spec/specs-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
diff --git a/vendor/github.com/opencontainers/runc/libcontainer/container_linux.go b/vendor/github.com/opencontainers/runc/libcontainer/container_linux.go
|
|
||||||
index f4855f4..9c949fd 100644
|
|
||||||
--- a/vendor/github.com/opencontainers/runc/libcontainer/container_linux.go
|
|
||||||
+++ b/vendor/github.com/opencontainers/runc/libcontainer/container_linux.go
|
|
||||||
@@ -17,7 +17,7 @@ import (
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
- "github.com/Sirupsen/logrus"
|
|
||||||
+ "github.com/sirupsen/logrus"
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
|
||||||
"github.com/opencontainers/runc/libcontainer/configs"
|
|
||||||
diff --git a/vendor/github.com/opencontainers/runc/libcontainer/init_linux.go b/vendor/github.com/opencontainers/runc/libcontainer/init_linux.go
|
|
||||||
index 39b83a4..6d7958b 100644
|
|
||||||
--- a/vendor/github.com/opencontainers/runc/libcontainer/init_linux.go
|
|
||||||
+++ b/vendor/github.com/opencontainers/runc/libcontainer/init_linux.go
|
|
||||||
@@ -14,7 +14,7 @@ import (
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
- "github.com/Sirupsen/logrus"
|
|
||||||
+ "github.com/sirupsen/logrus"
|
|
||||||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
|
||||||
"github.com/opencontainers/runc/libcontainer/configs"
|
|
||||||
"github.com/opencontainers/runc/libcontainer/system"
|
|
||||||
diff --git a/vendor/github.com/opencontainers/runc/libcontainer/rootfs_linux.go b/vendor/github.com/opencontainers/runc/libcontainer/rootfs_linux.go
|
|
||||||
index 2635fd6..7b4fc00 100644
|
|
||||||
--- a/vendor/github.com/opencontainers/runc/libcontainer/rootfs_linux.go
|
|
||||||
+++ b/vendor/github.com/opencontainers/runc/libcontainer/rootfs_linux.go
|
|
||||||
@@ -539,7 +539,7 @@ func getMountInfo(mountinfo []*mount.Info, dir string) *mount.Info {
|
|
||||||
func getParentMount(rootfs string) (string, string, error) {
|
|
||||||
var path string
|
|
||||||
|
|
||||||
- mountinfos, err := mount.GetMounts()
|
|
||||||
+ mountinfos, err := mount.GetMounts(nil)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
diff --git a/vendor/github.com/opencontainers/runc/libcontainer/state_linux.go b/vendor/github.com/opencontainers/runc/libcontainer/state_linux.go
|
|
||||||
index 9f8def2..f548dfb 100644
|
|
||||||
--- a/vendor/github.com/opencontainers/runc/libcontainer/state_linux.go
|
|
||||||
+++ b/vendor/github.com/opencontainers/runc/libcontainer/state_linux.go
|
|
||||||
@@ -8,7 +8,7 @@ import (
|
|
||||||
"path/filepath"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
- "github.com/Sirupsen/logrus"
|
|
||||||
+ "github.com/sirupsen/logrus"
|
|
||||||
"github.com/opencontainers/runc/libcontainer/configs"
|
|
||||||
"github.com/opencontainers/runc/libcontainer/utils"
|
|
||||||
)
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
diff --git a/github.com/opencontainers/runc/libcontainer/rootfs_linux.go b/vendor/github.com/opencontainers/runc/libcontainer/rootfs_linux.go
|
|
||||||
index 7b4fc00..2635fd6 100644
|
|
||||||
--- a/github.com/opencontainers/runc/libcontainer/rootfs_linux.go
|
|
||||||
+++ b/vendor/github.com/opencontainers/runc/libcontainer/rootfs_linux.go
|
|
||||||
@@ -539,7 +539,7 @@ func getMountInfo(mountinfo []*mount.Info, dir string) *mount.Info {
|
|
||||||
func getParentMount(rootfs string) (string, string, error) {
|
|
||||||
var path string
|
|
||||||
|
|
||||||
- mountinfos, err := mount.GetMounts(nil)
|
|
||||||
+ mountinfos, err := mount.GetMounts()
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
@ -1,78 +0,0 @@
|
|||||||
/******************************************************************************
|
|
||||||
* Copyright (c) Huawei Technologies Co., Ltd. 2017-2019. All rights reserved.
|
|
||||||
* syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
* You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
* http://license.coscl.org.cn/MulanPSL2
|
|
||||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
* PURPOSE.
|
|
||||||
* See the Mulan PSL v2 for more details.
|
|
||||||
* Author: zhangwentao
|
|
||||||
* Create: 2017-07-19
|
|
||||||
* Description: config net device tx/sg/tso on
|
|
||||||
******************************************************************************/
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <endian.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <linux/sockios.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <net/if.h>
|
|
||||||
#include <linux/netlink.h>
|
|
||||||
#include <linux/ethtool.h>
|
|
||||||
|
|
||||||
int setNetDeviceTSO(int fd, char* name, int on)
|
|
||||||
{
|
|
||||||
struct ifreq ifr = {0};
|
|
||||||
struct ethtool_value eval = {0};
|
|
||||||
int ret;
|
|
||||||
eval.data = on;
|
|
||||||
eval.cmd = ETHTOOL_STSO;
|
|
||||||
|
|
||||||
strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name) - 1);
|
|
||||||
ifr.ifr_data = (void*)&eval;
|
|
||||||
ret = ioctl(fd, SIOCETHTOOL, &ifr);
|
|
||||||
if (ret < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int setNetDeviceSG(int fd, char* name, int on)
|
|
||||||
{
|
|
||||||
struct ifreq ifr = {0};
|
|
||||||
struct ethtool_value eval = {0};
|
|
||||||
int ret;
|
|
||||||
eval.data = on;
|
|
||||||
eval.cmd = ETHTOOL_SSG;
|
|
||||||
|
|
||||||
strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name) - 1);
|
|
||||||
ifr.ifr_data = (void*)&eval;
|
|
||||||
ret = ioctl(fd, SIOCETHTOOL, &ifr);
|
|
||||||
if (ret < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int setNetDeviceTX(int fd, char* name, int on)
|
|
||||||
{
|
|
||||||
struct ifreq ifr = {0};
|
|
||||||
struct ethtool_value eval = {0};
|
|
||||||
int ret;
|
|
||||||
eval.data = on;
|
|
||||||
eval.cmd = ETHTOOL_STXCSUM;
|
|
||||||
|
|
||||||
strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name) - 1);
|
|
||||||
ifr.ifr_data = (void*)&eval;
|
|
||||||
ret = ioctl(fd, SIOCETHTOOL, &ifr);
|
|
||||||
if (ret < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@ -1,111 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: ethtool for network interface
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package ethtool
|
|
||||||
|
|
||||||
/*
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "ethtool.h"
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Ethtool ethtool interface
|
|
||||||
type Ethtool interface {
|
|
||||||
SetNetDeviceTSO(on bool) error
|
|
||||||
SetNetDeviceTX(on bool) error
|
|
||||||
SetNetDeviceSG(on bool) error
|
|
||||||
Close() error
|
|
||||||
}
|
|
||||||
|
|
||||||
type ethtool struct {
|
|
||||||
fd int
|
|
||||||
name string
|
|
||||||
sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewEthtool init ethtool
|
|
||||||
func NewEthtool(name string) (Ethtool, error) {
|
|
||||||
fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, 0)
|
|
||||||
if err != nil {
|
|
||||||
fd, err = syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_GENERIC)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return ðtool{fd: fd, name: name}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (etool *ethtool) SetNetDeviceTSO(on bool) error {
|
|
||||||
etool.Lock()
|
|
||||||
defer etool.Unlock()
|
|
||||||
|
|
||||||
var cOn C.int
|
|
||||||
if on {
|
|
||||||
cOn = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
cName := C.CString(etool.name)
|
|
||||||
ret := C.setNetDeviceTSO(C.int(etool.fd), cName, cOn)
|
|
||||||
C.free(unsafe.Pointer(cName))
|
|
||||||
if ret != 0 {
|
|
||||||
return fmt.Errorf("setNetDeviceTso return error: %d", ret)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (etool *ethtool) SetNetDeviceTX(on bool) error {
|
|
||||||
etool.Lock()
|
|
||||||
defer etool.Unlock()
|
|
||||||
|
|
||||||
var cOn C.int
|
|
||||||
if on {
|
|
||||||
cOn = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
cName := C.CString(etool.name)
|
|
||||||
ret := C.setNetDeviceTX(C.int(etool.fd), cName, cOn)
|
|
||||||
C.free(unsafe.Pointer(cName))
|
|
||||||
if ret != 0 {
|
|
||||||
return fmt.Errorf("setNetDeviceTso return error: %d", ret)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (etool *ethtool) SetNetDeviceSG(on bool) error {
|
|
||||||
etool.Lock()
|
|
||||||
defer etool.Unlock()
|
|
||||||
|
|
||||||
var cOn C.int
|
|
||||||
if on {
|
|
||||||
cOn = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
cName := C.CString(etool.name)
|
|
||||||
ret := C.setNetDeviceSG(C.int(etool.fd), cName, cOn)
|
|
||||||
C.free(unsafe.Pointer(cName))
|
|
||||||
if ret != 0 {
|
|
||||||
return fmt.Errorf("setNetDeviceTso return error: %d", ret)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (etool *ethtool) Close() error {
|
|
||||||
return syscall.Close(etool.fd)
|
|
||||||
}
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
/******************************************************************************
|
|
||||||
* Copyright (c) Huawei Technologies Co., Ltd. 2017-2019. All rights reserved.
|
|
||||||
* syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
* You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
* http://license.coscl.org.cn/MulanPSL2
|
|
||||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
* PURPOSE.
|
|
||||||
* See the Mulan PSL v2 for more details.
|
|
||||||
* Author: zhangwentao
|
|
||||||
* Create: 2017-07-19
|
|
||||||
* Description: config net device tx/sg/tso on
|
|
||||||
******************************************************************************/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef __ETHTOOL_H
|
|
||||||
#define __ETHTOOL_H
|
|
||||||
|
|
||||||
extern int setNetDeviceTSO(int fd, char* name, int on);
|
|
||||||
extern int setNetDeviceTX(int fd, char* name, int on);
|
|
||||||
extern int setNetDeviceSG(int fd, char* name, int on);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: recursive unmount
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package mount
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
docker_mount "github.com/docker/docker/pkg/mount"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RecursiveUnmount unmounts the target and all mounts underneath, starting with
|
|
||||||
// the deepsest mount first.
|
|
||||||
func RecursiveUnmount(target string) error {
|
|
||||||
mounts, err := docker_mount.GetMounts()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make the deepest mount be first
|
|
||||||
sort.Sort(sort.Reverse(byMountpoint(mounts)))
|
|
||||||
|
|
||||||
for i, m := range mounts {
|
|
||||||
if !strings.HasPrefix(m.Mountpoint, target) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := docker_mount.Unmount(m.Mountpoint); err != nil && i == len(mounts)-1 {
|
|
||||||
if mounted, err := docker_mount.Mounted(m.Mountpoint); err != nil || mounted {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Ignore errors for submounts and continue trying to unmount others
|
|
||||||
// The final unmount should fail if there ane any submounts remaining
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@ -1,117 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: mount operation
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package mount
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
docker_mount "github.com/docker/docker/pkg/mount"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Mount is mount operation
|
|
||||||
func Mount(device, target, mType, options string) error {
|
|
||||||
flagint, data := docker_mount.ParseOptions(options)
|
|
||||||
flag := uintptr(flagint)
|
|
||||||
// propagation option
|
|
||||||
propagationFlags := (uintptr)(syscall.MS_SLAVE | syscall.MS_SHARED | syscall.MS_UNBINDABLE | syscall.MS_PRIVATE)
|
|
||||||
|
|
||||||
if err := syscall.Mount(device, target, mType, flag&^propagationFlags, data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have a bind mount or remount, remount...
|
|
||||||
if flag&syscall.MS_BIND == syscall.MS_BIND && flag&syscall.MS_RDONLY == syscall.MS_RDONLY {
|
|
||||||
return syscall.Mount(device, target, mType, flag|syscall.MS_REMOUNT, data)
|
|
||||||
}
|
|
||||||
if flag&propagationFlags != 0 {
|
|
||||||
return syscall.Mount("none", target, "none", flag&propagationFlags, data)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmount is unmount operation
|
|
||||||
func Unmount(target string) error {
|
|
||||||
return syscall.Unmount(target, syscall.MNT_DETACH)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidMountPropagation checks propagation of path
|
|
||||||
func ValidMountPropagation(path, mOpt string) error {
|
|
||||||
var bind, slave, shared bool
|
|
||||||
var slavemnt, sharedmnt bool
|
|
||||||
for _, opt := range strings.Split(mOpt, ",") {
|
|
||||||
if opt == "bind" {
|
|
||||||
bind = true
|
|
||||||
}
|
|
||||||
if opt == "shared" {
|
|
||||||
shared = true
|
|
||||||
}
|
|
||||||
if opt == "slave" {
|
|
||||||
slave = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !bind {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
source, options, err := getSource(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, opt := range strings.Split(options, " ") {
|
|
||||||
if strings.HasPrefix(opt, "shared:") {
|
|
||||||
sharedmnt = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(opt, "master:") {
|
|
||||||
slavemnt = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if shared && !sharedmnt {
|
|
||||||
return fmt.Errorf("Path %s is mounted on %s but it is not a shared mount", path, source)
|
|
||||||
}
|
|
||||||
if slave && !sharedmnt && !slavemnt {
|
|
||||||
return fmt.Errorf("Path %s is mounted on %s but it is not a shared or slave mount", path, source)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getSource(sourcepath string) (string, string, error) {
|
|
||||||
path, err := filepath.EvalSymlinks(sourcepath)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
mountinfos, err := docker_mount.GetMounts()
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
for _, m := range mountinfos {
|
|
||||||
if m.Mountpoint == path {
|
|
||||||
return path, m.Optional, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if path == "/" {
|
|
||||||
return "", "", fmt.Errorf("Could not find mount %s", sourcepath)
|
|
||||||
}
|
|
||||||
path = filepath.Dir(path)
|
|
||||||
}
|
|
||||||
return "", "", fmt.Errorf("Unexpected error in getMouont")
|
|
||||||
}
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: mount point compare implement
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package mount
|
|
||||||
|
|
||||||
import docker_mount "github.com/docker/docker/pkg/mount"
|
|
||||||
|
|
||||||
type byMountpoint []*docker_mount.Info
|
|
||||||
|
|
||||||
func (by byMountpoint) Len() int {
|
|
||||||
return len(by)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (by byMountpoint) Less(i, j int) bool {
|
|
||||||
return by[i].Mountpoint < by[j].Mountpoint
|
|
||||||
}
|
|
||||||
|
|
||||||
func (by byMountpoint) Swap(i, j int) {
|
|
||||||
by[i], by[j] = by[j], by[i]
|
|
||||||
}
|
|
||||||
@ -1,128 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: udevd rules
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package udevd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"fmt"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func usingUdevd() (bool, error) {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func reloadConfig() error {
|
|
||||||
_, err := exec.Command("udevadm", "control", "--reload").CombinedOutput()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func saveRules(path string, rules []*Rule) error {
|
|
||||||
f, err := os.Create(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
if err := f.Chmod(0600); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
f.WriteString("## This File is auto-generated by syscontainer-tools.\n")
|
|
||||||
f.WriteString("## DO NOT EDIT IT\n\n")
|
|
||||||
for _, r := range rules {
|
|
||||||
if _, err := f.WriteString(fmt.Sprintf("%s\n", r.ToUdevRuleString())); err != nil {
|
|
||||||
logrus.Errorf("f.WriteString err: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := f.Sync(); err != nil {
|
|
||||||
logrus.Errorf("f.WriteString err: %s", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadRules(path string) ([]*Rule, error) {
|
|
||||||
var rules []*Rule
|
|
||||||
f, err := os.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
// if non-existing, just return empty rules array
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return rules, nil
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
logrus.Infof("Start load rules from path: %s", path)
|
|
||||||
scanner := bufio.NewScanner(f)
|
|
||||||
for scanner.Scan() {
|
|
||||||
text := strings.TrimLeft(scanner.Text(), " ")
|
|
||||||
|
|
||||||
// ignore the comment line
|
|
||||||
if strings.HasPrefix(text, "#") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
array := strings.Split(text, ",")
|
|
||||||
|
|
||||||
var (
|
|
||||||
name string
|
|
||||||
ctrDevName string
|
|
||||||
containerID string
|
|
||||||
)
|
|
||||||
|
|
||||||
for _, tag := range array {
|
|
||||||
tag = strings.TrimLeft(tag, " ")
|
|
||||||
tag = strings.TrimRight(tag, " ")
|
|
||||||
|
|
||||||
// Parse device name from 'KERNEL' segment
|
|
||||||
if strings.HasPrefix(tag, "KERNEL") {
|
|
||||||
ar := strings.Split(tag, "\"")
|
|
||||||
if len(ar) >= 2 { // Minimum value for get split tag
|
|
||||||
sub := ar[1]
|
|
||||||
name = sub[:len(sub)-1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse Container ID, major, minor number from 'RUN' command
|
|
||||||
if strings.HasPrefix(tag, "RUN") {
|
|
||||||
ar := strings.Split(tag, "\"")
|
|
||||||
if len(ar) >= 2 { // Minimum value for get split tag
|
|
||||||
sub := ar[1]
|
|
||||||
cmdArray := strings.Split(sub, " ")
|
|
||||||
if len(cmdArray) >= 6 { // Minimum value for get split sub
|
|
||||||
containerID = cmdArray[2]
|
|
||||||
ctrDevName = cmdArray[4]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO: failed to parse some rules, ignore it??
|
|
||||||
if name == "" || containerID == "" || ctrDevName == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
rules = append(rules, &Rule{
|
|
||||||
Name: filepath.Join("/dev", name),
|
|
||||||
CtrDevName: ctrDevName,
|
|
||||||
Container: containerID,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
logrus.Infof("Finish load rules from path: %s", path)
|
|
||||||
return rules, nil
|
|
||||||
}
|
|
||||||
@ -1,172 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: udevd controller
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package udevd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
hconfig "isula.org/syscontainer-tools/config"
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
programe = "/lib/udev/syscontainer-tools_wrapper"
|
|
||||||
lockFile = "udevd_config_locker"
|
|
||||||
configPath = "/etc/udev/rules.d/99-syscontainer-tools.rules"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Rule defines an udev rule which used to capture the partition udev event
|
|
||||||
type Rule struct {
|
|
||||||
Name string
|
|
||||||
CtrDevName string
|
|
||||||
Container string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToUdevRuleString will format the Rule structure to udev rule
|
|
||||||
func (r *Rule) ToUdevRuleString() string {
|
|
||||||
return fmt.Sprintf("KERNEL==\"%s*\",ACTION==\"add|remove\", ENV{DEVTYPE}==\"partition\", SUBSYSTEM==\"block\", RUN{program}+=\"%s $env{ACTION} %s $name %s %s\"",
|
|
||||||
filepath.Base(r.Name), programe, r.TrimContainerID(), r.CtrDevName, filepath.Base(r.Name))
|
|
||||||
}
|
|
||||||
|
|
||||||
// TrimContainerID will return container id for short
|
|
||||||
func (r *Rule) TrimContainerID() string {
|
|
||||||
return r.Container[:8]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Controller is the interface which to manage udev rules
|
|
||||||
type Controller interface {
|
|
||||||
Lock() error
|
|
||||||
Unlock() error
|
|
||||||
LoadRules() error
|
|
||||||
AddRule(r *Rule)
|
|
||||||
RemoveRule(r *Rule)
|
|
||||||
ToDisk() error
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewUdevdController will return an UdevController interface
|
|
||||||
func NewUdevdController() Controller {
|
|
||||||
using, err := usingUdevd()
|
|
||||||
if err != nil {
|
|
||||||
return &udevdController{useUdevd: false}
|
|
||||||
}
|
|
||||||
return &udevdController{
|
|
||||||
dirty: false,
|
|
||||||
useUdevd: using,
|
|
||||||
configFile: configPath,
|
|
||||||
lockFile: filepath.Join(hconfig.IsuladToolsDir, lockFile),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type udevdController struct {
|
|
||||||
useUdevd bool
|
|
||||||
configFile string
|
|
||||||
lockFile string
|
|
||||||
rules []*Rule
|
|
||||||
dirty bool
|
|
||||||
lock *os.File
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lock uses filelock to lock the udev rule file.
|
|
||||||
// to make sure only one process could access the resource.
|
|
||||||
func (sc *udevdController) Lock() error {
|
|
||||||
if !sc.useUdevd {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
f, err := os.OpenFile(sc.lockFile, os.O_RDONLY|os.O_CREATE, 0600)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// FileLock will be released at 3 conditions:
|
|
||||||
// 1. process to unlock manully.
|
|
||||||
// 2. Close the opened fd.
|
|
||||||
// 3. process died without call unlock. kernel will close the file and release the lock.
|
|
||||||
// LOCK_EX means only one process could lock it at one time.
|
|
||||||
// LOCK_NB is not set, using block mode.
|
|
||||||
if err := unix.Flock(int(f.Fd()), unix.LOCK_EX); err != nil {
|
|
||||||
f.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
sc.lock = f
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unlock will release the file lock
|
|
||||||
func (sc *udevdController) Unlock() error {
|
|
||||||
if !sc.useUdevd || sc.lock == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
defer sc.lock.Close()
|
|
||||||
return unix.Flock(int(sc.lock.Fd()), unix.LOCK_UN)
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadRules loads the udev rules from rule config file
|
|
||||||
func (sc *udevdController) LoadRules() error {
|
|
||||||
if !sc.useUdevd {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
rules, err := loadRules(sc.configFile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
sc.rules = rules
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddRule will add a rule to manager in memory only
|
|
||||||
func (sc *udevdController) AddRule(r *Rule) {
|
|
||||||
if !sc.useUdevd {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, rule := range sc.rules {
|
|
||||||
if r.Name == rule.Name && r.CtrDevName == rule.CtrDevName && r.TrimContainerID() == rule.TrimContainerID() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sc.dirty = true
|
|
||||||
sc.rules = append(sc.rules, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveRule will add a rule to manager in memory only
|
|
||||||
func (sc *udevdController) RemoveRule(r *Rule) {
|
|
||||||
if !sc.useUdevd {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for index, rule := range sc.rules {
|
|
||||||
if r.Name == rule.Name && r.CtrDevName == rule.CtrDevName && r.TrimContainerID() == rule.TrimContainerID() {
|
|
||||||
sc.dirty = true
|
|
||||||
sc.rules = append(sc.rules[:index], sc.rules[index+1:]...)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToDisk will save the rules to udev rule config file
|
|
||||||
func (sc *udevdController) ToDisk() error {
|
|
||||||
if !sc.useUdevd || !sc.dirty {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := saveRules(sc.configFile, sc.rules); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return reloadConfig()
|
|
||||||
}
|
|
||||||
140
relabel.go
140
relabel.go
@ -1,140 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: selinux relabel commands
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
// go base main package
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os/exec"
|
|
||||||
|
|
||||||
"isula.org/syscontainer-tools/utils"
|
|
||||||
|
|
||||||
"github.com/opencontainers/runc/libcontainer/selinux"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// Seconfig is absolute path for SELinux config file
|
|
||||||
Seconfig = "/etc/selinux/config"
|
|
||||||
hostSystemd = "/lib/systemd/systemd"
|
|
||||||
)
|
|
||||||
|
|
||||||
type selinuxCommand struct {
|
|
||||||
cmd string
|
|
||||||
argv []string
|
|
||||||
}
|
|
||||||
|
|
||||||
type selinuxContext struct {
|
|
||||||
conType string
|
|
||||||
path string
|
|
||||||
}
|
|
||||||
|
|
||||||
func relabelIsuladBinary(path, bin string) error {
|
|
||||||
cmd := exec.Command("semanage", []string{"fcontext", "-a", "-t", "init_exec_t", fmt.Sprintf("%s/%s", path, bin)}...)
|
|
||||||
if err := cmd.Start(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func restartIsulad() error {
|
|
||||||
restart := selinuxCommand{"systemctl", []string{"restart", "isulad"}}
|
|
||||||
cmd := exec.Command(restart.cmd, restart.argv...)
|
|
||||||
logrus.Infof("%s %v", restart.cmd, restart.argv)
|
|
||||||
if err := cmd.Run(); err != nil {
|
|
||||||
logrus.Errorf("%s %v: %v", restart.cmd, restart.argv, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func relabel(path string) error {
|
|
||||||
var seType string
|
|
||||||
var attr string
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if seType, err = utils.SeconfigGet(Seconfig, "SELINUXTYPE"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
utils.SeconfigSet(Seconfig, "SELINUX", "permissive")
|
|
||||||
fileContexts := fmt.Sprintf("/etc/selinux/%s/contexts/files/file_contexts.local", seType)
|
|
||||||
if !utils.IsExist(fileContexts) {
|
|
||||||
if err = ioutil.WriteFile(fileContexts, []byte(""), 0600); err != nil {
|
|
||||||
logrus.Errorf("ioutil.WriteFile err: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
preStarts := []selinuxCommand{
|
|
||||||
{"setenforce", []string{"0"}},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, prestart := range preStarts {
|
|
||||||
cmd := exec.Command(prestart.cmd, prestart.argv...)
|
|
||||||
logrus.Infof("%s %v", prestart.cmd, prestart.argv)
|
|
||||||
if err := cmd.Run(); err != nil {
|
|
||||||
logrus.Errorf("%s %v: %v", prestart.cmd, prestart.argv, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if attr, err = selinux.Getfilecon(hostSystemd); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
modifyContexts := []selinuxContext{
|
|
||||||
{"init_exec_t", path + "/isulad"},
|
|
||||||
}
|
|
||||||
con := utils.NewContext(attr)
|
|
||||||
for _, context := range modifyContexts {
|
|
||||||
con.SetType(context.conType)
|
|
||||||
logrus.Infof("%s [%s]", context.path, con.Get())
|
|
||||||
selinux.Setfilecon(context.path, con.Get())
|
|
||||||
}
|
|
||||||
|
|
||||||
return restartIsulad()
|
|
||||||
}
|
|
||||||
|
|
||||||
func setUpSelinuxLabel(path, rootfs string) error {
|
|
||||||
if err := relabel(path); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var relabelCommand = cli.Command{
|
|
||||||
Name: "relabel",
|
|
||||||
Usage: "relabel rootfs for running SELinux in system container",
|
|
||||||
ArgsUsage: `[--isulad-path path] [--rootfs rootfs]`,
|
|
||||||
Description: `relabel rootfs for running SELinux in system container(a systemd based os is required)`,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "isulad-path",
|
|
||||||
Value: "/usr/bin",
|
|
||||||
Usage: "isulad's install path",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "rootfs",
|
|
||||||
Usage: "the absolute path for isulad's rootfs",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Action: func(context *cli.Context) {
|
|
||||||
if err := setUpSelinuxLabel(context.String("isulad-path"), context.String("rootfs")); err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
// print result to stdout
|
|
||||||
fmt.Printf("SELinux relabel success\n")
|
|
||||||
logrus.Infof("SELinux relabel successfully")
|
|
||||||
},
|
|
||||||
}
|
|
||||||
205
route.go
205
route.go
@ -1,205 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: route commands
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
// go base main package
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"isula.org/syscontainer-tools/container"
|
|
||||||
"isula.org/syscontainer-tools/libnetwork"
|
|
||||||
"isula.org/syscontainer-tools/types"
|
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
type routeGroup []*types.Route
|
|
||||||
|
|
||||||
var addRouteCommand = cli.Command{
|
|
||||||
Name: "add-route",
|
|
||||||
Usage: "add a new network route rule into container",
|
|
||||||
ArgsUsage: `<container_id> [{rule1}{rule2}]`,
|
|
||||||
Description: `This command is used to add a new route rule into the container,
|
|
||||||
rule example:
|
|
||||||
'[{"dest":"default", "gw":"192.168.10.1"},{"dest":"100.10.0.0/16","dev":"eth0","src":"1.1.1.2"}]' .
|
|
||||||
* dest: dest network, empty means default gateway
|
|
||||||
* src: route src ip
|
|
||||||
* gw: route gw
|
|
||||||
* dev: network device.
|
|
||||||
`,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "update-config-only",
|
|
||||||
Usage: "If this flag is set, will not add the route table to container but update config only.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Action: func(context *cli.Context) {
|
|
||||||
if context.NArg() < 2 {
|
|
||||||
fatalf("%s: %q must accept container-id and rules", os.Args[0], context.Command.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctrName := strings.TrimSpace(context.Args()[0])
|
|
||||||
if len(ctrName) == 0 {
|
|
||||||
fatalf("container-id can't be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
rules := strings.TrimSpace(context.Args()[1])
|
|
||||||
if len(rules) == 0 {
|
|
||||||
fatalf("rule can't be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
rg := make(routeGroup, 0)
|
|
||||||
if err := json.Unmarshal([]byte(rules), &rg); err != nil {
|
|
||||||
fatalf("malformed rule format: %v", err)
|
|
||||||
}
|
|
||||||
ctr, err := container.New(ctrName)
|
|
||||||
if err != nil {
|
|
||||||
fatalf("failed to get container info: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := libnetwork.AddRoutes(ctr, rg, context.Bool("update-config-only")); err != nil {
|
|
||||||
fatalf("failed to add route: %v", err)
|
|
||||||
}
|
|
||||||
logrus.Infof("add route to container %q successfully", ctrName)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var rmRouteCommand = cli.Command{
|
|
||||||
Name: "remove-route",
|
|
||||||
Usage: "remove a network route rule from container",
|
|
||||||
ArgsUsage: `<container_id> [{rule1}{rule2}]`,
|
|
||||||
Description: `This command is used to remove route rules from the container,
|
|
||||||
rule example:
|
|
||||||
'[{"dest":"default", "gw":"192.168.10.1"},{"dest":"100.10.0.0/16","dev":"eth0","src":"1.1.1.2"}]' .
|
|
||||||
* dest: dest network, empty means default gateway
|
|
||||||
* src: route src ip
|
|
||||||
* gw: route gw
|
|
||||||
* dev: network device.
|
|
||||||
`,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "update-config-only",
|
|
||||||
Usage: "If this flag is set, will not del the route table from container but update config only.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Action: func(context *cli.Context) {
|
|
||||||
if context.NArg() < 2 {
|
|
||||||
fatalf("%s: %q must accept container-id and rules", os.Args[0], context.Command.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctrName := strings.TrimSpace(context.Args()[0])
|
|
||||||
if len(ctrName) == 0 {
|
|
||||||
fatalf("container-id can't be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
rules := strings.TrimSpace(context.Args()[1])
|
|
||||||
if len(rules) == 0 {
|
|
||||||
fatalf("rule can't be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
rg := make(routeGroup, 0)
|
|
||||||
if err := json.Unmarshal([]byte(rules), &rg); err != nil {
|
|
||||||
fatalf("malformed rule format: %v", err)
|
|
||||||
}
|
|
||||||
ctr, err := container.New(ctrName)
|
|
||||||
if err != nil {
|
|
||||||
fatalf("failed to get container info: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := libnetwork.DelRoutes(ctr, rg, context.Bool("update-config-only")); err != nil {
|
|
||||||
fatalf("failed to remove route: %v", err)
|
|
||||||
}
|
|
||||||
logrus.Infof("remove route from container %q successfully", ctrName)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var listRouteCommand = cli.Command{
|
|
||||||
Name: "list-route",
|
|
||||||
Usage: "list all filterd network route rules in container",
|
|
||||||
ArgsUsage: `<container_id>`,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "pretty, p",
|
|
||||||
Usage: "If this flag is set, list routes in pretty json form",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "filter, f",
|
|
||||||
Usage: "Filter output based on conditions provided. E.g. '{\"dest\":\"1.1.1.0/24\", \"gw\":\"10.1.1.254\"}'",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Action: func(context *cli.Context) {
|
|
||||||
if context.NArg() < 1 {
|
|
||||||
fatalf("%s: %q must accept container-id", os.Args[0], context.Command.Name)
|
|
||||||
}
|
|
||||||
if context.NArg() > 1 {
|
|
||||||
fatalf("Don't put container-id in the middle of options")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctrName := strings.TrimSpace(context.Args()[0])
|
|
||||||
if len(ctrName) == 0 {
|
|
||||||
fatalf("container-id can't be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
filterStr := strings.TrimSpace(context.String("filter"))
|
|
||||||
if len(filterStr) == 0 {
|
|
||||||
filterStr = "{}"
|
|
||||||
}
|
|
||||||
|
|
||||||
filter := new(types.Route)
|
|
||||||
if err := json.Unmarshal([]byte(filterStr), filter); err != nil {
|
|
||||||
fatalf("malformed filter format: %v", err)
|
|
||||||
}
|
|
||||||
ctr, err := container.New(ctrName)
|
|
||||||
if err != nil {
|
|
||||||
fatalf("failed to get container info: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
routes, err := libnetwork.ListRoutes(ctr, filter)
|
|
||||||
if err != nil {
|
|
||||||
fatalf("failed to get list routes: %v", err)
|
|
||||||
}
|
|
||||||
if routes == nil || len(routes) == 0 {
|
|
||||||
logrus.Infof("list route in container %q successfully", ctrName)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
routeData, err := json.Marshal(routes)
|
|
||||||
if err != nil {
|
|
||||||
fatalf("failed to Marshal route config: %v", err)
|
|
||||||
}
|
|
||||||
routeBuffer := new(bytes.Buffer)
|
|
||||||
if _, err = routeBuffer.Write(routeData); err != nil {
|
|
||||||
fatalf("Buffer Write error %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if context.Bool("pretty") {
|
|
||||||
routeBuffer.Truncate(0)
|
|
||||||
if json.Indent(routeBuffer, routeData, "", "\t") != nil {
|
|
||||||
fatalf("failed to Indent route data: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = routeBuffer.WriteString("\n"); err != nil {
|
|
||||||
fatalf("Buffer WriteString error %v", err)
|
|
||||||
}
|
|
||||||
if _, err = os.Stdout.Write(routeBuffer.Bytes()); err != nil {
|
|
||||||
logrus.Errorf("Write routeBuffer error %v", err)
|
|
||||||
}
|
|
||||||
logrus.Infof("list route in container %q successfully", ctrName)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
patch/0001-fix-docker-pkg-mount.patch
|
|
||||||
patch/0002-runc-1.0.0-rc3-libcontainer.patch
|
|
||||||
patch/0003-runc-1.0.0-rc3-runtime-spec.patch
|
|
||||||
patch/0004-runc-libcontainer-sirupsen-logrus.patch
|
|
||||||
patch/0005-fix-docker-pkg-mount.patch
|
|
||||||
BIN
syscontainer-tools-0.9.37.tar.gz
Normal file
BIN
syscontainer-tools-0.9.37.tar.gz
Normal file
Binary file not shown.
@ -3,12 +3,12 @@
|
|||||||
|
|
||||||
#Basic Information
|
#Basic Information
|
||||||
Name: syscontainer-tools
|
Name: syscontainer-tools
|
||||||
Version: v0.9
|
Version: 0.9.37
|
||||||
Release: 37
|
Release: 1
|
||||||
Summary: syscontainer tools for IT, work with iSulad
|
Summary: syscontainer tools for IT, work with iSulad
|
||||||
License: Mulan PSL v2
|
License: Mulan PSL v2
|
||||||
URL: https://gitee.com/src-openeuler/syscontainer-tools
|
URL: https://gitee.com/openeuler/syscontainer-tools
|
||||||
Source0: %{name}-src.tar.gz
|
Source0: %{name}-%{version}.tar.gz
|
||||||
BuildRoot: %{_tmppath}/%{name}-root
|
BuildRoot: %{_tmppath}/%{name}-root
|
||||||
|
|
||||||
#Dependency
|
#Dependency
|
||||||
@ -22,7 +22,7 @@ This is syscontainer tools, to make it work, you need a isulad and util-linux
|
|||||||
|
|
||||||
#Build sections
|
#Build sections
|
||||||
%prep
|
%prep
|
||||||
%setup -q -c -n src/isula.org/syscontainer-tools
|
%setup -n %{name} -q
|
||||||
|
|
||||||
%build
|
%build
|
||||||
make init && make
|
make init && make
|
||||||
@ -108,8 +108,14 @@ chmod 0640 ${HOOK_SPEC}/hookspec.json
|
|||||||
rm -rfv %{buildroot}
|
rm -rfv %{buildroot}
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
* Tue Apr 27 2020 zhangtianyang <zhangtianyang2@huawei.com> - 0.9.37
|
* Tue Apr 26 2020 Zhangsong<zhangsong34@huawei.com> - 0.9.37-1
|
||||||
- update license to Mulan PSL v2
|
- release version 0.9.37
|
||||||
|
|
||||||
|
* Tue Apr 26 2020 Zhangsong<zhangsong34@huawei.com> - 0.9.37
|
||||||
|
- Type:enhancement
|
||||||
|
- ID:NA
|
||||||
|
- SUG:restart
|
||||||
|
- DESC:update to Mulan PSL V2
|
||||||
|
|
||||||
* Tue Jan 07 2020 Zhangsong<zhangsong34@huawei.com> - 0.9.35
|
* Tue Jan 07 2020 Zhangsong<zhangsong34@huawei.com> - 0.9.35
|
||||||
- Type:enhancement
|
- Type:enhancement
|
||||||
3
syscontainer-tools.yaml
Normal file
3
syscontainer-tools.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
version_control: gitee
|
||||||
|
src_repo: openEuler/syscontainer-tools
|
||||||
@ -1,138 +0,0 @@
|
|||||||
# Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
# syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
# You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
# http://license.coscl.org.cn/MulanPSL2
|
|
||||||
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
# PURPOSE.
|
|
||||||
# See the Mulan PSL v2 for more details.
|
|
||||||
# Description: test mount mutiple direct
|
|
||||||
# Author: zhangwei
|
|
||||||
# Create: 2018-01-18
|
|
||||||
|
|
||||||
#!/bin/bash
|
|
||||||
#test mount mutiple direct
|
|
||||||
|
|
||||||
. $CUR/env.sh
|
|
||||||
. $CUR/tools.sh
|
|
||||||
|
|
||||||
TEST_NAME="test_devices_many"
|
|
||||||
test_001(){
|
|
||||||
container_ID=`isula run --name one --hook-spec /var/lib/isulad/hooks/hookspec.json -d $UBUNTU_IMAGE bash -c "sleep 10000"`
|
|
||||||
container_status $container_ID
|
|
||||||
if [ "${status}x" != "runningx" ]; then
|
|
||||||
fail $TEST_NAME "01:FAIL"
|
|
||||||
fi
|
|
||||||
container_ID=`isula ps | grep one | awk '{print $1}'`
|
|
||||||
$ISULAD_TOOLS add-device $container_ID $DEV_SDA1:/dev/sda1:rw $DEV_SDA2:/dev/sda2:rw > /dev/null
|
|
||||||
out=`isula exec one bash -c "ls /dev/sda1"`
|
|
||||||
if [ "$out" == "/dev/sda1" ]; then
|
|
||||||
success $TEST_NAME "01-1:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "01-1:FAIL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
out=`isula exec one bash -c "ls /dev/sda2"`
|
|
||||||
if [ "$out" == "/dev/sda2" ]; then
|
|
||||||
success $TEST_NAME "01-2:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "01-2:FAIL"
|
|
||||||
fi
|
|
||||||
#test remove-device
|
|
||||||
$ISULAD_TOOLS remove-device $container_ID $DEV_SDA1:/dev/sda1:rw /dev/zero:/dev/sda2:rw > /dev/null
|
|
||||||
isula exec one bash -c "ls /dev/sda1 && /dev/sda2" >&$TEST_FOLDER/ab.txt
|
|
||||||
out=`cat $TEST_FOLDER/ab.txt`
|
|
||||||
out=${out##*:}
|
|
||||||
out=${out%%or*}
|
|
||||||
if [ "$out" == " No such file " ]; then
|
|
||||||
success $TEST_NAME "01-3:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "01-3:FAIL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
#test mount different dirct to container of the same direct
|
|
||||||
$ISULAD_TOOLS add-device $container_ID $DEV_SDA1:/dev/sda1:rw /dev/zero:/dev/sda1:rw>& $TEST_FOLDER/ab.txt
|
|
||||||
out=`cat $TEST_FOLDER/ab.txt | awk -F: 'END{print $1}'`
|
|
||||||
if [ "$out" == "Failed to add device" ]; then
|
|
||||||
success $TEST_NAME "01-4:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "01-4:FAIL"
|
|
||||||
fi
|
|
||||||
isula rm -f one > /dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
test_002(){
|
|
||||||
#test Multiple container mount the same direct
|
|
||||||
container_ID1=`isula run --name one --hook-spec /var/lib/isulad/hooks/hookspec.json -d $UBUNTU_IMAGE bash -c "sleep 10000"`
|
|
||||||
container_status $container_ID1
|
|
||||||
if [ "${status}x" != "runningx" ]; then
|
|
||||||
fail $TEST_NAME "02:FAIL"
|
|
||||||
fi
|
|
||||||
container_ID2=`isula run --name two --hook-spec /var/lib/isulad/hooks/hookspec.json -d $UBUNTU_IMAGE bash -c "sleep 10000"`
|
|
||||||
container_status $container_ID2
|
|
||||||
if [ "${status}x" != "runningx" ]; then
|
|
||||||
fail $TEST_NAME "021:FAIL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
out=`$ISULAD_TOOLS add-device $container_ID1 $DEV_SDA1:/dev/sda1:rw > /dev/null`
|
|
||||||
out=`isula exec one bash -c "ls /dev/sda1"`
|
|
||||||
if [ "$out" != "/dev/sda1" ]; then
|
|
||||||
fail $TEST_NAME "02-1:FAIL"
|
|
||||||
else
|
|
||||||
success $TEST_NAME "02-1:PASS"
|
|
||||||
fi
|
|
||||||
|
|
||||||
out=`$ISULAD_TOOLS add-device $container_ID2 $DEV_SDA1:/dev/sda1:rw > /dev/null`
|
|
||||||
out1=`isula exec two bash -c "ls /dev/sda1"`
|
|
||||||
if [ "$out1" != "/dev/sda1" ]; then
|
|
||||||
fail $TEST_NAME "02-2:FAIL"
|
|
||||||
else
|
|
||||||
success $TEST_NAME "02-2:PASS"
|
|
||||||
fi
|
|
||||||
|
|
||||||
$ISULAD_TOOLS remove-device $container_ID1 $DEV_SDA1:/dev/sda1:rw > /dev/null
|
|
||||||
isula exec $container_ID1 bash -c "ls /dev/sda1" > /dev/null 2>&1
|
|
||||||
out=`echo $?`
|
|
||||||
if [ $out -eq 0 ];then
|
|
||||||
fail $TEST_NAME "02-3:FAIL"
|
|
||||||
else
|
|
||||||
success $TEST_NAME "02-3:PASS"
|
|
||||||
fi
|
|
||||||
|
|
||||||
out1=`isula exec two bash -c "ls /dev/sda1"`
|
|
||||||
if [ "$out1" == "/dev/sda1" ]; then
|
|
||||||
success $TEST_NAME "02-4:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "02-4:FAIL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
#test stop start
|
|
||||||
isula stop two > /dev/null
|
|
||||||
container_status $container_ID2
|
|
||||||
if [ "${status}x" != "exitedx" ]; then
|
|
||||||
fail $TEST_NAME "02-5:FAIL"
|
|
||||||
fi
|
|
||||||
isula start two > /dev/null
|
|
||||||
container_status $container_ID2
|
|
||||||
if [ "${status}x" != "runningx" ]; then
|
|
||||||
fail $TEST_NAME "02-6:FAIL"
|
|
||||||
fi
|
|
||||||
out1=`isula exec two bash -c "ls /dev/sda1"`
|
|
||||||
if [ "$out1" == "/dev/sda1" ]; then
|
|
||||||
success $TEST_NAME "02-7:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "02-7:FAIL"
|
|
||||||
fi
|
|
||||||
isula rm -f one > /dev/null
|
|
||||||
isula rm -f two > /dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
main(){
|
|
||||||
test_001
|
|
||||||
test_002
|
|
||||||
}
|
|
||||||
|
|
||||||
main
|
|
||||||
@ -1,191 +0,0 @@
|
|||||||
# Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
# syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
# You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
# http://license.coscl.org.cn/MulanPSL2
|
|
||||||
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
# PURPOSE.
|
|
||||||
# See the Mulan PSL v2 for more details.
|
|
||||||
# Description: device tests
|
|
||||||
# Author: zhangwei
|
|
||||||
# Create: 2018-01-18
|
|
||||||
|
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
. $CUR/env.sh
|
|
||||||
. $CUR/tools.sh
|
|
||||||
|
|
||||||
TEST_NAME="test_devices"
|
|
||||||
|
|
||||||
test_001(){
|
|
||||||
# test add-device.
|
|
||||||
out=`isula run --name one --hook-spec /var/lib/isulad/hooks/hookspec.json -d $UBUNTU_IMAGE bash -c "sleep 100000"`
|
|
||||||
container_status $out
|
|
||||||
if [ "${status}x" != "runningx" ]; then
|
|
||||||
fail $TEST_NAME "01:FAIL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
$ISULAD_TOOLS add-device $out $DEV_SDA1:/dev/sda1:rw> /dev/null
|
|
||||||
out1=`isula exec one sh -c "ls /dev/sda1"`
|
|
||||||
if [ "$out1" == "/dev/sda1" ]; then
|
|
||||||
success $TEST_NAME "01-1:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "01-1:FAIL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
#test remove-device
|
|
||||||
$ISULAD_TOOLS remove-device $out $DEV_SDA1:/dev/sda1:rwm > /dev/null
|
|
||||||
out1=`isula exec one sh -c "ls -l /dev/sda1" > /dev/null 2>&1`
|
|
||||||
if [ "$out1" == "" ]; then
|
|
||||||
success $TEST_NAME "01-2:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "01-2:FAIL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
isula rm -f one > /dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
test_002(){
|
|
||||||
#test exited container
|
|
||||||
out=`isula run --name one --hook-spec /var/lib/isulad/hooks/hookspec.json -d $UBUNTU_IMAGE`
|
|
||||||
sleep 3
|
|
||||||
container_status $out
|
|
||||||
if [ "${status}x" != "exitedx" ]; then
|
|
||||||
fail $TEST_NAME "02:FAIL" $out $status
|
|
||||||
fi
|
|
||||||
$ISULAD_TOOLS add-device $out $DEV_SDA1:/dev/sda1:rwm >&`pwd`/ab.txt
|
|
||||||
out1=`cat ab.txt | awk -F: 'END{print $1}'`
|
|
||||||
if [ "$out1" == "Failed to add device" ]; then
|
|
||||||
success $TEST_NAME "02-1:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "02-1:FAIL"
|
|
||||||
fi
|
|
||||||
isula rm one > /dev/null
|
|
||||||
rm -f ab.txt >/dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
test_003(){
|
|
||||||
#test created container
|
|
||||||
out=`isula create --name one --hook-spec /var/lib/isulad/hooks/hookspec.json -ti $UBUNTU_IMAGE`
|
|
||||||
container_status $out
|
|
||||||
if [ "${status}x" != "createdx" ]; then
|
|
||||||
fail $TEST_NAME "03:FAIL"
|
|
||||||
fi
|
|
||||||
$ISULAD_TOOLS add-device $out $DEV_SDA1:/dev/sda1:rwm >&`pwd`/ab.txt
|
|
||||||
out1=`cat ab.txt | awk -F: 'END{print $1}'`
|
|
||||||
if [ "$out1" == "Failed to add device" ]; then
|
|
||||||
success $TEST_NAME "03-1:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "03-1:FAIL"
|
|
||||||
fi
|
|
||||||
rm -f ab.txt > /dev/null
|
|
||||||
|
|
||||||
#created->up container
|
|
||||||
isula start one > /dev/null
|
|
||||||
out=`isula ps | grep one | awk '{print $1}'`
|
|
||||||
out1=`$ISULAD_TOOLS add-device $out $DEV_SDA1:/dev/sda1:rwm`
|
|
||||||
out1=`isula exec one sh -c "ls /dev/sda1"`
|
|
||||||
|
|
||||||
if [ "$out1" == "/dev/sda1" ]; then
|
|
||||||
success $TEST_NAME "03-2:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "03-2:FAIL"
|
|
||||||
fi
|
|
||||||
isula rm -f one > /dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
test_004(){
|
|
||||||
#test r
|
|
||||||
out=`isula run --name one --hook-spec /var/lib/isulad/hooks/hookspec.json -d $UBUNTU_IMAGE bash -c "sleep 10000"`
|
|
||||||
container_status $out
|
|
||||||
if [ "${status}x" != "runningx" ]; then
|
|
||||||
fail $TEST_NAME "04:FAIL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
$ISULAD_TOOLS add-device $out $DEV_SDA1:/dev/sda1:r > /dev/null
|
|
||||||
out1=`isula exec one sh -c "ls /dev/sda1"`
|
|
||||||
if [ "$out1" != "/dev/sda1" ]; then
|
|
||||||
fail $TEST_NAME "04-1:FAIL"
|
|
||||||
else
|
|
||||||
success $TEST_NAME "04-1:PASS"
|
|
||||||
fi
|
|
||||||
$ISULAD_TOOLS remove-device $out $DEV_SDA1:/dev/sda1:r > /dev/null
|
|
||||||
out=`isula exec one sh -c "ls /dev/sda1" > /dev/null 2>&1`
|
|
||||||
if [ "$out" == "" ]; then
|
|
||||||
success $TEST_NAME "04-2:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "04-2:FAIL"
|
|
||||||
fi
|
|
||||||
rm -rf ab.txt > /dev/null
|
|
||||||
|
|
||||||
#test rw
|
|
||||||
out=`isula ps | grep one | awk '{print $1}'`
|
|
||||||
$ISULAD_TOOLS add-device $out $DEV_SDA1:/dev/sda1:rw > /dev/null
|
|
||||||
out=`isula exec one sh -c "ls /dev/sda1"`
|
|
||||||
if [ "$out" != "/dev/sda1" ]; then
|
|
||||||
fail $TEST_NAME "04-3:FAIL"
|
|
||||||
else
|
|
||||||
success $TEST_NAME "04-3:PASS"
|
|
||||||
fi
|
|
||||||
isula exec one bash -c "dd if=/dev/sda1 of=/dev/null bs=1M count=10" >&`pwd`/ab.txt
|
|
||||||
out=`cat ab.txt | awk -F',' 'END{print $1}'`
|
|
||||||
out=`echo $out | awk -F ' ' '{print $1}'`
|
|
||||||
if [ "$out" == "10485760" ]; then
|
|
||||||
success $TEST_NAME "04-4:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "04-4:FAIL"
|
|
||||||
fi
|
|
||||||
isula rm -f one > /dev/null
|
|
||||||
rm -f ab.txt > /dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
test_006(){
|
|
||||||
#test not exist device
|
|
||||||
out=`isula run --name one --hook-spec /var/lib/isulad/hooks/hookspec.json -d $UBUNTU_IMAGE bash -c "sleep 100000"`
|
|
||||||
container_status $out
|
|
||||||
if [ "${status}x" == "runningx" ]; then
|
|
||||||
success $TEST_NAME "06:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "06:FAIL"
|
|
||||||
fi
|
|
||||||
$ISULAD_TOOLS add-device $out $DEV_NOT_EXIST:/dev/sda1:rw >&`pwd`/ab.txt
|
|
||||||
out=`cat ab.txt | awk -F: 'END{print $1}'`
|
|
||||||
if [ "$out" == "Failed to parse device" ]; then
|
|
||||||
success $TEST_NAME "06-1:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "06-1:FAIL"
|
|
||||||
fi
|
|
||||||
rm -f ab.txt > /dev/null
|
|
||||||
isula rm -f one > /dev/null
|
|
||||||
|
|
||||||
#test no r w
|
|
||||||
out=`isula run --name one --hook-spec /var/lib/isulad/hooks/hookspec.json -d $UBUNTU_IMAGE bash -c "sleep 100000"`
|
|
||||||
container_status $out
|
|
||||||
if [ "${status}x" != "runningx" ]; then
|
|
||||||
fail $TEST_NAME "06-2:FAIL"
|
|
||||||
else
|
|
||||||
success $TEST_NAME "06-2:PASS"
|
|
||||||
fi
|
|
||||||
$ISULAD_TOOLS add-device $out $DEV_SDA:/dev/sda > /dev/null
|
|
||||||
isula exec one bash -c "dd if=/dev/sda of=/dev/null bs=1M count=10" >&$TMP/ab.txt
|
|
||||||
out=`cat $TMP/ab.txt | awk -F',' 'END{print $1}'`
|
|
||||||
out=`echo $out | awk -F ' ' '{print $1}'`
|
|
||||||
if [ "$out" == "10485760" ]; then
|
|
||||||
success $TEST_NAME "06-3:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "06-3:FAIL"
|
|
||||||
fi
|
|
||||||
isula rm -f one > /dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
main(){
|
|
||||||
test_001
|
|
||||||
test_002
|
|
||||||
test_003
|
|
||||||
test_004
|
|
||||||
test_006
|
|
||||||
}
|
|
||||||
|
|
||||||
main
|
|
||||||
32
test/env.sh
32
test/env.sh
@ -1,32 +0,0 @@
|
|||||||
# Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
# syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
# You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
# http://license.coscl.org.cn/MulanPSL2
|
|
||||||
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
# PURPOSE.
|
|
||||||
# See the Mulan PSL v2 for more details.
|
|
||||||
# Description: env tests
|
|
||||||
# Author: zhangwei
|
|
||||||
# Create: 2018-01-18
|
|
||||||
|
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
## syscontainer-tools paths.
|
|
||||||
ISULAD_TOOLS="$CUR/../build/syscontainer-tools"
|
|
||||||
|
|
||||||
## Ubuntu image
|
|
||||||
UBUNTU_IMAGE="ubuntu"
|
|
||||||
|
|
||||||
## busybox image:
|
|
||||||
BUSYBOX_IMAGE="busybox"
|
|
||||||
|
|
||||||
## tmp directory:
|
|
||||||
TMP=$CUR/tmpdir
|
|
||||||
|
|
||||||
## block device:
|
|
||||||
DEV_SDA=/dev/sda
|
|
||||||
DEV_SDA1=/dev/sda
|
|
||||||
DEV_SDA2=/dev/zero
|
|
||||||
DEV_NOT_EXIST=/dev/not_exist_at_all
|
|
||||||
76
test/main.sh
76
test/main.sh
@ -1,76 +0,0 @@
|
|||||||
# Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
# syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
# You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
# http://license.coscl.org.cn/MulanPSL2
|
|
||||||
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
# PURPOSE.
|
|
||||||
# See the Mulan PSL v2 for more details.
|
|
||||||
# Description: main test
|
|
||||||
# Author: zhangwei
|
|
||||||
# Create: 2018-01-18
|
|
||||||
|
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
|
|
||||||
## current directory:
|
|
||||||
export CUR=$(cd `dirname $0`; pwd)
|
|
||||||
. $CUR/env.sh
|
|
||||||
|
|
||||||
declare -i total_cases=0
|
|
||||||
declare -i failed_cases=0
|
|
||||||
declare -i success_cases=0
|
|
||||||
export total_cases
|
|
||||||
export failed_cases
|
|
||||||
export success_cases
|
|
||||||
|
|
||||||
setupImage(){
|
|
||||||
declare -a Images=($UBUNTU_IMAGE $BUSYBOX_IMAGE)
|
|
||||||
for img in "${Images[@]}";
|
|
||||||
do
|
|
||||||
out=`isula images | grep $img`
|
|
||||||
if [ "x$out" = "x" ]; then
|
|
||||||
echo "Image [" $img "] does not exist, pull it from hub."
|
|
||||||
isula pull $img
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
setup_device_hook(){
|
|
||||||
mkdir -p /var/lib/isulad/hooks
|
|
||||||
cp $CUR/../hooks/syscontainer-hooks/example/hookspec.json /var/lib/isulad/hooks/
|
|
||||||
cp $CUR/../build/syscontainer-hooks /var/lib/isulad/hooks/
|
|
||||||
}
|
|
||||||
|
|
||||||
main_test(){
|
|
||||||
. $CUR/devices_test.sh
|
|
||||||
. $CUR/devices_many_test.sh
|
|
||||||
. $CUR/path_test.sh
|
|
||||||
. $CUR/path_many_test.sh
|
|
||||||
. $CUR/network_test.sh
|
|
||||||
. $CUR/route_test.sh
|
|
||||||
}
|
|
||||||
|
|
||||||
report(){
|
|
||||||
echo "============ Result =========="
|
|
||||||
echo "total cases :" $total_cases
|
|
||||||
echo "failed cases :" $failed_cases
|
|
||||||
echo "success cases:" $success_cases
|
|
||||||
}
|
|
||||||
|
|
||||||
main(){
|
|
||||||
mkdir -p $TMP
|
|
||||||
setupImage
|
|
||||||
setup_device_hook
|
|
||||||
main_test
|
|
||||||
rm -rf $TMP
|
|
||||||
|
|
||||||
# report the result
|
|
||||||
report
|
|
||||||
exit $failed_cases
|
|
||||||
}
|
|
||||||
|
|
||||||
main
|
|
||||||
@ -1,121 +0,0 @@
|
|||||||
# Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
# syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
# You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
# http://license.coscl.org.cn/MulanPSL2
|
|
||||||
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
# PURPOSE.
|
|
||||||
# See the Mulan PSL v2 for more details.
|
|
||||||
# Description: network test
|
|
||||||
# Author: zhangwei
|
|
||||||
# Create: 2018-01-18
|
|
||||||
|
|
||||||
#!/bin/bash
|
|
||||||
############################################################################
|
|
||||||
#
|
|
||||||
#This script is to test
|
|
||||||
#
|
|
||||||
############################################################################
|
|
||||||
|
|
||||||
. $CUR/env.sh
|
|
||||||
. $CUR/tools.sh
|
|
||||||
|
|
||||||
test_001(){
|
|
||||||
#testcase01
|
|
||||||
CONTAINER_ID=`isula run -d $BUSYBOX_IMAGE top`
|
|
||||||
$ISULAD_TOOLS --debug --log $TMP/syscontainer-tools.log add-nic \
|
|
||||||
--type veth --name eth10 --ip 192.168.182.2/24 \
|
|
||||||
--mac "aa:bb:cc:dd:ee:aa" --bridge "docker0" --mtu 1350 \
|
|
||||||
$CONTAINER_ID
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
fail $TEST_NAME "01-1:FAIL"
|
|
||||||
else
|
|
||||||
success $TEST_NAME "01-1:PASS"
|
|
||||||
fi
|
|
||||||
|
|
||||||
out=`isula exec $CONTAINER_ID ip a s eth10`
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
fail $TEST_NAME "01-2:FAIL"
|
|
||||||
else
|
|
||||||
success $TEST_NAME "01-2:PASS"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo $out | grep "192.168.182.2/24" > /dev/null 2>&1
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
fail $TEST_NAME "01-3:FAIL"
|
|
||||||
else
|
|
||||||
success $TEST_NAME "01-3:PASS"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo $out | grep "aa:bb:cc:dd:ee:aa" > /dev/null 2>&1
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
fail $TEST_NAME "01-4:FAIL"
|
|
||||||
else
|
|
||||||
success $TEST_NAME "01-4:PASS"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo $out | grep "1350" > /dev/null 2>&1
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
fail $TEST_NAME "01-5:FAIL"
|
|
||||||
else
|
|
||||||
success $TEST_NAME "01-5:PASS"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# check if the bridge contains veth nic
|
|
||||||
brctl show docker0 | grep -E "veth[a-z0-9]{10}" > /dev/null 2>&1
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
fail $TEST_NAME "01-6:FAIL"
|
|
||||||
else
|
|
||||||
success $TEST_NAME "01-6:PASS"
|
|
||||||
fi
|
|
||||||
isula rm -f $CONTAINER_ID > /dev/null 2>&1
|
|
||||||
}
|
|
||||||
|
|
||||||
test_002(){
|
|
||||||
# testcase02
|
|
||||||
# test ovs bridge
|
|
||||||
OVS_BR=test_ovs_bridge
|
|
||||||
ovs-vsctl --if-exists del-br $OVS_BR
|
|
||||||
ovs-vsctl add-br $OVS_BR
|
|
||||||
ovs-vsctl br-exists $OVS_BR
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
fail "02-1:FAIL"
|
|
||||||
fi
|
|
||||||
CONTAINER_ID=`isula run -d $BUSYBOX_IMAGE top`
|
|
||||||
$ISULAD_TOOLS --debug --log $TMP/syscontainer-tools.log add-nic \
|
|
||||||
--type veth --name eth11 --ip 192.168.182.2/24 \
|
|
||||||
--mac "aa:bb:cc:dd:ee:aa" --bridge $OVS_BR --mtu 1350 \
|
|
||||||
$CONTAINER_ID
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
fail $TEST_NAME "02-1:FAIL"
|
|
||||||
else
|
|
||||||
success $TEST_NAME "02-1:PASS"
|
|
||||||
fi
|
|
||||||
|
|
||||||
out=`isula exec $CONTAINER_ID ip a s eth11`
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
fail $TEST_NAME "02-2:FAIL"
|
|
||||||
else
|
|
||||||
success $TEST_NAME "02-2:PASS"
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
# check if the bridge contains veth nic
|
|
||||||
ovs-vsctl list-ports $OVS_BR | grep -E "veth[a-z0-9]{10}" > /dev/null 2>&1
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
fail $TEST_NAME "02-3:FAIL"
|
|
||||||
else
|
|
||||||
success $TEST_NAME "02-3:PASS"
|
|
||||||
fi
|
|
||||||
isula rm -f $CONTAINER_ID > /dev/null 2>&1
|
|
||||||
ovs-vsctl --if-exists del-br $OVS_BR
|
|
||||||
}
|
|
||||||
|
|
||||||
main(){
|
|
||||||
test_001
|
|
||||||
test_002
|
|
||||||
}
|
|
||||||
|
|
||||||
main
|
|
||||||
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
# Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
# syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
# You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
# http://license.coscl.org.cn/MulanPSL2
|
|
||||||
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
# PURPOSE.
|
|
||||||
# See the Mulan PSL v2 for more details.
|
|
||||||
# Description: network wrapper
|
|
||||||
# Author: zhangwei
|
|
||||||
# Create: 2018-01-18
|
|
||||||
|
|
||||||
#!/bin/bash
|
|
||||||
ip addr add 10.0.0.100/24 dev mynet1
|
|
||||||
ip link set mynet1 up
|
|
||||||
ping -c 5 10.0.0.100
|
|
||||||
ping -c 5 10.0.0.1
|
|
||||||
@ -1,199 +0,0 @@
|
|||||||
# Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
# syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
# You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
# http://license.coscl.org.cn/MulanPSL2
|
|
||||||
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
# PURPOSE.
|
|
||||||
# See the Mulan PSL v2 for more details.
|
|
||||||
# Description: test mount mutiple direct
|
|
||||||
# Author: zhangwei
|
|
||||||
# Create: 2018-01-18
|
|
||||||
|
|
||||||
#!/bin/bash
|
|
||||||
#test mount mutiple direct
|
|
||||||
|
|
||||||
. $CUR/env.sh
|
|
||||||
. $CUR/tools.sh
|
|
||||||
|
|
||||||
TEST_NAME="test_path_many"
|
|
||||||
|
|
||||||
test_001(){
|
|
||||||
container_ID=`isula run --name one --hook-spec /var/lib/isulad/hooks/hookspec.json -d $UBUNTU_IMAGE bash -c "sleep 10000"`
|
|
||||||
container_status $container_ID
|
|
||||||
if [ "${status}x" != "runningx" ]; then
|
|
||||||
fail $TEST_NAME "01:FAIL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
container_ID=`isula ps | grep one | awk '{print $1}'`
|
|
||||||
|
|
||||||
TEST_FOLDER1=$TMP/$TEST_NAME/001/test1
|
|
||||||
TEST_FOLDER2=$TMP/$TEST_NAME/001/test2
|
|
||||||
|
|
||||||
mkdir -p $TEST_FOLDER1
|
|
||||||
mkdir -p $TEST_FOLDER2
|
|
||||||
|
|
||||||
echo hello > $TEST_FOLDER1/b.txt
|
|
||||||
echo cc > $TEST_FOLDER2/c.txt
|
|
||||||
|
|
||||||
$ISULAD_TOOLS add-path $container_ID $TEST_FOLDER2:/tmp:rw $TEST_FOLDER1:/home:rw > /dev/null
|
|
||||||
out=`echo $?`
|
|
||||||
if [ $out -ne 0 ]; then
|
|
||||||
fail $TEST_NAME "01-1:FAIL"
|
|
||||||
else
|
|
||||||
success $TEST_NAME "01-1:PASS"
|
|
||||||
fi
|
|
||||||
out=`isula exec one bash -c "cat /tmp/c.txt"`
|
|
||||||
if [ "$out" == "cc" ]; then
|
|
||||||
success $TEST_NAME "01-2:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "01-2:FAIL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
out=`isula exec one bash -c "cat /home/b.txt"`
|
|
||||||
if [ "$out" == "hello" ]; then
|
|
||||||
success $TEST_NAME "01-3:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "01-3:FAIL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
#test remove-path
|
|
||||||
$ISULAD_TOOLS remove-path $container_ID $TEST_FOLDER2:/tmp:rw $TEST_FOLDER1:/home:rw > /dev/null
|
|
||||||
out=`isula exec one bash -c "ls /tmp && ls /home"`
|
|
||||||
if [ "$out" == "" ]; then
|
|
||||||
success $TEST_NAME "01-4:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "01-4:FAIL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
#test mount different dirct to container of the same direct
|
|
||||||
$ISULAD_TOOLS add-path $container_ID $TEST_FOLDER2:/tmp:rw $TEST_FOLDER1:/tmp:rw > /dev/null
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
fail $TEST_NAME "01-5:FAIL"
|
|
||||||
else
|
|
||||||
success $TEST_NAME "01-6:PASS"
|
|
||||||
fi
|
|
||||||
out=`isula exec $container_ID bash -c "ls /tmp"`
|
|
||||||
if [ "$out" == "b.txt" ]; then
|
|
||||||
success $TEST_NAME "01-7:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "01-7:FAIL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
isula rm -f one > /dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
test_002(){
|
|
||||||
#test Multiple container mount the same direct
|
|
||||||
container_ID1=`isula run --name one1 --hook-spec /var/lib/isulad/hooks/hookspec.json -d $UBUNTU_IMAGE bash -c "sleep 1000"`
|
|
||||||
container_status $container_ID1
|
|
||||||
if [ "${status}x" != "runningx" ]; then
|
|
||||||
fail $TEST_NAME "02:FAIL"
|
|
||||||
fi
|
|
||||||
container_ID2=`isula run --name two --hook-spec /var/lib/isulad/hooks/hookspec.json -d $UBUNTU_IMAGE bash -c "sleep 1000"`
|
|
||||||
container_status $container_ID2
|
|
||||||
if [ "${status}x" != "runningx" ]; then
|
|
||||||
fail $TEST_NAME "021:FAIL"
|
|
||||||
fi
|
|
||||||
TEST_FOLDER1=$TMP/$TEST_NAME/002
|
|
||||||
mkdir -p $TEST_FOLDER1
|
|
||||||
echo hello > $TEST_FOLDER1/b.txt
|
|
||||||
|
|
||||||
$ISULAD_TOOLS add-path $container_ID1 $TEST_FOLDER1:/tmp:rw > /dev/null
|
|
||||||
out=`isula exec one1 sh -c "ls /tmp"`
|
|
||||||
if [ "$out" != "b.txt" ]; then
|
|
||||||
fail $TEST_NAME "02-1:FAIL"
|
|
||||||
else
|
|
||||||
success $TEST_NAME "02-1:PASS"
|
|
||||||
fi
|
|
||||||
|
|
||||||
$ISULAD_TOOLS add-path $container_ID2 $TEST_FOLDER1:/tmp:rw > /dev/null
|
|
||||||
out1=`isula exec two sh -c "cd tmp && ls"`
|
|
||||||
if [ "$out1" != "b.txt" ]; then
|
|
||||||
fail $TEST_NAME "02-2:FAIL"
|
|
||||||
else
|
|
||||||
success $TEST_NAME "02-2:PASS"
|
|
||||||
fi
|
|
||||||
$ISULAD_TOOLS remove-path $container_ID1 $TEST_FOLDER1:/tmp:ro > /dev/null
|
|
||||||
out=`echo $?`
|
|
||||||
if [ $out -ne 0 ];then
|
|
||||||
fail $TEST_NAME "02-3:FAIL"
|
|
||||||
else
|
|
||||||
success $TEST_NAME "02-3:PASS"
|
|
||||||
fi
|
|
||||||
|
|
||||||
out1=`isula exec two sh -c "cd tmp && ls"`
|
|
||||||
if [ "$out1" == "b.txt" ]; then
|
|
||||||
success $TEST_NAME "02-4:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "02-4:FAIL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
#test stop start
|
|
||||||
isula stop two > /dev/null
|
|
||||||
container_status $container_ID2
|
|
||||||
if [ "${status}x" != "exitedx" ]; then
|
|
||||||
fail $TEST_NAME "02-5:FAIL"
|
|
||||||
fi
|
|
||||||
isula start two > /dev/null
|
|
||||||
container_status $container_ID2
|
|
||||||
if [ "${status}x" != "runningx" ]; then
|
|
||||||
fail $TEST_NAME "02-6:FAIL"
|
|
||||||
fi
|
|
||||||
out1=`isula exec two sh -c "cd tmp && ls"`
|
|
||||||
if [ "$out1" == "b.txt" ]; then
|
|
||||||
success $TEST_NAME "02-7:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "02-7:FAIL"
|
|
||||||
fi
|
|
||||||
isula rm -f one1 > /dev/null
|
|
||||||
isula rm -f two > /dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
test_003(){
|
|
||||||
#test one direct is ro ,the other is direct is rw
|
|
||||||
out=`isula run --name one --hook-spec /var/lib/isulad/hooks/hookspec.json -d $UBUNTU_IMAGE bash -c "sleep 1000"`
|
|
||||||
container_status $out
|
|
||||||
if [ "${status}x" != "runningx" ]; then
|
|
||||||
fail $TEST_NAME "03:FAIL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
TEST_FOLDER1=$TMP/$TEST_NAME/003/test1
|
|
||||||
TEST_FOLDER2=$TMP/$TEST_NAME/003/test2
|
|
||||||
|
|
||||||
mkdir -p $TEST_FOLDER1
|
|
||||||
mkdir -p $TEST_FOLDER2
|
|
||||||
|
|
||||||
echo hello > $TEST_FOLDER1/b.txt
|
|
||||||
echo cc > $TEST_FOLDER2/c.txt
|
|
||||||
|
|
||||||
$ISULAD_TOOLS add-path $out $TEST_FOLDER1:/tmp:rw $TEST_FOLDER2:/home:ro > /dev/null 2>&1
|
|
||||||
out1=`isula exec one bash -c "cat /tmp/b.txt"`
|
|
||||||
if [ "$out1" != "hello" ]; then
|
|
||||||
fail $TEST_NAME "03-1:FAIL"
|
|
||||||
fi
|
|
||||||
out1=`isula exec one bash -c "cat /home/c.txt"`
|
|
||||||
if [ "$out1" != "cc" ]; then
|
|
||||||
fail $TEST_NAME "03-2:FAIL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
isula exec one bash -c "cd /home && echo hello>c.txt" > /dev/null 2>&1
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
fail $TEST_NAME "03-3:FAIL"
|
|
||||||
fi
|
|
||||||
out=`isula exec one bash -c "cd /home && cat c.txt"`
|
|
||||||
if [ "$out" == "cc" ]; then
|
|
||||||
success $TEST_NAME "03-4:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "03-4:FAIL"
|
|
||||||
fi
|
|
||||||
isula rm -f one > /dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
main(){
|
|
||||||
test_001
|
|
||||||
test_002
|
|
||||||
test_003
|
|
||||||
}
|
|
||||||
main
|
|
||||||
@ -1,227 +0,0 @@
|
|||||||
# Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
# syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
# You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
# http://license.coscl.org.cn/MulanPSL2
|
|
||||||
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
# PURPOSE.
|
|
||||||
# See the Mulan PSL v2 for more details.
|
|
||||||
# Description: test up container
|
|
||||||
# Author: zhangwei
|
|
||||||
# Create: 2018-01-18
|
|
||||||
|
|
||||||
#!/bin/bash
|
|
||||||
#test up container
|
|
||||||
|
|
||||||
. $CUR/env.sh
|
|
||||||
. $CUR/tools.sh
|
|
||||||
|
|
||||||
TEST_NAME="test_path"
|
|
||||||
|
|
||||||
test_001(){
|
|
||||||
out=`isula run --name one --hook-spec /var/lib/isulad/hooks/hookspec.json -d $UBUNTU_IMAGE bash -c "sleep 100000"`
|
|
||||||
container_status $out
|
|
||||||
if [ "${status}x" != "runningx" ]; then
|
|
||||||
fail $TEST_NAME "01:FAIL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
out1=`isula ps | grep one | awk '{print $1}'`
|
|
||||||
TEST_FOLDER=$TMP/$TEST_NAME/001
|
|
||||||
if [ -d $TEST_FOLDER ]; then
|
|
||||||
rm -rf $TEST_FOLDER > /dev/null
|
|
||||||
fi
|
|
||||||
mkdir -p $TEST_FOLDER
|
|
||||||
|
|
||||||
echo hello > $TEST_FOLDER/b.txt
|
|
||||||
|
|
||||||
$ISULAD_TOOLS add-path $out1 $TEST_FOLDER:/tmp:rw > /dev/null
|
|
||||||
out=`echo $?`
|
|
||||||
if [ $out -ne 0 ]; then
|
|
||||||
fail $TEST_NAME "01-1:FAIL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
out=`isula exec one sh -c "cat /tmp/b.txt"`
|
|
||||||
if [ "$out" == "hello" ]; then
|
|
||||||
success $TEST_NAME "01-2:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "01-2:FAIL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
#test remove-path
|
|
||||||
$ISULAD_TOOLS remove-path $out1 $TEST_FOLDER:/tmp > /dev/null
|
|
||||||
out=`isula exec one sh -c "cd tmp && ls" > /dev/null 2>&1 `
|
|
||||||
if [ "$out" == "" ]; then
|
|
||||||
success $TEST_NAME "01-3:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "01-3:FAIL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# clean up container.
|
|
||||||
isula rm -f one > /dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
test_002(){
|
|
||||||
#test exited container
|
|
||||||
out=`isula run --name one --hook-spec /var/lib/isulad/hooks/hookspec.json -d $UBUNTU_IMAGE`
|
|
||||||
sleep 3
|
|
||||||
container_status $out
|
|
||||||
if [ "${status}x" != "exitedx" ]; then
|
|
||||||
fail $TEST_NAME "02:FAIL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
$ISULAD_TOOLS add-path $out `pwd`:/tmp:rw > /dev/null 2>&1
|
|
||||||
out=`echo $?`
|
|
||||||
if [ $out -ne 0 ]; then
|
|
||||||
success $TEST_NAME "02-1:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "02-1:Fail"
|
|
||||||
fi
|
|
||||||
isula rm one > /dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
test_003(){
|
|
||||||
#test created container
|
|
||||||
out=`isula create --name one --hook-spec /var/lib/isulad/hooks/hookspec.json -ti $UBUNTU_IMAGE`
|
|
||||||
container_status $out
|
|
||||||
if [ "${status}x" != "createdx" ]; then
|
|
||||||
success $TEST_NAME "03:PASS"
|
|
||||||
fi
|
|
||||||
$ISULAD_TOOLS add-path $out `pwd`:/tmp:rw > /dev/null 2>&1
|
|
||||||
out=`echo $?`
|
|
||||||
if [ $out -ne 0 ]; then
|
|
||||||
success $TEST_NAME "03-1:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "03-1:FAIL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
TEST_FOLDER=$TMP/$TEST_NAME/003
|
|
||||||
rm -rf $TEST_FOLDER > /dev/null
|
|
||||||
mkdir -p $TEST_FOLDER
|
|
||||||
|
|
||||||
echo hello > $TEST_FOLDER/b.txt
|
|
||||||
|
|
||||||
#created->up container
|
|
||||||
out1=`isula start one`
|
|
||||||
sleep 1
|
|
||||||
out=`isula ps | grep one | awk '{print $1}'`
|
|
||||||
$ISULAD_TOOLS add-path $out $TEST_FOLDER:/tmp:ro > /dev/null 2>&1
|
|
||||||
out1=`isula exec $out sh -c "cat /tmp/b.txt"`
|
|
||||||
if [ "$out1" == "hello" ]; then
|
|
||||||
success $TEST_NAME "03-2:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "03-2:FAIL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
#test ro
|
|
||||||
isula exec one sh -c "cd tmp && ls && echo abcddd> b.txt" > /dev/null 2>&1
|
|
||||||
out=`echo $?`
|
|
||||||
if [ $out -ne 0 ]; then
|
|
||||||
success $TEST_NAME "03-2:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "03-2:FAIL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
out=`isula ps | grep one | awk '{print $1}'`
|
|
||||||
isula rm -f one > /dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
test_005(){
|
|
||||||
#test mount a Empty dirct
|
|
||||||
out=`isula run --name one --hook-spec /var/lib/isulad/hooks/hookspec.json -d $UBUNTU_IMAGE bash -c "sleep 10000"`
|
|
||||||
container_status $out
|
|
||||||
if [ "${status}x" != "runningx" ]; then
|
|
||||||
fail $TEST_NAME "05:FAIL"
|
|
||||||
fi
|
|
||||||
mkdir -p /tmp/isulad_test/test
|
|
||||||
$ISULAD_TOOLS add-path $out /tmp/isulad_test/test:/tmp:rw > /dev/null 2>&1
|
|
||||||
out1=`isula exec one bash -c "mount | awk 'END{print $1}'"`
|
|
||||||
out1=${out1%on*}
|
|
||||||
out2=${out1##*/}
|
|
||||||
if [ "$out1" == "/dev/$out2" ]; then
|
|
||||||
success $TEST_NAME "05-1:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "05-1:FAIL"
|
|
||||||
fi
|
|
||||||
isula rm -f one > /dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
test_006(){
|
|
||||||
#test can not add ro and rw
|
|
||||||
out=`isula run --name one --hook-spec /var/lib/isulad/hooks/hookspec.json -d $UBUNTU_IMAGE bash -c "sleep 10000"`
|
|
||||||
container_status $out
|
|
||||||
if [ "${status}x" != "runningx" ]; then
|
|
||||||
fail $TEST_NAME "06:FAIL"
|
|
||||||
fi
|
|
||||||
out=`isula exec $out sh -c "cd tmp && ls && echo cc > b.txt && cat b.txt"`
|
|
||||||
if [ "$out" == "cc" ]; then
|
|
||||||
success $TEST_NAME "06-1:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "06-1:FAIL"
|
|
||||||
fi
|
|
||||||
isula rm -f one > /dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
test_007(){
|
|
||||||
#test remove dirct
|
|
||||||
out=`isula run --name one --hook-spec /var/lib/isulad/hooks/hookspec.json -d $UBUNTU_IMAGE bash -c "sleep 10000"`
|
|
||||||
container_status $out
|
|
||||||
if [ "${status}x" != "runningx" ]; then
|
|
||||||
fail $TEST_NAME "07:FAIL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
TEST_FOLDER=$TMP/$TEST_NAME/007
|
|
||||||
if [ -d $TEST_FOLDER ]; then
|
|
||||||
rm -rf $TEST_FOLDER > /dev/null
|
|
||||||
fi
|
|
||||||
mkdir -p $TEST_FOLDER
|
|
||||||
|
|
||||||
echo hello > $TEST_FOLDER/b.txt
|
|
||||||
|
|
||||||
$ISULAD_TOOLS add-path $id $TEST_FOLDER:/tmp:rw > /dev/null
|
|
||||||
out=`echo $?`
|
|
||||||
if [ $out -ne 0 ]; then
|
|
||||||
fail $TEST_NAME "07-1:FAIL"
|
|
||||||
fi
|
|
||||||
out=`isula exec one bash -c "cat /tmp/b.txt"`
|
|
||||||
if [ "$out" != "hello" ]; then
|
|
||||||
fail $TEST_NAME "07-2:FAIL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# remove the path from container.
|
|
||||||
$ISULAD_TOOLS remove-path one $TEST_FOLDER:/tmp:rw > /dev/null
|
|
||||||
out=`isula exec one bash -c "ls -l /tmp"`
|
|
||||||
if [ "$out" == "total 0" ]; then
|
|
||||||
success $TEST_NAME "07-3:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "07-3:FAIL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# clean up container.
|
|
||||||
isula rm -f one > /dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
test_008(){
|
|
||||||
out=`isula run --name one --hook-spec /var/lib/isulad/hooks/hookspec.json -d $UBUNTU_IMAGE bash -c "sleep 100000"`
|
|
||||||
out2=`isula ps | grep one | awk '{print $1}'`
|
|
||||||
$ISULAD_TOOLS add-path $out2 $out1:/tmp:rw > /dev/null 2>&1
|
|
||||||
out=`echo $?`
|
|
||||||
if [ $out -ne 0 ]; then
|
|
||||||
success $TEST_NAME "08-1:PASS"
|
|
||||||
else
|
|
||||||
fail $TEST_NAME "08-1:FAIL"
|
|
||||||
fi
|
|
||||||
isula rm -f one > /dev/null
|
|
||||||
}
|
|
||||||
main(){
|
|
||||||
test_001
|
|
||||||
test_002
|
|
||||||
test_003
|
|
||||||
test_005
|
|
||||||
test_006
|
|
||||||
test_007
|
|
||||||
test_008
|
|
||||||
}
|
|
||||||
|
|
||||||
main
|
|
||||||
|
|
||||||
@ -1,62 +0,0 @@
|
|||||||
# Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
# syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
# You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
# http://license.coscl.org.cn/MulanPSL2
|
|
||||||
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
# PURPOSE.
|
|
||||||
# See the Mulan PSL v2 for more details.
|
|
||||||
# Description: test route
|
|
||||||
# Author: zhangwei
|
|
||||||
# Create: 2018-01-18
|
|
||||||
|
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
. $CUR/env.sh
|
|
||||||
. $CUR/tools.sh
|
|
||||||
|
|
||||||
test_001(){
|
|
||||||
BR=tool_br
|
|
||||||
ip link delete $BR > /dev/null 2>&1
|
|
||||||
brctl addbr $BR > /dev/null 2>&1
|
|
||||||
ip link set $BR up
|
|
||||||
ip a a 192.168.182.1/24 dev $BR
|
|
||||||
|
|
||||||
CONTAINER_ID=`isula run -d --net none $BUSYBOX_IMAGE top`
|
|
||||||
$ISULAD_TOOLS --debug --log $TMP/syscontainer-tools.log add-nic \
|
|
||||||
--type veth --name eth0 --ip 192.168.182.2/24 \
|
|
||||||
--mac "aa:bb:cc:dd:ee:aa" --bridge $BR --mtu 1450 \
|
|
||||||
$CONTAINER_ID
|
|
||||||
isula exec --privileged $CONTAINER_ID ip route delete 192.168.182.0/24
|
|
||||||
$ISULAD_TOOLS add-route $CONTAINER_ID '[{"dest":"192.168.182.0/24", "src":"192.168.182.2","dev":"eth0"}]'
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
fail $TEST_NAME "01-1:FAIL"
|
|
||||||
else
|
|
||||||
success $TEST_NAME "01-1:PASS"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# TODO: default gw can't set, what's the problem?
|
|
||||||
#$ISULAD_TOOLS add-route $CONTAINER_ID '[{"gw":"192.168.182.1","dev":"eth0"}]'
|
|
||||||
#if [ $? -ne 0 ]; then
|
|
||||||
# fail $TEST_NAME "01-1:FAIL"
|
|
||||||
#else
|
|
||||||
# success $TEST_NAME "01-1:PASS"
|
|
||||||
#fi
|
|
||||||
|
|
||||||
rules=`isula exec $CONTAINER_ID ip route`
|
|
||||||
echo $rules | grep "192.168.182.0/24 dev eth0 src 192.168.182.2" > /dev/null 2>&1
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
fail $TEST_NAME "01-2:FAIL"
|
|
||||||
else
|
|
||||||
success $TEST_NAME "01-2:PASS"
|
|
||||||
fi
|
|
||||||
isula rm -f $CONTAINER_ID > /dev/null 2>&1
|
|
||||||
brctl delbr $BR > /dev/null 2>&1
|
|
||||||
}
|
|
||||||
|
|
||||||
main(){
|
|
||||||
test_001
|
|
||||||
}
|
|
||||||
|
|
||||||
main
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
# Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
# syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
# You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
# http://license.coscl.org.cn/MulanPSL2
|
|
||||||
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
# PURPOSE.
|
|
||||||
# See the Mulan PSL v2 for more details.
|
|
||||||
# Description: test tools
|
|
||||||
# Author: zhangwei
|
|
||||||
# Create: 2018-01-18
|
|
||||||
|
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
container_status(){
|
|
||||||
id=$1
|
|
||||||
status=`isula inspect ${id:00:12} | grep Status | awk -F ":" '{print $2}'`
|
|
||||||
status=${status#*\"}
|
|
||||||
status=${status%%\"*}
|
|
||||||
}
|
|
||||||
|
|
||||||
fail(){
|
|
||||||
total_cases=$((total_cases+1))
|
|
||||||
failed_cases=$((failed_cases+1))
|
|
||||||
echo $@
|
|
||||||
}
|
|
||||||
|
|
||||||
success(){
|
|
||||||
total_cases=$((total_cases+1))
|
|
||||||
success_cases=$((success_cases+1))
|
|
||||||
echo $@
|
|
||||||
}
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: mount type
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package types
|
|
||||||
|
|
||||||
// Sysctl is sysctl value
|
|
||||||
type Sysctl struct {
|
|
||||||
Key string
|
|
||||||
Value string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mount is mount value
|
|
||||||
type Mount struct {
|
|
||||||
Source string
|
|
||||||
Destination string
|
|
||||||
Type string
|
|
||||||
Options string
|
|
||||||
UID int // User ID
|
|
||||||
GID int // Group ID
|
|
||||||
}
|
|
||||||
184
types/device.go
184
types/device.go
@ -1,184 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: device and bind type
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package types
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"os/exec"
|
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
wildcard = -1
|
|
||||||
majorNum = 8 // the device number of major
|
|
||||||
minorNum = 12 // the device number of minor
|
|
||||||
)
|
|
||||||
|
|
||||||
// ErrMsg is a structure used by parent and child processes to transfer error messages
|
|
||||||
type ErrMsg struct {
|
|
||||||
Error string
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddDeviceMsg is a parent and child message, used to transfer 'add device' operation
|
|
||||||
type AddDeviceMsg struct {
|
|
||||||
Force bool
|
|
||||||
Device *Device
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bind is a parent and child message, used to transfer bind operation
|
|
||||||
type Bind struct {
|
|
||||||
HostPath string // Path on Host relative path, (based on entry point.)
|
|
||||||
IsDir bool // Path is Directory?
|
|
||||||
ResolvPath string // Relative path of Mountpoint
|
|
||||||
ContainerPath string // Path in Container
|
|
||||||
MountOption string // Bind Mount options, to dest path.
|
|
||||||
UID int // User ID
|
|
||||||
GID int // Group ID
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToString returns the storage format string of the bind in device hook config file
|
|
||||||
func (bind *Bind) ToString() string {
|
|
||||||
return fmt.Sprintf("%s:%s:%s", bind.HostPath, bind.ContainerPath, bind.MountOption)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Device is the device abstract structure used by the entire workspace
|
|
||||||
type Device struct {
|
|
||||||
Type string // Device type: c or b
|
|
||||||
Path string // Path in container.
|
|
||||||
PathOnHost string // Path on Host.
|
|
||||||
Major int64 // Major number of device
|
|
||||||
Minor int64 // Minor number of device
|
|
||||||
Permissions string // Permissions which user input.
|
|
||||||
FileMode os.FileMode // File Mode,
|
|
||||||
UID uint32 // User ID
|
|
||||||
GID uint32 // Group ID
|
|
||||||
Allow bool // Used to differ add or remove
|
|
||||||
Parent string // Parent device name(pathonhost)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Qos is the device Qos structure
|
|
||||||
type Qos struct {
|
|
||||||
Major int64 `json:"major"`
|
|
||||||
Minor int64 `json:"minor"`
|
|
||||||
Path string `json:"path"`
|
|
||||||
Value string `json:"value"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddDeviceOptions defines the optsions for add device operation
|
|
||||||
type AddDeviceOptions struct {
|
|
||||||
ReadBps []*Qos
|
|
||||||
WriteBps []*Qos
|
|
||||||
ReadIOPS []*Qos
|
|
||||||
WriteIOPS []*Qos
|
|
||||||
BlkioWeight []*Qos
|
|
||||||
Force bool
|
|
||||||
UpdateConfigOnly bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q Qos) String() string {
|
|
||||||
return fmt.Sprintf("%d:%d %s", q.Major, q.Minor, q.Value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCfqAbility get cfq ability or not
|
|
||||||
func (q Qos) GetCfqAbility() (bool, error) {
|
|
||||||
q.Path = filepath.Clean(q.Path)
|
|
||||||
Cfq, err := q.ReadCFQ(q.Path)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return Cfq, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadCFQ read cfq value
|
|
||||||
func (q Qos) ReadCFQ(devName string) (bool, error) {
|
|
||||||
path := fmt.Sprintf("/sys/block/%s/queue/scheduler", filepath.Base(devName))
|
|
||||||
|
|
||||||
cfqFile, err := os.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stdout, "fail to open cfq file:%s", path)
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
defer cfqFile.Close()
|
|
||||||
|
|
||||||
buff := bufio.NewReader(cfqFile)
|
|
||||||
|
|
||||||
line, _, err := buff.ReadLine()
|
|
||||||
if err != nil && err != io.EOF {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.Contains(string(line), "cfq") || strings.Contains(string(line), "bfq") {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the device string which stores in config file
|
|
||||||
func (d *Device) String() string {
|
|
||||||
return fmt.Sprintf("%s:%s:%s", d.PathOnHost, d.Path, d.Permissions)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CgroupString returns the cgroup string of a device
|
|
||||||
func (d *Device) CgroupString() string {
|
|
||||||
// Agreement with Product:
|
|
||||||
// we do not care about sub logic block device, they will take care of it.
|
|
||||||
return fmt.Sprintf("%s %s:%s %s", d.Type, deviceNumberString(d.Major), deviceNumberString(d.Minor), d.Permissions)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBaseDevName returns base device of a partition
|
|
||||||
func GetBaseDevName(deviceName string) (string, error) {
|
|
||||||
cmd := exec.Command("lsblk", "-n", "-p", "-r", "-s", "-o", "NAME", deviceName)
|
|
||||||
out, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("Failed to lsblk %s : %v", string(out), err)
|
|
||||||
}
|
|
||||||
rawString := strings.Split(string(out), "\n")
|
|
||||||
if len(rawString) <= 1 {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
return rawString[1], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDeviceType get device type
|
|
||||||
func GetDeviceType(devName string) (string, error) {
|
|
||||||
cmd := exec.Command("lsblk", "-n", "-d", "-o", "TYPE", devName)
|
|
||||||
out, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("Failed to lsblk %s : %v", string(out), err)
|
|
||||||
}
|
|
||||||
devType := strings.Trim(string(out), "\n")
|
|
||||||
logrus.Debugf("%s type is %s\n", devName, devType)
|
|
||||||
return devType, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mkdev returns the device number in int format
|
|
||||||
func (d *Device) Mkdev() int {
|
|
||||||
return int((d.Major << majorNum) | (d.Minor & 0xff) | ((d.Minor & 0xfff00) << minorNum))
|
|
||||||
}
|
|
||||||
|
|
||||||
// deviceNumberString returns the device number by string
|
|
||||||
func deviceNumberString(number int64) string {
|
|
||||||
if number == wildcard {
|
|
||||||
return "*"
|
|
||||||
}
|
|
||||||
return fmt.Sprint(number)
|
|
||||||
}
|
|
||||||
175
types/network.go
175
types/network.go
@ -1,175 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: network interface type
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package types
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NamespacePath namespace paths
|
|
||||||
type NamespacePath struct {
|
|
||||||
Pid string `json:"pid,omitempty"`
|
|
||||||
Net string `json:"net"`
|
|
||||||
Mnt string `json:"mnt,omitempty"`
|
|
||||||
User string `json:"user,omitempty"`
|
|
||||||
Ipc string `json:"ipc,omitempty"`
|
|
||||||
Uts string `json:"uts,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// InterfaceConf is the network interface config
|
|
||||||
type InterfaceConf struct {
|
|
||||||
IP string `json:"Ip"`
|
|
||||||
Mac string `json:"Mac"`
|
|
||||||
Mtu int `json:"Mtu"`
|
|
||||||
Qlen int `json:"Qlen"`
|
|
||||||
Type string `json:"Type"`
|
|
||||||
Bridge string `json:"Bridge"`
|
|
||||||
HostNicName string `json:"HostNicName"`
|
|
||||||
CtrNicName string `json:"CtrNicName"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (nic *InterfaceConf) String() string {
|
|
||||||
return fmt.Sprintf("Type:%s,ip:%s,name:%s->%s", nic.Type, nic.IP, nic.HostNicName, nic.CtrNicName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Route is the network route
|
|
||||||
type Route struct {
|
|
||||||
Dest string `json:"dest"`
|
|
||||||
Src string `json:"src"`
|
|
||||||
Gw string `json:"gw"`
|
|
||||||
Dev string `json:"dev"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// String will format the route to string format
|
|
||||||
func (r *Route) String() string {
|
|
||||||
return fmt.Sprintf("{dest:%s,src:%s,gw:%s,dev:%s}", r.Dest, r.Src, r.Gw, r.Dev)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsConflictNic will check if the nic1 config is conflict with nic2
|
|
||||||
func IsConflictNic(nic1, nic2 *InterfaceConf) error {
|
|
||||||
if nic1.CtrNicName == nic2.CtrNicName {
|
|
||||||
return fmt.Errorf("interface name conflict: %s", nic1.CtrNicName)
|
|
||||||
}
|
|
||||||
if nic1.HostNicName == nic2.HostNicName {
|
|
||||||
return fmt.Errorf("interface name conflict: %s", nic1.HostNicName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if nic1.Mac != "" && (nic1.Mac == nic2.Mac) {
|
|
||||||
return fmt.Errorf("interface mac conflict: %s", nic1.Mac)
|
|
||||||
}
|
|
||||||
if nic1.IP == nic2.IP {
|
|
||||||
return fmt.Errorf("interface ip conflict: %s", nic1.IP)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsSameNic will check if the nic1 and nic2 is the same
|
|
||||||
func IsSameNic(obj, src *InterfaceConf) bool {
|
|
||||||
if obj.IP != src.IP && obj.IP != "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if obj.Mac != src.Mac && obj.Mac != "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if obj.Mtu != src.Mtu && obj.Mtu != 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if obj.Qlen != src.Qlen && obj.Qlen != 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if obj.Type != src.Type && obj.Type != "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if obj.Bridge != src.Bridge && obj.Bridge != "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if obj.HostNicName != src.HostNicName && obj.HostNicName != "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if obj.CtrNicName != src.CtrNicName && obj.CtrNicName != "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsConflictRoute will check if the r1 route config is conflict with r2
|
|
||||||
func IsConflictRoute(r1, r2 *Route) error {
|
|
||||||
if IsSameRoute(r1, r2) {
|
|
||||||
return fmt.Errorf("route %v alread exist", r1)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsSameRoute will check if the obj route config is the same with src
|
|
||||||
func IsSameRoute(obj, src *Route) bool {
|
|
||||||
if obj.Dest != src.Dest && obj.Dest != "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if obj.Src != src.Src && obj.Src != "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if obj.Gw != src.Gw && obj.Gw != "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if obj.Dev != src.Dev && obj.Dev != "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidNetworkConfig validate network config
|
|
||||||
func ValidNetworkConfig(conf *InterfaceConf) error {
|
|
||||||
// check IP here
|
|
||||||
conf.IP = strings.TrimSpace(conf.IP)
|
|
||||||
if _, err := netlink.ParseIPNet(conf.IP); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check mac here
|
|
||||||
conf.Mac = strings.TrimSpace(conf.Mac)
|
|
||||||
if len(conf.Mac) != 0 {
|
|
||||||
if _, err := net.ParseMAC(conf.Mac); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch conf.Type {
|
|
||||||
case "veth":
|
|
||||||
if _, err := netlink.LinkByName(conf.HostNicName); err == nil {
|
|
||||||
// found same link with hostNicName, just error out
|
|
||||||
return fmt.Errorf("Host has nic with name %s, please choose another one", conf.HostNicName)
|
|
||||||
}
|
|
||||||
conf.Bridge = strings.TrimSpace(conf.Bridge)
|
|
||||||
if conf.Bridge == "" {
|
|
||||||
return fmt.Errorf("bridge must be specified")
|
|
||||||
}
|
|
||||||
case "eth":
|
|
||||||
if conf.HostNicName == "" {
|
|
||||||
return fmt.Errorf("host nic name input error")
|
|
||||||
}
|
|
||||||
if conf.Bridge != "" {
|
|
||||||
return fmt.Errorf("for eth type, bridge cannot be set")
|
|
||||||
}
|
|
||||||
if _, err := netlink.LinkByName(conf.HostNicName); err != nil {
|
|
||||||
// if HostNicName not found, just error out
|
|
||||||
return fmt.Errorf("HostNic(%s) not found, please check", conf.HostNicName)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unsupported type %s", conf.Type)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
145
utils/hooks.go
145
utils/hooks.go
@ -1,145 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: hook utils
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/opencontainers/runc/libcontainer/configs"
|
|
||||||
"github.com/opencontainers/runtime-spec/specs-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
// isula info may result in dead lock when start with restart policy
|
|
||||||
// try to get isulad root path with hook path
|
|
||||||
func getGraphDriverPath() (string, error) {
|
|
||||||
path, err := os.Executable()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// get /var/lib/isulad from /var/lib/isulad/hooks/syscontainer-hooks
|
|
||||||
dir := filepath.Dir(filepath.Dir(path))
|
|
||||||
return dir, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetContainerStoragePath returns the isulad container storage path
|
|
||||||
func GetContainerStoragePath() (string, error) {
|
|
||||||
// check graph driver here!
|
|
||||||
graphDriverPath, err := getGraphDriverPath()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
base := filepath.Join(graphDriverPath, "engines", "lcr")
|
|
||||||
finfo, err := os.Stat(base)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if !finfo.IsDir() {
|
|
||||||
return "", fmt.Errorf("Container Path:%s is not a directory", base)
|
|
||||||
}
|
|
||||||
return base, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CompatHookState hook state compat with old version
|
|
||||||
type CompatHookState struct {
|
|
||||||
configs.SpecState
|
|
||||||
Bundle string `json:"bundlePath"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseHookState parses the config of HookState from isulad via stdin
|
|
||||||
func ParseHookState(reader io.Reader) (*configs.HookState, error) {
|
|
||||||
// We expect configs.HookState as a json string in <stdin>
|
|
||||||
stateBuf, err := ioutil.ReadAll(reader)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var state configs.HookState
|
|
||||||
if err = json.Unmarshal(stateBuf, &state); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var compatStat CompatHookState
|
|
||||||
if state.Bundle == "" {
|
|
||||||
if err = json.Unmarshal(stateBuf, &compatStat); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if compatStat.Bundle == "" {
|
|
||||||
return nil, fmt.Errorf("unmarshal hook state failed %s", stateBuf)
|
|
||||||
}
|
|
||||||
state.Bundle = compatStat.Bundle
|
|
||||||
}
|
|
||||||
|
|
||||||
return &state, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadSpec will load the oci config of isulad container from disk.
|
|
||||||
func LoadSpec(configPath string) (spec *specs.Spec, err error) {
|
|
||||||
config, err := os.Open(configPath)
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return nil, fmt.Errorf("config file %s not found", configPath)
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer config.Close()
|
|
||||||
|
|
||||||
if err = json.NewDecoder(config).Decode(&spec); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return spec, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadCompatSpec will load the oci config of isulad container from disk.
|
|
||||||
func LoadCompatSpec(configPath string) (spec *specs.CompatSpec, err error) {
|
|
||||||
config, err := os.Open(configPath)
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return nil, fmt.Errorf("config file %s not found", configPath)
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer config.Close()
|
|
||||||
|
|
||||||
if err = json.NewDecoder(config).Decode(&spec); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return spec, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func hostIDFromMapping(containerID uint32, uMap []specs.LinuxIDMapping) int {
|
|
||||||
if uMap != nil {
|
|
||||||
for _, m := range uMap {
|
|
||||||
if (containerID >= m.ContainerID) && (containerID <= (m.ContainerID + m.Size - 1)) {
|
|
||||||
hostID := m.HostID + (containerID - m.ContainerID)
|
|
||||||
return int(hostID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUIDGid get uid and gid from spec
|
|
||||||
func GetUIDGid(spec *specs.Spec) (int, int) {
|
|
||||||
for _, namespace := range spec.Linux.Namespaces {
|
|
||||||
if namespace.Type == specs.UserNamespace {
|
|
||||||
return hostIDFromMapping(0, spec.Linux.UIDMappings), hostIDFromMapping(0, spec.Linux.GIDMappings)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1, -1
|
|
||||||
}
|
|
||||||
179
utils/selinux.go
179
utils/selinux.go
@ -1,179 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: selinux utils
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// InitPath is the path of container'init process
|
|
||||||
InitPath = "/sbin/init"
|
|
||||||
|
|
||||||
systemdInit = "systemd"
|
|
||||||
xattrNameSelinux = "security.selinux"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SELinuxContext user:role:type:level
|
|
||||||
type SELinuxContext map[string]string
|
|
||||||
|
|
||||||
// IsExist judges whether a file exists
|
|
||||||
func IsExist(filename string) bool {
|
|
||||||
_, err := os.Stat(filename)
|
|
||||||
return err == nil || os.IsExist(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SeconfigGet gets the k/v that come from /etc/selinux/config,like 'SELINUX=permissive'
|
|
||||||
func SeconfigGet(path, key string) (string, error) {
|
|
||||||
f, err := os.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(f)
|
|
||||||
for scanner.Scan() {
|
|
||||||
txt := scanner.Text()
|
|
||||||
if strings.HasPrefix(txt, "#") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
fields := strings.Split(txt, "=")
|
|
||||||
if len(fields) == 2 && fields[0] == key {
|
|
||||||
return fields[1], nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := scanner.Err(); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return "", fmt.Errorf("Parse SELinux config file err")
|
|
||||||
}
|
|
||||||
|
|
||||||
// SeconfigSet sets the k/v that come from /etc/selinux/config,like 'SELINUX=permissive'
|
|
||||||
func SeconfigSet(path, key, value string) error {
|
|
||||||
f, err := os.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
buf := bytes.NewBufferString("")
|
|
||||||
scanner := bufio.NewScanner(f)
|
|
||||||
for scanner.Scan() {
|
|
||||||
txt := scanner.Text()
|
|
||||||
if strings.HasPrefix(txt, "#") {
|
|
||||||
if _, err := buf.WriteString(txt + "\n"); err != nil {
|
|
||||||
logrus.Errorf("buf.WriteString err: %v", err)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
fields := strings.Split(txt, "=")
|
|
||||||
if len(fields) == 2 && fields[0] == key {
|
|
||||||
if _, err := buf.WriteString(fmt.Sprintf("%s=%s\n", key, value)); err != nil {
|
|
||||||
logrus.Errorf("buf.WriteString err: %v", err)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, err := buf.WriteString(txt + "\n"); err != nil {
|
|
||||||
logrus.Errorf("buf.WriteString err: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := scanner.Err(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return ioutil.WriteFile(path, buf.Bytes(), 0600)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BindMount creates bind mount
|
|
||||||
func BindMount(source, dest string, readonly bool) error {
|
|
||||||
if err := syscall.Mount(source, dest, "bind", syscall.MS_BIND|syscall.MS_REC, ""); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remount bind mount to read/only if requested by the caller */
|
|
||||||
if readonly {
|
|
||||||
if err := syscall.Mount(source, dest, "bind", syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_REC, ""); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSelinuxMountPount gets the path which selinuxfs will be mounted.
|
|
||||||
func GetSelinuxMountPount(rootfs string) string {
|
|
||||||
selinuxMountPoint := []string{
|
|
||||||
"/selinux",
|
|
||||||
"/sys/fs/selinux",
|
|
||||||
}
|
|
||||||
for _, path := range selinuxMountPoint {
|
|
||||||
if IsExist(rootfs + path) {
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return selinuxMountPoint[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsSystemdInit judges whether a systemd
|
|
||||||
func IsSystemdInit(rootfs string) bool {
|
|
||||||
if initPath, err := filepath.EvalSymlinks(rootfs + InitPath); err == nil && strings.Contains(initPath, systemdInit) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fatal fatal err, need exit.
|
|
||||||
func Fatal(err error) {
|
|
||||||
logrus.Error(err)
|
|
||||||
fmt.Fprintln(os.Stderr, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get gets SELinuxContext string
|
|
||||||
func (c SELinuxContext) Get() string {
|
|
||||||
return fmt.Sprintf("%s:%s:%s:%s", c["user"], c["role"], c["type"], c["level"])
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetType sets SELinuxContext type
|
|
||||||
func (c SELinuxContext) SetType(t string) {
|
|
||||||
c["type"] = t
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetType gets SELinuxContext type
|
|
||||||
func (c SELinuxContext) GetType() string {
|
|
||||||
return c["type"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewContext creates a new SELinuxContext
|
|
||||||
func NewContext(scon string) SELinuxContext {
|
|
||||||
c := make(SELinuxContext)
|
|
||||||
// unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
|
|
||||||
if len(scon) != 0 {
|
|
||||||
con := strings.SplitN(scon, ":", 4)
|
|
||||||
c["user"] = con[0]
|
|
||||||
c["role"] = con[1]
|
|
||||||
c["type"] = con[2]
|
|
||||||
c["level"] = con[3]
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
@ -1,191 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: mount transfer utils
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/sha256"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
mymount "isula.org/syscontainer-tools/pkg/mount"
|
|
||||||
"github.com/docker/docker/pkg/mount"
|
|
||||||
|
|
||||||
"isula.org/syscontainer-tools/types"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
masterPath = "/.sharedpath/master"
|
|
||||||
midTransferPath = "/.sharedpath/midpath"
|
|
||||||
slavePath = "/.sharedpath"
|
|
||||||
)
|
|
||||||
|
|
||||||
/* Add path to container when it is running
|
|
||||||
|
|
||||||
We use mount propagation mechanism to do it. Take
|
|
||||||
syscontainer tools add-path /hostpath1:/guest1 for example.
|
|
||||||
|
|
||||||
|
|
||||||
1. Add a sharing path using hook as belowing. Then every
|
|
||||||
new mount event will propagate to container.
|
|
||||||
|
|
||||||
container ---->/.sharedpath (rslave,ro)
|
|
||||||
host --->/.sharedpath/master/containerid (rshared,rw)
|
|
||||||
|
|
||||||
2. Add transfer path
|
|
||||||
|
|
||||||
a. (host)mount --bind -o rw /host1 /.sharedpath/midpath/containerid/hostpath1
|
|
||||||
b. (host)mount --bind -o rw /.sharedpath/midpath/containerid/hostpath1 /.sharedpath/master/containerid/hostpath1
|
|
||||||
c. (container) mount --bind -ro /.sharedpath/hostpath1 /guest1
|
|
||||||
*/
|
|
||||||
|
|
||||||
func releaseMountpoint(path string) error {
|
|
||||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err := mymount.Unmount(path); err != nil {
|
|
||||||
logrus.Errorf("releaseMountpoint: Failed to umount: %s, error: %s, still try to remove path", path, err)
|
|
||||||
}
|
|
||||||
if err := os.RemoveAll(path); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveTransferPath remove transfer path
|
|
||||||
func RemoveTransferPath(id string, bind *types.Bind) error {
|
|
||||||
midPath, tarsferPath := getTransferPath(id, bind.HostPath)
|
|
||||||
if err := releaseMountpoint(midPath); err != nil {
|
|
||||||
logrus.Errorf("RemoveTransferPath failed: %s", err)
|
|
||||||
}
|
|
||||||
if err := releaseMountpoint(tarsferPath); err != nil {
|
|
||||||
logrus.Errorf("RemoveTransferPath failed: %s", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveContainerSpecPath remove container spec
|
|
||||||
func RemoveContainerSpecPath(id string) error {
|
|
||||||
if err := os.RemoveAll(GetContainerMidDir(id)); err != nil {
|
|
||||||
logrus.Errorf("RemoveSharedPath failed, err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := os.RemoveAll(GetContainerSpecDir(id)); err != nil {
|
|
||||||
logrus.Errorf("RemoveSharedPath failed, err: %s", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parepareMountpoint(sPath, dPath, mOpt string, isDir bool) error {
|
|
||||||
if isDir {
|
|
||||||
if err := os.MkdirAll(dPath, 0600); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err := os.MkdirAll(filepath.Dir(dPath), 0600); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
f, err := os.OpenFile(dPath, os.O_RDWR|os.O_CREATE, 0600)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Fail to create transfer path,err: %s", err)
|
|
||||||
}
|
|
||||||
f.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
if m, err := mount.Mounted(dPath); err != nil {
|
|
||||||
return fmt.Errorf("Failed to mount path %s, err: %s", dPath, err)
|
|
||||||
} else if m == true {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return mymount.Mount(sPath, dPath, "none", mOpt)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrepareTransferPath prepares the transfer path for sharing.
|
|
||||||
// To propagate mount options to container, we need two middle paths.
|
|
||||||
func PrepareTransferPath(containerPath, id string, bind *types.Bind, doMount bool) error {
|
|
||||||
midpath, tarsferPath := getTransferPath(id, bind.HostPath)
|
|
||||||
bind.MountOption += ",bind"
|
|
||||||
bind.ResolvPath = filepath.Join(containerPath, getRelativePath(bind.HostPath))
|
|
||||||
if !doMount {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. check mount propagation
|
|
||||||
if err := mymount.ValidMountPropagation(bind.HostPath, bind.MountOption); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. prepare midpath
|
|
||||||
if err := parepareMountpoint(bind.HostPath, midpath, bind.MountOption, bind.IsDir); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. prapare transferpath
|
|
||||||
if err := parepareMountpoint(midpath, tarsferPath, bind.MountOption, bind.IsDir); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func getRelativePath(hostpath string) string {
|
|
||||||
return filepath.Join(slavePath, getTransferBase(hostpath))
|
|
||||||
}
|
|
||||||
func getTransferPath(id, hostpath string) (string, string) {
|
|
||||||
transfer := filepath.Join(GetContainerSpecDir(id), getTransferBase(hostpath))
|
|
||||||
midpath := filepath.Join(midTransferPath, id, getTransferBase(hostpath))
|
|
||||||
return midpath, transfer
|
|
||||||
}
|
|
||||||
func getTransferBase(path string) string {
|
|
||||||
return fmt.Sprintf("%x", sha256.Sum256([]byte(path)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetContainerSpecDir get container spec dir
|
|
||||||
func GetContainerSpecDir(id string) string {
|
|
||||||
return filepath.Join(masterPath, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetContainerMidDir get container middle dir
|
|
||||||
func GetContainerMidDir(id string) string {
|
|
||||||
return filepath.Join(midTransferPath, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSlavePath get slave path
|
|
||||||
func GetSlavePath() string {
|
|
||||||
return slavePath
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrepareHostPath prepare host path
|
|
||||||
func PrepareHostPath(id string) error {
|
|
||||||
|
|
||||||
if err := os.MkdirAll(masterPath, 0600); err != nil {
|
|
||||||
return fmt.Errorf("create host shared path failed, err: %s", err)
|
|
||||||
}
|
|
||||||
if m, _ := mount.Mounted(masterPath); m != true {
|
|
||||||
if err := mount.Mount("none", masterPath, "tmpfs", "size=16m"); err != nil {
|
|
||||||
return fmt.Errorf("mount host shared path failed:, %s", err)
|
|
||||||
}
|
|
||||||
if err := syscall.Mount("none", masterPath, "none", syscall.MS_SHARED|syscall.MS_REC, ""); err != nil {
|
|
||||||
return fmt.Errorf("failed to make mountpoint shared, err: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := os.MkdirAll(filepath.Join(masterPath, id), 0600); err != nil {
|
|
||||||
return fmt.Errorf("create host shared path failed, err: %s", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
186
utils/utils.go
186
utils/utils.go
@ -1,186 +0,0 @@
|
|||||||
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
||||||
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
||||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
// You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
// http://license.coscl.org.cn/MulanPSL2
|
|
||||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
||||||
// PURPOSE.
|
|
||||||
// See the Mulan PSL v2 for more details.
|
|
||||||
// Description: common utils
|
|
||||||
// Author: zhangwei
|
|
||||||
// Create: 2018-01-18
|
|
||||||
|
|
||||||
package utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rand"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"log/syslog"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
libctr_utils "github.com/opencontainers/runc/libcontainer/utils"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SyslogHook to send logs via syslog.
|
|
||||||
type syslogHook struct {
|
|
||||||
logger *log.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a hook to be added to an instance of logger. This is called with
|
|
||||||
// `hook, err := newSyslogHook("default", "udp", "localhost:514", syslog.LOG_DEBUG, "")`
|
|
||||||
// `if err == nil { log.Hooks.Add(hook) }`
|
|
||||||
func newSyslogHook(network, raddr string, priority syslog.Priority, tag string) (*syslogHook, error) {
|
|
||||||
var logger *log.Logger
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if network == "default" {
|
|
||||||
logger, err = syslog.NewLogger(priority, log.Lshortfile)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
logger.SetPrefix(tag)
|
|
||||||
} else {
|
|
||||||
w, err := syslog.Dial(network, raddr, priority, "")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
logger = log.New(w, tag, log.Lshortfile)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &syslogHook{logger}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hook *syslogHook) Fire(entry *logrus.Entry) error {
|
|
||||||
line, err := entry.String()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := hook.logger.Output(8, line); err != nil {
|
|
||||||
logrus.Errorf("hook.logger.Output err: %s", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hook *syslogHook) Levels() []logrus.Level {
|
|
||||||
return logrus.AllLevels
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
syslogUDPPrefix = "udp://"
|
|
||||||
syslogTCPPrefix = "tcp://"
|
|
||||||
syslogUnixSock = "unix://"
|
|
||||||
syslogDefaultUDPService = "localhost:541"
|
|
||||||
syslogDefaultTCPService = "localhost:541"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SyslogService is a structure which records the syslog service type and serivce address.
|
|
||||||
type SyslogService struct {
|
|
||||||
Type string
|
|
||||||
Addr string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseSyslogService parses syslog service from input string
|
|
||||||
func ParseSyslogService(service string) (*SyslogService, error) {
|
|
||||||
var serviceType, serviceAddr string
|
|
||||||
|
|
||||||
if service == "" {
|
|
||||||
serviceType = "default"
|
|
||||||
serviceAddr = ""
|
|
||||||
} else if strings.HasPrefix(service, syslogUDPPrefix) {
|
|
||||||
serviceType = "udp"
|
|
||||||
serviceAddr := service[len(syslogUDPPrefix):]
|
|
||||||
if serviceAddr == "" {
|
|
||||||
serviceAddr = syslogDefaultUDPService
|
|
||||||
}
|
|
||||||
} else if strings.HasPrefix(service, syslogTCPPrefix) {
|
|
||||||
serviceType = "tcp"
|
|
||||||
serviceAddr = service[len(syslogTCPPrefix):]
|
|
||||||
if serviceAddr == "" {
|
|
||||||
serviceAddr = syslogDefaultTCPService
|
|
||||||
}
|
|
||||||
} else if strings.HasPrefix(service, syslogUnixSock) {
|
|
||||||
// syslog package will use empty string as network,
|
|
||||||
// and syslog will lookup the unix socket on host, we do not care.
|
|
||||||
serviceType = ""
|
|
||||||
serviceAddr = service[len(syslogUnixSock):]
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("Unspported syslog network: %s", service)
|
|
||||||
}
|
|
||||||
|
|
||||||
serv := &SyslogService{
|
|
||||||
Type: serviceType,
|
|
||||||
Addr: serviceAddr,
|
|
||||||
}
|
|
||||||
return serv, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// HookSyslog will hook syslog service to logrus
|
|
||||||
// syslog supports 4 kinds of service:
|
|
||||||
// 1. default socket: ""
|
|
||||||
// 2. unix socket: "unix:///dev/log"
|
|
||||||
// 3. udp port: "udp://localhost:541"
|
|
||||||
// 4. tcp port: "tcp://localhost:541"
|
|
||||||
// by default, if we output to local syslog, use default will be fine.
|
|
||||||
// syslog Tag:
|
|
||||||
// syslog will use tag to separate the output stream.
|
|
||||||
func HookSyslog(service, tag string) error {
|
|
||||||
serv, err := ParseSyslogService(service)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
hook, err := newSyslogHook(serv.Type, serv.Addr, syslog.LOG_INFO|syslog.LOG_USER, tag)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Unable to connect to syslog daemon")
|
|
||||||
}
|
|
||||||
logrus.AddHook(hook)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPipe creates a pair of new socket pipe.
|
|
||||||
func NewPipe() (parent, child *os.File, err error) {
|
|
||||||
fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
return os.NewFile(uintptr(fds[1]), "parent"), os.NewFile(uintptr(fds[0]), "child"), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteJSON write json data to io stream
|
|
||||||
func WriteJSON(w io.Writer, v interface{}) error {
|
|
||||||
return libctr_utils.WriteJSON(w, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RandomID returns a 8-bit ramdon string which read from rand.Reader first,
|
|
||||||
// and if failed, will use time stamp as random id
|
|
||||||
func RandomID() string {
|
|
||||||
id := make([]byte, 32)
|
|
||||||
if _, err := io.ReadFull(rand.Reader, id); err != nil {
|
|
||||||
cur := time.Now()
|
|
||||||
return fmt.Sprint(cur.UnixNano())
|
|
||||||
}
|
|
||||||
return hex.EncodeToString(id)[:8]
|
|
||||||
}
|
|
||||||
|
|
||||||
// RandomFile will find a non-existing file in given folder.
|
|
||||||
func RandomFile(folder string) string {
|
|
||||||
path := ""
|
|
||||||
for {
|
|
||||||
id := RandomID()
|
|
||||||
path = filepath.Join(folder, id)
|
|
||||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
1
vendor/github.com/Microsoft/go-winio/.gitignore
generated
vendored
1
vendor/github.com/Microsoft/go-winio/.gitignore
generated
vendored
@ -1 +0,0 @@
|
|||||||
*.exe
|
|
||||||
22
vendor/github.com/Microsoft/go-winio/LICENSE
generated
vendored
22
vendor/github.com/Microsoft/go-winio/LICENSE
generated
vendored
@ -1,22 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2015 Microsoft
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
||||||
22
vendor/github.com/Microsoft/go-winio/README.md
generated
vendored
22
vendor/github.com/Microsoft/go-winio/README.md
generated
vendored
@ -1,22 +0,0 @@
|
|||||||
# go-winio
|
|
||||||
|
|
||||||
This repository contains utilities for efficiently performing Win32 IO operations in
|
|
||||||
Go. Currently, this is focused on accessing named pipes and other file handles, and
|
|
||||||
for using named pipes as a net transport.
|
|
||||||
|
|
||||||
This code relies on IO completion ports to avoid blocking IO on system threads, allowing Go
|
|
||||||
to reuse the thread to schedule another goroutine. This limits support to Windows Vista and
|
|
||||||
newer operating systems. This is similar to the implementation of network sockets in Go's net
|
|
||||||
package.
|
|
||||||
|
|
||||||
Please see the LICENSE file for licensing information.
|
|
||||||
|
|
||||||
This project has adopted the [Microsoft Open Source Code of
|
|
||||||
Conduct](https://opensource.microsoft.com/codeofconduct/). For more information
|
|
||||||
see the [Code of Conduct
|
|
||||||
FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact
|
|
||||||
[opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional
|
|
||||||
questions or comments.
|
|
||||||
|
|
||||||
Thanks to natefinch for the inspiration for this library. See https://github.com/natefinch/npipe
|
|
||||||
for another named pipe implementation.
|
|
||||||
280
vendor/github.com/Microsoft/go-winio/backup.go
generated
vendored
280
vendor/github.com/Microsoft/go-winio/backup.go
generated
vendored
@ -1,280 +0,0 @@
|
|||||||
// +build windows
|
|
||||||
|
|
||||||
package winio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"syscall"
|
|
||||||
"unicode/utf16"
|
|
||||||
)
|
|
||||||
|
|
||||||
//sys backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead
|
|
||||||
//sys backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupWrite
|
|
||||||
|
|
||||||
const (
|
|
||||||
BackupData = uint32(iota + 1)
|
|
||||||
BackupEaData
|
|
||||||
BackupSecurity
|
|
||||||
BackupAlternateData
|
|
||||||
BackupLink
|
|
||||||
BackupPropertyData
|
|
||||||
BackupObjectId
|
|
||||||
BackupReparseData
|
|
||||||
BackupSparseBlock
|
|
||||||
BackupTxfsData
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
StreamSparseAttributes = uint32(8)
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
WRITE_DAC = 0x40000
|
|
||||||
WRITE_OWNER = 0x80000
|
|
||||||
ACCESS_SYSTEM_SECURITY = 0x1000000
|
|
||||||
)
|
|
||||||
|
|
||||||
// BackupHeader represents a backup stream of a file.
|
|
||||||
type BackupHeader struct {
|
|
||||||
Id uint32 // The backup stream ID
|
|
||||||
Attributes uint32 // Stream attributes
|
|
||||||
Size int64 // The size of the stream in bytes
|
|
||||||
Name string // The name of the stream (for BackupAlternateData only).
|
|
||||||
Offset int64 // The offset of the stream in the file (for BackupSparseBlock only).
|
|
||||||
}
|
|
||||||
|
|
||||||
type win32StreamId struct {
|
|
||||||
StreamId uint32
|
|
||||||
Attributes uint32
|
|
||||||
Size uint64
|
|
||||||
NameSize uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// BackupStreamReader reads from a stream produced by the BackupRead Win32 API and produces a series
|
|
||||||
// of BackupHeader values.
|
|
||||||
type BackupStreamReader struct {
|
|
||||||
r io.Reader
|
|
||||||
bytesLeft int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBackupStreamReader produces a BackupStreamReader from any io.Reader.
|
|
||||||
func NewBackupStreamReader(r io.Reader) *BackupStreamReader {
|
|
||||||
return &BackupStreamReader{r, 0}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next returns the next backup stream and prepares for calls to Read(). It skips the remainder of the current stream if
|
|
||||||
// it was not completely read.
|
|
||||||
func (r *BackupStreamReader) Next() (*BackupHeader, error) {
|
|
||||||
if r.bytesLeft > 0 {
|
|
||||||
if s, ok := r.r.(io.Seeker); ok {
|
|
||||||
// Make sure Seek on io.SeekCurrent sometimes succeeds
|
|
||||||
// before trying the actual seek.
|
|
||||||
if _, err := s.Seek(0, io.SeekCurrent); err == nil {
|
|
||||||
if _, err = s.Seek(r.bytesLeft, io.SeekCurrent); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
r.bytesLeft = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if _, err := io.Copy(ioutil.Discard, r); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var wsi win32StreamId
|
|
||||||
if err := binary.Read(r.r, binary.LittleEndian, &wsi); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
hdr := &BackupHeader{
|
|
||||||
Id: wsi.StreamId,
|
|
||||||
Attributes: wsi.Attributes,
|
|
||||||
Size: int64(wsi.Size),
|
|
||||||
}
|
|
||||||
if wsi.NameSize != 0 {
|
|
||||||
name := make([]uint16, int(wsi.NameSize/2))
|
|
||||||
if err := binary.Read(r.r, binary.LittleEndian, name); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
hdr.Name = syscall.UTF16ToString(name)
|
|
||||||
}
|
|
||||||
if wsi.StreamId == BackupSparseBlock {
|
|
||||||
if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
hdr.Size -= 8
|
|
||||||
}
|
|
||||||
r.bytesLeft = hdr.Size
|
|
||||||
return hdr, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read reads from the current backup stream.
|
|
||||||
func (r *BackupStreamReader) Read(b []byte) (int, error) {
|
|
||||||
if r.bytesLeft == 0 {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
if int64(len(b)) > r.bytesLeft {
|
|
||||||
b = b[:r.bytesLeft]
|
|
||||||
}
|
|
||||||
n, err := r.r.Read(b)
|
|
||||||
r.bytesLeft -= int64(n)
|
|
||||||
if err == io.EOF {
|
|
||||||
err = io.ErrUnexpectedEOF
|
|
||||||
} else if r.bytesLeft == 0 && err == nil {
|
|
||||||
err = io.EOF
|
|
||||||
}
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// BackupStreamWriter writes a stream compatible with the BackupWrite Win32 API.
|
|
||||||
type BackupStreamWriter struct {
|
|
||||||
w io.Writer
|
|
||||||
bytesLeft int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBackupStreamWriter produces a BackupStreamWriter on top of an io.Writer.
|
|
||||||
func NewBackupStreamWriter(w io.Writer) *BackupStreamWriter {
|
|
||||||
return &BackupStreamWriter{w, 0}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteHeader writes the next backup stream header and prepares for calls to Write().
|
|
||||||
func (w *BackupStreamWriter) WriteHeader(hdr *BackupHeader) error {
|
|
||||||
if w.bytesLeft != 0 {
|
|
||||||
return fmt.Errorf("missing %d bytes", w.bytesLeft)
|
|
||||||
}
|
|
||||||
name := utf16.Encode([]rune(hdr.Name))
|
|
||||||
wsi := win32StreamId{
|
|
||||||
StreamId: hdr.Id,
|
|
||||||
Attributes: hdr.Attributes,
|
|
||||||
Size: uint64(hdr.Size),
|
|
||||||
NameSize: uint32(len(name) * 2),
|
|
||||||
}
|
|
||||||
if hdr.Id == BackupSparseBlock {
|
|
||||||
// Include space for the int64 block offset
|
|
||||||
wsi.Size += 8
|
|
||||||
}
|
|
||||||
if err := binary.Write(w.w, binary.LittleEndian, &wsi); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(name) != 0 {
|
|
||||||
if err := binary.Write(w.w, binary.LittleEndian, name); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if hdr.Id == BackupSparseBlock {
|
|
||||||
if err := binary.Write(w.w, binary.LittleEndian, hdr.Offset); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w.bytesLeft = hdr.Size
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write writes to the current backup stream.
|
|
||||||
func (w *BackupStreamWriter) Write(b []byte) (int, error) {
|
|
||||||
if w.bytesLeft < int64(len(b)) {
|
|
||||||
return 0, fmt.Errorf("too many bytes by %d", int64(len(b))-w.bytesLeft)
|
|
||||||
}
|
|
||||||
n, err := w.w.Write(b)
|
|
||||||
w.bytesLeft -= int64(n)
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// BackupFileReader provides an io.ReadCloser interface on top of the BackupRead Win32 API.
|
|
||||||
type BackupFileReader struct {
|
|
||||||
f *os.File
|
|
||||||
includeSecurity bool
|
|
||||||
ctx uintptr
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBackupFileReader returns a new BackupFileReader from a file handle. If includeSecurity is true,
|
|
||||||
// Read will attempt to read the security descriptor of the file.
|
|
||||||
func NewBackupFileReader(f *os.File, includeSecurity bool) *BackupFileReader {
|
|
||||||
r := &BackupFileReader{f, includeSecurity, 0}
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read reads a backup stream from the file by calling the Win32 API BackupRead().
|
|
||||||
func (r *BackupFileReader) Read(b []byte) (int, error) {
|
|
||||||
var bytesRead uint32
|
|
||||||
err := backupRead(syscall.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx)
|
|
||||||
if err != nil {
|
|
||||||
return 0, &os.PathError{"BackupRead", r.f.Name(), err}
|
|
||||||
}
|
|
||||||
runtime.KeepAlive(r.f)
|
|
||||||
if bytesRead == 0 {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
return int(bytesRead), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close frees Win32 resources associated with the BackupFileReader. It does not close
|
|
||||||
// the underlying file.
|
|
||||||
func (r *BackupFileReader) Close() error {
|
|
||||||
if r.ctx != 0 {
|
|
||||||
backupRead(syscall.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx)
|
|
||||||
runtime.KeepAlive(r.f)
|
|
||||||
r.ctx = 0
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// BackupFileWriter provides an io.WriteCloser interface on top of the BackupWrite Win32 API.
|
|
||||||
type BackupFileWriter struct {
|
|
||||||
f *os.File
|
|
||||||
includeSecurity bool
|
|
||||||
ctx uintptr
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBackupFileWriter returns a new BackupFileWriter from a file handle. If includeSecurity is true,
|
|
||||||
// Write() will attempt to restore the security descriptor from the stream.
|
|
||||||
func NewBackupFileWriter(f *os.File, includeSecurity bool) *BackupFileWriter {
|
|
||||||
w := &BackupFileWriter{f, includeSecurity, 0}
|
|
||||||
return w
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write restores a portion of the file using the provided backup stream.
|
|
||||||
func (w *BackupFileWriter) Write(b []byte) (int, error) {
|
|
||||||
var bytesWritten uint32
|
|
||||||
err := backupWrite(syscall.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx)
|
|
||||||
if err != nil {
|
|
||||||
return 0, &os.PathError{"BackupWrite", w.f.Name(), err}
|
|
||||||
}
|
|
||||||
runtime.KeepAlive(w.f)
|
|
||||||
if int(bytesWritten) != len(b) {
|
|
||||||
return int(bytesWritten), errors.New("not all bytes could be written")
|
|
||||||
}
|
|
||||||
return len(b), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close frees Win32 resources associated with the BackupFileWriter. It does not
|
|
||||||
// close the underlying file.
|
|
||||||
func (w *BackupFileWriter) Close() error {
|
|
||||||
if w.ctx != 0 {
|
|
||||||
backupWrite(syscall.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx)
|
|
||||||
runtime.KeepAlive(w.f)
|
|
||||||
w.ctx = 0
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// OpenForBackup opens a file or directory, potentially skipping access checks if the backup
|
|
||||||
// or restore privileges have been acquired.
|
|
||||||
//
|
|
||||||
// If the file opened was a directory, it cannot be used with Readdir().
|
|
||||||
func OpenForBackup(path string, access uint32, share uint32, createmode uint32) (*os.File, error) {
|
|
||||||
winPath, err := syscall.UTF16FromString(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
h, err := syscall.CreateFile(&winPath[0], access, share, nil, createmode, syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OPEN_REPARSE_POINT, 0)
|
|
||||||
if err != nil {
|
|
||||||
err = &os.PathError{Op: "open", Path: path, Err: err}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return os.NewFile(uintptr(h), path), nil
|
|
||||||
}
|
|
||||||
137
vendor/github.com/Microsoft/go-winio/ea.go
generated
vendored
137
vendor/github.com/Microsoft/go-winio/ea.go
generated
vendored
@ -1,137 +0,0 @@
|
|||||||
package winio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
type fileFullEaInformation struct {
|
|
||||||
NextEntryOffset uint32
|
|
||||||
Flags uint8
|
|
||||||
NameLength uint8
|
|
||||||
ValueLength uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
fileFullEaInformationSize = binary.Size(&fileFullEaInformation{})
|
|
||||||
|
|
||||||
errInvalidEaBuffer = errors.New("invalid extended attribute buffer")
|
|
||||||
errEaNameTooLarge = errors.New("extended attribute name too large")
|
|
||||||
errEaValueTooLarge = errors.New("extended attribute value too large")
|
|
||||||
)
|
|
||||||
|
|
||||||
// ExtendedAttribute represents a single Windows EA.
|
|
||||||
type ExtendedAttribute struct {
|
|
||||||
Name string
|
|
||||||
Value []byte
|
|
||||||
Flags uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) {
|
|
||||||
var info fileFullEaInformation
|
|
||||||
err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info)
|
|
||||||
if err != nil {
|
|
||||||
err = errInvalidEaBuffer
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
nameOffset := fileFullEaInformationSize
|
|
||||||
nameLen := int(info.NameLength)
|
|
||||||
valueOffset := nameOffset + int(info.NameLength) + 1
|
|
||||||
valueLen := int(info.ValueLength)
|
|
||||||
nextOffset := int(info.NextEntryOffset)
|
|
||||||
if valueLen+valueOffset > len(b) || nextOffset < 0 || nextOffset > len(b) {
|
|
||||||
err = errInvalidEaBuffer
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ea.Name = string(b[nameOffset : nameOffset+nameLen])
|
|
||||||
ea.Value = b[valueOffset : valueOffset+valueLen]
|
|
||||||
ea.Flags = info.Flags
|
|
||||||
if info.NextEntryOffset != 0 {
|
|
||||||
nb = b[info.NextEntryOffset:]
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION
|
|
||||||
// buffer retrieved from BackupRead, ZwQueryEaFile, etc.
|
|
||||||
func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) {
|
|
||||||
for len(b) != 0 {
|
|
||||||
ea, nb, err := parseEa(b)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
eas = append(eas, ea)
|
|
||||||
b = nb
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error {
|
|
||||||
if int(uint8(len(ea.Name))) != len(ea.Name) {
|
|
||||||
return errEaNameTooLarge
|
|
||||||
}
|
|
||||||
if int(uint16(len(ea.Value))) != len(ea.Value) {
|
|
||||||
return errEaValueTooLarge
|
|
||||||
}
|
|
||||||
entrySize := uint32(fileFullEaInformationSize + len(ea.Name) + 1 + len(ea.Value))
|
|
||||||
withPadding := (entrySize + 3) &^ 3
|
|
||||||
nextOffset := uint32(0)
|
|
||||||
if !last {
|
|
||||||
nextOffset = withPadding
|
|
||||||
}
|
|
||||||
info := fileFullEaInformation{
|
|
||||||
NextEntryOffset: nextOffset,
|
|
||||||
Flags: ea.Flags,
|
|
||||||
NameLength: uint8(len(ea.Name)),
|
|
||||||
ValueLength: uint16(len(ea.Value)),
|
|
||||||
}
|
|
||||||
|
|
||||||
err := binary.Write(buf, binary.LittleEndian, &info)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = buf.Write([]byte(ea.Name))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = buf.WriteByte(0)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = buf.Write(ea.Value)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = buf.Write([]byte{0, 0, 0}[0 : withPadding-entrySize])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeExtendedAttributes encodes a list of EAs into a FILE_FULL_EA_INFORMATION
|
|
||||||
// buffer for use with BackupWrite, ZwSetEaFile, etc.
|
|
||||||
func EncodeExtendedAttributes(eas []ExtendedAttribute) ([]byte, error) {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
for i := range eas {
|
|
||||||
last := false
|
|
||||||
if i == len(eas)-1 {
|
|
||||||
last = true
|
|
||||||
}
|
|
||||||
|
|
||||||
err := writeEa(&buf, &eas[i], last)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return buf.Bytes(), nil
|
|
||||||
}
|
|
||||||
323
vendor/github.com/Microsoft/go-winio/file.go
generated
vendored
323
vendor/github.com/Microsoft/go-winio/file.go
generated
vendored
@ -1,323 +0,0 @@
|
|||||||
// +build windows
|
|
||||||
|
|
||||||
package winio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"runtime"
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
//sys cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) = CancelIoEx
|
|
||||||
//sys createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) = CreateIoCompletionPort
|
|
||||||
//sys getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus
|
|
||||||
//sys setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
|
|
||||||
//sys wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) = ws2_32.WSAGetOverlappedResult
|
|
||||||
|
|
||||||
type atomicBool int32
|
|
||||||
|
|
||||||
func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 }
|
|
||||||
func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) }
|
|
||||||
func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) }
|
|
||||||
func (b *atomicBool) swap(new bool) bool {
|
|
||||||
var newInt int32
|
|
||||||
if new {
|
|
||||||
newInt = 1
|
|
||||||
}
|
|
||||||
return atomic.SwapInt32((*int32)(b), newInt) == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1
|
|
||||||
cFILE_SKIP_SET_EVENT_ON_HANDLE = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrFileClosed = errors.New("file has already been closed")
|
|
||||||
ErrTimeout = &timeoutError{}
|
|
||||||
)
|
|
||||||
|
|
||||||
type timeoutError struct{}
|
|
||||||
|
|
||||||
func (e *timeoutError) Error() string { return "i/o timeout" }
|
|
||||||
func (e *timeoutError) Timeout() bool { return true }
|
|
||||||
func (e *timeoutError) Temporary() bool { return true }
|
|
||||||
|
|
||||||
type timeoutChan chan struct{}
|
|
||||||
|
|
||||||
var ioInitOnce sync.Once
|
|
||||||
var ioCompletionPort syscall.Handle
|
|
||||||
|
|
||||||
// ioResult contains the result of an asynchronous IO operation
|
|
||||||
type ioResult struct {
|
|
||||||
bytes uint32
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
// ioOperation represents an outstanding asynchronous Win32 IO
|
|
||||||
type ioOperation struct {
|
|
||||||
o syscall.Overlapped
|
|
||||||
ch chan ioResult
|
|
||||||
}
|
|
||||||
|
|
||||||
func initIo() {
|
|
||||||
h, err := createIoCompletionPort(syscall.InvalidHandle, 0, 0, 0xffffffff)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
ioCompletionPort = h
|
|
||||||
go ioCompletionProcessor(h)
|
|
||||||
}
|
|
||||||
|
|
||||||
// win32File implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall.
|
|
||||||
// It takes ownership of this handle and will close it if it is garbage collected.
|
|
||||||
type win32File struct {
|
|
||||||
handle syscall.Handle
|
|
||||||
wg sync.WaitGroup
|
|
||||||
wgLock sync.RWMutex
|
|
||||||
closing atomicBool
|
|
||||||
socket bool
|
|
||||||
readDeadline deadlineHandler
|
|
||||||
writeDeadline deadlineHandler
|
|
||||||
}
|
|
||||||
|
|
||||||
type deadlineHandler struct {
|
|
||||||
setLock sync.Mutex
|
|
||||||
channel timeoutChan
|
|
||||||
channelLock sync.RWMutex
|
|
||||||
timer *time.Timer
|
|
||||||
timedout atomicBool
|
|
||||||
}
|
|
||||||
|
|
||||||
// makeWin32File makes a new win32File from an existing file handle
|
|
||||||
func makeWin32File(h syscall.Handle) (*win32File, error) {
|
|
||||||
f := &win32File{handle: h}
|
|
||||||
ioInitOnce.Do(initIo)
|
|
||||||
_, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = setFileCompletionNotificationModes(h, cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS|cFILE_SKIP_SET_EVENT_ON_HANDLE)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
f.readDeadline.channel = make(timeoutChan)
|
|
||||||
f.writeDeadline.channel = make(timeoutChan)
|
|
||||||
return f, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
|
|
||||||
// If we return the result of makeWin32File directly, it can result in an
|
|
||||||
// interface-wrapped nil, rather than a nil interface value.
|
|
||||||
f, err := makeWin32File(h)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return f, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// closeHandle closes the resources associated with a Win32 handle
|
|
||||||
func (f *win32File) closeHandle() {
|
|
||||||
f.wgLock.Lock()
|
|
||||||
// Atomically set that we are closing, releasing the resources only once.
|
|
||||||
if !f.closing.swap(true) {
|
|
||||||
f.wgLock.Unlock()
|
|
||||||
// cancel all IO and wait for it to complete
|
|
||||||
cancelIoEx(f.handle, nil)
|
|
||||||
f.wg.Wait()
|
|
||||||
// at this point, no new IO can start
|
|
||||||
syscall.Close(f.handle)
|
|
||||||
f.handle = 0
|
|
||||||
} else {
|
|
||||||
f.wgLock.Unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes a win32File.
|
|
||||||
func (f *win32File) Close() error {
|
|
||||||
f.closeHandle()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// prepareIo prepares for a new IO operation.
|
|
||||||
// The caller must call f.wg.Done() when the IO is finished, prior to Close() returning.
|
|
||||||
func (f *win32File) prepareIo() (*ioOperation, error) {
|
|
||||||
f.wgLock.RLock()
|
|
||||||
if f.closing.isSet() {
|
|
||||||
f.wgLock.RUnlock()
|
|
||||||
return nil, ErrFileClosed
|
|
||||||
}
|
|
||||||
f.wg.Add(1)
|
|
||||||
f.wgLock.RUnlock()
|
|
||||||
c := &ioOperation{}
|
|
||||||
c.ch = make(chan ioResult)
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ioCompletionProcessor processes completed async IOs forever
|
|
||||||
func ioCompletionProcessor(h syscall.Handle) {
|
|
||||||
for {
|
|
||||||
var bytes uint32
|
|
||||||
var key uintptr
|
|
||||||
var op *ioOperation
|
|
||||||
err := getQueuedCompletionStatus(h, &bytes, &key, &op, syscall.INFINITE)
|
|
||||||
if op == nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
op.ch <- ioResult{bytes, err}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// asyncIo processes the return value from ReadFile or WriteFile, blocking until
|
|
||||||
// the operation has actually completed.
|
|
||||||
func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) {
|
|
||||||
if err != syscall.ERROR_IO_PENDING {
|
|
||||||
return int(bytes), err
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.closing.isSet() {
|
|
||||||
cancelIoEx(f.handle, &c.o)
|
|
||||||
}
|
|
||||||
|
|
||||||
var timeout timeoutChan
|
|
||||||
if d != nil {
|
|
||||||
d.channelLock.Lock()
|
|
||||||
timeout = d.channel
|
|
||||||
d.channelLock.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
var r ioResult
|
|
||||||
select {
|
|
||||||
case r = <-c.ch:
|
|
||||||
err = r.err
|
|
||||||
if err == syscall.ERROR_OPERATION_ABORTED {
|
|
||||||
if f.closing.isSet() {
|
|
||||||
err = ErrFileClosed
|
|
||||||
}
|
|
||||||
} else if err != nil && f.socket {
|
|
||||||
// err is from Win32. Query the overlapped structure to get the winsock error.
|
|
||||||
var bytes, flags uint32
|
|
||||||
err = wsaGetOverlappedResult(f.handle, &c.o, &bytes, false, &flags)
|
|
||||||
}
|
|
||||||
case <-timeout:
|
|
||||||
cancelIoEx(f.handle, &c.o)
|
|
||||||
r = <-c.ch
|
|
||||||
err = r.err
|
|
||||||
if err == syscall.ERROR_OPERATION_ABORTED {
|
|
||||||
err = ErrTimeout
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// runtime.KeepAlive is needed, as c is passed via native
|
|
||||||
// code to ioCompletionProcessor, c must remain alive
|
|
||||||
// until the channel read is complete.
|
|
||||||
runtime.KeepAlive(c)
|
|
||||||
return int(r.bytes), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read reads from a file handle.
|
|
||||||
func (f *win32File) Read(b []byte) (int, error) {
|
|
||||||
c, err := f.prepareIo()
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
defer f.wg.Done()
|
|
||||||
|
|
||||||
if f.readDeadline.timedout.isSet() {
|
|
||||||
return 0, ErrTimeout
|
|
||||||
}
|
|
||||||
|
|
||||||
var bytes uint32
|
|
||||||
err = syscall.ReadFile(f.handle, b, &bytes, &c.o)
|
|
||||||
n, err := f.asyncIo(c, &f.readDeadline, bytes, err)
|
|
||||||
runtime.KeepAlive(b)
|
|
||||||
|
|
||||||
// Handle EOF conditions.
|
|
||||||
if err == nil && n == 0 && len(b) != 0 {
|
|
||||||
return 0, io.EOF
|
|
||||||
} else if err == syscall.ERROR_BROKEN_PIPE {
|
|
||||||
return 0, io.EOF
|
|
||||||
} else {
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write writes to a file handle.
|
|
||||||
func (f *win32File) Write(b []byte) (int, error) {
|
|
||||||
c, err := f.prepareIo()
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
defer f.wg.Done()
|
|
||||||
|
|
||||||
if f.writeDeadline.timedout.isSet() {
|
|
||||||
return 0, ErrTimeout
|
|
||||||
}
|
|
||||||
|
|
||||||
var bytes uint32
|
|
||||||
err = syscall.WriteFile(f.handle, b, &bytes, &c.o)
|
|
||||||
n, err := f.asyncIo(c, &f.writeDeadline, bytes, err)
|
|
||||||
runtime.KeepAlive(b)
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *win32File) SetReadDeadline(deadline time.Time) error {
|
|
||||||
return f.readDeadline.set(deadline)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *win32File) SetWriteDeadline(deadline time.Time) error {
|
|
||||||
return f.writeDeadline.set(deadline)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *win32File) Flush() error {
|
|
||||||
return syscall.FlushFileBuffers(f.handle)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *win32File) Fd() uintptr {
|
|
||||||
return uintptr(f.handle)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *deadlineHandler) set(deadline time.Time) error {
|
|
||||||
d.setLock.Lock()
|
|
||||||
defer d.setLock.Unlock()
|
|
||||||
|
|
||||||
if d.timer != nil {
|
|
||||||
if !d.timer.Stop() {
|
|
||||||
<-d.channel
|
|
||||||
}
|
|
||||||
d.timer = nil
|
|
||||||
}
|
|
||||||
d.timedout.setFalse()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-d.channel:
|
|
||||||
d.channelLock.Lock()
|
|
||||||
d.channel = make(chan struct{})
|
|
||||||
d.channelLock.Unlock()
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
if deadline.IsZero() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
timeoutIO := func() {
|
|
||||||
d.timedout.setTrue()
|
|
||||||
close(d.channel)
|
|
||||||
}
|
|
||||||
|
|
||||||
now := time.Now()
|
|
||||||
duration := deadline.Sub(now)
|
|
||||||
if deadline.After(now) {
|
|
||||||
// Deadline is in the future, set a timer to wait
|
|
||||||
d.timer = time.AfterFunc(duration, timeoutIO)
|
|
||||||
} else {
|
|
||||||
// Deadline is in the past. Cancel all pending IO now.
|
|
||||||
timeoutIO()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
61
vendor/github.com/Microsoft/go-winio/fileinfo.go
generated
vendored
61
vendor/github.com/Microsoft/go-winio/fileinfo.go
generated
vendored
@ -1,61 +0,0 @@
|
|||||||
// +build windows
|
|
||||||
|
|
||||||
package winio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
//sys getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = GetFileInformationByHandleEx
|
|
||||||
//sys setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = SetFileInformationByHandle
|
|
||||||
|
|
||||||
const (
|
|
||||||
fileBasicInfo = 0
|
|
||||||
fileIDInfo = 0x12
|
|
||||||
)
|
|
||||||
|
|
||||||
// FileBasicInfo contains file access time and file attributes information.
|
|
||||||
type FileBasicInfo struct {
|
|
||||||
CreationTime, LastAccessTime, LastWriteTime, ChangeTime syscall.Filetime
|
|
||||||
FileAttributes uint32
|
|
||||||
pad uint32 // padding
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFileBasicInfo retrieves times and attributes for a file.
|
|
||||||
func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) {
|
|
||||||
bi := &FileBasicInfo{}
|
|
||||||
if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil {
|
|
||||||
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
|
|
||||||
}
|
|
||||||
runtime.KeepAlive(f)
|
|
||||||
return bi, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetFileBasicInfo sets times and attributes for a file.
|
|
||||||
func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error {
|
|
||||||
if err := setFileInformationByHandle(syscall.Handle(f.Fd()), fileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil {
|
|
||||||
return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err}
|
|
||||||
}
|
|
||||||
runtime.KeepAlive(f)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FileIDInfo contains the volume serial number and file ID for a file. This pair should be
|
|
||||||
// unique on a system.
|
|
||||||
type FileIDInfo struct {
|
|
||||||
VolumeSerialNumber uint64
|
|
||||||
FileID [16]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFileID retrieves the unique (volume, file ID) pair for a file.
|
|
||||||
func GetFileID(f *os.File) (*FileIDInfo, error) {
|
|
||||||
fileID := &FileIDInfo{}
|
|
||||||
if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileIDInfo, (*byte)(unsafe.Pointer(fileID)), uint32(unsafe.Sizeof(*fileID))); err != nil {
|
|
||||||
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
|
|
||||||
}
|
|
||||||
runtime.KeepAlive(f)
|
|
||||||
return fileID, nil
|
|
||||||
}
|
|
||||||
9
vendor/github.com/Microsoft/go-winio/go.mod
generated
vendored
9
vendor/github.com/Microsoft/go-winio/go.mod
generated
vendored
@ -1,9 +0,0 @@
|
|||||||
module github.com/Microsoft/go-winio
|
|
||||||
|
|
||||||
go 1.12
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/pkg/errors v0.8.1
|
|
||||||
github.com/sirupsen/logrus v1.4.1
|
|
||||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b
|
|
||||||
)
|
|
||||||
16
vendor/github.com/Microsoft/go-winio/go.sum
generated
vendored
16
vendor/github.com/Microsoft/go-winio/go.sum
generated
vendored
@ -1,16 +0,0 @@
|
|||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
|
||||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
||||||
github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k=
|
|
||||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qdNLDHHtQ4mlgQIZPPNA=
|
|
||||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
305
vendor/github.com/Microsoft/go-winio/hvsock.go
generated
vendored
305
vendor/github.com/Microsoft/go-winio/hvsock.go
generated
vendored
@ -1,305 +0,0 @@
|
|||||||
package winio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"github.com/Microsoft/go-winio/pkg/guid"
|
|
||||||
)
|
|
||||||
|
|
||||||
//sys bind(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) [failretval==socketError] = ws2_32.bind
|
|
||||||
|
|
||||||
const (
|
|
||||||
afHvSock = 34 // AF_HYPERV
|
|
||||||
|
|
||||||
socketError = ^uintptr(0)
|
|
||||||
)
|
|
||||||
|
|
||||||
// An HvsockAddr is an address for a AF_HYPERV socket.
|
|
||||||
type HvsockAddr struct {
|
|
||||||
VMID guid.GUID
|
|
||||||
ServiceID guid.GUID
|
|
||||||
}
|
|
||||||
|
|
||||||
type rawHvsockAddr struct {
|
|
||||||
Family uint16
|
|
||||||
_ uint16
|
|
||||||
VMID guid.GUID
|
|
||||||
ServiceID guid.GUID
|
|
||||||
}
|
|
||||||
|
|
||||||
// Network returns the address's network name, "hvsock".
|
|
||||||
func (addr *HvsockAddr) Network() string {
|
|
||||||
return "hvsock"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (addr *HvsockAddr) String() string {
|
|
||||||
return fmt.Sprintf("%s:%s", &addr.VMID, &addr.ServiceID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// VsockServiceID returns an hvsock service ID corresponding to the specified AF_VSOCK port.
|
|
||||||
func VsockServiceID(port uint32) guid.GUID {
|
|
||||||
g, _ := guid.FromString("00000000-facb-11e6-bd58-64006a7986d3")
|
|
||||||
g.Data1 = port
|
|
||||||
return g
|
|
||||||
}
|
|
||||||
|
|
||||||
func (addr *HvsockAddr) raw() rawHvsockAddr {
|
|
||||||
return rawHvsockAddr{
|
|
||||||
Family: afHvSock,
|
|
||||||
VMID: addr.VMID,
|
|
||||||
ServiceID: addr.ServiceID,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (addr *HvsockAddr) fromRaw(raw *rawHvsockAddr) {
|
|
||||||
addr.VMID = raw.VMID
|
|
||||||
addr.ServiceID = raw.ServiceID
|
|
||||||
}
|
|
||||||
|
|
||||||
// HvsockListener is a socket listener for the AF_HYPERV address family.
|
|
||||||
type HvsockListener struct {
|
|
||||||
sock *win32File
|
|
||||||
addr HvsockAddr
|
|
||||||
}
|
|
||||||
|
|
||||||
// HvsockConn is a connected socket of the AF_HYPERV address family.
|
|
||||||
type HvsockConn struct {
|
|
||||||
sock *win32File
|
|
||||||
local, remote HvsockAddr
|
|
||||||
}
|
|
||||||
|
|
||||||
func newHvSocket() (*win32File, error) {
|
|
||||||
fd, err := syscall.Socket(afHvSock, syscall.SOCK_STREAM, 1)
|
|
||||||
if err != nil {
|
|
||||||
return nil, os.NewSyscallError("socket", err)
|
|
||||||
}
|
|
||||||
f, err := makeWin32File(fd)
|
|
||||||
if err != nil {
|
|
||||||
syscall.Close(fd)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
f.socket = true
|
|
||||||
return f, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListenHvsock listens for connections on the specified hvsock address.
|
|
||||||
func ListenHvsock(addr *HvsockAddr) (_ *HvsockListener, err error) {
|
|
||||||
l := &HvsockListener{addr: *addr}
|
|
||||||
sock, err := newHvSocket()
|
|
||||||
if err != nil {
|
|
||||||
return nil, l.opErr("listen", err)
|
|
||||||
}
|
|
||||||
sa := addr.raw()
|
|
||||||
err = bind(sock.handle, unsafe.Pointer(&sa), int32(unsafe.Sizeof(sa)))
|
|
||||||
if err != nil {
|
|
||||||
return nil, l.opErr("listen", os.NewSyscallError("socket", err))
|
|
||||||
}
|
|
||||||
err = syscall.Listen(sock.handle, 16)
|
|
||||||
if err != nil {
|
|
||||||
return nil, l.opErr("listen", os.NewSyscallError("listen", err))
|
|
||||||
}
|
|
||||||
return &HvsockListener{sock: sock, addr: *addr}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *HvsockListener) opErr(op string, err error) error {
|
|
||||||
return &net.OpError{Op: op, Net: "hvsock", Addr: &l.addr, Err: err}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Addr returns the listener's network address.
|
|
||||||
func (l *HvsockListener) Addr() net.Addr {
|
|
||||||
return &l.addr
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accept waits for the next connection and returns it.
|
|
||||||
func (l *HvsockListener) Accept() (_ net.Conn, err error) {
|
|
||||||
sock, err := newHvSocket()
|
|
||||||
if err != nil {
|
|
||||||
return nil, l.opErr("accept", err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if sock != nil {
|
|
||||||
sock.Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
c, err := l.sock.prepareIo()
|
|
||||||
if err != nil {
|
|
||||||
return nil, l.opErr("accept", err)
|
|
||||||
}
|
|
||||||
defer l.sock.wg.Done()
|
|
||||||
|
|
||||||
// AcceptEx, per documentation, requires an extra 16 bytes per address.
|
|
||||||
const addrlen = uint32(16 + unsafe.Sizeof(rawHvsockAddr{}))
|
|
||||||
var addrbuf [addrlen * 2]byte
|
|
||||||
|
|
||||||
var bytes uint32
|
|
||||||
err = syscall.AcceptEx(l.sock.handle, sock.handle, &addrbuf[0], 0, addrlen, addrlen, &bytes, &c.o)
|
|
||||||
_, err = l.sock.asyncIo(c, nil, bytes, err)
|
|
||||||
if err != nil {
|
|
||||||
return nil, l.opErr("accept", os.NewSyscallError("acceptex", err))
|
|
||||||
}
|
|
||||||
conn := &HvsockConn{
|
|
||||||
sock: sock,
|
|
||||||
}
|
|
||||||
conn.local.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[0])))
|
|
||||||
conn.remote.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[addrlen])))
|
|
||||||
sock = nil
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes the listener, causing any pending Accept calls to fail.
|
|
||||||
func (l *HvsockListener) Close() error {
|
|
||||||
return l.sock.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Need to finish ConnectEx handling
|
|
||||||
func DialHvsock(ctx context.Context, addr *HvsockAddr) (*HvsockConn, error) {
|
|
||||||
sock, err := newHvSocket()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if sock != nil {
|
|
||||||
sock.Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
c, err := sock.prepareIo()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer sock.wg.Done()
|
|
||||||
var bytes uint32
|
|
||||||
err = windows.ConnectEx(windows.Handle(sock.handle), sa, nil, 0, &bytes, &c.o)
|
|
||||||
_, err = sock.asyncIo(ctx, c, nil, bytes, err)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
conn := &HvsockConn{
|
|
||||||
sock: sock,
|
|
||||||
remote: *addr,
|
|
||||||
}
|
|
||||||
sock = nil
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
func (conn *HvsockConn) opErr(op string, err error) error {
|
|
||||||
return &net.OpError{Op: op, Net: "hvsock", Source: &conn.local, Addr: &conn.remote, Err: err}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (conn *HvsockConn) Read(b []byte) (int, error) {
|
|
||||||
c, err := conn.sock.prepareIo()
|
|
||||||
if err != nil {
|
|
||||||
return 0, conn.opErr("read", err)
|
|
||||||
}
|
|
||||||
defer conn.sock.wg.Done()
|
|
||||||
buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))}
|
|
||||||
var flags, bytes uint32
|
|
||||||
err = syscall.WSARecv(conn.sock.handle, &buf, 1, &bytes, &flags, &c.o, nil)
|
|
||||||
n, err := conn.sock.asyncIo(c, &conn.sock.readDeadline, bytes, err)
|
|
||||||
if err != nil {
|
|
||||||
if _, ok := err.(syscall.Errno); ok {
|
|
||||||
err = os.NewSyscallError("wsarecv", err)
|
|
||||||
}
|
|
||||||
return 0, conn.opErr("read", err)
|
|
||||||
} else if n == 0 {
|
|
||||||
err = io.EOF
|
|
||||||
}
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (conn *HvsockConn) Write(b []byte) (int, error) {
|
|
||||||
t := 0
|
|
||||||
for len(b) != 0 {
|
|
||||||
n, err := conn.write(b)
|
|
||||||
if err != nil {
|
|
||||||
return t + n, err
|
|
||||||
}
|
|
||||||
t += n
|
|
||||||
b = b[n:]
|
|
||||||
}
|
|
||||||
return t, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (conn *HvsockConn) write(b []byte) (int, error) {
|
|
||||||
c, err := conn.sock.prepareIo()
|
|
||||||
if err != nil {
|
|
||||||
return 0, conn.opErr("write", err)
|
|
||||||
}
|
|
||||||
defer conn.sock.wg.Done()
|
|
||||||
buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))}
|
|
||||||
var bytes uint32
|
|
||||||
err = syscall.WSASend(conn.sock.handle, &buf, 1, &bytes, 0, &c.o, nil)
|
|
||||||
n, err := conn.sock.asyncIo(c, &conn.sock.writeDeadline, bytes, err)
|
|
||||||
if err != nil {
|
|
||||||
if _, ok := err.(syscall.Errno); ok {
|
|
||||||
err = os.NewSyscallError("wsasend", err)
|
|
||||||
}
|
|
||||||
return 0, conn.opErr("write", err)
|
|
||||||
}
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes the socket connection, failing any pending read or write calls.
|
|
||||||
func (conn *HvsockConn) Close() error {
|
|
||||||
return conn.sock.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (conn *HvsockConn) shutdown(how int) error {
|
|
||||||
err := syscall.Shutdown(conn.sock.handle, syscall.SHUT_RD)
|
|
||||||
if err != nil {
|
|
||||||
return os.NewSyscallError("shutdown", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CloseRead shuts down the read end of the socket.
|
|
||||||
func (conn *HvsockConn) CloseRead() error {
|
|
||||||
err := conn.shutdown(syscall.SHUT_RD)
|
|
||||||
if err != nil {
|
|
||||||
return conn.opErr("close", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CloseWrite shuts down the write end of the socket, notifying the other endpoint that
|
|
||||||
// no more data will be written.
|
|
||||||
func (conn *HvsockConn) CloseWrite() error {
|
|
||||||
err := conn.shutdown(syscall.SHUT_WR)
|
|
||||||
if err != nil {
|
|
||||||
return conn.opErr("close", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// LocalAddr returns the local address of the connection.
|
|
||||||
func (conn *HvsockConn) LocalAddr() net.Addr {
|
|
||||||
return &conn.local
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoteAddr returns the remote address of the connection.
|
|
||||||
func (conn *HvsockConn) RemoteAddr() net.Addr {
|
|
||||||
return &conn.remote
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetDeadline implements the net.Conn SetDeadline method.
|
|
||||||
func (conn *HvsockConn) SetDeadline(t time.Time) error {
|
|
||||||
conn.SetReadDeadline(t)
|
|
||||||
conn.SetWriteDeadline(t)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetReadDeadline implements the net.Conn SetReadDeadline method.
|
|
||||||
func (conn *HvsockConn) SetReadDeadline(t time.Time) error {
|
|
||||||
return conn.sock.SetReadDeadline(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetWriteDeadline implements the net.Conn SetWriteDeadline method.
|
|
||||||
func (conn *HvsockConn) SetWriteDeadline(t time.Time) error {
|
|
||||||
return conn.sock.SetWriteDeadline(t)
|
|
||||||
}
|
|
||||||
510
vendor/github.com/Microsoft/go-winio/pipe.go
generated
vendored
510
vendor/github.com/Microsoft/go-winio/pipe.go
generated
vendored
@ -1,510 +0,0 @@
|
|||||||
// +build windows
|
|
||||||
|
|
||||||
package winio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
//sys connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) = ConnectNamedPipe
|
|
||||||
//sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateNamedPipeW
|
|
||||||
//sys createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateFileW
|
|
||||||
//sys getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo
|
|
||||||
//sys getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
|
|
||||||
//sys localAlloc(uFlags uint32, length uint32) (ptr uintptr) = LocalAlloc
|
|
||||||
//sys ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) = ntdll.NtCreateNamedPipeFile
|
|
||||||
//sys rtlNtStatusToDosError(status ntstatus) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb
|
|
||||||
//sys rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntstatus) = ntdll.RtlDosPathNameToNtPathName_U
|
|
||||||
//sys rtlDefaultNpAcl(dacl *uintptr) (status ntstatus) = ntdll.RtlDefaultNpAcl
|
|
||||||
|
|
||||||
type ioStatusBlock struct {
|
|
||||||
Status, Information uintptr
|
|
||||||
}
|
|
||||||
|
|
||||||
type objectAttributes struct {
|
|
||||||
Length uintptr
|
|
||||||
RootDirectory uintptr
|
|
||||||
ObjectName *unicodeString
|
|
||||||
Attributes uintptr
|
|
||||||
SecurityDescriptor *securityDescriptor
|
|
||||||
SecurityQoS uintptr
|
|
||||||
}
|
|
||||||
|
|
||||||
type unicodeString struct {
|
|
||||||
Length uint16
|
|
||||||
MaximumLength uint16
|
|
||||||
Buffer uintptr
|
|
||||||
}
|
|
||||||
|
|
||||||
type securityDescriptor struct {
|
|
||||||
Revision byte
|
|
||||||
Sbz1 byte
|
|
||||||
Control uint16
|
|
||||||
Owner uintptr
|
|
||||||
Group uintptr
|
|
||||||
Sacl uintptr
|
|
||||||
Dacl uintptr
|
|
||||||
}
|
|
||||||
|
|
||||||
type ntstatus int32
|
|
||||||
|
|
||||||
func (status ntstatus) Err() error {
|
|
||||||
if status >= 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return rtlNtStatusToDosError(status)
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
cERROR_PIPE_BUSY = syscall.Errno(231)
|
|
||||||
cERROR_NO_DATA = syscall.Errno(232)
|
|
||||||
cERROR_PIPE_CONNECTED = syscall.Errno(535)
|
|
||||||
cERROR_SEM_TIMEOUT = syscall.Errno(121)
|
|
||||||
|
|
||||||
cSECURITY_SQOS_PRESENT = 0x100000
|
|
||||||
cSECURITY_ANONYMOUS = 0
|
|
||||||
|
|
||||||
cPIPE_TYPE_MESSAGE = 4
|
|
||||||
|
|
||||||
cPIPE_READMODE_MESSAGE = 2
|
|
||||||
|
|
||||||
cFILE_OPEN = 1
|
|
||||||
cFILE_CREATE = 2
|
|
||||||
|
|
||||||
cFILE_PIPE_MESSAGE_TYPE = 1
|
|
||||||
cFILE_PIPE_REJECT_REMOTE_CLIENTS = 2
|
|
||||||
|
|
||||||
cSE_DACL_PRESENT = 4
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// ErrPipeListenerClosed is returned for pipe operations on listeners that have been closed.
|
|
||||||
// This error should match net.errClosing since docker takes a dependency on its text.
|
|
||||||
ErrPipeListenerClosed = errors.New("use of closed network connection")
|
|
||||||
|
|
||||||
errPipeWriteClosed = errors.New("pipe has been closed for write")
|
|
||||||
)
|
|
||||||
|
|
||||||
type win32Pipe struct {
|
|
||||||
*win32File
|
|
||||||
path string
|
|
||||||
}
|
|
||||||
|
|
||||||
type win32MessageBytePipe struct {
|
|
||||||
win32Pipe
|
|
||||||
writeClosed bool
|
|
||||||
readEOF bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type pipeAddress string
|
|
||||||
|
|
||||||
func (f *win32Pipe) LocalAddr() net.Addr {
|
|
||||||
return pipeAddress(f.path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *win32Pipe) RemoteAddr() net.Addr {
|
|
||||||
return pipeAddress(f.path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *win32Pipe) SetDeadline(t time.Time) error {
|
|
||||||
f.SetReadDeadline(t)
|
|
||||||
f.SetWriteDeadline(t)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CloseWrite closes the write side of a message pipe in byte mode.
|
|
||||||
func (f *win32MessageBytePipe) CloseWrite() error {
|
|
||||||
if f.writeClosed {
|
|
||||||
return errPipeWriteClosed
|
|
||||||
}
|
|
||||||
err := f.win32File.Flush()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = f.win32File.Write(nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
f.writeClosed = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write writes bytes to a message pipe in byte mode. Zero-byte writes are ignored, since
|
|
||||||
// they are used to implement CloseWrite().
|
|
||||||
func (f *win32MessageBytePipe) Write(b []byte) (int, error) {
|
|
||||||
if f.writeClosed {
|
|
||||||
return 0, errPipeWriteClosed
|
|
||||||
}
|
|
||||||
if len(b) == 0 {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
return f.win32File.Write(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read reads bytes from a message pipe in byte mode. A read of a zero-byte message on a message
|
|
||||||
// mode pipe will return io.EOF, as will all subsequent reads.
|
|
||||||
func (f *win32MessageBytePipe) Read(b []byte) (int, error) {
|
|
||||||
if f.readEOF {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
n, err := f.win32File.Read(b)
|
|
||||||
if err == io.EOF {
|
|
||||||
// If this was the result of a zero-byte read, then
|
|
||||||
// it is possible that the read was due to a zero-size
|
|
||||||
// message. Since we are simulating CloseWrite with a
|
|
||||||
// zero-byte message, ensure that all future Read() calls
|
|
||||||
// also return EOF.
|
|
||||||
f.readEOF = true
|
|
||||||
} else if err == syscall.ERROR_MORE_DATA {
|
|
||||||
// ERROR_MORE_DATA indicates that the pipe's read mode is message mode
|
|
||||||
// and the message still has more bytes. Treat this as a success, since
|
|
||||||
// this package presents all named pipes as byte streams.
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s pipeAddress) Network() string {
|
|
||||||
return "pipe"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s pipeAddress) String() string {
|
|
||||||
return string(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// tryDialPipe attempts to dial the pipe at `path` until `ctx` cancellation or timeout.
|
|
||||||
func tryDialPipe(ctx context.Context, path *string) (syscall.Handle, error) {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return syscall.Handle(0), ctx.Err()
|
|
||||||
default:
|
|
||||||
h, err := createFile(*path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0)
|
|
||||||
if err == nil {
|
|
||||||
return h, nil
|
|
||||||
}
|
|
||||||
if err != cERROR_PIPE_BUSY {
|
|
||||||
return h, &os.PathError{Err: err, Op: "open", Path: *path}
|
|
||||||
}
|
|
||||||
// Wait 10 msec and try again. This is a rather simplistic
|
|
||||||
// view, as we always try each 10 milliseconds.
|
|
||||||
time.Sleep(time.Millisecond * 10)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DialPipe connects to a named pipe by path, timing out if the connection
|
|
||||||
// takes longer than the specified duration. If timeout is nil, then we use
|
|
||||||
// a default timeout of 2 seconds. (We do not use WaitNamedPipe.)
|
|
||||||
func DialPipe(path string, timeout *time.Duration) (net.Conn, error) {
|
|
||||||
var absTimeout time.Time
|
|
||||||
if timeout != nil {
|
|
||||||
absTimeout = time.Now().Add(*timeout)
|
|
||||||
} else {
|
|
||||||
absTimeout = time.Now().Add(time.Second * 2)
|
|
||||||
}
|
|
||||||
ctx, _ := context.WithDeadline(context.Background(), absTimeout)
|
|
||||||
conn, err := DialPipeContext(ctx, path)
|
|
||||||
if err == context.DeadlineExceeded {
|
|
||||||
return nil, ErrTimeout
|
|
||||||
}
|
|
||||||
return conn, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// DialPipeContext attempts to connect to a named pipe by `path` until `ctx`
|
|
||||||
// cancellation or timeout.
|
|
||||||
func DialPipeContext(ctx context.Context, path string) (net.Conn, error) {
|
|
||||||
var err error
|
|
||||||
var h syscall.Handle
|
|
||||||
h, err = tryDialPipe(ctx, &path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var flags uint32
|
|
||||||
err = getNamedPipeInfo(h, &flags, nil, nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := makeWin32File(h)
|
|
||||||
if err != nil {
|
|
||||||
syscall.Close(h)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the pipe is in message mode, return a message byte pipe, which
|
|
||||||
// supports CloseWrite().
|
|
||||||
if flags&cPIPE_TYPE_MESSAGE != 0 {
|
|
||||||
return &win32MessageBytePipe{
|
|
||||||
win32Pipe: win32Pipe{win32File: f, path: path},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
return &win32Pipe{win32File: f, path: path}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type acceptResponse struct {
|
|
||||||
f *win32File
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
type win32PipeListener struct {
|
|
||||||
firstHandle syscall.Handle
|
|
||||||
path string
|
|
||||||
config PipeConfig
|
|
||||||
acceptCh chan (chan acceptResponse)
|
|
||||||
closeCh chan int
|
|
||||||
doneCh chan int
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (syscall.Handle, error) {
|
|
||||||
path16, err := syscall.UTF16FromString(path)
|
|
||||||
if err != nil {
|
|
||||||
return 0, &os.PathError{Op: "open", Path: path, Err: err}
|
|
||||||
}
|
|
||||||
|
|
||||||
var oa objectAttributes
|
|
||||||
oa.Length = unsafe.Sizeof(oa)
|
|
||||||
|
|
||||||
var ntPath unicodeString
|
|
||||||
if err := rtlDosPathNameToNtPathName(&path16[0], &ntPath, 0, 0).Err(); err != nil {
|
|
||||||
return 0, &os.PathError{Op: "open", Path: path, Err: err}
|
|
||||||
}
|
|
||||||
defer localFree(ntPath.Buffer)
|
|
||||||
oa.ObjectName = &ntPath
|
|
||||||
|
|
||||||
// The security descriptor is only needed for the first pipe.
|
|
||||||
if first {
|
|
||||||
if sd != nil {
|
|
||||||
len := uint32(len(sd))
|
|
||||||
sdb := localAlloc(0, len)
|
|
||||||
defer localFree(sdb)
|
|
||||||
copy((*[0xffff]byte)(unsafe.Pointer(sdb))[:], sd)
|
|
||||||
oa.SecurityDescriptor = (*securityDescriptor)(unsafe.Pointer(sdb))
|
|
||||||
} else {
|
|
||||||
// Construct the default named pipe security descriptor.
|
|
||||||
var dacl uintptr
|
|
||||||
if err := rtlDefaultNpAcl(&dacl).Err(); err != nil {
|
|
||||||
return 0, fmt.Errorf("getting default named pipe ACL: %s", err)
|
|
||||||
}
|
|
||||||
defer localFree(dacl)
|
|
||||||
|
|
||||||
sdb := &securityDescriptor{
|
|
||||||
Revision: 1,
|
|
||||||
Control: cSE_DACL_PRESENT,
|
|
||||||
Dacl: dacl,
|
|
||||||
}
|
|
||||||
oa.SecurityDescriptor = sdb
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
typ := uint32(cFILE_PIPE_REJECT_REMOTE_CLIENTS)
|
|
||||||
if c.MessageMode {
|
|
||||||
typ |= cFILE_PIPE_MESSAGE_TYPE
|
|
||||||
}
|
|
||||||
|
|
||||||
disposition := uint32(cFILE_OPEN)
|
|
||||||
access := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | syscall.SYNCHRONIZE)
|
|
||||||
if first {
|
|
||||||
disposition = cFILE_CREATE
|
|
||||||
// By not asking for read or write access, the named pipe file system
|
|
||||||
// will put this pipe into an initially disconnected state, blocking
|
|
||||||
// client connections until the next call with first == false.
|
|
||||||
access = syscall.SYNCHRONIZE
|
|
||||||
}
|
|
||||||
|
|
||||||
timeout := int64(-50 * 10000) // 50ms
|
|
||||||
|
|
||||||
var (
|
|
||||||
h syscall.Handle
|
|
||||||
iosb ioStatusBlock
|
|
||||||
)
|
|
||||||
err = ntCreateNamedPipeFile(&h, access, &oa, &iosb, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE, disposition, 0, typ, 0, 0, 0xffffffff, uint32(c.InputBufferSize), uint32(c.OutputBufferSize), &timeout).Err()
|
|
||||||
if err != nil {
|
|
||||||
return 0, &os.PathError{Op: "open", Path: path, Err: err}
|
|
||||||
}
|
|
||||||
|
|
||||||
runtime.KeepAlive(ntPath)
|
|
||||||
return h, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *win32PipeListener) makeServerPipe() (*win32File, error) {
|
|
||||||
h, err := makeServerPipeHandle(l.path, nil, &l.config, false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
f, err := makeWin32File(h)
|
|
||||||
if err != nil {
|
|
||||||
syscall.Close(h)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return f, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *win32PipeListener) makeConnectedServerPipe() (*win32File, error) {
|
|
||||||
p, err := l.makeServerPipe()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for the client to connect.
|
|
||||||
ch := make(chan error)
|
|
||||||
go func(p *win32File) {
|
|
||||||
ch <- connectPipe(p)
|
|
||||||
}(p)
|
|
||||||
|
|
||||||
select {
|
|
||||||
case err = <-ch:
|
|
||||||
if err != nil {
|
|
||||||
p.Close()
|
|
||||||
p = nil
|
|
||||||
}
|
|
||||||
case <-l.closeCh:
|
|
||||||
// Abort the connect request by closing the handle.
|
|
||||||
p.Close()
|
|
||||||
p = nil
|
|
||||||
err = <-ch
|
|
||||||
if err == nil || err == ErrFileClosed {
|
|
||||||
err = ErrPipeListenerClosed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return p, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *win32PipeListener) listenerRoutine() {
|
|
||||||
closed := false
|
|
||||||
for !closed {
|
|
||||||
select {
|
|
||||||
case <-l.closeCh:
|
|
||||||
closed = true
|
|
||||||
case responseCh := <-l.acceptCh:
|
|
||||||
var (
|
|
||||||
p *win32File
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
for {
|
|
||||||
p, err = l.makeConnectedServerPipe()
|
|
||||||
// If the connection was immediately closed by the client, try
|
|
||||||
// again.
|
|
||||||
if err != cERROR_NO_DATA {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
responseCh <- acceptResponse{p, err}
|
|
||||||
closed = err == ErrPipeListenerClosed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
syscall.Close(l.firstHandle)
|
|
||||||
l.firstHandle = 0
|
|
||||||
// Notify Close() and Accept() callers that the handle has been closed.
|
|
||||||
close(l.doneCh)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PipeConfig contain configuration for the pipe listener.
|
|
||||||
type PipeConfig struct {
|
|
||||||
// SecurityDescriptor contains a Windows security descriptor in SDDL format.
|
|
||||||
SecurityDescriptor string
|
|
||||||
|
|
||||||
// MessageMode determines whether the pipe is in byte or message mode. In either
|
|
||||||
// case the pipe is read in byte mode by default. The only practical difference in
|
|
||||||
// this implementation is that CloseWrite() is only supported for message mode pipes;
|
|
||||||
// CloseWrite() is implemented as a zero-byte write, but zero-byte writes are only
|
|
||||||
// transferred to the reader (and returned as io.EOF in this implementation)
|
|
||||||
// when the pipe is in message mode.
|
|
||||||
MessageMode bool
|
|
||||||
|
|
||||||
// InputBufferSize specifies the size the input buffer, in bytes.
|
|
||||||
InputBufferSize int32
|
|
||||||
|
|
||||||
// OutputBufferSize specifies the size the input buffer, in bytes.
|
|
||||||
OutputBufferSize int32
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListenPipe creates a listener on a Windows named pipe path, e.g. \\.\pipe\mypipe.
|
|
||||||
// The pipe must not already exist.
|
|
||||||
func ListenPipe(path string, c *PipeConfig) (net.Listener, error) {
|
|
||||||
var (
|
|
||||||
sd []byte
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
if c == nil {
|
|
||||||
c = &PipeConfig{}
|
|
||||||
}
|
|
||||||
if c.SecurityDescriptor != "" {
|
|
||||||
sd, err = SddlToSecurityDescriptor(c.SecurityDescriptor)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
h, err := makeServerPipeHandle(path, sd, c, true)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
l := &win32PipeListener{
|
|
||||||
firstHandle: h,
|
|
||||||
path: path,
|
|
||||||
config: *c,
|
|
||||||
acceptCh: make(chan (chan acceptResponse)),
|
|
||||||
closeCh: make(chan int),
|
|
||||||
doneCh: make(chan int),
|
|
||||||
}
|
|
||||||
go l.listenerRoutine()
|
|
||||||
return l, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func connectPipe(p *win32File) error {
|
|
||||||
c, err := p.prepareIo()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer p.wg.Done()
|
|
||||||
|
|
||||||
err = connectNamedPipe(p.handle, &c.o)
|
|
||||||
_, err = p.asyncIo(c, nil, 0, err)
|
|
||||||
if err != nil && err != cERROR_PIPE_CONNECTED {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *win32PipeListener) Accept() (net.Conn, error) {
|
|
||||||
ch := make(chan acceptResponse)
|
|
||||||
select {
|
|
||||||
case l.acceptCh <- ch:
|
|
||||||
response := <-ch
|
|
||||||
err := response.err
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if l.config.MessageMode {
|
|
||||||
return &win32MessageBytePipe{
|
|
||||||
win32Pipe: win32Pipe{win32File: response.f, path: l.path},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
return &win32Pipe{win32File: response.f, path: l.path}, nil
|
|
||||||
case <-l.doneCh:
|
|
||||||
return nil, ErrPipeListenerClosed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *win32PipeListener) Close() error {
|
|
||||||
select {
|
|
||||||
case l.closeCh <- 1:
|
|
||||||
<-l.doneCh
|
|
||||||
case <-l.doneCh:
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *win32PipeListener) Addr() net.Addr {
|
|
||||||
return pipeAddress(l.path)
|
|
||||||
}
|
|
||||||
235
vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go
generated
vendored
235
vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go
generated
vendored
@ -1,235 +0,0 @@
|
|||||||
// Package guid provides a GUID type. The backing structure for a GUID is
|
|
||||||
// identical to that used by the golang.org/x/sys/windows GUID type.
|
|
||||||
// There are two main binary encodings used for a GUID, the big-endian encoding,
|
|
||||||
// and the Windows (mixed-endian) encoding. See here for details:
|
|
||||||
// https://en.wikipedia.org/wiki/Universally_unique_identifier#Encoding
|
|
||||||
package guid
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/sha1"
|
|
||||||
"encoding"
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Variant specifies which GUID variant (or "type") of the GUID. It determines
|
|
||||||
// how the entirety of the rest of the GUID is interpreted.
|
|
||||||
type Variant uint8
|
|
||||||
|
|
||||||
// The variants specified by RFC 4122.
|
|
||||||
const (
|
|
||||||
// VariantUnknown specifies a GUID variant which does not conform to one of
|
|
||||||
// the variant encodings specified in RFC 4122.
|
|
||||||
VariantUnknown Variant = iota
|
|
||||||
VariantNCS
|
|
||||||
VariantRFC4122
|
|
||||||
VariantMicrosoft
|
|
||||||
VariantFuture
|
|
||||||
)
|
|
||||||
|
|
||||||
// Version specifies how the bits in the GUID were generated. For instance, a
|
|
||||||
// version 4 GUID is randomly generated, and a version 5 is generated from the
|
|
||||||
// hash of an input string.
|
|
||||||
type Version uint8
|
|
||||||
|
|
||||||
var _ = (encoding.TextMarshaler)(GUID{})
|
|
||||||
var _ = (encoding.TextUnmarshaler)(&GUID{})
|
|
||||||
|
|
||||||
// GUID represents a GUID/UUID. It has the same structure as
|
|
||||||
// golang.org/x/sys/windows.GUID so that it can be used with functions expecting
|
|
||||||
// that type. It is defined as its own type so that stringification and
|
|
||||||
// marshaling can be supported. The representation matches that used by native
|
|
||||||
// Windows code.
|
|
||||||
type GUID windows.GUID
|
|
||||||
|
|
||||||
// NewV4 returns a new version 4 (pseudorandom) GUID, as defined by RFC 4122.
|
|
||||||
func NewV4() (GUID, error) {
|
|
||||||
var b [16]byte
|
|
||||||
if _, err := rand.Read(b[:]); err != nil {
|
|
||||||
return GUID{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
g := FromArray(b)
|
|
||||||
g.setVersion(4) // Version 4 means randomly generated.
|
|
||||||
g.setVariant(VariantRFC4122)
|
|
||||||
|
|
||||||
return g, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewV5 returns a new version 5 (generated from a string via SHA-1 hashing)
|
|
||||||
// GUID, as defined by RFC 4122. The RFC is unclear on the encoding of the name,
|
|
||||||
// and the sample code treats it as a series of bytes, so we do the same here.
|
|
||||||
//
|
|
||||||
// Some implementations, such as those found on Windows, treat the name as a
|
|
||||||
// big-endian UTF16 stream of bytes. If that is desired, the string can be
|
|
||||||
// encoded as such before being passed to this function.
|
|
||||||
func NewV5(namespace GUID, name []byte) (GUID, error) {
|
|
||||||
b := sha1.New()
|
|
||||||
namespaceBytes := namespace.ToArray()
|
|
||||||
b.Write(namespaceBytes[:])
|
|
||||||
b.Write(name)
|
|
||||||
|
|
||||||
a := [16]byte{}
|
|
||||||
copy(a[:], b.Sum(nil))
|
|
||||||
|
|
||||||
g := FromArray(a)
|
|
||||||
g.setVersion(5) // Version 5 means generated from a string.
|
|
||||||
g.setVariant(VariantRFC4122)
|
|
||||||
|
|
||||||
return g, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func fromArray(b [16]byte, order binary.ByteOrder) GUID {
|
|
||||||
var g GUID
|
|
||||||
g.Data1 = order.Uint32(b[0:4])
|
|
||||||
g.Data2 = order.Uint16(b[4:6])
|
|
||||||
g.Data3 = order.Uint16(b[6:8])
|
|
||||||
copy(g.Data4[:], b[8:16])
|
|
||||||
return g
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g GUID) toArray(order binary.ByteOrder) [16]byte {
|
|
||||||
b := [16]byte{}
|
|
||||||
order.PutUint32(b[0:4], g.Data1)
|
|
||||||
order.PutUint16(b[4:6], g.Data2)
|
|
||||||
order.PutUint16(b[6:8], g.Data3)
|
|
||||||
copy(b[8:16], g.Data4[:])
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromArray constructs a GUID from a big-endian encoding array of 16 bytes.
|
|
||||||
func FromArray(b [16]byte) GUID {
|
|
||||||
return fromArray(b, binary.BigEndian)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToArray returns an array of 16 bytes representing the GUID in big-endian
|
|
||||||
// encoding.
|
|
||||||
func (g GUID) ToArray() [16]byte {
|
|
||||||
return g.toArray(binary.BigEndian)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromWindowsArray constructs a GUID from a Windows encoding array of bytes.
|
|
||||||
func FromWindowsArray(b [16]byte) GUID {
|
|
||||||
return fromArray(b, binary.LittleEndian)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToWindowsArray returns an array of 16 bytes representing the GUID in Windows
|
|
||||||
// encoding.
|
|
||||||
func (g GUID) ToWindowsArray() [16]byte {
|
|
||||||
return g.toArray(binary.LittleEndian)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g GUID) String() string {
|
|
||||||
return fmt.Sprintf(
|
|
||||||
"%08x-%04x-%04x-%04x-%012x",
|
|
||||||
g.Data1,
|
|
||||||
g.Data2,
|
|
||||||
g.Data3,
|
|
||||||
g.Data4[:2],
|
|
||||||
g.Data4[2:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromString parses a string containing a GUID and returns the GUID. The only
|
|
||||||
// format currently supported is the `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
|
|
||||||
// format.
|
|
||||||
func FromString(s string) (GUID, error) {
|
|
||||||
if len(s) != 36 {
|
|
||||||
return GUID{}, fmt.Errorf("invalid GUID %q", s)
|
|
||||||
}
|
|
||||||
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
|
|
||||||
return GUID{}, fmt.Errorf("invalid GUID %q", s)
|
|
||||||
}
|
|
||||||
|
|
||||||
var g GUID
|
|
||||||
|
|
||||||
data1, err := strconv.ParseUint(s[0:8], 16, 32)
|
|
||||||
if err != nil {
|
|
||||||
return GUID{}, fmt.Errorf("invalid GUID %q", s)
|
|
||||||
}
|
|
||||||
g.Data1 = uint32(data1)
|
|
||||||
|
|
||||||
data2, err := strconv.ParseUint(s[9:13], 16, 16)
|
|
||||||
if err != nil {
|
|
||||||
return GUID{}, fmt.Errorf("invalid GUID %q", s)
|
|
||||||
}
|
|
||||||
g.Data2 = uint16(data2)
|
|
||||||
|
|
||||||
data3, err := strconv.ParseUint(s[14:18], 16, 16)
|
|
||||||
if err != nil {
|
|
||||||
return GUID{}, fmt.Errorf("invalid GUID %q", s)
|
|
||||||
}
|
|
||||||
g.Data3 = uint16(data3)
|
|
||||||
|
|
||||||
for i, x := range []int{19, 21, 24, 26, 28, 30, 32, 34} {
|
|
||||||
v, err := strconv.ParseUint(s[x:x+2], 16, 8)
|
|
||||||
if err != nil {
|
|
||||||
return GUID{}, fmt.Errorf("invalid GUID %q", s)
|
|
||||||
}
|
|
||||||
g.Data4[i] = uint8(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
return g, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *GUID) setVariant(v Variant) {
|
|
||||||
d := g.Data4[0]
|
|
||||||
switch v {
|
|
||||||
case VariantNCS:
|
|
||||||
d = (d & 0x7f)
|
|
||||||
case VariantRFC4122:
|
|
||||||
d = (d & 0x3f) | 0x80
|
|
||||||
case VariantMicrosoft:
|
|
||||||
d = (d & 0x1f) | 0xc0
|
|
||||||
case VariantFuture:
|
|
||||||
d = (d & 0x0f) | 0xe0
|
|
||||||
case VariantUnknown:
|
|
||||||
fallthrough
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("invalid variant: %d", v))
|
|
||||||
}
|
|
||||||
g.Data4[0] = d
|
|
||||||
}
|
|
||||||
|
|
||||||
// Variant returns the GUID variant, as defined in RFC 4122.
|
|
||||||
func (g GUID) Variant() Variant {
|
|
||||||
b := g.Data4[0]
|
|
||||||
if b&0x80 == 0 {
|
|
||||||
return VariantNCS
|
|
||||||
} else if b&0xc0 == 0x80 {
|
|
||||||
return VariantRFC4122
|
|
||||||
} else if b&0xe0 == 0xc0 {
|
|
||||||
return VariantMicrosoft
|
|
||||||
} else if b&0xe0 == 0xe0 {
|
|
||||||
return VariantFuture
|
|
||||||
}
|
|
||||||
return VariantUnknown
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *GUID) setVersion(v Version) {
|
|
||||||
g.Data3 = (g.Data3 & 0x0fff) | (uint16(v) << 12)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Version returns the GUID version, as defined in RFC 4122.
|
|
||||||
func (g GUID) Version() Version {
|
|
||||||
return Version((g.Data3 & 0xF000) >> 12)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalText returns the textual representation of the GUID.
|
|
||||||
func (g GUID) MarshalText() ([]byte, error) {
|
|
||||||
return []byte(g.String()), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalText takes the textual representation of a GUID, and unmarhals it
|
|
||||||
// into this GUID.
|
|
||||||
func (g *GUID) UnmarshalText(text []byte) error {
|
|
||||||
g2, err := FromString(string(text))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*g = g2
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
202
vendor/github.com/Microsoft/go-winio/privilege.go
generated
vendored
202
vendor/github.com/Microsoft/go-winio/privilege.go
generated
vendored
@ -1,202 +0,0 @@
|
|||||||
// +build windows
|
|
||||||
|
|
||||||
package winio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"runtime"
|
|
||||||
"sync"
|
|
||||||
"syscall"
|
|
||||||
"unicode/utf16"
|
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
|
||||||
)
|
|
||||||
|
|
||||||
//sys adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges
|
|
||||||
//sys impersonateSelf(level uint32) (err error) = advapi32.ImpersonateSelf
|
|
||||||
//sys revertToSelf() (err error) = advapi32.RevertToSelf
|
|
||||||
//sys openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) = advapi32.OpenThreadToken
|
|
||||||
//sys getCurrentThread() (h syscall.Handle) = GetCurrentThread
|
|
||||||
//sys lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) = advapi32.LookupPrivilegeValueW
|
|
||||||
//sys lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW
|
|
||||||
//sys lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW
|
|
||||||
|
|
||||||
const (
|
|
||||||
SE_PRIVILEGE_ENABLED = 2
|
|
||||||
|
|
||||||
ERROR_NOT_ALL_ASSIGNED syscall.Errno = 1300
|
|
||||||
|
|
||||||
SeBackupPrivilege = "SeBackupPrivilege"
|
|
||||||
SeRestorePrivilege = "SeRestorePrivilege"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
securityAnonymous = iota
|
|
||||||
securityIdentification
|
|
||||||
securityImpersonation
|
|
||||||
securityDelegation
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
privNames = make(map[string]uint64)
|
|
||||||
privNameMutex sync.Mutex
|
|
||||||
)
|
|
||||||
|
|
||||||
// PrivilegeError represents an error enabling privileges.
|
|
||||||
type PrivilegeError struct {
|
|
||||||
privileges []uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *PrivilegeError) Error() string {
|
|
||||||
s := ""
|
|
||||||
if len(e.privileges) > 1 {
|
|
||||||
s = "Could not enable privileges "
|
|
||||||
} else {
|
|
||||||
s = "Could not enable privilege "
|
|
||||||
}
|
|
||||||
for i, p := range e.privileges {
|
|
||||||
if i != 0 {
|
|
||||||
s += ", "
|
|
||||||
}
|
|
||||||
s += `"`
|
|
||||||
s += getPrivilegeName(p)
|
|
||||||
s += `"`
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunWithPrivilege enables a single privilege for a function call.
|
|
||||||
func RunWithPrivilege(name string, fn func() error) error {
|
|
||||||
return RunWithPrivileges([]string{name}, fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunWithPrivileges enables privileges for a function call.
|
|
||||||
func RunWithPrivileges(names []string, fn func() error) error {
|
|
||||||
privileges, err := mapPrivileges(names)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
runtime.LockOSThread()
|
|
||||||
defer runtime.UnlockOSThread()
|
|
||||||
token, err := newThreadToken()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer releaseThreadToken(token)
|
|
||||||
err = adjustPrivileges(token, privileges, SE_PRIVILEGE_ENABLED)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return fn()
|
|
||||||
}
|
|
||||||
|
|
||||||
func mapPrivileges(names []string) ([]uint64, error) {
|
|
||||||
var privileges []uint64
|
|
||||||
privNameMutex.Lock()
|
|
||||||
defer privNameMutex.Unlock()
|
|
||||||
for _, name := range names {
|
|
||||||
p, ok := privNames[name]
|
|
||||||
if !ok {
|
|
||||||
err := lookupPrivilegeValue("", name, &p)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
privNames[name] = p
|
|
||||||
}
|
|
||||||
privileges = append(privileges, p)
|
|
||||||
}
|
|
||||||
return privileges, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnableProcessPrivileges enables privileges globally for the process.
|
|
||||||
func EnableProcessPrivileges(names []string) error {
|
|
||||||
return enableDisableProcessPrivilege(names, SE_PRIVILEGE_ENABLED)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DisableProcessPrivileges disables privileges globally for the process.
|
|
||||||
func DisableProcessPrivileges(names []string) error {
|
|
||||||
return enableDisableProcessPrivilege(names, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func enableDisableProcessPrivilege(names []string, action uint32) error {
|
|
||||||
privileges, err := mapPrivileges(names)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
p, _ := windows.GetCurrentProcess()
|
|
||||||
var token windows.Token
|
|
||||||
err = windows.OpenProcessToken(p, windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY, &token)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer token.Close()
|
|
||||||
return adjustPrivileges(token, privileges, action)
|
|
||||||
}
|
|
||||||
|
|
||||||
func adjustPrivileges(token windows.Token, privileges []uint64, action uint32) error {
|
|
||||||
var b bytes.Buffer
|
|
||||||
binary.Write(&b, binary.LittleEndian, uint32(len(privileges)))
|
|
||||||
for _, p := range privileges {
|
|
||||||
binary.Write(&b, binary.LittleEndian, p)
|
|
||||||
binary.Write(&b, binary.LittleEndian, action)
|
|
||||||
}
|
|
||||||
prevState := make([]byte, b.Len())
|
|
||||||
reqSize := uint32(0)
|
|
||||||
success, err := adjustTokenPrivileges(token, false, &b.Bytes()[0], uint32(len(prevState)), &prevState[0], &reqSize)
|
|
||||||
if !success {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err == ERROR_NOT_ALL_ASSIGNED {
|
|
||||||
return &PrivilegeError{privileges}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPrivilegeName(luid uint64) string {
|
|
||||||
var nameBuffer [256]uint16
|
|
||||||
bufSize := uint32(len(nameBuffer))
|
|
||||||
err := lookupPrivilegeName("", &luid, &nameBuffer[0], &bufSize)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Sprintf("<unknown privilege %d>", luid)
|
|
||||||
}
|
|
||||||
|
|
||||||
var displayNameBuffer [256]uint16
|
|
||||||
displayBufSize := uint32(len(displayNameBuffer))
|
|
||||||
var langID uint32
|
|
||||||
err = lookupPrivilegeDisplayName("", &nameBuffer[0], &displayNameBuffer[0], &displayBufSize, &langID)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Sprintf("<unknown privilege %s>", string(utf16.Decode(nameBuffer[:bufSize])))
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(utf16.Decode(displayNameBuffer[:displayBufSize]))
|
|
||||||
}
|
|
||||||
|
|
||||||
func newThreadToken() (windows.Token, error) {
|
|
||||||
err := impersonateSelf(securityImpersonation)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var token windows.Token
|
|
||||||
err = openThreadToken(getCurrentThread(), syscall.TOKEN_ADJUST_PRIVILEGES|syscall.TOKEN_QUERY, false, &token)
|
|
||||||
if err != nil {
|
|
||||||
rerr := revertToSelf()
|
|
||||||
if rerr != nil {
|
|
||||||
panic(rerr)
|
|
||||||
}
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return token, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func releaseThreadToken(h windows.Token) {
|
|
||||||
err := revertToSelf()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
h.Close()
|
|
||||||
}
|
|
||||||
128
vendor/github.com/Microsoft/go-winio/reparse.go
generated
vendored
128
vendor/github.com/Microsoft/go-winio/reparse.go
generated
vendored
@ -1,128 +0,0 @@
|
|||||||
package winio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"unicode/utf16"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
reparseTagMountPoint = 0xA0000003
|
|
||||||
reparseTagSymlink = 0xA000000C
|
|
||||||
)
|
|
||||||
|
|
||||||
type reparseDataBuffer struct {
|
|
||||||
ReparseTag uint32
|
|
||||||
ReparseDataLength uint16
|
|
||||||
Reserved uint16
|
|
||||||
SubstituteNameOffset uint16
|
|
||||||
SubstituteNameLength uint16
|
|
||||||
PrintNameOffset uint16
|
|
||||||
PrintNameLength uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReparsePoint describes a Win32 symlink or mount point.
|
|
||||||
type ReparsePoint struct {
|
|
||||||
Target string
|
|
||||||
IsMountPoint bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnsupportedReparsePointError is returned when trying to decode a non-symlink or
|
|
||||||
// mount point reparse point.
|
|
||||||
type UnsupportedReparsePointError struct {
|
|
||||||
Tag uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *UnsupportedReparsePointError) Error() string {
|
|
||||||
return fmt.Sprintf("unsupported reparse point %x", e.Tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeReparsePoint decodes a Win32 REPARSE_DATA_BUFFER structure containing either a symlink
|
|
||||||
// or a mount point.
|
|
||||||
func DecodeReparsePoint(b []byte) (*ReparsePoint, error) {
|
|
||||||
tag := binary.LittleEndian.Uint32(b[0:4])
|
|
||||||
return DecodeReparsePointData(tag, b[8:])
|
|
||||||
}
|
|
||||||
|
|
||||||
func DecodeReparsePointData(tag uint32, b []byte) (*ReparsePoint, error) {
|
|
||||||
isMountPoint := false
|
|
||||||
switch tag {
|
|
||||||
case reparseTagMountPoint:
|
|
||||||
isMountPoint = true
|
|
||||||
case reparseTagSymlink:
|
|
||||||
default:
|
|
||||||
return nil, &UnsupportedReparsePointError{tag}
|
|
||||||
}
|
|
||||||
nameOffset := 8 + binary.LittleEndian.Uint16(b[4:6])
|
|
||||||
if !isMountPoint {
|
|
||||||
nameOffset += 4
|
|
||||||
}
|
|
||||||
nameLength := binary.LittleEndian.Uint16(b[6:8])
|
|
||||||
name := make([]uint16, nameLength/2)
|
|
||||||
err := binary.Read(bytes.NewReader(b[nameOffset:nameOffset+nameLength]), binary.LittleEndian, &name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &ReparsePoint{string(utf16.Decode(name)), isMountPoint}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func isDriveLetter(c byte) bool {
|
|
||||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeReparsePoint encodes a Win32 REPARSE_DATA_BUFFER structure describing a symlink or
|
|
||||||
// mount point.
|
|
||||||
func EncodeReparsePoint(rp *ReparsePoint) []byte {
|
|
||||||
// Generate an NT path and determine if this is a relative path.
|
|
||||||
var ntTarget string
|
|
||||||
relative := false
|
|
||||||
if strings.HasPrefix(rp.Target, `\\?\`) {
|
|
||||||
ntTarget = `\??\` + rp.Target[4:]
|
|
||||||
} else if strings.HasPrefix(rp.Target, `\\`) {
|
|
||||||
ntTarget = `\??\UNC\` + rp.Target[2:]
|
|
||||||
} else if len(rp.Target) >= 2 && isDriveLetter(rp.Target[0]) && rp.Target[1] == ':' {
|
|
||||||
ntTarget = `\??\` + rp.Target
|
|
||||||
} else {
|
|
||||||
ntTarget = rp.Target
|
|
||||||
relative = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// The paths must be NUL-terminated even though they are counted strings.
|
|
||||||
target16 := utf16.Encode([]rune(rp.Target + "\x00"))
|
|
||||||
ntTarget16 := utf16.Encode([]rune(ntTarget + "\x00"))
|
|
||||||
|
|
||||||
size := int(unsafe.Sizeof(reparseDataBuffer{})) - 8
|
|
||||||
size += len(ntTarget16)*2 + len(target16)*2
|
|
||||||
|
|
||||||
tag := uint32(reparseTagMountPoint)
|
|
||||||
if !rp.IsMountPoint {
|
|
||||||
tag = reparseTagSymlink
|
|
||||||
size += 4 // Add room for symlink flags
|
|
||||||
}
|
|
||||||
|
|
||||||
data := reparseDataBuffer{
|
|
||||||
ReparseTag: tag,
|
|
||||||
ReparseDataLength: uint16(size),
|
|
||||||
SubstituteNameOffset: 0,
|
|
||||||
SubstituteNameLength: uint16((len(ntTarget16) - 1) * 2),
|
|
||||||
PrintNameOffset: uint16(len(ntTarget16) * 2),
|
|
||||||
PrintNameLength: uint16((len(target16) - 1) * 2),
|
|
||||||
}
|
|
||||||
|
|
||||||
var b bytes.Buffer
|
|
||||||
binary.Write(&b, binary.LittleEndian, &data)
|
|
||||||
if !rp.IsMountPoint {
|
|
||||||
flags := uint32(0)
|
|
||||||
if relative {
|
|
||||||
flags |= 1
|
|
||||||
}
|
|
||||||
binary.Write(&b, binary.LittleEndian, flags)
|
|
||||||
}
|
|
||||||
|
|
||||||
binary.Write(&b, binary.LittleEndian, ntTarget16)
|
|
||||||
binary.Write(&b, binary.LittleEndian, target16)
|
|
||||||
return b.Bytes()
|
|
||||||
}
|
|
||||||
98
vendor/github.com/Microsoft/go-winio/sd.go
generated
vendored
98
vendor/github.com/Microsoft/go-winio/sd.go
generated
vendored
@ -1,98 +0,0 @@
|
|||||||
// +build windows
|
|
||||||
|
|
||||||
package winio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
//sys lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountNameW
|
|
||||||
//sys convertSidToStringSid(sid *byte, str **uint16) (err error) = advapi32.ConvertSidToStringSidW
|
|
||||||
//sys convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) = advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW
|
|
||||||
//sys convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) = advapi32.ConvertSecurityDescriptorToStringSecurityDescriptorW
|
|
||||||
//sys localFree(mem uintptr) = LocalFree
|
|
||||||
//sys getSecurityDescriptorLength(sd uintptr) (len uint32) = advapi32.GetSecurityDescriptorLength
|
|
||||||
|
|
||||||
const (
|
|
||||||
cERROR_NONE_MAPPED = syscall.Errno(1332)
|
|
||||||
)
|
|
||||||
|
|
||||||
type AccountLookupError struct {
|
|
||||||
Name string
|
|
||||||
Err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *AccountLookupError) Error() string {
|
|
||||||
if e.Name == "" {
|
|
||||||
return "lookup account: empty account name specified"
|
|
||||||
}
|
|
||||||
var s string
|
|
||||||
switch e.Err {
|
|
||||||
case cERROR_NONE_MAPPED:
|
|
||||||
s = "not found"
|
|
||||||
default:
|
|
||||||
s = e.Err.Error()
|
|
||||||
}
|
|
||||||
return "lookup account " + e.Name + ": " + s
|
|
||||||
}
|
|
||||||
|
|
||||||
type SddlConversionError struct {
|
|
||||||
Sddl string
|
|
||||||
Err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *SddlConversionError) Error() string {
|
|
||||||
return "convert " + e.Sddl + ": " + e.Err.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
// LookupSidByName looks up the SID of an account by name
|
|
||||||
func LookupSidByName(name string) (sid string, err error) {
|
|
||||||
if name == "" {
|
|
||||||
return "", &AccountLookupError{name, cERROR_NONE_MAPPED}
|
|
||||||
}
|
|
||||||
|
|
||||||
var sidSize, sidNameUse, refDomainSize uint32
|
|
||||||
err = lookupAccountName(nil, name, nil, &sidSize, nil, &refDomainSize, &sidNameUse)
|
|
||||||
if err != nil && err != syscall.ERROR_INSUFFICIENT_BUFFER {
|
|
||||||
return "", &AccountLookupError{name, err}
|
|
||||||
}
|
|
||||||
sidBuffer := make([]byte, sidSize)
|
|
||||||
refDomainBuffer := make([]uint16, refDomainSize)
|
|
||||||
err = lookupAccountName(nil, name, &sidBuffer[0], &sidSize, &refDomainBuffer[0], &refDomainSize, &sidNameUse)
|
|
||||||
if err != nil {
|
|
||||||
return "", &AccountLookupError{name, err}
|
|
||||||
}
|
|
||||||
var strBuffer *uint16
|
|
||||||
err = convertSidToStringSid(&sidBuffer[0], &strBuffer)
|
|
||||||
if err != nil {
|
|
||||||
return "", &AccountLookupError{name, err}
|
|
||||||
}
|
|
||||||
sid = syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(strBuffer))[:])
|
|
||||||
localFree(uintptr(unsafe.Pointer(strBuffer)))
|
|
||||||
return sid, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func SddlToSecurityDescriptor(sddl string) ([]byte, error) {
|
|
||||||
var sdBuffer uintptr
|
|
||||||
err := convertStringSecurityDescriptorToSecurityDescriptor(sddl, 1, &sdBuffer, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, &SddlConversionError{sddl, err}
|
|
||||||
}
|
|
||||||
defer localFree(sdBuffer)
|
|
||||||
sd := make([]byte, getSecurityDescriptorLength(sdBuffer))
|
|
||||||
copy(sd, (*[0xffff]byte)(unsafe.Pointer(sdBuffer))[:len(sd)])
|
|
||||||
return sd, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func SecurityDescriptorToSddl(sd []byte) (string, error) {
|
|
||||||
var sddl *uint16
|
|
||||||
// The returned string length seems to including an aribtrary number of terminating NULs.
|
|
||||||
// Don't use it.
|
|
||||||
err := convertSecurityDescriptorToStringSecurityDescriptor(&sd[0], 1, 0xff, &sddl, nil)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer localFree(uintptr(unsafe.Pointer(sddl)))
|
|
||||||
return syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(sddl))[:]), nil
|
|
||||||
}
|
|
||||||
3
vendor/github.com/Microsoft/go-winio/syscall.go
generated
vendored
3
vendor/github.com/Microsoft/go-winio/syscall.go
generated
vendored
@ -1,3 +0,0 @@
|
|||||||
package winio
|
|
||||||
|
|
||||||
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go file.go pipe.go sd.go fileinfo.go privilege.go backup.go hvsock.go
|
|
||||||
562
vendor/github.com/Microsoft/go-winio/zsyscall_windows.go
generated
vendored
562
vendor/github.com/Microsoft/go-winio/zsyscall_windows.go
generated
vendored
@ -1,562 +0,0 @@
|
|||||||
// Code generated by 'go generate'; DO NOT EDIT.
|
|
||||||
|
|
||||||
package winio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ unsafe.Pointer
|
|
||||||
|
|
||||||
// Do the interface allocations only once for common
|
|
||||||
// Errno values.
|
|
||||||
const (
|
|
||||||
errnoERROR_IO_PENDING = 997
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
|
||||||
)
|
|
||||||
|
|
||||||
// errnoErr returns common boxed Errno values, to prevent
|
|
||||||
// allocations at runtime.
|
|
||||||
func errnoErr(e syscall.Errno) error {
|
|
||||||
switch e {
|
|
||||||
case 0:
|
|
||||||
return nil
|
|
||||||
case errnoERROR_IO_PENDING:
|
|
||||||
return errERROR_IO_PENDING
|
|
||||||
}
|
|
||||||
// TODO: add more here, after collecting data on the common
|
|
||||||
// error values see on Windows. (perhaps when running
|
|
||||||
// all.bat?)
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
|
||||||
modws2_32 = windows.NewLazySystemDLL("ws2_32.dll")
|
|
||||||
modntdll = windows.NewLazySystemDLL("ntdll.dll")
|
|
||||||
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
|
|
||||||
|
|
||||||
procCancelIoEx = modkernel32.NewProc("CancelIoEx")
|
|
||||||
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
|
|
||||||
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
|
|
||||||
procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes")
|
|
||||||
procWSAGetOverlappedResult = modws2_32.NewProc("WSAGetOverlappedResult")
|
|
||||||
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe")
|
|
||||||
procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW")
|
|
||||||
procCreateFileW = modkernel32.NewProc("CreateFileW")
|
|
||||||
procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo")
|
|
||||||
procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW")
|
|
||||||
procLocalAlloc = modkernel32.NewProc("LocalAlloc")
|
|
||||||
procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile")
|
|
||||||
procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb")
|
|
||||||
procRtlDosPathNameToNtPathName_U = modntdll.NewProc("RtlDosPathNameToNtPathName_U")
|
|
||||||
procRtlDefaultNpAcl = modntdll.NewProc("RtlDefaultNpAcl")
|
|
||||||
procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW")
|
|
||||||
procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW")
|
|
||||||
procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW")
|
|
||||||
procConvertSecurityDescriptorToStringSecurityDescriptorW = modadvapi32.NewProc("ConvertSecurityDescriptorToStringSecurityDescriptorW")
|
|
||||||
procLocalFree = modkernel32.NewProc("LocalFree")
|
|
||||||
procGetSecurityDescriptorLength = modadvapi32.NewProc("GetSecurityDescriptorLength")
|
|
||||||
procGetFileInformationByHandleEx = modkernel32.NewProc("GetFileInformationByHandleEx")
|
|
||||||
procSetFileInformationByHandle = modkernel32.NewProc("SetFileInformationByHandle")
|
|
||||||
procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
|
|
||||||
procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf")
|
|
||||||
procRevertToSelf = modadvapi32.NewProc("RevertToSelf")
|
|
||||||
procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken")
|
|
||||||
procGetCurrentThread = modkernel32.NewProc("GetCurrentThread")
|
|
||||||
procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW")
|
|
||||||
procLookupPrivilegeNameW = modadvapi32.NewProc("LookupPrivilegeNameW")
|
|
||||||
procLookupPrivilegeDisplayNameW = modadvapi32.NewProc("LookupPrivilegeDisplayNameW")
|
|
||||||
procBackupRead = modkernel32.NewProc("BackupRead")
|
|
||||||
procBackupWrite = modkernel32.NewProc("BackupWrite")
|
|
||||||
procbind = modws2_32.NewProc("bind")
|
|
||||||
)
|
|
||||||
|
|
||||||
func cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(file), uintptr(unsafe.Pointer(o)), 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) {
|
|
||||||
r0, _, e1 := syscall.Syscall6(procCreateIoCompletionPort.Addr(), 4, uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount), 0, 0)
|
|
||||||
newport = syscall.Handle(r0)
|
|
||||||
if newport == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout), 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(h), uintptr(flags), 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) {
|
|
||||||
var _p0 uint32
|
|
||||||
if wait {
|
|
||||||
_p0 = 1
|
|
||||||
} else {
|
|
||||||
_p0 = 0
|
|
||||||
}
|
|
||||||
r1, _, e1 := syscall.Syscall6(procWSAGetOverlappedResult.Addr(), 5, uintptr(h), uintptr(unsafe.Pointer(o)), uintptr(unsafe.Pointer(bytes)), uintptr(_p0), uintptr(unsafe.Pointer(flags)), 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(o)), 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) {
|
|
||||||
var _p0 *uint16
|
|
||||||
_p0, err = syscall.UTF16PtrFromString(name)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) {
|
|
||||||
r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0)
|
|
||||||
handle = syscall.Handle(r0)
|
|
||||||
if handle == syscall.InvalidHandle {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
|
|
||||||
var _p0 *uint16
|
|
||||||
_p0, err = syscall.UTF16PtrFromString(name)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return _createFile(_p0, access, mode, sa, createmode, attrs, templatefile)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _createFile(name *uint16, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
|
|
||||||
r0, _, e1 := syscall.Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0)
|
|
||||||
handle = syscall.Handle(r0)
|
|
||||||
if handle == syscall.InvalidHandle {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall6(procGetNamedPipeInfo.Addr(), 5, uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)), 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall9(procGetNamedPipeHandleStateW.Addr(), 7, uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize), 0, 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func localAlloc(uFlags uint32, length uint32) (ptr uintptr) {
|
|
||||||
r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(uFlags), uintptr(length), 0)
|
|
||||||
ptr = uintptr(r0)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) {
|
|
||||||
r0, _, _ := syscall.Syscall15(procNtCreateNamedPipeFile.Addr(), 14, uintptr(unsafe.Pointer(pipe)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(share), uintptr(disposition), uintptr(options), uintptr(typ), uintptr(readMode), uintptr(completionMode), uintptr(maxInstances), uintptr(inboundQuota), uintptr(outputQuota), uintptr(unsafe.Pointer(timeout)), 0)
|
|
||||||
status = ntstatus(r0)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func rtlNtStatusToDosError(status ntstatus) (winerr error) {
|
|
||||||
r0, _, _ := syscall.Syscall(procRtlNtStatusToDosErrorNoTeb.Addr(), 1, uintptr(status), 0, 0)
|
|
||||||
if r0 != 0 {
|
|
||||||
winerr = syscall.Errno(r0)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntstatus) {
|
|
||||||
r0, _, _ := syscall.Syscall6(procRtlDosPathNameToNtPathName_U.Addr(), 4, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(ntName)), uintptr(filePart), uintptr(reserved), 0, 0)
|
|
||||||
status = ntstatus(r0)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func rtlDefaultNpAcl(dacl *uintptr) (status ntstatus) {
|
|
||||||
r0, _, _ := syscall.Syscall(procRtlDefaultNpAcl.Addr(), 1, uintptr(unsafe.Pointer(dacl)), 0, 0)
|
|
||||||
status = ntstatus(r0)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
|
|
||||||
var _p0 *uint16
|
|
||||||
_p0, err = syscall.UTF16PtrFromString(accountName)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return _lookupAccountName(systemName, _p0, sid, sidSize, refDomain, refDomainSize, sidNameUse)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall9(procLookupAccountNameW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)), 0, 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertSidToStringSid(sid *byte, str **uint16) (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall(procConvertSidToStringSidW.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(str)), 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) {
|
|
||||||
var _p0 *uint16
|
|
||||||
_p0, err = syscall.UTF16PtrFromString(str)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return _convertStringSecurityDescriptorToSecurityDescriptor(_p0, revision, sd, size)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision uint32, sd *uintptr, size *uint32) (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall6(procConvertStringSecurityDescriptorToSecurityDescriptorW.Addr(), 4, uintptr(unsafe.Pointer(str)), uintptr(revision), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(size)), 0, 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall6(procConvertSecurityDescriptorToStringSecurityDescriptorW.Addr(), 5, uintptr(unsafe.Pointer(sd)), uintptr(revision), uintptr(secInfo), uintptr(unsafe.Pointer(sddl)), uintptr(unsafe.Pointer(sddlSize)), 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func localFree(mem uintptr) {
|
|
||||||
syscall.Syscall(procLocalFree.Addr(), 1, uintptr(mem), 0, 0)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func getSecurityDescriptorLength(sd uintptr) (len uint32) {
|
|
||||||
r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(sd), 0, 0)
|
|
||||||
len = uint32(r0)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(), 4, uintptr(h), uintptr(class), uintptr(unsafe.Pointer(buffer)), uintptr(size), 0, 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall6(procSetFileInformationByHandle.Addr(), 4, uintptr(h), uintptr(class), uintptr(unsafe.Pointer(buffer)), uintptr(size), 0, 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) {
|
|
||||||
var _p0 uint32
|
|
||||||
if releaseAll {
|
|
||||||
_p0 = 1
|
|
||||||
} else {
|
|
||||||
_p0 = 0
|
|
||||||
}
|
|
||||||
r0, _, e1 := syscall.Syscall6(procAdjustTokenPrivileges.Addr(), 6, uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(input)), uintptr(outputSize), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(requiredSize)))
|
|
||||||
success = r0 != 0
|
|
||||||
if true {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func impersonateSelf(level uint32) (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall(procImpersonateSelf.Addr(), 1, uintptr(level), 0, 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func revertToSelf() (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall(procRevertToSelf.Addr(), 0, 0, 0, 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) {
|
|
||||||
var _p0 uint32
|
|
||||||
if openAsSelf {
|
|
||||||
_p0 = 1
|
|
||||||
} else {
|
|
||||||
_p0 = 0
|
|
||||||
}
|
|
||||||
r1, _, e1 := syscall.Syscall6(procOpenThreadToken.Addr(), 4, uintptr(thread), uintptr(accessMask), uintptr(_p0), uintptr(unsafe.Pointer(token)), 0, 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCurrentThread() (h syscall.Handle) {
|
|
||||||
r0, _, _ := syscall.Syscall(procGetCurrentThread.Addr(), 0, 0, 0, 0)
|
|
||||||
h = syscall.Handle(r0)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) {
|
|
||||||
var _p0 *uint16
|
|
||||||
_p0, err = syscall.UTF16PtrFromString(systemName)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var _p1 *uint16
|
|
||||||
_p1, err = syscall.UTF16PtrFromString(name)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return _lookupPrivilegeValue(_p0, _p1, luid)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _lookupPrivilegeValue(systemName *uint16, name *uint16, luid *uint64) (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall(procLookupPrivilegeValueW.Addr(), 3, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid)))
|
|
||||||
if r1 == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) {
|
|
||||||
var _p0 *uint16
|
|
||||||
_p0, err = syscall.UTF16PtrFromString(systemName)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return _lookupPrivilegeName(_p0, luid, buffer, size)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _lookupPrivilegeName(systemName *uint16, luid *uint64, buffer *uint16, size *uint32) (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeNameW.Addr(), 4, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), 0, 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
|
|
||||||
var _p0 *uint16
|
|
||||||
_p0, err = syscall.UTF16PtrFromString(systemName)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return _lookupPrivilegeDisplayName(_p0, name, buffer, size, languageId)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _lookupPrivilegeDisplayName(systemName *uint16, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeDisplayNameW.Addr(), 5, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(languageId)), 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
|
|
||||||
var _p0 *byte
|
|
||||||
if len(b) > 0 {
|
|
||||||
_p0 = &b[0]
|
|
||||||
}
|
|
||||||
var _p1 uint32
|
|
||||||
if abort {
|
|
||||||
_p1 = 1
|
|
||||||
} else {
|
|
||||||
_p1 = 0
|
|
||||||
}
|
|
||||||
var _p2 uint32
|
|
||||||
if processSecurity {
|
|
||||||
_p2 = 1
|
|
||||||
} else {
|
|
||||||
_p2 = 0
|
|
||||||
}
|
|
||||||
r1, _, e1 := syscall.Syscall9(procBackupRead.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesRead)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
|
|
||||||
var _p0 *byte
|
|
||||||
if len(b) > 0 {
|
|
||||||
_p0 = &b[0]
|
|
||||||
}
|
|
||||||
var _p1 uint32
|
|
||||||
if abort {
|
|
||||||
_p1 = 1
|
|
||||||
} else {
|
|
||||||
_p1 = 0
|
|
||||||
}
|
|
||||||
var _p2 uint32
|
|
||||||
if processSecurity {
|
|
||||||
_p2 = 1
|
|
||||||
} else {
|
|
||||||
_p2 = 0
|
|
||||||
}
|
|
||||||
r1, _, e1 := syscall.Syscall9(procBackupWrite.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesWritten)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func bind(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen))
|
|
||||||
if r1 == socketError {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
191
vendor/github.com/coreos/go-systemd/LICENSE
generated
vendored
191
vendor/github.com/coreos/go-systemd/LICENSE
generated
vendored
@ -1,191 +0,0 @@
|
|||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction, and
|
|
||||||
distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright
|
|
||||||
owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all other entities
|
|
||||||
that control, are controlled by, or are under common control with that entity.
|
|
||||||
For the purposes of this definition, "control" means (i) the power, direct or
|
|
||||||
indirect, to cause the direction or management of such entity, whether by
|
|
||||||
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity exercising
|
|
||||||
permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications, including
|
|
||||||
but not limited to software source code, documentation source, and configuration
|
|
||||||
files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical transformation or
|
|
||||||
translation of a Source form, including but not limited to compiled object code,
|
|
||||||
generated documentation, and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or Object form, made
|
|
||||||
available under the License, as indicated by a copyright notice that is included
|
|
||||||
in or attached to the work (an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object form, that
|
|
||||||
is based on (or derived from) the Work and for which the editorial revisions,
|
|
||||||
annotations, elaborations, or other modifications represent, as a whole, an
|
|
||||||
original work of authorship. For the purposes of this License, Derivative Works
|
|
||||||
shall not include works that remain separable from, or merely link (or bind by
|
|
||||||
name) to the interfaces of, the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including the original version
|
|
||||||
of the Work and any modifications or additions to that Work or Derivative Works
|
|
||||||
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
|
||||||
by the copyright owner or by an individual or Legal Entity authorized to submit
|
|
||||||
on behalf of the copyright owner. For the purposes of this definition,
|
|
||||||
"submitted" means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems, and
|
|
||||||
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
|
||||||
the purpose of discussing and improving the Work, but excluding communication
|
|
||||||
that is conspicuously marked or otherwise designated in writing by the copyright
|
|
||||||
owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
|
|
||||||
of whom a Contribution has been received by Licensor and subsequently
|
|
||||||
incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License.
|
|
||||||
|
|
||||||
Subject to the terms and conditions of this License, each Contributor hereby
|
|
||||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
|
||||||
irrevocable copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the Work and such
|
|
||||||
Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License.
|
|
||||||
|
|
||||||
Subject to the terms and conditions of this License, each Contributor hereby
|
|
||||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
|
||||||
irrevocable (except as stated in this section) patent license to make, have
|
|
||||||
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
|
|
||||||
such license applies only to those patent claims licensable by such Contributor
|
|
||||||
that are necessarily infringed by their Contribution(s) alone or by combination
|
|
||||||
of their Contribution(s) with the Work to which such Contribution(s) was
|
|
||||||
submitted. If You institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
|
||||||
Contribution incorporated within the Work constitutes direct or contributory
|
|
||||||
patent infringement, then any patent licenses granted to You under this License
|
|
||||||
for that Work shall terminate as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution.
|
|
||||||
|
|
||||||
You may reproduce and distribute copies of the Work or Derivative Works thereof
|
|
||||||
in any medium, with or without modifications, and in Source or Object form,
|
|
||||||
provided that You meet the following conditions:
|
|
||||||
|
|
||||||
You must give any other recipients of the Work or Derivative Works a copy of
|
|
||||||
this License; and
|
|
||||||
You must cause any modified files to carry prominent notices stating that You
|
|
||||||
changed the files; and
|
|
||||||
You must retain, in the Source form of any Derivative Works that You distribute,
|
|
||||||
all copyright, patent, trademark, and attribution notices from the Source form
|
|
||||||
of the Work, excluding those notices that do not pertain to any part of the
|
|
||||||
Derivative Works; and
|
|
||||||
If the Work includes a "NOTICE" text file as part of its distribution, then any
|
|
||||||
Derivative Works that You distribute must include a readable copy of the
|
|
||||||
attribution notices contained within such NOTICE file, excluding those notices
|
|
||||||
that do not pertain to any part of the Derivative Works, in at least one of the
|
|
||||||
following places: within a NOTICE text file distributed as part of the
|
|
||||||
Derivative Works; within the Source form or documentation, if provided along
|
|
||||||
with the Derivative Works; or, within a display generated by the Derivative
|
|
||||||
Works, if and wherever such third-party notices normally appear. The contents of
|
|
||||||
the NOTICE file are for informational purposes only and do not modify the
|
|
||||||
License. You may add Your own attribution notices within Derivative Works that
|
|
||||||
You distribute, alongside or as an addendum to the NOTICE text from the Work,
|
|
||||||
provided that such additional attribution notices cannot be construed as
|
|
||||||
modifying the License.
|
|
||||||
You may add Your own copyright statement to Your modifications and may provide
|
|
||||||
additional or different license terms and conditions for use, reproduction, or
|
|
||||||
distribution of Your modifications, or for any such Derivative Works as a whole,
|
|
||||||
provided Your use, reproduction, and distribution of the Work otherwise complies
|
|
||||||
with the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions.
|
|
||||||
|
|
||||||
Unless You explicitly state otherwise, any Contribution intentionally submitted
|
|
||||||
for inclusion in the Work by You to the Licensor shall be under the terms and
|
|
||||||
conditions of this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify the terms of
|
|
||||||
any separate license agreement you may have executed with Licensor regarding
|
|
||||||
such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks.
|
|
||||||
|
|
||||||
This License does not grant permission to use the trade names, trademarks,
|
|
||||||
service marks, or product names of the Licensor, except as required for
|
|
||||||
reasonable and customary use in describing the origin of the Work and
|
|
||||||
reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty.
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, Licensor provides the
|
|
||||||
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
|
||||||
including, without limitation, any warranties or conditions of TITLE,
|
|
||||||
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
|
|
||||||
solely responsible for determining the appropriateness of using or
|
|
||||||
redistributing the Work and assume any risks associated with Your exercise of
|
|
||||||
permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability.
|
|
||||||
|
|
||||||
In no event and under no legal theory, whether in tort (including negligence),
|
|
||||||
contract, or otherwise, unless required by applicable law (such as deliberate
|
|
||||||
and grossly negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special, incidental,
|
|
||||||
or consequential damages of any character arising as a result of this License or
|
|
||||||
out of the use or inability to use the Work (including but not limited to
|
|
||||||
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
|
|
||||||
any and all other commercial damages or losses), even if such Contributor has
|
|
||||||
been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability.
|
|
||||||
|
|
||||||
While redistributing the Work or Derivative Works thereof, You may choose to
|
|
||||||
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
|
|
||||||
other liability obligations and/or rights consistent with this License. However,
|
|
||||||
in accepting such obligations, You may act only on Your own behalf and on Your
|
|
||||||
sole responsibility, not on behalf of any other Contributor, and only if You
|
|
||||||
agree to indemnify, defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason of your
|
|
||||||
accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following boilerplate
|
|
||||||
notice, with the fields enclosed by brackets "[]" replaced with your own
|
|
||||||
identifying information. (Don't include the brackets!) The text should be
|
|
||||||
enclosed in the appropriate comment syntax for the file format. We also
|
|
||||||
recommend that a file or class name and description of purpose be included on
|
|
||||||
the same "printed page" as the copyright notice for easier identification within
|
|
||||||
third-party archives.
|
|
||||||
|
|
||||||
Copyright [yyyy] [name of copyright owner]
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
213
vendor/github.com/coreos/go-systemd/dbus/dbus.go
generated
vendored
213
vendor/github.com/coreos/go-systemd/dbus/dbus.go
generated
vendored
@ -1,213 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// Integration with the systemd D-Bus API. See http://www.freedesktop.org/wiki/Software/systemd/dbus/
|
|
||||||
package dbus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/godbus/dbus"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
alpha = `abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ`
|
|
||||||
num = `0123456789`
|
|
||||||
alphanum = alpha + num
|
|
||||||
signalBuffer = 100
|
|
||||||
)
|
|
||||||
|
|
||||||
// needsEscape checks whether a byte in a potential dbus ObjectPath needs to be escaped
|
|
||||||
func needsEscape(i int, b byte) bool {
|
|
||||||
// Escape everything that is not a-z-A-Z-0-9
|
|
||||||
// Also escape 0-9 if it's the first character
|
|
||||||
return strings.IndexByte(alphanum, b) == -1 ||
|
|
||||||
(i == 0 && strings.IndexByte(num, b) != -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PathBusEscape sanitizes a constituent string of a dbus ObjectPath using the
|
|
||||||
// rules that systemd uses for serializing special characters.
|
|
||||||
func PathBusEscape(path string) string {
|
|
||||||
// Special case the empty string
|
|
||||||
if len(path) == 0 {
|
|
||||||
return "_"
|
|
||||||
}
|
|
||||||
n := []byte{}
|
|
||||||
for i := 0; i < len(path); i++ {
|
|
||||||
c := path[i]
|
|
||||||
if needsEscape(i, c) {
|
|
||||||
e := fmt.Sprintf("_%x", c)
|
|
||||||
n = append(n, []byte(e)...)
|
|
||||||
} else {
|
|
||||||
n = append(n, c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return string(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Conn is a connection to systemd's dbus endpoint.
|
|
||||||
type Conn struct {
|
|
||||||
// sysconn/sysobj are only used to call dbus methods
|
|
||||||
sysconn *dbus.Conn
|
|
||||||
sysobj dbus.BusObject
|
|
||||||
|
|
||||||
// sigconn/sigobj are only used to receive dbus signals
|
|
||||||
sigconn *dbus.Conn
|
|
||||||
sigobj dbus.BusObject
|
|
||||||
|
|
||||||
jobListener struct {
|
|
||||||
jobs map[dbus.ObjectPath]chan<- string
|
|
||||||
sync.Mutex
|
|
||||||
}
|
|
||||||
subscriber struct {
|
|
||||||
updateCh chan<- *SubStateUpdate
|
|
||||||
errCh chan<- error
|
|
||||||
sync.Mutex
|
|
||||||
ignore map[dbus.ObjectPath]int64
|
|
||||||
cleanIgnore int64
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// New establishes a connection to any available bus and authenticates.
|
|
||||||
// Callers should call Close() when done with the connection.
|
|
||||||
func New() (*Conn, error) {
|
|
||||||
conn, err := NewSystemConnection()
|
|
||||||
if err != nil && os.Geteuid() == 0 {
|
|
||||||
return NewSystemdConnection()
|
|
||||||
}
|
|
||||||
return conn, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSystemConnection establishes a connection to the system bus and authenticates.
|
|
||||||
// Callers should call Close() when done with the connection
|
|
||||||
func NewSystemConnection() (*Conn, error) {
|
|
||||||
return NewConnection(func() (*dbus.Conn, error) {
|
|
||||||
return dbusAuthHelloConnection(dbus.SystemBusPrivate)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewUserConnection establishes a connection to the session bus and
|
|
||||||
// authenticates. This can be used to connect to systemd user instances.
|
|
||||||
// Callers should call Close() when done with the connection.
|
|
||||||
func NewUserConnection() (*Conn, error) {
|
|
||||||
return NewConnection(func() (*dbus.Conn, error) {
|
|
||||||
return dbusAuthHelloConnection(dbus.SessionBusPrivate)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSystemdConnection establishes a private, direct connection to systemd.
|
|
||||||
// This can be used for communicating with systemd without a dbus daemon.
|
|
||||||
// Callers should call Close() when done with the connection.
|
|
||||||
func NewSystemdConnection() (*Conn, error) {
|
|
||||||
return NewConnection(func() (*dbus.Conn, error) {
|
|
||||||
// We skip Hello when talking directly to systemd.
|
|
||||||
return dbusAuthConnection(func() (*dbus.Conn, error) {
|
|
||||||
return dbus.Dial("unix:path=/run/systemd/private")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes an established connection
|
|
||||||
func (c *Conn) Close() {
|
|
||||||
c.sysconn.Close()
|
|
||||||
c.sigconn.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewConnection establishes a connection to a bus using a caller-supplied function.
|
|
||||||
// This allows connecting to remote buses through a user-supplied mechanism.
|
|
||||||
// The supplied function may be called multiple times, and should return independent connections.
|
|
||||||
// The returned connection must be fully initialised: the org.freedesktop.DBus.Hello call must have succeeded,
|
|
||||||
// and any authentication should be handled by the function.
|
|
||||||
func NewConnection(dialBus func() (*dbus.Conn, error)) (*Conn, error) {
|
|
||||||
sysconn, err := dialBus()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
sigconn, err := dialBus()
|
|
||||||
if err != nil {
|
|
||||||
sysconn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
c := &Conn{
|
|
||||||
sysconn: sysconn,
|
|
||||||
sysobj: systemdObject(sysconn),
|
|
||||||
sigconn: sigconn,
|
|
||||||
sigobj: systemdObject(sigconn),
|
|
||||||
}
|
|
||||||
|
|
||||||
c.subscriber.ignore = make(map[dbus.ObjectPath]int64)
|
|
||||||
c.jobListener.jobs = make(map[dbus.ObjectPath]chan<- string)
|
|
||||||
|
|
||||||
// Setup the listeners on jobs so that we can get completions
|
|
||||||
c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
|
|
||||||
"type='signal', interface='org.freedesktop.systemd1.Manager', member='JobRemoved'")
|
|
||||||
|
|
||||||
c.dispatch()
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetManagerProperty returns the value of a property on the org.freedesktop.systemd1.Manager
|
|
||||||
// interface. The value is returned in its string representation, as defined at
|
|
||||||
// https://developer.gnome.org/glib/unstable/gvariant-text.html
|
|
||||||
func (c *Conn) GetManagerProperty(prop string) (string, error) {
|
|
||||||
variant, err := c.sysobj.GetProperty("org.freedesktop.systemd1.Manager." + prop)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return variant.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func dbusAuthConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error) {
|
|
||||||
conn, err := createBus()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only use EXTERNAL method, and hardcode the uid (not username)
|
|
||||||
// to avoid a username lookup (which requires a dynamically linked
|
|
||||||
// libc)
|
|
||||||
methods := []dbus.Auth{dbus.AuthExternal(strconv.Itoa(os.Getuid()))}
|
|
||||||
|
|
||||||
err = conn.Auth(methods)
|
|
||||||
if err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func dbusAuthHelloConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error) {
|
|
||||||
conn, err := dbusAuthConnection(createBus)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = conn.Hello(); err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func systemdObject(conn *dbus.Conn) dbus.BusObject {
|
|
||||||
return conn.Object("org.freedesktop.systemd1", dbus.ObjectPath("/org/freedesktop/systemd1"))
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user