{"id":26644,"date":"2018-06-19T14:01:45","date_gmt":"2018-06-19T14:01:45","guid":{"rendered":"http:\/\/www.sickgaming.net\/blog\/?p=26644"},"modified":"2018-06-19T14:01:45","modified_gmt":"2018-06-19T14:01:45","slug":"systemd-services-monitoring-files-and-directories","status":"publish","type":"post","link":"https:\/\/sickgaming.net\/blog\/2018\/06\/19\/systemd-services-monitoring-files-and-directories\/","title":{"rendered":"Systemd Services: Monitoring Files and Directories"},"content":{"rendered":"<div><img decoding=\"async\" src=\"http:\/\/www.sickgaming.net\/blog\/wp-content\/uploads\/2018\/06\/systemd-services-monitoring-files-and-directories.png\" class=\"ff-og-image-inserted\" \/><\/div>\n<p>So far in this systemd multi-part tutorial, we\u2019ve covered <a href=\"https:\/\/www.linux.com\/blog\/learn\/intro-to-linux\/2018\/5\/writing-systemd-services-fun-and-profit\">how to start and stop a service by hand<\/a>, <a href=\"https:\/\/www.linux.com\/blog\/learn\/2018\/5\/systemd-services-beyond-starting-and-stopping\">how to start a service when booting your OS and have it stop on power down<\/a>, and <a href=\"https:\/\/www.linux.com\/blog\/intro-to-linux\/2018\/6\/systemd-services-reacting-change\">how to boot a service when a certain device is detected<\/a>. This installment does something different yet again and covers how to create a unit that starts a service when something changes in the filesystem. For the practical example, you&#8217;ll see how you can use one of these units to extend the <a href=\"https:\/\/www.linux.com\/blog\/learn\/intro-to-linux\/2018\/6\/systemd-services-monitoring-files-and-directories\">surveillance system we talked about last time<\/a>.<\/p>\n<h3>Where we left off<\/h3>\n<p><a href=\"https:\/\/www.linux.com\/blog\/intro-to-linux\/2018\/6\/systemd-services-reacting-change\">Last time we saw how the surveillance system took pictures, but it did nothing with them<\/a>. In fact, it even overwrote the last picture it took when it detected movement so as not to fill the storage of the device.<\/p>\n<p>Does that mean the system is useless? Not by a long shot. Because, you see, systemd offers yet another type of units, <i>paths<\/i>, that can help you out. <i>Path<\/i> units allow you to trigger a service when an event happens in the filesystem, say, when a file gets deleted or a directory accessed. And, overwriting an image is exactly the kind of event we are talking about here.<\/p>\n<h3>Anatomy of a Path Unit<\/h3>\n<p>A systemd path unit takes the extension <i>.path<\/i>, and it monitors a file or directory. A <i>.path<\/i> unit calls another unit (usually a <i>.service<\/i> unit with the same name) when something happens to the monitored file or directory. For example, if you have a <i>picchanged.path<\/i> unit to monitor the snapshot from your webcam, you will also have a <i>picchanged.service<\/i> that will execute a script when the snapshot is overwritten.<\/p>\n<p>Path units contain a new section, <code>[Path]<\/code>, with few more directives. First, you have the what-to-watch-for directives:<\/p>\n<ul>\n<li><strong><code>PathExists=<\/code><\/strong> monitors whether the file or directory exists. If it does, the associated unit gets triggered. <code>PathExistsGlob=<\/code> works in a similar fashion, but lets you use globbing, like when you use <code>ls *.jpg<\/code> to search for all the JPEG images in a directory. This lets you check, for example, whether a file with a certain extension exists.<\/li>\n<li><strong><code>PathChanged=<\/code><\/strong> watches a file or directory and activates the configured unit whenever it changes. It is not activated on every write to the watched file but only when a monitored file open for for writing is changed and then closed. The associated unit is executed when the file is closed.<\/li>\n<li><strong><code>PathModified=<\/code><\/strong>, on the other hand, does activate the unit when anything is changed in the file you are monitoring, even before you close the file.<\/li>\n<li><strong><code>DirectoryNotEmpty=<\/code><\/strong> does what it says on the box, that is, it activates the associated unit if the monitored directory contains files or subdirectories.<\/li>\n<\/ul>\n<p>Then, we have <code>Unit=<\/code> that tells the <i>.path<\/i> which <i>.service<\/i> unit to activate, in case you want to give it a different name to that of your <i>.path<\/i> unit; <code>MakeDirectory=<\/code> can be <code>true<\/code> or <code>false<\/code> (or <code>0<\/code> or <code>1<\/code>, or <code>yes<\/code> or <code>no<\/code>) and creates the directory you want to monitor before monitoring starts. Obviously, using <code>MakeDirectory=<\/code> in combination with <code>PathExists=<\/code> does not make sense. However, <code>MakeDirectory=<\/code> can be used in combination with <code>DirectoryMode=<\/code>, which you use to set the the mode (permissions) of the new directory. If you don&#8217;t use <code>DirectoryMode=<\/code>, the default permissions for the new directory are <code>0755<\/code>.<\/p>\n<h3>Building <i>picchanged.path<\/i><\/h3>\n<p>All these directives are very useful, but you will be just looking for changes made to one single file, so your <i>.path<\/i> unit is very simple:<\/p>\n<pre>\n#picchanged.path\n[Unit] Wants= webcam.service [Path] PathChanged= \/home\/[<i>user name<\/i>]\/monitor\/monitor.jpg <\/pre>\n<p>In the <code>Unit=<\/code> section the line that says<\/p>\n<pre>\nWants= webcam.service <\/pre>\n<p>The <code>Wants=<\/code> directive is the preferred way of starting up a unit the current unit needs to work properly. <a href=\"https:\/\/www.linux.com\/blog\/intro-to-linux\/2018\/6\/systemd-services-reacting-change\"><code>webcam.service<\/code> is the name you gave the surveillance service that you saw in the previous article<\/a> and is the service that actually controls the webcam and makes it take a snap every half second. This means it\u2019s <i>picchanged.path<\/i> that is going to start up <i>webcam.service<\/i> now, and not the <a href=\"https:\/\/www.linux.com\/blog\/intro-to-linux\/2018\/6\/systemd-services-reacting-change\">Udev rule you saw in the prior article<\/a>. You will use the Udev rule to start <i>picchanged.path<\/i> instead.<\/p>\n<p>To summarize: the Udev rule pulls in your new <i>picchanged.path<\/i> unit, which, in turn pulls in the <i>webcam.service<\/i> as a requirement for everything to work perfectly.<\/p>\n<p>The &#8220;thing&#8221; that <i>picchanged.path<\/i> monitors is the <i>monitor.jpg<\/i> file in the <i>monitor\/<\/i> directory in your home directory. As you saw last time, <i>webcam.service<\/i> called a script, <i>checkimage.sh<\/i>, took a picture at the beginning of its execution and stored it in <i>monitor\/temp.jpg<\/i>. <i>checkimage.sh<\/i> then took another pic, <i>temp.jpg<\/i>, and compared it with <i>monitor.jpg<\/i>. If it found significant differences (like when somebody walks into frame) the script overwrote <i>monitor.jpg<\/i> with the <i>temp.jpg<\/i>. That is when <i>picchanged.path<\/i> fires.<\/p>\n<p>As you haven&#8217;t included a <code>Unit=<\/code> directive in your <i>.path<\/i>, the unit systemd expects a matching <i>picchanged.service<\/i> unit which it will trigger when <i>\/home\/[<i>user name<\/i>]\/monitor\/monitor.jpg<\/i> gets modified:<\/p>\n<pre>\n#picchanged.service\n[Service] Type= simple ExecStart= \/home\/[<i>user name<\/i>]\/bin\/picmonitor.sh\n<\/pre>\n<p>For the time being, let\u2019s make <i>picmonitor.sh<\/i> save a time-stamped copy of <i>monitor.jpg<\/i> every time changes get detected:<\/p>\n<pre>\n#!\/bin\/bash\n# This is the pcmonitor.sh script cp \/home\/[<i>user name<\/i>]\/monitor\/monitor.jpg \/home\/[<i>user name<\/i>]\/monitor\/\"`date`.jpg\"\n<\/pre>\n<h3>Udev Changes<\/h3>\n<p>You have to change the custom Udev rule you wrote in <a href=\"https:\/\/www.linux.com\/blog\/intro-to-linux\/2018\/6\/systemd-services-reacting-change\">the previous installment<\/a> so everything works. Edit <i>\/etc\/udev\/rules.d\/01-webcam.rules<\/i> so instead of looking like this:<\/p>\n<pre>\nACTION==\"add\", SUBSYSTEM==\"video4linux\", ATTRS{idVendor}==\"03f0\", \u00a0 ATTRS{idProduct}==\"e207\", SYMLINK+=\"mywebcam\", TAG+=\"systemd\", \u00a0 MODE=\"0666\", ENV{SYSTEMD_WANTS}=\"webcam.service\"\n<\/pre>\n<p>It looks like this:<\/p>\n<pre>\nACTION==\"add\", SUBSYSTEM==\"video4linux\", ATTRS{idVendor}==\"03f0\", \u00a0 ATTRS{idProduct}==\"e207\", SYMLINK+=\"mywebcam\", TAG+=\"systemd\", \u00a0 MODE=\"0666\", ENV{SYSTEMD_WANTS}=\"picchanged.path\"\n<\/pre>\n<p>The new rule, instead of calling <i>webcam.service<\/i>, now calls <i>picchanged.path<\/i> when your webcam gets detected. (Note that you will have to change the <code>idVendor<\/code> and <code>IdProduct<\/code> to those of your own webcam &#8212; you saw how to find these out previously).<\/p>\n<p>For the record, I also changed <i>checkimage.sh<\/i> from using PNG to JPEG images. I did this because I found some dependency problems with PNG images when working with <i>mplayer<\/i> on some versions of Debian. <i>checkimage.sh<\/i> now looks like this:<\/p>\n<pre>\n#!\/bin\/bash mplayer -vo jpeg -frames 1 tv:\/\/ -tv driver=v4l2:width=640:height=480:device=\n\u00a0 \/dev\/mywebcam &amp;&gt;\/dev\/null mv 00000001.jpg \/home\/paul\/monitor\/monitor.jpg while true do mplayer -vo jpeg -frames 1 tv:\/\/ -tv driver=v4l2:width=640:height=480:device=\n\u00a0 \/dev\/mywebcam &amp;&gt;\/dev\/null mv 00000001.jpg \/home\/paul\/monitor\/temp.jpg imagediff=`compare -metric mae \/home\/paul\/monitor\/monitor.jpg \u00a0 \/home\/paul\/monitor\/temp.jpg \/home\/paul\/monitor\/diff.png 2&gt;&amp;1 &gt; \u00a0 \/dev\/null | cut -f 1 -d \" \"` if [ `echo \"$imagediff &gt; 700.0\" | bc` -eq 1 ] then mv \/home\/paul\/monitor\/temp.jpg \/home\/paul\/monitor\/monitor.jpg fi sleep 0.5 done\n<\/pre>\n<h3>Firing up<\/h3>\n<p>This is a multi-unit service that, when all its bits and pieces are in place, you don&#8217;t have to worry much about: you plug in the designated webcam (or boot the machine with the webcam already connected), <i>picchanged.path<\/i> gets started thanks to the Udev rule and takes over, bringing up the <i>webcam.service<\/i> and starting to check on the snaps. There is nothing else you need to do.<\/p>\n<h3>Conclusion<\/h3>\n<p>Having the process split into two doesn&#8217;t only help explain how path units work, but it\u2019s also very useful for debugging. One service does not &#8220;touch&#8221; the other in any way, which means that you could, for example, improve the &#8220;motion detection&#8221; part, and it would be very easy to roll back if things didn&#8217;t work as expected.<\/p>\n<p>Admittedly, the example is a bit goofy, as there are definitely <a href=\"https:\/\/www.linux.com\/learn\/how-operate-linux-spycams-motion\">better ways of monitoring movement using a webcam<\/a>. But remember: the main aim of these articles is to help you learn how systemd units work within a context.<\/p>\n<p>Next time, we&#8217;ll finish up with systemd units by looking at some of the other types of units available and show how to improve your home-monitoring system further by setting up service that sends images to another machine.<\/p>\n<p><em>Learn more about Linux through the free\u00a0<a href=\"https:\/\/training.linuxfoundation.org\/linux-courses\/system-administration-training\/introduction-to-linux\">&#8220;Introduction to Linux&#8221;\u00a0<\/a>course from The Linux Foundation and edX.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>So far in this systemd multi-part tutorial, we\u2019ve covered how to start and stop a service by hand, how to start a service when booting your OS and have it stop on power down, and how to boot a service when a certain device is detected. This installment does something different yet again and covers [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":26645,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[40],"tags":[],"class_list":["post-26644","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-linux-freebsd-unix"],"_links":{"self":[{"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/posts\/26644","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/comments?post=26644"}],"version-history":[{"count":0,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/posts\/26644\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/media\/26645"}],"wp:attachment":[{"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/media?parent=26644"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/categories?post=26644"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/tags?post=26644"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}