#if !defined(lint) && !defined(SABER)
static char rcsid[] = "$Id: services.c,v 1.3 1992/05/09 22:58:24 epeisach Exp $";
#endif

#include "mkserv.h"

char *service_suffix(int d) 
{
	switch (d) {
	case S_ADD:
		return ADD_SUFFIX;
 	case S_DEL:
		return DEL_SUFFIX;
	case S_SYNC:
		return SYNC_SUFFIX;
	case S_DEP:
		return DEP_SUFFIX;
	default:
		fprintf(stderr, "Internal error in service suffix %d\n",d);
		abort();
	}

	return NULL;
}




/* 
 * get_service(struct service *serv, struct search_path *path);
 *	Copies file required for service locally to destdir.
 *	Searchpath will be used for handling precendence and vno support
 * returns:
 *	0 success
 *	ENOENT - could not find service
 *	-1 Any unexpected error
 */

get_service(struct service *serv, struct search_path *path)
{
	/* First determine local vno if it exists */

	struct service s;
	int found=0;	/* Have yet to find the service */
	struct search_path *p;
	int i=0;
	char *p1;

	bzero(&s, sizeof(s));
	s.name = strdup(serv->name);

	p = path;
	while(p && found==0) {
		if(read_service_vno(p->path, &s)) {
			fprintf(stderr, "Error from read_service_vno unexpected\n");
			return -1;
		}

		for(i=0; i < NSERVICES; i++) {
			if(s.exists[i]) {
				found++;
			}
		}
		if(found) {
			if((s.path = strdup(p->path)) == NULL) return ENOMEM;
		}
		p = p->next;
	} /* while */

	/* Clear up memory - keep ref to name. */
	p1 = serv->name;
	serv->name = NULL;
	free_service(serv);
	serv->name = p1;

	if(found) {
		for(i = 0; i < NSERVICES; i++) {
			bcopy(&s.fh[i], &serv->fh[i], sizeof(struct file_header));
			serv->exists[i] = s.exists[i];
		}
		serv->path = s.path;
		return 0;
	} else {
		return ENOENT;
	}
}

void 
free_service(struct service *s)
{
	int i=0;
	for(i = 0; i < NSERVICES; i++) 
		free_header(&s->fh[i]);
	if(s->path) {
		free(s->path);
		s->path = NULL;
	}
	if(s->name) {
		free(s->name);
		s->name = NULL;
	}

}

/*
 * read_service_vno(char *dir, struct service *serv) 
 *    	Gets the vno's of services in directory dir
 *	Returns:
 *     		0 success (no fatal errors - may not have any data)
 *		-1 error
 */
read_service_vno(char *dir, struct service *serv) 
{
	char buf[MAXPATHLEN+1];
	int i;

	for(i=0; i < NSERVICES; i++) {
		strcpy(buf,dir);
		if((buf[0] != '\0') && (buf[strlen(dir)-1] != '/')) 
			strcat(buf, "/");
		strcat(buf, serv->name);
	        strcat(buf, service_suffix(i));
		serv->exists[i]=0;
		if(!get_file_header(buf, &serv->fh[i])) serv->exists[i] = 1;
	}
	return 0;
}

/* Copy service using prefix and suffixes 
 * Error returns the same as copy_file
 */

copy_service_file(char *sname, char *suffix, char *srcdir, char *destdir)
{
	char src[MAXPATHLEN+1], dest[MAXPATHLEN+1];

	(void) strcpy(src, srcdir);
	if((src[0] != '\0') && (src[strlen(srcdir)-1] != '/')) strcat(src, "/");
	(void) strcat(src, sname);
	(void) strcat(src, suffix);

	(void) strcpy(dest, destdir);
	if((dest[0] != '0') && (dest[strlen(destdir)-1] != '/'))
		strcat(dest, "/");
	(void) strcat(dest, sname);
	(void) strcat(dest, suffix);

	return(copy_file(src,dest));
}

void
print_service_path(struct search_path *p) 
{
	printf("search path :");
	
	while(p) {
		printf("%s:", p->path);
		p = p->next;
	}

	printf("\n");
}

/*
 *
 * Appends to search path
 * 	returns 0 on success
 *	ENOMEM on out of memory 
 */
int 
append_service_path(struct search_path **p, char *add)
{
	struct search_path *p1;

	if(*p == NULL) {
		/* Doesn't exist */
		*p = (struct search_path *) malloc(sizeof(struct search_path));
		if(*p == NULL) 
			return ENOMEM;
		(*p)->next = NULL;
		(*p)->path = strdup(add);
		if((*p)->path == NULL) 
			return ENOMEM;
		return 0;
	}

	/* Append action */

	p1 = *p;
	while(p1->next != NULL) p1 = p1->next;
	p1->next = (struct search_path *) malloc(sizeof(struct search_path));
	if(p1->next == NULL) 
		return ENOMEM;
	p1 = p1->next;
	p1->next = NULL;
	p1->path = strdup(add);
	if(p1->path == NULL) 
		return ENOMEM;
			
	return 0;
}

/* Read services file */
/* Will allocate the struct service field */
/* Returns -1 on failure, 0 success */
read_services_file(char *name, struct service **serv) 
{
	FILE *f;
	char buf[BUFSIZ];
	char *p;
	struct service *s;

	*serv = NULL;
	s = NULL;
	
	
	if((f = fopen(name, "r")) == NULL) 
		return -1;

	while(fgets(buf, BUFSIZ, f) != NULL) {
		/* Get first word only. Break on newline or space */
		p = buf;
		while(*p && !isspace(*p) && *p != '\n') p++;
		*p = '\0';
		if(s == NULL) {
			*serv = (struct service *) malloc(sizeof(struct service));
			if(!(*serv)) {
				(void) fclose(f);
				return -1;
			}
			s = *serv;
		} else {
			/* s valid, setup next */
			s->next = (struct service *) malloc(sizeof(struct service));
			if(!s->next) {
				(void) fclose(f);
				return -1;
			}
			s = s->next;
		}
		bzero(s, sizeof(struct service));
		if((s->name = strdup(buf)) == NULL) {
			(void) fclose(f);
			return -1;
		}
	}
		
	(void) fclose(f);
	return 0;
}

struct service *find_service(char *name, struct service *s)
{
	while(s) {
		if(!strcmp(s->name, name)) return s;
		s = s->next;
	}
	return NULL;
}

struct service *allocate_service(char *name)
{
	struct service *s;

	s = (struct service *) malloc(sizeof(struct service));
	if(s) {
		bzero(s, sizeof(struct service));
		if((s->name = strdup(name)) == NULL) {
			free(s);
			s = NULL;
		}
	}
	if(!s) fprintf(stderr, "Out of memory\n");
	return s;
}

/*
 * config_services
 * Will configure the serv structure by attaching filesystems and verifying 
 * everything is present.
 * Returns:
 *	0 - all ok with services list
 *	-1 - not ok
 */

config_services(struct service *serv, struct local_env *lenv)
{
	struct service *s;
	char *p;
	struct search_path *path = NULL;
	int fail = 0;

	/* Configure the path */
	/* First the /mit/mkserv/services/vers */
	if(append_service_path(&path, os_start_path(lenv))) return -1;
	
	/* Then the CONFIGDIR */
	if(append_service_path(&path, lenv->configdir)) return -1;

	/* Search the services for any other paths */
	s = serv;
	while(s) {
		s->configured = 0;
		if((p = index(s->name, ':')) != NULL) {
			if(os_path_ok(s->name)) return -1;
			if(append_service_path(&path, ++p)) return -1;
			s->configured = 1;
		} 
		s = s->next;
	} /* while */

	/* Lookup services now */
	s = serv;
	while(s) {
		if(index(s->name, ':') == NULL) {
			/* Real service */
			if(!get_service(s, path) && !check_platform(s)) {
				s->configured = 1;
			} else { 
				fail = -1;
			}
		}
		s = s->next;
	} /* while */

	if(fail) return fail;

	/* Handle dependencies.... */
	fail = depend_service(serv, path);
	return fail;
}

/* depend_service(struct service *serv, struct search_path *path)
 * Runs dependencies and builds up new services...
 * Return 0 - success
 * 	  1 - failure
 * Will run get_service on entries.... 
 */
depend_service(struct service *serv, struct search_path *path)
{
#define CPP_IN "/tmp/mkserv.cpp.in"
#define CPP_OUT "/tmp/mkserv.cpp.out"
	int done = 0, count=0, found, nserv=0;
	int out_fd, in_fd;
	char buf[BUFSIZ];
	struct service *s, *news;
	char *p;
	FILE *f;


	if(serv == NULL) return 0;
	/* Build up command file */
	while((done == 0) && (count <= nserv +2) ) {
		(void) unlink(CPP_IN);
		if((out_fd = open(CPP_IN, O_CREAT|O_TRUNC|O_WRONLY, 0777)) < 0)
			return -1;

		s = serv;
		nserv = 0;
		while(s) {
			nserv++;
			if(!index(s->name, ':')) {
				strcpy(buf, "#define ");
				strcat(buf, s->name);
				strcat(buf, " 1\n");
				if(write(out_fd, buf, strlen(buf)) != strlen(buf)){
					(void) close(out_fd);
					return -1;
				}
			} /* index */
			s = s->next;
			
		}

		/* Copy the deps as needed */
		found = 0;
		s = serv;
		while(s) {
			if(s->exists[S_DEP]) {
				found = 1;
				sprintf(buf, "%s/%s%s", s->path, s->name, 
					service_suffix(S_DEP));
				if((in_fd = open(buf, O_RDONLY)) < 0) {
					(void) close(out_fd);
					fprintf(stderr, "Depend internal failure\n");
					return -1;
				}
				if(copy_data(in_fd, out_fd) || 
				   (write(out_fd, "\n", 1) != 1)) {
					(void) close(in_fd);
					(void) close(out_fd);
					fprintf(stderr, "Could not copy data\n");
					return -1;
				}
				
			}
			s = s->next;
		} /* while */

		
		(void) close(out_fd);
		if(found == 0) {
			/* No deps */
			(void) unlink(CPP_IN);
			return 0;
		}	

		/* Run cpp */
		sprintf(buf, "/lib/cpp -D`/bin/athena/machtype` %s > %s", 
			CPP_IN, CPP_OUT);
		(void) system(buf);
		if(access(CPP_OUT, R_OK)) {
			fprintf(stderr, "Unexpected failure in getting dependencies\n");
			return -1;
		}

		/* Read the data in ... */
		done = 1;
		if((f = fopen(CPP_OUT, "r")) == NULL) return -1; 
		
		while(fgets(buf, BUFSIZ, f) != NULL) {
			if(buf[0] != '\0' && buf[0] != '#' && buf[0] != '\n') {
				p = buf;
				while(*p) {
					/* Convert to lower case */
					if(isupper(*p)) *p = tolower(*p);
					if(*p == '\n') *p='\0';
					else p++;
				}
				/* Get rid of white space */
				p = buf;
				while(*p && isspace(*p)) p++;
				/* At NULL or printing character... */
				if(*p && !isdigit(*p)) {
					/* Have a service name */
					/* Search if have already...
					   if so - someone screwed up dep */
					news = serv;
					while(news) {
						if(!strcmp(news->name, p)) {
							/* Terminate */
							news = NULL;
						} else {
							if(news->next) 
								news = news->next;
							else {
								done = 0;
								news->next = 
									allocate_service(p);
								if(!news->next) return -1;
								news = news->next;
								
								news->coreq =1;
								if(!get_service(news, path) &&
!check_platform(news)) {
									news->coreq =1;
									news->configured = 1;
								} else {
									return -1;
									}
								news = NULL;
									
							}
						}	
					}
				}
			} /* if buf */
		} /* while */
		(void) fclose(f);

		/* Loop again ? */
		count ++;
	} /* while */

	return 0;
}
			
	
	
/*
 * Check platform - checks the platform string of a service against 
 * operating system one.
 * Returns 0 - ok for this platform
 *        -1 - if not acceptable
 */
int check_platform(struct service *s)
{
	int len;
	char *p;
	char *os;

	if(!(p = s->fh[S_ADD].platforms)) return -1;

	os = os_platform();
	len = strlen(os);

	while(*p && *p != '$') {
		/* Iterate over p looking for string matches... */
		if(!strncmp(p, os, len)) {
			/* check end XXXX */
			return 0;
		} 

		/* Let's be spiffy and find the comma or end.. */
		while(*p && *p != ',' && *p != '$') p++;
		if(*p == ',') p++;
		
	}
	
	return -1;
}

/*
 * write_out_services(car *name, struct service *s)
 * Attempts to write out the services file with any changes/updates
 * Returns 0 success, -1 failure
 */
write_out_services(char *name, struct service *s)
{
	FILE *f;

	(void) unlink(name);

	if((f = fopen(name, "w")) == NULL) return -1;

	while(s) {
		if(s->coreq == 0) {
			fputs(s->name,f);
			if(s->fh[S_ADD].version) {
				fputs(" ", f);
				fputs(s->fh[S_ADD].version,f);
			}
			fputs("\n",f);
		}
		s = s->next;
	}

	if(fclose(f) == EOF) return -1;

	return 0;
}
	
