1 /* $NetBSD: pidlock.c,v 1.16 2012/04/07 16:17:17 christos 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.16 2012/04/07 16:17:17 christos 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 #include <paths.h> 44 45 /* 46 * Create a lockfile. Return 0 when locked, -1 on error. 47 */ 48 int 49 pidlock(const char *lockfile, int flags, pid_t *locker, const char *info) 50 { 51 char tempfile[MAXPATHLEN]; 52 char hostname[MAXHOSTNAMELEN + 1]; 53 pid_t pid2 = -1; 54 struct stat st; 55 ssize_t n; 56 int f = -1, savee; 57 char s[256]; 58 char *p; 59 size_t len; 60 61 _DIAGASSERT(lockfile != NULL); 62 /* locker may be NULL */ 63 /* info may be NULL */ 64 65 66 if (gethostname(hostname, sizeof(hostname))) 67 return -1; 68 hostname[sizeof(hostname) - 1] = '\0'; 69 70 /* 71 * Build a path to the temporary file. 72 * We use the path with the PID and hostname appended. 73 * XXX This is not thread safe. 74 */ 75 if (snprintf(tempfile, sizeof(tempfile), "%s.%d.%s", lockfile, 76 (int) getpid(), hostname) >= (int)sizeof(tempfile)) { 77 errno = ENAMETOOLONG; 78 return -1; 79 } 80 81 /* Open it, write pid, hostname, info. */ 82 if ((f = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0600)) == -1) 83 goto out; 84 85 (void)snprintf(s, sizeof(s), "%10d\n", getpid()); /* pid */ 86 if (write(f, s, (size_t)11) != 11) 87 goto out; 88 89 if ((flags & PIDLOCK_USEHOSTNAME)) { /* hostname */ 90 len = strlen(hostname); 91 if ((size_t)write(f, hostname, len) != len 92 || write(f, "\n", (size_t)1) != 1) 93 goto out; 94 } 95 if (info) { /* info */ 96 if (!(flags & PIDLOCK_USEHOSTNAME)) { 97 /* write blank line because there's no hostname */ 98 if (write(f, "\n", (size_t)1) != 1) 99 goto out; 100 } 101 len = strlen(info); 102 if ((size_t)write(f, info, len) != len || 103 write(f, "\n", (size_t)1) != 1) 104 goto out; 105 } 106 (void)close(f); 107 f = -1; 108 109 /* Hard link the temporary file to the real lock file. */ 110 /* This is an atomic operation. */ 111 lockfailed: 112 while (link(tempfile, lockfile) == -1) { 113 if (errno != EEXIST) 114 goto out; 115 /* Find out who has this lockfile. */ 116 if ((f = open(lockfile, O_RDONLY, 0)) != -1) { 117 if ((n = read(f, s, (size_t)11)) == -1) 118 goto out; 119 if (n == 0) { 120 errno = EINVAL; 121 goto out; 122 } 123 pid2 = atoi(s); 124 if ((n = read(f, s, sizeof(s) - 2)) == -1) 125 goto out; 126 if (n == 0) 127 *s = '\0'; 128 s[sizeof(s) - 1] = '\0'; 129 if ((p = strchr(s, '\n')) != NULL) 130 *p = '\0'; 131 (void)close(f); 132 f = -1; 133 134 if ((flags & PIDLOCK_USEHOSTNAME) == 0 || 135 strcmp(s, hostname) == 0) { 136 if (kill(pid2, 0) == -1 && errno == ESRCH) { 137 /* process doesn't exist */ 138 (void)unlink(lockfile); 139 continue; 140 } 141 } 142 } 143 if (flags & PIDLOCK_NONBLOCK) { 144 if (locker) 145 *locker = pid2; 146 errno = EWOULDBLOCK; 147 goto out; 148 } else 149 sleep(5); 150 } 151 /* 152 * Check to see that we really were successful (in case we're 153 * using NFS) by making sure that something really is linked 154 * to our tempfile (reference count is two). 155 */ 156 if (stat(tempfile, &st) == -1) 157 goto out; 158 if (st.st_nlink != 2) 159 goto lockfailed; 160 161 (void)unlink(tempfile); 162 if (locker) 163 *locker = getpid(); /* return this process's PID on lock */ 164 errno = 0; 165 return 0; 166 out: 167 savee = errno; 168 if (f != -1) 169 (void)close(f); 170 (void)unlink(tempfile); 171 errno = savee; 172 return -1; 173 } 174 175 static int 176 checktty(const char *tty) 177 { 178 char ttyfile[MAXPATHLEN]; 179 struct stat sb; 180 181 (void)strlcpy(ttyfile, _PATH_DEV, sizeof(ttyfile)); 182 (void)strlcat(ttyfile, tty, sizeof(ttyfile)); 183 184 /* make sure the tty exists */ 185 if (stat(ttyfile, &sb) == -1) 186 return -1; 187 if (!S_ISCHR(sb.st_mode)) { 188 errno = EFTYPE; 189 return -1; 190 } 191 return 0; 192 } 193 194 #define LOCKPATH "/var/spool/lock/LCK.." 195 196 static char * 197 makelock(char *buf, size_t bufsiz, const char *tty) 198 { 199 (void)strlcpy(buf, LOCKPATH, bufsiz); 200 (void)strlcat(buf, tty, bufsiz); 201 return buf; 202 } 203 204 /*ARGSUSED*/ 205 int 206 ttylock(const char *tty, int flags, pid_t *locker) 207 { 208 char lockfile[MAXPATHLEN]; 209 210 _DIAGASSERT(tty != NULL); 211 212 if (checktty(tty) != 0) 213 return -1; 214 215 /* do the lock */ 216 return pidlock(makelock(lockfile, sizeof(lockfile), tty), 217 flags, locker, 0); 218 } 219 220 int 221 ttyunlock(const char *tty) 222 { 223 char lockfile[MAXPATHLEN]; 224 225 _DIAGASSERT(tty != NULL); 226 227 if (checktty(tty) != 0) 228 return -1; 229 230 /* remove the lock */ 231 return unlink(makelock(lockfile, sizeof(lockfile), tty)); 232 } 233