ipc/chromium/src/base/process_util_linux.cc

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/ipc/chromium/src/base/process_util_linux.cc	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,433 @@
     1.4 +// Copyright (c) 2008 The Chromium Authors. All rights reserved.
     1.5 +// Use of this source code is governed by a BSD-style license that can be
     1.6 +// found in the LICENSE file.
     1.7 +
     1.8 +#include "base/process_util.h"
     1.9 +
    1.10 +#include <ctype.h>
    1.11 +#include <dirent.h>
    1.12 +#include <fcntl.h>
    1.13 +#include <memory>
    1.14 +#include <unistd.h>
    1.15 +#include <string>
    1.16 +#include <sys/types.h>
    1.17 +#include <sys/wait.h>
    1.18 +
    1.19 +#include "base/debug_util.h"
    1.20 +#include "base/eintr_wrapper.h"
    1.21 +#include "base/file_util.h"
    1.22 +#include "base/logging.h"
    1.23 +#include "base/string_tokenizer.h"
    1.24 +#include "base/string_util.h"
    1.25 +
    1.26 +#ifdef MOZ_WIDGET_GONK
    1.27 +/*
    1.28 + * AID_APP is the first application UID used by Android. We're using
    1.29 + * it as our unprivilegied UID.  This ensure the UID used is not
    1.30 + * shared with any other processes than our own childs.
    1.31 + */
    1.32 +# include <private/android_filesystem_config.h>
    1.33 +# define CHILD_UNPRIVILEGED_UID AID_APP
    1.34 +# define CHILD_UNPRIVILEGED_GID AID_APP
    1.35 +#else
    1.36 +/*
    1.37 + * On platforms that are not gonk based, we fall back to an arbitrary
    1.38 + * UID. This is generally the UID for user `nobody', albeit it is not
    1.39 + * always the case.
    1.40 + */
    1.41 +# define CHILD_UNPRIVILEGED_UID 65534
    1.42 +# define CHILD_UNPRIVILEGED_GID 65534
    1.43 +#endif
    1.44 +
    1.45 +#ifdef ANDROID
    1.46 +#include <pthread.h>
    1.47 +/*
    1.48 + * Currently, PR_DuplicateEnvironment is implemented in
    1.49 + * mozglue/build/BionicGlue.cpp
    1.50 + */
    1.51 +#define HAVE_PR_DUPLICATE_ENVIRONMENT
    1.52 +
    1.53 +#include "plstr.h"
    1.54 +#include "prenv.h"
    1.55 +#include "prmem.h"
    1.56 +/* Temporary until we have PR_DuplicateEnvironment in prenv.h */
    1.57 +extern "C" { NSPR_API(pthread_mutex_t *)PR_GetEnvLock(void); }
    1.58 +#endif
    1.59 +
    1.60 +namespace {
    1.61 +
    1.62 +enum ParsingState {
    1.63 +  KEY_NAME,
    1.64 +  KEY_VALUE
    1.65 +};
    1.66 +
    1.67 +static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG");
    1.68 +
    1.69 +}  // namespace
    1.70 +
    1.71 +namespace base {
    1.72 +
    1.73 +#ifdef HAVE_PR_DUPLICATE_ENVIRONMENT
    1.74 +/* 
    1.75 + * I tried to put PR_DuplicateEnvironment down in mozglue, but on android 
    1.76 + * this winds up failing because the strdup/free calls wind up mismatching. 
    1.77 + */
    1.78 +
    1.79 +static char **
    1.80 +PR_DuplicateEnvironment(void)
    1.81 +{
    1.82 +    char **result = NULL;
    1.83 +    char **s;
    1.84 +    char **d;
    1.85 +    pthread_mutex_lock(PR_GetEnvLock());
    1.86 +    for (s = environ; *s != NULL; s++)
    1.87 +      ;
    1.88 +    if ((result = (char **)PR_Malloc(sizeof(char *) * (s - environ + 1))) != NULL) {
    1.89 +      for (s = environ, d = result; *s != NULL; s++, d++) {
    1.90 +        *d = PL_strdup(*s);
    1.91 +      }
    1.92 +      *d = NULL;
    1.93 +    }
    1.94 +    pthread_mutex_unlock(PR_GetEnvLock());
    1.95 +    return result;
    1.96 +}
    1.97 +
    1.98 +class EnvironmentEnvp
    1.99 +{
   1.100 +public:
   1.101 +  EnvironmentEnvp()
   1.102 +    : mEnvp(PR_DuplicateEnvironment()) {}
   1.103 +
   1.104 +  EnvironmentEnvp(const environment_map &em)
   1.105 +  {
   1.106 +    mEnvp = (char **)PR_Malloc(sizeof(char *) * (em.size() + 1));
   1.107 +    if (!mEnvp) {
   1.108 +      return;
   1.109 +    }
   1.110 +    char **e = mEnvp;
   1.111 +    for (environment_map::const_iterator it = em.begin();
   1.112 +         it != em.end(); ++it, ++e) {
   1.113 +      std::string str = it->first;
   1.114 +      str += "=";
   1.115 +      str += it->second;
   1.116 +      *e = PL_strdup(str.c_str());
   1.117 +    }
   1.118 +    *e = NULL;
   1.119 +  }
   1.120 +
   1.121 +  ~EnvironmentEnvp()
   1.122 +  {
   1.123 +    if (!mEnvp) {
   1.124 +      return;
   1.125 +    }
   1.126 +    for (char **e = mEnvp; *e; ++e) {
   1.127 +      PL_strfree(*e);
   1.128 +    }
   1.129 +    PR_Free(mEnvp);
   1.130 +  }
   1.131 +
   1.132 +  char * const *AsEnvp() { return mEnvp; }
   1.133 +
   1.134 +  void ToMap(environment_map &em)
   1.135 +  {
   1.136 +    if (!mEnvp) {
   1.137 +      return;
   1.138 +    }
   1.139 +    em.clear();
   1.140 +    for (char **e = mEnvp; *e; ++e) {
   1.141 +      const char *eq;
   1.142 +      if ((eq = strchr(*e, '=')) != NULL) {
   1.143 +        std::string varname(*e, eq - *e);
   1.144 +        em[varname.c_str()] = &eq[1];
   1.145 +      }
   1.146 +    }
   1.147 +  }
   1.148 +
   1.149 +private:
   1.150 +  char **mEnvp;
   1.151 +};
   1.152 +
   1.153 +class Environment : public environment_map
   1.154 +{
   1.155 +public:
   1.156 +  Environment()
   1.157 +  {
   1.158 +    EnvironmentEnvp envp;
   1.159 +    envp.ToMap(*this);
   1.160 +  }
   1.161 +
   1.162 +  char * const *AsEnvp() {
   1.163 +    mEnvp.reset(new EnvironmentEnvp(*this));
   1.164 +    return mEnvp->AsEnvp();
   1.165 +  }
   1.166 +
   1.167 +  void Merge(const environment_map &em)
   1.168 +  {
   1.169 +    for (const_iterator it = em.begin(); it != em.end(); ++it) {
   1.170 +      (*this)[it->first] = it->second;
   1.171 +    }
   1.172 +  }
   1.173 +private:
   1.174 +  std::auto_ptr<EnvironmentEnvp> mEnvp;
   1.175 +};
   1.176 +#endif // HAVE_PR_DUPLICATE_ENVIRONMENT
   1.177 +
   1.178 +bool LaunchApp(const std::vector<std::string>& argv,
   1.179 +               const file_handle_mapping_vector& fds_to_remap,
   1.180 +               bool wait, ProcessHandle* process_handle) {
   1.181 +  return LaunchApp(argv, fds_to_remap, environment_map(),
   1.182 +                   wait, process_handle);
   1.183 +}
   1.184 +
   1.185 +bool LaunchApp(const std::vector<std::string>& argv,
   1.186 +               const file_handle_mapping_vector& fds_to_remap,
   1.187 +               const environment_map& env_vars_to_set,
   1.188 +               bool wait, ProcessHandle* process_handle,
   1.189 +               ProcessArchitecture arch) {
   1.190 +  return LaunchApp(argv, fds_to_remap, env_vars_to_set,
   1.191 +                   PRIVILEGES_INHERIT,
   1.192 +                   wait, process_handle);
   1.193 +}
   1.194 +
   1.195 +bool LaunchApp(const std::vector<std::string>& argv,
   1.196 +               const file_handle_mapping_vector& fds_to_remap,
   1.197 +               const environment_map& env_vars_to_set,
   1.198 +               ChildPrivileges privs,
   1.199 +               bool wait, ProcessHandle* process_handle,
   1.200 +               ProcessArchitecture arch) {
   1.201 +  scoped_array<char*> argv_cstr(new char*[argv.size() + 1]);
   1.202 +  // Illegal to allocate memory after fork and before execvp
   1.203 +  InjectiveMultimap fd_shuffle1, fd_shuffle2;
   1.204 +  fd_shuffle1.reserve(fds_to_remap.size());
   1.205 +  fd_shuffle2.reserve(fds_to_remap.size());
   1.206 +
   1.207 +#ifdef HAVE_PR_DUPLICATE_ENVIRONMENT
   1.208 +  Environment env;
   1.209 +  env.Merge(env_vars_to_set);
   1.210 +  char * const *envp = env.AsEnvp();
   1.211 +  if (!envp) {
   1.212 +    DLOG(ERROR) << "FAILED to duplicate environment for: " << argv_cstr[0];
   1.213 +    return false;
   1.214 +  }
   1.215 +#endif
   1.216 +
   1.217 +  pid_t pid = fork();
   1.218 +  if (pid < 0)
   1.219 +    return false;
   1.220 +
   1.221 +  if (pid == 0) {
   1.222 +    for (file_handle_mapping_vector::const_iterator
   1.223 +        it = fds_to_remap.begin(); it != fds_to_remap.end(); ++it) {
   1.224 +      fd_shuffle1.push_back(InjectionArc(it->first, it->second, false));
   1.225 +      fd_shuffle2.push_back(InjectionArc(it->first, it->second, false));
   1.226 +    }
   1.227 +
   1.228 +    if (!ShuffleFileDescriptors(&fd_shuffle1))
   1.229 +      _exit(127);
   1.230 +
   1.231 +    CloseSuperfluousFds(fd_shuffle2);
   1.232 +
   1.233 +    for (size_t i = 0; i < argv.size(); i++)
   1.234 +      argv_cstr[i] = const_cast<char*>(argv[i].c_str());
   1.235 +    argv_cstr[argv.size()] = NULL;
   1.236 +
   1.237 +    SetCurrentProcessPrivileges(privs);
   1.238 +
   1.239 +#ifdef HAVE_PR_DUPLICATE_ENVIRONMENT
   1.240 +    execve(argv_cstr[0], argv_cstr.get(), envp);
   1.241 +#else
   1.242 +    for (environment_map::const_iterator it = env_vars_to_set.begin();
   1.243 +         it != env_vars_to_set.end(); ++it) {
   1.244 +      if (setenv(it->first.c_str(), it->second.c_str(), 1/*overwrite*/))
   1.245 +        _exit(127);
   1.246 +    }
   1.247 +    execv(argv_cstr[0], argv_cstr.get());
   1.248 +#endif
   1.249 +    // if we get here, we're in serious trouble and should complain loudly
   1.250 +    DLOG(ERROR) << "FAILED TO exec() CHILD PROCESS, path: " << argv_cstr[0];
   1.251 +    _exit(127);
   1.252 +  } else {
   1.253 +    gProcessLog.print("==> process %d launched child process %d\n",
   1.254 +                      GetCurrentProcId(), pid);
   1.255 +    if (wait)
   1.256 +      HANDLE_EINTR(waitpid(pid, 0, 0));
   1.257 +
   1.258 +    if (process_handle)
   1.259 +      *process_handle = pid;
   1.260 +  }
   1.261 +
   1.262 +  return true;
   1.263 +}
   1.264 +
   1.265 +bool LaunchApp(const CommandLine& cl,
   1.266 +               bool wait, bool start_hidden,
   1.267 +               ProcessHandle* process_handle) {
   1.268 +  file_handle_mapping_vector no_files;
   1.269 +  return LaunchApp(cl.argv(), no_files, wait, process_handle);
   1.270 +}
   1.271 +
   1.272 +void SetCurrentProcessPrivileges(ChildPrivileges privs) {
   1.273 +  if (privs == PRIVILEGES_INHERIT) {
   1.274 +    return;
   1.275 +  }
   1.276 +
   1.277 +  gid_t gid = CHILD_UNPRIVILEGED_GID;
   1.278 +  uid_t uid = CHILD_UNPRIVILEGED_UID;
   1.279 +#ifdef MOZ_WIDGET_GONK
   1.280 +  {
   1.281 +    static bool checked_pix_max, pix_max_ok;
   1.282 +    if (!checked_pix_max) {
   1.283 +      checked_pix_max = true;
   1.284 +      int fd = open("/proc/sys/kernel/pid_max", O_CLOEXEC | O_RDONLY);
   1.285 +      if (fd < 0) {
   1.286 +        DLOG(ERROR) << "Failed to open pid_max";
   1.287 +        _exit(127);
   1.288 +      }
   1.289 +      char buf[PATH_MAX];
   1.290 +      ssize_t len = read(fd, buf, sizeof(buf) - 1);
   1.291 +      close(fd);
   1.292 +      if (len < 0) {
   1.293 +        DLOG(ERROR) << "Failed to read pid_max";
   1.294 +        _exit(127);
   1.295 +      }
   1.296 +      buf[len] = '\0';
   1.297 +      int pid_max = atoi(buf);
   1.298 +      pix_max_ok =
   1.299 +        (pid_max + CHILD_UNPRIVILEGED_UID > CHILD_UNPRIVILEGED_UID);
   1.300 +    }
   1.301 +    if (!pix_max_ok) {
   1.302 +      DLOG(ERROR) << "Can't safely get unique uid/gid";
   1.303 +      _exit(127);
   1.304 +    }
   1.305 +    gid += getpid();
   1.306 +    uid += getpid();
   1.307 +  }
   1.308 +#endif
   1.309 +  if (setgid(gid) != 0) {
   1.310 +    DLOG(ERROR) << "FAILED TO setgid() CHILD PROCESS";
   1.311 +    _exit(127);
   1.312 +  }
   1.313 +  if (setuid(uid) != 0) {
   1.314 +    DLOG(ERROR) << "FAILED TO setuid() CHILD PROCESS";
   1.315 +    _exit(127);
   1.316 +  }
   1.317 +  if (chdir("/") != 0)
   1.318 +    gProcessLog.print("==> could not chdir()\n");
   1.319 +}
   1.320 +
   1.321 +NamedProcessIterator::NamedProcessIterator(const std::wstring& executable_name,
   1.322 +                                           const ProcessFilter* filter)
   1.323 +    : executable_name_(executable_name), filter_(filter) {
   1.324 +  procfs_dir_ = opendir("/proc");
   1.325 +}
   1.326 +
   1.327 +NamedProcessIterator::~NamedProcessIterator() {
   1.328 +  if (procfs_dir_) {
   1.329 +    closedir(procfs_dir_);
   1.330 +    procfs_dir_ = NULL;
   1.331 +  }
   1.332 +}
   1.333 +
   1.334 +const ProcessEntry* NamedProcessIterator::NextProcessEntry() {
   1.335 +  bool result = false;
   1.336 +  do {
   1.337 +    result = CheckForNextProcess();
   1.338 +  } while (result && !IncludeEntry());
   1.339 +
   1.340 +  if (result)
   1.341 +    return &entry_;
   1.342 +
   1.343 +  return NULL;
   1.344 +}
   1.345 +
   1.346 +bool NamedProcessIterator::CheckForNextProcess() {
   1.347 +  // TODO(port): skip processes owned by different UID
   1.348 +
   1.349 +  dirent* slot = 0;
   1.350 +  const char* openparen;
   1.351 +  const char* closeparen;
   1.352 +
   1.353 +  // Arbitrarily guess that there will never be more than 200 non-process
   1.354 +  // files in /proc.  Hardy has 53.
   1.355 +  int skipped = 0;
   1.356 +  const int kSkipLimit = 200;
   1.357 +  while (skipped < kSkipLimit) {
   1.358 +    slot = readdir(procfs_dir_);
   1.359 +    // all done looking through /proc?
   1.360 +    if (!slot)
   1.361 +      return false;
   1.362 +
   1.363 +    // If not a process, keep looking for one.
   1.364 +    bool notprocess = false;
   1.365 +    int i;
   1.366 +    for (i = 0; i < NAME_MAX && slot->d_name[i]; ++i) {
   1.367 +       if (!isdigit(slot->d_name[i])) {
   1.368 +         notprocess = true;
   1.369 +         break;
   1.370 +       }
   1.371 +    }
   1.372 +    if (i == NAME_MAX || notprocess) {
   1.373 +      skipped++;
   1.374 +      continue;
   1.375 +    }
   1.376 +
   1.377 +    // Read the process's status.
   1.378 +    char buf[NAME_MAX + 12];
   1.379 +    sprintf(buf, "/proc/%s/stat", slot->d_name);
   1.380 +    FILE *fp = fopen(buf, "r");
   1.381 +    if (!fp)
   1.382 +      return false;
   1.383 +    const char* result = fgets(buf, sizeof(buf), fp);
   1.384 +    fclose(fp);
   1.385 +    if (!result)
   1.386 +      return false;
   1.387 +
   1.388 +    // Parse the status.  It is formatted like this:
   1.389 +    // %d (%s) %c %d ...
   1.390 +    // pid (name) runstate ppid
   1.391 +    // To avoid being fooled by names containing a closing paren, scan
   1.392 +    // backwards.
   1.393 +    openparen = strchr(buf, '(');
   1.394 +    closeparen = strrchr(buf, ')');
   1.395 +    if (!openparen || !closeparen)
   1.396 +      return false;
   1.397 +    char runstate = closeparen[2];
   1.398 +
   1.399 +    // Is the process in 'Zombie' state, i.e. dead but waiting to be reaped?
   1.400 +    // Allowed values: D R S T Z
   1.401 +    if (runstate != 'Z')
   1.402 +      break;
   1.403 +
   1.404 +    // Nope, it's a zombie; somebody isn't cleaning up after their children.
   1.405 +    // (e.g. WaitForProcessesToExit doesn't clean up after dead children yet.)
   1.406 +    // There could be a lot of zombies, can't really decrement i here.
   1.407 +  }
   1.408 +  if (skipped >= kSkipLimit) {
   1.409 +    NOTREACHED();
   1.410 +    return false;
   1.411 +  }
   1.412 +
   1.413 +  entry_.pid = atoi(slot->d_name);
   1.414 +  entry_.ppid = atoi(closeparen + 3);
   1.415 +
   1.416 +  // TODO(port): read pid's commandline's $0, like killall does.  Using the
   1.417 +  // short name between openparen and closeparen won't work for long names!
   1.418 +  int len = closeparen - openparen - 1;
   1.419 +  if (len > NAME_MAX)
   1.420 +    len = NAME_MAX;
   1.421 +  memcpy(entry_.szExeFile, openparen + 1, len);
   1.422 +  entry_.szExeFile[len] = 0;
   1.423 +
   1.424 +  return true;
   1.425 +}
   1.426 +
   1.427 +bool NamedProcessIterator::IncludeEntry() {
   1.428 +  // TODO(port): make this also work for non-ASCII filenames
   1.429 +  if (WideToASCII(executable_name_) != entry_.szExeFile)
   1.430 +    return false;
   1.431 +  if (!filter_)
   1.432 +    return true;
   1.433 +  return filter_->Includes(entry_.pid, entry_.ppid);
   1.434 +}
   1.435 +
   1.436 +}  // namespace base

mercurial