1/*
2 * Copyright (c) 1999, 2006, 2011 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <sys/cdefs.h>
25#include <stdio.h>
26#include <string.h>
27#include <stdbool.h>
28#include <stdlib.h>
29#include <stddef.h>
30#include <unistd.h>
31#include <spawn.h>
32#include <sys/types.h>
33#include <sys/stat.h>
34#include <sys/param.h>
35#include <paths.h>
36#include <err.h>
37#include <mach/mach.h>
38#include <mach-o/arch.h>
39#include <limits.h>
40#include <sys/fcntl.h>
41#include <glob.h>
42#include <CoreFoundation/CoreFoundation.h>
43#include <NSSystemDirectories.h>
44
45#ifndef ARCH_PROG
46#define ARCH_PROG	"arch"
47#endif
48#ifndef MACHINE_PROG
49#define MACHINE_PROG	"machine"
50#endif
51
52#define kKeyExecPath "ExecutablePath"
53#define kKeyPlistVersion "PropertyListVersion"
54#define kKeyPrefOrder "PreferredOrder"
55#define kPlistExtension ".plist"
56#define kSettingsDir "archSettings"
57
58static const char envname[] = "ARCHPREFERENCE";
59
60/* The CPU struct contains the argument buffer to posix_spawnattr_setbinpref_np */
61
62typedef struct {
63    cpu_type_t *buf;
64    int errs;
65    size_t count;
66    size_t capacity;
67} CPU;
68
69typedef struct {
70    const char *arch;
71    cpu_type_t cpu;
72} CPUTypes;
73
74static const CPUTypes knownArchs[] = {
75#if defined(__i386__) || defined(__x86_64__)
76    {"i386", CPU_TYPE_I386},
77    {"x86_64", CPU_TYPE_X86_64},
78#elif defined(__arm__)
79    {"arm", CPU_TYPE_ARM},
80#else
81#error "Unsupported architecture"
82#endif
83};
84
85/* environment SPI */
86char **_copyenv(char **env);
87int _setenvp(const char *name, const char *value, int rewrite, char ***envp, void *state);
88int _unsetenvp(const char *name, char ***envp, void *state);
89
90/* copy of environment */
91char **envCopy = NULL;
92extern char **environ;
93
94/*
95 * The native 32 and 64-bit architectures (this is relative to the architecture
96 * the arch command is running.  NULL means unsupported.
97 */
98#if defined(__i386__) || defined(__x86_64__)
99#define NATIVE_32	"i386"
100#define NATIVE_64	"x86_64"
101#elif defined(__arm__)
102#define NATIVE_32	"arm"
103#define NATIVE_64	NULL
104#else
105#error "Unsupported architecture"
106#endif
107bool unrecognizednative32seen = false;
108bool unrecognizednative64seen = false;
109
110/*
111 * arch - perform the original behavior of the arch and machine commands.
112 * The archcmd flag is non-zero for the arch command, zero for the machine
113 * command.  This routine never returns.
114 */
115static void __dead2
116arch(int archcmd)
117{
118    const NXArchInfo *arch = NXGetLocalArchInfo();
119
120    if(!arch)
121        errx(-1, "Unknown architecture.");
122    if(archcmd) {
123        arch = NXGetArchInfoFromCpuType(arch->cputype, CPU_SUBTYPE_MULTIPLE);
124        if(!arch)
125            errx(-1, "Unknown architecture.");
126    }
127    printf("%s%s", arch->name, (isatty(STDIN_FILENO) ? "\n" : ""));
128    exit(0);
129}
130
131/*
132 * spawnIt - run the posix_spawn command.  cpu is the auto-sizing CPU structure.
133 * pflag is non-zero to call posix_spawnp; zero means to call posix_spawn.
134 * str is the name/path to pass to posix_spawn{,p}, and argv are
135 * the argument arrays to pass.  This routine never returns.
136 */
137static void __dead2
138spawnIt(CPU *cpu, int pflag, const char *str, char **argv)
139{
140    posix_spawnattr_t attr;
141    pid_t pid;
142    int ret;
143    size_t copied;
144    size_t count = cpu->count;
145    cpu_type_t *prefs = cpu->buf;
146
147    if(count == 0) {
148        if(unrecognizednative32seen)
149            warnx("Unsupported native 32-bit architecture");
150        if(unrecognizednative64seen)
151            warnx("Unsupported native 64-bit architecture");
152        exit(1);
153    }
154
155    if(unrecognizednative32seen)
156        fprintf(stderr, "warning: unsupported native 32-bit architecture\n");
157    if(unrecognizednative64seen)
158        fprintf(stderr, "warning: unsupported native 64-bit architecture\n");
159
160    if((ret = posix_spawnattr_init(&attr)) != 0)
161        errc(1, ret, "posix_spawnattr_init");
162    /* do the equivalent of exec, rather than creating a separate process */
163    if((ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETEXEC)) != 0)
164        errc(1, ret, "posix_spawnattr_setflags");
165    if((ret = posix_spawnattr_setbinpref_np(&attr, count, prefs, &copied)) != 0)
166        errc(1, ret, "posix_spawnattr_setbinpref_np");
167    if(copied != count)
168        errx(1, "posix_spawnattr_setbinpref_np only copied %lu of %lu", copied, count);
169    if(pflag)
170        ret = posix_spawnp(&pid, str, NULL, &attr, argv, envCopy ? envCopy : environ);
171    else
172        ret = posix_spawn(&pid, str, NULL, &attr, argv, envCopy ? envCopy : environ);
173    errc(1, ret, "posix_spawn%s: %s", (pflag ? "p" : ""), str);
174}
175
176/*
177 * initCPU - initialize a CPU structure, a dynamically expanding CPU types
178 * array.
179 */
180static void
181initCPU(CPU *cpu)
182{
183    cpu->errs = 0;
184    cpu->count = 0;
185    cpu->capacity = 1;
186    cpu->buf = (cpu_type_t *)malloc(cpu->capacity * sizeof(cpu_type_t));
187    if(!cpu->buf)
188        err(1, "Failed to malloc CPU buffer");
189}
190
191/*
192 * addCPU - add a new CPU type value to the CPU structure, expanding
193 * the array as necessary.
194 */
195static void
196addCPU(CPU *cpu, cpu_type_t n)
197{
198    if(cpu->count == cpu->capacity) {
199        cpu_type_t *newcpubuf;
200
201        cpu->capacity *= 2;
202        newcpubuf = (cpu_type_t *)realloc(cpu->buf, cpu->capacity * sizeof(cpu_type_t));
203        if(!newcpubuf)
204            err(1, "Out of memory realloc-ing CPU structure");
205        cpu->buf = newcpubuf;
206    }
207    cpu->buf[cpu->count++] = n;
208}
209
210/*
211 * addCPUbyname - add a new CPU type, given by name, to the CPU structure,
212 * expanding the array as necessary.  The name is converted to a type value
213 * by the ArchDict dictionary.
214 */
215static void
216addCPUbyname(CPU *cpu, const char *name)
217{
218    int i;
219
220    for (i=0; i < sizeof(knownArchs)/sizeof(knownArchs[0]); i++) {
221        if (0 == strcasecmp(name, knownArchs[i].arch)) {
222            addCPU(cpu, knownArchs[i].cpu);
223            return;
224        }
225    }
226
227    /* Didn't match a string in knownArchs */
228    warnx("Unknown architecture: %s", name);
229    cpu->errs++;
230}
231
232/*
233 * useEnv - parse the environment variable for CPU preferences.  Use name
234 * to look for program-specific preferences, and append any CPU types to cpu.
235 * Returns the number of CPU types.  Returns any specified execute path in
236 * execpath.
237 *
238 * The environment variable ARCHPREFERENCE has the format:
239 *    spec[;spec]...
240 * a semicolon separated list of specifiers.  Each specifier has the format:
241 *    [prog:[execpath:]]type[,type]...
242 * a comma separate list of CPU type names, optionally proceeded by a program
243 * name and an execpath.  If program name exist, that types only apply to that
244 * program.  If execpath is specified, it is returned.  If no program name
245 * exists, then it applies to all programs.  So ordering of the specifiers is
246 * important, as the default (no program name) specifier must be last.
247 */
248static size_t
249useEnv(CPU *cpu, const char *name, char **execpath)
250{
251    char *val = getenv(envname);
252    if(!val)
253        return 0;
254
255    /* cp will point to the basename of name */
256    const char *cp = strrchr(name, '/');
257    if(cp) {
258        cp++;
259        if(!*cp)
260            errx(1, "%s: no name after last slash", name);
261    } else
262        cp = name;
263    /* make a copy of the environment variable value, so we can modify it */
264    val = strdup(val);
265    if(!val)
266        err(1, "Can't copy environment %s", envname);
267    char *str = val;
268    char *blk;
269    /* for each specifier */
270    while((blk = strsep(&str, ";")) != NULL) {
271        if(*blk == 0)
272            continue; /* two adjacent semicolons */
273        /* now split on colons */
274        char *n = strsep(&blk, ":");
275        if(blk) {
276            char *p = strsep(&blk, ":");
277            if(!blk) { /* there is only one colon, so no execpath */
278                blk = p;
279                p = NULL;
280            } else if(!*p) /* two consecutive colons, so no execpath */
281                p = NULL;
282            if(!*blk)
283                continue; /* no cpu list, so skip */
284            /* if the name matches, or there is no name, process the cpus */
285            if(!*n || strcmp(n, cp) == 0) {
286                if(cpu->count == 0) { /* only if we haven't processed architectures */
287                    char *t;
288                    while((t = strsep(&blk, ",")) != NULL)
289                        addCPUbyname(cpu, t);
290                }
291                *execpath = (*n ? p : NULL); /* only use the exec path is name is set */
292                break;
293            }
294        } else { /* no colons at all, so process as default */
295            if(cpu->count == 0) { /* only if we haven't processed architectures */
296                blk = n;
297                while((n = strsep(&blk, ",")) != NULL)
298                    addCPUbyname(cpu, n);
299            }
300            *execpath = NULL;
301            break;
302        }
303    }
304    if(cpu->errs) /* errors during addCPUbyname are fatal */
305        exit(1);
306    return cpu->count; /* return count of architectures */
307}
308
309/*
310 * spawnFromPreference - called when argv[0] is not "arch" or "machine", or
311 * argv[0] was arch, but no commandline architectures were specified.
312 * If the environment variable ARCHPREFERENCE is specified, and there is a
313 * match to argv[0], use the specified cpu preferences.  If no exec path
314 * is specified in ARCHPREFERENCE, or no match is found in ARCHPREFERENCE,
315 * get any additional information from a .plist file with the name of argv[0].
316 * This routine never returns.
317 */
318static void __dead2
319spawnFromPreferences(CPU *cpu, int needexecpath, char **argv)
320{
321    char *epath = NULL;
322    char fpath[PATH_MAX];
323    char execpath2[PATH_MAX];
324    CFDictionaryRef plist = NULL;
325    NSSearchPathEnumerationState state;
326    size_t count, i;
327    const char *prog = strrchr(*argv, '/');
328
329    if(prog)
330        prog++;
331    else
332        prog = *argv;
333    if(!*prog)
334        errx(1, "Not program name specified");
335
336    /* check the environment variable first */
337    if((count = useEnv(cpu, prog, &epath)) > 0) {
338        /* if we were called as arch, use posix_spawnp */
339        if(!needexecpath)
340            spawnIt(cpu, 1, (epath ? epath : *argv), argv);
341        /* otherwise, if we have the executable path, call posix_spawn */
342        if(epath)
343            spawnIt(cpu, 0, epath, argv);
344    }
345
346    state = NSStartSearchPathEnumeration(NSLibraryDirectory, NSAllDomainsMask);
347    while ((state = NSGetNextSearchPathEnumeration(state, fpath))) {
348
349        CFURLRef url;
350        CFReadStreamRef stream;
351
352        if (fpath[0] == '~') {
353            glob_t pglob;
354            int gret;
355
356            bzero(&pglob, sizeof(pglob));
357
358            gret = glob(fpath, GLOB_TILDE, NULL, &pglob);
359            if (gret == 0) {
360                int i;
361                for (i=0; i < pglob.gl_pathc; i++) {
362                    /* take the first glob expansion */
363                    strlcpy(fpath, pglob.gl_pathv[i], sizeof(fpath));
364                    break;
365                }
366            }
367            globfree(&pglob);
368        }
369
370        // Handle path
371        strlcat(fpath, "/" kSettingsDir "/", sizeof(fpath));
372        strlcat(fpath, prog, sizeof(fpath));
373        strlcat(fpath, kPlistExtension, sizeof(fpath));
374        // printf("component: %s\n", fpath);
375
376        int fd, ret;
377        size_t length;
378        ssize_t rsize;
379        struct stat sb;
380        void *buffer;
381        fd = open(fpath, O_RDONLY, 0);
382        if (fd >= 0) {
383            ret = fstat(fd, &sb);
384            if (ret == 0) {
385                if (sb.st_size <= SIZE_T_MAX) {
386                    length = (size_t)sb.st_size;
387                    buffer = malloc(length); /* ownership transferred to CFData */
388                    if (buffer) {
389                        rsize = read(fd, buffer, length);
390                        if (rsize == length) {
391                            CFDataRef data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, buffer, length, kCFAllocatorMalloc);
392                            if (data) {
393                                buffer = NULL;
394                                plist = CFPropertyListCreateWithData(kCFAllocatorDefault, data, kCFPropertyListImmutable, NULL, NULL);
395                                CFRelease(data);
396                            }
397                        }
398                        if (buffer) {
399                            free(buffer);
400                        }
401                    }
402                }
403            }
404            close(fd);
405        }
406
407        if (plist) {
408            break;
409        }
410    }
411
412    if (plist) {
413        if (CFGetTypeID(plist) != CFDictionaryGetTypeID())
414            errx(1, "%s: plist not a dictionary", fpath);
415    } else {
416        errx(1, "Can't find any plists for %s", prog);
417    }
418
419
420    int errs = 0; /* scan for all errors and fail later */
421    do { /* begin block */
422        /* check the plist version */
423        CFStringRef vers = CFDictionaryGetValue(plist, CFSTR(kKeyPlistVersion));
424        if(!vers) {
425            warnx("%s: No key %s", fpath, kKeyPlistVersion);
426            errs++;
427        } else if(CFGetTypeID(vers) != CFStringGetTypeID()) {
428            warnx("%s: %s is not a string", fpath, kKeyPlistVersion);
429            errs++;
430        } else if(!CFEqual(vers, CFSTR("1.0"))) {
431            warnx("%s: %s not 1.0", fpath, kKeyPlistVersion);
432            errs++;
433        }
434        /* get the execpath */
435        CFStringRef execpath = CFDictionaryGetValue(plist, CFSTR(kKeyExecPath));
436        if(!execpath) {
437            warnx("%s: No key %s", fpath, kKeyExecPath);
438            errs++;
439        } else if(CFGetTypeID(execpath) != CFStringGetTypeID()) {
440            warnx("%s: %s is not a string", fpath, kKeyExecPath);
441            errs++;
442        }
443        if (!CFStringGetFileSystemRepresentation(execpath, execpath2, sizeof(execpath2))) {
444            warnx("%s: could not get exec path", fpath);
445            errs++;
446        }
447        /* if we already got cpu preferences from ARCHPREFERENCE, we are done */
448        if(count > 0)
449            break;
450        /* otherwise, parse the cpu preferences from the plist */
451        CFArrayRef p = CFDictionaryGetValue(plist, CFSTR(kKeyPrefOrder));
452        if(!p) {
453            warnx("%s: No key %s", fpath, kKeyPrefOrder);
454            errs++;
455        } else if(CFGetTypeID(p) != CFArrayGetTypeID()) {
456            warnx("%s: %s is not an array", fpath, kKeyPrefOrder);
457            errs++;
458        } else if((count = CFArrayGetCount(p)) == 0) {
459            warnx("%s: no entries in %s", fpath, kKeyPrefOrder);
460            errs++;
461        } else {
462            /* finally build the cpu type array */
463            for(i = 0; i < count; i++) {
464                CFStringRef a = CFArrayGetValueAtIndex(p, i);
465                if(CFGetTypeID(a) != CFStringGetTypeID()) {
466                    warnx("%s: entry %lu of %s is not a string", fpath, i, kKeyPrefOrder);
467                    errs++;
468                } else {
469                    char astr[128];
470                    if (CFStringGetCString(a, astr, sizeof(astr), kCFStringEncodingASCII)) {
471                        addCPUbyname(cpu, astr);
472                    }
473                }
474            }
475        }
476    } while(0); /* end block */
477    if(errs) /* exit if there were any reported errors */
478        exit(1);
479
480    CFRelease(plist);
481
482    /* call posix_spawn */
483    spawnIt(cpu, 0, execpath2, argv);
484}
485
486static void __dead2
487usage(int ret)
488{
489    fprintf(stderr,
490            "Usage: %s\n"
491            "       Display the machine's architecture type\n"
492            "Usage: %s {-arch_name | -arch arch_name} ... [-c] [-d envname] ... [-e envname=value] ... [-h] prog [arg ...]\n"
493            "       Run prog with any arguments, using the given architecture\n"
494            "       order.  If no architectures are specified, use the\n"
495            "       ARCHPREFERENCE environment variable, or a property list file.\n"
496            "       -c will clear out all environment variables before running prog.\n"
497            "       -d will delete the given environment variable before running prog.\n"
498            "       -e will add the given environment variable/value before running prog.\n"
499            "       -h will print usage message and exit.\n",
500            ARCH_PROG, ARCH_PROG);
501    exit(ret);
502}
503
504/*
505 * wrapped - check the path to see if it is a link to /usr/bin/arch.
506 */
507static int
508wrapped(const char *name)
509{
510    size_t lp, ln;
511    char *p;
512    char *bp = NULL;
513    char *cur, *path;
514    char buf[MAXPATHLEN], rpbuf[MAXPATHLEN];
515    struct stat sb;
516
517    ln = strlen(name);
518
519    do { /* begin block */
520        /* If it's an absolute or relative path name, it's easy. */
521        if(index(name, '/')) {
522            if(stat(name, &sb) == 0 && S_ISREG(sb.st_mode) && access(name, X_OK) == 0) {
523                bp = (char *)name;
524                break;
525            }
526            errx(1, "%s isn't executable", name);
527        }
528
529        /* search the PATH, looking for name */
530        if((path = getenv("PATH")) == NULL)
531            path = _PATH_DEFPATH;
532
533        cur = alloca(strlen(path) + 1);
534        if(cur == NULL)
535            err(1, "alloca");
536        strcpy(cur, path);
537        while((p = strsep(&cur, ":")) != NULL) {
538            /*
539             * It's a SHELL path -- double, leading and trailing colons
540             * mean the current directory.
541             */
542            if(*p == '\0') {
543                p = ".";
544                lp = 1;
545            } else
546                lp = strlen(p);
547
548            /*
549             * If the path is too long complain.  This is a possible
550             * security issue; given a way to make the path too long
551             * the user may execute the wrong program.
552             */
553            if(lp + ln + 2 > sizeof(buf)) {
554                warn("%s: path too long", p);
555                continue;
556            }
557            bcopy(p, buf, lp);
558            buf[lp] = '/';
559            bcopy(name, buf + lp + 1, ln);
560            buf[lp + ln + 1] = '\0';
561            if(stat(buf, &sb) == 0 && S_ISREG(sb.st_mode) && access(buf, X_OK) == 0) {
562                bp = buf;
563                break;
564            }
565        }
566        if(p == NULL)
567            errx(1, "Can't find %s in PATH", name);
568    } while(0); /* end block */
569    if(realpath(bp, rpbuf) == NULL)
570        errx(1, "realpath failed on %s", bp);
571    return (strcmp(rpbuf, "/usr/bin/" ARCH_PROG) == 0);
572}
573
574/*
575 * spawnFromArgs - called when arch has arguments specified.  The arch command
576 * line arguments are:
577 * % arch [[{-xxx | -arch xxx}]...] prog [arg]...
578 * where xxx is a cpu name, and the command to execute and its arguments follow.
579 * If no commandline cpu names are given, the environment variable
580 * ARCHPREFERENCE is used.  This routine never returns.
581 */
582
583#define MATCHARG(a,m)	({ \
584    const char *arg = *(a); \
585    if(arg[1] == '-') arg++; \
586    strcmp(arg, (m)) == 0; \
587})
588
589#define MATCHARGWITHVALUE(a,m,n,e)	({ \
590    const char *ret = NULL; \
591    const char *arg = *(a); \
592    if(arg[1] == '-') arg++; \
593    if(strcmp(arg, (m)) == 0) { \
594        if(*++(a) == NULL) { \
595            warnx(e); \
596            usage(1); \
597        } \
598        ret = *(a); \
599    } else if(strncmp(arg, (m), (n)) == 0 && arg[n] == '=') { \
600         ret = arg + (n) + 1; \
601    } \
602    ret; \
603})
604
605#define MAKEENVCOPY(e)	\
606    if(!envCopy) { \
607        envCopy = _copyenv(environ); \
608        if(envCopy == NULL) \
609            errx(1, (e)); \
610    }
611
612static void __dead2
613spawnFromArgs(CPU *cpu, char **argv)
614{
615    const char *ap, *ret;
616
617    /* process arguments */
618    for(argv++; *argv && **argv == '-'; argv++) {
619        if((ret = MATCHARGWITHVALUE(argv, "-arch", 5, "-arch without architecture"))) {
620            ap = ret;
621        } else if(MATCHARG(argv, "-32")) {
622            ap = NATIVE_32;
623            if(!ap) {
624                unrecognizednative32seen = true;
625                continue;
626            }
627        } else if(MATCHARG(argv, "-64")) {
628            ap = NATIVE_64;
629            if(!ap) {
630                unrecognizednative64seen = true;
631                continue;
632            }
633        } else if(MATCHARG(argv, "-c")) {
634            free(envCopy);
635            envCopy = _copyenv(NULL); // create empty environment
636            if(!envCopy)
637                errx(1, "Out of memory processing -c");
638            continue;
639        } else if((ret = MATCHARGWITHVALUE(argv, "-d", 2, "-d without envname"))) {
640            MAKEENVCOPY("Out of memory processing -d");
641            _unsetenvp(ret, &envCopy, NULL);
642            continue;
643        } else if((ret = MATCHARGWITHVALUE(argv, "-e", 2, "-e without envname=value"))) {
644            MAKEENVCOPY("Out of memory processing -e");
645            const char *cp = strchr(ret, '=');
646            if(!cp) {
647                warnx("-e %s: no equal sign", ret);
648                usage(1);
649            }
650            cp++; // skip to value
651            /*
652             * _setenvp() only uses the name before any equal sign found in
653             * the first argument.
654             */
655            _setenvp(ret, cp, 1, &envCopy, NULL);
656            continue;
657        } else if(MATCHARG(argv, "-h")) {
658            usage(0);
659        } else {
660            ap = *argv + 1;
661            if(*ap == '-') ap++;
662        }
663        addCPUbyname(cpu, ap);
664    }
665    if(cpu->errs)
666        exit(1);
667    if(!*argv || !**argv) {
668        warnx("No command to execute");
669        usage(1);
670    }
671    /* if the program is already a link to arch, then force execpath */
672    int needexecpath = wrapped(*argv);
673
674    /*
675     * If we don't have any architecutures, try ARCHPREFERENCE and plist
676     * files.
677     */
678    if((cpu->count == 0) || needexecpath)
679        spawnFromPreferences(cpu, needexecpath, argv); /* doesn't return */
680
681    /*
682     * Call posix_spawnp on the program name.
683     */
684    spawnIt(cpu, 1, *argv, argv);
685}
686
687
688/* the main() routine */
689int
690main(int argc, char **argv)
691{
692    const char *prog = getprogname();
693    int my_name_is_arch;
694    CPU cpu;
695
696    if(strcmp(prog, MACHINE_PROG) == 0) {
697        if(argc > 1)
698            errx(-1, "no arguments accepted");
699        arch(0); /* the "machine" command was called */
700    } else if((my_name_is_arch = (strcmp(prog, ARCH_PROG) == 0))) {
701        if(argc == 1)
702            arch(1); /* the "arch" command with no arguments was called */
703    }
704
705    initCPU(&cpu);
706
707    if(my_name_is_arch)
708        spawnFromArgs(&cpu, argv);
709    else
710        spawnFromPreferences(&cpu, 1, argv);
711
712    /* should never get here */
713    errx(1, "returned from spawn");
714}
715