source: trunk/daemon/daemon.c @ 14241

Last change on this file since 14241 was 14241, checked in by jordan, 8 years ago

Copyedit the license's revised text: (1) remove unnecessary repitition use of the word 'license' from the top of the header and source files (2) add the standard 'we hope it's useful, but no warranty' clause to COPYING (3) make explicit that linking OpenSSL is allowed (see https://people.gnome.org/~markmc/openssl-and-the-gpl.html for background) (4) sync the Qt and GTK+ clients' license popups with COPYING's revised text

  • Property svn:keywords set to Date Rev Author Id
File size: 21.6 KB
Line 
1/*
2 * This file Copyright (C) 2008-2014 Mnemosyne LLC
3 *
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 * $Id: daemon.c 14241 2014-01-21 03:10:30Z jordan $
8 */
9
10#include <errno.h>
11#include <stdio.h> /* printf */
12#include <stdlib.h> /* exit, atoi */
13
14#include <fcntl.h> /* open */
15#include <signal.h>
16#ifdef HAVE_SYSLOG
17#include <syslog.h>
18#endif
19#include <unistd.h> /* daemon */
20
21#include <event2/buffer.h>
22
23#include <libtransmission/transmission.h>
24#include <libtransmission/tr-getopt.h>
25#include <libtransmission/log.h>
26#include <libtransmission/utils.h>
27#include <libtransmission/variant.h>
28#include <libtransmission/version.h>
29
30#ifdef USE_SYSTEMD_DAEMON
31 #include <systemd/sd-daemon.h>
32#else
33 static void sd_notify (int status UNUSED, const char * str UNUSED) { }
34 static void sd_notifyf (int status UNUSED, const char * fmt UNUSED, ...) { }
35#endif
36
37#include "watch.h"
38
39#define MY_NAME "transmission-daemon"
40
41#define MEM_K 1024
42#define MEM_K_STR "KiB"
43#define MEM_M_STR "MiB"
44#define MEM_G_STR "GiB"
45#define MEM_T_STR "TiB"
46
47#define DISK_K 1000
48#define DISK_B_STR  "B"
49#define DISK_K_STR "kB"
50#define DISK_M_STR "MB"
51#define DISK_G_STR "GB"
52#define DISK_T_STR "TB"
53
54#define SPEED_K 1000
55#define SPEED_B_STR  "B/s"
56#define SPEED_K_STR "kB/s"
57#define SPEED_M_STR "MB/s"
58#define SPEED_G_STR "GB/s"
59#define SPEED_T_STR "TB/s"
60
61#define LOGFILE_MODE_STR "a+"
62
63static bool paused = false;
64static bool closing = false;
65static bool seenHUP = false;
66static const char *logfileName = NULL;
67static FILE *logfile = NULL;
68static tr_session * mySession = NULL;
69static tr_quark key_pidfile = 0;
70
71/***
72****  Config File
73***/
74
75static const char *
76getUsage (void)
77{
78    return "Transmission " LONG_VERSION_STRING
79           "  http://www.transmissionbt.com/\n"
80           "A fast and easy BitTorrent client\n"
81           "\n"
82           MY_NAME " is a headless Transmission session\n"
83           "that can be controlled via transmission-remote\n"
84           "or the web interface.\n"
85           "\n"
86           "Usage: " MY_NAME " [options]";
87}
88
89static const struct tr_option options[] =
90{
91
92    { 'a', "allowed", "Allowed IP addresses. (Default: " TR_DEFAULT_RPC_WHITELIST ")", "a", 1, "<list>" },
93    { 'b', "blocklist", "Enable peer blocklists", "b", 0, NULL },
94    { 'B', "no-blocklist", "Disable peer blocklists", "B", 0, NULL },
95    { 'c', "watch-dir", "Where to watch for new .torrent files", "c", 1, "<directory>" },
96    { 'C', "no-watch-dir", "Disable the watch-dir", "C", 0, NULL },
97    { 941, "incomplete-dir", "Where to store new torrents until they're complete", NULL, 1, "<directory>" },
98    { 942, "no-incomplete-dir", "Don't store incomplete torrents in a different location", NULL, 0, NULL },
99    { 'd', "dump-settings", "Dump the settings and exit", "d", 0, NULL },
100    { 'e', "logfile", "Dump the log messages to this filename", "e", 1, "<filename>" },
101    { 'f', "foreground", "Run in the foreground instead of daemonizing", "f", 0, NULL },
102    { 'g', "config-dir", "Where to look for configuration files", "g", 1, "<path>" },
103    { 'p', "port", "RPC port (Default: " TR_DEFAULT_RPC_PORT_STR ")", "p", 1, "<port>" },
104    { 't', "auth", "Require authentication", "t", 0, NULL },
105    { 'T', "no-auth", "Don't require authentication", "T", 0, NULL },
106    { 'u', "username", "Set username for authentication", "u", 1, "<username>" },
107    { 'v', "password", "Set password for authentication", "v", 1, "<password>" },
108    { 'V', "version", "Show version number and exit", "V", 0, NULL },
109    { 810, "log-error", "Show error messages", NULL, 0, NULL },
110    { 811, "log-info", "Show error and info messages", NULL, 0, NULL },
111    { 812, "log-debug", "Show error, info, and debug messages", NULL, 0, NULL },
112    { 'w', "download-dir", "Where to save downloaded data", "w", 1, "<path>" },
113    { 800, "paused", "Pause all torrents on startup", NULL, 0, NULL },
114    { 'o', "dht", "Enable distributed hash tables (DHT)", "o", 0, NULL },
115    { 'O', "no-dht", "Disable distributed hash tables (DHT)", "O", 0, NULL },
116    { 'y', "lpd", "Enable local peer discovery (LPD)", "y", 0, NULL },
117    { 'Y', "no-lpd", "Disable local peer discovery (LPD)", "Y", 0, NULL },
118    { 830, "utp", "Enable uTP for peer connections", NULL, 0, NULL },
119    { 831, "no-utp", "Disable uTP for peer connections", NULL, 0, NULL },
120    { 'P', "peerport", "Port for incoming peers (Default: " TR_DEFAULT_PEER_PORT_STR ")", "P", 1, "<port>" },
121    { 'm', "portmap", "Enable portmapping via NAT-PMP or UPnP", "m", 0, NULL },
122    { 'M', "no-portmap", "Disable portmapping", "M", 0, NULL },
123    { 'L', "peerlimit-global", "Maximum overall number of peers (Default: " TR_DEFAULT_PEER_LIMIT_GLOBAL_STR ")", "L", 1, "<limit>" },
124    { 'l', "peerlimit-torrent", "Maximum number of peers per torrent (Default: " TR_DEFAULT_PEER_LIMIT_TORRENT_STR ")", "l", 1, "<limit>" },
125    { 910, "encryption-required",  "Encrypt all peer connections", "er", 0, NULL },
126    { 911, "encryption-preferred", "Prefer encrypted peer connections", "ep", 0, NULL },
127    { 912, "encryption-tolerated", "Prefer unencrypted peer connections", "et", 0, NULL },
128    { 'i', "bind-address-ipv4", "Where to listen for peer connections", "i", 1, "<ipv4 addr>" },
129    { 'I', "bind-address-ipv6", "Where to listen for peer connections", "I", 1, "<ipv6 addr>" },
130    { 'r', "rpc-bind-address", "Where to listen for RPC connections", "r", 1, "<ipv4 addr>" },
131    { 953, "global-seedratio", "All torrents, unless overridden by a per-torrent setting, should seed until a specific ratio", "gsr", 1, "ratio" },
132    { 954, "no-global-seedratio", "All torrents, unless overridden by a per-torrent setting, should seed regardless of ratio", "GSR", 0, NULL },
133    { 'x', "pid-file", "Enable PID file", "x", 1, "<pid-file>" },
134    { 0, NULL, NULL, NULL, 0, NULL }
135};
136
137static void
138showUsage (void)
139{
140    tr_getopt_usage (MY_NAME, getUsage (), options);
141    exit (0);
142}
143
144static void
145gotsig (int sig)
146{
147    switch (sig)
148    {
149        case SIGHUP:
150        {
151            if (!mySession)
152            {
153                tr_logAddInfo ("Deferring reload until session is fully started.");
154                seenHUP = true;
155            }
156            else
157            {
158                tr_variant settings;
159                const char * configDir;
160
161                /* reopen the logfile to allow for log rotation */
162                if (logfileName) {
163                    logfile = freopen (logfileName, LOGFILE_MODE_STR, logfile);
164                    if (!logfile)
165                        fprintf (stderr, "Couldn't reopen \"%s\": %s\n", logfileName, tr_strerror (errno));
166                }
167
168                configDir = tr_sessionGetConfigDir (mySession);
169                tr_logAddInfo ("Reloading settings from \"%s\"", configDir);
170                tr_variantInitDict (&settings, 0);
171                tr_variantDictAddBool (&settings, TR_KEY_rpc_enabled, true);
172                tr_sessionLoadSettings (&settings, configDir, MY_NAME);
173                tr_sessionSet (mySession, &settings);
174                tr_variantFree (&settings);
175                tr_sessionReloadBlocklists (mySession);
176            }
177            break;
178        }
179
180        default:
181            tr_logAddError ("Unexpected signal (%d) in daemon, closing.", sig);
182            /* no break */
183
184        case SIGINT:
185        case SIGTERM:
186            closing = true;
187            break;
188    }
189}
190
191#if defined (WIN32)
192 #define USE_NO_DAEMON
193#elif !defined (HAVE_DAEMON) || defined (__UCLIBC__)
194 #define USE_TR_DAEMON
195#else
196 #define USE_OS_DAEMON
197#endif
198
199static int
200tr_daemon (int nochdir, int noclose)
201{
202#if defined (USE_OS_DAEMON)
203
204    return daemon (nochdir, noclose);
205
206#elif defined (USE_TR_DAEMON)
207
208    /* this is loosely based off of glibc's daemon () implementation
209     * http://sourceware.org/git/?p=glibc.git;a=blob_plain;f=misc/daemon.c */
210
211    switch (fork ()) {
212        case -1: return -1;
213        case 0: break;
214        default: _exit (0);
215    }
216
217    if (setsid () == -1)
218        return -1;
219
220    if (!nochdir)
221        chdir ("/");
222
223    if (!noclose) {
224        int fd = open ("/dev/null", O_RDWR, 0);
225        dup2 (fd, STDIN_FILENO);
226        dup2 (fd, STDOUT_FILENO);
227        dup2 (fd, STDERR_FILENO);
228        close (fd);
229    }
230
231    return 0;
232
233#else /* USE_NO_DAEMON */
234    return 0;
235#endif
236}
237
238static const char*
239getConfigDir (int argc, const char ** argv)
240{
241    int c;
242    const char * configDir = NULL;
243    const char * optarg;
244    const int ind = tr_optind;
245
246    while ((c = tr_getopt (getUsage (), argc, argv, options, &optarg))) {
247        if (c == 'g') {
248            configDir = optarg;
249            break;
250        }
251    }
252
253    tr_optind = ind;
254
255    if (configDir == NULL)
256        configDir = tr_getDefaultConfigDir (MY_NAME);
257
258    return configDir;
259}
260
261static void
262onFileAdded (tr_session * session, const char * dir, const char * file)
263{
264    char * filename = tr_buildPath (dir, file, NULL);
265    tr_ctor * ctor = tr_ctorNew (session);
266    int err = tr_ctorSetMetainfoFromFile (ctor, filename);
267
268    if (!err)
269    {
270        tr_torrentNew (ctor, &err, NULL);
271
272        if (err == TR_PARSE_ERR)
273            tr_logAddError ("Error parsing .torrent file \"%s\"", file);
274        else
275        {
276            bool trash = false;
277            int test = tr_ctorGetDeleteSource (ctor, &trash);
278
279            tr_logAddInfo ("Parsing .torrent file successful \"%s\"", file);
280
281            if (!test && trash)
282            {
283                tr_logAddInfo ("Deleting input .torrent file \"%s\"", file);
284                if (tr_remove (filename))
285                    tr_logAddError ("Error deleting .torrent file: %s", tr_strerror (errno));
286            }
287            else
288            {
289                char * new_filename = tr_strdup_printf ("%s.added", filename);
290                tr_rename (filename, new_filename);
291                tr_free (new_filename);
292            }
293        }
294    }
295
296    tr_ctorFree (ctor);
297    tr_free (filename);
298}
299
300static void
301printMessage (FILE * logfile, int level, const char * name, const char * message, const char * file, int line)
302{
303    if (logfile != NULL)
304    {
305        char timestr[64];
306        tr_logGetTimeStr (timestr, sizeof (timestr));
307        if (name)
308            fprintf (logfile, "[%s] %s %s (%s:%d)\n", timestr, name, message, file, line);
309        else
310            fprintf (logfile, "[%s] %s (%s:%d)\n", timestr, message, file, line);
311    }
312#ifdef HAVE_SYSLOG
313    else /* daemon... write to syslog */
314    {
315        int priority;
316
317        /* figure out the syslog priority */
318        switch (level) {
319            case TR_LOG_ERROR: priority = LOG_ERR; break;
320            case TR_LOG_DEBUG: priority = LOG_DEBUG; break;
321            default:           priority = LOG_INFO; break;
322        }
323
324        if (name)
325            syslog (priority, "%s %s (%s:%d)", name, message, file, line);
326        else
327            syslog (priority, "%s (%s:%d)", message, file, line);
328    }
329#endif
330}
331
332static void
333pumpLogMessages (FILE * logfile)
334{
335    const tr_log_message * l;
336    tr_log_message * list = tr_logGetQueue ();
337
338    for (l=list; l!=NULL; l=l->next)
339        printMessage (logfile, l->level, l->name, l->message, l->file, l->line);
340
341    if (logfile != NULL)
342        fflush (logfile);
343
344    tr_logFreeQueue (list);
345}
346
347static tr_rpc_callback_status
348on_rpc_callback (tr_session            * session UNUSED,
349                 tr_rpc_callback_type    type,
350                 struct tr_torrent     * tor UNUSED,
351                 void                  * user_data UNUSED)
352{
353    if (type == TR_RPC_SESSION_CLOSE)
354        closing = true;
355    return TR_RPC_OK;
356}
357
358int
359main (int argc, char ** argv)
360{
361    int c;
362    const char * optarg;
363    tr_variant settings;
364    bool boolVal;
365    bool loaded;
366    bool foreground = false;
367    bool dumpSettings = false;
368    const char * configDir = NULL;
369    const char * pid_filename;
370    dtr_watchdir * watchdir = NULL;
371    bool pidfile_created = false;
372    tr_session * session = NULL;
373
374    key_pidfile = tr_quark_new ("pidfile",  7);
375
376    signal (SIGINT, gotsig);
377    signal (SIGTERM, gotsig);
378#ifndef WIN32
379    signal (SIGHUP, gotsig);
380#endif
381
382    /* load settings from defaults + config file */
383    tr_variantInitDict (&settings, 0);
384    tr_variantDictAddBool (&settings, TR_KEY_rpc_enabled, true);
385    configDir = getConfigDir (argc, (const char**)argv);
386    loaded = tr_sessionLoadSettings (&settings, configDir, MY_NAME);
387
388    /* overwrite settings from the comamndline */
389    tr_optind = 1;
390    while ((c = tr_getopt (getUsage (), argc, (const char**)argv, options, &optarg))) {
391        switch (c) {
392            case 'a': tr_variantDictAddStr  (&settings, TR_KEY_rpc_whitelist, optarg);
393                      tr_variantDictAddBool (&settings, TR_KEY_rpc_whitelist_enabled, true);
394                      break;
395            case 'b': tr_variantDictAddBool (&settings, TR_KEY_blocklist_enabled, true);
396                      break;
397            case 'B': tr_variantDictAddBool (&settings, TR_KEY_blocklist_enabled, false);
398                      break;
399            case 'c': tr_variantDictAddStr  (&settings, TR_KEY_watch_dir, optarg);
400                      tr_variantDictAddBool (&settings, TR_KEY_watch_dir_enabled, true);
401                      break;
402            case 'C': tr_variantDictAddBool (&settings, TR_KEY_watch_dir_enabled, false);
403                      break;
404            case 941: tr_variantDictAddStr  (&settings, TR_KEY_incomplete_dir, optarg);
405                      tr_variantDictAddBool (&settings, TR_KEY_incomplete_dir_enabled, true);
406                      break;
407            case 942: tr_variantDictAddBool (&settings, TR_KEY_incomplete_dir_enabled, false);
408                      break;
409            case 'd': dumpSettings = true;
410                      break;
411            case 'e': logfile = fopen (optarg, LOGFILE_MODE_STR);
412                      if (logfile)
413                          logfileName = optarg;
414                      else
415                          fprintf (stderr, "Couldn't open \"%s\": %s\n", optarg, tr_strerror (errno));
416                      break;
417            case 'f': foreground = true;
418                      break;
419            case 'g': /* handled above */
420                      break;
421            case 'V': /* version */
422                      fprintf (stderr, "%s %s\n", MY_NAME, LONG_VERSION_STRING);
423                      exit (0);
424            case 'o': tr_variantDictAddBool (&settings, TR_KEY_dht_enabled, true);
425                      break;
426            case 'O': tr_variantDictAddBool (&settings, TR_KEY_dht_enabled, false);
427                      break;
428            case 'p': tr_variantDictAddInt (&settings, TR_KEY_rpc_port, atoi (optarg));
429                      break;
430            case 't': tr_variantDictAddBool (&settings, TR_KEY_rpc_authentication_required, true);
431                      break;
432            case 'T': tr_variantDictAddBool (&settings, TR_KEY_rpc_authentication_required, false);
433                      break;
434            case 'u': tr_variantDictAddStr (&settings, TR_KEY_rpc_username, optarg);
435                      break;
436            case 'v': tr_variantDictAddStr (&settings, TR_KEY_rpc_password, optarg);
437                      break;
438            case 'w': tr_variantDictAddStr (&settings, TR_KEY_download_dir, optarg);
439                      break;
440            case 'P': tr_variantDictAddInt (&settings, TR_KEY_peer_port, atoi (optarg));
441                      break;
442            case 'm': tr_variantDictAddBool (&settings, TR_KEY_port_forwarding_enabled, true);
443                      break;
444            case 'M': tr_variantDictAddBool (&settings, TR_KEY_port_forwarding_enabled, false);
445                      break;
446            case 'L': tr_variantDictAddInt (&settings, TR_KEY_peer_limit_global, atoi (optarg));
447                      break;
448            case 'l': tr_variantDictAddInt (&settings, TR_KEY_peer_limit_per_torrent, atoi (optarg));
449                      break;
450            case 800: paused = true;
451                      break;
452            case 910: tr_variantDictAddInt (&settings, TR_KEY_encryption, TR_ENCRYPTION_REQUIRED);
453                      break;
454            case 911: tr_variantDictAddInt (&settings, TR_KEY_encryption, TR_ENCRYPTION_PREFERRED);
455                      break;
456            case 912: tr_variantDictAddInt (&settings, TR_KEY_encryption, TR_CLEAR_PREFERRED);
457                      break;
458            case 'i': tr_variantDictAddStr (&settings, TR_KEY_bind_address_ipv4, optarg);
459                      break;
460            case 'I': tr_variantDictAddStr (&settings, TR_KEY_bind_address_ipv6, optarg);
461                      break;
462            case 'r': tr_variantDictAddStr (&settings, TR_KEY_rpc_bind_address, optarg);
463                      break;
464            case 953: tr_variantDictAddReal (&settings, TR_KEY_ratio_limit, atof (optarg));
465                      tr_variantDictAddBool (&settings, TR_KEY_ratio_limit_enabled, true);
466                      break;
467            case 954: tr_variantDictAddBool (&settings, TR_KEY_ratio_limit_enabled, false);
468                      break;
469            case 'x': tr_variantDictAddStr (&settings, key_pidfile, optarg);
470                      break;
471            case 'y': tr_variantDictAddBool (&settings, TR_KEY_lpd_enabled, true);
472                      break;
473            case 'Y': tr_variantDictAddBool (&settings, TR_KEY_lpd_enabled, false);
474                      break;
475            case 810: tr_variantDictAddInt (&settings,  TR_KEY_message_level, TR_LOG_ERROR);
476                      break;
477            case 811: tr_variantDictAddInt (&settings,  TR_KEY_message_level, TR_LOG_INFO);
478                      break;
479            case 812: tr_variantDictAddInt (&settings,  TR_KEY_message_level, TR_LOG_DEBUG);
480                      break;
481            case 830: tr_variantDictAddBool (&settings, TR_KEY_utp_enabled, true);
482                      break;
483            case 831: tr_variantDictAddBool (&settings, TR_KEY_utp_enabled, false);
484                      break;
485            default:  showUsage ();
486                      break;
487        }
488    }
489
490    if (foreground && !logfile)
491        logfile = stderr;
492
493    if (!loaded)
494    {
495        printMessage (logfile, TR_LOG_ERROR, MY_NAME, "Error loading config file -- exiting.", __FILE__, __LINE__);
496        return -1;
497    }
498
499    if (dumpSettings)
500    {
501        char * str = tr_variantToStr (&settings, TR_VARIANT_FMT_JSON, NULL);
502        fprintf (stderr, "%s", str);
503        tr_free (str);
504        return 0;
505    }
506
507    if (!foreground && tr_daemon (true, false) < 0)
508    {
509        char buf[256];
510        tr_snprintf (buf, sizeof (buf), "Failed to daemonize: %s", tr_strerror (errno));
511        printMessage (logfile, TR_LOG_ERROR, MY_NAME, buf, __FILE__, __LINE__);
512        exit (1);
513    }
514
515    sd_notifyf (0, "MAINPID=%d\n", (int)getpid()); 
516
517    /* start the session */
518    tr_formatter_mem_init (MEM_K, MEM_K_STR, MEM_M_STR, MEM_G_STR, MEM_T_STR);
519    tr_formatter_size_init (DISK_K, DISK_K_STR, DISK_M_STR, DISK_G_STR, DISK_T_STR);
520    tr_formatter_speed_init (SPEED_K, SPEED_K_STR, SPEED_M_STR, SPEED_G_STR, SPEED_T_STR);
521    session = tr_sessionInit ("daemon", configDir, true, &settings);
522    tr_sessionSetRPCCallback (session, on_rpc_callback, NULL);
523    tr_logAddNamedInfo (NULL, "Using settings from \"%s\"", configDir);
524    tr_sessionSaveSettings (session, configDir, &settings);
525
526    pid_filename = NULL;
527    tr_variantDictFindStr (&settings, key_pidfile, &pid_filename, NULL);
528    if (pid_filename && *pid_filename)
529    {
530        FILE * fp = fopen (pid_filename, "w+");
531        if (fp != NULL)
532        {
533            fprintf (fp, "%d", (int)getpid ());
534            fclose (fp);
535            tr_logAddInfo ("Saved pidfile \"%s\"", pid_filename);
536            pidfile_created = true;
537        }
538        else
539            tr_logAddError ("Unable to save pidfile \"%s\": %s", pid_filename, tr_strerror (errno));
540    }
541
542    if (tr_variantDictFindBool (&settings, TR_KEY_rpc_authentication_required, &boolVal) && boolVal)
543        tr_logAddNamedInfo (MY_NAME, "requiring authentication");
544
545    mySession = session;
546
547    /* If we got a SIGHUP during startup, process that now. */
548    if (seenHUP)
549        gotsig (SIGHUP);
550
551    /* maybe add a watchdir */
552    {
553        const char * dir;
554
555        if (tr_variantDictFindBool (&settings, TR_KEY_watch_dir_enabled, &boolVal)
556            && boolVal
557            && tr_variantDictFindStr (&settings, TR_KEY_watch_dir, &dir, NULL)
558            && dir
559            && *dir)
560        {
561            tr_logAddInfo ("Watching \"%s\" for new .torrent files", dir);
562            watchdir = dtr_watchdir_new (mySession, dir, onFileAdded);
563        }
564    }
565
566    /* load the torrents */
567    {
568        tr_torrent ** torrents;
569        tr_ctor * ctor = tr_ctorNew (mySession);
570        if (paused)
571            tr_ctorSetPaused (ctor, TR_FORCE, true);
572        torrents = tr_sessionLoadTorrents (mySession, ctor, NULL);
573        tr_free (torrents);
574        tr_ctorFree (ctor);
575    }
576
577#ifdef HAVE_SYSLOG
578    if (!foreground)
579        openlog (MY_NAME, LOG_CONS|LOG_PID, LOG_DAEMON);
580#endif
581
582  sd_notify( 0, "READY=1\n" ); 
583
584  while (!closing)
585    { 
586      double up;
587      double dn;
588
589      tr_wait_msec (1000); /* sleep one second */ 
590
591      dtr_watchdir_update (watchdir); 
592      pumpLogMessages (logfile); 
593
594      up = tr_sessionGetRawSpeed_KBps (mySession, TR_UP);
595      dn = tr_sessionGetRawSpeed_KBps (mySession, TR_DOWN);
596      if (up>0 || dn>0)
597        sd_notifyf (0, "STATUS=Uploading %.2f KBps, Downloading %.2f KBps.\n", up, dn); 
598      else
599        sd_notify (0, "STATUS=Idle.\n"); 
600    } 
601
602    sd_notify( 0, "STATUS=Closing transmission session...\n" );
603    printf ("Closing transmission session...");
604    tr_sessionSaveSettings (mySession, configDir, &settings);
605    dtr_watchdir_free (watchdir);
606    tr_sessionClose (mySession);
607    pumpLogMessages (logfile);
608    printf (" done.\n");
609
610    /* shutdown */
611#if HAVE_SYSLOG
612    if (!foreground)
613    {
614        syslog (LOG_INFO, "%s", "Closing session");
615        closelog ();
616    }
617#endif
618
619    /* cleanup */
620    if (pidfile_created)
621        tr_remove (pid_filename);
622    tr_variantFree (&settings);
623    sd_notify (0, "STATUS=\n");
624    return 0;
625}
Note: See TracBrowser for help on using the repository browser.