domain-control: added domain-control plugin (modified decision-proto).
[profile/ivi/murphy.git] / src / plugins / decision-proto / test-client.c
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <netdb.h>
6 #include <signal.h>
7 #include <sys/types.h>
8 #include <sys/socket.h>
9
10 #define _GNU_SOURCE
11 #include <getopt.h>
12
13 #include <readline/readline.h>
14 #include <readline/history.h>
15
16 #include <murphy/common.h>
17 #include "client.h"
18
19 #define DEFAULT_PROMPT "test-pep> "
20
21
22 /*
23  * client context
24  */
25
26 typedef struct {
27     const char      *addrstr;            /* server address */
28     int              zone;               /* run in zone control mode */
29     int              verbose;            /* verbose mode */
30
31     mrp_mainloop_t  *ml;                 /* murphy mainloop */
32     void            *pep;                /* enforcement point client */
33     int              fd;                 /* fd for terminal input */
34     mrp_io_watch_t  *iow;                /* I/O watch for terminal input */
35 } client_t;
36
37
38 #define NVALUE 512
39
40
41 /*
42  * device and stream definitions
43  */
44
45 #define NDEVICE        (MRP_ARRAY_SIZE(devices) - 1)
46 #define DEVICE_NCOLUMN 4
47
48 typedef struct {
49     const char *name;
50     const char *type;
51     int         public;
52     int         available;
53 } device_t;
54
55 static device_t devices[] = {
56     { "builtin-speaker" , "speaker"  , TRUE , TRUE  },
57     { "builtin-earpiece", "speaker"  , FALSE, TRUE  },
58     { "usb-speaker"     , "speaker"  , TRUE , FALSE },
59     { "a2dp-speaker"    , "speaker"  , TRUE , FALSE },
60     { "wired-headset"   , "headset"  , FALSE, FALSE },
61     { "usb-headphone"   , "headphone", FALSE, FALSE },
62     { "a2dp-headphone"  , "headphone", FALSE, FALSE },
63     { "sco-headset"     , "headset"  , FALSE, FALSE },
64     { NULL              , NULL       , FALSE, FALSE }
65 };
66
67 #define NSTREAM        (MRP_ARRAY_SIZE(streams) - 1)
68 #define STREAM_NCOLUMN 4
69
70 typedef struct {
71     const char *name;
72     const char *role;
73     pid_t       owner;
74     int         playing;
75 } stream_t;
76
77 static stream_t streams[] = {
78     { "player1", "player"   , 1234, FALSE },
79     { "player2", "player"   , 4321, FALSE },
80     { "navit"  , "navigator", 5432, FALSE },
81     { "phone"  , "call"     , 6666, FALSE },
82     { NULL     , NULL       , 0   , FALSE }
83 };
84
85
86 /*
87  * device and stream descriptors
88  */
89
90 MRP_PEP_TABLE_COLUMNS(device_columns,
91                       MRP_PEP_STRING ("name", 32 , TRUE ),
92                       MRP_PEP_STRING ("type", 32 , FALSE),
93                       MRP_PEP_INTEGER("public"   , FALSE),
94                       MRP_PEP_INTEGER("available", FALSE));
95
96 MRP_PEP_TABLE_COLUMNS(stream_columns,
97                       MRP_PEP_STRING  ("name", 32, TRUE ),
98                       MRP_PEP_STRING  ("role", 32, FALSE),
99                       MRP_PEP_UNSIGNED("owner"   , FALSE),
100                       MRP_PEP_INTEGER ("playing" , FALSE));
101
102 MRP_PEP_TABLES(media_tables,
103                MRP_PEP_TABLE("devices", device_columns),
104                MRP_PEP_TABLE("streams", stream_columns));
105
106
107 /*
108  * zone and call definitions
109  */
110
111 #define NZONE        (MRP_ARRAY_SIZE(zones) - 1)
112 #define ZONE_NCOLUMN 3
113
114 typedef struct {
115     const char *name;
116     int         occupied;
117     int         active;
118 } zone_t;
119
120 static zone_t zones[] = {
121     { "driver"     , TRUE , FALSE },
122     { "fearer"     , FALSE, TRUE  },
123     { "back-left"  , TRUE , FALSE },
124     { "back-center", FALSE, FALSE },
125     { "back-right" , TRUE , TRUE  },
126     { NULL         , FALSE, FALSE }
127 };
128
129
130 #define NCALL        (MRP_ARRAY_SIZE(calls) - 1)
131 #define CALL_NCOLUMN 3
132
133 typedef struct {
134     int         id;
135     const char *state;
136     const char *modem;
137 } call_t;
138
139 static call_t calls[] = {
140     { 1, "active"  , "modem1" },
141     { 2, "ringing" , "modem1" },
142     { 3, "held"    , "modem2" },
143     { 4, "alerting", "modem2" },
144     { 0, NULL      , NULL     }
145 };
146
147
148 /*
149  * zone and call descriptors
150  */
151
152 MRP_PEP_TABLE_COLUMNS(zone_columns,
153                       MRP_PEP_STRING ("name", 32, TRUE),
154                       MRP_PEP_INTEGER("occupied", FALSE),
155                       MRP_PEP_INTEGER("active"  , FALSE));
156
157 MRP_PEP_TABLE_COLUMNS(call_columns,
158                       MRP_PEP_INTEGER("id"       , TRUE ),
159                       MRP_PEP_STRING ("state", 32, FALSE),
160                       MRP_PEP_STRING ("modem", 32, FALSE));
161
162 MRP_PEP_TABLES(zone_tables,
163                MRP_PEP_TABLE("zones", zone_columns),
164                MRP_PEP_TABLE("calls", call_columns));
165
166
167
168 mrp_pep_table_t *exports;
169 int              nexport;
170 mrp_pep_table_t *imports;
171 int              nimport;
172
173
174 static client_t *client;
175
176
177
178 static void fatal_msg(int error, const char *format, ...);
179 static void error_msg(const char *format, ...);
180 static void info_msg(const char *format, ...);
181
182 static void terminal_input_cb(char *input);
183
184 static void export_data(client_t *c);
185
186
187 static void plug_device(client_t *c, const char *name, int plug)
188 {
189     device_t *d;
190     int       changed;
191
192     if (c->zone) {
193         error_msg("cannot plug/unplug, client is in zone mode");
194         return;
195     }
196
197     changed = FALSE;
198
199     for (d = devices; d->name != NULL; d++) {
200         if (!strcmp(d->name, name)) {
201             changed = plug ^ d->available;
202             d->available = plug;
203             break;
204         }
205     }
206
207     if (changed) {
208         info_msg("device '%s' is now %splugged", d->name, plug ? "" : "un");
209         export_data(c);
210     }
211 }
212
213
214 static void list_devices(void)
215 {
216     device_t *d;
217     int       n;
218
219     for (d = devices, n = 0; d->name != NULL; d++, n++) {
220         info_msg("device '%s': (%s, %s), %s",
221                  d->name, d->type, d->public ? "public" : "private",
222                  d->available ? "available" : "currently unplugged");
223     }
224
225     if (n == 0)
226         info_msg("devices: none");
227 }
228
229
230 static void play_stream(client_t *c, const char *name, int play)
231 {
232     stream_t *s;
233     int       changed;
234
235     if (c->zone) {
236         error_msg("cannot control streams, client is in zone mode");
237         return;
238     }
239
240     changed = FALSE;
241
242     for (s = streams; s->name != NULL; s++) {
243         if (!strcmp(s->name, name)) {
244             changed = play ^ s->playing;
245             s->playing = play;
246             break;
247         }
248     }
249
250     if (changed) {
251         info_msg("stream '%s' is now %s", s->name, play ? "playing":"stopped");
252         export_data(c);
253     }
254 }
255
256
257 static void list_streams(void)
258 {
259     stream_t *s;
260     int       n;
261
262     for (s = streams, n = 0; s->name != NULL; s++, n++) {
263         info_msg("stream '%s': role %s, owner %u, currently %splaying",
264                  s->name, s->role, s->owner, s->playing ? "" : "not ");
265     }
266
267     if (n == 0)
268         info_msg("streams: none");
269 }
270
271
272 static void set_zone_state(client_t *c, char *config)
273 {
274     zone_t *z;
275     int     occupied, active, changed, len;
276     char    name[256], *end;
277
278     if (!c->zone) {
279         error_msg("cannot control zones, client is not in zone mode");
280         return;
281     }
282
283     while (*config == ' ' || *config == '\t')
284         config++;
285
286     end = strchr(config, ' ');
287     if (end == NULL)
288         return;
289
290     len = end - config;
291     strncpy(name, config, len);
292     name[len] = '\0';
293
294     config = end + 1;
295     while (*config == ' ' || *config == '\t')
296         config++;
297
298     occupied = FALSE;
299     active   = FALSE;
300     changed  = FALSE;
301
302     if (strstr(config, "occupied"))
303         occupied = TRUE;
304     if (strstr(config, "active"))
305         active = TRUE;
306
307     for (z = zones; z->name != NULL; z++) {
308         if (!strcmp(z->name, name)) {
309             changed     = (active & z->active) | (occupied ^ z->occupied);
310             z->active   = active;
311             z->occupied = occupied;
312             break;
313         }
314     }
315
316     if (changed) {
317         info_msg("zone '%s' is now %s and %s", z->name,
318                  z->occupied ? "occupied" : "free",
319                  z->active   ? "actvie"   : "idle");
320         export_data(c);
321     }
322 }
323
324
325 static void list_zones(void)
326 {
327     zone_t *z;
328     int     n;
329
330     for (z = zones, n = 0; z->name != NULL; z++, n++) {
331         info_msg("zone '%s' is now %s and %s", z->name,
332                  z->occupied ? "occupied" : "free",
333                  z->active   ? "actvie"   : "idle");
334     }
335
336     if (n == 0)
337         info_msg("zones: none");
338 }
339
340
341 static void set_call_state(client_t *c, const char *config)
342 {
343     call_t *call;
344     char   idstr[64], *state, *end;
345     int     id, changed, len;
346
347     if (!c->zone) {
348         error_msg("cannot control calls, client is not in zone mode");
349         return;
350     }
351
352     while (*config == ' ' || *config == '\t')
353         config++;
354
355     end = strchr(config, ' ');
356     if (end == NULL)
357         return;
358
359     len = end - config;
360     strncpy(idstr, config, len);
361     idstr[len] = '\0';
362
363     config = end + 1;
364     while (*config == ' ' || *config == '\t')
365         config++;
366     state = (char *)config;
367
368     id = strtoul(idstr, &end, 10);
369
370     if (end && *end) {
371         error_msg("invalid call id '%s'", idstr);
372         return;
373     }
374
375     for (call = calls; call->id > 0; call++) {
376         if (call->id == id) {
377             if (strcmp(call->state, state)) {
378                 mrp_free((char *)call->state);
379                 call->state = mrp_strdup(state);
380                 changed     = TRUE;
381                 break;
382             }
383         }
384     }
385
386     if (changed) {
387         info_msg("call #%d is now %s", call->id, call->state);
388         export_data(c);
389     }
390 }
391
392
393 static void list_calls(void)
394 {
395     call_t *c;
396     int     n;
397
398     for (c = calls, n = 0; c->id > 0; c++, n++) {
399         info_msg("call #%d: %s (on modem %s)", c->id, c->state, c->modem);
400     }
401
402     if (n == 0)
403         info_msg("calls: none");
404 }
405
406
407 static void reset_devices(void)
408 {
409     mrp_clear(&devices);
410 }
411
412
413 void update_devices(mrp_pep_data_t *data)
414 {
415     device_t        *d;
416     mrp_pep_value_t *v;
417     int              i;
418
419     if (data->ncolumn != DEVICE_NCOLUMN) {
420         error_msg("incorrect number of columns (%d) in device update",
421                   data->ncolumn);
422         return;
423     }
424
425     if (data->nrow > (int)NDEVICE) {
426         error_msg("too many rows (%d) in device update", data->nrow);
427         return;
428     }
429
430     if (data->nrow == 0)
431         reset_devices();
432     else {
433         v = data->columns;
434         d = devices;
435
436         for (i = 0; i < data->nrow; i++) {
437             mrp_free((char *)d->name);
438             mrp_free((char *)d->type);
439
440             d->name      = mrp_strdup(v[0].str);
441             d->type      = mrp_strdup(v[1].str);
442             d->public    = v[2].s32;
443             d->available = v[3].s32;
444
445             v += 4;
446             d += 1;
447         }
448     }
449
450     list_devices();
451 }
452
453
454 static void reset_streams(void)
455 {
456     mrp_clear(&streams);
457 }
458
459
460 void update_streams(mrp_pep_data_t *data)
461 {
462     stream_t        *s;
463     mrp_pep_value_t *v;
464     int              i;
465
466     if (data->ncolumn != STREAM_NCOLUMN) {
467         error_msg("incorrect number of columns (%d) in stream update",
468                   data->ncolumn);
469         return;
470     }
471
472     if (data->nrow > (int)NSTREAM) {
473         error_msg("too many rows (%d) in stream update", data->nrow);
474         return;
475     }
476
477     if (data->nrow == 0)
478         reset_streams();
479     else {
480         v = data->columns;
481         s = streams;
482
483         for (i = 0; i < data->nrow; i++) {
484             mrp_free((char *)s->name);
485             mrp_free((char *)s->role);
486
487             s->name    = mrp_strdup(v[0].str);
488             s->role    = mrp_strdup(v[1].str);
489             s->owner   = v[2].u32;
490             s->playing = v[3].s32;
491
492             v += 4;
493             s += 1;
494         }
495     }
496
497     list_streams();
498 }
499
500 static void reset_zones(void)
501 {
502     mrp_clear(&zones);
503 }
504
505
506 void update_zones(mrp_pep_data_t *data)
507 {
508     zone_t          *z;
509     mrp_pep_value_t *v;
510     int              i;
511
512     if (data->ncolumn != ZONE_NCOLUMN) {
513         error_msg("incorrect number of columns (%d) in zone update",
514                   data->ncolumn);
515         return;
516     }
517
518     if (data->nrow > (int)NZONE) {
519         error_msg("too many rows (%d) in zone update", data->nrow);
520         return;
521     }
522
523     if (data->nrow == 0)
524         reset_zones();
525     else {
526
527         v = data->columns;
528         z = zones;
529
530         for (i = 0; i < data->nrow; i++) {
531             mrp_free((char *)z->name);
532
533             z->name     = mrp_strdup(v[0].str);
534             z->occupied = v[1].s32;
535             z->active   = v[2].s32;
536
537             v += 3;
538             z += 1;
539         }
540     }
541
542     list_zones();
543 }
544
545
546 static void reset_calls(void)
547 {
548     mrp_clear(&calls);
549 }
550
551
552 void update_calls(mrp_pep_data_t *data)
553 {
554     call_t          *c;
555     mrp_pep_value_t *v;
556     int              i;
557
558     if (data->ncolumn != CALL_NCOLUMN) {
559         error_msg("incorrect number of columns (%d) in call update.",
560                data->ncolumn);
561         return;
562     }
563
564     if (data->nrow > (int)NCALL) {
565         error_msg("too many rows (%d) in call update", data->nrow);
566         return;
567     }
568
569     if (data->nrow == 0)
570         reset_calls();
571     else {
572         v = data->columns;
573         c = calls;
574
575         for (i = 0; i < data->nrow; i++) {
576             mrp_free((char *)c->state);
577             mrp_free((char *)c->modem);
578
579             c->id    = v[0].s32;
580             c->state = mrp_strdup(v[1].str);
581             c->modem = mrp_strdup(v[2].str);
582
583             v += 3;
584             c += 1;
585         }
586     }
587
588     list_calls();
589 }
590
591
592 void update_imports(client_t *c, mrp_pep_data_t *data, int ntable)
593 {
594     int i;
595
596     for (i = 0; i < ntable; i++) {
597         if (c->zone) {
598             if (data[i].id == 0)
599                 update_devices(data + i);
600             else
601                 update_streams(data + i);
602         }
603         else {
604             if (data[i].id == 0)
605                 update_zones(data + i);
606             else
607                 update_calls(data + i);
608         }
609     }
610 }
611
612
613
614 static void terminal_prompt_erase(void)
615 {
616     int n = strlen(DEFAULT_PROMPT);
617
618     printf("\r");
619     while (n-- > 0)
620         printf(" ");
621     printf("\r");
622 }
623
624
625 static void terminal_prompt_display(void)
626 {
627     rl_callback_handler_remove();
628     rl_callback_handler_install(DEFAULT_PROMPT, terminal_input_cb);
629 }
630
631
632 static void show_help(void)
633 {
634 #define P info_msg
635
636     P("Available commands:");
637     P("  help                                  show this help");
638     P("  list                                  list all data");
639     P("  list {devices|streams|zones|calls}    list the requested data");
640     P("  plug <device>                         update <device> as plugged");
641     P("  unplug <device>                       update <device> as unplugged");
642     P("  play <stream>                         update <stream> as playing");
643     P("  stop <stream>                         update <stream> as stopped");
644     P("  call <call> <state>                   update state of <call>");
645     P("  zone <zone> [occupied,[active]]       update state of <zone>");
646
647 #undef P
648 }
649
650
651 static void terminal_process_input(char *input)
652 {
653     int len;
654
655     add_history(input);
656
657     if (input == NULL || !strcmp(input, "exit")) {
658         terminal_prompt_erase();
659         exit(0);
660     }
661     else if (!strcmp(input, "help")) {
662         show_help();
663     }
664     else if (!strcmp(input, "list")) {
665         list_devices();
666         list_streams();
667         list_zones();
668         list_calls();
669     }
670     else if (!strcmp(input, "list devices"))
671         list_devices();
672     else if (!strcmp(input, "list streams"))
673         list_streams();
674     else if (!strcmp(input, "list zones"))
675         list_zones();
676     else if (!strcmp(input, "list calls"))
677         list_calls();
678     else if (!strncmp(input, "plug "  , len=sizeof("plug ")   - 1) ||
679              !strncmp(input, "unplug ", len=sizeof("unplug ") - 1)) {
680         plug_device(client, input + len, *input == 'p');
681     }
682     else if (!strncmp(input, "play "  , len=sizeof("play ")   - 1) ||
683              !strncmp(input, "stop ", len=sizeof("stop ") - 1)) {
684         play_stream(client, input + len, *input == 'p');
685     }
686     else if (!strncmp(input, "call "  , len=sizeof("call ")   - 1)) {
687         set_call_state(client, input + len);
688     }
689     else if (!strncmp(input, "zone "  , len=sizeof("zone ")   - 1)) {
690         set_zone_state(client, input + len);
691     }
692 }
693
694
695 static void terminal_input_cb(char *input)
696 {
697     terminal_process_input(input);
698     free(input);
699 }
700
701
702 static void terminal_cb(mrp_mainloop_t *ml, mrp_io_watch_t *w, int fd,
703                         mrp_io_event_t events, void *user_data)
704 {
705     MRP_UNUSED(w);
706     MRP_UNUSED(fd);
707     MRP_UNUSED(user_data);
708
709     if (events & MRP_IO_EVENT_IN)
710         rl_callback_read_char();
711
712     if (events & MRP_IO_EVENT_HUP)
713         mrp_mainloop_quit(ml, 0);
714 }
715
716
717 static void terminal_setup(client_t *c)
718 {
719     mrp_io_event_t events;
720
721     c->fd  = fileno(stdin);
722     events = MRP_IO_EVENT_IN | MRP_IO_EVENT_HUP;
723     c->iow = mrp_add_io_watch(c->ml, c->fd, events, terminal_cb, c);
724
725     if (c->iow == NULL)
726         fatal_msg(1, "Failed to create terminal input I/O watch.");
727     else
728         terminal_prompt_display();
729 }
730
731
732 static void terminal_cleanup(client_t *c)
733 {
734     mrp_del_io_watch(c->iow);
735     c->iow = NULL;
736
737     rl_callback_handler_remove();
738 }
739
740
741 static void fatal_msg(int error, const char *format, ...)
742 {
743     va_list ap;
744
745     terminal_prompt_erase();
746
747     fprintf(stderr, "fatal error: ");
748     va_start(ap, format);
749     vfprintf(stderr, format, ap);
750     va_end(ap);
751     fprintf(stderr, "\n");
752     fflush(stderr);
753
754     exit(error);
755 }
756
757
758 static void error_msg(const char *format, ...)
759 {
760     va_list ap;
761
762     terminal_prompt_erase();
763
764     fprintf(stderr, "error: ");
765     va_start(ap, format);
766     vfprintf(stderr, format, ap);
767     va_end(ap);
768     fprintf(stderr, "\n");
769     fflush(stderr);
770
771     terminal_prompt_display();
772 }
773
774
775 static void info_msg(const char *format, ...)
776 {
777     va_list ap;
778
779     terminal_prompt_erase();
780
781     va_start(ap, format);
782     vfprintf(stdout, format, ap);
783     va_end(ap);
784     fprintf(stdout, "\n");
785     fflush(stdout);
786
787     terminal_prompt_display();
788 }
789
790
791 static void signal_handler(mrp_mainloop_t *ml, mrp_sighandler_t *h,
792                            int signum, void *user_data)
793 {
794     MRP_UNUSED(h);
795     MRP_UNUSED(user_data);
796
797     switch (signum) {
798     case SIGINT:
799         info_msg("Got SIGINT, stopping...");
800         mrp_mainloop_quit(ml, 0);
801         break;
802     }
803 }
804
805
806 static void connect_notify(mrp_pep_t *pep, int connected, int errcode,
807                            const char *errmsg, void *user_data)
808 {
809     MRP_UNUSED(pep);
810     MRP_UNUSED(user_data);
811
812     if (connected) {
813         info_msg("Successfully registered to server.");
814         export_data(client);
815     }
816     else
817         error_msg("No connection to server (%d: %s).", errcode, errmsg);
818 }
819
820
821 static void data_notify(mrp_pep_t *pep, mrp_pep_data_t *tables,
822                         int ntable, void *user_data)
823 {
824     client_t *client = (client_t *)user_data;
825
826     MRP_UNUSED(pep);
827
828     update_imports(client, tables, ntable);
829 }
830
831
832 static void export_notify(mrp_pep_t *pep, int errcode, const char *errmsg,
833                           void *user_data)
834 {
835     MRP_UNUSED(pep);
836     MRP_UNUSED(user_data);
837
838     if (errcode != 0) {
839         error_msg("Data set request failed (%d: %s).", errcode, errmsg);
840     }
841 }
842
843
844 static void export_data(client_t *c)
845 {
846     mrp_pep_value_t values[NVALUE], *col, *val;
847     mrp_pep_data_t  tables[2];
848     int             i;
849
850     val = values;
851     col = val;
852
853     if (!c->zone) {
854         tables[0].id      = 0;
855         tables[0].nrow    = NDEVICE;
856         tables[0].columns = col;
857
858         for (i = 0; i < (int)NDEVICE; i++) {
859             col[0].str = devices[i].name;
860             col[1].str = devices[i].type;
861             col[2].s32 = devices[i].public;
862             col[3].s32 = devices[i].available;
863             col += 4;
864         }
865
866         tables[1].id      = 1;
867         tables[1].nrow    = NSTREAM;
868         tables[1].columns = col;
869
870         for (i = 0; i < (int)NSTREAM; i++) {
871             col[0].str = streams[i].name;
872             col[1].str = streams[i].role;
873             col[2].u32 = streams[i].owner;
874             col[3].s32 = streams[i].playing;
875             col += 4;
876         }
877     }
878     else {
879         tables[0].id      = 0;
880         tables[0].nrow    = NZONE;
881         tables[0].columns = col;
882
883         for (i = 0; i < (int)NZONE; i++) {
884             col[0].str = zones[i].name;
885             col[1].s32 = zones[i].occupied;
886             col[2].s32 = zones[i].active;
887             col += 3;
888         }
889
890         tables[1].id      = 1;
891         tables[1].nrow    = NCALL;
892         tables[1].columns = col;
893
894         for (i = 0; i < (int)NCALL; i++) {
895             col[0].s32 = calls[i].id;
896             col[1].str = calls[i].state;
897             col[2].str = calls[i].modem;
898             col += 3;
899         }
900     }
901
902     if (!mrp_pep_set_data(c->pep, tables, MRP_ARRAY_SIZE(tables),
903                           export_notify, c))
904         error_msg("Failed to send data set request to server.");
905 }
906
907
908 static void client_setup(client_t *c)
909 {
910     mrp_mainloop_t *ml;
911     mrp_pep_t      *pep;
912
913     ml = mrp_mainloop_create();
914
915     if (ml != NULL) {
916         if (!c->zone) {
917             exports = media_tables;
918             nexport = MRP_ARRAY_SIZE(media_tables);
919             imports = zone_tables;
920             nimport = MRP_ARRAY_SIZE(zone_tables);
921         }
922         else {
923             exports = zone_tables;
924             nexport = MRP_ARRAY_SIZE(zone_tables);
925             imports = media_tables;
926             nimport = MRP_ARRAY_SIZE(media_tables);
927         }
928
929         pep = mrp_pep_create(c->zone ? "zone-pep" : "media-pep", ml,
930                              exports, nexport, imports, nimport,
931                              connect_notify, data_notify, c);
932
933         if (pep != NULL) {
934             c->ml  = ml;
935             c->pep = pep;
936
937             mrp_add_sighandler(ml, SIGINT, signal_handler, c);
938
939             if (c->zone) {
940                 zone_t *z;
941                 call_t *call;
942
943                 for (z = zones; z->name != NULL; z++) {
944                     z->name = mrp_strdup(z->name);
945                 }
946
947                 for (call = calls; call->id > 0; call++) {
948                     call->state = mrp_strdup(call->state);
949                     call->modem = mrp_strdup(call->modem);
950                 }
951
952                 reset_devices();
953                 reset_streams();
954             }
955             else {
956                 device_t *d;
957                 stream_t *s;
958
959                 for (d = devices; d->name != NULL; d++) {
960                     d->name = mrp_strdup(d->name);
961                     d->type = mrp_strdup(d->type);
962                 }
963
964                 for (s = streams; s->name != NULL; s++) {
965                     s->name = mrp_strdup(s->name);
966                     s->role = mrp_strdup(s->role);
967                 }
968
969                 reset_zones();
970                 reset_calls();
971             }
972         }
973         else
974             fatal_msg(1, "Failed to create enforcement point.");
975     }
976     else
977         fatal_msg(1, "Failed to create mainloop.");
978 }
979
980
981 static void client_cleanup(client_t *c)
982 {
983     mrp_mainloop_destroy(c->ml);
984     mrp_pep_destroy(c->pep);
985
986     c->ml  = NULL;
987     c->pep = NULL;
988 }
989
990
991 static void client_run(client_t *c)
992 {
993     if (mrp_pep_connect(c->pep, c->addrstr))
994         info_msg("Connected to server at %s.", c->addrstr);
995     else
996         error_msg("Failed to connect to server at %s.", c->addrstr);
997
998     mrp_mainloop_run(c->ml);
999 }
1000
1001
1002 static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
1003 {
1004     va_list ap;
1005
1006     if (fmt && *fmt) {
1007         va_start(ap, fmt);
1008         vprintf(fmt, ap);
1009         va_end(ap);
1010     }
1011
1012     printf("usage: %s [options]\n\n"
1013            "The possible options are:\n"
1014            "  -s, --server <address>     connect to murphy at given address\n"
1015            "  -z, --zone                 run as zone controller\n"
1016            "  -v, --verbose              run in verbose mode\n"
1017            "  -h, --help                 show this help on usage\n",
1018            argv0);
1019
1020     if (exit_code < 0)
1021         return;
1022     else
1023         exit(exit_code);
1024 }
1025
1026
1027 static void client_set_defaults(client_t *c)
1028 {
1029     mrp_clear(c);
1030     c->addrstr = MRP_DEFAULT_PEP_ADDRESS;
1031     c->zone    = FALSE;
1032     c->verbose = FALSE;
1033 }
1034
1035
1036 int parse_cmdline(client_t *c, int argc, char **argv)
1037 {
1038 #   define OPTIONS "vzhs:"
1039     struct option options[] = {
1040         { "server"    , required_argument, NULL, 's' },
1041         { "zone"      , no_argument      , NULL, 'z' },
1042         { "verbose"   , optional_argument, NULL, 'v' },
1043         { "help"      , no_argument      , NULL, 'h' },
1044         { NULL, 0, NULL, 0 }
1045     };
1046
1047     int opt;
1048
1049     client_set_defaults(c);
1050
1051     while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
1052         switch (opt) {
1053         case 'z':
1054             c->zone = TRUE;
1055             break;
1056
1057         case 'v':
1058             c->verbose = TRUE;
1059             break;
1060
1061         case 'a':
1062             c->addrstr = optarg;
1063             break;
1064
1065         case 'h':
1066             print_usage(argv[0], -1, "");
1067             exit(0);
1068             break;
1069
1070         default:
1071             print_usage(argv[0], EINVAL, "invalid option '%c'", opt);
1072         }
1073     }
1074
1075     return TRUE;
1076 }
1077
1078
1079 int main(int argc, char *argv[])
1080 {
1081     client_t  c;
1082
1083     client_set_defaults(&c);
1084     parse_cmdline(&c, argc, argv);
1085
1086     client_setup(&c);
1087     terminal_setup(&c);
1088
1089     client = &c;
1090     client_run(&c);
1091
1092     terminal_cleanup(&c);
1093     client_cleanup(&c);
1094
1095     return 0;
1096 }