/*======================================================================
                    S T D E R R . C 
                    doc: Mon Feb 24 16:24:35 1992
                    dlm: Tue Aug 17 16:21:14 1993
                    (c) 1992 ant@julia
                    uE-Info: 92 0 T 0 0 72 10 2 8 ofnI
======================================================================*/

/*#define		VERBOSE			/* Babble */

#include	<stdio.h>
#include	<syslog.h>
#include	<signal.h>
#include	<errno.h>
#include	<fcntl.h>
#include	<sys/types.h>
#include	<sys/socket.h>
#include	<sys/stat.h>
#include	<sys/file.h>		/* SOLARIS */
#include	"config.h"
#include	"common.h"
#ifdef NOSYSLOG_QUIRK
#	include <string.h>
#	include	<varargs.h>
#	include <time.h>
#endif

#define	READBUF	128			/* read buffer size */

extern int	errno;			/* for stderr2syslog */
static int	ep[2];			/* error pipe */
static char	redirOk = TRUE;		/* stderr redirection worked */
#ifdef NOASYNCIO_QUIRK
static char	*errFile;		/* name of error file */
#endif
#ifdef NOSYSLOG_QUIRK
static char	syslogFile[STRLEN];	/* name of syslog file */
static char	tag[STRLEN];		/* tag of syslog entry */
void		noSyslogOpen();
void		noSyslogClose();
void		noSyslog();
extern char 	*sys_errlist[];
#endif

/*----------------------------------------------------------------------*/

static void stderr2syslog()			/* stderr -> syslog */
{
	int	nByte,i;
	char	buf[READBUF];
	static char	line[READBUF];
	static int	lPos = 0;

	if (!redirOk) {				/* testing */
		nByte = read(ep[IN],buf,READBUF);
		if (nByte != 1 || buf[0] != '?') {
			SYSLOG(LOG_ERR,"Warning: Wrong stderr message!\n");
		}
		redirOk = TRUE;
		signal(SIGIO,stderr2syslog);
		return;
	}

	do { 					/* read everything */
		nByte = read(ep[IN],buf,READBUF);/* read (must be ready) */
		if (nByte == 0) continue; 
		if (nByte < 0) {
			if (errno == EWOULDBLOCK) continue; 
			SYSLOG(LOG_ERR,"stderr2syslog read: %m");
		}
		for (i=0; i<nByte; i++)	{	/* copy message part */
			if (buf[i] == '\0')	/* skip \0 */
				continue;
			if (buf[i] == '\n') {	/* terminate line */
				line[lPos] = '\0';
				SYSLOG(LOG_ERR,"%s",line);
				lPos = 0;
				continue;
			}
			line[lPos++] = buf[i];
		}
	} while (nByte > 0); 
	signal(SIGIO,stderr2syslog);		/* reenable sysV signal */
}

openStderr(prog)
char *prog;
{
#ifdef NOASYNCIO_QUIRK

	errFile = tempnam(NULL,"inetray.");
	ep[OUT] = open(errFile,O_WRONLY|O_CREAT,0666);
	if (ep[OUT] < 0) {
		SYSLOG(LOG_ERR,"open(%s): %m",errFile);
		return -1;
	}
#else
	int	pid;
	
	if (socketpair(PF_UNIX,SOCK_STREAM,0,ep) < 0) {	/* make pipe */
		SYSLOG(LOG_ERR,"socketpair: %m");
		return -1;
	}
	if (fcntl(ep[IN],F_SETFL,FASYNC|FNDELAY) < 0) { /* async mode */
		SYSLOG(LOG_ERR,"fcntl F_SETFL: %m");
		return -1;
	}

	pid = getpid(0);				/* set prgp */
	
	if (fcntl(ep[IN],F_SETOWN,pid) < 0) {
		SYSLOG(LOG_ERR,"fcntl F_SETOWN: %m");
		return -1;
	}
	if ((int)signal(SIGIO,stderr2syslog) < 0) {	/* async read */
		SYSLOG(LOG_ERR,"signal: %m");
		return -1;
	}
#endif
#ifdef VERBOSE
	SYSLOG(LOG_NOTICE,"dup()ing...");
#endif
	if (dup2(ep[OUT],ERR) < 0) {			/* stderr */
		SYSLOG(LOG_ERR,"dup2 ERR: %m");
		return -1;
	}
#ifdef VERBOSE
	SYSLOG(LOG_NOTICE,"CLOSELOG()");
#endif
	CLOSELOG();					/* ultrix bug */
#ifdef VERBOSE
	SYSLOG(LOG_NOTICE,"OPENLOG(%s)",prog);
#endif
	OPENLOG(prog);

#ifndef NOASYNCIO_QUIRK					/* check */
#ifdef VERBOSE
	SYSLOG(LOG_NOTICE,"Testing Stderr...");
#endif
	redirOk = FALSE;
	fprintf(stderr,"?");
	if (!redirOk) {
		SYSLOG(LOG_ERR,"Warning: stderr is lost!\n");
		redirOk = TRUE;
	}
#endif
#ifdef VERBOSE
	SYSLOG(LOG_NOTICE,"return 0");
#endif
	return 0;
}

static unlinkEmpty(name)				/* unlink if empty */
char *name;						/* ignore errs */
{
	struct stat sBuf;

	if (name == NULL ||
	    stat(name,&sBuf) < 0) return FALSE;
	    
	if (sBuf.st_size == 0) {			/* empty file */
		unlink(name);
		return FALSE;
	} else {					/* errors in file */
		return TRUE;
	}
}
	
void closeStderr()
{
#ifdef NOASYNCIO_QUIRK
	if (unlinkEmpty(errFile))
		SYSLOG(LOG_ERR,"Errors in %s",errFile);
#endif
}

/*----------------------------------------------------------------------*/

#ifdef NOSYSLOG_QUIRK
void noSyslogOpen(prog)					/* try to open file */
char *prog;						/* no errmes */
{
	int fd;
	
	strncpy(syslogFile,_P_tmpdir,STRLEN);
	strncat(syslogFile,NOSYSLOG,STRLEN-strlen(_P_tmpdir));
	strncpy(tag,prog,STRLEN);

	if ((fd = open(syslogFile,O_WRONLY|O_APPEND)) < 0) {
		if ((fd = creat(syslogFile,0666)) < 0 ||
		    fchmod(fd,0666) < 0) exit(1);
	}
	close(fd);
}

void noSyslog(va_alist)					/* write message */
va_dcl							/* %s %d %f %c %m ok */
{							/* no errmes */
	va_list	av;
	char	*fmt,msg[STRLEN];
	int	i,fd,pri,timer;

	if ((fd = open(syslogFile,O_WRONLY|O_APPEND)) < 0)
		return;

	timer = time(NULL);
	strftime(msg,STRLEN,"%b %d %T ",localtime(&timer));/* date & time */
	write(fd,msg,strlen(msg));
		
	sprintf(msg,"%s[%d]: ",tag,getpid());		/* tag[pid] */
	write(fd,msg,strlen(msg));

	va_start(av);
	pri = va_arg(av,int);				/* print priority */
	switch (pri) {
#ifdef LOG_EMERG
		case LOG_EMERG: sprintf(msg,"%s: ","LOG_EMERG"); break;
#endif
#ifdef LOG_ALERT
		case LOG_ALERT: sprintf(msg,"%s: ","LOG_ALERT"); break;
#endif
#ifdef LOG_CRIT
		case LOG_CRIT: sprintf(msg,"%s: ","LOG_CRIT"); break;
#endif
#ifdef LOG_ERR
		case LOG_ERR: sprintf(msg,"%s: ","LOG_ERR"); break;
#endif
#ifdef LOG_WARNING
		case LOG_WARNING: sprintf(msg,"%s: ","LOG_WARNING"); break;
#endif
#ifdef LOG_NOTICE
		case LOG_NOTICE: sprintf(msg,"%s: ","LOG_NOTICE"); break;
#endif
#ifdef LOG_INFO
		case LOG_INFO: sprintf(msg,"%s: ","LOG_INFO"); break;
#endif
#ifdef LOG_DEBUG
		case LOG_DEBUG: sprintf(msg,"%s: ","LOG_DEBUG"); break;
#endif
		default: sprintf(msg,"Priority <%d>: ",pri); break;
	}
	write(fd,msg,strlen(msg));
	
	fmt = va_arg(av,char *);			/* handle args */
	
	for (i=0; fmt[i]!='\0'; i++) {
		if (fmt[i] != '%') {			/* normal char */
			write(fd,&fmt[i],1);
			continue;
		}
		i++;
		switch (fmt[i]) {
			case 'c': sprintf(msg,"%c",va_arg(av,char));
				  break;
			case 's': sprintf(msg,"%s",va_arg(av,char *));
				  break;
			case 'd': sprintf(msg,"%d",va_arg(av,long));
				  break;
			case 'f': sprintf(msg,"%f",va_arg(av,double));
				  break;
			case 'm': sprintf(msg,"%s",sys_errlist[errno]);
				  break;
			default:  sprintf(msg,"<%%%c skipped!>",fmt[i+1]);
				  (void)va_arg(av,void *);
				  break;
		}
		write(fd,msg,strlen(msg));
	}
	va_end(av);
	write(fd,"\n",2);
	close(fd);
}
#endif
