root/rat/trunk/main_control.c @ 4213

Revision 4213, 17.5 KB (checked in by turam, 6 years ago)

Accept -X arguments on command line, and pass to underlying apps and into tcl interpreter

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