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 58static const char envname[] = "ARCHPREFERENCE"; 59 60/* The CPU struct contains the argument buffer to posix_spawnattr_setbinpref_np */ 61 62typedef struct { 63 cpu_type_t *buf; 64 int errs; 65 size_t count; 66 size_t capacity; 67} CPU; 68 69typedef struct { 70 const char *arch; 71 cpu_type_t cpu; 72} CPUTypes; 73 74static 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}, 80#else 81#error "Unsupported architecture" 82#endif 83}; 84 85/* environment SPI */ 86char **_copyenv(char **env); 87int _setenvp(const char *name, const char *value, int rewrite, char ***envp, void *state); 88int _unsetenvp(const char *name, char ***envp, void *state); 89 90/* copy of environment */ 91char **envCopy = NULL; 92extern 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 104#else 105#error "Unsupported architecture" 106#endif 107bool unrecognizednative32seen = false; 108bool 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 */ 115static void __dead2 116arch(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 */ 137static void __dead2 138spawnIt(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 */ 180static void 181initCPU(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 */ 195static void 196addCPU(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 */ 215static void 216addCPUbyname(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 */ 248static size_t 249useEnv(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 */ 318static void __dead2 319spawnFromPreferences(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 486static void __dead2 487usage(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 */ 507static int 508wrapped(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 612static void __dead2 613spawnFromArgs(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 */ 689int 690main(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} 715