This is the mail archive of the guile@cygnus.com mailing list for the guile project.
Index Nav: | [Date Index] [Subject Index] [Author Index] [Thread Index] | |
---|---|---|
Message Nav: | [Date Prev] [Date Next] | [Thread Prev] [Thread Next] |
As I couldn't find out how to contribute code in the Guile-1.2 distribution (I was probably looking in the wrong place) - I'm posting this to the mailing list (luckily it's not too long). I need to be able to run a child process on a pseudo-terminal for the regression-testing framework I'm writing. So I've written a primitive to do the job - and hacked it into my copy of the 1.2 library. I'd like to see it (or something very like it) in the next release of Guile if possible - so my test framework can be a simple guile module rather than requiring programs to be linked with an extra library. The code implements 'pty-child'. This primitive expects the name and arguments of a program to be run on a pseudo-terminal as it's argument. The return value is a list containing the read and write ports to talk to the child process and the process-id under which it is running. This code works for me under Gnu/Linux, though I have put some SysVR4 code in as a conditional compilation option, and that is untested. #include <signal.h> #include <sys/file.h> #include <sys/ioctl.h> #include <sys/wait.h> #ifndef MAX_OPEN #define MAX_OPEN 64 #endif static int pty_master(char* name, int len) { int master; #if defined(__svr4__) master = open("/dev/ptmx", O_RDWR); if (master >= 0) { const char *slave; grantpt(master); /* Change permission of slave. */ unlockpt(master); /* Unlock slave. */ slave = ptsname(master); if (slave == 0 || strlen(slave) >= len) { close(master); master = -1; } else { strcpy(name, (char*)slave); } } #else const char *groups = "pqrstuvwxyzPQRSTUVWXYZ"; master = -1; if (len > 10) { strcpy(name, "/dev/ptyXX"); while (master < 0 && *groups != '\0') { int i; name[8] = *groups++; for (i = 0; i < 16; i++) { name[9] = "0123456789abcdef"[i]; master = open(name, O_RDWR); if (master >= 0) { name[5] = 't'; break; } } } } #endif return master; } static int pty_slave(const char* name) { int slave; slave = open(name, O_RDWR); #if defined(__svr4__) if (ioctl(slave, I_PUSH, "ptem") < 0) { (void)close(slave); slave = -1; } if (ioctl(slave, I_PUSH, "ldterm") < 0) { (void)close(slave); slave = -1; } #endif return slave; } SCM_PROC (s_pty_child, "pty-child", 0, 0, 1, scm_pty_child); static SCM scm_pty_child(SCM args) { SCM ans = SCM_EOL; SCM prg; char slave_name[32]; int master; prg = SCM_CAR(args); /* * We permit this to be called either with multiple string arguments * or with a list of arguments. */ if (scm_list_p(prg) == SCM_BOOL_T && SCM_CDR(args) == SCM_EOL) { args = prg; prg = SCM_CAR(args); } SCM_ASSERT (SCM_NIMP(prg) && SCM_STRINGP(prg), prg, SCM_ARG1, s_pty_child); master = pty_master(slave_name, sizeof(slave_name)); if (master >= 0) { int p[2]; int pid; if (pipe(p) < 0) { (void)close(master); scm_misc_error("pty-child", "failed to open pipe", SCM_EOL); } pid = fork(); if (pid < 0) { (void)close(master); (void)close(p[0]); (void)close(p[1]); scm_misc_error("pty-child", "failed to fork child pipe", SCM_EOL); } if (pid == 0) { int i; for (i = 1; i < 32; i++) { signal(i, SIG_DFL); } if (p[1] != 3) { (void)dup2(p[1], 3); p[1] = 3; } for (i = 0; i < MAX_OPEN; i++) { if (i != 2 && i != p[1]) { (void)close(i); } } i = -1; #ifdef HAVE_SETSID i = setsid(); #endif #ifdef HAVE_SETPGID if (i < 0) { i = getpid(); i = setpgid(i, i); } #endif #ifdef TIOCNOTTY i = open("/dev/tty", O_RDWR); if (i >= 0) { (void)ioctl(i, TIOCNOTTY, 0); (void)close(i); } #endif i = pty_slave(slave_name); if (i < 0) { exit(1); /* Failed to open slave! */ } if (i != 0) { (void)dup2(i, 0); } if (i != 1) { (void)dup2(i, 1); } if (i > 1) { (void)close(i); } (void)write(p[1], "*", 1); /* Tell parent we are ready. */ (void)close(p[1]); (void)dup2(1, 2); scm_execl(scm_cons(prg, args)); exit(1); } else { char info; int len; SCM cpid; SCM rport; SCM wport; (void)close(p[1]); /* * Synchronize with child process - it should send us a byte * when everything is set up - immediately before the exec. */ len = read(p[0], &info, 1); (void)close(p[0]); if (len != 1) { (void)close(master); #ifdef HAVE_WAITPID { int status; int opts = 0; (void)waitpid(pid, &status, opts); } #endif scm_misc_error("pty-child", "failed to sync with child", SCM_EOL); } cpid = SCM_MAKINUM(pid); rport = scm_fdopen(SCM_MAKINUM(master), scm_makfrom0str("r")); wport = scm_fdopen(SCM_MAKINUM(master), scm_makfrom0str("w")); ans = scm_cons(rport, scm_cons(wport, scm_cons(cpid, SCM_EOL))); } } else { scm_misc_error("pty-child", "failed to get master pty", SCM_EOL); } return ans; }