Merge pull request #811 from miaoyq/replace-two-Unlock-with-defer-Unlock
User 'defer Unlock' to replace two 'Unlock's
diff --git a/.travis.yml b/.travis.yml
index b7cd1c8..7792c30 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -29,9 +29,9 @@
script:
- export GOOS=$TRAVIS_GOOS
- export CGO_ENABLED=$TRAVIS_CGO_ENABLED
+ - GIT_CHECK_EXCLUDE="./vendor" TRAVIS_COMMIT_RANGE="${TRAVIS_COMMIT_RANGE/.../..}" make dco
- make fmt
- make binaries
- - TRAVIS_COMMIT_RANGE="${TRAVIS_COMMIT_RANGE/.../..}" make dco
- if [ "$GOOS" != "windows" ]; then make coverage ; fi
- if [ "$GOOS" != "windows" ]; then sudo PATH=$PATH GOPATH=$GOPATH make root-coverage ; fi
diff --git a/MAINTAINERS b/MAINTAINERS
index 66ba86d..ce60778 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -61,6 +61,23 @@
merged.
"""
+ [Rules.adding-sub-projects]
+
+ title = "How are sub projects added?"
+
+ text = """
+Similar to adding maintainers, new sub projects can be added to containerd
+GitHub organization as long as they adhere to the CNCF
+[charter](https://www.cncf.io/about/charter/) and mission. After a project
+proposal has been announced on a public forum (GitHub issue or mailing list),
+the existing maintainers are given five business days to discuss the new
+project, raise objections and cast their vote. Projects must be approved by at
+least 66% of the current maintainers by adding their vote.
+
+If a project is approved, a maintainer will add the project to the containerd
+GitHub organization, and make an announcement on a public forum.
+"""
+
[Rules.stepping-down-policy]
title = "Stepping down policy"
diff --git a/api/services/content/content.pb.go b/api/services/content/content.pb.go
index d42341c..0346b12 100644
--- a/api/services/content/content.pb.go
+++ b/api/services/content/content.pb.go
@@ -2588,7 +2588,7 @@
var fileDescriptorContent = []byte{
// 768 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0x4f, 0x6f, 0xd3, 0x4e,
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xb4, 0x55, 0x4f, 0x6f, 0xd3, 0x4e,
0x10, 0xcd, 0xe6, 0x8f, 0x7f, 0xcd, 0x24, 0xed, 0x2f, 0x6c, 0x43, 0x15, 0xb9, 0xad, 0x13, 0xc2,
0x25, 0xaa, 0x84, 0x53, 0xc2, 0x0d, 0x0e, 0xc8, 0x49, 0x4b, 0x55, 0xa4, 0x52, 0xc9, 0x8d, 0x54,
0x71, 0x42, 0x4e, 0xb2, 0x09, 0x96, 0x1a, 0xaf, 0xb1, 0x37, 0x55, 0xe1, 0xc4, 0x05, 0x09, 0xf5,
diff --git a/api/services/execution/execution.pb.go b/api/services/execution/execution.pb.go
index ac585b8..bbd0d3e 100644
--- a/api/services/execution/execution.pb.go
+++ b/api/services/execution/execution.pb.go
@@ -23,6 +23,8 @@
ExecResponse
PtyRequest
CloseStdinRequest
+ PauseRequest
+ ResumeRequest
*/
package execution
@@ -193,6 +195,22 @@
func (*CloseStdinRequest) ProtoMessage() {}
func (*CloseStdinRequest) Descriptor() ([]byte, []int) { return fileDescriptorExecution, []int{13} }
+type PauseRequest struct {
+ ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+}
+
+func (m *PauseRequest) Reset() { *m = PauseRequest{} }
+func (*PauseRequest) ProtoMessage() {}
+func (*PauseRequest) Descriptor() ([]byte, []int) { return fileDescriptorExecution, []int{14} }
+
+type ResumeRequest struct {
+ ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+}
+
+func (m *ResumeRequest) Reset() { *m = ResumeRequest{} }
+func (*ResumeRequest) ProtoMessage() {}
+func (*ResumeRequest) Descriptor() ([]byte, []int) { return fileDescriptorExecution, []int{15} }
+
func init() {
proto.RegisterType((*CreateRequest)(nil), "containerd.v1.services.CreateRequest")
proto.RegisterType((*CreateResponse)(nil), "containerd.v1.services.CreateResponse")
@@ -208,6 +226,8 @@
proto.RegisterType((*ExecResponse)(nil), "containerd.v1.services.ExecResponse")
proto.RegisterType((*PtyRequest)(nil), "containerd.v1.services.PtyRequest")
proto.RegisterType((*CloseStdinRequest)(nil), "containerd.v1.services.CloseStdinRequest")
+ proto.RegisterType((*PauseRequest)(nil), "containerd.v1.services.PauseRequest")
+ proto.RegisterType((*ResumeRequest)(nil), "containerd.v1.services.ResumeRequest")
}
// Reference imports to suppress errors if they are not otherwise used.
@@ -231,6 +251,8 @@
Exec(ctx context.Context, in *ExecRequest, opts ...grpc.CallOption) (*ExecResponse, error)
Pty(ctx context.Context, in *PtyRequest, opts ...grpc.CallOption) (*google_protobuf.Empty, error)
CloseStdin(ctx context.Context, in *CloseStdinRequest, opts ...grpc.CallOption) (*google_protobuf.Empty, error)
+ Pause(ctx context.Context, in *PauseRequest, opts ...grpc.CallOption) (*google_protobuf.Empty, error)
+ Resume(ctx context.Context, in *ResumeRequest, opts ...grpc.CallOption) (*google_protobuf.Empty, error)
}
type containerServiceClient struct {
@@ -354,6 +376,24 @@
return out, nil
}
+func (c *containerServiceClient) Pause(ctx context.Context, in *PauseRequest, opts ...grpc.CallOption) (*google_protobuf.Empty, error) {
+ out := new(google_protobuf.Empty)
+ err := grpc.Invoke(ctx, "/containerd.v1.services.ContainerService/Pause", in, out, c.cc, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *containerServiceClient) Resume(ctx context.Context, in *ResumeRequest, opts ...grpc.CallOption) (*google_protobuf.Empty, error) {
+ out := new(google_protobuf.Empty)
+ err := grpc.Invoke(ctx, "/containerd.v1.services.ContainerService/Resume", in, out, c.cc, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
// Server API for ContainerService service
type ContainerServiceServer interface {
@@ -367,6 +407,8 @@
Exec(context.Context, *ExecRequest) (*ExecResponse, error)
Pty(context.Context, *PtyRequest) (*google_protobuf.Empty, error)
CloseStdin(context.Context, *CloseStdinRequest) (*google_protobuf.Empty, error)
+ Pause(context.Context, *PauseRequest) (*google_protobuf.Empty, error)
+ Resume(context.Context, *ResumeRequest) (*google_protobuf.Empty, error)
}
func RegisterContainerServiceServer(s *grpc.Server, srv ContainerServiceServer) {
@@ -556,6 +598,42 @@
return interceptor(ctx, in, info, handler)
}
+func _ContainerService_Pause_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(PauseRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(ContainerServiceServer).Pause(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/containerd.v1.services.ContainerService/Pause",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(ContainerServiceServer).Pause(ctx, req.(*PauseRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _ContainerService_Resume_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(ResumeRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(ContainerServiceServer).Resume(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/containerd.v1.services.ContainerService/Resume",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(ContainerServiceServer).Resume(ctx, req.(*ResumeRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
var _ContainerService_serviceDesc = grpc.ServiceDesc{
ServiceName: "containerd.v1.services.ContainerService",
HandlerType: (*ContainerServiceServer)(nil),
@@ -596,6 +674,14 @@
MethodName: "CloseStdin",
Handler: _ContainerService_CloseStdin_Handler,
},
+ {
+ MethodName: "Pause",
+ Handler: _ContainerService_Pause_Handler,
+ },
+ {
+ MethodName: "Resume",
+ Handler: _ContainerService_Resume_Handler,
+ },
},
Streams: []grpc.StreamDesc{
{
@@ -1083,6 +1169,54 @@
return i, nil
}
+func (m *PauseRequest) Marshal() (dAtA []byte, err error) {
+ size := m.Size()
+ dAtA = make([]byte, size)
+ n, err := m.MarshalTo(dAtA)
+ if err != nil {
+ return nil, err
+ }
+ return dAtA[:n], nil
+}
+
+func (m *PauseRequest) MarshalTo(dAtA []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ if len(m.ID) > 0 {
+ dAtA[i] = 0xa
+ i++
+ i = encodeVarintExecution(dAtA, i, uint64(len(m.ID)))
+ i += copy(dAtA[i:], m.ID)
+ }
+ return i, nil
+}
+
+func (m *ResumeRequest) Marshal() (dAtA []byte, err error) {
+ size := m.Size()
+ dAtA = make([]byte, size)
+ n, err := m.MarshalTo(dAtA)
+ if err != nil {
+ return nil, err
+ }
+ return dAtA[:n], nil
+}
+
+func (m *ResumeRequest) MarshalTo(dAtA []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ if len(m.ID) > 0 {
+ dAtA[i] = 0xa
+ i++
+ i = encodeVarintExecution(dAtA, i, uint64(len(m.ID)))
+ i += copy(dAtA[i:], m.ID)
+ }
+ return i, nil
+}
+
func encodeFixed64Execution(dAtA []byte, offset int, v uint64) int {
dAtA[offset] = uint8(v)
dAtA[offset+1] = uint8(v >> 8)
@@ -1317,6 +1451,26 @@
return n
}
+func (m *PauseRequest) Size() (n int) {
+ var l int
+ _ = l
+ l = len(m.ID)
+ if l > 0 {
+ n += 1 + l + sovExecution(uint64(l))
+ }
+ return n
+}
+
+func (m *ResumeRequest) Size() (n int) {
+ var l int
+ _ = l
+ l = len(m.ID)
+ if l > 0 {
+ n += 1 + l + sovExecution(uint64(l))
+ }
+ return n
+}
+
func sovExecution(x uint64) (n int) {
for {
n++
@@ -1489,6 +1643,26 @@
}, "")
return s
}
+func (this *PauseRequest) String() string {
+ if this == nil {
+ return "nil"
+ }
+ s := strings.Join([]string{`&PauseRequest{`,
+ `ID:` + fmt.Sprintf("%v", this.ID) + `,`,
+ `}`,
+ }, "")
+ return s
+}
+func (this *ResumeRequest) String() string {
+ if this == nil {
+ return "nil"
+ }
+ s := strings.Join([]string{`&ResumeRequest{`,
+ `ID:` + fmt.Sprintf("%v", this.ID) + `,`,
+ `}`,
+ }, "")
+ return s
+}
func valueToStringExecution(v interface{}) string {
rv := reflect.ValueOf(v)
if rv.IsNil() {
@@ -3060,6 +3234,164 @@
}
return nil
}
+func (m *PauseRequest) Unmarshal(dAtA []byte) error {
+ l := len(dAtA)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowExecution
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: PauseRequest: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: PauseRequest: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowExecution
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ stringLen |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLengthExecution
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.ID = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ default:
+ iNdEx = preIndex
+ skippy, err := skipExecution(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthExecution
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
+func (m *ResumeRequest) Unmarshal(dAtA []byte) error {
+ l := len(dAtA)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowExecution
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: ResumeRequest: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: ResumeRequest: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowExecution
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ stringLen |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLengthExecution
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.ID = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ default:
+ iNdEx = preIndex
+ skippy, err := skipExecution(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthExecution
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
func skipExecution(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
@@ -3170,56 +3502,59 @@
}
var fileDescriptorExecution = []byte{
- // 814 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x55, 0xcf, 0x6f, 0xe2, 0x46,
- 0x14, 0x8e, 0xf9, 0xe1, 0x90, 0x47, 0x48, 0xd3, 0x51, 0x84, 0x5c, 0x57, 0x02, 0xe4, 0x26, 0x29,
- 0xbd, 0x98, 0x96, 0xde, 0xaa, 0xb6, 0x12, 0x21, 0xa8, 0x8a, 0xd2, 0x34, 0xa9, 0xa9, 0xd4, 0x63,
- 0xe4, 0xe0, 0x09, 0x8c, 0x64, 0x3c, 0xae, 0x67, 0x9c, 0x86, 0x5b, 0x7b, 0xef, 0xa1, 0x7f, 0xc5,
- 0xde, 0xf7, 0xbf, 0xc8, 0x71, 0x8f, 0x7b, 0xca, 0x6e, 0xf8, 0x4b, 0x56, 0xe3, 0x1f, 0x60, 0x08,
- 0xb3, 0xde, 0x5c, 0xd0, 0x7b, 0x8f, 0xf7, 0xc6, 0xdf, 0xfb, 0xe6, 0xfb, 0x6c, 0xf8, 0x65, 0x4c,
- 0xf8, 0x24, 0xbc, 0x31, 0x47, 0x74, 0xda, 0x19, 0x51, 0x8f, 0xdb, 0xc4, 0xc3, 0x81, 0x93, 0x0d,
- 0x6d, 0x9f, 0x74, 0x18, 0x0e, 0xee, 0xc8, 0x08, 0xb3, 0x0e, 0xbe, 0xc7, 0xa3, 0x90, 0x13, 0xea,
- 0x2d, 0x23, 0xd3, 0x0f, 0x28, 0xa7, 0xa8, 0xbe, 0x1c, 0x31, 0xef, 0xbe, 0x33, 0xd3, 0x09, 0xfd,
- 0xcb, 0x31, 0xa5, 0x63, 0x17, 0x77, 0xa2, 0xae, 0x9b, 0xf0, 0xb6, 0x83, 0xa7, 0x3e, 0x9f, 0xc5,
- 0x43, 0xfa, 0x17, 0xeb, 0x7f, 0xda, 0x5e, 0xfa, 0xd7, 0xc1, 0x98, 0x8e, 0x69, 0x14, 0x76, 0x44,
- 0x94, 0x54, 0x7f, 0xfc, 0x24, 0xb8, 0x7c, 0xe6, 0x63, 0xd6, 0x99, 0xd2, 0xd0, 0xe3, 0xf1, 0x6f,
- 0x32, 0x7d, 0xfa, 0x82, 0xe9, 0x45, 0x71, 0x19, 0x25, 0xa7, 0x34, 0xd7, 0x41, 0x73, 0x32, 0xc5,
- 0x8c, 0xdb, 0x53, 0x3f, 0x6e, 0x30, 0xfe, 0x2d, 0x40, 0xad, 0x1f, 0x60, 0x9b, 0x63, 0x0b, 0xff,
- 0x15, 0x62, 0xc6, 0x51, 0x1d, 0x0a, 0xc4, 0xd1, 0x94, 0x96, 0xd2, 0xde, 0x39, 0x51, 0xe7, 0x8f,
- 0xcd, 0xc2, 0xd9, 0xa9, 0x55, 0x20, 0x0e, 0x6a, 0x43, 0x89, 0xf9, 0x78, 0xa4, 0x15, 0x5a, 0x4a,
- 0xbb, 0xda, 0x3d, 0x30, 0xe3, 0x93, 0xcd, 0xf4, 0x64, 0xb3, 0xe7, 0xcd, 0xac, 0xa8, 0x03, 0x75,
- 0x41, 0x0d, 0x28, 0xe5, 0xb7, 0x4c, 0x2b, 0xb6, 0x8a, 0xed, 0x6a, 0x57, 0x37, 0x57, 0xf9, 0x8e,
- 0x40, 0x9b, 0x17, 0x62, 0x59, 0x2b, 0xe9, 0x44, 0x1a, 0x6c, 0x07, 0xa1, 0x27, 0xd0, 0x69, 0x25,
- 0xf1, 0x68, 0x2b, 0x4d, 0xd1, 0x01, 0x94, 0x19, 0x77, 0x88, 0xa7, 0x95, 0xa3, 0x7a, 0x9c, 0xa0,
- 0x3a, 0xa8, 0x8c, 0x3b, 0x34, 0xe4, 0x9a, 0x1a, 0x95, 0x93, 0x2c, 0xa9, 0xe3, 0x20, 0xd0, 0xb6,
- 0x17, 0x75, 0x1c, 0x04, 0x48, 0x87, 0x0a, 0xc7, 0xc1, 0x94, 0x78, 0xb6, 0xab, 0x55, 0x5a, 0x4a,
- 0xbb, 0x62, 0x2d, 0x72, 0xe3, 0x07, 0xd8, 0x4b, 0x29, 0x60, 0x3e, 0xf5, 0x18, 0x96, 0x72, 0xb0,
- 0x0f, 0x45, 0x9f, 0x38, 0x11, 0x05, 0x35, 0x4b, 0x84, 0xc6, 0x31, 0xec, 0x0e, 0xb9, 0x1d, 0xf0,
- 0x1c, 0xf6, 0x8c, 0xaf, 0xa1, 0x76, 0x8a, 0x5d, 0x9c, 0x4b, 0xb3, 0xf1, 0x9f, 0x02, 0x7b, 0x69,
- 0x67, 0x0e, 0x9a, 0x26, 0x54, 0xf1, 0x3d, 0xe1, 0xd7, 0x8c, 0xdb, 0x3c, 0x64, 0x09, 0x2a, 0x10,
- 0xa5, 0x61, 0x54, 0x41, 0x3d, 0xd8, 0x11, 0x19, 0x76, 0xae, 0x6d, 0xae, 0x15, 0xa3, 0x7b, 0xd3,
- 0x9f, 0xdd, 0xdb, 0x1f, 0xa9, 0x22, 0x4e, 0x2a, 0x0f, 0x8f, 0xcd, 0xad, 0xff, 0xdf, 0x35, 0x15,
- 0xab, 0x12, 0x8f, 0xf5, 0xb8, 0x71, 0x04, 0xd5, 0x33, 0xef, 0x96, 0xe6, 0xa1, 0xae, 0x41, 0xf5,
- 0x57, 0xc2, 0x52, 0x16, 0x8c, 0xdf, 0x60, 0x37, 0x4e, 0x93, 0x0d, 0x7e, 0x06, 0x58, 0x48, 0x80,
- 0x69, 0x4a, 0xa4, 0x8a, 0xc6, 0x46, 0x55, 0xf4, 0xd3, 0x9a, 0x95, 0x99, 0x30, 0x2e, 0xa1, 0x7a,
- 0x4e, 0x5c, 0x37, 0x4f, 0xa2, 0xe2, 0xf2, 0xc9, 0x58, 0x5c, 0x71, 0xcc, 0x45, 0x92, 0x89, 0x6b,
- 0xb3, 0x5d, 0x37, 0x62, 0xa0, 0x62, 0x89, 0xd0, 0xf8, 0x0c, 0x6a, 0x83, 0x3b, 0xec, 0x71, 0x96,
- 0x22, 0x7e, 0xad, 0x40, 0x75, 0x70, 0x8f, 0x47, 0x79, 0x8f, 0xc8, 0xea, 0xa8, 0xb0, 0xaa, 0xa3,
- 0xa5, 0x52, 0x8b, 0x9b, 0x95, 0x5a, 0x92, 0x28, 0xb5, 0xbc, 0xa2, 0xd4, 0xd4, 0x67, 0x6a, 0x9e,
- 0xcf, 0x8c, 0x16, 0xec, 0xc6, 0x90, 0x13, 0x96, 0x13, 0x75, 0x2a, 0x4b, 0x75, 0x3a, 0x00, 0x57,
- 0x7c, 0x96, 0xb7, 0xd3, 0x33, 0x55, 0x8b, 0x4d, 0xfe, 0x26, 0x0e, 0x9f, 0x44, 0x9b, 0xd4, 0xac,
- 0x38, 0x11, 0x88, 0x27, 0x98, 0x8c, 0x27, 0xf1, 0x26, 0x35, 0x2b, 0xc9, 0x8c, 0x9f, 0xe0, 0xf3,
- 0xbe, 0x4b, 0x19, 0x1e, 0x8a, 0x7d, 0x5f, 0xfc, 0xb0, 0xee, 0x2b, 0x15, 0xf6, 0x17, 0xd7, 0x3e,
- 0x8c, 0xdf, 0xc5, 0xe8, 0x4f, 0x50, 0x63, 0x4f, 0xa2, 0x23, 0x73, 0xf3, 0xdb, 0xda, 0x5c, 0x79,
- 0x6d, 0xe9, 0xc7, 0x79, 0x6d, 0x09, 0x49, 0x03, 0x28, 0x47, 0x86, 0x45, 0x87, 0xb2, 0x81, 0xac,
- 0x9f, 0xf5, 0xfa, 0x33, 0xfe, 0x07, 0xe2, 0x9b, 0x20, 0xf0, 0xc5, 0x2e, 0x95, 0xe3, 0x5b, 0xf1,
- 0xbb, 0x1c, 0xdf, 0x9a, 0xd9, 0xcf, 0xa1, 0x24, 0x0c, 0x87, 0xbe, 0x92, 0xf5, 0x67, 0xec, 0xa8,
- 0xe7, 0x78, 0x08, 0xfd, 0x0e, 0x25, 0xe1, 0x43, 0xf9, 0x61, 0x19, 0xd3, 0xea, 0x87, 0x1f, 0x6f,
- 0x4a, 0xf0, 0xf5, 0xa1, 0x24, 0xac, 0x28, 0x3f, 0x32, 0x63, 0x54, 0x29, 0x7b, 0x17, 0xa0, 0xc6,
- 0xf6, 0x93, 0xb3, 0xb7, 0x62, 0x4f, 0x7d, 0xf3, 0x27, 0x24, 0xea, 0xf9, 0x56, 0x11, 0x6b, 0x0a,
- 0x23, 0xc8, 0x31, 0x65, 0x9c, 0x2d, 0x5f, 0x73, 0xc5, 0x4b, 0x3d, 0x28, 0x5e, 0xf1, 0x19, 0x32,
- 0x64, 0xcd, 0x4b, 0x5b, 0x49, 0x97, 0xbc, 0x04, 0x58, 0xda, 0x02, 0x7d, 0x23, 0xd5, 0xe7, 0xba,
- 0x75, 0x64, 0x07, 0x9e, 0x68, 0x0f, 0x4f, 0x8d, 0xad, 0xb7, 0x4f, 0x8d, 0xad, 0x7f, 0xe6, 0x0d,
- 0xe5, 0x61, 0xde, 0x50, 0xde, 0xcc, 0x1b, 0xca, 0xfb, 0x79, 0x43, 0xb9, 0x51, 0xa3, 0xce, 0xef,
- 0x3f, 0x04, 0x00, 0x00, 0xff, 0xff, 0x74, 0xa6, 0x26, 0x26, 0x22, 0x09, 0x00, 0x00,
+ // 854 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x94, 0x55, 0x4f, 0x8f, 0xdb, 0x44,
+ 0x14, 0x5f, 0xe7, 0x8f, 0x37, 0x7d, 0xd9, 0x94, 0x32, 0x5a, 0xad, 0x8c, 0x91, 0x92, 0xc8, 0xb4,
+ 0x25, 0x5c, 0x1c, 0x58, 0x6e, 0x08, 0x90, 0xb2, 0xd9, 0xa8, 0xaa, 0x4a, 0xe9, 0xe2, 0x20, 0x71,
+ 0xac, 0xbc, 0xf1, 0x6c, 0x32, 0x92, 0xe3, 0x31, 0x9e, 0xf1, 0xb2, 0xb9, 0xc1, 0x9d, 0x03, 0x5f,
+ 0x85, 0x0b, 0x9f, 0x61, 0x8f, 0x1c, 0x39, 0x15, 0x9a, 0x4f, 0x82, 0x66, 0xc6, 0x4e, 0xec, 0x6c,
+ 0xa6, 0xee, 0x5e, 0xa2, 0x79, 0x2f, 0xef, 0xbd, 0xf9, 0xbd, 0x37, 0xbf, 0xdf, 0x33, 0x3c, 0x9b,
+ 0x13, 0xbe, 0x48, 0x2f, 0xdd, 0x19, 0x5d, 0x0e, 0x67, 0x34, 0xe2, 0x3e, 0x89, 0x70, 0x12, 0x14,
+ 0x8f, 0x7e, 0x4c, 0x86, 0x0c, 0x27, 0xd7, 0x64, 0x86, 0xd9, 0x10, 0xdf, 0xe0, 0x59, 0xca, 0x09,
+ 0x8d, 0xb6, 0x27, 0x37, 0x4e, 0x28, 0xa7, 0xe8, 0x64, 0x9b, 0xe2, 0x5e, 0x7f, 0xe1, 0xe6, 0x19,
+ 0xf6, 0xc7, 0x73, 0x4a, 0xe7, 0x21, 0x1e, 0xca, 0xa8, 0xcb, 0xf4, 0x6a, 0x88, 0x97, 0x31, 0x5f,
+ 0xa9, 0x24, 0xfb, 0xa3, 0xdd, 0x3f, 0xfd, 0x28, 0xff, 0xeb, 0x78, 0x4e, 0xe7, 0x54, 0x1e, 0x87,
+ 0xe2, 0x94, 0x79, 0xbf, 0x7e, 0x2f, 0xb8, 0x7c, 0x15, 0x63, 0x36, 0x5c, 0xd2, 0x34, 0xe2, 0xea,
+ 0x37, 0xcb, 0x3e, 0xbf, 0x47, 0xf6, 0xc6, 0xb9, 0x3d, 0x65, 0x55, 0x7a, 0xbb, 0xa0, 0x39, 0x59,
+ 0x62, 0xc6, 0xfd, 0x65, 0xac, 0x02, 0x9c, 0xdf, 0x6a, 0xd0, 0x19, 0x27, 0xd8, 0xe7, 0xd8, 0xc3,
+ 0x3f, 0xa7, 0x98, 0x71, 0x74, 0x02, 0x35, 0x12, 0x58, 0x46, 0xdf, 0x18, 0x3c, 0x38, 0x33, 0xd7,
+ 0x6f, 0x7a, 0xb5, 0xe7, 0xe7, 0x5e, 0x8d, 0x04, 0x68, 0x00, 0x0d, 0x16, 0xe3, 0x99, 0x55, 0xeb,
+ 0x1b, 0x83, 0xf6, 0xe9, 0xb1, 0xab, 0x2a, 0xbb, 0x79, 0x65, 0x77, 0x14, 0xad, 0x3c, 0x19, 0x81,
+ 0x4e, 0xc1, 0x4c, 0x28, 0xe5, 0x57, 0xcc, 0xaa, 0xf7, 0xeb, 0x83, 0xf6, 0xa9, 0xed, 0x96, 0xe7,
+ 0x2d, 0x41, 0xbb, 0x2f, 0x45, 0xb3, 0x5e, 0x16, 0x89, 0x2c, 0x38, 0x4c, 0xd2, 0x48, 0xa0, 0xb3,
+ 0x1a, 0xe2, 0x6a, 0x2f, 0x37, 0xd1, 0x31, 0x34, 0x19, 0x0f, 0x48, 0x64, 0x35, 0xa5, 0x5f, 0x19,
+ 0xe8, 0x04, 0x4c, 0xc6, 0x03, 0x9a, 0x72, 0xcb, 0x94, 0xee, 0xcc, 0xca, 0xfc, 0x38, 0x49, 0xac,
+ 0xc3, 0x8d, 0x1f, 0x27, 0x09, 0xb2, 0xa1, 0xc5, 0x71, 0xb2, 0x24, 0x91, 0x1f, 0x5a, 0xad, 0xbe,
+ 0x31, 0x68, 0x79, 0x1b, 0xdb, 0xf9, 0x0a, 0x1e, 0xe6, 0x23, 0x60, 0x31, 0x8d, 0x18, 0xd6, 0xce,
+ 0xe0, 0x11, 0xd4, 0x63, 0x12, 0xc8, 0x11, 0x74, 0x3c, 0x71, 0x74, 0x9e, 0xc2, 0xd1, 0x94, 0xfb,
+ 0x09, 0xaf, 0x98, 0x9e, 0xf3, 0x29, 0x74, 0xce, 0x71, 0x88, 0x2b, 0xc7, 0xec, 0xfc, 0x6e, 0xc0,
+ 0xc3, 0x3c, 0xb2, 0x02, 0x4d, 0x0f, 0xda, 0xf8, 0x86, 0xf0, 0xd7, 0x8c, 0xfb, 0x3c, 0x65, 0x19,
+ 0x2a, 0x10, 0xae, 0xa9, 0xf4, 0xa0, 0x11, 0x3c, 0x10, 0x16, 0x0e, 0x5e, 0xfb, 0xdc, 0xaa, 0xcb,
+ 0x77, 0xb3, 0xef, 0xbc, 0xdb, 0x8f, 0x39, 0x23, 0xce, 0x5a, 0xb7, 0x6f, 0x7a, 0x07, 0x7f, 0xfc,
+ 0xdb, 0x33, 0xbc, 0x96, 0x4a, 0x1b, 0x71, 0xe7, 0x09, 0xb4, 0x9f, 0x47, 0x57, 0xb4, 0x0a, 0x75,
+ 0x07, 0xda, 0xdf, 0x11, 0x96, 0x4f, 0xc1, 0xf9, 0x1e, 0x8e, 0x94, 0x99, 0x75, 0xf0, 0x2d, 0xc0,
+ 0x86, 0x02, 0xcc, 0x32, 0x24, 0x2b, 0xba, 0x7b, 0x59, 0x31, 0xce, 0x7d, 0x5e, 0x21, 0xc3, 0x79,
+ 0x05, 0xed, 0x17, 0x24, 0x0c, 0xab, 0x28, 0x2a, 0x1e, 0x9f, 0xcc, 0xc5, 0x13, 0xab, 0x59, 0x64,
+ 0x96, 0x78, 0x36, 0x3f, 0x0c, 0xe5, 0x04, 0x5a, 0x9e, 0x38, 0x3a, 0x1f, 0x40, 0x67, 0x72, 0x8d,
+ 0x23, 0xce, 0x72, 0xc4, 0x7f, 0x1a, 0xd0, 0x9e, 0xdc, 0xe0, 0x59, 0xd5, 0x15, 0x45, 0x1e, 0xd5,
+ 0xca, 0x3c, 0xda, 0x32, 0xb5, 0xbe, 0x9f, 0xa9, 0x0d, 0x0d, 0x53, 0x9b, 0x25, 0xa6, 0xe6, 0x3a,
+ 0x33, 0xab, 0x74, 0xe6, 0xf4, 0xe1, 0x48, 0x41, 0xce, 0xa6, 0x9c, 0xb1, 0xd3, 0xd8, 0xb2, 0x33,
+ 0x00, 0xb8, 0xe0, 0xab, 0xaa, 0x9e, 0xee, 0xb0, 0x5a, 0x74, 0xf2, 0x0b, 0x09, 0xf8, 0x42, 0x76,
+ 0xd2, 0xf1, 0x94, 0x21, 0x10, 0x2f, 0x30, 0x99, 0x2f, 0x54, 0x27, 0x1d, 0x2f, 0xb3, 0x9c, 0x6f,
+ 0xe0, 0xc3, 0x71, 0x48, 0x19, 0x9e, 0x8a, 0x7e, 0xef, 0x7d, 0x99, 0x90, 0xd0, 0x85, 0x9f, 0x32,
+ 0xfc, 0x1e, 0x12, 0xf2, 0x30, 0x4b, 0x97, 0x55, 0x81, 0xa7, 0x7f, 0x1d, 0xc2, 0xa3, 0x0d, 0x8f,
+ 0xa6, 0x6a, 0xb9, 0xa3, 0x9f, 0xc0, 0x54, 0x22, 0x47, 0x4f, 0xdc, 0xfd, 0xeb, 0xdf, 0x2d, 0xed,
+ 0x41, 0xfb, 0x69, 0x55, 0x58, 0x36, 0xf5, 0x09, 0x34, 0xe5, 0x06, 0x40, 0x8f, 0x75, 0x09, 0xc5,
+ 0x05, 0x61, 0x9f, 0xdc, 0x79, 0xd0, 0x89, 0xf8, 0xc8, 0x08, 0x7c, 0x4a, 0xf6, 0x7a, 0x7c, 0xa5,
+ 0x05, 0xa2, 0xc7, 0xb7, 0xb3, 0x3d, 0x5e, 0x40, 0x43, 0x28, 0x18, 0x7d, 0xa2, 0x8b, 0x2f, 0xe8,
+ 0xdb, 0xae, 0x10, 0x25, 0xfa, 0x01, 0x1a, 0x42, 0xd8, 0xfa, 0x62, 0x85, 0x2d, 0x60, 0x3f, 0x7e,
+ 0x77, 0x50, 0x86, 0x6f, 0x0c, 0x0d, 0xa1, 0x6d, 0x7d, 0xc9, 0x82, 0xf2, 0xb5, 0xd3, 0x7b, 0x09,
+ 0xa6, 0xd2, 0xb3, 0x7e, 0x7a, 0x25, 0xbd, 0xdb, 0xfb, 0xbf, 0x49, 0x32, 0xe6, 0x73, 0x43, 0xb4,
+ 0x29, 0x94, 0xa5, 0xc7, 0x54, 0x58, 0x15, 0xfa, 0x36, 0x4b, 0xe2, 0x1c, 0x41, 0xfd, 0x82, 0xaf,
+ 0x90, 0xa3, 0x0b, 0xde, 0xea, 0x54, 0xdb, 0xe4, 0x2b, 0x80, 0xad, 0xce, 0xd0, 0x67, 0x5a, 0x7e,
+ 0xee, 0x6a, 0x51, 0x5b, 0x70, 0x02, 0x4d, 0xa9, 0x3c, 0x3d, 0x75, 0x8b, 0xc2, 0xd4, 0x96, 0x79,
+ 0x06, 0xa6, 0x12, 0xa6, 0x7e, 0xf8, 0x25, 0xe1, 0xea, 0x0a, 0x9d, 0x59, 0xb7, 0x6f, 0xbb, 0x07,
+ 0xff, 0xbc, 0xed, 0x1e, 0xfc, 0xba, 0xee, 0x1a, 0xb7, 0xeb, 0xae, 0xf1, 0xf7, 0xba, 0x6b, 0xfc,
+ 0xb7, 0xee, 0x1a, 0x97, 0xa6, 0x8c, 0xfc, 0xf2, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x67, 0xba,
+ 0xdb, 0xdc, 0x03, 0x0a, 0x00, 0x00,
}
diff --git a/api/services/execution/execution.proto b/api/services/execution/execution.proto
index 4770138..b964a87 100644
--- a/api/services/execution/execution.proto
+++ b/api/services/execution/execution.proto
@@ -20,6 +20,8 @@
rpc Exec(ExecRequest) returns (ExecResponse);
rpc Pty(PtyRequest) returns (google.protobuf.Empty);
rpc CloseStdin(CloseStdinRequest) returns (google.protobuf.Empty);
+ rpc Pause(PauseRequest) returns (google.protobuf.Empty);
+ rpc Resume(ResumeRequest) returns (google.protobuf.Empty);
}
message CreateRequest {
@@ -96,3 +98,11 @@
string id = 1 [(gogoproto.customname) = "ID"];
uint32 pid = 2;
}
+
+message PauseRequest {
+ string id = 1 [(gogoproto.customname) = "ID"];
+}
+
+message ResumeRequest {
+ string id = 1 [(gogoproto.customname) = "ID"];
+}
diff --git a/api/services/images/images.pb.go b/api/services/images/images.pb.go
index 63bfcde..40680b0 100644
--- a/api/services/images/images.pb.go
+++ b/api/services/images/images.pb.go
@@ -1335,7 +1335,7 @@
var fileDescriptorImages = []byte{
// 419 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x52, 0x4d, 0x6f, 0xd3, 0x40,
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x94, 0x52, 0x4d, 0x6f, 0xd3, 0x40,
0x10, 0xcd, 0x36, 0xa9, 0x25, 0xc6, 0xe4, 0xb2, 0xaa, 0x90, 0x71, 0x91, 0x6b, 0x99, 0x4b, 0xc5,
0x61, 0x0d, 0xe6, 0x02, 0x52, 0x29, 0x22, 0x2a, 0x54, 0x48, 0x1c, 0x2a, 0x1f, 0xb9, 0x39, 0xee,
0x60, 0x2c, 0xd5, 0x5e, 0xe3, 0x5d, 0x57, 0xca, 0x0d, 0xfe, 0x5d, 0x8e, 0x1c, 0x39, 0x21, 0xe2,
diff --git a/api/services/rootfs/rootfs.pb.go b/api/services/rootfs/rootfs.pb.go
index b012d69..1335eee 100644
--- a/api/services/rootfs/rootfs.pb.go
+++ b/api/services/rootfs/rootfs.pb.go
@@ -1103,7 +1103,7 @@
var fileDescriptorRootfs = []byte{
// 428 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x52, 0x4d, 0xab, 0xd3, 0x40,
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x52, 0x4d, 0xab, 0xd3, 0x40,
0x14, 0xed, 0xf8, 0x24, 0xad, 0x23, 0x7d, 0x8b, 0xc1, 0x45, 0x08, 0x9a, 0x94, 0xb8, 0x29, 0x82,
0x09, 0xd6, 0x85, 0x1b, 0x5d, 0xf8, 0x5e, 0x2c, 0xbe, 0x85, 0x20, 0x11, 0xd1, 0x9d, 0x4c, 0x93,
0x31, 0x1d, 0x6c, 0xe7, 0x8e, 0x33, 0xd3, 0x42, 0x77, 0xfe, 0x0e, 0x7f, 0x51, 0x97, 0x2e, 0x45,
diff --git a/api/services/shim/shim.pb.go b/api/services/shim/shim.pb.go
index b19c1cc..ef44327 100644
--- a/api/services/shim/shim.pb.go
+++ b/api/services/shim/shim.pb.go
@@ -3468,7 +3468,7 @@
var fileDescriptorShim = []byte{
// 913 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x55, 0x4f, 0x6f, 0xe3, 0x44,
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x94, 0x55, 0x4f, 0x6f, 0xe3, 0x44,
0x14, 0xaf, 0x93, 0xd4, 0x4d, 0x5f, 0xd6, 0x5d, 0x18, 0x55, 0x95, 0x37, 0x45, 0x49, 0xb1, 0x84,
0xc8, 0x2e, 0x92, 0x03, 0xd9, 0x1b, 0x82, 0x43, 0xbb, 0x2d, 0x62, 0x61, 0x91, 0xa2, 0xe9, 0xde,
0x90, 0xa8, 0xdc, 0x78, 0x9a, 0x8c, 0x64, 0x7b, 0x8c, 0x67, 0x5c, 0x36, 0x37, 0xce, 0x9c, 0xb8,
diff --git a/api/types/container/container.pb.go b/api/types/container/container.pb.go
index b1e6602..e2444ac 100644
--- a/api/types/container/container.pb.go
+++ b/api/types/container/container.pb.go
@@ -1364,7 +1364,7 @@
var fileDescriptorContainer = []byte{
// 576 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x52, 0xcf, 0x6e, 0xd3, 0x4e,
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x52, 0xcf, 0x6e, 0xd3, 0x4e,
0x18, 0xcc, 0xda, 0xce, 0xbf, 0x2f, 0x6a, 0x7e, 0xd6, 0xfe, 0x10, 0x32, 0x41, 0x4a, 0xa2, 0x08,
0x09, 0x0b, 0x09, 0x47, 0xa4, 0x17, 0xae, 0x6e, 0x6d, 0x95, 0x0a, 0xe1, 0x84, 0x8d, 0x23, 0x7a,
0x8b, 0xdc, 0x78, 0x31, 0x8b, 0x1a, 0xdb, 0xb2, 0xd7, 0x85, 0xde, 0x78, 0x04, 0x9e, 0x81, 0xa7,
diff --git a/api/types/descriptor/descriptor.pb.go b/api/types/descriptor/descriptor.pb.go
index e379da3..4062abc 100644
--- a/api/types/descriptor/descriptor.pb.go
+++ b/api/types/descriptor/descriptor.pb.go
@@ -404,7 +404,7 @@
var fileDescriptorDescriptor = []byte{
// 229 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x72, 0x4b, 0xcf, 0x2c, 0xc9,
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x72, 0x4b, 0xcf, 0x2c, 0xc9,
0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d,
0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0x97, 0x54, 0x16, 0xa4, 0x16, 0xeb, 0xa7, 0xa4, 0x16,
0x27, 0x17, 0x65, 0x16, 0x94, 0xe4, 0x17, 0x21, 0x31, 0xf5, 0x0a, 0x8a, 0xf2, 0x4b, 0xf2, 0x85,
diff --git a/api/types/mount/mount.pb.go b/api/types/mount/mount.pb.go
index 1748f6e..c509ea1 100644
--- a/api/types/mount/mount.pb.go
+++ b/api/types/mount/mount.pb.go
@@ -473,7 +473,7 @@
var fileDescriptorMount = []byte{
// 197 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xb2, 0x49, 0xcf, 0x2c, 0xc9,
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xb2, 0x49, 0xcf, 0x2c, 0xc9,
0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d,
0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0x97, 0x54, 0x16, 0xa4, 0x16, 0xeb, 0xe7, 0xe6, 0x97,
0xe6, 0x95, 0x40, 0x48, 0xbd, 0x82, 0xa2, 0xfc, 0x92, 0x7c, 0x21, 0x61, 0x84, 0x3a, 0xbd, 0x32,
diff --git a/archive/tar_test.go b/archive/tar_test.go
index db216c0..33b83b0 100644
--- a/archive/tar_test.go
+++ b/archive/tar_test.go
@@ -1,6 +1,7 @@
package archive
import (
+ "bytes"
"context"
"io/ioutil"
"os"
@@ -46,64 +47,7 @@
}
func TestDiffApply(t *testing.T) {
- as := []fstest.Applier{
- baseApplier,
- fstest.Apply(
- fstest.CreateFile("/etc/hosts", []byte("127.0.0.1 localhost.localdomain"), 0644),
- fstest.CreateFile("/etc/fstab", []byte("/dev/sda1\t/\text4\tdefaults 1 1\n"), 0600),
- fstest.CreateFile("/etc/badfile", []byte(""), 0666),
- fstest.CreateFile("/home/derek/.zshrc", []byte("#ZSH is just better\n"), 0640),
- ),
- fstest.Apply(
- fstest.Remove("/etc/badfile"),
- fstest.Rename("/home/derek", "/home/notderek"),
- ),
- fstest.Apply(
- fstest.RemoveAll("/usr"),
- fstest.Remove("/etc/hosts.allow"),
- ),
- fstest.Apply(
- fstest.RemoveAll("/home"),
- fstest.CreateDir("/home/derek", 0700),
- fstest.CreateFile("/home/derek/.bashrc", []byte("#not going away\n"), 0640),
- fstest.Link("/etc/hosts", "/etc/hosts.allow"),
- ),
- }
-
- if err := testDiffApply(as...); err != nil {
- t.Fatalf("Test diff apply failed: %+v", err)
- }
-}
-
-// TestDiffApplyDeletion checks various deletion scenarios to ensure
-// deletions are properly picked up and applied
-func TestDiffApplyDeletion(t *testing.T) {
- as := []fstest.Applier{
- fstest.Apply(
- fstest.CreateDir("/test/somedir", 0755),
- fstest.CreateDir("/lib", 0700),
- fstest.CreateFile("/lib/hidden", []byte{}, 0644),
- ),
- fstest.Apply(
- fstest.CreateFile("/test/a", []byte{}, 0644),
- fstest.CreateFile("/test/b", []byte{}, 0644),
- fstest.CreateDir("/test/otherdir", 0755),
- fstest.CreateFile("/test/otherdir/.empty", []byte{}, 0644),
- fstest.RemoveAll("/lib"),
- fstest.CreateDir("/lib", 0700),
- fstest.CreateFile("/lib/not-hidden", []byte{}, 0644),
- ),
- fstest.Apply(
- fstest.Remove("/test/a"),
- fstest.Remove("/test/b"),
- fstest.RemoveAll("/test/otherdir"),
- fstest.CreateFile("/lib/newfile", []byte{}, 0644),
- ),
- }
-
- if err := testDiffApply(as...); err != nil {
- t.Fatalf("Test diff apply failed: %+v", err)
- }
+ fstest.FSSuite(t, diffApplier{})
}
func testApply(a fstest.Applier) error {
@@ -174,48 +118,43 @@
return fstest.CheckDirectoryEqual(td, dest)
}
-func testDiffApply(as ...fstest.Applier) error {
- base, err := ioutil.TempDir("", "test-diff-apply-base-")
- if err != nil {
- return errors.Wrap(err, "failed to create temp dir")
- }
- defer os.RemoveAll(base)
- dest, err := ioutil.TempDir("", "test-diff-apply-dest-")
- if err != nil {
- return errors.Wrap(err, "failed to create temp dir")
- }
- defer os.RemoveAll(dest)
+type diffApplier struct{}
- ctx := context.Background()
- for i, a := range as {
- if err := diffApply(ctx, a, base, dest); err != nil {
- return errors.Wrapf(err, "diff apply failed at layer %d", i)
- }
+func (d diffApplier) TestContext(ctx context.Context) (context.Context, func(), error) {
+ base, err := ioutil.TempDir("", "test-diff-apply-")
+ if err != nil {
+ return ctx, nil, errors.Wrap(err, "failed to create temp dir")
}
- return nil
+ return context.WithValue(ctx, d, base), func() {
+ os.RemoveAll(base)
+ }, nil
}
-// diffApply applies the given changes on the base and
-// computes the diff and applies to the dest.
-func diffApply(ctx context.Context, a fstest.Applier, base, dest string) error {
- baseCopy, err := ioutil.TempDir("", "test-diff-apply-copy-")
+func (d diffApplier) Apply(ctx context.Context, a fstest.Applier) (string, func(), error) {
+ base := ctx.Value(d).(string)
+
+ applyCopy, err := ioutil.TempDir("", "test-diffapply-apply-copy-")
if err != nil {
- return errors.Wrap(err, "failed to create temp dir")
+ return "", nil, errors.Wrap(err, "failed to create temp dir")
}
- defer os.RemoveAll(baseCopy)
- if err := fs.CopyDir(baseCopy, base); err != nil {
- return errors.Wrap(err, "failed to copy base")
+ defer os.RemoveAll(applyCopy)
+ if err = fs.CopyDir(applyCopy, base); err != nil {
+ return "", nil, errors.Wrap(err, "failed to copy base")
+ }
+ if err := a.Apply(applyCopy); err != nil {
+ return "", nil, errors.Wrap(err, "failed to apply changes to copy of base")
}
- if err := a.Apply(base); err != nil {
- return errors.Wrap(err, "failed to apply changes to base")
+ diffBytes, err := ioutil.ReadAll(Diff(ctx, base, applyCopy))
+ if err != nil {
+ return "", nil, errors.Wrap(err, "failed to create diff")
}
- if _, err := Apply(ctx, dest, Diff(ctx, baseCopy, base)); err != nil {
- return errors.Wrap(err, "failed to apply tar stream")
+ if _, err = Apply(ctx, base, bytes.NewReader(diffBytes)); err != nil {
+ return "", nil, errors.Wrap(err, "failed to apply tar stream")
}
- return fstest.CheckDirectoryEqual(base, dest)
+ return base, nil, nil
}
func readDirNames(p string) ([]string, error) {
diff --git a/archive/tar_linux.go b/archive/tar_unix.go
similarity index 97%
rename from archive/tar_linux.go
rename to archive/tar_unix.go
index f4b6a09..107d1a5 100644
--- a/archive/tar_linux.go
+++ b/archive/tar_unix.go
@@ -1,3 +1,5 @@
+// +build !windows
+
package archive
import (
@@ -6,9 +8,9 @@
"sync"
"syscall"
+ "github.com/containerd/continuity/sysx"
"github.com/opencontainers/runc/libcontainer/system"
"github.com/pkg/errors"
- "github.com/stevvooe/continuity/sysx"
)
func tarName(p string) (string, error) {
diff --git a/archive/time_darwin.go b/archive/time_darwin.go
new file mode 100644
index 0000000..2ac517a
--- /dev/null
+++ b/archive/time_darwin.go
@@ -0,0 +1,14 @@
+package archive
+
+import (
+ "time"
+
+ "github.com/pkg/errors"
+)
+
+// as at MacOS 10.12 there is apparently no way to set timestamps
+// with nanosecond precision. We could fall back to utimes/lutimes
+// and lose the precision as a temporary workaround.
+func chtimes(path string, atime, mtime time.Time) error {
+ return errors.New("OSX missing UtimesNanoAt")
+}
diff --git a/archive/time_linux.go b/archive/time_unix.go
similarity index 94%
rename from archive/time_linux.go
rename to archive/time_unix.go
index 4863608..5f17084 100644
--- a/archive/time_linux.go
+++ b/archive/time_unix.go
@@ -1,3 +1,5 @@
+// +build linux freebsd
+
package archive
import (
diff --git a/cmd/containerd/builtins_btrfs_linux.go b/cmd/containerd/builtins_btrfs_linux.go
new file mode 100644
index 0000000..f5aa917
--- /dev/null
+++ b/cmd/containerd/builtins_btrfs_linux.go
@@ -0,0 +1,5 @@
+// +build !containerd_no_btrfs
+
+package main
+
+import _ "github.com/containerd/containerd/snapshot/btrfs"
diff --git a/cmd/containerd/builtins_linux.go b/cmd/containerd/builtins_linux.go
index fa26830..1116acc 100644
--- a/cmd/containerd/builtins_linux.go
+++ b/cmd/containerd/builtins_linux.go
@@ -3,6 +3,5 @@
import (
_ "github.com/containerd/containerd/linux"
_ "github.com/containerd/containerd/metrics/cgroups"
- _ "github.com/containerd/containerd/snapshot/btrfs"
_ "github.com/containerd/containerd/snapshot/overlay"
)
diff --git a/cmd/containerd/builtins_unix.go b/cmd/containerd/builtins_unix.go
new file mode 100644
index 0000000..1513315
--- /dev/null
+++ b/cmd/containerd/builtins_unix.go
@@ -0,0 +1,7 @@
+// +build darwin freebsd
+
+package main
+
+import (
+ _ "github.com/containerd/containerd/snapshot/naive"
+)
diff --git a/cmd/containerd/config_linux.go b/cmd/containerd/config_linux.go
new file mode 100644
index 0000000..64863fa
--- /dev/null
+++ b/cmd/containerd/config_linux.go
@@ -0,0 +1,16 @@
+package main
+
+func defaultConfig() *config {
+ return &config{
+ Root: "/var/lib/containerd",
+ State: "/run/containerd",
+ GRPC: grpcConfig{
+ Address: "/run/containerd/containerd.sock",
+ },
+ Debug: debug{
+ Level: "info",
+ Address: "/run/containerd/debug.sock",
+ },
+ Snapshotter: "overlay",
+ }
+}
diff --git a/cmd/containerd/config_unix.go b/cmd/containerd/config_unix.go
index 7cb0062..358969b 100644
--- a/cmd/containerd/config_unix.go
+++ b/cmd/containerd/config_unix.go
@@ -1,4 +1,4 @@
-// +build !windows
+// +build darwin freebsd
package main
@@ -13,6 +13,6 @@
Level: "info",
Address: "/run/containerd/debug.sock",
},
- Snapshotter: "overlay",
+ Snapshotter: "naive",
}
}
diff --git a/cmd/containerd/main_linux.go b/cmd/containerd/main_linux.go
new file mode 100644
index 0000000..0717e78
--- /dev/null
+++ b/cmd/containerd/main_linux.go
@@ -0,0 +1,59 @@
+package main
+
+import (
+ "os"
+
+ "golang.org/x/sys/unix"
+
+ "github.com/containerd/containerd/log"
+ "github.com/containerd/containerd/reaper"
+ "github.com/containerd/containerd/sys"
+ "github.com/urfave/cli"
+ "google.golang.org/grpc"
+)
+
+const (
+ defaultConfigPath = "/etc/containerd/config.toml"
+)
+
+var (
+ handledSignals = []os.Signal{unix.SIGTERM, unix.SIGINT, unix.SIGUSR1, unix.SIGCHLD}
+)
+
+func platformInit(context *cli.Context) error {
+ if conf.Subreaper {
+ log.G(global).Info("setting subreaper...")
+ if err := sys.SetSubreaper(1); err != nil {
+ return err
+ }
+ }
+ if conf.OOMScore != 0 {
+ log.G(global).Infof("changing OOM score to %d", conf.OOMScore)
+ if err := sys.SetOOMScore(os.Getpid(), conf.OOMScore); err != nil {
+ return err
+ }
+ }
+ if err := os.MkdirAll(conf.State, 0750); err != nil {
+ return err
+ }
+ if err := os.Chown(conf.State, conf.GRPC.Uid, conf.GRPC.Gid); err != nil {
+ return err
+ }
+ return nil
+}
+
+func handleSignals(signals chan os.Signal, server *grpc.Server) error {
+ for s := range signals {
+ log.G(global).WithField("signal", s).Debug("received signal")
+ switch s {
+ case unix.SIGCHLD:
+ if err := reaper.Reap(); err != nil {
+ log.G(global).WithError(err).Error("reap containerd processes")
+ }
+ default:
+ server.Stop()
+ return nil
+ }
+ }
+ return nil
+}
diff --git a/cmd/containerd/main_unix.go b/cmd/containerd/main_unix.go
index 6aa5626..24ccab7 100644
--- a/cmd/containerd/main_unix.go
+++ b/cmd/containerd/main_unix.go
@@ -1,4 +1,4 @@
-// +build !windows
+// +build darwin freebsd
package main
@@ -9,7 +9,6 @@
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/reaper"
- "github.com/containerd/containerd/sys"
"github.com/urfave/cli"
"google.golang.org/grpc"
)
@@ -23,17 +22,11 @@
)
func platformInit(context *cli.Context) error {
- if conf.Subreaper {
- log.G(global).Info("setting subreaper...")
- if err := sys.SetSubreaper(1); err != nil {
- return err
- }
+ if err := os.MkdirAll(conf.State, 0750); err != nil {
+ return err
}
- if conf.OOMScore != 0 {
- log.G(global).Infof("changing OOM score to %d", conf.OOMScore)
- if err := sys.SetOOMScore(os.Getpid(), conf.OOMScore); err != nil {
- return err
- }
+ if err := os.Chown(conf.State, conf.GRPC.Uid, conf.GRPC.Gid); err != nil {
+ return err
}
return nil
}
diff --git a/cmd/ctr/delete.go b/cmd/ctr/delete.go
index 4a31d57..99988e6 100644
--- a/cmd/ctr/delete.go
+++ b/cmd/ctr/delete.go
@@ -2,9 +2,9 @@
import (
gocontext "context"
- "errors"
"github.com/containerd/containerd/api/services/execution"
+ "github.com/pkg/errors"
"github.com/urfave/cli"
)
@@ -19,7 +19,7 @@
}
id := context.Args().First()
if id == "" {
- return errors.New(" id must be provided")
+ return errors.New("container id must be provided")
}
_, err = containers.Delete(gocontext.Background(), &execution.DeleteRequest{
ID: id,
diff --git a/cmd/ctr/exec.go b/cmd/ctr/exec.go
index e2cd1e5..67f440e 100644
--- a/cmd/ctr/exec.go
+++ b/cmd/ctr/exec.go
@@ -7,6 +7,7 @@
"github.com/Sirupsen/logrus"
"github.com/containerd/containerd/api/services/execution"
"github.com/crosbymichael/console"
+ "github.com/pkg/errors"
"github.com/urfave/cli"
)
@@ -32,6 +33,9 @@
id = context.String("id")
ctx = gocontext.Background()
)
+ if id == "" {
+ return errors.New("container id must be provided")
+ }
containers, err := getExecutionService(context)
if err != nil {
diff --git a/cmd/ctr/kill.go b/cmd/ctr/kill.go
index a6d7281..f5deb60 100644
--- a/cmd/ctr/kill.go
+++ b/cmd/ctr/kill.go
@@ -10,7 +10,7 @@
var killCommand = cli.Command{
Name: "kill",
- Usage: "signal a container",
+ Usage: "signal a container (default: SIGTERM)",
Flags: []cli.Flag{
cli.StringFlag{
Name: "id",
diff --git a/cmd/ctr/main.go b/cmd/ctr/main.go
index 73216de..9497a15 100644
--- a/cmd/ctr/main.go
+++ b/cmd/ctr/main.go
@@ -59,6 +59,8 @@
killCommand,
pprofCommand,
execCommand,
+ pauseCommand,
+ resumeCommand,
}
app.Commands = append(app.Commands, extraCmds...)
app.Before = func(context *cli.Context) error {
diff --git a/cmd/ctr/pause.go b/cmd/ctr/pause.go
new file mode 100644
index 0000000..8a45d2c
--- /dev/null
+++ b/cmd/ctr/pause.go
@@ -0,0 +1,29 @@
+package main
+
+import (
+ gocontext "context"
+ "errors"
+
+ "github.com/containerd/containerd/api/services/execution"
+ "github.com/urfave/cli"
+)
+
+var pauseCommand = cli.Command{
+ Name: "pause",
+ Usage: "pause an existing container",
+ ArgsUsage: "CONTAINER",
+ Action: func(context *cli.Context) error {
+ containers, err := getExecutionService(context)
+ if err != nil {
+ return err
+ }
+ id := context.Args().First()
+ if id == "" {
+ return errors.New("container id must be provided")
+ }
+ _, err = containers.Pause(gocontext.Background(), &execution.PauseRequest{
+ ID: id,
+ })
+ return err
+ },
+}
diff --git a/cmd/ctr/resume.go b/cmd/ctr/resume.go
new file mode 100644
index 0000000..af11b2d
--- /dev/null
+++ b/cmd/ctr/resume.go
@@ -0,0 +1,29 @@
+package main
+
+import (
+ gocontext "context"
+ "errors"
+
+ "github.com/containerd/containerd/api/services/execution"
+ "github.com/urfave/cli"
+)
+
+var resumeCommand = cli.Command{
+ Name: "resume",
+ Usage: "resume a paused container",
+ ArgsUsage: "CONTAINER",
+ Action: func(context *cli.Context) error {
+ containers, err := getExecutionService(context)
+ if err != nil {
+ return err
+ }
+ id := context.Args().First()
+ if id == "" {
+ return errors.New("container id must be provided")
+ }
+ _, err = containers.Resume(gocontext.Background(), &execution.ResumeRequest{
+ ID: id,
+ })
+ return err
+ },
+}
diff --git a/cmd/ctr/shim.go b/cmd/ctr/shim.go
index 44165b8..a7e8235 100644
--- a/cmd/ctr/shim.go
+++ b/cmd/ctr/shim.go
@@ -1,4 +1,4 @@
-// +build linux
+// +build !windows
package main
diff --git a/cmd/ctr/signals_linux.go b/cmd/ctr/signals_linux.go
new file mode 100644
index 0000000..cb17133
--- /dev/null
+++ b/cmd/ctr/signals_linux.go
@@ -0,0 +1,45 @@
+package main
+
+import (
+ "syscall"
+
+ "golang.org/x/sys/unix"
+)
+
+var signalMap = map[string]syscall.Signal{
+ "ABRT": unix.SIGABRT,
+ "ALRM": unix.SIGALRM,
+ "BUS": unix.SIGBUS,
+ "CHLD": unix.SIGCHLD,
+ "CLD": unix.SIGCLD,
+ "CONT": unix.SIGCONT,
+ "FPE": unix.SIGFPE,
+ "HUP": unix.SIGHUP,
+ "ILL": unix.SIGILL,
+ "INT": unix.SIGINT,
+ "IO": unix.SIGIO,
+ "IOT": unix.SIGIOT,
+ "KILL": unix.SIGKILL,
+ "PIPE": unix.SIGPIPE,
+ "POLL": unix.SIGPOLL,
+ "PROF": unix.SIGPROF,
+ "PWR": unix.SIGPWR,
+ "QUIT": unix.SIGQUIT,
+ "SEGV": unix.SIGSEGV,
+ "STKFLT": unix.SIGSTKFLT,
+ "STOP": unix.SIGSTOP,
+ "SYS": unix.SIGSYS,
+ "TERM": unix.SIGTERM,
+ "TRAP": unix.SIGTRAP,
+ "TSTP": unix.SIGTSTP,
+ "TTIN": unix.SIGTTIN,
+ "TTOU": unix.SIGTTOU,
+ "UNUSED": unix.SIGUNUSED,
+ "URG": unix.SIGURG,
+ "USR1": unix.SIGUSR1,
+ "USR2": unix.SIGUSR2,
+ "VTALRM": unix.SIGVTALRM,
+ "WINCH": unix.SIGWINCH,
+ "XCPU": unix.SIGXCPU,
+ "XFSZ": unix.SIGXFSZ,
+}
diff --git a/cmd/ctr/signals_unix.go b/cmd/ctr/signals_unix.go
new file mode 100644
index 0000000..f7b962f
--- /dev/null
+++ b/cmd/ctr/signals_unix.go
@@ -0,0 +1,42 @@
+// +build darwin freebsd
+
+package main
+
+import (
+ "syscall"
+
+ "golang.org/x/sys/unix"
+)
+
+var signalMap = map[string]syscall.Signal{
+ "ABRT": unix.SIGABRT,
+ "ALRM": unix.SIGALRM,
+ "BUS": unix.SIGBUS,
+ "CHLD": unix.SIGCHLD,
+ "CONT": unix.SIGCONT,
+ "FPE": unix.SIGFPE,
+ "HUP": unix.SIGHUP,
+ "ILL": unix.SIGILL,
+ "INT": unix.SIGINT,
+ "IO": unix.SIGIO,
+ "IOT": unix.SIGIOT,
+ "KILL": unix.SIGKILL,
+ "PIPE": unix.SIGPIPE,
+ "PROF": unix.SIGPROF,
+ "QUIT": unix.SIGQUIT,
+ "SEGV": unix.SIGSEGV,
+ "STOP": unix.SIGSTOP,
+ "SYS": unix.SIGSYS,
+ "TERM": unix.SIGTERM,
+ "TRAP": unix.SIGTRAP,
+ "TSTP": unix.SIGTSTP,
+ "TTIN": unix.SIGTTIN,
+ "TTOU": unix.SIGTTOU,
+ "URG": unix.SIGURG,
+ "USR1": unix.SIGUSR1,
+ "USR2": unix.SIGUSR2,
+ "VTALRM": unix.SIGVTALRM,
+ "WINCH": unix.SIGWINCH,
+ "XCPU": unix.SIGXCPU,
+ "XFSZ": unix.SIGXFSZ,
+}
diff --git a/cmd/ctr/utils.go b/cmd/ctr/utils.go
index e244c11..2254003 100644
--- a/cmd/ctr/utils.go
+++ b/cmd/ctr/utils.go
@@ -100,7 +100,7 @@
}
_, err := containers.Kill(gocontext.Background(), killRequest)
if err != nil {
- logrus.WithError(err).Error("grpc event from kill")
+ logrus.Fatalln(err)
}
}
}()
diff --git a/cmd/ctr/utils_unix.go b/cmd/ctr/utils_unix.go
index 6d07f25..9ef2fcd 100644
--- a/cmd/ctr/utils_unix.go
+++ b/cmd/ctr/utils_unix.go
@@ -17,7 +17,6 @@
"github.com/pkg/errors"
"github.com/tonistiigi/fifo"
"github.com/urfave/cli"
- "golang.org/x/sys/unix"
"google.golang.org/grpc"
"google.golang.org/grpc/grpclog"
)
@@ -100,41 +99,3 @@
grpcConn = conn
return grpcConn, nil
}
-
-var signalMap = map[string]syscall.Signal{
- "ABRT": unix.SIGABRT,
- "ALRM": unix.SIGALRM,
- "BUS": unix.SIGBUS,
- "CHLD": unix.SIGCHLD,
- "CLD": unix.SIGCLD,
- "CONT": unix.SIGCONT,
- "FPE": unix.SIGFPE,
- "HUP": unix.SIGHUP,
- "ILL": unix.SIGILL,
- "INT": unix.SIGINT,
- "IO": unix.SIGIO,
- "IOT": unix.SIGIOT,
- "KILL": unix.SIGKILL,
- "PIPE": unix.SIGPIPE,
- "POLL": unix.SIGPOLL,
- "PROF": unix.SIGPROF,
- "PWR": unix.SIGPWR,
- "QUIT": unix.SIGQUIT,
- "SEGV": unix.SIGSEGV,
- "STKFLT": unix.SIGSTKFLT,
- "STOP": unix.SIGSTOP,
- "SYS": unix.SIGSYS,
- "TERM": unix.SIGTERM,
- "TRAP": unix.SIGTRAP,
- "TSTP": unix.SIGTSTP,
- "TTIN": unix.SIGTTIN,
- "TTOU": unix.SIGTTOU,
- "UNUSED": unix.SIGUNUSED,
- "URG": unix.SIGURG,
- "USR1": unix.SIGUSR1,
- "USR2": unix.SIGUSR2,
- "VTALRM": unix.SIGVTALRM,
- "WINCH": unix.SIGWINCH,
- "XCPU": unix.SIGXCPU,
- "XFSZ": unix.SIGXFSZ,
-}
diff --git a/cmd/dist/common.go b/cmd/dist/common.go
index 7c8d41e..0f3190a 100644
--- a/cmd/dist/common.go
+++ b/cmd/dist/common.go
@@ -1,9 +1,14 @@
package main
import (
+ "bufio"
"context"
+ "crypto/tls"
+ "fmt"
"net"
+ "net/http"
"path/filepath"
+ "strings"
"time"
imagesapi "github.com/containerd/containerd/api/services/images"
@@ -12,10 +17,31 @@
"github.com/containerd/containerd/remotes"
"github.com/containerd/containerd/remotes/docker"
imagesservice "github.com/containerd/containerd/services/images"
+ "github.com/crosbymichael/console"
+ "github.com/pkg/errors"
"github.com/urfave/cli"
"google.golang.org/grpc"
)
+var registryFlags = []cli.Flag{
+ cli.BoolFlag{
+ Name: "skip-verify,k",
+ Usage: "Skip SSL certificate validation",
+ },
+ cli.BoolFlag{
+ Name: "plain-http",
+ Usage: "Allow connections using plain HTTP",
+ },
+ cli.StringFlag{
+ Name: "user,u",
+ Usage: "user[:password] Registry user and password",
+ },
+ cli.StringFlag{
+ Name: "refresh",
+ Usage: "Refresh token for authorization server",
+ },
+}
+
func resolveContentStore(context *cli.Context) (*content.Store, error) {
root := filepath.Join(context.GlobalString("root"), "content")
if !filepath.IsAbs(root) {
@@ -50,6 +76,71 @@
}
// getResolver prepares the resolver from the environment and options.
-func getResolver(ctx context.Context) (remotes.Resolver, error) {
- return docker.NewResolver(), nil
+func getResolver(ctx context.Context, clicontext *cli.Context) (remotes.Resolver, error) {
+ username := clicontext.String("user")
+ var secret string
+ if i := strings.IndexByte(username, ':'); i > 0 {
+ secret = username[i+1:]
+ username = username[0:i]
+ }
+ options := docker.ResolverOptions{
+ PlainHTTP: clicontext.Bool("plain-http"),
+ }
+ if username != "" {
+ if secret == "" {
+ fmt.Printf("Password: ")
+
+ var err error
+ secret, err = passwordPrompt()
+ if err != nil {
+ return nil, err
+ }
+
+ fmt.Print("\n")
+ }
+ } else if rt := clicontext.String("refresh"); rt != "" {
+ secret = rt
+ }
+
+ options.Credentials = func(host string) (string, string, error) {
+ // Only one host
+ return username, secret, nil
+ }
+
+ tr := &http.Transport{
+ Proxy: http.ProxyFromEnvironment,
+ DialContext: (&net.Dialer{
+ Timeout: 30 * time.Second,
+ KeepAlive: 30 * time.Second,
+ DualStack: true,
+ }).DialContext,
+ MaxIdleConns: 10,
+ IdleConnTimeout: 30 * time.Second,
+ TLSHandshakeTimeout: 10 * time.Second,
+ TLSClientConfig: &tls.Config{
+ InsecureSkipVerify: clicontext.Bool("insecure"),
+ },
+ ExpectContinueTimeout: 5 * time.Second,
+ }
+
+ options.Client = &http.Client{
+ Transport: tr,
+ }
+
+ return docker.NewResolver(options), nil
+}
+
+func passwordPrompt() (string, error) {
+ c := console.Current()
+ defer c.Reset()
+
+ if err := c.DisableEcho(); err != nil {
+ return "", errors.Wrap(err, "failed to disable echo")
+ }
+
+ line, _, err := bufio.NewReader(c).ReadLine()
+ if err != nil {
+ return "", errors.Wrap(err, "failed to read line")
+ }
+ return string(line), nil
}
diff --git a/cmd/dist/edit.go b/cmd/dist/edit.go
new file mode 100644
index 0000000..c7f655e
--- /dev/null
+++ b/cmd/dist/edit.go
@@ -0,0 +1,113 @@
+package main
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "os/exec"
+
+ contentapi "github.com/containerd/containerd/api/services/content"
+ contentservice "github.com/containerd/containerd/services/content"
+ digest "github.com/opencontainers/go-digest"
+ "github.com/urfave/cli"
+)
+
+var editCommand = cli.Command{
+ Name: "edit",
+ Usage: "edit a blob and return a new digest.",
+ ArgsUsage: "[flags] <digest>",
+ Description: `Edit a blob and return a new digest.`,
+ Flags: []cli.Flag{
+ cli.StringFlag{
+ Name: "validate",
+ Usage: "validate the result against a format (json, mediatype, etc.)",
+ },
+ },
+ Action: func(context *cli.Context) error {
+ var (
+ ctx = background
+ validate = context.String("validate")
+ object = context.Args().First()
+ )
+
+ if validate != "" {
+ return errors.New("validating the edit result not supported")
+ }
+
+ // TODO(stevvooe): Support looking up objects by a reference through
+ // the image metadata storage.
+
+ dgst, err := digest.Parse(object)
+ if err != nil {
+ return err
+ }
+
+ conn, err := connectGRPC(context)
+ if err != nil {
+ return err
+ }
+
+ provider := contentservice.NewProviderFromClient(contentapi.NewContentClient(conn))
+ ingester := contentservice.NewIngesterFromClient(contentapi.NewContentClient(conn))
+
+ rc, err := provider.Reader(ctx, dgst)
+ if err != nil {
+ return err
+ }
+ defer rc.Close()
+
+ nrc, err := edit(rc)
+ if err != nil {
+ return err
+ }
+
+ wr, err := ingester.Writer(ctx, "edit-"+object, 0, "") // TODO(stevvooe): Choose a better key?
+ if err != nil {
+ return err
+ }
+
+ if _, err := io.Copy(wr, nrc); err != nil {
+ return err
+ }
+
+ if err := wr.Commit(0, wr.Digest()); err != nil {
+ return err
+ }
+
+ fmt.Println(wr.Digest())
+ return nil
+ },
+}
+
+func edit(rd io.Reader) (io.ReadCloser, error) {
+ tmp, err := ioutil.TempFile("", "edit-")
+ if err != nil {
+ return nil, err
+ }
+
+ if _, err := io.Copy(tmp, rd); err != nil {
+ tmp.Close()
+ return nil, err
+ }
+
+ cmd := exec.Command("sh", "-c", "$EDITOR "+tmp.Name())
+
+ cmd.Stdin = os.Stdin
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ cmd.Env = os.Environ()
+
+ if err := cmd.Run(); err != nil {
+ tmp.Close()
+ return nil, err
+ }
+
+ if _, err := tmp.Seek(0, io.SeekStart); err != nil {
+ tmp.Close()
+ return nil, err
+ }
+
+ return tmp, nil
+}
diff --git a/cmd/dist/fetch.go b/cmd/dist/fetch.go
index f319b4b..4446f72 100644
--- a/cmd/dist/fetch.go
+++ b/cmd/dist/fetch.go
@@ -39,7 +39,7 @@
content and snapshots ready for a direct use via the 'ctr run'.
Most of this is experimental and there are few leaps to make this work.`,
- Flags: []cli.Flag{},
+ Flags: registryFlags,
Action: func(clicontext *cli.Context) error {
var (
ctx = background
@@ -51,7 +51,7 @@
return err
}
- resolver, err := getResolver(ctx)
+ resolver, err := getResolver(ctx, clicontext)
if err != nil {
return err
}
diff --git a/cmd/dist/fetchobject.go b/cmd/dist/fetchobject.go
index c38d4b6..69d393e 100644
--- a/cmd/dist/fetchobject.go
+++ b/cmd/dist/fetchobject.go
@@ -18,13 +18,13 @@
Usage: "retrieve objects from a remote",
ArgsUsage: "[flags] <remote> <object> [<hint>, ...]",
Description: `Fetch objects by identifier from a remote.`,
- Flags: []cli.Flag{
+ Flags: append([]cli.Flag{
cli.DurationFlag{
Name: "timeout",
Usage: "total timeout for fetch",
EnvVar: "CONTAINERD_FETCH_TIMEOUT",
},
- },
+ }, registryFlags...),
Action: func(context *cli.Context) error {
var (
ctx = background
@@ -38,7 +38,7 @@
defer cancel()
}
- resolver, err := getResolver(ctx)
+ resolver, err := getResolver(ctx, context)
if err != nil {
return err
}
diff --git a/cmd/dist/main.go b/cmd/dist/main.go
index f9acfc8..0d1f946 100644
--- a/cmd/dist/main.go
+++ b/cmd/dist/main.go
@@ -106,6 +106,7 @@
ingestCommand,
activeCommand,
getCommand,
+ editCommand,
deleteCommand,
},
}
diff --git a/cmd/dist/pull.go b/cmd/dist/pull.go
index 8484034..920bc60 100644
--- a/cmd/dist/pull.go
+++ b/cmd/dist/pull.go
@@ -35,7 +35,7 @@
2. Prepare the snapshot filesystem with the pulled resources.
3. Register metadata for the image.
`,
- Flags: []cli.Flag{},
+ Flags: registryFlags,
Action: func(clicontext *cli.Context) error {
var (
ctx = background
@@ -52,7 +52,7 @@
return err
}
- resolver, err := getResolver(ctx)
+ resolver, err := getResolver(ctx, clicontext)
if err != nil {
return err
}
@@ -75,6 +75,7 @@
ongoing.add(ref)
name, desc, fetcher, err := resolver.Resolve(ctx, ref)
if err != nil {
+ log.G(ctx).WithError(err).Error("failed to resolve")
return err
}
log.G(ctx).WithField("image", name).Debug("fetching")
diff --git a/container.go b/container.go
index e07e0a7..36e0841 100644
--- a/container.go
+++ b/container.go
@@ -14,6 +14,10 @@
Start(context.Context) error
// State returns the container's state
State(context.Context) (State, error)
+ // Pause pauses the container process
+ Pause(context.Context) error
+ // Resume unpauses the container process
+ Resume(context.Context) error
// Kill signals a container
Kill(context.Context, uint32, bool) error
// Exec adds a process into the container
@@ -26,9 +30,6 @@
type LinuxContainer interface {
Container
-
- Pause(context.Context) error
- Resume(context.Context) error
}
type ExecOpts struct {
diff --git a/content/store_linux.go b/content/store_linux.go
new file mode 100644
index 0000000..ed25f0c
--- /dev/null
+++ b/content/store_linux.go
@@ -0,0 +1,15 @@
+package content
+
+import (
+ "os"
+ "syscall"
+ "time"
+)
+
+func getStartTime(fi os.FileInfo) time.Time {
+ if st, ok := fi.Sys().(*syscall.Stat_t); ok {
+ return time.Unix(int64(st.Ctim.Sec), int64(st.Ctim.Nsec))
+ }
+
+ return fi.ModTime()
+}
diff --git a/content/store_unix.go b/content/store_unix.go
index 9d334c9..a7c6b1b 100644
--- a/content/store_unix.go
+++ b/content/store_unix.go
@@ -1,4 +1,4 @@
-// +build linux
+// +build darwin freebsd
package content
@@ -10,7 +10,7 @@
func getStartTime(fi os.FileInfo) time.Time {
if st, ok := fi.Sys().(*syscall.Stat_t); ok {
- return time.Unix(int64(st.Ctim.Sec), int64(st.Ctim.Nsec))
+ return time.Unix(int64(st.Ctimespec.Sec), int64(st.Ctimespec.Nsec))
}
return fi.ModTime()
diff --git a/fs/copy_linux.go b/fs/copy_linux.go
index a6c193c..efa9248 100644
--- a/fs/copy_linux.go
+++ b/fs/copy_linux.go
@@ -5,8 +5,9 @@
"os"
"syscall"
+ "github.com/containerd/continuity/sysx"
"github.com/pkg/errors"
- "github.com/stevvooe/continuity/sysx"
+ "golang.org/x/sys/unix"
)
func copyFileInfo(fi os.FileInfo, name string) error {
@@ -21,7 +22,8 @@
}
}
- if err := syscall.UtimesNano(name, []syscall.Timespec{st.Atim, st.Mtim}); err != nil {
+ timespec := []unix.Timespec{unix.Timespec(st.Atim), unix.Timespec(st.Mtim)}
+ if err := unix.UtimesNanoAt(unix.AT_FDCWD, name, timespec, unix.AT_SYMLINK_NOFOLLOW); err != nil {
return errors.Wrapf(err, "failed to utime %s", name)
}
diff --git a/fs/copy_test.go b/fs/copy_test.go
index 475e76f..2a51bf6 100644
--- a/fs/copy_test.go
+++ b/fs/copy_test.go
@@ -32,6 +32,20 @@
}
}
+// This test used to fail because link-no-nothing.txt would be copied first,
+// then file operations in dst during the CopyDir would follow the symlink and
+// fail.
+func TestCopyDirectoryWithLocalSymlink(t *testing.T) {
+ apply := fstest.Apply(
+ fstest.CreateFile("nothing.txt", []byte{0x00, 0x00}, 0755),
+ fstest.Symlink("nothing.txt", "link-no-nothing.txt"),
+ )
+
+ if err := testCopy(apply); err != nil {
+ t.Fatalf("Copy test failed: %+v", err)
+ }
+}
+
func testCopy(apply fstest.Applier) error {
t1, err := ioutil.TempDir("", "test-copy-src-")
if err != nil {
diff --git a/fs/copy_unix.go b/fs/copy_unix.go
new file mode 100644
index 0000000..4a6cce4
--- /dev/null
+++ b/fs/copy_unix.go
@@ -0,0 +1,65 @@
+// +build darwin freebsd
+
+package fs
+
+import (
+ "io"
+ "os"
+ "syscall"
+
+ "github.com/containerd/continuity/sysx"
+ "github.com/pkg/errors"
+)
+
+func copyFileInfo(fi os.FileInfo, name string) error {
+ st := fi.Sys().(*syscall.Stat_t)
+ if err := os.Lchown(name, int(st.Uid), int(st.Gid)); err != nil {
+ return errors.Wrapf(err, "failed to chown %s", name)
+ }
+
+ if (fi.Mode() & os.ModeSymlink) != os.ModeSymlink {
+ if err := os.Chmod(name, fi.Mode()); err != nil {
+ return errors.Wrapf(err, "failed to chmod %s", name)
+ }
+ }
+
+ if err := syscall.UtimesNano(name, []syscall.Timespec{st.Atimespec, st.Mtimespec}); err != nil {
+ return errors.Wrapf(err, "failed to utime %s", name)
+ }
+
+ return nil
+}
+
+func copyFileContent(dst, src *os.File) error {
+ buf := bufferPool.Get().([]byte)
+ _, err := io.CopyBuffer(dst, src, buf)
+ bufferPool.Put(buf)
+
+ return err
+}
+
+func copyXAttrs(dst, src string) error {
+ xattrKeys, err := sysx.LListxattr(src)
+ if err != nil {
+ return errors.Wrapf(err, "failed to list xattrs on %s", src)
+ }
+ for _, xattr := range xattrKeys {
+ data, err := sysx.LGetxattr(src, xattr)
+ if err != nil {
+ return errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src)
+ }
+ if err := sysx.LSetxattr(dst, xattr, data, 0); err != nil {
+ return errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst)
+ }
+ }
+
+ return nil
+}
+
+func copyDevice(dst string, fi os.FileInfo) error {
+ st, ok := fi.Sys().(*syscall.Stat_t)
+ if !ok {
+ return errors.New("unsupported stat type")
+ }
+ return syscall.Mknod(dst, uint32(fi.Mode()), int(st.Rdev))
+}
diff --git a/fs/diff_linux.go b/fs/diff_unix.go
similarity index 97%
rename from fs/diff_linux.go
rename to fs/diff_unix.go
index 24c290a..14add16 100644
--- a/fs/diff_linux.go
+++ b/fs/diff_unix.go
@@ -1,3 +1,5 @@
+// +build !windows
+
package fs
import (
@@ -7,8 +9,8 @@
"strings"
"syscall"
+ "github.com/containerd/continuity/sysx"
"github.com/pkg/errors"
- "github.com/stevvooe/continuity/sysx"
)
// whiteouts are files with a special meaning for the layered filesystem.
diff --git a/fs/du.go b/fs/du.go
new file mode 100644
index 0000000..8dfdaeb
--- /dev/null
+++ b/fs/du.go
@@ -0,0 +1,12 @@
+package fs
+
+type Usage struct {
+ Inodes int64
+ Size int64
+}
+
+// DiskUsage counts the number of inodes and disk usage for the resources under
+// path.
+func DiskUsage(roots ...string) (Usage, error) {
+ return diskUsage(roots...)
+}
diff --git a/fs/du_unix.go b/fs/du_unix.go
new file mode 100644
index 0000000..3178df5
--- /dev/null
+++ b/fs/du_unix.go
@@ -0,0 +1,42 @@
+// +build !windows
+
+package fs
+
+import (
+ "os"
+ "path/filepath"
+ "syscall"
+)
+
+func diskUsage(roots ...string) (Usage, error) {
+ type inode struct {
+ // TODO(stevvooe): Can probably reduce memory usage by not tracking
+ // device, but we can leave this right for now.
+ dev, ino uint64
+ }
+
+ var (
+ size int64
+ inodes = map[inode]struct{}{} // expensive!
+ )
+
+ for _, root := range roots {
+ if err := filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
+ if err != nil {
+ return err
+ }
+
+ stat := fi.Sys().(*syscall.Stat_t)
+ inodes[inode{dev: uint64(stat.Dev), ino: stat.Ino}] = struct{}{}
+ size += fi.Size()
+ return nil
+ }); err != nil {
+ return Usage{}, err
+ }
+ }
+
+ return Usage{
+ Inodes: int64(len(inodes)),
+ Size: size,
+ }, nil
+}
diff --git a/fs/du_windows.go b/fs/du_windows.go
new file mode 100644
index 0000000..4a0363c
--- /dev/null
+++ b/fs/du_windows.go
@@ -0,0 +1,33 @@
+// +build windows
+
+package fs
+
+import (
+ "os"
+ "path/filepath"
+)
+
+func diskUsage(roots ...string) (Usage, error) {
+ var (
+ size int64
+ )
+
+ // TODO(stevvooe): Support inodes (or equivalent) for windows.
+
+ for _, root := range roots {
+ if err := filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
+ if err != nil {
+ return err
+ }
+
+ size += fi.Size()
+ return nil
+ }); err != nil {
+ return Usage{}, err
+ }
+ }
+
+ return Usage{
+ Size: size,
+ }, nil
+}
diff --git a/fs/fstest/compare.go b/fs/fstest/compare.go
index a569d69..4f55f1c 100644
--- a/fs/fstest/compare.go
+++ b/fs/fstest/compare.go
@@ -4,8 +4,8 @@
"io/ioutil"
"os"
+ "github.com/containerd/continuity"
"github.com/pkg/errors"
- "github.com/stevvooe/continuity"
)
// CheckDirectoryEqual compares two directory paths to make sure that
diff --git a/fs/fstest/continuity_util.go b/fs/fstest/continuity_util.go
index a300388..1100ca4 100644
--- a/fs/fstest/continuity_util.go
+++ b/fs/fstest/continuity_util.go
@@ -4,7 +4,7 @@
"bytes"
"fmt"
- "github.com/stevvooe/continuity"
+ "github.com/containerd/continuity"
)
type resourceUpdate struct {
diff --git a/fs/fstest/file.go b/fs/fstest/file.go
index 41c1047..2b93454 100644
--- a/fs/fstest/file.go
+++ b/fs/fstest/file.go
@@ -6,7 +6,7 @@
"path/filepath"
"time"
- "github.com/stevvooe/continuity/sysx"
+ "github.com/containerd/continuity/sysx"
)
// Applier applies single file changes
diff --git a/fs/fstest/testsuite.go b/fs/fstest/testsuite.go
new file mode 100644
index 0000000..372d76f
--- /dev/null
+++ b/fs/fstest/testsuite.go
@@ -0,0 +1,161 @@
+package fstest
+
+import (
+ "context"
+ "io/ioutil"
+ "os"
+ "testing"
+)
+
+type TestApplier interface {
+ TestContext(context.Context) (context.Context, func(), error)
+ Apply(context.Context, Applier) (string, func(), error)
+}
+
+func FSSuite(t *testing.T, a TestApplier) {
+ t.Run("Basic", makeTest(t, a, basicTest))
+ t.Run("Deletion", makeTest(t, a, deletionTest))
+ // TODO: Add hard section, run if command line arg or function arg set to true
+ // Hard tests
+ t.Run("HardlinkUnmodified", makeTest(t, a, hardlinkUnmodified))
+ t.Run("HardlinkBeforeUnmodified", makeTest(t, a, hardlinkBeforeUnmodified))
+ t.Run("HardlinkBeforeModified", makeTest(t, a, hardlinkBeforeModified))
+}
+
+func makeTest(t *testing.T, ta TestApplier, as []Applier) func(t *testing.T) {
+ return func(t *testing.T) {
+ ctx, cleanup, err := ta.TestContext(context.Background())
+ if err != nil {
+ t.Fatalf("Unable to get test context: %+v", err)
+ }
+ defer cleanup()
+
+ applyDir, err := ioutil.TempDir("", "test-expected-")
+ if err != nil {
+ t.Fatalf("Unable to make temp directory: %+v", err)
+ }
+ defer os.RemoveAll(applyDir)
+
+ for i, a := range as {
+ testDir, c, err := ta.Apply(ctx, a)
+ if err != nil {
+ t.Fatalf("Apply failed at %d: %+v", i, err)
+ }
+ if err := a.Apply(applyDir); err != nil {
+ if c != nil {
+ c()
+ }
+ t.Fatalf("Error applying change to apply directory: %+v", err)
+ }
+
+ err = CheckDirectoryEqual(applyDir, testDir)
+ if c != nil {
+ c()
+ }
+ if err != nil {
+ t.Fatalf("Directories not equal at %d (expected <> tested): %+v", i, err)
+ }
+ }
+ }
+}
+
+var (
+ // baseApplier creates a basic filesystem layout
+ // with multiple types of files for basic tests.
+ baseApplier = Apply(
+ CreateDir("/etc/", 0755),
+ CreateFile("/etc/hosts", []byte("127.0.0.1 localhost"), 0644),
+ Link("/etc/hosts", "/etc/hosts.allow"),
+ CreateDir("/usr/local/lib", 0755),
+ CreateFile("/usr/local/lib/libnothing.so", []byte{0x00, 0x00}, 0755),
+ Symlink("libnothing.so", "/usr/local/lib/libnothing.so.2"),
+ CreateDir("/home", 0755),
+ CreateDir("/home/derek", 0700),
+ )
+
+ // basicTest covers basic operations
+ basicTest = []Applier{
+ baseApplier,
+ Apply(
+ CreateFile("/etc/hosts", []byte("127.0.0.1 localhost.localdomain"), 0644),
+ CreateFile("/etc/fstab", []byte("/dev/sda1\t/\text4\tdefaults 1 1\n"), 0600),
+ CreateFile("/etc/badfile", []byte(""), 0666),
+ CreateFile("/home/derek/.zshrc", []byte("#ZSH is just better\n"), 0640),
+ ),
+ Apply(
+ Remove("/etc/badfile"),
+ Rename("/home/derek", "/home/notderek"),
+ ),
+ Apply(
+ RemoveAll("/usr"),
+ Remove("/etc/hosts.allow"),
+ ),
+ Apply(
+ RemoveAll("/home"),
+ CreateDir("/home/derek", 0700),
+ CreateFile("/home/derek/.bashrc", []byte("#not going away\n"), 0640),
+ Link("/etc/hosts", "/etc/hosts.allow"),
+ ),
+ }
+
+ // deletionTest covers various deletion scenarios to ensure
+ // deletions are properly picked up and applied
+ deletionTest = []Applier{
+ Apply(
+ CreateDir("/test/somedir", 0755),
+ CreateDir("/lib", 0700),
+ CreateFile("/lib/hidden", []byte{}, 0644),
+ ),
+ Apply(
+ CreateFile("/test/a", []byte{}, 0644),
+ CreateFile("/test/b", []byte{}, 0644),
+ CreateDir("/test/otherdir", 0755),
+ CreateFile("/test/otherdir/.empty", []byte{}, 0644),
+ RemoveAll("/lib"),
+ CreateDir("/lib", 0700),
+ CreateFile("/lib/not-hidden", []byte{}, 0644),
+ ),
+ Apply(
+ Remove("/test/a"),
+ Remove("/test/b"),
+ RemoveAll("/test/otherdir"),
+ CreateFile("/lib/newfile", []byte{}, 0644),
+ ),
+ }
+
+ hardlinkUnmodified = []Applier{
+ baseApplier,
+ Apply(
+ CreateFile("/etc/hosts", []byte("127.0.0.1 localhost.localdomain"), 0644),
+ ),
+ Apply(
+ Link("/etc/hosts", "/etc/hosts.deny"),
+ ),
+ }
+
+ // Hardlink name before with modification
+ // Tests link is created for unmodified files when new hardlinked file is seen first
+ hardlinkBeforeUnmodified = []Applier{
+ baseApplier,
+ Apply(
+ CreateFile("/etc/hosts", []byte("127.0.0.1 localhost.localdomain"), 0644),
+ ),
+ Apply(
+ Link("/etc/hosts", "/etc/before-hosts"),
+ ),
+ }
+
+ // Hardlink name after without modification
+ // tests link is created for modified file with new hardlink
+ hardlinkBeforeModified = []Applier{
+ baseApplier,
+ Apply(
+ CreateFile("/etc/hosts", []byte("127.0.0.1 localhost.localdomain"), 0644),
+ ),
+ Apply(
+ Remove("/etc/hosts"),
+ CreateFile("/etc/hosts", []byte("127.0.0.1 localhost"), 0644),
+ Link("/etc/hosts", "/etc/before-hosts"),
+ ),
+ }
+)
diff --git a/linux/shim.go b/linux/shim.go
index 0c28098..f054b58 100644
--- a/linux/shim.go
+++ b/linux/shim.go
@@ -24,7 +24,7 @@
func newShim(path string, remote bool) (shim.ShimClient, error) {
if !remote {
- return localShim.Client(path), nil
+ return localShim.Client(path)
}
socket := filepath.Join(path, "shim.sock")
l, err := sys.CreateUnixSocket(socket)
@@ -59,7 +59,7 @@
func loadShim(path string, remote bool) (shim.ShimClient, error) {
if !remote {
- return localShim.Client(path), nil
+ return localShim.Client(path)
}
socket := filepath.Join(path, "shim.sock")
return connectShim(socket)
diff --git a/linux/shim/client.go b/linux/shim/client.go
index 1c782ed..b44456e 100644
--- a/linux/shim/client.go
+++ b/linux/shim/client.go
@@ -5,6 +5,7 @@
shimapi "github.com/containerd/containerd/api/services/shim"
"github.com/containerd/containerd/api/types/container"
+ runc "github.com/crosbymichael/go-runc"
google_protobuf "github.com/golang/protobuf/ptypes/empty"
"golang.org/x/net/context"
"golang.org/x/sys/unix"
@@ -12,10 +13,26 @@
"google.golang.org/grpc/metadata"
)
-func Client(path string) shimapi.ShimClient {
- return &client{
+func Client(path string) (shimapi.ShimClient, error) {
+ pid, err := runc.ReadPidFile(filepath.Join(path, "init.pid"))
+ if err != nil {
+ return nil, err
+ }
+
+ cl := &client{
s: New(path),
}
+
+ // used when quering container status and info
+ cl.s.initProcess = &initProcess{
+ id: filepath.Base(path),
+ pid: pid,
+ runc: &runc.Runc{
+ Log: filepath.Join(path, "log.json"),
+ LogFormat: runc.JSON,
+ },
+ }
+ return cl, nil
}
type client struct {
diff --git a/linux/shim/io.go b/linux/shim/io.go
index 60e41b8..2194a99 100644
--- a/linux/shim/io.go
+++ b/linux/shim/io.go
@@ -15,11 +15,13 @@
)
func copyConsole(ctx context.Context, console console.Console, stdin, stdout, stderr string, wg *sync.WaitGroup) error {
- in, err := fifo.OpenFifo(ctx, stdin, syscall.O_RDONLY, 0)
- if err != nil {
- return err
+ if stdin != "" {
+ in, err := fifo.OpenFifo(ctx, stdin, syscall.O_RDONLY, 0)
+ if err != nil {
+ return err
+ }
+ go io.Copy(console, in)
}
- go io.Copy(console, in)
outw, err := fifo.OpenFifo(ctx, stdout, syscall.O_WRONLY, 0)
if err != nil {
return err
diff --git a/mount_linux.go b/mount_linux.go
new file mode 100644
index 0000000..85c652a
--- /dev/null
+++ b/mount_linux.go
@@ -0,0 +1,70 @@
+package containerd
+
+import (
+ "strings"
+
+ "golang.org/x/sys/unix"
+)
+
+func (m *Mount) Mount(target string) error {
+ flags, data := parseMountOptions(m.Options)
+ return unix.Mount(m.Source, target, m.Type, uintptr(flags), data)
+}
+
+func Unmount(mount string, flags int) error {
+ return unix.Unmount(mount, flags)
+}
+
+// parseMountOptions takes fstab style mount options and parses them for
+// use with a standard mount() syscall
+func parseMountOptions(options []string) (int, string) {
+ var (
+ flag int
+ data []string
+ )
+ flags := map[string]struct {
+ clear bool
+ flag int
+ }{
+ "async": {true, unix.MS_SYNCHRONOUS},
+ "atime": {true, unix.MS_NOATIME},
+ "bind": {false, unix.MS_BIND},
+ "defaults": {false, 0},
+ "dev": {true, unix.MS_NODEV},
+ "diratime": {true, unix.MS_NODIRATIME},
+ "dirsync": {false, unix.MS_DIRSYNC},
+ "exec": {true, unix.MS_NOEXEC},
+ "mand": {false, unix.MS_MANDLOCK},
+ "noatime": {false, unix.MS_NOATIME},
+ "nodev": {false, unix.MS_NODEV},
+ "nodiratime": {false, unix.MS_NODIRATIME},
+ "noexec": {false, unix.MS_NOEXEC},
+ "nomand": {true, unix.MS_MANDLOCK},
+ "norelatime": {true, unix.MS_RELATIME},
+ "nostrictatime": {true, unix.MS_STRICTATIME},
+ "nosuid": {false, unix.MS_NOSUID},
+ "rbind": {false, unix.MS_BIND | unix.MS_REC},
+ "relatime": {false, unix.MS_RELATIME},
+ "remount": {false, unix.MS_REMOUNT},
+ "ro": {false, unix.MS_RDONLY},
+ "rw": {true, unix.MS_RDONLY},
+ "strictatime": {false, unix.MS_STRICTATIME},
+ "suid": {true, unix.MS_NOSUID},
+ "sync": {false, unix.MS_SYNCHRONOUS},
+ }
+ for _, o := range options {
+ // If the option does not exist in the flags table or the flag
+ // is not supported on the platform,
+ // then it is a data value for a specific fs type
+ if f, exists := flags[o]; exists && f.flag != 0 {
+ if f.clear {
+ flag &^= f.flag
+ } else {
+ flag |= f.flag
+ }
+ } else {
+ data = append(data, o)
+ }
+ }
+ return flag, strings.Join(data, ",")
+}
diff --git a/mount_unix.go b/mount_unix.go
index 8ed4023..8746424 100644
--- a/mount_unix.go
+++ b/mount_unix.go
@@ -1,72 +1,17 @@
-// +build linux
+// +build darwin freebsd
package containerd
-import (
- "strings"
+import "github.com/pkg/errors"
- "golang.org/x/sys/unix"
+var (
+ ErrNotImplementOnUnix = errors.New("not implemented under unix")
)
func (m *Mount) Mount(target string) error {
- flags, data := parseMountOptions(m.Options)
- return unix.Mount(m.Source, target, m.Type, uintptr(flags), data)
+ return ErrNotImplementOnUnix
}
func Unmount(mount string, flags int) error {
- return unix.Unmount(mount, flags)
-}
-
-// parseMountOptions takes fstab style mount options and parses them for
-// use with a standard mount() syscall
-func parseMountOptions(options []string) (int, string) {
- var (
- flag int
- data []string
- )
- flags := map[string]struct {
- clear bool
- flag int
- }{
- "async": {true, unix.MS_SYNCHRONOUS},
- "atime": {true, unix.MS_NOATIME},
- "bind": {false, unix.MS_BIND},
- "defaults": {false, 0},
- "dev": {true, unix.MS_NODEV},
- "diratime": {true, unix.MS_NODIRATIME},
- "dirsync": {false, unix.MS_DIRSYNC},
- "exec": {true, unix.MS_NOEXEC},
- "mand": {false, unix.MS_MANDLOCK},
- "noatime": {false, unix.MS_NOATIME},
- "nodev": {false, unix.MS_NODEV},
- "nodiratime": {false, unix.MS_NODIRATIME},
- "noexec": {false, unix.MS_NOEXEC},
- "nomand": {true, unix.MS_MANDLOCK},
- "norelatime": {true, unix.MS_RELATIME},
- "nostrictatime": {true, unix.MS_STRICTATIME},
- "nosuid": {false, unix.MS_NOSUID},
- "rbind": {false, unix.MS_BIND | unix.MS_REC},
- "relatime": {false, unix.MS_RELATIME},
- "remount": {false, unix.MS_REMOUNT},
- "ro": {false, unix.MS_RDONLY},
- "rw": {true, unix.MS_RDONLY},
- "strictatime": {false, unix.MS_STRICTATIME},
- "suid": {true, unix.MS_NOSUID},
- "sync": {false, unix.MS_SYNCHRONOUS},
- }
- for _, o := range options {
- // If the option does not exist in the flags table or the flag
- // is not supported on the platform,
- // then it is a data value for a specific fs type
- if f, exists := flags[o]; exists && f.flag != 0 {
- if f.clear {
- flag &^= f.flag
- } else {
- flag |= f.flag
- }
- } else {
- data = append(data, o)
- }
- }
- return flag, strings.Join(data, ",")
+ return ErrNotImplementOnUnix
}
diff --git a/remotes/docker/auth.go b/remotes/docker/auth.go
new file mode 100644
index 0000000..aa33752
--- /dev/null
+++ b/remotes/docker/auth.go
@@ -0,0 +1,182 @@
+package docker
+
+import (
+ "net/http"
+ "sort"
+ "strings"
+)
+
+type authenticationScheme byte
+
+const (
+ basicAuth authenticationScheme = 1 << iota // Defined in RFC 7617
+ digestAuth // Defined in RFC 7616
+ bearerAuth // Defined in RFC 6750
+)
+
+// challenge carries information from a WWW-Authenticate response header.
+// See RFC 2617.
+type challenge struct {
+ // scheme is the auth-scheme according to RFC 2617
+ scheme authenticationScheme
+
+ // parameters are the auth-params according to RFC 2617
+ parameters map[string]string
+}
+
+type byScheme []challenge
+
+func (bs byScheme) Len() int { return len(bs) }
+func (bs byScheme) Swap(i, j int) { bs[i], bs[j] = bs[j], bs[i] }
+
+// Sort in priority order: token > digest > basic
+func (bs byScheme) Less(i, j int) bool { return bs[i].scheme > bs[j].scheme }
+
+// Octet types from RFC 2616.
+type octetType byte
+
+var octetTypes [256]octetType
+
+const (
+ isToken octetType = 1 << iota
+ isSpace
+)
+
+func init() {
+ // OCTET = <any 8-bit sequence of data>
+ // CHAR = <any US-ASCII character (octets 0 - 127)>
+ // CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
+ // CR = <US-ASCII CR, carriage return (13)>
+ // LF = <US-ASCII LF, linefeed (10)>
+ // SP = <US-ASCII SP, space (32)>
+ // HT = <US-ASCII HT, horizontal-tab (9)>
+ // <"> = <US-ASCII double-quote mark (34)>
+ // CRLF = CR LF
+ // LWS = [CRLF] 1*( SP | HT )
+ // TEXT = <any OCTET except CTLs, but including LWS>
+ // separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <">
+ // | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT
+ // token = 1*<any CHAR except CTLs or separators>
+ // qdtext = <any TEXT except <">>
+
+ for c := 0; c < 256; c++ {
+ var t octetType
+ isCtl := c <= 31 || c == 127
+ isChar := 0 <= c && c <= 127
+ isSeparator := strings.IndexRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) >= 0
+ if strings.IndexRune(" \t\r\n", rune(c)) >= 0 {
+ t |= isSpace
+ }
+ if isChar && !isCtl && !isSeparator {
+ t |= isToken
+ }
+ octetTypes[c] = t
+ }
+}
+
+func parseAuthHeader(header http.Header) []challenge {
+ challenges := []challenge{}
+ for _, h := range header[http.CanonicalHeaderKey("WWW-Authenticate")] {
+ v, p := parseValueAndParams(h)
+ var s authenticationScheme
+ switch v {
+ case "basic":
+ s = basicAuth
+ case "digest":
+ s = digestAuth
+ case "bearer":
+ s = bearerAuth
+ default:
+ continue
+ }
+ challenges = append(challenges, challenge{scheme: s, parameters: p})
+ }
+ sort.Stable(byScheme(challenges))
+ return challenges
+}
+
+func parseValueAndParams(header string) (value string, params map[string]string) {
+ params = make(map[string]string)
+ value, s := expectToken(header)
+ if value == "" {
+ return
+ }
+ value = strings.ToLower(value)
+ for {
+ var pkey string
+ pkey, s = expectToken(skipSpace(s))
+ if pkey == "" {
+ return
+ }
+ if !strings.HasPrefix(s, "=") {
+ return
+ }
+ var pvalue string
+ pvalue, s = expectTokenOrQuoted(s[1:])
+ if pvalue == "" {
+ return
+ }
+ pkey = strings.ToLower(pkey)
+ params[pkey] = pvalue
+ s = skipSpace(s)
+ if !strings.HasPrefix(s, ",") {
+ return
+ }
+ s = s[1:]
+ }
+}
+
+func skipSpace(s string) (rest string) {
+ i := 0
+ for ; i < len(s); i++ {
+ if octetTypes[s[i]]&isSpace == 0 {
+ break
+ }
+ }
+ return s[i:]
+}
+
+func expectToken(s string) (token, rest string) {
+ i := 0
+ for ; i < len(s); i++ {
+ if octetTypes[s[i]]&isToken == 0 {
+ break
+ }
+ }
+ return s[:i], s[i:]
+}
+
+func expectTokenOrQuoted(s string) (value string, rest string) {
+ if !strings.HasPrefix(s, "\"") {
+ return expectToken(s)
+ }
+ s = s[1:]
+ for i := 0; i < len(s); i++ {
+ switch s[i] {
+ case '"':
+ return s[:i], s[i+1:]
+ case '\\':
+ p := make([]byte, len(s)-1)
+ j := copy(p, s[:i])
+ escape := true
+ for i = i + 1; i < len(s); i++ {
+ b := s[i]
+ switch {
+ case escape:
+ escape = false
+ p[j] = b
+ j++
+ case b == '\\':
+ escape = true
+ case b == '"':
+ return string(p[:j]), s[i+1:]
+ default:
+ p[j] = b
+ j++
+ }
+ }
+ return "", ""
+ }
+ }
+ return "", ""
+}
diff --git a/remotes/docker/resolver.go b/remotes/docker/resolver.go
index 985d885..804ac55 100644
--- a/remotes/docker/resolver.go
+++ b/remotes/docker/resolver.go
@@ -7,10 +7,12 @@
"io"
"io/ioutil"
"net/http"
+ "net/textproto"
"net/url"
"path"
"strconv"
"strings"
+ "time"
"github.com/Sirupsen/logrus"
"github.com/containerd/containerd/images"
@@ -23,15 +25,43 @@
"golang.org/x/net/context/ctxhttp"
)
-// NOTE(stevvooe): Most of the code below this point is prototype code to
-// demonstrate a very simplified docker.io fetcher. We have a lot of hard coded
-// values but we leave many of the details down to the fetcher, creating a lot
-// of room for ways to fetch content.
+var (
+ // ErrNoToken is returned if a request is successful but the body does not
+ // contain an authorization token.
+ ErrNoToken = errors.New("authorization server did not include a token in the response")
-type dockerResolver struct{}
+ // ErrInvalidAuthorization is used when credentials are passed to a server but
+ // those credentials are rejected.
+ ErrInvalidAuthorization = errors.New("authorization failed")
+)
-func NewResolver() remotes.Resolver {
- return &dockerResolver{}
+type dockerResolver struct {
+ credentials func(string) (string, string, error)
+ plainHTTP bool
+ client *http.Client
+}
+
+// ResolverOptions are used to configured a new Docker register resolver
+type ResolverOptions struct {
+ // Credentials provides username and secret given a host.
+ // If username is empty but a secret is given, that secret
+ // is interpretted as a long lived token.
+ Credentials func(string) (string, string, error)
+
+ // PlainHTTP specifies to use plain http and not https
+ PlainHTTP bool
+
+ // Client is the http client to used when making registry requests
+ Client *http.Client
+}
+
+// NewResolver returns a new resolver to a Docker registry
+func NewResolver(options ResolverOptions) remotes.Resolver {
+ return &dockerResolver{
+ credentials: options.Credentials,
+ plainHTTP: options.PlainHTTP,
+ client: options.Client,
+ }
}
var _ remotes.Resolver = &dockerResolver{}
@@ -43,31 +73,38 @@
}
var (
- base url.URL
- token string
+ base url.URL
+ username, secret string
)
- switch refspec.Hostname() {
- case "docker.io":
- base.Scheme = "https"
+ host := refspec.Hostname()
+ base.Scheme = "https"
+
+ if host == "docker.io" {
base.Host = "registry-1.docker.io"
- prefix := strings.TrimPrefix(refspec.Locator, "docker.io/")
- base.Path = path.Join("/v2", prefix)
- token, err = getToken(ctx, "repository:"+prefix+":pull")
+ } else {
+ base.Host = host
+
+ if r.plainHTTP || strings.HasPrefix(host, "localhost:") {
+ base.Scheme = "http"
+ }
+ }
+
+ if r.credentials != nil {
+ username, secret, err = r.credentials(base.Host)
if err != nil {
return "", ocispec.Descriptor{}, nil, err
}
- case "localhost:5000":
- base.Scheme = "http"
- base.Host = "localhost:5000"
- base.Path = path.Join("/v2", strings.TrimPrefix(refspec.Locator, "localhost:5000/"))
- default:
- return "", ocispec.Descriptor{}, nil, errors.Errorf("unsupported locator: %q", refspec.Locator)
}
+ prefix := strings.TrimPrefix(refspec.Locator, host+"/")
+ base.Path = path.Join("/v2", prefix)
+
fetcher := &dockerFetcher{
- base: base,
- token: token,
+ base: base,
+ client: r.client,
+ username: username,
+ secret: secret,
}
var (
@@ -125,16 +162,13 @@
if dgstHeader != "" {
if err := dgstHeader.Validate(); err != nil {
- if err == nil {
- return "", ocispec.Descriptor{}, nil, errors.Errorf("%q in header not a valid digest", dgstHeader)
- }
return "", ocispec.Descriptor{}, nil, errors.Wrapf(err, "%q in header not a valid digest", dgstHeader)
}
dgst = dgstHeader
}
if dgst == "" {
- return "", ocispec.Descriptor{}, nil, errors.Wrapf(err, "could not resolve digest for %v", ref)
+ return "", ocispec.Descriptor{}, nil, errors.Errorf("could not resolve digest for %v", ref)
}
var (
@@ -143,8 +177,12 @@
)
size, err = strconv.ParseInt(sizeHeader, 10, 64)
- if err != nil || size < 0 {
- return "", ocispec.Descriptor{}, nil, errors.Wrapf(err, "%q in header not a valid size", sizeHeader)
+ if err != nil {
+
+ return "", ocispec.Descriptor{}, nil, errors.Wrapf(err, "invalid size header: %q", sizeHeader)
+ }
+ if size < 0 {
+ return "", ocispec.Descriptor{}, nil, errors.Errorf("%q in header not a valid size", sizeHeader)
}
desc := ocispec.Descriptor{
@@ -163,6 +201,11 @@
type dockerFetcher struct {
base url.URL
token string
+
+ client *http.Client
+ useBasic bool
+ username string
+ secret string
}
func (r *dockerFetcher) Fetch(ctx context.Context, desc ocispec.Descriptor) (io.ReadCloser, error) {
@@ -213,13 +256,14 @@
}
func (r *dockerFetcher) doRequest(ctx context.Context, req *http.Request) (*http.Response, error) {
+ return r.doRequestWithRetries(ctx, req, nil)
+}
+
+func (r *dockerFetcher) doRequestWithRetries(ctx context.Context, req *http.Request, responses []*http.Response) (*http.Response, error) {
ctx = log.WithLogger(ctx, log.G(ctx).WithField("url", req.URL.String()))
log.G(ctx).WithField("request.headers", req.Header).Debug("fetch content")
- if r.token != "" {
- req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", r.token))
- }
-
- resp, err := ctxhttp.Do(ctx, http.DefaultClient, req)
+ r.authorize(req)
+ resp, err := ctxhttp.Do(ctx, r.client, req)
if err != nil {
return nil, err
}
@@ -228,50 +272,119 @@
"response.headers": resp.Header,
}).Debug("fetch response received")
+ responses = append(responses, resp)
+ req, err = r.retryRequest(ctx, req, responses)
+ if err != nil {
+ return nil, err
+ }
+ if req != nil {
+ return r.doRequestWithRetries(ctx, req, responses)
+ }
return resp, err
}
-func getToken(ctx context.Context, scopes ...string) (string, error) {
- var (
- u = url.URL{
- Scheme: "https",
- Host: "auth.docker.io",
- Path: "/token",
+func (r *dockerFetcher) authorize(req *http.Request) {
+ if r.useBasic {
+ req.SetBasicAuth(r.username, r.secret)
+ } else if r.token != "" {
+ req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", r.token))
+ }
+}
+
+func (r *dockerFetcher) retryRequest(ctx context.Context, req *http.Request, responses []*http.Response) (*http.Request, error) {
+ if len(responses) > 5 {
+ return nil, nil
+ }
+ last := responses[len(responses)-1]
+ if last.StatusCode == http.StatusUnauthorized {
+ log.G(ctx).WithField("header", last.Header.Get("WWW-Authenticate")).Debug("Unauthorized")
+ for _, c := range parseAuthHeader(last.Header) {
+ if c.scheme == bearerAuth {
+ if errStr := c.parameters["error"]; errStr != "" {
+ // TODO: handle expired case
+ return nil, errors.Wrapf(ErrInvalidAuthorization, "server message: %s", errStr)
+ }
+ if err := r.setTokenAuth(ctx, c.parameters); err != nil {
+ return nil, err
+ }
+ return req, nil
+ } else if c.scheme == basicAuth {
+ if r.username != "" && r.secret != "" {
+ r.useBasic = true
+ }
+ return req, nil
+ }
}
-
- q = url.Values{
- "scope": scopes,
- "service": []string{"registry.docker.io"}, // usually comes from auth challenge
+ return nil, nil
+ } else if last.StatusCode == http.StatusMethodNotAllowed && req.Method == http.MethodHead {
+ // Support registries which have not properly implemented the HEAD method for
+ // manifests endpoint
+ if strings.Contains(req.URL.Path, "/manifests/") {
+ // TODO: copy request?
+ req.Method = http.MethodGet
+ return req, nil
}
- )
+ }
- u.RawQuery = q.Encode()
+ // TODO: Handle 50x errors accounting for attempt history
+ return nil, nil
+}
- log.G(ctx).WithField("token.url", u.String()).Debug("requesting token")
- resp, err := ctxhttp.Get(ctx, http.DefaultClient, u.String())
+func isManifestAccept(h http.Header) bool {
+ for _, ah := range h[textproto.CanonicalMIMEHeaderKey("Accept")] {
+ switch ah {
+ case images.MediaTypeDockerSchema2Manifest:
+ fallthrough
+ case images.MediaTypeDockerSchema2ManifestList:
+ fallthrough
+ case ocispec.MediaTypeImageManifest:
+ fallthrough
+ case ocispec.MediaTypeImageIndex:
+ return true
+ }
+ }
+ return false
+}
+
+func (r *dockerFetcher) setTokenAuth(ctx context.Context, params map[string]string) error {
+ realm, ok := params["realm"]
+ if !ok {
+ return errors.New("no realm specified for token auth challenge")
+ }
+
+ realmURL, err := url.Parse(realm)
if err != nil {
- return "", err
- }
- defer resp.Body.Close()
-
- if resp.StatusCode > 299 {
- return "", errors.Errorf("unexpected status code: %v %v", resp.StatusCode, resp.Status)
+ return fmt.Errorf("invalid token auth challenge realm: %s", err)
}
- p, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- return "", err
+ to := tokenOptions{
+ realm: realmURL.String(),
+ service: params["service"],
}
- var tokenResponse struct {
- Token string `json:"token"`
+ scope, ok := params["scope"]
+ if !ok {
+ return errors.Errorf("no scope specified for token auth challenge")
}
- if err := json.Unmarshal(p, &tokenResponse); err != nil {
- return "", err
+ // TODO: Get added scopes from context
+ to.scopes = []string{scope}
+
+ if r.secret != "" {
+ // Credential information is provided, use oauth POST endpoint
+ r.token, err = r.fetchTokenWithOAuth(ctx, to)
+ if err != nil {
+ return errors.Wrap(err, "failed to fetch oauth token")
+ }
+ } else {
+ // Do request anonymously
+ r.token, err = r.getToken(ctx, to)
+ if err != nil {
+ return errors.Wrap(err, "failed to fetch anonymous token")
+ }
}
- return tokenResponse.Token, nil
+ return nil
}
// getV2URLPaths generates the candidate urls paths for the object based on the
@@ -291,3 +404,125 @@
return urls, nil
}
+
+type tokenOptions struct {
+ realm string
+ service string
+ scopes []string
+}
+
+type postTokenResponse struct {
+ AccessToken string `json:"access_token"`
+ RefreshToken string `json:"refresh_token"`
+ ExpiresIn int `json:"expires_in"`
+ IssuedAt time.Time `json:"issued_at"`
+ Scope string `json:"scope"`
+}
+
+func (r *dockerFetcher) fetchTokenWithOAuth(ctx context.Context, to tokenOptions) (string, error) {
+ form := url.Values{}
+ form.Set("scope", strings.Join(to.scopes, " "))
+ form.Set("service", to.service)
+ // TODO: Allow setting client_id
+ form.Set("client_id", "containerd-dist-tool")
+
+ if r.username == "" {
+ form.Set("grant_type", "refresh_token")
+ form.Set("refresh_token", r.secret)
+ } else {
+ form.Set("grant_type", "password")
+ form.Set("username", r.username)
+ form.Set("password", r.secret)
+ }
+
+ resp, err := ctxhttp.PostForm(ctx, r.client, to.realm, form)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode == 405 && r.username != "" {
+ // It would be nice if registries would implement the specifications
+ return r.getToken(ctx, to)
+ } else if resp.StatusCode < 200 || resp.StatusCode >= 400 {
+ b, _ := ioutil.ReadAll(resp.Body)
+ log.G(ctx).WithFields(logrus.Fields{
+ "status": resp.Status,
+ "body": string(b),
+ }).Debugf("token request failed")
+ // TODO: handle error body and write debug output
+ return "", errors.Errorf("unexpected status: %s", resp.Status)
+ }
+
+ decoder := json.NewDecoder(resp.Body)
+
+ var tr postTokenResponse
+ if err = decoder.Decode(&tr); err != nil {
+ return "", fmt.Errorf("unable to decode token response: %s", err)
+ }
+
+ return tr.AccessToken, nil
+}
+
+type getTokenResponse struct {
+ Token string `json:"token"`
+ AccessToken string `json:"access_token"`
+ ExpiresIn int `json:"expires_in"`
+ IssuedAt time.Time `json:"issued_at"`
+ RefreshToken string `json:"refresh_token"`
+}
+
+// getToken fetches a token using a GET request
+func (r *dockerFetcher) getToken(ctx context.Context, to tokenOptions) (string, error) {
+ req, err := http.NewRequest("GET", to.realm, nil)
+ if err != nil {
+ return "", err
+ }
+
+ reqParams := req.URL.Query()
+
+ if to.service != "" {
+ reqParams.Add("service", to.service)
+ }
+
+ for _, scope := range to.scopes {
+ reqParams.Add("scope", scope)
+ }
+
+ if r.secret != "" {
+ req.SetBasicAuth(r.username, r.secret)
+ }
+
+ req.URL.RawQuery = reqParams.Encode()
+
+ resp, err := ctxhttp.Do(ctx, r.client, req)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode < 200 || resp.StatusCode >= 400 {
+ // TODO: handle error body and write debug output
+ return "", errors.Errorf("unexpected status: %s", resp.Status)
+ }
+
+ decoder := json.NewDecoder(resp.Body)
+
+ var tr getTokenResponse
+ if err = decoder.Decode(&tr); err != nil {
+ return "", fmt.Errorf("unable to decode token response: %s", err)
+ }
+
+ // `access_token` is equivalent to `token` and if both are specified
+ // the choice is undefined. Canonicalize `access_token` by sticking
+ // things in `token`.
+ if tr.AccessToken != "" {
+ tr.Token = tr.AccessToken
+ }
+
+ if tr.Token == "" {
+ return "", ErrNoToken
+ }
+
+ return tr.Token, nil
+}
diff --git a/remotes/docker/resolver_test.go b/remotes/docker/resolver_test.go
new file mode 100644
index 0000000..ee9f150
--- /dev/null
+++ b/remotes/docker/resolver_test.go
@@ -0,0 +1,388 @@
+package docker
+
+import (
+ "context"
+ "crypto/tls"
+ "crypto/x509"
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "strconv"
+ "strings"
+ "testing"
+
+ "github.com/containerd/containerd/remotes"
+ digest "github.com/opencontainers/go-digest"
+ specs "github.com/opencontainers/image-spec/specs-go"
+ ocispec "github.com/opencontainers/image-spec/specs-go/v1"
+ "github.com/pkg/errors"
+)
+
+func TestHTTPResolver(t *testing.T) {
+ s := func(h http.Handler) (string, ResolverOptions, func()) {
+ s := httptest.NewServer(h)
+
+ options := ResolverOptions{
+ PlainHTTP: true,
+ }
+ base := s.URL[7:] // strip "http://"
+ return base, options, s.Close
+ }
+
+ runBasicTest(t, "testname", s)
+}
+
+func TestHTTPSResolver(t *testing.T) {
+ runBasicTest(t, "testname", tlsServer)
+}
+
+func TestBasicResolver(t *testing.T) {
+ basicAuth := func(h http.Handler) (string, ResolverOptions, func()) {
+ // Wrap with basic auth
+ wrapped := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
+ username, password, ok := r.BasicAuth()
+ if !ok || username != "user1" || password != "password1" {
+ rw.Header().Set("WWW-Authenticate", "Basic realm=localhost")
+ rw.WriteHeader(http.StatusUnauthorized)
+ return
+ }
+ h.ServeHTTP(rw, r)
+ })
+
+ base, options, close := tlsServer(wrapped)
+ options.Credentials = func(string) (string, string, error) {
+ return "user1", "password1", nil
+ }
+ return base, options, close
+ }
+ runBasicTest(t, "testname", basicAuth)
+}
+
+func TestAnonymousTokenResolver(t *testing.T) {
+ th := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodGet {
+ rw.WriteHeader(http.StatusMethodNotAllowed)
+ return
+ }
+ rw.Header().Set("Content-Type", "application/json")
+ rw.WriteHeader(http.StatusOK)
+ rw.Write([]byte(`{"access_token":"perfectlyvalidopaquetoken"}`))
+ })
+
+ runBasicTest(t, "testname", withTokenServer(th, nil))
+}
+
+func TestBasicAuthTokenResolver(t *testing.T) {
+ th := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodGet {
+ rw.WriteHeader(http.StatusMethodNotAllowed)
+ return
+ }
+ rw.Header().Set("Content-Type", "application/json")
+ rw.WriteHeader(http.StatusOK)
+ username, password, ok := r.BasicAuth()
+ if !ok || username != "user1" || password != "password1" {
+ rw.Write([]byte(`{"access_token":"insufficientscope"}`))
+ } else {
+ rw.Write([]byte(`{"access_token":"perfectlyvalidopaquetoken"}`))
+ }
+ })
+ creds := func(string) (string, string, error) {
+ return "user1", "password1", nil
+ }
+
+ runBasicTest(t, "testname", withTokenServer(th, creds))
+}
+
+func TestRefreshTokenResolver(t *testing.T) {
+ th := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodPost {
+ rw.WriteHeader(http.StatusMethodNotAllowed)
+ return
+ }
+ rw.Header().Set("Content-Type", "application/json")
+ rw.WriteHeader(http.StatusOK)
+
+ r.ParseForm()
+ if r.PostForm.Get("grant_type") != "refresh_token" || r.PostForm.Get("refresh_token") != "somerefreshtoken" {
+ rw.Write([]byte(`{"access_token":"insufficientscope"}`))
+ } else {
+ rw.Write([]byte(`{"access_token":"perfectlyvalidopaquetoken"}`))
+ }
+ })
+ creds := func(string) (string, string, error) {
+ return "", "somerefreshtoken", nil
+ }
+
+ runBasicTest(t, "testname", withTokenServer(th, creds))
+}
+
+func TestPostBasicAuthTokenResolver(t *testing.T) {
+ th := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodPost {
+ rw.WriteHeader(http.StatusMethodNotAllowed)
+ return
+ }
+ rw.Header().Set("Content-Type", "application/json")
+ rw.WriteHeader(http.StatusOK)
+
+ r.ParseForm()
+ if r.PostForm.Get("grant_type") != "password" || r.PostForm.Get("username") != "user1" || r.PostForm.Get("password") != "password1" {
+ rw.Write([]byte(`{"access_token":"insufficientscope"}`))
+ } else {
+ rw.Write([]byte(`{"access_token":"perfectlyvalidopaquetoken"}`))
+ }
+ })
+ creds := func(string) (string, string, error) {
+ return "user1", "password1", nil
+ }
+
+ runBasicTest(t, "testname", withTokenServer(th, creds))
+}
+
+func TestBadTokenResolver(t *testing.T) {
+ th := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodPost {
+ rw.WriteHeader(http.StatusMethodNotAllowed)
+ return
+ }
+ rw.Header().Set("Content-Type", "application/json")
+ rw.WriteHeader(http.StatusOK)
+ rw.Write([]byte(`{"access_token":"insufficientscope"}`))
+ })
+ creds := func(string) (string, string, error) {
+ return "", "somerefreshtoken", nil
+ }
+
+ ctx := context.Background()
+ h := content(ocispec.MediaTypeImageManifest, []byte("not anything parse-able"))
+
+ base, ro, close := withTokenServer(th, creds)(logHandler{t, h})
+ defer close()
+
+ resolver := NewResolver(ro)
+ image := fmt.Sprintf("%s/doesntmatter:sometatg", base)
+
+ _, _, _, err := resolver.Resolve(ctx, image)
+ if err == nil {
+ t.Fatal("Expected error getting token with inssufficient scope")
+ }
+ if errors.Cause(err) != ErrInvalidAuthorization {
+ t.Fatal(err)
+ }
+}
+
+func withTokenServer(th http.Handler, creds func(string) (string, string, error)) func(h http.Handler) (string, ResolverOptions, func()) {
+ return func(h http.Handler) (string, ResolverOptions, func()) {
+ s := httptest.NewUnstartedServer(th)
+ s.StartTLS()
+
+ cert, _ := x509.ParseCertificate(s.TLS.Certificates[0].Certificate[0])
+ tokenBase := s.URL + "/token"
+
+ // Wrap with token auth
+ wrapped := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
+ auth := strings.ToLower(r.Header.Get("Authorization"))
+ if auth != "bearer perfectlyvalidopaquetoken" {
+ authHeader := fmt.Sprintf("Bearer realm=%q,service=registry,scope=\"repository:testname:pull,pull\"", tokenBase)
+ if strings.HasPrefix(auth, "bearer ") {
+ authHeader = authHeader + ",error=" + auth[7:]
+ }
+ rw.Header().Set("WWW-Authenticate", authHeader)
+ rw.WriteHeader(http.StatusUnauthorized)
+ return
+ }
+ h.ServeHTTP(rw, r)
+ })
+
+ base, options, close := tlsServer(wrapped)
+ options.Credentials = creds
+ options.Client.Transport.(*http.Transport).TLSClientConfig.RootCAs.AddCert(cert)
+ return base, options, func() {
+ s.Close()
+ close()
+ }
+ }
+}
+
+func tlsServer(h http.Handler) (string, ResolverOptions, func()) {
+ s := httptest.NewUnstartedServer(h)
+ s.StartTLS()
+
+ capool := x509.NewCertPool()
+ cert, _ := x509.ParseCertificate(s.TLS.Certificates[0].Certificate[0])
+ capool.AddCert(cert)
+
+ options := ResolverOptions{
+ Client: &http.Client{
+ Transport: &http.Transport{
+ TLSClientConfig: &tls.Config{
+ RootCAs: capool,
+ },
+ },
+ },
+ }
+ base := s.URL[8:] // strip "https://"
+ return base, options, s.Close
+}
+
+type logHandler struct {
+ t *testing.T
+ handler http.Handler
+}
+
+func (h logHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
+ h.t.Logf("%s %s", r.Method, r.URL.String())
+ h.handler.ServeHTTP(rw, r)
+}
+
+func runBasicTest(t *testing.T, name string, sf func(h http.Handler) (string, ResolverOptions, func())) {
+ var (
+ ctx = context.Background()
+ tag = "latest"
+ r = http.NewServeMux()
+ )
+
+ m := newManifest(
+ content(ocispec.MediaTypeImageConfig, []byte("1")),
+ content(ocispec.MediaTypeImageLayerGzip, []byte("2")),
+ )
+ mc := content(ocispec.MediaTypeImageManifest, m.OCIManifest())
+ m.RegisterHandler(r, name)
+ r.Handle(fmt.Sprintf("/v2/%s/manifests/%s", name, tag), mc)
+ r.Handle(fmt.Sprintf("/v2/%s/manifests/%s", name, mc.Digest()), mc)
+
+ base, ro, close := sf(logHandler{t, r})
+ defer close()
+
+ resolver := NewResolver(ro)
+ image := fmt.Sprintf("%s/%s:%s", base, name, tag)
+
+ _, d, f, err := resolver.Resolve(ctx, image)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ refs, err := testocimanifest(ctx, f, d)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if len(refs) != 2 {
+ t.Fatalf("Unexpected number of references: %d, expected 2", len(refs))
+ }
+
+ for _, ref := range refs {
+ if err := testFetch(ctx, f, ref); err != nil {
+ t.Fatal(err)
+ }
+ }
+}
+
+func testFetch(ctx context.Context, f remotes.Fetcher, desc ocispec.Descriptor) error {
+ r, err := f.Fetch(ctx, desc)
+ if err != nil {
+ return err
+ }
+ dgstr := desc.Digest.Algorithm().Digester()
+ io.Copy(dgstr.Hash(), r)
+ if dgstr.Digest() != desc.Digest {
+ return errors.Errorf("content mismatch: %s != %s", dgstr.Digest(), desc.Digest)
+ }
+
+ return nil
+}
+
+func testocimanifest(ctx context.Context, f remotes.Fetcher, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
+ r, err := f.Fetch(ctx, desc)
+ if err != nil {
+ return nil, errors.Wrapf(err, "failed to fetch %s", desc.Digest)
+ }
+ p, err := ioutil.ReadAll(r)
+ if err != nil {
+ return nil, err
+ }
+ if dgst := desc.Digest.Algorithm().FromBytes(p); dgst != desc.Digest {
+ return nil, errors.Errorf("digest mismatch: %s != %s", dgst, desc.Digest)
+ }
+
+ var manifest ocispec.Manifest
+ if err := json.Unmarshal(p, &manifest); err != nil {
+ return nil, err
+ }
+
+ var descs []ocispec.Descriptor
+
+ descs = append(descs, manifest.Config)
+ descs = append(descs, manifest.Layers...)
+
+ return descs, nil
+}
+
+type testContent struct {
+ mediaType string
+ content []byte
+}
+
+func content(mediaType string, b []byte) testContent {
+ return testContent{
+ mediaType: mediaType,
+ content: b,
+ }
+}
+
+func (tc testContent) Descriptor() ocispec.Descriptor {
+ return ocispec.Descriptor{
+ MediaType: tc.mediaType,
+ Digest: digest.FromBytes(tc.content),
+ Size: int64(len(tc.content)),
+ }
+}
+
+func (tc testContent) Digest() digest.Digest {
+ return digest.FromBytes(tc.content)
+}
+
+func (tc testContent) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ w.Header().Add("Content-Type", tc.mediaType)
+ w.Header().Add("Content-Length", strconv.Itoa(len(tc.content)))
+ w.Header().Add("Docker-Content-Digest", tc.Digest().String())
+ w.WriteHeader(http.StatusOK)
+ w.Write(tc.content)
+}
+
+type testManifest struct {
+ config testContent
+ references []testContent
+}
+
+func newManifest(config testContent, refs ...testContent) testManifest {
+ return testManifest{
+ config: config,
+ references: refs,
+ }
+}
+
+func (m testManifest) OCIManifest() []byte {
+ manifest := ocispec.Manifest{
+ Versioned: specs.Versioned{
+ SchemaVersion: 1,
+ },
+ Config: m.config.Descriptor(),
+ Layers: make([]ocispec.Descriptor, len(m.references)),
+ }
+ for i, c := range append(m.references) {
+ manifest.Layers[i] = c.Descriptor()
+ }
+ b, _ := json.Marshal(manifest)
+ return b
+}
+
+func (m testManifest) RegisterHandler(r *http.ServeMux, name string) {
+ for _, c := range append(m.references, m.config) {
+ r.Handle(fmt.Sprintf("/v2/%s/blobs/%s", name, c.Digest()), c)
+ }
+}
diff --git a/reports/2017-04-28.md b/reports/2017-04-28.md
new file mode 100644
index 0000000..b8903d2
--- /dev/null
+++ b/reports/2017-04-28.md
@@ -0,0 +1,88 @@
+# Development Report for April 28, 2017
+
+Sorry for the slow reports lately. Last week was Dockercon and many people inside Docker and external contributors where in Austin for the week. It was a long week and we didn't get much work done on the project but it was good meeting everyone IRL and putting a face to the github handle. Also, thank you to everyone that stayed a day late to attend the summit on Thursday. You can see the summit notes at the bottom of this report.
+
+## New Projects to the Org.
+
+We are looking to move some of our dependencies used by containerd into the org. We want to have a simple maintainer model for containerd and any other repository that is added to the org. Bascially, if you are a maintainer of containerd, you are a maintainer on all of the repos. We don't want to have separate maintainers for the various parts as we should all share the responsibility for quality and stability for the project as a whole.
+
+We have an issue on github discussing the various projects that we want to add. If you have any feedback please let us know on the issue below.
+
+[projects!](https://github.com/containerd/containerd/issues/772)
+[process](https://github.com/containerd/containerd/pull/779)
+
+## Darwin/BSD Port
+
+Justin has begun work on porting over containerd to Darwin and BSD. `ctr` compiles and there were a few changes in terminal handling that had to be made but overall its going fairly smooth. You can ping @justincormack for more information or if you are interested in working on something like a BSD jails port of `runc`.
+
+## Introspection
+
+@AkihiroSuda has been working on introspection RPCs to make sure consumers know the services and features that they are interacting with. There is still a lot to consider when exposing service versions, containerd versions and functionality; if you have any input you can take a look at the current PR [here](https://github.com/containerd/containerd/pull/776).
+
+## Expanded Registry Support
+
+@dmcgowan has been working on expanding registry support with authentication in [783](https://github.com/containerd/containerd/pull/783). This expands registry support for gcr.io, Dockerhub, and local registries.
+
+
+## Task List for week of May 1
+
+So, now that we are all back from Dockercon and not rested at all, whats on the agenda for next week?
+
+* Docker Integration Pass
+ - Replace the current v0.2.x branch of containerd in Docker for execution with the 1.0 execution service
+* Converge Event Handling
+* Move new projects into containerd organization
+* New Layer creation for supporting build
+* Continue work on metadata service
+ - container
+ - layer
+ - image
+ - namespace support
+
+If are you looking for areas to contribute to containerd just take a pass through the issue tracker. There are issues such as [770](https://github.com/containerd/containerd/issues/770) that could use some help in solving as well as other. Also feel free to stop by the slack channel for containerd to chat and find ideas on what needs to be developed next.
+
+## containerd Summit Notes
+
+Here are the notes from the containerd summit that was held after Dockercon. If there are important to you and you would like to start working on a feature, fix, request, please go ahead and open an issue on github. Just remember, we shouldn't have to have a summit to get things into containerd. Open issues, disscuss with other members of the community and write the code ;)
+
+* “Since containerd is one of the bottom bricks in the stack, how can we setup automated integration tests for consumers of containerd?”
+ - Looking for others to provide compute (testing within Docker, K8s, Microsoft)
+ - Would we publish results of integration tests, not likely per PR
+ - K8s had a similar problem, solved by pushing builds to buckets and creating dashboards from results pushed back to buckets
+ - Need tests for projects containerd is integrated with (Windows, runv), testing containerd connection to shims
+* “We'd like to propose an Authorization plugin to containerd that would allow an external component to police events like container start & stop (and have a discussion about the best way to go about it)”
+ - Need to look into how it could be implemented with containerd
+ - Possible to be done by MITM the GRPC socket
+ - Currently seen as out of scope for server, can be done via client (or proxy), we could build a client package to support this so it does not have to be implemented by each integrator.
+ - Currently only unix socket allowed which prevents unauthorized access
+* “Should containerd provide image filesystem metrics? If yes, what metrics should be included? How to implement that?”
+ - Previously discussed, yes, PR to provide this information through the snapshot interface.
+ - Figuring out what is being shared, could figure out it from mapping layers to images
+* “Support for disk quotas: How? What is the role of containerd? How is it going to be integrated with volume managers that want to be in the same quota group?”
+ - Expanding question, “will each snapshot driver have a quota system? What is the granularity, per container, global?”
+ - Stephen’s hope is that quota management can be done based on the returned mounts, otherwise need to figure out quota abstraction
+ - Could we rely on work being done for volumes with CSI
+ - Need to open up issue to discuss project quotas and general snapshot quotas
+* “Checkpoint/Restore: how can we support more use cases? One of the big issues here is the large number of options that can be passed to CRIU.”
+ - Not currently in Containerd, requires runc update as well
+ - Need to figure out a way to pass in options opaquely
+ - Needs to be discuss with Michael for what best solution for runc will be
+ - Today caller must know runtime since runtime spec is passed opaquely on create to runtime
+* “How to support multi-OS docker images, for example, Linux Vs Windows using one graph driver plugin properly?”
+ - From looking into implementing linux containers on HyperV
+ - Missing options when creating rootfs to specify the snapshot driver
+ - Containerd will support running multiple snapshotter
+ - Timeline, Docker will first use update runtime after stabilizing
+* “Containerd is acting as init system in LinuxKit, is there a plan to support what init systems support”
+- Containerd was not implemented as an init system, we are not trying to compete with it. A higher level init system could be implemented with it.
+* “Will containerd be able to leverage runtime plugins specified within Docker?”
+ - Docker would have to orchestrate re-configuring containerd with the new runtime
+ - Currently cannot hotswap plugins, runtime and shim are built in
+ - Would require defining plugin interface or getting go 1.8 plugins working
+ - We could have a plugin which connects to a grpc address
+ - Resolved in rocket by first allowing downloading a runtime and running it as privileged
+ - Need to open up an issue to discuss this
+* “What do we want from orchestrator who are looking at integration?”
+ - Feedback is biggest
+ - Currently targeting K8s and SwarmKit to keep API grounded
+ - Starting PoCs now, we have already made changes based on early feedback
diff --git a/reports/2017-05-05.md b/reports/2017-05-05.md
new file mode 100644
index 0000000..175ac6d
--- /dev/null
+++ b/reports/2017-05-05.md
@@ -0,0 +1,47 @@
+# Development Report for May 05, 2017
+
+## runc and runtime-spec 1.0 final
+
+This week we have been working on getting Docker integrated with the final RCs for the OCI runtime-spec and runc. We currently have a [PR open on Docker](https://github.com/moby/moby/pull/33007) that is ready for review.
+
+This is important for the containerd project because we need a stable runtime like runc and have a spec that is not constantly changing so that people can begin integrating with containerd and know that the APIs we expose and APIs/specs that we depend on are stable for the long term.
+
+## New Projects
+
+We finished moving a few of our external dependencies into the containerd organization this week. There were a few projects that we built outside but wanted to bring these under the project to ensure that our dependencies are held to the same standards as the rest of our codebase.
+
+### go-runc
+
+[go-runc](https://github.com/containerd/go-runc)
+
+This project contains the runc bindings that we consume in containerd to interface with runc and other OCI runtimes. It lets us interact with the binary and handles many of the common options like working with the console socket and other IO operations.
+
+### fifo
+
+[fifo](https://github.com/containerd/fifo)
+
+This package contains helpers for handling fifos. Fifos are a little more complex than regular pipes and sometimes requires special handling and blocking semantics depending on the flags used with opening the fifo on either end. This package helps to handle many of the common use cases that we use in containerd.
+
+### console
+
+[console](https://github.com/containerd/console)
+
+The console package is a refresh of the `term` package from Docker. It provides a cleaner API for working with the current console for a program or creating new terminals and keeping the flags in sync for proxying reads and writes between the two sides of the console.
+
+### cgroups
+
+[cgroups](https://github.com/containerd/cgroups)
+
+The cgroups package is currently used in containerd for collecting stats from cgroups that are created for a container. It exposes a package for exporting cgroup level metrics to prometheus for containers.
+
+### btrfs
+
+[btrfs](https://github.com/containerd/btrfs)
+
+The btrfs package handles interfacing with btrfs for our snapshotter. It binds to the btrfs C library to create subvolumes and handle any other interaction with the filesystem.
+
+### continuity
+
+[continuity](https://github.com/containerd/continuity)
+
+continuity provides a transport agnostic filesystem metadata manifest. This allows us to work with filesystems at the file level instead of interacting with a "layer". We also intend to concentrate a rich set of file system utility packages for use in containerd.
diff --git a/services/content/ingester.go b/services/content/ingester.go
index 191626b..a4182cc 100644
--- a/services/content/ingester.go
+++ b/services/content/ingester.go
@@ -127,6 +127,9 @@
}
rw.offset += int64(n)
+ if resp.Digest != "" {
+ rw.digest = resp.Digest
+ }
return
}
@@ -149,6 +152,8 @@
return errors.Errorf("unexpected digest: %v != %v", resp.Digest, expected)
}
+ rw.digest = resp.Digest
+ rw.offset = resp.Offset
return nil
}
diff --git a/services/content/service.go b/services/content/service.go
index 128104f..c138184 100644
--- a/services/content/service.go
+++ b/services/content/service.go
@@ -304,9 +304,9 @@
if err := wr.Commit(total, expected); err != nil {
return err
}
-
- msg.Digest = wr.Digest()
}
+
+ msg.Digest = wr.Digest()
case api.WriteActionAbort:
return s.store.Abort(ref)
}
diff --git a/services/execution/service.go b/services/execution/service.go
index 086312a..ef481ab 100644
--- a/services/execution/service.go
+++ b/services/execution/service.go
@@ -188,6 +188,30 @@
return resp, nil
}
+func (s *Service) Pause(ctx context.Context, r *api.PauseRequest) (*google_protobuf.Empty, error) {
+ c, err := s.getContainer(r.ID)
+ if err != nil {
+ return nil, err
+ }
+ err = c.Pause(ctx)
+ if err != nil {
+ return nil, err
+ }
+ return empty, nil
+}
+
+func (s *Service) Resume(ctx context.Context, r *api.ResumeRequest) (*google_protobuf.Empty, error) {
+ c, err := s.getContainer(r.ID)
+ if err != nil {
+ return nil, err
+ }
+ err = c.Resume(ctx)
+ if err != nil {
+ return nil, err
+ }
+ return empty, nil
+}
+
func (s *Service) Kill(ctx context.Context, r *api.KillRequest) (*google_protobuf.Empty, error) {
c, err := s.getContainer(r.ID)
if err != nil {
@@ -220,6 +244,9 @@
Terminal: r.Terminal,
},
})
+ if err != nil {
+ return nil, err
+ }
state, err := process.State(ctx)
if err != nil {
return nil, err
diff --git a/snapshot/btrfs/btrfs.go b/snapshot/btrfs/btrfs.go
index 61c6094..5705f3c 100644
--- a/snapshot/btrfs/btrfs.go
+++ b/snapshot/btrfs/btrfs.go
@@ -9,13 +9,13 @@
"path/filepath"
"github.com/Sirupsen/logrus"
+ "github.com/containerd/btrfs"
"github.com/containerd/containerd"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/snapshot"
"github.com/containerd/containerd/snapshot/storage"
"github.com/pkg/errors"
- "github.com/stevvooe/go-btrfs"
)
type btrfsConfig struct {
@@ -88,7 +88,28 @@
return snapshot.Info{}, err
}
defer t.Rollback()
- return storage.GetInfo(ctx, key)
+ _, info, _, err := storage.GetInfo(ctx, key)
+ if err != nil {
+ return snapshot.Info{}, err
+ }
+
+ return info, nil
+}
+
+// Usage retrieves the disk usage of the top-level snapshot.
+func (b *snapshotter) Usage(ctx context.Context, key string) (snapshot.Usage, error) {
+ panic("not implemented")
+
+ // TODO(stevvooe): Btrfs has a quota model where data can be exclusive to a
+ // snapshot or shared among other resources. We may find that this is the
+ // correct value to reoprt but the stability of the implementation is under
+ // question.
+ //
+ // In general, this has impact on the model we choose for reporting usage.
+ // Ideally, the value should allow aggregration. For overlay, this is
+ // simple since we can scan the diff directory to get a unique value. This
+ // breaks down when start looking the behavior when data is shared between
+ // snapshots, such as that for btrfs.
}
// Walk the committed snapshots.
@@ -193,7 +214,7 @@
}
}()
- id, err := storage.CommitActive(ctx, key, name)
+ id, err := storage.CommitActive(ctx, key, name, snapshot.Usage{}) // TODO(stevvooe): Resolve a usage value for btrfs
if err != nil {
return errors.Wrap(err, "failed to commit")
}
diff --git a/snapshot/naive/naive.go b/snapshot/naive/naive.go
index 9fb52d8..0a1d88f 100644
--- a/snapshot/naive/naive.go
+++ b/snapshot/naive/naive.go
@@ -61,7 +61,35 @@
return snapshot.Info{}, err
}
defer t.Rollback()
- return storage.GetInfo(ctx, key)
+ _, info, _, err := storage.GetInfo(ctx, key)
+ if err != nil {
+ return snapshot.Info{}, err
+ }
+
+ return info, nil
+}
+
+func (o *snapshotter) Usage(ctx context.Context, key string) (snapshot.Usage, error) {
+ ctx, t, err := o.ms.TransactionContext(ctx, false)
+ if err != nil {
+ return snapshot.Usage{}, err
+ }
+ defer t.Rollback()
+
+ id, info, usage, err := storage.GetInfo(ctx, key)
+ if err != nil {
+ return snapshot.Usage{}, err
+ }
+
+ if info.Kind == snapshot.KindActive {
+ du, err := fs.DiskUsage(o.getSnapshotDir(id))
+ if err != nil {
+ return snapshot.Usage{}, err
+ }
+ usage = snapshot.Usage(du)
+ }
+
+ return usage, nil
}
func (o *snapshotter) Prepare(ctx context.Context, key, parent string) ([]containerd.Mount, error) {
@@ -94,7 +122,18 @@
if err != nil {
return err
}
- if _, err := storage.CommitActive(ctx, key, name); err != nil {
+
+ id, _, _, err := storage.GetInfo(ctx, key)
+ if err != nil {
+ return err
+ }
+
+ usage, err := fs.DiskUsage(o.getSnapshotDir(id))
+ if err != nil {
+ return err
+ }
+
+ if _, err := storage.CommitActive(ctx, key, name, snapshot.Usage(usage)); err != nil {
if rerr := t.Rollback(); rerr != nil {
log.G(ctx).WithError(rerr).Warn("Failure rolling back transaction")
}
diff --git a/snapshot/overlay/overlay.go b/snapshot/overlay/overlay.go
index 9df80bd..c60c152 100644
--- a/snapshot/overlay/overlay.go
+++ b/snapshot/overlay/overlay.go
@@ -11,6 +11,7 @@
"strings"
"github.com/containerd/containerd"
+ "github.com/containerd/containerd/fs"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/snapshot"
@@ -72,7 +73,44 @@
return snapshot.Info{}, err
}
defer t.Rollback()
- return storage.GetInfo(ctx, key)
+ _, info, _, err := storage.GetInfo(ctx, key)
+ if err != nil {
+ return snapshot.Info{}, err
+ }
+
+ return info, nil
+}
+
+// Usage returns the resources taken by the snapshot identified by key.
+//
+// For active snapshots, this will scan the usage of the overlay "diff" (aka
+// "upper") directory and may take some time.
+//
+// For committed snapshots, the value is returned from the metadata database.
+func (o *snapshotter) Usage(ctx context.Context, key string) (snapshot.Usage, error) {
+ ctx, t, err := o.ms.TransactionContext(ctx, false)
+ if err != nil {
+ return snapshot.Usage{}, err
+ }
+ id, info, usage, err := storage.GetInfo(ctx, key)
+ if err != nil {
+ return snapshot.Usage{}, err
+ }
+
+ upperPath := o.upperPath(id)
+ t.Rollback() // transaction no longer needed at this point.
+
+ if info.Kind == snapshot.KindActive {
+ du, err := fs.DiskUsage(upperPath)
+ if err != nil {
+ // TODO(stevvooe): Consider not reporting an error in this case.
+ return snapshot.Usage{}, err
+ }
+
+ usage = snapshot.Usage(du)
+ }
+
+ return usage, nil
}
func (o *snapshotter) Prepare(ctx context.Context, key, parent string) ([]containerd.Mount, error) {
@@ -105,7 +143,19 @@
if err != nil {
return err
}
- if _, err := storage.CommitActive(ctx, key, name); err != nil {
+
+ // grab the existing id
+ id, _, _, err := storage.GetInfo(ctx, key)
+ if err != nil {
+ return err
+ }
+
+ usage, err := fs.DiskUsage(o.upperPath(id))
+ if err != nil {
+ return err
+ }
+
+ if _, err := storage.CommitActive(ctx, key, name, snapshot.Usage(usage)); err != nil {
if rerr := t.Rollback(); rerr != nil {
log.G(ctx).WithError(rerr).Warn("Failure rolling back transaction")
}
diff --git a/snapshot/snapshotter.go b/snapshot/snapshotter.go
index 474fd10..aba5c4a 100644
--- a/snapshot/snapshotter.go
+++ b/snapshot/snapshotter.go
@@ -23,6 +23,24 @@
Readonly bool // true if readonly, only valid for active
}
+// Usage defines statistics for disk resources consumed by the snapshot.
+//
+// These resources only include the resources consumed by the snapshot itself
+// and does not include resources usage by the parent.
+type Usage struct {
+ Inodes int64 // number of inodes in use.
+ Size int64 // provides usage, in bytes, of snapshot
+}
+
+func (u *Usage) Add(other Usage) {
+ u.Size += other.Size
+
+ // TODO(stevvooe): assumes independent inodes, but provides and upper
+ // bound. This should be pretty close, assumming the inodes for a
+ // snapshot are roughly unique to it. Don't trust this assumption.
+ u.Inodes += other.Inodes
+}
+
// Snapshotter defines the methods required to implement a snapshot snapshotter for
// allocating, snapshotting and mounting filesystem changesets. The model works
// by building up sets of changes with parent-child relationships.
@@ -146,6 +164,16 @@
// the kind of snapshot.
Stat(ctx context.Context, key string) (Info, error)
+ // Usage returns the resource usage of an active or committed snapshot
+ // excluding the usage of parent snapshots.
+ //
+ // The running time of this call for active snapshots is dependent on
+ // implementation, but may be proportional to the size of the resource.
+ // Callers should take this into consideration. Implementations should
+ // attempt to honer context cancellation and avoid taking locks when making
+ // the calculation.
+ Usage(ctx context.Context, key string) (Usage, error)
+
// Mounts returns the mounts for the active snapshot transaction identified
// by key. Can be called on an read-write or readonly transaction. This is
// available only for active snapshots.
@@ -204,7 +232,7 @@
// removed before proceeding.
Remove(ctx context.Context, key string) error
- // Walk the committed snapshots. For each snapshot in the snapshotter, the
- // function will be called.
+ // Walk all snapshots in the snapshotter. For each snapshot in the
+ // snapshotter, the function will be called.
Walk(ctx context.Context, fn func(context.Context, Info) error) error
}
diff --git a/snapshot/storage/bolt.go b/snapshot/storage/bolt.go
index 455f760..9bb6673 100644
--- a/snapshot/storage/bolt.go
+++ b/snapshot/storage/bolt.go
@@ -63,21 +63,26 @@
// GetInfo returns the snapshot Info directly from the metadata. Requires a
// context with a storage transaction.
-func GetInfo(ctx context.Context, key string) (snapshot.Info, error) {
+func GetInfo(ctx context.Context, key string) (string, snapshot.Info, snapshot.Usage, error) {
var ss db.Snapshot
err := withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error {
return getSnapshot(bkt, key, &ss)
})
if err != nil {
- return snapshot.Info{}, err
+ return "", snapshot.Info{}, snapshot.Usage{}, err
}
- return snapshot.Info{
+ usage := snapshot.Usage{
+ Inodes: ss.Inodes,
+ Size: ss.Size_,
+ }
+
+ return fmt.Sprint(ss.ID), snapshot.Info{
Name: key,
Parent: ss.Parent,
Kind: fromProtoKind(ss.Kind),
Readonly: ss.Readonly,
- }, nil
+ }, usage, nil
}
// WalkInfo iterates through all metadata Info for the stored snapshots and
@@ -263,7 +268,7 @@
// lookup or removal. The returned string identifier for the committed snapshot
// is the same identifier of the original active snapshot. The provided context
// must contain a writable transaction.
-func CommitActive(ctx context.Context, key, name string) (id string, err error) {
+func CommitActive(ctx context.Context, key, name string, usage snapshot.Usage) (id string, err error) {
err = withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error {
b := bkt.Get([]byte(name))
if len(b) != 0 {
@@ -283,6 +288,8 @@
ss.Kind = db.KindCommitted
ss.Readonly = true
+ ss.Inodes = usage.Inodes
+ ss.Size_ = usage.Size
if err := putSnapshot(bkt, name, &ss); err != nil {
return err
diff --git a/snapshot/storage/metastore_bench_test.go b/snapshot/storage/metastore_bench_test.go
index fd5cf2e..21406f0 100644
--- a/snapshot/storage/metastore_bench_test.go
+++ b/snapshot/storage/metastore_bench_test.go
@@ -6,6 +6,8 @@
"io/ioutil"
"os"
"testing"
+
+ "github.com/containerd/containerd/snapshot"
)
// Benchmarks returns a benchmark suite using the provided metadata store
@@ -107,7 +109,7 @@
if _, err := CreateActive(ctx, "bottom", "", false); err != nil {
return err
}
- if _, err := CommitActive(ctx, "bottom", base); err != nil {
+ if _, err := CommitActive(ctx, "bottom", base, snapshot.Usage{}); err != nil {
return err
}
@@ -122,7 +124,7 @@
b.ResetTimer()
for i := 0; i < b.N; i++ {
- _, err := GetInfo(ctx, "active")
+ _, _, _, err := GetInfo(ctx, "active")
if err != nil {
b.Fatal(err)
}
@@ -133,13 +135,13 @@
if err := createActiveFromBase(ctx, ms, "active", "base"); err != nil {
b.Fatal(err)
}
- if _, err := CommitActive(ctx, "active", "committed"); err != nil {
+ if _, err := CommitActive(ctx, "active", "committed", snapshot.Usage{}); err != nil {
b.Fatal(err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
- _, err := GetInfo(ctx, "committed")
+ _, _, _, err := GetInfo(ctx, "committed")
if err != nil {
b.Fatal(err)
}
@@ -179,7 +181,7 @@
b.Fatal(err)
}
b.StartTimer()
- if _, err := CommitActive(ctx, "active", "committed"); err != nil {
+ if _, err := CommitActive(ctx, "active", "committed", snapshot.Usage{}); err != nil {
b.Fatal(err)
}
b.StopTimer()
@@ -196,7 +198,7 @@
b.Fatalf("create active failed: %+v", err)
}
base = fmt.Sprintf("base-%d", i)
- if _, err := CommitActive(ctx, "tmp", base); err != nil {
+ if _, err := CommitActive(ctx, "tmp", base, snapshot.Usage{}); err != nil {
b.Fatalf("commit failed: %+v", err)
}
diff --git a/snapshot/storage/metastore_test.go b/snapshot/storage/metastore_test.go
index f2e223f..d12d066 100644
--- a/snapshot/storage/metastore_test.go
+++ b/snapshot/storage/metastore_test.go
@@ -128,13 +128,13 @@
if _, err := CreateActive(ctx, "committed-tmp-1", "", false); err != nil {
return errors.Wrap(err, "failed to create active")
}
- if _, err := CommitActive(ctx, "committed-tmp-1", "committed-1"); err != nil {
+ if _, err := CommitActive(ctx, "committed-tmp-1", "committed-1", snapshot.Usage{Size: 1}); err != nil {
return errors.Wrap(err, "failed to create active")
}
if _, err := CreateActive(ctx, "committed-tmp-2", "committed-1", false); err != nil {
return errors.Wrap(err, "failed to create active")
}
- if _, err := CommitActive(ctx, "committed-tmp-2", "committed-2"); err != nil {
+ if _, err := CommitActive(ctx, "committed-tmp-2", "committed-2", snapshot.Usage{Size: 2}); err != nil {
return errors.Wrap(err, "failed to create active")
}
if _, err := CreateActive(ctx, "active-1", "", false); err != nil {
@@ -238,7 +238,7 @@
func testGetInfo(ctx context.Context, t *testing.T, ms *MetaStore) {
for key, expected := range baseInfo {
- info, err := GetInfo(ctx, key)
+ _, info, _, err := GetInfo(ctx, key)
if err != nil {
t.Fatalf("GetInfo on %v failed: %+v", key, err)
}
@@ -247,7 +247,7 @@
}
func testGetInfoNotExist(ctx context.Context, t *testing.T, ms *MetaStore) {
- _, err := GetInfo(ctx, "active-not-exist")
+ _, _, _, err := GetInfo(ctx, "active-not-exist")
assertNotExist(t, err)
}
@@ -272,7 +272,7 @@
if _, err := CreateActive(ctx, "committed-tmp-1", "", false); err != nil {
return errors.Wrap(err, "failed to create active")
}
- if _, err := CommitActive(ctx, "committed-tmp-1", "committed-1"); err != nil {
+ if _, err := CommitActive(ctx, "committed-tmp-1", "committed-1", snapshot.Usage{}); err != nil {
return errors.Wrap(err, "failed to create active")
}
@@ -350,7 +350,7 @@
t.Fatal("Expected readonly active")
}
- commitID, err := CommitActive(ctx, "active-1", "committed-1")
+ commitID, err := CommitActive(ctx, "active-1", "committed-1", snapshot.Usage{})
if err != nil {
t.Fatal(err)
}
@@ -425,7 +425,7 @@
t.Fatal("Expected writable active")
}
- commitID, err := CommitActive(ctx, "active-1", "committed-1")
+ commitID, err := CommitActive(ctx, "active-1", "committed-1", snapshot.Usage{})
if err != nil {
t.Fatal(err)
}
@@ -440,7 +440,7 @@
}
func testCommitNotExist(ctx context.Context, t *testing.T, ms *MetaStore) {
- _, err := CommitActive(ctx, "active-not-exist", "committed-1")
+ _, err := CommitActive(ctx, "active-not-exist", "committed-1", snapshot.Usage{})
assertNotExist(t, err)
}
@@ -448,7 +448,7 @@
if err := basePopulate(ctx, ms); err != nil {
t.Fatalf("Populate failed: %+v", err)
}
- _, err := CommitActive(ctx, "active-1", "committed-1")
+ _, err := CommitActive(ctx, "active-1", "committed-1", snapshot.Usage{})
assertExist(t, err)
}
@@ -456,7 +456,7 @@
if err := basePopulate(ctx, ms); err != nil {
t.Fatalf("Populate failed: %+v", err)
}
- _, err := CommitActive(ctx, "committed-1", "committed-3")
+ _, err := CommitActive(ctx, "committed-1", "committed-3", snapshot.Usage{})
assertNotActive(t, err)
}
@@ -464,7 +464,7 @@
if err := basePopulate(ctx, ms); err != nil {
t.Fatalf("Populate failed: %+v", err)
}
- _, err := CommitActive(ctx, "active-5", "committed-3")
+ _, err := CommitActive(ctx, "active-5", "committed-3", snapshot.Usage{})
if err == nil {
t.Fatal("Expected error committing readonly active")
}
@@ -476,7 +476,7 @@
t.Fatal(err)
}
- commitID, err := CommitActive(ctx, "active-1", "committed-1")
+ commitID, err := CommitActive(ctx, "active-1", "committed-1", snapshot.Usage{})
if err != nil {
t.Fatal(err)
}
diff --git a/snapshot/storage/proto/record.pb.go b/snapshot/storage/proto/record.pb.go
index 6b09269..7eb8d67 100644
--- a/snapshot/storage/proto/record.pb.go
+++ b/snapshot/storage/proto/record.pb.go
@@ -65,6 +65,15 @@
Parent string `protobuf:"bytes,2,opt,name=parent,proto3" json:"parent,omitempty"`
Kind Kind `protobuf:"varint,4,opt,name=kind,proto3,enum=containerd.v1.Kind" json:"kind,omitempty"`
Readonly bool `protobuf:"varint,5,opt,name=readonly,proto3" json:"readonly,omitempty"`
+ // inodes stores the number inodes in use for the snapshot.
+ //
+ // Only valid for committed snapshots.
+ Inodes int64 `protobuf:"varint,6,opt,name=inodes,proto3" json:"inodes,omitempty"`
+ // Size reports the disk used by the snapshot, excluding the parents.
+ //
+ // Only valid for committed snapshots, active snapshots must read the
+ // current usage from the disk.
+ Size_ int64 `protobuf:"varint,7,opt,name=size,proto3" json:"size,omitempty"`
}
func (m *Snapshot) Reset() { *m = Snapshot{} }
@@ -116,6 +125,16 @@
}
i++
}
+ if m.Inodes != 0 {
+ dAtA[i] = 0x30
+ i++
+ i = encodeVarintRecord(dAtA, i, uint64(m.Inodes))
+ }
+ if m.Size_ != 0 {
+ dAtA[i] = 0x38
+ i++
+ i = encodeVarintRecord(dAtA, i, uint64(m.Size_))
+ }
return i, nil
}
@@ -162,6 +181,12 @@
if m.Readonly {
n += 2
}
+ if m.Inodes != 0 {
+ n += 1 + sovRecord(uint64(m.Inodes))
+ }
+ if m.Size_ != 0 {
+ n += 1 + sovRecord(uint64(m.Size_))
+ }
return n
}
@@ -187,6 +212,8 @@
`Parent:` + fmt.Sprintf("%v", this.Parent) + `,`,
`Kind:` + fmt.Sprintf("%v", this.Kind) + `,`,
`Readonly:` + fmt.Sprintf("%v", this.Readonly) + `,`,
+ `Inodes:` + fmt.Sprintf("%v", this.Inodes) + `,`,
+ `Size_:` + fmt.Sprintf("%v", this.Size_) + `,`,
`}`,
}, "")
return s
@@ -315,6 +342,44 @@
}
}
m.Readonly = bool(v != 0)
+ case 6:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Inodes", wireType)
+ }
+ m.Inodes = 0
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowRecord
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ m.Inodes |= (int64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 7:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Size_", wireType)
+ }
+ m.Size_ = 0
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowRecord
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ m.Size_ |= (int64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
default:
iNdEx = preIndex
skippy, err := skipRecord(dAtA[iNdEx:])
@@ -446,24 +511,26 @@
}
var fileDescriptorRecord = []byte{
- // 304 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x72, 0x49, 0xcf, 0x2c, 0xc9,
- 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d,
- 0x4a, 0x41, 0x66, 0x16, 0xe7, 0x25, 0x16, 0x14, 0x67, 0xe4, 0x97, 0xe8, 0x17, 0x97, 0xe4, 0x17,
- 0x25, 0xa6, 0xa7, 0xea, 0x17, 0x14, 0xe5, 0x97, 0xe4, 0xeb, 0x17, 0xa5, 0x26, 0xe7, 0x17, 0xa5,
- 0xe8, 0x81, 0x39, 0x42, 0xbc, 0x08, 0xf5, 0x7a, 0x65, 0x86, 0x52, 0x22, 0xe9, 0xf9, 0xe9, 0xf9,
- 0x10, 0x65, 0x20, 0x16, 0x44, 0x91, 0x52, 0x3d, 0x17, 0x47, 0x30, 0xd4, 0x2c, 0x21, 0x31, 0x2e,
- 0xa6, 0xcc, 0x14, 0x09, 0x46, 0x05, 0x46, 0x0d, 0x16, 0x27, 0xb6, 0x47, 0xf7, 0xe4, 0x99, 0x3c,
- 0x5d, 0x82, 0x98, 0x32, 0x53, 0x84, 0xc4, 0xb8, 0xd8, 0x0a, 0x12, 0x8b, 0x52, 0xf3, 0x4a, 0x24,
- 0x98, 0x14, 0x18, 0x35, 0x38, 0x83, 0xa0, 0x3c, 0x21, 0x75, 0x2e, 0x96, 0xec, 0xcc, 0xbc, 0x14,
- 0x09, 0x16, 0x05, 0x46, 0x0d, 0x3e, 0x23, 0x61, 0x3d, 0x14, 0xfb, 0xf4, 0xbc, 0x33, 0xf3, 0x52,
- 0x82, 0xc0, 0x0a, 0x84, 0xa4, 0xb8, 0x38, 0x8a, 0x52, 0x13, 0x53, 0xf2, 0xf3, 0x72, 0x2a, 0x25,
- 0x58, 0x15, 0x18, 0x35, 0x38, 0x82, 0xe0, 0x7c, 0xad, 0x20, 0x2e, 0x16, 0x6f, 0x88, 0x1a, 0x36,
- 0x47, 0xe7, 0x10, 0xcf, 0x30, 0x57, 0x01, 0x06, 0x29, 0xbe, 0xae, 0xb9, 0x0a, 0x5c, 0x20, 0x51,
- 0xc7, 0xe4, 0x92, 0xcc, 0xb2, 0x54, 0x21, 0x05, 0x2e, 0x4e, 0x67, 0x7f, 0x5f, 0x5f, 0xcf, 0x90,
- 0x10, 0x57, 0x17, 0x01, 0x46, 0x29, 0xc1, 0xae, 0xb9, 0x0a, 0xbc, 0x20, 0x69, 0xe7, 0xfc, 0xdc,
- 0xdc, 0xcc, 0x92, 0x92, 0xd4, 0x14, 0x29, 0x9e, 0x8e, 0xc5, 0x72, 0x0c, 0xbb, 0x96, 0xc8, 0x81,
- 0xcd, 0x72, 0x92, 0x38, 0xf1, 0x50, 0x8e, 0xe1, 0xc6, 0x43, 0x39, 0x86, 0x86, 0x47, 0x72, 0x8c,
- 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0x63, 0x12, 0x1b, 0xd8,
- 0xd7, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xbb, 0x89, 0x2c, 0x37, 0x62, 0x01, 0x00, 0x00,
+ // 330 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x54, 0x90, 0xbd, 0x4e, 0xf3, 0x30,
+ 0x18, 0x85, 0xe3, 0x7c, 0xf9, 0x42, 0x6b, 0xd1, 0xaa, 0x18, 0x54, 0x59, 0x19, 0x8c, 0xc5, 0x42,
+ 0xc4, 0x90, 0x08, 0xb8, 0x82, 0xfe, 0x0d, 0x55, 0x55, 0x21, 0x85, 0x8a, 0x3d, 0x8d, 0xad, 0xd4,
+ 0x82, 0xda, 0x95, 0x63, 0x2a, 0xc1, 0xc4, 0x88, 0x7a, 0x0f, 0x9d, 0x60, 0xe3, 0x0e, 0xb8, 0x82,
+ 0x8e, 0x8c, 0x4c, 0x88, 0xe6, 0x4a, 0x50, 0xd2, 0x8a, 0x9f, 0xed, 0x9c, 0xe3, 0x47, 0x8f, 0xa5,
+ 0x17, 0x76, 0x53, 0x61, 0x26, 0xb7, 0xe3, 0x20, 0x51, 0xd3, 0x30, 0x51, 0xd2, 0xc4, 0x42, 0x72,
+ 0xcd, 0x7e, 0xc7, 0x4c, 0xc6, 0xb3, 0x6c, 0xa2, 0x4c, 0x98, 0x19, 0xa5, 0xe3, 0x94, 0x87, 0x33,
+ 0xad, 0x8c, 0x0a, 0x35, 0x4f, 0x94, 0x66, 0x41, 0x59, 0x50, 0xed, 0x87, 0x0f, 0xe6, 0xa7, 0xde,
+ 0x41, 0xaa, 0x52, 0xb5, 0xc1, 0x8a, 0xb4, 0x81, 0x8e, 0x5e, 0x00, 0xac, 0x5c, 0x6e, 0x65, 0xa8,
+ 0x09, 0x6d, 0xc1, 0x30, 0xa0, 0xc0, 0x77, 0xda, 0x6e, 0xfe, 0x71, 0x68, 0xf7, 0xbb, 0x91, 0x2d,
+ 0x18, 0x6a, 0x42, 0x77, 0x16, 0x6b, 0x2e, 0x0d, 0xb6, 0x29, 0xf0, 0xab, 0xd1, 0xb6, 0xa1, 0x63,
+ 0xe8, 0x5c, 0x0b, 0xc9, 0xb0, 0x43, 0x81, 0x5f, 0x3f, 0xdb, 0x0f, 0xfe, 0x7c, 0x18, 0x0c, 0x84,
+ 0x64, 0x51, 0x09, 0x20, 0x0f, 0x56, 0x34, 0x8f, 0x99, 0x92, 0x37, 0x77, 0xf8, 0x3f, 0x05, 0x7e,
+ 0x25, 0xfa, 0xee, 0x85, 0x5c, 0x48, 0xc5, 0x78, 0x86, 0x5d, 0x0a, 0xfc, 0x7f, 0xd1, 0xb6, 0x21,
+ 0x04, 0x9d, 0x4c, 0xdc, 0x73, 0xbc, 0x53, 0xae, 0x65, 0x3e, 0x89, 0xa0, 0x33, 0xd8, 0xf8, 0xdc,
+ 0x56, 0x67, 0xd4, 0xbf, 0xea, 0x35, 0x2c, 0xaf, 0xbe, 0x58, 0x52, 0x58, 0xac, 0xad, 0xc4, 0x88,
+ 0x39, 0x47, 0x14, 0x56, 0x3b, 0x17, 0xc3, 0x61, 0x7f, 0x34, 0xea, 0x75, 0x1b, 0xc0, 0xdb, 0x5b,
+ 0x2c, 0x69, 0xad, 0x78, 0xee, 0xa8, 0xe9, 0x54, 0x18, 0xc3, 0x99, 0xb7, 0xfb, 0xf8, 0x44, 0xac,
+ 0xd7, 0x67, 0x52, 0xba, 0xda, 0x78, 0xb5, 0x26, 0xd6, 0xfb, 0x9a, 0x58, 0x0f, 0x39, 0x01, 0xab,
+ 0x9c, 0x80, 0xb7, 0x9c, 0x80, 0xcf, 0x9c, 0x80, 0xb1, 0x5b, 0x9e, 0xe8, 0xfc, 0x2b, 0x00, 0x00,
+ 0xff, 0xff, 0xce, 0x7e, 0x6f, 0x85, 0x8f, 0x01, 0x00, 0x00,
}
diff --git a/snapshot/storage/proto/record.proto b/snapshot/storage/proto/record.proto
index 50fcfe6..64eb36b 100644
--- a/snapshot/storage/proto/record.proto
+++ b/snapshot/storage/proto/record.proto
@@ -23,4 +23,15 @@
string parent = 2;
Kind kind = 4;
bool readonly = 5;
+
+ // inodes stores the number inodes in use for the snapshot.
+ //
+ // Only valid for committed snapshots.
+ int64 inodes = 6;
+
+ // Size reports the disk used by the snapshot, excluding the parents.
+ //
+ // Only valid for committed snapshots, active snapshots must read the
+ // current usage from the disk.
+ int64 size = 7;
}
diff --git a/snapshot/windows/windows.go b/snapshot/windows/windows.go
index b45e223..8294125 100644
--- a/snapshot/windows/windows.go
+++ b/snapshot/windows/windows.go
@@ -44,6 +44,10 @@
panic("not implemented")
}
+func (o *Snapshotter) Usage(ctx context.Context, key string) (snapshot.Usage, error) {
+ panic("not implemented")
+}
+
func (o *Snapshotter) Prepare(ctx context.Context, key, parent string) ([]containerd.Mount, error) {
panic("not implemented")
}
diff --git a/sys/socket_unix.go b/sys/socket_unix.go
index e5f2ba6..cd361c2 100644
--- a/sys/socket_unix.go
+++ b/sys/socket_unix.go
@@ -28,6 +28,11 @@
return l, err
}
+ if err := os.Chmod(path, 0660); err != nil {
+ l.Close()
+ return nil, err
+ }
+
if err := os.Chown(path, uid, gid); err != nil {
l.Close()
return nil, err
diff --git a/vendor.conf b/vendor.conf
index f3e4eb5..c3725b6 100644
--- a/vendor.conf
+++ b/vendor.conf
@@ -1,7 +1,9 @@
+github.com/coreos/go-systemd 48702e0da86bd25e76cfef347e2adeb434a0d0a6
github.com/crosbymichael/go-runc 65847bfc51952703ca24b564d10de50d3f2db6e7
-github.com/crosbymichael/console f13f890e20a94bdec6c328cdf9410b7158f0cfa4
+github.com/crosbymichael/console 2a5cbd32a84cd1268c20c69bd090ec49e37009f8
github.com/crosbymichael/cgroups e950a27f3faf567abbf995bfbec90eaddc766d25
github.com/docker/go-metrics 8fd5772bf1584597834c6f7961a530f06cbfbb87
+github.com/godbus/dbus c7fdd8b5cd55e87b4e1f4e372cdb1db61dd6c66f
github.com/prometheus/client_golang v0.8.0
github.com/prometheus/client_model fa8ad6fec33561be4280a8f0514318c79d7f6cb6
github.com/prometheus/common 195bde7883f7c39ea62b0d92ab7359b5327065cb
@@ -14,24 +16,24 @@
github.com/opencontainers/runc 50401b5b4c2e01e4f1372b73a021742deeaf4e2d
github.com/opencontainers/runtime-spec 035da1dca3dfbb00d752eb58b0b158d6129f3776
github.com/Sirupsen/logrus v0.11.0
-github.com/stevvooe/go-btrfs ea304655a3ed8f00773db1844f921d12541ee0d1
+github.com/containerd/btrfs e9c546f46bccffefe71a6bc137e4c21b5503cc18
github.com/stretchr/testify v1.1.4
github.com/davecgh/go-spew v1.1.0
github.com/pmezard/go-difflib v1.0.0
-github.com/tonistiigi/fifo fe870ccf293940774c2b44e23f6c71fff8f7547d
+github.com/tonistiigi/fifo f071cd4a2739654fec4e768a14efd9332b3e8af1
github.com/urfave/cli 8ba6f23b6e36d03666a14bd9421f5e3efcb59aca
golang.org/x/net 8b4af36cd21a1f85a7484b49feb7c79363106d8e
google.golang.org/grpc v1.0.5
github.com/pkg/errors v0.8.0
github.com/nightlyone/lockfile 1d49c987357a327b5b03aa84cbddd582c328615d
github.com/opencontainers/go-digest 21dfd564fd89c944783d00d069f33e3e7123c448
-golang.org/x/sys/unix f3918c30c5c2cb527c0b071a27c35120a6c0719a
+golang.org/x/sys f3918c30c5c2cb527c0b071a27c35120a6c0719a
github.com/opencontainers/image-spec a431dbcf6a74fca2e0e040b819a836dbe3fb23ca
-github.com/stevvooe/continuity 577e137350afb00343495f55bb8671fe7e22b0bf
+github.com/containerd/continuity 6414d06cab9e2fe082ea29ff42aab627e740d00c
golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c
-github.com/BurntSushi/toml v0.2.0-21-g9906417
+github.com/BurntSushi/toml v0.2.0-21-g9906417
github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0
github.com/Microsoft/go-winio fff283ad5116362ca252298cfc9b95828956d85d
github.com/boltdb/bolt e9cf4fae01b5a8ff89d0ec6b32f0d9c9f79aefdd
github.com/Microsoft/hcsshim v0.5.15
-github.com/Azure/go-ansiterm/winterm fa152c58bc15761d0200cb75fe958b89a9d4888e
+github.com/Azure/go-ansiterm fa152c58bc15761d0200cb75fe958b89a9d4888e
diff --git a/vendor/github.com/stevvooe/go-btrfs/LICENSE b/vendor/github.com/containerd/btrfs/LICENSE
similarity index 100%
rename from vendor/github.com/stevvooe/go-btrfs/LICENSE
rename to vendor/github.com/containerd/btrfs/LICENSE
diff --git a/vendor/github.com/stevvooe/go-btrfs/README.md b/vendor/github.com/containerd/btrfs/README.md
similarity index 67%
rename from vendor/github.com/stevvooe/go-btrfs/README.md
rename to vendor/github.com/containerd/btrfs/README.md
index 3e3d71d..4d0f273 100644
--- a/vendor/github.com/stevvooe/go-btrfs/README.md
+++ b/vendor/github.com/containerd/btrfs/README.md
@@ -1,5 +1,5 @@
# go-btrfs
-[](https://godoc.org/github.com/stevvooe/go-btrfs) [](https://travis-ci.org/stevvooe/go-btrfs)
+[](https://godoc.org/github.com/containerd/btrfs) [](https://travis-ci.org/stevvooe/go-btrfs)
Native Go bindings for btrfs.
diff --git a/vendor/github.com/stevvooe/go-btrfs/btrfs.c b/vendor/github.com/containerd/btrfs/btrfs.c
similarity index 100%
rename from vendor/github.com/stevvooe/go-btrfs/btrfs.c
rename to vendor/github.com/containerd/btrfs/btrfs.c
diff --git a/vendor/github.com/stevvooe/go-btrfs/btrfs.go b/vendor/github.com/containerd/btrfs/btrfs.go
similarity index 100%
rename from vendor/github.com/stevvooe/go-btrfs/btrfs.go
rename to vendor/github.com/containerd/btrfs/btrfs.go
diff --git a/vendor/github.com/stevvooe/go-btrfs/btrfs.h b/vendor/github.com/containerd/btrfs/btrfs.h
similarity index 100%
rename from vendor/github.com/stevvooe/go-btrfs/btrfs.h
rename to vendor/github.com/containerd/btrfs/btrfs.h
diff --git a/vendor/github.com/stevvooe/go-btrfs/helpers.go b/vendor/github.com/containerd/btrfs/helpers.go
similarity index 100%
rename from vendor/github.com/stevvooe/go-btrfs/helpers.go
rename to vendor/github.com/containerd/btrfs/helpers.go
diff --git a/vendor/github.com/stevvooe/go-btrfs/info.go b/vendor/github.com/containerd/btrfs/info.go
similarity index 100%
rename from vendor/github.com/stevvooe/go-btrfs/info.go
rename to vendor/github.com/containerd/btrfs/info.go
diff --git a/vendor/github.com/stevvooe/go-btrfs/ioctl.go b/vendor/github.com/containerd/btrfs/ioctl.go
similarity index 100%
rename from vendor/github.com/stevvooe/go-btrfs/ioctl.go
rename to vendor/github.com/containerd/btrfs/ioctl.go
diff --git a/vendor/github.com/stevvooe/continuity/LICENSE b/vendor/github.com/containerd/continuity/LICENSE
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/LICENSE
rename to vendor/github.com/containerd/continuity/LICENSE
diff --git a/vendor/github.com/stevvooe/continuity/README.md b/vendor/github.com/containerd/continuity/README.md
similarity index 61%
rename from vendor/github.com/stevvooe/continuity/README.md
rename to vendor/github.com/containerd/continuity/README.md
index 12d6553..50b64c2 100644
--- a/vendor/github.com/stevvooe/continuity/README.md
+++ b/vendor/github.com/containerd/continuity/README.md
@@ -1,5 +1,8 @@
# continuity
+[](https://godoc.org/github.com/containerd/continuity)
+[](https://travis-ci.org/containerd/continuity)
+
A transport-agnostic, filesystem metadata manifest system
This project is a staging area for experiments in providing transport agnostic
diff --git a/vendor/github.com/stevvooe/continuity/context.go b/vendor/github.com/containerd/continuity/context.go
similarity index 95%
rename from vendor/github.com/stevvooe/continuity/context.go
rename to vendor/github.com/containerd/continuity/context.go
index b897173..9e72fd0 100644
--- a/vendor/github.com/stevvooe/continuity/context.go
+++ b/vendor/github.com/containerd/continuity/context.go
@@ -8,7 +8,6 @@
"os"
"path/filepath"
"strings"
- "syscall"
"github.com/opencontainers/go-digest"
)
@@ -115,19 +114,7 @@
}
}
- // TODO(stevvooe): This need to be resolved for the container's root,
- // where here we are really getting the host OS's value. We need to allow
- // this be passed in and fixed up to make these uid/gid mappings portable.
- // Either this can be part of the driver or we can achieve it through some
- // other mechanism.
- sys, ok := fi.Sys().(*syscall.Stat_t)
- if !ok {
- // TODO(stevvooe): This may not be a hard error for all platforms. We
- // may want to move this to the driver.
- return nil, fmt.Errorf("unable to resolve syscall.Stat_t from (os.FileInfo).Sys(): %#v", fi)
- }
-
- base, err := newBaseResource(p, fi.Mode(), fmt.Sprint(sys.Uid), fmt.Sprint(sys.Gid))
+ base, err := newBaseResource(p, fi)
if err != nil {
return nil, err
}
@@ -463,8 +450,10 @@
}
if target != r.Target() {
- if err := c.driver.Remove(fp); err != nil { // RemoveAll?
- return err
+ if fi != nil {
+ if err := c.driver.Remove(fp); err != nil { // RemoveAll in case of directory?
+ return err
+ }
}
if err := c.driver.Symlink(r.Target(), fp); err != nil {
diff --git a/vendor/github.com/stevvooe/continuity/devices_darwin.go b/vendor/github.com/containerd/continuity/devices_darwin.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/devices_darwin.go
rename to vendor/github.com/containerd/continuity/devices_darwin.go
diff --git a/vendor/github.com/stevvooe/continuity/devices_linux.go b/vendor/github.com/containerd/continuity/devices_linux.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/devices_linux.go
rename to vendor/github.com/containerd/continuity/devices_linux.go
diff --git a/vendor/github.com/stevvooe/continuity/devices_unix.go b/vendor/github.com/containerd/continuity/devices_unix.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/devices_unix.go
rename to vendor/github.com/containerd/continuity/devices_unix.go
diff --git a/vendor/github.com/containerd/continuity/devices_windows.go b/vendor/github.com/containerd/continuity/devices_windows.go
new file mode 100644
index 0000000..3e7753a
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/devices_windows.go
@@ -0,0 +1,11 @@
+package continuity
+
+import (
+ "os"
+
+ "github.com/pkg/errors"
+)
+
+func deviceInfo(fi os.FileInfo) (uint64, uint64, error) {
+ return 0, 0, errors.Wrap(ErrNotSupported, "cannot get device info on windows")
+}
diff --git a/vendor/github.com/stevvooe/continuity/digests.go b/vendor/github.com/containerd/continuity/digests.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/digests.go
rename to vendor/github.com/containerd/continuity/digests.go
diff --git a/vendor/github.com/stevvooe/continuity/driver.go b/vendor/github.com/containerd/continuity/driver.go
similarity index 91%
rename from vendor/github.com/stevvooe/continuity/driver.go
rename to vendor/github.com/containerd/continuity/driver.go
index 6af7f02..ae06615 100644
--- a/vendor/github.com/stevvooe/continuity/driver.go
+++ b/vendor/github.com/containerd/continuity/driver.go
@@ -1,7 +1,6 @@
package continuity
import (
- "errors"
"os"
"strconv"
)
@@ -143,16 +142,3 @@
func (d *driver) Symlink(oldname, newname string) error {
return os.Symlink(oldname, newname)
}
-
-func (d *driver) Mknod(path string, mode os.FileMode, major, minor int) error {
- return mknod(path, mode, major, minor)
-}
-
-func (d *driver) Mkfifo(path string, mode os.FileMode) error {
- if mode&os.ModeNamedPipe == 0 {
- return errors.New("mode passed to Mkfifo does not have the named pipe bit set")
- }
- // mknod with a mode that has ModeNamedPipe set creates a fifo, not a
- // device.
- return mknod(path, mode, 0, 0)
-}
diff --git a/vendor/github.com/stevvooe/continuity/driver_darwin.go b/vendor/github.com/containerd/continuity/driver_darwin.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/driver_darwin.go
rename to vendor/github.com/containerd/continuity/driver_darwin.go
diff --git a/vendor/github.com/stevvooe/continuity/driver_unix.go b/vendor/github.com/containerd/continuity/driver_unix.go
similarity index 86%
rename from vendor/github.com/stevvooe/continuity/driver_unix.go
rename to vendor/github.com/containerd/continuity/driver_unix.go
index 337ab12..7336a87 100644
--- a/vendor/github.com/stevvooe/continuity/driver_unix.go
+++ b/vendor/github.com/containerd/continuity/driver_unix.go
@@ -3,14 +3,28 @@
package continuity
import (
+ "errors"
"fmt"
"os"
"path/filepath"
"sort"
- "github.com/stevvooe/continuity/sysx"
+ "github.com/containerd/continuity/sysx"
)
+func (d *driver) Mknod(path string, mode os.FileMode, major, minor int) error {
+ return mknod(path, mode, major, minor)
+}
+
+func (d *driver) Mkfifo(path string, mode os.FileMode) error {
+ if mode&os.ModeNamedPipe == 0 {
+ return errors.New("mode passed to Mkfifo does not have the named pipe bit set")
+ }
+ // mknod with a mode that has ModeNamedPipe set creates a fifo, not a
+ // device.
+ return mknod(path, mode, 0, 0)
+}
+
// Lchmod changes the mode of an file not following symlinks.
func (d *driver) Lchmod(path string, mode os.FileMode) (err error) {
if !filepath.IsAbs(path) {
diff --git a/vendor/github.com/containerd/continuity/driver_windows.go b/vendor/github.com/containerd/continuity/driver_windows.go
new file mode 100644
index 0000000..a2cda20
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/driver_windows.go
@@ -0,0 +1,21 @@
+package continuity
+
+import (
+ "os"
+
+ "github.com/pkg/errors"
+)
+
+func (d *driver) Mknod(path string, mode os.FileMode, major, minor int) error {
+ return errors.Wrap(ErrNotSupported, "cannot create device node on Windows")
+}
+
+func (d *driver) Mkfifo(path string, mode os.FileMode) error {
+ return errors.Wrap(ErrNotSupported, "cannot create fifo on Windows")
+}
+
+// Lchmod changes the mode of an file not following symlinks.
+func (d *driver) Lchmod(path string, mode os.FileMode) (err error) {
+ // TODO: Use Window's equivalent
+ return os.Chmod(path, mode)
+}
diff --git a/vendor/github.com/stevvooe/continuity/groups_unix.go b/vendor/github.com/containerd/continuity/groups_unix.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/groups_unix.go
rename to vendor/github.com/containerd/continuity/groups_unix.go
diff --git a/vendor/github.com/stevvooe/continuity/hardlinks.go b/vendor/github.com/containerd/continuity/hardlinks.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/hardlinks.go
rename to vendor/github.com/containerd/continuity/hardlinks.go
diff --git a/vendor/github.com/stevvooe/continuity/hardlinks_unix.go b/vendor/github.com/containerd/continuity/hardlinks_unix.go
similarity index 97%
rename from vendor/github.com/stevvooe/continuity/hardlinks_unix.go
rename to vendor/github.com/containerd/continuity/hardlinks_unix.go
index 5880f06..3a3c36e 100644
--- a/vendor/github.com/stevvooe/continuity/hardlinks_unix.go
+++ b/vendor/github.com/containerd/continuity/hardlinks_unix.go
@@ -1,3 +1,5 @@
+// +build linux darwin
+
package continuity
import (
diff --git a/vendor/github.com/containerd/continuity/hardlinks_windows.go b/vendor/github.com/containerd/continuity/hardlinks_windows.go
new file mode 100644
index 0000000..be516c5
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/hardlinks_windows.go
@@ -0,0 +1,12 @@
+package continuity
+
+import "os"
+
+type hardlinkKey struct{}
+
+func newHardlinkKey(fi os.FileInfo) (hardlinkKey, error) {
+ // NOTE(stevvooe): Obviously, this is not yet implemented. However, the
+ // makings of an implementation are available in src/os/types_windows.go. More
+ // investigation needs to be done to figure out exactly how to do this.
+ return hardlinkKey{}, errNotAHardLink
+}
diff --git a/vendor/github.com/stevvooe/continuity/ioutils.go b/vendor/github.com/containerd/continuity/ioutils.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/ioutils.go
rename to vendor/github.com/containerd/continuity/ioutils.go
diff --git a/vendor/github.com/stevvooe/continuity/manifest.go b/vendor/github.com/containerd/continuity/manifest.go
similarity index 98%
rename from vendor/github.com/stevvooe/continuity/manifest.go
rename to vendor/github.com/containerd/continuity/manifest.go
index 4315bcb..36e9210 100644
--- a/vendor/github.com/stevvooe/continuity/manifest.go
+++ b/vendor/github.com/containerd/continuity/manifest.go
@@ -8,7 +8,7 @@
"sort"
"github.com/golang/protobuf/proto"
- pb "github.com/stevvooe/continuity/proto"
+ pb "github.com/containerd/continuity/proto"
)
// Manifest provides the contents of a manifest. Users of this struct should
diff --git a/vendor/github.com/stevvooe/continuity/manifest_test_darwin.go b/vendor/github.com/containerd/continuity/manifest_test_darwin.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/manifest_test_darwin.go
rename to vendor/github.com/containerd/continuity/manifest_test_darwin.go
diff --git a/vendor/github.com/stevvooe/continuity/proto/gen.go b/vendor/github.com/containerd/continuity/proto/gen.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/proto/gen.go
rename to vendor/github.com/containerd/continuity/proto/gen.go
diff --git a/vendor/github.com/containerd/continuity/proto/manifest.pb.go b/vendor/github.com/containerd/continuity/proto/manifest.pb.go
new file mode 100644
index 0000000..151b758
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/proto/manifest.pb.go
@@ -0,0 +1,181 @@
+// Code generated by protoc-gen-go.
+// source: manifest.proto
+// DO NOT EDIT!
+
+/*
+Package proto is a generated protocol buffer package.
+
+It is generated from these files:
+ manifest.proto
+
+It has these top-level messages:
+ Manifest
+ Resource
+ XAttr
+ ADSEntry
+*/
+package proto
+
+import proto1 "github.com/golang/protobuf/proto"
+import fmt "fmt"
+import math "math"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto1.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto1.ProtoPackageIsVersion2 // please upgrade the proto package
+
+// Manifest specifies the entries in a container bundle, keyed and sorted by
+// path.
+type Manifest struct {
+ Resource []*Resource `protobuf:"bytes,1,rep,name=resource" json:"resource,omitempty"`
+}
+
+func (m *Manifest) Reset() { *m = Manifest{} }
+func (m *Manifest) String() string { return proto1.CompactTextString(m) }
+func (*Manifest) ProtoMessage() {}
+func (*Manifest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+
+func (m *Manifest) GetResource() []*Resource {
+ if m != nil {
+ return m.Resource
+ }
+ return nil
+}
+
+type Resource struct {
+ // Path specifies the path from the bundle root. If more than one
+ // path is present, the entry may represent a hardlink, rather than using
+ // a link target. The path format is operating system specific.
+ Path []string `protobuf:"bytes,1,rep,name=path" json:"path,omitempty"`
+ // Uid specifies the user id for the resource. A string type is used for
+ // compatibility across different OS.
+ Uid string `protobuf:"bytes,2,opt,name=uid" json:"uid,omitempty"`
+ // Gid specifies the group id for the resource. A string type is used for
+ // compatibility across different OS.
+ Gid string `protobuf:"bytes,3,opt,name=gid" json:"gid,omitempty"`
+ // user and group are not currently used but their field numbers have been
+ // reserved for future use. As such, they are marked as deprecated.
+ User string `protobuf:"bytes,4,opt,name=user" json:"user,omitempty"`
+ Group string `protobuf:"bytes,5,opt,name=group" json:"group,omitempty"`
+ // Mode defines the file mode and permissions. We've used the same
+ // bit-packing from Go's os package,
+ // http://golang.org/pkg/os/#FileMode, since they've done the work of
+ // creating a cross-platform layout.
+ Mode uint32 `protobuf:"varint,6,opt,name=mode" json:"mode,omitempty"`
+ // Size specifies the size in bytes of the resource. This is only valid
+ // for regular files.
+ Size uint64 `protobuf:"varint,7,opt,name=size" json:"size,omitempty"`
+ // Digest specifies the content digest of the target file. Only valid for
+ // regular files. The strings are formatted as <alg>:<digest hex bytes>.
+ // The digests are sorted in lexical order and implementations may choose
+ // which algorithms they prefer.
+ Digest []string `protobuf:"bytes,8,rep,name=digest" json:"digest,omitempty"`
+ // Target defines the target of a hard or soft link. Absolute links start
+ // with a slash and specify the resource relative to the bundle root.
+ // Relative links do not start with a slash and are relative to the
+ // resource path.
+ Target string `protobuf:"bytes,9,opt,name=target" json:"target,omitempty"`
+ // Major specifies the major device number for charactor and block devices.
+ Major uint64 `protobuf:"varint,10,opt,name=major" json:"major,omitempty"`
+ // Minor specifies the minor device number for charactor and block devices.
+ Minor uint64 `protobuf:"varint,11,opt,name=minor" json:"minor,omitempty"`
+ // Xattr provides storage for extended attributes for the target resource.
+ Xattr []*XAttr `protobuf:"bytes,12,rep,name=xattr" json:"xattr,omitempty"`
+ // Ads stores one or more alternate data streams for the target resource.
+ Ads []*ADSEntry `protobuf:"bytes,13,rep,name=ads" json:"ads,omitempty"`
+}
+
+func (m *Resource) Reset() { *m = Resource{} }
+func (m *Resource) String() string { return proto1.CompactTextString(m) }
+func (*Resource) ProtoMessage() {}
+func (*Resource) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
+
+func (m *Resource) GetXattr() []*XAttr {
+ if m != nil {
+ return m.Xattr
+ }
+ return nil
+}
+
+func (m *Resource) GetAds() []*ADSEntry {
+ if m != nil {
+ return m.Ads
+ }
+ return nil
+}
+
+// XAttr encodes extended attributes for a resource.
+type XAttr struct {
+ // Name specifies the attribute name.
+ Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+ // Data specifies the associated data for the attribute.
+ Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
+}
+
+func (m *XAttr) Reset() { *m = XAttr{} }
+func (m *XAttr) String() string { return proto1.CompactTextString(m) }
+func (*XAttr) ProtoMessage() {}
+func (*XAttr) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
+
+// ADSEntry encodes information for a Windows Alternate Data Stream.
+type ADSEntry struct {
+ // Name specifices the stream name.
+ Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+ // Data specifies the stream data.
+ // See also the description about the digest below.
+ Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
+ // Digest is a CAS representation of the stream data.
+ //
+ // At least one of data or digest MUST be specified, and either one of them
+ // SHOULD be specified.
+ //
+ // How to access the actual data using the digest is implementation-specific,
+ // and implementations can choose not to implement digest.
+ // So, digest SHOULD be used only when the stream data is large.
+ Digest string `protobuf:"bytes,3,opt,name=digest" json:"digest,omitempty"`
+}
+
+func (m *ADSEntry) Reset() { *m = ADSEntry{} }
+func (m *ADSEntry) String() string { return proto1.CompactTextString(m) }
+func (*ADSEntry) ProtoMessage() {}
+func (*ADSEntry) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
+
+func init() {
+ proto1.RegisterType((*Manifest)(nil), "proto.Manifest")
+ proto1.RegisterType((*Resource)(nil), "proto.Resource")
+ proto1.RegisterType((*XAttr)(nil), "proto.XAttr")
+ proto1.RegisterType((*ADSEntry)(nil), "proto.ADSEntry")
+}
+
+func init() { proto1.RegisterFile("manifest.proto", fileDescriptor0) }
+
+var fileDescriptor0 = []byte{
+ // 313 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x90, 0x41, 0x4b, 0xfb, 0x40,
+ 0x10, 0xc5, 0x49, 0x93, 0xf4, 0x9f, 0x4e, 0xdb, 0xbf, 0xb2, 0x48, 0x99, 0x63, 0xcc, 0x29, 0x20,
+ 0x54, 0xd0, 0x83, 0xe7, 0x8a, 0x5e, 0x04, 0x2f, 0xeb, 0xc5, 0xeb, 0xea, 0xae, 0x71, 0x85, 0x64,
+ 0xc3, 0x66, 0x03, 0xea, 0x97, 0xf3, 0xab, 0xc9, 0x4c, 0xb6, 0x45, 0x6f, 0x9e, 0xf2, 0xde, 0x6f,
+ 0x76, 0x26, 0x8f, 0x07, 0xff, 0x5b, 0xd5, 0xd9, 0x17, 0x33, 0x84, 0x6d, 0xef, 0x5d, 0x70, 0x22,
+ 0xe7, 0x4f, 0x75, 0x05, 0xc5, 0x7d, 0x1c, 0x88, 0x33, 0x28, 0xbc, 0x19, 0xdc, 0xe8, 0x9f, 0x0d,
+ 0x26, 0x65, 0x5a, 0x2f, 0x2f, 0x8e, 0xa6, 0xc7, 0x5b, 0x19, 0xb1, 0x3c, 0x3c, 0xa8, 0xbe, 0x66,
+ 0x50, 0xec, 0xb1, 0x10, 0x90, 0xf5, 0x2a, 0xbc, 0xf2, 0xd6, 0x42, 0xb2, 0x16, 0xc7, 0x90, 0x8e,
+ 0x56, 0xe3, 0xac, 0x4c, 0xea, 0x85, 0x24, 0x49, 0xa4, 0xb1, 0x1a, 0xd3, 0x89, 0x34, 0x56, 0x8b,
+ 0x0d, 0x64, 0xe3, 0x60, 0x3c, 0x66, 0x84, 0xae, 0x67, 0x98, 0x48, 0xf6, 0x02, 0x21, 0x6f, 0xbc,
+ 0x1b, 0x7b, 0xcc, 0x0f, 0x83, 0x09, 0xd0, 0x9f, 0x5a, 0xa7, 0x0d, 0xce, 0xcb, 0xa4, 0x5e, 0x4b,
+ 0xd6, 0xc4, 0x06, 0xfb, 0x69, 0xf0, 0x5f, 0x99, 0xd4, 0x99, 0x64, 0x2d, 0x36, 0x30, 0xd7, 0xb6,
+ 0x31, 0x43, 0xc0, 0x82, 0x33, 0x45, 0x47, 0x3c, 0x28, 0xdf, 0x98, 0x80, 0x0b, 0x8e, 0x11, 0x9d,
+ 0x38, 0x81, 0xbc, 0x55, 0x6f, 0xce, 0x23, 0xf0, 0x91, 0xc9, 0x30, 0xb5, 0x9d, 0xf3, 0xb8, 0x8c,
+ 0x94, 0x8c, 0xa8, 0x20, 0x7f, 0x57, 0x21, 0x78, 0x5c, 0x71, 0x49, 0xab, 0x58, 0xd2, 0xe3, 0x2e,
+ 0x04, 0x2f, 0xa7, 0x91, 0x38, 0x85, 0x54, 0xe9, 0x01, 0xd7, 0xbf, 0x6a, 0xdc, 0xdd, 0x3c, 0xdc,
+ 0x76, 0xc1, 0x7f, 0x48, 0x9a, 0x55, 0xe7, 0x90, 0xf3, 0x0a, 0xe5, 0xef, 0x54, 0x4b, 0x9d, 0x53,
+ 0x22, 0xd6, 0xc4, 0xb4, 0x0a, 0x8a, 0xeb, 0x5b, 0x49, 0xd6, 0xd5, 0x1d, 0x14, 0xfb, 0x0b, 0x7f,
+ 0xdd, 0xf9, 0xd1, 0xc3, 0x54, 0x7b, 0x74, 0x4f, 0x73, 0x4e, 0x74, 0xf9, 0x1d, 0x00, 0x00, 0xff,
+ 0xff, 0x91, 0xfe, 0x64, 0xca, 0x17, 0x02, 0x00, 0x00,
+}
diff --git a/vendor/github.com/stevvooe/continuity/proto/manifest.proto b/vendor/github.com/containerd/continuity/proto/manifest.proto
similarity index 71%
rename from vendor/github.com/stevvooe/continuity/proto/manifest.proto
rename to vendor/github.com/containerd/continuity/proto/manifest.proto
index f7f5d3e..f7fd9aa 100644
--- a/vendor/github.com/stevvooe/continuity/proto/manifest.proto
+++ b/vendor/github.com/containerd/continuity/proto/manifest.proto
@@ -59,19 +59,39 @@
// Minor specifies the minor device number for charactor and block devices.
uint64 minor = 11;
- // TODO(stevvooe): The use of maps here may be problematic for
- // deterministic generation. Check out this comment:
- // https://developers.google.com/protocol-buffers/docs/proto3#backwards-compatibility
- // Fortunately, the Go implementation correctly sorts the map keys to
- // ensure deterministic generation, but this is not guaranteed for all
- // implementations. If this is problem, we should generate that schema and
- // sort by key. We can do this at any time and retain backwards
- // compatibility.
-
// Xattr provides storage for extended attributes for the target resource.
- map<string, bytes> xattr = 12;
+ repeated XAttr xattr = 12;
// Ads stores one or more alternate data streams for the target resource.
- map<string, bytes> ads = 13;
+ repeated ADSEntry ads = 13;
}
+
+// XAttr encodes extended attributes for a resource.
+message XAttr {
+ // Name specifies the attribute name.
+ string name = 1;
+
+ // Data specifies the associated data for the attribute.
+ bytes data = 2;
+}
+
+// ADSEntry encodes information for a Windows Alternate Data Stream.
+message ADSEntry {
+ // Name specifices the stream name.
+ string name = 1;
+
+ // Data specifies the stream data.
+ // See also the description about the digest below.
+ bytes data = 2;
+
+ // Digest is a CAS representation of the stream data.
+ //
+ // At least one of data or digest MUST be specified, and either one of them
+ // SHOULD be specified.
+ //
+ // How to access the actual data using the digest is implementation-specific,
+ // and implementations can choose not to implement digest.
+ // So, digest SHOULD be used only when the stream data is large.
+ string digest = 3;
+}
diff --git a/vendor/github.com/stevvooe/continuity/resource.go b/vendor/github.com/containerd/continuity/resource.go
similarity index 93%
rename from vendor/github.com/stevvooe/continuity/resource.go
rename to vendor/github.com/containerd/continuity/resource.go
index 3ba5431..60abe19 100644
--- a/vendor/github.com/stevvooe/continuity/resource.go
+++ b/vendor/github.com/containerd/continuity/resource.go
@@ -8,7 +8,7 @@
"sort"
"github.com/opencontainers/go-digest"
- pb "github.com/stevvooe/continuity/proto"
+ pb "github.com/containerd/continuity/proto"
)
// TODO(stevvooe): A record based model, somewhat sketched out at the bottom
@@ -111,7 +111,7 @@
if xattrer, ok := f.(XAttrer); ok {
fxattrs := xattrer.XAttrs()
if !reflect.DeepEqual(fxattrs, xattrs) {
- return nil, fmt.Errorf("resource %q xattrs do not match: %v != %v", fxattrs, xattrs)
+ return nil, fmt.Errorf("resource %q xattrs do not match: %v != %v", f, fxattrs, xattrs)
}
}
@@ -246,22 +246,6 @@
var _ Resource = &resource{}
-// newBaseResource returns a *resource, populated with data from p and fi,
-// where p will be populated directly.
-func newBaseResource(p string, mode os.FileMode, uid, gid string) (*resource, error) {
- return &resource{
- paths: []string{p},
- mode: mode,
-
- uid: uid,
- gid: gid,
-
- // NOTE(stevvooe): Population of shared xattrs field is deferred to
- // the resource types that populate it. Since they are a property of
- // the context, they must set there.
- }, nil
-}
-
func (r *resource) Path() string {
if len(r.paths) < 1 {
return ""
@@ -482,7 +466,17 @@
}
if xattrer, ok := resource.(XAttrer); ok {
- b.Xattr = xattrer.XAttrs()
+ // Sorts the XAttrs by name for consistent ordering.
+ keys := []string{}
+ xattrs := xattrer.XAttrs()
+ for k := range xattrs {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+
+ for _, k := range keys {
+ b.Xattr = append(b.Xattr, &pb.XAttr{Name: k, Data: xattrs[k]})
+ }
}
switch r := resource.(type) {
@@ -511,15 +505,17 @@
// fromProto converts from a protobuf Resource to a Resource interface.
func fromProto(b *pb.Resource) (Resource, error) {
- base, err := newBaseResource(b.Path[0], os.FileMode(b.Mode), b.Uid, b.Gid)
- if err != nil {
- return nil, err
+ base := &resource{
+ paths: b.Path,
+ mode: os.FileMode(b.Mode),
+ uid: b.Uid,
+ gid: b.Gid,
}
base.xattrs = make(map[string][]byte, len(b.Xattr))
- for attr, value := range b.Xattr {
- base.xattrs[attr] = append(base.xattrs[attr], value...)
+ for _, attr := range b.Xattr {
+ base.xattrs[attr.Name] = attr.Data
}
switch {
diff --git a/vendor/github.com/containerd/continuity/resource_unix.go b/vendor/github.com/containerd/continuity/resource_unix.go
new file mode 100644
index 0000000..f159a27
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/resource_unix.go
@@ -0,0 +1,37 @@
+// +build linux darwin
+
+package continuity
+
+import (
+ "fmt"
+ "os"
+ "syscall"
+)
+
+// newBaseResource returns a *resource, populated with data from p and fi,
+// where p will be populated directly.
+func newBaseResource(p string, fi os.FileInfo) (*resource, error) {
+ // TODO(stevvooe): This need to be resolved for the container's root,
+ // where here we are really getting the host OS's value. We need to allow
+ // this be passed in and fixed up to make these uid/gid mappings portable.
+ // Either this can be part of the driver or we can achieve it through some
+ // other mechanism.
+ sys, ok := fi.Sys().(*syscall.Stat_t)
+ if !ok {
+ // TODO(stevvooe): This may not be a hard error for all platforms. We
+ // may want to move this to the driver.
+ return nil, fmt.Errorf("unable to resolve syscall.Stat_t from (os.FileInfo).Sys(): %#v", fi)
+ }
+
+ return &resource{
+ paths: []string{p},
+ mode: fi.Mode(),
+
+ uid: fmt.Sprint(sys.Uid),
+ gid: fmt.Sprint(sys.Gid),
+
+ // NOTE(stevvooe): Population of shared xattrs field is deferred to
+ // the resource types that populate it. Since they are a property of
+ // the context, they must set there.
+ }, nil
+}
diff --git a/vendor/github.com/containerd/continuity/resource_windows.go b/vendor/github.com/containerd/continuity/resource_windows.go
new file mode 100644
index 0000000..7b44414
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/resource_windows.go
@@ -0,0 +1,12 @@
+package continuity
+
+import "os"
+
+// newBaseResource returns a *resource, populated with data from p and fi,
+// where p will be populated directly.
+func newBaseResource(p string, fi os.FileInfo) (*resource, error) {
+ return &resource{
+ paths: []string{p},
+ mode: fi.Mode(),
+ }, nil
+}
diff --git a/vendor/github.com/stevvooe/continuity/sysx/asm.s b/vendor/github.com/containerd/continuity/sysx/asm.s
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/sysx/asm.s
rename to vendor/github.com/containerd/continuity/sysx/asm.s
diff --git a/vendor/github.com/stevvooe/continuity/sysx/chmod_darwin.go b/vendor/github.com/containerd/continuity/sysx/chmod_darwin.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/sysx/chmod_darwin.go
rename to vendor/github.com/containerd/continuity/sysx/chmod_darwin.go
diff --git a/vendor/github.com/stevvooe/continuity/sysx/chmod_darwin_386.go b/vendor/github.com/containerd/continuity/sysx/chmod_darwin_386.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/sysx/chmod_darwin_386.go
rename to vendor/github.com/containerd/continuity/sysx/chmod_darwin_386.go
diff --git a/vendor/github.com/stevvooe/continuity/sysx/chmod_darwin_amd64.go b/vendor/github.com/containerd/continuity/sysx/chmod_darwin_amd64.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/sysx/chmod_darwin_amd64.go
rename to vendor/github.com/containerd/continuity/sysx/chmod_darwin_amd64.go
diff --git a/vendor/github.com/stevvooe/continuity/sysx/chmod_linux.go b/vendor/github.com/containerd/continuity/sysx/chmod_linux.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/sysx/chmod_linux.go
rename to vendor/github.com/containerd/continuity/sysx/chmod_linux.go
diff --git a/vendor/github.com/stevvooe/continuity/sysx/copy_linux.go b/vendor/github.com/containerd/continuity/sysx/copy_linux.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/sysx/copy_linux.go
rename to vendor/github.com/containerd/continuity/sysx/copy_linux.go
diff --git a/vendor/github.com/stevvooe/continuity/sysx/copy_linux_386.go b/vendor/github.com/containerd/continuity/sysx/copy_linux_386.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/sysx/copy_linux_386.go
rename to vendor/github.com/containerd/continuity/sysx/copy_linux_386.go
diff --git a/vendor/github.com/stevvooe/continuity/sysx/copy_linux_amd64.go b/vendor/github.com/containerd/continuity/sysx/copy_linux_amd64.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/sysx/copy_linux_amd64.go
rename to vendor/github.com/containerd/continuity/sysx/copy_linux_amd64.go
diff --git a/vendor/github.com/stevvooe/continuity/sysx/copy_linux_arm.go b/vendor/github.com/containerd/continuity/sysx/copy_linux_arm.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/sysx/copy_linux_arm.go
rename to vendor/github.com/containerd/continuity/sysx/copy_linux_arm.go
diff --git a/vendor/github.com/stevvooe/continuity/sysx/copy_linux_arm64.go b/vendor/github.com/containerd/continuity/sysx/copy_linux_arm64.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/sysx/copy_linux_arm64.go
rename to vendor/github.com/containerd/continuity/sysx/copy_linux_arm64.go
diff --git a/vendor/github.com/stevvooe/continuity/sysx/sys.go b/vendor/github.com/containerd/continuity/sysx/sys.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/sysx/sys.go
rename to vendor/github.com/containerd/continuity/sysx/sys.go
diff --git a/vendor/github.com/stevvooe/continuity/sysx/sysnum_linux_386.go b/vendor/github.com/containerd/continuity/sysx/sysnum_linux_386.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/sysx/sysnum_linux_386.go
rename to vendor/github.com/containerd/continuity/sysx/sysnum_linux_386.go
diff --git a/vendor/github.com/stevvooe/continuity/sysx/sysnum_linux_amd64.go b/vendor/github.com/containerd/continuity/sysx/sysnum_linux_amd64.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/sysx/sysnum_linux_amd64.go
rename to vendor/github.com/containerd/continuity/sysx/sysnum_linux_amd64.go
diff --git a/vendor/github.com/stevvooe/continuity/sysx/sysnum_linux_arm.go b/vendor/github.com/containerd/continuity/sysx/sysnum_linux_arm.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/sysx/sysnum_linux_arm.go
rename to vendor/github.com/containerd/continuity/sysx/sysnum_linux_arm.go
diff --git a/vendor/github.com/stevvooe/continuity/sysx/sysnum_linux_arm64.go b/vendor/github.com/containerd/continuity/sysx/sysnum_linux_arm64.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/sysx/sysnum_linux_arm64.go
rename to vendor/github.com/containerd/continuity/sysx/sysnum_linux_arm64.go
diff --git a/vendor/github.com/stevvooe/continuity/sysx/xattr.go b/vendor/github.com/containerd/continuity/sysx/xattr.go
similarity index 95%
rename from vendor/github.com/stevvooe/continuity/sysx/xattr.go
rename to vendor/github.com/containerd/continuity/sysx/xattr.go
index 0c07101..20937c2 100644
--- a/vendor/github.com/stevvooe/continuity/sysx/xattr.go
+++ b/vendor/github.com/containerd/continuity/sysx/xattr.go
@@ -2,11 +2,14 @@
import (
"bytes"
+ "fmt"
"syscall"
)
const defaultXattrBufferSize = 5
+var ErrNotSupported = fmt.Errorf("not supported")
+
type listxattrFunc func(path string, dest []byte) (int, error)
func listxattrAll(path string, listFunc listxattrFunc) ([]string, error) {
diff --git a/vendor/github.com/stevvooe/continuity/sysx/xattr_darwin.go b/vendor/github.com/containerd/continuity/sysx/xattr_darwin.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/sysx/xattr_darwin.go
rename to vendor/github.com/containerd/continuity/sysx/xattr_darwin.go
diff --git a/vendor/github.com/stevvooe/continuity/sysx/xattr_darwin_386.go b/vendor/github.com/containerd/continuity/sysx/xattr_darwin_386.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/sysx/xattr_darwin_386.go
rename to vendor/github.com/containerd/continuity/sysx/xattr_darwin_386.go
diff --git a/vendor/github.com/stevvooe/continuity/sysx/xattr_darwin_amd64.go b/vendor/github.com/containerd/continuity/sysx/xattr_darwin_amd64.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/sysx/xattr_darwin_amd64.go
rename to vendor/github.com/containerd/continuity/sysx/xattr_darwin_amd64.go
diff --git a/vendor/github.com/stevvooe/continuity/sysx/xattr_linux.go b/vendor/github.com/containerd/continuity/sysx/xattr_linux.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/sysx/xattr_linux.go
rename to vendor/github.com/containerd/continuity/sysx/xattr_linux.go
diff --git a/vendor/github.com/stevvooe/continuity/sysx/xattr_linux_386.go b/vendor/github.com/containerd/continuity/sysx/xattr_linux_386.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/sysx/xattr_linux_386.go
rename to vendor/github.com/containerd/continuity/sysx/xattr_linux_386.go
diff --git a/vendor/github.com/stevvooe/continuity/sysx/xattr_linux_amd64.go b/vendor/github.com/containerd/continuity/sysx/xattr_linux_amd64.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/sysx/xattr_linux_amd64.go
rename to vendor/github.com/containerd/continuity/sysx/xattr_linux_amd64.go
diff --git a/vendor/github.com/stevvooe/continuity/sysx/xattr_linux_arm.go b/vendor/github.com/containerd/continuity/sysx/xattr_linux_arm.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/sysx/xattr_linux_arm.go
rename to vendor/github.com/containerd/continuity/sysx/xattr_linux_arm.go
diff --git a/vendor/github.com/stevvooe/continuity/sysx/xattr_linux_arm64.go b/vendor/github.com/containerd/continuity/sysx/xattr_linux_arm64.go
similarity index 100%
rename from vendor/github.com/stevvooe/continuity/sysx/xattr_linux_arm64.go
rename to vendor/github.com/containerd/continuity/sysx/xattr_linux_arm64.go
diff --git a/vendor/github.com/containerd/continuity/sysx/xattr_linux_ppc64.go b/vendor/github.com/containerd/continuity/sysx/xattr_linux_ppc64.go
new file mode 100644
index 0000000..33385d7
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/sysx/xattr_linux_ppc64.go
@@ -0,0 +1,19 @@
+package sysx
+
+import "github.com/pkg/errors"
+
+func llistxattr(path string, dest []byte) (sz int, err error) {
+ return 0, errors.Wrap(ErrNotSupported, "llistxattr not implemented on ppc64")
+}
+
+func lremovexattr(path string, attr string) (err error) {
+ return errors.Wrap(ErrNotSupported, "lremovexattr not implemented on ppc64")
+}
+
+func lsetxattr(path string, attr string, data []byte, flags int) (err error) {
+ return errors.Wrap(ErrNotSupported, "lsetxattr not implemented on ppc64")
+}
+
+func lgetxattr(path string, attr string, dest []byte) (sz int, err error) {
+ return 0, errors.Wrap(ErrNotSupported, "lgetxattr not implemented on ppc64")
+}
diff --git a/vendor/github.com/containerd/continuity/sysx/xattr_linux_ppc64le.go b/vendor/github.com/containerd/continuity/sysx/xattr_linux_ppc64le.go
new file mode 100644
index 0000000..dc65a72
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/sysx/xattr_linux_ppc64le.go
@@ -0,0 +1,19 @@
+package sysx
+
+import "github.com/pkg/errors"
+
+func llistxattr(path string, dest []byte) (sz int, err error) {
+ return 0, errors.Wrap(ErrNotSupported, "llistxattr not implemented on ppc64le")
+}
+
+func lremovexattr(path string, attr string) (err error) {
+ return errors.Wrap(ErrNotSupported, "lremovexattr not implemented on ppc64le")
+}
+
+func lsetxattr(path string, attr string, data []byte, flags int) (err error) {
+ return errors.Wrap(ErrNotSupported, "lsetxattr not implemented on ppc64le")
+}
+
+func lgetxattr(path string, attr string, dest []byte) (sz int, err error) {
+ return 0, errors.Wrap(ErrNotSupported, "lgetxattr not implemented on ppc64le")
+}
diff --git a/vendor/github.com/containerd/continuity/sysx/xattr_linux_s390x.go b/vendor/github.com/containerd/continuity/sysx/xattr_linux_s390x.go
new file mode 100644
index 0000000..0af2b2c
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/sysx/xattr_linux_s390x.go
@@ -0,0 +1,19 @@
+package sysx
+
+import "github.com/pkg/errors"
+
+func llistxattr(path string, dest []byte) (sz int, err error) {
+ return 0, errors.Wrap(ErrNotSupported, "llistxattr not implemented on s390x")
+}
+
+func lremovexattr(path string, attr string) (err error) {
+ return errors.Wrap(ErrNotSupported, "lremovexattr not implemented on s390x")
+}
+
+func lsetxattr(path string, attr string, data []byte, flags int) (err error) {
+ return errors.Wrap(ErrNotSupported, "lsetxattr not implemented on s390x")
+}
+
+func lgetxattr(path string, attr string, dest []byte) (sz int, err error) {
+ return 0, errors.Wrap(ErrNotSupported, "lgetxattr not implemented on s390x")
+}
diff --git a/vendor/github.com/coreos/go-systemd/LICENSE b/vendor/github.com/coreos/go-systemd/LICENSE
new file mode 100644
index 0000000..37ec93a
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/LICENSE
@@ -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.
diff --git a/vendor/github.com/coreos/go-systemd/README.md b/vendor/github.com/coreos/go-systemd/README.md
new file mode 100644
index 0000000..cb87a11
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/README.md
@@ -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).
diff --git a/vendor/github.com/coreos/go-systemd/dbus/dbus.go b/vendor/github.com/coreos/go-systemd/dbus/dbus.go
new file mode 100644
index 0000000..c1694fb
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/dbus/dbus.go
@@ -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"))
+}
diff --git a/vendor/github.com/coreos/go-systemd/dbus/methods.go b/vendor/github.com/coreos/go-systemd/dbus/methods.go
new file mode 100644
index 0000000..ab17f7c
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/dbus/methods.go
@@ -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))
+}
diff --git a/vendor/github.com/coreos/go-systemd/dbus/properties.go b/vendor/github.com/coreos/go-systemd/dbus/properties.go
new file mode 100644
index 0000000..6c81895
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/dbus/properties.go
@@ -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),
+ }
+}
diff --git a/vendor/github.com/coreos/go-systemd/dbus/set.go b/vendor/github.com/coreos/go-systemd/dbus/set.go
new file mode 100644
index 0000000..f92e6fb
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/dbus/set.go
@@ -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)}
+}
diff --git a/vendor/github.com/coreos/go-systemd/dbus/subscription.go b/vendor/github.com/coreos/go-systemd/dbus/subscription.go
new file mode 100644
index 0000000..9964514
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/dbus/subscription.go
@@ -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)
+ }
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/go-systemd/dbus/subscription_set.go b/vendor/github.com/coreos/go-systemd/dbus/subscription_set.go
new file mode 100644
index 0000000..5b408d5
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/dbus/subscription_set.go
@@ -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
+}
diff --git a/vendor/github.com/crosbymichael/console/console.go b/vendor/github.com/crosbymichael/console/console.go
index a1d392c..8be788b 100644
--- a/vendor/github.com/crosbymichael/console/console.go
+++ b/vendor/github.com/crosbymichael/console/console.go
@@ -20,6 +20,8 @@
ResizeFrom(Console) error
// SetRaw sets the console in raw mode
SetRaw() error
+ // DisableEcho disables echo on the console
+ DisableEcho() error
// Reset restores the console to its orignal state
Reset() error
// Size returns the window size of the console
diff --git a/vendor/github.com/crosbymichael/console/console_linux.go b/vendor/github.com/crosbymichael/console/console_unix.go
similarity index 75%
rename from vendor/github.com/crosbymichael/console/console_linux.go
rename to vendor/github.com/crosbymichael/console/console_unix.go
index a259150..fb27011 100644
--- a/vendor/github.com/crosbymichael/console/console_linux.go
+++ b/vendor/github.com/crosbymichael/console/console_unix.go
@@ -1,3 +1,5 @@
+// +build darwin freebsd linux
+
package console
// #include <termios.h>
@@ -34,8 +36,8 @@
}
type master struct {
- f *os.File
- termios *unix.Termios
+ f *os.File
+ original *unix.Termios
}
func (m *master) Read(b []byte) (int, error) {
@@ -67,23 +69,42 @@
}
func (m *master) Reset() error {
- if m.termios == nil {
+ if m.original == nil {
return nil
}
- return tcset(m.f.Fd(), m.termios)
+ return tcset(m.f.Fd(), m.original)
+}
+
+func (m *master) getCurrent() (unix.Termios, error) {
+ var termios unix.Termios
+ if err := tcget(m.f.Fd(), &termios); err != nil {
+ return unix.Termios{}, err
+ }
+ if m.original == nil {
+ m.original = &termios
+ }
+ return termios, nil
}
func (m *master) SetRaw() error {
- m.termios = &unix.Termios{}
- if err := tcget(m.f.Fd(), m.termios); err != nil {
+ rawState, err := m.getCurrent()
+ if err != nil {
return err
}
- rawState := *m.termios
C.cfmakeraw((*C.struct_termios)(unsafe.Pointer(&rawState)))
rawState.Oflag = rawState.Oflag | C.OPOST
return tcset(m.f.Fd(), &rawState)
}
+func (m *master) DisableEcho() error {
+ rawState, err := m.getCurrent()
+ if err != nil {
+ return err
+ }
+ rawState.Lflag = rawState.Lflag &^ unix.ECHO
+ return tcset(m.f.Fd(), &rawState)
+}
+
func (m *master) Size() (WinSize, error) {
var ws WinSize
if err := ioctl(
diff --git a/vendor/github.com/crosbymichael/console/console_windows.go b/vendor/github.com/crosbymichael/console/console_windows.go
index 0dcefff..8d133ea 100644
--- a/vendor/github.com/crosbymichael/console/console_windows.go
+++ b/vendor/github.com/crosbymichael/console/console_windows.go
@@ -126,6 +126,18 @@
return ErrNotImplemented
}
+func (m *master) DisableEcho() error {
+ mode := m.inMode &^ winterm.ENABLE_ECHO_INPUT
+ mode |= winterm.ENABLE_PROCESSED_INPUT
+ mode |= winterm.ENABLE_LINE_INPUT
+
+ if err := winterm.SetConsoleMode(m.in, mode); err != nil {
+ return errors.Wrap(err, "unable to set console to disable echo")
+ }
+
+ return nil
+}
+
func (m *master) Close() error {
return nil
}
diff --git a/vendor/github.com/crosbymichael/console/tc.go b/vendor/github.com/crosbymichael/console/tc_darwin.go
similarity index 78%
copy from vendor/github.com/crosbymichael/console/tc.go
copy to vendor/github.com/crosbymichael/console/tc_darwin.go
index 260396b..d86c315 100644
--- a/vendor/github.com/crosbymichael/console/tc.go
+++ b/vendor/github.com/crosbymichael/console/tc_darwin.go
@@ -1,5 +1,3 @@
-// +build linux
-
package console
import (
@@ -11,11 +9,11 @@
)
func tcget(fd uintptr, p *unix.Termios) error {
- return ioctl(fd, unix.TCGETS, uintptr(unsafe.Pointer(p)))
+ return ioctl(fd, unix.TIOCGETA, uintptr(unsafe.Pointer(p)))
}
func tcset(fd uintptr, p *unix.Termios) error {
- return ioctl(fd, unix.TCSETS, uintptr(unsafe.Pointer(p)))
+ return ioctl(fd, unix.TIOCSETA, uintptr(unsafe.Pointer(p)))
}
func ioctl(fd, flag, data uintptr) error {
@@ -29,13 +27,13 @@
// unlockpt should be called before opening the slave side of a pty.
func unlockpt(f *os.File) error {
var u int32
- return ioctl(f.Fd(), unix.TIOCSPTLCK, uintptr(unsafe.Pointer(&u)))
+ return ioctl(f.Fd(), unix.TIOCPTYUNLK, uintptr(unsafe.Pointer(&u)))
}
// ptsname retrieves the name of the first available pts for the given master.
func ptsname(f *os.File) (string, error) {
var n int32
- if err := ioctl(f.Fd(), unix.TIOCGPTN, uintptr(unsafe.Pointer(&n))); err != nil {
+ if err := ioctl(f.Fd(), unix.TIOCPTYGNAME, uintptr(unsafe.Pointer(&n))); err != nil {
return "", err
}
return fmt.Sprintf("/dev/pts/%d", n), nil
diff --git a/vendor/github.com/crosbymichael/console/tc.go b/vendor/github.com/crosbymichael/console/tc_freebsd.go
similarity index 83%
copy from vendor/github.com/crosbymichael/console/tc.go
copy to vendor/github.com/crosbymichael/console/tc_freebsd.go
index 260396b..478f60a 100644
--- a/vendor/github.com/crosbymichael/console/tc.go
+++ b/vendor/github.com/crosbymichael/console/tc_freebsd.go
@@ -1,5 +1,3 @@
-// +build linux
-
package console
import (
@@ -11,11 +9,11 @@
)
func tcget(fd uintptr, p *unix.Termios) error {
- return ioctl(fd, unix.TCGETS, uintptr(unsafe.Pointer(p)))
+ return ioctl(fd, unix.TIOCGETA, uintptr(unsafe.Pointer(p)))
}
func tcset(fd uintptr, p *unix.Termios) error {
- return ioctl(fd, unix.TCSETS, uintptr(unsafe.Pointer(p)))
+ return ioctl(fd, unix.TIOCSETA, uintptr(unsafe.Pointer(p)))
}
func ioctl(fd, flag, data uintptr) error {
@@ -27,9 +25,9 @@
// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f.
// unlockpt should be called before opening the slave side of a pty.
+// This does not exist on FreeBSD, it does not allocate controlling terminals on open
func unlockpt(f *os.File) error {
- var u int32
- return ioctl(f.Fd(), unix.TIOCSPTLCK, uintptr(unsafe.Pointer(&u)))
+ return nil
}
// ptsname retrieves the name of the first available pts for the given master.
diff --git a/vendor/github.com/crosbymichael/console/tc.go b/vendor/github.com/crosbymichael/console/tc_linux.go
similarity index 98%
rename from vendor/github.com/crosbymichael/console/tc.go
rename to vendor/github.com/crosbymichael/console/tc_linux.go
index 260396b..67c89f0 100644
--- a/vendor/github.com/crosbymichael/console/tc.go
+++ b/vendor/github.com/crosbymichael/console/tc_linux.go
@@ -1,5 +1,3 @@
-// +build linux
-
package console
import (
diff --git a/vendor/github.com/godbus/dbus/LICENSE b/vendor/github.com/godbus/dbus/LICENSE
new file mode 100644
index 0000000..670d88f
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/LICENSE
@@ -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.
diff --git a/vendor/github.com/godbus/dbus/README.markdown b/vendor/github.com/godbus/dbus/README.markdown
new file mode 100644
index 0000000..0a6e7e5
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/README.markdown
@@ -0,0 +1,41 @@
+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.
+
+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.
diff --git a/vendor/github.com/godbus/dbus/auth.go b/vendor/github.com/godbus/dbus/auth.go
new file mode 100644
index 0000000..98017b6
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/auth.go
@@ -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
+}
diff --git a/vendor/github.com/godbus/dbus/auth_external.go b/vendor/github.com/godbus/dbus/auth_external.go
new file mode 100644
index 0000000..7e376d3
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/auth_external.go
@@ -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
+}
diff --git a/vendor/github.com/godbus/dbus/auth_sha1.go b/vendor/github.com/godbus/dbus/auth_sha1.go
new file mode 100644
index 0000000..df15b46
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/auth_sha1.go
@@ -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
+}
diff --git a/vendor/github.com/godbus/dbus/call.go b/vendor/github.com/godbus/dbus/call.go
new file mode 100644
index 0000000..ba6e73f
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/call.go
@@ -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...)
+}
diff --git a/vendor/github.com/godbus/dbus/conn.go b/vendor/github.com/godbus/dbus/conn.go
new file mode 100644
index 0000000..a4f5394
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/conn.go
@@ -0,0 +1,625 @@
+package dbus
+
+import (
+ "errors"
+ "io"
+ "os"
+ "reflect"
+ "strings"
+ "sync"
+)
+
+const defaultSystemBusAddress = "unix:path=/var/run/dbus/system_bus_socket"
+
+var (
+ systemBus *Conn
+ systemBusLck sync.Mutex
+ sessionBus *Conn
+ sessionBusLck 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
+
+ handlers map[ObjectPath]map[string]exportWithMapping
+ handlersLck sync.RWMutex
+
+ out chan *Message
+ closed bool
+ outLck sync.RWMutex
+
+ signals []chan<- *Signal
+ signalsLck sync.Mutex
+
+ 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
+}
+
+// SessionBusPrivate returns a new private connection to the session bus.
+func SessionBusPrivate() (*Conn, error) {
+ address := os.Getenv("DBUS_SESSION_BUS_ADDRESS")
+ if address != "" && address != "autolaunch:" {
+ return Dial(address)
+ }
+
+ return sessionBusPlatform()
+}
+
+// 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) {
+ address := os.Getenv("DBUS_SYSTEM_BUS_ADDRESS")
+ if address != "" {
+ return Dial(address)
+ }
+ return Dial(defaultSystemBusAddress)
+}
+
+// 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)
+}
+
+// NewConn creates a new private *Conn from an already established connection.
+func NewConn(conn io.ReadWriteCloser) (*Conn, error) {
+ return newConn(genericTransport{conn})
+}
+
+// newConn creates a new *Conn from a transport.
+func newConn(tr transport) (*Conn, error) {
+ conn := new(Conn)
+ conn.transport = tr
+ conn.calls = make(map[uint32]*Call)
+ conn.out = make(chan *Message, 10)
+ conn.handlers = make(map[ObjectPath]map[string]exportWithMapping)
+ 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()
+ conn.signalsLck.Lock()
+ for _, ch := range conn.signals {
+ close(ch)
+ }
+ conn.signalsLck.Unlock()
+ 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()
+ }
+ }
+ signal := &Signal{
+ Sender: sender,
+ Path: msg.Headers[FieldPath].value.(ObjectPath),
+ Name: iface + "." + member,
+ Body: msg.Body,
+ }
+ conn.signalsLck.Lock()
+ for _, ch := range conn.signals {
+ ch <- signal
+ }
+ conn.signalsLck.Unlock()
+ 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
+ }
+}
+
+// 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(e Error, dest string, serial uint32) {
+ 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()
+}
+
+// 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. Passing a
+// channel that already is registered will remove it from the list of the
+// registered channels.
+//
+// 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.signalsLck.Lock()
+ conn.signals = append(conn.signals, ch)
+ conn.signalsLck.Unlock()
+}
+
+// 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 {
+ i := strings.Index(s, key)
+ if i == -1 {
+ return ""
+ }
+ if i+len(key)+1 >= len(s) || s[i+len(key)] != '=' {
+ return ""
+ }
+ j := strings.Index(s, ",")
+ if j == -1 {
+ j = len(s)
+ }
+ return s[i+len(key)+1 : j]
+}
diff --git a/vendor/github.com/godbus/dbus/conn_darwin.go b/vendor/github.com/godbus/dbus/conn_darwin.go
new file mode 100644
index 0000000..b67bb1b
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/conn_darwin.go
@@ -0,0 +1,21 @@
+package dbus
+
+import (
+ "errors"
+ "os/exec"
+)
+
+func sessionBusPlatform() (*Conn, error) {
+ cmd := exec.Command("launchctl", "getenv", "DBUS_LAUNCHD_SESSION_BUS_SOCKET")
+ b, err := cmd.CombinedOutput()
+
+ if err != nil {
+ return nil, err
+ }
+
+ if len(b) == 0 {
+ return nil, errors.New("dbus: couldn't determine address of session bus")
+ }
+
+ return Dial("unix:path=" + string(b[:len(b)-1]))
+}
diff --git a/vendor/github.com/godbus/dbus/conn_other.go b/vendor/github.com/godbus/dbus/conn_other.go
new file mode 100644
index 0000000..f74b875
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/conn_other.go
@@ -0,0 +1,27 @@
+// +build !darwin
+
+package dbus
+
+import (
+ "bytes"
+ "errors"
+ "os/exec"
+)
+
+func sessionBusPlatform() (*Conn, error) {
+ cmd := exec.Command("dbus-launch")
+ b, err := cmd.CombinedOutput()
+
+ if err != nil {
+ return nil, err
+ }
+
+ i := bytes.IndexByte(b, '=')
+ j := bytes.IndexByte(b, '\n')
+
+ if i == -1 || j == -1 {
+ return nil, errors.New("dbus: couldn't determine address of session bus")
+ }
+
+ return Dial(string(b[i+1 : j]))
+}
diff --git a/vendor/github.com/godbus/dbus/dbus.go b/vendor/github.com/godbus/dbus/dbus.go
new file mode 100644
index 0000000..2ce6873
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/dbus.go
@@ -0,0 +1,258 @@
+package dbus
+
+import (
+ "errors"
+ "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))
+ 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{}{})
+ 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 := store(src[i], dest[i]); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func store(src, dest interface{}) error {
+ if reflect.TypeOf(dest).Elem() == reflect.TypeOf(src) {
+ reflect.ValueOf(dest).Elem().Set(reflect.ValueOf(src))
+ return nil
+ } else if hasStruct(dest) {
+ rv := reflect.ValueOf(dest).Elem()
+ switch rv.Kind() {
+ case reflect.Struct:
+ vs, ok := src.([]interface{})
+ if !ok {
+ return errors.New("dbus.Store: type mismatch")
+ }
+ t := rv.Type()
+ ndest := make([]interface{}, 0, rv.NumField())
+ for i := 0; i < rv.NumField(); i++ {
+ field := t.Field(i)
+ if field.PkgPath == "" && field.Tag.Get("dbus") != "-" {
+ ndest = append(ndest, rv.Field(i).Addr().Interface())
+ }
+ }
+ if len(vs) != len(ndest) {
+ return errors.New("dbus.Store: type mismatch")
+ }
+ err := Store(vs, ndest...)
+ if err != nil {
+ return errors.New("dbus.Store: type mismatch")
+ }
+ case reflect.Slice:
+ sv := reflect.ValueOf(src)
+ if sv.Kind() != reflect.Slice {
+ return errors.New("dbus.Store: type mismatch")
+ }
+ rv.Set(reflect.MakeSlice(rv.Type(), sv.Len(), sv.Len()))
+ for i := 0; i < sv.Len(); i++ {
+ if err := store(sv.Index(i).Interface(), rv.Index(i).Addr().Interface()); err != nil {
+ return err
+ }
+ }
+ case reflect.Map:
+ sv := reflect.ValueOf(src)
+ if sv.Kind() != reflect.Map {
+ return errors.New("dbus.Store: type mismatch")
+ }
+ keys := sv.MapKeys()
+ rv.Set(reflect.MakeMap(sv.Type()))
+ for _, key := range keys {
+ v := reflect.New(sv.Type().Elem())
+ if err := store(v, sv.MapIndex(key).Interface()); err != nil {
+ return err
+ }
+ rv.SetMapIndex(key, v.Elem())
+ }
+ default:
+ return errors.New("dbus.Store: type mismatch")
+ }
+ return nil
+ } else {
+ return errors.New("dbus.Store: type mismatch")
+ }
+}
+
+func hasStruct(v interface{}) bool {
+ t := reflect.TypeOf(v)
+ for {
+ switch t.Kind() {
+ case reflect.Struct:
+ return true
+ case reflect.Slice, reflect.Ptr, reflect.Map:
+ t = t.Elem()
+ default:
+ return false
+ }
+ }
+}
+
+// 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: // sometimes used for structs
+ return 8
+ }
+ switch t.Kind() {
+ case reflect.Uint8:
+ return 1
+ case reflect.Uint16, reflect.Int16:
+ return 2
+ case 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:
+
+ 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 == '_'
+}
diff --git a/vendor/github.com/godbus/dbus/decoder.go b/vendor/github.com/godbus/dbus/decoder.go
new file mode 100644
index 0000000..ef50dca
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/decoder.go
@@ -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)
+}
diff --git a/vendor/github.com/godbus/dbus/doc.go b/vendor/github.com/godbus/dbus/doc.go
new file mode 100644
index 0000000..deff554
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/doc.go
@@ -0,0 +1,63 @@
+/*
+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
+ int32 | INT32
+ uint32 | UINT32
+ int64 | INT64
+ uint64 | UINT64
+ float64 | DOUBLE
+ string | STRING
+ ObjectPath | OBJECT_PATH
+ Signature | SIGNATURE
+ Variant | 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.
+
+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
diff --git a/vendor/github.com/godbus/dbus/encoder.go b/vendor/github.com/godbus/dbus/encoder.go
new file mode 100644
index 0000000..9f0a9e8
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/encoder.go
@@ -0,0 +1,208 @@
+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.Int32:
+ enc.binwrite(int32(v.Int()))
+ enc.pos += 4
+ case 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
+ default:
+ panic(InvalidTypeError{v.Type()})
+ }
+}
diff --git a/vendor/github.com/godbus/dbus/export.go b/vendor/github.com/godbus/dbus/export.go
new file mode 100644
index 0000000..c6440a7
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/export.go
@@ -0,0 +1,411 @@
+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"},
+ }
+)
+
+// exportWithMapping represents an exported struct along with a method name
+// mapping to allow for exporting lower-case methods, etc.
+type exportWithMapping struct {
+ export interface{}
+
+ // Method name mapping; key -> struct method, value -> dbus method.
+ mapping map[string]string
+
+ // Whether or not this export is for the entire subtree
+ includeSubtree bool
+}
+
+// Sender is a type which can be used in exported methods to receive the message
+// sender.
+type Sender string
+
+func exportedMethod(export exportWithMapping, name string) reflect.Value {
+ if export.export == nil {
+ return reflect.Value{}
+ }
+
+ // If a mapping was included in the export, check the map to see if we
+ // should be looking for a different method in the export.
+ if export.mapping != nil {
+ for key, value := range export.mapping {
+ if value == name {
+ name = key
+ break
+ }
+
+ // Catch the case where a method is aliased but the client is calling
+ // the original, e.g. the "Foo" method was exported mapped to
+ // "foo," and dbus client called the original "Foo."
+ if key == name {
+ return reflect.Value{}
+ }
+ }
+ }
+
+ value := reflect.ValueOf(export.export)
+ m := value.MethodByName(name)
+
+ // Catch the case of attempting to call an unexported method
+ method, ok := value.Type().MethodByName(name)
+
+ if !m.IsValid() || !ok || method.PkgPath != "" {
+ return reflect.Value{}
+ }
+ t := m.Type()
+ if t.NumOut() == 0 ||
+ t.Out(t.NumOut()-1) != reflect.TypeOf(&errmsgInvalidArg) {
+
+ return reflect.Value{}
+ }
+ return m
+}
+
+// searchHandlers will look through all registered handlers looking for one
+// to handle the given path. If a verbatim one isn't found, it will check for
+// a subtree registration for the path as well.
+func (conn *Conn) searchHandlers(path ObjectPath) (map[string]exportWithMapping, bool) {
+ conn.handlersLck.RLock()
+ defer conn.handlersLck.RUnlock()
+
+ handlers, ok := conn.handlers[path]
+ if ok {
+ return handlers, ok
+ }
+
+ // If handlers weren't found for this exact path, look for a matching subtree
+ // registration
+ handlers = make(map[string]exportWithMapping)
+ path = path[:strings.LastIndex(string(path), "/")]
+ for len(path) > 0 {
+ var subtreeHandlers map[string]exportWithMapping
+ subtreeHandlers, ok = conn.handlers[path]
+ if ok {
+ for iface, handler := range subtreeHandlers {
+ // Only include this handler if it registered for the subtree
+ if handler.includeSubtree {
+ handlers[iface] = handler
+ }
+ }
+
+ break
+ }
+
+ path = path[:strings.LastIndex(string(path), "/")]
+ }
+
+ return handlers, ok
+}
+
+// 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, hasIface := 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)
+ }
+
+ // Find the exported handler (if any) for this path
+ handlers, ok := conn.searchHandlers(path)
+ if !ok {
+ conn.sendError(errmsgNoObject, sender, serial)
+ return
+ }
+
+ var m reflect.Value
+ if hasIface {
+ iface := handlers[ifaceName]
+ m = exportedMethod(iface, name)
+ } else {
+ for _, v := range handlers {
+ m = exportedMethod(v, name)
+ if m.IsValid() {
+ break
+ }
+ }
+ }
+
+ if !m.IsValid() {
+ conn.sendError(errmsgUnknownMethod, sender, serial)
+ return
+ }
+
+ t := m.Type()
+ vs := msg.Body
+ pointers := make([]interface{}, t.NumIn())
+ decode := make([]interface{}, 0, len(vs))
+ for i := 0; i < t.NumIn(); i++ {
+ tp := t.In(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(vs) {
+ conn.sendError(errmsgInvalidArg, sender, serial)
+ return
+ }
+
+ if err := Store(vs, decode...); err != nil {
+ conn.sendError(errmsgInvalidArg, sender, serial)
+ return
+ }
+
+ // Extract parameters
+ params := make([]reflect.Value, len(pointers))
+ for i := 0; i < len(pointers); i++ {
+ params[i] = reflect.ValueOf(pointers[i]).Elem()
+ }
+
+ // Call method
+ ret := m.Call(params)
+ if em := ret[t.NumOut()-1].Interface().(*Error); em != nil {
+ conn.sendError(*em, 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)-1)
+ for i := 0; i < len(ret)-1; i++ {
+ reply.Body[i] = ret[i].Interface()
+ }
+ if len(ret) != 1 {
+ 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.exportWithMap(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.exportWithMap(v, mapping, path, iface, true)
+}
+
+// exportWithMap is the worker function for all exports/registrations.
+func (conn *Conn) exportWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string, includeSubtree bool) error {
+ if !path.IsValid() {
+ return fmt.Errorf(`dbus: Invalid path name: "%s"`, path)
+ }
+
+ conn.handlersLck.Lock()
+ defer conn.handlersLck.Unlock()
+
+ // Remove a previous export if the interface is nil
+ if v == nil {
+ if _, ok := conn.handlers[path]; ok {
+ delete(conn.handlers[path], iface)
+ if len(conn.handlers[path]) == 0 {
+ delete(conn.handlers, path)
+ }
+ }
+
+ return nil
+ }
+
+ // If this is the first handler for this path, make a new map to hold all
+ // handlers for this path.
+ if _, ok := conn.handlers[path]; !ok {
+ conn.handlers[path] = make(map[string]exportWithMapping)
+ }
+
+ // Finally, save this handler
+ conn.handlers[path][iface] = exportWithMapping{export: v, mapping: mapping, includeSubtree: 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
+)
diff --git a/vendor/github.com/godbus/dbus/homedir.go b/vendor/github.com/godbus/dbus/homedir.go
new file mode 100644
index 0000000..0b745f9
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/homedir.go
@@ -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
+}
diff --git a/vendor/github.com/godbus/dbus/homedir_dynamic.go b/vendor/github.com/godbus/dbus/homedir_dynamic.go
new file mode 100644
index 0000000..2732081
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/homedir_dynamic.go
@@ -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
+}
diff --git a/vendor/github.com/godbus/dbus/homedir_static.go b/vendor/github.com/godbus/dbus/homedir_static.go
new file mode 100644
index 0000000..b9d9cb5
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/homedir_static.go
@@ -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 "/"
+}
diff --git a/vendor/github.com/godbus/dbus/message.go b/vendor/github.com/godbus/dbus/message.go
new file mode 100644
index 0000000..075d6e3
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/message.go
@@ -0,0 +1,346 @@
+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
+)
+
+// 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) != 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
+}
diff --git a/vendor/github.com/godbus/dbus/object.go b/vendor/github.com/godbus/dbus/object.go
new file mode 100644
index 0000000..7ef45da
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/object.go
@@ -0,0 +1,126 @@
+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
+}
+
+// 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 are sent to.
+func (o *Object) Destination() string {
+ return o.dest
+}
+
+// Path returns the path that calls on o are sent to.
+func (o *Object) Path() ObjectPath {
+ return o.path
+}
diff --git a/vendor/github.com/godbus/dbus/sig.go b/vendor/github.com/godbus/dbus/sig.go
new file mode 100644
index 0000000..f45b53c
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/sig.go
@@ -0,0 +1,257 @@
+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.Int32:
+ if t == unixFDType {
+ return "h"
+ }
+ return "i"
+ case 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()) + "}"
+ }
+ 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 successfull, 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
+}
diff --git a/vendor/github.com/godbus/dbus/transport_darwin.go b/vendor/github.com/godbus/dbus/transport_darwin.go
new file mode 100644
index 0000000..1bba0d6
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/transport_darwin.go
@@ -0,0 +1,6 @@
+package dbus
+
+func (t *unixTransport) SendNullByte() error {
+ _, err := t.Write([]byte{0})
+ return err
+}
diff --git a/vendor/github.com/godbus/dbus/transport_generic.go b/vendor/github.com/godbus/dbus/transport_generic.go
new file mode 100644
index 0000000..46f8f49
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/transport_generic.go
@@ -0,0 +1,35 @@
+package dbus
+
+import (
+ "encoding/binary"
+ "errors"
+ "io"
+)
+
+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, binary.LittleEndian)
+}
diff --git a/vendor/github.com/godbus/dbus/transport_unix.go b/vendor/github.com/godbus/dbus/transport_unix.go
new file mode 100644
index 0000000..3fafeab
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/transport_unix.go
@@ -0,0 +1,196 @@
+//+build !windows
+
+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, binary.LittleEndian)
+ 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, binary.LittleEndian); err != nil {
+ return nil
+ }
+ }
+ return nil
+}
+
+func (t *unixTransport) SupportsUnixFDs() bool {
+ return true
+}
diff --git a/vendor/github.com/godbus/dbus/transport_unixcred_dragonfly.go b/vendor/github.com/godbus/dbus/transport_unixcred_dragonfly.go
new file mode 100644
index 0000000..a8cd393
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/transport_unixcred_dragonfly.go
@@ -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
+}
diff --git a/vendor/github.com/godbus/dbus/transport_unixcred_linux.go b/vendor/github.com/godbus/dbus/transport_unixcred_linux.go
new file mode 100644
index 0000000..d9dfdf6
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/transport_unixcred_linux.go
@@ -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
+}
diff --git a/vendor/github.com/godbus/dbus/variant.go b/vendor/github.com/godbus/dbus/variant.go
new file mode 100644
index 0000000..b7b13ae
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/variant.go
@@ -0,0 +1,139 @@
+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 Variant{SignatureOf(v), 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
+}
diff --git a/vendor/github.com/godbus/dbus/variant_lexer.go b/vendor/github.com/godbus/dbus/variant_lexer.go
new file mode 100644
index 0000000..332007d
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/variant_lexer.go
@@ -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
+}
diff --git a/vendor/github.com/godbus/dbus/variant_parser.go b/vendor/github.com/godbus/dbus/variant_parser.go
new file mode 100644
index 0000000..d20f5da
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/variant_parser.go
@@ -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()
+}
diff --git a/vendor/github.com/opencontainers/runc/vendor.conf b/vendor/github.com/opencontainers/runc/vendor.conf
new file mode 100644
index 0000000..60b80d0
--- /dev/null
+++ b/vendor/github.com/opencontainers/runc/vendor.conf
@@ -0,0 +1,14 @@
+github.com/Sirupsen/logrus 26709e2714106fb8ad40b773b711ebce25b78914
+github.com/coreos/go-systemd 48702e0da86bd25e76cfef347e2adeb434a0d0a6
+github.com/coreos/pkg/dlopen 3ac0863d7acf3bc44daf49afef8919af12f704ef
+github.com/docker/docker 0f5c9d301b9b1cca66b3ea0f9dec3b5317d3686d
+github.com/docker/go-units 9b001659dd36225e356b4467c465d732e745f53d
+github.com/godbus/dbus c7fdd8b5cd55e87b4e1f4e372cdb1db61dd6c66f
+github.com/golang/protobuf/proto f7137ae6b19afbfd61a94b746fda3b3fe0491874
+github.com/mrunalp/fileutils ed869b029674c0e9ce4c0dfa781405c2d9946d08
+github.com/opencontainers/runtime-spec/specs-go 035da1dca3dfbb00d752eb58b0b158d6129f3776
+github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0
+github.com/syndtr/gocapability/capability e7cb7fa329f456b3855136a2642b197bad7366ba
+github.com/urfave/cli d53eb991652b1d438abdd34ce4bfa3ef1539108e
+github.com/vishvananda/netlink 1e2e08e8a2dcdacaae3f14ac44c5cfa31361f270
+golang.org/x/sys 9a7256cb28ed514b4e1e5f68959914c4c28a92e0 https://github.com/golang/sys
diff --git a/vendor/github.com/stevvooe/continuity/hardlinks_windows.go b/vendor/github.com/stevvooe/continuity/hardlinks_windows.go
deleted file mode 100644
index 9d55972..0000000
--- a/vendor/github.com/stevvooe/continuity/hardlinks_windows.go
+++ /dev/null
@@ -1,5 +0,0 @@
-package continuity
-
-// NOTE(stevvooe): Obviously, this is not yet implemented. However, the
-// makings of an implementation are available in src/os/types_windows.go. More
-// investigation needs to be done to figure out exactly how to do this.
diff --git a/vendor/github.com/stevvooe/continuity/proto/manifest.pb.go b/vendor/github.com/stevvooe/continuity/proto/manifest.pb.go
deleted file mode 100644
index edb1781..0000000
--- a/vendor/github.com/stevvooe/continuity/proto/manifest.pb.go
+++ /dev/null
@@ -1,98 +0,0 @@
-// Code generated by protoc-gen-go.
-// source: manifest.proto
-// DO NOT EDIT!
-
-/*
-Package proto is a generated protocol buffer package.
-
-It is generated from these files:
- manifest.proto
-
-It has these top-level messages:
- Manifest
- Resource
-*/
-package proto
-
-import proto1 "github.com/golang/protobuf/proto"
-
-// Reference imports to suppress errors if they are not otherwise used.
-var _ = proto1.Marshal
-
-// Manifest specifies the entries in a container bundle, keyed and sorted by
-// path.
-type Manifest struct {
- Resource []*Resource `protobuf:"bytes,1,rep,name=resource" json:"resource,omitempty"`
-}
-
-func (m *Manifest) Reset() { *m = Manifest{} }
-func (m *Manifest) String() string { return proto1.CompactTextString(m) }
-func (*Manifest) ProtoMessage() {}
-
-func (m *Manifest) GetResource() []*Resource {
- if m != nil {
- return m.Resource
- }
- return nil
-}
-
-type Resource struct {
- // Path specifies the path from the bundle root. If more than one
- // path is present, the entry may represent a hardlink, rather than using
- // a link target. The path format is operating system specific.
- Path []string `protobuf:"bytes,1,rep,name=path" json:"path,omitempty"`
- // Uid specifies the user id for the resource. A string type is used for
- // compatibility across different OS.
- Uid string `protobuf:"bytes,2,opt,name=uid" json:"uid,omitempty"`
- // Gid specifies the group id for the resource. A string type is used for
- // compatibility across different OS.
- Gid string `protobuf:"bytes,3,opt,name=gid" json:"gid,omitempty"`
- // user and group are not currently used but their field numbers have been
- // reserved for future use. As such, they are marked as deprecated.
- User string `protobuf:"bytes,4,opt,name=user" json:"user,omitempty"`
- Group string `protobuf:"bytes,5,opt,name=group" json:"group,omitempty"`
- // Mode defines the file mode and permissions. We've used the same
- // bit-packing from Go's os package,
- // http://golang.org/pkg/os/#FileMode, since they've done the work of
- // creating a cross-platform layout.
- Mode uint32 `protobuf:"varint,6,opt,name=mode" json:"mode,omitempty"`
- // Size specifies the size in bytes of the resource. This is only valid
- // for regular files.
- Size uint64 `protobuf:"varint,7,opt,name=size" json:"size,omitempty"`
- // Digest specifies the content digest of the target file. Only valid for
- // regular files. The strings are formatted as <alg>:<digest hex bytes>.
- // The digests are sorted in lexical order and implementations may choose
- // which algorithms they prefer.
- Digest []string `protobuf:"bytes,8,rep,name=digest" json:"digest,omitempty"`
- // Target defines the target of a hard or soft link. Absolute links start
- // with a slash and specify the resource relative to the bundle root.
- // Relative links do not start with a slash and are relative to the
- // resource path.
- Target string `protobuf:"bytes,9,opt,name=target" json:"target,omitempty"`
- // Major specifies the major device number for charactor and block devices.
- Major uint64 `protobuf:"varint,10,opt,name=major" json:"major,omitempty"`
- // Minor specifies the minor device number for charactor and block devices.
- Minor uint64 `protobuf:"varint,11,opt,name=minor" json:"minor,omitempty"`
- // Xattr provides storage for extended attributes for the target resource.
- Xattr map[string][]byte `protobuf:"bytes,12,rep,name=xattr" json:"xattr,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value,proto3"`
- // Ads stores one or more alternate data streams for the target resource.
- Ads map[string][]byte `protobuf:"bytes,13,rep,name=ads" json:"ads,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value,proto3"`
-}
-
-func (m *Resource) Reset() { *m = Resource{} }
-func (m *Resource) String() string { return proto1.CompactTextString(m) }
-func (*Resource) ProtoMessage() {}
-
-func (m *Resource) GetXattr() map[string][]byte {
- if m != nil {
- return m.Xattr
- }
- return nil
-}
-
-func (m *Resource) GetAds() map[string][]byte {
- if m != nil {
- return m.Ads
- }
- return nil
-}
diff --git a/vendor/github.com/tonistiigi/fifo/handle_linux.go b/vendor/github.com/tonistiigi/fifo/handle_linux.go
index 7bda64c..520ef26 100644
--- a/vendor/github.com/tonistiigi/fifo/handle_linux.go
+++ b/vendor/github.com/tonistiigi/fifo/handle_linux.go
@@ -36,7 +36,7 @@
h := &handle{
f: f,
name: fn,
- dev: stat.Dev,
+ dev: uint64(stat.Dev),
ino: stat.Ino,
}
@@ -62,7 +62,7 @@
if err := syscall.Stat(h.procPath(), &stat); err != nil {
return "", errors.Wrapf(err, "path %v could not be statted", h.procPath())
}
- if stat.Dev != h.dev || stat.Ino != h.ino {
+ if uint64(stat.Dev) != h.dev || stat.Ino != h.ino {
return "", errors.Errorf("failed to verify handle %v/%v %v/%v", stat.Dev, h.dev, stat.Ino, h.ino)
}
return h.procPath(), nil
diff --git a/vendor/github.com/tonistiigi/fifo/handle_nolinux.go b/vendor/github.com/tonistiigi/fifo/handle_nolinux.go
index d9648d8..f0c8485 100644
--- a/vendor/github.com/tonistiigi/fifo/handle_nolinux.go
+++ b/vendor/github.com/tonistiigi/fifo/handle_nolinux.go
@@ -23,7 +23,7 @@
h := &handle{
fn: fn,
dev: uint64(stat.Dev),
- ino: stat.Ino,
+ ino: uint64(stat.Ino),
}
return h, nil
@@ -34,7 +34,7 @@
if err := syscall.Stat(h.fn, &stat); err != nil {
return "", errors.Wrapf(err, "path %v could not be statted", h.fn)
}
- if uint64(stat.Dev) != h.dev || stat.Ino != h.ino {
+ if uint64(stat.Dev) != h.dev || uint64(stat.Ino) != h.ino {
return "", errors.Errorf("failed to verify handle %v/%v %v/%v for %v", stat.Dev, h.dev, stat.Ino, h.ino, h.fn)
}
return h.fn, nil
diff --git a/windows/container.go b/windows/container.go
index 6f7adc5..186067f 100644
--- a/windows/container.go
+++ b/windows/container.go
@@ -4,6 +4,7 @@
import (
"encoding/json"
+ "fmt"
"sync"
"time"
@@ -101,6 +102,20 @@
return nil
}
+func (c *container) Pause(ctx context.Context) error {
+ if c.ctr.GetConfiguration().UseHyperV == false {
+ return fmt.Errorf("Windows non-HyperV containers do not support pause")
+ }
+ return c.ctr.Pause()
+}
+
+func (c *container) Resume(ctx context.Context) error {
+ if c.ctr.GetConfiguration().UseHyperV == false {
+ return fmt.Errorf("Windows non-HyperV containers do not support resume")
+ }
+ return c.ctr.Resume()
+}
+
func (c *container) State(ctx context.Context) (containerd.State, error) {
return c, nil
}
diff --git a/windows/hcs/hcs.go b/windows/hcs/hcs.go
index 27794e9..4f54fe4 100644
--- a/windows/hcs/hcs.go
+++ b/windows/hcs/hcs.go
@@ -299,6 +299,10 @@
return c.processes[0].ExitCode()
}
+func (c *Container) GetConfiguration() Configuration {
+ return c.conf
+}
+
func (c *Container) AddProcess(ctx context.Context, spec specs.Process, io *IO) (*Process, error) {
if len(c.processes) == 0 {
return nil, errors.New("container not started")