/* * Copyright (c) 1995, 1996 Gunther Schadow. All rights reserved. * * 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 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "pg_config.h" #include "ptydrv.h" #include #include #include #include #include #include "trap.h" static volatile bool ch_exited; static volatile int ch_result; static volatile int ch_signal; static pid_t ch_pid; static jmp_buf released_by_server; static void reaper(int sig) { int status; syslog(LOG_DEBUG, "reaper called by signal %d `%s'", sig, sys_siglist[sig]); if(waitpid(ch_pid, &status, 0) == FAIL) syslog(LOG_ERR, "waitpid: %m"); if(WIFEXITED(status)) { ch_exited = TRUE; ch_result = WEXITSTATUS(status); DBG(syslog(LOG_DEBUG, "process %d exited with %d", ch_pid, ch_result)); if(ch_result == 0) longjmp(released_by_server, TRUE); } else if(WIFSIGNALED(status)) { ch_exited = TRUE; ch_signal = WTERMSIG(status); syslog(LOG_DEBUG, "process %d received signal %d `%s'", ch_pid, ch_signal, sys_siglist[ch_signal]); } else if(WIFSTOPPED(status)) { ch_exited = FALSE; ch_signal = WSTOPSIG(status); syslog(LOG_DEBUG, "process %d stopped by signal %d `s'", ch_pid, ch_signal, sys_siglist[ch_signal]); kill(ch_pid, SIGCONT); } } result duplex(int mfd, int dfd) { ch_exited = FALSE; ch_signal = 0; ch_result = -1; ssignal(SIGCHLD, reaper, 0); if(setjmp(released_by_server)) goto success; if((ch_pid = fork()) == FAIL) { syslog(LOG_ERR,"fork: %m"); return FAIL; } if(ch_pid == 0) /* I am the child */ { if(bwddata(dfd, mfd) == FAIL) exit(1); else exit(0); } else /* I am the parent */ { if(fwddata(mfd, dfd) == FAIL) goto fail; } /* Fwddata returns only when there is noone refering to * the slave side of the pty, thus we don't need to give time * to the children. */ #ifdef LET_THE_CHILDREN_PLAY /* * Wait up to TIMEOUT seconds for child process to finish */ #define TIMEOUT 30 attimeout(TIMEOUT) goto kill_children; while(!ch_exited); ctimeout(); goto success; #endif kill_children: kill(ch_pid, SIGHUP); success: return SUCCESS; fail: /* * Kill child process */ kill(ch_pid, SIGHUP); ctimeout(); return FAIL; } result bwddata(int dfd, int mfd) { char buf[BUF_SIZE + 1]; int cnt; size_t total = 0; atsignal(SIGHUP, 0) goto success; do { DBG(syslog(LOG_DEBUG, "reading from pty")); cnt = read(dfd, buf, BUF_SIZE); if (cnt < 0) /* error */ { syslog(LOG_ERR, "read: %m"); goto fail; } else if (cnt > 0) /* normal data */ { int ncnt, wcnt = cnt, start = 0; DBG(syslog(LOG_DEBUG, "writing %d bytes to %s", cnt, master)); do { ncnt = write(mfd, &buf[start], wcnt); TSW(&buf[start], wcnt); if (ncnt < 1) { syslog(LOG_ERR, "write: %m"); goto fail; } wcnt -= ncnt; start += ncnt; total += ncnt; } while(wcnt > 0); } else DBG(syslog(LOG_DEBUG, "read returned 0")); } while(cnt != 0); success: csignal(SIGHUP); syslog(LOG_INFO, "%lu bytes transfered", total); return SUCCESS; fail: csignal(SIGHUP); syslog(LOG_INFO, "%lu bytes transfered", total); return FAIL; }