Add new option --udp-lock-timeout.
[dvblast.git] / dvblastctl.c
blobccdca6dfe0f1a796cf5b7d8fe683a842f1fc716c
1 /*****************************************************************************
2 * dvblastctl.c
3 *****************************************************************************
4 * Copyright (C) 2008 VideoLAN
6 * Authors: Christophe Massiot <massiot@via.ecp.fr>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
23 #include <stdlib.h>
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include <unistd.h>
27 #include <stdint.h>
28 #include <stdbool.h>
29 #include <string.h>
30 #include <inttypes.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/param.h>
34 #include <sys/socket.h>
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
37 #include <sys/un.h>
38 #include <errno.h>
39 #include <getopt.h>
41 #include <iconv.h>
43 #include <bitstream/mpeg/psi.h>
44 #include <bitstream/dvb/si.h>
45 #include <bitstream/dvb/si_print.h>
46 #include <bitstream/mpeg/psi_print.h>
48 #include "dvblast.h"
49 #include "en50221.h"
50 #include "comm.h"
52 int i_verbose = 3;
53 int i_syslog = 0;
54 unsigned int i_timeout_seconds = 15;
56 print_type_t i_print_type = PRINT_TEXT;
57 mtime_t now;
59 int i_fd = -1;
60 char psz_client_socket[PATH_MAX] = {0};
62 static iconv_t iconv_handle = (iconv_t)-1;
64 static void clean_client_socket() {
65 if ( i_fd > -1 )
67 close( i_fd );
68 i_fd = -1;
70 if ( psz_client_socket[0] )
72 unlink( psz_client_socket );
73 psz_client_socket[0] = '\0';
77 /*****************************************************************************
78 * The following two functions are from biTStream's examples and are under the
79 * WTFPL (see LICENSE.WTFPL).
80 ****************************************************************************/
81 __attribute__ ((format(printf, 2, 3)))
82 static void psi_print(void *_unused, const char *psz_format, ...)
84 char psz_fmt[strlen(psz_format) + 2];
85 va_list args;
86 va_start(args, psz_format);
87 strcpy(psz_fmt, psz_format);
88 strcat(psz_fmt, "\n");
89 vprintf(psz_fmt, args);
90 va_end(args);
93 __attribute__ ((format(printf, 1, 2)))
94 void return_error( const char *psz_format, ... )
96 va_list args;
97 char psz_fmt[1024];
99 clean_client_socket();
101 va_start( args, psz_format );
102 if ( i_print_type == PRINT_XML )
103 snprintf( psz_fmt, sizeof(psz_fmt) - 1, "<ERROR msg=\"%s\"/>\n", psz_format );
104 else
105 snprintf( psz_fmt, sizeof(psz_fmt) - 1, "ERROR: %s\n", psz_format );
106 psz_fmt[sizeof(psz_fmt) - 1] = '\0';
107 vfprintf( stderr, psz_fmt, args );
108 va_end(args);
109 exit(255);
112 static char *iconv_append_null(const char *p_string, size_t i_length)
114 char *psz_string = malloc(i_length + 1);
115 memcpy(psz_string, p_string, i_length);
116 psz_string[i_length] = '\0';
117 return psz_string;
120 const char *psz_native_charset = "UTF-8//IGNORE";
122 char *psi_iconv(void *_unused, const char *psz_encoding,
123 char *p_string, size_t i_length)
125 static const char *psz_current_encoding = "";
127 char *psz_string, *p;
128 size_t i_out_length;
130 if (!strcmp(psz_encoding, psz_native_charset))
131 return iconv_append_null(p_string, i_length);
133 if (iconv_handle != (iconv_t)-1 &&
134 strcmp(psz_encoding, psz_current_encoding)) {
135 iconv_close(iconv_handle);
136 iconv_handle = (iconv_t)-1;
139 if (iconv_handle == (iconv_t)-1)
140 iconv_handle = iconv_open(psz_native_charset, psz_encoding);
141 if (iconv_handle == (iconv_t)-1) {
142 msg_Warn(NULL, "couldn't open converter from %s to %s (%m)", psz_encoding,
143 psz_native_charset);
144 return iconv_append_null(p_string, i_length);
146 psz_current_encoding = psz_encoding;
148 /* converted strings can be up to six times larger */
149 i_out_length = i_length * 6;
150 p = psz_string = malloc(i_out_length);
151 if (iconv(iconv_handle, &p_string, &i_length, &p, &i_out_length) == -1) {
152 msg_Warn(NULL, "couldn't convert from %s to %s (%m)", psz_encoding,
153 psz_native_charset);
154 free(psz_string);
155 return iconv_append_null(p_string, i_length);
157 if (i_length)
158 msg_Warn(NULL, "partial conversion from %s to %s", psz_encoding,
159 psz_native_charset);
161 *p = '\0';
162 return psz_string;
165 void print_pids_header( void )
167 if ( i_print_type == PRINT_XML )
168 printf("<PIDS>\n");
171 void print_pids_footer( void )
173 if ( i_print_type == PRINT_XML )
174 printf("</PIDS>\n");
177 void print_pid(uint16_t i_pid, ts_pid_info_t *p_info)
179 if ( p_info->i_packets == 0 )
180 return;
181 if ( i_print_type == PRINT_TEXT )
182 printf("pid %d packn %lu ccerr %lu tserr %lu scramble %d Bps %lu seen %"PRId64"\n",
183 i_pid,
184 p_info->i_packets,
185 p_info->i_cc_errors,
186 p_info->i_transport_errors,
187 p_info->i_scrambling,
188 p_info->i_bytes_per_sec,
189 now - p_info->i_last_packet_ts
191 else
192 printf("<PID pid=\"%d\" packn=\"%lu\" ccerr=\"%lu\" tserr=\"%lu\" scramble=\"%d\" Bps=\"%lu\" seen=\"%"PRId64"\" />\n",
193 i_pid,
194 p_info->i_packets,
195 p_info->i_cc_errors,
196 p_info->i_transport_errors,
197 p_info->i_scrambling,
198 p_info->i_bytes_per_sec,
199 now - p_info->i_last_packet_ts
203 void print_pids( uint8_t *p_data )
205 int i_pid;
206 print_pids_header();
207 for ( i_pid = 0; i_pid < MAX_PIDS; i_pid++ ) {
208 ts_pid_info_t *p_info = (ts_pid_info_t *)(p_data + i_pid * sizeof(ts_pid_info_t));
209 print_pid( i_pid, p_info );
211 print_pids_footer();
214 void print_eit_events(uint8_t *p_eit, f_print pf_print, void *print_opaque, f_iconv pf_iconv, void *iconv_opaque, print_type_t i_print_type)
216 uint8_t *p_event;
217 uint8_t j = 0;
218 while ((p_event = eit_get_event(p_eit, j)) != NULL) {
219 j++;
220 char start_str[24], duration_str[10];
221 int duration, hour, min, sec;
222 time_t start_ts;
224 start_ts = dvb_time_format_UTC(eitn_get_start_time(p_event), NULL, start_str);
226 dvb_time_decode_bcd(eitn_get_duration_bcd(p_event), &duration, &hour, &min, &sec);
227 sprintf(duration_str, "%02d:%02d:%02d", hour, min, sec);
229 switch (i_print_type) {
230 case PRINT_XML:
231 pf_print(print_opaque, "<EVENT id=\"%u\" start_time=\"%ld\" start_time_dec=\"%s\""
232 " duration=\"%u\" duration_dec=\"%s\""
233 " running=\"%d\" free_CA=\"%d\">",
234 eitn_get_event_id(p_event),
235 start_ts, start_str,
236 duration, duration_str,
237 eitn_get_running(p_event),
238 eitn_get_ca(p_event)
240 break;
241 default:
242 pf_print(print_opaque, " * EVENT id=%u start_time=%ld start_time_dec=\"%s\" duration=%u duration_dec=%s running=%d free_CA=%d",
243 eitn_get_event_id(p_event),
244 start_ts, start_str,
245 duration, duration_str,
246 eitn_get_running(p_event),
247 eitn_get_ca(p_event)
251 descs_print(eitn_get_descs(p_event), pf_print, print_opaque,
252 pf_iconv, iconv_opaque, i_print_type);
254 switch (i_print_type) {
255 case PRINT_XML:
256 pf_print(print_opaque, "</EVENT>");
257 break;
258 default:
259 break;
264 void print_eit(uint8_t *p_eit, unsigned int i_eit_size, f_print pf_print, void *print_opaque, f_iconv pf_iconv, void *iconv_opaque, print_type_t i_print_type)
266 uint8_t *p_eit_end = p_eit + i_eit_size;
267 uint8_t i_tid = psi_get_tableid(p_eit);
268 const char *psz_tid = "unknown";
270 if (i_tid == EIT_TABLE_ID_PF_ACTUAL)
271 psz_tid = "actual_pf";
272 else if (i_tid == EIT_TABLE_ID_PF_OTHER)
273 psz_tid = "other_pf";
274 else if (i_tid >= EIT_TABLE_ID_SCHED_ACTUAL_FIRST && i_tid <= EIT_TABLE_ID_SCHED_ACTUAL_LAST)
275 psz_tid = "actual_schedule";
276 else if (i_tid >= EIT_TABLE_ID_SCHED_OTHER_FIRST && i_tid <= EIT_TABLE_ID_SCHED_OTHER_LAST)
277 psz_tid = "other_schedule";
279 switch (i_print_type) {
280 case PRINT_XML:
281 pf_print(print_opaque,
282 "<EIT tableid=\"0x%02x\" type=\"%s\" service_id=\"%u\""
283 " version=\"%u\""
284 " current_next=\"%u\""
285 " tsid=\"%u\" onid=\"%u\">",
286 i_tid, psz_tid,
287 eit_get_sid(p_eit),
288 psi_get_version(p_eit),
289 !psi_get_current(p_eit) ? 0 : 1,
290 eit_get_tsid(p_eit),
291 eit_get_onid(p_eit)
293 break;
294 default:
295 pf_print(print_opaque,
296 "new EIT tableid=0x%02x type=%s service_id=%u version=%u%s"
297 " tsid=%u"
298 " onid=%u",
299 i_tid, psz_tid,
300 eit_get_sid(p_eit),
301 psi_get_version(p_eit),
302 !psi_get_current(p_eit) ? " (next)" : "",
303 eit_get_tsid(p_eit),
304 eit_get_onid(p_eit)
308 while (p_eit < p_eit_end) {
309 print_eit_events(p_eit, pf_print, print_opaque, pf_iconv, iconv_opaque, i_print_type);
310 p_eit += psi_get_length( p_eit ) + PSI_HEADER_SIZE;
313 switch (i_print_type) {
314 case PRINT_XML:
315 pf_print(print_opaque, "</EIT>");
316 break;
317 default:
318 pf_print(print_opaque, "end EIT");
322 struct dvblastctl_option {
323 char * opt;
324 int nparams;
325 ctl_cmd_t cmd;
328 static const struct dvblastctl_option options[] =
330 { "reload", 0, CMD_RELOAD },
331 { "shutdown", 0, CMD_SHUTDOWN },
333 { "fe_status", 0, CMD_FRONTEND_STATUS },
334 { "mmi_status", 0, CMD_MMI_STATUS },
336 { "mmi_slot_status", 1, CMD_MMI_SLOT_STATUS }, /* arg: slot */
337 { "mmi_open", 1, CMD_MMI_OPEN }, /* arg: slot */
338 { "mmi_close", 1, CMD_MMI_CLOSE }, /* arg: slot */
339 { "mmi_get", 1, CMD_MMI_RECV }, /* arg: slot */
340 { "mmi_send_text", 1, CMD_MMI_SEND_TEXT }, /* arg: slot, en50221_mmi_object_t */
341 { "mmi_send_choice", 2, CMD_MMI_SEND_CHOICE }, /* arg: slot, en50221_mmi_object_t */
343 { "get_pat", 0, CMD_GET_PAT },
344 { "get_cat", 0, CMD_GET_CAT },
345 { "get_nit", 0, CMD_GET_NIT },
346 { "get_sdt", 0, CMD_GET_SDT },
347 { "get_eit_pf", 1, CMD_GET_EIT_PF }, /* arg: service_id (uint16_t) */
348 { "get_eit_schedule", 1, CMD_GET_EIT_SCHEDULE }, /* arg: service_id (uint16_t) */
349 { "get_pmt", 1, CMD_GET_PMT }, /* arg: service_id (uint16_t) */
350 { "get_pids", 0, CMD_GET_PIDS },
351 { "get_pid", 1, CMD_GET_PID }, /* arg: pid (uint16_t) */
353 { NULL, 0, 0 }
356 void usage()
358 printf("DVBlastctl %s (%s)\n", VERSION, VERSION_EXTRA );
359 printf("Usage: dvblastctl -r <remote socket> [-x <text|xml>] [cmd]\n");
360 printf("Options:\n");
361 printf(" -r --remote-socket <name> Set socket name to <name>.\n" );
362 printf(" -t --timeout <seconds> Set socket read/write timeout in seconds (default 15).\n" );
363 printf(" -j --system-charset <name> Character set used for output (default UTF-8//IGNORE)\n" );
364 printf(" -x --print <text|xml> Choose output format for info commands.\n" );
365 printf("Control commands:\n");
366 printf(" reload Reload configuration.\n");
367 printf(" shutdown Shutdown DVBlast.\n");
368 #ifdef HAVE_DVB_SUPPORT
369 printf("Status commands:\n");
370 printf(" fe_status Read frontend status information.\n");
371 printf(" mmi_status Read CAM status.\n");
372 printf("MMI commands:\n");
373 printf(" mmi_slot_status <slot> Read MMI slot status.\n");
374 printf(" mmi_open <slot> Open MMI slot.\n");
375 printf(" mmi_close <slot> Close MMI slot.\n");
376 printf(" mmi_get <slot> Read MMI slot.\n");
377 printf(" mmi_send_text <slot> <text> Send text to MMI slot.\n");
378 printf(" mmi_send_choice <slot> <choice> Send choice to MMI slot.\n");
379 #endif
380 printf("Demux info commands:\n");
381 printf(" get_pat Return last PAT table.\n");
382 printf(" get_cat Return last CAT table.\n");
383 printf(" get_nit Return last NIT table.\n");
384 printf(" get_sdt Return last SDT table.\n");
385 printf(" get_eit_pf <service_id> Return last EIT present/following data.\n");
386 printf(" get_eit_schedule <service_id> Return last EIT schedule data.\n");
387 printf(" get_pmt <service_id> Return last PMT table.\n");
388 printf(" get_pids Return info about all pids.\n");
389 printf(" get_pid <pid> Return info for chosen pid only.\n");
390 printf("\n");
391 exit(1);
394 int main( int i_argc, char **ppsz_argv )
396 char *client_socket_tmpl = "dvblastctl.clientsock.XXXXXX";
397 char *psz_srv_socket = NULL;
398 int i;
399 char *p_cmd, *p_arg1 = NULL, *p_arg2 = NULL;
400 ssize_t i_size;
401 struct sockaddr_un sun_client, sun_server;
402 uint8_t p_buffer[COMM_BUFFER_SIZE];
403 uint8_t *p_data = p_buffer + COMM_HEADER_SIZE;
404 uint16_t i_pid = 0;
405 struct dvblastctl_option opt = { 0, 0, 0 };
407 for ( ; ; )
409 int c;
411 static const struct option long_options[] =
413 {"remote-socket", required_argument, NULL, 'r'},
414 {"timeout", required_argument, NULL, 't'},
415 {"system-charset", required_argument, NULL, 'j'},
416 {"print", required_argument, NULL, 'x'},
417 {"help", no_argument, NULL, 'h'},
418 {0, 0, 0, 0}
421 if ( (c = getopt_long(i_argc, ppsz_argv, "r:t:x:j:h", long_options, NULL)) == -1 )
422 break;
424 switch ( c )
426 case 'r':
427 psz_srv_socket = optarg;
428 break;
430 case 't':
431 i_timeout_seconds = (unsigned int)strtoul(optarg, NULL, 10);
432 break;
434 case 'j':
435 psz_native_charset = optarg;
436 break;
438 case 'x':
439 if ( !strcmp(optarg, "text") )
440 i_print_type = PRINT_TEXT;
441 else if ( !strcmp(optarg, "xml") )
442 i_print_type = PRINT_XML;
443 else
444 msg_Warn( NULL, "unrecognized print type %s", optarg );
445 /* Make stdout line-buffered */
446 setvbuf(stdout, NULL, _IOLBF, 0);
447 break;
449 case 'h':
450 default:
451 usage();
455 /* Validate commands */
456 #define usage_error(msg, ...) \
457 do { \
458 msg_Err( NULL, msg, ##__VA_ARGS__ ); \
459 usage(); \
460 } while(0)
461 p_cmd = ppsz_argv[optind];
462 p_arg1 = ppsz_argv[optind + 1];
463 p_arg2 = ppsz_argv[optind + 2];
465 if ( !psz_srv_socket )
466 usage_error( "Remote socket is not set.\n" );
468 if ( !p_cmd )
469 usage_error( "Command is not set.\n" );
471 i = 0;
472 do {
473 if ( streq(ppsz_argv[optind], options[i].opt) )
475 opt = options[i];
476 break;
478 } while ( options[++i].opt );
480 if ( !opt.opt )
481 usage_error( "Unknown command: %s\n", p_cmd );
483 if ( opt.nparams == 1 && !p_arg1 )
484 usage_error( "%s option needs parameter.\n", opt.opt );
486 if ( opt.nparams == 2 && (!p_arg1 || !p_arg2) )
487 usage_error( "%s option needs two parameters.\n", opt.opt );
488 #undef usage_error
490 /* Create client socket name */
491 char *tmpdir = getenv("TMPDIR");
492 snprintf( psz_client_socket, PATH_MAX - 1, "%s/%s",
493 tmpdir ? tmpdir : "/tmp", client_socket_tmpl );
494 psz_client_socket[PATH_MAX - 1] = '\0';
496 int tmp_fd = mkstemp(psz_client_socket);
497 if ( tmp_fd > -1 ) {
498 close(tmp_fd);
499 unlink(psz_client_socket);
500 } else {
501 return_error( "Cannot build UNIX socket %s (%s)", psz_client_socket, strerror(errno) );
504 if ( (i_fd = socket( AF_UNIX, SOCK_DGRAM, 0 )) < 0 )
505 return_error( "Cannot create UNIX socket (%s)", strerror(errno) );
507 i = COMM_MAX_MSG_CHUNK;
508 setsockopt( i_fd, SOL_SOCKET, SO_RCVBUF, &i, sizeof(i) );
510 memset( &sun_client, 0, sizeof(sun_client) );
511 sun_client.sun_family = AF_UNIX;
512 strncpy( sun_client.sun_path, psz_client_socket,
513 sizeof(sun_client.sun_path) );
514 sun_client.sun_path[sizeof(sun_client.sun_path) - 1] = '\0';
516 if ( bind( i_fd, (struct sockaddr *)&sun_client,
517 SUN_LEN(&sun_client) ) < 0 )
518 return_error( "Cannot bind (%s)", strerror(errno) );
520 memset( &sun_server, 0, sizeof(sun_server) );
521 sun_server.sun_family = AF_UNIX;
522 strncpy( sun_server.sun_path, psz_srv_socket, sizeof(sun_server.sun_path) );
523 sun_server.sun_path[sizeof(sun_server.sun_path) - 1] = '\0';
525 p_buffer[0] = COMM_HEADER_MAGIC;
526 p_buffer[1] = opt.cmd;
527 memset( p_buffer + 2, 0, COMM_HEADER_SIZE - 2 );
528 i_size = COMM_HEADER_SIZE;
530 /* Handle commands that send parameters */
531 switch ( opt.cmd )
533 case CMD_INVALID:
534 case CMD_RELOAD:
535 case CMD_SHUTDOWN:
536 case CMD_FRONTEND_STATUS:
537 case CMD_MMI_STATUS:
538 case CMD_GET_PAT:
539 case CMD_GET_CAT:
540 case CMD_GET_NIT:
541 case CMD_GET_SDT:
542 case CMD_GET_PIDS:
543 /* These commands need no special handling because they have no parameters */
544 break;
545 case CMD_GET_EIT_PF:
546 case CMD_GET_EIT_SCHEDULE:
547 case CMD_GET_PMT:
549 uint16_t i_sid = atoi(p_arg1);
550 i_size = COMM_HEADER_SIZE + 2;
551 p_data[0] = (uint8_t)((i_sid >> 8) & 0xff);
552 p_data[1] = (uint8_t)(i_sid & 0xff);
553 break;
555 case CMD_GET_PID:
557 i_pid = (uint16_t)atoi(p_arg1);
558 i_size = COMM_HEADER_SIZE + 2;
559 p_data[0] = (uint8_t)((i_pid >> 8) & 0xff);
560 p_data[1] = (uint8_t)(i_pid & 0xff);
561 break;
563 #ifdef HAVE_DVB_SUPPORT
564 case CMD_MMI_SEND_TEXT:
566 struct cmd_mmi_send *p_cmd = (struct cmd_mmi_send *)p_data;
567 p_cmd->i_slot = atoi(p_arg1);
569 en50221_mmi_object_t object;
570 object.i_object_type = EN50221_MMI_ANSW;
571 if ( !p_arg2 || p_arg2[0] == '\0' )
573 object.u.answ.b_ok = 0;
574 object.u.answ.psz_answ = "";
576 else
578 object.u.answ.b_ok = 1;
579 object.u.answ.psz_answ = p_arg2;
581 i_size = COMM_BUFFER_SIZE - COMM_HEADER_SIZE
582 - ((void *)&p_cmd->object - (void *)p_cmd);
583 if ( en50221_SerializeMMIObject( (uint8_t *)&p_cmd->object,
584 &i_size, &object ) == -1 )
585 return_error( "Comm buffer is too small" );
587 i_size += COMM_HEADER_SIZE
588 + ((void *)&p_cmd->object - (void *)p_cmd);
589 break;
591 case CMD_MMI_SEND_CHOICE:
593 struct cmd_mmi_send *p_cmd = (struct cmd_mmi_send *)p_data;
594 p_cmd->i_slot = atoi(p_arg1);
596 i_size = COMM_HEADER_SIZE + sizeof(struct cmd_mmi_send);
597 p_cmd->object.i_object_type = EN50221_MMI_MENU_ANSW;
598 p_cmd->object.u.menu_answ.i_choice = atoi(p_arg2);
599 break;
601 case CMD_MMI_SLOT_STATUS:
602 case CMD_MMI_OPEN:
603 case CMD_MMI_CLOSE:
604 case CMD_MMI_RECV:
606 p_data[0] = atoi(p_arg1);
607 i_size = COMM_HEADER_SIZE + 1;
608 break;
610 #endif
611 default:
612 /* This should not happen */
613 return_error( "Unhandled option (%d)", opt.cmd );
616 if ( i_timeout_seconds > 0 ) {
617 struct timeval tv_timeout = {
618 .tv_sec = i_timeout_seconds,
619 .tv_usec = 0,
621 if ( setsockopt( i_fd, SOL_SOCKET, SO_SNDTIMEO, &tv_timeout, sizeof( tv_timeout ) ) != 0 )
622 return_error( "Cannot set SO_SNDTIMEO (%s)", strerror(errno) );
624 if ( setsockopt( i_fd, SOL_SOCKET, SO_RCVTIMEO, &tv_timeout, sizeof( tv_timeout ) ) != 0 )
625 return_error( "Cannot set SO_RCVTIMEO (%s)", strerror(errno) );
628 /* Send command and receive answer */
629 if ( sendto( i_fd, p_buffer, i_size, 0, (struct sockaddr *)&sun_server,
630 SUN_LEN(&sun_server) ) < 0 )
631 return_error( "Cannot send comm socket (%s)", strerror(errno) );
633 uint32_t i_packet_size = 0, i_received = 0;
634 do {
635 i_size = recv( i_fd, p_buffer + i_received, COMM_MAX_MSG_CHUNK, 0 );
636 if ( i_size == -1 )
637 break;
638 if ( !i_packet_size ) {
639 uint32_t *p_packet_size = (uint32_t *)&p_buffer[4];
640 i_packet_size = *p_packet_size;
641 if ( i_packet_size > COMM_BUFFER_SIZE ) {
642 i_size = -1;
643 break;
646 i_received += i_size;
647 } while ( i_received < i_packet_size );
649 clean_client_socket();
650 if ( i_packet_size < COMM_HEADER_SIZE )
651 return_error( "Cannot recv from comm socket, size:%"PRIu32" (%s)", i_packet_size, strerror(errno) );
653 /* Process answer */
654 if ( p_buffer[0] != COMM_HEADER_MAGIC )
655 return_error( "Wrong protocol version 0x%x", p_buffer[0] );
657 now = mdate();
659 ctl_cmd_answer_t c_answer = p_buffer[1];
660 switch ( c_answer )
662 case RET_OK:
663 break;
665 case RET_MMI_WAIT:
666 exit(252);
667 break;
669 case RET_ERR:
670 return_error( "Request failed" );
671 break;
673 case RET_HUH:
674 return_error( "Internal error" );
675 break;
677 case RET_NODATA:
678 return_error( "No data" );
679 break;
681 case RET_PAT:
682 case RET_CAT:
683 case RET_NIT:
684 case RET_SDT:
686 uint8_t *p_flat_data = p_buffer + COMM_HEADER_SIZE;
687 unsigned int i_flat_data_size = i_packet_size - COMM_HEADER_SIZE;
688 uint8_t **pp_sections = psi_unpack_sections( p_flat_data, i_flat_data_size );
689 if ( !pp_sections )
691 return_error( "Error unpacking PSI" );
692 break;
695 switch( c_answer )
697 case RET_PAT: pat_table_print( pp_sections, psi_print, NULL, i_print_type ); break;
698 case RET_CAT: cat_table_print( pp_sections, psi_print, NULL, i_print_type ); break;
699 case RET_NIT: nit_table_print( pp_sections, psi_print, NULL, psi_iconv, NULL, i_print_type ); break;
700 case RET_SDT: sdt_table_print( pp_sections, psi_print, NULL, psi_iconv, NULL, i_print_type ); break;
701 default: break; /* Can't happen */
704 psi_table_free( pp_sections );
705 free( pp_sections );
706 break;
709 case RET_EIT_PF:
710 case RET_EIT_SCHEDULE:
712 uint8_t *p_eit_data = p_buffer + COMM_HEADER_SIZE;
713 unsigned int i_eit_data_size = i_received - COMM_HEADER_SIZE;
714 print_eit( p_eit_data, i_eit_data_size, psi_print, NULL, psi_iconv, NULL, i_print_type );
715 break;
718 case RET_PMT:
720 pmt_print( p_data, psi_print, NULL, psi_iconv, NULL, i_print_type );
721 break;
724 case RET_PID:
726 print_pids_header();
727 print_pid( i_pid, (ts_pid_info_t *)p_data );
728 print_pids_footer();
729 break;
732 case RET_PIDS:
734 print_pids( p_data );
735 break;
738 #ifdef HAVE_DVB_SUPPORT
739 case RET_FRONTEND_STATUS:
741 int ret = 1;
742 struct ret_frontend_status *p_ret =
743 (struct ret_frontend_status *)&p_buffer[COMM_HEADER_SIZE];
744 if ( i_packet_size != COMM_HEADER_SIZE + sizeof(struct ret_frontend_status) )
745 return_error( "Bad frontend status" );
747 if ( i_print_type == PRINT_XML )
748 printf("<FRONTEND>\n");
750 #define PRINT_TYPE( x ) \
751 do { \
752 if ( i_print_type == PRINT_XML ) \
753 printf( " <TYPE type=\"%s\"/>\n", STRINGIFY(x) ); \
754 else \
755 printf( "type: %s\n", STRINGIFY(x) ); \
756 } while(0)
757 switch ( p_ret->info.type )
759 case FE_QPSK: PRINT_TYPE(QPSK); break;
760 case FE_QAM : PRINT_TYPE(QAM); break;
761 case FE_OFDM: PRINT_TYPE(OFDM); break;
762 case FE_ATSC: PRINT_TYPE(ATSC); break;
763 default : PRINT_TYPE(UNKNOWN); break;
765 #undef PRINT_TYPE
767 #define PRINT_INFO( x ) \
768 do { \
769 if ( i_print_type == PRINT_XML ) \
770 printf( " <SETTING %s=\"%u\"/>\n", STRINGIFY(x), p_ret->info.x ); \
771 else \
772 printf( "%s: %u\n", STRINGIFY(x), p_ret->info.x ); \
773 } while(0)
775 if ( i_print_type == PRINT_XML )
776 printf( " <SETTING name=\"%s\"/>\n", p_ret->info.name );
777 else
778 printf( "name: %s\n", p_ret->info.name );
780 PRINT_INFO( frequency_min );
781 PRINT_INFO( frequency_max );
782 PRINT_INFO( frequency_stepsize );
783 PRINT_INFO( frequency_tolerance );
784 PRINT_INFO( symbol_rate_min );
785 PRINT_INFO( symbol_rate_max );
786 PRINT_INFO( symbol_rate_tolerance );
787 PRINT_INFO( notifier_delay );
788 #undef PRINT_INFO
790 if ( i_print_type == PRINT_TEXT )
791 printf("\ncapability list:\n");
793 #define PRINT_CAPS( x ) \
794 do { \
795 if ( p_ret->info.caps & (FE_##x) ) { \
796 if ( i_print_type == PRINT_XML ) { \
797 printf( " <CAPABILITY %s=\"1\"/>\n", STRINGIFY(x) ); \
798 } else { \
799 printf( "%s\n", STRINGIFY(x) ); \
802 } while(0)
803 PRINT_CAPS( IS_STUPID );
804 PRINT_CAPS( CAN_INVERSION_AUTO );
805 PRINT_CAPS( CAN_FEC_1_2 );
806 PRINT_CAPS( CAN_FEC_2_3 );
807 PRINT_CAPS( CAN_FEC_3_4 );
808 PRINT_CAPS( CAN_FEC_4_5 );
809 PRINT_CAPS( CAN_FEC_5_6 );
810 PRINT_CAPS( CAN_FEC_6_7 );
811 PRINT_CAPS( CAN_FEC_7_8 );
812 PRINT_CAPS( CAN_FEC_8_9 );
813 PRINT_CAPS( CAN_FEC_AUTO );
814 PRINT_CAPS( CAN_QPSK );
815 PRINT_CAPS( CAN_QAM_16 );
816 PRINT_CAPS( CAN_QAM_32 );
817 PRINT_CAPS( CAN_QAM_64 );
818 PRINT_CAPS( CAN_QAM_128 );
819 PRINT_CAPS( CAN_QAM_256 );
820 PRINT_CAPS( CAN_QAM_AUTO );
821 PRINT_CAPS( CAN_TRANSMISSION_MODE_AUTO );
822 PRINT_CAPS( CAN_BANDWIDTH_AUTO );
823 PRINT_CAPS( CAN_GUARD_INTERVAL_AUTO );
824 PRINT_CAPS( CAN_HIERARCHY_AUTO );
825 PRINT_CAPS( CAN_MUTE_TS );
827 #if DVBAPI_VERSION >= 301
828 PRINT_CAPS( CAN_8VSB );
829 PRINT_CAPS( CAN_16VSB );
830 PRINT_CAPS( NEEDS_BENDING );
831 PRINT_CAPS( CAN_RECOVER );
832 #endif
833 #if DVBAPI_VERSION >= 500
834 PRINT_CAPS( HAS_EXTENDED_CAPS );
835 #endif
836 #if DVBAPI_VERSION >= 501
837 PRINT_CAPS( CAN_2G_MODULATION );
838 #endif
839 #if DVBAPI_VERSION >= 508
840 PRINT_CAPS( CAN_TURBO_FEC );
841 PRINT_CAPS( CAN_MULTISTREAM );
842 #endif
843 #undef PRINT_CAPS
845 if ( i_print_type == PRINT_TEXT )
846 printf("\nstatus:\n");
848 #define PRINT_STATUS( x ) \
849 do { \
850 if ( p_ret->i_status & (FE_##x) ) { \
851 if ( i_print_type == PRINT_XML ) { \
852 printf( " <STATUS status=\"%s\"/>\n", STRINGIFY(x) ); \
853 } else { \
854 printf( "%s\n", STRINGIFY(x) ); \
857 } while(0)
858 PRINT_STATUS( HAS_SIGNAL );
859 PRINT_STATUS( HAS_CARRIER );
860 PRINT_STATUS( HAS_VITERBI );
861 PRINT_STATUS( HAS_SYNC );
862 PRINT_STATUS( HAS_LOCK );
863 PRINT_STATUS( REINIT );
864 #undef PRINT_STATUS
866 if ( p_ret->i_status & FE_HAS_LOCK )
868 if ( i_print_type == PRINT_XML )
870 printf(" <VALUE bit_error_rate=\"%u\"/>\n", p_ret->i_ber);
871 printf(" <VALUE signal_strength=\"%u\"/>\n", p_ret->i_strength);
872 printf(" <VALUE SNR=\"%u\"/>\n", p_ret->i_snr);
873 } else {
874 printf("\nBit error rate: %u\n", p_ret->i_ber);
875 printf("Signal strength: %u\n", p_ret->i_strength);
876 printf("SNR: %u\n", p_ret->i_snr);
878 ret = 0;
881 if ( i_print_type == PRINT_XML )
882 printf("</FRONTEND>\n" );
884 exit(ret);
885 break;
888 case RET_MMI_STATUS:
890 struct ret_mmi_status *p_ret =
891 (struct ret_mmi_status *)&p_buffer[COMM_HEADER_SIZE];
892 if ( i_packet_size != COMM_HEADER_SIZE + sizeof(struct ret_mmi_status) )
893 return_error( "Bad MMI status" );
895 printf("CA interface with %d %s, type:\n", p_ret->caps.slot_num,
896 p_ret->caps.slot_num == 1 ? "slot" : "slots");
897 #define PRINT_CAPS( x, s ) \
898 if ( p_ret->caps.slot_type & (CA_##x) ) \
899 printf(s "\n");
900 PRINT_CAPS( CI, "CI high level interface" );
901 PRINT_CAPS( CI_LINK, "CI link layer level interface" );
902 PRINT_CAPS( CI_PHYS, "CI physical layer level interface (not supported)" );
903 PRINT_CAPS( DESCR, "built-in descrambler" );
904 PRINT_CAPS( SC, "simple smartcard interface" );
905 #undef PRINT_CAPS
907 printf("\n%d available %s\n", p_ret->caps.descr_num,
908 p_ret->caps.descr_num == 1 ? "descrambler (key)" :
909 "descramblers (keys)");
910 #define PRINT_DESC( x ) \
911 if ( p_ret->caps.descr_type & (CA_##x) ) \
912 printf( STRINGIFY(x) "\n" );
913 PRINT_DESC( ECD );
914 PRINT_DESC( NDS );
915 PRINT_DESC( DSS );
916 #undef PRINT_DESC
918 exit( p_ret->caps.slot_num );
919 break;
922 case RET_MMI_SLOT_STATUS:
924 struct ret_mmi_slot_status *p_ret =
925 (struct ret_mmi_slot_status *)&p_buffer[COMM_HEADER_SIZE];
926 if ( i_packet_size < COMM_HEADER_SIZE + sizeof(struct ret_mmi_slot_status) )
927 return_error( "Bad MMI slot status" );
929 printf("CA slot #%u: ", p_ret->sinfo.num);
931 #define PRINT_TYPE( x, s ) \
932 if ( p_ret->sinfo.type & (CA_##x) ) \
933 printf(s);
935 PRINT_TYPE( CI, "high level, " );
936 PRINT_TYPE( CI_LINK, "link layer level, " );
937 PRINT_TYPE( CI_PHYS, "physical layer level, " );
938 #undef PRINT_TYPE
940 if ( p_ret->sinfo.flags & CA_CI_MODULE_READY )
942 printf("module present and ready\n");
943 exit(0);
946 if ( p_ret->sinfo.flags & CA_CI_MODULE_PRESENT )
947 printf("module present, not ready\n");
948 else
949 printf("module not present\n");
951 exit(1);
952 break;
955 case RET_MMI_RECV:
957 struct ret_mmi_recv *p_ret =
958 (struct ret_mmi_recv *)&p_buffer[COMM_HEADER_SIZE];
959 if ( i_packet_size < COMM_HEADER_SIZE + sizeof(struct ret_mmi_recv) )
960 return_error( "Bad MMI recv" );
962 en50221_UnserializeMMIObject( &p_ret->object, i_packet_size
963 - COMM_HEADER_SIZE - ((void *)&p_ret->object - (void *)p_ret) );
965 switch ( p_ret->object.i_object_type )
967 case EN50221_MMI_ENQ:
968 printf("%s\n", p_ret->object.u.enq.psz_text);
969 printf("(empty to cancel)\n");
970 exit(p_ret->object.u.enq.b_blind ? 253 : 254);
971 break;
973 case EN50221_MMI_MENU:
974 printf("%s\n", p_ret->object.u.menu.psz_title);
975 printf("%s\n", p_ret->object.u.menu.psz_subtitle);
976 printf("0 - Cancel\n");
977 for ( i = 0; i < p_ret->object.u.menu.i_choices; i++ )
978 printf("%d - %s\n", i + 1,
979 p_ret->object.u.menu.ppsz_choices[i]);
980 printf("%s\n", p_ret->object.u.menu.psz_bottom);
981 exit(p_ret->object.u.menu.i_choices);
982 break;
984 case EN50221_MMI_LIST:
985 printf("%s\n", p_ret->object.u.menu.psz_title);
986 printf("%s\n", p_ret->object.u.menu.psz_subtitle);
987 for ( i = 0; i < p_ret->object.u.menu.i_choices; i++ )
988 printf("%s\n", p_ret->object.u.menu.ppsz_choices[i]);
989 printf("%s\n", p_ret->object.u.menu.psz_bottom);
990 printf("(0 to cancel)\n");
991 exit(0);
992 break;
994 default:
995 return_error( "Unknown MMI object" );
996 break;
999 exit(255);
1000 break;
1002 #endif
1004 default:
1005 return_error( "Unknown command answer: %u", c_answer );
1008 if (iconv_handle != (iconv_t)-1) {
1009 iconv_close(iconv_handle);
1010 iconv_handle = (iconv_t)-1;
1013 return 0;