{"id":14452,"date":"2017-10-22T23:26:39","date_gmt":"2017-10-23T03:26:39","guid":{"rendered":"http:\/\/scruss.com\/blog\/?p=14452"},"modified":"2017-10-22T23:44:40","modified_gmt":"2017-10-23T03:44:40","slug":"creating-a-systemd-user-service-on-your-raspberry-pi","status":"publish","type":"post","link":"https:\/\/scruss.com\/blog\/2017\/10\/22\/creating-a-systemd-user-service-on-your-raspberry-pi\/","title":{"rendered":"Creating a systemd user service on your Raspberry Pi"},"content":{"rendered":"<p>Flushed with success from yesterday&#8217;s post where I made my first <a href=\"http:\/\/scruss.com\/blog\/2017\/10\/21\/combined-restart-shutdown-button-for-raspberry-pi\/\">systemd service<\/a>, I got carried away and wanted to show you how to create a service that runs as a regular user.<\/p>\n<p>A fairly common question on the <a href=\"https:\/\/www.raspberrypi.org\/forums\/\">Raspberry Pi Forums<\/a> is \u00e2\u20ac\u0153How do I run a script every time I reboot?\u00e2\u20ac\u009d. The traditional answer (and one I&#8217;ve given more than once) is to add a <strong>@reboot<\/strong> clause to your crontab. This will indeed run a command when the computer reboots, but it will run pretty early on in the boot sequence when there&#8217;s no guarantee of network or time services. So the usual remedy is a bit of a kludge:<\/p>\n<pre><code>@reboot sleep 60 &amp;&amp; <\/code>\u00e2\u20ac\u00a6<\/pre>\n<p>This waits a full minute after rebooting, then executes the command. Network and time services are really likely to be available, but it&#8217;s not very elegant. Cron also has some weird gotchas with PATH settings, so while it&#8217;s ubiquitous and has worked for decades, it&#8217;s not easy to get working. Systemd, however, has a much better way of doing it, and better yet, you can do it all without ever hitting <strong>sudo<\/strong>.<\/p>\n<p>I&#8217;ll take as a basis for this post the forum query \u00e2\u20ac\u0153<a href=\"https:\/\/www.raspberrypi.org\/forums\/viewtopic.php?f=91&amp;t=195819&amp;p=1225535#p1224995\">python and crontab<\/a>\u00e2\u20ac\u009d. The asker wanted to log the time when their Raspberry Pi had rebooted, but they&#8217;ve hit the usual problem that the clock didn&#8217;t have the right time when their script was triggered, so the log was useless.<\/p>\n<p>(I&#8217;m not going to do <em>exactly<\/em> what the forum poster did, but this is more a demo of a systemd user service than recreating their results.)<\/p>\n<p>First off, here&#8217;s the script to log the time to a file (saved as <code>~\/bin\/boot_time.py<\/code>):<\/p>\n<pre>#!\/usr\/bin\/python3\r\nfrom time import strftime\r\nwith open(\"\/home\/pi\/logs\/boot_time.txt\", \"a\") as log:\r\n log.write(strftime(\"%d-%m-%Y,%H:%M:%S\\n\"))<\/pre>\n<p>I&#8217;d have done this as a shell script, but the OP used Python, so why fight it?<\/p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #ff0000;\"><strong>FUN FACT<\/strong><\/span>: Under <em>most<\/em> Linux flavours, if you create a\u00c2\u00a0<code>bin<\/code> folder in your home directory, it&#8217;s automatically added to your path. So I could just type <code>boot_time.py<\/code> and the shell would find it.<br \/>\n(You might have to log out and log back in again for the shell to review your path.)<\/p>\n<p>In order to get that to run, I need to do a little housekeeping: make the script executable, and make sure the\u00c2\u00a0<code>logs<\/code> folder exits:<\/p>\n<pre>chmod +x ~\/bin\/boot_time.py\r\nmkdir -p ~\/logs<\/pre>\n<p>Now we need to do the bits that pertain to systemd. First off, you must make a folder for user services:<\/p>\n<pre>mkdir -p ~\/.config\/systemd\/user<\/pre>\n<p style=\"padding-left: 30px;\"><span style=\"color: #ff0000;\"><strong>NOTE<\/strong><\/span>: <code>mkdir -p \u00e2\u20ac\u00a6<\/code> is useful here as it makes the directory and any parent directories that don&#8217;t exist. It also doesn&#8217;t complain if any of them already exist. It&#8217;s kind of a \u00e2\u20ac\u0153<em>make sure this directory exists<\/em>\u00e2\u20ac\u009d command. Make friends with it.<\/p>\n<p>And here&#8217;s the service file, which I saved as <code>~\/.config\/systemd\/user\/boot_time_log.service<\/code>:<\/p>\n<pre>[Unit]\r\nDescription=boot time log\r\nDefaultDependencies=no\r\nAfter=local-fs.target time-sync.target\r\n\r\n[Service]\r\nType=oneshot\r\nExecStart=\/home\/pi\/bin\/boot_time.py\r\n\r\n[Install]\r\nWantedBy=default.target<\/pre>\n<p>The service file does the following (even if I&#8217;m slightly mystified by some of the headings \u00e2\u20ac\u00a6):<\/p>\n<ul>\n<li><em><strong>Unit<\/strong><\/em>\n<ul>\n<li><strong>Description<\/strong> \u00e2\u20ac\u201d a plain text name for the service. This appears in logs when it starts, stops or fails.<\/li>\n<li><strong>DefaultDependencies<\/strong> \u00e2\u20ac\u201d as this service runs once at boot-up, it doesn&#8217;t need the normal systemd functions of restarting and shutting down on reboot. Most service files omit this line.<\/li>\n<li><strong>After<\/strong> \u00e2\u20ac\u201d here we tell systemd what service targets must be running before this service is started. As we need to write to a file and have the right time, the <code>local-fs.target<\/code> and <code>time-sync.target<\/code> seem sensible.<\/li>\n<\/ul>\n<\/li>\n<li><em><strong>Service<\/strong><\/em>\n<ul>\n<li><strong>Type<\/strong> \u00e2\u20ac\u201d this is run once, so it&#8217;s a <code>oneshot<\/code> rather than the usual <code>simple<\/code> service.<\/li>\n<li><strong>ExecStart<\/strong> \u00e2\u20ac\u201d this is the command to run when the service is required.<\/li>\n<\/ul>\n<\/li>\n<li><em><strong>Install<\/strong><\/em>\n<ul>\n<li><strong>WantedBy<\/strong> \u00e2\u20ac\u201d tbh no idea what this does, but if you omit it the service won&#8217;t install. Found the answer in this <a href=\"https:\/\/unix.stackexchange.com\/questions\/251211\/why-doesnt-my-systemd-user-unit-start-at-boot\/251225#251225\">SE<\/a>, and it works. So I guess what it does is <em>make the service not fail<\/em> \u00e2\u20ac\u00a6<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>Finally, you enable the service with:<\/p>\n<pre>systemctl --user enable boot_time_log.service<\/pre>\n<p>Next time you reboot, the time will be appended to the log file <code>~\/logs\/boot_time.txt<\/code>.<\/p>\n<p>Unlike most (that is, <code>Type=simple<\/code>) services, it&#8217;s perfectly fine if this one spends most of its time inactive:<\/p>\n<pre>$ systemctl status --user boot_time_log.service\r\n\u00e2\u2014\u008f boot_time_log.service - boot time log\r\n Loaded: loaded (\/home\/pi\/.config\/systemd\/user\/boot_time_log.service; enabled;\r\n Active: inactive (dead) since Sun 2017-10-22 22:17:56 EDT; 1h 5min ago\r\n Process: 722 ExecStart=\/home\/pi\/bin\/boot_time.py (code=exited, status=0\/SUCCES\r\n Main PID: 722 (code=exited, status=0\/SUCCESS)<\/pre>\n<p>It has executed successfully, so the process doesn&#8217;t have to stick around.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Flushed with success from yesterday&#8217;s post where I made my first systemd service, I got carried away and wanted to show you how to create a service that runs as a regular user. A fairly common question on the Raspberry Pi Forums is \u00e2\u20ac\u0153How do I run a script every time I reboot?\u00e2\u20ac\u009d. The traditional [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[7],"tags":[2510,3086,3090],"class_list":["post-14452","post","type-post","status-publish","format-standard","hentry","category-computers-suck","tag-raspberrypi","tag-systemd","tag-user"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/pQNZZ-3L6","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/scruss.com\/blog\/wp-json\/wp\/v2\/posts\/14452","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/scruss.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/scruss.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/scruss.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/scruss.com\/blog\/wp-json\/wp\/v2\/comments?post=14452"}],"version-history":[{"count":3,"href":"https:\/\/scruss.com\/blog\/wp-json\/wp\/v2\/posts\/14452\/revisions"}],"predecessor-version":[{"id":14455,"href":"https:\/\/scruss.com\/blog\/wp-json\/wp\/v2\/posts\/14452\/revisions\/14455"}],"wp:attachment":[{"href":"https:\/\/scruss.com\/blog\/wp-json\/wp\/v2\/media?parent=14452"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/scruss.com\/blog\/wp-json\/wp\/v2\/categories?post=14452"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/scruss.com\/blog\/wp-json\/wp\/v2\/tags?post=14452"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}