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
},
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
107 #error "Unsupported architecture"
109 bool unrecognizednative32seen
= false;
110 bool unrecognizednative64seen
= false;
113 * arch - perform the original behavior of the arch and machine commands.
114 * The archcmd flag is non-zero for the arch command, zero for the machine
115 * command. This routine never returns.
120 const NXArchInfo
*arch
= NXGetLocalArchInfo();
123 errx(-1, "Unknown architecture.");
125 arch
= NXGetArchInfoFromCpuType(arch
->cputype
, CPU_SUBTYPE_MULTIPLE
);
127 errx(-1, "Unknown architecture.");
129 printf("%s%s", arch
->name
, (isatty(STDIN_FILENO
) ? "\n" : ""));
134 * spawnIt - run the posix_spawn command. cpu is the auto-sizing CPU structure.
135 * pflag is non-zero to call posix_spawnp; zero means to call posix_spawn.
136 * str is the name/path to pass to posix_spawn{,p}, and argv are
137 * the argument arrays to pass. This routine never returns.
140 spawnIt(CPU
*cpu
, int pflag
, const char *str
, char **argv
)
142 posix_spawnattr_t attr
;
146 size_t count
= cpu
->count
;
147 cpu_type_t
*prefs
= cpu
->buf
;
150 if(unrecognizednative32seen
)
151 warnx("Unsupported native 32-bit architecture");
152 if(unrecognizednative64seen
)
153 warnx("Unsupported native 64-bit architecture");
157 if(unrecognizednative32seen
)
158 fprintf(stderr
, "warning: unsupported native 32-bit architecture\n");
159 if(unrecognizednative64seen
)
160 fprintf(stderr
, "warning: unsupported native 64-bit architecture\n");
162 if((ret
= posix_spawnattr_init(&attr
)) != 0)
163 errc(1, ret
, "posix_spawnattr_init");
164 /* do the equivalent of exec, rather than creating a separate process */
165 if((ret
= posix_spawnattr_setflags(&attr
, POSIX_SPAWN_SETEXEC
)) != 0)
166 errc(1, ret
, "posix_spawnattr_setflags");
167 if((ret
= posix_spawnattr_setbinpref_np(&attr
, count
, prefs
, &copied
)) != 0)
168 errc(1, ret
, "posix_spawnattr_setbinpref_np");
170 errx(1, "posix_spawnattr_setbinpref_np only copied %lu of %lu", copied
, count
);
172 ret
= posix_spawnp(&pid
, str
, NULL
, &attr
, argv
, envCopy
? envCopy
: environ
);
174 ret
= posix_spawn(&pid
, str
, NULL
, &attr
, argv
, envCopy
? envCopy
: environ
);
175 errc(1, ret
, "posix_spawn%s: %s", (pflag
? "p" : ""), str
);
179 * initCPU - initialize a CPU structure, a dynamically expanding CPU types
188 cpu
->buf
= (cpu_type_t
*)malloc(cpu
->capacity
* sizeof(cpu_type_t
));
190 err(1, "Failed to malloc CPU buffer");
194 * addCPU - add a new CPU type value to the CPU structure, expanding
195 * the array as necessary.
198 addCPU(CPU
*cpu
, cpu_type_t n
)
200 if(cpu
->count
== cpu
->capacity
) {
201 cpu_type_t
*newcpubuf
;
204 newcpubuf
= (cpu_type_t
*)realloc(cpu
->buf
, cpu
->capacity
* sizeof(cpu_type_t
));
206 err(1, "Out of memory realloc-ing CPU structure");
207 cpu
->buf
= newcpubuf
;
209 cpu
->buf
[cpu
->count
++] = n
;
213 * addCPUbyname - add a new CPU type, given by name, to the CPU structure,
214 * expanding the array as necessary. The name is converted to a type value
215 * by the ArchDict dictionary.
218 addCPUbyname(CPU
*cpu
, const char *name
)
222 for (i
=0; i
< sizeof(knownArchs
)/sizeof(knownArchs
[0]); i
++) {
223 if (0 == strcasecmp(name
, knownArchs
[i
].arch
)) {
224 addCPU(cpu
, knownArchs
[i
].cpu
);
229 /* Didn't match a string in knownArchs */
230 warnx("Unknown architecture: %s", name
);
235 * useEnv - parse the environment variable for CPU preferences. Use name
236 * to look for program-specific preferences, and append any CPU types to cpu.
237 * Returns the number of CPU types. Returns any specified execute path in
240 * The environment variable ARCHPREFERENCE has the format:
242 * a semicolon separated list of specifiers. Each specifier has the format:
243 * [prog:[execpath:]]type[,type]...
244 * a comma separate list of CPU type names, optionally proceeded by a program
245 * name and an execpath. If program name exist, that types only apply to that
246 * program. If execpath is specified, it is returned. If no program name
247 * exists, then it applies to all programs. So ordering of the specifiers is
248 * important, as the default (no program name) specifier must be last.
251 useEnv(CPU
*cpu
, const char *name
, char **execpath
)
253 char *val
= getenv(envname
);
257 /* cp will point to the basename of name */
258 const char *cp
= strrchr(name
, '/');
262 errx(1, "%s: no name after last slash", name
);
265 /* make a copy of the environment variable value, so we can modify it */
268 err(1, "Can't copy environment %s", envname
);
271 /* for each specifier */
272 while((blk
= strsep(&str
, ";")) != NULL
) {
274 continue; /* two adjacent semicolons */
275 /* now split on colons */
276 char *n
= strsep(&blk
, ":");
278 char *p
= strsep(&blk
, ":");
279 if(!blk
) { /* there is only one colon, so no execpath */
282 } else if(!*p
) /* two consecutive colons, so no execpath */
285 continue; /* no cpu list, so skip */
286 /* if the name matches, or there is no name, process the cpus */
287 if(!*n
|| strcmp(n
, cp
) == 0) {
288 if(cpu
->count
== 0) { /* only if we haven't processed architectures */
290 while((t
= strsep(&blk
, ",")) != NULL
)
291 addCPUbyname(cpu
, t
);
293 *execpath
= (*n
? p
: NULL
); /* only use the exec path is name is set */
296 } else { /* no colons at all, so process as default */
297 if(cpu
->count
== 0) { /* only if we haven't processed architectures */
299 while((n
= strsep(&blk
, ",")) != NULL
)
300 addCPUbyname(cpu
, n
);
306 if(cpu
->errs
) /* errors during addCPUbyname are fatal */
308 return cpu
->count
; /* return count of architectures */
312 * spawnFromPreference - called when argv[0] is not "arch" or "machine", or
313 * argv[0] was arch, but no commandline architectures were specified.
314 * If the environment variable ARCHPREFERENCE is specified, and there is a
315 * match to argv[0], use the specified cpu preferences. If no exec path
316 * is specified in ARCHPREFERENCE, or no match is found in ARCHPREFERENCE,
317 * get any additional information from a .plist file with the name of argv[0].
318 * This routine never returns.
321 spawnFromPreferences(CPU
*cpu
, int needexecpath
, char **argv
)
324 char fpath
[PATH_MAX
];
325 char execpath2
[PATH_MAX
];
326 CFDictionaryRef plist
= NULL
;
327 NSSearchPathEnumerationState state
;
329 const char *prog
= strrchr(*argv
, '/');
336 errx(1, "Not program name specified");
338 /* check the environment variable first */
339 if((count
= useEnv(cpu
, prog
, &epath
)) > 0) {
340 /* if we were called as arch, use posix_spawnp */
342 spawnIt(cpu
, 1, (epath
? epath
: *argv
), argv
);
343 /* otherwise, if we have the executable path, call posix_spawn */
345 spawnIt(cpu
, 0, epath
, argv
);
348 state
= NSStartSearchPathEnumeration(NSLibraryDirectory
, NSAllDomainsMask
);
349 while ((state
= NSGetNextSearchPathEnumeration(state
, fpath
))) {
352 CFReadStreamRef stream
;
354 if (fpath
[0] == '~') {
358 bzero(&pglob
, sizeof(pglob
));
360 gret
= glob(fpath
, GLOB_TILDE
, NULL
, &pglob
);
363 for (i
=0; i
< pglob
.gl_pathc
; i
++) {
364 /* take the first glob expansion */
365 strlcpy(fpath
, pglob
.gl_pathv
[i
], sizeof(fpath
));
373 strlcat(fpath
, "/" kSettingsDir
"/", sizeof(fpath
));
374 strlcat(fpath
, prog
, sizeof(fpath
));
375 strlcat(fpath
, kPlistExtension
, sizeof(fpath
));
376 // printf("component: %s\n", fpath);
383 fd
= open(fpath
, O_RDONLY
, 0);
385 ret
= fstat(fd
, &sb
);
387 if (sb
.st_size
<= SIZE_T_MAX
) {
388 length
= (size_t)sb
.st_size
;
389 buffer
= malloc(length
); /* ownership transferred to CFData */
391 rsize
= read(fd
, buffer
, length
);
392 if (rsize
== length
) {
393 CFDataRef data
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, buffer
, length
, kCFAllocatorMalloc
);
396 plist
= CFPropertyListCreateWithData(kCFAllocatorDefault
, data
, kCFPropertyListImmutable
, NULL
, NULL
);
415 if (CFGetTypeID(plist
) != CFDictionaryGetTypeID())
416 errx(1, "%s: plist not a dictionary", fpath
);
418 errx(1, "Can't find any plists for %s", prog
);
422 int errs
= 0; /* scan for all errors and fail later */
423 do { /* begin block */
424 /* check the plist version */
425 CFStringRef vers
= CFDictionaryGetValue(plist
, CFSTR(kKeyPlistVersion
));
427 warnx("%s: No key %s", fpath
, kKeyPlistVersion
);
429 } else if(CFGetTypeID(vers
) != CFStringGetTypeID()) {
430 warnx("%s: %s is not a string", fpath
, kKeyPlistVersion
);
432 } else if(!CFEqual(vers
, CFSTR("1.0"))) {
433 warnx("%s: %s not 1.0", fpath
, kKeyPlistVersion
);
436 /* get the execpath */
437 CFStringRef execpath
= CFDictionaryGetValue(plist
, CFSTR(kKeyExecPath
));
439 warnx("%s: No key %s", fpath
, kKeyExecPath
);
441 } else if(CFGetTypeID(execpath
) != CFStringGetTypeID()) {
442 warnx("%s: %s is not a string", fpath
, kKeyExecPath
);
445 if (!CFStringGetFileSystemRepresentation(execpath
, execpath2
, sizeof(execpath2
))) {
446 warnx("%s: could not get exec path", fpath
);
449 /* if we already got cpu preferences from ARCHPREFERENCE, we are done */
452 /* otherwise, parse the cpu preferences from the plist */
453 CFArrayRef p
= CFDictionaryGetValue(plist
, CFSTR(kKeyPrefOrder
));
455 warnx("%s: No key %s", fpath
, kKeyPrefOrder
);
457 } else if(CFGetTypeID(p
) != CFArrayGetTypeID()) {
458 warnx("%s: %s is not an array", fpath
, kKeyPrefOrder
);
460 } else if((count
= CFArrayGetCount(p
)) == 0) {
461 warnx("%s: no entries in %s", fpath
, kKeyPrefOrder
);
464 /* finally build the cpu type array */
465 for(i
= 0; i
< count
; i
++) {
466 CFStringRef a
= CFArrayGetValueAtIndex(p
, i
);
467 if(CFGetTypeID(a
) != CFStringGetTypeID()) {
468 warnx("%s: entry %lu of %s is not a string", fpath
, i
, kKeyPrefOrder
);
472 if (CFStringGetCString(a
, astr
, sizeof(astr
), kCFStringEncodingASCII
)) {
473 addCPUbyname(cpu
, astr
);
478 } while(0); /* end block */
479 if(errs
) /* exit if there were any reported errors */
484 /* call posix_spawn */
485 spawnIt(cpu
, 0, execpath2
, argv
);
493 " Display the machine's architecture type\n"
494 "Usage: %s {-arch_name | -arch arch_name} ... [-c] [-d envname] ... [-e envname=value] ... [-h] prog [arg ...]\n"
495 " Run prog with any arguments, using the given architecture\n"
496 " order. If no architectures are specified, use the\n"
497 " ARCHPREFERENCE environment variable, or a property list file.\n"
498 " -c will clear out all environment variables before running prog.\n"
499 " -d will delete the given environment variable before running prog.\n"
500 " -e will add the given environment variable/value before running prog.\n"
501 " -h will print usage message and exit.\n",
502 ARCH_PROG
, ARCH_PROG
);
507 * wrapped - check the path to see if it is a link to /usr/bin/arch.
510 wrapped(const char *name
)
516 char buf
[MAXPATHLEN
], rpbuf
[MAXPATHLEN
];
521 do { /* begin block */
522 /* If it's an absolute or relative path name, it's easy. */
523 if(index(name
, '/')) {
524 if(stat(name
, &sb
) == 0 && S_ISREG(sb
.st_mode
) && access(name
, X_OK
) == 0) {
528 errx(1, "%s isn't executable", name
);
531 /* search the PATH, looking for name */
532 if((path
= getenv("PATH")) == NULL
)
533 path
= _PATH_DEFPATH
;
535 cur
= alloca(strlen(path
) + 1);
539 while((p
= strsep(&cur
, ":")) != NULL
) {
541 * It's a SHELL path -- double, leading and trailing colons
542 * mean the current directory.
551 * If the path is too long complain. This is a possible
552 * security issue; given a way to make the path too long
553 * the user may execute the wrong program.
555 if(lp
+ ln
+ 2 > sizeof(buf
)) {
556 warn("%s: path too long", p
);
561 bcopy(name
, buf
+ lp
+ 1, ln
);
562 buf
[lp
+ ln
+ 1] = '\0';
563 if(stat(buf
, &sb
) == 0 && S_ISREG(sb
.st_mode
) && access(buf
, X_OK
) == 0) {
569 errx(1, "Can't find %s in PATH", name
);
570 } while(0); /* end block */
571 if(realpath(bp
, rpbuf
) == NULL
)
572 errx(1, "realpath failed on %s", bp
);
573 return (strcmp(rpbuf
, "/usr/bin/" ARCH_PROG
) == 0);
577 * spawnFromArgs - called when arch has arguments specified. The arch command
578 * line arguments are:
579 * % arch [[{-xxx | -arch xxx}]...] prog [arg]...
580 * where xxx is a cpu name, and the command to execute and its arguments follow.
581 * If no commandline cpu names are given, the environment variable
582 * ARCHPREFERENCE is used. This routine never returns.
585 #define MATCHARG(a,m) ({ \
586 const char *arg = *(a); \
587 if(arg[1] == '-') arg++; \
588 strcmp(arg, (m)) == 0; \
591 #define MATCHARGWITHVALUE(a,m,n,e) ({ \
592 const char *ret = NULL; \
593 const char *arg = *(a); \
594 if(arg[1] == '-') arg++; \
595 if(strcmp(arg, (m)) == 0) { \
596 if(*++(a) == NULL) { \
601 } else if(strncmp(arg, (m), (n)) == 0 && arg[n] == '=') { \
602 ret = arg + (n) + 1; \
607 #define MAKEENVCOPY(e) \
609 envCopy = _copyenv(environ); \
610 if(envCopy == NULL) \
615 spawnFromArgs(CPU
*cpu
, char **argv
)
617 const char *ap
, *ret
;
619 /* process arguments */
620 for(argv
++; *argv
&& **argv
== '-'; argv
++) {
621 if((ret
= MATCHARGWITHVALUE(argv
, "-arch", 5, "-arch without architecture"))) {
623 } else if(MATCHARG(argv
, "-32")) {
626 unrecognizednative32seen
= true;
629 } else if(MATCHARG(argv
, "-64")) {
632 unrecognizednative64seen
= true;
635 } else if(MATCHARG(argv
, "-c")) {
637 envCopy
= _copyenv(NULL
); // create empty environment
639 errx(1, "Out of memory processing -c");
641 } else if((ret
= MATCHARGWITHVALUE(argv
, "-d", 2, "-d without envname"))) {
642 MAKEENVCOPY("Out of memory processing -d");
643 _unsetenvp(ret
, &envCopy
, NULL
);
645 } else if((ret
= MATCHARGWITHVALUE(argv
, "-e", 2, "-e without envname=value"))) {
646 MAKEENVCOPY("Out of memory processing -e");
647 const char *cp
= strchr(ret
, '=');
649 warnx("-e %s: no equal sign", ret
);
652 cp
++; // skip to value
654 * _setenvp() only uses the name before any equal sign found in
655 * the first argument.
657 _setenvp(ret
, cp
, 1, &envCopy
, NULL
);
659 } else if(MATCHARG(argv
, "-h")) {
665 addCPUbyname(cpu
, ap
);
669 if(!*argv
|| !**argv
) {
670 warnx("No command to execute");
673 /* if the program is already a link to arch, then force execpath */
674 int needexecpath
= wrapped(*argv
);
677 * If we don't have any architecutures, try ARCHPREFERENCE and plist
680 if((cpu
->count
== 0) || needexecpath
)
681 spawnFromPreferences(cpu
, needexecpath
, argv
); /* doesn't return */
684 * Call posix_spawnp on the program name.
686 spawnIt(cpu
, 1, *argv
, argv
);
690 /* the main() routine */
692 main(int argc
, char **argv
)
694 const char *prog
= getprogname();
698 if(strcmp(prog
, MACHINE_PROG
) == 0) {
700 errx(-1, "no arguments accepted");
701 arch(0); /* the "machine" command was called */
702 } else if((my_name_is_arch
= (strcmp(prog
, ARCH_PROG
) == 0))) {
704 arch(1); /* the "arch" command with no arguments was called */
710 spawnFromArgs(&cpu
, argv
);
712 spawnFromPreferences(&cpu
, 1, argv
);
714 /* should never get here */
715 errx(1, "returned from spawn");