/* vt_mode.c */ /* support vtp-sessions written by Christian A. Lademann <cal@zls.com> */ /* 02.05.95:cal:ported to samba-1.9.13 */ #define __vt_mode_c__ /* #include <stdio.h> */ /* #include <fcntl.h> */ /* #include <sys/types.h> */ /* #include <unistd.h> */ /* #include <signal.h> */ /* #include <errno.h> */ /* #include <ctype.h> */ /* #include <utmp.h> */ /* #include <sys/param.h> */ /* #include <sys/ioctl.h> */ /* #include <stdlib.h> */ /* #include <string.h> */ #include "includes.h" #include "vt_mode.h" #include <utmp.h> #ifdef SCO extern char *strdup(); #endif extern int Client; #ifdef LINUX # define HAS_VTY #endif #ifdef SCO # define HAS_PTY # define HAS_VTY # include <sys/tty.h> #endif extern int DEBUGLEVEL; extern char *InBuffer, *OutBuffer; extern int done_become_user; char master_name [64], slave_name [64]; int master, slave, i, o, e; int ms_type = MS_NONE, ms_poll = 0; /* VT_Check: test incoming packet for "vtp" or "iVT1\0" */ int VT_Check(char *buffer) { DEBUG(3,("Checking packet: <%10s...>\n", buffer+4)); if((strncmp(buffer+4, "vtp", 3) == 0 && smb_len(buffer) == 3) || (strncmp(buffer+4, "iVT1\0", 5) == 0 && smb_len(buffer) == 5)) return(1); else return(0); } /* VT_Start_utmp: prepare /etc/utmp for /bin/login */ int VT_Start_utmp(void) { struct utmp u, *v; char *tt; setutent(); strcpy(u.ut_line, VT_Line); if((v = getutline(&u)) == NULL) { if(strncmp(VT_Line, "tty", 3) == 0) tt = VT_Line + 3; else if(strlen(VT_Line) > 4) tt = VT_Line + strlen(VT_Line) - 4; else tt = VT_Line; strcpy(u.ut_id, tt); u.ut_time = time((time_t*)0); } strcpy(u.ut_user, "LOGIN"); strcpy(u.ut_line, VT_Line); u.ut_pid = getpid(); u.ut_type = LOGIN_PROCESS; pututline(&u); endutent(); return(0); } /* VT_Stop_utmp: prepare /etc/utmp for other processes */ int VT_Stop_utmp(void) { struct utmp u, *v; if(VT_Line != NULL) { setutent(); strcpy(u.ut_line, VT_Line); if((v = getutline(&u)) != NULL) { strcpy(v->ut_user, ""); v->ut_type = DEAD_PROCESS; v->ut_time = time((time_t*)0); pututline(v); } endutent(); } return(0); } /* VT_AtExit: Things to do when the program exits */ void VT_AtExit(void) { if(VT_ChildPID > 0) { kill(VT_ChildPID, SIGHUP); (void)wait(NULL); } VT_Stop_utmp(); } /* VT_SigCLD: signalhandler for SIGCLD: set flag if child-process died */ void VT_SigCLD(int sig) { if(wait(NULL) == VT_ChildPID) VT_ChildDied = True; else signal(SIGCLD, VT_SigCLD); } /* VT_SigEXIT: signalhandler for signals that cause the process to exit */ void VT_SigEXIT(int sig) { VT_AtExit(); exit(1); } /* VT_Start: initialize vt-specific data, alloc pty, spawn shell and send ACK */ int VT_Start(void) { char OutBuf [64], *X, *Y; ms_type = MS_NONE; master = slave = -1; #ifdef HAS_VTY #ifdef LINUX # define MASTER_TMPL "/dev/pty " # define SLAVE_TMPL "/dev/tty " # define LETTER1 "pqrs" # define POS1 8 # define LETTER2 "0123456789abcdef" # define POS2 9 #endif #ifdef SCO # define MASTER_TMPL "/dev/ptyp_ " # define SLAVE_TMPL "/dev/ttyp_ " # define LETTER1 "0123456" # define POS1 10 # define LETTER2 "0123456789abcdef" # define POS2 11 #endif if(ms_poll == MS_VTY || ms_poll == 0) { strcpy(master_name, MASTER_TMPL); strcpy(slave_name, SLAVE_TMPL); for(X = LETTER1; *X && master < 0; X++) for(Y = LETTER2; *Y && master < 0; Y++) { master_name [POS1] = *X; master_name [POS2] = *Y; if((master = open(master_name, O_RDWR)) >= 0) { slave_name [POS1] = *X; slave_name [POS2] = *Y; if((slave = open(slave_name, O_RDWR)) < 0) close(master); } } if(master >= 0 && slave >= 0) ms_type = MS_VTY; } # undef MASTER_TMPL # undef SLAVE_TMPL # undef LETTER1 # undef LETTER2 # undef POS1 # undef POS2 #endif #ifdef HAS_PTY #ifdef SCO # define MASTER_TMPL "/dev/ptyp%d" # define SLAVE_TMPL "/dev/ttyp%d" # define MIN_I 0 # define MAX_I 63 #endif if(ms_poll == MS_PTY || ms_poll == 0) { int i; for(i = MIN_I; i <= MAX_I && master < 0; i++) { sprintf(master_name, MASTER_TMPL, i); if((master = open(master_name, O_RDWR)) >= 0) { sprintf(slave_name, SLAVE_TMPL, i); if((slave = open(slave_name, O_RDWR)) < 0) close(master); } } if(master >= 0 && slave >= 0) ms_type = MS_PTY; } # undef MASTER_TMPL # undef SLAVE_TMPL # undef MIN_I # undef MAX_I #endif if(! ms_type) return(-1); VT_Line = strdup(strrchr(slave_name, '/') + 1); switch((VT_ChildPID = fork())) { case -1: return(-1); break; case 0: #ifdef SCO setsid(); #endif close(0); close(1); close(2); i = open(slave_name, O_RDWR); o = open(slave_name, O_RDWR); e = open(slave_name, O_RDWR); #ifdef LINUX setsid(); if (ioctl(slave, TIOCSCTTY, (char *)NULL) == -1) exit(1); #endif #ifdef SCO tcsetpgrp(0, getpid()); #endif VT_Start_utmp(); system("stty sane"); execlp("/bin/login", "login", "-c", (char*)0); exit(1); break; default: VT_Mode = True; VT_Status = VT_OPEN; VT_ChildDied = False; VT_Fd = master; signal(SIGCLD, VT_SigCLD); signal(SIGHUP, VT_SigEXIT); signal(SIGTERM, VT_SigEXIT); signal(SIGINT, VT_SigEXIT); signal(SIGQUIT, VT_SigEXIT); memset(OutBuf, 0, sizeof(OutBuf)); OutBuf [4] = 0x06; _smb_setlen(OutBuf, 1); send_smb(Client,OutBuf); return(0); break; } } /* VT_Output: transport data from socket to pty */ int VT_Output(char *Buffer) { int i, len, nb; if(VT_Status != VT_OPEN) return(-1); len = smb_len(Buffer); nb = write(VT_Fd, Buffer + 4, len); return((nb == len) ? 0 : -1); } /* VT_Input: transport data from pty to socket */ int VT_Input(char *Buffer,int Size) { int len; if(VT_Status != VT_OPEN) return(-1); memset(Buffer, 0, Size); len = read(VT_Fd, Buffer + 4, MIN(VT_MAXREAD, Size)); _smb_setlen(Buffer, len); return(len + 4); } /* VT_Process: main loop while in vt-mode */ void VT_Process(void) { static int trans_num = 0; extern int Client; int nread; VT_Start(); atexit(VT_AtExit); while (True) { int32 len; int msg_type; int msg_flags; int counter; int last_keepalive=0; struct fd_set si; struct timeval to, *top; int n, ret, t; errno = 0; t = SMBD_SELECT_LOOP*1000; FD_ZERO(&si); FD_SET(Client, &si); FD_SET(VT_Fd, &si); if(t >= 0) { to.tv_sec = t / 1000; to.tv_usec = t - (to.tv_sec * 1000); top = &to; } else top = NULL; if(VT_ChildDied) goto leave_VT_Process; n = select(MAX(VT_Fd, Client) + 1, &si, NULL, NULL, top); if(VT_ChildDied) goto leave_VT_Process; if(n == 0) { int i; time_t t; BOOL allidle = True; extern int keepalive; counter += SMBD_SELECT_LOOP; t = time(NULL); if (keepalive && (counter-last_keepalive)>keepalive) { if (!send_keepalive(Client)) goto leave_VT_Process; last_keepalive = counter; } } else if(n > 0) { counter = 0; if(FD_ISSET(VT_Fd, &si)) { /* got input from vt */ nread = VT_Input(OutBuffer, MIN(BUFFER_SIZE,lp_maxxmit())); if(nread > 0) send_smb(Client,OutBuffer); } if(FD_ISSET(Client, &si)) { /* got input from socket */ if(receive_smb(Client,InBuffer, 0)) { msg_type = CVAL(InBuffer,0); msg_flags = CVAL(InBuffer,1); len = smb_len(InBuffer); DEBUG(6,("got message type 0x%x of len 0x%x\n",msg_type,len)); nread = len + 4; DEBUG(3,("%s Transaction %d of length %d\n",timestring(),trans_num,nread)); if(msg_type == 0) VT_Output(InBuffer); else { nread = construct_reply(InBuffer,OutBuffer,nread,MIN(BUFFER_SIZE,lp_maxxmit())); if(nread > 0) { if (nread != smb_len(OutBuffer) + 4) { DEBUG(0,("ERROR: Invalid message response size! %d %d\n", nread, smb_len(OutBuffer))); } else send_smb(Client,OutBuffer); } } } else if(errno == EBADF) goto leave_VT_Process; } } trans_num++; } leave_VT_Process: /* if(VT_ChildPID > 0) kill(VT_ChildPID, SIGHUP); VT_Stop_utmp(VT_Line); return; */ close_sockets(); exit(0); }