Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Chatting via socketpair: Your Cool Library Wanted

7 views
Skip to first unread message

Stephen M. Wynne

unread,
Dec 27, 1994, 10:20:38 PM12/27/94
to
I'm working with a modified version of Dov Grobgeld
<d...@menora.weizmann.ac.il>'s program to talk with wish in several
applications. To summarize, it uses socketpair to access STDIN in the
child and STDOUT in the parent; this isn't expect because it doesn't
supply a psuedo terminal.

Has anyone developed a library like this for extensible chatting with
processes other than chat2?

Thanks!

Steve Wynne
CPID Engineering System Administration
M/S 63-430
Wilsonville Industrial Park
Tektronix, Inc. Wilsonville, OR 97070
Email: ste...@pogo.wv.tek.com
Tollfree Voicemail: (800) 756-7000 + 685-3526
Voicemail Direct: (503) 321-2214
Phone: (503) 685-3526
FAX: (503) 685-3883


Tom Christiansen

unread,
Dec 28, 1994, 10:09:00 AM12/28/94
to
In comp.lang.perl,
ste...@florida.wv.tek.com (Stephen M. Wynne) writes:
:I'm working with a modified version of Dov Grobgeld

:<d...@menora.weizmann.ac.il>'s program to talk with wish in several
:applications. To summarize, it uses socketpair to access STDIN in the
:child and STDOUT in the parent; this isn't expect because it doesn't
:supply a psuedo terminal.
:
:Has anyone developed a library like this for extensible chatting with
:processes other than chat2?

Have you considered using a modified open2 such that it doesn't do the
exec in the kid, but rather return a 0?

I don't use socketpair(), as that's less portable than pipe(), although
you certainly could.

Hm... here's a bit of decade old code from my undergrad days that does
this.

--tom

/* process.c
*
* written by tom christiansen on Wed May 22 15:02:19 CDT 1985 to open
* a socket pair and let the user read and write from both sides.
* return -1 on error, otherwise put the correct file pointers
* into *input and *output.
*
* CAVEAT UTILITOR:
* you will block forever if one of the sides of the
* pipes blocks because of too much in the buffer.
*/

#include <stdio.h>
#include <signal.h>
#include <sys/param.h>
#include <sys/wait.h>
#include <sys/socket.h>

#define READ 0
#define WRITE 1
#define CHILD 0
#define PARENT 1

/*
* define QUIET if you don't want error messages
* to print out if something blows up due to test
* macros. this assumes you will perror() it yourself
* when the routine returns.
*/
#ifdef QUIET
# define announce(x) /* nothing at all */
#else
# define announce(x) perror(x)
#endif QUIET

/*
* first some macros to avoid lots of typing and ugly
* code.
*/
#define test0(x) \
if (!(x)) { \
announce("process: x"); \
return -1; \
}

#define test(x) \
if ( (x) < 0 ) { \
announce("process: x"); \
return -1; \
}

char FD_READ[] = "r";
char FD_WRITE[] = "w";

/*
* first a static array to hold the pid of
* the process we create so we can wait for
* it to die when we close up. there is enough
* room for all possible file descriptors (NOFILE)
* so this function may be called repeatedly by
* a program with no ill effects.
*/
static int pids[NOFILE];

/*****************************************************************
*
* process - a function to do what popen does, but to
* give you back file pointers for read as
* well as for write.
*
*****************************************************************/

int
process(cmd,input,output)
char *cmd;
FILE **input,**output;
{
int sock[2];
register pid;


/*
* use connected socket pair so we can do reads and
* writes on these things. if we used a pipe() call,
* it would be unidirectional and we would have to
* make two calls to get 4 file descriptors.
*/
test(socketpair(AF_UNIX,SOCK_STREAM,0,sock));

/*
* fork for the child command. don't bother doing
* a real fork, since we're just going to exec anyway,
* so borrow the parent's pages with a vfork.
*/
if((pid = vfork()) == CHILD) {

/*
* close old stdin and stdout to make room for socket
* descriptors.
*/
test(close(READ));
test(close(WRITE));

/*
* the kid will use the CHILD end. connect both his stdin
* and stdout to the CHILD socket, which is fine because
* unix domain sockets are bidirectional.
*/
test(dup2(sock[CHILD], WRITE));
test(dup2(sock[CHILD], READ));

/*
* don't need these anymore, and if we don't get rid of them, we
* won't be happy later on, because the process doesn't seem to die.
*/
test(close(sock[CHILD]));
test(close(sock[PARENT]));
/*
* now do the command. use the csh for tilda's sake.
*/
execl("/bin/csh", "csh", "-fc", cmd, 0);
perror("process kid: execl");
_exit(1);
}

/*
* -1 pid means we couldn't fork.
*/
if(pid == -1) {
perror("process: vfork");
(void) close(sock[CHILD]);
(void) close(sock[PARENT]);
return -1;
}

/*
* otherwise, we are the parent and healthy;
* remember the kid pid for a later close.
*/
pids[sock[PARENT]] = pid;

/*
* connect up the user's input and output file
* pointers to our end of the socket connection.
* give him one for read and one for write.
*/
test0(*input = fdopen(sock[PARENT],FD_READ));
test0(*output = fdopen(sock[PARENT],FD_WRITE));

test(close(sock[CHILD]));

return 0;
}


/****************************************************************
* close up the passed file and wait for the
* child to die.
***************************************************************/

#undef test
#define test(x) \
if ( (x) < 0 ) { \
announce("process_close: x"); \
return -1; \
}

/*
* don't need them both since they are the
* same thing
*/
int
process_close(input)
FILE *input;
{
register f,r, (*hstat)(), (*istat)(), (*qstat)();
int status;

f = fileno(input);
test(fclose(input));
/*
* don't need to close also output, as it is the same
*/

/*
* protect ourselves from unfriendly signals while
* waiting for child's death.
*/
istat = signal(SIGINT, SIG_IGN);
qstat = signal(SIGQUIT, SIG_IGN);
hstat = signal(SIGHUP, SIG_IGN);

/*
* wait for the child to die, keeping track of status.
* we saved the kid pid in the pids[] array when we
* first did the process call.
*/
while((r = wait((union wait *)&status)) != pids[f] && r == -1)
;
if(r == -1)
status = -1;

/*
* restore old sig values.
*/
(void) signal(SIGINT, istat);
(void) signal(SIGQUIT, qstat);
(void) signal(SIGHUP, hstat);

return(status);

}


/* lint output:
process.c:
*/

/*
* yes, folks, unlike the kernel, this file lints clean!
*/
--
Tom Christiansen Perl Consultant, Gamer, Hiker tch...@mox.perl.com
"I'll put an end to the idea that a woman's body belongs to her . . . the
practice of abortion shall be exterminated with a strong hand."
--Adolf Hitler, _Mein Kampf_

0 new messages