Chinaunix首页 | 论坛 | 博客
  • 博客访问: 103631675
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类: C/C++

2008-04-16 22:47:33

/*
 * 伪终端主控程序 for SCO UNIX
 *
 * Usage: mypty [-f config-file] [-l logdevice]
 *      default config-file is /etc/mypty.conf 
 *            logdevice   is /dev/tty12
 *
 *     cc -o mypty mypty.c -lsocket
 *                             陈杰 2001/01/11
 *
 * 注:本程序为简单计,各伪终端需使用不同的连接端口号
 * 配置文件形如
 * 终端号 合法地址  端口(需不重复)
 * ttyp11 192.0.0.1 2000
 * ttyp11 192.0.0.2 2001
 */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "varargs.h"

#if defined(INTER)
//struct tty { int x; };
#include 
#endif

// int  LOGERR=1;

#define      SCO32

#if !defined(TIOCPKT)
#undef   FIONBIO
#include 
#endif

#define  DEFAULT_CONFIG_FILE_NAME   "/etc/mypty.conf"
#define  DEFAULT_LOGDEVICE          "/dev/tty12"
#define  BUFLEN                     1024

typedef struct myptyConfigStru Config, *PConfig;

struct myptyConfigStru {
   PConfig  next;                    // 下一设备
   char     p_tty[32];               // 伪终端设备名
   long     ipaddr;                  // 合法客户地址
   unsigned short   port;            // 侦听端口号
   short    status;                  // 状态
   short    flags;                   // 标志
   long     timeout;                 // 关闭连接的超时
   int      ptyfd;                   // 伪终端文件描述字
   int      sockfd;                  // 套接字
   int      waitfd;                  // 侦听套接字
   char     pty_buf[BUFLEN+1];       // 伪终端输入缓冲区
   int      pty_count;               // 已输入字节数
   char     sock_buf[BUFLEN+1];      // 套接字缓冲区
   int      sock_count;              // 已输入字节数
};

/* Define status */
#define   IN_INIT     0
#define   IN_ACT      1
#define   PTY_OK      10
#define   PTY_ERR     11
#define   SOCK_OK     20
#define   SOCK_ERR    21
#define   WAIT_OK     30
#define   WAIT_ERR    31

/* Define flags */
#define   CONFIGURED   0x01   /* 已配置 */
#define   INWAITCONN   0x02   /* 进入等待连接状态 */

PConfig  szConf=NULL;
PConfig  ConfRoot=NULL;
int      AnyTimeout=0;
int      MaxFD=0;
unsigned long localip=0;
char     *cnf_file=NULL;
char     *errname=NULL;
pid_t    mypid=-1;

void daemon_init(void);
void mypty(void);

void cj_Errlog(va_alist)
va_dcl
{
   FILE   *fp  
   va_list args;
   char *fmt,buf[640];
   struct tm *tm;
   time_t t;

// if (LOGERR){
   fp = fopen(errname , "a" )  
   if (fp==NULL) return 
   va_start(args);
   fmt = va_arg(args, char * );
   vsprintf(buf, fmt, args );
   va_end(args);
   t = time(NULL);
   tm = localtime(&t);
   if (mypid==-1) mypid = getpid();
   fprintf(fp, "%04d-%02d-%02d %02d:%02d:%02d <%d>:%s\r\n", tm->tm_year+1900,
            tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
            mypid, buf )  
   fclose(fp)  
// }
   return 
}

usage(void)
{
   fprintf(stderr,"Usage: mypty [-f config-file] [-l logdevice]\n\7");
   fprintf(stderr,"Default config-file is /etc/mypty.conf\n");
   fprintf(stderr,"        logdevice   is /dev/tty12\n");
   fprintf(stderr,"Example:mypty -f /etc/mypty.conf\n");
   fprintf(stderr,"        mypty -f /etc/mypty.conf -l /dev/null\n");
   exit(-1);
}

main(int argc, char *argv[])
{
   int c;

   errname  = DEFAULT_LOGDEVICE;
   cnf_file = DEFAULT_CONFIG_FILE_NAME;
   while ((c=getopt(argc, argv, "f:l:"))!=-1){
      switch(c){
         case 'f':cnf_file = optarg;
            if (access(cnf_file, R_OK)!=0){
               perror(cnf_file);
               exit(2);
            }
            break;
         case 'l':errname = optarg;
            if (access(errname, F_OK)!=0)
               close(open(errname, O_CREAT, 0660));
               if (access(errname, W_OK)!=0){
                  perror(errname);
                  exit(2);
               }
            break;
         case '?':usage();break;
         default :fprintf(stderr,"Error options: %c <%d>", c, c);
            exit(1);
      }
   }
   daemon_init();
   read_config(NULL);
   mypid = getpid();
   mypty();

   exit(0);
}

static int lockfd=-1;
static FILE *lockfp=NULL;
void endproc(int sig)
{
   PConfig   pc;

   for (pc=ConfRoot;ConfRoot;pc=ConfRoot){
      close(pc->ptyfd);
      close(pc->waitfd);
      close(pc->sockfd);
      ConfRoot=pc->next;
      free(pc);
   }
   lockf(lockfd, F_ULOCK, 0);
   close(lockfd);
   cj_Errlog("Terminated signal received");
   exit(0);
}

void my_child(int sig)
{
   int  i, pid, stat;

   pid = waitpid(-1, &stat, WNOHANG);
   while (pid>0)
      pid = waitpid(-1, &stat, WNOHANG);
}

/* 转入后台, 监听客户连接 */
void daemon_init(void)
{
   int   i;
   int   fd;
   int   status;
   int   frominit;

   signal(SIGTERM, endproc);
   signal(SIGCLD, my_child);
   signal(SIGHUP, SIG_IGN);
   signal(SIGINT, SIG_IGN);
   signal(SIGQUIT, SIG_IGN);
#ifdef SIGTTOU
   signal(SIGTTOU, SIG_IGN);
#endif
#ifdef SIGTTIN
   signal(SIGTTIN, SIG_IGN);
#endif
#ifdef SIGTSTP
   signal(SIGTSTP, SIG_IGN);
#endif

   /* 如果由 inittab 启动, 则无需手工进入后台 */
   frominit = (getppid() == 1);
   if (!frominit) {
      switch (i=fork()) {
         case -1: perror("fork");
                  exit(1);
         case  0: break;
         default: exit(0);
      }
   }

   umask(0);
   /* 回到根目录以避免文件系统装卸问题 */
   chdir("/");

   /* 设置成为新的进程组长 */
   setpgrp();

   /* 再次 fork 脱离控制台, 如果由 init 启动, 则父进程 wait */
   switch (i=fork()) {
      case -1: perror("fork2");
               exit(1);
      case  0: break;
      default: if (frominit)
                  wait(&status);
               exit(0);
   }
}

void reread_config(int sig)
{
   /*
   重读配置文件
   */
   cj_Errlog("Reconfigure: signal (%d) received.", sig);
   read_config(NULL);
}

read_config(char *fname)
{
   char    buf[256], p_tty[32], ip[32], ports[32], dummy[64];
   int     lines=0, port;
   long    ipaddr;
   PConfig pc, tail;
   struct  hostent   *hp;
   struct  in_addr in;

   if (fname)
      cnf_file = fname;
   cj_Errlog("read_config(%s) start.", cnf_file);
   if (lockfd==-1){   /* 第一次运行,测试锁 */
      lockfd = open(cnf_file, O_RDWR);
      if (lockfd<0) {
         perror(cnf_file);
         exit(3);
      }
      if (lockf(lockfd, F_TLOCK, 0)<0){
         cj_Errlog("Another copy of this program is running...");
         close(lockfd);
         exit(1);
      }
      else cj_Errlog("Filelock of %s successfully", cnf_file);
   }
   if (lockfp==NULL)
      lockfp = fdopen(lockfd, "r");
   if (lockfp==NULL) {
      // perror(cnf_file);
      cj_Errlog("Error open config file:<%d>%s", errno, cnf_file);
      goto done;
   }
   rewind(lockfp);
   for (pc=ConfRoot,tail=NULL;pc;tail=pc,pc=pc->next)
      pc->flags &= ~CONFIGURED;                        /* 置未配置标志 */
   while (fgets(buf, sizeof(buf), lockfp)) {
      lines++;
      if (buf[0]==0 || buf[0]=='\n' || buf[0]=='#')
         continue;
      if (sscanf(buf,"%31s%31s%31s%31s",p_tty,ip,ports,dummy) != 3) {
         cj_Errlog("Config file format error: line=%d", lines);
         continue;
      }
      if (p_tty[0]=='#') continue; // 注释行
      if (strncmp(p_tty, "ttyp", 4) != 0) {
         cj_Errlog("pty name error: lines=%d", lines);
         continue;
      }
      hp = gethostbyname(ip);
      if (hp)
         ipaddr = *(long *) hp->h_addr;
      else {
         ipaddr = inet_addr(ip);
         if (ipaddr==-1) {
            cj_Errlog("ip address error: lines=%d", lines);
            continue;
         }
      }
      port = atoi(ports);
      if (port <= 1024 || port >= 0x30000) {
         cj_Errlog("tcp port error: lines=%d", lines);
         continue;
      }
      for (pc=ConfRoot;pc;pc=pc->next) {
         if (strcmp(pc->p_tty, p_tty)==0)
            break;
      }
      if (pc==NULL) {
         pc = (PConfig) calloc(sizeof(Config),1);
         if (pc == NULL) {
            perror("calloc");
            exit(2);
         }
         pc->next = NULL;
         if (tail)
            tail->next = pc;
         else
            ConfRoot   = pc;
         tail = pc;
         strcpy(pc->p_tty, p_tty);
         AnyTimeout++;
         pc->status  = IN_INIT;
         pc->flags   = 0;
         pc->timeout = 0;
         pc->ptyfd   = -1;
         pc->sockfd  = -1;
         in.s_addr   = ipaddr;
         cj_Errlog("new tty added:%s at %s:%d", pc->p_tty, inet_ntoa(in), port);
      }
      if (pc->status == IN_ACT || pc->status == SOCK_OK) {
         if (pc->ipaddr != ipaddr || pc->port != htons(port))
            lost_connection(pc);
      }
      if (pc->status == WAIT_OK) {
         if (pc->ipaddr != ipaddr || pc->port != htons(port))
            lost_listen(pc);
      }
      pc->ipaddr = ipaddr;
      pc->port   = htons(port);
      pc->flags |= CONFIGURED;
   }
   // fclose(lockfp);
   endhostent();
done:
   for (pc=ConfRoot,tail=NULL;pc;pc=(tail?tail->next:ConfRoot)) {
      if (pc->flags & CONFIGURED) {
         tail = pc;
         continue;
      }
      /* 关闭未配置的伪终端
      printf("Shutting down\n");
      */
      cj_Errlog("Shutting down: %s", pc->p_tty);
      close(pc->ptyfd);
      close(pc->waitfd);
      close(pc->sockfd);

      if (tail==NULL)
         ConfRoot = pc->next;
      else
         tail->next = pc->next;
      free(pc);
   }
   cj_Errlog("read_config(%s) complete.", cnf_file);
}

open_pty(PConfig pc)
{
   int  on = 1, target;
   long t = time(NULL);
   char p_tty[256];

   sprintf(p_tty, "/dev/p%s", &pc->p_tty[1]);
   pc->ptyfd = open(p_tty, O_RDWR | O_NDELAY);
   // pc->ptyfd = open(p_tty, O_RDWR);

   if (pc->ptyfd < 0) {
      // perror(pc->p_tty);
      cj_Errlog("error open %s", pc->p_tty);
      pc->timeout = t + 5;
      AnyTimeout++;
      pc->status = PTY_ERR;
   }
   else {
      /* pty devices */
      ioctl(pc->ptyfd, TIOCPKT, &on);
      pc->status = PTY_OK;
      if (pc->ptyfd > MaxFD)
         MaxFD = pc->ptyfd;
      cj_Errlog("%s opened successfully", pc->p_tty);
   }
}

open_socket(PConfig pc)
{
   int    on = 1, flags;
   long   t = time(NULL);
   struct sockaddr_in   sin;
   struct linger   lg;

   sin.sin_family      = AF_INET;
   sin.sin_addr.s_addr = INADDR_ANY;
   sin.sin_port        = pc->port;
   pc->waitfd = socket(AF_INET, SOCK_STREAM, 0);
   if (pc->waitfd < 0) {
      perror("socket");
      exit(2);
   }
   flags = fcntl(pc->waitfd, F_GETFL, 0);
   flags |= O_NONBLOCK;
   fcntl(pc->waitfd, F_SETFL, flags);
   lg.l_onoff  = 1;
   lg.l_linger = 30;
   if (setsockopt(pc->waitfd,SOL_SOCKET,SO_LINGER,(char *)&lg,sizeof(lg)) < 0) {
      perror("SO_LINGER");
      exit(2);
   }
   setsockopt(pc->waitfd,SOL_SOCKET,SO_REUSEADDR,NULL,0);
   if (bind(pc->waitfd, (struct sockaddr *)&sin, sizeof(sin))<0) {
      perror("Bind");
      close(pc->waitfd);
      exit(2);
   }
   if (listen(pc->waitfd, 1)<0){
      perror("Listen");
      close(pc->waitfd);
      exit(2);
   }
   pc->sockfd = -1;
   pc->status = WAIT_OK;
   if (pc->waitfd > MaxFD)
      MaxFD = pc->waitfd;
   AnyTimeout++;
// cj_Errlog("get waitfd ok: %d<%s>", pc->waitfd, pc->p_tty);
}

lost_listen(PConfig pc)         /* 失去监听套接字 */
{
   long   t=time(NULL);

   cj_Errlog("lost listen on %s at port %d", pc->p_tty, ntohs(pc->port));
   close(pc->waitfd);
   pc->timeout = t + 5;
   AnyTimeout++;
   pc->status = WAIT_ERR;
}

lost_connection(PConfig pc)         /* 失去连接, 进入 wait */
{
   long   t=time(NULL);
   struct in_addr from;

   from.s_addr = pc->ipaddr;
   cj_Errlog("lost connection on %s from %s:%d", pc->p_tty, inet_ntoa(from), ntohs(pc->port));
   pc->timeout = t + 5;
   AnyTimeout++;
   close(pc->sockfd);
   pc->status = PTY_OK;
}

check_status(void)
{
   PConfig pc;
   long t=time(NULL);

// cj_Errlog("Check status begin ", AnyTimeout);
   for (pc = ConfRoot;pc;pc=pc->next) {
      // cj_Errlog("Check %s: status=%d", pc->p_tty, pc->status);
      if (pc->status == IN_INIT) {
         /* 打开伪终端设备 */
         AnyTimeout--;
         open_pty(pc);
      } else if (pc->status==PTY_ERR) {
         /* 超时结束重新尝试 */
         if (pc->timeout <= t) {
            AnyTimeout--;
            open_pty(pc);
         }
      }

      if (pc->status==PTY_OK) {
         /* 打开套接字, 进入 wait */
         AnyTimeout--;
         open_socket(pc);
      } else if (pc->status==WAIT_ERR) {
         /* 超时结束重新尝试 */
         if (pc->timeout <= t) {
            AnyTimeout--;
            open_socket(pc);
         }
      }

      if (pc->status==WAIT_OK){
         /* 等待客户连接 */
      }
      else if (pc->status==SOCK_ERR){
         AnyTimeout--;
         close(pc->sockfd);
         pc->status = WAIT_OK;
      }

      if (pc->status == SOCK_OK){
         // AnyTimeout--;
         pc->status = IN_ACT;
         close(pc->waitfd);
      }
      else if (pc->status == IN_ACT) AnyTimeout--;
   }
// cj_Errlog("Check status end ", AnyTimeout);
}

void mypty(void)
{
   PConfig pc;
   fd_set  infd, outfd, errfd;
   int     n, len, on=1;
   char    ch, *BufPtr;
   struct  timeval   tv;
   struct  sockaddr_in from;
   char mesg[256];

   sigset(SIGHUP, reread_config);
   for (;;) {
      while (ConfRoot == NULL) {
         // printf("mypty has nothing to do.\n");
         /* 等待, 直到读完配置文件 */
         pause();
      }
      if (AnyTimeout)
         check_status();            /* 检查状态 */
      FD_ZERO(&infd);
      FD_ZERO(&outfd);
      FD_ZERO(&errfd);
      for (pc=ConfRoot;pc;pc=pc->next) {
         if (pc->status != IN_ACT && pc->status != WAIT_OK){
            if (pc->status==PTY_OK) read(pc->ptyfd, pc->pty_buf, BUFLEN);
            continue;
         }
         if (pc->status==IN_ACT){
            FD_SET(pc->sockfd, &errfd);
            if (pc->pty_count > 0)
               FD_SET(pc->sockfd, &outfd);
            else
               FD_SET(pc->ptyfd, &infd);

            if (pc->sock_count > 0)
               FD_SET(pc->ptyfd, &outfd);
            else
               FD_SET(pc->sockfd, &infd);
         }
         else
            FD_SET(pc->waitfd, &infd);
      }
      tv.tv_sec  = 5;
      tv.tv_usec = 0;
      n = select(MaxFD+1, &infd, &outfd, &errfd, &tv);
// cj_Errlog("select end of <%d>", n);
      if (n <= 0) {
         if (n < 0 && errno != EINTR)
            cj_Errlog("select return error:%d", errno);
         continue;
      }
      for (pc=ConfRoot;pc;pc=pc->next) {
         if (pc->status != IN_ACT && pc->status != WAIT_OK)
            continue;
         if (pc->status == IN_ACT){
            BufPtr=pc->pty_buf;

            if (FD_ISSET(pc->sockfd, &errfd)) {
cj_Errlog("%s socket exception in errfd", pc->p_tty);
               lost_connection(pc);
            }

            if (FD_ISSET(pc->ptyfd, &infd)) {
               pc->pty_count = read(pc->ptyfd, pc->pty_buf, BUFLEN);
               if (pc->pty_count<0)
                   lost_connection(pc);
// cj_Errlog("%s ptty in end<%d>", pc->p_tty, pc->pty_count);
            }
            if (FD_ISSET(pc->sockfd, &infd)) {
               pc->sock_count = read(pc->sockfd, pc->sock_buf, BUFLEN);
               if (pc->sock_count<=0)
                   lost_connection(pc);
// cj_Errlog("%s socket in end<%d>", pc->p_tty, pc->sock_count);
            }

            if (FD_ISSET(pc->ptyfd, &outfd) || pc->sock_count>0) {
// cj_Errlog("%s ptty out OK", pc->p_tty);
               if (write(pc->ptyfd, pc->sock_buf, pc->sock_count)<0)
                   lost_connection(pc);
               pc->sock_count = 0;
            }

            if (FD_ISSET(pc->ptyfd, &infd)) {
               if (pc->pty_count==0){            /* 描述字可读且读到0个字符 */
                  close(pc->ptyfd);
                  sprintf(mesg, "/dev/p%s", &pc->p_tty[1]);
                  pc->ptyfd = open(mesg, O_RDWR | O_NDELAY);
                  if (pc->ptyfd<0) lost_connection(pc);
                  else {
                     ioctl(pc->ptyfd, TIOCPKT, &on);
                     if (pc->ptyfd>MaxFD) MaxFD = pc->ptyfd;
                  }
               }
            }
            if (FD_ISSET(pc->sockfd, &outfd) || pc->pty_count>0) {
// cj_Errlog("%s socket out OK", pc->p_tty);
               if ( *BufPtr=='\0') {
                   BufPtr++;
                   if (write(pc->sockfd, BufPtr, --pc->pty_count)<0)
                      lost_connection(pc);
               }
               else {
                   if (send(pc->sockfd, BufPtr, 1, MSG_OOB)<0)
                      lost_connection(pc);
               }
               pc->pty_count = 0;
            }
         }
         else{
            if (FD_ISSET(pc->waitfd, &infd)) {
            /* 客户连接建立, 检查是否合法IP地址 */
               // cj_Errlog("%s socket accept OK", pc->p_tty);
               len = sizeof(from);
               bzero((char *)&from,sizeof(from));
               pc->sockfd = accept(pc->waitfd, (struct sockaddr *)&from, &len);
               // fcntl(pc->sockfd, F_SETFL, O_NDELAY);
               if (pc->sockfd<0)
                  pc->status = SOCK_ERR;
               else{
                  if (from.sin_addr.s_addr != pc->ipaddr){
                     write(pc->sockfd, "Invalid custom address\r\n\7", 26);
                     cj_Errlog("Remote address %s:%d denied",
                        inet_ntoa(from.sin_addr), ntohs(pc->port));
                     pc->status = SOCK_ERR;
                  }
                  else{
                     sprintf(mesg, "Terminal ID: %s", pc->p_tty);
                     write(pc->sockfd, mesg, strlen(mesg)+1);
                     
 伪终端主控程序 for SCO UNIX
作者:  出处:Unix爱好者家园unix-cd.com  更新时间: 2005年08月03日 
/*
 * 伪终端主控程序 for SCO UNIX
 *
 * Usage: mypty [-f config-file] [-l logdevice]
 *      default config-file is /etc/mypty.conf 
 *            logdevice   is /dev/tty12
 *
 *     cc -o mypty mypty.c -lsocket
 *                             陈杰 2001/01/11
 *
 * 注:本程序为简单计,各伪终端需使用不同的连接端口号
 * 配置文件形如
 * 终端号 合法地址  端口(需不重复)
 * ttyp11 192.0.0.1 2000
 * ttyp11 192.0.0.2 2001
 */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "varargs.h"

#if defined(INTER)
//struct tty { int x; };
#include 
#endif

// int  LOGERR=1;

#define      SCO32

#if !defined(TIOCPKT)
#undef   FIONBIO
#include 
#endif

#define  DEFAULT_CONFIG_FILE_NAME   "/etc/mypty.conf"
#define  DEFAULT_LOGDEVICE          "/dev/tty12"
#define  BUFLEN                     1024

typedef struct myptyConfigStru Config, *PConfig;

struct myptyConfigStru {
   PConfig  next;                    // 下一设备
   char     p_tty[32];               // 伪终端设备名
   long     ipaddr;                  // 合法客户地址
   unsigned short   port;            // 侦听端口号
   short    status;                  // 状态
   short    flags;                   // 标志
   long     timeout;                 // 关闭连接的超时
   int      ptyfd;                   // 伪终端文件描述字
   int      sockfd;                  // 套接字
   int      waitfd;                  // 侦听套接字
   char     pty_buf[BUFLEN+1];       // 伪终端输入缓冲区
   int      pty_count;               // 已输入字节数
   char     sock_buf[BUFLEN+1];      // 套接字缓冲区
   int      sock_count;              // 已输入字节数
};

/* Define status */
#define   IN_INIT     0
#define   IN_ACT      1
#define   PTY_OK      10
#define   PTY_ERR     11
#define   SOCK_OK     20
#define   SOCK_ERR    21
#define   WAIT_OK     30
#define   WAIT_ERR    31

/* Define flags */
#define   CONFIGURED   0x01   /* 已配置 */
#define   INWAITCONN   0x02   /* 进入等待连接状态 */

PConfig  szConf=NULL;
PConfig  ConfRoot=NULL;
int      AnyTimeout=0;
int      MaxFD=0;
unsigned long localip=0;
char     *cnf_file=NULL;
char     *errname=NULL;
pid_t    mypid=-1;

void daemon_init(void);
void mypty(void);

void cj_Errlog(va_alist)
va_dcl
{
   FILE   *fp  
   va_list args;
   char *fmt,buf[640];
   struct tm *tm;
   time_t t;

// if (LOGERR){
   fp = fopen(errname , "a" )  
   if (fp==NULL) return 
   va_start(args);
   fmt = va_arg(args, char * );
   vsprintf(buf, fmt, args );
   va_end(args);
   t = time(NULL);
   tm = localtime(&t);
   if (mypid==-1) mypid = getpid();
   fprintf(fp, "%04d-%02d-%02d %02d:%02d:%02d <%d>:%s\r\n", tm->tm_year+1900,
            tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
            mypid, buf )  
   fclose(fp)  
// }
   return 
}

usage(void)
{
   fprintf(stderr,"Usage: mypty [-f config-file] [-l logdevice]\n\7");
   fprintf(stderr,"Default config-file is /etc/mypty.conf\n");
   fprintf(stderr,"        logdevice   is /dev/tty12\n");
   fprintf(stderr,"Example:mypty -f /etc/mypty.conf\n");
   fprintf(stderr,"        mypty -f /etc/mypty.conf -l /dev/null\n");
   exit(-1);
}

main(int argc, char *argv[])
{
   int c;

   errname  = DEFAULT_LOGDEVICE;
   cnf_file = DEFAULT_CONFIG_FILE_NAME;
   while ((c=getopt(argc, argv, "f:l:"))!=-1){
      switch(c){
         case 'f':cnf_file = optarg;
            if (access(cnf_file, R_OK)!=0){
               perror(cnf_file);
               exit(2);
            }
            break;
         case 'l':errname = optarg;
            if (access(errname, F_OK)!=0)
               close(open(errname, O_CREAT, 0660));
               if (access(errname, W_OK)!=0){
                  perror(errname);
                  exit(2);
               }
            break;
         case '?':usage();break;
         default :fprintf(stderr,"Error options: %c <%d>", c, c);
            exit(1);
      }
   }
   daemon_init();
   read_config(NULL);
   mypid = getpid();
   mypty();

   exit(0);
}

static int lockfd=-1;
static FILE *lockfp=NULL;
void endproc(int sig)
{
   PConfig   pc;

   for (pc=ConfRoot;ConfRoot;pc=ConfRoot){
      close(pc->ptyfd);
      close(pc->waitfd);
      close(pc->sockfd);
      ConfRoot=pc->next;
      free(pc);
   }
   lockf(lockfd, F_ULOCK, 0);
   close(lockfd);
   cj_Errlog("Terminated signal received");
   exit(0);
}

void my_child(int sig)
{
   int  i, pid, stat;

   pid = waitpid(-1, &stat, WNOHANG);
   while (pid>0)
      pid = waitpid(-1, &stat, WNOHANG);
}

/* 转入后台, 监听客户连接 */
void daemon_init(void)
{
   int   i;
   int   fd;
   int   status;
   int   frominit;

   signal(SIGTERM, endproc);
   signal(SIGCLD, my_child);
   signal(SIGHUP, SIG_IGN);
   signal(SIGINT, SIG_IGN);
   signal(SIGQUIT, SIG_IGN);
#ifdef SIGTTOU
   signal(SIGTTOU, SIG_IGN);
#endif
#ifdef SIGTTIN
   signal(SIGTTIN, SIG_IGN);
#endif
#ifdef SIGTSTP
   signal(SIGTSTP, SIG_IGN);
#endif

   /* 如果由 inittab 启动, 则无需手工进入后台 */
   frominit = (getppid() == 1);
   if (!frominit) {
      switch (i=fork()) {
         case -1: perror("fork");
                  exit(1);
         case  0: break;
         default: exit(0);
      }
   }

   umask(0);
   /* 回到根目录以避免文件系统装卸问题 */
   chdir("/");

   /* 设置成为新的进程组长 */
   setpgrp();

   /* 再次 fork 脱离控制台, 如果由 init 启动, 则父进程 wait */
   switch (i=fork()) {
      case -1: perror("fork2");
               exit(1);
      case  0: break;
      default: if (frominit)
                  wait(&status);
               exit(0);
   }
}

void reread_config(int sig)
{
   /*
   重读配置文件
   */
   cj_Errlog("Reconfigure: signal (%d) received.", sig);
   read_config(NULL);
}

read_config(char *fname)
{
   char    buf[256], p_tty[32], ip[32], ports[32], dummy[64];
   int     lines=0, port;
   long    ipaddr;
   PConfig pc, tail;
   struct  hostent   *hp;
   struct  in_addr in;

   if (fname)
      cnf_file = fname;
   cj_Errlog("read_config(%s) start.", cnf_file);
   if (lockfd==-1){   /* 第一次运行,测试锁 */
      lockfd = open(cnf_file, O_RDWR);
      if (lockfd<0) {
         perror(cnf_file);
         exit(3);
      }
      if (lockf(lockfd, F_TLOCK, 0)<0){
         cj_Errlog("Another copy of this program is running...");
         close(lockfd);
         exit(1);
      }
      else cj_Errlog("Filelock of %s successfully", cnf_file);
   }
   if (lockfp==NULL)
      lockfp = fdopen(lockfd, "r");
   if (lockfp==NULL) {
      // perror(cnf_file);
      cj_Errlog("Error open config file:<%d>%s", errno, cnf_file);
      goto done;
   }
   rewind(lockfp);
   for (pc=ConfRoot,tail=NULL;pc;tail=pc,pc=pc->next)
      pc->flags &= ~CONFIGURED;                        /* 置未配置标志 */
   while (fgets(buf, sizeof(buf), lockfp)) {
      lines++;
      if (buf[0]==0 || buf[0]=='\n' || buf[0]=='#')
         continue;
      if (sscanf(buf,"%31s%31s%31s%31s",p_tty,ip,ports,dummy) != 3) {
         cj_Errlog("Config file format error: line=%d", lines);
         continue;
      }
      if (p_tty[0]=='#') continue; // 注释行
      if (strncmp(p_tty, "ttyp", 4) != 0) {
         cj_Errlog("pty name error: lines=%d", lines);
         continue;
      }
      hp = gethostbyname(ip);
      if (hp)
         ipaddr = *(long *) hp->h_addr;
      else {
         ipaddr = inet_addr(ip);
         if (ipaddr==-1) {
            cj_Errlog("ip address error: lines=%d", lines);
            continue;
         }
      }
      port = atoi(ports);
      if (port <= 1024 || port >= 0x30000) {
         cj_Errlog("tcp port error: lines=%d", lines);
         continue;
      }
      for (pc=ConfRoot;pc;pc=pc->next) {
         if (strcmp(pc->p_tty, p_tty)==0)
            break;
      }
      if (pc==NULL) {
         pc = (PConfig) calloc(sizeof(Config),1);
         if (pc == NULL) {
            perror("calloc");
            exit(2);
         }
         pc->next = NULL;
         if (tail)
            tail->next = pc;
         else
            ConfRoot   = pc;
         tail = pc;
         strcpy(pc->p_tty, p_tty);
         AnyTimeout++;
         pc->status  = IN_INIT;
         pc->flags   = 0;
         pc->timeout = 0;
         pc->ptyfd   = -1;
         pc->sockfd  = -1;
         in.s_addr   = ipaddr;
         cj_Errlog("new tty added:%s at %s:%d", pc->p_tty, inet_ntoa(in), port);
      }
      if (pc->status == IN_ACT || pc->status == SOCK_OK) {
         if (pc->ipaddr != ipaddr || pc->port != htons(port))
            lost_connection(pc);
      }
      if (pc->status == WAIT_OK) {
         if (pc->ipaddr != ipaddr || pc->port != htons(port))
            lost_listen(pc);
      }
      pc->ipaddr = ipaddr;
      pc->port   = htons(port);
      pc->flags |= CONFIGURED;
   }
   // fclose(lockfp);
   endhostent();
done:
   for (pc=ConfRoot,tail=NULL;pc;pc=(tail?tail->next:ConfRoot)) {
      if (pc->flags & CONFIGURED) {
         tail = pc;
         continue;
      }
      /* 关闭未配置的伪终端
      printf("Shutting down\n");
      */
      cj_Errlog("Shutting down: %s", pc->p_tty);
      close(pc->ptyfd);
      close(pc->waitfd);
      close(pc->sockfd);

      if (tail==NULL)
         ConfRoot = pc->next;
      else
         tail->next = pc->next;
      free(pc);
   }
   cj_Errlog("read_config(%s) complete.", cnf_file);
}

open_pty(PConfig pc)
{
   int  on = 1, target;
   long t = time(NULL);
   char p_tty[256];

   sprintf(p_tty, "/dev/p%s", &pc->p_tty[1]);
   pc->ptyfd = open(p_tty, O_RDWR | O_NDELAY);
   // pc->ptyfd = open(p_tty, O_RDWR);

   if (pc->ptyfd < 0) {
      // perror(pc->p_tty);
      cj_Errlog("error open %s", pc->p_tty);
      pc->timeout = t + 5;
      AnyTimeout++;
      pc->status = PTY_ERR;
   }
   else {
      /* pty devices */
      ioctl(pc->ptyfd, TIOCPKT, &on);
      pc->status = PTY_OK;
      if (pc->ptyfd > MaxFD)
         MaxFD = pc->ptyfd;
      cj_Errlog("%s opened successfully", pc->p_tty);
   }
}

open_socket(PConfig pc)
{
   int    on = 1, flags;
   long   t = time(NULL);
   struct sockaddr_in   sin;
   struct linger   lg;

   sin.sin_family      = AF_INET;
   sin.sin_addr.s_addr = INADDR_ANY;
   sin.sin_port        = pc->port;
   pc->waitfd = socket(AF_INET, SOCK_STREAM, 0);
   if (pc->waitfd < 0) {
      perror("socket");
      exit(2);
   }
   flags = fcntl(pc->waitfd, F_GETFL, 0);
   flags |= O_NONBLOCK;
   fcntl(pc->waitfd, F_SETFL, flags);
   lg.l_onoff  = 1;
   lg.l_linger = 30;
   if (setsockopt(pc->waitfd,SOL_SOCKET,SO_LINGER,(char *)&lg,sizeof(lg)) < 0) {
      perror("SO_LINGER");
      exit(2);
   }
   setsockopt(pc->waitfd,SOL_SOCKET,SO_REUSEADDR,NULL,0);
   if (bind(pc->waitfd, (struct sockaddr *)&sin, sizeof(sin))<0) {
      perror("Bind");
      close(pc->waitfd);
      exit(2);
   }
   if (listen(pc->waitfd, 1)<0){
      perror("Listen");
      close(pc->waitfd);
      exit(2);
   }
   pc->sockfd = -1;
   pc->status = WAIT_OK;
   if (pc->waitfd > MaxFD)
      MaxFD = pc->waitfd;
   AnyTimeout++;
// cj_Errlog("get waitfd ok: %d<%s>", pc->waitfd, pc->p_tty);
}

lost_listen(PConfig pc)         /* 失去监听套接字 */
{
   long   t=time(NULL);

   cj_Errlog("lost listen on %s at port %d", pc->p_tty, ntohs(pc->port));
   close(pc->waitfd);
   pc->timeout = t + 5;
   AnyTimeout++;
   pc->status = WAIT_ERR;
}

lost_connection(PConfig pc)         /* 失去连接, 进入 wait */
{
   long   t=time(NULL);
   struct in_addr from;

   from.s_addr = pc->ipaddr;
   cj_Errlog("lost connection on %s from %s:%d", pc->p_tty, inet_ntoa(from), ntohs(pc->port));
   pc->timeout = t + 5;
   AnyTimeout++;
   close(pc->sockfd);
   pc->status = PTY_OK;
}

check_status(void)
{
   PConfig pc;
   long t=time(NULL);

// cj_Errlog("Check status begin ", AnyTimeout);
   for (pc = ConfRoot;pc;pc=pc->next) {
      // cj_Errlog("Check %s: status=%d", pc->p_tty, pc->status);
      if (pc->status == IN_INIT) {
         /* 打开伪终端设备 */
         AnyTimeout--;
         open_pty(pc);
      } else if (pc->status==PTY_ERR) {
         /* 超时结束重新尝试 */
         if (pc->timeout <= t) {
            AnyTimeout--;
            open_pty(pc);
         }
      }

      if (pc->status==PTY_OK) {
         /* 打开套接字, 进入 wait */
         AnyTimeout--;
         open_socket(pc);
      } else if (pc->status==WAIT_ERR) {
         /* 超时结束重新尝试 */
         if (pc->timeout <= t) {
            AnyTimeout--;
            open_socket(pc);
         }
      }

      if (pc->status==WAIT_OK){
         /* 等待客户连接 */
      }
      else if (pc->status==SOCK_ERR){
         AnyTimeout--;
         close(pc->sockfd);
         pc->status = WAIT_OK;
      }

      if (pc->status == SOCK_OK){
         // AnyTimeout--;
         pc->status = IN_ACT;
         close(pc->waitfd);
      }
      else if (pc->status == IN_ACT) AnyTimeout--;
   }
// cj_Errlog("Check status end ", AnyTimeout);
}

void mypty(void)
{
   PConfig pc;
   fd_set  infd, outfd, errfd;
   int     n, len, on=1;
   char    ch, *BufPtr;
   struct  timeval   tv;
   struct  sockaddr_in from;
   char mesg[256];

   sigset(SIGHUP, reread_config);
   for (;;) {
      while (ConfRoot == NULL) {
         // printf("mypty has nothing to do.\n");
         /* 等待, 直到读完配置文件 */
         pause();
      }
      if (AnyTimeout)
         check_status();            /* 检查状态 */
      FD_ZERO(&infd);
      FD_ZERO(&outfd);
      FD_ZERO(&errfd);
      for (pc=ConfRoot;pc;pc=pc->next) {
         if (pc->status != IN_ACT && pc->status != WAIT_OK){
            if (pc->status==PTY_OK) read(pc->ptyfd, pc->pty_buf, BUFLEN);
            continue;
         }
         if (pc->status==IN_ACT){
            FD_SET(pc->sockfd, &errfd);
            if (pc->pty_count > 0)
               FD_SET(pc->sockfd, &outfd);
            else
               FD_SET(pc->ptyfd, &infd);

            if (pc->sock_count > 0)
               FD_SET(pc->ptyfd, &outfd);
            else
               FD_SET(pc->sockfd, &infd);
         }
         else
            FD_SET(pc->waitfd, &infd);
      }
      tv.tv_sec  = 5;
      tv.tv_usec = 0;
      n = select(MaxFD+1, &infd, &outfd, &errfd, &tv);
// cj_Errlog("select end of <%d>", n);
      if (n <= 0) {
         if (n < 0 && errno != EINTR)
            cj_Errlog("select return error:%d", errno);
         continue;
      }
      for (pc=ConfRoot;pc;pc=pc->next) {
         if (pc->status != IN_ACT && pc->status != WAIT_OK)
            continue;
         if (pc->status == IN_ACT){
            BufPtr=pc->pty_buf;

            if (FD_ISSET(pc->sockfd, &errfd)) {
cj_Errlog("%s socket exception in errfd", pc->p_tty);
               lost_connection(pc);
            }

            if (FD_ISSET(pc->ptyfd, &infd)) {
               pc->pty_count = read(pc->ptyfd, pc->pty_buf, BUFLEN);
               if (pc->pty_count<0)
                   lost_connection(pc);
// cj_Errlog("%s ptty in end<%d>", pc->p_tty, pc->pty_count);
            }
            if (FD_ISSET(pc->sockfd, &infd)) {
               pc->sock_count = read(pc->sockfd, pc->sock_buf, BUFLEN);
               if (pc->sock_count<=0)
                   lost_connection(pc);
// cj_Errlog("%s socket in end<%d>", pc->p_tty, pc->sock_count);
            }

            if (FD_ISSET(pc->ptyfd, &outfd) || pc->sock_count>0) {
// cj_Errlog("%s ptty out OK", pc->p_tty);
               if (write(pc->ptyfd, pc->sock_buf, pc->sock_count)<0)
                   lost_connection(pc);
               pc->sock_count = 0;
            }

            if (FD_ISSET(pc->ptyfd, &infd)) {
               if (pc->pty_count==0){            /* 描述字可读且读到0个字符 */
                  close(pc->ptyfd);
                  sprintf(mesg, "/dev/p%s", &pc->p_tty[1]);
                  pc->ptyfd = open(mesg, O_RDWR | O_NDELAY);
                  if (pc->ptyfd<0) lost_connection(pc);
                  else {
                     ioctl(pc->ptyfd, TIOCPKT, &on);
                     if (pc->ptyfd>MaxFD) MaxFD = pc->ptyfd;
                  }
               }
            }
            if (FD_ISSET(pc->sockfd, &outfd) || pc->pty_count>0) {
// cj_Errlog("%s socket out OK", pc->p_tty);
               if ( *BufPtr=='\0') {
                   BufPtr++;
                   if (write(pc->sockfd, BufPtr, --pc->pty_count)<0)
                      lost_connection(pc);
               }
               else {
                   if (send(pc->sockfd, BufPtr, 1, MSG_OOB)<0)
                      lost_connection(pc);
               }
               pc->pty_count = 0;
            }
         }
         else{
            if (FD_ISSET(pc->waitfd, &infd)) {
            /* 客户连接建立, 检查是否合法IP地址 */
               // cj_Errlog("%s socket accept OK", pc->p_tty);
               len = sizeof(from);
               bzero((char *)&from,sizeof(from));
               pc->sockfd = accept(pc->waitfd, (struct sockaddr *)&from, &len);
               // fcntl(pc->sockfd, F_SETFL, O_NDELAY);
               if (pc->sockfd<0)
                  pc->status = SOCK_ERR;
               else{
                  if (from.sin_addr.s_addr != pc->ipaddr){
                     write(pc->sockfd, "Invalid custom address\r\n\7", 26);
                     cj_Errlog("Remote address %s:%d denied",
                        inet_ntoa(from.sin_addr), ntohs(pc->port));
                     pc->status = SOCK_ERR;
                  }
                  else{
                     sprintf(mesg, "Terminal ID: %s", pc->p_tty);
                     write(pc->sockfd, mesg, strlen(mesg)+1);
                     pc->status = SOCK_OK;
                     cj_Errlog("Remote address %s:%d accepted at %s",
                        inet_ntoa(from.sin_addr), ntohs(pc->port), pc->p_tty);
                     if (pc->sockfd > MaxFD)
                        MaxFD = pc->sockfd;
                  }
               }
               AnyTimeout++;
            }
         }
      }
   }
}

pc->status = SOCK_OK;
                     cj_Errlog("Remote address %s:%d accepted at %s",
                        inet_ntoa(from.sin_addr), ntohs(pc->port), pc->p_tty);
                     if (pc->sockfd > MaxFD)
                        MaxFD = pc->sockfd;
                  }
               }
               AnyTimeout++;
            }
         }
      }
   }
}

阅读(366) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~