Function: make-network-process

make-network-process is a function defined in process.c.

Signature

(make-network-process &rest ARGS)

Documentation

Create and return a network server or client process.

In Emacs, network connections are represented by process objects, so input and output work as for subprocesses and delete-process closes a network connection. However, a network process has no process id, it cannot be signaled, and the status codes are different from normal processes.

Arguments are specified as keyword/argument pairs. The following arguments are defined:

:name NAME -- NAME is name for process. It is modified if necessary
to make it unique.

:buffer BUFFER -- BUFFER is the buffer (or buffer-name) to associate
with the process. Process output goes at end of that buffer, unless you specify a filter function to handle the output. BUFFER may be also nil, meaning that this process is not associated with any buffer.

:host HOST -- HOST is name of the host to connect to, or its IP
address. The symbol local specifies the local host. If specified for a server process, it must be a valid name or address for the local host, and only clients connecting to that address will be accepted. If all interfaces should be bound, an address of "0.0.0.0" (for IPv4) or "::" (for IPv6) can be used. (On some operating systems,
using "::" listens on both IPv4 and IPv6.) local will use IPv4 by
default, use a FAMILY of ipv6 to override this.

:service SERVICE -- SERVICE is name of the service desired, or an
integer specifying a port number to connect to. If SERVICE is t, a random port number is selected for the server. A port number can be specified as an integer string, e.g., "80", as well as an integer.

:type TYPE -- TYPE is the type of connection. The default (nil) is a
stream type connection, datagram creates a datagram type connection, seqpacket creates a reliable datagram connection.

:family FAMILY -- FAMILY is the address (and protocol) family for the
service specified by HOST and SERVICE. The default (nil) is to use whatever address family (IPv4 or IPv6) that is defined for the host and port number specified by HOST and SERVICE. Other address families supported are:
  local -- for a local (i.e. UNIX) address specified by SERVICE.
  ipv4 -- use IPv4 address family only.
  ipv6 -- use IPv6 address family only.

:local ADDRESS -- ADDRESS is the local address used for the connection.
This parameter is ignored when opening a client process. When specified for a server process, the FAMILY, HOST and SERVICE args are ignored.

:remote ADDRESS -- ADDRESS is the remote partner's address for the
connection. This parameter is ignored when opening a stream server process. For a datagram server process, it specifies the initial setting of the remote datagram address. When specified for a client process, the FAMILY, HOST, and SERVICE args are ignored.

The format of ADDRESS depends on the address family:
- An IPv4 address is represented as a vector of integers [A B C D P]
corresponding to numeric IP address A.B.C.D and port number P.
- An IPv6 address has the same format as an IPv4 address but with 9
elements rather than 5.
- A local address is represented as a string with the address in the
local address space.
- An "unsupported family" address is represented by a cons (F . AV)
where F is the family number and AV is a vector containing the socket address data with one element per address data byte. Do not rely on this format in portable code, as it may depend on implementation defined constants, data sizes, and data structure alignment.

:coding CODING -- If CODING is a symbol, it specifies the coding
system used for both reading and writing for this process. If CODING is a cons (DECODING . ENCODING), DECODING is used for reading, and ENCODING is used for writing.

:nowait BOOL -- If NOWAIT is non-nil for a stream type client
process, return without waiting for the connection to complete; instead, the sentinel function will be called with second arg matching
"open" (if successful) or "failed" when the connect completes.
Default is to use a blocking connect (i.e. wait) for stream type connections.

:noquery BOOL -- Query the user unless BOOL is non-nil, and process is
running when Emacs is exited.

:stop BOOL -- Start process in the stopped state if BOOL non-nil.
In the stopped state, a server process does not accept new connections, and a client process does not handle incoming traffic. The stopped state is cleared by continue-process and set by stop-process.

:filter FILTER -- Install FILTER as the process filter.

:filter-multibyte BOOL -- If BOOL is non-nil, strings given to the
process filter are multibyte, otherwise they are unibyte. If this keyword is not specified, the strings are multibyte.

:sentinel SENTINEL -- Install SENTINEL as the process sentinel.

:log LOG -- Install LOG as the server process log function. This
function is called when the server accepts a network connection from a client. The arguments are SERVER, CLIENT, and MESSAGE, where SERVER is the server process, CLIENT is the new process for the connection, and MESSAGE is a string.

:plist PLIST -- Install PLIST as the new process's initial plist.

:tls-parameters LIST -- is a list that should be supplied if you're
opening a TLS connection. The first element is the TLS type (either gnutls-x509pki or gnutls-anon), and the remaining elements should be a keyword list accepted by gnutls-boot (as returned by gnutls-boot-parameters).

:server QLEN -- if QLEN is non-nil, create a server process for the
specified FAMILY, SERVICE, and connection type (stream or datagram). If QLEN is an integer, it is used as the max. length of the server's pending connection queue (also known as the backlog); the default queue length is 5. Default is to create a client process.

The following network options can be specified for this connection:

:broadcast BOOL -- Allow send and receive of datagram broadcasts.
:dontroute BOOL -- Only send to directly connected hosts.
:keepalive BOOL -- Send keep-alive messages on network stream.
:linger BOOL or TIMEOUT -- Send queued messages before closing.
:oobinline BOOL -- Place out-of-band data in receive data stream.
:priority INT -- Set protocol defined priority for sent packets.
:reuseaddr BOOL -- Allow reusing a recently used local address
                      (this is allowed by default for a server process).
:bindtodevice NAME -- bind to interface NAME. Using this may require
                      special privileges on some systems.
:use-external-socket BOOL -- Use any pre-allocated sockets that have
                             been passed to Emacs. If Emacs wasn't
                             passed a socket, this option is silently
                             ignored.


Consult the relevant system programmer's manual pages for more information on using these options.

A server process will listen for and accept connections from clients. When a client connection is accepted, a new network process is created for the connection with the following parameters:

- The client's process name is constructed by concatenating the server
process's NAME and a client identification string.
- If the FILTER argument is non-nil, the client process will not get a
separate process buffer; otherwise, the client's process buffer is a newly created buffer named after the server process's BUFFER name or process NAME concatenated with the client identification string.
- The connection type and the process filter and sentinel parameters are
inherited from the server process's TYPE, FILTER and SENTINEL.
- The client process's contact info is set according to the client's
addressing information (typically an IP address and a port number).
- The client process's plist is initialized from the server's plist.

Notice that the FILTER and SENTINEL args are never used directly by the server process. Also, the BUFFER argument is not used directly by the server process, but via the optional :log function, accepted (and failed) connections may be logged in the server process's buffer.

The original argument list, modified with the actual connection information, is available via the process-contact function.

View in manual

Probably introduced at or before Emacs version 22.1.

Source Code

// Defined in /usr/src/emacs/src/process.c
// Skipping highlighting due to helpful-max-highlight.
{
  Lisp_Object proc;
  Lisp_Object contact;
  struct Lisp_Process *p;
  const char *portstring UNINIT;
  char portbuf[INT_BUFSIZE_BOUND (EMACS_INT)];
#ifdef HAVE_LOCAL_SOCKETS
  struct sockaddr_un address_un;
#endif
  EMACS_INT port = 0;
  Lisp_Object tem;
  Lisp_Object name, buffer, host, service, address;
  Lisp_Object filter, sentinel, use_external_socket_p;
  Lisp_Object addrinfos = Qnil;
  int socktype;
  int family = -1;
  enum { any_protocol = 0 };
#ifdef HAVE_GETADDRINFO_A
  struct gaicb *dns_request = NULL;
#endif
  specpdl_ref count = SPECPDL_INDEX ();

  if (nargs == 0)
    return Qnil;

  /* Save arguments for process-contact and clone-process.  */
  contact = Flist (nargs, args);

#ifdef WINDOWSNT
  /* Ensure socket support is loaded if available.  */
  init_winsock (TRUE);
#endif

  /* :type TYPE  (nil: stream, datagram */
  tem = plist_get (contact, QCtype);
  if (NILP (tem))
    socktype = SOCK_STREAM;
#ifdef DATAGRAM_SOCKETS
  else if (EQ (tem, Qdatagram))
    socktype = SOCK_DGRAM;
#endif
#ifdef HAVE_SEQPACKET
  else if (EQ (tem, Qseqpacket))
    socktype = SOCK_SEQPACKET;
#endif
  else
    error ("Unsupported connection type");

  name = plist_get (contact, QCname);
  buffer = plist_get (contact, QCbuffer);
  filter = plist_get (contact, QCfilter);
  sentinel = plist_get (contact, QCsentinel);
  use_external_socket_p = plist_get (contact, QCuse_external_socket);
  Lisp_Object server = plist_get (contact, QCserver);
  bool nowait = !NILP (plist_get (contact, QCnowait));

  if (!NILP (server) && nowait)
    error ("`:server' is incompatible with `:nowait'");
  CHECK_STRING (name);

  /* :local ADDRESS or :remote ADDRESS */
  if (NILP (server))
    address = plist_get (contact, QCremote);
  else
    address = plist_get (contact, QClocal);
  if (!NILP (address))
    {
      host = service = Qnil;

      if (!get_lisp_to_sockaddr_size (address, &family))
	error ("Malformed :address");

      addrinfos = list1 (Fcons (make_fixnum (any_protocol), address));
      goto open_socket;
    }

  /* :family FAMILY -- nil (for Inet), local, or integer.  */
  tem = plist_get (contact, QCfamily);
  if (NILP (tem))
    {
#ifdef AF_INET6
      family = AF_UNSPEC;
#else
      family = AF_INET;
#endif
    }
#ifdef HAVE_LOCAL_SOCKETS
  else if (EQ (tem, Qlocal))
    family = AF_LOCAL;
#endif
#ifdef AF_INET6
  else if (EQ (tem, Qipv6))
    family = AF_INET6;
#endif
  else if (EQ (tem, Qipv4))
    family = AF_INET;
  else if (TYPE_RANGED_FIXNUMP (int, tem))
    family = XFIXNUM (tem);
  else
    error ("Unknown address family");

  /* :service SERVICE -- string, integer (port number), or t (random port).  */
  service = plist_get (contact, QCservice);

  /* :host HOST -- hostname, ip address, or 'local for localhost.  */
  host = plist_get (contact, QChost);
  if (NILP (host))
    {
      /* The "connection" function gets it bind info from the address we're
	 given, so use this dummy address if nothing is specified. */
#ifdef HAVE_LOCAL_SOCKETS
      if (family != AF_LOCAL)
#endif
        {
#ifdef AF_INET6
        if (family == AF_INET6)
          host = build_string ("::1");
        else
#endif
          host = build_string ("127.0.0.1");
        }
    }
  else
    {
      if (EQ (host, Qlocal))
        {
	/* Depending on setup, "localhost" may map to different IPv4 and/or
	   IPv6 addresses, so it's better to be explicit (Bug#6781).  */
#ifdef AF_INET6
        if (family == AF_INET6)
          host = build_string ("::1");
        else
#endif
          host = build_string ("127.0.0.1");
        }
      CHECK_STRING (host);
    }

#ifdef HAVE_LOCAL_SOCKETS
  if (family == AF_LOCAL)
    {
      if (!NILP (host))
	{
	  message (":family local ignores the :host property");
	  contact = plist_put (contact, QChost, Qnil);
	  host = Qnil;
	}
      CHECK_STRING (service);
      if (sizeof address_un.sun_path <= SBYTES (service))
	error ("Service name too long");
      addrinfos = list1 (Fcons (make_fixnum (any_protocol), service));
      goto open_socket;
    }
#endif

  /* Slow down polling to every ten seconds.
     Some kernels have a bug which causes retrying connect to fail
     after a connect.  Polling can interfere with gethostbyname too.  */
#ifdef POLL_FOR_INPUT
  if (socktype != SOCK_DGRAM)
    {
      record_unwind_protect_void (run_all_atimers);
      bind_polling_period (10);
    }
#endif

  if (!NILP (host))
    {
      MAYBE_UNUSED ptrdiff_t portstringlen;

      /* SERVICE can either be a string or int.
	 Convert to a C string for later use by getaddrinfo.  */
      if (EQ (service, Qt))
	{
	  portstring = "0";
	  portstringlen = 1;
	}
      else if (FIXNUMP (service))
	{
	  portstring = portbuf;
	  portstringlen = sprintf (portbuf, "%"pI"d", XFIXNUM (service));
	}
      else
	{
	  CHECK_STRING (service);
	  portstring = SSDATA (service);
	  portstringlen = SBYTES (service);
	}

#ifdef HAVE_GETADDRINFO_A
      if (nowait)
	{
	  ptrdiff_t hostlen = SBYTES (host);
	  struct req
	  {
	    struct gaicb gaicb;
	    struct addrinfo hints;
	    char str[FLEXIBLE_ARRAY_MEMBER];
	  } *req = xmalloc (FLEXSIZEOF (struct req, str,
					hostlen + 1 + portstringlen + 1));
	  dns_request = &req->gaicb;
	  dns_request->ar_name = req->str;
	  dns_request->ar_service = req->str + hostlen + 1;
	  dns_request->ar_request = &req->hints;
	  dns_request->ar_result = NULL;
	  memset (&req->hints, 0, sizeof req->hints);
	  req->hints.ai_family = family;
	  req->hints.ai_socktype = socktype;
	  strcpy (req->str, SSDATA (host));
	  strcpy (req->str + hostlen + 1, portstring);

	  int ret = getaddrinfo_a (GAI_NOWAIT, &dns_request, 1, NULL);
	  if (ret)
	    error ("%s/%s getaddrinfo_a error %d",
		   SSDATA (host), portstring, ret);

	  goto open_socket;
	}
#endif /* HAVE_GETADDRINFO_A */
    }

  /* If we have a host, use getaddrinfo to resolve both host and service.
     Otherwise, use getservbyname to lookup the service.  */

  if (!NILP (host))
    {
      struct addrinfo *res, *lres;
      Lisp_Object msg;

      maybe_quit ();

      struct addrinfo hints;
      memset (&hints, 0, sizeof hints);
      hints.ai_family = family;
      hints.ai_socktype = socktype;

      msg = network_lookup_address_info_1 (host, portstring, &hints, &res);
      if (!EQ (msg, Qt))
	error ("%s", SSDATA (msg));

      for (lres = res; lres; lres = lres->ai_next)
	addrinfos = Fcons (conv_addrinfo_to_lisp (lres), addrinfos);

      addrinfos = Fnreverse (addrinfos);

      freeaddrinfo (res);

      goto open_socket;
    }

  /* No hostname has been specified (e.g., a local server process).  */

  if (EQ (service, Qt))
    port = 0;
  else if (FIXNUMP (service))
    port = XFIXNUM (service);
  else
    {
      CHECK_STRING (service);

      port = -1;
      if (SBYTES (service) != 0)
	{
	  /* Allow the service to be a string containing the port number,
	     because that's allowed if you have getaddrbyname.  */
	  char *service_end;
	  long int lport = strtol (SSDATA (service), &service_end, 10);
	  if (service_end == SSDATA (service) + SBYTES (service))
	    port = lport;
	  else
	    {
	      struct servent *svc_info
		= getservbyname (SSDATA (service),
				 socktype == SOCK_DGRAM ? "udp" : "tcp");
	      if (svc_info)
		port = ntohs (svc_info->s_port);
	    }
	}
    }

  if (! (0 <= port && port < 1 << 16))
    {
      AUTO_STRING (unknown_service, "Unknown service: %s");
      xsignal1 (Qerror, CALLN (Fformat, unknown_service, service));
    }

 open_socket:

  if (!NILP (buffer))
    buffer = Fget_buffer_create (buffer, Qnil);

  /* Unwind bind_polling_period.  */
  unbind_to (count, Qnil);

  proc = make_process (name);
  record_unwind_protect (remove_process, proc);
  p = XPROCESS (proc);
  pset_childp (p, contact);
  pset_plist (p, Fcopy_sequence (plist_get (contact, QCplist)));
  pset_type (p, Qnetwork);

  pset_buffer (p, buffer);
  pset_sentinel (p, sentinel);
  pset_filter (p, filter);
  pset_log (p, plist_get (contact, QClog));
  if (tem = plist_get (contact, QCnoquery), !NILP (tem))
    p->kill_without_query = 1;
  if ((tem = plist_get (contact, QCstop), !NILP (tem)))
    pset_command (p, Qt);
  eassert (p->pid == 0);
  p->backlog = 5;
  eassert (! p->is_non_blocking_client);
  eassert (! p->is_server);
  p->port = port;
  p->socktype = socktype;
#ifdef HAVE_GETADDRINFO_A
  eassert (! p->dns_request);
#endif
#ifdef HAVE_GNUTLS
  tem = plist_get (contact, QCtls_parameters);
  CHECK_LIST (tem);
  p->gnutls_boot_parameters = tem;
#endif

  set_network_socket_coding_system (proc, host, service, name);

  /* :server QLEN */
  p->is_server = !NILP (server);
  if (TYPE_RANGED_FIXNUMP (int, server))
    p->backlog = XFIXNUM (server);

  /* :nowait BOOL */
  if (!p->is_server && socktype != SOCK_DGRAM && nowait)
    p->is_non_blocking_client = true;

  bool postpone_connection = false;
#ifdef HAVE_GETADDRINFO_A
  /* With async address resolution, the list of addresses is empty, so
     postpone connecting to the server. */
  if (!p->is_server && NILP (addrinfos))
    {
      p->dns_request = dns_request;
      p->status = list1 (Qconnect);
      postpone_connection = true;
    }
#endif
  if (! postpone_connection)
    connect_network_socket (proc, addrinfos, use_external_socket_p);

  specpdl_ptr = specpdl_ref_to_ptr (count);
  return proc;
}