Foregrounding, process management, os/exec.Cmd

442 views
Skip to first unread message

James Aguilar

unread,
Jan 21, 2017, 7:07:03 PM1/21/17
to golang-nuts
If you don't know or care about pagers and process management, you can stop reading now.

Background: I have a program that compile my code each time I modify it. It produces logs continuously on a terminal. 

I'm trying to write a go program to wrap this program so that each compile log is opened in a pager, and so that when a new compile starts, the pager for the previous compile is automatically closed. Essentially, I'm trying to change the program from doing (pseudo-shell)

while true; do
  awaitChange
  build
end

to:

while true; do
  awaitChange
  kill $!
  build | less &  # <--- except less should be in the foreground of the terminal
  to_kill=$!
end

There are wrinkles: I don't control the program that executes this loop. So I wrote a go program to process the input and separate it into a series of output buffers based on a regexp. I've gotten it to the point where I can start and kill less, and feed it the separated inputs. 

My problem: less is not behaving as it would if you ran it on the commandline. The arrow keys don't work, ^C either does nothing (on the first run of less) or interrupts the parent program, etc.

I believe my mistake is that I am not correctly putting less into the foreground. I also suspect that I'm not correctly moving my go program back into the foreground each time I kill the child less process. I'm wondering if anyone knows the magical incantation that is required to make this work properly. My current code is here (with the compiler replaced by a random number generator, since it is proprietary to the company I work for). I have marked with TODOs the things I think are not working correctly.


Any ideas what I'm doing wrong here?

Ian Lance Taylor

unread,
Jan 22, 2017, 11:46:04 PM1/22/17
to James Aguilar, golang-nuts
Your program seems to work for me, so I guess I misunderstand what the
problem is.

I ran `go build foo.go; ./foo`. It prints a series of random numbers
from 0 to 99, fed into less. When it prints a number 95 to 99, it
restarts less. When it prints enough numbers to fill my terminal, I
see the `less` prompt (a colon). I can use arrows, space, etc.
Eventually that instance of less is killed and a new one starts.
Hitting ^C just causes `less` to beep. Hitting `q` causes less and
the main program to exit.

What do you see? (Note that you shouldn't use `go run` for a program
like this.)

Ian

Peter Waller

unread,
Jan 23, 2017, 6:25:33 AM1/23/17
to James Aguilar, golang-nuts
I'm afraid I can't find the code, but I actually did this in the distant past and recall having some success. (I was the one who introduced Ctty to SysProcAttr for this very purpose! :).

One thing it seems as though you're missing is you don't set SysProcAttr.Setctty. The documentation string for that field notes that it won't have any effect unless you also use setsid.

type SysProcAttr struct {
        ...
        Setctty      bool           // Set controlling terminal to fd Ctty (only meaningful if Setsid is set)
        ...
}

Did you try these things?

I do remember reading about a lot of arcane behaviour in the manpages. See the manpages for setsid(2), credentials(7), pty(7) and be sure to understand them in as much detail as you can.

By the way, a neat hack for finding other people who have done similar things:

https://github.com/search?l=Go&q=SysProcAttr+ctty&type=Code&utf8=%E2%9C%93


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

James Aguilar

unread,
Jan 23, 2017, 12:40:56 PM1/23/17
to Ian Lance Taylor, golang-nuts
Huh. It looks like it is working. The bug here is in my understanding of less. I thought that I could interrupt and get a prompt when I was looking at the unfinished part of a file. But it seems that doesn't work. So I probably need to provide an EOF once I'm sure there will be no more output, so the user can quit out from less if the build log is less than a full page long.

There was also the issue of being able to interrupt the whole program with ^C during the second less and after. I believe that this was being caused by a race between starting the second less exec.Cmd and the TIOCSPGRP ioctl call at the end of the first one. The ioctl was stealing the foreground from the running less program, because it ran after the command started. Remove that call seems to fix the problem.

Thanks so much for your help!

-- James
Reply all
Reply to author
Forward
0 new messages