Home | History | Annotate | Download | only in kati
      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