1 /*
2  * Copyright (c) 2014 Sippy Software, Inc., http://www.sippysoft.com
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  */
27 
28 #include <sys/socket.h>
29 #include <sys/types.h>
30 #include <stdint.h>
31 #include <stdio.h>
32 #include <string.h>
33 
34 #include "config.h"
35 
36 #include "rtpp_ssrc.h"
37 #include "rtpa_stats.h"
38 #include "rtpp_log.h"
39 #include "rtpp_types.h"
40 #include "rtpp_log_obj.h"
41 #include "rtpp_analyzer.h"
42 #include "rtpp_command.h"
43 #include "rtpp_command_private.h"
44 #include "rtpp_pcount.h"
45 #include "rtpp_pcnt_strm.h"
46 #include "rtpp_pipe.h"
47 #include "rtpp_stream.h"
48 #include "rtpp_util.h"
49 #include "rtpp_command_query.h"
50 
51 #define CHECK_OVERFLOW() \
52     if (len > sizeof(cmd->buf_t) - 2) { \
53         RTPP_LOG(spp->log, RTPP_LOG_ERR, \
54           "QUERY: output buffer overflow"); \
55         return (ECODE_RTOOBIG_2); \
56     }
57 
58 static int
59 handle_query_simple(struct cfg *cf, struct rtpp_command *cmd,
60   struct rtpp_pipe *spp, int idx, int verbose)
61 {
62     int len, ttl;
63     struct rtpps_pcount pcnts;
64     struct rtpp_pcnts_strm pst[2];
65 
66     ttl = CALL_METHOD(spp, get_ttl);
67     CALL_METHOD(spp->pcount, get_stats, &pcnts);
68     CALL_METHOD(spp->stream[idx]->pcnt_strm, get_stats, &pst[0]);
69     CALL_METHOD(spp->stream[NOT(idx)]->pcnt_strm, get_stats, &pst[1]);
70     if (verbose == 0) {
71         len = snprintf(cmd->buf_t, sizeof(cmd->buf_t), "%d %lu %lu %lu %lu\n",
72           ttl, pst[0].npkts_in, pst[1].npkts_in, pcnts.nrelayed, pcnts.ndropped);
73     } else {
74         len = snprintf(cmd->buf_t, sizeof(cmd->buf_t), "ttl=%d npkts_ina=%lu "
75           "npkts_ino=%lu nrelayed=%lu ndropped=%lu\n", ttl,
76           pst[0].npkts_in, pst[1].npkts_in, pcnts.nrelayed, pcnts.ndropped);
77     }
78     rtpc_doreply(cmd, cmd->buf_t, len, 0);
79     return (0);
80 }
81 
82 #define PULL_RST() \
83     if (rst_pulled == 0) { \
84         CALL_METHOD(spp->stream[idx]->analyzer, get_stats, &rst); \
85         rst_pulled = 1; \
86     }
87 
88 #define PULL_PCNT() \
89     if (pcnt_pulled == 0) { \
90         CALL_METHOD(spp->pcount, get_stats, &pcnts); \
91         pcnt_pulled = 1; \
92     }
93 
94 #define PULL_PCNT_STRM() \
95     if (pcnt_strm_pulled == 0) { \
96         CALL_METHOD(spp->stream[idx]->pcnt_strm, get_stats, &pst[0]); \
97         CALL_METHOD(spp->stream[NOT(idx)]->pcnt_strm, get_stats, &pst[1]); \
98         pcnt_strm_pulled = 1; \
99     }
100 
101 int
102 handle_query(struct cfg *cf, struct rtpp_command *cmd,
103   struct rtpp_pipe *spp, int idx)
104 {
105     int len, i, verbose, rst_pulled, pcnt_pulled, pcnt_strm_pulled;
106     char *cp;
107     struct rtpa_stats rst;
108     struct rtpps_pcount pcnts;
109     struct rtpp_pcnts_strm pst[2];
110 
111     verbose = 0;
112     for (cp = cmd->argv[0] + 1; *cp != '\0'; cp++) {
113         switch (*cp) {
114         case 'v':
115         case 'V':
116             verbose = 1;
117             break;
118 
119         default:
120             RTPP_LOG(spp->log, RTPP_LOG_ERR,
121               "QUERY: unknown command modifier `%c'", *cp);
122             return (ECODE_PARSE_8);
123         }
124     }
125     if (cmd->argc <= 4) {
126         return (handle_query_simple(cf, cmd, spp, idx, verbose));
127     }
128     len = 0;
129     rst_pulled = pcnt_pulled = pcnt_strm_pulled = 0;
130     for (i = 4; i < cmd->argc && len < (sizeof(cmd->buf_t) - 2); i++) {
131         if (i > 4) {
132             CHECK_OVERFLOW();
133             len += snprintf(cmd->buf_t + len, sizeof(cmd->buf_t) - len, " ");
134         }
135         if (verbose != 0) {
136             CHECK_OVERFLOW();
137             len += snprintf(cmd->buf_t + len, sizeof(cmd->buf_t) - len, "%s=", \
138               cmd->argv[i]);
139         }
140         CHECK_OVERFLOW();
141         if (strcmp(cmd->argv[i], "ttl") == 0) {
142             int ttl = CALL_METHOD(spp, get_ttl);
143             len += snprintf(cmd->buf_t + len, sizeof(cmd->buf_t) - len, "%d",
144               ttl);
145             continue;
146         }
147         if (strcmp(cmd->argv[i], "npkts_ina") == 0) {
148             PULL_PCNT_STRM();
149             len += snprintf(cmd->buf_t + len, sizeof(cmd->buf_t) - len, "%lu",
150               pst[0].npkts_in);
151             continue;
152         }
153         if (strcmp(cmd->argv[i], "npkts_ino") == 0) {
154             PULL_PCNT_STRM();
155             len += snprintf(cmd->buf_t + len, sizeof(cmd->buf_t) - len, "%lu",
156               pst[1].npkts_in);
157             continue;
158         }
159         if (strcmp(cmd->argv[i], "nrelayed") == 0) {
160             PULL_PCNT();
161             len += snprintf(cmd->buf_t + len, sizeof(cmd->buf_t) - len, "%lu",
162               pcnts.nrelayed);
163             continue;
164         }
165         if (strcmp(cmd->argv[i], "ndropped") == 0) {
166             PULL_PCNT();
167             len += snprintf(cmd->buf_t + len, sizeof(cmd->buf_t) - len, "%lu",
168               pcnts.ndropped);
169             continue;
170         }
171         if (strcmp(cmd->argv[i], "rtpa_nsent") == 0) {
172             PULL_RST();
173             len += snprintf(cmd->buf_t + len, sizeof(cmd->buf_t) - len, "%lu",
174               rst.psent);
175             continue;
176         }
177         if (strcmp(cmd->argv[i], "rtpa_nrcvd") == 0) {
178             PULL_RST();
179             len += snprintf(cmd->buf_t + len, sizeof(cmd->buf_t) - len, "%lu",
180               rst.precvd);
181             continue;
182         }
183         if (strcmp(cmd->argv[i], "rtpa_ndups") == 0) {
184             PULL_RST();
185             len += snprintf(cmd->buf_t + len, sizeof(cmd->buf_t) - len, "%lu",
186               rst.pdups);
187             continue;
188         }
189         if (strcmp(cmd->argv[i], "rtpa_nlost") == 0) {
190             PULL_RST();
191             len += snprintf(cmd->buf_t + len, sizeof(cmd->buf_t) - len, "%lu",
192               rst.plost);
193             continue;
194         }
195         if (strcmp(cmd->argv[i], "rtpa_perrs") == 0) {
196             PULL_RST();
197             len += snprintf(cmd->buf_t + len, sizeof(cmd->buf_t) - len, "%lu",
198               rst.pecount);
199             continue;
200         }
201         RTPP_LOG(spp->log, RTPP_LOG_ERR,
202               "QUERY: unsupported/invalid counter name `%s'", cmd->argv[i]);
203         return (ECODE_QRYFAIL);
204     }
205     CHECK_OVERFLOW();
206     len += snprintf(cmd->buf_t + len, sizeof(cmd->buf_t) - len, "\n");
207     rtpc_doreply(cmd, cmd->buf_t, len, 0);
208     return (0);
209 }
210