#include <errno.h>
#include <string.h> /* strerr */
#include <fcntl.h>
#include <stdbool.h>
#include <syslog.h>
#include <unistd.h>

#define PAM_SM_SESSION

#include <security/pam_modules.h>
#include <security/_pam_macros.h>
#include <security/pam_modutil.h>
#include <security/pam_ext.h>

char *
get_dconf_profile_path (void)
{
  return strdup ("/etc/dconf/profile/user");
}

PAM_EXTERN int
pam_sm_open_session (pam_handle_t  *pamh,
                     int            flags,
                     int            argc,
                     const char   **argv)
{
  const char *runtime_dir_path;
  int runtime_dir_fd;
  char *dconf_profile_path;
  bool success;

  runtime_dir_path = pam_getenv (pamh, "XDG_RUNTIME_DIR");

  if (runtime_dir_path == NULL)
    {
      pam_syslog(pamh, LOG_NOTICE, "XDG_RUNTIME_DIR has not been set yet.  Cannot set up dconf profile.");
      return PAM_IGNORE;
    }

  runtime_dir_fd = open (runtime_dir_path, O_RDONLY | O_DIRECTORY);

  if (runtime_dir_fd < 0)
    {
      int saved_errno = errno;

      pam_syslog(pamh, LOG_NOTICE, "XDG_RUNTIME_DIR set, but cannot open.  Cannot set up dconf profile.");
      pam_syslog(pamh, LOG_NOTICE, strerror (saved_errno));
      return PAM_IGNORE;
    }

  dconf_profile_path = get_dconf_profile_path ();

  success = (symlinkat (get_dconf_profile_path (), runtime_dir_fd, "dconf-profile") == 0) || (errno == EEXIST);

  if (!success)
    {
      int saved_errno = errno;

      pam_syslog(pamh, LOG_NOTICE, "failed to create symlink for dconf profile in XDG_RUNTIME_DIR");
      pam_syslog(pamh, LOG_NOTICE, strerror (saved_errno));
    }

  close (runtime_dir_fd);
  free (dconf_profile_path);

  return success ? PAM_SUCCESS : PAM_IGNORE;
}

PAM_EXTERN int
pam_sm_close_session (pam_handle_t  *pamh,
                      int            flags,
                      int            argc,
                      const char   **argv)
{
   return PAM_SUCCESS;
}
