1/*- 2 * Copyright (c) 2009 Advanced Computing Technologies LLC 3 * Written by: John H. Baldwin <jhb@FreeBSD.org> 4 * All rights reserved. 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 * 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/* 32 * Regression tests for the closefrom(2) system call. 33 */ 34 35#include <sys/param.h> 36#include <sys/mman.h> 37#include <sys/user.h> 38#include <sys/wait.h> 39#include <errno.h> 40#include <fcntl.h> 41#include <libutil.h> 42#include <stdarg.h> 43#include <stdio.h> 44#include <stdlib.h> 45#include <string.h> 46#include <unistd.h> 47 48struct shared_info { 49 int failed; 50 char tag[64]; 51 char message[0]; 52}; 53 54static int test = 1; 55 56static void 57ok(const char *descr) 58{ 59 60 printf("ok %d - %s\n", test, descr); 61 test++; 62} 63 64static void 65fail(const char *descr, const char *fmt, ...) 66{ 67 va_list ap; 68 69 printf("not ok %d - %s", test, descr); 70 test++; 71 if (fmt) { 72 va_start(ap, fmt); 73 printf(" # "); 74 vprintf(fmt, ap); 75 va_end(ap); 76 } 77 printf("\n"); 78 exit(1); 79} 80 81#define fail_err(descr) fail((descr), "%s", strerror(errno)) 82 83static void 84cok(struct shared_info *info, const char *descr) 85{ 86 87 info->failed = 0; 88 strlcpy(info->tag, descr, sizeof(info->tag)); 89 exit(0); 90} 91 92static void 93cfail(struct shared_info *info, const char *descr, const char *fmt, ...) 94{ 95 va_list ap; 96 97 info->failed = 1; 98 strlcpy(info->tag, descr, sizeof(info->tag)); 99 if (fmt) { 100 va_start(ap, fmt); 101 vsprintf(info->message, fmt, ap); 102 va_end(ap); 103 } 104 exit(0); 105} 106 107#define cfail_err(info, descr) cfail((info), (descr), "%s", strerror(errno)) 108 109/* 110 * Use kinfo_getfile() to fetch the list of file descriptors and figure out 111 * the highest open file descriptor. 112 */ 113static int 114highest_fd(void) 115{ 116 struct kinfo_file *kif; 117 int cnt, i, highest; 118 119 kif = kinfo_getfile(getpid(), &cnt); 120 if (kif == NULL) 121 fail_err("kinfo_getfile"); 122 highest = INT_MIN; 123 for (i = 0; i < cnt; i++) 124 if (kif[i].kf_fd > highest) 125 highest = kif[i].kf_fd; 126 free(kif); 127 return (highest); 128} 129 130static int 131devnull(void) 132{ 133 int fd; 134 135 fd = open("/dev/null", O_RDONLY); 136 if (fd < 0) 137 fail_err("open(\"/dev/null\")"); 138 return (fd); 139} 140 141int 142main(int __unused argc, char __unused *argv[]) 143{ 144 struct shared_info *info; 145 pid_t pid; 146 int fd, i; 147 148 printf("1..15\n"); 149 150 /* We better start up with fd's 0, 1, and 2 open. */ 151 fd = devnull(); 152 if (fd != 3) 153 fail("open", "bad descriptor %d", fd); 154 ok("open"); 155 156 /* Make sure highest_fd() works. */ 157 fd = highest_fd(); 158 if (fd != 3) 159 fail("highest_fd", "bad descriptor %d", fd); 160 ok("highest_fd"); 161 162 /* Try to use closefrom() for just closing fd 3. */ 163 closefrom(3); 164 fd = highest_fd(); 165 if (fd != 2) 166 fail("closefrom", "highest fd %d", fd); 167 ok("closefrom"); 168 169 /* Eat up 16 descriptors. */ 170 for (i = 0; i < 16; i++) 171 (void)devnull(); 172 fd = highest_fd(); 173 if (fd != 18) 174 fail("open 16", "highest fd %d", fd); 175 ok("open 16"); 176 177 /* Close half of them. */ 178 closefrom(11); 179 fd = highest_fd(); 180 if (fd != 10) 181 fail("closefrom", "highest fd %d", fd); 182 ok("closefrom"); 183 184 /* Explicitly close descriptors 6 and 8 to create holes. */ 185 if (close(6) < 0 || close(8) < 0) 186 fail_err("close2 "); 187 ok("close 2"); 188 189 /* Verify that close on 6 and 8 fails with EBADF. */ 190 if (close(6) == 0) 191 fail("close(6)", "did not fail"); 192 if (errno != EBADF) 193 fail_err("close(6)"); 194 ok("close(6)"); 195 if (close(8) == 0) 196 fail("close(8)", "did not fail"); 197 if (errno != EBADF) 198 fail_err("close(8)"); 199 ok("close(8)"); 200 201 /* Close from 4 on. */ 202 closefrom(4); 203 fd = highest_fd(); 204 if (fd != 3) 205 fail("closefrom", "highest fd %d", fd); 206 ok("closefrom"); 207 208 /* Allocate a small SHM region for IPC with our child. */ 209 info = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_ANON | 210 MAP_SHARED, -1, 0); 211 if (info == MAP_FAILED) 212 fail_err("mmap"); 213 ok("mmap"); 214 215 /* Fork a child process to test closefrom(0). */ 216 pid = fork(); 217 if (pid < 0) 218 fail_err("fork"); 219 if (pid == 0) { 220 /* Child. */ 221 closefrom(0); 222 fd = highest_fd(); 223 if (fd >= 0) 224 cfail(info, "closefrom(0)", "highest fd %d", fd); 225 cok(info, "closefrom(0)"); 226 } 227 if (wait(NULL) < 0) 228 fail_err("wait"); 229 if (info->failed) 230 fail(info->tag, "%s", info->message); 231 ok(info->tag); 232 233 /* Fork a child process to test closefrom(-1). */ 234 pid = fork(); 235 if (pid < 0) 236 fail_err("fork"); 237 if (pid == 0) { 238 /* Child. */ 239 closefrom(-1); 240 fd = highest_fd(); 241 if (fd >= 0) 242 cfail(info, "closefrom(-1)", "highest fd %d", fd); 243 cok(info, "closefrom(-1)"); 244 } 245 if (wait(NULL) < 0) 246 fail_err("wait"); 247 if (info->failed) 248 fail(info->tag, "%s", info->message); 249 ok(info->tag); 250 251 /* Dup stdout to 6. */ 252 if (dup2(1, 6) < 0) 253 fail_err("dup2"); 254 fd = highest_fd(); 255 if (fd != 6) 256 fail("dup2", "highest fd %d", fd); 257 ok("dup2"); 258 259 /* Do a closefrom() starting in a hole. */ 260 closefrom(4); 261 fd = highest_fd(); 262 if (fd != 3) 263 fail("closefrom", "highest fd %d", fd); 264 ok("closefrom"); 265 266 /* Do a closefrom() beyond our highest open fd. */ 267 closefrom(32); 268 fd = highest_fd(); 269 if (fd != 3) 270 fail("closefrom", "highest fd %d", fd); 271 ok("closefrom"); 272 273 return (0); 274} 275