Subject: python exiting w/curses and w/o a terminal; failure of try/except
To: None <netbsd-help@netbsd.org>
From: Greg Troxel <gdt@ir.bbn.com>
List: netbsd-help
Date: 01/18/2005 15:24:06
On NetBSD/i386 2.0ish, I have python23-pth and bittorrent installed:

Information for bittorrent-3.4.2nb1:
Requires:
py23pth-cursespanel-[0-9]*
python23-pth>=2.3.3nb2

I am using btdownloadheadless.py, which writes status to stdout, and
running it with '< /dev/null > LOGFILE 2.&1 &'.
My intent is to push new binary software distributions (./build.sh
release, plus packages and a tarball for non-pkgsrc stuff under
/usr/foo) around to a 20-node ad hoc wireless testbed, after doing a
build on the (wired) build machine.

If I start the downloader while logged in, it starts, and continues to
run in the background.

If I start the downloader by

  ssh host23 /usr/foo/bin/torrent downloader

which then invokes btdownloadheadless.py as above, I get a failure
during initscr in curses.  Note that running '/usr/foo/bin/torrent
downloader' as the same uid works fine from login window.

The failure output is:

Error opening terminal: unknown.

Here is the snippet of code that seems to be at fault:

----------------------------------------
def run(params):
    try:
        import curses
        curses.initscr()
        cols = curses.COLS
        curses.endwin()
    except:
        cols = 80

    h = HeadlessDisplayer()
----------------------------------------

I believe this should leave the try block on an error, and run the
except block.  I put print statements before/after each statement, and
found that it was the initscr that failed:

 13219 python2p3 CALL  read(3,0x813bb90,8)
 13219 python2p3 GIO   fd 3 read 8 bytes
       "\a\0\r\0
        \0
        \0"
 13219 python2p3 RET   read 8
 13219 python2p3 CALL  read(3,0xbfbfdb90,0xa)
 13219 python2p3 RET   read 0
 13219 python2p3 CALL  close(3)
 13219 python2p3 RET   close 0
 13219 python2p3 CALL  ioctl(2,TIOCGETA,0xbfbff470)
 13219 python2p3 RET   ioctl -1 errno 25 Inappropriate ioctl for device
 13219 python2p3 CALL  ioctl(2,TIOCGETA,0xbfbff430)
 13219 python2p3 RET   ioctl -1 errno 25 Inappropriate ioctl for device
 13219 python2p3 CALL  write(2,0xbfbfec40,0x21)
 13219 python2p3 GIO   fd 2 wrote 33 bytes
       "Error opening terminal: unknown.
       "
 13219 python2p3 RET   write 33/0x21
 13219 python2p3 CALL  write(1,0x8283000,0x2e)
 13219 python2p3 GIO   fd 1 wrote 46 bytes
       "before try block
        before import
        before initscr
       "
 13219 python2p3 RET   write 46/0x2e
 13219 python2p3 CALL  exit(1)

After commenting out the initscr call, I got this:

before try block
before import
before initscr
before assignment
before 80
after 80
after try block

and the download started.  This indicates that cols = curses.COLS
failed and the except block was run.

Here is my modified code:

def run(params):
    print 'before try block'
    try:
        print 'before import'
        import curses
        print 'before initscr'
        #curses.initscr()
        print 'before assignment'
        cols = curses.COLS
        print 'before endwin'
        curses.endwin()
        print 'after endwin'
    except:
        print 'before 80'
        cols = 80
        print 'after 80'

    print 'after try block'
    h = HeadlessDisplayer()


So, it seems that curses.initscr fails in a way that causes an exit,
rather than running the except block.  Clearly something is buggy, but
the question is whether this is a bug in the bittorrent code, the
python interpreter, the python curses glue code, or libcurses.  I
looked at python's curses code and libcurses, and didn't see anything
that would exit instead of just returning NULL, more or less.

I plan to patch my bt package to not try curses, but I'd be interested
to be enlightened.