#if !defined(lint) && !defined(SABER)
static char rcsid[] = "$Id: os.c,v 1.3 1996/03/19 12:41:17 warlord Exp $";
#endif

/* This file handles the machine specific configuration */

#include "mkserv.h"
#include "os.h"
#include <pwd.h>

#ifdef _IBMR2
#include <userpw.h>
#include <usersec.h>
#include <sys/id.h>
#endif

/* 
 * char *os_serverdir() 
 *	Returns the operating system location of OS_SERVERDIR
 *	Static allocation - do not free or modify 
 */


char *
os_serverdir()
{
	return(OS_SERVERDIR);
}

char *
os_sitedir()
{
	return(OS_SITE);
}

char *os_versionfile()
{
	struct stat statb;
	static char versionfile[MAXPATHLEN+1];
	static int init=0;

	if(init) return versionfile;

	/* Test for correct */
	strcpy(versionfile, VERSION_FILE);
	if(!stat(versionfile, &statb)) {
		init=1;
		return versionfile;
	}
	/* Test for old */
	strcpy(versionfile, OLD_VERSION_FILE);
	if(!stat(versionfile, &statb)) {
		init=1;
		return versionfile;
	}
	/* Fallback */
	strcpy(versionfile, VERSION_FILE);
	return versionfile;
}

os_create_serverdir(char *dir)
{
#ifdef _IBMR2
	if(access(OS_SITE, F_OK)) {
		if(mkdir(OS_SITE, 0755)) {
			fprintf(stderr, "Unable to create %s\n", OS_SITE);
			return -1;
		}
	}			
#endif
	/* Umask handles the issues */
	if(mkdir(dir, 0755)) {
		fprintf(stderr, "Unable to create %s\n", dir);
		return -1;
	}
	return 0;
}

/* Checks if service path is valid 
 * Returns 0 if ok, -1 if not, -2 if format wrong
 * Handles parsing of filesystem:path
 * Will attach if necessary
 */
os_path_ok(char *name)
{
	char filesystem[MAXPATHLEN+1], path[MAXPATHLEN+1], *p;
	register DIR *dirp;

	if(name == NULL) return -2;

	strcpy(filesystem, name);
	if((p = index(filesystem, ':')) == NULL) {
		/* Format wrong.... fatal */
		return -2;
	}
	*p = '\0';
	p++;
	strcpy(path, name + (p - filesystem));

	/* May need to attach */
	if(filesystem[0]) {
		if(os_attach(filesystem)) return -1;
	}

	/* Code to check..... */

	if((dirp = opendir(path)) == NULL) {
		/* Can't open direct - fatal */
		return -1;
	}
	if(readdir(dirp) == NULL) {
		/* There has to be at least '.' */
		closedir(dirp);
		return -1;
	}
	closedir(dirp);
	
	/* Path looks ok */
	return 0;
}
	   
os_attach(char *filesys)
{
	char buf[MAXPATHLEN+1];
	sprintf(buf, "/bin/athena/attach -q -n -h %s\n", filesys);
	return(system(buf));
}

char *os_start_path(struct local_env *lenv)
{
	static char buf[MAXPATHLEN+1];
	char buf1[30], *p;

	strcpy(buf1, lenv->vers);
	p = buf1;
	while(*p && (isdigit(*p) || *p == '.')) p++;
	*p = '\0';
	
	sprintf(buf, "/mit/mkserv/services/%s", buf1);

	return buf;
}

char *os_platform()
{
	return (OS_PLATFORM);
}

void os_run_script(struct service *s, int d) 
{
	char buf[MAXPATHLEN+1];

	sprintf(buf, "%s/%s%s", s->path, s->name, service_suffix(d));
	if(s->fh[d].version) 
		printf("\nRunning %s (%s)\n", buf, s->fh[d].version);
	else
		printf("\nRunning %s\n", buf);
	system(buf);
}

/* char *os_swicth_service(char *line, char *on)
 * Switches service. Returns a poitiner to a new line after freeing the old
 * Returns NULL if unable to do so
 */
char *os_switch_service(char *line, char *on)
{
#if defined(ultrix) || defined(ibm032) || defined(vax)
	int count=0, ingap;
	char buf[200];
	char *p, *newstring;
	int force = 1;

	newstring="unswitched";
	if(!strcmp(on, "on")) {
		newstring="switched"; 
		force = 0;
	}
	/* swon = 1-> switched entry */
	
	/* First get to data point */
	/* Format: "service stream tcp nowait {switched/unswitched}" */
	p = line;
	ingap = 0;
	while(*p && count < 4) {
		if(*p == ' ' || *p == '\t') {
			ingap = 1;
		} else {
			if(ingap == 1) {
				ingap = 0;
				count ++;
			}
		}
		if(count < 4) p++;
	}

	if(!*p) return NULL;
	/* Now pointing at switched or unswitched */
	if(!strncmp(p, newstring, strlen(newstring)) && (force == 0)) {
		return line;
	}

#if 0
	/* We don't do this - some services by default are not enabled */
	/* Need to make a substitution */
	if(line[0] == '#') {
		strcpy(buf, line + 1);
		buf[p-line+1] = '\0';
	}
	else {
#endif
		strcpy(buf, line);
		buf[p-line]='\0';
#if 0
	}
#endif
	strcat(buf, newstring);
	ingap = 0;
	count = 0;
	while(*p && isalpha(*p)) p++;
	if(!*p) return NULL;
	strcat(buf, p);
	p = strdup(buf);
	if(p) free(line);
	return p;
#else /* ultrix, ibm032, vax */
#if defined(_IBMR2) || defined(sun) || defined(__NetBSD__) || defined(linux)
	char buf[200], *p;

	if(!strcmp(on, "on")) {
		if(line[0] == '#') {
			return line;
		} else {
			/* Need to insert # */
			strcpy(buf, "#");
			strcat(buf, line);
		}
	} else {
		if(line[0] != '#') {
			return line;
		} else {
			/* Need to remove a # */
			strcpy(buf, line + 1);
		}
	}
	p = strdup(buf);
	if(p) free(line);
	return p;
		
#else
need configuration	    
#endif /* IBMR2 */
#endif
}

/* The next two routines are stolen from hesiod/hespwnam.c */
/* Move the pointer forward to the next colon-separated field in the
 * password entry.
 */

static char *
_NextPWField(ptr)
char *ptr;
{
	while (*ptr && *ptr != '\n' && *ptr != ':')
		ptr++;
	if (*ptr)
		*ptr++ = '\0';
	return(ptr);
}

struct passwd * os_convert_hesiod_to_pwd(char *line) 
{
	static char buf[256];
	static struct passwd pw_entry;

	register char *p; 
	(void) strcpy(buf, line);
	p = buf;
	pw_entry.pw_name = p;
	p = _NextPWField(p);
	pw_entry.pw_passwd = p;
	p = _NextPWField(p);
	pw_entry.pw_uid = atoi(p);
	p = _NextPWField(p);
	pw_entry.pw_gid = atoi(p);
#if (!defined(_AIX) || (AIXV < 31)) && !defined(sun) && !defined(__NetBSD__) && !defined(linux)
	pw_entry.pw_quota = 0;
#if defined(_AIX) && (AIXV < 31)
	pw_entry.pw_age =
#endif
	pw_entry.pw_comment = "";
#endif
#if defined(__NetBSD__)
	pw_entry.pw_class=NULL;
	pw_entry.pw_expire=0;
#endif
	p = _NextPWField(p);
	pw_entry.pw_gecos = p;
	p = _NextPWField(p);
	pw_entry.pw_dir = p;
	p = _NextPWField(p);
	pw_entry.pw_shell = p;
	while (*p && *p != '\n')
		p++;
	*p = '\0';
	return(&pw_entry);
}

static lock_passwd()
{
	int cnt, fd;

	cnt = 10;
	while (cnt-- > 0 &&
	       (fd = open("/etc/ptmp", O_WRONLY|O_CREAT|O_EXCL, 0644)) < 0)
		sleep(1);
	if (fd < 0) {
		printf("Failed to lock passwd\n");
	}
	(void) close(fd);
}

static unlock_passwd() 
{
	(void) unlink("/etc/ptmp");
}
os_add_to_passwd (struct passwd *pwd) 
{
#if defined(ultrix) || defined(vax) || defined(ibm032) || defined(sun) || defined(linux)
	FILE *pfile;
	struct stat statb;

	/* Handle /etc/passwd and /etc/passwd.local */
	if(!stat("/etc/passwd.local", &statb)) {
		/* Copy /etc/passwd to /etc/passwd.local */
		printf("Copying /etc/passwd to /etc/passwd.local\n");
		if(copy_file("/etc/passwd", "/etc/passwd.local")) {
			perror("Copying passwd.local");
			unlink("/etc/passwd.local");
		}
	}
#if !defined(sun) && !defined(linux)
	setpwfile("/etc/passwd.local");
#endif
	if(getpwnam(pwd->pw_name) == NULL) {
		lock_passwd();
		if((pfile=fopen("/etc/passwd.local", "a")) != NULL) {
			fprintf(pfile, "%s:%s:%d:%d:%s:%s:%s\n",
				pwd->pw_name,
				pwd->pw_passwd,
				pwd->pw_uid,
				pwd->pw_gid,
				pwd->pw_gecos,
				pwd->pw_dir,
				pwd->pw_shell);
			fclose(pfile);
		}
		unlock_passwd();
	}
#if !defined(sun) && !defined(linux)
	setpwfile("/etc/passwd");
#endif
	if(getpwnam(pwd->pw_name) == NULL) {
		lock_passwd();
		if((pfile=fopen("/etc/passwd", "a")) != NULL) {
			fprintf(pfile, "%s:%s:%d:%d:%s:%s:%s\n",
				pwd->pw_name,
				pwd->pw_passwd,
				pwd->pw_uid,
				pwd->pw_gid,
				pwd->pw_gecos,
				pwd->pw_dir,
				pwd->pw_shell);
			fclose(pfile);
		}
		unlock_passwd();
	}
	return 0;
#else
#ifdef _IBMR2
	struct userpw pw_stuff;

	if(getpwnam(pwd->pw_name) == NULL) {
		putuserattr(pwd->pw_name,(char *)NULL,((void *) 0),SEC_NEW);
		putuserattr(pwd->pw_name,S_ID,(void *)(pwd->pw_uid),SEC_INT);
		putuserattr(pwd->pw_name,S_PWD,(void *)"!",SEC_CHAR);
		putuserattr(pwd->pw_name,S_PGRP,(void *)"mit",SEC_CHAR);
		putuserattr(pwd->pw_name,S_HOME,(void *)(pwd->pw_dir),SEC_CHAR);
		putuserattr(pwd->pw_name,S_SHELL,(void *)(pwd->pw_shell),SEC_CHAR);
		putuserattr(pwd->pw_name,S_GECOS,(void *)(pwd->pw_gecos),SEC_CHAR);
		putuserattr(pwd->pw_name,S_LOGINCHK,(void *)1,SEC_BOOL);
		putuserattr(pwd->pw_name,S_SUCHK,(void *)1,SEC_BOOL);
		putuserattr(pwd->pw_name,S_RLOGINCHK,(void *)1,SEC_BOOL);
		putuserattr(pwd->pw_name,S_ADMIN,(void *)0,SEC_BOOL);
		/* putuserattr(p->pw_name,"athena_temp",(void *)1,SEC_INT);*/
		putuserattr(pwd->pw_name,(char *)0,((void *) 0),SEC_COMMIT);

		enduserdb();

		setpwdb(S_READ|S_WRITE);
		strncpy(pw_stuff.upw_name,pwd->pw_name,PW_NAMELEN);
		pw_stuff.upw_passwd = pwd->pw_passwd;
		pw_stuff.upw_flags = 0;
		pw_stuff.upw_lastupdate = 0;
		putuserpw(&pw_stuff);
		endpwdb();
	}
	return 0;
#else
#if defined(__NetBSD__)
	FILE *pfile;
	struct stat statb;

	if(!stat("/etc/master.passwd.local", &statb)) {
		/* Copy /etc/master.passwd to /etc/master.passwd.local */
		printf("Copying /etc/master.passwd to /etc/master.passwd.local\n");
		if(copy_file("/etc/master.passwd", "/etc/master.passwd.local")) {
			perror("Copying master.passwd.local");
			unlink("/etc/master.passwd.local");
		}
	}
	if(getpwnam(pwd->pw_name) == NULL) {
		lock_passwd();
		if((pfile=fopen("/etc/master.passwd.local", "a")) != NULL) {
			fprintf(pfile, "%s:%s:%d:%d::0:0:%s:%s:%s\n",
				pwd->pw_name,
				pwd->pw_passwd,
				pwd->pw_uid,
				pwd->pw_gid,
				pwd->pw_gecos,
				pwd->pw_dir,
				pwd->pw_shell);
			fclose(pfile);
		}
		unlock_passwd();
	}
	if(getpwnam(pwd->pw_name) == NULL) {
		lock_passwd();
		if((pfile=fopen("/etc/passwd", "a")) != NULL) {
			fprintf(pfile, "%s:%s:%d:%d:%s:%s:%s\n",
				pwd->pw_name,
				pwd->pw_passwd,
				pwd->pw_uid,
				pwd->pw_gid,
				pwd->pw_gecos,
				pwd->pw_dir,
				pwd->pw_shell);
			fclose(pfile);
		}
		unlock_passwd();
	}
	return 0;
#else
Need configuration
#endif /* NetBSD */
#endif /* _IBMR2*/
#endif
}

#if defined(vax) || defined(ultrix) || defined(ibm032) || defined(sun) || defined (__NetBSD__) || defined(linux)
static void os_remove_pentry(char *name, char *file)
{
    FILE *newfile;
    struct passwd *copypw;
    struct stat statb;
    int cnt, fd;

    cnt = 10;
    while (cnt-- > 0 &&
	   (fd = open("/etc/ptmp", O_WRONLY|O_CREAT|O_EXCL, 0644)) < 0)
      sleep(1);
    if (fd < 0) {
	fprintf(stderr, "Failed to remove passwd entry %s\n", name);
	return;
    }
    
    if ((newfile = fdopen(fd, "w")) != NULL) {
	    setpwent();
	    while ((copypw = getpwent()) != 0)
		    if (strcmp(copypw->pw_name, name))
#ifdef __NetBSD__
			    fprintf(newfile, "%s:%s:%d:%d::0:0:%s:%s:%s\n",
#else
			    fprintf(newfile, "%s:%s:%d:%d:%s:%s:%s\n",
#endif
				    copypw->pw_name,
				    copypw->pw_passwd,
				    copypw->pw_uid,
				    copypw->pw_gid,
				    copypw->pw_gecos,
				    copypw->pw_dir,
				    copypw->pw_shell);
	    endpwent();
	    fclose(newfile);
	    if (stat("/etc/ptmp", &statb) != 0 || statb.st_size < 80) {
		    fprintf(stderr, "Something stomped on /etc/ptmp during removal of passwd entry\n");
	    } else
		    rename("/etc/ptmp", file);
    }
}
os_remove_passwd(char *name)
{
#if !defined(sun) && !defined (__NetBSD__) && !defined(linux)
	setpwfile("/etc/passwd.local");
#endif
	if(getpwnam(name) != NULL) {
		os_remove_pentry(name, "/etc/passwd.local");
	}

#if !defined(sun) && !defined (__NetBSD__) && !defined(linux)
	setpwfile("/etc/passwd");
#endif
	if(getpwnam(name) != NULL) {
		os_remove_pentry(name, "/etc/passwd");
	}
	return 0;
}
#else
#ifdef _IBMR2
os_remove_passwd(char *name)
{
    static char *empty = "\0";
    char *grp, *usr;
    int i;
    
    if(getpwnam(name) != NULL) {
	    setuserdb(S_READ|S_WRITE);
	    
	    /* Decrement reference count on user's temporary groups */
	    if (getuserattr(name, S_GROUPS, (void *)&grp, SEC_LIST) == 0) {
		    while (*grp) {
			    if (getgroupattr(grp, "athena_temp", (void *)&i, SEC_INT) == 0) {
				    if (--i > 0) {
					    putgroupattr(grp, "athena_temp", 
							 (void *)i, SEC_INT);
					    putgroupattr(grp, (char *)0,
							 (void *)0, SEC_COMMIT);
				    } else {
					    putgroupattr(grp, S_USERS,
							 (void *)empty, SEC_LIST);
					    putgroupattr(grp, (char *)0,
							 (void *)0, SEC_COMMIT);
					    rmufile(grp, 0, GROUP_TABLE);
				    }
			    }
			    while(*grp) grp++;
			    grp++;
		    }
	    }

	    /* Decrement reference count on temporary users */
	    if (getuserattr(name, "athena_temp", (void *)&i, SEC_INT) == 0) {
		    if (--i > 0) {
			    putuserattr(name, "athena_temp", (void *)i, SEC_INT);
			    putuserattr(name, (char *)0, (void *)0, SEC_COMMIT);
		    } else {
			    putuserattr(name, S_GROUPS, (void *)empty, SEC_LIST);
			    putuserattr(name, (char *)0, (void *)0, SEC_COMMIT);
			    rmufile(name, 1, USER_TABLE);
		    }
	    }

	    /* Remove any empty temporary groups */
	    grp = nextgroup(S_LOCAL, 0);
	    while (grp) {
		    if (getgroupattr(grp, "athena_temp", 
				     (void *)&i, SEC_INT) == 0) {
			    if ((getgroupattr(grp, S_USERS,
					      (void *)&usr, SEC_LIST) == -1) ||
				*usr == 0)
			    {
				    rmufile(grp, 0, GROUP_TABLE);
			    }
		    }
		    grp = nextgroup(0, 0);
	    }
	    
	    enduserdb();
    }
    return 0;
}
#else
Need confirguartion
#endif /* IBMR2 */
#endif /* os_remove_passwd */

char *os_where_inetd()
{
return(OS_INETD);
}

/* Converts a BSD style inetd entry to machine specific */
char *os_convert_service_to_inetd(char *s)
{
#if defined(vax) || defined(ibm032) || defined(ultrix) || defined(__NetBSD__) || defined (linux)
	return(s);
#else
#if defined(_IBMR2) || defined(sun)
	/* Need to remove switched or unswitched characters */
	int count = 0;
	char buf[300];
	int ingap=0;
	char *p=s, *p1=buf;
	while(*p && count < 4) {
		if(*p == ' ' || *p == '\t') {
			ingap = 1;
		} else {
			if(ingap == 1) {
				ingap = 0;
				count ++;
			}
		}
		if(count < 4) *p1++ = *p++;
	}

	if(!*p) return NULL;
	/* Now pointing at switched or unswitched */
	/* search for next space */
	while(*p && (*p != ' ') && (*p != '\t')) p++;
	/* Add a username to run commands */
	strcat(p1, "root");
	p1 += 4; /* length of root */

	while(*p) *p1++ = *p++;
	*p1 = '\0';
	p = strdup(buf);
	if(p) free(s);
	return p;
#else
Need configuration
#endif /* IBMR2 */
#endif
}

os_usr_not_on_root()
{
  return(OS_USR_NOT_ON_ROOT);
}
