diff options
author | Daniel Black <grooverdan@users.sourceforge.net> | 2015-03-12 07:26:04 +1100 |
---|---|---|
committer | Daniel Black <daniel@mariadb.org> | 2021-03-28 13:53:55 +1100 |
commit | 460d480c7496e670686c01a1a1d707fb4d412ece (patch) | |
tree | a0e2cdcd128c952c89d4c4cc5795120a47c12ece | |
parent | fa0ad2fb11859fdeec93bbf3c4a6fc418eb372ac (diff) | |
download | mariadb-git-460d480c7496e670686c01a1a1d707fb4d412ece.tar.gz |
MDEV-5536: add systemd socket activation
Systemd has a socket activation feature where a mariadb.socket
definition defines the sockets to listen to, and passes those
file descriptors directly to mariadbd to use when a connection
occurs.
The new functionality is utilized when starting as follows:
systemctl start mariadb.socket
The mariadb.socket definition only needs to contain the network
information, ListenStream= directives, the mariadb.service
definition is still used for service instigation.
When mariadbd is started in this way, the socket, port, bind-address
backlog are all assumed to be self contained in the mariadb.socket
definition and as such the mariadb settings and command line
arguments of these network settings are ignored.
See man systemd.socket for how to limit this to specific ports.
Extra ports, those specified with extra_port in socket activation
mode, are those with a FileDescriptorName=extra. These need
to be in a separate service name like mariadb-extra.socket and
these require a Service={mariadb.service} directive to map to the
original service. Extra ports need systemd v227 or greater
(not RHEL/Centos7 - v219) when FileDescriptorName= was added,
otherwise the extra ports are treated like ordinary ports.
The number of sockets isn't limited when using systemd socket activation
(except by operating system limits on file descriptors and a minimal
amount of memory used per file descriptor). The systemd sockets passed
can include any ownership or permissions, including those the
mariadbd process wouldn't normally have the permission to create.
This implementation is compatible with mariadb.service definitions.
Those services started with:
systemctl start mariadb.service
does actually start the mariadb.service and used all the my.cnf
settings of sockets and ports like it previously did.
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | cmake/systemd.cmake | 1 | ||||
-rw-r--r-- | config.h.cmake | 1 | ||||
-rwxr-xr-x | debian/rules | 2 | ||||
-rw-r--r-- | include/my_service_manager.h | 8 | ||||
-rw-r--r-- | include/mysql/psi/mysql_socket.h | 49 | ||||
-rw-r--r-- | sql/mysqld.cc | 157 | ||||
-rw-r--r-- | support-files/CMakeLists.txt | 19 | ||||
-rw-r--r-- | support-files/mariadb-extra.socket.in | 20 | ||||
-rw-r--r-- | support-files/mariadb-extra@.socket.in | 20 | ||||
-rw-r--r-- | support-files/mariadb.socket.in | 24 | ||||
-rw-r--r-- | support-files/mariadb@.socket.in | 23 |
12 files changed, 321 insertions, 7 deletions
diff --git a/.gitignore b/.gitignore index 9eb935f127c..4fee2deaea5 100644 --- a/.gitignore +++ b/.gitignore @@ -241,7 +241,11 @@ support-files/config.small.ini support-files/mariadb.pc support-files/mariadb.pp support-files/mariadb.service +support-files/mariadb.socket +support-files/mariadb-extra.socket support-files/mariadb@.service +support-files/mariadb@.socket +support-files/mariadb-extra@.socket support-files/my-huge.cnf support-files/my-innodb-heavy-4G.cnf support-files/my-large.cnf diff --git a/cmake/systemd.cmake b/cmake/systemd.cmake index 84d0cba94d9..c223716faeb 100644 --- a/cmake/systemd.cmake +++ b/cmake/systemd.cmake @@ -37,6 +37,7 @@ MACRO(CHECK_SYSTEMD) ENDIF() SET(CMAKE_REQUIRED_LIBRARIES ${LIBSYSTEMD}) CHECK_LIBRARY_EXISTS(systemd sd_listen_fds "" HAVE_SYSTEMD_SD_LISTEN_FDS) + CHECK_LIBRARY_EXISTS(systemd sd_listen_fds_with_names "" HAVE_SYSTEMD_SD_LISTEN_FDS_WITH_NAMES) CHECK_INCLUDE_FILES(systemd/sd-daemon.h HAVE_SYSTEMD_SD_DAEMON_H) CHECK_FUNCTION_EXISTS(sd_notify HAVE_SYSTEMD_SD_NOTIFY) CHECK_FUNCTION_EXISTS(sd_notifyf HAVE_SYSTEMD_SD_NOTIFYF) diff --git a/config.h.cmake b/config.h.cmake index 2af94f96d9a..698ea65efd5 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -103,6 +103,7 @@ /* Libraries */ #cmakedefine HAVE_LIBWRAP 1 #cmakedefine HAVE_SYSTEMD 1 +#cmakedefine HAVE_SYSTEMD_SD_LISTEN_FDS_WITH_NAMES 1 /* Does "struct timespec" have a "sec" and "nsec" field? */ #cmakedefine HAVE_TIMESPEC_TS_SEC 1 diff --git a/debian/rules b/debian/rules index a8bd26d43df..758831fcdf6 100755 --- a/debian/rules +++ b/debian/rules @@ -190,7 +190,7 @@ override_dh_systemd_enable: dh_systemd_enable --name=mariadb dh_systemd_enable --no-enable --name=mariadb@ -# Start mysql at sequence number 19 before 20 where apache, proftpd etc gets +# Start MariaDB at sequence number 19 before 20 where apache, proftpd etc gets # started which might depend on a running database server. override_dh_installinit-arch: dh_installinit --name=mariadb --no-start -- defaults 19 21 diff --git a/include/my_service_manager.h b/include/my_service_manager.h index 95b5235e755..3eff1253f20 100644 --- a/include/my_service_manager.h +++ b/include/my_service_manager.h @@ -31,8 +31,16 @@ /** INTERVAL in seconds followed by printf style status */ #define service_manager_extend_timeout(INTERVAL, FMTSTR, ...) \ sd_notifyf(0, "STATUS=" FMTSTR "\nEXTEND_TIMEOUT_USEC=%u\n", ##__VA_ARGS__, INTERVAL * 1000000) +/* sd_listen_fds_with_names added v227 however RHEL/Centos7 has v219, fallback to sd_listen_fds */ +#ifndef HAVE_SYSTEMD_SD_LISTEN_FDS_WITH_NAMES +#define sd_listen_fds_with_names(FD, NAMES) sd_listen_fds(FD) +#endif #else +#define sd_listen_fds_with_names(FD, NAMES) (0) +#define sd_is_socket_unix(FD, TYPE, LISTENING, PATH, SIZE) (0) +#define sd_is_socket_inet(FD, FAMILY, TYPE, LISTENING, PORT) (0) +#define SD_LISTEN_FDS_START (0) #define sd_notify(X, Y) #define sd_notifyf(E, F, ...) #define service_manager_extend_timeout(I, FMTSTR, ...) diff --git a/include/mysql/psi/mysql_socket.h b/include/mysql/psi/mysql_socket.h index 59489fabe25..d70e1c8fc5a 100644 --- a/include/mysql/psi/mysql_socket.h +++ b/include/mysql/psi/mysql_socket.h @@ -305,6 +305,22 @@ inline_mysql_socket_set_state(MYSQL_SOCKET socket, enum PSI_socket_state state) #endif /* HAVE_PSI_SOCKET_INTERFACE */ /** + @def mysql_socket_fd(K, F) + Create a socket. + @c mysql_socket_fd is a replacement for @c socket. + @param K PSI_socket_key for this instrumented socket + @param F File descriptor +*/ + +#ifdef HAVE_PSI_SOCKET_INTERFACE + #define mysql_socket_fd(K, F) \ + inline_mysql_socket_fd(K, F) +#else + #define mysql_socket_fd(K, F) \ + inline_mysql_socket_fd(F) +#endif + +/** @def mysql_socket_socket(K, D, T, P) Create a socket. @c mysql_socket_socket is a replacement for @c socket. @@ -578,6 +594,39 @@ static inline void inline_mysql_socket_register( } #endif +/** mysql_socket_fd */ + +static inline MYSQL_SOCKET +inline_mysql_socket_fd +( +#ifdef HAVE_PSI_SOCKET_INTERFACE + PSI_socket_key key, +#endif + int fd) +{ + MYSQL_SOCKET mysql_socket= MYSQL_INVALID_SOCKET; + mysql_socket.fd= fd; + + DBUG_ASSERT(mysql_socket.fd != INVALID_SOCKET); +#ifdef HAVE_PSI_SOCKET_INTERFACE + mysql_socket.m_psi= PSI_SOCKET_CALL(init_socket) + (key, (const my_socket*)&mysql_socket.fd, NULL, 0); +#endif + /** + Currently systemd socket activation is the user of this + function. Its API (man sd_listen_fds) says FD_CLOSE_EXEC + is already called. If there becomes another user, we + can call it again without detriment. + + If needed later: + #if defined(HAVE_FCNTL) && defined(FD_CLOEXEC) + (void) fcntl(mysql_socket.fd, F_SETFD, FD_CLOEXEC); + #endif + */ + + return mysql_socket; +} + /** mysql_socket_socket */ static inline MYSQL_SOCKET diff --git a/sql/mysqld.cc b/sql/mysqld.cc index adb6ff82662..7a1d56e7ff9 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1346,6 +1346,9 @@ struct my_rnd_struct sql_rand; ///< used by sql_class.cc:THD::THD() Dynamic_array<MYSQL_SOCKET> listen_sockets(PSI_INSTRUMENT_MEM, 0); bool unix_sock_is_online= false; +static int systemd_sock_activation; /* systemd socket activation */ + + /** Error reporter that buffer log messages. @param level log message level @@ -1690,8 +1693,10 @@ static void close_connections(void) { MYSQL_SOCKET *sock= listen_sockets.get_pos(i); (void) mysql_socket_close(*sock); - if (sock->is_unix_domain_socket) + if (sock->is_unix_domain_socket && !systemd_sock_activation) + { (void) unlink(mysqld_unix_port); + } } listen_sockets.free_memory(); mysql_mutex_unlock(&LOCK_start_thread); @@ -2342,6 +2347,144 @@ static void activate_tcp_port(uint port, DBUG_VOID_RETURN; } + +/** + Activate usage of a systemd activated sockets + i.e started by mariadb.socket +*/ + +static void use_systemd_activated_sockets() +{ +#ifndef __linux__ + return; +#else + char **names = NULL; + int sd_sockets; + DBUG_ENTER("use_systemd_activated_sockets"); + + sd_sockets= sd_listen_fds_with_names(0, &names); + + if (!sd_sockets) + DBUG_VOID_RETURN; + + DBUG_PRINT("general",("Systemd listen_fds is %d", sd_sockets)); + while (sd_sockets--) + { + MYSQL_SOCKET sock; + int stype= 0, accepting= 0, getnameinfo_err; + socklen_t l; + union + { + struct sockaddr sa; + struct sockaddr_storage storage; + struct sockaddr_in in; + struct sockaddr_in6 in6; + struct sockaddr_un un; + } addr; + SOCKET_SIZE_TYPE addrlen= sizeof(addr); + char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; + + int fd= SD_LISTEN_FDS_START + sd_sockets; + + if (getsockname(fd, &addr.sa, &addrlen)) + { + sql_print_error("Unable to getsockname on systemd socket activation socket %d," + " errno %d", fd, errno); + goto err; + } + + l= sizeof(stype); + if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &stype, &l) < 0) + { + sql_print_error("Unable to getsockopt(SOL_SOCKET, SO_TYPE) on" + " systemd socket activation socket %d," + " errno %d", fd, errno); + goto err; + } + + if (stype != SOCK_STREAM) + { + sql_print_error("Unknown systemd socket activation socket %d," + " not of type SOCK_STREAM - type %d", fd, stype); + goto err; + } + + l= sizeof(accepting); + if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0) + { + sql_print_error("Unable to getsockopt(SOL_SOCKET, SO_ACCEPTCONN) on" + " systemd socket activation socket %d," + " errno %d", fd, errno); + goto err; + } + + if (!accepting) + { + sql_print_error("Unknown systemd socket activation socket %d," + " is not listening", fd); + goto err; + } + + switch (addr.sa.sa_family) + { + case AF_INET: + sock= mysql_socket_fd(key_socket_tcpip, fd); + sock.is_unix_domain_socket= 0; + mysqld_port= ntohs(addr.in.sin_port); + break; + case AF_INET6: + sock= mysql_socket_fd(key_socket_tcpip, fd); + sock.is_unix_domain_socket= 0; + mysqld_port= ntohs(addr.in6.sin6_port); + break; + case AF_UNIX: + sock= mysql_socket_fd(key_socket_unix, fd); + sock.is_unix_domain_socket= 1; + break; + default: + sql_print_error("Unknown systemd socket activation socket %d," + " not UNIX or INET socket", fd); + goto err; + } + + getnameinfo_err= getnameinfo(&addr.sa, addrlen, hbuf, sizeof(hbuf), sbuf, + sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV); + if (getnameinfo_err) + sql_print_warning("getnameinfo() on systemd socket activation socket %d" + " failed with error %d", fd, getnameinfo_err); + else + { + /* + Handle abstract sockets and present them in @ form. + We don't just use sbuf because of https://sourceware.org/bugzilla/show_bug.cgi?id=27634. + */ + if (sbuf[0] == '\0') + addr.un.sun_path[0] = '@'; + sql_print_information("Using systemd activated socket %s port %s", hbuf, + sbuf[0] == '\0' ? addr.un.sun_path : sbuf); + } + + /* + We check names!=NULL here because sd_listen_fds_with_names maybe + just sd_listen_fds on older pre v227 systemd + */ + sock.is_extra_port= names && strcmp(names[sd_sockets], "extra") == 0; + mysql_socket_set_thread_owner(sock); + listen_sockets.push(sock); + } + systemd_sock_activation= 1; + free(names); + + DBUG_VOID_RETURN; + +err: + free(names); + unireg_abort(1); + DBUG_VOID_RETURN; +#endif /* __linux__ */ +} + + static void network_init(void) { #ifdef HAVE_SYS_UN_H @@ -2350,6 +2493,8 @@ static void network_init(void) #endif DBUG_ENTER("network_init"); + use_systemd_activated_sockets(); + if (MYSQL_CALLBACK_ELSE(thread_scheduler, init, (), 0)) unireg_abort(1); /* purecov: inspected */ @@ -2366,7 +2511,7 @@ static void network_init(void) if (!opt_disable_networking) DBUG_ASSERT(report_port != 0); #endif - if (!opt_disable_networking && !opt_bootstrap) + if (!opt_disable_networking && !opt_bootstrap && !systemd_sock_activation) { if (mysqld_port) activate_tcp_port(mysqld_port, &listen_sockets, @@ -2380,7 +2525,7 @@ static void network_init(void) /* ** Create the UNIX socket */ - if (mysqld_unix_port[0] && !opt_bootstrap) + if (mysqld_unix_port[0] && !opt_bootstrap && systemd_sock_activation==0) { MYSQL_SOCKET unix_sock= MYSQL_INVALID_SOCKET; size_t port_len; @@ -5589,7 +5734,8 @@ int mysqld_main(int argc, char **argv) if (IS_SYSVAR_AUTOSIZE(&server_version_ptr)) sql_print_information(ER_DEFAULT(ER_STARTUP), my_progname, server_version, - (unix_sock_is_online ? mysqld_unix_port : (char*) ""), + (systemd_sock_activation ? "Systemd socket activated ports" : + (unix_sock_is_online ? mysqld_unix_port : (char*) "")), mysqld_port, MYSQL_COMPILATION_COMMENT); else { @@ -5601,7 +5747,8 @@ int mysqld_main(int argc, char **argv) sql_print_information(ER_DEFAULT(ER_STARTUP), my_progname, real_server_version, - (unix_sock_is_online ? mysqld_unix_port : (char*) ""), + (systemd_sock_activation ? "Systemd socket activated ports" : + (unix_sock_is_online ? mysqld_unix_port : (char*) "")), mysqld_port, MYSQL_COMPILATION_COMMENT); } diff --git a/support-files/CMakeLists.txt b/support-files/CMakeLists.txt index 338ae90cd42..851f0394f4a 100644 --- a/support-files/CMakeLists.txt +++ b/support-files/CMakeLists.txt @@ -120,6 +120,10 @@ IF(UNIX AND NOT WITHOUT_SERVER) IF(HAVE_SYSTEMD) CONFIGURE_FILE(mariadb.service.in ${CMAKE_CURRENT_BINARY_DIR}/mariadb.service @ONLY) + CONFIGURE_FILE(mariadb.socket.in + ${CMAKE_CURRENT_BINARY_DIR}/mariadb.socket @ONLY) + CONFIGURE_FILE(mariadb-extra.socket.in + ${CMAKE_CURRENT_BINARY_DIR}/mariadb-extra.socket @ONLY) EXECUTE_PROCESS( COMMAND ${CMAKE_COMMAND} -E create_symlink ./mariadb.service mysql.service COMMAND ${CMAKE_COMMAND} -E create_symlink ./mariadb.service mysqld.service @@ -136,13 +140,24 @@ IF(UNIX AND NOT WITHOUT_SERVER) IF(NOT CMAKE_VERSION VERSION_LESS 3.3.0 OR NOT RPM) CONFIGURE_FILE(mariadb@.service.in ${CMAKE_CURRENT_BINARY_DIR}/mariadb@.service @ONLY) - INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/mariadb@.service + CONFIGURE_FILE(mariadb@.socket.in + ${CMAKE_CURRENT_BINARY_DIR}/mariadb@.socket @ONLY) + CONFIGURE_FILE(mariadb-extra@.socket.in + ${CMAKE_CURRENT_BINARY_DIR}/mariadb-extra@.socket @ONLY) + INSTALL(FILES + ${CMAKE_CURRENT_BINARY_DIR}/mariadb@.service + ${CMAKE_CURRENT_BINARY_DIR}/mariadb@.socket + ${CMAKE_CURRENT_BINARY_DIR}/mariadb-extra@.socket + ${CMAKE_CURRENT_BINARY_DIR}/mysql.service + ${CMAKE_CURRENT_BINARY_DIR}/mysqld.service DESTINATION ${inst_location}/systemd COMPONENT SupportFiles) ENDIF() IF(INSTALL_SYSTEMD_UNITDIR) INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/mariadb.service + ${CMAKE_CURRENT_BINARY_DIR}/mariadb.socket + ${CMAKE_CURRENT_BINARY_DIR}/mariadb-extra.socket ${CMAKE_CURRENT_BINARY_DIR}/mysql.service ${CMAKE_CURRENT_BINARY_DIR}/mysqld.service DESTINATION ${INSTALL_SYSTEMD_UNITDIR} COMPONENT Server) @@ -154,6 +169,8 @@ IF(UNIX AND NOT WITHOUT_SERVER) "${INSTALL_SYSTEMD_UNITDIR}/mariadb@bootstrap.service.d" COMPONENT Server) INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/mariadb@.service + ${CMAKE_CURRENT_BINARY_DIR}/mariadb@.socket + ${CMAKE_CURRENT_BINARY_DIR}/mariadb-extra@.socket DESTINATION ${INSTALL_SYSTEMD_UNITDIR} COMPONENT Server) ENDIF() diff --git a/support-files/mariadb-extra.socket.in b/support-files/mariadb-extra.socket.in new file mode 100644 index 00000000000..f268a0ad2a8 --- /dev/null +++ b/support-files/mariadb-extra.socket.in @@ -0,0 +1,20 @@ + +[Unit] + +Description=MariaDB @VERSION@ database server (socket activation extra port) +Documentation=man:mariadbd(8) +Documentation=https://mariadb.com/kb/en/library/systemd/ + +[Socket] + +Service=mariadb.service + +# An "extra" as a descriptor name that means treat these ListenStreams as +# the same as an extra_port. +# Ref: https://mariadb.com/kb/en/thread-pool-system-status-variables/#extra_port + +FileDescriptorName=extra + +ListenStream=@mariadb-extra +ListenStream=@MYSQL_UNIX_ADDR@-extra + diff --git a/support-files/mariadb-extra@.socket.in b/support-files/mariadb-extra@.socket.in new file mode 100644 index 00000000000..f33344f2abd --- /dev/null +++ b/support-files/mariadb-extra@.socket.in @@ -0,0 +1,20 @@ + +[Unit] + +Description=MariaDB @VERSION@ database server (socket activation extra port multi-instance %I) +Documentation=man:mariadbd(8) +Documentation=https://mariadb.com/kb/en/library/systemd/ + +[Socket] + +Service=mariadb@%i.service + +# An "extra" as a descriptor name that means treat these ListenStreams as +# the same as an extra_port. +# Ref: https://mariadb.com/kb/en/thread-pool-system-status-variables/#extra_port + +FileDescriptorName=extra + +ListenStream=@mariadb-extra-%I +ListenStream=@MYSQL_UNIX_ADDR@-extra-%I + diff --git a/support-files/mariadb.socket.in b/support-files/mariadb.socket.in new file mode 100644 index 00000000000..d99fdee0334 --- /dev/null +++ b/support-files/mariadb.socket.in @@ -0,0 +1,24 @@ + +[Unit] +Description=MariaDB @VERSION@ database server (socket activation) +Documentation=man:mariadbd(8) +Documentation=https://mariadb.com/kb/en/library/systemd/ + +[Socket] + +############################################################################## +## USERs can override +## +## +## by creating a file in /etc/systemd/system/mariadb.socket.d/MY_SPECIAL.conf +## and adding/setting the following under [Socket] will override this file's +## settings. + +SocketUser=@MYSQLD_USER@ +SocketMode=777 + +ListenStream=@mariadb +ListenStream=@MYSQL_UNIX_ADDR@ +ListenStream=@MYSQL_TCP_PORT@ + +# Backlog=150 diff --git a/support-files/mariadb@.socket.in b/support-files/mariadb@.socket.in new file mode 100644 index 00000000000..561a44443c9 --- /dev/null +++ b/support-files/mariadb@.socket.in @@ -0,0 +1,23 @@ + +[Unit] +Description=MariaDB @VERSION@ database server (socket activation multi-instance %I)) +Documentation=man:mariadbd(8) +Documentation=https://mariadb.com/kb/en/library/systemd/ + +[Socket] + +############################################################################## +## USERs can override +## +## +## by creating a file in /etc/systemd/system/mariadb.socket.d/MY_SPECIAL.conf +## and adding/setting the following under [Socket] will override this file's +## settings. + +SocketUser=@MYSQLD_USER@ +SocketMode=777 + +ListenStream=@mariadb-%I +ListenStream=@MYSQL_UNIX_ADDR@-%I + +# Backlog=150 |