1 /* $NetBSD: pidlock.c,v 1.8 1999/09/20 04:48:08 lukem Exp $ */ 2 3 /* 4 * Copyright 1996, 1997 by Curt Sampson <cjs@netbsd.org>. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND 13 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 15 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE 16 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 17 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 18 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 19 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 20 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 21 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 22 * SUCH DAMAGE. 23 */ 24 25 #include <sys/cdefs.h> 26 #if defined(LIBC_SCCS) && !defined(lint) 27 __RCSID("$NetBSD: pidlock.c,v 1.8 1999/09/20 04:48:08 lukem Exp $"); 28 #endif /* LIBC_SCCS and not lint */ 29 30 #include <sys/param.h> 31 #include <sys/stat.h> 32 #include <sys/types.h> 33 34 #include <assert.h> 35 #include <errno.h> 36 #include <fcntl.h> 37 #include <signal.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <unistd.h> 42 #include <util.h> 43 44 /* 45 * Create a lockfile. Return 0 when locked, -1 on error. 46 */ 47 int 48 pidlock(lockfile, flags, locker, info) 49 const char *lockfile; 50 int flags; 51 pid_t *locker; 52 const char *info; 53 { 54 char tempfile[MAXPATHLEN]; 55 char hostname[MAXHOSTNAMELEN + 1]; 56 pid_t pid2 = -1; 57 struct stat st; 58 int err; 59 int f; 60 char s[256]; 61 char *p; 62 63 _DIAGASSERT(lockfile != NULL); 64 /* locker may be NULL */ 65 /* info may be NULL */ 66 67 68 if (gethostname(hostname, sizeof(hostname))) 69 return -1; 70 hostname[sizeof(hostname) - 1] = '\0'; 71 72 /* 73 * Build a path to the temporary file. 74 * We use the path with the PID and hostname appended. 75 * XXX This is not thread safe. 76 */ 77 if (snprintf(tempfile, sizeof(tempfile), "%s.%d.%s", lockfile, 78 (int) getpid(), hostname) >= sizeof(tempfile)) { 79 errno = ENAMETOOLONG; 80 return -1; 81 } 82 83 /* Open it, write pid, hostname, info. */ 84 if ( (f = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0600)) == -1 ) { 85 err = errno; 86 unlink(tempfile); 87 errno = err; 88 return -1; 89 } 90 snprintf(s, sizeof(s), "%10d\n", getpid()); /* pid */ 91 if (write(f, s, 11) != 11) { 92 err = errno; 93 close(f); 94 unlink(tempfile); 95 errno = err; 96 return -1; 97 } 98 if ((flags & PIDLOCK_USEHOSTNAME)) { /* hostname */ 99 if ((write(f, hostname, strlen(hostname)) != strlen(hostname)) 100 || (write(f, "\n", 1) != 1)) { 101 err = errno; 102 close(f); 103 unlink(tempfile); 104 errno = err; 105 return -1; 106 } 107 } 108 if (info) { /* info */ 109 if (!(flags & PIDLOCK_USEHOSTNAME)) { 110 /* write blank line because there's no hostname */ 111 if (write(f, "\n", 1) != 1) { 112 err = errno; 113 close(f); 114 unlink(tempfile); 115 errno = err; 116 return -1; 117 } 118 } 119 if (write(f, info, strlen(info)) != strlen(info) || 120 (write(f, "\n", 1) != 1)) { 121 err = errno; 122 close(f); 123 unlink(tempfile); 124 errno = err; 125 return -1; 126 } 127 } 128 close(f); 129 130 /* Hard link the temporary file to the real lock file. */ 131 /* This is an atomic operation. */ 132 lockfailed: 133 while (link(tempfile, lockfile) != 0) { 134 if (errno != EEXIST) { 135 err = errno; 136 unlink(tempfile); 137 errno = err; 138 return -1; 139 } 140 /* Find out who has this lockfile. */ 141 if ((f = open(lockfile, O_RDONLY, 0)) != 0) { 142 read(f, s, 11); 143 pid2 = atoi(s); 144 read(f, s, sizeof(s)-2); 145 s[sizeof(s)-1] = '\0'; 146 if ((p=strchr(s, '\n')) != NULL) 147 *p = '\0'; 148 close(f); 149 150 if (!((flags & PIDLOCK_USEHOSTNAME) && 151 strcmp(s, hostname))) { 152 if ((kill(pid2, 0) != 0) && (errno == ESRCH)) { 153 /* process doesn't exist */ 154 unlink(lockfile); 155 continue; 156 } 157 } 158 } 159 if (flags & PIDLOCK_NONBLOCK) { 160 if (locker) 161 *locker = pid2; 162 unlink(tempfile); 163 errno = EWOULDBLOCK; 164 return -1; 165 } else 166 sleep(5); 167 } 168 /* 169 * Check to see that we really were successful (in case we're 170 * using NFS) by making sure that something really is linked 171 * to our tempfile (reference count is two). 172 */ 173 if (stat(tempfile, &st) != 0) { 174 err = errno; 175 /* 176 * We don't know if lockfile was really created by us, 177 * so we can't remove it. 178 */ 179 unlink(tempfile); 180 errno = err; 181 return -1; 182 } 183 if (st.st_nlink != 2) 184 goto lockfailed; 185 186 unlink(tempfile); 187 if (locker) 188 *locker = getpid(); /* return this process's PID on lock */ 189 errno = 0; 190 return 0; 191 } 192 193 #define LOCKPATH "/var/spool/lock/LCK.." 194 #define DEVPATH "/dev/" 195 196 /*ARGSUSED*/ 197 int 198 ttylock(tty, flags, locker) 199 const char *tty; 200 int flags; 201 pid_t *locker; 202 { 203 char lockfile[MAXPATHLEN]; 204 char ttyfile[MAXPATHLEN]; 205 struct stat sb; 206 207 _DIAGASSERT(tty != NULL); 208 /* locker is not used */ 209 210 /* make sure the tty exists */ 211 strcpy(ttyfile, DEVPATH); 212 strncat(ttyfile, tty, MAXPATHLEN-strlen(DEVPATH)); 213 if (stat(ttyfile, &sb)) { 214 errno = ENOENT; return -1; 215 } 216 if (!S_ISCHR(sb.st_mode)) { 217 errno = ENOENT; return -1; 218 } 219 220 /* do the lock */ 221 strcpy(lockfile, LOCKPATH); 222 strncat(lockfile, tty, MAXPATHLEN-strlen(LOCKPATH)); 223 return pidlock(lockfile, 0, 0, 0); 224 } 225 226 int 227 ttyunlock(tty) 228 const char *tty; 229 { 230 char lockfile[MAXPATHLEN]; 231 char ttyfile[MAXPATHLEN]; 232 struct stat sb; 233 234 _DIAGASSERT(tty != NULL); 235 236 /* make sure the tty exists */ 237 strcpy(ttyfile, DEVPATH); 238 strncat(ttyfile, tty, MAXPATHLEN-strlen(DEVPATH)); 239 if (stat(ttyfile, &sb)) { 240 errno = ENOENT; return -1; 241 } 242 if (!S_ISCHR(sb.st_mode)) { 243 errno = ENOENT; return -1; 244 } 245 246 /* undo the lock */ 247 strcpy(lockfile, LOCKPATH); 248 strncat(lockfile, tty, MAXPATHLEN-strlen(LOCKPATH)); 249 return unlink(lockfile); 250 } 251