home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 3 / 3348 / pwio.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-05-17  |  10.8 KB  |  578 lines

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