/* * kissdx - KiSS PC-Link Daemon eXtended (based on kissd) * * Copyright (C) 2005 Stelian Pop * 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 // gethostname #include #include #include #include #include #include #include #include // Standard GNU Exit code #include #include #include #include #include #include #include #include "kissdx.h" #include "utils.h" #include "config.h" #include "connection.h" #include "cmdclient.h" #include "cmdserver.h" // Global variables option_set_t options; int is_socket_timed_out = 0; char commandserver_path[L_tmpnam] = ""; // tmp file for socket "Local Command" time_t last_time_list_hidden_entries_on = (time_t)0; // Set of text constant defined by default language char inetAddr_MediaCenter[64] = "127.0.0.1"; // Default ip adress to send the admin command // Set of flag managing priority between CommandLine_Options (CLO) and kissdx.conf_Options (KCO) short adminserver_port_CLO = 0; // Default: adminserver_port is not detected from command line options // ======================================================== // Prefix log information with [pid] time-hour // ex: [14084] 2007-01-30 21:31:46 Using config file ./kissdx.conf char * extended_logfile_format(const char *format) { static char extended_format[1024]; if (options.verbose) { char timestr[32]; time_t current_time = time(NULL); strftime(timestr, sizeof timestr, "%F %T", localtime(¤t_time)); snprintf(extended_format, sizeof extended_format, "[%5d] %s %s", getpid(), timestr, format); } else { strncpy(extended_format, format, sizeof extended_format); } return extended_format; } // ======================================================== // Handle the reception of SIGTERM or SIGKILL or SIGINT (see signal() in do_daemon() ) static void server_shutdown(int signal) { log("%s", "shutting down kissdx..."); // Delete temporary files created for socket "Local Command" if (*commandserver_path) unlink(commandserver_path); if (*config.pidfilepath) unlink(config.pidfilepath); // Exit normally exit(0); } // ======================================================== // Handle the reception of SIGCHLD (Child status has changed (POSIX).) // Wait for the status of child (clean exit of child to avoid zombies) static void kill_children(int signal) { int pid, preserved_errno = errno; do { do { errno = 0; pid = waitpid(-1, NULL, WNOHANG); } while (pid <= 0 && errno == EINTR); if (pid > 0) logv("Child process [%5d] exited", pid); } while (pid > 0); errno = preserved_errno; } // ======================================================== static void create_socket(int *sock, int port, int type) { struct sockaddr_in sin; int yes = 1; /* get an internet domain socket */ if ((*sock = socket(AF_INET, type, 0)) < 0) { log("socket: %s", strerror(errno)); exit(1); } /* lose the pesky "address already in use" error message */ if (setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0) { log("setsockopt: %s", strerror(errno)); exit(1); } /* complete the socket structure */ memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr = config.listenaddress; sin.sin_port = htons(port); /* bind the socket to the port number */ if (bind(*sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) { log("Err: bind: %s", strerror(errno)); exit(1); } /* everything done for a udp socket / no need to start a listener */ if (type == SOCK_DGRAM) { return; } /* listen up to 5 incoming connection queue = simultanous TCP call */ /* Stress capabilities and heavy load support may need to increase this queue size */ /* Analysis in running system : netstat -s -p tcp |grep "overflow" */ /* candidate to be a parameter in .conf */ if (listen(*sock, 5) < 0) { log("Err: listen: %s", strerror(errno)); exit(1); } } // ======================================================== static void udp_responder (int sd) { // UDP listener to respond on "ARE_YOU_KISS_PCLINK_SERVER?" request // Based on config.configautoload(yes/no) a new parsing of config is triggered struct sockaddr_in sin; socklen_t sinlen = sizeof(sin); char udp_buf[128]; /* complete the socket structure */ memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr = config.listenaddress; sin.sin_port = htons(PORT_PCLINK); if (recvfrom(sd, udp_buf, sizeof(udp_buf), 0, (struct sockaddr*)&sin, &sinlen) < 0) { log("Err: recvfrom: %s", strerror(errno)); return; // exit gracefully } if (!strstr(udp_buf, "ARE_YOU_KISS_PCLINK_SERVER?")) { return; // no other UDP request could be treated than ARE_YOU_KISS..... } logv("Received identity request from %s", inet_ntoa(sin.sin_addr)); strncpy(udp_buf, config.serversignature, sizeof(udp_buf)); if (config.configautoload) { // Re-read config file to get at any changes. Keep current configuration if error. config_settings_t new_config; int config_error = parse_config(&new_config); if (!config_error) { free_config(&config); memcpy(&config, &new_config, sizeof (config)); } else { log("Loading new config failed from : %s",new_config.config_file); log("Keep previous configuration from : %s",config.config_file); show_current_config_Verbose(&config); // Show the old (still current) configuration in verbose mode } logv0("... Ready for streaming business"); // response with hostname signature string to KiSS player + ConfigReloadStatus information string_substitute(udp_buf, "{ConfigReloadStatus}", config_error? "(conf!)" : "(OK)", sizeof udp_buf); } else { // response with hostname signature string to KiSS player string_substitute(udp_buf, "{ConfigReloadStatus}", " ", sizeof udp_buf); } logv("Server signature : %s",udp_buf); const char* buf_p = can_convert_charset(cept_config, cept_client)? convert_charset(udp_buf, cept_config, cept_client) : udp_buf; if (sendto(sd, buf_p, strlen(buf_p), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0) { log("Err: sendto: %s", strerror(errno)); } } // ======================================================== static void do_daemon(void) { // message for verbose mode to isolate 'heavy load task'(fork) and 'light load task'(no fork) const char *msg_noforkneeded ="Admin command parsing detects light action with no fork needed"; int sd_server, sd_client; // 'sd' stand for Socket Descriptor int sd_kml = -1; // default behavior is not to create a KML forwarder feature int sd_pclink; // Receive library browsing and media GET int sd_udp_responder; // Receive identity request int sd_loccmd; // receiving local command (e.g. hidden file ) int sd_admcmd = -1; // receiving Admin Command on TCP:8003 short udp_pclink_req = 0; // flag : which socket type of request have we received ? short tcp_pclink_req = 0; short tcp_kml_req = 0; short tcp_admcmd_req = 0; short tcp_loccmd_req = 0; enum admin_request_t admin_command_request_type = adm_unknown ; // Each admin command is referenced in servercmd.h short enable_fork = 0; // flag : does the admin command received is 'light' time consuming task (no fork) (0=false) struct sockaddr_in pin; socklen_t addrlen; fd_set set; struct timeval *listener_select_timeout = NULL; // No timeout : To block indefinitely until one of the file descriptors is ready pid_t child_pid; // child pid pid_t daemon_pid; // daemon pid // Set handler for signal: // Server Interuption => action : Remove tmp socket // Child process Interuption = Clean wait status signal(SIGINT, server_shutdown); // Signal sent by a Ctrl C in a terminal signal(SIGTERM, server_shutdown); // Signal sent by a kill command signal(SIGCHLD, kill_children); // Signal sent by a child change of status if (options.daemonize) openlog("kissdx", LOG_PID, LOG_DAEMON); log("%s", "Starting kissdx..."); if (*config.pidfilepath) { FILE *pidfile; if ((pidfile = fopen(config.pidfilepath, "w")) != NULL) { fprintf(pidfile, "%ld\n", (long) getpid()); fclose(pidfile); } else { log("Error creating pidfile '%s': %s", config.pidfilepath, strerror(errno)); } } // PCLINK listener on TCP port : TCP.PCLINK and UDP.PCLINK from kiSS player create_socket(&sd_pclink, PORT_PCLINK, SOCK_STREAM); // Stream = TCP 8000 default create_socket(&sd_udp_responder, PORT_PCLINK, SOCK_DGRAM); // Dgram = UDP 8888 default logv("kissdx listener PCLINK TCP port %d open",PORT_PCLINK); logv("kissdx listener PCLINK UDP port %d open",PORT_PCLINK); // KML listener is configure in kissdx.conf and started only if needed if (strlen(config.kmlfwdurl) != 0) { create_socket(&sd_kml, PORT_KML, SOCK_STREAM); logv("kissdx listener KML.Fwd TCP port %d open",PORT_KML); } // Command Admin listener is created for Admin purpose (Stop, Save Config....) if (config.adminserver_port > 0) { create_socket(&sd_admcmd, config.adminserver_port, SOCK_STREAM); // TCP: 8003 default logv("kissdx listener ADM.CMD TCP port %d open",config.adminserver_port); } // Local Command listener targets the child purpose of options change (hidden options ...) // usage of local socket based on tmp file setup_local_commandserver_socket(&sd_loccmd); logv0("... Ready for streaming business"); // -- Loop for daemon ---- again: // Automatically reset config.listhiddenentries after a timeout period. if (config.enablehiddenfilesminutes && last_time_list_hidden_entries_on != (time_t)0) { if (difftime(time(NULL), last_time_list_hidden_entries_on) / 60.0 >= (double)config.enablehiddenfilesminutes) { config.listhiddenentries = 0; last_time_list_hidden_entries_on = (time_t)0; } } // Starting listening for incoming request udp_pclink_req = tcp_pclink_req = tcp_kml_req = tcp_admcmd_req = tcp_loccmd_req = 0; // Default request received is expected as light timeconsuming task (UDP reload, ...) enable_fork = 0; // for security, default is heavy load, generating a fork (preserve Daemon) FD_ZERO(&set); // Initialise the file descriptor destinated to select() (identify entry request tcp/udp) FD_SET(sd_pclink, &set); // Add file descriptor of listener PCLINK FD_SET(sd_udp_responder, &set); // Add file descriptor of listener UDP "Identity request" FD_SET(sd_loccmd, &set); // Add file descriptor of listener LocalCommand if (config.adminserver_port > 0) { FD_SET(sd_admcmd, &set); // Add file descriptor of listener AdministrationCommand } if (strlen(config.kmlfwdurl) != 0) { FD_SET(sd_kml, &set); } memset(&pin, 0, sizeof(pin)); memset(&addrlen, 0, sizeof(addrlen)); // Design of TCP request treatment : // kissdx listen to multiple request type (pclink, udp, admcmd, localcmd, kmlforwarder ....) // Select() does not lost request but response with the total number of ready file descriptors in all of the sets // As the current implementation treats only once request (set) per dodaemon.loop, a priority of treatment is hard coded // - a) Identify each request ready to be treated (and set the corresponding flag (eg. udp_pclink_req=1) // - b) Get the socket descriptor in the following priority : PCLINK (browsing/streaming) > > KMLFwd > > ADMCMD // - c) Some request are so quick to serve that a new daemon.loop is a waste of time, they are treated online in daemon itself // UDP broadcast (identity request) and TCP.LocalSocket (set of Hidden options to ON/OFF) (not the image generation) // This order afford no freeze in video streaming, and treat other task in a second priority if (select(max(max(max(max(sd_admcmd,sd_pclink), sd_kml), sd_udp_responder), sd_loccmd) + 1, &set, NULL,NULL,listener_select_timeout) < 0) { if (errno == EINTR) goto again; log("Err: select: %s", strerror(errno)); exit(1); } // TASK treated directly in daemon without impact on capability to respond to KiSS tcp request // ==== Task qualified "light load task" : UDP and LocCmd // UDP broadcast reception from KiSS player (=> answers with server identification) // UDP response is treated in master daemon (against TCP PCLINK treated in a dedicated Child) if (FD_ISSET(sd_udp_responder, &set)) { udp_pclink_req = 1; // Flag "request comes from UDP PCLINK" udp_responder(sd_udp_responder); } // Local_socket reception from kissdx.Child options change request (HIDDEN options set/reset) // treated in master daemon if (FD_ISSET(sd_loccmd, &set)) { tcp_loccmd_req = 1; // Flag "request comes from Child local command" enable_fork = 0; // Default behaviors of local command is !EnableFork do { sd_client = accept(sd_loccmd, (struct sockaddr *)&pin, &addrlen); } while (sd_client < 0 && errno == EINTR); if (sd_client < 0) { log("Err: local command accept: %s", strerror(errno)); exit(1); } handle_local_command(sd_client); } // TASK treated in child (fork) due to impact on capability to respond to KiSS tcp request // ==== Task qualified "heavy load task" : // Priority of treatment : PCLINK (browsing/streaming) > > KMLFwd > > ADMCMD // Preparation of fork child configuration (need to accept a socket for sd_server and create sd_client) // Set the socket descriptor of server to the listening socket ready to be read (due to request reception) sd_server = -1; // no server detected (initialisation) if (FD_ISSET(sd_pclink, &set)) { tcp_pclink_req = 1; // Flag "request comes from TCP PCLINK" enable_fork = 1; // Default behaviors of PCLINK command is EnableFork sd_server = sd_pclink; } else if ( (strlen(config.kmlfwdurl) != 0) && (FD_ISSET(sd_kml, &set))) { tcp_kml_req = 1; // Flag "request comes from TCP KML" enable_fork = 1; sd_server = sd_kml; } else if ( (config.adminserver_port > 0) && FD_ISSET(sd_admcmd, &set)) { tcp_admcmd_req = 1; // Flag "request comes from TCP Admin Command" enable_fork = 1; // Default behaviors of admin command is EnableFork sd_server = sd_admcmd; } // Flag feature (eg. tcp_admcmd_req) enable to focus on the most prioritary listener // even if FD_ISSET() may be true for multiple listeners (set) // Accept TCP socket and decide if needed to fork to answer to network request // Maybe a listener of type : PCLINK or KMLFwd or ADMCMD if (sd_server != -1) { do { sd_client = accept(sd_server, (struct sockaddr *)&pin, &addrlen); } while (sd_client < 0 && errno == EINTR); if (sd_client < 0) { log("Err: accept: %s", strerror(errno)); exit(1); } // TCP.CommandAdmin server request // ==> Which admin command use case is detected ? // ==> Does this command afford to be treated by daemon itself ? (enable_fork=0) if (tcp_admcmd_req) { // Read socket, store it in options.admincommand, parse it and isolate which admin usecase is requested // next implementation : continue connection to acknowlegde ...(and close sd_client) admin_command_request_type = parse_admcmd_request(sd_client, options.admincommand, options_admincommand_len); switch(admin_command_request_type) { case adm_unknown: //unknown command detected enable_fork = 0; // UNKNOWN is no heavy load task, no need to fork() logv("Admin command unknown : %s",options.admincommand); break; case adm_stop: enable_fork = 0; // STOP is no heavy load task, no need to fork() logv ("%s",msg_noforkneeded); handle_admincommand_stop(getpid()); break; case adm_forcestop: enable_fork = 0; // FORCESTOP is no heavy load task, no need to fork() logv ("%s",msg_noforkneeded); handle_admincommand_forcestop(); break; case adm_reloadconfig: enable_fork = 0; // RELOADCONFIG is no heavy load task, no need to fork() logv ("%s",msg_noforkneeded); handle_admincommand_reloadconfig(); break; case adm_gettxtconfig: enable_fork = 0; // GET_TXT_CONFIG is no heavy load task, no need to fork() logv ("%s",msg_noforkneeded); handle_admincommand_gettxtconfig(sd_client); break; default: break; } // admin light task are done, then close socket if (!enable_fork) { close(sd_client); } } // if admin command from TCP receiver // =================================================================================================== // if heavy resources task requiring a fork if (enable_fork) { daemon_pid = getpid(); child_pid = fork(); switch (child_pid) { case -1: // Fork error wile attempting to fork log("Err: fork: %s", strerror(errno)); exit(1); case 0: // Child execution close(sd_server); // during fork, a heritage of link on sd_server // close is needed to let parent daemon manage this socket sd_server // TCP.PCLINK or TCP.KML comes from KiSS player if ( tcp_pclink_req || tcp_kml_req ) { logv("%s", "KiSS started connection"); if (tcp_pclink_req) handle_pclink_request(sd_client); if (tcp_kml_req) handle_kml_request(sd_client); } // OGG Vorbis media "KiSS unclosed socket" handling (until solving by a new firmware) if (is_socket_timed_out) shutdown(sd_client, SHUT_RDWR); // Child should close the socket accepted before forking (the server should also, BTW) close(sd_client); // closed connection log message if ((tcp_pclink_req || tcp_kml_req) && !(is_socket_timed_out) ) logv("%s","KiSS closed connection"); else if ((tcp_pclink_req || tcp_kml_req) && (is_socket_timed_out) ) logv("%s","kissdx shut down and closed connection"); //logv("Child process termination exit [%d]",getpid()); _exit(0); default: // Parent execution logv("kissdx fork child for services [%d]",child_pid); close(sd_client); // Server action of closing socket accpeted before forking break; } } // if heavy timeconsuming task requiring a fork } goto again; /* not reached */ } // ======================================================== static void usage(const char *program, int verbose) { fprintf(stderr, "\n" "KiSS PC-Link daemon " KISSDX_VERSION " - " KISSDX_VERSION_DATE "\n\n" "Usage: %s [media center options] | [admin options]\n" "\nNote: Option sets 'Media' and 'Admin' are mutually exclusive.\n" "kissdx may be invoked as a server: Media center\n" "kissdx may be invoded as a client: Administration command\n" "\nMedia center options:\n" "---------------------\n" " -c, --config=FILE path to the configuration file\n" " -d, --daemon fork into background and log using syslog\n" " -h, --help print this on-line help\n" " -v, --verbose log every request and response\n" "\nAdministration options: -s COMMAND [-m HOST] [-p PORT]\n" "-----------------------\n" " -s, --send COMMAND Invoke admin controller with COMMAND\n" " -m, --mediacenter Target name/IP to send command (default: localhost)\n" " -p, --port TCP port to send admin command (default: 8003)\n" "\nAdministration commands: -s COMMAND\n" "------------------------------\n" " -s STOP Stop kissdx mediacenter gracefully\n" " -s FORCESTOP Stop kissdx mediacenter immediately\n" " -s RELOAD_CONFIG Reload configuration file\n" " -s GET_TXT_CONFIG Show currently loaded configuration\n" "\nSamples:\n" "-------\n" " kissdx -vh Verbose help including network TCP port usage\n" " kissdx -v -s STOP Graceful stop (local run): Current streaming continues\n" " kissdx -s STOP -p 8003 -m media.home.fr\n" " Remote stopping of a media center\n" , program); if (verbose) { fprintf(stderr, "\nNetwork topology (best viewed in window 120 chars wide):\n" "--------------------------------------------------------\n\n" "/----------\\ /----------------------------------\\ \n" "| kissdx | | KiSS player |\n" "| daemon | <== Request UDP:8000 <== ARE_YOU_KISS_PCLINK_SERVER? | Menu:Searching PCLINK host |\n" "| daemon | ==> Response UDP:8000 ==== HOSTNAME ===============================> | (identification of mediacenter) |\n" "| | | |\n" "| daemon | <== Request PCLINK:8000 <== GET, SIZE, LIST, ACTION1, ACTION2 | Action from remote control |\n" "| (fork) | ==> Response PCLINK:8000 ==== MEDIA or BROWSING LIRARY ============> | (streaming video,music,picture) |\n" "| | | |\n" "| daemon | <== Request KML:8888 <== GET index.kml | Action from remote control |\n" "| (fork) | ==> Response KML:8888 ==== Any KML content page ================> | (browsing like web does) |\n" "| | | |\n" "| | \\----------------------------------/\n" "| |\n" "| | /--------------------------------\\ \n" "| daemon | <== Request ADMCMD:8003 <== STOP, FORCESTOP, RELOADCONFIG,... | kissdx or any ADMCMD client |\n" "| (fork) | ==> Response ADMCMD:8003 ==========================================> | administration information |\n" "| | | |\n" "\\----------/ \\--------------------------------/\n" "\n" ); } // end verbose mode of help usage screen } // ======================================================== int main(int argc, char *argv[]) { int c; /* getopt_long stores the option index here. */ int option_index = 0; short commandline_error = 0; // flag of command line argument error detection short admincommandmode = 0; // flag of admin command mode detection short mediastreamingmode = 0; // flag of media streaming server mode detection const int verbose_usage = 1; static struct option long_options[] = { { "config", required_argument, NULL, 'c' }, // 1= one parameter is requested (the configuration file) { "daemon", no_argument, NULL, 'd' }, { "help", no_argument, NULL, 'h' }, { "verbose", no_argument, NULL, 'v' }, // Administration feature options { "send", required_argument, NULL, 's' }, // parameter is the command to send to media center { "mediacenter",required_argument, NULL, 'm' }, // Full hostname of mediacenter to administrate { "port", required_argument, NULL, 'p' }, // TCP Port to connect mediacenter and send admin command { NULL, 0, NULL, 0 } // standard ending records for options feature in GNU C }; setlocale (LC_ALL, ""); memset(&options, 0, sizeof (options)); // Default value of options options.mediacenter = (char *)inetAddr_MediaCenter ; // localhost mediacenter is targeted options.adminserver_port = PORT_ADMCMD; // default port is 8003 options.help = 0; // no help request (in command line argument) options.verbose = 0; // no verbose mode in command line argument while (1) { c = getopt_long(argc, argv, "c:dhvs:m:p:", long_options, &option_index); if (c == -1) { break; } switch (c) { case 'c': // Configuration file mediastreamingmode = 1; options.config_file = optarg; break; case 'd': // Daemon mediastreamingmode = 1; options.daemonize = 1; break; case 'v': // Verbose options.verbose = 1; break; // Administration command options case 's': // Send admin command to mediacenter admincommandmode = 1; if (strlen(optarg) > 0) { log("Admin command requested : %s",optarg); strncpy(options.admincommand, optarg, sizeof(options.admincommand)); } else { // wrong admin command strncpy(options.admincommand, "ERROR", sizeof(options.admincommand)); } break; case 'm': // Mediacenter full hostname or IP adress for Admin command admincommandmode = 1; options.mediacenter = optarg; break; case 'p': // Specify port of media center to be sent an admin command options.adminserver_port = atoi(optarg); adminserver_port_CLO = 1; // flag : Commmand line options 'adminserver_port' priority on kissdx_conf_options break; case '?': // Argument missing or not consistent with declaration "ac:dhkvs:m:p:" => EXIT commandline_error=1; case 'h': // Help mode (cumulative with verbose mode) options.help = 1; break; //default: } } // Section for Options Consistency Check of Command line parameter // Does not accept a "Admin command mode " + "daemon config mode" ..... // answer is returned with an usage(argv[0]);exit(EX_USAGE); // many test should validate the acceptance of command requested // ex: -vc kissdx.conf -s STOP -s FORCESTOP -s STATUS -a ==> need usage() to be displayed again // First: if command line options error => help and quit if (commandline_error) { usage(argv[0],verbose_usage); exit(EX_USAGE); } // error in command line options if (options.help) { usage (argv[0],options.verbose); exit(EX_USAGE); } // help request // Admin command SEND + [Any All-Config-Daemon-Kml] option => help and quit if ( (admincommandmode) && (mediastreamingmode) ) { usage(argv[0],options.verbose); exit(EX_USAGE); } // Admin command SEND + COMMAND not in ['STOP','FORCESTOP','RELOAD_CONFIG' ....] => help and quit if (strlen(options.admincommand) > 0) { if ( (strcasecmp(options.admincommand, admincommand_STOP)) && (strcasecmp(options.admincommand, admincommand_RELOADCONFIG)) && (strcasecmp(options.admincommand, admincommand_FORCESTOP)) && (strcasecmp(options.admincommand, "LIST_VIDEO")) && (strcasecmp(options.admincommand, admincommand_GET_TXT_CONFIG)) && (strcasecmp(options.admincommand, "SET_LIST_HIDDEN_ENTRIES_ON")) ) { log("Admin command not supported : %s",options.admincommand); usage(argv[0],options.verbose); exit(EX_USAGE); } } // === kissdx used as an Admin Command client if (strlen(options.admincommand) > 0) { log("kissdx Administration Client with command [%s][%d]", options.admincommand,strlen(options.admincommand)); //logv("%s %s","ADMCMD Command request : ", options.admincommand); //logv("%s %s","ADMCMD Mediacenter name : ", options.mediacenter); //logv("%s %d","ADMCMD Mediacenter tcp.port: ", options.adminserver_port); if (!strcasecmp(options.admincommand, admincommand_STOP)) { logv("%s %s","ADMCMD STOP Command request : ", options.admincommand); deliver_admcmd((char *)admincommand_STOP,"NORESPONSE"); // If a exchange protocol is developped for this option, gather it in a function // perform_admcmd_stop("STOP"); exit(0);} if (!strcasecmp(options.admincommand, admincommand_FORCESTOP)) { logv("%s %s","ADMCMD FORCESTOP Command request : ", options.admincommand); deliver_admcmd((char *)admincommand_FORCESTOP,"NORESPONSE"); // If a exchange protocol is developped for this option, gather it in a function // perform_admcmd_forcestop("FORCESTOP"); exit(0);} if (!strcasecmp(options.admincommand, admincommand_RELOADCONFIG)) { logv("%s %s","ADMCMD RELOAD Command request : ", options.admincommand); deliver_admcmd((char *)admincommand_RELOADCONFIG,"NORESPONSE"); // If a exchange protocol is developped for this option, gather it in a function //perform_admcmd_reloadconfig("RELOAD_CONFIG"); exit(0);} if (!strcasecmp(options.admincommand,"SET_LIST_HIDDEN_ENTRIES_ON")) { logv("%s %s","ADMCMD SET_LIST_HIDDEN_ENTRIES_ON Command request : ", options.admincommand); deliver_admcmd((char *)"SET_LIST_HIDDEN_ENTRIES_ON","NORESPONSE"); //perform_admcmd_reloadconfig("RELOAD_CONFIG"); exit(0);} // [LIST_VIDEO] This feature is a first step to provide a KiSS player simulator : here is LIST_VIDEO command // Beta test with kissdx PcLink server are ok/ Direct connection to Kiss DP558.PcLink failed if (!strcasecmp(options.admincommand,"LIST_VIDEO")) { logv("%s %s","ADMCMD LIST_VIDEO Command request : ", options.admincommand); simulplayer((char *)"LIST_VIDEO","RESPONSE"); exit(0);} // [GET_TXT_CONFIG] admin command if (!strcasecmp(options.admincommand, admincommand_GET_TXT_CONFIG)) { logv("%s %s","ADMCMD GET_TXT_CONFIG Command request : ", options.admincommand); get_txt_config((char *)admincommand_GET_TXT_CONFIG,"RESPONSE"); exit(0);} } // === kissdx start protocol to be a streaming server ================================================= // First parse config before any operation if (parse_config(&config)) { // First parsing of configuratio could not afford a fatal error log("kissdx is unable to start due to bad configuration%s","."); exit(1); } if (options.daemonize) { int fd; switch(fork()) { case 0: break; case -1: perror("fork"); exit(1); default: _exit(0); } if (setsid() < 0) { perror("setsid"); } switch(fork()) { case 0: break; case -1: perror("fork 2"); exit(1); default: _exit(0); } chdir("/"); // Avoid tying up access to whatever dir we were run from close(0); close(1); close(2); if ((fd = open("/dev/null", O_RDONLY)) != 0) { dup2(fd, 0); close(fd); } if ((fd = open("/dev/null", O_WRONLY)) != 1) { dup2(fd, 1); close(fd); } if ((fd = open("/dev/null", O_WRONLY)) != 2) { dup2(fd, 2); close(fd); } do_daemon(); } else do_daemon(); exit(0); }