From owner-svn-src-head@FreeBSD.ORG Thu Nov 12 01:37:03 2009 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 527E2106566C; Thu, 12 Nov 2009 01:37:03 +0000 (UTC) (envelope-from des@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 40A958FC1D; Thu, 12 Nov 2009 01:37:03 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id nAC1b3oQ014022; Thu, 12 Nov 2009 01:37:03 GMT (envelope-from des@svn.freebsd.org) Received: (from des@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id nAC1b3wX014020; Thu, 12 Nov 2009 01:37:03 GMT (envelope-from des@svn.freebsd.org) Message-Id: <200911120137.nAC1b3wX014020@svn.freebsd.org> From: Dag-Erling Smorgrav Date: Thu, 12 Nov 2009 01:37:03 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r199213 - head/tools/regression/lib/libutil X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 12 Nov 2009 01:37:03 -0000 Author: des Date: Thu Nov 12 01:37:02 2009 New Revision: 199213 URL: http://svn.freebsd.org/changeset/base/199213 Log: Test cases for pidfile(3) - including two designed to catch issues arising from the incorrect use of fcntl(2) instead of flock(2). Added: head/tools/regression/lib/libutil/test-pidfile.c (contents, props changed) Added: head/tools/regression/lib/libutil/test-pidfile.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/tools/regression/lib/libutil/test-pidfile.c Thu Nov 12 01:37:02 2009 (r199213) @@ -0,0 +1,280 @@ +/*- + * Copyright (c) 2007-2009 Dag-Erling Coïdan Smørgrav + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * We need a signal handler so kill(2) will interrupt our child's + * select(2) instead of killing it. + */ +static void +signal_handler(int sig) +{ + (void)sig; +} + +/* + * Test that pidfile_open() can create a pidfile and that pidfile_write() + * can write to it. + */ +static const char * +test_pidfile_uncontested(void) +{ + const char *fn = "test_pidfile_uncontested"; + struct pidfh *pf; + pid_t other = 0; + + unlink(fn); + pf = pidfile_open(fn, 0600, &other); + if (pf == NULL && other != 0) + return ("pidfile exists and is locked"); + if (pf == NULL) + return (strerror(errno)); + if (pidfile_write(pf) != 0) { + pidfile_close(pf); + unlink(fn); + return ("failed to write PID"); + } + pidfile_close(pf); + unlink(fn); + return (NULL); +} + +/* + * Test that pidfile_open() locks against self. + */ +static const char * +test_pidfile_self(void) +{ + const char *fn = "test_pidfile_self"; + struct pidfh *pf1, *pf2; + pid_t other = 0; + int serrno; + + unlink(fn); + pf1 = pidfile_open(fn, 0600, &other); + if (pf1 == NULL && other != 0) + return ("pidfile exists and is locked"); + if (pf1 == NULL) + return (strerror(errno)); + if (pidfile_write(pf1) != 0) { + serrno = errno; + pidfile_close(pf1); + unlink(fn); + return (strerror(serrno)); + } + // second open should fail + pf2 = pidfile_open(fn, 0600, &other); + if (pf2 != NULL) { + pidfile_close(pf1); + pidfile_close(pf2); + unlink(fn); + return ("managed to opened pidfile twice"); + } + if (other != getpid()) { + pidfile_close(pf1); + unlink(fn); + return ("pidfile contained wrong PID"); + } + pidfile_close(pf1); + unlink(fn); + return (NULL); +} + +/* + * Common code for test_pidfile_{contested,inherited}. + */ +static const char * +common_test_pidfile_child(const char *fn, int parent_open) +{ + struct pidfh *pf = NULL; + pid_t other = 0, pid = 0; + int fd[2], serrno, status; + char ch; + + unlink(fn); + if (pipe(fd) != 0) + return (strerror(errno)); + + if (parent_open) { + pf = pidfile_open(fn, 0600, &other); + if (pf == NULL && other != 0) + return ("pidfile exists and is locked"); + if (pf == NULL) + return (strerror(errno)); + } + + pid = fork(); + if (pid == -1) + return (strerror(errno)); + if (pid == 0) { + // child + close(fd[0]); + signal(SIGINT, signal_handler); + if (!parent_open) { + pf = pidfile_open(fn, 0600, &other); + if (pf == NULL && other != 0) + return ("pidfile exists and is locked"); + if (pf == NULL) + return (strerror(errno)); + } + if (pidfile_write(pf) != 0) { + serrno = errno; + pidfile_close(pf); + unlink(fn); + return (strerror(serrno)); + } + if (pf == NULL) + _exit(1); + if (pidfile_write(pf) != 0) + _exit(1); + if (write(fd[1], "*", 1) != 1) + _exit(1); + select(0, 0, 0, 0, 0); + _exit(0); + } + // parent + close(fd[1]); + if (pf) + pidfile_close(pf); + + // wait for the child to signal us + if (read(fd[0], &ch, 1) != 1) { + serrno = errno; + unlink(fn); + kill(pid, SIGTERM); + errno = serrno; + return (strerror(errno)); + } + + // We shouldn't be able to lock the same pidfile as our child + pf = pidfile_open(fn, 0600, &other); + if (pf != NULL) { + pidfile_close(pf); + unlink(fn); + return ("managed to lock contested pidfile"); + } + + // Failed to lock, but not because it was contested + if (other == 0) { + unlink(fn); + return (strerror(errno)); + } + + // Locked by the wrong process + if (other != pid) { + unlink(fn); + return ("pidfile contained wrong PID"); + } + + // check our child's fate + if (pf) + pidfile_close(pf); + unlink(fn); + if (kill(pid, SIGINT) != 0) + return (strerror(errno)); + if (waitpid(pid, &status, 0) == -1) + return (strerror(errno)); + if (WIFSIGNALED(status)) + return ("child caught signal"); + if (WEXITSTATUS(status) != 0) + return ("child returned non-zero status"); + + // success + return (NULL); +} + +/* + * Test that pidfile_open() fails when attempting to open a pidfile that + * is already locked, and that it returns the correct PID. + */ +static const char * +test_pidfile_contested(void) +{ + const char *fn = "test_pidfile_contested"; + const char *result; + + result = common_test_pidfile_child(fn, 0); + return (result); +} + +/* + * Test that the pidfile lock is inherited. + */ +static const char * +test_pidfile_inherited(void) +{ + const char *fn = "test_pidfile_inherited"; + const char *result; + + result = common_test_pidfile_child(fn, 1); + return (result); +} + +static struct test { + const char *name; + const char *(*func)(void); +} t[] = { + { "pidfile_uncontested", test_pidfile_uncontested }, + { "pidfile_self", test_pidfile_self }, + { "pidfile_contested", test_pidfile_contested }, + { "pidfile_inherited", test_pidfile_inherited }, +}; + +int +main(void) +{ + const char *result; + int i, nt; + + nt = sizeof(t) / sizeof(*t); + printf("1..%d\n", nt); + for (i = 0; i < nt; ++i) { + if ((result = t[i].func()) != NULL) + printf("not ok %d - %s # %s\n", i + 1, + t[i].name, result); + else + printf("ok %d - %s\n", i + 1, + t[i].name); + } + exit(0); +}