client_session.c 6.49 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* client_session.c
 *
 * $Id$
 */

/* lsh, an implementation of the ssh protocol
 *
 * Copyright (C) 1998, 1999, 2000 Niels Mller
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

25
#include "client.h"
26
27
28
29

#include "channel_commands.h"
#include "client.h"
#include "io.h"
30
#include "read_data.h"
31
32
33
34
35
36
37
38
39
40
41
#include "ssh.h"
#include "werror.h"
#include "xalloc.h"

#include <assert.h>

#include "client_session.c.x"

/* Initiate and manage a session */
/* GABA:
   (class
42
     (name client_session_channel)
43
44
45
46
47
48
49
     (super ssh_channel)
     (vars
       ; To access stdio
       (in object lsh_fd)
       (out object lsh_fd)
       (err object lsh_fd)

50
51
       ; Escape char handling
       (escape object escape_info)
52
53
54
55
56
       ; Where to save the exit code.
       (exit_status . "int *")))
*/

/* Callback used when the server sends us eof */
Niels Möller's avatar
Niels Möller committed
57

58
59
60
static void
do_client_session_eof(struct ssh_channel *c)
{
61
  CAST(client_session_channel, session, c);
62

Niels Möller's avatar
Niels Möller committed
63
64
  close_fd_nicely(session->out);
  close_fd_nicely(session->err);
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
}  

static void
do_client_session_close(struct ssh_channel *c)
{
  static const struct exception finish_exception
    = STATIC_EXCEPTION(EXC_FINISH_PENDING, "Session closed.");

  EXCEPTION_RAISE(c->e, &finish_exception);
}


/* Receive channel data */
static void
do_receive(struct ssh_channel *c,
	   int type, struct lsh_string *data)
{
82
  CAST(client_session_channel, closure, c);
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
  
  switch(type)
    {
    case CHANNEL_DATA:
      A_WRITE(&closure->out->write_buffer->super, data);
      break;
    case CHANNEL_STDERR_DATA:
      A_WRITE(&closure->err->write_buffer->super, data);
      break;
    default:
      fatal("Internal error!\n");
    }
}

/* We may send more data */
static void
do_send_adjust(struct ssh_channel *s,
	       UINT32 i UNUSED)
{
102
  CAST(client_session_channel, self, s);
103
104
105

  assert(self->in->read);

106
  lsh_oop_register_read_fd(self->in);
107
108
}

Niels Möller's avatar
Niels Möller committed
109
110
111
/* Escape char handling */

static struct io_callback *
112
client_read_stdin(struct client_session_channel *session)
Niels Möller's avatar
Niels Möller committed
113
{
114
115
116
117
118
119
  struct abstract_write *write = make_channel_write(&session->super);

  if (session->escape)
    write = make_handle_escape(session->escape, write);
  
  return make_read_data(&session->super, write);
Niels Möller's avatar
Niels Möller committed
120
121
}

122
123
124
125
126
127
128
129
/* We have a remote shell */
static void
do_client_io(struct command *s UNUSED,
	     struct lsh_object *x,
	     struct command_continuation *c,
	     struct exception_handler *e UNUSED)

{
130
  CAST(client_session_channel, session, x);
131
132
133
134
135
136
137
  struct ssh_channel *channel = &session->super;
  assert(x);

  /* Set up write fd:s. */
  
  channel->receive = do_receive;

Niels Möller's avatar
Niels Möller committed
138
139
140
141
  /* FIXME: It seems a little kludgy to modify exception handlers
   * here; it would be better to create the fd-objects at a point
   * where the right exception handlers can be installed from the
   * start. */
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
  session->out->e
    = make_channel_io_exception_handler(channel,
					"lsh: I/O error on stdout",
					session->out->e,
					HANDLER_CONTEXT);

  session->err->e
    = make_channel_io_exception_handler(channel,
					"lsh: I/O error on stderr",
					session->err->e,
					HANDLER_CONTEXT);

  /* Set up the fd we read from. */
  channel->send_adjust = do_send_adjust;

157
158
  /* Setup escape char handler, if appropriate. */
  session->in->read = client_read_stdin(session);
Niels Möller's avatar
Niels Möller committed
159

160
161
162
  /* FIXME: Perhaps there is some way to arrange that channel.c calls
   * the CHANNEL_SEND_ADJUST method instead? */
  if (session->super.send_window_size)
163
    lsh_oop_register_read_fd(session->in);
Niels Möller's avatar
Niels Möller committed
164
165
166

  /* FIXME: We should also arrange so that the tty is reset before we
   * close it. */
167
168
169
170
  session->in->close_callback
    = make_channel_read_close_callback(channel);

  /* Make sure stdio is closed properly if the channel or connection dies */
171
172
173
  remember_resource(channel->resources, &session->in->super);
  remember_resource(channel->resources, &session->out->super);
  remember_resource(channel->resources, &session->err->super);
174
175
  
  ALIST_SET(channel->request_types, ATOM_EXIT_STATUS,
176
	    &make_handle_exit_status(session->exit_status)->super);
177
  ALIST_SET(channel->request_types, ATOM_EXIT_SIGNAL,
178
	    &make_handle_exit_signal(session->exit_status)->super);
179
180
181
182
183
184
185
186
187
188
189

  channel->eof = do_client_session_eof;
      
  COMMAND_RETURN(c, channel);
}

struct command client_io =
{ STATIC_HEADER, do_client_io };


struct ssh_channel *
190
191
192
make_client_session_channel(struct lsh_fd *in,
			    struct lsh_fd *out,
			    struct lsh_fd *err,
193
			    struct escape_info *escape,
194
195
			    UINT32 initial_window,
			    int *exit_status)
196
{
197
  NEW(client_session_channel, self);
198

199
  trace("make_client_session_channel\n");
200
201
202
203
204
  init_channel(&self->super);

  /* Makes sure the pending_close bit is set whenever this session
   * dies, no matter when or how. */
  self->super.close = do_client_session_close;
Niels Möller's avatar
Niels Möller committed
205

206
207
208
209
210
211
212
  /* We could miss the server's exit-status or exit-signal message if
   * we close the channel directly at EOF. So don't do that.
   *
   * FIXME: Perhaps we need to set this bit again in do_exit_status
   * and do_exit_signal. */
  self->super.flags &= ~CHANNEL_CLOSE_AT_EOF;
  
Niels Möller's avatar
Niels Möller committed
213
214
215
216
  /* FIXME: We make rec_window_size non-zero here, but we don't setup
   * the receive pointer until later, in do_client_io. That's bad. Do
   * something similar to server_session.c: Add an inital_window
   * attribute, and call channel_start_receive from client_io. */
217
218
219
220
221
222
223
224
225
226
227
  self->super.rec_window_size = initial_window;

  /* FIXME: Make maximum packet size configurable */
  self->super.rec_max_packet = SSH_MAX_PACKET - SSH_CHANNEL_MAX_PACKET_FUZZ;

  self->super.request_types = make_alist(0, -1);

  /* self->expect_close = 0; */
  self->in = in;
  self->out = out;
  self->err = err;
228
229
  self->escape = escape;
  
230
231
232
  remember_resource(self->super.resources, &in->super);
  remember_resource(self->super.resources, &out->super);
  remember_resource(self->super.resources, &err->super);
233
  
234
235
236
237
238
239
240
241
  /* Flow control */
  out->write_buffer->report = &self->super.super;
  err->write_buffer->report = &self->super.super;
  
  self->exit_status = exit_status;
  
  return &self->super;
}