Merge from vendor branch BINUTILS:
[dragonfly.git] / contrib / libpam / modules / pam_unix / lckpwdf.-c
1 /*
2  * This is a hack, but until libc and glibc both include this function
3  * by default (libc only includes it if nys is not being used, at the
4  * moment, and glibc doesn't appear to have it at all) we need to have
5  * it here, too.  :-(
6  *
7  * This should not become an official part of PAM.
8  *
9  * BEGIN_HACK
10  */
11
12 /*
13  * lckpwdf.c -- prevent simultaneous updates of password files
14  *
15  * Before modifying any of the password files, call lckpwdf().  It may block
16  * for up to 15 seconds trying to get the lock.  Return value is 0 on success
17  * or -1 on failure.  When you are done, call ulckpwdf() to release the lock.
18  * The lock is also released automatically when the process exits.  Only one
19  * process at a time may hold the lock.
20  *
21  * These functions are supposed to be conformant with AT&T SVID Issue 3.
22  *
23  * Written by Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>,
24  * public domain.
25  * $FreeBSD: src/contrib/libpam/modules/pam_unix/lckpwdf.-c,v 1.1.1.1.2.2 2001/06/11 15:28:30 markm Exp $
26  * $DragonFly: src/contrib/libpam/modules/pam_unix/Attic/lckpwdf.-c,v 1.2 2003/06/17 04:24:03 dillon Exp $
27  */
28
29 #include <fcntl.h>
30 #include <signal.h>
31
32 #define LOCKFILE "/etc/.pwd.lock"
33 #define TIMEOUT 15
34
35 static int lockfd = -1;
36
37 static int set_close_on_exec(int fd)
38 {
39         int flags = fcntl(fd, F_GETFD, 0);
40         if (flags == -1)
41                 return -1;
42         flags |= FD_CLOEXEC;
43         return fcntl(fd, F_SETFD, flags);
44 }
45
46 static int do_lock(int fd)
47 {
48         struct flock fl;
49
50         memset(&fl, 0, sizeof fl);
51         fl.l_type = F_WRLCK;
52         fl.l_whence = SEEK_SET;
53         return fcntl(fd, F_SETLKW, &fl);
54 }
55
56 static void alarm_catch(int sig)
57 {
58 /* does nothing, but fcntl F_SETLKW will fail with EINTR */
59 }
60
61 static int lckpwdf(void)
62 {
63         struct sigaction act, oldact;
64         sigset_t set, oldset;
65
66         if (lockfd != -1)
67                 return -1;
68
69         lockfd = open(LOCKFILE, O_CREAT | O_WRONLY, 0600);
70         if (lockfd == -1)
71                 return -1;
72         if (set_close_on_exec(lockfd) == -1)
73                 goto cleanup_fd;
74
75         memset(&act, 0, sizeof act);
76         act.sa_handler = alarm_catch;
77         act.sa_flags = 0;
78         sigfillset(&act.sa_mask);
79         if (sigaction(SIGALRM, &act, &oldact) == -1)
80                 goto cleanup_fd;
81
82         sigemptyset(&set);
83         sigaddset(&set, SIGALRM);
84         if (sigprocmask(SIG_UNBLOCK, &set, &oldset) == -1)
85                 goto cleanup_sig;
86
87         alarm(TIMEOUT);
88         if (do_lock(lockfd) == -1)
89                 goto cleanup_alarm;
90         alarm(0);
91         sigprocmask(SIG_SETMASK, &oldset, NULL);
92         sigaction(SIGALRM, &oldact, NULL);
93         return 0;
94
95       cleanup_alarm:
96         alarm(0);
97         sigprocmask(SIG_SETMASK, &oldset, NULL);
98       cleanup_sig:
99         sigaction(SIGALRM, &oldact, NULL);
100       cleanup_fd:
101         close(lockfd);
102         lockfd = -1;
103         return -1;
104 }
105
106 static int ulckpwdf(void)
107 {
108         unlink(LOCKFILE);
109         if (lockfd == -1)
110                 return -1;
111
112         if (close(lockfd) == -1) {
113                 lockfd = -1;
114                 return -1;
115         }
116         lockfd = -1;
117         return 0;
118 }
119 /* END_HACK */