diff --git a/backend/wslbridge-backend.cc b/backend/wslbridge-backend.cc index 683b9c3..7236bf5 100644 --- a/backend/wslbridge-backend.cc +++ b/backend/wslbridge-backend.cc @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -67,6 +68,7 @@ struct ChildParams { int cols = -1; int rows = -1; std::vector env; + std::string prog; std::vector argv; std::string cwd; }; @@ -200,7 +202,7 @@ static Child spawnChild(const ChildParams ¶ms) { childFailed(SpawnError::Type::ChdirFailed, errno); } } - execvp(params.argv[0], params.argv.data()); + execvp(params.prog.c_str(), params.argv.data()); childFailed(SpawnError::Type::ExecFailed, errno); } @@ -508,6 +510,7 @@ int main(int argc, char *argv[]) { int windowThreshold = -1; ChildParams childParams; int ptyMode = -1; + bool loginMode = false; const struct option kOptionTable[] = { { "pty", false, &ptyMode, 1 }, @@ -520,7 +523,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:C:", kOptionTable, nullptr)) != -1) { + while ((ch = getopt_long(argc, argv, "+3:0:1:2:k:c:r:w:t:e:C:l", kOptionTable, nullptr)) != -1) { switch (ch) { case 0: // This is returned for the two long options. getopt_long @@ -537,6 +540,7 @@ int main(int argc, char *argv[]) { case 't': windowThreshold = atoi(optarg); break; case 'e': childParams.env.push_back(strdup(optarg)); break; case 'C': childParams.cwd = optarg; break; + case 'l': loginMode = true; break; case 'v': printf("wslbridge-backend " STRINGIFY(WSLBRIDGE_VERSION) "\n"); exit(0); @@ -548,7 +552,27 @@ int main(int argc, char *argv[]) { childParams.argv.push_back(argv[i]); } if (childParams.argv.empty()) { - fatal("error: no command line given\n"); + const char *shell = "/bin/sh"; + struct passwd *pw = getpwuid(getuid()); + if (pw == nullptr) { + perror("error: getpwuid failed"); + } else if (pw->pw_shell == nullptr) { + fprintf(stderr, "error: getpwuid(...)->pw_shell is NULL\n"); + } else { + shell = pw->pw_shell; + } + childParams.argv.push_back(strdup(shell)); + } + // XXX: Replace char* args/envstrings with std::string? + childParams.prog = childParams.argv[0]; + if (loginMode) { + std::string argv0 = childParams.argv[0]; + const auto pos = argv0.find_last_of('/'); + if (pos != std::string::npos) { + argv0 = argv0.substr(pos + 1); + } + argv0 = '-' + argv0; + childParams.argv[0] = strdup(argv0.c_str()); } childParams.argv.push_back(nullptr); @@ -598,7 +622,7 @@ int main(int argc, char *argv[]) { mainLoop(childParams.usePty, controlSocket, inputSocket, outputSocket, errorSocket, - childParams.argv[0], child, windowParams); + childParams.prog.c_str(), child, windowParams); return 0; } diff --git a/frontend/wslbridge.cc b/frontend/wslbridge.cc index b53b08e..a02e993 100644 --- a/frontend/wslbridge.cc +++ b/frontend/wslbridge.cc @@ -619,6 +619,8 @@ static void usage(const char *prog) { 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(" -l Start a login shell.\n"); + printf(" --no-login Do not start a login shell.\n"); printf(" -T Do not use a pty.\n"); printf(" -t Use a pty (as long as stdin is a tty).\n"); printf(" -t -t Force a pty (even if stdin is not a tty).\n"); @@ -982,17 +984,22 @@ int main(int argc, char *argv[]) { std::string spawnCwd; std::string distroGuid; enum class TtyRequest { Auto, Yes, No, Force } ttyRequest = TtyRequest::Auto; + enum class LoginMode { Auto, Yes, No } loginMode = LoginMode::Auto; int debugFork = 0; int c = 0; + if (argv[0][0] == '-') { + loginMode = LoginMode::Yes; + } const struct option kOptionTable[] = { { "help", false, nullptr, 'h' }, { "debug-fork", false, &debugFork, 1 }, { "version", false, nullptr, 'v' }, { "distro-guid", true, nullptr, 'd' }, + { "no-login", false, nullptr, 'L' }, { nullptr, false, nullptr, 0 }, }; - while ((c = getopt_long(argc, argv, "+e:C:tT", kOptionTable, nullptr)) != -1) { + while ((c = getopt_long(argc, argv, "+e:C:tTl", kOptionTable, nullptr)) != -1) { switch (c) { case 0: // Ignore long option. @@ -1038,14 +1045,23 @@ int main(int argc, char *argv[]) { fatal("error: the --distro-guid argument '%s' is invalid\n", optarg); } break; + case 'l': + loginMode = LoginMode::Yes; + break; + case 'L': + loginMode = LoginMode::No; + break; default: fatal("Try '%s --help' for more information.\n", argv[0]); } } const bool hasCommand = optind < argc; + if (loginMode == LoginMode::Auto) { + loginMode = hasCommand ? LoginMode::No : LoginMode::Yes; + } if (ttyRequest == TtyRequest::Auto) { - ttyRequest = hasCommand ? TtyRequest::No : TtyRequest::Yes; + ttyRequest = loginMode == LoginMode::No ? TtyRequest::No : TtyRequest::Yes; } if (ttyRequest == TtyRequest::Yes && !isatty(STDIN_FILENO)) { fprintf(stderr, "Pseudo-terminal will not be allocated because stdin is not a terminal.\n"); @@ -1126,6 +1142,9 @@ int main(int argc, char *argv[]) { assert(iRet > 0); bashCmdLine.append(buffer.data()); + if (loginMode == LoginMode::Yes) { + appendBashArg(bashCmdLine, L"-l"); + } for (const auto &envPair : env.pairs()) { appendBashArg(bashCmdLine, L"-e" + envPair.first + L"=" + envPair.second); } @@ -1133,14 +1152,8 @@ int main(int argc, char *argv[]) { appendBashArg(bashCmdLine, L"-C" + mbsToWcs(spawnCwd)); } appendBashArg(bashCmdLine, L"--"); - - if (optind == argc) { - // No command-line specified. Use a default one. - appendBashArg(bashCmdLine, L"/bin/bash"); - } else { - for (int i = optind; i < argc; ++i) { - appendBashArg(bashCmdLine, mbsToWcs(argv[i])); - } + for (int i = optind; i < argc; ++i) { + appendBashArg(bashCmdLine, mbsToWcs(argv[i])); } std::wstring cmdLine;