]>
Commit | Line | Data |
---|---|---|
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> | |
8459d725 | 27 | #include <stdbool.h> |
34d340d7 A |
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 <Foundation/Foundation.h> | |
ef8ad44b | 40 | #include <TargetConditionals.h> |
34d340d7 A |
41 | |
42 | #ifndef ARCH_PROG | |
43 | #define ARCH_PROG "arch" | |
44 | #endif | |
45 | #define CPUDELTA 10 | |
46 | #ifndef MACHINE_PROG | |
47 | #define MACHINE_PROG "machine" | |
48 | #endif | |
49 | ||
50 | #define CPUCOUNT(c) ((c)->ptr - (c)->buf) | |
51 | ||
52 | static NSMutableDictionary *ArchDict; | |
53 | static NSString *KeyExecPath = @"ExecutablePath"; | |
54 | static NSString *KeyPlistVersion = @"PropertyListVersion"; | |
55 | static NSString *KeyPrefOrder = @"PreferredOrder"; | |
56 | static NSString *PlistExtension = @"plist"; | |
57 | static NSString *SettingsDir = @"archSettings"; | |
58 | ||
59 | static const char envname[] = "ARCHPREFERENCE"; | |
60 | ||
61 | typedef struct { | |
62 | cpu_type_t *buf; | |
63 | cpu_type_t *ptr; | |
64 | cpu_type_t *end; | |
65 | int errs; | |
66 | } CPU; | |
67 | ||
68 | typedef struct { | |
8459d725 A |
69 | const char *arch; |
70 | int cpu; | |
71 | } CPUTypes; | |
34d340d7 | 72 | |
8459d725 A |
73 | static const CPUTypes initArches[] = { |
74 | #if defined(__i386__) || defined(__x86_64__) | |
34d340d7 | 75 | {"i386", CPU_TYPE_I386}, |
34d340d7 | 76 | {"x86_64", CPU_TYPE_X86_64}, |
8459d725 A |
77 | #elif defined(__arm__) |
78 | {"arm", CPU_TYPE_ARM}, | |
79 | #else | |
80 | #error "Unsupported architecture" | |
81 | #endif | |
82 | {NULL, 0} // sentinel | |
34d340d7 A |
83 | }; |
84 | ||
8459d725 A |
85 | /* |
86 | * ignore contains architectures supported by previous releases, but | |
87 | * now unsupported. The seen field will be set to true if the corresponding | |
88 | * architecture was specified. | |
89 | */ | |
90 | ||
91 | typedef struct { | |
92 | const char *arch; | |
93 | bool seen; | |
94 | } Ignore; | |
95 | ||
96 | static Ignore ignore[] = { | |
97 | #if defined(__i386__) || defined(__x86_64__) | |
98 | {"ppc", false}, | |
99 | {"ppc64", false}, | |
100 | #endif | |
101 | {NULL, false} // sentinel | |
102 | }; | |
103 | ||
104 | /* The total number of architectures specified */ | |
105 | static int archCount = 0; | |
106 | ||
107 | /* environment SPI */ | |
108 | char **_copyenv(char **env); | |
109 | int _setenvp(const char *name, const char *value, int rewrite, char ***envp, void *state); | |
110 | int _unsetenvp(const char *name, char ***envp, void *state); | |
111 | ||
112 | /* copy of environment */ | |
113 | char **envCopy = NULL; | |
114 | extern char **environ; | |
115 | ||
116 | /* | |
117 | * The native 32 and 64-bit architectures (this is relative to the architecture | |
118 | * the arch command is running. NULL means unsupported. | |
119 | */ | |
120 | #if defined(__i386__) || defined(__x86_64__) | |
121 | #define NATIVE_32 "i386" | |
122 | #define NATIVE_64 "x86_64" | |
123 | #elif defined(__arm__) | |
124 | #define NATIVE_32 "arm" | |
125 | #define NATIVE_64 NULL | |
126 | #else | |
127 | #error "Unsupported architecture" | |
128 | #endif | |
129 | bool native32seen = false; | |
130 | bool native64seen = false; | |
131 | ||
34d340d7 A |
132 | /* |
133 | * arch - perform the original behavior of the arch and machine commands. | |
134 | * The archcmd flag is non-zero for the arch command, zero for the machine | |
135 | * command. This routine never returns. | |
136 | */ | |
137 | static void __dead2 | |
138 | arch(int archcmd) | |
139 | { | |
140 | const NXArchInfo *arch = NXGetLocalArchInfo(); | |
141 | ||
142 | if(!arch) | |
143 | errx(-1, "Unknown architecture."); | |
144 | if(archcmd) { | |
145 | arch = NXGetArchInfoFromCpuType(arch->cputype, CPU_SUBTYPE_MULTIPLE); | |
146 | if(!arch) | |
147 | errx(-1, "Unknown architecture."); | |
148 | } | |
149 | printf("%s%s", arch->name, (isatty(STDIN_FILENO) ? "\n" : "")); | |
150 | exit(0); | |
151 | } | |
152 | ||
153 | /* | |
8459d725 A |
154 | * spawnIt - run the posix_spawn command. cpu is the auto-sizing CPU structure. |
155 | * pflag is non-zero to call posix_spawnp; zero means to call posix_spawn. | |
156 | * str is the name/path to pass to posix_spawn{,p}, and argv are | |
157 | * the argument arrays to pass. This routine never returns. | |
34d340d7 A |
158 | */ |
159 | static void __dead2 | |
8459d725 | 160 | spawnIt(CPU *cpu, int pflag, const char *str, char **argv) |
34d340d7 | 161 | { |
34d340d7 A |
162 | posix_spawnattr_t attr; |
163 | pid_t pid; | |
164 | int ret; | |
165 | size_t copied; | |
8459d725 A |
166 | const Ignore *ip; |
167 | int count = CPUCOUNT(cpu); | |
168 | cpu_type_t *prefs = cpu->buf; | |
169 | ||
170 | if(count == 0) { | |
171 | if(archCount == 0) // shouldn't happen | |
172 | errx(1, "spawnIt called with no architectures specified"); | |
173 | for(ip = ignore; ip->arch; ip++) { | |
174 | if(ip->seen) | |
175 | warnx("Unsupported architecture: %s", ip->arch); | |
176 | } | |
177 | if(native32seen) | |
178 | warnx("Unsupported native 32-bit architecture"); | |
179 | if(native64seen) | |
180 | warnx("Unsupported native 64-bit architecture"); | |
181 | exit(1); | |
182 | } | |
183 | for(ip = ignore; ip->arch; ip++) { | |
184 | if(ip->seen) | |
185 | fprintf(stderr, "warning: unsupported architecture: %s\n", ip->arch); | |
186 | } | |
187 | if(native32seen) | |
188 | fprintf(stderr, "warning: unsupported native 32-bit architecture\n"); | |
189 | if(native64seen) | |
190 | fprintf(stderr, "warning: unsupported native 64-bit architecture\n"); | |
34d340d7 A |
191 | |
192 | if((ret = posix_spawnattr_init(&attr)) != 0) | |
193 | errc(1, ret, "posix_spawnattr_init"); | |
194 | /* do the equivalent of exec, rather than creating a separate process */ | |
195 | if((ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETEXEC)) != 0) | |
196 | errc(1, ret, "posix_spawnattr_setflags"); | |
197 | if((ret = posix_spawnattr_setbinpref_np(&attr, count, prefs, &copied)) != 0) | |
198 | errc(1, ret, "posix_spawnattr_setbinpref_np"); | |
199 | if(copied != count) | |
200 | errx(1, "posix_spawnattr_setbinpref_np only copied %d of %d", (int)copied, count); | |
201 | if(pflag) | |
8459d725 | 202 | ret = posix_spawnp(&pid, str, NULL, &attr, argv, envCopy ? envCopy : environ); |
34d340d7 | 203 | else |
8459d725 | 204 | ret = posix_spawn(&pid, str, NULL, &attr, argv, envCopy ? envCopy : environ); |
34d340d7 A |
205 | errc(1, ret, "posix_spawn%s: %s", (pflag ? "p" : ""), str); |
206 | } | |
207 | ||
208 | /* | |
209 | * initCPU - initialize a CPU structure, a dynamically expanding CPU types | |
210 | * array. | |
211 | */ | |
212 | static void | |
213 | initCPU(CPU *cpu) | |
214 | { | |
215 | cpu->buf = (cpu_type_t *)malloc(CPUDELTA * sizeof(cpu_type_t)); | |
216 | if(!cpu->buf) | |
217 | err(1, "Failed to malloc CPU buffer"); | |
218 | cpu->ptr = cpu->buf; | |
219 | cpu->end = cpu->buf + CPUDELTA; | |
220 | cpu->errs = 0; | |
221 | } | |
222 | ||
223 | /* | |
224 | * addCPU - add a new CPU type value to the CPU structure, expanding | |
225 | * the array as necessary. | |
226 | */ | |
227 | static void | |
228 | addCPU(CPU *cpu, int n) | |
229 | { | |
8459d725 A |
230 | archCount++; |
231 | if(n <= 0) { // ignore values | |
232 | ignore[-n].seen = true; // negate to get real index | |
233 | return; | |
234 | } | |
34d340d7 A |
235 | if(cpu->ptr >= cpu->end) { |
236 | cpu_type_t *new = realloc(cpu->buf, (cpu->end - cpu->buf + CPUDELTA) * sizeof(cpu_type_t)); | |
237 | if(!new) | |
238 | err(1, "Out of memory realloc-ing CPU structure"); | |
239 | ptrdiff_t diff = (void *)new - (void *)cpu->buf; | |
240 | cpu->buf = new; | |
241 | cpu->ptr = (cpu_type_t *)((void *)cpu->ptr + diff); | |
242 | cpu->end = (cpu_type_t *)((void *)cpu->end + diff) + CPUDELTA; | |
243 | } | |
244 | *cpu->ptr++ = n; | |
245 | } | |
246 | ||
247 | /* | |
248 | * addCPUbyname - add a new CPU type, given by name, to the CPU structure, | |
249 | * expanding the array as necessary. The name is converted to a type value | |
250 | * by the ArchDict dictionary. | |
251 | */ | |
252 | static void | |
253 | addCPUbyname(CPU *cpu, const char *name) | |
254 | { | |
255 | NSNumber *n = (NSNumber *)[ArchDict objectForKey: [[NSString stringWithUTF8String: name] lowercaseString]]; | |
256 | if(n == nil) { | |
257 | warnx("Unknown architecture: %s", name); | |
258 | cpu->errs++; | |
259 | return; | |
260 | } | |
261 | addCPU(cpu, [n intValue]); | |
262 | } | |
263 | ||
264 | /* | |
265 | * useEnv - parse the environment variable for CPU preferences. Use name | |
266 | * to look for program-specific preferences, and append any CPU types to cpu. | |
267 | * Returns the number of CPU types. Returns any specified execute path in | |
268 | * execpath. | |
269 | * | |
270 | * The environment variable ARCHPREFERENCE has the format: | |
271 | * spec[;spec]... | |
272 | * a semicolon separated list of specifiers. Each specifier has the format: | |
273 | * [prog:[execpath:]]type[,type]... | |
274 | * a comma separate list of CPU type names, optionally proceeded by a program | |
275 | * name and an execpath. If program name exist, that types only apply to that | |
276 | * program. If execpath is specified, it is returned. If no program name | |
277 | * exists, then it applies to all programs. So ordering of the specifiers is | |
278 | * important, as the default (no program name) specifier must be last. | |
279 | */ | |
280 | static int | |
281 | useEnv(CPU *cpu, const char *name, char **execpath) | |
282 | { | |
283 | char *val = getenv(envname); | |
284 | if(!val) | |
285 | return 0; | |
286 | ||
287 | /* cp will point to the basename of name */ | |
288 | const char *cp = strrchr(name, '/'); | |
289 | if(cp) { | |
290 | cp++; | |
291 | if(!*cp) | |
292 | errx(1, "%s: no name after last slash", name); | |
293 | } else | |
294 | cp = name; | |
295 | /* make a copy of the environment variable value, so we can modify it */ | |
296 | val = strdup(val); | |
297 | if(!val) | |
298 | err(1, "Can't copy environment %s", envname); | |
299 | char *str = val; | |
300 | char *blk; | |
301 | /* for each specifier */ | |
302 | while((blk = strsep(&str, ";")) != NULL) { | |
303 | if(*blk == 0) | |
304 | continue; /* two adjacent semicolons */ | |
305 | /* now split on colons */ | |
306 | char *n = strsep(&blk, ":"); | |
307 | if(blk) { | |
308 | char *p = strsep(&blk, ":"); | |
309 | if(!blk) { /* there is only one colon, so no execpath */ | |
310 | blk = p; | |
311 | p = NULL; | |
312 | } else if(!*p) /* two consecutive colons, so no execpath */ | |
313 | p = NULL; | |
314 | if(!*blk) | |
315 | continue; /* no cpu list, so skip */ | |
316 | /* if the name matches, or there is no name, process the cpus */ | |
317 | if(!*n || strcmp(n, cp) == 0) { | |
8459d725 | 318 | if(archCount <= 0) { /* only if we haven't processed architectures */ |
34d340d7 A |
319 | char *t; |
320 | while((t = strsep(&blk, ",")) != NULL) | |
321 | addCPUbyname(cpu, t); | |
322 | } | |
323 | *execpath = (*n ? p : NULL); /* only use the exec path is name is set */ | |
324 | break; | |
325 | } | |
326 | } else { /* no colons at all, so process as default */ | |
8459d725 | 327 | if(archCount <= 0) { /* only if we haven't processed architectures */ |
34d340d7 A |
328 | blk = n; |
329 | while((n = strsep(&blk, ",")) != NULL) | |
330 | addCPUbyname(cpu, n); | |
331 | } | |
332 | *execpath = NULL; | |
333 | break; | |
334 | } | |
335 | } | |
336 | if(cpu->errs) /* errors during addCPUbyname are fatal */ | |
337 | exit(1); | |
8459d725 | 338 | return archCount; /* return count of architectures */ |
34d340d7 A |
339 | } |
340 | ||
341 | /* | |
342 | * spawnFromPreference - called when argv[0] is not "arch" or "machine", or | |
343 | * argv[0] was arch, but no commandline architectures were specified. | |
344 | * If the environment variable ARCHPREFERENCE is specified, and there is a | |
345 | * match to argv[0], use the specified cpu preferences. If no exec path | |
346 | * is specified in ARCHPREFERENCE, or no match is found in ARCHPREFERENCE, | |
347 | * get any additional information from a .plist file with the name of argv[0]. | |
348 | * This routine never returns. | |
349 | */ | |
350 | static void __dead2 | |
8459d725 | 351 | spawnFromPreferences(CPU *cpu, int needexecpath, char **argv) |
34d340d7 A |
352 | { |
353 | char *epath = NULL; | |
354 | int count; | |
355 | const char *prog = strrchr(*argv, '/'); | |
356 | if(prog) | |
357 | prog++; | |
358 | else | |
359 | prog = *argv; | |
360 | if(!*prog) | |
361 | errx(1, "Not program name specified"); | |
362 | ||
363 | /* check the environment variable first */ | |
364 | if((count = useEnv(cpu, prog, &epath)) > 0) { | |
365 | /* if we were called as arch, use posix_spawnp */ | |
366 | if(!needexecpath) | |
8459d725 | 367 | spawnIt(cpu, 1, (epath ? epath : *argv), argv); |
34d340d7 A |
368 | /* otherwise, if we have the executable path, call posix_spawn */ |
369 | if(epath) | |
8459d725 | 370 | spawnIt(cpu, 0, epath, argv); |
34d340d7 A |
371 | } |
372 | ||
373 | /* pathArray is use to build the .plist file path for each domain */ | |
374 | NSMutableArray *pathArray = [NSMutableArray arrayWithCapacity: 3]; | |
375 | if(!pathArray) | |
376 | errx(1, "Can't create NSMutableArray"); | |
377 | [pathArray addObject: @""]; // placeholder for domain directory | |
378 | [pathArray addObject: SettingsDir]; | |
379 | [pathArray addObject: [[NSString stringWithUTF8String: prog] stringByAppendingPathExtension: PlistExtension]]; | |
380 | ||
381 | /* get the list of top level directories for all domains */ | |
382 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSAllDomainsMask, true); | |
383 | int cnt = [paths count]; | |
384 | if(!cnt) | |
385 | errx(1, "No domains"); | |
386 | ||
387 | /* create the .plist path, and try to read it */ | |
388 | int i; | |
389 | NSString *path = NULL; | |
390 | NSData *data = NULL; | |
391 | for(i = 0; i < cnt; i++) { | |
392 | [pathArray replaceObjectAtIndex: 0 withObject: [paths objectAtIndex: i]]; | |
393 | path = [NSString pathWithComponents: pathArray]; | |
394 | data = [NSData dataWithContentsOfFile: path]; | |
395 | if(data) /* found it */ | |
396 | break; | |
397 | } | |
398 | if(!data) | |
399 | errx(1, "Can't find any plists for %s", prog); | |
400 | ||
401 | /* try to convert the file into a NSDictionary */ | |
402 | NSString *error; | |
403 | id plist = [NSPropertyListSerialization propertyListFromData: data mutabilityOption: NSPropertyListImmutable format: nil errorDescription: &error]; | |
404 | if(!plist) | |
405 | errx(1, "%s: NSPropertyListSerialization error: %s", [path UTF8String], [error UTF8String]); | |
406 | if(![plist isKindOfClass: [NSDictionary class]]) | |
407 | errx(1, "%s: plist not a dictionary", [path UTF8String]); | |
408 | ||
409 | NSString *execpath; | |
410 | int errs = 0; /* scan for all errors and fail later */ | |
411 | do { /* begin block */ | |
412 | /* check the plist version */ | |
413 | NSString *vers = [(NSDictionary *)plist objectForKey: KeyPlistVersion]; | |
414 | if(!vers) { | |
415 | warnx("%s: No key %s", [path UTF8String], [KeyPlistVersion UTF8String]); | |
416 | errs++; | |
417 | } else if(![vers isKindOfClass: [NSString class]]) { | |
418 | warnx("%s: %s is not a string", [path UTF8String], [KeyPlistVersion UTF8String]); | |
419 | errs++; | |
420 | } else if(![vers isEqualToString: @"1.0"]) { | |
421 | warnx("%s: %s not 1.0", [path UTF8String], [KeyPlistVersion UTF8String]); | |
422 | errs++; | |
423 | } | |
424 | /* get the execpath */ | |
425 | execpath = [(NSDictionary *)plist objectForKey: KeyExecPath]; | |
426 | if(!execpath) { | |
427 | warnx("%s: No key %s", [path UTF8String], [KeyExecPath UTF8String]); | |
428 | errs++; | |
429 | } else if(![execpath isKindOfClass: [NSString class]]) { | |
430 | warnx("%s: %s is not a string", [path UTF8String], [KeyExecPath UTF8String]); | |
431 | errs++; | |
432 | } | |
433 | /* if we already got cpu preferences from ARCHPREFERENCE, we are done */ | |
434 | if(count > 0) | |
435 | break; | |
436 | /* otherwise, parse the cpu preferences from the plist */ | |
437 | id p = [(NSDictionary *)plist objectForKey: KeyPrefOrder]; | |
438 | if(!p) { | |
439 | warnx("%s: No key %s", [path UTF8String], [KeyPrefOrder UTF8String]); | |
440 | errs++; | |
441 | } else if(![p isKindOfClass: [NSArray class]]) { | |
442 | warnx("%s: %s is not an array", [path UTF8String], [KeyPrefOrder UTF8String]); | |
443 | errs++; | |
444 | } else if((count = [p count]) == 0) { | |
445 | warnx("%s: no entries in %s", [path UTF8String], [KeyPrefOrder UTF8String]); | |
446 | errs++; | |
447 | } else { | |
8459d725 | 448 | /* finally build the cpu type array */ |
34d340d7 A |
449 | for(i = 0; i < count; i++) { |
450 | id a = [(NSArray *)p objectAtIndex: i]; | |
451 | NSNumber *n; | |
452 | if(![a isKindOfClass: [NSString class]]) { | |
453 | warnx("%s: entries %d of %s is not a string", [path UTF8String], i, [KeyPrefOrder UTF8String]); | |
454 | errs++; | |
455 | } else if((n = (NSNumber *)[ArchDict objectForKey: [(NSString *)a lowercaseString]]) == nil) { | |
456 | warnx("%s: %s: unknown architecture %s", [path UTF8String], [KeyPrefOrder UTF8String], [(NSString *)a UTF8String]); | |
457 | errs++; | |
458 | } else { | |
459 | addCPU(cpu, [n intValue]); | |
460 | } | |
461 | } | |
462 | } | |
463 | } while(0); /* end block */ | |
464 | if(errs) /* exit if there were any reported errors */ | |
465 | exit(1); | |
466 | ||
467 | /* call posix_spawn */ | |
8459d725 | 468 | spawnIt(cpu, 0, [execpath fileSystemRepresentation], argv); |
34d340d7 A |
469 | } |
470 | ||
471 | static void __dead2 | |
472 | usage(int ret) | |
473 | { | |
474 | fprintf(stderr, | |
475 | "Usage: %s\n" | |
476 | " Display the machine's architecture type\n" | |
8459d725 | 477 | "Usage: %s {-arch_name | -arch arch_name} ... [-c] [-d envname] ... [-e envname=value] ... [-h] prog [arg ...]\n" |
34d340d7 A |
478 | " Run prog with any arguments, using the given architecture\n" |
479 | " order. If no architectures are specified, use the\n" | |
480 | " ARCHPREFERENCE environment variable, or a property list file.\n" | |
8459d725 A |
481 | " -c will clear out all environment variables before running prog.\n" |
482 | " -d will delete the given environment variable before running prog.\n" | |
483 | " -e will add the given environment variable/value before running prog.\n" | |
34d340d7 A |
484 | " -h will print usage message and exit.\n", |
485 | ARCH_PROG, ARCH_PROG); | |
486 | exit(ret); | |
487 | } | |
488 | ||
489 | /* | |
490 | * wrapped - check the path to see if it is a link to /usr/bin/arch. | |
491 | */ | |
492 | static int | |
493 | wrapped(const char *name) | |
494 | { | |
495 | int lp, ln; | |
496 | char *p; | |
497 | char *bp = NULL; | |
498 | char *cur, *path; | |
499 | char buf[MAXPATHLEN], rpbuf[MAXPATHLEN]; | |
500 | struct stat sb; | |
501 | ||
502 | ln = strlen(name); | |
503 | ||
504 | do { /* begin block */ | |
505 | /* If it's an absolute or relative path name, it's easy. */ | |
506 | if(index(name, '/')) { | |
507 | if(stat(name, &sb) == 0 && S_ISREG(sb.st_mode) && access(name, X_OK) == 0) { | |
508 | bp = (char *)name; | |
509 | break; | |
510 | } | |
511 | errx(1, "%s isn't executable", name); | |
512 | } | |
513 | ||
514 | /* search the PATH, looking for name */ | |
515 | if((path = getenv("PATH")) == NULL) | |
516 | path = _PATH_DEFPATH; | |
517 | ||
518 | cur = alloca(strlen(path) + 1); | |
519 | if(cur == NULL) | |
520 | err(1, "alloca"); | |
521 | strcpy(cur, path); | |
522 | while((p = strsep(&cur, ":")) != NULL) { | |
523 | /* | |
524 | * It's a SHELL path -- double, leading and trailing colons | |
525 | * mean the current directory. | |
526 | */ | |
527 | if(*p == '\0') { | |
528 | p = "."; | |
529 | lp = 1; | |
530 | } else | |
531 | lp = strlen(p); | |
532 | ||
533 | /* | |
534 | * If the path is too long complain. This is a possible | |
535 | * security issue; given a way to make the path too long | |
536 | * the user may execute the wrong program. | |
537 | */ | |
538 | if(lp + ln + 2 > sizeof(buf)) { | |
539 | warn("%s: path too long", p); | |
540 | continue; | |
541 | } | |
542 | bcopy(p, buf, lp); | |
543 | buf[lp] = '/'; | |
544 | bcopy(name, buf + lp + 1, ln); | |
545 | buf[lp + ln + 1] = '\0'; | |
546 | if(stat(buf, &sb) == 0 && S_ISREG(sb.st_mode) && access(buf, X_OK) == 0) { | |
547 | bp = buf; | |
548 | break; | |
549 | } | |
550 | } | |
551 | if(p == NULL) | |
552 | errx(1, "Can't find %s in PATH", name); | |
553 | } while(0); /* end block */ | |
554 | if(realpath(bp, rpbuf) == NULL) | |
555 | errx(1, "realpath failed on %s", bp); | |
556 | return (strcmp(rpbuf, "/usr/bin/" ARCH_PROG) == 0); | |
557 | } | |
558 | ||
559 | /* | |
560 | * spawnFromArgs - called when arch has arguments specified. The arch command | |
561 | * line arguments are: | |
562 | * % arch [[{-xxx | -arch xxx}]...] prog [arg]... | |
563 | * where xxx is a cpu name, and the command to execute and its arguments follow. | |
564 | * If no commandline cpu names are given, the environment variable | |
565 | * ARCHPREFERENCE is used. This routine never returns. | |
566 | */ | |
8459d725 A |
567 | |
568 | #define MATCHARG(a,m) ({ \ | |
569 | const char *arg = *(a); \ | |
570 | if(arg[1] == '-') arg++; \ | |
571 | strcmp(arg, (m)) == 0; \ | |
572 | }) | |
573 | ||
574 | #define MATCHARGWITHVALUE(a,m,n,e) ({ \ | |
575 | const char *ret = NULL; \ | |
576 | const char *arg = *(a); \ | |
577 | if(arg[1] == '-') arg++; \ | |
578 | if(strcmp(arg, (m)) == 0) { \ | |
579 | if(*++(a) == NULL) { \ | |
580 | warnx(e); \ | |
581 | usage(1); \ | |
582 | } \ | |
583 | ret = *(a); \ | |
584 | } else if(strncmp(arg, (m), (n)) == 0 && arg[n] == '=') { \ | |
585 | ret = arg + (n) + 1; \ | |
586 | } \ | |
587 | ret; \ | |
588 | }) | |
589 | ||
590 | #define MAKEENVCOPY(e) \ | |
591 | if(!envCopy) { \ | |
592 | envCopy = _copyenv(environ); \ | |
593 | if(envCopy == NULL) \ | |
594 | errx(1, (e)); \ | |
595 | } | |
596 | ||
34d340d7 | 597 | static void __dead2 |
8459d725 | 598 | spawnFromArgs(CPU *cpu, char **argv) |
34d340d7 | 599 | { |
8459d725 | 600 | const char *ap, *ret; |
34d340d7 | 601 | |
8459d725 | 602 | /* process arguments */ |
34d340d7 | 603 | for(argv++; *argv && **argv == '-'; argv++) { |
8459d725 A |
604 | if((ret = MATCHARGWITHVALUE(argv, "-arch", 5, "-arch without architecture"))) { |
605 | ap = ret; | |
606 | } else if(MATCHARG(argv, "-32")) { | |
607 | ap = NATIVE_32; | |
608 | if(!ap) { | |
609 | native32seen = true; | |
610 | archCount++; | |
611 | continue; | |
612 | } | |
613 | } else if(MATCHARG(argv, "-64")) { | |
614 | ap = NATIVE_64; | |
615 | if(!ap) { | |
616 | native64seen = true; | |
617 | archCount++; | |
618 | continue; | |
619 | } | |
620 | } else if(MATCHARG(argv, "-c")) { | |
621 | free(envCopy); | |
622 | envCopy = _copyenv(NULL); // create empty environment | |
623 | if(!envCopy) | |
624 | errx(1, "Out of memory processing -c"); | |
625 | continue; | |
626 | } else if((ret = MATCHARGWITHVALUE(argv, "-d", 2, "-d without envname"))) { | |
627 | MAKEENVCOPY("Out of memory processing -d"); | |
628 | _unsetenvp(ret, &envCopy, NULL); | |
629 | continue; | |
630 | } else if((ret = MATCHARGWITHVALUE(argv, "-e", 2, "-e without envname=value"))) { | |
631 | MAKEENVCOPY("Out of memory processing -e"); | |
632 | const char *cp = strchr(ret, '='); | |
633 | if(!cp) { | |
634 | warnx("-e %s: no equal sign", ret); | |
34d340d7 A |
635 | usage(1); |
636 | } | |
8459d725 A |
637 | cp++; // skip to value |
638 | /* | |
639 | * _setenvp() only uses the name before any equal sign found in | |
640 | * the first argument. | |
641 | */ | |
642 | _setenvp(ret, cp, 1, &envCopy, NULL); | |
643 | continue; | |
644 | } else if(MATCHARG(argv, "-h")) { | |
34d340d7 | 645 | usage(0); |
8459d725 | 646 | } else { |
34d340d7 | 647 | ap = *argv + 1; |
8459d725 A |
648 | if(*ap == '-') ap++; |
649 | } | |
34d340d7 A |
650 | addCPUbyname(cpu, ap); |
651 | } | |
652 | if(cpu->errs) | |
653 | exit(1); | |
654 | if(!*argv || !**argv) { | |
655 | warnx("No command to execute"); | |
656 | usage(1); | |
657 | } | |
658 | /* if the program is already a link to arch, then force execpath */ | |
659 | int needexecpath = wrapped(*argv); | |
660 | ||
661 | /* | |
662 | * If we don't have any architecutures, try ARCHPREFERENCE and plist | |
663 | * files. | |
664 | */ | |
8459d725 A |
665 | if(archCount <= 0 || needexecpath) |
666 | spawnFromPreferences(cpu, needexecpath, argv); /* doesn't return */ | |
34d340d7 A |
667 | |
668 | /* | |
669 | * Call posix_spawnp on the program name. | |
670 | */ | |
8459d725 | 671 | spawnIt(cpu, 1, *argv, argv); |
34d340d7 A |
672 | } |
673 | ||
674 | /* | |
675 | * init - initializes the ArchDict dictionary from the values in initArches, | |
676 | * and the CPU structure. | |
677 | */ | |
678 | static void | |
679 | init(CPU *cpu) | |
680 | { | |
681 | ArchDict = [NSMutableDictionary dictionaryWithCapacity: 4]; | |
682 | if(!ArchDict) | |
683 | errx(1, "Can't create NSMutableDictionary"); | |
8459d725 A |
684 | const CPUTypes *cp; |
685 | for(cp = initArches; cp->arch; cp++) { | |
686 | NSString *s = [NSString stringWithUTF8String: cp->arch]; | |
687 | if(!s) | |
688 | errx(1, "Can't create NSString for %s", cp->arch); | |
689 | NSNumber *n = [NSNumber numberWithInt: cp->cpu]; | |
690 | if(!n) | |
691 | errx(1, "Can't create NSNumber for %s", cp->arch); | |
692 | [ArchDict setObject: n forKey: s]; | |
693 | } | |
694 | /* | |
695 | * The valid architecture numbers above are one or greater. The ignore | |
696 | * values are the negative of the index in the ignore array. | |
697 | */ | |
698 | const Ignore *ip; | |
699 | int i; | |
700 | for(ip = ignore, i = 0; ip->arch; ip++, i--) { // i is negative of index | |
701 | NSString *s = [NSString stringWithUTF8String: ip->arch]; | |
34d340d7 | 702 | if(!s) |
8459d725 A |
703 | errx(1, "Can't create NSString for %s", ip->arch); |
704 | NSNumber *n = [NSNumber numberWithInt: i]; | |
34d340d7 | 705 | if(!n) |
8459d725 | 706 | errx(1, "Can't create NSNumber for %s", ip->arch); |
34d340d7 A |
707 | [ArchDict setObject: n forKey: s]; |
708 | } | |
709 | initCPU(cpu); | |
710 | } | |
711 | ||
712 | /* the main() routine */ | |
713 | int | |
8459d725 | 714 | main(int argc, char **argv) |
34d340d7 A |
715 | { |
716 | const char *prog = getprogname(); | |
717 | int my_name_is_arch; | |
718 | CPU cpu; | |
719 | ||
720 | if(strcmp(prog, MACHINE_PROG) == 0) { | |
721 | if(argc > 1) | |
722 | errx(-1, "no arguments accepted"); | |
723 | arch(0); /* the "machine" command was called */ | |
724 | } else if((my_name_is_arch = (strcmp(prog, ARCH_PROG) == 0))) { | |
725 | if(argc == 1) | |
726 | arch(1); /* the "arch" command with no arguments was called */ | |
727 | } | |
728 | ||
729 | /* set up Objective C autorelease pool */ | |
730 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; | |
731 | init(&cpu); /* initialize */ | |
732 | ||
733 | if(my_name_is_arch) | |
8459d725 | 734 | spawnFromArgs(&cpu, argv); |
34d340d7 | 735 | else |
8459d725 | 736 | spawnFromPreferences(&cpu, 1, argv); |
34d340d7 A |
737 | /* should never get here */ |
738 | [pool release]; | |
739 | errx(1, "returned from spawn"); | |
740 | } |