in audit.go [505:560]
func (c *AuditClient) closeAndUnsetPid() error {
msg := syscall.NetlinkMessage{
Header: syscall.NlMsghdr{
Type: AuditSet,
Flags: syscall.NLM_F_REQUEST,
},
Data: AuditStatus{
Mask: AuditStatusPID,
PID: 0,
}.toWireFormat(),
}
// If our request to unset the PID would block, then try to drain events from
// the netlink socket, resend, try again.
// In netlink, EAGAIN usually indicates our read buffer is full.
// The auditd code (which I'm using as a reference implementation) doesn't wait for a response when unsetting the audit pid.
// The retry count here is largely arbitrary, and provides a buffer for either transient errors (EINTR) or retries.
retries := 5
outer:
for i := 0; i < retries; i++ {
_, err := c.Netlink.SendNoWait(msg)
switch {
case err == nil:
return nil
case errors.Is(err, syscall.EINTR):
// got a transient interrupt, try again
continue
case errors.Is(err, syscall.EAGAIN):
// send would block, try to drain the receive socket. The recv count here is just so we have enough of a buffer to attempt a send again/
// The number is just here so we ideally have enough of a buffer to attempt the send again.
maxRecv := 10000
for i := 0; i < maxRecv; i++ {
_, err = c.Netlink.Receive(true, noParse)
switch {
case err == nil, errors.Is(err, syscall.EINTR), errors.Is(err, syscall.ENOBUFS):
// continue with receive, try to read more data
continue
case errors.Is(err, syscall.EAGAIN):
// receive would block, try to send again
continue outer
default:
// if receive returns an other error, just return that.
return err
}
}
default:
// if Send returns and other error, just return that
return err
}
}
// we may not want to treat this as a hard error?
// It's not a massive error if this fails, since the kernel will unset the PID if it can't communicate with the process,
// so this is largely for neatness.
return fmt.Errorf("could not unset pid from audit after retries")
}