mirror of
https://github.com/Netflix/dial-reference.git
synced 2026-06-08 10:59:59 +00:00
Added app manager IPC interface. Updated verions to 2.1
This commit is contained in:
@@ -157,21 +157,21 @@ private:
|
||||
*/
|
||||
void stop(
|
||||
string& responseHeaders )
|
||||
{ stop(_stopurl, responseHeaders ); }
|
||||
{ stop(_name, responseHeaders ); }
|
||||
|
||||
/**
|
||||
* Stop the application using a custom stop URL.
|
||||
*
|
||||
* @param[out] responseHeaders HTML response headers
|
||||
*/
|
||||
void stop( string& stopurl, string& responseHeaders)
|
||||
void stop( string& appName, string& responseHeaders)
|
||||
{
|
||||
if( !stopurl.empty() )
|
||||
if( !appName.empty() )
|
||||
{
|
||||
_pServer->stopApplication( stopurl, responseHeaders );
|
||||
_pServer->stopApplication( appName, responseHeaders );
|
||||
}
|
||||
#ifdef DEBUG
|
||||
else ATRACE("%s: Not sending stop, stop URL is empty\n", __FUNCTION__);
|
||||
else ATRACE("%s: Not sending stop, appName is empty\n", __FUNCTION__);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -263,7 +263,7 @@ int DialServer::hideApplication(
|
||||
getStatus( application, responseHeaders, responseBody );
|
||||
|
||||
int status = sendCommand(
|
||||
(appUrl.append(application)).append("/"+m_stopEndPoint).append("/suspend"),
|
||||
(appUrl.append(application)).append("/"+m_stopEndPoint).append("/hide"),
|
||||
COMMAND_HIDE, emptyPayload, responseHeaders, responseBody );
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
TARGET=/usr/local/i686-netflix-linux-gnu-4.2/bin/i686-netflix-linux-gnu- \
|
||||
LDFLAGS="-L/usr/local/i686-netflix-linux-gnu-4.2/netflix/lib \
|
||||
-Wl,-rpath,/usr/local/i686-netflix-linux-gnu-4.2/netflix/lib" \
|
||||
INCLUDES=-I/usr/local/i686-netflix-linux-gnu-4.2/netflix/include \
|
||||
TARGET=/usr/local/i686-netflix-linux-gnu-4.3/bin/i686-netflix-linux-gnu- \
|
||||
LDFLAGS="-L/usr/local/i686-netflix-linux-gnu-4.3/netflix/lib \
|
||||
-Wl,-rpath,/usr/local/i686-netflix-linux-gnu-4.3/netflix/lib" \
|
||||
INCLUDES=-I/usr/local/i686-netflix-linux-gnu-4.3/netflix/include \
|
||||
make
|
||||
|
||||
@@ -223,11 +223,11 @@ int handleUser(DialDiscovery *pDial) {
|
||||
printf("0. Rescan and list DIAL servers\n");
|
||||
printf("1. Launch Netflix\n");
|
||||
printf("2. Hide Netflix\n");
|
||||
printf("3. Kill Netflix\n");
|
||||
printf("3. Stop Netflix\n");
|
||||
printf("4. Netflix status\n");
|
||||
printf("5. Launch YouTube\n");
|
||||
printf("6. Hide YouTube\n");
|
||||
printf("7. Kill YouTube\n");
|
||||
printf("7. Stop YouTube\n");
|
||||
printf("8. YouTube status\n");
|
||||
printf("9. Run conformance tests\n");
|
||||
printf("10. Wake up on lan/wlan\n");
|
||||
@@ -250,7 +250,7 @@ int handleUser(DialDiscovery *pDial) {
|
||||
pServer->hideApplication( netflix, responseHeaders, responseBody );
|
||||
break;
|
||||
case 3:
|
||||
printf("Kill Netflix\n");
|
||||
printf("Stop Netflix\n");
|
||||
pServer->stopApplication( netflix, responseHeaders );
|
||||
break;
|
||||
case 4:
|
||||
@@ -267,7 +267,7 @@ int handleUser(DialDiscovery *pDial) {
|
||||
pServer->hideApplication( youtube, responseHeaders, responseBody );
|
||||
break;
|
||||
case 7:
|
||||
printf("Kill YouTube\n");
|
||||
printf("Stop YouTube\n");
|
||||
pServer->stopApplication( youtube, responseHeaders );
|
||||
break;
|
||||
case 8:
|
||||
|
||||
@@ -217,6 +217,18 @@ static void handle_app_status(struct mg_connection *conn,
|
||||
|
||||
app->state = app->callbacks.status_cb(ds, app_name, app->run_id, &canStop,
|
||||
app->callback_data);
|
||||
char dial_state_str[20];
|
||||
switch(app->state){
|
||||
case kDIALStatusHide:
|
||||
strcpy (dial_state_str, "hidden");
|
||||
break;
|
||||
case kDIALStatusRunning:
|
||||
strcpy (dial_state_str, "running");
|
||||
break;
|
||||
default:
|
||||
strcpy (dial_state_str, "stopped");
|
||||
}
|
||||
|
||||
mg_printf(
|
||||
conn,
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
@@ -237,7 +249,7 @@ static void handle_app_status(struct mg_connection *conn,
|
||||
DIAL_VERSION,
|
||||
app->name,
|
||||
canStop ? "true" : "false",
|
||||
app->state ? "running" : "stopped",
|
||||
dial_state_str,
|
||||
app->state == kDIALStatusStopped ?
|
||||
"" : " <link rel=\"run\" href=\"run\"/>\r\n",
|
||||
dial_data);
|
||||
@@ -261,7 +273,7 @@ static void handle_app_stop(struct mg_connection *conn,
|
||||
&canStop, app->callback_data);
|
||||
}
|
||||
|
||||
if (!app || app->state != kDIALStatusRunning) {
|
||||
if (!app || app->state == kDIALStatusStopped) {
|
||||
mg_send_http_error(conn, 404, "Not Found", "Not Found");
|
||||
} else {
|
||||
app->callbacks.stop_cb(ds, app_name, app->run_id, app->callback_data);
|
||||
@@ -449,7 +461,7 @@ static int is_allowed_origin(DIALServer* ds, char * origin, const char * app_nam
|
||||
|
||||
#define APPS_URI "/apps/"
|
||||
#define RUN_URI "/run"
|
||||
#define HIDE_URI "/suspend"
|
||||
#define HIDE_URI "/hide"
|
||||
|
||||
static void *options_response(DIALServer *ds, struct mg_connection *conn, char *host_header, char *origin_header, const char* app_name, const char* methods)
|
||||
{
|
||||
|
||||
@@ -28,6 +28,13 @@
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
//#define DEBUG
|
||||
#ifdef DEBUG
|
||||
#define ATRACE(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define ATRACE(...)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Dial application states
|
||||
*/
|
||||
@@ -40,7 +47,7 @@ typedef enum {
|
||||
/*
|
||||
* DIAL version that is reported via in the status response.
|
||||
*/
|
||||
#define DIAL_VERSION ("\"2.0\"")
|
||||
#define DIAL_VERSION ("\"2.1\"")
|
||||
|
||||
/*
|
||||
* The maximum DIAL payload accepted per the DIAL 1.6.1 specification.
|
||||
|
||||
311
server/jsmn.c
Normal file
311
server/jsmn.c
Normal file
@@ -0,0 +1,311 @@
|
||||
#include "jsmn.h"
|
||||
|
||||
/**
|
||||
* Allocates a fresh unused token from the token pull.
|
||||
*/
|
||||
static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
|
||||
jsmntok_t *tokens, size_t num_tokens) {
|
||||
jsmntok_t *tok;
|
||||
if (parser->toknext >= num_tokens) {
|
||||
return NULL;
|
||||
}
|
||||
tok = &tokens[parser->toknext++];
|
||||
tok->start = tok->end = -1;
|
||||
tok->size = 0;
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
tok->parent = -1;
|
||||
#endif
|
||||
return tok;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills token type and boundaries.
|
||||
*/
|
||||
static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
|
||||
int start, int end) {
|
||||
token->type = type;
|
||||
token->start = start;
|
||||
token->end = end;
|
||||
token->size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills next available token with JSON primitive.
|
||||
*/
|
||||
static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
|
||||
size_t len, jsmntok_t *tokens, size_t num_tokens) {
|
||||
jsmntok_t *token;
|
||||
int start;
|
||||
|
||||
start = parser->pos;
|
||||
|
||||
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
|
||||
switch (js[parser->pos]) {
|
||||
#ifndef JSMN_STRICT
|
||||
/* In strict mode primitive must be followed by "," or "}" or "]" */
|
||||
case ':':
|
||||
#endif
|
||||
case '\t' : case '\r' : case '\n' : case ' ' :
|
||||
case ',' : case ']' : case '}' :
|
||||
goto found;
|
||||
}
|
||||
if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
}
|
||||
#ifdef JSMN_STRICT
|
||||
/* In strict mode primitive must be followed by a comma/object/array */
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_PART;
|
||||
#endif
|
||||
|
||||
found:
|
||||
if (tokens == NULL) {
|
||||
parser->pos--;
|
||||
return 0;
|
||||
}
|
||||
token = jsmn_alloc_token(parser, tokens, num_tokens);
|
||||
if (token == NULL) {
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_NOMEM;
|
||||
}
|
||||
jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
token->parent = parser->toksuper;
|
||||
#endif
|
||||
parser->pos--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills next token with JSON string.
|
||||
*/
|
||||
static int jsmn_parse_string(jsmn_parser *parser, const char *js,
|
||||
size_t len, jsmntok_t *tokens, size_t num_tokens) {
|
||||
jsmntok_t *token;
|
||||
|
||||
int start = parser->pos;
|
||||
|
||||
parser->pos++;
|
||||
|
||||
/* Skip starting quote */
|
||||
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
|
||||
char c = js[parser->pos];
|
||||
|
||||
/* Quote: end of string */
|
||||
if (c == '\"') {
|
||||
if (tokens == NULL) {
|
||||
return 0;
|
||||
}
|
||||
token = jsmn_alloc_token(parser, tokens, num_tokens);
|
||||
if (token == NULL) {
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_NOMEM;
|
||||
}
|
||||
jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
token->parent = parser->toksuper;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Backslash: Quoted symbol expected */
|
||||
if (c == '\\' && parser->pos + 1 < len) {
|
||||
int i;
|
||||
parser->pos++;
|
||||
switch (js[parser->pos]) {
|
||||
/* Allowed escaped symbols */
|
||||
case '\"': case '/' : case '\\' : case 'b' :
|
||||
case 'f' : case 'r' : case 'n' : case 't' :
|
||||
break;
|
||||
/* Allows escaped symbol \uXXXX */
|
||||
case 'u':
|
||||
parser->pos++;
|
||||
for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
|
||||
/* If it isn't a hex character we have an error */
|
||||
if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
|
||||
(js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
|
||||
(js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
parser->pos++;
|
||||
}
|
||||
parser->pos--;
|
||||
break;
|
||||
/* Unexpected symbol */
|
||||
default:
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_PART;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse JSON string and fill tokens.
|
||||
*/
|
||||
int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
|
||||
jsmntok_t *tokens, unsigned int num_tokens) {
|
||||
int r;
|
||||
int i;
|
||||
jsmntok_t *token;
|
||||
int count = parser->toknext;
|
||||
|
||||
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
|
||||
char c;
|
||||
jsmntype_t type;
|
||||
|
||||
c = js[parser->pos];
|
||||
switch (c) {
|
||||
case '{': case '[':
|
||||
count++;
|
||||
if (tokens == NULL) {
|
||||
break;
|
||||
}
|
||||
token = jsmn_alloc_token(parser, tokens, num_tokens);
|
||||
if (token == NULL)
|
||||
return JSMN_ERROR_NOMEM;
|
||||
if (parser->toksuper != -1) {
|
||||
tokens[parser->toksuper].size++;
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
token->parent = parser->toksuper;
|
||||
#endif
|
||||
}
|
||||
token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
|
||||
token->start = parser->pos;
|
||||
parser->toksuper = parser->toknext - 1;
|
||||
break;
|
||||
case '}': case ']':
|
||||
if (tokens == NULL)
|
||||
break;
|
||||
type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
if (parser->toknext < 1) {
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
token = &tokens[parser->toknext - 1];
|
||||
for (;;) {
|
||||
if (token->start != -1 && token->end == -1) {
|
||||
if (token->type != type) {
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
token->end = parser->pos + 1;
|
||||
parser->toksuper = token->parent;
|
||||
break;
|
||||
}
|
||||
if (token->parent == -1) {
|
||||
break;
|
||||
}
|
||||
token = &tokens[token->parent];
|
||||
}
|
||||
#else
|
||||
for (i = parser->toknext - 1; i >= 0; i--) {
|
||||
token = &tokens[i];
|
||||
if (token->start != -1 && token->end == -1) {
|
||||
if (token->type != type) {
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
parser->toksuper = -1;
|
||||
token->end = parser->pos + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Error if unmatched closing bracket */
|
||||
if (i == -1) return JSMN_ERROR_INVAL;
|
||||
for (; i >= 0; i--) {
|
||||
token = &tokens[i];
|
||||
if (token->start != -1 && token->end == -1) {
|
||||
parser->toksuper = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case '\"':
|
||||
r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
|
||||
if (r < 0) return r;
|
||||
count++;
|
||||
if (parser->toksuper != -1 && tokens != NULL)
|
||||
tokens[parser->toksuper].size++;
|
||||
break;
|
||||
case '\t' : case '\r' : case '\n' : case ' ':
|
||||
break;
|
||||
case ':':
|
||||
parser->toksuper = parser->toknext - 1;
|
||||
break;
|
||||
case ',':
|
||||
if (tokens != NULL && parser->toksuper != -1 &&
|
||||
tokens[parser->toksuper].type != JSMN_ARRAY &&
|
||||
tokens[parser->toksuper].type != JSMN_OBJECT) {
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
parser->toksuper = tokens[parser->toksuper].parent;
|
||||
#else
|
||||
for (i = parser->toknext - 1; i >= 0; i--) {
|
||||
if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
|
||||
if (tokens[i].start != -1 && tokens[i].end == -1) {
|
||||
parser->toksuper = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
#ifdef JSMN_STRICT
|
||||
/* In strict mode primitives are: numbers and booleans */
|
||||
case '-': case '0': case '1' : case '2': case '3' : case '4':
|
||||
case '5': case '6': case '7' : case '8': case '9':
|
||||
case 't': case 'f': case 'n' :
|
||||
/* And they must not be keys of the object */
|
||||
if (tokens != NULL && parser->toksuper != -1) {
|
||||
jsmntok_t *t = &tokens[parser->toksuper];
|
||||
if (t->type == JSMN_OBJECT ||
|
||||
(t->type == JSMN_STRING && t->size != 0)) {
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* In non-strict mode every unquoted value is a primitive */
|
||||
default:
|
||||
#endif
|
||||
r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
|
||||
if (r < 0) return r;
|
||||
count++;
|
||||
if (parser->toksuper != -1 && tokens != NULL)
|
||||
tokens[parser->toksuper].size++;
|
||||
break;
|
||||
|
||||
#ifdef JSMN_STRICT
|
||||
/* Unexpected char in strict mode */
|
||||
default:
|
||||
return JSMN_ERROR_INVAL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (tokens != NULL) {
|
||||
for (i = parser->toknext - 1; i >= 0; i--) {
|
||||
/* Unmatched opened object or array */
|
||||
if (tokens[i].start != -1 && tokens[i].end == -1) {
|
||||
return JSMN_ERROR_PART;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new parser based over a given buffer with an array of tokens
|
||||
* available.
|
||||
*/
|
||||
void jsmn_init(jsmn_parser *parser) {
|
||||
parser->pos = 0;
|
||||
parser->toknext = 0;
|
||||
parser->toksuper = -1;
|
||||
}
|
||||
|
||||
76
server/jsmn.h
Normal file
76
server/jsmn.h
Normal file
@@ -0,0 +1,76 @@
|
||||
#ifndef __JSMN_H_
|
||||
#define __JSMN_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* JSON type identifier. Basic types are:
|
||||
* o Object
|
||||
* o Array
|
||||
* o String
|
||||
* o Other primitive: number, boolean (true/false) or null
|
||||
*/
|
||||
typedef enum {
|
||||
JSMN_UNDEFINED = 0,
|
||||
JSMN_OBJECT = 1,
|
||||
JSMN_ARRAY = 2,
|
||||
JSMN_STRING = 3,
|
||||
JSMN_PRIMITIVE = 4
|
||||
} jsmntype_t;
|
||||
|
||||
enum jsmnerr {
|
||||
/* Not enough tokens were provided */
|
||||
JSMN_ERROR_NOMEM = -1,
|
||||
/* Invalid character inside JSON string */
|
||||
JSMN_ERROR_INVAL = -2,
|
||||
/* The string is not a full JSON packet, more bytes expected */
|
||||
JSMN_ERROR_PART = -3
|
||||
};
|
||||
|
||||
/**
|
||||
* JSON token description.
|
||||
* @param type type (object, array, string etc.)
|
||||
* @param start start position in JSON data string
|
||||
* @param end end position in JSON data string
|
||||
*/
|
||||
typedef struct {
|
||||
jsmntype_t type;
|
||||
int start;
|
||||
int end;
|
||||
int size;
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
int parent;
|
||||
#endif
|
||||
} jsmntok_t;
|
||||
|
||||
/**
|
||||
* JSON parser. Contains an array of token blocks available. Also stores
|
||||
* the string being parsed now and current position in that string
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned int pos; /* offset in the JSON string */
|
||||
unsigned int toknext; /* next token to allocate */
|
||||
int toksuper; /* superior token node, e.g parent object or array */
|
||||
} jsmn_parser;
|
||||
|
||||
/**
|
||||
* Create JSON parser over an array of tokens
|
||||
*/
|
||||
void jsmn_init(jsmn_parser *parser);
|
||||
|
||||
/**
|
||||
* Run JSON parser. It parses a JSON data string into and array of tokens, each describing
|
||||
* a single JSON object.
|
||||
*/
|
||||
int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
|
||||
jsmntok_t *tokens, unsigned int num_tokens);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __JSMN_H_ */
|
||||
@@ -40,6 +40,7 @@
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "url_lib.h"
|
||||
#include "nf_appmanager.h"
|
||||
|
||||
#define NF_APP_MANAGER
|
||||
@@ -104,49 +105,6 @@ void signalHandler(int signal)
|
||||
}
|
||||
}
|
||||
|
||||
/* The URL encoding source code was obtained here:
|
||||
* http://www.geekhideout.com/urlcode.shtml
|
||||
*/
|
||||
|
||||
/* Converts a hex character to its integer value */
|
||||
char from_hex(char ch) {
|
||||
return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;
|
||||
}
|
||||
|
||||
/* Converts an integer value to its hex character*/
|
||||
char to_hex(char code) {
|
||||
static char hex[] = "0123456789abcdef";
|
||||
return hex[code & 15];
|
||||
}
|
||||
|
||||
/* Returns a url-encoded version of str */
|
||||
/* IMPORTANT: be sure to free() the returned string after use */
|
||||
char *url_encode(const char *str) {
|
||||
const char *pstr;
|
||||
char *buf, *pbuf;
|
||||
pstr = str;
|
||||
buf = malloc(strlen(str) * 3 + 1);
|
||||
pbuf = buf;
|
||||
if( buf )
|
||||
{
|
||||
while (*pstr) {
|
||||
if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~')
|
||||
*pbuf++ = *pstr;
|
||||
else if (*pstr == ' ')
|
||||
*pbuf++ = '+';
|
||||
else
|
||||
*pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15);
|
||||
pstr++;
|
||||
}
|
||||
*pbuf = '\0';
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* End of URL ENCODE source
|
||||
*/
|
||||
|
||||
/*
|
||||
* This function will walk /proc and look for the application in
|
||||
* /proc/<PID>/comm. and /proc/<PID>/cmdline to find it's command (executable
|
||||
|
||||
@@ -3,7 +3,7 @@ CC=$(TARGET)gcc
|
||||
.PHONY: clean
|
||||
.DEFAULT_GOAL=all
|
||||
|
||||
OBJS := main.o dial_server.o mongoose.o quick_ssdp.o url_lib.o dial_data.o LinuxInterfaces.o nf_appmanager.o
|
||||
OBJS := main.o dial_server.o mongoose.o quick_ssdp.o url_lib.o dial_data.o LinuxInterfaces.o nf_appmanager.o mq_ipc.o jsmn.o
|
||||
HEADERS := $(wildcard *.h)
|
||||
|
||||
%.c: $(HEADERS)
|
||||
@@ -14,7 +14,7 @@ HEADERS := $(wildcard *.h)
|
||||
all: dialserver test
|
||||
|
||||
dialserver: $(OBJS)
|
||||
$(CC) -Wall -Werror -g $(OBJS) -ldl -lpthread -o dialserver
|
||||
$(CC) -Wall -Werror -g $(OBJS) -ldl -lpthread -lrt -o dialserver
|
||||
|
||||
test:
|
||||
make -C tests
|
||||
|
||||
130
server/mq_ipc.c
Normal file
130
server/mq_ipc.c
Normal file
@@ -0,0 +1,130 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "mq_ipc.h"
|
||||
|
||||
#define PMODE 0655
|
||||
|
||||
static mqd_t mClientServer=-1;
|
||||
static mqd_t mServerClient=-1;
|
||||
|
||||
int createMessageQ(const char* fd, int flags)
|
||||
{
|
||||
struct mq_attr attr;
|
||||
attr.mq_maxmsg = 10;
|
||||
attr.mq_msgsize = 512;
|
||||
|
||||
mqd_t handle = mq_open(fd, flags, PMODE, &attr);
|
||||
if (handle == -1) {
|
||||
printf("Failed to open Q at %s: %s \n", fd, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
int sendInternal(const char* payload, int timeoutInSec, bool block)
|
||||
{
|
||||
if (mClientServer == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (block) {
|
||||
if (mq_send(mClientServer, payload, strlen(payload)+1, 0) == -1) {
|
||||
printf("Failed to send message: %s \n", strerror(errno));
|
||||
return -1;
|
||||
}else{
|
||||
printf("mq message sent.\n");
|
||||
}
|
||||
} else {
|
||||
struct timespec tm;
|
||||
clock_gettime(CLOCK_REALTIME, &tm);
|
||||
tm.tv_sec += timeoutInSec;
|
||||
|
||||
if (mq_timedsend(mClientServer, payload, strlen(payload)+1, 0, &tm) == -1) {
|
||||
printf("Failed to send message: %s \n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int receiveInternal(char* buffer, int bufferSize, int timeoutInSec, bool block)
|
||||
{
|
||||
/* mqd_t qFd = getMessageQ(fd); */
|
||||
|
||||
if (mServerClient == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (block) {
|
||||
if (mq_receive(mServerClient, buffer, bufferSize, 0) == -1) {
|
||||
printf("Failed to receive message: %s \n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
struct timespec tm;
|
||||
clock_gettime(CLOCK_REALTIME, &tm);
|
||||
tm.tv_sec += timeoutInSec;
|
||||
|
||||
if (mq_timedreceive(mServerClient, buffer, bufferSize, 0, &tm) == -1) {
|
||||
printf("Failed to receive message: %s \n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============== public APIs ===================
|
||||
|
||||
int mq_ipc_connect(const char* clientServer, const char* serverClient)
|
||||
{
|
||||
// Write-only to send message to server
|
||||
mClientServer = createMessageQ(clientServer, O_WRONLY);
|
||||
|
||||
// Read-only to receive messages from server.
|
||||
mServerClient = createMessageQ(serverClient, O_RDONLY);
|
||||
|
||||
if ((mServerClient == -1 ) || (mClientServer == -1)) {
|
||||
printf(" failed to conncet to server \n");
|
||||
return -1;
|
||||
}else{
|
||||
printf("mq connected.\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mq_ipc_disconnect()
|
||||
{
|
||||
mq_close(mClientServer);
|
||||
mq_close(mServerClient);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mq_ipc_send(const char* payload)
|
||||
{
|
||||
return sendInternal(payload, 0, true);
|
||||
}
|
||||
|
||||
int mq_ipc_receive(char* buffer, int bufferSize)
|
||||
{
|
||||
return receiveInternal(buffer, bufferSize, 0, true);
|
||||
}
|
||||
|
||||
int mq_ipc_send_timed(const char* payload, int timeoutInSec)
|
||||
{
|
||||
return sendInternal(payload, timeoutInSec, false);
|
||||
}
|
||||
|
||||
int mq_ipc_receive_timed(char* buffer, int bufferSize, int timeoutInSec)
|
||||
{
|
||||
return receiveInternal(buffer, bufferSize, timeoutInSec, false);
|
||||
}
|
||||
|
||||
|
||||
18
server/mq_ipc.h
Normal file
18
server/mq_ipc.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef _MQ_IPC_H_
|
||||
#define _MQ_IPC_H_
|
||||
|
||||
#include <fcntl.h> /* For O_* constants */
|
||||
#include <sys/stat.h> /* For mode constants */
|
||||
#include <mqueue.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
int mq_ipc_connect(const char* clientServer, const char* serverClient);
|
||||
int mq_ipc_disconnect();
|
||||
|
||||
int mq_ipc_send(const char* payload);
|
||||
int mq_ipc_receive(char* buffer, int bufferSize);
|
||||
|
||||
int mq_ipc_send_timed(const char* payload, int timeoutInSec);
|
||||
int mq_ipc_receive_timed(char* buffer, int bufferSize, int timeoutInSec);
|
||||
|
||||
#endif
|
||||
@@ -1,28 +1,247 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "nf_appmanager.h"
|
||||
#include "mq_ipc.h"
|
||||
#include "jsmn.h"
|
||||
#include "url_lib.h"
|
||||
|
||||
static char *defaultLaunchParam = "source_type=12";
|
||||
static char * mq_send_ch = "/fromAppManager";
|
||||
static char * mq_receive_ch = "/toAppManager";
|
||||
static char * mq_tx_msg_base = " {\"args\":\"%s\",\"message\":%d}";
|
||||
|
||||
static int s_run_id = 0;
|
||||
|
||||
typedef enum ControllerCommand {
|
||||
//-----------------------------------
|
||||
// Commands sent to AM
|
||||
//-----------------------------------
|
||||
CMD_CNT_START, // Launch the app
|
||||
CMD_CNT_HIDE, // Suspend app
|
||||
CMD_CNT_START_FROM_HIDE, // Resume app
|
||||
CMD_CNT_STOP=6, // Terminate app nicely because the user killed Netflix.
|
||||
CMD_CNT_GET_STATUS=8, // Provide status of the app
|
||||
}ControllerCommand_t;
|
||||
|
||||
typedef enum ControllerResp {
|
||||
//-----------------------------------
|
||||
// Resp sent from AM
|
||||
//-----------------------------------
|
||||
RESP_OK,
|
||||
RESP_ERR,
|
||||
}ControllerResp_t;
|
||||
|
||||
typedef enum NetflixStatus {
|
||||
//-----------------------------------
|
||||
// Netflix status
|
||||
//-----------------------------------
|
||||
STATUS_NETFLIX_RUNNING,
|
||||
STATUS_NETFLIX_HIDE1,
|
||||
STATUS_NETFLIX_HIDE2,
|
||||
STATUS_NETFLIX_STOPPED,
|
||||
}NetflixStatus_t;
|
||||
|
||||
|
||||
void errorMsg()
|
||||
{
|
||||
printf("\nIn this mode, a matching application manager must be started and listen on the appropriate IPC.\n\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static int jsoneq(const char *json, jsmntok_t *tok, const char *s) {
|
||||
if (tok->type == JSMN_STRING && (int) strlen(s) == tok->end - tok->start &&
|
||||
strncmp(json + tok->start, s, tok->end - tok->start) == 0) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
NetflixStatus_t parse_nf_state(char* json_str, jsmntok_t* tokens, int num_tokens)
|
||||
{
|
||||
for (int i=0; i<num_tokens; i++){
|
||||
if (jsoneq(json_str, &tokens[i], "state") == 0) {
|
||||
char * tempStr = strndup(json_str+tokens[i+1].start, tokens[i+1].end - tokens[i+1].start);
|
||||
printf ("token: %s\n", tempStr);
|
||||
NetflixStatus_t status = (NetflixStatus_t) (atoi(tempStr));
|
||||
free(tempStr);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
return STATUS_NETFLIX_STOPPED;
|
||||
}
|
||||
|
||||
DIALStatus get_nf_state()
|
||||
{
|
||||
//connect
|
||||
if (mq_ipc_connect(mq_send_ch, mq_receive_ch))
|
||||
exit(1);
|
||||
//send status request
|
||||
char tx_buf[64];
|
||||
snprintf(tx_buf, 64, mq_tx_msg_base, "", CMD_CNT_GET_STATUS);
|
||||
if (mq_ipc_send_timed(tx_buf, 2))
|
||||
errorMsg();
|
||||
//recieve status
|
||||
char rx_buf[512];
|
||||
if(mq_ipc_receive_timed(rx_buf, 512, 2))
|
||||
errorMsg();
|
||||
ATRACE("recieved: \n %s\n", rx_buf);
|
||||
|
||||
mq_ipc_disconnect();
|
||||
|
||||
// parse response
|
||||
jsmn_parser json_parser;
|
||||
jsmntok_t tokens[32]; /* We expect no more than 32 tokens */
|
||||
int num_tokens;
|
||||
jsmn_init(&json_parser);
|
||||
num_tokens = jsmn_parse(&json_parser, rx_buf, strlen(rx_buf), tokens, 32);
|
||||
|
||||
// get status
|
||||
NetflixStatus_t am_status = parse_nf_state(rx_buf, tokens, num_tokens);
|
||||
|
||||
switch (am_status){
|
||||
case STATUS_NETFLIX_HIDE1:
|
||||
case STATUS_NETFLIX_HIDE2:
|
||||
// hide state
|
||||
return kDIALStatusHide;
|
||||
break;
|
||||
case STATUS_NETFLIX_STOPPED:
|
||||
// stopped state
|
||||
return kDIALStatusStopped;
|
||||
break;
|
||||
case STATUS_NETFLIX_RUNNING:
|
||||
// running state
|
||||
return kDIALStatusRunning;
|
||||
break;
|
||||
default:
|
||||
// error state
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return kDIALStatusStopped;
|
||||
}
|
||||
|
||||
ControllerResp_t parse_am_status(char* buf)
|
||||
{
|
||||
jsmn_parser json_parser;
|
||||
jsmntok_t tokens[32]; /* We expect no more than 32 tokens */
|
||||
int num_tokens;
|
||||
jsmn_init(&json_parser);
|
||||
num_tokens = jsmn_parse(&json_parser, buf, strlen(buf), tokens, 32);
|
||||
|
||||
for (int i=0; i<num_tokens; i++){
|
||||
if (jsoneq(buf, &tokens[i], "state") == 0) {
|
||||
char * tempStr = strndup(buf+tokens[i+1].start, tokens[i+1].end - tokens[i+1].start);
|
||||
printf ("token: %s\n", tempStr);
|
||||
ControllerResp_t status = (ControllerResp_t) (atoi(tempStr));
|
||||
free(tempStr);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
return RESP_ERR;
|
||||
}
|
||||
|
||||
/******************* public API ********************/
|
||||
DIALStatus am_netflix_start(DIALServer *ds, const char *appname,
|
||||
const char *payload, const char *additionalDataUrl,
|
||||
DIAL_run_t *run_id, void *callback_data)
|
||||
{
|
||||
printf ("it's all wired..\n");
|
||||
return kDIALStatusRunning;
|
||||
DIALStatus current_state = get_nf_state();
|
||||
ATRACE("current state: %d\n", current_state);
|
||||
|
||||
ATRACE ("start netflix via app manager..\n");
|
||||
|
||||
char tx_buf[512];
|
||||
char rx_buf[512];
|
||||
mq_ipc_connect(mq_send_ch, mq_receive_ch);
|
||||
|
||||
char sQueryParam[DIAL_MAX_PAYLOAD+DIAL_MAX_ADDITIONALURL+40];
|
||||
memset( sQueryParam, 0, sizeof(sQueryParam) );
|
||||
strcat( sQueryParam, "-Q ");
|
||||
strcat( sQueryParam, defaultLaunchParam );
|
||||
if(strlen(payload)){
|
||||
char * pUrlEncodedParams;
|
||||
pUrlEncodedParams = url_encode( payload );
|
||||
if( pUrlEncodedParams ){
|
||||
strcat( sQueryParam, "&dial=");
|
||||
strcat( sQueryParam, pUrlEncodedParams );
|
||||
free( pUrlEncodedParams );
|
||||
}
|
||||
}
|
||||
if(strlen(additionalDataUrl)){
|
||||
strcat(sQueryParam, "&");
|
||||
strcat(sQueryParam, additionalDataUrl);
|
||||
}
|
||||
|
||||
switch (current_state) {
|
||||
case kDIALStatusHide:
|
||||
// resume Netflix
|
||||
snprintf(tx_buf, 512, mq_tx_msg_base, sQueryParam, CMD_CNT_START_FROM_HIDE);
|
||||
break;
|
||||
case kDIALStatusStopped:
|
||||
// start Netflix
|
||||
s_run_id++;
|
||||
snprintf(tx_buf, 512, mq_tx_msg_base, sQueryParam, CMD_CNT_START);
|
||||
break;
|
||||
default:
|
||||
// Already running. Just get status
|
||||
snprintf(tx_buf, 512, mq_tx_msg_base, "", CMD_CNT_GET_STATUS);
|
||||
break;
|
||||
}
|
||||
|
||||
if (mq_ipc_send_timed(tx_buf, 2))
|
||||
errorMsg();
|
||||
if (mq_ipc_receive_timed(rx_buf, 512, 2))
|
||||
errorMsg();
|
||||
ATRACE("recieved: \n %s\n", rx_buf);
|
||||
|
||||
mq_ipc_disconnect();
|
||||
|
||||
return get_nf_state();
|
||||
}
|
||||
|
||||
DIALStatus am_netflix_hide(DIALServer *ds, const char *app_name,
|
||||
DIAL_run_t *run_id, void *callback_data)
|
||||
{
|
||||
return kDIALStatusHide;
|
||||
//connect
|
||||
if (mq_ipc_connect(mq_send_ch, mq_receive_ch))
|
||||
exit(1);
|
||||
//send status request
|
||||
char tx_buf[64];
|
||||
snprintf(tx_buf, 64, mq_tx_msg_base, "", CMD_CNT_HIDE);
|
||||
if (mq_ipc_send_timed(tx_buf, 2))
|
||||
errorMsg();
|
||||
//recieve status
|
||||
char rx_buf[512];
|
||||
if(mq_ipc_receive_timed(rx_buf, 512, 2))
|
||||
errorMsg();
|
||||
ATRACE("recieved: \n %s\n", rx_buf);
|
||||
mq_ipc_disconnect();
|
||||
|
||||
return get_nf_state();
|
||||
}
|
||||
|
||||
DIALStatus am_netflix_status(DIALServer *ds, const char *appname,
|
||||
DIAL_run_t run_id, int* pCanStop, void *callback_data)
|
||||
{
|
||||
return kDIALStatusStopped;
|
||||
return get_nf_state();
|
||||
}
|
||||
|
||||
void am_netflix_stop(DIALServer *ds, const char *appname, DIAL_run_t run_id,
|
||||
void *callback_data)
|
||||
{
|
||||
//connect
|
||||
if (mq_ipc_connect(mq_send_ch, mq_receive_ch))
|
||||
exit(1);
|
||||
//send status request
|
||||
char tx_buf[64];
|
||||
snprintf(tx_buf, 64, mq_tx_msg_base, "", CMD_CNT_STOP);
|
||||
if (mq_ipc_send_timed(tx_buf, 2))
|
||||
errorMsg();
|
||||
//recieve status
|
||||
char rx_buf[512];
|
||||
if(mq_ipc_receive_timed(rx_buf, 512, 2))
|
||||
errorMsg();
|
||||
ATRACE("recieved: \n %s\n", rx_buf);
|
||||
mq_ipc_disconnect();
|
||||
}
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _NF_APPMANAGER_H_
|
||||
#define _NF_APPMANAGER_H_
|
||||
|
||||
#include "dial_server.h"
|
||||
|
||||
DIALStatus am_netflix_start(DIALServer *ds, const char *appname,
|
||||
@@ -38,3 +41,5 @@ DIALStatus am_netflix_status(DIALServer *ds, const char *appname,
|
||||
|
||||
void am_netflix_stop(DIALServer *ds, const char *appname, DIAL_run_t run_id,
|
||||
void *callback_data);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -181,3 +181,47 @@ DIALData *parse_params(char * query_string) {
|
||||
free(query_string_dup);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* The URL encoding source code was obtained here:
|
||||
* http://www.geekhideout.com/urlcode.shtml
|
||||
*/
|
||||
|
||||
/* Converts a hex character to its integer value */
|
||||
char from_hex(char ch) {
|
||||
return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;
|
||||
}
|
||||
|
||||
/* Converts an integer value to its hex character*/
|
||||
char to_hex(char code) {
|
||||
static char hex[] = "0123456789abcdef";
|
||||
return hex[code & 15];
|
||||
}
|
||||
|
||||
|
||||
/* Returns a url-encoded version of str */
|
||||
/* IMPORTANT: be sure to free() the returned string after use */
|
||||
char *url_encode(const char *str) {
|
||||
const char *pstr;
|
||||
char *buf, *pbuf;
|
||||
pstr = str;
|
||||
buf = malloc(strlen(str) * 3 + 1);
|
||||
pbuf = buf;
|
||||
if( buf )
|
||||
{
|
||||
while (*pstr) {
|
||||
if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~')
|
||||
*pbuf++ = *pstr;
|
||||
else if (*pstr == ' ')
|
||||
*pbuf++ = '+';
|
||||
else
|
||||
*pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15);
|
||||
pstr++;
|
||||
}
|
||||
*pbuf = '\0';
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* End of URL ENCODE source
|
||||
*/
|
||||
|
||||
@@ -58,4 +58,8 @@ char *parse_app_name(const char *uri);
|
||||
*/
|
||||
DIALData *parse_params(char * query_string);
|
||||
|
||||
/* Returns a url-encoded version of str */
|
||||
/* IMPORTANT: be sure to free() the returned string after use */
|
||||
char *url_encode(const char *str);
|
||||
|
||||
#endif // URLLIB_H_
|
||||
|
||||
Reference in New Issue
Block a user