... | ... |
@@ -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 |