]> 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 b5e401c81cbcd71723b21e9f5dfd1fcd465cf3aa..1a2ae1b68ccce9645643f24632c5174911ab6841 100644 (file)
 # define ADDR_LIMIT_3GB          0x8000000
 #endif
 
-static int archwrapper;
 
-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 (!archwrapper)
-               printf(_(" %s <arch> [options] [<program> [<argument>...]]\n"), program_invocation_short_name);
+               printf(_(" %s [<arch>] [options] [<program> [<argument>...]]\n"), program_invocation_short_name);
        else
                printf(_(" %s [options] [<program> [<argument>...]]\n"), program_invocation_short_name);
 
@@ -114,41 +120,22 @@ static void __attribute__((__noreturn__)) show_help(void)
                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)
-{
-       printf(UTIL_LINUX_VERSION);
-       exit(EXIT_SUCCESS);
-}
-
-static int set_arch(const char *pers, unsigned long options, int list)
+/*
+ * Returns inilialized list of all available execution domains.
+ */
+static struct arch_domain *init_arch_domains(void)
 {
-       struct utsname un;
-       int i;
-       unsigned long pers_value;
+       static struct utsname un;
+       size_t i;
 
-       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},
@@ -160,6 +147,8 @@ static int set_arch(const char *pers, unsigned long options, int list)
                {PER_LINUX,     "ppc64pseries", "ppc64"},
                {PER_LINUX,     "ppc64iseries", "ppc64"},
 # else
+               {PER_LINUX32,   "ppc32",        "ppcle"},
+               {PER_LINUX32,   "ppc",          "ppcle"},
                {PER_LINUX32,   "ppc32le",      "ppcle"},
                {PER_LINUX32,   "ppcle",        "ppcle"},
                {PER_LINUX,     "ppc64le",      "ppc64le"},
@@ -204,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},
@@ -225,39 +227,49 @@ 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) < 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)
-                       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, _("Kernel cannot set architecture to %s"), pers);
+                       errx(EXIT_FAILURE, _("Kernel cannot set architecture to %s"), wanted);
        }
-       return 0;
 }
 
 int main(int argc, char *argv[])
@@ -265,7 +277,11 @@ int main(int argc, char *argv[])
        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 {
@@ -274,7 +290,7 @@ int main(int argc, char *argv[])
                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'},
@@ -299,15 +315,24 @@ int main(int argc, char *argv[])
        setlocale(LC_ALL, "");
        bindtextdomain(PACKAGE, LOCALEDIR);
        textdomain(PACKAGE);
-       atexit(close_stdout);
-
-       if (argc < 1)
-               show_usage(_("Not enough arguments"));
+       close_stdout_atexit();
 
+       if (argc < 1) {
+               warnx(_("Not enough arguments"));
+               errtryhelp(EXIT_FAILURE);
+       }
        archwrapper = strcmp(program_invocation_short_name, "setarch") != 0;
-       if (archwrapper)
+       if (archwrapper) {
                arch = program_invocation_short_name;   /* symlinks to setarch */
-       else {
+
+               /* 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 */
@@ -316,23 +341,8 @@ int main(int argc, char *argv[])
                }
        }
 
-#if defined(__sparc64__) || defined(__sparc__)
-       if (archwrapper && strcmp(arch, "sparc32bash") == 0) {
-               if (set_arch(arch, 0L, 0))
-                       err(EXIT_FAILURE, _("Failed to set personality to %s"), arch);
-               execl("/bin/bash", NULL);
-               err(EXIT_FAILURE, _("failed to execute %s"), "/bin/bash");
-       }
-#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;
@@ -373,33 +383,76 @@ int main(int argc, char *argv[])
                        break;
                case OPT_LIST:
                        if (!archwrapper) {
-                               set_arch(NULL, 0, 1);
+                               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)
-               errx(EXIT_FAILURE, _("no architecture argument specified"));
+       if (!arch && !options)
+               errx(EXIT_FAILURE, _("no architecture argument or personality flags specified"));
 
        argc -= optind;
        argv += optind;
 
-       if (set_arch(arch, options, 0))
-               err(EXIT_FAILURE, _("failed to set personality to %s"), arch);
+set_arch:
+       /* get execution domain (architecture) */
+       if (arch) {
+               doms = init_arch_domains();
+               target = get_arch_domain(doms, arch);
+
+               if (!target)
+                       errx(EXIT_FAILURE, _("%s: Unrecognized architecture"), arch);
+               pers_value = target->perval;
+       }
 
-       /* flush all output streams before exec */
-       fflush(NULL);
+       /* 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]);
 }