openbcm

Git mirror of https://github.com/Broadcom-Network-Switching-Software/OpenBCM
git clone git://git.finwo.net/mirror/broadcom/openbcm
Log | Files | Refs | README

netserve.c (6674B)


      1 /*
      2  * 
      3  * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file.
      4  * 
      5  * Copyright 2007-2019 Broadcom Inc. All rights reserved.
      6  *
      7  * netserve: proxy server for stdio.
      8  *
      9  * This daemon connects a network socket to stdio of a local
     10  * application through a pseudo TTY, thus enabling access to
     11  * the application from a telnet client. Terminating the
     12  * telnet session does not terminate the application, i.e.
     13  * it is possible to connect several times to the same 
     14  * instance of the application.
     15  *
     16  * Example launching bcm.user on port 5611:
     17  *
     18  *   netserve -d 5611 bcm.user
     19  *
     20  * Now connect to bcm.user, e.g. like this:
     21  *
     22  *   telnet 127.0.0.1 5611
     23  */
     24 
     25 #include <stdlib.h>
     26 #include <stdio.h>
     27 #include <string.h>
     28 
     29 #include <sys/types.h>
     30 #include <sys/socket.h>
     31 #include <sys/time.h>
     32 #include <sys/wait.h>
     33 
     34 #include <unistd.h>
     35 #include <signal.h>
     36 #include <pthread.h>
     37 #include <pty.h>
     38 #include <arpa/inet.h>
     39 
     40 /* Telnet definitions */
     41 #define T_CMD   255
     42 #define T_DONT  254
     43 #define T_DO    253
     44 #define T_WONT  252
     45 #define T_WILL  251
     46 #define T_ECHO  1
     47 #define T_SGA   3
     48 
     49 static unsigned char will_sga[]  = { T_CMD, T_WILL, T_SGA  };
     50 static unsigned char will_echo[] = { T_CMD, T_WILL, T_ECHO };
     51 static unsigned char dont_echo[] = { T_CMD, T_DONT, T_ECHO };
     52 
     53 /* Network server */
     54 static volatile int _netfd = -1;
     55 static int _server_socket;
     56 
     57 static int
     58 _setup_socket(int port)
     59 {
     60     struct sockaddr_in addr;
     61     int i;
     62     int sockfd;
     63 
     64     if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
     65 	perror("server: can't open stream socket");
     66 	exit(1);
     67     }
     68 
     69     /*
     70      * Set socket option to reuse local address. This is supposed
     71      * to have the effect of freeing up the local address.
     72      */
     73     i = 1;
     74     if (setsockopt(sockfd, SOL_SOCKET,
     75                    SO_REUSEADDR, (char *) &i, 4) < 0) {
     76 	perror("setsockopt");
     77     }
     78 
     79     /* Set up server address */
     80     memset((void *) &addr,0x0, sizeof(addr));
     81     addr.sin_family = AF_INET;
     82     addr.sin_addr.s_addr = htonl(INADDR_ANY);
     83     addr.sin_port = htons(port);
     84 
     85     /* Bind our local address and port */
     86     if (bind(sockfd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
     87 	perror("server: can't bind local address");
     88 	exit(1);
     89     }
     90 
     91     /* Only process one connection at a time */
     92     listen(sockfd, 1);
     93 
     94     return sockfd;
     95 }
     96 
     97 static int 
     98 _wait_for_connection(int sockfd)
     99 {
    100     struct sockaddr_in addr;
    101     socklen_t len;
    102     int newsockfd;
    103 
    104     len = sizeof(addr);
    105     if ((newsockfd = accept(sockfd, (struct sockaddr *) &addr, &len)) < 0) {
    106 	perror("accept");
    107 	exit(1);
    108     }
    109 
    110     /* Telnet response required for correct interaction with app */
    111     write(newsockfd, will_sga, sizeof(will_sga));
    112     write(newsockfd, will_echo, sizeof(will_echo));
    113     write(newsockfd, dont_echo, sizeof(dont_echo));
    114 
    115     return newsockfd;
    116 }
    117 
    118 static void
    119 _do_telnet(int ifd, int ofd)
    120 {
    121     unsigned char cmd[8];
    122     int i;
    123 
    124     /* Read command */
    125     for (i = 1; i < 3; i++) {
    126         if (read(ifd, &cmd[i], 1) != 1) {
    127             return;
    128         }
    129     }
    130 
    131     /* Select negative response */
    132     switch (cmd[1]) {
    133     case T_WILL:
    134     case T_WONT:
    135         cmd[1] = T_DONT;
    136         break;
    137     case T_DO:
    138     case T_DONT:
    139         cmd[1] = T_WONT;
    140         break;
    141     default:
    142         /* Ignore */
    143         return;
    144     }
    145 
    146     /* Ignore responses to our own commands */
    147     switch (cmd[2]) {
    148     case T_ECHO:
    149     case T_SGA:
    150         return;
    151     }
    152 
    153     /* Send response */
    154     cmd[0] = T_CMD;
    155     write(ofd, &cmd, 3);
    156 }
    157 
    158 static void *
    159 _net2tty(void *arg)
    160 {
    161     int fd = *((int *)arg);
    162     unsigned char data;
    163 
    164     while (1) {
    165         while (_netfd < 0) {
    166             usleep(100000);
    167         }
    168         if (read(_netfd, &data, 1) != 1) {
    169             /* Broken pipe -- client quit */
    170             close(_netfd);
    171             _netfd = -1;
    172             raise(SIGUSR2);
    173         } else if (data == T_CMD) {
    174             _do_telnet(_netfd, fd);
    175         } else {
    176             write(fd, &data, 1);
    177             fsync(fd);
    178         }
    179     }
    180     return NULL;
    181 }
    182 
    183 static void *
    184 _tty2net(void *arg)
    185 {
    186     int fd = *((int *)arg);
    187     unsigned char data;
    188 
    189     while (1) {
    190         if (read(fd, &data, 1) != 1) {
    191             /* Broken pipe -- app quit */
    192             close(fd);
    193             break;
    194         }
    195         if (_netfd >= 0) {
    196             write(_netfd, &data, 1);
    197             fsync(_netfd);
    198         }
    199     }
    200     return NULL;
    201 }
    202 
    203 static int
    204 _start_app(char **args, int s)
    205 {
    206     pid_t pid;
    207 
    208     if ((pid = fork()) < 0) {
    209         perror ("fork");
    210         return -1;
    211     }
    212     if (pid > 0) {
    213         return pid;
    214     }
    215     dup2(s, 0);
    216     dup2(s, 1);
    217     dup2(s, 2);
    218     execv(*args, args); 
    219     exit(0); 
    220 }
    221 
    222 static void 
    223 _restart(void)
    224 {
    225     int s;
    226 
    227     if ((s = _wait_for_connection(_server_socket)) < 0) {
    228         fprintf(stderr, "connection error\n"); 
    229         exit(0); 
    230     }
    231     /* Reenable the proxies with the new fd */
    232     _netfd = s; 
    233 }
    234 
    235 static void 
    236 _sig(int sig)
    237 {
    238     if (sig == SIGUSR2) {
    239         _restart(); 
    240     }
    241 }
    242 
    243 int 
    244 main(int argc, char *argv[])
    245 {
    246     int p;
    247     int pid;
    248     int do_fork = 0;
    249     int ttyfd, appfd;
    250     pthread_t id;
    251     
    252     argv++; 
    253 
    254     if (!*argv || !strcmp(*argv, "--help") || !strcmp(*argv, "-h")) {
    255         printf("usage: netserve [-d] <port> <program> [args]\n"); 
    256         exit(0); 
    257     }
    258 
    259     if (!strcmp(*argv, "-d")) {
    260         do_fork = 1; 
    261         argv++; 
    262     }
    263     
    264     if ((p = atoi(*argv++)) == 0) {
    265         fprintf(stderr, "bad port number\n"); 
    266         exit(0); 
    267     }
    268 
    269     if (do_fork) {
    270         /* Daemonize */
    271         pid = fork(); 
    272         if (pid < 0) {
    273             perror("fork()"); 
    274             exit(0);
    275         }
    276         if (pid > 0) {
    277             exit(0); 
    278         }
    279         setsid();
    280     }
    281 
    282     /* Broken pipes are not a problem */
    283     signal(SIGPIPE, SIG_IGN); 
    284     
    285     /* Get a pseudo tty */
    286     if (openpty(&ttyfd, &appfd, NULL, NULL, NULL) < 0) {
    287         perror("open pty"); 
    288         exit(0); 
    289     }
    290 
    291     /* Start the application up with sv[1] as its stdio */
    292     pid = _start_app(argv, appfd);
    293 
    294     /* Start proxy for input */
    295     if (pthread_create(&id, NULL, _net2tty, (void *)&ttyfd) < 0) {
    296         perror("pthread_create"); 
    297         exit(0); 
    298     }
    299 
    300     /* Start proxy for output */
    301     if (pthread_create(&id, NULL, _tty2net, (void *)&ttyfd) < 0) {
    302         perror("pthread_create"); 
    303         exit(0); 
    304     }
    305 
    306     /* Setup server */
    307     _server_socket = _setup_socket(p);
    308     
    309     /* SIGUSR2 restarts the server when the client connection closes */
    310     signal(SIGUSR2, _sig);
    311 
    312     /* Start the first server */
    313     raise(SIGUSR2);
    314 
    315     /* Wait for our child to exit */
    316     waitpid(pid, &p, 0);
    317 
    318     return 0;
    319 }