You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
202 lines
5.4 KiB
202 lines
5.4 KiB
/* -------------------------------------------------------------------------- */ |
|
/* */ |
|
/* [shell.cpp] Shell */ |
|
/* */ |
|
/* -------------------------------------------------------------------------- */ |
|
/* */ |
|
/* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> */ |
|
/* */ |
|
/* This file is part of Konsole - an X terminal for KDE */ |
|
/* */ |
|
/* -------------------------------------------------------------------------- */ |
|
|
|
/* FIXME: |
|
- should be made able to be instanciated more than once |
|
*/ |
|
|
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
|
|
#include <termios.h> |
|
#include <fcntl.h> |
|
#include <unistd.h> |
|
#include <sys/ioctl.h> |
|
#include <grp.h> |
|
|
|
#if defined (_HPUX_SOURCE) |
|
#define _TERMIOS_INCLUDED |
|
#include <bsdtty.h> |
|
#endif |
|
|
|
#include <assert.h> |
|
#include <time.h> |
|
#include <signal.h> |
|
#include <qintdict.h> |
|
#include <sys/wait.h> |
|
|
|
#include "TEShell.h" |
|
#include "TEShell.moc" |
|
|
|
#define HERE fprintf(stdout,"%s(%d): here\n",__FILE__,__LINE__) |
|
|
|
FILE* log = NULL; //stdout; |
|
|
|
/* -------------------------------------------------------------------------- */ |
|
|
|
void Shell::setSize(int lines, int columns) |
|
// Tell the teletype handler what size the window is. |
|
// Called after a window size change. |
|
{ struct winsize wsize; |
|
if(fd < 0) return; |
|
wsize.ws_row = (unsigned short)lines; |
|
wsize.ws_col = (unsigned short)columns; |
|
ioctl(fd,TIOCSWINSZ,(char *)&wsize); |
|
} |
|
|
|
static char ptynam[] = "/dev/ptyxx"; |
|
static char ttynam[] = "/dev/ttyxx"; |
|
//static int comm_pid; |
|
|
|
static QIntDict<Shell> shells; |
|
|
|
static void catchChild(int) |
|
// Catch a SIGCHLD signal and exit if the child has died. |
|
{ pid_t pid; int status; |
|
pid = wait(&status); |
|
Shell* sh = shells.find(pid); |
|
if (sh) |
|
{ |
|
shells.remove(pid); sh->doneShell(status); |
|
} |
|
} |
|
|
|
void Shell::doneShell(int status) |
|
{ |
|
emit done(status); |
|
} |
|
|
|
int Shell::run(char* argv[], const char* term) |
|
{ |
|
pid_t comm_pid = fork(); |
|
if (comm_pid < 0) { fprintf(stderr,"Can't fork\n"); return -1; } |
|
if (comm_pid == 0) makeShell(ttynam,argv,term); |
|
if (comm_pid > 0) shells.insert(comm_pid,this); |
|
return 0; |
|
} |
|
|
|
void Shell::makeShell(const char* dev, char* argv[], const char* term) |
|
// only used internally. See `run' for interface |
|
{ int sig; char* t; |
|
// open and set all standard files to master/slave tty |
|
int tt = open(dev, O_RDWR); |
|
|
|
//reset signal handlers for child process |
|
for (sig = 1; sig < NSIG; sig++) |
|
signal(sig,SIG_DFL); |
|
|
|
// Don't know why, but his is vital for SIGHUP to find the child. |
|
// Could be, we get rid of the controling terminal by this. |
|
for (int i = 0; i < getdtablesize(); i++) if (i != tt) close(i); |
|
|
|
dup2(tt,fileno(stdin)); |
|
dup2(tt,fileno(stdout)); |
|
dup2(tt,fileno(stderr)); |
|
|
|
setsid(); // set process group (vital for bash) |
|
|
|
int pgrp = getpid(); // This sequence is necessary for |
|
ioctl(tt, TIOCSPGRP, (char *)&pgrp); // event propagation. Omitting this |
|
setpgid(tt,0); // is not noticeable with all |
|
close(open(dev, O_WRONLY, 0)); // clients (bash,vi). Because bash |
|
setpgid(tt,0); // heals this, use '-e' to test it. |
|
|
|
setuid(getuid()); setgid(getgid()); // drop privileges |
|
|
|
if (term && term[0]) |
|
{ |
|
t = (char*)malloc(6+strlen(term)); |
|
strcpy(t,"TERM="); strcat(t,term); |
|
putenv(t); |
|
free(t); |
|
} |
|
|
|
execvp (argv[0], argv); |
|
perror("exec failed"); |
|
exit(1); // control should never come here. |
|
} |
|
|
|
int openShell() |
|
{ int ptyfd; char *s3, *s4; |
|
static char ptyc3[] = "pqrstuvwxyz"; |
|
static char ptyc4[] = "0123456789abcdef"; |
|
|
|
// Find a master pty that we can open //////////////////////////////// |
|
|
|
ptyfd = -1; |
|
for (s3 = ptyc3; *s3 != 0; s3++) |
|
{ |
|
for (s4 = ptyc4; *s4 != 0; s4++) |
|
{ |
|
ptynam[8] = ttynam[8] = *s3; |
|
ptynam[9] = ttynam[9] = *s4; |
|
if ((ptyfd = open(ptynam,O_RDWR)) >= 0) |
|
{ |
|
if (geteuid() == 0 || access(ttynam,R_OK|W_OK) == 0) break; |
|
close(ptyfd); ptyfd = -1; |
|
} |
|
} |
|
if (ptyfd >= 0) break; |
|
} |
|
if (ptyfd < 0) { fprintf(stderr,"Can't open a pseudo teletype\n"); exit(1); } |
|
fcntl(ptyfd,F_SETFL,O_NDELAY); |
|
|
|
return ptyfd; |
|
} |
|
|
|
Shell::Shell() |
|
/* setup shell */ |
|
{ |
|
fd = openShell(); |
|
|
|
//for (int i = 1; i <= 15; i++) if (i!=SIGCHLD) signal(i,catch_sig); |
|
signal(SIGCHLD,catchChild); |
|
|
|
mn = new QSocketNotifier(fd, QSocketNotifier::Read); |
|
mw = new QSocketNotifier(fd, QSocketNotifier::Write); |
|
connect( mn, SIGNAL(activated(int)), this, SLOT(DataReceived(int)) ); |
|
connect( mw, SIGNAL(activated(int)), this, SLOT(DataWritten(int)) ); |
|
} |
|
|
|
Shell::~Shell() |
|
{ |
|
delete mn; |
|
delete mw; |
|
close(fd); |
|
} |
|
|
|
void Shell::send_byte(char c) |
|
{ |
|
write(fd,&c,1); mw->setEnabled(TRUE); |
|
} |
|
|
|
void Shell::send_string(const char* s) |
|
{ |
|
write(fd,s,strlen(s)); mw->setEnabled(TRUE); |
|
} |
|
|
|
void Shell::send_bytes(const char* s, int len) |
|
{ |
|
write(fd,s,len); mw->setEnabled(TRUE); |
|
} |
|
|
|
void Shell::DataReceived(int) |
|
{ char buf[4096]; |
|
int n = read(fd, buf, 4096); |
|
emit block_in(buf,n); |
|
if (log) for (int i = 0; i < n; i++) fputc(buf[i],log); |
|
} |
|
|
|
void Shell::DataWritten(int) |
|
{ |
|
mw->setEnabled(FALSE); written(); |
|
}
|
|
|