UNIX Socket FAQ

A forum for questions and answers about network programming on Linux and all other Unix-like systems

You are not logged in.

#1 2004-11-03 04:03 PM

felix
Member
Registered: 2003-12-04
Posts: 171

Re: Libexpect

Hi,

I'm using libexpect to interact with binarys, it's working well.


1 - But i noted that it always re-print what was typed in the command to interact!  :roll:

For example, i wrote a small code to interact with gdb, see:

#include <stdio.h>
#include <expect.h>

// gcc -lexpect5.32 -ltcl8.3 -lm -o x test.c

int main(){

FILE *fp1;
int ec;
char buff[128];

exp_loguser = 1;
exp_timeout = 3600;



if (0 == (fp1 = exp_popen("gdb"))) {

        printf("exp_popen failed\n");
        exit(-1);
}

while(1){

        if (0 > exp_fexpectl(fp1,exp_glob,"(gdb)",0,exp_end)) exit(-1);
        fgets(buff, sizeof(buff), stdin);
        fprintf(fp1,"%s", buff);
}

return(0);
}

The code compile and work well

# gcc -lexpect5.32 -ltcl8.3 -lm -o x test.c
# ./x
GNU gdb 2002-04-01-cvs
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-linux".
(gdb)
(gdb)help
help
List of classes of commands:

aliases -- Aliases of other commands
breakpoints -- Making program stop at certain points
data -- Examining data
files -- Specifying and examining files
internals -- Maintenance commands
obscure -- Obscure features
running -- Running the program
stack -- Examining the stack
status -- Status inquiries
support -- Support facilities
tracepoints -- Tracing of program execution without stopping the program
tui -- Text User Interface commands
user-defined -- User-defined commands

Type "help" followed by a class name for a list of commands in that class.
Type "help" followed by command name for full documentation.
Command name abbreviations are allowed if unambiguous.
(gdb)

As we can see, if i type enter then two lines are jumped instead of one. If i type help it show help two times, etc. I know that this "echoing" is made by me with my fprintf(), but if i remove this line the code stop to work.  :?

Someone know how to fix it ?

2 - Why i can't compile static with libexpect ? Someone know some trick to make it ?

Regards,

Offline

#2 2004-11-03 10:48 PM

RobSeace
Administrator
From: Boston, MA
Registered: 2002-06-12
Posts: 3,833
Website

Re: Libexpect

1. Well, I've never used libexpect, but taking a quick look at the
man pages and header files, try adding this:

exp_stty_init = "-echo";

2. I'm guessing probably because you have no "*.a" static version
of libexpect (or, one of the other libs you link against) installed...

Offline

#3 2004-11-04 03:00 PM

felix
Member
Registered: 2003-12-04
Posts: 171

Re: Libexpect

Hi RobSeace,

Thks once again by help! :)

RobSeace wrote:

1. Well, I've never used libexpect, but taking a quick look at the
man pages and header files, try adding this:

exp_stty_init = "-echo";


Lolll! Where do you found that ? I looked at my man 3 libexpect and it only say:

The tty setting can be further  modified  by  setting  the
       variable  exp_stty_init.   This variable is interpreted in
       the style of stty arguments.  For example, exp_stty_init =
       "sane"; repeats the default initialization.

Where fo you found the paramters that it accept ? Like the -echo ?  :o

RobSeace wrote:

2. I'm guessing probably because you have no "*.a" static version of libexpect (or, one of the other libs you link against) installed...

Hummm. What means a .a ? I searched by that i found

# ls -la /usr/lib/libexpect5.32.a
-rw-r--r--    1 root     root       194462 Aug  7  2001 /usr/lib/libexpect5.32.a

# file /usr/lib/libexpect5.32.a
/usr/lib/libexpect5.32.a: current ar archive

Is that ?

Ahh, when i try to compile in static i get it:

# gcc -lexpect5.32 -ltcl8.3 -lm -static -o x test.c
/tmp/cc2h4DjF.o: In function `main':
/tmp/cc2h4DjF.o(.text+0xb): undefined reference to `exp_loguser'
/tmp/cc2h4DjF.o(.text+0x15): undefined reference to `exp_timeout'
/tmp/cc2h4DjF.o(.text+0x1f): undefined reference to `exp_stty_init'
/tmp/cc2h4DjF.o(.text+0x30): undefined reference to `exp_popen'
/tmp/cc2h4DjF.o(.text+0x78): undefined reference to `exp_fexpectl'
collect2: ld returned 1 exit status

?

Ahhh, i tryed compile in a system without libexpect compile using my headers (copied from other system and added in #includes), but i don't got much sucess:

$ gcc -lexpect -ltcl8.3 -lm -o x test.c
/usr/bin/ld: cannot find -lexpect
collect2: ld returned 1 exit status

Ideas ?

Thks!

Regards,

Offline

#4 2004-11-04 03:09 PM

felix
Member
Registered: 2003-12-04
Posts: 171

Re: Libexpect

hello,

felix wrote:

Lolll! Where do you found that ? I looked at my man 3 libexpect and it only say:

The tty setting can be further  modified  by  setting  the
       variable  exp_stty_init.   This variable is interpreted in
       the style of stty arguments.  For example, exp_stty_init =
       "sane"; repeats the default initialization.

Where fo you found the paramters that it accept ? Like the -echo ?  :o

Opzzz! I found that at man stty! :)

Ahh, can i pass more then one paramter to it ? How ? If i do exp_stty_init = new-value, it always get only the last value...

Thks!

Regards,

Offline

#5 2004-11-04 10:17 PM

RobSeace
Administrator
From: Boston, MA
Registered: 2002-06-12
Posts: 3,833
Website

Re: Libexpect

Yep, "man stty" is the place to look... ;-)  And, I would assume
you could just separate multiple options with spaces, and have
them all be used...

What means a .a ?

Archive, I believe it stands for...  But, it's a standard suffix for a
static version of a library...  It's basically just a big archive of all
of the separate *.o files...

I'm not sure why it doesn't find that "libexpect5.32.a" and use it
with "-static"...  But, try just passing that *.a file on the command-line
to gcc, as if it were just another object to link in...

Offline

#6 2004-11-08 08:37 PM

felix
Member
Registered: 2003-12-04
Posts: 171

Re: Libexpect

Hello,

More one thks for you (I already completed 1 hundred?).  :lol:

RobSeace wrote:

Archive, I believe it stands for...  But, it's a standard suffix for a
static version of a library...  It's basically just a big archive of all
of the separate *.o files...

I'm not sure why it doesn't find that "libexpect5.32.a" and use it
with "-static"...  But, try just passing that *.a file on the command-line
to gcc, as if it were just another object to link in...

Hummm. Understood what is it. I tryed...

$ gcc -static -lexpect5.32 -ltcl8.3 -lm -o x l.c libexpect5.32.a
libexpect5.32.a(pty_termios.o): In function `exp_getptymaster':
pty_termios.o(.text+0x171): undefined reference to `openpty'
collect2: ld returned 1 exit status

Now, only one error! I looked at it in the .a file..

$ objdump  -t libexpect5.32.a
pty_termios.o:     file format elf32-i386

SYMBOL TABLE:

00000000         *UND*  00000000 openpty

I searched by a pty_termios.a or similar to pass as paramter to try solve it, but i couldn't find that.  :cry:

Ideas ? Any tip ?

ps.: A little off-topic, what signal a ctrl+D generate ? I tryed to get it with strace but in some systems appear a sigint other the sigabort... o.O What are the rigth ?

Thks..

Regards,

Offline

#7 2004-11-08 09:19 PM

RobSeace
Administrator
From: Boston, MA
Registered: 2002-06-12
Posts: 3,833
Website

Re: Libexpect

Well, CTRL-D generally doesn't generate a signal...  Typically,
it merely simulates EOF...  Ie: if you're manually typing in input to
some command, you can make it believe it has reached end of
file by hitting CTRL-D...

As for the other problem, I have no idea what library openpty() is
supposed to be in, but you just need to find it (and, hope it has a
"*.a" static version), and then just link it in as well...

Oh, hell, I just decided to search through my various system
libs, and I found openpty() in "libutil"...  So, try throwing "-lutil" into
your command-line, or if that doesn't work, hunt around your
system for "libutil.a" (or similar), and pass that to gcc, as well...

Offline

#8 2004-11-09 02:37 PM

felix
Member
Registered: 2003-12-04
Posts: 171

Re: Libexpect

Hi Robseace,

Thkz a lot again! :)

1 - You was rigth, it was the libutil.a!  :lol:

How do you found that ? I was thinking and the best that i could do was:

$ find /usr/lib/lib*.a -exec nm {} \; -print |grep -A 100 "T openpty"
000000ac T openpty
00000000 t pts_name
         U ptsname_r
         U realloc
         U strcpy
         U tcsetattr
         U unlockpt

forkpty.o:
0000007e t Letext
         U _exit
         U close
         U fork
00000000 T forkpty
         U login_tty
         U openpty
/usr/lib/libutil.a

Do you know a easier way ? :)

ps.: The important is that it worked! Thkz again! Thks for search it on your system...

2 - Ahh, I was asking about the CTRL+D because i noted the libexpect "handle" some keyboard action different of a "normal shell". Examples:

- If i make CTRL+D in a normal shell it exit, if i make in libexpect the last line is repeated.

- If i open the vi in libexpect the commands like "ESC+:q!" are printed in vi like "^[:q!" instead "exit" from vi.

So i'm trying to "fix" this problem... if you (or someone) have some idea, it's welcome! :)

Cya

Regards,

Offline

#9 2004-11-09 02:57 PM

RobSeace
Administrator
From: Boston, MA
Registered: 2002-06-12
Posts: 3,833
Website

Re: Libexpect

All I did was a simple for loop through various libs, printing them
and passing them to "nm | grep", as well...  The "find" approach
is just as good, if not easier...

It sounds like the pty isn't getting initialized properly, or something...
Are "exp_ttycopy" and "exp_ttyinit" set to 1?  Maybe throw "sane"
into the "exp_stty_init" options, as well? *shrug*

Offline

#10 2004-11-09 08:57 PM

felix
Member
Registered: 2003-12-04
Posts: 171

Re: Libexpect

Hi,

More one thkz for you! :)

So I will use the "find" way when i need... :)

About the tty, i was looking, i runned "stty -a" to compare differences...

The normal shell stty -a output
speed 38400 baud; rows 24; columns 80; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>;
eol2 = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W;
lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff
-iuclc -ixany -imaxbel
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt
echoctl echoke

The libexpect stty -a output
speed 38400 baud; rows 24; columns 80; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>;
eol2 = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W;
lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff
-iuclc -ixany imaxbel
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten -echo echoe echok -echonl -noflsh -xcase -tostop -echoprt
echoctl echoke

The two are exactily equal!  :roll:

ps.: I already had tryed setup exp_ttyinit = 1, exp_ttycopy = 1 and sane, but no result! :(

I was thinking, couldn't be it a problem how i read it from libexpect ? For example i read the stding with fgets() so I pass it to libexpect handler (via printf()).

So I really think that the problem can be it, I done some tests, i changed the line

fgets(buff, sizeof(buff), stdin);
fprintf(hnd,"%s", buff);

By

read(stdin, buff, sizeof(buff));
write(hnd, buff, sizeof(buff));

And now, it simple don't "intercept" nothing more... like if the commands wasn't sent, it neither is showed in the screen...

I tryed too

fgets(buff, sizeof(buff), stdin);
fwrite(buff, sizeof(buff), 1, hnd);

Now, if i press "TAB", "Backspace" it work, the "CTRL+D" is making something like a "TAB". So the keyboard is a little crazy in this way.. hehehe

If I use fread() and fwrite() it have a reaction like read() e write()...

What do you think ?

Thkz once again...

ps.: This thread is begin to be big...

Regards,

Offline

#11 2004-11-09 09:48 PM

RobSeace
Administrator
From: Boston, MA
Registered: 2002-06-12
Posts: 3,833
Website

Re: Libexpect

If you're going to run some curses type interactive app, then you
probably want to put your stdin into raw mode, and read single
key presses...  This old thread has code for how to...
Note: if you do so, you probably want to re-enable echoing in the
pty, too...

But, your posted read()/write() code is wrong as you have it, too...
You can't read() from stdin, since stdin in a stdio file pointer; you
need to read() from fileno(stdin) or STDIN_FILENO or just 0...  The
same goes for write()'ing to the exp_popen()'d file pointer...  But,
really, like I say, if you're going to run some interactive curses type
app, then you probably want single char/key input, not reading in
whole blocks/lines at a time...

Offline

#12 2004-11-10 03:40 PM

felix
Member
Registered: 2003-12-04
Posts: 171

Re: Libexpect

Hello,

You was rigth, my error was doesn't use the fileno()! hehehe

I read the thread and tryed to implement something like this getche().. the code is more or less like that

char ch;
struct termios old, new;

exp_loguser = 1;
exp_timeout = 3600;
//exp_stty_init = "-echo";
exp_ttyinit = 1;
exp_ttycopy = 1;

hnd = exp_popen(sh);

while(1){

        if (0 > exp_fexpectl(hnd, exp_glob, "$" , 0, exp_end)) exit(1);

        tcgetattr (0, &old);
        memcpy (&new, &old, sizeof (struct termios));
        new.c_lflag &= ~(ICANON | ECHO);
        tcsetattr (0, TCSANOW, &new);
        read (0, &ch, 1);
        write(fileno(hnd), &ch, 1);
        tcsetattr (0, TCSANOW, &old);
        fflush(hnd);
}

I changed the binary to a shell (to test), so if i compile and run it, it show the shell normal, if i press enter is show next line (of prompt) correct, but if i press any command (like ls) it doesn't show the output of command...

Example...

./x
mex:/tmp$
mex:/tmp$
mex:/tmp$ ls
[Here be all in blank]
[Here be all in blank]
....

I'm looking, i THINK that it can be because, I read 1 char from stdin and print exactily 1 char to expect handler, so the output command can be bigger then 1 char, so it "fail" ... !?

Strange...

Thks a lot and sorry for persistence...

Regards,

Offline

#13 2004-11-11 12:05 AM

RobSeace
Administrator
From: Boston, MA
Registered: 2002-06-12
Posts: 3,833
Website

Re: Libexpect

Yeah, the problem is you do exp_fexpectl() after every single char
of input, looking for a new shell prompt, which isn't going to come...
Why do you do that exp_fexpectl(), anyway??  Why not simply
just read as much data as it has to give you, and output it?  Or,
if you really want to use exp_fexpectl(), why not simply call it
without the search for "$", and with "exp_timeout" set to 0, so it
just reads as much as it can without blocking?  (Technically, you
should use select() on 0 (stdin) and the expect FD, and whenever
either one has data to read, read it and then send it to the other...)

And, there's no point in constantly switching back and forth between
canonical input mode and non...  Just set it to raw mode outside
the loop at the top, and restore outside at the bottom...

Offline

#14 2004-11-13 03:48 PM

felix
Member
Registered: 2003-12-04
Posts: 171

Re: Libexpect

Hi RobSeace and Others,

RobSeace wrote:

Yeah, the problem is you do exp_fexpectl() after every single char of input, looking for a new shell prompt, which isn't going to come...

Humm, what I immaginated...

RobSeace wrote:

Why do you do that exp_fexpectl(), anyway??

Well, because this libexpect is the unique way that i know to really interact with programs, since with normal popen(), etc i only can write() or read(), what isn't useful for me..  :cry:

RobSeace wrote:

  Why not simply just read as much data as it has to give you, and output it?

If I doesn't call (for exampel comment) the exp_fexpctl(), and try to read() from the while(1) nothing work...

RobSeace wrote:

  Or, if you really want to use exp_fexpectl(), why not simply call it without the search for "$",

Well, If i pass NULL or 0 in the place of $, it segfaults! If I pass "" in the place of "$", it simple doesn't read() or write() anything... :cry:

RobSeace wrote:

and with "exp_timeout" set to 0,

I tryed it too, and If i set exp_timeout to 0 it read a char and return me the shell, it mess (cause confusion) on the screen output...

I think you should suggest me to set -1, from man page

If a pattern matches, then the corresponding integer value
       is  returned.   Values  need  not  be unique, however they
       should be positive to avoid being  mistaken  for  EXP_EOF,
       EXP_TIMEOUT,  or EXP_FULLBUFFER.  Upon EOF or timeout, the
       value EXP_EOF or EXP_TIMEOUT  is  returned.   The  default
       timeout period is 10 seconds but may be changed by setting
       the variable exp_timeout.  A value of -1 disables a  time­
       out  from occurring.  A value of 0 causes the expect func­
       tion to return immediately (i.e., poll) after one  read().
       However  it must be preceded by a function such as select,
       poll, or an event manager callback to guarantee that there
       is data to be read.

But, the timeout appear to be good... not the problem...

RobSeace wrote:

so it just reads as much as it can without blocking?  (Technically, you should use select() on 0 (stdin) and the expect FD, and whenever either one has data to read, read it and then send it to the other...)

I'm not sure how to do it, since:

- I need use exp_fexpctl() else it doesn't work...

- If i read a big ammount of data (like you suggest me) I back to the old problem that i will be unable to "handle" char by char.. so specail keys (ctrl, alt, combination will not work)...

RobSeace wrote:

And, there's no point in constantly switching back and forth between canonical input mode and non...  Just set it to raw mode outside the loop at the top, and restore outside at the bottom...

Humm, OK. I putted the tcsetattr (0, TCSANOW, &old); when the while(1) finish.. hehhe ;)

Thks...

Regars,

Offline

#15 2004-11-13 08:01 PM

RobSeace
Administrator
From: Boston, MA
Registered: 2002-06-12
Posts: 3,833
Website

Re: Libexpect

I need use exp_fexpctl() else it doesn't work...

I think you're wrong about that...  After all, all exp_fexpectl() is doing
itself is a read() or fread(), at some point, and then searching for
some pattern you specified...  So, there should be no problem with
simply eliminating exp_fexpectl(), and doing your own read()/fread()
instead...

Also, I didn't mean to pass NULL or an empty string in place of "$",
I meant to leave it out of the passed arg list entirely!  Ie: call just

exp_fexpectl(hnd, exp_end)

But, really, I think you could just do your own reading, too...  And,
since I don't see much use in having a file pointer, I'd replace
exp_popen() with exp_spawn*()...

Oh, hell, ok here's a full working example program I just whipped
up quickly, which seems to work fine:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/select.h>
#include <expect.h>

int main (int argc, char *argv[])
{
    int fd, ret, i;
    fd_set rset;
    struct termios new, old;
    char buf[1024];

    if (argc < 2) {
        printf ("Usage: %s <command> [<args>...]\n", argv[0]);
        exit (1);
    }

    fd = exp_spawnv (argv[1], &(argv[1]));
    if (fd < 0) {
        perror ("exp_spawnv()");
        exit (-1);
    }

    /* set the FD and stdin to non-blocking mode */
    if (fcntl (fd, F_SETFL, fcntl (fd, F_GETFL, 0) | O_NONBLOCK)) {
        perror ("fcntl()");
        exit (-1);
    }
    if (fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) | O_NONBLOCK)) {
        perror ("fcntl(0)");
        exit (-1);
    }

    /* put the stdin tty into raw mode */
    if (tcgetattr (0, &old)) {
        perror ("tcgetattr()");
        exit (-1);
    }
    memcpy (&new, &old, sizeof (struct termios));
    cfmakeraw (&new);
    if (tcsetattr (0, TCSANOW, &new)) {
        perror ("tcsetattr()");
        exit (-1);
    }

    for (ret = 0;;) {
        FD_ZERO (&rset);
        FD_SET (0, &rset);
        FD_SET (fd, &rset);
        if (select (fd + 1, &rset, NULL, NULL, NULL) < 0) {
            perror ("select()");
            ret = -1;
            break;
        }

        if (FD_ISSET (0, &rset)) {
            i = read (0, buf, sizeof (buf));
            if (i < 0) {
                if ((errno != EINTR) && (errno != EAGAIN)) {
                    perror ("read(0)");
                    ret = -1;
                    break;
                }
            } else if (i > 0) {
                if (write (fd, buf, i) != i) {
                    perror ("write()");
                    ret = -1;
                    break;
                }
            } else {
                break;  /* EOF on stdin */
            }
        }

        if (FD_ISSET (fd, &rset)) {
            i = read (fd, buf, sizeof (buf));
            if (i <= 0) {
                if ((errno != EINTR) && (errno != EAGAIN)) {
                    /* just call it EOF; we seem to get EIO on EOF */
                    break;
                }
            } else if (i > 0) {
                if (write (1, buf, i) != i) {
                    perror ("write(1)");
                    ret = -1;
                    break;
                }
            }
        }
    }

    if (tcsetattr (0, TCSANOW, &old)) {
        perror ("tcsetattr()");
        ret = -1;
    }
    close (fd);

    exit (ret);
}

This is just quick and dirty code, and may have some flaws, but
at least I hope it gives you an idea of what I've been trying to say,
anyway...  It sure seems to work just fine for me, running "bash"
or "lynx" or various other things...

Offline

#16 2004-11-16 09:37 PM

felix
Member
Registered: 2003-12-04
Posts: 171

Re: Libexpect

Hi,

Well, what I can say ? YOU ARE THE GUY ROBSEACE!!!  :wink:

Thks a lottttttttttt!!! :)

Based in your code I understood better and make my code work correct!

Thks once again.

Regards,

Offline

#17 2017-11-14 11:38 AM

xdev
Member
Registered: 2017-11-14
Posts: 2

Re: Libexpect

Hi Felix,

Do you have the working code ? i am also facing the same issue.

felix wrote:

Hi,

Well, what I can say ? YOU ARE THE GUY ROBSEACE!!!  :wink:

Thks a lottttttttttt!!! :)

Based in your code I understood better and make my code work correct!

Thks once again.

Regards,

Offline

Board footer

Powered by FluxBB