1 /*- 2 * Copyright (c) 2007-2009 Dag-Erling Coïdan Smørgrav 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer 10 * in this position and unchanged. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include <sys/param.h> 32 #include <sys/wait.h> 33 34 #include <fcntl.h> 35 #include <errno.h> 36 #include <signal.h> 37 #include <stdint.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <unistd.h> 42 43 #include <libutil.h> 44 45 /* 46 * We need a signal handler so kill(2) will interrupt our child's 47 * select(2) instead of killing it. 48 */ 49 static void 50 signal_handler(int sig) 51 { 52 (void)sig; 53 } 54 55 /* 56 * Test that pidfile_open() can create a pidfile and that pidfile_write() 57 * can write to it. 58 */ 59 static const char * 60 test_pidfile_uncontested(void) 61 { 62 const char *fn = "test_pidfile_uncontested"; 63 struct pidfh *pf; 64 pid_t other = 0; 65 66 unlink(fn); 67 pf = pidfile_open(fn, 0600, &other); 68 if (pf == NULL && other != 0) 69 return ("pidfile exists and is locked"); 70 if (pf == NULL) 71 return (strerror(errno)); 72 if (pidfile_write(pf) != 0) { 73 pidfile_close(pf); 74 unlink(fn); 75 return ("failed to write PID"); 76 } 77 pidfile_close(pf); 78 unlink(fn); 79 return (NULL); 80 } 81 82 /* 83 * Test that pidfile_open() locks against self. 84 */ 85 static const char * 86 test_pidfile_self(void) 87 { 88 const char *fn = "test_pidfile_self"; 89 struct pidfh *pf1, *pf2; 90 pid_t other = 0; 91 int serrno; 92 93 unlink(fn); 94 pf1 = pidfile_open(fn, 0600, &other); 95 if (pf1 == NULL && other != 0) 96 return ("pidfile exists and is locked"); 97 if (pf1 == NULL) 98 return (strerror(errno)); 99 if (pidfile_write(pf1) != 0) { 100 serrno = errno; 101 pidfile_close(pf1); 102 unlink(fn); 103 return (strerror(serrno)); 104 } 105 // second open should fail 106 pf2 = pidfile_open(fn, 0600, &other); 107 if (pf2 != NULL) { 108 pidfile_close(pf1); 109 pidfile_close(pf2); 110 unlink(fn); 111 return ("managed to opened pidfile twice"); 112 } 113 if (other != getpid()) { 114 pidfile_close(pf1); 115 unlink(fn); 116 return ("pidfile contained wrong PID"); 117 } 118 pidfile_close(pf1); 119 unlink(fn); 120 return (NULL); 121 } 122 123 /* 124 * Common code for test_pidfile_{contested,inherited}. 125 */ 126 static const char * 127 common_test_pidfile_child(const char *fn, int parent_open) 128 { 129 struct pidfh *pf = NULL; 130 pid_t other = 0, pid = 0; 131 int fd[2], serrno, status; 132 char ch; 133 134 unlink(fn); 135 if (pipe(fd) != 0) 136 return (strerror(errno)); 137 138 if (parent_open) { 139 pf = pidfile_open(fn, 0600, &other); 140 if (pf == NULL && other != 0) 141 return ("pidfile exists and is locked"); 142 if (pf == NULL) 143 return (strerror(errno)); 144 } 145 146 pid = fork(); 147 if (pid == -1) 148 return (strerror(errno)); 149 if (pid == 0) { 150 // child 151 close(fd[0]); 152 signal(SIGINT, signal_handler); 153 if (!parent_open) { 154 pf = pidfile_open(fn, 0600, &other); 155 if (pf == NULL && other != 0) 156 return ("pidfile exists and is locked"); 157 if (pf == NULL) 158 return (strerror(errno)); 159 } 160 if (pidfile_write(pf) != 0) { 161 serrno = errno; 162 pidfile_close(pf); 163 unlink(fn); 164 return (strerror(serrno)); 165 } 166 if (pf == NULL) 167 _exit(1); 168 if (pidfile_write(pf) != 0) 169 _exit(1); 170 if (write(fd[1], "*", 1) != 1) 171 _exit(1); 172 select(0, 0, 0, 0, 0); 173 _exit(0); 174 } 175 // parent 176 close(fd[1]); 177 if (pf) 178 pidfile_close(pf); 179 180 // wait for the child to signal us 181 if (read(fd[0], &ch, 1) != 1) { 182 serrno = errno; 183 unlink(fn); 184 kill(pid, SIGTERM); 185 errno = serrno; 186 return (strerror(errno)); 187 } 188 189 // We shouldn't be able to lock the same pidfile as our child 190 pf = pidfile_open(fn, 0600, &other); 191 if (pf != NULL) { 192 pidfile_close(pf); 193 unlink(fn); 194 return ("managed to lock contested pidfile"); 195 } 196 197 // Failed to lock, but not because it was contested 198 if (other == 0) { 199 unlink(fn); 200 return (strerror(errno)); 201 } 202 203 // Locked by the wrong process 204 if (other != pid) { 205 unlink(fn); 206 return ("pidfile contained wrong PID"); 207 } 208 209 // check our child's fate 210 if (pf) 211 pidfile_close(pf); 212 unlink(fn); 213 if (kill(pid, SIGINT) != 0) 214 return (strerror(errno)); 215 if (waitpid(pid, &status, 0) == -1) 216 return (strerror(errno)); 217 if (WIFSIGNALED(status)) 218 return ("child caught signal"); 219 if (WEXITSTATUS(status) != 0) 220 return ("child returned non-zero status"); 221 222 // success 223 return (NULL); 224 } 225 226 /* 227 * Test that pidfile_open() fails when attempting to open a pidfile that 228 * is already locked, and that it returns the correct PID. 229 */ 230 static const char * 231 test_pidfile_contested(void) 232 { 233 const char *fn = "test_pidfile_contested"; 234 const char *result; 235 236 result = common_test_pidfile_child(fn, 0); 237 return (result); 238 } 239 240 /* 241 * Test that the pidfile lock is inherited. 242 */ 243 static const char * 244 test_pidfile_inherited(void) 245 { 246 const char *fn = "test_pidfile_inherited"; 247 const char *result; 248 249 result = common_test_pidfile_child(fn, 1); 250 return (result); 251 } 252 253 static struct test { 254 const char *name; 255 const char *(*func)(void); 256 } t[] = { 257 { "pidfile_uncontested", test_pidfile_uncontested }, 258 { "pidfile_self", test_pidfile_self }, 259 { "pidfile_contested", test_pidfile_contested }, 260 { "pidfile_inherited", test_pidfile_inherited }, 261 }; 262 263 int 264 main(void) 265 { 266 const char *result; 267 int i, nt; 268 269 nt = sizeof(t) / sizeof(*t); 270 printf("1..%d\n", nt); 271 for (i = 0; i < nt; ++i) { 272 if ((result = t[i].func()) != NULL) 273 printf("not ok %d - %s # %s\n", i + 1, 274 t[i].name, result); 275 else 276 printf("ok %d - %s\n", i + 1, 277 t[i].name); 278 } 279 exit(0); 280 } 281