From 559d079902a8921cbc849d2fbd80b38a4f89cdfd Mon Sep 17 00:00:00 2001 From: Cory Bennett Date: Mon, 31 Jan 2022 20:05:17 +0000 Subject: [PATCH] Allow signals to be sent to gateway exec containers Signed-off-by: Cory Bennett --- client/build_test.go | 100 +++++ executor/containerdexecutor/executor.go | 36 +- executor/executor.go | 2 + executor/runcexecutor/executor.go | 89 +++- executor/runcexecutor/executor_common.go | 51 ++- executor/runcexecutor/executor_linux.go | 62 ++- frontend/gateway/client/client.go | 3 +- frontend/gateway/container.go | 33 +- frontend/gateway/gateway.go | 15 + frontend/gateway/grpcclient/client.go | 32 +- frontend/gateway/pb/caps.go | 11 + frontend/gateway/pb/gateway.pb.go | 542 +++++++++++++++++------ frontend/gateway/pb/gateway.proto | 8 + 13 files changed, 784 insertions(+), 200 deletions(-) diff --git a/client/build_test.go b/client/build_test.go index a4c26c2314d5..72442161d364 100644 --- a/client/build_test.go +++ b/client/build_test.go @@ -10,6 +10,7 @@ import ( "path/filepath" "strconv" "strings" + "syscall" "testing" "time" @@ -52,6 +53,7 @@ func TestClientGatewayIntegration(t *testing.T) { testClientGatewaySlowCacheExecError, testClientGatewayExecFileActionError, testClientGatewayContainerExtraHosts, + testClientGatewayContainerSignal, testWarnings, ), integration.WithMirroredImages(integration.OfficialImages("busybox:latest"))) @@ -1894,6 +1896,104 @@ func testClientGatewayContainerHostNetworking(t *testing.T, sb integration.Sandb checkAllReleasable(t, c, sb, true) } +// testClientGatewayContainerSignal is testing that we can send a signal +func testClientGatewayContainerSignal(t *testing.T, sb integration.Sandbox) { + requiresLinux(t) + ctx := sb.Context() + + c, err := New(ctx, sb.Address()) + require.NoError(t, err) + defer c.Close() + + product := "buildkit_test" + + b := func(ctx context.Context, c client.Client) (*client.Result, error) { + ctx, timeout := context.WithTimeout(ctx, 10*time.Second) + defer timeout() + + st := llb.Image("busybox:latest") + + def, err := st.Marshal(ctx) + if err != nil { + return nil, errors.Wrap(err, "failed to marshal state") + } + + r, err := c.Solve(ctx, client.SolveRequest{ + Definition: def.ToPB(), + }) + if err != nil { + return nil, errors.Wrap(err, "failed to solve") + } + + ctr1, err := c.NewContainer(ctx, client.NewContainerRequest{ + Mounts: []client.Mount{{ + Dest: "/", + MountType: pb.MountType_BIND, + Ref: r.Ref, + }}, + }) + require.NoError(t, err) + defer ctr1.Release(ctx) + + pid1, err := ctr1.Start(ctx, client.StartRequest{ + Args: []string{"sh", "-c", `trap 'kill $(jobs -p); exit 99' HUP; sleep 10 & wait`}, + }) + require.NoError(t, err) + + // allow for the shell script to setup the trap before we signal it + time.Sleep(time.Second) + + err = pid1.Signal(ctx, syscall.SIGHUP) + require.NoError(t, err) + + err = pid1.Wait() + var exitError *gatewayapi.ExitError + require.ErrorAs(t, err, &exitError) + require.Equal(t, uint32(99), exitError.ExitCode) + + // Now try again to signal an exec process + + ctr2, err := c.NewContainer(ctx, client.NewContainerRequest{ + Mounts: []client.Mount{{ + Dest: "/", + MountType: pb.MountType_BIND, + Ref: r.Ref, + }}, + }) + require.NoError(t, err) + defer ctr2.Release(ctx) + + pid1, err = ctr2.Start(ctx, client.StartRequest{ + Args: []string{"sleep", "10"}, + }) + require.NoError(t, err) + + pid2, err := ctr2.Start(ctx, client.StartRequest{ + Args: []string{"sh", "-c", `trap 'kill $(jobs -p); exit 111' INT; sleep 10 & wait`}, + }) + require.NoError(t, err) + + // allow for the shell script to setup the trap before we signal it + time.Sleep(time.Second) + + err = pid2.Signal(ctx, syscall.SIGINT) + require.NoError(t, err) + + err = pid2.Wait() + require.ErrorAs(t, err, &exitError) + require.Equal(t, uint32(111), exitError.ExitCode) + + pid1.Signal(ctx, syscall.SIGKILL) + pid1.Wait() + return &client.Result{}, err + } + + _, err = c.Build(ctx, SolveOpt{}, product, b, nil) + require.Error(t, err) + + checkAllReleasable(t, c, sb, true) +} + type nopCloser struct { io.Writer } diff --git a/executor/containerdexecutor/executor.go b/executor/containerdexecutor/executor.go index 7506b031476b..18261b60e86f 100644 --- a/executor/containerdexecutor/executor.go +++ b/executor/containerdexecutor/executor.go @@ -199,7 +199,7 @@ func (w *containerdExecutor) Run(ctx context.Context, id string, root executor.M } }() - err = w.runProcess(ctx, task, process.Resize, func() { + err = w.runProcess(ctx, task, process.Resize, process.Signal, func() { startedOnce.Do(func() { if started != nil { close(started) @@ -293,7 +293,7 @@ func (w *containerdExecutor) Exec(ctx context.Context, id string, process execut return errors.WithStack(err) } - err = w.runProcess(ctx, taskProcess, process.Resize, nil) + err = w.runProcess(ctx, taskProcess, process.Resize, process.Signal, nil) return err } @@ -310,7 +310,7 @@ func fixProcessOutput(process *executor.ProcessInfo) { } } -func (w *containerdExecutor) runProcess(ctx context.Context, p containerd.Process, resize <-chan executor.WinSize, started func()) error { +func (w *containerdExecutor) runProcess(ctx context.Context, p containerd.Process, resize <-chan executor.WinSize, signal <-chan syscall.Signal, started func()) error { // Not using `ctx` here because the context passed only affects the statusCh which we // don't want cancelled when ctx.Done is sent. We want to process statusCh on cancel. statusCh, err := p.Wait(context.Background()) @@ -335,22 +335,38 @@ func (w *containerdExecutor) runProcess(ctx context.Context, p containerd.Proces p.CloseIO(ctx, containerd.WithStdinCloser) - // resize in separate go loop so it does not potentially block - // the container cancel/exit status loop below. - resizeCtx, resizeCancel := context.WithCancel(ctx) - defer resizeCancel() + // handle signals (and resize) in separate go loop so it does not + // potentially block the container cancel/exit status loop below. + eventCtx, eventCancel := context.WithCancel(ctx) + defer eventCancel() go func() { for { select { - case <-resizeCtx.Done(): + case <-eventCtx.Done(): return case size, ok := <-resize: if !ok { return // chan closed } - err = p.Resize(resizeCtx, size.Cols, size.Rows) + err = p.Resize(eventCtx, size.Cols, size.Rows) if err != nil { - bklog.G(resizeCtx).Warnf("Failed to resize %s: %s", p.ID(), err) + bklog.G(eventCtx).Warnf("Failed to resize %s: %s", p.ID(), err) + } + } + } + }() + go func() { + for { + select { + case <-eventCtx.Done(): + return + case sig, ok := <-signal: + if !ok { + return // chan closed + } + err = p.Kill(eventCtx, sig) + if err != nil { + bklog.G(eventCtx).Warnf("Failed to signal %s: %s", p.ID(), err) } } } diff --git a/executor/executor.go b/executor/executor.go index 72152b988361..4727af4b03ef 100644 --- a/executor/executor.go +++ b/executor/executor.go @@ -4,6 +4,7 @@ import ( "context" "io" "net" + "syscall" "github.com/moby/buildkit/snapshot" "github.com/moby/buildkit/solver/pb" @@ -45,6 +46,7 @@ type ProcessInfo struct { Stdin io.ReadCloser Stdout, Stderr io.WriteCloser Resize <-chan WinSize + Signal <-chan syscall.Signal } type Executor interface { diff --git a/executor/runcexecutor/executor.go b/executor/runcexecutor/executor.go index 702d5f23e4ff..e7f4b82d9d1b 100644 --- a/executor/runcexecutor/executor.go +++ b/executor/runcexecutor/executor.go @@ -315,14 +315,14 @@ func (w *runcExecutor) Run(ctx context.Context, id string, root executor.Mount, }() bklog.G(ctx).Debugf("> creating %s %v", id, meta.Args) - // this is a cheat, we have not actually started, but as close as we can get with runc for now - if started != nil { + + err = w.run(runCtx, id, bundle, process, func() { startedOnce.Do(func() { - close(started) + if started != nil { + close(started) + } }) - } - - err = w.run(runCtx, id, bundle, process) + }) close(ended) return exitError(ctx, err) } @@ -414,7 +414,7 @@ func (w *runcExecutor) Exec(ctx context.Context, id string, process executor.Pro spec.Process.Env = process.Meta.Env } - err = w.exec(ctx, id, state.Bundle, spec.Process, process) + err = w.exec(ctx, id, state.Bundle, spec.Process, process, nil) return exitError(ctx, err) } @@ -444,3 +444,78 @@ func (s *forwardIO) Stdout() io.ReadCloser { func (s *forwardIO) Stderr() io.ReadCloser { return nil } + +// startingProcess is to track the os process so we can send signals to it. +type startingProcess struct { + Process *os.Process + ready chan struct{} +} + +// Release will free resources with a startingProcess. +func (p *startingProcess) Release() { + if p.Process != nil { + p.Process.Release() + } +} + +// WaitForReady will wait until the Process has been populated or the +// provided context was cancelled. This should be called before using +// the Process field. +func (p *startingProcess) WaitForReady(ctx context.Context) error { + select { + case <-ctx.Done(): + return ctx.Err() + case <-p.ready: + return nil + } +} + +// WaitForStart will record the pid reported by Runc via the channel. +// We wait for up to 10s for the runc process to start. If the started +// callback is non-nil it will be called after receiving the pid. +func (p *startingProcess) WaitForStart(ctx context.Context, startedCh <-chan int, started func()) error { + startedCtx, timeout := context.WithTimeout(ctx, 10*time.Second) + defer timeout() + var err error + select { + case <-startedCtx.Done(): + return errors.New("runc started message never received") + case pid, ok := <-startedCh: + if !ok { + return errors.New("runc process failed to send pid") + } + if started != nil { + started() + } + p.Process, err = os.FindProcess(pid) + if err != nil { + return errors.Wrapf(err, "unable to find runc process for pid %d", pid) + } + close(p.ready) + } + return nil +} + +// handleSignals will wait until the runcProcess is ready then will +// send each signal received on the channel to the process. +func handleSignals(ctx context.Context, runcProcess *startingProcess, signals <-chan syscall.Signal) error { + if signals == nil { + return nil + } + err := runcProcess.WaitForReady(ctx) + if err != nil { + return err + } + for { + select { + case <-ctx.Done(): + return nil + case sig := <-signals: + err := runcProcess.Process.Signal(sig) + if err != nil { + bklog.G(ctx).Errorf("failed to signal %s to process: %s", sig, err) + return err + } + } + } +} diff --git a/executor/runcexecutor/executor_common.go b/executor/runcexecutor/executor_common.go index a41cd524e9a0..447c4a96b958 100644 --- a/executor/runcexecutor/executor_common.go +++ b/executor/runcexecutor/executor_common.go @@ -10,28 +10,63 @@ import ( "github.com/moby/buildkit/executor" "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" + "golang.org/x/sync/errgroup" ) var unsupportedConsoleError = errors.New("tty for runc is only supported on linux") func updateRuncFieldsForHostOS(runtime *runc.Runc) {} -func (w *runcExecutor) run(ctx context.Context, id, bundle string, process executor.ProcessInfo) error { +func (w *runcExecutor) run(ctx context.Context, id, bundle string, process executor.ProcessInfo, started func()) error { if process.Meta.Tty { return unsupportedConsoleError } - _, err := w.runc.Run(ctx, id, bundle, &runc.CreateOpts{ - IO: &forwardIO{stdin: process.Stdin, stdout: process.Stdout, stderr: process.Stderr}, - NoPivot: w.noPivot, + return w.commonCall(ctx, id, bundle, process, started, func(ctx context.Context, started chan<- int, io runc.IO) error { + _, err := w.runc.Run(ctx, id, bundle, &runc.CreateOpts{ + NoPivot: w.noPivot, + Started: started, + IO: io, + }) + return err }) - return err } -func (w *runcExecutor) exec(ctx context.Context, id, bundle string, specsProcess *specs.Process, process executor.ProcessInfo) error { +func (w *runcExecutor) exec(ctx context.Context, id, bundle string, specsProcess *specs.Process, process executor.ProcessInfo, started func()) error { if process.Meta.Tty { return unsupportedConsoleError } - return w.runc.Exec(ctx, id, *specsProcess, &runc.ExecOpts{ - IO: &forwardIO{stdin: process.Stdin, stdout: process.Stdout, stderr: process.Stderr}, + return w.commonCall(ctx, id, bundle, process, started, func(ctx context.Context, started chan<- int, io runc.IO) error { + return w.runc.Exec(ctx, id, *specsProcess, &runc.ExecOpts{ + Started: started, + IO: io, + }) }) } + +type runcCall func(ctx context.Context, started chan<- int, io runc.IO) error + +// commonCall is the common run/exec logic used for non-linux runtimes. A tty +// is only supported for linux, so this really just handles signal propagation +// to the started runc process. +func (w *runcExecutor) commonCall(ctx context.Context, id, bundle string, process executor.ProcessInfo, started func(), call runcCall) error { + runcProcess := &startingProcess{ + ready: make(chan struct{}), + } + defer runcProcess.Release() + + var eg errgroup.Group + egCtx, cancel := context.WithCancel(ctx) + defer eg.Wait() + defer cancel() + + startedCh := make(chan int, 1) + eg.Go(func() error { + return runcProcess.WaitForStart(egCtx, startedCh, started) + }) + + eg.Go(func() error { + return handleSignals(egCtx, runcProcess, process.Signal) + }) + + return call(ctx, startedCh, &forwardIO{stdin: process.Stdin, stdout: process.Stdout, stderr: process.Stderr}) +} diff --git a/executor/runcexecutor/executor_linux.go b/executor/runcexecutor/executor_linux.go index 0eea7c610a85..15ea812a5a23 100644 --- a/executor/runcexecutor/executor_linux.go +++ b/executor/runcexecutor/executor_linux.go @@ -5,7 +5,6 @@ import ( "io" "os" "syscall" - "time" "github.com/containerd/console" runc "github.com/containerd/go-runc" @@ -22,8 +21,8 @@ func updateRuncFieldsForHostOS(runtime *runc.Runc) { runtime.PdeathSignal = syscall.SIGKILL // this can still leak the process } -func (w *runcExecutor) run(ctx context.Context, id, bundle string, process executor.ProcessInfo) error { - return w.callWithIO(ctx, id, bundle, process, func(ctx context.Context, started chan<- int, io runc.IO) error { +func (w *runcExecutor) run(ctx context.Context, id, bundle string, process executor.ProcessInfo, started func()) error { + return w.callWithIO(ctx, id, bundle, process, started, func(ctx context.Context, started chan<- int, io runc.IO) error { _, err := w.runc.Run(ctx, id, bundle, &runc.CreateOpts{ NoPivot: w.noPivot, Started: started, @@ -33,8 +32,8 @@ func (w *runcExecutor) run(ctx context.Context, id, bundle string, process execu }) } -func (w *runcExecutor) exec(ctx context.Context, id, bundle string, specsProcess *specs.Process, process executor.ProcessInfo) error { - return w.callWithIO(ctx, id, bundle, process, func(ctx context.Context, started chan<- int, io runc.IO) error { +func (w *runcExecutor) exec(ctx context.Context, id, bundle string, specsProcess *specs.Process, process executor.ProcessInfo, started func()) error { + return w.callWithIO(ctx, id, bundle, process, started, func(ctx context.Context, started chan<- int, io runc.IO) error { return w.runc.Exec(ctx, id, *specsProcess, &runc.ExecOpts{ Started: started, IO: io, @@ -44,12 +43,28 @@ func (w *runcExecutor) exec(ctx context.Context, id, bundle string, specsProcess type runcCall func(ctx context.Context, started chan<- int, io runc.IO) error -func (w *runcExecutor) callWithIO(ctx context.Context, id, bundle string, process executor.ProcessInfo, call runcCall) error { - ctx, cancel := context.WithCancel(ctx) +func (w *runcExecutor) callWithIO(ctx context.Context, id, bundle string, process executor.ProcessInfo, started func(), call runcCall) error { + runcProcess := &startingProcess{ + ready: make(chan struct{}), + } + defer runcProcess.Release() + + var eg errgroup.Group + egCtx, cancel := context.WithCancel(ctx) + defer eg.Wait() defer cancel() + startedCh := make(chan int, 1) + eg.Go(func() error { + return runcProcess.WaitForStart(egCtx, startedCh, started) + }) + + eg.Go(func() error { + return handleSignals(egCtx, runcProcess, process.Signal) + }) + if !process.Meta.Tty { - return call(ctx, nil, &forwardIO{stdin: process.Stdin, stdout: process.Stdout, stderr: process.Stderr}) + return call(ctx, startedCh, &forwardIO{stdin: process.Stdin, stdout: process.Stdout, stderr: process.Stderr}) } ptm, ptsName, err := console.NewPty() @@ -63,15 +78,13 @@ func (w *runcExecutor) callWithIO(ctx context.Context, id, bundle string, proces return err } - eg, ctx := errgroup.WithContext(ctx) - defer func() { if process.Stdin != nil { process.Stdin.Close() } pts.Close() ptm.Close() - cancel() // this will shutdown resize loop + cancel() // this will shutdown resize and signal loops err := eg.Wait() if err != nil { bklog.G(ctx).Warningf("error while shutting down tty io: %s", err) @@ -105,29 +118,14 @@ func (w *runcExecutor) callWithIO(ctx context.Context, id, bundle string, proces }) } - started := make(chan int, 1) - eg.Go(func() error { - startedCtx, timeout := context.WithTimeout(ctx, 10*time.Second) - defer timeout() - var runcProcess *os.Process - select { - case <-startedCtx.Done(): - return errors.New("runc started message never received") - case pid, ok := <-started: - if !ok { - return errors.New("runc process failed to send pid") - } - runcProcess, err = os.FindProcess(pid) - if err != nil { - return errors.Wrapf(err, "unable to find runc process for pid %d", pid) - } - defer runcProcess.Release() + err := runcProcess.WaitForReady(egCtx) + if err != nil { + return err } - for { select { - case <-ctx.Done(): + case <-egCtx.Done(): return nil case resize := <-process.Resize: err = ptm.Resize(console.WinSize{ @@ -137,7 +135,7 @@ func (w *runcExecutor) callWithIO(ctx context.Context, id, bundle string, proces if err != nil { bklog.G(ctx).Errorf("failed to resize ptm: %s", err) } - err = runcProcess.Signal(signal.SIGWINCH) + err = runcProcess.Process.Signal(signal.SIGWINCH) if err != nil { bklog.G(ctx).Errorf("failed to send SIGWINCH to process: %s", err) } @@ -156,5 +154,5 @@ func (w *runcExecutor) callWithIO(ctx context.Context, id, bundle string, proces runcIO.stderr = pts } - return call(ctx, started, runcIO) + return call(ctx, startedCh, runcIO) } diff --git a/frontend/gateway/client/client.go b/frontend/gateway/client/client.go index 4475a24a7206..61bc018ff5df 100644 --- a/frontend/gateway/client/client.go +++ b/frontend/gateway/client/client.go @@ -3,6 +3,7 @@ package client import ( "context" "io" + "syscall" "github.com/moby/buildkit/client/llb" "github.com/moby/buildkit/solver/pb" @@ -76,7 +77,7 @@ type WinSize struct { type ContainerProcess interface { Wait() error Resize(ctx context.Context, size WinSize) error - // TODO Signal(ctx context.Context, sig os.Signal) + Signal(ctx context.Context, sig syscall.Signal) error } type Reference interface { diff --git a/frontend/gateway/container.go b/frontend/gateway/container.go index dff9e12d8339..824a503ffb55 100644 --- a/frontend/gateway/container.go +++ b/frontend/gateway/container.go @@ -8,6 +8,7 @@ import ( "sort" "strings" "sync" + "syscall" "github.com/moby/buildkit/util/bklog" @@ -294,6 +295,7 @@ type gatewayContainer struct { func (gwCtr *gatewayContainer) Start(ctx context.Context, req client.StartRequest) (client.ContainerProcess, error) { resize := make(chan executor.WinSize) + signal := make(chan syscall.Signal) procInfo := executor.ProcessInfo{ Meta: executor.Meta{ Args: req.Args, @@ -309,6 +311,7 @@ func (gwCtr *gatewayContainer) Start(ctx context.Context, req client.StartReques Stdout: req.Stdout, Stderr: req.Stderr, Resize: resize, + Signal: signal, } if procInfo.Meta.Cwd == "" { procInfo.Meta.Cwd = "/" @@ -328,6 +331,7 @@ func (gwCtr *gatewayContainer) Start(ctx context.Context, req client.StartReques eg, ctx := errgroup.WithContext(gwCtr.ctx) gwProc := &gatewayContainerProcess{ resize: resize, + signal: signal, errGroup: eg, groupCtx: ctx, } @@ -378,6 +382,7 @@ type gatewayContainerProcess struct { errGroup *errgroup.Group groupCtx context.Context resize chan<- executor.WinSize + signal chan<- syscall.Signal mu sync.Mutex } @@ -386,6 +391,7 @@ func (gwProc *gatewayContainerProcess) Wait() error { gwProc.mu.Lock() defer gwProc.mu.Unlock() close(gwProc.resize) + close(gwProc.signal) return err } @@ -393,7 +399,7 @@ func (gwProc *gatewayContainerProcess) Resize(ctx context.Context, size client.W gwProc.mu.Lock() defer gwProc.mu.Unlock() - // is the container done or should we proceed with sending event? + // is the container done or should we proceed with sending event? select { case <-gwProc.groupCtx.Done(): return nil @@ -414,6 +420,31 @@ func (gwProc *gatewayContainerProcess) Resize(ctx context.Context, size client.W return nil } +func (gwProc *gatewayContainerProcess) Signal(ctx context.Context, sig syscall.Signal) error { + gwProc.mu.Lock() + defer gwProc.mu.Unlock() + + // is the container done or should we proceed with sending event? + select { + case <-gwProc.groupCtx.Done(): + return nil + case <-ctx.Done(): + return nil + default: + } + + // now we select on contexts again in case p.signal blocks b/c + // container no longer reading from it. In that case when + // the errgroup finishes we want to unblock on the write + // and exit + select { + case <-gwProc.groupCtx.Done(): + case <-ctx.Done(): + case gwProc.signal <- sig: + } + return nil +} + func addDefaultEnvvar(env []string, k, v string) []string { for _, e := range env { if strings.HasPrefix(e, k+"=") { diff --git a/frontend/gateway/gateway.go b/frontend/gateway/gateway.go index f099b9e136cc..1f8070f7390f 100644 --- a/frontend/gateway/gateway.go +++ b/frontend/gateway/gateway.go @@ -11,6 +11,7 @@ import ( "strconv" "strings" "sync" + "syscall" "time" "github.com/containerd/containerd/mount" @@ -42,6 +43,7 @@ import ( "github.com/moby/buildkit/util/stack" "github.com/moby/buildkit/util/tracing" "github.com/moby/buildkit/worker" + "github.com/moby/sys/signal" digest "github.com/opencontainers/go-digest" ocispecs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" @@ -1008,6 +1010,7 @@ type processIO struct { id string mu sync.Mutex resize func(context.Context, gwclient.WinSize) error + signal func(context.Context, syscall.Signal) error done chan struct{} doneOnce sync.Once // these track the process side of the io pipe for @@ -1146,6 +1149,8 @@ func (lbf *llbBridgeForwarder) ExecProcess(srv pb.LLBBridge_ExecProcessServer) e } case *pb.ExecMessage_Resize: bklog.G(ctx).Debugf("|<--- Resize Message %s", execMsg.ProcessID) + case *pb.ExecMessage_Signal: + bklog.G(ctx).Debugf("|<--- Signal Message %s: %s", execMsg.ProcessID, m.Signal.Name) } select { case <-ctx.Done(): @@ -1198,6 +1203,15 @@ func (lbf *llbBridgeForwarder) ExecProcess(srv pb.LLBBridge_ExecProcessServer) e Cols: resize.Cols, Rows: resize.Rows, }) + } else if sig := execMsg.GetSignal(); sig != nil { + if !pioFound { + return stack.Enable(status.Errorf(codes.NotFound, "IO for process %q not found", pid)) + } + syscallSignal, ok := signal.SignalMap[sig.Name] + if !ok { + return stack.Enable(status.Errorf(codes.InvalidArgument, "Unknown signal %s", sig.Name)) + } + pio.signal(ctx, syscallSignal) } else if init := execMsg.GetInit(); init != nil { if pioFound { return stack.Enable(status.Errorf(codes.AlreadyExists, "Process %s already exists", pid)) @@ -1231,6 +1245,7 @@ func (lbf *llbBridgeForwarder) ExecProcess(srv pb.LLBBridge_ExecProcessServer) e return stack.Enable(err) } pio.resize = proc.Resize + pio.signal = proc.Signal eg.Go(func() error { <-pio.done diff --git a/frontend/gateway/grpcclient/client.go b/frontend/gateway/grpcclient/client.go index 762d69138e4d..a7394891fb9e 100644 --- a/frontend/gateway/grpcclient/client.go +++ b/frontend/gateway/grpcclient/client.go @@ -9,11 +9,9 @@ import ( "os" "strings" "sync" + "syscall" "time" - "github.com/moby/buildkit/util/bklog" - "google.golang.org/grpc/credentials/insecure" - "github.com/gogo/googleapis/google/rpc" gogotypes "github.com/gogo/protobuf/types" "github.com/golang/protobuf/ptypes/any" @@ -23,7 +21,9 @@ import ( "github.com/moby/buildkit/identity" opspb "github.com/moby/buildkit/solver/pb" "github.com/moby/buildkit/util/apicaps" + "github.com/moby/buildkit/util/bklog" "github.com/moby/buildkit/util/grpcerrors" + "github.com/moby/sys/signal" digest "github.com/opencontainers/go-digest" "github.com/pkg/errors" fstypes "github.com/tonistiigi/fsutil/types" @@ -31,6 +31,7 @@ import ( spb "google.golang.org/genproto/googleapis/rpc/status" "google.golang.org/grpc" "google.golang.org/grpc/codes" + "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/status" ) @@ -643,6 +644,8 @@ func debugMessage(msg *pb.ExecMessage) string { return fmt.Sprintf("File Message %s, fd=%d, %d bytes", msg.ProcessID, m.File.Fd, len(m.File.Data)) case *pb.ExecMessage_Resize: return fmt.Sprintf("Resize Message %s", msg.ProcessID) + case *pb.ExecMessage_Signal: + return fmt.Sprintf("Signal Message %s: %s", msg.ProcessID, m.Signal.Name) case *pb.ExecMessage_Started: return fmt.Sprintf("Started Message %s", msg.ProcessID) case *pb.ExecMessage_Exit: @@ -964,6 +967,29 @@ func (ctrProc *containerProcess) Resize(_ context.Context, size client.WinSize) }) } +var sigToName = map[syscall.Signal]string{} + +func init() { + for name, value := range signal.SignalMap { + sigToName[value] = name + } +} + +func (ctrProc *containerProcess) Signal(_ context.Context, sig syscall.Signal) error { + name := sigToName[sig] + if name == "" { + return errors.Errorf("unknown signal %v", sig) + } + return ctrProc.execMsgs.Send(&pb.ExecMessage{ + ProcessID: ctrProc.id, + Input: &pb.ExecMessage_Signal{ + Signal: &pb.SignalMessage{ + Name: name, + }, + }, + }) +} + type reference struct { c *grpcClient id string diff --git a/frontend/gateway/pb/caps.go b/frontend/gateway/pb/caps.go index c60386f2a91a..c4af39f3f0b9 100644 --- a/frontend/gateway/pb/caps.go +++ b/frontend/gateway/pb/caps.go @@ -44,6 +44,10 @@ const ( // /etc/hosts for containers created via gateway exec. CapGatewayExecExtraHosts apicaps.CapID = "gateway.exec.extrahosts" + // CapGatewayExecExtraHosts is the capability to send signals to a process + // created via gateway exec. + CapGatewayExecSignals apicaps.CapID = "gateway.exec.signals" + // CapFrontendCaps can be used to check that frontends define support for certain capabilities CapFrontendCaps apicaps.CapID = "frontend.caps" @@ -169,6 +173,13 @@ func init() { Status: apicaps.CapStatusExperimental, }) + Caps.Init(apicaps.Cap{ + ID: CapGatewayExecSignals, + Name: "gateway exec signals", + Enabled: true, + Status: apicaps.CapStatusExperimental, + }) + Caps.Init(apicaps.Cap{ ID: CapFrontendCaps, Name: "frontend capabilities", diff --git a/frontend/gateway/pb/gateway.pb.go b/frontend/gateway/pb/gateway.pb.go index cacba346a1d2..e8e797ca7e10 100644 --- a/frontend/gateway/pb/gateway.pb.go +++ b/frontend/gateway/pb/gateway.pb.go @@ -1679,6 +1679,7 @@ type ExecMessage struct { // *ExecMessage_Started // *ExecMessage_Exit // *ExecMessage_Done + // *ExecMessage_Signal Input isExecMessage_Input `protobuf_oneof:"Input"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` @@ -1742,6 +1743,9 @@ type ExecMessage_Exit struct { type ExecMessage_Done struct { Done *DoneMessage `protobuf:"bytes,7,opt,name=Done,proto3,oneof" json:"Done,omitempty"` } +type ExecMessage_Signal struct { + Signal *SignalMessage `protobuf:"bytes,8,opt,name=Signal,proto3,oneof" json:"Signal,omitempty"` +} func (*ExecMessage_Init) isExecMessage_Input() {} func (*ExecMessage_File) isExecMessage_Input() {} @@ -1749,6 +1753,7 @@ func (*ExecMessage_Resize) isExecMessage_Input() {} func (*ExecMessage_Started) isExecMessage_Input() {} func (*ExecMessage_Exit) isExecMessage_Input() {} func (*ExecMessage_Done) isExecMessage_Input() {} +func (*ExecMessage_Signal) isExecMessage_Input() {} func (m *ExecMessage) GetInput() isExecMessage_Input { if m != nil { @@ -1806,6 +1811,13 @@ func (m *ExecMessage) GetDone() *DoneMessage { return nil } +func (m *ExecMessage) GetSignal() *SignalMessage { + if x, ok := m.GetInput().(*ExecMessage_Signal); ok { + return x.Signal + } + return nil +} + // XXX_OneofWrappers is for the internal use of the proto package. func (*ExecMessage) XXX_OneofWrappers() []interface{} { return []interface{}{ @@ -1815,6 +1827,7 @@ func (*ExecMessage) XXX_OneofWrappers() []interface{} { (*ExecMessage_Started)(nil), (*ExecMessage_Exit)(nil), (*ExecMessage_Done)(nil), + (*ExecMessage_Signal)(nil), } } @@ -2148,6 +2161,55 @@ func (m *ResizeMessage) GetCols() uint32 { return 0 } +type SignalMessage struct { + // we only send name (ie HUP, INT) because the int values + // are platform dependent. + Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SignalMessage) Reset() { *m = SignalMessage{} } +func (m *SignalMessage) String() string { return proto.CompactTextString(m) } +func (*SignalMessage) ProtoMessage() {} +func (*SignalMessage) Descriptor() ([]byte, []int) { + return fileDescriptor_f1a937782ebbded5, []int{35} +} +func (m *SignalMessage) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignalMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignalMessage.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SignalMessage) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignalMessage.Merge(m, src) +} +func (m *SignalMessage) XXX_Size() int { + return m.Size() +} +func (m *SignalMessage) XXX_DiscardUnknown() { + xxx_messageInfo_SignalMessage.DiscardUnknown(m) +} + +var xxx_messageInfo_SignalMessage proto.InternalMessageInfo + +func (m *SignalMessage) GetName() string { + if m != nil { + return m.Name + } + return "" +} + func init() { proto.RegisterType((*Result)(nil), "moby.buildkit.v1.frontend.Result") proto.RegisterMapType((map[string][]byte)(nil), "moby.buildkit.v1.frontend.Result.MetadataEntry") @@ -2191,140 +2253,143 @@ func init() { proto.RegisterType((*DoneMessage)(nil), "moby.buildkit.v1.frontend.DoneMessage") proto.RegisterType((*FdMessage)(nil), "moby.buildkit.v1.frontend.FdMessage") proto.RegisterType((*ResizeMessage)(nil), "moby.buildkit.v1.frontend.ResizeMessage") + proto.RegisterType((*SignalMessage)(nil), "moby.buildkit.v1.frontend.SignalMessage") } func init() { proto.RegisterFile("gateway.proto", fileDescriptor_f1a937782ebbded5) } var fileDescriptor_f1a937782ebbded5 = []byte{ - // 2048 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x58, 0xcd, 0x6f, 0x1b, 0xc7, - 0x15, 0xf7, 0x8a, 0x94, 0x48, 0x3e, 0x7e, 0x58, 0x19, 0xa7, 0xe9, 0x7a, 0x11, 0x38, 0xca, 0x22, - 0x55, 0x69, 0x47, 0x59, 0xa6, 0x72, 0x02, 0xb9, 0x72, 0x90, 0xd4, 0xfa, 0x82, 0x94, 0x48, 0xb2, - 0x3a, 0x4a, 0x61, 0x20, 0x48, 0x81, 0xae, 0xb8, 0x43, 0x7a, 0xe1, 0xd5, 0xce, 0x76, 0x76, 0x68, - 0x59, 0xc9, 0xa5, 0xbd, 0xf5, 0x58, 0xa0, 0x40, 0xaf, 0x05, 0xfa, 0x17, 0xf4, 0xd2, 0x6b, 0xcf, - 0x39, 0xf6, 0x58, 0xf4, 0x10, 0x14, 0xfe, 0x1b, 0x8a, 0x9e, 0x8b, 0x37, 0x33, 0x4b, 0x2e, 0x29, - 0x6a, 0x29, 0x22, 0x27, 0xee, 0xbc, 0x79, 0xbf, 0x37, 0xef, 0x6b, 0xde, 0x7b, 0x43, 0x68, 0xf6, - 0x7d, 0xc9, 0x2e, 0xfc, 0x4b, 0x2f, 0x11, 0x5c, 0x72, 0x72, 0xf7, 0x9c, 0x9f, 0x5d, 0x7a, 0x67, - 0x83, 0x30, 0x0a, 0x5e, 0x84, 0xd2, 0x7b, 0xf9, 0x33, 0xaf, 0x27, 0x78, 0x2c, 0x59, 0x1c, 0x38, - 0x1f, 0xf4, 0x43, 0xf9, 0x7c, 0x70, 0xe6, 0x75, 0xf9, 0x79, 0xa7, 0xcf, 0xfb, 0xbc, 0xa3, 0x10, - 0x67, 0x83, 0x9e, 0x5a, 0xa9, 0x85, 0xfa, 0xd2, 0x92, 0x9c, 0xf5, 0x49, 0xf6, 0x3e, 0xe7, 0xfd, - 0x88, 0xf9, 0x49, 0x98, 0x9a, 0xcf, 0x8e, 0x48, 0xba, 0x9d, 0x54, 0xfa, 0x72, 0x90, 0x1a, 0xcc, - 0x5a, 0x0e, 0x83, 0x8a, 0x74, 0x32, 0x45, 0x3a, 0x29, 0x8f, 0x5e, 0x32, 0xd1, 0x49, 0xce, 0x3a, - 0x3c, 0xc9, 0xb8, 0x3b, 0xd7, 0x72, 0xfb, 0x49, 0xd8, 0x91, 0x97, 0x09, 0x4b, 0x3b, 0x17, 0x5c, - 0xbc, 0x60, 0xc2, 0x00, 0x1e, 0x5e, 0x0b, 0x18, 0xc8, 0x30, 0x42, 0x54, 0xd7, 0x4f, 0x52, 0x3c, - 0x04, 0x7f, 0x0d, 0x28, 0x6f, 0xb6, 0xe4, 0x71, 0x98, 0xca, 0x30, 0xec, 0x87, 0x9d, 0x5e, 0xaa, - 0x30, 0xfa, 0x14, 0x34, 0x42, 0xb3, 0xbb, 0x7f, 0x28, 0xc1, 0x12, 0x65, 0xe9, 0x20, 0x92, 0x64, - 0x15, 0x9a, 0x82, 0xf5, 0x76, 0x58, 0x22, 0x58, 0xd7, 0x97, 0x2c, 0xb0, 0xad, 0x15, 0xab, 0x5d, - 0xdb, 0xbf, 0x45, 0xc7, 0xc9, 0xe4, 0x57, 0xd0, 0x12, 0xac, 0x97, 0xe6, 0x18, 0x17, 0x56, 0xac, - 0x76, 0x7d, 0xfd, 0x7d, 0xef, 0xda, 0x60, 0x78, 0x94, 0xf5, 0x8e, 0xfc, 0x64, 0x04, 0xd9, 0xbf, - 0x45, 0x27, 0x84, 0x90, 0x75, 0x28, 0x09, 0xd6, 0xb3, 0x4b, 0x4a, 0xd6, 0xbd, 0x62, 0x59, 0xfb, - 0xb7, 0x28, 0x32, 0x93, 0x0d, 0x28, 0xa3, 0x14, 0xbb, 0xac, 0x40, 0xef, 0xce, 0x54, 0x60, 0xff, - 0x16, 0x55, 0x00, 0xf2, 0x05, 0x54, 0xcf, 0x99, 0xf4, 0x03, 0x5f, 0xfa, 0x36, 0xac, 0x94, 0xda, - 0xf5, 0xf5, 0x4e, 0x21, 0x18, 0x1d, 0xe4, 0x1d, 0x19, 0xc4, 0x6e, 0x2c, 0xc5, 0x25, 0x1d, 0x0a, - 0x70, 0x1e, 0x43, 0x73, 0x6c, 0x8b, 0x2c, 0x43, 0xe9, 0x05, 0xbb, 0xd4, 0xfe, 0xa3, 0xf8, 0x49, - 0xde, 0x84, 0xc5, 0x97, 0x7e, 0x34, 0x60, 0xca, 0x55, 0x0d, 0xaa, 0x17, 0x9b, 0x0b, 0x8f, 0xac, - 0xad, 0x2a, 0x2c, 0x09, 0x25, 0xde, 0xfd, 0xb3, 0x05, 0xcb, 0x93, 0x7e, 0x22, 0x07, 0xc6, 0x42, - 0x4b, 0x29, 0xf9, 0xf1, 0x1c, 0x2e, 0x46, 0x42, 0xaa, 0x55, 0x55, 0x22, 0x9c, 0x0d, 0xa8, 0x0d, - 0x49, 0xb3, 0x54, 0xac, 0xe5, 0x54, 0x74, 0x37, 0xa0, 0x44, 0x59, 0x8f, 0xb4, 0x60, 0x21, 0x34, - 0x49, 0x41, 0x17, 0xc2, 0x80, 0xac, 0x40, 0x29, 0x60, 0x3d, 0x13, 0xfc, 0x96, 0x97, 0x9c, 0x79, - 0x3b, 0xac, 0x17, 0xc6, 0xa1, 0x0c, 0x79, 0x4c, 0x71, 0xcb, 0xfd, 0xab, 0x85, 0xc9, 0x85, 0x6a, - 0x91, 0xcf, 0xc6, 0xec, 0x98, 0x9d, 0x2a, 0x57, 0xb4, 0x7f, 0x56, 0xac, 0xfd, 0x47, 0x79, 0xed, - 0x67, 0xe6, 0x4f, 0xde, 0x3a, 0x09, 0x4d, 0xca, 0xe4, 0x40, 0xc4, 0x94, 0xfd, 0x76, 0xc0, 0x52, - 0x49, 0x7e, 0x9e, 0x45, 0x44, 0xc9, 0x9f, 0x95, 0x56, 0xc8, 0x48, 0x0d, 0x80, 0xb4, 0x61, 0x91, - 0x09, 0xc1, 0x85, 0xd1, 0x82, 0x78, 0xba, 0x72, 0x78, 0x22, 0xe9, 0x7a, 0xa7, 0xaa, 0x72, 0x50, - 0xcd, 0xe0, 0x2e, 0x43, 0x2b, 0x3b, 0x35, 0x4d, 0x78, 0x9c, 0x32, 0xf7, 0x36, 0x34, 0x0f, 0xe2, - 0x64, 0x20, 0x53, 0xa3, 0x87, 0xfb, 0x0f, 0x0b, 0x5a, 0x19, 0x45, 0xf3, 0x90, 0xaf, 0xa1, 0x3e, - 0xf2, 0x71, 0xe6, 0xcc, 0xcd, 0x02, 0xfd, 0xc6, 0xf1, 0xb9, 0x00, 0x19, 0xdf, 0xe6, 0xc5, 0x39, - 0xc7, 0xb0, 0x3c, 0xc9, 0x30, 0xc5, 0xd3, 0xef, 0x8d, 0x7b, 0x7a, 0x32, 0xf0, 0x39, 0xcf, 0xfe, - 0xc9, 0x82, 0xbb, 0x94, 0xa9, 0x52, 0x78, 0x70, 0xee, 0xf7, 0xd9, 0x36, 0x8f, 0x7b, 0x61, 0x3f, - 0x73, 0xf3, 0xb2, 0xca, 0xaa, 0x4c, 0x32, 0x26, 0x58, 0x1b, 0xaa, 0x27, 0x91, 0x2f, 0x7b, 0x5c, - 0x9c, 0x1b, 0xe1, 0x0d, 0x14, 0x9e, 0xd1, 0xe8, 0x70, 0x97, 0xac, 0x40, 0xdd, 0x08, 0x3e, 0xe2, - 0x01, 0x53, 0x35, 0xa3, 0x46, 0xf3, 0x24, 0x62, 0x43, 0xe5, 0x90, 0xf7, 0x8f, 0xfd, 0x73, 0xa6, - 0x8a, 0x43, 0x8d, 0x66, 0x4b, 0xf7, 0x77, 0x16, 0x38, 0xd3, 0xb4, 0x32, 0x2e, 0xfe, 0x1c, 0x96, - 0x76, 0xc2, 0x3e, 0x4b, 0x75, 0xf4, 0x6b, 0x5b, 0xeb, 0xdf, 0x7d, 0xff, 0xce, 0xad, 0x7f, 0x7f, - 0xff, 0xce, 0x83, 0x5c, 0x5d, 0xe5, 0x09, 0x8b, 0xbb, 0x3c, 0x96, 0x7e, 0x18, 0x33, 0x81, 0xed, - 0xe1, 0x83, 0x40, 0x41, 0x3c, 0x8d, 0xa4, 0x46, 0x02, 0x79, 0x0b, 0x96, 0xb4, 0x74, 0x73, 0xed, - 0xcd, 0xca, 0xfd, 0xef, 0x22, 0x34, 0x4e, 0x51, 0x81, 0xcc, 0x17, 0x1e, 0xc0, 0xc8, 0x85, 0x26, - 0xed, 0x26, 0x1d, 0x9b, 0xe3, 0x20, 0x0e, 0x54, 0xf7, 0x4c, 0x88, 0xcd, 0x75, 0x1d, 0xae, 0xc9, - 0x57, 0x50, 0xcf, 0xbe, 0x9f, 0x26, 0xd2, 0x2e, 0xa9, 0x1c, 0x79, 0x54, 0x90, 0x23, 0x79, 0x4d, - 0xbc, 0x1c, 0xd4, 0x64, 0x48, 0x8e, 0x42, 0x3e, 0x81, 0xbb, 0x07, 0xe7, 0x09, 0x17, 0x72, 0xdb, - 0xef, 0x3e, 0x67, 0x74, 0xbc, 0x0b, 0x94, 0x57, 0x4a, 0xed, 0x1a, 0xbd, 0x9e, 0x81, 0xac, 0xc1, - 0x1b, 0x7e, 0x14, 0xf1, 0x0b, 0x73, 0x69, 0x54, 0xfa, 0xdb, 0x8b, 0x2b, 0x56, 0xbb, 0x4a, 0xaf, - 0x6e, 0x90, 0x0f, 0xe1, 0x4e, 0x8e, 0xf8, 0x44, 0x08, 0xff, 0x12, 0xf3, 0x65, 0x49, 0xf1, 0x4f, - 0xdb, 0xc2, 0x0a, 0xb6, 0x17, 0xc6, 0x7e, 0x64, 0x83, 0xe2, 0xd1, 0x0b, 0xe2, 0x42, 0x63, 0xf7, - 0x15, 0xaa, 0xc4, 0xc4, 0x13, 0x29, 0x85, 0x5d, 0x57, 0xa1, 0x18, 0xa3, 0x91, 0x13, 0x68, 0x28, - 0x85, 0xb5, 0xee, 0xa9, 0xdd, 0x50, 0x4e, 0x5b, 0x2b, 0x70, 0x9a, 0x62, 0x7f, 0x9a, 0xe4, 0xae, - 0xd2, 0x98, 0x04, 0xd2, 0x85, 0x56, 0xe6, 0x38, 0x7d, 0x07, 0xed, 0xa6, 0x92, 0xf9, 0x78, 0xde, - 0x40, 0x68, 0xb4, 0x3e, 0x62, 0x42, 0x24, 0xa6, 0xc1, 0x2e, 0x5e, 0x37, 0x5f, 0x32, 0xbb, 0xa5, - 0x6c, 0x1e, 0xae, 0x9d, 0x4f, 0x61, 0x79, 0x32, 0x96, 0xf3, 0x14, 0x7d, 0xe7, 0x97, 0x70, 0x67, - 0x8a, 0x0a, 0x3f, 0xa8, 0x1e, 0xfc, 0xcd, 0x82, 0x37, 0xae, 0xf8, 0x8d, 0x10, 0x28, 0x7f, 0x79, - 0x99, 0x30, 0x23, 0x52, 0x7d, 0x93, 0x23, 0x58, 0xc4, 0xb8, 0xa4, 0xf6, 0x82, 0x72, 0xda, 0xc6, - 0x3c, 0x81, 0xf0, 0x14, 0x52, 0x3b, 0x4c, 0x4b, 0x71, 0x1e, 0x01, 0x8c, 0x88, 0x73, 0xb5, 0xbe, - 0xaf, 0xa1, 0x69, 0xa2, 0x62, 0xca, 0xc3, 0xb2, 0x9e, 0x52, 0x0c, 0x18, 0x67, 0x90, 0x51, 0xbb, - 0x28, 0xcd, 0xd9, 0x2e, 0xdc, 0x6f, 0xe1, 0x36, 0x65, 0x7e, 0xb0, 0x17, 0x46, 0xec, 0xfa, 0xaa, - 0x88, 0x77, 0x3d, 0x8c, 0xd8, 0x89, 0x2f, 0x9f, 0x0f, 0xef, 0xba, 0x59, 0x93, 0x4d, 0x58, 0xa4, - 0x7e, 0xdc, 0x67, 0xe6, 0xe8, 0xf7, 0x0a, 0x8e, 0x56, 0x87, 0x20, 0x2f, 0xd5, 0x10, 0xf7, 0x31, - 0xd4, 0x86, 0x34, 0xac, 0x54, 0x4f, 0x7b, 0xbd, 0x94, 0xe9, 0xaa, 0x57, 0xa2, 0x66, 0x85, 0xf4, - 0x43, 0x16, 0xf7, 0xcd, 0xd1, 0x25, 0x6a, 0x56, 0xee, 0x2a, 0x8e, 0x2a, 0x99, 0xe6, 0xc6, 0x35, - 0x04, 0xca, 0x3b, 0x38, 0x4f, 0x59, 0xea, 0x82, 0xa9, 0x6f, 0x37, 0xc0, 0x36, 0xe7, 0x07, 0x3b, - 0xa1, 0xb8, 0xde, 0x40, 0x1b, 0x2a, 0x3b, 0xa1, 0xc8, 0xd9, 0x97, 0x2d, 0xc9, 0x2a, 0x36, 0xc0, - 0x6e, 0x34, 0x08, 0xd0, 0x5a, 0xc9, 0x44, 0x6c, 0x2a, 0xfd, 0x04, 0xd5, 0xfd, 0x4c, 0xfb, 0x51, - 0x9d, 0x62, 0x94, 0x59, 0x83, 0x0a, 0x8b, 0xa5, 0x08, 0x59, 0xd6, 0x25, 0x89, 0xa7, 0x47, 0x60, - 0x4f, 0x8d, 0xc0, 0xaa, 0x1b, 0xd3, 0x8c, 0xc5, 0xdd, 0x80, 0xdb, 0x48, 0x28, 0x0e, 0x04, 0x81, - 0x72, 0x4e, 0x49, 0xf5, 0xed, 0x6e, 0xc2, 0xf2, 0x08, 0x68, 0x8e, 0x5e, 0x85, 0x32, 0x0e, 0xd8, - 0xa6, 0x8c, 0x4f, 0x3b, 0x57, 0xed, 0xbb, 0x4d, 0xa8, 0x9f, 0x84, 0x71, 0xd6, 0x0f, 0xdd, 0xd7, - 0x16, 0x34, 0x4e, 0x78, 0x3c, 0xea, 0x44, 0x27, 0x70, 0x3b, 0xbb, 0x81, 0x4f, 0x4e, 0x0e, 0xb6, - 0xfd, 0x24, 0x33, 0x65, 0xe5, 0x6a, 0x98, 0xcd, 0x5b, 0xc0, 0xd3, 0x8c, 0x5b, 0x65, 0x6c, 0x5a, - 0x74, 0x12, 0x4e, 0x7e, 0x01, 0x95, 0xc3, 0xc3, 0x2d, 0x25, 0x69, 0x61, 0x2e, 0x49, 0x19, 0x8c, - 0x7c, 0x0a, 0x95, 0x67, 0xea, 0x89, 0x92, 0x9a, 0xc6, 0x32, 0x25, 0xe5, 0xb4, 0xa1, 0x9a, 0x8d, - 0xb2, 0x2e, 0x17, 0x01, 0xcd, 0x40, 0xee, 0xff, 0x2c, 0xa8, 0x3f, 0xf3, 0x47, 0xb3, 0xd6, 0xe7, - 0xb0, 0x14, 0xfc, 0xe0, 0x6e, 0xab, 0x97, 0x78, 0x8b, 0x23, 0xf6, 0x92, 0x45, 0x26, 0x55, 0xf5, - 0x02, 0xa9, 0xe9, 0x73, 0x2e, 0xf4, 0xed, 0x6c, 0x50, 0xbd, 0xc0, 0xbc, 0x0e, 0x98, 0xf4, 0xc3, - 0x48, 0x75, 0xad, 0x06, 0x35, 0x2b, 0x8c, 0xfa, 0x40, 0x44, 0xaa, 0x29, 0xd5, 0x28, 0x7e, 0x12, - 0x17, 0xca, 0x61, 0xdc, 0xe3, 0xaa, 0xef, 0x98, 0xea, 0x76, 0xca, 0x07, 0xa2, 0xcb, 0x0e, 0xe2, - 0x1e, 0xa7, 0x6a, 0x8f, 0xbc, 0x0b, 0x4b, 0x02, 0xaf, 0x51, 0x6a, 0x57, 0x94, 0x53, 0x6a, 0xc8, - 0xa5, 0x2f, 0x9b, 0xd9, 0x70, 0x5b, 0xd0, 0xd0, 0x76, 0x9b, 0x69, 0xef, 0x8f, 0x0b, 0x70, 0xe7, - 0x98, 0x5d, 0x6c, 0x67, 0x76, 0x65, 0x0e, 0x59, 0x81, 0xfa, 0x90, 0x76, 0xb0, 0x63, 0xd2, 0x2f, - 0x4f, 0xc2, 0xc3, 0x8e, 0xf8, 0x20, 0x96, 0x59, 0x0c, 0xd5, 0x61, 0x8a, 0x42, 0xcd, 0x06, 0xf9, - 0x09, 0x54, 0x8e, 0x99, 0xc4, 0xb7, 0xa4, 0xb2, 0xba, 0xb5, 0x5e, 0x47, 0x9e, 0x63, 0x26, 0x71, - 0x34, 0xa2, 0xd9, 0x1e, 0xce, 0x5b, 0x49, 0x36, 0x6f, 0x95, 0xa7, 0xcd, 0x5b, 0xd9, 0x2e, 0xd9, - 0x80, 0x7a, 0x97, 0xc7, 0xa9, 0x14, 0x7e, 0x88, 0x07, 0x2f, 0x2a, 0xe6, 0x1f, 0x21, 0xb3, 0x0e, - 0xec, 0xf6, 0x68, 0x93, 0xe6, 0x39, 0xc9, 0x03, 0x00, 0xf6, 0x4a, 0x0a, 0x7f, 0x9f, 0xa7, 0x32, - 0xb5, 0x97, 0x94, 0xc2, 0x80, 0x38, 0x24, 0x1c, 0x9c, 0xd0, 0xdc, 0xae, 0xfb, 0x16, 0xbc, 0x39, - 0xee, 0x11, 0xe3, 0xaa, 0xc7, 0xf0, 0x63, 0xca, 0x22, 0xe6, 0xa7, 0x6c, 0x7e, 0x6f, 0xb9, 0x0e, - 0xd8, 0x57, 0xc1, 0x46, 0xf0, 0xdf, 0x4b, 0x50, 0xdf, 0x7d, 0xc5, 0xba, 0x47, 0x2c, 0x4d, 0xfd, - 0x3e, 0x23, 0x6f, 0x43, 0xed, 0x44, 0xf0, 0x2e, 0x4b, 0xd3, 0xa1, 0xac, 0x11, 0x81, 0x7c, 0x02, - 0xe5, 0x83, 0x38, 0x94, 0xa6, 0xcd, 0xad, 0x16, 0x0e, 0xdd, 0xa1, 0x34, 0x32, 0xf1, 0xc1, 0x89, - 0x4b, 0xb2, 0x09, 0x65, 0x2c, 0x12, 0x37, 0x29, 0xd4, 0x41, 0x0e, 0x8b, 0x18, 0xb2, 0xa5, 0x9e, - 0xe8, 0xe1, 0x37, 0xcc, 0x44, 0xa9, 0x5d, 0xdc, 0x61, 0xc2, 0x6f, 0xd8, 0x48, 0x82, 0x41, 0x92, - 0x5d, 0xa8, 0x9c, 0x4a, 0x5f, 0xe0, 0x9c, 0xa6, 0xa3, 0x77, 0xbf, 0x68, 0x10, 0xd1, 0x9c, 0x23, - 0x29, 0x19, 0x16, 0x9d, 0xb0, 0xfb, 0x2a, 0x94, 0xe6, 0x36, 0x14, 0x39, 0x01, 0xd9, 0x72, 0x86, - 0xe0, 0x12, 0xd1, 0x3b, 0x3c, 0x66, 0x76, 0x65, 0x26, 0x1a, 0xd9, 0x72, 0x68, 0x5c, 0x6e, 0x55, - 0x60, 0x51, 0x4d, 0x22, 0xee, 0x5f, 0x2c, 0xa8, 0xe7, 0x7c, 0x7c, 0x83, 0x3b, 0xf3, 0x36, 0x94, - 0xf1, 0x85, 0x6e, 0x62, 0x57, 0x55, 0x37, 0x86, 0x49, 0x9f, 0x2a, 0x2a, 0x5e, 0xfa, 0xbd, 0x40, - 0x17, 0xb4, 0x26, 0xc5, 0x4f, 0xa4, 0x7c, 0x29, 0x2f, 0x95, 0xbb, 0xab, 0x14, 0x3f, 0xc9, 0x1a, - 0x54, 0x4f, 0x59, 0x77, 0x20, 0x42, 0x79, 0xa9, 0x1c, 0xd8, 0x5a, 0x5f, 0x56, 0xa5, 0xc0, 0xd0, - 0xd4, 0xc5, 0x1a, 0x72, 0xb8, 0x5f, 0x60, 0x62, 0x8d, 0x14, 0x24, 0x50, 0xde, 0xc6, 0x77, 0x0a, - 0x6a, 0xd6, 0xa4, 0xea, 0x1b, 0x9f, 0x8a, 0xbb, 0xb3, 0x9e, 0x8a, 0xbb, 0xd9, 0x53, 0x71, 0x3c, - 0x20, 0xd8, 0x39, 0x72, 0x0e, 0x72, 0x9f, 0x40, 0x6d, 0x98, 0x34, 0xf8, 0x4a, 0xdf, 0x0b, 0xcc, - 0x49, 0x0b, 0x7b, 0x01, 0x9a, 0xb2, 0xfb, 0x74, 0x4f, 0x9d, 0x52, 0xa5, 0xf8, 0x39, 0xec, 0xd3, - 0xa5, 0x5c, 0x9f, 0xde, 0xc0, 0x47, 0x70, 0x2e, 0x73, 0x90, 0x89, 0xf2, 0x8b, 0x34, 0x53, 0x19, - 0xbf, 0xb5, 0x19, 0x51, 0xaa, 0x64, 0x29, 0x33, 0xa2, 0x74, 0xfd, 0x5f, 0x35, 0xa8, 0x1d, 0x1e, - 0x6e, 0x6d, 0x89, 0x30, 0xe8, 0x33, 0xf2, 0x7b, 0x0b, 0xc8, 0xd5, 0xb7, 0x15, 0xf9, 0xa8, 0x38, - 0x61, 0xa7, 0x3f, 0x10, 0x9d, 0x8f, 0xe7, 0x44, 0x99, 0xb6, 0xf9, 0x15, 0x2c, 0xaa, 0x91, 0x8d, - 0xfc, 0xf4, 0x86, 0xa3, 0xb6, 0xd3, 0x9e, 0xcd, 0x68, 0x64, 0x77, 0xa1, 0x9a, 0x8d, 0x3d, 0xe4, - 0x41, 0xa1, 0x7a, 0x63, 0x53, 0x9d, 0xf3, 0xfe, 0x8d, 0x78, 0xcd, 0x21, 0xbf, 0x81, 0x8a, 0x99, - 0x66, 0xc8, 0xfd, 0x19, 0xb8, 0xd1, 0x5c, 0xe5, 0x3c, 0xb8, 0x09, 0xeb, 0xc8, 0x8c, 0x6c, 0x6a, - 0x29, 0x34, 0x63, 0x62, 0x26, 0x2a, 0x34, 0xe3, 0xca, 0x18, 0xf4, 0x0c, 0xca, 0x38, 0xde, 0x90, - 0xa2, 0x6b, 0x9e, 0x9b, 0x7f, 0x9c, 0xa2, 0x70, 0x8d, 0xcd, 0x45, 0xbf, 0xc6, 0x72, 0xa8, 0x9e, - 0x88, 0xc5, 0x85, 0x30, 0xf7, 0x9f, 0x8e, 0x73, 0xff, 0x06, 0x9c, 0x23, 0xf1, 0xe6, 0x79, 0xd5, - 0xbe, 0xc1, 0x1f, 0x2b, 0xb3, 0xc5, 0x4f, 0xfc, 0x85, 0xc3, 0xa1, 0x91, 0xef, 0x72, 0xc4, 0x2b, - 0x80, 0x4e, 0x19, 0x10, 0x9c, 0xce, 0x8d, 0xf9, 0xcd, 0x81, 0xdf, 0xe2, 0xa8, 0x3e, 0xde, 0x01, - 0xc9, 0x7a, 0xa1, 0x3b, 0xa6, 0xf6, 0x5a, 0xe7, 0xe1, 0x5c, 0x18, 0x73, 0xb8, 0xaf, 0x3b, 0xac, - 0xe9, 0xa2, 0xa4, 0xb8, 0x61, 0x0c, 0x3b, 0xb1, 0x73, 0x43, 0xbe, 0xb6, 0xf5, 0xa1, 0x85, 0x79, - 0x86, 0x93, 0x55, 0xa1, 0xec, 0xdc, 0xc8, 0x59, 0x98, 0x67, 0xf9, 0x11, 0x6d, 0xab, 0xf1, 0xdd, - 0xeb, 0x7b, 0xd6, 0x3f, 0x5f, 0xdf, 0xb3, 0xfe, 0xf3, 0xfa, 0x9e, 0x75, 0xb6, 0xa4, 0xfe, 0x2f, - 0x7f, 0xf8, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x8b, 0x9a, 0x89, 0x6b, 0x81, 0x18, 0x00, 0x00, + // 2078 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x58, 0x4f, 0x6f, 0x1b, 0xc7, + 0x15, 0xd7, 0x8a, 0x94, 0x48, 0x3e, 0xfe, 0xb1, 0x32, 0x4e, 0x53, 0x7a, 0x11, 0x38, 0xca, 0x36, + 0x55, 0x69, 0x47, 0x59, 0xa6, 0x72, 0x02, 0xb9, 0x72, 0x90, 0xd4, 0xfa, 0x07, 0x29, 0x91, 0x64, + 0x75, 0x94, 0xc2, 0x40, 0x90, 0x02, 0x5d, 0x71, 0x87, 0xf4, 0xc2, 0xab, 0x9d, 0xed, 0xec, 0xd0, + 0xb2, 0x92, 0x4b, 0x7b, 0xeb, 0xb1, 0x40, 0x81, 0x5e, 0x0b, 0xf4, 0x13, 0xf4, 0x13, 0xf4, 0x9c, + 0x63, 0x8f, 0x45, 0x0f, 0x41, 0xe1, 0xcf, 0x50, 0x14, 0xe8, 0x2d, 0x78, 0x33, 0xb3, 0xe4, 0x92, + 0xa2, 0x96, 0x24, 0x7c, 0xe2, 0xcc, 0xdb, 0xf7, 0x7b, 0xf3, 0xfe, 0xcd, 0x7b, 0x6f, 0x08, 0xf5, + 0x9e, 0x27, 0xd9, 0xa5, 0x77, 0xe5, 0xc6, 0x82, 0x4b, 0x4e, 0xee, 0x5c, 0xf0, 0xf3, 0x2b, 0xf7, + 0xbc, 0x1f, 0x84, 0xfe, 0xf3, 0x40, 0xba, 0x2f, 0x7e, 0xee, 0x76, 0x05, 0x8f, 0x24, 0x8b, 0x7c, + 0xfb, 0x83, 0x5e, 0x20, 0x9f, 0xf5, 0xcf, 0xdd, 0x0e, 0xbf, 0x68, 0xf7, 0x78, 0x8f, 0xb7, 0x15, + 0xe2, 0xbc, 0xdf, 0x55, 0x3b, 0xb5, 0x51, 0x2b, 0x2d, 0xc9, 0xde, 0x18, 0x67, 0xef, 0x71, 0xde, + 0x0b, 0x99, 0x17, 0x07, 0x89, 0x59, 0xb6, 0x45, 0xdc, 0x69, 0x27, 0xd2, 0x93, 0xfd, 0xc4, 0x60, + 0xd6, 0x33, 0x18, 0x54, 0xa4, 0x9d, 0x2a, 0xd2, 0x4e, 0x78, 0xf8, 0x82, 0x89, 0x76, 0x7c, 0xde, + 0xe6, 0x71, 0xca, 0xdd, 0xbe, 0x91, 0xdb, 0x8b, 0x83, 0xb6, 0xbc, 0x8a, 0x59, 0xd2, 0xbe, 0xe4, + 0xe2, 0x39, 0x13, 0x06, 0xf0, 0xe0, 0x46, 0x40, 0x5f, 0x06, 0x21, 0xa2, 0x3a, 0x5e, 0x9c, 0xe0, + 0x21, 0xf8, 0x6b, 0x40, 0x59, 0xb3, 0x25, 0x8f, 0x82, 0x44, 0x06, 0x41, 0x2f, 0x68, 0x77, 0x13, + 0x85, 0xd1, 0xa7, 0xa0, 0x11, 0x9a, 0xdd, 0xf9, 0x63, 0x01, 0x96, 0x29, 0x4b, 0xfa, 0xa1, 0x24, + 0x6b, 0x50, 0x17, 0xac, 0xbb, 0xcb, 0x62, 0xc1, 0x3a, 0x9e, 0x64, 0x7e, 0xd3, 0x5a, 0xb5, 0x5a, + 0x95, 0x83, 0x05, 0x3a, 0x4a, 0x26, 0xbf, 0x86, 0x86, 0x60, 0xdd, 0x24, 0xc3, 0xb8, 0xb8, 0x6a, + 0xb5, 0xaa, 0x1b, 0xef, 0xbb, 0x37, 0x06, 0xc3, 0xa5, 0xac, 0x7b, 0xec, 0xc5, 0x43, 0xc8, 0xc1, + 0x02, 0x1d, 0x13, 0x42, 0x36, 0xa0, 0x20, 0x58, 0xb7, 0x59, 0x50, 0xb2, 0xee, 0xe6, 0xcb, 0x3a, + 0x58, 0xa0, 0xc8, 0x4c, 0x36, 0xa1, 0x88, 0x52, 0x9a, 0x45, 0x05, 0x7a, 0x77, 0xaa, 0x02, 0x07, + 0x0b, 0x54, 0x01, 0xc8, 0x17, 0x50, 0xbe, 0x60, 0xd2, 0xf3, 0x3d, 0xe9, 0x35, 0x61, 0xb5, 0xd0, + 0xaa, 0x6e, 0xb4, 0x73, 0xc1, 0xe8, 0x20, 0xf7, 0xd8, 0x20, 0xf6, 0x22, 0x29, 0xae, 0xe8, 0x40, + 0x80, 0xfd, 0x08, 0xea, 0x23, 0x9f, 0xc8, 0x0a, 0x14, 0x9e, 0xb3, 0x2b, 0xed, 0x3f, 0x8a, 0x4b, + 0xf2, 0x26, 0x2c, 0xbd, 0xf0, 0xc2, 0x3e, 0x53, 0xae, 0xaa, 0x51, 0xbd, 0xd9, 0x5a, 0x7c, 0x68, + 0x6d, 0x97, 0x61, 0x59, 0x28, 0xf1, 0xce, 0x5f, 0x2c, 0x58, 0x19, 0xf7, 0x13, 0x39, 0x34, 0x16, + 0x5a, 0x4a, 0xc9, 0x8f, 0xe7, 0x70, 0x31, 0x12, 0x12, 0xad, 0xaa, 0x12, 0x61, 0x6f, 0x42, 0x65, + 0x40, 0x9a, 0xa6, 0x62, 0x25, 0xa3, 0xa2, 0xb3, 0x09, 0x05, 0xca, 0xba, 0xa4, 0x01, 0x8b, 0x81, + 0x49, 0x0a, 0xba, 0x18, 0xf8, 0x64, 0x15, 0x0a, 0x3e, 0xeb, 0x9a, 0xe0, 0x37, 0xdc, 0xf8, 0xdc, + 0xdd, 0x65, 0xdd, 0x20, 0x0a, 0x64, 0xc0, 0x23, 0x8a, 0x9f, 0x9c, 0xbf, 0x59, 0x98, 0x5c, 0xa8, + 0x16, 0xf9, 0x6c, 0xc4, 0x8e, 0xe9, 0xa9, 0x72, 0x4d, 0xfb, 0xa7, 0xf9, 0xda, 0x7f, 0x94, 0xd5, + 0x7e, 0x6a, 0xfe, 0x64, 0xad, 0x93, 0x50, 0xa7, 0x4c, 0xf6, 0x45, 0x44, 0xd9, 0xef, 0xfa, 0x2c, + 0x91, 0xe4, 0x17, 0x69, 0x44, 0x94, 0xfc, 0x69, 0x69, 0x85, 0x8c, 0xd4, 0x00, 0x48, 0x0b, 0x96, + 0x98, 0x10, 0x5c, 0x18, 0x2d, 0x88, 0xab, 0x2b, 0x87, 0x2b, 0xe2, 0x8e, 0x7b, 0xa6, 0x2a, 0x07, + 0xd5, 0x0c, 0xce, 0x0a, 0x34, 0xd2, 0x53, 0x93, 0x98, 0x47, 0x09, 0x73, 0x6e, 0x41, 0xfd, 0x30, + 0x8a, 0xfb, 0x32, 0x31, 0x7a, 0x38, 0xff, 0xb0, 0xa0, 0x91, 0x52, 0x34, 0x0f, 0xf9, 0x1a, 0xaa, + 0x43, 0x1f, 0xa7, 0xce, 0xdc, 0xca, 0xd1, 0x6f, 0x14, 0x9f, 0x09, 0x90, 0xf1, 0x6d, 0x56, 0x9c, + 0x7d, 0x02, 0x2b, 0xe3, 0x0c, 0x13, 0x3c, 0xfd, 0xde, 0xa8, 0xa7, 0xc7, 0x03, 0x9f, 0xf1, 0xec, + 0x9f, 0x2d, 0xb8, 0x43, 0x99, 0x2a, 0x85, 0x87, 0x17, 0x5e, 0x8f, 0xed, 0xf0, 0xa8, 0x1b, 0xf4, + 0x52, 0x37, 0xaf, 0xa8, 0xac, 0x4a, 0x25, 0x63, 0x82, 0xb5, 0xa0, 0x7c, 0x1a, 0x7a, 0xb2, 0xcb, + 0xc5, 0x85, 0x11, 0x5e, 0x43, 0xe1, 0x29, 0x8d, 0x0e, 0xbe, 0x92, 0x55, 0xa8, 0x1a, 0xc1, 0xc7, + 0xdc, 0x67, 0xaa, 0x66, 0x54, 0x68, 0x96, 0x44, 0x9a, 0x50, 0x3a, 0xe2, 0xbd, 0x13, 0xef, 0x82, + 0xa9, 0xe2, 0x50, 0xa1, 0xe9, 0xd6, 0xf9, 0xbd, 0x05, 0xf6, 0x24, 0xad, 0x8c, 0x8b, 0x3f, 0x87, + 0xe5, 0xdd, 0xa0, 0xc7, 0x12, 0x1d, 0xfd, 0xca, 0xf6, 0xc6, 0x77, 0xdf, 0xbf, 0xb3, 0xf0, 0xef, + 0xef, 0xdf, 0xb9, 0x9f, 0xa9, 0xab, 0x3c, 0x66, 0x51, 0x87, 0x47, 0xd2, 0x0b, 0x22, 0x26, 0xb0, + 0x3d, 0x7c, 0xe0, 0x2b, 0x88, 0xab, 0x91, 0xd4, 0x48, 0x20, 0x6f, 0xc1, 0xb2, 0x96, 0x6e, 0xae, + 0xbd, 0xd9, 0x39, 0xff, 0x5d, 0x82, 0xda, 0x19, 0x2a, 0x90, 0xfa, 0xc2, 0x05, 0x18, 0xba, 0xd0, + 0xa4, 0xdd, 0xb8, 0x63, 0x33, 0x1c, 0xc4, 0x86, 0xf2, 0xbe, 0x09, 0xb1, 0xb9, 0xae, 0x83, 0x3d, + 0xf9, 0x0a, 0xaa, 0xe9, 0xfa, 0x49, 0x2c, 0x9b, 0x05, 0x95, 0x23, 0x0f, 0x73, 0x72, 0x24, 0xab, + 0x89, 0x9b, 0x81, 0x9a, 0x0c, 0xc9, 0x50, 0xc8, 0x27, 0x70, 0xe7, 0xf0, 0x22, 0xe6, 0x42, 0xee, + 0x78, 0x9d, 0x67, 0x8c, 0x8e, 0x76, 0x81, 0xe2, 0x6a, 0xa1, 0x55, 0xa1, 0x37, 0x33, 0x90, 0x75, + 0x78, 0xc3, 0x0b, 0x43, 0x7e, 0x69, 0x2e, 0x8d, 0x4a, 0xff, 0xe6, 0xd2, 0xaa, 0xd5, 0x2a, 0xd3, + 0xeb, 0x1f, 0xc8, 0x87, 0x70, 0x3b, 0x43, 0x7c, 0x2c, 0x84, 0x77, 0x85, 0xf9, 0xb2, 0xac, 0xf8, + 0x27, 0x7d, 0xc2, 0x0a, 0xb6, 0x1f, 0x44, 0x5e, 0xd8, 0x04, 0xc5, 0xa3, 0x37, 0xc4, 0x81, 0xda, + 0xde, 0x4b, 0x54, 0x89, 0x89, 0xc7, 0x52, 0x8a, 0x66, 0x55, 0x85, 0x62, 0x84, 0x46, 0x4e, 0xa1, + 0xa6, 0x14, 0xd6, 0xba, 0x27, 0xcd, 0x9a, 0x72, 0xda, 0x7a, 0x8e, 0xd3, 0x14, 0xfb, 0x93, 0x38, + 0x73, 0x95, 0x46, 0x24, 0x90, 0x0e, 0x34, 0x52, 0xc7, 0xe9, 0x3b, 0xd8, 0xac, 0x2b, 0x99, 0x8f, + 0xe6, 0x0d, 0x84, 0x46, 0xeb, 0x23, 0xc6, 0x44, 0x62, 0x1a, 0xec, 0xe1, 0x75, 0xf3, 0x24, 0x6b, + 0x36, 0x94, 0xcd, 0x83, 0xbd, 0xfd, 0x29, 0xac, 0x8c, 0xc7, 0x72, 0x9e, 0xa2, 0x6f, 0xff, 0x0a, + 0x6e, 0x4f, 0x50, 0xe1, 0xb5, 0xea, 0xc1, 0xdf, 0x2d, 0x78, 0xe3, 0x9a, 0xdf, 0x08, 0x81, 0xe2, + 0x97, 0x57, 0x31, 0x33, 0x22, 0xd5, 0x9a, 0x1c, 0xc3, 0x12, 0xc6, 0x25, 0x69, 0x2e, 0x2a, 0xa7, + 0x6d, 0xce, 0x13, 0x08, 0x57, 0x21, 0xb5, 0xc3, 0xb4, 0x14, 0xfb, 0x21, 0xc0, 0x90, 0x38, 0x57, + 0xeb, 0xfb, 0x1a, 0xea, 0x26, 0x2a, 0xa6, 0x3c, 0xac, 0xe8, 0x29, 0xc5, 0x80, 0x71, 0x06, 0x19, + 0xb6, 0x8b, 0xc2, 0x9c, 0xed, 0xc2, 0xf9, 0x16, 0x6e, 0x51, 0xe6, 0xf9, 0xfb, 0x41, 0xc8, 0x6e, + 0xae, 0x8a, 0x78, 0xd7, 0x83, 0x90, 0x9d, 0x7a, 0xf2, 0xd9, 0xe0, 0xae, 0x9b, 0x3d, 0xd9, 0x82, + 0x25, 0xea, 0x45, 0x3d, 0x66, 0x8e, 0x7e, 0x2f, 0xe7, 0x68, 0x75, 0x08, 0xf2, 0x52, 0x0d, 0x71, + 0x1e, 0x41, 0x65, 0x40, 0xc3, 0x4a, 0xf5, 0xa4, 0xdb, 0x4d, 0x98, 0xae, 0x7a, 0x05, 0x6a, 0x76, + 0x48, 0x3f, 0x62, 0x51, 0xcf, 0x1c, 0x5d, 0xa0, 0x66, 0xe7, 0xac, 0xe1, 0xa8, 0x92, 0x6a, 0x6e, + 0x5c, 0x43, 0xa0, 0xb8, 0x8b, 0xf3, 0x94, 0xa5, 0x2e, 0x98, 0x5a, 0x3b, 0x3e, 0xb6, 0x39, 0xcf, + 0xdf, 0x0d, 0xc4, 0xcd, 0x06, 0x36, 0xa1, 0xb4, 0x1b, 0x88, 0x8c, 0x7d, 0xe9, 0x96, 0xac, 0x61, + 0x03, 0xec, 0x84, 0x7d, 0x1f, 0xad, 0x95, 0x4c, 0x44, 0xa6, 0xd2, 0x8f, 0x51, 0x9d, 0xcf, 0xb4, + 0x1f, 0xd5, 0x29, 0x46, 0x99, 0x75, 0x28, 0xb1, 0x48, 0x8a, 0x80, 0xa5, 0x5d, 0x92, 0xb8, 0x7a, + 0x04, 0x76, 0xd5, 0x08, 0xac, 0xba, 0x31, 0x4d, 0x59, 0x9c, 0x4d, 0xb8, 0x85, 0x84, 0xfc, 0x40, + 0x10, 0x28, 0x66, 0x94, 0x54, 0x6b, 0x67, 0x0b, 0x56, 0x86, 0x40, 0x73, 0xf4, 0x1a, 0x14, 0x71, + 0xc0, 0x36, 0x65, 0x7c, 0xd2, 0xb9, 0xea, 0xbb, 0x53, 0x87, 0xea, 0x69, 0x10, 0xa5, 0xfd, 0xd0, + 0x79, 0x65, 0x41, 0xed, 0x94, 0x47, 0xc3, 0x4e, 0x74, 0x0a, 0xb7, 0xd2, 0x1b, 0xf8, 0xf8, 0xf4, + 0x70, 0xc7, 0x8b, 0x53, 0x53, 0x56, 0xaf, 0x87, 0xd9, 0xbc, 0x05, 0x5c, 0xcd, 0xb8, 0x5d, 0xc4, + 0xa6, 0x45, 0xc7, 0xe1, 0xe4, 0x97, 0x50, 0x3a, 0x3a, 0xda, 0x56, 0x92, 0x16, 0xe7, 0x92, 0x94, + 0xc2, 0xc8, 0xa7, 0x50, 0x7a, 0xaa, 0x9e, 0x28, 0x89, 0x69, 0x2c, 0x13, 0x52, 0x4e, 0x1b, 0xaa, + 0xd9, 0x28, 0xeb, 0x70, 0xe1, 0xd3, 0x14, 0xe4, 0xfc, 0xcf, 0x82, 0xea, 0x53, 0x6f, 0x38, 0x6b, + 0x7d, 0x0e, 0xcb, 0xfe, 0x6b, 0x77, 0x5b, 0xbd, 0xc5, 0x5b, 0x1c, 0xb2, 0x17, 0x2c, 0x34, 0xa9, + 0xaa, 0x37, 0x48, 0x4d, 0x9e, 0x71, 0xa1, 0x6f, 0x67, 0x8d, 0xea, 0x0d, 0xe6, 0xb5, 0xcf, 0xa4, + 0x17, 0x84, 0xaa, 0x6b, 0xd5, 0xa8, 0xd9, 0x61, 0xd4, 0xfb, 0x22, 0x54, 0x4d, 0xa9, 0x42, 0x71, + 0x49, 0x1c, 0x28, 0x06, 0x51, 0x97, 0xab, 0xbe, 0x63, 0xaa, 0xdb, 0x19, 0xef, 0x8b, 0x0e, 0x3b, + 0x8c, 0xba, 0x9c, 0xaa, 0x6f, 0xe4, 0x5d, 0x58, 0x16, 0x78, 0x8d, 0x92, 0x66, 0x49, 0x39, 0xa5, + 0x82, 0x5c, 0xfa, 0xb2, 0x99, 0x0f, 0x4e, 0x03, 0x6a, 0xda, 0x6e, 0x33, 0xed, 0xfd, 0x69, 0x11, + 0x6e, 0x9f, 0xb0, 0xcb, 0x9d, 0xd4, 0xae, 0xd4, 0x21, 0xab, 0x50, 0x1d, 0xd0, 0x0e, 0x77, 0x4d, + 0xfa, 0x65, 0x49, 0x78, 0xd8, 0x31, 0xef, 0x47, 0x32, 0x8d, 0xa1, 0x3a, 0x4c, 0x51, 0xa8, 0xf9, + 0x40, 0x7e, 0x0a, 0xa5, 0x13, 0x26, 0xf1, 0x2d, 0xa9, 0xac, 0x6e, 0x6c, 0x54, 0x91, 0xe7, 0x84, + 0x49, 0x1c, 0x8d, 0x68, 0xfa, 0x0d, 0xe7, 0xad, 0x38, 0x9d, 0xb7, 0x8a, 0x93, 0xe6, 0xad, 0xf4, + 0x2b, 0xd9, 0x84, 0x6a, 0x87, 0x47, 0x89, 0x14, 0x5e, 0x80, 0x07, 0x2f, 0x29, 0xe6, 0x1f, 0x21, + 0xb3, 0x0e, 0xec, 0xce, 0xf0, 0x23, 0xcd, 0x72, 0x92, 0xfb, 0x00, 0xec, 0xa5, 0x14, 0xde, 0x01, + 0x4f, 0x64, 0xd2, 0x5c, 0x56, 0x0a, 0x03, 0xe2, 0x90, 0x70, 0x78, 0x4a, 0x33, 0x5f, 0x9d, 0xb7, + 0xe0, 0xcd, 0x51, 0x8f, 0x18, 0x57, 0x3d, 0x82, 0x1f, 0x53, 0x16, 0x32, 0x2f, 0x61, 0xf3, 0x7b, + 0xcb, 0xb1, 0xa1, 0x79, 0x1d, 0x6c, 0x04, 0xff, 0xbf, 0x00, 0xd5, 0xbd, 0x97, 0xac, 0x73, 0xcc, + 0x92, 0xc4, 0xeb, 0x31, 0xf2, 0x36, 0x54, 0x4e, 0x05, 0xef, 0xb0, 0x24, 0x19, 0xc8, 0x1a, 0x12, + 0xc8, 0x27, 0x50, 0x3c, 0x8c, 0x02, 0x69, 0xda, 0xdc, 0x5a, 0xee, 0xd0, 0x1d, 0x48, 0x23, 0x13, + 0x1f, 0x9c, 0xb8, 0x25, 0x5b, 0x50, 0xc4, 0x22, 0x31, 0x4b, 0xa1, 0xf6, 0x33, 0x58, 0xc4, 0x90, + 0x6d, 0xf5, 0x44, 0x0f, 0xbe, 0x61, 0x26, 0x4a, 0xad, 0xfc, 0x0e, 0x13, 0x7c, 0xc3, 0x86, 0x12, + 0x0c, 0x92, 0xec, 0x41, 0xe9, 0x4c, 0x7a, 0x02, 0xe7, 0x34, 0x1d, 0xbd, 0x7b, 0x79, 0x83, 0x88, + 0xe6, 0x1c, 0x4a, 0x49, 0xb1, 0xe8, 0x84, 0xbd, 0x97, 0x81, 0x34, 0xb7, 0x21, 0xcf, 0x09, 0xc8, + 0x96, 0x31, 0x04, 0xb7, 0x88, 0xde, 0xe5, 0x11, 0x6b, 0x96, 0xa6, 0xa2, 0x91, 0x2d, 0x83, 0xc6, + 0x2d, 0xba, 0xe1, 0x2c, 0xe8, 0xe1, 0x7c, 0x57, 0x9e, 0xea, 0x06, 0xcd, 0x98, 0x71, 0x83, 0x26, + 0x6c, 0x97, 0x60, 0x49, 0x4d, 0x33, 0xce, 0x5f, 0x2d, 0xa8, 0x66, 0xe2, 0x34, 0xc3, 0xbd, 0x7b, + 0x1b, 0x8a, 0xf8, 0xca, 0x37, 0xf1, 0x2f, 0xab, 0x5b, 0xc7, 0xa4, 0x47, 0x15, 0x15, 0x0b, 0xc7, + 0xbe, 0xaf, 0x8b, 0x62, 0x9d, 0xe2, 0x12, 0x29, 0x5f, 0xca, 0x2b, 0x15, 0xb2, 0x32, 0xc5, 0x25, + 0x59, 0x87, 0xf2, 0x19, 0xeb, 0xf4, 0x45, 0x20, 0xaf, 0x54, 0x10, 0x1a, 0x1b, 0x2b, 0xaa, 0x9c, + 0x18, 0x9a, 0xba, 0x9c, 0x03, 0x0e, 0xe7, 0x0b, 0x4c, 0xce, 0xa1, 0x82, 0x04, 0x8a, 0x3b, 0xf8, + 0xd6, 0x41, 0xcd, 0xea, 0x54, 0xad, 0xf1, 0xb9, 0xb9, 0x37, 0xed, 0xb9, 0xb9, 0x97, 0x3e, 0x37, + 0x47, 0x83, 0x8a, 0xdd, 0x27, 0xe3, 0x64, 0xe7, 0x31, 0x54, 0x06, 0x89, 0x87, 0x2f, 0xfd, 0x7d, + 0xdf, 0x9c, 0xb4, 0xb8, 0xef, 0xa3, 0x29, 0x7b, 0x4f, 0xf6, 0xd5, 0x29, 0x65, 0x8a, 0xcb, 0x41, + 0xaf, 0x2f, 0x64, 0x7a, 0xfd, 0x26, 0x3e, 0xa4, 0x33, 0xd9, 0x87, 0x4c, 0x94, 0x5f, 0x26, 0xa9, + 0xca, 0xb8, 0xd6, 0x66, 0x84, 0x89, 0x92, 0xa5, 0xcc, 0x08, 0x13, 0xe7, 0x27, 0x50, 0x1f, 0x89, + 0x17, 0x32, 0xa9, 0x97, 0x9b, 0x19, 0x09, 0x71, 0xbd, 0xf1, 0xaf, 0x0a, 0x54, 0x8e, 0x8e, 0xb6, + 0xb7, 0x45, 0xe0, 0xf7, 0x18, 0xf9, 0x83, 0x05, 0xe4, 0xfa, 0x23, 0x8e, 0x7c, 0x94, 0x7f, 0x33, + 0x26, 0xbf, 0x44, 0xed, 0x8f, 0xe7, 0x44, 0x99, 0xfe, 0xfc, 0x15, 0x2c, 0xa9, 0xd9, 0x90, 0xfc, + 0x6c, 0xc6, 0x99, 0xde, 0x6e, 0x4d, 0x67, 0x34, 0xb2, 0x3b, 0x50, 0x4e, 0xe7, 0x2b, 0x72, 0x3f, + 0x57, 0xbd, 0x91, 0xf1, 0xd1, 0x7e, 0x7f, 0x26, 0x5e, 0x73, 0xc8, 0x6f, 0xa1, 0x64, 0xc6, 0x26, + 0x72, 0x6f, 0x0a, 0x6e, 0x38, 0xc0, 0xd9, 0xf7, 0x67, 0x61, 0x1d, 0x9a, 0x91, 0x8e, 0x47, 0xb9, + 0x66, 0x8c, 0x0d, 0x5f, 0xb9, 0x66, 0x5c, 0x9b, 0xb7, 0x9e, 0x42, 0x11, 0xe7, 0x28, 0x92, 0x57, + 0x4f, 0x32, 0x83, 0x96, 0x9d, 0x17, 0xae, 0x91, 0x01, 0xec, 0x37, 0x58, 0x77, 0xd5, 0x5b, 0x34, + 0xbf, 0xe2, 0x66, 0xfe, 0x3c, 0xb2, 0xef, 0xcd, 0xc0, 0x39, 0x14, 0x6f, 0xde, 0x71, 0xad, 0x19, + 0xfe, 0xc1, 0x99, 0x2e, 0x7e, 0xec, 0xbf, 0x22, 0x0e, 0xb5, 0x6c, 0x3b, 0x25, 0x6e, 0x0e, 0x74, + 0xc2, 0x24, 0x62, 0xb7, 0x67, 0xe6, 0x37, 0x07, 0x7e, 0x8b, 0x6f, 0x82, 0xd1, 0x56, 0x4b, 0x36, + 0x72, 0xdd, 0x31, 0xb1, 0xa9, 0xdb, 0x0f, 0xe6, 0xc2, 0x98, 0xc3, 0x3d, 0xdd, 0xca, 0x4d, 0xbb, + 0x26, 0xf9, 0x9d, 0x69, 0xd0, 0xf2, 0xed, 0x19, 0xf9, 0x5a, 0xd6, 0x87, 0x16, 0xe6, 0x19, 0x8e, + 0x70, 0xb9, 0xb2, 0x33, 0xb3, 0x6d, 0x6e, 0x9e, 0x65, 0x67, 0xc1, 0xed, 0xda, 0x77, 0xaf, 0xee, + 0x5a, 0xff, 0x7c, 0x75, 0xd7, 0xfa, 0xcf, 0xab, 0xbb, 0xd6, 0xf9, 0xb2, 0xfa, 0x63, 0xfe, 0xc1, + 0x0f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x92, 0x5d, 0x25, 0xb8, 0xea, 0x18, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -4500,6 +4565,27 @@ func (m *ExecMessage_Done) MarshalToSizedBuffer(dAtA []byte) (int, error) { } return len(dAtA) - i, nil } +func (m *ExecMessage_Signal) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ExecMessage_Signal) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.Signal != nil { + { + size, err := m.Signal.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGateway(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + return len(dAtA) - i, nil +} func (m *InitMessage) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -4540,20 +4626,20 @@ func (m *InitMessage) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x20 } if len(m.Fds) > 0 { - dAtA25 := make([]byte, len(m.Fds)*10) - var j24 int + dAtA26 := make([]byte, len(m.Fds)*10) + var j25 int for _, num := range m.Fds { for num >= 1<<7 { - dAtA25[j24] = uint8(uint64(num)&0x7f | 0x80) + dAtA26[j25] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j24++ + j25++ } - dAtA25[j24] = uint8(num) - j24++ + dAtA26[j25] = uint8(num) + j25++ } - i -= j24 - copy(dAtA[i:], dAtA25[:j24]) - i = encodeVarintGateway(dAtA, i, uint64(j24)) + i -= j25 + copy(dAtA[i:], dAtA26[:j25]) + i = encodeVarintGateway(dAtA, i, uint64(j25)) i-- dAtA[i] = 0x1a } @@ -4763,6 +4849,40 @@ func (m *ResizeMessage) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *SignalMessage) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SignalMessage) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignalMessage) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintGateway(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintGateway(dAtA []byte, offset int, v uint64) int { offset -= sovGateway(v) base := offset @@ -5545,6 +5665,18 @@ func (m *ExecMessage_Done) Size() (n int) { } return n } +func (m *ExecMessage_Signal) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Signal != nil { + l = m.Signal.Size() + n += 1 + l + sovGateway(uint64(l)) + } + return n +} func (m *InitMessage) Size() (n int) { if m == nil { return 0 @@ -5661,6 +5793,22 @@ func (m *ResizeMessage) Size() (n int) { return n } +func (m *SignalMessage) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovGateway(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + func sovGateway(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -10031,6 +10179,41 @@ func (m *ExecMessage) Unmarshal(dAtA []byte) error { } m.Input = &ExecMessage_Done{v} iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signal", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGateway + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGateway + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &SignalMessage{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Input = &ExecMessage_Signal{v} + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGateway(dAtA[iNdEx:]) @@ -10708,6 +10891,89 @@ func (m *ResizeMessage) Unmarshal(dAtA []byte) error { } return nil } +func (m *SignalMessage) 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 ErrIntOverflowGateway + } + 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: SignalMessage: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignalMessage: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + 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 ErrInvalidLengthGateway + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGateway + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGateway(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGateway + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipGateway(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/frontend/gateway/pb/gateway.proto b/frontend/gateway/pb/gateway.proto index 388a46ef718d..31aaf3b20d85 100644 --- a/frontend/gateway/pb/gateway.proto +++ b/frontend/gateway/pb/gateway.proto @@ -223,6 +223,8 @@ message ExecMessage { // DoneMessage from server to client will be the last message for any // process. Note that FdMessage might be sent after ExitMessage. DoneMessage Done = 7; + // SignalMessage is used from client to server to send signal events + SignalMessage Signal = 8; } } @@ -253,3 +255,9 @@ message ResizeMessage{ uint32 Rows = 1; uint32 Cols = 2; } + +message SignalMessage { + // we only send name (ie HUP, INT) because the int values + // are platform dependent. + string Name = 1; +}