t_fileactions.c revision 313535
11553Srgrimes/* $NetBSD: t_fileactions.c,v 1.6 2017/01/10 22:36:29 christos Exp $ */ 21553Srgrimes 31553Srgrimes/*- 41553Srgrimes * Copyright (c) 2012 The NetBSD Foundation, Inc. 51553Srgrimes * All rights reserved. 61553Srgrimes * 71553Srgrimes * This code is derived from software contributed to The NetBSD Foundation 81553Srgrimes * by Charles Zhang <charles@NetBSD.org> and 91553Srgrimes * Martin Husemann <martin@NetBSD.org>. 101553Srgrimes * 111553Srgrimes * Redistribution and use in source and binary forms, with or without 121553Srgrimes * modification, are permitted provided that the following conditions 131553Srgrimes * are met: 141553Srgrimes * 1. Redistributions of source code must retain the above copyright 151553Srgrimes * notice, this list of conditions and the following disclaimer. 161553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 171553Srgrimes * notice, this list of conditions and the following disclaimer in the 181553Srgrimes * documentation and/or other materials provided with the distribution. 191553Srgrimes * 201553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 211553Srgrimes * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 221553Srgrimes * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 231553Srgrimes * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 241553Srgrimes * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 251553Srgrimes * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 261553Srgrimes * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 271553Srgrimes * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 281553Srgrimes * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2950479Speter * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 301553Srgrimes * POSSIBILITY OF SUCH DAMAGE. 31169507Swkoszek */ 321553Srgrimes 3379537Sru 341553Srgrimes#include <atf-c.h> 351553Srgrimes 361553Srgrimes#include <sys/wait.h> 371553Srgrimes#include <sys/stat.h> 3868965Sru 39169507Swkoszek#include <stdio.h> 4052653Smarcel#include <stdlib.h> 411566Srgrimes#include <string.h> 42169507Swkoszek#include <errno.h> 43169507Swkoszek#include <fcntl.h> 441553Srgrimes#include <spawn.h> 4599968Scharnier#include <unistd.h> 4669850Sben 4799968Scharnier 481553SrgrimesATF_TC(t_spawn_openmode); 491553Srgrimes 501553SrgrimesATF_TC_HEAD(t_spawn_openmode, tc) 511553Srgrimes{ 521553Srgrimes atf_tc_set_md_var(tc, "descr", 5329451Scharnier "Test the proper handling of 'mode' for 'open' fileactions"); 541553Srgrimes atf_tc_set_md_var(tc, "require.progs", "/bin/cat"); 551553Srgrimes} 5629451Scharnier 571553Srgrimesstatic off_t 581553Srgrimesfilesize(const char * restrict fname) 5929451Scharnier{ 601553Srgrimes struct stat st; 616631Sjkh int err; 6285536Sru 63148916Sobrien err = stat(fname, &st); 64148916Sobrien ATF_REQUIRE(err == 0); 65148916Sobrien return st.st_size; 66148916Sobrien} 67169507Swkoszek 68169507Swkoszek#define TESTFILE "./the_input_data" 69169507Swkoszek#define CHECKFILE "./the_output_data" 70169507Swkoszek#define TESTCONTENT "marry has a little lamb" 71169507Swkoszek 7252653Smarcelstatic void 7352653Smarcelmake_testfile(const char *restrict file) 7452653Smarcel{ 7557673Ssheldonh FILE *f; 7685536Sru size_t written; 7785536Sru 7885536Sru f = fopen(file, "w"); 7952653Smarcel ATF_REQUIRE(f != NULL); 8052653Smarcel written = fwrite(TESTCONTENT, 1, strlen(TESTCONTENT), f); 81209969Snwhitehorn fclose(f); 82209969Snwhitehorn ATF_REQUIRE(written == strlen(TESTCONTENT)); 83209969Snwhitehorn} 841566Srgrimes 8545579Sgrogstatic void 86169507Swkoszekempty_outfile(const char *restrict filename) 87169507Swkoszek{ 88169507Swkoszek FILE *f; 89233648Seadler 90169507Swkoszek f = fopen(filename, "w"); 91169507Swkoszek ATF_REQUIRE(f != NULL); 921553Srgrimes fclose(f); 9329451Scharnier} 941553Srgrimes 951553SrgrimesATF_TC_BODY(t_spawn_openmode, tc) 961553Srgrimes{ 9713107Sbde int status, err; 9813107Sbde pid_t pid; 9913107Sbde size_t insize, outsize; 10029451Scharnier char * const args[2] = { __UNCONST("cat"), NULL }; 10145427Sgrog posix_spawn_file_actions_t fa; 1021553Srgrimes 10356481Scharnier /* 1041553Srgrimes * try a "cat < testfile > checkfile" 1051553Srgrimes */ 1061553Srgrimes make_testfile(TESTFILE); 1071553Srgrimes unlink(CHECKFILE); 10899968Scharnier 10969850Sben posix_spawn_file_actions_init(&fa); 11099968Scharnier posix_spawn_file_actions_addopen(&fa, fileno(stdin), 1111553Srgrimes TESTFILE, O_RDONLY, 0); 1121553Srgrimes posix_spawn_file_actions_addopen(&fa, fileno(stdout), 11385536Sru CHECKFILE, O_WRONLY|O_CREAT, 0600); 11445579Sgrog err = posix_spawn(&pid, "/bin/cat", &fa, NULL, args, NULL); 11585536Sru posix_spawn_file_actions_destroy(&fa); 11668716Sru 11768716Sru ATF_REQUIRE(err == 0); 11899968Scharnier 11969850Sben /* ok, wait for the child to finish */ 12099968Scharnier waitpid(pid, &status, 0); 12185536Sru ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS); 12252653Smarcel 12352653Smarcel /* now check that input and output have the same size */ 12479755Sdd insize = filesize(TESTFILE); 12552653Smarcel outsize = filesize(CHECKFILE); 1261553Srgrimes ATF_REQUIRE(insize == strlen(TESTCONTENT)); 12729451Scharnier ATF_REQUIRE(insize == outsize); 1281553Srgrimes 1291566Srgrimes /* 1301553Srgrimes * try a "cat < testfile >> checkfile" 1311553Srgrimes */ 1321553Srgrimes make_testfile(TESTFILE); 1331553Srgrimes make_testfile(CHECKFILE); 1341553Srgrimes 1351553Srgrimes posix_spawn_file_actions_init(&fa); 1361553Srgrimes posix_spawn_file_actions_addopen(&fa, fileno(stdin), 13755605Speter TESTFILE, O_RDONLY, 0); 1381553Srgrimes posix_spawn_file_actions_addopen(&fa, fileno(stdout), 1391553Srgrimes CHECKFILE, O_WRONLY|O_APPEND, 0); 140101828Sru err = posix_spawn(&pid, "/bin/cat", &fa, NULL, args, NULL); 1411553Srgrimes posix_spawn_file_actions_destroy(&fa); 1421553Srgrimes 1431553Srgrimes ATF_REQUIRE(err == 0); 1441553Srgrimes 14599968Scharnier /* ok, wait for the child to finish */ 14669850Sben waitpid(pid, &status, 0); 14799968Scharnier ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS); 1481553Srgrimes 1491553Srgrimes /* now check that output is twice as long as input */ 150101828Sru insize = filesize(TESTFILE); 1511553Srgrimes outsize = filesize(CHECKFILE); 15229451Scharnier ATF_REQUIRE(insize == strlen(TESTCONTENT)); 1531553Srgrimes ATF_REQUIRE(insize*2 == outsize); 1541553Srgrimes 1551553Srgrimes /* 15645427Sgrog * try a "cat < testfile > checkfile" with input and output swapped 15781622Sru */ 15881622Sru make_testfile(TESTFILE); 159113749Sbrueffer empty_outfile(CHECKFILE); 16045579Sgrog 16168575Sru posix_spawn_file_actions_init(&fa); 16285536Sru posix_spawn_file_actions_addopen(&fa, fileno(stdout), 16385536Sru TESTFILE, O_RDONLY, 0); 16485536Sru posix_spawn_file_actions_addopen(&fa, fileno(stdin), 16585536Sru CHECKFILE, O_WRONLY, 0); 16681622Sru err = posix_spawn(&pid, "/bin/cat", &fa, NULL, args, NULL); 16781622Sru posix_spawn_file_actions_destroy(&fa); 16881622Sru 16979755Sdd ATF_REQUIRE(err == 0); 17050000Schris 17145579Sgrog /* ok, wait for the child to finish */ 17245579Sgrog waitpid(pid, &status, 0); 17345427Sgrog ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_FAILURE); 17445579Sgrog 17568716Sru /* now check that input and output are still the same size */ 17668716Sru insize = filesize(TESTFILE); 17768716Sru outsize = filesize(CHECKFILE); 17845579Sgrog ATF_REQUIRE(insize == strlen(TESTCONTENT)); 17985536Sru ATF_REQUIRE(outsize == 0); 18085536Sru} 18185536Sru 18285536SruATF_TC(t_spawn_reopen); 18385536Sru 18485536SruATF_TC_HEAD(t_spawn_reopen, tc) 18545579Sgrog{ 18685536Sru atf_tc_set_md_var(tc, "descr", 18785536Sru "an open filehandle can be replaced by a 'open' fileaction"); 18845579Sgrog atf_tc_set_md_var(tc, "require.progs", "/bin/cat"); 18945579Sgrog} 19045579Sgrog 19145579SgrogATF_TC_BODY(t_spawn_reopen, tc) 19285536Sru{ 19345579Sgrog int status, err; 19445579Sgrog pid_t pid; 19585536Sru char * const args[2] = { __UNCONST("cat"), NULL }; 19685536Sru posix_spawn_file_actions_t fa; 19785536Sru 19845579Sgrog /* 19945579Sgrog * make sure stdin is open in the parent 20045427Sgrog */ 20145427Sgrog freopen("/dev/zero", "r", stdin); 20285536Sru /* 20385536Sru * now request an open for this fd again in the child 20445427Sgrog */ 20545427Sgrog posix_spawn_file_actions_init(&fa); 20645427Sgrog posix_spawn_file_actions_addopen(&fa, fileno(stdin), 20785536Sru "/dev/null", O_RDONLY, 0); 20845579Sgrog err = posix_spawn(&pid, "/bin/cat", &fa, NULL, args, NULL); 20985536Sru posix_spawn_file_actions_destroy(&fa); 210107788Sru 21145427Sgrog ATF_REQUIRE(err == 0); 21285536Sru 21345579Sgrog waitpid(pid, &status, 0); 21485536Sru ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS); 215107788Sru} 21645427Sgrog 2171553SrgrimesATF_TC(t_spawn_open_nonexistent); 21885536Sru 2191553SrgrimesATF_TC_HEAD(t_spawn_open_nonexistent, tc) 2201553Srgrimes{ 22185536Sru atf_tc_set_md_var(tc, "descr", 22285536Sru "posix_spawn fails when a file to open does not exist"); 22385536Sru atf_tc_set_md_var(tc, "require.progs", "/bin/cat"); 22485536Sru} 2251553Srgrimes 22685536SruATF_TC_BODY(t_spawn_open_nonexistent, tc) 2271553Srgrimes{ 22885536Sru int err, status; 22952653Smarcel pid_t pid; 23085536Sru char * const args[2] = { __UNCONST("cat"), NULL }; 23179236Simp posix_spawn_file_actions_t fa; 23285536Sru 2331553Srgrimes posix_spawn_file_actions_init(&fa); 2341553Srgrimes posix_spawn_file_actions_addopen(&fa, STDIN_FILENO, 235118564Ssimon "./non/ex/ist/ent", O_RDONLY, 0); 236118564Ssimon err = posix_spawn(&pid, "/bin/cat", &fa, NULL, args, NULL); 23756481Scharnier if (err == 0) { 23856481Scharnier /* 23956481Scharnier * The child has been created - it should fail and 2401553Srgrimes * return exit code 127 2411553Srgrimes */ 2421553Srgrimes waitpid(pid, &status, 0); 2431553Srgrimes ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == 127); 2441553Srgrimes } else { 2451553Srgrimes /* 24699968Scharnier * The error has been noticed early enough, no child has 2471553Srgrimes * been run 248169507Swkoszek */ 249233648Seadler ATF_REQUIRE(err == ENOENT); 250169507Swkoszek } 251169507Swkoszek posix_spawn_file_actions_destroy(&fa); 252169507Swkoszek} 253169507Swkoszek 254169507Swkoszek#ifdef __NetBSD__ 255169507SwkoszekATF_TC(t_spawn_open_nonexistent_diag); 256169507Swkoszek 257169507SwkoszekATF_TC_HEAD(t_spawn_open_nonexistent_diag, tc) 258233648Seadler{ 259169507Swkoszek atf_tc_set_md_var(tc, "descr", 260169507Swkoszek "posix_spawn fails when a file to open does not exist " 261169507Swkoszek "and delivers proper diagnostic"); 262140442Sru atf_tc_set_md_var(tc, "require.progs", "/bin/cat"); 263140442Sru} 264 265ATF_TC_BODY(t_spawn_open_nonexistent_diag, tc) 266{ 267 int err; 268 pid_t pid; 269 char * const args[2] = { __UNCONST("cat"), NULL }; 270 posix_spawnattr_t attr; 271 posix_spawn_file_actions_t fa; 272 273 posix_spawnattr_init(&attr); 274 /* 275 * POSIX_SPAWN_RETURNERROR is a NetBSD specific flag that 276 * will cause a "proper" return value from posix_spawn(2) 277 * instead of a (potential) success there and a 127 exit 278 * status from the child process (c.f. the non-diag variant 279 * of this test). 280 */ 281 posix_spawnattr_setflags(&attr, POSIX_SPAWN_RETURNERROR); 282 posix_spawn_file_actions_init(&fa); 283 posix_spawn_file_actions_addopen(&fa, STDIN_FILENO, 284 "./non/ex/ist/ent", O_RDONLY, 0); 285 err = posix_spawn(&pid, "/bin/cat", &fa, &attr, args, NULL); 286 ATF_REQUIRE(err == ENOENT); 287 posix_spawn_file_actions_destroy(&fa); 288 posix_spawnattr_destroy(&attr); 289} 290#endif 291 292ATF_TC(t_spawn_fileactions); 293 294ATF_TC_HEAD(t_spawn_fileactions, tc) 295{ 296 atf_tc_set_md_var(tc, "descr", 297 "Tests various complex fileactions"); 298} 299 300ATF_TC_BODY(t_spawn_fileactions, tc) 301{ 302 int fd1, fd2, fd3, status, err; 303 pid_t pid; 304 char * const args[2] = { __UNCONST("h_fileactions"), NULL }; 305 char helper[FILENAME_MAX]; 306 posix_spawn_file_actions_t fa; 307 308 posix_spawn_file_actions_init(&fa); 309 310 closefrom(fileno(stderr)+1); 311 312 fd1 = open("/dev/null", O_RDONLY); 313 ATF_REQUIRE(fd1 == 3); 314 315 fd2 = open("/dev/null", O_WRONLY, O_CLOEXEC); 316 ATF_REQUIRE(fd2 == 4); 317 318 fd3 = open("/dev/null", O_WRONLY); 319 ATF_REQUIRE(fd3 == 5); 320 321 posix_spawn_file_actions_addclose(&fa, fd1); 322 posix_spawn_file_actions_addopen(&fa, 6, "/dev/null", O_RDWR, 0); 323 posix_spawn_file_actions_adddup2(&fa, 1, 7); 324 325 snprintf(helper, sizeof helper, "%s/h_fileactions", 326 atf_tc_get_config_var(tc, "srcdir")); 327 err = posix_spawn(&pid, helper, &fa, NULL, args, NULL); 328 posix_spawn_file_actions_destroy(&fa); 329 330 ATF_REQUIRE(err == 0); 331 332 waitpid(pid, &status, 0); 333 ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS); 334} 335 336ATF_TC(t_spawn_empty_fileactions); 337 338ATF_TC_HEAD(t_spawn_empty_fileactions, tc) 339{ 340 atf_tc_set_md_var(tc, "descr", 341 "posix_spawn with empty fileactions (PR kern/46038)"); 342 atf_tc_set_md_var(tc, "require.progs", "/bin/cat"); 343} 344 345ATF_TC_BODY(t_spawn_empty_fileactions, tc) 346{ 347 int status, err; 348 pid_t pid; 349 char * const args[2] = { __UNCONST("cat"), NULL }; 350 posix_spawn_file_actions_t fa; 351 size_t insize, outsize; 352 353 /* 354 * try a "cat < testfile > checkfile", but set up stdin/stdout 355 * already in the parent and pass empty file actions to the child. 356 */ 357 make_testfile(TESTFILE); 358 unlink(CHECKFILE); 359 360 freopen(TESTFILE, "r", stdin); 361 freopen(CHECKFILE, "w", stdout); 362 363 posix_spawn_file_actions_init(&fa); 364 err = posix_spawn(&pid, "/bin/cat", &fa, NULL, args, NULL); 365 posix_spawn_file_actions_destroy(&fa); 366 367 ATF_REQUIRE(err == 0); 368 369 /* ok, wait for the child to finish */ 370 waitpid(pid, &status, 0); 371 ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS); 372 373 /* now check that input and output have the same size */ 374 insize = filesize(TESTFILE); 375 outsize = filesize(CHECKFILE); 376 ATF_REQUIRE(insize == strlen(TESTCONTENT)); 377 ATF_REQUIRE(insize == outsize); 378} 379 380ATF_TP_ADD_TCS(tp) 381{ 382 ATF_TP_ADD_TC(tp, t_spawn_fileactions); 383 ATF_TP_ADD_TC(tp, t_spawn_open_nonexistent); 384#ifdef __NetBSD__ 385 ATF_TP_ADD_TC(tp, t_spawn_open_nonexistent_diag); 386#endif 387 ATF_TP_ADD_TC(tp, t_spawn_reopen); 388 ATF_TP_ADD_TC(tp, t_spawn_openmode); 389 ATF_TP_ADD_TC(tp, t_spawn_empty_fileactions); 390 391 return atf_no_error(); 392} 393