blob: 37c69620a2c47f64953cef74c72187500e5b2ac7 [file] [log] [blame]
// Copyright 2017 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 <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <launchpad/launchpad.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/object.h>
extern char** environ;
static void usage(const char *exe_name)
{
fprintf(stderr, "Usage: %s [options] [NAME=VALUE]... [command]\n", exe_name);
fprintf(stderr, "Execute command in a modified environment or list environment\n");
fprintf(stderr, "\n");
fprintf(stderr, "Options:\n");
fprintf(stderr, " -i Set only the values provided\n");
fprintf(stderr, " --help Print this message and exit\n");
}
static void dumpenv(const char **envp)
{
while (*envp) {
puts(*envp);
envp++;
}
}
static int env_var_count(char** envp) {
int ndx = 0;
while (envp[ndx])
ndx++;
return ndx;
}
int main(int argc, char* const argv[]) {
int next_arg = 1;
bool use_empty_env = false;
if (argc > next_arg && argv[next_arg][0] == '-') {
if (! strcmp(&argv[next_arg][1], "i")) {
use_empty_env = true;
} else if (! strcmp(&argv[next_arg][1], "-help")) {
usage(argv[0]);
return 0;
} else {
fprintf(stderr, "%s: Unrecognized option '%s'\n", argv[0], argv[next_arg]);
fprintf(stderr, " use --help to get a list of options\n");
return 125;
}
next_arg++;
}
int env_vars_ndx = next_arg;
while (next_arg < argc && strchr(argv[next_arg], '='))
next_arg++;
int new_var_count = next_arg - env_vars_ndx;
int old_var_count = use_empty_env ? 0 : env_var_count(environ);
int total_var_count = new_var_count + old_var_count;
// Construct our execution environment
const char** envp = malloc(sizeof(const char*) * (total_var_count + 1));
if (! envp) {
fprintf(stderr, "Out of memory\n");
return 124;
}
int env_ndx = 0;
if (!use_empty_env) {
while (environ[env_ndx]) {
envp[env_ndx] = environ[env_ndx];
env_ndx++;
}
}
int arg_ndx;
for(arg_ndx = env_vars_ndx; arg_ndx < next_arg; arg_ndx++) {
envp[env_ndx] = argv[arg_ndx];
env_ndx++;
}
envp[env_ndx] = NULL;
// If no command is given, just dump the environment to stdout
if (next_arg == argc) {
dumpenv(envp);
free(envp);
return 0;
}
// Execute utility
launchpad_t* lp;
launchpad_create(ZX_HANDLE_INVALID, argv[next_arg], &lp);
zx_status_t status = launchpad_load_from_file(lp, argv[next_arg]);
if (status < 0) {
fprintf(stderr, "%s: Failed to load from '%s'\n", argv[0],
argv[next_arg]);
launchpad_destroy(lp);
free(envp);
return 127;
}
int num_args = (argc - next_arg);
launchpad_set_args(lp, num_args, (const char* const*) &argv[next_arg]);
launchpad_set_environ(lp, envp);
launchpad_clone(lp, LP_CLONE_FDIO_NAMESPACE | LP_CLONE_FDIO_CWD |
LP_CLONE_FDIO_STDIO);
zx_handle_t proc;
const char* errmsg;
status = launchpad_go(lp, &proc, &errmsg);
free(envp);
if (status < 0) {
fprintf(stderr, "%s: Failed to launch: %s\n", argv[0], errmsg);
return 126;
}
// Wait for utility to complete and return status
status = zx_object_wait_one (proc, ZX_TASK_TERMINATED,
ZX_TIME_INFINITE, NULL);
if (status != ZX_OK) {
fprintf(stderr, "%s: Failed during object_wait_one\n", argv[0]);
return 123;
}
zx_info_process_t proc_info;
if (zx_object_get_info(proc, ZX_INFO_PROCESS, &proc_info,
sizeof(proc_info), NULL, NULL) != ZX_OK) {
fprintf(stderr, "%s: Failed during object_get_info\n", argv[0]);
return 122;
}
return proc_info.return_code & 0xff;
}