]> git.saurik.com Git - apple/system_cmds.git/blob - system_cmds-597.1.1/arch.tproj/arch.c
system_cmds-597.90.1.tar.gz
[apple/system_cmds.git] / system_cmds-597.1.1 / arch.tproj / arch.c
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
58 static const char envname[] = "ARCHPREFERENCE";
59
60 /* The CPU struct contains the argument buffer to posix_spawnattr_setbinpref_np */
61
62 typedef struct {
63 cpu_type_t *buf;
64 int errs;
65 size_t count;
66 size_t capacity;
67 } CPU;
68
69 typedef struct {
70 const char *arch;
71 cpu_type_t cpu;
72 } CPUTypes;
73
74 static 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 */
86 char **_copyenv(char **env);
87 int _setenvp(const char *name, const char *value, int rewrite, char ***envp, void *state);
88 int _unsetenvp(const char *name, char ***envp, void *state);
89
90 /* copy of environment */
91 char **envCopy = NULL;
92 extern 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
107 bool unrecognizednative32seen = false;
108 bool 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 */
115 static void __dead2
116 arch(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 */
137 static void __dead2
138 spawnIt(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 */
180 static void
181 initCPU(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 */
195 static void
196 addCPU(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 */
215 static void
216 addCPUbyname(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 */
248 static size_t
249 useEnv(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 */
318 static void __dead2
319 spawnFromPreferences(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
486 static void __dead2
487 usage(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 */
507 static int
508 wrapped(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
612 static void __dead2
613 spawnFromArgs(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 */
689 int
690 main(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 }