void resp_free(HTTPResponse *resp) { if (resp->headers) { st_free(resp->headers); } if (resp->responseStrings) str_free(resp->responseStrings); if (resp->statusMsg && (resp->statusMsg != apple)) WOFREE(resp->statusMsg); if (resp->content && !(resp->flags & RESP_DONT_FREE_CONTENT)) WOFREE(resp->content); if (resp->instanceConnection && (resp->flags & RESP_CLOSE_CONNECTION)) tr_close(resp->instanceConnection, resp->instHandle, resp->keepConnection); WOFREE(resp); }
void rtr_stop(struct rtr_socket *rtr_socket) { RTR_DBG1("rtr_stop()"); rtr_change_socket_state(rtr_socket, RTR_SHUTDOWN); if(rtr_socket->thread_id != 0) { RTR_DBG1("pthread_cancel()"); pthread_cancel(rtr_socket->thread_id); RTR_DBG1("pthread_join()"); pthread_join(rtr_socket->thread_id, NULL); tr_close(rtr_socket->tr_socket); rtr_socket->request_session_id = true; rtr_socket->serial_number = 0; rtr_socket->last_update = 0; pfx_table_src_remove(rtr_socket->pfx_table, rtr_socket); spki_table_src_remove(rtr_socket->spki_table, rtr_socket); rtr_socket->thread_id = 0; } RTR_DBG1("Socket shut down"); }
void tr_reset(void) { char fname[150]; char fpath[150]; char s[200]; pthread_mutex_lock(&mutex); tr_close(); snprintf(fpath, sizeof(fpath), "%s/%s", USBDISK_PATH, DATA_DIR); snprintf(fname, sizeof(fname), "%s", TR_DATA_FNAME); snprintf(s, sizeof(s), "%s/%s", fpath, fname); unlink(s); snprintf(fpath, sizeof(fpath), "%s/%s", USBDISK_PATH, DATA_DIR); snprintf(fname, sizeof(fname), "%s", TR_HEAD_FNAME); snprintf(s, sizeof(s), "%s/%s", fpath, fname); unlink(s); tr_open(); pthread_mutex_unlock(&mutex); }
int libbt_cleanup() { if(engine) { //firstly close all torrent int i; for(i = 0; i < MAX_DOWNLOAD_NS; i++) { if(g_tor_array[i]) { tr_torrentClose(engine, g_tor_array[i]); g_tor_array[i] = NULL; } } tr_close(engine); engine = NULL; } if(g_tor_array) { FREE(g_tor_array); } return 0; }
/* * run the request */ static HTTPResponse *_runRequest(WOAppReq *app, WOAppHandle woappHandle, WOInstanceHandle instHandle, HTTPRequest *req) { WOConnection *c; HTTPResponse *resp = NULL; int send_status, keepConnection, retryRequest = 0; const char *idString = NULL; WOLog(WO_INFO,"Trying to contact %s:%s on %s(%d)", app->name,app->instance,app->host,app->port); /* Tag the request with a unique identifier (if it hasn't been already) */ /* (It might already have been tagged if we are retrying the request due to refusing new sessions, for example) */ idString = req_HeaderForKey(req, REQUEST_ID_HEADER); if (idString == NULL) { String *requestID = tr_uniqueID(); if (requestID) { req_addHeader(req, REQUEST_ID_HEADER, requestID->text, STR_COPYVALUE); idString = req_HeaderForKey(req, REQUEST_ID_HEADER); str_free(requestID); } } do { c = tr_open(instHandle); /* connect */ if (c == NULL) { WOLog(WO_INFO,"%s:%s NOT LISTENING on %s(%d)", app->name,app->instance,app->host,app->port); app->error = err_connect; return NULL; } /* * app found and is listening on port */ if (app->scheduler->beginTransaction) app->scheduler->beginTransaction(app, instHandle); /* Make sure that we're the only connection header, and we're explicit about the setting */ req_removeHeader(req,CONNECTION); if (c->isPooled) { req_addHeader(req,CONNECTION,HTTP_KEEP_ALIVE,0); } else { req_addHeader(req,CONNECTION,HTTP_CLOSE,0); } WOLog(WO_INFO,"%s:%s on %s(%d) connected [pooled: %s]", app->name, app->instance, app->host, app->port, c->isPooled ? "Yes" : "No"); /* * send the request.... */ send_status = req_sendRequest(req, c->fd); if (send_status != 0) { if ((send_status == TR_RESET) && (retryRequest == 0) && !req->haveReadStreamedData) { /* If we get here the connection was reset. This means the instance has either quit or crashed. */ /* Bump the generation number so all pooled connections to this instance will be invalidated. */ /* Then retry the request with a new connection. If the instance is not running the retry will */ /* fail with a different error and the instance will be marked dead. */ _WOInstance *inst = ac_lockInstance(instHandle); /* note: if we get here, keepConnection == 0 --> this connection will be closed */ if (inst) { ac_cycleInstance(inst, c->generation); ac_unlockInstance(instHandle); } retryRequest++; WOLog(WO_INFO, "retrying request due to connection reset"); /* Must close connection before continuing */ tr_close(c, instHandle, 0); continue; } else { WOLog(WO_ERR,"Failed to send request"); tr_close(c, instHandle, 0); /* close app connection */ if (send_status == -1) app->error = err_read; else app->error = err_send; return NULL; } } /* Note that we have a request queued */ WOLog(WO_INFO,"Request %s sent, awaiting response", req->request_str); /* While the app is processing the request, take the opportunity to check/update the config. */ ac_readConfiguration(); /* * now wait for the response... */ resp = resp_getResponseHeaders(c, instHandle); /* go ahead and read the first chunk of response data */ if (resp && req->method != HTTP_HEAD_METHOD) { if (resp_getResponseContent(resp, 1) == -1) { resp_free(resp); resp = NULL; } } /* Validate the ID */ if (idString && resp) { const char *respID = st_valueFor(resp->headers, REQUEST_ID_HEADER); if (respID != NULL) { if (strcmp(respID, idString) != 0) { WOLog(WO_ERR, "Got response with wrong ID! Dumping response. request ID = %s, response ID = %s", idString, respID); /* note this will cause the connection to be closed below */ resp_free(resp); resp = NULL; } else st_removeKey(resp->headers, REQUEST_ID_HEADER); } else WOLog(WO_WARN, "Got response with no ID."); } app->response = resp; /* * check if this connection can be kept open */ keepConnection = 0; #ifndef CGI /* doesn't make sense to keep the connection for CGI */ if (resp && resp->headers) { const char *keepAlive; keepAlive = st_valueFor(resp->headers, CONNECTION); if (keepAlive) { /* if the keep alive header is set, honor the value */ if (strcasecmp(keepAlive, HTTP_KEEP_ALIVE) == 0) keepConnection = 1; } else { /* no keep alive header - keep alive by default for HTTP/1.1 only */ if (resp->flags & RESP_HTTP11) keepConnection = 1; } } #endif if (resp != NULL) { if (app->scheduler->finalizeTransaction) if (app->scheduler->finalizeTransaction(app, instHandle)) keepConnection = 0; st_removeKey(resp->headers, REFUSING_SESSIONS_HEADER); st_removeKey(resp->headers, LOAD_AVERAGE_HEADER); st_removeKey(resp->headers, CONNECTION); WOLog(WO_INFO,"received ->%d %s",resp->status,resp->statusMsg); retryRequest = 0; } else { if (c != NULL && tr_connectionWasReset(c)) { /* If we get here the connection was reset. This means the instance has either quit or crashed. */ /* Bump the generation number so all pooled connections to this instance will be invalidated. */ /* Then retry the request with a new connection. If the instance is not running the retry will */ /* fail with a different error and the instance will be marked dead. */ /* Note that only one retry due to a connection reset error is allowed. This is to prevent an */ /* infinite loop if the instance dies while processing the request and restarts quickly enough */ /* to process the retry. */ _WOInstance *inst = ac_lockInstance(instHandle); /* note: if we get here, keepConnection == 0 --> this connection will be closed */ if (inst) { ac_cycleInstance(inst, c->generation); ac_unlockInstance(instHandle); } retryRequest++; if (retryRequest == 1) WOLog(WO_INFO, "retrying request due to connection reset"); } else app->error = err_response; } if (resp && resp->content_read < resp->content_length) { resp->keepConnection = keepConnection; resp->instHandle = instHandle; resp->flags |= RESP_CLOSE_CONNECTION; } else { tr_close(c, instHandle, keepConnection); } } while (retryRequest == 1); return resp; }
void rtr_fsm_start(struct rtr_socket *rtr_socket) { rtr_socket->state = RTR_CONNECTING; install_sig_handler(); while(1) { if(rtr_socket->state == RTR_CONNECTING) { RTR_DBG1("State: RTR_CONNECTING"); //old pfx_record could exists in the pfx_table, check if they are too old and must be removed rtr_purge_outdated_records(rtr_socket); if(tr_open(rtr_socket->tr_socket) == TR_ERROR) { rtr_change_socket_state(rtr_socket, RTR_ERROR_TRANSPORT); } else if(rtr_socket->request_session_id) { //change to state RESET, if socket dont has a session_id rtr_change_socket_state(rtr_socket, RTR_RESET); } else { //if we already have a session_id, send a serial query and start to sync if(rtr_send_serial_query(rtr_socket) == RTR_SUCCESS) rtr_change_socket_state(rtr_socket, RTR_SYNC); else rtr_change_socket_state(rtr_socket, RTR_ERROR_FATAL); } } else if(rtr_socket->state == RTR_RESET) { RTR_DBG1("State: RTR_RESET"); if (rtr_send_reset_query(rtr_socket) == 0) { RTR_DBG1("rtr_start: reset pdu sent"); rtr_change_socket_state(rtr_socket, RTR_SYNC); //send reset query after connection established } } else if(rtr_socket->state == RTR_SYNC) { RTR_DBG1("State: RTR_SYNC"); if(rtr_sync(rtr_socket) == 0) rtr_change_socket_state(rtr_socket, RTR_ESTABLISHED); //send reset query after connection established } else if(rtr_socket->state == RTR_ESTABLISHED) { RTR_DBG1("State: RTR_ESTABLISHED"); if(rtr_wait_for_sync(rtr_socket) == RTR_SUCCESS) { //blocks till cache_timeout is expired or PDU was received //serial query senden if(rtr_send_serial_query(rtr_socket) == RTR_SUCCESS) rtr_change_socket_state(rtr_socket, RTR_SYNC); } } else if(rtr_socket->state == RTR_ERROR_NO_DATA_AVAIL) { RTR_DBG1("State: RTR_ERROR_NO_DATA_AVAIL"); rtr_socket->request_session_id = true; rtr_socket->serial_number = 0; rtr_change_socket_state(rtr_socket, RTR_RESET); sleep(ERR_TIMEOUT); rtr_purge_outdated_records(rtr_socket); } else if(rtr_socket->state == RTR_ERROR_NO_INCR_UPDATE_AVAIL) { RTR_DBG1("State: RTR_ERROR_NO_INCR_UPDATE_AVAIL"); rtr_socket->request_session_id = true; rtr_socket->serial_number = 0; rtr_change_socket_state(rtr_socket, RTR_RESET); rtr_purge_outdated_records(rtr_socket); } else if(rtr_socket->state == RTR_ERROR_TRANSPORT) { RTR_DBG1("State: RTR_ERROR_TRANSPORT"); tr_close(rtr_socket->tr_socket); rtr_change_socket_state(rtr_socket, RTR_CONNECTING); sleep(ERR_TIMEOUT); } else if(rtr_socket->state == RTR_ERROR_FATAL) { RTR_DBG1("State: RTR_ERROR_FATAL"); tr_close(rtr_socket->tr_socket); rtr_change_socket_state(rtr_socket, RTR_CONNECTING); sleep(ERR_TIMEOUT); } else if(rtr_socket->state == RTR_SHUTDOWN) { RTR_DBG1("State: RTR_SHUTDOWN"); tr_close(rtr_socket->tr_socket); rtr_socket->request_session_id = true; rtr_socket->serial_number = 0; rtr_socket->last_update = 0; pfx_table_src_remove(rtr_socket->pfx_table, rtr_socket); pthread_exit(NULL); } } }
/* WARNING: This Function has cancelable sections*/ void *rtr_fsm_start(struct rtr_socket *rtr_socket) { if (rtr_socket->state == RTR_SHUTDOWN) return NULL; // We don't care about the old state, but POSIX demands a non null value for setcancelstate int oldcancelstate; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldcancelstate); rtr_socket->state = RTR_CONNECTING; while(1) { if(rtr_socket->state == RTR_CONNECTING) { RTR_DBG1("State: RTR_CONNECTING"); rtr_socket->has_received_pdus = false; //old pfx_record could exists in the pfx_table, check if they are too old and must be removed //old key_entry could exists in the spki_table, check if they are too old and must be removed rtr_purge_outdated_records(rtr_socket); if(tr_open(rtr_socket->tr_socket) == TR_ERROR) { rtr_change_socket_state(rtr_socket, RTR_ERROR_TRANSPORT); } else if(rtr_socket->request_session_id) { //change to state RESET, if socket dont has a session_id rtr_change_socket_state(rtr_socket, RTR_RESET); } else { //if we already have a session_id, send a serial query and start to sync if(rtr_send_serial_query(rtr_socket) == RTR_SUCCESS) rtr_change_socket_state(rtr_socket, RTR_SYNC); else rtr_change_socket_state(rtr_socket, RTR_ERROR_FATAL); } } else if(rtr_socket->state == RTR_RESET) { RTR_DBG1("State: RTR_RESET"); if (rtr_send_reset_query(rtr_socket) == RTR_SUCCESS) { RTR_DBG1("rtr_start: reset pdu sent"); rtr_change_socket_state(rtr_socket, RTR_SYNC); //start to sync after connection is established } } else if(rtr_socket->state == RTR_SYNC) { RTR_DBG1("State: RTR_SYNC"); if(rtr_sync(rtr_socket) == RTR_SUCCESS) rtr_change_socket_state(rtr_socket, RTR_ESTABLISHED); //wait for next sync after first successful sync } else if(rtr_socket->state == RTR_ESTABLISHED) { RTR_DBG1("State: RTR_ESTABLISHED"); // Allow thread cancellation for recv code path only. // This should be enough since we spend most of the time blocking on recv pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldcancelstate); int ret = rtr_wait_for_sync(rtr_socket); //blocks till expire_interval is expired or PDU was received pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldcancelstate); if(ret == RTR_SUCCESS) { //send serial query if(rtr_send_serial_query(rtr_socket) == RTR_SUCCESS) rtr_change_socket_state(rtr_socket, RTR_SYNC); } } else if(rtr_socket->state == RTR_FAST_RECONNECT){ RTR_DBG1("State: RTR_FAST_RECONNECT"); tr_close(rtr_socket->tr_socket); rtr_change_socket_state(rtr_socket, RTR_CONNECTING); } else if(rtr_socket->state == RTR_ERROR_NO_DATA_AVAIL) { RTR_DBG1("State: RTR_ERROR_NO_DATA_AVAIL"); rtr_socket->request_session_id = true; rtr_socket->serial_number = 0; rtr_change_socket_state(rtr_socket, RTR_RESET); sleep(rtr_socket->retry_interval); rtr_purge_outdated_records(rtr_socket); } else if(rtr_socket->state == RTR_ERROR_NO_INCR_UPDATE_AVAIL) { RTR_DBG1("State: RTR_ERROR_NO_INCR_UPDATE_AVAIL"); rtr_socket->request_session_id = true; rtr_socket->serial_number = 0; rtr_change_socket_state(rtr_socket, RTR_RESET); rtr_purge_outdated_records(rtr_socket); } else if(rtr_socket->state == RTR_ERROR_TRANSPORT) { RTR_DBG1("State: RTR_ERROR_TRANSPORT"); tr_close(rtr_socket->tr_socket); rtr_change_socket_state(rtr_socket, RTR_CONNECTING); RTR_DBG("Waiting %u", rtr_socket->retry_interval); sleep(rtr_socket->retry_interval); } else if(rtr_socket->state == RTR_ERROR_FATAL) { RTR_DBG1("State: RTR_ERROR_FATAL"); tr_close(rtr_socket->tr_socket); rtr_change_socket_state(rtr_socket, RTR_CONNECTING); RTR_DBG("Waiting %u", rtr_socket->retry_interval); sleep(rtr_socket->retry_interval); } else if(rtr_socket->state == RTR_SHUTDOWN) { RTR_DBG1("State: RTR_SHUTDOWN"); pthread_exit(NULL); } } }
/******************************************************************************* * main ******************************************************************************/ int main(int argc, char ** argv) { /* vars */ int i, error, nat; tr_handle_t * h; tr_torrent_t * tor; tr_stat_t * s; double tf_sharing = 0.0; char tf_string[80]; int tf_seeders, tf_leechers; /* Get options */ if (parseCommandLine(argc, argv)) { printf("Transmission %s [%d] - tfCLI [%d]\nhttp://transmission.m0k.org/ - http://tf-b4rt.berlios.de/\n\n", VERSION_STRING, VERSION_REVISION, VERSION_REVISION_CLI); printf(USAGE, argv[0], TR_DEFAULT_PORT); return 1; } /* show help */ if (showHelp) { printf("Transmission %s [%d] - tfCLI [%d]\nhttp://transmission.m0k.org/ - http://tf-b4rt.berlios.de/\n\n", VERSION_STRING, VERSION_REVISION, VERSION_REVISION_CLI); printf(USAGE, argv[0], TR_DEFAULT_PORT); return 0; } // verbose if (verboseLevel < 0) { verboseLevel = 0; } else if (verboseLevel > 9) { verboseLevel = 9; } if (verboseLevel) { static char env[11]; sprintf(env, "TR_DEBUG=%d", verboseLevel); putenv(env); } // check port if (bindPort < 1 || bindPort > 65535) { printf("Invalid port '%d'\n", bindPort); return 1; } // Initialize libtransmission h = tr_init(); // Open and parse torrent file if (!(tor = tr_torrentInit(h, torrentPath, 0, &error))) { printf("Failed opening torrent file `%s'\n", torrentPath); goto failed; } /* show info */ if (showInfo) { // info tr_info_t * info = tr_torrentInfo(tor); // stat s = tr_torrentStat(tor); // Print torrent info (quite à la btshowmetainfo) printf("hash: "); for (i = 0; i < SHA_DIGEST_LENGTH; i++) { printf("%02x", info->hash[i]); } printf("\n"); printf( "tracker: %s:%d\n", s->trackerAddress, s->trackerPort ); printf( "announce: %s\n", s->trackerAnnounce ); printf("size: %"PRIu64" (%"PRIu64" * %d + %"PRIu64")\n", info->totalSize, info->totalSize / info->pieceSize, info->pieceSize, info->totalSize % info->pieceSize); if (info->comment[0]) { printf("comment: %s\n", info->comment); } if (info->creator[0]) { printf("creator: %s\n", info->creator); } printf("file(s):\n"); for (i = 0; i < info->fileCount; i++) { printf(" %s (%"PRIu64")\n", info->files[i].name, info->files[i].length); } // cleanup goto cleanup; } /* show scrape */ if (showScrape) { int seeders, leechers, downloaded; if (tr_torrentScrape(tor, &seeders, &leechers, &downloaded)) { printf("Scrape failed.\n"); } else { printf("%d seeder(s), %d leecher(s), %d download(s).\n", seeders, leechers, downloaded); } // cleanup goto cleanup; } /* start up transmission */ // Create PID file if wanted by user if (tf_pid != NULL) { FILE * pid_file; pid_file = fopen(tf_pid, "w+"); if (pid_file != NULL) { fprintf(pid_file, "%d", getpid()); fclose(pid_file); } } // signal signal(SIGINT, sigHandler); // If running torrentflux, Download limit = 0 means no limit if (tf_stat_file != NULL) { /* tfCLI */ // up switch (uploadLimit) { case 0: uploadLimit = -1; break; case -2: uploadLimit = 0; break; } // down switch (downloadLimit) { case 0: downloadLimit = -1; break; case -2: downloadLimit = 0; break; } } // init some things tr_setBindPort(h, bindPort); tr_setGlobalUploadLimit(h, uploadLimit); tr_setGlobalDownloadLimit(h, downloadLimit); // nat-traversal if (natTraversal) { tr_natTraversalEnable(h); } else { tr_natTraversalDisable(h); } // set folder tr_torrentSetFolder(tor, "."); // start the torrent tr_torrentStart(tor); /* main-loop */ while (!mustDie) { // status-string char string[80]; int chars = 0; // int result; char stat_state; // sleep sleep(displayInterval); // Check if we must stop if (tf_stat_file != NULL) { /* tfCLI */ tf_stat = fopen(tf_stat_file, "r"); if (tf_stat != NULL) { // Get state stat_state = fgetc(tf_stat); // Close the file fclose(tf_stat); // Torrentflux asked to shutdown the torrent if (stat_state == '0') { mustDie = 1; } } } // torrent-stat s = tr_torrentStat(tor); if (s->status & TR_STATUS_CHECK) { /* --- CHECK --- */ if (tf_stat_file == NULL) { /* standalone */ // status-string chars = snprintf(string, 80, "Checking files... %.2f %%", 100.0 * s->progress ); } else { /* tfCLI */ // write tf-stat-file tr_info_t * info = tr_torrentInfo(tor); tf_stat = fopen(tf_stat_file, "w+"); if (tf_stat != NULL) { fprintf(tf_stat, "%d\n%.1f\n%s\n0 kB/s\n0 kB/s\n%s\n0\n0\n0.0\n%d\n0\n%" PRIu64 "\n%" PRIu64, 1, /* State */ 100.0 * s->progress, /* checking progress */ "Checking existing data", /* State text */ /* download speed */ /* upload speed */ tf_user, /* user */ /* seeds */ /* peers */ /* sharing */ seedLimit, /* seedlimit */ /* uploaded bytes */ s->downloaded, /* downloaded bytes */ info->totalSize); /* global size */ fclose(tf_stat); } } } else if (s->status & TR_STATUS_DOWNLOAD) { /* --- DOWNLOAD --- */ if (tf_stat_file == NULL) { /* standalone */ // status-string chars = snprintf(string, 80, "Progress: %.2f %%, %d peer%s, dl from %d (%.2f KB/s), " "ul to %d (%.2f KB/s)", 100.0 * s->progress, s->peersTotal, ( s->peersTotal == 1 ) ? "" : "s", s->peersUploading, s->rateDownload, s->peersDownloading, s->rateUpload); } else { /* tfCLI */ // sharing if (s->downloaded != 0) { tf_sharing = ((double)(s->uploaded) / (double)(s->downloaded)) * 100; } // seeders + leechers if (s->seeders < 0) { tf_seeders = 0; } else { tf_seeders = s->seeders; } if (s->leechers < 0) { tf_leechers = 0; } else { tf_leechers = s->leechers; } // eta if (s->eta != -1) { // sanity-check. value of eta >= 7 days is not really of use if (s->eta < 604800) { if ((s->eta / (24 * 60 * 60)) != 0) { sprintf(tf_string,"%d:%02d:%02d:%02d", s->eta / (24 * 60 * 60), ((s->eta) % (24 * 60 * 60)) / (60 * 60), ((s->eta) % (60 * 60) / 60), s->eta % 60); } else if ((s->eta / (60 * 60)) != 0) { sprintf(tf_string, "%d:%02d:%02d", (s->eta) / (60 * 60), ((s->eta) % (60 * 60) / 60), s->eta % 60); } else { sprintf(tf_string, "%d:%02d", (s->eta) / 60, s->eta % 60); } } else { sprintf(tf_string,"-"); } } else { sprintf(tf_string,"-"); } if ((s->seeders == -1) && (s->peersTotal == 0)) { sprintf(tf_string,"Connecting to Peers"); } // write tf-stat-file tf_stat = fopen(tf_stat_file, "w+"); if (tf_stat != NULL) { tr_info_t * info = tr_torrentInfo( tor ); fprintf(tf_stat, "%d\n%.1f\n%s\n%.1f kB/s\n%.1f kB/s\n%s\n%d (%d)\n%d (%d)\n%.1f\n%d\n%" PRIu64 "\n%" PRIu64 "\n%" PRIu64, 1, /* State */ 100.0 * s->progress, /* progress */ tf_string, /* Estimated time */ s->rateDownload, /* download speed */ s->rateUpload, /* upload speed */ tf_user, /* user */ s->peersUploading, tf_seeders, /* seeds */ s->peersDownloading, tf_leechers, /* peers */ tf_sharing, /* sharing */ seedLimit, /* seedlimit */ s->uploaded, /* uploaded bytes */ s->downloaded, /* downloaded bytes */ info->totalSize); /* global size */ fclose(tf_stat); } } } else if (s->status & TR_STATUS_SEED) { /* --- SEED --- */ // info tr_info_t * info = tr_torrentInfo(tor); if (tf_stat_file == NULL) { /* standalone */ // status-string chars = snprintf(string, 80, "Seeding, uploading to %d of %d peer(s), %.2f KB/s", s->peersDownloading, s->peersTotal, s->rateUpload); } else { /* tfCLI */ // sharing if (s->downloaded != 0) { tf_sharing = ((double)(s->uploaded) / (double)(s->downloaded)) * 100; } else { tf_sharing = ((double)(s->uploaded) / (double)(info->totalSize)) * 100; } // If we reached the seeding limit, we have to quit transmission if ((seedLimit != 0) && ((tf_sharing > (double)(seedLimit)) || (seedLimit == -1))) { mustDie = 1; } // seeders + leechers if (s->seeders < 0) { tf_seeders = 0; } else { tf_seeders = s->seeders; } if (s->leechers < 0) { tf_leechers = 0; } else { tf_leechers = s->leechers; } // write tf-stat-file tf_stat = fopen(tf_stat_file, "w+"); if (tf_stat != NULL) { fprintf(tf_stat, "%d\n%.1f\n%s\n%.1f kB/s\n%.1f kB/s\n%s\n%d (%d)\n%d (%d)\n%.1f\n%d\n%" PRIu64 "\n%" PRIu64 "\n%" PRIu64, 1, /* State */ 100.0 * s->progress, /* progress */ "Download Succeeded!", /* State text */ s->rateDownload, /* download speed */ s->rateUpload, /* upload speed */ tf_user, /* user */ s->peersUploading, tf_seeders, /* seeds */ s->peersDownloading, tf_leechers, /* peers */ tf_sharing, /* sharing */ seedLimit, /* seedlimit */ s->uploaded, /* uploaded bytes */ s->downloaded, /* downloaded bytes */ info->totalSize); /* global size */ fclose(tf_stat); } } } // status-string if (tf_stat_file == NULL) { /* standalone */ memset( &string[chars], ' ', 79 - chars ); string[79] = '\0'; // print status to stderr fprintf(stderr, "\r%s", string); } // errors if (s->error & TR_ETRACKER) { if (tf_stat_file == NULL) { /* standalone */ // print errors to stderr fprintf(stderr, "\n%s\n", s->trackerError); } else { /* tfCLI */ // append errors to stat-file tf_stat = fopen(tf_stat_file, "a+"); if (tf_stat != NULL) { fprintf(tf_stat, "\n%s\n", s->trackerError); fclose(tf_stat); } } } else if (verboseLevel > 0) { if (tf_stat_file == NULL) { /* standalone */ // stderr fprintf(stderr, "\n"); } } // finishCall if (tr_getFinished(tor)) { result = system(finishCall); } } /* main-loop */ // mark torrent as stopped in tf-stat-file if (tf_stat_file != NULL) { /* tfCLI */ // info tr_info_t * info = tr_torrentInfo(tor); // sharing if (s->downloaded != 0) { tf_sharing = ((double)(s->uploaded) / (double)(s->downloaded)) * 100; } else { tf_sharing = ((double)(s->uploaded) / (double)(info->totalSize)) * 100; } // write tf-stat-file tf_stat = fopen(tf_stat_file, "w+"); if (tf_stat != NULL) { float progress; if (s->status & TR_STATUS_SEED) { sprintf(tf_string,"Download Succeeded!"); progress = 100; } else { sprintf(tf_string,"Torrent Stopped"); progress = -(1 + s->progress) * 100; } fprintf(tf_stat, "%d\n%.1f\n%s\n\n\n%s\n\n\n%.1f\n%d\n%" PRIu64 "\n%" PRIu64 "\n%" PRIu64, 0, /* State */ progress, /* progress */ tf_string, /* State text */ /* download speed */ /* upload speed */ tf_user, /* user */ /* seeds */ /* peers */ tf_sharing, /* sharing */ seedLimit, /* seedlimit */ s->uploaded, /* uploaded bytes */ s->downloaded, /* downloaded bytes */ info->totalSize); /* global size */ fclose(tf_stat); } } // stderr if (tf_stat_file == NULL) { /* standalone */ fprintf(stderr, "\n"); } // Try for 5 seconds to notify the tracker that we are leaving // and to delete any port mappings for nat traversal tr_torrentStop(tor); tr_natTraversalDisable(h); for (i = 0; i < 10; i++) { s = tr_torrentStat(tor); nat = tr_natTraversalStatus(h); if (s->status & TR_STATUS_PAUSE && TR_NAT_TRAVERSAL_DISABLED == nat) { // The 'stopped' tracker message was sent // and port mappings were deleted break; } usleep(500000); } // Remove PID file if created ! if (tf_pid != NULL) { remove(tf_pid); } cleanup: tr_torrentClose(h, tor); failed: tr_close(h); return 0; }