func getExitPipe(path string) (*os.File, error) { if err := unix.Mkfifo(path, 0755); err != nil && !os.IsExist(err) { return nil, err } // add NONBLOCK in case the other side has already closed or else // this function would never return return os.OpenFile(path, syscall.O_RDONLY|syscall.O_NONBLOCK, 0) }
// mktmpfifo creates a temporary FIFO and provides a cleanup function. func mktmpfifo(t *testing.T) (*os.File, func()) { err := unix.Mkfifo("fifo", 0666) if err != nil { t.Fatalf("mktmpfifo: failed to create FIFO: %v", err) } f, err := os.OpenFile("fifo", os.O_RDWR, 0666) if err != nil { os.Remove("fifo") t.Fatalf("mktmpfifo: failed to open FIFO: %v", err) } return f, func() { f.Close() os.Remove("fifo") } }
func getControlPipe(path string) (*os.File, error) { if err := unix.Mkfifo(path, 0755); err != nil && !os.IsExist(err) { return nil, err } return os.OpenFile(path, syscall.O_RDWR|syscall.O_NONBLOCK, 0) }
// OpenFifo opens a fifo. Returns io.ReadWriteCloser. // Context can be used to cancel this function until open(2) has not returned. // Accepted flags: // - syscall.O_CREAT - create new fifo if one doesn't exist // - syscall.O_RDONLY - open fifo only from reader side // - syscall.O_WRONLY - open fifo only from writer side // - syscall.O_RDWR - open fifo from both sides, never block on syscall level // - syscall.O_NONBLOCK - return io.ReadWriteCloser even if other side of the // fifo isn't open. read/write will be connected after the actual fifo is // open or after fifo is closed. func OpenFifo(ctx context.Context, fn string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) { if _, err := os.Stat(fn); err != nil { if os.IsNotExist(err) && flag&syscall.O_CREAT != 0 { if err := unix.Mkfifo(fn, uint32(perm&os.ModePerm)); err != nil && !os.IsExist(err) { return nil, errors.Wrapf(err, "error creating fifo %v", fn) } } else { return nil, err } } block := flag&syscall.O_NONBLOCK == 0 || flag&syscall.O_RDWR != 0 flag &= ^syscall.O_CREAT flag &= ^syscall.O_NONBLOCK h, err := getHandle(fn) if err != nil { return nil, err } f := &fifo{ handle: h, flag: flag, opened: make(chan struct{}), closed: make(chan struct{}), closing: make(chan struct{}), } wg := leakCheckWg if wg != nil { wg.Add(2) } go func() { if wg != nil { defer wg.Done() } select { case <-ctx.Done(): f.Close() case <-f.opened: case <-f.closed: } }() go func() { if wg != nil { defer wg.Done() } var file *os.File fn, err := h.Path() if err == nil { file, err = os.OpenFile(fn, flag, 0) } select { case <-f.closing: if err == nil { select { case <-ctx.Done(): err = ctx.Err() default: err = errors.Errorf("fifo %v was closed before opening", h.Name()) } if file != nil { file.Close() } } default: } if err != nil { f.closedOnce.Do(func() { f.err = err close(f.closed) }) return } f.file = file close(f.opened) }() if block { select { case <-f.opened: case <-f.closed: return nil, f.err } } return f, nil }