caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* [Caml-list] calling telnet from a caml program
@ 2002-11-06 16:11 Alan Schmitt
  2002-11-06 16:26 ` Andrei Errapart
  2002-11-06 21:07 ` Alan Schmitt
  0 siblings, 2 replies; 8+ messages in thread
From: Alan Schmitt @ 2002-11-06 16:11 UTC (permalink / raw)
  To: caml-list

Hi,

I've spent a couple hours trying to make this work, but it does not want
to ... I want to call telnet from a caml program, send some commands,
and read some output. However there is something weird going on, as
telnet does not seem to flush anything.

I've tried the following:

let pin,pout = Unix.open_process "/usr/bin/telnet" in
print_endline "got it";
flush stdout;
output_string pout "open 127.0.0.1\r\n";
flush pout;
let str = input_line pin in
print_endline str;
output_string pout "quit\r\n";
flush pout;
print_endline "got it";
flush stdout

which does not work (there should at least be the line "Trying
127.0.0.1")

and the following (but I am probably completely mistaken about the
handling of pipes):

let outin, outout = Unix.pipe () in
let inin, inout = Unix.pipe () in
let pid = Unix.create_process "telnet" [| |] inin outout Unix.stderr in
Unix.set_nonblock outin;
let str = String.create 5 in
let res = Unix.read outin str 0 1 in
print_int res;
print_newline ();
print_endline str;
flush stdout

(this should at least return the "t" of the telnet prompt ... but it
fails with an exception saying there is no data to be read).

I'd really someone to point to me what I am doing wrong ...

Best,

Alan


-- 
The hacker: someone who figured things out and made something cool happen.
-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Caml-list] calling telnet from a caml program
  2002-11-06 16:11 [Caml-list] calling telnet from a caml program Alan Schmitt
@ 2002-11-06 16:26 ` Andrei Errapart
  2002-11-06 16:34   ` Alan Schmitt
  2002-11-06 21:07 ` Alan Schmitt
  1 sibling, 1 reply; 8+ messages in thread
From: Andrei Errapart @ 2002-11-06 16:26 UTC (permalink / raw)
  To: caml-list

On Wed, 6 Nov 2002, Alan Schmitt wrote:
> Hi,
> 
> I've spent a couple hours trying to make this work, but it does not want
> to ... I want to call telnet from a caml program, send some commands,
> and read some output. However there is something weird going on, as
> telnet does not seem to flush anything.

IIRC stdio/stdout use different buffering strategy (line-buffering vs. 
fixed size buffer) depending upon whether isatty(0) is true or false. If 
this is the case, there is nothing you can do to force telnet into 
line-buffering mode except using pseudo-ttys (pty), which is what programs 
like "screen" and "except" do.


Andrei

-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Caml-list] calling telnet from a caml program
  2002-11-06 16:26 ` Andrei Errapart
@ 2002-11-06 16:34   ` Alan Schmitt
  2002-11-06 17:20     ` Andrei Errapart
  0 siblings, 1 reply; 8+ messages in thread
From: Alan Schmitt @ 2002-11-06 16:34 UTC (permalink / raw)
  To: caml-list

* Andrei Errapart (andreie@no.spam.ee) wrote:
> On Wed, 6 Nov 2002, Alan Schmitt wrote:
> > Hi,
> > 
> > I've spent a couple hours trying to make this work, but it does not want
> > to ... I want to call telnet from a caml program, send some commands,
> > and read some output. However there is something weird going on, as
> > telnet does not seem to flush anything.
> 
> IIRC stdio/stdout use different buffering strategy (line-buffering vs. 
> fixed size buffer) depending upon whether isatty(0) is true or false. If 
> this is the case, there is nothing you can do to force telnet into 
> line-buffering mode except using pseudo-ttys (pty), which is what programs 
> like "screen" and "except" do.

That's what I was afraid of ... So on to the next question: is it
possible to use pseudy-ttys with caml ? Or should I start thinking about
reimplementing the telnet client protocol ...

Alan

-- 
The hacker: someone who figured things out and made something cool happen.
-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Caml-list] calling telnet from a caml program
  2002-11-06 16:34   ` Alan Schmitt
@ 2002-11-06 17:20     ` Andrei Errapart
  2002-11-06 17:39       ` Nicolas George
  2002-11-06 20:56       ` Gerd Stolpmann
  0 siblings, 2 replies; 8+ messages in thread
From: Andrei Errapart @ 2002-11-06 17:20 UTC (permalink / raw)
  To: caml-list

On Wed, 6 Nov 2002, Alan Schmitt wrote:
...
> That's what I was afraid of ... So on to the next question: is it
> possible to use pseudy-ttys with caml ? Or should I start thinking about
> reimplementing the telnet client protocol ...

If it is possible in C, then it is possible in OCaml, too. Most 
probably some termios(3) calls are needed, but not much more. By the 
way, Gerd Stolpmann mentions telnet client in his Netclient library.


Andrei

-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Caml-list] calling telnet from a caml program
  2002-11-06 17:20     ` Andrei Errapart
@ 2002-11-06 17:39       ` Nicolas George
  2002-11-06 19:51         ` David Brown
  2002-11-06 20:56       ` Gerd Stolpmann
  1 sibling, 1 reply; 8+ messages in thread
From: Nicolas George @ 2002-11-06 17:39 UTC (permalink / raw)
  To: caml-list

[-- Attachment #1: Type: text/plain, Size: 803 bytes --]

Le sextidi 16 brumaire, an CCXI, Andrei Errapart a écrit :
> If it is possible in C, then it is possible in OCaml, too. Most 
> probably some termios(3) calls are needed, but not much more.

It is More Complicated Than That(tm). On modern Unix, the steps to do
that are:

- master = open(/dev/ptmx)
- grantpt(master)
- unlockpt(master)
- fork
- setsid
- slave = open(ptsname(master))
- ioctl(slave, I_PUSH, "ptem")
- ioctl(slave, I_PUSH, "ldterm")
- ioctl(slave, I_PUSH, "ttcompat")

On BSD, this is more painful, since you must find among /dev/pty*
someone that can be opened, and change ownership of the corresponding
/dev/tty* (so you must be root). The only common step is the
fork/setsid.

There is nothing impossible, but there are horripilating portability
problems here.

[-- Attachment #2: Type: application/pgp-signature, Size: 185 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Caml-list] calling telnet from a caml program
  2002-11-06 17:39       ` Nicolas George
@ 2002-11-06 19:51         ` David Brown
  0 siblings, 0 replies; 8+ messages in thread
From: David Brown @ 2002-11-06 19:51 UTC (permalink / raw)
  To: caml-list

[-- Attachment #1: Type: text/plain, Size: 828 bytes --]

On Wed, Nov 06, 2002 at 06:39:14PM +0100, Nicolas George wrote:

> Le sextidi 16 brumaire, an CCXI, Andrei Errapart a ?crit?:
> > If it is possible in C, then it is possible in OCaml, too. Most 
> > probably some termios(3) calls are needed, but not much more.

> There is nothing impossible, but there are horripilating portability
> problems here.

The CDK contains an mlexpect program, which, if it works, should do
that.

However, what is it you are trying to do with the "telnet" protocol.
For the most part, telnet is just a simple connection to a port.
Unix.open_connection will probably do exactly what you want, just make
sure you get flushing working.

If you need to deal with the tty management functionality of telnet (say
for something interactive) that will take a bit more work.

Dave Brown

[-- Attachment #2: Type: application/pgp-signature, Size: 240 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Caml-list] calling telnet from a caml program
  2002-11-06 17:20     ` Andrei Errapart
  2002-11-06 17:39       ` Nicolas George
@ 2002-11-06 20:56       ` Gerd Stolpmann
  1 sibling, 0 replies; 8+ messages in thread
From: Gerd Stolpmann @ 2002-11-06 20:56 UTC (permalink / raw)
  To: Andrei Errapart; +Cc: caml-list

[-- Attachment #1: Type: text/plain, Size: 970 bytes --]


Am 2002.11.06 18:20 schrieb(en) Andrei Errapart:
> On Wed, 6 Nov 2002, Alan Schmitt wrote:
> ...
> > That's what I was afraid of ... So on to the next question: is it
> > possible to use pseudy-ttys with caml ? Or should I start thinking about
> > reimplementing the telnet client protocol ...
> 
> If it is possible in C, then it is possible in OCaml, too. Most 
> probably some termios(3) calls are needed, but not much more. By the 
> way, Gerd Stolpmann mentions telnet client in his Netclient library.

Yes, there is a telnet client in netclient version 0.90.1. I have attached
the MLI interface, and a simple example.

You can download netclient at
http://www.gerd-stolpmann.de/packages/netclient-0.90.1.tar.gz

Gerd

------------------------------------------------------------
Gerd Stolpmann * Viktoriastr. 45 * 64293 Darmstadt * Germany 
gerd@gerd-stolpmann.de          http://www.gerd-stolpmann.de
------------------------------------------------------------

[-- Attachment #2: telnet_client.mli --]
[-- Type: text/plain, Size: 14226 bytes --]

(* $Id: telnet_client.mli,v 1.2 2001/09/05 21:41:48 gerd Exp $
 * ----------------------------------------------------------------------
 *
 *)

(* This is a Telnet client providing the basic Telnet services. It
 * supports sending and receiving data (asynchronously), and the
 * negotiation of Telnet options, but it does not implement any option.
 *)

exception Telnet_protocol of exn;;
(* Wrapper for exceptions that already passed the exception handler. *)


type telnet_command =
    Telnet_data of string
  | Telnet_nop
  | Telnet_dm           (* data mark *)
  | Telnet_brk          (* break *)
  | Telnet_ip           (* interrupt process *)
  | Telnet_ao           (* abort output *)
  | Telnet_ayt          (* are you there? *)
  | Telnet_ec           (* erase character *)
  | Telnet_el           (* erase line *)
  | Telnet_ga           (* Go ahead *)
  | Telnet_sb of char   (* Begin of subnegotiation *)
  | Telnet_se           (* End of subnegotation *)
  | Telnet_will of char (* Acknowledges that option is in effect *)
  | Telnet_wont of char (* Acknowledges that option is rejected *)
  | Telnet_do of char   (* Requests to turn on an option *)
  | Telnet_dont of char (* Requests to turn off an option *)
  | Telnet_unknown of char (* Unknown command *)
  | Telnet_eof          (* End of file *)
  | Telnet_timeout      (* Timeout event *)
;;

(* A telnet_command is the interpretation of the octets in a Telnet
 * session, i.e. it is one level above the octet stream. See RFC854
 * for an explanation what the commands mean. Telnet_data represents
 * the data chunks between the commands. Note that you do not need
 * to double octets having value 255; this is done automatically.
 * Telnet_unknown represents any command not covered by RFC854, for
 * example the End-of-record-mark (introduced in RFC885) would be
 * Telnet_unknown '\239'. Telnet_eof represents the end of the octet
 * stream, useable in both directions. Telnet_timeout is added to the
 * input queue if I/O has not been happened for the configured period
 * of time.
 *)

type telnet_options =
    { connection_timeout : float;
      verbose_connection : bool;
      verbose_input : bool;
      verbose_output : bool;
    }
;;

(* telnet_options: modifies the behaviour of the client. Do not mix these
 * options up with the options negotiated with the remote side.
 *
 * 'connection_timeout':     After this period of time (in seconds) a
 *                           Telnet_timeout pseudo-command is added to
 *                           the input queue, and the connection is
 *                           aborted.
 * 'verbose_connection':     Enables printing of connection events to stderr.
 * 'verbose_input':          Enables printing of input events to stderr.
 * 'verbose_output':         Enables printing of output events to stderr.
 *)


type telnet_negotiated_option =
    Telnet_binary       (* see RFC 856 *)
  | Telnet_echo         (* see RFC 857 *)
  | Telnet_suppress_GA  (* see RFC 858 *)
  | Telnet_status       (* see RFC 859 *)
  | Telnet_timing_mark  (* see RFC 860 *)
  | Telnet_ext_opt_list (* see RFC 861 *)
  | Telnet_end_of_rec   (* see RFC 885 *)
  | Telnet_window_size  (* see RFC 1073 *)
  | Telnet_term_speed   (* see RFC 1079 *)
  | Telnet_term_type    (* see RFC 1091 *)
  | Telnet_X_display    (* see RFC 1096 *)
  | Telnet_linemode     (* see RFC 1184 *)
  | Telnet_flow_ctrl    (* see RFC 1372 *)
  | Telnet_auth         (* see RFC 1416 *)
  | Telnet_new_environ  (* see RFC 1572 and 1571 *)
  | Telnet_option of int   (* all other options *)
;;

(* telnet_negotiated_option: names for the most common options, and
 * the generic name Telnet_option for other options.
 *)

type telnet_option_state =
    Not_negotiated
  | Accepted
  | Rejected
;;

(* An option has one of three states:
 * - Not_negotiated: There was no negotiation about the option. This means
 *   that the option is turned off (but this client is allowed to reject
 *   it explicitly)
 * - Accepted: Both sides have accepted the option.
 * - Rejected: One side has rejected the option. This also means that the
 *   option is off, but the client refuses to send further acknoledgements
 *   that the option is off (to avoid endless negotiation loops).
 *)


val char_of_option : telnet_negotiated_option -> char
(* Converts the option name to the character representing it on the
 * octet-stream level.
 *)

val option_of_char : char -> telnet_negotiated_option
(* Converts a character representing an option to the internal option
 * name.
 *)


type telnet_connector =
    Telnet_connect of (string * int)
  | Telnet_socket of Unix.file_descr
;;

(* telnet_connector:
 * Telnet_connect(host,port): The client connects to this port.
 * Telnet_socket s: The client uses an already connected socket.
 *
 * Why Telnet_socket? Telnet is a symmetrical protocol; client and servers
 * implement the same protocol features (the only difference is the
 * environment: a client is typically connected with a real terminal; a server
 * is connected with a pseudo terminal). This simply means that this
 * implementation of a CLIENT can also be used as a SERVER implementation.
 * You need only to add code which accepts new connections and which passes
 * these connections over to a telnet_session object via Telnet_socket.
 *)


class telnet_session :
  object
    (* GENERAL:
     *
     * The telnet_session object has two queues, one for arriving data,
     * one for data to send.
     * Once the session object is attached to an event system, it connects
     * to the remote peer, and processes the queues. Input is appended to
     * the input queue; output found on the output queue is sent to the
     * other side.
     * If input arrives, a callback function is invoked.
     * You may close the output side of the socket by putting Telnet_eof
     * onto the output queue.
     * Once the EOF marker has been received, a Telnet_eof is appended to
     * the input queue, and the connection is closed (completely). The
     * session object detaches from the event system automatically in this
     * case.
     * 
     * HOW TO USE THE SESSION OBJECT:
     *
     * Pass an input handler as callback function to the session object.
     * The input handler is called when new input data have been arrived.
     * It should inspect the input queue, process the queue as much as
     * possible, and it should remove the processed items from the queue.
     * While processing, it may add new items to the output queue. 
     *
     * If you are not within the callback function and add items to the
     * output queue, the session object will not detect that there are
     * new items to send - unless you invoke the "update" method.
     *
     * If you want option negotiation, it is the simplest way to use
     * the special option negotiation methods. Configure the options
     * as you want (invoking "enable", "disable" etc), but do not forget
     * to modify the way input is processed. Every Telnet_will, _wont,
     * _do, and _dont command must be passed to process_option_command
     *)

    method set_connection : telnet_connector -> unit
	(* Sets the host name and the port of the remote server to contact. *)

    method set_event_system : Unixqueue.event_system -> unit
	(* Sets the event system to use. By default, a private event system
	 * is used.
	 *)

    method set_callback : (bool -> unit) -> unit
	(* Sets the callback function. This function is called after new
	 * commands have been put onto the input queue. 
	 * The argument passed to the callback function indicates whether
	 * a 'Synch' sequence was received from the remote side or not.
	 *
	 * NOTE SYNCH: If the client sees a data mark command it will assume
	 * that it is actually a Synch sequence. The client automatically
	 * discards any Telnet_data commands from the input queue (but not
	 * Telnet_datas inside subnegotiations). The data mark command
	 * itself remains on the queue.
	 *)

    method set_exception_handler : (exn -> unit) -> unit
	(* Sets the exception handler. Every known error condition is
	 * caught and passed to the exception handler.
	 * The exception handler can do whatever it wants to do. If it
	 * raises again an exception, the new exception is always propagated
	 * up to the caller (whoever this is). Often the caller is the
	 * event system scheduler (i.e. Unixqueue.run); see the documention
	 * there.
	 * If you do not set the exception handler, a default handler is
	 * active. It first resets the session (see method "reset"), and
	 * then wraps the exception into the Telnet_protocol exception,
	 * and raises this exception again.
	 *)

    method output_queue : telnet_command Queue.t
        (* The queue of commands to send to the remote side. If you add new
	 * commands to this queue, do not forget to invoke the 'update'
	 * method which indicates to the event system that new data to
	 * send is available.
	 * After commands have been sent, they are removed from the queue.
	 *)

    method input_queue : telnet_command Queue.t
        (* The queue of commands received from the remote side. This class
	 * only adds commands to the queue (and invokes the callback 
	 * function). The user of this class is responsible for removing
	 * commands from the queue which have been processed.
	 *)

    method get_options : telnet_options
        (* Get the configuration options. *)

    method set_options : telnet_options -> unit
	(* Set the configuration options. *)

    method reset : unit -> unit 
        (* Closes the connection immediately and empties all queues.
	 * All negotiated options are reset, too.
	 *)

    (* The following methods deal with Telnet protocol options. These
     * are negotiated between local and remote side by the Will, Won't,
     * Do and Don't commands. 
     * The "local" options describe the modification of the behaviour
     * of the local side; the "remote" options describe the modifications
     * of the remote side. Both set of options are independent.
     * This object may track the set of accepted and rejected options
     * if the following methods are used; but this works only if
     * the Telnet_will, _wont, _do, and _dont commands received from
     * the remote side are processed by 'process_option_command'. So
     * you need to invoke this method for the mentioned commands in
     * your command interpretation loop.
     * The idea is: If you ENABLE an option, it is possible to
     * switch it on. If the remote side requests an enabled option,
     * the request will be acknowledged. If the remote side does not
     * request an enabled option, it remains off.
     * You can also actively demand an option (OFFER_local_option,
     * REQUEST_remote_option); this is of course only possible if
     * the option is already enabled. In this case the client tries
     * actively to switch it on.
     * You can also DISABLE an option. If the option is 'on', the
     * client actively rejects the option; following the Telnet protocol
     * this is always possible (rejections cannot be rejected).
     * The "reset" methods are somewhat dangerous. They simply reset
     * the internal state of the client, but do not negotiate. This
     * possibility was added to allow the Timing Mark option to send
     * again timing marks even if the previous timing marks have
     * already been accepted. After "reset", the client thinks the
     * option was never negotiated; but nothing is done to tell
     * the remote side about this.
     * option_negotiation_is_over: true if no option negotiation is
     * pending (i.e. nothing has still to be acknowledged).
     *)
    method enable_local_option : telnet_negotiated_option -> unit
    method enable_remote_option : telnet_negotiated_option -> unit
    method disable_local_option : telnet_negotiated_option -> unit
    method disable_remote_option : telnet_negotiated_option -> unit
    method offer_local_option : telnet_negotiated_option -> unit
    method request_remote_option : telnet_negotiated_option -> unit
    method reset_local_option : telnet_negotiated_option -> unit
    method reset_remote_option : telnet_negotiated_option -> unit
    method get_local_option : telnet_negotiated_option -> telnet_option_state
    method get_remote_option : telnet_negotiated_option -> telnet_option_state
    method option_negotiation_is_over : bool
    method process_option_command : telnet_command -> unit

    method fetch_subnegotiation : string option
      (* This method should be called as follows:
       * If you find a Telnet_sb at the beginning of the input queue,
       * remove this command (Queue.take), and invoke fetch_subnegotiation.
       * This method scans the queue and looks for the associated 
       * Telnet_se command. If it does not find it, None is returned.
       * If Telnet_se is found, the parameter enclosed by the two commands
       * is returned as Some s where s is the parameter string. Furthermore,
       * in the latter case the data items and the closing Telnet_se are
       * removed from the queue.
       *)

    (* --- *)



    method attach : unit -> unit
        (* Attach to the event system. After being attached, the client
	 * is ready to work.
	 *)

    method run : unit -> unit
	(* Run the event system *)

    method update : unit -> unit
        (* If there are commands in the output queue, the event system is
	 * signaled that this client wants to do network I/O.
	 *)

    method send_synch : telnet_command list -> unit
        (* At the next output oppurtunity, a Synch sequence is sent to
	 * the remote peer. This means that the passed commands, extended
	 * by an additional Data Mark command, are sent to the peer as
	 * urgent data.
	 * Sending a Synch sequence has higher priority than the output
	 * queue; processing of the output queue is deferred until the
	 * Synch sequence has been completely sent.
	 *)
  end
;;


(* ======================================================================
 * History:
 * 
 * $Log: telnet_client.mli,v $
 * Revision 1.2  2001/09/05 21:41:48  gerd
 * 	Fixed types.
 *
 * Revision 1.1  2000/02/18 01:42:32  gerd
 * 	Initial revision.
 *
 * 
 *)

[-- Attachment #3: telnet.ml --]
[-- Type: text/plain, Size: 4603 bytes --]

#require "netclient";;

(* This is an example for the telnet client. The function below
 * connects with localhost, and logs the user in. It simulates
 * keyboard typing for the username and the password, and finally
 * starts the command "ls".
 *
 * The example may or may not work with your version of telnetd.
 * The program expects the string "login" before the user name must
 * be typed in, and it expects the string "password" before the password
 * must be entered. Furthermore, a new command line is recognized 
 * by the characters >, # or $.
 *)

open Telnet_client;;

type state =
    Start               (* just connected *)
  | Username_sent       (* the user name has been sent to the server *)
  | Password_sent       (* the password has been sent to the server *)
  | Command_sent        (* the command to execute has been sent to the server *)
;;


let login_re = Str.regexp_case_fold "\\(.\\|\n\\)*login";;
let passwd_re = Str.regexp_case_fold "\\(.\\|\n\\)*password";;
let cmd_re = Str.regexp "\\(.\\|\n\\)*[>$#]";;


let login_and_ls username password =
  (* Create a new event system, and the telnet session. We need the 
   * event system only to call Unixqueue.once.
   *)
  let esys = Unixqueue.create_unix_event_system() in
  let session = new telnet_session in
  let state = ref Start in

  let send_string s new_state =
    (* Emulate keyboard typing of the string s. Between the characters there
     * is a delay of 0.1 seconds.
     * When the string has been completely sent, change the state to
     * new_state.
     *)
    let t = ref 0.1 in
    let g = Unixqueue.new_group esys in
    let l = String.length s in
    for i = 0 to l - 1 do
      let c = s.[i] in
      let cs = if c = '\n' then "\r\n" else String.make 1 c in
      (* Do the function !t seconds in the future: *)
      Unixqueue.once esys g !t
	(fun () ->
	   Queue.add (Telnet_data cs) session#output_queue;
	   (* We must call update because we are outside of the regular
	    * callback function. Otherwise the session object would not
	    * notice that the queue has been extended.
	    *)
	   session # update();   
	   if i = l-1 then 
	     state := new_state
	);
      t := !t +. 0.1;
    done
  in

  let got_input is_urgent =
    (* This is the callback function. The session object calls it when
     * telnet commands have been added to the input queue.
     *)
    let oq = session # output_queue in
    let iq = session # input_queue in
    (* Process the input queue command by command: *)
    while Queue.length iq > 0 do
      let cmd = Queue.take iq in
      match cmd with
	| Telnet_will _
	| Telnet_wont _
	| Telnet_do _
	| Telnet_dont _ ->
	    (* These are the commands used to negotiate the telnet options.
	     * The session object can do it for you.
	     *)
	    session # process_option_command cmd
	| Telnet_data s ->
	    (* The data string s has been received. *)
	    ( match !state with
		  Start ->
		    if Str.string_match login_re s 0 then begin
		      (* Assume the host wants our username, and send it. *)
		      send_string (username ^ "\n") Username_sent
		    end
		| Username_sent ->
		    if Str.string_match passwd_re s 0 then begin
		      (* Assume the host wants our password, and send it. *)
		      send_string (password ^ "\n") Password_sent;
		    end
		| Password_sent ->
		    if Str.string_match cmd_re s 0 then begin
		      (* Assume the host wants the command: *)
		      (* Disable now echoing: *)
		      session # disable_remote_option Telnet_echo;
		      (* Send the command "ls" 0.1 seconds in the future.
		       * This way Telnet_echo can be disabled in the
		       * meantime.
		       *)
		      let g = Unixqueue.new_group esys in
		      Unixqueue.once esys g 0.1
			(fun () ->
			   Queue.add (Telnet_data("ls\n")) oq;
			   session # update();
			   state := Command_sent
			)
		    end;
		| Command_sent ->
		    print_string s;
		    (* Again the command-line prompt? *)
		    if Str.string_match cmd_re s 0 then
		      Queue.add Telnet_eof oq;       (* terminate the session *)
	    )
	| Telnet_eof ->
	    ()
	| _ ->
	    (* Unexpected command. *)
	    ()
    done
  in

  session # set_event_system esys;
  let opts = session # get_options in
  session # set_options { opts with 
			    verbose_connection = false;
			    verbose_input = false;
			    verbose_output = false };

  session # set_connection (Telnet_connect("localhost", 23));
  session # enable_remote_option Telnet_suppress_GA;
  session # enable_remote_option Telnet_echo;
  session # set_callback got_input;
  session # attach();
  session # run()
;;

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Caml-list] calling telnet from a caml program
  2002-11-06 16:11 [Caml-list] calling telnet from a caml program Alan Schmitt
  2002-11-06 16:26 ` Andrei Errapart
@ 2002-11-06 21:07 ` Alan Schmitt
  1 sibling, 0 replies; 8+ messages in thread
From: Alan Schmitt @ 2002-11-06 21:07 UTC (permalink / raw)
  To: caml-list

In response to my own post, if someone has the same problem ... I simply
used Cash. Here is the result:

let pid, pin, pout, pty_name = Cash.fork_pty_session (fun () -> Cash.exec
"/usr/bin/telnet" [])
in
let str = String.create 1024 in
let res = input pin str 0 1024 in
print_int res;
print_newline ();
print_endline str;
flush stdout

compiled with:
ocamlc -I +site-lib/pcre unix.cma str.cma camlp4/camlp4.cma pcre.cma cash.cma main.ml -o test

I get:
schmitta@localhost:~/SpeedTouchPro> ./test 
8
telnet> 

which is what I wanted ;-)

I still have a couple other small issues, but it feels like a giant leap
;-)

Alan

* Alan Schmitt (alan.schmitt@polytechnique.org) wrote:
> Hi,
> 
> I've spent a couple hours trying to make this work, but it does not want
> to ... I want to call telnet from a caml program, send some commands,
> and read some output. However there is something weird going on, as
> telnet does not seem to flush anything.
> 
> I've tried the following:
> 
> let pin,pout = Unix.open_process "/usr/bin/telnet" in
> print_endline "got it";
> flush stdout;
> output_string pout "open 127.0.0.1\r\n";
> flush pout;
> let str = input_line pin in
> print_endline str;
> output_string pout "quit\r\n";
> flush pout;
> print_endline "got it";
> flush stdout
> 
> which does not work (there should at least be the line "Trying
> 127.0.0.1")
> 
> and the following (but I am probably completely mistaken about the
> handling of pipes):
> 
> let outin, outout = Unix.pipe () in
> let inin, inout = Unix.pipe () in
> let pid = Unix.create_process "telnet" [| |] inin outout Unix.stderr in
> Unix.set_nonblock outin;
> let str = String.create 5 in
> let res = Unix.read outin str 0 1 in
> print_int res;
> print_newline ();
> print_endline str;
> flush stdout
> 
> (this should at least return the "t" of the telnet prompt ... but it
> fails with an exception saying there is no data to be read).
> 
> I'd really someone to point to me what I am doing wrong ...
> 
> Best,
> 
> Alan
> 
> 
> -- 
> The hacker: someone who figured things out and made something cool happen.
> -------------------
> To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
> Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> 


-- 
The hacker: someone who figured things out and made something cool happen.
-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2002-11-06 21:08 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2002-11-06 16:11 [Caml-list] calling telnet from a caml program Alan Schmitt
2002-11-06 16:26 ` Andrei Errapart
2002-11-06 16:34   ` Alan Schmitt
2002-11-06 17:20     ` Andrei Errapart
2002-11-06 17:39       ` Nicolas George
2002-11-06 19:51         ` David Brown
2002-11-06 20:56       ` Gerd Stolpmann
2002-11-06 21:07 ` Alan Schmitt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).