blob: 56508bd22edc7652c8bc2e98bd363ce81ab4615c [file] [log] [blame]
// Copyright 2016 The Fuchsia 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 <assert.h>
#include <lib/fdio/io.h>
#include <lib/fdio/spawn.h>
#include <lib/fdio/util.h>
#include <memory.h>
#include <memory>
#include <string>
#include <unistd.h>
#include <vector>
#include <zircon/process.h>
#include <zircon/processargs.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/object.h>
#include "deProcess.h"
#if (DE_OS != DE_OS_FUCHSIA)
#error DE_OS_FUCHSIA expected
#endif
#include "deCommandLine.h"
struct deProcess_s {
};
class ModifiedEnviron {
public:
explicit ModifiedEnviron(char** env)
{
for (size_t i = 0; env[i]; i++) {
envs_.push_back(env[i]);
}
}
void ReplacePwd(const char* working_directory)
{
assert(working_directory);
char pwd_string[strlen(working_directory) + 4 + 1];
sprintf(pwd_string, "PWD=%s", working_directory);
for (size_t i = 0; i < envs_.size(); i++) {
if (strstr(envs_[i].c_str(), "PWD=") == 0) {
envs_[i] = pwd_string;
return;
}
}
envs_.push_back(pwd_string);
}
// Return value must not outlive ModifiedEnviron. Invalidated if ReplacePwd
// is called.
std::vector<const char*> GetEnviron()
{
std::vector<const char*> env;
for (size_t i = 0; i < envs_.size(); i++) {
env.push_back(envs_[i].c_str());
}
env.push_back(nullptr);
return env;
}
private:
std::vector<std::string> envs_;
};
class DeProcess : public deProcess_s {
public:
static std::unique_ptr<DeProcess> Create()
{
return std::unique_ptr<DeProcess>(new DeProcess());
}
DeProcess() : proc_(ZX_HANDLE_INVALID), fstdin_(nullptr), fstdout_(nullptr), fstderr_(nullptr)
{
}
~DeProcess()
{
if (proc_ != ZX_HANDLE_INVALID)
zx_handle_close(proc_);
closeStdIn();
closeStdOut();
closeStdErr();
}
bool start(const char* commandLine, const char* workingDirectory)
{
deCommandLine* cmdLine = deCommandLine_parse(commandLine);
assert(cmdLine->args[cmdLine->numArgs] == 0);
fdio_spawn_action_t spawn_actions[3];
int result;
int stdin_pipes[2];
result = pipe(stdin_pipes);
assert(result == 0);
int stdout_pipes[2];
result = pipe(stdout_pipes);
assert(result == 0);
int stderr_pipes[2];
result = pipe(stderr_pipes);
assert(result == 0);
spawn_actions[0].action = FDIO_SPAWN_ACTION_TRANSFER_FD;
spawn_actions[0].fd.local_fd = stdin_pipes[0];
spawn_actions[0].fd.target_fd = STDIN_FILENO;
fstdin_ = deFile_createFromHandle(stdin_pipes[1]);
spawn_actions[1].action = FDIO_SPAWN_ACTION_TRANSFER_FD;
spawn_actions[1].fd.local_fd = stdout_pipes[1];
spawn_actions[1].fd.target_fd = STDOUT_FILENO;
fstdout_ = deFile_createFromHandle(stdout_pipes[0]);
#ifndef RAW_STDERR
spawn_actions[2].action = FDIO_SPAWN_ACTION_TRANSFER_FD;
spawn_actions[2].fd.local_fd = stderr_pipes[1];
spawn_actions[2].fd.target_fd = STDERR_FILENO;
fstderr_ = deFile_createFromHandle(stderr_pipes[0]);
#else
// Printing to our stderr sometimes works better.
spawn_actions[2].action = FDIO_SPAWN_ACTION_CLONE_FD;
spawn_actions[2].fd.local_fd = STDERR_FILENO;
spawn_actions[2].fd.target_fd = STDERR_FILENO;
fstderr_ = deFile_createFromHandle(stderr_pipes[0]);
#endif
ModifiedEnviron env(environ);
if (workingDirectory)
env.ReplacePwd(workingDirectory);
char err_msg_out[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
uint32_t spawn_flags = FDIO_SPAWN_CLONE_NAMESPACE | FDIO_SPAWN_CLONE_ENVIRON |
FDIO_SPAWN_CLONE_LDSVC | FDIO_SPAWN_CLONE_JOB;
zx_status_t status = fdio_spawn_etc(
ZX_HANDLE_INVALID, spawn_flags, cmdLine->args[0], (const char* const*)&cmdLine->args[0],
env.GetEnviron().data(), 3, spawn_actions, &proc_, err_msg_out);
if (status != ZX_OK) {
fprintf(stderr, "fdio_spawn error: %d %s\n", status, err_msg_out);
return false;
}
return true;
}
bool IsRunning()
{
zx_signals_t pending = 0;
zx_status_t status = zx_object_wait_one(proc_, ZX_TASK_TERMINATED, 0, &pending);
if (pending & ZX_TASK_TERMINATED)
return false;
assert(status == ZX_ERR_TIMED_OUT);
return true;
}
bool Wait()
{
zx_status_t status = zx_object_wait_one(proc_, ZX_TASK_TERMINATED, ZX_TIME_INFINITE, NULL);
return status == ZX_OK;
}
int GetExitCode()
{
zx_info_process_t info;
zx_status_t status =
zx_object_get_info(proc_, ZX_INFO_PROCESS, &info, sizeof(info), NULL, NULL);
assert(status == ZX_OK);
return info.return_code;
}
static DeProcess* cast(deProcess* process) { return static_cast<DeProcess*>(process); }
deFile* fstdin() { return fstdin_; }
deFile* fstdout() { return fstdout_; }
deFile* fstderr() { return fstderr_; }
void closeStdIn()
{
if (fstdin_) {
deFile_destroy(fstdin_);
fstdin_ = nullptr;
}
}
void closeStdOut()
{
if (fstdout_) {
deFile_destroy(fstdout_);
fstdout_ = nullptr;
}
}
void closeStdErr()
{
if (fstderr_) {
deFile_destroy(fstderr_);
fstderr_ = nullptr;
}
}
private:
zx_handle_t proc_;
deFile* fstdin_;
deFile* fstdout_;
deFile* fstderr_;
};
deProcess* deProcess_create()
{
std::unique_ptr<DeProcess> process = DeProcess::Create();
if (!process)
return nullptr;
return process.release();
}
void deProcess_destroy(deProcess* process) { delete DeProcess::cast(process); }
deBool deProcess_start(deProcess* de_process, const char* commandLine, const char* workingDirectory)
{
(void)workingDirectory;
DeProcess* process = DeProcess::cast(de_process);
if (!process->start(commandLine, workingDirectory))
return false;
return true;
}
deBool deProcess_isRunning(deProcess* process) { return DeProcess::cast(process)->IsRunning(); }
deBool deProcess_waitForFinish(deProcess* process)
{
return DeProcess::cast(process)->Wait() ? DE_TRUE : DE_FALSE;
}
const char* deProcess_getLastError(const deProcess*) { return nullptr; }
int deProcess_getExitCode(const deProcess* process)
{
return DeProcess::cast((deProcess*)process)->GetExitCode();
}
deBool deProcess_terminate(deProcess*)
{
printf("%s:%d Not implemented\n", __FILE__, __LINE__);
return DE_FALSE;
}
deBool deProcess_kill(deProcess*)
{
printf("%s:%d Not implemented\n", __FILE__, __LINE__);
return DE_FALSE;
}
deFile* deProcess_getStdIn(deProcess* process) { return DeProcess::cast(process)->fstdin(); }
deFile* deProcess_getStdOut(deProcess* process) { return DeProcess::cast(process)->fstdout(); }
deFile* deProcess_getStdErr(deProcess* process) { return DeProcess::cast(process)->fstderr(); }
deBool deProcess_closeStdIn(deProcess* process)
{
DeProcess::cast(process)->closeStdIn();
return DE_TRUE;
}
deBool deProcess_closeStdOut(deProcess* process)
{
DeProcess::cast(process)->closeStdOut();
return DE_TRUE;
}
deBool deProcess_closeStdErr(deProcess* process)
{
DeProcess::cast(process)->closeStdErr();
return DE_TRUE;
}