home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 3 / 3346 / groupio.c next >
Encoding:
C/C++ Source or Header  |  1991-05-17  |  10.5 KB  |  586 lines

  1. /*
  2.  * Copyright 1990, John F. Haugh II
  3.  * An unpublished work.
  4.  * All rights reserved.
  5.  *
  6.  * Use, duplication, and disclosure prohibited without
  7.  * the express written permission of the author.
  8.  *
  9.  *    This file implements a transaction oriented group database
  10.  *    library.  The group file is updated one entry at a time.
  11.  *    After each transaction the file must be logically closed and
  12.  *    transferred to the existing group file.  The sequence of
  13.  *    events is
  14.  *
  15.  *    gr_lock                -- lock group file
  16.  *    gr_open                -- logically open group file
  17.  *    while transaction to process
  18.  *        gr_(locate,update,remove) -- perform transaction
  19.  *    done
  20.  *    gr_close            -- commit transactions
  21.  *    gr_unlock            -- remove group lock
  22.  */
  23.  
  24. #include <sys/stat.h>
  25. #include <fcntl.h>
  26. #include <errno.h>
  27. #include <grp.h>
  28. #include <stdio.h>
  29. #ifdef    BSD
  30. #include <strings.h>
  31. #else
  32. #include <string.h>
  33. #endif
  34.  
  35. #ifndef    lint
  36. static    char    sccsid[] = "@(#)groupio.c    3.6 15:59:12 12/9/90";
  37. #endif
  38.  
  39. static    int    islocked;
  40. static    int    isopen;
  41. static    int    open_modes;
  42. static    FILE    *grfp;
  43.  
  44. struct    gr_file_entry {
  45.     char    *grf_line;
  46.     int    grf_changed;
  47.     struct    group    *grf_entry;
  48.     struct    gr_file_entry *grf_next;
  49. };
  50.  
  51. static    struct    gr_file_entry    *grf_head;
  52. static    struct    gr_file_entry    *grf_tail;
  53. static    struct    gr_file_entry    *grf_cursor;
  54. static    int    gr_changed;
  55. static    int    lock_pid;
  56.  
  57. #define    GR_LOCK    "/etc/group.lock"
  58. #define    GR_TEMP "/etc/grp.%d"
  59. #define    GROUP    "/etc/group"
  60.  
  61. static    char    gr_filename[BUFSIZ] = GROUP;
  62.  
  63. extern    char    *strdup();
  64. extern    struct    group    *sgetgrent();
  65.  
  66. /*
  67.  * gr_dup - duplicate a group file entry
  68.  *
  69.  *    gr_dup() accepts a pointer to a group file entry and
  70.  *    returns a pointer to a group file entry in allocated
  71.  *    memory.
  72.  */
  73.  
  74. static struct group *
  75. gr_dup (grent)
  76. struct    group    *grent;
  77. {
  78.     struct    group    *gr;
  79.     int    i;
  80.  
  81.     if (! (gr = (struct group *) malloc (sizeof *gr)))
  82.         return 0;
  83.  
  84.     if ((gr->gr_name = strdup (grent->gr_name)) == 0 ||
  85.             (gr->gr_passwd = strdup (grent->gr_passwd)) == 0)
  86.         return 0;
  87.  
  88.     for (i = 0;grent->gr_mem[i];i++)
  89.         ;
  90.  
  91.     gr->gr_mem = (char **) malloc (sizeof (char *) * (i + 1));
  92.     for (i = 0;grent->gr_mem[i];i++)
  93.         if (! (gr->gr_mem[i] = strdup (grent->gr_mem[i])))
  94.             return 0;
  95.  
  96.     gr->gr_mem[i] = 0;
  97.     gr->gr_gid = grent->gr_gid;
  98.  
  99.     return gr;
  100. }
  101.  
  102. /*
  103.  * gr_free - free a dynamically allocated group file entry
  104.  *
  105.  *    gr_free() frees up the memory which was allocated for the
  106.  *    pointed to entry.
  107.  */
  108.  
  109. static void
  110. gr_free (grent)
  111. struct    group    *grent;
  112. {
  113.     int    i;
  114.  
  115.     free (grent->gr_name);
  116.     free (grent->gr_passwd);
  117.  
  118.     for (i = 0;grent->gr_mem[i];i++)
  119.         free (grent->gr_mem[i]);
  120.  
  121.     free (grent->gr_mem);
  122. }
  123.  
  124. /*
  125.  * gr_name - change the name of the group file
  126.  */
  127.  
  128. int
  129. gr_name (name)
  130. char    *name;
  131. {
  132.     if (isopen || strlen (name) > (BUFSIZ-10))
  133.         return -1;
  134.  
  135.     strcpy (gr_filename, name);
  136.     return 0;
  137. }
  138.  
  139. /*
  140.  * gr_lock - lock a group file
  141.  *
  142.  *    gr_lock() encapsulates the lock operation.  it returns
  143.  *    TRUE or FALSE depending on the group file being
  144.  *    properly locked.  the lock is set by creating a semaphore
  145.  *    file, GR_LOCK.
  146.  */
  147.  
  148. int
  149. gr_lock ()
  150. {
  151.     int    fd;
  152.     int    pid;
  153.     int    len;
  154.     char    file[BUFSIZ];
  155.     char    buf[32];
  156.     struct    stat    sb;
  157.  
  158.     if (islocked)
  159.         return 1;
  160.  
  161.     if (strcmp (gr_filename, GROUP) != 0)
  162.         return 0;
  163.  
  164.     /*
  165.      * Create a lock file which can be switched into place
  166.      */
  167.  
  168.     sprintf (file, GR_TEMP, lock_pid = getpid ());
  169.     if ((fd = open (file, O_CREAT|O_EXCL|O_WRONLY, 0600)) == -1)
  170.         return 0;
  171.  
  172.     sprintf (buf, "%d", lock_pid);
  173.     if (write (fd, buf, strlen (buf) + 1) != strlen (buf) + 1) {
  174.         (void) close (fd);
  175.         (void) unlink (file);
  176.         return 0;
  177.     }
  178.     close (fd);
  179.  
  180.     /*
  181.      * Simple case first -
  182.      *    Link fails (in a sane environment ...) if the target
  183.      *    exists already.  So we try to switch in a new lock
  184.      *    file.  If that succeeds, we assume we have the only
  185.      *    valid lock.  Needs work for NFS where this assumption
  186.      *    may not hold.  The simple hack is to check the link
  187.      *    count on the source file, which should be 2 iff the
  188.      *    link =really= worked.
  189.      */
  190.  
  191.     if (link (file, GR_LOCK) == 0) {
  192.         if (stat (file, &sb) != 0)
  193.             return 0;
  194.  
  195.         if (sb.st_nlink != 2)
  196.             return 0;
  197.  
  198.         (void) unlink (file);
  199.         islocked = 1;
  200.         return 1;
  201.     }
  202.  
  203.     /*
  204.      * Invalid lock test -
  205.      *    Open the lock file and see if the lock is valid.
  206.      *    The PID of the lock file is checked, and if the PID
  207.      *    is not valid, the lock file is removed.  If the unlink
  208.      *    of the lock file fails, it should mean that someone
  209.      *    else is executing this code.  They will get success,
  210.      *    and we will fail.
  211.      */
  212.  
  213.     if ((fd = open (GR_LOCK, O_RDWR)) == -1 ||
  214.             (len = read (fd, buf, BUFSIZ)) <= 0) {
  215.         errno = EINVAL;
  216.         return 0;
  217.     }
  218.     buf[len] = '\0';
  219.     if ((pid = strtol (buf, (char **) 0, 10)) == 0) {
  220.         errno = EINVAL;
  221.         return 0;
  222.     }
  223.     if (kill (pid, 0) == 0)  {
  224.         errno = EEXIST;
  225.         return 0;
  226.     }
  227.     if (unlink (GR_LOCK)) {
  228.         (void) close (fd);
  229.         (void) unlink (file);
  230.  
  231.         return 0;
  232.     }
  233.  
  234.     /*
  235.      * Re-try lock -
  236.      *    The invalid lock has now been removed and I should
  237.      *    be able to acquire a lock for myself just fine.  If
  238.      *    this fails there will be no retry.  The link count
  239.      *    test here makes certain someone executing the previous
  240.      *    block of code didn't just remove the lock we just
  241.      *    linked to.
  242.      */
  243.  
  244.     if (link (file, GR_LOCK) == 0) {
  245.         if (stat (file, &sb) != 0)
  246.             return 0;
  247.  
  248.         if (sb.st_nlink != 2)
  249.             return 0;
  250.  
  251.         (void) unlink (file);
  252.         islocked = 1;
  253.         return 1;
  254.     }
  255.     (void) unlink (file);
  256.     return 0;
  257. }
  258.  
  259. /*
  260.  * gr_unlock - logically unlock a group file
  261.  *
  262.  *    gr_unlock() removes the lock which was set by an earlier
  263.  *    invocation of gr_lock().
  264.  */
  265.  
  266. int
  267. gr_unlock ()
  268. {
  269.     if (isopen) {
  270.         open_modes = O_RDONLY;
  271.         if (! gr_close ())
  272.             return 0;
  273.     }
  274.     if (islocked) {
  275.         islocked = 0;
  276.         if (lock_pid != getpid ())
  277.             return 0;
  278.  
  279.         (void) unlink (GR_LOCK);
  280.         return 1;
  281.     }
  282.     return 0;
  283. }
  284.  
  285. /*
  286.  * gr_open - open a group file
  287.  *
  288.  *    gr_open() encapsulates the open operation.  it returns
  289.  *    TRUE or FALSE depending on the group file being
  290.  *    properly opened.
  291.  */
  292.  
  293. int
  294. gr_open (mode)
  295. int    mode;
  296. {
  297.     char    buf[8192];
  298.     char    *cp;
  299.     struct    gr_file_entry    *grf;
  300.     struct    group    *grent;
  301.  
  302.     if (isopen || (mode != O_RDONLY && mode != O_RDWR))
  303.         return 0;
  304.  
  305.     if (mode != O_RDONLY && ! islocked &&
  306.             strcmp (gr_filename, GROUP) == 0)
  307.         return 0;
  308.  
  309.     if ((grfp = fopen (gr_filename, mode == O_RDONLY ? "r":"r+")) == 0)
  310.         return 0;
  311.  
  312.     grf_head = grf_tail = grf_cursor = 0;
  313.     gr_changed = 0;
  314.  
  315.     while (fgetsx (buf, sizeof buf, grfp) != (char *) 0) {
  316.         if (cp = strrchr (buf, '\n'))
  317.             *cp = '\0';
  318.  
  319.         if (! (grf = (struct gr_file_entry *) malloc (sizeof *grf)))
  320.             return 0;
  321.  
  322.         grf->grf_changed = 0;
  323.         grf->grf_line = strdup (buf);
  324.         if ((grent = sgetgrent (buf)) && ! (grent = gr_dup (grent)))
  325.             return 0;
  326.  
  327.         grf->grf_entry = grent;
  328.  
  329.         if (grf_head == 0) {
  330.             grf_head = grf_tail = grf;
  331.             grf->grf_next = 0;
  332.         } else {
  333.             grf_tail->grf_next = grf;
  334.             grf->grf_next = 0;
  335.             grf_tail = grf;
  336.         }
  337.     }
  338.     isopen++;
  339.     open_modes = mode;
  340.  
  341.     return 1;
  342. }
  343.  
  344. /*
  345.  * gr_close - close the group file
  346.  *
  347.  *    gr_close() outputs any modified group file entries and
  348.  *    frees any allocated memory.
  349.  */
  350.  
  351. int
  352. gr_close ()
  353. {
  354.     char    backup[BUFSIZ];
  355.     int    fd;
  356.     int    mask;
  357.     int    c;
  358.     int    i;
  359.     int    errors = 0;
  360.     FILE    *bkfp;
  361.     struct    gr_file_entry *grf;
  362.     struct    gr_file_entry *ogrf;
  363.  
  364.     if (! isopen) {
  365.         errno = EINVAL;
  366.         return 0;
  367.     }
  368.     if (islocked && lock_pid != getpid ()) {
  369.         isopen = 0;
  370.         islocked = 0;
  371.         errno = EACCES;
  372.         return 0;
  373.     }
  374.     strcpy (backup, gr_filename);
  375.     strcat (backup, "-");
  376.  
  377.     if (open_modes == O_RDWR && gr_changed) {
  378.         mask = umask (0222);
  379.         if ((bkfp = fopen (backup, "w")) == 0) {
  380.             umask (mask);
  381.             return 0;
  382.         }
  383.         umask (mask);
  384.  
  385.         rewind (grfp);
  386.         while ((c = getc (grfp)) != EOF) {
  387.             if (putc (c, bkfp) == EOF) {
  388.                 fclose (bkfp);
  389.                 return 0;
  390.             }
  391.         }
  392.         if (fclose (bkfp))
  393.             return 0;
  394.  
  395.         isopen = 0;
  396.         (void) fclose (grfp);
  397.  
  398.         mask = umask (0222);
  399.         if (! (grfp = fopen (gr_filename, "w"))) {
  400.             umask (mask);
  401.             return 0;
  402.         }
  403.         umask (mask);
  404.  
  405.         for (grf = grf_head;! errors && grf;grf = grf->grf_next) {
  406.             if (grf->grf_changed) {
  407.                 if (putgrent (grf->grf_entry, grfp))
  408.                     errors++;
  409.             } else {
  410.                 if (fputsx (grf->grf_line, grfp))
  411.                     errors++;
  412.  
  413.                 if (putc ('\n', grfp) == EOF)
  414.                     errors++;
  415.             }
  416.         }
  417.         if (fflush (grfp))
  418.             errors++;
  419.  
  420.         if (errors) {
  421.             unlink (gr_filename);
  422.             link (backup, gr_filename);
  423.             unlink (backup);
  424.             return 0;
  425.         }
  426.     }
  427.     if (fclose (grfp))
  428.         return 0;
  429.  
  430.     grfp = 0;
  431.  
  432.     while (grf_head != 0) {
  433.         grf = grf_head;
  434.         grf_head = grf->grf_next;
  435.  
  436.         if (grf->grf_entry) {
  437.             gr_free (grf->grf_entry);
  438.             free (grf->grf_entry);
  439.         }
  440.         if (grf->grf_line)
  441.             free (grf->grf_line);
  442.  
  443.         free (grf);
  444.     }
  445.     grf_tail = 0;
  446.     return 1;
  447. }
  448.  
  449. int
  450. gr_update (grent)
  451. struct    group    *grent;
  452. {
  453.     struct    gr_file_entry    *grf;
  454.     struct    group    *ngr;
  455.  
  456.     if (! isopen || open_modes == O_RDONLY) {
  457.         errno = EINVAL;
  458.         return 0;
  459.     }
  460.     for (grf = grf_head;grf != 0;grf = grf->grf_next) {
  461.         if (grf->grf_entry == 0)
  462.             continue;
  463.  
  464.         if (strcmp (grent->gr_name, grf->grf_entry->gr_name) != 0)
  465.             continue;
  466.  
  467.         if (! (ngr = gr_dup (grent)))
  468.             return 0;
  469.         else {
  470.             gr_free (grf->grf_entry);
  471.             *(grf->grf_entry) = *ngr;
  472.         }
  473.         grf->grf_changed = 1;
  474.         grf_cursor = grf;
  475.         return gr_changed = 1;
  476.     }
  477.     grf = (struct gr_file_entry *) malloc (sizeof *grf);
  478.     if (! (grf->grf_entry = gr_dup (grent)))
  479.         return 0;
  480.  
  481.     grf->grf_changed = 1;
  482.     grf->grf_next = 0;
  483.     grf->grf_line = 0;
  484.  
  485.     if (grf_tail)
  486.         grf_tail->grf_next = grf;
  487.  
  488.     if (! grf_head)
  489.         grf_head = grf;
  490.  
  491.     grf_tail = grf;
  492.  
  493.     return gr_changed = 1;
  494. }
  495.  
  496. int
  497. gr_remove (name)
  498. char    *name;
  499. {
  500.     struct    gr_file_entry    *grf;
  501.     struct    gr_file_entry    *ogrf;
  502.  
  503.     if (! isopen || open_modes == O_RDONLY) {
  504.         errno = EINVAL;
  505.         return 0;
  506.     }
  507.     for (ogrf = 0, grf = grf_head;grf != 0;
  508.             ogrf = grf, grf = grf->grf_next) {
  509.         if (! grf->grf_entry)
  510.             continue;
  511.  
  512.         if (strcmp (name, grf->grf_entry->gr_name) != 0)
  513.             continue;
  514.  
  515.         if (grf == grf_cursor)
  516.             grf_cursor = ogrf;
  517.  
  518.         if (ogrf != 0)
  519.             ogrf->grf_next = grf->grf_next;
  520.         else
  521.             grf_head = grf->grf_next;
  522.  
  523.         if (grf == grf_tail)
  524.             grf_tail = ogrf;
  525.  
  526.         return gr_changed = 1;
  527.     }
  528.     errno = ENOENT;
  529.     return 0;
  530. }
  531.  
  532. struct group *
  533. gr_locate (name)
  534. char    *name;
  535. {
  536.     struct    gr_file_entry    *grf;
  537.  
  538.     if (! isopen) {
  539.         errno = EINVAL;
  540.         return 0;
  541.     }
  542.     for (grf = grf_head;grf != 0;grf = grf->grf_next) {
  543.         if (grf->grf_entry == 0)
  544.             continue;
  545.  
  546.         if (strcmp (name, grf->grf_entry->gr_name) == 0) {
  547.             grf_cursor = grf;
  548.             return grf->grf_entry;
  549.         }
  550.     }
  551.     errno = ENOENT;
  552.     return 0;
  553. }
  554.  
  555. int
  556. gr_rewind ()
  557. {
  558.     if (! isopen) {
  559.         errno = EINVAL;
  560.         return 0;
  561.     }
  562.     grf_cursor = 0;
  563.     return 1;
  564. }
  565.  
  566. struct group *
  567. gr_next ()
  568. {
  569.     if (! isopen) {
  570.         errno = EINVAL;
  571.         return 0;
  572.     }
  573.     if (grf_cursor == 0)
  574.         grf_cursor = grf_head;
  575.     else
  576.         grf_cursor = grf_cursor->grf_next;
  577.  
  578.     while (grf_cursor) {
  579.         if (grf_cursor->grf_entry)
  580.             return grf_cursor->grf_entry;
  581.  
  582.         grf_cursor = grf_cursor->grf_next;
  583.     }
  584.     return 0;
  585. }
  586.