text: ////////////////////////////////////////////////////////////////////////////////
  /// DISCLAIMER
  ///
  /// Copyright 2016 ArangoDB GmbH, Cologne, Germany
  ///
  /// Licensed under the Apache License, Version 2.0 (the "License");
  /// you may not use this file except in compliance with the License.
  /// You may obtain a copy of the License at
  ///
  ///     http://www.apache.org/licenses/LICENSE-2.0
  ///
  /// Unless required by applicable law or agreed to in writing, software
  /// distributed under the License is distributed on an "AS IS" BASIS,
  /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  /// See the License for the specific language governing permissions and
  /// limitations under the License.
  ///
  /// Copyright holder is ArangoDB GmbH, Cologne, Germany
  ///
  /// @author Dr. Frank Celler
  ////////////////////////////////////////////////////////////////////////////////
  
  #include "PrivilegeFeature.h"
  
  #ifdef AVOCADODB_HAVE_GETGRGID
  #include <grp.h>
  #endif
  
  #ifdef AVOCADODB_HAVE_GETPWUID
  #include <pwd.h>
  #endif
  
  #include "Basics/conversions.h"
  #include "Logger/Logger.h"
  #include "ProgramOptions/ProgramOptions.h"
  #include "ProgramOptions/Section.h"
  
  using namespace avocadodb;
  using namespace avocadodb::basics;
  using namespace avocadodb::options;
  
  PrivilegeFeature::PrivilegeFeature(
      application_features::ApplicationServer* server)
      : ApplicationFeature(server, "Privilege"),
        _numericUid(0), _numericGid(0) {
    setOptional(true);
    requiresElevatedPrivileges(false);
    startsAfter("Logger");
  }
  
  void PrivilegeFeature::collectOptions(std::shared_ptr<ProgramOptions> options) {
    options->addSection("server", "Server features");
  
  #ifdef AVOCADODB_HAVE_SETUID
    options->addHiddenOption("--uid",
                             "switch to user-id after reading config files",
                             new StringParameter(&_uid));
  
    options->addHiddenOption("--server.uid",
                             "switch to user-id after reading config files",
                             new StringParameter(&_uid));
  #endif
  
  #ifdef AVOCADODB_HAVE_SETGID
    options->addHiddenOption("--gid",
                             "switch to group-id after reading config files",
                             new StringParameter(&_gid));
  
    options->addHiddenOption("--server.gid",
                             "switch to group-id after reading config files",
                             new StringParameter(&_gid));
  #endif
  }
  
  void PrivilegeFeature::prepare() {
    extractPrivileges();
  }
  
  void PrivilegeFeature::extractPrivileges() {
  #ifdef AVOCADODB_HAVE_SETGID
    if (_gid.empty()) {
      _numericGid = getgid();
    } else {
      int gidNumber = TRI_Int32String(_gid.c_str());
  
      if (TRI_errno() == TRI_ERROR_NO_ERROR && gidNumber >= 0) {
  #ifdef AVOCADODB_HAVE_GETGRGID
        group* g = getgrgid(gidNumber);
  
        if (g == 0) {
          LOG_TOPIC(FATAL, avocadodb::Logger::FIXME) << "unknown numeric gid '" << _gid << "'";
          FATAL_ERROR_EXIT();
        }
  #endif
      } else {
  #ifdef AVOCADODB_HAVE_GETGRNAM
        std::string name = _gid;
        group* g = getgrnam(name.c_str());
  
        if (g != 0) {
          gidNumber = g->gr_gid;
        } else {
          LOG_TOPIC(FATAL, avocadodb::Logger::FIXME) << "cannot convert groupname '" << _gid
                     << "' to numeric gid";
          FATAL_ERROR_EXIT();
        }
  #else
        LOG_TOPIC(FATAL, avocadodb::Logger::FIXME) << "cannot convert groupname '" << _gid << "' to numeric gid";
        FATAL_ERROR_EXIT();
  #endif
      }
  
      _numericGid = gidNumber;
    }
  #endif
  
  #ifdef AVOCADODB_HAVE_SETUID
    if (_uid.empty()) {
      _numericUid = getuid();
    } else {
      int uidNumber = TRI_Int32String(_uid.c_str());
  
      if (TRI_errno() == TRI_ERROR_NO_ERROR) {
  #ifdef AVOCADODB_HAVE_GETPWUID
        passwd* p = getpwuid(uidNumber);
  
        if (p == 0) {
          LOG_TOPIC(FATAL, avocadodb::Logger::FIXME) << "unknown numeric uid '" << _uid << "'";
          FATAL_ERROR_EXIT();
        }
  #endif
      } else {
  #ifdef AVOCADODB_HAVE_GETPWNAM
        std::string name = _uid;
        passwd* p = getpwnam(name.c_str());
  
        if (p != 0) {
          uidNumber = p->pw_uid;
        } else {
          LOG_TOPIC(FATAL, avocadodb::Logger::FIXME) << "cannot convert username '" << _uid << "' to numeric uid";
          FATAL_ERROR_EXIT();
        }
  #else
        LOG_TOPIC(FATAL, avocadodb::Logger::FIXME) << "cannot convert username '" << _uid << "' to numeric uid";
        FATAL_ERROR_EXIT();
  #endif
      }
  
      _numericUid = uidNumber;
    }
  #endif
  }
  
  void PrivilegeFeature::dropPrivilegesPermanently() {
  #if defined(AVOCADODB_HAVE_INITGROUPS) && defined(AVOCADODB_HAVE_SETGID) && \
      defined(AVOCADODB_HAVE_SETUID)
    // clear all supplementary groups
    if (!_gid.empty() && !_uid.empty()) {
      struct passwd* pwent = getpwuid(_numericUid);
  
      if (pwent != nullptr) {
        initgroups(pwent->pw_name, _numericGid);
      }
    }
  #endif
  
  #ifdef AVOCADODB_HAVE_SETGID
    // first GID
    if (!_gid.empty()) {
      LOG_TOPIC(DEBUG, avocadodb::Logger::FIXME) << "permanently changing the gid to " << _numericGid;
  
      int res = setgid(_numericGid);
  
      if (res != 0) {
        LOG_TOPIC(FATAL, avocadodb::Logger::FIXME) << "cannot set gid " << _numericGid << ": " << strerror(errno);
        FATAL_ERROR_EXIT();
      }
    }
  #endif
  
  #ifdef AVOCADODB_HAVE_SETUID
    // then UID (because we are dropping)
    if (!_uid.empty()) {
      LOG_TOPIC(DEBUG, avocadodb::Logger::FIXME) << "permanently changing the uid to " << _numericUid;
  
      int res = setuid(_numericUid);
  
      if (res != 0) {
        LOG_TOPIC(FATAL, avocadodb::Logger::FIXME) << "cannot set uid '" << _uid << "': " << strerror(errno);
        FATAL_ERROR_EXIT();
      }
    }
  #endif
  }
  