fixes
[lcr.git] / admin_client.c
index 67d08c9..2c0b362 100644 (file)
@@ -19,6 +19,8 @@
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <sys/un.h>
+#include <sys/time.h>
+#include <errno.h>
 #include <curses.h>
 #include "macro.h"
 #include "join.h"
@@ -405,6 +407,14 @@ int debug_join(struct admin_message *msg, struct admin_message *m, int line, int
                SPRINT(buffer, "%d\n", m[i].u.j.partyline);
                addstr(buffer);
        }
+       if (m[i].u.j.remote[0])
+       {
+               color(cyan);
+               addstr(" remote=");
+               color(white);
+               SPRINT(buffer, "%s\n", m[i].u.j.remote);
+               addstr(buffer);
+       }
        /* find number of epoints */
        j = msg->u.s.interfaces+msg->u.s.joins;
        jj = j + msg->u.s.epoints;
@@ -439,13 +449,13 @@ int debug_join(struct admin_message *msg, struct admin_message *m, int line, int
 
        return(line);
 }
-char *admin_state(int sock)
+char *admin_state(int sock, char *argv[])
 {
        struct admin_message    msg,
                                *m;
        char                    buffer[512],
                                *p;
-       int                     line, offset = 0;
+       int                     line, offset = 0, hoffset = 0;
        int                     i, ii, j, jj, k;
        unsigned long           l, ll;
        int                     num;
@@ -453,6 +463,10 @@ char *admin_state(int sock)
        int                     off;
        int                     ltee;
        int                     anything;
+       int                     enter = 0;
+       char                    enter_string[128] = "", ch;
+       fd_set                  select_rfds;
+       struct timeval          select_tv;
 
        /* flush logfile name */
        logfile[0] = '\0';
@@ -483,7 +497,7 @@ char *admin_state(int sock)
                cleanup_curses();
                return("Response not valid. Expecting state response.");
        }
-       num = msg.u.s.interfaces + msg.u.s.joins + msg.u.s.epoints + msg.u.s.ports;
+       num = msg.u.s.interfaces + msg.u.s.remotes + msg.u.s.joins + msg.u.s.epoints + msg.u.s.ports;
        m = (struct admin_message *)MALLOC(num*sizeof(struct admin_message));
        off=0;
        if (num)
@@ -520,13 +534,25 @@ char *admin_state(int sock)
                j++;
        }
        i = 0;
+       while(i < msg.u.s.remotes)
+       {
+               if (m[j].message != ADMIN_RESPONSE_S_REMOTE)
+               {
+                       FREE(m, 0);
+                       cleanup_curses();
+                       return("Response not valid. Expecting remote application information.");
+               }
+               i++;
+               j++;
+       }
+       i = 0;
        while(i < msg.u.s.joins)
        {
                if (m[j].message != ADMIN_RESPONSE_S_JOIN)
                {
                        FREE(m, 0);
                        cleanup_curses();
-                       return("Response not valid. Expecting call information.");
+                       return("Response not valid. Expecting join information.");
                }
                i++;
                j++;
@@ -690,17 +716,43 @@ char *admin_state(int sock)
                                                        else
                                                                SPRINT(buffer,"B%2d: ", j+1);
                                                        addstr(buffer);
-                                                       if (!m[i].u.i.ptp)
-                                                               goto ptmp;
-                                                       if (m[i].u.i.l2link && m[i].u.i.block==0)
-                                                       {
-                                                               ptmp:
-                                                               color((m[i].u.i.busy[j])?yellow:blue);
-                                                               addstr((m[i].u.i.busy[j])?"busy":"idle");
-                                                       } else
+                                                       switch(m[i].u.i.busy[j])
                                                        {
-                                                               color(red);
-                                                               addstr("blk ");
+                                                               case B_STATE_IDLE:
+                                                               if ((!m[i].u.i.l2link && m[i].u.i.ptp) || m[i].u.i.block)
+                                                               {
+                                                                       color(red);
+                                                                       addstr("blocked ");
+                                                               } else
+                                                               {
+                                                                       color(blue);
+                                                                       addstr("idle    ");
+                                                               }
+                                                               break;
+                                                               case B_STATE_ACTIVATING:
+                                                               color(yellow);
+                                                               addstr("act'ing ");
+                                                               break;
+                                                               case B_STATE_ACTIVE:
+                                                               color(green);
+                                                               addstr("busy    ");
+                                                               break;
+                                                               case B_STATE_DEACTIVATING:
+                                                               color(yellow);
+                                                               addstr("dact'ing");
+                                                               break;
+                                                               case B_STATE_EXPORTING:
+                                                               color(yellow);
+                                                               addstr("exp'ing ");
+                                                               break;
+                                                               case B_STATE_REMOTE:
+                                                               color(green);
+                                                               addstr("remote  ");
+                                                               break;
+                                                               case B_STATE_IMPORTING:
+                                                               color(yellow);
+                                                               addstr("imp'ing ");
+                                                               break;
                                                        }
                                                        if (m[i].u.i.port[j])
                                                        {
@@ -761,6 +813,17 @@ char *admin_state(int sock)
                        i++;
                        anything = 1;
                }
+               i = 0;
+               ii = i + msg.u.s.remotes;
+               while(i < ii)
+               {
+                       /* show remote summary */
+                       move(++line>1?line:1, 0);
+                       color(white);
+                       SPRINT(buffer, "Remote: %s", m[i].u.r.name);
+                       addstr(buffer);
+                       i++;
+               }
                if (anything)
                        line++;
                if (line+2 >= LINES) goto end;
@@ -933,10 +996,19 @@ char *admin_state(int sock)
                        while(l!=ll)
                        {
                                move(line++>1?line-1:1, 0);
-                               SCPY(buffer, logline[l % LOGLINES]);
+                               if ((int)strlen(logline[l % LOGLINES]) > hoffset)
+                                       SCPY(buffer, logline[l % LOGLINES] + hoffset);
+                               else
+                                       buffer[0] = '\0';
                                if (COLS < (int)strlen(buffer))
-                                       buffer[COLS] = '\0';
-                               addstr(buffer);
+                               {
+                                       buffer[COLS-1] = '\0';
+                                       addstr(buffer);
+                                       color(mangenta);
+                                       addch('*');
+                                       color(white);
+                               } else
+                                       addstr(buffer);
                                l++;
                        }
                }
@@ -949,7 +1021,7 @@ char *admin_state(int sock)
 //     move(0, 0);
 //     hline(' ', COLS);
        move(0, 0);
-       color(white);
+       color(cyan);
        msg.u.s.version_string[sizeof(msg.u.s.version_string)-1] = '\0';
        SPRINT(buffer, "LCR %s", msg.u.s.version_string);
        addstr(buffer);
@@ -972,13 +1044,27 @@ char *admin_state(int sock)
                color(red);
                addstr(buffer);
        }
+       if (hoffset)
+       {
+               move(1, 13);
+               SPRINT(buffer, "H-Offset +%d", hoffset);
+               color(red);
+               addstr(buffer);
+       }
        /* display end */
        move(LINES-2, 0);
-       color(white);
+       color(blue);
        hline(ACS_HLINE, COLS);
        move(LINES-1, 0);
-       color(white);
-       SPRINT(buffer, "i = interfaces '%s'  c = calls '%s'  l = log  q = quit  +/- = scroll", text_interfaces[show_interfaces], text_calls[show_calls]);
+       if (enter)
+       {
+               color(white);
+               SPRINT(buffer, "-> %s", enter_string);
+       } else
+       {
+               color(cyan);
+               SPRINT(buffer, "i=interfaces '%s'  c=calls '%s'  l=log  q=quit  +-*/=scroll  enter", text_interfaces[show_interfaces], text_calls[show_calls]);
+       }
        addstr(buffer);
        refresh();
 
@@ -990,47 +1076,143 @@ char *admin_state(int sock)
                goto again;
        }
 
-       /* user input */
-       switch(getch())
+       if (enter)
        {
-               case 12: /* refresh */
-               cleanup_curses();
-               init_curses();
-               goto again;
-               break;
-
-               case 3: /* abort */
-               case 'q':
-               case 'Q':
-               break;
+               /* user input in enter mode */
+               ch = getch();
+               enter_again:
+               if (ch == 10)
+               {
+                       FILE *fp;
+
+                       enter = 0;
+                       if (!enter_string[0])
+                               goto again;
+
+                       SPRINT(logline[logcur++ % LOGLINES], "> %s", enter_string);
+                       if (!!strncmp(enter_string, "interface", 10) &&
+                           !!strncmp(enter_string, "route", 6) &&
+                           !!strncmp(enter_string, "release ", 8) &&
+                           !!strncmp(enter_string, "block ", 6) &&
+                           !!strncmp(enter_string, "unblock ", 8) &&
+                           !!strncmp(enter_string, "unload ", 7))
+                       {
+                               SPRINT(logline[logcur++ % LOGLINES], "usage:");
+                               SPRINT(logline[logcur++ % LOGLINES], "interface (reload interface.conf)");
+                               SPRINT(logline[logcur++ % LOGLINES], "route (reload routing.conf)");
+                               SPRINT(logline[logcur++ % LOGLINES], "release <EP> (release endpoint with given ID)");
+                               SPRINT(logline[logcur++ % LOGLINES], "block <port> (block port for further calls)");
+                               SPRINT(logline[logcur++ % LOGLINES], "unblock <port> (unblock port for further calls, load if not loaded)");
+                               SPRINT(logline[logcur++ % LOGLINES], "unload <port> (unload mISDN stack, release call calls)");
+                       } else
+                       {
+                               /* applend output to log window */
+                               SPRINT(buffer, "%s %s", argv[0], enter_string);
+                               fp = popen(buffer, "r");
+                               if (fp)
+                               {
+                                       while(fgets(logline[logcur % LOGLINES], sizeof(logline[0]), fp))
+                                               logline[logcur++ % LOGLINES][sizeof(logline[0])-1] = '\0';
+                                       pclose(fp);
+                               } else
+                               {
+                                       SPRINT(logline[logcur++ % LOGLINES], "failed to execute '%s'", buffer);
+                               }
+                       }
+                       logline[logcur % LOGLINES][0] = '\0';
+                       enter_string[0] = '\0';
+                       goto again;
+               }
+               if (ch>=32 && ch<=126)
+               {
+                       SCCAT(enter_string, ch);
+                       ch = getch();
+                       if (ch > 0)
+                               goto enter_again;
+                       goto again;
+               } else
+               if (ch==8 || ch==127)
+               {
+                       if (enter_string[0])
+                               enter_string[strlen(enter_string)-1] = '\0';
+                       ch = getch();
+                       if (ch > 0)
+                               goto enter_again;
+                       goto again;
+               } else
+               if (ch != 3)
+               {
+                       ch = getch();
+                       if (ch > 0)
+                               goto enter_again;
+                       FD_ZERO(&select_rfds);
+                       FD_SET(0, &select_rfds);
+                       select_tv.tv_sec = 0;
+                       select_tv.tv_usec = 250000;
+                       select(1, &select_rfds, NULL, NULL, &select_tv);
+                       goto again;
+               }
+       } else
+       {
+               /* user input in normal mode */
+               switch(getch())
+               {
+                       case 12: /* refresh */
+                       cleanup_curses();
+                       init_curses();
+                       goto again;
+                       break;
 
-               case 'i': /* toggle interface */
-               show_interfaces++;
-               if (show_interfaces > 3) show_interfaces = 0;
-               goto again;
+                       case 3: /* abort */
+                       case 'q':
+                       case 'Q':
+                       break;
 
-               case 'c': /* toggle calls */
-               show_calls++;
-               if (show_calls > 2) show_calls = 0;
-               goto again;
+                       case 'i': /* toggle interface */
+                       show_interfaces++;
+                       if (show_interfaces > 3) show_interfaces = 0;
+                       goto again;
 
-               case 'l': /* toggle log */
-               show_log++;
-               if (show_log > 1) show_log = 0;
-               goto again;
+                       case 'c': /* toggle calls */
+                       show_calls++;
+                       if (show_calls > 2) show_calls = 0;
+                       goto again;
 
-               case '+': /* scroll down */
-               offset++;
-               goto again;
-               
-               case '-': /* scroll up */
-               if (offset)
-                       offset--;
-               goto again;
+                       case 'l': /* toggle log */
+                       show_log++;
+                       if (show_log > 1) show_log = 0;
+                       goto again;
 
-               default:
-               usleep(250000);
-               goto again;
+                       case '+': /* scroll down */
+                       offset++;
+                       goto again;
+                       
+                       case '-': /* scroll up */
+                       if (offset)
+                               offset--;
+                       goto again;
+
+                       case '*': /* scroll right */
+                       hoffset += 2;
+                       goto again;
+                       
+                       case '/': /* scroll left */
+                       if (hoffset)
+                               hoffset -= 2;
+                       goto again;
+
+                       case 10: /* entermode */
+                       enter = 1;
+                       goto again;
+
+                       default:
+                       FD_ZERO(&select_rfds);
+                       FD_SET(0, &select_rfds);
+                       select_tv.tv_sec = 0;
+                       select_tv.tv_usec = 250000;
+                       select(1, &select_rfds, NULL, NULL, &select_tv);
+                       goto again;
+               }
        }
 
        /* check for logfh */
@@ -1132,75 +1314,163 @@ char *admin_cmd(int sock, int mode, char *extension, char *number)
 /*
  * makes a testcall
  */
+#define GET_NOW() { \
+       gettimeofday(&now_tv, &now_tz); \
+       now_d = ((double)(now_tv.tv_usec))/1000000 + now_tv.tv_sec; \
+       }
 char *admin_testcall(int sock, int argc, char *argv[])
 {
        static struct admin_message msg;
+       int ar = 2;
+       int stimeout = 0, ptimeout = 0, atimeout = 0, ctimeout = 0;
+       int l;
+       double timer = 0, now_d;
+       unsigned long on = 1;
+       struct timeval now_tv;
+       struct timezone now_tz;
 
        printf("pid=%d\n", getpid()); fflush(stdout);
 
+       while (argc > ar)
+       {
+               if (!strcmp(argv[ar], "--setup-timeout"))
+               {
+                       ar++;
+                       if (argc == ar)
+                               return("Missing setup timeout value.\n");
+                       stimeout = atoi(argv[ar]);
+                       ar++;
+               } else
+               if (!strcmp(argv[ar], "--proceeding-timeout"))
+               {
+                       ar++;
+                       if (argc == ar)
+                               return("Missing proceeding timeout value.\n");
+                       ptimeout = atoi(argv[ar]);
+                       ar++;
+               } else
+               if (!strcmp(argv[ar], "--alerting-timeout"))
+               {
+                       ar++;
+                       if (argc == ar)
+                               return("Missing alerting timeout value.\n");
+                       atimeout = atoi(argv[ar]);
+                       ar++;
+               } else
+               if (!strcmp(argv[ar], "--connect-timeout"))
+               {
+                       ar++;
+                       if (argc == ar)
+                               return("Missing connect timeout value.\n");
+                       ctimeout = atoi(argv[ar]);
+                       ar++;
+               } else
+               {
+                       break;
+               }
+       }
+
        /* send reload command */
        memset(&msg, 0, sizeof(msg));
        msg.message = ADMIN_CALL_SETUP;
-       if (argc > 2)
+       msg.u.call.present = 1;
+
+       if (argc > ar)
        {
-               SCPY(msg.u.call.interface, argv[2]);
+               SCPY(msg.u.call.interface, argv[ar]);
        }
-       if (argc > 3)
+       ar++;
+       if (argc > ar)
        {
-               SCPY(msg.u.call.callerid, argv[3]);
+               SCPY(msg.u.call.callerid, argv[ar]);
        }
-       if (argc > 4)
+       ar++;
+       if (argc > ar)
        {
-               SCPY(msg.u.call.dialing, argv[4]);
+               SCPY(msg.u.call.dialing, argv[ar]);
        }
-       if (argc > 5)
+       ar++;
+       if (argc > ar)
        {
-               if (argv[5][0] == 'p')
-                       msg.u.call.present = 1;
+               if (argv[ar][0] == 'r')
+                       msg.u.call.present = 0;
        }
+       ar++;
        msg.u.call.bc_capa = 0x00; /*INFO_BC_SPEECH*/
        msg.u.call.bc_mode = 0x00; /*INFO_BMODE_CIRCUIT*/
        msg.u.call.bc_info1 = 0;
        msg.u.call.hlc = 0;
        msg.u.call.exthlc = 0;
-       if (argc > 6)
-               msg.u.call.bc_capa = strtol(argv[6],NULL,0);
+       if (argc > ar)
+               msg.u.call.bc_capa = strtol(argv[ar],NULL,0);
        else
                msg.u.call.bc_info1 = 3 | 0x80; /* alaw, if no capability is given at all */
-       if (argc > 7) {
-               msg.u.call.bc_mode = strtol(argv[7],NULL,0);
+       ar++;
+       if (argc > ar) {
+               msg.u.call.bc_mode = strtol(argv[ar],NULL,0);
                if (msg.u.call.bc_mode) msg.u.call.bc_mode = 2;
        }
-       if (argc > 8) {
-               msg.u.call.bc_info1 = strtol(argv[8],NULL,0);
+       ar++;
+       if (argc > ar) {
+               msg.u.call.bc_info1 = strtol(argv[ar],NULL,0);
                if (msg.u.call.bc_info1 < 0)
                        msg.u.call.bc_info1 = 0;
                else
                        msg.u.call.bc_info1 |= 0x80;
        }
-       if (argc > 9) {
-               msg.u.call.hlc = strtol(argv[9],NULL,0);
+       ar++;
+       if (argc > ar) {
+               msg.u.call.hlc = strtol(argv[ar],NULL,0);
                if (msg.u.call.hlc < 0)
                        msg.u.call.hlc = 0;
                else
                        msg.u.call.hlc |= 0x80;
        }
-//             printf("hlc=%d\n",  msg.u.call.hlc);
-       if (argc > 10) {
-               msg.u.call.exthlc = strtol(argv[10],NULL,0);
+       ar++;
+       if (argc > ar) {
+               msg.u.call.exthlc = strtol(argv[ar],NULL,0);
                if (msg.u.call.exthlc < 0)
                        msg.u.call.exthlc = 0;
                else
                        msg.u.call.exthlc |= 0x80;
        }
+       ar++;
 
        if (write(sock, &msg, sizeof(msg)) != sizeof(msg))
                return("Broken pipe while sending command.");
 
+       if (ioctl(sock, FIONBIO, (unsigned char *)(&on)) < 0)
+               return("Failed to set socket into non-blocking IO.");
+
+       if (stimeout)
+       {
+               GET_NOW();
+               timer = now_d + (double)stimeout;
+       }
+       
        /* receive response */
 next:
-       if (read(sock, &msg, sizeof(msg)) != sizeof(msg))
+       l = read(sock, &msg, sizeof(msg));
+       if (l < 0)
+       {
+               if (errno == EWOULDBLOCK)
+               {
+                       if (timer)
+                       {
+                               GET_NOW();
+                               if (timer <= now_d)
+                               {
+                                       printf("Timeout\n"); fflush(stdout);
+                                       return(NULL);
+                               }
+                       }
+                       usleep(30000);
+                       goto next;
+               }
                return("Broken pipe while receiving response.");
+       }
+       if (l != sizeof(msg))
+               return("Response has unexpected message size.");
        switch(msg.message)
        {
                case ADMIN_CALL_SETUP_ACK:
@@ -1209,14 +1479,29 @@ next:
 
                case ADMIN_CALL_PROCEEDING:
                printf("PROCEEDING\n"); fflush(stdout);
+               if (ptimeout)
+               {
+                       GET_NOW();
+                       timer = now_d + (double)ptimeout;
+               }
                goto next;
 
                case ADMIN_CALL_ALERTING:
                printf("ALERTING\n"); fflush(stdout);
+               if (atimeout)
+               {
+                       GET_NOW();
+                       timer = now_d + (double)atimeout;
+               }
                goto next;
 
                case ADMIN_CALL_CONNECT:
                printf("CONNECT\n number=%s\n", msg.u.call.callerid); fflush(stdout);
+               if (ctimeout)
+               {
+                       GET_NOW();
+                       timer = now_d + (double)ctimeout;
+               }
                goto next;
 
                case ADMIN_CALL_NOTIFY:
@@ -1225,7 +1510,7 @@ next:
 
                case ADMIN_CALL_DISCONNECT:
                printf("DISCONNECT\n cause=%d %s\n location=%d %s\n", msg.u.call.cause, (msg.u.call.cause>0 && msg.u.call.cause<128)?isdn_cause[msg.u.call.cause].german:"", msg.u.call.location, (msg.u.call.location>=0 && msg.u.call.location<128)?isdn_location[msg.u.call.location].german:""); fflush(stdout);
-               goto next;
+               break;
 
                case ADMIN_CALL_RELEASE:
                printf("RELEASE\n cause=%d %s\n location=%d %s\n", msg.u.call.cause, (msg.u.call.cause>0 && msg.u.call.cause<128)?isdn_cause[msg.u.call.cause].german:"", msg.u.call.location, (msg.u.call.location>=0 && msg.u.call.location<128)?isdn_location[msg.u.call.location].german:""); fflush(stdout);
@@ -1234,8 +1519,8 @@ next:
                default:
                return("Response not valid.");
        }
-       
-       printf("Command successfull.\n");
+
+       printf("Call released.\n"); fflush(stdout);
        return(NULL);
 }
 
@@ -1339,7 +1624,9 @@ int main(int argc, char *argv[])
                printf("block <port> - Block given port.\n");
                printf("unblock <port> - Unblock given port.\n");
                printf("unload <port> - Unload port. To load port use 'block' or 'unblock'.\n");
-               printf("testcall <interface> <callerid> <number> [present|restrict [<capability>]] - Testcall\n");
+               printf("testcall [options] <interface> <callerid> <number> [present|restrict [<capability>]] - Testcall\n");
+               printf(" -> options = --setup-timeout <seconds> --proceeding-timeout <seconds>\n");
+               printf("              --alerting-timeout <seconds> --connect-timeout <seconds>\n");
                printf(" -> capability = <bc> <mode> <codec> <hlc> <exthlc> (Values must be numbers, -1 to omit.)\n");
                printf("trace [brief|short] [<filter> [...]] - Shows call trace. Use filter to reduce output.\n");
                printf(" -> Use 'trace help' to see filter description.\n");
@@ -1425,7 +1712,7 @@ int main(int argc, char *argv[])
        switch(mode)
        {
                case MODE_STATE:
-               ret = admin_state(sock);
+               ret = admin_state(sock, argv);
                break;
        
                case MODE_INTERFACE:
@@ -1446,9 +1733,11 @@ int main(int argc, char *argv[])
 
                case MODE_TESTCALL:
                ret = admin_testcall(sock, argc, argv);
+               break;
 
                case MODE_TRACE:
                ret = admin_trace(sock, argc, argv);
+               break;
        }
 
        close(sock);