Skip to content

Instantly share code, notes, and snippets.

@rboyer
Created June 14, 2019 15:59
Show Gist options
  • Save rboyer/1ea07a64945693710bfe6a9c34d82de2 to your computer and use it in GitHub Desktop.
Save rboyer/1ea07a64945693710bfe6a9c34d82de2 to your computer and use it in GitHub Desktop.
linux pipes fun
package main
import (
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"syscall"
"golang.org/x/sys/unix"
)
var (
flagMode = flag.String("mode", "parent", "")
flagWantSize = flag.Int("size", 1024, "")
)
func main() {
flag.Parse()
err := run(*flagMode)
if err != nil {
log.Fatal(err)
}
}
func run(mode string) error {
switch mode {
case "parent":
log.SetPrefix("PARENT: ")
return runParent()
case "child":
log.SetPrefix("CHILD: ")
return runChild()
default:
log.SetPrefix("UNKNOWN: ")
return fmt.Errorf("unexpected mode %q provided", mode)
}
}
func runParent() error {
log.Printf("starting")
if *flagWantSize < 1 {
return fmt.Errorf("size should be > 0: %d", *flagWantSize)
}
// Load a bunch of sample data.
data, err := loadData(*flagWantSize)
// data, err := ioutil.ReadFile("s.yml")
if err != nil {
return err
}
log.Printf("sample data is %d bytes", len(data))
const pipeFile = "my.pipe"
if err := os.Remove(pipeFile); err != nil {
if !os.IsNotExist(err) {
return err
}
}
if err = syscall.Mkfifo(pipeFile, 0600); err != nil {
return err
}
log.Printf("created pipe %q", pipeFile)
pr, pw, err := os.Pipe()
if err != nil {
return err
}
log.Printf("created anonymous pipe")
if _, err := pw.Write(data); err != nil {
return err
}
if err := pw.Close(); err != nil {
return err
}
log.Printf("wrote data to anonymous pipe")
// start detached child
var attr = os.ProcAttr{
Dir: ".",
Env: os.Environ(),
Files: []*os.File{
pr,
os.Stdout, // for debugging
os.Stderr, // for debugging
// nil,
// nil,
},
}
log.Printf("launching child proc")
proc, err := os.StartProcess(
os.Args[0],
[]string{os.Args[0], "-mode", "child", pipeFile},
// "/bin/cp",
// []string{"cp", "/dev/stdin", pipeFile},
&attr,
)
if err != nil {
return err
}
// TODO: what will wait on this process when it dies?
log.Printf("releasing child proc")
if err = proc.Release(); err != nil {
return err
}
/////////////////////////////////////
log.Printf("exec-ing fake envoy (wc)")
err = unix.Exec(
// "/bin/cat",
// []string{"cat", pipeFile},
"/usr/bin/wc",
[]string{"wc", "-c", pipeFile},
os.Environ(),
)
if err != nil {
return err
}
return nil
}
func loadData(size int) ([]byte, error) {
// Load sample data
data, err := ioutil.ReadFile("s.yml")
if err != nil {
return nil, err
}
actual := make([]byte, 0, size)
for len(actual) < size {
actual = append(actual, data...)
}
actual = actual[0:size]
return actual, nil
}
func runChild() error {
log.Printf("starting")
args := flag.Args()
if len(args) != 1 {
return fmt.Errorf("need one arg")
}
pipeFile := args[0]
log.Printf("reading data from STDIN")
log.Printf("writing data from %q", pipeFile)
log.Printf("opening output file")
f, err := os.OpenFile(pipeFile, os.O_WRONLY|os.O_APPEND, 0600)
if err != nil {
return err
}
n, err := io.Copy(f, os.Stdin)
if err != nil {
return err
}
if err = f.Close(); err != nil {
return err
}
log.Printf("copied %d bytes from stdin to output file", n)
return nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment