diff options
author | Cameron Katri <me@cameronkatri.com> | 2021-05-09 14:20:58 -0400 |
---|---|---|
committer | Cameron Katri <me@cameronkatri.com> | 2021-05-09 14:20:58 -0400 |
commit | 5fd83771641d15c418f747bd343ba6738d3875f7 (patch) | |
tree | 5abf0f78f680d9837dbd93d4d4c3933bb7509599 /system_cmds/arch.tproj | |
download | apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.gz apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.zst apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.zip |
Import macOS userland
adv_cmds-176
basic_cmds-55
bootstrap_cmds-116.100.1
developer_cmds-66
diskdev_cmds-667.40.1
doc_cmds-53.60.1
file_cmds-321.40.3
mail_cmds-35
misc_cmds-34
network_cmds-606.40.1
patch_cmds-17
remote_cmds-63
shell_cmds-216.60.1
system_cmds-880.60.2
text_cmds-106
Diffstat (limited to 'system_cmds/arch.tproj')
-rw-r--r-- | system_cmds/arch.tproj/arch.1 | 245 | ||||
-rw-r--r-- | system_cmds/arch.tproj/arch.c | 802 | ||||
-rw-r--r-- | system_cmds/arch.tproj/machine.1 | 49 |
3 files changed, 1096 insertions, 0 deletions
diff --git a/system_cmds/arch.tproj/arch.1 b/system_cmds/arch.tproj/arch.1 new file mode 100644 index 0000000..234b339 --- /dev/null +++ b/system_cmds/arch.tproj/arch.1 @@ -0,0 +1,245 @@ +.\" Copyright (c) 1994 SigmaSoft, Th. Lockert +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by SigmaSoft, Th. Lockert. +.\" 4. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $OpenBSD: arch.1,v 1.2 1996/06/29 20:29:34 tholo Exp $ +.\" +.\" Modifications made 8/20/97 (c) Apple Computer, Inc. +.\" Modifications made 11/12/06 (c) Apple Computer, Inc. + +.Dd July 8, 2010 +.Dt ARCH 1 +.Os "Mac OS X" +.Sh NAME +.Nm arch +.Nd print architecture type or run selected architecture of a universal binary +.Sh SYNOPSIS +.Nm arch +.Nm arch +.Op Fl 32 +.Op Fl 64 +.Oo +.Oo Fl Ns Ar arch_name | Fl arch Ar arch_name Oc Ns ... +.Oc +.Op Fl c +.Oo Fl d Ar envname Oc Ns ... +.Oo Fl e Ar envname=value Oc Ns ... +.Op Fl h +.Ar prog +.Op Ar args No ... +.Sh DESCRIPTION +The +.Nm arch +command with no arguments, displays the machine's architecture type. +.Pp +The other use of the +.Nm arch +command is to run a selected architecture of a universal binary. +A universal binary contains code that can run on different architectures. +By default, the operating system will select the architecture that most closely +matches the processor type. +A 64-bit architecture is preferred over a 32-bit architecture on a 64-bit +processor, while only 32-bit architectures can run on a 32-bit processor. +.Pp +When the most natural architecture is unavailable, the operating system will +try to pick another architecture. +On 64-bit processors, a 32-bit architecture is tried. +Otherwise, no architecture is run, and an error results. +.Pp +The +.Nm arch +command can be used to alter the operating system's normal selection order. +The most common use is to select the 32-bit architecture on a 64-bit processor, +even if a 64-bit architecture is available. +.Pp +The +.Ar arch_name +argument must be one of the currently supported architectures: +.Bl -tag -width x86_64h -offset indent +.It i386 +32-bit intel +.It x86_64 +64-bit intel +.It x86_64h +64-bit intel (haswell) +.It arm64 +64-bit arm +.It arm64e +64-bit arm (Apple Silicon) +.El +.Pp +If the binary does not contain code for +.Ar arch_name , +the +.Nm arch +command may try to select a close match. If arm64 is specified and not found, +arm64e will be tried next. If this happens, the order the architectures will +be tried is not guaranteed. +.Pp +Either prefix the architecture with a hyphen, or (for compatibility with +other commands), use +.Fl arch +followed by the architecture. +.Pp +If more than one architecture is specified, the operating system will try each +one in order, skipping an architecture that is not supported on the current +processor, or is unavailable in the universal binary. +.Pp +The other options are: +.Bl -tag -width ".Fl e Ar envname=value" +.It Fl 32 +Add the native 32-bit architecture to the list of architectures. +.It Fl 64 +Add the native 64-bit architecture to the list of architectures. +.It Fl c +Clears the environment that will be passed to the command to be run. +.It Fl d Ar envname +Deletes the named environment variable from the environment that will be passed +to the command to be run. +.It Fl e Ar envname=value +Assigns the given value to the named environment variable in the environment +that will be passed to the command to be run. +Any existing environment variable with the same name will be replaced. +.It Fl h +Prints a usage message and exits. +.El +.Pp +The +.Ar prog +argument is the command to run, followed by any arguments to pass to the +command. +It can be a full or partial path, while a lone name will be looked up in the user's +command search path. +.Pp +If no architectures are specified on the command line, the +.Nm arch +command takes the basename of the +.Ar prog +argument and searches for the first property list file with that basename and +the +.Pa \&.plist +suffix, in the +.Pa archSettings +sub-directory in each of the standard domains, in the following order: +.Bl -tag -width ".Pa /Network/Library/archSettings" -offset indent +.It ~/Library/archSettings +User settings +.It /Library/archSettings +Local settings +.It /Network/Library/archSettings +Network settings +.It /System/Library/archSettings +System settings +.El +.Pp +This property list contains the architecture order preferences, as well +as the full path to the real executable. +For examples of the property list format, look at the files in +.Pa /System/Library/archSettings . +.Ss Example +On an intel processor: +.Bd -literal -offset indent +% perl -MConfig -e 'printf "%s\\n", $Config{byteorder}' +1234 +.Ed +.Pp +shows the intel little endian byte order. +.Ss Making links to the arch command +When a link is made to +.Nm arch +command with a different name, that name is used to find +the corresponding property list file. +Thus, other commands can be wrapped so that they have custom architecture +selection order. +.Pp +Because of some internal logic in the code, hard links to the +.Nm arch +command may not work quite right. +It is best to avoid using hard links, and only use symbolic links to the +.Nm arch +command. +.Ss Environment +The environment variable +.Ev ARCHPREFERENCE +can be used to provide architecture order preferences. +It is checked before looking for the corresponding property list file. +.Pp +The value of the environment variable +.Ev ARCHPREFERENCE +is composed of one or more specifiers, separated by semicolons. +A specifier is made up of one, two or three fields, separated by colons. +Architectures specified in order, are separated by commas and make up the last +(mandatory) field. +The first field, if specified, is a name of a program, which selects this +specifier if that name matches the program name in question. +If the name field is empty or there is no name field, the specifier matches +any program name. +Thus, ordering of specifiers is important, and the one with no name should +be last. +.Pp +When the +.Nm arch +command is called directly, the +.Ar prog +name provides the path information to the executable (possibly via the command +search path). +When a name is specified in a +.Ev ARCHPREFERENCE +specifier, the path information can alternately be specified as a second +field following the name. +When the +.Nm arch +command is called indirectly via a link, this path information must be +specified. +If not specified as a second field in a specifier, the executable path will +be looked up in the corresponding property list file. +.Ss Example ARCHPREFERENCE Values +.Bl -tag -width " " +.It i386,x86_64,x86_64h,arm64,arm64e +A specifier that matches any name. +.It foo:i386,x86_64,x86_64h,arm64,arm64e +A specifier that matches the program named +.Nm foo +(the full executable path is in the +.Pa foo.plist +file). +.It foo:/op/bin/boo:i386,x86_64,x86_64h,arm64,arm64e +A specifier with all fields specified. +.It baz:i386;x86_64;x86_64h,arm64,arm64e +A specifier for +.Nm baz +and a second specifier that would match any other name. +.El +.Sh BUGS +Running the +.Nm arch +command on an interpreter script may not work if the interpreter is a link +to the arch command, especially if a 64-bit architecture is specified (since the +.Nm arch +command is 2-way universal, 32-bit only). +.Sh SEE ALSO +.Xr machine 1 diff --git a/system_cmds/arch.tproj/arch.c b/system_cmds/arch.tproj/arch.c new file mode 100644 index 0000000..b522ce8 --- /dev/null +++ b/system_cmds/arch.tproj/arch.c @@ -0,0 +1,802 @@ +/* + * Copyright (c) 1999-2018 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include <sys/cdefs.h> +#include <stdio.h> +#include <string.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stddef.h> +#include <unistd.h> +#include <spawn.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <paths.h> +#include <err.h> +#include <mach/mach.h> +#include <mach-o/arch.h> +#include <limits.h> +#include <sys/fcntl.h> +#include <glob.h> +#include <CoreFoundation/CoreFoundation.h> +#include <NSSystemDirectories.h> +#include <sysdir.h> + +#if defined(__x86_64__) +#include <System/i386/cpu_capabilities.h> +#endif + +#if TARGET_OS_OSX +#include <sys/types.h> +#include <sys/sysctl.h> +#endif + +#ifndef ARCH_PROG +#define ARCH_PROG "arch" +#endif +#ifndef MACHINE_PROG +#define MACHINE_PROG "machine" +#endif + +#define kKeyExecPath "ExecutablePath" +#define kKeyPlistVersion "PropertyListVersion" +#define kKeyPrefOrder "PreferredOrder" +#define kPlistExtension ".plist" +#define kSettingsDir "archSettings" + +static const char envname[] = "ARCHPREFERENCE"; + +/* The CPU struct contains the argument buffer to posix_spawnattr_setbinpref_np */ + +typedef struct { + cpu_type_t *types; + cpu_subtype_t *subtypes; + int errs; + size_t count; + size_t capacity; +} CPU; + +typedef struct { + const char *arch; + cpu_type_t cpu; + cpu_subtype_t cpusubtype; +} CPUTypes; + +static const CPUTypes knownArchs[] = { + {"i386", CPU_TYPE_I386, CPU_SUBTYPE_X86_ALL}, + {"x86_64", CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL}, + {"x86_64h", CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_H}, + {"arm", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_ALL}, + {"arm64", CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL}, + /* rdar://65725144 ("arch -arm64" command should launch arm64e slice for 2-way fat x86_64+arm64e binaries) */ + /* This will modify the order if someone, for whatever reason, specifies -arch arm64 -arch x86_64 -arch arm64e */ + {"arm64", CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64E}, + {"arm64e", CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64E}, + {"arm64_32", CPU_TYPE_ARM64_32, CPU_SUBTYPE_ARM64_32_ALL}, +}; + +/* environment SPI */ +char **_copyenv(char **env); +int _setenvp(const char *name, const char *value, int rewrite, char ***envp, void *state); +int _unsetenvp(const char *name, char ***envp, void *state); + +/* copy of environment */ +char **envCopy = NULL; +extern char **environ; + +/* + * The native 32 and 64-bit architectures (this is relative to the architecture + * the arch command is running). NULL means unsupported. + */ + +static const char* +native32(void) +{ +#if defined(__i386__) || defined(__x86_64__) + return "i386"; +#elif defined(__arm64__) && !defined(__LP64__) + return "arm64_32"; +#elif defined(__arm__) + return "arm"; +#else + return NULL; +#endif +} + +static const char* +native64(void) +{ +#if defined(__x86_64__) + // FIXME: It is not clear if we should do this check, given the comment above which states "this is relative to the architecture the arch command is running". + if (((*(uint64_t*)_COMM_PAGE_CPU_CAPABILITIES64) & kIsTranslated)) + return "arm64"; + return "x86_64"; +#elif defined(__i386__) + return "x86_64"; +#elif defined(__arm64__) && defined(__LP64__) + return "arm64"; +#elif defined(__arm64__) + return "arm64_32"; +#else + return NULL; +#endif +} + +static bool +isSupportedCPU(cpu_type_t cpu) +{ +#if defined(__x86_64__) + if (((*(uint64_t*)_COMM_PAGE_CPU_CAPABILITIES64) & kIsTranslated)) + return cpu == CPU_TYPE_X86_64 || cpu == CPU_TYPE_ARM64; + return cpu == CPU_TYPE_X86_64; +#elif defined(__i386__) + return cpu == CPU_TYPE_I386 || cpu == CPU_TYPE_X86_64; +#elif defined(__arm64__) && defined(__LP64__) && TARGET_OS_OSX + return cpu == CPU_TYPE_X86_64 || cpu == CPU_TYPE_ARM64; +#elif defined(__arm64__) && defined(__LP64__) + return cpu == CPU_TYPE_ARM64; +#elif defined(__arm64__) + return cpu == CPU_TYPE_ARM64_32; +#elif defined(__arm__) + return cpu == CPU_TYPE_ARM; +#else + #error "Unsupported architecture" +#endif +} + +bool unrecognizednative32seen = false; +bool unrecognizednative64seen = false; + +/* + * arch - perform the original behavior of the arch and machine commands. + * The archcmd flag is non-zero for the arch command, zero for the machine + * command. This routine never returns. + */ +static void __dead2 +arch(int archcmd) +{ + const NXArchInfo *arch = NXGetLocalArchInfo(); + + if(!arch) + errx(-1, "Unknown architecture."); + if(archcmd) { + arch = NXGetArchInfoFromCpuType(arch->cputype, CPU_SUBTYPE_MULTIPLE); + if(!arch) + errx(-1, "Unknown architecture."); + } + printf("%s%s", arch->name, (isatty(STDIN_FILENO) ? "\n" : "")); + exit(0); +} + +/* + * spawnIt - run the posix_spawn command. cpu is the auto-sizing CPU structure. + * pflag is non-zero to call posix_spawnp; zero means to call posix_spawn. + * str is the name/path to pass to posix_spawn{,p}, and argv are + * the argument arrays to pass. This routine never returns. + */ +static void __dead2 +spawnIt(CPU *cpu, int pflag, const char *str, char **argv) +{ + posix_spawnattr_t attr; + pid_t pid; + int ret; + size_t copied; + size_t count = cpu->count; + cpu_type_t *prefs = cpu->types; + cpu_subtype_t *subprefs = cpu->subtypes; + + if(count == 0) { + if(unrecognizednative32seen) + warnx("Unsupported native 32-bit architecture"); + if(unrecognizednative64seen) + warnx("Unsupported native 64-bit architecture"); + exit(1); + } + + if(unrecognizednative32seen) + fprintf(stderr, "warning: unsupported native 32-bit architecture\n"); + if(unrecognizednative64seen) + fprintf(stderr, "warning: unsupported native 64-bit architecture\n"); + + if((ret = posix_spawnattr_init(&attr)) != 0) + errc(1, ret, "posix_spawnattr_init"); + /* do the equivalent of exec, rather than creating a separate process */ + if((ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETEXEC)) != 0) + errc(1, ret, "posix_spawnattr_setflags"); + if((ret = posix_spawnattr_setarchpref_np(&attr, count, prefs, subprefs, &copied)) != 0) + errc(1, ret, "posix_spawnattr_setbinpref_np"); + +#if TARGET_OS_OSX + for (size_t i = 0; i < copied; i++) { + if (prefs[i] == CPU_TYPE_ARM64) { + int affinity = 0; + size_t asize = sizeof(affinity); + sysctlbyname("kern.curproc_arch_affinity", NULL, NULL, &affinity, asize); + } + } +#endif /* TARGET_OS_OSX */ + + if(copied != count) + errx(1, "posix_spawnattr_setbinpref_np only copied %lu of %lu", copied, count); + if(pflag) + ret = posix_spawnp(&pid, str, NULL, &attr, argv, envCopy ? envCopy : environ); + else + ret = posix_spawn(&pid, str, NULL, &attr, argv, envCopy ? envCopy : environ); + errc(1, ret, "posix_spawn%s: %s", (pflag ? "p" : ""), str); +} + +/* + * initCPU - initialize a CPU structure, a dynamically expanding CPU types + * array. + */ +static void +initCPU(CPU *cpu) +{ + cpu->errs = 0; + cpu->count = 0; + cpu->capacity = 1; + cpu->types = (cpu_type_t *)malloc(cpu->capacity * sizeof(cpu_type_t)); + if(!cpu->types) + err(1, "Failed to malloc CPU type buffer"); + cpu->subtypes = (cpu_subtype_t *)malloc(cpu->capacity * sizeof(cpu_subtype_t)); + if(!cpu->subtypes) + err(1, "Failed to malloc CPU subtype buffer"); +} + +/* + * addCPU - add a new CPU type value to the CPU structure, expanding + * the array as necessary. + */ +static void +addCPU(CPU *cpu, cpu_type_t n, cpu_subtype_t s) +{ + if(cpu->count == cpu->capacity) { + cpu_type_t *newcpubuf; + cpu_subtype_t *newcpusubbuf; + + cpu->capacity *= 2; + newcpubuf = (cpu_type_t *)realloc(cpu->types, cpu->capacity * sizeof(cpu_type_t)); + if(!newcpubuf) + err(1, "Out of memory realloc-ing CPU types structure"); + cpu->types = newcpubuf; + newcpusubbuf = (cpu_subtype_t *)realloc(cpu->subtypes, cpu->capacity * sizeof(cpu_subtype_t)); + if(!newcpusubbuf) + err(1, "Out of memory realloc-ing CPU subtypes structure"); + cpu->subtypes = newcpusubbuf; + + } + cpu->types[cpu->count] = n; + cpu->subtypes[cpu->count++] = s; +} + +/* + * addCPUbyname - add a new CPU type, given by name, to the CPU structure, + * expanding the array as necessary. The name is converted to a type value + * by the ArchDict dictionary. + */ +static void +addCPUbyname(CPU *cpu, const char *name) +{ + int i; + size_t numKnownArchs = sizeof(knownArchs)/sizeof(knownArchs[0]); + + for (i=0; i < numKnownArchs; i++) { + if (!isSupportedCPU(knownArchs[i].cpu)) + continue; + int start = i; + /* rdar://65725144 ("arch -arm64" command should launch arm64e slice for 2-way fat x86_64+arm64e binaries) */ + /* Add subtypes that closely match the specified name */ + while ( i < numKnownArchs && + 0 == strcasecmp(name, knownArchs[i].arch) ) { + addCPU(cpu, knownArchs[i].cpu, knownArchs[i].cpusubtype); + i++; + } + if (start != i) + return; + } + + /* Didn't match a string in knownArchs */ + warnx("Unknown architecture: %s", name); + cpu->errs++; +} + +/* + * useEnv - parse the environment variable for CPU preferences. Use name + * to look for program-specific preferences, and append any CPU types to cpu. + * Returns the number of CPU types. Returns any specified execute path in + * execpath. + * + * The environment variable ARCHPREFERENCE has the format: + * spec[;spec]... + * a semicolon separated list of specifiers. Each specifier has the format: + * [prog:[execpath:]]type[,type]... + * a comma separate list of CPU type names, optionally proceeded by a program + * name and an execpath. If program name exist, that types only apply to that + * program. If execpath is specified, it is returned. If no program name + * exists, then it applies to all programs. So ordering of the specifiers is + * important, as the default (no program name) specifier must be last. + */ +static size_t +useEnv(CPU *cpu, const char *name, char **execpath) +{ + char *val = getenv(envname); + if(!val) + return 0; + + /* cp will point to the basename of name */ + const char *cp = strrchr(name, '/'); + if(cp) { + cp++; + if(!*cp) + errx(1, "%s: no name after last slash", name); + } else + cp = name; + /* make a copy of the environment variable value, so we can modify it */ + val = strdup(val); + if(!val) + err(1, "Can't copy environment %s", envname); + char *str = val; + char *blk; + /* for each specifier */ + while((blk = strsep(&str, ";")) != NULL) { + if(*blk == 0) + continue; /* two adjacent semicolons */ + /* now split on colons */ + char *n = strsep(&blk, ":"); + if(blk) { + char *p = strsep(&blk, ":"); + if(!blk) { /* there is only one colon, so no execpath */ + blk = p; + p = NULL; + } else if(!*p) /* two consecutive colons, so no execpath */ + p = NULL; + if(!*blk) + continue; /* no cpu list, so skip */ + /* if the name matches, or there is no name, process the cpus */ + if(!*n || strcmp(n, cp) == 0) { + if(cpu->count == 0) { /* only if we haven't processed architectures */ + char *t; + while((t = strsep(&blk, ",")) != NULL) + addCPUbyname(cpu, t); + } + *execpath = (*n ? p : NULL); /* only use the exec path is name is set */ + break; + } + } else { /* no colons at all, so process as default */ + if(cpu->count == 0) { /* only if we haven't processed architectures */ + blk = n; + while((n = strsep(&blk, ",")) != NULL) + addCPUbyname(cpu, n); + } + *execpath = NULL; + break; + } + } + if(cpu->errs) /* errors during addCPUbyname are fatal */ + exit(1); + return cpu->count; /* return count of architectures */ +} + +/* + * spawnFromPreference - called when argv[0] is not "arch" or "machine", or + * argv[0] was arch, but no commandline architectures were specified. + * If the environment variable ARCHPREFERENCE is specified, and there is a + * match to argv[0], use the specified cpu preferences. If no exec path + * is specified in ARCHPREFERENCE, or no match is found in ARCHPREFERENCE, + * get any additional information from a .plist file with the name of argv[0]. + * This routine never returns. + */ +static void __dead2 +spawnFromPreferences(CPU *cpu, int needexecpath, char **argv) +{ + char *epath = NULL; + char fpath[PATH_MAX]; + char execpath2[PATH_MAX]; + CFDictionaryRef plist = NULL; + sysdir_search_path_enumeration_state state; + size_t count, i; + const char *prog = strrchr(*argv, '/'); + + if(prog) + prog++; + else + prog = *argv; + if(!*prog) + errx(1, "Not program name specified"); + + /* check the environment variable first */ + if((count = useEnv(cpu, prog, &epath)) > 0) { + /* if we were called as arch, use posix_spawnp */ + if(!needexecpath) + spawnIt(cpu, 1, (epath ? epath : *argv), argv); + /* otherwise, if we have the executable path, call posix_spawn */ + if(epath) + spawnIt(cpu, 0, epath, argv); + } + + state = sysdir_start_search_path_enumeration(SYSDIR_DIRECTORY_LIBRARY, SYSDIR_DOMAIN_MASK_ALL); + while ((state = sysdir_get_next_search_path_enumeration(state, fpath))) { + + if (fpath[0] == '~') { + glob_t pglob; + int gret; + + bzero(&pglob, sizeof(pglob)); + + gret = glob(fpath, GLOB_TILDE, NULL, &pglob); + if (gret == 0) { + int i; + for (i=0; i < pglob.gl_pathc; i++) { + /* take the first glob expansion */ + strlcpy(fpath, pglob.gl_pathv[i], sizeof(fpath)); + break; + } + } + globfree(&pglob); + } + + // Handle path + strlcat(fpath, "/" kSettingsDir "/", sizeof(fpath)); + strlcat(fpath, prog, sizeof(fpath)); + strlcat(fpath, kPlistExtension, sizeof(fpath)); + // printf("component: %s\n", fpath); + + int fd, ret; + size_t length; + ssize_t rsize; + struct stat sb; + void *buffer; + fd = open(fpath, O_RDONLY, 0); + if (fd >= 0) { + ret = fstat(fd, &sb); + if (ret == 0) { + if (sb.st_size <= SIZE_T_MAX) { + length = (size_t)sb.st_size; + buffer = malloc(length); /* ownership transferred to CFData */ + if (buffer) { + rsize = read(fd, buffer, length); + if (rsize == length) { + CFDataRef data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, buffer, length, kCFAllocatorMalloc); + if (data) { + buffer = NULL; + plist = CFPropertyListCreateWithData(kCFAllocatorDefault, data, kCFPropertyListImmutable, NULL, NULL); + CFRelease(data); + } + } + if (buffer) { + free(buffer); + } + } + } + } + close(fd); + } + + if (plist) { + break; + } + } + + if (plist) { + if (CFGetTypeID(plist) != CFDictionaryGetTypeID()) + errx(1, "%s: plist not a dictionary", fpath); + } else { + errx(1, "Can't find any plists for %s", prog); + } + + int errs = 0; /* scan for all errors and fail later */ + do { /* begin block */ + /* check the plist version */ + CFStringRef vers = CFDictionaryGetValue(plist, CFSTR(kKeyPlistVersion)); + if(!vers) { + warnx("%s: No key %s", fpath, kKeyPlistVersion); + errs++; + } else if(CFGetTypeID(vers) != CFStringGetTypeID()) { + warnx("%s: %s is not a string", fpath, kKeyPlistVersion); + errs++; + } else if(!CFEqual(vers, CFSTR("1.0"))) { + warnx("%s: %s not 1.0", fpath, kKeyPlistVersion); + errs++; + } + /* get the execpath */ + CFStringRef execpath = CFDictionaryGetValue(plist, CFSTR(kKeyExecPath)); + if(!execpath) { + warnx("%s: No key %s", fpath, kKeyExecPath); + errs++; + } else if(CFGetTypeID(execpath) != CFStringGetTypeID()) { + warnx("%s: %s is not a string", fpath, kKeyExecPath); + errs++; + } + if (!CFStringGetFileSystemRepresentation(execpath, execpath2, sizeof(execpath2))) { + warnx("%s: could not get exec path", fpath); + errs++; + } + /* if we already got cpu preferences from ARCHPREFERENCE, we are done */ + if(count > 0) + break; + /* otherwise, parse the cpu preferences from the plist */ + CFArrayRef p = CFDictionaryGetValue(plist, CFSTR(kKeyPrefOrder)); + if(!p) { + warnx("%s: No key %s", fpath, kKeyPrefOrder); + errs++; + } else if(CFGetTypeID(p) != CFArrayGetTypeID()) { + warnx("%s: %s is not an array", fpath, kKeyPrefOrder); + errs++; + } else if((count = CFArrayGetCount(p)) == 0) { + warnx("%s: no entries in %s", fpath, kKeyPrefOrder); + errs++; + } else { + /* finally build the cpu type array */ + for(i = 0; i < count; i++) { + CFStringRef a = CFArrayGetValueAtIndex(p, i); + if(CFGetTypeID(a) != CFStringGetTypeID()) { + warnx("%s: entry %lu of %s is not a string", fpath, i, kKeyPrefOrder); + errs++; + } else { + char astr[128]; + if (CFStringGetCString(a, astr, sizeof(astr), kCFStringEncodingASCII)) { + addCPUbyname(cpu, astr); + } + } + } + } + } while(0); /* end block */ + if(errs) /* exit if there were any reported errors */ + exit(1); + + CFRelease(plist); + + /* call posix_spawn */ + spawnIt(cpu, 0, execpath2, argv); +} + +static void __dead2 +usage(int ret) +{ + fprintf(stderr, + "Usage: %s\n" + " Display the machine's architecture type\n" + "Usage: %s {-arch_name | -arch arch_name} ... [-c] [-d envname] ... [-e envname=value] ... [-h] prog [arg ...]\n" + " Run prog with any arguments, using the given architecture\n" + " order. If no architectures are specified, use the\n" + " ARCHPREFERENCE environment variable, or a property list file.\n" + " -c will clear out all environment variables before running prog.\n" + " -d will delete the given environment variable before running prog.\n" + " -e will add the given environment variable/value before running prog.\n" + " -h will print usage message and exit.\n", + ARCH_PROG, ARCH_PROG); + exit(ret); +} + +/* + * wrapped - check the path to see if it is a link to /usr/bin/arch. + */ +static int +wrapped(const char *name) +{ + size_t lp, ln; + char *p; + char *bp = NULL; + char *cur, *path; + char buf[MAXPATHLEN], rpbuf[MAXPATHLEN]; + struct stat sb; + + ln = strlen(name); + + do { /* begin block */ + /* If it's an absolute or relative path name, it's easy. */ + if(index(name, '/')) { + if(stat(name, &sb) == 0 && S_ISREG(sb.st_mode) && access(name, X_OK) == 0) { + bp = (char *)name; + break; + } + errx(1, "%s isn't executable", name); + } + + /* search the PATH, looking for name */ + if((path = getenv("PATH")) == NULL) + path = _PATH_DEFPATH; + + cur = alloca(strlen(path) + 1); + if(cur == NULL) + err(1, "alloca"); + strcpy(cur, path); + while((p = strsep(&cur, ":")) != NULL) { + /* + * It's a SHELL path -- double, leading and trailing colons + * mean the current directory. + */ + if(*p == '\0') { + p = "."; + lp = 1; + } else + lp = strlen(p); + + /* + * If the path is too long complain. This is a possible + * security issue; given a way to make the path too long + * the user may execute the wrong program. + */ + if(lp + ln + 2 > sizeof(buf)) { + warn("%s: path too long", p); + continue; + } + bcopy(p, buf, lp); + buf[lp] = '/'; + bcopy(name, buf + lp + 1, ln); + buf[lp + ln + 1] = '\0'; + if(stat(buf, &sb) == 0 && S_ISREG(sb.st_mode) && access(buf, X_OK) == 0) { + bp = buf; + break; + } + } + if(p == NULL) + errx(1, "Can't find %s in PATH", name); + } while(0); /* end block */ + if(realpath(bp, rpbuf) == NULL) + errx(1, "realpath failed on %s", bp); + return (strcmp(rpbuf, "/usr/bin/" ARCH_PROG) == 0); +} + +/* + * spawnFromArgs - called when arch has arguments specified. The arch command + * line arguments are: + * % arch [[{-xxx | -arch xxx}]...] prog [arg]... + * where xxx is a cpu name, and the command to execute and its arguments follow. + * If no commandline cpu names are given, the environment variable + * ARCHPREFERENCE is used. This routine never returns. + */ + +#define MATCHARG(a,m) ({ \ + const char *arg = *(a); \ + if(arg[1] == '-') arg++; \ + strcmp(arg, (m)) == 0; \ +}) + +#define MATCHARGWITHVALUE(a,m,n,e) ({ \ + const char *ret = NULL; \ + const char *arg = *(a); \ + if(arg[1] == '-') arg++; \ + if(strcmp(arg, (m)) == 0) { \ + if(*++(a) == NULL) { \ + warnx(e); \ + usage(1); \ + } \ + ret = *(a); \ + } else if(strncmp(arg, (m), (n)) == 0 && arg[n] == '=') { \ + ret = arg + (n) + 1; \ + } \ + ret; \ +}) + +#define MAKEENVCOPY(e) \ + if(!envCopy) { \ + envCopy = _copyenv(environ); \ + if(envCopy == NULL) \ + errx(1, (e)); \ + } + +static void __dead2 +spawnFromArgs(CPU *cpu, char **argv) +{ + const char *ap, *ret; + + /* process arguments */ + for(argv++; *argv && **argv == '-'; argv++) { + if((ret = MATCHARGWITHVALUE(argv, "-arch", 5, "-arch without architecture"))) { + ap = ret; + } else if(MATCHARG(argv, "-32")) { + ap = native32(); + if(!ap) { + unrecognizednative32seen = true; + continue; + } + } else if(MATCHARG(argv, "-64")) { + ap = native64(); + if(!ap) { + unrecognizednative64seen = true; + continue; + } + } else if(MATCHARG(argv, "-c")) { + free(envCopy); + envCopy = _copyenv(NULL); // create empty environment + if(!envCopy) + errx(1, "Out of memory processing -c"); + continue; + } else if((ret = MATCHARGWITHVALUE(argv, "-d", 2, "-d without envname"))) { + MAKEENVCOPY("Out of memory processing -d"); + _unsetenvp(ret, &envCopy, NULL); + continue; + } else if((ret = MATCHARGWITHVALUE(argv, "-e", 2, "-e without envname=value"))) { + MAKEENVCOPY("Out of memory processing -e"); + const char *cp = strchr(ret, '='); + if(!cp) { + warnx("-e %s: no equal sign", ret); + usage(1); + } + cp++; // skip to value + /* + * _setenvp() only uses the name before any equal sign found in + * the first argument. + */ + _setenvp(ret, cp, 1, &envCopy, NULL); + continue; + } else if(MATCHARG(argv, "-h")) { + usage(0); + } else { + ap = *argv + 1; + if(*ap == '-') ap++; + } + addCPUbyname(cpu, ap); + } + if(cpu->errs) + exit(1); + if(!*argv || !**argv) { + warnx("No command to execute"); + usage(1); + } + /* if the program is already a link to arch, then force execpath */ + int needexecpath = wrapped(*argv); + + /* + * If we don't have any architecutures, try ARCHPREFERENCE and plist + * files. + */ + if((cpu->count == 0) || needexecpath) + spawnFromPreferences(cpu, needexecpath, argv); /* doesn't return */ + + /* + * Call posix_spawnp on the program name. + */ + spawnIt(cpu, 1, *argv, argv); +} + + +/* the main() routine */ +int +main(int argc, char **argv) +{ + const char *prog = getprogname(); + int my_name_is_arch; + CPU cpu; + + if(strcmp(prog, MACHINE_PROG) == 0) { + if(argc > 1) + errx(-1, "no arguments accepted"); + arch(0); /* the "machine" command was called */ + } else if((my_name_is_arch = (strcmp(prog, ARCH_PROG) == 0))) { + if(argc == 1) + arch(1); /* the "arch" command with no arguments was called */ + } + + initCPU(&cpu); + + if(my_name_is_arch) + spawnFromArgs(&cpu, argv); + else + spawnFromPreferences(&cpu, 1, argv); + + /* should never get here */ + errx(1, "returned from spawn"); +} diff --git a/system_cmds/arch.tproj/machine.1 b/system_cmds/arch.tproj/machine.1 new file mode 100644 index 0000000..139c2ec --- /dev/null +++ b/system_cmds/arch.tproj/machine.1 @@ -0,0 +1,49 @@ +.\" $OpenBSD: machine.1,v 1.2 1996/06/26 05:36:23 deraadt Exp $ +.\" Copyright (c) 1980, 1990 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" from: @(#)machine.1 5.5 (Berkeley) 7/26/91 +.\" +.Dd July 26, 1991 +.Dt MACHINE 1 +.Os +.Sh NAME +.Nm machine +.Nd print machine type +.Sh SYNOPSIS +.Nm machine +.Sh DESCRIPTION +The +.Nm machine +command displays the machine type. +.Sh SEE ALSO +.Xr arch 1 , +.Xr make 1 |