Skip to content

Commit

Permalink
Add a -C option that changes the directory just before calling exec
Browse files Browse the repository at this point in the history
If the argument is '~' or starts with '~/', then the '~' is replaced with
the content of the HOME environment variable.

Implements #2
  • Loading branch information
rprichard committed Sep 28, 2016
1 parent 3dd52c8 commit fff7828
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 18 deletions.
37 changes: 27 additions & 10 deletions backend/wslbridge-backend.cc
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,21 @@ static int connectSocket(int port, const std::string &key) {
return s;
}

static std::string resolveCwd(const std::string &cwd) {
if (cwd == "~" || cwd.substr(0, 2) == "~/") {
const auto home = getenv("HOME") ?: "";
return home + cwd.substr(1);
}
return cwd;
}

struct ChildParams {
bool usePty = false;
int cols = -1;
int rows = -1;
std::vector<char*> env;
std::vector<char*> argv;
std::string cwd;
};

struct Child {
Expand Down Expand Up @@ -174,29 +183,36 @@ static Child spawnChild(const ChildParams &params) {

} else if (pid == 0) {
// forked process
const auto childFailed = [&](SpawnError::Type type, int savedErrno) {
const SpawnError err = {
type,
bridgedError(savedErrno)
};
writeAllRestarting(spawnErrPipe.write.fd(), &err, sizeof(err));
_exit(1);
};
spawnErrPipe.read.close();
for (const auto &setting : params.env) {
putenv(setting);
}
if (!params.cwd.empty()) {
if (chdir(resolveCwd(params.cwd).c_str()) != 0) {
childFailed(SpawnError::Type::ChdirFailed, errno);
}
}
execvp(params.argv[0], params.argv.data());
const int err = errno;
writeAllRestarting(spawnErrPipe.write.fd(), &err, sizeof(err));
abort();
childFailed(SpawnError::Type::ExecFailed, errno);
}

UniqueFd masterFd(masterFdRaw);

spawnErrPipe.write.close();

int execErrno = -1;
if (readAllRestarting(spawnErrPipe.read.fd(), &execErrno, sizeof(execErrno))) {
SpawnError err = {};
if (readAllRestarting(spawnErrPipe.read.fd(), &err, sizeof(err))) {
// The child exec call failed.
int dummy = 0;
waitpid(pid, &dummy, 0);
const SpawnError err = {
SpawnError::Type::ExecFailed,
bridgedError(execErrno)
};
Child ret;
ret.spawnError = err;
return ret;
Expand Down Expand Up @@ -489,7 +505,7 @@ int main(int argc, char *argv[]) {
};

int ch = 0;
while ((ch = getopt_long(argc, argv, "+3:0:1:2:k:c:r:w:t:e:", kOptionTable, nullptr)) != -1) {
while ((ch = getopt_long(argc, argv, "+3:0:1:2:k:c:r:w:t:e:C:", kOptionTable, nullptr)) != -1) {
switch (ch) {
case 0:
// This is returned for the two long options. getopt_long
Expand All @@ -505,6 +521,7 @@ int main(int argc, char *argv[]) {
case 'w': windowSize = atoi(optarg); break;
case 't': windowThreshold = atoi(optarg); break;
case 'e': childParams.env.push_back(strdup(optarg)); break;
case 'C': childParams.cwd = optarg; break;
case 'v':
printf("wslbridge-backend " STRINGIFY(WSLBRIDGE_VERSION) "\n");
exit(0);
Expand Down
7 changes: 6 additions & 1 deletion common/SocketIo.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,12 @@ struct BridgedError {
};

struct SpawnError {
enum class Type : int32_t { Success, ForkPtyFailed, ExecFailed } type;
enum class Type : int32_t {
Success = 0,
ForkPtyFailed,
ExecFailed,
ChdirFailed,
} type;
BridgedError error;
};

Expand Down
37 changes: 30 additions & 7 deletions frontend/wslbridge.cc
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ void TerminalState::exitCleanly(int exitStatus) {
static TerminalState g_terminalState;

struct IoLoop {
std::string spawnCwd;
std::string spawnProgName;
bool usePty = false;
std::mutex mutex;
Expand Down Expand Up @@ -365,10 +366,18 @@ static void handlePacket(IoLoop *ioloop, const Packet &p) {
}
case Packet::Type::SpawnFailed: {
std::string msg;
if (p.u.spawnError.type == SpawnError::Type::ForkPtyFailed) {
msg = "error: forkpty failed: ";
} else {
msg = "error: could not start '" + ioloop->spawnProgName + "': ";
switch (p.u.spawnError.type) {
case SpawnError::Type::ForkPtyFailed:
msg = "error: forkpty failed: ";
break;
case SpawnError::Type::ChdirFailed:
msg = "error: could not chdir to '" + ioloop->spawnCwd + "': ";
break;
case SpawnError::Type::ExecFailed:
msg = "error: could not exec '" + ioloop->spawnProgName + "': ";
break;
default:
assert(false && "Unhandled SpawnError type");
}
msg += errorString(p.u.spawnError.error);
g_terminalState.fatal("%s\n", msg.c_str());
Expand All @@ -381,11 +390,13 @@ static void handlePacket(IoLoop *ioloop, const Packet &p) {
}
}

static void mainLoop(const std::string &spawnProgName,
static void mainLoop(const std::string &spawnCwd,
const std::string &spawnProgName,
bool usePty, int controlSocketFd,
int inputSocketFd, int outputSocketFd, int errorSocketFd,
TermSize termSize) {
IoLoop ioloop;
ioloop.spawnCwd = spawnCwd;
ioloop.spawnProgName = spawnProgName;
ioloop.usePty = usePty;
ioloop.controlSocketFd = controlSocketFd;
Expand Down Expand Up @@ -599,6 +610,8 @@ static void usage(const char *prog) {
printf("Runs a program within a Windows Subsystem for Linux (WSL) pty\n");
printf("\n");
printf("Options:\n");
printf(" -C WSLDIR Changes the working directory to WSLDIR first.\n");
printf(" An initial '~' indicates the WSL home directory.\n");
printf(" -e VAR Copies VAR into the WSL environment.\n");
printf(" -e VAR=VAL Sets VAR to VAL in the WSL environment.\n");
printf(" -T Do not use a pty.\n");
Expand Down Expand Up @@ -741,6 +754,7 @@ int main(int argc, char *argv[]) {
g_wakeupFd = new WakeupFd();

Environment env;
std::string spawnCwd;
enum class TtyRequest { Auto, Yes, No, Force } ttyRequest = TtyRequest::Auto;

int debugFork = 0;
Expand All @@ -751,7 +765,7 @@ int main(int argc, char *argv[]) {
{ "version", false, nullptr, 'v' },
{ nullptr, false, nullptr, 0 },
};
while ((c = getopt_long(argc, argv, "+e:tT", kOptionTable, nullptr)) != -1) {
while ((c = getopt_long(argc, argv, "+e:C:tT", kOptionTable, nullptr)) != -1) {
switch (c) {
case 0:
// Ignore long option.
Expand All @@ -769,6 +783,12 @@ int main(int argc, char *argv[]) {
}
break;
}
case 'C':
spawnCwd = optarg;
if (spawnCwd.empty()) {
fatal("error: the -C option requires a non-empty string argument");
}
break;
case 'h':
usage(argv[0]);
break;
Expand Down Expand Up @@ -871,6 +891,9 @@ int main(int argc, char *argv[]) {
for (const auto &envPair : env.pairs()) {
appendBashArg(bashCmdLine, L"-e" + envPair.first + L"=" + envPair.second);
}
if (!spawnCwd.empty()) {
appendBashArg(bashCmdLine, L"-C" + mbsToWcs(spawnCwd));
}
appendBashArg(bashCmdLine, L"--");

std::string spawnProgName;
Expand Down Expand Up @@ -944,7 +967,7 @@ int main(int argc, char *argv[]) {

backendStarted = true;

mainLoop(spawnProgName,
mainLoop(spawnCwd, spawnProgName,
usePty, controlSocketC,
inputSocketC, outputSocketC, errorSocketC,
initialSize);
Expand Down

0 comments on commit fff7828

Please sign in to comment.