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

       ;; FIXME: Do we really need the exception handler here?
82
       (e object exception_handler)))
83
84
*/

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

  struct simple_buffer buffer;
93
  unsigned msg_number;
Niels Möller's avatar
Niels Möller committed
94
  int name;
95

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

118
struct packet_handler *
119
make_accept_service_handler(UINT32 service,
120
121
			    struct command_continuation *c,
			    struct exception_handler *e)
122
{
123
  NEW(accept_service_handler, closure);
124

Niels Möller's avatar
Niels Möller committed
125
  closure->super.handler = do_accept_service;
126
  closure->service = service;
127
  closure->c = c;
128
129
  closure->e = e;
  
130
131
132
  return &closure->super;
}

133
void
Niels Möller's avatar
Niels Möller committed
134
135
136
137
do_request_service(struct command *s,
		   struct lsh_object *x,
		   struct command_continuation *c,
		   struct exception_handler *e)
138
{
139
140
  CAST(request_service, self, s);
  CAST(ssh_connection, connection, x);
141
  
Niels Möller's avatar
Niels Möller committed
142
  connection->dispatch[SSH_MSG_SERVICE_ACCEPT]
143
    = make_accept_service_handler(self->service, c, e);
Niels Möller's avatar
Niels Möller committed
144
  
Niels Möller's avatar
Niels Möller committed
145
146
  C_WRITE(connection,
	  format_service_request(self->service));
147
148
}

149
150
struct command *
make_request_service(int service)
151
{
152
  NEW(request_service, closure);
153

154
  closure->super.call = do_request_service;
155
156
157
158
159
  closure->service = service;

  return &closure->super;
}

160

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

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

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

190
191
      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
192

193
      COMMAND_RETURN(s, NULL);
Niels Möller's avatar
Niels Möller committed
194
    }
Niels Möller's avatar
Niels Möller committed
195
196
  else
    /* Invalid request */
197
    PROTOCOL_ERROR(e, "Invalid exit-status message");
Niels Möller's avatar
Niels Möller committed
198
199
}

Niels Möller's avatar
Niels Möller committed
200
201
202
static void
do_exit_signal(struct channel_request *c,
	       struct ssh_channel *channel,
203
	       struct ssh_connection *connection UNUSED,
Niels Möller's avatar
Niels Möller committed
204
	       struct channel_request_info *info,
205
206
207
	       struct simple_buffer *args,
	       struct command_continuation *s,
	       struct exception_handler *e)
Niels Möller's avatar
Niels Möller committed
208
{
209
210
  CAST(exit_handler, closure, c);

211
  UINT32 signal;
Niels Möller's avatar
Niels Möller committed
212
213
  int core;

Niels Möller's avatar
Niels Möller committed
214
  const UINT8 *msg;
Niels Möller's avatar
Niels Möller committed
215
216
  UINT32 length;

Niels Möller's avatar
Niels Möller committed
217
  const UINT8 *language;
Niels Möller's avatar
Niels Möller committed
218
219
  UINT32 language_length;
  
Niels Möller's avatar
Niels Möller committed
220
  if (!info->want_reply
221
      && parse_atom(args, &signal)
Niels Möller's avatar
Niels Möller committed
222
223
224
225
226
227
228
229
230
231
      && 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;

232
233
      werror("Remote process was killed by signal: %ups %z\n",
	     length, msg,
234
	     core ? "(core dumped remotely)\n": "");
235
      
236
237
      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
238

239
      COMMAND_RETURN(s, NULL);
Niels Möller's avatar
Niels Möller committed
240
    }
Niels Möller's avatar
Niels Möller committed
241
242
  else
    /* Invalid request */
243
    PROTOCOL_ERROR(e, "Invalid exit-signal message");
Niels Möller's avatar
Niels Möller committed
244
245
}

246
247
struct channel_request *
make_handle_exit_status(int *exit_status)
Niels Möller's avatar
Niels Möller committed
248
{
249
  NEW(exit_handler, self);
Niels Möller's avatar
Niels Möller committed
250
251
252
253
254
255
256
257

  self->super.handler = do_exit_status;

  self->exit_status = exit_status;

  return &self->super;
}

258
259
struct channel_request *
make_handle_exit_signal(int *exit_status)
Niels Möller's avatar
Niels Möller committed
260
{
261
  NEW(exit_handler, self);
Niels Möller's avatar
Niels Möller committed
262
263
264
265
266
267
268
269

  self->super.handler = do_exit_signal;

  self->exit_status = exit_status;

  return &self->super;
}

270
271
272
273
274
275
276
/* 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.
277
       (session object ssh_channel)))
278
279
280
281
282
*/

static struct ssh_channel *
new_session(struct channel_open_command *s,
	    struct ssh_connection *connection,
283
	    UINT32 local_channel_number,
284
285
286
287
	    struct lsh_string **request)
{
  CAST(session_open_command, self, s);
  struct ssh_channel *res;
288
289
290

  self->session->write = connection->write;
  
291
292
293
  *request = format_channel_open(ATOM_SESSION,
				 local_channel_number,
				 self->session, "");
294
  
295
  res = self->session;
296
297
298
299
300
301
302

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

  return res;
}

303
304
struct command *
make_open_session_command(struct ssh_channel *session)
305
306
307
308
309
{
  NEW(session_open_command, self);
  self->super.super.call = do_channel_open_command;
  self->super.new_channel = new_session;
  self->session = session;
310
311

  return &self->super.super;
312
313
}

314
315
316
317
318
319
320
321

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, "");
}
322

323
324
struct channel_request_command request_shell =
{ { STATIC_HEADER, do_channel_request_command }, do_format_shell_request };
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
354
355
356
357
358

/* 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;
}
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
390
391
392

/* 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,
		    struct io_backend *backend,
		    struct exception_handler *handler,
		    int *exit_code)			 
{
  self->backend = backend;
  self->tty = make_unix_interact(backend);
393
394
  self->escape = -1;
  
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
  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;

  self->stdin_file = NULL;
  self->stdout_file = NULL;
  self->stderr_file = NULL;

  self->stdin_fork = 0;
  self->stdout_fork = 0;
414
  self->stderr_fork = 1;
415
416
417
418
419

  self->used_stdin = 0;
  self->used_pty = 0;

  self->start_shell = 1;
420
  self->remote_forward = 0;
421
422
423
424
425

  object_queue_init(&self->actions);  
}

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

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

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

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 },
462
#if 0
463
464
465
466
  { "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 },
467
  { "background", 'B', NULL, 0, "Put process into the background. Implies -N.", 0 },
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
  { "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}, 
487

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

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

528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
static struct command *
make_client_start_session(struct command *request)
{
  CAST_SUBTYPE(command, r, client_start_session(request));
  return r;
}

/* Create an interactive session */
static struct command *
client_shell_session(struct client_options *options)
{
  struct command *get_pty = NULL;
  struct command *get_shell;
  
  struct object_list *session_requests;
  struct ssh_channel *session = make_client_session(options);

  if (!session)
    return NULL;
  
#if WITH_PTY_SUPPORT
  if (options->with_pty && !options->used_pty)
    {
      options->used_pty = 1;
      
      if (options->tty && INTERACT_IS_TTY(options->tty))
	{
	  get_pty = make_pty_request(options->tty);
	  if (!get_pty)
	    {
558
	      werror("lsh: Can't use tty (probably getattr or atexit failed).\n");
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
	    }
	}
      else
	{
	  werror("lsh: No tty available.\n");
	}
    }

  get_shell = make_client_start_session(&request_shell.super);
  
  /* FIXME: We need a non-varargs constructor for lists. */
  if (get_pty)
    session_requests
      = make_object_list(2,
			 /* 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),
			 get_shell, -1);
  else
#endif /* WITH_PTY_SUPPORT */
    session_requests = make_object_list(1, get_shell, -1);

  {
    CAST_SUBTYPE(command, r,
		 make_start_session
		 (make_open_session_command(session), session_requests));
    return r;
  }
}

/* Create a session executing a command line */
static struct command *
client_command_session(struct client_options *options,
		       struct lsh_string *command)
595
{
596
597
598
599
600
601
602
603
604
605
606
607
608
  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_exec_request(command)),
		     -1)));
      return r;
    }
  
609
610
611
  return NULL;
}

612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
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
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];
    }
}

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
/* A callback that exits the process immediately. */
static void
do_exit(struct lsh_callback *self UNUSED)
{
  exit(EXIT_SUCCESS);
}

/* FIXME: Use const? */
static struct lsh_callback
exit_callback = { STATIC_HEADER, do_exit };

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

747
748
749
750
751
/* 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 *
752
make_client_session(struct client_options *options)
753
754
755
756
{
  int in;
  int out;
  int err;
757
758
  int is_tty = 0;
  
759
760
  struct escape_info *escape = NULL;
  
761
762
763
764
  debug("lsh.c: Setting up stdin\n");

  if (options->stdin_file)
    in = open(options->stdin_file, O_RDONLY);
765
766
  else
    {
767
      if (options->used_stdin)
768
	in = open("/dev/null", O_RDONLY);
769
      else 
770
	{
771
	  in = (options->stdin_fork ? fork_input : dup)(STDIN_FILENO);
772
773
	  is_tty = isatty(STDIN_FILENO);
	  
774
	  options->used_stdin = 1;
775
776
	}
    }
777

778
779
  if (in < 0)
    {
780
      werror("lsh: Can't dup/open stdin (errno = %i): %z!\n",
781
782
783
784
	     errno, strerror(errno));
      return NULL;
    }

785
786
787
788
789
790
791
792
793
794
795
796
797
  /* 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);
    }
798

799
  /* Bind ^Z to suspend. */
800
  if (escape)
801
802
803
804
805
    {
      /* Bind ^Z to suspend. */
      escape->dispatch[26] = &suspend_callback;
      escape->dispatch['.'] = &exit_callback;
    }
806
  
807
808
809
810
811
812
813
814
815
  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);

816
817
818
819
820
821
822
823
  if (out < 0)
    {
      werror("lsh: Can't dup/open stdout (errno = %i): %z!\n",
	     errno, strerror(errno));
      close(in);
      return NULL;
    }

824
825
826
827
828
829
  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);
830
831
832
  else
    {
      err = dup(STDERR_FILENO);
833
      set_error_nonblocking(STDERR_FILENO);
834
835
836
837
    }

  if (err < 0) 
    {
838
      werror("lsh: Can't dup/open stderr!\n");
839
840
841
842
843
844
      close(in);
      close(out);
      return NULL;
    }

  /* Clear options */
845
  options->stdin_file = options->stdout_file = options->stderr_file = NULL;
846
  
847
  return make_client_session_channel
848
849
    (io_read(make_lsh_fd(options->backend,
			 in, "client stdin", options->handler),
850
	     NULL, NULL),
851
852
     io_write(make_lsh_fd(options->backend,
			  out, "client stdout", options->handler),
853
	      BLOCK_SIZE, NULL),
854
855
     io_write(make_lsh_fd(options->backend,
			  err, "client stderr", options->handler),
856
	      BLOCK_SIZE, NULL),
857
     escape,
858
     WINDOW_SIZE,
859
     options->exit_code);
860
861
}

862

863
864
865
866
867
/* Parse the argument for -R and -L */
int
client_parse_forward_arg(char *arg,
			 UINT32 *listen_port,
			 struct address_info **target)
868
{
869
870
871
872
  char *first;
  char *second;
  char *end;
  long port;
873
  
874
875
876
  first = strchr(arg, ':');
  if (!first)
    return 0;
877

878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
  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);
898
  
899
900
  return 1;
}
901

902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
#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:
      return ARGP_ERR_UNKNOWN;
    case ARGP_KEY_NO_ARGS:
      argp_usage(state);
      break;
    case ARGP_KEY_ARG:
      if (!state->arg_num)
944
	{
945
946
947
948
949
950
951
952
953
	  if (options->port)
	    options->remote = make_address_info_c(arg, options->port, 0);
	  else
	    options->remote = make_address_info_c(arg, "ssh", 22);
	  
	  if (!options->remote)
	    argp_error(state, "Invalid port or service '%s'.", options->port);

	  break;
954
955
	}
      else
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
	/* Let the next case parse it.  */
	return ARGP_ERR_UNKNOWN;

      break;
    case ARGP_KEY_ARGS:
      client_add_action
	(options,
	 client_command_session
	 (options, rebuild_command_line(state->argc - state->next,
				     state->argv + state->next)));
      options->start_shell = 0;
      break;

    case ARGP_KEY_END:
      if (!options->user)
971
	{
972
973
974
	  argp_error(state, "No user name given. Use the -l option, or set LOGNAME in the environment.");
	  break;
	}
975
976
977
978
979
980
981
982

#if WITH_TCP_FORWARD
      if (options->remote_forward)
	client_add_action(options,
			  make_install_fix_channel_open_handler
			  (ATOM_FORWARDED_TCPIP, &channel_open_forwarded_tcpip));
#endif /* WITH_TCP_FORWARD */
      
983
984
985
      /* Add shell action */
      if (options->start_shell)
	client_add_action(options, client_shell_session(options));
986

987
988
      /* Install suspend-handler */
      suspend_install_handler();
989
990
991
992
993
994
995
996
997
998
      break;

    case 'p':
      options->port = arg;
      break;

    case 'l':
      options->user = arg;
      break;

999
1000
    case 'e':
      if (arg[0] && !arg[1])
For faster browsing, not all history is shown. View entire blame