char *ConvertToText( Datum value, Oid column_type, MemoryContext fn_mcxt, char** pbuf )
{
	bool typIsVarlena;
	Oid typiofunc;
	FmgrInfo	proc;
	char*		result;
//	FILE* log;

//	log = fopen("/var/lib/postgresql/serializer.log", "a");

	getTypeOutputInfo(column_type, &typiofunc, &typIsVarlena);
	fmgr_info_cxt( typiofunc, &proc, fn_mcxt );

//	fprintf(log, "Oid of function: %i\n", proc.fn_oid);

	result =  OutputFunctionCall( &proc, value );

	if((column_type != INT8OID) && (column_type != BOOLOID) &&
           (column_type != INT4OID) && (column_type != FLOAT8OID) &&
           (column_type != INT2OID) && (column_type != FLOAT4OID)) {
//		fprintf(log, "WELL WELL\n");
		result = json_escape_str(pbuf, result);
//		fprintf(log, "result: %s\n", result);
//		fclose(log);
		return result;
	}
//	fclose(log);
	return result;
}
Exemple #2
0
void *ls2_monitor(void *ctx) {
  LSError lserror;
  LSErrorInit(&lserror);
  LSHandle* lshandle = pub_serviceHandle;
  char buffer[MAXBUFLEN];
  char esc_buffer[MAXBUFLEN];
  char command[MAXLINLEN] = "/usr/bin/ls-monitor 2>&1";
  THREAD_DATA data;
  char line[MAXLINLEN];
  pthread_key_t key;

  if (ctx) 
    data.message = (LSMessage *)ctx;
  else
    return NULL;

  // Create thread key/value
  pthread_key_create(&key, ls2_thread_cleanup);
  pthread_setspecific(key, &data);

  data.fp = popen(command, "r");
  syslog(LOG_DEBUG, "ls2 pipe fp %p\n", data.fp);

  if (!data.fp) {
    if (!LSMessageReply(lshandle, data.message, "{\"returnValue\": false, \"ouroboros\": true, \"stage\": \"failed\"}", &lserror)) goto error;
    return NULL;
  }

  // Loop through the output lines
  while (fgets(line, sizeof line, data.fp)) {
    // Chomp the newline
    char *nl = strchr(line,'\n'); if (nl) *nl = 0;

    // Have status updates been requested?
    if (lshandle && data.message) {

      if (!strstr(line, "ouroboros")) {

	// Send it as a status message.
	strcpy(buffer, "{\"returnValue\": true, \"ouroboros\": true, \"stage\": \"status\", \"status\": \"");
	strcat(buffer, json_escape_str(line, esc_buffer));
	strcat(buffer, "\"}");

	// %%% Should we break out of the loop here, or just ignore the error? %%%
	if (!LSMessageReply(lshandle, data.message, buffer, &lserror)) goto error;

      }
    }
  }

  goto end;

 error:
  LSErrorPrint(&lserror, stderr);
  LSErrorFree(&lserror);
 end:
  return NULL;
}
Exemple #3
0
//
// Send a standard format command failure message back to webOS.
// The command will be escaped.  The output argument should be a JSON array and is not escaped.
// The additional text  will not be escaped.
// The return value is from the LSMessageReply call, not related to the command execution.
//
static bool report_command_failure(LSHandle* lshandle, LSMessage *message, char *command, char *stdErrText, char *additional) {
  LSError lserror;
  LSErrorInit(&lserror);

  char buffer[MAXBUFLEN];
  char esc_buffer[MAXBUFLEN];

  // Include the command that was executed, in escaped form.
  snprintf(buffer, MAXBUFLEN,
	   "{\"errorText\": \"Unable to run command: %s\"",
	   json_escape_str(command, esc_buffer));

  // Include any stderr fields from the command.
  if (stdErrText) {
    strcat(buffer, ", \"stdErr\": ");
    strcat(buffer, stdErrText);
  }

  // Report that an error occurred.
  strcat(buffer, ", \"returnValue\": false, \"errorCode\": -1");

  // Add any additional JSON fields.
  if (additional) {
    strcat(buffer, ", ");
    strcat(buffer, additional);
  }

  // Terminate the JSON reply message ...
  strcat(buffer, "}");

  // fprintf(stderr, "Message is %s\n", buffer);

  // and send it.
  if (!LSMessageReply(lshandle, message, buffer, &lserror)) goto error;

  return true;
 error:
  LSErrorPrint(&lserror, stderr);
  LSErrorFree(&lserror);
 end:
  return false;
}
Exemple #4
0
void json_profile_argument_debug(struct emu_profile_argument *argument, int indent, bool has_name, GString *str)
{
	switch( argument->render )
	{
	case render_struct:
		if( has_name )
			g_string_append_printf(str, "%*s\"%s\" : {\n", indent*4, " ", argument->argname);
//			printf("%*s\"%s\" : {\n", indent*4, " ", argument->argname);
		else
			g_string_append_printf(str, "%*s{\n", indent*4, " ");
//			printf("%*s{\n", indent*4, " ");


		struct emu_profile_argument *argumentit;
		for( argumentit = emu_profile_arguments_first(argument->value.tstruct.arguments); 
		   !emu_profile_arguments_istail(argumentit); 
		   argumentit = emu_profile_arguments_next(argumentit) )
		{
			if( argumentit != emu_profile_arguments_first(argument->value.tstruct.arguments) )
				g_string_append_printf(str, ",\n");
//				printf(",\n");
			json_profile_argument_debug(argumentit,indent+1, true, str);
		}
		g_string_append_printf(str, "\n");
//		printf("\n");
		g_string_append_printf(str, "%*s}", indent*4," ");
//		printf("%*s}", indent*4," ");
		break;

	case render_array:
		if( has_name )
			g_string_append_printf(str, "%*s\"%s\" : [\n", indent*4, " ", argument->argname);
//			printf("%*s\"%s\" : [\n", indent*4, " ", argument->argname);
		else
			g_string_append_printf(str, "[\n");
//			printf("[\n");
		for( argumentit = emu_profile_arguments_first(argument->value.tstruct.arguments); 
		   !emu_profile_arguments_istail(argumentit); 
		   argumentit = emu_profile_arguments_next(argumentit) )
		{
			if( argumentit != emu_profile_arguments_first(argument->value.tstruct.arguments) )
				g_string_append_printf(str, ",\n");
//				printf(",\n");
			json_profile_argument_debug(argumentit,indent+1, false, str);
		}
		g_string_append_printf(str, "\n");
//		printf("\n");
		g_string_append_printf(str, "%*s]", indent*4, " ");
//		printf("%*s]", indent*4, " ");
		break;

	case render_int:
		if( has_name )
			g_string_append_printf(str, "%*s\"%s\" : \"%i\"", indent*4, " ", argument->argname, argument->value.tint);
//			printf("%*s\"%s\" : \"%i\"", indent*4, " ", argument->argname, argument->value.tint);
		else
			g_string_append_printf(str, "%*s\"%i\"", indent*4, " ", argument->value.tint);
//			printf("%*s\"%i\"", indent*4, " ", argument->value.tint);
		break;

	case render_short:
		if( has_name )
			g_string_append_printf(str, "%*s\"%s\" : \"%i\"", indent*4, " ", argument->argname, argument->value.tshort);
//			printf("%*s\"%s\" : \"%i\"", indent*4, " ", argument->argname, argument->value.tshort);
		else
			g_string_append_printf(str, "%*s\"%i\"", indent*4, " ", argument->value.tshort);
//			printf("%*s\"%i\"", indent*4, " ", argument->value.tshort);
		break;


	case render_string:
		{
			char *data = argument->value.tchar;
			GString *escaped = g_string_sized_new(strlen(data)*2);
			json_escape_str(escaped, data);

			if( has_name )
				g_string_append_printf(str, "%*s\"%s\" : \"%s\"", indent*4, " ", argument->argname, escaped->str);
			//			printf("%*s\"%s\" : \"%s\"", indent*4, " ", argument->argname, argument->value.tchar);
			else
				g_string_append_printf(str, "%*s\"%s\"", indent*4, " ", escaped->str);
			//			printf("%*s\"%s\"", indent*4, " ", argument->value.tchar);
			g_string_free(escaped, TRUE);

		}
		break;

	case render_bytea:
		{
			unsigned char *data = argument->value.bytea.data;
			unsigned int size = argument->value.bytea.size;
			GString *escaped = g_string_sized_new(size*5);
			json_escape_bytea(escaped, data, size);

			if( has_name )
				g_string_append_printf(str, "%*s\"%s\" : \"%s\"", indent*4, " ", argument->argname, escaped->str);
			//			printf("%*s\"%s\" : \"%s\"", indent*4, " ", argument->argname, argument->value.tchar);
			else
				g_string_append_printf(str, "%*s\"%s\"", indent*4, " ", escaped->str);
			//			printf("%*s\"%s\"", indent*4, " ", argument->value.tchar);
			g_string_free(escaped, TRUE);
		}
		break;

	case render_ptr:
		json_profile_argument_debug(argument->value.tptr.ptr, indent+1, false, str);
		break;

	case render_ip:
		if( has_name )
			g_string_append_printf(str, "%*s\"%s\" : \"%s\"", indent*4, " ", argument->argname, inet_ntoa(*(struct in_addr *)&argument->value.tint));
//			printf("%*s\"%s\" : \"%s\"", indent*4, " ", argument->argname, inet_ntoa(*(struct in_addr *)&argument->value.tint));
		else
			g_string_append_printf(str, "%*s\"%s\"", indent*4, " ", inet_ntoa(*(struct in_addr *)&argument->value.tint));
//			printf("%*s\"%s\"", indent*4, " ", inet_ntoa(*(struct in_addr *)&argument->value.tint));

		break;

	case render_port:
		if( has_name )
			g_string_append_printf(str, "%*s\"%s\" : \"%i\"", indent*4, " ", argument->argname, ntohs((uint16_t)argument->value.tint));
//			printf("%*s\"%s\" : \"%i\"", indent*4, " ", argument->argname, ntohs((uint16_t)argument->value.tint));
		else
			g_string_append_printf(str, "%*s\"%i\"", indent*4, " ", ntohs((uint16_t)argument->value.tint));
//			printf("%*s\"%i\"", indent*4, " ", ntohs((uint16_t)argument->value.tint));

		break;

	case render_none:
		if( has_name )
			g_string_append_printf(str, "%*s\"%s\" : \"\"", indent*4, " ", argument->argname);
//			printf("%*s\"%s\" : \"\"", indent*4, " ", argument->argname);
		else
			g_string_append_printf(str, "%*s\"\"", indent*4, " ");
//			printf("%*s\"\"", indent*4, " ");

		break;
	}
}
Exemple #5
0
static bool read_file(LSHandle* lshandle, LSMessage *message, char *filename, bool subscribed) {
  LSError lserror;
  LSErrorInit(&lserror);

  FILE * file = fopen(filename, "r");
  if (!file) {
    sprintf(file_buffer,
	    "{\"returnValue\": false, \"errorCode\": -1, \"errorText\": \"Cannot open %s\"}",
	    filename);
    
    if (!LSMessageReply(lshandle, message, file_buffer, &lserror)) goto error;
    return true;
  }
  
  char chunk[CHUNKSIZE];
  int chunksize = CHUNKSIZE;

  syslog(LOG_DEBUG, "Reading file %s\n", filename);

  fseek(file, 0, SEEK_END);
  int filesize = ftell(file);
  fseek(file, 0, SEEK_SET);

  if (subscribed) {
    if (sprintf(file_buffer,
		"{\"returnValue\": true, \"filesize\": %d, \"chunksize\": %d, \"stage\": \"start\"}",
		filesize, chunksize)) {

      if (!LSMessageReply(lshandle, message, file_buffer, &lserror)) goto error;

    }
  }
  else if (filesize < chunksize) {
    chunksize = filesize;
  }

  int size;
  int datasize = 0;
  while ((size = fread(chunk, 1, chunksize, file)) > 0) {
    datasize += size;
    chunk[size] = '\0';
    sprintf(file_buffer, "{\"returnValue\": true, \"size\": %d, \"contents\": \"", size);
    strcat(file_buffer, json_escape_str(chunk, file_esc_buffer));
    strcat(file_buffer, "\"");
    if (subscribed) {
      strcat(file_buffer, ", \"stage\": \"middle\"");
    }
    strcat(file_buffer, "}");

    if (!LSMessageReply(lshandle, message, file_buffer, &lserror)) goto error;

  }

  if (!fclose(file)) {
    if (subscribed) {
      sprintf(file_buffer, "{\"returnValue\": true, \"datasize\": %d, \"stage\": \"end\"}", datasize);

      if (!LSMessageReply(lshandle, message, file_buffer, &lserror)) goto error;

    }
  }
  else {
    sprintf(file_buffer, "{\"returnValue\": false, \"errorCode\": -1, \"errorText\": \"Cannot close file\"}");

    if (!LSMessageReply(lshandle, message, file_buffer, &lserror)) goto error;

  }

  return true;
 error:
  LSErrorPrint(&lserror, stderr);
  LSErrorFree(&lserror);
 end:
  return false;
}
Exemple #6
0
//
// Run a shell command, and return the output in-line in a buffer for returning to webOS.
// The global run_command_buffer must be initialised before calling this function.
// The return value says whether the command executed successfully or not.
//
static bool run_command(char *command, bool escape, char *buffer) {
  LSError lserror;
  LSErrorInit(&lserror);

  char esc_buffer[MAXBUFLEN];

  // Local buffers to store the current and previous lines.
  char line[MAXLINLEN];

  // fprintf(stderr, "Running command %s\n", command);

  // buffer is assumed to be initialised, ready for strcat to append.

  // Is this the first line of output?
  bool first = true;

  bool array = false;

  // Start execution of the command, and read the output.
  FILE *fp = popen(command, "r");

  // Return immediately if we cannot even start the command.
  if (!fp) {
    return false;
  }

  // Loop through the output lines
  while (fgets(line, sizeof line, fp)) {

    // Chomp the newline
    char *nl = strchr(line,'\n'); if (nl) *nl = 0;

    // Add formatting breaks between lines
    if (first) {
      if (buffer[strlen(buffer)-1] == '[') {
	array = true;
      }
      first = false;
    }
    else {
      if (array) {
	strcat(buffer, ", ");
      }
      else {
	strcat(buffer, "<br>");
      }
    }
    
    // Append the unfiltered output to the buffer.
    if (escape) {
      if (array) {
	strcat(buffer, "\"");
      }
      strcat(buffer, json_escape_str(line, esc_buffer));
      if (array) {
	strcat(buffer, "\"");
      }
    }
    else {
      strcat(buffer, line);
    }
  }
  
  // Check the close status of the process
  if (pclose(fp)) {
    return false;
  }

  return true;
 error:
  LSErrorPrint(&lserror, stderr);
  LSErrorFree(&lserror);
 end:
  // %%% We need a way to distinguish command failures from LSMessage failures %%%
  // %%% This may need to be true if we just want to ignore LSMessage failures %%%
  return false;
}
Exemple #7
0
//
// Get the listing of a directory, and return it's contents.
//
bool get_dir_listing_method(LSHandle* lshandle, LSMessage *message, void *ctx) {
  LSError lserror;
  LSErrorInit(&lserror);

  char buffer[MAXBUFLEN];
  char esc_buffer[MAXBUFLEN];

  struct dirent *ep;

  // Local buffer to hold each line of output from ls
  char line[MAXLINLEN];

  // Is this the first line of output?
  bool first = true;

  // Was there an error in accessing any of the files?
  bool error = false;

  json_t *object = json_parse_document(LSMessageGetPayload(message));
  json_t *id = json_find_first_label(object, "directory");

  if (!id || (id->child->type != JSON_STRING) || (strspn(id->child->text, ALLOWED_CHARS"/") != strlen(id->child->text))) {
    if (!LSMessageRespond(message,
			"{\"returnValue\": false, \"errorCode\": -1, \"errorText\": \"Invalid or missing directory\"}",
			&lserror)) goto error;
  }

  // Start execution of the command to list the directory contents
  DIR *dp = opendir(id->child->text);

  // If the command cannot be started
  if (!dp) {
    if (!LSMessageRespond(message,
			"{\"returnValue\": false, \"errorCode\": -1, \"errorText\": \"Unable to open directory\"}",
			&lserror)) goto error;

    // The error report has been sent, so return to webOS.
    return true;
  }

  // Initialise the output message.
  strcpy(buffer, "{");

  // Loop through the list of directory entries.
  while (ep = readdir(dp)) {

    // Start or continue the JSON array
    if (first) {
      strcat(buffer, "\"contents\": [");
      first = false;
    }
    else {
      strcat(buffer, ", ");
    }

    strcat(buffer, "{\"name\":\"");
    strcat(buffer, json_escape_str(ep->d_name, esc_buffer));
    strcat(buffer, "\", ");

    strcat(buffer, "\"type\":\"");
    if (ep->d_type == DT_DIR) {
      strcat(buffer, "directory");
    }
    else if (ep->d_type == DT_REG) {
      strcat(buffer, "file");
    }
    else if (ep->d_type == DT_LNK) {
      strcat(buffer, "symlink");
    }
    else {
      strcat(buffer, "other");
    }
    strcat(buffer, "\"}");
  }

  // Terminate the JSON array
  if (!first) {
    strcat(buffer, "], ");
  }

  // Check the close status of the process, and return the combined error status
  if (closedir(dp) || error) {
    strcat(buffer, "\"returnValue\": false}");
  }
  else {
    strcat(buffer, "\"returnValue\": true}");
  }

  // Return the results to webOS.
  if (!LSMessageRespond(message, buffer, &lserror)) goto error;

  return true;
 error:
  LSErrorPrint(&lserror, stderr);
  LSErrorFree(&lserror);
 end:
  return false;
}
Exemple #8
0
//
// Dump the contents of an sqlite3 database table
//
static bool dump_sqlite(LSMessage *message, char *database, char *table) {
  LSError lserror;
  LSErrorInit(&lserror);

  char buffer[MAXBUFLEN];
  char esc_buffer[MAXBUFLEN];

  char line[MAXLINLEN];

  // Local buffer to store the command
  char command[MAXLINLEN];

  sprintf(command, "sqlite3 %s .dump 2>&1", database);

  // Is this the first line of output?
  bool first = true;

  // Was there an error in accessing any of the files?
  bool error = false;

  // Length of buffer before the last command
  int lastlen = 0;

  // Start execution of the command to list the config files.
  FILE *fp = popen(command, "r");

  // If the command cannot be started
  if (!fp) {

    // then report the error to webOS.
    if (!report_command_failure(message, command, NULL, NULL)) goto end;

    // The error report has been sent, so return to webOS.
    return true;
  }

  if (!LSMessageRespond(message, "{\"stage\": \"start\", \"returnValue\": true}", &lserror)) goto error;

  // Initialise the output message.
  strcpy(buffer, "{");
  lastlen = strlen(buffer);

  // Loop through the list of files in the scripts directory.
  while (fgets( line, sizeof line, fp)) {

    // Chomp the newline
    char *nl = strchr(line,'\n'); if (nl) *nl = 0;

    if ((strlen(line) <= 13+strlen(table)+9) ||
	strncmp(line, "INSERT INTO \"", 13) ||
	strncmp(line+13, table, strlen(table)) ||
	strncmp(line+13+strlen(table), "\" VALUES(", 9) ||
	strncmp(line+strlen(line)-2, ");", 2)) {
      continue;
    }

    *(line+strlen(line)-2) = 0;

    // Push out a partial chunk
    if (strlen(buffer) >= CHUNKSIZE) {

      // Terminate the JSON array
      if (!first) {
	strcat(buffer, "], ");
      }

      strcat(buffer, "\"stage\": \"middle\", ");

      // Check the error status, and return the current error status
      if (error) {
	strcat(buffer, "\"returnValue\": false}");
      }
      else {
	strcat(buffer, "\"returnValue\": true}");
      }
      
      // fprintf(stderr, "Message is %s\n", buffer);

      // Return the results to webOS.
      if (!LSMessageRespond(message, buffer, &lserror)) goto error;

      // This is now the first line of output
      first = true;

      // Initialise the output message.
      strcpy(buffer, "{");
      lastlen = strlen(buffer);
    }

    // Start or continue the JSON array
    if (first) {
      strcat(buffer, "\"results\": [");
      lastlen = strlen(buffer);
      first = false;
    }
    else if (strlen(buffer) > lastlen) {
      strcat(buffer, ", ");
      lastlen = strlen(buffer);
    }

    // Store the command output
    strcat(buffer, "\"");
    strcat(buffer, json_escape_str(line+13+strlen(table)+9, esc_buffer));
    strcat(buffer, "\"");
  }

  // Terminate the JSON array
  if (!first) {
    strcat(buffer, "], ");
  }

  strcat(buffer, "\"stage\": \"end\", ");

  // Check the close status of the process, and return the combined error status
  if (pclose(fp) || error) {
    strcat(buffer, "\"returnValue\": false}");
  }
  else {
    strcat(buffer, "\"returnValue\": true}");
  }

  // fprintf(stderr, "Message is %s\n", buffer);

  // Return the results to webOS.
  if (!LSMessageRespond(message, buffer, &lserror)) goto error;

  return true;
 error:
  LSErrorPrint(&lserror, stderr);
  LSErrorFree(&lserror);
 end:
  return false;
}
Exemple #9
0
int
lwes_event_attribute_to_string
  (struct lwes_event_attribute *attribute,
   char *buffer,
   int offset)
{
  int n;
  char *tmp_buffer;

  if (attribute->type == LWES_U_INT_16_TOKEN)
  {
    return lwes_U_INT_16_to_string(*((LWES_U_INT_16 *)attribute->value), buffer, offset);
  }
  else if (attribute->type == LWES_INT_16_TOKEN)
  {
    return lwes_INT_16_to_string(*((LWES_INT_16 *)attribute->value), buffer, offset);
  }
  else if (attribute->type == LWES_U_INT_32_TOKEN)
  {
    return lwes_U_INT_32_to_string(*((LWES_U_INT_32 *)attribute->value), buffer, offset);
  }
  else if (attribute->type == LWES_INT_32_TOKEN)
  {
    return lwes_INT_32_to_string(*((LWES_INT_32 *)attribute->value), buffer, offset);
  }
  else if (attribute->type == LWES_U_INT_64_TOKEN)
  {
    n = sprintf(buffer + offset, "\"");
    n += lwes_U_INT_64_to_string(*((LWES_U_INT_64 *)attribute->value), buffer, offset + n);
    n += sprintf(buffer + offset + n, "\"");
    return n;
  }
  else if (attribute->type == LWES_INT_64_TOKEN)
  {
    n = sprintf(buffer + offset, "\"");
    n += lwes_INT_64_to_string(*((LWES_INT_64 *)attribute->value), buffer, offset + n);
    n += sprintf(buffer + offset + n, "\"");
    return n;
  }
  else if (attribute->type == LWES_BOOLEAN_TOKEN)
  {
    return lwes_BOOLEAN_to_string(*((LWES_BOOLEAN *)attribute->value), buffer, offset);
  }
  else if (attribute->type == LWES_IP_ADDR_TOKEN)
  {
    n = sprintf(buffer + offset, "\"");
    n += lwes_IP_ADDR_to_string(*((LWES_IP_ADDR *)attribute->value), buffer, offset + n);
    n += sprintf(buffer + offset + n, "\"");
    return n;
  }
  else if (attribute->type == LWES_STRING_TOKEN)
  {
    n = sprintf(buffer + offset, "\"");
    tmp_buffer = json_escape_str((char *)attribute->value);
    if (tmp_buffer != NULL)
    {
      n += lwes_LONG_STRING_to_string((LWES_LONG_STRING)tmp_buffer, buffer, offset + n);
      free(tmp_buffer);
    }
    n += sprintf(buffer + offset + n, "\"");
    return n;
  }
  return 0;
}
void ICACHE_FLASH_ATTR mod_led_8x8_rgb_handler(
	struct espconn *pConnection, 
	request_method method, 
	char *url, 
	char *data, 
	uint16 data_len, 
	uint32 content_len, 
	char *response,
	uint16 response_len
) {
	
	
	if (mod_led_8x8_text == NULL) {
		mod_led_8x8_text = (char *)os_zalloc(MOD_LED_8x8_RGB_MAX_TEXT);
	}
	
	if (method == POST && data != NULL && data_len != 0) {
		if (led_8x8_rgb_busy()) {
			json_error(response, MOD_LED8x8RGB, BUSY_STR, NULL);
			return;
		}
		
		if (mod_led_8x8_rgb_parse(data, data_len)) {
			if (!led_8x8_rgb_set_dimensions(mod_led_8x8_cols, mod_led_8x8_rows)) {
				json_error(response, MOD_LED8x8RGB, "Dimensions can not be set", NULL);
				return;
			}
			
			mod_led_8x8_rgb_preferences_set();
		}
		
		if (
			!led_8x8_rgb_scroll(
				mod_led_8x8_r, 
				mod_led_8x8_g, 
				mod_led_8x8_b, 
				mod_led_8x8_text, 
				MOD_LED_8x8_RGB_MAX_SPEED - mod_led_8x8_speed + 1, 
				mod_led_8x8_rgb_scroll_done
			)
		) {
			json_error(response, MOD_LED8x8RGB, BUSY_STR, NULL);
			return;
		}
	}
	
	char data_str[WEBSERVER_MAX_VALUE * 2];
	char *escaped = json_escape_str(mod_led_8x8_text, MOD_LED_8x8_RGB_MAX_TEXT);
	
	json_data(
		response, MOD_LED8x8RGB, OK_STR,
		json_sprintf(
			data_str,
			"\"cols\" : %d, "
			"\"rows\" : %d, "
			"\"Speed\" : %d, "
			"\"R\" : %d, "
			"\"G\" : %d, "
			"\"B\" : %d, "
			"\"Text\" : \"%s\"",
			led_8x8_rgb_get_cols(),
			led_8x8_rgb_get_rows(),
			mod_led_8x8_speed,
			mod_led_8x8_r,
			mod_led_8x8_g,
			mod_led_8x8_b,
			escaped
		),
		NULL
	);
	
	os_free(escaped);
}