/*-1* Copyright (c) 2014 Spectra Logic Corporation. All rights reserved.2* Redistribution and use in source and binary forms, with or without3* modification, are permitted provided that the following conditions4* are met:5* 1. Redistributions of source code must retain the above copyright6* notice, this list of conditions and the following disclaimer.7* 2. Redistributions in binary form must reproduce the above copyright8* notice, this list of conditions and the following disclaimer in the9* documentation and/or other materials provided with the distribution.10*11* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND12* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE13* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE14* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE15* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL16* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS17* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)18* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT19* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY20* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF21* SUCH DAMAGE.22*/2324#include <sys/param.h>25#include <sys/socket.h>26#include <sys/un.h>2728#include <stdbool.h>29#include <stdio.h>30#include <stdlib.h>3132#include <atf-c.h>3334const char create_pat[] = "!system=DEVFS subsystem=CDEV type=CREATE cdev=md";35const char destroy_pat[] = "!system=DEVFS subsystem=CDEV type=DESTROY cdev=md";3637/* Helper functions*/3839/*40* Create two devd events. The easiest way I know of, that requires no special41* hardware, is to create md(4) devices.42*/43static void44create_two_events(void)45{46FILE *create_stdout;47FILE *destroy_stdout;48char mdname[80];49char destroy_cmd[95];50char *error;5152create_stdout = popen("mdconfig -a -s 64 -t null", "r");53ATF_REQUIRE(create_stdout != NULL);54error = fgets(mdname, sizeof(mdname), create_stdout);55ATF_REQUIRE(error != NULL);56/* We only expect one line of output */57ATF_REQUIRE_EQ(0, pclose(create_stdout));5859snprintf(destroy_cmd, nitems(destroy_cmd), "mdconfig -d -u %s", mdname);60destroy_stdout = popen(destroy_cmd, "r");61ATF_REQUIRE(destroy_stdout != NULL);62/* We expect no output */63ATF_REQUIRE_EQ(0, pclose(destroy_stdout));64}6566/* Setup and return an open client socket */67static int68common_setup(int socktype, const char* sockpath) {69struct sockaddr_un devd_addr;70int s, error;7172memset(&devd_addr, 0, sizeof(devd_addr));73devd_addr.sun_family = PF_LOCAL;74strlcpy(devd_addr.sun_path, sockpath, sizeof(devd_addr.sun_path));75s = socket(PF_LOCAL, socktype, 0);76ATF_REQUIRE(s >= 0);77error = connect(s, (struct sockaddr*)&devd_addr, SUN_LEN(&devd_addr));78ATF_REQUIRE_EQ(0, error);7980create_two_events();81return (s);82}8384/*85* Test Cases86*/8788/*89* Open a client connection to devd, create some events, and test that they can90* be read _whole_ and _one_at_a_time_ from the socket91*/92ATF_TC_WITHOUT_HEAD(seqpacket);93ATF_TC_BODY(seqpacket, tc)94{95int s;96bool got_create_event = false;97bool got_destroy_event = false;9899s = common_setup(SOCK_SEQPACKET, "/var/run/devd.seqpacket.pipe");100/*101* Loop until both events are detected on _different_ reads102* There may be extra events due to unrelated system activity103* If we never get both events, then the test will timeout.104*/105while (!(got_create_event && got_destroy_event)) {106int cmp;107ssize_t len;108char event[1024];109110/* Read 1 less than sizeof(event) to allow space for NULL */111len = recv(s, event, sizeof(event) - 1, MSG_WAITALL);112ATF_REQUIRE(len != -1);113/* NULL terminate the result */114event[len] = '\0';115printf("%s", event);116cmp = strncmp(event, create_pat, sizeof(create_pat) - 1);117if (cmp == 0)118got_create_event = true;119120cmp = strncmp(event, destroy_pat, sizeof(destroy_pat) - 1);121if (cmp == 0)122got_destroy_event = true;123}124125close(s);126}127128/*129* Open a client connection to devd using the stream socket, create some130* events, and test that they can be read in any number of reads.131*/132ATF_TC_WITHOUT_HEAD(stream);133ATF_TC_BODY(stream, tc)134{135char *event;136int s;137bool got_create_event = false;138bool got_destroy_event = false;139size_t len = 0, sz;140141s = common_setup(SOCK_STREAM, "/var/run/devd.pipe");142143/*144* Use a large buffer: we're reading from a stream socket so can't rely145* on record boundaries. Instead, we just keep appending to the buffer.146*/147sz = 1024 * 1024;148event = malloc(sz);149ATF_REQUIRE(event != NULL);150151/*152* Loop until both events are detected on the same or different reads.153* There may be extra events due to unrelated system activity.154* If we never get both events, then the test will timeout.155*/156while (!(got_create_event && got_destroy_event) && len < sz - 1) {157ssize_t newlen;158char *create_pos, *destroy_pos;159160/* Read 1 less than sizeof(event) to allow space for NULL */161newlen = read(s, &event[len], sz - len - 1);162ATF_REQUIRE(newlen > 0);163len += newlen;164/* NULL terminate the result */165event[len] = '\0';166167create_pos = strstr(event, create_pat);168if (create_pos != NULL)169got_create_event = true;170171destroy_pos = strstr(event, destroy_pat);172if (destroy_pos != NULL)173got_destroy_event = true;174}175printf("%s", event);176if (len >= sz - 1)177atf_tc_fail("Event buffer overflowed");178179free(event);180close(s);181}182183/*184* Main.185*/186187ATF_TP_ADD_TCS(tp)188{189ATF_TP_ADD_TC(tp, seqpacket);190ATF_TP_ADD_TC(tp, stream);191192return (atf_no_error());193}194195196197