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 
Exists(StringPiece filename)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 
GetTimestampFromStat(const struct stat & st)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 
GetTimestamp(StringPiece filename)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 
RunCommand(const string & shell,const string & shellflag,const string & cmd,RedirectStderr redirect_stderr,string * s)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 
GetExecutablePath(string * path)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:
~GlobCache()161   ~GlobCache() { Clear(); }
162 
Get(const char * pat,vector<string> ** files)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 
GetAll() const182   const unordered_map<string, vector<string>*>& GetAll() const {
183     return cache_;
184   }
185 
Clear()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 
Glob(const char * pat,vector<string> ** files)201 void Glob(const char* pat, vector<string>** files) {
202   g_gc.Get(pat, files);
203 }
204 
GetAllGlobCache()205 const unordered_map<string, vector<string>*>& GetAllGlobCache() {
206   return g_gc.GetAll();
207 }
208 
ClearGlobCache()209 void ClearGlobCache() {
210   g_gc.Clear();
211 }
212