rev |
line source |
yuuji@0
|
1 /* ========================================================================
|
yuuji@0
|
2 * Copyright 1988-2008 University of Washington
|
yuuji@0
|
3 *
|
yuuji@0
|
4 * Licensed under the Apache License, Version 2.0 (the "License");
|
yuuji@0
|
5 * you may not use this file except in compliance with the License.
|
yuuji@0
|
6 * You may obtain a copy of the License at
|
yuuji@0
|
7 *
|
yuuji@0
|
8 * http://www.apache.org/licenses/LICENSE-2.0
|
yuuji@0
|
9 *
|
yuuji@0
|
10 *
|
yuuji@0
|
11 * ========================================================================
|
yuuji@0
|
12 */
|
yuuji@0
|
13
|
yuuji@0
|
14 /*
|
yuuji@0
|
15 * Program: Standalone Mailbox Lock program
|
yuuji@0
|
16 *
|
yuuji@0
|
17 * Author: Mark Crispin
|
yuuji@0
|
18 * Networks and Distributed Computing
|
yuuji@0
|
19 * Computing & Communications
|
yuuji@0
|
20 * University of Washington
|
yuuji@0
|
21 * Administration Building, AG-44
|
yuuji@0
|
22 * Seattle, WA 98195
|
yuuji@0
|
23 * Internet: MRC@CAC.Washington.EDU
|
yuuji@0
|
24 *
|
yuuji@0
|
25 * Date: 8 February 1999
|
yuuji@0
|
26 * Last Edited: 3 March 2008
|
yuuji@0
|
27 */
|
yuuji@0
|
28
|
yuuji@0
|
29 #include <errno.h>
|
yuuji@0
|
30 #include <fcntl.h>
|
yuuji@0
|
31 #include <stdio.h>
|
yuuji@0
|
32 #include <sysexits.h>
|
yuuji@0
|
33 #include <syslog.h>
|
yuuji@0
|
34 #include <grp.h>
|
yuuji@0
|
35 #include <sys/types.h>
|
yuuji@0
|
36 #include <sys/file.h>
|
yuuji@0
|
37 #include <sys/stat.h>
|
yuuji@0
|
38 #include <sys/param.h>
|
yuuji@0
|
39 #include <stdlib.h>
|
yuuji@0
|
40 #include <netdb.h>
|
yuuji@0
|
41 #include <ctype.h>
|
yuuji@0
|
42 #include <string.h>
|
yuuji@0
|
43
|
yuuji@0
|
44 #define LOCKTIMEOUT 5 /* lock timeout in minutes */
|
yuuji@0
|
45 #define LOCKPROTECTION 0664
|
yuuji@0
|
46
|
yuuji@0
|
47 #ifndef MAXHOSTNAMELEN /* Solaris still sucks */
|
yuuji@0
|
48 #define MAXHOSTNAMELEN 256
|
yuuji@0
|
49 #endif
|
yuuji@0
|
50
|
yuuji@0
|
51 /* Fatal error
|
yuuji@0
|
52 * Accepts: Message string
|
yuuji@0
|
53 * exit code
|
yuuji@0
|
54 * Returns: code
|
yuuji@0
|
55 */
|
yuuji@0
|
56
|
yuuji@0
|
57 int die (char *msg,int code)
|
yuuji@0
|
58 {
|
yuuji@0
|
59 syslog (LOG_NOTICE,"(%u) %s",code,msg);
|
yuuji@0
|
60 write (1,"?",1); /* indicate "impossible" failure */
|
yuuji@0
|
61 return code;
|
yuuji@0
|
62 }
|
yuuji@0
|
63
|
yuuji@0
|
64
|
yuuji@0
|
65 int main (int argc,char *argv[])
|
yuuji@0
|
66 {
|
yuuji@0
|
67 int ld,i;
|
yuuji@0
|
68 int tries = LOCKTIMEOUT * 60 - 1;
|
yuuji@0
|
69 char *s,*dir,*file,*lock,*hitch,tmp[1024];
|
yuuji@0
|
70 size_t dlen,len;
|
yuuji@0
|
71 struct stat sb,fsb;
|
yuuji@0
|
72 struct group *grp = getgrnam ("mail");
|
yuuji@0
|
73 /* get syslog */
|
yuuji@0
|
74 openlog (argv[0],LOG_PID,LOG_MAIL);
|
yuuji@0
|
75 if (!grp || (grp->gr_gid != getegid ()))
|
yuuji@0
|
76 return die ("not setgid mail",EX_USAGE);
|
yuuji@0
|
77 if (argc != 3) return die ("invalid arguments",EX_USAGE);
|
yuuji@0
|
78 for (s = argv[1]; *s; s++)
|
yuuji@0
|
79 if (!isdigit (*s)) return die ("invalid fd",EX_USAGE);
|
yuuji@0
|
80 /* find directory */
|
yuuji@0
|
81 if ((*argv[2] != '/') || !(file = strrchr (argv[2],'/')) || !file[1])
|
yuuji@0
|
82 return die ("invalid path",EX_USAGE);
|
yuuji@0
|
83 /* calculate lengths of directory and file */
|
yuuji@0
|
84 if (!(dlen = file - argv[2])) dlen = 1;
|
yuuji@0
|
85 len = strlen (++file);
|
yuuji@0
|
86 /* make buffers */
|
yuuji@0
|
87 dir = (char *) malloc (dlen + 1);
|
yuuji@0
|
88 lock = (char *) malloc (len + 6);
|
yuuji@0
|
89 hitch = (char *) malloc (len + 6 + 40 + MAXHOSTNAMELEN);
|
yuuji@0
|
90 if (!dir || !lock || !hitch) return die ("malloc failure",errno);
|
yuuji@0
|
91 strncpy (dir,argv[2],dlen); /* connect to desired directory */
|
yuuji@0
|
92 dir[dlen] = '\0';
|
yuuji@0
|
93 printf ("dir=%s, file=%s\n",dir,file);
|
yuuji@0
|
94 chdir (dir);
|
yuuji@0
|
95 /* get device/inode of file descriptor */
|
yuuji@0
|
96 if (fstat (atoi (argv[1]),&fsb)) return die ("fstat failure",errno);
|
yuuji@0
|
97 /* better be a regular file */
|
yuuji@0
|
98 if ((fsb.st_mode & S_IFMT) != S_IFREG)
|
yuuji@0
|
99 return die ("fd not regular file",EX_USAGE);
|
yuuji@0
|
100 /* now get device/inode of file */
|
yuuji@0
|
101 if (lstat (file,&sb)) return die ("lstat failure",errno);
|
yuuji@0
|
102 /* does it match? */
|
yuuji@0
|
103 if ((sb.st_mode & S_IFMT) != S_IFREG)
|
yuuji@0
|
104 return die ("name not regular file",EX_USAGE);
|
yuuji@0
|
105 if ((sb.st_dev != fsb.st_dev) || (sb.st_ino != fsb.st_ino))
|
yuuji@0
|
106 return die ("fd and name different",EX_USAGE);
|
yuuji@0
|
107 /* build lock filename */
|
yuuji@0
|
108 sprintf (lock,"%s.lock",file);
|
yuuji@0
|
109 if (!lstat (lock,&sb) && ((sb.st_mode & S_IFMT) != S_IFREG))
|
yuuji@0
|
110 return die ("existing lock not regular file",EX_NOPERM);
|
yuuji@0
|
111
|
yuuji@0
|
112 do { /* until OK or out of tries */
|
yuuji@0
|
113 if (!stat (lock,&sb) && (time (0) > (sb.st_ctime + LOCKTIMEOUT * 60)))
|
yuuji@0
|
114 unlink (lock); /* time out lock if enough time has passed */
|
yuuji@0
|
115 /* SUN-OS had an NFS
|
yuuji@0
|
116 * As kludgy as an albatross;
|
yuuji@0
|
117 * And everywhere that it was installed,
|
yuuji@0
|
118 * It was a total loss.
|
yuuji@0
|
119 * -- MRC 9/25/91
|
yuuji@0
|
120 */
|
yuuji@0
|
121 /* build hitching post file name */
|
yuuji@0
|
122 sprintf (hitch,"%s.%lu.%lu.",lock,(unsigned long) time (0),
|
yuuji@0
|
123 (unsigned long) getpid ());
|
yuuji@0
|
124 len = strlen (hitch); /* append local host name */
|
yuuji@0
|
125 gethostname (hitch + len,MAXHOSTNAMELEN);
|
yuuji@0
|
126 /* try to get hitching-post file */
|
yuuji@0
|
127 if ((ld = open (hitch,O_WRONLY|O_CREAT|O_EXCL,LOCKPROTECTION)) >= 0) {
|
yuuji@0
|
128 /* make sure others can break the lock */
|
yuuji@0
|
129 chmod (hitch,LOCKPROTECTION);
|
yuuji@0
|
130 /* get device/inode of hitch file */
|
yuuji@0
|
131 if (fstat (ld,&fsb)) return die ("hitch fstat failure",errno);
|
yuuji@0
|
132 close (ld); /* close the hitching-post */
|
yuuji@0
|
133 /* Note: link() may return an error even if it actually succeeded. So we
|
yuuji@0
|
134 * always check for success via the link count, and ignore the error if
|
yuuji@0
|
135 * the link count is right.
|
yuuji@0
|
136 */
|
yuuji@0
|
137 /* tie hitching-post to lock */
|
yuuji@0
|
138 i = link (hitch,lock) ? errno : 0;
|
yuuji@0
|
139 /* success if link count now 2 */
|
yuuji@0
|
140 if (stat (hitch,&sb) || (sb.st_nlink != 2) ||
|
yuuji@0
|
141 (fsb.st_dev != sb.st_dev) || (fsb.st_ino != sb.st_ino)) {
|
yuuji@0
|
142 ld = -1; /* failed to hitch */
|
yuuji@0
|
143 if (i == EPERM) { /* was it because links not allowed? */
|
yuuji@0
|
144 /* Probably a FAT filesystem on Linux. It can't be NFS, so try
|
yuuji@0
|
145 * creating the lock file directly.
|
yuuji@0
|
146 */
|
yuuji@0
|
147 if ((ld = open (lock,O_WRONLY|O_CREAT|O_EXCL,LOCKPROTECTION)) >= 0) {
|
yuuji@0
|
148 /* get device/inode of lock file */
|
yuuji@0
|
149 if (fstat (ld,&fsb)) return die ("lock fstat failure",errno);
|
yuuji@0
|
150 close (ld); /* close the file */
|
yuuji@0
|
151 }
|
yuuji@0
|
152 /* give up immediately if protection failure */
|
yuuji@0
|
153 else if (errno != EEXIST) tries = 0;
|
yuuji@0
|
154 }
|
yuuji@0
|
155 }
|
yuuji@0
|
156 unlink (hitch); /* flush hitching post */
|
yuuji@0
|
157 }
|
yuuji@0
|
158 /* give up immediately if protection failure */
|
yuuji@0
|
159 else if (errno == EACCES) tries = 0;
|
yuuji@0
|
160 if (ld < 0) { /* lock failed */
|
yuuji@0
|
161 if (tries--) sleep (1); /* sleep 1 second and try again */
|
yuuji@0
|
162 else {
|
yuuji@0
|
163 write (1,"-",1); /* hard failure */
|
yuuji@0
|
164 return EX_CANTCREAT;
|
yuuji@0
|
165 }
|
yuuji@0
|
166 }
|
yuuji@0
|
167 } while (ld < 0);
|
yuuji@0
|
168 write (1,"+",1); /* indicate that all is well */
|
yuuji@0
|
169 read (0,tmp,1); /* read continue signal from parent */
|
yuuji@0
|
170 /* flush the lock file */
|
yuuji@0
|
171 if (!stat (lock,&sb) && (fsb.st_dev == sb.st_dev) &&
|
yuuji@0
|
172 (fsb.st_ino == sb.st_ino)) unlink (lock);
|
yuuji@0
|
173 else syslog (LOG_NOTICE,"lock file %s/%s changed dev/inode",dir,lock);
|
yuuji@0
|
174 return EX_OK;
|
yuuji@0
|
175 }
|