2 * Copyright (c) 1999-2016 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 #include <sys/cdefs.h>
32 #include <sys/types.h>
34 #include <sys/param.h>
37 #include <mach/mach.h>
38 #include <mach-o/arch.h>
40 #include <sys/fcntl.h>
42 #include <CoreFoundation/CoreFoundation.h>
43 #include <NSSystemDirectories.h>
47 #define ARCH_PROG "arch"
50 #define MACHINE_PROG "machine"
53 #define kKeyExecPath "ExecutablePath"
54 #define kKeyPlistVersion "PropertyListVersion"
55 #define kKeyPrefOrder "PreferredOrder"
56 #define kPlistExtension ".plist"
57 #define kSettingsDir "archSettings"
59 static const char envname
[] = "ARCHPREFERENCE";
61 /* The CPU struct contains the argument buffer to posix_spawnattr_setbinpref_np */
75 static 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
},
82 #error "Unsupported architecture"
87 char **_copyenv(char **env
);
88 int _setenvp(const char *name
, const char *value
, int rewrite
, char ***envp
, void *state
);
89 int _unsetenvp(const char *name
, char ***envp
, void *state
);
91 /* copy of environment */
92 char **envCopy
= NULL
;
93 extern char **environ
;
96 * The native 32 and 64-bit architectures (this is relative to the architecture
97 * the arch command is running. NULL means unsupported.
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
106 #error "Unsupported architecture"
108 bool unrecognizednative32seen
= false;
109 bool unrecognizednative64seen
= false;
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.
119 const NXArchInfo
*arch
= NXGetLocalArchInfo();
122 errx(-1, "Unknown architecture.");
124 arch
= NXGetArchInfoFromCpuType(arch
->cputype
, CPU_SUBTYPE_MULTIPLE
);
126 errx(-1, "Unknown architecture.");
128 printf("%s%s", arch
->name
, (isatty(STDIN_FILENO
) ? "\n" : ""));
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.
139 spawnIt(CPU
*cpu
, int pflag
, const char *str
, char **argv
)
141 posix_spawnattr_t attr
;
145 size_t count
= cpu
->count
;
146 cpu_type_t
*prefs
= cpu
->buf
;
149 if(unrecognizednative32seen
)
150 warnx("Unsupported native 32-bit architecture");
151 if(unrecognizednative64seen
)
152 warnx("Unsupported native 64-bit architecture");
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");
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");
169 errx(1, "posix_spawnattr_setbinpref_np only copied %lu of %lu", copied
, count
);
171 ret
= posix_spawnp(&pid
, str
, NULL
, &attr
, argv
, envCopy
? envCopy
: environ
);
173 ret
= posix_spawn(&pid
, str
, NULL
, &attr
, argv
, envCopy
? envCopy
: environ
);
174 errc(1, ret
, "posix_spawn%s: %s", (pflag
? "p" : ""), str
);
178 * initCPU - initialize a CPU structure, a dynamically expanding CPU types
187 cpu
->buf
= (cpu_type_t
*)malloc(cpu
->capacity
* sizeof(cpu_type_t
));
189 err(1, "Failed to malloc CPU buffer");
193 * addCPU - add a new CPU type value to the CPU structure, expanding
194 * the array as necessary.
197 addCPU(CPU
*cpu
, cpu_type_t n
)
199 if(cpu
->count
== cpu
->capacity
) {
200 cpu_type_t
*newcpubuf
;
203 newcpubuf
= (cpu_type_t
*)realloc(cpu
->buf
, cpu
->capacity
* sizeof(cpu_type_t
));
205 err(1, "Out of memory realloc-ing CPU structure");
206 cpu
->buf
= newcpubuf
;
208 cpu
->buf
[cpu
->count
++] = n
;
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.
217 addCPUbyname(CPU
*cpu
, const char *name
)
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
);
228 /* Didn't match a string in knownArchs */
229 warnx("Unknown architecture: %s", name
);
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
239 * The environment variable ARCHPREFERENCE has the format:
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.
250 useEnv(CPU
*cpu
, const char *name
, char **execpath
)
252 char *val
= getenv(envname
);
256 /* cp will point to the basename of name */
257 const char *cp
= strrchr(name
, '/');
261 errx(1, "%s: no name after last slash", name
);
264 /* make a copy of the environment variable value, so we can modify it */
267 err(1, "Can't copy environment %s", envname
);
270 /* for each specifier */
271 while((blk
= strsep(&str
, ";")) != NULL
) {
273 continue; /* two adjacent semicolons */
274 /* now split on colons */
275 char *n
= strsep(&blk
, ":");
277 char *p
= strsep(&blk
, ":");
278 if(!blk
) { /* there is only one colon, so no execpath */
281 } else if(!*p
) /* two consecutive colons, so no execpath */
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 */
289 while((t
= strsep(&blk
, ",")) != NULL
)
290 addCPUbyname(cpu
, t
);
292 *execpath
= (*n
? p
: NULL
); /* only use the exec path is name is set */
295 } else { /* no colons at all, so process as default */
296 if(cpu
->count
== 0) { /* only if we haven't processed architectures */
298 while((n
= strsep(&blk
, ",")) != NULL
)
299 addCPUbyname(cpu
, n
);
305 if(cpu
->errs
) /* errors during addCPUbyname are fatal */
307 return cpu
->count
; /* return count of architectures */
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.
320 spawnFromPreferences(CPU
*cpu
, int needexecpath
, char **argv
)
323 char fpath
[PATH_MAX
];
324 char execpath2
[PATH_MAX
];
325 CFDictionaryRef plist
= NULL
;
326 sysdir_search_path_enumeration_state state
;
328 const char *prog
= strrchr(*argv
, '/');
335 errx(1, "Not program name specified");
337 /* check the environment variable first */
338 if((count
= useEnv(cpu
, prog
, &epath
)) > 0) {
339 /* if we were called as arch, use posix_spawnp */
341 spawnIt(cpu
, 1, (epath
? epath
: *argv
), argv
);
342 /* otherwise, if we have the executable path, call posix_spawn */
344 spawnIt(cpu
, 0, epath
, argv
);
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
))) {
350 if (fpath
[0] == '~') {
354 bzero(&pglob
, sizeof(pglob
));
356 gret
= glob(fpath
, GLOB_TILDE
, NULL
, &pglob
);
359 for (i
=0; i
< pglob
.gl_pathc
; i
++) {
360 /* take the first glob expansion */
361 strlcpy(fpath
, pglob
.gl_pathv
[i
], sizeof(fpath
));
369 strlcat(fpath
, "/" kSettingsDir
"/", sizeof(fpath
));
370 strlcat(fpath
, prog
, sizeof(fpath
));
371 strlcat(fpath
, kPlistExtension
, sizeof(fpath
));
372 // printf("component: %s\n", fpath);
379 fd
= open(fpath
, O_RDONLY
, 0);
381 ret
= fstat(fd
, &sb
);
383 if (sb
.st_size
<= SIZE_T_MAX
) {
384 length
= (size_t)sb
.st_size
;
385 buffer
= malloc(length
); /* ownership transferred to CFData */
387 rsize
= read(fd
, buffer
, length
);
388 if (rsize
== length
) {
389 CFDataRef data
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, buffer
, length
, kCFAllocatorMalloc
);
392 plist
= CFPropertyListCreateWithData(kCFAllocatorDefault
, data
, kCFPropertyListImmutable
, NULL
, NULL
);
411 if (CFGetTypeID(plist
) != CFDictionaryGetTypeID())
412 errx(1, "%s: plist not a dictionary", fpath
);
414 errx(1, "Can't find any plists for %s", prog
);
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
));
422 warnx("%s: No key %s", fpath
, kKeyPlistVersion
);
424 } else if(CFGetTypeID(vers
) != CFStringGetTypeID()) {
425 warnx("%s: %s is not a string", fpath
, kKeyPlistVersion
);
427 } else if(!CFEqual(vers
, CFSTR("1.0"))) {
428 warnx("%s: %s not 1.0", fpath
, kKeyPlistVersion
);
431 /* get the execpath */
432 CFStringRef execpath
= CFDictionaryGetValue(plist
, CFSTR(kKeyExecPath
));
434 warnx("%s: No key %s", fpath
, kKeyExecPath
);
436 } else if(CFGetTypeID(execpath
) != CFStringGetTypeID()) {
437 warnx("%s: %s is not a string", fpath
, kKeyExecPath
);
440 if (!CFStringGetFileSystemRepresentation(execpath
, execpath2
, sizeof(execpath2
))) {
441 warnx("%s: could not get exec path", fpath
);
444 /* if we already got cpu preferences from ARCHPREFERENCE, we are done */
447 /* otherwise, parse the cpu preferences from the plist */
448 CFArrayRef p
= CFDictionaryGetValue(plist
, CFSTR(kKeyPrefOrder
));
450 warnx("%s: No key %s", fpath
, kKeyPrefOrder
);
452 } else if(CFGetTypeID(p
) != CFArrayGetTypeID()) {
453 warnx("%s: %s is not an array", fpath
, kKeyPrefOrder
);
455 } else if((count
= CFArrayGetCount(p
)) == 0) {
456 warnx("%s: no entries in %s", fpath
, kKeyPrefOrder
);
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
);
467 if (CFStringGetCString(a
, astr
, sizeof(astr
), kCFStringEncodingASCII
)) {
468 addCPUbyname(cpu
, astr
);
473 } while(0); /* end block */
474 if(errs
) /* exit if there were any reported errors */
479 /* call posix_spawn */
480 spawnIt(cpu
, 0, execpath2
, argv
);
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
);
502 * wrapped - check the path to see if it is a link to /usr/bin/arch.
505 wrapped(const char *name
)
511 char buf
[MAXPATHLEN
], rpbuf
[MAXPATHLEN
];
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) {
523 errx(1, "%s isn't executable", name
);
526 /* search the PATH, looking for name */
527 if((path
= getenv("PATH")) == NULL
)
528 path
= _PATH_DEFPATH
;
530 cur
= alloca(strlen(path
) + 1);
534 while((p
= strsep(&cur
, ":")) != NULL
) {
536 * It's a SHELL path -- double, leading and trailing colons
537 * mean the current directory.
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.
550 if(lp
+ ln
+ 2 > sizeof(buf
)) {
551 warn("%s: path too long", p
);
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) {
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);
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.
580 #define MATCHARG(a,m) ({ \
581 const char *arg = *(a); \
582 if(arg[1] == '-') arg++; \
583 strcmp(arg, (m)) == 0; \
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) { \
596 } else if(strncmp(arg, (m), (n)) == 0 && arg[n] == '=') { \
597 ret = arg + (n) + 1; \
602 #define MAKEENVCOPY(e) \
604 envCopy = _copyenv(environ); \
605 if(envCopy == NULL) \
610 spawnFromArgs(CPU
*cpu
, char **argv
)
612 const char *ap
, *ret
;
614 /* process arguments */
615 for(argv
++; *argv
&& **argv
== '-'; argv
++) {
616 if((ret
= MATCHARGWITHVALUE(argv
, "-arch", 5, "-arch without architecture"))) {
618 } else if(MATCHARG(argv
, "-32")) {
621 unrecognizednative32seen
= true;
624 } else if(MATCHARG(argv
, "-64")) {
627 unrecognizednative64seen
= true;
630 } else if(MATCHARG(argv
, "-c")) {
632 envCopy
= _copyenv(NULL
); // create empty environment
634 errx(1, "Out of memory processing -c");
636 } else if((ret
= MATCHARGWITHVALUE(argv
, "-d", 2, "-d without envname"))) {
637 MAKEENVCOPY("Out of memory processing -d");
638 _unsetenvp(ret
, &envCopy
, NULL
);
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
, '=');
644 warnx("-e %s: no equal sign", ret
);
647 cp
++; // skip to value
649 * _setenvp() only uses the name before any equal sign found in
650 * the first argument.
652 _setenvp(ret
, cp
, 1, &envCopy
, NULL
);
654 } else if(MATCHARG(argv
, "-h")) {
660 addCPUbyname(cpu
, ap
);
664 if(!*argv
|| !**argv
) {
665 warnx("No command to execute");
668 /* if the program is already a link to arch, then force execpath */
669 int needexecpath
= wrapped(*argv
);
672 * If we don't have any architecutures, try ARCHPREFERENCE and plist
675 if((cpu
->count
== 0) || needexecpath
)
676 spawnFromPreferences(cpu
, needexecpath
, argv
); /* doesn't return */
679 * Call posix_spawnp on the program name.
681 spawnIt(cpu
, 1, *argv
, argv
);
685 /* the main() routine */
687 main(int argc
, char **argv
)
689 const char *prog
= getprogname();
693 if(strcmp(prog
, MACHINE_PROG
) == 0) {
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))) {
699 arch(1); /* the "arch" command with no arguments was called */
705 spawnFromArgs(&cpu
, argv
);
707 spawnFromPreferences(&cpu
, 1, argv
);
709 /* should never get here */
710 errx(1, "returned from spawn");