all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: Spencer Baugh <sbaugh@catern.com>
To: emacs-devel@gnu.org
Cc: Spencer Baugh <sbaugh@catern.com>
Subject: [PATCH 1/5] process: add features for direct use of FDs
Date: Mon,  6 Jun 2016 21:25:02 -0400	[thread overview]
Message-ID: <1465262706-5229-2-git-send-email-sbaugh@catern.com> (raw)
In-Reply-To: <1465262706-5229-1-git-send-email-sbaugh@catern.com>

- A new keyword argument for make-network-process, :ancillary. When
non-nil, Emacs will check for ancillary data when reading from the
network process. If any is found, it is passed as an additional argument
to the process filter function. At the moment, this only supports
reading passed file descriptors out of ancillary data.
This is inherited by the children of server processes.

- A new Lisp function make-fd-process, which accepts keyword arguments
exactly like make-pipe-process, but also accepts :infd and :outfd, which
take Lisp integers which should be file descriptors that can be read
from and written to, respectively.
---
 src/process.c | 215 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 src/process.h |   2 +
 2 files changed, 212 insertions(+), 5 deletions(-)

diff --git a/src/process.c b/src/process.c
index 9ca3e594..dc96166 100644
--- a/src/process.c
+++ b/src/process.c
@@ -21,6 +21,7 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include <config.h>
 
+#include <sys/socket.h>
 #include <stdio.h>
 #include <errno.h>
 #include <sys/types.h>		/* Some typedefs are used in sys/file.h.  */
@@ -2085,6 +2086,157 @@ create_pty (Lisp_Object process)
   p->pid = -2;
 }
 
+DEFUN ("make-fd-process", Fmake_fd_process, Smake_fd_process,
+       0, MANY, 0,
+       doc: /* Create a process from passed file descriptors.
+
+:infd FD
+
+:outfd FD
+
+usage:  (make-fd-process &rest ARGS)  */)
+  (ptrdiff_t nargs, Lisp_Object *args)
+{
+  Lisp_Object proc, contact;
+  struct Lisp_Process *p;
+  Lisp_Object name, buffer;
+  Lisp_Object tem;
+  int infd, outfd;
+  ptrdiff_t specpdl_count;
+  int inchannel, outchannel;
+
+  if (nargs == 0)
+    return Qnil;
+
+  contact = Flist (nargs, args);
+
+  infd = XINT (Fplist_get (contact, QCinfd));
+  outfd = XINT (Fplist_get (contact, QCoutfd));
+
+  name = Fplist_get (contact, QCname);
+  CHECK_STRING (name);
+  proc = make_process (name);
+  specpdl_count = SPECPDL_INDEX ();
+  record_unwind_protect (remove_process, proc);
+  p = XPROCESS (proc);
+
+  outchannel = outfd;
+  inchannel = infd;
+  p->open_fd[WRITE_TO_SUBPROCESS] = outfd;
+  p->open_fd[READ_FROM_SUBPROCESS] = infd;
+
+  fcntl (inchannel, F_SETFL, O_NONBLOCK);
+  fcntl (outchannel, F_SETFL, O_NONBLOCK);
+
+#ifdef WINDOWSNT
+  register_aux_fd (inchannel);
+#endif
+
+  /* Record this as an active process, with its channels.  */
+  chan_process[inchannel] = proc;
+  p->infd = inchannel;
+  p->outfd = outchannel;
+
+  if (inchannel > max_process_desc)
+    max_process_desc = inchannel;
+
+  buffer = Fplist_get (contact, QCbuffer);
+  if (NILP (buffer))
+    buffer = name;
+  buffer = Fget_buffer_create (buffer);
+  pset_buffer (p, buffer);
+
+  pset_childp (p, contact);
+  pset_plist (p, Fcopy_sequence (Fplist_get (contact, QCplist)));
+  pset_type (p, Qpipe);
+  pset_sentinel (p, Fplist_get (contact, QCsentinel));
+  pset_filter (p, Fplist_get (contact, QCfilter));
+  pset_log (p, Qnil);
+  if (tem = Fplist_get (contact, QCnoquery), !NILP (tem))
+    p->kill_without_query = 1;
+  if (tem = Fplist_get (contact, QCstop), !NILP (tem))
+    pset_command (p, Qt);
+  eassert (! p->pty_flag);
+
+  if (!EQ (p->command, Qt))
+    {
+      FD_SET (inchannel, &input_wait_mask);
+      FD_SET (inchannel, &non_keyboard_wait_mask);
+    }
+  p->adaptive_read_buffering
+    = (NILP (Vprocess_adaptive_read_buffering) ? 0
+       : EQ (Vprocess_adaptive_read_buffering, Qt) ? 1 : 2);
+
+  /* Make the process marker point into the process buffer (if any).  */
+  if (BUFFERP (buffer))
+    set_marker_both (p->mark, buffer,
+		     BUF_ZV (XBUFFER (buffer)),
+		     BUF_ZV_BYTE (XBUFFER (buffer)));
+
+  {
+    /* Setup coding systems for communicating with the network stream.  */
+
+    /* Qt denotes we have not yet called Ffind_operation_coding_system.  */
+    Lisp_Object coding_systems = Qt;
+    Lisp_Object val;
+
+    tem = Fplist_get (contact, QCcoding);
+    val = Qnil;
+    if (!NILP (tem))
+      {
+	val = tem;
+	if (CONSP (val))
+	  val = XCAR (val);
+      }
+    else if (!NILP (Vcoding_system_for_read))
+      val = Vcoding_system_for_read;
+    else if ((!NILP (buffer) && NILP (BVAR (XBUFFER (buffer), enable_multibyte_characters)))
+	     || (NILP (buffer) && NILP (BVAR (&buffer_defaults, enable_multibyte_characters))))
+      /* We dare not decode end-of-line format by setting VAL to
+	 Qraw_text, because the existing Emacs Lisp libraries
+	 assume that they receive bare code including a sequence of
+	 CR LF.  */
+      val = Qnil;
+    else
+      {
+	if (CONSP (coding_systems))
+	  val = XCAR (coding_systems);
+	else if (CONSP (Vdefault_process_coding_system))
+	  val = XCAR (Vdefault_process_coding_system);
+	else
+	  val = Qnil;
+      }
+    pset_decode_coding_system (p, val);
+
+    if (!NILP (tem))
+      {
+	val = tem;
+	if (CONSP (val))
+	  val = XCDR (val);
+      }
+    else if (!NILP (Vcoding_system_for_write))
+      val = Vcoding_system_for_write;
+    else if (NILP (BVAR (current_buffer, enable_multibyte_characters)))
+      val = Qnil;
+    else
+      {
+	if (CONSP (coding_systems))
+	  val = XCDR (coding_systems);
+	else if (CONSP (Vdefault_process_coding_system))
+	  val = XCDR (Vdefault_process_coding_system);
+	else
+	  val = Qnil;
+      }
+    pset_encode_coding_system (p, val);
+  }
+  /* This may signal an error.  */
+  setup_process_coding_systems (proc);
+
+  specpdl_ptr = specpdl + specpdl_count;
+
+  return proc;
+}
+
 DEFUN ("make-pipe-process", Fmake_pipe_process, Smake_pipe_process,
        0, MANY, 0,
        doc: /* Create and return a bidirectional pipe process.
@@ -3919,6 +4071,7 @@ usage: (make-network-process &rest ARGS)  */)
     p->kill_without_query = 1;
   if ((tem = Fplist_get (contact, QCstop), !NILP (tem)))
     pset_command (p, Qt);
+  p->ancillary_data = !NILP (Fplist_get (contact, QCancillary_data));
   p->pid = 0;
   p->backlog = 5;
   p->is_non_blocking_client = false;
@@ -4574,6 +4727,11 @@ server_accept_connection (Lisp_Object server, int channel)
 
   p = XPROCESS (proc);
 
+  /* TODO: I think this is supposed to be done by checking for a
+     property in contact, not by just copying the field from the
+     server process, but I'm not sure exactly what is correct */
+  p->ancillary_data = ps->ancillary_data;
+
   /* Build new contact information for this setup.  */
   contact = Fcopy_sequence (ps->childp);
   contact = Fplist_put (contact, QCserver, Qnil);
@@ -5587,6 +5745,7 @@ read_process_output_error_handler (Lisp_Object error_val)
 static void
 read_and_dispose_of_process_output (struct Lisp_Process *p, char *chars,
 				    ssize_t nbytes,
+				    Lisp_Object *fds, size_t nfds,
 				    struct coding_system *coding);
 
 /* Read pending output from the process channel,
@@ -5611,6 +5770,9 @@ read_process_output (Lisp_Object proc, int channel)
   ptrdiff_t count = SPECPDL_INDEX ();
   Lisp_Object odeactivate;
   char chars[sizeof coding->carryover + readmax];
+  char cbuf[512] = {};
+  int nfds = 0;
+  int *fd_data = NULL;
 
   if (carryover)
     /* See the comment above.  */
@@ -5639,8 +5801,34 @@ read_process_output (Lisp_Object proc, int channel)
 				    readmax - buffered);
       else
 #endif
-	nbytes = emacs_read (channel, chars + carryover + buffered,
-			     readmax - buffered);
+	{
+	  struct stat statbuf;
+	  fstat(channel, &statbuf);
+	  if (S_ISSOCK(statbuf.st_mode) && p->ancillary_data) {
+	    /* we declare cbuf outside of here since it is the backing
+	       storage for the fd_data array */
+	    struct iovec iov = { .iov_base = chars + carryover + buffered,
+				 .iov_len = readmax - buffered, };
+	    struct msghdr msgh = { .msg_iov = &iov,
+				   .msg_iovlen = 1,
+				   .msg_control = cbuf,
+				   .msg_controllen = sizeof cbuf, };
+	    nbytes = recvmsg(channel, &msgh, MSG_CMSG_CLOEXEC);
+	    /* check for control messages */
+	    for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL;
+		 cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
+	      if (cmsg->cmsg_level == SOL_SOCKET
+		  && cmsg->cmsg_type == SCM_RIGHTS) {
+		nfds = cmsg->cmsg_len / (sizeof(int));
+		fd_data = (int *) CMSG_DATA(cmsg);
+	      }
+	    }
+	  } else {
+	    nbytes = emacs_read (channel, chars + carryover + buffered,
+				 readmax - buffered);
+	  }
+	}
+
       if (nbytes > 0 && p->adaptive_read_buffering)
 	{
 	  int delay = p->read_output_delay;
@@ -5669,6 +5857,12 @@ read_process_output (Lisp_Object proc, int channel)
       nbytes += buffered;
       nbytes += buffered && nbytes <= 0;
     }
+  /* if we saw any fds, put them in an array of Lisp_Objects; this is
+     a no-op if we saw no fds */
+  Lisp_Object fds[nfds];
+  for (int i = 0; i < nfds; i++) {
+    fds[i] = make_number(fd_data[i]);
+  }
 
   p->decoding_carryover = 0;
 
@@ -5690,7 +5884,7 @@ read_process_output (Lisp_Object proc, int channel)
      friends don't expect current-buffer to be changed from under them.  */
   record_unwind_current_buffer ();
 
-  read_and_dispose_of_process_output (p, chars, nbytes, coding);
+  read_and_dispose_of_process_output (p, chars, nbytes, fds, nfds, coding);
 
   /* Handling the process output should not deactivate the mark.  */
   Vdeactivate_mark = odeactivate;
@@ -5702,6 +5896,7 @@ read_process_output (Lisp_Object proc, int channel)
 static void
 read_and_dispose_of_process_output (struct Lisp_Process *p, char *chars,
 				    ssize_t nbytes,
+				    Lisp_Object *fds, size_t nfds,
 				    struct coding_system *coding)
 {
   Lisp_Object outstream = p->filter;
@@ -5775,14 +5970,20 @@ read_and_dispose_of_process_output (struct Lisp_Process *p, char *chars,
 	      coding->carryover_bytes);
       p->decoding_carryover = coding->carryover_bytes;
     }
-  if (SBYTES (text) > 0)
+  if (SBYTES (text) > 0) {
     /* FIXME: It's wrong to wrap or not based on debug-on-error, and
        sometimes it's simply wrong to wrap (e.g. when called from
        accept-process-output).  */
+    Lisp_Object form;
+    if (p->ancillary_data)
+      form = list4 (outstream, make_lisp_proc (p), text, Flist(nfds, fds));
+    else
+      form = list3 (outstream, make_lisp_proc (p), text);
     internal_condition_case_1 (read_process_output_call,
-			       list3 (outstream, make_lisp_proc (p), text),
+			       form,
 			       !NILP (Vdebug_on_error) ? Qnil : Qerror,
 			       read_process_output_error_handler);
+  }
 
   /* If we saved the match data nonrecursively, restore it now.  */
   restore_search_regs ();
@@ -7866,6 +8067,7 @@ syms_of_process (void)
   DEFSYM (QCnowait, ":nowait");
   DEFSYM (QCsentinel, ":sentinel");
   DEFSYM (QCuse_external_socket, ":use-external-socket");
+  DEFSYM (QCancillary_data, ":ancillary-data");
   DEFSYM (QCtls_parameters, ":tls-parameters");
   DEFSYM (Qnsm_verify_connection, "nsm-verify-connection");
   DEFSYM (QClog, ":log");
@@ -7877,6 +8079,8 @@ syms_of_process (void)
   DEFSYM (QCstderr, ":stderr");
   DEFSYM (Qpty, "pty");
   DEFSYM (Qpipe, "pipe");
+  DEFSYM (QCinfd, ":infd");
+  DEFSYM (QCoutfd, ":outfd");
 
   DEFSYM (Qlast_nonmenu_event, "last-nonmenu-event");
 
@@ -7979,6 +8183,7 @@ The variable takes effect when `start-process' is called.  */);
   defsubr (&Sprocess_list);
   defsubr (&Smake_process);
   defsubr (&Smake_pipe_process);
+  defsubr (&Smake_fd_process);
   defsubr (&Sserial_process_configure);
   defsubr (&Smake_serial_process);
   defsubr (&Sset_network_process_option);
diff --git a/src/process.h b/src/process.h
index a5f690d..72ba204 100644
--- a/src/process.h
+++ b/src/process.h
@@ -166,6 +166,8 @@ struct Lisp_Process
     bool_bf is_non_blocking_client : 1;
     /* Whether this is a server or a client socket. */
     bool_bf is_server : 1;
+    /* Whether the filter should have ancillary data passed to it */
+    bool_bf ancillary_data : 1;
     int raw_status;
     /* The length of the socket backlog. */
     int backlog;
-- 
2.8.2




  reply	other threads:[~2016-06-07  1:25 UTC|newest]

Thread overview: 40+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-06-07  1:25 Teaching emacsclient to act as a pager, and more Spencer Baugh
2016-06-07  1:25 ` Spencer Baugh [this message]
2016-06-07  1:25 ` [PATCH 2/5] server.el: accept FDs from emacsclient Spencer Baugh
2016-06-07  1:25 ` [PATCH 3/5] emacsclient: support passing stdin/out/err to emacs Spencer Baugh
2016-06-07  1:25 ` [PATCH 4/5] server: add pager tapping and show-active Spencer Baugh
2016-06-07  1:25 ` [PATCH 5/5] emacsclient: add extra-quiet mode Spencer Baugh
2016-06-08 15:51 ` Teaching emacsclient to act as a pager, and more Tassilo Horn
2016-06-08 16:13   ` Anders Lindgren
2016-06-08 17:30     ` Tassilo Horn
2016-06-09  0:25   ` raman
2016-06-09 11:31 ` H. Dieter Wilhelm
2016-06-27 22:42 ` Ole JørgenBrønner
2016-07-24 18:22 ` sbaugh
2016-09-09 13:42   ` Noam Postavsky
2016-09-09 14:14     ` sbaugh
2016-09-09 14:59       ` Stefan Monnier
2016-09-09 15:58         ` sbaugh
2016-09-09 19:26           ` Stefan Monnier
2016-09-09 19:42             ` Eli Zaretskii
2016-09-09 21:13             ` sbaugh
2016-09-10  6:37               ` Using file descriptors in Emacs (was: Teaching emacsclient to act as a pager, and more) Eli Zaretskii
2016-09-10 20:15             ` Teaching emacsclient to act as a pager, and more sbaugh
2016-09-11  2:11               ` Leo Liu
2018-02-16 23:14               ` Kaushal Modi
2018-02-17 15:46                 ` Göktuğ Kayaalp
2016-09-09 15:53       ` Eli Zaretskii
2016-09-09 17:16         ` sbaugh
2016-09-09 18:50           ` Eli Zaretskii
2016-09-09 19:03             ` sbaugh
2016-09-09 19:26               ` Eli Zaretskii
2016-09-09 20:38                 ` sbaugh
2016-09-10  7:12                   ` Using file descriptors in Emacs Eli Zaretskii
2016-09-10 14:28                     ` sbaugh
2016-09-11 15:28                       ` Eli Zaretskii
2016-09-11 16:00                         ` sbaugh
2016-09-11 16:39                           ` Eli Zaretskii
2016-09-11 16:57                             ` sbaugh
2016-09-11 17:13                               ` Eli Zaretskii
2016-09-12 15:40                               ` Davis Herring
2016-09-09 13:27 ` Teaching emacsclient to act as a pager, and more sbaugh

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1465262706-5229-2-git-send-email-sbaugh@catern.com \
    --to=sbaugh@catern.com \
    --cc=emacs-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.