1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #define LOG_TAG "execns" 18 #include <log/log.h> 19 20 #include <errno.h> 21 #include <fcntl.h> 22 #include <sched.h> 23 #include <stdio.h> 24 #include <string.h> 25 #include <unistd.h> 26 #include <sys/stat.h> 27 #include <sys/types.h> 28 29 #include <string> 30 #include <vector> 31 32 static bool isTerminal = false; 33 // Print errors to stderr if running from a terminal, otherwise print to logcat 34 // This is useful for debugging from a terminal 35 #define LOGE(...) do { \ 36 if (isTerminal) { \ 37 fprintf(stderr, __VA_ARGS__); \ 38 fprintf(stderr, "\n"); \ 39 } else { \ 40 ALOGE(__VA_ARGS__); \ 41 } \ 42 } while (0) 43 44 static const char kNetNsDir[] = "/data/vendor/var/run/netns"; 45 46 class FileDescriptor { 47 public: 48 explicit FileDescriptor(int fd) : mFd(fd) { } 49 FileDescriptor(const FileDescriptor&) = delete; 50 ~FileDescriptor() { 51 if (mFd != -1) { 52 close(mFd); 53 mFd = -1; 54 } 55 } 56 int get() const { return mFd; } 57 FileDescriptor& operator=(const FileDescriptor&) = delete; 58 private: 59 int mFd; 60 }; 61 62 class File { 63 public: 64 explicit File(FILE* file) : mFile(file) { } 65 File(const File&) = delete; 66 ~File() { 67 if (mFile) { 68 ::fclose(mFile); 69 mFile = nullptr; 70 } 71 } 72 73 FILE* get() const { return mFile; } 74 File& operator=(const File&) = delete; 75 private: 76 FILE* mFile; 77 }; 78 79 static void printUsage(const char* program) { 80 LOGE("%s <namespace> <program> [options...]", program); 81 } 82 83 static bool isNumericString(const char* str) { 84 while (isdigit(*str)) { 85 ++str; 86 } 87 return *str == '\0'; 88 } 89 90 static std::string readNamespacePid(const char* ns) { 91 char nsPath[PATH_MAX]; 92 snprintf(nsPath, sizeof(nsPath), "%s/%s.pid", kNetNsDir, ns); 93 94 File file(::fopen(nsPath, "r")); 95 if (file.get() == nullptr) { 96 LOGE("Unable to open file %s for namespace %s: %s", 97 nsPath, ns, strerror(errno)); 98 return std::string(); 99 } 100 101 char buffer[32]; 102 size_t bytesRead = ::fread(buffer, 1, sizeof(buffer), file.get()); 103 if (bytesRead < sizeof(buffer) && feof(file.get())) { 104 // Reached end-of-file, null-terminate 105 buffer[bytesRead] = '\0'; 106 if (isNumericString(buffer)) { 107 // File is valid and contains a number, return it 108 return buffer; 109 } 110 LOGE("File %s does not contain a valid pid '%s'", nsPath, buffer); 111 } else if (ferror(file.get())) { 112 LOGE("Error reading from file %s: %s", nsPath, strerror(errno)); 113 } else { 114 LOGE("Invalid contents of pid file %s", nsPath); 115 } 116 return std::string(); 117 } 118 119 static bool setNetworkNamespace(const char* ns) { 120 // There is a file in the net namespace dir (/data/vendor/var/run/netns) with 121 // the name "<namespace>.pid". This file contains the pid of the createns 122 // process that created the namespace. 123 // 124 // To switch network namespace we're going to call setns which requires an 125 // open file descriptor to /proc/<pid>/ns/net where <pid> refers to a 126 // process already running in that namespace. So using the pid from the file 127 // above we can determine which path to use. 128 std::string pid = readNamespacePid(ns); 129 if (pid.empty()) { 130 return false; 131 } 132 char nsPath[PATH_MAX]; 133 snprintf(nsPath, sizeof(nsPath), "/proc/%s/ns/net", pid.c_str()); 134 135 FileDescriptor nsFd(open(nsPath, O_RDONLY | O_CLOEXEC)); 136 if (nsFd.get() == -1) { 137 LOGE("Cannot open network namespace '%s' at '%s': %s", 138 ns, nsPath, strerror(errno)); 139 return false; 140 } 141 142 if (setns(nsFd.get(), CLONE_NEWNET) == -1) { 143 LOGE("Cannot set network namespace '%s': %s", 144 ns, strerror(errno)); 145 return false; 146 } 147 return true; 148 } 149 150 // Append a formatted string to the end of |buffer|. The total size in |buffer| 151 // is |size|, including any existing string data. The string to append is 152 // specified by |fmt| and any additional arguments required by the format 153 // string. If the function fails it returns -1, otherwise it returns the number 154 // of characters printed (excluding the terminating NULL). On success the 155 // string is always null-terminated. 156 static int sncatf(char* buffer, size_t size, const char* fmt, ...) { 157 size_t len = strnlen(buffer, size); 158 if (len >= size) { 159 // The length exceeds the available size, if len == size then there is 160 // also a terminating null after len bytes which would then be outside 161 // the provided buffer. 162 return -1; 163 } 164 165 va_list args; 166 va_start(args, fmt); 167 int printed = vsnprintf(buffer + len, size - len, fmt, args); 168 buffer[size - 1] = '\0'; 169 va_end(args); 170 return printed; 171 } 172 173 /** 174 * Execute a given |command| with |argc| number of parameters that are located 175 * in |argv|. The first parameter in |argv| is the command that should be run 176 * followed by its arguments. 177 */ 178 static int execCommand( int argc, char** argv) { 179 if (argc <= 0 || argv == nullptr || argv[0] == nullptr) { 180 LOGE("No command specified"); 181 return 1; 182 } 183 184 std::vector<char*> arguments; 185 // Place all the arguments in the vector and the terminating null 186 arguments.insert(arguments.begin(), argv, argv + argc); 187 arguments.push_back(nullptr); 188 189 char buffer[4096]; 190 if (execvp(argv[0], arguments.data()) == -1) { 191 // Save errno in case it gets changed by printing stuff. 192 int error = errno; 193 int printed = snprintf(buffer, sizeof(buffer), 194 "Could not execute command '%s", argv[0]); 195 if (printed < 0) { 196 LOGE("Could not execute command: %s", strerror(error)); 197 return error; 198 } 199 for (int i = 1; i < argc; ++i) { 200 // Be nice to the user and print quotes if there are spaces to 201 // indicate how we saw it. If there are already single quotes in 202 // there confusion will ensue. 203 if (strchr(argv[i], ' ')) { 204 sncatf(buffer, sizeof(buffer), " \"%s\"", argv[i]); 205 } else { 206 sncatf(buffer, sizeof(buffer), " %s", argv[i]); 207 } 208 } 209 sncatf(buffer, sizeof(buffer), "': %s", strerror(error)); 210 LOGE("%s", buffer); 211 return error; 212 } 213 // execvp never returns unless it fails so this is just to return something. 214 return 0; 215 } 216 217 /** 218 * Enter a given network namespace argv[1] and execute command argv[2] with 219 * options argv[3..argc-1] in that namespace. 220 */ 221 int main(int argc, char* argv[]) { 222 isTerminal = isatty(STDOUT_FILENO) != 0; 223 if (argc < 3) { 224 printUsage(argv[0]); 225 return 1; 226 } 227 228 // First set the new network namespace for this process 229 if (!setNetworkNamespace(argv[1])) { 230 return 1; 231 } 232 233 // Now run the command with all the remaining parameters 234 return execCommand(argc - 2, &argv[2]); 235 } 236 237