명명된 파이프(mkfifo): 소비자가 쓰기 측에 쓰지 않고 읽기 측(소비자)의 연결이 끊어졌음을 Macos에서 어떻게 감지합니까?

명명된 파이프(mkfifo): 소비자가 쓰기 측에 쓰지 않고 읽기 측(소비자)의 연결이 끊어졌음을 Macos에서 어떻게 감지합니까?

노트:이것은 대략생산자대신 소비자 연결 끊김 감지소비자생산자의 연결이 끊어진 것으로 감지되었습니다(EOF라고도 함). 생산자는 오랫동안 데이터를 쓰지 않을 수 있지만 소비자의 연결이 끊어졌음을 빨리 알리고 싶습니다.

일반적인 이벤트 순서:

  1. 소비자(Wireshark)는 명명된 파이프를 생성하고( 사용하여 mkfifo) 읽기 끝을 엽니다.
  2. 생산자(소위 Wireshark 외부 캡처 프로그램, extcap이라고도 함)가 시작되고 명명된 파이프의 이름/경로를 전달합니다.
  3. 생산자는 쓰기 측을 O_WRONLY로 열고 먼저 일부 데이터를 파이프에 씁니다.
  4. 크리켓
  5. 사용자가 Wireshark에서 중지 버튼을 누르면 Wireshark는 파이프의 읽기 끝을 닫습니다.
  6. ???생산자에게 보낼 데이터가 없는데도 소비자가 명명된 파이프에서 연결이 끊어졌는지 여부를 생산자에서 감지하려고 합니다???

리눅스

Linux의 생산자에서 fd를 select읽기 fd 세트에 있는 명명된 파이프의 쓰기 끝으로 사용하는 시스템 호출을 사용하면 소비자의 연결이 끊어지고 읽을 수 있게 될 때 fd의 쓰기 끝이 반환됩니다.

애플 시스템

그러나 macos에서는 명명된 파이프의 쓰기 측 fd생산자가 데이터를 쓸 때마다 읽을 수 있게 됩니다(sic!). 물론아니요소비자의 연결이 끊어진 후에 읽을 수 있게 됩니다.

편집: fd set 오류를 추가해도 상황은 바뀌지 않습니다. fd set 오류는 발생하지 않습니다. /편집하다

오랫동안 쓰기가 작성되지 않았지만 사용자가 Wireshark 로깅을 중지했기 때문에 SIGPIPE 없이 macos의 명명된 파이프에서 소비자 연결 끊김을 감지하는 방법에 대한 아이디어가 있습니까?

생산자 명명된 파이프에서 소비자 연결 끊김 감지

https://github.com/siemens/cshargextcap/blob/macos/pipe/checker_notwin.go

package pipe

import (
    "os"

    "golang.org/x/sys/unix"

    log "github.com/sirupsen/logrus"
)

// WaitTillBreak continuously checks a fifo/pipe to see when it breaks. When
// called, WaitTillBreak blocks until the fifo/pipe finally has broken.
//
// This implementation leverages [syscall.Select].
func WaitTillBreak(fifo *os.File) {
    log.Debug("constantly monitoring packet capture fifo status...")
    fds := unix.FdSet{}
    for {
        // Check the fifo becomming readable, which signals that it has been
        // closed. In this case, ex-termi-nate ;) Oh, and remember to correctly
        // initialize the fdset each time before calling select() ... well, just
        // because that's a good idea to do. :(
        fds.Set(int(fifo.Fd()))
        n, err := unix.Select(
            int(fifo.Fd())+1, // highest fd is our file descriptor.
            &fds, nil, nil,   // only watch readable.
            nil, // no timeout, ever.
        )
        if n != 0 || err != nil {
            // Either the pipe was broken by Wireshark, or we did break it on
            // purpose in the piping process. Anyway, we're done.
            log.Debug("capture fifo broken, stopped monitoring.")
            return
        }
    }
}

MacOS에서 잘못된 동작을 생성하는 단위 테스트

https://github.com/siemens/cshargextcap/blob/macos/pipe/checker_notwin_test.goWaitTillBreak--Named Pipe의 소비자 측을 실제로 닫을 때까지 반환되어서는 안 되는 어설션 실패입니다.

package pipe

import (
    "io"
    "os"
    "time"

    . "github.com/onsi/ginkgo/v2"
    . "github.com/onsi/gomega"
    . "github.com/thediveo/success"
    "golang.org/x/sys/unix"
)

var _ = Describe("pipes", func() {

    It("detects on the write end when a pipe breaks", func() {
        // As Wireshark uses a named pipe it passes an extcap its name (path)
        // and then expects the extcap to open this named pipe for writing
        // packet capture data into it. For this test we simulate Wireshark
        // closing its reading end and we must properly detect this situation on
        // our writing end of the pipe.
        By("creating a temporary named pipe/fifo and opening its ends")
        tmpfifodir := Successful(os.MkdirTemp("", "test-fifo-*"))
        defer os.RemoveAll(tmpfifodir)

        fifoname := tmpfifodir + "/fifo"
        unix.Mkfifo(fifoname, 0660)
        wch := make(chan *os.File)
        go func() {
            defer GinkgoRecover()
            wch <- Successful(os.OpenFile(fifoname, os.O_WRONLY, os.ModeNamedPipe))
        }()

        rch := make(chan *os.File)
        go func() {
            defer GinkgoRecover()
            rch <- Successful(os.OpenFile(fifoname, os.O_RDONLY, os.ModeNamedPipe))
        }()

        var r, w *os.File
        Eventually(rch).Should(Receive(&r))
        Eventually(wch).Should(Receive(&w))
        defer w.Close()

        go func() {
            defer GinkgoRecover()
            By("continously draining the read end of the pipe into /dev/null")
            null := Successful(os.OpenFile("/dev/null", os.O_WRONLY, 0))
            defer null.Close()
            io.Copy(null, r)
            By("pipe draining done")
        }()

        go func() {
            defer GinkgoRecover()
            time.Sleep(2 * time.Second)
            By("closing read end of pipe")
            Expect(r.Close()).To(Succeed())
        }()

        go func() {
            defer GinkgoRecover()
            time.Sleep(300 * time.Microsecond)
            By("writing some data into the pipe")
            w.WriteString("Wireshark rulez")
        }()

        By("waiting for pipe to break")
        start := time.Now()
        WaitTillBreak(w)
        Expect(time.Since(start).Milliseconds()).To(
            BeNumerically(">", 1900), "pipe wasn't broken yet")
    })

})

설문조사 기반 버전

이것은 macOS에서도 작동하지 않으며 아무것도 반환하지 않습니다 POLLERR. 하지만 Linux에서는 잘 작동합니다.

package pipe

import (
    "os"

    "golang.org/x/sys/unix"

    log "github.com/sirupsen/logrus"
)

// WaitTillBreak continuously checks a fifo/pipe to see when it breaks. When
// called, WaitTillBreak blocks until the fifo/pipe finally has broken.
//
// This implementation leverages [unix.Poll].
func WaitTillBreak(fifo *os.File) {
    log.Debug("constantly monitoring packet capture fifo status...")
    fds := []unix.PollFd{
        {
            Fd:     int32(fifo.Fd()),
            Events: 0,
        },
    }
    for {
        // Check the fifo becomming readable, which signals that it has been
        // closed. In this case, ex-termi-nate ;) Oh, and remember to correctly
        // initialize the fdset each time before calling select() ... well, just
        // because that's a good idea to do. :(
        n, err := unix.Poll(fds, 1000 /*ms*/)
        if err != nil {
            if err == unix.EINTR {
                continue
            }
            log.Debugf("capture fifo broken, reason: %s", err.Error())
            return
        }
        if n <= 0 {
            continue
        }
        log.Debugf("poll: %+v", fds)
        if fds[0].Revents&unix.POLLERR != 0 {
            // Either the pipe was broken by Wireshark, or we did break it on
            // purpose in the piping process. Anyway, we're done.
            log.Debug("capture fifo broken, stopped monitoring.")
            return
        }
    }
}

관련 정보