static void
logFunc (int severity, const char * message)
{
    if (severity >= _EVENT_LOG_ERR)
        tr_logAddError ("%s", message);
    else
        tr_logAddDebug ("%s", message);
}
static void
libeventThreadFunc (void * veh)
{
    struct event_base * base;
    tr_event_handle * eh = veh;

#ifndef WIN32
    /* Don't exit when writing on a broken socket */
    signal (SIGPIPE, SIG_IGN);
#endif

    /* create the libevent bases */
    base = event_base_new ();

    /* set the struct's fields */
    eh->base = base;
    eh->session->event_base = base;
    eh->session->evdns_base = evdns_base_new (base, true);
    eh->session->events = eh;

    /* listen to the pipe's read fd */
    eh->pipeEvent = event_new (base, eh->fds[0], EV_READ | EV_PERSIST, readFromPipe, veh);
    event_add (eh->pipeEvent, NULL);
    event_set_log_callback (logFunc);

    /* loop until all the events are done */
    while (!eh->die)
        event_base_dispatch (base);

    /* shut down the thread */
    tr_lockFree (eh->lock);
    event_base_free (base);
    eh->session->events = NULL;
    tr_free (eh);
    tr_logAddDebug ("Closing libevent thread");
}
Example #3
0
static tr_socket_t tr_netBindTCPImpl(tr_address const* addr, tr_port port, bool suppressMsgs, int* errOut)
{
    TR_ASSERT(tr_address_is_valid(addr));

    static int const domains[NUM_TR_AF_INET_TYPES] = { AF_INET, AF_INET6 };
    struct sockaddr_storage sock;
    tr_socket_t fd;
    int addrlen;
    int optval;

    fd = socket(domains[addr->type], SOCK_STREAM, 0);

    if (fd == TR_BAD_SOCKET)
    {
        *errOut = sockerrno;
        return TR_BAD_SOCKET;
    }

    if (evutil_make_socket_nonblocking(fd) == -1)
    {
        *errOut = sockerrno;
        tr_netCloseSocket(fd);
        return TR_BAD_SOCKET;
    }

    optval = 1;
    setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void const*)&optval, sizeof(optval));
    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void const*)&optval, sizeof(optval));

#ifdef IPV6_V6ONLY

    if (addr->type == TR_AF_INET6)
    {
        if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (void const*)&optval, sizeof(optval)) == -1)
        {
            if (sockerrno != ENOPROTOOPT) /* if the kernel doesn't support it, ignore it */
            {
                *errOut = sockerrno;
                tr_netCloseSocket(fd);
                return TR_BAD_SOCKET;
            }
        }
    }

#endif

    addrlen = setup_sockaddr(addr, htons(port), &sock);

    if (bind(fd, (struct sockaddr*)&sock, addrlen) == -1)
    {
        int const err = sockerrno;

        if (!suppressMsgs)
        {
            char const* fmt;
            char const* hint;
            char err_buf[512];

            if (err == EADDRINUSE)
            {
                hint = _("Is another copy of Transmission already running?");
            }
            else
            {
                hint = NULL;
            }

            if (hint == NULL)
            {
                fmt = _("Couldn't bind port %d on %s: %s");
            }
            else
            {
                fmt = _("Couldn't bind port %d on %s: %s (%s)");
            }

            tr_logAddError(fmt, port, tr_address_to_string(addr), tr_net_strerror(err_buf, sizeof(err_buf), err), hint);
        }

        tr_netCloseSocket(fd);
        *errOut = err;
        return TR_BAD_SOCKET;
    }

    if (!suppressMsgs)
    {
        tr_logAddDebug("Bound socket %" PRIdMAX " to port %d on %s", (intmax_t)fd, port, tr_address_to_string(addr));
    }

#ifdef TCP_FASTOPEN

#ifndef SOL_TCP
#define SOL_TCP IPPROTO_TCP
#endif

    optval = 5;
    setsockopt(fd, SOL_TCP, TCP_FASTOPEN, (void const*)&optval, sizeof(optval));

#endif

    if (listen(fd, 128) == -1)
    {
        *errOut = sockerrno;
        tr_netCloseSocket(fd);
        return TR_BAD_SOCKET;
    }

    return fd;
}
static int
pgpipe (int handles[2])
{
    SOCKET s;
    struct sockaddr_in serv_addr;
    int len = sizeof (serv_addr);

    handles[0] = handles[1] = INVALID_SOCKET;

    if ((s = socket (AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
    {
        tr_logAddDebug ("pgpipe failed to create socket: %ui", WSAGetLastError ());
        return -1;
    }

    memset (&serv_addr, 0, sizeof (serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons (0);
    serv_addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
    if (bind (s, (SOCKADDR *) & serv_addr, len) == SOCKET_ERROR)
    {
        tr_logAddDebug ("pgpipe failed to bind: %ui", WSAGetLastError ());
        closesocket (s);
        return -1;
    }
    if (listen (s, 1) == SOCKET_ERROR)
    {
        tr_logAddNamedDbg ("event","pgpipe failed to listen: %ui", WSAGetLastError ());
        closesocket (s);
        return -1;
    }
    if (getsockname (s, (SOCKADDR *) & serv_addr, &len) == SOCKET_ERROR)
    {
        tr_logAddDebug ("pgpipe failed to getsockname: %ui", WSAGetLastError ());
        closesocket (s);
        return -1;
    }
    if ((handles[1] = socket (PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
    {
        tr_logAddDebug ("pgpipe failed to create socket 2: %ui", WSAGetLastError ());
        closesocket (s);
        return -1;
    }

    if (connect (handles[1], (SOCKADDR *) & serv_addr, len) == SOCKET_ERROR)
    {
        tr_logAddDebug ("pgpipe failed to connect socket: %ui", WSAGetLastError ());
        closesocket (s);
        return -1;
    }
    if ((handles[0] = accept (s, (SOCKADDR *) & serv_addr, &len)) == INVALID_SOCKET)
    {
        tr_logAddDebug ("pgpipe failed to accept socket: %ui", WSAGetLastError ());
        closesocket (handles[1]);
        handles[1] = INVALID_SOCKET;
        closesocket (s);
        return -1;
    }
    closesocket (s);
    return 0;
}
Example #5
0
/**
 * returns 0 on success, or an errno value on failure.
 * errno values include ENOENT if the parent folder doesn't exist,
 * plus the errno values set by tr_mkdirp () and open ().
 */
static int
cached_file_open (struct tr_cached_file  * o,
                  const char             * filename,
                  bool                     writable,
                  tr_preallocation_mode    allocation,
                  uint64_t                 file_size)
{
  int flags;
  struct stat sb;
  bool already_existed;
  bool resize_needed;

  /* create subfolders, if any */
  if (writable)
    {
      char * dir = tr_dirname (filename);
      const int err = tr_mkdirp (dir, 0777) ? errno : 0;
      if (err)
        {
          tr_logAddError (_("Couldn't create \"%1$s\": %2$s"), dir, tr_strerror (err));
          tr_free (dir);
          return err;
        }
      tr_free (dir);
    }

  already_existed = !stat (filename, &sb) && S_ISREG (sb.st_mode);

  if (writable && !already_existed && (allocation == TR_PREALLOCATE_FULL))
    if (preallocate_file_full (filename, file_size))
      tr_logAddDebug ("Preallocated file \"%s\"", filename);

  /* we can't resize the file w/o write permissions */
  resize_needed = already_existed && (file_size < (uint64_t)sb.st_size);
  writable |= resize_needed;

  /* open the file */
  flags = writable ? (O_RDWR | O_CREAT) : O_RDONLY;
  flags |= O_LARGEFILE | O_BINARY | O_SEQUENTIAL;
  o->fd = open (filename, flags, 0666);

  if (o->fd == -1)
    {
      const int err = errno;
      tr_logAddError (_("Couldn't open \"%1$s\": %2$s"), filename, tr_strerror (err));
      return err;
    }

  /* If the file already exists and it's too large, truncate it.
   * This is a fringe case that happens if a torrent's been updated
   * and one of the updated torrent's files is smaller.
   * http://trac.transmissionbt.com/ticket/2228
   * https://bugs.launchpad.net/ubuntu/+source/transmission/+bug/318249
   */
  if (resize_needed && (ftruncate (o->fd, file_size) == -1))
    {
      const int err = errno;
      tr_logAddError (_("Couldn't truncate \"%1$s\": %2$s"), filename, tr_strerror (err));
      return err;
    }

  if (writable && !already_existed && (allocation == TR_PREALLOCATE_SPARSE))
    preallocate_file_sparse (o->fd, file_size);

  /* Many (most?) clients request blocks in ascending order,
   * so increase the readahead buffer.
   * Also, disable OS-level caching because "inactive memory" angers users. */
  tr_set_file_for_single_pass (o->fd);

  return 0;
}
Example #6
0
/**
 * returns 0 on success, or an errno value on failure.
 * errno values include ENOENT if the parent folder doesn't exist,
 * plus the errno values set by tr_sys_dir_create () and tr_sys_file_open ().
 */
static int
cached_file_open (struct tr_cached_file  * o,
                  const char             * filename,
                  bool                     writable,
                  tr_preallocation_mode    allocation,
                  uint64_t                 file_size)
{
  int flags;
  tr_sys_path_info info;
  bool already_existed;
  bool resize_needed;
  tr_sys_file_t fd = TR_BAD_SYS_FILE;
  tr_error * error = NULL;

  /* create subfolders, if any */
  if (writable)
    {
      char * dir = tr_sys_path_dirname (filename, NULL);
      if (!tr_sys_dir_create (dir, TR_SYS_DIR_CREATE_PARENTS, 0777, &error))
        {
          tr_logAddError (_("Couldn't create \"%1$s\": %2$s"), dir, error->message);
          tr_free (dir);
          goto fail;
        }
      tr_free (dir);
    }

  already_existed = tr_sys_path_get_info (filename, 0, &info, NULL) && info.type == TR_SYS_PATH_IS_FILE;

  /* we can't resize the file w/o write permissions */
  resize_needed = already_existed && (file_size < info.size);
  writable |= resize_needed;

  /* open the file */
  flags = writable ? (TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE) : 0;
  flags |= TR_SYS_FILE_READ | TR_SYS_FILE_SEQUENTIAL;
  fd = tr_sys_file_open (filename, flags, 0666, &error);

  if (fd == TR_BAD_SYS_FILE)
    {
      tr_logAddError (_("Couldn't open \"%1$s\": %2$s"), filename, error->message);
      goto fail;
    }

  if (writable && !already_existed && allocation != TR_PREALLOCATE_NONE)
    {
      bool success = false;
      const char * type = NULL;

      if (allocation == TR_PREALLOCATE_FULL)
        {
          success = preallocate_file_full (fd, file_size, &error);
          type = _("full");
        }
      else if (allocation == TR_PREALLOCATE_SPARSE)
        {
          success = preallocate_file_sparse (fd, file_size, &error);
          type = _("sparse");
        }

      assert (type != NULL);

      if (!success)
        {
          tr_logAddError (_("Couldn't preallocate file \"%1$s\" (%2$s, size: %3$"PRIu64"): %4$s"),
            filename, type, file_size, error->message);
          goto fail;
        }

      tr_logAddDebug (_("Preallocated file \"%1$s\" (%2$s, size: %3$"PRIu64")"), filename, type, file_size);
    }

  /* If the file already exists and it's too large, truncate it.
   * This is a fringe case that happens if a torrent's been updated
   * and one of the updated torrent's files is smaller.
   * http://trac.transmissionbt.com/ticket/2228
   * https://bugs.launchpad.net/ubuntu/+source/transmission/+bug/318249
   */
  if (resize_needed && !tr_sys_file_truncate (fd, file_size, &error))
    {
      tr_logAddError (_("Couldn't truncate \"%1$s\": %2$s"), filename, error->message);
      goto fail;
    }

  o->fd = fd;
  return 0;

fail:
  {
    const int err = error->code;
    tr_error_free (error);

    if (fd != TR_BAD_SYS_FILE)
      tr_sys_file_close (fd, NULL);

    return err;
  }
}