xref: /openbsd-src/lib/libc/stdlib/posix_pty.c (revision bf198cc6eba0ca1f6d79f71e8e2243d386241fa8)
1*bf198cc6Smillert /*	$OpenBSD: posix_pty.c,v 1.3 2019/01/25 00:19:25 millert Exp $	*/
28d2ec7eaSmillert 
38d2ec7eaSmillert /*
4*bf198cc6Smillert  * Copyright (c) 2012 Todd C. Miller <millert@openbsd.org>
58d2ec7eaSmillert  *
68d2ec7eaSmillert  * Permission to use, copy, modify, and distribute this software for any
78d2ec7eaSmillert  * purpose with or without fee is hereby granted, provided that the above
88d2ec7eaSmillert  * copyright notice and this permission notice appear in all copies.
98d2ec7eaSmillert  *
108d2ec7eaSmillert  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
118d2ec7eaSmillert  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
128d2ec7eaSmillert  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
138d2ec7eaSmillert  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
148d2ec7eaSmillert  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
158d2ec7eaSmillert  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
168d2ec7eaSmillert  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
178d2ec7eaSmillert  */
188d2ec7eaSmillert 
198d2ec7eaSmillert #include <sys/types.h>
208d2ec7eaSmillert #include <sys/ioctl.h>
218d2ec7eaSmillert #include <sys/stat.h>
228d2ec7eaSmillert #include <sys/tty.h>
238d2ec7eaSmillert #include <errno.h>
248d2ec7eaSmillert #include <fcntl.h>
258d2ec7eaSmillert #include <paths.h>
268d2ec7eaSmillert #include <stddef.h>
278d2ec7eaSmillert #include <stdlib.h>
288d2ec7eaSmillert #include <string.h>
298d2ec7eaSmillert #include <unistd.h>
308d2ec7eaSmillert 
318d2ec7eaSmillert int
posix_openpt(int oflag)328d2ec7eaSmillert posix_openpt(int oflag)
338d2ec7eaSmillert {
348d2ec7eaSmillert 	struct ptmget ptm;
358d2ec7eaSmillert 	int fd, mfd = -1;
368d2ec7eaSmillert 
378d2ec7eaSmillert 	/* User must specify O_RDWR in oflag. */
38dc519336Sguenther 	if ((oflag & O_ACCMODE) != O_RDWR ||
39dc519336Sguenther 	    (oflag & ~(O_ACCMODE | O_NOCTTY)) != 0) {
408d2ec7eaSmillert 		errno = EINVAL;
418d2ec7eaSmillert 		return -1;
428d2ec7eaSmillert 	}
438d2ec7eaSmillert 
448d2ec7eaSmillert 	/* Get pty master and slave (this API only uses the master). */
458d2ec7eaSmillert 	fd = open(PATH_PTMDEV, O_RDWR);
468d2ec7eaSmillert 	if (fd != -1) {
478d2ec7eaSmillert 		if (ioctl(fd, PTMGET, &ptm) != -1) {
488d2ec7eaSmillert 			close(ptm.sfd);
498d2ec7eaSmillert 			mfd = ptm.cfd;
508d2ec7eaSmillert 		}
518d2ec7eaSmillert 		close(fd);
528d2ec7eaSmillert 	}
538d2ec7eaSmillert 
548d2ec7eaSmillert 	return mfd;
558d2ec7eaSmillert }
568d2ec7eaSmillert 
578d2ec7eaSmillert /*
588d2ec7eaSmillert  * Look up the name of the specified pty master fd.
598d2ec7eaSmillert  * Note that the name returned does *not* include the /dev/ prefix.
608d2ec7eaSmillert  * Returns the name on success and NULL on error, setting errno.
618d2ec7eaSmillert  */
628d2ec7eaSmillert static const char *
ptmname(int mfd)638d2ec7eaSmillert ptmname(int mfd)
648d2ec7eaSmillert {
658d2ec7eaSmillert 	struct stat sb;
668d2ec7eaSmillert 	const char *name;
678d2ec7eaSmillert 
688d2ec7eaSmillert 	/* Make sure it is a pty master. */
698d2ec7eaSmillert 	if (fstat(mfd, &sb) != 0)
708d2ec7eaSmillert 		return NULL;
718d2ec7eaSmillert 	if (!S_ISCHR(sb.st_mode)) {
728d2ec7eaSmillert 		errno = EINVAL;
738d2ec7eaSmillert 		return NULL;
748d2ec7eaSmillert 	}
758d2ec7eaSmillert 	name = devname(sb.st_rdev, S_IFCHR);
768d2ec7eaSmillert 	if (strncmp(name, "pty", 3) != 0) {
778d2ec7eaSmillert 		errno = EINVAL;
788d2ec7eaSmillert 		return NULL;
798d2ec7eaSmillert 	}
808d2ec7eaSmillert 	return name;
818d2ec7eaSmillert }
828d2ec7eaSmillert 
838d2ec7eaSmillert /*
848d2ec7eaSmillert  * The PTMGET ioctl handles the mode and owner for us.
858d2ec7eaSmillert  */
868d2ec7eaSmillert int
grantpt(int mfd)878d2ec7eaSmillert grantpt(int mfd)
888d2ec7eaSmillert {
898d2ec7eaSmillert 	return ptmname(mfd) ? 0 : -1;
908d2ec7eaSmillert }
918d2ec7eaSmillert 
928d2ec7eaSmillert /*
938d2ec7eaSmillert  * The PTMGET ioctl unlocks the pty master and slave for us.
948d2ec7eaSmillert  */
958d2ec7eaSmillert int
unlockpt(int mfd)968d2ec7eaSmillert unlockpt(int mfd)
978d2ec7eaSmillert {
988d2ec7eaSmillert 	return ptmname(mfd) ? 0 : -1;
998d2ec7eaSmillert }
1008d2ec7eaSmillert 
1018d2ec7eaSmillert /*
1028d2ec7eaSmillert  * Look up the path of the slave pty that corresponds to the master fd.
1038d2ec7eaSmillert  * Returns the path if successful or NULL on error.
1048d2ec7eaSmillert  */
1058d2ec7eaSmillert char *
ptsname(int mfd)1068d2ec7eaSmillert ptsname(int mfd)
1078d2ec7eaSmillert {
1088d2ec7eaSmillert 	const char *master;
1098d2ec7eaSmillert 	static char slave[sizeof(((struct ptmget *)NULL)->sn)];
1108d2ec7eaSmillert 
1118d2ec7eaSmillert 	if ((master = ptmname(mfd)) == NULL)
1128d2ec7eaSmillert 		return NULL;
1138d2ec7eaSmillert 
1148d2ec7eaSmillert 	/* Add /dev/ prefix and convert "pty" to "tty". */
1158d2ec7eaSmillert 	strlcpy(slave, _PATH_DEV, sizeof(slave));
1168d2ec7eaSmillert 	strlcat(slave, master, sizeof(slave));
1178d2ec7eaSmillert 	slave[sizeof(_PATH_DEV) - 1] = 't';
1188d2ec7eaSmillert 
1198d2ec7eaSmillert 	return slave;
1208d2ec7eaSmillert }
121