#if !defined(lint) && !defined(SABER)
static char rcsid[] = "$Id: mkserv.c,v 1.7 1995/08/27 06:23:40 yoav Exp $";
#endif

#include "mkserv.h"

static void cat_file();

main(int argc, char **argv)
{
	struct cmd_line cmd;
	struct cmd_service *cs;
	struct local_env env;
	char servicesfile[MAXPATHLEN + 1];
	struct stat sbuf;
	char syncopts[50];
	int ret;
	int bad=0;
	char buf[BUFSIZ], buf1[200];
	time_t my_time;
	struct service *current_services = NULL, *new_services = NULL, *sp, *ns;

	umask(022);
	ret = parse_cmd(argc, argv, &cmd);
	if(ret == -2) {
		fprintf(stderr, "Out of memory\n");
		exit(1);
	}

	if(ret == -1) {
		fprintf(stderr, "%s: [-v] [service ...] [-service ...]\n", 
			cmd.progname);
		exit(2);
	}

#ifdef __NetBSD__
	/* since we don't use a script, we make sure mkserv is attached
	   in the binary, where the default services are
	   */
	if ((stat("/mit/mkserv", &sbuf)) != 0) {
		system("attach -h -n -q mkserv");
	}
#endif

	if((getuid() != 0) && (cmd.updatetest == 0)) {
		fprintf(stderr, "Must be root to run mkserv\n");
		exit(11);
	}
	/* Have parsed commands */
	/* Get the environment */
	if(setup_env(&env)) {
		fprintf(stderr, "%s: Unable to setup environment\n",
			cmd.progname);
		exit(3);
	}
	

	if(!strcmp(env.vers, "Update")) {
		fprintf(stderr, "You are in the middle of an update..\n");
		exit(1);
	}

	sprintf(servicesfile, "%s/.services", env.serverdir);
	if(access(servicesfile, R_OK) == 0) {
		if(read_services_file(servicesfile, &current_services)) {
			fprintf(stderr, "Problem reading current services\n");
			exit(5);
		}
	}

	/* Verify that current_services configs are all present */
	if(config_services(current_services, &env)) {
		if(cmd.updatetest) exit(15);
		/* We have a problem ... */
		fprintf(stderr, "Services missing from path configuration: ");
		while(current_services) {
			if(current_services->configured == 0) 
				fprintf(stderr, "%s ", current_services->name);
			current_services = current_services->next;
		}
		fprintf(stderr, "\n");
		exit(9);
	}
			

	/* Handle command line arguments. Deletions etc... */

	/* First copy current services to  new_services for handling */
	/* For public - remove all services */
	/* For clean - don't do any deletions.... */
	if(!cmd.public) {
		sp = current_services;
		ns = NULL;
		while(sp) {
			if(sp->coreq == 0) {
				/* Don't copy coreqs - let them be
				   determined */
				if(ns) {
					ns->next = allocate_service(sp->name);
					if(!ns->next) exit(6);
					ns = ns->next;
				} else {
					ns = allocate_service(sp->name);
					if(!ns) exit(6);
					new_services = ns;
				}
			}
			sp = sp->next;
		} /* while */

		/* Deletions are removed, additions added... */

		cs = cmd.services;
		while(cs) {
			sp = find_service(cs->sname, new_services);
			if(sp) {
				/* Have a definition of service */
				if((sp->del != cs->del) && (cmd.clean == 0)) {
					/* Have to remove */
					if(sp == new_services) {
						free_service(new_services);
						new_services = new_services->next;
					} else { /* Not head of list */
						ns = new_services;
						while(ns && ns->next != sp) 
							ns = ns->next;
						/* Pointing at culprit */
						if(!ns) {
							fprintf(stderr, "Fatal error in logic\n"); 
							exit(8);
						}
						free_service(ns->next);
						ns->next = ns->next->next;
					}

				}
			} else /* service found */ {
				/* Don't have a def for service */
				/* Append.... if not deleting */
				if(cs->del == 0) {
					sp = new_services;
					while(sp && sp->next) sp = sp->next;
					if(sp == NULL) {
						/* First add tack on new_service*/
						new_services = allocate_service(cs->sname);
						if(!new_services) exit(7);
					} else {
						sp->next = allocate_service(cs->sname);
						if(!sp->next) exit(7);
					}

				}
			} /* find_service */
			cs = cs->next;
		}

			


	} /* public */


	/* Now to embark on the meat of the matter. Have the list of services,
	   so find dependency generated ones...*/
	/* Check if we are configured properly */
	/* Verify that current_services configs are all present */
	if(config_services(new_services, &env)) {
		if(cmd.updatetest) exit(15);
		/* We have a problem ... */
		fprintf(stderr, "The following services could not be found:\n");
		while(new_services) {
			if(new_services->configured == 0) 
				fprintf(stderr, "%s ", new_services->name);
			new_services = new_services->next;
		}
		fprintf(stderr, "\n");
		exit(10);
	}

	if(cmd.updatetest) exit(0);

	/* From here on out must be root */
	if(getuid() != 0) {
		fprintf(stderr, "Must be root to run mkserv\n");
		exit(11);
	}

	if(!strcmp(env.vers, "Update")) {
		fprintf(stderr, "You are in the middle of an update..\n");
		exit(1);
	}

	if(stat(env.serverdir, &sbuf)) {
		if(os_create_serverdir(env.serverdir)) exit(4);
	}

	/* We is golden - all services check out */
	/* Output the list to a file */
	my_time = time((time_t *)0);
	sprintf(buf, "Athena Server (%s) Version Update %s",
		env.mach, ctime(&my_time));
	append_file(os_versionfile(), buf);

	if(write_out_services(env.services, new_services)) {
		fprintf(stderr, "Unable to create temp services file\n");
		exit(11);
	}

	/* What else to unlink ???? */
	(void) unlink(env.confcng);
	(void) unlink(env.logfile);
	creat(env.logfile, 0755);
	creat(env.confcng, 0755);
	if(cmd.public) {
		append_file(env.confcng,"conf PUBLIC default\n");
	} else {
		append_file(env.confcng,"conf PUBLIC false\n");

	}

	/* let's prune running add and del scripts */
	/* First the deletions */
 	sp = current_services;
	while(sp) {
		if(!find_service(sp->name, new_services) && 
		   !index(sp->name, ':')) {
			if(sp->exists[S_DEL]) {
				os_run_script(sp, S_DEL);
			}
		}
		sp = sp->next;
	}

	/* Now the additions */
 	sp = new_services;
	while(sp) {
		if(!index(sp->name, ':')) {
			if(sp->exists[S_ADD]) {
				os_run_script(sp, S_ADD);
			}
		}
		sp = sp->next;
	}


	if (!new_services) {
		printf("\nRemoving %s\n", env.serverdir);
		sprintf(buf, "/bin/rm -rf %s", env.serverdir);
		system(buf);
	}

	/* Run synctreees */
	strcpy(syncopts, "-nodstrules");
	if(!cmd.vflag) strcat(syncopts," -q");

	fprintf(stdout, "\nUpdating %s\n", env.serverdir);
	if(new_services) 
		bad = synctree_services(new_services, &env, syncopts);

	/* Clean root symlinks */
	fprintf(stdout, "Updating root symlinks...\n");
	if(fix_root_links(env.serverdir, env.rvdroot)) {
		fprintf(stderr, "Error in fixing root links\n");
	}

	/* Fix root synctree */
	if((bad == 0) && new_services) {
		synctree_final(env.serverdir, syncopts);
	}

	/* Fix services based on config file */
	ret = 0;
	if(bad == 0) {
		ret = change_configs(env.confcng);
	}

	/* cleanup */

	/* Finish off */
	printf("\n");
	
	cat_file(env.logfile);
	(void) unlink(env.logfile);

	if(new_services && write_out_services(servicesfile, new_services)) {
		fprintf(stderr, "Unable to create services file\n");
		exit(11);
	}


	sprintf(buf, "%s/.private", env.serverdir);
	if(!access(buf, X_OK)) {
		printf("Running %s ...\n", buf);
		sprintf(buf, "sh %s/.private", env.serverdir);
		ret = system(buf);
		if(ret) bad = 1;
	}

	strcpy(buf, "Workstation");
	if(!access(env.serverdir, F_OK)) strcpy(buf, "Server");
	if(bad == 0) {
		my_time = time((time_t *)0);
		sprintf(buf1, "Athena %s (%s) Version %s %s",
			buf, env.mach, env.vers, ctime(&my_time));
		append_file(os_versionfile(), buf1);
	}		
	
	printf("\n\nUpdate finished.\n\n");
	printf("The service change requests will take effect upon the next reboot\nof the machine.\n");

	if(!access(env.serverdir, F_OK)) {
		printf("\n%s has been used for the server specific files.\n", env.serverdir);
		printf("Do not modify these files as they can be replaced during an update\n");
	}
	exit(0);
}

static void cat_file(char *name) 
{
	FILE *f;
	char buf[BUFSIZ];

	if((f = fopen(name, "r")) == NULL) return;
	while(fgets(buf, BUFSIZ, f) != NULL) {
		fputs(buf, stdout);
	}

	fclose(f);
}



