<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>my tech blog &#187; systemd</title>
	<atom:link href="http://billauer.se/blog/category/systemd/feed/" rel="self" type="application/rss+xml" />
	<link>https://billauer.se/blog</link>
	<description>Anything I found worthy to write down.</description>
	<lastBuildDate>Thu, 12 Mar 2026 11:36:00 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.2</generator>
		<item>
		<title>Un-ignore /usr/lib/systemd/ in .gitignore with git repo on root filesystem</title>
		<link>https://billauer.se/blog/2025/12/git-unignore-subdirectory/</link>
		<comments>https://billauer.se/blog/2025/12/git-unignore-subdirectory/#comments</comments>
		<pubDate>Tue, 23 Dec 2025 14:27:35 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[systemd]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=7183</guid>
		<description><![CDATA[Actually, this is about un-ignoring any subdirectory that is grandchild to an ignored directory. Running Linux Mint 22.2 (based upon Ubuntu 24.04), and having a git repository on root filesystem to keep track of the computer&#8217;s configuration, the vast majority of directories are ignored. One of the is /lib, however /lib/systemd/ should not be ignored, [...]]]></description>
			<content:encoded><![CDATA[<p>Actually, this is about un-ignoring any subdirectory that is grandchild to an ignored directory.</p>
<p>Running Linux Mint 22.2 (based upon Ubuntu 24.04), and having a git repository on root filesystem to keep track of the computer&#8217;s configuration, the vast majority of directories are ignored. One of the is /lib, however /lib/systemd/ should not be ignored, as it contains crucial files for the system&#8217;s configuration.</p>
<p>On other distributions, the relevant part in .gitignore usually goes:</p>
<pre><span class="yadayada">[ ... ]</span>
bin/
boot/
dev/
home/
<span class="punch">lib/*
!lib/systemd/
</span>lib64/
lib32/
libx32/
lost+found/
media/
mnt/
opt/
proc/
root/
run/
sbin/
<span class="yadayada">[ ... ]</span></pre>
<p>So lib/ isn&#8217;t ignored as a directory, but all its content, including subdirectories is. That allows for un-ignoring lib/systemd/ on the following row. That&#8217;s why lib/ isn&#8217;t ignore-listed like the other ones.</p>
<p>But on Linux Mint 22.2, /lib is a symbolic link to /usr/lib. And since git treats a symbolic link just like a file, /lib/systemd/ is treated as /usr/lib/systemd. Ignoring /lib as a directory has no effect, and un-ignoring /lib/systemd has no effect, because to git, this directory doesn&#8217;t even exist.</p>
<p>So go</p>
<pre>$ <strong>man gitignore</strong></pre>
<p>and try to figure out what to do. It&#8217;s quite difficult actually, but it boils down to this:</p>
<pre>usr/*
!usr/lib/
usr/lib/*
!usr/lib/systemd/</pre>
<p>It&#8217;s a bit tangled, but the point is that /usr/lib is un-ignored, then all its files are ignored, and then /usr/lib/systemd is un-ignored.</p>
<p>The only good part about this solution is that it works.</p>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2025/12/git-unignore-subdirectory/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Measuring how much RAM a Linux service eats</title>
		<link>https://billauer.se/blog/2024/12/cgroup-peak-ram-consumption/</link>
		<comments>https://billauer.se/blog/2024/12/cgroup-peak-ram-consumption/#comments</comments>
		<pubDate>Fri, 13 Dec 2024 17:20:02 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Server admin]]></category>
		<category><![CDATA[systemd]]></category>
		<category><![CDATA[Virtualization]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=7142</guid>
		<description><![CDATA[Introduction Motivation: I wanted to move a service to another server that is dedicated only to that service. But how much RAM does this new server need? RAM is $$$, so too much is a waste of money, too little means problems. The method is to run the service and expose it to a scenario [...]]]></description>
			<content:encoded><![CDATA[<h3>Introduction</h3>
<p>Motivation: I wanted to move a service to another server that is dedicated only to that service. But how much RAM does this new server need? RAM is $$$, so too much is a waste of money, too little means problems.</p>
<p>The method is to run the service and expose it to a scenario that causes it to consume RAM. And then look at the maximal consumption.</p>
<p>This can be done with &#8220;top&#8221; and similar programs, but these show the current use. I needed the maximal RAM use. Besides, a service may spread out its RAM consumption across several processes. It&#8217;s the cumulative consumption that is interesting.</p>
<p>The appealing solution is to use the fact that systemd creates a cgroup for the service. The answer hence lies in the RAM consumption of the cgroup as a whole. It&#8217;s also possible to create a dedicated cgroup and run a program within that one, as shown in <a rel="noopener" href="https://billauer.se/blog/2016/05/linux-cgroups-swap-quartus/" target="_blank">another post of mine</a>.</p>
<p>This method is somewhat crude, because this memory consumption includes disk cache as well. In other words, this method shows how much RAM is consumed when there&#8217;s plenty of memory, and hence when there&#8217;s no pressure to reclaim any RAM. Therefore, if the service runs on a server with less RAM (or the service&#8217;s RAM consumption is limited in the systemd unit file), it&#8217;s more than possible that everything will work just fine. It might run somewhat slower due to disk access that was previously substituted by the cache.</p>
<p>So using a server with as much memory as measured by the test described below (plus some extra for the OS itself) will result in quick execution, but it might be OK to go for less RAM. A tight RAM limit will cause a lot of disk activity at first, and only afterwards will processes be killed by the OOM killer.</p>
<h3>Where the information is</h3>
<p>All said in this post relates to Linux kernel v4.15. Things are different with later kernels, not necessarily for the better.</p>
<p>There are in principle two versions of the interface with cgroup&#8217;s memory management: First, the one I won&#8217;t use, which is <a rel="noopener" href="https://www.kernel.org/doc/Documentation/cgroup-v2.txt" target="_blank">cgroup-v2</a> (or maybe <a rel="noopener" href="https://docs.kernel.org/admin-guide/cgroup-v2.html" target="_blank">this doc</a> for v2 is better?). The sysfs files for this interface for a service named &#8220;theservice&#8221; reside in /sys/fs/cgroup/unified/system.slice/theservice.service.</p>
<p>I shall be working with the <a rel="noopener" href="https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt" target="_blank">memory control of cgroup-v1</a>. The sysfs files in question are in /sys/fs/cgroup/memory/system.slice/theservice.service/.</p>
<p>If /sys/fs/cgroup/memory/ doesn&#8217;t exist, it might be necessary to mount it explicitly. Also, if system.slice doesn&#8217;t exist under /sys/fs/cgroup/memory/ it&#8217;s most likely because systemd&#8217;s memory accounting is not in action. This can be enabled globally, or by setting MemoryAccounting=true on the service&#8217;s systemd unit (or maybe any unit?).</p>
<p>Speaking of which, it might be a good idea to set MemoryMax in the service&#8217;s systemd unit in order to see what happens when the RAM is really restricted. Or change the limit dynamically, as shown below.</p>
<p>And there&#8217;s always the alternative of creating a separate cgroup and running the service in that group. I&#8217;ll refer to <a rel="noopener" href="https://billauer.se/blog/2016/05/linux-cgroups-swap-quartus/" target="_blank">my own blog post</a> again.</p>
<h3>Getting the info</h3>
<p>All files mentioned below are in /sys/fs/cgroup/unified/system.slice/theservice.service/ (assuming that the systemd service in question is theservice).</p>
<p><strong>The maximal memory used</strong>: memory.max_usage_in_bytes. As it&#8217;s name implies this is the maximal amount of RAM used, measured in bytes. This includes disk cache, so the number is higher than what appears in &#8220;top&#8221;.</p>
<p><strong>The memory currently used</strong>: memory.usage_in_bytes.</p>
<p>For more detailed info about memory use: memory.stat. For example:</p>
<pre>$ <strong>cat memory.stat </strong>
<span class="punch">cache 1138688</span>
rss 4268224512
rss_huge 0
shmem 0
mapped_file 516096
dirty 0
writeback 0
pgpgin 36038063
pgpgout 34995738
pgfault 21217095
pgmajfault 176307
inactive_anon 0
active_anon 4268224512
inactive_file 581632
active_file 401408
unevictable 0
hierarchical_memory_limit 4294967296
total_cache 1138688
total_rss 4268224512
total_rss_huge 0
total_shmem 0
total_mapped_file 516096
total_dirty 0
total_writeback 0
total_pgpgin 36038063
total_pgpgout 34995738
total_pgfault 21217095
total_pgmajfault 176307
total_inactive_anon 0
total_active_anon 4268224512
total_inactive_file 581632
total_active_file 401408
total_unevictable 0</pre>
<p>Note the &#8220;cache&#8221; part at the beginning. It&#8217;s no coincidence that it&#8217;s first. That&#8217;s the most important part: How much can be reclaimed just by flushing the cache.</p>
<p>On a 6.1.0 kernel, I&#8217;ve seen memory.peak and memory.current instead of memory.max_usage_in_bytes and memory.usage_in_bytes. memory.peak wasn&#8217;t writable however (neither in its permissions nor was it possible to write to it), so it wasn&#8217;t possible to reset the max level.</p>
<h3>Setting memory limits</h3>
<p>It&#8217;s possible to set memory limits in systemd&#8217;s unit file, but it can be more convenient to do this on the fly. In order to set the hard limit of memory use to 40 MiB, go (as root)</p>
<pre># echo 40M &gt; memory.limit_in_bytes</pre>
<p>To disable the limit, pick an unreasonably high number, e.g.</p>
<pre># echo 100G &gt; memory.limit_in_bytes</pre>
<p>Note that restarting the systemd service has no effect on these parameters (unless a memory limit is required in the unit file). The cgroup directory remains intact.</p>
<h3>Resetting between tests</h3>
<p>To reset the maximal value that has been recorded for RAM use (as root)</p>
<pre># echo 0 &gt; memory.max_usage_in_bytes</pre>
<p>But to really want to start from fresh, all disk cache needs to be cleared as well. The sledge-hammer way is going</p>
<pre># echo 1 &gt; /proc/sys/vm/drop_caches</pre>
<p>This frees the page caches system-wide, so everything running on the computer will need to re-read things again from the disk. There&#8217;s a slight and temporary global impact on the performance. On a GUI desktop, it gets a bit slow for a while.</p>
<p>A message like this will appear in the kernel log in response:</p>
<pre>bash (43262): drop_caches: 1</pre>
<p>This is perfectly fine, and indicates no error.</p>
<p>Alternatively, set a low limit for the RAM usage with memory.limit_in_bytes, as shown above. This impacts the cgroup only, forcing a reclaim of disk cache.</p>
<p>Two things that have <strong>no effect</strong>:</p>
<ul>
<li>Reducing the soft limit (memory.soft_limit_in_bytes). This limit is relevant only when the system is in a shortage of RAM overall. Otherwise, it does nothing.</li>
<li>Restarting the service with systemd. It wouldn&#8217;t make any sense to flush a disk cache when restarting a service.</li>
</ul>
<p>It&#8217;s of course a good idea to get rid of the disk cache before clearing memory.max_usage_in_bytes, so the max value starts without taking the disk cache into account.</p>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2024/12/cgroup-peak-ram-consumption/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Migrating an OpenVZ container to KVM</title>
		<link>https://billauer.se/blog/2024/07/container-to-kvm-virtualization/</link>
		<comments>https://billauer.se/blog/2024/07/container-to-kvm-virtualization/#comments</comments>
		<pubDate>Fri, 12 Jul 2024 15:11:13 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Linux kernel]]></category>
		<category><![CDATA[Server admin]]></category>
		<category><![CDATA[systemd]]></category>
		<category><![CDATA[Virtualization]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=7097</guid>
		<description><![CDATA[Introduction My Debian 8-based web server had been running for several years as an OpenVZ container, when the web host told me that containers are phased out, and it&#8217;s time to move on to a KVM. This is an opportunity to upgrade to a newer distribution, most of you would say, but if a machine [...]]]></description>
			<content:encoded><![CDATA[<h3>Introduction</h3>
<p>My Debian 8-based web server had been running for several years as an OpenVZ container, when the web host told me that containers are phased out, and it&#8217;s time to move on to a KVM.</p>
<p>This is an opportunity to upgrade to a newer distribution, most of you would say, but if a machine works flawlessly for a long period of time, I&#8217;m very reluctant to change anything. Don&#8217;t touch a stable system. It just happened to have an uptime of 426 days, and the last time this server caused me trouble was way before that.</p>
<p>So the question is if it&#8217;s possible to convert a container into a KVM machine, just by copying the filesystem. After all, what&#8217;s the difference if /sbin/init (systemd) is kicked off as a plain process inside a container or if the kernel does the same thing?</p>
<p>The answer is yes-ish, this manipulation is possible, but it requires some adjustments.</p>
<p>These are my notes and action items while I found my way to get it done. Everything below is very specific to my own slightly bizarre case, and at times I ended up carrying out tasks in a different order than as listed here. But this can be useful for understanding what&#8217;s ahead.</p>
<p>By the way, the wisest thing I did throughout this process, was to go through the whole process on a KVM machine that I built on my own local computer. This virtual machine functioned as a mockup of the server to be installed. Not only did it make the trial and error much easier, but it also allowed me to test all kind of things after the real server was up and running without messing the real machine.</p>
<h3>Faking Ubuntu 24.04 LTS</h3>
<p>To make things even more interesting, I also wanted to push the next time I&#8217;ll be required to mess with the virtual machine as long as possible into the future. Put differently, I wanted to hide the fact that the machine runs on ancient software. There should not be a request to upgrade in the foreseeable future because the old system isn&#8217;t compatible with some future version of KVM.</p>
<p>So to the KVM hypervisor, my machine should feel like an Ubuntu 24.04, which was the latest server distribution offered at the time I did this trick. Which brings the question: What does the hypervisor see?</p>
<p>The KVM guest interfaces with its hypervisor in three ways:</p>
<ul>
<li>With GRUB, which accesses the virtual disk.</li>
<li>Through the kernel, which interacts with the virtual hardware.</li>
<li>Through the guest&#8217;s DHCP client, which fetches the IP address, default gateway and DNS from the hypervisor&#8217;s dnsmasq.</li>
</ul>
<p>Or so I hope. Maybe there&#8217;s some aspect I&#8217;m not aware of. It&#8217;s not like I&#8217;m such an expert in virtualization.</p>
<p>So the idea was that both GRUB and the kernel should be the same as in Ubuntu 24.04. This way, any KVM setting that works with this distribution will work with my machine. The Naphthalene smell from the user-space software underneath will not reach the hypervisor.</p>
<p>This presumption can turn out to be wrong, and the third item in the list above demonstrates that: The guest machine gets its IP address from the hypervisor through a DHCP request issued by systemd-networkd, which is part of systemd version 215. So the bluff is exposed. Will there be some kind of incompatibility between the old systemd&#8217;s DHCP client and some future hypervisor&#8217;s response?</p>
<p>Regarding this specific issue, I doubt there will be a problem, as DHCP is such a simple and well-established protocol. And even if that functionality broke, the IP address is fixed anyhow, so the virtual NIC can be configured statically.</p>
<p>But who knows, maybe there is some kind of interaction with systemd that I&#8217;m not aware of? Future will tell.</p>
<p>So it boils down to faking GRUB and using a recent kernel.</p>
<h3>Solving the GRUB problem</h3>
<p>Debian 8 comes with GRUB version 0.97. Could we call that GRUB 1? I can already imagine the answer to my support ticket saying &#8220;please upgrade your system, as our KVM hypervisor doesn&#8217;t support old versions of GRUB&#8221;.</p>
<p>So I need a new one.</p>
<p>Unfortunately, the common way to install GRUB is with a couple of hocus-pocus tools that do the work well in the usual scenario.</p>
<p>As it turns out, there are two parts that need to be installed: The first part consists of the GRUB binary on the boot partition (GRUB partition or EFI, pick your choice), plus several files (modules and other) in /boot/grub/. The second part is a script file, grub.cfg, which is a textual file that can be edited manually.</p>
<p>To make a long story short, I installed the distribution on a virtual machine with the same layout, and made a copy of the grub.cfg file that was created. I then edited this file directly to fit into the new machine. As for installing GRUB binary, I did this from a Live ISO Ubuntu 24.04, so it&#8217;s genuine and legit.</p>
<p>For the full and explained story, I&#8217;ve written <a rel="noopener" href="https://billauer.se/blog/2024/07/installing-grub-rescue/" target="_blank">a separate post</a>.</p>
<h3>Fitting a decent kernel</h3>
<p>This way or another, a kernel and its modules must be added to the filesystem in order to convert it from a container to a KVM machine. This is the essential difference: With a container, one kernel runs all containers and gives them the illusion that they&#8217;re the only one. With KVM, the boot starts from the very beginning.</p>
<p>If there was something I <strong>didn&#8217;t</strong> worry about, it was the concept of running an ancient distribution with a very recent kernel. I have a lot of experience with compiling the hot-hot-latest-out kernel and run it with steam engine distributions, and very rarely have I seen any issue with that. The Linux kernel is backward compatible in a remarkable way.</p>
<p>My original idea was to grab the kernel image and the modules from a running installation of Ubuntu 24.04. However, the module format of this distro is incompatible with old Debian 8 (ZST compression seems to have been the crux), and as a result, no modules were loaded.</p>
<p>So I took config-6.8.0-36-generic from Ubuntu 24.04 and used it as the starting point for the .config file used for compiling the vanilla stable kernel with version v6.8.12.</p>
<p>And then there were a few modifications to .config:</p>
<ul>
<li>&#8220;make oldconfig&#8221; asked a few questions and made some minor modifications, nothing apparently related.</li>
<li>Dropped kernel module compression (CONFIG_MODULE_COMPRESS_ZSTD off) and set kernel&#8217;s own compression to gzip. This was probably the reason the distribution&#8217;s modules didn&#8217;t load.</li>
<li>Some crypto stuff was disabled: CONFIG_INTEGRITY_PLATFORM_KEYRING, CONFIG_SYSTEM_BLACKLIST_KEYRING and CONFIG_INTEGRITY_MACHINE_KEYRING were dropped, same with CONFIG_LOAD_UEFI_KEYS and most important, CONFIG_SYSTEM_REVOCATION_KEYS was set to &#8220;&#8221;. Its previous value, &#8220;debian/canonical-revoked-certs.pem&#8221; made the compilation fail.</li>
<li>Dropped CONFIG_DRM_I915, which caused some weird compilation error.</li>
<li>After making a test run with the kernel, I also dropped CONFIG_UBSAN with everything that comes with it. UBSAN spat a lot of warning messages on mainstream drivers, and it&#8217;s really annoying. It&#8217;s still unclear to me why these warnings don&#8217;t appear on the distribution kernel. Maybe because a difference between compiler versions (the warnings stem from checks inserted by gcc).</li>
</ul>
<p>The compilation took 32 minutes on a machine with 12 cores (6 hyperthreaded). By far, the longest and most difficult kernel compilation I can remember for a long time.</p>
<p>Based upon <a rel="noopener" href="https://billauer.se/blog/2015/10/linux-kernel-compilation-jots/" target="_blank">my own post</a>, I created the Debian packages for the whole thing, using the bindeb-pkg make target.</p>
<p>That took additional 20 minutes, running on all cores. I used two of these packages in the installation of the KVM machine, as shown in the cookbook below.</p>
<h3>Methodology</h3>
<p>So the deal with my web host was like this: They started a KVM machine (with a different IP address, of course). I prepared this KVM machine, and when that was ready, I sent a support ticket asking for swapping the IP addresses. This way, the KVM machine became the new server, and the old container machine went to the junkyard.</p>
<p>As this machine involved a mail server and web sites with user content (comments to my blog, for example), I decided to stop the active server, copy &#8220;all data&#8221;, and restart the server only after the IP swap. In other words, the net result should be as if the same server had been shut down for an hour, and then restarted. No discontinuities.</p>
<p>As it turned out, everything that is related to the web server and email, including the logs of everything, are in /var/ and /home/. So I could therefore copy all files from the old server to the new one for the sake of setting it up, and verify that everything is smooth as a first stage.</p>
<p>Then I shut down the services and copied /var/ and /home/. And then came the IP swap.</p>
<p>This simple command is handy for checking which files have changed during the past week. The first finds the directories, and the second the plain files.</p>
<pre># find / -xdev -ctime -7 -type d | sort
# find / -xdev -ctime -7 -type f | sort</pre>
<p>The purpose of the -xdev flag is to remain on one filesystem. Otherwise, a lot of files from /proc and such are printed out. If your system has several relevant filesystems, be sure to add them to &#8220;/&#8221; in this example.</p>
<p>The next few sections below are the cookbook I wrote for myself in order to get it done without messing around (and hence mess up).</p>
<p>In hindsight, I can say that except for dealing with GRUB and the kernel, most of the hassle had to with the NIC: Its name changed from venet0 to eth0, and it got its address through DHCP relatively late in the boot process. And that required some adaptations.</p>
<h3>Preparing the virtual machine</h3>
<ul>
<li>Start the installation Ubuntu 24.04 LTS server edition (or whatever is available, it doesn&#8217;t matter much). Possible stop the installation as soon as files are being copied: The only purpose of this step is to partition the disk neatly, so that /dev/vda1 is a small partition for GRUB, and /dev/vda3 is the root filesystem (/dev/vda2 is a swap partition).</li>
<li>Start the KVM machine with a rescue image (preferable graphical or with sshd running). I went for Ubuntu 24.04 LTS server Live ISO (the best choice provided by my web host). See notes below on using Ubuntu&#8217;s server ISO as a rescue image.</li>
<li>Wipe the existing root filesystem, if such has been installed. I considered this necessary at the time, because the default inode size may be 256, and GRUB version 1 won&#8217;t play ball with that. But later on I decided on GRUB 2. Anyhow, I forced it to be 128 bytes, despite the warning that 128-byte inodes cannot handle dates beyond 2038 and are deprecated:
<pre># mkfs.ext4 -I 128 /dev/vda3</pre>
</li>
<li>And since I was at it, no automatic fsck check. Ever. It&#8217;s really annoying when you want to kick off the server quickly.
<pre># tune2fs -c 0 -i 0 /dev/vda3</pre>
</li>
<li>Mount new system as /mnt/new:
<pre># mkdir /mnt/new
# mount /dev/vda3 /mnt/new</pre>
</li>
<li>Copy the filesystem. On the OpenVZ machine:
<pre># tar --one-file-system -cz / | nc -q 0 185.250.251.160 1234 &gt; /dev/null</pre>
<p>and the other side goes (run this before the command above):</p>
<pre># nc -l 1234 &lt; /dev/null | time tar -C /mnt/new/ -xzv</pre>
<p>This took about 30 minutes. The purpose of the &#8220;-q 0&#8243; flag and those /dev/null redirections is merely to make nc quit when the tar finishes.<br />
Or, doing the same from a backup tarball:</p>
<pre>$ cat myserver-all-24.07.08-08.22.tar.gz | nc -q 0 -l 1234 &gt; /dev/null</pre>
<p>and the other side goes</p>
<pre># nc 10.1.1.3 1234 &lt; /dev/null | time tar -C /mnt/new/ -xzv</pre>
</li>
<li>Remove old /lib/modules and boot directory:
<pre># rm -rf /mnt/new/lib/modules/ /mnt/new/boot/</pre>
</li>
<li>Create /boot/grub and copy the grub.cfg file that I&#8217;ve prepared in advance to there. <a rel="noopener" href="https://billauer.se/blog/2024/07/installing-grub-rescue/" target="_blank">This separate post</a> explains the logic behind doing it this way.</li>
<li>Install GRUB on the boot parition (this also adds a lot of files to /boot/grub/):
<pre># grub-install --root-directory=/mnt/new /dev/vda</pre>
</li>
<li>In order to work inside the chroot, some bind and tmpfs mounts are necessary:
<pre># mount -o bind /dev /mnt/new/dev
# mount -o bind /sys /mnt/new/sys
# mount -t proc /proc /mnt/new/proc
# mount -t tmpfs tmpfs /mnt/new/tmp
# mount -t tmpfs tmpfs /mnt/new/run</pre>
</li>
<li>Copy the two .deb files that contain the Linux kernel files to somewhere in /mnt/new/</li>
<li>Chroot into the new fs:
<pre># chroot /mnt/new/</pre>
</li>
<li>Check that /dev, /sys, /proc, /run and /tmp are as expected (mounted correctly).</li>
<li>Disable and stop these services: bind9, sendmail, cron.</li>
<li>This wins the prize for the oddest fix: Probably in relation to the OpenVZ container, the LSB modules_dep service is active, and it deletes all module files in /lib/modules on reboot. So make sure to never see it again. Just disabling it wasn&#8217;t good enough.
<pre># systemctl mask modules_dep.service</pre>
</li>
<li>Install the Linux kernel and its modules into /boot and /lib/modules:
<pre># dpkg -i linux-image-6.8.12-myserver_6.8.12-myserver-2_amd64.deb</pre>
</li>
<li>Also install the headers for compilation (why not?)
<pre># dpkg -i linux-headers-6.8.12-myserver_6.8.12-myserver-2_amd64.deb</pre>
</li>
<li>Add /etc/systemd/network/20-eth0.network
<pre>[Match]
Name=eth0

[Network]
DHCP=yes</pre>
<p>The NIC was a given in a container, but now it has to be raised explicitly and the IP address possibly obtained from the hypervisor via DHCP, as I&#8217;ve done here.</li>
<li>Add the two following lines to /etc/sysctl.conf, in order to turn off IPv6:
<pre>net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1</pre>
</li>
<li>Adjust the firewall rules, so that they don&#8217;t depend on the server having a specific IP address (because a temporary IP address will be used).</li>
<li>Add support for lspci (better do it now if something goes wrong after booting):
<pre># apt install pciutils</pre>
</li>
<li>Ban the evbug module, which is intended to generate debug message on input devices. Unfortunately, it floods the kernel log sometimes when the mouse goes over the virtual machine&#8217;s console window. So ditch it by adding /etc/modprobe.d/evbug-blacklist.conf having this single line:
<pre>blacklist evbug</pre>
</li>
<li>Edit /etc/fstab. Remove everything, and leave only this row:
<pre>/dev/vda3 / ext4 defaults 0 1</pre>
</li>
<li>Remove persistence udev rules, if such exist, at /etc/udev/rules.d. Oddly enough, there was nothing in this directory, not in the existing OpenVZ server and not in a regular Ubuntu 24.04 server installation.</li>
<li>Boot up the system from disk, and perform post-boot fixes as mentioned below.</li>
</ul>
<h3>Post-boot fixes</h3>
<ul>
<li>Verify that /tmp is indeed mounted as a tmpfs.</li>
<li>Disable (actually, mask) the automount service, which is useless and fails. This makes systemd&#8217;s status degraded, which is practically harmless, but confusing.
<pre># systemctl mask proc-sys-fs-binfmt_misc.automount</pre>
</li>
<li>Install the dbus service:
<pre># apt install dbus</pre>
<p>Not only is it the right thing to do on a Linux system, but it also silences this warning:</p>
<pre>Cannot add dependency job for unit dbus.socket, ignoring: Unit dbus.socket failed to load: No such file or directory.</pre>
</li>
<li>Enable login prompt on the default visible console (tty1) so that a prompt appears after all the boot messages:
<pre># systemctl enable getty@tty1.service</pre>
<p>The other tty&#8217;s got a login prompt when using Ctrl-Alt-Fn, but not the visible console. So this fixed it. Otherwise, one can be mislead into thinking that the boot process is stuck.</li>
<li>Optionally: Disable <a rel="noopener" href="https://wiki.openvz.org/Debian_template_creation" target="_blank">vzfifo service</a> and remove /.vzfifo.</li>
</ul>
<h3>Just before the IP address swap</h3>
<ul>
<li>Reboot the openVZ server to make sure that it wakes up OK.</li>
<li>Change the openVZ server&#8217;s firewall, so works with a different IP address. Otherwise, it becomes unreachable after the IP swap.</li>
<li>Boot the target KVM machine <span class="punch">in rescue mode</span>. No need to set up the ssh server as all will be done through VNC.</li>
<li>On the KVM machine, mount new system as /mnt/new:
<pre># mkdir /mnt/new
# mount /dev/vda3 /mnt/new</pre>
</li>
<li>On the OpenVZ server, check for recently changed directories and files:
<pre># find / -xdev -ctime -7 -type d | sort &gt; recently-changed-dirs.txt
# find / -xdev -ctime -7 -type f | sort &gt; recently-changed-files.txt</pre>
</li>
<li>Verify that the changes are only in the places that are going to be updated. If not, consider if and how to update these other files.</li>
<li>Verify that the mail queue is empty, or let sendmail empty it if possible. Not a good idea to have something firing off as soon as sendmail resumes:
<pre># mailq</pre>
</li>
<li>Disable all services except sshd on the OpenVZ server:
<pre># systemctl disable cron dovecot apache2 bind9 sendmail mysql xinetd</pre>
</li>
<li>Run &#8220;mailq&#8221; again to verify that the mail queue is empty (unless there was a reason to leave a message there in the previous check).</li>
<li>Reboot OpenVZ server and verify that none of these is running. This is the point at which this machine is dismissed as a server, and the downtime clock begins ticking.</li>
<li>Verify that this server doesn&#8217;t listen to any ports except ssh, as an indication that all services are down:
<pre># netstat -n -a | less</pre>
</li>
<li>Repeat the check of recently changed files.</li>
<li>On <strong>KVM machine</strong>, remove /var and /home.</li>
<li>
<pre># rm -rf /mnt/new/var /mnt/new/home</pre>
</li>
<li>Copy these parts:<br />
On the KVM machine, <span class="punch">using the VNC console</span>, go&nbsp;</p>
<pre># nc -l 1234 &lt; /dev/null | time tar -C /mnt/new/ -xzv</pre>
<p>and on myserver:</p>
<pre># tar --one-file-system -cz /var /home | nc -q 0 185.250.251.160 1234 &gt; /dev/null</pre>
<p>Took 28 minutes.</li>
<li>Check that /mnt/new/tmp and /mnt/tmp/run are empty and remove whatever is found, if there&#8217;s something there. There&#8217;s no reason for anything to be there, and it would be weird if there was, given the way the filesystem was copied from the original machine. But if there are any files, it&#8217;s just confusing, as /tmp and /run are tmpfs on the running machine, so any files there will be invisible anyhow.</li>
<li>Reboot the KVM machine <strong>with a reboot command</strong>. It will stop anyhow for removing the CDROM.</li>
<li>Remove the KVM&#8217;s CDROM and continue the reboot normally.</li>
<li>Login to the KVM machine with <span class="punch">ssh</span>.</li>
<li>Check that all is OK: systemctl status as well as journalctl. Note that the apache, mysql and dovecot should be running now.</li>
<li>Power down both virtual machines.</li>
<li>Request an IP address swap. Let them do whatever they want with the <span class="punch">IPv6</span> addresses, as they are ignored anyhow.</li>
</ul>
<h3>After IP address swap</h3>
<ul>
<li>Start the KVM server normally, and login normally <span class="punch">through ssh</span>.</li>
<li>Try to browse into the web sites: The web server should already be working properly (even though the DNS is off, but there&#8217;s a backup DNS).</li>
<li>Check journalctl and systemctl status.</li>
<li>Resume the original firewall rules and <span class="punch">verify that the firewall works properly</span>:
<pre># systemctl restart netfilter-persistent
# iptables -vn -L</pre>
</li>
<li>Start all services, and check status and journalctl again:
<pre># systemctl start cron dovecot apache2 bind9 sendmail mysql xinetd</pre>
</li>
<li>If all is fine, enable these services:
<pre># systemctl enable cron dovecot apache2 bind9 sendmail mysql xinetd</pre>
</li>
<li>Reboot (with reboot command), and check that all is fine.</li>
<li>In particular, send DNS queries directly to the server with dig, and also send an email to a foreign address (e.g. gmail). My web host blocked outgoing connections to port 25 on the new server, for example.</li>
<li>Delete ifcfg-venet0 and ifcfg-venet0:0 in /etc/sysconfig/network-scripts/, as they relate to the venet0 interface that exists only in the container machine. It&#8217;s just misleading to have it there.</li>
<li>Compare /etc/rc* and /etc/systemd with the situation before the transition in the git repo, to verify that everything is like it should be.</li>
</ul>
<ul>
<li>Check the server with nmap (run this from another machine):
<pre>$ nmap -v -A <span style="color: #888888;"><em>server</em></span>
$ sudo nmap -v -sU <span style="color: #888888;"><em>server</em></span></pre>
</li>
</ul>
<h3>And then the DNS didn&#8217;t work</h3>
<p>I knew very well why I left plenty of time free for after the IP swap. Something will always go wrong after a maneuver like this, and this time was no different. And for some odd reason, it was the bind9 DNS that played two different kinds of pranks.</p>
<p>I noted immediately that the server didn&#8217;t answer to DNS queries. As it turned out, there were two apparently independent reasons for it.</p>
<p>The first was that when I re-enabled the bind9 service (after disabling it for the sake of moving), systemctl went for the SYSV scripts instead of its own. So I got:</p>
<pre># <strong>systemctl enable bind9</strong>
Synchronizing state for bind9.service with sysvinit using update-rc.d...
Executing /usr/sbin/update-rc.d bind9 defaults
insserv: warning: current start runlevel(s) (empty) of script `bind9' overrides LSB defaults (2 3 4 5).
insserv: warning: current stop runlevel(s) (0 1 2 3 4 5 6) of script `bind9' overrides LSB defaults (0 1 6).
Executing /usr/sbin/update-rc.d bind9 enable</pre>
<p>This could have been harmless and gone unnoticed, had it not been that I&#8217;ve added a &#8220;-4&#8243; flag to bind9&#8242;s command, or else it wouldn&#8217;t work. So by running the SYSV scripts, my change in /etc/systemd/system/bind9.service wasn&#8217;t in effect.</p>
<p>Solution: Delete all files related to bind9 in /etc/init.d/ and /etc/rc*.d/. Quite aggressive, but did the job.</p>
<p>Having that fixed, it still didn&#8217;t work. The problem now was that eth0 was configured through DHCP after the bind9 had begun running. As a result, the DNS didn&#8217;t listen to eth0.</p>
<p>I slapped myself for thinking about adding a &#8220;sleep&#8221; command before launching bind9, and went for the right way to do this. Namely:</p>
<pre>$ <strong>cat /etc/systemd/system/bind9.service</strong>
[Unit]
Description=BIND Domain Name Server
Documentation=man:named(8)
After=network-online.target <span class="punch">systemd-networkd-wait-online.service</span>
Wants=network-online.target <span class="punch">systemd-networkd-wait-online.service</span>

[Service]
ExecStart=/usr/sbin/named -4 -f -u bind
ExecReload=/usr/sbin/rndc reload
ExecStop=/usr/sbin/rndc stop

[Install]
WantedBy=multi-user.target</pre>
<p>The systemd-networkd-wait-online.service is not there by coincidence. Without it, bind9 was launched before eth0 had received an address. With this, systemd consistently waited for the DHCP to finish, and then launched bind9. As it turned out, this also delayed the start of apache2 and sendmail.</p>
<p>If anything, network-online.target is most likely redundant.</p>
<p>And with this fix, the crucial row appeared in the log:</p>
<pre>named[379]: listening on IPv4 interface eth0, 193.29.56.92#53</pre>
<p>Another solution could have been to assign an address to eth0 statically. For some odd reason, I prefer to let DHCP do this, even though the firewall will block all traffic anyhow if the IP address changes.</p>
<h3>Using Live Ubuntu as rescue mode</h3>
<p>Set Ubuntu 24.04 server amd64 as the CDROM image.</p>
<p>After the machine has booted, send a Ctrl-Alt-F2 to switch to the second console. Don&#8217;t go on with the installation wizard, as it will of course wipe the server.</p>
<p>In order to establish an ssh connection:</p>
<ul>
<li>Choose a password for the default user (ubuntu-server).
<pre>$ passwd</pre>
<p>If you insist on a weak password, remember that you can do that only as root.</li>
<li>Use ssh to log in:
<pre>$ ssh ubuntu-server@185.250.251.160</pre>
</li>
</ul>
<p>Root login is forbidden (by default), so don&#8217;t even try.</p>
<p>Note that even though sshd apparently listens only to IPv6 ports, it&#8217;s actually accepting IPv4 connection by virtue of IPv4-mapped IPv6 addresses:</p>
<pre># <strong>lsof -n -P -i tcp 2&gt;/dev/null</strong>
COMMAND    PID            USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
systemd      1            root  143u  <span class="punch">IPv6</span>   5323      0t0  <span class="punch">TCP *:22</span> (LISTEN)
systemd-r  911 systemd-resolve   15u  IPv4   1766      0t0  TCP 127.0.0.53:53 (LISTEN)
systemd-r  911 systemd-resolve   17u  IPv4   1768      0t0  TCP 127.0.0.54:53 (LISTEN)
<span class="punch">sshd</span>      1687            root    3u  <span class="punch">IPv6</span>   5323      0t0  <span class="punch">TCP *:22</span> (LISTEN)
sshd      1847            root    4u  <span class="punch">IPv6</span>  11147      0t0  <span class="punch">TCP 185.250.251.160:22-&gt;85.64.140.6:57208</span> (ESTABLISHED)
sshd      1902   ubuntu-server    4u  IPv6  11147      0t0  TCP 185.250.251.160:22-&gt;85.64.140.6:57208 (ESTABLISHED)<span style="font-family: verdana, Arial, Helvetica, sans-serif; font-size: 16px;">One can get the impression that sshd listens only to IPv6. But somehow, it also accepts</span></pre>
<p>So don&#8217;t get confused by e.g. netstat and other similar utilities.</p>
<h3>To NTP or not?</h3>
<p>I wasn&#8217;t sure if I should run an NTP client inside a KVM virtual machine. So these are the notes I took.</p>
<ul>
<li><a rel="noopener" href="https://opensource.com/article/17/6/timekeeping-linux-vms" target="_blank">This</a> is a nice tutorial to start with.</li>
<li>It&#8217;s probably a good idea to run an NTP client on the client. It <a rel="noopener" href="https://sanjuroe.dev/sync-kvm-guest-using-ptp" target="_blank">would have been better to utilize the PTP protocol</a>, and get the host&#8217;s clock directly. But this is really an overkill. The drawback with these daemons is that if the client goes down and back up again, it will start with the old time, and then jump.</li>
<li>It&#8217;s also <a rel="noopener" href="https://doc.opensuse.org/documentation/leap/archive/42.1/virtualization/html/book.virt/sec.kvm.managing.clock.html" target="_blank">a good idea</a> to use kvm_clock in addition to NTP. This kernel feature uses the pvclock protocol to <a rel="noopener" href="https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/6/html/virtualization_administration_guide/sect-virtualization-tips_and_tricks-libvirt_managed_timers#sect-timer-element" target="_blank">lets guest virtual machines read the host physical machine’s wall clock time</a> as well as its TSC. See <a rel="noopener" href="https://rwmj.wordpress.com/2010/10/15/kvm-pvclock/" target="_blank">this post for a nice tutoria</a>l about kvm_clock.</li>
<li>In order to know which clock source the kernel uses, <a rel="noopener" href="https://access.redhat.com/solutions/18627" target="_blank">look in /sys/devices/system/clocksource/clocksource0/current_clocksource</a>. Quite expectedly, it was kvm-clock (available sources were kvm-clock, tsc and acpi_pm).</li>
<li>It so turned out that systemd-timesyncd started running without my intervention when moving from a container to KVM.</li>
</ul>
<p>On a working KVM machine, timesyncd tells about its presence in the log:</p>
<pre>Jul 11 20:52:52 myserver systemd-timesyncd[197]: interval/delta/delay/jitter/drift 2048s/+0.001s/0.007s/0.003s/+0ppm
Jul 11 21:27:00 myserver systemd-timesyncd[197]: interval/delta/delay/jitter/drift 2048s/-0.000s/0.007s/0.001s/+0ppm
Jul 11 22:01:08 myserver systemd-timesyncd[197]: interval/delta/delay/jitter/drift 2048s/-0.002s/0.007s/0.001s/+0ppm
Jul 11 22:35:17 myserver systemd-timesyncd[197]: interval/delta/delay/jitter/drift 2048s/-0.001s/0.007s/0.001s/+0ppm
Jul 11 23:09:25 myserver systemd-timesyncd[197]: interval/delta/delay/jitter/drift 2048s/+0.007s/0.007s/0.003s/+0ppm
Jul 11 23:43:33 myserver systemd-timesyncd[197]: interval/delta/delay/jitter/drift 2048s/-0.003s/0.007s/0.005s/+0ppm (ignored)
Jul 12 00:17:41 myserver systemd-timesyncd[197]: interval/delta/delay/jitter/drift 2048s/-0.006s/0.007s/0.005s/-1ppm
Jul 12 00:51:50 myserver systemd-timesyncd[197]: interval/delta/delay/jitter/drift 2048s/+0.001s/0.007s/0.005s/+0ppm
Jul 12 01:25:58 myserver systemd-timesyncd[197]: interval/delta/delay/jitter/drift 2048s/+0.002s/0.007s/0.005s/+0ppm
Jul 12 02:00:06 myserver systemd-timesyncd[197]: interval/delta/delay/jitter/drift 2048s/+0.002s/0.007s/0.005s/+0ppm
Jul 12 02:34:14 myserver systemd-timesyncd[197]: interval/delta/delay/jitter/drift 2048s/-0.001s/0.007s/0.005s/+0ppm
Jul 12 03:08:23 myserver systemd-timesyncd[197]: interval/delta/delay/jitter/drift 2048s/-0.000s/0.007s/0.005s/+0ppm
Jul 12 03:42:31 myserver systemd-timesyncd[197]: interval/delta/delay/jitter/drift 2048s/-0.001s/0.007s/0.004s/+0ppm
Jul 12 04:17:11 myserver systemd-timesyncd[197]: interval/delta/delay/jitter/drift 2048s/-0.000s/0.007s/0.003s/+0ppm</pre>
<p>So a resync takes place every 2048 seconds (34 minutes and 8 seconds), like a clockwork. As apparent from the values, there&#8217;s no dispute about the time between Debian&#8217;s NTP server and the web host&#8217;s hypervisor.</p>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2024/07/container-to-kvm-virtualization/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>systemd dependencies and processing /etc/fstab</title>
		<link>https://billauer.se/blog/2023/12/systemd-dependencies-fstab/</link>
		<comments>https://billauer.se/blog/2023/12/systemd-dependencies-fstab/#comments</comments>
		<pubDate>Wed, 06 Dec 2023 10:05:42 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[systemd]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=6979</guid>
		<description><![CDATA[The problem I wanted to solve On an embedded arm system running LUbuntu 16.04 LTS (systemd 229), I wanted to add a mount point to the /etc/fstab file, which originally was like this: # UNCONFIGURED FSTAB FOR BASE SYSTEM Empty, except for this message. Is that a bad omen or what? This is the time [...]]]></description>
			<content:encoded><![CDATA[<h3>The problem I wanted to solve</h3>
<p>On an embedded arm system running LUbuntu 16.04 LTS (systemd 229), I wanted to add a mount point to the /etc/fstab file, which originally was like this:</p>
<pre># UNCONFIGURED FSTAB FOR BASE SYSTEM</pre>
<p>Empty, except for this message. Is that a bad omen or what? This is the time to mention that the system was set up with debootstrap.</p>
<p>So I added this row to /etc/fstab:</p>
<pre>#/dev/mmcblk0p1 /mnt/tfcard vfat defaults 0 0</pre>
<p>The result: Boot failed, and systemd asked me for the root password for the sake of getting a rescue shell (the overall systemd status was &#8220;maintenance&#8221;, cutely enough).</p>
<p>Journalctl was kind enough to offer some not-so-helpful hints:</p>
<pre>systemd[1]: dev-mmcblk0p1.device: Job dev-mmcblk0p1.device/start timed out.
systemd[1]: Timed out waiting for device dev-mmcblk0p1.device.
systemd[1]: Dependency failed for /mnt/tfcard.
systemd[1]: Dependency failed for Local File Systems.
systemd[1]: <span class="punch">local-fs.target: Job local-fs.target/start failed with result 'dependency'.</span>
systemd[1]: local-fs.target: Triggering OnFailure= dependencies.
systemd[1]: <span class="punch">mnt-tfcard.mount: Job mnt-tfcard.mount/start failed with result 'dependency'.</span>
systemd[1]: dev-mmcblk0p1.device: Job dev-mmcblk0p1.device/start failed with result 'timeout'.</pre>
<p>Changing the &#8220;defaults&#8221; option to &#8220;nofail&#8221; got the system up and running, but without the required mount. That said, running &#8220;mount -a&#8221; did successfully mount the requested partition.</p>
<p>Spoiler: I didn&#8217;t really find a way to solve this, but I offer a fairly digestible workaround at the end of this post.</p>
<p>This is nevertheless an opportunity to discuss systemd dependencies and how systemd relates with /etc/fstab.</p>
<h3>The main takeaway: Use &#8220;nofail&#8221;</h3>
<p>This is only slightly related to the main topic, but nevertheless the most important conclusion: Open your /etc/fstab, and add &#8220;nofail&#8221; as an option to all mount points, except those that really are crucial for booting the system. That includes &#8220;/home&#8221; if it&#8217;s on a separate file system, as well as those huge file systems for storing a lot of large files. The rationale is simple: If any of these file systems are corrupt during boot, you really don&#8217;t want to be stuck on that basic textual screen (possibly with very small fonts), not having an idea what happened, and fsck asking you if you want to fix this and that. It&#8217;s much easier to figure out the problem when the failing mount points simply don&#8217;t mount, and take it from there. Not to mention that it&#8217;s much less stressful when the system is up and running. Even if not fully so.</p>
<p>Citing &#8220;man systemd.mount&#8221;: &#8220;With nofail, this mount will be only wanted, not required, by local-fs.target or remote-fs.target. This means that the boot will continue even if this mount point is not mounted successfully&#8221;.</p>
<p>To do this properly, make a tarball of /run/systemd/generator. Keep an eye on what&#8217;s in local-fs.target.requires/ vs. local-fs.target.wants/ in this directory. Only the absolutely necessary mounts should be in local-fs.target.requires/. In my system there&#8217;s only one symlink for &#8220;-.mount&#8221;, which is the root filesystem. That&#8217;s it.</p>
<p>Regardless, it&#8217;s a good idea that the root filesystem has the errors=remount-ro option, so if it&#8217;s mountable albeit with errors, the system still goes up.</p>
<p>This brief discussion will be clearer after reading through this post. Also see &#8220;nofail and dependencies&#8221; below for an elaboration on how systemd treats the &#8220;nofail&#8221; option.</p>
<h3>/etc/fstab and systemd</h3>
<p>At an early stage of the boot process, systemd-fstab-generator reads through /etc/fstab, and generates *.mount systemd units in /run/systemd/generator/. Inside this directory, it also generates and populates a local-fs.target.requires/ directory as well local-fs.target.wants/ in order to reflect the necessity of these units for the boot process (more about dependencies below).</p>
<p>So for example, in response to this row in /etc/fstab,</p>
<pre>/dev/mmcblk0p1 /mnt/tfcard vfat nofail 0 0</pre>
<p>the automatically generated file can be found as /run/systemd/generator/mnt-tfcard.mount as follows:</p>
<pre># Automatically generated by systemd-fstab-generator

[Unit]
SourcePath=/etc/fstab
Documentation=man:fstab(5) man:systemd-fstab-generator(8)

[Mount]
What=/dev/mmcblk0p1
Where=/mnt/tfcard
Type=vfat
Options=nofail</pre>
<p>Note that the file name of a mount unit must match the mount point (as it does in this case). This is only relevant when writing mount units manually, of course.</p>
<p>Which brings me to my first attempt to solve the problem: Namely, to copy the automatically generated file into /etc/systemd/system/, and enable it as if I wrote it myself. And then remove the row in /etc/fstab.</p>
<p>More precisely, I created this file as /etc/systemd/system/mnt-tfcard.mount:</p>
<pre>[Unit]
Description=Mount /dev/mmcblk0p1 as /mnt/tfcard

[Mount]
What=/dev/mmcblk0p1
Where=/mnt/tfcard
Type=vfat
Options=nofail

[Install]
WantedBy=multi-user.target</pre>
<p>Note that the WantedBy is necessary for enabling the unit. multi-user.target is a bit inaccurate for this purpose, but it failed for a different reason anyhow, so who cares. It should have been &#8220;WantedBy=local-fs.target&#8221;, I believe.</p>
<p>Anyhow, I then went:</p>
<pre># <strong>systemctl daemon-reload</strong>
# <strong>systemctl enable mnt-tfcard.mount</strong>
Created symlink from /etc/systemd/system/multi-user.target.wants/mnt-tfcard.mount to /etc/systemd/system/mnt-tfcard.mount.</pre>
<p>And then ran</p>
<pre># <strong>systemctl start mnt-tfcard.mount</strong></pre>
<p>but that was just stuck for a few seconds, and then an error message. There was no problem booting (because of &#8220;nofail&#8221;), but the mount wasn&#8217;t performed.</p>
<p>To investigate further, I <a rel="noopener" href="https://billauer.se/blog/2019/05/dbus-dump-systemd-debugging/" target="_blank">attached strace</a> to the systemd process (PID 1) while running the &#8220;systemctl start&#8221; command, and there was no fork. In other words, I had already then a good reason to suspect that the problem wasn&#8217;t a failed mount attempt, but that systemd didn&#8217;t go as far as trying. Which isn&#8217;t so surprising, given that the original complaint was a dependency problem.</p>
<p>Also, the command</p>
<pre># <strong>systemctl start dev-mmcblk0p1.device</strong></pre>
<p>didn&#8217;t finish. Checking with &#8220;systemctl&#8221;, it just said:</p>
<pre>UNIT                 LOAD   ACTIVE     SUB       JOB   DESCRIPTION
dev-mmcblk0p1.device loaded inactive   dead      start dev-mmcblk0p1.device

<span class="yadayada">[ ... ]</span></pre>
<p>So have we just learned? That it&#8217;s possible to create .mount units instead of populating /etc/fstab. And that the result is exactly the same. Why that would be useful, I don&#8217;t know.</p>
<p>See &#8220;man systemd-fstab-generator&#8221; about how the content of fstab is translated automatically to systemd units. Also see &#8220;man systemd-fsck@.service&#8221; regarding the service that runs fsck, and refer to &#8220;man systemd.mount&#8221; regarding mount units.</p>
<h3>A quick recap on systemd dependencies</h3>
<p>It&#8217;s important to make a distinction between dependencies that express requirements and dependencies that express the order of launching units (&#8220;temporal dependencies&#8221;).</p>
<p>I&#8217;ll start with the first sort: Those that express that if one unit is activated, other units need to be activated too (&#8220;pulled in&#8221; as it&#8217;s often referred to). Note that the requirement kind of dependencies <strong>don&#8217;t</strong> say anything about one unit waiting for another to complete its activation or anything of that sort. The whole point is to start a lot of units in parallel.</p>
<p>So first, we have Requires=. When it appears in a unit file, it means that the listed unit must be started if the current unit is started, and that if that listed unit fails, the current unit will fail as well.</p>
<p>In the opposite direction, there&#8217;s RequiredBy=. It&#8217;s exactly like putting a Required= directive in the unit that is listed.</p>
<p>Then we have another famous couple, Wants= and its opposite WantedBy=. These are the merciful counterparts of Requires= and RequriedBy=. The difference: If the listed unit fails, the other unit may activate successfully nevertheless. The failure is marked by the fact that &#8220;systemctl status&#8221; will declare the system as &#8220;degraded&#8221;.</p>
<p>So the fact that basically every service unit ends with &#8220;WantedBy=multi-user.target&#8221; means that the unit file says &#8220;if multi-user.target is a requested target (i.e. practically always), you need to activate me&#8221;, but at the same time it says &#8220;if I fail, don&#8217;t fail the entire boot process&#8221;. In other words, had RequriedBy= been used all over the place instead, it would have worked just the same, as long as all units started successfully. But if one of them didn&#8217;t, one would get a textual rescue shell instead of an almost fully functional (&#8220;degraded&#8221;) system.</p>
<p>There are a whole lot of other directives for defining dependencies. In particular, there are also Requisite=, ConsistsOf=, BindsTo=, PartOf=, Conflicts=, plus tons of possible directives for fine-tuning the behavior of the unit, including conditions for starting and whatnot. See &#8220;man systemd.unit&#8221;. There&#8217;s also a nice summary table on that man page.</p>
<p>It&#8217;s important to note that even if unit X requires unit Y by virtue of the directives mentioned above, systemd may (and often will) activate them at the same time. That&#8217;s true for both &#8220;require&#8221; and &#8220;want&#8221; kind of directives. In order to control the order of activation, we have After= and Before=. After= and Before= also ensure that the stopping of units occur in the reverse order.</p>
<p>After= and Before= only define <strong>when</strong> the units are started, and are often used in conjunction with the directives that define requirements. But by themselves, After= and Before= don&#8217;t define a relation of requirement between units.</p>
<p>It&#8217;s therefore common to use Required= and After= on the same listed unit, meaning that the listed unit must complete successfully before activating the unit for which these directives are given. Same with RequiredBy= and Before=, meaning that this unit must complete before the listed unit can start.</p>
<p>Once again, there are more commands, and more to say on those I just mentioned. See &#8220;man systemd.unit&#8221; for detailed info on this. Really, do. It&#8217;s a good man page. There&#8217;s a lot of &#8220;but if&#8221; to be aware of.</p>
<h3>&#8220;nofail&#8221; and dependencies</h3>
<p>One somewhat scary side-effect of adding &#8220;nofail&#8221; is that the line  saying &#8220;Before=local-fs.target&#8221; doesn&#8217;t appear in the  *.mount unit  files that are automatically generated. Does it mean that the services  might be started before these mounts have taken place?</p>
<p>I can&#8217;t say I&#8217;m  100% sure about this, but it would be really poor design if it was that  way, as it would have have added a very unexpected side-effect to  &#8220;nofail&#8221;. Looking at the system log of a boot with almost all mounts  with &#8220;nofail&#8221;, it&#8217;s evident that they were mounted <strong>after</strong> &#8220;Reached target Local File Systems&#8221; is announced, but before &#8220;Reached  target System Initialization&#8221;. Looking at a system log before adding  these &#8220;nofail&#8221; options, all mounts were made before &#8220;Reached target  Local File Systems&#8221;.</p>
<p>So what happens here? It&#8217;s worth mentioning  that in local-fs.target, it only says After=local-fs-pre.target in  relation to temporal dependencies. So that can explain why &#8220;Reached  target Local File Systems&#8221; is announced before all mounts have been  completed.</p>
<p>But then, local-fs.target is listed in sysinit.target&#8217;s   After= as well as Wants= directives. I haven&#8217;t found any clear-cut  definition to whether &#8220;After=&#8221; waits until all &#8220;wanted&#8221; units have been  fully started (or officially failed). The term that is used in &#8220;man  systemd.unit&#8221; in relation to waiting is &#8220;finished started up&#8221;, but what does that mean exactly?  This manpage also says &#8220;Most importantly, for service units start-up is  considered completed for the purpose of Before=/After= when all its  configured start-up commands have been invoked and they either failed or reported start-up success&#8221;. But what about &#8220;want&#8221; relations?</p>
<p>So I guess systemd interprets the &#8220;After&#8221; directive on local-fs.target in sysinit.target as &#8220;wait for local-fs.target&#8217;s temporal dependencies as well, even if local-fs.target doesn&#8217;t wait for them&#8221;.</p>
<h3>Querying dependencies</h3>
<p>So how can we know which unit depends on which? In order to get the system&#8217;s tree of dependencies, go</p>
<pre>$ systemctl list-dependencies</pre>
<p>This recursively shows the dependency tree that is created by Requires, RequiredBy, Wants, WantedBy and similar relations. Usually, only target units are followed recursively. In order to display the entire tree, add the &#8211;all flag.</p>
<p>The units that are listed are those that are required (or &#8220;wanted&#8221;) in order to reach the target that is being queried (&#8220;default.target&#8221; if no target specified).</p>
<p>As can be seen from the output, the filesystem mounts are made in order to reach the local-fs.target target. To get the tree of only this target:</p>
<pre>$ systemctl list-dependencies local-fs.target</pre>
<p>The output of this command are the units that are activated for the sake of reaching local-fs.target. &#8220;-.mount&#8221; is the root mount, by the way.</p>
<p>It&#8217;s also possible to obtain the reverse dependencies with the &#8211;reverse flag. In other words, in order to see which targets rely on a local-fs.target, go</p>
<pre>$ systemctl list-dependencies --reverse local-fs.target</pre>
<p>list-dependencies doesn&#8217;t make a distinction between &#8220;required&#8221; or &#8220;wanted&#8221;, and neither does it show the nuances of the various other possibilities for creating dependencies. For this, use &#8220;systemctl show&#8221;, e.g.</p>
<pre>$ systemctl show local-fs.target</pre>
<p>This lists all directives, explicit and implicit, that have been made on this unit. A lot of information to go through, but that&#8217;s the full picture.</p>
<p>If the &#8211;after flag is added, the tree of targets with an After= directive (and other implicit temporal dependencies, e.g. mirrored Before= directives) is printed out. Recall that this only tells us something about the order of execution, and nothing about which unit requires which. For example:</p>
<pre>$ systemctl list-dependencies --after local-fs.target</pre>
<p>Likewise, there&#8217;s &#8211;before, which does the opposite.</p>
<p>Note that the names of the flags are confusing: &#8211;after tells us about the targets that are started <strong>before</strong> local-fs.target, and &#8211;before tells us about the targets started <strong>after</strong> local-fs.target. The rationale: The spirit of list-dependencies (without &#8211;reverse) is that it tells us what is needed to reach the target. Hence following the After= directives tells us what had to be before. And vice versa.</p>
<h3>Red herring #1</h3>
<p>After this long general discussion about dependencies, let&#8217;s go back to the original problem: The mnt-tfcard.mount unit failed with status &#8220;dependency&#8221; and dev-mmcblk0p1.device had the status inactive / dead.</p>
<p>Could this be a dependency thing?</p>
<pre># <strong>systemctl list-dependencies dev-mmcblk0p1.device</strong>
dev-mmcblk0p2.device
<span style="color: #e03e2d;">●</span> └─mnt-tfcard.mount
# <strong>systemctl list-dependencies mnt-tfcard.mount</strong>
mnt-tfcard.mount
<span style="color: #e03e2d;">●</span> ├─dev-mmcblk0p1.device
<span style="color: #169179;">●</span> └─system.slice</pre>
<p>Say what? dev-mmcblk0p2.device depends on mnt-tfcard.mount? And even more intriguing, mnt-tfcard.mount depends on dev-mmcblk0p2.device, so it&#8217;s a circular dependency! This must be the problem! (not)</p>
<p>This was the result regardless of whether I used /etc/fstab or my own mount unit file instead. I also tried adding DefaultDependencies=no on mnt-tfcard.mount&#8217;s unit file, but that made no difference.</p>
<p>Neither did this dependency go away when the last parameter in /dev/fstab was changed from 0 to 2, indicating that fsck should be run on the block device prior to mount. The dependency changed, though. What did change was mnt-tfcard.mount&#8217;s dependencies, but that&#8217;s not the problem.</p>
<pre># <strong>systemctl list-dependencies mnt-tfcard.mount</strong>
mnt-tfcard.mount
<span style="color: #e03e2d;">●</span> ├─dev-mmcblk0p1.device
<span style="color: #169179;">●</span> ├─system.slice
<span style="color: #e03e2d;">●</span> └─systemd-fsck@dev-mmcblk0p1.service
# <strong>systemctl list-dependencies systemd-fsck@dev-mmcblk0p1.service</strong>
systemd-fsck@dev-mmcblk0p1.service
<span style="color: #e03e2d;">●</span> ├─dev-mmcblk0p1.device
<span style="color: #169179;">●</span> ├─system-systemd\x2dfsck.slice
<span style="color: #169179;">●</span> └─systemd-fsckd.socket</pre>
<p>But the thing is that the cyclic dependency isn&#8217;t an error. This is the same queries on the /boot mount (/dev/sda1, ext4) of another computer:</p>
<pre>$ <strong>systemctl list-dependencies boot.mount</strong>
boot.mount
<span style="color: #169179;">●</span> ├─-.mount
<span style="color: #169179;">●</span> ├─dev-disk-by\x2duuid-063f1689\x2d3729\x2d425e\x2d9319\x2dc815ccd8ecaf.device
<span style="color: #169179;">●</span> ├─system.slice
<span style="color: #169179;">●</span> └─systemd-fsck@dev-disk-by\x2duuid-063f1689\x2d3729\x2d425e\x2d9319\x2dc815ccd8ecaf.service</pre>
<p>And the back dependency:</p>
<pre>$ <strong>systemctl list-dependencies 'dev-disk-by\x2duuid-063f1689\x2d3729\x2d425e\x2d9319\x2dc815ccd8ecaf.device'</strong>
dev-disk-by\x2duuid-063f1689\x2d3729\x2d425e\x2d9319\x2dc815ccd8ecaf.device
<span style="color: #169179;">●</span> └─boot.mount</pre>
<p>The mount unit depends on the device unit and vice versa. Same thing, but this time it works.</p>
<p>Why does the device unit depend on the mount unit, one may ask. Not clear.</p>
<h3>Red herring #2</h3>
<p>At some point, I thought that the reason for the problem was that the filesystem&#8217;s &#8220;dirty bit&#8221; was set:</p>
<pre># <strong>fsck /dev/mmcblk0p1 </strong>
fsck from util-linux 2.27.1
fsck.fat 3.0.28 (2015-05-16)
0x25: Dirty bit is set. Fs was not properly unmounted and some data may be corrupt.
1) Remove dirty bit
2) No action</pre>
<p>By the way, using &#8220;fsck.vfat&#8221; instead of just &#8220;fsck&#8221; did exactly the same. The former is what the systemd-fsck service uses, according to the man page.</p>
<p>But this wasn&#8217;t the problem. Even after removing the dirty bit, and with fsck reporting success, dev-mmcblk0p1.device would not start.</p>
<h3>The really stinking fish</h3>
<p>What really is fishy on the system is this output of &#8220;systemctl &#8211;all&#8221;:</p>
<pre>dev-mmcblk0p2.device      loaded <span class="punch">activating tentative</span> /dev/mmcblk0p2</pre>
<p>/dev/mmcblk0p2 is the partition that is mounted as root! It should be &#8220;loaded active plugged&#8221;! All devices have that status, except for this one.</p>
<p>Lennart Poettering (i.e. the horse&#8217;s mouth) <a rel="noopener" href="https://github.com/systemd/systemd/issues/5781" target="_blank">mentions</a> that &#8220;If the device stays around in &#8220;tentative&#8221; state, then this indicates that a device appears in /proc/self/mountinfo with some name, and systemd can&#8217;t find a matching device in /sys for it, probably because for some reason it has a different name&#8221;.</p>
<p>And indeed, this is the relevant row in /proc/self/mountinfo:</p>
<pre>16 0 179:2 / / rw,relatime shared:1 - ext4 <span class="punch">/dev/root</span> rw,data=ordered</pre>
<p>So the root partition appears as /dev/root. This device file doesn&#8217;t even exist. The real one is /dev/mmcblk0p2, and it&#8217;s mentioned in the kernel&#8217;s command line. On this platform, Linux boots without any initrd image. The kernel mounts root by itself, and takes it from there.</p>
<p>As Lennart <a rel="noopener" href="https://systemd-devel.freedesktop.narkive.com/4zVGwj7X/dev-root-device-is-not-active-results-in-an-umount-spree" target="_blank">points out in a different place</a>, /dev/root is a special creature that is made up in relation to mounting a root filesystem by the kernel.</p>
<p>At this point, I realized that I&#8217;m up against a quirk that has probably been solved silently during the six years since the relevant distribution was released. In other words, no point wasting time looking for the root cause. So this calls for&#8230;</p>
<h3>The workaround</h3>
<p>With systemd around, writing a service is a piece of cake. So I wrote a trivial service which runs mount when it&#8217;s started and umount when it&#8217;s stopped. Not a masterpiece in terms of software engineering, but it gets the job done without polluting too much.</p>
<p>The service file, /etc/systemd/system/mount-card.service is as follows:</p>
<pre>[Unit]
Description=TF card automatic mount

[Service]
ExecStart=/bin/mount /dev/mmcblk0p1 /mnt/tfcard/
ExecStop=/bin/umount /mnt/tfcard/
Type=simple
RemainAfterExit=yes

[Install]
WantedBy=local-fs.target</pre>
<p>Activating the service:</p>
<pre># <strong>systemctl daemon-reload</strong>
# <strong>systemctl enable mount-card</strong>
Created symlink from /etc/systemd/system/local-fs.target.wants/mount-card.service to /etc/systemd/system/mount-card.service.</pre>
<p>And reboot.</p>
<p>The purpose of &#8220;RemainAfterExit=yes&#8221; is to make systemd consider the service active even after the command exits. Without this row, the command for ExecStop runs immediately after ExecStart, so the device is unmounted immediately after it has been mounted. Setting Type to oneshot doesn&#8217;t solve this issue, by the way. The only difference between &#8220;oneshot&#8221; and &#8220;simple&#8221; is that oneshot delays the execution of other units until it has exited.</p>
<p>There is no need to add an explicit dependency on the existence of the root filesystem, because all services have an implicit Required= and After= dependency on sysinit.target, which in turn depends on local-fs.target. This also ensures that the service is stopped before an attempt to remount root as read-only during shutdown.</p>
<p>As for choosing local-fs.target for the WantedBy, it&#8217;s somewhat questionable, because no service can run until sysinit.target has been reached, and the latter depends on local-fs.target, as just mentioned. More precisely (citing &#8220;man systemd.service&#8221;), &#8220;service units will have dependencies of type Requires= and After= on sysinit.target, a dependency of type After= on basic.target&#8221;. So this ensures that the service is started after the Basic Target has been reached and shuts down before this target is shut down.</p>
<p>That said, setting WantedBy this way is a more accurate expression of the purpose of this service. Either way, the result is that the unit is marked for activation on every boot.</p>
<p>I don&#8217;t know if this is the place to write &#8220;all well that ends well&#8221;. I usually try to avoid workarounds. But in this case it was really not worth the effort to insist on a clean solution. Not sure if it&#8217;s actually possible.</p>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2023/12/systemd-dependencies-fstab/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Linux Mint + Cinnamon: Volume buttons stop to work suddenly</title>
		<link>https://billauer.se/blog/2023/05/cinnamon-volume-buttons-dbus/</link>
		<comments>https://billauer.se/blog/2023/05/cinnamon-volume-buttons-dbus/#comments</comments>
		<pubDate>Wed, 31 May 2023 09:58:16 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[cinnamon]]></category>
		<category><![CDATA[Linux sound]]></category>
		<category><![CDATA[systemd]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=6875</guid>
		<description><![CDATA[The nuisance After trying to fix another problem with Pulseaudio, and messing up a bit with its configuration files, the option to control the volume by virtue of keyboard buttons suddenly stopped to work. The cursor would blink briefly when pressing the button, but nothing else happened. I tried all the fairly normal things, including [...]]]></description>
			<content:encoded><![CDATA[<h3>The nuisance</h3>
<p>After trying to fix another problem with Pulseaudio, and messing up a bit with its configuration files, the option to control the volume by virtue of keyboard buttons suddenly stopped to work. The cursor would blink briefly when pressing the button, but nothing else happened.</p>
<p>I tried all the fairly normal things, including to restart the Pulseaudio daemon, to restart Cinnamon (with <a title="When mplayer plays a black window (or: Cinnamon leaking GPU memory)" href="https://billauer.se/blog/2019/04/cinnamon-gpu-memory-leak-restart/" target="_blank">Alt-F2 + &#8220;r&#8221;</a>), restart the session by changing the user (to the same user), disconnect and reconnect the (USB) keyboard, and a whole lot of other things.</p>
<p>It took me quite some time to fix this, so here&#8217;s what worked for me this time, along with some debug info.</p>
<p>The machine was a Linux Mint 19 with Cinnamon.</p>
<p>At an earlier stage, even the sound applet was gone from its desktop panel. To fix that, I right-clicked the panel, picked Troubleshoot &gt; Looking Glass, and picked the Extension tab. There&#8217;s an applet listed called &#8220;Sound&#8221;. So I right-clicked it, and chose &#8220;Reload Code&#8221;. That got the applet back to the panel, and it worked flawlessly. But then I was stuck with the volume button problem.</p>
<h3>The fix</h3>
<p>What I actually did:</p>
<ul>
<li>Kill the process named csd-keyboard (probably irrelevant)</li>
<li>Kill the process named csd-media-keys</li>
<li>Restart pulseaudio with &#8220;pulseaudio -k&#8221;</li>
</ul>
<p>This was the output of journalctl as a result of these:</p>
<pre>May 31 12:17:03 cinnamon-session[2115]: WARNING: t+1132491.55749s: Application 'cinnamon-settings-daemon-keyboard.desktop' killed by signal 15
May 31 12:17:17 cinnamon-session[2115]: WARNING: t+1132505.78238s: Application 'cinnamon-settings-daemon-media-keys.desktop' killed by signal 15
May 31 12:17:17 rtkit-daemon[2284]: Successfully made thread 4304 of process 4304 (n/a) owned by '1010' high priority at nice level -11.
May 31 12:17:17 rtkit-daemon[2284]: Supervising 8 threads of 4 processes of 1 users.
<span class="punch">May 31 12:17:17 pulseaudio[4304]: [pulseaudio] pid.c: Daemon already running.
</span>May 31 12:18:04 bluetoothd[12009]: Endpoint unregistered: sender=:1.120210 path=/MediaEndpoint/A2DPSource
May 31 12:18:04 bluetoothd[12009]: Endpoint unregistered: sender=:1.120210 path=/MediaEndpoint/A2DPSink
May 31 12:18:04 rtkit-daemon[2284]: Successfully made thread 4345 of process 4345 (n/a) owned by '1010' high priority at nice level -11.
May 31 12:18:04 rtkit-daemon[2284]: Supervising 4 threads of 4 processes of 1 users.
May 31 12:18:04 pulseaudio[4345]: [pulseaudio] sink.c: Default and alternate sample rates are the same.
May 31 12:18:04 rtkit-daemon[2284]: Supervising 3 threads of 3 processes of 1 users.
May 31 12:18:04 rtkit-daemon[2284]: Successfully made thread 4347 of process 4345 (n/a) owned by '1010' RT at priority 5.
May 31 12:18:04 rtkit-daemon[2284]: Supervising 4 threads of 3 processes of 1 users.
May 31 12:18:04 rtkit-daemon[2284]: Supervising 4 threads of 3 processes of 1 users.
May 31 12:18:04 rtkit-daemon[2284]: Successfully made thread 4348 of process 4345 (n/a) owned by '1010' RT at priority 5.
May 31 12:18:04 rtkit-daemon[2284]: Supervising 5 threads of 3 processes of 1 users.
May 31 12:18:04 rtkit-daemon[2284]: Supervising 5 threads of 3 processes of 1 users.
May 31 12:18:04 rtkit-daemon[2284]: Successfully made thread 4349 of process 4345 (n/a) owned by '1010' RT at priority 5.
May 31 12:18:04 rtkit-daemon[2284]: Supervising 6 threads of 3 processes of 1 users.
May 31 12:18:05 rtkit-daemon[2284]: Supervising 6 threads of 3 processes of 1 users.
May 31 12:18:05 rtkit-daemon[2284]: Successfully made thread 4350 of process 4345 (n/a) owned by '1010' RT at priority 5.
May 31 12:18:05 rtkit-daemon[2284]: Supervising 7 threads of 3 processes of 1 users.
May 31 12:18:05 bluetoothd[12009]: Endpoint registered: sender=:1.120482 path=/MediaEndpoint/A2DPSource
May 31 12:18:05 bluetoothd[12009]: Endpoint registered: sender=:1.120482 path=/MediaEndpoint/A2DPSink
May 31 12:18:05 pulseaudio[4345]: [pulseaudio] backend-ofono.c: Failed to register as a handsfree audio agent with ofono: org.freedesktop.DBus.Error.ServiceUnknown: The name org.ofono was not provided by any .service files
May 31 12:18:05 rtkit-daemon[2284]: Successfully made thread 4352 of process 4352 (n/a) owned by '1010' high priority at nice level -11.
May 31 12:18:05 rtkit-daemon[2284]: Supervising 8 threads of 4 processes of 1 users.
May 31 12:18:05 pulseaudio[4352]: [pulseaudio] pid.c: Daemon already running.
May 31 12:18:58 gnome-keyring-daemon[2204]: asked to register item /org/freedesktop/secrets/collection/login/9, but it's already registered</pre>
<p>I suppose that killing csd-media-keys was the part that really did the trick, because that caused a request to start Pulseaudio.</p>
<h3>Why it worked (presumably)</h3>
<p>The cinnamon-settings-daemon is the component responsible for passing the keyboard request to Pulseaudio. Apparently, messing up with Pulseaudio caused one of its components, csd-media-keys, to mess up as well. Killing it caused cinnamon-settings-daemon to restart it automatically. From fresh.</p>
<p>It&#8217;s fine to kill the csd-* processes. Nothing dramatic happens. The killed process is just restarted. But I haven&#8217;t tried to kill cinnamon-settings-daemon itself. I believe it will restart Cinnamon completely, something I wanted to avoid.</p>
<h3>Dbus activity</h3>
<p>I&#8217;ve already <a title="systemd / DBus debugging starter pack" href="https://billauer.se/blog/2019/05/dbus-dump-systemd-debugging/" target="_blank">written a post</a> mentioning how to monitor the Dbus. So here&#8217;s a different take on the same topic.</p>
<p>The difference is quite evident when running dbus-monitor (not as root) as follows in order to monitor the activity for the current session:</p>
<pre>$ dbus-monitor</pre>
<p>This is the output as a result of pressing a volume up button, when it works correctly (i.e. after fixing the problem):</p>
<pre>method call time=1685525392.173838 sender=:<span class="punch">1.6531</span> -&gt; destination=:<span class="punch">1.6616</span> serial=1431 path=/org/cinnamon/SettingsDaemon/KeybindingHandler; interface=org.cinnamon.SettingsDaemon.KeybindingHandler; member=HandleKeybinding
   uint32 2
method call time=1685525392.174967 sender=:1.6616 -&gt; destination=org.freedesktop.DBus serial=566 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
   string "type='signal',interface='ca.desrt.dconf.Writer',path='/ca/desrt/dconf/Writer/user',arg0path='/org/cinnamon/desktop/sound/'"
method return time=1685525392.175012 sender=org.freedesktop.DBus -&gt; destination=:1.6616 serial=382 reply_serial=566
method call time=1685525392.175026 sender=:1.6616 -&gt; destination=:1.6531 serial=567 path=/org/Cinnamon; interface=org.Cinnamon; member=ShowOSD
   array [
      dict entry(
         string "icon"
         variant             string "audio-volume-medium-symbolic"
      )
      dict entry(
         string "level"
         variant             int32 34
      )
   ]
method call time=1685525392.175170 sender=:1.6616 -&gt; destination=org.freedesktop.DBus serial=568 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=RemoveMatch
   string "type='signal',interface='ca.desrt.dconf.Writer',path='/ca/desrt/dconf/Writer/user',arg0path='/org/cinnamon/desktop/sound/'"
method return time=1685525392.175189 sender=org.freedesktop.DBus -&gt; destination=:1.6616 serial=383 reply_serial=568
method call time=1685525392.175198 sender=:1.6616 -&gt; destination=org.freedesktop.DBus serial=569 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
   string "type='signal',interface='ca.desrt.dconf.Writer',path='/ca/desrt/dconf/Writer/user',arg0path='/org/cinnamon/desktop/sound/'"
method return time=1685525392.175216 sender=org.freedesktop.DBus -&gt; destination=:1.6616 serial=384 reply_serial=569
method call time=1685525392.176330 sender=:<span class="punch">1.6618</span> -&gt; destination=org.freedesktop.DBus serial=26 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=RequestName
   string "org.freedesktop.ReserveDevice1.Audio2"
   uint32 5
signal time=1685525392.176383 sender=org.freedesktop.DBus -&gt; destination=(null destination) serial=50 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
   string "org.freedesktop.ReserveDevice1.Audio2"
   string ""
   string ":1.6618"
signal time=1685525392.176411 sender=org.freedesktop.DBus -&gt; destination=:1.6618 serial=51 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameAcquired
   string "org.freedesktop.ReserveDevice1.Audio2"
method return time=1685525392.176427 sender=org.freedesktop.DBus -&gt; destination=:1.6618 serial=52 reply_serial=26
   uint32 1
method return time=1685525392.184853 sender=:1.6616 -&gt; destination=:1.6531 serial=570 reply_serial=1431
method call time=1685525392.184915 sender=:1.6616 -&gt; destination=org.freedesktop.DBus serial=571 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=RemoveMatch
   string "type='signal',interface='ca.desrt.dconf.Writer',path='/ca/desrt/dconf/Writer/user',arg0path='/org/cinnamon/desktop/sound/'"
method return time=1685525392.184937 sender=org.freedesktop.DBus -&gt; destination=:1.6616 serial=385 reply_serial=571
method return time=1685525392.190757 sender=:1.6531 -&gt; destination=:1.6616 serial=1432 reply_serial=567</pre>
<p>Unfortunately, I didn&#8217;t save the output before the fix. But it was exactly two rows of output. As far as I recall, the first and last row from this dump were present when it didn&#8217;t work (i.e. the row involving KeybindingHandler&#8217;s request, and the return method that closed it).</p>
<p>The bus addresses can be listed with the following command:</p>
<pre>$ busctl <span style="color: #ff0000;"><strong>--user</strong></span></pre>
<p>In this specific session the output was:</p>
<pre>NAME                                                       PID PROCESS         USER             CONNECTION    UNIT                      SESSION    DESCRIPTION
:1.0                                                      2103 systemd         eli              :1.0          user@1010.service         -          -
:1.1014                                                  45251 pxgsettings     eli              :1.1014       session-c1.scope          c1         -
:1.1015                                                  45277 scp-dbus-servic eli              :1.1015       user@1010.service         -          -
:1.1016                                                  45277 scp-dbus-servic eli              :1.1016       user@1010.service         -          -
:1.12                                                     2213 csd-a11y-keyboa eli              :1.12         session-c1.scope          c1         -
:1.1234                                                  53331 nemo            eli              :1.1234       session-c1.scope          c1         -
:1.13                                                     2221 csd-a11y-settin eli              :1.13         session-c1.scope          c1         -
:1.131                                                   16564 klauncher       eli              :1.131        session-c1.scope          c1         -
:1.16                                                     2232 csd-cursor      eli              :1.16         session-c1.scope          c1         -
:1.17                                                     2225 csd-color       eli              :1.17         session-c1.scope          c1         -
:1.18                                                     2238 csd-clipboard   eli              :1.18         session-c1.scope          c1         -
:1.19                                                     2242 csd-orientation eli              :1.19         session-c1.scope          c1         -
:1.20                                                     2233 csd-wacom       eli              :1.20         session-c1.scope          c1         -
:1.21                                                     2245 csd-xsettings   eli              :1.21         session-c1.scope          c1         -
:1.22                                                     2248 csd-mouse       eli              :1.22         session-c1.scope          c1         -
:1.24                                                     2256 csd-print-notif eli              :1.24         session-c1.scope          c1         -
:1.26                                                     2258 csd-background  eli              :1.26         session-c1.scope          c1         -
:1.27                                                     2260 gvfsd           eli              :1.27         user@1010.service         -          -
:1.28                                                     2267 csd-housekeepin eli              :1.28         session-c1.scope          c1         -
:1.29                                                     2274 csd-screensaver eli              :1.29         session-c1.scope          c1         -
:1.30                                                     2270 csd-power       eli              :1.30         session-c1.scope          c1         -
:1.31                                                     2299 dconf-service   eli              :1.31         user@1010.service         -          -
:1.32                                                     2285 csd-automount   eli              :1.32         session-c1.scope          c1         -
:1.329                                                   22590 xreader         eli              :1.329        session-c1.scope          c1         -
:1.33                                                     2306 gvfsd-fuse      eli              :1.33         user@1010.service         -          -
:1.330                                                   22596 xreaderd        eli              :1.330        user@1010.service         -          -
:1.332                                                   22606 WebKitNetworkPr eli              :1.332        session-c1.scope          c1         -
:1.35                                                     2313 csd-printer     eli              :1.35         session-c1.scope          c1         -
:1.37                                                     2349 gvfs-udisks2-vo eli              :1.37         user@1010.service         -          -
:1.38                                                     2384 gvfs-gphoto2-vo eli              :1.38         user@1010.service         -          -
:1.39                                                     2277 csd-xrandr      eli              :1.39         session-c1.scope          c1         -
:1.40                                                     2410 gvfs-goa-volume eli              :1.40         user@1010.service         -          -
:1.41                                                     2414 goa-daemon      eli              :1.41         user@1010.service         -          -
:1.42                                                     2427 goa-identity-se eli              :1.42         user@1010.service         -          -
:1.43                                                     2432 gvfs-mtp-volume eli              :1.43         user@1010.service         -          -
:1.44                                                     2436 gvfs-afc-volume eli              :1.44         user@1010.service         -          -
:1.448                                                   31116 gvfsd-http      eli              :1.448        user@1010.service         -          -
:1.457                                                   31367 gvfsd-network   eli              :1.457        user@1010.service         -          -
:1.46                                                     2472 polkit-gnome-au eli              :1.46         session-c1.scope          c1         -
:1.463                                                   31401 gvfsd-dnssd     eli              :1.463        user@1010.service         -          -
:1.47                                                     2481 cinnamon-killer eli              :1.47         session-c1.scope          c1         -
:1.48                                                     2478 nemo-desktop    eli              :1.48         session-c1.scope          c1         -
:1.49                                                     2480 nm-applet       eli              :1.49         session-c1.scope          c1         -
:1.5                                                      2115 cinnamon-sessio eli              :1.5          session-c1.scope          c1         -
:1.50                                                     2474 blueberry-obex- eli              :1.50         session-c1.scope          c1         -
:1.51                                                     2474 blueberry-obex- eli              :1.51         session-c1.scope          c1         -
:1.52                                                     2499 obexd           eli              :1.52         user@1010.service         -          -
:1.54                                                     2506 gvfsd-trash     eli              :1.54         user@1010.service         -          -
:1.55                                                     2512 gvfsd-metadata  eli              :1.55         user@1010.service         -          -
:1.56                                                     2535 python2         eli              :1.56         session-c1.scope          c1         -
:1.5653                                                  62813 chrome          eli              :1.5653       session-c1.scope          c1         -
:1.5654                                                  62813 chrome          eli              :1.5654       session-c1.scope          c1         -
:1.5655                                                  62813 chrome          eli              :1.5655       session-c1.scope          c1         -
:1.5656                                                  62813 chrome          eli              :1.5656       session-c1.scope          c1         -
:1.5661                                                  62813 chrome          eli              :1.5661       session-c1.scope          c1         -
:1.57                                                     2534 cinnamon-screen eli              :1.57         session-c1.scope          c1         -
:1.58                                                     2574 gnome-terminal  eli              :1.58         session-c1.scope          c1         -
:1.5834                                                  52878 xreader         eli              :1.5834       session-c1.scope          c1         -
:1.5836                                                  52891 WebKitNetworkPr eli              :1.5836       session-c1.scope          c1         -
:1.5842                                                  54509 xreader         eli              :1.5842       session-c1.scope          c1         -
:1.5843                                                  54522 WebKitNetworkPr eli              :1.5843       session-c1.scope          c1         -
:1.59                                                     2574 gnome-terminal  eli              :1.59         session-c1.scope          c1         -
:1.6                                                      2115 cinnamon-sessio eli              :1.6          session-c1.scope          c1         -
:1.60                                                     2579 gconfd-2        eli              :1.60         user@1010.service         -          -
:1.61                                                     2574 gnome-terminal  eli              :1.61         session-c1.scope          c1         -
:1.6105                                                  47745 xreader         eli              :1.6105       session-c1.scope          c1         -
:1.6107                                                  47757 WebKitNetworkPr eli              :1.6107       session-c1.scope          c1         -
:1.6477                                                  31538 thunderbird-bin eli              :1.6477       session-c1.scope          c1         -
:1.6478                                                  31538 thunderbird-bin eli              :1.6478       session-c1.scope          c1         -
<span class="punch">:1.6531                                                   2451 cinnamon        eli              :1.6531       session-c1.scope          c1         -
</span>:1.6562                                                  69100 csd-sound       eli              :1.6562       session-c1.scope          c1         -
:1.6592                                                   1641 firefox-bin     eli              :1.6592       session-c1.scope          c1         -
:1.6593                                                   1641 firefox-bin     eli              :1.6593       session-c1.scope          c1         -
:1.6594                                                   1700 Web Content     eli              :1.6594       session-c1.scope          c1         -
:1.6595                                                   1760 WebExtensions   eli              :1.6595       session-c1.scope          c1         -
:1.6596                                                   1824 Web Content     eli              :1.6596       session-c1.scope          c1         -
:1.6597                                                   1876 Web Content     eli              :1.6597       session-c1.scope          c1         -
:1.6598                                                   1975 cinnamon-settin eli              :1.6598       session-c1.scope          c1         -
:1.6599                                                   1975 cinnamon-settin eli              :1.6599       session-c1.scope          c1         -
:1.6608                                                   2640 Web Content     eli              :1.6608       session-c1.scope          c1         -
:1.6609                                                   3399 Web Content     eli              :1.6609       session-c1.scope          c1         -
:1.6615                                                   4286 csd-keyboard    eli              :1.6615       session-c1.scope          c1         -
<span class="punch">:1.6616                                                   4296 csd-media-keys  eli              :1.6616       session-c1.scope          c1         -
</span>:1.6617                                                   4296 csd-media-keys  eli              :1.6617       session-c1.scope          c1         -
<span class="punch">:1.6618                                                   4345 pulseaudio      eli              :1.6618       session-c1.scope          c1         -
</span>:1.6621                                                   4692 xed             eli              :1.6621       session-c1.scope          c1         -
:1.6625                                                   5225 busctl          eli              :1.6625       session-c1.scope          c1         -
:1.67                                                     2860 applet.py       eli              :1.67         session-c1.scope          c1         -
:1.7                                                      2189 at-spi-bus-laun eli              :1.7          user@1010.service         -          -
:1.73                                                     3537 gnome-system-mo eli              :1.73         session-c1.scope          c1         -
:1.8                                                      2196 at-spi2-registr eli              :1.8          user@1010.service         -          -
:1.9                                                      2204 gnome-keyring-d eli              :1.9          session-c1.scope          c1         -
:1.92                                                     8884 xdg-desktop-por eli              :1.92         user@1010.service         -          -
:1.93                                                     8888 xdg-document-po eli              :1.93         user@1010.service         -          -
:1.94                                                     8891 xdg-permission- eli              :1.94         user@1010.service         -          -
:1.95                                                     8902 pxgsettings     eli              :1.95         user@1010.service         -          -
:1.96                                                     8906 xdg-desktop-por eli              :1.96         user@1010.service         -          -
ca.desrt.dconf                                            2299 dconf-service   eli              :1.31         user@1010.service         -          -
ca.desrt.dconf-editor                                        - -               -                (activatable) -                         -
org.Cinnamon                                              2451 cinnamon        eli              :1.6531       session-c1.scope          c1         -
org.Cinnamon.HotplugSniffer                                  - -               -                (activatable) -                         -
org.Cinnamon.LookingGlass                                 2451 cinnamon        eli              :1.6531       session-c1.scope          c1         -
org.Cinnamon.Melange                                         - -               -                (activatable) -                         -
org.Cinnamon.Slideshow                                       - -               -                (activatable) -                         -
org.Nemo                                                 53331 nemo            eli              :1.1234       session-c1.scope          c1         -
org.NemoDesktop                                           2478 nemo-desktop    eli              :1.48         session-c1.scope          c1         -
org.PulseAudio1                                           4345 pulseaudio      eli              :1.6618       session-c1.scope          c1         -
org.a11y.Bus                                              2189 at-spi-bus-laun eli              :1.7          user@1010.service         -          -
org.bluez.obex                                            2499 obexd           eli              :1.52         user@1010.service         -          -
org.cinnamon.ScreenSaver                                  2534 cinnamon-screen eli              :1.57         session-c1.scope          c1         -
org.cinnamon.SettingsDaemon.KeybindingHandler             4296 csd-media-keys  eli              :1.6616       session-c1.scope          c1         -</pre>
<p>So it&#8217;s quite clear that the players here were cinnamon&#8217;s main socket, csd-media-keys and pulseaudio.</p>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2023/05/cinnamon-volume-buttons-dbus/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>systemd: Shut down computer at a certain uptime</title>
		<link>https://billauer.se/blog/2021/01/systemd-money-saver/</link>
		<comments>https://billauer.se/blog/2021/01/systemd-money-saver/#comments</comments>
		<pubDate>Sun, 31 Jan 2021 17:32:17 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[systemd]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=6226</guid>
		<description><![CDATA[Motivation Paid-per-time cloud services. I don&#8217;t want to forget one of those running, just to get a fat bill at the end of the month. And if the intended use is short sessions anyhow, make sure that the machine shuts down by itself after a given amount of time. Just make sure that a shutdown [...]]]></description>
			<content:encoded><![CDATA[<h3>Motivation</h3>
<p>Paid-per-time cloud services. I don&#8217;t want to forget one of those running, just to get a fat bill at the end of the month. And if the intended use is short sessions anyhow, make sure that the machine shuts down by itself after a given amount of time. Just make sure that a shutdown by the machine itself accounts for cutting the costs. And sane cloud provider does that except for, possibly, costs for storing the VM&#8217;s disk image.</p>
<p>So this is the cloud computing parallel to &#8220;did I lock the door?&#8221;.</p>
<p>The examples here are based upon systemd 241 on Debian GNU/Linux 10.</p>
<h3>The main service</h3>
<p>There is more than one way to do this. I went for two services: One that calls /sbin/shutdown with a five minute delay (so I get a chance to cancel it) and then second is a timer for the uptime limit.</p>
<p>So the main service is this file as /etc/systemd/system/uptime-limiter.service:</p>
<pre>[Unit]
Description=Limit uptime service

[Service]
ExecStart=/sbin/shutdown -h +5 "System it taken down by uptime-limit.service"
Type=simple

[Install]
WantedBy=multi-user.target</pre>
<p>The naïve approach is to just enable the service and expect it to work. Well, it does work when started manually, but when this service starts as part of the system bringup, the shutdown request is registered but later ignored. Most likely because systemd somehow cancels pending shutdown requests when it reaches the ultimate target.</p>
<p>I should mention that adding After=multi-user.target in the unit file didn&#8217;t help. Maybe some other target. Don&#8217;t know.</p>
<h3>The timer service</h3>
<p>So the way to ensure that the shutdown command is respected is to trigger it off with a timer service.</p>
<p>The timer service as /etc/systemd/system/uptime-limiter.timer, in this case allows for 6 hours of uptime (plus the extra 5 minutes given by the main service):</p>
<pre>[Unit]
Description=Timer for Limit uptime service

[Timer]
OnBootSec=6h
AccuracySec=1s

[Install]
WantedBy=timers.target</pre>
<p>and enable it:</p>
<pre># <strong>systemctl enable uptime-limiter<span style="color: #ff0000;">.timer</span></strong>
Created symlink /etc/systemd/system/timers.target.wants/uptime-limiter.timer → /etc/systemd/system/uptime-limiter.timer.</pre>
<p>Note two things here: That I enabled the timer, not the service itself, by adding the .timer suffix. And I didn&#8217;t start it. For that, there&#8217;s the &#8211;now flag.</p>
<p>So there are two steps: When the timer fires off, the call to /sbin/shutdown takes place, and that causes nagging wall messages to start once a minute, and eventually a shutdown. Mission complete.</p>
<h3>What timers are pending</h3>
<p>Ah, that&#8217;s surprisingly easy:</p>
<pre># <strong>systemctl list-timers</strong>
NEXT                         LEFT          LAST                         PASSED       UNIT                         ACTIVATES
Sun 2021-01-31 17:38:28 UTC  14min left    n/a                          n/a          systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
Sun 2021-01-31 20:50:22 UTC  3h 26min left Sun 2021-01-31 12:36:41 UTC  4h 47min ago apt-daily.timer              apt-daily.service
<span style="color: #ff0000;"><strong>Sun 2021-01-31 23:23:28 UTC  5h 59min left n/a                          n/a          uptime-limiter.timer         uptime-limiter.service
</strong></span>Sun 2021-01-31 23:23:34 UTC  5h 59min left Sun 2021-01-31 17:23:34 UTC  44s ago      google-oslogin-cache.timer   google-oslogin-cache.service
Mon 2021-02-01 00:00:00 UTC  6h left       Sun 2021-01-31 12:36:41 UTC  4h 47min ago logrotate.timer              logrotate.service
Mon 2021-02-01 00:00:00 UTC  6h left       Sun 2021-01-31 12:36:41 UTC  4h 47min ago man-db.timer                 man-db.service
Mon 2021-02-01 06:49:19 UTC  13h left      Sun 2021-01-31 12:36:41 UTC  4h 47min ago apt-daily-upgrade.timer      apt-daily-upgrade.service</pre>
<p>Clean and simple. And this is probably why this method is better than a long delay on shutdown, which is less clear about what it&#8217;s about to do, as shown next.</p>
<p>Note that a timer service can be stopped, which is parallel to canceling a shutdown. Restarting it to push the time limit further won&#8217;t work in this case, because the service is written related to OnBootSec.</p>
<h3>Is there a shutdown pending?</h3>
<p>To check if a shutdown is about to happen:</p>
<pre>$ <strong>cat /run/systemd/shutdown/scheduled</strong>
USEC=<span style="color: #ff0000;"><strong>1612103418427661</strong></span>
WARN_WALL=1
MODE=poweroff
WALL_MESSAGE=System it taken down by uptime-limit.service</pre>
<p>There are different reports on what happens when the shutdown is canceled. On my system, the file was deleted in response to &#8220;shutdown -c&#8221;, but not when the shutdown was canceled because the system had just booted up. There&#8217;s <a href="https://unix.stackexchange.com/questions/229745/systemd-how-to-check-scheduled-time-of-a-delayed-shutdown" target="_blank">other suggested</a> ways too, but in the end, it appears like there&#8217;s no definite way to tell if a system has a shutdown scheduled or not. At least not as of systemd 241.</p>
<p>That USEC line is the epoch time for when shutdown will take place. A Perl guy like me goes</p>
<pre>$ perl -e 'print scalar gmtime(<span style="color: #ff0000;"><strong>1612103418427661</strong></span>/1e6)'</pre>
<p>but that&#8217;s me.</p>
<h3>What didn&#8217;t work</h3>
<p>So this shows what <strong>doesn&#8217;t</strong> work: Enable the main service (as well as start it right away with the &#8211;now flag):</p>
<p><span style="text-decoration: line-through;"> </span></p>
<pre># <strong>systemctl enable --now uptime-limiter</strong>
Created symlink /etc/systemd/system/multi-user.target.wants/uptime-limiter.service → /etc/systemd/system/uptime-limiter.service.

Broadcast message from root@instance-1 (Sun 2021-01-31 14:15:19 UTC):

System it taken down by uptime-limit.service
The system is going down for poweroff at Sun 2021-01-31 14:25:19 UTC!</pre>
<p>So the broadcast message is out there right away. But this is misleading: It won&#8217;t work at all when the service is started automatically during system boot.</p>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2021/01/systemd-money-saver/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Systemd services as cronjobs: No process runs away</title>
		<link>https://billauer.se/blog/2021/01/systemd-cron-cgroups/</link>
		<comments>https://billauer.se/blog/2021/01/systemd-cron-cgroups/#comments</comments>
		<pubDate>Mon, 18 Jan 2021 06:10:06 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[Server admin]]></category>
		<category><![CDATA[systemd]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=6214</guid>
		<description><![CDATA[But why? Cronjobs typically consists of a single utility which we&#8217;re pretty confident about. Even if it takes quite some time to complete (updatedb, for example), there&#8217;s always a simple story, a single task to complete with a known beginning and end. If the task involves a shell script that calls a few utilities, that [...]]]></description>
			<content:encoded><![CDATA[<h3>But why?</h3>
<p>Cronjobs typically consists of a single utility which we&#8217;re pretty confident about. Even if it takes quite some time to complete (updatedb, for example), there&#8217;s always a simple story, a single task to complete with a known beginning and end.</p>
<p>If the task involves a shell script that calls a few utilities, that feeling of control fades. It&#8217;s therefore reassuring to know that everything can be cleaned up neatly by simple stopping a service. Systemd is good at that, since all processes that are involved in the service are kept in a separate cgroup. So when the service is stopped, all processes that were possibly generated eventually get a SIGKILL, typically 90 seconds after the request to stop the service, unless they terminated voluntarily in response to the initial SIGTERM.</p>
<p>Advantage number two is that the systemd allows for a series of capabilities to limit what the cronjob is capable of doing, thanks to the cgroup arrangement. This doesn&#8217;t fall very short from the possibilities of container virtualization, with pretty simple assignments in the unit file. This includes making certain directories inaccessible or accessible for read-only, setting up temporary directories, disallow external network connection, limit the set of allowed syscalls, and of course limit the amount of resources that are consumed by the service. They&#8217;re called Control Groups for a reason.</p>
<p>There&#8217;s also the RuntimeMaxSec parameter in the service unit file, which is the maximal wall clock time the service is allowed to run. The service is terminated and put in failure state if this time   is exceeded. This is however supported from systemd version 229 and later, so check with  &#8220;systemctl &#8211;version&#8221;.</p>
<p>My original idea was to use systemd timers to kick off the job, and let RuntimeMaxSec make sure it would get cleaned up if it ran too long (i.e. got stuck somehow). But because the server in question ran a rather old version of systemd, I went for a cron entry for starting the service and another one for stopping it, with a certain time difference between them. In hindsight, cron turned to be neater for kicking off the jobs, because I had multiple variants of them in different times. So one single file enclosed all.</p>
<p>The main practical difference is that if a service reaches RuntimeMaxSec, it&#8217;s terminated with a failed status. The cron solution stops the service without this. I guess there&#8217;s a systemctl way to achieve the failed status, if that&#8217;s really important.</p>
<p>As a side note, I have a <a href="https://billauer.se/blog/2020/06/firejail-cgroups/" target="_blank">separate post</a> on Firejail, which is yet another possibility to use cgroups for controlling what processes do.</p>
<h3>Timer basics</h3>
<p>The idea is simple: A service can be started as a result of a timer event. That&#8217;s all that timer units do.</p>
<p>Timer units are configured like any systemd units (man systemd.unit)   but have a .timer suffix and a dedicated [Timer] section. By  convention,  the timer unit named foo.timer activates the service  foo.service,  unless specified differently with the Unit= assignment  (useful for  generating confusion).</p>
<p>Units that are already running when the timer event occurs are not restarted, but are left to keep running. Exactly like systemctl start would do.</p>
<p>For an cronjob-style timer, use OnCalendar= to specify the times. See  man systemd.time for the format. Note that AccuracySec= should be set  too to control how much systemd can play with the exact time of  execution, or systemd&#8217;s behavior might be confusing.</p>
<p>To see all active timers, go</p>
<pre>$ systemctl list-timers</pre>
<h3>The unit file</h3>
<p>As usual, the unit file (e.g. /etc/systemd/system/cronjob-test@.service) is short and concise:</p>
<pre>[Unit]
Description=Cronjob test service

[Service]
ExecStart=/home/eli/shellout/utils/shellout.pl "%I"
Type=simple
User=eli
WorkingDirectory=/home/eli/shellout/utils
KillMode=mixed
<span style="text-decoration: line-through;">NoNewPrivileges=true</span></pre>
<p>This is a simple service, meaning that systemd expects the process launched by ExecStart to run in the foreground.</p>
<p>Note however that the service unit&#8217;s file name has a &#8220;@&#8221; character and that %I is used to choose what to run, based upon the unescaped instance name (see main systemd.unit). This turns the unit file into a template, and allows choosing an arbitrary command (the shellout.pl script is explained below) with something like (really, this works)</p>
<pre># systemctl start cronjob-test@'echo "Hello, world"'</pre>
<p>This might seems dangerous, but recall that root privileges are required  to start the service, and you get a plain-user process (possibly with no ability  to escalate privileges) in return. Not the big jackpot.</p>
<p>For stopping the service, exactly the same service specifier string is required. But it&#8217;s also possible to stop all instances of a service with</p>
<pre># systemctl stop 'cronjob-test@*'</pre>
<p>How neat is that?</p>
<p>A few comments on this:</p>
<ul>
<li>The service should not be systemd-wise enabled (i.e. no &#8220;systemctl enable&#8221;) &#8212; that&#8217;s what you do to get it started on boot or following some kind of event. This is not the case, as the whole point is to start the service directly by a timer or crond.</li>
<li>Accordingly, the service unit file does <strong>not</strong> have an [Install] section.</li>
<li>A side effect of this is that the service may not appear in the list made by &#8220;systemctl&#8221; (without any arguments) unless it has processes running on its behalf currently running (or possibly if it&#8217;s in the failed state). Simple logic: It&#8217;s not loaded unless it has a cgroup allocated, and the cgroup is removed along with the last process. But it may appear anyhow under some conditions.</li>
<li>ExecStart must have a full path (i.e. not relative) even if the WorkingDirectory is set. In particular, it can&#8217;t be ./something.</li>
<li>A &#8220;systemctl start&#8221; on a service that is marked as failed will be started anyhow (i.e. the fact that it&#8217;s marked failed doesn&#8217;t prevent that). Quite obvious, but I tested it to be sure.</li>
<li>Also, a &#8220;systemctl start&#8221; causes the execution of ExecStart if and only if there&#8217;s no cgroup for it, which is equivalent to not having a process running on its behalf</li>
<li>KillMode is set to &#8220;mixed&#8221; which sends a SIGTERM only to the process that is launched directly when the service is stopped. The SIGKILL 90 seconds later, if any, is sent to all processes however. The default is to give all processes in the cgroup the SIGTERM when stopping.</li>
<li>NoNewPrivileges is a little paranoid thing: When no process has any reason to change its privileges or user IDs,  block this possibility. This mitigates damage, should the job be successfully attacked in some way. But I ended up not using it, as running sendmail fails (it has some setuid thing to allow access to the mail spooler).</li>
</ul>
<h3>Stopping</h3>
<p>There is no log entry for a service of simple type that terminates with a success status. Even though it&#8217;s stopped in the sense that it has no allocated cgroup and &#8220;systemctl start&#8221; behaves as if it was stopped, a successful termination is silent. Not sure if I like this, but that&#8217;s the way it is.</p>
<p>When the process doesn&#8217;t respond to SIGTERM:</p>
<pre>Jan 16 19:13:03 systemd[1]: <strong>Stopping</strong> Cronjob test service...
Jan 16 19:14:33 systemd[1]: cronjob-test.service stop-sigterm timed out. Killing.
Jan 16 19:14:33 systemd[1]: cronjob-test.service: main process exited, code=killed, status=9/KILL
Jan 16 19:14:33 systemd[1]: <strong>Stopped</strong> Cronjob test service.
Jan 16 19:14:33 systemd[1]: Unit cronjob-test.service entered failed state.</pre>
<p>So there&#8217;s always &#8220;Stopping&#8221; first and then &#8220;Stopped&#8221;. And if there are processes in the control group 90 seconds after &#8220;Stopping&#8221;, SIGKILL is sent, and the service gets a &#8220;failed&#8221; status. Not being able to quit properly is a failure.</p>
<p>A &#8220;systemctl stop&#8221; on a service that is already stopped is legit: The systemctl utility returns silently with a success status, and a &#8220;Stopped&#8221; message appears in the log without anything actually taking place. Neither does the service&#8217;s status change, so if it was considered failed before, so it remains. And if the target to stop was a group if instances (e.g. systemctl stop &#8216;cronjob-test@*&#8217;) and there were no instances to stop, there&#8217;s even not a log message on that.</p>
<p>Same logic with &#8220;Starting&#8221; and &#8220;Started&#8221;: A superfluous &#8220;systemctl start&#8221; does nothing except for a &#8220;Started&#8221; log message, and the utility is silent, returning success.</p>
<h3>Capturing the output</h3>
<p>By default, the output (stdout and  stderr) of the processes is logged in the journal. This is usually  pretty convenient, however I wanted the good old cronjob behavior: An  email is sent unless the job is completely silent and exits with a success  status (actually, crond doesn&#8217;t care, but I wanted this too).</p>
<p>This  concept doesn&#8217;t fit systemd&#8217;s spirit: You don&#8217;t start sending mails  each time a service has something to say. One could use OnFailure for  activating another service that calls home when the service gets into a  failure status (which includes a non-success termination of the main  process), but that mail won&#8217;t tell me the output. To achieve this, I wrote a  Perl script. So there&#8217;s one extra process, but who cares, systemd  kills&#8217;em all in the end anyhow.</p>
<p>Here it comes (I called it shellout.pl):</p>
<pre>#!/usr/bin/perl

use strict;
use warnings;

# Parameters for sending mail to report errors
my $sender = 'eli';
my $recipient = 'eli';
my $sendmail = "/usr/sbin/sendmail -i -f$sender";

my $cmd = shift;
my $start = time();

my $output = '';

my $catcher = sub { finish("Received signal."); };

$SIG{HUP} = $catcher;
$SIG{TERM} = $catcher;
$SIG{INT} = $catcher;
$SIG{QUIT} = $catcher;

my $pid = open (my $fh, '-|');

finish("Failed to fork: $!")
  unless (defined $pid);

if (!$pid) { # Child process
  # Redirect stderr to stdout for child processes as well
  open (STDERR, "&gt;&amp;STDOUT");

  exec($cmd) or die("Failed to exec $cmd: $!\n");
}

# Parent
while (defined (my $l = &lt;$fh&gt;)) {
  $output .= $l;
}

close $fh
 or finish("Error: $! $?");

finish("Execution successful, but output was generated.")
 if (length $output);

exit 0; # Happy end
sub finish {
  my ($msg) = @_;

  my $elapsed = time() - $start;

  $msg .= "\n\nOutput generated:\n\n$output\n"
    if (length $output);

  open (my $fh, '|-', "$sendmail $recipient") or
    finish("Failed to run sendmail: $!");

  print $fh &lt;&lt;"END";
From: Shellout script &lt;$sender&gt;
Subject: systemd cron job issue
To: $recipient

The script with command \"$cmd\" ran $elapsed seconds.

$msg
END

  close $fh
    or die("Failed to send email: $! $?\n");

  $SIG{TERM} = sub { }; # Not sure this matters
  kill -15, $$; # Kill entire process group

  exit(1);
}</pre>
<p>First, let&#8217;s pay attention to</p>
<pre>open (STDERR, "&gt;&amp;STDOUT");</pre>
<p>which makes sure standard error is redirected to standard output. This is inherited by child processes, which is exactly the point.</p>
<p>The script catches the signals (SIGTERM in particular, which is systemd&#8217;s first hint that it&#8217;s time to pack and leave) and sends a SIGTERM to all other processes in turn. This is combined with KillMode being set to &#8220;mixed&#8221; in the service unit file, so that only shellout.pl gets the signal, and not the other processes.</p>
<p>The rationale is that if all processes get the signal at once, it may (theoretically?) turn out that the child process terminates before the script reacted to the signal it got itself, so it will fail to report that the reason for the termination was a signal, as opposed to the termination of the child. This could miss a situation where the child process got stuck and said nothing when being killed.</p>
<p>Note that the script kills all processes in the process group just before quitting due to a signal it got, or when the invoked process terminates and there was output. Before doing so, it sets the signal handler to a NOP, to avoid an endless loop, since the script&#8217;s process will get it as well (?). This NOP thing appears to be unnecessary, but better safe than sorry.</p>
<p>Also note that the while loop quits when there&#8217;s nothing more in &lt;$fh&gt;. This means that if the child process forks and then terminates, the while loop will continue, because unless the forked process closed its output file handles, it will keep the reference count of the script&#8217;s stdin above zero. The first child process will remain as a zombie until the forked process is done. Only then will it be reaped by virtue of the close $fh. This machinery is not intended for fork() sorcery.</p>
<p>I took a different approach in <a href="https://billauer.se/blog/2020/10/perl-fork-ipc-kill-children/" target="_blank">another post of mine</a>, where the idea was to fork explicitly and modify the child&#8217;s attributes. <a href="https://billauer.se/blog/2013/03/fork-wait-and-the-return-values-in-perl-in-different-scenarios/" target="_blank">Another post</a> discusses timing out a child process in general.</p>
<h3>Summary</h3>
<p>Yes, cronjobs are much simpler. But in the long run, it&#8217;s a good idea to acquire the ability to run cronjobs as services for the sake of keeping the system clean from runaway processes.</p>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2021/01/systemd-cron-cgroups/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Root over NFS remains read only with Linux v5.7</title>
		<link>https://billauer.se/blog/2020/07/nfsroot-read-only-remount/</link>
		<comments>https://billauer.se/blog/2020/07/nfsroot-read-only-remount/#comments</comments>
		<pubDate>Sun, 26 Jul 2020 13:18:22 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Linux kernel]]></category>
		<category><![CDATA[Server admin]]></category>
		<category><![CDATA[systemd]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=6078</guid>
		<description><![CDATA[Upgrading the kernel should be quick and painless&#8230; After upgrading the kernel from v5.3 to 5.7, a lot of systemd services failed (Debian 8), in particular systemd-remount-fs: ● systemd-remount-fs.service - Remount Root and Kernel File Systems Loaded: loaded (/lib/systemd/system/systemd-remount-fs.service; static) Active: failed (Result: exit-code) since Sun 2020-07-26 15:28:15 IDT; 17min ago Docs: man:systemd-remount-fs.service(8) http://www.freedesktop.org/wiki/Software/systemd/APIFileSystems Process: [...]]]></description>
			<content:encoded><![CDATA[<h3>Upgrading the kernel should be quick and painless&#8230;</h3>
<p>After upgrading the kernel from v5.3 to 5.7, a lot of systemd services failed (Debian 8), in particular systemd-remount-fs:</p>
<pre>● systemd-remount-fs.service - Remount Root and Kernel File Systems
   Loaded: loaded (/lib/systemd/system/systemd-remount-fs.service; static)
   Active: failed (Result: exit-code) since Sun 2020-07-26 15:28:15 IDT; 17min ago
     Docs: man:systemd-remount-fs.service(8)

http://www.freedesktop.org/wiki/Software/systemd/APIFileSystems

  Process: 223 ExecStart=/lib/systemd/systemd-remount-fs (code=exited, status=1/FAILURE)
 Main PID: 223 (code=exited, status=1/FAILURE)

Jul 26 15:28:15 systemd[1]: systemd-remount-fs.service: main process exited, code=exited, status=1/FAILURE
Jul 26 15:28:15 systemd[1]: Failed to start Remount Root and Kernel File Systems.
Jul 26 15:28:15 systemd[1]: Unit systemd-remount-fs.service entered failed state.</pre>
<p>and indeed, the root NFS remained read-only (checked with &#8220;mount&#8221; command), which explains why so many other services failed.</p>
<p>After an strace session, I managed to nail down the problem: The system call to mount(), which was supposed to do the remount, simply failed:</p>
<pre>mount("10.1.1.1:/path/to/debian-82", "/", 0x61a250, MS_REMOUNT, "addr=10.1.1.1") = -1 EINVAL (Invalid argument)</pre>
<p>On the other hand, any attempt to remount another read-only NFS mount, which had been mounted the regular way (i.e. after boot) went through clean, of course:</p>
<pre>mount("10.1.1.1:/path/to/debian-82", "/mnt/tmp", 0x61a230, MS_REMOUNT, "addr=10.1.1.1") = 0</pre>
<p>The only apparent difference between the two cases is the third argument, which is ignored for MS_REMOUNT according to the manpage.</p>
<p>The manpage also says something about the EINVAL return value:</p>
<blockquote><p>EINVAL   A remount operation (MS_REMOUNT) was attempted, but  source was not already mounted on target.</p></blockquote>
<p>A hint to the problem could be that the type of the mount, as listed in /proc/mounts,  is &#8220;nfs&#8221; for the root mounted filesystem, but &#8220;nfs4&#8243; for the one in /mnt/tmp. The reason for this difference isn&#8217;t completely clear.</p>
<h3>The solution</h3>
<p>So it&#8217;s all about that little hint: If the nfsroot is selected to boot as version 4, then there&#8217;s no problem remounting it. Why it made a difference from one kernel version to another is beyond me. So the fix is to add nfsvers=4 to the nfsroot assignment. Something like</p>
<pre>root=/dev/nfs nfsroot=10.1.1.1:/path/to/debian-82,<span style="color: #ff0000;"><strong>nfsvers=4</strong></span></pre>
<p>For the record, I re-ran the remount command with strace again, and exactly the same system call was made, including that most-likely-ignored 0x61a250 argument, and it simply returned success (zero) instead of EINVAL.</p>
<p>As a side note, the rootfstype=nfs in the kernel command line is  completely ignored. Write any junk instead of &#8220;nfs&#8221; and it makes no  difference.</p>
<p>Another yak shaved successfully.</p>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2020/07/nfsroot-read-only-remount/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>When umount says target is busy, but no process can be blamed</title>
		<link>https://billauer.se/blog/2020/06/umount-restart-nfs/</link>
		<comments>https://billauer.se/blog/2020/06/umount-restart-nfs/#comments</comments>
		<pubDate>Sun, 28 Jun 2020 10:15:54 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Server admin]]></category>
		<category><![CDATA[systemd]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=6067</guid>
		<description><![CDATA[A short one: What to do if unmount is impossible with a # umount /path/to/mount umount: /path/to/mount: target is busy but grepping the output of lsof for the said path yields nothing. In other words, the mount is busy, but no process can be blamed for accessing it (even as a home directory). If this [...]]]></description>
			<content:encoded><![CDATA[<p>A short one: What to do if unmount is impossible with a</p>
<pre># umount /path/to/mount
umount: /path/to/mount: target is busy</pre>
<p>but grepping the output of lsof for the said path yields nothing. In other words, the mount is busy, but no process can be blamed for accessing it (even as a home directory).</p>
<p>If this happens, odds are that it&#8217;s an NFS mount, held by some remote machine. The access might have been over long ago, but the mount is still considered busy. So the solution for this case is simple: Restart the NFS daemon. On Linux Mint 19 (and probably a lot of others) it&#8217;s simply</p>
<pre># systemctl restart nfs-server</pre>
<p>and after this, umount is sucessful (hopefully&#8230;)</p>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2020/06/umount-restart-nfs/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>systemd: Reacting to USB NIC hotplugging (post-up scripting)</title>
		<link>https://billauer.se/blog/2019/11/systemd-udev-ifup-usb-nic/</link>
		<comments>https://billauer.se/blog/2019/11/systemd-udev-ifup-usb-nic/#comments</comments>
		<pubDate>Mon, 11 Nov 2019 17:17:57 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[systemd]]></category>
		<category><![CDATA[udev]]></category>
		<category><![CDATA[USB]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=5920</guid>
		<description><![CDATA[The problem Using Linux Mint 19, I have a network device that needs DHCP address allocation connected to a USB network dongle. When I plug it in, the device appears, but the DHCP daemon ignored eth2 (the assigned network device name) and didn&#8217;t respond to its DHCP discovery packets. But restarting the DHCP server well [...]]]></description>
			<content:encoded><![CDATA[<h3>The problem</h3>
<p>Using Linux Mint 19, I have a network device that needs DHCP address allocation connected to a USB network dongle. When I plug it in, the device appears, but the DHCP daemon ignored eth2 (the assigned network device name) and didn&#8217;t respond to its DHCP discovery packets. But restarting the DHCP server well after plugging in the USB network card solved the issue.</p>
<p>I should mention that I use a vintage DHCP server for this or other reason (not necessarily a good one). There&#8217;s a good chance that a systemd-aware DHCP daemon will resynchronize itself following a network hotplug event. It&#8217;s evident that avahi-daemon, hostapd, systemd-timesyncd and vmnet-natd trigger some activity as a result of the new network device.</p>
<p>Most notable is systemd-timesyncd, which goes</p>
<pre>Nov 11 11:25:59 systemd-timesyncd[1101]: Network configuration changed, trying to establish connection.</pre>
<p>twice, once when the new device appears, and a second time when it is configured. See sample kernel log below.</p>
<p>It&#8217;s not clear to me how these daemons get their notification on the new network device. I could have dug deeper into this, but ended up with a rather ugly solution. I&#8217;m sure this can be done better, but I&#8217;ve wasted enough time on this &#8212; please comment below if you know how.</p>
<h3>Setting up a systemd service</h3>
<p>The very systemd way to run a script when a networking device appears is to add a service. Namely, add this file as /etc/systemd/system/eth2-up.service:</p>
<pre>[Unit]
Description=Restart dhcp when eth2 is up

[Service]
ExecStart=/bin/sleep 10 ; /bin/systemctl restart my-dhcpd
Type=oneshot

[Install]
WantedBy=sys-subsystem-net-devices-eth2.device</pre>
<p>And then activate the service:</p>
<pre># systemctl daemon-reload
# systemctl enable eth2-up</pre>
<p>The concept is simple: A on-shot service depends on the relevant device. When it&#8217;s up, what&#8217;s on ExecStart is run, the DHCP server is restarted, end of story.</p>
<p>I promised ugly, didn&#8217;t I: Note the 10 second sleep before kicking off the daemon restart. This is required because the service is launched when the networking device appears, and not when it&#8217;s fully configured. So starting the DHCP daemon right away misses the point (or simply put: It doesn&#8217;t work).</p>
<p>I guess the DHCP daemon will be restarted one time extra on boot due to this extra service. In that sense, the 10 seconds delay is possible better than restarting it soon after or while it being started by systemd in general.</p>
<p>So with the service activated, this is what the log looks like (the restarting of the DHCP server not included):</p>
<pre>Nov 11 11:25:54 kernel: usb 1-12: new high-speed USB device number 125 using xhci_hcd
Nov 11 11:25:54 kernel: usb 1-12: New USB device found, idVendor=0bda, idProduct=8153
Nov 11 11:25:54 kernel: usb 1-12: New USB device strings: Mfr=1, Product=2, SerialNumber=6
Nov 11 11:25:54 kernel: usb 1-12: Product: USB 10/100/1000 LAN
Nov 11 11:25:54 kernel: usb 1-12: Manufacturer: Realtek
Nov 11 11:25:54 kernel: usb 1-12: SerialNumber: 001000001
Nov 11 11:25:55 kernel: usb 1-12: reset high-speed USB device number 125 using xhci_hcd
Nov 11 11:25:55 vmnet-natd[1845]: RTM_NEWLINK: name:eth2 index:848 flags:0x00001002
Nov 11 11:25:55 vmnetBridge[1620]: RTM_NEWLINK: name:eth2 index:848 flags:0x00001002
Nov 11 11:25:55 kernel: r8152 1-12:1.0 eth2: v1.09.9
Nov 11 11:25:55 mtp-probe[59372]: checking bus 1, device 125: "/sys/devices/pci0000:00/0000:00:14.0/usb1/1-12"
Nov 11 11:25:55 mtp-probe[59372]: bus: 1, device: 125 was not an MTP device
Nov 11 11:25:55 upowerd[2203]: unhandled action 'bind' on /sys/devices/pci0000:00/0000:00:14.0/usb1/1-12
Nov 11 11:25:55 systemd-networkd[65515]: ppp0: Link is not managed by us
Nov 11 11:25:55 systemd-networkd[65515]: vmnet8: Link is not managed by us
Nov 11 11:25:55 systemd-networkd[65515]: vmnet1: Link is not managed by us
Nov 11 11:25:55 networkd-dispatcher[1140]: WARNING:Unknown index 848 seen, reloading interface list
Nov 11 11:25:55 systemd-networkd[65515]: lo: Link is not managed by us
Nov 11 11:25:55 systemd-networkd[65515]: eth2: IPv6 successfully enabled
<strong>Nov 11 11:25:55 systemd[1]: Starting Restart dhcp when eth2 is up...
</strong>Nov 11 11:25:55 kernel: IPv6: ADDRCONF(NETDEV_UP): eth2: link is not ready
Nov 11 11:25:55 vmnet-natd[1845]: RTM_NEWLINK: name:eth2 index:848 flags:0x00001043
Nov 11 11:25:55 vmnetBridge[1620]: RTM_NEWLINK: name:eth2 index:848 flags:0x00001043
Nov 11 11:25:55 vmnetBridge[1620]: Adding interface eth2 index:848
Nov 11 11:25:55 vmnet-natd[1845]: RTM_NEWLINK: name:eth2 index:848 flags:0x00001043
Nov 11 11:25:55 vmnetBridge[1620]: RTM_NEWLINK: name:eth2 index:848 flags:0x00001043
Nov 11 11:25:55 systemd-timesyncd[1101]: Network configuration changed, trying to establish connection.
Nov 11 11:25:55 vmnetBridge[1620]: RTM_NEWLINK: name:eth2 index:848 flags:0x00001003
Nov 11 11:25:55 vmnetBridge[1620]: Removing interface eth2 index:848
Nov 11 11:25:55 vmnet-natd[1845]: RTM_NEWLINK: name:eth2 index:848 flags:0x00001003
Nov 11 11:25:55 upowerd[2203]: unhandled action 'bind' on /sys/devices/pci0000:00/0000:00:14.0/usb1/1-12/1-12:1.0
Nov 11 11:25:55 kernel: IPv6: ADDRCONF(NETDEV_UP): eth2: link is not ready
Nov 11 11:25:55 systemd-timesyncd[1101]: Synchronized to time server 91.189.89.198:123 (ntp.ubuntu.com).
Nov 11 11:25:55 kernel: userif-3: sent link down event.
Nov 11 11:25:55 kernel: userif-3: sent link up event.
Nov 11 11:25:57 vmnetBridge[1620]: RTM_NEWLINK: name:eth2 index:848 flags:0x00011043
Nov 11 11:25:57 vmnetBridge[1620]: Adding interface eth2 index:848
Nov 11 11:25:57 systemd-networkd[65515]: eth2: Gained carrier
Nov 11 11:25:57 systemd-timesyncd[1101]: Network configuration changed, trying to establish connection.
Nov 11 11:25:57 avahi-daemon[1115]: Joining mDNS multicast group on interface eth2.IPv4 with address 10.20.30.1.
Nov 11 11:25:57 avahi-daemon[1115]: New relevant interface eth2.IPv4 for mDNS.
Nov 11 11:25:57 avahi-daemon[1115]: Registering new address record for 10.20.30.1 on eth2.IPv4.
Nov 11 11:25:57 kernel: r8152 1-12:1.0 eth2: carrier on
Nov 11 11:25:57 kernel: IPv6: ADDRCONF(NETDEV_CHANGE): eth2: link becomes ready
Nov 11 11:25:57 vmnet-natd[1845]: RTM_NEWLINK: name:eth2 index:848 flags:0x00011043
Nov 11 11:25:57 vmnet-natd[1845]: RTM_NEWADDR: index:848, addr:10.20.30.1
Nov 11 11:25:57 systemd-timesyncd[1101]: Synchronized to time server 91.189.89.198:123 (ntp.ubuntu.com).
Nov 11 11:25:58 kernel: userif-3: sent link down event.
Nov 11 11:25:58 kernel: userif-3: sent link up event.
Nov 11 11:25:59 avahi-daemon[1115]: Joining mDNS multicast group on interface eth2.IPv6 with address fe80::2e0:4cff:fe68:71d.
Nov 11 11:25:59 avahi-daemon[1115]: New relevant interface eth2.IPv6 for mDNS.
Nov 11 11:25:59 systemd-networkd[65515]: eth2: Gained IPv6LL
Nov 11 11:25:59 avahi-daemon[1115]: Registering new address record for fe80::2e0:4cff:fe68:71d on eth2.*.
<strong>Nov 11 11:25:59 systemd-networkd[65515]: eth2: Configured
</strong>Nov 11 11:25:59 systemd-timesyncd[1101]: Network configuration changed, trying to establish connection.
Nov 11 11:25:59 systemd-timesyncd[1101]: Synchronized to time server 91.189.89.198:123 (ntp.ubuntu.com).</pre>
<p>As emphasized in bold above, there are 4 seconds between the activation of the script and systemd-networkd&#8217;s declaration that it&#8217;s finished with it.</p>
<p>It would have been much nicer to kick off the script where systemd-timesyncd detects the change for the second time. It would have been much wiser had WantedBy=sys-subsystem-net-devices-eth2.device meant that the target is reached when it&#8217;s actually configured. Once again, if someone has an idea, please comment below.</p>
<h3>A udev rule instead</h3>
<p>The truth is that I started off with a udev rule first, ran into the problem with the DHCP server being restarted too early, and tried to solve it with systemd as shown above, hoping that it would work better. The bottom line is that it&#8217;s effectively the same. So here&#8217;s the udev rule, which I kept as /etc/udev/rules.d/99-network-dongle.rules:</p>
<pre>SUBSYSTEM=="net", ACTION=="add", KERNEL=="eth2", RUN+="/bin/sleep 10 ; /bin/systemctl restart my-dhcpd"</pre>
<p>Note that I nail down the device by its name (eth2). It would have been nicer to do it based upon the USB device&#8217;s Vendor / Product IDs, however that failed for me. Somehow, it didn&#8217;t match for me when using SUBSYSTEM==&#8221;usb&#8221;. How I ensure the repeatable eth2 name is explained on <a href="https://billauer.se/blog/2017/12/systemd-systemctl-services/" target="_blank">this post</a>.</p>
<p>Also worth noting that these two commands for getting the udev rule together:</p>
<pre># udevadm test -a add /sys/class/net/eth2
# udevadm info -a /sys/class/net/eth2</pre>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2019/11/systemd-udev-ifup-usb-nic/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
