home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 2 / 2082 / pwio.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-12-28  |  9.7 KB  |  529 lines

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