2 * Copyright (c) 1999, 2006, 2011 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>
46 #define ARCH_PROG "arch"
49 #define MACHINE_PROG "machine"
52 #define kKeyExecPath "ExecutablePath"
53 #define kKeyPlistVersion "PropertyListVersion"
54 #define kKeyPrefOrder "PreferredOrder"
55 #define kPlistExtension ".plist"
56 #define kSettingsDir "archSettings"
58 static const char envname
[] = "ARCHPREFERENCE";
60 /* The CPU struct contains the argument buffer to posix_spawnattr_setbinpref_np */
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
},
81 #error "Unsupported architecture"
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
);
90 /* copy of environment */
91 char **envCopy
= NULL
;
92 extern char **environ
;
95 * The native 32 and 64-bit architectures (this is relative to the architecture
96 * the arch command is running. NULL means unsupported.
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
105 #error "Unsupported architecture"
107 bool unrecognizednative32seen
= false;
108 bool unrecognizednative64seen
= false;
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.
118 const NXArchInfo
*arch
= NXGetLocalArchInfo();
121 errx(-1, "Unknown architecture.");
123 arch
= NXGetArchInfoFromCpuType(arch
->cputype
, CPU_SUBTYPE_MULTIPLE
);
125 errx(-1, "Unknown architecture.");
127 printf("%s%s", arch
->name
, (isatty(STDIN_FILENO
) ? "\n" : ""));
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.
138 spawnIt(CPU
*cpu
, int pflag
, const char *str
, char **argv
)
140 posix_spawnattr_t attr
;
144 size_t count
= cpu
->count
;
145 cpu_type_t
*prefs
= cpu
->buf
;
148 if(unrecognizednative32seen
)
149 warnx("Unsupported native 32-bit architecture");
150 if(unrecognizednative64seen
)
151 warnx("Unsupported native 64-bit architecture");
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");
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");
168 errx(1, "posix_spawnattr_setbinpref_np only copied %lu of %lu", copied
, count
);
170 ret
= posix_spawnp(&pid
, str
, NULL
, &attr
, argv
, envCopy
? envCopy
: environ
);
172 ret
= posix_spawn(&pid
, str
, NULL
, &attr
, argv
, envCopy
? envCopy
: environ
);
173 errc(1, ret
, "posix_spawn%s: %s", (pflag
? "p" : ""), str
);
177 * initCPU - initialize a CPU structure, a dynamically expanding CPU types
186 cpu
->buf
= (cpu_type_t
*)malloc(cpu
->capacity
* sizeof(cpu_type_t
));
188 err(1, "Failed to malloc CPU buffer");
192 * addCPU - add a new CPU type value to the CPU structure, expanding
193 * the array as necessary.
196 addCPU(CPU
*cpu
, cpu_type_t n
)
198 if(cpu
->count
== cpu
->capacity
) {
199 cpu_type_t
*newcpubuf
;
202 newcpubuf
= (cpu_type_t
*)realloc(cpu
->buf
, cpu
->capacity
* sizeof(cpu_type_t
));
204 err(1, "Out of memory realloc-ing CPU structure");
205 cpu
->buf
= newcpubuf
;
207 cpu
->buf
[cpu
->count
++] = n
;
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.
216 addCPUbyname(CPU
*cpu
, const char *name
)
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
);
227 /* Didn't match a string in knownArchs */
228 warnx("Unknown architecture: %s", name
);
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
238 * The environment variable ARCHPREFERENCE has the format:
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.
249 useEnv(CPU
*cpu
, const char *name
, char **execpath
)
251 char *val
= getenv(envname
);
255 /* cp will point to the basename of name */
256 const char *cp
= strrchr(name
, '/');
260 errx(1, "%s: no name after last slash", name
);
263 /* make a copy of the environment variable value, so we can modify it */
266 err(1, "Can't copy environment %s", envname
);
269 /* for each specifier */
270 while((blk
= strsep(&str
, ";")) != NULL
) {
272 continue; /* two adjacent semicolons */
273 /* now split on colons */
274 char *n
= strsep(&blk
, ":");
276 char *p
= strsep(&blk
, ":");
277 if(!blk
) { /* there is only one colon, so no execpath */
280 } else if(!*p
) /* two consecutive colons, so no execpath */
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 */
288 while((t
= strsep(&blk
, ",")) != NULL
)
289 addCPUbyname(cpu
, t
);
291 *execpath
= (*n
? p
: NULL
); /* only use the exec path is name is set */
294 } else { /* no colons at all, so process as default */
295 if(cpu
->count
== 0) { /* only if we haven't processed architectures */
297 while((n
= strsep(&blk
, ",")) != NULL
)
298 addCPUbyname(cpu
, n
);
304 if(cpu
->errs
) /* errors during addCPUbyname are fatal */
306 return cpu
->count
; /* return count of architectures */
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.
319 spawnFromPreferences(CPU
*cpu
, int needexecpath
, char **argv
)
322 char fpath
[PATH_MAX
];
323 char execpath2
[PATH_MAX
];
324 CFDictionaryRef plist
= NULL
;
325 NSSearchPathEnumerationState state
;
327 const char *prog
= strrchr(*argv
, '/');
334 errx(1, "Not program name specified");
336 /* check the environment variable first */
337 if((count
= useEnv(cpu
, prog
, &epath
)) > 0) {
338 /* if we were called as arch, use posix_spawnp */
340 spawnIt(cpu
, 1, (epath
? epath
: *argv
), argv
);
341 /* otherwise, if we have the executable path, call posix_spawn */
343 spawnIt(cpu
, 0, epath
, argv
);
346 state
= NSStartSearchPathEnumeration(NSLibraryDirectory
, NSAllDomainsMask
);
347 while ((state
= NSGetNextSearchPathEnumeration(state
, fpath
))) {
350 CFReadStreamRef stream
;
352 if (fpath
[0] == '~') {
356 bzero(&pglob
, sizeof(pglob
));
358 gret
= glob(fpath
, GLOB_TILDE
, NULL
, &pglob
);
361 for (i
=0; i
< pglob
.gl_pathc
; i
++) {
362 /* take the first glob expansion */
363 strlcpy(fpath
, pglob
.gl_pathv
[i
], sizeof(fpath
));
371 strlcat(fpath
, "/" kSettingsDir
"/", sizeof(fpath
));
372 strlcat(fpath
, prog
, sizeof(fpath
));
373 strlcat(fpath
, kPlistExtension
, sizeof(fpath
));
374 // printf("component: %s\n", fpath);
381 fd
= open(fpath
, O_RDONLY
, 0);
383 ret
= fstat(fd
, &sb
);
385 if (sb
.st_size
<= SIZE_T_MAX
) {
386 length
= (size_t)sb
.st_size
;
387 buffer
= malloc(length
); /* ownership transferred to CFData */
389 rsize
= read(fd
, buffer
, length
);
390 if (rsize
== length
) {
391 CFDataRef data
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, buffer
, length
, kCFAllocatorMalloc
);
394 plist
= CFPropertyListCreateWithData(kCFAllocatorDefault
, data
, kCFPropertyListImmutable
, NULL
, NULL
);
413 if (CFGetTypeID(plist
) != CFDictionaryGetTypeID())
414 errx(1, "%s: plist not a dictionary", fpath
);
416 errx(1, "Can't find any plists for %s", prog
);
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
));
425 warnx("%s: No key %s", fpath
, kKeyPlistVersion
);
427 } else if(CFGetTypeID(vers
) != CFStringGetTypeID()) {
428 warnx("%s: %s is not a string", fpath
, kKeyPlistVersion
);
430 } else if(!CFEqual(vers
, CFSTR("1.0"))) {
431 warnx("%s: %s not 1.0", fpath
, kKeyPlistVersion
);
434 /* get the execpath */
435 CFStringRef execpath
= CFDictionaryGetValue(plist
, CFSTR(kKeyExecPath
));
437 warnx("%s: No key %s", fpath
, kKeyExecPath
);
439 } else if(CFGetTypeID(execpath
) != CFStringGetTypeID()) {
440 warnx("%s: %s is not a string", fpath
, kKeyExecPath
);
443 if (!CFStringGetFileSystemRepresentation(execpath
, execpath2
, sizeof(execpath2
))) {
444 warnx("%s: could not get exec path", fpath
);
447 /* if we already got cpu preferences from ARCHPREFERENCE, we are done */
450 /* otherwise, parse the cpu preferences from the plist */
451 CFArrayRef p
= CFDictionaryGetValue(plist
, CFSTR(kKeyPrefOrder
));
453 warnx("%s: No key %s", fpath
, kKeyPrefOrder
);
455 } else if(CFGetTypeID(p
) != CFArrayGetTypeID()) {
456 warnx("%s: %s is not an array", fpath
, kKeyPrefOrder
);
458 } else if((count
= CFArrayGetCount(p
)) == 0) {
459 warnx("%s: no entries in %s", fpath
, kKeyPrefOrder
);
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
);
470 if (CFStringGetCString(a
, astr
, sizeof(astr
), kCFStringEncodingASCII
)) {
471 addCPUbyname(cpu
, astr
);
476 } while(0); /* end block */
477 if(errs
) /* exit if there were any reported errors */
482 /* call posix_spawn */
483 spawnIt(cpu
, 0, execpath2
, argv
);
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
);
505 * wrapped - check the path to see if it is a link to /usr/bin/arch.
508 wrapped(const char *name
)
514 char buf
[MAXPATHLEN
], rpbuf
[MAXPATHLEN
];
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) {
526 errx(1, "%s isn't executable", name
);
529 /* search the PATH, looking for name */
530 if((path
= getenv("PATH")) == NULL
)
531 path
= _PATH_DEFPATH
;
533 cur
= alloca(strlen(path
) + 1);
537 while((p
= strsep(&cur
, ":")) != NULL
) {
539 * It's a SHELL path -- double, leading and trailing colons
540 * mean the current directory.
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.
553 if(lp
+ ln
+ 2 > sizeof(buf
)) {
554 warn("%s: path too long", p
);
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) {
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);
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.
583 #define MATCHARG(a,m) ({ \
584 const char *arg = *(a); \
585 if(arg[1] == '-') arg++; \
586 strcmp(arg, (m)) == 0; \
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) { \
599 } else if(strncmp(arg, (m), (n)) == 0 && arg[n] == '=') { \
600 ret = arg + (n) + 1; \
605 #define MAKEENVCOPY(e) \
607 envCopy = _copyenv(environ); \
608 if(envCopy == NULL) \
613 spawnFromArgs(CPU
*cpu
, char **argv
)
615 const char *ap
, *ret
;
617 /* process arguments */
618 for(argv
++; *argv
&& **argv
== '-'; argv
++) {
619 if((ret
= MATCHARGWITHVALUE(argv
, "-arch", 5, "-arch without architecture"))) {
621 } else if(MATCHARG(argv
, "-32")) {
624 unrecognizednative32seen
= true;
627 } else if(MATCHARG(argv
, "-64")) {
630 unrecognizednative64seen
= true;
633 } else if(MATCHARG(argv
, "-c")) {
635 envCopy
= _copyenv(NULL
); // create empty environment
637 errx(1, "Out of memory processing -c");
639 } else if((ret
= MATCHARGWITHVALUE(argv
, "-d", 2, "-d without envname"))) {
640 MAKEENVCOPY("Out of memory processing -d");
641 _unsetenvp(ret
, &envCopy
, NULL
);
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
, '=');
647 warnx("-e %s: no equal sign", ret
);
650 cp
++; // skip to value
652 * _setenvp() only uses the name before any equal sign found in
653 * the first argument.
655 _setenvp(ret
, cp
, 1, &envCopy
, NULL
);
657 } else if(MATCHARG(argv
, "-h")) {
663 addCPUbyname(cpu
, ap
);
667 if(!*argv
|| !**argv
) {
668 warnx("No command to execute");
671 /* if the program is already a link to arch, then force execpath */
672 int needexecpath
= wrapped(*argv
);
675 * If we don't have any architecutures, try ARCHPREFERENCE and plist
678 if((cpu
->count
== 0) || needexecpath
)
679 spawnFromPreferences(cpu
, needexecpath
, argv
); /* doesn't return */
682 * Call posix_spawnp on the program name.
684 spawnIt(cpu
, 1, *argv
, argv
);
688 /* the main() routine */
690 main(int argc
, char **argv
)
692 const char *prog
= getprogname();
696 if(strcmp(prog
, MACHINE_PROG
) == 0) {
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))) {
702 arch(1); /* the "arch" command with no arguments was called */
708 spawnFromArgs(&cpu
, argv
);
710 spawnFromPreferences(&cpu
, 1, argv
);
712 /* should never get here */
713 errx(1, "returned from spawn");