transmission / transmission

Official Transmission BitTorrent client repository
https://transmissionbt.com
Other
11.76k stars 1.19k forks source link

Move runtime and state data out of config directory #6769

Open ManDay opened 3 months ago

ManDay commented 3 months ago

What is the issue?

Currently, transmission stores state data such as the resume or torrents files in $XDG_CONFIG_HOME together with the configuration. It would be better if transmission placed these files in configurable places, defaulting to $XDG_STATE_HOME by XDG. It's a not a big bother but unfortunate in enough cases where you may want to separate (possibly sensitive) runtime data from configuration, including backups. Could this be changed?

Which application of Transmission?

transmission-daemon

Which version of Transmission?

No response

killemov commented 3 months ago

Currently, transmission stores state data such as the resume or torrents files in $XDG_CONFIG_HOME together with the configuration.

Would it be more accurate to state that you or your distro have configured Transmission to store that data in $XDG_CONFIG_HOME?

So reconfigure it to store torrents and resume-files in one place outside of $XDG_CONFIG_HOME and use a symbolic link to the settings.json inside $XDG_CONFIG_HOME. I did something similar with the actual configuration in /etc/transmission-daemon.

ManDay commented 3 months ago

Would it be more accurate to state that you or your distro have configured Transmission to store that data in $XDG_CONFIG_HOME?

I don't think so. The documentation seems to be clear about the fact that everything (see Files) is stored in a single folder. While that folder can be moved or chosen, the fact that configuration and state lives together in it seems to be unconfigurable behaviour.

qu1ck commented 3 months ago

Well, symlinks are a thing. Move your folder to where you want state to be and then symlink your config from $XDG_CONFIG_HOME into it. Debian package stores config in /etc and data in /var/lib for example.

/var/lib/transmission-daemon/info$ ls -la
total 220
drwxr-xr-x 5 quick quick  4096 Apr 14 23:25 .
drwxr-xr-x 3 quick quick  4096 Aug  8  2022 ..
-rw------- 1 quick quick     2 Feb  4 15:25 bandwidth-groups.json
drwxr-xr-x 2 quick quick  4096 Jul  6  2019 blocklists
-rw------- 1 quick quick   664 Feb  3 20:44 dht.dat
drwxr-xr-x 2 quick quick 94208 Apr 14 23:25 resume
lrwxrwxrwx 1 quick quick    38 Jul  6  2019 settings.json -> /etc/transmission-daemon/settings.json
-rw------- 1 quick quick   168 Apr 14 23:25 stats.json
drwxr-xr-x 2 quick quick 98304 Apr 14 09:20 torrents
ManDay commented 3 months ago

Symlinks are a way to work around the issue, yes (and thank you for the suggestion). I would still like to see transmission adhere to the XDG standard and leaving the directories configurable, so that one doesn't have to tinker with symlinks. Compensating for a lack of separation or configurability of a software data store is not the purpose of symlinks and using them in that manner still has downsides.

qu1ck commented 3 months ago

I agree that it makes sense to have a separate location for config. However XDG standard would not be right thing to appeal to here because it is for desktop apps and transmission daemon isn't one.

ManDay commented 3 months ago

I agree but since I don't know of any other standard which is even remotely-as-established as XDG for these matters, what's to say that transmission shouldn't use it; it's already using .config for that matter.

stephenboston commented 2 months ago

There is no respectable standard that supports storing configuration and state in the same location. We might want to version config but we don't want to version state.

But never mind. The symlink workaround works okay. It's likely that achieving backwards compatibility for the change is a nightmare and risks destabilizing the app through the release.

ManDay commented 2 months ago

The symlink workaround is quite tedious, if done correctly. Just recently, I wrote a script for firefox, which has the same problem, just a dozen times worse, to do that. And in the given form it's probably still missing a handful of cases.

It would be much more sustainable to -- instead of leaving it to each user to "hack around" the issue -- to do the change in transmission-daemon, bringing it to par with other software with state and config, and then write a migration/wrapper script for those who need to rely on the deprecated behaviour. Lest transmission becomes like firefox in that regard.

#!/bin/bash

run_dir="$XDG_RUNTIME_DIR/browser"
config_dir="$XDG_CONFIG_HOME/browser"
cache_dir="$XDG_CACHE_HOME/browser"

# Classes:
# Config: Always persistent (into config_dir)
# Cache: Deleteable after program termination, losing expected functionality (into cache_dir)
# State: Deletable after program termination (into run_dir)

# Will create a profile directory and symlink resources from different sources into place before binary execution
# Will replace config and cache resources by symlinks to their moved versions after binary termination

config_files=(
 "places.sqlite"
 "prefs.js"
 "addons.json"
 "content-prefs.sqlite"
 "logins.json"
 "permissions.sqlite"
 "storage.sqlite"
 "favicons.sqlite"
 "storage/default"
 "storage/permanent"
 "extensions.json"
 "extension-settings.json"
 "extensions"
 "cookies.sqlite"
 "chrome/userContent.css"
)

cache_files=(
)

# state_files=<everything else>

###

assert_dir( ) {
 if ! mkdir -p "$1"
 then
  echo "$1 is not a directory" >&2
 fi
}

symlink_all( ) {
 local d="$1"
 shift 1

 for t in "$@"
 do
  if [[ -e "$d/$t" ]]
  then
   mkdir -p "$(dirname "$t")"
   echo "Symlinking '$t' from '$d'" >&2
   ln -s "$d/$t" "$t"
  else
   echo "'$t' not yet in '$d'" >&2
   echo "$t"
  fi
 done
}

move_all( ) {
 local d="$1"
 shift 1

 for t in "$@"
 do
  if [[ -f "$t" ]]
  then
   mkdir -p "${d}/$(dirname "$t")"
   echo "Moving '$t' to '$d'" >&2
   mv "$t" "$d/$t"
   ln -s "$d/$t" "$t"
  else
   echo "'$t' not to be moved" >&2
  fi
 done
}

if ! [[ -d "$run_dir" ]]
then
 assert_dir "$run_dir"
 cd "$run_dir"

 mapfile -t missing_config < <(symlink_all "${config_dir}" "${config_files[@]}") 
 mapfile -t missing_cache < <(symlink_all "${cache_dir}" "${cache_files[@]}")
else
 missing_config=("${config_files[@]}")
 missing_cache=("${missing_cache[@]}")
fi

export HOME="$run_dir"
firefox --profile "$run_dir" "$@"

move_all "${config_dir}" "${missing_config[@]}"
move_all "${cache_dir}" "${missing_cache[@]}"
stephenboston commented 2 months ago

For back compatibility perhaps all we need to do is set the default directories through new entries in settings.json. Users who want torrents and resume etc in other directories can set them through those entries.