aboutsummaryrefslogtreecommitdiffstats
path: root/brillo/process.cc
blob: a70bfd7dc94041737936acf8412bab4285adf4f4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "brillo/process.h"

#include <fcntl.h>
#include <signal.h>
#include <stdint.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#include <map>
#include <memory>

#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/posix/eintr_wrapper.h>
#include <base/process/process_metrics.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include <base/time/time.h>

#ifndef __linux__
#define setresuid(_u1, _u2, _u3) setreuid(_u1, _u2)
#define setresgid(_g1, _g2, _g3) setregid(_g1, _g2)
#endif  // !__linux__

namespace brillo {

bool ReturnTrue() {
  return true;
}

Process::Process() {
}

Process::~Process() {
}

bool Process::ProcessExists(pid_t pid) {
  return base::DirectoryExists(
      base::FilePath(base::StringPrintf("/proc/%d", pid)));
}

ProcessImpl::ProcessImpl()
    : pid_(0),
      uid_(-1),
      gid_(-1),
      pre_exec_(base::Bind(&ReturnTrue)),
      search_path_(false),
      inherit_parent_signal_mask_(false),
      close_unused_file_descriptors_(false) {
}

ProcessImpl::~ProcessImpl() {
  Reset(0);
}

void ProcessImpl::AddArg(const std::string& arg) {
  arguments_.push_back(arg);
}

void ProcessImpl::RedirectInput(const std::string& input_file) {
  input_file_ = input_file;
}

void ProcessImpl::RedirectOutput(const std::string& output_file) {
  output_file_ = output_file;
}

void ProcessImpl::RedirectUsingPipe(int child_fd, bool is_input) {
  PipeInfo info;
  info.is_input_ = is_input;
  info.is_bound_ = false;
  pipe_map_[child_fd] = info;
}

void ProcessImpl::BindFd(int parent_fd, int child_fd) {
  PipeInfo info;
  info.is_bound_ = true;

  // info.child_fd_ is the 'child half' of the pipe, which gets dup2()ed into
  // place over child_fd. Since we already have the child we want to dup2() into
  // place, we can set info.child_fd_ to parent_fd and leave info.parent_fd_
  // invalid.
  info.child_fd_ = parent_fd;
  info.parent_fd_ = -1;
  pipe_map_[child_fd] = info;
}

void ProcessImpl::SetCloseUnusedFileDescriptors(bool close_unused_fds) {
  close_unused_file_descriptors_ = close_unused_fds;
}

void ProcessImpl::SetUid(uid_t uid) {
  uid_ = uid;
}

void ProcessImpl::SetGid(gid_t gid) {
  gid_ = gid;
}

void ProcessImpl::SetCapabilities(uint64_t /*capmask*/) {
  // No-op, since ProcessImpl does not support sandboxing.
  return;
}

void ProcessImpl::ApplySyscallFilter(const std::string& /*path*/) {
  // No-op, since ProcessImpl does not support sandboxing.
  return;
}

void ProcessImpl::EnterNewPidNamespace() {
  // No-op, since ProcessImpl does not support sandboxing.
  return;
}

void ProcessImpl::SetInheritParentSignalMask(bool inherit) {
  inherit_parent_signal_mask_ = inherit;
}

void ProcessImpl::SetPreExecCallback(const PreExecCallback& cb) {
  pre_exec_ = cb;
}

void ProcessImpl::SetSearchPath(bool search_path) {
  search_path_ = search_path;
}

int ProcessImpl::GetPipe(int child_fd) {
  PipeMap::iterator i = pipe_map_.find(child_fd);
  if (i == pipe_map_.end())
    return -1;
  else
    return i->second.parent_fd_;
}

bool ProcessImpl::PopulatePipeMap() {
  // Verify all target fds are already open.  With this assumption we
  // can be sure that the pipe fds created below do not overlap with
  // any of the target fds which simplifies how we dup2 to them.  Note
  // that multi-threaded code could close i->first between this loop
  // and the next.
  for (PipeMap::iterator i = pipe_map_.begin(); i != pipe_map_.end(); ++i) {
    struct stat stat_buffer;
    if (fstat(i->first, &stat_buffer) < 0) {
      int saved_errno = errno;
      LOG(ERROR) << "Unable to fstat fd " << i->first << ": " << saved_errno;
      return false;
    }
  }

  for (PipeMap::iterator i = pipe_map_.begin(); i != pipe_map_.end(); ++i) {
    if (i->second.is_bound_) {
      // already have a parent fd, and the child fd gets dup()ed later.
      continue;
    }
    int pipefds[2];
    if (pipe(pipefds) < 0) {
      int saved_errno = errno;
      LOG(ERROR) << "pipe call failed with: " << saved_errno;
      return false;
    }
    if (i->second.is_input_) {
      // pipe is an input from the prospective of the child.
      i->second.parent_fd_ = pipefds[1];
      i->second.child_fd_ = pipefds[0];
    } else {
      i->second.parent_fd_ = pipefds[0];
      i->second.child_fd_ = pipefds[1];
    }
  }
  return true;
}

bool ProcessImpl::IsFileDescriptorInPipeMap(int fd) const {
  for (const auto& pipe : pipe_map_) {
    if (fd == pipe.second.parent_fd_ ||
        fd == pipe.second.child_fd_ ||
        fd == pipe.first) {
      return true;
    }
  }
  return false;
}

void ProcessImpl::CloseUnusedFileDescriptors() {
  size_t max_fds = base::GetMaxFds();
  for (size_t i = 0; i < max_fds; i++) {
    const int fd = static_cast<int>(i);

    // Ignore STD file descriptors.
    if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO) {
      continue;
    }

    // Ignore file descriptors used by the PipeMap, they will be handled
    // by this process later on.
    if (IsFileDescriptorInPipeMap(fd)) {
      continue;
    }

    // Since we're just trying to close anything we can find,
    // ignore any error return values of close().
    IGNORE_EINTR(close(fd));
  }
}

bool ProcessImpl::Start() {
  // If no arguments are provided, fail.
  if (arguments_.empty()) {
    return false;
  }
  std::unique_ptr<char* []> argv =
      std::make_unique<char* []>(arguments_.size() + 1);

  for (size_t i = 0; i < arguments_.size(); ++i)
    argv[i] = const_cast<char*>(arguments_[i].c_str());

  argv[arguments_.size()] = nullptr;

  if (!PopulatePipeMap()) {
    LOG(ERROR) << "Failing to start because pipe creation failed";
    return false;
  }

  pid_t pid = fork();
  int saved_errno = errno;
  if (pid < 0) {
    LOG(ERROR) << "Fork failed: " << saved_errno;
    Reset(0);
    return false;
  }

  if (pid == 0) {
    // Executing inside the child process.
    // Close unused file descriptors.
    if (close_unused_file_descriptors_) {
      CloseUnusedFileDescriptors();
    }
    // Close parent's side of the child pipes. dup2 ours into place and
    // then close our ends.
    for (PipeMap::iterator i = pipe_map_.begin(); i != pipe_map_.end(); ++i) {
      if (i->second.parent_fd_ != -1)
        IGNORE_EINTR(close(i->second.parent_fd_));
      // If we want to bind a fd to the same fd in the child, we don't need to
      // close and dup2 it.
      if (i->second.child_fd_ == i->first)
        continue;
      HANDLE_EINTR(dup2(i->second.child_fd_, i->first));
    }
    // Defer the actual close() of the child fd until afterward; this lets the
    // same child fd be bound to multiple fds using BindFd. Don't close the fd
    // if it was bound to itself.
    for (PipeMap::iterator i = pipe_map_.begin(); i != pipe_map_.end(); ++i) {
      if (i->second.child_fd_ == i->first)
        continue;
      IGNORE_EINTR(close(i->second.child_fd_));
    }

    if (!input_file_.empty()) {
      int input_handle =
          HANDLE_EINTR(open(input_file_.c_str(),
                            O_RDONLY | O_NOFOLLOW | O_NOCTTY));
      if (input_handle < 0) {
        PLOG(ERROR) << "Could not open " << input_file_;
        // Avoid exit() to avoid atexit handlers from parent.
        _exit(kErrorExitStatus);
      }

      // It's possible input_handle is already stdin. But if not, we need
      // to dup into that file descriptor and close the original.
      if (input_handle != STDIN_FILENO) {
        if (HANDLE_EINTR(dup2(input_handle, STDIN_FILENO)) < 0) {
          PLOG(ERROR) << "Could not dup fd to stdin for " << input_file_;
          _exit(kErrorExitStatus);
        }
        IGNORE_EINTR(close(input_handle));
      }
    }

    if (!output_file_.empty()) {
      int output_handle = HANDLE_EINTR(open(
          output_file_.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW,
          0666));
      if (output_handle < 0) {
        PLOG(ERROR) << "Could not create " << output_file_;
        // Avoid exit() to avoid atexit handlers from parent.
        _exit(kErrorExitStatus);
      }
      HANDLE_EINTR(dup2(output_handle, STDOUT_FILENO));
      HANDLE_EINTR(dup2(output_handle, STDERR_FILENO));
      // Only close output_handle if it does not happen to be one of
      // the two standard file descriptors we are trying to redirect.
      if (output_handle != STDOUT_FILENO && output_handle != STDERR_FILENO) {
        IGNORE_EINTR(close(output_handle));
      }
    }
    if (gid_ != static_cast<gid_t>(-1) && setresgid(gid_, gid_, gid_) < 0) {
      int saved_errno = errno;
      LOG(ERROR) << "Unable to set GID to " << gid_ << ": " << saved_errno;
      _exit(kErrorExitStatus);
    }
    if (uid_ != static_cast<uid_t>(-1) && setresuid(uid_, uid_, uid_) < 0) {
      int saved_errno = errno;
      LOG(ERROR) << "Unable to set UID to " << uid_ << ": " << saved_errno;
      _exit(kErrorExitStatus);
    }
    if (!pre_exec_.Run()) {
      LOG(ERROR) << "Pre-exec callback failed";
      _exit(kErrorExitStatus);
    }
    // Reset signal mask for the child process if not inheriting signal mask
    // from the parent process.
    if (!inherit_parent_signal_mask_) {
      sigset_t signal_mask;
      CHECK_EQ(0, sigemptyset(&signal_mask));
      CHECK_EQ(0, sigprocmask(SIG_SETMASK, &signal_mask, nullptr));
    }
    if (search_path_) {
      execvp(argv[0], &argv[0]);
    } else {
      execv(argv[0], &argv[0]);
    }
    PLOG(ERROR) << "Exec of " << argv[0] << " failed";
    _exit(kErrorExitStatus);
  } else {
    // Still executing inside the parent process with known child pid.
    arguments_.clear();
    UpdatePid(pid);
    // Close our copy of child side pipes only if we created those pipes.
    for (const auto& i : pipe_map_) {
      if (!i.second.is_bound_) {
        IGNORE_EINTR(close(i.second.child_fd_));
      }
    }
  }
  return true;
}

int ProcessImpl::Wait() {
  int status = 0;
  if (pid_ == 0) {
    LOG(ERROR) << "Process not running";
    return -1;
  }
  if (HANDLE_EINTR(waitpid(pid_, &status, 0)) < 0) {
    int saved_errno = errno;
    LOG(ERROR) << "Problem waiting for pid " << pid_ << ": " << saved_errno;
    return -1;
  }
  pid_t old_pid = pid_;
  // Update the pid to 0 - do not Reset as we do not want to try to
  // kill the process that has just exited.
  UpdatePid(0);
  if (!WIFEXITED(status)) {
    DCHECK(WIFSIGNALED(status)) << old_pid
                                << " neither exited, nor died on a signal?";
    LOG(ERROR) << "Process " << old_pid
               << " did not exit normally: " << WTERMSIG(status);
    return -1;
  }
  return WEXITSTATUS(status);
}

int ProcessImpl::Run() {
  if (!Start()) {
    return -1;
  }
  return Wait();
}

pid_t ProcessImpl::pid() {
  return pid_;
}

bool ProcessImpl::Kill(int signal, int timeout) {
  if (pid_ == 0) {
    // Passing pid == 0 to kill is committing suicide.  Check specifically.
    LOG(ERROR) << "Process not running";
    return false;
  }
  if (kill(pid_, signal) < 0) {
    PLOG(ERROR) << "Unable to send signal to " << pid_;
    return false;
  }
  base::TimeTicks start_signal = base::TimeTicks::Now();
  do {
    int status = 0;
    pid_t w = waitpid(pid_, &status, WNOHANG);
    if (w < 0) {
      if (errno == ECHILD)
        return true;
      PLOG(ERROR) << "Waitpid returned " << w;
      return false;
    }
    if (w > 0) {
      Reset(0);
      return true;
    }
    usleep(100);
  } while ((base::TimeTicks::Now() - start_signal).InSecondsF() <= timeout);
  LOG(INFO) << "process " << pid_ << " did not exit from signal " << signal
            << " in " << timeout << " seconds";
  return false;
}

void ProcessImpl::UpdatePid(pid_t new_pid) {
  pid_ = new_pid;
}

void ProcessImpl::Reset(pid_t new_pid) {
  arguments_.clear();
  // Close our side of all pipes to this child giving the child to
  // handle sigpipes and shutdown nicely, though likely it won't
  // have time.
  for (PipeMap::iterator i = pipe_map_.begin(); i != pipe_map_.end(); ++i)
    IGNORE_EINTR(close(i->second.parent_fd_));
  pipe_map_.clear();
  if (pid_)
    Kill(SIGKILL, 0);
  UpdatePid(new_pid);
}

bool ProcessImpl::ResetPidByFile(const std::string& pid_file) {
  std::string contents;
  if (!base::ReadFileToString(base::FilePath(pid_file), &contents)) {
    LOG(ERROR) << "Could not read pid file" << pid_file;
    return false;
  }
  base::TrimWhitespaceASCII(contents, base::TRIM_TRAILING, &contents);
  int64_t pid_int64 = 0;
  if (!base::StringToInt64(contents, &pid_int64)) {
    LOG(ERROR) << "Unexpected pid file contents";
    return false;
  }
  Reset(pid_int64);
  return true;
}

pid_t ProcessImpl::Release() {
  pid_t old_pid = pid_;
  pid_ = 0;
  return old_pid;
}

}  // namespace brillo