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; }
static void blocklistLoad(tr_blocklistFile* b) { tr_sys_file_t fd; uint64_t byteCount; tr_sys_path_info info; char* base; tr_error* error = NULL; char const* err_fmt = _("Couldn't read \"%1$s\": %2$s"); blocklistClose(b); if (!tr_sys_path_get_info(b->filename, 0, &info, NULL)) { return; } byteCount = info.size; if (byteCount == 0) { return; } fd = tr_sys_file_open(b->filename, TR_SYS_FILE_READ, 0, &error); if (fd == TR_BAD_SYS_FILE) { tr_logAddError(err_fmt, b->filename, error->message); tr_error_free(error); return; } b->rules = tr_sys_file_map_for_reading(fd, 0, byteCount, &error); if (b->rules == NULL) { tr_logAddError(err_fmt, b->filename, error->message); tr_sys_file_close(fd, NULL); tr_error_free(error); return; } b->fd = fd; b->byteCount = byteCount; b->ruleCount = byteCount / sizeof(struct tr_ipv4_range); base = tr_sys_path_basename(b->filename, NULL); tr_logAddInfo(_("Blocklist \"%s\" contains %zu entries"), base, b->ruleCount); tr_free(base); }
static void logFunc (int severity, const char * message) { if (severity >= _EVENT_LOG_ERR) tr_logAddError ("%s", message); else tr_logAddDebug ("%s", message); }
static void onFileAdded (tr_session * session, const char * dir, const char * file) { char * filename = tr_buildPath (dir, file, NULL); tr_ctor * ctor = tr_ctorNew (session); int err = tr_ctorSetMetainfoFromFile (ctor, filename); if (!err) { tr_torrentNew (ctor, &err, NULL); if (err == TR_PARSE_ERR) tr_logAddError ("Error parsing .torrent file \"%s\"", file); else { bool trash = false; int test = tr_ctorGetDeleteSource (ctor, &trash); tr_logAddInfo ("Parsing .torrent file successful \"%s\"", file); if (!test && trash) { tr_logAddInfo ("Deleting input .torrent file \"%s\"", file); if (tr_remove (filename)) tr_logAddError ("Error deleting .torrent file: %s", tr_strerror (errno)); } else { char * new_filename = tr_strdup_printf ("%s.added", filename); tr_rename (filename, new_filename); tr_free (new_filename); } } } tr_ctorFree (ctor); tr_free (filename); }
static void blocklistLoad (tr_blocklistFile * b) { int fd; size_t byteCount; struct stat st; char * base; const char * err_fmt = _("Couldn't read \"%1$s\": %2$s"); blocklistClose (b); if (stat (b->filename, &st) == -1) return; fd = open (b->filename, O_RDONLY | O_BINARY); if (fd == -1) { tr_logAddError (err_fmt, b->filename, tr_strerror (errno)); return; } byteCount = (size_t) st.st_size; b->rules = mmap (NULL, byteCount, PROT_READ, MAP_PRIVATE, fd, 0); if (!b->rules) { tr_logAddError (err_fmt, b->filename, tr_strerror (errno)); close (fd); return; } b->fd = fd; b->byteCount = byteCount; b->ruleCount = byteCount / sizeof (struct tr_ipv4_range); base = tr_basename (b->filename); tr_logAddInfo (_("Blocklist \"%s\" contains %zu entries"), base, b->ruleCount); tr_free (base); }
static void gotsig (int sig) { switch (sig) { case SIGHUP: { if (!mySession) { tr_logAddInfo ("Deferring reload until session is fully started."); seenHUP = true; } else { tr_variant settings; const char * configDir; /* reopen the logfile to allow for log rotation */ if (logfileName) { logfile = freopen (logfileName, LOGFILE_MODE_STR, logfile); if (!logfile) fprintf (stderr, "Couldn't reopen \"%s\": %s\n", logfileName, tr_strerror (errno)); } configDir = tr_sessionGetConfigDir (mySession); tr_logAddInfo ("Reloading settings from \"%s\"", configDir); tr_variantInitDict (&settings, 0); tr_variantDictAddBool (&settings, TR_KEY_rpc_enabled, true); tr_sessionLoadSettings (&settings, configDir, MY_NAME); tr_sessionSet (mySession, &settings); tr_variantFree (&settings); tr_sessionReloadBlocklists (mySession); } break; } default: tr_logAddError ("Unexpected signal (%d) in daemon, closing.", sig); /* no break */ case SIGINT: case SIGTERM: event_base_loopexit(ev_base, NULL); break; } }
void tr_eventInit (tr_session * session) { tr_event_handle * eh; session->events = NULL; eh = tr_new0 (tr_event_handle, 1); eh->lock = tr_lockNew (); if (pipe (eh->fds) == -1) tr_logAddError ("Unable to write to pipe() in libtransmission: %s", tr_strerror(errno)); eh->session = session; eh->thread = tr_threadNew (libeventThreadFunc, eh); /* wait until the libevent thread is running */ while (session->events == NULL) tr_wait_msec (100); }
static void watchdir_new_impl (dtr_watchdir * w) { int i; DIR * odir; w->inotify_fd = inotify_init (); if (w->inotify_fd < 0) { i = -1; } else { tr_logAddInfo ("Using inotify to watch directory \"%s\"", w->dir); i = inotify_add_watch (w->inotify_fd, w->dir, DTR_INOTIFY_MASK); } if (i < 0) { tr_logAddError ("Unable to watch \"%s\": %s", w->dir, tr_strerror (errno)); } else if ((odir = opendir (w->dir))) { struct dirent * d; while ((d = readdir (odir))) { const char * name = d->d_name; if (!tr_str_has_suffix (name, ".torrent")) /* skip non-torrents */ continue; tr_logAddInfo ("Found new .torrent file \"%s\" in watchdir \"%s\"", name, w->dir); w->callback (w->session, w->dir, name); } closedir (odir); } }
static void watchdir_new_impl (dtr_watchdir * w) { int i; tr_sys_dir_t odir; w->inotify_fd = inotify_init (); if (w->inotify_fd < 0) { i = -1; } else { tr_logAddInfo ("Using inotify to watch directory \"%s\"", w->dir); i = inotify_add_watch (w->inotify_fd, w->dir, DTR_INOTIFY_MASK); } if (i < 0) { tr_logAddError ("Unable to watch \"%s\": %s", w->dir, tr_strerror (errno)); } else if ((odir = tr_sys_dir_open (w->dir, NULL)) != TR_BAD_SYS_DIR) { const char * name; while ((name = tr_sys_dir_read_name (odir, NULL)) != NULL) { if (!tr_str_has_suffix (name, ".torrent")) /* skip non-torrents */ continue; tr_logAddInfo ("Found new .torrent file \"%s\" in watchdir \"%s\"", name, w->dir); w->callback (w->session, w->dir, name); } tr_sys_dir_close (odir, NULL); } }
void tr_runInEventThread (tr_session * session, void func (void*), void * user_data) { assert (tr_isSession (session)); assert (session->events != NULL); if (tr_amInThread (session->events->thread)) { (func)(user_data); } else { int fd; char ch; ssize_t res_1; ssize_t res_2; tr_event_handle * e = session->events; struct tr_run_data data; tr_lockLock (e->lock); fd = e->fds[1]; ch = 'r'; res_1 = pipewrite (fd, &ch, 1); data.func = func; data.user_data = user_data; res_2 = pipewrite (fd, &data, sizeof (data)); tr_lockUnlock (e->lock); if ((res_1 == -1) || (res_2 == -1)) tr_logAddError ("Unable to write to libtransmisison event queue: %s", tr_strerror(errno)); } }
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; }
struct tr_peer_socket tr_netOpenPeerSocket(tr_session* session, tr_address const* addr, tr_port port, bool clientIsSeed) { TR_ASSERT(tr_address_is_valid(addr)); struct tr_peer_socket ret = TR_PEER_SOCKET_INIT; static int const domains[NUM_TR_AF_INET_TYPES] = { AF_INET, AF_INET6 }; tr_socket_t s; struct sockaddr_storage sock; socklen_t addrlen; tr_address const* source_addr; socklen_t sourcelen; struct sockaddr_storage source_sock; char err_buf[512]; if (!tr_address_is_valid_for_peers(addr, port)) { return ret; } s = tr_fdSocketCreate(session, domains[addr->type], SOCK_STREAM); if (s == TR_BAD_SOCKET) { return ret; } /* seeds don't need much of a read buffer... */ if (clientIsSeed) { int n = 8192; if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void const*)&n, sizeof(n)) == -1) { tr_logAddInfo("Unable to set SO_RCVBUF on socket %" PRIdMAX ": %s", (intmax_t)s, tr_net_strerror(err_buf, sizeof(err_buf), sockerrno)); } } if (evutil_make_socket_nonblocking(s) == -1) { tr_netClose(session, s); return ret; } addrlen = setup_sockaddr(addr, port, &sock); /* set source address */ source_addr = tr_sessionGetPublicAddress(session, addr->type, NULL); TR_ASSERT(source_addr != NULL); sourcelen = setup_sockaddr(source_addr, 0, &source_sock); if (bind(s, (struct sockaddr*)&source_sock, sourcelen) == -1) { tr_logAddError(_("Couldn't set source address %s on %" PRIdMAX ": %s"), tr_address_to_string(source_addr), (intmax_t)s, tr_net_strerror(err_buf, sizeof(err_buf), sockerrno)); tr_netClose(session, s); return ret; } if (connect(s, (struct sockaddr*)&sock, addrlen) == -1 && #ifdef _WIN32 sockerrno != WSAEWOULDBLOCK && #endif sockerrno != EINPROGRESS) { int const tmperrno = sockerrno; if ((tmperrno != ENETUNREACH && tmperrno != EHOSTUNREACH) || addr->type == TR_AF_INET) { tr_logAddError(_("Couldn't connect socket %" PRIdMAX " to %s, port %d (errno %d - %s)"), (intmax_t)s, tr_address_to_string(addr), (int)ntohs(port), tmperrno, tr_net_strerror(err_buf, sizeof(err_buf), tmperrno)); } tr_netClose(session, s); } else { ret = tr_peer_socket_tcp_create(s); } tr_logAddDeep(__FILE__, __LINE__, NULL, "New OUTGOING connection %" PRIdMAX " (%s)", (intmax_t)s, tr_peerIoAddrStr(addr, port)); return ret; }
int tr_blocklistFileSetContent (tr_blocklistFile * b, const char * filename) { tr_sys_file_t in; tr_sys_file_t out; int inCount = 0; char line[2048]; const char * err_fmt = _("Couldn't read \"%1$s\": %2$s"); struct tr_ipv4_range * ranges = NULL; size_t ranges_alloc = 0; size_t ranges_count = 0; tr_error * error = NULL; if (!filename) { blocklistDelete (b); return 0; } in = tr_sys_file_open (filename, TR_SYS_FILE_READ, 0, &error); if (in == TR_BAD_SYS_FILE) { tr_logAddError (err_fmt, filename, error->message); tr_error_free (error); return 0; } blocklistClose (b); out = tr_sys_file_open (b->filename, TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE | TR_SYS_FILE_TRUNCATE, 0666, &error); if (out == TR_BAD_SYS_FILE) { tr_logAddError (err_fmt, b->filename, error->message); tr_error_free (error); tr_sys_file_close (in, NULL); return 0; } /* load the rules into memory */ while (tr_sys_file_read_line (in, line, sizeof (line), NULL)) { struct tr_ipv4_range range; ++inCount; if (!parseLine (line, &range)) { /* don't try to display the actual lines - it causes issues */ tr_logAddError (_("blocklist skipped invalid address at line %d"), inCount); continue; } if (ranges_alloc == ranges_count) { ranges_alloc += 4096; /* arbitrary */ ranges = tr_renew (struct tr_ipv4_range, ranges, ranges_alloc); } ranges[ranges_count++] = range; }
/** * 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; } }
/** * 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; }
static bool create_path (const char * path_in, int permissions, tr_error ** error) { char * p; char * pp; bool done; int tmperr; int rv; struct stat sb; char * path; /* make a temporary copy of path */ path = tr_strdup (path_in); /* walk past the root */ p = path; while (*p == TR_PATH_DELIMITER) ++p; pp = p; done = false; while ((p = strchr (pp, TR_PATH_DELIMITER)) || (p = strchr (pp, '\0'))) { if (!*p) done = true; else *p = '\0'; tmperr = errno; rv = stat (path, &sb); errno = tmperr; if (rv) { tr_error * my_error = NULL; /* Folder doesn't exist yet */ if (!tr_sys_dir_create (path, 0, permissions, &my_error)) { tr_logAddError (_ ("Couldn't create \"%1$s\": %2$s"), path, my_error->message); tr_free (path); tr_error_propagate (error, &my_error); return false; } } else if ((sb.st_mode & S_IFMT) != S_IFDIR) { /* Node exists but isn't a folder */ char * const buf = tr_strdup_printf (_ ("File \"%s\" is in the way"), path); tr_logAddError (_ ("Couldn't create \"%1$s\": %2$s"), path_in, buf); tr_free (buf); tr_free (path); set_system_error (error, ENOTDIR); return false; } if (done) break; *p = TR_PATH_DELIMITER; p++; pp = p; } tr_free (path); return true; }
int tr_blocklistFileSetContent (tr_blocklistFile * b, const char * filename) { FILE * in; FILE * out; int inCount = 0; char line[2048]; const char * err_fmt = _("Couldn't read \"%1$s\": %2$s"); struct tr_ipv4_range * ranges = NULL; size_t ranges_alloc = 0; size_t ranges_count = 0; if (!filename) { blocklistDelete (b); return 0; } in = fopen (filename, "rb"); if (in == NULL) { tr_logAddError (err_fmt, filename, tr_strerror (errno)); return 0; } blocklistClose (b); out = fopen (b->filename, "wb+"); if (out == NULL) { tr_logAddError (err_fmt, b->filename, tr_strerror (errno)); fclose (in); return 0; } /* load the rules into memory */ while (fgets (line, sizeof (line), in) != NULL) { char * walk; struct tr_ipv4_range range; ++inCount; /* zap the linefeed */ if ((walk = strchr (line, '\r'))) *walk = '\0'; if ((walk = strchr (line, '\n'))) *walk = '\0'; if (!parseLine (line, &range)) { /* don't try to display the actual lines - it causes issues */ tr_logAddError (_("blocklist skipped invalid address at line %d"), inCount); continue; } if (ranges_alloc == ranges_count) { ranges_alloc += 4096; /* arbitrary */ ranges = tr_renew (struct tr_ipv4_range, ranges, ranges_alloc); } ranges[ranges_count++] = range; }