#!/usr/bin/env ruby -w
# encoding: UTF-8
#
# = Daemon.rb -- The TaskJuggler III Project Management Software
#
# Copyright (c) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014
#               by Chris Schlaeger <cs@taskjuggler.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
#

require 'taskjuggler/MessageHandler'

class TaskJuggler

  # This class provides the basic functionality to turn the current process
  # into a background process (daemon). To use it, derive you main class from
  # this class and call the start() method.
  class Daemon

    include MessageHandler

    attr_accessor :pidFile, :daemonize

    def initialize
      # You can set this flag to false to prevent the program from
      # disconnecting from the current terminal. This is useful for debugging
      # purposes.
      @daemonize = true
      # Save the PID of the running daemon as number into this file.
      @pidFile = nil
    end

    # Call this method to turn the process into a background process.
    def start
      return 0 unless @daemonize

      # Fork and have the parent exit
      if (pid = fork) == -1
        fatal('first_fork_failed', 'First fork failed')
      elsif !pid.nil?
        # This is the parent. We can exit now.
        debug('', "Forked a child process with PID #{pid}")
        exit! 0
      end

      # Create a new session
      Process.setsid
      # Fork again to make sure we lose the controlling terminal
      if (pid = fork) == -1
        fatal('second_fork_failed', 'Second fork failed')
      elsif !pid.nil?
        # This is the parent. We can exit now.
        debug('', "Forked a child process with PID #{pid}")
        exit! 0
      end

      @pid = Process.pid

      writePidFile

      # Change current working directory to the file system root
      Dir.chdir '/'
      # Make sure we can create files with any permission
      File.umask 0

      # We no longer have a controlling terminal, so these are useless.
      $stdin.reopen('/dev/null')
      $stdout.reopen('/dev/null', 'a')
      $stderr.reopen($stdout)

      info('daemon_pid',
           "The process is running as daemon now with PID #{@pid}")

      0
    end

    # This method may provide some cleanup functionality in the future. You
    # better call it before you exit.
    def stop
      if @pidFile
        begin
          File.delete(@pidFile)
        rescue
          warning('cannot_delete_pidfile',
                  "Cannote delete the PID file (#{@pidFile}): #{$!}")
        end
        info('daemon_deleted_pidfile', "PID file #{@pidFile} deleted")
      end
    end

    private

    def writePidFile
      if @pidFile
        # Prepend the current working dir to @pidFile unless it's already an
        # absolute path. The working dir is changed to '/' later. We need the
        # absolute name to be able to delete it on exit again.
        if @pidFile[0] != '/'
          @pidFile = File.join(Dir.getwd, @pidFile)
        end

        # If requested, write the PID of the daemon to the specified file.
        begin
          File.open(@pidFile, 'w') do |f|
            f.puts @pid
          end
        rescue
          warning('cannot_save_pidfile', "Cannot write PID to #{@pidFile}")
        end
        info('daemon_wrote_pidfile',
             "PID file #{@pidFile} written with PID #{@pid}")
      end

    end

  end

end

