poweroff, halt, reboot and systemctl
by Amit
On Fedora (and perhaps other Linux distros using systemd) you will see that the poweroff
, reboot
and halt
commands are all symlinks to systemctl
:
> ls -l /sbin/poweroff /sbin/halt /sbin/reboot lrwxrwxrwx. 1 root root 16 Oct 1 11:04 /sbin/halt -> ../bin/systemctl lrwxrwxrwx. 1 root root 16 Oct 1 11:04 /sbin/poweroff -> ../bin/systemctl lrwxrwxrwx. 1 root root 16 Oct 1 11:04 /sbin/reboot -> ../bin/systemctl
So, how does it all work? The answer lies in this code block from systemctl.c:
.. 5556 if (program_invocation_short_name) { 5557 5558 if (strstr(program_invocation_short_name, "halt")) { 5559 arg_action = ACTION_HALT; 5560 return halt_parse_argv(argc, argv); 5561 } else if (strstr(program_invocation_short_name, "poweroff")) { 5562 arg_action = ACTION_POWEROFF; 5563 return halt_parse_argv(argc, argv); 5564 } else if (strstr(program_invocation_short_name, "reboot")) { 5565 if (kexec_loaded()) ..
program_invocation_short_name
program_invocation_short_name
is a variable (GNU extension) which contains the name used to invoke a program. The short indicates that if you call your program as /bin/myprogram
, it is set to ‘myprogram’. There is also a program_invocation_name
variable consisting of the entire path. Here is a demo:
/*myprogram.c*/ # include <stdio.h> extern char *program_invocation_short_name; extern char *program_invocation_name; int main(int argc, char **argv) { printf("%s \n", program_invocation_short_name); printf("%s \n", program_invocation_name); return 0; }
Assume that the executable for the above program is created as myprogram
, execute the program from a directory which is one level up from where it resides. For example, in my case, myprogram
is in $HOME/work
and I am executing it from $HOME
:
> ./work/myprogram myprogram ./work/myprogram
You can see the difference between the values of the two variables. Note that any command line arguments passed are not included in any of the variables.
Back to systemctl
Okay, so now we know that when we execute the poweroff
command (for example), program_invocation_short_name
is set to poweroff
and this check matches:
if (strstr(program_invocation_short_name, "poweroff")) ..
and then the actual action of powering down the system takes place. Also note that how the halt_parse_argv
function is called with the parameters argc and argv so that when you invoke the poweroff
command with a switch such as --help
, it is passed appropriately to halt_parse_argv
:
5194 static const struct option options[] = { 5195 { "help", no_argument, NULL, ARG_HELP }, .. .. 5218 case ARG_HELP: 5219 return halt_help();
Fooling around
Considering that systemctl
uses strstr
to match the command it was invoked as, it allows for some fooling around. Create a symlink mypoweroff
to /bin/systemctl
and then execute it as follows:
> ln -s /bin/systemctl mypoweroff > ./mypoweroff --help mypoweroff [OPTIONS...] Power off the system. --help Show this help --halt Halt the machine -p --poweroff Switch off the machine --reboot Reboot the machine -f --force Force immediate halt/power-off/reboot -w --wtmp-only Don't halt/power-off/reboot, just write wtmp record -d --no-wtmp Don't write wtmp record --no-wall Don't send wall message before halt/power-off/reboot
This symlink is for all purpose going to act like the poweroff
command since systemctl basically checks whether ‘poweroff’ is a substring of the invoked command.
To learn more, see systemctl.c
Related
Few months back, I had demoed invoking a similar behaviour in programs where a program behaves differently based on how you invoke it using argv[0] here. I didn’t know of the GNU extensions back then.
busybox
works the same way, doing whatls
,grep
,sed
and others do, based on the name under which is called. Though I have no idea if it uses those GNU extensios.Hi Cristian, could you please explain a little more? I couldn’t quite understand what you meant. Thanks!
Sorry for not being clear enough the first time.
busybox
implements the basic functionality ofls
,mv
,grep
,sed
and other utilities into a single binary. All those programs are just symlinks tobusybox
, e.g.ls
is a symlink tobusybox
,mv
is a symlink tobusybox
and so on. So when you runls
you’ll get a listing of the current directory and when you runmv
you move files.P.S. In case anyone wonders why would someone use busybox instead of coreutils plus sed plus other packages, the answer is disk space. It’s like the Pareto principle: 80% of the functionality with 20% of the space :-)
Thanks for that. I looked into busybox source code. BusyBox calls itself a multi-call binary (See http://www.redbooks.ibm.com/abstracts/tips0092.html?Open). I also learned that it calls the programs such as ‘ls’ which it implements as applets and it is in libbb/appletlib.c where all the magic happens.
When only the busybox binary is called, it displays all the currently registered applets. Else, it simply checks the argv[1], argv[2] and so on to execute the applets with their arguments. A good starting point to learn more is the function busybox_main().
Very interesting. Thanks for sharing and referring to it!
On Sat, Nov 23, 2013 at 11:53 PM, Bits from /home/gene
[…] poweroff, halt, reboot and systemctl (echorand.me) […]