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