Home | History | Annotate | Line # | Download | only in ttyio
      1 /* $NetBSD: t_ptm.c,v 1.3 2025/08/05 23:59:42 gutteridge Exp $ */
      2 
      3 /*
      4  * Copyright (c) 2004, 2008 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Christos Zoulas.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 __COPYRIGHT("@(#) Copyright (c) 2008\
     34  The NetBSD Foundation, inc. All rights reserved.");
     35 __RCSID("$NetBSD: t_ptm.c,v 1.3 2025/08/05 23:59:42 gutteridge Exp $");
     36 
     37 #include <sys/ioctl.h>
     38 #include <sys/stat.h>
     39 
     40 #include <errno.h>
     41 #include <fcntl.h>
     42 #include <grp.h>
     43 #include <stdlib.h>
     44 #include <string.h>
     45 #include <unistd.h>
     46 
     47 #include <atf-c.h>
     48 
     49 #define REQUIRE_ERRNO(x, v) \
     50 		ATF_REQUIRE_MSG(x != v, "%s: %s", #x, strerror(errno))
     51 
     52 ATF_TC(ptm);
     53 
     54 ATF_TC_HEAD(ptm, tc)
     55 {
     56 
     57 	atf_tc_set_md_var(tc, "descr", "Checks /dev/ptm device");
     58 }
     59 
     60 ATF_TC_BODY(ptm, tc)
     61 {
     62 	struct stat stm, sts;
     63 	struct ptmget ptm;
     64 	int fdm;
     65 	struct group *gp;
     66 
     67 	if ((fdm = open("/dev/ptm", O_RDWR)) == -1) {
     68 		if (errno == ENOENT || errno == ENODEV)
     69 			atf_tc_skip("/dev/ptm: %s", strerror(errno));
     70 		atf_tc_fail("/dev/ptm: %s", strerror(errno));
     71 	}
     72 
     73 	REQUIRE_ERRNO(fstat(fdm, &stm), -1);
     74 	ATF_REQUIRE_EQ(major(stm.st_rdev), 165);
     75 	REQUIRE_ERRNO(ioctl(fdm, TIOCPTMGET, &ptm), -1);
     76 
     77 	ATF_REQUIRE_MSG(strncmp(ptm.cn, "/dev/pty", 8) == 0
     78 		|| strncmp(ptm.cn, "/dev/null", 9) == 0,
     79 		"bad master name: %s", ptm.cn);
     80 
     81 	ATF_REQUIRE_MSG(strncmp(ptm.sn, "/dev/tty", 8) == 0
     82 		|| strncmp(ptm.sn, "/dev/pts/", 9) == 0,
     83 		"bad slave name: %s", ptm.sn);
     84 
     85 	if (strncmp(ptm.cn, "/dev/null", 9) != 0) {
     86 		REQUIRE_ERRNO(fstat(ptm.cfd, &stm), -1);
     87 		REQUIRE_ERRNO(stat(ptm.cn, &sts), -1);
     88 		ATF_REQUIRE_EQ(stm.st_rdev, sts.st_rdev);
     89 	}
     90 
     91 	REQUIRE_ERRNO(fstat(ptm.sfd, &stm), -1);
     92 	REQUIRE_ERRNO(stat(ptm.sn, &sts), -1);
     93 	ATF_REQUIRE_EQ(stm.st_rdev, sts.st_rdev);
     94 
     95 	ATF_REQUIRE_EQ_MSG(sts.st_uid, getuid(), "bad slave uid");
     96 
     97 	ATF_REQUIRE_MSG((gp = getgrnam("tty")) != NULL,
     98 	    "cannot find `tty' group");
     99 	ATF_REQUIRE_EQ_MSG(sts.st_gid, gp->gr_gid, "bad slave grid");
    100 
    101 	(void)close(ptm.sfd);
    102 	(void)close(ptm.cfd);
    103 	(void)close(fdm);
    104 }
    105 
    106 /*
    107  * On NetBSD /dev/ptyp0 == /dev/pts/0 so we can check for major
    108  * and minor device numbers. This check is non-portable. This
    109  * check is now disabled because we might not have /dev/ptyp0
    110  * at all.
    111  */
    112 
    113 /*
    114  * #define PTY_DEVNO_CHECK
    115  */
    116 
    117 ATF_TC(ptmx);
    118 
    119 ATF_TC_HEAD(ptmx, tc)
    120 {
    121 
    122 	atf_tc_set_md_var(tc, "descr", "Checks /dev/ptmx device");
    123 }
    124 
    125 ATF_TC_BODY(ptmx, tc)
    126 {
    127 	struct stat stm, sts;
    128 	char *pty;
    129 	int fdm, fds;
    130 	struct group *gp;
    131 
    132 	if ((fdm = posix_openpt(O_RDWR|O_NOCTTY)) == -1) {
    133 		if (errno == ENOENT || errno == ENODEV)
    134 			atf_tc_skip("/dev/ptmx: %s", strerror(errno));
    135 
    136 		atf_tc_fail("/dev/ptmx: %s", strerror(errno));
    137 	}
    138 
    139 	REQUIRE_ERRNO(fstat(fdm, &stm), -1);
    140 
    141 #ifdef PTY_DEVNO_CHECK
    142 	REQUIRE_ERRNO(stat("/dev/ptyp0", &sts), -1);
    143 
    144 	ATF_REQUIRE_EQ_MSG(major(stm.st_rdev), major(sts.st_rdev),
    145 		"bad master major number");
    146 #endif
    147 
    148 	REQUIRE_ERRNO(grantpt(fdm), -1);
    149 	REQUIRE_ERRNO(unlockpt(fdm), -1);
    150 	REQUIRE_ERRNO((pty = ptsname(fdm)), NULL);
    151 
    152 	REQUIRE_ERRNO((fds = open(pty, O_RDWR|O_NOCTTY)), -1);
    153 	REQUIRE_ERRNO(fstat(fds, &sts), -1);
    154 
    155 #ifdef PTY_DEVNO_CHECK
    156 	ATF_REQUIRE_EQ_MSG(minor(stm.st_rdev), minor(sts.st_rdev),
    157 		"bad slave minor number");
    158 #endif
    159 
    160 	ATF_REQUIRE_EQ_MSG(sts.st_uid, getuid(), "bad slave uid");
    161 	ATF_REQUIRE_MSG((gp = getgrnam("tty")) != NULL,
    162 	    "cannot find `tty' group");
    163 
    164 	ATF_REQUIRE_EQ_MSG(sts.st_gid, gp->gr_gid, "bad slave gid");
    165 }
    166 
    167 ATF_TC(ptmx_extra);
    168 
    169 ATF_TC_HEAD(ptmx_extra, tc)
    170 {
    171 
    172 	atf_tc_set_md_var(tc, "descr", "Checks /dev/ptmx device "
    173 	    "applies O_NONBLOCK, O_CLOEXEC, and O_CLOFORK");
    174 }
    175 
    176 ATF_TC_BODY(ptmx_extra, tc)
    177 {
    178 	int fdm;
    179 
    180 	if ((fdm = posix_openpt(O_RDWR|O_NONBLOCK|O_CLOEXEC|O_CLOFORK)) == -1) {
    181 		if (errno == ENOENT || errno == ENODEV)
    182 			atf_tc_skip("/dev/ptmx: %s", strerror(errno));
    183 
    184 		atf_tc_fail("/dev/ptmx: %s", strerror(errno));
    185 	}
    186 
    187 	ATF_CHECK_EQ(O_RDWR|O_NONBLOCK, fcntl(fdm, F_GETFL));
    188 	ATF_CHECK_EQ(FD_CLOEXEC|FD_CLOFORK, fcntl(fdm, F_GETFD));
    189 }
    190 
    191 ATF_TP_ADD_TCS(tp)
    192 {
    193 
    194 	ATF_TP_ADD_TC(tp, ptm);
    195 	ATF_TP_ADD_TC(tp, ptmx);
    196 	ATF_TP_ADD_TC(tp, ptmx_extra);
    197 
    198 	return atf_no_error();
    199 }
    200