Adding option to configure cgroup to start cri-containerd
Signed-off-by: Abhinandan Prativadi <abhi@docker.com>
This commit is contained in:
parent
80b57f54a6
commit
e1edeae4c9
@ -46,6 +46,7 @@ func main() {
|
|||||||
o.NetworkPluginConfDir,
|
o.NetworkPluginConfDir,
|
||||||
o.StreamServerAddress,
|
o.StreamServerAddress,
|
||||||
o.StreamServerPort,
|
o.StreamServerPort,
|
||||||
|
o.CgroupPath,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Exitf("Failed to create CRI containerd service %+v: %v", o, err)
|
glog.Exitf("Failed to create CRI containerd service %+v: %v", o, err)
|
||||||
|
@ -44,6 +44,8 @@ type CRIContainerdOptions struct {
|
|||||||
StreamServerAddress string
|
StreamServerAddress string
|
||||||
// StreamServerPort is the port streaming server is listening on.
|
// StreamServerPort is the port streaming server is listening on.
|
||||||
StreamServerPort string
|
StreamServerPort string
|
||||||
|
// CgroupPath is the path for the cgroup that cri-containerd is placed in.
|
||||||
|
CgroupPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCRIContainerdOptions returns a reference to CRIContainerdOptions
|
// NewCRIContainerdOptions returns a reference to CRIContainerdOptions
|
||||||
@ -71,6 +73,7 @@ func (c *CRIContainerdOptions) AddFlags(fs *pflag.FlagSet) {
|
|||||||
"", "The ip address streaming server is listening on. Default host interface is used if this is empty.")
|
"", "The ip address streaming server is listening on. Default host interface is used if this is empty.")
|
||||||
fs.StringVar(&c.StreamServerPort, "stream-port",
|
fs.StringVar(&c.StreamServerPort, "stream-port",
|
||||||
"10010", "The port streaming server is listening on.")
|
"10010", "The port streaming server is listening on.")
|
||||||
|
fs.StringVar(&c.CgroupPath, "cgroup-path", "", "The cgroup that cri-containerd is part of. By default cri-containerd is not placed in a cgroup")
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitFlags must be called after adding all cli options flags are defined and
|
// InitFlags must be called after adding all cli options flags are defined and
|
||||||
|
@ -24,12 +24,14 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containerd/cgroups"
|
||||||
"github.com/containerd/containerd/content"
|
"github.com/containerd/containerd/content"
|
||||||
"github.com/containerd/containerd/errdefs"
|
"github.com/containerd/containerd/errdefs"
|
||||||
"github.com/docker/distribution/reference"
|
"github.com/docker/distribution/reference"
|
||||||
imagedigest "github.com/opencontainers/go-digest"
|
imagedigest "github.com/opencontainers/go-digest"
|
||||||
"github.com/opencontainers/image-spec/identity"
|
"github.com/opencontainers/image-spec/identity"
|
||||||
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
||||||
|
|
||||||
@ -352,3 +354,23 @@ func resolveSymbolicLink(path string) (string, error) {
|
|||||||
}
|
}
|
||||||
return filepath.EvalSymlinks(path)
|
return filepath.EvalSymlinks(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// loadCgroup loads the cgroup associated with path if it exists and moves the current process into the cgroup. If the cgroup
|
||||||
|
// is not created it is created and returned.
|
||||||
|
func loadCgroup(cgroupPath string) (cgroups.Cgroup, error) {
|
||||||
|
cg, err := cgroups.Load(cgroups.V1, cgroups.StaticPath(cgroupPath))
|
||||||
|
if err != nil {
|
||||||
|
if err != cgroups.ErrCgroupDeleted {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if cg, err = cgroups.New(cgroups.V1, cgroups.StaticPath(cgroupPath), &specs.LinuxResources{}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := cg.Add(cgroups.Process{
|
||||||
|
Pid: os.Getpid(),
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return cg, nil
|
||||||
|
}
|
||||||
|
@ -84,6 +84,8 @@ type criContainerdService struct {
|
|||||||
client *containerd.Client
|
client *containerd.Client
|
||||||
// streamServer is the streaming server serves container streaming request.
|
// streamServer is the streaming server serves container streaming request.
|
||||||
streamServer streaming.Server
|
streamServer streaming.Server
|
||||||
|
// cgroupPath in which the cri-containerd is placed in
|
||||||
|
cgroupPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCRIContainerdService returns a new instance of CRIContainerdService
|
// NewCRIContainerdService returns a new instance of CRIContainerdService
|
||||||
@ -95,13 +97,20 @@ func NewCRIContainerdService(
|
|||||||
networkPluginBinDir,
|
networkPluginBinDir,
|
||||||
networkPluginConfDir,
|
networkPluginConfDir,
|
||||||
streamAddress,
|
streamAddress,
|
||||||
streamPort string) (CRIContainerdService, error) {
|
streamPort string,
|
||||||
|
cgroupPath string) (CRIContainerdService, error) {
|
||||||
// TODO(random-liu): [P2] Recover from runtime state and checkpoint.
|
// TODO(random-liu): [P2] Recover from runtime state and checkpoint.
|
||||||
|
|
||||||
client, err := containerd.New(containerdEndpoint, containerd.WithDefaultNamespace(k8sContainerdNamespace))
|
client, err := containerd.New(containerdEndpoint, containerd.WithDefaultNamespace(k8sContainerdNamespace))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to initialize containerd client with endpoint %q: %v", containerdEndpoint, err)
|
return nil, fmt.Errorf("failed to initialize containerd client with endpoint %q: %v", containerdEndpoint, err)
|
||||||
}
|
}
|
||||||
|
if cgroupPath != "" {
|
||||||
|
_, err := loadCgroup(cgroupPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to load cgroup for cgroup path %v: %v", cgroupPath, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
c := &criContainerdService{
|
c := &criContainerdService{
|
||||||
os: osinterface.RealOS{},
|
os: osinterface.RealOS{},
|
||||||
@ -118,6 +127,7 @@ func NewCRIContainerdService(
|
|||||||
eventService: client.EventService(),
|
eventService: client.EventService(),
|
||||||
contentStoreService: client.ContentStore(),
|
contentStoreService: client.ContentStore(),
|
||||||
client: client,
|
client: client,
|
||||||
|
cgroupPath: cgroupPath,
|
||||||
}
|
}
|
||||||
|
|
||||||
netPlugin, err := ocicni.InitCNI(networkPluginConfDir, networkPluginBinDir)
|
netPlugin, err := ocicni.InitCNI(networkPluginConfDir, networkPluginBinDir)
|
||||||
|
@ -3,6 +3,8 @@ github.com/boltdb/bolt v1.3.0-58-ge9cf4fa
|
|||||||
github.com/containerd/containerd cf09e32618398fc59fcb45bcfe9b4c0335972733
|
github.com/containerd/containerd cf09e32618398fc59fcb45bcfe9b4c0335972733
|
||||||
github.com/containerd/continuity cf279e6ac893682272b4479d4c67fd3abf878b4e
|
github.com/containerd/continuity cf279e6ac893682272b4479d4c67fd3abf878b4e
|
||||||
github.com/containerd/fifo fbfb6a11ec671efbe94ad1c12c2e98773f19e1e6
|
github.com/containerd/fifo fbfb6a11ec671efbe94ad1c12c2e98773f19e1e6
|
||||||
|
github.com/containerd/cgroups 7a5fdd8330119dc70d850260db8f3594d89d6943
|
||||||
|
github.com/coreos/go-systemd d2196463941895ee908e13531a23a39feb9e1243
|
||||||
github.com/containernetworking/cni v0.6.0
|
github.com/containernetworking/cni v0.6.0
|
||||||
github.com/containernetworking/plugins v0.6.0
|
github.com/containernetworking/plugins v0.6.0
|
||||||
github.com/cri-o/ocicni 0f90d35d89e9ab7e972a9edeb36b0aaffa250335
|
github.com/cri-o/ocicni 0f90d35d89e9ab7e972a9edeb36b0aaffa250335
|
||||||
@ -11,6 +13,7 @@ github.com/docker/distribution b38e5838b7b2f2ad48e06ec4b500011976080621
|
|||||||
github.com/docker/docker cc4da8112814cdbb00dbf23370f9ed764383de1f
|
github.com/docker/docker cc4da8112814cdbb00dbf23370f9ed764383de1f
|
||||||
github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
|
github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
|
||||||
github.com/docker/spdystream 449fdfce4d962303d702fec724ef0ad181c92528
|
github.com/docker/spdystream 449fdfce4d962303d702fec724ef0ad181c92528
|
||||||
|
github.com/docker/go-units 0dadbb0345b35ec7ef35e228dabb8de89a65bf52
|
||||||
github.com/emicklei/go-restful ff4f55a206334ef123e4f79bbf348980da81ca46
|
github.com/emicklei/go-restful ff4f55a206334ef123e4f79bbf348980da81ca46
|
||||||
github.com/fsnotify/fsnotify 7d7316ed6e1ed2de075aab8dfc76de5d158d66e1
|
github.com/fsnotify/fsnotify 7d7316ed6e1ed2de075aab8dfc76de5d158d66e1
|
||||||
github.com/ghodss/yaml 73d445a93680fa1a78ae23a5839bad48f32ba1ee
|
github.com/ghodss/yaml 73d445a93680fa1a78ae23a5839bad48f32ba1ee
|
||||||
@ -22,6 +25,7 @@ github.com/go-openapi/jsonpointer 46af16f9f7b149af66e5d1bd010e3574dc06de98
|
|||||||
github.com/go-openapi/jsonreference 13c6e3589ad90f49bd3e3bbe2c2cb3d7a4142272
|
github.com/go-openapi/jsonreference 13c6e3589ad90f49bd3e3bbe2c2cb3d7a4142272
|
||||||
github.com/go-openapi/spec 6aced65f8501fe1217321abf0749d354824ba2ff
|
github.com/go-openapi/spec 6aced65f8501fe1217321abf0749d354824ba2ff
|
||||||
github.com/go-openapi/swag 1d0bd113de87027671077d3c71eb3ac5d7dbba72
|
github.com/go-openapi/swag 1d0bd113de87027671077d3c71eb3ac5d7dbba72
|
||||||
|
github.com/godbus/dbus 97646858c46433e4afb3432ad28c12e968efa298
|
||||||
github.com/jpillora/backoff 06c7a16c845dc8e0bf575fafeeca0f5462f5eb4d
|
github.com/jpillora/backoff 06c7a16c845dc8e0bf575fafeeca0f5462f5eb4d
|
||||||
github.com/juju/ratelimit 5b9ff866471762aa2ab2dced63c9fb6f53921342
|
github.com/juju/ratelimit 5b9ff866471762aa2ab2dced63c9fb6f53921342
|
||||||
github.com/mailru/easyjson d5b7844b561a7bc640052f1b935f7b800330d7e0
|
github.com/mailru/easyjson d5b7844b561a7bc640052f1b935f7b800330d7e0
|
||||||
|
201
vendor/github.com/containerd/cgroups/LICENSE
generated
vendored
Normal file
201
vendor/github.com/containerd/cgroups/LICENSE
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
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:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) 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
|
||||||
|
|
||||||
|
(d) 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.
|
112
vendor/github.com/containerd/cgroups/README.md
generated
vendored
Normal file
112
vendor/github.com/containerd/cgroups/README.md
generated
vendored
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
# cgroups
|
||||||
|
|
||||||
|
[](https://travis-ci.org/containerd/cgroups)
|
||||||
|
|
||||||
|
[](https://codecov.io/gh/containerd/cgroups)
|
||||||
|
|
||||||
|
Go package for creating, managing, inspecting, and destroying cgroups.
|
||||||
|
The resources format for settings on the cgroup uses the OCI runtime-spec found
|
||||||
|
[here](https://github.com/opencontainers/runtime-spec).
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Create a new cgroup
|
||||||
|
|
||||||
|
This creates a new cgroup using a static path for all subsystems under `/test`.
|
||||||
|
|
||||||
|
* /sys/fs/cgroup/cpu/test
|
||||||
|
* /sys/fs/cgroup/memory/test
|
||||||
|
* etc....
|
||||||
|
|
||||||
|
It uses a single hierarchy and specifies cpu shares as a resource constraint and
|
||||||
|
uses the v1 implementation of cgroups.
|
||||||
|
|
||||||
|
|
||||||
|
```go
|
||||||
|
shares := uint64(100)
|
||||||
|
control, err := cgroups.New(cgroups.V1, cgroups.StaticPath("/test"), &specs.LinuxResources{
|
||||||
|
CPU: &specs.CPU{
|
||||||
|
Shares: &shares,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
defer control.Delete()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create with systemd slice support
|
||||||
|
|
||||||
|
|
||||||
|
```go
|
||||||
|
control, err := cgroups.New(cgroups.Systemd, cgroups.Slice("system.slice", "runc-test"), &specs.LinuxResources{
|
||||||
|
CPU: &specs.CPU{
|
||||||
|
Shares: &shares,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Load an existing cgroup
|
||||||
|
|
||||||
|
```go
|
||||||
|
control, err = cgroups.Load(cgroups.V1, cgroups.StaticPath("/test"))
|
||||||
|
```
|
||||||
|
|
||||||
|
### Add a process to the cgroup
|
||||||
|
|
||||||
|
```go
|
||||||
|
if err := control.Add(cgroups.Process{Pid:1234}); err != nil {
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Update the cgroup
|
||||||
|
|
||||||
|
To update the resources applied in the cgroup
|
||||||
|
|
||||||
|
```go
|
||||||
|
shares = uint64(200)
|
||||||
|
if err := control.Update(&specs.LinuxResources{
|
||||||
|
CPU: &specs.CPU{
|
||||||
|
Shares: &shares,
|
||||||
|
},
|
||||||
|
}); err != nil {
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Freeze and Thaw the cgroup
|
||||||
|
|
||||||
|
```go
|
||||||
|
if err := control.Freeze(); err != nil {
|
||||||
|
}
|
||||||
|
if err := control.Thaw(); err != nil {
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### List all processes in the cgroup or recursively
|
||||||
|
|
||||||
|
```go
|
||||||
|
processes, err := control.Processes(cgroups.Devices, recursive)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Get Stats on the cgroup
|
||||||
|
|
||||||
|
```go
|
||||||
|
stats, err := control.Stat()
|
||||||
|
```
|
||||||
|
|
||||||
|
By adding `cgroups.IgnoreNotExist` all non-existent files will be ignored, e.g. swap memory stats without swap enabled
|
||||||
|
```go
|
||||||
|
stats, err := control.Stat(cgroups.IgnoreNotExist)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Move process across cgroups
|
||||||
|
|
||||||
|
This allows you to take processes from one cgroup and move them to another.
|
||||||
|
|
||||||
|
```go
|
||||||
|
err := control.MoveTo(destination)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create subcgroup
|
||||||
|
|
||||||
|
```go
|
||||||
|
subCgroup, err := control.New("child", resources)
|
||||||
|
```
|
323
vendor/github.com/containerd/cgroups/blkio.go
generated
vendored
Normal file
323
vendor/github.com/containerd/cgroups/blkio.go
generated
vendored
Normal file
@ -0,0 +1,323 @@
|
|||||||
|
package cgroups
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewBlkio(root string) *blkioController {
|
||||||
|
return &blkioController{
|
||||||
|
root: filepath.Join(root, string(Blkio)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type blkioController struct {
|
||||||
|
root string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *blkioController) Name() Name {
|
||||||
|
return Blkio
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *blkioController) Path(path string) string {
|
||||||
|
return filepath.Join(b.root, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *blkioController) Create(path string, resources *specs.LinuxResources) error {
|
||||||
|
if err := os.MkdirAll(b.Path(path), defaultDirPerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if resources.BlockIO == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, t := range createBlkioSettings(resources.BlockIO) {
|
||||||
|
if t.value != nil {
|
||||||
|
if err := ioutil.WriteFile(
|
||||||
|
filepath.Join(b.Path(path), fmt.Sprintf("blkio.%s", t.name)),
|
||||||
|
t.format(t.value),
|
||||||
|
defaultFilePerm,
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *blkioController) Update(path string, resources *specs.LinuxResources) error {
|
||||||
|
return b.Create(path, resources)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *blkioController) Stat(path string, stats *Stats) error {
|
||||||
|
stats.Blkio = &BlkioStat{}
|
||||||
|
settings := []blkioStatSettings{
|
||||||
|
{
|
||||||
|
name: "throttle.io_serviced",
|
||||||
|
entry: &stats.Blkio.IoServicedRecursive,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "throttle.io_service_bytes",
|
||||||
|
entry: &stats.Blkio.IoServiceBytesRecursive,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// Try to read CFQ stats available on all CFQ enabled kernels first
|
||||||
|
if _, err := os.Lstat(filepath.Join(b.Path(path), fmt.Sprintf("blkio.io_serviced_recursive"))); err == nil {
|
||||||
|
settings = append(settings,
|
||||||
|
blkioStatSettings{
|
||||||
|
name: "sectors_recursive",
|
||||||
|
entry: &stats.Blkio.SectorsRecursive,
|
||||||
|
},
|
||||||
|
blkioStatSettings{
|
||||||
|
name: "io_service_bytes_recursive",
|
||||||
|
entry: &stats.Blkio.IoServiceBytesRecursive,
|
||||||
|
},
|
||||||
|
blkioStatSettings{
|
||||||
|
name: "io_serviced_recursive",
|
||||||
|
entry: &stats.Blkio.IoServicedRecursive,
|
||||||
|
},
|
||||||
|
blkioStatSettings{
|
||||||
|
name: "io_queued_recursive",
|
||||||
|
entry: &stats.Blkio.IoQueuedRecursive,
|
||||||
|
},
|
||||||
|
blkioStatSettings{
|
||||||
|
name: "io_service_time_recursive",
|
||||||
|
entry: &stats.Blkio.IoServiceTimeRecursive,
|
||||||
|
},
|
||||||
|
blkioStatSettings{
|
||||||
|
name: "io_wait_time_recursive",
|
||||||
|
entry: &stats.Blkio.IoWaitTimeRecursive,
|
||||||
|
},
|
||||||
|
blkioStatSettings{
|
||||||
|
name: "io_merged_recursive",
|
||||||
|
entry: &stats.Blkio.IoMergedRecursive,
|
||||||
|
},
|
||||||
|
blkioStatSettings{
|
||||||
|
name: "time_recursive",
|
||||||
|
entry: &stats.Blkio.IoTimeRecursive,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
devices, err := getDevices("/dev")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, t := range settings {
|
||||||
|
if err := b.readEntry(devices, path, t.name, t.entry); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *blkioController) readEntry(devices map[deviceKey]string, path, name string, entry *[]BlkioEntry) error {
|
||||||
|
f, err := os.Open(filepath.Join(b.Path(path), fmt.Sprintf("blkio.%s", name)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
sc := bufio.NewScanner(f)
|
||||||
|
for sc.Scan() {
|
||||||
|
if err := sc.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// format: dev type amount
|
||||||
|
fields := strings.FieldsFunc(sc.Text(), splitBlkioStatLine)
|
||||||
|
if len(fields) < 3 {
|
||||||
|
if len(fields) == 2 && fields[0] == "Total" {
|
||||||
|
// skip total line
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("Invalid line found while parsing %s: %s", path, sc.Text())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
major, err := strconv.ParseUint(fields[0], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
minor, err := strconv.ParseUint(fields[1], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
op := ""
|
||||||
|
valueField := 2
|
||||||
|
if len(fields) == 4 {
|
||||||
|
op = fields[2]
|
||||||
|
valueField = 3
|
||||||
|
}
|
||||||
|
v, err := strconv.ParseUint(fields[valueField], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*entry = append(*entry, BlkioEntry{
|
||||||
|
Device: devices[deviceKey{major, minor}],
|
||||||
|
Major: major,
|
||||||
|
Minor: minor,
|
||||||
|
Op: op,
|
||||||
|
Value: v,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createBlkioSettings(blkio *specs.LinuxBlockIO) []blkioSettings {
|
||||||
|
settings := []blkioSettings{
|
||||||
|
{
|
||||||
|
name: "weight",
|
||||||
|
value: blkio.Weight,
|
||||||
|
format: uintf,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "leaf_weight",
|
||||||
|
value: blkio.LeafWeight,
|
||||||
|
format: uintf,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, wd := range blkio.WeightDevice {
|
||||||
|
settings = append(settings,
|
||||||
|
blkioSettings{
|
||||||
|
name: "weight_device",
|
||||||
|
value: wd,
|
||||||
|
format: weightdev,
|
||||||
|
},
|
||||||
|
blkioSettings{
|
||||||
|
name: "leaf_weight_device",
|
||||||
|
value: wd,
|
||||||
|
format: weightleafdev,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, t := range []struct {
|
||||||
|
name string
|
||||||
|
list []specs.LinuxThrottleDevice
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "throttle.read_bps_device",
|
||||||
|
list: blkio.ThrottleReadBpsDevice,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "throttle.read_iops_device",
|
||||||
|
list: blkio.ThrottleReadIOPSDevice,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "throttle.write_bps_device",
|
||||||
|
list: blkio.ThrottleWriteBpsDevice,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "throttle.write_iops_device",
|
||||||
|
list: blkio.ThrottleWriteIOPSDevice,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
for _, td := range t.list {
|
||||||
|
settings = append(settings, blkioSettings{
|
||||||
|
name: t.name,
|
||||||
|
value: td,
|
||||||
|
format: throttleddev,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return settings
|
||||||
|
}
|
||||||
|
|
||||||
|
type blkioSettings struct {
|
||||||
|
name string
|
||||||
|
value interface{}
|
||||||
|
format func(v interface{}) []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type blkioStatSettings struct {
|
||||||
|
name string
|
||||||
|
entry *[]BlkioEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
func uintf(v interface{}) []byte {
|
||||||
|
return []byte(strconv.FormatUint(uint64(*v.(*uint16)), 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
func weightdev(v interface{}) []byte {
|
||||||
|
wd := v.(specs.LinuxWeightDevice)
|
||||||
|
return []byte(fmt.Sprintf("%d:%d %d", wd.Major, wd.Minor, wd.Weight))
|
||||||
|
}
|
||||||
|
|
||||||
|
func weightleafdev(v interface{}) []byte {
|
||||||
|
wd := v.(specs.LinuxWeightDevice)
|
||||||
|
return []byte(fmt.Sprintf("%d:%d %d", wd.Major, wd.Minor, wd.LeafWeight))
|
||||||
|
}
|
||||||
|
|
||||||
|
func throttleddev(v interface{}) []byte {
|
||||||
|
td := v.(specs.LinuxThrottleDevice)
|
||||||
|
return []byte(fmt.Sprintf("%d:%d %d", td.Major, td.Minor, td.Rate))
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitBlkioStatLine(r rune) bool {
|
||||||
|
return r == ' ' || r == ':'
|
||||||
|
}
|
||||||
|
|
||||||
|
type deviceKey struct {
|
||||||
|
major, minor uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// getDevices makes a best effort attempt to read all the devices into a map
|
||||||
|
// keyed by major and minor number. Since devices may be mapped multiple times,
|
||||||
|
// we err on taking the first occurrence.
|
||||||
|
func getDevices(path string) (map[deviceKey]string, error) {
|
||||||
|
// TODO(stevvooe): We are ignoring lots of errors. It might be kind of
|
||||||
|
// challenging to debug this if we aren't mapping devices correctly.
|
||||||
|
// Consider logging these errors.
|
||||||
|
devices := map[deviceKey]string{}
|
||||||
|
if err := filepath.Walk(path, func(p string, fi os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case fi.IsDir():
|
||||||
|
switch fi.Name() {
|
||||||
|
case "pts", "shm", "fd", "mqueue", ".lxc", ".lxd-mounts":
|
||||||
|
return filepath.SkipDir
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
case fi.Name() == "console":
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
if fi.Mode()&os.ModeDevice == 0 {
|
||||||
|
// skip non-devices
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
st, ok := fi.Sys().(*syscall.Stat_t)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("%s: unable to convert to system stat", p)
|
||||||
|
}
|
||||||
|
|
||||||
|
key := deviceKey{major(st.Rdev), minor(st.Rdev)}
|
||||||
|
if _, ok := devices[key]; ok {
|
||||||
|
return nil // skip it if we have already populated the path.
|
||||||
|
}
|
||||||
|
|
||||||
|
devices[key] = p
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return devices, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func major(devNumber uint64) uint64 {
|
||||||
|
return (devNumber >> 8) & 0xfff
|
||||||
|
}
|
||||||
|
|
||||||
|
func minor(devNumber uint64) uint64 {
|
||||||
|
return (devNumber & 0xff) | ((devNumber >> 12) & 0xfff00)
|
||||||
|
}
|
389
vendor/github.com/containerd/cgroups/cgroup.go
generated
vendored
Normal file
389
vendor/github.com/containerd/cgroups/cgroup.go
generated
vendored
Normal file
@ -0,0 +1,389 @@
|
|||||||
|
package cgroups
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// New returns a new control via the cgroup cgroups interface
|
||||||
|
func New(hierarchy Hierarchy, path Path, resources *specs.LinuxResources) (Cgroup, error) {
|
||||||
|
subsystems, err := hierarchy()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, s := range subsystems {
|
||||||
|
if err := initializeSubsystem(s, path, resources); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &cgroup{
|
||||||
|
path: path,
|
||||||
|
subsystems: subsystems,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load will load an existing cgroup and allow it to be controlled
|
||||||
|
func Load(hierarchy Hierarchy, path Path) (Cgroup, error) {
|
||||||
|
subsystems, err := hierarchy()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// check the the subsystems still exist
|
||||||
|
for _, s := range pathers(subsystems) {
|
||||||
|
p, err := path(s.Name())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, err := os.Lstat(s.Path(p)); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil, ErrCgroupDeleted
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &cgroup{
|
||||||
|
path: path,
|
||||||
|
subsystems: subsystems,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type cgroup struct {
|
||||||
|
path Path
|
||||||
|
|
||||||
|
subsystems []Subsystem
|
||||||
|
mu sync.Mutex
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new sub cgroup
|
||||||
|
func (c *cgroup) New(name string, resources *specs.LinuxResources) (Cgroup, error) {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
if c.err != nil {
|
||||||
|
return nil, c.err
|
||||||
|
}
|
||||||
|
path := subPath(c.path, name)
|
||||||
|
for _, s := range c.subsystems {
|
||||||
|
if err := initializeSubsystem(s, path, resources); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &cgroup{
|
||||||
|
path: path,
|
||||||
|
subsystems: c.subsystems,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subsystems returns all the subsystems that are currently being
|
||||||
|
// consumed by the group
|
||||||
|
func (c *cgroup) Subsystems() []Subsystem {
|
||||||
|
return c.subsystems
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add moves the provided process into the new cgroup
|
||||||
|
func (c *cgroup) Add(process Process) error {
|
||||||
|
if process.Pid <= 0 {
|
||||||
|
return ErrInvalidPid
|
||||||
|
}
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
if c.err != nil {
|
||||||
|
return c.err
|
||||||
|
}
|
||||||
|
return c.add(process)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cgroup) add(process Process) error {
|
||||||
|
for _, s := range pathers(c.subsystems) {
|
||||||
|
p, err := c.path(s.Name())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile(
|
||||||
|
filepath.Join(s.Path(p), cgroupProcs),
|
||||||
|
[]byte(strconv.Itoa(process.Pid)),
|
||||||
|
defaultFilePerm,
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete will remove the control group from each of the subsystems registered
|
||||||
|
func (c *cgroup) Delete() error {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
if c.err != nil {
|
||||||
|
return c.err
|
||||||
|
}
|
||||||
|
var errors []string
|
||||||
|
for _, s := range c.subsystems {
|
||||||
|
if d, ok := s.(deleter); ok {
|
||||||
|
sp, err := c.path(s.Name())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := d.Delete(sp); err != nil {
|
||||||
|
errors = append(errors, string(s.Name()))
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if p, ok := s.(pather); ok {
|
||||||
|
sp, err := c.path(s.Name())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
path := p.Path(sp)
|
||||||
|
if err := remove(path); err != nil {
|
||||||
|
errors = append(errors, path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(errors) > 0 {
|
||||||
|
return fmt.Errorf("cgroups: unable to remove paths %s", strings.Join(errors, ", "))
|
||||||
|
}
|
||||||
|
c.err = ErrCgroupDeleted
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat returns the current stats for the cgroup
|
||||||
|
func (c *cgroup) Stat(handlers ...ErrorHandler) (*Stats, error) {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
if c.err != nil {
|
||||||
|
return nil, c.err
|
||||||
|
}
|
||||||
|
if len(handlers) == 0 {
|
||||||
|
handlers = append(handlers, errPassthrough)
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
stats = &Stats{}
|
||||||
|
wg = &sync.WaitGroup{}
|
||||||
|
errs = make(chan error, len(c.subsystems))
|
||||||
|
)
|
||||||
|
for _, s := range c.subsystems {
|
||||||
|
if ss, ok := s.(stater); ok {
|
||||||
|
sp, err := c.path(s.Name())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
if err := ss.Stat(sp, stats); err != nil {
|
||||||
|
for _, eh := range handlers {
|
||||||
|
if herr := eh(err); herr != nil {
|
||||||
|
errs <- herr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
close(errs)
|
||||||
|
for err := range errs {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return stats, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update updates the cgroup with the new resource values provided
|
||||||
|
//
|
||||||
|
// Be prepared to handle EBUSY when trying to update a cgroup with
|
||||||
|
// live processes and other operations like Stats being performed at the
|
||||||
|
// same time
|
||||||
|
func (c *cgroup) Update(resources *specs.LinuxResources) error {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
if c.err != nil {
|
||||||
|
return c.err
|
||||||
|
}
|
||||||
|
for _, s := range c.subsystems {
|
||||||
|
if u, ok := s.(updater); ok {
|
||||||
|
sp, err := c.path(s.Name())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := u.Update(sp, resources); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Processes returns the processes running inside the cgroup along
|
||||||
|
// with the subsystem used, pid, and path
|
||||||
|
func (c *cgroup) Processes(subsystem Name, recursive bool) ([]Process, error) {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
if c.err != nil {
|
||||||
|
return nil, c.err
|
||||||
|
}
|
||||||
|
return c.processes(subsystem, recursive)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cgroup) processes(subsystem Name, recursive bool) ([]Process, error) {
|
||||||
|
s := c.getSubsystem(subsystem)
|
||||||
|
sp, err := c.path(subsystem)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
path := s.(pather).Path(sp)
|
||||||
|
var processes []Process
|
||||||
|
err = filepath.Walk(path, func(p string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !recursive && info.IsDir() {
|
||||||
|
if p == path {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
dir, name := filepath.Split(p)
|
||||||
|
if name != cgroupProcs {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
procs, err := readPids(dir, subsystem)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
processes = append(processes, procs...)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return processes, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Freeze freezes the entire cgroup and all the processes inside it
|
||||||
|
func (c *cgroup) Freeze() error {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
if c.err != nil {
|
||||||
|
return c.err
|
||||||
|
}
|
||||||
|
s := c.getSubsystem(Freezer)
|
||||||
|
if s == nil {
|
||||||
|
return ErrFreezerNotSupported
|
||||||
|
}
|
||||||
|
sp, err := c.path(Freezer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return s.(*freezerController).Freeze(sp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thaw thaws out the cgroup and all the processes inside it
|
||||||
|
func (c *cgroup) Thaw() error {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
if c.err != nil {
|
||||||
|
return c.err
|
||||||
|
}
|
||||||
|
s := c.getSubsystem(Freezer)
|
||||||
|
if s == nil {
|
||||||
|
return ErrFreezerNotSupported
|
||||||
|
}
|
||||||
|
sp, err := c.path(Freezer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return s.(*freezerController).Thaw(sp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OOMEventFD returns the memory cgroup's out of memory event fd that triggers
|
||||||
|
// when processes inside the cgroup receive an oom event
|
||||||
|
func (c *cgroup) OOMEventFD() (uintptr, error) {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
if c.err != nil {
|
||||||
|
return 0, c.err
|
||||||
|
}
|
||||||
|
s := c.getSubsystem(Memory)
|
||||||
|
if s == nil {
|
||||||
|
return 0, ErrMemoryNotSupported
|
||||||
|
}
|
||||||
|
sp, err := c.path(Memory)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return s.(*memoryController).OOMEventFD(sp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// State returns the state of the cgroup and its processes
|
||||||
|
func (c *cgroup) State() State {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
c.checkExists()
|
||||||
|
if c.err != nil && c.err == ErrCgroupDeleted {
|
||||||
|
return Deleted
|
||||||
|
}
|
||||||
|
s := c.getSubsystem(Freezer)
|
||||||
|
if s == nil {
|
||||||
|
return Thawed
|
||||||
|
}
|
||||||
|
sp, err := c.path(Freezer)
|
||||||
|
if err != nil {
|
||||||
|
return Unknown
|
||||||
|
}
|
||||||
|
state, err := s.(*freezerController).state(sp)
|
||||||
|
if err != nil {
|
||||||
|
return Unknown
|
||||||
|
}
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
// MoveTo does a recursive move subsystem by subsystem of all the processes
|
||||||
|
// inside the group
|
||||||
|
func (c *cgroup) MoveTo(destination Cgroup) error {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
if c.err != nil {
|
||||||
|
return c.err
|
||||||
|
}
|
||||||
|
for _, s := range c.subsystems {
|
||||||
|
processes, err := c.processes(s.Name(), true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, p := range processes {
|
||||||
|
if err := destination.Add(p); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cgroup) getSubsystem(n Name) Subsystem {
|
||||||
|
for _, s := range c.subsystems {
|
||||||
|
if s.Name() == n {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cgroup) checkExists() {
|
||||||
|
for _, s := range pathers(c.subsystems) {
|
||||||
|
p, err := c.path(s.Name())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err := os.Lstat(s.Path(p)); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
c.err = ErrCgroupDeleted
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
58
vendor/github.com/containerd/cgroups/control.go
generated
vendored
Normal file
58
vendor/github.com/containerd/cgroups/control.go
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package cgroups
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
cgroupProcs = "cgroup.procs"
|
||||||
|
defaultDirPerm = 0755
|
||||||
|
)
|
||||||
|
|
||||||
|
// defaultFilePerm is a var so that the test framework can change the filemode
|
||||||
|
// of all files created when the tests are running. The difference between the
|
||||||
|
// tests and real world use is that files like "cgroup.procs" will exist when writing
|
||||||
|
// to a read cgroup filesystem and do not exist prior when running in the tests.
|
||||||
|
// this is set to a non 0 value in the test code
|
||||||
|
var defaultFilePerm = os.FileMode(0)
|
||||||
|
|
||||||
|
type Process struct {
|
||||||
|
// Subsystem is the name of the subsystem that the process is in
|
||||||
|
Subsystem Name
|
||||||
|
// Pid is the process id of the process
|
||||||
|
Pid int
|
||||||
|
// Path is the full path of the subsystem and location that the process is in
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cgroup handles interactions with the individual groups to perform
|
||||||
|
// actions on them as them main interface to this cgroup package
|
||||||
|
type Cgroup interface {
|
||||||
|
// New creates a new cgroup under the calling cgroup
|
||||||
|
New(string, *specs.LinuxResources) (Cgroup, error)
|
||||||
|
// Add adds a process to the cgroup
|
||||||
|
Add(Process) error
|
||||||
|
// Delete removes the cgroup as a whole
|
||||||
|
Delete() error
|
||||||
|
// MoveTo moves all the processes under the calling cgroup to the provided one
|
||||||
|
// subsystems are moved one at a time
|
||||||
|
MoveTo(Cgroup) error
|
||||||
|
// Stat returns the stats for all subsystems in the cgroup
|
||||||
|
Stat(...ErrorHandler) (*Stats, error)
|
||||||
|
// Update updates all the subsystems with the provided resource changes
|
||||||
|
Update(resources *specs.LinuxResources) error
|
||||||
|
// Processes returns all the processes in a select subsystem for the cgroup
|
||||||
|
Processes(Name, bool) ([]Process, error)
|
||||||
|
// Freeze freezes or pauses all processes inside the cgroup
|
||||||
|
Freeze() error
|
||||||
|
// Thaw thaw or resumes all processes inside the cgroup
|
||||||
|
Thaw() error
|
||||||
|
// OOMEventFD returns the memory subsystem's event fd for OOM events
|
||||||
|
OOMEventFD() (uintptr, error)
|
||||||
|
// State returns the cgroups current state
|
||||||
|
State() State
|
||||||
|
// Subsystems returns all the subsystems in the cgroup
|
||||||
|
Subsystems() []Subsystem
|
||||||
|
}
|
120
vendor/github.com/containerd/cgroups/cpu.go
generated
vendored
Normal file
120
vendor/github.com/containerd/cgroups/cpu.go
generated
vendored
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
package cgroups
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewCpu(root string) *cpuController {
|
||||||
|
return &cpuController{
|
||||||
|
root: filepath.Join(root, string(Cpu)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type cpuController struct {
|
||||||
|
root string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cpuController) Name() Name {
|
||||||
|
return Cpu
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cpuController) Path(path string) string {
|
||||||
|
return filepath.Join(c.root, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cpuController) Create(path string, resources *specs.LinuxResources) error {
|
||||||
|
if err := os.MkdirAll(c.Path(path), defaultDirPerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if cpu := resources.CPU; cpu != nil {
|
||||||
|
for _, t := range []struct {
|
||||||
|
name string
|
||||||
|
ivalue *int64
|
||||||
|
uvalue *uint64
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "rt_period_us",
|
||||||
|
uvalue: cpu.RealtimePeriod,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rt_runtime_us",
|
||||||
|
ivalue: cpu.RealtimeRuntime,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "shares",
|
||||||
|
uvalue: cpu.Shares,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cfs_period_us",
|
||||||
|
uvalue: cpu.Period,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cfs_quota_us",
|
||||||
|
ivalue: cpu.Quota,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
var value []byte
|
||||||
|
if t.uvalue != nil {
|
||||||
|
value = []byte(strconv.FormatUint(*t.uvalue, 10))
|
||||||
|
} else if t.ivalue != nil {
|
||||||
|
value = []byte(strconv.FormatInt(*t.ivalue, 10))
|
||||||
|
}
|
||||||
|
if value != nil {
|
||||||
|
if err := ioutil.WriteFile(
|
||||||
|
filepath.Join(c.Path(path), fmt.Sprintf("cpu.%s", t.name)),
|
||||||
|
value,
|
||||||
|
defaultFilePerm,
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cpuController) Update(path string, resources *specs.LinuxResources) error {
|
||||||
|
return c.Create(path, resources)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cpuController) Stat(path string, stats *Stats) error {
|
||||||
|
f, err := os.Open(filepath.Join(c.Path(path), "cpu.stat"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
// get or create the cpu field because cpuacct can also set values on this struct
|
||||||
|
stats.cpuMu.Lock()
|
||||||
|
cpu := stats.Cpu
|
||||||
|
if cpu == nil {
|
||||||
|
cpu = &CpuStat{}
|
||||||
|
stats.Cpu = cpu
|
||||||
|
}
|
||||||
|
stats.cpuMu.Unlock()
|
||||||
|
sc := bufio.NewScanner(f)
|
||||||
|
for sc.Scan() {
|
||||||
|
if err := sc.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
key, v, err := parseKV(sc.Text())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch key {
|
||||||
|
case "nr_periods":
|
||||||
|
cpu.Throttling.Periods = v
|
||||||
|
case "nr_throttled":
|
||||||
|
cpu.Throttling.ThrottledPeriods = v
|
||||||
|
case "throttled_time":
|
||||||
|
cpu.Throttling.ThrottledTime = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
112
vendor/github.com/containerd/cgroups/cpuacct.go
generated
vendored
Normal file
112
vendor/github.com/containerd/cgroups/cpuacct.go
generated
vendored
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
package cgroups
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const nanosecondsInSecond = 1000000000
|
||||||
|
|
||||||
|
var clockTicks = getClockTicks()
|
||||||
|
|
||||||
|
func NewCpuacct(root string) *cpuacctController {
|
||||||
|
return &cpuacctController{
|
||||||
|
root: filepath.Join(root, string(Cpuacct)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type cpuacctController struct {
|
||||||
|
root string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cpuacctController) Name() Name {
|
||||||
|
return Cpuacct
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cpuacctController) Path(path string) string {
|
||||||
|
return filepath.Join(c.root, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cpuacctController) Stat(path string, stats *Stats) error {
|
||||||
|
user, kernel, err := c.getUsage(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
total, err := readUint(filepath.Join(c.Path(path), "cpuacct.usage"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
percpu, err := c.percpuUsage(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
stats.cpuMu.Lock()
|
||||||
|
cpu := stats.Cpu
|
||||||
|
if cpu == nil {
|
||||||
|
cpu = &CpuStat{}
|
||||||
|
stats.Cpu = cpu
|
||||||
|
}
|
||||||
|
stats.cpuMu.Unlock()
|
||||||
|
cpu.Usage.Total = total
|
||||||
|
cpu.Usage.User = user
|
||||||
|
cpu.Usage.Kernel = kernel
|
||||||
|
cpu.Usage.PerCpu = percpu
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cpuacctController) percpuUsage(path string) ([]uint64, error) {
|
||||||
|
var usage []uint64
|
||||||
|
data, err := ioutil.ReadFile(filepath.Join(c.Path(path), "cpuacct.usage_percpu"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, v := range strings.Fields(string(data)) {
|
||||||
|
u, err := strconv.ParseUint(v, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
usage = append(usage, u)
|
||||||
|
}
|
||||||
|
return usage, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cpuacctController) getUsage(path string) (user uint64, kernel uint64, err error) {
|
||||||
|
statPath := filepath.Join(c.Path(path), "cpuacct.stat")
|
||||||
|
data, err := ioutil.ReadFile(statPath)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
fields := strings.Fields(string(data))
|
||||||
|
if len(fields) != 4 {
|
||||||
|
return 0, 0, fmt.Errorf("%q is expected to have 4 fields", statPath)
|
||||||
|
}
|
||||||
|
for _, t := range []struct {
|
||||||
|
index int
|
||||||
|
name string
|
||||||
|
value *uint64
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
index: 0,
|
||||||
|
name: "user",
|
||||||
|
value: &user,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 2,
|
||||||
|
name: "system",
|
||||||
|
value: &kernel,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
if fields[t.index] != t.name {
|
||||||
|
return 0, 0, fmt.Errorf("expected field %q but found %q in %q", t.name, fields[t.index], statPath)
|
||||||
|
}
|
||||||
|
v, err := strconv.ParseUint(fields[t.index+1], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
*t.value = v
|
||||||
|
}
|
||||||
|
return (user * nanosecondsInSecond) / clockTicks, (kernel * nanosecondsInSecond) / clockTicks, nil
|
||||||
|
}
|
139
vendor/github.com/containerd/cgroups/cpuset.go
generated
vendored
Normal file
139
vendor/github.com/containerd/cgroups/cpuset.go
generated
vendored
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
package cgroups
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewCputset(root string) *cpusetController {
|
||||||
|
return &cpusetController{
|
||||||
|
root: filepath.Join(root, string(Cpuset)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type cpusetController struct {
|
||||||
|
root string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cpusetController) Name() Name {
|
||||||
|
return Cpuset
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cpusetController) Path(path string) string {
|
||||||
|
return filepath.Join(c.root, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cpusetController) Create(path string, resources *specs.LinuxResources) error {
|
||||||
|
if err := c.ensureParent(c.Path(path), c.root); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(c.Path(path), defaultDirPerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := c.copyIfNeeded(c.Path(path), filepath.Dir(c.Path(path))); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if resources.CPU != nil {
|
||||||
|
for _, t := range []struct {
|
||||||
|
name string
|
||||||
|
value *string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "cpus",
|
||||||
|
value: &resources.CPU.Cpus,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mems",
|
||||||
|
value: &resources.CPU.Mems,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
if t.value != nil {
|
||||||
|
if err := ioutil.WriteFile(
|
||||||
|
filepath.Join(c.Path(path), fmt.Sprintf("cpuset.%s", t.name)),
|
||||||
|
[]byte(*t.value),
|
||||||
|
defaultFilePerm,
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cpusetController) getValues(path string) (cpus []byte, mems []byte, err error) {
|
||||||
|
if cpus, err = ioutil.ReadFile(filepath.Join(path, "cpuset.cpus")); err != nil && !os.IsNotExist(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if mems, err = ioutil.ReadFile(filepath.Join(path, "cpuset.mems")); err != nil && !os.IsNotExist(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return cpus, mems, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensureParent makes sure that the parent directory of current is created
|
||||||
|
// and populated with the proper cpus and mems files copied from
|
||||||
|
// it's parent.
|
||||||
|
func (c *cpusetController) ensureParent(current, root string) error {
|
||||||
|
parent := filepath.Dir(current)
|
||||||
|
if _, err := filepath.Rel(root, parent); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Avoid infinite recursion.
|
||||||
|
if parent == current {
|
||||||
|
return fmt.Errorf("cpuset: cgroup parent path outside cgroup root")
|
||||||
|
}
|
||||||
|
if cleanPath(parent) != root {
|
||||||
|
if err := c.ensureParent(parent, root); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(current, defaultDirPerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.copyIfNeeded(current, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyIfNeeded copies the cpuset.cpus and cpuset.mems from the parent
|
||||||
|
// directory to the current directory if the file's contents are 0
|
||||||
|
func (c *cpusetController) copyIfNeeded(current, parent string) error {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
currentCpus, currentMems []byte
|
||||||
|
parentCpus, parentMems []byte
|
||||||
|
)
|
||||||
|
if currentCpus, currentMems, err = c.getValues(current); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if parentCpus, parentMems, err = c.getValues(parent); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if isEmpty(currentCpus) {
|
||||||
|
if err := ioutil.WriteFile(
|
||||||
|
filepath.Join(current, "cpuset.cpus"),
|
||||||
|
parentCpus,
|
||||||
|
defaultFilePerm,
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isEmpty(currentMems) {
|
||||||
|
if err := ioutil.WriteFile(
|
||||||
|
filepath.Join(current, "cpuset.mems"),
|
||||||
|
parentMems,
|
||||||
|
defaultFilePerm,
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isEmpty(b []byte) bool {
|
||||||
|
return len(bytes.Trim(b, "\n")) == 0
|
||||||
|
}
|
74
vendor/github.com/containerd/cgroups/devices.go
generated
vendored
Normal file
74
vendor/github.com/containerd/cgroups/devices.go
generated
vendored
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package cgroups
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
allowDeviceFile = "devices.allow"
|
||||||
|
denyDeviceFile = "devices.deny"
|
||||||
|
wildcard = -1
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewDevices(root string) *devicesController {
|
||||||
|
return &devicesController{
|
||||||
|
root: filepath.Join(root, string(Devices)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type devicesController struct {
|
||||||
|
root string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *devicesController) Name() Name {
|
||||||
|
return Devices
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *devicesController) Path(path string) string {
|
||||||
|
return filepath.Join(d.root, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *devicesController) Create(path string, resources *specs.LinuxResources) error {
|
||||||
|
if err := os.MkdirAll(d.Path(path), defaultDirPerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, device := range resources.Devices {
|
||||||
|
file := denyDeviceFile
|
||||||
|
if device.Allow {
|
||||||
|
file = allowDeviceFile
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile(
|
||||||
|
filepath.Join(d.Path(path), file),
|
||||||
|
[]byte(deviceString(device)),
|
||||||
|
defaultFilePerm,
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *devicesController) Update(path string, resources *specs.LinuxResources) error {
|
||||||
|
return d.Create(path, resources)
|
||||||
|
}
|
||||||
|
|
||||||
|
func deviceString(device specs.LinuxDeviceCgroup) string {
|
||||||
|
return fmt.Sprintf("%c %s:%s %s",
|
||||||
|
&device.Type,
|
||||||
|
deviceNumber(device.Major),
|
||||||
|
deviceNumber(device.Minor),
|
||||||
|
&device.Access,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func deviceNumber(number *int64) string {
|
||||||
|
if number == nil || *number == wildcard {
|
||||||
|
return "*"
|
||||||
|
}
|
||||||
|
return fmt.Sprint(*number)
|
||||||
|
}
|
31
vendor/github.com/containerd/cgroups/errors.go
generated
vendored
Normal file
31
vendor/github.com/containerd/cgroups/errors.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package cgroups
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidPid = errors.New("cgroups: pid must be greater than 0")
|
||||||
|
ErrMountPointNotExist = errors.New("cgroups: cgroup mountpoint does not exist")
|
||||||
|
ErrInvalidFormat = errors.New("cgroups: parsing file with invalid format failed")
|
||||||
|
ErrFreezerNotSupported = errors.New("cgroups: freezer cgroup not supported on this system")
|
||||||
|
ErrMemoryNotSupported = errors.New("cgroups: memory cgroup not supported on this system")
|
||||||
|
ErrCgroupDeleted = errors.New("cgroups: cgroup deleted")
|
||||||
|
ErrNoCgroupMountDestination = errors.New("cgroups: cannot found cgroup mount destination")
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrorHandler is a function that handles and acts on errors
|
||||||
|
type ErrorHandler func(err error) error
|
||||||
|
|
||||||
|
// IgnoreNotExist ignores any errors that are for not existing files
|
||||||
|
func IgnoreNotExist(err error) error {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func errPassthrough(err error) error {
|
||||||
|
return err
|
||||||
|
}
|
69
vendor/github.com/containerd/cgroups/freezer.go
generated
vendored
Normal file
69
vendor/github.com/containerd/cgroups/freezer.go
generated
vendored
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package cgroups
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewFreezer(root string) *freezerController {
|
||||||
|
return &freezerController{
|
||||||
|
root: filepath.Join(root, string(Freezer)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type freezerController struct {
|
||||||
|
root string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *freezerController) Name() Name {
|
||||||
|
return Freezer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *freezerController) Path(path string) string {
|
||||||
|
return filepath.Join(f.root, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *freezerController) Freeze(path string) error {
|
||||||
|
if err := f.changeState(path, Frozen); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return f.waitState(path, Frozen)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *freezerController) Thaw(path string) error {
|
||||||
|
if err := f.changeState(path, Thawed); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return f.waitState(path, Thawed)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *freezerController) changeState(path string, state State) error {
|
||||||
|
return ioutil.WriteFile(
|
||||||
|
filepath.Join(f.root, path, "freezer.state"),
|
||||||
|
[]byte(strings.ToUpper(string(state))),
|
||||||
|
defaultFilePerm,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *freezerController) state(path string) (State, error) {
|
||||||
|
current, err := ioutil.ReadFile(filepath.Join(f.root, path, "freezer.state"))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return State(strings.ToLower(strings.TrimSpace(string(current)))), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *freezerController) waitState(path string, state State) error {
|
||||||
|
for {
|
||||||
|
current, err := f.state(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if current == state {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
4
vendor/github.com/containerd/cgroups/hierarchy.go
generated
vendored
Normal file
4
vendor/github.com/containerd/cgroups/hierarchy.go
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
package cgroups
|
||||||
|
|
||||||
|
// Hierarchy enableds both unified and split hierarchy for cgroups
|
||||||
|
type Hierarchy func() ([]Subsystem, error)
|
92
vendor/github.com/containerd/cgroups/hugetlb.go
generated
vendored
Normal file
92
vendor/github.com/containerd/cgroups/hugetlb.go
generated
vendored
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
package cgroups
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewHugetlb(root string) (*hugetlbController, error) {
|
||||||
|
sizes, err := hugePageSizes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &hugetlbController{
|
||||||
|
root: filepath.Join(root, string(Hugetlb)),
|
||||||
|
sizes: sizes,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type hugetlbController struct {
|
||||||
|
root string
|
||||||
|
sizes []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *hugetlbController) Name() Name {
|
||||||
|
return Hugetlb
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *hugetlbController) Path(path string) string {
|
||||||
|
return filepath.Join(h.root, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *hugetlbController) Create(path string, resources *specs.LinuxResources) error {
|
||||||
|
if err := os.MkdirAll(h.Path(path), defaultDirPerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, limit := range resources.HugepageLimits {
|
||||||
|
if err := ioutil.WriteFile(
|
||||||
|
filepath.Join(h.Path(path), strings.Join([]string{"hugetlb", limit.Pagesize, "limit_in_bytes"}, ".")),
|
||||||
|
[]byte(strconv.FormatUint(limit.Limit, 10)),
|
||||||
|
defaultFilePerm,
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *hugetlbController) Stat(path string, stats *Stats) error {
|
||||||
|
stats.Hugetlb = make(map[string]HugetlbStat)
|
||||||
|
for _, size := range h.sizes {
|
||||||
|
s, err := h.readSizeStat(path, size)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
stats.Hugetlb[size] = s
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *hugetlbController) readSizeStat(path, size string) (HugetlbStat, error) {
|
||||||
|
var s HugetlbStat
|
||||||
|
for _, t := range []struct {
|
||||||
|
name string
|
||||||
|
value *uint64
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "usage_in_bytes",
|
||||||
|
value: &s.Usage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "max_usage_in_bytes",
|
||||||
|
value: &s.Max,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "failcnt",
|
||||||
|
value: &s.Failcnt,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
v, err := readUint(filepath.Join(h.Path(path), strings.Join([]string{"hugetlb", size, t.name}, ".")))
|
||||||
|
if err != nil {
|
||||||
|
return s, err
|
||||||
|
}
|
||||||
|
*t.value = v
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
304
vendor/github.com/containerd/cgroups/memory.go
generated
vendored
Normal file
304
vendor/github.com/containerd/cgroups/memory.go
generated
vendored
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
package cgroups
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewMemory(root string) *memoryController {
|
||||||
|
return &memoryController{
|
||||||
|
root: filepath.Join(root, string(Memory)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type memoryController struct {
|
||||||
|
root string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryController) Name() Name {
|
||||||
|
return Memory
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryController) Path(path string) string {
|
||||||
|
return filepath.Join(m.root, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryController) Create(path string, resources *specs.LinuxResources) error {
|
||||||
|
if err := os.MkdirAll(m.Path(path), defaultDirPerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if resources.Memory == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if resources.Memory.Kernel != nil {
|
||||||
|
// Check if kernel memory is enabled
|
||||||
|
// We have to limit the kernel memory here as it won't be accounted at all
|
||||||
|
// until a limit is set on the cgroup and limit cannot be set once the
|
||||||
|
// cgroup has children, or if there are already tasks in the cgroup.
|
||||||
|
for _, i := range []int64{1, -1} {
|
||||||
|
if err := ioutil.WriteFile(
|
||||||
|
filepath.Join(m.Path(path), "memory.kmem.limit_in_bytes"),
|
||||||
|
[]byte(strconv.FormatInt(i, 10)),
|
||||||
|
defaultFilePerm,
|
||||||
|
); err != nil {
|
||||||
|
return checkEBUSY(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m.set(path, getMemorySettings(resources))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryController) Update(path string, resources *specs.LinuxResources) error {
|
||||||
|
if resources.Memory == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
g := func(v *int64) bool {
|
||||||
|
return v != nil && *v > 0
|
||||||
|
}
|
||||||
|
settings := getMemorySettings(resources)
|
||||||
|
if g(resources.Memory.Limit) && g(resources.Memory.Swap) {
|
||||||
|
// if the updated swap value is larger than the current memory limit set the swap changes first
|
||||||
|
// then set the memory limit as swap must always be larger than the current limit
|
||||||
|
current, err := readUint(filepath.Join(m.Path(path), "memory.limit_in_bytes"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if current < uint64(*resources.Memory.Swap) {
|
||||||
|
settings[0], settings[1] = settings[1], settings[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m.set(path, settings)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryController) Stat(path string, stats *Stats) error {
|
||||||
|
f, err := os.Open(filepath.Join(m.Path(path), "memory.stat"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
stats.Memory = &MemoryStat{}
|
||||||
|
if err := m.parseStats(f, stats.Memory); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, t := range []struct {
|
||||||
|
module string
|
||||||
|
entry *MemoryEntry
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
module: "",
|
||||||
|
entry: &stats.Memory.Usage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
module: "memsw",
|
||||||
|
entry: &stats.Memory.Swap,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
module: "kmem",
|
||||||
|
entry: &stats.Memory.Kernel,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
module: "kmem.tcp",
|
||||||
|
entry: &stats.Memory.KernelTCP,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
for _, tt := range []struct {
|
||||||
|
name string
|
||||||
|
value *uint64
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "usage_in_bytes",
|
||||||
|
value: &t.entry.Usage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "max_usage_in_bytes",
|
||||||
|
value: &t.entry.Max,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "failcnt",
|
||||||
|
value: &t.entry.Failcnt,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "limit_in_bytes",
|
||||||
|
value: &t.entry.Limit,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
parts := []string{"memory"}
|
||||||
|
if t.module != "" {
|
||||||
|
parts = append(parts, t.module)
|
||||||
|
}
|
||||||
|
parts = append(parts, tt.name)
|
||||||
|
v, err := readUint(filepath.Join(m.Path(path), strings.Join(parts, ".")))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*tt.value = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryController) OOMEventFD(path string) (uintptr, error) {
|
||||||
|
root := m.Path(path)
|
||||||
|
f, err := os.Open(filepath.Join(root, "memory.oom_control"))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
fd, _, serr := unix.RawSyscall(unix.SYS_EVENTFD2, 0, unix.FD_CLOEXEC, 0)
|
||||||
|
if serr != 0 {
|
||||||
|
return 0, serr
|
||||||
|
}
|
||||||
|
if err := writeEventFD(root, f.Fd(), fd); err != nil {
|
||||||
|
unix.Close(int(fd))
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return fd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeEventFD(root string, cfd, efd uintptr) error {
|
||||||
|
f, err := os.OpenFile(filepath.Join(root, "cgroup.event_control"), os.O_WRONLY, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = f.WriteString(fmt.Sprintf("%d %d", efd, cfd))
|
||||||
|
f.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryController) parseStats(r io.Reader, stat *MemoryStat) error {
|
||||||
|
var (
|
||||||
|
raw = make(map[string]uint64)
|
||||||
|
sc = bufio.NewScanner(r)
|
||||||
|
line int
|
||||||
|
)
|
||||||
|
for sc.Scan() {
|
||||||
|
if err := sc.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
key, v, err := parseKV(sc.Text())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%d: %v", line, err)
|
||||||
|
}
|
||||||
|
raw[key] = v
|
||||||
|
line++
|
||||||
|
}
|
||||||
|
stat.Cache = raw["cache"]
|
||||||
|
stat.RSS = raw["rss"]
|
||||||
|
stat.RSSHuge = raw["rss_huge"]
|
||||||
|
stat.MappedFile = raw["mapped_file"]
|
||||||
|
stat.Dirty = raw["dirty"]
|
||||||
|
stat.Writeback = raw["writeback"]
|
||||||
|
stat.PgPgIn = raw["pgpgin"]
|
||||||
|
stat.PgPgOut = raw["pgpgout"]
|
||||||
|
stat.PgFault = raw["pgfault"]
|
||||||
|
stat.PgMajFault = raw["pgmajfault"]
|
||||||
|
stat.InactiveAnon = raw["inactive_anon"]
|
||||||
|
stat.ActiveAnon = raw["active_anon"]
|
||||||
|
stat.InactiveFile = raw["inactive_file"]
|
||||||
|
stat.ActiveFile = raw["active_file"]
|
||||||
|
stat.Unevictable = raw["unevictable"]
|
||||||
|
stat.HierarchicalMemoryLimit = raw["hierarchical_memory_limit"]
|
||||||
|
stat.HierarchicalSwapLimit = raw["hierarchical_memsw_limit"]
|
||||||
|
stat.TotalCache = raw["total_cache"]
|
||||||
|
stat.TotalRSS = raw["total_rss"]
|
||||||
|
stat.TotalRSSHuge = raw["total_rss_huge"]
|
||||||
|
stat.TotalMappedFile = raw["total_mapped_file"]
|
||||||
|
stat.TotalDirty = raw["total_dirty"]
|
||||||
|
stat.TotalWriteback = raw["total_writeback"]
|
||||||
|
stat.TotalPgPgIn = raw["total_pgpgin"]
|
||||||
|
stat.TotalPgPgOut = raw["total_pgpgout"]
|
||||||
|
stat.TotalPgFault = raw["total_pgfault"]
|
||||||
|
stat.TotalPgMajFault = raw["total_pgmajfault"]
|
||||||
|
stat.TotalInactiveAnon = raw["total_inactive_anon"]
|
||||||
|
stat.TotalActiveAnon = raw["total_active_anon"]
|
||||||
|
stat.TotalInactiveFile = raw["total_inactive_file"]
|
||||||
|
stat.TotalActiveFile = raw["total_active_file"]
|
||||||
|
stat.TotalUnevictable = raw["total_unevictable"]
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryController) set(path string, settings []memorySettings) error {
|
||||||
|
for _, t := range settings {
|
||||||
|
if t.value != nil {
|
||||||
|
if err := ioutil.WriteFile(
|
||||||
|
filepath.Join(m.Path(path), fmt.Sprintf("memory.%s", t.name)),
|
||||||
|
[]byte(strconv.FormatInt(*t.value, 10)),
|
||||||
|
defaultFilePerm,
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type memorySettings struct {
|
||||||
|
name string
|
||||||
|
value *int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMemorySettings(resources *specs.LinuxResources) []memorySettings {
|
||||||
|
mem := resources.Memory
|
||||||
|
var swappiness *int64
|
||||||
|
if mem.Swappiness != nil {
|
||||||
|
v := int64(*mem.Swappiness)
|
||||||
|
swappiness = &v
|
||||||
|
}
|
||||||
|
return []memorySettings{
|
||||||
|
{
|
||||||
|
name: "limit_in_bytes",
|
||||||
|
value: mem.Limit,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "memsw.limit_in_bytes",
|
||||||
|
value: mem.Swap,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "kmem.limit_in_bytes",
|
||||||
|
value: mem.Kernel,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "kmem.tcp.limit_in_bytes",
|
||||||
|
value: mem.KernelTCP,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "oom_control",
|
||||||
|
value: getOomControlValue(mem),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "swappiness",
|
||||||
|
value: swappiness,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkEBUSY(err error) error {
|
||||||
|
if pathErr, ok := err.(*os.PathError); ok {
|
||||||
|
if errNo, ok := pathErr.Err.(syscall.Errno); ok {
|
||||||
|
if errNo == unix.EBUSY {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"failed to set memory.kmem.limit_in_bytes, because either tasks have already joined this cgroup or it has children")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func getOomControlValue(mem *specs.LinuxMemory) *int64 {
|
||||||
|
if mem.DisableOOMKiller != nil && *mem.DisableOOMKiller {
|
||||||
|
i := int64(1)
|
||||||
|
return &i
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
23
vendor/github.com/containerd/cgroups/named.go
generated
vendored
Normal file
23
vendor/github.com/containerd/cgroups/named.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package cgroups
|
||||||
|
|
||||||
|
import "path/filepath"
|
||||||
|
|
||||||
|
func NewNamed(root string, name Name) *namedController {
|
||||||
|
return &namedController{
|
||||||
|
root: root,
|
||||||
|
name: name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type namedController struct {
|
||||||
|
root string
|
||||||
|
name Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *namedController) Name() Name {
|
||||||
|
return n.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *namedController) Path(path string) string {
|
||||||
|
return filepath.Join(n.root, string(n.name), path)
|
||||||
|
}
|
42
vendor/github.com/containerd/cgroups/net_cls.go
generated
vendored
Normal file
42
vendor/github.com/containerd/cgroups/net_cls.go
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package cgroups
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewNetCls(root string) *netclsController {
|
||||||
|
return &netclsController{
|
||||||
|
root: filepath.Join(root, string(NetCLS)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type netclsController struct {
|
||||||
|
root string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *netclsController) Name() Name {
|
||||||
|
return NetCLS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *netclsController) Path(path string) string {
|
||||||
|
return filepath.Join(n.root, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *netclsController) Create(path string, resources *specs.LinuxResources) error {
|
||||||
|
if err := os.MkdirAll(n.Path(path), defaultDirPerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if resources.Network != nil && resources.Network.ClassID != nil && *resources.Network.ClassID > 0 {
|
||||||
|
return ioutil.WriteFile(
|
||||||
|
filepath.Join(n.Path(path), "net_cls.classid"),
|
||||||
|
[]byte(strconv.FormatUint(uint64(*resources.Network.ClassID), 10)),
|
||||||
|
defaultFilePerm,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
50
vendor/github.com/containerd/cgroups/net_prio.go
generated
vendored
Normal file
50
vendor/github.com/containerd/cgroups/net_prio.go
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package cgroups
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewNetPrio(root string) *netprioController {
|
||||||
|
return &netprioController{
|
||||||
|
root: filepath.Join(root, string(NetPrio)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type netprioController struct {
|
||||||
|
root string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *netprioController) Name() Name {
|
||||||
|
return NetPrio
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *netprioController) Path(path string) string {
|
||||||
|
return filepath.Join(n.root, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *netprioController) Create(path string, resources *specs.LinuxResources) error {
|
||||||
|
if err := os.MkdirAll(n.Path(path), defaultDirPerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if resources.Network != nil {
|
||||||
|
for _, prio := range resources.Network.Priorities {
|
||||||
|
if err := ioutil.WriteFile(
|
||||||
|
filepath.Join(n.Path(path), "net_prio_ifpriomap"),
|
||||||
|
formatPrio(prio.Name, prio.Priority),
|
||||||
|
defaultFilePerm,
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatPrio(name string, prio uint32) []byte {
|
||||||
|
return []byte(fmt.Sprintf("%s %d", name, prio))
|
||||||
|
}
|
88
vendor/github.com/containerd/cgroups/paths.go
generated
vendored
Normal file
88
vendor/github.com/containerd/cgroups/paths.go
generated
vendored
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
package cgroups
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Path func(subsystem Name) (string, error)
|
||||||
|
|
||||||
|
func RootPath(subsysem Name) (string, error) {
|
||||||
|
return "/", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StaticPath returns a static path to use for all cgroups
|
||||||
|
func StaticPath(path string) Path {
|
||||||
|
return func(_ Name) (string, error) {
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NestedPath will nest the cgroups based on the calling processes cgroup
|
||||||
|
// placing its child processes inside its own path
|
||||||
|
func NestedPath(suffix string) Path {
|
||||||
|
paths, err := parseCgroupFile("/proc/self/cgroup")
|
||||||
|
if err != nil {
|
||||||
|
return errorPath(err)
|
||||||
|
}
|
||||||
|
return existingPath(paths, suffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PidPath will return the correct cgroup paths for an existing process running inside a cgroup
|
||||||
|
// This is commonly used for the Load function to restore an existing container
|
||||||
|
func PidPath(pid int) Path {
|
||||||
|
p := fmt.Sprintf("/proc/%d/cgroup", pid)
|
||||||
|
paths, err := parseCgroupFile(p)
|
||||||
|
if err != nil {
|
||||||
|
return errorPath(errors.Wrapf(err, "parse cgroup file %s", p))
|
||||||
|
}
|
||||||
|
return existingPath(paths, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func existingPath(paths map[string]string, suffix string) Path {
|
||||||
|
// localize the paths based on the root mount dest for nested cgroups
|
||||||
|
for n, p := range paths {
|
||||||
|
dest, err := getCgroupDestination(string(n))
|
||||||
|
if err != nil {
|
||||||
|
return errorPath(err)
|
||||||
|
}
|
||||||
|
rel, err := filepath.Rel(dest, p)
|
||||||
|
if err != nil {
|
||||||
|
return errorPath(err)
|
||||||
|
}
|
||||||
|
if rel == "." {
|
||||||
|
rel = dest
|
||||||
|
}
|
||||||
|
paths[n] = filepath.Join("/", rel)
|
||||||
|
}
|
||||||
|
return func(name Name) (string, error) {
|
||||||
|
root, ok := paths[string(name)]
|
||||||
|
if !ok {
|
||||||
|
if root, ok = paths[fmt.Sprintf("name=%s", name)]; !ok {
|
||||||
|
return "", fmt.Errorf("unable to find %q in controller set", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if suffix != "" {
|
||||||
|
return filepath.Join(root, suffix), nil
|
||||||
|
}
|
||||||
|
return root, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func subPath(path Path, subName string) Path {
|
||||||
|
return func(name Name) (string, error) {
|
||||||
|
p, err := path(name)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return filepath.Join(p, subName), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func errorPath(err error) Path {
|
||||||
|
return func(_ Name) (string, error) {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
21
vendor/github.com/containerd/cgroups/perf_event.go
generated
vendored
Normal file
21
vendor/github.com/containerd/cgroups/perf_event.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package cgroups
|
||||||
|
|
||||||
|
import "path/filepath"
|
||||||
|
|
||||||
|
func NewPerfEvent(root string) *PerfEventController {
|
||||||
|
return &PerfEventController{
|
||||||
|
root: filepath.Join(root, string(PerfEvent)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type PerfEventController struct {
|
||||||
|
root string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PerfEventController) Name() Name {
|
||||||
|
return PerfEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PerfEventController) Path(path string) string {
|
||||||
|
return filepath.Join(p.root, path)
|
||||||
|
}
|
69
vendor/github.com/containerd/cgroups/pids.go
generated
vendored
Normal file
69
vendor/github.com/containerd/cgroups/pids.go
generated
vendored
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package cgroups
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewPids(root string) *pidsController {
|
||||||
|
return &pidsController{
|
||||||
|
root: filepath.Join(root, string(Pids)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type pidsController struct {
|
||||||
|
root string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *pidsController) Name() Name {
|
||||||
|
return Pids
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *pidsController) Path(path string) string {
|
||||||
|
return filepath.Join(p.root, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *pidsController) Create(path string, resources *specs.LinuxResources) error {
|
||||||
|
if err := os.MkdirAll(p.Path(path), defaultDirPerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if resources.Pids != nil && resources.Pids.Limit > 0 {
|
||||||
|
return ioutil.WriteFile(
|
||||||
|
filepath.Join(p.Path(path), "pids.max"),
|
||||||
|
[]byte(strconv.FormatInt(resources.Pids.Limit, 10)),
|
||||||
|
defaultFilePerm,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *pidsController) Update(path string, resources *specs.LinuxResources) error {
|
||||||
|
return p.Create(path, resources)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *pidsController) Stat(path string, stats *Stats) error {
|
||||||
|
current, err := readUint(filepath.Join(p.Path(path), "pids.current"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var max uint64
|
||||||
|
maxData, err := ioutil.ReadFile(filepath.Join(p.Path(path), "pids.max"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if maxS := strings.TrimSpace(string(maxData)); maxS != "max" {
|
||||||
|
if max, err = parseUint(maxS, 10, 64); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stats.Pids = &PidsStat{
|
||||||
|
Current: current,
|
||||||
|
Limit: max,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
12
vendor/github.com/containerd/cgroups/state.go
generated
vendored
Normal file
12
vendor/github.com/containerd/cgroups/state.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package cgroups
|
||||||
|
|
||||||
|
// State is a type that represents the state of the current cgroup
|
||||||
|
type State string
|
||||||
|
|
||||||
|
const (
|
||||||
|
Unknown State = ""
|
||||||
|
Thawed State = "thawed"
|
||||||
|
Frozen State = "frozen"
|
||||||
|
Freezing State = "freezing"
|
||||||
|
Deleted State = "deleted"
|
||||||
|
)
|
109
vendor/github.com/containerd/cgroups/stats.go
generated
vendored
Normal file
109
vendor/github.com/containerd/cgroups/stats.go
generated
vendored
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
package cgroups
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
|
type Stats struct {
|
||||||
|
cpuMu sync.Mutex
|
||||||
|
|
||||||
|
Hugetlb map[string]HugetlbStat
|
||||||
|
Pids *PidsStat
|
||||||
|
Cpu *CpuStat
|
||||||
|
Memory *MemoryStat
|
||||||
|
Blkio *BlkioStat
|
||||||
|
}
|
||||||
|
|
||||||
|
type HugetlbStat struct {
|
||||||
|
Usage uint64
|
||||||
|
Max uint64
|
||||||
|
Failcnt uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type PidsStat struct {
|
||||||
|
Current uint64
|
||||||
|
Limit uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type CpuStat struct {
|
||||||
|
Usage CpuUsage
|
||||||
|
Throttling Throttle
|
||||||
|
}
|
||||||
|
|
||||||
|
type CpuUsage struct {
|
||||||
|
// Units: nanoseconds.
|
||||||
|
Total uint64
|
||||||
|
PerCpu []uint64
|
||||||
|
Kernel uint64
|
||||||
|
User uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type Throttle struct {
|
||||||
|
Periods uint64
|
||||||
|
ThrottledPeriods uint64
|
||||||
|
ThrottledTime uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type MemoryStat struct {
|
||||||
|
Cache uint64
|
||||||
|
RSS uint64
|
||||||
|
RSSHuge uint64
|
||||||
|
MappedFile uint64
|
||||||
|
Dirty uint64
|
||||||
|
Writeback uint64
|
||||||
|
PgPgIn uint64
|
||||||
|
PgPgOut uint64
|
||||||
|
PgFault uint64
|
||||||
|
PgMajFault uint64
|
||||||
|
InactiveAnon uint64
|
||||||
|
ActiveAnon uint64
|
||||||
|
InactiveFile uint64
|
||||||
|
ActiveFile uint64
|
||||||
|
Unevictable uint64
|
||||||
|
HierarchicalMemoryLimit uint64
|
||||||
|
HierarchicalSwapLimit uint64
|
||||||
|
TotalCache uint64
|
||||||
|
TotalRSS uint64
|
||||||
|
TotalRSSHuge uint64
|
||||||
|
TotalMappedFile uint64
|
||||||
|
TotalDirty uint64
|
||||||
|
TotalWriteback uint64
|
||||||
|
TotalPgPgIn uint64
|
||||||
|
TotalPgPgOut uint64
|
||||||
|
TotalPgFault uint64
|
||||||
|
TotalPgMajFault uint64
|
||||||
|
TotalInactiveAnon uint64
|
||||||
|
TotalActiveAnon uint64
|
||||||
|
TotalInactiveFile uint64
|
||||||
|
TotalActiveFile uint64
|
||||||
|
TotalUnevictable uint64
|
||||||
|
|
||||||
|
Usage MemoryEntry
|
||||||
|
Swap MemoryEntry
|
||||||
|
Kernel MemoryEntry
|
||||||
|
KernelTCP MemoryEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
type MemoryEntry struct {
|
||||||
|
Limit uint64
|
||||||
|
Usage uint64
|
||||||
|
Max uint64
|
||||||
|
Failcnt uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlkioStat struct {
|
||||||
|
IoServiceBytesRecursive []BlkioEntry
|
||||||
|
IoServicedRecursive []BlkioEntry
|
||||||
|
IoQueuedRecursive []BlkioEntry
|
||||||
|
IoServiceTimeRecursive []BlkioEntry
|
||||||
|
IoWaitTimeRecursive []BlkioEntry
|
||||||
|
IoMergedRecursive []BlkioEntry
|
||||||
|
IoTimeRecursive []BlkioEntry
|
||||||
|
SectorsRecursive []BlkioEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlkioEntry struct {
|
||||||
|
Op string
|
||||||
|
Device string
|
||||||
|
Major uint64
|
||||||
|
Minor uint64
|
||||||
|
Value uint64
|
||||||
|
}
|
94
vendor/github.com/containerd/cgroups/subsystem.go
generated
vendored
Normal file
94
vendor/github.com/containerd/cgroups/subsystem.go
generated
vendored
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
package cgroups
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Name is a typed name for a cgroup subsystem
|
||||||
|
type Name string
|
||||||
|
|
||||||
|
const (
|
||||||
|
Devices Name = "devices"
|
||||||
|
Hugetlb Name = "hugetlb"
|
||||||
|
Freezer Name = "freezer"
|
||||||
|
Pids Name = "pids"
|
||||||
|
NetCLS Name = "net_cls"
|
||||||
|
NetPrio Name = "net_prio"
|
||||||
|
PerfEvent Name = "perf_event"
|
||||||
|
Cpuset Name = "cpuset"
|
||||||
|
Cpu Name = "cpu"
|
||||||
|
Cpuacct Name = "cpuacct"
|
||||||
|
Memory Name = "memory"
|
||||||
|
Blkio Name = "blkio"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Subsystems returns a complete list of the default cgroups
|
||||||
|
// avaliable on most linux systems
|
||||||
|
func Subsystems() []Name {
|
||||||
|
n := []Name{
|
||||||
|
Hugetlb,
|
||||||
|
Freezer,
|
||||||
|
Pids,
|
||||||
|
NetCLS,
|
||||||
|
NetPrio,
|
||||||
|
PerfEvent,
|
||||||
|
Cpuset,
|
||||||
|
Cpu,
|
||||||
|
Cpuacct,
|
||||||
|
Memory,
|
||||||
|
Blkio,
|
||||||
|
}
|
||||||
|
if !isUserNS {
|
||||||
|
n = append(n, Devices)
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
type Subsystem interface {
|
||||||
|
Name() Name
|
||||||
|
}
|
||||||
|
|
||||||
|
type pather interface {
|
||||||
|
Subsystem
|
||||||
|
Path(path string) string
|
||||||
|
}
|
||||||
|
|
||||||
|
type creator interface {
|
||||||
|
Subsystem
|
||||||
|
Create(path string, resources *specs.LinuxResources) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type deleter interface {
|
||||||
|
Subsystem
|
||||||
|
Delete(path string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type stater interface {
|
||||||
|
Subsystem
|
||||||
|
Stat(path string, stats *Stats) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type updater interface {
|
||||||
|
Subsystem
|
||||||
|
Update(path string, resources *specs.LinuxResources) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// SingleSubsystem returns a single cgroup subsystem within the base Hierarchy
|
||||||
|
func SingleSubsystem(baseHierarchy Hierarchy, subsystem Name) Hierarchy {
|
||||||
|
return func() ([]Subsystem, error) {
|
||||||
|
subsystems, err := baseHierarchy()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, s := range subsystems {
|
||||||
|
if s.Name() == subsystem {
|
||||||
|
return []Subsystem{
|
||||||
|
s,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("unable to find subsystem %s", subsystem)
|
||||||
|
}
|
||||||
|
}
|
101
vendor/github.com/containerd/cgroups/systemd.go
generated
vendored
Normal file
101
vendor/github.com/containerd/cgroups/systemd.go
generated
vendored
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
package cgroups
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
systemdDbus "github.com/coreos/go-systemd/dbus"
|
||||||
|
"github.com/godbus/dbus"
|
||||||
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SystemdDbus Name = "systemd"
|
||||||
|
defaultSlice = "system.slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Systemd() ([]Subsystem, error) {
|
||||||
|
root, err := v1MountPoint()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defaultSubsystems, err := defaults(root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s, err := NewSystemd(root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// make sure the systemd controller is added first
|
||||||
|
return append([]Subsystem{s}, defaultSubsystems...), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Slice(slice, name string) Path {
|
||||||
|
if slice == "" {
|
||||||
|
slice = defaultSlice
|
||||||
|
}
|
||||||
|
return func(subsystem Name) (string, error) {
|
||||||
|
return filepath.Join(slice, unitName(name)), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSystemd(root string) (*SystemdController, error) {
|
||||||
|
conn, err := systemdDbus.New()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &SystemdController{
|
||||||
|
root: root,
|
||||||
|
conn: conn,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type SystemdController struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
conn *systemdDbus.Conn
|
||||||
|
root string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SystemdController) Name() Name {
|
||||||
|
return SystemdDbus
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SystemdController) Create(path string, resources *specs.LinuxResources) error {
|
||||||
|
slice, name := splitName(path)
|
||||||
|
properties := []systemdDbus.Property{
|
||||||
|
systemdDbus.PropDescription(fmt.Sprintf("cgroup %s", name)),
|
||||||
|
systemdDbus.PropWants(slice),
|
||||||
|
newProperty("DefaultDependencies", false),
|
||||||
|
newProperty("Delegate", true),
|
||||||
|
newProperty("MemoryAccounting", true),
|
||||||
|
newProperty("CPUAccounting", true),
|
||||||
|
newProperty("BlockIOAccounting", true),
|
||||||
|
}
|
||||||
|
_, err := s.conn.StartTransientUnit(name, "replace", properties, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SystemdController) Delete(path string) error {
|
||||||
|
_, name := splitName(path)
|
||||||
|
_, err := s.conn.StopUnit(name, "replace", nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func newProperty(name string, units interface{}) systemdDbus.Property {
|
||||||
|
return systemdDbus.Property{
|
||||||
|
Name: name,
|
||||||
|
Value: dbus.MakeVariant(units),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unitName(name string) string {
|
||||||
|
return fmt.Sprintf("%s.slice", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitName(path string) (slice string, unit string) {
|
||||||
|
slice, unit = filepath.Split(path)
|
||||||
|
return strings.TrimSuffix(slice, "/"), unit
|
||||||
|
}
|
10
vendor/github.com/containerd/cgroups/ticks.go
generated
vendored
Normal file
10
vendor/github.com/containerd/cgroups/ticks.go
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package cgroups
|
||||||
|
|
||||||
|
func getClockTicks() uint64 {
|
||||||
|
// The value comes from `C.sysconf(C._SC_CLK_TCK)`, and
|
||||||
|
// on Linux it's a constant which is safe to be hard coded,
|
||||||
|
// so we can avoid using cgo here.
|
||||||
|
// See https://github.com/containerd/cgroups/pull/12 for
|
||||||
|
// more details.
|
||||||
|
return 100
|
||||||
|
}
|
280
vendor/github.com/containerd/cgroups/utils.go
generated
vendored
Normal file
280
vendor/github.com/containerd/cgroups/utils.go
generated
vendored
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
package cgroups
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
units "github.com/docker/go-units"
|
||||||
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
var isUserNS = runningInUserNS()
|
||||||
|
|
||||||
|
// runningInUserNS detects whether we are currently running in a user namespace.
|
||||||
|
// Copied from github.com/lxc/lxd/shared/util.go
|
||||||
|
func runningInUserNS() bool {
|
||||||
|
file, err := os.Open("/proc/self/uid_map")
|
||||||
|
if err != nil {
|
||||||
|
// This kernel-provided file only exists if user namespaces are supported
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
buf := bufio.NewReader(file)
|
||||||
|
l, _, err := buf.ReadLine()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
line := string(l)
|
||||||
|
var a, b, c int64
|
||||||
|
fmt.Sscanf(line, "%d %d %d", &a, &b, &c)
|
||||||
|
/*
|
||||||
|
* We assume we are in the initial user namespace if we have a full
|
||||||
|
* range - 4294967295 uids starting at uid 0.
|
||||||
|
*/
|
||||||
|
if a == 0 && b == 0 && c == 4294967295 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaults returns all known groups
|
||||||
|
func defaults(root string) ([]Subsystem, error) {
|
||||||
|
h, err := NewHugetlb(root)
|
||||||
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s := []Subsystem{
|
||||||
|
NewNamed(root, "systemd"),
|
||||||
|
NewFreezer(root),
|
||||||
|
NewPids(root),
|
||||||
|
NewNetCls(root),
|
||||||
|
NewNetPrio(root),
|
||||||
|
NewPerfEvent(root),
|
||||||
|
NewCputset(root),
|
||||||
|
NewCpu(root),
|
||||||
|
NewCpuacct(root),
|
||||||
|
NewMemory(root),
|
||||||
|
NewBlkio(root),
|
||||||
|
}
|
||||||
|
// only add the devices cgroup if we are not in a user namespace
|
||||||
|
// because modifications are not allowed
|
||||||
|
if !isUserNS {
|
||||||
|
s = append(s, NewDevices(root))
|
||||||
|
}
|
||||||
|
// add the hugetlb cgroup if error wasn't due to missing hugetlb
|
||||||
|
// cgroup support on the host
|
||||||
|
if err == nil {
|
||||||
|
s = append(s, h)
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove will remove a cgroup path handling EAGAIN and EBUSY errors and
|
||||||
|
// retrying the remove after a exp timeout
|
||||||
|
func remove(path string) error {
|
||||||
|
delay := 10 * time.Millisecond
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
if i != 0 {
|
||||||
|
time.Sleep(delay)
|
||||||
|
delay *= 2
|
||||||
|
}
|
||||||
|
if err := os.RemoveAll(path); err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("cgroups: unable to remove path %q", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// readPids will read all the pids in a cgroup by the provided path
|
||||||
|
func readPids(path string, subsystem Name) ([]Process, error) {
|
||||||
|
f, err := os.Open(filepath.Join(path, cgroupProcs))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
var (
|
||||||
|
out []Process
|
||||||
|
s = bufio.NewScanner(f)
|
||||||
|
)
|
||||||
|
for s.Scan() {
|
||||||
|
if t := s.Text(); t != "" {
|
||||||
|
pid, err := strconv.Atoi(t)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
out = append(out, Process{
|
||||||
|
Pid: pid,
|
||||||
|
Subsystem: subsystem,
|
||||||
|
Path: path,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func hugePageSizes() ([]string, error) {
|
||||||
|
var (
|
||||||
|
pageSizes []string
|
||||||
|
sizeList = []string{"B", "kB", "MB", "GB", "TB", "PB"}
|
||||||
|
)
|
||||||
|
files, err := ioutil.ReadDir("/sys/kernel/mm/hugepages")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, st := range files {
|
||||||
|
nameArray := strings.Split(st.Name(), "-")
|
||||||
|
pageSize, err := units.RAMInBytes(nameArray[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pageSizes = append(pageSizes, units.CustomSize("%g%s", float64(pageSize), 1024.0, sizeList))
|
||||||
|
}
|
||||||
|
return pageSizes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readUint(path string) (uint64, error) {
|
||||||
|
v, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return parseUint(strings.TrimSpace(string(v)), 10, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseUint(s string, base, bitSize int) (uint64, error) {
|
||||||
|
v, err := strconv.ParseUint(s, base, bitSize)
|
||||||
|
if err != nil {
|
||||||
|
intValue, intErr := strconv.ParseInt(s, base, bitSize)
|
||||||
|
// 1. Handle negative values greater than MinInt64 (and)
|
||||||
|
// 2. Handle negative values lesser than MinInt64
|
||||||
|
if intErr == nil && intValue < 0 {
|
||||||
|
return 0, nil
|
||||||
|
} else if intErr != nil &&
|
||||||
|
intErr.(*strconv.NumError).Err == strconv.ErrRange &&
|
||||||
|
intValue < 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseKV(raw string) (string, uint64, error) {
|
||||||
|
parts := strings.Fields(raw)
|
||||||
|
switch len(parts) {
|
||||||
|
case 2:
|
||||||
|
v, err := parseUint(parts[1], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, err
|
||||||
|
}
|
||||||
|
return parts[0], v, nil
|
||||||
|
default:
|
||||||
|
return "", 0, ErrInvalidFormat
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseCgroupFile(path string) (map[string]string, error) {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
return parseCgroupFromReader(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseCgroupFromReader(r io.Reader) (map[string]string, error) {
|
||||||
|
var (
|
||||||
|
cgroups = make(map[string]string)
|
||||||
|
s = bufio.NewScanner(r)
|
||||||
|
)
|
||||||
|
for s.Scan() {
|
||||||
|
if err := s.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
text = s.Text()
|
||||||
|
parts = strings.SplitN(text, ":", 3)
|
||||||
|
)
|
||||||
|
if len(parts) < 3 {
|
||||||
|
return nil, fmt.Errorf("invalid cgroup entry: %q", text)
|
||||||
|
}
|
||||||
|
for _, subs := range strings.Split(parts[1], ",") {
|
||||||
|
if subs != "" {
|
||||||
|
cgroups[subs] = parts[2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cgroups, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCgroupDestination(subsystem string) (string, error) {
|
||||||
|
f, err := os.Open("/proc/self/mountinfo")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
s := bufio.NewScanner(f)
|
||||||
|
for s.Scan() {
|
||||||
|
if err := s.Err(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
fields := strings.Fields(s.Text())
|
||||||
|
for _, opt := range strings.Split(fields[len(fields)-1], ",") {
|
||||||
|
if opt == subsystem {
|
||||||
|
return fields[3], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", ErrNoCgroupMountDestination
|
||||||
|
}
|
||||||
|
|
||||||
|
func pathers(subystems []Subsystem) []pather {
|
||||||
|
var out []pather
|
||||||
|
for _, s := range subystems {
|
||||||
|
if p, ok := s.(pather); ok {
|
||||||
|
out = append(out, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func initializeSubsystem(s Subsystem, path Path, resources *specs.LinuxResources) error {
|
||||||
|
if c, ok := s.(creator); ok {
|
||||||
|
p, err := path(s.Name())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := c.Create(p, resources); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if c, ok := s.(pather); ok {
|
||||||
|
p, err := path(s.Name())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// do the default create if the group does not have a custom one
|
||||||
|
if err := os.MkdirAll(c.Path(p), defaultDirPerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleanPath(path string) string {
|
||||||
|
if path == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
path = filepath.Clean(path)
|
||||||
|
if !filepath.IsAbs(path) {
|
||||||
|
path, _ = filepath.Rel(string(os.PathSeparator), filepath.Clean(string(os.PathSeparator)+path))
|
||||||
|
}
|
||||||
|
return filepath.Clean(path)
|
||||||
|
}
|
65
vendor/github.com/containerd/cgroups/v1.go
generated
vendored
Normal file
65
vendor/github.com/containerd/cgroups/v1.go
generated
vendored
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package cgroups
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// V1 returns all the groups in the default cgroups mountpoint in a single hierarchy
|
||||||
|
func V1() ([]Subsystem, error) {
|
||||||
|
root, err := v1MountPoint()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
subsystems, err := defaults(root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var enabled []Subsystem
|
||||||
|
for _, s := range pathers(subsystems) {
|
||||||
|
// check and remove the default groups that do not exist
|
||||||
|
if _, err := os.Lstat(s.Path("/")); err == nil {
|
||||||
|
enabled = append(enabled, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return enabled, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// v1MountPoint returns the mount point where the cgroup
|
||||||
|
// mountpoints are mounted in a single hiearchy
|
||||||
|
func v1MountPoint() (string, error) {
|
||||||
|
f, err := os.Open("/proc/self/mountinfo")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
scanner := bufio.NewScanner(f)
|
||||||
|
for scanner.Scan() {
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
text = scanner.Text()
|
||||||
|
fields = strings.Split(text, " ")
|
||||||
|
// safe as mountinfo encodes mountpoints with spaces as \040.
|
||||||
|
index = strings.Index(text, " - ")
|
||||||
|
postSeparatorFields = strings.Fields(text[index+3:])
|
||||||
|
numPostFields = len(postSeparatorFields)
|
||||||
|
)
|
||||||
|
// this is an error as we can't detect if the mount is for "cgroup"
|
||||||
|
if numPostFields == 0 {
|
||||||
|
return "", fmt.Errorf("Found no fields post '-' in %q", text)
|
||||||
|
}
|
||||||
|
if postSeparatorFields[0] == "cgroup" {
|
||||||
|
// check that the mount is properly formated.
|
||||||
|
if numPostFields < 3 {
|
||||||
|
return "", fmt.Errorf("Error found less than 3 fields post '-' in %q", text)
|
||||||
|
}
|
||||||
|
return filepath.Dir(fields[4]), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", ErrMountPointNotExist
|
||||||
|
}
|
191
vendor/github.com/coreos/go-systemd/LICENSE
generated
vendored
Normal file
191
vendor/github.com/coreos/go-systemd/LICENSE
generated
vendored
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
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.
|
54
vendor/github.com/coreos/go-systemd/README.md
generated
vendored
Normal file
54
vendor/github.com/coreos/go-systemd/README.md
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
# go-systemd
|
||||||
|
|
||||||
|
[](https://travis-ci.org/coreos/go-systemd)
|
||||||
|
[](http://godoc.org/github.com/coreos/go-systemd)
|
||||||
|
|
||||||
|
Go bindings to systemd. The project has several packages:
|
||||||
|
|
||||||
|
- `activation` - for writing and using socket activation from Go
|
||||||
|
- `dbus` - for starting/stopping/inspecting running services and units
|
||||||
|
- `journal` - for writing to systemd's logging service, journald
|
||||||
|
- `sdjournal` - for reading from journald by wrapping its C API
|
||||||
|
- `machine1` - for registering machines/containers with systemd
|
||||||
|
- `unit` - for (de)serialization and comparison of unit files
|
||||||
|
|
||||||
|
## Socket Activation
|
||||||
|
|
||||||
|
An example HTTP server using socket activation can be quickly set up by following this README on a Linux machine running systemd:
|
||||||
|
|
||||||
|
https://github.com/coreos/go-systemd/tree/master/examples/activation/httpserver
|
||||||
|
|
||||||
|
## Journal
|
||||||
|
|
||||||
|
Using the pure-Go `journal` package you can submit journal entries directly to systemd's journal, taking advantage of features like indexed key/value pairs for each log entry.
|
||||||
|
The `sdjournal` package provides read access to the journal by wrapping around journald's native C API; consequently it requires cgo and the journal headers to be available.
|
||||||
|
|
||||||
|
## D-Bus
|
||||||
|
|
||||||
|
The `dbus` package connects to the [systemd D-Bus API](http://www.freedesktop.org/wiki/Software/systemd/dbus/) and lets you start, stop and introspect systemd units. The API docs are here:
|
||||||
|
|
||||||
|
http://godoc.org/github.com/coreos/go-systemd/dbus
|
||||||
|
|
||||||
|
### Debugging
|
||||||
|
|
||||||
|
Create `/etc/dbus-1/system-local.conf` that looks like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
<!DOCTYPE busconfig PUBLIC
|
||||||
|
"-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
|
||||||
|
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
|
||||||
|
<busconfig>
|
||||||
|
<policy user="root">
|
||||||
|
<allow eavesdrop="true"/>
|
||||||
|
<allow eavesdrop="true" send_destination="*"/>
|
||||||
|
</policy>
|
||||||
|
</busconfig>
|
||||||
|
```
|
||||||
|
|
||||||
|
## machined
|
||||||
|
|
||||||
|
The `machine1` package allows interaction with the [systemd machined D-Bus API](http://www.freedesktop.org/wiki/Software/systemd/machined/).
|
||||||
|
|
||||||
|
## Units
|
||||||
|
|
||||||
|
The `unit` package provides various functions for working with [systemd unit files](http://www.freedesktop.org/software/systemd/man/systemd.unit.html).
|
213
vendor/github.com/coreos/go-systemd/dbus/dbus.go
generated
vendored
Normal file
213
vendor/github.com/coreos/go-systemd/dbus/dbus.go
generated
vendored
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
// 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"))
|
||||||
|
}
|
565
vendor/github.com/coreos/go-systemd/dbus/methods.go
generated
vendored
Normal file
565
vendor/github.com/coreos/go-systemd/dbus/methods.go
generated
vendored
Normal file
@ -0,0 +1,565 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/godbus/dbus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Conn) jobComplete(signal *dbus.Signal) {
|
||||||
|
var id uint32
|
||||||
|
var job dbus.ObjectPath
|
||||||
|
var unit string
|
||||||
|
var result string
|
||||||
|
dbus.Store(signal.Body, &id, &job, &unit, &result)
|
||||||
|
c.jobListener.Lock()
|
||||||
|
out, ok := c.jobListener.jobs[job]
|
||||||
|
if ok {
|
||||||
|
out <- result
|
||||||
|
delete(c.jobListener.jobs, job)
|
||||||
|
}
|
||||||
|
c.jobListener.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) startJob(ch chan<- string, job string, args ...interface{}) (int, error) {
|
||||||
|
if ch != nil {
|
||||||
|
c.jobListener.Lock()
|
||||||
|
defer c.jobListener.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
var p dbus.ObjectPath
|
||||||
|
err := c.sysobj.Call(job, 0, args...).Store(&p)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ch != nil {
|
||||||
|
c.jobListener.jobs[p] = ch
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore error since 0 is fine if conversion fails
|
||||||
|
jobID, _ := strconv.Atoi(path.Base(string(p)))
|
||||||
|
|
||||||
|
return jobID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartUnit enqueues a start job and depending jobs, if any (unless otherwise
|
||||||
|
// specified by the mode string).
|
||||||
|
//
|
||||||
|
// Takes the unit to activate, plus a mode string. The mode needs to be one of
|
||||||
|
// replace, fail, isolate, ignore-dependencies, ignore-requirements. If
|
||||||
|
// "replace" the call will start the unit and its dependencies, possibly
|
||||||
|
// replacing already queued jobs that conflict with this. If "fail" the call
|
||||||
|
// will start the unit and its dependencies, but will fail if this would change
|
||||||
|
// an already queued job. If "isolate" the call will start the unit in question
|
||||||
|
// and terminate all units that aren't dependencies of it. If
|
||||||
|
// "ignore-dependencies" it will start a unit but ignore all its dependencies.
|
||||||
|
// If "ignore-requirements" it will start a unit but only ignore the
|
||||||
|
// requirement dependencies. It is not recommended to make use of the latter
|
||||||
|
// two options.
|
||||||
|
//
|
||||||
|
// If the provided channel is non-nil, a result string will be sent to it upon
|
||||||
|
// job completion: one of done, canceled, timeout, failed, dependency, skipped.
|
||||||
|
// done indicates successful execution of a job. canceled indicates that a job
|
||||||
|
// has been canceled before it finished execution. timeout indicates that the
|
||||||
|
// job timeout was reached. failed indicates that the job failed. dependency
|
||||||
|
// indicates that a job this job has been depending on failed and the job hence
|
||||||
|
// has been removed too. skipped indicates that a job was skipped because it
|
||||||
|
// didn't apply to the units current state.
|
||||||
|
//
|
||||||
|
// If no error occurs, the ID of the underlying systemd job will be returned. There
|
||||||
|
// does exist the possibility for no error to be returned, but for the returned job
|
||||||
|
// ID to be 0. In this case, the actual underlying ID is not 0 and this datapoint
|
||||||
|
// should not be considered authoritative.
|
||||||
|
//
|
||||||
|
// If an error does occur, it will be returned to the user alongside a job ID of 0.
|
||||||
|
func (c *Conn) StartUnit(name string, mode string, ch chan<- string) (int, error) {
|
||||||
|
return c.startJob(ch, "org.freedesktop.systemd1.Manager.StartUnit", name, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StopUnit is similar to StartUnit but stops the specified unit rather
|
||||||
|
// than starting it.
|
||||||
|
func (c *Conn) StopUnit(name string, mode string, ch chan<- string) (int, error) {
|
||||||
|
return c.startJob(ch, "org.freedesktop.systemd1.Manager.StopUnit", name, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReloadUnit reloads a unit. Reloading is done only if the unit is already running and fails otherwise.
|
||||||
|
func (c *Conn) ReloadUnit(name string, mode string, ch chan<- string) (int, error) {
|
||||||
|
return c.startJob(ch, "org.freedesktop.systemd1.Manager.ReloadUnit", name, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RestartUnit restarts a service. If a service is restarted that isn't
|
||||||
|
// running it will be started.
|
||||||
|
func (c *Conn) RestartUnit(name string, mode string, ch chan<- string) (int, error) {
|
||||||
|
return c.startJob(ch, "org.freedesktop.systemd1.Manager.RestartUnit", name, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryRestartUnit is like RestartUnit, except that a service that isn't running
|
||||||
|
// is not affected by the restart.
|
||||||
|
func (c *Conn) TryRestartUnit(name string, mode string, ch chan<- string) (int, error) {
|
||||||
|
return c.startJob(ch, "org.freedesktop.systemd1.Manager.TryRestartUnit", name, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReloadOrRestart attempts a reload if the unit supports it and use a restart
|
||||||
|
// otherwise.
|
||||||
|
func (c *Conn) ReloadOrRestartUnit(name string, mode string, ch chan<- string) (int, error) {
|
||||||
|
return c.startJob(ch, "org.freedesktop.systemd1.Manager.ReloadOrRestartUnit", name, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReloadOrTryRestart attempts a reload if the unit supports it and use a "Try"
|
||||||
|
// flavored restart otherwise.
|
||||||
|
func (c *Conn) ReloadOrTryRestartUnit(name string, mode string, ch chan<- string) (int, error) {
|
||||||
|
return c.startJob(ch, "org.freedesktop.systemd1.Manager.ReloadOrTryRestartUnit", name, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartTransientUnit() may be used to create and start a transient unit, which
|
||||||
|
// will be released as soon as it is not running or referenced anymore or the
|
||||||
|
// system is rebooted. name is the unit name including suffix, and must be
|
||||||
|
// unique. mode is the same as in StartUnit(), properties contains properties
|
||||||
|
// of the unit.
|
||||||
|
func (c *Conn) StartTransientUnit(name string, mode string, properties []Property, ch chan<- string) (int, error) {
|
||||||
|
return c.startJob(ch, "org.freedesktop.systemd1.Manager.StartTransientUnit", name, mode, properties, make([]PropertyCollection, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
// KillUnit takes the unit name and a UNIX signal number to send. All of the unit's
|
||||||
|
// processes are killed.
|
||||||
|
func (c *Conn) KillUnit(name string, signal int32) {
|
||||||
|
c.sysobj.Call("org.freedesktop.systemd1.Manager.KillUnit", 0, name, "all", signal).Store()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetFailedUnit resets the "failed" state of a specific unit.
|
||||||
|
func (c *Conn) ResetFailedUnit(name string) error {
|
||||||
|
return c.sysobj.Call("org.freedesktop.systemd1.Manager.ResetFailedUnit", 0, name).Store()
|
||||||
|
}
|
||||||
|
|
||||||
|
// getProperties takes the unit name and returns all of its dbus object properties, for the given dbus interface
|
||||||
|
func (c *Conn) getProperties(unit string, dbusInterface string) (map[string]interface{}, error) {
|
||||||
|
var err error
|
||||||
|
var props map[string]dbus.Variant
|
||||||
|
|
||||||
|
path := unitPath(unit)
|
||||||
|
if !path.IsValid() {
|
||||||
|
return nil, errors.New("invalid unit name: " + unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
obj := c.sysconn.Object("org.freedesktop.systemd1", path)
|
||||||
|
err = obj.Call("org.freedesktop.DBus.Properties.GetAll", 0, dbusInterface).Store(&props)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
out := make(map[string]interface{}, len(props))
|
||||||
|
for k, v := range props {
|
||||||
|
out[k] = v.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUnitProperties takes the unit name and returns all of its dbus object properties.
|
||||||
|
func (c *Conn) GetUnitProperties(unit string) (map[string]interface{}, error) {
|
||||||
|
return c.getProperties(unit, "org.freedesktop.systemd1.Unit")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) getProperty(unit string, dbusInterface string, propertyName string) (*Property, error) {
|
||||||
|
var err error
|
||||||
|
var prop dbus.Variant
|
||||||
|
|
||||||
|
path := unitPath(unit)
|
||||||
|
if !path.IsValid() {
|
||||||
|
return nil, errors.New("invalid unit name: " + unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
obj := c.sysconn.Object("org.freedesktop.systemd1", path)
|
||||||
|
err = obj.Call("org.freedesktop.DBus.Properties.Get", 0, dbusInterface, propertyName).Store(&prop)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Property{Name: propertyName, Value: prop}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) GetUnitProperty(unit string, propertyName string) (*Property, error) {
|
||||||
|
return c.getProperty(unit, "org.freedesktop.systemd1.Unit", propertyName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetServiceProperty returns property for given service name and property name
|
||||||
|
func (c *Conn) GetServiceProperty(service string, propertyName string) (*Property, error) {
|
||||||
|
return c.getProperty(service, "org.freedesktop.systemd1.Service", propertyName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUnitTypeProperties returns the extra properties for a unit, specific to the unit type.
|
||||||
|
// Valid values for unitType: Service, Socket, Target, Device, Mount, Automount, Snapshot, Timer, Swap, Path, Slice, Scope
|
||||||
|
// return "dbus.Error: Unknown interface" if the unitType is not the correct type of the unit
|
||||||
|
func (c *Conn) GetUnitTypeProperties(unit string, unitType string) (map[string]interface{}, error) {
|
||||||
|
return c.getProperties(unit, "org.freedesktop.systemd1."+unitType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUnitProperties() may be used to modify certain unit properties at runtime.
|
||||||
|
// Not all properties may be changed at runtime, but many resource management
|
||||||
|
// settings (primarily those in systemd.cgroup(5)) may. The changes are applied
|
||||||
|
// instantly, and stored on disk for future boots, unless runtime is true, in which
|
||||||
|
// case the settings only apply until the next reboot. name is the name of the unit
|
||||||
|
// to modify. properties are the settings to set, encoded as an array of property
|
||||||
|
// name and value pairs.
|
||||||
|
func (c *Conn) SetUnitProperties(name string, runtime bool, properties ...Property) error {
|
||||||
|
return c.sysobj.Call("org.freedesktop.systemd1.Manager.SetUnitProperties", 0, name, runtime, properties).Store()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) GetUnitTypeProperty(unit string, unitType string, propertyName string) (*Property, error) {
|
||||||
|
return c.getProperty(unit, "org.freedesktop.systemd1."+unitType, propertyName)
|
||||||
|
}
|
||||||
|
|
||||||
|
type UnitStatus struct {
|
||||||
|
Name string // The primary unit name as string
|
||||||
|
Description string // The human readable description string
|
||||||
|
LoadState string // The load state (i.e. whether the unit file has been loaded successfully)
|
||||||
|
ActiveState string // The active state (i.e. whether the unit is currently started or not)
|
||||||
|
SubState string // The sub state (a more fine-grained version of the active state that is specific to the unit type, which the active state is not)
|
||||||
|
Followed string // A unit that is being followed in its state by this unit, if there is any, otherwise the empty string.
|
||||||
|
Path dbus.ObjectPath // The unit object path
|
||||||
|
JobId uint32 // If there is a job queued for the job unit the numeric job id, 0 otherwise
|
||||||
|
JobType string // The job type as string
|
||||||
|
JobPath dbus.ObjectPath // The job object path
|
||||||
|
}
|
||||||
|
|
||||||
|
type storeFunc func(retvalues ...interface{}) error
|
||||||
|
|
||||||
|
func (c *Conn) listUnitsInternal(f storeFunc) ([]UnitStatus, error) {
|
||||||
|
result := make([][]interface{}, 0)
|
||||||
|
err := f(&result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resultInterface := make([]interface{}, len(result))
|
||||||
|
for i := range result {
|
||||||
|
resultInterface[i] = result[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
status := make([]UnitStatus, len(result))
|
||||||
|
statusInterface := make([]interface{}, len(status))
|
||||||
|
for i := range status {
|
||||||
|
statusInterface[i] = &status[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dbus.Store(resultInterface, statusInterface...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return status, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListUnits returns an array with all currently loaded units. Note that
|
||||||
|
// units may be known by multiple names at the same time, and hence there might
|
||||||
|
// be more unit names loaded than actual units behind them.
|
||||||
|
func (c *Conn) ListUnits() ([]UnitStatus, error) {
|
||||||
|
return c.listUnitsInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnits", 0).Store)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListUnitsFiltered returns an array with units filtered by state.
|
||||||
|
// It takes a list of units' statuses to filter.
|
||||||
|
func (c *Conn) ListUnitsFiltered(states []string) ([]UnitStatus, error) {
|
||||||
|
return c.listUnitsInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnitsFiltered", 0, states).Store)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListUnitsByPatterns returns an array with units.
|
||||||
|
// It takes a list of units' statuses and names to filter.
|
||||||
|
// Note that units may be known by multiple names at the same time,
|
||||||
|
// and hence there might be more unit names loaded than actual units behind them.
|
||||||
|
func (c *Conn) ListUnitsByPatterns(states []string, patterns []string) ([]UnitStatus, error) {
|
||||||
|
return c.listUnitsInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnitsByPatterns", 0, states, patterns).Store)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListUnitsByNames returns an array with units. It takes a list of units'
|
||||||
|
// names and returns an UnitStatus array. Comparing to ListUnitsByPatterns
|
||||||
|
// method, this method returns statuses even for inactive or non-existing
|
||||||
|
// units. Input array should contain exact unit names, but not patterns.
|
||||||
|
func (c *Conn) ListUnitsByNames(units []string) ([]UnitStatus, error) {
|
||||||
|
return c.listUnitsInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnitsByNames", 0, units).Store)
|
||||||
|
}
|
||||||
|
|
||||||
|
type UnitFile struct {
|
||||||
|
Path string
|
||||||
|
Type string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) listUnitFilesInternal(f storeFunc) ([]UnitFile, error) {
|
||||||
|
result := make([][]interface{}, 0)
|
||||||
|
err := f(&result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resultInterface := make([]interface{}, len(result))
|
||||||
|
for i := range result {
|
||||||
|
resultInterface[i] = result[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
files := make([]UnitFile, len(result))
|
||||||
|
fileInterface := make([]interface{}, len(files))
|
||||||
|
for i := range files {
|
||||||
|
fileInterface[i] = &files[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dbus.Store(resultInterface, fileInterface...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListUnitFiles returns an array of all available units on disk.
|
||||||
|
func (c *Conn) ListUnitFiles() ([]UnitFile, error) {
|
||||||
|
return c.listUnitFilesInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnitFiles", 0).Store)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListUnitFilesByPatterns returns an array of all available units on disk matched the patterns.
|
||||||
|
func (c *Conn) ListUnitFilesByPatterns(states []string, patterns []string) ([]UnitFile, error) {
|
||||||
|
return c.listUnitFilesInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnitFilesByPatterns", 0, states, patterns).Store)
|
||||||
|
}
|
||||||
|
|
||||||
|
type LinkUnitFileChange EnableUnitFileChange
|
||||||
|
|
||||||
|
// LinkUnitFiles() links unit files (that are located outside of the
|
||||||
|
// usual unit search paths) into the unit search path.
|
||||||
|
//
|
||||||
|
// It takes a list of absolute paths to unit files to link and two
|
||||||
|
// booleans. The first boolean controls whether the unit shall be
|
||||||
|
// enabled for runtime only (true, /run), or persistently (false,
|
||||||
|
// /etc).
|
||||||
|
// The second controls whether symlinks pointing to other units shall
|
||||||
|
// be replaced if necessary.
|
||||||
|
//
|
||||||
|
// This call returns a list of the changes made. The list consists of
|
||||||
|
// structures with three strings: the type of the change (one of symlink
|
||||||
|
// or unlink), the file name of the symlink and the destination of the
|
||||||
|
// symlink.
|
||||||
|
func (c *Conn) LinkUnitFiles(files []string, runtime bool, force bool) ([]LinkUnitFileChange, error) {
|
||||||
|
result := make([][]interface{}, 0)
|
||||||
|
err := c.sysobj.Call("org.freedesktop.systemd1.Manager.LinkUnitFiles", 0, files, runtime, force).Store(&result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resultInterface := make([]interface{}, len(result))
|
||||||
|
for i := range result {
|
||||||
|
resultInterface[i] = result[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
changes := make([]LinkUnitFileChange, len(result))
|
||||||
|
changesInterface := make([]interface{}, len(changes))
|
||||||
|
for i := range changes {
|
||||||
|
changesInterface[i] = &changes[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dbus.Store(resultInterface, changesInterface...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableUnitFiles() may be used to enable one or more units in the system (by
|
||||||
|
// creating symlinks to them in /etc or /run).
|
||||||
|
//
|
||||||
|
// It takes a list of unit files to enable (either just file names or full
|
||||||
|
// absolute paths if the unit files are residing outside the usual unit
|
||||||
|
// search paths), and two booleans: the first controls whether the unit shall
|
||||||
|
// be enabled for runtime only (true, /run), or persistently (false, /etc).
|
||||||
|
// The second one controls whether symlinks pointing to other units shall
|
||||||
|
// be replaced if necessary.
|
||||||
|
//
|
||||||
|
// This call returns one boolean and an array with the changes made. The
|
||||||
|
// boolean signals whether the unit files contained any enablement
|
||||||
|
// information (i.e. an [Install]) section. The changes list consists of
|
||||||
|
// structures with three strings: the type of the change (one of symlink
|
||||||
|
// or unlink), the file name of the symlink and the destination of the
|
||||||
|
// symlink.
|
||||||
|
func (c *Conn) EnableUnitFiles(files []string, runtime bool, force bool) (bool, []EnableUnitFileChange, error) {
|
||||||
|
var carries_install_info bool
|
||||||
|
|
||||||
|
result := make([][]interface{}, 0)
|
||||||
|
err := c.sysobj.Call("org.freedesktop.systemd1.Manager.EnableUnitFiles", 0, files, runtime, force).Store(&carries_install_info, &result)
|
||||||
|
if err != nil {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resultInterface := make([]interface{}, len(result))
|
||||||
|
for i := range result {
|
||||||
|
resultInterface[i] = result[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
changes := make([]EnableUnitFileChange, len(result))
|
||||||
|
changesInterface := make([]interface{}, len(changes))
|
||||||
|
for i := range changes {
|
||||||
|
changesInterface[i] = &changes[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dbus.Store(resultInterface, changesInterface...)
|
||||||
|
if err != nil {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return carries_install_info, changes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type EnableUnitFileChange struct {
|
||||||
|
Type string // Type of the change (one of symlink or unlink)
|
||||||
|
Filename string // File name of the symlink
|
||||||
|
Destination string // Destination of the symlink
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableUnitFiles() may be used to disable one or more units in the system (by
|
||||||
|
// removing symlinks to them from /etc or /run).
|
||||||
|
//
|
||||||
|
// It takes a list of unit files to disable (either just file names or full
|
||||||
|
// absolute paths if the unit files are residing outside the usual unit
|
||||||
|
// search paths), and one boolean: whether the unit was enabled for runtime
|
||||||
|
// only (true, /run), or persistently (false, /etc).
|
||||||
|
//
|
||||||
|
// This call returns an array with the changes made. The changes list
|
||||||
|
// consists of structures with three strings: the type of the change (one of
|
||||||
|
// symlink or unlink), the file name of the symlink and the destination of the
|
||||||
|
// symlink.
|
||||||
|
func (c *Conn) DisableUnitFiles(files []string, runtime bool) ([]DisableUnitFileChange, error) {
|
||||||
|
result := make([][]interface{}, 0)
|
||||||
|
err := c.sysobj.Call("org.freedesktop.systemd1.Manager.DisableUnitFiles", 0, files, runtime).Store(&result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resultInterface := make([]interface{}, len(result))
|
||||||
|
for i := range result {
|
||||||
|
resultInterface[i] = result[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
changes := make([]DisableUnitFileChange, len(result))
|
||||||
|
changesInterface := make([]interface{}, len(changes))
|
||||||
|
for i := range changes {
|
||||||
|
changesInterface[i] = &changes[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dbus.Store(resultInterface, changesInterface...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type DisableUnitFileChange struct {
|
||||||
|
Type string // Type of the change (one of symlink or unlink)
|
||||||
|
Filename string // File name of the symlink
|
||||||
|
Destination string // Destination of the symlink
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaskUnitFiles masks one or more units in the system
|
||||||
|
//
|
||||||
|
// It takes three arguments:
|
||||||
|
// * list of units to mask (either just file names or full
|
||||||
|
// absolute paths if the unit files are residing outside
|
||||||
|
// the usual unit search paths)
|
||||||
|
// * runtime to specify whether the unit was enabled for runtime
|
||||||
|
// only (true, /run/systemd/..), or persistently (false, /etc/systemd/..)
|
||||||
|
// * force flag
|
||||||
|
func (c *Conn) MaskUnitFiles(files []string, runtime bool, force bool) ([]MaskUnitFileChange, error) {
|
||||||
|
result := make([][]interface{}, 0)
|
||||||
|
err := c.sysobj.Call("org.freedesktop.systemd1.Manager.MaskUnitFiles", 0, files, runtime, force).Store(&result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resultInterface := make([]interface{}, len(result))
|
||||||
|
for i := range result {
|
||||||
|
resultInterface[i] = result[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
changes := make([]MaskUnitFileChange, len(result))
|
||||||
|
changesInterface := make([]interface{}, len(changes))
|
||||||
|
for i := range changes {
|
||||||
|
changesInterface[i] = &changes[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dbus.Store(resultInterface, changesInterface...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type MaskUnitFileChange struct {
|
||||||
|
Type string // Type of the change (one of symlink or unlink)
|
||||||
|
Filename string // File name of the symlink
|
||||||
|
Destination string // Destination of the symlink
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmaskUnitFiles unmasks one or more units in the system
|
||||||
|
//
|
||||||
|
// It takes two arguments:
|
||||||
|
// * list of unit files to mask (either just file names or full
|
||||||
|
// absolute paths if the unit files are residing outside
|
||||||
|
// the usual unit search paths)
|
||||||
|
// * runtime to specify whether the unit was enabled for runtime
|
||||||
|
// only (true, /run/systemd/..), or persistently (false, /etc/systemd/..)
|
||||||
|
func (c *Conn) UnmaskUnitFiles(files []string, runtime bool) ([]UnmaskUnitFileChange, error) {
|
||||||
|
result := make([][]interface{}, 0)
|
||||||
|
err := c.sysobj.Call("org.freedesktop.systemd1.Manager.UnmaskUnitFiles", 0, files, runtime).Store(&result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resultInterface := make([]interface{}, len(result))
|
||||||
|
for i := range result {
|
||||||
|
resultInterface[i] = result[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
changes := make([]UnmaskUnitFileChange, len(result))
|
||||||
|
changesInterface := make([]interface{}, len(changes))
|
||||||
|
for i := range changes {
|
||||||
|
changesInterface[i] = &changes[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dbus.Store(resultInterface, changesInterface...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type UnmaskUnitFileChange struct {
|
||||||
|
Type string // Type of the change (one of symlink or unlink)
|
||||||
|
Filename string // File name of the symlink
|
||||||
|
Destination string // Destination of the symlink
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reload instructs systemd to scan for and reload unit files. This is
|
||||||
|
// equivalent to a 'systemctl daemon-reload'.
|
||||||
|
func (c *Conn) Reload() error {
|
||||||
|
return c.sysobj.Call("org.freedesktop.systemd1.Manager.Reload", 0).Store()
|
||||||
|
}
|
||||||
|
|
||||||
|
func unitPath(name string) dbus.ObjectPath {
|
||||||
|
return dbus.ObjectPath("/org/freedesktop/systemd1/unit/" + PathBusEscape(name))
|
||||||
|
}
|
237
vendor/github.com/coreos/go-systemd/dbus/properties.go
generated
vendored
Normal file
237
vendor/github.com/coreos/go-systemd/dbus/properties.go
generated
vendored
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/godbus/dbus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// From the systemd docs:
|
||||||
|
//
|
||||||
|
// The properties array of StartTransientUnit() may take many of the settings
|
||||||
|
// that may also be configured in unit files. Not all parameters are currently
|
||||||
|
// accepted though, but we plan to cover more properties with future release.
|
||||||
|
// Currently you may set the Description, Slice and all dependency types of
|
||||||
|
// units, as well as RemainAfterExit, ExecStart for service units,
|
||||||
|
// TimeoutStopUSec and PIDs for scope units, and CPUAccounting, CPUShares,
|
||||||
|
// BlockIOAccounting, BlockIOWeight, BlockIOReadBandwidth,
|
||||||
|
// BlockIOWriteBandwidth, BlockIODeviceWeight, MemoryAccounting, MemoryLimit,
|
||||||
|
// DevicePolicy, DeviceAllow for services/scopes/slices. These fields map
|
||||||
|
// directly to their counterparts in unit files and as normal D-Bus object
|
||||||
|
// properties. The exception here is the PIDs field of scope units which is
|
||||||
|
// used for construction of the scope only and specifies the initial PIDs to
|
||||||
|
// add to the scope object.
|
||||||
|
|
||||||
|
type Property struct {
|
||||||
|
Name string
|
||||||
|
Value dbus.Variant
|
||||||
|
}
|
||||||
|
|
||||||
|
type PropertyCollection struct {
|
||||||
|
Name string
|
||||||
|
Properties []Property
|
||||||
|
}
|
||||||
|
|
||||||
|
type execStart struct {
|
||||||
|
Path string // the binary path to execute
|
||||||
|
Args []string // an array with all arguments to pass to the executed command, starting with argument 0
|
||||||
|
UncleanIsFailure bool // a boolean whether it should be considered a failure if the process exits uncleanly
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropExecStart sets the ExecStart service property. The first argument is a
|
||||||
|
// slice with the binary path to execute followed by the arguments to pass to
|
||||||
|
// the executed command. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart=
|
||||||
|
func PropExecStart(command []string, uncleanIsFailure bool) Property {
|
||||||
|
execStarts := []execStart{
|
||||||
|
execStart{
|
||||||
|
Path: command[0],
|
||||||
|
Args: command,
|
||||||
|
UncleanIsFailure: uncleanIsFailure,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return Property{
|
||||||
|
Name: "ExecStart",
|
||||||
|
Value: dbus.MakeVariant(execStarts),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropRemainAfterExit sets the RemainAfterExit service property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.service.html#RemainAfterExit=
|
||||||
|
func PropRemainAfterExit(b bool) Property {
|
||||||
|
return Property{
|
||||||
|
Name: "RemainAfterExit",
|
||||||
|
Value: dbus.MakeVariant(b),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropType sets the Type service property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.service.html#Type=
|
||||||
|
func PropType(t string) Property {
|
||||||
|
return Property{
|
||||||
|
Name: "Type",
|
||||||
|
Value: dbus.MakeVariant(t),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropDescription sets the Description unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit#Description=
|
||||||
|
func PropDescription(desc string) Property {
|
||||||
|
return Property{
|
||||||
|
Name: "Description",
|
||||||
|
Value: dbus.MakeVariant(desc),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func propDependency(name string, units []string) Property {
|
||||||
|
return Property{
|
||||||
|
Name: name,
|
||||||
|
Value: dbus.MakeVariant(units),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropRequires sets the Requires unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Requires=
|
||||||
|
func PropRequires(units ...string) Property {
|
||||||
|
return propDependency("Requires", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropRequiresOverridable sets the RequiresOverridable unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiresOverridable=
|
||||||
|
func PropRequiresOverridable(units ...string) Property {
|
||||||
|
return propDependency("RequiresOverridable", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropRequisite sets the Requisite unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Requisite=
|
||||||
|
func PropRequisite(units ...string) Property {
|
||||||
|
return propDependency("Requisite", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropRequisiteOverridable sets the RequisiteOverridable unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequisiteOverridable=
|
||||||
|
func PropRequisiteOverridable(units ...string) Property {
|
||||||
|
return propDependency("RequisiteOverridable", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropWants sets the Wants unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Wants=
|
||||||
|
func PropWants(units ...string) Property {
|
||||||
|
return propDependency("Wants", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropBindsTo sets the BindsTo unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#BindsTo=
|
||||||
|
func PropBindsTo(units ...string) Property {
|
||||||
|
return propDependency("BindsTo", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropRequiredBy sets the RequiredBy unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiredBy=
|
||||||
|
func PropRequiredBy(units ...string) Property {
|
||||||
|
return propDependency("RequiredBy", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropRequiredByOverridable sets the RequiredByOverridable unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiredByOverridable=
|
||||||
|
func PropRequiredByOverridable(units ...string) Property {
|
||||||
|
return propDependency("RequiredByOverridable", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropWantedBy sets the WantedBy unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#WantedBy=
|
||||||
|
func PropWantedBy(units ...string) Property {
|
||||||
|
return propDependency("WantedBy", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropBoundBy sets the BoundBy unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/main/systemd.unit.html#BoundBy=
|
||||||
|
func PropBoundBy(units ...string) Property {
|
||||||
|
return propDependency("BoundBy", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropConflicts sets the Conflicts unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Conflicts=
|
||||||
|
func PropConflicts(units ...string) Property {
|
||||||
|
return propDependency("Conflicts", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropConflictedBy sets the ConflictedBy unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#ConflictedBy=
|
||||||
|
func PropConflictedBy(units ...string) Property {
|
||||||
|
return propDependency("ConflictedBy", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropBefore sets the Before unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Before=
|
||||||
|
func PropBefore(units ...string) Property {
|
||||||
|
return propDependency("Before", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropAfter sets the After unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#After=
|
||||||
|
func PropAfter(units ...string) Property {
|
||||||
|
return propDependency("After", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropOnFailure sets the OnFailure unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#OnFailure=
|
||||||
|
func PropOnFailure(units ...string) Property {
|
||||||
|
return propDependency("OnFailure", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropTriggers sets the Triggers unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Triggers=
|
||||||
|
func PropTriggers(units ...string) Property {
|
||||||
|
return propDependency("Triggers", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropTriggeredBy sets the TriggeredBy unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#TriggeredBy=
|
||||||
|
func PropTriggeredBy(units ...string) Property {
|
||||||
|
return propDependency("TriggeredBy", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropPropagatesReloadTo sets the PropagatesReloadTo unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#PropagatesReloadTo=
|
||||||
|
func PropPropagatesReloadTo(units ...string) Property {
|
||||||
|
return propDependency("PropagatesReloadTo", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropRequiresMountsFor sets the RequiresMountsFor unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiresMountsFor=
|
||||||
|
func PropRequiresMountsFor(units ...string) Property {
|
||||||
|
return propDependency("RequiresMountsFor", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropSlice sets the Slice unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#Slice=
|
||||||
|
func PropSlice(slice string) Property {
|
||||||
|
return Property{
|
||||||
|
Name: "Slice",
|
||||||
|
Value: dbus.MakeVariant(slice),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropPids sets the PIDs field of scope units used in the initial construction
|
||||||
|
// of the scope only and specifies the initial PIDs to add to the scope object.
|
||||||
|
// See https://www.freedesktop.org/wiki/Software/systemd/ControlGroupInterface/#properties
|
||||||
|
func PropPids(pids ...uint32) Property {
|
||||||
|
return Property{
|
||||||
|
Name: "PIDs",
|
||||||
|
Value: dbus.MakeVariant(pids),
|
||||||
|
}
|
||||||
|
}
|
47
vendor/github.com/coreos/go-systemd/dbus/set.go
generated
vendored
Normal file
47
vendor/github.com/coreos/go-systemd/dbus/set.go
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
type set struct {
|
||||||
|
data map[string]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *set) Add(value string) {
|
||||||
|
s.data[value] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *set) Remove(value string) {
|
||||||
|
delete(s.data, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *set) Contains(value string) (exists bool) {
|
||||||
|
_, exists = s.data[value]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *set) Length() int {
|
||||||
|
return len(s.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *set) Values() (values []string) {
|
||||||
|
for val, _ := range s.data {
|
||||||
|
values = append(values, val)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSet() *set {
|
||||||
|
return &set{make(map[string]bool)}
|
||||||
|
}
|
250
vendor/github.com/coreos/go-systemd/dbus/subscription.go
generated
vendored
Normal file
250
vendor/github.com/coreos/go-systemd/dbus/subscription.go
generated
vendored
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/godbus/dbus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
cleanIgnoreInterval = int64(10 * time.Second)
|
||||||
|
ignoreInterval = int64(30 * time.Millisecond)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Subscribe sets up this connection to subscribe to all systemd dbus events.
|
||||||
|
// This is required before calling SubscribeUnits. When the connection closes
|
||||||
|
// systemd will automatically stop sending signals so there is no need to
|
||||||
|
// explicitly call Unsubscribe().
|
||||||
|
func (c *Conn) Subscribe() error {
|
||||||
|
c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
|
||||||
|
"type='signal',interface='org.freedesktop.systemd1.Manager',member='UnitNew'")
|
||||||
|
c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
|
||||||
|
"type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'")
|
||||||
|
|
||||||
|
err := c.sigobj.Call("org.freedesktop.systemd1.Manager.Subscribe", 0).Store()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsubscribe this connection from systemd dbus events.
|
||||||
|
func (c *Conn) Unsubscribe() error {
|
||||||
|
err := c.sigobj.Call("org.freedesktop.systemd1.Manager.Unsubscribe", 0).Store()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) dispatch() {
|
||||||
|
ch := make(chan *dbus.Signal, signalBuffer)
|
||||||
|
|
||||||
|
c.sigconn.Signal(ch)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
signal, ok := <-ch
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if signal.Name == "org.freedesktop.systemd1.Manager.JobRemoved" {
|
||||||
|
c.jobComplete(signal)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.subscriber.updateCh == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var unitPath dbus.ObjectPath
|
||||||
|
switch signal.Name {
|
||||||
|
case "org.freedesktop.systemd1.Manager.JobRemoved":
|
||||||
|
unitName := signal.Body[2].(string)
|
||||||
|
c.sysobj.Call("org.freedesktop.systemd1.Manager.GetUnit", 0, unitName).Store(&unitPath)
|
||||||
|
case "org.freedesktop.systemd1.Manager.UnitNew":
|
||||||
|
unitPath = signal.Body[1].(dbus.ObjectPath)
|
||||||
|
case "org.freedesktop.DBus.Properties.PropertiesChanged":
|
||||||
|
if signal.Body[0].(string) == "org.freedesktop.systemd1.Unit" {
|
||||||
|
unitPath = signal.Path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if unitPath == dbus.ObjectPath("") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
c.sendSubStateUpdate(unitPath)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns two unbuffered channels which will receive all changed units every
|
||||||
|
// interval. Deleted units are sent as nil.
|
||||||
|
func (c *Conn) SubscribeUnits(interval time.Duration) (<-chan map[string]*UnitStatus, <-chan error) {
|
||||||
|
return c.SubscribeUnitsCustom(interval, 0, func(u1, u2 *UnitStatus) bool { return *u1 != *u2 }, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscribeUnitsCustom is like SubscribeUnits but lets you specify the buffer
|
||||||
|
// size of the channels, the comparison function for detecting changes and a filter
|
||||||
|
// function for cutting down on the noise that your channel receives.
|
||||||
|
func (c *Conn) SubscribeUnitsCustom(interval time.Duration, buffer int, isChanged func(*UnitStatus, *UnitStatus) bool, filterUnit func(string) bool) (<-chan map[string]*UnitStatus, <-chan error) {
|
||||||
|
old := make(map[string]*UnitStatus)
|
||||||
|
statusChan := make(chan map[string]*UnitStatus, buffer)
|
||||||
|
errChan := make(chan error, buffer)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
timerChan := time.After(interval)
|
||||||
|
|
||||||
|
units, err := c.ListUnits()
|
||||||
|
if err == nil {
|
||||||
|
cur := make(map[string]*UnitStatus)
|
||||||
|
for i := range units {
|
||||||
|
if filterUnit != nil && filterUnit(units[i].Name) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cur[units[i].Name] = &units[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// add all new or changed units
|
||||||
|
changed := make(map[string]*UnitStatus)
|
||||||
|
for n, u := range cur {
|
||||||
|
if oldU, ok := old[n]; !ok || isChanged(oldU, u) {
|
||||||
|
changed[n] = u
|
||||||
|
}
|
||||||
|
delete(old, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// add all deleted units
|
||||||
|
for oldN := range old {
|
||||||
|
changed[oldN] = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
old = cur
|
||||||
|
|
||||||
|
if len(changed) != 0 {
|
||||||
|
statusChan <- changed
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errChan <- err
|
||||||
|
}
|
||||||
|
|
||||||
|
<-timerChan
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return statusChan, errChan
|
||||||
|
}
|
||||||
|
|
||||||
|
type SubStateUpdate struct {
|
||||||
|
UnitName string
|
||||||
|
SubState string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSubStateSubscriber writes to updateCh when any unit's substate changes.
|
||||||
|
// Although this writes to updateCh on every state change, the reported state
|
||||||
|
// may be more recent than the change that generated it (due to an unavoidable
|
||||||
|
// race in the systemd dbus interface). That is, this method provides a good
|
||||||
|
// way to keep a current view of all units' states, but is not guaranteed to
|
||||||
|
// show every state transition they go through. Furthermore, state changes
|
||||||
|
// will only be written to the channel with non-blocking writes. If updateCh
|
||||||
|
// is full, it attempts to write an error to errCh; if errCh is full, the error
|
||||||
|
// passes silently.
|
||||||
|
func (c *Conn) SetSubStateSubscriber(updateCh chan<- *SubStateUpdate, errCh chan<- error) {
|
||||||
|
c.subscriber.Lock()
|
||||||
|
defer c.subscriber.Unlock()
|
||||||
|
c.subscriber.updateCh = updateCh
|
||||||
|
c.subscriber.errCh = errCh
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) sendSubStateUpdate(path dbus.ObjectPath) {
|
||||||
|
c.subscriber.Lock()
|
||||||
|
defer c.subscriber.Unlock()
|
||||||
|
|
||||||
|
if c.shouldIgnore(path) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := c.GetUnitProperties(string(path))
|
||||||
|
if err != nil {
|
||||||
|
select {
|
||||||
|
case c.subscriber.errCh <- err:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
name := info["Id"].(string)
|
||||||
|
substate := info["SubState"].(string)
|
||||||
|
|
||||||
|
update := &SubStateUpdate{name, substate}
|
||||||
|
select {
|
||||||
|
case c.subscriber.updateCh <- update:
|
||||||
|
default:
|
||||||
|
select {
|
||||||
|
case c.subscriber.errCh <- errors.New("update channel full!"):
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.updateIgnore(path, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The ignore functions work around a wart in the systemd dbus interface.
|
||||||
|
// Requesting the properties of an unloaded unit will cause systemd to send a
|
||||||
|
// pair of UnitNew/UnitRemoved signals. Because we need to get a unit's
|
||||||
|
// properties on UnitNew (as that's the only indication of a new unit coming up
|
||||||
|
// for the first time), we would enter an infinite loop if we did not attempt
|
||||||
|
// to detect and ignore these spurious signals. The signal themselves are
|
||||||
|
// indistinguishable from relevant ones, so we (somewhat hackishly) ignore an
|
||||||
|
// unloaded unit's signals for a short time after requesting its properties.
|
||||||
|
// This means that we will miss e.g. a transient unit being restarted
|
||||||
|
// *immediately* upon failure and also a transient unit being started
|
||||||
|
// immediately after requesting its status (with systemctl status, for example,
|
||||||
|
// because this causes a UnitNew signal to be sent which then causes us to fetch
|
||||||
|
// the properties).
|
||||||
|
|
||||||
|
func (c *Conn) shouldIgnore(path dbus.ObjectPath) bool {
|
||||||
|
t, ok := c.subscriber.ignore[path]
|
||||||
|
return ok && t >= time.Now().UnixNano()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) updateIgnore(path dbus.ObjectPath, info map[string]interface{}) {
|
||||||
|
c.cleanIgnore()
|
||||||
|
|
||||||
|
// unit is unloaded - it will trigger bad systemd dbus behavior
|
||||||
|
if info["LoadState"].(string) == "not-found" {
|
||||||
|
c.subscriber.ignore[path] = time.Now().UnixNano() + ignoreInterval
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// without this, ignore would grow unboundedly over time
|
||||||
|
func (c *Conn) cleanIgnore() {
|
||||||
|
now := time.Now().UnixNano()
|
||||||
|
if c.subscriber.cleanIgnore < now {
|
||||||
|
c.subscriber.cleanIgnore = now + cleanIgnoreInterval
|
||||||
|
|
||||||
|
for p, t := range c.subscriber.ignore {
|
||||||
|
if t < now {
|
||||||
|
delete(c.subscriber.ignore, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
57
vendor/github.com/coreos/go-systemd/dbus/subscription_set.go
generated
vendored
Normal file
57
vendor/github.com/coreos/go-systemd/dbus/subscription_set.go
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SubscriptionSet returns a subscription set which is like conn.Subscribe but
|
||||||
|
// can filter to only return events for a set of units.
|
||||||
|
type SubscriptionSet struct {
|
||||||
|
*set
|
||||||
|
conn *Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SubscriptionSet) filter(unit string) bool {
|
||||||
|
return !s.Contains(unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribe starts listening for dbus events for all of the units in the set.
|
||||||
|
// Returns channels identical to conn.SubscribeUnits.
|
||||||
|
func (s *SubscriptionSet) Subscribe() (<-chan map[string]*UnitStatus, <-chan error) {
|
||||||
|
// TODO: Make fully evented by using systemd 209 with properties changed values
|
||||||
|
return s.conn.SubscribeUnitsCustom(time.Second, 0,
|
||||||
|
mismatchUnitStatus,
|
||||||
|
func(unit string) bool { return s.filter(unit) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSubscriptionSet returns a new subscription set.
|
||||||
|
func (conn *Conn) NewSubscriptionSet() *SubscriptionSet {
|
||||||
|
return &SubscriptionSet{newSet(), conn}
|
||||||
|
}
|
||||||
|
|
||||||
|
// mismatchUnitStatus returns true if the provided UnitStatus objects
|
||||||
|
// are not equivalent. false is returned if the objects are equivalent.
|
||||||
|
// Only the Name, Description and state-related fields are used in
|
||||||
|
// the comparison.
|
||||||
|
func mismatchUnitStatus(u1, u2 *UnitStatus) bool {
|
||||||
|
return u1.Name != u2.Name ||
|
||||||
|
u1.Description != u2.Description ||
|
||||||
|
u1.LoadState != u2.LoadState ||
|
||||||
|
u1.ActiveState != u2.ActiveState ||
|
||||||
|
u1.SubState != u2.SubState
|
||||||
|
}
|
191
vendor/github.com/docker/go-units/LICENSE
generated
vendored
Normal file
191
vendor/github.com/docker/go-units/LICENSE
generated
vendored
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
https://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:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) 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
|
||||||
|
|
||||||
|
(d) 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
|
||||||
|
|
||||||
|
Copyright 2015 Docker, 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
|
||||||
|
|
||||||
|
https://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.
|
16
vendor/github.com/docker/go-units/README.md
generated
vendored
Normal file
16
vendor/github.com/docker/go-units/README.md
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
[](https://godoc.org/github.com/docker/go-units)
|
||||||
|
|
||||||
|
# Introduction
|
||||||
|
|
||||||
|
go-units is a library to transform human friendly measurements into machine friendly values.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
See the [docs in godoc](https://godoc.org/github.com/docker/go-units) for examples and documentation.
|
||||||
|
|
||||||
|
## Copyright and license
|
||||||
|
|
||||||
|
Copyright © 2015 Docker, Inc.
|
||||||
|
|
||||||
|
go-units is licensed under the Apache License, Version 2.0.
|
||||||
|
See [LICENSE](LICENSE) for the full text of the license.
|
35
vendor/github.com/docker/go-units/duration.go
generated
vendored
Normal file
35
vendor/github.com/docker/go-units/duration.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Package units provides helper function to parse and print size and time units
|
||||||
|
// in human-readable format.
|
||||||
|
package units
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HumanDuration returns a human-readable approximation of a duration
|
||||||
|
// (eg. "About a minute", "4 hours ago", etc.).
|
||||||
|
func HumanDuration(d time.Duration) string {
|
||||||
|
if seconds := int(d.Seconds()); seconds < 1 {
|
||||||
|
return "Less than a second"
|
||||||
|
} else if seconds == 1 {
|
||||||
|
return "1 second"
|
||||||
|
} else if seconds < 60 {
|
||||||
|
return fmt.Sprintf("%d seconds", seconds)
|
||||||
|
} else if minutes := int(d.Minutes()); minutes == 1 {
|
||||||
|
return "About a minute"
|
||||||
|
} else if minutes < 46 {
|
||||||
|
return fmt.Sprintf("%d minutes", minutes)
|
||||||
|
} else if hours := int(d.Hours() + 0.5); hours == 1 {
|
||||||
|
return "About an hour"
|
||||||
|
} else if hours < 48 {
|
||||||
|
return fmt.Sprintf("%d hours", hours)
|
||||||
|
} else if hours < 24*7*2 {
|
||||||
|
return fmt.Sprintf("%d days", hours/24)
|
||||||
|
} else if hours < 24*30*2 {
|
||||||
|
return fmt.Sprintf("%d weeks", hours/24/7)
|
||||||
|
} else if hours < 24*365*2 {
|
||||||
|
return fmt.Sprintf("%d months", hours/24/30)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%d years", int(d.Hours())/24/365)
|
||||||
|
}
|
108
vendor/github.com/docker/go-units/size.go
generated
vendored
Normal file
108
vendor/github.com/docker/go-units/size.go
generated
vendored
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
package units
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// See: http://en.wikipedia.org/wiki/Binary_prefix
|
||||||
|
const (
|
||||||
|
// Decimal
|
||||||
|
|
||||||
|
KB = 1000
|
||||||
|
MB = 1000 * KB
|
||||||
|
GB = 1000 * MB
|
||||||
|
TB = 1000 * GB
|
||||||
|
PB = 1000 * TB
|
||||||
|
|
||||||
|
// Binary
|
||||||
|
|
||||||
|
KiB = 1024
|
||||||
|
MiB = 1024 * KiB
|
||||||
|
GiB = 1024 * MiB
|
||||||
|
TiB = 1024 * GiB
|
||||||
|
PiB = 1024 * TiB
|
||||||
|
)
|
||||||
|
|
||||||
|
type unitMap map[string]int64
|
||||||
|
|
||||||
|
var (
|
||||||
|
decimalMap = unitMap{"k": KB, "m": MB, "g": GB, "t": TB, "p": PB}
|
||||||
|
binaryMap = unitMap{"k": KiB, "m": MiB, "g": GiB, "t": TiB, "p": PiB}
|
||||||
|
sizeRegex = regexp.MustCompile(`^(\d+(\.\d+)*) ?([kKmMgGtTpP])?[bB]?$`)
|
||||||
|
)
|
||||||
|
|
||||||
|
var decimapAbbrs = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
|
||||||
|
var binaryAbbrs = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"}
|
||||||
|
|
||||||
|
func getSizeAndUnit(size float64, base float64, _map []string) (float64, string) {
|
||||||
|
i := 0
|
||||||
|
unitsLimit := len(_map) - 1
|
||||||
|
for size >= base && i < unitsLimit {
|
||||||
|
size = size / base
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return size, _map[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// CustomSize returns a human-readable approximation of a size
|
||||||
|
// using custom format.
|
||||||
|
func CustomSize(format string, size float64, base float64, _map []string) string {
|
||||||
|
size, unit := getSizeAndUnit(size, base, _map)
|
||||||
|
return fmt.Sprintf(format, size, unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HumanSizeWithPrecision allows the size to be in any precision,
|
||||||
|
// instead of 4 digit precision used in units.HumanSize.
|
||||||
|
func HumanSizeWithPrecision(size float64, precision int) string {
|
||||||
|
size, unit := getSizeAndUnit(size, 1000.0, decimapAbbrs)
|
||||||
|
return fmt.Sprintf("%.*g%s", precision, size, unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HumanSize returns a human-readable approximation of a size
|
||||||
|
// capped at 4 valid numbers (eg. "2.746 MB", "796 KB").
|
||||||
|
func HumanSize(size float64) string {
|
||||||
|
return HumanSizeWithPrecision(size, 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesSize returns a human-readable size in bytes, kibibytes,
|
||||||
|
// mebibytes, gibibytes, or tebibytes (eg. "44kiB", "17MiB").
|
||||||
|
func BytesSize(size float64) string {
|
||||||
|
return CustomSize("%.4g%s", size, 1024.0, binaryAbbrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromHumanSize returns an integer from a human-readable specification of a
|
||||||
|
// size using SI standard (eg. "44kB", "17MB").
|
||||||
|
func FromHumanSize(size string) (int64, error) {
|
||||||
|
return parseSize(size, decimalMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RAMInBytes parses a human-readable string representing an amount of RAM
|
||||||
|
// in bytes, kibibytes, mebibytes, gibibytes, or tebibytes and
|
||||||
|
// returns the number of bytes, or -1 if the string is unparseable.
|
||||||
|
// Units are case-insensitive, and the 'b' suffix is optional.
|
||||||
|
func RAMInBytes(size string) (int64, error) {
|
||||||
|
return parseSize(size, binaryMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parses the human-readable size string into the amount it represents.
|
||||||
|
func parseSize(sizeStr string, uMap unitMap) (int64, error) {
|
||||||
|
matches := sizeRegex.FindStringSubmatch(sizeStr)
|
||||||
|
if len(matches) != 4 {
|
||||||
|
return -1, fmt.Errorf("invalid size: '%s'", sizeStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
size, err := strconv.ParseFloat(matches[1], 64)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
unitPrefix := strings.ToLower(matches[3])
|
||||||
|
if mul, ok := uMap[unitPrefix]; ok {
|
||||||
|
size *= float64(mul)
|
||||||
|
}
|
||||||
|
|
||||||
|
return int64(size), nil
|
||||||
|
}
|
118
vendor/github.com/docker/go-units/ulimit.go
generated
vendored
Normal file
118
vendor/github.com/docker/go-units/ulimit.go
generated
vendored
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
package units
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ulimit is a human friendly version of Rlimit.
|
||||||
|
type Ulimit struct {
|
||||||
|
Name string
|
||||||
|
Hard int64
|
||||||
|
Soft int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rlimit specifies the resource limits, such as max open files.
|
||||||
|
type Rlimit struct {
|
||||||
|
Type int `json:"type,omitempty"`
|
||||||
|
Hard uint64 `json:"hard,omitempty"`
|
||||||
|
Soft uint64 `json:"soft,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// magic numbers for making the syscall
|
||||||
|
// some of these are defined in the syscall package, but not all.
|
||||||
|
// Also since Windows client doesn't get access to the syscall package, need to
|
||||||
|
// define these here
|
||||||
|
rlimitAs = 9
|
||||||
|
rlimitCore = 4
|
||||||
|
rlimitCPU = 0
|
||||||
|
rlimitData = 2
|
||||||
|
rlimitFsize = 1
|
||||||
|
rlimitLocks = 10
|
||||||
|
rlimitMemlock = 8
|
||||||
|
rlimitMsgqueue = 12
|
||||||
|
rlimitNice = 13
|
||||||
|
rlimitNofile = 7
|
||||||
|
rlimitNproc = 6
|
||||||
|
rlimitRss = 5
|
||||||
|
rlimitRtprio = 14
|
||||||
|
rlimitRttime = 15
|
||||||
|
rlimitSigpending = 11
|
||||||
|
rlimitStack = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
var ulimitNameMapping = map[string]int{
|
||||||
|
//"as": rlimitAs, // Disabled since this doesn't seem usable with the way Docker inits a container.
|
||||||
|
"core": rlimitCore,
|
||||||
|
"cpu": rlimitCPU,
|
||||||
|
"data": rlimitData,
|
||||||
|
"fsize": rlimitFsize,
|
||||||
|
"locks": rlimitLocks,
|
||||||
|
"memlock": rlimitMemlock,
|
||||||
|
"msgqueue": rlimitMsgqueue,
|
||||||
|
"nice": rlimitNice,
|
||||||
|
"nofile": rlimitNofile,
|
||||||
|
"nproc": rlimitNproc,
|
||||||
|
"rss": rlimitRss,
|
||||||
|
"rtprio": rlimitRtprio,
|
||||||
|
"rttime": rlimitRttime,
|
||||||
|
"sigpending": rlimitSigpending,
|
||||||
|
"stack": rlimitStack,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseUlimit parses and returns a Ulimit from the specified string.
|
||||||
|
func ParseUlimit(val string) (*Ulimit, error) {
|
||||||
|
parts := strings.SplitN(val, "=", 2)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return nil, fmt.Errorf("invalid ulimit argument: %s", val)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, exists := ulimitNameMapping[parts[0]]; !exists {
|
||||||
|
return nil, fmt.Errorf("invalid ulimit type: %s", parts[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
soft int64
|
||||||
|
hard = &soft // default to soft in case no hard was set
|
||||||
|
temp int64
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
switch limitVals := strings.Split(parts[1], ":"); len(limitVals) {
|
||||||
|
case 2:
|
||||||
|
temp, err = strconv.ParseInt(limitVals[1], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hard = &temp
|
||||||
|
fallthrough
|
||||||
|
case 1:
|
||||||
|
soft, err = strconv.ParseInt(limitVals[0], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("too many limit value arguments - %s, can only have up to two, `soft[:hard]`", parts[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
if soft > *hard {
|
||||||
|
return nil, fmt.Errorf("ulimit soft limit must be less than or equal to hard limit: %d > %d", soft, *hard)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Ulimit{Name: parts[0], Soft: soft, Hard: *hard}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRlimit returns the RLimit corresponding to Ulimit.
|
||||||
|
func (u *Ulimit) GetRlimit() (*Rlimit, error) {
|
||||||
|
t, exists := ulimitNameMapping[u.Name]
|
||||||
|
if !exists {
|
||||||
|
return nil, fmt.Errorf("invalid ulimit name %s", u.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Rlimit{Type: t, Soft: uint64(u.Soft), Hard: uint64(u.Hard)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Ulimit) String() string {
|
||||||
|
return fmt.Sprintf("%s=%d:%d", u.Name, u.Soft, u.Hard)
|
||||||
|
}
|
25
vendor/github.com/godbus/dbus/LICENSE
generated
vendored
Normal file
25
vendor/github.com/godbus/dbus/LICENSE
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
Copyright (c) 2013, Georg Reinke (<guelfey at gmail dot com>), Google
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
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.
|
44
vendor/github.com/godbus/dbus/README.markdown
generated
vendored
Normal file
44
vendor/github.com/godbus/dbus/README.markdown
generated
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
[](https://travis-ci.org/godbus/dbus)
|
||||||
|
|
||||||
|
dbus
|
||||||
|
----
|
||||||
|
|
||||||
|
dbus is a simple library that implements native Go client bindings for the
|
||||||
|
D-Bus message bus system.
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Complete native implementation of the D-Bus message protocol
|
||||||
|
* Go-like API (channels for signals / asynchronous method calls, Goroutine-safe connections)
|
||||||
|
* Subpackages that help with the introspection / property interfaces
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
This packages requires Go 1.1. If you installed it and set up your GOPATH, just run:
|
||||||
|
|
||||||
|
```
|
||||||
|
go get github.com/godbus/dbus
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to use the subpackages, you can install them the same way.
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
The complete package documentation and some simple examples are available at
|
||||||
|
[godoc.org](http://godoc.org/github.com/godbus/dbus). Also, the
|
||||||
|
[_examples](https://github.com/godbus/dbus/tree/master/_examples) directory
|
||||||
|
gives a short overview over the basic usage.
|
||||||
|
|
||||||
|
#### Projects using godbus
|
||||||
|
- [notify](https://github.com/esiqveland/notify) provides desktop notifications over dbus into a library.
|
||||||
|
- [go-bluetooth](https://github.com/muka/go-bluetooth) provides a bluetooth client over bluez dbus API.
|
||||||
|
|
||||||
|
Please note that the API is considered unstable for now and may change without
|
||||||
|
further notice.
|
||||||
|
|
||||||
|
### License
|
||||||
|
|
||||||
|
go.dbus is available under the Simplified BSD License; see LICENSE for the full
|
||||||
|
text.
|
||||||
|
|
||||||
|
Nearly all of the credit for this library goes to github.com/guelfey/go.dbus.
|
253
vendor/github.com/godbus/dbus/auth.go
generated
vendored
Normal file
253
vendor/github.com/godbus/dbus/auth.go
generated
vendored
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AuthStatus represents the Status of an authentication mechanism.
|
||||||
|
type AuthStatus byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
// AuthOk signals that authentication is finished; the next command
|
||||||
|
// from the server should be an OK.
|
||||||
|
AuthOk AuthStatus = iota
|
||||||
|
|
||||||
|
// AuthContinue signals that additional data is needed; the next command
|
||||||
|
// from the server should be a DATA.
|
||||||
|
AuthContinue
|
||||||
|
|
||||||
|
// AuthError signals an error; the server sent invalid data or some
|
||||||
|
// other unexpected thing happened and the current authentication
|
||||||
|
// process should be aborted.
|
||||||
|
AuthError
|
||||||
|
)
|
||||||
|
|
||||||
|
type authState byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
waitingForData authState = iota
|
||||||
|
waitingForOk
|
||||||
|
waitingForReject
|
||||||
|
)
|
||||||
|
|
||||||
|
// Auth defines the behaviour of an authentication mechanism.
|
||||||
|
type Auth interface {
|
||||||
|
// Return the name of the mechnism, the argument to the first AUTH command
|
||||||
|
// and the next status.
|
||||||
|
FirstData() (name, resp []byte, status AuthStatus)
|
||||||
|
|
||||||
|
// Process the given DATA command, and return the argument to the DATA
|
||||||
|
// command and the next status. If len(resp) == 0, no DATA command is sent.
|
||||||
|
HandleData(data []byte) (resp []byte, status AuthStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auth authenticates the connection, trying the given list of authentication
|
||||||
|
// mechanisms (in that order). If nil is passed, the EXTERNAL and
|
||||||
|
// DBUS_COOKIE_SHA1 mechanisms are tried for the current user. For private
|
||||||
|
// connections, this method must be called before sending any messages to the
|
||||||
|
// bus. Auth must not be called on shared connections.
|
||||||
|
func (conn *Conn) Auth(methods []Auth) error {
|
||||||
|
if methods == nil {
|
||||||
|
uid := strconv.Itoa(os.Getuid())
|
||||||
|
methods = []Auth{AuthExternal(uid), AuthCookieSha1(uid, getHomeDir())}
|
||||||
|
}
|
||||||
|
in := bufio.NewReader(conn.transport)
|
||||||
|
err := conn.transport.SendNullByte()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = authWriteLine(conn.transport, []byte("AUTH"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s, err := authReadLine(in)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(s) < 2 || !bytes.Equal(s[0], []byte("REJECTED")) {
|
||||||
|
return errors.New("dbus: authentication protocol error")
|
||||||
|
}
|
||||||
|
s = s[1:]
|
||||||
|
for _, v := range s {
|
||||||
|
for _, m := range methods {
|
||||||
|
if name, data, status := m.FirstData(); bytes.Equal(v, name) {
|
||||||
|
var ok bool
|
||||||
|
err = authWriteLine(conn.transport, []byte("AUTH"), []byte(v), data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch status {
|
||||||
|
case AuthOk:
|
||||||
|
err, ok = conn.tryAuth(m, waitingForOk, in)
|
||||||
|
case AuthContinue:
|
||||||
|
err, ok = conn.tryAuth(m, waitingForData, in)
|
||||||
|
default:
|
||||||
|
panic("dbus: invalid authentication status")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
if conn.transport.SupportsUnixFDs() {
|
||||||
|
err = authWriteLine(conn, []byte("NEGOTIATE_UNIX_FD"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
line, err := authReadLine(in)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case bytes.Equal(line[0], []byte("AGREE_UNIX_FD")):
|
||||||
|
conn.EnableUnixFDs()
|
||||||
|
conn.unixFD = true
|
||||||
|
case bytes.Equal(line[0], []byte("ERROR")):
|
||||||
|
default:
|
||||||
|
return errors.New("dbus: authentication protocol error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = authWriteLine(conn.transport, []byte("BEGIN"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go conn.inWorker()
|
||||||
|
go conn.outWorker()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errors.New("dbus: authentication failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// tryAuth tries to authenticate with m as the mechanism, using state as the
|
||||||
|
// initial authState and in for reading input. It returns (nil, true) on
|
||||||
|
// success, (nil, false) on a REJECTED and (someErr, false) if some other
|
||||||
|
// error occured.
|
||||||
|
func (conn *Conn) tryAuth(m Auth, state authState, in *bufio.Reader) (error, bool) {
|
||||||
|
for {
|
||||||
|
s, err := authReadLine(in)
|
||||||
|
if err != nil {
|
||||||
|
return err, false
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case state == waitingForData && string(s[0]) == "DATA":
|
||||||
|
if len(s) != 2 {
|
||||||
|
err = authWriteLine(conn.transport, []byte("ERROR"))
|
||||||
|
if err != nil {
|
||||||
|
return err, false
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
data, status := m.HandleData(s[1])
|
||||||
|
switch status {
|
||||||
|
case AuthOk, AuthContinue:
|
||||||
|
if len(data) != 0 {
|
||||||
|
err = authWriteLine(conn.transport, []byte("DATA"), data)
|
||||||
|
if err != nil {
|
||||||
|
return err, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if status == AuthOk {
|
||||||
|
state = waitingForOk
|
||||||
|
}
|
||||||
|
case AuthError:
|
||||||
|
err = authWriteLine(conn.transport, []byte("ERROR"))
|
||||||
|
if err != nil {
|
||||||
|
return err, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case state == waitingForData && string(s[0]) == "REJECTED":
|
||||||
|
return nil, false
|
||||||
|
case state == waitingForData && string(s[0]) == "ERROR":
|
||||||
|
err = authWriteLine(conn.transport, []byte("CANCEL"))
|
||||||
|
if err != nil {
|
||||||
|
return err, false
|
||||||
|
}
|
||||||
|
state = waitingForReject
|
||||||
|
case state == waitingForData && string(s[0]) == "OK":
|
||||||
|
if len(s) != 2 {
|
||||||
|
err = authWriteLine(conn.transport, []byte("CANCEL"))
|
||||||
|
if err != nil {
|
||||||
|
return err, false
|
||||||
|
}
|
||||||
|
state = waitingForReject
|
||||||
|
}
|
||||||
|
conn.uuid = string(s[1])
|
||||||
|
return nil, true
|
||||||
|
case state == waitingForData:
|
||||||
|
err = authWriteLine(conn.transport, []byte("ERROR"))
|
||||||
|
if err != nil {
|
||||||
|
return err, false
|
||||||
|
}
|
||||||
|
case state == waitingForOk && string(s[0]) == "OK":
|
||||||
|
if len(s) != 2 {
|
||||||
|
err = authWriteLine(conn.transport, []byte("CANCEL"))
|
||||||
|
if err != nil {
|
||||||
|
return err, false
|
||||||
|
}
|
||||||
|
state = waitingForReject
|
||||||
|
}
|
||||||
|
conn.uuid = string(s[1])
|
||||||
|
return nil, true
|
||||||
|
case state == waitingForOk && string(s[0]) == "REJECTED":
|
||||||
|
return nil, false
|
||||||
|
case state == waitingForOk && (string(s[0]) == "DATA" ||
|
||||||
|
string(s[0]) == "ERROR"):
|
||||||
|
|
||||||
|
err = authWriteLine(conn.transport, []byte("CANCEL"))
|
||||||
|
if err != nil {
|
||||||
|
return err, false
|
||||||
|
}
|
||||||
|
state = waitingForReject
|
||||||
|
case state == waitingForOk:
|
||||||
|
err = authWriteLine(conn.transport, []byte("ERROR"))
|
||||||
|
if err != nil {
|
||||||
|
return err, false
|
||||||
|
}
|
||||||
|
case state == waitingForReject && string(s[0]) == "REJECTED":
|
||||||
|
return nil, false
|
||||||
|
case state == waitingForReject:
|
||||||
|
return errors.New("dbus: authentication protocol error"), false
|
||||||
|
default:
|
||||||
|
panic("dbus: invalid auth state")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// authReadLine reads a line and separates it into its fields.
|
||||||
|
func authReadLine(in *bufio.Reader) ([][]byte, error) {
|
||||||
|
data, err := in.ReadBytes('\n')
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
data = bytes.TrimSuffix(data, []byte("\r\n"))
|
||||||
|
return bytes.Split(data, []byte{' '}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// authWriteLine writes the given line in the authentication protocol format
|
||||||
|
// (elements of data separated by a " " and terminated by "\r\n").
|
||||||
|
func authWriteLine(out io.Writer, data ...[]byte) error {
|
||||||
|
buf := make([]byte, 0)
|
||||||
|
for i, v := range data {
|
||||||
|
buf = append(buf, v...)
|
||||||
|
if i != len(data)-1 {
|
||||||
|
buf = append(buf, ' ')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf = append(buf, '\r')
|
||||||
|
buf = append(buf, '\n')
|
||||||
|
n, err := out.Write(buf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if n != len(buf) {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
26
vendor/github.com/godbus/dbus/auth_external.go
generated
vendored
Normal file
26
vendor/github.com/godbus/dbus/auth_external.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AuthExternal returns an Auth that authenticates as the given user with the
|
||||||
|
// EXTERNAL mechanism.
|
||||||
|
func AuthExternal(user string) Auth {
|
||||||
|
return authExternal{user}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthExternal implements the EXTERNAL authentication mechanism.
|
||||||
|
type authExternal struct {
|
||||||
|
user string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a authExternal) FirstData() ([]byte, []byte, AuthStatus) {
|
||||||
|
b := make([]byte, 2*len(a.user))
|
||||||
|
hex.Encode(b, []byte(a.user))
|
||||||
|
return []byte("EXTERNAL"), b, AuthOk
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a authExternal) HandleData(b []byte) ([]byte, AuthStatus) {
|
||||||
|
return nil, AuthError
|
||||||
|
}
|
102
vendor/github.com/godbus/dbus/auth_sha1.go
generated
vendored
Normal file
102
vendor/github.com/godbus/dbus/auth_sha1.go
generated
vendored
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding/hex"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AuthCookieSha1 returns an Auth that authenticates as the given user with the
|
||||||
|
// DBUS_COOKIE_SHA1 mechanism. The home parameter should specify the home
|
||||||
|
// directory of the user.
|
||||||
|
func AuthCookieSha1(user, home string) Auth {
|
||||||
|
return authCookieSha1{user, home}
|
||||||
|
}
|
||||||
|
|
||||||
|
type authCookieSha1 struct {
|
||||||
|
user, home string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a authCookieSha1) FirstData() ([]byte, []byte, AuthStatus) {
|
||||||
|
b := make([]byte, 2*len(a.user))
|
||||||
|
hex.Encode(b, []byte(a.user))
|
||||||
|
return []byte("DBUS_COOKIE_SHA1"), b, AuthContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a authCookieSha1) HandleData(data []byte) ([]byte, AuthStatus) {
|
||||||
|
challenge := make([]byte, len(data)/2)
|
||||||
|
_, err := hex.Decode(challenge, data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, AuthError
|
||||||
|
}
|
||||||
|
b := bytes.Split(challenge, []byte{' '})
|
||||||
|
if len(b) != 3 {
|
||||||
|
return nil, AuthError
|
||||||
|
}
|
||||||
|
context := b[0]
|
||||||
|
id := b[1]
|
||||||
|
svchallenge := b[2]
|
||||||
|
cookie := a.getCookie(context, id)
|
||||||
|
if cookie == nil {
|
||||||
|
return nil, AuthError
|
||||||
|
}
|
||||||
|
clchallenge := a.generateChallenge()
|
||||||
|
if clchallenge == nil {
|
||||||
|
return nil, AuthError
|
||||||
|
}
|
||||||
|
hash := sha1.New()
|
||||||
|
hash.Write(bytes.Join([][]byte{svchallenge, clchallenge, cookie}, []byte{':'}))
|
||||||
|
hexhash := make([]byte, 2*hash.Size())
|
||||||
|
hex.Encode(hexhash, hash.Sum(nil))
|
||||||
|
data = append(clchallenge, ' ')
|
||||||
|
data = append(data, hexhash...)
|
||||||
|
resp := make([]byte, 2*len(data))
|
||||||
|
hex.Encode(resp, data)
|
||||||
|
return resp, AuthOk
|
||||||
|
}
|
||||||
|
|
||||||
|
// getCookie searches for the cookie identified by id in context and returns
|
||||||
|
// the cookie content or nil. (Since HandleData can't return a specific error,
|
||||||
|
// but only whether an error occured, this function also doesn't bother to
|
||||||
|
// return an error.)
|
||||||
|
func (a authCookieSha1) getCookie(context, id []byte) []byte {
|
||||||
|
file, err := os.Open(a.home + "/.dbus-keyrings/" + string(context))
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
rd := bufio.NewReader(file)
|
||||||
|
for {
|
||||||
|
line, err := rd.ReadBytes('\n')
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
line = line[:len(line)-1]
|
||||||
|
b := bytes.Split(line, []byte{' '})
|
||||||
|
if len(b) != 3 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if bytes.Equal(b[0], id) {
|
||||||
|
return b[2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateChallenge returns a random, hex-encoded challenge, or nil on error
|
||||||
|
// (see above).
|
||||||
|
func (a authCookieSha1) generateChallenge() []byte {
|
||||||
|
b := make([]byte, 16)
|
||||||
|
n, err := rand.Read(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if n != 16 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
enc := make([]byte, 32)
|
||||||
|
hex.Encode(enc, b)
|
||||||
|
return enc
|
||||||
|
}
|
36
vendor/github.com/godbus/dbus/call.go
generated
vendored
Normal file
36
vendor/github.com/godbus/dbus/call.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Call represents a pending or completed method call.
|
||||||
|
type Call struct {
|
||||||
|
Destination string
|
||||||
|
Path ObjectPath
|
||||||
|
Method string
|
||||||
|
Args []interface{}
|
||||||
|
|
||||||
|
// Strobes when the call is complete.
|
||||||
|
Done chan *Call
|
||||||
|
|
||||||
|
// After completion, the error status. If this is non-nil, it may be an
|
||||||
|
// error message from the peer (with Error as its type) or some other error.
|
||||||
|
Err error
|
||||||
|
|
||||||
|
// Holds the response once the call is done.
|
||||||
|
Body []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var errSignature = errors.New("dbus: mismatched signature")
|
||||||
|
|
||||||
|
// Store stores the body of the reply into the provided pointers. It returns
|
||||||
|
// an error if the signatures of the body and retvalues don't match, or if
|
||||||
|
// the error status is not nil.
|
||||||
|
func (c *Call) Store(retvalues ...interface{}) error {
|
||||||
|
if c.Err != nil {
|
||||||
|
return c.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
return Store(c.Body, retvalues...)
|
||||||
|
}
|
683
vendor/github.com/godbus/dbus/conn.go
generated
vendored
Normal file
683
vendor/github.com/godbus/dbus/conn.go
generated
vendored
Normal file
@ -0,0 +1,683 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
systemBus *Conn
|
||||||
|
systemBusLck sync.Mutex
|
||||||
|
sessionBus *Conn
|
||||||
|
sessionBusLck sync.Mutex
|
||||||
|
sessionEnvLck sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrClosed is the error returned by calls on a closed connection.
|
||||||
|
var ErrClosed = errors.New("dbus: connection closed by user")
|
||||||
|
|
||||||
|
// Conn represents a connection to a message bus (usually, the system or
|
||||||
|
// session bus).
|
||||||
|
//
|
||||||
|
// Connections are either shared or private. Shared connections
|
||||||
|
// are shared between calls to the functions that return them. As a result,
|
||||||
|
// the methods Close, Auth and Hello must not be called on them.
|
||||||
|
//
|
||||||
|
// Multiple goroutines may invoke methods on a connection simultaneously.
|
||||||
|
type Conn struct {
|
||||||
|
transport
|
||||||
|
|
||||||
|
busObj BusObject
|
||||||
|
unixFD bool
|
||||||
|
uuid string
|
||||||
|
|
||||||
|
names []string
|
||||||
|
namesLck sync.RWMutex
|
||||||
|
|
||||||
|
serialLck sync.Mutex
|
||||||
|
nextSerial uint32
|
||||||
|
serialUsed map[uint32]bool
|
||||||
|
|
||||||
|
calls map[uint32]*Call
|
||||||
|
callsLck sync.RWMutex
|
||||||
|
|
||||||
|
handler Handler
|
||||||
|
|
||||||
|
out chan *Message
|
||||||
|
closed bool
|
||||||
|
outLck sync.RWMutex
|
||||||
|
|
||||||
|
signalHandler SignalHandler
|
||||||
|
|
||||||
|
eavesdropped chan<- *Message
|
||||||
|
eavesdroppedLck sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionBus returns a shared connection to the session bus, connecting to it
|
||||||
|
// if not already done.
|
||||||
|
func SessionBus() (conn *Conn, err error) {
|
||||||
|
sessionBusLck.Lock()
|
||||||
|
defer sessionBusLck.Unlock()
|
||||||
|
if sessionBus != nil {
|
||||||
|
return sessionBus, nil
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if conn != nil {
|
||||||
|
sessionBus = conn
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
conn, err = SessionBusPrivate()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = conn.Auth(nil); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
conn = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = conn.Hello(); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
conn = nil
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSessionBusAddress() (string, error) {
|
||||||
|
sessionEnvLck.Lock()
|
||||||
|
defer sessionEnvLck.Unlock()
|
||||||
|
address := os.Getenv("DBUS_SESSION_BUS_ADDRESS")
|
||||||
|
if address != "" && address != "autolaunch:" {
|
||||||
|
return address, nil
|
||||||
|
}
|
||||||
|
return getSessionBusPlatformAddress()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionBusPrivate returns a new private connection to the session bus.
|
||||||
|
func SessionBusPrivate() (*Conn, error) {
|
||||||
|
address, err := getSessionBusAddress()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return Dial(address)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionBusPrivate returns a new private connection to the session bus.
|
||||||
|
func SessionBusPrivateHandler(handler Handler, signalHandler SignalHandler) (*Conn, error) {
|
||||||
|
address, err := getSessionBusAddress()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return DialHandler(address, handler, signalHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SystemBus returns a shared connection to the system bus, connecting to it if
|
||||||
|
// not already done.
|
||||||
|
func SystemBus() (conn *Conn, err error) {
|
||||||
|
systemBusLck.Lock()
|
||||||
|
defer systemBusLck.Unlock()
|
||||||
|
if systemBus != nil {
|
||||||
|
return systemBus, nil
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if conn != nil {
|
||||||
|
systemBus = conn
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
conn, err = SystemBusPrivate()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = conn.Auth(nil); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
conn = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = conn.Hello(); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
conn = nil
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SystemBusPrivate returns a new private connection to the system bus.
|
||||||
|
func SystemBusPrivate() (*Conn, error) {
|
||||||
|
return Dial(getSystemBusPlatformAddress())
|
||||||
|
}
|
||||||
|
|
||||||
|
// SystemBusPrivateHandler returns a new private connection to the system bus, using the provided handlers.
|
||||||
|
func SystemBusPrivateHandler(handler Handler, signalHandler SignalHandler) (*Conn, error) {
|
||||||
|
return DialHandler(getSystemBusPlatformAddress(), handler, signalHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dial establishes a new private connection to the message bus specified by address.
|
||||||
|
func Dial(address string) (*Conn, error) {
|
||||||
|
tr, err := getTransport(address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return newConn(tr, newDefaultHandler(), newDefaultSignalHandler())
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialHandler establishes a new private connection to the message bus specified by address, using the supplied handlers.
|
||||||
|
func DialHandler(address string, handler Handler, signalHandler SignalHandler) (*Conn, error) {
|
||||||
|
tr, err := getTransport(address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return newConn(tr, handler, signalHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConn creates a new private *Conn from an already established connection.
|
||||||
|
func NewConn(conn io.ReadWriteCloser) (*Conn, error) {
|
||||||
|
return NewConnHandler(conn, newDefaultHandler(), newDefaultSignalHandler())
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConnHandler creates a new private *Conn from an already established connection, using the supplied handlers.
|
||||||
|
func NewConnHandler(conn io.ReadWriteCloser, handler Handler, signalHandler SignalHandler) (*Conn, error) {
|
||||||
|
return newConn(genericTransport{conn}, handler, signalHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// newConn creates a new *Conn from a transport.
|
||||||
|
func newConn(tr transport, handler Handler, signalHandler SignalHandler) (*Conn, error) {
|
||||||
|
conn := new(Conn)
|
||||||
|
conn.transport = tr
|
||||||
|
conn.calls = make(map[uint32]*Call)
|
||||||
|
conn.out = make(chan *Message, 10)
|
||||||
|
conn.handler = handler
|
||||||
|
conn.signalHandler = signalHandler
|
||||||
|
conn.nextSerial = 1
|
||||||
|
conn.serialUsed = map[uint32]bool{0: true}
|
||||||
|
conn.busObj = conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus")
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BusObject returns the object owned by the bus daemon which handles
|
||||||
|
// administrative requests.
|
||||||
|
func (conn *Conn) BusObject() BusObject {
|
||||||
|
return conn.busObj
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the connection. Any blocked operations will return with errors
|
||||||
|
// and the channels passed to Eavesdrop and Signal are closed. This method must
|
||||||
|
// not be called on shared connections.
|
||||||
|
func (conn *Conn) Close() error {
|
||||||
|
conn.outLck.Lock()
|
||||||
|
if conn.closed {
|
||||||
|
// inWorker calls Close on read error, the read error may
|
||||||
|
// be caused by another caller calling Close to shutdown the
|
||||||
|
// dbus connection, a double-close scenario we prevent here.
|
||||||
|
conn.outLck.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
close(conn.out)
|
||||||
|
conn.closed = true
|
||||||
|
conn.outLck.Unlock()
|
||||||
|
|
||||||
|
if term, ok := conn.signalHandler.(Terminator); ok {
|
||||||
|
term.Terminate()
|
||||||
|
}
|
||||||
|
|
||||||
|
if term, ok := conn.handler.(Terminator); ok {
|
||||||
|
term.Terminate()
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.eavesdroppedLck.Lock()
|
||||||
|
if conn.eavesdropped != nil {
|
||||||
|
close(conn.eavesdropped)
|
||||||
|
}
|
||||||
|
conn.eavesdroppedLck.Unlock()
|
||||||
|
|
||||||
|
return conn.transport.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eavesdrop causes conn to send all incoming messages to the given channel
|
||||||
|
// without further processing. Method replies, errors and signals will not be
|
||||||
|
// sent to the appropiate channels and method calls will not be handled. If nil
|
||||||
|
// is passed, the normal behaviour is restored.
|
||||||
|
//
|
||||||
|
// The caller has to make sure that ch is sufficiently buffered;
|
||||||
|
// if a message arrives when a write to ch is not possible, the message is
|
||||||
|
// discarded.
|
||||||
|
func (conn *Conn) Eavesdrop(ch chan<- *Message) {
|
||||||
|
conn.eavesdroppedLck.Lock()
|
||||||
|
conn.eavesdropped = ch
|
||||||
|
conn.eavesdroppedLck.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSerial returns an unused serial.
|
||||||
|
func (conn *Conn) getSerial() uint32 {
|
||||||
|
conn.serialLck.Lock()
|
||||||
|
defer conn.serialLck.Unlock()
|
||||||
|
n := conn.nextSerial
|
||||||
|
for conn.serialUsed[n] {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
conn.serialUsed[n] = true
|
||||||
|
conn.nextSerial = n + 1
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hello sends the initial org.freedesktop.DBus.Hello call. This method must be
|
||||||
|
// called after authentication, but before sending any other messages to the
|
||||||
|
// bus. Hello must not be called for shared connections.
|
||||||
|
func (conn *Conn) Hello() error {
|
||||||
|
var s string
|
||||||
|
err := conn.busObj.Call("org.freedesktop.DBus.Hello", 0).Store(&s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
conn.namesLck.Lock()
|
||||||
|
conn.names = make([]string, 1)
|
||||||
|
conn.names[0] = s
|
||||||
|
conn.namesLck.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// inWorker runs in an own goroutine, reading incoming messages from the
|
||||||
|
// transport and dispatching them appropiately.
|
||||||
|
func (conn *Conn) inWorker() {
|
||||||
|
for {
|
||||||
|
msg, err := conn.ReadMessage()
|
||||||
|
if err == nil {
|
||||||
|
conn.eavesdroppedLck.Lock()
|
||||||
|
if conn.eavesdropped != nil {
|
||||||
|
select {
|
||||||
|
case conn.eavesdropped <- msg:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
conn.eavesdroppedLck.Unlock()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
conn.eavesdroppedLck.Unlock()
|
||||||
|
dest, _ := msg.Headers[FieldDestination].value.(string)
|
||||||
|
found := false
|
||||||
|
if dest == "" {
|
||||||
|
found = true
|
||||||
|
} else {
|
||||||
|
conn.namesLck.RLock()
|
||||||
|
if len(conn.names) == 0 {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
for _, v := range conn.names {
|
||||||
|
if dest == v {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conn.namesLck.RUnlock()
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
// Eavesdropped a message, but no channel for it is registered.
|
||||||
|
// Ignore it.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch msg.Type {
|
||||||
|
case TypeMethodReply, TypeError:
|
||||||
|
serial := msg.Headers[FieldReplySerial].value.(uint32)
|
||||||
|
conn.callsLck.Lock()
|
||||||
|
if c, ok := conn.calls[serial]; ok {
|
||||||
|
if msg.Type == TypeError {
|
||||||
|
name, _ := msg.Headers[FieldErrorName].value.(string)
|
||||||
|
c.Err = Error{name, msg.Body}
|
||||||
|
} else {
|
||||||
|
c.Body = msg.Body
|
||||||
|
}
|
||||||
|
c.Done <- c
|
||||||
|
conn.serialLck.Lock()
|
||||||
|
delete(conn.serialUsed, serial)
|
||||||
|
conn.serialLck.Unlock()
|
||||||
|
delete(conn.calls, serial)
|
||||||
|
}
|
||||||
|
conn.callsLck.Unlock()
|
||||||
|
case TypeSignal:
|
||||||
|
iface := msg.Headers[FieldInterface].value.(string)
|
||||||
|
member := msg.Headers[FieldMember].value.(string)
|
||||||
|
// as per http://dbus.freedesktop.org/doc/dbus-specification.html ,
|
||||||
|
// sender is optional for signals.
|
||||||
|
sender, _ := msg.Headers[FieldSender].value.(string)
|
||||||
|
if iface == "org.freedesktop.DBus" && sender == "org.freedesktop.DBus" {
|
||||||
|
if member == "NameLost" {
|
||||||
|
// If we lost the name on the bus, remove it from our
|
||||||
|
// tracking list.
|
||||||
|
name, ok := msg.Body[0].(string)
|
||||||
|
if !ok {
|
||||||
|
panic("Unable to read the lost name")
|
||||||
|
}
|
||||||
|
conn.namesLck.Lock()
|
||||||
|
for i, v := range conn.names {
|
||||||
|
if v == name {
|
||||||
|
conn.names = append(conn.names[:i],
|
||||||
|
conn.names[i+1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conn.namesLck.Unlock()
|
||||||
|
} else if member == "NameAcquired" {
|
||||||
|
// If we acquired the name on the bus, add it to our
|
||||||
|
// tracking list.
|
||||||
|
name, ok := msg.Body[0].(string)
|
||||||
|
if !ok {
|
||||||
|
panic("Unable to read the acquired name")
|
||||||
|
}
|
||||||
|
conn.namesLck.Lock()
|
||||||
|
conn.names = append(conn.names, name)
|
||||||
|
conn.namesLck.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
go conn.handleSignal(msg)
|
||||||
|
case TypeMethodCall:
|
||||||
|
go conn.handleCall(msg)
|
||||||
|
}
|
||||||
|
} else if _, ok := err.(InvalidMessageError); !ok {
|
||||||
|
// Some read error occured (usually EOF); we can't really do
|
||||||
|
// anything but to shut down all stuff and returns errors to all
|
||||||
|
// pending replies.
|
||||||
|
conn.Close()
|
||||||
|
conn.callsLck.RLock()
|
||||||
|
for _, v := range conn.calls {
|
||||||
|
v.Err = err
|
||||||
|
v.Done <- v
|
||||||
|
}
|
||||||
|
conn.callsLck.RUnlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// invalid messages are ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) handleSignal(msg *Message) {
|
||||||
|
iface := msg.Headers[FieldInterface].value.(string)
|
||||||
|
member := msg.Headers[FieldMember].value.(string)
|
||||||
|
// as per http://dbus.freedesktop.org/doc/dbus-specification.html ,
|
||||||
|
// sender is optional for signals.
|
||||||
|
sender, _ := msg.Headers[FieldSender].value.(string)
|
||||||
|
signal := &Signal{
|
||||||
|
Sender: sender,
|
||||||
|
Path: msg.Headers[FieldPath].value.(ObjectPath),
|
||||||
|
Name: iface + "." + member,
|
||||||
|
Body: msg.Body,
|
||||||
|
}
|
||||||
|
conn.signalHandler.DeliverSignal(iface, member, signal)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Names returns the list of all names that are currently owned by this
|
||||||
|
// connection. The slice is always at least one element long, the first element
|
||||||
|
// being the unique name of the connection.
|
||||||
|
func (conn *Conn) Names() []string {
|
||||||
|
conn.namesLck.RLock()
|
||||||
|
// copy the slice so it can't be modified
|
||||||
|
s := make([]string, len(conn.names))
|
||||||
|
copy(s, conn.names)
|
||||||
|
conn.namesLck.RUnlock()
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Object returns the object identified by the given destination name and path.
|
||||||
|
func (conn *Conn) Object(dest string, path ObjectPath) BusObject {
|
||||||
|
return &Object{conn, dest, path}
|
||||||
|
}
|
||||||
|
|
||||||
|
// outWorker runs in an own goroutine, encoding and sending messages that are
|
||||||
|
// sent to conn.out.
|
||||||
|
func (conn *Conn) outWorker() {
|
||||||
|
for msg := range conn.out {
|
||||||
|
err := conn.SendMessage(msg)
|
||||||
|
conn.callsLck.RLock()
|
||||||
|
if err != nil {
|
||||||
|
if c := conn.calls[msg.serial]; c != nil {
|
||||||
|
c.Err = err
|
||||||
|
c.Done <- c
|
||||||
|
}
|
||||||
|
conn.serialLck.Lock()
|
||||||
|
delete(conn.serialUsed, msg.serial)
|
||||||
|
conn.serialLck.Unlock()
|
||||||
|
} else if msg.Type != TypeMethodCall {
|
||||||
|
conn.serialLck.Lock()
|
||||||
|
delete(conn.serialUsed, msg.serial)
|
||||||
|
conn.serialLck.Unlock()
|
||||||
|
}
|
||||||
|
conn.callsLck.RUnlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send sends the given message to the message bus. You usually don't need to
|
||||||
|
// use this; use the higher-level equivalents (Call / Go, Emit and Export)
|
||||||
|
// instead. If msg is a method call and NoReplyExpected is not set, a non-nil
|
||||||
|
// call is returned and the same value is sent to ch (which must be buffered)
|
||||||
|
// once the call is complete. Otherwise, ch is ignored and a Call structure is
|
||||||
|
// returned of which only the Err member is valid.
|
||||||
|
func (conn *Conn) Send(msg *Message, ch chan *Call) *Call {
|
||||||
|
var call *Call
|
||||||
|
|
||||||
|
msg.serial = conn.getSerial()
|
||||||
|
if msg.Type == TypeMethodCall && msg.Flags&FlagNoReplyExpected == 0 {
|
||||||
|
if ch == nil {
|
||||||
|
ch = make(chan *Call, 5)
|
||||||
|
} else if cap(ch) == 0 {
|
||||||
|
panic("dbus: unbuffered channel passed to (*Conn).Send")
|
||||||
|
}
|
||||||
|
call = new(Call)
|
||||||
|
call.Destination, _ = msg.Headers[FieldDestination].value.(string)
|
||||||
|
call.Path, _ = msg.Headers[FieldPath].value.(ObjectPath)
|
||||||
|
iface, _ := msg.Headers[FieldInterface].value.(string)
|
||||||
|
member, _ := msg.Headers[FieldMember].value.(string)
|
||||||
|
call.Method = iface + "." + member
|
||||||
|
call.Args = msg.Body
|
||||||
|
call.Done = ch
|
||||||
|
conn.callsLck.Lock()
|
||||||
|
conn.calls[msg.serial] = call
|
||||||
|
conn.callsLck.Unlock()
|
||||||
|
conn.outLck.RLock()
|
||||||
|
if conn.closed {
|
||||||
|
call.Err = ErrClosed
|
||||||
|
call.Done <- call
|
||||||
|
} else {
|
||||||
|
conn.out <- msg
|
||||||
|
}
|
||||||
|
conn.outLck.RUnlock()
|
||||||
|
} else {
|
||||||
|
conn.outLck.RLock()
|
||||||
|
if conn.closed {
|
||||||
|
call = &Call{Err: ErrClosed}
|
||||||
|
} else {
|
||||||
|
conn.out <- msg
|
||||||
|
call = &Call{Err: nil}
|
||||||
|
}
|
||||||
|
conn.outLck.RUnlock()
|
||||||
|
}
|
||||||
|
return call
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendError creates an error message corresponding to the parameters and sends
|
||||||
|
// it to conn.out.
|
||||||
|
func (conn *Conn) sendError(err error, dest string, serial uint32) {
|
||||||
|
var e *Error
|
||||||
|
switch em := err.(type) {
|
||||||
|
case Error:
|
||||||
|
e = &em
|
||||||
|
case *Error:
|
||||||
|
e = em
|
||||||
|
case DBusError:
|
||||||
|
name, body := em.DBusError()
|
||||||
|
e = NewError(name, body)
|
||||||
|
default:
|
||||||
|
e = MakeFailedError(err)
|
||||||
|
}
|
||||||
|
msg := new(Message)
|
||||||
|
msg.Type = TypeError
|
||||||
|
msg.serial = conn.getSerial()
|
||||||
|
msg.Headers = make(map[HeaderField]Variant)
|
||||||
|
if dest != "" {
|
||||||
|
msg.Headers[FieldDestination] = MakeVariant(dest)
|
||||||
|
}
|
||||||
|
msg.Headers[FieldErrorName] = MakeVariant(e.Name)
|
||||||
|
msg.Headers[FieldReplySerial] = MakeVariant(serial)
|
||||||
|
msg.Body = e.Body
|
||||||
|
if len(e.Body) > 0 {
|
||||||
|
msg.Headers[FieldSignature] = MakeVariant(SignatureOf(e.Body...))
|
||||||
|
}
|
||||||
|
conn.outLck.RLock()
|
||||||
|
if !conn.closed {
|
||||||
|
conn.out <- msg
|
||||||
|
}
|
||||||
|
conn.outLck.RUnlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendReply creates a method reply message corresponding to the parameters and
|
||||||
|
// sends it to conn.out.
|
||||||
|
func (conn *Conn) sendReply(dest string, serial uint32, values ...interface{}) {
|
||||||
|
msg := new(Message)
|
||||||
|
msg.Type = TypeMethodReply
|
||||||
|
msg.serial = conn.getSerial()
|
||||||
|
msg.Headers = make(map[HeaderField]Variant)
|
||||||
|
if dest != "" {
|
||||||
|
msg.Headers[FieldDestination] = MakeVariant(dest)
|
||||||
|
}
|
||||||
|
msg.Headers[FieldReplySerial] = MakeVariant(serial)
|
||||||
|
msg.Body = values
|
||||||
|
if len(values) > 0 {
|
||||||
|
msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...))
|
||||||
|
}
|
||||||
|
conn.outLck.RLock()
|
||||||
|
if !conn.closed {
|
||||||
|
conn.out <- msg
|
||||||
|
}
|
||||||
|
conn.outLck.RUnlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) defaultSignalAction(fn func(h *defaultSignalHandler, ch chan<- *Signal), ch chan<- *Signal) {
|
||||||
|
if !isDefaultSignalHandler(conn.signalHandler) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
handler := conn.signalHandler.(*defaultSignalHandler)
|
||||||
|
fn(handler, ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signal registers the given channel to be passed all received signal messages.
|
||||||
|
// The caller has to make sure that ch is sufficiently buffered; if a message
|
||||||
|
// arrives when a write to c is not possible, it is discarded.
|
||||||
|
//
|
||||||
|
// Multiple of these channels can be registered at the same time.
|
||||||
|
//
|
||||||
|
// These channels are "overwritten" by Eavesdrop; i.e., if there currently is a
|
||||||
|
// channel for eavesdropped messages, this channel receives all signals, and
|
||||||
|
// none of the channels passed to Signal will receive any signals.
|
||||||
|
func (conn *Conn) Signal(ch chan<- *Signal) {
|
||||||
|
conn.defaultSignalAction((*defaultSignalHandler).addSignal, ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveSignal removes the given channel from the list of the registered channels.
|
||||||
|
func (conn *Conn) RemoveSignal(ch chan<- *Signal) {
|
||||||
|
conn.defaultSignalAction((*defaultSignalHandler).removeSignal, ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SupportsUnixFDs returns whether the underlying transport supports passing of
|
||||||
|
// unix file descriptors. If this is false, method calls containing unix file
|
||||||
|
// descriptors will return an error and emitted signals containing them will
|
||||||
|
// not be sent.
|
||||||
|
func (conn *Conn) SupportsUnixFDs() bool {
|
||||||
|
return conn.unixFD
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error represents a D-Bus message of type Error.
|
||||||
|
type Error struct {
|
||||||
|
Name string
|
||||||
|
Body []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewError(name string, body []interface{}) *Error {
|
||||||
|
return &Error{name, body}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e Error) Error() string {
|
||||||
|
if len(e.Body) >= 1 {
|
||||||
|
s, ok := e.Body[0].(string)
|
||||||
|
if ok {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return e.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signal represents a D-Bus message of type Signal. The name member is given in
|
||||||
|
// "interface.member" notation, e.g. org.freedesktop.D-Bus.NameLost.
|
||||||
|
type Signal struct {
|
||||||
|
Sender string
|
||||||
|
Path ObjectPath
|
||||||
|
Name string
|
||||||
|
Body []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// transport is a D-Bus transport.
|
||||||
|
type transport interface {
|
||||||
|
// Read and Write raw data (for example, for the authentication protocol).
|
||||||
|
io.ReadWriteCloser
|
||||||
|
|
||||||
|
// Send the initial null byte used for the EXTERNAL mechanism.
|
||||||
|
SendNullByte() error
|
||||||
|
|
||||||
|
// Returns whether this transport supports passing Unix FDs.
|
||||||
|
SupportsUnixFDs() bool
|
||||||
|
|
||||||
|
// Signal the transport that Unix FD passing is enabled for this connection.
|
||||||
|
EnableUnixFDs()
|
||||||
|
|
||||||
|
// Read / send a message, handling things like Unix FDs.
|
||||||
|
ReadMessage() (*Message, error)
|
||||||
|
SendMessage(*Message) error
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
transports = make(map[string]func(string) (transport, error))
|
||||||
|
)
|
||||||
|
|
||||||
|
func getTransport(address string) (transport, error) {
|
||||||
|
var err error
|
||||||
|
var t transport
|
||||||
|
|
||||||
|
addresses := strings.Split(address, ";")
|
||||||
|
for _, v := range addresses {
|
||||||
|
i := strings.IndexRune(v, ':')
|
||||||
|
if i == -1 {
|
||||||
|
err = errors.New("dbus: invalid bus address (no transport)")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
f := transports[v[:i]]
|
||||||
|
if f == nil {
|
||||||
|
err = errors.New("dbus: invalid bus address (invalid or unsupported transport)")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t, err = f(v[i+1:])
|
||||||
|
if err == nil {
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// dereferenceAll returns a slice that, assuming that vs is a slice of pointers
|
||||||
|
// of arbitrary types, containes the values that are obtained from dereferencing
|
||||||
|
// all elements in vs.
|
||||||
|
func dereferenceAll(vs []interface{}) []interface{} {
|
||||||
|
for i := range vs {
|
||||||
|
v := reflect.ValueOf(vs[i])
|
||||||
|
v = v.Elem()
|
||||||
|
vs[i] = v.Interface()
|
||||||
|
}
|
||||||
|
return vs
|
||||||
|
}
|
||||||
|
|
||||||
|
// getKey gets a key from a the list of keys. Returns "" on error / not found...
|
||||||
|
func getKey(s, key string) string {
|
||||||
|
for _, keyEqualsValue := range strings.Split(s, ",") {
|
||||||
|
keyValue := strings.SplitN(keyEqualsValue, "=", 2)
|
||||||
|
if len(keyValue) == 2 && keyValue[0] == key {
|
||||||
|
return keyValue[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
33
vendor/github.com/godbus/dbus/conn_darwin.go
generated
vendored
Normal file
33
vendor/github.com/godbus/dbus/conn_darwin.go
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultSystemBusAddress = "unix:path=/opt/local/var/run/dbus/system_bus_socket"
|
||||||
|
|
||||||
|
func getSessionBusPlatformAddress() (string, error) {
|
||||||
|
cmd := exec.Command("launchctl", "getenv", "DBUS_LAUNCHD_SESSION_BUS_SOCKET")
|
||||||
|
b, err := cmd.CombinedOutput()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(b) == 0 {
|
||||||
|
return "", errors.New("dbus: couldn't determine address of session bus")
|
||||||
|
}
|
||||||
|
|
||||||
|
return "unix:path=" + string(b[:len(b)-1]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSystemBusPlatformAddress() string {
|
||||||
|
address := os.Getenv("DBUS_LAUNCHD_SESSION_BUS_SOCKET")
|
||||||
|
if address != "" {
|
||||||
|
return fmt.Sprintf("unix:path=%s", address)
|
||||||
|
}
|
||||||
|
return defaultSystemBusAddress
|
||||||
|
}
|
42
vendor/github.com/godbus/dbus/conn_other.go
generated
vendored
Normal file
42
vendor/github.com/godbus/dbus/conn_other.go
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// +build !darwin
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultSystemBusAddress = "unix:path=/var/run/dbus/system_bus_socket"
|
||||||
|
|
||||||
|
func getSessionBusPlatformAddress() (string, error) {
|
||||||
|
cmd := exec.Command("dbus-launch")
|
||||||
|
b, err := cmd.CombinedOutput()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
i := bytes.IndexByte(b, '=')
|
||||||
|
j := bytes.IndexByte(b, '\n')
|
||||||
|
|
||||||
|
if i == -1 || j == -1 {
|
||||||
|
return "", errors.New("dbus: couldn't determine address of session bus")
|
||||||
|
}
|
||||||
|
|
||||||
|
env, addr := string(b[0:i]), string(b[i+1:j])
|
||||||
|
os.Setenv(env, addr)
|
||||||
|
|
||||||
|
return addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSystemBusPlatformAddress() string {
|
||||||
|
address := os.Getenv("DBUS_SYSTEM_BUS_ADDRESS")
|
||||||
|
if address != "" {
|
||||||
|
return fmt.Sprintf("unix:path=%s", address)
|
||||||
|
}
|
||||||
|
return defaultSystemBusAddress
|
||||||
|
}
|
427
vendor/github.com/godbus/dbus/dbus.go
generated
vendored
Normal file
427
vendor/github.com/godbus/dbus/dbus.go
generated
vendored
Normal file
@ -0,0 +1,427 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
byteType = reflect.TypeOf(byte(0))
|
||||||
|
boolType = reflect.TypeOf(false)
|
||||||
|
uint8Type = reflect.TypeOf(uint8(0))
|
||||||
|
int16Type = reflect.TypeOf(int16(0))
|
||||||
|
uint16Type = reflect.TypeOf(uint16(0))
|
||||||
|
intType = reflect.TypeOf(int(0))
|
||||||
|
uintType = reflect.TypeOf(uint(0))
|
||||||
|
int32Type = reflect.TypeOf(int32(0))
|
||||||
|
uint32Type = reflect.TypeOf(uint32(0))
|
||||||
|
int64Type = reflect.TypeOf(int64(0))
|
||||||
|
uint64Type = reflect.TypeOf(uint64(0))
|
||||||
|
float64Type = reflect.TypeOf(float64(0))
|
||||||
|
stringType = reflect.TypeOf("")
|
||||||
|
signatureType = reflect.TypeOf(Signature{""})
|
||||||
|
objectPathType = reflect.TypeOf(ObjectPath(""))
|
||||||
|
variantType = reflect.TypeOf(Variant{Signature{""}, nil})
|
||||||
|
interfacesType = reflect.TypeOf([]interface{}{})
|
||||||
|
interfaceType = reflect.TypeOf((*interface{})(nil)).Elem()
|
||||||
|
unixFDType = reflect.TypeOf(UnixFD(0))
|
||||||
|
unixFDIndexType = reflect.TypeOf(UnixFDIndex(0))
|
||||||
|
)
|
||||||
|
|
||||||
|
// An InvalidTypeError signals that a value which cannot be represented in the
|
||||||
|
// D-Bus wire format was passed to a function.
|
||||||
|
type InvalidTypeError struct {
|
||||||
|
Type reflect.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e InvalidTypeError) Error() string {
|
||||||
|
return "dbus: invalid type " + e.Type.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store copies the values contained in src to dest, which must be a slice of
|
||||||
|
// pointers. It converts slices of interfaces from src to corresponding structs
|
||||||
|
// in dest. An error is returned if the lengths of src and dest or the types of
|
||||||
|
// their elements don't match.
|
||||||
|
func Store(src []interface{}, dest ...interface{}) error {
|
||||||
|
if len(src) != len(dest) {
|
||||||
|
return errors.New("dbus.Store: length mismatch")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range src {
|
||||||
|
if err := storeInterfaces(src[i], dest[i]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeInterfaces(src, dest interface{}) error {
|
||||||
|
return store(reflect.ValueOf(dest), reflect.ValueOf(src))
|
||||||
|
}
|
||||||
|
|
||||||
|
func store(dest, src reflect.Value) error {
|
||||||
|
if dest.Kind() == reflect.Ptr {
|
||||||
|
return store(dest.Elem(), src)
|
||||||
|
}
|
||||||
|
switch src.Kind() {
|
||||||
|
case reflect.Slice:
|
||||||
|
return storeSlice(dest, src)
|
||||||
|
case reflect.Map:
|
||||||
|
return storeMap(dest, src)
|
||||||
|
default:
|
||||||
|
return storeBase(dest, src)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeBase(dest, src reflect.Value) error {
|
||||||
|
return setDest(dest, src)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setDest(dest, src reflect.Value) error {
|
||||||
|
if !isVariant(src.Type()) && isVariant(dest.Type()) {
|
||||||
|
//special conversion for dbus.Variant
|
||||||
|
dest.Set(reflect.ValueOf(MakeVariant(src.Interface())))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if isVariant(src.Type()) && !isVariant(dest.Type()) {
|
||||||
|
src = getVariantValue(src)
|
||||||
|
}
|
||||||
|
if !src.Type().ConvertibleTo(dest.Type()) {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"dbus.Store: type mismatch: cannot convert %s to %s",
|
||||||
|
src.Type(), dest.Type())
|
||||||
|
}
|
||||||
|
dest.Set(src.Convert(dest.Type()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func kindsAreCompatible(dest, src reflect.Type) bool {
|
||||||
|
switch {
|
||||||
|
case isVariant(dest):
|
||||||
|
return true
|
||||||
|
case dest.Kind() == reflect.Interface:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return dest.Kind() == src.Kind()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isConvertibleTo(dest, src reflect.Type) bool {
|
||||||
|
switch {
|
||||||
|
case isVariant(dest):
|
||||||
|
return true
|
||||||
|
case dest.Kind() == reflect.Interface:
|
||||||
|
return true
|
||||||
|
case dest.Kind() == reflect.Slice:
|
||||||
|
return src.Kind() == reflect.Slice &&
|
||||||
|
isConvertibleTo(dest.Elem(), src.Elem())
|
||||||
|
case dest.Kind() == reflect.Struct:
|
||||||
|
return src == interfacesType
|
||||||
|
default:
|
||||||
|
return src.ConvertibleTo(dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeMap(dest, src reflect.Value) error {
|
||||||
|
switch {
|
||||||
|
case !kindsAreCompatible(dest.Type(), src.Type()):
|
||||||
|
return fmt.Errorf(
|
||||||
|
"dbus.Store: type mismatch: "+
|
||||||
|
"map: cannot store a value of %s into %s",
|
||||||
|
src.Type(), dest.Type())
|
||||||
|
case isVariant(dest.Type()):
|
||||||
|
return storeMapIntoVariant(dest, src)
|
||||||
|
case dest.Kind() == reflect.Interface:
|
||||||
|
return storeMapIntoInterface(dest, src)
|
||||||
|
case isConvertibleTo(dest.Type().Key(), src.Type().Key()) &&
|
||||||
|
isConvertibleTo(dest.Type().Elem(), src.Type().Elem()):
|
||||||
|
return storeMapIntoMap(dest, src)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf(
|
||||||
|
"dbus.Store: type mismatch: "+
|
||||||
|
"map: cannot convert a value of %s into %s",
|
||||||
|
src.Type(), dest.Type())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeMapIntoVariant(dest, src reflect.Value) error {
|
||||||
|
dv := reflect.MakeMap(src.Type())
|
||||||
|
err := store(dv, src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return storeBase(dest, dv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeMapIntoInterface(dest, src reflect.Value) error {
|
||||||
|
var dv reflect.Value
|
||||||
|
if isVariant(src.Type().Elem()) {
|
||||||
|
//Convert variants to interface{} recursively when converting
|
||||||
|
//to interface{}
|
||||||
|
dv = reflect.MakeMap(
|
||||||
|
reflect.MapOf(src.Type().Key(), interfaceType))
|
||||||
|
} else {
|
||||||
|
dv = reflect.MakeMap(src.Type())
|
||||||
|
}
|
||||||
|
err := store(dv, src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return storeBase(dest, dv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeMapIntoMap(dest, src reflect.Value) error {
|
||||||
|
if dest.IsNil() {
|
||||||
|
dest.Set(reflect.MakeMap(dest.Type()))
|
||||||
|
}
|
||||||
|
keys := src.MapKeys()
|
||||||
|
for _, key := range keys {
|
||||||
|
dkey := key.Convert(dest.Type().Key())
|
||||||
|
dval := reflect.New(dest.Type().Elem()).Elem()
|
||||||
|
err := store(dval, getVariantValue(src.MapIndex(key)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dest.SetMapIndex(dkey, dval)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeSlice(dest, src reflect.Value) error {
|
||||||
|
switch {
|
||||||
|
case src.Type() == interfacesType && dest.Kind() == reflect.Struct:
|
||||||
|
//The decoder always decodes structs as slices of interface{}
|
||||||
|
return storeStruct(dest, src)
|
||||||
|
case !kindsAreCompatible(dest.Type(), src.Type()):
|
||||||
|
return fmt.Errorf(
|
||||||
|
"dbus.Store: type mismatch: "+
|
||||||
|
"slice: cannot store a value of %s into %s",
|
||||||
|
src.Type(), dest.Type())
|
||||||
|
case isVariant(dest.Type()):
|
||||||
|
return storeSliceIntoVariant(dest, src)
|
||||||
|
case dest.Kind() == reflect.Interface:
|
||||||
|
return storeSliceIntoInterface(dest, src)
|
||||||
|
case isConvertibleTo(dest.Type().Elem(), src.Type().Elem()):
|
||||||
|
return storeSliceIntoSlice(dest, src)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf(
|
||||||
|
"dbus.Store: type mismatch: "+
|
||||||
|
"slice: cannot convert a value of %s into %s",
|
||||||
|
src.Type(), dest.Type())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeStruct(dest, src reflect.Value) error {
|
||||||
|
if isVariant(dest.Type()) {
|
||||||
|
return storeBase(dest, src)
|
||||||
|
}
|
||||||
|
dval := make([]interface{}, 0, dest.NumField())
|
||||||
|
dtype := dest.Type()
|
||||||
|
for i := 0; i < dest.NumField(); i++ {
|
||||||
|
field := dest.Field(i)
|
||||||
|
ftype := dtype.Field(i)
|
||||||
|
if ftype.PkgPath != "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ftype.Tag.Get("dbus") == "-" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dval = append(dval, field.Addr().Interface())
|
||||||
|
}
|
||||||
|
if src.Len() != len(dval) {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"dbus.Store: type mismatch: "+
|
||||||
|
"destination struct does not have "+
|
||||||
|
"enough fields need: %d have: %d",
|
||||||
|
src.Len(), len(dval))
|
||||||
|
}
|
||||||
|
return Store(src.Interface().([]interface{}), dval...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeSliceIntoVariant(dest, src reflect.Value) error {
|
||||||
|
dv := reflect.MakeSlice(src.Type(), src.Len(), src.Cap())
|
||||||
|
err := store(dv, src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return storeBase(dest, dv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeSliceIntoInterface(dest, src reflect.Value) error {
|
||||||
|
var dv reflect.Value
|
||||||
|
if isVariant(src.Type().Elem()) {
|
||||||
|
//Convert variants to interface{} recursively when converting
|
||||||
|
//to interface{}
|
||||||
|
dv = reflect.MakeSlice(reflect.SliceOf(interfaceType),
|
||||||
|
src.Len(), src.Cap())
|
||||||
|
} else {
|
||||||
|
dv = reflect.MakeSlice(src.Type(), src.Len(), src.Cap())
|
||||||
|
}
|
||||||
|
err := store(dv, src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return storeBase(dest, dv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeSliceIntoSlice(dest, src reflect.Value) error {
|
||||||
|
if dest.IsNil() || dest.Len() < src.Len() {
|
||||||
|
dest.Set(reflect.MakeSlice(dest.Type(), src.Len(), src.Cap()))
|
||||||
|
}
|
||||||
|
if dest.Len() != src.Len() {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"dbus.Store: type mismatch: "+
|
||||||
|
"slices are different lengths "+
|
||||||
|
"need: %d have: %d",
|
||||||
|
src.Len(), dest.Len())
|
||||||
|
}
|
||||||
|
for i := 0; i < src.Len(); i++ {
|
||||||
|
err := store(dest.Index(i), getVariantValue(src.Index(i)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getVariantValue(in reflect.Value) reflect.Value {
|
||||||
|
if isVariant(in.Type()) {
|
||||||
|
return reflect.ValueOf(in.Interface().(Variant).Value())
|
||||||
|
}
|
||||||
|
return in
|
||||||
|
}
|
||||||
|
|
||||||
|
func isVariant(t reflect.Type) bool {
|
||||||
|
return t == variantType
|
||||||
|
}
|
||||||
|
|
||||||
|
// An ObjectPath is an object path as defined by the D-Bus spec.
|
||||||
|
type ObjectPath string
|
||||||
|
|
||||||
|
// IsValid returns whether the object path is valid.
|
||||||
|
func (o ObjectPath) IsValid() bool {
|
||||||
|
s := string(o)
|
||||||
|
if len(s) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if s[0] != '/' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if s[len(s)-1] == '/' && len(s) != 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// probably not used, but technically possible
|
||||||
|
if s == "/" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
split := strings.Split(s[1:], "/")
|
||||||
|
for _, v := range split {
|
||||||
|
if len(v) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, c := range v {
|
||||||
|
if !isMemberChar(c) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// A UnixFD is a Unix file descriptor sent over the wire. See the package-level
|
||||||
|
// documentation for more information about Unix file descriptor passsing.
|
||||||
|
type UnixFD int32
|
||||||
|
|
||||||
|
// A UnixFDIndex is the representation of a Unix file descriptor in a message.
|
||||||
|
type UnixFDIndex uint32
|
||||||
|
|
||||||
|
// alignment returns the alignment of values of type t.
|
||||||
|
func alignment(t reflect.Type) int {
|
||||||
|
switch t {
|
||||||
|
case variantType:
|
||||||
|
return 1
|
||||||
|
case objectPathType:
|
||||||
|
return 4
|
||||||
|
case signatureType:
|
||||||
|
return 1
|
||||||
|
case interfacesType:
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
switch t.Kind() {
|
||||||
|
case reflect.Uint8:
|
||||||
|
return 1
|
||||||
|
case reflect.Uint16, reflect.Int16:
|
||||||
|
return 2
|
||||||
|
case reflect.Uint, reflect.Int, reflect.Uint32, reflect.Int32, reflect.String, reflect.Array, reflect.Slice, reflect.Map:
|
||||||
|
return 4
|
||||||
|
case reflect.Uint64, reflect.Int64, reflect.Float64, reflect.Struct:
|
||||||
|
return 8
|
||||||
|
case reflect.Ptr:
|
||||||
|
return alignment(t.Elem())
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// isKeyType returns whether t is a valid type for a D-Bus dict.
|
||||||
|
func isKeyType(t reflect.Type) bool {
|
||||||
|
switch t.Kind() {
|
||||||
|
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
||||||
|
reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float64,
|
||||||
|
reflect.String, reflect.Uint, reflect.Int:
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// isValidInterface returns whether s is a valid name for an interface.
|
||||||
|
func isValidInterface(s string) bool {
|
||||||
|
if len(s) == 0 || len(s) > 255 || s[0] == '.' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
elem := strings.Split(s, ".")
|
||||||
|
if len(elem) < 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, v := range elem {
|
||||||
|
if len(v) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if v[0] >= '0' && v[0] <= '9' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, c := range v {
|
||||||
|
if !isMemberChar(c) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// isValidMember returns whether s is a valid name for a member.
|
||||||
|
func isValidMember(s string) bool {
|
||||||
|
if len(s) == 0 || len(s) > 255 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
i := strings.Index(s, ".")
|
||||||
|
if i != -1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if s[0] >= '0' && s[0] <= '9' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, c := range s {
|
||||||
|
if !isMemberChar(c) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func isMemberChar(c rune) bool {
|
||||||
|
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') ||
|
||||||
|
(c >= 'a' && c <= 'z') || c == '_'
|
||||||
|
}
|
228
vendor/github.com/godbus/dbus/decoder.go
generated
vendored
Normal file
228
vendor/github.com/godbus/dbus/decoder.go
generated
vendored
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
type decoder struct {
|
||||||
|
in io.Reader
|
||||||
|
order binary.ByteOrder
|
||||||
|
pos int
|
||||||
|
}
|
||||||
|
|
||||||
|
// newDecoder returns a new decoder that reads values from in. The input is
|
||||||
|
// expected to be in the given byte order.
|
||||||
|
func newDecoder(in io.Reader, order binary.ByteOrder) *decoder {
|
||||||
|
dec := new(decoder)
|
||||||
|
dec.in = in
|
||||||
|
dec.order = order
|
||||||
|
return dec
|
||||||
|
}
|
||||||
|
|
||||||
|
// align aligns the input to the given boundary and panics on error.
|
||||||
|
func (dec *decoder) align(n int) {
|
||||||
|
if dec.pos%n != 0 {
|
||||||
|
newpos := (dec.pos + n - 1) & ^(n - 1)
|
||||||
|
empty := make([]byte, newpos-dec.pos)
|
||||||
|
if _, err := io.ReadFull(dec.in, empty); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
dec.pos = newpos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calls binary.Read(dec.in, dec.order, v) and panics on read errors.
|
||||||
|
func (dec *decoder) binread(v interface{}) {
|
||||||
|
if err := binary.Read(dec.in, dec.order, v); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dec *decoder) Decode(sig Signature) (vs []interface{}, err error) {
|
||||||
|
defer func() {
|
||||||
|
var ok bool
|
||||||
|
v := recover()
|
||||||
|
if err, ok = v.(error); ok {
|
||||||
|
if err == io.EOF || err == io.ErrUnexpectedEOF {
|
||||||
|
err = FormatError("unexpected EOF")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
vs = make([]interface{}, 0)
|
||||||
|
s := sig.str
|
||||||
|
for s != "" {
|
||||||
|
err, rem := validSingle(s, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
v := dec.decode(s[:len(s)-len(rem)], 0)
|
||||||
|
vs = append(vs, v)
|
||||||
|
s = rem
|
||||||
|
}
|
||||||
|
return vs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dec *decoder) decode(s string, depth int) interface{} {
|
||||||
|
dec.align(alignment(typeFor(s)))
|
||||||
|
switch s[0] {
|
||||||
|
case 'y':
|
||||||
|
var b [1]byte
|
||||||
|
if _, err := dec.in.Read(b[:]); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
dec.pos++
|
||||||
|
return b[0]
|
||||||
|
case 'b':
|
||||||
|
i := dec.decode("u", depth).(uint32)
|
||||||
|
switch {
|
||||||
|
case i == 0:
|
||||||
|
return false
|
||||||
|
case i == 1:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
panic(FormatError("invalid value for boolean"))
|
||||||
|
}
|
||||||
|
case 'n':
|
||||||
|
var i int16
|
||||||
|
dec.binread(&i)
|
||||||
|
dec.pos += 2
|
||||||
|
return i
|
||||||
|
case 'i':
|
||||||
|
var i int32
|
||||||
|
dec.binread(&i)
|
||||||
|
dec.pos += 4
|
||||||
|
return i
|
||||||
|
case 'x':
|
||||||
|
var i int64
|
||||||
|
dec.binread(&i)
|
||||||
|
dec.pos += 8
|
||||||
|
return i
|
||||||
|
case 'q':
|
||||||
|
var i uint16
|
||||||
|
dec.binread(&i)
|
||||||
|
dec.pos += 2
|
||||||
|
return i
|
||||||
|
case 'u':
|
||||||
|
var i uint32
|
||||||
|
dec.binread(&i)
|
||||||
|
dec.pos += 4
|
||||||
|
return i
|
||||||
|
case 't':
|
||||||
|
var i uint64
|
||||||
|
dec.binread(&i)
|
||||||
|
dec.pos += 8
|
||||||
|
return i
|
||||||
|
case 'd':
|
||||||
|
var f float64
|
||||||
|
dec.binread(&f)
|
||||||
|
dec.pos += 8
|
||||||
|
return f
|
||||||
|
case 's':
|
||||||
|
length := dec.decode("u", depth).(uint32)
|
||||||
|
b := make([]byte, int(length)+1)
|
||||||
|
if _, err := io.ReadFull(dec.in, b); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
dec.pos += int(length) + 1
|
||||||
|
return string(b[:len(b)-1])
|
||||||
|
case 'o':
|
||||||
|
return ObjectPath(dec.decode("s", depth).(string))
|
||||||
|
case 'g':
|
||||||
|
length := dec.decode("y", depth).(byte)
|
||||||
|
b := make([]byte, int(length)+1)
|
||||||
|
if _, err := io.ReadFull(dec.in, b); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
dec.pos += int(length) + 1
|
||||||
|
sig, err := ParseSignature(string(b[:len(b)-1]))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return sig
|
||||||
|
case 'v':
|
||||||
|
if depth >= 64 {
|
||||||
|
panic(FormatError("input exceeds container depth limit"))
|
||||||
|
}
|
||||||
|
var variant Variant
|
||||||
|
sig := dec.decode("g", depth).(Signature)
|
||||||
|
if len(sig.str) == 0 {
|
||||||
|
panic(FormatError("variant signature is empty"))
|
||||||
|
}
|
||||||
|
err, rem := validSingle(sig.str, 0)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if rem != "" {
|
||||||
|
panic(FormatError("variant signature has multiple types"))
|
||||||
|
}
|
||||||
|
variant.sig = sig
|
||||||
|
variant.value = dec.decode(sig.str, depth+1)
|
||||||
|
return variant
|
||||||
|
case 'h':
|
||||||
|
return UnixFDIndex(dec.decode("u", depth).(uint32))
|
||||||
|
case 'a':
|
||||||
|
if len(s) > 1 && s[1] == '{' {
|
||||||
|
ksig := s[2:3]
|
||||||
|
vsig := s[3 : len(s)-1]
|
||||||
|
v := reflect.MakeMap(reflect.MapOf(typeFor(ksig), typeFor(vsig)))
|
||||||
|
if depth >= 63 {
|
||||||
|
panic(FormatError("input exceeds container depth limit"))
|
||||||
|
}
|
||||||
|
length := dec.decode("u", depth).(uint32)
|
||||||
|
// Even for empty maps, the correct padding must be included
|
||||||
|
dec.align(8)
|
||||||
|
spos := dec.pos
|
||||||
|
for dec.pos < spos+int(length) {
|
||||||
|
dec.align(8)
|
||||||
|
if !isKeyType(v.Type().Key()) {
|
||||||
|
panic(InvalidTypeError{v.Type()})
|
||||||
|
}
|
||||||
|
kv := dec.decode(ksig, depth+2)
|
||||||
|
vv := dec.decode(vsig, depth+2)
|
||||||
|
v.SetMapIndex(reflect.ValueOf(kv), reflect.ValueOf(vv))
|
||||||
|
}
|
||||||
|
return v.Interface()
|
||||||
|
}
|
||||||
|
if depth >= 64 {
|
||||||
|
panic(FormatError("input exceeds container depth limit"))
|
||||||
|
}
|
||||||
|
length := dec.decode("u", depth).(uint32)
|
||||||
|
v := reflect.MakeSlice(reflect.SliceOf(typeFor(s[1:])), 0, int(length))
|
||||||
|
// Even for empty arrays, the correct padding must be included
|
||||||
|
dec.align(alignment(typeFor(s[1:])))
|
||||||
|
spos := dec.pos
|
||||||
|
for dec.pos < spos+int(length) {
|
||||||
|
ev := dec.decode(s[1:], depth+1)
|
||||||
|
v = reflect.Append(v, reflect.ValueOf(ev))
|
||||||
|
}
|
||||||
|
return v.Interface()
|
||||||
|
case '(':
|
||||||
|
if depth >= 64 {
|
||||||
|
panic(FormatError("input exceeds container depth limit"))
|
||||||
|
}
|
||||||
|
dec.align(8)
|
||||||
|
v := make([]interface{}, 0)
|
||||||
|
s = s[1 : len(s)-1]
|
||||||
|
for s != "" {
|
||||||
|
err, rem := validSingle(s, 0)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
ev := dec.decode(s[:len(s)-len(rem)], depth+1)
|
||||||
|
v = append(v, ev)
|
||||||
|
s = rem
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
default:
|
||||||
|
panic(SignatureError{Sig: s})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A FormatError is an error in the wire format.
|
||||||
|
type FormatError string
|
||||||
|
|
||||||
|
func (e FormatError) Error() string {
|
||||||
|
return "dbus: wire format error: " + string(e)
|
||||||
|
}
|
283
vendor/github.com/godbus/dbus/default_handler.go
generated
vendored
Normal file
283
vendor/github.com/godbus/dbus/default_handler.go
generated
vendored
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newIntrospectIntf(h *defaultHandler) *exportedIntf {
|
||||||
|
methods := make(map[string]Method)
|
||||||
|
methods["Introspect"] = exportedMethod{
|
||||||
|
reflect.ValueOf(func(msg Message) (string, *Error) {
|
||||||
|
path := msg.Headers[FieldPath].value.(ObjectPath)
|
||||||
|
return h.introspectPath(path), nil
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
return newExportedIntf(methods, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDefaultHandler() *defaultHandler {
|
||||||
|
h := &defaultHandler{
|
||||||
|
objects: make(map[ObjectPath]*exportedObj),
|
||||||
|
defaultIntf: make(map[string]*exportedIntf),
|
||||||
|
}
|
||||||
|
h.defaultIntf["org.freedesktop.DBus.Introspectable"] = newIntrospectIntf(h)
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
type defaultHandler struct {
|
||||||
|
sync.RWMutex
|
||||||
|
objects map[ObjectPath]*exportedObj
|
||||||
|
defaultIntf map[string]*exportedIntf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *defaultHandler) PathExists(path ObjectPath) bool {
|
||||||
|
_, ok := h.objects[path]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *defaultHandler) introspectPath(path ObjectPath) string {
|
||||||
|
subpath := make(map[string]struct{})
|
||||||
|
var xml bytes.Buffer
|
||||||
|
xml.WriteString("<node>")
|
||||||
|
for obj, _ := range h.objects {
|
||||||
|
p := string(path)
|
||||||
|
if p != "/" {
|
||||||
|
p += "/"
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(string(obj), p) {
|
||||||
|
node_name := strings.Split(string(obj[len(p):]), "/")[0]
|
||||||
|
subpath[node_name] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for s, _ := range subpath {
|
||||||
|
xml.WriteString("\n\t<node name=\"" + s + "\"/>")
|
||||||
|
}
|
||||||
|
xml.WriteString("\n</node>")
|
||||||
|
return xml.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *defaultHandler) LookupObject(path ObjectPath) (ServerObject, bool) {
|
||||||
|
h.RLock()
|
||||||
|
defer h.RUnlock()
|
||||||
|
object, ok := h.objects[path]
|
||||||
|
if ok {
|
||||||
|
return object, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// If an object wasn't found for this exact path,
|
||||||
|
// look for a matching subtree registration
|
||||||
|
subtreeObject := newExportedObject()
|
||||||
|
path = path[:strings.LastIndex(string(path), "/")]
|
||||||
|
for len(path) > 0 {
|
||||||
|
object, ok = h.objects[path]
|
||||||
|
if ok {
|
||||||
|
for name, iface := range object.interfaces {
|
||||||
|
// Only include this handler if it registered for the subtree
|
||||||
|
if iface.isFallbackInterface() {
|
||||||
|
subtreeObject.interfaces[name] = iface
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
path = path[:strings.LastIndex(string(path), "/")]
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, intf := range h.defaultIntf {
|
||||||
|
if _, exists := subtreeObject.interfaces[name]; exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
subtreeObject.interfaces[name] = intf
|
||||||
|
}
|
||||||
|
|
||||||
|
return subtreeObject, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *defaultHandler) AddObject(path ObjectPath, object *exportedObj) {
|
||||||
|
h.Lock()
|
||||||
|
h.objects[path] = object
|
||||||
|
h.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *defaultHandler) DeleteObject(path ObjectPath) {
|
||||||
|
h.Lock()
|
||||||
|
delete(h.objects, path)
|
||||||
|
h.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
type exportedMethod struct {
|
||||||
|
reflect.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m exportedMethod) Call(args ...interface{}) ([]interface{}, error) {
|
||||||
|
t := m.Type()
|
||||||
|
|
||||||
|
params := make([]reflect.Value, len(args))
|
||||||
|
for i := 0; i < len(args); i++ {
|
||||||
|
params[i] = reflect.ValueOf(args[i]).Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := m.Value.Call(params)
|
||||||
|
|
||||||
|
err := ret[t.NumOut()-1].Interface().(*Error)
|
||||||
|
ret = ret[:t.NumOut()-1]
|
||||||
|
out := make([]interface{}, len(ret))
|
||||||
|
for i, val := range ret {
|
||||||
|
out[i] = val.Interface()
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
//concrete type to interface nil is a special case
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m exportedMethod) NumArguments() int {
|
||||||
|
return m.Value.Type().NumIn()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m exportedMethod) ArgumentValue(i int) interface{} {
|
||||||
|
return reflect.Zero(m.Type().In(i)).Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m exportedMethod) NumReturns() int {
|
||||||
|
return m.Value.Type().NumOut()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m exportedMethod) ReturnValue(i int) interface{} {
|
||||||
|
return reflect.Zero(m.Type().Out(i)).Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
func newExportedObject() *exportedObj {
|
||||||
|
return &exportedObj{
|
||||||
|
interfaces: make(map[string]*exportedIntf),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type exportedObj struct {
|
||||||
|
interfaces map[string]*exportedIntf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *exportedObj) LookupInterface(name string) (Interface, bool) {
|
||||||
|
if name == "" {
|
||||||
|
return obj, true
|
||||||
|
}
|
||||||
|
intf, exists := obj.interfaces[name]
|
||||||
|
return intf, exists
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *exportedObj) AddInterface(name string, iface *exportedIntf) {
|
||||||
|
obj.interfaces[name] = iface
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *exportedObj) DeleteInterface(name string) {
|
||||||
|
delete(obj.interfaces, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *exportedObj) LookupMethod(name string) (Method, bool) {
|
||||||
|
for _, intf := range obj.interfaces {
|
||||||
|
method, exists := intf.LookupMethod(name)
|
||||||
|
if exists {
|
||||||
|
return method, exists
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *exportedObj) isFallbackInterface() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func newExportedIntf(methods map[string]Method, includeSubtree bool) *exportedIntf {
|
||||||
|
return &exportedIntf{
|
||||||
|
methods: methods,
|
||||||
|
includeSubtree: includeSubtree,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type exportedIntf struct {
|
||||||
|
methods map[string]Method
|
||||||
|
|
||||||
|
// Whether or not this export is for the entire subtree
|
||||||
|
includeSubtree bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *exportedIntf) LookupMethod(name string) (Method, bool) {
|
||||||
|
out, exists := obj.methods[name]
|
||||||
|
return out, exists
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *exportedIntf) isFallbackInterface() bool {
|
||||||
|
return obj.includeSubtree
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDefaultSignalHandler() *defaultSignalHandler {
|
||||||
|
return &defaultSignalHandler{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isDefaultSignalHandler(handler SignalHandler) bool {
|
||||||
|
_, ok := handler.(*defaultSignalHandler)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
type defaultSignalHandler struct {
|
||||||
|
sync.RWMutex
|
||||||
|
closed bool
|
||||||
|
signals []chan<- *Signal
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sh *defaultSignalHandler) DeliverSignal(intf, name string, signal *Signal) {
|
||||||
|
sh.RLock()
|
||||||
|
defer sh.RUnlock()
|
||||||
|
if sh.closed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, ch := range sh.signals {
|
||||||
|
ch <- signal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sh *defaultSignalHandler) Init() error {
|
||||||
|
sh.Lock()
|
||||||
|
sh.signals = make([]chan<- *Signal, 0)
|
||||||
|
sh.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sh *defaultSignalHandler) Terminate() {
|
||||||
|
sh.Lock()
|
||||||
|
sh.closed = true
|
||||||
|
for _, ch := range sh.signals {
|
||||||
|
close(ch)
|
||||||
|
}
|
||||||
|
sh.signals = nil
|
||||||
|
sh.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sh *defaultSignalHandler) addSignal(ch chan<- *Signal) {
|
||||||
|
sh.Lock()
|
||||||
|
defer sh.Unlock()
|
||||||
|
if sh.closed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sh.signals = append(sh.signals, ch)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sh *defaultSignalHandler) removeSignal(ch chan<- *Signal) {
|
||||||
|
sh.Lock()
|
||||||
|
defer sh.Unlock()
|
||||||
|
if sh.closed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := len(sh.signals) - 1; i >= 0; i-- {
|
||||||
|
if ch == sh.signals[i] {
|
||||||
|
copy(sh.signals[i:], sh.signals[i+1:])
|
||||||
|
sh.signals[len(sh.signals)-1] = nil
|
||||||
|
sh.signals = sh.signals[:len(sh.signals)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
69
vendor/github.com/godbus/dbus/doc.go
generated
vendored
Normal file
69
vendor/github.com/godbus/dbus/doc.go
generated
vendored
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
Package dbus implements bindings to the D-Bus message bus system.
|
||||||
|
|
||||||
|
To use the message bus API, you first need to connect to a bus (usually the
|
||||||
|
session or system bus). The acquired connection then can be used to call methods
|
||||||
|
on remote objects and emit or receive signals. Using the Export method, you can
|
||||||
|
arrange D-Bus methods calls to be directly translated to method calls on a Go
|
||||||
|
value.
|
||||||
|
|
||||||
|
Conversion Rules
|
||||||
|
|
||||||
|
For outgoing messages, Go types are automatically converted to the
|
||||||
|
corresponding D-Bus types. The following types are directly encoded as their
|
||||||
|
respective D-Bus equivalents:
|
||||||
|
|
||||||
|
Go type | D-Bus type
|
||||||
|
------------+-----------
|
||||||
|
byte | BYTE
|
||||||
|
bool | BOOLEAN
|
||||||
|
int16 | INT16
|
||||||
|
uint16 | UINT16
|
||||||
|
int | INT32
|
||||||
|
uint | UINT32
|
||||||
|
int32 | INT32
|
||||||
|
uint32 | UINT32
|
||||||
|
int64 | INT64
|
||||||
|
uint64 | UINT64
|
||||||
|
float64 | DOUBLE
|
||||||
|
string | STRING
|
||||||
|
ObjectPath | OBJECT_PATH
|
||||||
|
Signature | SIGNATURE
|
||||||
|
Variant | VARIANT
|
||||||
|
interface{} | VARIANT
|
||||||
|
UnixFDIndex | UNIX_FD
|
||||||
|
|
||||||
|
Slices and arrays encode as ARRAYs of their element type.
|
||||||
|
|
||||||
|
Maps encode as DICTs, provided that their key type can be used as a key for
|
||||||
|
a DICT.
|
||||||
|
|
||||||
|
Structs other than Variant and Signature encode as a STRUCT containing their
|
||||||
|
exported fields. Fields whose tags contain `dbus:"-"` and unexported fields will
|
||||||
|
be skipped.
|
||||||
|
|
||||||
|
Pointers encode as the value they're pointed to.
|
||||||
|
|
||||||
|
Types convertible to one of the base types above will be mapped as the
|
||||||
|
base type.
|
||||||
|
|
||||||
|
Trying to encode any other type or a slice, map or struct containing an
|
||||||
|
unsupported type will result in an InvalidTypeError.
|
||||||
|
|
||||||
|
For incoming messages, the inverse of these rules are used, with the exception
|
||||||
|
of STRUCTs. Incoming STRUCTS are represented as a slice of empty interfaces
|
||||||
|
containing the struct fields in the correct order. The Store function can be
|
||||||
|
used to convert such values to Go structs.
|
||||||
|
|
||||||
|
Unix FD passing
|
||||||
|
|
||||||
|
Handling Unix file descriptors deserves special mention. To use them, you should
|
||||||
|
first check that they are supported on a connection by calling SupportsUnixFDs.
|
||||||
|
If it returns true, all method of Connection will translate messages containing
|
||||||
|
UnixFD's to messages that are accompanied by the given file descriptors with the
|
||||||
|
UnixFD values being substituted by the correct indices. Similarily, the indices
|
||||||
|
of incoming messages are automatically resolved. It shouldn't be necessary to use
|
||||||
|
UnixFDIndex.
|
||||||
|
|
||||||
|
*/
|
||||||
|
package dbus
|
210
vendor/github.com/godbus/dbus/encoder.go
generated
vendored
Normal file
210
vendor/github.com/godbus/dbus/encoder.go
generated
vendored
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// An encoder encodes values to the D-Bus wire format.
|
||||||
|
type encoder struct {
|
||||||
|
out io.Writer
|
||||||
|
order binary.ByteOrder
|
||||||
|
pos int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEncoder returns a new encoder that writes to out in the given byte order.
|
||||||
|
func newEncoder(out io.Writer, order binary.ByteOrder) *encoder {
|
||||||
|
return newEncoderAtOffset(out, 0, order)
|
||||||
|
}
|
||||||
|
|
||||||
|
// newEncoderAtOffset returns a new encoder that writes to out in the given
|
||||||
|
// byte order. Specify the offset to initialize pos for proper alignment
|
||||||
|
// computation.
|
||||||
|
func newEncoderAtOffset(out io.Writer, offset int, order binary.ByteOrder) *encoder {
|
||||||
|
enc := new(encoder)
|
||||||
|
enc.out = out
|
||||||
|
enc.order = order
|
||||||
|
enc.pos = offset
|
||||||
|
return enc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aligns the next output to be on a multiple of n. Panics on write errors.
|
||||||
|
func (enc *encoder) align(n int) {
|
||||||
|
pad := enc.padding(0, n)
|
||||||
|
if pad > 0 {
|
||||||
|
empty := make([]byte, pad)
|
||||||
|
if _, err := enc.out.Write(empty); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
enc.pos += pad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pad returns the number of bytes of padding, based on current position and additional offset.
|
||||||
|
// and alignment.
|
||||||
|
func (enc *encoder) padding(offset, algn int) int {
|
||||||
|
abs := enc.pos + offset
|
||||||
|
if abs%algn != 0 {
|
||||||
|
newabs := (abs + algn - 1) & ^(algn - 1)
|
||||||
|
return newabs - abs
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calls binary.Write(enc.out, enc.order, v) and panics on write errors.
|
||||||
|
func (enc *encoder) binwrite(v interface{}) {
|
||||||
|
if err := binary.Write(enc.out, enc.order, v); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode encodes the given values to the underyling reader. All written values
|
||||||
|
// are aligned properly as required by the D-Bus spec.
|
||||||
|
func (enc *encoder) Encode(vs ...interface{}) (err error) {
|
||||||
|
defer func() {
|
||||||
|
err, _ = recover().(error)
|
||||||
|
}()
|
||||||
|
for _, v := range vs {
|
||||||
|
enc.encode(reflect.ValueOf(v), 0)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// encode encodes the given value to the writer and panics on error. depth holds
|
||||||
|
// the depth of the container nesting.
|
||||||
|
func (enc *encoder) encode(v reflect.Value, depth int) {
|
||||||
|
enc.align(alignment(v.Type()))
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Uint8:
|
||||||
|
var b [1]byte
|
||||||
|
b[0] = byte(v.Uint())
|
||||||
|
if _, err := enc.out.Write(b[:]); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
enc.pos++
|
||||||
|
case reflect.Bool:
|
||||||
|
if v.Bool() {
|
||||||
|
enc.encode(reflect.ValueOf(uint32(1)), depth)
|
||||||
|
} else {
|
||||||
|
enc.encode(reflect.ValueOf(uint32(0)), depth)
|
||||||
|
}
|
||||||
|
case reflect.Int16:
|
||||||
|
enc.binwrite(int16(v.Int()))
|
||||||
|
enc.pos += 2
|
||||||
|
case reflect.Uint16:
|
||||||
|
enc.binwrite(uint16(v.Uint()))
|
||||||
|
enc.pos += 2
|
||||||
|
case reflect.Int, reflect.Int32:
|
||||||
|
enc.binwrite(int32(v.Int()))
|
||||||
|
enc.pos += 4
|
||||||
|
case reflect.Uint, reflect.Uint32:
|
||||||
|
enc.binwrite(uint32(v.Uint()))
|
||||||
|
enc.pos += 4
|
||||||
|
case reflect.Int64:
|
||||||
|
enc.binwrite(v.Int())
|
||||||
|
enc.pos += 8
|
||||||
|
case reflect.Uint64:
|
||||||
|
enc.binwrite(v.Uint())
|
||||||
|
enc.pos += 8
|
||||||
|
case reflect.Float64:
|
||||||
|
enc.binwrite(v.Float())
|
||||||
|
enc.pos += 8
|
||||||
|
case reflect.String:
|
||||||
|
enc.encode(reflect.ValueOf(uint32(len(v.String()))), depth)
|
||||||
|
b := make([]byte, v.Len()+1)
|
||||||
|
copy(b, v.String())
|
||||||
|
b[len(b)-1] = 0
|
||||||
|
n, err := enc.out.Write(b)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
enc.pos += n
|
||||||
|
case reflect.Ptr:
|
||||||
|
enc.encode(v.Elem(), depth)
|
||||||
|
case reflect.Slice, reflect.Array:
|
||||||
|
if depth >= 64 {
|
||||||
|
panic(FormatError("input exceeds container depth limit"))
|
||||||
|
}
|
||||||
|
// Lookahead offset: 4 bytes for uint32 length (with alignment),
|
||||||
|
// plus alignment for elements.
|
||||||
|
n := enc.padding(0, 4) + 4
|
||||||
|
offset := enc.pos + n + enc.padding(n, alignment(v.Type().Elem()))
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
bufenc := newEncoderAtOffset(&buf, offset, enc.order)
|
||||||
|
|
||||||
|
for i := 0; i < v.Len(); i++ {
|
||||||
|
bufenc.encode(v.Index(i), depth+1)
|
||||||
|
}
|
||||||
|
enc.encode(reflect.ValueOf(uint32(buf.Len())), depth)
|
||||||
|
length := buf.Len()
|
||||||
|
enc.align(alignment(v.Type().Elem()))
|
||||||
|
if _, err := buf.WriteTo(enc.out); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
enc.pos += length
|
||||||
|
case reflect.Struct:
|
||||||
|
if depth >= 64 && v.Type() != signatureType {
|
||||||
|
panic(FormatError("input exceeds container depth limit"))
|
||||||
|
}
|
||||||
|
switch t := v.Type(); t {
|
||||||
|
case signatureType:
|
||||||
|
str := v.Field(0)
|
||||||
|
enc.encode(reflect.ValueOf(byte(str.Len())), depth+1)
|
||||||
|
b := make([]byte, str.Len()+1)
|
||||||
|
copy(b, str.String())
|
||||||
|
b[len(b)-1] = 0
|
||||||
|
n, err := enc.out.Write(b)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
enc.pos += n
|
||||||
|
case variantType:
|
||||||
|
variant := v.Interface().(Variant)
|
||||||
|
enc.encode(reflect.ValueOf(variant.sig), depth+1)
|
||||||
|
enc.encode(reflect.ValueOf(variant.value), depth+1)
|
||||||
|
default:
|
||||||
|
for i := 0; i < v.Type().NumField(); i++ {
|
||||||
|
field := t.Field(i)
|
||||||
|
if field.PkgPath == "" && field.Tag.Get("dbus") != "-" {
|
||||||
|
enc.encode(v.Field(i), depth+1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
// Maps are arrays of structures, so they actually increase the depth by
|
||||||
|
// 2.
|
||||||
|
if depth >= 63 {
|
||||||
|
panic(FormatError("input exceeds container depth limit"))
|
||||||
|
}
|
||||||
|
if !isKeyType(v.Type().Key()) {
|
||||||
|
panic(InvalidTypeError{v.Type()})
|
||||||
|
}
|
||||||
|
keys := v.MapKeys()
|
||||||
|
// Lookahead offset: 4 bytes for uint32 length (with alignment),
|
||||||
|
// plus 8-byte alignment
|
||||||
|
n := enc.padding(0, 4) + 4
|
||||||
|
offset := enc.pos + n + enc.padding(n, 8)
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
bufenc := newEncoderAtOffset(&buf, offset, enc.order)
|
||||||
|
for _, k := range keys {
|
||||||
|
bufenc.align(8)
|
||||||
|
bufenc.encode(k, depth+2)
|
||||||
|
bufenc.encode(v.MapIndex(k), depth+2)
|
||||||
|
}
|
||||||
|
enc.encode(reflect.ValueOf(uint32(buf.Len())), depth)
|
||||||
|
length := buf.Len()
|
||||||
|
enc.align(8)
|
||||||
|
if _, err := buf.WriteTo(enc.out); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
enc.pos += length
|
||||||
|
case reflect.Interface:
|
||||||
|
enc.encode(reflect.ValueOf(MakeVariant(v.Interface())), depth)
|
||||||
|
default:
|
||||||
|
panic(InvalidTypeError{v.Type()})
|
||||||
|
}
|
||||||
|
}
|
413
vendor/github.com/godbus/dbus/export.go
generated
vendored
Normal file
413
vendor/github.com/godbus/dbus/export.go
generated
vendored
Normal file
@ -0,0 +1,413 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrMsgInvalidArg = Error{
|
||||||
|
"org.freedesktop.DBus.Error.InvalidArgs",
|
||||||
|
[]interface{}{"Invalid type / number of args"},
|
||||||
|
}
|
||||||
|
ErrMsgNoObject = Error{
|
||||||
|
"org.freedesktop.DBus.Error.NoSuchObject",
|
||||||
|
[]interface{}{"No such object"},
|
||||||
|
}
|
||||||
|
ErrMsgUnknownMethod = Error{
|
||||||
|
"org.freedesktop.DBus.Error.UnknownMethod",
|
||||||
|
[]interface{}{"Unknown / invalid method"},
|
||||||
|
}
|
||||||
|
ErrMsgUnknownInterface = Error{
|
||||||
|
"org.freedesktop.DBus.Error.UnknownInterface",
|
||||||
|
[]interface{}{"Object does not implement the interface"},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func MakeFailedError(err error) *Error {
|
||||||
|
return &Error{
|
||||||
|
"org.freedesktop.DBus.Error.Failed",
|
||||||
|
[]interface{}{err.Error()},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sender is a type which can be used in exported methods to receive the message
|
||||||
|
// sender.
|
||||||
|
type Sender string
|
||||||
|
|
||||||
|
func computeMethodName(name string, mapping map[string]string) string {
|
||||||
|
newname, ok := mapping[name]
|
||||||
|
if ok {
|
||||||
|
name = newname
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMethods(in interface{}, mapping map[string]string) map[string]reflect.Value {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
methods := make(map[string]reflect.Value)
|
||||||
|
val := reflect.ValueOf(in)
|
||||||
|
typ := val.Type()
|
||||||
|
for i := 0; i < typ.NumMethod(); i++ {
|
||||||
|
methtype := typ.Method(i)
|
||||||
|
method := val.Method(i)
|
||||||
|
t := method.Type()
|
||||||
|
// only track valid methods must return *Error as last arg
|
||||||
|
// and must be exported
|
||||||
|
if t.NumOut() == 0 ||
|
||||||
|
t.Out(t.NumOut()-1) != reflect.TypeOf(&ErrMsgInvalidArg) ||
|
||||||
|
methtype.PkgPath != "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// map names while building table
|
||||||
|
methods[computeMethodName(methtype.Name, mapping)] = method
|
||||||
|
}
|
||||||
|
return methods
|
||||||
|
}
|
||||||
|
|
||||||
|
func standardMethodArgumentDecode(m Method, sender string, msg *Message, body []interface{}) ([]interface{}, error) {
|
||||||
|
pointers := make([]interface{}, m.NumArguments())
|
||||||
|
decode := make([]interface{}, 0, len(body))
|
||||||
|
|
||||||
|
for i := 0; i < m.NumArguments(); i++ {
|
||||||
|
tp := reflect.TypeOf(m.ArgumentValue(i))
|
||||||
|
val := reflect.New(tp)
|
||||||
|
pointers[i] = val.Interface()
|
||||||
|
if tp == reflect.TypeOf((*Sender)(nil)).Elem() {
|
||||||
|
val.Elem().SetString(sender)
|
||||||
|
} else if tp == reflect.TypeOf((*Message)(nil)).Elem() {
|
||||||
|
val.Elem().Set(reflect.ValueOf(*msg))
|
||||||
|
} else {
|
||||||
|
decode = append(decode, pointers[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(decode) != len(body) {
|
||||||
|
return nil, ErrMsgInvalidArg
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := Store(body, decode...); err != nil {
|
||||||
|
return nil, ErrMsgInvalidArg
|
||||||
|
}
|
||||||
|
|
||||||
|
return pointers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) decodeArguments(m Method, sender string, msg *Message) ([]interface{}, error) {
|
||||||
|
if decoder, ok := m.(ArgumentDecoder); ok {
|
||||||
|
return decoder.DecodeArguments(conn, sender, msg, msg.Body)
|
||||||
|
}
|
||||||
|
return standardMethodArgumentDecode(m, sender, msg, msg.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleCall handles the given method call (i.e. looks if it's one of the
|
||||||
|
// pre-implemented ones and searches for a corresponding handler if not).
|
||||||
|
func (conn *Conn) handleCall(msg *Message) {
|
||||||
|
name := msg.Headers[FieldMember].value.(string)
|
||||||
|
path := msg.Headers[FieldPath].value.(ObjectPath)
|
||||||
|
ifaceName, _ := msg.Headers[FieldInterface].value.(string)
|
||||||
|
sender, hasSender := msg.Headers[FieldSender].value.(string)
|
||||||
|
serial := msg.serial
|
||||||
|
if ifaceName == "org.freedesktop.DBus.Peer" {
|
||||||
|
switch name {
|
||||||
|
case "Ping":
|
||||||
|
conn.sendReply(sender, serial)
|
||||||
|
case "GetMachineId":
|
||||||
|
conn.sendReply(sender, serial, conn.uuid)
|
||||||
|
default:
|
||||||
|
conn.sendError(ErrMsgUnknownMethod, sender, serial)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(name) == 0 {
|
||||||
|
conn.sendError(ErrMsgUnknownMethod, sender, serial)
|
||||||
|
}
|
||||||
|
|
||||||
|
object, ok := conn.handler.LookupObject(path)
|
||||||
|
if !ok {
|
||||||
|
conn.sendError(ErrMsgNoObject, sender, serial)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
iface, exists := object.LookupInterface(ifaceName)
|
||||||
|
if !exists {
|
||||||
|
conn.sendError(ErrMsgUnknownInterface, sender, serial)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
m, exists := iface.LookupMethod(name)
|
||||||
|
if !exists {
|
||||||
|
conn.sendError(ErrMsgUnknownMethod, sender, serial)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
args, err := conn.decodeArguments(m, sender, msg)
|
||||||
|
if err != nil {
|
||||||
|
conn.sendError(err, sender, serial)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ret, err := m.Call(args...)
|
||||||
|
if err != nil {
|
||||||
|
conn.sendError(err, sender, serial)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.Flags&FlagNoReplyExpected == 0 {
|
||||||
|
reply := new(Message)
|
||||||
|
reply.Type = TypeMethodReply
|
||||||
|
reply.serial = conn.getSerial()
|
||||||
|
reply.Headers = make(map[HeaderField]Variant)
|
||||||
|
if hasSender {
|
||||||
|
reply.Headers[FieldDestination] = msg.Headers[FieldSender]
|
||||||
|
}
|
||||||
|
reply.Headers[FieldReplySerial] = MakeVariant(msg.serial)
|
||||||
|
reply.Body = make([]interface{}, len(ret))
|
||||||
|
for i := 0; i < len(ret); i++ {
|
||||||
|
reply.Body[i] = ret[i]
|
||||||
|
}
|
||||||
|
reply.Headers[FieldSignature] = MakeVariant(SignatureOf(reply.Body...))
|
||||||
|
conn.outLck.RLock()
|
||||||
|
if !conn.closed {
|
||||||
|
conn.out <- reply
|
||||||
|
}
|
||||||
|
conn.outLck.RUnlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit emits the given signal on the message bus. The name parameter must be
|
||||||
|
// formatted as "interface.member", e.g., "org.freedesktop.DBus.NameLost".
|
||||||
|
func (conn *Conn) Emit(path ObjectPath, name string, values ...interface{}) error {
|
||||||
|
if !path.IsValid() {
|
||||||
|
return errors.New("dbus: invalid object path")
|
||||||
|
}
|
||||||
|
i := strings.LastIndex(name, ".")
|
||||||
|
if i == -1 {
|
||||||
|
return errors.New("dbus: invalid method name")
|
||||||
|
}
|
||||||
|
iface := name[:i]
|
||||||
|
member := name[i+1:]
|
||||||
|
if !isValidMember(member) {
|
||||||
|
return errors.New("dbus: invalid method name")
|
||||||
|
}
|
||||||
|
if !isValidInterface(iface) {
|
||||||
|
return errors.New("dbus: invalid interface name")
|
||||||
|
}
|
||||||
|
msg := new(Message)
|
||||||
|
msg.Type = TypeSignal
|
||||||
|
msg.serial = conn.getSerial()
|
||||||
|
msg.Headers = make(map[HeaderField]Variant)
|
||||||
|
msg.Headers[FieldInterface] = MakeVariant(iface)
|
||||||
|
msg.Headers[FieldMember] = MakeVariant(member)
|
||||||
|
msg.Headers[FieldPath] = MakeVariant(path)
|
||||||
|
msg.Body = values
|
||||||
|
if len(values) > 0 {
|
||||||
|
msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...))
|
||||||
|
}
|
||||||
|
conn.outLck.RLock()
|
||||||
|
defer conn.outLck.RUnlock()
|
||||||
|
if conn.closed {
|
||||||
|
return ErrClosed
|
||||||
|
}
|
||||||
|
conn.out <- msg
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export registers the given value to be exported as an object on the
|
||||||
|
// message bus.
|
||||||
|
//
|
||||||
|
// If a method call on the given path and interface is received, an exported
|
||||||
|
// method with the same name is called with v as the receiver if the
|
||||||
|
// parameters match and the last return value is of type *Error. If this
|
||||||
|
// *Error is not nil, it is sent back to the caller as an error.
|
||||||
|
// Otherwise, a method reply is sent with the other return values as its body.
|
||||||
|
//
|
||||||
|
// Any parameters with the special type Sender are set to the sender of the
|
||||||
|
// dbus message when the method is called. Parameters of this type do not
|
||||||
|
// contribute to the dbus signature of the method (i.e. the method is exposed
|
||||||
|
// as if the parameters of type Sender were not there).
|
||||||
|
//
|
||||||
|
// Similarly, any parameters with the type Message are set to the raw message
|
||||||
|
// received on the bus. Again, parameters of this type do not contribute to the
|
||||||
|
// dbus signature of the method.
|
||||||
|
//
|
||||||
|
// Every method call is executed in a new goroutine, so the method may be called
|
||||||
|
// in multiple goroutines at once.
|
||||||
|
//
|
||||||
|
// Method calls on the interface org.freedesktop.DBus.Peer will be automatically
|
||||||
|
// handled for every object.
|
||||||
|
//
|
||||||
|
// Passing nil as the first parameter will cause conn to cease handling calls on
|
||||||
|
// the given combination of path and interface.
|
||||||
|
//
|
||||||
|
// Export returns an error if path is not a valid path name.
|
||||||
|
func (conn *Conn) Export(v interface{}, path ObjectPath, iface string) error {
|
||||||
|
return conn.ExportWithMap(v, nil, path, iface)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportWithMap works exactly like Export but provides the ability to remap
|
||||||
|
// method names (e.g. export a lower-case method).
|
||||||
|
//
|
||||||
|
// The keys in the map are the real method names (exported on the struct), and
|
||||||
|
// the values are the method names to be exported on DBus.
|
||||||
|
func (conn *Conn) ExportWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string) error {
|
||||||
|
return conn.export(getMethods(v, mapping), path, iface, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportSubtree works exactly like Export but registers the given value for
|
||||||
|
// an entire subtree rather under the root path provided.
|
||||||
|
//
|
||||||
|
// In order to make this useful, one parameter in each of the value's exported
|
||||||
|
// methods should be a Message, in which case it will contain the raw message
|
||||||
|
// (allowing one to get access to the path that caused the method to be called).
|
||||||
|
//
|
||||||
|
// Note that more specific export paths take precedence over less specific. For
|
||||||
|
// example, a method call using the ObjectPath /foo/bar/baz will call a method
|
||||||
|
// exported on /foo/bar before a method exported on /foo.
|
||||||
|
func (conn *Conn) ExportSubtree(v interface{}, path ObjectPath, iface string) error {
|
||||||
|
return conn.ExportSubtreeWithMap(v, nil, path, iface)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportSubtreeWithMap works exactly like ExportSubtree but provides the
|
||||||
|
// ability to remap method names (e.g. export a lower-case method).
|
||||||
|
//
|
||||||
|
// The keys in the map are the real method names (exported on the struct), and
|
||||||
|
// the values are the method names to be exported on DBus.
|
||||||
|
func (conn *Conn) ExportSubtreeWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string) error {
|
||||||
|
return conn.export(getMethods(v, mapping), path, iface, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportMethodTable like Export registers the given methods as an object
|
||||||
|
// on the message bus. Unlike Export the it uses a method table to define
|
||||||
|
// the object instead of a native go object.
|
||||||
|
//
|
||||||
|
// The method table is a map from method name to function closure
|
||||||
|
// representing the method. This allows an object exported on the bus to not
|
||||||
|
// necessarily be a native go object. It can be useful for generating exposed
|
||||||
|
// methods on the fly.
|
||||||
|
//
|
||||||
|
// Any non-function objects in the method table are ignored.
|
||||||
|
func (conn *Conn) ExportMethodTable(methods map[string]interface{}, path ObjectPath, iface string) error {
|
||||||
|
return conn.exportMethodTable(methods, path, iface, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Like ExportSubtree, but with the same caveats as ExportMethodTable.
|
||||||
|
func (conn *Conn) ExportSubtreeMethodTable(methods map[string]interface{}, path ObjectPath, iface string) error {
|
||||||
|
return conn.exportMethodTable(methods, path, iface, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) exportMethodTable(methods map[string]interface{}, path ObjectPath, iface string, includeSubtree bool) error {
|
||||||
|
out := make(map[string]reflect.Value)
|
||||||
|
for name, method := range methods {
|
||||||
|
rval := reflect.ValueOf(method)
|
||||||
|
if rval.Kind() != reflect.Func {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t := rval.Type()
|
||||||
|
// only track valid methods must return *Error as last arg
|
||||||
|
if t.NumOut() == 0 ||
|
||||||
|
t.Out(t.NumOut()-1) != reflect.TypeOf(&ErrMsgInvalidArg) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out[name] = rval
|
||||||
|
}
|
||||||
|
return conn.export(out, path, iface, includeSubtree)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) unexport(h *defaultHandler, path ObjectPath, iface string) error {
|
||||||
|
if h.PathExists(path) {
|
||||||
|
obj := h.objects[path]
|
||||||
|
obj.DeleteInterface(iface)
|
||||||
|
if len(obj.interfaces) == 0 {
|
||||||
|
h.DeleteObject(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// exportWithMap is the worker function for all exports/registrations.
|
||||||
|
func (conn *Conn) export(methods map[string]reflect.Value, path ObjectPath, iface string, includeSubtree bool) error {
|
||||||
|
h, ok := conn.handler.(*defaultHandler)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf(
|
||||||
|
`dbus: export only allowed on the default hander handler have %T"`,
|
||||||
|
conn.handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !path.IsValid() {
|
||||||
|
return fmt.Errorf(`dbus: Invalid path name: "%s"`, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove a previous export if the interface is nil
|
||||||
|
if methods == nil {
|
||||||
|
return conn.unexport(h, path, iface)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is the first handler for this path, make a new map to hold all
|
||||||
|
// handlers for this path.
|
||||||
|
if !h.PathExists(path) {
|
||||||
|
h.AddObject(path, newExportedObject())
|
||||||
|
}
|
||||||
|
|
||||||
|
exportedMethods := make(map[string]Method)
|
||||||
|
for name, method := range methods {
|
||||||
|
exportedMethods[name] = exportedMethod{method}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, save this handler
|
||||||
|
obj := h.objects[path]
|
||||||
|
obj.AddInterface(iface, newExportedIntf(exportedMethods, includeSubtree))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReleaseName calls org.freedesktop.DBus.ReleaseName and awaits a response.
|
||||||
|
func (conn *Conn) ReleaseName(name string) (ReleaseNameReply, error) {
|
||||||
|
var r uint32
|
||||||
|
err := conn.busObj.Call("org.freedesktop.DBus.ReleaseName", 0, name).Store(&r)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return ReleaseNameReply(r), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestName calls org.freedesktop.DBus.RequestName and awaits a response.
|
||||||
|
func (conn *Conn) RequestName(name string, flags RequestNameFlags) (RequestNameReply, error) {
|
||||||
|
var r uint32
|
||||||
|
err := conn.busObj.Call("org.freedesktop.DBus.RequestName", 0, name, flags).Store(&r)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return RequestNameReply(r), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReleaseNameReply is the reply to a ReleaseName call.
|
||||||
|
type ReleaseNameReply uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
ReleaseNameReplyReleased ReleaseNameReply = 1 + iota
|
||||||
|
ReleaseNameReplyNonExistent
|
||||||
|
ReleaseNameReplyNotOwner
|
||||||
|
)
|
||||||
|
|
||||||
|
// RequestNameFlags represents the possible flags for a RequestName call.
|
||||||
|
type RequestNameFlags uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
NameFlagAllowReplacement RequestNameFlags = 1 << iota
|
||||||
|
NameFlagReplaceExisting
|
||||||
|
NameFlagDoNotQueue
|
||||||
|
)
|
||||||
|
|
||||||
|
// RequestNameReply is the reply to a RequestName call.
|
||||||
|
type RequestNameReply uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
RequestNameReplyPrimaryOwner RequestNameReply = 1 + iota
|
||||||
|
RequestNameReplyInQueue
|
||||||
|
RequestNameReplyExists
|
||||||
|
RequestNameReplyAlreadyOwner
|
||||||
|
)
|
28
vendor/github.com/godbus/dbus/homedir.go
generated
vendored
Normal file
28
vendor/github.com/godbus/dbus/homedir.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
homeDir string
|
||||||
|
homeDirLock sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
|
func getHomeDir() string {
|
||||||
|
homeDirLock.Lock()
|
||||||
|
defer homeDirLock.Unlock()
|
||||||
|
|
||||||
|
if homeDir != "" {
|
||||||
|
return homeDir
|
||||||
|
}
|
||||||
|
|
||||||
|
homeDir = os.Getenv("HOME")
|
||||||
|
if homeDir != "" {
|
||||||
|
return homeDir
|
||||||
|
}
|
||||||
|
|
||||||
|
homeDir = lookupHomeDir()
|
||||||
|
return homeDir
|
||||||
|
}
|
15
vendor/github.com/godbus/dbus/homedir_dynamic.go
generated
vendored
Normal file
15
vendor/github.com/godbus/dbus/homedir_dynamic.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// +build !static_build
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os/user"
|
||||||
|
)
|
||||||
|
|
||||||
|
func lookupHomeDir() string {
|
||||||
|
u, err := user.Current()
|
||||||
|
if err != nil {
|
||||||
|
return "/"
|
||||||
|
}
|
||||||
|
return u.HomeDir
|
||||||
|
}
|
45
vendor/github.com/godbus/dbus/homedir_static.go
generated
vendored
Normal file
45
vendor/github.com/godbus/dbus/homedir_static.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// +build static_build
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func lookupHomeDir() string {
|
||||||
|
myUid := os.Getuid()
|
||||||
|
|
||||||
|
f, err := os.Open("/etc/passwd")
|
||||||
|
if err != nil {
|
||||||
|
return "/"
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
s := bufio.NewScanner(f)
|
||||||
|
|
||||||
|
for s.Scan() {
|
||||||
|
if err := s.Err(); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
line := strings.TrimSpace(s.Text())
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Split(line, ":")
|
||||||
|
|
||||||
|
if len(parts) >= 6 {
|
||||||
|
uid, err := strconv.Atoi(parts[2])
|
||||||
|
if err == nil && uid == myUid {
|
||||||
|
return parts[5]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to / if we can't get a better value
|
||||||
|
return "/"
|
||||||
|
}
|
353
vendor/github.com/godbus/dbus/message.go
generated
vendored
Normal file
353
vendor/github.com/godbus/dbus/message.go
generated
vendored
Normal file
@ -0,0 +1,353 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
const protoVersion byte = 1
|
||||||
|
|
||||||
|
// Flags represents the possible flags of a D-Bus message.
|
||||||
|
type Flags byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
// FlagNoReplyExpected signals that the message is not expected to generate
|
||||||
|
// a reply. If this flag is set on outgoing messages, any possible reply
|
||||||
|
// will be discarded.
|
||||||
|
FlagNoReplyExpected Flags = 1 << iota
|
||||||
|
// FlagNoAutoStart signals that the message bus should not automatically
|
||||||
|
// start an application when handling this message.
|
||||||
|
FlagNoAutoStart
|
||||||
|
// FlagAllowInteractiveAuthorization may be set on a method call
|
||||||
|
// message to inform the receiving side that the caller is prepared
|
||||||
|
// to wait for interactive authorization, which might take a
|
||||||
|
// considerable time to complete. For instance, if this flag is set,
|
||||||
|
// it would be appropriate to query the user for passwords or
|
||||||
|
// confirmation via Polkit or a similar framework.
|
||||||
|
FlagAllowInteractiveAuthorization
|
||||||
|
)
|
||||||
|
|
||||||
|
// Type represents the possible types of a D-Bus message.
|
||||||
|
type Type byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
TypeMethodCall Type = 1 + iota
|
||||||
|
TypeMethodReply
|
||||||
|
TypeError
|
||||||
|
TypeSignal
|
||||||
|
typeMax
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t Type) String() string {
|
||||||
|
switch t {
|
||||||
|
case TypeMethodCall:
|
||||||
|
return "method call"
|
||||||
|
case TypeMethodReply:
|
||||||
|
return "reply"
|
||||||
|
case TypeError:
|
||||||
|
return "error"
|
||||||
|
case TypeSignal:
|
||||||
|
return "signal"
|
||||||
|
}
|
||||||
|
return "invalid"
|
||||||
|
}
|
||||||
|
|
||||||
|
// HeaderField represents the possible byte codes for the headers
|
||||||
|
// of a D-Bus message.
|
||||||
|
type HeaderField byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
FieldPath HeaderField = 1 + iota
|
||||||
|
FieldInterface
|
||||||
|
FieldMember
|
||||||
|
FieldErrorName
|
||||||
|
FieldReplySerial
|
||||||
|
FieldDestination
|
||||||
|
FieldSender
|
||||||
|
FieldSignature
|
||||||
|
FieldUnixFDs
|
||||||
|
fieldMax
|
||||||
|
)
|
||||||
|
|
||||||
|
// An InvalidMessageError describes the reason why a D-Bus message is regarded as
|
||||||
|
// invalid.
|
||||||
|
type InvalidMessageError string
|
||||||
|
|
||||||
|
func (e InvalidMessageError) Error() string {
|
||||||
|
return "dbus: invalid message: " + string(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fieldType are the types of the various header fields.
|
||||||
|
var fieldTypes = [fieldMax]reflect.Type{
|
||||||
|
FieldPath: objectPathType,
|
||||||
|
FieldInterface: stringType,
|
||||||
|
FieldMember: stringType,
|
||||||
|
FieldErrorName: stringType,
|
||||||
|
FieldReplySerial: uint32Type,
|
||||||
|
FieldDestination: stringType,
|
||||||
|
FieldSender: stringType,
|
||||||
|
FieldSignature: signatureType,
|
||||||
|
FieldUnixFDs: uint32Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
// requiredFields lists the header fields that are required by the different
|
||||||
|
// message types.
|
||||||
|
var requiredFields = [typeMax][]HeaderField{
|
||||||
|
TypeMethodCall: {FieldPath, FieldMember},
|
||||||
|
TypeMethodReply: {FieldReplySerial},
|
||||||
|
TypeError: {FieldErrorName, FieldReplySerial},
|
||||||
|
TypeSignal: {FieldPath, FieldInterface, FieldMember},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message represents a single D-Bus message.
|
||||||
|
type Message struct {
|
||||||
|
Type
|
||||||
|
Flags
|
||||||
|
Headers map[HeaderField]Variant
|
||||||
|
Body []interface{}
|
||||||
|
|
||||||
|
serial uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type header struct {
|
||||||
|
Field byte
|
||||||
|
Variant
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeMessage tries to decode a single message in the D-Bus wire format
|
||||||
|
// from the given reader. The byte order is figured out from the first byte.
|
||||||
|
// The possibly returned error can be an error of the underlying reader, an
|
||||||
|
// InvalidMessageError or a FormatError.
|
||||||
|
func DecodeMessage(rd io.Reader) (msg *Message, err error) {
|
||||||
|
var order binary.ByteOrder
|
||||||
|
var hlength, length uint32
|
||||||
|
var typ, flags, proto byte
|
||||||
|
var headers []header
|
||||||
|
|
||||||
|
b := make([]byte, 1)
|
||||||
|
_, err = rd.Read(b)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch b[0] {
|
||||||
|
case 'l':
|
||||||
|
order = binary.LittleEndian
|
||||||
|
case 'B':
|
||||||
|
order = binary.BigEndian
|
||||||
|
default:
|
||||||
|
return nil, InvalidMessageError("invalid byte order")
|
||||||
|
}
|
||||||
|
|
||||||
|
dec := newDecoder(rd, order)
|
||||||
|
dec.pos = 1
|
||||||
|
|
||||||
|
msg = new(Message)
|
||||||
|
vs, err := dec.Decode(Signature{"yyyuu"})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = Store(vs, &typ, &flags, &proto, &length, &msg.serial); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
msg.Type = Type(typ)
|
||||||
|
msg.Flags = Flags(flags)
|
||||||
|
|
||||||
|
// get the header length separately because we need it later
|
||||||
|
b = make([]byte, 4)
|
||||||
|
_, err = io.ReadFull(rd, b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
binary.Read(bytes.NewBuffer(b), order, &hlength)
|
||||||
|
if hlength+length+16 > 1<<27 {
|
||||||
|
return nil, InvalidMessageError("message is too long")
|
||||||
|
}
|
||||||
|
dec = newDecoder(io.MultiReader(bytes.NewBuffer(b), rd), order)
|
||||||
|
dec.pos = 12
|
||||||
|
vs, err = dec.Decode(Signature{"a(yv)"})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = Store(vs, &headers); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.Headers = make(map[HeaderField]Variant)
|
||||||
|
for _, v := range headers {
|
||||||
|
msg.Headers[HeaderField(v.Field)] = v.Variant
|
||||||
|
}
|
||||||
|
|
||||||
|
dec.align(8)
|
||||||
|
body := make([]byte, int(length))
|
||||||
|
if length != 0 {
|
||||||
|
_, err := io.ReadFull(rd, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = msg.IsValid(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sig, _ := msg.Headers[FieldSignature].value.(Signature)
|
||||||
|
if sig.str != "" {
|
||||||
|
buf := bytes.NewBuffer(body)
|
||||||
|
dec = newDecoder(buf, order)
|
||||||
|
vs, err := dec.Decode(sig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
msg.Body = vs
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeTo encodes and sends a message to the given writer. The byte order must
|
||||||
|
// be either binary.LittleEndian or binary.BigEndian. If the message is not
|
||||||
|
// valid or an error occurs when writing, an error is returned.
|
||||||
|
func (msg *Message) EncodeTo(out io.Writer, order binary.ByteOrder) error {
|
||||||
|
if err := msg.IsValid(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var vs [7]interface{}
|
||||||
|
switch order {
|
||||||
|
case binary.LittleEndian:
|
||||||
|
vs[0] = byte('l')
|
||||||
|
case binary.BigEndian:
|
||||||
|
vs[0] = byte('B')
|
||||||
|
default:
|
||||||
|
return errors.New("dbus: invalid byte order")
|
||||||
|
}
|
||||||
|
body := new(bytes.Buffer)
|
||||||
|
enc := newEncoder(body, order)
|
||||||
|
if len(msg.Body) != 0 {
|
||||||
|
enc.Encode(msg.Body...)
|
||||||
|
}
|
||||||
|
vs[1] = msg.Type
|
||||||
|
vs[2] = msg.Flags
|
||||||
|
vs[3] = protoVersion
|
||||||
|
vs[4] = uint32(len(body.Bytes()))
|
||||||
|
vs[5] = msg.serial
|
||||||
|
headers := make([]header, 0, len(msg.Headers))
|
||||||
|
for k, v := range msg.Headers {
|
||||||
|
headers = append(headers, header{byte(k), v})
|
||||||
|
}
|
||||||
|
vs[6] = headers
|
||||||
|
var buf bytes.Buffer
|
||||||
|
enc = newEncoder(&buf, order)
|
||||||
|
enc.Encode(vs[:]...)
|
||||||
|
enc.align(8)
|
||||||
|
body.WriteTo(&buf)
|
||||||
|
if buf.Len() > 1<<27 {
|
||||||
|
return InvalidMessageError("message is too long")
|
||||||
|
}
|
||||||
|
if _, err := buf.WriteTo(out); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValid checks whether msg is a valid message and returns an
|
||||||
|
// InvalidMessageError if it is not.
|
||||||
|
func (msg *Message) IsValid() error {
|
||||||
|
if msg.Flags & ^(FlagNoAutoStart|FlagNoReplyExpected|FlagAllowInteractiveAuthorization) != 0 {
|
||||||
|
return InvalidMessageError("invalid flags")
|
||||||
|
}
|
||||||
|
if msg.Type == 0 || msg.Type >= typeMax {
|
||||||
|
return InvalidMessageError("invalid message type")
|
||||||
|
}
|
||||||
|
for k, v := range msg.Headers {
|
||||||
|
if k == 0 || k >= fieldMax {
|
||||||
|
return InvalidMessageError("invalid header")
|
||||||
|
}
|
||||||
|
if reflect.TypeOf(v.value) != fieldTypes[k] {
|
||||||
|
return InvalidMessageError("invalid type of header field")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, v := range requiredFields[msg.Type] {
|
||||||
|
if _, ok := msg.Headers[v]; !ok {
|
||||||
|
return InvalidMessageError("missing required header")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if path, ok := msg.Headers[FieldPath]; ok {
|
||||||
|
if !path.value.(ObjectPath).IsValid() {
|
||||||
|
return InvalidMessageError("invalid path name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if iface, ok := msg.Headers[FieldInterface]; ok {
|
||||||
|
if !isValidInterface(iface.value.(string)) {
|
||||||
|
return InvalidMessageError("invalid interface name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if member, ok := msg.Headers[FieldMember]; ok {
|
||||||
|
if !isValidMember(member.value.(string)) {
|
||||||
|
return InvalidMessageError("invalid member name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if errname, ok := msg.Headers[FieldErrorName]; ok {
|
||||||
|
if !isValidInterface(errname.value.(string)) {
|
||||||
|
return InvalidMessageError("invalid error name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(msg.Body) != 0 {
|
||||||
|
if _, ok := msg.Headers[FieldSignature]; !ok {
|
||||||
|
return InvalidMessageError("missing signature")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serial returns the message's serial number. The returned value is only valid
|
||||||
|
// for messages received by eavesdropping.
|
||||||
|
func (msg *Message) Serial() uint32 {
|
||||||
|
return msg.serial
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string representation of a message similar to the format of
|
||||||
|
// dbus-monitor.
|
||||||
|
func (msg *Message) String() string {
|
||||||
|
if err := msg.IsValid(); err != nil {
|
||||||
|
return "<invalid>"
|
||||||
|
}
|
||||||
|
s := msg.Type.String()
|
||||||
|
if v, ok := msg.Headers[FieldSender]; ok {
|
||||||
|
s += " from " + v.value.(string)
|
||||||
|
}
|
||||||
|
if v, ok := msg.Headers[FieldDestination]; ok {
|
||||||
|
s += " to " + v.value.(string)
|
||||||
|
}
|
||||||
|
s += " serial " + strconv.FormatUint(uint64(msg.serial), 10)
|
||||||
|
if v, ok := msg.Headers[FieldReplySerial]; ok {
|
||||||
|
s += " reply_serial " + strconv.FormatUint(uint64(v.value.(uint32)), 10)
|
||||||
|
}
|
||||||
|
if v, ok := msg.Headers[FieldUnixFDs]; ok {
|
||||||
|
s += " unixfds " + strconv.FormatUint(uint64(v.value.(uint32)), 10)
|
||||||
|
}
|
||||||
|
if v, ok := msg.Headers[FieldPath]; ok {
|
||||||
|
s += " path " + string(v.value.(ObjectPath))
|
||||||
|
}
|
||||||
|
if v, ok := msg.Headers[FieldInterface]; ok {
|
||||||
|
s += " interface " + v.value.(string)
|
||||||
|
}
|
||||||
|
if v, ok := msg.Headers[FieldErrorName]; ok {
|
||||||
|
s += " error " + v.value.(string)
|
||||||
|
}
|
||||||
|
if v, ok := msg.Headers[FieldMember]; ok {
|
||||||
|
s += " member " + v.value.(string)
|
||||||
|
}
|
||||||
|
if len(msg.Body) != 0 {
|
||||||
|
s += "\n"
|
||||||
|
}
|
||||||
|
for i, v := range msg.Body {
|
||||||
|
s += " " + MakeVariant(v).String()
|
||||||
|
if i != len(msg.Body)-1 {
|
||||||
|
s += "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
136
vendor/github.com/godbus/dbus/object.go
generated
vendored
Normal file
136
vendor/github.com/godbus/dbus/object.go
generated
vendored
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BusObject is the interface of a remote object on which methods can be
|
||||||
|
// invoked.
|
||||||
|
type BusObject interface {
|
||||||
|
Call(method string, flags Flags, args ...interface{}) *Call
|
||||||
|
Go(method string, flags Flags, ch chan *Call, args ...interface{}) *Call
|
||||||
|
GetProperty(p string) (Variant, error)
|
||||||
|
Destination() string
|
||||||
|
Path() ObjectPath
|
||||||
|
}
|
||||||
|
|
||||||
|
// Object represents a remote object on which methods can be invoked.
|
||||||
|
type Object struct {
|
||||||
|
conn *Conn
|
||||||
|
dest string
|
||||||
|
path ObjectPath
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call calls a method with (*Object).Go and waits for its reply.
|
||||||
|
func (o *Object) Call(method string, flags Flags, args ...interface{}) *Call {
|
||||||
|
return <-o.Go(method, flags, make(chan *Call, 1), args...).Done
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMatchSignal subscribes BusObject to signals from specified interface and
|
||||||
|
// method (member).
|
||||||
|
func (o *Object) AddMatchSignal(iface, member string) *Call {
|
||||||
|
return o.Call(
|
||||||
|
"org.freedesktop.DBus.AddMatch",
|
||||||
|
0,
|
||||||
|
"type='signal',interface='"+iface+"',member='"+member+"'",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go calls a method with the given arguments asynchronously. It returns a
|
||||||
|
// Call structure representing this method call. The passed channel will
|
||||||
|
// return the same value once the call is done. If ch is nil, a new channel
|
||||||
|
// will be allocated. Otherwise, ch has to be buffered or Go will panic.
|
||||||
|
//
|
||||||
|
// If the flags include FlagNoReplyExpected, ch is ignored and a Call structure
|
||||||
|
// is returned of which only the Err member is valid.
|
||||||
|
//
|
||||||
|
// If the method parameter contains a dot ('.'), the part before the last dot
|
||||||
|
// specifies the interface on which the method is called.
|
||||||
|
func (o *Object) Go(method string, flags Flags, ch chan *Call, args ...interface{}) *Call {
|
||||||
|
iface := ""
|
||||||
|
i := strings.LastIndex(method, ".")
|
||||||
|
if i != -1 {
|
||||||
|
iface = method[:i]
|
||||||
|
}
|
||||||
|
method = method[i+1:]
|
||||||
|
msg := new(Message)
|
||||||
|
msg.Type = TypeMethodCall
|
||||||
|
msg.serial = o.conn.getSerial()
|
||||||
|
msg.Flags = flags & (FlagNoAutoStart | FlagNoReplyExpected)
|
||||||
|
msg.Headers = make(map[HeaderField]Variant)
|
||||||
|
msg.Headers[FieldPath] = MakeVariant(o.path)
|
||||||
|
msg.Headers[FieldDestination] = MakeVariant(o.dest)
|
||||||
|
msg.Headers[FieldMember] = MakeVariant(method)
|
||||||
|
if iface != "" {
|
||||||
|
msg.Headers[FieldInterface] = MakeVariant(iface)
|
||||||
|
}
|
||||||
|
msg.Body = args
|
||||||
|
if len(args) > 0 {
|
||||||
|
msg.Headers[FieldSignature] = MakeVariant(SignatureOf(args...))
|
||||||
|
}
|
||||||
|
if msg.Flags&FlagNoReplyExpected == 0 {
|
||||||
|
if ch == nil {
|
||||||
|
ch = make(chan *Call, 10)
|
||||||
|
} else if cap(ch) == 0 {
|
||||||
|
panic("dbus: unbuffered channel passed to (*Object).Go")
|
||||||
|
}
|
||||||
|
call := &Call{
|
||||||
|
Destination: o.dest,
|
||||||
|
Path: o.path,
|
||||||
|
Method: method,
|
||||||
|
Args: args,
|
||||||
|
Done: ch,
|
||||||
|
}
|
||||||
|
o.conn.callsLck.Lock()
|
||||||
|
o.conn.calls[msg.serial] = call
|
||||||
|
o.conn.callsLck.Unlock()
|
||||||
|
o.conn.outLck.RLock()
|
||||||
|
if o.conn.closed {
|
||||||
|
call.Err = ErrClosed
|
||||||
|
call.Done <- call
|
||||||
|
} else {
|
||||||
|
o.conn.out <- msg
|
||||||
|
}
|
||||||
|
o.conn.outLck.RUnlock()
|
||||||
|
return call
|
||||||
|
}
|
||||||
|
o.conn.outLck.RLock()
|
||||||
|
defer o.conn.outLck.RUnlock()
|
||||||
|
if o.conn.closed {
|
||||||
|
return &Call{Err: ErrClosed}
|
||||||
|
}
|
||||||
|
o.conn.out <- msg
|
||||||
|
return &Call{Err: nil}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetProperty calls org.freedesktop.DBus.Properties.GetProperty on the given
|
||||||
|
// object. The property name must be given in interface.member notation.
|
||||||
|
func (o *Object) GetProperty(p string) (Variant, error) {
|
||||||
|
idx := strings.LastIndex(p, ".")
|
||||||
|
if idx == -1 || idx+1 == len(p) {
|
||||||
|
return Variant{}, errors.New("dbus: invalid property " + p)
|
||||||
|
}
|
||||||
|
|
||||||
|
iface := p[:idx]
|
||||||
|
prop := p[idx+1:]
|
||||||
|
|
||||||
|
result := Variant{}
|
||||||
|
err := o.Call("org.freedesktop.DBus.Properties.Get", 0, iface, prop).Store(&result)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return Variant{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destination returns the destination that calls on (o *Object) are sent to.
|
||||||
|
func (o *Object) Destination() string {
|
||||||
|
return o.dest
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path returns the path that calls on (o *Object") are sent to.
|
||||||
|
func (o *Object) Path() ObjectPath {
|
||||||
|
return o.path
|
||||||
|
}
|
89
vendor/github.com/godbus/dbus/server_interfaces.go
generated
vendored
Normal file
89
vendor/github.com/godbus/dbus/server_interfaces.go
generated
vendored
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
// Terminator allows a handler to implement a shutdown mechanism that
|
||||||
|
// is called when the connection terminates.
|
||||||
|
type Terminator interface {
|
||||||
|
Terminate()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handler is the representation of a D-Bus Application.
|
||||||
|
//
|
||||||
|
// The Handler must have a way to lookup objects given
|
||||||
|
// an ObjectPath. The returned object must implement the
|
||||||
|
// ServerObject interface.
|
||||||
|
type Handler interface {
|
||||||
|
LookupObject(path ObjectPath) (ServerObject, bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerObject is the representation of an D-Bus Object.
|
||||||
|
//
|
||||||
|
// Objects are registered at a path for a given Handler.
|
||||||
|
// The Objects implement D-Bus interfaces. The semantics
|
||||||
|
// of Interface lookup is up to the implementation of
|
||||||
|
// the ServerObject. The ServerObject implementation may
|
||||||
|
// choose to implement empty string as a valid interface
|
||||||
|
// represeting all methods or not per the D-Bus specification.
|
||||||
|
type ServerObject interface {
|
||||||
|
LookupInterface(name string) (Interface, bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Interface is the representation of a D-Bus Interface.
|
||||||
|
//
|
||||||
|
// Interfaces are a grouping of methods implemented by the Objects.
|
||||||
|
// Interfaces are responsible for routing method calls.
|
||||||
|
type Interface interface {
|
||||||
|
LookupMethod(name string) (Method, bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Method represents the exposed methods on D-Bus.
|
||||||
|
type Method interface {
|
||||||
|
// Call requires that all arguments are decoded before being passed to it.
|
||||||
|
Call(args ...interface{}) ([]interface{}, error)
|
||||||
|
NumArguments() int
|
||||||
|
NumReturns() int
|
||||||
|
// ArgumentValue returns a representative value for the argument at position
|
||||||
|
// it should be of the proper type. reflect.Zero would be a good mechanism
|
||||||
|
// to use for this Value.
|
||||||
|
ArgumentValue(position int) interface{}
|
||||||
|
// ReturnValue returns a representative value for the return at position
|
||||||
|
// it should be of the proper type. reflect.Zero would be a good mechanism
|
||||||
|
// to use for this Value.
|
||||||
|
ReturnValue(position int) interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Argument Decoder can decode arguments using the non-standard mechanism
|
||||||
|
//
|
||||||
|
// If a method implements this interface then the non-standard
|
||||||
|
// decoder will be used.
|
||||||
|
//
|
||||||
|
// Method arguments must be decoded from the message.
|
||||||
|
// The mechanism for doing this will vary based on the
|
||||||
|
// implementation of the method. A normal approach is provided
|
||||||
|
// as part of this library, but may be replaced with
|
||||||
|
// any other decoding scheme.
|
||||||
|
type ArgumentDecoder interface {
|
||||||
|
// To decode the arguments of a method the sender and message are
|
||||||
|
// provided incase the semantics of the implementer provides access
|
||||||
|
// to these as part of the method invocation.
|
||||||
|
DecodeArguments(conn *Conn, sender string, msg *Message, args []interface{}) ([]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A SignalHandler is responsible for delivering a signal.
|
||||||
|
//
|
||||||
|
// Signal delivery may be changed from the default channel
|
||||||
|
// based approach by Handlers implementing the SignalHandler
|
||||||
|
// interface.
|
||||||
|
type SignalHandler interface {
|
||||||
|
DeliverSignal(iface, name string, signal *Signal)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A DBusError is used to convert a generic object to a D-Bus error.
|
||||||
|
//
|
||||||
|
// Any custom error mechanism may implement this interface to provide
|
||||||
|
// a custom encoding of the error on D-Bus. By default if a normal
|
||||||
|
// error is returned, it will be encoded as the generic
|
||||||
|
// "org.freedesktop.DBus.Error.Failed" error. By implementing this
|
||||||
|
// interface as well a custom encoding may be provided.
|
||||||
|
type DBusError interface {
|
||||||
|
DBusError() (string, []interface{})
|
||||||
|
}
|
259
vendor/github.com/godbus/dbus/sig.go
generated
vendored
Normal file
259
vendor/github.com/godbus/dbus/sig.go
generated
vendored
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var sigToType = map[byte]reflect.Type{
|
||||||
|
'y': byteType,
|
||||||
|
'b': boolType,
|
||||||
|
'n': int16Type,
|
||||||
|
'q': uint16Type,
|
||||||
|
'i': int32Type,
|
||||||
|
'u': uint32Type,
|
||||||
|
'x': int64Type,
|
||||||
|
't': uint64Type,
|
||||||
|
'd': float64Type,
|
||||||
|
's': stringType,
|
||||||
|
'g': signatureType,
|
||||||
|
'o': objectPathType,
|
||||||
|
'v': variantType,
|
||||||
|
'h': unixFDIndexType,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signature represents a correct type signature as specified by the D-Bus
|
||||||
|
// specification. The zero value represents the empty signature, "".
|
||||||
|
type Signature struct {
|
||||||
|
str string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignatureOf returns the concatenation of all the signatures of the given
|
||||||
|
// values. It panics if one of them is not representable in D-Bus.
|
||||||
|
func SignatureOf(vs ...interface{}) Signature {
|
||||||
|
var s string
|
||||||
|
for _, v := range vs {
|
||||||
|
s += getSignature(reflect.TypeOf(v))
|
||||||
|
}
|
||||||
|
return Signature{s}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignatureOfType returns the signature of the given type. It panics if the
|
||||||
|
// type is not representable in D-Bus.
|
||||||
|
func SignatureOfType(t reflect.Type) Signature {
|
||||||
|
return Signature{getSignature(t)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSignature returns the signature of the given type and panics on unknown types.
|
||||||
|
func getSignature(t reflect.Type) string {
|
||||||
|
// handle simple types first
|
||||||
|
switch t.Kind() {
|
||||||
|
case reflect.Uint8:
|
||||||
|
return "y"
|
||||||
|
case reflect.Bool:
|
||||||
|
return "b"
|
||||||
|
case reflect.Int16:
|
||||||
|
return "n"
|
||||||
|
case reflect.Uint16:
|
||||||
|
return "q"
|
||||||
|
case reflect.Int, reflect.Int32:
|
||||||
|
if t == unixFDType {
|
||||||
|
return "h"
|
||||||
|
}
|
||||||
|
return "i"
|
||||||
|
case reflect.Uint, reflect.Uint32:
|
||||||
|
if t == unixFDIndexType {
|
||||||
|
return "h"
|
||||||
|
}
|
||||||
|
return "u"
|
||||||
|
case reflect.Int64:
|
||||||
|
return "x"
|
||||||
|
case reflect.Uint64:
|
||||||
|
return "t"
|
||||||
|
case reflect.Float64:
|
||||||
|
return "d"
|
||||||
|
case reflect.Ptr:
|
||||||
|
return getSignature(t.Elem())
|
||||||
|
case reflect.String:
|
||||||
|
if t == objectPathType {
|
||||||
|
return "o"
|
||||||
|
}
|
||||||
|
return "s"
|
||||||
|
case reflect.Struct:
|
||||||
|
if t == variantType {
|
||||||
|
return "v"
|
||||||
|
} else if t == signatureType {
|
||||||
|
return "g"
|
||||||
|
}
|
||||||
|
var s string
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
field := t.Field(i)
|
||||||
|
if field.PkgPath == "" && field.Tag.Get("dbus") != "-" {
|
||||||
|
s += getSignature(t.Field(i).Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "(" + s + ")"
|
||||||
|
case reflect.Array, reflect.Slice:
|
||||||
|
return "a" + getSignature(t.Elem())
|
||||||
|
case reflect.Map:
|
||||||
|
if !isKeyType(t.Key()) {
|
||||||
|
panic(InvalidTypeError{t})
|
||||||
|
}
|
||||||
|
return "a{" + getSignature(t.Key()) + getSignature(t.Elem()) + "}"
|
||||||
|
case reflect.Interface:
|
||||||
|
return "v"
|
||||||
|
}
|
||||||
|
panic(InvalidTypeError{t})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseSignature returns the signature represented by this string, or a
|
||||||
|
// SignatureError if the string is not a valid signature.
|
||||||
|
func ParseSignature(s string) (sig Signature, err error) {
|
||||||
|
if len(s) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(s) > 255 {
|
||||||
|
return Signature{""}, SignatureError{s, "too long"}
|
||||||
|
}
|
||||||
|
sig.str = s
|
||||||
|
for err == nil && len(s) != 0 {
|
||||||
|
err, s = validSingle(s, 0)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
sig = Signature{""}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseSignatureMust behaves like ParseSignature, except that it panics if s
|
||||||
|
// is not valid.
|
||||||
|
func ParseSignatureMust(s string) Signature {
|
||||||
|
sig, err := ParseSignature(s)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return sig
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty retruns whether the signature is the empty signature.
|
||||||
|
func (s Signature) Empty() bool {
|
||||||
|
return s.str == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Single returns whether the signature represents a single, complete type.
|
||||||
|
func (s Signature) Single() bool {
|
||||||
|
err, r := validSingle(s.str, 0)
|
||||||
|
return err != nil && r == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the signature's string representation.
|
||||||
|
func (s Signature) String() string {
|
||||||
|
return s.str
|
||||||
|
}
|
||||||
|
|
||||||
|
// A SignatureError indicates that a signature passed to a function or received
|
||||||
|
// on a connection is not a valid signature.
|
||||||
|
type SignatureError struct {
|
||||||
|
Sig string
|
||||||
|
Reason string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e SignatureError) Error() string {
|
||||||
|
return fmt.Sprintf("dbus: invalid signature: %q (%s)", e.Sig, e.Reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to read a single type from this string. If it was successful, err is nil
|
||||||
|
// and rem is the remaining unparsed part. Otherwise, err is a non-nil
|
||||||
|
// SignatureError and rem is "". depth is the current recursion depth which may
|
||||||
|
// not be greater than 64 and should be given as 0 on the first call.
|
||||||
|
func validSingle(s string, depth int) (err error, rem string) {
|
||||||
|
if s == "" {
|
||||||
|
return SignatureError{Sig: s, Reason: "empty signature"}, ""
|
||||||
|
}
|
||||||
|
if depth > 64 {
|
||||||
|
return SignatureError{Sig: s, Reason: "container nesting too deep"}, ""
|
||||||
|
}
|
||||||
|
switch s[0] {
|
||||||
|
case 'y', 'b', 'n', 'q', 'i', 'u', 'x', 't', 'd', 's', 'g', 'o', 'v', 'h':
|
||||||
|
return nil, s[1:]
|
||||||
|
case 'a':
|
||||||
|
if len(s) > 1 && s[1] == '{' {
|
||||||
|
i := findMatching(s[1:], '{', '}')
|
||||||
|
if i == -1 {
|
||||||
|
return SignatureError{Sig: s, Reason: "unmatched '{'"}, ""
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
rem = s[i+1:]
|
||||||
|
s = s[2:i]
|
||||||
|
if err, _ = validSingle(s[:1], depth+1); err != nil {
|
||||||
|
return err, ""
|
||||||
|
}
|
||||||
|
err, nr := validSingle(s[1:], depth+1)
|
||||||
|
if err != nil {
|
||||||
|
return err, ""
|
||||||
|
}
|
||||||
|
if nr != "" {
|
||||||
|
return SignatureError{Sig: s, Reason: "too many types in dict"}, ""
|
||||||
|
}
|
||||||
|
return nil, rem
|
||||||
|
}
|
||||||
|
return validSingle(s[1:], depth+1)
|
||||||
|
case '(':
|
||||||
|
i := findMatching(s, '(', ')')
|
||||||
|
if i == -1 {
|
||||||
|
return SignatureError{Sig: s, Reason: "unmatched ')'"}, ""
|
||||||
|
}
|
||||||
|
rem = s[i+1:]
|
||||||
|
s = s[1:i]
|
||||||
|
for err == nil && s != "" {
|
||||||
|
err, s = validSingle(s, depth+1)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
rem = ""
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return SignatureError{Sig: s, Reason: "invalid type character"}, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func findMatching(s string, left, right rune) int {
|
||||||
|
n := 0
|
||||||
|
for i, v := range s {
|
||||||
|
if v == left {
|
||||||
|
n++
|
||||||
|
} else if v == right {
|
||||||
|
n--
|
||||||
|
}
|
||||||
|
if n == 0 {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// typeFor returns the type of the given signature. It ignores any left over
|
||||||
|
// characters and panics if s doesn't start with a valid type signature.
|
||||||
|
func typeFor(s string) (t reflect.Type) {
|
||||||
|
err, _ := validSingle(s, 0)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, ok := sigToType[s[0]]; ok {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
switch s[0] {
|
||||||
|
case 'a':
|
||||||
|
if s[1] == '{' {
|
||||||
|
i := strings.LastIndex(s, "}")
|
||||||
|
t = reflect.MapOf(sigToType[s[2]], typeFor(s[3:i]))
|
||||||
|
} else {
|
||||||
|
t = reflect.SliceOf(typeFor(s[1:]))
|
||||||
|
}
|
||||||
|
case '(':
|
||||||
|
t = interfacesType
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
6
vendor/github.com/godbus/dbus/transport_darwin.go
generated
vendored
Normal file
6
vendor/github.com/godbus/dbus/transport_darwin.go
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
func (t *unixTransport) SendNullByte() error {
|
||||||
|
_, err := t.Write([]byte{0})
|
||||||
|
return err
|
||||||
|
}
|
50
vendor/github.com/godbus/dbus/transport_generic.go
generated
vendored
Normal file
50
vendor/github.com/godbus/dbus/transport_generic.go
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var nativeEndian binary.ByteOrder
|
||||||
|
|
||||||
|
func detectEndianness() binary.ByteOrder {
|
||||||
|
var x uint32 = 0x01020304
|
||||||
|
if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
|
||||||
|
return binary.BigEndian
|
||||||
|
}
|
||||||
|
return binary.LittleEndian
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
nativeEndian = detectEndianness()
|
||||||
|
}
|
||||||
|
|
||||||
|
type genericTransport struct {
|
||||||
|
io.ReadWriteCloser
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t genericTransport) SendNullByte() error {
|
||||||
|
_, err := t.Write([]byte{0})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t genericTransport) SupportsUnixFDs() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t genericTransport) EnableUnixFDs() {}
|
||||||
|
|
||||||
|
func (t genericTransport) ReadMessage() (*Message, error) {
|
||||||
|
return DecodeMessage(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t genericTransport) SendMessage(msg *Message) error {
|
||||||
|
for _, v := range msg.Body {
|
||||||
|
if _, ok := v.(UnixFD); ok {
|
||||||
|
return errors.New("dbus: unix fd passing not enabled")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return msg.EncodeTo(t, nativeEndian)
|
||||||
|
}
|
43
vendor/github.com/godbus/dbus/transport_tcp.go
generated
vendored
Normal file
43
vendor/github.com/godbus/dbus/transport_tcp.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
//+build !windows
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
transports["tcp"] = newTcpTransport
|
||||||
|
}
|
||||||
|
|
||||||
|
func tcpFamily(keys string) (string, error) {
|
||||||
|
switch getKey(keys, "family") {
|
||||||
|
case "":
|
||||||
|
return "tcp", nil
|
||||||
|
case "ipv4":
|
||||||
|
return "tcp4", nil
|
||||||
|
case "ipv6":
|
||||||
|
return "tcp6", nil
|
||||||
|
default:
|
||||||
|
return "", errors.New("dbus: invalid tcp family (must be ipv4 or ipv6)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTcpTransport(keys string) (transport, error) {
|
||||||
|
host := getKey(keys, "host")
|
||||||
|
port := getKey(keys, "port")
|
||||||
|
if host == "" || port == "" {
|
||||||
|
return nil, errors.New("dbus: unsupported address (must set host and port)")
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol, err := tcpFamily(keys)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
socket, err := net.Dial(protocol, net.JoinHostPort(host, port))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewConn(socket)
|
||||||
|
}
|
196
vendor/github.com/godbus/dbus/transport_unix.go
generated
vendored
Normal file
196
vendor/github.com/godbus/dbus/transport_unix.go
generated
vendored
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
//+build !windows,!solaris
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
type oobReader struct {
|
||||||
|
conn *net.UnixConn
|
||||||
|
oob []byte
|
||||||
|
buf [4096]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *oobReader) Read(b []byte) (n int, err error) {
|
||||||
|
n, oobn, flags, _, err := o.conn.ReadMsgUnix(b, o.buf[:])
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
if flags&syscall.MSG_CTRUNC != 0 {
|
||||||
|
return n, errors.New("dbus: control data truncated (too many fds received)")
|
||||||
|
}
|
||||||
|
o.oob = append(o.oob, o.buf[:oobn]...)
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type unixTransport struct {
|
||||||
|
*net.UnixConn
|
||||||
|
hasUnixFDs bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newUnixTransport(keys string) (transport, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
t := new(unixTransport)
|
||||||
|
abstract := getKey(keys, "abstract")
|
||||||
|
path := getKey(keys, "path")
|
||||||
|
switch {
|
||||||
|
case abstract == "" && path == "":
|
||||||
|
return nil, errors.New("dbus: invalid address (neither path nor abstract set)")
|
||||||
|
case abstract != "" && path == "":
|
||||||
|
t.UnixConn, err = net.DialUnix("unix", nil, &net.UnixAddr{Name: "@" + abstract, Net: "unix"})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return t, nil
|
||||||
|
case abstract == "" && path != "":
|
||||||
|
t.UnixConn, err = net.DialUnix("unix", nil, &net.UnixAddr{Name: path, Net: "unix"})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return t, nil
|
||||||
|
default:
|
||||||
|
return nil, errors.New("dbus: invalid address (both path and abstract set)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
transports["unix"] = newUnixTransport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *unixTransport) EnableUnixFDs() {
|
||||||
|
t.hasUnixFDs = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *unixTransport) ReadMessage() (*Message, error) {
|
||||||
|
var (
|
||||||
|
blen, hlen uint32
|
||||||
|
csheader [16]byte
|
||||||
|
headers []header
|
||||||
|
order binary.ByteOrder
|
||||||
|
unixfds uint32
|
||||||
|
)
|
||||||
|
// To be sure that all bytes of out-of-band data are read, we use a special
|
||||||
|
// reader that uses ReadUnix on the underlying connection instead of Read
|
||||||
|
// and gathers the out-of-band data in a buffer.
|
||||||
|
rd := &oobReader{conn: t.UnixConn}
|
||||||
|
// read the first 16 bytes (the part of the header that has a constant size),
|
||||||
|
// from which we can figure out the length of the rest of the message
|
||||||
|
if _, err := io.ReadFull(rd, csheader[:]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch csheader[0] {
|
||||||
|
case 'l':
|
||||||
|
order = binary.LittleEndian
|
||||||
|
case 'B':
|
||||||
|
order = binary.BigEndian
|
||||||
|
default:
|
||||||
|
return nil, InvalidMessageError("invalid byte order")
|
||||||
|
}
|
||||||
|
// csheader[4:8] -> length of message body, csheader[12:16] -> length of
|
||||||
|
// header fields (without alignment)
|
||||||
|
binary.Read(bytes.NewBuffer(csheader[4:8]), order, &blen)
|
||||||
|
binary.Read(bytes.NewBuffer(csheader[12:]), order, &hlen)
|
||||||
|
if hlen%8 != 0 {
|
||||||
|
hlen += 8 - (hlen % 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
// decode headers and look for unix fds
|
||||||
|
headerdata := make([]byte, hlen+4)
|
||||||
|
copy(headerdata, csheader[12:])
|
||||||
|
if _, err := io.ReadFull(t, headerdata[4:]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dec := newDecoder(bytes.NewBuffer(headerdata), order)
|
||||||
|
dec.pos = 12
|
||||||
|
vs, err := dec.Decode(Signature{"a(yv)"})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
Store(vs, &headers)
|
||||||
|
for _, v := range headers {
|
||||||
|
if v.Field == byte(FieldUnixFDs) {
|
||||||
|
unixfds, _ = v.Variant.value.(uint32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
all := make([]byte, 16+hlen+blen)
|
||||||
|
copy(all, csheader[:])
|
||||||
|
copy(all[16:], headerdata[4:])
|
||||||
|
if _, err := io.ReadFull(rd, all[16+hlen:]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if unixfds != 0 {
|
||||||
|
if !t.hasUnixFDs {
|
||||||
|
return nil, errors.New("dbus: got unix fds on unsupported transport")
|
||||||
|
}
|
||||||
|
// read the fds from the OOB data
|
||||||
|
scms, err := syscall.ParseSocketControlMessage(rd.oob)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(scms) != 1 {
|
||||||
|
return nil, errors.New("dbus: received more than one socket control message")
|
||||||
|
}
|
||||||
|
fds, err := syscall.ParseUnixRights(&scms[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
msg, err := DecodeMessage(bytes.NewBuffer(all))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// substitute the values in the message body (which are indices for the
|
||||||
|
// array receiver via OOB) with the actual values
|
||||||
|
for i, v := range msg.Body {
|
||||||
|
if j, ok := v.(UnixFDIndex); ok {
|
||||||
|
if uint32(j) >= unixfds {
|
||||||
|
return nil, InvalidMessageError("invalid index for unix fd")
|
||||||
|
}
|
||||||
|
msg.Body[i] = UnixFD(fds[j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return msg, nil
|
||||||
|
}
|
||||||
|
return DecodeMessage(bytes.NewBuffer(all))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *unixTransport) SendMessage(msg *Message) error {
|
||||||
|
fds := make([]int, 0)
|
||||||
|
for i, v := range msg.Body {
|
||||||
|
if fd, ok := v.(UnixFD); ok {
|
||||||
|
msg.Body[i] = UnixFDIndex(len(fds))
|
||||||
|
fds = append(fds, int(fd))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(fds) != 0 {
|
||||||
|
if !t.hasUnixFDs {
|
||||||
|
return errors.New("dbus: unix fd passing not enabled")
|
||||||
|
}
|
||||||
|
msg.Headers[FieldUnixFDs] = MakeVariant(uint32(len(fds)))
|
||||||
|
oob := syscall.UnixRights(fds...)
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
msg.EncodeTo(buf, nativeEndian)
|
||||||
|
n, oobn, err := t.UnixConn.WriteMsgUnix(buf.Bytes(), oob, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if n != buf.Len() || oobn != len(oob) {
|
||||||
|
return io.ErrShortWrite
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := msg.EncodeTo(t, nativeEndian); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *unixTransport) SupportsUnixFDs() bool {
|
||||||
|
return true
|
||||||
|
}
|
95
vendor/github.com/godbus/dbus/transport_unixcred_dragonfly.go
generated
vendored
Normal file
95
vendor/github.com/godbus/dbus/transport_unixcred_dragonfly.go
generated
vendored
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
// The UnixCredentials system call is currently only implemented on Linux
|
||||||
|
// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
|
||||||
|
// https://golang.org/s/go1.4-syscall
|
||||||
|
// http://code.google.com/p/go/source/browse/unix/sockcmsg_linux.go?repo=sys
|
||||||
|
|
||||||
|
// Local implementation of the UnixCredentials system call for DragonFly BSD
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <sys/ucred.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// http://golang.org/src/pkg/syscall/ztypes_linux_amd64.go
|
||||||
|
// http://golang.org/src/pkg/syscall/ztypes_dragonfly_amd64.go
|
||||||
|
type Ucred struct {
|
||||||
|
Pid int32
|
||||||
|
Uid uint32
|
||||||
|
Gid uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://golang.org/src/pkg/syscall/types_linux.go
|
||||||
|
// http://golang.org/src/pkg/syscall/types_dragonfly.go
|
||||||
|
// https://github.com/DragonFlyBSD/DragonFlyBSD/blob/master/sys/sys/ucred.h
|
||||||
|
const (
|
||||||
|
SizeofUcred = C.sizeof_struct_ucred
|
||||||
|
)
|
||||||
|
|
||||||
|
// http://golang.org/src/pkg/syscall/sockcmsg_unix.go
|
||||||
|
func cmsgAlignOf(salen int) int {
|
||||||
|
// From http://golang.org/src/pkg/syscall/sockcmsg_unix.go
|
||||||
|
//salign := sizeofPtr
|
||||||
|
// NOTE: It seems like 64-bit Darwin and DragonFly BSD kernels
|
||||||
|
// still require 32-bit aligned access to network subsystem.
|
||||||
|
//if darwin64Bit || dragonfly64Bit {
|
||||||
|
// salign = 4
|
||||||
|
//}
|
||||||
|
salign := 4
|
||||||
|
return (salen + salign - 1) & ^(salign - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://golang.org/src/pkg/syscall/sockcmsg_unix.go
|
||||||
|
func cmsgData(h *syscall.Cmsghdr) unsafe.Pointer {
|
||||||
|
return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(cmsgAlignOf(syscall.SizeofCmsghdr)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
|
||||||
|
// UnixCredentials encodes credentials into a socket control message
|
||||||
|
// for sending to another process. This can be used for
|
||||||
|
// authentication.
|
||||||
|
func UnixCredentials(ucred *Ucred) []byte {
|
||||||
|
b := make([]byte, syscall.CmsgSpace(SizeofUcred))
|
||||||
|
h := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
|
||||||
|
h.Level = syscall.SOL_SOCKET
|
||||||
|
h.Type = syscall.SCM_CREDS
|
||||||
|
h.SetLen(syscall.CmsgLen(SizeofUcred))
|
||||||
|
*((*Ucred)(cmsgData(h))) = *ucred
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
|
||||||
|
// ParseUnixCredentials decodes a socket control message that contains
|
||||||
|
// credentials in a Ucred structure. To receive such a message, the
|
||||||
|
// SO_PASSCRED option must be enabled on the socket.
|
||||||
|
func ParseUnixCredentials(m *syscall.SocketControlMessage) (*Ucred, error) {
|
||||||
|
if m.Header.Level != syscall.SOL_SOCKET {
|
||||||
|
return nil, syscall.EINVAL
|
||||||
|
}
|
||||||
|
if m.Header.Type != syscall.SCM_CREDS {
|
||||||
|
return nil, syscall.EINVAL
|
||||||
|
}
|
||||||
|
ucred := *(*Ucred)(unsafe.Pointer(&m.Data[0]))
|
||||||
|
return &ucred, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *unixTransport) SendNullByte() error {
|
||||||
|
ucred := &Ucred{Pid: int32(os.Getpid()), Uid: uint32(os.Getuid()), Gid: uint32(os.Getgid())}
|
||||||
|
b := UnixCredentials(ucred)
|
||||||
|
_, oobn, err := t.UnixConn.WriteMsgUnix([]byte{0}, b, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if oobn != len(b) {
|
||||||
|
return io.ErrShortWrite
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
91
vendor/github.com/godbus/dbus/transport_unixcred_freebsd.go
generated
vendored
Normal file
91
vendor/github.com/godbus/dbus/transport_unixcred_freebsd.go
generated
vendored
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
// The UnixCredentials system call is currently only implemented on Linux
|
||||||
|
// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
|
||||||
|
// https://golang.org/s/go1.4-syscall
|
||||||
|
// http://code.google.com/p/go/source/browse/unix/sockcmsg_linux.go?repo=sys
|
||||||
|
|
||||||
|
// Local implementation of the UnixCredentials system call for FreeBSD
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
/*
|
||||||
|
const int sizeofPtr = sizeof(void*);
|
||||||
|
#define _WANT_UCRED
|
||||||
|
#include <sys/ucred.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// http://golang.org/src/pkg/syscall/ztypes_linux_amd64.go
|
||||||
|
// https://golang.org/src/syscall/ztypes_freebsd_amd64.go
|
||||||
|
type Ucred struct {
|
||||||
|
Pid int32
|
||||||
|
Uid uint32
|
||||||
|
Gid uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://golang.org/src/pkg/syscall/types_linux.go
|
||||||
|
// https://golang.org/src/syscall/types_freebsd.go
|
||||||
|
// https://github.com/freebsd/freebsd/blob/master/sys/sys/ucred.h
|
||||||
|
const (
|
||||||
|
SizeofUcred = C.sizeof_struct_ucred
|
||||||
|
)
|
||||||
|
|
||||||
|
// http://golang.org/src/pkg/syscall/sockcmsg_unix.go
|
||||||
|
func cmsgAlignOf(salen int) int {
|
||||||
|
salign := C.sizeofPtr
|
||||||
|
|
||||||
|
return (salen + salign - 1) & ^(salign - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://golang.org/src/pkg/syscall/sockcmsg_unix.go
|
||||||
|
func cmsgData(h *syscall.Cmsghdr) unsafe.Pointer {
|
||||||
|
return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(cmsgAlignOf(syscall.SizeofCmsghdr)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
|
||||||
|
// UnixCredentials encodes credentials into a socket control message
|
||||||
|
// for sending to another process. This can be used for
|
||||||
|
// authentication.
|
||||||
|
func UnixCredentials(ucred *Ucred) []byte {
|
||||||
|
b := make([]byte, syscall.CmsgSpace(SizeofUcred))
|
||||||
|
h := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
|
||||||
|
h.Level = syscall.SOL_SOCKET
|
||||||
|
h.Type = syscall.SCM_CREDS
|
||||||
|
h.SetLen(syscall.CmsgLen(SizeofUcred))
|
||||||
|
*((*Ucred)(cmsgData(h))) = *ucred
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
|
||||||
|
// ParseUnixCredentials decodes a socket control message that contains
|
||||||
|
// credentials in a Ucred structure. To receive such a message, the
|
||||||
|
// SO_PASSCRED option must be enabled on the socket.
|
||||||
|
func ParseUnixCredentials(m *syscall.SocketControlMessage) (*Ucred, error) {
|
||||||
|
if m.Header.Level != syscall.SOL_SOCKET {
|
||||||
|
return nil, syscall.EINVAL
|
||||||
|
}
|
||||||
|
if m.Header.Type != syscall.SCM_CREDS {
|
||||||
|
return nil, syscall.EINVAL
|
||||||
|
}
|
||||||
|
ucred := *(*Ucred)(unsafe.Pointer(&m.Data[0]))
|
||||||
|
return &ucred, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *unixTransport) SendNullByte() error {
|
||||||
|
ucred := &Ucred{Pid: int32(os.Getpid()), Uid: uint32(os.Getuid()), Gid: uint32(os.Getgid())}
|
||||||
|
b := UnixCredentials(ucred)
|
||||||
|
_, oobn, err := t.UnixConn.WriteMsgUnix([]byte{0}, b, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if oobn != len(b) {
|
||||||
|
return io.ErrShortWrite
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
25
vendor/github.com/godbus/dbus/transport_unixcred_linux.go
generated
vendored
Normal file
25
vendor/github.com/godbus/dbus/transport_unixcred_linux.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// The UnixCredentials system call is currently only implemented on Linux
|
||||||
|
// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
|
||||||
|
// https://golang.org/s/go1.4-syscall
|
||||||
|
// http://code.google.com/p/go/source/browse/unix/sockcmsg_linux.go?repo=sys
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t *unixTransport) SendNullByte() error {
|
||||||
|
ucred := &syscall.Ucred{Pid: int32(os.Getpid()), Uid: uint32(os.Getuid()), Gid: uint32(os.Getgid())}
|
||||||
|
b := syscall.UnixCredentials(ucred)
|
||||||
|
_, oobn, err := t.UnixConn.WriteMsgUnix([]byte{0}, b, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if oobn != len(b) {
|
||||||
|
return io.ErrShortWrite
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
14
vendor/github.com/godbus/dbus/transport_unixcred_openbsd.go
generated
vendored
Normal file
14
vendor/github.com/godbus/dbus/transport_unixcred_openbsd.go
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
func (t *unixTransport) SendNullByte() error {
|
||||||
|
n, _, err := t.UnixConn.WriteMsgUnix([]byte{0}, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if n != 1 {
|
||||||
|
return io.ErrShortWrite
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
144
vendor/github.com/godbus/dbus/variant.go
generated
vendored
Normal file
144
vendor/github.com/godbus/dbus/variant.go
generated
vendored
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Variant represents the D-Bus variant type.
|
||||||
|
type Variant struct {
|
||||||
|
sig Signature
|
||||||
|
value interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeVariant converts the given value to a Variant. It panics if v cannot be
|
||||||
|
// represented as a D-Bus type.
|
||||||
|
func MakeVariant(v interface{}) Variant {
|
||||||
|
return MakeVariantWithSignature(v, SignatureOf(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeVariantWithSignature converts the given value to a Variant.
|
||||||
|
func MakeVariantWithSignature(v interface{}, s Signature) Variant {
|
||||||
|
return Variant{s, v}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseVariant parses the given string as a variant as described at
|
||||||
|
// https://developer.gnome.org/glib/unstable/gvariant-text.html. If sig is not
|
||||||
|
// empty, it is taken to be the expected signature for the variant.
|
||||||
|
func ParseVariant(s string, sig Signature) (Variant, error) {
|
||||||
|
tokens := varLex(s)
|
||||||
|
p := &varParser{tokens: tokens}
|
||||||
|
n, err := varMakeNode(p)
|
||||||
|
if err != nil {
|
||||||
|
return Variant{}, err
|
||||||
|
}
|
||||||
|
if sig.str == "" {
|
||||||
|
sig, err = varInfer(n)
|
||||||
|
if err != nil {
|
||||||
|
return Variant{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v, err := n.Value(sig)
|
||||||
|
if err != nil {
|
||||||
|
return Variant{}, err
|
||||||
|
}
|
||||||
|
return MakeVariant(v), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// format returns a formatted version of v and whether this string can be parsed
|
||||||
|
// unambigously.
|
||||||
|
func (v Variant) format() (string, bool) {
|
||||||
|
switch v.sig.str[0] {
|
||||||
|
case 'b', 'i':
|
||||||
|
return fmt.Sprint(v.value), true
|
||||||
|
case 'n', 'q', 'u', 'x', 't', 'd', 'h':
|
||||||
|
return fmt.Sprint(v.value), false
|
||||||
|
case 's':
|
||||||
|
return strconv.Quote(v.value.(string)), true
|
||||||
|
case 'o':
|
||||||
|
return strconv.Quote(string(v.value.(ObjectPath))), false
|
||||||
|
case 'g':
|
||||||
|
return strconv.Quote(v.value.(Signature).str), false
|
||||||
|
case 'v':
|
||||||
|
s, unamb := v.value.(Variant).format()
|
||||||
|
if !unamb {
|
||||||
|
return "<@" + v.value.(Variant).sig.str + " " + s + ">", true
|
||||||
|
}
|
||||||
|
return "<" + s + ">", true
|
||||||
|
case 'y':
|
||||||
|
return fmt.Sprintf("%#x", v.value.(byte)), false
|
||||||
|
}
|
||||||
|
rv := reflect.ValueOf(v.value)
|
||||||
|
switch rv.Kind() {
|
||||||
|
case reflect.Slice:
|
||||||
|
if rv.Len() == 0 {
|
||||||
|
return "[]", false
|
||||||
|
}
|
||||||
|
unamb := true
|
||||||
|
buf := bytes.NewBuffer([]byte("["))
|
||||||
|
for i := 0; i < rv.Len(); i++ {
|
||||||
|
// TODO: slooow
|
||||||
|
s, b := MakeVariant(rv.Index(i).Interface()).format()
|
||||||
|
unamb = unamb && b
|
||||||
|
buf.WriteString(s)
|
||||||
|
if i != rv.Len()-1 {
|
||||||
|
buf.WriteString(", ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.WriteByte(']')
|
||||||
|
return buf.String(), unamb
|
||||||
|
case reflect.Map:
|
||||||
|
if rv.Len() == 0 {
|
||||||
|
return "{}", false
|
||||||
|
}
|
||||||
|
unamb := true
|
||||||
|
var buf bytes.Buffer
|
||||||
|
kvs := make([]string, rv.Len())
|
||||||
|
for i, k := range rv.MapKeys() {
|
||||||
|
s, b := MakeVariant(k.Interface()).format()
|
||||||
|
unamb = unamb && b
|
||||||
|
buf.Reset()
|
||||||
|
buf.WriteString(s)
|
||||||
|
buf.WriteString(": ")
|
||||||
|
s, b = MakeVariant(rv.MapIndex(k).Interface()).format()
|
||||||
|
unamb = unamb && b
|
||||||
|
buf.WriteString(s)
|
||||||
|
kvs[i] = buf.String()
|
||||||
|
}
|
||||||
|
buf.Reset()
|
||||||
|
buf.WriteByte('{')
|
||||||
|
sort.Strings(kvs)
|
||||||
|
for i, kv := range kvs {
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteString(", ")
|
||||||
|
}
|
||||||
|
buf.WriteString(kv)
|
||||||
|
}
|
||||||
|
buf.WriteByte('}')
|
||||||
|
return buf.String(), unamb
|
||||||
|
}
|
||||||
|
return `"INVALID"`, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signature returns the D-Bus signature of the underlying value of v.
|
||||||
|
func (v Variant) Signature() Signature {
|
||||||
|
return v.sig
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation of the underlying value of v as
|
||||||
|
// described at https://developer.gnome.org/glib/unstable/gvariant-text.html.
|
||||||
|
func (v Variant) String() string {
|
||||||
|
s, unamb := v.format()
|
||||||
|
if !unamb {
|
||||||
|
return "@" + v.sig.str + " " + s
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns the underlying value of v.
|
||||||
|
func (v Variant) Value() interface{} {
|
||||||
|
return v.value
|
||||||
|
}
|
284
vendor/github.com/godbus/dbus/variant_lexer.go
generated
vendored
Normal file
284
vendor/github.com/godbus/dbus/variant_lexer.go
generated
vendored
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Heavily inspired by the lexer from text/template.
|
||||||
|
|
||||||
|
type varToken struct {
|
||||||
|
typ varTokenType
|
||||||
|
val string
|
||||||
|
}
|
||||||
|
|
||||||
|
type varTokenType byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
tokEOF varTokenType = iota
|
||||||
|
tokError
|
||||||
|
tokNumber
|
||||||
|
tokString
|
||||||
|
tokBool
|
||||||
|
tokArrayStart
|
||||||
|
tokArrayEnd
|
||||||
|
tokDictStart
|
||||||
|
tokDictEnd
|
||||||
|
tokVariantStart
|
||||||
|
tokVariantEnd
|
||||||
|
tokComma
|
||||||
|
tokColon
|
||||||
|
tokType
|
||||||
|
tokByteString
|
||||||
|
)
|
||||||
|
|
||||||
|
type varLexer struct {
|
||||||
|
input string
|
||||||
|
start int
|
||||||
|
pos int
|
||||||
|
width int
|
||||||
|
tokens []varToken
|
||||||
|
}
|
||||||
|
|
||||||
|
type lexState func(*varLexer) lexState
|
||||||
|
|
||||||
|
func varLex(s string) []varToken {
|
||||||
|
l := &varLexer{input: s}
|
||||||
|
l.run()
|
||||||
|
return l.tokens
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *varLexer) accept(valid string) bool {
|
||||||
|
if strings.IndexRune(valid, l.next()) >= 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
l.backup()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *varLexer) backup() {
|
||||||
|
l.pos -= l.width
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *varLexer) emit(t varTokenType) {
|
||||||
|
l.tokens = append(l.tokens, varToken{t, l.input[l.start:l.pos]})
|
||||||
|
l.start = l.pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *varLexer) errorf(format string, v ...interface{}) lexState {
|
||||||
|
l.tokens = append(l.tokens, varToken{
|
||||||
|
tokError,
|
||||||
|
fmt.Sprintf(format, v...),
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *varLexer) ignore() {
|
||||||
|
l.start = l.pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *varLexer) next() rune {
|
||||||
|
var r rune
|
||||||
|
|
||||||
|
if l.pos >= len(l.input) {
|
||||||
|
l.width = 0
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
r, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
|
||||||
|
l.pos += l.width
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *varLexer) run() {
|
||||||
|
for state := varLexNormal; state != nil; {
|
||||||
|
state = state(l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *varLexer) peek() rune {
|
||||||
|
r := l.next()
|
||||||
|
l.backup()
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func varLexNormal(l *varLexer) lexState {
|
||||||
|
for {
|
||||||
|
r := l.next()
|
||||||
|
switch {
|
||||||
|
case r == -1:
|
||||||
|
l.emit(tokEOF)
|
||||||
|
return nil
|
||||||
|
case r == '[':
|
||||||
|
l.emit(tokArrayStart)
|
||||||
|
case r == ']':
|
||||||
|
l.emit(tokArrayEnd)
|
||||||
|
case r == '{':
|
||||||
|
l.emit(tokDictStart)
|
||||||
|
case r == '}':
|
||||||
|
l.emit(tokDictEnd)
|
||||||
|
case r == '<':
|
||||||
|
l.emit(tokVariantStart)
|
||||||
|
case r == '>':
|
||||||
|
l.emit(tokVariantEnd)
|
||||||
|
case r == ':':
|
||||||
|
l.emit(tokColon)
|
||||||
|
case r == ',':
|
||||||
|
l.emit(tokComma)
|
||||||
|
case r == '\'' || r == '"':
|
||||||
|
l.backup()
|
||||||
|
return varLexString
|
||||||
|
case r == '@':
|
||||||
|
l.backup()
|
||||||
|
return varLexType
|
||||||
|
case unicode.IsSpace(r):
|
||||||
|
l.ignore()
|
||||||
|
case unicode.IsNumber(r) || r == '+' || r == '-':
|
||||||
|
l.backup()
|
||||||
|
return varLexNumber
|
||||||
|
case r == 'b':
|
||||||
|
pos := l.start
|
||||||
|
if n := l.peek(); n == '"' || n == '\'' {
|
||||||
|
return varLexByteString
|
||||||
|
}
|
||||||
|
// not a byte string; try to parse it as a type or bool below
|
||||||
|
l.pos = pos + 1
|
||||||
|
l.width = 1
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
// either a bool or a type. Try bools first.
|
||||||
|
l.backup()
|
||||||
|
if l.pos+4 <= len(l.input) {
|
||||||
|
if l.input[l.pos:l.pos+4] == "true" {
|
||||||
|
l.pos += 4
|
||||||
|
l.emit(tokBool)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if l.pos+5 <= len(l.input) {
|
||||||
|
if l.input[l.pos:l.pos+5] == "false" {
|
||||||
|
l.pos += 5
|
||||||
|
l.emit(tokBool)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// must be a type.
|
||||||
|
return varLexType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var varTypeMap = map[string]string{
|
||||||
|
"boolean": "b",
|
||||||
|
"byte": "y",
|
||||||
|
"int16": "n",
|
||||||
|
"uint16": "q",
|
||||||
|
"int32": "i",
|
||||||
|
"uint32": "u",
|
||||||
|
"int64": "x",
|
||||||
|
"uint64": "t",
|
||||||
|
"double": "f",
|
||||||
|
"string": "s",
|
||||||
|
"objectpath": "o",
|
||||||
|
"signature": "g",
|
||||||
|
}
|
||||||
|
|
||||||
|
func varLexByteString(l *varLexer) lexState {
|
||||||
|
q := l.next()
|
||||||
|
Loop:
|
||||||
|
for {
|
||||||
|
switch l.next() {
|
||||||
|
case '\\':
|
||||||
|
if r := l.next(); r != -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case -1:
|
||||||
|
return l.errorf("unterminated bytestring")
|
||||||
|
case q:
|
||||||
|
break Loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.emit(tokByteString)
|
||||||
|
return varLexNormal
|
||||||
|
}
|
||||||
|
|
||||||
|
func varLexNumber(l *varLexer) lexState {
|
||||||
|
l.accept("+-")
|
||||||
|
digits := "0123456789"
|
||||||
|
if l.accept("0") {
|
||||||
|
if l.accept("x") {
|
||||||
|
digits = "0123456789abcdefABCDEF"
|
||||||
|
} else {
|
||||||
|
digits = "01234567"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for strings.IndexRune(digits, l.next()) >= 0 {
|
||||||
|
}
|
||||||
|
l.backup()
|
||||||
|
if l.accept(".") {
|
||||||
|
for strings.IndexRune(digits, l.next()) >= 0 {
|
||||||
|
}
|
||||||
|
l.backup()
|
||||||
|
}
|
||||||
|
if l.accept("eE") {
|
||||||
|
l.accept("+-")
|
||||||
|
for strings.IndexRune("0123456789", l.next()) >= 0 {
|
||||||
|
}
|
||||||
|
l.backup()
|
||||||
|
}
|
||||||
|
if r := l.peek(); unicode.IsLetter(r) {
|
||||||
|
l.next()
|
||||||
|
return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
|
||||||
|
}
|
||||||
|
l.emit(tokNumber)
|
||||||
|
return varLexNormal
|
||||||
|
}
|
||||||
|
|
||||||
|
func varLexString(l *varLexer) lexState {
|
||||||
|
q := l.next()
|
||||||
|
Loop:
|
||||||
|
for {
|
||||||
|
switch l.next() {
|
||||||
|
case '\\':
|
||||||
|
if r := l.next(); r != -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case -1:
|
||||||
|
return l.errorf("unterminated string")
|
||||||
|
case q:
|
||||||
|
break Loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.emit(tokString)
|
||||||
|
return varLexNormal
|
||||||
|
}
|
||||||
|
|
||||||
|
func varLexType(l *varLexer) lexState {
|
||||||
|
at := l.accept("@")
|
||||||
|
for {
|
||||||
|
r := l.next()
|
||||||
|
if r == -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if unicode.IsSpace(r) {
|
||||||
|
l.backup()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if at {
|
||||||
|
if _, err := ParseSignature(l.input[l.start+1 : l.pos]); err != nil {
|
||||||
|
return l.errorf("%s", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if _, ok := varTypeMap[l.input[l.start:l.pos]]; ok {
|
||||||
|
l.emit(tokType)
|
||||||
|
return varLexNormal
|
||||||
|
}
|
||||||
|
return l.errorf("unrecognized type %q", l.input[l.start:l.pos])
|
||||||
|
}
|
||||||
|
l.emit(tokType)
|
||||||
|
return varLexNormal
|
||||||
|
}
|
817
vendor/github.com/godbus/dbus/variant_parser.go
generated
vendored
Normal file
817
vendor/github.com/godbus/dbus/variant_parser.go
generated
vendored
Normal file
@ -0,0 +1,817 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
type varParser struct {
|
||||||
|
tokens []varToken
|
||||||
|
i int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *varParser) backup() {
|
||||||
|
p.i--
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *varParser) next() varToken {
|
||||||
|
if p.i < len(p.tokens) {
|
||||||
|
t := p.tokens[p.i]
|
||||||
|
p.i++
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
return varToken{typ: tokEOF}
|
||||||
|
}
|
||||||
|
|
||||||
|
type varNode interface {
|
||||||
|
Infer() (Signature, error)
|
||||||
|
String() string
|
||||||
|
Sigs() sigSet
|
||||||
|
Value(Signature) (interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func varMakeNode(p *varParser) (varNode, error) {
|
||||||
|
var sig Signature
|
||||||
|
|
||||||
|
for {
|
||||||
|
t := p.next()
|
||||||
|
switch t.typ {
|
||||||
|
case tokEOF:
|
||||||
|
return nil, io.ErrUnexpectedEOF
|
||||||
|
case tokError:
|
||||||
|
return nil, errors.New(t.val)
|
||||||
|
case tokNumber:
|
||||||
|
return varMakeNumNode(t, sig)
|
||||||
|
case tokString:
|
||||||
|
return varMakeStringNode(t, sig)
|
||||||
|
case tokBool:
|
||||||
|
if sig.str != "" && sig.str != "b" {
|
||||||
|
return nil, varTypeError{t.val, sig}
|
||||||
|
}
|
||||||
|
b, err := strconv.ParseBool(t.val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return boolNode(b), nil
|
||||||
|
case tokArrayStart:
|
||||||
|
return varMakeArrayNode(p, sig)
|
||||||
|
case tokVariantStart:
|
||||||
|
return varMakeVariantNode(p, sig)
|
||||||
|
case tokDictStart:
|
||||||
|
return varMakeDictNode(p, sig)
|
||||||
|
case tokType:
|
||||||
|
if sig.str != "" {
|
||||||
|
return nil, errors.New("unexpected type annotation")
|
||||||
|
}
|
||||||
|
if t.val[0] == '@' {
|
||||||
|
sig.str = t.val[1:]
|
||||||
|
} else {
|
||||||
|
sig.str = varTypeMap[t.val]
|
||||||
|
}
|
||||||
|
case tokByteString:
|
||||||
|
if sig.str != "" && sig.str != "ay" {
|
||||||
|
return nil, varTypeError{t.val, sig}
|
||||||
|
}
|
||||||
|
b, err := varParseByteString(t.val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return byteStringNode(b), nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unexpected %q", t.val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type varTypeError struct {
|
||||||
|
val string
|
||||||
|
sig Signature
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e varTypeError) Error() string {
|
||||||
|
return fmt.Sprintf("dbus: can't parse %q as type %q", e.val, e.sig.str)
|
||||||
|
}
|
||||||
|
|
||||||
|
type sigSet map[Signature]bool
|
||||||
|
|
||||||
|
func (s sigSet) Empty() bool {
|
||||||
|
return len(s) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s sigSet) Intersect(s2 sigSet) sigSet {
|
||||||
|
r := make(sigSet)
|
||||||
|
for k := range s {
|
||||||
|
if s2[k] {
|
||||||
|
r[k] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s sigSet) Single() (Signature, bool) {
|
||||||
|
if len(s) == 1 {
|
||||||
|
for k := range s {
|
||||||
|
return k, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Signature{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s sigSet) ToArray() sigSet {
|
||||||
|
r := make(sigSet, len(s))
|
||||||
|
for k := range s {
|
||||||
|
r[Signature{"a" + k.str}] = true
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
type numNode struct {
|
||||||
|
sig Signature
|
||||||
|
str string
|
||||||
|
val interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var numSigSet = sigSet{
|
||||||
|
Signature{"y"}: true,
|
||||||
|
Signature{"n"}: true,
|
||||||
|
Signature{"q"}: true,
|
||||||
|
Signature{"i"}: true,
|
||||||
|
Signature{"u"}: true,
|
||||||
|
Signature{"x"}: true,
|
||||||
|
Signature{"t"}: true,
|
||||||
|
Signature{"d"}: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n numNode) Infer() (Signature, error) {
|
||||||
|
if strings.ContainsAny(n.str, ".e") {
|
||||||
|
return Signature{"d"}, nil
|
||||||
|
}
|
||||||
|
return Signature{"i"}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n numNode) String() string {
|
||||||
|
return n.str
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n numNode) Sigs() sigSet {
|
||||||
|
if n.sig.str != "" {
|
||||||
|
return sigSet{n.sig: true}
|
||||||
|
}
|
||||||
|
if strings.ContainsAny(n.str, ".e") {
|
||||||
|
return sigSet{Signature{"d"}: true}
|
||||||
|
}
|
||||||
|
return numSigSet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n numNode) Value(sig Signature) (interface{}, error) {
|
||||||
|
if n.sig.str != "" && n.sig != sig {
|
||||||
|
return nil, varTypeError{n.str, sig}
|
||||||
|
}
|
||||||
|
if n.val != nil {
|
||||||
|
return n.val, nil
|
||||||
|
}
|
||||||
|
return varNumAs(n.str, sig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func varMakeNumNode(tok varToken, sig Signature) (varNode, error) {
|
||||||
|
if sig.str == "" {
|
||||||
|
return numNode{str: tok.val}, nil
|
||||||
|
}
|
||||||
|
num, err := varNumAs(tok.val, sig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return numNode{sig: sig, val: num}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func varNumAs(s string, sig Signature) (interface{}, error) {
|
||||||
|
isUnsigned := false
|
||||||
|
size := 32
|
||||||
|
switch sig.str {
|
||||||
|
case "n":
|
||||||
|
size = 16
|
||||||
|
case "i":
|
||||||
|
case "x":
|
||||||
|
size = 64
|
||||||
|
case "y":
|
||||||
|
size = 8
|
||||||
|
isUnsigned = true
|
||||||
|
case "q":
|
||||||
|
size = 16
|
||||||
|
isUnsigned = true
|
||||||
|
case "u":
|
||||||
|
isUnsigned = true
|
||||||
|
case "t":
|
||||||
|
size = 64
|
||||||
|
isUnsigned = true
|
||||||
|
case "d":
|
||||||
|
d, err := strconv.ParseFloat(s, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return d, nil
|
||||||
|
default:
|
||||||
|
return nil, varTypeError{s, sig}
|
||||||
|
}
|
||||||
|
base := 10
|
||||||
|
if strings.HasPrefix(s, "0x") {
|
||||||
|
base = 16
|
||||||
|
s = s[2:]
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(s, "0") && len(s) != 1 {
|
||||||
|
base = 8
|
||||||
|
s = s[1:]
|
||||||
|
}
|
||||||
|
if isUnsigned {
|
||||||
|
i, err := strconv.ParseUint(s, base, size)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var v interface{} = i
|
||||||
|
switch sig.str {
|
||||||
|
case "y":
|
||||||
|
v = byte(i)
|
||||||
|
case "q":
|
||||||
|
v = uint16(i)
|
||||||
|
case "u":
|
||||||
|
v = uint32(i)
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
i, err := strconv.ParseInt(s, base, size)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var v interface{} = i
|
||||||
|
switch sig.str {
|
||||||
|
case "n":
|
||||||
|
v = int16(i)
|
||||||
|
case "i":
|
||||||
|
v = int32(i)
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type stringNode struct {
|
||||||
|
sig Signature
|
||||||
|
str string // parsed
|
||||||
|
val interface{} // has correct type
|
||||||
|
}
|
||||||
|
|
||||||
|
var stringSigSet = sigSet{
|
||||||
|
Signature{"s"}: true,
|
||||||
|
Signature{"g"}: true,
|
||||||
|
Signature{"o"}: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n stringNode) Infer() (Signature, error) {
|
||||||
|
return Signature{"s"}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n stringNode) String() string {
|
||||||
|
return n.str
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n stringNode) Sigs() sigSet {
|
||||||
|
if n.sig.str != "" {
|
||||||
|
return sigSet{n.sig: true}
|
||||||
|
}
|
||||||
|
return stringSigSet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n stringNode) Value(sig Signature) (interface{}, error) {
|
||||||
|
if n.sig.str != "" && n.sig != sig {
|
||||||
|
return nil, varTypeError{n.str, sig}
|
||||||
|
}
|
||||||
|
if n.val != nil {
|
||||||
|
return n.val, nil
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case sig.str == "g":
|
||||||
|
return Signature{n.str}, nil
|
||||||
|
case sig.str == "o":
|
||||||
|
return ObjectPath(n.str), nil
|
||||||
|
case sig.str == "s":
|
||||||
|
return n.str, nil
|
||||||
|
default:
|
||||||
|
return nil, varTypeError{n.str, sig}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func varMakeStringNode(tok varToken, sig Signature) (varNode, error) {
|
||||||
|
if sig.str != "" && sig.str != "s" && sig.str != "g" && sig.str != "o" {
|
||||||
|
return nil, fmt.Errorf("invalid type %q for string", sig.str)
|
||||||
|
}
|
||||||
|
s, err := varParseString(tok.val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
n := stringNode{str: s}
|
||||||
|
if sig.str == "" {
|
||||||
|
return stringNode{str: s}, nil
|
||||||
|
}
|
||||||
|
n.sig = sig
|
||||||
|
switch sig.str {
|
||||||
|
case "o":
|
||||||
|
n.val = ObjectPath(s)
|
||||||
|
case "g":
|
||||||
|
n.val = Signature{s}
|
||||||
|
case "s":
|
||||||
|
n.val = s
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func varParseString(s string) (string, error) {
|
||||||
|
// quotes are guaranteed to be there
|
||||||
|
s = s[1 : len(s)-1]
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
for len(s) != 0 {
|
||||||
|
r, size := utf8.DecodeRuneInString(s)
|
||||||
|
if r == utf8.RuneError && size == 1 {
|
||||||
|
return "", errors.New("invalid UTF-8")
|
||||||
|
}
|
||||||
|
s = s[size:]
|
||||||
|
if r != '\\' {
|
||||||
|
buf.WriteRune(r)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
r, size = utf8.DecodeRuneInString(s)
|
||||||
|
if r == utf8.RuneError && size == 1 {
|
||||||
|
return "", errors.New("invalid UTF-8")
|
||||||
|
}
|
||||||
|
s = s[size:]
|
||||||
|
switch r {
|
||||||
|
case 'a':
|
||||||
|
buf.WriteRune(0x7)
|
||||||
|
case 'b':
|
||||||
|
buf.WriteRune(0x8)
|
||||||
|
case 'f':
|
||||||
|
buf.WriteRune(0xc)
|
||||||
|
case 'n':
|
||||||
|
buf.WriteRune('\n')
|
||||||
|
case 'r':
|
||||||
|
buf.WriteRune('\r')
|
||||||
|
case 't':
|
||||||
|
buf.WriteRune('\t')
|
||||||
|
case '\n':
|
||||||
|
case 'u':
|
||||||
|
if len(s) < 4 {
|
||||||
|
return "", errors.New("short unicode escape")
|
||||||
|
}
|
||||||
|
r, err := strconv.ParseUint(s[:4], 16, 32)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
buf.WriteRune(rune(r))
|
||||||
|
s = s[4:]
|
||||||
|
case 'U':
|
||||||
|
if len(s) < 8 {
|
||||||
|
return "", errors.New("short unicode escape")
|
||||||
|
}
|
||||||
|
r, err := strconv.ParseUint(s[:8], 16, 32)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
buf.WriteRune(rune(r))
|
||||||
|
s = s[8:]
|
||||||
|
default:
|
||||||
|
buf.WriteRune(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var boolSigSet = sigSet{Signature{"b"}: true}
|
||||||
|
|
||||||
|
type boolNode bool
|
||||||
|
|
||||||
|
func (boolNode) Infer() (Signature, error) {
|
||||||
|
return Signature{"b"}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b boolNode) String() string {
|
||||||
|
if b {
|
||||||
|
return "true"
|
||||||
|
}
|
||||||
|
return "false"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (boolNode) Sigs() sigSet {
|
||||||
|
return boolSigSet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b boolNode) Value(sig Signature) (interface{}, error) {
|
||||||
|
if sig.str != "b" {
|
||||||
|
return nil, varTypeError{b.String(), sig}
|
||||||
|
}
|
||||||
|
return bool(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type arrayNode struct {
|
||||||
|
set sigSet
|
||||||
|
children []varNode
|
||||||
|
val interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n arrayNode) Infer() (Signature, error) {
|
||||||
|
for _, v := range n.children {
|
||||||
|
csig, err := varInfer(v)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return Signature{"a" + csig.str}, nil
|
||||||
|
}
|
||||||
|
return Signature{}, fmt.Errorf("can't infer type for %q", n.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n arrayNode) String() string {
|
||||||
|
s := "["
|
||||||
|
for i, v := range n.children {
|
||||||
|
s += v.String()
|
||||||
|
if i != len(n.children)-1 {
|
||||||
|
s += ", "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s + "]"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n arrayNode) Sigs() sigSet {
|
||||||
|
return n.set
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n arrayNode) Value(sig Signature) (interface{}, error) {
|
||||||
|
if n.set.Empty() {
|
||||||
|
// no type information whatsoever, so this must be an empty slice
|
||||||
|
return reflect.MakeSlice(typeFor(sig.str), 0, 0).Interface(), nil
|
||||||
|
}
|
||||||
|
if !n.set[sig] {
|
||||||
|
return nil, varTypeError{n.String(), sig}
|
||||||
|
}
|
||||||
|
s := reflect.MakeSlice(typeFor(sig.str), len(n.children), len(n.children))
|
||||||
|
for i, v := range n.children {
|
||||||
|
rv, err := v.Value(Signature{sig.str[1:]})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s.Index(i).Set(reflect.ValueOf(rv))
|
||||||
|
}
|
||||||
|
return s.Interface(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func varMakeArrayNode(p *varParser, sig Signature) (varNode, error) {
|
||||||
|
var n arrayNode
|
||||||
|
if sig.str != "" {
|
||||||
|
n.set = sigSet{sig: true}
|
||||||
|
}
|
||||||
|
if t := p.next(); t.typ == tokArrayEnd {
|
||||||
|
return n, nil
|
||||||
|
} else {
|
||||||
|
p.backup()
|
||||||
|
}
|
||||||
|
Loop:
|
||||||
|
for {
|
||||||
|
t := p.next()
|
||||||
|
switch t.typ {
|
||||||
|
case tokEOF:
|
||||||
|
return nil, io.ErrUnexpectedEOF
|
||||||
|
case tokError:
|
||||||
|
return nil, errors.New(t.val)
|
||||||
|
}
|
||||||
|
p.backup()
|
||||||
|
cn, err := varMakeNode(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if cset := cn.Sigs(); !cset.Empty() {
|
||||||
|
if n.set.Empty() {
|
||||||
|
n.set = cset.ToArray()
|
||||||
|
} else {
|
||||||
|
nset := cset.ToArray().Intersect(n.set)
|
||||||
|
if nset.Empty() {
|
||||||
|
return nil, fmt.Errorf("can't parse %q with given type information", cn.String())
|
||||||
|
}
|
||||||
|
n.set = nset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n.children = append(n.children, cn)
|
||||||
|
switch t := p.next(); t.typ {
|
||||||
|
case tokEOF:
|
||||||
|
return nil, io.ErrUnexpectedEOF
|
||||||
|
case tokError:
|
||||||
|
return nil, errors.New(t.val)
|
||||||
|
case tokArrayEnd:
|
||||||
|
break Loop
|
||||||
|
case tokComma:
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unexpected %q", t.val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type variantNode struct {
|
||||||
|
n varNode
|
||||||
|
}
|
||||||
|
|
||||||
|
var variantSet = sigSet{
|
||||||
|
Signature{"v"}: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (variantNode) Infer() (Signature, error) {
|
||||||
|
return Signature{"v"}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n variantNode) String() string {
|
||||||
|
return "<" + n.n.String() + ">"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (variantNode) Sigs() sigSet {
|
||||||
|
return variantSet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n variantNode) Value(sig Signature) (interface{}, error) {
|
||||||
|
if sig.str != "v" {
|
||||||
|
return nil, varTypeError{n.String(), sig}
|
||||||
|
}
|
||||||
|
sig, err := varInfer(n.n)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
v, err := n.n.Value(sig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return MakeVariant(v), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func varMakeVariantNode(p *varParser, sig Signature) (varNode, error) {
|
||||||
|
n, err := varMakeNode(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if t := p.next(); t.typ != tokVariantEnd {
|
||||||
|
return nil, fmt.Errorf("unexpected %q", t.val)
|
||||||
|
}
|
||||||
|
vn := variantNode{n}
|
||||||
|
if sig.str != "" && sig.str != "v" {
|
||||||
|
return nil, varTypeError{vn.String(), sig}
|
||||||
|
}
|
||||||
|
return variantNode{n}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type dictEntry struct {
|
||||||
|
key, val varNode
|
||||||
|
}
|
||||||
|
|
||||||
|
type dictNode struct {
|
||||||
|
kset, vset sigSet
|
||||||
|
children []dictEntry
|
||||||
|
val interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n dictNode) Infer() (Signature, error) {
|
||||||
|
for _, v := range n.children {
|
||||||
|
ksig, err := varInfer(v.key)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
vsig, err := varInfer(v.val)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return Signature{"a{" + ksig.str + vsig.str + "}"}, nil
|
||||||
|
}
|
||||||
|
return Signature{}, fmt.Errorf("can't infer type for %q", n.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n dictNode) String() string {
|
||||||
|
s := "{"
|
||||||
|
for i, v := range n.children {
|
||||||
|
s += v.key.String() + ": " + v.val.String()
|
||||||
|
if i != len(n.children)-1 {
|
||||||
|
s += ", "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s + "}"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n dictNode) Sigs() sigSet {
|
||||||
|
r := sigSet{}
|
||||||
|
for k := range n.kset {
|
||||||
|
for v := range n.vset {
|
||||||
|
sig := "a{" + k.str + v.str + "}"
|
||||||
|
r[Signature{sig}] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n dictNode) Value(sig Signature) (interface{}, error) {
|
||||||
|
set := n.Sigs()
|
||||||
|
if set.Empty() {
|
||||||
|
// no type information -> empty dict
|
||||||
|
return reflect.MakeMap(typeFor(sig.str)).Interface(), nil
|
||||||
|
}
|
||||||
|
if !set[sig] {
|
||||||
|
return nil, varTypeError{n.String(), sig}
|
||||||
|
}
|
||||||
|
m := reflect.MakeMap(typeFor(sig.str))
|
||||||
|
ksig := Signature{sig.str[2:3]}
|
||||||
|
vsig := Signature{sig.str[3 : len(sig.str)-1]}
|
||||||
|
for _, v := range n.children {
|
||||||
|
kv, err := v.key.Value(ksig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
vv, err := v.val.Value(vsig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
m.SetMapIndex(reflect.ValueOf(kv), reflect.ValueOf(vv))
|
||||||
|
}
|
||||||
|
return m.Interface(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func varMakeDictNode(p *varParser, sig Signature) (varNode, error) {
|
||||||
|
var n dictNode
|
||||||
|
|
||||||
|
if sig.str != "" {
|
||||||
|
if len(sig.str) < 5 {
|
||||||
|
return nil, fmt.Errorf("invalid signature %q for dict type", sig)
|
||||||
|
}
|
||||||
|
ksig := Signature{string(sig.str[2])}
|
||||||
|
vsig := Signature{sig.str[3 : len(sig.str)-1]}
|
||||||
|
n.kset = sigSet{ksig: true}
|
||||||
|
n.vset = sigSet{vsig: true}
|
||||||
|
}
|
||||||
|
if t := p.next(); t.typ == tokDictEnd {
|
||||||
|
return n, nil
|
||||||
|
} else {
|
||||||
|
p.backup()
|
||||||
|
}
|
||||||
|
Loop:
|
||||||
|
for {
|
||||||
|
t := p.next()
|
||||||
|
switch t.typ {
|
||||||
|
case tokEOF:
|
||||||
|
return nil, io.ErrUnexpectedEOF
|
||||||
|
case tokError:
|
||||||
|
return nil, errors.New(t.val)
|
||||||
|
}
|
||||||
|
p.backup()
|
||||||
|
kn, err := varMakeNode(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if kset := kn.Sigs(); !kset.Empty() {
|
||||||
|
if n.kset.Empty() {
|
||||||
|
n.kset = kset
|
||||||
|
} else {
|
||||||
|
n.kset = kset.Intersect(n.kset)
|
||||||
|
if n.kset.Empty() {
|
||||||
|
return nil, fmt.Errorf("can't parse %q with given type information", kn.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t = p.next()
|
||||||
|
switch t.typ {
|
||||||
|
case tokEOF:
|
||||||
|
return nil, io.ErrUnexpectedEOF
|
||||||
|
case tokError:
|
||||||
|
return nil, errors.New(t.val)
|
||||||
|
case tokColon:
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unexpected %q", t.val)
|
||||||
|
}
|
||||||
|
t = p.next()
|
||||||
|
switch t.typ {
|
||||||
|
case tokEOF:
|
||||||
|
return nil, io.ErrUnexpectedEOF
|
||||||
|
case tokError:
|
||||||
|
return nil, errors.New(t.val)
|
||||||
|
}
|
||||||
|
p.backup()
|
||||||
|
vn, err := varMakeNode(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if vset := vn.Sigs(); !vset.Empty() {
|
||||||
|
if n.vset.Empty() {
|
||||||
|
n.vset = vset
|
||||||
|
} else {
|
||||||
|
n.vset = n.vset.Intersect(vset)
|
||||||
|
if n.vset.Empty() {
|
||||||
|
return nil, fmt.Errorf("can't parse %q with given type information", vn.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n.children = append(n.children, dictEntry{kn, vn})
|
||||||
|
t = p.next()
|
||||||
|
switch t.typ {
|
||||||
|
case tokEOF:
|
||||||
|
return nil, io.ErrUnexpectedEOF
|
||||||
|
case tokError:
|
||||||
|
return nil, errors.New(t.val)
|
||||||
|
case tokDictEnd:
|
||||||
|
break Loop
|
||||||
|
case tokComma:
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unexpected %q", t.val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type byteStringNode []byte
|
||||||
|
|
||||||
|
var byteStringSet = sigSet{
|
||||||
|
Signature{"ay"}: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (byteStringNode) Infer() (Signature, error) {
|
||||||
|
return Signature{"ay"}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b byteStringNode) String() string {
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b byteStringNode) Sigs() sigSet {
|
||||||
|
return byteStringSet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b byteStringNode) Value(sig Signature) (interface{}, error) {
|
||||||
|
if sig.str != "ay" {
|
||||||
|
return nil, varTypeError{b.String(), sig}
|
||||||
|
}
|
||||||
|
return []byte(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func varParseByteString(s string) ([]byte, error) {
|
||||||
|
// quotes and b at start are guaranteed to be there
|
||||||
|
b := make([]byte, 0, 1)
|
||||||
|
s = s[2 : len(s)-1]
|
||||||
|
for len(s) != 0 {
|
||||||
|
c := s[0]
|
||||||
|
s = s[1:]
|
||||||
|
if c != '\\' {
|
||||||
|
b = append(b, c)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
c = s[0]
|
||||||
|
s = s[1:]
|
||||||
|
switch c {
|
||||||
|
case 'a':
|
||||||
|
b = append(b, 0x7)
|
||||||
|
case 'b':
|
||||||
|
b = append(b, 0x8)
|
||||||
|
case 'f':
|
||||||
|
b = append(b, 0xc)
|
||||||
|
case 'n':
|
||||||
|
b = append(b, '\n')
|
||||||
|
case 'r':
|
||||||
|
b = append(b, '\r')
|
||||||
|
case 't':
|
||||||
|
b = append(b, '\t')
|
||||||
|
case 'x':
|
||||||
|
if len(s) < 2 {
|
||||||
|
return nil, errors.New("short escape")
|
||||||
|
}
|
||||||
|
n, err := strconv.ParseUint(s[:2], 16, 8)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b = append(b, byte(n))
|
||||||
|
s = s[2:]
|
||||||
|
case '0':
|
||||||
|
if len(s) < 3 {
|
||||||
|
return nil, errors.New("short escape")
|
||||||
|
}
|
||||||
|
n, err := strconv.ParseUint(s[:3], 8, 8)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b = append(b, byte(n))
|
||||||
|
s = s[3:]
|
||||||
|
default:
|
||||||
|
b = append(b, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return append(b, 0), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func varInfer(n varNode) (Signature, error) {
|
||||||
|
if sig, ok := n.Sigs().Single(); ok {
|
||||||
|
return sig, nil
|
||||||
|
}
|
||||||
|
return n.Infer()
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user