[Asterisk-code-review] Core: Add support for systemd socket activation. (asterisk[master])

Corey Farrell asteriskteam at digium.com
Sun Jun 18 19:42:34 CDT 2017


Corey Farrell has uploaded this change for review. ( https://gerrit.asterisk.org/5877


Change subject: Core: Add support for systemd socket activation.
......................................................................

Core: Add support for systemd socket activation.

This change adds support for socket activation of certain SOCK_STREAM
listeners in Asterisk:
* AMI / AMI over TLS
* CLI
* HTTP / HTTPS
* chan_sip TCP / TLS

Example systemd units are provided.  This support extends to any socket
which is initialized using ast_tcptls_server_start, so any unknown
modules using this function will support socket activation.

Asterisk continues to function as normal if socket activation is not
enabled or if systemd development headers are not available during
build.

ASTERISK-27063 #close

Change-Id: Id814ee6a892f4b80d018365c8ad8d89063474f4d
---
A contrib/systemd/README.txt
A contrib/systemd/asterisk-ami.socket
A contrib/systemd/asterisk-amis.socket
A contrib/systemd/asterisk-cli.socket
A contrib/systemd/asterisk-http.socket
A contrib/systemd/asterisk-https.socket
A contrib/systemd/asterisk-sip-tcp.socket
A contrib/systemd/asterisk-sip-tls.socket
A contrib/systemd/asterisk.service
A contrib/systemd/asterisk.socket
M include/asterisk/io.h
M include/asterisk/netsock2.h
M main/asterisk.c
M main/io.c
M main/netsock2.c
M main/tcptls.c
16 files changed, 382 insertions(+), 5 deletions(-)



  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/77/5877/1

diff --git a/contrib/systemd/README.txt b/contrib/systemd/README.txt
new file mode 100644
index 0000000..6090d2b
--- /dev/null
+++ b/contrib/systemd/README.txt
@@ -0,0 +1,97 @@
+SystemD Socket Activation for Asterisk
+======================================
+
+This folder contains sample unit files which can be used as the basis of a
+socket activated Asterisk deployment.  Socket activation support currently
+extends to the following listeners:
+
+* Asterisk Command-line Interface
+* Asterisk Manager Interface (clear text and TLS)
+* Builtin HTTP / HTTPS server
+* chan_sip TCP and TLS transports
+
+
+Separate .socket units or a single unit
+=======================================
+
+Asterisk is a complex system with many components which can be enabled or
+disabled individually.  Using socket activation requires deciding to use
+a single socket file or multiple separate socket files.
+
+The remainder of this README assumes separate socket units are used for each
+listener.
+
+
+Service and Socket files
+========================
+
+All .socket and .service examples in this folder use "reasonable" default
+paths for Linux.  Depending on your distribution and ./configure options
+you may need to modify these before installing.  The files are meant to
+be examples rather than files to be blindly installed.
+
+
+Installing and enabling socket units
+====================================
+
+Modify socket files as desired.  Install them to a location where systemd
+will find them.  pkg-config can be used to determine an appropriate location.
+
+For socket files to be managed directly by the local administrator:
+    pkg-config systemd --variable systemdsystemconfdir
+
+For socket files to be deployed by package manager:
+    pkg-config systemd --variable systemdsystemunitdir
+
+
+After installing socket files you must run 'systemctl daemon-reload' for
+systemd to read the added/modified units.  After this you can enable the
+desired sockets, for example to enable AMI:
+    systemctl enable asterisk-ami.socket
+
+
+Socket Selection
+================
+
+Asterisk configuration is unchanged by use of socket activation.  When a
+component that supports socket activation starts a listener in Asterisk,
+any sockets provided by systemd are iterated.  The systemd socket is used
+when the bound address configured by Asterisk is an exact match with the
+address given by the ListenStream setting in the systemd socket.
+
+
+Command-line Interface
+======================
+
+Symbolic links do not appear to be resolved when checking the CLI listener.
+This may be of concern since /var/run is often a symbolic link to /run. Both
+Asterisk and systemd must use /var/run, or both must use /run.  Mismatching
+will result in service startup failure.
+
+When socket activation is used for Asterisk CLI some asterisk.conf options
+are ignored.  The following options from the [files] section are ignored
+and must instead be set by the systemd socket file.
+* astctlowner - use SocketUser
+* astctlgroup - use SocketGroup
+* astctlpermissions - use SocketMode
+
+See asterisk-cli.socket for an example of these settings.
+
+
+Stopping Asterisk
+=================
+
+Some existing asterisk.service files use CLI 'core stop now' for the ExecStop
+command.  This is not recommended.  If Asterisk fails to start systemd tries
+running the ExecStop command.  This can result in an loop where ExecStop causes
+CLI socket activation to start Asterisk again.  A better way to deal with this
+is to use Type=notify and do not specify an ExecStop command.  See the example
+asterisk.service.
+
+
+Unused Sockets
+==============
+
+Asterisk makes no attempt to check for sockets provided by systemd that are not
+used.  It is the users responsibility to only provide sockets which Asterisk is
+configured to use.
diff --git a/contrib/systemd/asterisk-ami.socket b/contrib/systemd/asterisk-ami.socket
new file mode 100644
index 0000000..1fd45e4
--- /dev/null
+++ b/contrib/systemd/asterisk-ami.socket
@@ -0,0 +1,10 @@
+[Unit]
+Description=Asterisk Manager Interface Socket
+
+[Socket]
+Service=asterisk.service
+ListenStream=0.0.0.0:5038
+
+[Install]
+WantedBy=sockets.target
+RequiredBy=asterisk.service
diff --git a/contrib/systemd/asterisk-amis.socket b/contrib/systemd/asterisk-amis.socket
new file mode 100644
index 0000000..c17cee3
--- /dev/null
+++ b/contrib/systemd/asterisk-amis.socket
@@ -0,0 +1,10 @@
+[Unit]
+Description=Asterisk Manager Interface TLS Socket
+
+[Socket]
+Service=asterisk.service
+ListenStream=0.0.0.0:5039
+
+[Install]
+WantedBy=sockets.target
+RequiredBy=asterisk.service
diff --git a/contrib/systemd/asterisk-cli.socket b/contrib/systemd/asterisk-cli.socket
new file mode 100644
index 0000000..9161a7b
--- /dev/null
+++ b/contrib/systemd/asterisk-cli.socket
@@ -0,0 +1,13 @@
+[Unit]
+Description=Asterisk Command-line Interface Socket
+
+[Socket]
+Service=asterisk.service
+ListenStream=/var/run/asterisk/asterisk.ctl
+SocketUser=asterisk
+SocketGroup=asterisk
+SocketMode=0660
+
+[Install]
+WantedBy=sockets.target
+RequiredBy=asterisk.service
diff --git a/contrib/systemd/asterisk-http.socket b/contrib/systemd/asterisk-http.socket
new file mode 100644
index 0000000..e6862b5
--- /dev/null
+++ b/contrib/systemd/asterisk-http.socket
@@ -0,0 +1,11 @@
+[Unit]
+Description=Asterisk HTTP Socket
+
+[Socket]
+Service=asterisk.service
+FreeBind=true
+ListenStream=127.0.0.1:8088
+
+[Install]
+WantedBy=sockets.target
+RequiredBy=asterisk.service
diff --git a/contrib/systemd/asterisk-https.socket b/contrib/systemd/asterisk-https.socket
new file mode 100644
index 0000000..d9240dd
--- /dev/null
+++ b/contrib/systemd/asterisk-https.socket
@@ -0,0 +1,11 @@
+[Unit]
+Description=Asterisk HTTPS Socket
+
+[Socket]
+Service=asterisk.service
+FreeBind=true
+ListenStream=127.0.0.1:8089
+
+[Install]
+WantedBy=sockets.target
+RequiredBy=asterisk.service
diff --git a/contrib/systemd/asterisk-sip-tcp.socket b/contrib/systemd/asterisk-sip-tcp.socket
new file mode 100644
index 0000000..9bb93ca
--- /dev/null
+++ b/contrib/systemd/asterisk-sip-tcp.socket
@@ -0,0 +1,10 @@
+[Unit]
+Description=Asterisk SIP TCP Socket
+
+[Socket]
+Service=asterisk.service
+ListenStream=0.0.0.0:5060
+
+[Install]
+WantedBy=sockets.target
+RequiredBy=asterisk.service
diff --git a/contrib/systemd/asterisk-sip-tls.socket b/contrib/systemd/asterisk-sip-tls.socket
new file mode 100644
index 0000000..91673c4
--- /dev/null
+++ b/contrib/systemd/asterisk-sip-tls.socket
@@ -0,0 +1,10 @@
+[Unit]
+Description=Asterisk SIP TLS Socket
+
+[Socket]
+Service=asterisk.service
+ListenStream=0.0.0.0:5061
+
+[Install]
+WantedBy=sockets.target
+RequiredBy=asterisk.service
diff --git a/contrib/systemd/asterisk.service b/contrib/systemd/asterisk.service
new file mode 100644
index 0000000..c3d4648
--- /dev/null
+++ b/contrib/systemd/asterisk.service
@@ -0,0 +1,27 @@
+[Unit]
+Description=Asterisk PBX and telephony daemon.
+After=network.target
+
+[Service]
+Type=notify
+Environment=HOME=/var/lib/asterisk
+WorkingDirectory=/var/lib/asterisk
+User=asterisk
+Group=asterisk
+ExecStart=/usr/sbin/asterisk -mqf -C /etc/asterisk/asterisk.conf
+ExecReload=/usr/sbin/asterisk -rx 'core reload'
+
+#Nice=0
+#UMask=0002
+LimitCORE=infinity
+#LimitNOFILE=
+Restart=always
+RestartSec=4
+
+# Prevent duplication of logs with color codes to /var/log/messages
+StandardOutput=null
+
+PrivateTmp=true
+
+[Install]
+WantedBy=multi-user.target
diff --git a/contrib/systemd/asterisk.socket b/contrib/systemd/asterisk.socket
new file mode 100644
index 0000000..afdca0d
--- /dev/null
+++ b/contrib/systemd/asterisk.socket
@@ -0,0 +1,26 @@
+[Unit]
+Description=Asterisk Sockets
+
+[Socket]
+FreeBind=true
+SocketUser=asterisk
+SocketGroup=asterisk
+SocketMode=0660
+
+# CLI
+ListenStream=/var/run/asterisk/asterisk.ctl
+# AMI
+ListenStream=0.0.0.0:5038
+# AMIS
+ListenStream=0.0.0.0:5039
+# HTTP
+ListenStream=127.0.0.1:8088
+# HTTPS
+ListenStream=127.0.0.1:8089
+# chan_sip TCP
+ListenStream=0.0.0.0:5060
+# chan_sip TLS
+ListenStream=0.0.0.0:5061
+
+[Install]
+WantedBy=sockets.target
diff --git a/include/asterisk/io.h b/include/asterisk/io.h
index 6ee8450..75cc48e 100644
--- a/include/asterisk/io.h
+++ b/include/asterisk/io.h
@@ -24,6 +24,7 @@
 #define _ASTERISK_IO_H
 
 #include "asterisk/poll-compat.h"
+#include "asterisk/netsock2.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
@@ -148,6 +149,42 @@
  */
 int ast_sd_notify(const char *state);
 
+/*!
+ * \brief Check the type and sockaddr of a file descriptor.
+ * \param fd File Descriptor to check.
+ * \param type SOCK_STREAM or SOCK_DGRAM
+ * \param addr The socket address to match.
+ * \retval 0 if matching
+ * \retval -1 if not matching
+ *
+ * \note This function returns -1 if systemd's development headers were not
+ * detected on the system.
+ */
+int ast_sd_is_socket_sockaddr(int fd, int type, const struct ast_sockaddr* addr);
+
+/*!
+ * \brief Find a listening file descriptor provided by socket activation.
+ * \param type SOCK_STREAM or SOCK_DGRAM
+ * \param addr The socket address of the bound listener.
+ * \retval <0 File Descriptor to check.
+ * \retval >0 File Descriptor matching sockaddr.
+ *
+ * \note This function returns -1 if systemd's development headers were not
+ * detected on the system.
+ */
+int ast_sd_get_fd(int type, const struct ast_sockaddr *addr);
+
+/*!
+ * \brief Find a listening AF_LOCAL file descriptor provided by socket activation.
+ * \param type SOCK_STREAM or SOCK_DGRAM
+ * \param path The path of the listener.
+ * \retval <0 No match.
+ * \retval >0 File Descriptor matching path.
+ *
+ * \note This function returns -1 if systemd's development headers were not
+ * detected on the system.
+ */
+int ast_sd_get_fd_un(int type, const char *path);
 
 #if defined(__cplusplus) || defined(c_plusplus)
 }
diff --git a/include/asterisk/netsock2.h b/include/asterisk/netsock2.h
index 3ede990..6c0dd63 100644
--- a/include/asterisk/netsock2.h
+++ b/include/asterisk/netsock2.h
@@ -129,6 +129,22 @@
 }
 
 /*!
+ * \brief
+ * Copies the data from a sockaddr to an ast_sockaddr
+ *
+ * \param dst The destination ast_sockaddr
+ * \param src The source sockaddr
+ * \param len Length of the value stored in sockaddr
+ * \retval void
+ */
+static inline void ast_sockaddr_copy_sockaddr(struct ast_sockaddr *dst,
+		struct sockaddr *src, socklen_t len)
+{
+	memcpy(dst, src, len);
+	dst->len = len;
+}
+
+/*!
  * \since 1.8
  *
  * \brief
diff --git a/main/asterisk.c b/main/asterisk.c
index 16313ea..4424022 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -350,6 +350,7 @@
 char record_cache_dir[AST_CACHE_DIR_LEN] = DEFAULT_TMP_DIR;
 
 static int ast_socket = -1;		/*!< UNIX Socket for allowing remote control */
+static int ast_socket_is_sd = 0; /*!< Is socket activation responsible for ast_socket? */
 static int ast_consock = -1;		/*!< UNIX Socket for controlling another asterisk */
 pid_t ast_mainpid;
 struct console {
@@ -1576,8 +1577,16 @@
 	uid_t uid = -1;
 	gid_t gid = -1;
 
-	for (x = 0; x < AST_MAX_CONNECTS; x++)
+	for (x = 0; x < AST_MAX_CONNECTS; x++) {
 		consoles[x].fd = -1;
+	}
+
+	if (ast_socket_is_sd) {
+		ast_socket = ast_sd_get_fd_un(SOCK_STREAM, ast_config_AST_SOCKET);
+
+		goto start_lthread;
+	}
+
 	unlink(ast_config_AST_SOCKET);
 	ast_socket = socket(PF_LOCAL, SOCK_STREAM, 0);
 	if (ast_socket < 0) {
@@ -1602,10 +1611,17 @@
 		return -1;
 	}
 
+start_lthread:
 	if (ast_pthread_create_background(&lthread, NULL, listener, NULL)) {
 		ast_log(LOG_WARNING, "Unable to create listener thread.\n");
 		close(ast_socket);
 		return -1;
+	}
+
+	if (ast_socket_is_sd) {
+		/* owner/group/permissions are set by systemd, we might not even have access
+		 * to socket file so leave it alone */
+		return 0;
 	}
 
 	if (!ast_strlen_zero(ast_config_AST_CTL_OWNER)) {
@@ -2075,7 +2091,9 @@
 		pthread_cancel(lthread);
 		close(ast_socket);
 		ast_socket = -1;
-		unlink(ast_config_AST_SOCKET);
+		if (!ast_socket_is_sd) {
+			unlink(ast_config_AST_SOCKET);
+		}
 		pthread_kill(lthread, SIGURG);
 		pthread_join(lthread, NULL);
 	}
@@ -4317,7 +4335,12 @@
 	/* Initial value of the maximum active system verbosity level. */
 	ast_verb_sys_level = option_verbose;
 
-	if (ast_tryconnect()) {
+	if (ast_sd_get_fd_un(SOCK_STREAM, ast_config_AST_SOCKET) > 0) {
+		ast_socket_is_sd = 1;
+	}
+
+	/* DO NOT perform check for existing daemon if systemd has CLI socket activation */
+	if (!ast_socket_is_sd && ast_tryconnect()) {
 		/* One is already running */
 		if (ast_opt_remote) {
 			multi_thread_safe = 1;
diff --git a/main/io.c b/main/io.c
index b063c22..8a3e01b 100644
--- a/main/io.c
+++ b/main/io.c
@@ -36,6 +36,10 @@
 #include "asterisk/utils.h"
 #ifdef HAVE_SYSTEMD
 #include <systemd/sd-daemon.h>
+
+#ifndef SD_LISTEN_FDS_START
+#define SD_LISTEN_FDS_START 3
+#endif
 #endif
 
 #ifdef DEBUG_IO
@@ -392,3 +396,58 @@
 	return 0;
 #endif
 }
+
+int ast_sd_is_socket_sockaddr(int fd, int type, const struct ast_sockaddr* addr)
+{
+#ifdef HAVE_SYSTEMD
+	struct ast_sockaddr fd_addr;
+	struct sockaddr ss;
+	socklen_t ss_len;
+
+	if (sd_is_socket(fd, AF_UNSPEC, type, 1) <= 0) {
+		return -1;
+	}
+
+	if (getsockname(fd, &ss, &ss_len) != 0) {
+		return -1;
+	}
+
+	ast_sockaddr_copy_sockaddr(&fd_addr, &ss, ss_len);
+
+	return ast_sockaddr_cmp(addr, &fd_addr);
+#else
+	return -1;
+#endif
+}
+
+int ast_sd_get_fd(int type, const struct ast_sockaddr *addr)
+{
+#ifdef HAVE_SYSTEMD
+	int count = sd_listen_fds(0);
+	int idx;
+
+	for (idx = 0; idx < count; idx++) {
+		if (!ast_sd_is_socket_sockaddr(idx + SD_LISTEN_FDS_START, type, addr)) {
+			return idx + SD_LISTEN_FDS_START;
+		}
+	}
+#endif
+
+	return -1;
+}
+
+int ast_sd_get_fd_un(int type, const char *path)
+{
+#ifdef HAVE_SYSTEMD
+	int count = sd_listen_fds(0);
+	int idx;
+
+	for (idx = 0; idx < count; idx++) {
+		if (sd_is_socket_unix(idx + SD_LISTEN_FDS_START, type, 1, path, 0) > 0) {
+			return idx + SD_LISTEN_FDS_START;
+		}
+	}
+#endif
+
+	return -1;
+}
diff --git a/main/netsock2.c b/main/netsock2.c
index 8fb9c9e..b27913e 100644
--- a/main/netsock2.c
+++ b/main/netsock2.c
@@ -225,8 +225,6 @@
 	return 1;
 }
 
-
-
 int ast_sockaddr_parse(struct ast_sockaddr *addr, const char *str, int flags)
 {
 	struct addrinfo hints;
diff --git a/main/tcptls.c b/main/tcptls.c
index a3c7dfa..c26ed90 100644
--- a/main/tcptls.c
+++ b/main/tcptls.c
@@ -40,6 +40,7 @@
 
 #include "asterisk/compat.h"
 #include "asterisk/tcptls.h"
+#include "asterisk/io.h"
 #include "asterisk/http.h"
 #include "asterisk/utils.h"
 #include "asterisk/strings.h"
@@ -618,6 +619,7 @@
 	int flags;
 	int x = 1;
 	int tls_changed = 0;
+	int sd_socket;
 
 	if (desc->tls_cfg) {
 		char hash[41];
@@ -689,6 +691,19 @@
 		pthread_join(desc->master, NULL);
 	}
 
+	sd_socket = ast_sd_get_fd(SOCK_STREAM, &desc->local_address);
+
+	if (sd_socket != -1) {
+		if (desc->accept_fd != sd_socket) {
+			if (desc->accept_fd != -1) {
+				close(desc->accept_fd);
+			}
+			desc->accept_fd = sd_socket;
+		}
+
+		goto systemd_socket_activation;
+	}
+
 	if (desc->accept_fd != -1) {
 		close(desc->accept_fd);
 	}
@@ -718,8 +733,12 @@
 		ast_log(LOG_ERROR, "Unable to listen for %s!\n", desc->name);
 		goto error;
 	}
+
+systemd_socket_activation:
+	/* In case NonBlocking=true is not be set in the systemd service file. */
 	flags = fcntl(desc->accept_fd, F_GETFL);
 	fcntl(desc->accept_fd, F_SETFL, flags | O_NONBLOCK);
+
 	if (ast_pthread_create_background(&desc->master, NULL, desc->accept_fn, desc)) {
 		ast_log(LOG_ERROR, "Unable to launch thread for %s on %s: %s\n",
 			desc->name,

-- 
To view, visit https://gerrit.asterisk.org/5877
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-MessageType: newchange
Gerrit-Change-Id: Id814ee6a892f4b80d018365c8ad8d89063474f4d
Gerrit-Change-Number: 5877
Gerrit-PatchSet: 1
Gerrit-Owner: Corey Farrell <git at cfware.com>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20170618/a4a03a30/attachment-0001.html>


More information about the asterisk-code-review mailing list