/**************************************************************************
 * LPRng IFHP Filter
 * Copyright 1994-1999 Patrick Powell, San Diego, CA <papowell@astart.com>
 **************************************************************************/
/**** HEADER *****/
 static char *const _id = "$Id: errormsg.c,v 1.19 2004/02/25 15:56:25 papowell Exp papowell $";

#include "ifhp.h"

/**** ENDINCLUDE ****/

/****************************************************************************
 * char *Errormsg( int err )
 *  returns a printable form of the
 *  errormessage corresponding to the valie of err.
 *  This is the poor man's version of sperror(), not available on all systems
 *  Patrick Powell Tue Apr 11 08:05:05 PDT 1995
 ****************************************************************************/
/****************************************************************************/
#if !defined(HAVE_STRERROR)

# if defined(HAVE_SYS_NERR)
#  if !defined(HAVE_SYS_NERR_DEF)
     extern int sys_nerr;
#  endif
#  define num_errors    (sys_nerr)
# else
#  define num_errors    (-1)            /* always use "errno=%d" */
# endif

# if defined(HAVE_SYS_ERRLIST)
#  if !defined(HAVE_SYS_ERRLIST_DEF)
     extern const char *const sys_errlist[];
#  endif
# else
#  undef  num_errors
#  define num_errors   (-1)            /* always use "errno=%d" */
# endif

#endif

 static int in_log;

 int udp_open( char *device );
 void setstatus( char *msg, char *details, int trace );

const char * Errormsg ( int err )
{
    const char *cp;

	if( err == 0 ){
		cp = "No Error";
	} else {
#if defined(HAVE_STRERROR)
		cp = strerror(err);
#else
# if defined(HAVE_SYS_ERRLIST)
		if (err >= 0 && err < num_errors) {
			cp = sys_errlist[err];
		} else
# endif
		{
			static char msgbuf[32];     /* holds "errno=%d". */
			(void) SNPRINTF(msgbuf,sizeof(msgbuf) "errno=%d", err);
			cp = msgbuf;
		}
#endif
	}
    return (cp);
}

 static char log_buf[LARGEBUFFER];
 static char *log_start;

 static void log_init()
{
	int n;
	char *s;

	log_buf[0] = 0;
	n = strlen(log_buf); s = log_buf+n; n = sizeof(log_buf)-n;
	if (Name && *Name) {
		(void) SNPRINTF( s, n) "%s ", Name );
		n = strlen(log_buf); s = log_buf+n; n = sizeof(log_buf)-n;
	}
	(void) SNPRINTF( s, n) "%s ", Time_str(1,0) );
	n = strlen(log_buf); s = log_buf+n; n = sizeof(log_buf)-n;
	(void) SNPRINTF(s, n) "[%d] ", (int)getpid ());
	n = strlen(log_buf); s = log_buf+n; n = sizeof(log_buf)-n;
	log_start = s;
}

 static void log_backend( int trace, int stderr_output )
{
	int n;
	char *s;

	n = strlen(log_buf); s = log_buf+n; n = sizeof(log_buf)-n;
	setstatus( log_buf, log_start, trace );
	*s = 0;
	if( Trace_on_stderr || stderr_output ){
		n = strlen(log_buf); s = log_buf+n; n = sizeof(log_buf)-n;
		(void)SNPRINTF( s, n) "\n" );
		Write_fd_str( 2, log_buf );
	}
}

/* VARARGS2 */
#ifdef HAVE_STDARGS
 void logmsg( char *msg,...)
#else
 void logmsg(va_alist) va_dcl
#endif
{
#ifndef HAVE_STDARGS
    char *msg;
#endif
	int n;
	char *s;
	int err = errno;
    VA_LOCAL_DECL
    VA_START (msg);
    VA_SHIFT (msg, char *);

	if( in_log++ == 0 ){
		log_init();
		n = strlen(log_buf); s = log_buf+n; n = sizeof(log_buf)-n;
		(void) VSNPRINTF(s, n) msg, ap);
		log_backend(0,0);
		in_log = 0;
	}
    VA_END;
	errno = err;
}


/* VARARGS2 */
#ifdef HAVE_STDARGS
 void logDebug( char *msg,...)
#else
 void logDebug(va_alist) va_dcl
#endif
{
#ifndef HAVE_STDARGS
    char *msg;
#endif
	int n;
	char *s;
	int err = errno;
    VA_LOCAL_DECL
    VA_START (msg);
    VA_SHIFT (msg, char *);

	if( in_log++ == 0 ){
		log_init();
		n = strlen(log_buf); s = log_buf+n; n = sizeof(log_buf)-n;
		(void) VSNPRINTF(s, n) msg, ap);
		log_backend(1,0);
		in_log = 0;
	}
    VA_END;
	errno = err;
}

/* VARARGS2 */
#ifdef HAVE_STDARGS
 void fatal ( char *msg,...)
#else
 void fatal (va_alist) va_dcl
#endif
{
#ifndef HAVE_STDARGS
    char *msg;
#endif
	int n;
	char *s;
    VA_LOCAL_DECL
    VA_START (msg);
    VA_SHIFT (msg, char *);
	
	if( in_log++ == 0 ){
		log_init();
		n = strlen(log_buf); s = log_buf+n; n = sizeof(log_buf)-n;
		(void) VSNPRINTF(s, n) msg, ap);
		log_backend(0,1);
		if( Errorcode == 0 ){
			Errorcode = JFAIL;
		}
		if( Errorcode == -1 ) abort();
		cleanup(0);
		in_log = 0;
	}
    VA_END;
}

/* VARARGS2 */
#ifdef HAVE_STDARGS
 void logerr (char *msg,...)
#else
 void logerr (va_alist) va_dcl
#endif
{
#ifndef HAVE_STDARGS
    char *msg;
#endif
	int err = errno;
	int n;
	char *s;
    VA_LOCAL_DECL
    VA_START (msg);
    VA_SHIFT (msg, char *);

	if( in_log++ == 0 ){
		log_init();
		n = strlen(log_buf); s = log_buf+n; n = sizeof(log_buf)-n;
		(void) VSNPRINTF(s, n) msg, ap);
		n = strlen(log_buf); s = log_buf+n; n = sizeof(log_buf)-n;
		(void) SNPRINTF (s, n) " - %s", Errormsg (err));
		log_backend(0,0);
		in_log = 0;
	}
    VA_END;
    errno = err;
}

/* VARARGS2 */
#ifdef HAVE_STDARGS
 void logerr_die ( char *msg,...)
#else
 void logerr_die (va_alist) va_dcl
#endif
{
#ifndef HAVE_STDARGS
    char *msg;
#endif
	int err = errno;
	int n;
	char *s;
    VA_LOCAL_DECL

    VA_START (msg);
    VA_SHIFT (msg, char *);

	if( in_log++ == 0 ){
		log_init();
		n = strlen(log_buf); s = log_buf+n; n = sizeof(log_buf)-n;
		(void) VSNPRINTF (s, n) msg, ap);
		n = strlen(log_buf); s = log_buf+n; n = sizeof(log_buf)-n;
		(void) SNPRINTF (s, n) " - %s", Errormsg (err));
		log_backend(0,1);
		if( Errorcode == 0 ){
			Errorcode = JFAIL;
		}
		if( Errorcode == -1 ) abort();
		cleanup(0);
		in_log = 0;
	}
    VA_END;
}

/*
 * Time_str: return "cleaned up" ctime() string...
 *
 * in YY/MO/DY/hr:mn:sc
 * Thu Aug 4 12:34:17 BST 1994 -> 12:34:17
 */

char *Time_str(int shortform, time_t t)
{
    static char buffer[99];
	struct tm *tmptr;
	struct timeval tv;

	tv.tv_usec = 0;
	if( t == 0 ){
		if( gettimeofday( &tv, 0 ) == -1 ){
			LOGERR_DIE(LOG_INFO) "gettimeofday failed");
		}
		t = tv.tv_sec;
	}
	tmptr = localtime( &t );
	if( shortform && Full_time == 0 ){
		SNPRINTF( buffer, sizeof(buffer) )
			"%02d:%02d:%02d.%03d",
			tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec,
			(int)(tv.tv_usec/1000) );
	} else {
		SNPRINTF( buffer, sizeof(buffer) )
			"%d-%02d-%02d-%02d:%02d:%02d.%03d",
			tmptr->tm_year+1900, tmptr->tm_mon+1, tmptr->tm_mday,
			tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec,
			(int)(tv.tv_usec/1000) );
	}
	return( buffer );
}

/*
 * Write_fd_str( int fd, char *str )
 *  returns: 0 successful
 *           < 0  error
 */

int Write_fd_str( int fd, const char *str )
{
	if( str ){
		return (Write_fd_len( fd, str, strlen(str) ) );
	}
	return 0;
}

int Write_fd_len( int fd, const char *str, int len )
{
	int i;
	if( str ) while( len > 0 ){
		i = write( fd, str, len );
		if( i < 0 ) return(i);
		str += i;
		len -= i;
	}
	return 0;
}



/***************************************************************************
 * char *Sigstr(n)
 * Return a printable form the the signal
 ***************************************************************************/

 struct signame {
    char *str;
    int value;
};

#undef PAIR
#ifndef _UNPROTO_
# define PAIR(X) { #X , X }
#else
# define __string(X) "X"
# define PAIR(X) { __string(X) , X }
#endif

 struct signame signals[] = {
{ "NO SIGNAL", 0 },
#ifdef SIGHUP
 PAIR(SIGHUP),
#endif
#ifdef SIGINT
 PAIR(SIGINT),
#endif
#ifdef SIGQUIT
 PAIR(SIGQUIT),
#endif
#ifdef SIGILL
 PAIR(SIGILL),
#endif
#ifdef SIGTRAP
 PAIR(SIGTRAP),
#endif
#ifdef SIGIOT
 PAIR(SIGIOT),
#endif
#ifdef SIGABRT
 PAIR(SIGABRT),
#endif
#ifdef SIGEMT
 PAIR(SIGEMT),
#endif
#ifdef SIGFPE
 PAIR(SIGFPE),
#endif
#ifdef SIGKILL
 PAIR(SIGKILL),
#endif
#ifdef SIGBUS
 PAIR(SIGBUS),
#endif
#ifdef SIGSEGV
 PAIR(SIGSEGV),
#endif
#ifdef SIGSYS
 PAIR(SIGSYS),
#endif
#ifdef SIGPIPE
 PAIR(SIGPIPE),
#endif
#ifdef SIGALRM
 PAIR(SIGALRM),
#endif
#ifdef SIGTERM
 PAIR(SIGTERM),
#endif
#ifdef SIGURG
 PAIR(SIGURG),
#endif
#ifdef SIGSTOP
 PAIR(SIGSTOP),
#endif
#ifdef SIGTSTP
 PAIR(SIGTSTP),
#endif
#ifdef SIGCONT
 PAIR(SIGCONT),
#endif
#ifdef SIGCHLD
 PAIR(SIGCHLD),
#endif
#ifdef SIGCLD
 PAIR(SIGCLD),
#endif
#ifdef SIGTTIN
 PAIR(SIGTTIN),
#endif
#ifdef SIGTTOU
 PAIR(SIGTTOU),
#endif
#ifdef SIGIO
 PAIR(SIGIO),
#endif
#ifdef SIGPOLL
 PAIR(SIGPOLL),
#endif
#ifdef SIGXCPU
 PAIR(SIGXCPU),
#endif
#ifdef SIGXFSZ
 PAIR(SIGXFSZ),
#endif
#ifdef SIGVTALRM
 PAIR(SIGVTALRM),
#endif
#ifdef SIGPROF
 PAIR(SIGPROF),
#endif
#ifdef SIGWINCH
 PAIR(SIGWINCH),
#endif
#ifdef SIGLOST
 PAIR(SIGLOST),
#endif
#ifdef SIGUSR1
 PAIR(SIGUSR1),
#endif
#ifdef SIGUSR2
 PAIR(SIGUSR2),
#endif
{0,0}
    /* that's all */
};

#ifndef NSIG
# define  NSIG 32
#endif


const char *Sigstr (int n)
{
    static char buf[40];
	const char *s = 0;

	if( n == 0 ){
		return( "No signal");
	}
    if (n < NSIG && n >= 0) {
#if defined(HAVE_SYS_SIGLIST)
# if !defined(HAVE_SYS_SIGLIST_DEF)
		extern const char *sys_siglist[];
# endif
		s = sys_siglist[n];
#else
# if defined(HAVE__SYS_SIGLIST)
#  if !defined(HAVE__SYS_SIGLIST_DEF)
		extern const char *_sys_siglist[];
#  endif
		s = _sys_siglist[n];
# else
		int i;
		for( i = 0; signals[i].str && signals[i].value != n; ++i );
		s = signals[i].str;
# endif
#endif
	}
	if( s == 0 ){
		s = buf;
		(void) SNPRINTF(buf, sizeof(buf)) "signal %d", n);
	}
    return(s);
}

void setstatus( char *msg, char *details, int trace )
{
	char *s;
	int size, minsize, n, temp_fd;
	static int active = 0;
	struct stat statb;
	char path[MAXPATHLEN];
	char buffer[SMALLBUFFER];

	if( active ) return;
	++active;
	if( Status_fd < 0 && Statusfile && stat( Statusfile, &statb) != -1 ){
		/*
		char b[SMALLBUFFER];
		SNPRINTF(b, sizeof(b),"Statusfile %s\n", Statusfile );
		Write_fd_str( 2, b );
		*/
		size = Max_status_size;
		if( size == 0 ) size = 8;
		/*
		SNPRINTF(b,sizeof(b), "setstatus: size %d, max %d\n", size, (int)statb.st_size );
		Write_fd_str( 2, b );
		*/
		if( (statb.st_size/1024) > size ){
			minsize = Min_status_size;
			if( minsize > size ){
				minsize = size/4;
			}
			if( minsize == 0 ){
				minsize = 512;
			} else {
				minsize *= 1024;
			}
			/*
			SNPRINTF(b,sizeof(b), "setstatus: min size %d\n", minsize );
			Write_fd_str( 2, b );
			*/
			Status_fd = open( Statusfile, O_RDWR );
			/*Set_max_fd( Status_fd ); */
			SNPRINTF(path,sizeof(path)) "%s.x", Statusfile );
			unlink( path );
			temp_fd = open( path, O_RDWR );
			/*Set_max_fd( temp_fd );*/
			if( Status_fd >= 0 && temp_fd >=0 ){
				if( lseek( Status_fd, -minsize, SEEK_END ) < 0 ){
					LOGERR_DIE(LOG_INFO) "setstatus: cannot seek '%s'", Statusfile );
				}
				s = 0;
				while( s == 0 && (n = read(Status_fd, buffer,sizeof(buffer)-1)) > 0 ){
					buffer[n] = 0;
					if( (s = strchr(buffer,'\n')) ){
						*s++ = 0;
						write(temp_fd, s,strlen(s));
					}
				}
				while( (n = read(Status_fd, buffer,sizeof(buffer)-1)) > 0 ){
					write(temp_fd, buffer,n);
				}
				close( Status_fd );
				close( temp_fd );
				rename( path, Statusfile );
			}
		}
		Status_fd = open( Statusfile, O_WRONLY|O_APPEND, 0600 );
		/*Set_max_fd( Status_fd );*/
		if( Status_fd == 0 || Status_fd == 1 || Status_fd == 2 ){
			int fd;
			for( fd = 3; fstat(fd,&statb) == 0; ++fd );
			if( dup2(Status_fd,fd) == -1 ){
				LOGERR_DIE(LOG_INFO) "setstatus: dup2 failed");
			}
			close( Status_fd );
			Status_fd = fd;
		}
		/*
		stat( Statusfile, &statb );
		SNPRINTF(b,sizeof(b), "setstatus: new size %d\n", (int)(statb.st_size) );
		Write_fd_str( 2, b );
		*/
	}
	msg = details;
	if( !Debug && !trace && (s = strpbrk(msg," \t:")) && *s == ':' && isspace(cval(s+1)) ){
		for(msg = s+1; isspace(cval(msg)); ++msg );
	}
	n = strlen(log_buf); s = log_buf+n; n = sizeof(log_buf)-n;
	(void)SNPRINTF( s, n) " at %s\n", Time_str(1,0) );
	if( Status_fd >= 0 ){
		if( OF_Mode ){
			Write_fd_str(Status_fd, "(of) ");
		}
		Write_fd_str( Status_fd, msg );
	}
	--active;
}

/**********************************************
 * Support a simple one line summary message for status
 * This is handy for debugging and reporting information
 **********************************************/

int udp_open( char *device )
{
	int port, i, fd, err;
	struct hostent *hostent;
	struct sockaddr_in sin;
	struct servent *servent;
	char *s;

	LOGMSG(LOG_INFO) "udp_open: '%s'\n",device );
	if( (s = strpbrk( device, "@%" )) == 0 ){
		LOGMSG(LOG_INFO) "udp_open: missing port number '%s'\n",device );
		return( -1 );
	}
	if( strpbrk( s+1, "@%" ) ){
		LOGMSG(LOG_INFO) "udp_open: two '@' or '%%' in name '%s'\n",
			device );
		return( -1 );
	}
	port = atoi( s+1 );
	if( port <= 0 ){
		servent = getservbyname( s+1, "udp" );
		if( servent ){
			port = ntohs( servent->s_port );
		}
	}
	if( port <= 0 ){
		LOGMSG(LOG_INFO) "udp_open: bad port number '%s'\n",s+1 );
		return( -1 );
	}
	i = *s;
	*s = 0;
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = -1;

	if( (hostent = gethostbyname(device)) ){
		/*
		 * set up the address information
		 */
		if( hostent->h_addrtype != AF_INET ){
			LOGMSG(LOG_INFO) "udp_open: bad address type for host '%s'\n",
				device);
		}
		memmove( &sin.sin_addr, hostent->h_addr, hostent->h_length );
	} else {
		sin.sin_addr.s_addr = inet_addr(device);
	}
	*s = i;
	if( (int)sin.sin_addr.s_addr == -1){
		LOGMSG(LOG_INFO)"udp_open: unknown host '%s'\n", device);
		return(-1);
	}
	sin.sin_port = htons( port );
	LOGMSG(LOG_INFO) "udp_open: destination '%s' port %d\n",
		inet_ntoa( sin.sin_addr ), ntohs( sin.sin_port ) );
	fd = socket (AF_INET, SOCK_DGRAM, 0);
	/*Set_max_fd( fd );*/
	err = errno;
	if (fd < 0) {
		LOGMSG(LOG_INFO)"udp_open: socket call failed - %s\n", Errormsg(err) );
		return( -1 );
	}
	i = connect (fd, (struct sockaddr *) & sin, sizeof (sin));
	err = errno;

	if( i < 0 ){
		LOGMSG(LOG_INFO)"udp_open: connect to '%s port %d' failed - %s\n",
			inet_ntoa( sin.sin_addr ), ntohs( sin.sin_port ),
			Errormsg(errno) );
		close(fd);
		fd = -1;
	}
	return( fd );
}

/* VARARGS2 */
#ifdef HAVE_STDARGS
 void safefprintf (int fd, char *format,...)
#else
 void safefprintf (va_alist) va_dcl
#endif
{
#ifndef HAVE_STDARGS
	int fd;
    char *format;
#endif
	char buf[1024];
    VA_LOCAL_DECL

    VA_START (format);
    VA_SHIFT (fd, int);
    VA_SHIFT (format, char *);

	buf[0] = 0;
	(void) VSNPRINTF (buf, sizeof(buf)) format, ap);
	Write_fd_str(fd,buf);
}


