#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <pwd.h>
#include <time.h>
#include <utmp.h>
#include <errno.h>
#include <paths.h>
#include <lastlog.h>
#include <sys/types.h>
#include "wrapper.h"

static int dowrite(int fd, const void *buf, size_t len)
{
	ssize_t ret;
	while (len>0)
	{
		ret=write(fd, buf, len);
		if (ret==-1) { if (errno==EINTR) continue; else break; }
		len-=ret;
		(const char *)buf+=ret;
	}
	return len?1:0;
}

static void make_utmp(struct utmp *u, const char *line, const char *host, 
	const char *name, time_t time, int add)
{
	int i;
	memset(u, 0, sizeof(*u));
	i = strlen(line);
	if (i >= sizeof(u->ut_id)) i-=sizeof(u->ut_id); else i=0;
	strncpy (u->ut_id, line+i, sizeof(u->ut_id));
	strncpy (u->ut_line, line, sizeof(u->ut_line));
	if (add) strncpy(u->ut_name, name, sizeof(u->ut_name));
	if (add) strncpy(u->ut_host, host, sizeof(u->ut_host));
	u->ut_pid = add?getppid():0;
	u->ut_type = add?USER_PROCESS:DEAD_PROCESS;
	u->ut_time = time;
}

static void utmp(struct utmp *u)
{
	setutent();
	pututline(u);
	endutent();
}

static int wtmp(struct utmp *u, int silent)
{
	int wtmp, ret;
	if ((wtmp=open(WTMP_FILE, O_WRONLY|O_APPEND))==-1)
	{
		if (errno==ENOENT) return 0;
		if (!silent) perror("open wtmp");
		return 1;
	}
	ret=dowrite(wtmp, u, sizeof(*u));
	ret|=close(wtmp);
	if (ret && !silent) perror("write wtmp");
	return ret;
}

static void make_llog(struct lastlog *l, const char *line, const char *host, 
	time_t time)
{
	memset(l, 0, sizeof(*l));
	l->ll_time=time;
	strncpy(l->ll_line, line, sizeof(l->ll_line));
	strncpy(l->ll_host, host, sizeof(l->ll_host));
}

static int llog(struct lastlog *l, uid_t uid, int silent)
{
	int llog, ret;

	if ((llog=open(_PATH_LASTLOG, O_WRONLY))==-1)
	{
		if (errno==ENOENT) return 0;
		if (!silent) perror("open lastlog");
		return 1;
	}
	ret=(lseek(llog, ((long)uid)*sizeof(*l), SEEK_SET)==-1);
	if (!ret) ret|=dowrite(llog, l, sizeof(*l));
	ret|=close(llog);
	if (ret && !silent) perror("write lastlog");
	return ret;
}

int sessreg(const struct passwd *pwd, const char *line, const char *host, 
	int noutmp, int add, int silent)
{
	time_t current_time;
	struct utmp entry;
	struct lastlog last;
	int ret;

	current_time = time(NULL);

	make_utmp(&entry, line, host, pwd->pw_name, current_time, add);
	make_llog(&last, line, host, current_time);
	
	if (!noutmp) utmp(&entry);
	ret=wtmp(&entry, silent);
	if (!ret && add) ret=llog(&last, pwd->pw_uid, silent);
	return ret;
}

