about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2013-09-01 02:06:51 +0000
committerEric Wong <normalperson@yhbt.net>2013-09-01 02:12:13 +0000
commit0208f3bfd132d58c31d5db06db6378277fa71c7b (patch)
tree8ed0c5b21b7bc12f7f7f891a6454a88014bdc29b
parent53b984b1e054a0804884ee83e2debe5baea8a222 (diff)
downloaddtas-0208f3bfd132d58c31d5db06db6378277fa71c7b.tar.gz
This helps in case dtas-player is hit with SIGKILL or the system
crashes.  This does not fsync(2) as that could introduce delays on
slow filesystems.  Users should open the file manually and fsync
themselves if they need to.
-rw-r--r--Documentation/dtas-player_protocol.txt8
-rw-r--r--lib/dtas/player.rb2
-rw-r--r--lib/dtas/player/client_handler.rb21
-rw-r--r--lib/dtas/state_file.rb2
-rw-r--r--test/test_player_integration.rb18
5 files changed, 51 insertions, 0 deletions
diff --git a/Documentation/dtas-player_protocol.txt b/Documentation/dtas-player_protocol.txt
index efbd065..b431180 100644
--- a/Documentation/dtas-player_protocol.txt
+++ b/Documentation/dtas-player_protocol.txt
@@ -203,6 +203,14 @@ Commands here should be alphabetized according to `LC_ALL=C sort'
         1. input type (flac/opus/mp3/etc)
         2. transport protocol (local FS/http/ftp/sftp/etc)
 
+* state dump [FILENAME]
+  Immediately dump the state of the player.  If a FILENAME is specified,
+  the state is written to that file.  Otherwise, the default state file
+  (configured via DTAS_PLAYER_STATE environment variable, defaulting
+  to ~/.dtas/player_state.yml) is written to.  This does not use fsync(2),
+  users requiring fsync should open(2) that file and fsync(2) it
+  themselves if necessary.
+
 * watch - adds the client to the passive watch list for notifications.
   It is recommended clients issue no further commands and open
   another client socket to issue non-watch commands.
diff --git a/lib/dtas/player.rb b/lib/dtas/player.rb
index ba42490..3571a7f 100644
--- a/lib/dtas/player.rb
+++ b/lib/dtas/player.rb
@@ -212,6 +212,8 @@ class DTAS::Player # :nodoc:
       io.emit("OK")
     when "source"
       source_handler(io, msg)
+    when "state"
+      state_file_handler(io, msg)
     when "cd"
       chdir_handler(io, msg)
     when "pwd"
diff --git a/lib/dtas/player/client_handler.rb b/lib/dtas/player/client_handler.rb
index f601d46..bbdc1c5 100644
--- a/lib/dtas/player/client_handler.rb
+++ b/lib/dtas/player/client_handler.rb
@@ -479,5 +479,26 @@ module DTAS::Player::ClientHandler # :nodoc:
     # echo(%W(cd msg[0])) # should we broadcast this?
     io.emit("OK")
   end
+
+  def state_file_handler(io, msg)
+    case msg.shift
+    when "dump"
+      dest = msg.shift
+      if dest
+        sf = DTAS::StateFile.new(dest, false)
+      elsif @state_file
+        sf = @state_file
+        dest = sf.path
+      else
+        return io.emit("ERR no state file configured")
+      end
+      begin
+        sf.dump(self)
+      rescue => e
+        return io.emit("ERR dumping to #{xs(Array(dest))} #{e.message}")
+      end
+    end
+    io.emit("OK")
+  end
 end
 # :startdoc:
diff --git a/lib/dtas/state_file.rb b/lib/dtas/state_file.rb
index c009797..0ba876f 100644
--- a/lib/dtas/state_file.rb
+++ b/lib/dtas/state_file.rb
@@ -3,6 +3,8 @@
 require 'yaml'
 require 'tempfile'
 class DTAS::StateFile # :nodoc:
+  attr_reader :path
+
   def initialize(path, do_fsync = false)
     @path = path
     @do_fsync = do_fsync
diff --git a/test/test_player_integration.rb b/test/test_player_integration.rb
index b60ed36..e7f7306 100644
--- a/test/test_player_integration.rb
+++ b/test/test_player_integration.rb
@@ -188,6 +188,24 @@ class TestPlayerIntegration < Minitest::Unit::TestCase
     assert_equal "/", s.req("pwd")
   end
 
+  def test_state_file
+    state = Tempfile.new(%w(state .yml))
+    state_path = state.path
+    state.close!
+    s = client_socket
+    s.req_ok(%W(state dump #{state_path}))
+    hash = YAML.load(IO.binread(state_path))
+    assert_equal @sock_path, hash["socket"]
+    assert_equal "default", hash["sinks"][0]["name"]
+
+    assert_equal "", IO.binread(@state_tmp.path)
+    s.req_ok(%W(state dump))
+    orig = YAML.load(IO.binread(@state_tmp.path))
+    assert_equal orig, hash
+  ensure
+    File.unlink(state_path)
+  end
+
   def test_source_ed
     s = client_socket
     assert_equal "sox av ff", s.req("source ls")