blob: a1a85f00040af6e7ce38c9e0a248ae5419089a95 [file] [log] [blame]
/*
* Copyright (c) 2014 Netflix, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY NETFLIX, INC. AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NETFLIX OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <string>
#include "DialDiscovery.h"
#include "DialConformance.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
using namespace std;
static DialDiscovery* gpDiscovery;
static bool gUseMenu = true;
// TODO: Make it possible to pass applications from the command line
static vector<string> gAppList;
static string gOutputFile;
static string gInputFile;
// IP address of the DIAL server
static string gIpAddress;
static void printServerList( vector<DialServer*> list )
{
int i;
vector<DialServer*>::iterator it;
for( i = 0, it = list.begin(); it < list.end(); it++, i++ )
{
string uuid, name, macAddr;
int wolTimeOut;
(*it)->getFriendlyName( name );
(*it)->getUuid( uuid );
macAddr =(*it)->getMacAddress();
wolTimeOut =(*it)->getWakeOnLanTimeout();
printf("%Zu: Server IP[%s] UUID[%s] FriendlyName[%s] MacAddress[%s] WakeOnLanTimeout[%d]\n",
i+1, (*it)->getIpAddress().c_str(),
uuid.c_str(), name.c_str(), macAddr.c_str(), wolTimeOut);
}
}
static DialServer* getServerFromUser( vector<DialServer*> list )
{
DialServer* pServer;
// show a list to the user
char buf[80] = {0,};
vector<DialServer*>::iterator it;
printf("Found Multiple servers\n");
printf("0: Rescan and list DIAL servers\n");
printServerList(list);
printf("Enter server: ");
scanf("%s", buf);
unsigned int server = atoi(buf);
if( server > 0 && server <= list.size()){
pServer = list[server-1];
}else{
pServer = NULL;
}
return pServer;
}
static void runConformance()
{
vector<DialServer*> list;
gpDiscovery->getServerList(list);
if( list.size() )
{
DialServer *pServer = NULL;
if( !gIpAddress.empty() )
{
pServer = NULL;
vector<DialServer*>::iterator it;
for( it = list.begin(); it < list.end(); it ++ )
{
if( gIpAddress.compare((*it)->getIpAddress()) == 0 )
{
ATRACE("Found server %s in the list of servers\n",
gIpAddress.c_str() );
pServer = (*it);
break;
}
}
}
else
{
pServer = getServerFromUser( list );
}
if( pServer )
{
string name;
bool serverExists = pServer->getFriendlyName(name);
assert( serverExists );
string uuid;
pServer->getUuid( uuid );
printf("\nRunning conformance against: IP[%s] UUID[%s] FriendlyName[%s] \n",
pServer->getIpAddress().c_str(),
uuid.c_str(), name.c_str() );
DialConformance::instance()->run(
pServer,
gAppList,
gInputFile,
gOutputFile );
}
else
{
printf("DIAL server not found\n");
printf("%Zu available server(s): \n", list.size());
printServerList(list);
}
}
else
{
printf("No servers available\n");
}
}
static void sendMagic(string macAddr)
{
unsigned char tosend[102];
unsigned char mac[6];
/** first 6 bytes of 255 **/
for(int i = 0; i < 6; i++) {
tosend[i] = 0xFF;
}
/** store mac address **/
printf("sending magic packet to: ");
for (int i=0; i<6; i++){
mac[i]=(unsigned char)(strtoul(macAddr.substr(i*3, 2).c_str(), NULL, 16));
printf("%02x:", mac[i]);
}
printf("\n");
/** append it 16 times to packet **/
for(int i = 1; i <= 16; i++) {
memcpy(&tosend[i * 6], &mac, 6 * sizeof(unsigned char));
}
int udpSocket;
struct sockaddr_in udpClient, udpServer;
int broadcast = 1;
udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
/** you need to set this so you can broadcast **/
if (setsockopt(udpSocket, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof broadcast) == -1) {
perror("setsockopt (SO_BROADCAST)");
exit(1);
}
udpClient.sin_family = AF_INET;
udpClient.sin_addr.s_addr = INADDR_ANY;
udpClient.sin_port = 0;
bind(udpSocket, (struct sockaddr*)&udpClient, sizeof(udpClient));
/** set server end point (the broadcast addres)**/
udpServer.sin_family = AF_INET;
udpServer.sin_addr.s_addr = inet_addr("255.255.255.255");
udpServer.sin_port = htons(9);
/** send the packet **/
sendto(udpSocket, &tosend, sizeof(unsigned char) * 102, 0, (struct sockaddr*)&udpServer, sizeof(udpServer));
}
int handleUser(DialDiscovery *pDial) {
int processInputOuter = 1;
char buf[80];
vector<DialServer*> list;
while (processInputOuter){
pDial->getServerList(list);
if( list.size() == 0 ){
printf("No servers available\n");
return 1;
}
DialServer* pServer = getServerFromUser( list );
if (pServer==NULL){
pDial->send_mcast();
continue;
}
int processInput = 1;
while(processInput){
string responseHeaders, responseBody, payload;
string netflix = "Netflix";
string youtube = "YouTube";
memset(buf, 0, 80);
printf("0. Rescan and list DIAL servers\n");
printf("1. Launch Netflix\n");
printf("2. Kill Netflix\n");
printf("3. Netflix status\n");
printf("4. Launch YouTube\n");
printf("5. Kill YouTube\n");
printf("6. YouTube status\n");
printf("7. Run conformance tests\n");
printf("8. Wake up on lan/wlan\n");
printf("9. QUIT\n");
printf("Command (0:1:2:3:4:5:6:7:8:9): ");
scanf("%s", buf);
switch( atoi(buf) )
{
case 0:
{
pDial->send_mcast();
processInput=0;
}break;
case 1:
printf("Launch Netflix\n");
pServer->launchApplication( netflix, payload, responseHeaders, responseBody );
break;
case 2:
printf("Kill Netflix\n");
pServer->stopApplication( netflix, responseHeaders );
break;
case 3:
printf("Netflix Status: \n");
pServer->getStatus( netflix, responseHeaders, responseBody );
printf("RESPONSE: \n%s\n", responseBody.c_str());
break;
case 4:
printf("Launch YouTube\n");
pServer->launchApplication( youtube, payload, responseHeaders, responseBody );
break;
case 5:
printf("Kill YouTube\n");
pServer->stopApplication( youtube, responseHeaders );
break;
case 6:
printf("YouTube Status: \n");
pServer->getStatus( youtube, responseHeaders, responseBody );
break;
case 7:
runConformance();
break;
case 8:
printf("Sending the magic packet\n");
sendMagic(pServer->getMacAddress());
break;
case 9:
processInput = 0;
processInputOuter = 0;
break;
default:
printf("Invalid, try again\n");
}
}
}
return 0;
}
static const char usage[] = "\n"
" If no option is specified, the program will run a conformance test.\n"
"\n"
"usage: dialclient <option>\n"
" Option Parameter Description\n"
" -h none Usage menu\n"
" -s filename (optional) Run conformance test. Use filename as\n"
" the input, if provided\n"
" -o filename Reporter output file (./report.html)\n"
" -a ip_address IP address of DIAL server (used for conformance\n"
" testing)\n"
"\n";
inline void notSupported( string s )
{
printf( "%s not supported", s.c_str() );
printf( "%s\n", usage );
exit(0);
}
int parseArgs( int argc, char* argv[] )
{
for( int i = 1; i < argc; i++ )
{
string input(argv[i]);
if( input[0] != '-' ) notSupported(input);
switch(input[1])
{
case 's':
gUseMenu = false;
if( argv[i+1] != NULL && argv[i+1][0] != '-' )
{
//filename provided
gInputFile = argv[++i];
}
break;
case 'o':
gOutputFile = argv[++i];
break;
case 'a':
gIpAddress = argv[++i];
break;
case 'h':
printf("%s", usage);
exit(0);
break;
default:
notSupported(input);
}
}
return 0;
}
int main(int argc, char* argv[]) {
parseArgs(argc, argv);
gpDiscovery = DialDiscovery::create();
gpDiscovery->init();
DialConformance::create();
// Sleep for 2 seconds to allow DIAL servers to response to MSEARCH.
sleep(2);
if ( gUseMenu )
{
return handleUser(gpDiscovery);
}
// not using the menu, just run the conformance test.
runConformance();
return 0;
}