From 4f5e93857671d231372096457d024926635dcee6 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Sun, 17 Nov 2013 02:05:23 +0100 Subject: [PATCH] Add setarch and getarch commands --- build/jam/images/HaikuImage | 8 +- build/jam/images/HaikuImageBootstrap | 4 +- src/bin/Jamfile | 2 + src/bin/getarch.cpp | 169 ++++++++++++++++++++ src/bin/setarch.cpp | 230 +++++++++++++++++++++++++++ 5 files changed, 407 insertions(+), 6 deletions(-) create mode 100644 src/bin/getarch.cpp create mode 100644 src/bin/setarch.cpp diff --git a/build/jam/images/HaikuImage b/build/jam/images/HaikuImage index 74bfa6daba..9ecd191da3 100644 --- a/build/jam/images/HaikuImage +++ b/build/jam/images/HaikuImage @@ -14,7 +14,7 @@ SYSTEM_BIN = [ FFilterByBuildFeatures echo eject env error expand expr factor false fdinfo ffm filepanel find finddir findpaths FirstBootPrompt fmt fold fortune frcode fstrim ftp ftpd funzip fwcontrol@x86 - gawk gdb@x86 getlimits groupadd groupdel groupmod groups gzip gzexe + gawk gdb@x86 getarch getlimits groupadd groupdel groupmod groups gzip gzexe hd head hey hostname id ident ifconfig install installsound iroster isvolume ideinfo@ide idestatus@ide @@ -31,9 +31,9 @@ SYSTEM_BIN = [ FFilterByBuildFeatures query quit rc readlink reindex release renice rlog rm rmattr rmindex rmdir roster route - safemode screen_blanker screenmode screenshot sdiff setdecor setmime settype - setversion setvolume seq sha1sum sha256sum shar shred shuf shutdown sleep - sort spamdbm split stat strace stty su sum sync sysinfo + safemode screen_blanker screenmode screenshot sdiff setarch setdecor setmime + settype setversion setvolume seq sha1sum sha256sum shar shred shuf shutdown + sleep sort spamdbm split stat strace stty su sum sync sysinfo tac tail tcpdump tcptester tee telnet telnetd test timeout top touch tr traceroute translate trash true truncate tsort tty uname unchop unexpand unmount uniq unlink unshar unzip unzipsfx diff --git a/build/jam/images/HaikuImageBootstrap b/build/jam/images/HaikuImageBootstrap index 9ea5d4dfe2..8bb57e98d3 100644 --- a/build/jam/images/HaikuImageBootstrap +++ b/build/jam/images/HaikuImageBootstrap @@ -14,7 +14,7 @@ SYSTEM_BIN = [ FFilterByBuildFeatures echo eject env error expand expr factor false fdinfo ffm filepanel find finddir findpaths fmt fold fortune frcode ftp ftpd funzip - gawk gdb@x86 getlimits groupadd groupdel groupmod groups gzip gzexe + gawk gdb@x86 getarch getlimits groupadd groupdel groupmod groups gzip gzexe hd head hey hostname id ident ifconfig install isvolume ideinfo@ide idestatus@ide @@ -31,7 +31,7 @@ SYSTEM_BIN = [ FFilterByBuildFeatures query quit rc readlink reindex release renice rlog rm rmattr rmindex rmdir roster route - safemode screen_blanker screenmode sdiff setmime settype + safemode screen_blanker screenmode sdiff setarch setmime settype setversion setvolume seq sha1sum sha256sum shar shred shuf shutdown sleep sort split stat strace stty su sum sync sysinfo tac tail tcpdump tcptester tee telnet telnetd test timeout top touch diff --git a/src/bin/Jamfile b/src/bin/Jamfile index e2fb698fc4..eb7c6b2e7d 100644 --- a/src/bin/Jamfile +++ b/src/bin/Jamfile @@ -116,9 +116,11 @@ StdBinCommands alert.cpp eject.cpp findpaths.cpp + getarch.cpp hey.cpp reindex.cpp resattr.cpp + setarch.cpp setdecor.cpp settype.cpp spybmessage.cpp diff --git a/src/bin/getarch.cpp b/src/bin/getarch.cpp new file mode 100644 index 0000000000..47a7fe6221 --- /dev/null +++ b/src/bin/getarch.cpp @@ -0,0 +1,169 @@ +/* + * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include +#include +#include +#include + +#include +#include +#include +#include + + +extern const char* __progname; +const char* kCommandName = __progname; + + +static const char* kUsage = + "Usage: %s [ ] [ ]\n" + "Prints the architecture currently set via the PATH environment variable,\n" + "when no arguments are given. When is specified, the architecture\n" + "associated with that path is printed. The options allow to print the\n" + "primary architecture or the secondary architectures.\n" + "\n" + "Options:\n" + " -h, --help\n" + " Print this usage info.\n" + " -p, --primary\n" + " Print the primary architecture.\n" + " -s, --secondary\n" + " Print all secondary architectures for which support is installed.\n" +; + + +static void +print_usage_and_exit(bool error) +{ + fprintf(error ? stderr : stdout, kUsage, kCommandName); + exit(error ? 1 : 0); +} + + +static BString +get_current_architecture() +{ + // get the system installation location path + BPath systemPath; + if (find_directory(B_SYSTEM_DIRECTORY, &systemPath) != B_OK) + return BString(); + + // get all architectures + BStringList architectures; + get_architectures(architectures); + if (architectures.CountStrings() < 2) + return BString(); + + // get the system bin directory for each architecture + BStringList binDirectories; + BPathFinder pathFinder(systemPath.Path()); + int32 architectureCount = architectures.CountStrings(); + for (int32 i = 0; i < architectureCount; i++) { + BPath path; + if (pathFinder.FindPath(architectures.StringAt(i), + B_FIND_PATH_BIN_DIRECTORY, NULL, 0, path) != B_OK + || !binDirectories.Add(path.Path())) { + return BString(); + } + } + + // Get and split the PATH environmental variable value. The first system + // bin path we encounter implies the architecture. + char* pathVariableValue = getenv("PATH"); + BStringList paths; + if (pathVariableValue != NULL + && BString(pathVariableValue).Split(":", true, paths)) { + int32 count = paths.CountStrings(); + for (int32 i = 0; i < count; i++) { + // normalize the path, but skip a relative one + BPath path; + if (paths.StringAt(i)[0] != '/' + || path.SetTo(paths.StringAt(i), NULL, true) != B_OK) { + continue; + } + + int32 index = binDirectories.IndexOf(path.Path()); + if (index >= 0) + return architectures.StringAt(index); + } + } + + return BString(); +} + + +int +main(int argc, const char* const* argv) +{ + bool printPrimary = false; + bool printSecondary = false; + + while (true) { + static struct option sLongOptions[] = { + { "help", no_argument, 0, 'h' }, + { "primary", no_argument, 0, 'p' }, + { "secondary", no_argument, 0, 's' }, + { 0, 0, 0, 0 } + }; + + opterr = 0; // don't print errors + int c = getopt_long(argc, (char**)argv, "+hps", + sLongOptions, NULL); + if (c == -1) + break; + + switch (c) { + case 'h': + print_usage_and_exit(false); + break; + + case 'p': + printPrimary = true; + break; + + case 's': + printSecondary = true; + break; + + default: + print_usage_and_exit(true); + break; + } + } + + // The remaining argument is the optional path. + const char* path = optind < argc ? argv[optind++] : NULL; + if (optind < argc) + print_usage_and_exit(true); + + // only one of path, printPrimary, printSecondary may be specified + if (int(path != NULL) + int(printPrimary) + int(printSecondary) > 1) + print_usage_and_exit(true); + + if (path != NULL) { + // architecture for given path + printf("%s\n", guess_architecture_for_path(path)); + } else if (printPrimary) { + // primary architecture + printf("%s\n", get_primary_architecture()); + } else if (printSecondary) { + // secondary architectures + BStringList architectures; + get_secondary_architectures(architectures); + int32 count = architectures.CountStrings(); + for (int32 i = 0; i < count; i++) + printf("%s\n", architectures.StringAt(i).String()); + } else { + // current architecture as implied by PATH + BString architecture = get_current_architecture(); + printf("%s\n", + architecture.IsEmpty() + ? get_primary_architecture() : architecture.String()); + } + + return 0; +} diff --git a/src/bin/setarch.cpp b/src/bin/setarch.cpp new file mode 100644 index 0000000000..b6fc26cd60 --- /dev/null +++ b/src/bin/setarch.cpp @@ -0,0 +1,230 @@ +/* + * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +extern const char* __progname; +const char* kCommandName = __progname; + + +static const char* kUsage = + "Usage: %s [ ] [ ... ]\n" + "Executes the given command or, by default, a shell with a PATH\n" + "environment variable modified such that commands for the given\n" + "architecture will be preferred, respectively used exclusively in case of\n" + "the primary architecture.\n" + "\n" + "Options:\n" + " -h, --help\n" + " Print this usage info.\n" + " -p, --print-path\n" + " Only print the modified PATH variable value; don't execute any\n" + " command.\n" +; + + +static void +print_usage_and_exit(bool error) +{ + fprintf(error ? stderr : stdout, kUsage, kCommandName); + exit(error ? 1 : 0); +} + + +static bool +is_primary_architecture(const char* architecture) +{ + return strcmp(architecture, get_primary_architecture()) == 0; +} + + +static void +get_bin_directories(const char* architecture, BStringList& _directories) +{ + status_t error = BPathFinder::FindPaths(architecture, + B_FIND_PATH_BIN_DIRECTORY, NULL, 0, _directories); + if (error != B_OK) { + fprintf(stderr, "Error: Failed to get bin directories for architecture " + "%s: %s\n", architecture, strerror(error)); + exit(1); + } +} + + +static void +compute_new_paths(const char* architecture, BStringList& _paths) +{ + // get the primary architecture bin paths + BStringList primaryBinDirectories; + get_bin_directories(get_primary_architecture(), primaryBinDirectories); + + // get the bin paths to insert + BStringList binDirectoriesToInsert; + if (!is_primary_architecture(architecture)) + get_bin_directories(architecture, binDirectoriesToInsert); + + // split the PATH variable + char* pathVariableValue = getenv("PATH"); + BStringList paths; + if (pathVariableValue != NULL + && !BString(pathVariableValue).Split(":", true, paths)) { + fprintf(stderr, "Error: Out of memory!\n"); + exit(1); + } + + // Filter the paths, removing any path that isn't associated with the + // primary architecture. Also find the insertion index for the architecture + // bin paths. + int32 insertionIndex = -1; + int32 count = paths.CountStrings(); + for (int32 i = 0; i < count; i++) { + // We always keep relative paths. Filter absolute ones only. + const char* path = paths.StringAt(i); + if (path[0] == '/') { + // try to normalize the path + BPath normalizedPath; + if (normalizedPath.SetTo(path, NULL, true) == B_OK) + path = normalizedPath.Path(); + + // Check, if this is a primary bin directory. If not, determine the + // path's architecture. + int32 index = primaryBinDirectories.IndexOf(path); + if (index >= 0) { + if (insertionIndex < 0) + insertionIndex = i; + } else if (!is_primary_architecture( + guess_architecture_for_path(path))) { + // a non-primary architecture path -- skip + continue; + } + } + + if (!_paths.Add(paths.StringAt(i))) { + fprintf(stderr, "Error: Out of memory!\n"); + exit(1); + } + } + + // Insert the paths for the specified architecture, if any. + if (!binDirectoriesToInsert.IsEmpty()) { + if (!(insertionIndex < 0 + ? _paths.Add(binDirectoriesToInsert) + : _paths.Add(binDirectoriesToInsert, insertionIndex))) { + fprintf(stderr, "Error: Out of memory!\n"); + exit(1); + } + } +} + + +int +main(int argc, const char* const* argv) +{ + bool printPath = false; + + while (true) { + static struct option sLongOptions[] = { + { "help", no_argument, 0, 'h' }, + { "print-path", no_argument, 0, 'p' }, + { 0, 0, 0, 0 } + }; + + opterr = 0; // don't print errors + int c = getopt_long(argc, (char**)argv, "+hp", + sLongOptions, NULL); + if (c == -1) + break; + + switch (c) { + case 'h': + print_usage_and_exit(false); + break; + + case 'p': + printPath = true; + break; + + default: + print_usage_and_exit(true); + break; + } + } + + // The remaining arguments are the architecture and optionally the command + // to execute. + if (optind >= argc) + print_usage_and_exit(true); + const char* architecture = optind < argc ? argv[optind++] : NULL; + + int commandArgCount = argc - optind; + const char* const* commandArgs = commandArgCount > 0 ? argv + optind : NULL; + + if (printPath && commandArgs != NULL) + print_usage_and_exit(true); + + // check the architecture + BStringList architectures; + status_t error = get_architectures(architectures); + if (error != B_OK) { + fprintf(stderr, "Error: Failed to get architectures: %s\n", + strerror(error)); + exit(1); + } + + if (!architectures.HasString(architecture)) { + fprintf(stderr, "Error: Unsupported architecture \"%s\"\n", + architecture); + exit(1); + } + + // get the new paths + BStringList paths; + compute_new_paths(architecture, paths); + + BString pathVariableValue = paths.Join(":"); + if (!paths.IsEmpty() && pathVariableValue.IsEmpty()) + fprintf(stderr, "Error: Out of memory!\n"); + + if (printPath) { + printf("%s\n", pathVariableValue.String()); + return 0; + } + + // set PATH + if (setenv("PATH", pathVariableValue, 1) != 0) { + fprintf(stderr, "Error: Failed to set PATH: %s\n", strerror(errno)); + exit(1); + } + + // if no command is given, get the user's shell + const char* shellCommand[2]; + if (commandArgs == NULL) { + struct passwd* pwd = getpwuid(geteuid()); + shellCommand[0] = pwd != NULL ? pwd->pw_shell : "/bin/sh"; + shellCommand[1] = NULL; + commandArgs = shellCommand; + commandArgCount = 1; + } + + // exec the command + execvp(commandArgs[0], (char* const*)commandArgs); + + fprintf(stderr, "Error: Executing \"%s\" failed: %s\n", commandArgs[0], + strerror(errno)); + return 1; +}