From d2564db5d3f50adc67474ffb22b0ee7027a82828 Mon Sep 17 00:00:00 2001 From: Andreas Cadhalpun Date: Mon, 11 Aug 2014 23:22:05 +0200 Subject: Add upstream systemd support for clamav-daemon and clamav-freshclam. --- Makefile.am | 2 +- clamd/Makefile.am | 4 ++ clamd/clamav-daemon.service.in | 17 ++++++ clamd/clamav-daemon.socket | 12 ++++ clamd/clamd.c | 102 +++++++++++++++++++++++++++------- clamd/localserver.c | 34 ++++++++++++ clamd/server-th.c | 55 +++++++++++------- clamd/tcpserver.c | 50 +++++++++++++++++ configure.ac | 3 + freshclam/Makefile.am | 4 ++ freshclam/clamav-freshclam.service.in | 12 ++++ freshclam/freshclam.c | 39 +++++++++++-- m4/reorganization/libs/systemd.m4 | 18 ++++++ shared/misc.h | 8 +++ shared/optparser.c | 2 +- 15 files changed, 318 insertions(+), 44 deletions(-) create mode 100644 clamd/clamav-daemon.service.in create mode 100644 clamd/clamav-daemon.socket create mode 100644 freshclam/clamav-freshclam.service.in create mode 100644 m4/reorganization/libs/systemd.m4 diff --git a/Makefile.am b/Makefile.am index 2eecd94b668b..017d6327ff58 100644 --- a/Makefile.am +++ b/Makefile.am @@ -33,7 +33,7 @@ pkgconfig_DATA = libclamav.pc # don't complain that configuration files and databases are not removed, this is intended distuninstallcheck_listfiles = find . -type f ! -name clamd.conf ! -name freshclam.conf ! -name daily.cvd ! -name main.cvd -print DISTCLEANFILES = target.h -DISTCHECK_CONFIGURE_FLAGS=--enable-milter --disable-clamav --enable-all-jit-targets --enable-llvm=yes +DISTCHECK_CONFIGURE_FLAGS=--enable-milter --disable-clamav --enable-all-jit-targets --enable-llvm=yes --with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir) lcov: ($(MAKE); cd unit_tests; $(MAKE) lcov) quick-check: diff --git a/clamd/Makefile.am b/clamd/Makefile.am index 61bbd69d1133..edc133138e58 100644 --- a/clamd/Makefile.am +++ b/clamd/Makefile.am @@ -50,6 +50,10 @@ clamd_SOURCES = \ AM_CFLAGS=@WERR_CFLAGS@ +if INSTALL_SYSTEMD_UNITS +systemdsystemunit_DATA = clamav-daemon.socket clamav-daemon.service +endif + endif LIBS = $(top_builddir)/libclamav/libclamav.la @CLAMD_LIBS@ @THREAD_LIBS@ diff --git a/clamd/clamav-daemon.service.in b/clamd/clamav-daemon.service.in new file mode 100644 index 000000000000..0a5d456a2b36 --- /dev/null +++ b/clamd/clamav-daemon.service.in @@ -0,0 +1,17 @@ +[Unit] +Description=Clam AntiVirus userspace daemon +Documentation=man:clamd(8) man:clamd.conf(5) http://www.clamav.net/lang/en/doc/ +Requires=clamav-daemon.socket +# Check for database existence +ConditionPathExistsGlob=@DBDIR@/main.{c[vl]d,inc} +ConditionPathExistsGlob=@DBDIR@/daily.{c[vl]d,inc} + +[Service] +ExecStart=@prefix@/sbin/clamd --foreground=true +# Reload the database +ExecReload=/bin/kill -USR2 $MAINPID +StandardOutput=syslog + +[Install] +WantedBy=multi-user.target +Also=clamav-daemon.socket diff --git a/clamd/clamav-daemon.socket b/clamd/clamav-daemon.socket new file mode 100644 index 000000000000..43a34cd36aad --- /dev/null +++ b/clamd/clamav-daemon.socket @@ -0,0 +1,12 @@ +[Unit] +Description=Socket for Clam AntiVirus userspace daemon +Documentation=man:clamd(8) man:clamd.conf(5) http://www.clamav.net/lang/en/doc/ + +[Socket] +ListenStream=/run/clamav/clamd.ctl +#ListenStream=127.0.0.1:1024 +ExecStartPost=/bin/chown -R clamav:clamav /run/clamav/ +ExecStopPost=/bin/rm /run/clamav/clamd.ctl + +[Install] +WantedBy=sockets.target diff --git a/clamd/clamd.c b/clamd/clamd.c index 96204291f433..ec15eacc8c25 100644 --- a/clamd/clamd.c +++ b/clamd/clamd.c @@ -72,7 +72,7 @@ #include "scanner.h" short debug_mode = 0, logok = 0; -short foreground = 0; +short foreground = -1; char hostid[37]; char *get_hostid(void *cbdata); @@ -160,6 +160,31 @@ int main(int argc, char **argv) debug_mode = 1; } + /* check foreground option from command line to override config file */ + int j; + for(j = 0; j < argc; j += 1) + { + if ((memcmp(argv[j], "--foreground", 12) == 0) || (memcmp(argv[j], "-F", 2) == 0)) + { + /* found */ + break; + } + } + + if (j < argc) + { + if(optget(opts, "Foreground")->enabled) + { + foreground = 1; + } + else + { + foreground = 0; + } + } + + int num_fd = sd_listen_fds(0); + /* parse the config file */ cfgfile = optget(opts, "config-file")->strarg; pt = strdup(cfgfile); @@ -295,7 +320,9 @@ int main(int argc, char **argv) if(optget(opts, "LocalSocket")->enabled) localsock = 1; - if(!tcpsock && !localsock) { + logg("#Received %d file descriptor(s) from systemd.\n", num_fd); + + if(!tcpsock && !localsock && num_fd == 0) { logg("!Please define server type (local and/or TCP).\n"); ret = 1; break; @@ -577,7 +604,9 @@ int main(int argc, char **argv) break; } - if(tcpsock) { + if(tcpsock || num_fd > 0) { + int *t; + opt = optget(opts, "TCPAddr"); if (opt->enabled) { int breakout = 0; @@ -604,7 +633,7 @@ int main(int argc, char **argv) } } #ifndef _WIN32 - if(localsock) { + if(localsock && num_fd == 0) { int *t; mode_t sock_mode, umsk = umask(0777); /* socket is created with 000 to avoid races */ @@ -666,8 +695,43 @@ int main(int argc, char **argv) nlsockets++; } + /* check for local sockets passed by systemd */ + if (num_fd > 0) + { + int *t; + t = realloc(lsockets, sizeof(int) * (nlsockets + 1)); + if (!(t)) { + ret = 1; + break; + } + lsockets = t; + + lsockets[nlsockets] = localserver(opts); + if (lsockets[nlsockets] == -1) + { + ret = 1; + break; + } + else if (lsockets[nlsockets] > 0) + { + nlsockets++; + } + } + /* fork into background */ - if(!optget(opts, "Foreground")->enabled) { + if (foreground == -1) + { + if (optget(opts, "Foreground")->enabled) + { + foreground = 1; + } + else + { + foreground = 0; + } + } + if(foreground == 0) + { #ifdef C_BSD /* workaround for OpenBSD bug, see https://wwws.clamav.net/bugzilla/show_bug.cgi?id=885 */ for(ret=0;(unsigned int)ret 1) ? "s" : ""); - - for (i = 0; i < nlsockets; i++) { - closesocket(lsockets[i]); - } + if (num_fd == 0) + { + logg("*Closing the main socket%s.\n", (nlsockets > 1) ? "s" : ""); + for (i = 0; i < nlsockets; i++) { + closesocket(lsockets[i]); + } #ifndef _WIN32 - if(nlsockets && localsock) { - opt = optget(opts, "LocalSocket"); + if(nlsockets && localsock) { + opt = optget(opts, "LocalSocket"); - if(unlink(opt->strarg) == -1) - logg("!Can't unlink the socket file %s\n", opt->strarg); - else - logg("Socket file removed.\n"); - } + if(unlink(opt->strarg) == -1) + logg("!Can't unlink the socket file %s\n", opt->strarg); + else + logg("Socket file removed.\n"); + } #endif + } free(lsockets); diff --git a/clamd/localserver.c b/clamd/localserver.c index db07415212ad..d963efd244f5 100644 --- a/clamd/localserver.c +++ b/clamd/localserver.c @@ -39,6 +39,7 @@ #include "shared/optparser.h" #include "shared/output.h" +#include "shared/misc.h" #include "others.h" #include "server.h" @@ -60,6 +61,39 @@ int localserver(const struct optstruct *opts) STATBUF foo; char *estr; + int num_fd = sd_listen_fds(0); + if (num_fd > 2) + { + logg("!LOCAL: Received more than two file descriptors from systemd.\n"); + return -1; + } + else if (num_fd > 0) + { + /* use socket passed by systemd */ + int i; + for(i = 0; i < num_fd; i += 1) + { + sockfd = SD_LISTEN_FDS_START + i; + if (sd_is_socket(sockfd, AF_UNIX, SOCK_STREAM, 1) == 1) + { + /* correct socket */ + break; + } + else + { + /* wrong socket */ + sockfd = -2; + } + } + if (sockfd == -2) + { + logg("#LOCAL: No local AF_UNIX SOCK_STREAM socket received from systemd.\n"); + return -2; + } + logg("#LOCAL: Received AF_UNIX SOCK_STREAM socket from systemd.\n"); + return sockfd; + } + /* create socket */ memset((char *) &server, 0, sizeof(server)); server.sun_family = AF_UNIX; strncpy(server.sun_path, optget(opts, "LocalSocket")->strarg, sizeof(server.sun_path)); diff --git a/clamd/server-th.c b/clamd/server-th.c index d97cd6fb34e1..5c12811b662b 100644 --- a/clamd/server-th.c +++ b/clamd/server-th.c @@ -48,6 +48,7 @@ #include "shared/output.h" #include "shared/optparser.h" +#include "shared/misc.h" #include "fan.h" #include "server.h" @@ -445,13 +446,19 @@ static void *acceptloop_th(void *arg) } pthread_mutex_unlock(fds->buf_mutex); - for (i=0;i < fds->nfds; i++) { - if (fds->buf[i].fd == -1) - continue; - logg("$Shutdown: closed fd %d\n", fds->buf[i].fd); - shutdown(fds->buf[i].fd, 2); - closesocket(fds->buf[i].fd); + if (sd_listen_fds(0) == 0) + { + /* only close the sockets, when not using systemd socket activation */ + for (i=0;i < fds->nfds; i++) + { + if (fds->buf[i].fd == -1) + continue; + logg("$Shutdown: closed fd %d\n", fds->buf[i].fd); + shutdown(fds->buf[i].fd, 2); + closesocket(fds->buf[i].fd); + } } + fds_free(fds); pthread_mutex_destroy(fds->buf_mutex); pthread_mutex_lock(&exit_mutex); @@ -1344,16 +1351,22 @@ int recvloop_th(int *socketds, unsigned nsockets, struct cl_engine *engine, unsi if (progexit) { pthread_mutex_unlock(&exit_mutex); pthread_mutex_lock(fds->buf_mutex); - for (i=0;i < fds->nfds; i++) { - if (fds->buf[i].fd == -1) - continue; - thrmgr_group_terminate(fds->buf[i].group); - if (thrmgr_group_finished(fds->buf[i].group, EXIT_ERROR)) { - logg("$Shutdown closed fd %d\n", fds->buf[i].fd); - shutdown(fds->buf[i].fd, 2); - closesocket(fds->buf[i].fd); - fds->buf[i].fd = -1; - } + if (sd_listen_fds(0) == 0) + { + /* only close the sockets, when not using systemd socket activation */ + for (i=0;i < fds->nfds; i++) + { + if (fds->buf[i].fd == -1) + continue; + thrmgr_group_terminate(fds->buf[i].group); + if (thrmgr_group_finished(fds->buf[i].group, EXIT_ERROR)) + { + logg("$Shutdown closed fd %d\n", fds->buf[i].fd); + shutdown(fds->buf[i].fd, 2); + closesocket(fds->buf[i].fd); + fds->buf[i].fd = -1; + } + } } pthread_mutex_unlock(fds->buf_mutex); break; @@ -1462,9 +1475,13 @@ int recvloop_th(int *socketds, unsigned nsockets, struct cl_engine *engine, unsi #endif if(dbstat.entries) cl_statfree(&dbstat); - logg("*Shutting down the main socket%s.\n", (nsockets > 1) ? "s" : ""); - for (i = 0; i < nsockets; i++) - shutdown(socketds[i], 2); + if (sd_listen_fds(0) == 0) + { + /* only close the sockets, when not using systemd socket activation */ + logg("*Shutting down the main socket%s.\n", (nsockets > 1) ? "s" : ""); + for (i = 0; i < nsockets; i++) + shutdown(socketds[i], 2); + } if((opt = optget(opts, "PidFile"))->enabled) { if(unlink(opt->strarg) == -1) diff --git a/clamd/tcpserver.c b/clamd/tcpserver.c index c3027a85c1f4..81387b3c8512 100644 --- a/clamd/tcpserver.c +++ b/clamd/tcpserver.c @@ -60,6 +60,56 @@ int tcpserver(int **lsockets, unsigned int *nlsockets, char *ipaddr, const struc sockets = *lsockets; + int num_fd = sd_listen_fds(0); + if (num_fd > 2) + { + logg("!TCP: Received more than two file descriptors from systemd.\n"); + return -1; + } + else if (num_fd > 0) + { + /* use socket passed by systemd */ + int i; + for(i = 0; i < num_fd; i += 1) + { + sockfd = SD_LISTEN_FDS_START + i; + if (sd_is_socket(sockfd, AF_INET, SOCK_STREAM, 1) == 1) + { + /* correct socket */ + logg("#TCP: Received AF_INET SOCK_STREAM socket from systemd.\n"); + break; + } + else if (sd_is_socket(sockfd, AF_INET6, SOCK_STREAM, 1) == 1) + { + /* correct socket */ + logg("#TCP: Received AF_INET6 SOCK_STREAM socket from systemd.\n"); + break; + } + else + { + /* wrong socket */ + sockfd = -2; + } + } + if (sockfd == -2) + { + logg("#TCP: No tcp AF_INET/AF_INET6 SOCK_STREAM socket received from systemd.\n"); + return -2; + } + + t = realloc(sockets, sizeof(int) * (*nlsockets + 1)); + if (!(t)) { + return -1; + } + sockets = t; + + sockets[*nlsockets] = sockfd; + (*nlsockets)++; + *lsockets = sockets; + return 0; + } + + /* create socket */ snprintf(port, sizeof(port), "%lld", optget(opts, "TCPSocket")->numarg); memset(&hints, 0x00, sizeof(struct addrinfo)); diff --git a/configure.ac b/configure.ac index 41043753fc92..02caad29410a 100644 --- a/configure.ac +++ b/configure.ac @@ -86,6 +86,7 @@ AM_MAINTAINER_MODE m4_include([m4/reorganization/libs/libz.m4]) m4_include([m4/reorganization/libs/bzip.m4]) m4_include([m4/reorganization/libs/unrar.m4]) +m4_include([m4/reorganization/libs/systemd.m4]) m4_include([m4/reorganization/code_checks/ipv6.m4]) m4_include([m4/reorganization/code_checks/dns.m4]) m4_include([m4/reorganization/code_checks/fanotify.m4]) @@ -137,9 +138,11 @@ clamscan/Makefile database/Makefile docs/Makefile clamd/Makefile +clamd/clamav-daemon.service clamdscan/Makefile clamsubmit/Makefile clamav-milter/Makefile +freshclam/clamav-freshclam.service freshclam/Makefile sigtool/Makefile clamconf/Makefile diff --git a/freshclam/Makefile.am b/freshclam/Makefile.am index 58fc17471c35..051bf3380e98 100644 --- a/freshclam/Makefile.am +++ b/freshclam/Makefile.am @@ -49,6 +49,10 @@ freshclam_SOURCES = \ mirman.c \ mirman.h +if INSTALL_SYSTEMD_UNITS +systemdsystemunit_DATA = clamav-freshclam.service +endif + AM_CFLAGS=@WERR_CFLAGS@ DEFS = @DEFS@ -DCL_NOTHREADS AM_CPPFLAGS = @SSL_CPPFLAGS@ -I$(top_srcdir) -I$(top_srcdir)/shared -I$(top_srcdir)/libclamav @FRESHCLAM_CPPFLAGS@ @JSON_CPPFLAGS@ diff --git a/freshclam/clamav-freshclam.service.in b/freshclam/clamav-freshclam.service.in new file mode 100644 index 000000000000..f717cd642106 --- /dev/null +++ b/freshclam/clamav-freshclam.service.in @@ -0,0 +1,12 @@ +[Unit] +Description=ClamAV virus database updater +Documentation=man:freshclam(1) man:freshclam.conf(5) http://www.clamav.net/lang/en/doc/ +# If user wants it run from cron, don't start the daemon. +ConditionPathExists=!/etc/cron.d/clamav-freshclam + +[Service] +ExecStart=@prefix@/bin/freshclam -d --foreground=true +StandardOutput=syslog + +[Install] +WantedBy=multi-user.target diff --git a/freshclam/freshclam.c b/freshclam/freshclam.c index 166586bf6447..f170613877c4 100644 --- a/freshclam/freshclam.c +++ b/freshclam/freshclam.c @@ -64,7 +64,7 @@ static short terminate = 0; extern int active_children; -static short foreground = 1; +static short foreground = -1; char updtmpdir[512], dbdir[512]; int sigchld_wait = 1; const char *pidfile = NULL; @@ -117,7 +117,7 @@ sighandler (int sig) if (pidfile) unlink (pidfile); logg ("Update process terminated\n"); - exit (2); + exit (0); } return; @@ -322,6 +322,26 @@ main (int argc, char **argv) return 0; } + /* check foreground option from command line to override config file */ + int j; + for(j = 0; j < argc; j += 1) + { + if ((memcmp(argv[j], "--foreground", 12) == 0) || (memcmp(argv[j], "-F", 2) == 0)) + { + /* found */ + break; + } + } + + if (j < argc) { + if(optget(opts, "Foreground")->enabled) { + foreground = 1; + } + else { + foreground = 0; + } + } + /* parse the config file */ cfgfile = optget (opts, "config-file")->strarg; pt = strdup (cfgfile); @@ -638,7 +658,19 @@ main (int argc, char **argv) bigsleep = 24 * 3600 / checks; #ifndef _WIN32 - if (!optget (opts, "Foreground")->enabled) + /* fork into background */ + if (foreground == -1) + { + if (optget(opts, "Foreground")->enabled) + { + foreground = 1; + } + else + { + foreground = 0; + } + } + if(foreground == 0) { if (daemonize () == -1) { @@ -646,7 +678,6 @@ main (int argc, char **argv) optfree (opts); return FCE_FAILEDUPDATE; } - foreground = 0; mprintf_disabled = 1; } #endif diff --git a/m4/reorganization/libs/systemd.m4 b/m4/reorganization/libs/systemd.m4 new file mode 100644 index 000000000000..cac5d4272fda --- /dev/null +++ b/m4/reorganization/libs/systemd.m4 @@ -0,0 +1,18 @@ +dnl Check for systemd-daemon +PKG_CHECK_MODULES(SYSTEMD, [libsystemd-daemon], [AC_DEFINE([HAVE_SYSTEMD],,[systemd-daemon is supported])], [AC_MSG_RESULT([systemd-daemon is not supported])]) +CLAMD_LIBS="$CLAMD_LIBS $SYSTEMD_LIBS" +CFLAGS="$CFLAGS $SYSTEMD_CFLAGS" + +dnl Check for systemd system unit installation directory (see man 7 daemon) +AC_ARG_WITH([systemdsystemunitdir], AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]),, [with_systemdsystemunitdir=auto]) +AS_IF([test "x$with_systemdsystemunitdir" = "xyes" -o "x$with_systemdsystemunitdir" = "xauto"], [ + def_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd) + AS_IF([test "x$def_systemdsystemunitdir" = "x"], + [AS_IF([test "x$with_systemdsystemunitdir" = "xyes"], [AC_MSG_ERROR([systemd support requested but pkg-config unable to query systemd package])]) + with_systemdsystemunitdir=no], + [with_systemdsystemunitdir=$def_systemdsystemunitdir])]) +AS_IF([test "x$with_systemdsystemunitdir" != "xno"], + [AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])]) +AM_CONDITIONAL(INSTALL_SYSTEMD_UNITS, [test "x$with_systemdsystemunitdir" != "xno"]) +AC_MSG_RESULT([checking for systemd system unit installation directory... $with_systemdsystemunitdir]) + diff --git a/shared/misc.h b/shared/misc.h index 7f3a9f962d0d..3e093936caa1 100644 --- a/shared/misc.h +++ b/shared/misc.h @@ -37,6 +37,14 @@ # endif #endif +#ifdef HAVE_SYSTEMD +# include +#else +# define sd_listen_fds(u) 0 +# define SD_LISTEN_FDS_START 3 +# define sd_is_socket(f, a, s, l) 1 +#endif + #include #ifndef PATH_MAX diff --git a/shared/optparser.c b/shared/optparser.c index 63c88550d5b8..b61ec832a32f 100644 --- a/shared/optparser.c +++ b/shared/optparser.c @@ -272,7 +272,7 @@ const struct clam_option __clam_options[] = { { "AllowAllMatchScan", NULL, 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 1, NULL, 0, OPT_CLAMD, "Permit use of the ALLMATCHSCAN command.", "yes" }, - { "Foreground", NULL, 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM | OPT_MILTER, "Don't fork into background.", "no" }, + { "Foreground", "foreground", 'F', CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM | OPT_MILTER, "Don't fork into background.", "no" }, { "Debug", NULL, 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM, "Enable debug messages in libclamav.", "no" },