/* console state (for consctl) */
typedef struct Consstate Consstate;
struct Consstate{
int raw;
int hold;
};
Consstate *cons;
int notefd; /* for sending notes to the child */
int noproto; /* true if we shouldn't be using the telnet protocol */
int trusted; /* true if we need not authenticate - current user
is ok */
int nonone = 1; /* don't allow none logins */
int noworldonly; /* only noworld accounts */
enum
{
Maxpath= 256,
Maxuser= 64,
Maxvar= 32,
};
/* input and output buffers for network connection */
Biobuf netib;
Biobuf childib;
char remotesys[Maxpath]; /* name of remote system */
int alnum(int);
int conssim(void);
int fromchild(char*, int);
int fromnet(char*, int);
int termchange(Biobuf*, int);
int termsub(Biobuf*, uchar*, int);
int xlocchange(Biobuf*, int);
int xlocsub(Biobuf*, uchar*, int);
int challuser(char*);
int noworldlogin(char*);
void* share(ulong);
int doauth(char*);
/* shared data for console state */
cons = share(sizeof(Consstate));
if(cons == 0)
fatal("shared memory", 0, 0);
/* authenticate and create new name space */
Binit(&netib, 0, OREAD);
if (!trusted){
while(doauth(user) < 0)
if(++tries == 5){
logit("failed as %s: %r", user);
print("authentication failure:%r\r\n");
exits("authentication");
}
}
logit("logged in as %s", user);
putenv("service", "con");
/* simulate /dev/consctl and /dev/cons using pipes */
fd = conssim();
if(fd < 0)
fatal("simulating", 0, 0);
Binit(&childib, fd, OREAD);
/* start a shell in a different process group */
switch(childpid = rfork(RFPROC|RFNAMEG|RFFDG|RFNOTEG)){
case -1:
fatal("fork", 0, 0);
case 0:
close(fd);
fd = open("/dev/cons", OREAD);
dup(fd, 0);
close(fd);
fd = open("/dev/cons", OWRITE);
dup(fd, 1);
dup(fd, 2);
close(fd);
segdetach(cons);
execl("/bin/rc", "rc", "-il", nil);
fatal("/bin/rc", 0, 0);
default:
sprint(buf, "/proc/%d/notepg", childpid);
notefd = open(buf, OWRITE);
break;
}
/* two processes to shuttle bytes twixt children and network */
switch(fork()){
case -1:
fatal("fork", 0, 0);
case 0:
eofs = 0;
for(;;){
n = fromchild(buf, sizeof(buf));
if(n <= 0){
if(eofs++ > 2)
break;
continue;
}
eofs = 0;
if(write(1, buf, n) != n)
break;
}
break;
default:
while((n = fromnet(buf, sizeof(buf))) >= 0)
if(write(fd, buf, n) != n)
break;
break;
}
/* kill off all server processes */
sprint(buf, "/proc/%d/notepg", getpid());
fd = open(buf, OWRITE);
write(fd, "die", 3);
exits(0);
}
void
prompt(char *p, char *b, int n, int raw)
{
char *e;
int i;
int echo;
/*
* Process some input from the child, add protocol if needed. If
* the input buffer goes empty, return.
*/
int
fromchild(char *bp, int len)
{
int c;
char *start;
/*
* Read from the network up to a '\n' or some other break.
*
* If in binary mode, buffer characters but don't
*
* The following characters are special:
* '\r\n's and '\r's get turned into '\n's.
* ^H erases the last character buffered.
* ^U kills the whole line buffered.
* ^W erases the last word
* ^D causes a 0-length line to be returned.
* Intr causes an "interrupt" note to be sent to the children.
*/
#define ECHO(c) { *ebp++ = (c); }
int
fromnet(char *bp, int len)
{
int c;
char echobuf[1024];
char *ebp;
char *start;
static int crnl;
static int doeof;
/* simulate an EOF as a 0 length input */
if(doeof){
doeof = 0;
return 0;
}
/*
* bind a pipe onto consctl and keep reading it to
* get changes to console state.
*/
int
conssim(void)
{
int i, n;
int fd;
int tries;
char buf[128];
char *field[10];
/* a pipe to simulate the /dev/cons */
if(bind("#|", "/mnt/cons", MREPL) == -1)
fatal("/dev/cons1", 0, 0);
if(bind("/mnt/cons/data1", "/dev/cons", MREPL) == -1)
fatal("/dev/cons2", 0, 0);
/* a process to read /dev/consctl and set the state in cons */
switch(fork()){
case -1:
fatal("forking", 0, 0);
case 0:
break;
default:
return open("/mnt/cons/data", ORDWR);
}
int
alnum(int c)
{
/*
* Hard to get absolutely right. Use what we know about ASCII
* and assume anything above the Latin control characters is
* potentially an alphanumeric.
*/
if(c <= ' ')
return 0;
if(0x7F<=c && c<=0xA0)
return 0;
if(strchr("!\"#$%&'()*+,-./:;<=>?@`[\\]^{|}~", c))
return 0;
return 1;
}