Browse code

freshclam: load database in subprocess (bb #2147).

Török Edvin authored on 2010/11/05 03:08:58
Showing 3 changed files
... ...
@@ -1,3 +1,7 @@
1
+Thu Nov  4 19:47:17 EET 2010 (edwin)
2
+------------------------------------
3
+ * freshclam: load database in subprocess (bb #2147).
4
+
1 5
 Wed Nov  3 13:38:47 CET 2010 (tk)
2 6
 ---------------------------------
3 7
  * clamd: add new commands DETSTATS and DETSTATSCLEAR (part of bb#2312)
... ...
@@ -64,17 +64,25 @@ extern int active_children;
64 64
 
65 65
 static short foreground = 1;
66 66
 char updtmpdir[512];
67
+int sigchld_wait = 1;
67 68
 
68 69
 static void sighandler(int sig) {
69 70
 
70 71
     switch(sig) {
71 72
 #ifdef	SIGCHLD
72 73
 	case SIGCHLD:
73
-	    waitpid(-1, NULL, WNOHANG);
74
+	    if (sigchld_wait)
75
+		waitpid(-1, NULL, WNOHANG);
74 76
 	    active_children--;
75 77
 	    break;
76 78
 #endif
77 79
 
80
+#ifdef SIGPIPE
81
+	case SIGPIPE:
82
+	    /* no action, app will get EPIPE */
83
+	    break;
84
+#endif
85
+
78 86
 #ifdef	SIGALRM
79 87
 	case SIGALRM:
80 88
 		terminate = -1;
... ...
@@ -396,6 +404,7 @@ int main(int argc, char **argv)
396 396
     memset(&sigact, 0, sizeof(struct sigaction));
397 397
     sigact.sa_handler = sighandler;
398 398
     sigaction(SIGINT, &sigact, NULL);
399
+    sigaction(SIGPIPE, &sigact, NULL);
399 400
 #endif
400 401
     if(optget(opts, "daemon")->enabled) {
401 402
 	    int bigsleep, checks;
... ...
@@ -1519,11 +1519,128 @@ static int buildcld(const char *tmpdir, const char *dbname, const char *newfile,
1519 1519
     return 0;
1520 1520
 }
1521 1521
 
1522
+static int test_database(const char *newfile, const char *newdb, int bytecode)
1523
+{
1524
+    struct cl_engine *engine;
1525
+    unsigned newsigs = 0;
1526
+    int ret;
1527
+
1528
+    logg("*Loading signatures from %s\n", newdb);
1529
+    if(!(engine = cl_engine_new())) {
1530
+	return 55;
1531
+    }
1532
+
1533
+    if((ret = cl_load(newfile, engine, &newsigs, CL_DB_PHISHING | CL_DB_PHISHING_URLS | CL_DB_BYTECODE | CL_DB_PUA)) != CL_SUCCESS) {
1534
+	logg("!Failed to load new database: %s\n", cl_strerror(ret));
1535
+	cl_engine_free(engine);
1536
+	return 55;
1537
+    }
1538
+    if(bytecode && (ret = cli_bytecode_prepare2(engine, &engine->bcs, engine->dconf->bytecode/*FIXME: dconf has no sense here*/))) {
1539
+	logg("!Failed to compile/load bytecode: %s\n", cl_strerror(ret));
1540
+	cl_engine_free(engine);
1541
+	return 55;
1542
+    }
1543
+    logg("*Properly loaded %u signatures from new %s\n", newsigs, newdb);
1544
+    if(engine->domainlist_matcher && engine->domainlist_matcher->sha256_pfx_set.keys)
1545
+	cli_hashset_destroy(&engine->domainlist_matcher->sha256_pfx_set);
1546
+    cl_engine_free(engine);
1547
+    return 0;
1548
+}
1549
+
1550
+#ifndef WIN32
1551
+static int test_database_wrap(const char *file, const char *newdb, int bytecode)
1552
+{
1553
+    char firstline[256];
1554
+    char lastline[256];
1555
+    int pipefd[2];
1556
+    pid_t pid;
1557
+    int status = 0;
1558
+    FILE *f;
1559
+
1560
+    if (pipe(pipefd) == -1) {
1561
+	logg("^pipe() failed: %s\n", strerror(errno));
1562
+	return test_database(file, newdb, bytecode);
1563
+    }
1564
+
1565
+    switch ( pid = fork() ) {
1566
+	case 0:
1567
+	    close(pipefd[0]);
1568
+	    dup2(pipefd[1], 2);
1569
+	    exit(test_database(file, newdb, bytecode));
1570
+	case -1:
1571
+	    close(pipefd[0]);
1572
+	    close(pipefd[1]);
1573
+	    logg("^fork() failed: %s\n", strerror(errno));
1574
+	    return test_database(file, newdb, bytecode);
1575
+	default:
1576
+	    /* read first / last line printed by child*/
1577
+	    close(pipefd[1]);
1578
+	    f = fdopen(pipefd[0], "r");
1579
+	    firstline[0] = 0;
1580
+	    lastline[0] = 0;
1581
+	    do {
1582
+		fgets(firstline, sizeof(firstline), f);
1583
+		/* ignore warning messages, otherwise the outdated warning will
1584
+		 * make us miss the important part of the error message */
1585
+	    } while (!strncmp(firstline, "LibClamAV Warning:", 18));
1586
+	    /* must read entire output, child doesn't like EPIPE */
1587
+	    while (fgets(lastline, sizeof(firstline), f)) {
1588
+		/* print the full output only when LogVerbose or -v is given */
1589
+		logg("*%s", lastline);
1590
+	    }
1591
+	    fclose(f);
1592
+
1593
+	    if (waitpid(pid, &status, 0) == -1 && errno != ECHILD)
1594
+		logg("^waitpid() failed: %s\n", strerror(errno));
1595
+	    cli_chomp(firstline);
1596
+	    cli_chomp(lastline);
1597
+	    if (firstline[0]) {
1598
+		logg("!During database load : %s%s%s\n",
1599
+		     firstline, lastline[0] ? " [...] " : "",
1600
+		     lastline);
1601
+	    }
1602
+	    if (WIFEXITED(status)) {
1603
+		int ret = WEXITSTATUS(status);
1604
+		if (ret) {
1605
+		    logg("^Database load exited with status %d\n", ret);
1606
+		    return ret;
1607
+		}
1608
+		if (firstline[0])
1609
+		    logg("^Database successfully loaded, but there is stderr output\n");
1610
+		return 0;
1611
+	    }
1612
+	    if (WIFSIGNALED(status)) {
1613
+		logg("!Database load killed by signal %d\n", WTERMSIG(status));
1614
+		return 55;
1615
+	    }
1616
+	    logg("^Unknown status from wait: %d\n", status);
1617
+	    return 55;
1618
+    }
1619
+}
1620
+#else
1621
+static int test_database_wrap(const char *file, const char *newdb, int bytecode)
1622
+{
1623
+    int ret = 55;
1624
+    __try
1625
+    {
1626
+	ret = test_database(file, newdb, bytecode);
1627
+    }
1628
+    __finally {
1629
+	if (AbnormalTermination())
1630
+	    logg("!Exception during database testing, code %08x at %08x\n",
1631
+		 GetExceptionCode(), GetExceptionInformation()->ExceptionRecord->ExceptionAddress);
1632
+    }
1633
+    return ret;
1634
+}
1635
+#endif
1636
+
1637
+extern int sigchld_wait;
1638
+
1522 1639
 static int updatedb(const char *dbname, const char *hostname, char *ip, int *signo, const struct optstruct *opts, const char *dnsreply, char *localip, int outdated, struct mirdat *mdat, int logerr, int extra)
1523 1640
 {
1524 1641
 	struct cl_cvd *current, *remote;
1525 1642
 	const struct optstruct *opt;
1526
-	unsigned int nodb = 0, currver = 0, newver = 0, port = 0, i, j, newsigs = 0;
1643
+	unsigned int nodb = 0, currver = 0, newver = 0, port = 0, i, j;
1527 1644
 	int ret, ims = -1;
1528 1645
 	char *pt, cvdfile[32], localname[32], *tmpdir = NULL, *newfile, *newfile2, newdb[32];
1529 1646
 	char extradbinfo[64], *extradnsreply = NULL;
... ...
@@ -1531,7 +1648,6 @@ static int updatedb(const char *dbname, const char *hostname, char *ip, int *sig
1531 1531
 	unsigned int flevel = cl_retflevel(), remote_flevel = 0, maxattempts;
1532 1532
 	unsigned int can_whitelist = 0;
1533 1533
 	int ctimeout, rtimeout;
1534
-	struct cl_engine *engine;
1535 1534
 
1536 1535
 
1537 1536
     snprintf(cvdfile, sizeof(cvdfile), "%s.cvd", dbname);
... ...
@@ -1775,16 +1891,11 @@ static int updatedb(const char *dbname, const char *hostname, char *ip, int *sig
1775 1775
     }
1776 1776
 
1777 1777
     if(optget(opts, "TestDatabases")->enabled && strlen(newfile) > 4) {
1778
-	if(!(engine = cl_engine_new())) {
1779
-	    unlink(newfile);
1780
-	    free(newfile);
1781
-	    return 55;
1782
-	}
1783 1778
 	newfile2 = strdup(newfile);
1784 1779
 	if(!newfile2) {
1780
+	    logg("!Can't allocate memory for filename!\n");
1785 1781
 	    unlink(newfile);
1786 1782
 	    free(newfile);
1787
-	    cl_engine_free(engine);
1788 1783
 	    return 55;
1789 1784
 	}
1790 1785
 	newfile2[strlen(newfile2) - 4] = '.';
... ...
@@ -1796,29 +1907,18 @@ static int updatedb(const char *dbname, const char *hostname, char *ip, int *sig
1796 1796
 	    unlink(newfile);
1797 1797
 	    free(newfile);
1798 1798
 	    free(newfile2);
1799
-	    cl_engine_free(engine);
1800 1799
 	    return 57;
1801 1800
 	}
1802 1801
 	free(newfile);
1803 1802
 	newfile = newfile2;
1804
-	if((ret = cl_load(newfile, engine, &newsigs, CL_DB_PHISHING | CL_DB_PHISHING_URLS | CL_DB_BYTECODE | CL_DB_PUA)) != CL_SUCCESS) {
1803
+	sigchld_wait = 0;/* we need to wait() for the child ourselves */
1804
+	if (test_database_wrap(newfile, newdb, optget(opts, "Bytecode")->enabled)) {
1805 1805
 	    logg("!Failed to load new database: %s\n", cl_strerror(ret));
1806 1806
 	    unlink(newfile);
1807 1807
 	    free(newfile);
1808
-	    cl_engine_free(engine);
1809 1808
 	    return 55;
1810 1809
 	}
1811
-	if(optget(opts, "Bytecode")->enabled && (ret = cli_bytecode_prepare2(engine, &engine->bcs, engine->dconf->bytecode/*FIXME: dconf has no sense here*/))) {
1812
-	    logg("!Failed to compile/load bytecode: %s\n", cl_strerror(ret));
1813
-	    unlink(newfile);
1814
-	    free(newfile);
1815
-	    cl_engine_free(engine);
1816
-	    return 55;
1817
-	}
1818
-	logg("*Properly loaded %u signatures from new %s\n", newsigs, newdb);
1819
-	if(engine->domainlist_matcher && engine->domainlist_matcher->sha256_pfx_set.keys)
1820
-	    cli_hashset_destroy(&engine->domainlist_matcher->sha256_pfx_set);
1821
-	cl_engine_free(engine);
1810
+	sigchld_wait = 1;
1822 1811
     }
1823 1812
 
1824 1813
 #ifdef _WIN32
... ...
@@ -1872,7 +1972,6 @@ static int updatecustomdb(const char *url, int *signo, const struct optstruct *o
1872 1872
 	char *pt, *host, urlcpy[256], *newfile = NULL, mtime[36], *newfile2;
1873 1873
 	const char *proxy = NULL, *user = NULL, *pass = NULL, *uas = NULL, *rpath, *dbname;
1874 1874
 	int ctimeout, rtimeout;
1875
-	struct cl_engine *engine;
1876 1875
 	struct stat sb;
1877 1876
 	struct cl_cvd *cvd;
1878 1877
 
... ...
@@ -1982,29 +2081,14 @@ static int updatecustomdb(const char *url, int *signo, const struct optstruct *o
1982 1982
 	}
1983 1983
 	free(newfile);
1984 1984
 	newfile = newfile2;
1985
-	if(!(engine = cl_engine_new())) {
1986
-	    unlink(newfile);
1987
-	    free(newfile);
1988
-	    return 55;
1989
-	}
1990
-	if((ret = cl_load(newfile, engine, &newsigs, CL_DB_PHISHING | CL_DB_PHISHING_URLS | CL_DB_BYTECODE | CL_DB_PUA)) != CL_SUCCESS) {
1985
+	sigchld_wait = 0;/* we need to wait() for the child ourselves */
1986
+	if (test_database_wrap(newfile, dbname, optget(opts, "Bytecode")->enabled)) {
1991 1987
 	    logg("!Failed to load new database: %s\n", cl_strerror(ret));
1992 1988
 	    unlink(newfile);
1993 1989
 	    free(newfile);
1994
-	    cl_engine_free(engine);
1995
-	    return 55;
1996
-	}
1997
-	if(optget(opts, "Bytecode")->enabled && (ret = cli_bytecode_prepare2(engine, &engine->bcs, engine->dconf->bytecode/*FIXME: dconf has no sense here*/))) {
1998
-	    logg("!Failed to compile/load bytecode: %s\n", cl_strerror(ret));
1999
-	    unlink(newfile);
2000
-	    free(newfile);
2001
-	    cl_engine_free(engine);
2002 1990
 	    return 55;
2003 1991
 	}
2004
-	logg("*Properly loaded %u signatures from new (custom) %s\n", newsigs, dbname);
2005
-	if(engine->domainlist_matcher && engine->domainlist_matcher->sha256_pfx_set.keys)
2006
-	    cli_hashset_destroy(&engine->domainlist_matcher->sha256_pfx_set);
2007
-	cl_engine_free(engine);
1992
+	sigchld_wait = 1;
2008 1993
     }
2009 1994
 
2010 1995
 #ifdef _WIN32