Commit e390b7b5 authored by Matthew Leach's avatar Matthew Leach Committed by Eli Zaretskii

Add external socket launching support

* src/process.c (connect_network_socket): Allow a pre-allocated socket
descriptor to be used if passed to Emacs, avoiding the call to
'socket' and 'bind'.
(Fmake_network_process): Allow users to pass ':use-external-socket' in
the parameter plist to use any sockets that have been passed to Emacs.
(wait_reading_process_output): Call 'socket' and 'bind' every time.
(syms_of_process): New symbol ':use-external-socket'.
(set_external_socket_descriptor): New function.
(external_sock_fd): New variable.
* src/lisp.h: (set_external_socket_descriptor): New declaration.
* src/emacs.c (main): Call 'sd_listen_fds' to read the number of sockets
passed and call 'set_external_socket_descriptor' to set the external
socket.
* src/Makefile.in: Add libsystemd library and C flags to the Emacs
compilation options.

* configure.ac: Add new default-on option "systemd" and check for
libsystemd at configure time.

* lisp/server.el (server-start): Set ':use-external-socket' to 't' when
calling 'make-network-process'.

* etc/NEWS: Document new socket-passing functionality and the configure
option to disable systemd interaction.

* doc/emacs/misc.texi (Emacs Server): Document systemd socket passing
functionality and provide systemd unit examples.
* doc/lispref/processes.texi (Network Processes): Document new
'make-network-process' option ':use-external-socket'.
parent bb8c8fdf
......@@ -330,6 +330,7 @@ OPTION_DEFAULT_ON([tiff],[don't compile with TIFF image support])
OPTION_DEFAULT_ON([gif],[don't compile with GIF image support])
OPTION_DEFAULT_ON([png],[don't compile with PNG image support])
OPTION_DEFAULT_ON([rsvg],[don't compile with SVG image support])
OPTION_DEFAULT_ON([libsystemd],[don't compile with libsystemd support])
OPTION_DEFAULT_OFF([cairo],[compile with Cairo drawing (experimental)])
OPTION_DEFAULT_ON([xml2],[don't compile with XML parsing support])
OPTION_DEFAULT_ON([imagemagick],[don't compile with ImageMagick image support])
......@@ -2732,6 +2733,18 @@ fi
AC_SUBST(LIBGNUTLS_LIBS)
AC_SUBST(LIBGNUTLS_CFLAGS)
HAVE_LIBSYSTEMD=no
if test "${with_libsystemd}" = "yes" ; then
EMACS_CHECK_MODULES([LIBSYSTEMD], [libsystemd >= 226],
[HAVE_LIBSYSTEMD=yes], [HAVE_LIBSYSTEMD=no])
if test "${HAVE_LIBSYSTEMD}" = "yes"; then
AC_DEFINE(HAVE_LIBSYSTEMD, 1, [Define if using libsystemd.])
fi
fi
AC_SUBST(LIBSYSTEMD_LIBS)
AC_SUBST(LIBSYSTEMD_CFLAGS)
NOTIFY_OBJ=
NOTIFY_SUMMARY=no
......
......@@ -1580,6 +1580,44 @@ option. @xref{Initial Options}. When Emacs is started this way, it
calls @code{server-start} after initialization, and returns control to
the calling terminal instead of opening an initial frame; it then
waits in the background, listening for edit requests.
@cindex socket activation, systemd, Emacs
@item
An external process can invoke the Emacs server when a connection
event occurs upon a specified socket and pass the socket to the new
Emacs server process. An instance of this is @command{systemd}'s
socket functionaly: the @command{systemd} service creates a socket and
listens for connections on it; when @command{emacsclient} connects to
it for the first time, @command{systemd} can launch the Emacs server
and hand over the socket to it for servicing @command{emacsclient}
connections. A setup to use this functionality could be:
@file{~/.config/systemd/user/emacs.service}:
@example
[Unit]
Description=Emacs
[Service]
Type=forking
ExecStart=/path/to/emacs --daemon
ExecStop=/path/to/emacsclient --eval "(kill-emacs)"
Restart=always
[Install]
WantedBy=default.target
@end example
@file{~/.config/systemd/user/emacs.socket}:
@example
[Socket]
ListenStream=/path/to/.emacs.socket
[Install]
WantedBy=sockets.target
@end example
The @code{ListenStream} path will be the path that Emacs listens for
connections from @command{emacsclient}; this is a file of your choice.
@end itemize
@cindex @env{TEXEDIT} environment variable
......
......@@ -2367,6 +2367,12 @@ automatically for the given @var{host} and @var{service}.
ignored. @code{ipv4} and @code{ipv6} specify to use IPv4 and IPv6,
respectively.
@item :use-external-socket @var{use-external-socket}
If @var{use-external-socket} is non-@code{nil} use any sockets passed
to Emacs on invocation instead of allocating one. This is used by the
Emacs server code to allow on-demand socket activation. If Emacs
wasn't passed a socket, this option is silently ignored.
@item :local @var{local-address}
For a server process, @var{local-address} is the address to listen on.
It overrides @var{family}, @var{host} and @var{service}, so you
......
......@@ -32,6 +32,13 @@ the default in developer builds. As before, use
'--enable-gcc-warnings' to stop the build if GCC issues warnings.
+++
** Emacs server now has socket-launching support. This allows socket
based activation, where an external process can invoke the Emacs
server process upon a socket connection event and hand over the socket
to Emacs. Emacs will use this socket for servicing emacsclient
commands. systemd can make use of this new functionally but can be
disabled with the configure option '--disable-libsystemd'.
** New configure option '--disable-build-details' attempts to build an
Emacs that is more likely to be reproducible; that is, if you build
and install Emacs twice, the second Emacs is a copy of the first.
......
......@@ -655,6 +655,7 @@ server or call `\\[server-force-delete]' to forcibly disconnect it."))
:noquery t
:sentinel #'server-sentinel
:filter #'server-process-filter
:use-external-socket t
;; We must receive file names without being decoded.
;; Those are decoded by server-process-filter according
;; to file-name-coding-system. Also don't get
......
......@@ -307,6 +307,9 @@ LIBSELINUX_LIBS = @LIBSELINUX_LIBS@
LIBGNUTLS_LIBS = @LIBGNUTLS_LIBS@
LIBGNUTLS_CFLAGS = @LIBGNUTLS_CFLAGS@
LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
INTERVALS_H = dispextern.h intervals.h composite.h
GETLOADAVG_LIBS = @GETLOADAVG_LIBS@
......@@ -372,6 +375,7 @@ ALL_CFLAGS=-Demacs $(MYCPPFLAGS) -I. -I$(srcdir) \
$(WEBKIT_CFLAGS) \
$(SETTINGS_CFLAGS) $(FREETYPE_CFLAGS) $(FONTCONFIG_CFLAGS) \
$(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \
$(LIBSYSTEMD_CFLAGS) \
$(LIBGNUTLS_CFLAGS) $(NOTIFY_CFLAGS) $(CAIRO_CFLAGS) \
$(WARN_CFLAGS) $(WERROR_CFLAGS) $(CFLAGS)
ALL_OBJC_CFLAGS=$(ALL_CFLAGS) $(GNU_OBJC_CFLAGS)
......@@ -489,7 +493,7 @@ LIBES = $(LIBS) $(W32_LIBS) $(LIBS_GNUSTEP) $(LIBX_BASE) $(LIBIMAGE) \
$(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \
$(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \
$(LIBGNUTLS_LIBS) $(LIB_PTHREAD) $(GETADDRINFO_A_LIBS) \
$(NOTIFY_LIBS) $(LIB_MATH) $(LIBZ) $(LIBMODULES)
$(NOTIFY_LIBS) $(LIB_MATH) $(LIBZ) $(LIBMODULES) $(LIBSYSTEMD_LIBS)
$(leimdir)/leim-list.el: bootstrap-emacs$(EXEEXT)
$(MAKE) -C ../leim leim-list.el EMACS="$(bootstrap_exe)"
......
......@@ -56,6 +56,11 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include <binary-io.h>
#endif
#ifdef HAVE_LIBSYSTEMD
#include <systemd/sd-daemon.h>
#include <sys/socket.h>
#endif /* HAVE_LIBSYSTEMD */
#ifdef HAVE_WINDOW_SYSTEM
#include TERM_HEADER
#endif /* HAVE_WINDOW_SYSTEM */
......@@ -676,6 +681,9 @@ main (int argc, char **argv)
char dname_arg2[80];
#endif
char *ch_to_dir = 0;
#ifdef HAVE_LIBSYSTEMD
int systemd_socket;
#endif
/* If we use --chdir, this records the original directory. */
char *original_pwd = 0;
......@@ -999,6 +1007,20 @@ main (int argc, char **argv)
exit (1);
}
#ifdef HAVE_LIBSYSTEMD
/* Read the number of sockets passed through by systemd. */
systemd_socket = sd_listen_fds(1);
if (systemd_socket > 1)
fprintf (stderr, "\nWarning: systemd has passed more than one socket to the Emacs process.\n\
Try adding 'Accept=false' in the Emacs socket unit file.\n");
else if (systemd_socket == 1 &&
sd_is_socket (SD_LISTEN_FDS_START,
AF_UNSPEC, SOCK_STREAM, 1) >= 0)
set_external_socket_descriptor (SD_LISTEN_FDS_START);
#endif /* HAVE_LIBSYSTEMD */
#ifndef DAEMON_MUST_EXEC
#ifdef USE_GTK
fprintf (stderr, "\nWarning: due to a long standing Gtk+ bug\nhttp://bugzilla.gnome.org/show_bug.cgi?id=85715\n\
......
......@@ -4184,6 +4184,7 @@ extern void delete_gpm_wait_descriptor (int);
extern void init_process_emacs (void);
extern void syms_of_process (void);
extern void setup_process_coding_systems (Lisp_Object);
extern void set_external_socket_descriptor (int);
/* Defined in callproc.c. */
#ifndef DOS_NT
......
......@@ -267,6 +267,9 @@ static int max_process_desc;
/* The largest descriptor currently in use for input; -1 if none. */
static int max_input_desc;
/* The descriptor of any sockets passed to Emacs; -1 if none. */
static int external_sock_fd = -1;
/* Indexed by descriptor, gives the process (if any) for that descriptor. */
static Lisp_Object chan_process[FD_SETSIZE];
static void wait_for_socket_fds (Lisp_Object, char const *);
......@@ -3075,7 +3078,8 @@ finish_after_tls_connection (Lisp_Object proc)
#endif
static void
connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses)
connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses,
Lisp_Object use_external_socket_p)
{
ptrdiff_t count = SPECPDL_INDEX ();
ptrdiff_t count1;
......@@ -3089,6 +3093,16 @@ connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses)
struct Lisp_Process *p = XPROCESS (proc);
Lisp_Object contact = p->childp;
int optbits = 0;
int socket_to_use = -1;
if (!NILP (use_external_socket_p))
{
socket_to_use = external_sock_fd;
/* Ensure we don't consume the external socket twice. */
external_sock_fd = -1;
}
/* Do this in case we never enter the while-loop below. */
count1 = SPECPDL_INDEX ();
......@@ -3109,7 +3123,11 @@ connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses)
sa = xmalloc (addrlen);
conv_lisp_to_sockaddr (family, ip_address, sa, addrlen);
s = socket (family, p->socktype | SOCK_CLOEXEC, p->ai_protocol);
if (socket_to_use != -1)
s = socket_to_use;
else
s = socket (family, p->socktype | SOCK_CLOEXEC, p->ai_protocol);
if (s < 0)
{
xerrno = errno;
......@@ -3168,8 +3186,11 @@ connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses)
report_file_error ("Cannot set reuse option on server socket", Qnil);
}
if (bind (s, sa, addrlen))
report_file_error ("Cannot bind server socket", Qnil);
/* If we are passed a socket descriptor, it should be
already bound. */
if (socket_to_use == -1)
if (bind (s, sa, addrlen))
report_file_error ("Cannot bind server socket", Qnil);
#ifdef HAVE_GETSOCKNAME
if (p->port == 0)
......@@ -3534,6 +3555,11 @@ The following network options can be specified for this connection:
(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.
......@@ -3578,7 +3604,7 @@ usage: (make-network-process &rest ARGS) */)
EMACS_INT port = 0;
Lisp_Object tem;
Lisp_Object name, buffer, host, service, address;
Lisp_Object filter, sentinel;
Lisp_Object filter, sentinel, use_external_socket_p;
Lisp_Object ip_addresses = Qnil;
int socktype;
int family = -1;
......@@ -3618,6 +3644,7 @@ usage: (make-network-process &rest ARGS) */)
buffer = Fplist_get (contact, QCbuffer);
filter = Fplist_get (contact, QCfilter);
sentinel = Fplist_get (contact, QCsentinel);
use_external_socket_p = Fplist_get (contact, QCuse_external_socket);
CHECK_STRING (name);
......@@ -3914,7 +3941,7 @@ usage: (make-network-process &rest ARGS) */)
}
#endif
connect_network_socket (proc, ip_addresses);
connect_network_socket (proc, ip_addresses, use_external_socket_p);
return proc;
}
......@@ -4848,7 +4875,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
{
Lisp_Object ip_addresses = check_for_dns (aproc);
if (!NILP (ip_addresses) && !EQ (ip_addresses, Qt))
connect_network_socket (aproc, ip_addresses);
connect_network_socket (aproc, ip_addresses, Qnil);
else
retry_for_async = true;
}
......@@ -7709,6 +7736,16 @@ catch_child_signal (void)
}
#endif /* subprocesses */
/* Set the external socket descriptor for Emacs to use when
`make-network-process' is called with a non-nil
`:use-external-socket' option. The fd should have been checked to
ensure it is a valid socket and is already bound. */
void
set_external_socket_descriptor(int fd)
{
external_sock_fd = fd;
}
/* This is not called "init_process" because that is the name of a
Mach system call, so it would cause problems on Darwin systems. */
......@@ -7837,6 +7874,7 @@ syms_of_process (void)
DEFSYM (QCserver, ":server");
DEFSYM (QCnowait, ":nowait");
DEFSYM (QCsentinel, ":sentinel");
DEFSYM (QCuse_external_socket, ":use-external-socket");
DEFSYM (QCtls_parameters, ":tls-parameters");
DEFSYM (Qnsm_verify_connection, "nsm-verify-connection");
DEFSYM (QClog, ":log");
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment