Prepare for 1.4.0 release.
[inoclam.git] / src / clam.cxx
blob3fab0c69ec466d81ffe6dfc28e50c32107b5a185
1 /*
2 * inoclam - Inotify+ClamAV virus scanner
3 * Copyright (C) 2007, 2008, 2009 Vermont Department of Taxes
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include <unistd.h>
20 #include <clamav.h>
21 #include <errno.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <libdaemon/dlog.h>
26 #include <pthread.h>
27 #include "clam.hxx"
28 #include "inoclam.hxx"
29 #include "monitor.hxx"
30 #include "signal.hxx"
31 #include "smtp.hxx"
32 #include "config.hxx"
34 #include <string>
36 /**
37 * Thread that reloads virus definitions as needed
39 void *clam_refresh(void *v)
41 unsigned int sigs;
42 int ret;
43 struct cl_stat dbstat;
45 clam *clamav;
46 clamav = (clam *) v;
48 memset(&dbstat, 0, sizeof(struct cl_stat));
49 cl_statinidir(cl_retdbdir(), &dbstat);
51 do {
52 if (cl_statchkdir(&dbstat) == 1) {
53 struct cl_engine *tmp_engine = NULL;
54 struct cl_engine *old_engine = NULL;
56 daemon_log(LOG_INFO, "Reloading new virus definitions");
58 /* TODO: make options configurable. */
59 /* For example: enable/disable CL_DB_NCORE, CL_DB_PHISHING_URLS, etc. */
62 /* prepare the detection engine */
63 tmp_engine = cl_engine_new();
64 if (NULL == tmp_engine) {
65 daemon_log(LOG_ERR, "cl_engine_new() error.");
66 continue;
69 /* Load virus definition files */
70 ret = cl_load(cl_retdbdir(), tmp_engine, &sigs, CL_DB_STDOPT);
71 if (CL_SUCCESS != ret) {
72 daemon_log(LOG_ERR, "cl_load() error: %s", cl_strerror(ret));
73 cl_engine_free(tmp_engine);
74 tmp_engine = NULL;
75 continue;
78 daemon_log(LOG_INFO, "Virus definitions loaded (%d signatures).", sigs);
80 ret = cl_engine_compile(tmp_engine);
81 if (CL_SUCCESS != ret) {
82 daemon_log(LOG_ERR, "cl_engine_compile() error: %s", cl_strerror(ret));
83 tmp_engine = NULL;
86 daemon_log(LOG_INFO, "Virus detection engine ready.");
88 /* Swap tmp_engine and engine, free resources from old engine */
89 pthread_mutex_lock(&(clamav->engine_lock));
90 old_engine = clamav->engine;
91 clamav->engine = tmp_engine;
92 tmp_engine = NULL;
93 daemon_log(LOG_INFO, "Virus detection engine ready.");
94 pthread_mutex_unlock(&(clamav->engine_lock));
96 cl_engine_free(old_engine);
97 old_engine = NULL;
99 cl_statfree(&dbstat);
100 memset(&dbstat, 0, sizeof(struct cl_stat));
101 cl_statinidir(cl_retdbdir(), &dbstat);
104 sleep(5);
105 } while (!exit_now);
107 cl_statfree(&dbstat);
109 monitor_dec();
110 clamav->refresh_thread_alive = false;
111 return NULL;
115 * Load the virus definition files and prepare the engine.
117 clam::clam()
119 unsigned int sigs = 0;
120 int ret;
122 pthread_t tt;
124 engine = NULL;
125 refresh_thread_alive = false;
127 memset(&engine_lock, '\0', sizeof(pthread_mutex_t));
128 pthread_mutex_init(&engine_lock, 0);
129 pthread_mutex_lock(&engine_lock);
131 /* Initialize libclamav */
132 ret = cl_init(CL_INIT_DEFAULT);
133 if (CL_SUCCESS != ret) {
134 pthread_mutex_unlock(&engine_lock);
135 daemon_log(LOG_ERR, "cl_init() error: %s", cl_strerror(ret));
136 engine = NULL;
137 return;
140 /* prepare the detection engine */
141 engine = cl_engine_new();
142 if (NULL == engine) {
143 pthread_mutex_unlock(&engine_lock);
144 daemon_log(LOG_ERR, "cl_engine_new() error.");
145 return;
148 /* Load virus definition files */
149 ret = cl_load(cl_retdbdir(), engine, &sigs, CL_DB_STDOPT);
150 if (CL_SUCCESS != ret) {
151 daemon_log(LOG_ERR, "cl_load() error: %s", cl_strerror(ret));
152 cl_engine_free(engine);
153 engine = NULL;
154 return;
157 daemon_log(LOG_INFO, "Virus definitions loaded (%d signatures).", sigs);
159 ret = cl_engine_compile(engine);
160 if (CL_SUCCESS != ret) {
161 daemon_log(LOG_ERR, "cl_engine_compile() error: %s", cl_strerror(ret));
162 cl_engine_free(engine);
163 engine = NULL;
164 return;
167 daemon_log(LOG_INFO, "Virus detection engine ready.");
168 pthread_mutex_unlock(&engine_lock);
170 refresh_thread_alive = true;
171 monitor_inc();
172 pthread_attr_init(&ta);
173 pthread_attr_setdetachstate(&ta, PTHREAD_CREATE_DETACHED);
174 ret = pthread_create(&tt, &ta, clam_refresh, (void *) this);
175 if (ret) {
176 refresh_thread_alive = false;
177 monitor_dec();
178 daemon_log(LOG_ERR, "Can't create clam_refresh thread: %s", strerror(errno));
183 * Scans a file for virus.
184 * @return -1 Error || 0 No Virus || +1 Virus Found
186 int clam::clam_scan(std::string * filename, config * conf)
188 int ret;
189 const char *virname;
191 pthread_mutex_lock(&engine_lock);
193 /* TODO: make options configurable. */
194 /* For example: enable/disable CL_SCAN_BLOCKENCRYPTED, CL_SCAN_BLOCKMAX, CL_SCAN_OLE2, etc. */
196 daemon_log(LOG_INFO, "About to scan '%s'", filename->c_str());
198 ret = cl_scanfile(filename->c_str(), &virname, NULL, engine, CL_SCAN_STDOPT);
199 if (CL_VIRUS == ret) {
200 int file_removed = 0;
202 pthread_mutex_unlock(&engine_lock);
203 daemon_log(LOG_INFO, "%s: %s FOUND", filename->c_str(), virname);
205 if (conf->getVirusRemovalEnabled() == cfg_true) {
206 int rc;
207 rc = unlink(filename->c_str());
208 if (rc == 0) {
209 file_removed = 1;
210 } else {
211 daemon_log(LOG_ERR, "unlink failed for '%s': %s", filename->c_str(), strerror(errno));
215 if (conf->getVirusEMailEnabled() == cfg_true) {
216 /* Sample Message:
217 * "File: <filename>\n"
218 * "Virus: <virname>\n"
219 * "Date: Thu, 28 Jun 2001 14:17:15 +0000\n"
220 * "Deleted: <Yes|No>\n"
221 * "\n"
222 * " (o_ Powered by: inoclam v1.0 (Bender)\n"
223 * " //\ Homepage: http://www.inoclam.org/\n"
224 * " V_/_ Author: Vermont Department of Taxes\n"
227 std::string * smtp_body;
228 smtp_body = new std::string();
229 smtp_body->append("File: ");
230 smtp_body->append(filename->c_str());
231 smtp_body->append("\n");
233 std::string * vname;
234 vname = new std::string(virname);
235 smtp_body->append("Virus: ");
236 smtp_body->append(vname->c_str());
237 smtp_body->append("\n");
238 delete vname;
240 std::string * tstamp;
241 tstamp = smtp_get_timestamp();
242 if (!tstamp) {
243 return -1;
245 smtp_body->append("Date: ");
246 smtp_body->append(tstamp->c_str());
247 smtp_body->append("\n");
248 delete tstamp;
250 smtp_body->append("Deleted: ");
252 std::string * rmstatus;
254 if (file_removed == 1) {
255 rmstatus = new std::string("Yes");
256 } else {
257 rmstatus = new std::string("No");
260 smtp_body->append(rmstatus->c_str());
261 delete rmstatus;
263 smtp_body->append("\n\n");
265 std::string * banner;
266 banner = smtp_get_banner();
267 smtp_body->append(banner->c_str());
268 delete banner;
270 smtp_send(conf->getVirusEMailSubject(), smtp_body, conf);
272 delete smtp_body; /* Clean up email string */
275 return 1;
276 } else if (CL_CLEAN == ret) {
277 pthread_mutex_unlock(&engine_lock);
278 daemon_log(LOG_INFO, "%s: OK", filename->c_str());
279 return 0;
280 } else {
281 pthread_mutex_unlock(&engine_lock);
282 daemon_log(LOG_ERR, "Scan Error: %s (%s)", cl_strerror(ret), filename->c_str());
283 return -1;
288 * Free resources used by the engine.
290 clam::~clam()
292 pthread_mutex_lock(&engine_lock);
294 if (engine) {
295 cl_engine_free(engine);
296 engine = NULL;
299 while (refresh_thread_alive) {
300 sched_yield();
301 sleep(3);
304 pthread_mutex_unlock(&engine_lock);
305 pthread_mutex_destroy(&engine_lock);
306 pthread_attr_destroy(&ta);