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