home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume38 / shadow / part07 / groupio.c next >
C/C++ Source or Header  |  1993-08-14  |  11KB  |  596 lines

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