From 6d5a02fd80775d3e434c5bab008a7595e226a242 Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Sun, 3 May 2015 11:22:48 +0200 Subject: [PATCH] Fixed race condition in Sessions.create(). --- lib/sessions.rb | 93 ++++++++++++++++++++++++++++--------------------- 1 file changed, 54 insertions(+), 39 deletions(-) diff --git a/lib/sessions.rb b/lib/sessions.rb index d95cbd1fb..c9176a501 100644 --- a/lib/sessions.rb +++ b/lib/sessions.rb @@ -10,8 +10,7 @@ module Sessions end # get working directories - @path = @root + '/tmp/websocket' - @pid = @root + '/tmp/pids/sessionworker.pid' + @path = "#{@root}/tmp/websocket" # create global vars for threads @@client_threads = {} @@ -29,18 +28,27 @@ returns =end def self.create( client_id, session, meta ) - path = @path + '/' + client_id.to_s - FileUtils.mkpath path + path = "#{@path}/#{client_id}" + path_tmp = "#{@path}/tmp/#{client_id}" + session_file = "#{path_tmp}/session" + + # collect session data meta[:last_ping] = Time.new.to_i.to_s data = { user: session, meta: meta, } - content = data.to_s - File.open( path + '/session', 'wb' ) { |file| + content = data.to_json + + # store session data in session file + FileUtils.mkpath path_tmp + File.open( session_file, 'wb' ) { |file| file.write content } + # move to destination directory + FileUtils.mv( path_tmp, path ) + # send update to browser if session && session['id'] self.send( @@ -66,7 +74,7 @@ returns =end def self.sessions - path = @path + '/' + path = "#{@path}/" # just make sure that spool path exists if !File::exist?( path ) @@ -75,7 +83,10 @@ returns data = [] Dir.foreach( path ) do |entry| - next if entry == '.' || entry == '..' || entry == 'spool' + next if entry == '.' + next if entry == '..' + next if entry == 'tmp' + next if entry == 'spool' data.push entry.to_s end data @@ -153,7 +164,7 @@ returns =end def self.destory( client_id ) - path = @path + '/' + client_id.to_s + path = "#{@path}/#{client_id}" FileUtils.rm_rf path end @@ -171,7 +182,7 @@ returns def self.destory_idle_sessions(idle_time_in_sec = 240) list_of_closed_sessions = [] - clients = Sessions.list + clients = Sessions.list clients.each { |client_id, client| if !client[:meta] || !client[:meta][:last_ping] || ( client[:meta][:last_ping].to_i + idle_time_in_sec ) < Time.now.to_i list_of_closed_sessions.push client_id @@ -196,10 +207,11 @@ returns def self.touch( client_id ) data = self.get(client_id) return false if !data - path = @path + '/' + client_id.to_s + path = "#{@path}/#{client_id}" data[:meta][:last_ping] = Time.new.to_i.to_s + content = data.to_json File.open( path + '/session', 'wb' ) { |file| - file.write data.to_json + file.write content } true end @@ -225,12 +237,12 @@ returns =end def self.get( client_id ) - session_dir = @path + '/' + client_id.to_s - session_file = session_dir + '/session' - data = nil + session_dir = "#{@path}/#{client_id}" + session_file = "#{session_dir}/session" + data = nil if !File.exist? session_file self.destory(client_id) - puts "ERROR: missing session file for '#{client_id.to_s}', remove session." + puts "ERROR: missing session file for '#{client_id}', remove session." return end begin @@ -266,14 +278,14 @@ returns =end def self.send( client_id, data ) - path = @path + '/' + client_id.to_s + '/' - filename = 'send-' + Time.new().to_f.to_s# + '-' + rand(99999999).to_s - check = true - count = 0 + path = "#{@path}/#{client_id}/" + filename = "send-#{ Time.new().to_f }" + check = true + count = 0 while check if File::exist?( path + filename ) count += 1 - filename = filename + '-' + count + filename = "#{filename}-#{count}" else check = false end @@ -361,15 +373,16 @@ returns =end def self.queue( client_id ) - path = @path + '/' + client_id.to_s + '/' - data = [] + path = "#{@path}/#{client_id}/" + data = [] files = [] Dir.foreach( path ) {|entry| - next if entry == '.' || entry == '..' + next if entry == '.' + next if entry == '..' files.push entry } files.sort.each {|entry| - filename = path + '/' + entry + filename = "#{path}/#{entry}" if /^send/.match( entry ) data.push Sessions.queue_file_read( path, entry ) end @@ -378,10 +391,9 @@ returns end def self.queue_file_read( path, filename ) - file_old = path + filename - file_new = path + 'a-' + filename + file_old = "#{path}#{filename}" + file_new = "#{path}a-#{filename}" FileUtils.mv( file_old, file_new ) - data = nil all = '' File.open( file_new, 'rb' ) { |file| all = file.read @@ -390,15 +402,17 @@ returns JSON.parse( all ) end - def self.spool_cleanup - path = @path + '/spool/' + def self.cleanup + path = "#{@path}/spool/" + FileUtils.rm_rf path + path = "#{@path}/tmp/" FileUtils.rm_rf path end def self.spool_create( msg ) - path = @path + '/spool/' + path = "#{@path}/spool/" FileUtils.mkpath path - file = Time.new.to_f.to_s + '-' + rand(99_999).to_s + file = "#{Time.new.to_f}-#{rand(99_999)}" File.open( path + '/' + file, 'wb' ) { |file| data = { msg: msg, @@ -409,20 +423,21 @@ returns end def self.spool_list( timestamp, current_user_id ) - path = @path + '/spool/' + path = "#{@path}/spool/" FileUtils.mkpath path - data = [] + data = [] to_delete = [] - files = [] + files = [] Dir.foreach( path ) {|entry| - next if entry == '.' || entry == '..' + next if entry == '.' + next if entry == '..' files.push entry } files.sort.each {|entry| - filename = path + '/' + entry + filename = "#{path}/#{entry}" next if !File::exist?( filename ) File.open( filename, 'rb' ) { |file| - all = file.read + all = file.read spool = JSON.parse( all ) begin message_parsed = JSON.parse( spool['msg'] ) @@ -433,7 +448,7 @@ returns # ignore message older then 48h if spool['timestamp'] + (2 * 86_400) < Time.now.to_i - to_delete.push path + '/' + entry + to_delete.push "#{path}/#{entry}" next end