version 1.236, 2008/05/08 12:02:23 |
version 1.237, 2008/05/08 12:21:16 |
|
|
void session_pty_cleanup(Session *); |
void session_pty_cleanup(Session *); |
void session_proctitle(Session *); |
void session_proctitle(Session *); |
int session_setup_x11fwd(Session *); |
int session_setup_x11fwd(Session *); |
void do_exec_pty(Session *, const char *); |
int do_exec_pty(Session *, const char *); |
void do_exec_no_pty(Session *, const char *); |
int do_exec_no_pty(Session *, const char *); |
void do_exec(Session *, const char *); |
int do_exec(Session *, const char *); |
void do_login(Session *, const char *); |
void do_login(Session *, const char *); |
void do_child(Session *, const char *); |
void do_child(Session *, const char *); |
void do_motd(void); |
void do_motd(void); |
|
|
const char *original_command = NULL; |
const char *original_command = NULL; |
|
|
/* data */ |
/* data */ |
#define MAX_SESSIONS 10 |
static int sessions_first_unused = -1; |
Session sessions[MAX_SESSIONS]; |
static int sessions_nalloc = 0; |
|
static Session *sessions = NULL; |
|
|
#define SUBSYSTEM_NONE 0 |
#define SUBSYSTEM_NONE 0 |
#define SUBSYSTEM_EXT 1 |
#define SUBSYSTEM_EXT 1 |
|
|
auth_input_request_forwarding(struct passwd * pw) |
auth_input_request_forwarding(struct passwd * pw) |
{ |
{ |
Channel *nc; |
Channel *nc; |
int sock; |
int sock = -1; |
struct sockaddr_un sunaddr; |
struct sockaddr_un sunaddr; |
|
|
if (auth_sock_name != NULL) { |
if (auth_sock_name != NULL) { |
|
|
temporarily_use_uid(pw); |
temporarily_use_uid(pw); |
|
|
/* Allocate a buffer for the socket name, and format the name. */ |
/* Allocate a buffer for the socket name, and format the name. */ |
auth_sock_name = xmalloc(MAXPATHLEN); |
auth_sock_dir = xstrdup("/tmp/ssh-XXXXXXXXXX"); |
auth_sock_dir = xmalloc(MAXPATHLEN); |
|
strlcpy(auth_sock_dir, "/tmp/ssh-XXXXXXXXXX", MAXPATHLEN); |
|
|
|
/* Create private directory for socket */ |
/* Create private directory for socket */ |
if (mkdtemp(auth_sock_dir) == NULL) { |
if (mkdtemp(auth_sock_dir) == NULL) { |
packet_send_debug("Agent forwarding disabled: " |
packet_send_debug("Agent forwarding disabled: " |
"mkdtemp() failed: %.100s", strerror(errno)); |
"mkdtemp() failed: %.100s", strerror(errno)); |
restore_uid(); |
restore_uid(); |
xfree(auth_sock_name); |
|
xfree(auth_sock_dir); |
xfree(auth_sock_dir); |
auth_sock_name = NULL; |
|
auth_sock_dir = NULL; |
auth_sock_dir = NULL; |
return 0; |
goto authsock_err; |
} |
} |
snprintf(auth_sock_name, MAXPATHLEN, "%s/agent.%ld", |
|
auth_sock_dir, (long) getpid()); |
|
|
|
|
xasprintf(&auth_sock_name, "%s/agent.%ld", |
|
auth_sock_dir, (long) getpid()); |
|
|
/* Create the socket. */ |
/* Create the socket. */ |
sock = socket(AF_UNIX, SOCK_STREAM, 0); |
sock = socket(AF_UNIX, SOCK_STREAM, 0); |
if (sock < 0) |
if (sock < 0) { |
packet_disconnect("socket: %.100s", strerror(errno)); |
error("socket: %.100s", strerror(errno)); |
|
restore_uid(); |
|
goto authsock_err; |
|
} |
|
|
/* Bind it to the name. */ |
/* Bind it to the name. */ |
memset(&sunaddr, 0, sizeof(sunaddr)); |
memset(&sunaddr, 0, sizeof(sunaddr)); |
sunaddr.sun_family = AF_UNIX; |
sunaddr.sun_family = AF_UNIX; |
strlcpy(sunaddr.sun_path, auth_sock_name, sizeof(sunaddr.sun_path)); |
strlcpy(sunaddr.sun_path, auth_sock_name, sizeof(sunaddr.sun_path)); |
|
|
if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) |
if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) { |
packet_disconnect("bind: %.100s", strerror(errno)); |
error("bind: %.100s", strerror(errno)); |
|
restore_uid(); |
|
goto authsock_err; |
|
} |
|
|
/* Restore the privileged uid. */ |
/* Restore the privileged uid. */ |
restore_uid(); |
restore_uid(); |
|
|
/* Start listening on the socket. */ |
/* Start listening on the socket. */ |
if (listen(sock, SSH_LISTEN_BACKLOG) < 0) |
if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { |
packet_disconnect("listen: %.100s", strerror(errno)); |
error("listen: %.100s", strerror(errno)); |
|
goto authsock_err; |
|
} |
|
|
/* Allocate a channel for the authentication agent socket. */ |
/* Allocate a channel for the authentication agent socket. */ |
nc = channel_new("auth socket", |
nc = channel_new("auth socket", |
|
|
0, "auth socket", 1); |
0, "auth socket", 1); |
strlcpy(nc->path, auth_sock_name, sizeof(nc->path)); |
strlcpy(nc->path, auth_sock_name, sizeof(nc->path)); |
return 1; |
return 1; |
|
|
|
authsock_err: |
|
if (auth_sock_name != NULL) |
|
xfree(auth_sock_name); |
|
if (auth_sock_dir != NULL) { |
|
rmdir(auth_sock_dir); |
|
xfree(auth_sock_dir); |
|
} |
|
if (sock != -1) |
|
close(sock); |
|
auth_sock_name = NULL; |
|
auth_sock_dir = NULL; |
|
return 0; |
} |
} |
|
|
static void |
static void |
|
|
if (type == SSH_CMSG_EXEC_CMD) { |
if (type == SSH_CMSG_EXEC_CMD) { |
command = packet_get_string(&dlen); |
command = packet_get_string(&dlen); |
debug("Exec command '%.500s'", command); |
debug("Exec command '%.500s'", command); |
do_exec(s, command); |
if (do_exec(s, command) != 0) |
|
packet_disconnect( |
|
"command execution failed"); |
xfree(command); |
xfree(command); |
} else { |
} else { |
do_exec(s, NULL); |
if (do_exec(s, NULL) != 0) |
|
packet_disconnect( |
|
"shell execution failed"); |
} |
} |
packet_check_eom(); |
packet_check_eom(); |
session_close(s); |
session_close(s); |
|
|
* will call do_child from the child, and server_loop from the parent after |
* will call do_child from the child, and server_loop from the parent after |
* setting up file descriptors and such. |
* setting up file descriptors and such. |
*/ |
*/ |
void |
int |
do_exec_no_pty(Session *s, const char *command) |
do_exec_no_pty(Session *s, const char *command) |
{ |
{ |
pid_t pid; |
pid_t pid; |
|
|
int inout[2], err[2]; |
int inout[2], err[2]; |
|
|
/* Uses socket pairs to communicate with the program. */ |
/* Uses socket pairs to communicate with the program. */ |
if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0 || |
if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0) { |
socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0) |
error("%s: socketpair #1: %.100s", __func__, strerror(errno)); |
packet_disconnect("Could not create socket pairs: %.100s", |
return -1; |
strerror(errno)); |
} |
|
if (socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0) { |
|
error("%s: socketpair #2: %.100s", __func__, strerror(errno)); |
|
close(inout[0]); |
|
close(inout[1]); |
|
return -1; |
|
} |
|
|
if (s == NULL) |
if (s == NULL) |
fatal("do_exec_no_pty: no session"); |
fatal("do_exec_no_pty: no session"); |
|
|
session_proctitle(s); |
session_proctitle(s); |
|
|
/* Fork the child. */ |
/* Fork the child. */ |
if ((pid = fork()) == 0) { |
switch ((pid = fork())) { |
|
case -1: |
|
error("%s: fork: %.100s", __func__, strerror(errno)); |
|
close(inout[0]); |
|
close(inout[1]); |
|
close(err[0]); |
|
close(err[1]); |
|
return -1; |
|
case 0: |
is_child = 1; |
is_child = 1; |
|
|
/* Child. Reinitialize the log since the pid has changed. */ |
/* Child. Reinitialize the log since the pid has changed. */ |
log_init(__progname, options.log_level, options.log_facility, log_stderr); |
log_init(__progname, options.log_level, |
|
options.log_facility, log_stderr); |
|
|
/* |
/* |
* Create a new session and process group since the 4.4BSD |
* Create a new session and process group since the 4.4BSD |
|
|
close(err[1]); |
close(err[1]); |
if (dup2(inout[0], 0) < 0) /* stdin */ |
if (dup2(inout[0], 0) < 0) /* stdin */ |
perror("dup2 stdin"); |
perror("dup2 stdin"); |
if (dup2(inout[0], 1) < 0) /* stdout. Note: same socket as stdin. */ |
if (dup2(inout[0], 1) < 0) /* stdout (same as stdin) */ |
perror("dup2 stdout"); |
perror("dup2 stdout"); |
if (dup2(err[0], 2) < 0) /* stderr */ |
if (dup2(err[0], 2) < 0) /* stderr */ |
perror("dup2 stderr"); |
perror("dup2 stderr"); |
|
|
/* Do processing for the child (exec command etc). */ |
/* Do processing for the child (exec command etc). */ |
do_child(s, command); |
do_child(s, command); |
/* NOTREACHED */ |
/* NOTREACHED */ |
|
default: |
|
break; |
} |
} |
if (pid < 0) |
|
packet_disconnect("fork failed: %.100s", strerror(errno)); |
|
s->pid = pid; |
s->pid = pid; |
/* Set interactive/non-interactive mode. */ |
/* Set interactive/non-interactive mode. */ |
packet_set_interactive(s->display != NULL); |
packet_set_interactive(s->display != NULL); |
|
|
* handle the case that fdin and fdout are the same. |
* handle the case that fdin and fdout are the same. |
*/ |
*/ |
if (compat20) { |
if (compat20) { |
session_set_fds(s, inout[1], inout[1], s->is_subsystem ? -1 : err[1]); |
session_set_fds(s, inout[1], inout[1], |
|
s->is_subsystem ? -1 : err[1]); |
|
if (s->is_subsystem) |
|
close(err[1]); |
} else { |
} else { |
server_loop(pid, inout[1], inout[1], err[1]); |
server_loop(pid, inout[1], inout[1], err[1]); |
/* server_loop has closed inout[1] and err[1]. */ |
/* server_loop has closed inout[1] and err[1]. */ |
} |
} |
|
return 0; |
} |
} |
|
|
/* |
/* |
|
|
* setting up file descriptors, controlling tty, updating wtmp, utmp, |
* setting up file descriptors, controlling tty, updating wtmp, utmp, |
* lastlog, and other such operations. |
* lastlog, and other such operations. |
*/ |
*/ |
void |
int |
do_exec_pty(Session *s, const char *command) |
do_exec_pty(Session *s, const char *command) |
{ |
{ |
int fdout, ptyfd, ttyfd, ptymaster; |
int fdout, ptyfd, ttyfd, ptymaster; |
|
|
ptyfd = s->ptyfd; |
ptyfd = s->ptyfd; |
ttyfd = s->ttyfd; |
ttyfd = s->ttyfd; |
|
|
|
/* |
|
* Create another descriptor of the pty master side for use as the |
|
* standard input. We could use the original descriptor, but this |
|
* simplifies code in server_loop. The descriptor is bidirectional. |
|
* Do this before forking (and cleanup in the child) so as to |
|
* detect and gracefully fail out-of-fd conditions. |
|
*/ |
|
if ((fdout = dup(ptyfd)) < 0) { |
|
error("%s: dup #1: %s", __func__, strerror(errno)); |
|
close(ttyfd); |
|
close(ptyfd); |
|
return -1; |
|
} |
|
/* we keep a reference to the pty master */ |
|
if ((ptymaster = dup(ptyfd)) < 0) { |
|
error("%s: dup #2: %s", __func__, strerror(errno)); |
|
close(ttyfd); |
|
close(ptyfd); |
|
close(fdout); |
|
return -1; |
|
} |
|
|
/* Fork the child. */ |
/* Fork the child. */ |
if ((pid = fork()) == 0) { |
switch ((pid = fork())) { |
|
case -1: |
|
error("%s: fork: %.100s", __func__, strerror(errno)); |
|
close(fdout); |
|
close(ptymaster); |
|
close(ttyfd); |
|
close(ptyfd); |
|
return -1; |
|
case 0: |
is_child = 1; |
is_child = 1; |
|
|
|
close(fdout); |
|
close(ptymaster); |
|
|
/* Child. Reinitialize the log because the pid has changed. */ |
/* Child. Reinitialize the log because the pid has changed. */ |
log_init(__progname, options.log_level, options.log_facility, log_stderr); |
log_init(__progname, options.log_level, |
|
options.log_facility, log_stderr); |
/* Close the master side of the pseudo tty. */ |
/* Close the master side of the pseudo tty. */ |
close(ptyfd); |
close(ptyfd); |
|
|
|
|
if (!(options.use_login && command == NULL)) |
if (!(options.use_login && command == NULL)) |
do_login(s, command); |
do_login(s, command); |
|
|
/* Do common processing for the child, such as execing the command. */ |
/* |
|
* Do common processing for the child, such as execing |
|
* the command. |
|
*/ |
do_child(s, command); |
do_child(s, command); |
/* NOTREACHED */ |
/* NOTREACHED */ |
|
default: |
|
break; |
} |
} |
if (pid < 0) |
|
packet_disconnect("fork failed: %.100s", strerror(errno)); |
|
s->pid = pid; |
s->pid = pid; |
|
|
/* Parent. Close the slave side of the pseudo tty. */ |
/* Parent. Close the slave side of the pseudo tty. */ |
close(ttyfd); |
close(ttyfd); |
|
|
/* |
|
* Create another descriptor of the pty master side for use as the |
|
* standard input. We could use the original descriptor, but this |
|
* simplifies code in server_loop. The descriptor is bidirectional. |
|
*/ |
|
fdout = dup(ptyfd); |
|
if (fdout < 0) |
|
packet_disconnect("dup #1 failed: %.100s", strerror(errno)); |
|
|
|
/* we keep a reference to the pty master */ |
|
ptymaster = dup(ptyfd); |
|
if (ptymaster < 0) |
|
packet_disconnect("dup #2 failed: %.100s", strerror(errno)); |
|
s->ptymaster = ptymaster; |
|
|
|
/* Enter interactive session. */ |
/* Enter interactive session. */ |
|
s->ptymaster = ptymaster; |
packet_set_interactive(1); |
packet_set_interactive(1); |
if (compat20) { |
if (compat20) { |
session_set_fds(s, ptyfd, fdout, -1); |
session_set_fds(s, ptyfd, fdout, -1); |
|
|
server_loop(pid, ptyfd, fdout, -1); |
server_loop(pid, ptyfd, fdout, -1); |
/* server_loop _has_ closed ptyfd and fdout. */ |
/* server_loop _has_ closed ptyfd and fdout. */ |
} |
} |
|
return 0; |
} |
} |
|
|
/* |
/* |
* This is called to fork and execute a command. If another command is |
* This is called to fork and execute a command. If another command is |
* to be forced, execute that instead. |
* to be forced, execute that instead. |
*/ |
*/ |
void |
int |
do_exec(Session *s, const char *command) |
do_exec(Session *s, const char *command) |
{ |
{ |
|
int ret; |
|
|
if (options.adm_forced_command) { |
if (options.adm_forced_command) { |
original_command = command; |
original_command = command; |
command = options.adm_forced_command; |
command = options.adm_forced_command; |
|
|
} |
} |
#endif |
#endif |
if (s->ttyfd != -1) |
if (s->ttyfd != -1) |
do_exec_pty(s, command); |
ret = do_exec_pty(s, command); |
else |
else |
do_exec_no_pty(s, command); |
ret = do_exec_no_pty(s, command); |
|
|
original_command = NULL; |
original_command = NULL; |
|
|
|
|
* multiple copies of the login messages. |
* multiple copies of the login messages. |
*/ |
*/ |
buffer_clear(&loginmsg); |
buffer_clear(&loginmsg); |
|
|
|
return ret; |
} |
} |
|
|
|
|
|
|
exit(1); |
exit(1); |
} |
} |
|
|
|
void |
|
session_unused(int id) |
|
{ |
|
debug3("%s: session id %d unused", __func__, id); |
|
if (id >= options.max_sessions || |
|
id >= sessions_nalloc) { |
|
fatal("%s: insane session id %d (max %d nalloc %d)", |
|
__func__, id, options.max_sessions, sessions_nalloc); |
|
} |
|
bzero(&sessions[id], sizeof(*sessions)); |
|
sessions[id].self = id; |
|
sessions[id].used = 0; |
|
sessions[id].chanid = -1; |
|
sessions[id].ptyfd = -1; |
|
sessions[id].ttyfd = -1; |
|
sessions[id].ptymaster = -1; |
|
sessions[id].x11_chanids = NULL; |
|
sessions[id].next_unused = sessions_first_unused; |
|
sessions_first_unused = id; |
|
} |
|
|
Session * |
Session * |
session_new(void) |
session_new(void) |
{ |
{ |
int i; |
Session *s, *tmp; |
static int did_init = 0; |
|
if (!did_init) { |
if (sessions_first_unused == -1) { |
debug("session_new: init"); |
if (sessions_nalloc >= options.max_sessions) |
for (i = 0; i < MAX_SESSIONS; i++) { |
return NULL; |
sessions[i].used = 0; |
debug2("%s: allocate (allocated %d max %d)", |
|
__func__, sessions_nalloc, options.max_sessions); |
|
tmp = xrealloc(sessions, sessions_nalloc + 1, |
|
sizeof(*sessions)); |
|
if (tmp == NULL) { |
|
error("%s: cannot allocate %d sessions", |
|
__func__, sessions_nalloc + 1); |
|
return NULL; |
} |
} |
did_init = 1; |
sessions = tmp; |
|
session_unused(sessions_nalloc++); |
} |
} |
for (i = 0; i < MAX_SESSIONS; i++) { |
|
Session *s = &sessions[i]; |
if (sessions_first_unused >= sessions_nalloc || |
if (! s->used) { |
sessions_first_unused < 0) { |
memset(s, 0, sizeof(*s)); |
fatal("%s: insane first_unused %d max %d nalloc %d", |
s->chanid = -1; |
__func__, sessions_first_unused, options.max_sessions, |
s->ptyfd = -1; |
sessions_nalloc); |
s->ttyfd = -1; |
|
s->used = 1; |
|
s->self = i; |
|
s->x11_chanids = NULL; |
|
debug("session_new: session %d", i); |
|
return s; |
|
} |
|
} |
} |
return NULL; |
|
|
s = &sessions[sessions_first_unused]; |
|
if (s->used) { |
|
fatal("%s: session %d already used", |
|
__func__, sessions_first_unused); |
|
} |
|
sessions_first_unused = s->next_unused; |
|
s->used = 1; |
|
s->next_unused = -1; |
|
debug("session_new: session %d", s->self); |
|
|
|
return s; |
} |
} |
|
|
static void |
static void |
session_dump(void) |
session_dump(void) |
{ |
{ |
int i; |
int i; |
for (i = 0; i < MAX_SESSIONS; i++) { |
for (i = 0; i < sessions_nalloc; i++) { |
Session *s = &sessions[i]; |
Session *s = &sessions[i]; |
debug("dump: used %d session %d %p channel %d pid %ld", |
|
|
debug("dump: used %d next_unused %d session %d %p " |
|
"channel %d pid %ld", |
s->used, |
s->used, |
|
s->next_unused, |
s->self, |
s->self, |
s, |
s, |
s->chanid, |
s->chanid, |
|
|
session_by_tty(char *tty) |
session_by_tty(char *tty) |
{ |
{ |
int i; |
int i; |
for (i = 0; i < MAX_SESSIONS; i++) { |
for (i = 0; i < sessions_nalloc; i++) { |
Session *s = &sessions[i]; |
Session *s = &sessions[i]; |
if (s->used && s->ttyfd != -1 && strcmp(s->tty, tty) == 0) { |
if (s->used && s->ttyfd != -1 && strcmp(s->tty, tty) == 0) { |
debug("session_by_tty: session %d tty %s", i, tty); |
debug("session_by_tty: session %d tty %s", i, tty); |
|
|
session_by_channel(int id) |
session_by_channel(int id) |
{ |
{ |
int i; |
int i; |
for (i = 0; i < MAX_SESSIONS; i++) { |
for (i = 0; i < sessions_nalloc; i++) { |
Session *s = &sessions[i]; |
Session *s = &sessions[i]; |
if (s->used && s->chanid == id) { |
if (s->used && s->chanid == id) { |
debug("session_by_channel: session %d channel %d", i, id); |
debug("session_by_channel: session %d channel %d", |
|
i, id); |
return s; |
return s; |
} |
} |
} |
} |
|
|
{ |
{ |
int i, j; |
int i, j; |
|
|
for (i = 0; i < MAX_SESSIONS; i++) { |
for (i = 0; i < sessions_nalloc; i++) { |
Session *s = &sessions[i]; |
Session *s = &sessions[i]; |
|
|
if (s->x11_chanids == NULL || !s->used) |
if (s->x11_chanids == NULL || !s->used) |
|
|
{ |
{ |
int i; |
int i; |
debug("session_by_pid: pid %ld", (long)pid); |
debug("session_by_pid: pid %ld", (long)pid); |
for (i = 0; i < MAX_SESSIONS; i++) { |
for (i = 0; i < sessions_nalloc; i++) { |
Session *s = &sessions[i]; |
Session *s = &sessions[i]; |
if (s->used && s->pid == pid) |
if (s->used && s->pid == pid) |
return s; |
return s; |
|
|
|
|
/* Allocate a pty and open it. */ |
/* Allocate a pty and open it. */ |
debug("Allocating pty."); |
debug("Allocating pty."); |
if (!PRIVSEP(pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty)))) { |
if (!PRIVSEP(pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, |
|
sizeof(s->tty)))) { |
if (s->term) |
if (s->term) |
xfree(s->term); |
xfree(s->term); |
s->term = NULL; |
s->term = NULL; |
|
|
s->is_subsystem = SUBSYSTEM_EXT; |
s->is_subsystem = SUBSYSTEM_EXT; |
} |
} |
debug("subsystem: exec() %s", cmd); |
debug("subsystem: exec() %s", cmd); |
do_exec(s, cmd); |
success = do_exec(s, cmd) == 0; |
success = 1; |
|
break; |
break; |
} |
} |
} |
} |
|
|
session_shell_req(Session *s) |
session_shell_req(Session *s) |
{ |
{ |
packet_check_eom(); |
packet_check_eom(); |
do_exec(s, NULL); |
return do_exec(s, NULL) == 0; |
return 1; |
|
} |
} |
|
|
static int |
static int |
session_exec_req(Session *s) |
session_exec_req(Session *s) |
{ |
{ |
u_int len; |
u_int len, success; |
|
|
char *command = packet_get_string(&len); |
char *command = packet_get_string(&len); |
packet_check_eom(); |
packet_check_eom(); |
do_exec(s, command); |
success = do_exec(s, command) == 0; |
xfree(command); |
xfree(command); |
return 1; |
return success; |
} |
} |
|
|
static int |
static int |
|
|
packet_get_int(); /* ignored */ |
packet_get_int(); /* ignored */ |
packet_check_eom(); |
packet_check_eom(); |
|
|
if (s->ttyfd == -1 || |
if (s->ttyfd == -1 || tcsendbreak(s->ttyfd, 0) < 0) |
tcsendbreak(s->ttyfd, 0) < 0) |
|
return 0; |
return 0; |
return 1; |
return 1; |
} |
} |
|
|
* the pty cleanup, so that another process doesn't get this pty |
* the pty cleanup, so that another process doesn't get this pty |
* while we're still cleaning up. |
* while we're still cleaning up. |
*/ |
*/ |
if (close(s->ptymaster) < 0) |
if (s->ptymaster != -1 && close(s->ptymaster) < 0) |
error("close(s->ptymaster/%d): %s", s->ptymaster, strerror(errno)); |
error("close(s->ptymaster/%d): %s", |
|
s->ptymaster, strerror(errno)); |
|
|
/* unlink pty from session */ |
/* unlink pty from session */ |
s->ttyfd = -1; |
s->ttyfd = -1; |
|
|
xfree(s->auth_data); |
xfree(s->auth_data); |
if (s->auth_proto) |
if (s->auth_proto) |
xfree(s->auth_proto); |
xfree(s->auth_proto); |
s->used = 0; |
|
if (s->env != NULL) { |
if (s->env != NULL) { |
for (i = 0; i < s->num_env; i++) { |
for (i = 0; i < s->num_env; i++) { |
xfree(s->env[i].name); |
xfree(s->env[i].name); |
|
|
xfree(s->env); |
xfree(s->env); |
} |
} |
session_proctitle(s); |
session_proctitle(s); |
|
session_unused(s->self); |
} |
} |
|
|
void |
void |
|
|
session_destroy_all(void (*closefunc)(Session *)) |
session_destroy_all(void (*closefunc)(Session *)) |
{ |
{ |
int i; |
int i; |
for (i = 0; i < MAX_SESSIONS; i++) { |
for (i = 0; i < sessions_nalloc; i++) { |
Session *s = &sessions[i]; |
Session *s = &sessions[i]; |
if (s->used) { |
if (s->used) { |
if (closefunc != NULL) |
if (closefunc != NULL) |
|
|
static char buf[1024]; |
static char buf[1024]; |
int i; |
int i; |
buf[0] = '\0'; |
buf[0] = '\0'; |
for (i = 0; i < MAX_SESSIONS; i++) { |
for (i = 0; i < sessions_nalloc; i++) { |
Session *s = &sessions[i]; |
Session *s = &sessions[i]; |
if (s->used && s->ttyfd != -1) { |
if (s->used && s->ttyfd != -1) { |
if (buf[0] != '\0') |
if (buf[0] != '\0') |