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, const string& shellflag, 64 const string& cmd, RedirectStderr redirect_stderr, 65 string* s) { 66 const char* argv[] = { NULL, NULL, NULL, NULL }; 67 string cmd_with_shell; 68 if (shell[0] != '/' || shell.find_first_of(" $") != string::npos) { 69 string cmd_escaped = cmd; 70 EscapeShell(&cmd_escaped); 71 cmd_with_shell = shell + " " + shellflag + " \"" + cmd_escaped + "\""; 72 argv[0] = "/bin/sh"; 73 argv[1] = "-c"; 74 argv[2] = cmd_with_shell.c_str(); 75 } else { 76 // If the shell isn't complicated, we don't need to wrap in /bin/sh 77 argv[0] = shell.c_str(); 78 argv[1] = shellflag.c_str(); 79 argv[2] = cmd.c_str(); 80 } 81 82 int pipefd[2]; 83 if (pipe(pipefd) != 0) 84 PERROR("pipe failed"); 85 int pid; 86 if ((pid = vfork())) { 87 int status; 88 close(pipefd[1]); 89 while (true) { 90 int result = waitpid(pid, &status, WNOHANG); 91 if (result < 0) 92 PERROR("waitpid failed"); 93 94 while (true) { 95 char buf[4096]; 96 ssize_t r = HANDLE_EINTR(read(pipefd[0], buf, 4096)); 97 if (r < 0) 98 PERROR("read failed"); 99 if (r == 0) 100 break; 101 s->append(buf, buf+r); 102 } 103 104 if (result != 0) { 105 break; 106 } 107 } 108 close(pipefd[0]); 109 110 return status; 111 } else { 112 close(pipefd[0]); 113 if (redirect_stderr == RedirectStderr::STDOUT) { 114 if (dup2(pipefd[1], 2) < 0) 115 PERROR("dup2 failed"); 116 } else if (redirect_stderr == RedirectStderr::DEV_NULL) { 117 int fd = open("/dev/null", O_WRONLY); 118 if (dup2(fd, 2) < 0) 119 PERROR("dup2 failed"); 120 close(fd); 121 } 122 if (dup2(pipefd[1], 1) < 0) 123 PERROR("dup2 failed"); 124 close(pipefd[1]); 125 126 execvp(argv[0], const_cast<char**>(argv)); 127 PLOG("execvp for %s failed", argv[0]); 128 kill(getppid(), SIGTERM); 129 _exit(1); 130 } 131 } 132 133 void GetExecutablePath(string* path) { 134 #if defined(__linux__) 135 char mypath[PATH_MAX + 1]; 136 ssize_t l = readlink("/proc/self/exe", mypath, PATH_MAX); 137 if (l < 0) { 138 PERROR("readlink for /proc/self/exe"); 139 } 140 mypath[l] = '\0'; 141 *path = mypath; 142 #elif defined(__APPLE__) 143 char mypath[PATH_MAX + 1]; 144 uint32_t size = PATH_MAX; 145 if (_NSGetExecutablePath(mypath, &size) != 0) { 146 ERROR("_NSGetExecutablePath failed"); 147 } 148 mypath[size] = 0; 149 *path = mypath; 150 #else 151 #error "Unsupported OS" 152 #endif 153 } 154 155 namespace { 156 157 class GlobCache { 158 public: 159 ~GlobCache() { 160 Clear(); 161 } 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