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"); }
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; }
/** * 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; }
/** * 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; } }