/*
 * Copyright (c) 1994, 1995, 1996, 1998 by Sun Microsystems, Inc.
 * All Rights Reserved
 */

#pragma ident	"@(#)misc.c	1.12	99/08/23 SMI"

/*LINTLIBRARY*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <pwd.h>
#include <sys/mman.h>
#include <syslog.h>
#include <errno.h>

#include <print/misc.h>
#include <print/job.h>
#include <print/list.h>


char *
long_date()
{
	static char longDate[64];
	time_t	curr;

	(void) time(&curr);
	(void) cftime(longDate, "%b %d %R %Y", &curr);
	return (longDate);
}

char *
short_date()
{
	static char shortDate[64];
	time_t	curr;

	(void) time(&curr);
	(void) cftime(shortDate, "%b %d %R", &curr);
	return (shortDate);
}

/*
 *	info about spool directory that we validate and fix
 */
#define	ROOT_UID	0
#define	LP_GID		8
#define	SPOOL_MODE	(S_IFDIR|S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)

/*ARGSUSED*/
int
check_client_spool(char *printer)
{
	char *dir = SPOOL_DIR;
	struct stat st;

	if ((stat(dir, &st) < 0) && (errno == ENOENT)) {
		syslog(LOG_ERR, "no spool dir, creating %s", dir);
		if (mkdir(dir, 0755) < 0) {
			syslog(LOG_ERR, "mkdir(%s): %m", dir);
			return (-1);
		}
		if (chown(dir, ROOT_UID, LP_GID) < 0) {
			syslog(LOG_ERR, "chown(%s): %m", dir);
			return (-1);
		}
		return (0);
	}
	if ((st.st_uid != ROOT_UID) || (st.st_gid != LP_GID)) {
		syslog(LOG_ERR,
			"correcting spool directory owner/group (was %d/%d)",
			st.st_uid, st.st_gid);
		if (chown(dir, ROOT_UID, LP_GID) < 0) {
			syslog(LOG_ERR, "chown(%s): %m", dir);
			return (-1);
		}
	}
	if (st.st_mode != (S_IFDIR | SPOOL_MODE)) {
		syslog(LOG_ERR,
			"spool dir (%s), incorrect permission (%0), correcting",
			dir, st.st_mode);
		if (chmod(dir, 0755) < 0) {
			syslog(LOG_ERR, "chmod(%s): %m", dir);
			return (-1);
		}
	}
	return (0);
}

int
get_lock(char *name, int write_pid)
{
	int	fd;

	syslog(LOG_DEBUG, "get_lock(%s, %d)", ((name != NULL) ? name : "NULL"),
		write_pid);
	if ((fd = open(name, O_RDWR|O_CREAT, 0640)) < 0)
		return (fd);

	if (lockf(fd, F_TLOCK, 0) < 0) {
		close(fd);
		return (-1);
	}

	if (write_pid != 0) {
		char	pid[16];

		if (ftruncate(fd, 0) < 0) {
			close(fd);
			return (-1);
		}
		if (snprintf(pid, sizeof (pid), "%d\n", (int)getpid())
			>= sizeof (pid)) {
			syslog(LOG_ERR, "get_lock: pid buffer overflow");
			return (-1);
		}
		write(fd, pid, strlen(pid));
	}
	(void) fsync(fd);

	syslog(LOG_DEBUG, "get_lock(%s, %d) - have lock",
		((name != NULL) ? name : "NULL"), write_pid);
	return (fd);
}

uid_t
get_user_id(char *name)
{
	struct passwd *p = NULL;

	if (name == NULL)
		return (-1);
	if ((p = getpwnam(name)) != NULL)
		return (p->pw_uid);
	else if ((p = getpwnam("nobody")) != NULL)
		return (p->pw_uid);
	else
		return (-2);
}


/*
 *  get_user_name()
 */
char *
get_user_name()
{
	struct passwd *p = NULL;

	if ((p = getpwuid(getuid())) != NULL)
		return (strdup(p->pw_name));
	else
		return (strdup("unknown"));
}



/*
 *  strcdup() - duplicate a string up to the first occurence of a character
 */
char *
strcdup(char *p, char c)
{
	char	*q,
		*r;

	if (p == NULL)
		return (NULL);
	if ((c == NULL) || ((q = strchr(p, c)) == NULL))
		return (strdup(p));

	r = (char *)malloc((q - p) + 1);
	(void) memset(r, NULL, (q - p) + 1);
	(void) strncpy(r, p, q - p);
	return (r);
}


/*
 *	Should be obvious
 */
char *
strndup(char *s, int l)
{
	char *t;

	if ((s == NULL) || (l < 1))
		return (NULL);

	t = malloc(l+1);
	(void) strncpy(t, s, l);
	return (t);
}


/*
 *  file_size() - need I say more
 */
int
file_size(char *path)
{
	struct stat st;

	if (stat(path, &st) < 0)
		return (-1);
	else
		return (st.st_size);
}


/*
 *  copy_file() - need I say more
 */
int
copy_file(char *src, char *dst)
{
	char	*buf;
	int	size;

	syslog(LOG_DEBUG, "copy_file(%s, %s)", ((src != NULL) ? src : "NULL"),
		((dst != NULL) ? dst : "NULL"));

	if ((src == NULL) || (dst == NULL))
		return (-1);

	if ((size = map_in_file(src, &buf)) < 0)
		return (-1);
	if (write_buffer(dst, buf, size) < 0) {
		(void) munmap(buf, size);
		return (-1);
	}

	(void) munmap(buf, size);
	return (0);
}

int
backup_file(char *name)
{
	char buf[BUFSIZ];

	if (snprintf(buf, sizeof (buf), "%s-", name) >= sizeof (buf)) {
		syslog(LOG_ERR, "libprint:backup_file: buffer overrun");
		return (-1);
	}
	return (copy_file(name, buf));
}

/*
 *  map_in_file() - mmaps in a file into a buffer *buf.  returns the size of
 *	the mmapped buffer.
 */
int
map_in_file(const char *file, char **buf)
{
	struct stat st;
	int	fd;

	syslog(LOG_DEBUG, "map_in_file(%s)", (file ? file : "NULL"));

	if (buf == NULL)
		return (-1);

	if ((fd = open(file, O_RDONLY)) < 0)
		return (-1);

	if (fstat(fd, &st) < 0) {
		close(fd);
		return (-1);
	}

	if (st.st_size == 0) {
		close(fd);
		*buf = NULL;
		return (0);
	}

	if ((*buf = mmap((caddr_t)0, (size_t)st.st_size, PROT_READ,
			(MAP_PRIVATE | MAP_NORESERVE),
			fd, (off_t)0)) == MAP_FAILED) {
		syslog(LOG_ERR, "map_in_file(%s) - mmap:%m",
			(file ? file : "NULL"));
		close(fd);
		return (-1);
	}
	close(fd);

	syslog(LOG_DEBUG, "map_in_file(%s) - size(%d), addr(0x%x)",
		(file ? file : "NULL"), st.st_size, *buf);
	return (st.st_size);
}


/*
 *  write_buffer() - writes a buffer in memory out to the file name passed in.
 *	uses mmap and ftruncate to do this.
 */
int
write_buffer(char *file, char *buf, int len)
{
	int	fd;
	char	*tmp;

	syslog(LOG_DEBUG, "write_buffer(%s, 0x%x, %d)", (file ? file : "NULL"),
		buf, len);

	if ((fd = open(file, O_CREAT|O_TRUNC|O_RDWR, 0640)) < 0)
		return (-1);
	if (ftruncate(fd, len) < 0) {
		close(fd);
		return (-1);
	}
	if ((tmp = mmap((caddr_t)0, (size_t)len, PROT_READ| PROT_WRITE,
			(MAP_SHARED | MAP_NORESERVE),
			fd, (off_t)0)) == MAP_FAILED) {
		syslog(LOG_ERR, "write_buffer(%s, 0x%x, %d) - mmap:%m",
			(file ? file : "NULL"), buf, len);
		close(fd);
		return (-1);
	}
	close(fd);

	(void) memcpy(tmp, buf, len);
	(void) munmap(tmp, len);

	syslog(LOG_DEBUG, "write_buffer(%s, 0x%x, %d) - ok",
		(file ? file : "NULL"), buf, len);

	return (0);
}


/*
 *  start_daemon() - check for jobs queued, check if the lock is free.  If
 *	so, start a daemon either by forking and execing or just execing
 *	depending on the flag passed in.
 */
void
start_daemon(int do_fork)
{
	int	lock;
	job_t	**jobs = NULL;

	if ((jobs = job_list_append(NULL, NULL, NULL, SPOOL_DIR)) == NULL)
		return;

	list_iterate((void **)jobs, (VFUNC_T)job_free);
	free(jobs);

	close(lock = get_lock(MASTER_LOCK, 0));
	if (lock < 0)
		return;
	if (do_fork == 0) {
		(void) execl("/usr/bin/lp", MASTER_NAME, NULL);
		syslog(LOG_ERR, "start_daemon() - execl: %m");
		exit(-1);
	} else
		switch (fork()) {
		case -1:
			syslog(LOG_ERR, "start_daemon() - fork: %m");
			exit(-1);
			/* NOTREACHED */
		case 0:
			break;
		default:
			(void) execl("/usr/bin/lp", MASTER_NAME, NULL);
			syslog(LOG_ERR, "start_daemon() - execl: %m");
			exit(-1);
			/* NOTREACHED */
		}

}


/*
 *  kill_daemon() - read the master lock file and send SIGTERM to the process
 *	id stored in the file.
 */
int
kill_process(char *file)
{
	int	fd,
		pid;
	char	buf[BUFSIZ],
		*p;

	if ((fd = open(file, O_RDONLY)) < 0)
		return (-1);

	(void) memset(buf, NULL, sizeof (buf));
	if (read(fd, buf, sizeof (buf)) <= 0)
		return (-1);

	if ((p = strchr(buf, '\n')) == NULL) {	/* skip the 1st line */
		close(fd);
		return (-1);
	}
	pid = atoi(++p);		/* convert the PID */

	if ((pid < 2) || (kill(pid, SIGTERM) < 0)) {
		close(fd);
		return (-1);
	}
	close(fd);
	return (0);
}


char **
strsplit(char *string, char *seperators)
{
	char **list = NULL;
	char *where = NULL;
	char *element;

	for (element = strtok_r(string, seperators, &where); element != NULL;
	    element = strtok_r(NULL, seperators, &where))
		list = (char **)list_append((void **)list, element);

	return (list);
}
