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.

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

mercurial