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