/*
Copyright (c) 2010, Dirk Krause
All rights reserved.

Redistribution and use in source and binary forms,
with or without modification, are permitted provided
that the following conditions are met:

* Redistributions of source code must retain the above
  copyright notice, this list of conditions and the
  following disclaimer.
* Redistributions in binary form must reproduce the above 
  opyright notice, this list of conditions and the following
  disclaimer in the documentation and/or other materials
  provided with the distribution.
* Neither the name of the Dirk Krause nor the names of
  contributors may be used to endorse or promote
  products derived from this software without specific
  prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
*/


/**	@file	useraud.c	Main module of the useraud daemon. */



/**	Inside the useraud module.
*/
#define USERAUD_C	1

#include "useraudi.h"




#line 51 "useraud.ctr"




/**	VOLATILE is volatile or an empty string.
*/
#if DK_HAVE_VOLATILE
#define VOLATILE volatile
#else
#define VOLATILE /* nix */
#endif



/** Program name. */
static char appname[] = { "useraud" };



/** @defgroup exit	Exit code. */
/*@{*/
/** Exit code returned by the main function.
*/
static VOLATILE int exval = 0;

/**	Set exit code returned by the main function.
	@param	i	New exit code.
*/
void
ua_set_exit_code DK_P1(int,i) {
  if(i > exval) exval = i;
}
/*@}*/



/** @defgroup buffers Buffers for request and response. */
/*@{*/
/**	Input line.
*/
static char il[USERAUD_LINESIZE];

/**	Outut line.
*/
static char ol[USERAUD_LINESIZE];

/**	Temporary buffer 1.
*/
static char t1[USERAUD_LINESIZE];

/**	Temporary buffer 2.
*/
static char t2[USERAUD_LINESIZE];

/**	Temporary buffer 3.
*/
static char t3[USERAUD_LINESIZE];

/**	Return code.
*/
static int rc;
/*@}*/



/** @defgroup debugmode Debug mode. */
/*@{*/
/** Flag: Debug mode on.
*/
static int f_debug = 0;

/**	Check debug mode flag.
	@return	Flag: Debug mode.
*/
int
ua_get_debug DK_P0() {
  return f_debug;
}
/*@}*/

/** Name of configuration file.
*/
static char configFileName[ MAXPATHLEN ];



/** @defgroup loops	Loop conditions. */
/*@{*/
/** Flag: Can continue in inner loop.
*/
static VOLATILE int ccInnerLoop = 1;

/** Flag: Can continue in outer loop.
*/
static VOLATILE int ccOuterLoop = 1;
/*@}*/


int
useraud_get_outer_loop DK_P0()
{
  return ccOuterLoop;
}

/** @defgroup signal Signal handling. */
/*@{*/
/** Flag: SIGPIPE found.
*/
static VOLATILE int haveSigpipe = 0;

/**	Signal handler for SIGTERM.
	@param	signo	Signal number.
*/
static
dk_signal_ret_t
handler_sigterm DK_P1(int,signo) {
  dksignal_refresh(signo, handler_sigterm);
  ccOuterLoop = ccInnerLoop = 0;	
  dksignal_return(0);
}

/**	Signal handler for SIGINT.
	@param	signo	Signal number.
*/
static
dk_signal_ret_t
handler_sigint DK_P1(int,signo) {
  dksignal_refresh(signo, handler_sigint);
  ccOuterLoop = ccInnerLoop = 0;	
  dksignal_return(0);
}

/**	Signal handler for SIGPIPE.
	@param	signo	Signal number.
*/
static
dk_signal_ret_t
handler_sigpipe DK_P1(int,signo) {
  dksignal_refresh(signo, handler_sigpipe);
  haveSigpipe = 1;			
  dksignal_return(0);
}

/**	Signal handler for SIGHUP.
	@param	signo	Signal number.
*/
static
dk_signal_ret_t
handler_sighup DK_P1(int,signo) {
  dksignal_refresh(signo, handler_sighup);
  ccInnerLoop = 0;			
  dksignal_return(0);
}
/*@}*/



/** @defgroup protosession	Protocol session. */
/*@{*/


/**	Save cookie sitting in t1.
	@param	uac		UAC structure.
	@param	username	Name of users for cookie.
	@param	ip		IP address as text.
	@return	1 on success, 0 on error.
*/
static
int
save_cookie DK_P3(UAC *,uac, char *,username, char *,ip) {
  int back = 0;
  char buffer[64];
  time_t	tim1, tim2;
  time(&tim1); tim2 = (time_t)0UL;
  if(uac->ttl_cookie) {
    tim2 = tim1 + (time_t)(uac->ttl_cookie);
  }
  sprintf(buffer, "%lu", (unsigned long)tim2);
  if((strlen(buffer) + strlen(username) + 1) < sizeof(t2)) {
    sprintf(ol, "w:%s:%s", ip, t1);
    sprintf(t2, "%s:%s", username, buffer);
    if(dksdbi_string_store(uac->sdbi, ol, t2, 0)) {
      back = 1;
    }
  }
  return back;
}



/**	Find user.
	@param	uac	UAC structure.
	@param	n	User logname.
	@param	det	Flag: Detailed information wanted.
*/
static
UAU *
find_user DK_P3(UAC *,uac, char *,n, int,det) {
  UAU		*back	= NULL;		/* Function result. */
  UAB		*uab	= NULL;		/* Backend. */
  UAB_API	 a;
  UAP		*uap;
  UAP		*uap2;
  int		cc	= 1;		/* Flag: Can continue. */
  int		ex	= 0;		/* Flag: User excluded. */
  int		he	= 0;		/* Flag: Have error. */
  
  if((uac->s_be) && (uac->i_be)) {
    dksto_it_reset(uac->i_be);
    while((back == NULL) && (cc) && ccOuterLoop) {
      cc = 0;
      uab = (UAB *)dksto_it_next(uac->i_be);
      if(uab) {
        cc = 1;
	if((uab->s_ex) && (uab->i_ex)) {
	  if(dksto_it_find_like(uab->i_ex, (void *)n, 0)) {
	    ex = 1;
	  }
	}
	if(!ex) {
	  uabapi_init(&a);
	  a.a.c = UA_API_GET_USER;
	  a.a.v = n;
	  a.a.f = (det ? 1 : 0);
	  if(uab->f) {
	    (*(uab->f))(uac, (void *)uab, &a);
	    if(a.r.s) {
	      back = a.r.u;
	      he = 0;
	      if((uab->s_a) && (uab->i_a)) {
	        if(!(back->s_p)) {
		  back->s_p = dksto_open(0);
		  if(back->s_p) { 
		    back->i_p = dksto_it_open(back->s_p);
		    if(back->i_p) {
		      dksto_it_reset(uab->i_a);
		      while((uap = (UAP *)dksto_it_next(uab->i_a)) != NULL) {
		        uap2 =
			uau_property_new(uap->major, uap->minor, uap->value);
			if(uap2) {
			  if(!dksto_add(back->s_p, (void *)uap2)) {
			    he = 1;
			    uau_property_delete(uap2);
			  }
			} else {
			  he = 1;
			}
		      }
		    } else {
		      he = 1;
		    }
		  } else {
		    he = 1;
		  }
		}
	      }
	      if(he) {
	        /* uau_delete(back); back = NULL; */
		ualog_1(uac, DK_LOG_LEVEL_ERROR, 14);
	      }
	    } else {
	      if(a.r.u) { uau_delete(a.r.u); }
	    }
	  }
	}
      }
    }
  } 
  return back;
}



/**	Write one output line (prepared in ol).
	@param	uac	UAC structure.
*/
static
void
write_output_line DK_P1(UAC *,uac) {
  size_t lgt;
  ssize_t res;
  
  if(!haveSigpipe) {
    lgt = 1 + strlen(ol);
    ol[lgt - 1] = '\n';
    res = send(uac->sock, ol, lgt, 0);
  } 
}



/**	Remove cookie.
	@param	uac	UAC structure.
	@param	args	IP address, cookie.
*/
static
void
remove_cookie DK_P2(UAC *,uac, char *,args) {
  char *p1;
  char *p2;
  char *p3;
  p1 = args;
  p2 = dkstr_next(p1, NULL);
  if(p2) {
    p3 = dkstr_next(p2, NULL);
    if(p3 == NULL) {
      if((strlen(p1) + strlen(p2) + 3) < sizeof(ol)) {
        sprintf(ol, "w:%s:%s", p1, p2);
	dksdbi_string_delete(uac->sdbi, ol);
	rc = UA_RC_SUCCESS;
	sprintf(ol, "%03d", rc);
	write_output_line(uac);
      }
    }
  }
}


/**	Send details about a user as part of the response.
	@param	uac	UAC structure.
	@param	uau	User to handle.
	@param	cook	Flag: Create and send a cookie.
*/
static
void
report_user_details DK_P3(UAC *,uac, UAU *,uau, int,cook) {
  char buffer[64];
  char b2[64];
  UAG *g;
  UAP *uap;
  /* UA_RC_ADDITIONAL_DATA */
  if(uau->user_name) {	/* 500 0 0 */
    if((8 + strlen(uau->user_name)) < sizeof(ol)) {
      sprintf(ol, "%d 0 0 %s", UA_RC_ADDITIONAL_DATA, uau->user_name);
      write_output_line(uac);
    }
  }
  if(uau->gecos) {	/* 500 0 1 */
    if((8 + strlen(uau->gecos)) < sizeof(ol)) {
      sprintf(ol, "%d 0 1 %s", UA_RC_ADDITIONAL_DATA, uau->gecos);
      write_output_line(uac);
    }
  }
  if(uau->pg_name) {	/* 500 0 2 */
    sprintf(buffer, "%lu", (unsigned long)(uau->primary_group));
    if((9 + strlen(uau->pg_name) + strlen(buffer)) < sizeof(ol)) {
      sprintf(ol, "%d 0 2 %s %s", UA_RC_ADDITIONAL_DATA, uau->pg_name, buffer);
      write_output_line(uac);
    }
  }
  if((uau->s_g) && (uau->i_g)) {
    dksto_it_reset(uau->i_g);
    while(((g = (UAG *)dksto_it_next(uau->i_g)) != NULL) && ccOuterLoop) {
      if(g->n) {	/* 500 0 3 */
        sprintf(buffer, "%lu", (unsigned long)(g->g));
	if((9 + strlen(g->n) + strlen(buffer)) < sizeof(ol)) {
	  sprintf(ol, "%d 0 3 %s %s", UA_RC_ADDITIONAL_DATA, g->n, buffer);
	  write_output_line(uac);
	}
      }
    }
  }
  if((uau->s_p) && (uau->i_p)) {
    dksto_it_reset(uau->i_p);
    while((uap = (UAP *)dksto_it_next(uau->i_p)) != NULL) {
      if(uap->value) {
        sprintf(buffer, "%d", uap->major);
        sprintf(b2, "%d", uap->minor);
	if((strlen(buffer) + strlen(b2) + strlen(uap->value) + 6) < sizeof(ol))
	{
	  sprintf(
	    ol, "%d %s %s %s",
	    UA_RC_ADDITIONAL_DATA, buffer, b2, uap->value
	  );
	  write_output_line(uac);
	}
      }
    }
  }
  if(cook) {		/* 500 0 4 */
    sprintf(ol, "%d 0 4 %s", UA_RC_ADDITIONAL_DATA, t1);
    write_output_line(uac);
  }
  sprintf(ol, "%d", UA_RC_FINAL_LINE);
  write_output_line(uac);
}


/**	Validate user name and password.
	@param	uac	UAC structure.
	@param	args	Logname, password and IP address (optional).
	@param	det	Flag: Detailed response on success.
	@param	cook	Flag: Create and send cookie.
*/
static
void
logname_password DK_P4(UAC *,uac, char *,args, int,det, int,cook) {
  char *p1;		/* Logname. */
  char *p2;		/* Password. */
  char *p3;		/* IP address. */
  char *p4;		/* Nothing. */
  UAU	*uau;		/* User data. */
  int	hc;		/* Flag: Have cookie. */
  size_t	cl;	/* Cookie length. */
  
  p1 = args; p2 = p3 = p4 = NULL;
  p2 = dkstr_next(p1, NULL);
  if(p2) {
    p3 = dkstr_next(p2, NULL);
    if(p3) {
      p4 = dkstr_next(p3, NULL);
    }
  }
  if((p1) && (p2) && (cook ? ((p3) && (p4 == NULL)) : (p3 == NULL))) {
    rc = UA_RC_ERR_NO_SUCH_USER;		
    uau = find_user(uac, p1, det);
    if(uau) {					
      if(uau->pw_hash) {		
        rc = UA_RC_ERR_WRONG_CREDENTIALS;
	if(strlen(uau->pw_hash) > uau->sl) {	
	  if(uau->sl < sizeof(t1)) {		
	    strcpy(t1, uau->pw_hash);
	    t1[uau->sl] = '\0';
	    if(uatcs_one_hash(uac, ol, sizeof(ol), p2, t1, uau->ht)) {
	      
	      if(strcmp(uau->pw_hash, ol) == 0) {	
	        if(cook) {
		  hc = 0;
		  cl = sizeof(ol) - 9 - strlen(p3);
		  if(cl > (sizeof(ol) - 5)) {
		    cl = sizeof(ol) - 5;
		  }
		  if(uac->lgt_cookie) {
		    if(cl > uac->lgt_cookie) {
		      cl = uac->lgt_cookie;
		    }
		  }
		  cl = 4 * cl / 5 - 1;
		  if(cl > 0) {
		    if(RAND_bytes((unsigned char *)ol, cl) == 1) {
		      hc = 1;
		      dkenc_bin_to_ra85(t1, sizeof(t1), ol, cl);
		      if(!save_cookie(uac, p1, p3)) {
		        hc = 0;
		      }
		    }
		  }
		  if(det) {
		    rc = UA_RC_SUCCESS_FULL;
		    if(hc) rc = UA_RC_SUCCESS_FULL_WITH_COOKIE;
		    sprintf(ol, "%03d", rc);
		    write_output_line(uac);
		    report_user_details(uac, uau, hc);
		  } else {
		    if(hc) {
		      rc = UA_RC_SUCCESS_COOKIE;
		      sprintf(ol, "%03d %s", rc, t1);
		    } else {
		      rc = UA_RC_SUCCESS;
		      sprintf(ol, "%03d", rc);
		    }
		    write_output_line(uac);
		  }
		} else {
		  if(det) {
		    rc = UA_RC_SUCCESS_FULL;
		    sprintf(ol, "%03d", rc);
		    write_output_line(uac);
		    report_user_details(uac, uau, 0);
		  } else {
		    rc = UA_RC_SUCCESS;
		    sprintf(ol, "%03d", rc);
		    write_output_line(uac);
		  }
		}
	      }
	    } else {
	    }
	  }
	}
      }
      uau_delete(uau); uau = NULL;
    } else {		
    }
  } else {		
  }
  if(rc == UA_RC_ERR_NO_SUCH_USER) {
    if(!(uac->f_no_such)) {
      rc = UA_RC_ERR_WRONG_CREDENTIALS;
    }
  } 
}



/**	Handle IP address and cookie.
	@param	uac	UAC structure.
	@param	args	IP address and cookie.
	@param	det	Flag: Send detailed response.
*/
static
void
ip_cookie DK_P3(UAC *,uac, char *,args, int,det) {
  char *p1;		/* IP address. */
  char *p2;		/* Cookie. */
  char *p3;		/* Time entry in cookie db value. */
  unsigned long ul;	/* Time entry in cookie db value. */
  time_t ct;		/* Current time. */
  UAU *u;		/* User information. */
  size_t sl;		/* String length of user name. */
  char buffer[64];	/* Buffer for new expiration timestamp. */
  
  p1 = args;
  p2 = dkstr_next(p1, NULL);
  if(p2) {				
    p3 = dkstr_next(p2, NULL);
    if(p3 == NULL) {			
      if((strlen(p1) + strlen(p2) + 3) < sizeof(ol)) {
        rc = UA_RC_ERR_NO_SUCH_COOKIE;	
        sprintf(ol, "w:%s:%s", p1, p2);
	if(dksdbi_string_fetch(uac->sdbi, ol, t1, sizeof(t1))) {
	  p3 = dkstr_chr(t1, ':');	
	  if(p3) {			
	    *(p3++) = '\0';
	    sl = strlen(t1);
	    if(((sl + 4) < sizeof(ol)) && (sl > 0)) {	
	      if(sscanf(p3, "%lu", &ul) == 1) {		
	        time(&ct);
	        if((ul == 0UL) || ((unsigned long)ct <= ul)) {
	          u = find_user(uac, t1, det);		
		  if(u) {				
		    if(uac->ttl_cookie) {
		      ul = (unsigned long)ct + uac->ttl_cookie;
		    } else {
		      ul = 0UL;
		    }			
		    sprintf(buffer, "%lu", ul);
		    if(u->user_name) {
		      if((strlen(u->user_name)+strlen(buffer)+2) < sizeof(t1))
		      { 
		        sprintf(t1, "%s:%s", u->user_name, buffer);
			dksdbi_string_store(uac->sdbi, ol, t1, 0);
			strcpy(t1, u->user_name);
		      }
		    }
		    rc = UA_RC_SUCCESS_LOGNAME;
		    if(det) { rc = UA_RC_SUCCESS_COOKIE_FULL; }
		    sprintf(ol, "%03d %s", rc, t1);
		    write_output_line(uac);
		    if(det) {		
		      report_user_details(uac, u, 0);
		    }
		    uau_delete(u);
		  }
	        } else {
	          rc = UA_RC_ERR_INACTIVE_TOO_LONG;
	        }
	      }
	    }
	  }
	}
	if(rc >= 400) {		
	  dksdbi_string_delete(uac->sdbi, ol);
	}
      }
    }
  } 
}



/**	Create a hash for a faked user and save it in the database.
	@param	uac	UAC structure.
	@param	be	Backend.
	@return	1 on success, 0 on error.
*/
static
int
create_faked_hash DK_P2(UAC *,uac, UAB *,be) {
  int back = 0;	/* Function result. */
  int i;
  char unb[16];
  unsigned x;
  
  if(uatcs_create_salt(uac, t3, sizeof(t3), be->ht, be->st)) {
    back = strlen(t3);	
    for(i = 0; i < 8; i++) {
      if(RAND_bytes((unsigned char *)(&x), sizeof(x)) == 1) {
        unb[i] = uatcs_get_alnum(x);
      } else {
        back = 0;
      }
    }
    unb[i] = '\0';	
    if(back) {
      if(!uatcs_one_hash(uac, t1, sizeof(t1), unb, t3, be->ht)) {
        back = 0;
      } else {		
      }
    }
  } 
  return back;
}



/**	Handle logname and algorithms list.
	@param	uac	UAC structure.
	@param	args	Logname and algorithms list.
*/
static
void
logname_algorithms DK_P2(UAC *,uac, char *,args) {
  char slb[64];		/* Buffer for salt length 1. */
  char sl2[64];		/* Buffer for salt length / timestamp. */
  char *p1;
  char *p2;
  char *p3;
  char *pst1;		/* Salt type 1. */
  char *pst2;		/* Salt type 2. */
  UAB *be;
  UAU *u;
  int	ht;		/* Hash types supported by client. */
  size_t	ll;	/* Line length needed, */
  size_t	lgt;	/* Hash length. */
  int	ht2, ht2c, ht2t, i, htori;
  time_t	ct;
  unsigned long	ul;
  time(&ct);
  htori = 0;
  p1 = args;
  p2 = dkstr_next(p1, NULL);
  if(p2) {				
    p3 = dkstr_next(p2, NULL);
    if(p3 == NULL) {			
      htori = ht = uatcs_all_hash_types(p2);
      if(ht) {				
        rc = UA_RC_ERR_NO_SUCH_USER;	
        u = find_user(uac, p1, 0);
        if(u) {				
	  if(u->user_name) {		
	    ht &= (uac->hash_types | u->ht);
            rc = UA_RC_ERR_INTERNAL;
	    ht2t = USERAUD_HASH_MAXVAL; ht2c = ht2 = 0;
	    while(ht2t > 0) {
	      if(ht & ht2t) {
	        if(ht2t != u->ht) {
	          ht2 = ht2t; ht2t = 0;
	        } else {
	          ht2c = ht2t;
	        }
	      }
	      ht2t = ht2t / 2;
	    }
	    if(ht2 == 0) {
	      ht2 = ht2c;
	    }
	    if(ht2) {			
	      if(uatcs_create_salt(uac, t2, sizeof(t2), ht2, u->st)) {
	        
		pst1 = uatcs_get_hash_name(u->ht);
		pst2 = uatcs_get_hash_name(ht2);
		if((pst1) && (pst2)) {	
		  sprintf(slb, "%d", u->sl);
		  ll = 7 + strlen(pst1) + strlen(slb) + strlen(pst2) + u->sl
		       + strlen(t2);
		  if(ll < sizeof(ol)) {		
                    ll = 6 + strlen(u->user_name) +strlen(pst1) + strlen(slb)
		         + strlen(pst2) + u->sl + strlen(t2);
		    if(ll < sizeof(t3)) {
		      for(i = 0; i < u->sl; i++) { t1[i] = (u->pw_hash)[i]; }
		      t1[i] = '\0';
		      sprintf(ol, "203 %s:%s,%s %s%s", pst1, slb,pst2, t1, t2);
		      
		      sprintf(
		        t3, "c:%s:%s:%s,%s %s%s",
			u->user_name, pst1, slb, pst2, t1, t2
		      ); 
		      sprintf(
		        t1, "r:%lu",
			((uac->ttl_challenge)
			 ? ((unsigned long)ct + uac->ttl_challenge) : 0UL)
		      ); 
		      if(dksdbi_string_store(uac->sdbi, t3, t1, 0)) {
		        rc = 203;	
			write_output_line(uac);
		      }
		    }
		  }
		}
	      }
	    } else {
	      rc = UA_RC_ERR_ALGORITHM_OTHERS;
	    }
	  } else {
	    ht &= uac->hash_types;
	  }
	  uau_delete(u);
	} else {
	  ht &= uac->hash_types;
	}
      } else {
        ht &= uac->hash_types;
        rc = UA_RC_ERR_ALGORITHM_OTHERS;
      }
    }
  }
  if(rc == UA_RC_ERR_NO_SUCH_USER) {
    if(!(uac->f_no_such)) {	
      rc = UA_RC_ERR_INTERNAL;
      t1[0] = t2[0] = t3[0] = '\0'; lgt = 0;
      dksto_it_reset(uac->i_be);
      be = (UAB *)dksto_it_next(uac->i_be);
      if(be) {			
        ht2t = USERAUD_HASH_MAXVAL; ht2c = ht2 = 0;
	while(ht2t > 0) {
	  if(htori & ht2t) {
	    if(ht2t != be->ht) {
	      ht2 = ht2t; ht2t = 0;
	    } else {
	      ht2c = ht2t;
	    }
	  }
	  ht2t = ht2t / 2;
	}
	if(ht2 == 0) { ht2 = ht2c; }
	if(ht2) {		
          /*
	    create_faked_hash:
	    t2: DB key for faked hash
	    - get faked hash if possible, expand time ... or ...
	    - build faked hash ... and ...
	    t3: faked salt
	    t1: faked hash
	    - save faked hash
	    - find method for second hash step
	    - create second salt in t2
	    t2: second salt
	    - create entire salt in t3
	    t3: challenge (entire salt)
	    - create challenge type in t2
	    t2: challenge type
	    - create DB key in t1
	    - create DB value in ol
	    t1: DB key
	    ol: DB value
	    - save DB entry
	    - create output in ol
	    ol: Output line
          */
          if((strlen(p1) + 2) < sizeof(t2)) {	
            sprintf(t2, "f:%s", p1);		
	    if(dksdbi_string_fetch(uac->sdbi, t2, t1, sizeof(t1))) {
	      p2 = dkstr_chr(t1, ' ');	
	      if(p2) {
	        *(p2++) = '\0';
	        if(sscanf(p2, "%lu", &ul) == 1) {	
	          if((ul == 0UL) || (ul >= (unsigned long)ct)) {	
	            sprintf(
		      slb, " %lu",
		      ((uac->ttl_salt) ? ((unsigned long)ct + uac->ttl_salt) : 0UL)
		    );
		    if((strlen(t1) + strlen(slb)) < sizeof(t1)) {
		      strcat(t1, slb);		
		      if(dksdbi_string_store(uac->sdbi, t2, t1, 0)) {
		        p2--; *p2 = '\0';	
		        p2 = dkstr_chr(t1, ':');
		        if(p2) {		
		          *(p2++) = '\0';
		          if(sscanf(t1, "%lu", &ul) == 1) {
		            lgt = (size_t)ul;	
			    p3 = t1;
			    while(*p2) {
			      *(p3++) = *(p2++);
			    } *p3 = '\0';	
		          } else { t1[0] = '\0'; }
		        } else { t1[0] = '\0'; }
		      } else { t1[0] = '\0'; }
		    } else { t1[0] = '\0'; }
	          } else { t1[0] = '\0'; }
	        } else { t1[0] = '\0'; }
	      } else { t1[0] = '\0'; }
	    } else { t1[0] = '\0'; }
            if((t1[0] == '\0') || (lgt == 0)) {	
	      lgt = create_faked_hash(uac, be);
	      if(lgt > 0) {			
	        sprintf(slb, "%lu", (unsigned long)lgt);
	        sprintf(
	          sl2, "%lu",
		  ((uac->ttl_salt) ? ((unsigned long)ct + uac->ttl_salt) : 0UL)
	        );
	        ll = strlen(slb) + strlen(t1) + strlen(sl2) + 2;
	        if(ll < sizeof(t3)) {		
	          sprintf(t3, "%s:%s %s", slb, t1, sl2);
		  if(!dksdbi_string_store(uac->sdbi, t2, t3, 0)) {
		    t1[0] = '\0';		
		  }
	        } else { t1[0] = '\0'; }
	      } else { t1[0] = '\0'; }
            }
            if((t1[0] != '\0') && (lgt > 0)) {	
	      if(uatcs_create_salt(uac, t2, sizeof(t2), ht2, be->st)) { 
	        if((lgt + strlen(t2)) < sizeof(t3)) {	
	          for(i = 0; i < lgt; i++) {
		    t3[i] = t1[i];
		  } t3[i] = '\0';
		  strcat(t3, t2);			
		  pst1 = uatcs_get_hash_name(be->ht);
		  pst2 = uatcs_get_hash_name(ht2);
		  if((pst1) && (pst2)) {		
		    sprintf(slb, "%lu", (unsigned long)lgt);
		    ll = strlen(pst1) + strlen(pst2) + strlen(slb) + 2;
		    if(ll < sizeof(t2)) {		
		      sprintf(t2, "%s:%s,%s", pst1, slb, pst2);	
		      ll = strlen(p1) + strlen(t2) + strlen(t3) + 4;
		      if(ll < sizeof(t1)) {		
		        ll = strlen(t2) + strlen(t3) + 5;
			if(ll < sizeof(ol)) {		
			  sprintf(t1, "c:%s:%s %s", p1, t2, t3);	
			  sprintf(
			    ol, "f:%lu",
			    ((uac->ttl_challenge) ? ((unsigned long)ct + uac->ttl_challenge) : 0UL)
			  );				
			  if(dksdbi_string_store(uac->sdbi, t1, ol, 0)) {	
			    rc = UA_RC_SUCCESS_CHALLENGE;
			    sprintf(ol, "203 %s %s", t2, t3);	
			    write_output_line(uac);		
			  }
			}
		      }
		    }
		  }
	        }
	      }
            }
          }
	}
      }
    }
  }
}



/**	Handle response.
	@param	uac	UAC structure.
	@param	args	Logname, challenge type, challenge and response.
	@param	det	Flag: Send detailed information.
*/
static
void
response DK_P3(UAC *,uac, char *,args, int,det) {
  char 		*p1;		/* Logname. */
  char		*p2;		/* Challenge type. */
  char		*p3;		/* challenge. */
  char		*p4;		/* Response. */
  char		*p5;		/* Must be NULL. */
  size_t	 ll;		/* Line length. */
  unsigned long	 ul;		/* Timestamp from DB entry. */
  time_t	 ct;		/* Current time. */
  UAU		*u;		/* User data. */
  
  p1 = args; p2 = p3 = p4 = p5 = NULL;
  p2 = dkstr_next(p1, NULL);
  if(p2) {
    p3 = dkstr_next(p2, NULL);
    if(p3) {
      p4 = dkstr_next(p3, NULL);
      if(p4) {
        p5 = dkstr_next(p4, NULL);
      }
    }
  }
  if((p1) && (p2) && (p3) && (p4) && (!(p5))) {
    rc = UA_RC_ERR_NO_SUCH_CHALLENGE;
    ll = strlen(p1) + strlen(p2) + strlen(p3) + 4;
    if(ll < sizeof(t1)) {	/* NEVER CHANGE t1 IN THIS FUNCTION */
      sprintf(t1, "c:%s:%s %s", p1, p2, p3);
      if(dksdbi_string_fetch(uac->sdbi, t1, t2, sizeof(t2))) {
	if(t2[0] == 'f') {	/* Faked challenge for non-existing user. */
	  rc = UA_RC_ERR_WRONG_CREDENTIALS;
	} else {		/* Real user. */
          p5 = dkstr_chr(t2, ':');
	  if(p5) {
	    *(p5++) = '\0';
	    time(&ct);
	    if(sscanf(p5, "%lu", &ul) == 1) {
	      if((ul == 0UL) || ((unsigned long)ct <= ul)) {
	        rc = UA_RC_ERR_WRONG_CREDENTIALS;
		u = find_user(uac, p1, det);
		if(u) {
		  if(u->sl > 0) {
		    if(u->pw_hash) {
		      if(strlen(u->pw_hash) > u->sl) {
		        if(strlen(p3) > u->sl) {
			  if(strncmp(u->pw_hash, p3, u->sl) == 0) { 
			    if(uatcs_apply_challenge(uac, p2, p3, u->pw_hash, t3, sizeof(t3), 0)) {
			      if(strcmp(t3, p4) == 0) {
			        if(det) {
				  rc = UA_RC_SUCCESS_FULL;
				  sprintf(ol, "%03d", rc);
				  write_output_line(uac);
				  report_user_details(uac, u, 0);
				} else {
				  rc = UA_RC_SUCCESS;
				  sprintf(ol, "%03d", rc);
				  write_output_line(uac);
				}
			      }
			    }
			  }
			}
		      }
		    }
		  }
		  uau_delete(u);
		}
	      } else {
	        rc = UA_RC_ERR_CHALLENGE_TIMEOUT;
	      }
	    }
	  }
	}
	dksdbi_string_delete(uac->sdbi, t1);
      }
    }
  }
  
}



/**	Handle 110/111 requests to verify a user name.
	@param	uac	UAC structure.
	@param	un	User name to verify.
	@param	det	Flag: Send details.
*/
static
void
username_test DK_P3(UAC *,uac, char *,un, int,det) {
  UAU *u;
  rc = UA_RC_ERR_NO_SUCH_USER;
  if(uac->username_test) {
    u = find_user(uac, un, det);
    if(u) {
      if(det) {
        rc = UA_RC_SUCCESS_FULL;
	sprintf(ol, "%d", rc);
	write_output_line(uac);
	report_user_details(uac, u, 0);
      } else {
        rc = UA_RC_SUCCESS;
	sprintf(ol, "%d", rc);
	write_output_line(uac);
      }
    }
  }
}



/**	Do one session (read one request line, send answer).
	@param	uac	UAC structure.
*/
static
void
run_session DK_P1(UAC *,uac) {
  ssize_t	szrd;
  int		opcode = 0;
  char		*p1;
  char		*p2;
  
  haveSigpipe = 0;	rc = UA_RC_ERR_PROTOCOL;
  szrd = recv(uac->sock, il, sizeof(il), 0);
  if(szrd > 0) {			
    il[sizeof(il) - 1] = '\0';
    il[(szrd >= sizeof(il)) ? sizeof(il) - 1 : szrd] = '\0';
    p1 = dkstr_start(il, NULL);		
    if(p1) {				
      p2 = dkstr_next(p1, NULL);
      if(p2) {				
        if(sscanf(p1, "%d", &opcode) == 1) {	
	  switch(opcode) {
	    case UA_RQ_LOGNAME_PASSWORD: {
	      logname_password(uac, p2, 0, 0);
	    } break;
	    case UA_RQ_LOGNAME_PASSWORD_LONG: {
	      logname_password(uac, p2, 1, 0);
	    } break;
	    case UA_RQ_LOGNAME_PASSWORD_IP: {
	      logname_password(uac, p2, 0, 1);
	    } break;
	    case UA_RQ_LOGNAME_PASSWORD_IP_LONG: {
	      logname_password(uac, p2, 1, 1);
	    } break;
	    case UA_RQ_IP_COOKIE: {
	      ip_cookie(uac, p2, 0);
	    } break;
	    case UA_RQ_IP_COOKIE_LONG: {
	      ip_cookie(uac, p2, 1);
	    } break;
	    case UA_RQ_GET_CHALLENGE: {
	      logname_algorithms(uac, p2);
	    } break;
	    case UA_RQ_RESPONSE: {
	      response(uac, p2, 0);
	    } break;
	    case UA_RQ_RESPONSE_LONG: {
	      response(uac, p2, 1);
	    } break;
	    case UA_RQ_LOGOUT: {
	      remove_cookie(uac, p2);
	    } break;
	    case UA_RQ_VERIFY_LOGNAME: {
	      username_test(uac, p2, 0);
	    } break;
	    case UA_RQ_VERIFY_DETAILED: {
	      username_test(uac, p2, 1);
	    } break;
	  }
	}
      }
    }
    if((rc >= 400) && (rc < 500)) {
      if(rc == UA_RC_ERR_NO_SUCH_USER) {
        if(!(uac->f_no_such)) {
	  if((opcode != 110) && (opcode != 111)) {
	    rc = UA_RC_ERR_WRONG_CREDENTIALS;
	  }
	}
      }
      sprintf(ol, "%03d", rc);
      write_output_line(uac);
    }
  } else {
    /* No input. */
  }
  
}

/*@}*/




/** @defgroup initialize	Initialize and set up. */
/*@{*/
/**	Setup when running the first time.
	@param	uac	UAC structure.
	@return	Flag to indicate success.
*/
static
int
run_first DK_P1(UAC *,uac) {
  int back = 1;
  uid_t nu = 0;
  gid_t ng = 0;
  int have_nu = 0;
  int have_ng = 0;
  struct passwd *pw;
  struct group * gr;
  char *p1;
  
  /* Find user and group. */
  nu = geteuid();
  ng = getegid();
  if(uac->run_as_user) {
    pw = uat_getpwnam(uac->run_as_user);
    if(pw) {
      nu = pw->pw_uid;
      have_nu = 1;
    } else {
      back = 0;		
      ualog_3(uac, DK_LOG_LEVEL_ERROR, 34, 35, uac->run_as_user);
    }
  }
  if(uac->run_as_group) {
    gr = uat_getgrnam(uac->run_as_group);
    if(gr) {
      ng = gr->gr_gid;
      have_ng = 1;
    } else {
      back = 0;		
      ualog_3(uac, DK_LOG_LEVEL_ERROR, 36, 35, uac->run_as_group);
    }
  }
  /* Create directory structure for log file, socket and database. */
  if(uac->dbname) {
    p1 = dkstr_chr(uac->dbname, ':');
    if(p1) {
      p1++;
      if(!uat_create_parent(uac, p1, nu, ng)) {
        back = 0;
	ualog_3(uac, DK_LOG_LEVEL_ERROR, 37, 38, p1);
      }
    } else {
      if(!uat_create_parent(uac, uac->dbname, nu, ng)) {
        back = 0;
	ualog_3(uac, DK_LOG_LEVEL_ERROR, 37, 38, uac->dbname);
      }
    }
  } else { back = 0; }
  if(!uat_create_parent(uac, uac->logname, nu, ng)) {
    back = 0;
    ualog_3(uac, DK_LOG_LEVEL_ERROR, 37, 38, uac->logname);
  }
  if(!uat_create_parent(uac, uac->sockname, nu, ng)) {
    back = 0;
    ualog_3(uac, DK_LOG_LEVEL_ERROR, 37, 38, uac->sockname);
  }
  /* Seed PRNG. */
  if(!uat_get_seed(uac)) {
    back = 0;
    ualog_1(uac, DK_LOG_LEVEL_ERROR, 39);
  }
  /* Change user and group. */
  if(have_ng) { setgid(ng); }
  if(have_nu) { setuid(nu); } 
  return back;
}



/**	Traverse data used when cleaning up the database.
*/
typedef struct {
  dk_storage_t	*s;	/**< Storage for key names to delete. */
  time_t	*t;	/**< Current time. */
} TD;



/**	Traversal function used when cleaning up the database.
	@param	o	Traversal object (a TD here).
	@param	kp	Pointer to DB entry key.
	@param	kl	Key length.
	@param	vp	Pointer to DB entry value.
	@param	vl	Value length.
	@return	0 on success, 1 on warnings (can continue), -1 on errors (exit).
*/
int
traverse_fct DK_P5(void *,o, void *,kp, size_t,kl, void *,vp, size_t,vl)
{
  int back = 0;
  TD *td;
  char *pe;
  char *t;
  unsigned long ul;
  
  if((kp) && (kl) && (vp) && (vl) && (o)) {
    td = (TD *)o;
    if((kl < sizeof(il)) && (vl < sizeof(ol))) {
      DK_MEMCPY(il, kp, kl);
      DK_MEMCPY(ol, vp, vl);
      il[sizeof(il) - 1] = '\0'; ol[sizeof(ol) - 1] = '\0';
      il[kl] = '\0'; ol[vl] = '\0'; 
      pe = dkstr_chr(ol, ((*il != 'f') ? ':' : ' '));
      if(pe) {
        pe++;
	if(sscanf(pe, "%lu", &ul) == 1) {
	  if(ul > 0UL) {	
	    if(ul < (unsigned long)(*(td->t))) {
	      t = dkstr_dup(il);	
	      if(t) {
	        if(!dksto_add(td->s, (void *)t)) {
		  dk_delete(t); t = NULL; back = 1;
		}
	      } else { back = 1; }
	      if(!t) {			
	      }
	    }
	  }
	}
      }
    }
  }
  if(!ccOuterLoop) { back = -1; } 
  return back;
}



/**	Check cleanup timestamp, do cleanup if necessary.
	@param	uac	UAC structure.
*/
static
void
cleanup_if_necessary DK_P1(UAC *,uac) {
  TD td;
  time_t ct;
  dk_storage_iterator_t	*i_k;
  char			*k;
  char			 buffer[64];
  unsigned long		 ndel = 0UL;
  time(&ct);
  
  if(((uac->last_cleanup + (time_t)(uac->sec_cleanup)) < ct)
    || (uac->last_cleanup == (time_t)0))
  {
    ualog_1(uac, DK_LOG_LEVEL_PROGRESS, 26);
    td.t = &ct;
    td.s = dksto_open(0);
    if(td.s) {					
      i_k = dksto_it_open(td.s);
      if(i_k) {					
        ndel = 0UL;
        ualog_1(uac, DK_LOG_LEVEL_PROGRESS, 28);
        if(dksdbi_traverse(uac->sdbi, (void *)(&td), traverse_fct)) {
	  
        } else {				
        }
        ualog_1(uac, DK_LOG_LEVEL_PROGRESS, 29);
        ualog_1(uac, DK_LOG_LEVEL_PROGRESS, 30);
        dksto_it_reset(i_k);
        while(((k = (char *)dksto_it_next(i_k)) != NULL) && (ccOuterLoop)) {
          dksdbi_string_delete(uac->sdbi, k); ndel++;	
        }
        ualog_1(uac, DK_LOG_LEVEL_PROGRESS, 31);
        dksto_it_reset(i_k);
        while((k = (char *)dksto_it_next(i_k)) != NULL) {
          dk_delete(k);
        }
        dksto_it_close(i_k);
      }
      dksto_close(td.s); td.s = NULL;
    }
    sprintf(buffer, "%lu", ndel);
    ualog_3(uac, DK_LOG_LEVEL_PROGRESS, 32, 33, buffer);
    ualog_1(uac, DK_LOG_LEVEL_PROGRESS, 27);
    time(&ct);
    uac->last_cleanup = ct;
  } 
}



/**	Run loops.
	The outer loop is left when receiving an INT or
	TERM signal or if an error occures.
	The inner loop is left when receiving a HUP
	signal, so we can reconfigure.
*/
static
void
run_loops DK_P0() {
  UAC			*uac;
  char			*cfgFileName;
  dk_signal_disp_t	disp_term = NULL;
  dk_signal_disp_t	disp_int  = NULL;
  dk_signal_disp_t	disp_pipe = NULL;
  time_t		confread;
  time_t		confmod;
  int		is_first = 1;
  int		have_seed = 0;
  int		sockListen;
  struct sockaddr_un	soun;
  
#line 1307 "useraud.ctr"

  
  disp_term = dksignal_set(SIGTERM,handler_sigterm);
  disp_int  = dksignal_set(SIGINT,handler_sigint);
  disp_pipe = dksignal_set(SIGPIPE,handler_sigpipe);
  ccOuterLoop = 1;
  while(ccOuterLoop) {
    cfgFileName = configFileName;
    if(*cfgFileName == '\0') {
      cfgFileName = uatcs_get_default_config_file_name();
    }
    confread = uat_modtime(cfgFileName);
    uac = uac_open(cfgFileName, 1);
    if(uac) {		
      ccInnerLoop = 1;
      if(is_first) {	
        is_first = 0;
	if(run_first(uac)) {
	  have_seed = 1;
	} else {
	  ccOuterLoop = 0;
	  ccInnerLoop = 0;
	}
      }
      if((uac->dbname) && (uac->sockname)) {
      if(strlen(uac->sockname) < 108) {
        uac->sdbi =
	dksdbi_open(uac->dbname,DK_SDBI_TYPE_AUTO,DK_SDBI_MODE_RDWR,0600,1024);
	if(uac->sdbi) {		
	  cleanup_if_necessary(uac);
	  dksf_remove_file(uac->sockname);
	  unlink(uac->sockname);
	  sockListen = socket(PF_UNIX, SOCK_STREAM, 0);
	  if(sockListen > -1) {
	    soun.sun_family = AF_UNIX;
	    strcpy(soun.sun_path, uac->sockname);
	    if(bind(sockListen, (struct sockaddr *)(&soun), SZSOUN) == 0) {
	      if(listen(sockListen, 5) == 0) {
		ualog_1(uac, DK_LOG_LEVEL_INFO, 56);
	        while(ccInnerLoop && ccOuterLoop) {
		  uac->sock = accept(sockListen, NULL, 0);
		  if(uac->sock > -1) {
		    if(ccOuterLoop) {
		      run_session(uac);
		    }
		    close(uac->sock); uac->sock = -1;
		  } else {
		    ccOuterLoop = 0;
		    if(errno != EINTR) {
		      ualog_1(uac, DK_LOG_LEVEL_ERROR, 48);
		    }
		  }
	          confmod = uat_modtime(cfgFileName);
	          if(confmod > confread) {	
	            ccInnerLoop = 0;
		    ualog_1(uac, DK_LOG_LEVEL_INFO, 58);
	          }
		  if(ccOuterLoop) {
		    cleanup_if_necessary(uac);
		  }
		}
		ualog_1(uac, DK_LOG_LEVEL_INFO, 57);
	      } else {
	        ccOuterLoop = 0;
		ualog_1(uac, DK_LOG_LEVEL_ERROR, 47);
	      }
	    } else {
	      ccOuterLoop = 0;
	      ualog_3(uac, DK_LOG_LEVEL_ERROR, 46, 43, uac->sockname);
	    }
	    close(sockListen);
	    dksf_remove_file(uac->sockname);
	    unlink(uac->sockname);
	  } else {
	    ccOuterLoop = 0;
	    ualog_1(uac, DK_LOG_LEVEL_ERROR, 45);
	  }
	  dksdbi_close(uac->sdbi);
	} else {		
	  ccOuterLoop = 0;
	  ualog_3(uac, DK_LOG_LEVEL_ERROR, 44, 43, uac->dbname);
	}
      } else {
	ccOuterLoop = 0;
	ualog_3(uac, DK_LOG_LEVEL_ERROR, 42, 43, uac->sockname);
      }
      } else {
        ccOuterLoop = 0;
	if(!(uac->dbname)) {
	  ualog_1(uac, DK_LOG_LEVEL_ERROR, 41);
	}
	if(!(uac->sockname)) {
	  ualog_1(uac, DK_LOG_LEVEL_ERROR, 40);
	}
      }
      if(!ccOuterLoop) {	
        if(have_seed) {
          uat_save_seed(uac);
        }
      }
      uac_close(uac);
    } else {		
      ccOuterLoop = 0;
    }
  }
  if(disp_pipe) { dksignal_set(SIGPIPE,disp_pipe); }
  if(disp_int)  { dksignal_set(SIGPIPE,disp_int);  }
  if(disp_term) { dksignal_set(SIGPIPE,disp_term); }
  
  
#line 1416 "useraud.ctr"

}



/**	Run loops in foreground (debug mode).
*/
static
void
run_loops_in_foreground DK_P0() {
  int	maxfiles;
  int	i;
  (void)chdir("/");
  umask(077);
  maxfiles = (int)dksf_get_maxfiles();
  for(i = 3; i < maxfiles; i++) { (void)close(i); }
  run_loops();
}



/**	Run loops as daemon.
*/
static
void
run_loops_as_daemon DK_P0() {
  int			maxfiles;
  int			i;
  dk_signal_disp_t	disp_hup = NULL;
  pid_t			newpid;
  (void)chdir("/");
  umask(077);
  maxfiles = (int)dksf_get_maxfiles();
  for(i = 0; i < maxfiles; i++) { (void)close(i); }
  newpid = fork();
  if(newpid == 0) {
#if DK_HAVE_SETSID
    setsid();
#else
#if DK_HAVE_SETPGRP
    setpgrp();
#endif
#endif
    disp_hup = dksignal_set(SIGHUP,handler_sighup);
    newpid = fork();
    if(newpid == 0) {
      dksf_write_pid_file(appname, 1);
      run_loops();
      dksf_write_pid_file(appname, 0);
    } else {
      if(newpid == ((pid_t)(-1))) {
	ualog_1(NULL, DK_LOG_LEVEL_ERROR, 15);
      }
    }
    if(disp_hup) { dksignal_set(SIGHUP,disp_hup); }
  } else {
    if(newpid == ((pid_t)(-1))) {
      fprintf(stderr, "ERROR: fork() failed!\n");
      fflush(stderr);
    }
  }
}
/*@}*/


/** @defgroup	main	Command line arguments checking. */
/*@{*/
/**	Check command line arguments.
	@param	argc	Number of command line arguments.
	@param	argv	Command line arguments array.
*/
static
void
check_arguments DK_P2(int,argc, char **,argv) {
  int i;		/* Index of current command line arg. */
  int f_filename;	/* Flag: Have file name. */
  char **lfdptr;	/* Traverse command line arguments. */
  char *ptr;		/* Current command line arg processed. */
  char *optr;		/* Original ptr. */
  lfdptr = argv; lfdptr++; i = 1;
  f_filename = 0;
  while(i < argc) {
    ptr = *lfdptr; optr = ptr;
    switch(*ptr) {
      case '-': {
        ptr++;
	switch(*ptr) {
	  case 'd': {
	    f_debug = 1; ualog_set_debug(1);
	  } break;
	  default: {
	    exval = 1;
	    fprintf(stderr, "ERROR: Unknown option \"%s\"!\n", optr);
	    fflush(stderr);
	  } break;
	}
      } break;
      default: {
        if(f_filename) {
	  exval = 1;
	  fprintf(stderr, "ERROR: Too many file names, only one allowed\n");
	  fflush(stderr);
	} else {
	  if(strlen(ptr) < sizeof(configFileName)) {
	    strcpy(configFileName, ptr); f_filename = 1;
	  } else {
	    exval = 1;
	    fprintf(stderr, "ERROR: Configuration file name too long!\n");
	    fflush(stderr);
	  }
	}
      } break;
    }
    lfdptr++; i++;
  }
}



/**	The main() function.
	@param	argc	Number of command line arguments.
	@param	argv	Command line arguments array.
	@return	0 on success, any other value on error.
*/
#if DK_HAVE_PROTOTYPES
int main(int argc, char *argv[])
#else
int main(argc, argv) int argc; char *argv[];
#endif
{
  configFileName[0] = '\0';
  check_arguments(argc, argv);
  if(exval == 0) {
    if(f_debug) {
      run_loops_in_foreground();
    } else {
      run_loops_as_daemon();
    }
  }
  exit(exval); return exval;
}
/*@}*/




