aboutsummaryrefslogtreecommitdiffstats
path: root/system_cmds/arch.tproj
diff options
context:
space:
mode:
authorCameron Katri <me@cameronkatri.com>2021-05-09 14:20:58 -0400
committerCameron Katri <me@cameronkatri.com>2021-05-09 14:20:58 -0400
commit5fd83771641d15c418f747bd343ba6738d3875f7 (patch)
tree5abf0f78f680d9837dbd93d4d4c3933bb7509599 /system_cmds/arch.tproj
downloadapple_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.1245
-rw-r--r--system_cmds/arch.tproj/arch.c802
-rw-r--r--system_cmds/arch.tproj/machine.149
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