root/rat/trunk/main_control.c @ 4135

Revision 4135, 17.2 KB (checked in by piers, 6 years ago)

Tweaked timeout limits down to 20 secs - we may need to play with these.
Also removed a test Sleep() [windows only] call.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2 * FILE:    main_control.c
3 * PROGRAM: RAT - controller
4 * AUTHOR:  Colin Perkins / Orion Hodson
5 *
6 * This is the main program for the RAT controller.  It starts the
7 * media engine and user interface, and controls them via the mbus.
8 *
9 * Copyright (c) 1999-2001 University College London
10 * All rights reserved.
11 */
12
13#ifndef HIDE_SOURCE_STRINGS
14static const char cvsid[] =
15"$Id$";
16#endif /* HIDE_SOURCE_STRINGS */
17
18#include "config_unix.h"
19#include "config_win32.h"
20#include "debug.h"
21#include "version.h"
22#include "mbus_control.h"
23#include "crypt_random.h"
24#include "fatal_error.h"
25#include "mbus.h"
26#include "mbus_parser.h"
27#include "net_udp.h"
28#include "util.h"
29#include "process.h"
30#include "cmd_parser.h"
31
32#ifdef WIN32
33#define UI_NAME      "ratui.exe"
34#define ENGINE_NAME  "ratmedia.exe"
35#define CONTROL_NAME "rat.exe"
36#else
37#define UI_NAME      "rat-"RAT_VERSION"-ui"
38#define ENGINE_NAME  "rat-"RAT_VERSION"-media"
39#endif
40
41#define DEFAULT_RTP_PORT 5004
42
43char       *u_addr, *e_addr[2];
44pid_t       pid_ui, pid_engine;
45int         should_exit;
46int         ui_enabled = TRUE;
47int         continuing_from_stop=0;
48
49static int ttl = 127;
50
51#ifdef NEED_SNPRINTF
52static int snprintf(char *s, int buf_size, const char *format, ...)
53{
54        /* Quick hack replacement for snprintf... note that this */
55        /* doesn't check for buffer overflows, and so is open to */
56        /* many really nasty attacks!                            */
57        va_list ap;
58        int     rc;
59
60        UNUSED(buf_size);
61        va_start(ap, format);
62#ifdef WIN32
63        rc = _vsnprintf(s, buf_size, format, ap);
64#else
65        rc = vsprintf(s, format, ap);
66#endif
67        va_end(ap);
68        return rc;
69}
70#endif
71
72static int
73address_is_valid(const char *candidate)
74{
75        char *addr, *p;
76        int   i, port, okay = TRUE;
77
78        addr = xstrdup(candidate);
79
80        addr = strtok(addr, "/");
81        if (udp_addr_valid(addr) == FALSE) {
82                char *msg = "Invalid address: %s";
83                char *m   = xmalloc(strlen(candidate) + strlen(msg));
84                sprintf(m, msg, candidate);
85                usage(m);
86                xfree(m);
87                okay = FALSE;
88        }
89
90        /* rx_port then tx_port */
91        for (i = 0; i < 2; i++) {
92                p = strtok(NULL, "/");
93                if (p != NULL) {
94                        port = atoi(p);
95                        if (port < 1024 || port > 65534) {
96                                usage("Port must be > 1023 and <= 65534 (need one more for RTCP)\n");
97                                okay = FALSE;
98                        }
99                }
100        }
101        xfree(addr);
102
103        return okay;
104}
105
106static int parse_options_early(int argc, const char **argv)
107{
108        int i;
109
110        if (argc < 2) {
111                usage(NULL);
112                return FALSE;
113        }
114
115        /* Iterate over all args and pick out anything needed before
116         * launching other processes.
117         */
118        for (i = 1; i < argc; i++) {
119                if (argv[i][0] != '-' && argv[i][0] != '/') {
120                        continue;
121                } else if (tolower((int)argv[i][1]) == 'v') { //SV-XXX: NetBSD
122#ifdef WIN32
123                        MessageBox(NULL, "RAT v" RAT_VERSION, "RAT Version", MB_OK);
124#else
125                        printf("%s\n", "RAT v" RAT_VERSION);
126#endif
127                        return FALSE;
128                } else if (argv[i][1] == '?' || tolower((int)argv[i][1]) == 'h') { //SV-XXX: NetBSD
129                        usage(NULL);
130                        return FALSE;
131                } else if (argv[i][1] == 't' && i + 1 < argc) {
132                        /*
133                         * Handle ttl here because it is required before rtp
134                         * address can be sent.
135                         */
136                        i++;
137                        ttl = atoi(argv[i]);
138                        /* Hack because 128 is max ttl in rtp library
139                         * previous versions are 0-255.
140                         */
141                        if (ttl > 127) {
142                                ttl = 127;
143                        }
144                        if (ttl < 0 || ttl > 127) {
145                                usage("Usage: -t 0-127.\n");
146                                return FALSE;
147                        }
148                } else if (strcmp(argv[i], "-noui") == 0) {
149                        ui_enabled = FALSE;
150                }
151        }
152
153        /*
154         * Validate destination address.  Do it here before launching
155         * sub-processes and mbus.  This only checks the last argument
156         * for being a valid address.  In the case of layering this
157         * will not be the only one, but we have to parse all args to
158         * find this out.
159         */
160        return address_is_valid(argv[argc-1]);
161}
162
163static int
164parse_options_late(struct mbus *m, char *addr, int argc, char *argv[])
165{
166        const args_handler *a;
167        int                 i;
168
169        if (argc < 2) {
170                usage(NULL);
171                return FALSE;
172        }
173        argc -= 1; /* Skip process name */
174        argv += 1;
175
176        for (i = 0; i < argc; i++) {
177                a = cmd_args_handler(argv[i]);
178                if (a == NULL) {
179                        break;
180                }
181                if (a->cmd_proc && (argc - i) > a->argc) {
182                        a->cmd_proc(m, addr, a->argc, argv + i + 1);
183                }
184                i += a->argc;
185        }
186        return (i != argc);
187}
188
189static int
190address_count(int argc, char *argv[])
191{
192        const args_handler *a;
193        int                 i;
194
195        for (i = 0; i < argc; i++) {
196                a = cmd_args_handler(argv[i]);
197                if (a == NULL) {
198                        break;
199                }
200                i += a->argc;
201        }
202        return argc - i;
203}
204
205
206static int
207parse_addr(char *arg, char **addr, int *rx_port, int *tx_port)
208{
209        char *token;
210        int   port;
211
212        if (address_is_valid(arg) == FALSE) {
213                *addr    = NULL;
214                *rx_port = 0;
215                *tx_port = 0;
216                return FALSE;
217        }
218
219        /* Address is definitely valid... */
220
221        *addr = (char *)strtok(arg, "/");
222
223        *rx_port = DEFAULT_RTP_PORT;
224        token = strtok(NULL, "/");
225        if (token) {
226                port     = atoi(token);
227                port    &= ~1;
228                *rx_port = port;
229        }
230
231        *tx_port = *rx_port;
232        token = strtok(NULL, "/");
233        if (token) {
234                port     = atoi(token);
235                port    &= ~1;
236                *tx_port = port;
237        }
238        return TRUE;
239}
240
241static int
242parse_addresses(struct mbus *m, char *e_addr[2], int argc, char *argv[])
243{
244        char            *addr;
245        int              i, naddr, rx_port, tx_port;
246
247        if (argc < 2) {
248                usage(NULL);
249                return FALSE;
250        }
251        argc -= 1; /* Skip process name */
252        argv += 1;
253
254        naddr = address_count(argc, argv);
255        if (naddr < 1) {
256                usage(NULL);
257                return FALSE;
258        }
259
260        /* Send address to engine before parsing other args.  They need to context to be relevent */
261        for(i = 0; i < naddr; i++) {
262                if (parse_addr(argv[argc - naddr + i], &addr, &rx_port, &tx_port) == FALSE) {
263                        usage(NULL);
264                        return FALSE;
265                }
266                addr    = mbus_encode_str(addr);
267                mbus_qmsgf(m, e_addr[0], TRUE, "rtp.addr", "%s %d %d %d", addr, rx_port, tx_port, ttl);
268                if (e_addr[1] != NULL) {
269                        mbus_qmsgf(m, e_addr[1], TRUE, "rtp.addr", "\"224.1.2.3\" 1234 1234 1");
270                }
271                xfree(addr);
272        }
273        return TRUE;
274}
275
276static void mbus_err_handler(int seqnum, int reason)
277{
278        /* Something has gone wrong with the mbus transmission. At this time */
279        /* we don't try to recover, just kill off the media engine and user  */
280        /* interface processes and exit.                                     */
281
282        if (should_exit == FALSE) {
283                char msg[64];
284                sprintf(msg, "Could not send mbus message (%d:%d)\n", seqnum, reason);
285                fatal_error("RAT v" RAT_VERSION, msg);
286                kill_process(pid_ui);
287                kill_process(pid_engine);
288                abort();
289        }
290}
291
292static void terminate(struct mbus *m, char *addr, pid_t *pid)
293{
294        if (mbus_addr_valid(m, addr)) {
295                /* This is a valid address, ask that process to quit. */
296                debug_msg("Sending mbus.quit() to %s...\n", addr);
297                mbus_qmsgf(m, addr, TRUE, "mbus.quit", "");
298                do {
299                        struct timeval   timeout;
300                        mbus_send(m);
301                        mbus_heartbeat(m, 1);
302                        mbus_retransmit(m);
303                        timeout.tv_sec  = 0;
304                        timeout.tv_usec = 20000;
305                        mbus_recv(m, NULL, &timeout);
306                } while (!mbus_sent_all(m));
307                debug_msg("...done\n");
308        } else {
309                /* That process has already terminated, do nothing. */
310        }
311        *pid = 0;
312}
313
314#ifndef WIN32
315
316/* Signal Handlers */
317static void
318sigint_handler(int signal)
319{
320        UNUSED(signal);
321        debug_msg("Caught signal %d\n", signal);
322        kill_process(pid_ui);
323        kill_process(pid_engine);
324        exit(-1);
325}
326
327static void
328sigchld_handler(int signal)
329{
330        UNUSED(signal);
331        debug_msg("Caught signal SIGCHLD: %d\n", signal);
332        if (signal == SIGCONT)  {
333          continuing_from_stop=2;
334          debug_msg("Ignoring signal SIGCONT as we've probably just come back from being suspended(SIGTSTP/SIGSTOP): %d\n", signal);
335        } else {
336          if (--continuing_from_stop==-1) {
337          /* Child has terminated or stopped.  Try to shutdown nicely... */
338                should_exit = TRUE;
339                wait(NULL);     /* Buffy, the zombie slayer... */
340                return;
341          }
342          debug_msg("Ignoring signal SIGCHLD as we've probably just come back from being suspended(SIGTSTP/SIGSTOP): %d\n", signal);
343        }
344}
345
346#else
347
348/* Can use waitForMultipleObject to check liveness of child processes */
349#define NUM_CHILD_PROCS 2
350
351static void
352win32_check_children_running(void)
353{
354        HANDLE hProc[NUM_CHILD_PROCS];
355        DWORD  dwResult;
356
357        hProc[0] = (HANDLE)pid_ui;
358        hProc[1] = (HANDLE)pid_engine;
359
360        dwResult = WaitForMultipleObjects(NUM_CHILD_PROCS, hProc, FALSE, 0);
361        if (dwResult >= WAIT_OBJECT_0 && dwResult < WAIT_OBJECT_0 + NUM_CHILD_PROCS) {
362                debug_msg("Process %lu is no longer running\n",
363                        (uint32_t)hProc[dwResult - WAIT_OBJECT_0]);
364                kill_process(pid_ui);
365                kill_process(pid_engine);
366                exit(-1);
367                return;
368        }
369
370        if (dwResult >= WAIT_ABANDONED_0 && dwResult < WAIT_ABANDONED_0 + NUM_CHILD_PROCS) {
371                debug_msg("Process %lu wait abandoned\n",
372                        (uint32_t)hProc[dwResult - WAIT_ABANDONED_0]);
373                return; /* Nothing to do, process quit already */
374        }
375
376        if (dwResult == WAIT_FAILED) {
377                debug_msg("Wait failed\n");
378                return;
379        }
380
381        assert(dwResult == WAIT_TIMEOUT);
382        return;
383}
384
385LRESULT CALLBACK MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
386{
387        switch(msg) {
388        case WM_DESTROY:
389                should_exit = TRUE;
390                break;
391        }
392        return DefWindowProc(hWnd, msg, wParam, lParam);
393}
394
395static void win32_process_messages(void)
396{
397        MSG msg;
398        while (PeekMessage(&msg, NULL, 0, 0, 0)) {
399                if (GetMessage(&msg, NULL, 0, 0) == 0) {
400                        OutputDebugString("\nGot WM_QUIT\n");
401                        should_exit = TRUE;
402                        return;
403                }
404                TranslateMessage( &msg );
405                DispatchMessage( &msg );
406        }
407}
408
409extern HINSTANCE hAppPrevInstance, hAppInstance;
410
411void
412win32_create_null_window()
413{
414        WNDCLASS wc;
415        HWND     hWnd;
416
417        /* Create a non-visible window so we can process messages */
418
419        if( !hAppPrevInstance )
420        {
421                wc.lpszClassName = "RatControllerClass";
422                wc.lpfnWndProc = MainWndProc;
423                wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
424                wc.hInstance = hAppInstance;
425                wc.hIcon = NULL;
426                wc.hCursor = NULL;
427                wc.hbrBackground = (HBRUSH)( COLOR_WINDOW+1 );
428                wc.lpszMenuName = NULL;
429                wc.cbClsExtra = 0;
430                wc.cbWndExtra = 0;
431
432                RegisterClass( &wc );
433        }
434
435        hWnd = CreateWindow( "RatControllerClass",
436                "RAT",
437                WS_OVERLAPPEDWINDOW|WS_HSCROLL|WS_VSCROLL,
438                0,
439                0,
440                CW_USEDEFAULT,
441                CW_USEDEFAULT,
442                NULL,
443                NULL,
444                hAppInstance,
445                NULL
446                );
447}
448
449#endif /* WIN32 */
450
451#ifdef NDEF
452static int main_transcoder(int argc, char *argv[])
453{
454        /* This is the main function when we are the transcoder controller. The command line */
455        /* arguments are: -T -left <options for left side> -right <options for right side>.  */
456        /* where the options for each side are standard options you would give to the audio- */
457        /* tool version of rat. For example: -T -left 224.2.2.2/2222 -right -f gsm 10.0.1.4  */
458        assert(strcmp(argv[1], "-T") == 0);
459        UNUSED(argc);
460        return 0;
461}
462#endif
463
464static char *generate_token(void)
465{
466        char    *t = (char *) xmalloc(21);
467        sprintf(t, "rat-token-%08lx", lrand48());
468        return t;
469}
470
471int main(int argc, char *argv[])
472{
473        struct mbus     *m;
474        char             c_addr[60], *token_u[2], *token_e[2];
475        int              seed = (gethostid() << 8) | (getpid() & 0xff), final_iters;
476        struct timeval   timeout;
477        int              i, num_sessions = 0;
478
479#ifdef WIN32
480        win32_create_null_window(); /* Needed to listen to messages */
481#else
482        signal(SIGCONT, sigchld_handler);
483        signal(SIGCHLD, sigchld_handler);
484        signal(SIGINT, sigint_handler);
485        signal(SIGTERM, sigint_handler);
486        signal(SIGHUP, sigint_handler);
487#endif
488
489        /* We have two modes: one for operation as a transcoder, one */
490        /* when working as a normal end-system audio tool. We choose */
491        /* based on the first command line argument supplied.        */
492        if ((argc > 2) && (strcmp(argv[1], "-T") == 0)) {
493                num_sessions = 2;
494        } else {
495                num_sessions = 1;
496        }
497
498        if (parse_options_early(argc, (const char**)argv) == FALSE) {
499                return FALSE;
500        }
501
502        srand48(seed);
503        snprintf(c_addr, 60, "(media:audio module:control app:rat id:%lu)", (unsigned long) getpid());
504        debug_msg("c_addr = %s\n", c_addr);
505        m = mbus_init(mbus_control_rx, mbus_err_handler, c_addr);
506        if (m == NULL) {
507                fatal_error("RAT v" RAT_VERSION, "Could not initialize Mbus: Is multicast enabled?");
508                return FALSE;
509        }
510
511        if (ui_enabled) {
512                token_u[0] = generate_token();
513                fork_process(UI_NAME, c_addr, &pid_ui, 1, token_u);
514                if ((u_addr = mbus_rendezvous_waiting(m, "()", token_u[0], m, 20000000)) == NULL) {
515                    fatal_error("RAT v" RAT_VERSION, "MBUS Failed to rendezvous with UI - Likely firewall/VPN issue");
516                    return FALSE;
517                }
518        }
519
520        token_e[0] = generate_token();
521        token_e[1] = generate_token();
522        fork_process(ENGINE_NAME, c_addr, &pid_engine, num_sessions, token_e);
523        should_exit = FALSE;
524        for (i = 0; i < num_sessions; i++) {
525                debug_msg("Waiting for %s from media engine...\n", token_e[i]);
526                if ((e_addr[i] = mbus_rendezvous_waiting(m, "()", token_e[i], m, 20000000)) == NULL ) {
527                    fatal_error("RAT v" RAT_VERSION, "Failed to rendezvous with UI - Likely firewall/VPN issue");
528                    return FALSE;
529                }
530                debug_msg("...got it (%s)\n",e_addr[i]);
531        }
532
533        if (parse_addresses(m, e_addr, argc, argv) == TRUE) {
534                char    *peer;
535
536                if (ui_enabled) {
537                    if ((peer = mbus_rendezvous_go(m, token_u[0], (void *) m, 20000000)) == NULL) {
538                               fatal_error("RAT v" RAT_VERSION, "Failed to rendezvous with UI - Likely firewall/VPN issue");
539                               return FALSE;
540                    }
541                    debug_msg("User interface is %s\n", peer);
542                }
543                for (i = 0; i < num_sessions; i++) {
544                    if ((peer = mbus_rendezvous_go(m, token_e[i], (void *) m, 20000000)) == NULL) {
545                               fatal_error("RAT v" RAT_VERSION, "Failed to rendezvous with UI - Likely firewall/VPN issue");
546                               return FALSE;
547                    }
548                    debug_msg("Media engine %d is %s\n", i, peer);
549                }
550                debug_msg("Parsing options\n");
551                for (i = 0; i < num_sessions; i++) {
552                        parse_options_late(m, e_addr[i], argc, argv);
553                }
554                debug_msg("Entering main loop\n");
555                final_iters = 25;
556                while (final_iters > 0) {
557                        mbus_send(m);
558                        mbus_heartbeat(m, 1);
559                        mbus_retransmit(m);
560                        timeout.tv_sec  = 0;
561                        timeout.tv_usec = 20000;
562#ifdef WIN32
563                        win32_check_children_running();
564                        win32_process_messages();
565#endif
566                        mbus_recv(m, NULL, &timeout);
567                        if (should_exit) {
568                                final_iters--;
569                        }
570                }
571                if (ui_enabled) {
572                        terminate(m, u_addr, &pid_ui);
573                }
574                for (i = 0; i < num_sessions; i++) {
575                        terminate(m, e_addr[i], &pid_engine);
576                }
577        }
578
579        if (ui_enabled) {
580                kill_process(pid_ui);
581        }
582        kill_process(pid_engine);
583
584#ifdef WIN32
585        WSACleanup();
586#endif
587        if (ui_enabled) xfree(token_u[0]);
588        xfree(token_e[0]);
589        xfree(token_e[1]);
590        debug_msg("Controller exit\n");
591        return 0;
592}
Note: See TracBrowser for help on using the browser.