17 #include <sipwitch-config.h>
22 using namespace sipwitch;
24 #define RPC_MAX_PARAMS 96
40 static char *cgi_version = NULL;
41 static char *cgi_remuser = NULL;
42 static char *cgi_method = NULL;
43 static char *cgi_query = NULL;
44 static char *cgi_content = NULL;
45 static unsigned cgi_length = 0;
46 static const char *save_file;
47 static const char *temp_file;
48 static const char *control_file;
49 static const char *snapshot_file;
50 static const char *dump_file;
52 static void system_identity(
void);
53 static void system_methods(
void);
54 static void system_help(
void);
55 static void system_signature(
void);
56 static void system_status(
void);
57 static void server_realm(
void);
58 static void server_status(
void);
59 static void server_control(
void);
60 static void call_range(
void);
61 static void call_instance(
void);
62 static void stat_range(
void);
63 static void stat_instance(
void);
64 static void stat_periodic(
void);
65 static void user_range(
void);
66 static void user_instance(
void);
69 {
"system.identity", &system_identity,
"Identify server type and version",
"string"},
70 {
"system.listMethods", &system_methods,
"List server methods",
"array"},
71 {
"system.methodHelp", &system_help,
"Get help text for method",
"string, string"},
72 {
"system.methodSignature", &system_signature,
"Get parameter signature for specified method",
"array, string"},
73 {
"system.status", &system_status,
"Return server status information",
"struct"},
74 {
"server.status", &server_status,
"Return server status string",
"string"},
75 {
"server.control", &server_control,
"Return control request",
"boolean, string"},
76 {
"server.realm", &server_realm,
"Return server realm",
"string"},
77 {
"call.range", &call_range,
"Return list of active calls",
"array"},
78 {
"call.instance", &call_instance,
"Return specific call instance",
"struct, string"},
79 {
"stat.range", &stat_range,
"Return list of call stat nodes",
"array"},
80 {
"stat.instance", &stat_instance,
"return specific statistic node",
"struct, string"},
81 {
"stat.periodic", &stat_periodic,
"return periodic statistics of node",
"struct, string"},
82 {
"user.range", &user_range,
"Return list of user registrations",
"array"},
83 {
"user.instance", &user_instance,
"Return specific user registration",
"struct, string"},
84 {NULL, NULL, NULL, NULL}
96 static size_t xmlformat(
char *dp,
size_t max,
const char *fmt, ...)
104 vsnprintf(dp, max, fmt, args);
109 static const char *getIndexed(
unsigned short param,
unsigned short offset = 0)
117 while(count < params.count) {
118 if(params.param[count] > param)
121 if(params.param[count] == param)
122 if(member++ == offset)
123 return (
const char *)params.value[
count];
184 static size_t xmltext(
char *dp,
size_t max,
const char *src)
187 while(*src && count < max) {
190 snprintf(dp + count, max - count,
"&");
191 count += strlen(dp + count);
195 snprintf(dp + count, max - count,
"<");
196 count += strlen(dp + count);
200 snprintf(dp + count, max - count,
">");
201 count += strlen(dp + count);
205 snprintf(dp + count, max - count,
""");
206 count += strlen(dp + count);
210 snprintf(dp + count, max - count,
"'");
211 count = strlen(dp + count);
215 dp[count++] = *(src++);
221 static size_t b64encode(
char *dest,
const unsigned char *src,
size_t size,
size_t max)
223 static const unsigned char alphabet[65] =
224 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
229 while(size >= 3 && max > 4) {
230 bits = (((unsigned)src[0])<<16) |
231 (((
unsigned)src[1])<<8) | ((unsigned)src[2]);
236 *(dest++) = alphabet[bits >> 18];
237 *(dest++) = alphabet[(bits >> 12) & 0x3f];
238 *(dest++) = alphabet[(bits >> 6) & 0x3f];
239 *(dest++) = alphabet[bits & 0x3f];
247 bits = ((unsigned)src[0])<<16;
248 *(dest++) = alphabet[bits >> 18];
251 *(dest++) = alphabet[(bits >> 12) & 0x3f];
256 bits |= ((unsigned)src[1])<<8;
257 *(dest++) = alphabet[(bits >> 12) & 0x3f];
258 *(dest++) = alphabet[(bits >> 6) & 0x3f];
267 static char *parseText(
char *cp)
280 if(!strncmp(cp,
"&", 5)) {
285 else if(!strncmp(cp,
">", 4))
291 else if(!strncmp(cp,
"<", 4))
297 else if(!strncmp(cp,
""", 6))
303 else if(!strncmp(cp,
"'", 6))
315 static char *parseValue(
char *cp,
char **
value,
char **
map)
326 if(!strncmp(cp,
"<base64>", 8)) {
331 if(!strncmp(cp,
"<struct>", 8))
333 else if(!strncmp(cp,
"<array>", 7))
336 if(*cp ==
'<' && cp[1] !=
'/') {
339 while(*cp && *cp !=
'>')
347 while(*cp && *cp !=
'<')
353 while(*cp && *cp !=
'>')
364 static char *parseName(
char *cp,
char **value)
373 while(*cp && !isspace(*cp) && *cp !=
'<')
380 *value = parseText(t);
384 static void version(
void)
386 printf(
"sipwitch cgi 0.1.0\n"
387 "Copyright (C) 2008 David Sugar, Tycho Softworks\n"
388 "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n"
389 "This is free software: you are free to change and redistribute it.\n"
390 "There is NO WARRANTY, to the extent permitted by law.\n");
394 static void error(
unsigned err,
const char *text)
398 "Content-Type: text/plain\r\n"
400 "%s\r\n", err, text, text);
406 static void cgilock(
void)
410 static void cgiunlock(
void)
418 static pid_t pidfile(
void)
426 fd = open(DEFAULT_VARPATH
"/run/sipwitch/cgilock", O_RDONLY);
427 if(fd < 0 && errno == EPERM)
428 error(403,
"Lock access forbidden");
433 if(read(fd, buf, 16) < 1) {
442 if(kill(pid, 0) && errno == ESRCH)
451 if(ino.st_mtime + 30 < now)
456 static void cgiunlock(
void)
458 remove(DEFAULT_VARPATH
"/run/sipwitch/cgilock");
461 static void cgilock(
void)
469 fd = open(DEFAULT_VARPATH
"/run/sipwitch/cgilock", O_CREAT|O_WRONLY|O_TRUNC|O_EXCL, fsys::OWNER_PUBLIC);
472 if(!opid || opid == 1) {
473 remove(DEFAULT_VARPATH
"/run/sipwitch/cgilock");
481 error(408,
"Lock timed out");
484 snprintf(buf,
sizeof(buf),
"%ld\n", (
long)getpid());
485 if(write(fd, buf, strlen(buf)) < (ssize_t)strlen(buf))
486 error(500,
"Failed Lock");
492 static void request(
const char *fmt, ...)
503 sigaddset(&sigs, SIGUSR1);
504 sigaddset(&sigs, SIGUSR2);
505 sigaddset(&sigs, SIGALRM);
506 sigprocmask(SIG_BLOCK, &sigs, NULL);
507 snprintf(buf,
sizeof(buf),
"%ld ", (
long)getpid());
512 vsnprintf(buf + len,
sizeof(buf) - len, fmt, args);
514 if(!strchr(buf,
'\n'))
515 String::add(buf,
sizeof(buf),
"\n");
517 fp = fopen(control_file,
"w");
519 error(405,
"Server unavailable");
526 sigwait(&sigs, &signo);
528 signo = sigwait(&sigs);
531 error(405,
"Request failed");
533 error(408,
"Request timed out");
537 static void dump(
void)
543 FILE *fp = fopen(dump_file,
"r");
546 error(403,
"Dump unavailable");
550 "Content-Type: text/plain\r\n"
554 if(fgets(buf,
sizeof(buf) - 1, fp) != NULL)
561 static void snapshot(
void)
567 FILE *fp = fopen(snapshot_file,
"r");
570 error(403,
"Snapshot unavailable");
574 "Content-Type: text/plain\r\n"
578 if(fgets(buf,
sizeof(buf) - 1, fp) != NULL)
585 static void config(
void)
588 FILE *fp = fopen(save_file,
"r");
590 error(403,
"Config unavailable");
594 "Content-Type: text/xml\r\n"
598 if(fgets(buf,
sizeof(buf) - 1, fp) != NULL)
605 static void response(
char *buffer,
unsigned max,
const char *fmt, ...)
611 const unsigned char *xp;
614 const char *valtype =
"string";
616 bool end_flag =
false;
617 bool map_flag =
false;
618 bool struct_flag =
false;
619 bool array_flag =
false;
620 size_t count = strlen(buffer);
640 count += xmlformat(buffer + count, max - count,
641 "<?xml version=\"1.0\"?>\r\n"
642 "<methodResponse>\r\n"
643 " <params><param>\r\n");
657 while(*fmt && *fmt !=
'$' && count < max - 1 && !end_flag) {
660 count += xmlformat(buffer + count, max - count,
661 " <value><array><data>\r\n");
667 count += xmlformat(buffer + count, max - count,
668 " </data></array></value>\r\n");
673 map_flag = struct_flag =
false;
674 count += xmlformat(buffer + count, max - count,
675 " </struct></value></member>\r\n"
676 " </struct></value>\r\n");
681 name = va_arg(args,
const char *);
682 count += xmlformat(buffer + count, max - count,
683 " </struct></value></member>\r\n"
684 " <member><name>%s<value><struct>\r\n", name);
688 name = va_arg(args,
const char *);
689 count += xmlformat(buffer + count, max - count,
690 " <value><struct>\r\n"
691 " <member><name>%s</name><value><struct>\r\n", name);
692 struct_flag = map_flag =
true;
696 count += xmlformat(buffer + count, max - count,
" <value><struct>\r\n");
700 if(!map_flag && !array_flag)
702 count += xmlformat(buffer + count, max - count,
703 " </struct></value>\r\n");
729 valtype =
"dateTime.iso8601";
734 if(struct_flag && *fmt ==
'm') {
737 sv = va_arg(args,
const char *);
739 count += xmlformat(buffer + count, max - count,
741 while(*sv && *sv !=
':' && *sv !=
'=' && count < max - 35) {
742 buffer[count++] = tolower(*sv);
746 count += xmlformat(buffer + count, max - count,
748 " <value><%s>", valtype);
749 if(*sv ==
':' || *sv ==
'=')
753 while(*sv && *sv !=
';' && count < max - 20) {
756 count += xmlformat(buffer + count, max - count,
"<");
759 count += xmlformat(buffer + count, max - count,
">");
762 count += xmlformat(buffer + count, max - count,
"&");
765 count += xmlformat(buffer + count, max - count,
""");
768 count += xmlformat(buffer + count, max - count,
"'");
771 buffer[count++] = *sv;
775 count += xmlformat(buffer + count, max - count,
776 "</%s></value></member>\r\n", valtype);
781 name = va_arg(args,
const char *);
782 count += xmlformat(buffer + count, max - count,
783 " <member><name>%s</name>\r\n"
784 " <value><%s>", name, valtype);
787 count += xmlformat(buffer + count, max - count,
788 " <value><%s>", valtype);
792 xp = va_arg(args,
const unsigned char *);
793 xsize = va_arg(args,
size_t);
794 count += b64encode(buffer + count, xp, xsize, max - count);
797 sv = va_arg(args,
const char *);
800 count += xmltext(buffer + count, max - count, sv);
803 dv = va_arg(args,
double);
804 count += xmlformat(buffer + count, max - count,
"%f", dv);
809 if(*fmt ==
'b' && iv)
811 count += xmlformat(buffer + count, max - count,
"%ld", (
long)iv);
814 tv = va_arg(args, time_t);
815 dt = DateTime::local(&tv);
816 if(dt->tm_year < 1800)
818 count += xmlformat(buffer + count, max - count,
819 "%04d%02d%02dT%02d:%02d:%02d",
820 dt->tm_year, dt->tm_mon + 1, dt->tm_mday,
821 dt->tm_hour, dt->tm_min, dt->tm_sec);
822 DateTime::release(dt);
826 count += xmlformat(buffer + count, max - count,
827 "</%s></value></member>\r\n", valtype);
829 count += xmlformat(buffer + count, max - count,
830 "</%s></value>\r\n", valtype);
832 if(!struct_flag && !array_flag)
838 if(*fmt ==
'$' || end_flag)
839 xmlformat(buffer + count, max - count,
840 " </param></params>\r\n"
841 "</methodResponse>\r\n");
846 static void reply(
const char *buffer)
850 "Content-Length: %ld\r\n"
851 "Content-Type: text/xml\r\n"
852 "\r\n%s", (
long)strlen(buffer), buffer);
869 static void fault(
int code,
const char *
string)
873 size_t count = xmlformat(buffer,
sizeof(buffer),
874 "<?xml version=\"1.0\"?>\r\n"
875 "<methodResponse>\r\n"
876 " <fault><value><struct>\r\n"
877 " <member><name>faultCode</name>\r\n"
878 " <value><int>%d</int></value></member>\r\n"
879 " <member><name>faultString</name>\r\n"
880 " <value><string>", code);
881 count += xmltext(buffer + count,
sizeof(buffer) - count,
string);
882 xmlformat(buffer + count,
sizeof(buffer) - count,
883 "</string></value></member>\r\n"
884 " </struct></value></fault>\r\n"
885 "</methodResponse>\r\n");
890 static void system_methods(
void)
896 fault(3,
"Invalid Parameters");
898 response(buffer,
sizeof(buffer),
"^[");
900 while(nodes[index].method) {
901 response(buffer,
sizeof(buffer),
"!s", nodes[index].method);
905 response(buffer,
sizeof(buffer),
"]");
909 static void system_help(
void)
915 fault(3,
"Invalid Parameters");
917 const char *method = getIndexed(1);
918 if(!method || !*method)
919 fault(4,
"Invalid Method Argument");
921 while(nodes[index].method && !String::equal(nodes[index].method, method))
924 if(!nodes[index].help)
925 fault(4,
"Unknown Method");
927 response(buffer,
sizeof(buffer),
"^s", nodes[index].help);
931 static void system_signature(
void)
937 fault(3,
"Invalid Parameters");
939 const char *method = getIndexed(1);
940 if(!method || !*method)
941 fault(4,
"Invalid Method Argument");
943 while(nodes[index].method && !String::equal(nodes[index].method, method))
946 if(!nodes[index].signature)
947 fault(4,
"Unknown Method");
949 response(buffer,
sizeof(buffer),
"^[!s]", nodes[index].signature);
953 static void system_identity(
void)
958 fault(3,
"Invalid Parameters");
960 response(buffer,
sizeof(buffer),
"^s",
"sipwitch/" VERSION);
964 static bool iocontrol(
const char *cmd)
969 snprintf(buffer,
sizeof(buffer),
"%s\n", cmd);
971 snprintf(buffer,
sizeof(buffer),
"%ld %s\n", (
long)getpid(), cmd);
973 char *ep = strchr(buffer,
'\n');
981 sigaddset(&sigs, SIGUSR1);
982 sigaddset(&sigs, SIGUSR2);
983 sigaddset(&sigs, SIGALRM);
984 sigprocmask(SIG_BLOCK, &sigs, NULL);
987 fp = fopen(control_file,
"w");
989 fault(2,
"Server Offline");
996 sigwait(&sigs, &signo);
998 signo = sigwait(&sigs);
1000 if(signo == SIGUSR2)
1002 if(signo == SIGALRM)
1003 fault(6,
"Request Timed Out");
1008 static void server_control(
void)
1012 if(params.argc != 1)
1013 fault(3,
"Invalid Parameters");
1015 const char *command = getIndexed(1);
1016 if(!command || !*command)
1017 fault(5,
"Invalid Command Argument");
1019 response(buffer,
sizeof(buffer),
"^(b)", iocontrol(command));
1023 static void call_instance(
void)
1025 mapped_view<MappedCall> cr(
CALL_MAP);
1033 if(params.argc != 1)
1034 fault(3,
"Invalid Parameters");
1036 const char *cid = getIndexed(1);
1038 fault(5,
"Invalid Command Argument");
1041 unsigned count = cr.count();
1043 fault(2,
"Server Offline");
1046 while(index < count) {
1047 cr.copy(index++, map);
1051 snprintf(
id,
sizeof(
id),
"%08x:%d", map.
sequence, map.
cid);
1060 response(buffer,
sizeof(buffer),
"^(tsssssi)",
1065 fault(6,
"Unknown Call");
1066 reset_unsafe<MappedCall>(
map);
1069 static void call_range(
void)
1077 if(params.argc != 0)
1078 fault(3,
"Invalid Parameters");
1080 unsigned count = cr.count();
1082 fault(2,
"Server Offline");
1084 size = count * 64 + 128;
1085 char *buffer = (
char *)malloc(size);
1086 response(buffer, size,
"^[");
1088 while(index < count) {
1089 cr.copy(index++, map);
1094 snprintf(
id,
sizeof(
id),
"%08x:%d", map.
sequence, map.
cid);
1095 response(buffer, size,
"!s", buffer);
1097 response(buffer, size,
"]");
1099 reset_unsafe<MappedCall>(
map);
1102 static void stat_periodic(
void)
1109 if(params.argc != 1)
1110 fault(3,
"Invalid Parameters");
1112 const char *cid = getIndexed(1);
1114 fault(5,
"Invalid Command Argument");
1116 unsigned count = sta.count();
1118 fault(2,
"Server Offline");
1120 while(index < count) {
1121 sta.copy(index++, map);
1122 if(!eq(map.
id, cid))
1125 response(buffer,
sizeof(buffer),
"^(itiiiiii)",
1131 fault(7,
"Unknown Stat");
1132 reset_unsafe<stats>(
map);
1135 static void stat_instance(
void)
1142 if(params.argc != 1)
1143 fault(3,
"Invalid Parameters");
1145 const char *cid = getIndexed(1);
1147 fault(5,
"Invalid Command Argument");
1149 unsigned count = sta.count();
1151 fault(2,
"Server Offline");
1153 while(index < count) {
1154 sta.copy(index++, map);
1155 if(!eq(map.
id, cid))
1158 response(buffer,
sizeof(buffer),
"^(itiiiiii)",
1164 fault(7,
"Unknown Stat");
1165 reset_unsafe<stats>(
map);
1168 static void stat_range(
void)
1175 if(params.argc != 0)
1176 fault(3,
"Invalid Parameters");
1178 unsigned count = sta.count();
1180 fault(2,
"Server Offline");
1182 size = count * 48 + 128;
1183 char *buffer = (
char *)malloc(size);
1184 response(buffer, size,
"^[");
1186 while(index < count) {
1187 sta.copy(index++, map);
1191 response(buffer, size,
"!s", map.
id);
1193 response(buffer, size,
"]");
1195 reset_unsafe<stats>(
map);
1198 static void user_instance(
void)
1206 const char *status =
"idle";
1208 if(params.argc != 1)
1209 fault(3,
"Invalid Parameters");
1211 const char *
id = getIndexed(1);
1213 fault(5,
"Invalid Command Argument");
1215 unsigned count = reg.count();
1217 fault(2,
"Server Offline");
1220 while(index < count) {
1221 reg.copy(index++, map);
1233 snprintf(ext,
sizeof(ext),
"%u", map.
ext);
1235 String::set(ext,
sizeof(ext), map.
userid);
1254 response(buffer,
sizeof(buffer),
"^(ssssii)",
1259 fault(8,
"Unknown User");
1260 reset_unsafe<MappedRegistry>(
map);
1263 static void user_range(
void)
1270 if(params.argc != 0)
1271 fault(3,
"Invalid Parameters");
1273 unsigned count = reg.count();
1275 fault(2,
"Server Offline");
1277 size = count * 48 + 128;
1278 char *buffer = (
char *)malloc(size);
1279 response(buffer, size,
"^[");
1283 while(index < count) {
1284 reg.copy(index++, map);
1292 response(buffer, size,
"!s", map.
userid);
1294 response(buffer, size,
"]");
1296 reset_unsafe<MappedRegistry>(
map);
1299 static void server_realm(
void)
1305 if(params.argc != 0)
1306 fault(3,
"Invalid Parameters");
1308 fd.open(DEFAULT_CFGPATH
"/siprealm", fsys::RDONLY);
1310 fd.open(DEFAULT_VARPATH
"/lib/sipwitch/uuid", fsys::RDONLY);
1313 memset(realm, 0,
sizeof(realm));
1314 fd.read(realm,
sizeof(realm) - 1);
1317 char *cp = strchr(realm,
'\n');
1321 cp = strchr(realm,
':');
1326 fault(2,
"No Realm Available");
1328 response(buffer,
sizeof(buffer),
"^s", realm);
1332 static void server_status(
void)
1339 if(params.argc != 0)
1340 fault(3,
"Invalid Parameters");
1342 unsigned count = cr.count();
1344 fault(2,
"Server Offline");
1346 cp = (
char *)malloc(count + 1);
1348 memset(cp,
' ', count);
1349 while(index < count) {
1350 map = (
const volatile MappedCall*)(cr(index++));
1352 cp[index - 1] = map->
state[0];
1354 char *buffer = (
char *)malloc(count + 512);
1355 response(buffer, count + 512,
"^s", cp);
1359 static void system_status(
void)
1367 if(params.argc != 0)
1368 fault(3,
"Invalid Parameters");
1370 if(stat(control_file, &ino))
1371 fault(2,
"Server Offline");
1373 while(nodes[count].method)
1377 response(buffer,
sizeof(buffer),
"^(titissi)",
1380 "started", ino.st_ctime,
1381 "started_int", ino.st_ctime,
1389 static void dispatch(
const char *method)
1392 while(nodes[index].method && !String::equal(method, nodes[index].method))
1394 if(!nodes[index].method)
1395 fault(1,
"Unknown Method");
1396 (*nodes[index].
exec)();
1399 static void post(FILE *inp = stdin)
1406 char *method = NULL;
1407 bool name_flag =
false;
1413 error(411,
"Length required");
1414 if(!cgi_content || stricmp(cgi_content,
"text/xml"))
1415 error(415,
"Unsupported media type");
1417 buf =
new char[cgi_length + 1];
1418 if(fread(buf, cgi_length, 1, inp) < 1)
1419 error(400,
"Invalid read of input");
1421 buf[cgi_length] = 0;
1428 if(!strncmp(cp,
"<sipwitch>", 10)) {
1431 fp = fopen(temp_file,
"w");
1434 error(403,
"Access forbidden");
1437 if(fwrite(buf, cgi_length, 1, fp) < 1) {
1439 error(500,
"Invalid write of config");
1443 rename(temp_file, save_file);
1449 if(!strncmp(cp,
"<methodName>", 12)) {
1450 cp = parseName(cp + 12, &method);
1451 if(strncmp(cp,
"/methodName>", 12) || !method)
1452 error(400,
"Malformed request");
1459 if(!strncmp(cp,
"<methodCall>", 12)) {
1464 if(!strncmp(cp,
"<?", 2) || !strncmp(cp,
"<!", 2)) {
1465 while(*cp && *cp !=
'>')
1472 error(400,
"Malformed request");
1476 error(400,
"Malformed request");
1485 if(!strncmp(cp,
"<name>", 6)) {
1487 cp = parseName(cp + 6, ¶ms.name[params.count]);
1488 params.map[params.count] =
map;
1489 if(strncmp(cp,
"/name>", 6))
1490 error(400,
"Malformed request");
1495 if(!strncmp(cp,
"</struct>", 9) && map && !name_flag) {
1500 if(!strncmp(cp,
"<param>", 7)) {
1501 params.name[params.count] = params.value[params.count] = params.map[params.count] = NULL;
1506 if(!strncmp(cp,
"<value>", 7)) {
1507 params.param[params.count] = params.argc;
1508 cp = parseValue(cp, &value, NULL);
1510 params.value[params.count++] =
value;
1512 map = params.name[params.count];
1514 params.name[params.count] = params.map[params.count] = params.value[params.count] = NULL;
1517 if(!strncmp(cp,
"</params>", 9))
1521 while(*cp && *cp !=
'>')
1526 error(400,
"Malformed request");
1529 error(400,
"Malformed request");
1531 error(400,
"Malformed request");
1534 static void callfile(FILE *fp,
const char *
id)
1542 const char *cp = NULL;
1543 unsigned long date, line = 0;
1544 unsigned long last_date = 0, last_line = 0;
1551 _fstat(_fileno(fp), &ino);
1553 fstat(fileno(fp), &ino);
1556 dt = DateTime::local(&ino.st_ctime);
1558 if(dt->tm_year >= 2000)
1559 dt->tm_year -= 2000;
1561 date = dt->tm_hour + (32l * dt->tm_mday) + (1024l * dt->tm_mon) + (16384l * dt->tm_year);
1562 DateTime::release(dt);
1565 last_date = atol(
id);
1566 cp = strchr(
id,
'/');
1569 last_line = atol(++cp);
1571 if(date < last_date) {
1577 if(fgets(buf,
sizeof(buf), fp) == NULL)
1583 if(date == last_date && line <= last_line)
1585 if(
id && *
id && strnicmp(
id, cp, strlen(
id) > 0))
1587 printf(
"%07ld/%07ld %s", date, line, cp);
1593 static void calls(
const char *
id)
1596 "Status: 200 OK\r\n"
1597 "Content-Type: text/plain\r\n"
1599 callfile(fopen(DEFAULT_VARPATH
"/log/sipwitch.calls.0",
"r"),
id);
1600 callfile(fopen(DEFAULT_VARPATH
"/log/sipwitch.calls",
"r"),
id);
1604 static void info(
void)
1609 "Status: 200 OK\r\n"
1610 "Content-Type: text/xml\r\n"
1613 printf(
"<?xml version=\"1.0\"?>\n");
1614 printf(
"<serviceInfo>\n");
1615 printf(
" <version>" VERSION
"</version>\n");
1616 FILE *fp = fopen(DEFAULT_VARPATH
"/run/sipwitch/state.def",
"r");
1617 String::set(buf,
sizeof(buf),
"up");
1619 if(fgets(buf,
sizeof(buf), fp) == NULL)
1623 cp = strchr(buf,
'\r');
1625 cp = strchr(buf,
'\n');
1628 if(!stricmp(buf,
"none") || !buf[0])
1629 String::set(buf,
sizeof(buf),
"up");
1632 fp = fopen(DEFAULT_VARPATH
"/run/sipwitch/pidfile",
"r");
1634 if(fgets(buf,
sizeof(buf), fp) == NULL)
1639 if(kill(pid, 0) && errno == ESRCH)
1644 String::set(buf,
sizeof(buf),
"down");
1646 printf(
" <state>%s</state>\n", buf);
1647 printf(
"</serviceInfo>\n");
1651 static void dumpcalls(
const char *
id)
1653 mapped_view<MappedCall> calls(
CALL_MAP);
1654 unsigned count = calls.count();
1661 error(405,
"Server unavailable");
1664 "Status: 200 OK\r\n"
1665 "Content-Type: text/xml\r\n"
1668 printf(
"<?xml version=\"1.0\"?>\n");
1669 printf(
"<mappedCalls>\n");
1672 while(index < count) {
1673 calls.copy(index++, buffer);
1677 snprintf(idbuf,
sizeof(idbuf),
"%08x:%u", buffer.
sequence, buffer.
cid);
1678 if(
id && !String::equal(
id, idbuf))
1680 printf(
" <call id=\"%s\">\n", idbuf);
1681 printf(
" <source>%s</source>\n", buffer.
source);
1682 printf(
" <started>%ld</started>\n", (
long)(now - buffer.
created));
1684 printf(
" <active>%ld</active>\n", (
long)(now - buffer.
active));
1685 printf(
" <target>%s</target>\n", buffer.
target);
1687 printf(
" </call>\n");
1689 printf(
"</mappedCalls>\n");
1694 static void dumpstats(
const char *
id)
1697 unsigned count = sta.count();
1703 error(405,
"Server unavailable");
1706 "Status: 200 OK\r\n"
1707 "Content-Type: text/xml\r\n"
1710 printf(
"<?xml version=\"1.0\"?>\n");
1711 printf(
"<mappedStats>\n");
1714 while(index < count) {
1715 sta.copy(index++, buffer);
1718 if(
id && !eq(
id, buffer.
id))
1720 printf(
" <stat id=\"%s\">\n", buffer.
id);
1721 printf(
" <incoming>\n");
1722 printf(
" <total>%lu</total>\n", buffer.
data[0].
total);
1723 printf(
" <period>%lu</period>\n", buffer.
data[0].
period);
1724 printf(
" <current>%hu</current>\n", buffer.
data[0].
current);
1725 printf(
" <peak>%hu</peak>\n", buffer.
data[0].
peak);
1726 printf(
" </incoming>\n");
1727 printf(
" <outgoing>\n");
1728 printf(
" <total>%lu</total>\n", buffer.
data[1].
total);
1729 printf(
" <period>%lu</period>\n", buffer.
data[1].
period);
1730 printf(
" <current>%hu</current>\n", buffer.
data[1].
current);
1731 printf(
" <peak>%hu</peak>\n", buffer.
data[1].
peak);
1732 printf(
" </outgoing>\n");
1733 printf(
" </stat>\n");
1735 printf(
"</mappedStats>\n");
1740 static void registry(
const char *
id)
1743 unsigned count = reg.count();
1753 error(405,
"Server unavailable");
1756 "Status: 200 OK\r\n"
1757 "Content-Type: text/xml\r\n"
1760 printf(
"<?xml version=\"1.0\"?>\n");
1761 printf(
"<mappedRegistry>\n");
1763 while(index < count) {
1764 reg.copy(index++, buffer);
1772 if(
id && buffer.
ext && (
unsigned)atoi(
id) == buffer.
ext)
1774 if(
id && stricmp(
id, buffer.
userid))
1777 printf(
" <registry id=\"%s\">\n", buffer.
userid);
1779 printf(
" <extension>%d</extension>\n", buffer.
ext);
1780 printf(
" <used>%u</used>\n", buffer.
inuse);
1782 printf(
" <expires>%ld</expires>\n", (
long)(buffer.
expires - now));
1783 switch(buffer.
type) {
1802 printf(
" <type>%s</type>\n", type);
1803 printf(
" <class>%s</class>\n", buffer.
profile.
id);
1805 dt = DateTime::local(&buffer.
created);
1806 if(dt->tm_year < 1000)
1807 dt->tm_year += 1900;
1809 printf(
" <created>%04d%02d%02dT%02d%02d%02d</created>\n",
1810 dt->tm_year, dt->tm_mon + 1, dt->tm_mday,
1811 dt->tm_hour, dt->tm_min, dt->tm_sec);
1813 DateTime::release(dt);
1814 Socket::query((
struct sockaddr *)&buffer.
contact, buf,
sizeof(buf));
1815 port = Socket::port((
struct sockaddr *)&buffer.
contact);
1816 printf(
" <address>%s</address>\n", buf);
1817 printf(
" <service>%u</service>\n", port);
1818 printf(
" </registry>\n");
1821 printf(
"</mappedRegistry>\n");
1829 if(String::equal(argv[1],
"-version") || String::equal(argv[1],
"--version"))
1835 GetEnvironmentVariable(
"APPDATA", buf, 192);
1836 unsigned len = strlen(buf);
1837 snprintf(buf + len,
sizeof(buf) - len,
"\\sipwitch\\config.xml");
1838 save_file = strdup(buf);
1839 snprintf(buf + len,
sizeof(buf) - len,
"\\sipwitch\\config.tmp");
1840 temp_file = strdup(buf);
1841 snprintf(buf + len,
sizeof(buf) - len,
"\\sipwitch\\snapshot.log");
1842 snapshot_file = strdup(buf);
1843 snprintf(buf + len,
sizeof(buf) - len,
"\\sipwitch\\dumpfile.log");
1844 dump_file = strdup(buf);
1845 if(GetEnvironmentVariable(
"GATEWAY_INTERFACE", buf,
sizeof(buf)) > 0)
1846 cgi_version = strdup(buf);
1847 if(GetEnvironmentVariable(
"REMOTE_USER", buf,
sizeof(buf)) > 0)
1848 cgi_remuser = strdup(buf);
1849 if(GetEnvironmentVariable(
"REQUEST_METHOD", buf,
sizeof(buf)) > 0)
1850 cgi_method = strdup(buf);
1851 if(GetEnvironmentVariable(
"QUERY_STRING", buf,
sizeof(buf)) > 0)
1852 cgi_query = strdup(buf);
1853 if(GetEnvironmentVariable(
"CONTENT_TYPE", buf,
sizeof(buf)) > 0)
1854 cgi_content = strdup(buf);
1855 if(GetEnvironmentVariable(
"CONTENT_LENGTH", buf,
sizeof(buf)) > 0)
1856 cgi_length = atol(buf);
1857 control_file =
"\\\\.\\mailslot\\sipwitch_ctrl";
1859 save_file = DEFAULT_VARPATH
"/run/sipwitch/config.xml";
1860 temp_file = DEFAULT_VARPATH
"/run/sipwitch/config.tmp";
1861 control_file = DEFAULT_VARPATH
"/run/sipwitch/control";
1862 dump_file = DEFAULT_VARPATH
"/run/sipwitch/dumpfile";
1863 snapshot_file = DEFAULT_VARPATH
"/run/sipwitch/snapshot";
1864 cgi_version = getenv(
"GATEWAY_INTERFACE");
1865 cgi_remuser = getenv(
"REMOTE_USER");
1866 cgi_method = getenv(
"REQUEST_METHOD");
1867 cgi_query = getenv(
"QUERY_STRING");
1868 cgi_content = getenv(
"CONTENT_LENGTH");
1870 cgi_length = atol(cgi_content);
1871 cgi_content = getenv(
"CONTENT_TYPE");
1875 shell::errexit(1,
"*** sipwitch.cgi must execute from http server on password protected resource\n");
1877 if(!cgi_remuser || !*cgi_remuser)
1878 error(403,
"Unauthorized to access sipwitch interface");
1880 if(cgi_method && !stricmp(cgi_method,
"post"))
1884 if(!strnicmp(cgi_query,
"state-", 6) || !strnicmp(cgi_query,
"state=", 6) || !strnicmp(cgi_query,
"state_", 6)) {
1885 request(
"state %s", cgi_query + 6);
1889 if(!stricmp(cgi_query,
"reload")) {
1894 if(!stricmp(cgi_query,
"restart")) {
1899 if(!stricmp(cgi_query,
"check")) {
1904 if(!stricmp(cgi_query,
"snapshot"))
1907 if(!stricmp(cgi_query,
"dump"))
1910 if(!stricmp(cgi_query,
"info"))
1913 if(!stricmp(cgi_query,
"registry"))
1916 if(!stricmp(cgi_query,
"stats"))
1919 if(!stricmp(cgi_query,
"sessions"))
1922 if(!stricmp(cgi_query,
"calls"))
1925 if(!strnicmp(cgi_query,
"registry=", 9))
1928 if(!strnicmp(cgi_query,
"stats=", 6))
1929 dumpstats(cgi_query + 6);
1931 if(!strnicmp(cgi_query,
"calls=", 6))
1932 calls(cgi_query + 6);
1934 if(!strnicmp(cgi_query,
"sessions=", 9))
1935 dumpcalls(cgi_query + 9);
A stat element of call traffic.
Representation of a mapped active user record.
sockaddr_internet contact
Top level include directory for GNU Telephony SIP Witch Server.
Representation of an active call record.
struct sipwitch::stats::@9 data[2]
We have stats for both incoming and outgoing traffic of various kinds.
enum sipwitch::MappedRegistry::@5 type