archie/prospero/server/dirsrv.c

1798 lines
54 KiB
C
Raw Permalink Normal View History

2024-05-27 16:13:40 +02:00
/*
* Copyright (c) 1992, 1993 by the University of Southern California
*
* For copying and distribution information, please see the file
* <usc-license.h>
*
* Written by bcn 1989 modified 1989-1992
* Modified by swa 7/28/92 to use qsscanf()
* Modified by swa 1992 to break individual commands into modules
* Modified by swa 1992 to support V5 protocol
* Modified by bcn 1/19/93 to take args from environment and command loop
* Modified by swa 11/6/93 to call p_initialize().
* Modified by swa Dec 93 to be multithreaded.
*/
#include <usc-license.h>
#include <stdio.h>
#include <sgtty.h>
#include <string.h>
#include <posix_signal.h> /* get our version */
#include <errno.h>
#include <unistd.h> /*SOLARIS: for "write" */
#include <stdlib.h> /*SOLARIS: qfor malloc, free etc */
#include <ardp.h>
#include <pserver.h> /* must precede psrv.h since psrv.h prototypes
check_krb_auth() only if
PSRV_KERBEROS defined.
*/
#include <psite.h>
#include <pfs.h>
#include <plog.h>
#include <pprot.h>
#include <psrv.h>
#include <perrno.h>
#include <pparse.h>
#include <pmachine.h>
#if defined(AIX) || defined(SCOUNIX)
#include <time.h>
#include <signal.h>
#endif
#ifdef PSRV_P_PASSWORD
#include <ppasswd.h>
#endif
#include "dirsrv.h"
#ifdef PSRV_ACCOUNT
#include <math.h>
#endif
/*#define MASTER_IS_ONLY_SUBTHREAD */ /* DEBUGGING ONLY */
#ifdef PFS_THREADS
/* #define DIRSRV_SUB_THREAD_COUNT 60 /* # of sub-threads we're using in
dirsrv. This is usually set to the default value below. However, you can
redefine this if you want to experiment with using different thread counts
(performance tuning).
*/
#ifndef DIRSRV_SUB_THREAD_COUNT
/* Unless the user overrides the value for DIRSRV_SUB_THREAD_COUNT, pick the
largest legal value. */
#define DIRSRV_SUB_THREAD_COUNT (P_MAX_NUM_THREADS - 1)
#endif
/* Must be less than P_MAX_NUM_THREADS, since need one thread for dirsrv. . */
#if DIRSRV_SUB_THREAD_COUNT >= P_MAX_NUM_THREADS
#error DIRSRV_SUB_THREAD_COUNT too big.
#endif
#ifdef MASTER_IS_ONLY_SUBTHREAD
#undef DIRSRV_SUB_THREAD_COUNT
#define DIRSRV_SUB_THREAD_COUNT 1
#endif
#endif
extern char *acltypes[];
/* To check for memory leaks */
/* There needs to be a common declarations file shared between the PFS library
and dirsrv.c. Some way for these declarations to not needt o be
duplicated. */
/* There needs to be a common declarations file shared between the ARDP library
and dirsrv.c. Some way for these declarations to not needt o be
duplicated. */
extern int vlink_count, pattrib_count, acl_count, pfile_count;
extern int rreq_count, ptext_count, string_count, token_count;
extern int pauth_count, opt_count, filter_count, p_object_count;
/* There needs to be a common declarations file shared between the PSRV library
and dirsrv.c. Some way for these declarations to not needt o be
duplicated. */
extern int vlink_max, pattrib_max, acl_max, pfile_max;
extern int rreq_max, ptext_max, string_max, token_max;
extern int pauth_max, opt_max, filter_max, p_object_max;
extern int filelock_open, filelock_open_max, filelock_sepwaits;
extern int filelock_secwaits;
#ifdef DIRECTORYCACHING
extern int cache_attempt, cache_can, cache_yes, dsrobject_fail;
#endif
/* In ardp.h */
extern int dnscache_count, dnscache_max /* , filelock_count, filelock_max */;
extern int filelock_open, filelock_open_max;
#ifdef PSRV_GOPHER_GW
/* There needs to be a common declarations file shared between the gopher_gw
library and dirsrv.c. Some way for these declarations to not needt o be
extern int dnscache_count, dnscache_max, alldnscache_count;
duplicated. */
extern int glink_count, glink_max;
#endif
#ifdef PSRV_WAIS_GW
/* There needs to be a common declarations file shared between the wais_gw
library and dirsrv.c. Some way for these declarations to not needt o be
duplicated. */
extern int waismsgbuff_count, waismsgbuff_max;
extern int ietftype_count, ietftype_max;
extern int waissource_count, waissource_max;
#endif
extern int pQlen;
static cmd_lookup();
VLINK check_fwd();
char *month_sname();
char *getenv();
static int auth_fail_reply(RREQ req, char formatstring[], ...);
static void setup_disc();
#ifdef PSRV_ACCOUNT
static int str_to_fp();
static int subtract_fp();
static int add_fp();
static int charge();
static char *acc_method_name();
struct acc_lookup {
int acc_method_num;
char *acc_method_name;
};
static struct acc_lookup acc_methods[] =
{
PFSA_CREDIT_CARD, "CREDIT_CARD",
0, 0
};
#endif
char prog[MAXPATHLEN];
int fault_count = 0; /* # of serious faults in the server -- # of
times it has had to be automatically
restarted. */
/* #define DIRSRV_EXPLAIN_LAST_RESTART to report on the last request made to
the server before it crashed. */
#ifdef DIRSRV_EXPLAIN_LAST_RESTART
char last_request[ARDP_PTXT_LEN_R] = "";
#endif
char *last_error = NULL;
char st_time_str[40];
int in_port = -1;
char *portname = NULL;
static int mflag = 0; /* Manual start of server */
int req_count = 0;
#ifdef SERVER_SUPPORT_V1
int v1_req_count = 0;
int crdir_count = 0;
#endif
int crlnk_count = 0;
int crobj_count = 0;
int dellnk_count = 0;
int eoi_count = 0;
int goi_count = 0;
int list_count = 0;
int lacl_count = 0;
int eli_count = 0;
int eacl_count = 0;
int status_count = 0;
int upddir_count = 0;
int parameter_count = 0;
char shadow[MAXPATHLEN] = PSRV_FSHADOW;
char security[MAXPATHLEN] = PSRV_FSECURITY;
char pfsdat[MAXPATHLEN] = PSRV_FSTORAGE;
char dirshadow[MAXPATHLEN] = DSHADOW;
char dircont[MAXPATHLEN] = DCONTENTS;
char *object_pool = NULL; /* set later. */
char root[MAXPATHLEN] = "";
char aftpdir[MAXPATHLEN] = "";
char afsdir[MAXPATHLEN] = "";
char *logfile_arg = NULL;
extern int p__server;
#ifdef PSRV_ARCHIE
extern int arch_prioritize_request();
#endif /* PSRV_ARCHIE */
struct db_entry db_prefixes[] = DATABASE_PREFIXES;
const int db_num_ents = sizeof(db_prefixes)/sizeof(db_prefixes[0]);
char hostname[MAXPATHLEN] = ""; /* Server's host name */
char hostwport[MAXPATHLEN+30] = ""; /* Host name w/ port if non-standard */
#ifdef PFS_THREADS
int free_subthread_count = DIRSRV_SUB_THREAD_COUNT;
int subthread_count = 0;
int subthread_max = 0;
#endif
static void dirsrv(RREQ req);
#if defined(AIX) || defined(SOLARIS)
void *bsdSignal(sig, fn)
int sig;
void *fn;
{
struct sigaction act, oact;
act.sa_handler = fn;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
if (sig == SIGALRM)
{
#ifdef SA_INTERRUPT
act.sa_flags |= SA_INTERRUPT; /* SunOS */
#endif
}
else
{
#ifdef SA_RESTART
act.sa_flags |= SA_RESTART; /* SVR4, 4.3+BSD */
#endif
}
if (sigaction(sig, &act, &oact) < 0)
{
return SIG_ERR;
}
return oact.sa_handler;
}
#endif
void
main(int argc, char *argv[])
{
char *cur_arg; /* For argument parsing */
int on = 1;
struct sockaddr_in from;
int port_no;
struct hostent *current_host;
int fromlen;
PTEXT pkt;
RREQ curr_req;
register int n;
int child;
int retval;
char *envv; /* Temp pointer to environment variable */
time_t now;
static int dirsrv_internal_error_handler();
extern int fseek(); /* RHS of assignment. */
extern int dQmaxlen;
/* Sets thread to master if necessary. */
p_initialize("sP", 0, (struct p_initialize_st *) NULL);
#ifdef PFS_THREADS
psrv_init_mutexes();
#ifdef PSRV_GOPHER_GW
gopher_gw_init_mutexes();
#endif
#ifdef PSRV_WAIS_GW
wais_gw_init_mutexes();
#endif
p_th_mutex_init(p_th_mutexP_PARAMETER_MOTD);
#endif
p_srv_check_acl_initialize_defaults();
#ifdef SHARED_PREFIXES
p_init_shared_prefixes(); /* psrv routine. */
#endif
umask(0);
/* These function-valued variables depend upon whether we're the */
/* server or the client. The default values are the client */
/* values; the server values are these. */
internal_error_handler = dirsrv_internal_error_handler;
qoprintf = srv_qoprintf;
stdio_fseek = fseek;
p__server = 1; /* We are the Server. */
strcpy(prog,argv[0]);
argc--;argv++;
if(envv = getenv("PSRV_ROOT"))
strcpy(root,envv);
#ifdef PSRV_ROOT
else strcpy(root,PSRV_ROOT);
#endif
if(envv = getenv("PSRV_FSHADOW"))
strcpy(shadow,envv);
if(envv = getenv("PSRV_FSECURITY"))
strcpy(security,envv);
if(envv = getenv("PSRV_FSTORAGE"))
strcpy(pfsdat,envv);
if(envv = getenv("PSRV_AFTPDIR"))
strcpy(aftpdir,envv);
#ifdef AFTPDIRECTORY
else strcpy(aftpdir,AFTPDIRECTORY);
#endif
if(envv = getenv("PSRV_AFSDIR"))
strcpy(afsdir,envv);
#ifdef AFSDIRECTORY
else strcpy(afsdir,AFSDIRECTORY);
#endif
if(envv = getenv("PSRV_HOSTNAME"))
strncpy(hostname,envv,sizeof(hostname));
#ifdef PSRV_HOSTNAME
else strncpy(hostname,PSRV_HOSTNAME,sizeof(hostname));
#else
else strncpy(hostname,myhostname(),sizeof(hostname));
#endif
ardp_hostname2addr_initcache();
ardp_hostname2addr(hostname,NULL);
#ifdef P_SITE_HOST
ardp_hostname2addr(P_SITE_HOST,NULL);
#endif /*P_SITE_HOST*/
#ifdef GOPHER_GW_NEARBY_GOPHER_SERVER
ardp_hostname2addr(GOPHER_GW_NEARBY_GOPHER_SERVER,NULL);
#endif /*GOPHER_GW_NEARBY_GOPHER_SERVER*/
object_pool = qsprintf_stcopyr(object_pool, "%s/%s", pfsdat, OBJECT_POOL);
if(envv = getenv("PSRV_LOGFILE"))
set_logfile(envv);
while (argc > 0 && **argv == '-') {
cur_arg = argv[0]+1;
while (*cur_arg) {
switch (*cur_arg++) {
case 'a': /* Set AFS directory */
if(*cur_arg) {
strncpy(afsdir,cur_arg,sizeof(afsdir));
cur_arg += strlen(cur_arg);
}
else if (argc > 1) {
strncpy(afsdir,argv[1],sizeof(afsdir));
argc--;argv++;
}
else strcpy(afsdir,"/afs");
break;
case 'E': /* Set last error */
if(*cur_arg) {
last_error = cur_arg;
cur_arg += strlen(cur_arg);
}
else if (argc > 1) {
last_error = argv[1];
argc--;argv++;
}
else goto parse_err;
break;
case 'f': /* Set anonymous ftp area */
if(*cur_arg) {
strncpy(aftpdir,cur_arg,sizeof(aftpdir));
cur_arg += strlen(cur_arg);
}
else if (argc > 1) {
strncpy(aftpdir,argv[1],sizeof(aftpdir));
argc--;argv++;
}
else goto parse_err;
break;
case 'F': /* Set fault count from next argument */
fault_count = -1;
if(*cur_arg && strchr("0123456789",*cur_arg)) {
sscanf(cur_arg,"%d",&fault_count);
cur_arg += strspn(cur_arg,"0123456789");
}
else if(argc > 1) {
retval = sscanf(argv[1],"%d",&fault_count);
if (retval == 1) {argc--;argv++;}
}
else goto parse_err;
break;
case 'h': /* Set hostname */
if(*cur_arg) {
strncpy(hostname,cur_arg,sizeof(hostname));
cur_arg += strlen(cur_arg);
}
else if (argc > 1) {
strncpy(hostname,argv[1],sizeof(hostname));
argc--;argv++;
}
else goto parse_err;
break;
case 'L': /* Set logfile */
if(*cur_arg) {
set_logfile(cur_arg);
logfile_arg = cur_arg;
cur_arg += strlen(cur_arg);
}
else if (argc > 1) {
set_logfile(argv[1]);
logfile_arg = cur_arg;
argc--;argv++;
}
else goto parse_err;
break;
case 'm': /* Run server from terminal */
mflag++;
break;
case 'n': { /* clear info from the ardp_doneQ; this affects
how much stuff is saved and the accuracy of
logging. */
extern int ardp_clear_doneQ_loginfo; /* in ardp_respond() */
ardp_clear_doneQ_loginfo = 1;
}
break;
/* If form of port identifier is a positive integer, then */
/* integer reperesents the file descriptor of a privileged */
/* port already open. If it is of any other form, it is */
/* a port identifier to be opened later by bind_port. At */
/* most one of each may be specified. If neither, then */
/* -p dirsrv is assumed. Because most clients don't use */
/* the privileged prospero port, for the time being dirsrv */
/* is assumed even if a file descriptor is specified as */
/* as no other port is specified. For port names, a name */
/* is looked up in the /etc/services files. If the first */
/* char is #, then what follows is the port number itself. */
case 'p': /* Port on which to listen */
in_port = -1;
if(*cur_arg) {
if(strchr("0123456789",*cur_arg)) {
sscanf(cur_arg,"%d",&in_port);
ardp_set_prvport(in_port);
cur_arg += strspn(cur_arg,"0123456789");
}
else {
portname = cur_arg;
cur_arg += strlen(cur_arg);
}
}
else if(argc > 1) {
if(strchr("0123456789",*argv[1])) {
sscanf(argv[1],"%d",&in_port);
ardp_set_prvport(in_port);
cur_arg += strspn(cur_arg,"0123456789");
}
else {
portname = argv[1];
}
argc--;argv++;
}
else goto parse_err;
break;
case 'r': /* Set root of tree to make available */
if(*cur_arg) {
strncpy(root,cur_arg,sizeof(root));
cur_arg += strlen(cur_arg);
}
else if (argc > 1) {
strncpy(root,argv[1],sizeof(root));
argc--;argv++;
}
else goto parse_err;
break;
case 'S': /* Set shadow hierarchy */
if(*cur_arg) {
strncpy(shadow,cur_arg,sizeof(shadow));
cur_arg += strlen(cur_arg);
}
else if (argc > 1) {
strncpy(shadow,argv[1],sizeof(shadow));
argc--;argv++;
}
else goto parse_err;
break;
case 's': /* Set security hierarchy */
if(*cur_arg) {
strncpy(security,cur_arg,sizeof(security));
cur_arg += strlen(cur_arg);
}
else if (argc > 1) {
strncpy(security,argv[1],sizeof(security));
argc--;argv++;
}
else goto parse_err;
break;
case 'T': /* Set storage hierarchy */
if(*cur_arg) {
strncpy(pfsdat,cur_arg,sizeof(pfsdat));
cur_arg += strlen(cur_arg);
}
else if (argc > 1) {
strncpy(pfsdat,argv[1],sizeof(pfsdat));
argc--;argv++;
}
else goto parse_err;
break;
#ifdef PFS_THREADS
case 't': /* set # of threads */
if(*cur_arg) {
if(strchr("0123456789",*cur_arg)) {
sscanf(cur_arg,"%d",&free_subthread_count);
cur_arg += strspn(cur_arg,"0123456789");
} else goto parse_err;
} else if(argc > 1) {
if(strchr("0123456789",*argv[1])) {
sscanf(argv[1],"%d",&free_subthread_count);
cur_arg += strspn(cur_arg,"0123456789");
} else goto parse_err;
argc--;argv++;
}
else goto parse_err;
if (free_subthread_count >= P_MAX_NUM_THREADS
|| free_subthread_count < 1) {
fprintf(stderr, "dirsrv: asked to use %d subthreads; \
must use between 1 and the compiled-in maximum (%d).\n", free_subthread_count,
P_MAX_NUM_THREADS - 1);
exit(1);
}
break;
#endif
parse_err:
default:
fprintf(stderr,
"Usage: dirsrv [-m] [-p<port>] [-r root] [-h hostname]\n");
exit(1);
}
}
argc--, argv++;
}
if (argc > 0) {
#ifdef PFS_THREADS
fprintf(stderr,
"Usage: dirsrv [-m] [-t<num-subthreads>] [-p<port>] [-r root] [-h hostname]\n");
#else
fprintf(stderr,
"Usage: dirsrv [-m] [-p<port>] [-r root] [-h hostname]\n");
#endif
exit(1);
}
/* Here re really should get the host name in cannonical form */
ucase(hostname);
if (!mflag) {
if(envv = getenv("PSRV_RUNDIR"))
retval = chdir(envv);
else {
#ifdef P_RUNDIR
retval = chdir(P_RUNDIR);
#else
retval = chdir("/tmp");
#endif
}
if(retval) plog(L_STATUS,NOREQ,"Startup - chdir failed: %d",errno);
}
/* Note our start time */
(void) time(&now);
assert(P_IS_THIS_THREAD_MASTER()); /* gmtime unsafe */
p_th_mutex_lock(p_th_mutexPFS_TIMETOASN);
{
struct tm *tm= gmtime(&now); /* safe since parent thread. */
sprintf(st_time_str,"%2d-%s-%02d %02d:%02d:%02d UTC",tm->tm_mday,
month_sname(tm->tm_mon + 1),tm->tm_year,
tm->tm_hour, tm->tm_min, tm->tm_sec);
}
p_th_mutex_unlock(p_th_mutexPFS_TIMETOASN);
/* Eventually, we will only set up and bind a port if one */
/* wasn't already passed to us using the -p option. Until */
/* all clients can support alternative ports, however, we */
/* must bind the DIRSRV port in addition to any that were */
/* passed to us. Note that at this point, unless we are */
/* running as root (which we should not be) we would not */
/* have been able to bind the priveleged port. */
port_no = ardp_bind_port(portname ? portname : "dirsrv");
#ifdef TAG_UNASSIGNED_PORT
if((in_port < 0) && (port_no != PROSPERO_PORT))
sprintf(hostwport,"%s(%d)",hostname,port_no);
else
#endif
if((port_no != PROSPERO_PORT) && (port_no != DIRSRV_PORT))
sprintf(hostwport,"%s(%d)",hostname,port_no);
else strcpy(hostwport,hostname);
#ifdef PSRV_ARCHIE
ardp_set_queuing_policy(arch_prioritize_request,0);
#endif /* PSRV_ARCHIE */
if (!mflag) {
if ((child = fork()) != 0) {
printf("%s started, PID=%d, PORT=%d\n",
prog, child, port_no);
exit(0);
}
setup_disc();
}
else {
plog_manual(stderr);
printf("%s started, PORT=%d\n", prog, port_no);
}
set_restart_params(fault_count+1, port_no);
plog(L_STATUS,NOREQ,
#ifdef PFS_THREADS
"Startup - %sPID: %d, Root: %s, Shadow: %s, Security: %s, Aftpdir: %s, %s%s%sNum-Subthreads: %d, Host: %s",
#else
"Startup - %sPID: %d, Root: %s, Shadow: %s, Security: %s, Aftpdir: %s, %s%s%sHost: %s",
#endif
(mflag ? "Mode: manual " : ""), getpid(), root, shadow, security,
aftpdir, (*afsdir ? "Afsdir: " : ""), afsdir, (*afsdir ? ", " : ""),
#ifdef PFS_THREADS
free_subthread_count,
#endif
hostwport);
/* set dirsend timeout for chasing forwarding pointers */
ardp_set_retry(4,1);
#ifndef PFS_THREADS
/* receive loop */
for (;;) {
curr_req = ardp_get_nxt();
#ifdef DIRSRV_EXPLAIN_LAST_RESTART
pkt = curr_req->rcvd;
strcpy(last_request,pkt->text); /* For error logging */
#endif DIRSRV_EXPLAIN_LAST_RESTART
req_count++;
dirsrv(curr_req);
}
#else /* PFS_THREADS */
/* We've already called p__th_set_self_master() */
/* receive loop */
for (;;) {
curr_req = NULL; /* req to process */
if (subthread_count == 0) {
/* No worker threads currently running; ok to block in
ardp_get_nxt() */
curr_req = ardp_get_nxt();
} else {
if (free_subthread_count > 0) {
/* Worker threads are currently running, but there are also
threads that are free to get more work done, if it arrives or
is on the pending queue. */
/* can't call ardp_get_nxt() because it's blocking. */
curr_req = ardp_get_nxt_nonblocking();
}
if (!curr_req) {
/* sleep for a second to see if work gets done.
do this because a nonblocking select() is not currently
implemented. */
ardp_accept_and_wait(0,500000);
}
}
if (curr_req) {
p_th_t new_thread;
int tmp;
/* A request arrived and was ready to process */
if(++subthread_count > subthread_max)
subthread_max = subthread_count;
--free_subthread_count;
/* If pthread_create() ever fails, we have no way to recover, and
might as well consider it a fatal error. */
#ifdef MASTER_IS_ONLY_SUBTHREAD
req_count++;
dirsrv(curr_req);
#else
#if 1
req_count++;
if (p_th_create_detached(new_thread, dirsrv, curr_req))
abort();
/* If p_th_create_detached ever fails, we have no good way to
recover. So fail if it ever gives us a non-zero return value. */
#else /* the above replaces the following code */
/* Throw this away once a working FSU pthreads based implementation
is going again. */
tmp = pthread_create(&new_thread, (pthread_attr_t *) NULL,
(pthread_func_t) dirsrv, (any_t) curr_req);
assert(tmp == 0);
/* If pthread_detach ever fails, we have no good way to recover. */
/* So fail if it ever gives us a non-zero return value. */
/* Note that under the Draft 7 standard, pthread_detach
takes a pthread_t, whereas under Draft 6, it takes a pointer to
a pthread_t. This implementation uses Draft 6. */
tmp = pthread_detach(&new_thread);
assert(tmp == 0);
#endif
#endif
/* dirsrv handles decrementing subthread_count, incrementing
free_subthread_count, and de-assigning itself the thread
number on exit*/
/* dirsrv assigns itself a newthread # on startup, unless
MASTER_IS_ONLY_SUBTHREAD. */
}
#if 0
#ifdef DIRSRV_EXPLAIN_LAST_RESTART
#error /* This won't work correctly right now. */
pkt = curr_req->rcvd;
strcpy(last_request,pkt->text); /* For error logging */
#endif DIRSRV_EXPLAIN_LAST_RESTART
#endif /* 0 */
}
#endif /* PFS_THREADS */
}
#ifndef NDEBUG
void
diagnose(void)
{
#ifdef PFS_THREADS
printf("Subthreads: %d(%d)\n",subthread_count,subthread_max);
#endif
p_diagnose();
psrv_diagnose_mutexes();
#ifdef PSRV_GOPHER_GW
gopher_gw_diagnose_mutexes();
#endif
#ifdef PSRV_WAIS_GW
wais_gw_diagnose_mutexes();
#endif
#ifdef PFS_THREADS
DIAGMUTEX(P_PARAMETER_MOTD,"P_PARAMETER_MOTD");
#endif
}
#endif /*NDEBUG*/
static int dirsrv4real(RREQ req);
static void
dirsrv(RREQ req)
{
CHECK_MEM();
p_thread_initialize();
VLDEBUGBEGIN;
dirsrv4real(req);
VLDEBUGEND;
#ifdef PFS_THREADS
#ifndef MASTER_IS_ONLY_SUBTHREAD
p_th_deallocate_self_num();
#endif
CHECK_MEM();
--subthread_count;
++free_subthread_count;
#endif /*PFS_THREADS*/
}
/* dirsrv4real() returns a SUCCESS or FAILURE code that is currently unused.
It's there, in case we ever have a use for it. */
static int
dirsrv4real(RREQ req)
{
/*************************/
/* The following are set in one line and used by subsequent */
/* lines of the same message */
int client_version = MAX_VERSION; /* Protocol version no.;
default to max version. */
/* These store directory information, set on one line and used in
subsequent lines of the message. */
char dir_type[40]; /* Type of dir name (Currently, the
only supported value is ASCII) */
char client_dir[MAXPATHLEN] = ""; /* Current directory */
long dir_version = -1; /* Directory version nbr.
Currently ignored. */
long dir_magic_no = -1; /* Directory magic number */
#ifdef PSRV_ARCHIE
int max_list_commands = 5; /* Max lists in request */
#endif /* PSRV_ARCHIE */
/* used to parse commands. */
char *tmpline; /* line returned by in_line() */
AUTOSTAT_CHARPP(commandp); /* The current line. This is
memory allocated through
stcopy(). */
char *next_word; /* next_word: a temporary pointer to a position
within the command line. Points to the
next place from which we'll want to read
data. */
int retval; /* Return value from subfunctions */
INPUT_ST in_st;
INPUT in = &in_st;
*client_dir = '\0';
CHECK_MEM();
/* in_nextline(): the start of the next command line in the packet, or
NULL.
command: the head of the current command line, without newline, NULL
terminated. */
rreqtoin(req, in);
if (in_eof(in))
return error_reply(req, "ERROR Received empty Prospero request packet");
/* This loop always begins with in_nextline() either the head of the
next command line in the packet, or NULL. */
while (!in_eof(in)) {
VLDEBUGBEGIN;
/* get the next line. */
if(in_line(in, &tmpline, &next_word)) {
creplyf(req, "ERROR Malformed input line: %'s\n", p_err_string);
plog(L_DIR_ERR, req, "Malformed input line: %s", p_err_string);
RETURNPFAILURE;
}
*commandp = stcopyr(tmpline, *commandp);
next_word = *commandp + (next_word - tmpline);
/* now, in_nextline(in) either points to the start of the following
line or is NULL. *commandp points to the start of a NUL-terminated
string which contains what's supposed to be a Prospero protocol
command line. next_word points to the first word in that string
after command. */
switch(cmd_lookup(*commandp)) {
#ifdef PSRV_ACCOUNT
case ACCOUNT:
{
static char *t_options = NULL;
static char *acc_method = NULL; /* Accounting method */
static char *acc_server = NULL; /* Accounting server */
static char *acc_name = NULL; /* Account name */
static char *verifier = NULL; /* Verifier string */
static char *int_bill_ref = NULL; /* Internal billing */
/* reference */
static char *currency = NULL;
static char *amount = NULL;
int tmp, i;
char *cp; /* Temporary variable for use by qsscanf() */
PAUTH patmp;
tmp = qsscanf(next_word,"%'&s %'&s %'&s %'&s %'&s %'&s %r",
&t_options, &acc_method, &acc_server,
&acc_name, &verifier, &int_bill_ref, &cp);
if(tmp < 7)
return error_reply(req, "Invalid arguments: %s", *commandp);
if ((patmp = paalloc()) == NULL)
return error_reply(req, "Out of Memory (paalloc())!");
/* This memory will be freed by the rdgram transmit() function. */
patmp->next = req->auth_info;
req->auth_info = patmp;
/* Search for acc_method in array */
for (i=0; acc_methods[i].acc_method_num &&
strcmp(acc_methods[i].acc_method_name,
acc_method);
i++);
if (!acc_methods[i].acc_method_num)
return error_reply(req, "Invalid accounting method!");
patmp->ainfo_type = acc_methods[i].acc_method_num;
patmp->acc_server = stcopy(acc_server);
patmp->acc_name = stcopy(acc_name);
patmp->acc_verifier = stcopy(verifier);
patmp->int_bill_ref = stcopy(int_bill_ref);
while (1) {
struct amt_info *amt = NULL;
char *remainder = NULL;
if (!cp)
break;
tmp = qsscanf(cp, "%'&s %'&s %r",
&currency, &amount, &remainder);
if (tmp < 2)
break;
stfree(cp);
cp = remainder;
amt = (struct amt_info *) stalloc(sizeof(struct
amt_info));
amt->currency = stcopy(currency);
str_to_fp(amount, &amt->limit);
amt->amt_spent.sig = 0;
amt->amt_spent.exp = 0;
amt->next = patmp->allocated_amts;
patmp->allocated_amts = amt;
}
break;
}
#endif
case AUTHENTICATE:
{
AUTOSTAT_CHARPP(auth_typep); /* Type of Authentication */
/* Authentication data */
AUTOSTAT_CHARPP(t_optionsp);
AUTOSTAT_CHARPP(authentp);
AUTOSTAT_CHARPP(t_client_idp);
int tmp;
char *cp; /* Temporary variable for use by qsscanf() */
PAUTH patmp;
tmp = qsscanf(next_word,"%'&s %'&s %'&s %r",
t_optionsp, auth_typep, authentp, &cp);
/* Additional tokens are just principal names; purely
informational. */
if(tmp < 3)
return error_reply(req, "Invalid arguments: %s", *commandp);
if ((patmp = paalloc()) == NULL)
return error_reply(req, "Out of Memory (paalloc())!");
/* This memory will be freed by the rdgram transmit() function. */
patmp->next = req->auth_info;
req->auth_info = patmp;
#ifdef PSRV_KERBEROS
if (strequal(*auth_typep,"KERBEROS")) {
if (retval = check_krb_auth(*authentp, req->peer,
t_client_idp)) {
if (!*t_options || strequal(*t_optionsp, "MANDATORY"))
return auth_fail_reply(req,
"Invalid Kerberos Authenticator");
}
else {
patmp->principals = tkalloc(*t_client_idp);
patmp->ainfo_type = PFSA_KERBEROS;
req->client_name = stcopyr(t_client_id,
req->client_name);
}
} else
#endif
#ifdef PSRV_P_PASSWORD
if (strequal(*auth_typep, "P_PASSWORD")) {
qsscanf(cp, "%'&s", t_client_idp);
if (!passwd_correct(*t_client_idp, *authentp)) {
if ((!*t_optionsp && get_ppw_entry(*t_client_idp))
|| (*t_optionsp && strequal(*t_optionsp, "MANDATORY")))
return auth_fail_reply(req,
"Invalid password");
}
else {
req->client_name = stcopyr(*t_client_idp, req->client_name);
patmp->principals = tkalloc(*t_client_idp);
patmp->ainfo_type = PFSA_P_PASSWORD;
}
} else
#endif
if (strequal(*auth_typep, "UNAUTHENTICATED")) {
req->client_name = stcopyr(*authentp,req->client_name);
patmp->principals = tkalloc(*authentp);
patmp->ainfo_type = PFSA_UNAUTHENTICATED;
}
else {
if (strequal(*t_optionsp, "MANDATORY"))
return auth_fail_reply(req,
"Authentication type %s not supported",
*auth_typep,0);
}
}
break;
#ifndef PSRV_READ_ONLY
case CREATE_LINK:
crlnk_count++;
if(create_link(req, &*commandp, &next_word, in, client_dir,
dir_magic_no))
RETURNPFAILURE;
break;
case CREATE_OBJECT:
crobj_count++;
if(create_object(req, &*commandp, &next_word, in, client_dir,
dir_magic_no))
RETURNPFAILURE;
break;
case DELETE_LINK:
dellnk_count++;
if(delete_link(req, *commandp, next_word, in,
client_dir, dir_magic_no))
RETURNPFAILURE;
break;
#endif
case DIRECTORY:
{
int tmp;
char *cp;
dir_version = 0;
dir_magic_no = 0;
tmp = qsscanf(next_word,"%!!'s %!!'s %d %r",
dir_type, sizeof dir_type,
client_dir, sizeof client_dir,
&dir_version, &cp);
if (tmp < 0)
interr_buffer_full();
if(tmp < 2 || tmp > 3)
return error_reply(req, "Invalid arguments: %'s", *commandp);
if(!strequal(dir_type,"ASCII"))
return error_reply(req, "Directory handle type %'s not supported",
dir_type);
if(in_select(in, &dir_magic_no)) {
return error_reply(req, "DIRECTORY: %'s", p_err_string);
}
if(check_handle(client_dir) == FALSE) {
creply(req,"FAILURE NOT-AUTHORIZED\n");
plog(L_AUTH_ERR,req,"Invalid directory name: %'s",client_dir);
RETURNPFAILURE;
}
}
break;
#ifndef PSRV_READ_ONLY
case EDIT_ACL:
eacl_count++;
if(edit_acl(req, &*commandp, &next_word, in, client_dir,
dir_magic_no))
RETURNPFAILURE;
break;
case EDIT_LINK_INFO:
eli_count++;
if(edit_link_info(req, &*commandp, &next_word, in,
client_dir, dir_magic_no))
RETURNPFAILURE;
break;
case EDIT_OBJECT_INFO:
eoi_count++;
if(srv_edit_object_info(req, *commandp, next_word, in))
RETURNPFAILURE;
break;
#endif /* PSRV_READ_ONLY */
case GET_OBJECT_INFO:
goi_count++;
CHECK_MEM();
VLDEBUGBEGIN;
if(get_object_info(req, *commandp, next_word, in))
RETURNPFAILURE;
VLDEBUGEND;
CHECK_MEM();
break;
case LIST: {
list_count++;
#ifdef PSRV_ARCHIE
if(max_list_commands-- <= 0) {
creply(req,"FAILURE NOT-AUTHORIZED Too many list commands in a single request\n");
plog(L_AUTH_ERR,req,"Too many list commands");
RETURNPFAILURE;
}
#endif /* PSRV_ARCHIE */
VLDEBUGBEGIN;
if(list(req, &*commandp, &next_word, in, client_dir,
dir_magic_no))
RETURNPFAILURE;
VLDEBUGEND;
break;
}
case LIST_ACL:
lacl_count++;
if(retval =
list_acl(req, *commandp, next_word, in,
client_dir, dir_magic_no))
return retval;
break;
case PARAMETER:
parameter_count++;
if(retval = parameter(req, *commandp, next_word)) return retval;
break;
case STATUS:
{
char *cp; /* temporary */
int i;
status_count++;
if (qsscanf(next_word, "%*s%r", &cp,NULL) > 0)
return error_reply(req, "STATUS command takes no arguments, \
but we received: %s", *commandp);
replyf(req,"Prospero server (%s) %s\n",PFS_RELEASE,hostwport);
if(fault_count)
replyf(req,"Faults since startup %d\n",fault_count);
#ifdef SERVER_SUPPORT_V1
replyf(req,"Requests since startup %d (%d V1) (%d+%d+%d %d+%d+%d \
%d %d+%d+%d %d %d %d)\n",
req_count, v1_req_count, list_count, goi_count, lacl_count,
crlnk_count, crdir_count, crobj_count, dellnk_count,
eli_count, eoi_count, eacl_count, upddir_count,
parameter_count, status_count);
#else
replyf(req,"Requests since startup %d (%d+%d+%d %d+%d %d \
%d+%d+%d %d %d %d)\n",
req_count, list_count, goi_count, lacl_count,
crlnk_count, crobj_count, dellnk_count,
eli_count, eoi_count, eacl_count, upddir_count,
parameter_count, status_count);
#endif
replyf(req,"Started: %s\n",st_time_str);
#ifdef PROSPERO_CONTACT
replyf(req,"Contact: %s\n",PROSPERO_CONTACT);
#endif PROSPERO_CONTACT
#ifdef PFS_THREADS
replyf(req, "Threads: %d total worker, %d(%d) active, %d free.\n",
subthread_count + free_subthread_count, subthread_count,
subthread_max, free_subthread_count);
#endif
replyf(req," Files: %d(%d)open, %d waits, %d secs\n",
filelock_open,filelock_open_max, filelock_sepwaits,
filelock_secwaits);
#ifdef DNSCACHE_MAX
replyf(req," DNS: %d(%d) caching %d(%d)\n",
dnscache_count,dnscache_max,alldnscache_count,DNSCACHE_MAX);
#endif
replyf(req," Memory: %d(%d)vlink %d(%d)pattrib %d(%d)acl \
%d(%d)pfile %d(%d)rreq %d(%d)ptext\n",
vlink_count,vlink_max,pattrib_count,pattrib_max,
acl_count,acl_max,pfile_count,pfile_max,rreq_count,
rreq_max,ptext_count,ptext_max);
replyf(req,
" Memory: %d(%d)string %d(%d)token %d(%d)pauth \
%d(%d)filter %d(%d)p_object\n",
/* opt not implemented yet " Memory: %d(%d)str %d(%d)tk %d(%d)pa %d(%d)opt \
%d(%d)fil\n", */
string_count,string_max, token_count, token_max,
pauth_count, pauth_max, /* opt_count, opt_max, */
filter_count, filter_max, p_object_count, p_object_max);
#if 0
replyf(req, "Memory: %d(%d)dnscache %d(%d)filelock\n",
dnscache_count, dnscache_max,
filelock_count, filelock_max);
#endif
replyf(req, " Memory: %d(%d)dnscache\n",
dnscache_count, dnscache_max /*,
filelock_count, filelock_max */);
#ifdef PSRV_GOPHER_GW
replyf(req, " Memory: %d(%d)glink\n", glink_count, glink_max);
#endif
#ifdef PSRV_WAIS_GW
replyf(req, " Memory: %d(%d)waismsgbuff %d(%d)ietftype %d(%d)waissource\n",
waismsgbuff_count, waismsgbuff_max,
ietftype_count, ietftype_max,
waissource_count, waissource_max);
#endif
#ifdef DIRECTORYCACHING
replyf(req, "Caching: %d attempts, %d can, %d yes, %d fail\n",
cache_attempt, cache_can, cache_yes, dsrobject_fail);
#endif
#ifndef PSRV_READ_ONLY
if(*pfsdat) replyf(req," Data: %s\n", pfsdat);
#endif PSRV_READ_ONLY
if(*root) replyf(req," Root: %s\n", root);
if(*aftpdir) replyf(req," AFTP: %s\n", aftpdir);
if(*afsdir) replyf(req," AFS: %s\n", afsdir);
if(db_num_ents > 0) {
replyf(req," DB:");
for(i=0;i<db_num_ents;i++)
replyf(req," %s",db_prefixes[i].prefix);
replyf(req,"\n");
}
if(last_error) replyf(req," Error: %s\n",last_error);
/**/
#ifdef PSRV_ARCHIE
replyf(req, "%s", print_queue_timings());
#endif
plog(L_DIR_PINFO, req, "STATUS Request");
break;
}
#ifndef PSRV_READ_ONLY
case UPDATE:
upddir_count++;
if(update(req, *commandp, next_word, in,
client_dir, dir_magic_no))
RETURNPFAILURE;
break;
#endif
case VERSION:
if((client_version =
version(req, *commandp, next_word)) < 0)
RETURNPFAILURE;
#ifdef SERVER_SUPPORT_V1
if (client_version == 1) {
++v1_req_count;
/* We must pass a character pointer for the start of the place
in the packet to grab the next token from; this is just the
old next_line pointer. */
return dirsrv_v1(req,strchr(req->rcvd->text, '\n'));
}
#endif
/* Otherwise, we can continue to process this packet. */
break;
case UNIMPLEMENTED:
plog(L_DIR_PERR,req,"Unimplemented message: %s", *commandp);
creplyf(req,"FAILURE UNIMPLEMENTED %'s\n", *commandp);
RETURNPFAILURE;
break;
case UNKNOWN:
return error_reply(req, "Unknown message: %s", *commandp, 0);
break;
default:
plog(L_FAILURE,req,
"Somehow, we got to the default case in the giant switch \
in dirsrv (file %s, line %d). This should never happen.",
__FILE__, __LINE__);
return error_reply(req, "Unknown message: %s", *commandp, 0);
break;
} /* End of switch(cmd_lookup(*commandp)) */
VLDEBUGEND;
} /* command processing loop end: while(in_nextline(in)) */
creply(req,NULL); /* Send it out! */
return(PSUCCESS);
}
/*
* cmd_lookup - lookup the command name and return integer
*
* CMD_LOOKUP takes a pointer to a string containing a command.
* It then looks up the first word found in the string and
* returns an int that can be used in a switch to dispatch
* to the correct routines.
*
* This has been optimzed for the server side of the Prospero protocol.
*/
static int
cmd_lookup(char *cmd)
{
#ifdef PSRV_READ_ONLY
/* This command modifies the server data files. */
#define modcmd(dispatch_code) UNIMPLEMENTED
#else
#define modcmd(dispatch_code) (dispatch_code)
#endif
switch(*cmd) {
case 'A':
if(strnequal(cmd,"AUTHENTICATE", 12))
return AUTHENTICATOR;
#ifdef PSRV_ACCOUNT
else if(strnequal(cmd,"ACCOUNT",7))
return ACCOUNT;
#endif
else if(strnequal(cmd, "ATOMIC", 6))
return UNIMPLEMENTED;
else
return UNKNOWN;
case 'C':
if(strnequal(cmd,"CREATE-OBJECT",11))
return modcmd(CREATE_OBJECT);
else if(strnequal(cmd,"CREATE-LINK",11))
return modcmd(CREATE_LINK);
else return UNKNOWN;
case 'D':
if(strnequal(cmd,"DELETE-LINK",11))
return modcmd(DELETE_LINK);
else if(strnequal(cmd,"DIRECTORY",9))
return DIRECTORY;
else return UNKNOWN;
case 'E':
if (strnequal(cmd, "EDIT-LINK-INFO", 14))
return modcmd(EDIT_LINK_INFO);
else if(strnequal(cmd,"EDIT-OBJECT-INFO",16))
return modcmd(EDIT_OBJECT_INFO);
else if (strnequal(cmd, "EDIT-ACL", 8))
return modcmd(EDIT_ACL);
else
return UNKNOWN;
case 'G':
if(strnequal(cmd,"GET-OBJECT-INFO",13))
return GET_OBJECT_INFO;
else return UNKNOWN;
case 'L':
if(strnequal(cmd,"LIST-ACL",8))
return LIST_ACL;
else if(strnequal(cmd,"LIST",4))
return LIST;
else return UNKNOWN;
case 'P':
if(strnequal(cmd, "PARAMETER", 9))
return PARAMETER;
else return UNKNOWN;
case 'S':
if(strnequal(cmd,"STATUS",6))
return STATUS;
else return UNKNOWN;
case 'U':
if(strnequal(cmd,"UPDATE",6))
return modcmd(UPDATE);
else return UNKNOWN;
case 'V':
if(strnequal(cmd,"VERSION",7))
return VERSION;
else return UNKNOWN;
default:
return UNKNOWN;
}
}
SIGNAL_RET_TYPE term_sig()
{
char qlstring[30];
if(pQlen > 0) sprintf(qlstring," [%d pending]",pQlen);
else *qlstring = '\0';
plog(L_STATUS, NOREQ,
"Server killed (terminate signal received)%s",qlstring);
log_server_stats();
exit(0);
}
SIGNAL_RET_TYPE restart_sig()
{
char qlstring[30];
if(pQlen > 0) sprintf(qlstring," [%d pending]",pQlen);
else *qlstring = '\0';
plog(L_STATUS, NOREQ,
"Attempting server restart (restart signal received)%s",qlstring);
restart_server(fault_count,(char *) NULL);
}
SIGNAL_RET_TYPE trap_error(sig, code, scp)
int sig;
int code;
#ifndef SCOUNIX
struct sigcontext *scp;
#endif
{
char estring[400];
#ifdef COREDUMP
int pid;
if ( (pid = fork()) > 0 ) { /* parent */
signal(SIGIOT,SIG_DFL);
signal(SIGABRT,SIG_DFL);
abort();
exit(-1);
}
else { /* child */
sleep(5); /* just make sure that the parent creates the core */
#endif
if(pQlen > 0) qsprintf(estring, sizeof estring, " [%d pending]",pQlen);
else *estring = '\0';
#ifdef SIGCONTEXT_LACKS_SC_PC
plog(L_FAILURE,NOREQ,"Failure - Recovering from error (%d,%d)%s",sig,code,estring);
#else
plog(L_FAILURE,NOREQ,"Failure - Recovering from error at address 0x%x (%d,%d)%s",scp->sc_pc,sig,code,estring);
#endif
#ifdef DIRSRV_EXPLAIN_LAST_RESTART
plog(L_DIR_ERR,NOREQ,"Last request was: %s",last_request);
#ifdef SIGCONTEXT_LACKS_SC_PC
qsprintf(estring,sizeof estring, "Signal(%d,%d)\n%s", sig, code,
last_request);
#else
qsprintf(estring,sizeof estring, "Signal(%d,%d) at 0x%x\n%s", sig, code,
scp->sc_pc, last_request);
#endif
#else
#ifdef SIGCONTEXT_LACKS_SC_PC
qsprintf(estring,sizeof estring, "Signal(%d,%d) at 0x%x", sig, code);
#else
qsprintf(estring,sizeof estring, "Signal(%d,%d) at 0x%x", sig, code,
scp->sc_pc);
#endif
#endif DIRSRV_EXPLAIN_LAST_RESTART
restart_server(fault_count,estring);
abort();
#ifdef COREDUMP
}
#endif
}
/*
* setup_disc
*
* disconnect all descriptors, remove ourself from the process
* group that spawned us and set signal handlers.
*/
void
setup_disc()
{
int s;
for (s = 0; s < 3; s++) {
(void) close(s);
}
/* This routine is called EXCLUSIVELY by the MASTER thread before going
multithreaded. */
assert(P_IS_THIS_THREAD_MASTER());
(void) open("/dev/null", 0, 0);
(void) dup2(0, 1); /* Under Solaris 2.3, dup2() is MT unsafe; master
thread only */
(void) dup2(0, 2); /* MT unsafe; master thread only */
#ifdef SETSID
setsid();
#else
s = open("/dev/tty", 2, 0);
if (s >= 0) {
ioctl(s, TIOCNOTTY, (struct sgttyb *) 0);
(void) close(s);
}
#endif
signal(SIGHUP,term_sig);
signal(SIGINT,term_sig);
signal(SIGTERM,term_sig);
signal(SIGQUIT,term_sig);
signal(SIGILL,trap_error);
signal(SIGIOT,trap_error);
signal(SIGEMT,trap_error);
signal(SIGFPE,trap_error);
signal(SIGBUS,trap_error);
signal(SIGSEGV,trap_error);
signal(SIGPIPE,trap_error); /* Seen these with non-blocking io */
signal(SIGSYS,trap_error);
signal(SIGUSR1,restart_sig);
#if defined(AIX) || defined(SOLARIS)
bsdSignal(SIGUSR2,close_plog);
#else
signal(SIGUSR2,close_plog);
#endif
return;
}
void
log_server_stats()
{
plog(L_STATS,NOREQ,"Stats: %d List, %d GOI, %d LACL",
list_count,goi_count,lacl_count);
#ifdef SERVER_SUPPORT_V1
plog(L_STATS,NOREQ," %d CL, %d CD, %d CO, %d DL",
crlnk_count,crdir_count,crobj_count,dellnk_count);
#else
plog(L_STATS,NOREQ," %d CL, %d CO, %d DL",
crlnk_count,crobj_count,dellnk_count);
#endif
plog(L_STATS,NOREQ," %d ELI, %d EOI, %d EACL, %d Upd",
eli_count,eoi_count,eacl_count,upddir_count,0);
#ifdef SERVER_SUPPORT_V1
if(v1_req_count) plog(L_STATS,NOREQ," %d Total, %d V1 format",
req_count, v1_req_count);
else
#endif
plog(L_STATS,NOREQ," %d Total", req_count);
}
/* Check a handle (the filename portion of a system level name) and make sure
that we are allowed to access it. This uses the definitions from
"psite.h". */
int
check_handle(char *handle)
{
int i;
#ifdef NODOTDOT
char *t_handle = NULL;
#endif
if(*handle != '/') { /* Database or special prefix in use */
for(i=0;i<db_num_ents;i++) {
if(strnequal(handle, db_prefixes[i].prefix,
strlen(db_prefixes[i].prefix)))
return(TRUE);
}
}
#ifdef NODOTDOT
t_handle = qsprintf_stcopyr(t_handle, "/%s/", handle);
if(sindex(t_handle, "/../")) return FALSE;
stfree(t_handle); t_handle = NULL;
#endif
if(strnequal(handle,pfsdat, strlen(pfsdat))) return TRUE;
if(*root && strnequal(handle,root, strlen(root))) return TRUE;
if(*aftpdir && strnequal(handle,aftpdir, strlen(aftpdir))) return TRUE;
if(*aftpdir && strnequal(handle, "AFTP", 4)) return TRUE;
if(*afsdir && strnequal(handle, afsdir, strlen(afsdir))) return TRUE;
return FALSE;
}
/*
* dirsrv has an internal error handler. This
* is used by the internal_error macro and by the p__finternal_error()
* function.
*/
static void
dirsrv_internal_error_handler(file, line, mesg)
char file[];
int line;
char mesg[];
{
/* if we get into a loop (if qsscanf or plog or restart_server have
internal errors), we'll bail out eventually. */
/* Assume that incrementing this is an atomic operation; is on all
architectures. This is not a good choice, but since an internal error
has already occurred, we don't want to assume that the multithreading
kernel is working perfectly. */
static int been_here_before = 0;
char estring[400];
if (been_here_before++ < 5) {
/* Problem: if internal errors or assertion problems occur inside
PLOG (including running out of memory!) then we are in trouble.
*/
plog(L_FAILURE, NOREQ,
"Internal error in file %s, line %d: %s", file, line, mesg);
if (mflag)
plog(L_FAILURE, NOREQ, "In manual mode; not restarting server.");
else
plog(L_FAILURE, NOREQ, "Trying to restart server.");
qsprintf(estring, sizeof(estring),
"Internal error in file %s, line %d: %s", file, line, mesg, 0);
}
if (been_here_before < 10) {
if(!mflag)
restart_server(++fault_count, estring);
else
abort();
}
write(2, "In dirsrv.c, dirsrv_internal_error_handler(): \
This should never happen.\n",
sizeof "In dirsrv.c, dirsrv_internal_error_handler(): \
This should never happen.\n");
if (mflag) abort(); /* and if THAT fails... */
_exit(1); /* final desperation play. */
write(2, "In dirsrv.c, dirsrv_internal_error_handler(): \
_exit(1) didn't abort execution! This should REALLY never happen.\n",
sizeof "In dirsrv.c, dirsrv_internal_error_handler(): \
_exit(1) didn't abort execution! This should REALLY never happen.\n");
}
/* This function will execute both a vplog() and an vsendmqf() in order to
report on a failure.
It returns PFAILURE, since it should only be used if an operation
cannot be performed.
It also automatically prefixes the words
"FAILURE AUTHENTICATION-DATA" to the reply packets.
It appends the appropriate newlines, so you don't have to.
*/
static int
auth_fail_reply(RREQ req, char *format, ...)
{
va_list ap;
char *bufp;
va_start(ap, format);
bufp = vplog(L_AUTH_ERR, req, format, ap); /* return formatted string */
reply(req, "FAILURE AUTHENTICATION-DATA ");
reply(req, bufp);
creply(req, "\n");
va_end(ap);
RETURNPFAILURE;
}
#ifdef PSRV_ACCOUNT
/* Need to add overflow checking and normalization in these routines. */
/* Converts string of form "xxx.yy" into two integers a and b such */
/* that xxx.yy = a * 10^y */
static int
str_to_fp(char *amount, struct decimal *amt)
{
char *cp;
char *lim;
if (!amount)
RETURNPFAILURE;
if (!amt)
amt = (struct decimal *) stalloc(sizeof(struct decimal));
for(cp = amount; *cp && *cp != '.'; cp++);
if (!*cp) {
sscanf(amount, "%d", &amt->sig);
amt->exp = 0;
}
else {
lim = stalloc(strlen(amount));
lim = strncpy(lim, amount, cp-amount);
lim = strcat(lim, cp+1);
sscanf(lim, "%d", &amt->sig);
amt->exp = -strlen(cp+1);
stfree(lim);
}
return PSUCCESS;
}
static int
fp_to_str(struct decimal amt, char *amount)
{
char tmp[255];
int i;
qsprintf(tmp, sizeof tmp, "%d", amt.sig);
if (!amount)
amount = (char *) stalloc(strlen(tmp)+2);
if (amt.exp < 0) {
amount = strncpy(amount, tmp, strlen(tmp)+amt.exp);
amount = strcat(amount, ".");
amount = strcat(amount, tmp+strlen(tmp)+amt.exp);
}
else if (amt.exp > 0) {
amount = strcpy(amount, tmp);
for (i = 0; i < amt.exp; i++)
amount = strcat(amount, "0");
}
else
amount = strcpy(amount, tmp);
return PSUCCESS;
}
/* Subtracts amt2 from amt1 and returns amount left in result */
static int subtract_fp(struct decimal amt1, struct decimal amt2,
struct decimal *result)
{
while (amt1.exp > amt2.exp) {
amt1.exp--;
amt1.sig *= 10;
}
while (amt1.exp < amt2.exp) {
amt2.exp--;
amt2.sig *= 10;
}
if (amt1.sig < amt2.sig)
RETURNPFAILURE;
if (!result)
result = (struct decimal *) stalloc(sizeof(struct decimal));
result->sig = amt1.sig - amt2.sig;
result->exp = amt1.exp;
return PSUCCESS;
}
/* Adds amt1 to amt2 and returns amount in result */
static int add_fp(struct decimal amt1, struct decimal amt2,
struct decimal *result)
{
while (amt1.exp > amt2.exp) {
amt1.exp--;
amt1.sig *= 10;
}
while (amt1.exp < amt2.exp) {
amt2.exp--;
amt2.sig *= 10;
}
if (!result)
result = (struct decimal *) stalloc(sizeof(struct decimal));
result->sig = amt1.sig + amt2.sig;
result->exp = amt1.exp;
return PSUCCESS;
}
static int charge(RREQ req, char *currency, struct decimal amount,
char *vendor, struct decimal vendor_amount,
char *vendor_currency, char *description,
char *signature)
{
PAUTH auth_info;
struct amt_info *amtp;
struct decimal amt_left;
static char charge_amt[255], vendor_amt[255];
if (!req || !currency || !description)
RETURNPFAILURE;
if (!vendor)
vendor = stcopy("");
if (!vendor_currency)
vendor_currency = stcopy("");
if (!signature)
signature = stcopy("");
fp_to_str(amount, charge_amt);
fp_to_str(vendor_amount, vendor_amt);
auth_info = req->auth_info;
for (amtp = auth_info->allocated_amts;
amtp && strcmp(currency, amtp->currency);
amtp = amtp->next);
if (!amtp)
RETURNPFAILURE;
/* Find amount left */
if (subtract_fp(amtp->limit, amtp->amt_spent, &amt_left))
RETURNPFAILURE; /* Should never happen! */
/* Check if amount left sufficient */
if (subtract_fp(amt_left, amount, 0))
RETURNPFAILURE; /* Insufficient funds */
/* Add amount charged to total amount spent */
add_fp(amtp->amt_spent, amount, &amtp->amt_spent);
plog(L_ACCOUNT, NOREQ, "Charged %'s %'s %'s %'s %'s %'s %'s, \
Vendor: %'s %'s %'s, Transaction: %'s\n",
charge_amt, currency, acc_method_name(auth_info->ainfo_type),
auth_info->acc_server, auth_info->acc_name, signature,
auth_info->int_bill_ref, vendor,
vendor_amt, vendor_currency, description);
creplyf(req, "RECEIPT %'s %'s %'s %'s %'s %'s %'s %'s\n",
acc_method_name(auth_info->ainfo_type),
auth_info->acc_server, auth_info->acc_name, signature,
auth_info->int_bill_ref, description,
currency, charge_amt);
return PSUCCESS;
}
static char *acc_method_name(int acc_method)
{
int i;
/* Search for acc_method in array */
for (i=0; acc_methods[i].acc_method_num &&
acc_methods[i].acc_method_num != acc_method;
i++);
if (!acc_methods[i].acc_method_num)
return NULL; /* Unknown accounting method */
return acc_methods[i].acc_method_name;
}
#endif