Skip to content

Commit 2bcdb42

Browse files
AdSchellevisfichtner
authored andcommitted
(insight) check database integrity before start, repair if broken
(cherry picked from commit 5ec2101) (cherry picked from commit b0aeb04)
1 parent 01d5095 commit 2bcdb42

File tree

2 files changed

+73
-1
lines changed

2 files changed

+73
-1
lines changed

src/opnsense/scripts/netflow/flowd_aggregate.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
Aggregate flowd data for reporting
2929
"""
3030
import time
31-
import datetime
3231
import os
3332
import sys
3433
import signal
@@ -37,6 +36,7 @@
3736
import syslog
3837
import traceback
3938
sys.path.insert(0, "/usr/local/opnsense/site-python")
39+
from sqlite3_helper import check_and_repair
4040
from lib.parse import parse_flow
4141
from lib.aggregate import AggMetadata
4242
import lib.aggregates
@@ -130,6 +130,9 @@ def run(self):
130130
""" run, endless loop, until sigterm is received
131131
:return: None
132132
"""
133+
# check database consistency / repair
134+
check_and_repair('/var/netflow/*.sqlite')
135+
133136
vacuum_interval = (60*60*8) # 8 hour vacuum cycle
134137
vacuum_countdown = None
135138
while self.running:
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
"""
2+
Copyright (c) 2016 Ad Schellevis
3+
All rights reserved.
4+
5+
Redistribution and use in source and binary forms, with or without
6+
modification, are permitted provided that the following conditions are met:
7+
8+
1. Redistributions of source code must retain the above copyright notice,
9+
this list of conditions and the following disclaimer.
10+
11+
2. Redistributions in binary form must reproduce the above copyright
12+
notice, this list of conditions and the following disclaimer in the
13+
documentation and/or other materials provided with the distribution.
14+
15+
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16+
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17+
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
18+
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
19+
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24+
POSSIBILITY OF SUCH DAMAGE.
25+
26+
--------------------------------------------------------------------------------------
27+
SQLite3 support functions
28+
"""
29+
import datetime
30+
import glob
31+
import sqlite3
32+
import syslog
33+
import os
34+
35+
def check_and_repair(filename_mask):
36+
""" check and repair sqlite databases
37+
:param filename_mask: filenames (glob pattern)
38+
:return: None
39+
"""
40+
for filename in glob.glob(filename_mask):
41+
try:
42+
conn = sqlite3.connect(filename, detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)
43+
cur = conn.cursor()
44+
cur.execute("SELECT name FROM sqlite_master where type = 'table'")
45+
except sqlite3.DatabaseError:
46+
# unrecoverable, doesn't look like a database, rename to .bck
47+
filename_tmp = '%s.%s.bck'%(filename, datetime.datetime.now().strftime("%Y%m%d%H%M%S"))
48+
syslog.syslog(syslog.LOG_ERR, "sqlite3 %s doesn't look like a database, renamed to %s " % (filename,
49+
filename_tmp))
50+
cur = None
51+
os.rename(filename, filename_tmp)
52+
53+
# try to vacuum all tables, triggers a "database disk image is malformed" when corrupted
54+
# force a repair when corrupted, using a dump / import
55+
if cur is not None:
56+
try:
57+
for table in cur.fetchall():
58+
cur.execute('vacuum %s' % table[0])
59+
except sqlite3.DatabaseError, e:
60+
if e.message.find('malformed') > -1:
61+
syslog.syslog(syslog.LOG_ERR, "sqlite3 repair %s" % filename)
62+
filename_tmp = '%s.fix'%filename
63+
if os.path.exists(filename_tmp):
64+
os.remove(filename_tmp)
65+
os.system('echo ".dump" | /usr/local/bin/sqlite3 %s | /usr/local/bin/sqlite3 %s' % (filename,
66+
filename_tmp))
67+
if os.path.exists(filename_tmp):
68+
os.remove(filename)
69+
os.rename(filename_tmp, filename)

0 commit comments

Comments
 (0)