• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
FileDescriptor(int fd)48     explicit FileDescriptor(int fd) : mFd(fd) { }
49     FileDescriptor(const FileDescriptor&) = delete;
~FileDescriptor()50     ~FileDescriptor() {
51         if (mFd != -1) {
52             close(mFd);
53             mFd = -1;
54         }
55     }
get() const56     int get() const { return mFd; }
57     FileDescriptor& operator=(const FileDescriptor&) = delete;
58 private:
59     int mFd;
60 };
61 
62 class File {
63 public:
File(FILE * file)64     explicit File(FILE* file) : mFile(file) { }
65     File(const File&) = delete;
~File()66     ~File() {
67         if (mFile) {
68             ::fclose(mFile);
69             mFile = nullptr;
70         }
71     }
72 
get() const73     FILE* get() const { return mFile; }
74     File& operator=(const File&) = delete;
75 private:
76     FILE* mFile;
77 };
78 
printUsage(const char * program)79 static void printUsage(const char* program) {
80     LOGE("%s <namespace> <program> [options...]", program);
81 }
82 
isNumericString(const char * str)83 static bool isNumericString(const char* str) {
84     while (isdigit(*str)) {
85         ++str;
86     }
87     return *str == '\0';
88 }
89 
readNamespacePid(const char * ns)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 
setNetworkNamespace(const char * ns)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.
sncatf(char * buffer,size_t size,const char * fmt,...)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  */
execCommand(int argc,char ** argv)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  */
main(int argc,char * argv[])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