1 // Copyright 2015 Google Inc. All rights reserved 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // +build ignore 16 17 #include "fileutil.h" 18 19 #include <errno.h> 20 #include <fcntl.h> 21 #include <glob.h> 22 #include <limits.h> 23 #include <signal.h> 24 #include <sys/stat.h> 25 #include <sys/types.h> 26 #include <sys/wait.h> 27 #include <unistd.h> 28 #if defined(__APPLE__) 29 #include <mach-o/dyld.h> 30 #endif 31 32 #include <unordered_map> 33 34 #include "log.h" 35 #include "strutil.h" 36 37 bool Exists(StringPiece filename) { 38 CHECK(filename.size() < PATH_MAX); 39 struct stat st; 40 if (stat(filename.as_string().c_str(), &st) < 0) { 41 return false; 42 } 43 return true; 44 } 45 46 double GetTimestampFromStat(const struct stat& st) { 47 #if defined(__linux__) 48 return st.st_mtime + st.st_mtim.tv_nsec * 0.001 * 0.001 * 0.001; 49 #else 50 return st.st_mtime; 51 #endif 52 } 53 54 double GetTimestamp(StringPiece filename) { 55 CHECK(filename.size() < PATH_MAX); 56 struct stat st; 57 if (stat(filename.as_string().c_str(), &st) < 0) { 58 return -2.0; 59 } 60 return GetTimestampFromStat(st); 61 } 62 63 int RunCommand(const string& shell, 64 const string& shellflag, 65 const string& cmd, 66 RedirectStderr redirect_stderr, 67 string* s) { 68 const char* argv[] = {NULL, NULL, NULL, NULL}; 69 string cmd_with_shell; 70 if (shell[0] != '/' || shell.find_first_of(" $") != string::npos) { 71 string cmd_escaped = cmd; 72 EscapeShell(&cmd_escaped); 73 cmd_with_shell = shell + " " + shellflag + " \"" + cmd_escaped + "\""; 74 argv[0] = "/bin/sh"; 75 argv[1] = "-c"; 76 argv[2] = cmd_with_shell.c_str(); 77 } else { 78 // If the shell isn't complicated, we don't need to wrap in /bin/sh 79 argv[0] = shell.c_str(); 80 argv[1] = shellflag.c_str(); 81 argv[2] = cmd.c_str(); 82 } 83 84 int pipefd[2]; 85 if (pipe(pipefd) != 0) 86 PERROR("pipe failed"); 87 int pid; 88 if ((pid = vfork())) { 89 int status; 90 close(pipefd[1]); 91 while (true) { 92 int result = waitpid(pid, &status, WNOHANG); 93 if (result < 0) 94 PERROR("waitpid failed"); 95 96 while (true) { 97 char buf[4096]; 98 ssize_t r = HANDLE_EINTR(read(pipefd[0], buf, 4096)); 99 if (r < 0) 100 PERROR("read failed"); 101 if (r == 0) 102 break; 103 s->append(buf, buf + r); 104 } 105 106 if (result != 0) { 107 break; 108 } 109 } 110 close(pipefd[0]); 111 112 return status; 113 } else { 114 close(pipefd[0]); 115 if (redirect_stderr == RedirectStderr::STDOUT) { 116 if (dup2(pipefd[1], 2) < 0) 117 PERROR("dup2 failed"); 118 } else if (redirect_stderr == RedirectStderr::DEV_NULL) { 119 int fd = open("/dev/null", O_WRONLY); 120 if (dup2(fd, 2) < 0) 121 PERROR("dup2 failed"); 122 close(fd); 123 } 124 if (dup2(pipefd[1], 1) < 0) 125 PERROR("dup2 failed"); 126 close(pipefd[1]); 127 128 execvp(argv[0], const_cast<char**>(argv)); 129 PLOG("execvp for %s failed", argv[0]); 130 kill(getppid(), SIGTERM); 131 _exit(1); 132 } 133 } 134 135 void GetExecutablePath(string* path) { 136 #if defined(__linux__) 137 char mypath[PATH_MAX + 1]; 138 ssize_t l = readlink("/proc/self/exe", mypath, PATH_MAX); 139 if (l < 0) { 140 PERROR("readlink for /proc/self/exe"); 141 } 142 mypath[l] = '\0'; 143 *path = mypath; 144 #elif defined(__APPLE__) 145 char mypath[PATH_MAX + 1]; 146 uint32_t size = PATH_MAX; 147 if (_NSGetExecutablePath(mypath, &size) != 0) { 148 ERROR("_NSGetExecutablePath failed"); 149 } 150 mypath[size] = 0; 151 *path = mypath; 152 #else 153 #error "Unsupported OS" 154 #endif 155 } 156 157 namespace { 158 159 class GlobCache { 160 public: 161 ~GlobCache() { Clear(); } 162 163 void Get(const char* pat, vector<string>** files) { 164 auto p = cache_.emplace(pat, nullptr); 165 if (p.second) { 166 vector<string>* files = p.first->second = new vector<string>; 167 if (strcspn(pat, "?*[\\") != strlen(pat)) { 168 glob_t gl; 169 glob(pat, 0, NULL, &gl); 170 for (size_t i = 0; i < gl.gl_pathc; i++) { 171 files->push_back(gl.gl_pathv[i]); 172 } 173 globfree(&gl); 174 } else { 175 if (Exists(pat)) 176 files->push_back(pat); 177 } 178 } 179 *files = p.first->second; 180 } 181 182 const unordered_map<string, vector<string>*>& GetAll() const { 183 return cache_; 184 } 185 186 void Clear() { 187 for (auto& p : cache_) { 188 delete p.second; 189 } 190 cache_.clear(); 191 } 192 193 private: 194 unordered_map<string, vector<string>*> cache_; 195 }; 196 197 static GlobCache g_gc; 198 199 } // namespace 200 201 void Glob(const char* pat, vector<string>** files) { 202 g_gc.Get(pat, files); 203 } 204 205 const unordered_map<string, vector<string>*>& GetAllGlobCache() { 206 return g_gc.GetAll(); 207 } 208 209 void ClearGlobCache() { 210 g_gc.Clear(); 211 } 212