]> git.saurik.com Git - apple/system_cmds.git/blame - arch.tproj/arch.m
system_cmds-433.8.tar.gz
[apple/system_cmds.git] / arch.tproj / arch.m
CommitLineData
34d340d7
A
1/*
2 * Copyright (c) 1999, 2006 Apple Computer, 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 <stdlib.h>
28#include <stddef.h>
29#include <unistd.h>
30#include <spawn.h>
31#include <sys/types.h>
32#include <sys/stat.h>
33#include <sys/param.h>
34#include <paths.h>
35#include <err.h>
36#include <mach/mach.h>
37#include <mach-o/arch.h>
38#include <Foundation/Foundation.h>
39
40#ifndef ARCH_PROG
41#define ARCH_PROG "arch"
42#endif
43#define CPUDELTA 10
44#ifndef MACHINE_PROG
45#define MACHINE_PROG "machine"
46#endif
47
48#define CPUCOUNT(c) ((c)->ptr - (c)->buf)
49
50static NSMutableDictionary *ArchDict;
51static NSString *KeyExecPath = @"ExecutablePath";
52static NSString *KeyPlistVersion = @"PropertyListVersion";
53static NSString *KeyPrefOrder = @"PreferredOrder";
54static NSString *PlistExtension = @"plist";
55static NSString *SettingsDir = @"archSettings";
56
57static const char envname[] = "ARCHPREFERENCE";
58
59typedef struct {
60 cpu_type_t *buf;
61 cpu_type_t *ptr;
62 cpu_type_t *end;
63 int errs;
64} CPU;
65
66typedef struct {
67 char *str;
68 int i;
69} StrInt;
70
71static StrInt initArches[] = {
72 {"i386", CPU_TYPE_I386},
73 {"ppc", CPU_TYPE_POWERPC},
74 {"ppc64", CPU_TYPE_POWERPC64},
75 {"x86_64", CPU_TYPE_X86_64},
76 {NULL, 0}
77};
78
79/*
80 * arch - perform the original behavior of the arch and machine commands.
81 * The archcmd flag is non-zero for the arch command, zero for the machine
82 * command. This routine never returns.
83 */
84static void __dead2
85arch(int archcmd)
86{
87 const NXArchInfo *arch = NXGetLocalArchInfo();
88
89 if(!arch)
90 errx(-1, "Unknown architecture.");
91 if(archcmd) {
92 arch = NXGetArchInfoFromCpuType(arch->cputype, CPU_SUBTYPE_MULTIPLE);
93 if(!arch)
94 errx(-1, "Unknown architecture.");
95 }
96 printf("%s%s", arch->name, (isatty(STDIN_FILENO) ? "\n" : ""));
97 exit(0);
98}
99
100/*
101 * spawnIt - run the posix_spawn command. count is the number of CPU types
102 * in the prefs array. pflag is non-zero to call posix_spawnp; zero means to
103 * call posix_spawn. str is the name/path to pass to posix_spawn{,p}, and
104 * argv and environ are the argument and environment arrays to pass. This
105 * routine never returns.
106 */
107static void __dead2
108spawnIt(int count, cpu_type_t *prefs, int pflag, const char *str, char **argv, char **environ)
109{
110
111 posix_spawnattr_t attr;
112 pid_t pid;
113 int ret;
114 size_t copied;
115
116 if((ret = posix_spawnattr_init(&attr)) != 0)
117 errc(1, ret, "posix_spawnattr_init");
118 /* do the equivalent of exec, rather than creating a separate process */
119 if((ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETEXEC)) != 0)
120 errc(1, ret, "posix_spawnattr_setflags");
121 if((ret = posix_spawnattr_setbinpref_np(&attr, count, prefs, &copied)) != 0)
122 errc(1, ret, "posix_spawnattr_setbinpref_np");
123 if(copied != count)
124 errx(1, "posix_spawnattr_setbinpref_np only copied %d of %d", (int)copied, count);
125 if(pflag)
126 ret = posix_spawnp(&pid, str, NULL, &attr, argv, environ);
127 else
128 ret = posix_spawn(&pid, str, NULL, &attr, argv, environ);
129 errc(1, ret, "posix_spawn%s: %s", (pflag ? "p" : ""), str);
130}
131
132/*
133 * initCPU - initialize a CPU structure, a dynamically expanding CPU types
134 * array.
135 */
136static void
137initCPU(CPU *cpu)
138{
139 cpu->buf = (cpu_type_t *)malloc(CPUDELTA * sizeof(cpu_type_t));
140 if(!cpu->buf)
141 err(1, "Failed to malloc CPU buffer");
142 cpu->ptr = cpu->buf;
143 cpu->end = cpu->buf + CPUDELTA;
144 cpu->errs = 0;
145}
146
147/*
148 * addCPU - add a new CPU type value to the CPU structure, expanding
149 * the array as necessary.
150 */
151static void
152addCPU(CPU *cpu, int n)
153{
154 if(cpu->ptr >= cpu->end) {
155 cpu_type_t *new = realloc(cpu->buf, (cpu->end - cpu->buf + CPUDELTA) * sizeof(cpu_type_t));
156 if(!new)
157 err(1, "Out of memory realloc-ing CPU structure");
158 ptrdiff_t diff = (void *)new - (void *)cpu->buf;
159 cpu->buf = new;
160 cpu->ptr = (cpu_type_t *)((void *)cpu->ptr + diff);
161 cpu->end = (cpu_type_t *)((void *)cpu->end + diff) + CPUDELTA;
162 }
163 *cpu->ptr++ = n;
164}
165
166/*
167 * addCPUbyname - add a new CPU type, given by name, to the CPU structure,
168 * expanding the array as necessary. The name is converted to a type value
169 * by the ArchDict dictionary.
170 */
171static void
172addCPUbyname(CPU *cpu, const char *name)
173{
174 NSNumber *n = (NSNumber *)[ArchDict objectForKey: [[NSString stringWithUTF8String: name] lowercaseString]];
175 if(n == nil) {
176 warnx("Unknown architecture: %s", name);
177 cpu->errs++;
178 return;
179 }
180 addCPU(cpu, [n intValue]);
181}
182
183/*
184 * useEnv - parse the environment variable for CPU preferences. Use name
185 * to look for program-specific preferences, and append any CPU types to cpu.
186 * Returns the number of CPU types. Returns any specified execute path in
187 * execpath.
188 *
189 * The environment variable ARCHPREFERENCE has the format:
190 * spec[;spec]...
191 * a semicolon separated list of specifiers. Each specifier has the format:
192 * [prog:[execpath:]]type[,type]...
193 * a comma separate list of CPU type names, optionally proceeded by a program
194 * name and an execpath. If program name exist, that types only apply to that
195 * program. If execpath is specified, it is returned. If no program name
196 * exists, then it applies to all programs. So ordering of the specifiers is
197 * important, as the default (no program name) specifier must be last.
198 */
199static int
200useEnv(CPU *cpu, const char *name, char **execpath)
201{
202 char *val = getenv(envname);
203 if(!val)
204 return 0;
205
206 /* cp will point to the basename of name */
207 const char *cp = strrchr(name, '/');
208 if(cp) {
209 cp++;
210 if(!*cp)
211 errx(1, "%s: no name after last slash", name);
212 } else
213 cp = name;
214 /* make a copy of the environment variable value, so we can modify it */
215 val = strdup(val);
216 if(!val)
217 err(1, "Can't copy environment %s", envname);
218 char *str = val;
219 char *blk;
220 /* for each specifier */
221 while((blk = strsep(&str, ";")) != NULL) {
222 if(*blk == 0)
223 continue; /* two adjacent semicolons */
224 /* now split on colons */
225 char *n = strsep(&blk, ":");
226 if(blk) {
227 char *p = strsep(&blk, ":");
228 if(!blk) { /* there is only one colon, so no execpath */
229 blk = p;
230 p = NULL;
231 } else if(!*p) /* two consecutive colons, so no execpath */
232 p = NULL;
233 if(!*blk)
234 continue; /* no cpu list, so skip */
235 /* if the name matches, or there is no name, process the cpus */
236 if(!*n || strcmp(n, cp) == 0) {
237 if(CPUCOUNT(cpu) <= 0) { /* only if we don't already have cpu types */
238 char *t;
239 while((t = strsep(&blk, ",")) != NULL)
240 addCPUbyname(cpu, t);
241 }
242 *execpath = (*n ? p : NULL); /* only use the exec path is name is set */
243 break;
244 }
245 } else { /* no colons at all, so process as default */
246 if(CPUCOUNT(cpu) <= 0) { /* only if we don't already have cpu types */
247 blk = n;
248 while((n = strsep(&blk, ",")) != NULL)
249 addCPUbyname(cpu, n);
250 }
251 *execpath = NULL;
252 break;
253 }
254 }
255 if(cpu->errs) /* errors during addCPUbyname are fatal */
256 exit(1);
257 return CPUCOUNT(cpu); /* return count of cpus */
258}
259
260/*
261 * spawnFromPreference - called when argv[0] is not "arch" or "machine", or
262 * argv[0] was arch, but no commandline architectures were specified.
263 * If the environment variable ARCHPREFERENCE is specified, and there is a
264 * match to argv[0], use the specified cpu preferences. If no exec path
265 * is specified in ARCHPREFERENCE, or no match is found in ARCHPREFERENCE,
266 * get any additional information from a .plist file with the name of argv[0].
267 * This routine never returns.
268 */
269static void __dead2
270spawnFromPreferences(CPU *cpu, int needexecpath, char **argv, char **environ)
271{
272 char *epath = NULL;
273 int count;
274 const char *prog = strrchr(*argv, '/');
275 if(prog)
276 prog++;
277 else
278 prog = *argv;
279 if(!*prog)
280 errx(1, "Not program name specified");
281
282 /* check the environment variable first */
283 if((count = useEnv(cpu, prog, &epath)) > 0) {
284 /* if we were called as arch, use posix_spawnp */
285 if(!needexecpath)
286 spawnIt(count, cpu->buf, 1, (epath ? epath : *argv), argv, environ);
287 /* otherwise, if we have the executable path, call posix_spawn */
288 if(epath)
289 spawnIt(count, cpu->buf, 0, epath, argv, environ);
290 }
291
292 /* pathArray is use to build the .plist file path for each domain */
293 NSMutableArray *pathArray = [NSMutableArray arrayWithCapacity: 3];
294 if(!pathArray)
295 errx(1, "Can't create NSMutableArray");
296 [pathArray addObject: @""]; // placeholder for domain directory
297 [pathArray addObject: SettingsDir];
298 [pathArray addObject: [[NSString stringWithUTF8String: prog] stringByAppendingPathExtension: PlistExtension]];
299
300 /* get the list of top level directories for all domains */
301 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSAllDomainsMask, true);
302 int cnt = [paths count];
303 if(!cnt)
304 errx(1, "No domains");
305
306 /* create the .plist path, and try to read it */
307 int i;
308 NSString *path = NULL;
309 NSData *data = NULL;
310 for(i = 0; i < cnt; i++) {
311 [pathArray replaceObjectAtIndex: 0 withObject: [paths objectAtIndex: i]];
312 path = [NSString pathWithComponents: pathArray];
313 data = [NSData dataWithContentsOfFile: path];
314 if(data) /* found it */
315 break;
316 }
317 if(!data)
318 errx(1, "Can't find any plists for %s", prog);
319
320 /* try to convert the file into a NSDictionary */
321 NSString *error;
322 id plist = [NSPropertyListSerialization propertyListFromData: data mutabilityOption: NSPropertyListImmutable format: nil errorDescription: &error];
323 if(!plist)
324 errx(1, "%s: NSPropertyListSerialization error: %s", [path UTF8String], [error UTF8String]);
325 if(![plist isKindOfClass: [NSDictionary class]])
326 errx(1, "%s: plist not a dictionary", [path UTF8String]);
327
328 NSString *execpath;
329 int errs = 0; /* scan for all errors and fail later */
330 do { /* begin block */
331 /* check the plist version */
332 NSString *vers = [(NSDictionary *)plist objectForKey: KeyPlistVersion];
333 if(!vers) {
334 warnx("%s: No key %s", [path UTF8String], [KeyPlistVersion UTF8String]);
335 errs++;
336 } else if(![vers isKindOfClass: [NSString class]]) {
337 warnx("%s: %s is not a string", [path UTF8String], [KeyPlistVersion UTF8String]);
338 errs++;
339 } else if(![vers isEqualToString: @"1.0"]) {
340 warnx("%s: %s not 1.0", [path UTF8String], [KeyPlistVersion UTF8String]);
341 errs++;
342 }
343 /* get the execpath */
344 execpath = [(NSDictionary *)plist objectForKey: KeyExecPath];
345 if(!execpath) {
346 warnx("%s: No key %s", [path UTF8String], [KeyExecPath UTF8String]);
347 errs++;
348 } else if(![execpath isKindOfClass: [NSString class]]) {
349 warnx("%s: %s is not a string", [path UTF8String], [KeyExecPath UTF8String]);
350 errs++;
351 }
352 /* if we already got cpu preferences from ARCHPREFERENCE, we are done */
353 if(count > 0)
354 break;
355 /* otherwise, parse the cpu preferences from the plist */
356 id p = [(NSDictionary *)plist objectForKey: KeyPrefOrder];
357 if(!p) {
358 warnx("%s: No key %s", [path UTF8String], [KeyPrefOrder UTF8String]);
359 errs++;
360 } else if(![p isKindOfClass: [NSArray class]]) {
361 warnx("%s: %s is not an array", [path UTF8String], [KeyPrefOrder UTF8String]);
362 errs++;
363 } else if((count = [p count]) == 0) {
364 warnx("%s: no entries in %s", [path UTF8String], [KeyPrefOrder UTF8String]);
365 errs++;
366 } else {
367 /* finally but the cpu type array */
368 for(i = 0; i < count; i++) {
369 id a = [(NSArray *)p objectAtIndex: i];
370 NSNumber *n;
371 if(![a isKindOfClass: [NSString class]]) {
372 warnx("%s: entries %d of %s is not a string", [path UTF8String], i, [KeyPrefOrder UTF8String]);
373 errs++;
374 } else if((n = (NSNumber *)[ArchDict objectForKey: [(NSString *)a lowercaseString]]) == nil) {
375 warnx("%s: %s: unknown architecture %s", [path UTF8String], [KeyPrefOrder UTF8String], [(NSString *)a UTF8String]);
376 errs++;
377 } else {
378 addCPU(cpu, [n intValue]);
379 }
380 }
381 }
382 } while(0); /* end block */
383 if(errs) /* exit if there were any reported errors */
384 exit(1);
385
386 /* call posix_spawn */
387 spawnIt(count, cpu->buf, 0, [execpath fileSystemRepresentation], argv, environ);
388}
389
390static void __dead2
391usage(int ret)
392{
393 fprintf(stderr,
394 "Usage: %s\n"
395 " Display the machine's architecture type\n"
396 "Usage: %s [-h] [[-arch_name | -arch arch_name] ...] prog [arg ...]\n"
397 " Run prog with any arguments, using the given architecture\n"
398 " order. If no architectures are specified, use the\n"
399 " ARCHPREFERENCE environment variable, or a property list file.\n"
400 " -h will print usage message and exit.\n",
401 ARCH_PROG, ARCH_PROG);
402 exit(ret);
403}
404
405/*
406 * wrapped - check the path to see if it is a link to /usr/bin/arch.
407 */
408static int
409wrapped(const char *name)
410{
411 int lp, ln;
412 char *p;
413 char *bp = NULL;
414 char *cur, *path;
415 char buf[MAXPATHLEN], rpbuf[MAXPATHLEN];
416 struct stat sb;
417
418 ln = strlen(name);
419
420 do { /* begin block */
421 /* If it's an absolute or relative path name, it's easy. */
422 if(index(name, '/')) {
423 if(stat(name, &sb) == 0 && S_ISREG(sb.st_mode) && access(name, X_OK) == 0) {
424 bp = (char *)name;
425 break;
426 }
427 errx(1, "%s isn't executable", name);
428 }
429
430 /* search the PATH, looking for name */
431 if((path = getenv("PATH")) == NULL)
432 path = _PATH_DEFPATH;
433
434 cur = alloca(strlen(path) + 1);
435 if(cur == NULL)
436 err(1, "alloca");
437 strcpy(cur, path);
438 while((p = strsep(&cur, ":")) != NULL) {
439 /*
440 * It's a SHELL path -- double, leading and trailing colons
441 * mean the current directory.
442 */
443 if(*p == '\0') {
444 p = ".";
445 lp = 1;
446 } else
447 lp = strlen(p);
448
449 /*
450 * If the path is too long complain. This is a possible
451 * security issue; given a way to make the path too long
452 * the user may execute the wrong program.
453 */
454 if(lp + ln + 2 > sizeof(buf)) {
455 warn("%s: path too long", p);
456 continue;
457 }
458 bcopy(p, buf, lp);
459 buf[lp] = '/';
460 bcopy(name, buf + lp + 1, ln);
461 buf[lp + ln + 1] = '\0';
462 if(stat(buf, &sb) == 0 && S_ISREG(sb.st_mode) && access(buf, X_OK) == 0) {
463 bp = buf;
464 break;
465 }
466 }
467 if(p == NULL)
468 errx(1, "Can't find %s in PATH", name);
469 } while(0); /* end block */
470 if(realpath(bp, rpbuf) == NULL)
471 errx(1, "realpath failed on %s", bp);
472 return (strcmp(rpbuf, "/usr/bin/" ARCH_PROG) == 0);
473}
474
475/*
476 * spawnFromArgs - called when arch has arguments specified. The arch command
477 * line arguments are:
478 * % arch [[{-xxx | -arch xxx}]...] prog [arg]...
479 * where xxx is a cpu name, and the command to execute and its arguments follow.
480 * If no commandline cpu names are given, the environment variable
481 * ARCHPREFERENCE is used. This routine never returns.
482 */
483static void __dead2
484spawnFromArgs(CPU *cpu, char **argv, char **environ)
485{
486 char *ap;
487
488 /* process cpu options */
489 for(argv++; *argv && **argv == '-'; argv++) {
490 if(strcmp(*argv, "-arch") == 0) {
491 if(*++argv == NULL) {
492 warnx("-arch without architecture");
493 usage(1);
494 }
495 ap = *argv;
496 } else if(strcmp(*argv, "-h") == 0) {
497 usage(0);
498 } else
499 ap = *argv + 1;
500 addCPUbyname(cpu, ap);
501 }
502 if(cpu->errs)
503 exit(1);
504 if(!*argv || !**argv) {
505 warnx("No command to execute");
506 usage(1);
507 }
508 /* if the program is already a link to arch, then force execpath */
509 int needexecpath = wrapped(*argv);
510
511 /*
512 * If we don't have any architecutures, try ARCHPREFERENCE and plist
513 * files.
514 */
515 int count = CPUCOUNT(cpu);
516 if(count <= 0 || needexecpath)
517 spawnFromPreferences(cpu, needexecpath, argv, environ); /* doesn't return */
518
519 /*
520 * Call posix_spawnp on the program name.
521 */
522 spawnIt(count, cpu->buf, 1, *argv, argv, environ);
523}
524
525/*
526 * init - initializes the ArchDict dictionary from the values in initArches,
527 * and the CPU structure.
528 */
529static void
530init(CPU *cpu)
531{
532 ArchDict = [NSMutableDictionary dictionaryWithCapacity: 4];
533 if(!ArchDict)
534 errx(1, "Can't create NSMutableDictionary");
535 StrInt *sp;
536 for(sp = initArches; sp->str; sp++) {
537 NSString *s = [NSString stringWithUTF8String: sp->str];
538 if(!s)
539 errx(1, "Can't create NSString for %s", sp->str);
540 NSNumber *n = [NSNumber numberWithInt: sp->i];
541 if(!n)
542 errx(1, "Can't create NSNumber for %s", sp->str);
543 [ArchDict setObject: n forKey: s];
544 }
545 initCPU(cpu);
546}
547
548/* the main() routine */
549int
550main(int argc, char **argv, char **environ)
551{
552 const char *prog = getprogname();
553 int my_name_is_arch;
554 CPU cpu;
555
556 if(strcmp(prog, MACHINE_PROG) == 0) {
557 if(argc > 1)
558 errx(-1, "no arguments accepted");
559 arch(0); /* the "machine" command was called */
560 } else if((my_name_is_arch = (strcmp(prog, ARCH_PROG) == 0))) {
561 if(argc == 1)
562 arch(1); /* the "arch" command with no arguments was called */
563 }
564
565 /* set up Objective C autorelease pool */
566 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
567 init(&cpu); /* initialize */
568
569 if(my_name_is_arch)
570 spawnFromArgs(&cpu, argv, environ);
571 else
572 spawnFromPreferences(&cpu, 1, argv, environ);
573 /* should never get here */
574 [pool release];
575 errx(1, "returned from spawn");
576}