1 #include "EXTERN.h"
2 #include "perl.h"
3 #include "XSUB.h"
4 
5 #define PTY_DEBUG 1
6 
7 #ifdef PTY_DEBUG
8 static int print_debug;
9 #endif
10 
11 #ifdef PerlIO
12 typedef int SysRet;
13 typedef PerlIO * InOutStream;
14 #else
15 # define PERLIO_IS_STDIO 1
16 # define PerlIO_fileno fileno
17 typedef int SysRet;
18 typedef FILE * InOutStream;
19 #endif
20 
21 #include "patchlevel.h"
22 
23 #if (PATCHLEVEL < 3) || ((PATCHLEVEL == 3) && (SUBVERSION < 22))
24      /* before 5.003_22 */
25 #    define MY_start_subparse(fmt,flags) start_subparse()
26 #else
27 #  if (PATCHLEVEL == 3) && (SUBVERSION == 22)
28      /* 5.003_22 */
29 #    define MY_start_subparse(fmt,flags) start_subparse(flags)
30 #  else
31      /* 5.003_23  onwards */
32 #    define MY_start_subparse(fmt,flags) start_subparse(fmt,flags)
33 #  endif
34 #endif
35 
36 /*
37  * The following pty-allocation code was heavily inspired by its
38  * counterparts in openssh 3.0p1 and Xemacs 21.4.5 but is a complete
39  * rewrite by me, Roland Giersig <RGiersig@cpan.org>.
40  *
41  * Nevertheless my references to Tatu Ylonen <ylo@cs.hut.fi>
42  * and the Xemacs development team for their inspiring code.
43  *
44  * mysignal and strlcpy were borrowed from openssh and have their
45  * copyright messages attached.
46  */
47 
48 #include <unistd.h>
49 #include <stdlib.h>
50 #include <stdio.h>
51 #include <fcntl.h>
52 #include <sys/types.h>
53 #include <sys/stat.h>
54 #include <sys/ioctl.h>
55 
56 #ifdef HAVE_LIBUTIL_H
57 # include <libutil.h>
58 #endif /* HAVE_UTIL_H */
59 
60 #ifdef HAVE_UTIL_H
61 # ifdef UTIL_H_ABS_PATH
62 #  include UTIL_H_ABS_PATH
63 # elif ((PATCHLEVEL < 19) && (SUBVERSION < 4))
64 #  include <util.h>
65 # endif
66 #endif /* HAVE_UTIL_H */
67 
68 #ifdef HAVE_PTY_H
69 # include <pty.h>
70 #endif
71 
72 #ifdef HAVE_SYS_PTY_H
73 # include <sys/pty.h>
74 #endif
75 
76 #ifdef HAVE_SYS_PTYIO_H
77 # include <sys/ptyio.h>
78 #endif
79 
80 #if defined(HAVE_DEV_PTMX) && defined(HAVE_SYS_STROPTS_H)
81 # include <sys/stropts.h>
82 #endif
83 
84 #ifdef HAVE_TERMIOS_H
85 #include <termios.h>
86 #endif
87 
88 #ifdef HAVE_TERMIO_H
89 #include <termio.h>
90 #endif
91 
92 #ifndef O_NOCTTY
93 #define O_NOCTTY 0
94 #endif
95 
96 
97 /* from  $OpenBSD: misc.c,v 1.12 2001/06/26 17:27:24 markus Exp $        */
98 
99 /*
100  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
101  *
102  * Redistribution and use in source and binary forms, with or without
103  * modification, are permitted provided that the following conditions
104  * are met:
105  * 1. Redistributions of source code must retain the above copyright
106  *    notice, this list of conditions and the following disclaimer.
107  * 2. Redistributions in binary form must reproduce the above copyright
108  *    notice, this list of conditions and the following disclaimer in the
109  *    documentation and/or other materials provided with the distribution.
110  *
111  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
112  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
113  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
114  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
115  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
116  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
117  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
118  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
119  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
120  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
121  */
122 
123 #include <signal.h>
124 
125 typedef void (*mysig_t)(int);
126 
127 static mysig_t
mysignal(int sig,mysig_t act)128 mysignal(int sig, mysig_t act)
129 {
130 #ifdef HAVE_SIGACTION
131         struct sigaction sa, osa;
132 
133         if (sigaction(sig, NULL, &osa) == -1)
134                 return (mysig_t) -1;
135         if (osa.sa_handler != act) {
136                 memset(&sa, 0, sizeof(sa));
137                 sigemptyset(&sa.sa_mask);
138                 sa.sa_flags = 0;
139 #if defined(SA_INTERRUPT)
140                 if (sig == SIGALRM)
141                         sa.sa_flags |= SA_INTERRUPT;
142 #endif
143                 sa.sa_handler = act;
144                 if (sigaction(sig, &sa, NULL) == -1)
145                         return (mysig_t) -1;
146         }
147         return (osa.sa_handler);
148 #else
149         return (signal(sig, act));
150 #endif
151 }
152 
153 /*  from  $OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $     */
154 
155 /*
156  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
157  * All rights reserved.
158  *
159  * Redistribution and use in source and binary forms, with or without
160  * modification, are permitted provided that the following conditions
161  * are met:
162  * 1. Redistributions of source code must retain the above copyright
163  *    notice, this list of conditions and the following disclaimer.
164  * 2. Redistributions in binary form must reproduce the above copyright
165  *    notice, this list of conditions and the following disclaimer in the
166  *    documentation and/or other materials provided with the distribution.
167  * 3. The name of the author may not be used to endorse or promote products
168  *    derived from this software without specific prior written permission.
169  *
170  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
171  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
172  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
173  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
174  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
175  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
176  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
177  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
178  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
179  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
180  */
181 
182 #ifndef HAVE_STRLCPY
183 
184 /*
185  * Copy src to string dst of size siz.  At most siz-1 characters
186  * will be copied.  Always NUL terminates (unless siz == 0).
187  * Returns strlen(src); if retval >= siz, truncation occurred.
188  */
189 static size_t
strlcpy(dst,src,siz)190 strlcpy(dst, src, siz)
191         char *dst;
192         const char *src;
193         size_t siz;
194 {
195         register char *d = dst;
196         register const char *s = src;
197         register size_t n = siz;
198 
199         /* Copy as many bytes as will fit */
200         if (n != 0 && --n != 0) {
201                 do {
202                         if ((*d++ = *s++) == 0)
203                                 break;
204                 } while (--n != 0);
205         }
206 
207         /* Not enough room in dst, add NUL and traverse rest of src */
208         if (n == 0) {
209                 if (siz != 0)
210                         *d = '\0';              /* NUL-terminate dst */
211                 while (*s++)
212                         ;
213         }
214 
215         return(s - src - 1);    /* count does not include NUL */
216 }
217 
218 #endif /* !HAVE_STRLCPY */
219 
220 /*
221  * Move file descriptor so it doesn't collide with stdin/out/err
222  */
223 
224 static void
make_safe_fd(int * fd)225 make_safe_fd(int * fd)
226 {
227   if (*fd < 3) {
228     int newfd;
229     newfd = fcntl(*fd, F_DUPFD, 3);
230     if (newfd < 0) {
231       if (PL_dowarn)
232 	warn("IO::Tty::pty_allocate(nonfatal): tried to move fd %d up but fcntl() said %.100s", *fd, strerror(errno));
233     } else {
234       close (*fd);
235       *fd = newfd;
236     }
237   }
238 }
239 
240 /*
241  * After having acquired a master pty, try to find out the slave name,
242  * initialize and open the slave.
243  */
244 
245 #if defined (HAVE_PTSNAME)
246 char * ptsname(int);
247 #endif
248 
249 static int
open_slave(int * ptyfd,int * ttyfd,char * namebuf,int namebuflen)250 open_slave(int *ptyfd, int *ttyfd, char *namebuf, int namebuflen)
251 {
252     /*
253      * now do some things that are supposedly healthy for ptys,
254      * i.e. changing the access mode.
255      */
256 #if defined(HAVE_GRANTPT) ||  defined(HAVE_UNLOCKPT)
257     {
258 	mysig_t old_signal;
259 	old_signal = mysignal(SIGCHLD, SIG_DFL);
260 #if defined(HAVE_GRANTPT)
261 #if PTY_DEBUG
262 	if (print_debug)
263 	  fprintf(stderr, "trying grantpt()...\n");
264 #endif
265 	if (grantpt(*ptyfd) < 0) {
266 	    if (PL_dowarn)
267 		warn("IO::Tty::pty_allocate(nonfatal): grantpt(): %.100s", strerror(errno));
268 	}
269 
270 #endif /* HAVE_GRANTPT */
271 #if defined(HAVE_UNLOCKPT)
272 #if PTY_DEBUG
273 	if (print_debug)
274 	  fprintf(stderr, "trying unlockpt()...\n");
275 #endif
276 	if (unlockpt(*ptyfd) < 0) {
277 	    if (PL_dowarn)
278 		warn("IO::Tty::pty_allocate(nonfatal): unlockpt(): %.100s", strerror(errno));
279 	}
280 #endif /* HAVE_UNLOCKPT */
281 	mysignal(SIGCHLD, old_signal);
282     }
283 #endif /* HAVE_GRANTPT || HAVE_UNLOCKPT */
284 
285 
286     /*
287      * find the slave name, if we don't have it already
288      */
289 
290 #if defined (HAVE_PTSNAME_R)
291     if (namebuf[0] == 0) {
292 #if PTY_DEBUG
293 	if (print_debug)
294 	  fprintf(stderr, "trying ptsname_r()...\n");
295 #endif
296 	if(ptsname_r(*ptyfd, namebuf, namebuflen)) {
297 	    if (PL_dowarn)
298 		warn("IO::Tty::open_slave(nonfatal): ptsname_r(): %.100s", strerror(errno));
299 	}
300     }
301 #endif /* HAVE_PTSNAME_R */
302 
303 #if defined (HAVE_PTSNAME)
304     if (namebuf[0] == 0) {
305 	char * name;
306 #if PTY_DEBUG
307 	if (print_debug)
308 	  fprintf(stderr, "trying ptsname()...\n");
309 #endif
310 	name = ptsname(*ptyfd);
311 	if (name) {
312 	    if(strlcpy(namebuf, name, namebuflen) >= namebuflen) {
313 	      warn("ERROR: IO::Tty::open_slave: ttyname truncated");
314 	      return 0;
315 	    }
316 	} else {
317 	    if (PL_dowarn)
318 		warn("IO::Tty::open_slave(nonfatal): ptsname(): %.100s", strerror(errno));
319 	}
320     }
321 #endif /* HAVE_PTSNAME */
322 
323     if (namebuf[0] == 0)
324 	return 0;		/* we failed to get the slave name */
325 
326 #if defined (__SVR4) && defined (__sun)
327        #include <sys/types.h>
328        #include <unistd.h>
329        {
330            uid_t euid = geteuid();
331            uid_t uid  = getuid();
332 
333            /* root running as another user
334             * grantpt() has done the wrong thing
335              */
336            if (euid != uid && uid == 0) {
337 #if PTY_DEBUG
338 		if (print_debug)
339 	  	    fprintf(stderr, "trying seteuid() from %d to %d...\n",
340 			euid, uid);
341 #endif
342 		if (setuid(uid)) {
343 		    warn("ERROR: IO::Tty::open_slave: couldn't seteuid to root: %d", errno);
344 		    return 0;
345 		}
346 		if (chown(namebuf, euid, -1)) {
347 		    warn("ERROR: IO::Tty::open_slave: couldn't fchown the pty: %d", errno);
348 		    return 0;
349 		}
350 		if (seteuid(euid)) {
351 		    warn("ERROR: IO::Tty::open_slave: couldn't seteuid back: %d", errno);
352 		    return 0;
353 		}
354            }
355        }
356 #endif
357 
358     if (*ttyfd >= 0) {
359       make_safe_fd(ptyfd);
360       make_safe_fd(ttyfd);
361       return 1;			/* we already have an open slave, so
362                                    no more init is needed */
363     }
364 
365     /*
366      * Open the slave side.
367      */
368 #if PTY_DEBUG
369     if (print_debug)
370       fprintf(stderr, "trying to open %s...\n", namebuf);
371 #endif
372 
373     *ttyfd = open(namebuf, O_RDWR | O_NOCTTY);
374     if (*ttyfd < 0) {
375       if (PL_dowarn)
376 	warn("IO::Tty::open_slave(nonfatal): open(%.200s): %.100s",
377 	     namebuf, strerror(errno));
378       close(*ptyfd);
379       return 0;		/* too bad, couldn't open slave side */
380     }
381 
382 #if defined (I_PUSH)
383     /*
384      * Push appropriate streams modules for Solaris pty(7).
385      * HP-UX pty(7) doesn't have ttcompat module.
386      * We simply try to push all relevant modules but warn only on
387      * those platforms we know these are required.
388      */
389 #if PTY_DEBUG
390     if (print_debug)
391       fprintf(stderr, "trying to I_PUSH ptem...\n");
392 #endif
393     if (ioctl(*ttyfd, I_PUSH, "ptem") < 0)
394 #if defined (__solaris) || defined(__hpux)
395 	if (PL_dowarn)
396 	    warn("IO::Tty::pty_allocate: ioctl I_PUSH ptem: %.100s", strerror(errno))
397 #endif
398 	      ;
399 
400 #if PTY_DEBUG
401     if (print_debug)
402       fprintf(stderr, "trying to I_PUSH ldterm...\n");
403 #endif
404     if (ioctl(*ttyfd, I_PUSH, "ldterm") < 0)
405 #if defined (__solaris) || defined(__hpux)
406 	if (PL_dowarn)
407 	    warn("IO::Tty::pty_allocate: ioctl I_PUSH ldterm: %.100s", strerror(errno))
408 #endif
409 	      ;
410 
411 #if PTY_DEBUG
412     if (print_debug)
413       fprintf(stderr, "trying to I_PUSH ttcompat...\n");
414 #endif
415     if (ioctl(*ttyfd, I_PUSH, "ttcompat") < 0)
416 #if defined (__solaris)
417 	if (PL_dowarn)
418 	    warn("IO::Tty::pty_allocate: ioctl I_PUSH ttcompat: %.100s", strerror(errno))
419 #endif
420 	      ;
421 #endif /* I_PUSH */
422 
423     /* finally we make sure the filedescriptors are > 2 to avoid
424        problems with stdin/out/err.  This can happen if the user
425        closes one of them before allocating a pty and leads to nasty
426        side-effects, so we take a proactive stance here.  Normally I
427        would say "Those who mess with stdin/out/err shall bear the
428        consequences to the fullest" but hey, I'm a nice guy... ;O) */
429 
430     make_safe_fd(ptyfd);
431     make_safe_fd(ttyfd);
432 
433     return 1;
434 }
435 
436 /*
437  * Allocates and opens a pty.  Returns 0 if no pty could be allocated, or
438  * nonzero if a pty was successfully allocated.  On success, open file
439  * descriptors for the pty and tty sides and the name of the tty side are
440  * returned (the buffer must be able to hold at least 64 characters).
441  *
442  * Instead of trying just one method we go through all available
443  * methods until we get a positive result.
444  */
445 
446 static int
allocate_pty(int * ptyfd,int * ttyfd,char * namebuf,int namebuflen)447 allocate_pty(int *ptyfd, int *ttyfd, char *namebuf, int namebuflen)
448 {
449     *ptyfd = -1;
450     *ttyfd = -1;
451     namebuf[0] = 0;
452 
453     /*
454      * first we try to get a master device
455      */
456     do { /* we use do{}while(0) and break instead of goto */
457 
458 #if defined(HAVE__GETPTY)
459 	/* _getpty(3) for SGI Irix */
460 	{
461 	    char *slave;
462 	    mysig_t old_signal;
463 
464 #if PTY_DEBUG
465 	    if (print_debug)
466 	      fprintf(stderr, "trying _getpty()...\n");
467 #endif
468 	    /* _getpty spawns a suid prog, so don't ignore SIGCHLD */
469     	    old_signal = mysignal(SIGCHLD, SIG_DFL);
470 	    slave = _getpty(ptyfd, O_RDWR, 0622, 0);
471 	    mysignal(SIGCHLD, old_signal);
472 
473 	    if (slave != NULL) {
474 	        if (strlcpy(namebuf, slave, namebuflen) >= namebuflen) {
475 		  warn("ERROR: pty_allocate: ttyname truncated");
476 		  return 0;
477 		}
478 		if (open_slave(ptyfd, ttyfd, namebuf, namebuflen))
479 		    break;
480 		close(*ptyfd);
481 		*ptyfd = -1;
482 	    } else {
483 		if (PL_dowarn)
484 		    warn("pty_allocate(nonfatal): _getpty(): %.100s", strerror(errno));
485 		*ptyfd = -1;
486 	    }
487 	}
488 #endif
489 
490 #if defined(HAVE_PTSNAME) || defined(HAVE_PTSNAME_R)
491 /* we don't need to try these if we don't have a way to get the pty names */
492 
493 #if defined(HAVE_POSIX_OPENPT)
494 #if PTY_DEBUG
495 	if (print_debug)
496 	  fprintf(stderr, "trying posix_openpt()...\n");
497 #endif
498 	*ptyfd = posix_openpt(O_RDWR|O_NOCTTY);
499 	if (*ptyfd >= 0 && open_slave(ptyfd, ttyfd, namebuf, namebuflen))
500 	    break;		/* got one */
501 	if (PL_dowarn)
502 	    warn("pty_allocate(nonfatal): posix_openpt(): %.100s", strerror(errno));
503 #endif /* defined(HAVE_POSIX_OPENPT) */
504 
505 #if defined(HAVE_GETPT)
506 	/* glibc defines this */
507 #if PTY_DEBUG
508 	if (print_debug)
509 	  fprintf(stderr, "trying getpt()...\n");
510 #endif
511 	*ptyfd = getpt();
512 	if (*ptyfd >= 0 && open_slave(ptyfd, ttyfd, namebuf, namebuflen))
513 	    break;		/* got one */
514 	if (PL_dowarn)
515 	    warn("pty_allocate(nonfatal): getpt(): %.100s", strerror(errno));
516 #endif /* defined(HAVE_GETPT) */
517 
518 #if defined(HAVE_OPENPTY)
519 	/* openpty(3) exists in a variety of OS'es, but due to it's
520 	 * broken interface (no maxlen to slavename) we'll only use it
521 	 * to create the tty/pty pair and rely on ptsname to get the
522 	 * name.  */
523 	{
524 	    mysig_t old_signal;
525 	    int ret;
526 
527 #if PTY_DEBUG
528 	    if (print_debug)
529 	      fprintf(stderr, "trying openpty()...\n");
530 #endif
531 	    old_signal = mysignal(SIGCHLD, SIG_DFL);
532 	    ret = openpty(ptyfd, ttyfd, NULL, NULL, NULL);
533 	    mysignal(SIGCHLD, old_signal);
534 	    if (ret >= 0 && *ptyfd >= 0) {
535 		if (open_slave(ptyfd, ttyfd, namebuf, namebuflen))
536 		    break;
537 	    }
538 	    *ptyfd = -1;
539 	    *ttyfd = -1;
540 	    if (PL_dowarn)
541 		warn("pty_allocate(nonfatal): openpty(): %.100s", strerror(errno));
542 	}
543 #endif /* defined(HAVE_OPENPTY) */
544 
545 	/*
546 	 * now try various cloning devices
547 	 */
548 
549 #if defined(HAVE_DEV_PTMX)
550 #if PTY_DEBUG
551 	if (print_debug)
552 	  fprintf(stderr, "trying /dev/ptmx...\n");
553 #endif
554 
555 	*ptyfd = open("/dev/ptmx", O_RDWR | O_NOCTTY);
556 	if (*ptyfd >= 0 && open_slave(ptyfd, ttyfd, namebuf, namebuflen))
557 	    break;
558 	if (PL_dowarn)
559 	    warn("pty_allocate(nonfatal): open(/dev/ptmx): %.100s", strerror(errno));
560 #endif /* HAVE_DEV_PTMX */
561 
562 #if defined(HAVE_DEV_PTYM_CLONE)
563 #if PTY_DEBUG
564 	if (print_debug)
565 	  fprintf(stderr, "trying /dev/ptym/clone...\n");
566 #endif
567 
568 	*ptyfd = open("/dev/ptym/clone", O_RDWR | O_NOCTTY);
569 	if (*ptyfd >= 0 && open_slave(ptyfd, ttyfd, namebuf, namebuflen))
570 	    break;
571 	if (PL_dowarn)
572 	    warn("pty_allocate(nonfatal): open(/dev/ptym/clone): %.100s", strerror(errno));
573 #endif /* HAVE_DEV_PTYM_CLONE */
574 
575 #if defined(HAVE_DEV_PTC)
576 	/* AIX-style pty code. */
577 #if PTY_DEBUG
578 	if (print_debug)
579 	  fprintf(stderr, "trying /dev/ptc...\n");
580 #endif
581 
582 	*ptyfd = open("/dev/ptc", O_RDWR | O_NOCTTY);
583 	if (*ptyfd >= 0 && open_slave(ptyfd, ttyfd, namebuf, namebuflen))
584 	    break;
585 	if (PL_dowarn)
586 	    warn("pty_allocate(nonfatal): open(/dev/ptc): %.100s", strerror(errno));
587 #endif /* HAVE_DEV_PTC */
588 
589 #if defined(HAVE_DEV_PTMX_BSD)
590 #if PTY_DEBUG
591 	if (print_debug)
592 	  fprintf(stderr, "trying /dev/ptmx_bsd...\n");
593 #endif
594 	*ptyfd = open("/dev/ptmx_bsd", O_RDWR | O_NOCTTY);
595 	if (*ptyfd >= 0 && open_slave(ptyfd, ttyfd, namebuf, namebuflen))
596 	    break;
597 	if (PL_dowarn)
598 	    warn("pty_allocate(nonfatal): open(/dev/ptmx_bsd): %.100s", strerror(errno));
599 #endif /* HAVE_DEV_PTMX_BSD */
600 
601 #endif /* !defined(HAVE_PTSNAME) && !defined(HAVE_PTSNAME_R) */
602 
603 	/*
604 	 * we still don't have a pty, so try some oldfashioned stuff,
605 	 * looking for a pty/tty pair ourself.
606 	 */
607 
608 #if defined(_CRAY)
609 	{
610 	    char buf[64];
611 	    int i;
612 	    int highpty;
613 
614 #ifdef _SC_CRAY_NPTY
615 	    highpty = sysconf(_SC_CRAY_NPTY);
616 	    if (highpty == -1)
617 		highpty = 128;
618 #else
619 	    highpty = 128;
620 #endif
621 #if PTY_DEBUG
622 	    if (print_debug)
623 	      fprintf(stderr, "trying CRAY /dev/pty/???...\n");
624 #endif
625 	    for (i = 0; i < highpty; i++) {
626 		sprintf(buf, "/dev/pty/%03d", i);
627 		*ptyfd = open(buf, O_RDWR | O_NOCTTY);
628 		if (*ptyfd < 0)
629 		    continue;
630 		sprintf(buf, "/dev/ttyp%03d", i);
631 		if (strlcpy(namebuf, buf, namebuflen) >= namebuflen) {
632 		  warn("ERROR: pty_allocate: ttyname truncated");
633 		  return 0;
634 		}
635 		break;
636 	    }
637 	    if (*ptyfd >= 0 && open_slave(ptyfd, ttyfd, namebuf, namebuflen))
638 		break;
639 	}
640 #endif
641 
642 #if defined(HAVE_DEV_PTYM)
643 	{
644 	    /* HPUX */
645 	    char buf[64];
646 	    char tbuf[64];
647 	    int i;
648 	    struct stat sb;
649 	    const char *ptymajors = "abcefghijklmnopqrstuvwxyz";
650 	    const char *ptyminors = "0123456789abcdef";
651 	    int num_minors = strlen(ptyminors);
652 	    int num_ptys = strlen(ptymajors) * num_minors;
653 
654 #if PTY_DEBUG
655 	    if (print_debug)
656 	      fprintf(stderr, "trying HPUX /dev/ptym/pty[a-ce-z][0-9a-f]...\n");
657 #endif
658 	    /* try /dev/ptym/pty[a-ce-z][0-9a-f] */
659 	    for (i = 0; i < num_ptys; i++) {
660 		sprintf(buf, "/dev/ptym/pty%c%c",
661 			 ptymajors[i / num_minors],
662 			 ptyminors[i % num_minors]);
663 		sprintf(tbuf, "/dev/pty/tty%c%c",
664 			 ptymajors[i / num_minors],
665 			 ptyminors[i % num_minors]);
666 		if (strlcpy(namebuf, tbuf, namebuflen) >= namebuflen) {
667 		  warn("ERROR: pty_allocate: ttyname truncated");
668 		  return 0;
669 		}
670 		if(stat(buf, &sb))
671 		    break;	/* file does not exist, skip rest */
672 		*ptyfd = open(buf, O_RDWR | O_NOCTTY);
673 		if (*ptyfd >= 0 && open_slave(ptyfd, ttyfd, namebuf, namebuflen))
674 		    break;
675 		namebuf[0] = 0;
676 	    }
677 	    if (*ptyfd >= 0)
678 		break;
679 
680 #if PTY_DEBUG
681 	    if (print_debug)
682 	      fprintf(stderr, "trying HPUX /dev/ptym/pty[a-ce-z][0-9][0-9]...\n");
683 #endif
684 	    /* now try /dev/ptym/pty[a-ce-z][0-9][0-9] */
685 	    num_minors = 100;
686 	    num_ptys = strlen(ptymajors) * num_minors;
687 	    for (i = 0; i < num_ptys; i++) {
688 		sprintf(buf, "/dev/ptym/pty%c%02d",
689 			 ptymajors[i / num_minors],
690 			 i % num_minors);
691 		sprintf(tbuf, "/dev/pty/tty%c%02d",
692 			 ptymajors[i / num_minors], i % num_minors);
693 		if (strlcpy(namebuf, tbuf, namebuflen) >= namebuflen) {
694 		  warn("ERROR: pty_allocate: ttyname truncated");
695 		  return 0;
696 		}
697 
698 		if(stat(buf, &sb))
699 		    break;	/* file does not exist, skip rest */
700 		*ptyfd = open(buf, O_RDWR | O_NOCTTY);
701 		if (*ptyfd >= 0 && open_slave(ptyfd, ttyfd, namebuf, namebuflen))
702 		    break;
703 		namebuf[0] = 0;
704 	    }
705 	    if (*ptyfd >= 0)
706 		break;
707 	}
708 #endif /* HAVE_DEV_PTYM */
709 
710 	{
711 	    /* BSD-style pty code. */
712 	    char buf[64];
713 	    char tbuf[64];
714 	    int i;
715 	    const char *ptymajors = "pqrstuvwxyzabcdefghijklmnoABCDEFGHIJKLMNOPQRSTUVWXYZ";
716 	    const char *ptyminors = "0123456789abcdefghijklmnopqrstuv";
717 	    int num_minors = strlen(ptyminors);
718 	    int num_ptys = strlen(ptymajors) * num_minors;
719 
720 #if PTY_DEBUG
721 	    if (print_debug)
722 	      fprintf(stderr, "trying BSD /dev/pty??...\n");
723 #endif
724 	    for (i = 0; i < num_ptys; i++) {
725 		sprintf(buf, "/dev/pty%c%c",
726 			ptymajors[i / num_minors],
727 			ptyminors[i % num_minors]);
728 		sprintf(tbuf, "/dev/tty%c%c",
729 			ptymajors[i / num_minors],
730 			ptyminors[i % num_minors]);
731 		if (strlcpy(namebuf, tbuf, namebuflen) >= namebuflen) {
732 		  warn("ERROR: pty_allocate: ttyname truncated");
733 		  return 0;
734 		}
735 		*ptyfd = open(buf, O_RDWR | O_NOCTTY);
736 		if (*ptyfd >= 0 && open_slave(ptyfd, ttyfd, namebuf, namebuflen))
737 		    break;
738 
739 		/* Try SCO style naming */
740 		sprintf(buf, "/dev/ptyp%d", i);
741 		sprintf(tbuf, "/dev/ttyp%d", i);
742 		if (strlcpy(namebuf, tbuf, namebuflen) >= namebuflen) {
743 		  warn("ERROR: pty_allocate: ttyname truncated");
744 		  return 0;
745 		}
746 		*ptyfd = open(buf, O_RDWR | O_NOCTTY);
747 		if (*ptyfd >= 0 && open_slave(ptyfd, ttyfd, namebuf, namebuflen))
748 		    break;
749 
750 		/* Try BeOS style naming */
751 		sprintf(buf, "/dev/pt/%c%c",
752 			ptymajors[i / num_minors],
753 			ptyminors[i % num_minors]);
754 		sprintf(tbuf, "/dev/tt/%c%c",
755 			ptymajors[i / num_minors],
756 			ptyminors[i % num_minors]);
757 		if (strlcpy(namebuf, tbuf, namebuflen) >= namebuflen) {
758 		  warn("ERROR: pty_allocate: ttyname truncated");
759 		  return 0;
760 		}
761 		*ptyfd = open(buf, O_RDWR | O_NOCTTY);
762 		if (*ptyfd >= 0 && open_slave(ptyfd, ttyfd, namebuf, namebuflen))
763 		    break;
764 
765 		/* Try z/OS style naming */
766 		sprintf(buf, "/dev/ptyp%04d", i);
767 		sprintf(tbuf, "/dev/ttyp%04d", i);
768 		if (strlcpy(namebuf, tbuf, namebuflen) >= namebuflen) {
769 		  warn("ERROR: pty_allocate: ttyname truncated");
770 		  return 0;
771 		}
772 		*ptyfd = open(buf, O_RDWR | O_NOCTTY);
773 		if (*ptyfd >= 0 && open_slave(ptyfd, ttyfd, namebuf, namebuflen))
774 		    break;
775 
776 		namebuf[0] = 0;
777 	    }
778 	    if (*ptyfd >= 0)
779 		break;
780 	}
781 
782     } while (0);
783 
784     if (*ptyfd < 0 || namebuf[0] == 0)
785 	return 0;		/* we failed to allocate one */
786 
787     return 1;			/* whew, finally finished successfully */
788 } /* end allocate_pty */
789 
790 
791 
792 MODULE = IO::Tty	PACKAGE = IO::Pty
793 
794 PROTOTYPES: DISABLE
795 
796 void
797 pty_allocate()
798     INIT:
799 	int ptyfd, ttyfd, ret;
800 	char name[256];
801 #ifdef PTY_DEBUG
802         SV *debug;
803 #endif
804 
805     PPCODE:
806 #ifdef PTY_DEBUG
807         debug = perl_get_sv("IO::Tty::DEBUG", FALSE);
808   	if (SvTRUE(debug))
809           print_debug = 1;
810 #endif
811 	ret = allocate_pty(&ptyfd, &ttyfd, name, sizeof(name));
812 	if (ret) {
813 	    name[sizeof(name)-1] = 0;
814 	    EXTEND(SP,3);
815 	    PUSHs(sv_2mortal(newSViv(ptyfd)));
816 	    PUSHs(sv_2mortal(newSViv(ttyfd)));
817 	    PUSHs(sv_2mortal(newSVpv(name, strlen(name))));
818         } else {
819 	    /* empty list */
820 	}
821 
822 
823 MODULE = IO::Tty	PACKAGE = IO::Tty
824 
825 char *
826 ttyname(handle)
827 InOutStream handle
828     CODE:
829 #ifdef HAVE_TTYNAME
830 	if (handle)
831 	    RETVAL = ttyname(PerlIO_fileno(handle));
832 	else {
833 	    RETVAL = Nullch;
834 	    errno = EINVAL;
835 	}
836 #else
837 	warn("IO::Tty::ttyname not implemented on this architecture");
838 	RETVAL = Nullch;
839 #endif
840     OUTPUT:
841 	RETVAL
842 
843 SV *
844 pack_winsize(row, col, xpixel = 0, ypixel = 0)
845 	int row
846 	int col
847 	int xpixel
848 	int ypixel
849     INIT:
850 	struct winsize ws;
851     CODE:
852 	ws.ws_row = row;
853 	ws.ws_col = col;
854 	ws.ws_xpixel = xpixel;
855 	ws.ws_ypixel = ypixel;
856 	RETVAL = newSVpvn((char *)&ws, sizeof(ws));
857     OUTPUT:
858 	RETVAL
859 
860 void
861 unpack_winsize(winsize)
862 	SV *winsize;
863     INIT:
864 	struct winsize ws;
865     PPCODE:
866 	if(SvCUR(winsize) != sizeof(ws))
867 	    croak("IO::Tty::unpack_winsize(): Bad arg length - got %d, expected %d",
868 		SvCUR(winsize), sizeof(ws));
869 	Copy(SvPV_nolen(winsize), &ws, sizeof(ws), char);
870 	EXTEND(SP, 4);
871 	PUSHs(sv_2mortal(newSViv(ws.ws_row)));
872 	PUSHs(sv_2mortal(newSViv(ws.ws_col)));
873 	PUSHs(sv_2mortal(newSViv(ws.ws_xpixel)));
874 	PUSHs(sv_2mortal(newSViv(ws.ws_ypixel)));
875 
876 
877 BOOT:
878 {
879   HV *stash;
880   SV *config;
881 
882   stash = gv_stashpv("IO::Tty::Constant", TRUE);
883   config = perl_get_sv("IO::Tty::CONFIG", TRUE);
884 #include "xssubs.c"
885 }
886 
887 
888