client.c 28 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

/* Handling of options and operations shared by the plain lsh client
   and lshg. */

/* 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

void
init_client_options(struct client_options *self,
382
		    struct randomness_with_poll *random,
383
384
385
		    struct exception_handler *handler,
		    int *exit_code)			 
{
386
387
  self->random = random;
  
388
  self->tty = make_unix_interact();
389
390
  self->escape = -1;
  
391
392
393
394
395
396
397
398
399
400
401
402
  self->handler = handler;

  self->exit_code = exit_code;
  
  self->not = 0;
  self->port = "ssh";
  self->remote = NULL;

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

  self->with_remote_peers = 0; 
  self->with_pty = -1;
403
404
  self->with_x11 = 0;
    
405
406
407
408
409
410
  self->stdin_file = NULL;
  self->stdout_file = NULL;
  self->stderr_file = NULL;

  self->stdin_fork = 0;
  self->stdout_fork = 0;
411
  self->stderr_fork = 1;
412
413
414

  self->used_stdin = 0;
  self->used_pty = 0;
415
416
  self->used_x11 = 0;
  
417
  self->start_shell = 1;
418
  self->remote_forward = 0;
419
420
421
422
423

  object_queue_init(&self->actions);  
}

/* Host to connect to */
424
425
426
427
428
DEFINE_COMMAND(client_options2remote)
     (struct command *s UNUSED,
      struct lsh_object *a,
      struct command_continuation *c,
      struct exception_handler *e UNUSED)
429
430
431
432
{
  CAST_SUBTYPE(client_options, options, a);
  trace("client.c: client_options2remote\n");
  
433
  COMMAND_RETURN(c, options->remote);
434
435
436
}

/* Host to connect to */
437
438
439
440
441
DEFINE_COMMAND(client_options2actions)
     (struct command *s UNUSED,
      struct lsh_object *a,
      struct command_continuation *c,
      struct exception_handler *e UNUSED)
442
443
444
445
446
447
{
  CAST_SUBTYPE(client_options, options, a);

  trace("client.c: client_options2actions, %i actions\n",
	options->actions.length);
  
448
  COMMAND_RETURN(c, queue_to_list(&options->actions));
449
450
451
452
453
454
455
456
457
458
459
}

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 },
460
#if 0
461
462
463
464
  { "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 },
465
  { "background", 'B', NULL, 0, "Put process into the background. Implies -N.", 0 },
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
  { "execute", 'E', "command", 0, "Execute a command on the remote machine", 0 },
  { "shell", 'S', "command", 0, "Spawn a remote shell", 0 },
  /* { "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}, 
485

486
  { "cvs-workaround", OPT_FORK_STDIO, "i?o?e?", OPTION_ARG_OPTIONAL,
487
488
489
490
491
492
    "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 },
493

494
495
496
497
#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 */
498
499
  { "escape-char", 'e', "Character", 0, "Escape char. `none' means disable. "
    "Default is to use `~' if we have a tty, otherwise none.", 0 },
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
  { 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)))))
*/
515

516
517
518
519
520
521
522
523
524
/* 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)))))
525
526
*/

527
528
529
530
531
532
533
static struct command *
make_client_start_session(struct command *request)
{
  CAST_SUBTYPE(command, r, client_start_session(request));
  return r;
}

534
535
536
static void
client_maybe_pty(struct client_options *options,
		 struct object_queue *q)
537
538
539
540
541
542
543
544
{
#if WITH_PTY_SUPPORT
  if (options->with_pty && !options->used_pty)
    {
      options->used_pty = 1;
      
      if (options->tty && INTERACT_IS_TTY(options->tty))
	{
545
546
547
548
549
550
551
552
553
554
555
	  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");
556
557
	}
      else
558
559
560
561
562
563
564
565
566
567
568
569
	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");
570
571
      struct command *request = NULL;
      
572
      assert(options->random);
573
      if (display)
574
	request = make_forward_x11(display, &options->random->super);
575
	  
576
577
578
579
      if (request)
	{
	  object_queue_add_tail(q, &request->super);
	  options->used_x11 = 1;
580
	}
581
582
      else
	werror("Can't find any local X11 display to forward.\n");
583
    }
584
}
585

586
587
588
589
590
/* Create an interactive session */
static struct command *
client_shell_session(struct client_options *options)
{
  struct ssh_channel *session = make_client_session(options);
591
  
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
  if (session)
    {
      struct object_queue session_requests;
    
      object_queue_init(&session_requests);
  
      client_maybe_pty(options, &session_requests);
      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;
      }
    }
612
  else
613
    return NULL;
614
615
616
617
618
619
}

/* Create a session executing a command line */
static struct command *
client_command_session(struct client_options *options,
		       struct lsh_string *command)
620
{
621
  struct ssh_channel *session = make_client_session(options);
622
  
623
624
  if (session)
    {
625
626
627
628
629
630
631
      struct object_queue session_requests;
    
      object_queue_init(&session_requests);
  
      /* NOTE: Doesn't ask for a pty. That's traditional behaviour,
       * although perhaps not the Right Thing. */
      
632
      client_maybe_pty(options, &session_requests);
633
634
635
636
637
638
639
640
641
642
643
      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;
      }
644
645
    }
  
646
647
648
  return NULL;
}

649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
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
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];
    }
}

747
748
749
750
751
752
/* FIXME: Use const? */

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

755
/* A callback that exits the process immediately. */
756
DEFINE_CALLBACK(exit_callback)
757
758
759
760
{
  exit(EXIT_SUCCESS);
}

761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
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");
}
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

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);
    }
}
807

808
809
810
811
812
/* 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 *
813
make_client_session(struct client_options *options)
814
815
816
817
{
  int in;
  int out;
  int err;
818
819
  int is_tty = 0;
  
820
821
  struct escape_info *escape = NULL;
  
822
823
824
825
  debug("lsh.c: Setting up stdin\n");

  if (options->stdin_file)
    in = open(options->stdin_file, O_RDONLY);
826
827
  else
    {
828
      if (options->used_stdin)
829
	in = open("/dev/null", O_RDONLY);
830
      else 
831
	{
832
	  in = (options->stdin_fork ? fork_input : dup)(STDIN_FILENO);
833
834
	  is_tty = isatty(STDIN_FILENO);
	  
835
	  options->used_stdin = 1;
836
837
	}
    }
838

839
840
  if (in < 0)
    {
841
      werror("lsh: Can't dup/open stdin (errno = %i): %z!\n",
842
843
844
845
	     errno, strerror(errno));
      return NULL;
    }

846
847
848
849
850
851
852
853
854
855
856
857
858
  /* 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);
    }
859

860
  /* Bind ^Z to suspend. */
861
  if (escape)
862
863
864
865
    {
      /* Bind ^Z to suspend. */
      escape->dispatch[26] = &suspend_callback;
      escape->dispatch['.'] = &exit_callback;
866
867
868
869
870

      /* Toggle the verbosity flags */
      escape->dispatch['d'] = &debug_callback;
      escape->dispatch['v'] = &verbose_callback;
      escape->dispatch['q'] = &quiet_callback;      
871
    }
872
  
873
874
875
876
877
878
879
880
881
  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);

882
883
884
885
886
887
888
889
  if (out < 0)
    {
      werror("lsh: Can't dup/open stdout (errno = %i): %z!\n",
	     errno, strerror(errno));
      close(in);
      return NULL;
    }

890
891
892
893
894
895
  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);
896
897
898
  else
    {
      err = dup(STDERR_FILENO);
899
      set_error_nonblocking(STDERR_FILENO);
900
901
902
903
    }

  if (err < 0) 
    {
904
      werror("lsh: Can't dup/open stderr!\n");
905
906
907
908
909
910
      close(in);
      close(out);
      return NULL;
    }

  /* Clear options */
911
  options->stdin_file = options->stdout_file = options->stderr_file = NULL;
912
  
913
  return make_client_session_channel
914
    (io_read(make_lsh_fd(in, "client stdin", options->handler),
915
	     NULL, NULL),
916
     io_write(make_lsh_fd(out, "client stdout", options->handler),
917
	      BLOCK_SIZE, NULL),
918
     io_write(make_lsh_fd(err, "client stderr", options->handler),
919
	      BLOCK_SIZE, NULL),
920
     escape,
921
     WINDOW_SIZE,
922
     options->exit_code);
923
924
}

925

926
927
928
929
930
/* Parse the argument for -R and -L */
int
client_parse_forward_arg(char *arg,
			 UINT32 *listen_port,
			 struct address_info **target)
931
{
932
933
934
935
  char *first;
  char *second;
  char *end;
  long port;
936
  
937
938
939
  first = strchr(arg, ':');
  if (!first)
    return 0;
940

941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
  second = strchr(first + 1, ':');
  if (!second || (second == first + 1))
    return 0;

  if (strchr(second + 1, ':'))
    return 0;

  port = strtol(arg, &end, 0);
  if ( (end == arg)  || (end != first)
       || (port < 0) || (port > 0xffff) )
    return 0;

  *listen_port = port;

  port = strtol(second + 1, &end, 0);
  if ( (end == second + 1) || (*end != '\0')
       || (port < 0) || (port > 0xffff) )
    return 0;

  *target = make_address_info(ssh_format("%ls", second - first - 1, first + 1), port);
961
  
962
963
  return 1;
}
964

965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#define CASE_ARG(opt, attr, none)		\
  case opt:					\
    if (options->not)				\
      {						\
        options->not = 0;			\
						\
      case opt | ARG_NOT:			\
        options->attr = none;			\
        break;					\
      }						\
      						\
    options->attr = arg;			\
    break

#define CASE_FLAG(opt, flag)			\
  case opt:					\
    if (options->not)				\
      {						\
        options->not = 0;			\
						\
      case opt | ARG_NOT:			\
        options->flag = 0;			\
        break;					\
      }						\
      						\
    options->flag = 1;				\
    break

static error_t
client_argp_parser(int key, char *arg, struct argp_state *state)
{
  CAST_SUBTYPE(client_options, options, state->input);

  switch(key)
    {
    default:
For faster browsing, not all history is shown. View entire blame