mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Implement configurable detach key
Implement configurable detach keys (for `attach`, exec`, `run` and `start`) using the client-side configuration - Adds a `--detach-keys` flag to `attach`, `exec`, `run` and `start` commands. - Adds a new configuration field (in `~/.docker/config.json`) to configure the default escape keys for docker client. Signed-off-by: Vincent Demeester <vincent@sbr.pm>
This commit is contained in:
parent
eb551baf6f
commit
15aa2a663b
27 changed files with 583 additions and 61 deletions
|
@ -18,6 +18,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
|
|||
cmd := Cli.Subcmd("attach", []string{"CONTAINER"}, Cli.DockerCommands["attach"].Description, true)
|
||||
noStdin := cmd.Bool([]string{"-no-stdin"}, false, "Do not attach STDIN")
|
||||
proxy := cmd.Bool([]string{"-sig-proxy"}, true, "Proxy all received signals to the process")
|
||||
detachKeys := cmd.String([]string{"-detach-keys"}, "", "Override the key sequence for detaching a container")
|
||||
|
||||
cmd.Require(flag.Exact, 1)
|
||||
|
||||
|
@ -46,12 +47,17 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
|
|||
}
|
||||
}
|
||||
|
||||
if *detachKeys != "" {
|
||||
cli.configFile.DetachKeys = *detachKeys
|
||||
}
|
||||
|
||||
options := types.ContainerAttachOptions{
|
||||
ContainerID: cmd.Arg(0),
|
||||
Stream: true,
|
||||
Stdin: !*noStdin && c.Config.OpenStdin,
|
||||
Stdout: true,
|
||||
Stderr: true,
|
||||
DetachKeys: cli.configFile.DetachKeys,
|
||||
}
|
||||
|
||||
var in io.ReadCloser
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
// Usage: docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
|
||||
func (cli *DockerCli) CmdExec(args ...string) error {
|
||||
cmd := Cli.Subcmd("exec", []string{"CONTAINER COMMAND [ARG...]"}, Cli.DockerCommands["exec"].Description, true)
|
||||
detachKeys := cmd.String([]string{"-detach-keys"}, "", "Override the key sequence for detaching a container")
|
||||
|
||||
execConfig, err := runconfig.ParseExec(cmd, args)
|
||||
// just in case the ParseExec does not exit
|
||||
|
@ -23,6 +24,13 @@ func (cli *DockerCli) CmdExec(args ...string) error {
|
|||
return Cli.StatusError{StatusCode: 1}
|
||||
}
|
||||
|
||||
if *detachKeys != "" {
|
||||
cli.configFile.DetachKeys = *detachKeys
|
||||
}
|
||||
|
||||
// Send client escape keys
|
||||
execConfig.DetachKeys = cli.configFile.DetachKeys
|
||||
|
||||
response, err := cli.client.ContainerExecCreate(*execConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -24,6 +24,9 @@ func (cli *Client) ContainerAttach(options types.ContainerAttachOptions) (types.
|
|||
if options.Stderr {
|
||||
query.Set("stderr", "1")
|
||||
}
|
||||
if options.DetachKeys != "" {
|
||||
query.Set("detachKeys", options.DetachKeys)
|
||||
}
|
||||
|
||||
headers := map[string][]string{"Content-Type": {"text/plain"}}
|
||||
return cli.postHijacked("/containers/"+options.ContainerID+"/attach", query, nil, headers)
|
||||
|
|
|
@ -74,6 +74,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
|||
flDetach = cmd.Bool([]string{"d", "-detach"}, false, "Run container in background and print container ID")
|
||||
flSigProxy = cmd.Bool([]string{"-sig-proxy"}, true, "Proxy received signals to the process")
|
||||
flName = cmd.String([]string{"-name"}, "", "Assign a name to the container")
|
||||
flDetachKeys = cmd.String([]string{"-detach-keys"}, "", "Override the key sequence for detaching a container")
|
||||
flAttach *opts.ListOpts
|
||||
|
||||
ErrConflictAttachDetach = fmt.Errorf("Conflicting options: -a and -d")
|
||||
|
@ -188,12 +189,17 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
|||
}
|
||||
}
|
||||
|
||||
if *flDetachKeys != "" {
|
||||
cli.configFile.DetachKeys = *flDetachKeys
|
||||
}
|
||||
|
||||
options := types.ContainerAttachOptions{
|
||||
ContainerID: createResponse.ID,
|
||||
Stream: true,
|
||||
Stdin: config.AttachStdin,
|
||||
Stdout: config.AttachStdout,
|
||||
Stderr: config.AttachStderr,
|
||||
DetachKeys: cli.configFile.DetachKeys,
|
||||
}
|
||||
|
||||
resp, err := cli.client.ContainerAttach(options)
|
||||
|
|
|
@ -49,6 +49,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
|
|||
cmd := Cli.Subcmd("start", []string{"CONTAINER [CONTAINER...]"}, Cli.DockerCommands["start"].Description, true)
|
||||
attach := cmd.Bool([]string{"a", "-attach"}, false, "Attach STDOUT/STDERR and forward signals")
|
||||
openStdin := cmd.Bool([]string{"i", "-interactive"}, false, "Attach container's STDIN")
|
||||
detachKeys := cmd.String([]string{"-detach-keys"}, "", "Override the key sequence for detaching a container")
|
||||
cmd.Require(flag.Min, 1)
|
||||
|
||||
cmd.ParseFlags(args, true)
|
||||
|
@ -72,12 +73,17 @@ func (cli *DockerCli) CmdStart(args ...string) error {
|
|||
defer signal.StopCatch(sigc)
|
||||
}
|
||||
|
||||
if *detachKeys != "" {
|
||||
cli.configFile.DetachKeys = *detachKeys
|
||||
}
|
||||
|
||||
options := types.ContainerAttachOptions{
|
||||
ContainerID: containerID,
|
||||
Stream: true,
|
||||
Stdin: *openStdin && c.Config.OpenStdin,
|
||||
Stdout: true,
|
||||
Stderr: true,
|
||||
DetachKeys: cli.configFile.DetachKeys,
|
||||
}
|
||||
|
||||
var in io.ReadCloser
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/signal"
|
||||
"github.com/docker/docker/pkg/term"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/docker/utils"
|
||||
"golang.org/x/net/context"
|
||||
|
@ -420,13 +421,23 @@ func (s *containerRouter) postContainersResize(ctx context.Context, w http.Respo
|
|||
}
|
||||
|
||||
func (s *containerRouter) postContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
err := httputils.ParseForm(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
containerName := vars["name"]
|
||||
|
||||
_, upgrade := r.Header["Upgrade"]
|
||||
|
||||
keys := []byte{}
|
||||
detachKeys := r.FormValue("detachKeys")
|
||||
if detachKeys != "" {
|
||||
keys, err = term.ToBytes(detachKeys)
|
||||
if err != nil {
|
||||
logrus.Warnf("Invalid escape keys provided (%s) using default : ctrl-p ctrl-q", detachKeys)
|
||||
}
|
||||
}
|
||||
|
||||
attachWithLogsConfig := &daemon.ContainerAttachWithLogsConfig{
|
||||
Hijacker: w.(http.Hijacker),
|
||||
Upgrade: upgrade,
|
||||
|
@ -435,6 +446,7 @@ func (s *containerRouter) postContainersAttach(ctx context.Context, w http.Respo
|
|||
UseStderr: httputils.BoolValue(r, "stderr"),
|
||||
Logs: httputils.BoolValue(r, "logs"),
|
||||
Stream: httputils.BoolValue(r, "stream"),
|
||||
DetachKeys: keys,
|
||||
}
|
||||
|
||||
return s.backend.ContainerAttachWithLogs(containerName, attachWithLogsConfig)
|
||||
|
@ -450,6 +462,16 @@ func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.Respons
|
|||
return derr.ErrorCodeNoSuchContainer.WithArgs(containerName)
|
||||
}
|
||||
|
||||
var keys []byte
|
||||
var err error
|
||||
detachKeys := r.FormValue("detachKeys")
|
||||
if detachKeys != "" {
|
||||
keys, err = term.ToBytes(detachKeys)
|
||||
if err != nil {
|
||||
logrus.Warnf("Invalid escape keys provided (%s) using default : ctrl-p ctrl-q", detachKeys)
|
||||
}
|
||||
}
|
||||
|
||||
h := websocket.Handler(func(ws *websocket.Conn) {
|
||||
defer ws.Close()
|
||||
|
||||
|
@ -459,6 +481,7 @@ func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.Respons
|
|||
ErrStream: ws,
|
||||
Logs: httputils.BoolValue(r, "logs"),
|
||||
Stream: httputils.BoolValue(r, "stream"),
|
||||
DetachKeys: keys,
|
||||
}
|
||||
|
||||
if err := s.backend.ContainerWsAttachWithLogs(containerName, wsAttachWithLogsConfig); err != nil {
|
||||
|
|
|
@ -17,6 +17,7 @@ type ContainerAttachOptions struct {
|
|||
Stdin bool
|
||||
Stdout bool
|
||||
Stderr bool
|
||||
DetachKeys string
|
||||
}
|
||||
|
||||
// ContainerCommitOptions holds parameters to commit changes into a container.
|
||||
|
|
|
@ -45,5 +45,6 @@ type ExecConfig struct {
|
|||
AttachStderr bool // Attach the standard output
|
||||
AttachStdout bool // Attach the standard error
|
||||
Detach bool // Execute in detach mode
|
||||
DetachKeys string // Escape keys for detach
|
||||
Cmd []string // Execution commands and args
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ type ConfigFile struct {
|
|||
HTTPHeaders map[string]string `json:"HttpHeaders,omitempty"`
|
||||
PsFormat string `json:"psFormat,omitempty"`
|
||||
ImagesFormat string `json:"imagesFormat,omitempty"`
|
||||
DetachKeys string `json:"detachKeys,omitempty"`
|
||||
filename string // Note: not serialized - for internal use only
|
||||
}
|
||||
|
||||
|
|
|
@ -329,13 +329,13 @@ func (container *Container) GetExecIDs() []string {
|
|||
|
||||
// Attach connects to the container's TTY, delegating to standard
|
||||
// streams or websockets depending on the configuration.
|
||||
func (container *Container) Attach(stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) chan error {
|
||||
return AttachStreams(container.StreamConfig, container.Config.OpenStdin, container.Config.StdinOnce, container.Config.Tty, stdin, stdout, stderr)
|
||||
func (container *Container) Attach(stdin io.ReadCloser, stdout io.Writer, stderr io.Writer, keys []byte) chan error {
|
||||
return AttachStreams(container.StreamConfig, container.Config.OpenStdin, container.Config.StdinOnce, container.Config.Tty, stdin, stdout, stderr, keys)
|
||||
}
|
||||
|
||||
// AttachStreams connects streams to a TTY.
|
||||
// Used by exec too. Should this move somewhere else?
|
||||
func AttachStreams(streamConfig *runconfig.StreamConfig, openStdin, stdinOnce, tty bool, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) chan error {
|
||||
func AttachStreams(streamConfig *runconfig.StreamConfig, openStdin, stdinOnce, tty bool, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer, keys []byte) chan error {
|
||||
var (
|
||||
cStdout, cStderr io.ReadCloser
|
||||
cStdin io.WriteCloser
|
||||
|
@ -382,7 +382,7 @@ func AttachStreams(streamConfig *runconfig.StreamConfig, openStdin, stdinOnce, t
|
|||
|
||||
var err error
|
||||
if tty {
|
||||
_, err = copyEscapable(cStdin, stdin)
|
||||
_, err = copyEscapable(cStdin, stdin, keys)
|
||||
} else {
|
||||
_, err = io.Copy(cStdin, stdin)
|
||||
|
||||
|
@ -438,22 +438,27 @@ func AttachStreams(streamConfig *runconfig.StreamConfig, openStdin, stdinOnce, t
|
|||
}
|
||||
|
||||
// Code c/c from io.Copy() modified to handle escape sequence
|
||||
func copyEscapable(dst io.Writer, src io.ReadCloser) (written int64, err error) {
|
||||
func copyEscapable(dst io.Writer, src io.ReadCloser, keys []byte) (written int64, err error) {
|
||||
if len(keys) == 0 {
|
||||
// Default keys : ctrl-p ctrl-q
|
||||
keys = []byte{16, 17}
|
||||
}
|
||||
buf := make([]byte, 32*1024)
|
||||
for {
|
||||
nr, er := src.Read(buf)
|
||||
if nr > 0 {
|
||||
// ---- Docker addition
|
||||
// char 16 is C-p
|
||||
if nr == 1 && buf[0] == 16 {
|
||||
nr, er = src.Read(buf)
|
||||
// char 17 is C-q
|
||||
if nr == 1 && buf[0] == 17 {
|
||||
for i, key := range keys {
|
||||
if nr != 1 || buf[0] != key {
|
||||
break
|
||||
}
|
||||
if i == len(keys)-1 {
|
||||
if err := src.Close(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
nr, er = src.Read(buf)
|
||||
}
|
||||
// ---- End of docker
|
||||
nw, ew := dst.Write(buf[0:nr])
|
||||
|
|
|
@ -22,6 +22,7 @@ type ContainerAttachWithLogsConfig struct {
|
|||
UseStderr bool
|
||||
Logs bool
|
||||
Stream bool
|
||||
DetachKeys []byte
|
||||
}
|
||||
|
||||
// ContainerAttachWithLogs attaches to logs according to the config passed in. See ContainerAttachWithLogsConfig.
|
||||
|
@ -75,7 +76,7 @@ func (daemon *Daemon) ContainerAttachWithLogs(prefixOrName string, c *ContainerA
|
|||
stderr = errStream
|
||||
}
|
||||
|
||||
if err := daemon.attachWithLogs(container, stdin, stdout, stderr, c.Logs, c.Stream); err != nil {
|
||||
if err := daemon.attachWithLogs(container, stdin, stdout, stderr, c.Logs, c.Stream, c.DetachKeys); err != nil {
|
||||
fmt.Fprintf(outStream, "Error attaching: %s\n", err)
|
||||
}
|
||||
return nil
|
||||
|
@ -87,6 +88,7 @@ type ContainerWsAttachWithLogsConfig struct {
|
|||
InStream io.ReadCloser
|
||||
OutStream, ErrStream io.Writer
|
||||
Logs, Stream bool
|
||||
DetachKeys []byte
|
||||
}
|
||||
|
||||
// ContainerWsAttachWithLogs websocket connection
|
||||
|
@ -95,10 +97,10 @@ func (daemon *Daemon) ContainerWsAttachWithLogs(prefixOrName string, c *Containe
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return daemon.attachWithLogs(container, c.InStream, c.OutStream, c.ErrStream, c.Logs, c.Stream)
|
||||
return daemon.attachWithLogs(container, c.InStream, c.OutStream, c.ErrStream, c.Logs, c.Stream, c.DetachKeys)
|
||||
}
|
||||
|
||||
func (daemon *Daemon) attachWithLogs(container *container.Container, stdin io.ReadCloser, stdout, stderr io.Writer, logs, stream bool) error {
|
||||
func (daemon *Daemon) attachWithLogs(container *container.Container, stdin io.ReadCloser, stdout, stderr io.Writer, logs, stream bool, keys []byte) error {
|
||||
if logs {
|
||||
logDriver, err := daemon.getLogger(container)
|
||||
if err != nil {
|
||||
|
@ -144,7 +146,7 @@ func (daemon *Daemon) attachWithLogs(container *container.Container, stdin io.Re
|
|||
}()
|
||||
stdinPipe = r
|
||||
}
|
||||
<-container.Attach(stdinPipe, stdout, stderr)
|
||||
<-container.Attach(stdinPipe, stdout, stderr, keys)
|
||||
// If we are in stdinonce mode, wait for the process to end
|
||||
// otherwise, simply return
|
||||
if container.Config.StdinOnce && !container.Config.Tty {
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/pkg/pools"
|
||||
"github.com/docker/docker/pkg/promise"
|
||||
"github.com/docker/docker/pkg/term"
|
||||
)
|
||||
|
||||
func (d *Daemon) registerExecCommand(container *container.Container, config *exec.Config) {
|
||||
|
@ -88,6 +89,14 @@ func (d *Daemon) ContainerExecCreate(config *types.ExecConfig) (string, error) {
|
|||
cmd := strslice.New(config.Cmd...)
|
||||
entrypoint, args := d.getEntrypointAndArgs(strslice.New(), cmd)
|
||||
|
||||
keys := []byte{}
|
||||
if config.DetachKeys != "" {
|
||||
keys, err = term.ToBytes(config.DetachKeys)
|
||||
if err != nil {
|
||||
logrus.Warnf("Wrong escape keys provided (%s, error: %s) using default : ctrl-p ctrl-q", config.DetachKeys, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
processConfig := &execdriver.ProcessConfig{
|
||||
CommonProcessConfig: execdriver.CommonProcessConfig{
|
||||
Tty: config.Tty,
|
||||
|
@ -103,6 +112,7 @@ func (d *Daemon) ContainerExecCreate(config *types.ExecConfig) (string, error) {
|
|||
execConfig.OpenStderr = config.AttachStderr
|
||||
execConfig.ProcessConfig = processConfig
|
||||
execConfig.ContainerID = container.ID
|
||||
execConfig.DetachKeys = keys
|
||||
|
||||
d.registerExecCommand(container, execConfig)
|
||||
|
||||
|
@ -158,7 +168,8 @@ func (d *Daemon) ContainerExecStart(name string, stdin io.ReadCloser, stdout io.
|
|||
ec.NewNopInputPipe()
|
||||
}
|
||||
|
||||
attachErr := container.AttachStreams(ec.StreamConfig, ec.OpenStdin, true, ec.ProcessConfig.Tty, cStdin, cStdout, cStderr)
|
||||
attachErr := container.AttachStreams(ec.StreamConfig, ec.OpenStdin, true, ec.ProcessConfig.Tty, cStdin, cStdout, cStderr, ec.DetachKeys)
|
||||
|
||||
execErr := make(chan error)
|
||||
|
||||
// Note, the ExecConfig data will be removed when the container
|
||||
|
|
|
@ -25,6 +25,7 @@ type Config struct {
|
|||
OpenStdout bool
|
||||
CanRemove bool
|
||||
ContainerID string
|
||||
DetachKeys []byte
|
||||
|
||||
// waitStart will be closed immediately after the exec is really started.
|
||||
waitStart chan struct{}
|
||||
|
|
|
@ -862,10 +862,9 @@ This endpoint returns a live stream of a container's resource usage statistics.
|
|||
"total_usage" : 36488948,
|
||||
"usage_in_kernelmode" : 20000000
|
||||
},
|
||||
"system_cpu_usage" : 20091722000000000,
|
||||
"system_cpu_usage" : 20091722000000000,
|
||||
"throttling_data" : {}
|
||||
}
|
||||
}
|
||||
} }
|
||||
|
||||
Query Parameters:
|
||||
|
||||
|
@ -922,6 +921,12 @@ Start the container `id`
|
|||
|
||||
HTTP/1.1 204 No Content
|
||||
|
||||
Query Parameters:
|
||||
|
||||
- **detacheys** – Override the key sequence for detaching a
|
||||
container. Format is a single character `[a-Z]` or `ctrl-<value>`
|
||||
where `<value>` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`.
|
||||
|
||||
Status Codes:
|
||||
|
||||
- **204** – no error
|
||||
|
@ -1133,6 +1138,9 @@ Attach to the container `id`
|
|||
|
||||
Query Parameters:
|
||||
|
||||
- **detacheys** – Override the key sequence for detaching a
|
||||
container. Format is a single character `[a-Z]` or `ctrl-<value>`
|
||||
where `<value>` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`.
|
||||
- **logs** – 1/True/true or 0/False/false, return logs. Default `false`.
|
||||
- **stream** – 1/True/true or 0/False/false, return stream.
|
||||
Default `false`.
|
||||
|
@ -1213,6 +1221,9 @@ Implements websocket protocol handshake according to [RFC 6455](http://tools.iet
|
|||
|
||||
Query Parameters:
|
||||
|
||||
- **detacheys** – Override the key sequence for detaching a
|
||||
container. Format is a single character `[a-Z]` or `ctrl-<value>`
|
||||
where `<value>` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`.
|
||||
- **logs** – 1/True/true or 0/False/false, return logs. Default `false`.
|
||||
- **stream** – 1/True/true or 0/False/false, return stream.
|
||||
Default `false`.
|
||||
|
@ -2420,6 +2431,7 @@ Sets up an exec instance in a running container `id`
|
|||
"AttachStdin": false,
|
||||
"AttachStdout": true,
|
||||
"AttachStderr": true,
|
||||
"DetachKeys": "ctrl-p,ctrl-q",
|
||||
"Tty": false,
|
||||
"Cmd": [
|
||||
"date"
|
||||
|
@ -2441,6 +2453,9 @@ Json Parameters:
|
|||
- **AttachStdin** - Boolean value, attaches to `stdin` of the `exec` command.
|
||||
- **AttachStdout** - Boolean value, attaches to `stdout` of the `exec` command.
|
||||
- **AttachStderr** - Boolean value, attaches to `stderr` of the `exec` command.
|
||||
- **Detacheys** – Override the key sequence for detaching a
|
||||
container. Format is a single character `[a-Z]` or `ctrl-<value>`
|
||||
where `<value>` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`.
|
||||
- **Tty** - Boolean value to allocate a pseudo-TTY.
|
||||
- **Cmd** - Command to run specified as a string or an array of strings.
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ parent = "smn_cli"
|
|||
|
||||
Attach to a running container
|
||||
|
||||
--detach-keys="<sequence>" Set up escape key sequence
|
||||
--help Print usage
|
||||
--no-stdin Do not attach STDIN
|
||||
--sig-proxy=true Proxy all received signals to the process
|
||||
|
@ -24,11 +25,10 @@ interactively. You can attach to the same contained process multiple times
|
|||
simultaneously, screen sharing style, or quickly view the progress of your
|
||||
detached process.
|
||||
|
||||
You can detach from the container and leave it running with `CTRL-p CTRL-q`
|
||||
(for a quiet exit) or with `CTRL-c` if `--sig-proxy` is false.
|
||||
|
||||
If `--sig-proxy` is true (the default),`CTRL-c` sends a `SIGINT` to the
|
||||
container.
|
||||
To stop a container, use `CTRL-c`. This key sequence sends `SIGKILL` to the
|
||||
container. If `--sig-proxy` is true (the default),`CTRL-c` sends a `SIGINT` to
|
||||
the container. You can detach from a container and leave it running using the
|
||||
using `CTRL-p CTRL-q` key sequence.
|
||||
|
||||
> **Note:**
|
||||
> A process running as PID 1 inside a container is treated specially by
|
||||
|
@ -39,6 +39,31 @@ container.
|
|||
It is forbidden to redirect the standard input of a `docker attach` command
|
||||
while attaching to a tty-enabled container (i.e.: launched with `-t`).
|
||||
|
||||
|
||||
## Override the detach sequence
|
||||
|
||||
If you want, you can configure a override the Docker key sequence for detach.
|
||||
This is is useful if the Docker default sequence conflicts with key squence you
|
||||
use for other applications. There are two ways to defines a your own detach key
|
||||
sequence, as a per-container override or as a configuration property on your
|
||||
entire configuration.
|
||||
|
||||
To override the sequence for an individual container, use the
|
||||
`--detach-keys="<sequence>"` flag with the `docker attach` command. The format of
|
||||
the `<sequence>` is either a letter [a-Z], or the `ctrl-` combined with any of
|
||||
the following:
|
||||
|
||||
* `a-z` (a single lowercase alpha character )
|
||||
* `@` (ampersand)
|
||||
* `[` (left bracket)
|
||||
* `\\` (two backward slashes)
|
||||
* `_` (underscore)
|
||||
* `^` (caret)
|
||||
|
||||
These `a`, `ctrl-a`, `X`, or `ctrl-\\` values are all examples of valid key
|
||||
sequences. To configure a different configuration default key sequence for all
|
||||
containers, see [**Configuration file** section](cli.md#configuration-files).
|
||||
|
||||
#### Examples
|
||||
|
||||
$ docker run -d --name topdemo ubuntu /usr/bin/top -b
|
||||
|
|
|
@ -101,7 +101,26 @@ The property `psFormat` specifies the default format for `docker ps` output.
|
|||
When the `--format` flag is not provided with the `docker ps` command,
|
||||
Docker's client uses this property. If this property is not set, the client
|
||||
falls back to the default table format. For a list of supported formatting
|
||||
directives, see the [**Formatting** section in the `docker ps` documentation](ps.md)
|
||||
directives, see the
|
||||
[**Formatting** section in the `docker ps` documentation](ps.md)
|
||||
|
||||
Once attached to a container, users detach from it and leave it running using
|
||||
the using `CTRL-p CTRL-q` key sequence. This detach key sequence is customizable
|
||||
using the `detachKeys` property. Specify a `<sequence>` value for the
|
||||
property. The format of the `<sequence>` is either a letter [a-Z], or the `ctrl-`
|
||||
combined with any of the following:
|
||||
|
||||
* `a-z` (a single lowercase alpha character )
|
||||
* `@` (ampersand)
|
||||
* `[` (left bracket)
|
||||
* `\\` (two backward slashes)
|
||||
* `_` (underscore)
|
||||
* `^` (caret)
|
||||
|
||||
Your customization applies to all containers started in with your Docker client.
|
||||
Users can override your custom or the default key sequence on a per-container
|
||||
basis. To do this, the user specifies the `--detach-keys` flag with the `docker
|
||||
attach`, `docker exec`, `docker run` or `docker start` command.
|
||||
|
||||
The property `imagesFormat` specifies the default format for `docker images` output.
|
||||
When the `--format` flag is not provided with the `docker images` command,
|
||||
|
@ -115,8 +134,9 @@ Following is a sample `config.json` file:
|
|||
"HttpHeaders": {
|
||||
"MyHeader": "MyValue"
|
||||
},
|
||||
"psFormat": "table {{.ID}}\\t{{.Image}}\\t{{.Command}}\\t{{.Labels}}"
|
||||
"imagesFormat": "table {{.ID}}\\t{{.Repository}}\\t{{.Tag}}\\t{{.CreatedAt}}"
|
||||
"psFormat": "table {{.ID}}\\t{{.Image}}\\t{{.Command}}\\t{{.Labels}}",
|
||||
"imagesFormat": "table {{.ID}}\\t{{.Repository}}\\t{{.Tag}}\\t{{.CreatedAt}}",
|
||||
"detachKeys": "ctrl-e,e"
|
||||
}
|
||||
|
||||
### Notary
|
||||
|
|
|
@ -15,6 +15,7 @@ parent = "smn_cli"
|
|||
Run a command in a running container
|
||||
|
||||
-d, --detach Detached mode: run command in the background
|
||||
--detach-keys Specify the escape key sequence used to detach a container
|
||||
--help Print usage
|
||||
-i, --interactive Keep STDIN open even if not attached
|
||||
--privileged Give extended Linux capabilities to the command
|
||||
|
|
|
@ -28,6 +28,7 @@ parent = "smn_cli"
|
|||
--cpuset-cpus="" CPUs in which to allow execution (0-3, 0,1)
|
||||
--cpuset-mems="" Memory nodes (MEMs) in which to allow execution (0-3, 0,1)
|
||||
-d, --detach Run container in background and print container ID
|
||||
--detach-keys Specify the escape key sequence used to detach a container
|
||||
--device=[] Add a host device to the container
|
||||
--device-read-bps=[] Limit read rate (bytes per second) from a device (e.g., --device-read-bps=/dev/sda:1mb)
|
||||
--device-read-iops=[] Limit read rate (IO per second) from a device (e.g., --device-read-iops=/dev/sda:1000)
|
||||
|
|
|
@ -15,5 +15,6 @@ parent = "smn_cli"
|
|||
Start one or more containers
|
||||
|
||||
-a, --attach Attach STDOUT/STDERR and forward signals
|
||||
--detach-keys Specify the escape key sequence used to detach a container
|
||||
--help Print usage
|
||||
-i, --interactive Attach container's STDIN
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/pkg/homedir"
|
||||
"github.com/docker/docker/pkg/integration/checker"
|
||||
"github.com/docker/docker/pkg/mount"
|
||||
"github.com/docker/docker/pkg/parsers"
|
||||
|
@ -87,10 +88,13 @@ func (s *DockerSuite) TestRunDeviceDirectory(c *check.C) {
|
|||
c.Assert(strings.Trim(out, "\r\n"), checker.Contains, "seq", check.Commentf("expected output /dev/othersnd/seq"))
|
||||
}
|
||||
|
||||
// TestRunDetach checks attaching and detaching with the escape sequence.
|
||||
// TestRunDetach checks attaching and detaching with the default escape sequence.
|
||||
func (s *DockerSuite) TestRunAttachDetach(c *check.C) {
|
||||
name := "attach-detach"
|
||||
cmd := exec.Command(dockerBinary, "run", "--name", name, "-it", "busybox", "cat")
|
||||
|
||||
dockerCmd(c, "run", "--name", name, "-itd", "busybox", "cat")
|
||||
|
||||
cmd := exec.Command(dockerBinary, "attach", name)
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
c.Assert(err, checker.IsNil)
|
||||
cpty, tty, err := pty.Open()
|
||||
|
@ -120,21 +124,248 @@ func (s *DockerSuite) TestRunAttachDetach(c *check.C) {
|
|||
ch <- struct{}{}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ch:
|
||||
case <-time.After(10 * time.Second):
|
||||
c.Fatal("timed out waiting for container to exit")
|
||||
}
|
||||
|
||||
running, err := inspectField(name, "State.Running")
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(running, checker.Equals, "true", check.Commentf("expected container to still be running"))
|
||||
}
|
||||
|
||||
// TestRunDetach checks attaching and detaching with the escape sequence specified via flags.
|
||||
func (s *DockerSuite) TestRunAttachDetachFromFlag(c *check.C) {
|
||||
name := "attach-detach"
|
||||
keyCtrlA := []byte{1}
|
||||
keyA := []byte{97}
|
||||
|
||||
dockerCmd(c, "run", "--name", name, "-itd", "busybox", "cat")
|
||||
|
||||
cmd := exec.Command(dockerBinary, "attach", "--detach-keys='ctrl-a,a'", name)
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
cpty, tty, err := pty.Open()
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
defer cpty.Close()
|
||||
cmd.Stdin = tty
|
||||
if err := cmd.Start(); err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
c.Assert(waitRun(name), check.IsNil)
|
||||
|
||||
if _, err := cpty.Write([]byte("hello\n")); err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
out, err := bufio.NewReader(stdout).ReadString('\n')
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
if strings.TrimSpace(out) != "hello" {
|
||||
c.Fatalf("expected 'hello', got %q", out)
|
||||
}
|
||||
|
||||
// escape sequence
|
||||
if _, err := cpty.Write(keyCtrlA); err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
if _, err := cpty.Write(keyA); err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
ch := make(chan struct{})
|
||||
go func() {
|
||||
exec.Command(dockerBinary, "kill", name).Run()
|
||||
cmd.Wait()
|
||||
ch <- struct{}{}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ch:
|
||||
case <-time.After(10 * time.Millisecond):
|
||||
case <-time.After(10 * time.Second):
|
||||
c.Fatal("timed out waiting for container to exit")
|
||||
}
|
||||
|
||||
running, err := inspectField(name, "State.Running")
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(running, checker.Equals, "true", check.Commentf("expected container to still be running"))
|
||||
}
|
||||
|
||||
// TestRunDetach checks attaching and detaching with the escape sequence specified via config file.
|
||||
func (s *DockerSuite) TestRunAttachDetachFromConfig(c *check.C) {
|
||||
keyCtrlA := []byte{1}
|
||||
keyA := []byte{97}
|
||||
|
||||
// Setup config
|
||||
homeKey := homedir.Key()
|
||||
homeVal := homedir.Get()
|
||||
tmpDir, err := ioutil.TempDir("", "fake-home")
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
dotDocker := filepath.Join(tmpDir, ".docker")
|
||||
os.Mkdir(dotDocker, 0600)
|
||||
tmpCfg := filepath.Join(dotDocker, "config.json")
|
||||
|
||||
defer func() { os.Setenv(homeKey, homeVal) }()
|
||||
os.Setenv(homeKey, tmpDir)
|
||||
|
||||
data := `{
|
||||
"detachKeys": "ctrl-a,a"
|
||||
}`
|
||||
|
||||
err = ioutil.WriteFile(tmpCfg, []byte(data), 0600)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// Then do the work
|
||||
name := "attach-detach"
|
||||
dockerCmd(c, "run", "--name", name, "-itd", "busybox", "cat")
|
||||
|
||||
cmd := exec.Command(dockerBinary, "attach", name)
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
cpty, tty, err := pty.Open()
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
defer cpty.Close()
|
||||
cmd.Stdin = tty
|
||||
if err := cmd.Start(); err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
c.Assert(waitRun(name), check.IsNil)
|
||||
|
||||
if _, err := cpty.Write([]byte("hello\n")); err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
out, err := bufio.NewReader(stdout).ReadString('\n')
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
if strings.TrimSpace(out) != "hello" {
|
||||
c.Fatalf("expected 'hello', got %q", out)
|
||||
}
|
||||
|
||||
// escape sequence
|
||||
if _, err := cpty.Write(keyCtrlA); err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
if _, err := cpty.Write(keyA); err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
ch := make(chan struct{})
|
||||
go func() {
|
||||
cmd.Wait()
|
||||
ch <- struct{}{}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ch:
|
||||
case <-time.After(10 * time.Second):
|
||||
c.Fatal("timed out waiting for container to exit")
|
||||
}
|
||||
|
||||
running, err := inspectField(name, "State.Running")
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(running, checker.Equals, "true", check.Commentf("expected container to still be running"))
|
||||
}
|
||||
|
||||
// TestRunDetach checks attaching and detaching with the detach flags, making sure it overrides config file
|
||||
func (s *DockerSuite) TestRunAttachDetachKeysOverrideConfig(c *check.C) {
|
||||
keyCtrlA := []byte{1}
|
||||
keyA := []byte{97}
|
||||
|
||||
// Setup config
|
||||
homeKey := homedir.Key()
|
||||
homeVal := homedir.Get()
|
||||
tmpDir, err := ioutil.TempDir("", "fake-home")
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
dotDocker := filepath.Join(tmpDir, ".docker")
|
||||
os.Mkdir(dotDocker, 0600)
|
||||
tmpCfg := filepath.Join(dotDocker, "config.json")
|
||||
|
||||
defer func() { os.Setenv(homeKey, homeVal) }()
|
||||
os.Setenv(homeKey, tmpDir)
|
||||
|
||||
data := `{
|
||||
"detachKeys": "ctrl-e,e"
|
||||
}`
|
||||
|
||||
err = ioutil.WriteFile(tmpCfg, []byte(data), 0600)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// Then do the work
|
||||
name := "attach-detach"
|
||||
dockerCmd(c, "run", "--name", name, "-itd", "busybox", "cat")
|
||||
|
||||
cmd := exec.Command(dockerBinary, "attach", "--detach-keys='ctrl-a,a'", name)
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
cpty, tty, err := pty.Open()
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
defer cpty.Close()
|
||||
cmd.Stdin = tty
|
||||
if err := cmd.Start(); err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
c.Assert(waitRun(name), check.IsNil)
|
||||
|
||||
if _, err := cpty.Write([]byte("hello\n")); err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
out, err := bufio.NewReader(stdout).ReadString('\n')
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
if strings.TrimSpace(out) != "hello" {
|
||||
c.Fatalf("expected 'hello', got %q", out)
|
||||
}
|
||||
|
||||
// escape sequence
|
||||
if _, err := cpty.Write(keyCtrlA); err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
if _, err := cpty.Write(keyA); err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
ch := make(chan struct{})
|
||||
go func() {
|
||||
cmd.Wait()
|
||||
ch <- struct{}{}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ch:
|
||||
case <-time.After(10 * time.Second):
|
||||
c.Fatal("timed out waiting for container to exit")
|
||||
}
|
||||
|
||||
running, err := inspectField(name, "State.Running")
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(running, checker.Equals, "true", check.Commentf("expected container to still be running"))
|
||||
}
|
||||
|
||||
// "test" should be printed
|
||||
func (s *DockerSuite) TestRunWithCPUQuota(c *check.C) {
|
||||
testRequires(c, cpuCfsQuota)
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ docker-attach - Attach to a running container
|
|||
|
||||
# SYNOPSIS
|
||||
**docker attach**
|
||||
[**--detach-keys**[=*[]*]]
|
||||
[**--help**]
|
||||
[**--no-stdin**]
|
||||
[**--sig-proxy**[=*true*]]
|
||||
|
@ -18,15 +19,19 @@ interactively. You can attach to the same contained process multiple times
|
|||
simultaneously, screen sharing style, or quickly view the progress of your
|
||||
detached process.
|
||||
|
||||
You can detach from the container (and leave it running) with `CTRL-p CTRL-q`
|
||||
(for a quiet exit) or `CTRL-c` which will send a `SIGKILL` to the container.
|
||||
When you are attached to a container, and exit its main process, the process's
|
||||
exit code will be returned to the client.
|
||||
To stop a container, use `CTRL-c`. This key sequence sends `SIGKILL` to the
|
||||
container. You can detach from the container (and leave it running) using a
|
||||
configurable key sequence. The default sequence is `CTRL-p CTRL-q`. You
|
||||
configure the key sequence using the **--detach-keys** option or a configuration
|
||||
file. See **config-json(5)** for documentation on using a configuration file.
|
||||
|
||||
It is forbidden to redirect the standard input of a `docker attach` command while
|
||||
attaching to a tty-enabled container (i.e.: launched with `-t`).
|
||||
|
||||
# OPTIONS
|
||||
**--detach-keys**=""
|
||||
Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-<value>` where `<value>` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`.
|
||||
|
||||
**--help**
|
||||
Print usage statement
|
||||
|
||||
|
@ -36,6 +41,30 @@ attaching to a tty-enabled container (i.e.: launched with `-t`).
|
|||
**--sig-proxy**=*true*|*false*
|
||||
Proxy all received signals to the process (non-TTY mode only). SIGCHLD, SIGKILL, and SIGSTOP are not proxied. The default is *true*.
|
||||
|
||||
# Override the detach sequence
|
||||
|
||||
If you want, you can configure a override the Docker key sequence for detach.
|
||||
This is is useful if the Docker default sequence conflicts with key squence you
|
||||
use for other applications. There are two ways to defines a your own detach key
|
||||
sequence, as a per-container override or as a configuration property on your
|
||||
entire configuration.
|
||||
|
||||
To override the sequence for an individual container, use the
|
||||
`--detach-keys="<sequence>"` flag with the `docker attach` command. The format of
|
||||
the `<sequence>` is either a letter [a-Z], or the `ctrl-` combined with any of
|
||||
the following:
|
||||
|
||||
* `a-z` (a single lowercase alpha character )
|
||||
* `@` (ampersand)
|
||||
* `[` (left bracket)
|
||||
* `\\` (two backward slashes)
|
||||
* `_` (underscore)
|
||||
* `^` (caret)
|
||||
|
||||
These `a`, `ctrl-a`, `X`, or `ctrl-\\` values are all examples of valid key
|
||||
sequences. To configure a different configuration default key sequence for all
|
||||
containers, see **docker(1)**.
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
## Attaching to a container
|
||||
|
|
|
@ -7,6 +7,7 @@ docker-exec - Run a command in a running container
|
|||
# SYNOPSIS
|
||||
**docker exec**
|
||||
[**-d**|**--detach**]
|
||||
[**--detach-keys**[=*[]*]]
|
||||
[**--help**]
|
||||
[**-i**|**--interactive**]
|
||||
[**--privileged**]
|
||||
|
@ -26,7 +27,10 @@ container is unpaused, and then run
|
|||
|
||||
# OPTIONS
|
||||
**-d**, **--detach**=*true*|*false*
|
||||
Detached mode: run command in the background. The default is *false*.
|
||||
Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-<value>` where `<value>` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`.
|
||||
|
||||
**--detach-keys**=""
|
||||
Define the key sequence which detaches the container.
|
||||
|
||||
**--help**
|
||||
Print usage statement
|
||||
|
|
|
@ -20,6 +20,7 @@ docker-run - Run a command in a new container
|
|||
[**--cpuset-cpus**[=*CPUSET-CPUS*]]
|
||||
[**--cpuset-mems**[=*CPUSET-MEMS*]]
|
||||
[**-d**|**--detach**]
|
||||
[**--detach-keys**[=*[]*]]
|
||||
[**--device**[=*[]*]]
|
||||
[**--device-read-bps**[=*[]*]]
|
||||
[**--device-read-iops**[=*[]*]]
|
||||
|
@ -190,8 +191,13 @@ the other shell to view a list of the running containers. You can reattach to a
|
|||
detached container with **docker attach**. If you choose to run a container in
|
||||
the detached mode, then you cannot use the **-rm** option.
|
||||
|
||||
When attached in the tty mode, you can detach from a running container without
|
||||
stopping the process by pressing the keys CTRL-P CTRL-Q.
|
||||
When attached in the tty mode, you can detach from the container (and leave it
|
||||
running) using a configurable key sequence. The default sequence is `CTRL-p CTRL-q`.
|
||||
You configure the key sequence using the **--detach-keys** option or a configuration file.
|
||||
See **config-json(5)** for documentation on using a configuration file.
|
||||
|
||||
**--detach-keys**=""
|
||||
Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-<value>` where `<value>` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`.
|
||||
|
||||
**--device**=[]
|
||||
Add a host device to the container (e.g. --device=/dev/sdc:/dev/xvdc:rwm)
|
||||
|
|
|
@ -7,6 +7,7 @@ docker-start - Start one or more containers
|
|||
# SYNOPSIS
|
||||
**docker start**
|
||||
[**-a**|**--attach**]
|
||||
[**--detach-keys**[=*[]*]]
|
||||
[**--help**]
|
||||
[**-i**|**--interactive**]
|
||||
CONTAINER [CONTAINER...]
|
||||
|
@ -17,7 +18,11 @@ Start one or more containers.
|
|||
|
||||
# OPTIONS
|
||||
**-a**, **--attach**=*true*|*false*
|
||||
Attach container's STDOUT and STDERR and forward all signals to the process. The default is *false*.
|
||||
Attach container's STDOUT and STDERR and forward all signals to the
|
||||
process. The default is *false*.
|
||||
|
||||
**--detach-keys**=""
|
||||
Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-<value>` where `<value>` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`.
|
||||
|
||||
**--help**
|
||||
Print usage statement
|
||||
|
|
|
@ -223,6 +223,7 @@ inside it)
|
|||
Block until a container stops, then print its exit code
|
||||
See **docker-wait(1)** for full documentation on the **wait** command.
|
||||
|
||||
|
||||
# EXEC DRIVER OPTIONS
|
||||
|
||||
Use the **--exec-opt** flags to specify options to the execution driver. The only
|
||||
|
|
66
pkg/term/ascii.go
Normal file
66
pkg/term/ascii.go
Normal file
|
@ -0,0 +1,66 @@
|
|||
package term
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ASCII list the possible supported ASCII key sequence
|
||||
var ASCII = []string{
|
||||
"ctrl-@",
|
||||
"ctrl-a",
|
||||
"ctrl-b",
|
||||
"ctrl-c",
|
||||
"ctrl-d",
|
||||
"ctrl-e",
|
||||
"ctrl-f",
|
||||
"ctrl-g",
|
||||
"ctrl-h",
|
||||
"ctrl-i",
|
||||
"ctrl-j",
|
||||
"ctrl-k",
|
||||
"ctrl-l",
|
||||
"ctrl-m",
|
||||
"ctrl-n",
|
||||
"ctrl-o",
|
||||
"ctrl-p",
|
||||
"ctrl-q",
|
||||
"ctrl-r",
|
||||
"ctrl-s",
|
||||
"ctrl-t",
|
||||
"ctrl-u",
|
||||
"ctrl-v",
|
||||
"ctrl-w",
|
||||
"ctrl-x",
|
||||
"ctrl-y",
|
||||
"ctrl-z",
|
||||
"ctrl-[",
|
||||
"ctrl-\\",
|
||||
"ctrl-]",
|
||||
"ctrl-^",
|
||||
"ctrl-_",
|
||||
}
|
||||
|
||||
// ToBytes converts a string representing a suite of key-sequence to the corresponding ASCII code.
|
||||
func ToBytes(keys string) ([]byte, error) {
|
||||
codes := []byte{}
|
||||
next:
|
||||
for _, key := range strings.Split(keys, ",") {
|
||||
if len(key) != 1 {
|
||||
for code, ctrl := range ASCII {
|
||||
if ctrl == key {
|
||||
codes = append(codes, byte(code))
|
||||
continue next
|
||||
}
|
||||
}
|
||||
if key == "DEL" {
|
||||
codes = append(codes, 127)
|
||||
} else {
|
||||
return nil, fmt.Errorf("Unknown character: '%s'", key)
|
||||
}
|
||||
} else {
|
||||
codes = append(codes, byte(key[0]))
|
||||
}
|
||||
}
|
||||
return codes, nil
|
||||
}
|
43
pkg/term/ascii_test.go
Normal file
43
pkg/term/ascii_test.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
package term
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestToBytes(t *testing.T) {
|
||||
codes, err := ToBytes("ctrl-a,a")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(codes) != 2 {
|
||||
t.Fatalf("Expected 2 codes, got %d", len(codes))
|
||||
}
|
||||
if codes[0] != 1 || codes[1] != 97 {
|
||||
t.Fatalf("Expected '1' '97', got '%d' '%d'", codes[0], codes[1])
|
||||
}
|
||||
|
||||
codes, err = ToBytes("shift-z")
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error, got none")
|
||||
}
|
||||
|
||||
codes, err = ToBytes("ctrl-@,ctrl-[,~,ctrl-o")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(codes) != 4 {
|
||||
t.Fatalf("Expected 4 codes, got %d", len(codes))
|
||||
}
|
||||
if codes[0] != 0 || codes[1] != 27 || codes[2] != 126 || codes[3] != 15 {
|
||||
t.Fatalf("Expected '0' '27' '126', '15', got '%d' '%d' '%d' '%d'", codes[0], codes[1], codes[2], codes[3])
|
||||
}
|
||||
|
||||
codes, err = ToBytes("DEL,+")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(codes) != 2 {
|
||||
t.Fatalf("Expected 2 codes, got %d", len(codes))
|
||||
}
|
||||
if codes[0] != 127 || codes[1] != 43 {
|
||||
t.Fatalf("Expected '127 '43'', got '%d' '%d'", codes[0], codes[1])
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue