Skip to content

Instantly share code, notes, and snippets.

@progrium
Last active May 6, 2017 19:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save progrium/a902a78f5b7d224ec58c2b8e8c55d94b to your computer and use it in GitHub Desktop.
Save progrium/a902a78f5b7d224ec58c2b8e8c55d94b to your computer and use it in GitHub Desktop.
cd /tmp
rm hello.cmd
rm http.cmd
ssh cmd.io :delete hello
ssh cmd.io :delete http
source ~/.profile.d/demo
clear
[wait 2]# Let's build two quick commands with Cmd.io!
# First,[wait] a simple command that says hello.
vim [wait 20]hello.cmd[wait 3][begin-pty]
[wait 2]i[wait]#!cmd[wait] alpine bash[wait]
#!/bin/bash
[wait 20]echo "Hello, ${1:-world}!"[wait 20]
[wait][esc]:wq[wait][end-pty]
cat hello.cmd | ssh cmd.io :create[wait 10] hello
[wait 10]ssh cmd.io hello[wait 2]
ssh cmd.io hello[wait 5] everybody[wait 2]
[wait 10]clear
[wait]# Next, an HTTP API client with jq built-in.[wait 10]
# We'll also use stateful environment vars.[wait 5]
vim http.cmd[wait 5][begin-pty]
[wait]i[wait]#!cmd[wait] alpine bash curl jq[wait 10]
#!/bin/bash
curl -Ls ${BASE}${1}[wait 30] | jq -C ${2:-.}
[wait 20][esc]:wq[wait][end-pty]
cat http.cmd | ssh cmd.io :create http
[wait 5]ssh cmd.io http [wait 2]slack.com/api/api.test
ssh cmd.io http slack.com/api/api.test [wait 2].ok
[wait 10]
ssh cmd.io :env http set BASE=slack.com/api/[wait 10]
ssh cmd.io http api.test[wait]
[wait]
[wait 5]ssh cmd.io :env http set BASE=api.github.com/[wait 10]
ssh cmd.io http orgs/gliderlabs
[wait 5]ssh cmd.io http orgs/gliderlabs [wait].name
ssh cmd.io http orgs/gliderlabs .location
[wait]
[wait]clear
[wait]# Now let's see how we can run commands over HTTP.
# This will require using access tokens.
[wait]
TOKEN=$(ssh cmd.io :tokens new)[wait 5]
echo $TOKEN
[wait 5]ssh cmd.io :access http grant $TOKEN[wait 20]
[wait 10]curl -u $TOKEN:[wait 5] "https://cmd.io/run/progrium/http?args=orgs/gliderlabs"
[wait 5]curl -u $TOKEN: "https://cmd.io/run/progrium/http?args=orgs/gliderlabs+.name"
[wait 20]
[wait 20]ssh cmd.io :tokens rm $TOKEN
exit
package main
import (
"bufio"
"bytes"
"io"
"log"
"math/rand"
"os"
"os/exec"
"regexp"
"strconv"
"strings"
"syscall"
"time"
"github.com/creack/termios/raw"
"github.com/kr/pty"
"github.com/pkg/term" // redundant but meh
)
func getch() []byte {
t, _ := term.Open("/dev/tty")
term.RawMode(t)
bytes := make([]byte, 3)
numRead, err := t.Read(bytes)
t.Restore()
t.Close()
if err != nil {
return nil
}
return bytes[0:numRead]
}
const (
ETX = 3
EOT = 4
BS = 8
LF = 10
CR = 13
ESC = 27
DEL = 127
CSI = 91
CUU = 65
CUD = 66
CUF = 67
CUB = 68
)
const Speed = 2.0
const Min = 45
const Max = 145
var HardChars = []byte("$%^&*()_+{}|<>:")
var MediumChars = []byte("~?!/")
var blockChildren = true
var ops []func() ([]byte, error)
var opIdx = -1
var opRe = regexp.MustCompile("\\[[^\\]]+\\]")
var r = rand.New(rand.NewSource(99))
func Delay(min, max int) {
ms := min + r.Intn(max-min)
mult := 1000.0 * (1.0 / Speed)
delay := ms * int(mult)
time.Sleep(time.Duration(delay) * time.Microsecond)
}
func WaitForChildren(parent int) bool {
cmd := exec.Command("pgrep", "-P", strconv.Itoa(parent))
out, err := cmd.CombinedOutput()
if err != nil {
return false
}
if len(out) == 0 {
return false
}
procs := strings.Split(strings.Trim(string(out), " \n"), "\n")
for _, procid := range procs {
pid, err := strconv.Atoi(procid)
if err != nil {
continue
}
p, err := os.FindProcess(pid)
if err == nil {
var e error
for e == nil {
time.Sleep(20 * time.Millisecond)
e = p.Signal(syscall.Signal(0))
}
}
}
return true
}
func SignalChildren(parent int, signal os.Signal) bool {
cmd := exec.Command("pgrep", "-P", strconv.Itoa(parent))
out, err := cmd.CombinedOutput()
if err != nil {
return false
}
if len(out) == 0 {
return false
}
procs := strings.Split(strings.Trim(string(out), " \n"), "\n")
for _, procid := range procs {
pid, err := strconv.Atoi(procid)
if err != nil {
continue
}
p, err := os.FindProcess(pid)
if err == nil {
var e error
for e == nil {
time.Sleep(20 * time.Millisecond)
e = p.Signal(signal)
}
}
}
return true
}
func main() {
if len(os.Args) < 2 {
log.Fatal("Script argument required")
}
file, err := os.Open(os.Args[1])
if err != nil {
panic(err)
}
shell := os.Getenv("SHELL")
if shell == "" {
shell = "/bin/sh"
}
c := exec.Command(shell)
f, err := pty.Start(c)
if err != nil {
panic(err)
}
tios, err := raw.MakeRaw(f.Fd())
if err != nil {
panic(err)
}
defer raw.TcSetAttr(f.Fd(), tios)
go func() {
out := io.MultiWriter(os.Stdout, f)
lines := bufio.NewScanner(file)
for lines.Scan() {
line := opRe.ReplaceAllFunc(lines.Bytes(), func(op []byte) []byte {
opStr := strings.Trim(string(op), "[]")
args := strings.Split(opStr, " ")
if args[0] == "wait" {
x := 1
if len(args) > 1 {
i, err := strconv.Atoi(args[1])
if err != nil {
panic(err)
}
x = i
}
ops = append(ops, func() ([]byte, error) {
Delay(400*x, 500*x)
return []byte{}, nil
})
opIdx++
return []byte{ESC, byte(opIdx)}
}
if args[0] == "pause" {
ops = append(ops, func() ([]byte, error) {
getch()
return []byte{}, nil
})
opIdx++
return []byte{ESC, byte(opIdx)}
}
if args[0] == "sigint" {
ops = append(ops, func() ([]byte, error) {
SignalChildren(c.Process.Pid, os.Interrupt)
return []byte{}, nil
})
opIdx++
return []byte{ESC, byte(opIdx)}
}
if args[0] == "esc" {
ops = append(ops, func() ([]byte, error) {
return []byte{ESC}, nil
})
opIdx++
return []byte{ESC, byte(opIdx)}
}
if args[0] == "begin-stdin" {
ops = append(ops, func() ([]byte, error) {
blockChildren = false
return []byte{}, nil
})
opIdx++
return []byte{ESC, byte(opIdx)}
}
if args[0] == "end-stdin" {
ops = append(ops, func() ([]byte, error) {
blockChildren = true
return []byte{}, nil
})
opIdx++
return []byte{ESC, byte(opIdx)}
}
if args[0] == "begin-pty" {
ops = append(ops, func() ([]byte, error) {
blockChildren = false
os.Stdout.Write([]byte{LF})
out = f
return []byte{}, nil
})
opIdx++
return []byte{ESC, byte(opIdx)}
}
if args[0] == "end-pty" {
ops = append(ops, func() ([]byte, error) {
blockChildren = true
out = io.MultiWriter(os.Stdout, f)
return []byte{}, nil
})
opIdx++
return []byte{ESC, byte(opIdx)}
}
return []byte{}
})
if len(line) > 2 {
Delay(Min*3, Max*6)
} else {
Delay(Min, Max)
}
var space, comment, op bool
for _, b := range line {
bs := []byte{b}
if !op && b == ESC {
op = true
continue
}
if op {
op = false
nb, err := ops[b]()
if err != nil {
panic(err)
}
bs = nb
}
if len(bs) == 0 {
continue
}
if space {
Delay(Min*2, Max*3)
} else {
if bytes.Contains(HardChars, bs) {
Delay(Min*8, Max*8)
} else if bytes.Contains(MediumChars, bs) {
Delay(Min*4, Max*4)
} else {
Delay(Min, Max)
}
}
space = bs[0] == ' '
out.Write(bs)
if bs[0] == '#' {
comment = true
}
}
if comment {
Delay(Min*10, Max*10)
comment = false
} else {
Delay(Min*5, Max*8)
}
out.Write([]byte("\n"))
if blockChildren && WaitForChildren(c.Process.Pid) {
Delay(Min*5, Max*5)
}
}
Delay(Min*20, Max*20)
f.Write([]byte{EOT})
}()
//go io.Copy(f, os.Stdin)
io.Copy(os.Stdout, f)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment