[LTP] [PATCH v3 10/12] Refactor pty01 test

Andrea Cervesato
2025-01-09T14:14:51Z

Rewrite part of the code using the new LTP library and fix the execution
of the test inside a new session via setsid(). The test is now split
into multiple files, instead of having multiple test* functions
executing all in one file.

Fixes: https://github.com/linux-test-project/kirk/issues/28
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
testcases/kernel/pty/pty01.c | 409 +++++--------------------------------------
1 file changed, 39 insertions(+), 370 deletions(-)

diff --git a/testcases/kernel/pty/pty01.c b/testcases/kernel/pty/pty01.c
index 666b221b017fe214b7baa0b2bc608dfc2feff732..4320082eac1ac8b81b7e8dbbfac826ab4f4c1488 100644
--- a/testcases/kernel/pty/pty01.c
+++ b/testcases/kernel/pty/pty01.c
@@ -1,395 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
- *
- * Copyright (c) International Business Machines Corp., 2002
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
- * the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ * Copyright (c) International Business Machines Corp., 2002
+ * Copyright (C) 2024 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
*/

-/* 12/23/2002 Port to LTP robbiew@us.ibm.com */
-/* 06/30/2001 Port to Linux nsharoff@us.ibm.com */
+/*\
+ * [Description]
+ *
+ * Verify that write/read is properly working when master and slave
+ * pseudo terminals communicate with each other.
+ */

#define _GNU_SOURCE
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <termios.h>
-#include <unistd.h>
-
-#include "test.h"
-#include "safe_macros.h"
-#include "lapi/ioctl.h"
-
-char *TCID = "pty01"; /* Test program identifier. */
-int TST_TOTAL = 5; /* Total number of test cases. */
-/**************/

-/*
- * pty master clone device
- */
-#define MASTERCLONE "/dev/ptmx"
+#include "common.h"

-/*
- * string for testing read/write on ptys
- */
#define STRING "Linux Test Project\n"

-/*
- * test buffer size
- */
-#define TESTSIZE 1024
-
-/*
- * mode we expect grantpt() to leave pty as
- */
-#define PTY_MODE 020622
-
-/*
- * number of procs for parallel test
- */
-#define NUMPROCS 15
-
-/*
- * test slave locking
- */
-static int test1(void)
-{
- int masterfd; /* master pty fd */
- int slavefd; /* slave pty fd */
- char *slavename;
- struct stat st;
- char buf[TESTSIZE];
-
- masterfd = SAFE_OPEN(NULL, MASTERCLONE, O_RDWR);
-
- slavename = ptsname(masterfd);
- if (slavename == NULL) {
- tst_brkm(TBROK | TERRNO, NULL, "ptsname() call failed");
- }
-
- if (grantpt(masterfd) != 0) {
- tst_brkm(TBROK | TERRNO, NULL, "grantpt() call failed");
- }
-
- if (stat(slavename, &st) != 0) {
- tst_brkm(TBROK | TERRNO, NULL, "stat(%s) failed", slavename);
- }
- if (st.st_uid != getuid()) {
- tst_brkm(TBROK, NULL, "uid mismatch");
- }
-
- /* grantpt() is a no-op in bionic. */
-#ifndef __BIONIC__
- if (st.st_mode != (S_IFCHR | S_IRUSR | S_IWUSR | S_IWGRP)) {
- tst_brkm(TBROK, NULL, "mode mismatch (mode=%o)", st.st_mode);
- }
-#endif
-
- slavefd = open(slavename, O_RDWR);
- if (slavefd >= 0) {
- tst_brkm(TBROK, NULL, "open didn't fail as expected!");
- }
-
- if (unlockpt(masterfd) != 0) {
- tst_brkm(TBROK | TERRNO, NULL, "unlockpt() failed");
- }
-
- slavefd = SAFE_OPEN(NULL, slavename, O_RDWR);
-
- /*
- * test writing to the master / reading from the slave
- */
- if (write(masterfd, STRING, strlen(STRING)) != strlen(STRING)) {
- /*
- * XXX: the errno printout might be garbage, but better to be
- * safe than sorry..
- */
- tst_brkm(TFAIL | TERRNO, NULL, "write to master");
- }
-
- if (read(slavefd, buf, strlen(STRING)) != strlen(STRING)) {
- /* XXX: Same as write above.. */
- tst_brkm(TFAIL | TERRNO, NULL, "read from slave");
- }
- if (strncmp(STRING, buf, strlen(STRING) - 1) != 0) {
- tst_brkm(TFAIL, NULL,
- "strings are different (STRING = '%s' != buf = '%s')",
- STRING, buf);
- }
-
- /*
- * test writing to the slave / reading from the master
- */
- if (write(slavefd, STRING, strlen(STRING)) != strlen(STRING)) {
- /* XXX: Same as write above.. */
- tst_brkm(TFAIL | TERRNO, NULL, "write to slave");
- }
-
- if (read(masterfd, buf, strlen(STRING)) != strlen(STRING)) {
- /* XXX: Same as write above.. */
- tst_brkm(TFAIL | TERRNO, NULL, "read from master");
- }
- if (strncmp(STRING, buf, strlen(STRING) - 1) != 0) {
- tst_brkm(TFAIL, NULL,
- "strings are different (STRING = '%s' != buf = '%s').",
- STRING, buf);
- }
-
- /*
- * try an invalid ioctl on the slave...
- */
- if (ioctl(slavefd, TIOCGWINSZ, NULL) == 0) {
- tst_brkm(TFAIL, NULL,
- "invalid slave TIOCGWINSZ ioctl succeeded.. it should "
- "have failed");
- }
+static size_t string_len;
+static int masterfd = -1;
+static int slavefd = -1;

- /*
- * try an invalid ioctl on the master...
- */
- if (ioctl(masterfd, TIOCGWINSZ, NULL) == 0) {
- tst_brkm(TFAIL, NULL,
- "invalid master TIOCGWINSZ ioctl succeeded.. it should "
- "have failed");
- }
-
- /*
- * close pty fds
- */
- if (close(slavefd) != 0) {
- tst_brkm(TBROK | TERRNO, NULL, "close of slave");
- }
- if (close(masterfd) != 0) {
- tst_brkm(TBROK | TERRNO, NULL, "close of master");
- }
- tst_resm(TPASS, "test1");
- /** NOTREACHED **/
- return 0;
-}
-
-/*
- * test slave operations with closed master
- */
-static void test2(void)
+static void run(void)
{
- int masterfd; /* master pty fd */
- int slavefd; /* slave pty fd */
- int i;
- char *slavename;
- char c;
-
- masterfd = SAFE_OPEN(NULL, MASTERCLONE, O_RDWR);
-
- slavename = ptsname(masterfd);
- if (slavename == NULL) {
- tst_brkm(TBROK | TERRNO, NULL, "ptsname() call failed");
- }
-
- if (grantpt(masterfd) != 0) {
- tst_brkm(TBROK | TERRNO, NULL, "grantpt() call failed");
- }
-
- if (unlockpt(masterfd) != 0) {
- tst_brkm(TBROK | TERRNO, NULL, "unlockpt() call failed");
- }
-
- slavefd = SAFE_OPEN(NULL, slavename, O_RDWR);
+ char buf[BUFSIZ];

- /*
- * close pty fds. See what happens when we close the master
- * first.
- */
- if (close(masterfd) != 0) {
- tst_brkm(TBROK | TERRNO, NULL, "close()");
- }
+ tst_res(TINFO, "Send message to master and read it from slave");

- errno = 0;
- if ((i = read(slavefd, &c, 1)) == 1) {
- tst_brkm(TFAIL, NULL,
- "reading from slave fd should have failed, but didn't"
- "(read '%c')", c);
- }
+ memset(buf, 0, BUFSIZ);
+ SAFE_WRITE(SAFE_WRITE_ALL, masterfd, STRING, string_len);
+ SAFE_READ(0, slavefd, buf, string_len + 1);
+ TST_EXP_EQ_STRN(STRING, buf, string_len - 1);

- if ((i = write(slavefd, &c, 1)) == 1) {
- tst_brkm(TFAIL, NULL,
- "writing to slave fd should have failed, but didn't");
- }
+ tst_res(TINFO, "Send message to slave and read it from master");

- if (ioctl(slavefd, TIOCGWINSZ, NULL) == 0) {
- tst_brkm(TFAIL, NULL,
- "trying TIOCGWINSZ on slave fd should have failed, "
- "but didn't");
- }
-
- if (close(slavefd) != 0) {
- tst_brkm(TBROK, NULL, "close");
- }
- tst_resm(TPASS, "test2");
+ memset(buf, 0, BUFSIZ);
+ SAFE_WRITE(SAFE_WRITE_ALL, slavefd, STRING, string_len);
+ SAFE_READ(0, masterfd, buf, string_len + 1);
+ TST_EXP_EQ_STRN(STRING, buf, string_len - 1);
}

-/*
- * test operations on master with closed slave
- */
-static void test3(void)
+static void setup(void)
{
- int masterfd; /* master pty fd */
+ masterfd = open_master();
+ slavefd = open_slave(masterfd);

- masterfd = SAFE_OPEN(NULL, MASTERCLONE, O_RDWR);
-
- if (ioctl(masterfd, TIOCGWINSZ, NULL) == 0) {
- tst_brkm(TFAIL | TERRNO, NULL,
- "trying TIOCGWINSZ on master with no open slave "
- "succeeded unexpectedly");
- }
- tst_resm(TPASS, "test3");
+ string_len = strlen(STRING);
}

-/*
- * test multiple opens on slave side of pty
- */
-static void test4(void)
+static void cleanup(void)
{
- int masterfd; /* master pty fd */
- int slavefd; /* slave pty fd */
- int slavefd2;
- int slavefd3;
- char *slavename;
-
- masterfd = SAFE_OPEN(NULL, MASTERCLONE, O_RDWR);
-
- slavename = ptsname(masterfd);
- if (slavename == NULL) {
- tst_brkm(TBROK, NULL, "ptsname() call failed");
- }
-
- if (grantpt(masterfd) != 0) {
- tst_brkm(TBROK, NULL, "grantpt() call failed");
- }
+ if (masterfd != -1)
+ SAFE_CLOSE(masterfd);

- if (unlockpt(masterfd) != 0) {
- tst_brkm(TBROK | TERRNO, NULL, "unlockpt() call failed");
- }
-
- slavefd = SAFE_OPEN(NULL, slavename, O_RDWR);
-
- slavefd2 = open(slavename, O_RDWR);
- if (slavefd < 0) {
- tst_brkm(TFAIL | TERRNO, NULL, "Could not open %s (again)",
- slavename);
- }
-
- slavefd3 = open(slavename, O_RDWR);
- if (slavefd < 0) {
- tst_brkm(TFAIL | TERRNO, NULL, "Could not open %s (once more)",
- slavename);
- }
-
- /*
- * close pty fds.
- */
- if (close(slavefd) != 0) {
- tst_brkm(TBROK | TERRNO, NULL, "close slave");
- }
-
- if (close(slavefd2) != 0) {
- tst_brkm(TBROK, NULL, "close slave again");
- }
-
- if (close(slavefd3) != 0) {
- tst_brkm(TBROK, NULL, "close slave once more");
- }
-
- if (close(masterfd) != 0) {
- tst_brkm(TBROK, NULL, "close master");
- }
- tst_resm(TPASS, "test4");
-}
-
-/*
- * test opening/closing lots of ptys in parallel. We may run out
- * of ptys for this test depending on how the system is configured,
- * but that's not a fatal error.
- */
-static void test5(void)
-{
- int masterfd; /* master pty fd */
- char *slavename;
- int status;
- int i;
-
- for (i = 0; i < NUMPROCS; ++i) {
- switch (fork()) {
- case -1:
- tst_brkm(TBROK, NULL, "fork()");
- break;
- case 0:
- masterfd = open(MASTERCLONE, O_RDWR);
- if (masterfd < 0) {
- printf("proc %d: opening %s failed: %s",
- i, MASTERCLONE, strerror(errno));
- exit(1);
- }
- if (grantpt(masterfd) != 0) {
- printf("proc %d: grantpt() call failed: %s",
- i, strerror(errno));
- exit(1);
- }
- slavename = ptsname(masterfd);
- if (slavename == NULL) {
- printf("proc %d: ptsname() call failed: %s",
- i, strerror(errno));
- exit(1);
- }
- sleep(10);
- if (close(masterfd) != 0) {
- printf("proc %d: close failed: %s",
- i, strerror(errno));
- exit(1);
- }
- exit(0);
- default:
- break;
- }
- }
- while (wait(&status) > 0) {
- if (status) {
- tst_brkm(TFAIL, NULL,
- "child exited with non-zero status %d",
- status);
- }
- }
- tst_resm(TPASS, "test5");
+ if (slavefd != -1)
+ SAFE_CLOSE(slavefd);
}

-/*
- * main test driver
- */
-int main(void)
-{
- test1();
- test2();
- test3();
- test4();
- test5();
-
- /*
- * all done
- */
- tst_exit();
-}