]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/rdbfs.c
rdbfs: add -s srvname (from charles forsyth's rdbfs-srvname patch)
[plan9front.git] / sys / src / cmd / rdbfs.c
1 /*
2  * Remote debugging file system
3  */
4
5 #include <u.h>
6 #include <libc.h>
7 #include <auth.h>
8 #include <fcall.h>
9 #include <bio.h>
10 #include <thread.h>
11 #include <9p.h>
12
13 int dbg = 0;
14 #define DBG     if(dbg)fprint
15
16 enum {
17         NHASH = 4096,
18         Readlen = 4,
19         Pagequantum = 1024,
20 };
21
22 /* caching memory pages: a lot of space to avoid serial communications */
23 Lock pglock;
24 typedef struct  Page    Page;
25 struct Page {   /* cached memory contents */
26         Page *link;
27         ulong len;
28         ulong addr;
29         int count;
30         uchar val[Readlen];
31 };
32
33 Page *pgtab[NHASH];
34
35 Page *freelist;
36
37 /* called with pglock locked */
38 Page*
39 newpg(void)
40 {
41         int i;
42         Page *p, *q;
43
44         if(freelist == nil){
45                 p = malloc(sizeof(Page)*Pagequantum);
46                 if(p == nil)
47                         sysfatal("out of memory");
48
49                 for(i=0, q=p; i<Pagequantum-1; i++, q++)
50                         q->link = q+1;
51                 q->link = nil;
52
53                 freelist = p;
54         }
55         p = freelist;
56         freelist = freelist->link;
57         return p;
58 }
59
60 #define PHIINV 0.61803398874989484820
61 uint
62 ahash(ulong addr)
63 {
64         return (uint)floor(NHASH*fmod(addr*PHIINV, 1.0));
65 }
66
67 int
68 lookup(ulong addr, uchar *val, ulong count)
69 {
70         Page *p;
71
72         lock(&pglock);
73         for(p=pgtab[ahash(addr)]; p; p=p->link){
74                 if(p->addr == addr && p->count == count){
75                         memmove(val, p->val, count);
76                         unlock(&pglock);
77                         return 1;
78                 }
79         }
80         unlock(&pglock);
81         return 0;
82 }
83
84 void
85 insert(ulong addr, uchar *val, int count)
86 {
87         Page *p;
88         uint h;
89
90         lock(&pglock);
91         p = newpg();
92         p->addr = addr;
93         p->count = count;
94         memmove(p->val, val, count);
95         h = ahash(addr);
96         p->link = pgtab[h];
97         p->len = pgtab[h] ? pgtab[h]->len+1 : 1;
98         pgtab[h] = p;
99         unlock(&pglock);
100 }
101
102 void
103 flushcache(void)
104 {
105         int i;
106         Page *p;
107
108         lock(&pglock);
109         for(i=0; i<NHASH; i++){
110                 if(p=pgtab[i]){
111                         for(;p->link; p=p->link)
112                                 ;
113                         p->link = freelist;
114                         freelist = p;
115                 }
116                 pgtab[i] = nil;
117         }
118         unlock(&pglock);
119 }
120
121 enum
122 {
123         Xctl    = 1,
124         Xfpregs,
125         Xkregs,
126         Xmem,
127         Xproc,
128         Xregs,
129         Xtext,
130         Xstatus,
131
132 };
133
134 int     textfd;
135 int     rfd;
136 Biobuf  rfb;
137 char*   portname = "/dev/eia0";
138 char*   textfile = "/386/9pc";
139 char*   procname = "1";
140 char*   srvname;
141 Channel* rchan;
142
143 void
144 usage(void)
145 {
146         fprint(2, "usage: rdbfs [-p procnum] [-s srvname] [-t textfile] [serialport]\n");
147         exits("usage");
148 }
149
150 void
151 noalarm(void*, char *msg)
152 {
153         if(strstr(msg, "alarm"))
154                 noted(NCONT);
155         noted(NDFLT);
156 }
157
158 /*
159  *      send and receive responses on the serial line
160  */
161 void
162 eiaread(void*)
163 {
164         Req *r;
165         char *p;
166         uchar *data;
167         char err[ERRMAX];
168         char buf[1000];
169         int i, tries;
170
171         notify(noalarm);
172         while(r = recvp(rchan)){
173                 DBG(2, "got %F: here goes...", &r->ifcall);
174                 if(r->ifcall.count > Readlen)
175                         r->ifcall.count = Readlen;
176                 r->ofcall.count = r->ifcall.count;
177                 if(r->type == Tread && lookup(r->ifcall.offset, (uchar*)r->ofcall.data, r->ofcall.count)){
178                         respond(r, nil);
179                         continue;
180                 }
181                 for(tries=0; tries<5; tries++){
182                         if(r->type == Twrite){
183                                 DBG(2, "w%.8lux %.8lux...", (ulong)r->ifcall.offset, *(ulong*)r->ifcall.data);
184                                 fprint(rfd, "w%.8lux %.8lux\n", (ulong)r->ifcall.offset, *(ulong*)r->ifcall.data);
185                         }else if(r->type == Tread){
186                                 DBG(2, "r%.8lux...", (ulong)r->ifcall.offset);
187                                 fprint(rfd, "r%.8lux\n", (ulong)r->ifcall.offset);
188                         }else{
189                                 respond(r, "oops");
190                                 break;
191                         }
192                         for(;;){
193                                 werrstr("");
194                                 alarm(500);
195                                 p=Brdline(&rfb, '\n');
196                                 alarm(0);
197                                 if(p == nil){
198                                         rerrstr(err, sizeof err);
199                                         DBG(2, "error %s\n", err);
200                                         if(strstr(err, "alarm") || strstr(err, "interrupted"))
201                                                 break;
202                                         if(Blinelen(&rfb) == 0) // true eof
203                                                 sysfatal("eof on serial line?");
204                                         Bread(&rfb, buf, Blinelen(&rfb)<sizeof buf ? Blinelen(&rfb) : sizeof buf);
205                                         continue;
206                                 }
207                                 p[Blinelen(&rfb)-1] = 0;
208                                 if(p[0] == '\r')
209                                         p++;
210                                 DBG(2, "serial %s\n", p);
211                                 if(p[0] == 'R'){
212                                         if(strtoul(p+1, 0, 16) == (ulong)r->ifcall.offset){
213                                                 /* we know that data can handle Readlen bytes */
214                                                 data = (uchar*)r->ofcall.data;
215                                                 for(i=0; i<r->ifcall.count; i++)
216                                                         data[i] = strtol(p+1+8+1+3*i, 0, 16);
217                                                 insert(r->ifcall.offset, data, r->ifcall.count);
218                                                 respond(r, nil);
219                                                 goto Break2;
220                                         }else
221                                                 DBG(2, "%.8lux ≠ %.8lux\n", strtoul(p+1, 0, 16), (ulong)r->ifcall.offset);
222                                 }else if(p[0] == 'W'){
223                                         respond(r, nil);
224                                         goto Break2;
225                                 }else{
226                                         DBG(2, "unknown message\n");
227                                 }
228                         }
229                 }
230         Break2:;
231         }
232 }
233
234 void
235 attachremote(char* name)
236 {
237         int fd;
238         char buf[128];
239
240         print("attach %s\n", name);
241         rfd = open(name, ORDWR);
242         if(rfd < 0)
243                 sysfatal("can't open remote %s", name);
244
245         sprint(buf, "%sctl", name);
246         fd = open(buf, OWRITE);
247         if(fd < 0)
248                 sysfatal("can't set baud rate on %s", buf);
249         write(fd, "B9600", 6);
250         close(fd);
251         Binit(&rfb, rfd, OREAD);
252 }
253
254 void
255 fsopen(Req *r)
256 {
257         char buf[ERRMAX];
258
259         switch((uintptr)r->fid->file->aux){
260         case Xtext:
261                 close(textfd);
262                 textfd = open(textfile, OREAD);
263                 if(textfd < 0) {
264                         snprint(buf, sizeof buf, "text: %r");
265                         respond(r, buf);
266                         return;
267                 }
268                 break;
269         }               
270         respond(r, nil);
271 }
272
273 void
274 fsread(Req *r)
275 {
276         int i, n;
277         char buf[512];
278
279         switch((uintptr)r->fid->file->aux) {
280         case Xfpregs:
281         case Xproc:
282         case Xregs:
283                 respond(r, "Egreg");
284                 break;
285         case Xkregs:
286         case Xmem:
287                 if(sendp(rchan, r) != 1){
288                         snprint(buf, sizeof buf, "rdbfs sendp: %r");
289                         respond(r, buf);
290                         return;
291                 }
292                 break;
293         case Xtext:
294                 n = pread(textfd, r->ofcall.data, r->ifcall.count, r->ifcall.offset);
295                 if(n < 0) {
296                         rerrstr(buf, sizeof buf);
297                         respond(r, buf);
298                         break;
299                 }
300                 r->ofcall.count = n;
301                 respond(r, nil);
302                 break;
303         case Xstatus:
304                 n = sprint(buf, "%-28s%-28s%-28s", "remote", "system", "New");
305                 for(i = 0; i < 9; i++)
306                         n += sprint(buf+n, "%-12d", 0);
307                 readstr(r, buf);
308                 respond(r, nil);
309                 break;
310         default:
311                 respond(r, "unknown read");
312         }
313 }
314
315 void
316 fswrite(Req *r)
317 {
318         char buf[ERRMAX];
319
320         switch((uintptr)r->fid->file->aux) {
321         case Xctl:
322                 if(strncmp(r->ifcall.data, "kill", 4) == 0 ||
323                    strncmp(r->ifcall.data, "exit", 4) == 0) {
324                         respond(r, nil);
325                         postnote(PNGROUP, getpid(), "umount");
326                         exits(nil);
327                 }else if(strncmp(r->ifcall.data, "refresh", 7) == 0){
328                         flushcache();
329                         respond(r, nil);
330                 }else if(strncmp(r->ifcall.data, "hashstats", 9) == 0){
331                         int i;
332                         lock(&pglock);
333                         for(i=0; i<NHASH; i++)
334                                 if(pgtab[i])
335                                         print("%lud ", pgtab[i]->len);
336                         print("\n");
337                         unlock(&pglock);
338                         respond(r, nil);
339                 }else
340                         respond(r, "permission denied");
341                 break;
342         case Xkregs:
343         case Xmem:
344                 if(sendp(rchan, r) != 1) {
345                         snprint(buf, sizeof buf, "rdbfs sendp: %r");
346                         respond(r, buf);
347                         return;
348                 }
349                 break;
350         default:
351                 respond(r, "Egreg");
352                 break;
353         }
354 }
355
356 struct {
357         char *s;
358         int id;
359         int mode;
360 } tab[] = {
361         "ctl",          Xctl,           0222,
362         "fpregs",       Xfpregs,        0666,
363         "kregs",        Xkregs,         0666,
364         "mem",          Xmem,           0666,
365         "proc",         Xproc,          0444,
366         "regs",         Xregs,          0666,
367         "text",         Xtext,          0444,
368         "status",       Xstatus,        0444,
369 };
370
371 void
372 killall(Srv*)
373 {
374         postnote(PNGROUP, getpid(), "kill");
375 }
376
377 Srv fs = {
378 .open=  fsopen,
379 .read=  fsread,
380 .write= fswrite,
381 .end=   killall,
382 };
383
384 void
385 threadmain(int argc, char **argv)
386 {
387         int i, p[2];
388         File *dir;
389
390         rfork(RFNOTEG);
391         ARGBEGIN{
392         case 'D':
393                 chatty9p++;
394                 break;
395         case 'd':
396                 dbg = 1;
397                 break;
398         case 'p':
399                 procname = EARGF(usage());
400                 break;
401         case 's':
402                 srvname = EARGF(usage());
403                 break;
404         case 't':
405                 textfile = EARGF(usage());
406                 break;
407         default:
408                 usage();
409         }ARGEND;
410
411         switch(argc){
412         case 0:
413                 break;
414         case 1:
415                 portname = argv[0];
416                 break;
417         default:
418                 usage();
419         }
420
421         rchan = chancreate(sizeof(Req*), 10);
422         attachremote(portname);
423         if(pipe(p) < 0)
424                 sysfatal("pipe: %r");
425
426         fmtinstall('F', fcallfmt);
427         proccreate(eiaread, nil, 8192);
428
429         fs.tree = alloctree("rdbfs", "rdbfs", DMDIR|0555, nil);
430         dir = createfile(fs.tree->root, procname, "rdbfs", DMDIR|0555, 0);
431         for(i=0; i<nelem(tab); i++)
432                 closefile(createfile(dir, tab[i].s, "rdbfs", tab[i].mode, (void*)tab[i].id));
433         closefile(dir);
434         threadpostmountsrv(&fs, srvname, "/proc", MBEFORE);
435         exits(0);
436 }
437