]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/tty.c
mothra: never snarf the "Go:" box
[plan9front.git] / sys / src / cmd / tty.c
1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include <thread.h>
5 #include <fcall.h>
6 #include <9p.h>
7
8 #define MIN(a,b) ((a) < (b) ? (a) : (b))
9
10 enum {
11         Stacksz = 8192,
12 };
13
14 typedef struct Prog {
15         Channel *pidc;
16         char **argv;
17 } Prog;
18
19 typedef struct Line {
20         uchar *buf;
21         int x, len;
22 } Line;
23
24 int cons, consctl;
25 Channel *rchan, *wchan, *ichan;
26 File *devcons;
27
28 static void
29 killer(void *arg)
30 {
31         int *pid = arg;
32         ulong yes;
33         for(;;){
34                 yes = recvul(ichan);
35                 if(yes)
36                         postnote(PNGROUP, *pid, "interrupt");
37         }
38 }
39
40 static void
41 sendline(Channel *c, Line *L)
42 {
43         int n, k;
44         Req *r;
45         uchar *s;
46         
47         s = L->buf;
48         n = L->x;
49         do{
50                 while(!(r = recvp(c)));
51                 k = MIN(n, r->ifcall.count);
52                 memcpy(r->ofcall.data, s, k);
53                 r->ofcall.count = k;
54                 respond(r, nil);
55                 s += k;
56                 n -= k;
57         }while(n > 0);
58         L->x = 0;
59 }
60
61 static void
62 senderr(Channel *c, char *err)
63 {
64         Req *r;
65         while(!(r = recvp(c)));
66         respond(r, err);
67 }
68
69 static void
70 echo(uchar c)
71 {
72         write(cons, &c, 1);
73 }
74
75 static int
76 addchar(Line *L, uchar c)
77 {
78         int send;
79         
80         send = 0;
81         switch(c){
82         case '\n':
83                 send = 1;
84                 break;
85         case 4:
86                 return 1;
87         case 8:
88                 if(L->x > 0){
89                         echo(8);
90                         L->x--;
91                 }
92                 return 0;
93         case 21:
94                 while(L->x > 0){
95                         echo(8);
96                         L->x--;
97                 }
98                 return 0;
99         case 23:
100                 while(L->x > 0){
101                         c = L->buf[--L->x];
102                         echo(8);
103                         if(c == ' ' || c == '\t')
104                                 break;
105                 }
106                 return 0;
107         case 127:
108                 L->x = 0;
109                 sendul(ichan, 1);
110                 return 1;
111         }
112         echo(c);
113         L->buf[L->x++] = c;
114         if(L->x == L->len)
115                 send = 1;
116         return send;
117 }
118
119 static Line *
120 makeline(int len)
121 {
122         Line *L;
123         L = malloc(sizeof(*L));
124         L->buf = malloc(len);
125         L->x = 0;
126         L->len = len;
127         return L;
128 }
129
130 static void
131 reader(void *)
132 {
133         int n;
134         uchar c;
135         Line *L;
136         char err[ERRMAX];
137         
138         L = makeline(128);
139         for(;;){
140                 n = read(cons, &c, 1);
141                 if(n < 0){
142                         rerrstr(err, sizeof(err));
143                         if(L->x > 0)
144                                 sendline(rchan, L);
145                         senderr(rchan, err);
146                         continue;
147                 }
148                 if(addchar(L, c))
149                         sendline(rchan, L);
150         }
151 }
152
153 static void
154 writer(void *)
155 {
156         Req *r;
157         int n;
158         char err[ERRMAX];
159         
160         for(;;){
161                 do
162                         r = recvp(wchan);
163                 while(!r);
164                 
165                 n = write(cons, r->ifcall.data, r->ifcall.count);
166                 if(n < 0){
167                         rerrstr(err, sizeof(err));
168                         respond(r, err);
169                 }else{
170                         r->ofcall.count = n;
171                         respond(r, nil);
172                 }
173         }
174 }
175
176 static void
177 fsread(Req *r)
178 {
179         sendp(rchan, r);
180 }
181
182 static void
183 fswrite(Req *r)
184 {
185         sendp(wchan, r);
186 }
187
188 /* adapted from acme/win */
189 int
190 lookinbin(char *s)
191 {
192         if(s[0] == '/')
193                 return 0;
194         if(s[0]=='.' && s[1]=='/')
195                 return 0;
196         if(s[0]=='.' && s[1]=='.' && s[2]=='/')
197                 return 0;
198         return 1;
199 }
200
201 char*
202 estrstrdup(char *s, char *t)
203 {
204         char *u;
205
206         u = malloc(strlen(s)+strlen(t)+1);
207         sprint(u, "%s%s", s, t);
208         return u;
209 }
210
211 void
212 cmdproc(void *arg)
213 {
214         Prog *p = arg;
215         char **av = p->argv;
216         char *cmd;
217         
218         rfork(RFCFDG | RFNOTEG);
219         open("/dev/cons", OREAD);
220         open("/dev/cons", OWRITE);
221         dup(1, 2);
222         procexec(p->pidc, av[0], av);
223         if(lookinbin(av[0])){
224                 cmd = estrstrdup("/bin/", av[0]);
225                 procexec(p->pidc, cmd, av);
226         }
227         threadexitsall("exec");
228 }
229
230 void
231 runcmd(char **argv)
232 {
233         Channel *waitc;
234         Channel *pidc;
235         Waitmsg *w;
236         Prog prog;
237         int pid;
238
239         waitc = threadwaitchan();
240         pidc = chancreate(sizeof(int), 0);
241         prog.argv = argv;
242         prog.pidc = pidc;
243         proccreate(cmdproc, &prog, Stacksz);
244         while(recv(pidc, &pid) == -1 || pid == -1);
245         threadcreate(killer, &pid, Stacksz);
246         while(recv(waitc, &w) == -1);
247         free(w);
248 }
249
250 void
251 usage(void)
252 {
253         print("usage: tty [-D] cmd arg1 arg2 ...\n");
254         exits("usage");
255 }
256
257 Srv fs = {
258         .read = fsread,
259         .write = fswrite,
260 };
261
262 void
263 threadmain(int argc, char *argv[])
264 {
265         char *user;
266         
267         ARGBEGIN{
268                 case 'D':
269                         chatty9p++;
270                         break;
271                 default:
272                         usage();
273                         break;
274         }ARGEND;
275         
276         if(argc == 0)
277                 usage();
278
279         rfork(RFNAMEG);
280         
281         cons = open("/dev/cons", ORDWR);
282         if(cons < 0)
283                 sysfatal("cons: %r");
284         consctl = open("/dev/consctl", OWRITE);
285         if(consctl < 0)
286                 sysfatal("ctl: %r");
287         fprint(consctl, "rawon\n");
288         
289         rchan = chancreate(sizeof(void *), 8);
290         wchan = chancreate(sizeof(void *), 8);
291         ichan = chancreate(sizeof(ulong), 8);
292
293         proccreate(reader, nil, Stacksz);
294         proccreate(writer, nil, Stacksz);
295         
296         user = getuser();
297         fs.tree = alloctree(user, getuser(), DMDIR|0555, nil);
298         devcons = createfile(fs.tree->root, "cons", user, 0666, nil);
299         threadpostmountsrv(&fs, nil, "/dev", MBEFORE);
300
301         runcmd(argv);
302         
303         close(consctl);
304         close(cons);
305         threadexitsall(nil);
306 }