What kind of link to /bin/systemctl is /sbin/reboot?

Nice question, I found the answer, provided by @slm ♦, on Unix & Linux Q&A:

Many programs make use of this technique where there is a single executable that changes its behaviour based on how it was executed.

There's typically a structure inside the program called a case/switch statement that determines the name the executable was called with and then will call the appropriate functionality for that executable name. That name is usually the first argument the program receives.

Read the examples...

While in the original answer are provided two examples with C and Perl, here is an example with Bash. In Bash the positional parameter $0 contains the name of the script that is executed. So let's create a simple script, called program.sh and make it executable:

cat << EOF > program.sh && chmod +x program.sh
#!/bin/bash
echo "I was called as \"\${0##*/}\""
EOF
  • \$ will escape the special meaning of $ within cat during the script creation.

  • ${0##*/} will output only the name of the executable without the path during the script execution.

Next let's create three symbolic links to that script:

for link in call-{1..3}; do ln -s program.sh $link; done

Now, depending on how we call program.sh - directly or by any of the symbolic links, that we've created - the output will be different:

$ ./program.sh 
I was called as "program.sh"

$ ./call-1 
I was called as "call-1"

$ ./call-2
I was called as "call-2"

$ ./call-3
I was called as "call-3"

As pa4080 says, it's just a normal symbolic link, and systemctl itself is what checks what name it has been run as, in order to decide what action to perform. This sort of thing can be achieved with any mechanism that causes the same executable file to be run under multiple names; in practice, it is achieved through the use of symlinks or hard links. On GNU/Linux systems, like Ubuntu, it's most common to use symlinks for this purpose.

In Ubuntu releases with systemd (which currently includes all supported releases except 14.04 LTS, which uses Upstart), not just the reboot command but also the halt, poweroff, runlevel, shutdown, and telinit commands are all symlinks to /bin/systemctl:

$ file /sbin/{halt,poweroff,reboot,runlevel,shutdown,telinit}
/sbin/halt:     symbolic link to /bin/systemctl
/sbin/poweroff: symbolic link to /bin/systemctl
/sbin/reboot:   symbolic link to /bin/systemctl
/sbin/runlevel: symbolic link to /bin/systemctl
/sbin/shutdown: symbolic link to /bin/systemctl
/sbin/telinit:  symbolic link to /bin/systemctl

The precise actions systemctl undertakes based on the name you use to run it, as well as other ways you can specify those actions, are explained in detail in this excellent answer by JdeBP to What is the difference between these commands for bringing down a Linux server? on Unix.SE. As it explains, these commands (except runlevel) operate as shorthand for systemctl isolate ... commands with ... replaced by different targets.

In case you're interested, the C code that considers what name you used to invoke systemctl in deciding what action to perform is located in the parse_argv function defined in systemctl.c, which currently starts on line 6972 of that file. To find it you can search for:

static int parse_argv(int argc, char *argv[]) {

The relevant parts span most of that function, but it all roughly resembles this fragment of it, continuing similarly but with different code for each string, and with some other checks and branching logic:

                if (strstr(program_invocation_short_name, "halt")) {
                        arg_action = ACTION_HALT;
                        return halt_parse_argv(argc, argv);
                } else if (strstr(program_invocation_short_name, "poweroff")) {
                        arg_action = ACTION_POWEROFF;
                        return halt_parse_argv(argc, argv);
                } else if (strstr(program_invocation_short_name, "reboot")) {

Other examples of commands that examine how they were invoked to (sometimes) act differently include vim, which behaves differently when run as vim, ex, view, gvim, gview, and several others; ip, see this question; gksu, see that question; less, which changes its appearance if named more (but still lets you scroll bidirectionally, unlike Ubuntu's more); busybox; and most Bourne-style shells, such as bash and zsh, which automatically behave more compatibly with POSIX requirements for sh if run as sh.