1 /* 2 * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <errno.h> 8 #include <getopt.h> 9 #include <pwd.h> 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <string.h> 13 #include <unistd.h> 14 15 #include <Architecture.h> 16 #include <Path.h> 17 #include <PathFinder.h> 18 #include <StringList.h> 19 20 21 extern const char* __progname; 22 const char* kCommandName = __progname; 23 24 25 static const char* kUsage = 26 "Usage: %s [ <options> ] <architecture> [ <command> ... ]\n" 27 "Executes the given command or, by default, a shell with a PATH\n" 28 "environment variable modified such that commands for the given\n" 29 "architecture will be preferred, respectively used exclusively in case of\n" 30 "the primary architecture.\n" 31 "\n" 32 "Options:\n" 33 " -h, --help\n" 34 " Print this usage info.\n" 35 " -p, --print-path\n" 36 " Only print the modified PATH variable value; don't execute any\n" 37 " command.\n" 38 ; 39 40 41 static void 42 print_usage_and_exit(bool error) 43 { 44 fprintf(error ? stderr : stdout, kUsage, kCommandName); 45 exit(error ? 1 : 0); 46 } 47 48 49 static bool 50 is_primary_architecture(const char* architecture) 51 { 52 return strcmp(architecture, get_primary_architecture()) == 0; 53 } 54 55 56 static void 57 get_bin_directories(const char* architecture, BStringList& _directories) 58 { 59 status_t error = BPathFinder::FindPaths(architecture, 60 B_FIND_PATH_BIN_DIRECTORY, NULL, 0, _directories); 61 if (error != B_OK) { 62 fprintf(stderr, "Error: Failed to get bin directories for architecture " 63 "%s: %s\n", architecture, strerror(error)); 64 exit(1); 65 } 66 } 67 68 69 static void 70 compute_new_paths(const char* architecture, BStringList& _paths) 71 { 72 // get the primary architecture bin paths 73 BStringList primaryBinDirectories; 74 get_bin_directories(get_primary_architecture(), primaryBinDirectories); 75 76 // get the bin paths to insert 77 BStringList binDirectoriesToInsert; 78 if (!is_primary_architecture(architecture)) 79 get_bin_directories(architecture, binDirectoriesToInsert); 80 81 // split the PATH variable 82 char* pathVariableValue = getenv("PATH"); 83 BStringList paths; 84 if (pathVariableValue != NULL 85 && !BString(pathVariableValue).Split(":", true, paths)) { 86 fprintf(stderr, "Error: Out of memory!\n"); 87 exit(1); 88 } 89 90 // Filter the paths, removing any path that isn't associated with the 91 // primary architecture. Also find the insertion index for the architecture 92 // bin paths. 93 int32 insertionIndex = -1; 94 int32 count = paths.CountStrings(); 95 for (int32 i = 0; i < count; i++) { 96 // We always keep relative paths. Filter absolute ones only. 97 const char* path = paths.StringAt(i); 98 if (path[0] == '/') { 99 // try to normalize the path 100 BPath normalizedPath; 101 if (normalizedPath.SetTo(path, NULL, true) == B_OK) 102 path = normalizedPath.Path(); 103 104 // Check, if this is a primary bin directory. If not, determine the 105 // path's architecture. 106 int32 index = primaryBinDirectories.IndexOf(path); 107 if (index >= 0) { 108 if (insertionIndex < 0) 109 insertionIndex = i; 110 } else if (!is_primary_architecture( 111 guess_architecture_for_path(path))) { 112 // a non-primary architecture path -- skip 113 continue; 114 } 115 } 116 117 if (!_paths.Add(paths.StringAt(i))) { 118 fprintf(stderr, "Error: Out of memory!\n"); 119 exit(1); 120 } 121 } 122 123 // Insert the paths for the specified architecture, if any. 124 if (!binDirectoriesToInsert.IsEmpty()) { 125 if (!(insertionIndex < 0 126 ? _paths.Add(binDirectoriesToInsert) 127 : _paths.Add(binDirectoriesToInsert, insertionIndex))) { 128 fprintf(stderr, "Error: Out of memory!\n"); 129 exit(1); 130 } 131 } 132 } 133 134 135 int 136 main(int argc, const char* const* argv) 137 { 138 bool printPath = false; 139 140 while (true) { 141 static struct option sLongOptions[] = { 142 { "help", no_argument, 0, 'h' }, 143 { "print-path", no_argument, 0, 'p' }, 144 { 0, 0, 0, 0 } 145 }; 146 147 opterr = 0; // don't print errors 148 int c = getopt_long(argc, (char**)argv, "+hp", 149 sLongOptions, NULL); 150 if (c == -1) 151 break; 152 153 switch (c) { 154 case 'h': 155 print_usage_and_exit(false); 156 break; 157 158 case 'p': 159 printPath = true; 160 break; 161 162 default: 163 print_usage_and_exit(true); 164 break; 165 } 166 } 167 168 // The remaining arguments are the architecture and optionally the command 169 // to execute. 170 if (optind >= argc) 171 print_usage_and_exit(true); 172 const char* architecture = optind < argc ? argv[optind++] : NULL; 173 174 int commandArgCount = argc - optind; 175 const char* const* commandArgs = commandArgCount > 0 ? argv + optind : NULL; 176 177 if (printPath && commandArgs != NULL) 178 print_usage_and_exit(true); 179 180 // check the architecture 181 BStringList architectures; 182 status_t error = get_architectures(architectures); 183 if (error != B_OK) { 184 fprintf(stderr, "Error: Failed to get architectures: %s\n", 185 strerror(error)); 186 exit(1); 187 } 188 189 if (!architectures.HasString(architecture)) { 190 fprintf(stderr, "Error: Unsupported architecture \"%s\"\n", 191 architecture); 192 exit(1); 193 } 194 195 // get the new paths 196 BStringList paths; 197 compute_new_paths(architecture, paths); 198 199 BString pathVariableValue = paths.Join(":"); 200 if (!paths.IsEmpty() && pathVariableValue.IsEmpty()) 201 fprintf(stderr, "Error: Out of memory!\n"); 202 203 if (printPath) { 204 printf("%s\n", pathVariableValue.String()); 205 return 0; 206 } 207 208 // set PATH 209 if (setenv("PATH", pathVariableValue, 1) != 0) { 210 fprintf(stderr, "Error: Failed to set PATH: %s\n", strerror(errno)); 211 exit(1); 212 } 213 214 // if no command is given, get the user's shell 215 const char* shellCommand[2]; 216 if (commandArgs == NULL) { 217 struct passwd* pwd = getpwuid(geteuid()); 218 shellCommand[0] = pwd != NULL ? pwd->pw_shell : "/bin/sh"; 219 shellCommand[1] = NULL; 220 commandArgs = shellCommand; 221 commandArgCount = 1; 222 } 223 224 // exec the command 225 execvp(commandArgs[0], (char* const*)commandArgs); 226 227 fprintf(stderr, "Error: Executing \"%s\" failed: %s\n", commandArgs[0], 228 strerror(errno)); 229 return 1; 230 } 231