release version 0.9.37

Signed-off-by: zhangsong234 <zhangsong34@huawei.com>
This commit is contained in:
zhangsong234 2020-07-03 18:51:04 +08:00 committed by caihaomin
parent 2d51190752
commit adbb969f8b
835 changed files with 16 additions and 329679 deletions

130
CHANGELOG
View File

@ -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.

View File

@ -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 LicenseVersion 2
Mulan Permissive Software LicenseVersion 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 ITS 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 LicenseVersion 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.

View File

@ -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.

View File

@ -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

View File

@ -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.

View File

@ -1 +0,0 @@
v0.9

View File

@ -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

View File

@ -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
}

View File

@ -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
}
}
}

View File

@ -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
}

View File

@ -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
View File

@ -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
View File

@ -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
View File

@ -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=

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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": []
}
]
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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))
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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"]
}

View File

@ -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}})
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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 := &ethDriver{
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")
}

View File

@ -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())
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
View File

@ -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()
}

View File

@ -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)
},
}

View File

@ -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)
+}

View File

@ -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 {

View File

@ -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.

View File

@ -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"
)

View File

@ -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
}

View File

@ -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;
}

View File

@ -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 &ethtool{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)
}

View File

@ -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

View File

@ -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
}

View File

@ -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")
}

View File

@ -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]
}

View File

@ -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
}

View File

@ -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()
}

View File

@ -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
View File

@ -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)
},
}

View File

@ -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

Binary file not shown.

View File

@ -3,12 +3,12 @@
#Basic Information
Name: syscontainer-tools
Version: v0.9
Release: 37
Version: 0.9.37
Release: 1
Summary: syscontainer tools for IT, work with iSulad
License: Mulan PSL v2
URL: https://gitee.com/src-openeuler/syscontainer-tools
Source0: %{name}-src.tar.gz
URL: https://gitee.com/openeuler/syscontainer-tools
Source0: %{name}-%{version}.tar.gz
BuildRoot: %{_tmppath}/%{name}-root
#Dependency
@ -22,7 +22,7 @@ This is syscontainer tools, to make it work, you need a isulad and util-linux
#Build sections
%prep
%setup -q -c -n src/isula.org/syscontainer-tools
%setup -n %{name} -q
%build
make init && make
@ -108,8 +108,14 @@ chmod 0640 ${HOOK_SPEC}/hookspec.json
rm -rfv %{buildroot}
%changelog
* Tue Apr 27 2020 zhangtianyang <zhangtianyang2@huawei.com> - 0.9.37
- update license to Mulan PSL v2
* Tue Apr 26 2020 Zhangsong<zhangsong34@huawei.com> - 0.9.37-1
- 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
- Type:enhancement

3
syscontainer-tools.yaml Normal file
View File

@ -0,0 +1,3 @@
---
version_control: gitee
src_repo: openEuler/syscontainer-tools

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 $@
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -1 +0,0 @@
*.exe

View File

@ -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.

View File

@ -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.

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
)

View File

@ -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=

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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()
}

View File

@ -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()
}

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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.

View File

@ -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