From 085d2ef7594be22872b44e801e09fb46b01c575b Mon Sep 17 00:00:00 2001 From: Mahmood Ali Date: Sun, 28 Apr 2019 17:27:23 -0400 Subject: [PATCH 1/5] executor: scaffolding for executor grpc handling Prepare executor to handle streaming exec API calls that reuse drivers protobuf structs. --- drivers/shared/executor/client.go | 73 +++++++ drivers/shared/executor/executor.go | 3 + drivers/shared/executor/proto/executor.pb.go | 219 ++++++++++++------- drivers/shared/executor/proto/executor.proto | 1 + drivers/shared/executor/server.go | 16 ++ 5 files changed, 236 insertions(+), 76 deletions(-) diff --git a/drivers/shared/executor/client.go b/drivers/shared/executor/client.go index 101583605..dec2c9b3a 100644 --- a/drivers/shared/executor/client.go +++ b/drivers/shared/executor/client.go @@ -13,7 +13,9 @@ import ( hclog "github.com/hashicorp/go-hclog" cstructs "github.com/hashicorp/nomad/client/structs" "github.com/hashicorp/nomad/drivers/shared/executor/proto" + "github.com/hashicorp/nomad/helper/pluginutils/grpcutils" "github.com/hashicorp/nomad/plugins/drivers" + dproto "github.com/hashicorp/nomad/plugins/drivers/proto" ) var _ Executor = (*grpcExecutorClient)(nil) @@ -181,3 +183,74 @@ func (c *grpcExecutorClient) Exec(deadline time.Time, cmd string, args []string) return resp.Output, int(resp.ExitCode), nil } + +func (d *grpcExecutorClient) ExecStreaming(ctx context.Context, + command []string, + tty bool, + execStream drivers.ExecTaskStream) error { + + err := d.execStreaming(ctx, command, tty, execStream) + if err != nil { + return grpcutils.HandleGrpcErr(err, d.doneCtx) + } + return nil +} + +func (d *grpcExecutorClient) execStreaming(ctx context.Context, + command []string, + tty bool, + execStream drivers.ExecTaskStream) error { + + stream, err := d.client.ExecStreaming(ctx) + if err != nil { + return err + } + + err = stream.Send(&dproto.ExecTaskStreamingRequest{ + Setup: &dproto.ExecTaskStreamingRequest_Setup{ + Command: command, + Tty: tty, + }, + }) + if err != nil { + return err + } + + errCh := make(chan error, 1) + go func() { + for { + m, err := execStream.Recv() + if err == io.EOF { + return + } else if err != nil { + errCh <- err + return + } + + if err := stream.Send(m); err != nil { + errCh <- err + return + } + + } + }() + + for { + select { + case err := <-errCh: + return err + default: + } + + m, err := stream.Recv() + if err == io.EOF { + return nil + } else if err != nil { + return err + } + + if err := execStream.Send(m); err != nil { + return err + } + } +} diff --git a/drivers/shared/executor/executor.go b/drivers/shared/executor/executor.go index 699d3247a..f077bd488 100644 --- a/drivers/shared/executor/executor.go +++ b/drivers/shared/executor/executor.go @@ -77,6 +77,9 @@ type Executor interface { // Exec executes the given command and args inside the executor context // and returns the output and exit code. Exec(deadline time.Time, cmd string, args []string) ([]byte, int, error) + + ExecStreaming(ctx context.Context, cmd []string, tty bool, + stream drivers.ExecTaskStream) error } // ExecCommand holds the user command, args, and other isolation related diff --git a/drivers/shared/executor/proto/executor.pb.go b/drivers/shared/executor/proto/executor.pb.go index a108c7f5b..5b281f34c 100644 --- a/drivers/shared/executor/proto/executor.pb.go +++ b/drivers/shared/executor/proto/executor.pb.go @@ -47,7 +47,7 @@ func (m *LaunchRequest) Reset() { *m = LaunchRequest{} } func (m *LaunchRequest) String() string { return proto.CompactTextString(m) } func (*LaunchRequest) ProtoMessage() {} func (*LaunchRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_executor_1eb9aa6040002cd3, []int{0} + return fileDescriptor_executor_5ea6ca9df3b0f07e, []int{0} } func (m *LaunchRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_LaunchRequest.Unmarshal(m, b) @@ -162,7 +162,7 @@ func (m *LaunchResponse) Reset() { *m = LaunchResponse{} } func (m *LaunchResponse) String() string { return proto.CompactTextString(m) } func (*LaunchResponse) ProtoMessage() {} func (*LaunchResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_executor_1eb9aa6040002cd3, []int{1} + return fileDescriptor_executor_5ea6ca9df3b0f07e, []int{1} } func (m *LaunchResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_LaunchResponse.Unmarshal(m, b) @@ -199,7 +199,7 @@ func (m *WaitRequest) Reset() { *m = WaitRequest{} } func (m *WaitRequest) String() string { return proto.CompactTextString(m) } func (*WaitRequest) ProtoMessage() {} func (*WaitRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_executor_1eb9aa6040002cd3, []int{2} + return fileDescriptor_executor_5ea6ca9df3b0f07e, []int{2} } func (m *WaitRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_WaitRequest.Unmarshal(m, b) @@ -230,7 +230,7 @@ func (m *WaitResponse) Reset() { *m = WaitResponse{} } func (m *WaitResponse) String() string { return proto.CompactTextString(m) } func (*WaitResponse) ProtoMessage() {} func (*WaitResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_executor_1eb9aa6040002cd3, []int{3} + return fileDescriptor_executor_5ea6ca9df3b0f07e, []int{3} } func (m *WaitResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_WaitResponse.Unmarshal(m, b) @@ -269,7 +269,7 @@ func (m *ShutdownRequest) Reset() { *m = ShutdownRequest{} } func (m *ShutdownRequest) String() string { return proto.CompactTextString(m) } func (*ShutdownRequest) ProtoMessage() {} func (*ShutdownRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_executor_1eb9aa6040002cd3, []int{4} + return fileDescriptor_executor_5ea6ca9df3b0f07e, []int{4} } func (m *ShutdownRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ShutdownRequest.Unmarshal(m, b) @@ -313,7 +313,7 @@ func (m *ShutdownResponse) Reset() { *m = ShutdownResponse{} } func (m *ShutdownResponse) String() string { return proto.CompactTextString(m) } func (*ShutdownResponse) ProtoMessage() {} func (*ShutdownResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_executor_1eb9aa6040002cd3, []int{5} + return fileDescriptor_executor_5ea6ca9df3b0f07e, []int{5} } func (m *ShutdownResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ShutdownResponse.Unmarshal(m, b) @@ -344,7 +344,7 @@ func (m *UpdateResourcesRequest) Reset() { *m = UpdateResourcesRequest{} func (m *UpdateResourcesRequest) String() string { return proto.CompactTextString(m) } func (*UpdateResourcesRequest) ProtoMessage() {} func (*UpdateResourcesRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_executor_1eb9aa6040002cd3, []int{6} + return fileDescriptor_executor_5ea6ca9df3b0f07e, []int{6} } func (m *UpdateResourcesRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_UpdateResourcesRequest.Unmarshal(m, b) @@ -381,7 +381,7 @@ func (m *UpdateResourcesResponse) Reset() { *m = UpdateResourcesResponse func (m *UpdateResourcesResponse) String() string { return proto.CompactTextString(m) } func (*UpdateResourcesResponse) ProtoMessage() {} func (*UpdateResourcesResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_executor_1eb9aa6040002cd3, []int{7} + return fileDescriptor_executor_5ea6ca9df3b0f07e, []int{7} } func (m *UpdateResourcesResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_UpdateResourcesResponse.Unmarshal(m, b) @@ -411,7 +411,7 @@ func (m *VersionRequest) Reset() { *m = VersionRequest{} } func (m *VersionRequest) String() string { return proto.CompactTextString(m) } func (*VersionRequest) ProtoMessage() {} func (*VersionRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_executor_1eb9aa6040002cd3, []int{8} + return fileDescriptor_executor_5ea6ca9df3b0f07e, []int{8} } func (m *VersionRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_VersionRequest.Unmarshal(m, b) @@ -442,7 +442,7 @@ func (m *VersionResponse) Reset() { *m = VersionResponse{} } func (m *VersionResponse) String() string { return proto.CompactTextString(m) } func (*VersionResponse) ProtoMessage() {} func (*VersionResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_executor_1eb9aa6040002cd3, []int{9} + return fileDescriptor_executor_5ea6ca9df3b0f07e, []int{9} } func (m *VersionResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_VersionResponse.Unmarshal(m, b) @@ -480,7 +480,7 @@ func (m *StatsRequest) Reset() { *m = StatsRequest{} } func (m *StatsRequest) String() string { return proto.CompactTextString(m) } func (*StatsRequest) ProtoMessage() {} func (*StatsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_executor_1eb9aa6040002cd3, []int{10} + return fileDescriptor_executor_5ea6ca9df3b0f07e, []int{10} } func (m *StatsRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_StatsRequest.Unmarshal(m, b) @@ -518,7 +518,7 @@ func (m *StatsResponse) Reset() { *m = StatsResponse{} } func (m *StatsResponse) String() string { return proto.CompactTextString(m) } func (*StatsResponse) ProtoMessage() {} func (*StatsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_executor_1eb9aa6040002cd3, []int{11} + return fileDescriptor_executor_5ea6ca9df3b0f07e, []int{11} } func (m *StatsResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_StatsResponse.Unmarshal(m, b) @@ -556,7 +556,7 @@ func (m *SignalRequest) Reset() { *m = SignalRequest{} } func (m *SignalRequest) String() string { return proto.CompactTextString(m) } func (*SignalRequest) ProtoMessage() {} func (*SignalRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_executor_1eb9aa6040002cd3, []int{12} + return fileDescriptor_executor_5ea6ca9df3b0f07e, []int{12} } func (m *SignalRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_SignalRequest.Unmarshal(m, b) @@ -593,7 +593,7 @@ func (m *SignalResponse) Reset() { *m = SignalResponse{} } func (m *SignalResponse) String() string { return proto.CompactTextString(m) } func (*SignalResponse) ProtoMessage() {} func (*SignalResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_executor_1eb9aa6040002cd3, []int{13} + return fileDescriptor_executor_5ea6ca9df3b0f07e, []int{13} } func (m *SignalResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_SignalResponse.Unmarshal(m, b) @@ -626,7 +626,7 @@ func (m *ExecRequest) Reset() { *m = ExecRequest{} } func (m *ExecRequest) String() string { return proto.CompactTextString(m) } func (*ExecRequest) ProtoMessage() {} func (*ExecRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_executor_1eb9aa6040002cd3, []int{14} + return fileDescriptor_executor_5ea6ca9df3b0f07e, []int{14} } func (m *ExecRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ExecRequest.Unmarshal(m, b) @@ -679,7 +679,7 @@ func (m *ExecResponse) Reset() { *m = ExecResponse{} } func (m *ExecResponse) String() string { return proto.CompactTextString(m) } func (*ExecResponse) ProtoMessage() {} func (*ExecResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_executor_1eb9aa6040002cd3, []int{15} + return fileDescriptor_executor_5ea6ca9df3b0f07e, []int{15} } func (m *ExecResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ExecResponse.Unmarshal(m, b) @@ -727,7 +727,7 @@ func (m *ProcessState) Reset() { *m = ProcessState{} } func (m *ProcessState) String() string { return proto.CompactTextString(m) } func (*ProcessState) ProtoMessage() {} func (*ProcessState) Descriptor() ([]byte, []int) { - return fileDescriptor_executor_1eb9aa6040002cd3, []int{16} + return fileDescriptor_executor_5ea6ca9df3b0f07e, []int{16} } func (m *ProcessState) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ProcessState.Unmarshal(m, b) @@ -815,6 +815,7 @@ type ExecutorClient interface { Stats(ctx context.Context, in *StatsRequest, opts ...grpc.CallOption) (Executor_StatsClient, error) Signal(ctx context.Context, in *SignalRequest, opts ...grpc.CallOption) (*SignalResponse, error) Exec(ctx context.Context, in *ExecRequest, opts ...grpc.CallOption) (*ExecResponse, error) + ExecStreaming(ctx context.Context, opts ...grpc.CallOption) (Executor_ExecStreamingClient, error) } type executorClient struct { @@ -920,6 +921,37 @@ func (c *executorClient) Exec(ctx context.Context, in *ExecRequest, opts ...grpc return out, nil } +func (c *executorClient) ExecStreaming(ctx context.Context, opts ...grpc.CallOption) (Executor_ExecStreamingClient, error) { + stream, err := c.cc.NewStream(ctx, &_Executor_serviceDesc.Streams[1], "/hashicorp.nomad.plugins.executor.proto.Executor/ExecStreaming", opts...) + if err != nil { + return nil, err + } + x := &executorExecStreamingClient{stream} + return x, nil +} + +type Executor_ExecStreamingClient interface { + Send(*proto1.ExecTaskStreamingRequest) error + Recv() (*proto1.ExecTaskStreamingResponse, error) + grpc.ClientStream +} + +type executorExecStreamingClient struct { + grpc.ClientStream +} + +func (x *executorExecStreamingClient) Send(m *proto1.ExecTaskStreamingRequest) error { + return x.ClientStream.SendMsg(m) +} + +func (x *executorExecStreamingClient) Recv() (*proto1.ExecTaskStreamingResponse, error) { + m := new(proto1.ExecTaskStreamingResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + // ExecutorServer is the server API for Executor service. type ExecutorServer interface { Launch(context.Context, *LaunchRequest) (*LaunchResponse, error) @@ -930,6 +962,7 @@ type ExecutorServer interface { Stats(*StatsRequest, Executor_StatsServer) error Signal(context.Context, *SignalRequest) (*SignalResponse, error) Exec(context.Context, *ExecRequest) (*ExecResponse, error) + ExecStreaming(Executor_ExecStreamingServer) error } func RegisterExecutorServer(s *grpc.Server, srv ExecutorServer) { @@ -1083,6 +1116,32 @@ func _Executor_Exec_Handler(srv interface{}, ctx context.Context, dec func(inter return interceptor(ctx, in, info, handler) } +func _Executor_ExecStreaming_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(ExecutorServer).ExecStreaming(&executorExecStreamingServer{stream}) +} + +type Executor_ExecStreamingServer interface { + Send(*proto1.ExecTaskStreamingResponse) error + Recv() (*proto1.ExecTaskStreamingRequest, error) + grpc.ServerStream +} + +type executorExecStreamingServer struct { + grpc.ServerStream +} + +func (x *executorExecStreamingServer) Send(m *proto1.ExecTaskStreamingResponse) error { + return x.ServerStream.SendMsg(m) +} + +func (x *executorExecStreamingServer) Recv() (*proto1.ExecTaskStreamingRequest, error) { + m := new(proto1.ExecTaskStreamingRequest) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + var _Executor_serviceDesc = grpc.ServiceDesc{ ServiceName: "hashicorp.nomad.plugins.executor.proto.Executor", HandlerType: (*ExecutorServer)(nil), @@ -1122,70 +1181,78 @@ var _Executor_serviceDesc = grpc.ServiceDesc{ Handler: _Executor_Stats_Handler, ServerStreams: true, }, + { + StreamName: "ExecStreaming", + Handler: _Executor_ExecStreaming_Handler, + ServerStreams: true, + ClientStreams: true, + }, }, Metadata: "drivers/shared/executor/proto/executor.proto", } func init() { - proto.RegisterFile("drivers/shared/executor/proto/executor.proto", fileDescriptor_executor_1eb9aa6040002cd3) + proto.RegisterFile("drivers/shared/executor/proto/executor.proto", fileDescriptor_executor_5ea6ca9df3b0f07e) } -var fileDescriptor_executor_1eb9aa6040002cd3 = []byte{ - // 885 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0x4b, 0x6f, 0xe4, 0x44, - 0x10, 0x5e, 0xc7, 0x99, 0x57, 0xcd, 0xe4, 0xa1, 0x16, 0x0a, 0x5e, 0x73, 0xd8, 0xc1, 0x07, 0x76, - 0x04, 0x8b, 0x27, 0xca, 0xbe, 0xb8, 0x00, 0x12, 0xc9, 0xc2, 0x25, 0xac, 0x22, 0x67, 0x61, 0x25, - 0x0e, 0x0c, 0x1d, 0xbb, 0xb1, 0x5b, 0x99, 0x71, 0x9b, 0xee, 0xf6, 0x30, 0x48, 0x48, 0x9c, 0xf8, - 0x07, 0xfc, 0x52, 0x8e, 0x9c, 0x50, 0xbf, 0x9c, 0x99, 0xec, 0x12, 0x79, 0x40, 0x9c, 0xa6, 0xab, - 0x5c, 0xdf, 0x57, 0x55, 0xdd, 0x55, 0xdf, 0xc0, 0xa3, 0x8c, 0xd3, 0x25, 0xe1, 0x62, 0x2a, 0x0a, - 0xcc, 0x49, 0x36, 0x25, 0x2b, 0x92, 0xd6, 0x92, 0xf1, 0x69, 0xc5, 0x99, 0x64, 0x8d, 0x19, 0x6b, - 0x13, 0x7d, 0x50, 0x60, 0x51, 0xd0, 0x94, 0xf1, 0x2a, 0x2e, 0xd9, 0x02, 0x67, 0x71, 0x35, 0xaf, - 0x73, 0x5a, 0x8a, 0x78, 0x33, 0x2e, 0x7c, 0x90, 0x33, 0x96, 0xcf, 0x89, 0x21, 0xb9, 0xaa, 0x7f, - 0x9c, 0x4a, 0xba, 0x20, 0x42, 0xe2, 0x45, 0x65, 0x03, 0x3e, 0xcd, 0xa9, 0x2c, 0xea, 0xab, 0x38, - 0x65, 0x8b, 0x69, 0xc3, 0x39, 0xd5, 0x9c, 0x53, 0xcb, 0x39, 0x75, 0x95, 0x99, 0x4a, 0x8c, 0x65, - 0xe0, 0xd1, 0x9f, 0x3e, 0xec, 0x9d, 0xe3, 0xba, 0x4c, 0x8b, 0x84, 0xfc, 0x54, 0x13, 0x21, 0xd1, - 0x21, 0xf8, 0xe9, 0x22, 0x0b, 0xbc, 0xb1, 0x37, 0x19, 0x24, 0xea, 0x88, 0x10, 0xec, 0x62, 0x9e, - 0x8b, 0x60, 0x67, 0xec, 0x4f, 0x06, 0x89, 0x3e, 0xa3, 0x97, 0x30, 0xe0, 0x44, 0xb0, 0x9a, 0xa7, - 0x44, 0x04, 0xfe, 0xd8, 0x9b, 0x0c, 0x4f, 0x8e, 0xe3, 0x7f, 0xea, 0xc9, 0xe6, 0x37, 0x29, 0xe3, - 0xc4, 0xe1, 0x92, 0x1b, 0x0a, 0xf4, 0x00, 0x86, 0x42, 0x66, 0xac, 0x96, 0xb3, 0x0a, 0xcb, 0x22, - 0xd8, 0xd5, 0xd9, 0xc1, 0xb8, 0x2e, 0xb0, 0x2c, 0x6c, 0x00, 0xe1, 0xdc, 0x04, 0x74, 0x9a, 0x00, - 0xc2, 0xb9, 0x0e, 0x38, 0x04, 0x9f, 0x94, 0xcb, 0xa0, 0xab, 0x8b, 0x54, 0x47, 0x55, 0x77, 0x2d, - 0x08, 0x0f, 0x7a, 0x3a, 0x56, 0x9f, 0xd1, 0x7d, 0xe8, 0x4b, 0x2c, 0xae, 0x67, 0x19, 0xe5, 0x41, - 0x5f, 0xfb, 0x7b, 0xca, 0x3e, 0xa3, 0x1c, 0x3d, 0x84, 0x03, 0x57, 0xcf, 0x6c, 0x4e, 0x17, 0x54, - 0x8a, 0x60, 0x30, 0xf6, 0x26, 0xfd, 0x64, 0xdf, 0xb9, 0xcf, 0xb5, 0x17, 0x1d, 0xc3, 0x3b, 0x57, - 0x58, 0xd0, 0x74, 0x56, 0x71, 0x96, 0x12, 0x21, 0x66, 0x69, 0xce, 0x59, 0x5d, 0x05, 0xa0, 0xa3, - 0x91, 0xfe, 0x76, 0x61, 0x3e, 0x9d, 0xea, 0x2f, 0xe8, 0x0c, 0xba, 0x0b, 0x56, 0x97, 0x52, 0x04, - 0xc3, 0xb1, 0x3f, 0x19, 0x9e, 0x3c, 0x6a, 0x79, 0x55, 0x5f, 0x2b, 0x50, 0x62, 0xb1, 0xe8, 0x2b, - 0xe8, 0x65, 0x64, 0x49, 0xd5, 0x8d, 0x8f, 0x34, 0xcd, 0xc7, 0x2d, 0x69, 0xce, 0x34, 0x2a, 0x71, - 0xe8, 0xe8, 0x07, 0xd8, 0x77, 0x6f, 0x2e, 0x2a, 0x56, 0x0a, 0x82, 0x5e, 0x42, 0xcf, 0x36, 0xa3, - 0x1f, 0x7e, 0x78, 0xf2, 0x24, 0x6e, 0x37, 0xa0, 0xb1, 0x6d, 0xf4, 0x52, 0x62, 0x49, 0x12, 0x47, - 0x12, 0xed, 0xc1, 0xf0, 0x35, 0xa6, 0xd2, 0xce, 0x54, 0xf4, 0x3d, 0x8c, 0x8c, 0xf9, 0x3f, 0xa5, - 0x3b, 0x87, 0x83, 0xcb, 0xa2, 0x96, 0x19, 0xfb, 0xb9, 0x74, 0x63, 0x7c, 0x04, 0x5d, 0x41, 0xf3, - 0x12, 0xcf, 0xed, 0x24, 0x5b, 0x0b, 0xbd, 0x0f, 0xa3, 0x9c, 0xe3, 0x94, 0xcc, 0x2a, 0xc2, 0x29, - 0xcb, 0x82, 0x9d, 0xb1, 0x37, 0xf1, 0x93, 0xa1, 0xf6, 0x5d, 0x68, 0x57, 0x84, 0xe0, 0xf0, 0x86, - 0xcd, 0x54, 0x1c, 0x15, 0x70, 0xf4, 0x4d, 0x95, 0xa9, 0xa4, 0xcd, 0xf4, 0xda, 0x44, 0x1b, 0x9b, - 0xe0, 0xfd, 0xe7, 0x4d, 0x88, 0xee, 0xc3, 0xbb, 0x6f, 0x64, 0xb2, 0x45, 0x1c, 0xc2, 0xfe, 0xb7, - 0x84, 0x0b, 0xca, 0x5c, 0x97, 0xd1, 0x47, 0x70, 0xd0, 0x78, 0xec, 0xdd, 0x06, 0xd0, 0x5b, 0x1a, - 0x97, 0xed, 0xdc, 0x99, 0xd1, 0x87, 0x30, 0x52, 0xf7, 0xd6, 0x54, 0x1e, 0x42, 0x9f, 0x96, 0x92, - 0xf0, 0xa5, 0xbd, 0x24, 0x3f, 0x69, 0xec, 0xe8, 0x35, 0xec, 0xd9, 0x58, 0x4b, 0xfb, 0x25, 0x74, - 0x84, 0x72, 0x6c, 0xd9, 0xe2, 0x2b, 0x2c, 0xae, 0x0d, 0x91, 0x81, 0x47, 0x0f, 0x61, 0xef, 0x52, - 0xbf, 0xc4, 0xdb, 0x1f, 0xaa, 0xe3, 0x1e, 0x4a, 0x35, 0xeb, 0x02, 0x6d, 0xfb, 0xd7, 0x30, 0x7c, - 0xb1, 0x22, 0xa9, 0x03, 0x3e, 0x83, 0x7e, 0x46, 0x70, 0x36, 0xa7, 0x25, 0xb1, 0x45, 0x85, 0xb1, - 0x51, 0xcb, 0xd8, 0xa9, 0x65, 0xfc, 0xca, 0xa9, 0x65, 0xd2, 0xc4, 0x3a, 0x81, 0xdb, 0x79, 0x53, - 0xe0, 0xfc, 0x1b, 0x81, 0x8b, 0x4e, 0x61, 0x64, 0x92, 0xd9, 0xfe, 0x8f, 0xa0, 0xcb, 0x6a, 0x59, - 0xd5, 0x52, 0xe7, 0x1a, 0x25, 0xd6, 0x42, 0xef, 0xc1, 0x80, 0xac, 0xa8, 0x9c, 0xa5, 0x2c, 0x23, - 0x9a, 0xb3, 0x93, 0xf4, 0x95, 0xe3, 0x94, 0x65, 0x24, 0xfa, 0xdd, 0x83, 0xd1, 0xfa, 0xc4, 0xaa, - 0xdc, 0x15, 0xcd, 0x6c, 0xa7, 0xea, 0x78, 0x27, 0x7e, 0xed, 0x6e, 0xfc, 0xf5, 0xbb, 0x41, 0x31, - 0xec, 0xaa, 0xff, 0x01, 0x2d, 0x93, 0x77, 0xb7, 0xad, 0xe3, 0x4e, 0xfe, 0xea, 0x41, 0xff, 0x85, - 0x5d, 0x24, 0xf4, 0x0b, 0x74, 0xcd, 0xf6, 0xa3, 0xa7, 0x6d, 0xb7, 0x6e, 0xe3, 0x1f, 0x22, 0x7c, - 0xb6, 0x2d, 0xcc, 0xbe, 0xdf, 0x3d, 0x24, 0x60, 0x57, 0xe9, 0x00, 0x7a, 0xdc, 0x96, 0x61, 0x4d, - 0x44, 0xc2, 0x27, 0xdb, 0x81, 0x9a, 0xa4, 0xbf, 0x41, 0xdf, 0xad, 0x33, 0x7a, 0xde, 0x96, 0xe3, - 0x96, 0x9c, 0x84, 0x9f, 0x6c, 0x0f, 0x6c, 0x0a, 0xf8, 0xc3, 0x83, 0x83, 0x5b, 0x2b, 0x8d, 0x3e, - 0x6b, 0xcb, 0xf7, 0x76, 0xd5, 0x09, 0x3f, 0xff, 0xd7, 0xf8, 0xa6, 0xac, 0x5f, 0xa1, 0x67, 0xb5, - 0x03, 0xb5, 0x7e, 0xd1, 0x4d, 0xf9, 0x09, 0x9f, 0x6f, 0x8d, 0x6b, 0xb2, 0xaf, 0xa0, 0xa3, 0x75, - 0x01, 0xb5, 0x7e, 0xd6, 0x75, 0xed, 0x0a, 0x9f, 0x6e, 0x89, 0x72, 0x79, 0x8f, 0x3d, 0x35, 0xff, - 0x46, 0x58, 0xda, 0xcf, 0xff, 0x86, 0x62, 0xb5, 0x9f, 0xff, 0x5b, 0xfa, 0xa5, 0xe7, 0x5f, 0xad, - 0x61, 0xfb, 0xf9, 0x5f, 0xd3, 0xbb, 0xf6, 0xf3, 0xbf, 0xae, 0x5b, 0xd1, 0xbd, 0x2f, 0x7a, 0xdf, - 0x75, 0x8c, 0x30, 0x74, 0xf5, 0xcf, 0xe3, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x2f, 0x08, 0xfe, - 0x1f, 0xaa, 0x0a, 0x00, 0x00, +var fileDescriptor_executor_5ea6ca9df3b0f07e = []byte{ + // 919 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0x5f, 0x6f, 0xdc, 0x44, + 0x10, 0xaf, 0xeb, 0xdc, 0xbf, 0xb9, 0xbb, 0x24, 0x5a, 0xa1, 0xe0, 0x9a, 0x87, 0x1e, 0x7e, 0xa0, + 0x27, 0x28, 0xbe, 0x28, 0xfd, 0xc7, 0x0b, 0x14, 0x91, 0x14, 0x5e, 0x42, 0x15, 0x39, 0x85, 0x4a, + 0x3c, 0x70, 0x6c, 0xec, 0xc5, 0x5e, 0xe5, 0xce, 0x6b, 0x76, 0xd7, 0x47, 0x90, 0x90, 0x78, 0xe2, + 0x1b, 0x80, 0xc4, 0xe7, 0xe4, 0x13, 0xa0, 0xfd, 0xe7, 0xdc, 0xa5, 0xa5, 0xf2, 0x15, 0xf1, 0x74, + 0x3b, 0xe3, 0xf9, 0xfd, 0x66, 0x66, 0x77, 0xe6, 0x77, 0x70, 0x3f, 0xe3, 0x74, 0x45, 0xb8, 0x98, + 0x89, 0x02, 0x73, 0x92, 0xcd, 0xc8, 0x15, 0x49, 0x6b, 0xc9, 0xf8, 0xac, 0xe2, 0x4c, 0xb2, 0xc6, + 0x8c, 0xb5, 0x89, 0x3e, 0x28, 0xb0, 0x28, 0x68, 0xca, 0x78, 0x15, 0x97, 0x6c, 0x89, 0xb3, 0xb8, + 0x5a, 0xd4, 0x39, 0x2d, 0x45, 0xbc, 0x19, 0x17, 0xde, 0xcd, 0x19, 0xcb, 0x17, 0xc4, 0x90, 0x5c, + 0xd4, 0x3f, 0xce, 0x24, 0x5d, 0x12, 0x21, 0xf1, 0xb2, 0xb2, 0x01, 0x9f, 0xe6, 0x54, 0x16, 0xf5, + 0x45, 0x9c, 0xb2, 0xe5, 0xac, 0xe1, 0x9c, 0x69, 0xce, 0x99, 0xe5, 0x9c, 0xb9, 0xca, 0x4c, 0x25, + 0xc6, 0x32, 0xf0, 0xe8, 0x6f, 0x1f, 0xc6, 0xa7, 0xb8, 0x2e, 0xd3, 0x22, 0x21, 0x3f, 0xd5, 0x44, + 0x48, 0xb4, 0x0f, 0x7e, 0xba, 0xcc, 0x02, 0x6f, 0xe2, 0x4d, 0x07, 0x89, 0x3a, 0x22, 0x04, 0x3b, + 0x98, 0xe7, 0x22, 0xb8, 0x3d, 0xf1, 0xa7, 0x83, 0x44, 0x9f, 0xd1, 0x73, 0x18, 0x70, 0x22, 0x58, + 0xcd, 0x53, 0x22, 0x02, 0x7f, 0xe2, 0x4d, 0x87, 0x47, 0x87, 0xf1, 0xbf, 0xf5, 0x64, 0xf3, 0x9b, + 0x94, 0x71, 0xe2, 0x70, 0xc9, 0x35, 0x05, 0xba, 0x0b, 0x43, 0x21, 0x33, 0x56, 0xcb, 0x79, 0x85, + 0x65, 0x11, 0xec, 0xe8, 0xec, 0x60, 0x5c, 0x67, 0x58, 0x16, 0x36, 0x80, 0x70, 0x6e, 0x02, 0x3a, + 0x4d, 0x00, 0xe1, 0x5c, 0x07, 0xec, 0x83, 0x4f, 0xca, 0x55, 0xd0, 0xd5, 0x45, 0xaa, 0xa3, 0xaa, + 0xbb, 0x16, 0x84, 0x07, 0x3d, 0x1d, 0xab, 0xcf, 0xe8, 0x0e, 0xf4, 0x25, 0x16, 0x97, 0xf3, 0x8c, + 0xf2, 0xa0, 0xaf, 0xfd, 0x3d, 0x65, 0x9f, 0x50, 0x8e, 0xee, 0xc1, 0x9e, 0xab, 0x67, 0xbe, 0xa0, + 0x4b, 0x2a, 0x45, 0x30, 0x98, 0x78, 0xd3, 0x7e, 0xb2, 0xeb, 0xdc, 0xa7, 0xda, 0x8b, 0x0e, 0xe1, + 0x9d, 0x0b, 0x2c, 0x68, 0x3a, 0xaf, 0x38, 0x4b, 0x89, 0x10, 0xf3, 0x34, 0xe7, 0xac, 0xae, 0x02, + 0xd0, 0xd1, 0x48, 0x7f, 0x3b, 0x33, 0x9f, 0x8e, 0xf5, 0x17, 0x74, 0x02, 0xdd, 0x25, 0xab, 0x4b, + 0x29, 0x82, 0xe1, 0xc4, 0x9f, 0x0e, 0x8f, 0xee, 0xb7, 0xbc, 0xaa, 0xaf, 0x15, 0x28, 0xb1, 0x58, + 0xf4, 0x15, 0xf4, 0x32, 0xb2, 0xa2, 0xea, 0xc6, 0x47, 0x9a, 0xe6, 0xe3, 0x96, 0x34, 0x27, 0x1a, + 0x95, 0x38, 0x74, 0xf4, 0x03, 0xec, 0xba, 0x37, 0x17, 0x15, 0x2b, 0x05, 0x41, 0xcf, 0xa1, 0x67, + 0x9b, 0xd1, 0x0f, 0x3f, 0x3c, 0x7a, 0x18, 0xb7, 0x1b, 0xd0, 0xd8, 0x36, 0x7a, 0x2e, 0xb1, 0x24, + 0x89, 0x23, 0x89, 0xc6, 0x30, 0x7c, 0x89, 0xa9, 0xb4, 0x33, 0x15, 0x7d, 0x0f, 0x23, 0x63, 0xfe, + 0x4f, 0xe9, 0x4e, 0x61, 0xef, 0xbc, 0xa8, 0x65, 0xc6, 0x7e, 0x2e, 0xdd, 0x18, 0x1f, 0x40, 0x57, + 0xd0, 0xbc, 0xc4, 0x0b, 0x3b, 0xc9, 0xd6, 0x42, 0xef, 0xc3, 0x28, 0xe7, 0x38, 0x25, 0xf3, 0x8a, + 0x70, 0xca, 0xb2, 0xe0, 0xf6, 0xc4, 0x9b, 0xfa, 0xc9, 0x50, 0xfb, 0xce, 0xb4, 0x2b, 0x42, 0xb0, + 0x7f, 0xcd, 0x66, 0x2a, 0x8e, 0x0a, 0x38, 0xf8, 0xa6, 0xca, 0x54, 0xd2, 0x66, 0x7a, 0x6d, 0xa2, + 0x8d, 0x4d, 0xf0, 0xfe, 0xf3, 0x26, 0x44, 0x77, 0xe0, 0xdd, 0x57, 0x32, 0xd9, 0x22, 0xf6, 0x61, + 0xf7, 0x5b, 0xc2, 0x05, 0x65, 0xae, 0xcb, 0xe8, 0x23, 0xd8, 0x6b, 0x3c, 0xf6, 0x6e, 0x03, 0xe8, + 0xad, 0x8c, 0xcb, 0x76, 0xee, 0xcc, 0xe8, 0x43, 0x18, 0xa9, 0x7b, 0x6b, 0x2a, 0x0f, 0xa1, 0x4f, + 0x4b, 0x49, 0xf8, 0xca, 0x5e, 0x92, 0x9f, 0x34, 0x76, 0xf4, 0x12, 0xc6, 0x36, 0xd6, 0xd2, 0x7e, + 0x09, 0x1d, 0xa1, 0x1c, 0x5b, 0xb6, 0xf8, 0x02, 0x8b, 0x4b, 0x43, 0x64, 0xe0, 0xd1, 0x3d, 0x18, + 0x9f, 0xeb, 0x97, 0x78, 0xfd, 0x43, 0x75, 0xdc, 0x43, 0xa9, 0x66, 0x5d, 0xa0, 0x6d, 0xff, 0x12, + 0x86, 0xcf, 0xae, 0x48, 0xea, 0x80, 0x8f, 0xa1, 0x9f, 0x11, 0x9c, 0x2d, 0x68, 0x49, 0x6c, 0x51, + 0x61, 0x6c, 0xd4, 0x32, 0x76, 0x6a, 0x19, 0xbf, 0x70, 0x6a, 0x99, 0x34, 0xb1, 0x4e, 0xe0, 0x6e, + 0xbf, 0x2a, 0x70, 0xfe, 0xb5, 0xc0, 0x45, 0xc7, 0x30, 0x32, 0xc9, 0x6c, 0xff, 0x07, 0xd0, 0x65, + 0xb5, 0xac, 0x6a, 0xa9, 0x73, 0x8d, 0x12, 0x6b, 0xa1, 0xf7, 0x60, 0x40, 0xae, 0xa8, 0x9c, 0xa7, + 0x2c, 0x23, 0x9a, 0xb3, 0x93, 0xf4, 0x95, 0xe3, 0x98, 0x65, 0x24, 0xfa, 0xdd, 0x83, 0xd1, 0xfa, + 0xc4, 0xaa, 0xdc, 0x15, 0xcd, 0x6c, 0xa7, 0xea, 0xf8, 0x46, 0xfc, 0xda, 0xdd, 0xf8, 0xeb, 0x77, + 0x83, 0x62, 0xd8, 0x51, 0xff, 0x03, 0x5a, 0x26, 0xdf, 0xdc, 0xb6, 0x8e, 0x3b, 0xfa, 0x73, 0x00, + 0xfd, 0x67, 0x76, 0x91, 0xd0, 0x2f, 0xd0, 0x35, 0xdb, 0x8f, 0x1e, 0xb5, 0xdd, 0xba, 0x8d, 0x7f, + 0x88, 0xf0, 0xf1, 0xb6, 0x30, 0xfb, 0x7e, 0xb7, 0x90, 0x80, 0x1d, 0xa5, 0x03, 0xe8, 0x41, 0x5b, + 0x86, 0x35, 0x11, 0x09, 0x1f, 0x6e, 0x07, 0x6a, 0x92, 0xfe, 0x06, 0x7d, 0xb7, 0xce, 0xe8, 0x49, + 0x5b, 0x8e, 0x1b, 0x72, 0x12, 0x7e, 0xb2, 0x3d, 0xb0, 0x29, 0xe0, 0x0f, 0x0f, 0xf6, 0x6e, 0xac, + 0x34, 0xfa, 0xac, 0x2d, 0xdf, 0xeb, 0x55, 0x27, 0x7c, 0xfa, 0xd6, 0xf8, 0xa6, 0xac, 0x5f, 0xa1, + 0x67, 0xb5, 0x03, 0xb5, 0x7e, 0xd1, 0x4d, 0xf9, 0x09, 0x9f, 0x6c, 0x8d, 0x6b, 0xb2, 0x5f, 0x41, + 0x47, 0xeb, 0x02, 0x6a, 0xfd, 0xac, 0xeb, 0xda, 0x15, 0x3e, 0xda, 0x12, 0xe5, 0xf2, 0x1e, 0x7a, + 0x6a, 0xfe, 0x8d, 0xb0, 0xb4, 0x9f, 0xff, 0x0d, 0xc5, 0x6a, 0x3f, 0xff, 0x37, 0xf4, 0x4b, 0xcf, + 0xbf, 0x5a, 0xc3, 0xf6, 0xf3, 0xbf, 0xa6, 0x77, 0xed, 0xe7, 0x7f, 0x5d, 0xb7, 0xa2, 0x5b, 0xe8, + 0x2f, 0x0f, 0xc6, 0xca, 0x75, 0x2e, 0x39, 0xc1, 0x4b, 0x5a, 0xe6, 0xe8, 0x69, 0x4b, 0xf1, 0x56, + 0x28, 0x23, 0xe0, 0x16, 0xe9, 0x4a, 0xf9, 0xfc, 0xed, 0x09, 0x5c, 0x59, 0x53, 0xef, 0xd0, 0xfb, + 0xa2, 0xf7, 0x5d, 0xc7, 0x68, 0x56, 0x57, 0xff, 0x3c, 0xf8, 0x27, 0x00, 0x00, 0xff, 0xff, 0xe4, + 0x09, 0xe7, 0x2c, 0x45, 0x0b, 0x00, 0x00, } diff --git a/drivers/shared/executor/proto/executor.proto b/drivers/shared/executor/proto/executor.proto index 7d721c400..438b5e680 100644 --- a/drivers/shared/executor/proto/executor.proto +++ b/drivers/shared/executor/proto/executor.proto @@ -14,6 +14,7 @@ service Executor { rpc Stats(StatsRequest) returns (stream StatsResponse) {} rpc Signal(SignalRequest) returns (SignalResponse) {} rpc Exec(ExecRequest) returns (ExecResponse) {} + rpc ExecStreaming(stream hashicorp.nomad.plugins.drivers.proto.ExecTaskStreamingRequest) returns (stream hashicorp.nomad.plugins.drivers.proto.ExecTaskStreamingResponse) {} } message LaunchRequest { diff --git a/drivers/shared/executor/server.go b/drivers/shared/executor/server.go index a9028219b..df3b6b4bc 100644 --- a/drivers/shared/executor/server.go +++ b/drivers/shared/executor/server.go @@ -1,6 +1,7 @@ package executor import ( + "fmt" "syscall" "time" @@ -154,3 +155,18 @@ func (s *grpcExecutorServer) Exec(ctx context.Context, req *proto.ExecRequest) ( ExitCode: int32(exit), }, nil } + +func (s *grpcExecutorServer) ExecStreaming(server proto.Executor_ExecStreamingServer) error { + msg, err := server.Recv() + if err != nil { + return fmt.Errorf("failed to receive initial message: %v", err) + } + + if msg.Setup == nil { + return fmt.Errorf("first message should always be setup") + } + + return s.impl.ExecStreaming(server.Context(), + msg.Setup.Command, msg.Setup.Tty, + server) +} From 3055fd53df66f3c626d18360a76fe8fc98407740 Mon Sep 17 00:00:00 2001 From: Mahmood Ali Date: Sun, 28 Apr 2019 17:30:10 -0400 Subject: [PATCH 2/5] executors: implement streaming exec Implements streamign exec handling in both executors (i.e. universal and libcontainer). For creation of TTY, some incidental complexity leaked in. The universal executor uses github.com/kr/pty for creation of TTYs. On the other hand, libcontainer expects a console socket and for libcontainer to create the underlying console object on process start. The caller can then use `libcontainer.utils.RecvFd()` to get tty master end. I chose github.com/kr/pty for managing TTYs here. I tried `github.com/containerd/console` package (which is already imported), but the package did not work as expected on macOS. --- drivers/shared/executor/exec_utils.go | 287 ++++++++++++++++++++++ drivers/shared/executor/executor.go | 48 ++++ drivers/shared/executor/executor_linux.go | 49 ++++ drivers/shared/executor/pty_unix.go | 43 ++++ drivers/shared/executor/pty_windows.go | 23 ++ 5 files changed, 450 insertions(+) create mode 100644 drivers/shared/executor/exec_utils.go create mode 100644 drivers/shared/executor/pty_unix.go create mode 100644 drivers/shared/executor/pty_windows.go diff --git a/drivers/shared/executor/exec_utils.go b/drivers/shared/executor/exec_utils.go new file mode 100644 index 000000000..17194d0db --- /dev/null +++ b/drivers/shared/executor/exec_utils.go @@ -0,0 +1,287 @@ +package executor + +import ( + "context" + "fmt" + "io" + "os" + "os/exec" + "sync" + "syscall" + + hclog "github.com/hashicorp/go-hclog" + "github.com/hashicorp/nomad/plugins/drivers" + dproto "github.com/hashicorp/nomad/plugins/drivers/proto" +) + +// execHelper is a convenient wrapper for starting and executing commands, and handling their output +type execHelper struct { + logger hclog.Logger + + // newTerminal function creates a tty appropriate for the command + // The returned master end of tty function is to be called after process start. + newTerminal func() (master func() (*os.File, error), slave *os.File, err error) + + // setTTY is a callback to configure the command with slave end of the tty of the terminal, when tty is enabled + setTTY func(tty *os.File) error + + // setTTY is a callback to configure the command with std{in|out|err}, when tty is disabled + setIO func(stdin io.Reader, stdout, stderr io.Writer) error + + // processStart starts the process, like `exec.Cmd.Start()` + processStart func() error + + // processWait blocks until command terminates and returns its final state + processWait func() (*os.ProcessState, error) +} + +func (e *execHelper) run(ctx context.Context, tty bool, stream drivers.ExecTaskStream) error { + if tty { + return e.runTTY(ctx, stream) + } + return e.runNoTTY(ctx, stream) +} + +func (e *execHelper) runTTY(ctx context.Context, stream drivers.ExecTaskStream) error { + ptyF, tty, err := e.newTerminal() + if err != nil { + return fmt.Errorf("failed to open a tty: %v", err) + } + defer tty.Close() + + if err := e.setTTY(tty); err != nil { + return fmt.Errorf("failed to set command tty: %v", err) + } + if err := e.processStart(); err != nil { + return fmt.Errorf("failed to start command: %v", err) + } + + var wg sync.WaitGroup + errCh := make(chan error, 3) + + pty, err := ptyF() + if err != nil { + return fmt.Errorf("failed to get tty master: %v", err) + } + + defer pty.Close() + wg.Add(1) + go handleStdin(e.logger, pty, stream, errCh) + // when tty is on, stdout and stderr point to the same pty so only read once + go handleStdout(e.logger, pty, &wg, stream.Send, errCh) + + ps, err := e.processWait() + + // force close streams to close out the stream copying goroutines + tty.Close() + + // wait until we get all process output + wg.Wait() + + // wait to flush out output + stream.Send(cmdExitResult(ps, err)) + + select { + case cerr := <-errCh: + return cerr + default: + return nil + } +} + +func (e *execHelper) runNoTTY(ctx context.Context, stream drivers.ExecTaskStream) error { + var sendLock sync.Mutex + send := func(v *drivers.ExecTaskStreamingResponseMsg) error { + sendLock.Lock() + defer sendLock.Unlock() + + return stream.Send(v) + } + + stdinPr, stdinPw := io.Pipe() + stdoutPr, stdoutPw := io.Pipe() + stderrPr, stderrPw := io.Pipe() + + defer stdoutPw.Close() + defer stderrPw.Close() + + if err := e.setIO(stdinPr, stdoutPw, stderrPw); err != nil { + return fmt.Errorf("failed to set command io: %v", err) + } + + if err := e.processStart(); err != nil { + return fmt.Errorf("failed to start command: %v", err) + } + + var wg sync.WaitGroup + errCh := make(chan error, 3) + + wg.Add(2) + go handleStdin(e.logger, stdinPw, stream, errCh) + go handleStdout(e.logger, stdoutPr, &wg, send, errCh) + go handleStderr(e.logger, stderrPr, &wg, send, errCh) + + ps, err := e.processWait() + + // force close streams to close out the stream copying goroutines + stdinPr.Close() + stdoutPw.Close() + stderrPw.Close() + + // wait until we get all process output + wg.Wait() + + // wait to flush out output + stream.Send(cmdExitResult(ps, err)) + + select { + case cerr := <-errCh: + return cerr + default: + return nil + } +} +func cmdExitResult(ps *os.ProcessState, err error) *drivers.ExecTaskStreamingResponseMsg { + exitCode := -1 + + if ps == nil { + if ee, ok := err.(*exec.ExitError); ok { + ps = ee.ProcessState + } + } + + if ps == nil { + exitCode = -2 + } else if status, ok := ps.Sys().(syscall.WaitStatus); ok { + exitCode = status.ExitStatus() + if status.Signaled() { + const exitSignalBase = 128 + signal := int(status.Signal()) + exitCode = exitSignalBase + signal + } + } + + return &drivers.ExecTaskStreamingResponseMsg{ + Exited: true, + Result: &dproto.ExitResult{ + ExitCode: int32(exitCode), + }, + } +} + +func handleStdin(logger hclog.Logger, stdin io.WriteCloser, stream drivers.ExecTaskStream, errCh chan<- error) { + for { + m, err := stream.Recv() + if isClosedError(err) { + return + } else if err != nil { + errCh <- err + return + } + + if m.Stdin != nil { + if len(m.Stdin.Data) != 0 { + _, err := stdin.Write(m.Stdin.Data) + if err != nil { + errCh <- err + return + } + } + if m.Stdin.Close { + stdin.Close() + } + } else if m.TtySize != nil { + err := setTTYSize(stdin, m.TtySize.Height, m.TtySize.Width) + if err != nil { + errCh <- fmt.Errorf("failed to resize tty: %v", err) + return + } + } else { + // ignore heartbeats + } + } +} + +func handleStdout(logger hclog.Logger, reader io.Reader, wg *sync.WaitGroup, send func(*drivers.ExecTaskStreamingResponseMsg) error, errCh chan<- error) { + defer wg.Done() + + buf := make([]byte, 4096) + for { + n, err := reader.Read(buf) + // always send output first if we read something + if n > 0 { + if err := send(&drivers.ExecTaskStreamingResponseMsg{ + Stdout: &dproto.ExecTaskStreamingIOOperation{ + Data: buf[:n], + }, + }); err != nil { + errCh <- err + return + } + } + + // then process error + if isClosedError(err) { + if err := send(&drivers.ExecTaskStreamingResponseMsg{ + Stdout: &dproto.ExecTaskStreamingIOOperation{ + Close: true, + }, + }); err != nil { + errCh <- err + return + } + return + } else if err != nil { + errCh <- err + return + } + + } +} + +func handleStderr(logger hclog.Logger, reader io.Reader, wg *sync.WaitGroup, send func(*drivers.ExecTaskStreamingResponseMsg) error, errCh chan<- error) { + defer wg.Done() + + buf := make([]byte, 4096) + for { + n, err := reader.Read(buf) + // always send output first if we read something + if n > 0 { + if err := send(&drivers.ExecTaskStreamingResponseMsg{ + Stderr: &dproto.ExecTaskStreamingIOOperation{ + Data: buf[:n], + }, + }); err != nil { + errCh <- err + return + } + } + + // then process error + if isClosedError(err) { + if err := send(&drivers.ExecTaskStreamingResponseMsg{ + Stderr: &dproto.ExecTaskStreamingIOOperation{ + Close: true, + }, + }); err != nil { + errCh <- err + return + } + return + } else if err != nil { + errCh <- err + return + } + + } +} + +func isClosedError(err error) bool { + if err == nil { + return false + } + + return err == io.EOF || + err == io.ErrClosedPipe || + isUnixEIOErr(err) +} diff --git a/drivers/shared/executor/executor.go b/drivers/shared/executor/executor.go index f077bd488..255a49db8 100644 --- a/drivers/shared/executor/executor.go +++ b/drivers/shared/executor/executor.go @@ -22,6 +22,7 @@ import ( "github.com/hashicorp/nomad/client/stats" cstructs "github.com/hashicorp/nomad/client/structs" "github.com/hashicorp/nomad/plugins/drivers" + "github.com/kr/pty" shelpers "github.com/hashicorp/nomad/helper/stats" ) @@ -359,6 +360,53 @@ func ExecScript(ctx context.Context, dir string, env []string, attrs *syscall.Sy return buf.Bytes(), 0, nil } +func (e *UniversalExecutor) ExecStreaming(ctx context.Context, command []string, tty bool, + stream drivers.ExecTaskStream) error { + + if len(command) == 0 { + return fmt.Errorf("command is required") + } + + cmd := exec.CommandContext(ctx, command[0], command[1:]...) + + cmd.Dir = "/" + cmd.Env = e.childCmd.Env + + execHelper := &execHelper{ + logger: e.logger, + + newTerminal: func() (func() (*os.File, error), *os.File, error) { + pty, tty, err := pty.Open() + if err != nil { + return nil, nil, err + } + + return func() (*os.File, error) { return pty, nil }, tty, err + }, + setTTY: func(tty *os.File) error { + cmd.SysProcAttr = sessionCmdAttr(tty) + + cmd.Stdin = tty + cmd.Stdout = tty + cmd.Stderr = tty + return nil + }, + setIO: func(stdin io.Reader, stdout, stderr io.Writer) error { + cmd.Stdin = stdin + cmd.Stdout = stdout + cmd.Stderr = stderr + return nil + }, + processStart: cmd.Start, + processWait: func() (*os.ProcessState, error) { + err := cmd.Wait() + return cmd.ProcessState, err + }, + } + + return execHelper.run(ctx, tty, stream) +} + // Wait waits until a process has exited and returns it's exitcode and errors func (e *UniversalExecutor) Wait(ctx context.Context) (*ProcessState, error) { select { diff --git a/drivers/shared/executor/executor_linux.go b/drivers/shared/executor/executor_linux.go index b007da139..33900f876 100644 --- a/drivers/shared/executor/executor_linux.go +++ b/drivers/shared/executor/executor_linux.go @@ -5,6 +5,7 @@ package executor import ( "context" "fmt" + "io" "os" "os/exec" "path" @@ -28,6 +29,7 @@ import ( cgroupFs "github.com/opencontainers/runc/libcontainer/cgroups/fs" lconfigs "github.com/opencontainers/runc/libcontainer/configs" ldevices "github.com/opencontainers/runc/libcontainer/devices" + lutils "github.com/opencontainers/runc/libcontainer/utils" "github.com/syndtr/gocapability/capability" "golang.org/x/sys/unix" ) @@ -504,6 +506,53 @@ func (l *LibcontainerExecutor) Exec(deadline time.Time, cmd string, args []strin } +func (l *LibcontainerExecutor) newTerminalSocket() (master func() (*os.File, error), socket *os.File, err error) { + parent, child, err := lutils.NewSockPair("socket") + if err != nil { + return nil, nil, fmt.Errorf("failed to create terminal: %v", err) + } + + return func() (*os.File, error) { return lutils.RecvFd(parent) }, child, err + +} + +func (l *LibcontainerExecutor) ExecStreaming(ctx context.Context, cmd []string, tty bool, + stream drivers.ExecTaskStream) error { + + // the task process will be started by the container + process := &libcontainer.Process{ + Args: cmd, + Env: l.userProc.Env, + User: l.userProc.User, + Init: false, + Cwd: "/", + } + + execHelper := &execHelper{ + logger: l.logger, + + newTerminal: l.newTerminalSocket, + setTTY: func(tty *os.File) error { + process.ConsoleSocket = tty + return nil + }, + setIO: func(stdin io.Reader, stdout, stderr io.Writer) error { + process.Stdin = stdin + process.Stdout = stdout + process.Stderr = stderr + return nil + }, + + processStart: func() error { return l.container.Run(process) }, + processWait: func() (*os.ProcessState, error) { + return process.Wait() + }, + } + + return execHelper.run(ctx, tty, stream) + +} + type waitResult struct { ps *os.ProcessState err error diff --git a/drivers/shared/executor/pty_unix.go b/drivers/shared/executor/pty_unix.go new file mode 100644 index 000000000..0c1be1914 --- /dev/null +++ b/drivers/shared/executor/pty_unix.go @@ -0,0 +1,43 @@ +// +build darwin dragonfly freebsd linux netbsd openbsd solaris + +package executor + +import ( + "fmt" + "io" + "os" + "strings" + "syscall" + + "github.com/kr/pty" + "golang.org/x/sys/unix" +) + +func sessionCmdAttr(tty *os.File) *syscall.SysProcAttr { + return &syscall.SysProcAttr{ + Setsid: true, + Setctty: true, + Ctty: int(tty.Fd()), + } +} + +func setTTYSize(w io.Writer, height, width int32) error { + f, ok := w.(*os.File) + if !ok { + return fmt.Errorf("attempted to resize a non-tty session") + } + + return pty.Setsize(f, &pty.Winsize{ + Rows: uint16(height), + Cols: uint16(width), + }) + +} + +func isUnixEIOErr(err error) bool { + if err == nil { + return false + } + + return strings.Contains(err.Error(), unix.EIO.Error()) +} diff --git a/drivers/shared/executor/pty_windows.go b/drivers/shared/executor/pty_windows.go new file mode 100644 index 000000000..12411a5a5 --- /dev/null +++ b/drivers/shared/executor/pty_windows.go @@ -0,0 +1,23 @@ +// +build windows + +package executor + +import ( + "fmt" + "io" + "os" + "syscall" +) + +func sessionCmdAttr(tty *os.File) *syscall.SysProcAttr { + return &syscall.SysProcAttr{} +} + +func setTTYSize(w io.Writer, height, width int32) error { + return fmt.Errorf("unsupported") + +} + +func isUnixEIOErr(err error) bool { + return false +} From a4640db7a6cb981e41162516da9ece10046caecc Mon Sep 17 00:00:00 2001 From: Mahmood Ali Date: Sun, 28 Apr 2019 17:31:02 -0400 Subject: [PATCH 3/5] drivers: implement streaming exec for executor based drivers These simply delegate call to backend executor. --- drivers/exec/driver.go | 19 ++++++++++++++++ drivers/exec/driver_unix_test.go | 30 +++++++++++++++++++++++++ drivers/java/driver.go | 19 ++++++++++++++++ drivers/java/driver_test.go | 29 ++++++++++++++++++++++++ drivers/rawexec/driver.go | 19 ++++++++++++++++ drivers/rawexec/driver_unix_test.go | 34 +++++++++++++++++++++++++++++ 6 files changed, 150 insertions(+) diff --git a/drivers/exec/driver.go b/drivers/exec/driver.go index 391e44fda..eb4715293 100644 --- a/drivers/exec/driver.go +++ b/drivers/exec/driver.go @@ -530,3 +530,22 @@ func (d *Driver) ExecTask(taskID string, cmd []string, timeout time.Duration) (* }, }, nil } + +var _ drivers.ExecTaskStreamingRawDriver = (*Driver)(nil) + +func (d *Driver) ExecTaskStreamingRaw(ctx context.Context, + taskID string, + command []string, + tty bool, + stream drivers.ExecTaskStream) error { + + if len(command) == 0 { + return fmt.Errorf("error cmd must have atleast one value") + } + handle, ok := d.tasks.Get(taskID) + if !ok { + return drivers.ErrTaskNotFound + } + + return handle.exec.ExecStreaming(ctx, command, tty, stream) +} diff --git a/drivers/exec/driver_unix_test.go b/drivers/exec/driver_unix_test.go index 3d2844ba7..342993a8f 100644 --- a/drivers/exec/driver_unix_test.go +++ b/drivers/exec/driver_unix_test.go @@ -77,3 +77,33 @@ func TestExecDriver_StartWaitStop(t *testing.T) { require.NoError(harness.DestroyTask(task.ID, true)) } + +func TestExec_ExecTaskStreaming(t *testing.T) { + t.Parallel() + require := require.New(t) + + d := NewExecDriver(testlog.HCLogger(t)) + harness := dtestutil.NewDriverHarness(t, d) + defer harness.Kill() + + task := &drivers.TaskConfig{ + ID: uuid.Generate(), + Name: "sleep", + } + + cleanup := harness.MkAllocDir(task, false) + defer cleanup() + + tc := &TaskConfig{ + Command: "/bin/sleep", + Args: []string{"9000"}, + } + require.NoError(task.EncodeConcreteDriverConfig(&tc)) + + _, _, err := harness.StartTask(task) + require.NoError(err) + defer d.DestroyTask(task.ID, true) + + dtestutil.ExecTaskStreamingConformanceTests(t, harness, task.ID) + +} diff --git a/drivers/java/driver.go b/drivers/java/driver.go index 3a4a808d9..554875e9d 100644 --- a/drivers/java/driver.go +++ b/drivers/java/driver.go @@ -554,6 +554,25 @@ func (d *Driver) ExecTask(taskID string, cmd []string, timeout time.Duration) (* }, nil } +var _ drivers.ExecTaskStreamingRawDriver = (*Driver)(nil) + +func (d *Driver) ExecTaskStreamingRaw(ctx context.Context, + taskID string, + command []string, + tty bool, + stream drivers.ExecTaskStream) error { + + if len(command) == 0 { + return fmt.Errorf("error cmd must have atleast one value") + } + handle, ok := d.tasks.Get(taskID) + if !ok { + return drivers.ErrTaskNotFound + } + + return handle.exec.ExecStreaming(ctx, command, tty, stream) +} + // GetAbsolutePath returns the absolute path of the passed binary by resolving // it in the path and following symlinks. func GetAbsolutePath(bin string) (string, error) { diff --git a/drivers/java/driver_test.go b/drivers/java/driver_test.go index 4d431e29f..b4b3d0010 100644 --- a/drivers/java/driver_test.go +++ b/drivers/java/driver_test.go @@ -243,6 +243,35 @@ func TestJavaCmdArgs(t *testing.T) { } } +func TestJavaDriver_ExecTaskStreaming(t *testing.T) { + javaCompatible(t) + if !testutil.IsCI() { + t.Parallel() + } + + require := require.New(t) + d := NewDriver(testlog.HCLogger(t)) + harness := dtestutil.NewDriverHarness(t, d) + defer harness.Kill() + + tc := &TaskConfig{ + Class: "Hello", + Args: []string{"900"}, + } + task := basicTask(t, "demo-app", tc) + + cleanup := harness.MkAllocDir(task, true) + defer cleanup() + + copyFile("./test-resources/Hello.class", filepath.Join(task.TaskDir().Dir, "Hello.class"), t) + + _, _, err := harness.StartTask(task) + require.NoError(err) + defer d.DestroyTask(task.ID, true) + + dtestutil.ExecTaskStreamingConformanceTests(t, harness, task.ID) + +} func basicTask(t *testing.T, name string, taskConfig *TaskConfig) *drivers.TaskConfig { t.Helper() diff --git a/drivers/rawexec/driver.go b/drivers/rawexec/driver.go index bde086963..a79164c85 100644 --- a/drivers/rawexec/driver.go +++ b/drivers/rawexec/driver.go @@ -521,3 +521,22 @@ func (d *Driver) ExecTask(taskID string, cmd []string, timeout time.Duration) (* }, }, nil } + +var _ drivers.ExecTaskStreamingRawDriver = (*Driver)(nil) + +func (d *Driver) ExecTaskStreamingRaw(ctx context.Context, + taskID string, + command []string, + tty bool, + stream drivers.ExecTaskStream) error { + + if len(command) == 0 { + return fmt.Errorf("error cmd must have at least one value") + } + handle, ok := d.tasks.Get(taskID) + if !ok { + return drivers.ErrTaskNotFound + } + + return handle.exec.ExecStreaming(ctx, command, tty, stream) +} diff --git a/drivers/rawexec/driver_unix_test.go b/drivers/rawexec/driver_unix_test.go index 07491574f..d921e53b3 100644 --- a/drivers/rawexec/driver_unix_test.go +++ b/drivers/rawexec/driver_unix_test.go @@ -196,3 +196,37 @@ func TestRawExecDriver_StartWaitStop(t *testing.T) { require.NoError(harness.DestroyTask(task.ID, true)) } + +func TestRawExec_ExecTaskStreaming(t *testing.T) { + t.Parallel() + if runtime.GOOS == "darwin" { + t.Skip("skip running exec tasks on darwin as darwin has restrictions on starting tty shells") + } + require := require.New(t) + + d := NewRawExecDriver(testlog.HCLogger(t)) + harness := dtestutil.NewDriverHarness(t, d) + defer harness.Kill() + + task := &drivers.TaskConfig{ + ID: uuid.Generate(), + Name: "sleep", + } + + cleanup := harness.MkAllocDir(task, false) + defer cleanup() + + tc := &TaskConfig{ + Command: testtask.Path(), + Args: []string{"sleep", "9000s"}, + } + require.NoError(task.EncodeConcreteDriverConfig(&tc)) + testtask.SetTaskConfigEnv(task) + + _, _, err := harness.StartTask(task) + require.NoError(err) + defer d.DestroyTask(task.ID, true) + + dtestutil.ExecTaskStreamingConformanceTests(t, harness, task.ID) + +} From 7fdb7564e852da5b3efe88a5775a17d3097c54b4 Mon Sep 17 00:00:00 2001 From: Mahmood Ali Date: Mon, 29 Apr 2019 11:17:40 -0400 Subject: [PATCH 4/5] vendor github.com/kr/pty --- vendor/github.com/kr/pty/License | 23 ++++ vendor/github.com/kr/pty/README.md | 100 ++++++++++++++++++ vendor/github.com/kr/pty/doc.go | 16 +++ vendor/github.com/kr/pty/go.mod | 3 + vendor/github.com/kr/pty/ioctl.go | 13 +++ vendor/github.com/kr/pty/ioctl_bsd.go | 39 +++++++ vendor/github.com/kr/pty/mktypes.bash | 19 ++++ vendor/github.com/kr/pty/pty_darwin.go | 65 ++++++++++++ vendor/github.com/kr/pty/pty_dragonfly.go | 80 ++++++++++++++ vendor/github.com/kr/pty/pty_freebsd.go | 78 ++++++++++++++ vendor/github.com/kr/pty/pty_linux.go | 51 +++++++++ vendor/github.com/kr/pty/pty_openbsd.go | 33 ++++++ vendor/github.com/kr/pty/pty_unsupported.go | 11 ++ vendor/github.com/kr/pty/run.go | 57 ++++++++++ vendor/github.com/kr/pty/types.go | 10 ++ vendor/github.com/kr/pty/types_dragonfly.go | 17 +++ vendor/github.com/kr/pty/types_freebsd.go | 15 +++ vendor/github.com/kr/pty/types_openbsd.go | 14 +++ vendor/github.com/kr/pty/util.go | 64 +++++++++++ vendor/github.com/kr/pty/ztypes_386.go | 9 ++ vendor/github.com/kr/pty/ztypes_amd64.go | 9 ++ vendor/github.com/kr/pty/ztypes_arm.go | 9 ++ vendor/github.com/kr/pty/ztypes_arm64.go | 11 ++ .../kr/pty/ztypes_dragonfly_amd64.go | 14 +++ .../github.com/kr/pty/ztypes_freebsd_386.go | 13 +++ .../github.com/kr/pty/ztypes_freebsd_amd64.go | 14 +++ .../github.com/kr/pty/ztypes_freebsd_arm.go | 13 +++ vendor/github.com/kr/pty/ztypes_mipsx.go | 12 +++ .../github.com/kr/pty/ztypes_openbsd_386.go | 13 +++ .../github.com/kr/pty/ztypes_openbsd_amd64.go | 13 +++ vendor/github.com/kr/pty/ztypes_ppc64.go | 11 ++ vendor/github.com/kr/pty/ztypes_ppc64le.go | 11 ++ vendor/github.com/kr/pty/ztypes_s390x.go | 11 ++ vendor/vendor.json | 1 + 34 files changed, 872 insertions(+) create mode 100644 vendor/github.com/kr/pty/License create mode 100644 vendor/github.com/kr/pty/README.md create mode 100644 vendor/github.com/kr/pty/doc.go create mode 100644 vendor/github.com/kr/pty/go.mod create mode 100644 vendor/github.com/kr/pty/ioctl.go create mode 100644 vendor/github.com/kr/pty/ioctl_bsd.go create mode 100755 vendor/github.com/kr/pty/mktypes.bash create mode 100644 vendor/github.com/kr/pty/pty_darwin.go create mode 100644 vendor/github.com/kr/pty/pty_dragonfly.go create mode 100644 vendor/github.com/kr/pty/pty_freebsd.go create mode 100644 vendor/github.com/kr/pty/pty_linux.go create mode 100644 vendor/github.com/kr/pty/pty_openbsd.go create mode 100644 vendor/github.com/kr/pty/pty_unsupported.go create mode 100644 vendor/github.com/kr/pty/run.go create mode 100644 vendor/github.com/kr/pty/types.go create mode 100644 vendor/github.com/kr/pty/types_dragonfly.go create mode 100644 vendor/github.com/kr/pty/types_freebsd.go create mode 100644 vendor/github.com/kr/pty/types_openbsd.go create mode 100644 vendor/github.com/kr/pty/util.go create mode 100644 vendor/github.com/kr/pty/ztypes_386.go create mode 100644 vendor/github.com/kr/pty/ztypes_amd64.go create mode 100644 vendor/github.com/kr/pty/ztypes_arm.go create mode 100644 vendor/github.com/kr/pty/ztypes_arm64.go create mode 100644 vendor/github.com/kr/pty/ztypes_dragonfly_amd64.go create mode 100644 vendor/github.com/kr/pty/ztypes_freebsd_386.go create mode 100644 vendor/github.com/kr/pty/ztypes_freebsd_amd64.go create mode 100644 vendor/github.com/kr/pty/ztypes_freebsd_arm.go create mode 100644 vendor/github.com/kr/pty/ztypes_mipsx.go create mode 100644 vendor/github.com/kr/pty/ztypes_openbsd_386.go create mode 100644 vendor/github.com/kr/pty/ztypes_openbsd_amd64.go create mode 100644 vendor/github.com/kr/pty/ztypes_ppc64.go create mode 100644 vendor/github.com/kr/pty/ztypes_ppc64le.go create mode 100644 vendor/github.com/kr/pty/ztypes_s390x.go diff --git a/vendor/github.com/kr/pty/License b/vendor/github.com/kr/pty/License new file mode 100644 index 000000000..6b7558b6b --- /dev/null +++ b/vendor/github.com/kr/pty/License @@ -0,0 +1,23 @@ +Copyright (c) 2011 Keith Rarick + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall +be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/kr/pty/README.md b/vendor/github.com/kr/pty/README.md new file mode 100644 index 000000000..f9bb002e0 --- /dev/null +++ b/vendor/github.com/kr/pty/README.md @@ -0,0 +1,100 @@ +# pty + +Pty is a Go package for using unix pseudo-terminals. + +## Install + + go get github.com/kr/pty + +## Example + +### Command + +```go +package main + +import ( + "github.com/kr/pty" + "io" + "os" + "os/exec" +) + +func main() { + c := exec.Command("grep", "--color=auto", "bar") + f, err := pty.Start(c) + if err != nil { + panic(err) + } + + go func() { + f.Write([]byte("foo\n")) + f.Write([]byte("bar\n")) + f.Write([]byte("baz\n")) + f.Write([]byte{4}) // EOT + }() + io.Copy(os.Stdout, f) +} +``` + +### Shell + +```go +package main + +import ( + "io" + "log" + "os" + "os/exec" + "os/signal" + "syscall" + + "github.com/kr/pty" + "golang.org/x/crypto/ssh/terminal" +) + +func test() error { + // Create arbitrary command. + c := exec.Command("bash") + + // Start the command with a pty. + ptmx, err := pty.Start(c) + if err != nil { + return err + } + // Make sure to close the pty at the end. + defer func() { _ = ptmx.Close() }() // Best effort. + + // Handle pty size. + ch := make(chan os.Signal, 1) + signal.Notify(ch, syscall.SIGWINCH) + go func() { + for range ch { + if err := pty.InheritSize(os.Stdin, ptmx); err != nil { + log.Printf("error resizing pty: %s", err) + } + } + }() + ch <- syscall.SIGWINCH // Initial resize. + + // Set stdin in raw mode. + oldState, err := terminal.MakeRaw(int(os.Stdin.Fd())) + if err != nil { + panic(err) + } + defer func() { _ = terminal.Restore(int(os.Stdin.Fd()), oldState) }() // Best effort. + + // Copy stdin to the pty and the pty to stdout. + go func() { _, _ = io.Copy(ptmx, os.Stdin) }() + _, _ = io.Copy(os.Stdout, ptmx) + + return nil +} + +func main() { + if err := test(); err != nil { + log.Fatal(err) + } +} +``` diff --git a/vendor/github.com/kr/pty/doc.go b/vendor/github.com/kr/pty/doc.go new file mode 100644 index 000000000..190cfbea9 --- /dev/null +++ b/vendor/github.com/kr/pty/doc.go @@ -0,0 +1,16 @@ +// Package pty provides functions for working with Unix terminals. +package pty + +import ( + "errors" + "os" +) + +// ErrUnsupported is returned if a function is not +// available on the current platform. +var ErrUnsupported = errors.New("unsupported") + +// Opens a pty and its corresponding tty. +func Open() (pty, tty *os.File, err error) { + return open() +} diff --git a/vendor/github.com/kr/pty/go.mod b/vendor/github.com/kr/pty/go.mod new file mode 100644 index 000000000..f73bcd613 --- /dev/null +++ b/vendor/github.com/kr/pty/go.mod @@ -0,0 +1,3 @@ +module github.com/kr/pty + +go 1.12 diff --git a/vendor/github.com/kr/pty/ioctl.go b/vendor/github.com/kr/pty/ioctl.go new file mode 100644 index 000000000..c57c19e7e --- /dev/null +++ b/vendor/github.com/kr/pty/ioctl.go @@ -0,0 +1,13 @@ +// +build !windows + +package pty + +import "syscall" + +func ioctl(fd, cmd, ptr uintptr) error { + _, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, cmd, ptr) + if e != 0 { + return e + } + return nil +} diff --git a/vendor/github.com/kr/pty/ioctl_bsd.go b/vendor/github.com/kr/pty/ioctl_bsd.go new file mode 100644 index 000000000..73b12c53c --- /dev/null +++ b/vendor/github.com/kr/pty/ioctl_bsd.go @@ -0,0 +1,39 @@ +// +build darwin dragonfly freebsd netbsd openbsd + +package pty + +// from +const ( + _IOC_VOID uintptr = 0x20000000 + _IOC_OUT uintptr = 0x40000000 + _IOC_IN uintptr = 0x80000000 + _IOC_IN_OUT uintptr = _IOC_OUT | _IOC_IN + _IOC_DIRMASK = _IOC_VOID | _IOC_OUT | _IOC_IN + + _IOC_PARAM_SHIFT = 13 + _IOC_PARAM_MASK = (1 << _IOC_PARAM_SHIFT) - 1 +) + +func _IOC_PARM_LEN(ioctl uintptr) uintptr { + return (ioctl >> 16) & _IOC_PARAM_MASK +} + +func _IOC(inout uintptr, group byte, ioctl_num uintptr, param_len uintptr) uintptr { + return inout | (param_len&_IOC_PARAM_MASK)<<16 | uintptr(group)<<8 | ioctl_num +} + +func _IO(group byte, ioctl_num uintptr) uintptr { + return _IOC(_IOC_VOID, group, ioctl_num, 0) +} + +func _IOR(group byte, ioctl_num uintptr, param_len uintptr) uintptr { + return _IOC(_IOC_OUT, group, ioctl_num, param_len) +} + +func _IOW(group byte, ioctl_num uintptr, param_len uintptr) uintptr { + return _IOC(_IOC_IN, group, ioctl_num, param_len) +} + +func _IOWR(group byte, ioctl_num uintptr, param_len uintptr) uintptr { + return _IOC(_IOC_IN_OUT, group, ioctl_num, param_len) +} diff --git a/vendor/github.com/kr/pty/mktypes.bash b/vendor/github.com/kr/pty/mktypes.bash new file mode 100755 index 000000000..82ee16721 --- /dev/null +++ b/vendor/github.com/kr/pty/mktypes.bash @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +GOOSARCH="${GOOS}_${GOARCH}" +case "$GOOSARCH" in +_* | *_ | _) + echo 'undefined $GOOS_$GOARCH:' "$GOOSARCH" 1>&2 + exit 1 + ;; +esac + +GODEFS="go tool cgo -godefs" + +$GODEFS types.go |gofmt > ztypes_$GOARCH.go + +case $GOOS in +freebsd|dragonfly|openbsd) + $GODEFS types_$GOOS.go |gofmt > ztypes_$GOOSARCH.go + ;; +esac diff --git a/vendor/github.com/kr/pty/pty_darwin.go b/vendor/github.com/kr/pty/pty_darwin.go new file mode 100644 index 000000000..6344b6b0e --- /dev/null +++ b/vendor/github.com/kr/pty/pty_darwin.go @@ -0,0 +1,65 @@ +package pty + +import ( + "errors" + "os" + "syscall" + "unsafe" +) + +func open() (pty, tty *os.File, err error) { + pFD, err := syscall.Open("/dev/ptmx", syscall.O_RDWR|syscall.O_CLOEXEC, 0) + if err != nil { + return nil, nil, err + } + p := os.NewFile(uintptr(pFD), "/dev/ptmx") + // In case of error after this point, make sure we close the ptmx fd. + defer func() { + if err != nil { + _ = p.Close() // Best effort. + } + }() + + sname, err := ptsname(p) + if err != nil { + return nil, nil, err + } + + if err := grantpt(p); err != nil { + return nil, nil, err + } + + if err := unlockpt(p); err != nil { + return nil, nil, err + } + + t, err := os.OpenFile(sname, os.O_RDWR, 0) + if err != nil { + return nil, nil, err + } + return p, t, nil +} + +func ptsname(f *os.File) (string, error) { + n := make([]byte, _IOC_PARM_LEN(syscall.TIOCPTYGNAME)) + + err := ioctl(f.Fd(), syscall.TIOCPTYGNAME, uintptr(unsafe.Pointer(&n[0]))) + if err != nil { + return "", err + } + + for i, c := range n { + if c == 0 { + return string(n[:i]), nil + } + } + return "", errors.New("TIOCPTYGNAME string not NUL-terminated") +} + +func grantpt(f *os.File) error { + return ioctl(f.Fd(), syscall.TIOCPTYGRANT, 0) +} + +func unlockpt(f *os.File) error { + return ioctl(f.Fd(), syscall.TIOCPTYUNLK, 0) +} diff --git a/vendor/github.com/kr/pty/pty_dragonfly.go b/vendor/github.com/kr/pty/pty_dragonfly.go new file mode 100644 index 000000000..b7d1f20f2 --- /dev/null +++ b/vendor/github.com/kr/pty/pty_dragonfly.go @@ -0,0 +1,80 @@ +package pty + +import ( + "errors" + "os" + "strings" + "syscall" + "unsafe" +) + +// same code as pty_darwin.go +func open() (pty, tty *os.File, err error) { + p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0) + if err != nil { + return nil, nil, err + } + // In case of error after this point, make sure we close the ptmx fd. + defer func() { + if err != nil { + _ = p.Close() // Best effort. + } + }() + + sname, err := ptsname(p) + if err != nil { + return nil, nil, err + } + + if err := grantpt(p); err != nil { + return nil, nil, err + } + + if err := unlockpt(p); err != nil { + return nil, nil, err + } + + t, err := os.OpenFile(sname, os.O_RDWR, 0) + if err != nil { + return nil, nil, err + } + return p, t, nil +} + +func grantpt(f *os.File) error { + _, err := isptmaster(f.Fd()) + return err +} + +func unlockpt(f *os.File) error { + _, err := isptmaster(f.Fd()) + return err +} + +func isptmaster(fd uintptr) (bool, error) { + err := ioctl(fd, syscall.TIOCISPTMASTER, 0) + return err == nil, err +} + +var ( + emptyFiodgnameArg fiodgnameArg + ioctl_FIODNAME = _IOW('f', 120, unsafe.Sizeof(emptyFiodgnameArg)) +) + +func ptsname(f *os.File) (string, error) { + name := make([]byte, _C_SPECNAMELEN) + fa := fiodgnameArg{Name: (*byte)(unsafe.Pointer(&name[0])), Len: _C_SPECNAMELEN, Pad_cgo_0: [4]byte{0, 0, 0, 0}} + + err := ioctl(f.Fd(), ioctl_FIODNAME, uintptr(unsafe.Pointer(&fa))) + if err != nil { + return "", err + } + + for i, c := range name { + if c == 0 { + s := "/dev/" + string(name[:i]) + return strings.Replace(s, "ptm", "pts", -1), nil + } + } + return "", errors.New("TIOCPTYGNAME string not NUL-terminated") +} diff --git a/vendor/github.com/kr/pty/pty_freebsd.go b/vendor/github.com/kr/pty/pty_freebsd.go new file mode 100644 index 000000000..63b6d9133 --- /dev/null +++ b/vendor/github.com/kr/pty/pty_freebsd.go @@ -0,0 +1,78 @@ +package pty + +import ( + "errors" + "os" + "syscall" + "unsafe" +) + +func posixOpenpt(oflag int) (fd int, err error) { + r0, _, e1 := syscall.Syscall(syscall.SYS_POSIX_OPENPT, uintptr(oflag), 0, 0) + fd = int(r0) + if e1 != 0 { + err = e1 + } + return fd, err +} + +func open() (pty, tty *os.File, err error) { + fd, err := posixOpenpt(syscall.O_RDWR | syscall.O_CLOEXEC) + if err != nil { + return nil, nil, err + } + p := os.NewFile(uintptr(fd), "/dev/pts") + // In case of error after this point, make sure we close the pts fd. + defer func() { + if err != nil { + _ = p.Close() // Best effort. + } + }() + + sname, err := ptsname(p) + if err != nil { + return nil, nil, err + } + + t, err := os.OpenFile("/dev/"+sname, os.O_RDWR, 0) + if err != nil { + return nil, nil, err + } + return p, t, nil +} + +func isptmaster(fd uintptr) (bool, error) { + err := ioctl(fd, syscall.TIOCPTMASTER, 0) + return err == nil, err +} + +var ( + emptyFiodgnameArg fiodgnameArg + ioctlFIODGNAME = _IOW('f', 120, unsafe.Sizeof(emptyFiodgnameArg)) +) + +func ptsname(f *os.File) (string, error) { + master, err := isptmaster(f.Fd()) + if err != nil { + return "", err + } + if !master { + return "", syscall.EINVAL + } + + const n = _C_SPECNAMELEN + 1 + var ( + buf = make([]byte, n) + arg = fiodgnameArg{Len: n, Buf: (*byte)(unsafe.Pointer(&buf[0]))} + ) + if err := ioctl(f.Fd(), ioctlFIODGNAME, uintptr(unsafe.Pointer(&arg))); err != nil { + return "", err + } + + for i, c := range buf { + if c == 0 { + return string(buf[:i]), nil + } + } + return "", errors.New("FIODGNAME string not NUL-terminated") +} diff --git a/vendor/github.com/kr/pty/pty_linux.go b/vendor/github.com/kr/pty/pty_linux.go new file mode 100644 index 000000000..4a833de18 --- /dev/null +++ b/vendor/github.com/kr/pty/pty_linux.go @@ -0,0 +1,51 @@ +package pty + +import ( + "os" + "strconv" + "syscall" + "unsafe" +) + +func open() (pty, tty *os.File, err error) { + p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0) + if err != nil { + return nil, nil, err + } + // In case of error after this point, make sure we close the ptmx fd. + defer func() { + if err != nil { + _ = p.Close() // Best effort. + } + }() + + sname, err := ptsname(p) + if err != nil { + return nil, nil, err + } + + if err := unlockpt(p); err != nil { + return nil, nil, err + } + + t, err := os.OpenFile(sname, os.O_RDWR|syscall.O_NOCTTY, 0) + if err != nil { + return nil, nil, err + } + return p, t, nil +} + +func ptsname(f *os.File) (string, error) { + var n _C_uint + err := ioctl(f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))) + if err != nil { + return "", err + } + return "/dev/pts/" + strconv.Itoa(int(n)), nil +} + +func unlockpt(f *os.File) error { + var u _C_int + // use TIOCSPTLCK with a pointer to zero to clear the lock + return ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u))) +} diff --git a/vendor/github.com/kr/pty/pty_openbsd.go b/vendor/github.com/kr/pty/pty_openbsd.go new file mode 100644 index 000000000..a6a35d1e6 --- /dev/null +++ b/vendor/github.com/kr/pty/pty_openbsd.go @@ -0,0 +1,33 @@ +package pty + +import ( + "os" + "syscall" + "unsafe" +) + +func open() (pty, tty *os.File, err error) { + /* + * from ptm(4): + * The PTMGET command allocates a free pseudo terminal, changes its + * ownership to the caller, revokes the access privileges for all previous + * users, opens the file descriptors for the pty and tty devices and + * returns them to the caller in struct ptmget. + */ + + p, err := os.OpenFile("/dev/ptm", os.O_RDWR|syscall.O_CLOEXEC, 0) + if err != nil { + return nil, nil, err + } + defer p.Close() + + var ptm ptmget + if err := ioctl(p.Fd(), uintptr(ioctl_PTMGET), uintptr(unsafe.Pointer(&ptm))); err != nil { + return nil, nil, err + } + + pty = os.NewFile(uintptr(ptm.Cfd), "/dev/ptm") + tty = os.NewFile(uintptr(ptm.Sfd), "/dev/ptm") + + return pty, tty, nil +} diff --git a/vendor/github.com/kr/pty/pty_unsupported.go b/vendor/github.com/kr/pty/pty_unsupported.go new file mode 100644 index 000000000..9a3e721bc --- /dev/null +++ b/vendor/github.com/kr/pty/pty_unsupported.go @@ -0,0 +1,11 @@ +// +build !linux,!darwin,!freebsd,!dragonfly,!openbsd + +package pty + +import ( + "os" +) + +func open() (pty, tty *os.File, err error) { + return nil, nil, ErrUnsupported +} diff --git a/vendor/github.com/kr/pty/run.go b/vendor/github.com/kr/pty/run.go new file mode 100644 index 000000000..959be26b2 --- /dev/null +++ b/vendor/github.com/kr/pty/run.go @@ -0,0 +1,57 @@ +// +build !windows + +package pty + +import ( + "os" + "os/exec" + "syscall" +) + +// Start assigns a pseudo-terminal tty os.File to c.Stdin, c.Stdout, +// and c.Stderr, calls c.Start, and returns the File of the tty's +// corresponding pty. +func Start(c *exec.Cmd) (pty *os.File, err error) { + return StartWithSize(c, nil) +} + +// StartWithSize assigns a pseudo-terminal tty os.File to c.Stdin, c.Stdout, +// and c.Stderr, calls c.Start, and returns the File of the tty's +// corresponding pty. +// +// This will resize the pty to the specified size before starting the command +func StartWithSize(c *exec.Cmd, sz *Winsize) (pty *os.File, err error) { + pty, tty, err := Open() + if err != nil { + return nil, err + } + defer tty.Close() + if sz != nil { + err = Setsize(pty, sz) + if err != nil { + pty.Close() + return nil, err + } + } + if c.Stdout == nil { + c.Stdout = tty + } + if c.Stderr == nil { + c.Stderr = tty + } + if c.Stdin == nil { + c.Stdin = tty + } + if c.SysProcAttr == nil { + c.SysProcAttr = &syscall.SysProcAttr{} + } + c.SysProcAttr.Setctty = true + c.SysProcAttr.Setsid = true + c.SysProcAttr.Ctty = int(tty.Fd()) + err = c.Start() + if err != nil { + pty.Close() + return nil, err + } + return pty, err +} diff --git a/vendor/github.com/kr/pty/types.go b/vendor/github.com/kr/pty/types.go new file mode 100644 index 000000000..5aecb6bcd --- /dev/null +++ b/vendor/github.com/kr/pty/types.go @@ -0,0 +1,10 @@ +// +build ignore + +package pty + +import "C" + +type ( + _C_int C.int + _C_uint C.uint +) diff --git a/vendor/github.com/kr/pty/types_dragonfly.go b/vendor/github.com/kr/pty/types_dragonfly.go new file mode 100644 index 000000000..5c0493b85 --- /dev/null +++ b/vendor/github.com/kr/pty/types_dragonfly.go @@ -0,0 +1,17 @@ +// +build ignore + +package pty + +/* +#define _KERNEL +#include +#include +#include +*/ +import "C" + +const ( + _C_SPECNAMELEN = C.SPECNAMELEN /* max length of devicename */ +) + +type fiodgnameArg C.struct_fiodname_args diff --git a/vendor/github.com/kr/pty/types_freebsd.go b/vendor/github.com/kr/pty/types_freebsd.go new file mode 100644 index 000000000..ce3eb9518 --- /dev/null +++ b/vendor/github.com/kr/pty/types_freebsd.go @@ -0,0 +1,15 @@ +// +build ignore + +package pty + +/* +#include +#include +*/ +import "C" + +const ( + _C_SPECNAMELEN = C.SPECNAMELEN /* max length of devicename */ +) + +type fiodgnameArg C.struct_fiodgname_arg diff --git a/vendor/github.com/kr/pty/types_openbsd.go b/vendor/github.com/kr/pty/types_openbsd.go new file mode 100644 index 000000000..47701b5f9 --- /dev/null +++ b/vendor/github.com/kr/pty/types_openbsd.go @@ -0,0 +1,14 @@ +// +build ignore + +package pty + +/* +#include +#include +#include +*/ +import "C" + +type ptmget C.struct_ptmget + +var ioctl_PTMGET = C.PTMGET diff --git a/vendor/github.com/kr/pty/util.go b/vendor/github.com/kr/pty/util.go new file mode 100644 index 000000000..2fa741cca --- /dev/null +++ b/vendor/github.com/kr/pty/util.go @@ -0,0 +1,64 @@ +// +build !windows + +package pty + +import ( + "os" + "syscall" + "unsafe" +) + +// InheritSize applies the terminal size of pty to tty. This should be run +// in a signal handler for syscall.SIGWINCH to automatically resize the tty when +// the pty receives a window size change notification. +func InheritSize(pty, tty *os.File) error { + size, err := GetsizeFull(pty) + if err != nil { + return err + } + err = Setsize(tty, size) + if err != nil { + return err + } + return nil +} + +// Setsize resizes t to s. +func Setsize(t *os.File, ws *Winsize) error { + return windowRectCall(ws, t.Fd(), syscall.TIOCSWINSZ) +} + +// GetsizeFull returns the full terminal size description. +func GetsizeFull(t *os.File) (size *Winsize, err error) { + var ws Winsize + err = windowRectCall(&ws, t.Fd(), syscall.TIOCGWINSZ) + return &ws, err +} + +// Getsize returns the number of rows (lines) and cols (positions +// in each line) in terminal t. +func Getsize(t *os.File) (rows, cols int, err error) { + ws, err := GetsizeFull(t) + return int(ws.Rows), int(ws.Cols), err +} + +// Winsize describes the terminal size. +type Winsize struct { + Rows uint16 // ws_row: Number of rows (in cells) + Cols uint16 // ws_col: Number of columns (in cells) + X uint16 // ws_xpixel: Width in pixels + Y uint16 // ws_ypixel: Height in pixels +} + +func windowRectCall(ws *Winsize, fd, a2 uintptr) error { + _, _, errno := syscall.Syscall( + syscall.SYS_IOCTL, + fd, + a2, + uintptr(unsafe.Pointer(ws)), + ) + if errno != 0 { + return syscall.Errno(errno) + } + return nil +} diff --git a/vendor/github.com/kr/pty/ztypes_386.go b/vendor/github.com/kr/pty/ztypes_386.go new file mode 100644 index 000000000..ff0b8fd83 --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_386.go @@ -0,0 +1,9 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types.go + +package pty + +type ( + _C_int int32 + _C_uint uint32 +) diff --git a/vendor/github.com/kr/pty/ztypes_amd64.go b/vendor/github.com/kr/pty/ztypes_amd64.go new file mode 100644 index 000000000..ff0b8fd83 --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_amd64.go @@ -0,0 +1,9 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types.go + +package pty + +type ( + _C_int int32 + _C_uint uint32 +) diff --git a/vendor/github.com/kr/pty/ztypes_arm.go b/vendor/github.com/kr/pty/ztypes_arm.go new file mode 100644 index 000000000..ff0b8fd83 --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_arm.go @@ -0,0 +1,9 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types.go + +package pty + +type ( + _C_int int32 + _C_uint uint32 +) diff --git a/vendor/github.com/kr/pty/ztypes_arm64.go b/vendor/github.com/kr/pty/ztypes_arm64.go new file mode 100644 index 000000000..6c29a4b91 --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_arm64.go @@ -0,0 +1,11 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types.go + +// +build arm64 + +package pty + +type ( + _C_int int32 + _C_uint uint32 +) diff --git a/vendor/github.com/kr/pty/ztypes_dragonfly_amd64.go b/vendor/github.com/kr/pty/ztypes_dragonfly_amd64.go new file mode 100644 index 000000000..6b0ba037f --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_dragonfly_amd64.go @@ -0,0 +1,14 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_dragonfly.go + +package pty + +const ( + _C_SPECNAMELEN = 0x3f +) + +type fiodgnameArg struct { + Name *byte + Len uint32 + Pad_cgo_0 [4]byte +} diff --git a/vendor/github.com/kr/pty/ztypes_freebsd_386.go b/vendor/github.com/kr/pty/ztypes_freebsd_386.go new file mode 100644 index 000000000..d9975374e --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_freebsd_386.go @@ -0,0 +1,13 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_freebsd.go + +package pty + +const ( + _C_SPECNAMELEN = 0x3f +) + +type fiodgnameArg struct { + Len int32 + Buf *byte +} diff --git a/vendor/github.com/kr/pty/ztypes_freebsd_amd64.go b/vendor/github.com/kr/pty/ztypes_freebsd_amd64.go new file mode 100644 index 000000000..5fa102fcd --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_freebsd_amd64.go @@ -0,0 +1,14 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_freebsd.go + +package pty + +const ( + _C_SPECNAMELEN = 0x3f +) + +type fiodgnameArg struct { + Len int32 + Pad_cgo_0 [4]byte + Buf *byte +} diff --git a/vendor/github.com/kr/pty/ztypes_freebsd_arm.go b/vendor/github.com/kr/pty/ztypes_freebsd_arm.go new file mode 100644 index 000000000..d9975374e --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_freebsd_arm.go @@ -0,0 +1,13 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_freebsd.go + +package pty + +const ( + _C_SPECNAMELEN = 0x3f +) + +type fiodgnameArg struct { + Len int32 + Buf *byte +} diff --git a/vendor/github.com/kr/pty/ztypes_mipsx.go b/vendor/github.com/kr/pty/ztypes_mipsx.go new file mode 100644 index 000000000..f0ce74086 --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_mipsx.go @@ -0,0 +1,12 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types.go + +// +build linux +// +build mips mipsle mips64 mips64le + +package pty + +type ( + _C_int int32 + _C_uint uint32 +) diff --git a/vendor/github.com/kr/pty/ztypes_openbsd_386.go b/vendor/github.com/kr/pty/ztypes_openbsd_386.go new file mode 100644 index 000000000..ccb3aab9a --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_openbsd_386.go @@ -0,0 +1,13 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_openbsd.go + +package pty + +type ptmget struct { + Cfd int32 + Sfd int32 + Cn [16]int8 + Sn [16]int8 +} + +var ioctl_PTMGET = 0x40287401 diff --git a/vendor/github.com/kr/pty/ztypes_openbsd_amd64.go b/vendor/github.com/kr/pty/ztypes_openbsd_amd64.go new file mode 100644 index 000000000..e67051688 --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_openbsd_amd64.go @@ -0,0 +1,13 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_openbsd.go + +package pty + +type ptmget struct { + Cfd int32 + Sfd int32 + Cn [16]int8 + Sn [16]int8 +} + +var ioctl_PTMGET = 0x40287401 diff --git a/vendor/github.com/kr/pty/ztypes_ppc64.go b/vendor/github.com/kr/pty/ztypes_ppc64.go new file mode 100644 index 000000000..4e1af8431 --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_ppc64.go @@ -0,0 +1,11 @@ +// +build ppc64 + +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types.go + +package pty + +type ( + _C_int int32 + _C_uint uint32 +) diff --git a/vendor/github.com/kr/pty/ztypes_ppc64le.go b/vendor/github.com/kr/pty/ztypes_ppc64le.go new file mode 100644 index 000000000..e6780f4e2 --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_ppc64le.go @@ -0,0 +1,11 @@ +// +build ppc64le + +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types.go + +package pty + +type ( + _C_int int32 + _C_uint uint32 +) diff --git a/vendor/github.com/kr/pty/ztypes_s390x.go b/vendor/github.com/kr/pty/ztypes_s390x.go new file mode 100644 index 000000000..a7452b61c --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_s390x.go @@ -0,0 +1,11 @@ +// +build s390x + +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types.go + +package pty + +type ( + _C_int int32 + _C_uint uint32 +) diff --git a/vendor/vendor.json b/vendor/vendor.json index 44287ea93..d474165e2 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -244,6 +244,7 @@ {"path":"github.com/hpcloud/tail/watch","checksumSHA1":"TP4OAv5JMtzj2TB6OQBKqauaKDc=","revision":"37f4271387456dd1bf82ab1ad9229f060cc45386","revisionTime":"2017-08-14T16:06:53Z"}, {"path":"github.com/jmespath/go-jmespath","checksumSHA1":"3/Bhy+ua/DCv2ElMD5GzOYSGN6g=","comment":"0.2.2-2-gc01cf91","revision":"c01cf91b011868172fdcd9f41838e80c9d716264"}, {"path":"github.com/kr/pretty","checksumSHA1":"eOXF2PEvYLMeD8DSzLZJWbjYzco=","revision":"cfb55aafdaf3ec08f0db22699ab822c50091b1c4","revisionTime":"2016-08-23T17:07:15Z"}, + {"path":"github.com/kr/pty","checksumSHA1":"WD7GMln/NoduJr0DbumjOE59xI8=","revision":"b6e1bdd4a4f88614e0c6e5e8089c7abed98aae17","revisionTime":"2019-04-01T03:15:51Z"}, {"path":"github.com/kr/text","checksumSHA1":"uulQHQ7IsRKqDudBC8Go9J0gtAc=","revision":"7cafcd837844e784b526369c9bce262804aebc60","revisionTime":"2016-05-04T02:26:26Z"}, {"path":"github.com/mattn/go-colorable","checksumSHA1":"SEnjvwVyfuU2xBaOfXfwPD5MZqk=","revision":"efa589957cd060542a26d2dd7832fd6a6c6c3ade","revisionTime":"2018-03-10T13:32:14Z"}, {"path":"github.com/mattn/go-isatty","checksumSHA1":"AZO2VGorXTMDiSVUih3k73vORHY=","revision":"6ca4dbf54d38eea1a992b3c722a76a5d1c4cb25c","revisionTime":"2017-11-07T05:05:31Z"}, From b4df061fef2ae7d7ba3a7bf5b4af3f263994fead Mon Sep 17 00:00:00 2001 From: Mahmood Ali Date: Fri, 10 May 2019 18:44:19 -0400 Subject: [PATCH 5/5] use pty/tty terminology similar to github.com/kr/pty --- drivers/shared/executor/exec_utils.go | 6 +++--- drivers/shared/executor/executor_linux.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/shared/executor/exec_utils.go b/drivers/shared/executor/exec_utils.go index 17194d0db..ed3dd2ba8 100644 --- a/drivers/shared/executor/exec_utils.go +++ b/drivers/shared/executor/exec_utils.go @@ -19,8 +19,8 @@ type execHelper struct { logger hclog.Logger // newTerminal function creates a tty appropriate for the command - // The returned master end of tty function is to be called after process start. - newTerminal func() (master func() (*os.File, error), slave *os.File, err error) + // The returned pty end of tty function is to be called after process start. + newTerminal func() (pty func() (*os.File, error), tty *os.File, err error) // setTTY is a callback to configure the command with slave end of the tty of the terminal, when tty is enabled setTTY func(tty *os.File) error @@ -61,7 +61,7 @@ func (e *execHelper) runTTY(ctx context.Context, stream drivers.ExecTaskStream) pty, err := ptyF() if err != nil { - return fmt.Errorf("failed to get tty master: %v", err) + return fmt.Errorf("failed to get pty: %v", err) } defer pty.Close() diff --git a/drivers/shared/executor/executor_linux.go b/drivers/shared/executor/executor_linux.go index 33900f876..f1a465c16 100644 --- a/drivers/shared/executor/executor_linux.go +++ b/drivers/shared/executor/executor_linux.go @@ -506,7 +506,7 @@ func (l *LibcontainerExecutor) Exec(deadline time.Time, cmd string, args []strin } -func (l *LibcontainerExecutor) newTerminalSocket() (master func() (*os.File, error), socket *os.File, err error) { +func (l *LibcontainerExecutor) newTerminalSocket() (pty func() (*os.File, error), tty *os.File, err error) { parent, child, err := lutils.NewSockPair("socket") if err != nil { return nil, nil, fmt.Errorf("failed to create terminal: %v", err)