tr_watchdir_backend *
tr_watchdir_kqueue_new (tr_watchdir_t handle)
{
  const char * const path = tr_watchdir_get_path (handle);
  struct kevent ke;
  tr_watchdir_kqueue * backend;

  backend = tr_new0 (tr_watchdir_kqueue, 1);
  backend->base.free_func = &tr_watchdir_kqueue_free;
  backend->kq = -1;
  backend->dirfd = -1;

  if ((backend->kq = kqueue ()) == -1)
    {
      log_error ("Failed to start kqueue");
      goto fail;
    }

  /* Open fd for watching */
  if ((backend->dirfd = open (path, O_RDONLY | O_EVTONLY)) == -1)
    {
      log_error ("Failed to passively watch directory \"%s\": %s", path,
                 tr_strerror (errno));
      goto fail;
    }

  /* Register kevent filter with kqueue descriptor */
  EV_SET (&ke, backend->dirfd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR,
          KQUEUE_WATCH_MASK, 0, NULL);
  if (kevent (backend->kq, &ke, 1, NULL, 0, NULL) == -1)
    {
      log_error ("Failed to set directory event filter with fd %d: %s", backend->kq,
                 tr_strerror (errno));
      goto fail;
    }

  /* Create libevent task for event descriptor */
  if ((backend->event = event_new (tr_watchdir_get_event_base (handle), backend->kq,
                                   EV_READ | EV_ET | EV_PERSIST,
                                   &tr_watchdir_kqueue_on_event, handle)) == NULL)
    {
      log_error ("Failed to create event: %s", tr_strerror (errno));
      goto fail;
    }

  if (event_add (backend->event, NULL) == -1)
    {
      log_error ("Failed to add event: %s", tr_strerror (errno));
      goto fail;
    }

  /* Trigger one event for the initial scan */
  event_active (backend->event, EV_READ, 0);

  return BACKEND_DOWNCAST (backend);

fail:
  tr_watchdir_kqueue_free (BACKEND_DOWNCAST (backend));
  return NULL;
}
Beispiel #2
0
static tr_watchdir_status onFileAdded(tr_watchdir_t dir, char const* name, void* context)
{
    tr_session* session = context;

    if (!tr_str_has_suffix(name, ".torrent"))
    {
        return TR_WATCHDIR_IGNORE;
    }

    char* filename = tr_buildPath(tr_watchdir_get_path(dir), name, NULL);
    tr_ctor* ctor = tr_ctorNew(session);
    int err = tr_ctorSetMetainfoFromFile(ctor, filename);

    if (err == 0)
    {
        tr_torrentNew(ctor, &err, NULL);

        if (err == TR_PARSE_ERR)
        {
            tr_logAddError("Error parsing .torrent file \"%s\"", name);
        }
        else
        {
            bool trash = false;
            bool const test = tr_ctorGetDeleteSource(ctor, &trash);

            tr_logAddInfo("Parsing .torrent file successful \"%s\"", name);

            if (test && trash)
            {
                tr_error* error = NULL;

                tr_logAddInfo("Deleting input .torrent file \"%s\"", name);

                if (!tr_sys_path_remove(filename, &error))
                {
                    tr_logAddError("Error deleting .torrent file: %s", error->message);
                    tr_error_free(error);
                }
            }
            else
            {
                char* new_filename = tr_strdup_printf("%s.added", filename);
                tr_sys_path_rename(filename, new_filename, NULL);
                tr_free(new_filename);
            }
        }
    }
    else
    {
        err = TR_PARSE_ERR;
    }

    tr_ctorFree(ctor);
    tr_free(filename);

    return err == TR_PARSE_ERR ? TR_WATCHDIR_RETRY : TR_WATCHDIR_ACCEPT;
}
Beispiel #3
0
tr_watchdir_backend *
tr_watchdir_win32_new (tr_watchdir_t handle)
{
  const char * const path = tr_watchdir_get_path (handle);
  wchar_t * wide_path;
  tr_watchdir_win32 * backend;

  backend = tr_new0 (tr_watchdir_win32, 1);
  backend->base.free_func = &tr_watchdir_win32_free;
  backend->fd = INVALID_HANDLE_VALUE;
  backend->notify_pipe[0] = backend->notify_pipe[1] = TR_BAD_SOCKET;

  if ((wide_path = tr_win32_utf8_to_native (path, -1)) == NULL)
    {
      log_error ("Failed to convert \"%s\" to native path", path);
      goto fail;
    }

  if ((backend->fd = CreateFileW (wide_path, FILE_LIST_DIRECTORY,
                                  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                                  NULL, OPEN_EXISTING,
                                  FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
                                  NULL)) == INVALID_HANDLE_VALUE)
    {
      log_error ("Failed to open directory \"%s\"", path);
      goto fail;
    }

  tr_free (wide_path);
  wide_path = NULL;

  backend->overlapped.Pointer = handle;

  if (!ReadDirectoryChangesW (backend->fd, backend->buffer, sizeof (backend->buffer), FALSE,
                              WIN32_WATCH_MASK, NULL, &backend->overlapped, NULL))
    {
      log_error ("Failed to read directory changes");
      goto fail;
    }

  if (evutil_socketpair (AF_INET, SOCK_STREAM, 0, backend->notify_pipe) == -1)
    {
      log_error ("Failed to create notify pipe: %s", tr_strerror (errno));
      goto fail;
    }

  if ((backend->event = bufferevent_socket_new (tr_watchdir_get_event_base (handle),
                                                backend->notify_pipe[0], 0)) == NULL)
    {
      log_error ("Failed to create event buffer: %s", tr_strerror (errno));
      goto fail;
    }

  bufferevent_setwatermark (backend->event, EV_READ, sizeof (FILE_NOTIFY_INFORMATION), 0);
  bufferevent_setcb (backend->event, &tr_watchdir_win32_on_event, NULL, NULL, handle);
  bufferevent_enable (backend->event, EV_READ);

  if ((backend->thread = (HANDLE) _beginthreadex (NULL, 0, &tr_watchdir_win32_thread,
                                                  handle, 0, NULL)) == NULL)
    {
      log_error ("Failed to create thread");
      goto fail;
    }

  /* Perform an initial scan on the directory */
  if (event_base_once (tr_watchdir_get_event_base (handle), -1, EV_TIMEOUT,
                       &tr_watchdir_win32_on_first_scan, handle, NULL) == -1)
    log_error ("Failed to perform initial scan: %s", tr_strerror (errno));

  return BACKEND_DOWNCAST (backend);

fail:
  tr_watchdir_win32_free (BACKEND_DOWNCAST (backend));
  tr_free (wide_path);
  return NULL;
}