2007-08-18 03:02:32 +00:00
|
|
|
/*
|
2014-01-19 01:09:44 +00:00
|
|
|
* This file Copyright (C) 2007-2014 Mnemosyne LLC
|
2007-08-18 03:02:32 +00:00
|
|
|
*
|
2014-01-21 03:10:30 +00:00
|
|
|
* It may be used under the GNU GPL versions 2 or 3
|
2014-01-19 01:09:44 +00:00
|
|
|
* or any future license endorsed by Mnemosyne LLC.
|
2007-08-18 17:19:49 +00:00
|
|
|
*
|
2007-08-18 03:02:32 +00:00
|
|
|
*/
|
|
|
|
|
2021-10-17 20:17:18 +00:00
|
|
|
#include <cerrno>
|
|
|
|
#include <cstring>
|
2021-11-20 21:20:45 +00:00
|
|
|
#include <mutex>
|
2007-08-18 03:02:32 +00:00
|
|
|
|
2021-10-17 20:17:18 +00:00
|
|
|
#include <csignal>
|
2007-08-18 03:02:32 +00:00
|
|
|
|
2014-12-13 15:22:39 +00:00
|
|
|
#ifdef _WIN32
|
2017-04-19 12:04:45 +00:00
|
|
|
#include <winsock2.h>
|
2014-12-13 15:22:39 +00:00
|
|
|
#else
|
2017-04-21 07:40:57 +00:00
|
|
|
#include <unistd.h> /* read(), write(), pipe() */
|
2014-12-13 15:22:39 +00:00
|
|
|
#endif
|
|
|
|
|
2011-03-13 00:18:11 +00:00
|
|
|
#include <event2/dns.h>
|
2010-12-24 08:58:41 +00:00
|
|
|
#include <event2/event.h>
|
2010-01-22 02:40:11 +00:00
|
|
|
|
2008-12-23 17:27:15 +00:00
|
|
|
#include "transmission.h"
|
2013-01-25 23:34:20 +00:00
|
|
|
#include "log.h"
|
2009-10-27 20:27:27 +00:00
|
|
|
#include "net.h"
|
2008-12-23 17:27:15 +00:00
|
|
|
#include "session.h"
|
|
|
|
|
2014-12-13 15:22:39 +00:00
|
|
|
#include "transmission.h"
|
2021-11-20 21:20:45 +00:00
|
|
|
#include "platform.h"
|
2017-06-08 07:24:12 +00:00
|
|
|
#include "tr-assert.h"
|
2014-12-13 15:22:39 +00:00
|
|
|
#include "trevent.h"
|
2010-06-22 00:12:52 +00:00
|
|
|
#include "utils.h"
|
2014-12-13 15:22:39 +00:00
|
|
|
|
|
|
|
#ifdef _WIN32
|
2009-08-10 20:04:08 +00:00
|
|
|
|
2021-10-06 14:26:07 +00:00
|
|
|
using tr_pipe_end_t = SOCKET;
|
2015-03-18 07:34:26 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static int pgpipe(tr_pipe_end_t handles[2])
|
2008-10-14 18:17:33 +00:00
|
|
|
{
|
2010-06-22 00:12:52 +00:00
|
|
|
SOCKET s;
|
|
|
|
struct sockaddr_in serv_addr;
|
2017-04-19 12:04:45 +00:00
|
|
|
int len = sizeof(serv_addr);
|
2010-06-22 00:12:52 +00:00
|
|
|
|
|
|
|
handles[0] = handles[1] = INVALID_SOCKET;
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
|
2010-06-22 00:12:52 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_logAddDebug("pgpipe failed to create socket: %ui", WSAGetLastError());
|
2010-06-22 00:12:52 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
memset(&serv_addr, 0, sizeof(serv_addr));
|
2010-06-22 00:12:52 +00:00
|
|
|
serv_addr.sin_family = AF_INET;
|
2017-04-19 12:04:45 +00:00
|
|
|
serv_addr.sin_port = htons(0);
|
|
|
|
serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
|
|
|
|
|
|
if (bind(s, (SOCKADDR*)&serv_addr, len) == SOCKET_ERROR)
|
2010-06-22 00:12:52 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_logAddDebug("pgpipe failed to bind: %ui", WSAGetLastError());
|
|
|
|
closesocket(s);
|
2010-06-22 00:12:52 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
if (listen(s, 1) == SOCKET_ERROR)
|
2010-06-22 00:12:52 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_logAddNamedDbg("event", "pgpipe failed to listen: %ui", WSAGetLastError());
|
|
|
|
closesocket(s);
|
2010-06-22 00:12:52 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
if (getsockname(s, (SOCKADDR*)&serv_addr, &len) == SOCKET_ERROR)
|
2010-06-22 00:12:52 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_logAddDebug("pgpipe failed to getsockname: %ui", WSAGetLastError());
|
|
|
|
closesocket(s);
|
2010-06-22 00:12:52 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
if ((handles[1] = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
|
2010-06-22 00:12:52 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_logAddDebug("pgpipe failed to create socket 2: %ui", WSAGetLastError());
|
|
|
|
closesocket(s);
|
2010-06-22 00:12:52 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2009-08-10 20:04:08 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (connect(handles[1], (SOCKADDR*)&serv_addr, len) == SOCKET_ERROR)
|
2010-06-22 00:12:52 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_logAddDebug("pgpipe failed to connect socket: %ui", WSAGetLastError());
|
|
|
|
closesocket(s);
|
2010-06-22 00:12:52 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
if ((handles[0] = accept(s, (SOCKADDR*)&serv_addr, &len)) == INVALID_SOCKET)
|
2010-06-22 00:12:52 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_logAddDebug("pgpipe failed to accept socket: %ui", WSAGetLastError());
|
|
|
|
closesocket(handles[1]);
|
2010-06-22 00:12:52 +00:00
|
|
|
handles[1] = INVALID_SOCKET;
|
2017-04-19 12:04:45 +00:00
|
|
|
closesocket(s);
|
2010-06-22 00:12:52 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
closesocket(s);
|
2010-06-22 00:12:52 +00:00
|
|
|
return 0;
|
2008-10-14 18:17:33 +00:00
|
|
|
}
|
2009-08-10 20:04:08 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static int piperead(tr_pipe_end_t s, void* buf, int len)
|
2009-08-10 20:04:08 +00:00
|
|
|
{
|
2021-09-12 17:41:49 +00:00
|
|
|
int ret = recv(s, static_cast<char*>(buf), len, 0);
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2017-04-30 16:46:02 +00:00
|
|
|
if (ret == -1)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2017-04-20 16:02:19 +00:00
|
|
|
int const werror = WSAGetLastError();
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
switch (werror)
|
|
|
|
{
|
|
|
|
/* simplified error mapping (not valid for connect) */
|
|
|
|
case WSAEWOULDBLOCK:
|
|
|
|
errno = EAGAIN;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WSAECONNRESET:
|
|
|
|
/* EOF on the pipe! (win32 socket based implementation) */
|
|
|
|
ret = 0;
|
2021-10-06 17:24:02 +00:00
|
|
|
[[fallthrough]];
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
errno = werror;
|
|
|
|
break;
|
2010-06-22 00:12:52 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-06-22 00:12:52 +00:00
|
|
|
errno = 0;
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
|
|
|
|
2010-06-22 00:12:52 +00:00
|
|
|
return ret;
|
2009-08-10 20:04:08 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
#define pipe(a) pgpipe(a)
|
|
|
|
#define pipewrite(a, b, c) send(a, (char*)b, c, 0)
|
2008-10-14 18:17:33 +00:00
|
|
|
|
2008-05-06 15:52:57 +00:00
|
|
|
#else
|
2021-10-06 14:26:07 +00:00
|
|
|
using tr_pipe_end_t = int;
|
2017-04-19 12:04:45 +00:00
|
|
|
#define piperead(a, b, c) read(a, b, c)
|
|
|
|
#define pipewrite(a, b, c) write(a, b, c)
|
2008-05-06 15:52:57 +00:00
|
|
|
#endif
|
|
|
|
|
2007-08-18 03:02:32 +00:00
|
|
|
/***
|
|
|
|
****
|
|
|
|
***/
|
|
|
|
|
2021-10-06 14:26:07 +00:00
|
|
|
struct tr_event_handle
|
2007-08-18 03:02:32 +00:00
|
|
|
{
|
2021-11-20 21:20:45 +00:00
|
|
|
std::recursive_mutex fds_mutex;
|
|
|
|
tr_pipe_end_t fds[2] = {};
|
|
|
|
|
|
|
|
struct event* pipeEvent = nullptr;
|
|
|
|
struct event_base* base = nullptr;
|
|
|
|
tr_session* session = nullptr;
|
|
|
|
tr_thread* thread = nullptr;
|
|
|
|
|
|
|
|
bool die = false;
|
2021-10-06 14:26:07 +00:00
|
|
|
};
|
2007-08-18 03:02:32 +00:00
|
|
|
|
2008-05-06 15:52:57 +00:00
|
|
|
struct tr_run_data
|
2007-08-18 03:02:32 +00:00
|
|
|
{
|
2021-08-15 09:41:48 +00:00
|
|
|
void (*func)(void*);
|
2017-04-19 12:04:45 +00:00
|
|
|
void* user_data;
|
2007-09-20 16:32:01 +00:00
|
|
|
};
|
2007-08-18 03:02:32 +00:00
|
|
|
|
2017-05-22 20:12:57 +00:00
|
|
|
#define dbgmsg(...) tr_logAddDeepNamed("event", __VA_ARGS__)
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
static void readFromPipe(evutil_socket_t fd, short eventType, void* veh)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2021-09-12 17:41:49 +00:00
|
|
|
auto* eh = static_cast<tr_event_handle*>(veh);
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg("readFromPipe: eventType is %hd", eventType);
|
2007-08-18 03:02:32 +00:00
|
|
|
|
2008-05-06 15:52:57 +00:00
|
|
|
/* read the command type */
|
2021-09-12 17:41:49 +00:00
|
|
|
char ch = '\0';
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2021-10-23 15:43:15 +00:00
|
|
|
int ret = 0;
|
2008-09-23 19:11:04 +00:00
|
|
|
do
|
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
ret = piperead(fd, &ch, 1);
|
2021-08-15 09:41:48 +00:00
|
|
|
} while (!eh->die && ret == -1 && errno == EAGAIN);
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg("command is [%c], ret is %d, errno is %d", ch, ret, (int)errno);
|
2007-08-18 06:59:20 +00:00
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
switch (ch)
|
2008-05-06 15:52:57 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
case 'r': /* run in libevent thread */
|
2008-05-06 15:52:57 +00:00
|
|
|
{
|
|
|
|
struct tr_run_data data;
|
2017-04-20 16:02:19 +00:00
|
|
|
size_t const nwant = sizeof(data);
|
|
|
|
ev_ssize_t const ngot = piperead(fd, &data, nwant);
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2017-04-30 16:25:26 +00:00
|
|
|
if (!eh->die && ngot == (ev_ssize_t)nwant)
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg("invoking function in libevent thread");
|
2017-04-30 16:30:03 +00:00
|
|
|
(*data.func)(data.user_data);
|
2008-05-06 15:52:57 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2007-10-08 00:56:12 +00:00
|
|
|
break;
|
2008-05-06 15:52:57 +00:00
|
|
|
}
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case '\0': /* eof */
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg("pipe eof reached... removing event listener");
|
|
|
|
event_free(eh->pipeEvent);
|
|
|
|
tr_netCloseSocket(eh->fds[0]);
|
2021-09-15 00:18:09 +00:00
|
|
|
event_base_loopexit(eh->base, nullptr);
|
2008-05-06 15:52:57 +00:00
|
|
|
break;
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
default:
|
2008-05-06 15:52:57 +00:00
|
|
|
{
|
2017-06-08 07:24:12 +00:00
|
|
|
TR_ASSERT_MSG(false, "unhandled command type %d", (int)ch);
|
2008-05-06 15:52:57 +00:00
|
|
|
break;
|
2008-06-25 11:34:35 +00:00
|
|
|
}
|
2007-08-18 03:02:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-20 16:02:19 +00:00
|
|
|
static void logFunc(int severity, char const* message)
|
2007-08-19 03:54:27 +00:00
|
|
|
{
|
2012-12-05 17:29:46 +00:00
|
|
|
if (severity >= _EVENT_LOG_ERR)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
tr_logAddError("%s", message);
|
|
|
|
}
|
2008-03-18 02:23:39 +00:00
|
|
|
else
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
tr_logAddDebug("%s", message);
|
|
|
|
}
|
2007-08-19 03:54:27 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static void libeventThreadFunc(void* veh)
|
2007-08-18 03:02:32 +00:00
|
|
|
{
|
2021-09-12 17:41:49 +00:00
|
|
|
auto* eh = static_cast<tr_event_handle*>(veh);
|
2007-08-18 03:02:32 +00:00
|
|
|
|
2014-07-04 00:00:07 +00:00
|
|
|
#ifndef _WIN32
|
2007-09-20 16:32:01 +00:00
|
|
|
/* Don't exit when writing on a broken socket */
|
2017-04-19 12:04:45 +00:00
|
|
|
signal(SIGPIPE, SIG_IGN);
|
2007-09-20 16:32:01 +00:00
|
|
|
#endif
|
2007-08-18 03:02:32 +00:00
|
|
|
|
2011-03-13 00:18:11 +00:00
|
|
|
/* create the libevent bases */
|
2021-09-12 17:41:49 +00:00
|
|
|
struct event_base* base = event_base_new();
|
2011-03-13 00:18:11 +00:00
|
|
|
|
|
|
|
/* set the struct's fields */
|
2010-12-24 08:58:41 +00:00
|
|
|
eh->base = base;
|
|
|
|
eh->session->event_base = base;
|
2017-04-19 12:04:45 +00:00
|
|
|
eh->session->evdns_base = evdns_base_new(base, true);
|
2008-12-14 11:21:11 +00:00
|
|
|
eh->session->events = eh;
|
2008-05-06 15:52:57 +00:00
|
|
|
|
|
|
|
/* listen to the pipe's read fd */
|
2017-04-19 12:04:45 +00:00
|
|
|
eh->pipeEvent = event_new(base, eh->fds[0], EV_READ | EV_PERSIST, readFromPipe, veh);
|
2021-09-15 00:18:09 +00:00
|
|
|
event_add(eh->pipeEvent, nullptr);
|
2017-04-19 12:04:45 +00:00
|
|
|
event_set_log_callback(logFunc);
|
2009-01-26 02:51:50 +00:00
|
|
|
|
|
|
|
/* loop until all the events are done */
|
2012-12-05 17:29:46 +00:00
|
|
|
while (!eh->die)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
event_base_dispatch(base);
|
|
|
|
}
|
2007-08-18 03:02:32 +00:00
|
|
|
|
2009-01-26 02:51:50 +00:00
|
|
|
/* shut down the thread */
|
2017-04-19 12:04:45 +00:00
|
|
|
event_base_free(base);
|
2021-09-15 00:18:09 +00:00
|
|
|
eh->session->events = nullptr;
|
2021-11-24 16:39:09 +00:00
|
|
|
delete eh;
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_logAddDebug("Closing libevent thread");
|
2007-08-18 03:02:32 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
void tr_eventInit(tr_session* session)
|
2007-08-18 03:02:32 +00:00
|
|
|
{
|
2021-09-15 00:18:09 +00:00
|
|
|
session->events = nullptr;
|
2009-01-26 02:51:50 +00:00
|
|
|
|
2021-11-20 21:20:45 +00:00
|
|
|
auto* const eh = new tr_event_handle{};
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
if (pipe(eh->fds) == -1)
|
|
|
|
{
|
|
|
|
tr_logAddError("Unable to write to pipe() in libtransmission: %s", tr_strerror(errno));
|
|
|
|
}
|
|
|
|
|
2008-12-14 11:21:11 +00:00
|
|
|
eh->session = session;
|
2017-04-19 12:04:45 +00:00
|
|
|
eh->thread = tr_threadNew(libeventThreadFunc, eh);
|
2009-01-26 02:51:50 +00:00
|
|
|
|
|
|
|
/* wait until the libevent thread is running */
|
2021-09-15 00:18:09 +00:00
|
|
|
while (session->events == nullptr)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
tr_wait_msec(100);
|
|
|
|
}
|
2007-08-18 03:02:32 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
void tr_eventClose(tr_session* session)
|
2007-08-18 03:02:32 +00:00
|
|
|
{
|
2017-06-08 07:24:12 +00:00
|
|
|
TR_ASSERT(tr_isSession(session));
|
2009-01-29 16:56:43 +00:00
|
|
|
|
2021-09-15 00:18:09 +00:00
|
|
|
if (session->events == nullptr)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2015-05-09 08:37:55 +00:00
|
|
|
return;
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2015-05-09 08:37:55 +00:00
|
|
|
|
2011-03-22 15:19:54 +00:00
|
|
|
session->events->die = true;
|
2020-07-28 17:31:36 +00:00
|
|
|
if (tr_logGetDeepEnabled())
|
|
|
|
{
|
2021-09-15 00:18:09 +00:00
|
|
|
tr_logAddDeep(__FILE__, __LINE__, nullptr, "closing trevent pipe");
|
2020-07-28 17:31:36 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_netCloseSocket(session->events->fds[1]);
|
2007-08-18 03:02:32 +00:00
|
|
|
}
|
|
|
|
|
2007-09-20 16:32:01 +00:00
|
|
|
/**
|
|
|
|
***
|
|
|
|
**/
|
|
|
|
|
2017-04-20 16:02:19 +00:00
|
|
|
bool tr_amInEventThread(tr_session const* session)
|
2007-11-15 16:43:46 +00:00
|
|
|
{
|
2017-06-08 07:24:12 +00:00
|
|
|
TR_ASSERT(tr_isSession(session));
|
2021-09-15 00:18:09 +00:00
|
|
|
TR_ASSERT(session->events != nullptr);
|
2008-10-19 22:04:47 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
return tr_amInThread(session->events->thread);
|
2007-11-15 16:43:46 +00:00
|
|
|
}
|
|
|
|
|
2007-09-20 16:32:01 +00:00
|
|
|
/**
|
|
|
|
***
|
|
|
|
**/
|
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
void tr_runInEventThread(tr_session* session, void (*func)(void*), void* user_data)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-06-08 07:24:12 +00:00
|
|
|
TR_ASSERT(tr_isSession(session));
|
2021-09-15 00:18:09 +00:00
|
|
|
TR_ASSERT(session->events != nullptr);
|
2008-10-19 22:04:47 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (tr_amInThread(session->events->thread))
|
2008-05-06 15:52:57 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
(*func)(user_data);
|
2008-05-06 15:52:57 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
else
|
2008-05-06 15:52:57 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_event_handle* e = session->events;
|
2021-11-20 21:20:45 +00:00
|
|
|
auto const lock = std::unique_lock(e->fds_mutex);
|
|
|
|
auto data = tr_run_data{};
|
2013-08-17 16:31:03 +00:00
|
|
|
|
2021-10-23 15:43:15 +00:00
|
|
|
tr_pipe_end_t const fd = e->fds[1];
|
|
|
|
char ch = 'r';
|
|
|
|
ev_ssize_t const res_1 = pipewrite(fd, &ch, 1);
|
2013-08-17 16:31:03 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
data.func = func;
|
|
|
|
data.user_data = user_data;
|
2021-10-23 15:43:15 +00:00
|
|
|
ev_ssize_t const res_2 = pipewrite(fd, &data, sizeof(data));
|
2013-08-17 16:31:03 +00:00
|
|
|
|
2017-04-30 16:25:26 +00:00
|
|
|
if (res_1 == -1 || res_2 == -1)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
tr_logAddError("Unable to write to libtransmisison event queue: %s", tr_strerror(errno));
|
|
|
|
}
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
}
|