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