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