This repository was archived by the owner on Feb 25, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathQUESTIONS
More file actions
206 lines (149 loc) · 7.98 KB
/
Copy pathQUESTIONS
File metadata and controls
206 lines (149 loc) · 7.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
I selected the questions below from articles posted to the USENET
newsgroup comp.unix.questions. All the questions are reasonably easy to
answer with pty and not too easy to answer with other widely available
tools. So I hope this file is useful, and I hope other software authors
adopt the QUESTIONS idea.
1. How do I redirect telnet's input?
Answer: Run pty telnet instead of telnet.
Long answer: Be careful---as soon as telnet successfully connects to the
remote host, it flushes its input. So if you do something like
(echo help; echo quit) | pty telnet whatever 25
telnet will read ``help'' and ``quit'', then after a second or two
connect to the host, then flush the input. It won't get any input after
that. An easy solution is to delay the input a few seconds:
(sleep 5; echo help; echo quit) | pty telnet whatever 25
(Try it!) For a more robust solution, see question #4 below.
Why this was a problem: UNIX beginners learn that they can feed input to
programs with redirection or with pipes. They're often surprised to see
that this doesn't work:
(echo help; echo quit) | telnet whatever 25
telnet, like many other utilities, considers itself an ``interactive''
program. It assumes that it's talking to a user's terminal directly, so
that it can put the terminal into character-at-a-time mode. If you
redirect its input, it won't be able to find the terminal, and it'll
complain or do weird things. Similar comments apply to editors, like vi.
Why the answer solves the problem: pty creates a pseudo-terminal---
something which looks like a normal terminal but is under pty's control.
pty runs telnet under the pseudo-terminal and forwards all input and
output. Now telnet works, because it sees the pseudo-terminal and thinks
it's talking to the user.
2. How do I redirect passwd's input?
Answer: pty passwd.
Long answer: Like telnet, passwd flushes its input, so you probably want
to do something like
(sleep 5; echo oldpassword; sleep 5; echo newpassword; \
sleep 5; echo newpassword) | pty passwd
A better solution appears in #4 below. If you're using passwd as a
system administrator to change somebody else's password, you want
(sleep 5; echo password; sleep 5; echo password) | pty passwd shmoe
I certainly don't recommend keeping passwords visible in shell scripts,
but if you ever need to do this, you can. In single-user mode this is
reasonably safe.
Why this was a problem: passwd is even more insistent than telnet on
talking to a user directly. So it opens /dev/tty for its input. Some
other programs open /dev/tty for both input and output. This avoids any
redirection you might have put into place.
Why the answer solves the problem: As before, pty creates a
pseudo-terminal for passwd to talk to. When passwd opens /dev/tty, it
gets the pseudo-terminal. All the redirection is outside pty, safe from
passwd's tampering.
3. Why doesn't tr '[A-Z]' '[a-z]' | cat -t print anything I type?
Answer: Do nobuf tr '[A-Z]' '[a-z]' | cat -t.
Long answer: You can use pty -0 (also known as condom, or, if you're not
in such a picturesque mood, as ttyprotect) in place of nobuf, but nobuf
invokes pty carefully so that EOF on the input will be passed through to
tr. The effect of this pipeline is to change all uppercase letters into
lowercase, then print control characters visibly (e.g., ^H instead of
backspace).
Why this was a problem: The original pipeline, without nobuf, doesn't
seem to produce any output. What happens is that tr says ``Oh, I say.
My output is going into a pipe! I guess there's no point in sending
output as fast as possible. So I'll build up a buffer of output, say up
to 8192 characters, before I send any of it.'' In fact, any program
which uses stdio will do the same thing. The problem is that the guess
is wrong---you don't want 8192 characters held inside tr. You want each
line sent through without any buffering.
Why the answer solves the problem: Once again, nobuf (really pty)
creates a pseudo-terminal and runs tr inside it. Then tr (really stdio)
says ``Oh, I say. My output is going into a terminal! I'll bet there's a
user watching every word. So I'll send each line as soon as I'm done
with it.'' And it does.
4. How do I start a program, respond to its prompts, give the correct
replies, and catch the output---all from a script?
If you're good at shell programming, you might have already figured out
most of this. Let's say you're on a machine (like a Sun) which can
create named pipes with mknod foo p. You might try to automate a telnet
session, keeping a record in a file, like this:
#!/bin/sh
(umask 077; mknod input p; mknod output p)
telnet foo < input | tee record > output &
exec 4>input 5<output
waitfor login: <&5 2>/dev/null
echo 'username' >&4
waitfor Password: <&5 2>/dev/null
echo 'password' >&4
# etc.
(waitfor reads its input, character by character, until its first
argument matches the most recent characters as a literal string.)
This almost works. sh can handle this sort of automation without
trouble. The only problem is that telnet is interactive. That's where
pty comes in. This works:
#!/bin/sh
(umask 077; mknod input p; mknod output p)
pty -0 telnet foo < input | nobuf tee record > output &
exec 4>input 5<output
waitfor login: <&5 2>/dev/null
echo 'username' >&4
waitfor Password: <&5 2>/dev/null
echo 'password' >&4
# etc.
On machines without named pipes, you'll have to create (unnamed) pipes
in C. But whatever method of automation you use, pty will let you apply
that method to an interactive program.
5. How do I get rn to process KILL files in the background?
Answer:
% sess -R sh -c 'sessname; rn' &
[1] 20417 < the shell prints this >
session pf < this comes from sessname >
< rn produces output in the background, and you can do something else. >
< ... >
< when you want to put it back into the foreground: >
% %1
sess -R sh -c 'sessname; rn' < the shell prints this >
^C
% sess reconnect pf
< now it's just as if rn had started in the foreground. >
sess is an abbreviation for ``pty -s''. It starts a _session_ which you
can disconnect and reconnect. Later on, when you put it into the
background and type ^C, it stays disconnected in the background. Then
you can start a new session and reconnect to it.
-R means ``don't read.'' It's a lot like rsh -n. It tells pty not to
read anything from the keyboard. When you start ``sess reconnect pf'',
you don't specify -R, so pty does take keyboard input.
Why this was a problem: rn is yet another ``interactive'' program. It
insists on being in the foreground before it does anything else.
Unfortunately, after you put it into the foreground, it might spend ten
minutes processing KILL files!
Why the answer solves the problem: Once again, pty creates a
pseudo-terminal for rn. Under that pseudo-terminal, rn is in the
foreground, so it will happily process KILL files.
6. How do I get the terminal speed from a shell script?
Answer: Under sh, speed="`pty stty speed`".
Why this was a problem: When you type ``stty speed'', most versions of
stty will look for the terminal on stdout, get the speed of that
terminal, and print the result on stderr. So if you try
speed="`stty speed`"
from sh, two things go wrong. The first is that sh is reading stty's
output, so when stty looks for the terminal, it'll find only a pipe. The
second is that when stty prints the result, it goes to stderr, which sh
isn't paying attention to.
Other versions of stty look for the terminal on stdin or stderr, and
print the result to stdout. So even if you kludge around the first
problem by moving file descriptors around, chances are your code will
mysteriously blow up on the next machine.
Why the answer solves the problem: pty creates a pseudo-terminal for
stty, with exactly the same characteristics (including speed) as the
original tty. It doesn't matter whether stty looks at stdin or stdout or
stderr. It'll just see the pseudo-tty. It also doesn't matter whether
stty prints its result to stdout or stderr. pty will collect both
results into its stdout for the shell.