Subject: bug#31901: Incorrect make-network-process + nowait state handling for non-existing unix sockets in emacs-26.1

From: Mike Kazantsev <[email-masked]>
Date: Tue, 19 Jun 2018 10:43:51 +0500

Description:

Running (make-network-process ... :family 'local :nowait t) with
non-exising socket hangs forever in "open" state without ever calling
sentinel function.

100% reproducible on emacs-26.1 in current Archlinux OS.

==== How to reproduce:

Run following s-expressions from lisp-interaction-mode buffer in the
same sequence:

  (defun unix-socket-test-func (proc ev)
    (message "unix-socket-test: %s %s" proc ev))

  (defvar socket)
  (setq socket
    (make-network-process
      :name "unix-socket-test"
      :family 'local
      :service "/tmp/does-not-exist.sock"
      :nowait t
      :coding '(utf-8 . utf-8)
      :buffer (get-buffer-create "some-ipc-buffer")
      :noquery t
      :filter #'unix-socket-test-func
      :sentinel #'unix-socket-test-func))

  (process-live-p socket)
  (process-status socket)
  (delete-process socket)

Note - assuming that /tmp/does-not-exist.sock does not exist on the
filesystem.

==== Expected result:

One or more of:

- Message "unix-socket-test: failed" or some similar failure is
  reported by the sentinel function.

- Maybe "open" then "failed" states reported, in case socket() call is
  treated as 'open before connect() is issued.

- "socket" variable is set to nil, as it was with ":nowait t" before
  emacs-26, due to such simple and instant error.

- (process-live-p socket) reports nil when run few seconds after
  make-network-process call.

- (process-status socket) reports some kind of failed state, not 'open.

None of this happens.

==== Actual result:

- socket process is created and stuck forever in 'open state.

- strace on emacs process (monitoring syscalls) show this syscall
  sequence happening immediately when make-network-process call is made:

    [pid 31590] socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 21
    [pid 31590] connect(21, {sa_family=AF_UNIX, sun_path="/tmp/does-not-exist.sock"}, 110) = -1 ENOENT (No such file or directory)
    [pid 31590] close(21)                   = 0

- Function passed as :sentinel to make-network-process is never called
  with any state - neither 'open nor 'failed.

- (process-live-p socket) returns non-nil result for process associated
  with socket that is actually closed, and was closed immediately on
  connect, without any indication of that in elisp code.

- (process-status socket) returns 'open, even though it never sent
  'open status to sentinel function, nor is socket actually open at
  that point. It never succeeded at connecting to anything, in fact.

Same thing happens with socket that nothing is listening on, except
strace there gives different error on connect():

  [pid 31590] socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 21
  [pid 31590] connect(21, {sa_family=AF_UNIX, sun_path="/tmp/does-not-exist.sock"}, 110) = -1 ECONNREFUSED (Connection refused)
  [pid 31590] close(21)

This makes using unix sockets with emacs correctly and efficiently very
hard, if not impossible, as it there's no way to tell if it is stuck
connecting or failed except setting some kind of timeout on sentinel
response.

As far as I can tell, this is not documented behavior, definitely
unexpected, breaks all old code that used unix sockets, and probably
unintentional, i.e. a bug.

Apart from mentioned Archlinux setup, symptoms of the same issue were
also reported on EMMS (emacs multimedia system) mailing list with
emacs-26 recently on Ubuntu 18.10 pre-release version.

Have tried to find exising reports for such - rather basic, it seems -
problem, but haven't found any, so not entirely sure if maybe I'm doing
something wrong here.

Thanks for reading this far and any assistance.

-- 
Mike Kazantsev // fraggod.net