ipc/chromium/src/base/process_util_linux.cc

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 // Copyright (c) 2008 The Chromium Authors. All rights reserved.
     2 // Use of this source code is governed by a BSD-style license that can be
     3 // found in the LICENSE file.
     5 #include "base/process_util.h"
     7 #include <ctype.h>
     8 #include <dirent.h>
     9 #include <fcntl.h>
    10 #include <memory>
    11 #include <unistd.h>
    12 #include <string>
    13 #include <sys/types.h>
    14 #include <sys/wait.h>
    16 #include "base/debug_util.h"
    17 #include "base/eintr_wrapper.h"
    18 #include "base/file_util.h"
    19 #include "base/logging.h"
    20 #include "base/string_tokenizer.h"
    21 #include "base/string_util.h"
    23 #ifdef MOZ_WIDGET_GONK
    24 /*
    25  * AID_APP is the first application UID used by Android. We're using
    26  * it as our unprivilegied UID.  This ensure the UID used is not
    27  * shared with any other processes than our own childs.
    28  */
    29 # include <private/android_filesystem_config.h>
    30 # define CHILD_UNPRIVILEGED_UID AID_APP
    31 # define CHILD_UNPRIVILEGED_GID AID_APP
    32 #else
    33 /*
    34  * On platforms that are not gonk based, we fall back to an arbitrary
    35  * UID. This is generally the UID for user `nobody', albeit it is not
    36  * always the case.
    37  */
    38 # define CHILD_UNPRIVILEGED_UID 65534
    39 # define CHILD_UNPRIVILEGED_GID 65534
    40 #endif
    42 #ifdef ANDROID
    43 #include <pthread.h>
    44 /*
    45  * Currently, PR_DuplicateEnvironment is implemented in
    46  * mozglue/build/BionicGlue.cpp
    47  */
    48 #define HAVE_PR_DUPLICATE_ENVIRONMENT
    50 #include "plstr.h"
    51 #include "prenv.h"
    52 #include "prmem.h"
    53 /* Temporary until we have PR_DuplicateEnvironment in prenv.h */
    54 extern "C" { NSPR_API(pthread_mutex_t *)PR_GetEnvLock(void); }
    55 #endif
    57 namespace {
    59 enum ParsingState {
    60   KEY_NAME,
    61   KEY_VALUE
    62 };
    64 static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG");
    66 }  // namespace
    68 namespace base {
    70 #ifdef HAVE_PR_DUPLICATE_ENVIRONMENT
    71 /* 
    72  * I tried to put PR_DuplicateEnvironment down in mozglue, but on android 
    73  * this winds up failing because the strdup/free calls wind up mismatching. 
    74  */
    76 static char **
    77 PR_DuplicateEnvironment(void)
    78 {
    79     char **result = NULL;
    80     char **s;
    81     char **d;
    82     pthread_mutex_lock(PR_GetEnvLock());
    83     for (s = environ; *s != NULL; s++)
    84       ;
    85     if ((result = (char **)PR_Malloc(sizeof(char *) * (s - environ + 1))) != NULL) {
    86       for (s = environ, d = result; *s != NULL; s++, d++) {
    87         *d = PL_strdup(*s);
    88       }
    89       *d = NULL;
    90     }
    91     pthread_mutex_unlock(PR_GetEnvLock());
    92     return result;
    93 }
    95 class EnvironmentEnvp
    96 {
    97 public:
    98   EnvironmentEnvp()
    99     : mEnvp(PR_DuplicateEnvironment()) {}
   101   EnvironmentEnvp(const environment_map &em)
   102   {
   103     mEnvp = (char **)PR_Malloc(sizeof(char *) * (em.size() + 1));
   104     if (!mEnvp) {
   105       return;
   106     }
   107     char **e = mEnvp;
   108     for (environment_map::const_iterator it = em.begin();
   109          it != em.end(); ++it, ++e) {
   110       std::string str = it->first;
   111       str += "=";
   112       str += it->second;
   113       *e = PL_strdup(str.c_str());
   114     }
   115     *e = NULL;
   116   }
   118   ~EnvironmentEnvp()
   119   {
   120     if (!mEnvp) {
   121       return;
   122     }
   123     for (char **e = mEnvp; *e; ++e) {
   124       PL_strfree(*e);
   125     }
   126     PR_Free(mEnvp);
   127   }
   129   char * const *AsEnvp() { return mEnvp; }
   131   void ToMap(environment_map &em)
   132   {
   133     if (!mEnvp) {
   134       return;
   135     }
   136     em.clear();
   137     for (char **e = mEnvp; *e; ++e) {
   138       const char *eq;
   139       if ((eq = strchr(*e, '=')) != NULL) {
   140         std::string varname(*e, eq - *e);
   141         em[varname.c_str()] = &eq[1];
   142       }
   143     }
   144   }
   146 private:
   147   char **mEnvp;
   148 };
   150 class Environment : public environment_map
   151 {
   152 public:
   153   Environment()
   154   {
   155     EnvironmentEnvp envp;
   156     envp.ToMap(*this);
   157   }
   159   char * const *AsEnvp() {
   160     mEnvp.reset(new EnvironmentEnvp(*this));
   161     return mEnvp->AsEnvp();
   162   }
   164   void Merge(const environment_map &em)
   165   {
   166     for (const_iterator it = em.begin(); it != em.end(); ++it) {
   167       (*this)[it->first] = it->second;
   168     }
   169   }
   170 private:
   171   std::auto_ptr<EnvironmentEnvp> mEnvp;
   172 };
   173 #endif // HAVE_PR_DUPLICATE_ENVIRONMENT
   175 bool LaunchApp(const std::vector<std::string>& argv,
   176                const file_handle_mapping_vector& fds_to_remap,
   177                bool wait, ProcessHandle* process_handle) {
   178   return LaunchApp(argv, fds_to_remap, environment_map(),
   179                    wait, process_handle);
   180 }
   182 bool LaunchApp(const std::vector<std::string>& argv,
   183                const file_handle_mapping_vector& fds_to_remap,
   184                const environment_map& env_vars_to_set,
   185                bool wait, ProcessHandle* process_handle,
   186                ProcessArchitecture arch) {
   187   return LaunchApp(argv, fds_to_remap, env_vars_to_set,
   188                    PRIVILEGES_INHERIT,
   189                    wait, process_handle);
   190 }
   192 bool LaunchApp(const std::vector<std::string>& argv,
   193                const file_handle_mapping_vector& fds_to_remap,
   194                const environment_map& env_vars_to_set,
   195                ChildPrivileges privs,
   196                bool wait, ProcessHandle* process_handle,
   197                ProcessArchitecture arch) {
   198   scoped_array<char*> argv_cstr(new char*[argv.size() + 1]);
   199   // Illegal to allocate memory after fork and before execvp
   200   InjectiveMultimap fd_shuffle1, fd_shuffle2;
   201   fd_shuffle1.reserve(fds_to_remap.size());
   202   fd_shuffle2.reserve(fds_to_remap.size());
   204 #ifdef HAVE_PR_DUPLICATE_ENVIRONMENT
   205   Environment env;
   206   env.Merge(env_vars_to_set);
   207   char * const *envp = env.AsEnvp();
   208   if (!envp) {
   209     DLOG(ERROR) << "FAILED to duplicate environment for: " << argv_cstr[0];
   210     return false;
   211   }
   212 #endif
   214   pid_t pid = fork();
   215   if (pid < 0)
   216     return false;
   218   if (pid == 0) {
   219     for (file_handle_mapping_vector::const_iterator
   220         it = fds_to_remap.begin(); it != fds_to_remap.end(); ++it) {
   221       fd_shuffle1.push_back(InjectionArc(it->first, it->second, false));
   222       fd_shuffle2.push_back(InjectionArc(it->first, it->second, false));
   223     }
   225     if (!ShuffleFileDescriptors(&fd_shuffle1))
   226       _exit(127);
   228     CloseSuperfluousFds(fd_shuffle2);
   230     for (size_t i = 0; i < argv.size(); i++)
   231       argv_cstr[i] = const_cast<char*>(argv[i].c_str());
   232     argv_cstr[argv.size()] = NULL;
   234     SetCurrentProcessPrivileges(privs);
   236 #ifdef HAVE_PR_DUPLICATE_ENVIRONMENT
   237     execve(argv_cstr[0], argv_cstr.get(), envp);
   238 #else
   239     for (environment_map::const_iterator it = env_vars_to_set.begin();
   240          it != env_vars_to_set.end(); ++it) {
   241       if (setenv(it->first.c_str(), it->second.c_str(), 1/*overwrite*/))
   242         _exit(127);
   243     }
   244     execv(argv_cstr[0], argv_cstr.get());
   245 #endif
   246     // if we get here, we're in serious trouble and should complain loudly
   247     DLOG(ERROR) << "FAILED TO exec() CHILD PROCESS, path: " << argv_cstr[0];
   248     _exit(127);
   249   } else {
   250     gProcessLog.print("==> process %d launched child process %d\n",
   251                       GetCurrentProcId(), pid);
   252     if (wait)
   253       HANDLE_EINTR(waitpid(pid, 0, 0));
   255     if (process_handle)
   256       *process_handle = pid;
   257   }
   259   return true;
   260 }
   262 bool LaunchApp(const CommandLine& cl,
   263                bool wait, bool start_hidden,
   264                ProcessHandle* process_handle) {
   265   file_handle_mapping_vector no_files;
   266   return LaunchApp(cl.argv(), no_files, wait, process_handle);
   267 }
   269 void SetCurrentProcessPrivileges(ChildPrivileges privs) {
   270   if (privs == PRIVILEGES_INHERIT) {
   271     return;
   272   }
   274   gid_t gid = CHILD_UNPRIVILEGED_GID;
   275   uid_t uid = CHILD_UNPRIVILEGED_UID;
   276 #ifdef MOZ_WIDGET_GONK
   277   {
   278     static bool checked_pix_max, pix_max_ok;
   279     if (!checked_pix_max) {
   280       checked_pix_max = true;
   281       int fd = open("/proc/sys/kernel/pid_max", O_CLOEXEC | O_RDONLY);
   282       if (fd < 0) {
   283         DLOG(ERROR) << "Failed to open pid_max";
   284         _exit(127);
   285       }
   286       char buf[PATH_MAX];
   287       ssize_t len = read(fd, buf, sizeof(buf) - 1);
   288       close(fd);
   289       if (len < 0) {
   290         DLOG(ERROR) << "Failed to read pid_max";
   291         _exit(127);
   292       }
   293       buf[len] = '\0';
   294       int pid_max = atoi(buf);
   295       pix_max_ok =
   296         (pid_max + CHILD_UNPRIVILEGED_UID > CHILD_UNPRIVILEGED_UID);
   297     }
   298     if (!pix_max_ok) {
   299       DLOG(ERROR) << "Can't safely get unique uid/gid";
   300       _exit(127);
   301     }
   302     gid += getpid();
   303     uid += getpid();
   304   }
   305 #endif
   306   if (setgid(gid) != 0) {
   307     DLOG(ERROR) << "FAILED TO setgid() CHILD PROCESS";
   308     _exit(127);
   309   }
   310   if (setuid(uid) != 0) {
   311     DLOG(ERROR) << "FAILED TO setuid() CHILD PROCESS";
   312     _exit(127);
   313   }
   314   if (chdir("/") != 0)
   315     gProcessLog.print("==> could not chdir()\n");
   316 }
   318 NamedProcessIterator::NamedProcessIterator(const std::wstring& executable_name,
   319                                            const ProcessFilter* filter)
   320     : executable_name_(executable_name), filter_(filter) {
   321   procfs_dir_ = opendir("/proc");
   322 }
   324 NamedProcessIterator::~NamedProcessIterator() {
   325   if (procfs_dir_) {
   326     closedir(procfs_dir_);
   327     procfs_dir_ = NULL;
   328   }
   329 }
   331 const ProcessEntry* NamedProcessIterator::NextProcessEntry() {
   332   bool result = false;
   333   do {
   334     result = CheckForNextProcess();
   335   } while (result && !IncludeEntry());
   337   if (result)
   338     return &entry_;
   340   return NULL;
   341 }
   343 bool NamedProcessIterator::CheckForNextProcess() {
   344   // TODO(port): skip processes owned by different UID
   346   dirent* slot = 0;
   347   const char* openparen;
   348   const char* closeparen;
   350   // Arbitrarily guess that there will never be more than 200 non-process
   351   // files in /proc.  Hardy has 53.
   352   int skipped = 0;
   353   const int kSkipLimit = 200;
   354   while (skipped < kSkipLimit) {
   355     slot = readdir(procfs_dir_);
   356     // all done looking through /proc?
   357     if (!slot)
   358       return false;
   360     // If not a process, keep looking for one.
   361     bool notprocess = false;
   362     int i;
   363     for (i = 0; i < NAME_MAX && slot->d_name[i]; ++i) {
   364        if (!isdigit(slot->d_name[i])) {
   365          notprocess = true;
   366          break;
   367        }
   368     }
   369     if (i == NAME_MAX || notprocess) {
   370       skipped++;
   371       continue;
   372     }
   374     // Read the process's status.
   375     char buf[NAME_MAX + 12];
   376     sprintf(buf, "/proc/%s/stat", slot->d_name);
   377     FILE *fp = fopen(buf, "r");
   378     if (!fp)
   379       return false;
   380     const char* result = fgets(buf, sizeof(buf), fp);
   381     fclose(fp);
   382     if (!result)
   383       return false;
   385     // Parse the status.  It is formatted like this:
   386     // %d (%s) %c %d ...
   387     // pid (name) runstate ppid
   388     // To avoid being fooled by names containing a closing paren, scan
   389     // backwards.
   390     openparen = strchr(buf, '(');
   391     closeparen = strrchr(buf, ')');
   392     if (!openparen || !closeparen)
   393       return false;
   394     char runstate = closeparen[2];
   396     // Is the process in 'Zombie' state, i.e. dead but waiting to be reaped?
   397     // Allowed values: D R S T Z
   398     if (runstate != 'Z')
   399       break;
   401     // Nope, it's a zombie; somebody isn't cleaning up after their children.
   402     // (e.g. WaitForProcessesToExit doesn't clean up after dead children yet.)
   403     // There could be a lot of zombies, can't really decrement i here.
   404   }
   405   if (skipped >= kSkipLimit) {
   406     NOTREACHED();
   407     return false;
   408   }
   410   entry_.pid = atoi(slot->d_name);
   411   entry_.ppid = atoi(closeparen + 3);
   413   // TODO(port): read pid's commandline's $0, like killall does.  Using the
   414   // short name between openparen and closeparen won't work for long names!
   415   int len = closeparen - openparen - 1;
   416   if (len > NAME_MAX)
   417     len = NAME_MAX;
   418   memcpy(entry_.szExeFile, openparen + 1, len);
   419   entry_.szExeFile[len] = 0;
   421   return true;
   422 }
   424 bool NamedProcessIterator::IncludeEntry() {
   425   // TODO(port): make this also work for non-ASCII filenames
   426   if (WideToASCII(executable_name_) != entry_.szExeFile)
   427     return false;
   428   if (!filter_)
   429     return true;
   430   return filter_->Includes(entry_.pid, entry_.ppid);
   431 }
   433 }  // namespace base

mercurial