// -*- indent-tabs-mode: nil -*-

#ifndef __ARC_USER_H__
#define __ARC_USER_H__

#include <string>
#include <arc/Thread.h>

struct passwd;

namespace Arc {

  /// Platform independent representation of system user
  /** \ingroup common
   *  \headerfile User.h arc/User.h */
  class User {
  private:
    // local name, home directory, uid and gid of this user
    std::string name;
    std::string home;
    int uid;
    int gid;
    bool valid;
    bool set(const struct passwd*);

  public:
    /// Construct user from current process owner.
    User();
    /// Construct user from username and optional group name.
    /** If group is not specified it is determined automatically. */
    User(const std::string& name, const std::string& group="");
    /// Construct user from uid and optional gid.
    /** If gid is not specified it is determined automatically. */
    User(int uid, int gid=-1);
    /// Returns true if this is a valid user.
    operator bool() const {
      return valid;
    }
    /// Returns true is this is not a valid user.
    bool operator !() const {
      return !valid;
    }
    /// Returns the name of this user.
    const std::string& Name(void) const {
      return name;
    }
    /// Returns the path to the user's home directory.
    const std::string& Home(void) const {
      return home;
    }
    /// Returns the user's uid.
    int get_uid(void) const {
      return (int)uid;
    }
    /// Returns the user's gid.
    int get_gid(void) const {
      return (int)gid;
    }
    /// Returns true if this User's name is the same as n.
    bool operator==(const std::string& n) {
      return (n == name);
    }
    /// Check if this User has the rights specified by flags on the given path.
    /** \return 0 if User has the rights */
    int check_file_access(const std::string& path, int flags) const;
    /// Change the owner of the current process.
    /** Internally this method calls setuid() and setgid() with this User's
       values. It can be used in the initializer of Arc::Run to switch the
       owner of a child process just after fork(). To temporarily change the
       owner of a thread in a multi-threaded environment UserSwitch should be
       used instead.
       \return true if switch succeeded. */
    bool SwitchUser() const;
  }; // class User

  /// Class for temporary switching of user id.
  /** If this class is created, the user identity is switched to the provided
     uid and gid. Due to an internal lock there will be only one valid
     instance of this class. Any attempt to create another instance 
     will block until the first one is destroyed.
      If uid and gid are set to 0 then the user identity is not switched,
     but the lock is applied anyway.
      The lock has a dual purpose. The first and most important is to
     protect communication with the underlying operating system which may
     depend on user identity. For that it is advisable for code which
     talks to the operating system to acquire a valid instance of this class.
     Care must be taken not to hold that instance too long as
     that may block other code in a multithreaded environment.
      The other purpose of this lock is to provide a workaround for a glibc bug
     in __nptl_setxid. This bug causes lockup of seteuid() function
     if racing with fork. To avoid this problem the lock mentioned above
     is used by the Run class while spawning a new process.
     \ingroup common
     \headerfile User.h arc/User.h  */
  class UserSwitch {
  private:
    static SimpleCondition suid_lock;
    static int suid_count;
    static int suid_uid_orig;
    static int suid_gid_orig;
    bool valid;
  public:
    /// Switch uid and gid.
    UserSwitch(int uid,int gid);
    /// Switch back to old uid and gid and release lock on this class.
    ~UserSwitch(void);
    /// Returns true if switching user succeeded.
    operator bool(void) { return valid; };
    /// This method to be called after fork in child 
    /// process to avoid deadlock on global condition.
    void resetPostFork(void);
  }; // class UserSwitch


} // namespace Arc

#endif
