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