<commit_before>/*
 * (C) Copyright 2013 Kurento (http://kurento.org/)
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl-2.1.html
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 */

#include <sys/signalfd.h>
#include <config.h>

#include "media_config.hpp"

#include <glibmm.h>
#include <fstream>
#include <iostream>
#include <boost/filesystem.hpp>
#include <version.hpp>
#include "httpendpointserver.hpp"
#include <ModuleLoader.hpp>
#include <glib/gstdio.h>

#include "services/Service.hpp"
#include "services/ServiceFactory.hpp"

#define GST_CAT_DEFAULT kurento_media_server
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
#define GST_DEFAULT_NAME "KurentoMediaServer"

#define ENV_VAR "GST_PLUGIN_PATH"
#define CERTTOOL_TEMPLATE "autoCerttool.tmpl"
#define CERT_KEY_PEM_FILE "autoCertkey.pem"

using namespace ::kurento;

using boost::shared_ptr;
using namespace boost::filesystem;

using ::Glib::KeyFile;
using ::Glib::KeyFileFlags;

static Service *service;

GstSDPMessage *sdpPattern;
KmsHttpEPServer *httpepserver;
std::string stunServerAddress, pemCertificate;
gint stunServerPort;

Glib::RefPtr<Glib::MainLoop> loop = Glib::MainLoop::create ();

static gchar *conf_file;
gchar *autogenerated_pem_file, *template_path;

static GOptionEntry entries[] = {
  {
    "conf-file", 'f', 0, G_OPTION_ARG_FILENAME, &conf_file, "Configuration file",
    NULL
  },
  {NULL}
};

Glib::RefPtr<Glib::IOChannel> channel;

static void
check_port (int port)
{
  if (port <= 0 || port > G_MAXUSHORT) {
    throw Glib::KeyFileError (Glib::KeyFileError::PARSE, "Invalid value");
  }
}

static gchar *
read_entire_file (const gchar *file_name)
{
  gchar *data;
  long f_size;
  FILE *fp;

  fp = fopen (file_name, "r");

  if (fp == NULL) {
    return NULL;
  }

  fseek (fp, 0, SEEK_END);
  f_size = ftell (fp);
  fseek (fp, 0, SEEK_SET);
  data = (gchar *) g_malloc0 (f_size + 1);

  if (fread (data, 1, f_size, fp) != (size_t) f_size) {
    GST_ERROR ("Error reading file");
  }

  fclose (fp);

  data[f_size] = '\0';

  return data;
}

static GstSDPMessage *
load_sdp_pattern (Glib::KeyFile &configFile, const std::string &confFileName)
{
  GstSDPResult result;
  GstSDPMessage *sdp_pattern = NULL;
  gchar *sdp_pattern_text;
  std::string sdp_pattern_file_name;

  GST_DEBUG ("Load SDP Pattern");
  result = gst_sdp_message_new (&sdp_pattern);

  if (result != GST_SDP_OK) {
    GST_ERROR ("Error creating sdp message");
    return NULL;
  }

  sdp_pattern_file_name = configFile.get_string (SERVER_GROUP, SDP_PATTERN_KEY);
  boost::filesystem::path p (confFileName.c_str () );
  sdp_pattern_file_name.insert (0, "/");
  sdp_pattern_file_name.insert (0, p.parent_path ().c_str() );
  sdp_pattern_text = read_entire_file (sdp_pattern_file_name.c_str () );

  if (sdp_pattern_text == NULL) {
    GST_ERROR ("Error reading SDP pattern file");
    gst_sdp_message_free (sdp_pattern);
    return NULL;
  }

  result = gst_sdp_message_parse_buffer ( (const guint8 *) sdp_pattern_text, -1,
                                          sdp_pattern);
  g_free (sdp_pattern_text);

  if (result != GST_SDP_OK) {
    GST_ERROR ("Error parsing SDP config pattern");
    gst_sdp_message_free (sdp_pattern);
    return NULL;
  }

  return sdp_pattern;
}

static void
configure_kurento_media_server (KeyFile &configFile,
                                const std::string &file_name)
{
  gchar *sdpMessageText = NULL;

  try {
    sdpPattern = load_sdp_pattern (configFile, file_name);
    GST_DEBUG ("SDP: \n%s", sdpMessageText = gst_sdp_message_as_text (sdpPattern) );
    g_free (sdpMessageText);
  } catch (const Glib::KeyFileError &err) {
    GST_ERROR ("%s", err.what ().c_str () );
    GST_WARNING ("Wrong codec configuration, communication won't be possible");
  }

  try {
    service = ServiceFactory::create_service (configFile);
  } catch (std::exception &e) {
    GST_ERROR ("Error creating service: %s", e.what() );
    exit (1);
  } catch (Glib::Exception &e) {
    GST_ERROR ("Error creating service: %s", e.what().c_str() );
    exit (1);
  }
}

static gchar *
generate_certkey_pem_file (const gchar *dir)
{
  gchar *cmd, *pem_path;
  int ret;

  if (dir == NULL) {
    return NULL;
  }

  pem_path = g_strdup_printf ("%s/%s", dir, CERT_KEY_PEM_FILE);
  cmd =
    g_strconcat ("/bin/sh -c \"certtool --generate-privkey --outfile ",
                 pem_path, "\"", NULL);
  ret = system (cmd);
  g_free (cmd);

  if (ret == -1) {
    goto err;
  }

  template_path = g_strdup_printf ("%s/%s", dir, CERTTOOL_TEMPLATE);
  cmd =
    g_strconcat
    ("/bin/sh -c \"echo 'organization = kurento' > ", template_path,
     " && certtool --generate-self-signed --load-privkey ", pem_path,
     " --template ", template_path, " >> ", pem_path, " 2>/dev/null\"", NULL);
  ret = system (cmd);
  g_free (cmd);

  if (ret == -1) {
    goto err;
  }

  return pem_path;

err:

  GST_ERROR ("Error while generating certificate file");

  g_free (pem_path);
  return NULL;
}

static void
configure_web_rtc_end_point (KeyFile &configFile, const std::string &file_name)
{
  gint port;
  std::string pem_certificate_file_name;

  try {
    stunServerAddress = configFile.get_string (WEB_RTC_END_POINT_GROUP,
                        WEB_RTC_END_POINT_STUN_SERVER_ADDRESS_KEY);
  } catch (const Glib::KeyFileError &err) {
    GST_ERROR ("%s", err.what ().c_str () );
    GST_WARNING ("Setting default address %s to stun server",
                 STUN_SERVER_ADDRESS);
    stunServerAddress = STUN_SERVER_ADDRESS;
  }

  try {
    port = configFile.get_integer (WEB_RTC_END_POINT_GROUP,
                                   WEB_RTC_END_POINT_STUN_SERVER_PORT_KEY);
    check_port (port);
    stunServerPort = port;
  } catch (const Glib::KeyFileError &err) {
    GST_ERROR ("%s", err.what ().c_str () );
    GST_WARNING ("Setting default port %d to stun server",
                 STUN_SERVER_PORT);
    stunServerPort = STUN_SERVER_PORT;
  }

  try {
    pem_certificate_file_name = configFile.get_string (WEB_RTC_END_POINT_GROUP,
                                WEB_RTC_END_POINT_PEM_CERTIFICATE_KEY);
    boost::filesystem::path p (file_name.c_str () );
    pem_certificate_file_name.insert (0, "/");
    pem_certificate_file_name.insert (0, p.parent_path ().c_str() );
    pemCertificate = pem_certificate_file_name.c_str();

  } catch (const Glib::KeyFileError &err) {
    //generate a valid pem certificate
    autogenerated_pem_file = generate_certkey_pem_file ("/tmp/");

    if (autogenerated_pem_file == NULL ) {
      pemCertificate = "";
      GST_ERROR ("%s", err.what ().c_str () );
      GST_WARNING ("Could not create Pem Certificate or Pem Certificate not found");
    } else {
      pemCertificate = autogenerated_pem_file;
    }
  }
}

static void
load_config (const std::string &file_name)
{
  KeyFile configFile;

  GST_INFO ("Reading configuration from: %s", file_name.c_str () );

  try {
    configFile.load_from_file (file_name,
                               KeyFileFlags::KEY_FILE_KEEP_COMMENTS |
                               KeyFileFlags::KEY_FILE_KEEP_TRANSLATIONS);
  } catch (const Glib::Error &ex) {
    GST_ERROR ("Error loading configuration: %s", ex.what ().c_str () );
    throw ex;
  }

  /* parse options so as to configure services */
  configure_kurento_media_server (configFile, file_name);
  configure_web_rtc_end_point (configFile, file_name);

  GST_INFO ("Configuration loaded successfully");
}

static bool
signal_handler (Glib::IOCondition cond)
{
  static unsigned int __terminated = 0;
  struct signalfd_siginfo si;
  gsize bytes_read, count = sizeof (si);
  Glib::IOStatus status;

  if (cond & (Glib::IO_NVAL | Glib::IO_ERR | Glib::IO_HUP) ) {
    return false;
  }

  /* Read signal info */
  status = channel->read ( (char *) &si, count, bytes_read);

  switch (status) {
  case Glib::IO_STATUS_ERROR:
    GST_ERROR ("Error occurred.");
    return false;

  case Glib::IO_STATUS_EOF:
    GST_DEBUG ("End of file.");
    return false;

  case Glib::IO_STATUS_AGAIN:
    GST_DEBUG ("Resource temporarily unavailable.");
    return false;

  case Glib::IO_STATUS_NORMAL:
    if (bytes_read != count) {
      GST_DEBUG ("Could not read siginfo structure");
      return false;
    }

    break;
  }

  /* Manage signal */
  switch (si.ssi_signo) {
  case SIGINT:
  case SIGTERM:
    if (__terminated == 0) {
      GST_DEBUG ("Terminating.");
      loop->quit ();
    }

    __terminated = 1;
    break;

  case SIGPIPE:
    GST_DEBUG ("Ignore sigpipe signal");
    return false;

  case SIGSEGV:
    GST_DEBUG ("Segmentation fault. Aborting process execution");
    abort ();

  default:
    break;
  }

  return true;
}

static Glib::RefPtr<Glib::IOSource>
setup_signalfd ()
{
  Glib::RefPtr<Glib::IOSource> source;
  sigset_t mask;
  int fd;

  sigemptyset (&mask);
  sigaddset (&mask, SIGINT);
  sigaddset (&mask, SIGTERM);
  sigaddset (&mask, SIGSEGV);
  sigaddset (&mask, SIGPIPE);

  if (sigprocmask (SIG_BLOCK, &mask, NULL) < 0) {
    throw "Failed to set signal mask";
  }

  fd = signalfd (-1, &mask, 0);

  if (fd < 0) {
    throw "Failed to create signal descriptor";
  }

  channel = Glib::IOChannel::create_from_fd (fd);
  channel->set_close_on_unref (true);
  channel->set_encoding ("");
  channel->set_buffered (false);

  source = channel->create_watch (Glib::IO_IN | Glib::IO_HUP | Glib::IO_ERR |
                                  Glib::IO_NVAL);
  source->connect (sigc::ptr_fun (&signal_handler) );

  return source;
}

static void
deleteCertificate ()
{
  //delete pem certificate
  if (autogenerated_pem_file != NULL) {
    int rv = g_remove (autogenerated_pem_file);

    if (rv) {
      GST_WARNING ("Error deleting file: %s. %s", autogenerated_pem_file,
                   strerror (errno) );
    }

    g_free (autogenerated_pem_file);
  }

  if (template_path != NULL) {
    int rv = g_remove (template_path);

    if (rv) {
      GST_WARNING ("Error deleting file: %s. %s", template_path, strerror (errno) );
    }

    g_free (template_path);
  }
}

static void
check_if_plugins_are_available ()
{
  GstPlugin *plugin = gst_plugin_load_by_name ("kurento");

  if (plugin == NULL) {
    g_printerr ("Kurento plugin not found, try adding the plugins route with "
                "--gst-plugin-path parameter. See help (--help-gst) "
                "for more info\n");
    exit (1);
  }

  g_clear_object (&plugin);
}

int
main (int argc, char **argv)
{
  Glib::RefPtr<Glib::Source> source;
  GError *error = NULL;
  GOptionContext *context;
  gchar *oldEnv, *newEnv;

  Glib::init();

  oldEnv = getenv (ENV_VAR);

  if (oldEnv == NULL) {
    newEnv = g_strdup_printf ("%s", PLUGIN_PATH);
  } else {
    newEnv = g_strdup_printf ("%s:%s", oldEnv, PLUGIN_PATH);
  }

  setenv (ENV_VAR, newEnv, 1);
  g_free (newEnv);

  gst_init (&argc, &argv);
  GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, GST_DEFAULT_NAME, 0,
                           GST_DEFAULT_NAME);

  check_if_plugins_are_available ();

  context = g_option_context_new ("");
  g_option_context_add_main_entries (context, entries, NULL);
  g_option_context_add_group (context, gst_init_get_option_group () );

  if (!g_option_context_parse (context, &argc, &argv, &error) ) {
    g_printerr ("option parsing failed: %s\n", error->message);
    g_option_context_free (context);
    g_error_free (error);
    exit (1);
  }

  g_option_context_free (context);

  /* Install our signal handler */
  source = setup_signalfd();
  source->attach (Glib::MainContext::get_default() );

  GST_INFO ("Kmsc version: %s", get_version () );

  if (!conf_file) {
    load_config (DEFAULT_CONFIG_FILE);
  } else {
    load_config ( (std::string) conf_file);
  }

  /* Start service */
  service->start ();

  loop->run ();

  source->destroy();

  deleteCertificate ();

  service->stop();

  delete service;

  return 0;
}
<commit_msg>main: Do not let children processes to delete temporal files<commit_after>/*
 * (C) Copyright 2013 Kurento (http://kurento.org/)
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl-2.1.html
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 */

#include <sys/signalfd.h>
#include <config.h>

#include "media_config.hpp"

#include <glibmm.h>
#include <fstream>
#include <iostream>
#include <boost/filesystem.hpp>
#include <version.hpp>
#include "httpendpointserver.hpp"
#include <ModuleLoader.hpp>
#include <glib/gstdio.h>

#include "services/Service.hpp"
#include "services/ServiceFactory.hpp"

#define GST_CAT_DEFAULT kurento_media_server
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
#define GST_DEFAULT_NAME "KurentoMediaServer"

#define ENV_VAR "GST_PLUGIN_PATH"
#define CERTTOOL_TEMPLATE "autoCerttool.tmpl"
#define CERT_KEY_PEM_FILE "autoCertkey.pem"

using namespace ::kurento;

using boost::shared_ptr;
using namespace boost::filesystem;

using ::Glib::KeyFile;
using ::Glib::KeyFileFlags;

static Service *service;

GstSDPMessage *sdpPattern;
KmsHttpEPServer *httpepserver;
std::string stunServerAddress, pemCertificate;
__pid_t pid;
gint stunServerPort;

Glib::RefPtr<Glib::MainLoop> loop = Glib::MainLoop::create ();

static gchar *conf_file;
gchar *autogenerated_pem_file, *template_path;

static GOptionEntry entries[] = {
  {
    "conf-file", 'f', 0, G_OPTION_ARG_FILENAME, &conf_file, "Configuration file",
    NULL
  },
  {NULL}
};

Glib::RefPtr<Glib::IOChannel> channel;

static void
check_port (int port)
{
  if (port <= 0 || port > G_MAXUSHORT) {
    throw Glib::KeyFileError (Glib::KeyFileError::PARSE, "Invalid value");
  }
}

static gchar *
read_entire_file (const gchar *file_name)
{
  gchar *data;
  long f_size;
  FILE *fp;

  fp = fopen (file_name, "r");

  if (fp == NULL) {
    return NULL;
  }

  fseek (fp, 0, SEEK_END);
  f_size = ftell (fp);
  fseek (fp, 0, SEEK_SET);
  data = (gchar *) g_malloc0 (f_size + 1);

  if (fread (data, 1, f_size, fp) != (size_t) f_size) {
    GST_ERROR ("Error reading file");
  }

  fclose (fp);

  data[f_size] = '\0';

  return data;
}

static GstSDPMessage *
load_sdp_pattern (Glib::KeyFile &configFile, const std::string &confFileName)
{
  GstSDPResult result;
  GstSDPMessage *sdp_pattern = NULL;
  gchar *sdp_pattern_text;
  std::string sdp_pattern_file_name;

  GST_DEBUG ("Load SDP Pattern");
  result = gst_sdp_message_new (&sdp_pattern);

  if (result != GST_SDP_OK) {
    GST_ERROR ("Error creating sdp message");
    return NULL;
  }

  sdp_pattern_file_name = configFile.get_string (SERVER_GROUP, SDP_PATTERN_KEY);
  boost::filesystem::path p (confFileName.c_str () );
  sdp_pattern_file_name.insert (0, "/");
  sdp_pattern_file_name.insert (0, p.parent_path ().c_str() );
  sdp_pattern_text = read_entire_file (sdp_pattern_file_name.c_str () );

  if (sdp_pattern_text == NULL) {
    GST_ERROR ("Error reading SDP pattern file");
    gst_sdp_message_free (sdp_pattern);
    return NULL;
  }

  result = gst_sdp_message_parse_buffer ( (const guint8 *) sdp_pattern_text, -1,
                                          sdp_pattern);
  g_free (sdp_pattern_text);

  if (result != GST_SDP_OK) {
    GST_ERROR ("Error parsing SDP config pattern");
    gst_sdp_message_free (sdp_pattern);
    return NULL;
  }

  return sdp_pattern;
}

static void
configure_kurento_media_server (KeyFile &configFile,
                                const std::string &file_name)
{
  gchar *sdpMessageText = NULL;

  try {
    sdpPattern = load_sdp_pattern (configFile, file_name);
    GST_DEBUG ("SDP: \n%s", sdpMessageText = gst_sdp_message_as_text (sdpPattern) );
    g_free (sdpMessageText);
  } catch (const Glib::KeyFileError &err) {
    GST_ERROR ("%s", err.what ().c_str () );
    GST_WARNING ("Wrong codec configuration, communication won't be possible");
  }

  try {
    service = ServiceFactory::create_service (configFile);
  } catch (std::exception &e) {
    GST_ERROR ("Error creating service: %s", e.what() );
    exit (1);
  } catch (Glib::Exception &e) {
    GST_ERROR ("Error creating service: %s", e.what().c_str() );
    exit (1);
  }
}

static gchar *
generate_certkey_pem_file (const gchar *dir)
{
  gchar *cmd, *pem_path;
  int ret;

  if (dir == NULL) {
    return NULL;
  }

  pem_path = g_strdup_printf ("%s/%s", dir, CERT_KEY_PEM_FILE);
  cmd =
    g_strconcat ("/bin/sh -c \"certtool --generate-privkey --outfile ",
                 pem_path, "\"", NULL);
  ret = system (cmd);
  g_free (cmd);

  if (ret == -1) {
    goto err;
  }

  template_path = g_strdup_printf ("%s/%s", dir, CERTTOOL_TEMPLATE);
  cmd =
    g_strconcat
    ("/bin/sh -c \"echo 'organization = kurento' > ", template_path,
     " && certtool --generate-self-signed --load-privkey ", pem_path,
     " --template ", template_path, " >> ", pem_path, " 2>/dev/null\"", NULL);
  ret = system (cmd);
  g_free (cmd);

  if (ret == -1) {
    goto err;
  }

  return pem_path;

err:

  GST_ERROR ("Error while generating certificate file");

  g_free (pem_path);
  return NULL;
}

static void
configure_web_rtc_end_point (KeyFile &configFile, const std::string &file_name)
{
  gint port;
  std::string pem_certificate_file_name;

  try {
    stunServerAddress = configFile.get_string (WEB_RTC_END_POINT_GROUP,
                        WEB_RTC_END_POINT_STUN_SERVER_ADDRESS_KEY);
  } catch (const Glib::KeyFileError &err) {
    GST_ERROR ("%s", err.what ().c_str () );
    GST_WARNING ("Setting default address %s to stun server",
                 STUN_SERVER_ADDRESS);
    stunServerAddress = STUN_SERVER_ADDRESS;
  }

  try {
    port = configFile.get_integer (WEB_RTC_END_POINT_GROUP,
                                   WEB_RTC_END_POINT_STUN_SERVER_PORT_KEY);
    check_port (port);
    stunServerPort = port;
  } catch (const Glib::KeyFileError &err) {
    GST_ERROR ("%s", err.what ().c_str () );
    GST_WARNING ("Setting default port %d to stun server",
                 STUN_SERVER_PORT);
    stunServerPort = STUN_SERVER_PORT;
  }

  try {
    pem_certificate_file_name = configFile.get_string (WEB_RTC_END_POINT_GROUP,
                                WEB_RTC_END_POINT_PEM_CERTIFICATE_KEY);
    boost::filesystem::path p (file_name.c_str () );
    pem_certificate_file_name.insert (0, "/");
    pem_certificate_file_name.insert (0, p.parent_path ().c_str() );
    pemCertificate = pem_certificate_file_name.c_str();

  } catch (const Glib::KeyFileError &err) {
    //generate a valid pem certificate
    autogenerated_pem_file = generate_certkey_pem_file ("/tmp/");

    if (autogenerated_pem_file == NULL ) {
      pemCertificate = "";
      GST_ERROR ("%s", err.what ().c_str () );
      GST_WARNING ("Could not create Pem Certificate or Pem Certificate not found");
    } else {
      pemCertificate = autogenerated_pem_file;
    }
  }
}

static void
load_config (const std::string &file_name)
{
  KeyFile configFile;

  pid = getpid();

  GST_INFO ("Reading configuration from: %s", file_name.c_str () );

  try {
    configFile.load_from_file (file_name,
                               KeyFileFlags::KEY_FILE_KEEP_COMMENTS |
                               KeyFileFlags::KEY_FILE_KEEP_TRANSLATIONS);
  } catch (const Glib::Error &ex) {
    GST_ERROR ("Error loading configuration: %s", ex.what ().c_str () );
    throw ex;
  }

  /* parse options so as to configure services */
  configure_kurento_media_server (configFile, file_name);
  configure_web_rtc_end_point (configFile, file_name);

  GST_INFO ("Configuration loaded successfully");
}

static bool
signal_handler (Glib::IOCondition cond)
{
  static unsigned int __terminated = 0;
  struct signalfd_siginfo si;
  gsize bytes_read, count = sizeof (si);
  Glib::IOStatus status;

  if (cond & (Glib::IO_NVAL | Glib::IO_ERR | Glib::IO_HUP) ) {
    return false;
  }

  /* Read signal info */
  status = channel->read ( (char *) &si, count, bytes_read);

  switch (status) {
  case Glib::IO_STATUS_ERROR:
    GST_ERROR ("Error occurred.");
    return false;

  case Glib::IO_STATUS_EOF:
    GST_DEBUG ("End of file.");
    return false;

  case Glib::IO_STATUS_AGAIN:
    GST_DEBUG ("Resource temporarily unavailable.");
    return false;

  case Glib::IO_STATUS_NORMAL:
    if (bytes_read != count) {
      GST_DEBUG ("Could not read siginfo structure");
      return false;
    }

    break;
  }

  /* Manage signal */
  switch (si.ssi_signo) {
  case SIGINT:
  case SIGTERM:
    if (__terminated == 0) {
      GST_DEBUG ("Terminating.");
      loop->quit ();
    }

    __terminated = 1;
    break;

  case SIGPIPE:
    GST_DEBUG ("Ignore sigpipe signal");
    return false;

  case SIGSEGV:
    GST_DEBUG ("Segmentation fault. Aborting process execution");
    abort ();

  default:
    break;
  }

  return true;
}

static Glib::RefPtr<Glib::IOSource>
setup_signalfd ()
{
  Glib::RefPtr<Glib::IOSource> source;
  sigset_t mask;
  int fd;

  sigemptyset (&mask);
  sigaddset (&mask, SIGINT);
  sigaddset (&mask, SIGTERM);
  sigaddset (&mask, SIGSEGV);
  sigaddset (&mask, SIGPIPE);

  if (sigprocmask (SIG_BLOCK, &mask, NULL) < 0) {
    throw "Failed to set signal mask";
  }

  fd = signalfd (-1, &mask, 0);

  if (fd < 0) {
    throw "Failed to create signal descriptor";
  }

  channel = Glib::IOChannel::create_from_fd (fd);
  channel->set_close_on_unref (true);
  channel->set_encoding ("");
  channel->set_buffered (false);

  source = channel->create_watch (Glib::IO_IN | Glib::IO_HUP | Glib::IO_ERR |
                                  Glib::IO_NVAL);
  source->connect (sigc::ptr_fun (&signal_handler) );

  return source;
}

static void
deleteCertificate ()
{
  // Only parent process can delete certificate
  if (pid != getpid() ) {
    return;
  }

  //delete pem certificate
  if (autogenerated_pem_file != NULL) {
    int rv = g_remove (autogenerated_pem_file);

    if (rv) {
      GST_WARNING ("Error deleting file: %s. %s", autogenerated_pem_file,
                   strerror (errno) );
    }

    g_free (autogenerated_pem_file);
  }

  if (template_path != NULL) {
    int rv = g_remove (template_path);

    if (rv) {
      GST_WARNING ("Error deleting file: %s. %s", template_path, strerror (errno) );
    }

    g_free (template_path);
  }
}

static void
check_if_plugins_are_available ()
{
  GstPlugin *plugin = gst_plugin_load_by_name ("kurento");

  if (plugin == NULL) {
    g_printerr ("Kurento plugin not found, try adding the plugins route with "
                "--gst-plugin-path parameter. See help (--help-gst) "
                "for more info\n");
    exit (1);
  }

  g_clear_object (&plugin);
}

int
main (int argc, char **argv)
{
  Glib::RefPtr<Glib::Source> source;
  GError *error = NULL;
  GOptionContext *context;
  gchar *oldEnv, *newEnv;

  Glib::init();

  oldEnv = getenv (ENV_VAR);

  if (oldEnv == NULL) {
    newEnv = g_strdup_printf ("%s", PLUGIN_PATH);
  } else {
    newEnv = g_strdup_printf ("%s:%s", oldEnv, PLUGIN_PATH);
  }

  setenv (ENV_VAR, newEnv, 1);
  g_free (newEnv);

  gst_init (&argc, &argv);
  GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, GST_DEFAULT_NAME, 0,
                           GST_DEFAULT_NAME);

  check_if_plugins_are_available ();

  context = g_option_context_new ("");
  g_option_context_add_main_entries (context, entries, NULL);
  g_option_context_add_group (context, gst_init_get_option_group () );

  if (!g_option_context_parse (context, &argc, &argv, &error) ) {
    g_printerr ("option parsing failed: %s\n", error->message);
    g_option_context_free (context);
    g_error_free (error);
    exit (1);
  }

  g_option_context_free (context);

  /* Install our signal handler */
  source = setup_signalfd();
  source->attach (Glib::MainContext::get_default() );

  GST_INFO ("Kmsc version: %s", get_version () );

  if (!conf_file) {
    load_config (DEFAULT_CONFIG_FILE);
  } else {
    load_config ( (std::string) conf_file);
  }

  /* Start service */
  service->start ();

  loop->run ();

  source->destroy();

  deleteCertificate ();

  service->stop();

  delete service;

  return 0;
}
<|endoftext|>


<commit_before>/*
 * (C) Copyright 2013 Kurento (http://kurento.org/)
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl-2.1.html
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 */

#include <config.h>

#include <glibmm.h>
#include <fstream>
#include <iostream>
#include <boost/filesystem.hpp>
#include "version.hpp"
#include <glib/gstdio.h>
#include <ftw.h>

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>

#include "TransportFactory.hpp"

#include <SignalHandler.hpp>
#include <ServerMethods.hpp>
#include <gst/gst.h>

#include <boost/program_options.hpp>
#include <boost/exception/diagnostic_information.hpp>

#include "logging.hpp"
#include "modules.hpp"

#define GST_CAT_DEFAULT kurento_media_server
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
#define GST_DEFAULT_NAME "KurentoMediaServer"

const std::string DEFAULT_CONFIG_FILE = "/etc/kurento/kurento.conf.json";
const std::string ENV_PREFIX = "KURENTO_";

using namespace ::kurento;
namespace logging = boost::log;

Glib::RefPtr<Glib::MainLoop> loop = Glib::MainLoop::create ();

static std::shared_ptr<Transport>
load_config (boost::property_tree::ptree &config, const std::string &file_name)
{
  std::shared_ptr<Transport> transport;
  boost::filesystem::path configFilePath (file_name);
  GST_INFO ("Reading configuration from: %s", file_name.c_str () );

  try {
    boost::property_tree::read_json (file_name, config);
  } catch (boost::property_tree::ptree_error &e) {
    GST_ERROR ("Error reading configuration: %s", e.what() );
    std::cerr << "Error reading configuration: " << e.what() << std::endl;
    exit (1);
  }

  config.add ("configPath", configFilePath.parent_path().string() );

  GST_INFO ("Configuration loaded successfully");

  std::shared_ptr<ServerMethods> serverMethods (new ServerMethods (config) );

  try {
    transport = TransportFactory::create_transport (config, serverMethods);
  } catch (std::exception &e) {
    GST_ERROR ("Error creating transport: %s", e.what() );
    exit (1);
  }

  return transport;
}

static void
signal_handler (uint32_t signo)
{
  static unsigned int __terminated = 0;

  switch (signo) {
  case SIGINT:
  case SIGTERM:
    if (__terminated == 0) {
      GST_DEBUG ("Terminating.");
      loop->quit ();
    }

    __terminated = 1;
    break;

  case SIGPIPE:
    GST_DEBUG ("Ignore sigpipe signal");
    break;

  case SIGSEGV:
    GST_DEBUG ("Segmentation fault. Aborting process execution");
    abort ();

  default:
    break;
  }
}

static void
kms_init_dependencies (int argc, char **argv)
{
  Glib::init();

  gst_init (&argc, &argv);

  GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, GST_DEFAULT_NAME, 0,
                           GST_DEFAULT_NAME);

  /* Initialization routine to add commonly used */
  /* attributes to the logging system */
  logging::add_common_attributes();
}

int
main (int argc, char **argv)
{
  sigset_t mask;
  std::shared_ptr <SignalHandler> signalHandler;
  std::shared_ptr<Transport> transport;
  boost::property_tree::ptree config;
  std::string confFile;
  std::string path, logs_path;

  kms_init_dependencies (argc, argv);

  try {
    boost::program_options::options_description desc ("kurento-media-server usage");

    desc.add_options()
    ("help,h", "Display this help message")
    ("version,v", "Display the version number")
    ("list,l", "Lists all available factories")
    ("modules-path,p", boost::program_options::value<std::string>
     (&path), "Path where kurento modules can be found")
    ("conf-file,f", boost::program_options::value<std::string>
     (&confFile)->default_value (DEFAULT_CONFIG_FILE),
     "Configuration file location")
    ("logs-path,d", boost::program_options::value <std::string> (&logs_path),
     "Path where debug files will be stored");

    boost::program_options::command_line_parser clp (argc, argv);
    clp.options (desc).allow_unregistered();
    boost::program_options::variables_map vm;
    auto parsed = clp.run();
    boost::program_options::store (parsed, vm);

    boost::program_options::store (boost::program_options::parse_environment (desc,
    [&desc] (std::string & input) -> std::string {
      /* Look for KURENTO_ prefix and change to lower case */
      if (input.find (ENV_PREFIX) == 0)
      {
        std::string aux = input.substr (ENV_PREFIX.size() );
        std::transform (aux.begin(), aux.end(), aux.begin(), [] (int c) -> int {
          return (c == '_') ? '-' : tolower (c);
        });

        if (!desc.find_nothrow (aux, false) ) {
          return "";
        }

        return aux;
      }

      return "";
    }), vm);

    boost::program_options::notify (vm);

    if (vm.count ("logs-path") ) {
      if (kms_init_logging (logs_path) ) {
        GST_DEBUG ("Dumping logs to %s", logs_path.c_str() );
      } else {
        GST_WARNING ("Can no set debugging path %s", logs_path.c_str() );
      }
    }

    if (vm.count ("help") ) {
      std::cout << desc << "\n";
      exit (0);
    }

    if (vm.count ("version") || vm.count ("list") ) {
      // Disable log to just print version
      gst_debug_remove_log_function_by_data (NULL);
    }

    loadModules (path);

    if (vm.count ("list") ) {
      std::cout << "Available factories:" << std::endl;

      for (auto it : kurento::getModuleManager().getLoadedFactories() ) {
        std::cout << "\t" << it.first << std::endl;
      }

      exit (0);
    }

    if (vm.count ("version") ) {
      print_version();
      exit (0);
    }

  } catch (boost::program_options::error &e) {
    std::cerr <<  "Error : " << e.what() << std::endl;
    exit (1);
  }

  /* Install our signal handler */
  sigemptyset (&mask);
  sigaddset (&mask, SIGINT);
  sigaddset (&mask, SIGTERM);
  sigaddset (&mask, SIGSEGV);
  sigaddset (&mask, SIGPIPE);
  signalHandler = std::shared_ptr <SignalHandler> (new SignalHandler (mask,
                  signal_handler) );

  GST_INFO ("Kmsc version: %s", get_version () );

  transport = load_config (config, confFile);

  /* Start transport */
  transport->start ();

  GST_INFO ("Mediaserver started");

  loop->run ();

  transport->stop();

  GST_INFO ("Mediaserver stopped");

  return 0;
}
<commit_msg>Allow loading different config files for external modules<commit_after>/*
 * (C) Copyright 2013 Kurento (http://kurento.org/)
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl-2.1.html
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 */

#include <config.h>

#include <queue>
#include <boost/foreach.hpp>

#include <glibmm.h>
#include <fstream>
#include <iostream>
#include <boost/filesystem.hpp>
#include "version.hpp"
#include <glib/gstdio.h>
#include <ftw.h>

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>

#include "TransportFactory.hpp"

#include <SignalHandler.hpp>
#include <ServerMethods.hpp>
#include <gst/gst.h>

#include <boost/program_options.hpp>
#include <boost/exception/diagnostic_information.hpp>

#include "logging.hpp"
#include "modules.hpp"

#define GST_CAT_DEFAULT kurento_media_server
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
#define GST_DEFAULT_NAME "KurentoMediaServer"

const std::string DEFAULT_CONFIG_FILE = "/etc/kurento/kurento.conf.json";
const std::string ENV_PREFIX = "KURENTO_";

using namespace ::kurento;
namespace logging = boost::log;

Glib::RefPtr<Glib::MainLoop> loop = Glib::MainLoop::create ();

static std::list<std::string>
split (const std::string &s, char delim)
{
  std::list<std::string> elems;
  std::stringstream ss (s);
  std::string item;

  while (std::getline (ss, item, delim) ) {
    elems.push_back (item);
  }

  return elems;
}

static void
mergePropertyTrees (boost::property_tree::ptree &ptMerged,
                    const boost::property_tree::ptree &rptSecond )
{
  // Keep track of keys and values (subtrees) in second property tree
  std::queue<std::string> qKeys;
  std::queue<boost::property_tree::ptree> qValues;
  qValues.push ( rptSecond );

  // Iterate over second property tree
  while ( !qValues.empty() ) {
    // Setup keys and corresponding values
    boost::property_tree::ptree ptree = qValues.front();
    qValues.pop();
    std::string keychain;

    if ( !qKeys.empty() ) {
      keychain = qKeys.front();
      qKeys.pop();
    }

    BOOST_FOREACH ( const boost::property_tree::ptree::value_type & child, ptree ) {
      if ( child.second.size() == 0 ) {
        std::string s;

        if ( !keychain.empty() ) {
          s = keychain + "." + child.first.data();
        } else {
          s = child.first.data();
        }

        // Put into combined property tree
        ptMerged.put ( s, child.second.data() );
      } else {
        // Put keys (identifiers of subtrees) and all of its parents (where present)
        // aside for later iteration.
        if ( !keychain.empty() ) {
          qKeys.push ( keychain + "." + child.first.data() );
        } else {
          qKeys.push ( child.first.data() );
        }

        // Put values (the subtrees) aside, too
        qValues.push ( child.second );
      }
    }
  }
}

static void
loadModulesConfigFromDir (boost::property_tree::ptree &config,
                          const boost::filesystem::path &dir, const boost::filesystem::path &parentDir)
{
  GST_INFO ("Looking for config files in %s", dir.string().c_str() );

  if (!boost::filesystem::is_directory (dir) ) {
    GST_WARNING ("Unable to load config files from: %s, it is not a directory",
                 dir.string().c_str() );
    return;
  }

  boost::filesystem::directory_iterator end_itr;

  for ( boost::filesystem::directory_iterator itr ( dir ); itr != end_itr;
        ++itr ) {
    if (boost::filesystem::is_regular (*itr) ) {
      boost::filesystem::path extension = itr->path().extension();
      boost::filesystem::path extension2 = itr->path().stem().extension();
      std::string fileName = itr->path().filename().string();

      if (extension.string() == ".json" && extension2.string() == ".conf") {
        boost::property_tree::ptree moduleConfig;

        boost::property_tree::read_json (itr->path().string(), moduleConfig);

        moduleConfig.add ("configPath", itr->path().parent_path().string() );

        {
          boost::filesystem::path diffpath;

          boost::filesystem::path tmppath = itr->path().parent_path();

          while (tmppath != parentDir) {
            diffpath = tmppath.stem() / diffpath;
            tmppath = tmppath.parent_path();
          }

          tmppath = diffpath;

          boost::property_tree::ptree loadedConfig;
          std::string key = "modules";

          for (auto it = tmppath.begin(); it != tmppath.end(); it ++) {
            key += "." + it->string();
          }

          fileName = fileName.substr (0, fileName.size() - extension.string().size() );
          fileName = fileName.substr (0, fileName.size() - extension2.string().size() );

          key += "." + fileName;

          loadedConfig.put_child (key, moduleConfig);

          mergePropertyTrees (config, loadedConfig);
        }

        GST_INFO ("Loaded module config from: %s", itr->path().string().c_str() );
      }
    } else if (boost::filesystem::is_directory (*itr) ) {
      loadModulesConfigFromDir (config, itr->path(), parentDir);
    }
  }
}

static void
loadModulesConfig (boost::property_tree::ptree &config,
                   const boost::filesystem::path &configFilePath, std::string modulesConfigPath)
{
  std::list <std::string> locations;

  if (modulesConfigPath.empty() ) {
    boost::filesystem::path modulesConfigDir = configFilePath.parent_path() /
        "modules";

    modulesConfigPath = modulesConfigDir.string();
  }

  locations = split (modulesConfigPath, ':');

  for (std::string location : locations) {
    boost::filesystem::path dir (location);

    loadModulesConfigFromDir (config, dir, dir);
  }
}

static std::shared_ptr<Transport>
load_config (boost::property_tree::ptree &config, const std::string &file_name,
             const std::string &modulesConfigPath)
{
  std::shared_ptr<Transport> transport;
  boost::filesystem::path configFilePath (file_name);
  GST_INFO ("Reading configuration from: %s", file_name.c_str () );

  try {
    boost::property_tree::read_json (file_name, config);
  } catch (boost::property_tree::ptree_error &e) {
    GST_ERROR ("Error reading configuration: %s", e.what() );
    std::cerr << "Error reading configuration: " << e.what() << std::endl;
    exit (1);
  }

  loadModulesConfig (config, configFilePath, modulesConfigPath);

  config.add ("configPath", configFilePath.parent_path().string() );

  GST_INFO ("Configuration loaded successfully");

  std::ostringstream oss;
  boost::property_tree::write_json (oss, config, true);
  std::string jsonConfig = oss.str();

  GST_DEBUG ("Effective loaded config:\n%s", jsonConfig.c_str() );

  std::shared_ptr<ServerMethods> serverMethods (new ServerMethods (config) );

  try {
    transport = TransportFactory::create_transport (config, serverMethods);
  } catch (std::exception &e) {
    GST_ERROR ("Error creating transport: %s", e.what() );
    exit (1);
  }

  return transport;
}

static void
signal_handler (uint32_t signo)
{
  static unsigned int __terminated = 0;

  switch (signo) {
  case SIGINT:
  case SIGTERM:
    if (__terminated == 0) {
      GST_DEBUG ("Terminating.");
      loop->quit ();
    }

    __terminated = 1;
    break;

  case SIGPIPE:
    GST_DEBUG ("Ignore sigpipe signal");
    break;

  case SIGSEGV:
    GST_DEBUG ("Segmentation fault. Aborting process execution");
    abort ();

  default:
    break;
  }
}

static void
kms_init_dependencies (int argc, char **argv)
{
  Glib::init();

  gst_init (&argc, &argv);

  GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, GST_DEFAULT_NAME, 0,
                           GST_DEFAULT_NAME);

  /* Initialization routine to add commonly used */
  /* attributes to the logging system */
  logging::add_common_attributes();
}

int
main (int argc, char **argv)
{
  sigset_t mask;
  std::shared_ptr <SignalHandler> signalHandler;
  std::shared_ptr<Transport> transport;
  boost::property_tree::ptree config;
  std::string confFile;
  std::string path, logs_path, modulesConfigPath;

  kms_init_dependencies (argc, argv);

  try {
    boost::program_options::options_description desc ("kurento-media-server usage");

    desc.add_options()
    ("help,h", "Display this help message")
    ("version,v", "Display the version number")
    ("list,l", "Lists all available factories")
    ("modules-path,p", boost::program_options::value<std::string>
     (&path), "Path where kurento modules can be found")
    ("conf-file,f", boost::program_options::value<std::string>
     (&confFile)->default_value (DEFAULT_CONFIG_FILE),
     "Configuration file location")
    ("logs-path,d", boost::program_options::value <std::string> (&logs_path),
     "Path where debug files will be stored")
    ("modules-config-path,c",
     boost::program_options::value <std::string> (&modulesConfigPath),
     "Path where modules config files can be found");

    boost::program_options::command_line_parser clp (argc, argv);
    clp.options (desc).allow_unregistered();
    boost::program_options::variables_map vm;
    auto parsed = clp.run();
    boost::program_options::store (parsed, vm);

    boost::program_options::store (boost::program_options::parse_environment (desc,
    [&desc] (std::string & input) -> std::string {
      /* Look for KURENTO_ prefix and change to lower case */
      if (input.find (ENV_PREFIX) == 0)
      {
        std::string aux = input.substr (ENV_PREFIX.size() );
        std::transform (aux.begin(), aux.end(), aux.begin(), [] (int c) -> int {
          return (c == '_') ? '-' : tolower (c);
        });

        if (!desc.find_nothrow (aux, false) ) {
          return "";
        }

        return aux;
      }

      return "";
    }), vm);

    boost::program_options::notify (vm);

    if (vm.count ("logs-path") ) {
      if (kms_init_logging (logs_path) ) {
        GST_DEBUG ("Dumping logs to %s", logs_path.c_str() );
      } else {
        GST_WARNING ("Can no set debugging path %s", logs_path.c_str() );
      }
    }

    if (vm.count ("help") ) {
      std::cout << desc << "\n";
      exit (0);
    }

    if (vm.count ("version") || vm.count ("list") ) {
      // Disable log to just print version
      gst_debug_remove_log_function_by_data (NULL);
    }

    loadModules (path);

    if (vm.count ("list") ) {
      std::cout << "Available factories:" << std::endl;

      for (auto it : kurento::getModuleManager().getLoadedFactories() ) {
        std::cout << "\t" << it.first << std::endl;
      }

      exit (0);
    }

    if (vm.count ("version") ) {
      print_version();
      exit (0);
    }

  } catch (boost::program_options::error &e) {
    std::cerr <<  "Error : " << e.what() << std::endl;
    exit (1);
  }

  /* Install our signal handler */
  sigemptyset (&mask);
  sigaddset (&mask, SIGINT);
  sigaddset (&mask, SIGTERM);
  sigaddset (&mask, SIGSEGV);
  sigaddset (&mask, SIGPIPE);
  signalHandler = std::shared_ptr <SignalHandler> (new SignalHandler (mask,
                  signal_handler) );

  GST_INFO ("Kmsc version: %s", get_version () );

  transport = load_config (config, confFile, modulesConfigPath);

  /* Start transport */
  transport->start ();

  GST_INFO ("Mediaserver started");

  loop->run ();

  transport->stop();

  GST_INFO ("Mediaserver stopped");

  return 0;
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 Cloudius Systems, Ltd.
 */

#pragma once

#include <unordered_map>
#include <boost/range/iterator_range.hpp>
#include <boost/range/join.hpp>
#include <boost/range/algorithm/transform.hpp>

#include "cql3/column_specification.hh"
#include "core/shared_ptr.hh"
#include "types.hh"
#include "tuple.hh"
#include "gc_clock.hh"
#include "unimplemented.hh"

using column_id = uint32_t;

class column_definition final {
public:
    template<typename ColumnRange>
    static std::vector<const column_definition*> vectorize(ColumnRange&& columns) {
        std::vector<const column_definition*> r;
        boost::transform(std::forward<ColumnRange>(columns), std::back_inserter(r), [] (auto& def) { return &def; });
        return r;
    }
private:
    bytes _name;
public:
    enum class column_kind { PARTITION, CLUSTERING, REGULAR, STATIC };
    column_definition(bytes name, data_type type, column_id id, column_kind kind);
    data_type type;

    // Unique within (kind, schema instance).
    // schema::position() and component_index() depend on the fact that for PK columns this is
    // equivalent to component index.
    column_id id;

    column_kind kind;
    ::shared_ptr<cql3::column_specification> column_specification;
    bool is_static() const { return kind == column_kind::STATIC; }
    bool is_regular() const { return kind == column_kind::REGULAR; }
    bool is_partition_key() const { return kind == column_kind::PARTITION; }
    bool is_clustering_key() const { return kind == column_kind::CLUSTERING; }
    bool is_primary_key() const { return kind == column_kind::PARTITION || kind == column_kind::CLUSTERING; }
    bool is_atomic() const { return !type->is_multi_cell(); }
    bool is_compact_value() const;
    const sstring& name_as_text() const;
    const bytes& name() const;
    friend std::ostream& operator<<(std::ostream& os, const column_definition& cd) {
        return os << cd.name_as_text();
    }
    uint32_t component_index() const {
        assert(is_primary_key());
        return id;
    }
};

struct thrift_schema {
    shared_ptr<abstract_type> partition_key_type;
};

// Schema fields which can be safely default-copied
// FIXME: encapsulate public fields so that we can make this a private inner structure of schema
class raw_schema {
protected:
    std::vector<column_definition> _partition_key;
    std::vector<column_definition> _clustering_key;
    std::vector<column_definition> _regular_columns; // sorted by name
    std::vector<column_definition> _static_columns; // sorted by name, present only when there's any clustering column
    sstring _comment;
public:
    gc_clock::duration default_time_to_live = gc_clock::duration::zero();
    sstring ks_name;
    sstring cf_name;
    lw_shared_ptr<tuple_type<>> partition_key_type;
    lw_shared_ptr<tuple_type<>> clustering_key_type;
    lw_shared_ptr<tuple_prefix> clustering_key_prefix_type;
    data_type regular_column_name_type;
    thrift_schema thrift;
};

/*
 * Keep this effectively immutable.
 */
class schema final : public raw_schema {
private:
    std::unordered_map<bytes, const column_definition*> _columns_by_name;
    std::map<bytes, const column_definition*, serialized_compare> _regular_columns_by_name;
public:
    struct column {
        bytes name;
        data_type type;
        struct name_compare {
            shared_ptr<abstract_type> type;
            name_compare(shared_ptr<abstract_type> type) : type(type) {}
            bool operator()(const column& cd1, const column& cd2) const {
                return type->less(cd1.name, cd2.name);
            }
        };
    };
private:
    void build_columns(const std::vector<column>& columns, column_definition::column_kind kind, std::vector<column_definition>& dst);
    ::shared_ptr<cql3::column_specification> make_column_specification(const column_definition& def);
    void rehash_columns();
public:
    schema(sstring ks_name, sstring cf_name,
        std::vector<column> partition_key,
        std::vector<column> clustering_key,
        std::vector<column> regular_columns,
        std::vector<column> static_columns,
        shared_ptr<abstract_type> regular_column_name_type,
        sstring comment = {});
    schema(const schema&);
    bool is_dense() const {
        return false;
    }
    bool is_counter() const {
        return false;
    }
    const column_definition* get_column_definition(const bytes& name);
    auto regular_begin() {
        return _regular_columns.begin();
    }
    auto regular_end() {
        return _regular_columns.end();
    }
    auto regular_lower_bound(const bytes& name) {
        // TODO: use regular_columns and a version of std::lower_bound() with heterogeneous comparator
        auto i = _regular_columns_by_name.lower_bound(name);
        if (i == _regular_columns_by_name.end()) {
            return regular_end();
        } else {
            return _regular_columns.begin() + i->second->id;
        }
    }
    auto regular_upper_bound(const bytes& name) {
        // TODO: use regular_columns and a version of std::upper_bound() with heterogeneous comparator
        auto i = _regular_columns_by_name.upper_bound(name);
        if (i == _regular_columns_by_name.end()) {
            return regular_end();
        } else {
            return _regular_columns.begin() + i->second->id;
        }
    }
    data_type column_name_type(const column_definition& def) {
        return def.kind == column_definition::column_kind::REGULAR ? regular_column_name_type : utf8_type;
    }
    column_definition& regular_column_at(column_id id) {
        return _regular_columns[id];
    }
    column_definition& static_column_at(column_id id) {
        return _static_columns[id];
    }
    bool is_last_partition_key(const column_definition& def) {
        return &_partition_key[_partition_key.size() - 1] == &def;
    }
    bool has_collections() {
        warn(unimplemented::cause::COLLECTIONS);
        return false; // FIXME
    }
    bool has_static_columns() {
        return !_static_columns.empty();
    }
    size_t partition_key_size() const { return _partition_key.size(); }
    size_t clustering_key_size() const { return _clustering_key.size(); }
    size_t static_columns_count() const { return _static_columns.size(); }
    size_t regular_columns_count() const { return _regular_columns.size(); }
    // Returns a range of column definitions
    auto partition_key_columns() const {
        return boost::make_iterator_range(_partition_key.begin(), _partition_key.end());
    }
    // Returns a range of column definitions
    auto clustering_key_columns() const {
        return boost::make_iterator_range(_clustering_key.begin(), _clustering_key.end());
    }
    // Returns a range of column definitions
    auto static_columns() const {
        return boost::make_iterator_range(_static_columns.begin(), _static_columns.end());
    }
    // Returns a range of column definitions
    auto regular_columns() const {
        return boost::make_iterator_range(_regular_columns.begin(), _regular_columns.end());
    }
    // Returns a range of column definitions
    auto all_columns_in_select_order() const {
        return boost::join(partition_key_columns(),
            boost::join(clustering_key_columns(),
            boost::join(static_columns(), regular_columns())));
    }
    uint32_t position(const column_definition& column) const {
        if (column.is_primary_key()) {
            return column.id;
        }
        return _clustering_key.size();
    }
};

using schema_ptr = lw_shared_ptr<schema>;
<commit_msg>Add schema::set_comment() member function<commit_after>/*
 * Copyright (C) 2015 Cloudius Systems, Ltd.
 */

#pragma once

#include <unordered_map>
#include <boost/range/iterator_range.hpp>
#include <boost/range/join.hpp>
#include <boost/range/algorithm/transform.hpp>

#include "cql3/column_specification.hh"
#include "core/shared_ptr.hh"
#include "types.hh"
#include "tuple.hh"
#include "gc_clock.hh"
#include "unimplemented.hh"

using column_id = uint32_t;

class column_definition final {
public:
    template<typename ColumnRange>
    static std::vector<const column_definition*> vectorize(ColumnRange&& columns) {
        std::vector<const column_definition*> r;
        boost::transform(std::forward<ColumnRange>(columns), std::back_inserter(r), [] (auto& def) { return &def; });
        return r;
    }
private:
    bytes _name;
public:
    enum class column_kind { PARTITION, CLUSTERING, REGULAR, STATIC };
    column_definition(bytes name, data_type type, column_id id, column_kind kind);
    data_type type;

    // Unique within (kind, schema instance).
    // schema::position() and component_index() depend on the fact that for PK columns this is
    // equivalent to component index.
    column_id id;

    column_kind kind;
    ::shared_ptr<cql3::column_specification> column_specification;
    bool is_static() const { return kind == column_kind::STATIC; }
    bool is_regular() const { return kind == column_kind::REGULAR; }
    bool is_partition_key() const { return kind == column_kind::PARTITION; }
    bool is_clustering_key() const { return kind == column_kind::CLUSTERING; }
    bool is_primary_key() const { return kind == column_kind::PARTITION || kind == column_kind::CLUSTERING; }
    bool is_atomic() const { return !type->is_multi_cell(); }
    bool is_compact_value() const;
    const sstring& name_as_text() const;
    const bytes& name() const;
    friend std::ostream& operator<<(std::ostream& os, const column_definition& cd) {
        return os << cd.name_as_text();
    }
    uint32_t component_index() const {
        assert(is_primary_key());
        return id;
    }
};

struct thrift_schema {
    shared_ptr<abstract_type> partition_key_type;
};

// Schema fields which can be safely default-copied
// FIXME: encapsulate public fields so that we can make this a private inner structure of schema
class raw_schema {
protected:
    std::vector<column_definition> _partition_key;
    std::vector<column_definition> _clustering_key;
    std::vector<column_definition> _regular_columns; // sorted by name
    std::vector<column_definition> _static_columns; // sorted by name, present only when there's any clustering column
    sstring _comment;
public:
    gc_clock::duration default_time_to_live = gc_clock::duration::zero();
    sstring ks_name;
    sstring cf_name;
    lw_shared_ptr<tuple_type<>> partition_key_type;
    lw_shared_ptr<tuple_type<>> clustering_key_type;
    lw_shared_ptr<tuple_prefix> clustering_key_prefix_type;
    data_type regular_column_name_type;
    thrift_schema thrift;
};

/*
 * Keep this effectively immutable.
 */
class schema final : public raw_schema {
private:
    std::unordered_map<bytes, const column_definition*> _columns_by_name;
    std::map<bytes, const column_definition*, serialized_compare> _regular_columns_by_name;
public:
    struct column {
        bytes name;
        data_type type;
        struct name_compare {
            shared_ptr<abstract_type> type;
            name_compare(shared_ptr<abstract_type> type) : type(type) {}
            bool operator()(const column& cd1, const column& cd2) const {
                return type->less(cd1.name, cd2.name);
            }
        };
    };
private:
    void build_columns(const std::vector<column>& columns, column_definition::column_kind kind, std::vector<column_definition>& dst);
    ::shared_ptr<cql3::column_specification> make_column_specification(const column_definition& def);
    void rehash_columns();
public:
    schema(sstring ks_name, sstring cf_name,
        std::vector<column> partition_key,
        std::vector<column> clustering_key,
        std::vector<column> regular_columns,
        std::vector<column> static_columns,
        shared_ptr<abstract_type> regular_column_name_type,
        sstring comment = {});
    schema(const schema&);
    void set_comment(const sstring& comment) {
        _comment = comment;
    }
    bool is_dense() const {
        return false;
    }
    bool is_counter() const {
        return false;
    }
    const column_definition* get_column_definition(const bytes& name);
    auto regular_begin() {
        return _regular_columns.begin();
    }
    auto regular_end() {
        return _regular_columns.end();
    }
    auto regular_lower_bound(const bytes& name) {
        // TODO: use regular_columns and a version of std::lower_bound() with heterogeneous comparator
        auto i = _regular_columns_by_name.lower_bound(name);
        if (i == _regular_columns_by_name.end()) {
            return regular_end();
        } else {
            return _regular_columns.begin() + i->second->id;
        }
    }
    auto regular_upper_bound(const bytes& name) {
        // TODO: use regular_columns and a version of std::upper_bound() with heterogeneous comparator
        auto i = _regular_columns_by_name.upper_bound(name);
        if (i == _regular_columns_by_name.end()) {
            return regular_end();
        } else {
            return _regular_columns.begin() + i->second->id;
        }
    }
    data_type column_name_type(const column_definition& def) {
        return def.kind == column_definition::column_kind::REGULAR ? regular_column_name_type : utf8_type;
    }
    column_definition& regular_column_at(column_id id) {
        return _regular_columns[id];
    }
    column_definition& static_column_at(column_id id) {
        return _static_columns[id];
    }
    bool is_last_partition_key(const column_definition& def) {
        return &_partition_key[_partition_key.size() - 1] == &def;
    }
    bool has_collections() {
        warn(unimplemented::cause::COLLECTIONS);
        return false; // FIXME
    }
    bool has_static_columns() {
        return !_static_columns.empty();
    }
    size_t partition_key_size() const { return _partition_key.size(); }
    size_t clustering_key_size() const { return _clustering_key.size(); }
    size_t static_columns_count() const { return _static_columns.size(); }
    size_t regular_columns_count() const { return _regular_columns.size(); }
    // Returns a range of column definitions
    auto partition_key_columns() const {
        return boost::make_iterator_range(_partition_key.begin(), _partition_key.end());
    }
    // Returns a range of column definitions
    auto clustering_key_columns() const {
        return boost::make_iterator_range(_clustering_key.begin(), _clustering_key.end());
    }
    // Returns a range of column definitions
    auto static_columns() const {
        return boost::make_iterator_range(_static_columns.begin(), _static_columns.end());
    }
    // Returns a range of column definitions
    auto regular_columns() const {
        return boost::make_iterator_range(_regular_columns.begin(), _regular_columns.end());
    }
    // Returns a range of column definitions
    auto all_columns_in_select_order() const {
        return boost::join(partition_key_columns(),
            boost::join(clustering_key_columns(),
            boost::join(static_columns(), regular_columns())));
    }
    uint32_t position(const column_definition& column) const {
        if (column.is_primary_key()) {
            return column.id;
        }
        return _clustering_key.size();
    }
};

using schema_ptr = lw_shared_ptr<schema>;
<|endoftext|>


<commit_before>// Copyright 2010 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// A tool that writes the JS files given to it into an appropriate binary
// NamedScript proto, for use by the C++ test driver.
//
// TODO(jacobsa): This tool should also handle generating HTML appropriate for
// the browser driver.

#include <string.h>
#include <string>
#include <vector>

#include <gflags/gflags.h>
#include <glog/logging.h>

#include "base/integral_types.h"
#include "base/logging.h"
#include "base/stringprintf.h"
#include "file/file_utils.h"
#include "gjstest/internal/compiler/compiler.pb.h"
#include "strings/strutil.h"

DEFINE_string(js, "", "Give this flag once per JS file, topologically sorted.");
DEFINE_string(binarypb_file, "", "Path to the .binarypb output file.");

using gjstest::NamedScript;
using gjstest::NamedScripts;

int main(int argc, char** argv) {
  google::InitGoogleLogging(argv[0]);

  // Build a list of files to include, in the correct order. This tool should be
  // invoked with a command like this:
  //
  //     path/to/compiler
  //         '--js=testing/gjstest/internal/namespace.js'
  //         '--js=testing/gjstest/public/matcher_context.js'
  //         ...
  //
  // The C++ flags package doesn't support repeated flags, so we iterate through
  // the arguments here before calling the flags package.
  std::vector<std::string> script_paths;
  for (uint32 i = 1; i < static_cast<uint32>(argc); ++i) {
    const std::string arg = argv[i];
    if (HasPrefixString(arg, "--js=")) {
      script_paths.push_back(StripPrefixString(arg, "--js="));
    }
  }

  // Now initialize flag stuff.
  google::ParseCommandLineFlags(&argc, &argv, true);

  // Grab each script.
  NamedScripts result;
  for (uint32 i = 0; i < script_paths.size(); ++i) {
    const std::string& path = script_paths[i];

    NamedScript* script = result.add_script();
    script->set_name(Basename(path));
    script->set_source(ReadFileOrDie(path));
  }

  // Write the .binarypb output file.
  WriteStringToFileOrDie(result.SerializeAsString(), FLAGS_binarypb_file);

  return 0;
}
<commit_msg>Deleted compiler.cc, which is no longer used.<commit_after><|endoftext|>


<commit_before>#include <string.h>
#include <time.h>
#include "barconfig.hpp"
#include "file_to.hpp"
#include "set_icon.hpp"

using namespace std;

int set_battery (barconfig &myconfig) {
	bool fail;
	fail = false;
	string out, path, icon, status, width = "12:00 100%";
	long perc, e_now, e_full ,p_now, seconds;
	path = myconfig.device;
	path += "/capacity";
	perc = file_to_long (path.c_str(), fail);
	path = myconfig.device;
	path += "/status";
	status = file_to_string (path.c_str(), fail);
	path = myconfig.device;
	path += "/energy_now";
	e_now = file_to_long (path.c_str(), fail);
	path = myconfig.device;
	path += "/power_now";
	p_now = file_to_long (path.c_str(), fail);
	if (fail == true)
		return 0;
	if (p_now != 0)
		if (strcmp (status.c_str(), "Discharging\n") == 0)
			seconds = e_now * 3600 / p_now;
		else {
			path = myconfig.device;
			path += "/energy_full";
			e_full = file_to_long (path.c_str(), fail);
			if (fail == true)
				return 0;
			seconds = (e_full - e_now) * 3600 / p_now;
			}
		else
			seconds = 0;
	tm *tpoint = gmtime(&seconds);
	char buffer[128];
	strftime (buffer,128,"%H:%M",tpoint);
	out = buffer;
	out += " " + to_string (perc) + "%";
	json_object_object_add(myconfig.json_output, "full_text", json_object_new_string (out.c_str()));
	json_object_object_add(myconfig.json_output, "name", json_object_new_string (myconfig.name));
	json_object_object_add(myconfig.json_output, "min_width", json_object_new_string (width.c_str()));
	json_object_object_add(myconfig.json_output, "align", json_object_new_string (myconfig.align));
	if (perc <= myconfig.urgent) {
		json_object_object_add(myconfig.json_output, "color", json_object_new_string (myconfig.color_urgent));
		json_object_object_add(myconfig.json_output, "icon_color", json_object_new_string (myconfig.color_urgent));
	} else {
		json_object_object_add(myconfig.json_output, "color", json_object_new_string (myconfig.color));
		json_object_object_add(myconfig.json_output, "icon_color", json_object_new_string (myconfig.color));
		}
	set_icon (myconfig);
	set_icon_mask (myconfig, perc, 100);
	return 0;
}
<commit_msg>fix battery charging icon & color<commit_after>#include <string.h>
#include <time.h>
#include "barconfig.hpp"
#include "file_to.hpp"
#include "set_icon.hpp"

using namespace std;

int set_battery (barconfig &myconfig) {
	bool fail;
	fail = false;
	string out, path, icon, status, width = "12:00 100%";
	long perc, e_now, e_full ,p_now, seconds;
	path = myconfig.device;
	path += "/capacity";
	perc = file_to_long (path.c_str(), fail);
	path = myconfig.device;
	path += "/status";
	status = file_to_string (path.c_str(), fail);
	path = myconfig.device;
	path += "/energy_now";
	e_now = file_to_long (path.c_str(), fail);
	path = myconfig.device;
	path += "/power_now";
	p_now = file_to_long (path.c_str(), fail);
	if (fail == true)
		return 0;
	if (p_now != 0)
		if (strcmp (status.c_str(), "Discharging\n") == 0)
			seconds = e_now * 3600 / p_now;
		else {
			path = myconfig.device;
			path += "/energy_full";
			e_full = file_to_long (path.c_str(), fail);
			if (fail == true)
				return 0;
			seconds = (e_full - e_now) * 3600 / p_now;
			}
		else
			seconds = 0;
	tm *tpoint = gmtime(&seconds);
	char buffer[128];
	strftime (buffer,128,"%H:%M",tpoint);
	out = buffer;
	out += " " + to_string (perc) + "%";
	json_object_object_add(myconfig.json_output, "full_text", json_object_new_string (out.c_str()));
	json_object_object_add(myconfig.json_output, "name", json_object_new_string (myconfig.name));
	json_object_object_add(myconfig.json_output, "min_width", json_object_new_string (width.c_str()));
	json_object_object_add(myconfig.json_output, "align", json_object_new_string (myconfig.align));
	if (strcmp (status.c_str(), "Discharging\n") == 0) {
		if (perc <= myconfig.urgent) {
			json_object_object_add(myconfig.json_output, "color", json_object_new_string (myconfig.color_urgent));
			json_object_object_add(myconfig.json_output, "icon_color", json_object_new_string (myconfig.color_urgent));
		} else {
			json_object_object_add(myconfig.json_output, "color", json_object_new_string (myconfig.color));
			json_object_object_add(myconfig.json_output, "icon_color", json_object_new_string (myconfig.color));
			}
		set_icon_mask (myconfig, perc, 100);
	} else {
		json_object_object_add(myconfig.json_output, "color", json_object_new_string (myconfig.color));
		json_object_object_add(myconfig.json_output, "icon_color", json_object_new_string (myconfig.color));
		set_icon_mask_zero (myconfig);
		}
	set_icon (myconfig);
	return 0;
}
<|endoftext|>


<commit_before>#include "StateTransfer.h"
#include "Userdata.h"
#include <cstring>
#include <cstdlib>
#include <exception>

namespace LuaPoco
{

const char* STATE_TRANSFER_SOURCE_TABLES = "Poco.StateTransfer.Source.Tables";
const char* STATE_TRANSFER_SOURCE_TABLES_LASTINDEX = "Poco.StateTransfer.Source.LastIndex";
const char* STATE_TRANSFER_DESTINATION_TABLES = "Poco.StateTransfer.Destination.Tables";

#define DEFAULT_TRANSFER_BUFF_SIZE 1000

TransferBuffer::TransferBuffer() :
    mBuffer(NULL), mReadIdx(0), mWriteIdx(0)
{
    mCapacity = DEFAULT_TRANSFER_BUFF_SIZE;
    mBuffer = static_cast<char*>(std::malloc(mCapacity));
    if (!mBuffer) throw std::bad_alloc();
}

TransferBuffer::TransferBuffer(size_t startSize) :
    mBuffer(NULL), mReadIdx(0), mWriteIdx(0)
{
    mCapacity = startSize > 0 ? startSize : DEFAULT_TRANSFER_BUFF_SIZE;
    mBuffer = static_cast<char*>(std::malloc(mCapacity));
    if (!mBuffer) throw std::bad_alloc();
}

TransferBuffer::~TransferBuffer()
{
    std::free(mBuffer);
    mBuffer = NULL;
}

void TransferBuffer::insert(const char* data, size_t amount)
{
    if (amount > mCapacity - mWriteIdx)
    {
        mCapacity += amount + 1000;

        char* tempBuffer = static_cast<char*>(std::realloc(mBuffer, mCapacity));
        if (tempBuffer) mBuffer = tempBuffer;
        else throw std::bad_alloc();
    }

    std::memcpy(mBuffer + mWriteIdx, data, amount);
    mWriteIdx += amount;
}

void TransferBuffer::get(const char*& data, size_t& amount)
{
    size_t available = mWriteIdx - mReadIdx;

    amount = available;
    if (available == 0) data = NULL;
    else data = mBuffer + mReadIdx;

    mReadIdx += amount;
}

size_t TransferBuffer::contentSize()
{
    return mWriteIdx;
}

int functionWriter(lua_State* L, const void* p, size_t sz, void* ud)
{
    int rv = 0;

    TransferBuffer* tb = static_cast<TransferBuffer*>(ud);
    try
    {
        tb->insert(static_cast<const char*>(p), sz);
    }
    catch (const std::exception& e)
    {
        (void) e;
        // only likely scenario is a std::bad_alloc
        rv = -1;
    }

    return rv;
}

const char* functionReader(lua_State* L, void* data, size_t* size)
{
    const char* rv = NULL;
    TransferBuffer* tb = static_cast<TransferBuffer*>(data);
    tb->get(rv, *size);

    return rv;
}

bool transferFunction(lua_State* toL, lua_State* fromL)
{
    bool result = false;
    if (!lua_iscfunction(fromL, -1) && lua_isfunction(fromL, -1))
    {
        try
        {
            TransferBuffer tb;

            #if LUA_VERSION_NUM > 502
            if (lua_dump(fromL, functionWriter, &tb, 0) == 0)
            #else
            if (lua_dump(fromL, functionWriter, &tb) == 0)
            #endif
            {
                #if LUA_VERSION_NUM > 501
                if (lua_load(toL, functionReader, &tb, "transferFunction", NULL) == 0)
                    result = true;
                #else
                if (lua_load(toL, functionReader, &tb, "transferFunction") == 0)
                    result = true;
                #endif
            }
        }
        catch (const std::exception& e)
        {
            (void) e;
            // catch a std::bad_alloc
            result = false;
        }
    }

    return result;
}

// creates a table on toL, and stores it in REGISTRY[STATE_TRANSFER_DESTINATION_TABLES].
// the source table at the top of fromL, is stored in REGISTRY[STATE_TRANSFER_SOURCE_TABLES]
// populateTables() will later take each source and destination values from the registry and use
// populateTableValues() to transfer individual values.
//
// the rationale is to create an iterative way to populate nested tables across states, rather than
// a recursive approach which is constrained by the call stack size.
bool transferTable(lua_State* toL, lua_State* fromL)
{
    bool result = false;

    // create new table, which will be left on stack when done.
    lua_newtable(toL);

    // fetch the transfer tables.
    lua_getfield(toL, LUA_REGISTRYINDEX, STATE_TRANSFER_DESTINATION_TABLES);
    lua_getfield(fromL, LUA_REGISTRYINDEX, STATE_TRANSFER_SOURCE_TABLES);

    // these transfer tables are required to be present, but check for safety sake anyway.
    if (lua_istable(fromL, -1) && lua_istable(toL, -1))
    {
        // fetch the lastindex where a table is stored, 0/nil means no tables stored.
        lua_getfield(fromL, -1, STATE_TRANSFER_SOURCE_TABLES_LASTINDEX);
        lua_Integer lastIndex = lua_tointeger(fromL, -1);
        lua_pop(fromL, 1);
        ++lastIndex;

        // duplicate the tables to be transferred and set them in the respective transfer table.
        lua_pushvalue(fromL, -2);
        lua_rawseti(fromL, -2, lastIndex);

        lua_pushvalue(toL, -2);
        lua_rawseti(toL, -2, lastIndex);

        lua_pushinteger(fromL, lastIndex);
        lua_setfield(fromL, -2, STATE_TRANSFER_SOURCE_TABLES_LASTINDEX);

        result = true;
    }

    // remove transfer tables from stack, leaving the orignals that were transferred.
    lua_pop(fromL, 1);
    lua_pop(toL, 1);

    return result;
}

bool transferValueInternal(lua_State* toL, lua_State* fromL)
{
    bool result = false;
    int type = lua_type(fromL, -1);

    switch (type)
    {
    case LUA_TNIL:
        lua_pushnil(toL);
        result = true;
        break;
    case LUA_TNUMBER:
        lua_pushnumber(toL, lua_tonumber(fromL, -1));
        result = true;
        break;
    case LUA_TBOOLEAN:
        lua_pushboolean(toL, lua_toboolean(fromL, -1));
        result = true;
        break;
    case LUA_TSTRING:
    {
        size_t len;
        const char* str = lua_tolstring(fromL, -1, &len);
        lua_pushlstring(toL, str, len);
        result = true;
        break;
    }
    case LUA_TTABLE:
    {
        result = transferTable(toL, fromL);
        break;
    }
    case LUA_TFUNCTION:
        result = transferFunction(toL, fromL);
        break;
    case LUA_TUSERDATA:
    {
        Userdata* ud = getPrivateUserdata(fromL, -1);
        if (ud)
            result = ud->copyToState(toL);
        break;
    }
    case LUA_TTHREAD:
        // copying coroutines is not supported.
        break;
    case LUA_TLIGHTUSERDATA:
        lua_pushlightuserdata(toL, lua_touserdata(fromL, -1));
        result = true;
        break;
    default:
        break;
    }

    return result;
}

// destination table, and source table should be at -1 index.
bool transferTableValues(lua_State* toL, lua_State* fromL)
{
    bool result = true;

    lua_pushnil(fromL);

    // iterate key/value pairs
    // transfer them to the target state
    // set them in the destination table.
    while (lua_next(fromL, -2))
    {
        // duplicate key so it can be transferred first.
        lua_pushvalue(fromL, -2);

        // transfer key
        if (transferValueInternal(toL, fromL))
        {
            // pop extra key
            lua_pop(fromL, 1);
            // transfer value
            if (transferValueInternal(toL, fromL))
            {
                // pop value
                lua_pop(fromL, 1);
                // set the transfered value, key, leaving just the table.
                lua_rawset(toL, -3);
            }
            else
            {
                result = false;
                break;
            }
        }
        else
        {
            result = false;
            break;
        }
    }

    return result;
}

bool populateTables(lua_State* toL, lua_State* fromL)
{
    bool result = true;
    int fromTop = lua_gettop(fromL);
    int toTop = lua_gettop(toL);

    lua_getfield(fromL, LUA_REGISTRYINDEX, STATE_TRANSFER_SOURCE_TABLES);
    lua_getfield(toL, LUA_REGISTRYINDEX, STATE_TRANSFER_DESTINATION_TABLES);

    if (lua_istable(fromL, -1) && lua_istable(toL, -1))
    {
        while (true)
        {
            // lastIndex = registry.source_tables.lastindex
            lua_getfield(fromL, -1, STATE_TRANSFER_SOURCE_TABLES_LASTINDEX);
            lua_Integer lastIndex = lua_tointeger(fromL, -1);
            lua_pop(fromL, 1);

            // check if there is work to do, if not return.
            if (lastIndex == 0) { break; }

            // fetch the last added source/destination table pair.
            lua_rawgeti(fromL, -1, lastIndex);
            lua_rawgeti(toL, -1, lastIndex);

            // remove these tables from the in-progress transfer table list.
            // this frees up this slot and prevents "holes" in the table for any upcoming
            // nested tables.
            lua_pushnil(fromL);
            lua_rawseti(fromL, -3, lastIndex);
            lua_pushnil(toL);
            lua_rawseti(toL, -3, lastIndex);

            // reset the last index value
            lua_pushinteger(fromL, --lastIndex);
            lua_setfield(fromL, -3, STATE_TRANSFER_SOURCE_TABLES_LASTINDEX);

            if (!transferTableValues(toL, fromL))
            {
                result = false;
                break;
            }

            // source key/value pairs have been copied to destination table.
            // pop source/destination from stack.
            lua_pop(fromL, 1);
            lua_pop(toL, 1);
        }
    }

    lua_settop(fromL, fromTop);
    lua_settop(toL, toTop);

    return result;
}

bool transferValue(lua_State* toL, lua_State* fromL)
{
    bool result = false;
    bool isTableTransfer = lua_istable(fromL, -1);

    // prepare transfer tables only if we're operating on a table
    if (isTableTransfer)
    {
        lua_newtable(fromL);
        lua_setfield(fromL, LUA_REGISTRYINDEX, STATE_TRANSFER_SOURCE_TABLES);
        lua_newtable(toL);
        lua_setfield(toL, LUA_REGISTRYINDEX, STATE_TRANSFER_DESTINATION_TABLES);
    }

    result = transferValueInternal(toL, fromL);

    // remove transfer tables in registry
    if (isTableTransfer)
    {
        // do not attempt populating the tables if table creation failed.
        if (result) { result = populateTables(toL, fromL); }

        lua_pushnil(fromL);
        lua_setfield(fromL, LUA_REGISTRYINDEX, STATE_TRANSFER_SOURCE_TABLES);
        lua_pushnil(toL);
        lua_setfield(toL, LUA_REGISTRYINDEX, STATE_TRANSFER_DESTINATION_TABLES);
    }

    return result;
}

} // LuaPoco
<commit_msg>transfer numbers that are integers as an integer for 5.3 or greater.<commit_after>#include "StateTransfer.h"
#include "Userdata.h"
#include <cstring>
#include <cstdlib>
#include <exception>

namespace LuaPoco
{

const char* STATE_TRANSFER_SOURCE_TABLES = "Poco.StateTransfer.Source.Tables";
const char* STATE_TRANSFER_SOURCE_TABLES_LASTINDEX = "Poco.StateTransfer.Source.LastIndex";
const char* STATE_TRANSFER_DESTINATION_TABLES = "Poco.StateTransfer.Destination.Tables";

#define DEFAULT_TRANSFER_BUFF_SIZE 1000

TransferBuffer::TransferBuffer() :
    mBuffer(NULL), mReadIdx(0), mWriteIdx(0)
{
    mCapacity = DEFAULT_TRANSFER_BUFF_SIZE;
    mBuffer = static_cast<char*>(std::malloc(mCapacity));
    if (!mBuffer) throw std::bad_alloc();
}

TransferBuffer::TransferBuffer(size_t startSize) :
    mBuffer(NULL), mReadIdx(0), mWriteIdx(0)
{
    mCapacity = startSize > 0 ? startSize : DEFAULT_TRANSFER_BUFF_SIZE;
    mBuffer = static_cast<char*>(std::malloc(mCapacity));
    if (!mBuffer) throw std::bad_alloc();
}

TransferBuffer::~TransferBuffer()
{
    std::free(mBuffer);
    mBuffer = NULL;
}

void TransferBuffer::insert(const char* data, size_t amount)
{
    if (amount > mCapacity - mWriteIdx)
    {
        mCapacity += amount + 1000;

        char* tempBuffer = static_cast<char*>(std::realloc(mBuffer, mCapacity));
        if (tempBuffer) mBuffer = tempBuffer;
        else throw std::bad_alloc();
    }

    std::memcpy(mBuffer + mWriteIdx, data, amount);
    mWriteIdx += amount;
}

void TransferBuffer::get(const char*& data, size_t& amount)
{
    size_t available = mWriteIdx - mReadIdx;

    amount = available;
    if (available == 0) data = NULL;
    else data = mBuffer + mReadIdx;

    mReadIdx += amount;
}

size_t TransferBuffer::contentSize()
{
    return mWriteIdx;
}

int functionWriter(lua_State* L, const void* p, size_t sz, void* ud)
{
    int rv = 0;

    TransferBuffer* tb = static_cast<TransferBuffer*>(ud);
    try
    {
        tb->insert(static_cast<const char*>(p), sz);
    }
    catch (const std::exception& e)
    {
        (void) e;
        // only likely scenario is a std::bad_alloc
        rv = -1;
    }

    return rv;
}

const char* functionReader(lua_State* L, void* data, size_t* size)
{
    const char* rv = NULL;
    TransferBuffer* tb = static_cast<TransferBuffer*>(data);
    tb->get(rv, *size);

    return rv;
}

bool transferFunction(lua_State* toL, lua_State* fromL)
{
    bool result = false;
    if (!lua_iscfunction(fromL, -1) && lua_isfunction(fromL, -1))
    {
        try
        {
            TransferBuffer tb;

            #if LUA_VERSION_NUM > 502
            if (lua_dump(fromL, functionWriter, &tb, 0) == 0)
            #else
            if (lua_dump(fromL, functionWriter, &tb) == 0)
            #endif
            {
                #if LUA_VERSION_NUM > 501
                if (lua_load(toL, functionReader, &tb, "transferFunction", NULL) == 0)
                    result = true;
                #else
                if (lua_load(toL, functionReader, &tb, "transferFunction") == 0)
                    result = true;
                #endif
            }
        }
        catch (const std::exception& e)
        {
            (void) e;
            // catch a std::bad_alloc
            result = false;
        }
    }

    return result;
}

// creates a table on toL, and stores it in REGISTRY[STATE_TRANSFER_DESTINATION_TABLES].
// the source table at the top of fromL, is stored in REGISTRY[STATE_TRANSFER_SOURCE_TABLES]
// populateTables() will later take each source and destination values from the registry and use
// populateTableValues() to transfer individual values.
//
// the rationale is to create an iterative way to populate nested tables across states, rather than
// a recursive approach which is constrained by the call stack size.
bool transferTable(lua_State* toL, lua_State* fromL)
{
    bool result = false;

    // create new table, which will be left on stack when done.
    lua_newtable(toL);

    // fetch the transfer tables.
    lua_getfield(toL, LUA_REGISTRYINDEX, STATE_TRANSFER_DESTINATION_TABLES);
    lua_getfield(fromL, LUA_REGISTRYINDEX, STATE_TRANSFER_SOURCE_TABLES);

    // these transfer tables are required to be present, but check for safety sake anyway.
    if (lua_istable(fromL, -1) && lua_istable(toL, -1))
    {
        // fetch the lastindex where a table is stored, 0/nil means no tables stored.
        lua_getfield(fromL, -1, STATE_TRANSFER_SOURCE_TABLES_LASTINDEX);
        lua_Integer lastIndex = lua_tointeger(fromL, -1);
        lua_pop(fromL, 1);
        ++lastIndex;

        // duplicate the tables to be transferred and set them in the respective transfer table.
        lua_pushvalue(fromL, -2);
        lua_rawseti(fromL, -2, lastIndex);

        lua_pushvalue(toL, -2);
        lua_rawseti(toL, -2, lastIndex);

        lua_pushinteger(fromL, lastIndex);
        lua_setfield(fromL, -2, STATE_TRANSFER_SOURCE_TABLES_LASTINDEX);

        result = true;
    }

    // remove transfer tables from stack, leaving the orignals that were transferred.
    lua_pop(fromL, 1);
    lua_pop(toL, 1);

    return result;
}

bool transferValueInternal(lua_State* toL, lua_State* fromL)
{
    bool result = false;
    int type = lua_type(fromL, -1);

    switch (type)
    {
    case LUA_TNIL:
        lua_pushnil(toL);
        result = true;
        break;
    case LUA_TNUMBER:
    {
#if LUA_VERSION_NUM > 502
        if (lua_isinteger(fromL, -1)) { lua_pushinteger(toL, lua_tointeger(fromL, -1)); }
        else
#endif
        { lua_pushnumber(toL, lua_tonumber(fromL, -1)); }
        result = true;
        break;
    }
    case LUA_TBOOLEAN:
        lua_pushboolean(toL, lua_toboolean(fromL, -1));
        result = true;
        break;
    case LUA_TSTRING:
    {
        size_t len;
        const char* str = lua_tolstring(fromL, -1, &len);
        lua_pushlstring(toL, str, len);
        result = true;
        break;
    }
    case LUA_TTABLE:
    {
        result = transferTable(toL, fromL);
        break;
    }
    case LUA_TFUNCTION:
        result = transferFunction(toL, fromL);
        break;
    case LUA_TUSERDATA:
    {
        Userdata* ud = getPrivateUserdata(fromL, -1);
        if (ud)
            result = ud->copyToState(toL);
        break;
    }
    case LUA_TTHREAD:
        // copying coroutines is not supported.
        break;
    case LUA_TLIGHTUSERDATA:
        lua_pushlightuserdata(toL, lua_touserdata(fromL, -1));
        result = true;
        break;
    default:
        break;
    }

    return result;
}

// destination table, and source table should be at -1 index.
bool transferTableValues(lua_State* toL, lua_State* fromL)
{
    bool result = true;

    lua_pushnil(fromL);

    // iterate key/value pairs
    // transfer them to the target state
    // set them in the destination table.
    while (lua_next(fromL, -2))
    {
        // duplicate key so it can be transferred first.
        lua_pushvalue(fromL, -2);

        // transfer key
        if (transferValueInternal(toL, fromL))
        {
            // pop extra key
            lua_pop(fromL, 1);
            // transfer value
            if (transferValueInternal(toL, fromL))
            {
                // pop value
                lua_pop(fromL, 1);
                // set the transfered value, key, leaving just the table.
                lua_rawset(toL, -3);
            }
            else
            {
                result = false;
                break;
            }
        }
        else
        {
            result = false;
            break;
        }
    }

    return result;
}

bool populateTables(lua_State* toL, lua_State* fromL)
{
    bool result = true;
    int fromTop = lua_gettop(fromL);
    int toTop = lua_gettop(toL);

    lua_getfield(fromL, LUA_REGISTRYINDEX, STATE_TRANSFER_SOURCE_TABLES);
    lua_getfield(toL, LUA_REGISTRYINDEX, STATE_TRANSFER_DESTINATION_TABLES);

    if (lua_istable(fromL, -1) && lua_istable(toL, -1))
    {
        while (true)
        {
            // lastIndex = registry.source_tables.lastindex
            lua_getfield(fromL, -1, STATE_TRANSFER_SOURCE_TABLES_LASTINDEX);
            lua_Integer lastIndex = lua_tointeger(fromL, -1);
            lua_pop(fromL, 1);

            // check if there is work to do, if not return.
            if (lastIndex == 0) { break; }

            // fetch the last added source/destination table pair.
            lua_rawgeti(fromL, -1, lastIndex);
            lua_rawgeti(toL, -1, lastIndex);

            // remove these tables from the in-progress transfer table list.
            // this frees up this slot and prevents "holes" in the table for any upcoming
            // nested tables.
            lua_pushnil(fromL);
            lua_rawseti(fromL, -3, lastIndex);
            lua_pushnil(toL);
            lua_rawseti(toL, -3, lastIndex);

            // reset the last index value
            lua_pushinteger(fromL, --lastIndex);
            lua_setfield(fromL, -3, STATE_TRANSFER_SOURCE_TABLES_LASTINDEX);

            if (!transferTableValues(toL, fromL))
            {
                result = false;
                break;
            }

            // source key/value pairs have been copied to destination table.
            // pop source/destination from stack.
            lua_pop(fromL, 1);
            lua_pop(toL, 1);
        }
    }

    lua_settop(fromL, fromTop);
    lua_settop(toL, toTop);

    return result;
}

bool transferValue(lua_State* toL, lua_State* fromL)
{
    bool result = false;
    bool isTableTransfer = lua_istable(fromL, -1);

    // prepare transfer tables only if we're operating on a table
    if (isTableTransfer)
    {
        lua_newtable(fromL);
        lua_setfield(fromL, LUA_REGISTRYINDEX, STATE_TRANSFER_SOURCE_TABLES);
        lua_newtable(toL);
        lua_setfield(toL, LUA_REGISTRYINDEX, STATE_TRANSFER_DESTINATION_TABLES);
    }

    result = transferValueInternal(toL, fromL);

    // remove transfer tables in registry
    if (isTableTransfer)
    {
        // do not attempt populating the tables if table creation failed.
        if (result) { result = populateTables(toL, fromL); }

        lua_pushnil(fromL);
        lua_setfield(fromL, LUA_REGISTRYINDEX, STATE_TRANSFER_SOURCE_TABLES);
        lua_pushnil(toL);
        lua_setfield(toL, LUA_REGISTRYINDEX, STATE_TRANSFER_DESTINATION_TABLES);
    }

    return result;
}

} // LuaPoco
<|endoftext|>