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