Added a few previously unversioned utils.
[utils.git] / cidd.c
diff --git a/cidd.c b/cidd.c
new file mode 100644 (file)
index 0000000..be2e9e7
--- /dev/null
+++ b/cidd.c
@@ -0,0 +1,245 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <sys/select.h>
+#include <signal.h>
+#include <syslog.h>
+
+#define MODEM_DEV "/dev/modem"
+#define PIDFILE "/var/run/cidd.pid"
+#define PORT 5001
+
+int mdm, sock;
+int ibuflen = 0;
+unsigned char ibuf[1024];
+unsigned char tmpbuf[1024];
+fd_set cfds;
+int isdaemon;
+
+void write2(int fd, unsigned char *buf, int len)
+{
+    int ret;
+    
+    while(len)
+    {
+       ret = write(fd, buf, len);
+       if(ret < 0)
+       {
+           perror("write");
+           exit(1);
+       }
+       buf += ret;
+       len -= ret;
+    }
+}
+
+void sendcmd(unsigned char *cmd)
+{
+    unsigned char *lbuf;
+    
+    lbuf = strcpy(malloc(strlen(cmd) + 3), cmd);
+    strcat(lbuf, "\r\n");
+    write2(mdm, lbuf, strlen(cmd) + 2);
+    free(lbuf);
+}
+
+unsigned char *getline(int block)
+{
+    int i;
+    unsigned char *p;
+    int ret, len;
+    static unsigned char retbuf[1024];
+    struct pollfd pf;
+    
+    while(1)
+    {
+       while((p = memchr(ibuf, '\r', ibuflen)) != NULL)
+       {
+           memcpy(retbuf, ibuf, len = (p - ibuf));
+           retbuf[len] = 0;
+           memmove(ibuf, p + 1, ibuflen -= (len + 1));
+           for(p = retbuf; *p; p++)
+           {
+               while(*p == '\n')
+                   memmove(p, p + 1, len--);
+           }
+           if(len && (memcmp(retbuf, "AT", 2)))
+               return(retbuf);
+       }
+       if(!block)
+       {
+           pf.fd = mdm;
+           pf.events = POLLIN;
+           pf.revents = 0;
+           if(poll(&pf, 1, 0) == 0)
+               return(NULL);
+       }
+       ret = read(mdm, ibuf + ibuflen, 1024 - ibuflen);
+       if(ret < 0)
+       {
+           perror("read");
+           exit(1);
+       }
+       ibuflen += ret;
+    }
+}
+
+void broadcast(unsigned char *buf, ...)
+{
+    int i;
+    va_list args;
+    
+    va_start(args, buf);
+    vsprintf(tmpbuf, buf, args);
+    va_end(args);
+    strcat(tmpbuf, "\n");
+    for(i = 0; i < FD_SETSIZE; i++)
+    {
+       if(FD_ISSET(i, &cfds) && (i != mdm) && (i != sock))
+           write2(i, tmpbuf, strlen(tmpbuf));
+    }
+}
+
+void initmodem(void)
+{
+    sendcmd("ATZ");
+    if(strcmp(getline(1), "OK"))
+    {
+       if(isdaemon)
+           syslog(LOG_CRIT, "Could not reset modem");
+       else
+           fprintf(stderr, "Could not reset modem\n");
+       exit(1);
+    }
+    sendcmd("AT#CID=1");
+    if(strcmp(getline(1), "OK"))
+    {
+       if(isdaemon)
+           syslog(LOG_CRIT, "Could not activate called ID\n");
+       else
+           fprintf(stderr, "Could not activate caller ID\n");
+       exit(1);
+    }
+}
+
+void sighandler(int sig)
+{
+    if(sig == SIGHUP)
+    {
+       initmodem();
+    }
+}
+
+int main(int argc, unsigned char **argv)
+{
+    int i;
+    unsigned char *p;
+    int nsock;
+    int ret;
+    struct sockaddr_in name;
+    struct termios tio;
+    fd_set rfds;
+    FILE *pidst;
+    
+    isdaemon = 0;
+    if((mdm = open(MODEM_DEV, O_RDWR)) < 0)
+    {
+       perror(MODEM_DEV);
+       return(1);
+    }
+    if(tcgetattr(mdm, &tio) < 0)
+    {
+       perror("tcgetattr");
+       return(1);
+    }
+    cfmakeraw(&tio);
+    cfsetispeed(&tio, B0);
+    cfsetospeed(&tio, B38400);
+    if(tcsetattr(mdm, TCSANOW, &tio) < 0)
+    {
+       perror("tcsetattr");
+       return(1);
+    }
+    if((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0)
+    {
+       perror("socket");
+       return(1);
+    }
+    name.sin_family = AF_INET;
+    name.sin_port = htons(PORT);
+    name.sin_addr.s_addr = 0;
+    if(bind(sock, (struct sockaddr *)&name, sizeof(name)) < 0)
+    {
+       perror("bind");
+       return(1);
+    }
+    if(listen(sock, 16) < 0)
+    {
+       perror("listen");
+       return(1);
+    }
+    initmodem();
+    signal(SIGHUP, sighandler);
+    if((pidst = fopen(PIDFILE, "w")) == NULL)
+    {
+       perror("open " PIDFILE);
+       exit(1);
+    }
+    daemon(0, 0);
+    fprintf(pidst, "%i\n", getpid());
+    fclose(pidst);
+    openlog("cidd", LOG_PID, LOG_DAEMON);
+    isdaemon = 1;
+    FD_ZERO(&cfds);
+    FD_SET(mdm, &cfds);
+    FD_SET(sock, &cfds);
+    while(1)
+    {
+       rfds = cfds;
+       ret = select(FD_SETSIZE, &rfds, NULL, NULL, NULL);
+       if((ret < 0) && (errno != EINTR))
+       {
+           syslog(LOG_CRIT, "select return %m");
+           return(1);
+       }
+       if(ret > 0)
+       {
+           for(i = 0; i < FD_SETSIZE; i++)
+           {
+               if(FD_ISSET(i, &rfds))
+               {
+                   if(i == mdm)
+                   {
+                       while((p = getline(0)) != NULL)
+                       {
+                           if(!strcmp(p, "RING"))
+                               broadcast("R");
+                           if(!memcmp(p, "NMBR = ", 7))
+                           {
+                               p += 7;
+                               broadcast("N%s", p);
+                           }
+                       }
+                   } else if(i == sock) {
+                       if((nsock = accept(sock, NULL, 0)) >= 0)
+                           FD_SET(nsock, &cfds);
+                   } else {
+                       if(read(i, tmpbuf, 1024) == 0)
+                       {
+                           FD_CLR(i, &cfds);
+                           close(i);
+                       }
+                   }
+               }
+           }
+       }
+    }
+}