Subject: v08i048: Account creation/manipulation program, Part08/08 Newsgroups: mod.sources Approved: mirror!rs Submitted by: Kyle Jones 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@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 X#include X#include X#include X#include X#include 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 X#include X#include X#include X#include X#include X#include X#include 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 X#include X#include 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 X#include X#include 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 X#include 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 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 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 X#include X#include X#include X#include X#include 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 ", (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 ", (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 ", (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 ", (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 ", (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 ", (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