v08i048: Account creation/manipulation program, Part08/08

sources-request at mirror.UUCP sources-request at mirror.UUCP
Sat Feb 7 07:22:39 AEST 1987


Submitted by: Kyle Jones <xanth!kyle>
Mod.sources: Volume 8, Issue 48
Archive-name: mcp/Part08

#! /bin/sh
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
# If all goes well, you will see the message "End of archive 8 (of 8)."
# Contents:  src/save.c src/save.h src/shell.c src/sig.c src/sig.h
#   src/signals.c src/sort.c src/sort.h src/sysdep.h src/tty.c
#   src/version.c src/yesno.c src/update.c
# Wrapped by rs at mirror on Fri Feb  6 15:56:15 1987
PATH=/bin:/usr/bin:/usr/ucb; export PATH
echo shar: extracting "'src/save.c'" '(8666 characters)'
if test -f 'src/save.c' ; then 
  echo shar: will not over-write existing file "'src/save.c'"
else
sed 's/^X//' >src/save.c <<'@//E*O*F src/save.c//'
X/**************************************************************************\
X* 									   *
X* 	save.c								   *
X* 									   *
X* These are the routines that save the information into the respective	   *
X* accounting files.  An important thing to remember here is that the save  *
X* and checkpointing routines USE THE SAME TEMPORARY FILES.  So no	   *
X* checkpointing must be done while saving is being done, and vice versa.   *
X* Conflicts are avoided by blocking the all signals that would trigger	   *
X* either periodic or crash checkpointing, until saving is complete.	   *
X* 									   *
X* Also the tempfiles must be in the same filesystem as their associated	   *
X* accounting files or the rename() system call will fail attempting to	   *
X* link the tempfile to the accounting file with errno == EXDEV.		   *
X* 									   *
X\**************************************************************************/
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <signal.h>
X#include <lastlog.h>
X#include <strings.h>
X#include "sysdep.h"
X#include "macros.h"
X#include "mem.h"
X#include "lists.h"
X#include "account.h"
X#ifdef SENDMAIL
X#include "alias.h"
X#endif
X#include "class.h"
X#include "sig.h"
X#include "range.h"
X#include "groupmap.h"
X#include "save.h"
X
Xextern	int ModBits;
Xextern	time_t PWLockTime, time();
X
X#ifdef SENDMAIL
Xextern	struct list AliasList;
X#endif
Xextern	struct list AccountList, GroupMapList, SigList, ClassList, RangeList;
Xextern	struct list Vigs;
X
Xsave_pw()
X
X{
X	FILE *pwf;
X	int i;
X	struct account *ac;
X#ifdef BSD4_3
X	char *av[4];
X	char tmpdotdir[MEDIUM_BUF], tmpdotpag[MEDIUM_BUF];
X	char dotdir[MEDIUM_BUF], dotpag[MEDIUM_BUF];
X#endif
X
X	pwf = fopen(PWDTMP, "w");
X	if (pwf == NULL) {
X		perr(PWDTMP);
X		return;
X	}
X	for (i=0; i < AccountList.l_count; i++) {
X		ac = (struct account *) AccountList.l_list[i];
X		(void) fprintf(pwf, "%s:%s:%d:%d:%s:%s:%s\n",
X			ac->ac_name,
X			ac->ac_passwd,
X			ac->ac_uid,
X			ac->ac_gid,
X			ac->ac_gecos,
X			ac->ac_dir,
X			ac->ac_shell);
X	}
X	(void) fclose(pwf);
X#ifdef BSD4_3
X	(void) strcpy(tmpdotdir, PWDTMP);
X	(void) strcpy(tmpdotpag, PWDTMP);
X	(void) strcpy(dotdir, PWDFILE);
X	(void) strcpy(dotpag, PWDFILE);
X	(void) strcat(tmpdotdir, ".dir");
X	(void) strcat(tmpdotpag, ".pag");
X	(void) strcat(dotdir, ".dir");
X	(void) strcat(dotpag, ".pag");
X	(void) unlink(tmpdotdir);
X	(void) unlink(tmpdotpag);
X	av[0] = "shell-escape";
X	av[1] = DBMPASSWORD;
X	av[2] = PWDTMP;
X	av[3] = (char *) 0;
X	if (shellescape(3, (addr *)av) != 0) {
X		err1("%s failed", DBMPASSWORD);
X		return;
X	}
X	if (rename(tmpdotdir, dotdir) == -1) {
X		perr("rename");
X		err2("%s -> %s rename failed", tmpdotdir, dotdir);
X		return;
X	}
X	if (rename(tmpdotpag, dotpag) == -1) {
X		perr("rename");
X		err2("%s -> %s rename failed", tmpdotdir, dotdir);
X		return;
X	}
X#endif
X	if (rename(PWDTMP, PWDFILE) == -1) {
X		perr(PWDTMP);
X		return;
X	}
X	ModBits &= ~PW;
X	(void) unlink(PWDCKP);
X	return;
X}
X
X#ifdef SENDMAIL
Xsave_al()
X
X{
X	FILE *alf, *bindf;
X	struct alias *al;
X	char *av[3];
X	register int i;
X
X	alf = fopen(ALIASTMP, "w");
X	if (alf == NULL) {
X		perr(ALIASTMP);
X		return;
X	}
X	bindf = fopen(ALBINDTMP, "w");
X	if (bindf == NULL) {
X		perr(ALBINDTMP);
X		(void) fclose(alf);
X		return;
X	}
X	for (i=0; i < AliasList.l_count; i++) {
X		al = (struct alias *) AliasList.l_list[i];
X		(void) fprintf(alf, "%s:", al->al_name);
X		listout(&al->al_addresses, alf);
X		fputs("\n", alf);
X		(void) fprintf(bindf, "%s:", al->al_name);
X		listout(&al->al_groups, bindf);
X		fputs(":", bindf);
X		listout(&al->al_classes, bindf);
X		fputs(":", bindf);
X		listout(&al->al_sigs, bindf);
X		fputs("\n", bindf);
X	}
X	(void) fclose(alf);
X	(void) fclose(bindf);
X	av[0] = "shell-escape";
X	av[1] = NEWALIASES;
X	av[2] = (char *)0;
X	if (rename(ALIASTMP, ALIASFILE) == -1) {
X		perr(ALIASTMP);
X		return;
X	}
X	if (rename(ALBINDTMP, ALBIND) == -1) {
X		perr(ALBINDTMP);
X		return;
X	}
X	if (shellescape(2, (addr *) av) != 0) {
X		err1("newaliases seemed unhappy with %s", ALIASFILE);
X		return;
X	}
X	ModBits &= ~AL;
X	(void) unlink(ALIASCKP);
X	(void) unlink(ALBINDCKP);
X}
X#endif
X
Xsave_ac()
X
X{
X	FILE *acf;
X	register int i;
X	struct account *ac;
X
X	acf = fopen(ACTMP, "w");
X	if (acf == NULL) {
X		perr(ACTMP);
X		return;
X	}
X	for (i=0; i < AccountList.l_count; i++) {
X		ac = (struct account *) AccountList.l_list[i];
X		(void) fprintf(acf, "%s:%s:%s:%d:%d:",
X			ac->ac_name,
X			ac->ac_realname,
X			ac->ac_id,
X			ac->ac_uid,
X			ac->ac_gid);
X		listout(&ac->ac_groups, acf);
X		fputs(":", acf);
X		listout(&ac->ac_classes, acf);
X		fputs(":", acf);
X		listout(&ac->ac_sigs, acf);
X		fputs(":", acf);
X#ifdef SENDMAIL
X		listout(&ac->ac_aliases, acf);
X#endif
X		fputs("\n", acf);
X	}
X	(void) fclose(acf);
X	if (rename(ACTMP, ACFILE) == -1) {
X		perr(ACTMP);
X		return;
X	}
X	ModBits &= ~AC;
X	(void) unlink(ACCKP);
X	return;
X}
X
Xsave_gr()
X
X{
X	FILE *grf;
X	register int i;
X	struct groupmap *gm;
X
X	grf = fopen(GRPTMP, "w");
X	if (grf == NULL) {
X		perr(GRPTMP);
X		return;
X	}
X	for (i=0; i < GroupMapList.l_count; i++) {
X		gm = (struct groupmap *) GroupMapList.l_list[i];
X		(void) fprintf(grf, "%s:%s:%d:",
X			gm->gm_name,
X			gm->gm_passwd,
X			gm->gm_gid);
X		listout(&gm->gm_mem, grf);
X		fputs("\n", grf);
X	}
X	(void) fclose(grf);
X	if (rename(GRPTMP, GRPFILE) == -1) {
X		perr(GRPTMP);
X		return;
X	}
X	ModBits &= ~GR;
X	(void) unlink(GRPCKP);
X	return;
X}
X
Xsave_cs()
X
X{
X	struct class *cs;
X	register int i;
X	FILE *csf;
X
X	csf = fopen(CSTMP, "w");
X	if (csf == NULL) {
X		perr(CSTMP);
X		return;
X	}
X	for (i=0; i < ClassList.l_count; i++) {
X		cs = (struct class *) ClassList.l_list[i];
X		(void) fprintf(csf, "%s %d %d\n", cs->cs_name, cs->cs_dsize,
X					   cs->cs_exptime);
X		(void) fprintf(csf, "%s", cs->cs_desc);
X	}
X	(void) fclose(csf);
X	if (rename(CSTMP, CSFILE) == -1) {
X		perr(CSTMP);
X		return;
X	}
X	ModBits &= ~CS;
X	(void) unlink(CSCKP);
X	return;
X}
X
Xsave_sg()
X
X{
X	struct sig *sg;
X	register int i;
X	FILE *sgf;
X
X	sgf = fopen(SIGTMP, "w");
X	if (sgf == NULL) {
X		perr(SIGTMP);
X		return;
X	}
X	for (i=0; i < SigList.l_count; i++) {
X		sg = (struct sig *) SigList.l_list[i];
X		(void) fprintf(sgf, "%s %d %d\n", sg->sg_name, sg->sg_dsize,
X					   sg->sg_exptime);
X		(void) fprintf(sgf, "%s", sg->sg_desc);
X	}
X	(void) fclose(sgf);
X	if (rename(SIGTMP, SIGFILE) == -1) {
X		perr(SIGTMP);
X		return;
X	}
X	ModBits &= ~SG;
X	(void) unlink(SIGCKP);
X	return;
X}
X
Xsave_rg()
X
X{
X	struct range *rg;
X	register int i;
X	FILE *rgf;
X
X	rgf = fopen(RANGETMP, "w");
X	if (rgf == NULL) {
X		perr(RANGETMP);
X		return;
X	}
X	for (i=0; i < RangeList.l_count; i++) {
X		rg = (struct range *) RangeList.l_list[i];
X		(void) fprintf(rgf, "%s\t%d\t%d\t%s\n",
X			rg->rg_name,
X			rg->rg_from,
X			rg->rg_to,
X			(rg->rg_mode == RG_SHARED ? "shared" : "exclusive"));
X	}
X	(void) fclose(rgf);
X	if (rename(RANGETMP, RANGEFILE) == -1) {
X		perr(RANGETMP);
X		return;
X	}
X	ModBits &= ~RG;
X	(void) unlink(RANGECKP);
X	return;
X}
X
Xsave_vg()
X
X{
X	register int i;
X	FILE *vgf;
X
X	vgf = fopen(VIGTMP, "w");
X	if (vgf == NULL) {
X		perr(VIGTMP);
X		return;
X	}
X	for (i=0; i < Vigs.l_count; i++)
X		(void) fprintf(vgf, "%s\n", Vigs.l_list[i]);
X	(void) fclose(vgf);
X	if (rename(VIGTMP, VIGFILE) == -1) {
X		perr(VIGTMP);
X		return;
X	}
X	ModBits &= ~VG;
X	(void) unlink(VIGCKP);
X	return;
X}
X
Xsaveandexit()
X
X{
X	savechanges();
X	exitmcp();
X}
X
Xsavechanges()
X
X{
X	if (ModBits == 0 || lock_check() == 0)
X		return;
X	/*
X	 * Interrupts are disabled for obvious reasons.
X	 * SIGTERM, SIGALRM, SIGHUP, and SIGQUIT must be blocked because 
X	 * the save and checkpoint routines use the same tempfiles.
X	 * If a checkpoint were to occur while files where being saved,
X	 * chaos would ensue.
X	 */
X	critical();
X
X	do_jobs();
X
X	(ModBits&PW) && backup(PW) && save_pw();
X	(ModBits&AC) && backup(AC) && save_ac();
X#ifdef SENDMAIL
X	(ModBits&AL) && backup(AL) && save_al();
X#endif
X	(ModBits&CS) && backup(CS) && save_cs();
X	(ModBits&GR) && backup(GR) && save_gr();
X	(ModBits&RG) && backup(RG) && save_rg();
X	(ModBits&SG) && backup(SG) && save_sg();
X	(ModBits&VG) && backup(VG) && save_vg();
X	sync();
X	(void) time(&PWLockTime);
X
X	non_critical();
X	return;
X}
X
Xstatic	char *acctfile[] = {
X	PWDFILE,
X	ACFILE,
X#ifdef SENDMAIL
X	ALIASFILE,
X	ALBIND,
X#endif
X	CSFILE,
X	GRPFILE,
X	RANGEFILE,
X	SIGFILE,
X	VIGFILE,
X	(char *) 0
X};
X
Xint
Xlock_check()
X
X{
X	register int i;
X	struct stat s;
X	int uhoh = 0;
X
X	if (!fileexists(PWDLOCK)) {
X	    err1("My %s lockfile has been been removed!", PWDLOCK);
X	    if (yesno("Do the save anyway? ") == 0)
X		return 0;
X	}
X	for (i=0; acctfile[i]; i++) {
X	    if (stat(acctfile[i], &s) == -1)
X		continue;
X	    if (s.st_mtime > PWLockTime) {
X		err1("%s has been modified", acctfile[i]);
X		uhoh = 1;
X	    }
X	}
X	if (uhoh) {
X	    err1("My %s lock has been violated.", PWDLOCK);
X	    err("");
X	    return yesno("Do the save anyway? ");
X	}
X	return 1;
X}
@//E*O*F src/save.c//
if test 8666 -ne "`wc -c <'src/save.c'`"; then
    echo shar: error transmitting "'src/save.c'" '(should have been 8666 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'src/save.h'" '(601 characters)'
if test -f 'src/save.h' ; then 
  echo shar: will not over-write existing file "'src/save.h'"
else
sed 's/^X//' >src/save.h <<'@//E*O*F src/save.h//'
X/**********************************************************************\
X* 								       *
X* 	save.h							       *
X* 								       *
X* Definitions for ModBits flags which determine which files have been  *
X* modified.							       *
X* 								       *
X\**********************************************************************/
X
X#define AC	0x01	/* accounts */
X#ifdef SENDMAIL
X#define AL	0x02	/* aliases or alias bindings */
X#endif
X#define CS	0x04	/* classes */
X#define GR	0x08	/* group */
X#define PW	0x10	/* passwd */
X#define RG	0x20	/* ranges */
X#define SG	0x40	/* sigs */
X#define VG	0x80	/* vigs */
@//E*O*F src/save.h//
if test 601 -ne "`wc -c <'src/save.h'`"; then
    echo shar: error transmitting "'src/save.h'" '(should have been 601 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'src/shell.c'" '(3616 characters)'
if test -f 'src/shell.c' ; then 
  echo shar: will not over-write existing file "'src/shell.c'"
else
sed 's/^X//' >src/shell.c <<'@//E*O*F src/shell.c//'
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/time.h>
X#include <sys/resource.h>
X#include <sys/ioctl.h>
X#include <sys/wait.h>
X#include <signal.h>
X#include <strings.h>
X#include "sysdep.h"
X#include "macros.h"
X#include "mem.h"
X
X#ifdef BSD4_3
Xuid_t	getuid();
Xgid_t	getgid();
X#endif
X
X#define NILRUSAGE	(struct rusage *) 0
X
Xchar	*getenv();
X
Xextern 	addr DEF_SHELL;
Xextern	int DevTty, kids;
X
Xint
Xshellescape(c, v)
Xint c;
Xaddr *v;
X
X{
X	int kidpid, mypgrp, newpgrp, pid, omask, exitcode = 0;
X	union wait status;
X
X	mypgrp = getpgrp(0);
X
X	c--; v++;
X	/*
X	 * Don't want to be interrupted while forking and leave zombies
X	 * and things floating around.  WHo knows what happens when the
X	 * parent is interrupted in the midst of a vfork() ?
X	 */
X	critical();
X	kidpid = vfork();
X	if (kidpid == -1) {
X		perr("vfork failed!");
X		return -1;
X	}
X	non_critical();
X
X	if (kidpid) {
X		/*
X		 * Do nothing here, because we are the parent; we should go
X		 * directly to the wait loop.
X		 */
X	}
X	else if (c == 0) {
X		char *shell = getenv("SHELL");
X		char *sname;
X
X		if (!shell)
X			shell = (char *)DEF_SHELL;
X		if (!kidpid) {
X			sname = rindex(shell, '/');
X			sname = (sname ? sname+1 : shell);
X			pid = getpid();
X			(void) ioctl(DevTty, TIOCSPGRP, (char *)&pid);
X			(void) setpgrp(0, pid);
X			(void) setuid(getuid());  /* better safe than... */
X			(void) setgid(getgid());  /* ... */
X			execl(shell, sname, 0);
X			perr(shell);
X			_exit(1);
X		}
X	}
X	else {
X		if (!kidpid) {
X			pid = getpid();
X			(void) ioctl(DevTty, TIOCSPGRP, (char *)&pid);
X			(void) setpgrp(0, pid);
X			(void) setuid(getuid());
X			(void) setgid(getgid());
X			execvp((char *)*v, (char **)v);
X			perr((char *)*v);
X			_exit(1);
X		}
X	}
X	for (;;) {
X		/*
X		 *	KLUDGE ALERT! BATTLE STATIONS...
X		 *
X		 * Here we temporarily block possible SIGALRM's that
X		 * might be generated when mcp wants to checkpoint itself.
X		 * This is due to a bug in wait3() (4.2 BSD).  If the signal
X		 * were processed while in wait3(), the wait3() would be
X		 * restarted >>without<< the WUNTRACED option, which would
X		 * cause a deadlock here if the child were to stop.
X		 *
X		 * SIGALRM will be released once the child
X		 * has terminated.
X		 */
X		omask = sigblock(mask(SIGALRM));
X
X		pid = wait3(&status, WUNTRACED, NILRUSAGE);
X		if (pid == 0)
X			continue;
X		else if (pid > 0 && pid != kidpid) {
X			/*
X			 * Apparently this isn't the child we just spawned
X			 * (could be an omnichown that terminated), so
X			 * we note its passing.
X			 */
X			kids--;
X			continue;
X		}
X		if (WIFSTOPPED(status)) {
X			/*
X			 * The child has stopped due to some signal,
X			 * so mcp stops itself with the same signal
X			 * to achieve transparency.
X			 */
X			(void) kill(getpid(), (int)status.w_stopsig);
X			/*
X			 * We've been continued, but the our parent
X			 * (the shell) has given us back the tty, so
X			 * we must pass it back to the child before
X			 * continuing it.
X			 */
X			(void) setpgrp(kidpid, kidpid);
X			(void) ioctl(DevTty, TIOCSPGRP, (char *)&kidpid);
X			/*
X			 * Now set the child in motion...
X			 */
X			(void) killpg(kidpid, SIGCONT);
X			/*
X			 * And keep waiting...
X			 */
X			continue;
X		}
X		break;
X	}
X
X	/*
X	 * Note if anything went amiss.
X	 */
X	if (status.w_termsig != 0)
X		exitcode = status.w_termsig;
X	if (status.w_retcode != 0)
X		exitcode = status.w_retcode;
X	/*
X	 * Child has exited, so now we can release any pending
X	 * SIGALRM.
X	 */
X	(void) sigsetmask(omask);
X
X	/*
X	 * Take command of the tty again.
X	 */
X	(void) ioctl(DevTty, TIOCGPGRP, (char *)&newpgrp);
X	(void) setpgrp(0, newpgrp);
X	(void) ioctl(DevTty, TIOCSPGRP, (char *)&mypgrp);
X	(void) setpgrp(0, mypgrp);
X
X	return exitcode;
X}
@//E*O*F src/shell.c//
if test 3616 -ne "`wc -c <'src/shell.c'`"; then
    echo shar: error transmitting "'src/shell.c'" '(should have been 3616 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'src/sig.c'" '(1855 characters)'
if test -f 'src/sig.c' ; then 
  echo shar: will not over-write existing file "'src/sig.c'"
else
sed 's/^X//' >src/sig.c <<'@//E*O*F src/sig.c//'
X#include <sys/types.h>
X#include <sys/file.h>
X#include <stdio.h>
X#include "sysdep.h"
X#include "mem.h"
X#include "lists.h"
X#include "sig.h"
X
Xextern	struct list SigList;
Xextern	int ssigcmp();
Xlong	lseek();
X
Xstatic char sdesc[DESCSIZE+1], sname[SHORT_BUF], ssize[SHORT_BUF];
Xstatic char sexp[SHORT_BUF];
Xstruct sig sg = { sname, 0, (time_t)0, sdesc };
X
Xint SG_FileDes = UNDEFINED;
X
Xsetsgent()
X
X{
X	if (SG_FileDes == UNDEFINED) {
X		SG_FileDes = open(SIGFILE, O_RDONLY);
X		if (SG_FileDes < 0) {
X			perr(SIGFILE);
X			goodbye(1);
X		}
X	}
X	lseek(SG_FileDes, (long) 0, L_SET)<0 &&
X		perr("setsgent: lseek failed?!");
X	return;
X}
X
Xendsgent()
X
X{
X	if (SG_FileDes == UNDEFINED)
X		return;
X	(void) close(SG_FileDes);
X	SG_FileDes = UNDEFINED;
X	return;
X}
X
Xstruct sig *
Xgetsgent()
X
X{
X	register int i;
X	char c;
X
X	if (SG_FileDes == UNDEFINED)
X		setsgent();
X#ifdef SENDMAIL
X	zerolist(&sg.sg_aliases);
X#endif
X	i = 0;
X	while (read(SG_FileDes, &c, 1) != 0) {
X		c &= 0177;
X		if (c == ' ')
X			break;
X		sname[i++] = c;
X	}
X	sname[i] = '\0';
X	if (i == 0)
X		return (struct sig *)0;
X	i = 0;
X	while (read(SG_FileDes, &c, 1) != 0) {
X		c &= 0177;
X		if (c == ' ')
X			break;
X		ssize[i++] = c;
X	}
X	ssize[i] = '\0';
X	if (i == 0)
X		return (struct sig *)0;
X	i = 0;
X	while (read(SG_FileDes, &c, 1) != 0) {
X		c &= 0177;
X		if (c == '\n')
X			break;
X		sexp[i++] = c;
X	}
X	sexp[i] = '\0';
X	if (i == 0)
X		return (struct sig *)0;
X	/* result of intermediate assignment used in read() to stifle lint */
X	sg.sg_dsize = i = atoi(ssize);
X	sg.sg_exptime = atoi(sexp);
X	if (read(SG_FileDes, sg.sg_desc, i) != sg.sg_dsize)
X		fatal1("%s: bad file format", SIGFILE);
X	sg.sg_desc[sg.sg_dsize] = '\0';
X	return(&sg);
X}
X
Xstruct sig *
Xgetsgnam(name)
Xchar *name;
X
X{
X	int indx, found;
X
X	indx = search_list(&SigList, name, ssigcmp, &found);
X	if (found)
X		return (struct sig *) SigList.l_list[indx];
X	return (struct sig *) 0;
X}
@//E*O*F src/sig.c//
if test 1855 -ne "`wc -c <'src/sig.c'`"; then
    echo shar: error transmitting "'src/sig.c'" '(should have been 1855 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'src/sig.h'" '(298 characters)'
if test -f 'src/sig.h' ; then 
  echo shar: will not over-write existing file "'src/sig.h'"
else
sed 's/^X//' >src/sig.h <<'@//E*O*F src/sig.h//'
Xstruct sig {
X	char		*sg_name;
X	off_t		sg_dsize;	/* description size (bytes) */
X	time_t		sg_exptime;	/* expiration date (0=never) */
X	char		*sg_desc;	/* pointer to description */
X#ifdef SENDMAIL
X	struct list	sg_aliases; 	/* aliases sig is bound to */
X#endif
X};
X
Xstruct sig *getsgent(), *getsgnam();
@//E*O*F src/sig.h//
if test 298 -ne "`wc -c <'src/sig.h'`"; then
    echo shar: error transmitting "'src/sig.h'" '(should have been 298 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'src/signals.c'" '(1569 characters)'
if test -f 'src/signals.c' ; then 
  echo shar: will not over-write existing file "'src/signals.c'"
else
sed 's/^X//' >src/signals.c <<'@//E*O*F src/signals.c//'
X#include <stdio.h>
X#include <setjmp.h>
X#include <signal.h>
X#include "sysdep.h"
X#include "macros.h"
X
Xextern	jmp_buf interrupt;
Xstatic	int critlevel, handling_yet;
X#if CKPTIME > 0
Xextern	int wakeup();
X#endif
X
X#ifdef sun
X#define	sighandler	(void (*)())
X#else
X#define	sighandler	(int (*)())
X#endif
X
Xint_hand()
X
X{
X	(void) fflush(stdout);
X	longjmp(interrupt, SIGINT);
X}
X
Xhup_hand()	{ panic("Hangup received"); }
Xterm_hand()	{ panic("Terminate signal received"); }
Xquit_hand()	{ panic("Quit signal received"); }
X
Xsetsignals()
X
X{
X	extern int root;
X
X	(void) signal(SIGQUIT, sighandler quit_hand);
X	(void) signal(SIGINT, sighandler int_hand);
X	if (root) {
X	    (void) signal(SIGHUP, sighandler hup_hand);
X	    (void) signal(SIGTERM, sighandler term_hand);
X#if CKPTIME > 0
X	    (void) signal(SIGALRM, sighandler wakeup);
X#endif
X	}
X	(void) sigsetmask(0);
X	handling_yet = 1;
X}
X
Xtstp()
X
X{
X	(void) kill(getpid(), SIGTSTP);
X	return;
X}
X
X/*
X * Keep user from interrupting the program during a critical section
X * of code.  Used when updating interdependent data structures to insure
X * consistency. Also used by the memory management routines to make sure
X * allocations and free's are atomic.
X */
Xcritical()
X
X{
X	if (!handling_yet) return;
X	if (!critlevel++) {
X		(void) signal(SIGINT, sighandler SIG_IGN);
X		(void) sigsetmask(mask(SIGHUP)|mask(SIGTERM)|
X					mask(SIGQUIT)|mask(SIGALRM));
X	}
X	return;
X}
X
X/*
X * Critical's other half
X */
Xnon_critical()
X
X{
X	if (!handling_yet) return;
X	if (!--critlevel) {
X		(void) signal(SIGINT, sighandler int_hand);
X		(void) sigsetmask(0);
X	}
X	return;
X}
@//E*O*F src/signals.c//
if test 1569 -ne "`wc -c <'src/signals.c'`"; then
    echo shar: error transmitting "'src/signals.c'" '(should have been 1569 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'src/sort.c'" '(1838 characters)'
if test -f 'src/sort.c' ; then 
  echo shar: will not over-write existing file "'src/sort.c'"
else
sed 's/^X//' >src/sort.c <<'@//E*O*F src/sort.c//'
X#include <sys/types.h>
X#include <lastlog.h>
X#include "sysdep.h"
X#include "mem.h"
X#include "lists.h"
X#include "account.h"
X#ifdef SENDMAIL
X#include "alias.h"
X#endif
X#include "class.h"
X#include "command.h"
X#include "sig.h"
X#include "range.h"
X#include "groupmap.h"
X
X#ifdef SENDMAIL
Xint aliascmp(a, aa)
Xstruct alias **a, **aa;
X
X{
X	return strcmp((char *)((*a)->al_name), (char *)((*aa)->al_name));
X}
X
Xint saliascmp(s, a)
Xchar *s;
Xstruct alias *a;
X
X{
X	return strcmp(s, a->al_name);
X}
X#endif
X
Xint acctcmp(a, aa)
Xstruct account **a, **aa;
X
X{
X	register int cmpval;
X
X	cmpval = (*a)->ac_uid - (*aa)->ac_uid;
X	if (cmpval != 0) return cmpval;
X	return strcmp((char *)((*a)->ac_name), (char *)((*aa)->ac_name));
X}
X
Xint iacctcmp(n, a)
Xint *n;
Xstruct account *a;
X
X{
X	return *n - a->ac_uid;
X}
X
Xint commcmp(c, cc)
Xstruct command *c, *cc;
X
X{
X	return strcmp(c->c_name, cc->c_name);
X}
X
Xint scommcmp(s, c)
Xchar *s;
Xstruct command *c;
X
X{
X	return strcmp(s, c->c_name);
X}
X
X
Xint classcmp(c, cc)
Xstruct class **c, **cc;
X
X{
X	return strcmp((*c)->cs_name, (*cc)->cs_name);
X}
X
Xint sclasscmp(s, c)
Xchar *s;
Xstruct class *c;
X
X{
X	return strcmp(s, c->cs_name);
X}
X
Xint gmapcmp(g, gg)
Xstruct groupmap **g, **gg;
X
X{
X	return (*g)->gm_gid - (*gg)->gm_gid;
X}
X
Xint igmapcmp(n, g)
Xint *n;
Xstruct groupmap *g;
X
X{
X	return *n - g->gm_gid;
X}
X
Xint rangecmp(r, rr)
Xstruct range **r, **rr;
X
X{
X	return strcmp((*r)->rg_name, (*rr)->rg_name);
X}
X
Xint srangecmp(s, r)
Xchar *s;
Xstruct range *r;
X
X{
X	return strcmp(s, r->rg_name);
X}
X
Xint sigcmp(s, ss)
Xstruct sig **s, **ss;
X
X{
X	return strcmp((*s)->sg_name, (*ss)->sg_name);
X}
X
Xint ssigcmp(s, sg)
Xchar *s;
Xstruct sig *sg;
X
X{
X	return strcmp(s, sg->sg_name);
X}
X
Xpstrcmp(p, pp)
Xchar **p, **pp;
X
X{
X	return strcmp(*p, *pp);
X}
X
Xsort_list(l, compfunc)
Xstruct list *l;
Xint (*compfunc)();
X
X{
X	qsort((char *)l->l_list, l->l_count, sizeof (addr), compfunc);
X}
@//E*O*F src/sort.c//
if test 1838 -ne "`wc -c <'src/sort.c'`"; then
    echo shar: error transmitting "'src/sort.c'" '(should have been 1838 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'src/sort.h'" '(243 characters)'
if test -f 'src/sort.h' ; then 
  echo shar: will not over-write existing file "'src/sort.h'"
else
sed 's/^X//' >src/sort.h <<'@//E*O*F src/sort.h//'
Xextern	int acctcmp(), iacctcmp(), commcmp(), scommcmp(), classcmp();
Xextern	int sclasscmp(), gmapcmp(), igmapcmp(), rangecmp(), srangecmp();
Xextern	int sigcmp(), ssigcmp(), pstrcmp();
X#ifdef SENDMAIL
Xextern	int aliascmp(), saliascmp();
X#endif
@//E*O*F src/sort.h//
if test 243 -ne "`wc -c <'src/sort.h'`"; then
    echo shar: error transmitting "'src/sort.h'" '(should have been 243 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'src/sysdep.h'" '(10026 characters)'
if test -f 'src/sysdep.h' ; then 
  echo shar: will not over-write existing file "'src/sysdep.h'"
else
sed 's/^X//' >src/sysdep.h <<'@//E*O*F src/sysdep.h//'
X/****************************************************************************\
X* 									     *
X* 	sysdep.h							     *
X* 									     *
X* This file contains all the system dependent #define's.  In certain places  *
X* you may configure mcp to your liking by commenting out certain	     *
X* definitions.								     *
X* 									     *
X\****************************************************************************/
X
X/*
X * Define BSD4_3 if this is a 4.3 BSD system.  Currently all this entails
X * is that mcp must use mkpasswd(8) to create the .dir and .pag passwd
X * database files.
X */
X#define BSD4_3
X
X/*
X * If this is 4.3 BSD system then DBMPASSWORD must point to the mkpaswd(8)
X * command.
X */
X#ifdef BSD4_3
X#define DBMPASSWORD	"/etc/mkpasswd"
X#endif
X
X/*
X * DEF_VISUAL and DEF_EDITOR should define two different editors.
X * For dumb terminals 
X *     If getenv("EDITOR") returns NULL, DEF_EDITOR will be used when editing
X *     a class or sig description.
X * For smart terminals
X *     If getenv("VISUAL") returns NULL, DEF_VISUAL will be used when
X *     a class or sig description.
X * Smart terminals are anything but "dumb", "network", and "dialup"
X */
X#define	DEF_VISUAL	"/usr/new/jove"
X#define	DEF_EDITOR	"/bin/ed"
X
X/*
X * If for some ghastly reason TERM isn't defined, mcp will assume this.
X */
X#define	DEF_TERM	"dumb"
X
X/*
X * Disabling a user means changing his shell to be whatever DISABLED_SH
X * is defined to be.  An example disabled shell is in ../misc/sorry
X */
X#define	DISABLED_SH	"/usr/misc/sorry"
X
X/*
X * Freezing a user means changing his shell to be whatever FREEZE_SH
X * is defined to be.  An example shell for a frozen user is in
X * ../misc/freeze
X */
X#define	FREEZE_SH	"/usr/misc/freeze"
X
X/*
X * If defined HELPDIR should tell where the help files used in
X * describe-command wiil be installed.  If HELPDIR is not defined,
X * the help facilities will not be compiled in.
X */
X#define HELPDIR		"/usr/mcphelp"
X
X/*
X * DEF_PAGER is used to display the help pages from describe-command
X * if the environmental variable PAGER is not defined.
X * DEF_PAGER should understand nroff underlining sequences.
X * No need to define this if HELPDIR isn't defined.
X */
X#ifdef HELPDIR
X#define	DEF_PAGER	"/usr/ucb/more"
X#endif
X
X/*
X * If DOFILES is defined mcp will create home directories for new users,
X * remove home directories, mail, and secretmail of deleted users.  Also
X * if a user's uid is changed all his file ownerships will reflect this.
X * Otherwise it will be up to the system administrator to do these tasks.
X * Mcp will remind the SA of his responsibilities if DOFILES is undefined.
X */
X#define DOFILES
X
X/*
X * Mcp assumes that all user directories are under USERDIR as a default.
X * If this is a Sun system, code will be compiled in so that at runtime
X * mcp will check to see if USERDIR is mounted on a directory on another
X * host (a fileserver).  If so mcp will by necessity use
X * remote commands to manipulate the home directories of users.
X */
X#define	USERDIR		"/usr1"
X
X/*
X * If SENDMAIL is defined mcp will update the aliases data file and use
X * "newaliases" command as necessary to build the actual aliases database.
X * This includes the usual removal of users form aliases as accounts
X * are deleted, etc.
X *
X */
X#define SENDMAIL
X
X/*
X * This is the standard mail spool directory.  When users are deleted,
X * so is their mail if DOFILES is defined.
X */
X#define	MAILSPOOL	"/usr/spool/mail"
X/*
X * This is the standard secretmail spool.   I doubt if anyone actually
X * uses xsend and xget anymore but you never know...
X */
X#define	SMAILSPOOL	"/usr/spool/secretmail"
X
X/*
X * Directory for mcp to create it's temporary edit files in.  When adding
X * or updating class and sig descriptions mcp will put the description in
X * a temp file in this directory and invoke and either DEF_EDITOR or
X * DEF_VISUAL on it.
X */
X#define	TMPDIR		"/tmp"
X
X/*
X * New passwords created in add-user will be encrypted with this salt.
X * This is a convenience so that accounts with unchanged passwords can be
X * easily spotted by scanning the password file for this salt.
X */
X#define	CRYPT_SALT	"//"
X
X/*
X * DEF_GROUP will be the group offered as a default in add-user, so this
X * generally should be defined to be the name of the group with the most
X * members.
X */
X#define DEF_GROUP	"student"
X
X/*
X * DESCSIZE determines the maximum size that class and sig descriptions
X * will be allowed to be (in characters).  512 has proven to be more than
X * enough here.  Usually we can barely claw together 100.
X */
X#define DESCSIZE	512
X
X/*
X * If defined mcp will checkpoint changes in the accounting files each
X * CKPTIME minutes.  If not defined no automatic checkpointing will
X * be done although checkpoint files will still be written if mcp encounters
X * an unexpected signal.
X */
X#define	CKPTIME		5
X
X/****************************************************************************\
X* 									     *
X* These define the locations of the accounting files.  Each accounting file  *
X* (account, alias, class, group, passwd, range, sig, vig) also has a	     *
X* #define for a backup file, a temporary file for storing intermediate	     *
X* changes to the Accounts file, and a checkpoint file.  Mcp checkpoints any  *
X* changes it has made in memory to the checkpoint file once every CKPTIME    *
X* seconds.								     *
X* 									     *
X* Each temporary file (e.g. ACTMP) MUST be in the same filesystem as its     *
X* respective accounting file because the save routines do a rename(2) call   *
X* from the temp file to the accounting file.				     *
X* 									     *
X\****************************************************************************/
X
X/*
X * the account file
X */
X#define	ACFILE		"/usr/adm/accts/Accounts"
X#define	ACBAK		"/usr/adm/accts/Accounts.bak"
X#define	ACTMP		"/usr/adm/accts/Accounts.tmp"
X#define	ACCKP		"/usr/adm/accts/Accounts.mcp"
X
X/*
X * the class file
X */
X#define	CSFILE		"/usr/adm/accts/Classes"
X#define	CSBAK		"/usr/adm/accts/Classes.bak"
X#define	CSTMP		"/usr/adm/accts/Classes.tmp"
X#define	CSCKP		"/usr/adm/accts/Classes.mcp"
X
X/*
X * the group file
X *
X * Note that since mcp uses getgrent(3) to read the
X * group file, changing the define here does no good in that respect.
X * However you can make mcp write out its version of the group file
X * anywhere you like by changing these, if you suspect mcp of mangling.
X */
X#define	GRPFILE		"/etc/group"
X#define	GRPBAK		"/etc/group.bak"
X#define	GRPTMP		"/etc/group.tmp"
X#define	GRPCKP		"/etc/group.mcp"
X
X/*
X * the passwd file
X *
X * Note that since mcp uses getpwent(3) to read the
X * passwd file, changing the define here does no good in that respect.
X * However you can make mcp write out it's version of the passwd file
X * anywhere you like by changing these, if you suspect mcp of mangling.
X */
X#define	PWDFILE		"/etc/passwd"
X#define	PWDBAK		"/etc/passwd.bak"
X#define	PWDTMP		"/etc/passwd.tmp"
X#define	PWDCKP		"/etc/passwd.mcp"
X#define	PWDLOCK		"/etc/ptmp"
X
X/*
X * the range file
X */
X#define	RANGEFILE	"/usr/adm/accts/Ranges"
X#define	RANGEBAK	"/usr/adm/accts/Ranges.bak"
X#define	RANGETMP	"/usr/adm/accts/Ranges.tmp"
X#define	RANGECKP	"/usr/adm/accts/Ranges.mcp"
X
X/*
X * the sig file
X */
X#define	SIGFILE		"/usr/adm/accts/Sigs"
X#define	SIGBAK		"/usr/adm/accts/Sigs.bak"
X#define	SIGTMP		"/usr/adm/accts/Sigs.tmp"
X#define	SIGCKP		"/usr/adm/accts/Sigs.mcp"
X
X/*
X * the vig file
X */
X#define	VIGFILE		"/usr/adm/accts/Vigs"
X#define	VIGBAK		"/usr/adm/accts/Vigs.bak"
X#define	VIGTMP		"/usr/adm/accts/Vigs.tmp"
X#define	VIGCKP		"/usr/adm/accts/Vigs.mcp"
X
X/*
X * These alias file definitions will be #ifdef'ed out if you have not
X * #define'd SENDMAIL above.  If you do plan to use mcp to manipulate the
X * sendmail aliases, check these definitions.  ALIASFILE *must* be the aliases
X * file that sendmail uses.
X */
X#ifdef SENDMAIL
X# define ALIASFILE	"/usr/lib/aliases"
X# define ALIASBAK	"/usr/lib/aliases.bak"
X# define ALIASTMP	"/usr/lib/aliases.tmp"
X# define ALIASCKP	"/usr/lib/aliases.mcp"
X# define ALBIND		"/usr/adm/accts/AliasBindings"
X# define ALBINDBAK	"/usr/adm/accts/AliasBindings.bak"
X# define ALBINDTMP	"/usr/adm/accts/AliasBindings.tmp"
X# define ALBINDCKP	"/usr/adm/accts/AliasBindings.mcp"
X/* path to the newaliases command to update the aliases database */
X# define NEWALIASES	"/usr/ucb/newaliases"
X#endif
X
X/**************************\
X* 			   *
X* Other file definitions.  *
X* 			   *
X\**************************/
X
X/*
X * Standard location for the lastlog, used by login(1) and finger(1).
X */
X#define	LASTLOG		"/usr/adm/lastlog"
X
X
X/*
X * Mcp gets the list of available shells from this file and builds a
X * completion list from it.  The file format is one shell per line
X * with the first line containing the shell that will be the default
X * in add-user.  For 4.3 BSD systems it is convenient to use /etc/shells,
X * since the passwd(1) command already uses this list.  Mcp will ignore the
X * comments in this file, for compatibility with /etc/shells on 4.3 BSD
X * systems.
X */
X#define	SHELLFILE	"/etc/shells"
X
X/****************************************************************************\
X* 									     *
X* These definitions are needed only if DOFILES is #define'd above.  For Sun  *
X* systems, the pathnames for the executables should be correct for the	     *
X* fileserver and all the clients.  The paths given will work if the	     *
X* commands have not been moved from the locations in which they were	     *
X* distributed.								     *
X* 									     *
X* Mcp uses these chiefly as remote commands on Sun systems.  FIND is used    *
X* to change the onwership of user files in all cases involving more than     *
X* one file.  Otherwise mcp will use the system primitives chmod(),	     *
X* chown(), etc., instead of these commands.				     *
X* 									     *
X\****************************************************************************/
X
X#ifdef DOFILES
X#define MV	"/bin/mv"
X#define RM	"/bin/rm"
X#define MKDIR	"/bin/mkdir"
X#define CHGRP	"/bin/chgrp"
X#define CHOWN	"/etc/chown"
X#define CHMOD	"/bin/chmod"
X#define FIND	"/usr/bin/find"
X#define NICE	"/bin/nice"
X#endif
@//E*O*F src/sysdep.h//
if test 10026 -ne "`wc -c <'src/sysdep.h'`"; then
    echo shar: error transmitting "'src/sysdep.h'" '(should have been 10026 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'src/tty.c'" '(431 characters)'
if test -f 'src/tty.c' ; then 
  echo shar: will not over-write existing file "'src/tty.c'"
else
sed 's/^X//' >src/tty.c <<'@//E*O*F src/tty.c//'
X#include <sys/ioctl.h>
X
Xextern int DevTty;
X
Xcbreak()
X
X{
X	struct sgttyb sg;
X
X	(void) ioctl(DevTty, TIOCGETP, (char *)&sg);
X	sg.sg_flags |= CBREAK;
X	sg.sg_flags &= ~(CRMOD|ECHO|RAW);
X	(void) ioctl(DevTty, TIOCSETP, (char *)&sg);
X	return;
X}
X
Xnocbreak()
X
X{
X	struct sgttyb sg;
X
X	(void) ioctl(DevTty, TIOCGETP, (char *)&sg);
X	sg.sg_flags &= ~CBREAK;
X	sg.sg_flags |= (CRMOD|ECHO);
X	(void) ioctl(DevTty, TIOCSETP, (char *)&sg);
X	return;
X}
@//E*O*F src/tty.c//
if test 431 -ne "`wc -c <'src/tty.c'`"; then
    echo shar: error transmitting "'src/tty.c'" '(should have been 431 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'src/version.c'" '(577 characters)'
if test -f 'src/version.c' ; then 
  echo shar: will not over-write existing file "'src/version.c'"
else
sed 's/^X//' >src/version.c <<'@//E*O*F src/version.c//'
Xstatic	struct notice {
X	char	*n_version;
X	char	*n_copyright;
X};
Xstatic	struct notice Note = {
X"mcp version 1.0 (#2) ",
X"(c) 1986 by Kyle E. Jones\n\n\
XAll sources and documentation of this mcp distribution are\n\
Xincluded in this copyright, but permission is granted to\n\
Xcopy and redistribute any part of this distribution, provided\n\
Xthat this notice is a conspicuous part of the redistribution,\n\
Xand that no part of this distribution is sold.\n\n\
XThis software is distributed 'as is', without warranties of any kind.\n\
X"
X};
X	
X
XShowVersion()
X
X{
X	err(Note.n_version);
X}
@//E*O*F src/version.c//
if test 577 -ne "`wc -c <'src/version.c'`"; then
    echo shar: error transmitting "'src/version.c'" '(should have been 577 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'src/yesno.c'" '(1858 characters)'
if test -f 'src/yesno.c' ; then 
  echo shar: will not over-write existing file "'src/yesno.c'"
else
sed 's/^X//' >src/yesno.c <<'@//E*O*F src/yesno.c//'
X#include <stdio.h>
X#include "sysdep.h"
X#include "mem.h"
X#include "lists.h"
X#include "gpa.h"
X
Xstatic	char *yn[4] = { "yes", "no", "YES!!!", "NO!!!" };
Xstatic	char *y[1] = { "yes" };
Xstatic	char *n[1] = { "no" };
X
Xstruct	list YesNo = {	4, 4, (addr *)yn };
X
Xstatic	struct list Yes = { 1, 1, (addr *)y };
Xstatic	struct list No = { 1, 1, (addr *)n };
X
X/*
X * Query for a yes or no answer.  Allows carriage return to default
X * yes.  Returns 1 if answer is yes, 0 for no.
X */
Xyes(prompt)
Xchar *prompt;
X
X{
X	addr *argv;
X	int argc;
X	char **cpp;
X
X	argv = get_gpa(2);
X	cpp = (char **) argv;
X	do {
X		GetLine(prompt, 1, &argc, argv, &Yes);
X		if (argc == 0 || **cpp == 'y' || **cpp == 'Y') {
X			pop_gpa(2);
X			return(1);
X		}
X		else if (**cpp == 'n' || **cpp == 'N') {
X			pop_gpa(2);
X			return(0);
X		}
X	} while (clear_gpa(argv, 2));
X	/* NOTREACHED */
X}
X
X
X/*
X * Query for a yes or no answer.  Allows carriage return to default
X * no.  Returns 1 if answer is no, 0 for yes.
X */
Xno(prompt)
Xchar *prompt;
X
X{
X	addr *argv;
X	int argc;
X	char **cpp;
X
X	argv = get_gpa(2);
X	cpp = (char **) argv;
X	do {
X		GetLine(prompt, 1, &argc, argv, &No);
X		if (argc == 0 || **cpp == 'n' || **cpp == 'N') {
X			pop_gpa(2);
X			return(1);
X		}
X		else if (**cpp == 'y' || **cpp == 'Y') {
X			pop_gpa(2);
X			return(0);
X		}
X	} while (clear_gpa(argv, 2));
X	/* NOTREACHED */
X}
X
X
X/*
X * Query for a yes or no answer.  Disallows carriage return default;
X * answer must be specified.  Returns 1 if answer is yes, 0 for no.
X */
Xyesno(prompt)
Xchar *prompt;
X
X{
X	addr *argv;
X	int argc;
X	char **cpp;
X
X	argv = get_gpa(2);
X	cpp = (char **) argv;
X	do {
X		GetLine(prompt, 1, &argc, argv, &YesNo);
X		if (argc == 0) continue;
X		if (**cpp == 'y' || **cpp == 'Y') {
X			pop_gpa(2);
X			return(1);
X		}
X		else if (**cpp == 'n' || **cpp == 'N') {
X			pop_gpa(2);
X			return(0);
X		}
X	} while (clear_gpa(argv, 2));
X	/* NOTREACHED */
X}
@//E*O*F src/yesno.c//
if test 1858 -ne "`wc -c <'src/yesno.c'`"; then
    echo shar: error transmitting "'src/yesno.c'" '(should have been 1858 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'src/update.c'" '(22502 characters)'
if test -f 'src/update.c' ; then 
  echo shar: will not over-write existing file "'src/update.c'"
else
sed 's/^X//' >src/update.c <<'@//E*O*F src/update.c//'
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <strings.h>
X#include <ctype.h>
X#include <lastlog.h>
X#include "sysdep.h"
X#include "macros.h"
X#include "mem.h"
X#include "gpa.h"
X#include "lists.h"
X#include "account.h"
X#ifdef SENDMAIL
X#include "alias.h"
X#endif
X#include "class.h"
X#include "groupmap.h"
X#include "job.h"
X#include "range.h"
X#include "sig.h"
X#include "sort.h"
X#include "save.h"
X
X#define	DAY	(4*21600)
X
X#ifdef SENDMAIL
Xextern	struct list AliasList, Aliases;
X#endif
Xextern	struct list AccountList, Users, ClassList, Classes, GroupMapList;
Xextern	struct list Groups, RangeList, Ranges, Sigs, SigList, Vigs, Shells;
Xextern	struct list Null_List;
Xextern	int ModBits;
Xextern	addr makeusername(), DEF_SHELL;
Xextern	char *crypt(), *mktemp(), *sprintf(), *when(), *rsalt(), *makepass();
Xextern	time_t choosedate();
X
Xstatic	char *XXXXXX = "/mcpXXXXXX";
Xstatic	char desc[DESCSIZE+1];
X
X/* these are defined in add.c */
Xextern	struct	list idlist;
Xextern	struct	list rnlist;
Xextern	struct	list pwlist;
Xextern	struct	list mdlist;
X
X#ifdef SENDMAIL
X/*
X * Update an alias
X */
Xupdalias(c, v)
Xint c;
Xchar **v;
X
X{
X	struct alias *al, *a;
X	struct account *ac;
X	struct class *cs;
X	struct sig *sg;
X	struct groupmap *gm;
X	register int i;
X	int cc;
X	addr *namev;
X	char prompt[MEDIUM_BUF];
X
X	if (c > 2) {
X	    err1("%s: too many arguments", (char *)v[0]);
X	    return;
X	}
X	if (c < 2) {
X	    err1("usage: %s <alias>", (char *)v[0]);
X	    return;
X	}
X	al = getalnam((char *)v[1]);
X	if (!al) {
X	    err1("%s: no such alias", (char *)v[1]);
X	    return;
X	}
X
X	namev = get_gpa(2);
X	(void) sprintf(prompt, "Name [%s]: ", al->al_name);
X	GetLine(prompt, 1, &cc, namev, &Null_List);
X	if (cc == 0 || eq(*namev, al->al_name)) {
X	    err("no change");
X	    return;
X	}
X	if (aliasexists((char *)*namev)) {
X	    err1("%s: alias exists", (char *)*namev);
X	    return;
X	}
X
X	critical();
X
X	/*
X	 * If this alias name appears in any of the other alias lists
X	 * it must be changed there alias well.
X	 */
X	for (i=0; i < AliasList.l_count; i++) {
X	    a = (struct alias *) AliasList.l_list[i];
X	    (void) strlistchg(&a->al_addresses, al->al_name, (char *)*namev);
X	}
X	for (i=0; i < AccountList.l_count; i++) {
X	    ac = (struct account *) AccountList.l_list[i];
X	    if (strlistchg(&ac->ac_aliases, al->al_name, (char *)*namev))
X		ModBits |= AC;
X	}
X	for (i=0; i < GroupMapList.l_count; i++) {
X	    gm = (struct groupmap *) GroupMapList.l_list[i];
X	    (void) strlistchg(&gm->gm_aliases, al->al_name, (char *)*namev);
X	}
X	for (i=0; i < ClassList.l_count; i++) {
X	    cs = (struct class *) ClassList.l_list[i];
X	    (void) strlistchg(&cs->cs_aliases, al->al_name, (char *)*namev);
X	}
X	for (i=0; i < SigList.l_count; i++) {
X	    sg = (struct sig *) SigList.l_list[i];
X	    (void) strlistchg(&sg->sg_aliases, al->al_name, (char *)*namev);
X	}
X
X	(void) strlistchg(&Aliases, al->al_name, (char *)*namev);
X	FREEMEM(al->al_name);
X	savestr(&al->al_name, (char *)*namev);
X	sort_list(&AliasList, aliascmp);
X	ModBits |= AL;
X	puts("updated");
X	non_critical();
X
X	return;
X}
X#endif
X
X/*
X * Update a class
X */
Xupdclass(c, v)
Xint c;
Xaddr *v;
X
X{
X	struct class cl, *cs;
X	struct account *ac;
X#ifdef SENDMAIL
X	struct alias *al;
X#endif
X	struct stat statbuf;
X	addr *namev;
X	char tempf[MEDIUM_BUF], errmsg[LONG_BUF], prompt[LONG_BUF];
X	FILE *f, *fopen();
X	time_t now;
X	int i, cc, changed = 0, ch;
X
X	if ( c > 2 ) {
X		err1("%s: too many arguments", (char *)v[0]);
X		return;
X	}
X	if ( c != 2 ) {
X		err1("usage: %s <class>", (char *)v[0]);
X		return;
X	}
X	cs = getcsnam((char *)v[1]);
X	if (!cs) {
X		err1("%s: no such class", (char *)v[1]);
X		return;
X	}
X	bcopy(&cl, cs, sizeof (struct class));
X
X	namev = get_gpa(2);
X
X	(void) sprintf(prompt, "Name [%s]: ",  cl.cs_name);
X	GetLine(prompt, 1, &cc, namev, &Null_List);
X	if (cc) {
X		if (eq(*namev, v[1]))
X			; /* no change */
X		else if (classexists((char *)*namev)) {
X			err("that name is taken");
X			return;
X		}
X		else
X			changed = 1;
X	}
X	(void) printf("Class set to end %s\n", when(cl.cs_exptime));
X	if (no("Do you wish to change it? [no] ") == 0) {
X	    if (!cl.cs_exptime || yesno("Should the class expire? ")) {
X		err("Set the expiration date.");
X		cl.cs_exptime = choosedate(cl.cs_exptime);
X		(void) printf("Class set to end %s\n", when(cl.cs_exptime));
X	    }
X	    else
X		cl.cs_exptime = 0;
X	    if (cl.cs_exptime != cs->cs_exptime)
X		changed = 1;
X	}
X	i = no("Edit description? [no] ");
X
X	critical();
X	if (i)
X		goto finish;
X	(void) strcpy(tempf, TMPDIR);
X	(void) strcat(tempf, XXXXXX);
X	(void) mktemp(tempf);
X	f = fopen(tempf, "w");
X	if (f == NULL) {
X		err1("%s: cannot open (write)", tempf);
X		non_critical();
X		return;
X	}
X	fputs(cl.cs_desc, f);
X	(void) fclose(f);
X	(void) stat(tempf, &statbuf);
X	now = statbuf.st_mtime;
X	for (;;) {
X		edit(tempf);
X		if (stat(tempf, &statbuf) == -1) {
X			perr(tempf);
X			(void) unlink(tempf);
X			non_critical();
X			return;
X		}
X		if (statbuf.st_size > DESCSIZE) {
X			(void) sprintf(errmsg,
X				"description is %d characters too long",
X				DESCSIZE - statbuf.st_size);
X			err(errmsg);
X			continue;
X		}
X		break;
X	}
X	if (statbuf.st_mtime == now)
X		goto finish;
X	changed = 1;
X	f = fopen(tempf, "r");
X	if (f == NULL) {
X		err1("%s: cannot open (read)", tempf);
X		non_critical();
X		return;
X	}
X	FREEMEM(cl.cs_desc);
X	i = 0;
X	while ((ch = getc(f)) != EOF)
X		desc[i++] = ch;
X	desc[i] = '\0';
X	cl.cs_dsize = i;
X	savestr(&cl.cs_desc, desc);
X	(void) fclose(f);
X	(void) unlink(tempf);
X
Xfinish:
X	if (*namev != NIL && !eq(*namev, v[1])) {
X		FREEMEM(cl.cs_name);
X		savestr(&cl.cs_name, (char *)*namev);
X		for (i=0; i < AccountList.l_count; i++) {
X		    ac = (struct account *)AccountList.l_list[i];
X		    if (strlistchg(&ac->ac_classes, (char *)v[1], cl.cs_name))
X			ModBits |= AC;
X		}
X#ifdef SENDMAIL
X		for (i=0; i < AliasList.l_count; i++) {
X		    al = (struct alias *) AliasList.l_list[i];
X		    if (strlistchg(&al->al_classes, (char *)v[1], cl.cs_name))
X			ModBits |= AL;
X		}
X#endif
X		(void) strlistchg(&Classes, (char *)v[1], cl.cs_name);
X	}
X	if (changed) {
X		bcopy(cs, &cl, sizeof (struct class));
X		sort_list(&ClassList, classcmp);
X		ModBits |= CS;
X		puts("updated");
X	}
X	else
X		err("no change");
X	non_critical();
X
X	return;
X}
X
X/*
X * Update a sig
X */
Xupdsig(c, v)
Xint c;
Xaddr *v;
X
X{
X	struct sig s, *sg;
X#ifdef SENDMAIL
X	struct alias *al;
X#endif
X	struct account *ac;
X	struct stat statbuf;
X	addr *namev;
X	char tempf[MEDIUM_BUF], errmsg[LONG_BUF], prompt[LONG_BUF];
X	FILE *f, *fopen();
X	time_t now;
X	int i, cc, changed = 0, ch;
X
X	if ( c > 2 ) {
X		err1("%s: too many arguments", (char *)v[0]);
X		return;
X	}
X	if ( c != 2 ) {
X		err1("usage: %s <sig>", (char *)v[0]);
X		return;
X	}
X	sg = getsgnam((char *)v[1]);
X	if (!sg) {
X		err1("%s: no such sig", (char *)v[1]);
X		return;
X	}
X	bcopy(&s, sg, sizeof (struct sig));
X
X	namev = get_gpa(2);
X
X	(void) sprintf(prompt, "Name [%s]: ",  s.sg_name);
X	GetLine(prompt, 1, &cc, namev, &Null_List);
X	if (cc) {
X		if (eq(*namev, v[1]))
X			; /* no change */
X		else if (sigexists((char *)*namev)) {
X			err("that name is taken");
X			return;
X		}
X		else
X			changed = 1;
X	}
X	(void) printf("Sig set to end %s\n", when(s.sg_exptime));
X	if (no("Do you wish to change it? [no] ") == 0) {
X	    if (!s.sg_exptime || yesno("Should the sig expire? ")) {
X		err("Set the expiration date.");
X		s.sg_exptime = choosedate(s.sg_exptime);
X		(void) printf("Project set to end %s\n", when(s.sg_exptime));
X	    }
X	    else
X		s.sg_exptime = 0;
X	    if (s.sg_exptime != sg->sg_exptime)
X		changed = 1;
X	}
X	i = no("Edit description? [no] ");
X	
X	critical();
X	if (i)
X		goto finish;
X	(void) strcpy(tempf, TMPDIR);
X	(void) strcat(tempf, XXXXXX);
X	(void) mktemp(tempf);
X	f = fopen(tempf, "w");
X	if (f == NULL) {
X		err1("%s: cannot open (write)", tempf);
X		non_critical();
X		return;
X	}
X	fputs(s.sg_desc, f);
X	(void) fclose(f);
X	(void) stat(tempf, &statbuf);
X	now = statbuf.st_mtime;
X	for (;;) {
X		edit(tempf);
X		if (stat(tempf, &statbuf) == -1) {
X			perr(tempf);
X			(void) unlink(tempf);
X			non_critical();
X			return;
X		}
X		if (statbuf.st_size > DESCSIZE) {
X			(void) sprintf(errmsg,
X				"description is %d characters too long",
X				DESCSIZE - statbuf.st_size);
X			err(errmsg);
X			continue;
X		}
X		break;
X	}
X	if (statbuf.st_mtime == now)
X		goto finish;
X	changed = 1;
X	f = fopen(tempf, "r");
X	if (f == NULL) {
X		err1("%s: cannot open (read)", tempf);
X		non_critical();
X		return;
X	}
X	FREEMEM(s.sg_desc);
X	i = 0;
X	while ((ch = getc(f)) != EOF)
X		desc[i++] = ch;
X	desc[i] = '\0';
X	s.sg_dsize = i;
X	savestr(&s.sg_desc, desc);
X	(void) fclose(f);
X	(void) unlink(tempf);
X
Xfinish:
X	if (*namev != NIL && !eq(*namev, v[1])) {
X		FREEMEM(s.sg_name);
X		savestr(&s.sg_name, (char *)*namev);
X		for (i=0; i < AccountList.l_count; i++) {
X		    ac = (struct account *)AccountList.l_list[i];
X		    if (strlistchg(&ac->ac_sigs, (char *)v[1], s.sg_name))
X			ModBits |= AC;
X		}
X#ifdef SENDMAIL
X		for (i=0; i < AliasList.l_count; i++) {
X		    al = (struct alias *) AliasList.l_list[i];
X		    if (strlistchg(&al->al_sigs, (char *)v[1], s.sg_name))
X			ModBits |= AL;
X		}
X#endif
X		(void) strlistchg(&Sigs, (char *)v[1], s.sg_name);
X	}
X	if (changed) {
X		bcopy(sg, &s, sizeof (struct sig));
X		sort_list(&SigList, sigcmp);
X		ModBits |= SG;
X		puts("updated");
X	}
X	else
X		err("no change");
X	non_critical();
X
X	return;
X}
X
X/*
X * Add a group
X */
Xupdgroup(c, v)
Xint c;
Xaddr *v;
X
X{
X	struct groupmap g, *gm;
X#ifdef SENDMAIL
X	struct alias *al;
X#endif
X	struct account *ac;
X	struct range *rg;
X	char prompt[SHORT_BUF];
X	addr *tempv, *namev;
X	int i, cc, gid, changed = 0;
X
X	if ( c > 2 ) {
X		err1("%s: too many arguments", (char *)v[0]);
X		return;
X	}
X	if (c != 2) {
X		err1("usage: %s <name>", (char *)v[0]);
X		return;
X	}
X	gm = getgmnam((char *)v[1]);
X	if (!gm) {
X		err1("%s: group exists", (char *)v[1]);
X		return;
X	}
X	bcopy(&g, gm, sizeof (struct groupmap));
X
X	namev = get_gpa(2);
X	tempv = get_gpa(2);
X
X	(void) sprintf(prompt, "Name [%s]: ",  g.gm_name);
X	GetLine(prompt, 1, &cc, namev, &Null_List);
X	if (cc) {
X		if (!eq(*namev, v[1]))
X			; /* no change */
X		else if (groupexists((char *)*namev)) {
X			err("that name is taken");
X			return;
X		}
X		else
X			changed = 1;
X	}
X
X	(void) sprintf(prompt, "Gid [%d]: ", g.gm_gid);
X	GetLine(prompt, 1, &cc, tempv, &Null_List);
X	if (cc) {
X		if (!validint((char *)*tempv)) {
X			err1("%s makes no sense to me", (char *)tempv);
X			return;
X		}
X		gid = atoi((char *)*tempv);
X		if (gidexists(g.gm_gid)) {
X			err("that gid is taken");
X			return;
X		}
X		else {
X			g.gm_gid = gid;
X			changed = 1;
X		}
X	}
X
X	critical();
X	if (g.gm_gid != gm->gm_gid) {
X		changed = 1;
X		for (i=0; i < AccountList.l_count; i++) {
X			ac = (struct account *) AccountList.l_list[i];
X			if (ac->ac_gid == gm->gm_gid) {
X				ac->ac_gid = g.gm_gid;
X				ModBits |= AC;
X			}
X		}
X	}
X	if (*namev != NIL && !eq(*namev, v[1])) {
X	    changed = 1;
X	    FREEMEM(g.gm_name);
X	    savestr(&g.gm_name, (char *)*namev);
X	    (void) strlistchg(&Groups, (char *)v[1], g.gm_name);
X	    for (i=0; i < AccountList.l_count; i++) {
X		ac = (struct account *) AccountList.l_list[i];
X		if (strlistchg(&ac->ac_groups, (char *)v[1], g.gm_name))
X		    ModBits |= AC;
X	    }
X#ifdef SENDMAIL
X	    for (i=0; i < AliasList.l_count; i++) {
X		al = (struct alias *) AliasList.l_list[i];
X		if (strlistchg(&al->al_groups, (char *)v[1], g.gm_name))
X		    ModBits |= AL;
X	    }
X#endif
X	    rg = getrgnam((char *)v[1]);
X	    if (rg) {
X		FREEMEM(rg->rg_name);
X		savestr(&rg->rg_name, g.gm_name);
X		(void) strlistchg(&Ranges, (char *)v[1], g.gm_name);
X		sort_list(&RangeList, rangecmp);
X		ModBits |= RG;
X	    }
X	    if (vigexists((char *)v[1])) {
X		(void) strlistchg(&Vigs, (char *)v[1], g.gm_name);
X		ModBits |= VG;
X	    }
X	}
X	if (changed) {
X		bcopy(gm, &g, sizeof (struct groupmap));
X		sort_list(&GroupMapList, gmapcmp);
X		ModBits |= GR;
X		puts("updated");
X	}
X	else
X		err("no change");
X
X	non_critical();
X	return;
X}
X
X/*
X * Update a range
X */
Xupdrange(c, v)
Xint c;
Xaddr *v;
X
X{
X	struct range r, *rr, *rg;
X	char prompt[SHORT_BUF];
X	addr *tempv, *namev;
X	int cc, indx, changed = 0;
X
X	if ( c > 2 ) {
X		err1("%s: too many arguments", (char *)v[0]);
X		return;
X	}
X	if (c != 2) {
X		err1("usage: %s <name>", (char *)v[0]);
X		return;
X	}
X	rr = getrgnam((char *)v[1]);
X	if (!rr) {
X		err1("%s: no such range", (char *)v[1]);
X		return;
X	}
X	bcopy(&r, rr, sizeof (struct range));
X
X	namev = get_gpa(2);
X	tempv = get_gpa(2);
X
X	/*
X	 * New name?
X	 */
X	(void) sprintf(prompt, "Name [%s]: ",  r.rg_name);
X	GetLine(prompt, 1, &cc, namev, &Groups);
X	if (cc) {
X		if (!groupexists((char *)*namev)) {
X			err1("%s: no such group", (char *)*namev);
X			return;
X		}
X		if (eq(*namev, v[1]))
X			; /* no change */
X		else if (rangeexists((char *)namev)) {
X			err("that name is taken");
X			return;
X		}
X		else
X			changed = 1;
X	}
X
X	/*
X	 * From?
X	 */
X	(void) sprintf(prompt, "From [%d]: ", r.rg_from);
X	GetLine(prompt, 1, &cc, tempv, &Null_List);
X	if (cc) {
X		if (!validint((char *)*tempv)) {
X			err1("%s makes no sense to me", (char *)*tempv);
X			return;
X		}
X		r.rg_from = atoi((char *)*tempv);
X		(void) clear_gpa(tempv, 2);
X		changed = 1;
X	}
X
X	/*
X	 * To?
X	 */
X	(void) sprintf(prompt, "To [%d]: ", r.rg_to);
X	GetLine(prompt, 1, &cc, tempv, &Null_List);
X	if (cc) {
X		if (!validint((char *)*tempv)) {
X			err1("%s makes no sense to me", (char *)*tempv);
X			return;
X		}
X		r.rg_to = atoi((char *)*tempv);
X		(void) clear_gpa(tempv, 2);
X		changed = 1;
X	}
X
X	/*
X	 * New mode?
X	 */
X	(void) sprintf(prompt, "Mode [%s] : ",
X			(r.rg_mode == RG_SHARED ? "shared" : "exclusive"));
X	GetLine(prompt, 1, &cc, tempv, &mdlist);
X	if (!cc)
X		; /* no change */
X	else if (eq(*tempv, "shared")) {
X		r.rg_mode = RG_SHARED;
X		changed = 1;
X	}
X	else if (eq(*tempv, "exclusive")) {
X		r.rg_mode = RG_EXCLUSIVE;
X		changed = 1;
X	}
X	else {
X		err1("%s: unknown mode", (char *)*tempv);
X		return;
X	}
X
X	/*
X	 * Check to see if the new range conflicts with existing ranges
X	 */
X	for (indx=0; indx < RangeList.l_count; indx++) {
X		rg = (struct range *) RangeList.l_list[indx];
X		if (rg == rr)
X			continue;
X		if (rg->rg_mode == RG_SHARED && r.rg_mode == RG_SHARED)
X			continue;
X		if (INRANGE(r.rg_from, rg->rg_from, rg->rg_to)) {
X			err1("conflicts with range of group %s", rg->rg_name);
X			return;
X		}
X		if (INRANGE(r.rg_to, rg->rg_from, rg->rg_to)) {
X			err1("conflicts with range of group %s", rg->rg_name);
X			return;
X		}
X	}
X
X	critical();
X	if (*namev != NIL && !eq(*namev, v[1])) {
X		FREEMEM(r.rg_name);
X		savestr(&r.rg_name, (char *)*tempv);
X		(void) strlistchg(&Ranges, (char *)v[1], r.rg_name);
X	}
X	if (changed) {
X		bcopy(rr, &r, sizeof (struct range));
X		sort_list(&RangeList, rangecmp);
X		ModBits |= RG;
X		puts("updated");
X	}
X	else
X		err("no change");
X	non_critical();
X
X	return;
X}
X
Xupduser(c, v)
Xint c;
Xchar **v;
X
X{
X	struct account *ac, *ac2;
X	struct groupmap *gm;
X#ifdef SENDMAIL
X	struct alias *al;
X	struct class *cs;
X	struct sig *sg;
X	int ogid, j;
X#endif
X	addr *namev, *realnamev, *idv, *uidv, *gidv, *dirv, *passwdv, *shellv;
X	int uid, gid, changed = 0;
X#ifdef DOFILES
X	int mvdir = 0;
X#endif
X	int cc;
X	register int i;
X	char *cp, prompt[LONG_BUF], errmsg[LONG_BUF];
X
X	if ( c > 2 ) {
X		err1("%s: too many arguments", (char *)v[0]);
X		return;
X	}
X	if ( c != 2 ) {
X		err1("usage: %s <user>", (char *)v[0]);
X		return;
X	}
X	ac = getacnam((char *)v[1]);
X	if (!ac) {
X		err1("%s: no such user", (char *)v[1]);
X		return;
X	}
X
X	namev = get_gpa(2);
X	realnamev = get_gpa(17);
X	idv = get_gpa(2);
X	passwdv = get_gpa(2);
X	uidv = get_gpa(2);
X	gidv = get_gpa(2);
X	dirv = get_gpa(2);
X	shellv = get_gpa(2);
X
X	/*
X	 * Change login name?
X	 */
X	(void) sprintf(prompt, "Login name [%s]: ", ac->ac_name);
X	GetLine(prompt, 1, &cc, namev, &Null_List);
X
X	/*
X	 * Change real name?
X	 */
X	(void) sprintf(prompt, "Real Name [%s]: ", ac->ac_realname);
X	GetLine(prompt, 16, &cc, realnamev, &Null_List);
X
X	/*
X	 * Change id?
X	 */
X	(void) sprintf(prompt, "Id [%s]: ", ac->ac_id);
X	GetLine(prompt, 1, &cc, idv, &idlist);
X
X	/*
X	 * Change password?
X	 */
X	(void) sprintf(prompt, "Password (RETURN means no change): ");
X	GetLine(prompt, 1, &cc, passwdv, &pwlist);
X
X	/*
X	 * Change uid?
X	 */
X	(void) sprintf(prompt, "Uid [%d]: ", ac->ac_uid);
X	GetLine(prompt, 1, &cc, uidv, &Null_List);
X	if (cc && !validint((char *)*uidv)) {
X	    err1("%s makes no sense to me", (char *)*uidv);
X	    return;
X	}
X
X	/*
X	 * Change gid?
X	 */
X	(void) sprintf(prompt, "Gid [%d]: ", ac->ac_gid);
X	GetLine(prompt, 1, &cc, gidv, &Null_List);
X	if (cc && !validint((char *)*gidv)) {
X	    err1("%s makes no sense to me", (char *)*gidv);
X	    return;
X	}
X
X	/*
X	 * Rename home directory?
X	 */
X	(void) sprintf(prompt, "Home [%s]: ", ac->ac_dir);
X	GetFilenames(prompt, 1, &cc, dirv);
X#ifdef DOFILES
X	if (cc && !eq(*dirv, ac->ac_dir) && fileexists((char *)*dirv))
X	  err2("%s already exists, so I won't move %s", (char *)*dirv,
X	       (char *)ac->ac_dir);
X	else
X	  mvdir = 1;
X#endif
X
X	/*
X	 * New shell?
X	 */
X	(void) sprintf(prompt, "Shell [%s]: ", ac->ac_shell);
X	GetLine(prompt, 1, &cc, shellv, &Shells);
X	if (cc && !fileexists((char *)*shellv))
X	    err1("Warning: %s does not exist", (char *)*shellv);
X
X	critical();
X
X	/*
X	 * If given a different user name, use it.  No duplicate
X	 * user names allowed (of course).
X	 */
X	if (*namev != NIL && !eq(*namev, ac->ac_name)) {
X	    if (!userexists((char *)*namev)) {
X		/*
X		 * Update group member lists
X		 */
X		for (i=0; i < GroupMapList.l_count; i++) {
X		    gm = (struct groupmap *)GroupMapList.l_list[i];
X		    if (strlistchg(&gm->gm_mem, (char *)v[1], (char *)*namev))
X			ModBits |= GR;
X		}
X#ifdef SENDMAIL
X		/*
X		 * Update aliases
X		 */
X		for (i=0; i < AliasList.l_count; i++) {
X		    al = (struct alias *) AliasList.l_list[i];
X		    if (strlistchg(&al->al_addresses, (char *)v[1],
X				(char *)*namev))
X			ModBits |= AL;
X		}
X		al = getalnam((char *)ac->ac_name);
X		if (al) {
X		    ModBits |= AL;
X		    for (i=0; i < AccountList.l_count; i++) {
X			ac2 = (struct account *) AccountList.l_list[i];
X			if (!strlistchg(&ac2->ac_aliases, al->al_name,
X				(char *)*namev))
X			    continue;
X			ModBits |= AC;
X		    }
X		    for (i=0; i < GroupMapList.l_count; i++) {
X			gm = (struct groupmap *) GroupMapList.l_list[i];
X			(void) strlistchg(&gm->gm_aliases, al->al_name,
X					(char *)*namev);
X		    }
X		    for (i=0; i < ClassList.l_count; i++) {
X			cs = (struct class *) ClassList.l_list[i];
X			(void) strlistchg(&cs->cs_aliases, al->al_name,
X					(char *)*namev);
X		    }
X		    for (i=0; i < SigList.l_count; i++) {
X			sg = (struct sig *) SigList.l_list[i];
X			(void) strlistchg(&sg->sg_aliases, al->al_name,
X					(char *)*namev);
X		    }
X		    (void) strlistchg(&Aliases, al->al_name, (char *)*namev);
X		    FREEMEM(al->al_name);
X		    savestr(&al->al_name, (char *)*namev);
X		    sort_list(&AliasList, aliascmp);
X		}
X#endif
X		/*
X		 * Now fix the accounts struct and the users
X		 * completion list
X		 */
X		(void) strlistchg(&Users, (char *)ac->ac_name, (char *)*namev);
X		FREEMEM((char *)ac->ac_name);
X		savestr((char **)&ac->ac_name, (char *)*namev);
X		ModBits |= (AC|PW);
X		changed++;
X	    }
X	    else {
X		err1("%s: user exists", (char *)*namev);
X		err("login name unchanged");
X	    }
X	}
X
X	/*
X	 * If given a new real name, use it.
X	 */
X	if (*realnamev != NIL) {
X		cp = (char *)glob(realnamev);
X		if (!eq(cp, ac->ac_realname)) {
X			FREEMEM((char *)ac->ac_realname);
X			savestr((char **)&ac->ac_realname, cp);
X			ModBits |= AC;
X			changed++;
X		}
X	}
X
X	/*
X	 * If id changed, record it.  Since the user already has an
X	 * account we don't care if his id matches anyone else's
X	 * The check for duplicate ids is done at when a user is added.
X	 */
X	if (*idv != NIL && !eq(*idv, ac->ac_id)) {
X		FREEMEM((char *)ac->ac_id);
X		savestr((char **)&ac->ac_id, (char *)*idv);
X		ModBits |= AC;
X		changed++;
X	}
X
X	/*
X	 * Handle change of password
X	 */
X	if (*passwdv != NIL) {
X		FREEMEM((char *)ac->ac_passwd);
X		if (eq(*passwdv, "unused"))
X			savestr((char **)&ac->ac_passwd, "*");
X		else if (eq(*passwdv, "none"))
X			savestr((char **)&ac->ac_passwd, "");
X		else if (eq(*passwdv, "generate")) {
X			cp = makepass();
X			savestr((char **)&ac->ac_passwd, crypt(cp, rsalt()));
X			(void) printf("password is \"%s\"\n", cp);
X		}
X		else
X			savestr((char **)&ac->ac_passwd,
X				crypt((char *)*passwdv, rsalt()));
X		ModBits |= PW;
X		changed++;
X	}
X
X	/*
X	 * Note home directory change, if any.  This must be before
X	 * checking for a change in uid so that the omni_chown isn't
X	 * suddenly left high and dry if user's directory is moved.
X	 */
X	if (*dirv != NIL && !eq(*dirv, ac->ac_dir)) {
X#ifdef DOFILES
X		if (mvdir)
X			add_job(JB_MV, ac->ac_dir, *dirv, NIL);
X#else
X		err("Don't forget to move the user's home directory");
X#endif
X		FREEMEM((char *)ac->ac_dir);
X		savestr((char **)&ac->ac_dir, (char *)*dirv);
X		ModBits |= PW;
X		changed++;
X	}
X
X	/*
X	 * Handle a change of uid.  This entails changing the ownership
X	 * of this user's files when savechanges() is called.  Sharing
X	 * of uids is permitted but a warning message is printed.
X	 */
X	if (*uidv != NIL) {
X	    uid = atoi((char *)*uidv);
X	    if (uid <= 0)
X		err("uid is out of range");
X	    else if (uid != ac->ac_uid && uid >= 0) {
X		ac2 = getacuid(uid);
X		if (ac2)
X		    (void) printf("warning: uid %d is shared by %s\n",
X				uid, ac2->ac_name);
X#ifdef DOFILES
X		add_job(JB_OMNICHOWN, &ac->ac_uid, &uid, NIL);
X#else
X		err("Do not forget to chown the user files.");
X#endif
X		ac->ac_uid = uid;
X		sort_list(&AccountList, acctcmp);
X		ModBits |= (AC|PW);
X		add_job(JB_LASTLOG, &ac->ac_uid, (addr)&ac->ac_ll, NIL);
X		changed++;
X	    }
X	}
X
X	/*
X	 * Handle a change of gid.  Must make sure there is group
X	 * associated with the gid.
X	 */
X	if (*gidv != NIL) {
X		gid = atoi((char *)*gidv);
X		if (gid < 0)
X			err("gid is out of range");
X		else if (!(gm = getgmgid(gid))) {
X			(void) sprintf(errmsg, 
X				"no group associated with gid %d",
X				gid);
X			err(errmsg);
X		}
X		else if (gid != ac->ac_gid) {
X#ifdef SENDMAIL
X			ogid = ac->ac_gid;
X#endif
X			ac->ac_gid = gid;
X			ModBits |= (AC|PW);
X#ifdef SENDMAIL
X			if (gm->gm_aliases.l_count)
X			    RXBindings(ac);
X			gm = getgmgid(ogid);
X			for (j=0; j < gm->gm_aliases.l_count; j++) {
X			    al = getalnam((char *)gm->gm_aliases.l_list[j]);
X			    if (!al) continue;	/* trouble */
X			    if (!instrlist(&al->al_addresses, (char *)ac->ac_name)) {
X				strlistadd(&al->al_addresses, (char *)ac->ac_name);
X				sort_list(&al->al_addresses, pstrcmp);
X				ModBits |= AL;
X			    }
X			}
X#endif
X			changed++;
X		}
X	}
X
X	/*
X	 * Make change in shell if necessary.
X	 */
X	if (*shellv != NIL && !eq(*shellv, ac->ac_shell)) {
X		FREEMEM((char *)ac->ac_shell);
X		savestr((char **)&ac->ac_shell, (char *)*shellv);
X		ModBits |= PW;
X		changed++;
X	}
X
X	if (changed)
X		puts("updated");
X	else
X		err("no change");
X	non_critical();
X
X	return;
X}
@//E*O*F src/update.c//
if test 22502 -ne "`wc -c <'src/update.c'`"; then
    echo shar: error transmitting "'src/update.c'" '(should have been 22502 characters)'
fi
fi # end of overwriting check
echo shar: "End of archive 8 (of 8)."
cp /dev/null ark8isdone
DONE=true
for I in 1 2 3 4 5 6 7 8; do
    if test -! f ark${I}isdone; then
        echo "You still need to run archive ${I}."
        DONE=false
    fi
done
case $DONE in
    true)
        echo "You have run all 8 archives."
        echo 'See the README file'
        ;;
esac
##  End of shell archive.
exit 0



More information about the Mod.sources mailing list