]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ratrace.c
mothra: never snarf the "Go:" box
[plan9front.git] / sys / src / cmd / ratrace.c
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <thread.h>
5
6 enum {
7         Stacksize       = 8*1024,
8         Bufsize         = 8*1024,
9 };
10
11 Channel *out;
12 Channel *quit;
13 Channel *forkc;
14 int nread = 0;
15
16 typedef struct Str Str;
17 struct Str {
18         char    *buf;
19         int     len;
20 };
21
22 void
23 die(char *s)
24 {
25         fprint(2, "%s\n", s);
26         exits(s);
27 }
28
29 void
30 cwrite(int fd, char *path, char *cmd, int len)
31 {
32         if (write(fd, cmd, len) < len) {
33                 fprint(2, "cwrite: %s: failed %d bytes: %r\n", path, len);
34                 sendp(quit, nil);
35                 threadexits(nil);
36         }
37 }
38
39 void
40 reader(void *v)
41 {
42         int cfd, tfd, forking = 0, pid, newpid;
43         char *ctl, *truss;
44         Str *s;
45
46         pid = (int)(uintptr)v;
47         ctl = smprint("/proc/%d/ctl", pid);
48         if ((cfd = open(ctl, OWRITE)) < 0)
49                 die(smprint("%s: %r", ctl));
50         truss = smprint("/proc/%d/syscall", pid);
51         if ((tfd = open(truss, OREAD)) < 0)
52                 die(smprint("%s: %r", truss));
53
54         cwrite(cfd, ctl, "stop", 4);
55         cwrite(cfd, truss, "startsyscall", 12);
56
57         s = mallocz(sizeof(Str) + Bufsize, 1);
58         s->buf = (char *)&s[1];
59         while((s->len = pread(tfd, s->buf, Bufsize - 1, 0)) > 0){
60                 if (forking && s->buf[1] == '=' && s->buf[3] != '-') {
61                         forking = 0;
62                         newpid = strtol(&s->buf[3], 0, 0);
63                         sendp(forkc, (void*)newpid);
64                         procrfork(reader, (void*)newpid, Stacksize, 0);
65                 }
66
67                 /*
68                  * There are three tests here and they (I hope) guarantee
69                  * no false positives.
70                  */
71                 if (strstr(s->buf, " Rfork") != nil) {
72                         char *a[8];
73                         char *rf;
74
75                         rf = strdup(s->buf);
76                         if (tokenize(rf, a, 8) == 5) {
77                                 ulong flags;
78
79                                 flags = strtoul(a[4], 0, 16);
80                                 if (flags & RFPROC)
81                                         forking = 1;
82                         }
83                         free(rf);
84                 }
85                 sendp(out, s);
86                 cwrite(cfd, truss, "startsyscall", 12);
87                 s = mallocz(sizeof(Str) + Bufsize, 1);
88                 s->buf = (char *)&s[1];
89         }
90         sendp(quit, nil);
91         threadexitsall(nil);
92 }
93
94 void
95 writer(void *)
96 {
97         int newpid;
98         Alt a[4];
99         Str *s;
100
101         a[0].op = CHANRCV;
102         a[0].c = quit;
103         a[0].v = nil;
104         a[1].op = CHANRCV;
105         a[1].c = out;
106         a[1].v = &s;
107         a[2].op = CHANRCV;
108         a[2].c = forkc;
109         a[2].v = &newpid;
110         a[3].op = CHANEND;
111
112         for(;;)
113                 switch(alt(a)){
114                 case 0:
115                         nread--;
116                         if(nread <= 0)
117                                 goto done;
118                         break;
119                 case 1:
120                         /* it's a nice null terminated thing */
121                         fprint(2, "%s", s->buf);
122                         free(s);
123                         break;
124                 case 2:
125                         // procrfork(reader, (void*)newpid, Stacksize, 0);
126                         nread++;
127                         break;
128                 }
129 done:
130         exits(nil);
131 }
132
133 void
134 usage(void)
135 {
136         fprint(2, "Usage: ratrace [-c cmd [arg...]] | [pid]\n");
137         exits("usage");
138 }
139
140 void
141 threadmain(int argc, char **argv)
142 {
143         int pid;
144         char *cmd = nil;
145         char **args = nil;
146
147         /*
148          * don't bother with fancy arg processing, because it picks up options
149          * for the command you are starting.  Just check for -c as argv[1]
150          * and then take it from there.
151          */
152         if (argc < 2)
153                 usage();
154         if (argv[1][0] == '-')
155                 switch(argv[1][1]) {
156                 case 'c':
157                         if (argc < 3)
158                                 usage();
159                         cmd = strdup(argv[2]);
160                         args = &argv[2];
161                         break;
162                 default:
163                         usage();
164                 }
165
166         /* run a command? */
167         if(cmd) {
168                 pid = fork();
169                 if (pid < 0)
170                         sysfatal("fork failed: %r");
171                 if(pid == 0) {
172                         exec(cmd, args);
173                         if(cmd[0] != '/')
174                                 exec(smprint("/bin/%s", cmd), args);
175                         sysfatal("exec %s failed: %r", cmd);
176                 }
177         } else {
178                 if(argc != 2)
179                         usage();
180                 pid = atoi(argv[1]);
181         }
182
183         out   = chancreate(sizeof(char*), 0);
184         quit  = chancreate(sizeof(char*), 0);
185         forkc = chancreate(sizeof(ulong *), 0);
186         nread++;
187         procrfork(writer, nil, Stacksize, 0);
188         reader((void*)pid);
189 }