emacs-devel
[Top][All Lists]
Advanced

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

Re: address@hidden: Re: address@hidden: emacs-21.2.90 on HP 11.0]]


From: Stef Van Vlierberghe
Subject: Re: address@hidden: Re: address@hidden: emacs-21.2.90 on HP 11.0]]
Date: Fri, 12 Jul 2002 23:52:13 +0200

Richard Stallman writes:
 >     On hp-ux, ioctl TIOCGPGRP always fails with errno ENOTTY (this is normal,
 >     explained in the USG part of create_process).
 > 
 > I cannot find that explanation, and I am not sure what "the USG part
 > of create_process" refers to.  Would you please show me the specific
 > code you mean?  I'm trying to understand this.  In particular,
 > I wonder why this code
 > 
 >      if (!NILP (p->subtty))
 >        err = ioctl (XFASTINT (p->subtty), TIOCGPGRP, &gid);
 > 
 > does not get the right value.
 > 
 > Does err get set to -1 when the ioctl fails?

Below you will find the source code of create_process, where
I have put a !!! in front of the lines that are executed in
create_process when we use M-x shell (I'm a big fan of the
emacs macro's and the gdb-mode). 

This explains how we have NILP (p->subtty), and hence the 
'else' part of the 'if' you mentioned above is of interest :

        else
          err = ioctl (XINT (p->infd), TIOCGPGRP, &gid);

Here err is returned -1 and errno is 25=ENOTTY, which makes
sense because p->infd was set to inchannel, which is a PTY
returned from allocate_pty (); (I thought a pty and a tty were
like two ends of the same comms channel, but I guess this 
difference matters for the ioctl).

This failure passes by unnoticed, because the err is not tested
(unless if this #ifdef pfa kicks in, which does such a test and
 might already have worked around the same problem in a very
 awkward way).

So, if the uninitialized gid is initially -1, then the error
is discovered accidentally, no_pgrp = 1;, and this leads us
to the kill (XFASTINT (p->pid), signo);, which send the signal
to the shell, which does not interrupt the cat.

If the uninitialized gid is initially 1, then the error is not
discovered, gid is then set to -1 but that won't matter anymore,
and we get to     ioctl (XINT (p->infd), TIOCSIGSEND, signo);
which does indeed kill the cat.

Conclusion : setting gid initially to 1 is a workaround for us,
but the way it works is major voodoo.

P.S. neither of us understood what a USG system is, why HP is
one, and why somebody believes HP can't have a pty/tty creation
during a fork()/exec() construct, surely a program like xterm must
accomplish this. 

Best regards.
=============================================================
void
create_process (process, new_argv, current_dir)
     Lisp_Object process;
     char **new_argv;
     Lisp_Object current_dir;
{
  int pid, inchannel, outchannel;
  int sv[2];
#ifdef POSIX_SIGNALS
  sigset_t procmask;
  sigset_t blocked;
  struct sigaction sigint_action;
  struct sigaction sigquit_action;
#ifdef AIX
  struct sigaction sighup_action;
#endif
#else /* !POSIX_SIGNALS */
#if 0
#ifdef SIGCHLD
  SIGTYPE (*sigchld)();
#endif
#endif /* 0 */
#endif /* !POSIX_SIGNALS */
  /* Use volatile to protect variables from being clobbered by longjmp.  */
  volatile int forkin, forkout;
!!!  volatile int pty_flag = 0;
#ifndef USE_CRT_DLL
  extern char **environ;
#endif
!!!  Lisp_Object buffer = XPROCESS (process)->buffer;

!!!  inchannel = outchannel = -1;

#ifdef HAVE_PTYS
!!!  if (!NILP (Vprocess_connection_type))
!!!    outchannel = inchannel = allocate_pty ();

!!!  if (inchannel >= 0)
    {
#ifndef USG 
      /* On USG systems it does not work to open the pty's tty here
               and then close and reopen it in the child.  */
#ifdef O_NOCTTY
      /* Don't let this terminal become our controlling terminal
         (in case we don't have one).  */
      forkout = forkin = emacs_open (pty_name, O_RDWR | O_NOCTTY, 0);
#else
      forkout = forkin = emacs_open (pty_name, O_RDWR, 0);
#endif
      if (forkin < 0)
        report_file_error ("Opening pty", Qnil);
#else
!!!      forkin = forkout = -1;
#endif /* not USG */
!!!      pty_flag = 1;
    }
  else
#endif /* HAVE_PTYS */
#ifdef SKTPAIR
    {
      if (socketpair (AF_UNIX, SOCK_STREAM, 0, sv) < 0)
        report_file_error ("Opening socketpair", Qnil);
      outchannel = inchannel = sv[0];
      forkout = forkin = sv[1];
    }
#else /* not SKTPAIR */
    {
      int tem;
      tem = pipe (sv);
      if (tem < 0)
        report_file_error ("Creating pipe", Qnil);
      inchannel = sv[0];
      forkout = sv[1];
      tem = pipe (sv);
      if (tem < 0)
        {
          emacs_close (inchannel);
          emacs_close (forkout);
          report_file_error ("Creating pipe", Qnil);
        }
      outchannel = sv[1];
      forkin = sv[0];
    }
#endif /* not SKTPAIR */

#if 0
  /* Replaced by close_process_descs */
  set_exclusive_use (inchannel);
  set_exclusive_use (outchannel);
#endif

/* Stride people say it's a mystery why this is needed
   as well as the O_NDELAY, but that it fails without this.  */
#if defined (STRIDE) || (defined (pfa) && defined (HAVE_PTYS))
  {
    int one = 1;
    ioctl (inchannel, FIONBIO, &one);
  }
#endif

#ifdef O_NONBLOCK
!!!  fcntl (inchannel, F_SETFL, O_NONBLOCK);
!!!  fcntl (outchannel, F_SETFL, O_NONBLOCK);
#else
#ifdef O_NDELAY
  fcntl (inchannel, F_SETFL, O_NDELAY);
  fcntl (outchannel, F_SETFL, O_NDELAY);
#endif
#endif

  /* Record this as an active process, with its channels.
     As a result, child_setup will close Emacs's side of the pipes.  */
!!!  chan_process[inchannel] = process;
!!!  XSETINT (XPROCESS (process)->infd, inchannel);
!!!  XSETINT (XPROCESS (process)->outfd, outchannel);
  /* Record the tty descriptor used in the subprocess.  */
!!!  if (forkin < 0)
!!!    XPROCESS (process)->subtty = Qnil;
  else
    XSETFASTINT (XPROCESS (process)->subtty, forkin);
!!!  XPROCESS (process)->pty_flag = (pty_flag ? Qt : Qnil);
!!!  XPROCESS (process)->status = Qrun;
!!!  if (!proc_decode_coding_system[inchannel])
!!!    proc_decode_coding_system[inchannel]
      = (struct coding_system *) xmalloc (sizeof (struct coding_system));
!!!  setup_coding_system (XPROCESS (process)->decode_coding_system,
                       proc_decode_coding_system[inchannel]);
!!!  if (!proc_encode_coding_system[outchannel])
!!!    proc_encode_coding_system[outchannel]
      = (struct coding_system *) xmalloc (sizeof (struct coding_system));
!!!  setup_coding_system (XPROCESS (process)->encode_coding_system,
                       proc_encode_coding_system[outchannel]);

  /* Delay interrupts until we have a chance to store
     the new fork's pid in its process structure */
#ifdef POSIX_SIGNALS
!!!  sigemptyset (&blocked);
#ifdef SIGCHLD
!!!  sigaddset (&blocked, SIGCHLD);
#endif
#ifdef HAVE_VFORK
  /* On many hosts (e.g. Solaris 2.4), if a vforked child calls `signal',
     this sets the parent's signal handlers as well as the child's.
     So delay all interrupts whose handlers the child might munge,
     and record the current handlers so they can be restored later.  */
  sigaddset (&blocked, SIGINT );  sigaction (SIGINT , 0, &sigint_action );
  sigaddset (&blocked, SIGQUIT);  sigaction (SIGQUIT, 0, &sigquit_action);
#ifdef AIX
  sigaddset (&blocked, SIGHUP );  sigaction (SIGHUP , 0, &sighup_action );
#endif
#endif /* HAVE_VFORK */
!!!  sigprocmask (SIG_BLOCK, &blocked, &procmask);
#else /* !POSIX_SIGNALS */
#ifdef SIGCHLD
#ifdef BSD4_1
  sighold (SIGCHLD);
#else /* not BSD4_1 */
#if defined (BSD_SYSTEM) || defined (UNIPLUS) || defined (HPUX)
  sigsetmask (sigmask (SIGCHLD));
#else /* ordinary USG */
#if 0
  sigchld_deferred = 0;
  sigchld = signal (SIGCHLD, create_process_sigchld);
#endif
#endif /* ordinary USG */
#endif /* not BSD4_1 */
#endif /* SIGCHLD */
#endif /* !POSIX_SIGNALS */

!!!  FD_SET (inchannel, &input_wait_mask);
!!!  FD_SET (inchannel, &non_keyboard_wait_mask);
!!!  if (inchannel > max_process_desc)
!!!    max_process_desc = inchannel;

  /* Until we store the proper pid, enable sigchld_handler
     to recognize an unknown pid as standing for this process.
     It is very important not to let this `marker' value stay
     in the table after this function has returned; if it does
     it might cause call-process to hang and subsequent asynchronous
     processes to get their return values scrambled.  */
!!!  XSETINT (XPROCESS (process)->pid, -1);

!!!  BLOCK_INPUT;
  
  {
    /* child_setup must clobber environ on systems with true vfork.
       Protect it from permanent change.  */
!!!    char **save_environ = environ;

!!!    current_dir = ENCODE_FILE (current_dir);

#ifndef WINDOWSNT
!!!    pid = vfork ();
!!!    if (pid == 0)
#endif /* not WINDOWSNT */
      {
        int xforkin = forkin;
        int xforkout = forkout;

#if 0 /* This was probably a mistake--it duplicates code later on,
         but fails to handle all the cases.  */
        /* Make sure SIGCHLD is not blocked in the child.  */
        sigsetmask (SIGEMPTYMASK);
#endif

        /* Make the pty be the controlling terminal of the process.  */
#ifdef HAVE_PTYS
        /* First, disconnect its current controlling terminal.  */
#ifdef HAVE_SETSID
        /* We tried doing setsid only if pty_flag, but it caused
           process_set_signal to fail on SGI when using a pipe.  */
        setsid ();
        /* Make the pty's terminal the controlling terminal.  */
        if (pty_flag)
          {
#ifdef TIOCSCTTY
            /* We ignore the return value
               because address@hidden says that is necessary on Linux.  */
            ioctl (xforkin, TIOCSCTTY, 0);
#endif
          }
#else /* not HAVE_SETSID */
#ifdef USG
        /* It's very important to call setpgrp here and no time
           afterwards.  Otherwise, we lose our controlling tty which
           is set when we open the pty. */
        setpgrp ();
#endif /* USG */
#endif /* not HAVE_SETSID */
#if defined (HAVE_TERMIOS) && defined (LDISC1)
        if (pty_flag && xforkin >= 0)
          {
            struct termios t;
            tcgetattr (xforkin, &t);
            t.c_lflag = LDISC1;
            if (tcsetattr (xforkin, TCSANOW, &t) < 0)
              emacs_write (1, "create_process/tcsetattr LDISC1 failed\n", 39);
          }
#else
#if defined (NTTYDISC) && defined (TIOCSETD)
        if (pty_flag && xforkin >= 0)
          {
            /* Use new line discipline.  */
            int ldisc = NTTYDISC;
            ioctl (xforkin, TIOCSETD, &ldisc);
          }
#endif
#endif
#ifdef TIOCNOTTY 
        /* In 4.3BSD, the TIOCSPGRP bug has been fixed, and now you
           can do TIOCSPGRP only to the process's controlling tty.  */
        if (pty_flag)
          {
            /* I wonder: would just ioctl (0, TIOCNOTTY, 0) work here? 
               I can't test it since I don't have 4.3.  */
            int j = emacs_open ("/dev/tty", O_RDWR, 0);
            ioctl (j, TIOCNOTTY, 0);
            emacs_close (j);
#ifndef USG
            /* In order to get a controlling terminal on some versions
               of BSD, it is necessary to put the process in pgrp 0
               before it opens the terminal.  */
#ifdef HAVE_SETPGID
            setpgid (0, 0);
#else
            setpgrp (0, 0);
#endif
#endif
          }
#endif /* TIOCNOTTY */

#if !defined (RTU) && !defined (UNIPLUS) && !defined (DONT_REOPEN_PTY)
/*** There is a suggestion that this ought to be a
     conditional on TIOCSPGRP,
     or !(defined (HAVE_SETSID) && defined (TIOCSCTTY)).
     Trying the latter gave the wrong results on Debian GNU/Linux 1.1;
     that system does seem to need this code, even though
     both HAVE_SETSID and TIOCSCTTY are defined.  */
        /* Now close the pty (if we had it open) and reopen it.
           This makes the pty the controlling terminal of the subprocess.  */
        if (pty_flag)
          {
#ifdef SET_CHILD_PTY_PGRP
            int pgrp = getpid ();
#endif

            /* I wonder if emacs_close (emacs_open (pty_name, ...))
               would work?  */
            if (xforkin >= 0)
              emacs_close (xforkin);
            xforkout = xforkin = emacs_open (pty_name, O_RDWR, 0);

            if (xforkin < 0)
              {
                emacs_write (1, "Couldn't open the pty terminal ", 31);
                emacs_write (1, pty_name, strlen (pty_name));
                emacs_write (1, "\n", 1);
                _exit (1);
              }

#ifdef SET_CHILD_PTY_PGRP
            ioctl (xforkin, TIOCSPGRP, &pgrp);
            ioctl (xforkout, TIOCSPGRP, &pgrp);
#endif
          }
#endif /* not UNIPLUS and not RTU and not DONT_REOPEN_PTY */

#ifdef SETUP_SLAVE_PTY
        if (pty_flag)
          {
            SETUP_SLAVE_PTY;
          }
#endif /* SETUP_SLAVE_PTY */
#ifdef AIX
        /* On AIX, we've disabled SIGHUP above once we start a child on a pty.
           Now reenable it in the child, so it will die when we want it to.  */
        if (pty_flag)
          signal (SIGHUP, SIG_DFL);
#endif
#endif /* HAVE_PTYS */

        signal (SIGINT, SIG_DFL);
        signal (SIGQUIT, SIG_DFL);

        /* Stop blocking signals in the child.  */
#ifdef POSIX_SIGNALS
        sigprocmask (SIG_SETMASK, &procmask, 0);
#else /* !POSIX_SIGNALS */
#ifdef SIGCHLD
#ifdef BSD4_1
        sigrelse (SIGCHLD);
#else /* not BSD4_1 */
#if defined (BSD_SYSTEM) || defined (UNIPLUS) || defined (HPUX)
        sigsetmask (SIGEMPTYMASK);
#else /* ordinary USG */
#if 0
        signal (SIGCHLD, sigchld);
#endif
#endif /* ordinary USG */
#endif /* not BSD4_1 */
#endif /* SIGCHLD */
#endif /* !POSIX_SIGNALS */

        if (pty_flag)
          child_setup_tty (xforkout);
#ifdef WINDOWSNT
        pid = child_setup (xforkin, xforkout, xforkout,
                           new_argv, 1, current_dir);
#else  /* not WINDOWSNT */      
        child_setup (xforkin, xforkout, xforkout,
                     new_argv, 1, current_dir);
#endif /* not WINDOWSNT */
      }
!!!    environ = save_environ;
  }

!!!  UNBLOCK_INPUT;

  /* This runs in the Emacs process.  */
!!!  if (pid < 0)
    {
      if (forkin >= 0)
        emacs_close (forkin);
      if (forkin != forkout && forkout >= 0)
        emacs_close (forkout);
    }
  else
    {
      /* vfork succeeded.  */
!!!      XSETFASTINT (XPROCESS (process)->pid, pid);

#ifdef WINDOWSNT
      register_child (pid, inchannel);
#endif /* WINDOWSNT */

      /* If the subfork execv fails, and it exits,
         this close hangs.  I don't know why.
         So have an interrupt jar it loose.  */
      {
        struct atimer *timer;
        EMACS_TIME offset;
        
!!!     stop_polling ();
!!!     EMACS_SET_SECS_USECS (offset, 1, 0);
!!!     timer = start_atimer (ATIMER_RELATIVE, offset, create_process_1, 0);
        
!!!     XPROCESS (process)->subtty = Qnil;
!!!     if (forkin >= 0)
          emacs_close (forkin);

!!!     cancel_atimer (timer);
!!!     start_polling ();
      }
      
!!!      if (forkin != forkout && forkout >= 0)
        emacs_close (forkout);

#ifdef HAVE_PTYS
!!!      if (pty_flag)
!!!     XPROCESS (process)->tty_name = build_string (pty_name);
      else
#endif
        XPROCESS (process)->tty_name = Qnil;
    }

  /* Restore the signal state whether vfork succeeded or not.
     (We will signal an error, below, if it failed.)  */
#ifdef POSIX_SIGNALS
#ifdef HAVE_VFORK
  /* Restore the parent's signal handlers.  */
  sigaction (SIGINT, &sigint_action, 0);
  sigaction (SIGQUIT, &sigquit_action, 0);
#ifdef AIX
  sigaction (SIGHUP, &sighup_action, 0);
#endif
#endif /* HAVE_VFORK */
  /* Stop blocking signals in the parent.  */
!!!  sigprocmask (SIG_SETMASK, &procmask, 0);
#else /* !POSIX_SIGNALS */
#ifdef SIGCHLD
#ifdef BSD4_1
  sigrelse (SIGCHLD);
#else /* not BSD4_1 */
#if defined (BSD_SYSTEM) || defined (UNIPLUS) || defined (HPUX)
  sigsetmask (SIGEMPTYMASK);
#else /* ordinary USG */
#if 0
  signal (SIGCHLD, sigchld);
  /* Now really handle any of these signals
     that came in during this function.  */
  if (sigchld_deferred)
    kill (getpid (), SIGCHLD);
#endif
#endif /* ordinary USG */
#endif /* not BSD4_1 */
#endif /* SIGCHLD */
#endif /* !POSIX_SIGNALS */

  /* Now generate the error if vfork failed.  */
!!!  if (pid < 0)

-- 
Stef Van Vlierberghe                   Eurocontrol - CFMU room 20115
address@hidden   Raketstraat 96
Tel: +32 2 729 97 32                   B-1130 BRUSSELS
Fax: +32 2 729 90 22                   Belgium



reply via email to

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