利用程序名用做参数

利用程序名用做参数

背景

无意中发现Linux中的关机重启等程序都是systemctl的软连接:

lrwxrwxrwx 1 root root     14 11月10日 08:25 halt -> /bin/systemctl
lrwxrwxrwx 1 root root     14 11月10日 08:25 poweroff -> /bin/systemctl
lrwxrwxrwx 1 root root     14 11月10日 08:25 reboot -> /bin/systemctl
lrwxrwxrwx 1 root root     14 11月10日 08:25 runlevel -> /bin/systemctl
lrwxrwxrwx 1 root root     14 11月10日 08:25 shutdown -> /bin/systemctl
lrwxrwxrwx 1 root root     14 11月10日 08:25 telinit -> /bin/systemctl

所以好奇是怎么将参数传递给systemctl的,随后看了下systemctl的源码:

int systemctl_dispatch_parse_argv(int argc, char *argv[]) {
        assert(argc >= 0);
        assert(argv);

        if (invoked_as(argv, "halt")) {
                arg_action = ACTION_HALT;
                return halt_parse_argv(argc, argv);

        } else if (invoked_as(argv, "poweroff")) {
                arg_action = ACTION_POWEROFF;
                return halt_parse_argv(argc, argv);

        } else if (invoked_as(argv, "reboot")) {
                arg_action = ACTION_REBOOT;
                return halt_parse_argv(argc, argv);

        } else if (invoked_as(argv, "shutdown")) {
                arg_action = ACTION_POWEROFF;
                return shutdown_parse_argv(argc, argv);

        } else if (invoked_as(argv, "init")) {

                /* Matches invocations as "init" as well as "telinit", which are synonymous when run
                 * as PID != 1 on SysV.
                 *
                 * On SysV "telinit" was the official command to communicate with PID 1, but "init" would
                 * redirect itself to "telinit" if called with PID != 1. We follow the same logic here still,
                 * though we add one level of indirection, as we implement "telinit" in "systemctl". Hence,
                 * for us if you invoke "init" you get "systemd", but it will execve() "systemctl"
                 * immediately with argv[] unmodified if PID is != 1. If you invoke "telinit" you directly
                 * get "systemctl". In both cases we shall do the same thing, which is why we do
                 * invoked_as(argv, "init") here, as a quick way to match both.
                 *
                 * Also see redirect_telinit() in src/core/main.c. */

                if (sd_booted() > 0) {
                        arg_action = _ACTION_INVALID;
                        return telinit_parse_argv(argc, argv);
                } else {
                        /* Hmm, so some other init system is running, we need to forward this request to it.
                         */
                        arg_action = ACTION_TELINIT;
                        return 1;
                }

        } else if (invoked_as(argv, "runlevel")) {
                arg_action = ACTION_RUNLEVEL;
                return runlevel_parse_argv(argc, argv);
        }

        arg_action = ACTION_SYSTEMCTL;
        return systemctl_parse_argv(argc, argv);
}

接着跟到invoke_as

bool invoked_as(char *argv[], const char *token) {
        if (!argv || isempty(argv[0]))
                return false;

        if (isempty(token))
                return false;

        return strstr(last_path_component(argv[0]), token);
}

可以看到,就是通过argv[0]来传递参数的。

总结

我们在编写程序的时候,一般都是处理argv[1]及之后的参数,很少会去处理argv[0]。
但是如果我们想让程序表现出不同的行为,就像不同的程序一样,这时候可以将程序创建数个软连接,通过argv[0]来区分。这样的好处我认为有以下两点:

  1. 不用每次都在程序后面加单独的参数,使用起来简洁
  2. 软连接的名字可以让使用者一眼就看出程序的功能,语意上更明确

你可能感兴趣的:(Linux,源码阅读,linux)