static gpointer
delayed_control (gpointer data)
{
  GstPoll *set = data;
  GstPollFD fd = GST_POLL_FD_INIT;

  fd.fd = 1;

  THREAD_START ();

  g_usleep (500000);

  gst_poll_add_fd (set, &fd);
  gst_poll_fd_ctl_write (set, &fd, TRUE);
  gst_poll_restart (set);

  THREAD_SYNCHRONIZE ();

  g_usleep (500000);

  gst_poll_add_fd (set, &fd);
  gst_poll_fd_ctl_write (set, &fd, TRUE);
  gst_poll_restart (set);

  return NULL;
}
Beispiel #2
0
static gboolean
gst_fd_sink_start (GstBaseSink * basesink)
{
  GstFdSink *fdsink;
  GstPollFD fd = GST_POLL_FD_INIT;

  fdsink = GST_FD_SINK (basesink);
  if (!gst_fd_sink_check_fd (fdsink, fdsink->fd, NULL))
    return FALSE;

  if ((fdsink->fdset = gst_poll_new (TRUE)) == NULL)
    goto socket_pair;

  fd.fd = fdsink->fd;
  gst_poll_add_fd (fdsink->fdset, &fd);
  gst_poll_fd_ctl_write (fdsink->fdset, &fd, TRUE);

  fdsink->bytes_written = 0;
  fdsink->current_pos = 0;

  fdsink->seekable = gst_fd_sink_do_seek (fdsink, 0);
  GST_INFO_OBJECT (fdsink, "seeking supported: %d", fdsink->seekable);

  return TRUE;

  /* ERRORS */
socket_pair:
  {
    GST_ELEMENT_ERROR (fdsink, RESOURCE, OPEN_READ_WRITE, (NULL),
        GST_ERROR_SYSTEM);
    return FALSE;
  }
}
static FsMsnPollFD *
add_pollfd_locked (FsMsnConnection *self, int fd, PollFdCallback callback,
    gboolean read, gboolean write, gboolean server)
{
  FsMsnPollFD *pollfd = g_slice_new0 (FsMsnPollFD);
  gst_poll_fd_init (&pollfd->pollfd);
  pollfd->pollfd.fd = fd;
  pollfd->server = server;
  pollfd->want_read = read;
  pollfd->want_write = write;
  pollfd->status = FS_MSN_STATUS_AUTH;

  gst_poll_add_fd (self->poll, &pollfd->pollfd);

  gst_poll_fd_ctl_read (self->poll, &pollfd->pollfd, read);
  gst_poll_fd_ctl_write (self->poll, &pollfd->pollfd, write);
  pollfd->callback = callback;

  GST_DEBUG ("ADD_POLLFD %p (%p) - error %d, close %d, read %d-%d, write %d-%d",
      self->pollfds, pollfd,
      gst_poll_fd_has_error (self->poll, &pollfd->pollfd),
      gst_poll_fd_has_closed (self->poll, &pollfd->pollfd),
      pollfd->want_read,
      gst_poll_fd_can_read (self->poll, &pollfd->pollfd),
      pollfd->want_write,
      gst_poll_fd_can_write (self->poll, &pollfd->pollfd));

  g_ptr_array_add (self->pollfds, pollfd);
  gst_poll_restart (self->poll);
  return pollfd;
}
Beispiel #4
0
static void
put_chunk (GstPluginLoader * l, GstRegistryChunk * chunk, guint * pos)
{
  guint padsize = 0;
  guint len;
  guint8 *out;

  /* Might need to align the chunk */
  if (chunk->align && ((*pos) % ALIGNMENT) != 0)
    padsize = ALIGNMENT - ((*pos) % ALIGNMENT);

  len = padsize + chunk->size;

  if (G_UNLIKELY (l->tx_buf_write + len >= l->tx_buf_size)) {
    guint new_size = MAX (l->tx_buf_write + len,
        l->tx_buf_size + l->tx_buf_size / 4) + BUF_GROW_EXTRA;
    GST_LOG ("Expanding tx buf from %d to %d for chunk of size %d",
        l->tx_buf_size, new_size, chunk->size);
    l->tx_buf_size = new_size;
    l->tx_buf = g_realloc (l->tx_buf, l->tx_buf_size);
  }

  out = l->tx_buf + l->tx_buf_write;
  /* Clear the padding */
  if (padsize)
    memset (out, 0, padsize);
  memcpy (out + padsize, chunk->data, chunk->size);

  l->tx_buf_write += len;
  *pos += len;

  gst_poll_fd_ctl_write (l->fdset, &l->fd_w, TRUE);
};
Beispiel #5
0
static void
put_packet (GstPluginLoader * l, guint type, guint32 tag,
    const guint8 * payload, guint32 payload_len)
{
  guint8 *out;
  guint len = payload_len + HEADER_SIZE;

  if (l->tx_buf_write + len >= l->tx_buf_size) {
    GST_LOG ("Expanding tx buf from %d to %d for packet of size %d",
        l->tx_buf_size, l->tx_buf_write + len + BUF_GROW_EXTRA, len);
    l->tx_buf_size = l->tx_buf_write + len + BUF_GROW_EXTRA;
    l->tx_buf = g_realloc (l->tx_buf, l->tx_buf_size);
  }

  out = l->tx_buf + l->tx_buf_write;

  /* one byte packet type */
  out[0] = type;
  /* 3 byte packet tag number */
  GST_WRITE_UINT24_BE (out + 1, tag);
  /* 4 bytes packet length */
  GST_WRITE_UINT32_BE (out + 4, payload_len);
  /* payload */
  memcpy (out + HEADER_SIZE, payload, payload_len);
  /* Write magic into the header */
  GST_WRITE_UINT32_BE (out + 8, HEADER_MAGIC);

  l->tx_buf_write += len;
  gst_poll_fd_ctl_write (l->fdset, &l->fd_w, TRUE);
}
Beispiel #6
0
static gboolean
gst_fd_sink_update_fd (GstFdSink * fdsink, int new_fd)
{
  if (new_fd < 0)
    return FALSE;

  if (!gst_fd_sink_check_fd (fdsink, new_fd))
    goto invalid;

  /* assign the fd */
  GST_OBJECT_LOCK (fdsink);
  if (fdsink->fdset) {
    GstPollFD fd = GST_POLL_FD_INIT;

    fd.fd = fdsink->fd;
    gst_poll_remove_fd (fdsink->fdset, &fd);

    fd.fd = new_fd;
    gst_poll_add_fd (fdsink->fdset, &fd);
    gst_poll_fd_ctl_write (fdsink->fdset, &fd, TRUE);
  }
  fdsink->fd = new_fd;
  g_free (fdsink->uri);
  fdsink->uri = g_strdup_printf ("fd://%d", fdsink->fd);

  GST_OBJECT_UNLOCK (fdsink);

  return TRUE;

invalid:
  {
    return FALSE;
  }
}
Beispiel #7
0
static gboolean
write_one (GstPluginLoader * l)
{
  guint8 *out;
  guint32 to_write, magic;
  int res;

  if (l->tx_buf_read + HEADER_SIZE > l->tx_buf_write)
    return FALSE;

  out = l->tx_buf + l->tx_buf_read;

  magic = GST_READ_UINT32_BE (out + 8);
  if (magic != HEADER_MAGIC) {
    GST_ERROR ("Packet magic number is missing. Memory corruption detected");
    goto fail_and_cleanup;
  }

  to_write = GST_READ_UINT32_BE (out + 4) + HEADER_SIZE;
  /* Check that the magic is intact, and the size is sensible */
  if (to_write > l->tx_buf_size) {
    GST_ERROR ("Indicated packet size is too large. Corruption detected");
    goto fail_and_cleanup;
  }

  l->tx_buf_read += to_write;

  GST_LOG ("Writing packet of size %d bytes to fd %d", to_write, l->fd_w.fd);

  do {
    res = write (l->fd_w.fd, out, to_write);
    if (G_UNLIKELY (res < 0)) {
      if (errno == EAGAIN || errno == EINTR)
        continue;
      /* Failed to write -> child died */
      goto fail_and_cleanup;
    }
    to_write -= res;
    out += res;
  } while (to_write > 0);

  if (l->tx_buf_read == l->tx_buf_write) {
    gst_poll_fd_ctl_write (l->fdset, &l->fd_w, FALSE);
    l->tx_buf_read = l->tx_buf_write = 0;
  }

  return TRUE;

fail_and_cleanup:
  plugin_loader_cleanup_child (l);
  return FALSE;
}
/* This function gets called by libcurl after the socket() call but before
 * the connect() call. */
static int
gst_curl_base_sink_transfer_socket_cb (void *clientp, curl_socket_t curlfd,
    curlsocktype socket_type)
{
  GstCurlBaseSink *sink;
  gboolean ret = TRUE;

  sink = (GstCurlBaseSink *) clientp;

  g_assert (sink);

  if (curlfd < 0) {
    /* signal an unrecoverable error to the library which will close the socket
       and return CURLE_COULDNT_CONNECT
     */
    GST_DEBUG_OBJECT (sink, "no curlfd");
    return 1;
  }

  GST_OBJECT_LOCK (sink);
  sink->socket_type = socket_type;

  if (sink->fd.fd != curlfd) {
    if (sink->fd.fd > 0 && sink->socket_type != CURLSOCKTYPE_ACCEPT) {
      ret &= gst_poll_remove_fd (sink->fdset, &sink->fd);
    }
    sink->fd.fd = curlfd;
    ret &= gst_poll_add_fd (sink->fdset, &sink->fd);
    ret &= gst_poll_fd_ctl_write (sink->fdset, &sink->fd, TRUE);
    ret &= gst_poll_fd_ctl_read (sink->fdset, &sink->fd, TRUE);
  }
  GST_DEBUG_OBJECT (sink, "fd: %d", sink->fd.fd);
  gst_curl_base_sink_setup_dscp_unlocked (sink);
  GST_OBJECT_UNLOCK (sink);

  /* success */
  return ret ? 0 : 1;
}
Beispiel #9
0
static gboolean
gst_fd_sink_update_fd (GstFdSink * fdsink, int new_fd, GError ** error)
{
  if (new_fd < 0) {
    g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_REFERENCE,
        "File descriptor %d is not valid", new_fd);
    return FALSE;
  }

  if (!gst_fd_sink_check_fd (fdsink, new_fd, error))
    goto invalid;

  /* assign the fd */
  GST_OBJECT_LOCK (fdsink);
  if (fdsink->fdset) {
    GstPollFD fd = GST_POLL_FD_INIT;

    fd.fd = fdsink->fd;
    gst_poll_remove_fd (fdsink->fdset, &fd);

    fd.fd = new_fd;
    gst_poll_add_fd (fdsink->fdset, &fd);
    gst_poll_fd_ctl_write (fdsink->fdset, &fd, TRUE);
  }
  fdsink->fd = new_fd;
  g_free (fdsink->uri);
  fdsink->uri = g_strdup_printf ("fd://%d", fdsink->fd);

  GST_OBJECT_UNLOCK (fdsink);

  return TRUE;

invalid:
  {
    return FALSE;
  }
}
/* This function gets called by libcurl after the socket() call but before
 * the connect() call. */
static int
gst_curl_base_sink_transfer_socket_cb (void *clientp, curl_socket_t curlfd,
    curlsocktype G_GNUC_UNUSED purpose)
{
  GstCurlBaseSink *sink;
  gboolean ret = TRUE;

  sink = (GstCurlBaseSink *) clientp;

  g_assert (sink);

  if (curlfd < 0) {
    /* signal an unrecoverable error to the library which will close the socket
       and return CURLE_COULDNT_CONNECT
     */
    return 1;
  }

  gst_poll_fd_init (&sink->fd);
  sink->fd.fd = curlfd;

  ret = ret && gst_poll_add_fd (sink->fdset, &sink->fd);
  ret = ret && gst_poll_fd_ctl_write (sink->fdset, &sink->fd, TRUE);
  ret = ret && gst_poll_fd_ctl_read (sink->fdset, &sink->fd, TRUE);
  GST_DEBUG ("fd: %d", sink->fd.fd);
  GST_OBJECT_LOCK (sink);
  gst_curl_base_sink_setup_dscp_unlocked (sink);
  GST_OBJECT_UNLOCK (sink);

  /* success */
  if (ret) {
    return 0;
  } else {
    return 1;
  }
}
static void
connection_cb (FsMsnConnection *self, FsMsnPollFD *pollfd)
{
  gboolean success = FALSE;

  GST_DEBUG ("handler called on fd:%d server: %d status:%d r:%d w:%d",
      pollfd->pollfd.fd,
      pollfd->server, pollfd->status,
      gst_poll_fd_can_read (self->poll, &pollfd->pollfd),
      gst_poll_fd_can_write (self->poll, &pollfd->pollfd));

  if (gst_poll_fd_has_error (self->poll, &pollfd->pollfd) ||
      gst_poll_fd_has_closed (self->poll, &pollfd->pollfd))
  {
    GST_WARNING ("connecton closed or error (error: %d closed: %d)",
        gst_poll_fd_has_error (self->poll, &pollfd->pollfd),
        gst_poll_fd_has_closed (self->poll, &pollfd->pollfd));
    goto error;
  }

  if (gst_poll_fd_can_read (self->poll, &pollfd->pollfd))
  {
    switch (pollfd->status)
    {
      case FS_MSN_STATUS_AUTH:
        if (pollfd->server)
        {
          gchar str[35] = {0};
          gchar check[35] = {0};

          if (recv (pollfd->pollfd.fd, str, 34, 0) == 34)
          {
            GST_DEBUG ("Got %s, checking if it's auth", str);
            FS_MSN_CONNECTION_LOCK(self);
            snprintf(check, 35, "recipientid=%s&sessionid=%d\r\n\r\n",
                self->local_recipient_id, self->session_id);
            FS_MSN_CONNECTION_UNLOCK(self);
            if (strncmp (str, check, 35) == 0)
            {
              GST_DEBUG ("Authentication successful");
              pollfd->status = FS_MSN_STATUS_CONNECTED;
              pollfd->want_write = TRUE;
              gst_poll_fd_ctl_write (self->poll, &pollfd->pollfd, TRUE);
            }
            else
            {
              GST_WARNING ("Authentication failed check=%s", check);
              goto error;
            }
          }
          else
          {
            gchar error_str[256];
            strerror_r (errno, error_str, 256);
            GST_WARNING ("auth: %s", error_str);
            goto error;
          }

        } else {
          GST_ERROR ("shouldn't receive data when client on AUTH state");
          goto error;
        }
        break;
      case FS_MSN_STATUS_CONNECTED:
        if (!pollfd->server)
        {
          gchar str[14] = {0};
          ssize_t size;

          size = recv (pollfd->pollfd.fd, str, 13, MSG_PEEK);
          if (size > 0)
          {
            GST_DEBUG ("Got %s, checking if it's connected", str);
            if (size == 13 && strcmp (str, "connected\r\n\r\n") == 0)
            {
              GST_DEBUG ("connection successful");
              recv (pollfd->pollfd.fd, str, 13, 0);
              pollfd->status = FS_MSN_STATUS_CONNECTED2;
              pollfd->want_write = TRUE;
              gst_poll_fd_ctl_write (self->poll, &pollfd->pollfd, TRUE);
            }
            else if (!self->producer)
            {
              GST_DEBUG ("connection successful");
              pollfd->status = FS_MSN_STATUS_SEND_RECEIVE;
              success = TRUE;
            }
            else
            {
              GST_WARNING ("connected failed");
              goto error;
            }
          }
          else
          {
            gchar error_str[256];
            strerror_r (errno, error_str, 256);
            GST_WARNING ("recv: %s", error_str);
            goto error;
          }
        } else {
          GST_ERROR ("shouldn't receive data when server on CONNECTED state");
          goto error;
        }
        break;
      case FS_MSN_STATUS_CONNECTED2:
        if (pollfd->server)
        {
          gchar str[14] = {0};
          ssize_t size;

          size = recv (pollfd->pollfd.fd, str, 13, MSG_PEEK);
          if (size > 0)
          {
            GST_DEBUG ("Got %s, checking if it's connected", str);
            if (size == 13 && strcmp (str, "connected\r\n\r\n") == 0)
            {
              GST_DEBUG ("connection successful");
              recv (pollfd->pollfd.fd, str, 13, 0);
              pollfd->status = FS_MSN_STATUS_SEND_RECEIVE;
              success = TRUE;
            }
            else if (!self->producer)
            {
              GST_DEBUG ("connection successful");
              pollfd->status = FS_MSN_STATUS_SEND_RECEIVE;
              success = TRUE;
            }
            else
            {
              GST_WARNING ("connected failed");
              goto error;
            }
          }
          else
          {
            gchar error_str[256];
            strerror_r (errno, error_str, 256);
            GST_WARNING ("recv: %s", error_str);
            goto error;
          }

        } else {
          GST_ERROR ("shouldn't receive data when client on CONNECTED2 state");
          goto error;
        }
        break;
      default:
        GST_ERROR ("Invalid status %d", pollfd->status);
        goto error;
        break;

    }
  }
  else if (gst_poll_fd_can_write (self->poll, &pollfd->pollfd))
  {
    pollfd->want_write = FALSE;
    gst_poll_fd_ctl_write (self->poll, &pollfd->pollfd, FALSE);
    switch (pollfd->status)
    {
      case FS_MSN_STATUS_AUTH:
        if (!pollfd->server)
        {
          gchar *str;
          FS_MSN_CONNECTION_LOCK(self);
          str = g_strdup_printf("recipientid=%s&sessionid=%d\r\n\r\n",
              self->remote_recipient_id, self->session_id);
          FS_MSN_CONNECTION_UNLOCK(self);
          if (send(pollfd->pollfd.fd, str, strlen (str), 0) != -1)
          {
            GST_DEBUG ("Sent %s", str);
            pollfd->status = FS_MSN_STATUS_CONNECTED;
            g_free (str);
          }
          else
          {
            gchar error_str[256];
            strerror_r (errno, error_str, 256);
            GST_WARNING ("auth send: %s", error_str);
            g_free (str);
            goto error;
          }

        }
        break;
      case FS_MSN_STATUS_CONNECTED:
        if (pollfd->server)
        {

          if (send(pollfd->pollfd.fd, "connected\r\n\r\n", 13, 0) != -1)
          {
            GST_DEBUG ("sent connected");
            if (self->producer)
            {
              pollfd->status = FS_MSN_STATUS_SEND_RECEIVE;
              success = TRUE;
            }
            else
            {
              pollfd->status = FS_MSN_STATUS_CONNECTED2;
            }
          }
          else
          {
            gchar error_str[256];
            strerror_r (errno, error_str, 256);
            GST_WARNING ("sending connected: %s", error_str);
            goto error;
          }
        } else {
          GST_DEBUG ("shouldn't receive data when server on CONNECTED state");
          goto error;
        }
        break;
      case FS_MSN_STATUS_CONNECTED2:
        if (!pollfd->server)
        {

          if (send(pollfd->pollfd.fd, "connected\r\n\r\n", 13, 0) != -1)
          {
            GST_DEBUG ("sent connected");
            pollfd->status = FS_MSN_STATUS_SEND_RECEIVE;
            success = TRUE;
          }
          else
          {
            gchar error_str[256];
            strerror_r (errno, error_str, 256);
            GST_WARNING ("sending connected: %s", error_str);
            goto error;
          }
        } else {
          GST_ERROR ("shouldn't receive data when client on CONNECTED2 state");
          goto error;
        }
        break;
      default:
        GST_ERROR ("Invalid status %d", pollfd->status);
        goto error;
        break;
    }
  }

  if (success) {
    // success! we need to shutdown/close all other channels
    shutdown_fd (self, pollfd, FALSE);

    g_signal_emit (self, signals[SIGNAL_CONNECTED], 0, pollfd->pollfd.fd);

    pollfd->want_read = FALSE;
    pollfd->want_write = FALSE;
    gst_poll_fd_ctl_read (self->poll, &pollfd->pollfd, FALSE);
    gst_poll_fd_ctl_write (self->poll, &pollfd->pollfd, FALSE);
  }

  return;
 error:
  /* Error */
  GST_WARNING ("Got error from fd %d, closing", pollfd->pollfd.fd);
  shutdown_fd (self, pollfd, TRUE);

  FS_MSN_CONNECTION_LOCK (self);
  success = (self->pollfds->len > 1);
  FS_MSN_CONNECTION_UNLOCK (self);

  if (!success)
    g_signal_emit (self, signals[SIGNAL_CONNECTION_FAILED], 0);

  return;
}
Beispiel #12
0
static void
mess_some_more (void)
{
  GList *walk;
  gint random;
  gint removed = 0;

  g_mutex_lock (fdlock);

  for (walk = fds; walk;) {
    GstPollFD *fd = (GstPollFD *) walk->data;

    walk = g_list_next (walk);

    random = (gint) (10.0 * rand () / (RAND_MAX + 1.0));
    switch (random) {
      case 0:
      {
        /*
           GstPollFD *newfd = g_new0 (GstPollFD, 1);

           gst_poll_add_fd (set, newfd);
           fds = g_list_prepend (fds, newfd);
         */
        break;
      }
      case 1:
        if ((gint) (10.0 * rand () / (RAND_MAX + 1.0)) < 2) {
          gst_poll_remove_fd (set, fd);
          fds = g_list_remove (fds, fd);
          g_free (fd);
          removed++;
        }
        break;

      case 2:
        gst_poll_fd_ctl_write (set, fd, TRUE);
        break;
      case 3:
        gst_poll_fd_ctl_write (set, fd, FALSE);
        break;
      case 4:
        gst_poll_fd_ctl_read (set, fd, TRUE);
        break;
      case 5:
        gst_poll_fd_ctl_read (set, fd, FALSE);
        break;

      case 6:
        gst_poll_fd_has_closed (set, fd);
        break;
      case 7:
        gst_poll_fd_has_error (set, fd);
        break;
      case 8:
        gst_poll_fd_can_read (set, fd);
        break;
      case 9:
        gst_poll_fd_can_write (set, fd);
        break;
      default:
        g_assert_not_reached ();
        break;
    }
  }
  if (g_list_length (fds) < 900) {
    random = removed + (gint) (2.0 * rand () / (RAND_MAX + 1.0));
    while (random) {
      GstPollFD *newfd = g_new0 (GstPollFD, 1);

      gst_poll_add_fd (set, newfd);
      fds = g_list_prepend (fds, newfd);
      random--;
    }
  }

  g_mutex_unlock (fdlock);
}
/**
 * gst_v4l2_buffer_pool_new:
 * @obj:  the v4l2 object owning the pool
 *
 * Construct a new buffer pool.
 *
 * Returns: the new pool, use gst_object_unref() to free resources
 */
GstBufferPool *
gst_v4l2_buffer_pool_new (GstV4l2Object * obj, GstCaps * caps)
{
  GstV4l2BufferPool *pool;
  GstStructure *config;
  gchar *name, *parent_name;
  gint fd;

  fd = v4l2_dup (obj->video_fd);
  if (fd < 0)
    goto dup_failed;

  /* setting a significant unique name */
  parent_name = gst_object_get_name (GST_OBJECT (obj->element));
  name = g_strconcat (parent_name, ":", "pool:",
      V4L2_TYPE_IS_OUTPUT (obj->type) ? "sink" : "src", NULL);
  g_free (parent_name);

  pool = (GstV4l2BufferPool *) g_object_new (GST_TYPE_V4L2_BUFFER_POOL,
      "name", name, NULL);
  g_free (name);

  gst_poll_fd_init (&pool->pollfd);
  pool->pollfd.fd = fd;
  gst_poll_add_fd (pool->poll, &pool->pollfd);
  if (V4L2_TYPE_IS_OUTPUT (obj->type))
    gst_poll_fd_ctl_write (pool->poll, &pool->pollfd, TRUE);
  else
    gst_poll_fd_ctl_read (pool->poll, &pool->pollfd, TRUE);

  pool->video_fd = fd;
  pool->obj = obj;
  pool->can_poll_device = TRUE;

  pool->vallocator =
      gst_v4l2_allocator_new (GST_OBJECT (pool), obj->video_fd, &obj->format);
  if (pool->vallocator == NULL)
    goto allocator_failed;

  gst_object_ref (obj->element);

  config = gst_buffer_pool_get_config (GST_BUFFER_POOL_CAST (pool));
  gst_buffer_pool_config_set_params (config, caps, obj->info.size, 0, 0);
  /* This will simply set a default config, but will not configure the pool
   * because min and max are not valid */
  gst_buffer_pool_set_config (GST_BUFFER_POOL_CAST (pool), config);

  return GST_BUFFER_POOL (pool);

  /* ERRORS */
dup_failed:
  {
    GST_ERROR ("failed to dup fd %d (%s)", errno, g_strerror (errno));
    return NULL;
  }
allocator_failed:
  {
    GST_ERROR_OBJECT (pool, "Failed to create V4L2 allocator");
    return NULL;
  }
}