diff --git a/client.c b/client.c index 33af8e87..e00adc40 100644 --- a/client.c +++ b/client.c @@ -33,6 +33,7 @@ print_usage(const char *prog_name) printf("\n"); printf("Options:\n"); printf(" -t,--term=TERM value to set the environment variable TERM to (foot)\n" + " --login-shell start shell as a login shell\n" " -s,--server-socket=PATH path to the server UNIX domain socket (default=XDG_RUNTIME_DIR/foot.sock)\n" " -l,--log-colorize=[never|always|auto] enable/disable colorization of log output on stderr\n" " -v,--version show the version number and quit\n"); @@ -47,6 +48,7 @@ main(int argc, char *const *argv) static const struct option longopts[] = { {"term", required_argument, 0, 't'}, + {"login-shell", no_argument, 0, 'L'}, {"server-socket", required_argument, 0, 's'}, {"log-colorize", optional_argument, NULL, 'l'}, {"version", no_argument, 0, 'v'}, @@ -57,6 +59,7 @@ main(int argc, char *const *argv) const char *term = ""; const char *server_socket_path = NULL; enum log_colorize log_colorize = LOG_COLORIZE_AUTO; + bool login_shell = false; while (true) { int c = getopt_long(argc, argv, ":t:s:l::hv", longopts, NULL); @@ -68,6 +71,10 @@ main(int argc, char *const *argv) term = optarg; break; + case 'L': + login_shell = true; + break; + case 's': server_socket_path = optarg; break; @@ -154,6 +161,7 @@ main(int argc, char *const *argv) /* Calculate total length */ total_len += sizeof(cwd_len) + cwd_len; total_len += sizeof(term_len) + term_len; + total_len += sizeof(uint8_t); /* login_shell */ total_len += sizeof(argc); for (int i = 0; i < argc; i++) { @@ -183,6 +191,11 @@ main(int argc, char *const *argv) goto err; } + if (send(fd, &(uint8_t){login_shell}, sizeof(uint8_t), 0) != sizeof(uint8_t)) { + LOG_ERRNO("failed to send login-shell"); + goto err; + } + LOG_DBG("argc = %d", argc); if (send(fd, &argc, sizeof(argc), 0) != sizeof(argc)) { LOG_ERRNO("failed to send argc/argv to server"); diff --git a/completions/zsh/_foot b/completions/zsh/_foot index 9f79a730..ad04a467 100644 --- a/completions/zsh/_foot +++ b/completions/zsh/_foot @@ -5,6 +5,7 @@ _arguments \ '(-c --config)'{-c,--config}'[path to configuration file (XDG_CONFIG_HOME/footrc)]:config:_files' \ '(-f --font)'{-f,--font}'[font name and style in fontconfig format (monospace)]:font:->fonts' \ '(-t --term)'{-t,--term}'[value to set the environment variable TERM to (foot)]:term:->terms' \ + '--login-shell[start shell as a login shell]' \ '(-g --geometry)'{-g,--geometry}'[window WIDTHxHEIGHT, in pixels (80x24 cells)]:geometry:()' \ '(-s --server)'{-s,--server}'[run as server; open terminals by running footclient]:server:_files' \ '--hold[remain open after child process exits]' \ diff --git a/completions/zsh/_footclient b/completions/zsh/_footclient index 9b3eb3fe..75a99b95 100644 --- a/completions/zsh/_footclient +++ b/completions/zsh/_footclient @@ -3,6 +3,7 @@ _arguments \ -s \ '(-t --term)'{-t,--term}'[value to set the environment variable TERM to (foot)]:term:->terms' \ + '--login-shell[start shell as a login shell]' \ '(-s --server-socket)'{-s,--server-socket}'[override the default path to the foot server socket (XDG_RUNTIME_DIR/foot.sock)]:server:_files' \ '(-l --log-colorize)'{-l,--log-colorize}'[enable or disable colorization of log output on stderr]:logcolor:(never always auto)' \ '(-v --version)'{-v,--version}'[show the version number and quit]' \ diff --git a/config.c b/config.c index 1dd6652f..39a72648 100644 --- a/config.c +++ b/config.c @@ -159,6 +159,14 @@ parse_section_main(const char *key, const char *value, struct config *conf, conf->shell = strdup(value); } + else if (strcmp(key, "login-shell") == 0) { + conf->login_shell = ( + strcasecmp(value, "on") == 0 || + strcasecmp(value, "true") == 0 || + strcasecmp(value, "yes") == 0) || + strtoul(value, NULL, 0) > 0; + } + else if (strcmp(key, "geometry") == 0) { unsigned width, height; if (sscanf(value, "%ux%u", &width, &height) != 2 || width == 0 || height == 0) { diff --git a/config.h b/config.h index 90376fc0..3677129f 100644 --- a/config.h +++ b/config.h @@ -10,6 +10,7 @@ struct config { char *term; char *shell; + bool login_shell; unsigned width; unsigned height; unsigned pad_x; diff --git a/doc/foot.1.scd b/doc/foot.1.scd index 639ddf19..fcd9d1f8 100644 --- a/doc/foot.1.scd +++ b/doc/foot.1.scd @@ -36,6 +36,9 @@ execute (instead of the shell). *-t*,*--term*=_TERM_ Value to set the environment variable *TERM* to. Default: *foot*. +*--login-shell* + Start a login shell, by prepending a '-' to argv[0]. + *-s*,*--server*[=_PATH_] Run as a server. In this mode, a single foot instance hosts multiple terminals (windows). Use *footclient*(1) to launch new diff --git a/doc/footclient.1.scd b/doc/footclient.1.scd index e58be6a1..d6115906 100644 --- a/doc/footclient.1.scd +++ b/doc/footclient.1.scd @@ -15,6 +15,9 @@ execute (instead of the shell). *-t*,*--term*=_TERM_ Value to set the environment variable _TERM_ to. Default: _foot_. +*--login-shell* + Start a login shell, by prepending a '-' to argv[0]. + *-s*,*--server-socket*=_PATH_ Connect to _PATH_ instead of _XDG\_RUNTIME\_DIR/foot.sock_. diff --git a/footrc b/footrc index d001e644..337be31b 100644 --- a/footrc +++ b/footrc @@ -6,6 +6,7 @@ # pad=2x2 # shell= (you may need to override if you need a login shell) # term=foot +# login-shell=no # workers= [cursor] diff --git a/main.c b/main.c index 2b2006f5..7709e05c 100644 --- a/main.c +++ b/main.c @@ -46,6 +46,7 @@ print_usage(const char *prog_name) " -c,--config=PATH load configuration from PATH (XDG_CONFIG_HOME/footrc)\n" " -f,--font=FONT comma separated list of fonts in fontconfig format (monospace)\n" " -t,--term=TERM value to set the environment variable TERM to (foot)\n" + " --login-shell start shell as a login shell\n" " -g,--geometry=WIDTHxHEIGHT set initial width and height\n" " -s,--server[=PATH] run as a server (use 'footclient' to start terminals).\n" " Without PATH, XDG_RUNTIME_DIR/foot.sock will be used.\n" @@ -136,6 +137,7 @@ main(int argc, char *const *argv) static const struct option longopts[] = { {"config", required_argument, NULL, 'c'}, {"term", required_argument, NULL, 't'}, + {"login-shell", no_argument, NULL, 'L'}, {"font", required_argument, NULL, 'f'}, {"geometry", required_argument, NULL, 'g'}, {"server", optional_argument, NULL, 's'}, @@ -151,6 +153,7 @@ main(int argc, char *const *argv) const char *conf_path = NULL; const char *conf_term = NULL; + bool login_shell = false; tll(char *) conf_fonts = tll_init(); int conf_width = -1; int conf_height = -1; @@ -164,7 +167,7 @@ main(int argc, char *const *argv) bool log_syslog = true; while (true) { - int c = getopt_long(argc, argv, "c:t:f:g:s::Pp:l::Svh", longopts, NULL); + int c = getopt_long(argc, argv, "c:t:Lf:g:s::Pp:l::Svh", longopts, NULL); if (c == -1) break; @@ -177,6 +180,10 @@ main(int argc, char *const *argv) conf_term = optarg; break; + case 'L': + login_shell = true; + break; + case 'f': tll_free_and_free(conf_fonts, free); for (char *font = strtok(optarg, ","); font != NULL; font = strtok(NULL, ",")) { @@ -279,6 +286,8 @@ main(int argc, char *const *argv) free(conf.term); conf.term = strdup(conf_term); } + if (login_shell) + conf.login_shell = true; if (tll_length(conf_fonts) > 0) { tll_free_and_free(conf.fonts, free); tll_foreach(conf_fonts, it) @@ -323,7 +332,8 @@ main(int argc, char *const *argv) goto out; if (!as_server && (term = term_init( - &conf, fdm, wayl, conf.term, "foot", cwd, argc, argv, + &conf, fdm, wayl, conf.term, conf.login_shell, + "foot", cwd, argc, argv, &term_shutdown_cb, &shutdown_ctx)) == NULL) { free(cwd); goto out; diff --git a/server.c b/server.c index 92f326a0..dc0b1e47 100644 --- a/server.c +++ b/server.c @@ -213,6 +213,9 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data) goto shutdown; } + CHECK_BUF(sizeof(uint8_t)); + const uint8_t login_shell = *(const uint8_t *)p; p += sizeof(login_shell); + CHECK_BUF(sizeof(argc)); argc = *(int *)p; p += sizeof(argc); argv = calloc(argc + 1, sizeof(argv[0])); @@ -238,7 +241,7 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data) client->term = term_init( server->conf, server->fdm, server->wayl, - strlen(term_env) > 0 ? term_env : server->conf->term, + strlen(term_env) > 0 ? term_env : server->conf->term, login_shell, "footclient", cwd, argc, argv, &term_shutdown_handler, client); if (client->term == NULL) { diff --git a/slave.c b/slave.c index 857640b2..3bff59b7 100644 --- a/slave.c +++ b/slave.c @@ -17,7 +17,7 @@ #include "tokenize.h" static void -slave_exec(int ptmx, char *const argv[], int err_fd) +slave_exec(int ptmx, char *argv[], int err_fd, bool login_shell) { int pts = -1; const char *pts_name = ptsname(ptmx); @@ -56,7 +56,20 @@ slave_exec(int ptmx, char *const argv[], int err_fd) close(pts); pts = -1; - execvp(argv[0], argv); + const char *file; + if (login_shell) { + file = strdup(argv[0]); + + char *arg0 = malloc(strlen(argv[0]) + 1 + 1); + arg0[0] = '-'; + arg0[1] = '\0'; + strcat(arg0, argv[0]); + + argv[0] = arg0; + } else + file = argv[0]; + + execvp(file, argv); err: (void)!write(err_fd, &errno, sizeof(errno)); @@ -70,7 +83,7 @@ err: pid_t slave_spawn(int ptmx, int argc, const char *cwd, char *const *argv, - const char *term_env, const char *conf_shell) + const char *term_env, const char *conf_shell, bool login_shell) { int fork_pipe[2]; if (pipe2(fork_pipe, O_CLOEXEC) < 0) { @@ -107,7 +120,7 @@ slave_spawn(int ptmx, int argc, const char *cwd, char *const *argv, setenv("TERM", term_env, 1); char **_shell_argv = NULL; - char *const *shell_argv = argv; + char **shell_argv = NULL; if (argc == 0) { char *shell_copy = strdup(conf_shell); @@ -117,9 +130,17 @@ slave_spawn(int ptmx, int argc, const char *cwd, char *const *argv, _exit(0); } shell_argv = _shell_argv; + } else { + size_t count = 0; + for (; argv[count] != NULL; count++) + ; + shell_argv = malloc((count + 1) * sizeof(shell_argv[0])); + for (size_t i = 0; i < count; i++) + shell_argv[i] = argv[i]; + shell_argv[count] = NULL; } - slave_exec(ptmx, shell_argv, fork_pipe[1]); + slave_exec(ptmx, shell_argv, fork_pipe[1], login_shell); assert(false); break; diff --git a/slave.h b/slave.h index 63fe9bff..83891590 100644 --- a/slave.h +++ b/slave.h @@ -5,4 +5,4 @@ pid_t slave_spawn( int ptmx, int argc, const char *cwd, char *const *argv, const char *term_env, - const char *conf_shell); + const char *conf_shell, bool login_shell); diff --git a/terminal.c b/terminal.c index 89c18c75..37b2cc84 100644 --- a/terminal.c +++ b/terminal.c @@ -589,8 +589,8 @@ load_fonts_from_conf(const struct terminal *term, const struct config *conf, struct terminal * term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, - const char *term_env, const char *foot_exe, const char *cwd, - int argc, char *const *argv, + const char *term_env, bool login_shell, const char *foot_exe, + const char *cwd, int argc, char *const *argv, void (*shutdown_cb)(void *data, int exit_code), void *shutdown_data) { int ptmx = -1; @@ -777,7 +777,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, term_font_dpi_changed(term); /* Start the slave/client */ - if ((term->slave = slave_spawn(term->ptmx, argc, term->cwd, argv, term_env, conf->shell)) == -1) + if ((term->slave = slave_spawn(term->ptmx, argc, term->cwd, argv, term_env, conf->shell, login_shell)) == -1) goto err; if (term->width == 0 && term->height == 0) { diff --git a/terminal.h b/terminal.h index d400dca5..d9727b32 100644 --- a/terminal.h +++ b/terminal.h @@ -356,8 +356,8 @@ struct terminal { struct config; struct terminal *term_init( const struct config *conf, struct fdm *fdm, struct wayland *wayl, - const char *term_env, const char *foot_exe, const char *cwd, - int argc, char *const *argv, + const char *term_env, bool login_shell, const char *foot_exe, + const char *cwd, int argc, char *const *argv, void (*shutdown_cb)(void *data, int exit_code), void *shutdown_data); bool term_shutdown(struct terminal *term);