home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright 1990, John F. Haugh II
- * An unpublished work.
- * All rights reserved.
- *
- * Use, duplication, and disclosure prohibited without
- * the express written permission of the author.
- *
- * This file implements a transaction oriented group database
- * library. The group file is updated one entry at a time.
- * After each transaction the file must be logically closed and
- * transferred to the existing group file. The sequence of
- * events is
- *
- * gr_lock -- lock group file
- * gr_open -- logically open group file
- * while transaction to process
- * gr_(locate,update,remove) -- perform transaction
- * done
- * gr_close -- commit transactions
- * gr_unlock -- remove group lock
- */
-
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <errno.h>
- #include <grp.h>
- #include <stdio.h>
-
- #ifndef lint
- static char sccsid[] = "@(#)groupio.c 3.2 22:14:38 11/16/90";
- #endif
-
- static int islocked;
- static int isopen;
- static int open_modes;
- static FILE *grfp;
-
- struct gr_file_entry {
- char *grf_line;
- int grf_changed;
- struct group *grf_entry;
- struct gr_file_entry *grf_next;
- };
-
- static struct gr_file_entry *grf_head;
- static struct gr_file_entry *grf_tail;
- static struct gr_file_entry *grf_cursor;
- static int gr_changed;
-
- #define GR_LOCK "/etc/group.lock"
- #define GR_TEMP "/etc/grp.%d"
- #define GROUP "/etc/group"
- #define OGROUP "/etc/group-"
-
- extern char *strdup();
- extern struct group *sgetgrent();
-
- /*
- * gr_dup - duplicate a group file entry
- *
- * gr_dup() accepts a pointer to a group file entry and
- * returns a pointer to a group file entry in allocated
- * memory.
- */
-
- static struct group *
- gr_dup (grent)
- struct group *grent;
- {
- struct group *gr;
- int i;
-
- if (! (gr = (struct group *) malloc (sizeof *gr)))
- return 0;
-
- if ((gr->gr_name = strdup (grent->gr_name)) == 0 ||
- (gr->gr_passwd = strdup (grent->gr_passwd)) == 0)
- return 0;
-
- for (i = 0;grent->gr_mem[i];i++)
- ;
-
- gr->gr_mem = (char **) malloc (sizeof (char *) * (i + 1));
- for (i = 0;grent->gr_mem[i];i++)
- if (! (gr->gr_mem[i] = strdup (grent->gr_mem[i])))
- return 0;
-
- gr->gr_mem[i] = 0;
- gr->gr_gid = grent->gr_gid;
-
- return gr;
- }
-
- /*
- * gr_free - free a dynamically allocated group file entry
- *
- * gr_free() frees up the memory which was allocated for the
- * pointed to entry.
- */
-
- static void
- gr_free (grent)
- struct group *grent;
- {
- int i;
-
- free (grent->gr_name);
- free (grent->gr_passwd);
-
- for (i = 0;grent->gr_mem[i];i++)
- free (grent->gr_mem[i]);
-
- free (grent->gr_mem);
- }
-
- /*
- * gr_lock - lock a group file
- *
- * gr_lock() encapsulates the lock operation. it returns
- * TRUE or FALSE depending on the group file being
- * properly locked. the lock is set by creating a semaphore
- * file, GR_LOCK.
- */
-
- int
- gr_lock ()
- {
- int fd;
- int pid;
- int len;
- char file[BUFSIZ];
- char buf[32];
- struct stat sb;
-
- if (islocked)
- return 1;
-
- /*
- * Create a lock file which can be switched into place
- */
-
- sprintf (file, GR_TEMP, getpid ());
- if ((fd = open (file, O_CREAT|O_EXCL|O_WRONLY, 0600)) == -1)
- return 0;
-
- sprintf (buf, "%d", getpid ());
- if (write (fd, buf, strlen (buf) + 1) != strlen (buf) + 1) {
- (void) close (fd);
- (void) unlink (file);
- return 0;
- }
- close (fd);
-
- /*
- * Simple case first -
- * Link fails (in a sane environment ...) if the target
- * exists already. So we try to switch in a new lock
- * file. If that succeeds, we assume we have the only
- * valid lock. Needs work for NFS where this assumption
- * may not hold. The simple hack is to check the link
- * count on the source file, which should be 2 iff the
- * link =really= worked.
- */
-
- if (link (file, GR_LOCK) == 0) {
- if (stat (file, &sb) != 0)
- return 0;
-
- if (sb.st_nlink != 2)
- return 0;
-
- (void) unlink (file);
- islocked = 1;
- return 1;
- }
-
- /*
- * Invalid lock test -
- * Open the lock file and see if the lock is valid.
- * The PID of the lock file is checked, and if the PID
- * is not valid, the lock file is removed. If the unlink
- * of the lock file fails, it should mean that someone
- * else is executing this code. They will get success,
- * and we will fail.
- */
-
- if ((fd = open (GR_LOCK, O_RDWR)) == -1 ||
- (len = read (fd, buf, BUFSIZ)) <= 0) {
- errno = EINVAL;
- return 0;
- }
- buf[len] = '\0';
- if ((pid = strtol (buf, (char **) 0, 10)) == 0) {
- errno = EINVAL;
- return 0;
- }
- if (kill (pid, 0) == 0) {
- errno = EEXIST;
- return 0;
- }
- if (unlink (GR_LOCK)) {
- (void) close (fd);
- (void) unlink (file);
-
- return 0;
- }
-
- /*
- * Re-try lock -
- * The invalid lock has now been removed and I should
- * be able to acquire a lock for myself just fine. If
- * this fails there will be no retry. The link count
- * test here makes certain someone executing the previous
- * block of code didn't just remove the lock we just
- * linked to.
- */
-
- if (link (file, GR_LOCK) == 0) {
- if (stat (file, &sb) != 0)
- return 0;
-
- if (sb.st_nlink != 2)
- return 0;
-
- (void) unlink (file);
- islocked = 1;
- return 1;
- }
- (void) unlink (file);
- return 0;
- }
-
- /*
- * gr_unlock - logically unlock a group file
- *
- * gr_unlock() removes the lock which was set by an earlier
- * invocation of gr_lock().
- */
-
- int
- gr_unlock ()
- {
- if (islocked) {
- if (isopen) {
- open_modes = O_RDONLY;
- gr_close ();
- }
- unlink (GR_LOCK);
- islocked = 0;
- return 1;
- } else
- return 0;
- }
-
- /*
- * gr_open - open a group file
- *
- * gr_open() encapsulates the open operation. it returns
- * TRUE or FALSE depending on the group file being
- * properly opened.
- */
-
- int
- gr_open (mode)
- int mode;
- {
- char buf[8192];
- struct gr_file_entry *grf;
- struct group *grent;
-
- if (isopen || (mode != O_RDONLY && mode != O_RDWR))
- return 0;
-
- if (mode != O_RDONLY && ! islocked)
- return 0;
-
- if ((grfp = fopen (GROUP, mode == O_RDONLY ? "r":"r+")) == 0)
- return 0;
-
- grf_head = grf_tail = grf_cursor = 0;
- gr_changed = 0;
-
- while (fgets (buf, sizeof buf, grfp) != (char *) 0) {
- if (! (grf = (struct gr_file_entry *) malloc (sizeof *grf)))
- return 0;
-
- grf->grf_changed = 0;
- grf->grf_line = strdup (buf);
- if ((grent = sgetgrent (buf)) && ! (grent = gr_dup (grent)))
- return 0;
-
- grf->grf_entry = grent;
-
- if (grf_head == 0) {
- grf_head = grf_tail = grf;
- grf->grf_next = 0;
- } else {
- grf_tail->grf_next = grf;
- grf->grf_next = 0;
- grf_tail = grf;
- }
- }
- isopen++;
- open_modes = mode;
-
- return 1;
- }
-
- /*
- * gr_close - close the group file
- *
- * gr_close() outputs any modified group file entries and
- * frees any allocated memory.
- */
-
- int
- gr_close ()
- {
- int fd;
- int mask;
- int c;
- int i;
- int errors = 0;
- FILE *bkfp;
- struct gr_file_entry *grf;
- struct gr_file_entry *ogrf;
-
- if (! isopen) {
- errno = EINVAL;
- return 0;
- }
- if (open_modes == O_RDWR && gr_changed) {
- mask = umask (0222);
- if ((bkfp = fopen (OGROUP, "w")) == 0) {
- umask (mask);
- return 0;
- }
- umask (mask);
-
- rewind (grfp);
- while ((c = getc (grfp)) != EOF) {
- if (putc (c, bkfp) == EOF) {
- fclose (bkfp);
- return 0;
- }
- }
- if (fclose (bkfp))
- return 0;
-
- isopen = 0;
- (void) fclose (grfp);
-
- mask = umask (0222);
- if (! (grfp = fopen (GROUP, "w"))) {
- umask (mask);
- return 0;
- }
- umask (mask);
-
- for (grf = grf_head;errors == 0 && grf;grf = grf->grf_next) {
- if (grf->grf_changed) {
- if (putgrent (grf->grf_entry, grfp))
- errors++;
- } else {
- if (fputs (grf->grf_line, grfp) == EOF)
- errors++;
- }
- }
- if (fflush (grfp))
- errors++;
-
- if (errors) {
- unlink (GROUP);
- link (OGROUP, GROUP);
- unlink (OGROUP);
- return 0;
- }
- }
- if (fclose (grfp))
- return 0;
-
- grfp = 0;
-
- while (grf_head != 0) {
- grf = grf_head;
- grf_head = grf->grf_next;
-
- if (grf->grf_entry) {
- gr_free (grf->grf_entry);
- free (grf->grf_entry);
- }
- if (grf->grf_line)
- free (grf->grf_line);
-
- free (grf);
- }
- grf_tail = 0;
- return 1;
- }
-
- int
- gr_update (grent)
- struct group *grent;
- {
- struct gr_file_entry *grf;
- struct group *ngr;
-
- if (! isopen || open_modes == O_RDONLY) {
- errno = EINVAL;
- return 0;
- }
- for (grf = grf_head;grf != 0;grf = grf->grf_next) {
- if (grf->grf_entry == 0)
- continue;
-
- if (strcmp (grent->gr_name, grf->grf_entry->gr_name) != 0)
- continue;
-
- if (! (ngr = gr_dup (grent)))
- return 0;
- else {
- gr_free (grf->grf_entry);
- *(grf->grf_entry) = *ngr;
- }
- grf->grf_changed = 1;
- grf_cursor = grf;
- return gr_changed = 1;
- }
- grf = (struct gr_file_entry *) malloc (sizeof *grf);
- if (! (grf->grf_entry = gr_dup (grent)))
- return 0;
-
- grf->grf_changed = 1;
- grf->grf_next = 0;
- grf->grf_line = 0;
-
- if (grf_tail)
- grf_tail->grf_next = grf;
-
- if (! grf_head)
- grf_head = grf;
-
- grf_tail = grf;
-
- return gr_changed = 1;
- }
-
- int
- gr_remove (name)
- char *name;
- {
- struct gr_file_entry *grf;
- struct gr_file_entry *ogrf;
-
- if (! isopen || open_modes == O_RDONLY) {
- errno = EINVAL;
- return 0;
- }
- for (ogrf = 0, grf = grf_head;grf != 0;
- ogrf = grf, grf = grf->grf_next) {
- if (! grf->grf_entry)
- continue;
-
- if (strcmp (name, grf->grf_entry->gr_name) != 0)
- continue;
-
- if (grf == grf_cursor)
- grf_cursor = ogrf;
-
- if (ogrf != 0)
- ogrf->grf_next = grf->grf_next;
- else
- grf_head = grf->grf_next;
-
- if (grf == grf_tail)
- grf_tail = ogrf;
-
- return gr_changed = 1;
- }
- errno = ENOENT;
- return 0;
- }
-
- struct group *
- gr_locate (name)
- char *name;
- {
- struct gr_file_entry *grf;
-
- if (! isopen) {
- errno = EINVAL;
- return 0;
- }
- for (grf = grf_head;grf != 0;grf = grf->grf_next) {
- if (grf->grf_entry == 0)
- continue;
-
- if (strcmp (name, grf->grf_entry->gr_name) == 0) {
- grf_cursor = grf;
- return grf->grf_entry;
- }
- }
- errno = ENOENT;
- return 0;
- }
-
- int
- gr_rewind ()
- {
- if (! isopen) {
- errno = EINVAL;
- return 0;
- }
- grf_cursor = 0;
- return 1;
- }
-
- struct group *
- gr_next ()
- {
- if (! isopen) {
- errno = EINVAL;
- return 0;
- }
- if (grf_cursor == 0)
- grf_cursor = grf_head;
- else
- grf_cursor = grf_cursor->grf_next;
-
- while (grf_cursor) {
- if (grf_cursor->grf_entry)
- return grf_cursor->grf_entry;
-
- grf_cursor = grf_cursor->grf_next;
- }
- return 0;
- }
-