/*
**	uufuncs.c
**
**	functions needed by uugetty
*/

/*
**	Modification History:
**
**	10-Sep-02, CSJ: Release 2.1.0  - lots of fixes.
**	10-Jun-02, CSJ: Release 2.0.8 - lots of fixes, new maintainer.
**	21-Feb-97, SN:  Add baud rates higher than 38400, and remove baud
**			rates lower than 2400 (2.0.7.j)
**	25-Apr-96, JC:  Modify so that failure to open the "tty" after 3
**			attempts will terminate the program (2.0.7.i).
**	Unknown	   MB:  Fix segmentation fault when ttytype is not specified
**			on the command line (2.0.7.h).
**	Unknown	   ZT:  Several minor fixes, including bundling needed
**			sources (2.0.7g).
**	Unknown	   AW:	Modify so that utmp is updated correctly (2.0.7f).
**
**	AW: Alan Went (alan@ezlink.com)
**	CSJ: Christine S. Jamison (getty-info@nwmagic.net)
**	JC: Jeff Chua (jchua@fedex.com)
**	MB: Mike Blatchley (Mike_Blatchley@maxtor.com)
**	SN: Sho Nakagama (bbs.fdu.edu)
**	ZT: Zoltan Hidvegi (hzoli@cs.elte.hu)
**
**
**	Copyright 1989,1990 by Paul Sutcliffe Jr.
**	Portions copyright 2000,2002 by Christine Jamison.
**
**	Permission is hereby granted to copy, reproduce, redistribute,
**	or otherwise use this software as long as: there is no monetary
**	profit gained specifically from the use or reproduction of this
**	software; it is not sold, rented, traded or otherwise marketed;
**	and this copyright notice is included prominently in any copy
**	made.
**
**	The authors make no claims as to the fitness or correctness of
**	this software for any use whatsoever, and it is provided as is. 
**	Any use of this software is at the user's own risk.
*/

#define UUFUNCS
#define UUGETTY

#include "main.h"
#include "uufuncs.h"
#include "debug.h"

/* forward declarations */

void cshangup();

/*
** waitlocks: if there are UUCP style lockfiles around, sleep until
**	      they disappear
*/

void waitlocks()
{
	debug(D_RUN, "checking for lockfiles..."); 

	if(checklock(lock)) {
		while(checklock(lock)) (void) sleep(30);
		exit(0);
	}
	if((altlock) && checklock(altlock)) {
		while(checklock(altlock)) (void) sleep(30);
		exit(0);
	}
} 


/*
** lockline: lock the main (lock) and alternate (altlock) lines
**	     set to remove locks on a signal
*/

void lockline()
{
	debug(D_RUN, "locking the line");

	if(makelock(lock) == FAIL) exit(0);
	if((altlock) && (makelock(altlock) == FAIL)) exit(0);

	(void) signal(SIGHUP, rmlocks);
	(void) signal(SIGINT, rmlocks);
	(void) signal(SIGQUIT, rmlocks);
	(void) signal(SIGTERM, rmlocks);
}


/*
** makelock: attempt to make a lockfile
**	     Returns FAIL if lock could not be made (line in use).
*/

int makelock(name)
char *name;
{
	int fd, pid;
	int getpid();
#ifdef	ASCIIPID
	char apid[16];
#endif	/* ASCIIPID */
#if LIBRARIES < 1
	char *temp, buf[MAXLINE+1];
	char *mktemp();
#else		/* glibc2 */
	char *temp;
	int mkstemp();
#endif

	debug(D_LOCK, "makelock(%s) called", name);

	/* first make a temp file... */

#if LIBRARIES < 1
	(void) sprintf(buf, LOCK, "TM.XXXXXX");
	if ((fd = creat((temp=mktemp(buf)), 0444)) == FAIL) {
		logerr("create failed on temp lockfile \"%s\": %s",
		       temp, strerror(errno));
		return(FAIL);
	     }
#else		/* glibc2 */
	temp = malloc(strlen(LOCK)+10);
	sprintf(temp, LOCK, "TM.XXXXXX");
	if ((fd = mkstemp(temp)) == FAIL)
	     { logerr("mkstemp failed on temp lockfile \"%s\": %s",
		       temp, strerror(errno));
		return(FAIL);
	     }

	/*  Then, give it mode 0444... */

	if (fchmod(fd, 0444))
	     { logerr("chmod failed on temp lockfile \"%s\": %s",
		       temp, strerror(errno));
		return(FAIL);
	     }
#endif

	debug(D_LOCK, "temp, fd = (%s,%d)", temp, fd);

	/* And, now put my pid in it! */

#ifdef	ASCIIPID
	(void) sprintf(apid, "%09d", getpid());
	(void) write(fd, apid, strlen(apid));
#else
	pid = getpid();
	(void) write(fd, (char *)&pid, sizeof(pid));
#endif	/* ASCIIPID */
	(void) close(fd);

	/* link it to the lock file
	 */
	while (link(temp, name) == FAIL) {
		debug(D_LOCK, "link(temp,name) failed, errno=%d", errno);
		if (errno == EEXIST) {		/* lock file already there */
			if ((pid = readlock(name)) == FAIL)
				continue;
			if ((kill(pid, 0) == FAIL) && errno == ESRCH) {
				/* pid that created lockfile is gone */
				(void) unlink(name);
				continue;
			}
		}
		debug(D_LOCK, "lock NOT made");
		(void) unlink(temp);
		return(FAIL);
	}
	debug(D_LOCK, "lock made");
	(void) unlink(temp);
	return(SUCCESS);
}


/*
** checklock: test for presense of valid lock file
**	      Returns TRUE if lockfile found, FALSE if not.
*/

boolean checklock(name)
char *name;
{
	int pid;
	struct stat st;

	debug(D_LOCK, "checklock(%s) called", name);

	if ((stat(name, &st) == FAIL) && errno == ENOENT) {
		debug(D_LOCK, "no lockfile found");
		return(FALSE);
	}

	if ((pid = readlock(name)) == FAIL) {
		debug(D_LOCK, "couldn't read lockfile");
		return(FALSE);
	}

	if (pid == getpid()) {
		debug(D_LOCK, 
		  "lockfile belongs to me... damn race conditions");
		return(FALSE);
	}

	if ((kill(pid, 0) == FAIL) && errno == ESRCH) {
		debug(D_LOCK, "no active process has lock, will remove");
		(void) unlink(name);
		return(FALSE);
	}

	debug(D_LOCK, "active process has lock, return(TRUE)");
	return(TRUE);
}


/*
** readlock: read contents of lockfile
**	     Returns pid read or FAIL on error.
*/

int readlock(name)
char *name;
{
	int fd, pid, n=0;
#ifdef	ASCIIPID
	char apid[16];
#endif	/* ASCIIPID */

	if ((fd = open(name, O_RDONLY)) == FAIL)
		return(FAIL);

#ifdef	ASCIIPID
	(void) read(fd, apid, sizeof(apid));
	n = sscanf(apid, "%d", &pid);
#else
	(void) read(fd, (char *)&pid, sizeof(pid));
#endif	/* ASCIIPID */

#ifdef  BOTHPID
	if (n != 1){
		(void) close(fd);
		fd = open(name, O_RDONLY);
		(void) read(fd, (char *)&pid, sizeof(pid));
		}
#endif

	(void) close(fd);
	debug(D_LOCK, "read %d from the lockfile", pid);
	return(pid);
}


/*
** rmlocks: remove lockfile(s)
*/

void rmlocks()
{
	if (altlock != (char *) NULL)
		(void) unlink(altlock);

	(void) unlink(lock);
}
