/* * 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 [-hl]\n" " %s [-p] [ ... ]\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" " -l, --list-architectures\n" " List all architectures.\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, 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 = _paths.CountStrings(); } 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; bool listArchitectures = false; while (true) { static struct option sLongOptions[] = { { "help", no_argument, 0, 'h' }, { "list-architectures", no_argument, 0, 'l' }, { "print-path", no_argument, 0, 'p' }, { 0, 0, 0, 0 } }; opterr = 0; // don't print errors int c = getopt_long(argc, (char**)argv, "+hlp", sLongOptions, NULL); if (c == -1) break; switch (c) { case 'h': print_usage_and_exit(false); break; case 'l': listArchitectures = true; break; case 'p': printPath = true; break; default: print_usage_and_exit(true); break; } } // only one of listArchitectures, printPath may be specified if (listArchitectures && printPath) print_usage_and_exit(true); // get architectures 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); } // list architectures if (listArchitectures) { if (optind != argc) print_usage_and_exit(true); int32 count = architectures.CountStrings(); for (int32 i = 0; i < count; i++) printf("%s\n", architectures.StringAt(i).String()); return 0; } // 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 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[3]; if (commandArgs == NULL) { struct passwd* pwd = getpwuid(geteuid()); shellCommand[0] = pwd != NULL ? pwd->pw_shell : "/bin/sh"; shellCommand[1] = "-l"; shellCommand[2] = NULL; commandArgs = shellCommand; commandArgCount = 2; } // exec the command execvp(commandArgs[0], (char* const*)commandArgs); fprintf(stderr, "Error: Executing \"%s\" failed: %s\n", commandArgs[0], strerror(errno)); return 1; }