/*
 * Read in the file, and grant ownerships to whoever has the lock.
 */

#include "config.h"
#include <errno.h>
#include <pwd.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdarg.h>
#include <glob.h>
#include <locale.h>
#define STATIC static
#include "configfile.h"
#include "chmod.h"
#include "pam_console.h"

#include <security/_pam_macros.h>

#define CAST_ME_HARDER (const void**)
#define DEFAULT_PERMSFILE "/etc/security/console.perms"
#define PERMS_GLOB "/etc/security/console.perms.d/*.perms"

static const char consolelock[] = LOCKDIR "/" LOCKFILE;
static char consoleperms[PATH_MAX];
static char tty[PATH_MAX] = "tty0";
static int debug = 0;
static int syslogging = 0;

void
_pam_log(pam_handle_t *pamh, int err, int debug_p, const char *format, ...)
{
	va_list args;
	if (debug_p && !debug) return;
	va_start(args, format);
	if (syslogging) {
		vsyslog(err, format, args);
	}
	else {
		vfprintf(stderr, format, args);
		fprintf(stderr, "\n");
	}
	va_end(args);
}

static void
parse_files(void)
{
	int rc;
	glob_t globbuf;
	int i;
	const char *oldlocale;

	/* first we parse the console.perms file */
	parse_file(DEFAULT_PERMSFILE);

	/* set the LC_COLLATE so the sorting order doesn't depend
	on system locale */
	oldlocale = setlocale(LC_COLLATE, "C");

	rc = glob(PERMS_GLOB, GLOB_NOCHECK, NULL, &globbuf);
	setlocale(LC_COLLATE, oldlocale);
	if (rc)
		return;

	for (i = 0; globbuf.gl_pathv[i] != NULL; i++) {
		parse_file(globbuf.gl_pathv[i]);
	}
	globfree(&globbuf);
}

int
main(int argc, char **argv)
{
	int fd;
	int i, c;
	struct stat st;
	char *consoleuser = NULL;
	enum {Set, Reset} sense = Set;
	GSList *files = NULL;

	while((c = getopt(argc, argv, "c:f:t:rsd")) != -1) {
		switch(c) {
			case 'c': if (strlen(optarg) >= sizeof(consoleperms)) {
					fprintf(stderr, "Console.perms filename too long\n");
					exit(1);
				  }
				  strncpy(consoleperms, optarg, sizeof(consoleperms) - 1);
				  consoleperms[sizeof(consoleperms) - 1] = '\0';
				  break;
			case 'f': chmod_set_fstab(optarg);
				  break;
			case 't': if (strlen(optarg) >= sizeof(tty)) {
					fprintf(stderr, "TTY name too long\n");
					exit(1);
				  }
				  strncpy(tty, optarg, sizeof(tty) - 1);
				  tty[sizeof(tty) - 1] = '\0';
				  break;
			case 'r':
				  sense = Reset;
				  break;
			case 's': 
				  syslogging = TRUE;
				  break;
			case 'd': 
				  debug = TRUE;
				  break;
			default:
				  fprintf(stderr, "usage: %s [-f /etc/fstab] "
					  "[-c %s] [-t tty] [-r] [-s] [-d] [<device file> ...]\n", argv[0],
					  consoleperms);
				  exit(1);
		}
	}

	if (syslogging)
		openlog("pam_console_apply", LOG_CONS|LOG_PID, LOG_AUTH);

	for (i = argc-1; i >= optind;  i--) {
		files = g_slist_prepend(files, argv[i]);
        }

	if (*consoleperms == '\0')
		parse_files();
	else
		parse_file(consoleperms);
		
        if (sense != Reset && (fd=open(consolelock, O_RDONLY)) != -1) {
		if (fstat (fd, &st)) {
			_pam_log(NULL, LOG_ERR, FALSE,
			       "\"impossible\" fstat error on %s", consolelock);
			close(fd);
			goto return_error;
		}
		if (st.st_size) {
			consoleuser = _do_malloc(st.st_size+1);
			memset(consoleuser, '\0', st.st_size);
			if ((i = read (fd, consoleuser, st.st_size)) == -1) {
				_pam_log(NULL, LOG_ERR, FALSE,
				       "\"impossible\" read error on %s",
				       consolelock);
				goto return_error;
			}
			consoleuser[i] = '\0';
		}
		close(fd);
	} else {
		sense = Reset;
	}
	if((sense == Set) && (consoleuser != NULL)) {
		set_permissions(tty, consoleuser, files);
	}
	if(sense == Reset) {
		reset_permissions(tty, files);
	}
	return 0;

return_error:
	return 1;
}
