Created
June 14, 2019 15:59
-
-
Save rboyer/1ea07a64945693710bfe6a9c34d82de2 to your computer and use it in GitHub Desktop.
linux pipes fun
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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