UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
control_socket.cpp
Go to the documentation of this file.
1
5/*
6 * Copyright (c) 2013-2023 CESNET, z. s. p. o.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, is permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * 3. Neither the name of CESNET nor the names of its contributors may be
21 * used to endorse or promote products derived from this software without
22 * specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
26 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
27 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
28 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
35 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#ifdef HAVE_CONFIG_H
39#include "config.h"
40#include "config_unix.h"
41#include "config_win32.h"
42#endif // HAVE_CONFIG_H
43
44#include "control_socket.h"
46
47#include <condition_variable>
48#include <mutex>
49#include <queue>
50#include <string>
51#include <thread>
52
53#include "debug.h"
54#include "host.h"
55#include "messaging.h"
56#include "module.h"
57#include "rtp/net_udp.h" // socket_error
58#include "tv.h"
59#include "utils/net.h"
60#include "utils/thread.h"
61
62#define MAX_CLIENTS 16
63
64#ifdef _WIN32
65typedef const char *sso_val_type;
66#else
67typedef void *sso_val_type;
68#endif // defined _WIN32
69
70using namespace std;
71
72struct client {
74 char buff[1024];
76
77 struct client *prev;
78 struct client *next;
79};
80
85
86#define MAX_STAT_EVENT_QUEUE 100
87
111
112#define CONTROL_EXIT -1
113#define CONTROL_CLOSE_HANDLE -2
114
115static bool parse_msg(char *buffer, int buffer_len, /* out */ char *message, int *new_buffer_len);
116static int process_msg(struct control_state *s, fd_t client_fd, char *message, struct client *clients);
117static ssize_t write_all(fd_t fd, const void *buf, size_t count);
118static void * control_thread(void *args);
119static void * stat_event_thread(void *args);
120static void send_response(fd_t fd, struct response *resp);
121static void print_control_help();
122
123#ifndef MSG_NOSIGNAL
124#define MSG_NOSIGNAL 0
125#endif
126
127static ssize_t write_all(fd_t fd, const void *buf, size_t count)
128{
129 const char *p = (const char *) buf;
130 size_t rest = count;
131 ssize_t w = 0;
132
133 while (rest > 0 && ((fd > 2 && (w = send(fd, p, rest, MSG_NOSIGNAL)) > 0) ||
134 (w = write(fd, p, rest)) > 0)) {
135 p += w;
136 rest -= w;
137 }
138
139 if (rest > 0)
140 return w;
141 else
142 return count;
143}
144
145static void new_message(struct module *m) {
147 if(s->started) {
148 // just wake up from select
149 int ret = write_all(s->internal_fd[1], "noop\r\n", 6);
150 if (ret <= 0) {
151 log_msg(LOG_LEVEL_ERROR, "[control] Cannot write to pipe!\n");
152 }
153 }
154}
155
156ADD_TO_PARAM("control-report-audio-ch-count", "* control-report-audio-ch-count=<count>\n"
157 " The number of channels reported over control port.\n");
158
159int control_init(int port, int connection_type, struct control_state **state, struct module *root_module, int force_ip_version)
160{
161 control_state *s = new control_state();
162
163 s->root_module = root_module;
164 s->started = false;
165
169 s->mod.priv_data = s;
170
171 if(connection_type == 0) {
172 s->network_port = port;
174 } else {
175 s->network_port = port;
177 }
178
179 if(s->connection_type == SERVER) {
180 int ip_version = 6;
181 if (force_ip_version != 4) {
182 s->socket_fd = socket(AF_INET6, SOCK_STREAM, 0);
183 if (s->socket_fd == INVALID_SOCKET) {
184 socket_error("Control socket - IPv6 not available");
185 }
186 }
187 if (force_ip_version == 4 || (force_ip_version == 0 && s->socket_fd == INVALID_SOCKET && errno == EAFNOSUPPORT)) { // try IPv4
188 ip_version = 4;
189 s->socket_fd = socket(AF_INET, SOCK_STREAM, 0);
190 }
191 if (s->socket_fd == INVALID_SOCKET) {
192 socket_error("Control socket - socket");
193 goto error;
194 }
195 int val = 1;
196 int rc;
197 rc = setsockopt(s->socket_fd, SOL_SOCKET, SO_REUSEADDR,
198 (sso_val_type) &val, sizeof(val));
199 if (rc != 0) {
200 socket_error("Control socket - setsockopt");
201 }
202
203 int ipv6only = 0;
204 if (ip_version == 6 && setsockopt(s->socket_fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&ipv6only,
205 sizeof(ipv6only)) != 0) {
206 socket_error("setsockopt IPV6_V6ONLY");
207 }
208
209 /* setting address to in6addr_any allows connections to be established
210 * from both IPv4 and IPv6 hosts. This behavior can be modified
211 * using the IPPROTO_IPV6 level socket option IPV6_V6ONLY if required.*/
212 struct sockaddr_storage ss{};
213 socklen_t s_len = 0;
214 if (ip_version == 4) {
215 auto *s_in = reinterpret_cast<struct sockaddr_in *>(&ss);
216 s_in->sin_family = AF_INET;
217 s_in->sin_addr.s_addr = htonl(INADDR_ANY);
218 s_in->sin_port = htons(s->network_port);
219 s_len = sizeof *s_in;
220 } else {
221 auto *s_in6 = reinterpret_cast<struct sockaddr_in6 *>(&ss);
222 s_in6->sin6_family = AF_INET6;
223 s_in6->sin6_addr = in6addr_any;
224 s_in6->sin6_port = htons(s->network_port);
225 s_len = sizeof *s_in6;
226 }
227
228 rc = ::bind(s->socket_fd, reinterpret_cast<const struct sockaddr *>(&ss), s_len);
229 if (rc != 0) {
230 socket_error("Control socket - bind");
231 goto error;
232 } else {
233 rc = listen(s->socket_fd, MAX_CLIENTS);
234 if (rc != 0) {
235 socket_error("Control socket - listen");
236 goto error;
237 }
238 }
239 } else {
240 if (force_ip_version == 6) {
241 log_msg(LOG_LEVEL_ERROR, "Control socket: IPv6 unimplemented in client mode!\n");
242 goto error;
243 }
244 s->socket_fd = socket(AF_INET, SOCK_STREAM, 0);
245 assert(s->socket_fd != INVALID_SOCKET);
246 struct addrinfo hints, *res, *res0;
247 int err;
248
249 memset(&hints, 0, sizeof(hints));
250 hints.ai_family = AF_INET;
251 hints.ai_socktype = SOCK_STREAM;
252 char port_str[10];
253 snprintf(port_str, 10, "%d", s->network_port);
254 err = getaddrinfo("127.0.0.1", port_str, &hints, &res0);
255
256 if(err) {
257 fprintf(stderr, "Unable to get address: %s\n", gai_strerror(err));
258 goto error;
259 }
260 bool connected = false;
261
262 for (res = res0; res; res = res->ai_next) {
263 if(connect(s->socket_fd, res->ai_addr, res->ai_addrlen) == -1) {
264 continue;
265 }
266 connected = true;
267 break; /* okay we got one */
268 }
269
270 freeaddrinfo(res0);
271
272 if(!connected) {
273 fprintf(stderr, "Unable to connect to localhost:%d\n", s->network_port);
274 goto error;
275 }
276 }
277
278
279 module_register(&s->mod, root_module);
280
281 if (const char *val = get_commandline_param("control-report-audio-ch-count")) {
282 s->audio_channel_report_count = strtoll(val, NULL, 0);
283 }
284
285 *state = s;
286 return 0;
287
288error:
289 if (s->socket_fd != INVALID_SOCKET) {
291 }
292 delete s;
293 return -1;
294}
295
297{
298 if (s == NULL) {
299 return;
300 }
301
303
304 s->control_thread_id = thread(control_thread, s);
306
307 log_msg(LOG_LEVEL_NOTICE, "Control socket listening on port %d\n",
309 s->started = true;
310}
311
312#define prefix_matches(x,y) strncasecmp(x, y, strlen(y)) == 0
313#define suffix(x,y) x + strlen(y)
314#define is_internal_port(x) (x == s->internal_fd[0])
315
316static struct response *
317process_audio_message(struct module *root_module, const char *cmd)
318{
319 char path[STR_LEN] = "";
320 if (strcmp(cmd, "mute") == 0 || strstr(cmd, "-receiver") != nullptr) {
321 strncpy(path, "audio.receiver", sizeof path);
322 auto *msg = (struct msg_receiver *) new_message(
323 sizeof(struct msg_receiver));
324 if (strcmp(cmd, "mute") == 0) {
326 } else if (prefix_matches(cmd, "mute-")) {
327 msg->type = RECEIVER_MSG_MUTE;
328 } else if (prefix_matches(cmd, "unmute-")) {
329 msg->type = RECEIVER_MSG_UNMUTE;
330 } else {
331 free_message((struct message *) msg, nullptr);
333 "malformed audio recv mute msg");
334 }
335 return send_message(root_module, path, (struct message *) msg);
336 }
337 if (strstr(cmd, "-sender") != nullptr) {
338 strncpy(path, "audio.sender", sizeof path);
339 auto *msg = (struct msg_sender *) new_message(
340 sizeof(struct msg_sender));
341 if (prefix_matches(cmd, "mute-")) {
342 msg->type = SENDER_MSG_MUTE;
343 } else if (prefix_matches(cmd, "unmute-")) {
344 msg->type = SENDER_MSG_UNMUTE;
345 } else {
346 free_message((struct message *) msg, nullptr);
348 "malformed audio send mute msg");
349 }
350 return send_message(root_module, path, (struct message *) msg);
351 }
352 if (prefix_matches(cmd, "volume ")) {
353 strncpy(path, "audio.receiver", sizeof path);
354 auto *msg = (struct msg_receiver *) new_message(
355 sizeof(struct msg_receiver));
356 const char *dir = suffix(cmd, "volume ");
357 if (strcmp(dir, "up") == 0) {
359 } else if (strcmp(dir, "down") == 0) {
361 } else {
362 free_message((struct message *) msg, nullptr);
363 return new_response(RESPONSE_BAD_REQUEST, nullptr);
364 }
365 return send_message(root_module, path, (struct message *) msg);
366 }
367 return new_response(RESPONSE_BAD_REQUEST, "unexpected audio msg");
368}
369
374static int process_msg(struct control_state *s, fd_t client_fd, char *message, struct client *clients)
375{
376 int ret = 0;
377 struct response *resp = NULL;
378 char path[1024] = ""; // path for msg receiver (usually video)
379 char path_audio[1024] = ""; // auxiliary buffer used when we need to signalize both audio
380 // and video
381 char buf[1048];
382
383 if(prefix_matches(message, "port ")) {
384 message = suffix(message, "port ");
385 if (isdigit(message[0])) { // index is a number
386 snprintf(path, 1024, "%s[%d]", module_class_name(MODULE_CLASS_PORT), atoi(message));
387 while(isdigit(*message))
388 message++;
389 while(isspace(*message))
390 message++;
391 } else { // index is a name
392 char *port_id_name = message;
393 while (!isspace(message[0]) && message[0] != '\0')
394 message++;
395 if (message[0] != '\0') {
396 message[0] = '\0';
397 message++;
398 }
399 snprintf(path, 1024, "%s[%s]", module_class_name(MODULE_CLASS_PORT), port_id_name);
400 while (isspace(message[0]) && message[0] != '\0')
401 message++;
402 }
403
404 /* "port n compress" messages get forwarded to
405 * port[n].sender.compress, but that is wrong, because frames
406 * are now compressed before they are passed to the sender
407 * (sender compression is now always set to "none").
408 */
409 if (prefix_matches(message, "compress ")){
410 log_msg(LOG_LEVEL_ERROR, "\"port n compress\" was deprecated. Use \"port[n] compress\" instead\n");
411 return ret;
412 }
413 }
414
415 if(strcasecmp(message, "quit") == 0) {
416 return CONTROL_EXIT;
417 } else if (strcasecmp(message, "exit") == 0) {
418 exit_uv(0);
419 resp = new_response(RESPONSE_OK, NULL);
420 } else if (strcasecmp(message, "help") == 0) {
422 return ret;
423 } else if (strcasecmp(message, "noop") == 0) {
424 return ret;
425 } else if (prefix_matches(message, "stats ") || prefix_matches(message, "event ")) {
426 if (is_internal_port(client_fd)) {
427 struct client *cur = clients;
428 char *new_msg = NULL;
429
430 while (cur) {
431 if(is_internal_port(cur->fd)) { // skip local FD
432 cur = cur->next;
433 continue;
434 }
435 if (!new_msg) {
436 // append <CR><LF> again
437 new_msg = (char *) malloc(strlen(message) + 1 + 2);
438 strcpy(new_msg, message);
439 new_msg[strlen(message)] = '\r';
440 new_msg[strlen(message) + 1] = '\n';
441 new_msg[strlen(message) + 2] = '\0';
442 }
443
444 int ret = write_all(cur->fd, new_msg, strlen(new_msg));
445 if (ret != (int) strlen(new_msg)) {
446 log_msg(LOG_LEVEL_WARNING, "Cannot write stats/event!\n");
447 }
448 cur = cur->next;
449 }
450 free(new_msg);
451 return ret;
452 } else if (prefix_matches(message, "stats ")) {
453 const char *toggle = suffix(message, "stats ");
454 if (strcasecmp(toggle, "on") == 0) {
455 s->stats_on = true;
456 resp = new_response(RESPONSE_OK, NULL);
457 } else if (strcasecmp(toggle, "off") == 0) {
458 s->stats_on = false;
459 resp = new_response(RESPONSE_OK, NULL);
460 } else {
462 }
463 }
464 } else if (prefix_matches(message, "sender-port ")) {
465 struct msg_sender *msg = (struct msg_sender *)
466 new_message(sizeof(struct msg_sender));
467 struct msg_sender *msg_audio = (struct msg_sender *)
468 new_message(sizeof(struct msg_sender));
469 char *port_str = suffix(message, "sender-port ");
470
472 msg_audio->type = SENDER_MSG_CHANGE_PORT;
473
474 if (strchr(port_str, ':')) {
475 char *save_ptr, *item;
476 item = strtok_r(port_str, ":", &save_ptr);
477 msg->rx_port = atoi(item);
478 item = strtok_r(NULL, ":", &save_ptr);
479 msg->tx_port = atoi(item);
480
481 item = strtok_r(NULL, ":", &save_ptr);
482 if (item) {
483 char *item2 = strtok_r(NULL, ":", &save_ptr);
484 if (item2) { // change only if both RX and TX given
485 msg_audio->rx_port = atoi(item);
486 msg_audio->tx_port = atoi(item2);
487 } else {
488 log_msg(LOG_LEVEL_ERROR, "Not changing audio port!\n");
489 }
490 }
491 } else {
492 msg->tx_port = atoi(port_str);
493 }
494
495 if (msg_audio->rx_port == 0) {
496 msg_audio->rx_port = msg->rx_port ? msg->rx_port + 2 : 0;
497 }
498
499 if (msg_audio->tx_port == 0) {
500 msg_audio->tx_port = msg->tx_port + 2;
501 }
502
503 enum module_class path_sender[] = { MODULE_CLASS_SENDER, MODULE_CLASS_NONE };
505 memcpy(path_audio, path, sizeof(path_audio));
506 append_message_path(path, sizeof(path), path_sender);
507 append_message_path(path_audio, sizeof(path_audio), path_sender_audio);
508
509 resp =
510 send_message(s->root_module, path, (struct message *) msg);
511 struct response *resp_audio =
512 send_message(s->root_module, path_audio, (struct message *) msg_audio);
513 free_response(resp_audio);
514 } else if(prefix_matches(message, "receiver ") || prefix_matches(message, "play") ||
515 prefix_matches(message, "pause") || prefix_matches(message, "reset-ssrc")) {
516 struct msg_sender *msg =
517 (struct msg_sender *)
518 new_message(sizeof(struct msg_sender));
519 if(prefix_matches(message, "receiver ")) {
520 strncpy(msg->receiver, suffix(message, "receiver "), sizeof(msg->receiver) - 1);
522 } else if(prefix_matches(message, "reset-ssrc")) {
524 } else {
525 abort();
526 }
527
528 struct msg_sender *msg_audio = (struct msg_sender *) malloc(sizeof(struct msg_sender));
529 memcpy(msg_audio, msg, sizeof(struct msg_sender));
530
531 enum module_class path_sender[] = { MODULE_CLASS_SENDER, MODULE_CLASS_NONE };
533 memcpy(path_audio, path, sizeof(path_audio));
534 append_message_path(path, sizeof(path), path_sender);
535 append_message_path(path_audio, sizeof(path_audio), path_sender_audio);
536
537 resp =
538 send_message(s->root_module, path, (struct message *) msg);
539 struct response *resp_audio =
540 send_message(s->root_module, path_audio, (struct message *) msg_audio);
541 free_response(resp_audio);
542 } else if (prefix_matches(message, "receiver-port ")) {
543 struct msg_receiver *msg =
544 (struct msg_receiver *)
545 new_message(sizeof(struct msg_receiver));
546 struct msg_receiver *msg_audio =
547 (struct msg_receiver *)
548 new_message(sizeof(struct msg_receiver));
551
552 char *port_str = suffix(message, "receiver-port ");
553
554 msg->new_rx_port = atoi(port_str);
555 if (strchr(port_str, ':')) {
556 msg_audio->new_rx_port = atoi(strchr(port_str, ':') + 1);
557 } else {
558 msg_audio->new_rx_port = msg->new_rx_port + 2;
559 }
560
561 enum module_class path_receiver[] = { MODULE_CLASS_RECEIVER, MODULE_CLASS_NONE };
563 append_message_path(path, sizeof(path), path_receiver);
564 append_message_path(path_audio, sizeof(path_audio), path_audio_receiver);
565 resp =
566 send_message(s->root_module, path, (struct message *) msg);
567 struct response *resp_audio =
568 send_message(s->root_module, path_audio, (struct message *) msg_audio);
569 free_response(resp_audio);
570 } else if(prefix_matches(message, "fec ")) {
571 auto *msg = reinterpret_cast<struct msg_universal *>(new_message(sizeof(struct msg_universal)));
572 char *fec = suffix(message, "fec ");
573 enum tx_media_type media_type{};
574 strncpy(msg->text, MSG_UNIVERSAL_TAG_TX "fec ", sizeof(msg->text) - 1);
575
576 if(strncasecmp(fec, "audio ", 6) == 0) {
577 media_type = TX_MEDIA_AUDIO;
578 strncat(msg->text, fec + 6, sizeof(msg->text) - strlen(msg->text) - 1);
579 } else if(strncasecmp(fec, "video ", 6) == 0) {
580 media_type = TX_MEDIA_VIDEO;
581 strncat(msg->text, fec + 6, sizeof(msg->text) - strlen(msg->text) - 1);
582 } else {
583 resp = new_response(RESPONSE_NOT_FOUND, "unknown media type");
584 free(msg);
585 msg = nullptr;
586 }
587
588 if (msg) {
589 if (media_type == TX_MEDIA_VIDEO) {
591 append_message_path(path, sizeof(path),
592 path_tx);
593 } else {
595 append_message_path(path, sizeof(path),
596 path_audio_tx);
597 }
598 resp = send_message(s->root_module, path, (struct message *) msg);
599 }
600 } else if(prefix_matches(message, "compress ")) {
602 (struct msg_change_compress_data *)
604 char *compress = suffix(message, "compress ");
605
606 if(prefix_matches(compress, "param ")) {
607 compress = suffix(compress, "param ");
608 msg->what = CHANGE_PARAMS;
609 } else {
610 msg->what = CHANGE_COMPRESS;
611 }
612 strncpy(msg->config_string, compress, sizeof(msg->config_string) - 1);
613
614 if(!resp) {
616 append_message_path(path, sizeof(path), path_compress);
617 resp = send_message(s->root_module, path, (struct message *) msg);
618 }
619 } else if (prefix_matches(message, "volume ") ||
620 prefix_matches(message, "mute") ||
621 prefix_matches(message, "unmute")) {
623 } else if (prefix_matches(message, "av-delay ")) {
624 int val = atoi(suffix(message, "av-delay "));
626 resp = new_response(RESPONSE_OK, NULL);
627 } else if (prefix_matches(message, "postprocess ")) {
628 strncpy(path, "display", sizeof path);
629 struct msg_universal *msg = (struct msg_universal *) new_message(sizeof(struct msg_universal));
630 strncpy(msg->text, message, sizeof msg->text - 1);
631 msg->text[sizeof msg->text - 1] = '\0';
632 resp = send_message(s->root_module, path, (struct message *) msg);
633 } else if(strcasecmp(message, "bye") == 0) {
635 resp = new_response(RESPONSE_OK, NULL);
636 } else if(strcmp(message, "dump-tree") == 0) {
637 dump_tree(s->root_module, 0);
638 resp = new_response(RESPONSE_OK, NULL);
639 } else { // assume message in format "path message"
640 struct msg_universal *msg = (struct msg_universal *)
641 new_message(sizeof(struct msg_universal));
642
643 if (strchr(message, ' ')) {
644 memcpy(path, message, strchr(message, ' ') - message);
645 strncpy(msg->text, strchr(message, ' ') + 1, sizeof(path) - 1);
646 } else {
647 strncpy(path, message, sizeof(path) - 1); // empty message ??
648 }
649
650 resp = send_message(s->root_module, path, (struct message *) msg);
651 }
652
653 if(!resp) {
654 fprintf(stderr, "No response received!\n");
655 snprintf(buf, sizeof(buf), "(unknown path: %s)", path);
657 }
658 send_response(client_fd, resp);
659
660 return ret;
661}
662
663static void send_response(fd_t fd, struct response *resp)
664{
665 char buffer[1024];
666
667 snprintf(buffer, sizeof(buffer) - 2, "%d %s", response_get_status(resp),
669
670 if (response_get_text(resp)) {
671 strncat(buffer, " ", sizeof(buffer) - strlen(buffer) - 1 - 2);
672 strncat(buffer, response_get_text(resp), sizeof(buffer) - strlen(buffer) - 1 - 2);
673 }
674 strcat(buffer, "\r\n");
675
676 ssize_t ret = write_all(fd, buffer, strlen(buffer));
677 if (ret < 0) {
678 socket_error("Unable to write response");
679 }
680
681 free_response(resp);
682}
683
684static bool parse_msg(char *buffer, int buffer_len, /* out */ char *message, int *new_buffer_len)
685{
686 bool ret = false;
687 int i = 0;
688
689 while (i < buffer_len) {
690 if(buffer[i] == '\0' || buffer[i] == '\n' || buffer[i] == '\r') {
691 ++i;
692 } else {
693 break;
694 }
695 }
696
697 int start = i;
698
699 for( ; i < buffer_len; ++i) {
700 if(buffer[i] == '\0' || buffer[i] == '\n' || buffer[i] == '\r') {
701 memcpy(message, buffer + start, i - start);
702 message[i - start] = '\0';
703 ret = true;
704 break;
705 }
706 }
707
708 if(ret) {
709 memmove(buffer, buffer + i, buffer_len - i);
710 *new_buffer_len = buffer_len - i;
711 }
712
713 return ret;
714}
715
719static struct client *add_client(struct client *clients, fd_t fd) {
720 struct client *new_client = (struct client *)
721 malloc(sizeof(struct client));
722 new_client->fd = fd;
723 new_client->prev = NULL;
724 new_client->next = clients;
725 if (new_client->next) {
726 new_client->next->prev = new_client;
727 }
728 new_client->buff_len = 0;
729
730 return new_client;
731}
732
733static void process_messages(struct control_state *s)
734{
735 struct message *msg;
736 while((msg = check_message(&s->mod))) {
737 struct msg_universal *m = (struct msg_universal *) msg;
738 if (strcmp(m->text, "get_port") == 0) {
739 struct response *r;
740 uint16_t port = socket_get_recv_port(s->socket_fd);
741 if (port) {
742 char port_str[6];
743 snprintf(port_str, sizeof port_str, "%hu", port);
744 r = new_response(RESPONSE_OK, port_str);
745 } else {
746 r = new_response(RESPONSE_INT_SERV_ERR, "get_recv_port");
747 }
748 free_message(msg, r);
749 } else if (strstr(m->text, "execute ") == m->text) {
750 process_msg(s, 1, m->text + strlen("execute "), nullptr);
752 } else {
753 log_msg(LOG_LEVEL_WARNING, "[control] Unrecognized command: %s\n", m->text);
755 continue;
756 }
757 }
758}
759
760static void set_socket_nonblock(fd_t fd) {
761#ifdef _WIN32
762 unsigned long ul;
763 ioctlsocket(fd, FIONBIO, &ul);
764#else
765 if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
766 perror("fcntl");
767 }
768#endif
769}
770
771ADD_TO_PARAM("control-accept-global", "* control-accept-global\n"
772 " Open control socket to public network.\n");
773static void * control_thread(void *args)
774{
775 set_thread_name(__func__);
776 struct control_state *s = (struct control_state *) args;
777 struct client *clients = NULL;
778
779 assert(s->socket_fd != INVALID_SOCKET);
780 assert(s->internal_fd[0] != INVALID_SOCKET);
781
782 if(s->connection_type == CLIENT) {
783 clients = add_client(clients, s->socket_fd);
784 }
785 struct sockaddr_storage client_addr;
786 socklen_t len = sizeof client_addr;
787
788 errno = 0;
789
790 clients = add_client(clients, s->internal_fd[0]);
791
792 bool should_exit = false;
793
794 struct timeval last_report_sent;
795 const int report_interval_sec = 5;
796 gettimeofday(&last_report_sent, NULL);
797
798 while(!should_exit) {
800
801 fd_t max_fd = 0;
802 fd_set fds;
803 FD_ZERO(&fds);
804 if (s->connection_type == SERVER) {
805 FD_SET(s->socket_fd, &fds);
806 max_fd = s->socket_fd + 1;
807 }
808
809 struct client *cur = clients;
810
811 while(cur) {
812 FD_SET(cur->fd, &fds);
813 if(cur->fd + 1 > max_fd) {
814 max_fd = cur->fd + 1;
815 }
816 cur = cur->next;
817 }
818
819 struct timeval timeout;
820 timeout.tv_sec = report_interval_sec;
821 timeout.tv_usec = 0;
822
823 struct timeval *timeout_ptr = NULL;
824 if(clients->next != NULL) { // some remote client
825 timeout_ptr = &timeout;
826 }
827
828 int rc;
829
830 if ((rc = select(max_fd, &fds, NULL, NULL, timeout_ptr)) >= 1) {
831 if(s->connection_type == SERVER && FD_ISSET(s->socket_fd, &fds)) {
832 fd_t fd = accept(s->socket_fd, (struct sockaddr *) &client_addr, &len);
833 if (fd == INVALID_SOCKET) {
834 socket_error("[control socket] accept");
835 continue;
836 }
837
838 // all remote sockets are written sequentially so
839 // we don't want to block if one gets stuck
841
842 // refuse remote connections by default
843 if (!get_commandline_param("control-accept-global") &&
844 !is_addr_loopback((struct sockaddr *) &client_addr)) {
845 log_msg(LOG_LEVEL_WARNING, "[control socket] Refusing remote connection. Use \"--param control-accept-global\" to allow UG control from a remote host.\n");
846 const char *msg = "unauthorized\r\n";
847 write_all(fd, msg, strlen(msg));
848 CLOSESOCKET(fd);
849 continue;
850 }
851
852 // add to list
853 clients = add_client(clients, fd);
854 }
855
856 struct client *cur = clients;
857
858 while(cur) {
859 if(FD_ISSET(cur->fd, &fds)) {
860 ssize_t ret = PLATFORM_PIPE_READ(cur->fd, cur->buff + cur->buff_len,
861 sizeof(cur->buff) - cur->buff_len);
862 if(ret == -1) {
863 fprintf(stderr, "Error reading socket, closing!!!\n");
864 }
865 if(ret <= 0) {
866 struct client *next;
867 CLOSESOCKET(cur->fd);
868 if (cur->prev) {
869 cur->prev->next = cur->next;
870 } else {
871 clients = cur->next;
872 }
873 if (cur->next) {
874 cur->next->prev = cur->prev;
875 }
876 next = cur->next;
877 free(cur);
878 cur = next;
879 continue;
880 }
881 cur->buff_len += ret;
882 }
883 cur = cur->next;
884 }
885 } else if (rc < 0) {
886 socket_error("[control socket] select");
887 }
888
889 cur = clients;
890 while(cur) {
891 char msg[sizeof(cur->buff) + 1];
892 int cur_buffer_len;
893 while(parse_msg(cur->buff, cur->buff_len, msg, &cur_buffer_len)) {
894 cur->buff_len = cur_buffer_len;
895 int ret = process_msg(s, cur->fd, msg, clients);
896 if(ret == CONTROL_EXIT && is_internal_port(cur->fd)) {
897 should_exit = true;
898 } else if(ret == CONTROL_CLOSE_HANDLE) {
899 shutdown(cur->fd, SHUT_RDWR);
900 }
901 }
902 if(cur->buff_len == sizeof(cur->buff)) {
903 fprintf(stderr, "Socket buffer full and no delimited message. Discarding.\n");
904 cur->buff_len = 0;
905 }
906
907 cur = cur->next;
908 }
909 }
910
911 // notify clients about exit
912 struct client *cur = clients;
913 while(cur) {
914 struct client *tmp = cur;
915 if (!is_internal_port(cur->fd)) {
916 const char *msg = "event exit\r\n";
917 write_all(cur->fd, msg, strlen(msg));
918 }
919 CLOSESOCKET(cur->fd);
920 cur = cur->next;
921 free(tmp);
922 }
923
925
926 return NULL;
927}
928
929static void *stat_event_thread(void *args)
930{
931 set_thread_name(__func__);
932 struct control_state *s = (struct control_state *) args;
933
934 while (1) {
935 std::unique_lock<std::mutex> lk(s->stats_lock);
936 s->stat_event_cv.wait(lk, [s] { return s->stat_event_queue.size() > 0; });
937 string &line = s->stat_event_queue.front();
938
939 if (line.empty()) {
940 break;
941 }
942
943 int ret = write_all(s->internal_fd[1], line.c_str(), line.length());
944 s->stat_event_queue.pop();
945 if (ret <= 0) {
946 fprintf(stderr, "Cannot write stat line!\n");
947 }
948 }
949
950 return NULL;
951}
952
954{
955 if(!s) {
956 return;
957 }
958
959 module_done(&s->mod);
960
961 if(s->started) {
962 s->stats_lock.lock();
963 s->stat_event_queue.push({});
964 s->stats_lock.unlock();
965 s->stat_event_cv.notify_one();
966 s->stat_event_thread_id.join();
967
968 int ret = write_all(s->internal_fd[1], "quit\r\n", 6);
969 if (ret > 0) {
970 s->control_thread_id.join();
972 } else {
973 fprintf(stderr, "Cannot exit control thread!\n");
974 }
975 }
977 // for client, the socket has already been closed
978 // by the time of control_thread exit
980 }
981
982 delete s;
983}
984
985static void control_report_stats_event(struct control_state *s, const std::string &report_line)
986{
987 std::unique_lock<std::mutex> lk(s->stats_lock);
988
989 if (s->stat_event_queue.size() < MAX_STAT_EVENT_QUEUE) {
990 s->stat_event_queue.push(report_line);
991 } else {
992 log_msg(LOG_LEVEL_WARNING, "Cannot write stats/event - queue full!!!");
993 }
994 lk.unlock();
995 s->stat_event_cv.notify_one();
996}
997
998void control_report_stats(struct control_state *s, const std::string &report_line)
999{
1000 if (!s || !s->stats_on) {
1001 return;
1002 }
1003
1004 control_report_stats_event(s, "stats " + report_line + "\r\n");
1005}
1006
1007void control_report_event(struct control_state *s, const std::string &report_line)
1008{
1009 if (!s) {
1010 return;
1011 }
1012
1013 control_report_stats_event(s, "event " + report_line + "\r\n");
1014}
1015
1017{
1018 return s && s->stats_on;
1019}
1020
1022 return state ? state->audio_channel_report_count : 0;
1023}
1024
1025static void print_control_help() {
1026 color_printf("Control internal commands:\n"
1027 TBOLD("\texit") "\n"
1028 TBOLD("\tpause") "\n"
1029 TBOLD("\tplay") "\n"
1030 TBOLD("\treciever {pause|play}") "\n"
1031 TBOLD("\treset-ssrc") "\n"
1032 TBOLD("\t{receiver|sender}-port <XY>") "\n"
1033 TBOLD("\tfec {audio|video} <fec-string>") "\n"
1034 TBOLD("\tcompress <new-compress>") "\n"
1035 TBOLD("\tcompress param <new-compress-param>") "\n"
1036 TBOLD("\tvolume {up|down}") u8"¹\n"
1037 TBOLD("\tav-delay <ms>") u8"¹\n"
1038 TBOLD("\tmute") " - toggles receiver mute\n"
1039 TBOLD("\t[un]mute-{receiver,sender}")
1040 " - (un)mutes audio sender or receiver\n"
1041 TBOLD("\tpostprocess <new_postprocess> | flush") "\n"
1042 TBOLD("\tdump-tree")"\n");
1043 color_printf("\nOther commands can be issued directly to individual "
1044 "modules (see \"" TBOLD("dump-tree") "\"), eg.:\n"
1045 "\t" TBOLD("capture.filter mirror") "\n"
1046 "\nSometimes those modules support help (eg. \"" TBOLD("capture.filter help") "\")\n\n");
1047 color_printf(TBOLD(u8"¹") " audio commands applying to receiver\n\n");
1048}
1049
void ** state
int void * val
int void size_t * len
int color_printf(const char *format,...)
#define TBOLD(x)
Definition color_out.h:70
int fd_t
#define INVALID_SOCKET
#define CLOSESOCKET
static struct response * process_audio_message(struct module *root_module, const char *cmd)
int control_init(int port, int connection_type, struct control_state **state, struct module *root_module, int force_ip_version)
static struct client * add_client(struct client *clients, fd_t fd)
#define MAX_CLIENTS
#define is_internal_port(x)
static void new_message(struct module *m)
#define MAX_STAT_EVENT_QUEUE
static bool parse_msg(char *buffer, int buffer_len, char *message, int *new_buffer_len)
bool control_stats_enabled(struct control_state *s)
void control_done(struct control_state *s)
#define prefix_matches(x, y)
void control_start(struct control_state *s)
#define CONTROL_EXIT
static void * stat_event_thread(void *args)
#define MSG_NOSIGNAL
void * sso_val_type
static void control_report_stats_event(struct control_state *s, const std::string &report_line)
static void send_response(fd_t fd, struct response *resp)
static ssize_t write_all(fd_t fd, const void *buf, size_t count)
#define CONTROL_CLOSE_HANDLE
int control_audio_ch_report_count(struct control_state *state)
void control_report_stats(struct control_state *s, const std::string &report_line)
static int process_msg(struct control_state *s, fd_t client_fd, char *message, struct client *clients)
static void process_messages(struct control_state *s)
connection_type
@ CLIENT
@ SERVER
void control_report_event(struct control_state *s, const std::string &report_line)
#define suffix(x, y)
static void print_control_help()
static void set_socket_nonblock(fd_t fd)
static void * control_thread(void *args)
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
#define LOG_LEVEL_NOTICE
information that may be interesting
Definition debug.h:61
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
uint8_t u8
Definition dxt62tga.c:12
static void new_msg(struct module *mod)
Definition export.c:85
void module_init_default(struct module *module_data)
Definition module.c:48
void module_register(struct module *module_data, struct module *parent)
Definition module.c:82
void dump_tree(struct module *root_node, int indent)
Definition module.c:333
void module_done(struct module *module_data)
Definition module.c:93
module_class
Definition module.h:81
const char * module_class_name(enum module_class cls)
Definition module.c:178
void append_message_path(char *buf, int buflen, const enum module_class *modules)
Definition module.c:187
@ MODULE_CLASS_CONTROL
Definition module.h:91
@ MODULE_CLASS_NONE
Definition module.h:82
@ MODULE_CLASS_COMPRESS
Definition module.h:85
@ MODULE_CLASS_RECEIVER
Definition module.h:88
@ MODULE_CLASS_AUDIO
Definition module.h:90
@ MODULE_CLASS_TX
Definition module.h:89
@ MODULE_CLASS_SENDER
Definition module.h:87
@ MODULE_CLASS_PORT
Definition module.h:84
int count
Definition hd-rum.c:44
void set_audio_delay(int audio_delay)
Definition host.cpp:914
void exit_uv(int status)
Definition host.cpp:625
const char * get_commandline_param(const char *key)
Definition host.cpp:894
#define ADD_TO_PARAM(param, doc)
Definition host.h:203
@ STR_LEN
"standard" string length placeholder
Definition macros.h:134
void free_message(struct message *msg, struct response *r)
struct response * new_response(int status, const char *text)
int response_get_status(struct response *r)
struct response * send_message(struct module *root, const char *const_path, struct message *msg)
Sends message without waiting for response.
struct message * check_message(struct module *mod)
void free_response(struct response *r)
const char * response_status_to_text(int status)
const char * response_get_text(struct response *r)
@ SENDER_MSG_RESET_SSRC
Definition messaging.h:92
@ SENDER_MSG_MUTE
Definition messaging.h:87
@ SENDER_MSG_CHANGE_RECEIVER
Definition messaging.h:84
@ SENDER_MSG_UNMUTE
Definition messaging.h:88
@ SENDER_MSG_CHANGE_PORT
Definition messaging.h:85
#define RESPONSE_NOT_IMPL
Definition messaging.h:64
#define RESPONSE_OK
Definition messaging.h:57
@ CHANGE_COMPRESS
Definition messaging.h:129
@ CHANGE_PARAMS
Definition messaging.h:130
@ RECEIVER_MSG_MUTE_TOGGLE
Definition messaging.h:116
@ RECEIVER_MSG_DECREASE_VOLUME
Definition messaging.h:113
@ RECEIVER_MSG_UNMUTE
Definition messaging.h:115
@ RECEIVER_MSG_CHANGE_RX_PORT
Definition messaging.h:109
@ RECEIVER_MSG_INCREASE_VOLUME
Definition messaging.h:112
@ RECEIVER_MSG_MUTE
Definition messaging.h:114
#define RESPONSE_BAD_REQUEST
Definition messaging.h:60
#define RESPONSE_INT_SERV_ERR
Definition messaging.h:63
#define MSG_UNIVERSAL_TAG_TX
Definition messaging.h:160
#define RESPONSE_NOT_FOUND
Definition messaging.h:61
bool is_addr_loopback(struct sockaddr *sa)
Definition net.c:108
uint16_t socket_get_recv_port(int fd)
Definition net.c:200
static fd_t max_fd
Definition net_udp.c:947
int platform_pipe_init(fd_t p[2])
#define socket_error(...)
void platform_pipe_close(fd_t pipe)
#define PLATFORM_PIPE_READ
struct msghdr msg
static void start()
Definition sdp.c:202
struct client * prev
struct client * next
char buff[1024]
enum connection_type connection_type
struct module * root_module
std::mutex stats_lock
struct module mod
condition_variable stat_event_cv
thread stat_event_thread_id
queue< string > stat_event_queue
Definition fec.h:51
Definition hd-rum.c:27
enum module_class cls
Definition module.h:115
void * priv_data
Definition module.h:126
notify_t new_message
if set, notifies module that new message is in queue, receiver lock is hold during the call
Definition module.h:119
uint16_t new_rx_port
Definition messaging.h:122
enum msg_receiver_type type
Definition messaging.h:120
enum msg_sender_type type
Definition messaging.h:97
struct message m
Definition messaging.h:156
char text[8192]
Definition messaging.h:157
void set_thread_name(const char *name)
Definition thread.c:72
tx_media_type
Definition types.h:363
@ TX_MEDIA_AUDIO
Definition types.h:364
@ TX_MEDIA_VIDEO
Definition types.h:365