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 & cmd,RedirectStderr redirect_stderr,string * s)63 int RunCommand(const string& shell, const string& cmd,
64                RedirectStderr redirect_stderr,
65                string* s) {
66   string cmd_escaped = cmd;
67   EscapeShell(&cmd_escaped);
68   string cmd_with_shell = shell + " -c \"" + cmd_escaped + "\"";
69   const char* argv[] = {
70     "/bin/sh", "-c", cmd_with_shell.c_str(), NULL
71   };
72 
73   int pipefd[2];
74   if (pipe(pipefd) != 0)
75     PERROR("pipe failed");
76   int pid;
77   if ((pid = vfork())) {
78     int status;
79     close(pipefd[1]);
80     while (true) {
81       int result = waitpid(pid, &status, WNOHANG);
82       if (result < 0)
83         PERROR("waitpid failed");
84 
85       while (true) {
86         char buf[4096];
87         ssize_t r = read(pipefd[0], buf, 4096);
88         if (r < 0)
89           PERROR("read failed");
90         if (r == 0)
91           break;
92         s->append(buf, buf+r);
93       }
94 
95       if (result != 0) {
96         break;
97       }
98     }
99     close(pipefd[0]);
100 
101     return status;
102   } else {
103     close(pipefd[0]);
104     if (redirect_stderr == RedirectStderr::STDOUT) {
105       if (dup2(pipefd[1], 2) < 0)
106         PERROR("dup2 failed");
107     } else if (redirect_stderr == RedirectStderr::DEV_NULL) {
108       int fd = open("/dev/null", O_WRONLY);
109       if (dup2(fd, 2) < 0)
110         PERROR("dup2 failed");
111       close(fd);
112     }
113     if (dup2(pipefd[1], 1) < 0)
114       PERROR("dup2 failed");
115     close(pipefd[1]);
116 
117     execvp(argv[0], const_cast<char**>(argv));
118     PLOG("execvp for %s failed", argv[0]);
119     kill(getppid(), SIGTERM);
120     _exit(1);
121   }
122 }
123 
GetExecutablePath(string * path)124 void GetExecutablePath(string* path) {
125 #if defined(__linux__)
126   char mypath[PATH_MAX + 1];
127   ssize_t l = readlink("/proc/self/exe", mypath, PATH_MAX);
128   if (l < 0) {
129     PERROR("readlink for /proc/self/exe");
130   }
131   mypath[l] = '\0';
132   *path = mypath;
133 #elif defined(__APPLE__)
134   char mypath[PATH_MAX + 1];
135   uint32_t size = PATH_MAX;
136   if (_NSGetExecutablePath(mypath, &size) != 0) {
137     ERROR("_NSGetExecutablePath failed");
138   }
139   mypath[size] = 0;
140   *path = mypath;
141 #else
142 #error "Unsupported OS"
143 #endif
144 }
145 
146 namespace {
147 
148 class GlobCache {
149  public:
~GlobCache()150   ~GlobCache() {
151     Clear();
152   }
153 
Get(const char * pat,vector<string> ** files)154   void Get(const char* pat, vector<string>** files) {
155     auto p = cache_.emplace(pat, nullptr);
156     if (p.second) {
157       vector<string>* files = p.first->second = new vector<string>;
158       if (strcspn(pat, "?*[\\") != strlen(pat)) {
159         glob_t gl;
160         glob(pat, GLOB_NOSORT, NULL, &gl);
161         for (size_t i = 0; i < gl.gl_pathc; i++) {
162           files->push_back(gl.gl_pathv[i]);
163         }
164         globfree(&gl);
165       } else {
166         if (Exists(pat))
167           files->push_back(pat);
168       }
169     }
170     *files = p.first->second;
171   }
172 
GetAll() const173   const unordered_map<string, vector<string>*>& GetAll() const {
174     return cache_;
175   }
176 
Clear()177   void Clear() {
178     for (auto& p : cache_) {
179       delete p.second;
180     }
181     cache_.clear();
182   }
183 
184  private:
185   unordered_map<string, vector<string>*> cache_;
186 };
187 
188 static GlobCache g_gc;
189 
190 }  // namespace
191 
Glob(const char * pat,vector<string> ** files)192 void Glob(const char* pat, vector<string>** files) {
193   g_gc.Get(pat, files);
194 }
195 
GetAllGlobCache()196 const unordered_map<string, vector<string>*>& GetAllGlobCache() {
197   return g_gc.GetAll();
198 }
199 
ClearGlobCache()200 void ClearGlobCache() {
201   g_gc.Clear();
202 }
203