/* DESC: This script reads messages coming from the serial port and forwards them via TCP to remote hosts (and vice versa). * Copyright (C) 2014 NetModule AG, Switzerland * * Usage: usage: serial-tcp-broadcast.are .... * Version: 2.0 * */ /* * Options to configure by user */ /* Serial Baud Rate 9600, 19200, 38400, 57600, 115200 */ SER_SPEED = 115200; /* number of data bits (5, 6, 7, 8) */ SER_DATABIT = 8; /* number of stop bits (1, 2) */ SER_STOPBIT = 1; /* parity (0=no parity, 1=odd parity, 2=even parity) */ SER_PARITY = 0; /* flow control (0=none, 1=xon/xoff, 2=hardware) */ SER_FLOW = 0; /* name of the Serial Interface */ DEV = "SERIAL1"; DEVIDX = ((int) substr(DEV, 6, 1)) - 1; /* TCP Options */ /* number of retries when connecting to a slave */ RETRY = 3; /* the interval in seconds for each retry */ RETRY_INTERVAL = 3; /* recovery interval after slave got disconnected or retries were unsuccessful */ RECOVER_INTERVAL = 120; /* * Only changed things below if you know what you are doing */ POLL_TIMEOUT = 10; /* seconds */ REMOTE_PORT =2000; /* port of the remote serial slaves (default 2000) */ /* slave template */ template slave { hostname = ""; fd = -1; state = "down"; since = 0; void slave(string remotehost) { nb_syslog("Creating slave %s",remotehost); if (strlen(remotehost) > 0) { this.hostname = remotehost; } else { nb_syslog("ERROR: no hostname defined, slave could not be created"); } } int jiffy() { sys = sysinfo(); u = struct_get(sys, "uptime"); if (is_void(u) || u < 1) { return 0; } else { return u; } } int connect() { if (this.fd > -1) { close(this.fd); this.fd = -1; } nb_syslog("Connecting to %s", this.hostname); /* open TCP socket */ sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,1); setsockopt(sock, SOL_TCP, TCP_KEEPIDLE,1); setsockopt(sock, SOL_TCP, TCP_KEEPINTVL,5); setsockopt(sock, SOL_TCP, TCP_KEEPCNT,5); if (sock < 0) { nb_syslog("ERROR: unable to open socket"); } for (i = 0; i < RETRY; i++) { if (connect(sock, this.hostname, REMOTE_PORT) < 0){ nb_syslog("ERROR: could not connect to %s:%d", this.hostname, REMOTE_PORT); nb_syslog("Retrying to connect (trial #%i, waiting %s sec)", i, RETRY_INTERVAL); sleep(RETRY_INTERVAL); } else { nb_syslog("TCP connection established to host %s:%d", this.hostname, REMOTE_PORT); this.fd = sock; this.state = "up"; return 0; } } nb_syslog("ERROR: max. retries reached for %s, giving up", this.hostname); this.since = this.jiffy(); this.state = "down"; close(this.fd); this.fd = -1; return -1; } int disconnect() { if (this.fd > -1) { close(this.fd); this.fd = -1; } this.state = "down"; this.since = this.jiffy(); nb_syslog("Disconnected host %s", this.hostname); return 0; } } /* endof template slave */ int start_serial() { /* check serial port config */ status = nb_config_get(sprintf("serial.%d.status", DEVIDX)); if (status != "2") { nb_syslog("ERROR: serial port %d is not enabled for us", DEVIDX); exit(1); } /* set attributes */ rc = nb_serial_setattr(DEV, SER_SPEED, SER_DATABIT, SER_STOPBIT, SER_PARITY, SER_FLOW); if (rc != 0) { nb_syslog("ERROR: unable to set serial attributes (rc %d)", rc); exit(1); } /* open serial port */ fd = nb_serial_open(DEV); if (fd < 0) { nb_syslog("ERROR: unable to open serial device (%s)", DEV); exit(1); } return fd; } int stop_serial() { if (fd > -1) { close(fd); fd = -1; } return 0; } void usage() { nb_syslog("usage: serial-tcp-broadcast.are .... "); exit(1); } /* ------------------------ main ------------------------------- */ if (strlen(argv[1]) == 0) { nb_syslog("ERROR: no remote host specified"); usage(); } /* create slaves */ nb_syslog("creating slaves"); for (i = 1; i < argc; i++) { slaves[i-1] = new slave(argv[i]); } /* connect slave */ for (i = 0; i < length(slaves); i++) { nb_syslog("connecting slave %d", i); rc = slaves[i].connect(); if (rc < 0) { nb_syslog("ERROR: could not connect %s", slaves[i].hostname); } } nb_syslog("starting serial"); serial_fd = start_serial(); /* create file descriptor array */ fds = mkarray(); tcp_fds = mkarray(); j = 0; for (i = 0; i < length(slaves); i++) { if (slaves[i].state == "up") { tcp_fds[j] = slaves[i].fd; j++; } } fds = array_merge(serial_fd, tcp_fds); /* loop infinitely */ while (1) { buffer = ""; rc = select(fds, POLL_TIMEOUT); if (rc == -1) { nb_syslog("ERROR: select failed"); } else if (rc == serial_fd) { /* received something from serial device */ data = read(serial_fd, 4096); if (data) { serialbuffer = strcat(serialbuffer, data); nb_syslog("data on serial port: %s",serialbuffer); } } else if (rc != 0 && array_search(tcp_fds, rc) != NULL) { /* received something from TCP */ data = recv(rc); len=strlen(data); if (len > 0) { /* write to serial device */ write(serial_fd, data, len); } else if(len == 0) { //nb_syslog("tcp connection lost, reconnecting"); for (i = 0; i < length(slaves); i++) { if (rc == slaves[i].fd) { /* disconnect */ slaves[i].disconnect(); } } } } /* check serial buffer and send message */ len = strlen(serialbuffer); msg = serialbuffer; msglen = len; serialbuffer = ""; /* send TCP packet to every slave */ if (len > 0) { for (i = 0; i < length(slaves); i++) { if (slaves[i].state == "up" ) { sent = sendbuf(slaves[i].fd, msg, msglen); if (sent == -1) { nb_syslog("ERROR: failed to send packet to %s", slaves[i].hostname); /* disconnect slave */ slaves[i].disconnect(); } } } } /* check if we have an error on the socket */ for (i = 0; i < length(slaves); i++) { if (slaves[i].state == "up" ) { opt = getsockopt(slaves[i].fd, SOL_SOCKET, 4); if (opt != 0) { nb_syslog("ERROR: getsockopt(%d) on slave %s failed, reconnecting", opt, slaves[i].hostname); slaves[i].disconnect(); } } } /* reconnect to disconnected slaves */ for ( i=0; i < length(slaves); i++) { if (slaves[i].state == "down" ) { if ( (slaves[i].since + RECOVER_INTERVAL) < slaves[i].jiffy() ) { slaves[i].connect(); } } } /* re-create file descriptor array */ fds = mkarray(); tcp_fds = mkarray(); j = 0; for (i = 0; i < length(slaves); i++) { if (slaves[i].state == "up") { tcp_fds[j] = slaves[i].fd; j++; } } fds = array_merge(serial_fd, tcp_fds); } exit(0);