1 #include "stdinc.h"
2
3 #include "9.h"
4
5 enum {
6 Nl = 256, /* max. command line length */
7 Nq = 8*1024, /* amount of I/O buffered */
8 };
9
10 typedef struct Q {
11 VtLock* lock;
12 VtRendez* full;
13 VtRendez* empty;
14
15 char q[Nq];
16 int n;
17 int r;
18 int w;
19 } Q;
20
21 typedef struct Cons {
22 VtLock* lock;
23 int ref;
24 int closed;
25 int fd;
26 int srvfd;
27 int ctlfd;
28 Q* iq; /* points to console.iq */
29 Q* oq; /* points to console.oq */
30 } Cons;
31
32 char *currfsysname;
33
34 static struct {
35 Q* iq; /* input */
36 Q* oq; /* output */
37 char l[Nl]; /* command line assembly */
38 int nl; /* current line length */
39 int nopens;
40
41 char* prompt;
42 int np;
43 } console;
44
45 static void
consClose(Cons * cons)46 consClose(Cons* cons)
47 {
48 vtLock(cons->lock);
49 cons->closed = 1;
50
51 cons->ref--;
52 if(cons->ref > 0){
53 vtLock(cons->iq->lock);
54 vtWakeup(cons->iq->full);
55 vtUnlock(cons->iq->lock);
56 vtLock(cons->oq->lock);
57 vtWakeup(cons->oq->empty);
58 vtUnlock(cons->oq->lock);
59 vtUnlock(cons->lock);
60 return;
61 }
62
63 if(cons->ctlfd != -1){
64 close(cons->ctlfd);
65 cons->srvfd = -1;
66 }
67 if(cons->srvfd != -1){
68 close(cons->srvfd);
69 cons->srvfd = -1;
70 }
71 if(cons->fd != -1){
72 close(cons->fd);
73 cons->fd = -1;
74 }
75 vtUnlock(cons->lock);
76 vtLockFree(cons->lock);
77 vtMemFree(cons);
78 console.nopens--;
79 }
80
81 static void
consIProc(void * v)82 consIProc(void* v)
83 {
84 Q *q;
85 Cons *cons;
86 int n, w;
87 char buf[Nq/4];
88
89 vtThreadSetName("consI");
90
91 cons = v;
92 q = cons->iq;
93 for(;;){
94 /*
95 * Can't tell the difference between zero-length read
96 * and eof, so keep calling read until we get an error.
97 */
98 if(cons->closed || (n = read(cons->fd, buf, Nq/4)) < 0)
99 break;
100 vtLock(q->lock);
101 while(Nq - q->n < n && !cons->closed)
102 vtSleep(q->full);
103 w = Nq - q->w;
104 if(w < n){
105 memmove(&q->q[q->w], buf, w);
106 memmove(&q->q[0], buf + w, n - w);
107 }
108 else
109 memmove(&q->q[q->w], buf, n);
110 q->w = (q->w + n) % Nq;
111 q->n += n;
112 vtWakeup(q->empty);
113 vtUnlock(q->lock);
114 }
115 consClose(cons);
116 }
117
118 static void
consOProc(void * v)119 consOProc(void* v)
120 {
121 Q *q;
122 Cons *cons;
123 char buf[Nq];
124 int lastn, n, r;
125
126 vtThreadSetName("consO");
127
128 cons = v;
129 q = cons->oq;
130 vtLock(q->lock);
131 lastn = 0;
132 for(;;){
133 while(lastn == q->n && !cons->closed)
134 vtSleep(q->empty);
135 if((n = q->n - lastn) > Nq)
136 n = Nq;
137 if(n > q->w){
138 r = n - q->w;
139 memmove(buf, &q->q[Nq - r], r);
140 memmove(buf+r, &q->q[0], n - r);
141 }
142 else
143 memmove(buf, &q->q[q->w - n], n);
144 lastn = q->n;
145 vtUnlock(q->lock);
146 if(cons->closed || write(cons->fd, buf, n) < 0)
147 break;
148 vtLock(q->lock);
149 vtWakeup(q->empty);
150 }
151 consClose(cons);
152 }
153
154 int
consOpen(int fd,int srvfd,int ctlfd)155 consOpen(int fd, int srvfd, int ctlfd)
156 {
157 Cons *cons;
158
159 cons = vtMemAllocZ(sizeof(Cons));
160 cons->lock = vtLockAlloc();
161 cons->fd = fd;
162 cons->srvfd = srvfd;
163 cons->ctlfd = ctlfd;
164 cons->iq = console.iq;
165 cons->oq = console.oq;
166 console.nopens++;
167
168 vtLock(cons->lock);
169 cons->ref = 2;
170 cons->closed = 0;
171 if(vtThread(consOProc, cons) < 0){
172 cons->ref--;
173 vtUnlock(cons->lock);
174 consClose(cons);
175 return 0;
176 }
177 vtUnlock(cons->lock);
178
179 if(ctlfd >= 0)
180 consIProc(cons);
181 else if(vtThread(consIProc, cons) < 0){
182 consClose(cons);
183 return 0;
184 }
185
186 return 1;
187 }
188
189 static int
qWrite(Q * q,char * p,int n)190 qWrite(Q* q, char* p, int n)
191 {
192 int w;
193
194 vtLock(q->lock);
195 if(n > Nq - q->w){
196 w = Nq - q->w;
197 memmove(&q->q[q->w], p, w);
198 memmove(&q->q[0], p + w, n - w);
199 q->w = n - w;
200 }
201 else{
202 memmove(&q->q[q->w], p, n);
203 q->w += n;
204 }
205 q->n += n;
206 vtWakeup(q->empty);
207 vtUnlock(q->lock);
208
209 return n;
210 }
211
212 static Q*
qAlloc(void)213 qAlloc(void)
214 {
215 Q *q;
216
217 q = vtMemAllocZ(sizeof(Q));
218 q->lock = vtLockAlloc();
219 q->full = vtRendezAlloc(q->lock);
220 q->empty = vtRendezAlloc(q->lock);
221 q->n = q->r = q->w = 0;
222
223 return q;
224 }
225
226 static void
consProc(void *)227 consProc(void*)
228 {
229 Q *q;
230 int argc, i, n, r;
231 char *argv[20], buf[Nq], *lp, *wbuf;
232 char procname[64];
233
234 snprint(procname, sizeof procname, "cons %s", currfsysname);
235 vtThreadSetName(procname);
236
237 q = console.iq;
238 qWrite(console.oq, console.prompt, console.np);
239 vtLock(q->lock);
240 for(;;){
241 while((n = q->n) == 0)
242 vtSleep(q->empty);
243 r = Nq - q->r;
244 if(r < n){
245 memmove(buf, &q->q[q->r], r);
246 memmove(buf + r, &q->q[0], n - r);
247 }
248 else
249 memmove(buf, &q->q[q->r], n);
250 q->r = (q->r + n) % Nq;
251 q->n -= n;
252 vtWakeup(q->full);
253 vtUnlock(q->lock);
254
255 for(i = 0; i < n; i++){
256 switch(buf[i]){
257 case '\004': /* ^D */
258 if(console.nl == 0){
259 qWrite(console.oq, "\n", 1);
260 break;
261 }
262 /*FALLTHROUGH*/
263 default:
264 if(console.nl < Nl-1){
265 qWrite(console.oq, &buf[i], 1);
266 console.l[console.nl++] = buf[i];
267 }
268 continue;
269 case '\b':
270 if(console.nl != 0){
271 qWrite(console.oq, &buf[i], 1);
272 console.nl--;
273 }
274 continue;
275 case '\n':
276 qWrite(console.oq, &buf[i], 1);
277 break;
278 case '\025': /* ^U */
279 qWrite(console.oq, "^U\n", 3);
280 console.nl = 0;
281 break;
282 case '\027': /* ^W */
283 console.l[console.nl] = '\0';
284 wbuf = vtMemAlloc(console.nl+1);
285 memmove(wbuf, console.l, console.nl+1);
286 argc = tokenize(wbuf, argv, nelem(argv));
287 if(argc > 0)
288 argc--;
289 console.nl = 0;
290 lp = console.l;
291 for(i = 0; i < argc; i++)
292 lp += sprint(lp, "%q ", argv[i]);
293 console.nl = lp - console.l;
294 vtMemFree(wbuf);
295 qWrite(console.oq, "^W\n", 3);
296 if(console.nl == 0)
297 break;
298 qWrite(console.oq, console.l, console.nl);
299 continue;
300 case '\177':
301 qWrite(console.oq, "\n", 1);
302 console.nl = 0;
303 break;
304 }
305
306 console.l[console.nl] = '\0';
307 if(console.nl != 0)
308 cliExec(console.l);
309
310 console.nl = 0;
311 qWrite(console.oq, console.prompt, console.np);
312 }
313
314 vtLock(q->lock);
315 }
316 }
317
318 int
consWrite(char * buf,int len)319 consWrite(char* buf, int len)
320 {
321 if(console.oq == nil)
322 return write(2, buf, len);
323 if(console.nopens == 0)
324 write(2, buf, len);
325 return qWrite(console.oq, buf, len);
326 }
327
328 int
consPrompt(char * prompt)329 consPrompt(char* prompt)
330 {
331 char buf[ERRMAX];
332
333 if(prompt == nil)
334 prompt = "prompt";
335
336 vtMemFree(console.prompt);
337 console.np = snprint(buf, sizeof(buf), "%s: ", prompt);
338 console.prompt = vtStrDup(buf);
339
340 return console.np;
341 }
342
343 int
consTTY(void)344 consTTY(void)
345 {
346 int ctl, fd;
347 char *name, *p;
348
349 name = "/dev/cons";
350 if((fd = open(name, ORDWR)) < 0){
351 name = "#c/cons";
352 if((fd = open(name, ORDWR)) < 0){
353 vtSetError("consTTY: open %s: %r", name);
354 return 0;
355 }
356 }
357
358 p = smprint("%sctl", name);
359 if((ctl = open(p, OWRITE)) < 0){
360 close(fd);
361 vtSetError("consTTY: open %s: %r", p);
362 free(p);
363 return 0;
364 }
365 if(write(ctl, "rawon", 5) < 0){
366 close(ctl);
367 close(fd);
368 vtSetError("consTTY: write %s: %r", p);
369 free(p);
370 return 0;
371 }
372 free(p);
373
374 if(consOpen(fd, fd, ctl) == 0){
375 close(ctl);
376 close(fd);
377 return 0;
378 }
379
380 return 1;
381 }
382
383 int
consInit(void)384 consInit(void)
385 {
386 console.iq = qAlloc();
387 console.oq = qAlloc();
388 console.nl = 0;
389
390 consPrompt(nil);
391
392 if(vtThread(consProc, nil) < 0){
393 vtFatal("can't start console proc");
394 return 0;
395 }
396
397 return 1;
398 }
399