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, 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