bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: portable openpty, forkpty


From: Simon Josefsson
Subject: Re: portable openpty, forkpty
Date: Sun, 25 Apr 2010 12:04:17 +0200
User-agent: Gnus/5.110011 (No Gnus v0.11) Emacs/23.1 (gnu/linux)

Bruno Haible <address@hidden> writes:

o> Hi Simon,
>
>> Now the test-openpty self-test hangs on my Mac OS X machine, and the
>> process doesn't even go away from the process table after a 'kill -9'.
>> Here is a gdb session, which suggests it hangs when shutting down the
>> process:
>
> Strange. This program now uses the MacOS built-in openpty function.
> I see no problem on MacOS X 10.5.
>
> You could try to isolate the problem by adding explicit
>   printf ("%d\n", close (master));
>   printf ("%d\n", close (slave));
> statements before the "return 0;". Other than that, it looks like the simple
> test program is triggering a bug in MacOS X?

Yes.

The stand-alone program below reproduces the Mac OS X bug:

espresso:~ jas$ cc -Wall -o test-openpty test-openpty.c
espresso:~ jas$ ./test-openpty master
Closing master: 0
espresso:~ jas$ ./test-openpty slave
^C
espresso:~ jas$ ./test-openpty
^C^Z^C^Z^C^Z^C^Z

I have to close the SSH connection to resume control, and logging in
back in, the process is stuck and 'kill -9' doesn't kill it.

This is on a fully updated Mac OS X 10.4.11 (PPC) PowerBook G4 laptop.
If anyone else has access to an Intel 10.4.x Mac machine, it would be
interesting to attempt to reproduce this.

Bruno, can you confirm that this program works fine on your 10.5
machine?

How about this patch:

diff --git a/doc/glibc-functions/openpty.texi b/doc/glibc-functions/openpty.texi
index 31fdca0..f345f7d 100644
--- a/doc/glibc-functions/openpty.texi
+++ b/doc/glibc-functions/openpty.texi
@@ -30,4 +30,8 @@ Portability problems not fixed by Gnulib:
 @item
 This function is missing on some platforms:
 mingw.
address@hidden
+On Mac OS X 10.4.11 (PowerPC) the application needs to close the
+master file descriptor, otherwise the process hangs and is not
+possible to kill.
 @end itemize

/Simon
/* Test of pty.h and openpty function.
   Copyright (C) 2009, 2010 Free Software Foundation, Inc.

   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 3 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, see <http://www.gnu.org/licenses/>.  */

/* Written by Simon Josefsson <address@hidden>, 2009
   and Bruno Haible <address@hidden>, 2010.  */

#include <util.h>

#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>

int
main (int argc, char *argv[])
{
  {
    int master;
    int slave;

    /* Open a pseudo-terminal, as a master-slave pair.  */
    {
      int res = openpty (&master, &slave, NULL, NULL, NULL);
      if (res != 0)
        {
          fprintf (stderr, "openpty returned %d\n", res);
          return 1;
        }
    }

    /* Set the terminal characteristics.
       On Linux or MacOS X, they can be set on either the master or the slave;
       the effect is the same.  But on Solaris, they have to be set on the
       master; tcgetattr on the slave fails.  */
    {
      int tcfd = slave; /* You can try  tcfd = master;  here.  */
      struct termios attributes;

      if (tcgetattr (tcfd, &attributes) < 0)
        {
          fprintf (stderr, "tcgetattr failed\n");
          return 1;
        }
      /* Enable canonical processing, including erase.  */
      attributes.c_lflag |= ECHO | ICANON | ECHOE;
      attributes.c_cc[VERASE] = '\177';
      if (tcsetattr (tcfd, TCSANOW, &attributes) < 0)
        {
          fprintf (stderr, "tcsetattr failed\n");
          return 1;
        }
    }

    /* Write into the master side.  */
    {
      static const char input[] = "Hello worst\177\177ld!\n";

      if (write (master, input, strlen (input)) < (int) strlen (input))
        {
          fprintf (stderr, "write failed\n");
          return 1;
        }
    }

    /* Read from the slave side.  */
    {
      char buf[100];
      int res = read (slave, buf, sizeof (buf));
      static const char expected[] = "Hello world!\n";

      if (res < 0)
        {
          fprintf (stderr, "read failed\n");
          return 1;
        }
      if (!(res == strlen (expected)
            && memcmp (buf, expected, strlen (expected)) == 0))
        {
          fprintf (stderr, "read result unexpected\n");
          return 1;
        }
    }

    if (argc >= 1 && strcmp (argv[1], "master") == 0)
      printf ("Closing master: %d\n", close (master));
    if (argc >= 1 && strcmp (argv[1], "slave") == 0)
      printf ("Closing slave: %d\n", close (slave));
  }

  return 0;
}

reply via email to

[Prev in Thread] Current Thread [Next in Thread]