mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00:00
5d718ef64b
The number of people has been steadily increasing who read our source code with an editor that thinks tab stops are 4 spaces apart, as opposed to the traditional tty-derived 8 that the PuTTY code expects. So I've been wondering for ages about just fixing it, and switching to a spaces-only policy throughout the code. And I recently found out about 'git blame -w', which should make this change not too disruptive for the purposes of source-control archaeology; so perhaps now is the time. While I'm at it, I've also taken the opportunity to remove all the trailing spaces from source lines (on the basis that git dislikes them, and is the only thing that seems to have a strong opinion one way or the other). Apologies to anyone downstream of this code who has complicated patch sets to rebase past this change. I don't intend it to be needed again.
203 lines
4.7 KiB
C
203 lines
4.7 KiB
C
/*
|
|
* pty.c - pseudo-terminal handling
|
|
*/
|
|
|
|
#define _XOPEN_SOURCE 500
|
|
#include <features.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <termios.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/types.h>
|
|
#include <pwd.h>
|
|
|
|
#include "pty.h"
|
|
#include "malloc.h"
|
|
|
|
static char ptyname[FILENAME_MAX];
|
|
int master = -1;
|
|
|
|
void pty_preinit(void)
|
|
{
|
|
/*
|
|
* Allocate the pty.
|
|
*/
|
|
master = open("/dev/ptmx", O_RDWR);
|
|
if (master < 0) {
|
|
perror("/dev/ptmx: open");
|
|
exit(1);
|
|
}
|
|
|
|
if (grantpt(master) < 0) {
|
|
perror("grantpt");
|
|
exit(1);
|
|
}
|
|
|
|
if (unlockpt(master) < 0) {
|
|
perror("unlockpt");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
void pty_resize(int w, int h)
|
|
{
|
|
struct winsize sz;
|
|
|
|
assert(master >= 0);
|
|
|
|
sz.ws_row = h;
|
|
sz.ws_col = w;
|
|
sz.ws_xpixel = sz.ws_ypixel = 0;
|
|
ioctl(master, TIOCSWINSZ, &sz);
|
|
}
|
|
|
|
int run_program_in_pty(const struct shell_data *shdata,
|
|
char *directory, char **program_args)
|
|
{
|
|
int slave, pid;
|
|
char *fallback_args[2];
|
|
|
|
assert(master >= 0);
|
|
|
|
ptyname[FILENAME_MAX-1] = '\0';
|
|
strncpy(ptyname, ptsname(master), FILENAME_MAX-1);
|
|
|
|
#if 0
|
|
{
|
|
struct winsize ws;
|
|
struct termios ts;
|
|
|
|
/*
|
|
* FIXME: think up some good defaults here
|
|
*/
|
|
|
|
if (!ioctl(0, TIOCGWINSZ, &ws))
|
|
ioctl(master, TIOCSWINSZ, &ws);
|
|
if (!tcgetattr(0, &ts))
|
|
tcsetattr(master, TCSANOW, &ts);
|
|
}
|
|
#endif
|
|
|
|
slave = open(ptyname, O_RDWR | O_NOCTTY);
|
|
if (slave < 0) {
|
|
perror("slave pty: open");
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Fork and execute the command.
|
|
*/
|
|
pid = fork();
|
|
if (pid < 0) {
|
|
perror("fork");
|
|
return 1;
|
|
}
|
|
|
|
if (pid == 0) {
|
|
int i, fd;
|
|
|
|
/*
|
|
* We are the child.
|
|
*/
|
|
close(master);
|
|
|
|
fcntl(slave, F_SETFD, 0); /* don't close on exec */
|
|
dup2(slave, 0);
|
|
dup2(slave, 1);
|
|
if (slave != 0 && slave != 1)
|
|
close(slave);
|
|
dup2(1, 2);
|
|
setsid();
|
|
setpgrp();
|
|
i = 0;
|
|
#ifdef TIOCNOTTY
|
|
if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
|
|
ioctl(fd, TIOCNOTTY, &i);
|
|
close(fd);
|
|
}
|
|
#endif
|
|
/*
|
|
* Make the new pty our controlling terminal. On some OSes
|
|
* this is done with TIOCSCTTY; Cygwin doesn't have that, so
|
|
* instead it's done by simply opening the pty without
|
|
* O_NOCTTY. This code is primarily intended for Cygwin, but
|
|
* it's useful to have it work in other contexts for testing
|
|
* purposes, so I leave the TIOCSCTTY here anyway.
|
|
*/
|
|
if ((fd = open(ptyname, O_RDWR)) >= 0) {
|
|
#ifdef TIOCSCTTY
|
|
ioctl(fd, TIOCSCTTY, &i);
|
|
#endif
|
|
close(fd);
|
|
} else {
|
|
perror("slave pty: open");
|
|
exit(127);
|
|
}
|
|
tcsetpgrp(0, getpgrp());
|
|
|
|
for (i = 0; i < shdata->nenvvars; i++)
|
|
putenv(shdata->envvars[i]);
|
|
if (shdata->termtype)
|
|
putenv(shdata->termtype);
|
|
|
|
if (directory)
|
|
chdir(directory);
|
|
|
|
/*
|
|
* Use the provided shell program name, if the user gave
|
|
* one. Failing that, use $SHELL; failing that, look up
|
|
* the user's default shell in the password file; failing
|
|
* _that_, revert to the bog-standard /bin/sh.
|
|
*/
|
|
if (!program_args) {
|
|
char *shell;
|
|
|
|
shell = getenv("SHELL");
|
|
if (!shell) {
|
|
const char *login;
|
|
uid_t uid;
|
|
struct passwd *pwd;
|
|
|
|
/*
|
|
* For maximum generality in the face of multiple
|
|
* /etc/passwd entries with different login names and
|
|
* shells but a shared uid, we start by using
|
|
* getpwnam(getlogin()) if it's available - but we
|
|
* insist that its uid must match our real one, or we
|
|
* give up and fall back to getpwuid(getuid()).
|
|
*/
|
|
uid = getuid();
|
|
login = getlogin();
|
|
if (login && (pwd = getpwnam(login)) && pwd->pw_uid == uid)
|
|
shell = pwd->pw_shell;
|
|
else if ((pwd = getpwuid(uid)))
|
|
shell = pwd->pw_shell;
|
|
}
|
|
if (!shell)
|
|
shell = "/bin/sh";
|
|
|
|
fallback_args[0] = shell;
|
|
fallback_args[1] = NULL;
|
|
program_args = fallback_args;
|
|
}
|
|
|
|
execv(program_args[0], program_args);
|
|
|
|
/*
|
|
* If we're here, exec has gone badly foom.
|
|
*/
|
|
perror("exec");
|
|
exit(127);
|
|
}
|
|
|
|
close(slave);
|
|
|
|
return master;
|
|
}
|