this post was submitted on 29 Jun 2023
145 points (100.0% liked)
Linux
1259 readers
60 users here now
From Wikipedia, the free encyclopedia
Linux is a family of open source Unix-like operating systems based on the Linux kernel, an operating system kernel first released on September 17, 1991 by Linus Torvalds. Linux is typically packaged in a Linux distribution (or distro for short).
Distributions include the Linux kernel and supporting system software and libraries, many of which are provided by the GNU Project. Many Linux distributions use the word "Linux" in their name, but the Free Software Foundation uses the name GNU/Linux to emphasize the importance of GNU software, causing some controversy.
Rules
- Posts must be relevant to operating systems running the Linux kernel. GNU/Linux or otherwise.
- No misinformation
- No NSFW content
- No hate speech, bigotry, etc
Related Communities
Community icon by Alpár-Etele Méder, licensed under CC BY 3.0
founded 5 years ago
MODERATORS
you are viewing a single comment's thread
view the rest of the comments
view the rest of the comments
Wait, what? As far as I know, if you drop a System V init script in its usual place, systemd will treat it as a service and place its processes in the control group for services, even if it calls
fork
and/orsetuid
. How did it end up inuser.slice
?If you put an init script in /etc/rc.d/init.d/ systemd looks there, it has the systemd-sysv-generator which will look at the script, do it's best guess to build a systemd unit file, and then start it via unit file. But it's guessing at a LOT of things. Even if you have a LSB header in your init script, systemd's ExecStart is just calling the init script. The problem will calling an init script is you're almost certainly going to call another script. Well systemd thinks that ExecStart IS the parent. And since children can't live without the parent, if the script finishes, systemd sees the script finish and it kils all the children, because children shouldn't be around without a parent.
Now systemd CAN guess a bit with a fork, but if you're forking numerous times, init script calls X script calls Y script, it will NOT follow this. Hence why people decided that "RemainAfterExit=true" is the work around. That's NOT. That's NOT the answer. That is WRONG and systemd will NOT manage your application appropriately.
systemd starting a service when User= is set sets the user during exec. There is no change user. So if you use a switch user that has a login, which most everyone do as su invokes login natively, then it falls through the pam stack, which include pam.systemd.so which tells systemd, ok this is a login which means it's a meatbag move it over to the user.slice. So because it thinks it's a human, ergo in the user.slice, it will NOT manage resources properly, it will NOT shut things down nicely, it will NOT clean up children, etc.
systemd's reality is you don't use init scripts, you break your script down to the basic parts, you call your binary directly, you do everything via systemd's directives. Do NOT let it guess at anything, be absolutely explicit.
It follows it just fine. I have, for example, a Debian machine running the ISC DHCP server, which has only an LSB init script and no systemd service file. Starting it involves at least four forks: once to start the init script, once when the init script runs
start-stop-daemon
, once whenstart-stop-daemon
runsdhcpd
, and once whendhcpd
forks itself into the background.Systemd correctly recognizes that it's still running because, although the init script,
start-stop-daemon
, and the initialdhcpd
process have all exited, there is still another process in its control group:Similarly, starting Apache involves running
apachectl
, which then forkshttpd
, which then forks itself into the background, and if you're usingmpm_prefork
, that background process forks itself a whole bunch more times too. Systemd has no problem with this:That's one of the neat things about systemd: fork all you want, and it'll still keep track of which process belongs to which service.
Or just don't write messy, incorrect init scripts that use
su
. Be sensible and usestart-stop-daemon
. That's what it's there for, and it's been there since the '90s.So just to state, your comment is coming off condescending, I don't know if that was intentional or not. But saying something is "sensible" sounds rude. Everyone has different experiences and what you think is "sensible" or "common sense" someone else might have never heard of. I've never heard of a command called "start-stop-daemon". I've only heard start/stop init scripts. Looking at the daemon, I've never seen anyone use this. Vast majority of scripts I've looked at were other companies and never saw a single one use it. So maybe start-stop-daemon would work, but I don't have a clue. But when I talked to the developers of systemd and told them that people were falling into the user.slice because they were running a su in their init scripts they were all surprised as well, because they didn't think ANYONE did that. Well I was a sysadmin for 5 years before I worked at Red Hat for 6 and that's how everyone is taught to do it. So again, different experiences.
The other part I want to address is you saying that those two examples shows I'm wrong. The first instance:
There definitely seems to be something wrong there because sysetmd no longer is showing a Main Pid process which is the parent, so my guess is there's something like a "RemainAfterExit=true" or something else there, because systemd isn't showing a parent. And when there isn't a parent, unless overridden in the unit file, systemd will kill the children.
The second example:
Shows you're calling "/usr/sbin/apachectl" which then forks to apache and that stays as the main parent, you can see "Main PID" and then yes there are many children because they're children of 1492. That is called a double fork. systemd can figure out the parent in a double fork, but once you fork and fork and fork, NOT counting CHILDREN but the actual PARENT, then systemd doesn't know which to follow so usually ends up assuming the wrong PID to follow and will clean up everything when what it thinks is the parent goes away. But your second example is how systemd is supposed to work. You call an application that fork and the parent dies and lets the child take over. That's a standard double fork, that's what systemd expects. So that works as you expect, because you've called it as you expect. Wrap your /usr/sbin/apachectl in a script and then ExecStart= and unless they've made massive improvements you're gonna run into issues.
Edit: fixed formatting on the systemctl status output
It's not a daemon; it's a command-line program that starts and stops daemons. It is specifically designed to be called from init scripts. It was originally developed for Debian in the '90s, and has been used in its init scripts ever since.
I'm not familiar with non-Debian distributions, but I assume they have their own equivalents, if they aren't using a full-blown service manager like systemd.
An old pre-systemd CentOS machine I have access to uses some shell functions defined in
/etc/init.d/functions
to start and stop daemons. Those shell functions use therunuser
program from the util-linux package to change user ID, notsu
.Per the
systemd.service
man page forRemainAfterExit
: “Takes a boolean value that specifies whether the service shall be considered active even when all its processes exited. Defaults to no.” (Emphasis mine.)systemd-sysv-generator
generatesType=forking
service units.Per the
systemd.service
man page for this type of service: “If set to forking, it is expected that the process configured with ExecStart= will call fork() as part of its start-up. The parent process is expected to exit when start-up is complete and all communication channels are set up. The child continues to run as the main service process, and the service manager will consider the unit started when the parent process exits. This is the behavior of traditional UNIX services.”It would be counterproductive for systemd to kill all of the processes belonging to such a service when the parent process exits, since the whole point of this type of service is that the parent process exits immediately.
You might be thinking of how
logind
by default kills all processes belonging to a user session when the session ends. That's different from services, though.Works for me:
(
systemctl status
also shows recent log entries, which I have omitted here.)Even with the extra fork, and even without a
PidFile
setting, it correctly identifies all of the processes belonging to this service, including the main process. And, even without anExecStop
setting telling it to useapachectl
, it still correctly stops the service:(I have once again omitted the recent log entries from this output.)
runuser I do know and you're correct it's what is expected, and actually you CAN use su you just have to make sure you use the arguement that tells it not to follow a login process, but as a syaadmin myself, none of the guys I worked with, and none of the customers I had ever worked with hitting these issues had ever heard of it. But yes you can switch user without falling through the PAM stack and be fine.
As for the fork maybe they resolved that or maybe they made it better because you only have 1 child, but I can tell you I wouldn't trust systemd that way personally from my experience. And yes Type=forking also says you should have a PIDFile and that's in case there are multiple forks you can be explicit in which pid is thr parent. There are other issues you can get into like not setting ulimits or if you have 1 script that starts multiple daemons.