/* * kissdx - KiSS PC-Link Daemon eXtended (based on kissd) * * Copyright (C) * Portions Copyright (C) 2006 Vidar Tysse * Portions Copyright (C) 2007 Olivier Kahn * * Heavily based on kiss4lin, * Copyright (C) 2004 Jacob Kolding * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include #include #include #include #include #include #include #include #include // Standard GNU Exit code #include "cmdclient.h" #include "cmdserver.h" #include "kissdx.h" #include "utils.h" // ======================================================== void deliver_local_command(const char* command) // Set hidden options based on specific pclink://library_browsing { int sock; int yes = 1; struct sockaddr_un sun; /* get a local domain socket */ if ((sock = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) { log("Err: command socket: %s", strerror(errno)); return; } /* lose the pesky "address already in use" error message */ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0) { log("Err: command setsockopt: %s", strerror(errno)); return; } // Compose a socket structure to connect with the command server in our main process memset(&sun, 0, sizeof(sun)); sun.sun_family = AF_LOCAL; strncpy (sun.sun_path, commandserver_path, sizeof (sun.sun_path)); // Open a connection to the command server in our main process if (connect(sock, (struct sockaddr *) &sun, SUN_LEN (&sun)) < 0) { log("Err: command connect: %s", strerror(errno)); return; } logv("Sending local command: %s", command); send(sock, command, strlen(command), 0); close (sock); } // ======================================================== static void create_admcmd_tcp_socket(int *sock) { struct sockaddr sin_target; // socket of internet type (standard for tcp connection) int yes = 1; /* get a local domain socket */ if ((*sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { loglevel(LOGERROR,"Error: Creating TCP socket [%s]", strerror(errno)); exit(EX_OSERR); } /* lose the pesky "address already in use" error message */ if (setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0) { loglevel(LOGERROR,"Error: Configuring TCP socket [%s]", strerror(errno)); exit(EX_OSERR); return; } // Compose a socket structure to connect to mediacenter:port (main daemon) sin_target = tcpFormatAdress(options.mediacenter, (u_short)options.adminserver_port); loglevel(LOGDEBUG,"Debug: %s %s","CLIENT Admin Command request : ", options.admincommand); loglevel(LOGDEBUG,"Debug: %s %s","CLIENT Admin Mediacenter name : ", options.mediacenter); loglevel(LOGDEBUG,"Debug: %s %d","CLIENT Admin Mediacenter tcp.port: ", options.adminserver_port); // Open a connection to the command server in our main process if (connect(*sock, (struct sockaddr *)&sin_target, sizeof(sin_target)) < 0) { loglevel(LOGERROR,"Error: Openning admincommand [%d] [%s]",options.adminserver_port, strerror(errno)); exit(EX_OSERR); } } // ============================================================================================ // General comment about Client AdminCommand mode : // Client side of admin command architecture : a command detected in command line option // Send a tcp:8003 connection to MEDIACENTER.IP with it // Client side of admin command is not a fork due to its dedicated process to admin action // In the opposite, the server side will fork a handle_function to protect current streaming performance // except for short time consuming admin action, the server will treat them directly on reception void deliver_admcmd_action (char *admcmd_action, char *admcmd_cmd, char *param) { /* => ADMCMD_ACTION|RELOAD_CONFIG|\n <= ADMCMD_ACK|RELOAD_CONFIG|OK\n <= (or) ADMCMD_ACK|RELOAD_CONFIG|ERR|Error message\n <= EOL||\n => close connection once the ACK is received */ // A = ** Send Admin Command to server ** int sock; // socket to send command to server kissdx char tmp_line[512]; // used either in Sending than in Receiving on socket // Create a socket on TCP:8003 connected on the mediaserver (kissdx server engine) create_admcmd_tcp_socket(&sock); // do not add an ending \n to sendline: it's done by function send_line() snprintf(tmp_line, sizeof tmp_line, "%s|%s|%s",admcmd_action,admcmd_cmd,param); send_line(sock, tmp_line ); loglevel(LOGSTD,"Send to mediaserver :'%s'",tmp_line); // B = ** Wait ACK response from server int timeout = 5; // Timeout seconds before date returned from kissdx int resp_len; short received_EOL = 0; // flag to detect reception of \nEOL\n from kissdx server while ( !received_EOL ) { if ((resp_len = receive_line(sock, tmp_line, sizeof(tmp_line), timeout)) < 0) { return; // no more reception available on the socket } loglevel(LOGSTD,"Kissdx server response: '%s'",tmp_line); if ( (resp_len > 0) && (!strcasecmp(tmp_line, ADMCMD_EOL)) ) { loglevel(LOGDEBUG,"Debug: ADMCMD %s response EOL : %s",admcmd_cmd,tmp_line); received_EOL = 1; // response ending with EOL (end of response from PcLink server) } } // C = ** Close connection with kissdx // Not needed, kissdx server already close the connection. } // ======================================================== void simulplayer(char *tcp_player_command,char *tcp_player_param, char *tcp_response_expected) // PCLINK // Simulate a KiSS player : send on TCP:8000 the command specified // ex: LIST VIDEO ... { int sock=0; // socket to send TCP request to server kissdx char player_cmd[512]; // int len; len = snprintf(player_cmd, sizeof player_cmd, "%s |%s|",tcp_player_command, tcp_player_param); if (len > 0 ) { loglevel(LOGSTD,"Info: Simulplayer '%s'",player_cmd); sock = deliver_tcp_cmd(player_cmd, options.mediacenter, PORT_PCLINK ); } // Read kissdx response on PCLINK Protocol // based on connection.c : void handle_pclink_request(int sock) int timeout = 5; // Timeout seconds before date returned from kissdx char pclink_srv_response[512]; char *pclink_EOL = "EOL"; int resp_len; short received_EOL = 0; // flag to detect reception of \nEOL_n from PCLink_server char *resp_EOL; while ( !received_EOL ) { if ((resp_len = receive_line(sock, pclink_srv_response, sizeof(pclink_srv_response), timeout)) < 0) { return; } if ( (resp_len > 0) && (resp_EOL =strstr(pclink_srv_response,pclink_EOL)) ){ if ( strlen(resp_EOL) == strlen(pclink_EOL) ) { received_EOL = 1; // response ending with EOL (end of response from PcLink server) } } logv("CLIENT Simul Player Reading pclink response : [%s]", pclink_srv_response); log ("%s= [%s]",tcp_player_command,pclink_srv_response); } // C = Close connection with kissdx close (sock); // next step is to wait for a server response as expected in parameter } // ======================================================== int deliver_tcp_cmd(char *tcp_command,char *tcp_target_host, int tcp_port) // Goal : Create a socket, send a command string, and give back the socket created. // Create a socket on TCP port (param) and send a command string (param) // Response is managed by the caller (and could be different according various commands // Called by SimulPlayer() fct // Protocol expectation: (based on PCLINK standard) // client <=> server // "COMMAND_TEXT ||" => // <= response ||\n other response \nEOL||\n { int sock=0; // socket to send TCP request to server kissdx const int yes = 1; struct sockaddr sin_target; // socket of internet type (standard for tcp connection) /* get a local domain socket */ if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { log("Err: SendTcpCommand socket [%s]", strerror(errno)); exit(EX_OSERR); } /* lose the pesky "address already in use" error message */ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0) { log("Err: SendTcpCommand setsockopt [%s]", strerror(errno)); exit(EX_OSERR); return 0; } // Compose a socket structure to connect to mediacenter:port.ADMCMD (main daemon) sin_target = tcpFormatAdress(tcp_target_host, (u_short)tcp_port); logv("%s %s","SendTcpCommand Target name : ", tcp_target_host); logv("%s %d","SendTcpCommand Target tcp.port: ", (u_short)tcp_port); // Open a connection to the command server in our main process if (connect(sock, (struct sockaddr *)&sin_target, sizeof(sin_target)) < 0) { log("Err: SendTcpCommand connect [%s]", strerror(errno)); exit(EX_OSERR); } logv( "SendTcpCommand Sending : [%s]", tcp_command); send_line(sock, tcp_command); return sock; } // ========================================================================================== void perform_admcmd_identity_request_reloadconfig(char *admcmd) // Client side of admin command architecture : RELOADCONFIG // Send a udp:8000 "ARE_YOU_KISS_PCLINK_SERVER?" connection to MEDIACENTER.IP // function not used but kept for future purpose on UDP call { int sock; // socket to send command to server kissdx const int yes = 1; struct sockaddr sin_target; // socket of internet type (standard for tcp connection) const char *UDP_RELOADCONFIG ="ARE_YOU_KISS_PCLINK_SERVER?"; /* get a local domain socket */ if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { log("Err: admin command socket [%s]", strerror(errno)); exit(EX_OSERR); } /* lose the pesky "address already in use" error message */ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0) { log("Err: admin command setsockopt [%s]", strerror(errno)); exit(EX_OSERR); return; } // Compose a socket structure to connect to mediacenter:port 8000(UDP)(main daemon) sin_target = tcpFormatAdress(options.mediacenter, (u_short)PORT_PCLINK); logv("%s %s","CLIENT Admin Command request : ", admcmd); logv("%s %s","CLIENT Admin Mediacenter name : ", options.mediacenter); logv("%s %d","CLIENT Admin Mediacenter udp.port: ", PORT_PCLINK); logv("CLIENT Admin Sending command UDP : %s", UDP_RELOADCONFIG); //send(sock, UDP_RELOADCONFIG, strlen(UDP_RELOADCONFIG), 0); if (sendto(sock,UDP_RELOADCONFIG,strlen(UDP_RELOADCONFIG),0,(struct sockaddr *)&sin_target,sizeof sin_target) != strlen(UDP_RELOADCONFIG) ) { log("Err: admin command send [%s]", strerror(errno)); exit(EX_OSERR); } close (sock); } // ======================================================== struct sockaddr tcpFormatAdress( char * host, u_short port ) // accept full hostname or a 'x.x.x.x' IP and generate a sockaddr // declared in 'arpa/inet.h' { struct sockaddr_in addr; struct sockaddr addrRet; struct hostent *lphost ; u_long IP; memset((char*)&addr, 0, sizeof(addr)); /* Detect the IP address or a full hostname */ IP = inet_addr(host); // return empty INADDR_NONE ip if not x.x.x.x provided if (IP == (u_long)INADDR_NONE) { lphost = gethostbyname(host); // Null if hostname 'host' has no internet IP if (lphost == NULL) { memset( (char * )&addrRet, 0, sizeof(addrRet) ); //Clear addr // hostname specified is not good host name: return a empty address return addrRet; } addr.sin_family = lphost->h_addrtype; memcpy (&addr.sin_addr, lphost->h_addr, lphost->h_length); } else { // IP address is provided in function parameter addr.sin_family = AF_INET; addr.sin_addr.s_addr = IP; } /* Port destination */ addr.sin_port = htons((u_short)port ); memcpy( (char *)&addrRet, (char *)&addr, sizeof(addrRet) ); return addrRet; } // ========================================================