]>
Commit | Line | Data |
---|---|---|
aaff5f01 A |
1 | /* |
2 | * Copyright (c) 1999, 2006, 2011 Apple 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 <stdbool.h> | |
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 <limits.h> | |
40 | #include <sys/fcntl.h> | |
41 | #include <glob.h> | |
42 | #include <CoreFoundation/CoreFoundation.h> | |
43 | #include <NSSystemDirectories.h> | |
44 | ||
45 | #ifndef ARCH_PROG | |
46 | #define ARCH_PROG "arch" | |
47 | #endif | |
48 | #ifndef MACHINE_PROG | |
49 | #define MACHINE_PROG "machine" | |
50 | #endif | |
51 | ||
52 | #define kKeyExecPath "ExecutablePath" | |
53 | #define kKeyPlistVersion "PropertyListVersion" | |
54 | #define kKeyPrefOrder "PreferredOrder" | |
55 | #define kPlistExtension ".plist" | |
56 | #define kSettingsDir "archSettings" | |
57 | ||
58 | static const char envname[] = "ARCHPREFERENCE"; | |
59 | ||
60 | /* The CPU struct contains the argument buffer to posix_spawnattr_setbinpref_np */ | |
61 | ||
62 | typedef struct { | |
63 | cpu_type_t *buf; | |
64 | int errs; | |
65 | size_t count; | |
66 | size_t capacity; | |
67 | } CPU; | |
68 | ||
69 | typedef struct { | |
70 | const char *arch; | |
71 | cpu_type_t cpu; | |
72 | } CPUTypes; | |
73 | ||
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}, | |
aaff5f01 A |
80 | #else |
81 | #error "Unsupported architecture" | |
82 | #endif | |
83 | }; | |
84 | ||
85 | /* environment SPI */ | |
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); | |
89 | ||
90 | /* copy of environment */ | |
91 | char **envCopy = NULL; | |
92 | extern char **environ; | |
93 | ||
94 | /* | |
95 | * The native 32 and 64-bit architectures (this is relative to the architecture | |
96 | * the arch command is running. NULL means unsupported. | |
97 | */ | |
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 | |
aaff5f01 A |
104 | #else |
105 | #error "Unsupported architecture" | |
106 | #endif | |
107 | bool unrecognizednative32seen = false; | |
108 | bool unrecognizednative64seen = false; | |
109 | ||
110 | /* | |
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. | |
114 | */ | |
115 | static void __dead2 | |
116 | arch(int archcmd) | |
117 | { | |
118 | const NXArchInfo *arch = NXGetLocalArchInfo(); | |
119 | ||
120 | if(!arch) | |
121 | errx(-1, "Unknown architecture."); | |
122 | if(archcmd) { | |
123 | arch = NXGetArchInfoFromCpuType(arch->cputype, CPU_SUBTYPE_MULTIPLE); | |
124 | if(!arch) | |
125 | errx(-1, "Unknown architecture."); | |
126 | } | |
127 | printf("%s%s", arch->name, (isatty(STDIN_FILENO) ? "\n" : "")); | |
128 | exit(0); | |
129 | } | |
130 | ||
131 | /* | |
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. | |
136 | */ | |
137 | static void __dead2 | |
138 | spawnIt(CPU *cpu, int pflag, const char *str, char **argv) | |
139 | { | |
140 | posix_spawnattr_t attr; | |
141 | pid_t pid; | |
142 | int ret; | |
143 | size_t copied; | |
144 | size_t count = cpu->count; | |
145 | cpu_type_t *prefs = cpu->buf; | |
146 | ||
147 | if(count == 0) { | |
148 | if(unrecognizednative32seen) | |
149 | warnx("Unsupported native 32-bit architecture"); | |
150 | if(unrecognizednative64seen) | |
151 | warnx("Unsupported native 64-bit architecture"); | |
152 | exit(1); | |
153 | } | |
154 | ||
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"); | |
159 | ||
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"); | |
167 | if(copied != count) | |
168 | errx(1, "posix_spawnattr_setbinpref_np only copied %lu of %lu", copied, count); | |
169 | if(pflag) | |
170 | ret = posix_spawnp(&pid, str, NULL, &attr, argv, envCopy ? envCopy : environ); | |
171 | else | |
172 | ret = posix_spawn(&pid, str, NULL, &attr, argv, envCopy ? envCopy : environ); | |
173 | errc(1, ret, "posix_spawn%s: %s", (pflag ? "p" : ""), str); | |
174 | } | |
175 | ||
176 | /* | |
177 | * initCPU - initialize a CPU structure, a dynamically expanding CPU types | |
178 | * array. | |
179 | */ | |
180 | static void | |
181 | initCPU(CPU *cpu) | |
182 | { | |
183 | cpu->errs = 0; | |
184 | cpu->count = 0; | |
185 | cpu->capacity = 1; | |
186 | cpu->buf = (cpu_type_t *)malloc(cpu->capacity * sizeof(cpu_type_t)); | |
187 | if(!cpu->buf) | |
188 | err(1, "Failed to malloc CPU buffer"); | |
189 | } | |
190 | ||
191 | /* | |
192 | * addCPU - add a new CPU type value to the CPU structure, expanding | |
193 | * the array as necessary. | |
194 | */ | |
195 | static void | |
196 | addCPU(CPU *cpu, cpu_type_t n) | |
197 | { | |
198 | if(cpu->count == cpu->capacity) { | |
199 | cpu_type_t *newcpubuf; | |
200 | ||
201 | cpu->capacity *= 2; | |
202 | newcpubuf = (cpu_type_t *)realloc(cpu->buf, cpu->capacity * sizeof(cpu_type_t)); | |
203 | if(!newcpubuf) | |
204 | err(1, "Out of memory realloc-ing CPU structure"); | |
205 | cpu->buf = newcpubuf; | |
206 | } | |
207 | cpu->buf[cpu->count++] = n; | |
208 | } | |
209 | ||
210 | /* | |
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. | |
214 | */ | |
215 | static void | |
216 | addCPUbyname(CPU *cpu, const char *name) | |
217 | { | |
218 | int i; | |
219 | ||
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); | |
223 | return; | |
224 | } | |
225 | } | |
226 | ||
227 | /* Didn't match a string in knownArchs */ | |
228 | warnx("Unknown architecture: %s", name); | |
229 | cpu->errs++; | |
230 | } | |
231 | ||
232 | /* | |
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 | |
236 | * execpath. | |
237 | * | |
238 | * The environment variable ARCHPREFERENCE has the format: | |
239 | * spec[;spec]... | |
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. | |
247 | */ | |
248 | static size_t | |
249 | useEnv(CPU *cpu, const char *name, char **execpath) | |
250 | { | |
251 | char *val = getenv(envname); | |
252 | if(!val) | |
253 | return 0; | |
254 | ||
255 | /* cp will point to the basename of name */ | |
256 | const char *cp = strrchr(name, '/'); | |
257 | if(cp) { | |
258 | cp++; | |
259 | if(!*cp) | |
260 | errx(1, "%s: no name after last slash", name); | |
261 | } else | |
262 | cp = name; | |
263 | /* make a copy of the environment variable value, so we can modify it */ | |
264 | val = strdup(val); | |
265 | if(!val) | |
266 | err(1, "Can't copy environment %s", envname); | |
267 | char *str = val; | |
268 | char *blk; | |
269 | /* for each specifier */ | |
270 | while((blk = strsep(&str, ";")) != NULL) { | |
271 | if(*blk == 0) | |
272 | continue; /* two adjacent semicolons */ | |
273 | /* now split on colons */ | |
274 | char *n = strsep(&blk, ":"); | |
275 | if(blk) { | |
276 | char *p = strsep(&blk, ":"); | |
277 | if(!blk) { /* there is only one colon, so no execpath */ | |
278 | blk = p; | |
279 | p = NULL; | |
280 | } else if(!*p) /* two consecutive colons, so no execpath */ | |
281 | p = NULL; | |
282 | if(!*blk) | |
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 */ | |
287 | char *t; | |
288 | while((t = strsep(&blk, ",")) != NULL) | |
289 | addCPUbyname(cpu, t); | |
290 | } | |
291 | *execpath = (*n ? p : NULL); /* only use the exec path is name is set */ | |
292 | break; | |
293 | } | |
294 | } else { /* no colons at all, so process as default */ | |
295 | if(cpu->count == 0) { /* only if we haven't processed architectures */ | |
296 | blk = n; | |
297 | while((n = strsep(&blk, ",")) != NULL) | |
298 | addCPUbyname(cpu, n); | |
299 | } | |
300 | *execpath = NULL; | |
301 | break; | |
302 | } | |
303 | } | |
304 | if(cpu->errs) /* errors during addCPUbyname are fatal */ | |
305 | exit(1); | |
306 | return cpu->count; /* return count of architectures */ | |
307 | } | |
308 | ||
309 | /* | |
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. | |
317 | */ | |
318 | static void __dead2 | |
319 | spawnFromPreferences(CPU *cpu, int needexecpath, char **argv) | |
320 | { | |
321 | char *epath = NULL; | |
322 | char fpath[PATH_MAX]; | |
323 | char execpath2[PATH_MAX]; | |
324 | CFDictionaryRef plist = NULL; | |
325 | NSSearchPathEnumerationState state; | |
326 | size_t count, i; | |
327 | const char *prog = strrchr(*argv, '/'); | |
328 | ||
329 | if(prog) | |
330 | prog++; | |
331 | else | |
332 | prog = *argv; | |
333 | if(!*prog) | |
334 | errx(1, "Not program name specified"); | |
335 | ||
336 | /* check the environment variable first */ | |
337 | if((count = useEnv(cpu, prog, &epath)) > 0) { | |
338 | /* if we were called as arch, use posix_spawnp */ | |
339 | if(!needexecpath) | |
340 | spawnIt(cpu, 1, (epath ? epath : *argv), argv); | |
341 | /* otherwise, if we have the executable path, call posix_spawn */ | |
342 | if(epath) | |
343 | spawnIt(cpu, 0, epath, argv); | |
344 | } | |
345 | ||
346 | state = NSStartSearchPathEnumeration(NSLibraryDirectory, NSAllDomainsMask); | |
347 | while ((state = NSGetNextSearchPathEnumeration(state, fpath))) { | |
348 | ||
349 | CFURLRef url; | |
350 | CFReadStreamRef stream; | |
351 | ||
352 | if (fpath[0] == '~') { | |
353 | glob_t pglob; | |
354 | int gret; | |
355 | ||
356 | bzero(&pglob, sizeof(pglob)); | |
357 | ||
358 | gret = glob(fpath, GLOB_TILDE, NULL, &pglob); | |
359 | if (gret == 0) { | |
360 | int i; | |
361 | for (i=0; i < pglob.gl_pathc; i++) { | |
362 | /* take the first glob expansion */ | |
363 | strlcpy(fpath, pglob.gl_pathv[i], sizeof(fpath)); | |
364 | break; | |
365 | } | |
366 | } | |
367 | globfree(&pglob); | |
368 | } | |
369 | ||
370 | // Handle path | |
371 | strlcat(fpath, "/" kSettingsDir "/", sizeof(fpath)); | |
372 | strlcat(fpath, prog, sizeof(fpath)); | |
373 | strlcat(fpath, kPlistExtension, sizeof(fpath)); | |
374 | // printf("component: %s\n", fpath); | |
375 | ||
376 | int fd, ret; | |
377 | size_t length; | |
378 | ssize_t rsize; | |
379 | struct stat sb; | |
380 | void *buffer; | |
381 | fd = open(fpath, O_RDONLY, 0); | |
382 | if (fd >= 0) { | |
383 | ret = fstat(fd, &sb); | |
384 | if (ret == 0) { | |
385 | if (sb.st_size <= SIZE_T_MAX) { | |
386 | length = (size_t)sb.st_size; | |
387 | buffer = malloc(length); /* ownership transferred to CFData */ | |
388 | if (buffer) { | |
389 | rsize = read(fd, buffer, length); | |
390 | if (rsize == length) { | |
391 | CFDataRef data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, buffer, length, kCFAllocatorMalloc); | |
392 | if (data) { | |
393 | buffer = NULL; | |
394 | plist = CFPropertyListCreateWithData(kCFAllocatorDefault, data, kCFPropertyListImmutable, NULL, NULL); | |
395 | CFRelease(data); | |
396 | } | |
397 | } | |
398 | if (buffer) { | |
399 | free(buffer); | |
400 | } | |
401 | } | |
402 | } | |
403 | } | |
404 | close(fd); | |
405 | } | |
406 | ||
407 | if (plist) { | |
408 | break; | |
409 | } | |
410 | } | |
411 | ||
412 | if (plist) { | |
413 | if (CFGetTypeID(plist) != CFDictionaryGetTypeID()) | |
414 | errx(1, "%s: plist not a dictionary", fpath); | |
415 | } else { | |
416 | errx(1, "Can't find any plists for %s", prog); | |
417 | } | |
418 | ||
419 | ||
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)); | |
424 | if(!vers) { | |
425 | warnx("%s: No key %s", fpath, kKeyPlistVersion); | |
426 | errs++; | |
427 | } else if(CFGetTypeID(vers) != CFStringGetTypeID()) { | |
428 | warnx("%s: %s is not a string", fpath, kKeyPlistVersion); | |
429 | errs++; | |
430 | } else if(!CFEqual(vers, CFSTR("1.0"))) { | |
431 | warnx("%s: %s not 1.0", fpath, kKeyPlistVersion); | |
432 | errs++; | |
433 | } | |
434 | /* get the execpath */ | |
435 | CFStringRef execpath = CFDictionaryGetValue(plist, CFSTR(kKeyExecPath)); | |
436 | if(!execpath) { | |
437 | warnx("%s: No key %s", fpath, kKeyExecPath); | |
438 | errs++; | |
439 | } else if(CFGetTypeID(execpath) != CFStringGetTypeID()) { | |
440 | warnx("%s: %s is not a string", fpath, kKeyExecPath); | |
441 | errs++; | |
442 | } | |
443 | if (!CFStringGetFileSystemRepresentation(execpath, execpath2, sizeof(execpath2))) { | |
444 | warnx("%s: could not get exec path", fpath); | |
445 | errs++; | |
446 | } | |
447 | /* if we already got cpu preferences from ARCHPREFERENCE, we are done */ | |
448 | if(count > 0) | |
449 | break; | |
450 | /* otherwise, parse the cpu preferences from the plist */ | |
451 | CFArrayRef p = CFDictionaryGetValue(plist, CFSTR(kKeyPrefOrder)); | |
452 | if(!p) { | |
453 | warnx("%s: No key %s", fpath, kKeyPrefOrder); | |
454 | errs++; | |
455 | } else if(CFGetTypeID(p) != CFArrayGetTypeID()) { | |
456 | warnx("%s: %s is not an array", fpath, kKeyPrefOrder); | |
457 | errs++; | |
458 | } else if((count = CFArrayGetCount(p)) == 0) { | |
459 | warnx("%s: no entries in %s", fpath, kKeyPrefOrder); | |
460 | errs++; | |
461 | } else { | |
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); | |
467 | errs++; | |
468 | } else { | |
469 | char astr[128]; | |
470 | if (CFStringGetCString(a, astr, sizeof(astr), kCFStringEncodingASCII)) { | |
471 | addCPUbyname(cpu, astr); | |
472 | } | |
473 | } | |
474 | } | |
475 | } | |
476 | } while(0); /* end block */ | |
477 | if(errs) /* exit if there were any reported errors */ | |
478 | exit(1); | |
479 | ||
480 | CFRelease(plist); | |
481 | ||
482 | /* call posix_spawn */ | |
483 | spawnIt(cpu, 0, execpath2, argv); | |
484 | } | |
485 | ||
486 | static void __dead2 | |
487 | usage(int ret) | |
488 | { | |
489 | fprintf(stderr, | |
490 | "Usage: %s\n" | |
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); | |
501 | exit(ret); | |
502 | } | |
503 | ||
504 | /* | |
505 | * wrapped - check the path to see if it is a link to /usr/bin/arch. | |
506 | */ | |
507 | static int | |
508 | wrapped(const char *name) | |
509 | { | |
510 | size_t lp, ln; | |
511 | char *p; | |
512 | char *bp = NULL; | |
513 | char *cur, *path; | |
514 | char buf[MAXPATHLEN], rpbuf[MAXPATHLEN]; | |
515 | struct stat sb; | |
516 | ||
517 | ln = strlen(name); | |
518 | ||
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) { | |
523 | bp = (char *)name; | |
524 | break; | |
525 | } | |
526 | errx(1, "%s isn't executable", name); | |
527 | } | |
528 | ||
529 | /* search the PATH, looking for name */ | |
530 | if((path = getenv("PATH")) == NULL) | |
531 | path = _PATH_DEFPATH; | |
532 | ||
533 | cur = alloca(strlen(path) + 1); | |
534 | if(cur == NULL) | |
535 | err(1, "alloca"); | |
536 | strcpy(cur, path); | |
537 | while((p = strsep(&cur, ":")) != NULL) { | |
538 | /* | |
539 | * It's a SHELL path -- double, leading and trailing colons | |
540 | * mean the current directory. | |
541 | */ | |
542 | if(*p == '\0') { | |
543 | p = "."; | |
544 | lp = 1; | |
545 | } else | |
546 | lp = strlen(p); | |
547 | ||
548 | /* | |
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. | |
552 | */ | |
553 | if(lp + ln + 2 > sizeof(buf)) { | |
554 | warn("%s: path too long", p); | |
555 | continue; | |
556 | } | |
557 | bcopy(p, buf, lp); | |
558 | buf[lp] = '/'; | |
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) { | |
562 | bp = buf; | |
563 | break; | |
564 | } | |
565 | } | |
566 | if(p == NULL) | |
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); | |
572 | } | |
573 | ||
574 | /* | |
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. | |
581 | */ | |
582 | ||
583 | #define MATCHARG(a,m) ({ \ | |
584 | const char *arg = *(a); \ | |
585 | if(arg[1] == '-') arg++; \ | |
586 | strcmp(arg, (m)) == 0; \ | |
587 | }) | |
588 | ||
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) { \ | |
595 | warnx(e); \ | |
596 | usage(1); \ | |
597 | } \ | |
598 | ret = *(a); \ | |
599 | } else if(strncmp(arg, (m), (n)) == 0 && arg[n] == '=') { \ | |
600 | ret = arg + (n) + 1; \ | |
601 | } \ | |
602 | ret; \ | |
603 | }) | |
604 | ||
605 | #define MAKEENVCOPY(e) \ | |
606 | if(!envCopy) { \ | |
607 | envCopy = _copyenv(environ); \ | |
608 | if(envCopy == NULL) \ | |
609 | errx(1, (e)); \ | |
610 | } | |
611 | ||
612 | static void __dead2 | |
613 | spawnFromArgs(CPU *cpu, char **argv) | |
614 | { | |
615 | const char *ap, *ret; | |
616 | ||
617 | /* process arguments */ | |
618 | for(argv++; *argv && **argv == '-'; argv++) { | |
619 | if((ret = MATCHARGWITHVALUE(argv, "-arch", 5, "-arch without architecture"))) { | |
620 | ap = ret; | |
621 | } else if(MATCHARG(argv, "-32")) { | |
622 | ap = NATIVE_32; | |
623 | if(!ap) { | |
624 | unrecognizednative32seen = true; | |
625 | continue; | |
626 | } | |
627 | } else if(MATCHARG(argv, "-64")) { | |
628 | ap = NATIVE_64; | |
629 | if(!ap) { | |
630 | unrecognizednative64seen = true; | |
631 | continue; | |
632 | } | |
633 | } else if(MATCHARG(argv, "-c")) { | |
634 | free(envCopy); | |
635 | envCopy = _copyenv(NULL); // create empty environment | |
636 | if(!envCopy) | |
637 | errx(1, "Out of memory processing -c"); | |
638 | continue; | |
639 | } else if((ret = MATCHARGWITHVALUE(argv, "-d", 2, "-d without envname"))) { | |
640 | MAKEENVCOPY("Out of memory processing -d"); | |
641 | _unsetenvp(ret, &envCopy, NULL); | |
642 | continue; | |
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, '='); | |
646 | if(!cp) { | |
647 | warnx("-e %s: no equal sign", ret); | |
648 | usage(1); | |
649 | } | |
650 | cp++; // skip to value | |
651 | /* | |
652 | * _setenvp() only uses the name before any equal sign found in | |
653 | * the first argument. | |
654 | */ | |
655 | _setenvp(ret, cp, 1, &envCopy, NULL); | |
656 | continue; | |
657 | } else if(MATCHARG(argv, "-h")) { | |
658 | usage(0); | |
659 | } else { | |
660 | ap = *argv + 1; | |
661 | if(*ap == '-') ap++; | |
662 | } | |
663 | addCPUbyname(cpu, ap); | |
664 | } | |
665 | if(cpu->errs) | |
666 | exit(1); | |
667 | if(!*argv || !**argv) { | |
668 | warnx("No command to execute"); | |
669 | usage(1); | |
670 | } | |
671 | /* if the program is already a link to arch, then force execpath */ | |
672 | int needexecpath = wrapped(*argv); | |
673 | ||
674 | /* | |
675 | * If we don't have any architecutures, try ARCHPREFERENCE and plist | |
676 | * files. | |
677 | */ | |
678 | if((cpu->count == 0) || needexecpath) | |
679 | spawnFromPreferences(cpu, needexecpath, argv); /* doesn't return */ | |
680 | ||
681 | /* | |
682 | * Call posix_spawnp on the program name. | |
683 | */ | |
684 | spawnIt(cpu, 1, *argv, argv); | |
685 | } | |
686 | ||
687 | ||
688 | /* the main() routine */ | |
689 | int | |
690 | main(int argc, char **argv) | |
691 | { | |
692 | const char *prog = getprogname(); | |
693 | int my_name_is_arch; | |
694 | CPU cpu; | |
695 | ||
696 | if(strcmp(prog, MACHINE_PROG) == 0) { | |
697 | if(argc > 1) | |
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))) { | |
701 | if(argc == 1) | |
702 | arch(1); /* the "arch" command with no arguments was called */ | |
703 | } | |
704 | ||
705 | initCPU(&cpu); | |
706 | ||
707 | if(my_name_is_arch) | |
708 | spawnFromArgs(&cpu, argv); | |
709 | else | |
710 | spawnFromPreferences(&cpu, 1, argv); | |
711 | ||
712 | /* should never get here */ | |
713 | errx(1, "returned from spawn"); | |
714 | } |