]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - sys-utils/setarch.c
scriptreplay: cleanup usage()
[thirdparty/util-linux.git] / sys-utils / setarch.c
index 8e521793771c68e9adf70823bb33242c1d71e972..1a2ae1b68ccce9645643f24632c5174911ab6841 100644 (file)
 # define personality(pers) ((long)syscall(SYS_personality, pers))
 #endif
 
-/* Options without equivalent short options */
-enum {
-       OPT_4GB = CHAR_MAX + 1,
-       OPT_UNAME26
-};
-
 #define turn_on(_flag, _opts) \
        do { \
                (_opts) |= _flag; \
@@ -88,11 +82,19 @@ enum {
 # define ADDR_LIMIT_3GB          0x8000000
 #endif
 
-static void __attribute__((__noreturn__)) show_help(void)
+
+struct arch_domain {
+       int             perval;         /* PER_* */
+       const char      *target_arch;
+       const char      *result_arch;
+};
+
+
+static void __attribute__((__noreturn__)) usage(int archwrapper)
 {
        fputs(USAGE_HEADER, stdout);
-       if (!strcmp(program_invocation_short_name, "setarch"))
-               printf(_(" %s <arch> [options] [<program> [<argument>...]]\n"), program_invocation_short_name);
+       if (!archwrapper)
+               printf(_(" %s [<arch>] [options] [<program> [<argument>...]]\n"), program_invocation_short_name);
        else
                printf(_(" %s [options] [<program> [<argument>...]]\n"), program_invocation_short_name);
 
@@ -113,44 +115,27 @@ static void __attribute__((__noreturn__)) show_help(void)
        fputs(_("     --4gb                ignored (for backward compatibility only)\n"), stdout);
        fputs(_("     --uname-2.6          turns on UNAME26\n"), stdout);
        fputs(_(" -v, --verbose            say what options are being switched on\n"), stdout);
-       fputs(_("     --list               list settable architectures, and exit\n"), stdout);
+
+       if (!archwrapper)
+               fputs(_("     --list               list settable architectures, and exit\n"), stdout);
 
        fputs(USAGE_SEPARATOR, stdout);
-       fputs(USAGE_HELP, stdout);
-       fputs(USAGE_VERSION, stdout);
+       printf(USAGE_HELP_OPTIONS(26));
        printf(USAGE_MAN_TAIL("setarch(8)"));
 
        exit(EXIT_SUCCESS);
 }
 
-static void __attribute__((__noreturn__)) show_usage(const char *s)
-{
-       if (s)
-               errx(EXIT_FAILURE,
-                    _("%s\nTry `%s --help' for more information."), s,
-                    program_invocation_short_name);
-       else
-               errx(EXIT_FAILURE, _("Try `%s --help' for more information."),
-                    program_invocation_short_name);
-}
-
-static void __attribute__((__noreturn__))
-    show_version(void)
+/*
+ * Returns inilialized list of all available execution domains.
+ */
+static struct arch_domain *init_arch_domains(void)
 {
-       printf(UTIL_LINUX_VERSION);
-       exit(EXIT_SUCCESS);
-}
+       static struct utsname un;
+       size_t i;
 
-static int set_arch(const char *pers, unsigned long options, int list)
-{
-       struct utsname un;
-       int i;
-       unsigned long pers_value;
-
-       struct {
-               int perval;
-               const char *target_arch, *result_arch;
-       } transitions[] = {
+       static struct arch_domain transitions[] =
+       {
                {UNAME26,       "uname26",      NULL},
                {PER_LINUX32,   "linux32",      NULL},
                {PER_LINUX,     "linux64",      NULL},
@@ -162,9 +147,11 @@ static int set_arch(const char *pers, unsigned long options, int list)
                {PER_LINUX,     "ppc64pseries", "ppc64"},
                {PER_LINUX,     "ppc64iseries", "ppc64"},
 # else
-               PER_LINUX32,    "ppc32le",      "ppcle"},
-               PER_LINUX32,    "ppcle",        "ppcle"},
-               PER_LINUX,      "ppc64le",      "ppc64le"},
+               {PER_LINUX32,   "ppc32",        "ppcle"},
+               {PER_LINUX32,   "ppc",          "ppcle"},
+               {PER_LINUX32,   "ppc32le",      "ppcle"},
+               {PER_LINUX32,   "ppcle",        "ppcle"},
+               {PER_LINUX,     "ppc64le",      "ppc64le"},
 # endif
 #endif
 #if defined(__x86_64__) || defined(__i386__) || defined(__ia64__)
@@ -206,6 +193,19 @@ static int set_arch(const char *pers, unsigned long options, int list)
                {PER_LINUX,     "alphaev56",    "alpha"},
                {PER_LINUX,     "alphaev6",     "alpha"},
                {PER_LINUX,     "alphaev67",    "alpha"},
+#endif
+#if defined(__e2k__)
+               {PER_LINUX,     "e2k",      "e2k"},
+               {PER_LINUX,     "e2kv4",        "e2k"},
+               {PER_LINUX,     "e2kv5",        "e2k"},
+               {PER_LINUX,     "e2kv6",        "e2k"},
+               {PER_LINUX,     "e2k4c",        "e2k"},
+               {PER_LINUX,     "e2k8c",        "e2k"},
+               {PER_LINUX,     "e2k1cp",       "e2k"},
+               {PER_LINUX,     "e2k8c2",       "e2k"},
+               {PER_LINUX,     "e2k12c",       "e2k"},
+               {PER_LINUX,     "e2k16c",       "e2k"},
+               {PER_LINUX,     "e2k2c3",       "e2k"},
 #endif
                /* place holder, will be filled up at runtime */
                {-1,            NULL,           NULL},
@@ -227,39 +227,70 @@ static int set_arch(const char *pers, unsigned long options, int list)
                        transitions[i].result_arch = un.machine;
                }
        }
-       if (list) {
-               for (i = 0; transitions[i].target_arch != NULL; i++)
-                       printf("%s\n", transitions[i].target_arch);
-               return 0;
-       }
-       for (i = 0; transitions[i].perval >= 0; i++)
-               if (!strcmp(pers, transitions[i].target_arch))
+
+       return transitions;
+}
+
+/*
+ * List all execution domains from transitions
+ */
+static void list_arch_domains(struct arch_domain *doms)
+{
+       struct arch_domain *d;
+
+       for (d = doms; d->target_arch != NULL; d++)
+               printf("%s\n", d->target_arch);
+}
+
+static struct arch_domain *get_arch_domain(struct arch_domain *doms, const char *pers)
+{
+       struct arch_domain *d;
+
+       for (d = doms; d && d->perval >= 0; d++) {
+               if (!strcmp(pers, d->target_arch))
                        break;
-       if (transitions[i].perval < 0)
-               errx(EXIT_FAILURE, _("%s: Unrecognized architecture"), pers);
-       pers_value = transitions[i].perval | options;
-       if (personality(pers_value) == -EINVAL)
-               return 1;
+       }
+
+       return !d || d->perval < 0 ? NULL : d;
+}
+
+static void verify_arch_domain(struct arch_domain *dom, const char *wanted)
+{
+       struct utsname un;
+
+       if (!dom || !dom->result_arch)
+               return;
+
        uname(&un);
-       if (transitions[i].result_arch && strcmp(un.machine, transitions[i].result_arch)) {
-               if (strcmp(transitions[i].result_arch, "i386")
+       if (strcmp(un.machine, dom->result_arch)) {
+               if (strcmp(dom->result_arch, "i386")
                    || (strcmp(un.machine, "i486")
                        && strcmp(un.machine, "i586")
                        && strcmp(un.machine, "i686")
                        && strcmp(un.machine, "athlon")))
-                       errx(EXIT_FAILURE, _("%s: Unrecognized architecture"), pers);
+                       errx(EXIT_FAILURE, _("Kernel cannot set architecture to %s"), wanted);
        }
-       return 0;
 }
 
 int main(int argc, char *argv[])
 {
-       const char *p;
+       const char *arch = NULL;
        unsigned long options = 0;
        int verbose = 0;
+       int archwrapper;
        int c;
+       struct arch_domain *doms, *target = NULL;
+       unsigned long pers_value = 0;
+       char *shell = NULL, *shell_arg = NULL;
+
+       /* Options without equivalent short options */
+       enum {
+               OPT_4GB = CHAR_MAX + 1,
+               OPT_UNAME26,
+               OPT_LIST
+       };
 
-       /* Options --3gb and --4gb are for compatibitity with an old
+       /* Options --3gb and --4gb are for compatibility with an old
         * Debian setarch implementation.  */
        static const struct option longopts[] = {
                {"help",                no_argument,    NULL,   'h'},
@@ -277,51 +308,41 @@ int main(int argc, char *argv[])
                {"3gb",                 no_argument,    NULL,   '3'},
                {"4gb",                 no_argument,    NULL,   OPT_4GB},
                {"uname-2.6",           no_argument,    NULL,   OPT_UNAME26},
+               {"list",                no_argument,    NULL,   OPT_LIST},
                {NULL,                  0,              NULL,   0}
        };
 
        setlocale(LC_ALL, "");
        bindtextdomain(PACKAGE, LOCALEDIR);
        textdomain(PACKAGE);
-       atexit(close_stdout);
-
-       if (argc < 1)
-               show_usage(_("Not enough arguments"));
-
-       p = program_invocation_short_name;
-       if (!strcmp(p, "setarch")) {
-               argc--;
-               if (argc < 1)
-                       show_usage(_("Not enough arguments"));
-               p = argv[1];
-               argv[1] = argv[0];      /* for getopt_long() to get the program name */
-               argv++;
-               if (!strcmp(p, "-h") || !strcmp(p, "--help"))
-                       show_help();
-               else if (!strcmp(p, "-V") || !strcmp(p, "--version"))
-                       show_version();
-               else if (!strcmp(p, "--list")) {
-                       set_arch(argv[0], 0L, 1);
-                       return EXIT_SUCCESS;
-               }
+       close_stdout_atexit();
+
+       if (argc < 1) {
+               warnx(_("Not enough arguments"));
+               errtryhelp(EXIT_FAILURE);
        }
-#if defined(__sparc64__) || defined(__sparc__)
-       if (!strcmp(p, "sparc32bash")) {
-               if (set_arch(p, 0L, 0))
-                       err(EXIT_FAILURE, _("Failed to set personality to %s"), p);
-               execl("/bin/bash", NULL);
-               err(EXIT_FAILURE, _("failed to execute %s"), "/bin/bash");
+       archwrapper = strcmp(program_invocation_short_name, "setarch") != 0;
+       if (archwrapper) {
+               arch = program_invocation_short_name;   /* symlinks to setarch */
+
+               /* Don't use ifdef sparc here, we get "Unrecognized architecture"
+                * error message later if necessary */
+               if (strcmp(arch, "sparc32bash") == 0) {
+                       shell = "/bin/bash";
+                       shell_arg = "";
+                       goto set_arch;
+               }
+       } else {
+               if (1 < argc && *argv[1] != '-') {
+                       arch = argv[1];
+                       argv[1] = argv[0];      /* for getopt_long() to get the program name */
+                       argv++;
+                       argc--;
+               }
        }
-#endif
 
        while ((c = getopt_long(argc, argv, "+hVv3BFILRSTXZ", longopts, NULL)) != -1) {
                switch (c) {
-               case 'h':
-                       show_help();
-                       break;
-               case 'V':
-                       show_version();
-                       break;
                case 'v':
                        verbose = 1;
                        break;
@@ -360,26 +381,78 @@ int main(int argc, char *argv[])
                case OPT_UNAME26:
                        turn_on(UNAME26, options);
                        break;
+               case OPT_LIST:
+                       if (!archwrapper) {
+                               list_arch_domains(init_arch_domains());
+                               return EXIT_SUCCESS;
+                       } else
+                               warnx(_("unrecognized option '--list'"));
+                       /* fallthrough */
+
                default:
-                       show_usage(NULL);
+                       errtryhelp(EXIT_FAILURE);
+               case 'h':
+                       usage(archwrapper);
+               case 'V':
+                       print_version(EXIT_SUCCESS);
                }
        }
 
+       if (!arch && !options)
+               errx(EXIT_FAILURE, _("no architecture argument or personality flags specified"));
+
        argc -= optind;
        argv += optind;
 
-       if (set_arch(p, options, 0))
-               err(EXIT_FAILURE, _("Failed to set personality to %s"), p);
+set_arch:
+       /* get execution domain (architecture) */
+       if (arch) {
+               doms = init_arch_domains();
+               target = get_arch_domain(doms, arch);
 
-       /* flush all output streams before exec */
-       fflush(NULL);
+               if (!target)
+                       errx(EXIT_FAILURE, _("%s: Unrecognized architecture"), arch);
+               pers_value = target->perval;
+       }
+
+       /* add personality flags */
+       pers_value |= options;
+
+       /* call kernel */
+       if (personality(pers_value) < 0) {
+               /*
+                * Depending on architecture and kernel version, personality
+                * syscall is either capable or incapable of returning an error.
+                * If the return value is not an error, then it's the previous
+                * personality value, which can be an arbitrary value
+                * undistinguishable from an error value.
+                * To make things clear, a second call is needed.
+                */
+               if (personality(pers_value) < 0)
+                       err(EXIT_FAILURE, _("failed to set personality to %s"), arch);
+       }
+
+       /* make sure architecture is set as expected */
+       if (arch)
+               verify_arch_domain(target, arch);
 
        if (!argc) {
-               execl("/bin/sh", "-sh", NULL);
-               err(EXIT_FAILURE, _("failed to execute %s"), "/bin/sh");
+               shell = "/bin/sh";
+               shell_arg = "-sh";
+       }
+       if (verbose) {
+               printf(_("Execute command `%s'.\n"), shell ? shell : argv[0]);
+               /* flush all output streams before exec */
+               fflush(NULL);
+       }
+
+       /* Execute shell */
+       if (shell) {
+               execl(shell, shell_arg, NULL);
+               errexec(shell);
        }
 
+       /* Execute on command line specified command */
        execvp(argv[0], argv);
-       err(EXIT_FAILURE, "%s", argv[0]);
-       return EXIT_FAILURE;
+       errexec(argv[0]);
 }