client.c 29.6 KB
Newer Older
Niels Möller's avatar
Niels Möller committed
1
/* client.c
2
3
4
5
6
 *
 * $Id$ */

/* lsh, an implementation of the ssh protocol
 *
7
 * Copyright (C) 1998, 1999, 2000 Niels Mller
8
9
10
11
12
13
14
15
16
17
18
19
20
 *
 * 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
J.H.M. Dassen's avatar
J.H.M. Dassen committed
21
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Niels Möller's avatar
Niels Möller committed
22
23
24
 */

#include "client.h"
25

Niels Möller's avatar
Niels Möller committed
26
#include "abstract_io.h"
27
#include "channel.h"
28
#include "channel_commands.h"
29
#include "connection.h"
30
31
32
#include "format.h"
#include "interact.h"
#include "io.h"
Niels Möller's avatar
Niels Möller committed
33
#include "pad.h"
34
35
#include "parse.h"
#include "ssh.h"
36
#include "suspend.h"
37
#include "tcpforward_commands.h"
Niels Möller's avatar
Niels Möller committed
38
#include "translate_signal.h"
39
40
#include "werror.h"
#include "xalloc.h"
Niels Möller's avatar
Niels Möller committed
41

Niels Möller's avatar
Niels Möller committed
42
#include <string.h>
43
#include <assert.h>
Niels Möller's avatar
Niels Möller committed
44

45
46
47
48
49
50
51
52
53
54
55
56
#include <signal.h>

#if HAVE_UNISTD_H
#include <unistd.h>
#endif

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "lsh_argp.h"

57
58
59
60
#define GABA_DEFINE
#include "client.h.x"
#undef GABA_DEFINE

61
62
#include "client.c.x"

63
64
#define DEFAULT_ESCAPE_CHAR '~'

65
66
67
68
69
70
static struct lsh_string *
format_service_request(int name)
{
  return ssh_format("%c%a", SSH_MSG_SERVICE_REQUEST, name);
}

71
72
/* Start a service that the server has accepted (for instance
 * ssh-userauth). */
73
/* GABA:
74
75
76
77
   (class
     (name accept_service_handler)
     (super packet_handler)
     (vars
Niels Möller's avatar
Niels Möller committed
78
       (service . int)
79
       (c object command_continuation)))
80
81
*/

Niels Möller's avatar
Niels Möller committed
82
83
84
85
static void
do_accept_service(struct packet_handler *c,
		  struct ssh_connection *connection,
		  struct lsh_string *packet)
86
{
87
  CAST(accept_service_handler, closure, c);
88
89

  struct simple_buffer buffer;
90
  unsigned msg_number;
Niels Möller's avatar
Niels Möller committed
91
  int name;
92

93
94
  simple_buffer_init(&buffer, packet->length, packet->data);
  
95
96
  if (parse_uint8(&buffer, &msg_number)
      && (msg_number == SSH_MSG_SERVICE_ACCEPT)
97
98
99
100
101
102
103
104
105
      && (
#if DATAFELLOWS_WORKAROUNDS
	  (connection->peer_flags & PEER_SERVICE_ACCEPT_KLUDGE)
#else
	  0
#endif
	  || (parse_atom(&buffer, &name)
	      && (name == closure->service)))
      && parse_eod(&buffer))
106
    {
107
      connection->dispatch[SSH_MSG_SERVICE_ACCEPT] = &connection_fail_handler;
108
      
Niels Möller's avatar
Niels Möller committed
109
      COMMAND_RETURN(closure->c, connection);
110
    }
Niels Möller's avatar
Niels Möller committed
111
  else
112
    PROTOCOL_ERROR(connection->e, "Invalid SSH_MSG_SERVICE_ACCEPT message");
113
114
}

115
struct packet_handler *
116
make_accept_service_handler(UINT32 service,
117
			    struct command_continuation *c)
118
{
119
  NEW(accept_service_handler, closure);
120

Niels Möller's avatar
Niels Möller committed
121
  closure->super.handler = do_accept_service;
122
  closure->service = service;
123
  closure->c = c;
124
  
125
126
127
  return &closure->super;
}

128
void
Niels Möller's avatar
Niels Möller committed
129
130
131
do_request_service(struct command *s,
		   struct lsh_object *x,
		   struct command_continuation *c,
132
		   struct exception_handler *e UNUSED)
133
{
134
135
  CAST(request_service, self, s);
  CAST(ssh_connection, connection, x);
136
137
138

  /* NOTE: Uses the connection's exception handler, not the one passed
   * in. */
Niels Möller's avatar
Niels Möller committed
139
  connection->dispatch[SSH_MSG_SERVICE_ACCEPT]
140
    = make_accept_service_handler(self->service, c);
Niels Möller's avatar
Niels Möller committed
141
  
Niels Möller's avatar
Niels Möller committed
142
143
  C_WRITE(connection,
	  format_service_request(self->service));
144
145
}

146
147
struct command *
make_request_service(int service)
148
{
149
  NEW(request_service, closure);
150

151
  closure->super.call = do_request_service;
152
153
154
155
156
  closure->service = service;

  return &closure->super;
}

157

158
/* GABA:
159
160
161
162
   (class
     (name exit_handler)
     (super channel_request)
     (vars
Niels Möller's avatar
Niels Möller committed
163
       (exit_status . "int *")))
164
165
*/

Niels Möller's avatar
Niels Möller committed
166
167
168
static void
do_exit_status(struct channel_request *c,
	       struct ssh_channel *channel,
Niels Möller's avatar
Niels Möller committed
169
	       struct channel_request_info *info,
170
171
172
	       struct simple_buffer *args,
	       struct command_continuation *s,
	       struct exception_handler *e)
Niels Möller's avatar
Niels Möller committed
173
{
174
  CAST(exit_handler, closure, c);
175
  UINT32 status;
Niels Möller's avatar
Niels Möller committed
176

Niels Möller's avatar
Niels Möller committed
177
  if (!info->want_reply
Niels Möller's avatar
Niels Möller committed
178
179
180
      && parse_uint32(args, &status)
      && parse_eod(args))
    {
181
182
183
      verbose("client.c: Receiving exit-status %i on channel %i\n",
	      status, channel->channel_number);
      
Niels Möller's avatar
Niels Möller committed
184
185
      *closure->exit_status = status;

186
187
      ALIST_SET(channel->request_types, ATOM_EXIT_STATUS, NULL);
      ALIST_SET(channel->request_types, ATOM_EXIT_SIGNAL, NULL);
Niels Möller's avatar
Niels Möller committed
188

189
      COMMAND_RETURN(s, NULL);
Niels Möller's avatar
Niels Möller committed
190
    }
Niels Möller's avatar
Niels Möller committed
191
192
  else
    /* Invalid request */
193
    PROTOCOL_ERROR(e, "Invalid exit-status message");
Niels Möller's avatar
Niels Möller committed
194
195
}

Niels Möller's avatar
Niels Möller committed
196
197
198
static void
do_exit_signal(struct channel_request *c,
	       struct ssh_channel *channel,
Niels Möller's avatar
Niels Möller committed
199
	       struct channel_request_info *info,
200
201
202
	       struct simple_buffer *args,
	       struct command_continuation *s,
	       struct exception_handler *e)
Niels Möller's avatar
Niels Möller committed
203
{
204
205
  CAST(exit_handler, closure, c);

206
  UINT32 signal;
Niels Möller's avatar
Niels Möller committed
207
208
  int core;

Niels Möller's avatar
Niels Möller committed
209
  const UINT8 *msg;
Niels Möller's avatar
Niels Möller committed
210
211
  UINT32 length;

Niels Möller's avatar
Niels Möller committed
212
  const UINT8 *language;
Niels Möller's avatar
Niels Möller committed
213
214
  UINT32 language_length;
  
Niels Möller's avatar
Niels Möller committed
215
  if (!info->want_reply
216
      && parse_atom(args, &signal)
Niels Möller's avatar
Niels Möller committed
217
218
219
220
221
222
223
224
225
226
      && parse_boolean(args, &core)
      && parse_string(args, &length, &msg)
      && parse_string(args, &language_length, &language)
      && parse_eod(args))
    {
      /* FIXME: What exit status should be returned when the remote
       * process dies violently? */

      *closure->exit_status = 7;

227
228
      werror("Remote process was killed by signal: %ups %z\n",
	     length, msg,
229
	     core ? "(core dumped remotely)\n": "");
230
      
231
232
      ALIST_SET(channel->request_types, ATOM_EXIT_STATUS, NULL);
      ALIST_SET(channel->request_types, ATOM_EXIT_SIGNAL, NULL);
Niels Möller's avatar
Niels Möller committed
233

234
      COMMAND_RETURN(s, NULL);
Niels Möller's avatar
Niels Möller committed
235
    }
Niels Möller's avatar
Niels Möller committed
236
237
  else
    /* Invalid request */
238
    PROTOCOL_ERROR(e, "Invalid exit-signal message");
Niels Möller's avatar
Niels Möller committed
239
240
}

241
242
struct channel_request *
make_handle_exit_status(int *exit_status)
Niels Möller's avatar
Niels Möller committed
243
{
244
  NEW(exit_handler, self);
Niels Möller's avatar
Niels Möller committed
245
246
247
248
249
250
251
252

  self->super.handler = do_exit_status;

  self->exit_status = exit_status;

  return &self->super;
}

253
254
struct channel_request *
make_handle_exit_signal(int *exit_status)
Niels Möller's avatar
Niels Möller committed
255
{
256
  NEW(exit_handler, self);
Niels Möller's avatar
Niels Möller committed
257
258
259
260
261
262
263
264

  self->super.handler = do_exit_signal;

  self->exit_status = exit_status;

  return &self->super;
}

265
266
267
268
269
270
271
/* GABA:
   (class
     (name session_open_command)
     (super channel_open_command)
     (vars
       ; This command can only be executed once,
       ; so we can allocate the session object in advance.
272
       (session object ssh_channel)))
273
274
275
276
277
*/

static struct ssh_channel *
new_session(struct channel_open_command *s,
	    struct ssh_connection *connection,
278
	    UINT32 local_channel_number,
279
280
281
282
	    struct lsh_string **request)
{
  CAST(session_open_command, self, s);
  struct ssh_channel *res;
283

284
  self->session->connection = connection;
285
  
286
287
288
  *request = format_channel_open(ATOM_SESSION,
				 local_channel_number,
				 self->session, "");
289
  
290
  res = self->session;
291
292
293
294
295
296
297

  /* Make sure this command can not be invoked again */
  self->session = NULL;

  return res;
}

298
299
struct command *
make_open_session_command(struct ssh_channel *session)
300
301
302
303
304
{
  NEW(session_open_command, self);
  self->super.super.call = do_channel_open_command;
  self->super.new_channel = new_session;
  self->session = session;
305
306

  return &self->super.super;
307
308
}

309
310
311
312
313
314
315
316

static struct lsh_string *
do_format_shell_request(struct channel_request_command *s UNUSED,
			struct ssh_channel *channel,
			struct command_continuation **c)
{
  return format_channel_request(ATOM_SHELL, channel, !!*c, "");
}
317

318
319
struct channel_request_command request_shell =
{ { STATIC_HEADER, do_channel_request_command }, do_format_shell_request };
320

321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353

/* GABA:
   (class
     (name exec_request)
     (super channel_request_command)
     (vars
       (command string)))
*/

static struct lsh_string *
do_format_exec_request(struct channel_request_command *s,
		       struct ssh_channel *channel,
		       struct command_continuation **c)
{
  CAST(exec_request, self, s);

  verbose("lsh: Requesting remote exec.\n");

  return format_channel_request(ATOM_EXEC, channel,
				!!*c, "%S", self->command);
}

struct command *
make_exec_request(struct lsh_string *command)
{
  NEW(exec_request, req);

  req->super.format_request = do_format_exec_request;
  req->super.super.call = do_channel_request_command;
  req->command = command;

  return &req->super.super;
}
354

355

356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
/* GABA:
   (class
     (name subsystem_request)
     (super channel_request_command)
     (vars
       (subsystem string)))
*/

static struct lsh_string *
do_format_subsystem_request(struct channel_request_command *s,
			    struct ssh_channel *channel,
			    struct command_continuation **c)
{
  CAST(subsystem_request, self, s);

  verbose("lsh: Requesting remote subsystem.\n");

  return format_channel_request(ATOM_SUBSYSTEM, channel,
				!!*c, "%S", self->subsystem);
}

struct command *
make_subsystem_request(struct lsh_string *subsystem)
{
  NEW(subsystem_request, req);

  req->super.format_request = do_format_subsystem_request;
  req->super.super.call = do_channel_request_command;
  req->subsystem = subsystem;

  return &req->super.super;
}


390
/* Handling of options and operations shared by the plain lsh client
391
 * and lshg. */
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413

/* Forward declaration */

static struct ssh_channel *
make_client_session(struct client_options *options);

/* Block size for stdout and stderr buffers */
#define BLOCK_SIZE 32768

/* Window size for the session channel
 *
 * NOTE: Large windows seem to trig a bug in sshd2. */
#define WINDOW_SIZE 10000

#define ARG_NOT 0x400

#define OPT_STDIN 0x210
#define OPT_STDOUT 0x211
#define OPT_STDERR 0x212

#define OPT_FORK_STDIO 0x213

414
415
#define OPT_SUBSYSTEM 0x214

416
417
void
init_client_options(struct client_options *self,
418
		    struct randomness *random,
419
420
421
		    struct exception_handler *handler,
		    int *exit_code)			 
{
422
423
  self->random = random;
  
424
  self->tty = make_unix_interact();
425
426
  self->escape = -1;
  
427
428
429
430
431
  self->handler = handler;

  self->exit_code = exit_code;
  
  self->not = 0;
432
  self->port = NULL;
433
434
435
436
437
438
  self->remote = NULL;

  self->local_user = self->user = getenv("LOGNAME");

  self->with_remote_peers = 0; 
  self->with_pty = -1;
439
440
  self->with_x11 = 0;
    
441
442
443
444
445
446
  self->stdin_file = NULL;
  self->stdout_file = NULL;
  self->stderr_file = NULL;

  self->stdin_fork = 0;
  self->stdout_fork = 0;
447
  self->stderr_fork = 1;
448
449
450

  self->used_stdin = 0;
  self->used_pty = 0;
451
452
  self->used_x11 = 0;
  
453
  self->start_shell = 1;
454
  self->remote_forward = 0;
455
456
457
458
459

  object_queue_init(&self->actions);  
}

/* Host to connect to */
460
461
462
463
464
DEFINE_COMMAND(client_options2remote)
     (struct command *s UNUSED,
      struct lsh_object *a,
      struct command_continuation *c,
      struct exception_handler *e UNUSED)
465
466
467
468
{
  CAST_SUBTYPE(client_options, options, a);
  trace("client.c: client_options2remote\n");
  
469
  COMMAND_RETURN(c, options->remote);
470
471
472
}

/* Host to connect to */
473
474
475
476
477
DEFINE_COMMAND(client_options2actions)
     (struct command *s UNUSED,
      struct lsh_object *a,
      struct command_continuation *c,
      struct exception_handler *e UNUSED)
478
479
480
481
482
483
{
  CAST_SUBTYPE(client_options, options, a);

  trace("client.c: client_options2actions, %i actions\n",
	options->actions.length);
  
484
  COMMAND_RETURN(c, queue_to_list(&options->actions));
485
486
487
488
489
490
491
492
493
494
495
}

static const struct argp_option
client_options[] =
{
  /* Name, key, arg-name, flags, doc, group */
  { "port", 'p', "Port", 0, "Connect to this port.", 0 },
  { "user", 'l', "User name", 0, "Login as this user.", 0 },

  { NULL, 0, NULL, 0, "Actions:", CLIENT_ARGP_ACTION_GROUP },
  { "forward-local-port", 'L', "local-port:target-host:target-port", 0, "", 0 },
496
#if 0
497
498
499
500
  { "forward-remote-port", 'R', "remote-port:target-host:target-port", 0, "", 0 },
#endif
  { "nop", 'N', NULL, 0, "No operation (suppresses the default action, "
    "which is to spawn a remote shell)", 0 },
501
  { "background", 'B', NULL, 0, "Put process into the background. Implies -N.", 0 },
502
503
  { "execute", 'E', "command", 0, "Execute a command on the remote machine", 0 },
  { "shell", 'S', "command", 0, "Spawn a remote shell", 0 },
504
  { "subsystem", 'OPT_SUBSYSTEM', "subsystem-name", 0,
505
#if WITH_PTY_SUPPORT 
506
    "Connect to given subsystem. Implies --no-pty.",
507
#else
508
    "Connect to given subsystem.",
509
#endif
510
    0 },
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
  /* { "gateway", 'G', NULL, 0, "Setup a local gateway", 0 }, */
  { NULL, 0, NULL, 0, "Universal not:", 0 },
  { "no", 'n', NULL, 0, "Inverts the effect of the next modifier", 0 },

  { NULL, 0, NULL, 0, "Modifiers that apply to port forwarding:",
    CLIENT_ARGP_MODIFIER_GROUP - 10 },
  { "remote-peers", 'g', NULL, 0, "Allow remote access to forwarded ports", 0 },
  { "no-remote-peers", 'g' | ARG_NOT, NULL, 0, 
    "Disallow remote access to forwarded ports (default).", 0 },

  { NULL, 0, NULL, 0, "Modifiers that apply to remote execution:", 0 },
  { "stdin", OPT_STDIN, "Filename", 0, "Redirect stdin", 0},
  { "no-stdin", OPT_STDIN | ARG_NOT, NULL, 0, "Redirect stdin from /dev/null", 0}, 
  { "stdout", OPT_STDOUT, "Filename", 0, "Redirect stdout", 0},
  { "no-stdout", OPT_STDOUT | ARG_NOT, NULL, 0, "Redirect stdout to /dev/null", 0}, 
  { "stderr", OPT_STDERR, "Filename", 0, "Redirect stderr", 0},
  { "no-stderr", OPT_STDERR | ARG_NOT, NULL, 0, "Redirect stderr to /dev/null", 0}, 
528

529
  { "cvs-workaround", OPT_FORK_STDIO, "i?o?e?", OPTION_ARG_OPTIONAL,
530
531
532
533
534
535
    "Avoid setting one or more of the stdio file descriptors into "
    "non-blocking mode. If no argument is provided, the workaround is "
    "applied to all three file descriptors. By default, the workaround "
    "is applied to stderr only.", 0 },
  { "no-cvs-workaround", OPT_FORK_STDIO | ARG_NOT, NULL, 0,
    "Disable the cvs workaround.", 0 },
536

537
538
539
540
#if WITH_PTY_SUPPORT
  { "pty", 't', NULL, 0, "Request a remote pty (default).", 0 },
  { "no-pty", 't' | ARG_NOT, NULL, 0, "Don't request a remote pty.", 0 },
#endif /* WITH_PTY_SUPPORT */
541
542
  { "escape-char", 'e', "Character", 0, "Escape char. `none' means disable. "
    "Default is to use `~' if we have a tty, otherwise none.", 0 },
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
  { NULL, 0, NULL, 0, NULL, 0 }
};


/* GABA:
   (expr
     (name make_start_session)
     (params
       (open_session object command)
       (requests object object_list))
     (expr (lambda (connection)
       ((progn requests)
         ; Create a "session" channel
         (open_session connection)))))
*/
558

559
560
561
562
563
564
565
566
567
/* Requests a shell or command, and connects the channel to our stdio. */
/* GABA:
   (expr
     (name client_start_session)
     (params
       (request object command))
     (expr
       (lambda (session)
          (client_start_io (request session)))))
568
569
*/

570
571
572
573
574
575
576
static struct command *
make_client_start_session(struct command *request)
{
  CAST_SUBTYPE(command, r, client_start_session(request));
  return r;
}

577
578
static void
client_maybe_pty(struct client_options *options,
579
		 int default_pty,
580
		 struct object_queue *q)
581
582
{
#if WITH_PTY_SUPPORT
583
584
585
586
587
  int with_pty = options->with_pty;
  if (with_pty < 0)
    with_pty = default_pty;
  
  if (with_pty && !options->used_pty)
588
589
590
591
592
    {
      options->used_pty = 1;
      
      if (options->tty && INTERACT_IS_TTY(options->tty))
	{
593
594
595
596
597
598
599
600
601
602
603
	  struct command *get_pty = make_pty_request(options->tty);

	  if (get_pty)
	    object_queue_add_tail(q,
				  /* Ignore EXC_CHANNEL_REQUEST for the pty allocation call. */
				  &make_catch_apply
				  (make_catch_handler_info(EXC_ALL, EXC_CHANNEL_REQUEST,
							   0, NULL),
				   get_pty)->super);
	  else
	    werror("lsh: Can't use tty (probably getattr or atexit failed).\n");
604
605
	}
      else
606
607
608
609
610
611
612
613
614
615
616
617
	werror("lsh: No tty available.\n");
    }
#endif
}

static void
client_maybe_x11(struct client_options *options,
		 struct object_queue *q)
{
  if (options->with_x11)
    {
      char *display = getenv("DISPLAY");
618
619
      struct command *request = NULL;
      
620
      assert(options->random);
621
      if (display)
622
	request = make_forward_x11(display, options->random);
623
	  
624
625
626
627
      if (request)
	{
	  object_queue_add_tail(q, &request->super);
	  options->used_x11 = 1;
628
	}
629
630
      else
	werror("Can't find any local X11 display to forward.\n");
631
    }
632
}
633

634
635
636
637
638
/* Create an interactive session */
static struct command *
client_shell_session(struct client_options *options)
{
  struct ssh_channel *session = make_client_session(options);
639
  
640
641
642
  if (session)
    {
      struct object_queue session_requests;
643

644
      object_queue_init(&session_requests);
645

646
      client_maybe_pty(options, 1, &session_requests);
647
648
649
650
651
652
653
654
655
656
657
658
659
      client_maybe_x11(options, &session_requests);
  
      object_queue_add_tail(&session_requests,
			    &make_client_start_session(&request_shell.super)->super);
  
      {
	CAST_SUBTYPE(command, r,
		     make_start_session
		     (make_open_session_command(session),
		      queue_to_list_and_kill(&session_requests)));
	return r;
      }
    }
660
  else
661
    return NULL;
662
663
}

664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
/* Create a session for a subsystem */
static struct command *
client_subsystem_session(struct client_options *options,
		       struct lsh_string *subsystem)
{
  struct ssh_channel *session = make_client_session(options);
  
  if (session)
    {
      CAST_SUBTYPE(command, r,
		   make_start_session
		   (make_open_session_command(session),
		    make_object_list(1,
				     make_client_start_session
				     (make_subsystem_request(subsystem)),
				     -1)));
      return r;
    }
  
  return NULL;
}

686
687
688
689
/* Create a session executing a command line */
static struct command *
client_command_session(struct client_options *options,
		       struct lsh_string *command)
690
{
691
  struct ssh_channel *session = make_client_session(options);
692
  
693
694
  if (session)
    {
695
696
697
698
      struct object_queue session_requests;
    
      object_queue_init(&session_requests);
  
699
700
      /* NOTE: Doesn't ask for a pty by default. That's traditional
       * behaviour, although perhaps not the Right Thing. */
701
      
702
      client_maybe_pty(options, 0, &session_requests);
703
704
705
706
707
708
709
710
711
712
713
      client_maybe_x11(options, &session_requests);

      object_queue_add_tail(&session_requests,
			    &make_client_start_session(make_exec_request(command))->super);
      {
	CAST_SUBTYPE(command, r,
		     make_start_session
		     (make_open_session_command(session),
		      queue_to_list_and_kill(&session_requests)));
	return r;
      }
714
715
    }
  
716
717
718
  return NULL;
}

719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
struct command *
client_add_action(struct client_options *options,
		  struct command *action)
{
  if (action)
    object_queue_add_tail(&options->actions, &action->super);

  return action;
}

/* NOTE: Some of the original quoting is lost here. */
static struct lsh_string *
rebuild_command_line(unsigned argc, char **argv)
{
  unsigned length;
  unsigned i;
  unsigned pos;
  struct lsh_string *r;
  unsigned *alengths = alloca(sizeof(unsigned) * argc);
  
  assert (argc);
  length = argc - 1; /* Number of separating spaces. */

  for (i = 0; i<argc; i++)
    {
      alengths[i] = strlen(argv[i]);
      length += alengths[i];
    }

  r = lsh_string_alloc(length);
  memcpy(r->data, argv[0], alengths[0]);
  pos = alengths[0];
  for (i = 1; i<argc; i++)
    {
      r->data[pos++] = ' ';
      memcpy(r->data + pos, argv[i], alengths[i]);
      pos += alengths[i];
    }

  assert(pos == r->length);

  return r;
}

static int
fork_input(int in)
{
  /* pipe[0] for reading, pipe[1] for writing. */
  int pipe[2];

  if (!lsh_make_pipe(pipe))
    return -1;

  switch (fork())
    {
    case -1:
      /* Error */
      return -1;
    case 0:
      close(pipe[0]);
      if (lsh_copy_file(in, pipe[1]))
	_exit(EXIT_SUCCESS);
      else
	_exit(EXIT_FAILURE);
    default:
      /* Parent */
      close(pipe[1]);
      return pipe[0];
    }
}

static int
fork_output(int out)
{
  /* pipe[0] for reading, pipe[1] for writing. */
  int pipe[2];

  if (!lsh_make_pipe(pipe))
    return -1;

  switch (fork())
    {
    case -1:
      /* Error */
      return -1;
    case 0:
      close(pipe[1]);
      if (lsh_copy_file(pipe[0], out))
	_exit(EXIT_SUCCESS);
      else
	_exit(EXIT_FAILURE);
    default:
      /* Parent */
      close(pipe[0]);
      return pipe[1];
    }
}

817
818
819
820
821
822
/* FIXME: Use const? */

#define DEFINE_CALLBACK(name) \
static void do_##name(struct lsh_callback *self); \
static struct lsh_callback \
name = { STATIC_HEADER, do_##name }; \
823
static void do_##name(struct lsh_callback *self UNUSED)
824

825
/* A callback that exits the process immediately. */
826
DEFINE_CALLBACK(exit_callback)
827
828
829
830
{
  exit(EXIT_SUCCESS);
}

831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
DEFINE_CALLBACK(verbose_callback)
{
  verbose_flag = !verbose_flag;
  if (verbose_flag)
    verbose("Enabling verbose messages\n");
}

DEFINE_CALLBACK(debug_callback)
{
  debug_flag = !debug_flag;
  if (debug_flag)
    debug("Enabling debug messages\n");
}

DEFINE_CALLBACK(quiet_callback)
{
  quiet_flag = !quiet_flag;
  if (!quiet_flag)
    werror("Enabling warning messages\n");
}
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876

DEFINE_COMMAND(background_process)
     (struct command *s UNUSED,
      struct lsh_object *a,
      struct command_continuation *c,
      struct exception_handler *e UNUSED)
{
  switch (fork())
    {
    case 0:
      /* Child */
      /* FIXME: Should we create a new process group, close our tty
       * and stdio, etc? */
      COMMAND_RETURN(c, a);
      break;
    case -1:
      /* Error */
      werror("background_process: fork failed (errno = %i): %z\n",
	     errno, STRERROR(errno));
      COMMAND_RETURN(c, a);
      break;
    default:
      /* Parent */
      _exit(EXIT_SUCCESS);
    }
}
877

878
879
880
881
882
/* Create a session object. stdout and stderr are shared (although
 * with independent lsh_fd objects). stdin can be used by only one
 * session (until something "session-control"/"job-control" is added).
 * */
static struct ssh_channel *
883
make_client_session(struct client_options *options)
884
885
886
887
{
  int in;
  int out;
  int err;
888
  int is_tty = 0;
889
  struct ssh_channel *session;
890
  
891
892
  struct escape_info *escape = NULL;
  
893
894
895
896
  debug("lsh.c: Setting up stdin\n");

  if (options->stdin_file)
    in = open(options->stdin_file, O_RDONLY);
897
898
  else
    {
899
      if (options->used_stdin)
900
	in = open("/dev/null", O_RDONLY);
901
      else 
902
	{
903
	  in = (options->stdin_fork ? fork_input : dup)(STDIN_FILENO);
904
905
	  is_tty = isatty(STDIN_FILENO);
	  
906
	  options->used_stdin = 1;
907
908
	}
    }
909

910
911
  if (in < 0)
    {
912
      werror("lsh: Can't dup/open stdin (errno = %i): %z!\n",
913
914
915
916
	     errno, strerror(errno));
      return NULL;
    }

917
918
919
920
921
922
923
924
925
926
927
928
929
  /* Attach the escape char handler, if appropriate. */
  if (options->escape > 0)
    {
      verbose("Enabling explicit escape character `%pc'\n",
	      options->escape);
      escape = make_escape_info(options->escape);
    }
  else if ( (options->escape < 0) && is_tty)
    {
      verbose("Enabling default escape character `%pc'\n",
	      DEFAULT_ESCAPE_CHAR);
      escape = make_escape_info(DEFAULT_ESCAPE_CHAR);
    }
930

931
  /* Bind ^Z to suspend. */
932
  if (escape)
933
934
935
936
    {
      /* Bind ^Z to suspend. */
      escape->dispatch[26] = &suspend_callback;
      escape->dispatch['.'] = &exit_callback;
937
938
939
940
941

      /* Toggle the verbosity flags */
      escape->dispatch['d'] = &debug_callback;
      escape->dispatch['v'] = &verbose_callback;
      escape->dispatch['q'] = &quiet_callback;      
942
    }
943
  
944
945
946
947
948
949
950
951
952
  debug("lsh.c: Setting up stdout\n");

  if (options->stdout_file)
    out = open(options->stdout_file, O_WRONLY | O_CREAT, 0666);
  else if (options->stdout_fork)
    out = fork_output(STDOUT_FILENO);
  else
    out = dup(STDOUT_FILENO);

953
954
955
956
957
958
959
960
  if (out < 0)
    {
      werror("lsh: Can't dup/open stdout (errno = %i): %z!\n",
	     errno, strerror(errno));
      close(in);
      return NULL;
    }

961
962
963
964
965
966
  debug("lsh.c: Setting up stderr\n");
  
  if (options->stderr_file)
    err = open(options->stderr_file, O_WRONLY | O_CREAT, 0666);
  else if (options->stderr_fork)
    err = fork_output(STDERR_FILENO);
967
968
969
  else
    {
      err = dup(STDERR_FILENO);
970
      set_error_nonblocking(STDERR_FILENO);
971
972
973
974
    }

  if (err < 0) 
    {
975
      werror("lsh: Can't dup/open stderr!\n");
976
977
978
979
980
981
      close(in);
      close(out);
      return NULL;
    }

  /* Clear options */
982
  options->stdin_file = options->stdout_file = options->stderr_file = NULL;
983
984

  session = make_client_session_channel
985
    (io_read(make_lsh_fd(in, "client stdin", options->handler),
986
	     NULL, NULL),
987
     io_write(make_lsh_fd(out, "client stdout", options->handler),
988
	      BLOCK_SIZE, NULL),
989
     io_write(make_lsh_fd(err, "client stderr", options->handler),
990
	      BLOCK_SIZE, NULL),
991
     escape,
992
     WINDOW_SIZE,
993
     options->exit_code);
994
995
996
997
998
999
1000

  /* The channel won't get registered in any other resource_list
   * until later, so we must register it here to avoid a "garbage
   * collecting a live resource!" crashes if the connection fails
   * early. */
  gc_global(&session->resources->super);
  return session;
For faster browsing, not all history is shown. View entire blame