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