Zoneminder is a great tool to build a surveillance system, combining all kinds of ip cameras in one dashboard and use it to manage recordings.
But sometimes Zoneminder can be a bit of a pain, especially when the disk is getting filled. With the high resolutions of todays IP cameras this can happen pretty quickly. Although Zoneminder has an internal "filter" to automatically purge old events when the disk threshold hits a certain limit.
However this filter only works if there's actually still some space left available. The filter searches the oldest N events in the database, deleting them from the database and also on the filesystem. But when there's no disk space available at all, the database is likely to be frozen/unavailable. Ergo no clean up anymore. And in this situation you're stuck with a non-working Zoneminder.
This happened to me twice already on my Zoneminder installation (side note: I have to admit my current disk size of 120GB dedicated for Zoneminder is rather small) so I built a clean up script and this is what this post is about.
When you archive a footage, this usually means you want to keep it. The cleanup script needs to respect that. But it needs to know about the archived events first. This can be done by getting the relevant information from the database:
root@zoneminder:~# mysql -N -b -r -e "select Id from zm.Events where Archived = 1;"
+--------+
| 135933 |
| 136590 |
| 154831 |
| 160832 |
| 162647 |
| 162649 |
| 167562 |
+--------+
We can use the find command and the ID's from step one to find all events except the archived ones:
root@zoneminder:~# find /var/cache/zoneminder/events/ -mindepth 2 -type l ! -name ".135933" ! -name ".136590" ! -name ".154831" ! -name ".160832" ! -name ".162647" ! -name ".162649" ! -name ".167562" -exec ls -la {} + > /tmp/xxx
find will now look in the path /var/cache/zoneminder/events/ for symlinks (type l), except for the given names (excluded with exclamation mark). The output of the full path and other information will be saved in /tmp/xxx.
The output file /tmp/xxx will now look something like that:
root@zoneminder:~# tail /tmp/xxx
lrwxrwxrwx 1 www-data www-data 8 Dec 14 06:45 /var/cache/zoneminder/events/5/18/12/14/.448512 -> 06/45/12
lrwxrwxrwx 1 www-data www-data 8 Dec 14 06:51 /var/cache/zoneminder/events/5/18/12/14/.448517 -> 06/51/29
lrwxrwxrwx 1 www-data www-data 8 Dec 14 06:51 /var/cache/zoneminder/events/5/18/12/14/.448518 -> 06/51/34
lrwxrwxrwx 1 www-data www-data 8 Dec 14 07:02 /var/cache/zoneminder/events/5/18/12/14/.448533 -> 07/02/28
lrwxrwxrwx 1 www-data www-data 8 Dec 14 07:44 /var/cache/zoneminder/events/5/18/12/14/.448546 -> 07/44/56
lrwxrwxrwx 1 www-data www-data 8 Dec 14 07:47 /var/cache/zoneminder/events/5/18/12/14/.448548 -> 07/47/09
lrwxrwxrwx 1 www-data www-data 8 Dec 14 08:22 /var/cache/zoneminder/events/5/18/12/14/.448551 -> 08/22/17
lrwxrwxrwx 1 www-data www-data 8 Dec 14 08:26 /var/cache/zoneminder/events/5/18/12/14/.448552 -> 08/26/13
lrwxrwxrwx 1 www-data www-data 8 Dec 14 08:27 /var/cache/zoneminder/events/5/18/12/14/.448555 -> 08/27/30
lrwxrwxrwx 1 www-data www-data 8 Dec 14 08:28 /var/cache/zoneminder/events/5/18/12/14/.448557 -> 08/28/19
Each path in /tmp/xxx contains two important information: The event id and the real path.
/var/cache/zoneminder/events/5/18/12/14/.448512 -> 06/45/12
In this case .448512 is the symlink of the event pointing to the subfolders 06/45/12.
The name of the symlink also contains the event id (448512).
By removing the symlink and adding the subfolders into the path, we get the real path where the footage is stored:
/var/cache/zoneminder/events/5/18/12/14/06/45/12
Now that the real path is known, it can be deleted:
root@zoneminder:~# rm -rf /var/cache/zoneminder/events/5/18/12/14/06/45/12
We should also delete the symlink:
root@zoneminder:~# rm -f /var/cache/zoneminder/events/5/18/12/14/.448512
And now that there's some disk space available again, the MySQL database should accept writes again. Therefore we can delete this event (448512) from the relevant tables:
mysql> DELETE FROM zm.Events where Id = 448512;
mysql> DELETE FROM zm.Frames where EventId = 448512;
mysql> DELETE FROM zm.Stats where EventId = 448512;
As I mentioned at the beginning, I wrote a script to automate these tasks. It's called zoneminder-event-cleanup.sh and you can download it.
Using the script is very simple.
1) Download it
# wget https://www.claudiokuenzler.com/downloads/scripts/zoneminder-event-cleanup.sh
2) Give execute permissions:
# chmod 755 zoneminder-event-cleanup.sh
3) Open the script with an editor and adjust the user variables:
# User variables
olderthan=2 # Defines the minimum age in days of the events to be deleted
zmcache=/var/cache/zoneminder/events # Defines the path where zm stores events
mysqlhost=localhost # Defines the MySQL host for the zm database
mysqldb=zm # Defines the MySQL database name used by zm
mysqluser=zmuser # Defines a MySQL user to connect to the database
mysqlpass=secret # Defines the password for the MySQL user
root@zoneminder:~# ./zoneminder-cleanup.sh
Deleting 447900
Deleting 447901
Deleting 447902
Deleting 447903
Deleting 447904
Deleting 447905
Deleting 447906
Deleting 447907
Deleting 447908
Deleting 447909
Deleting 447911
Deleting 447912
Deleting 447913
Deleting 447914
Deleting 447915
The script is also available on Github.
Updated October 17th 2020
After Zoneminder 1.24, the symbolic links in /var/cache/zoneminder are gone. These are now real folders looking like this:
root@zm:~# ls -la /var/cache/zoneminder/events/4/2020-10-15
total 396
drwxr-xr-x 27 www-data www-data 36864 Oct 17 13:02 .
drwxr-xr-x 5 www-data www-data 4096 Oct 17 02:13 ..
drwxr-xr-x 2 www-data www-data 12288 Oct 17 12:58 371771
drwxr-xr-x 2 www-data www-data 12288 Oct 17 12:58 371772
drwxr-xr-x 2 www-data www-data 12288 Oct 17 12:58 371773
drwxr-xr-x 2 www-data www-data 12288 Oct 17 12:58 371774
drwxr-xr-x 2 www-data www-data 12288 Oct 17 12:58 371776
drwxr-xr-x 2 www-data www-data 12288 Oct 17 12:58 371777
drwxr-xr-x 2 www-data www-data 24576 Oct 17 12:58 371778
drwxr-xr-x 2 www-data www-data 36864 Oct 17 12:58 371779
drwxr-xr-x 2 www-data www-data 12288 Oct 17 12:58 371782
drwxr-xr-x 2 www-data www-data 12288 Oct 17 12:58 371783
drwxr-xr-x 2 www-data www-data 12288 Oct 17 12:58 371784
drwxr-xr-x 2 www-data www-data 4096 Oct 17 12:58 371785
drwxr-xr-x 2 www-data www-data 20480 Oct 17 12:58 371786
drwxr-xr-x 2 www-data www-data 12288 Oct 17 12:58 371787
drwxr-xr-x 2 www-data www-data 12288 Oct 17 12:58 371788
drwxr-xr-x 2 www-data www-data 20480 Oct 17 12:58 371789
drwxr-xr-x 2 www-data www-data 12288 Oct 17 12:58 371790
drwxr-xr-x 2 www-data www-data 12288 Oct 17 12:58 371791
drwxr-xr-x 2 www-data www-data 4096 Oct 17 12:58 371792
drwxr-xr-x 2 www-data www-data 12288 Oct 17 12:58 371793
drwxr-xr-x 2 www-data www-data 12288 Oct 17 12:58 371794
drwxr-xr-x 2 www-data www-data 20480 Oct 17 12:58 371795
drwxr-xr-x 2 www-data www-data 12288 Oct 17 12:58 371796
drwxr-xr-x 2 www-data www-data 12288 Oct 17 12:58 371797
drwxr-xr-x 2 www-data www-data 20480 Oct 17 12:58 371798
The cleanup script was modified accordingly to cover this. In the first few lines of the script there is the following variable:
root@zm:~/scripts# grep "^zm_linked" zoneminder-event-cleanup.sh
zm_linked_version="" # set "true" if using zoneminder pre 1.24 (old version with symlinks instead directories by events)
If you're still using Zoneminder 1.24 and older, set this variable to true. For newer Zoneminder releases, this variable can be left as is.
Download the new version of the script directly from GitHub.
ck from Switzerland wrote on Aug 7th, 2020:
Thanks for your contribution Brawn1! I have a couple of remarks before merging the changes. Let us do this on Github in the repo.
Brawn1 from Austria wrote on Aug 6th, 2020:
Thanks for the great Article and Script.
I have modified the script to use it with new Zoneminder 1.24+ and event folder instead of links.
ck from Switzerland wrote on Sep 4th, 2019:
Thanks for that hint, Elliot! As my Zoneminder is currently still running 1.29, I cannot very this. Could you however open an "issue" in the public GitHub repo: https://github.com/Napsty/scripts/issues. When I have time, I will look at it.
Elliot from wrote on Sep 4th, 2019:
I took a look at results of just using the commands, and it appears that the version of zoneminder I'm using (v1.32.3) doesn't use symbolic links, instead the events point directly at the associated directory (e.g. event 3270 = /var/cache/zoneminder/events/1/2019-08-28/3270/
and so the "find" line doesn't work as intended.
AWS Android Ansible Apache Apple Atlassian BSD Backup Bash Bluecoat CMS Chef Cloud Coding Consul Containers CouchDB DB DNS Database Databases Docker ELK Elasticsearch Filebeat FreeBSD Galera Git GlusterFS Grafana Graphics HAProxy HTML Hacks Hardware Icinga Influx Internet Java KVM Kibana Kodi Kubernetes LVM LXC Linux Logstash Mac Macintosh Mail MariaDB Minio MongoDB Monitoring Multimedia MySQL NFS Nagios Network Nginx OSSEC OTRS Office OpenSearch PGSQL PHP Perl Personal PostgreSQL Postgres PowerDNS Proxmox Proxy Python Rancher Rant Redis Roundcube SSL Samba Seafile Security Shell SmartOS Solaris Surveillance Systemd TLS Tomcat Ubuntu Unix VMWare VMware Varnish Virtualization Windows Wireless Wordpress Wyse ZFS Zoneminder