]>
Commit | Line | Data |
---|---|---|
a846e9cd MH |
1 | /* -*- linux-c -*- |
2 | * | |
3 | * mainloop - staprun main loop | |
4 | * | |
5 | * This file is part of systemtap, and is free software. You can | |
6 | * redistribute it and/or modify it under the terms of the GNU General | |
7 | * Public License (GPL); either version 2, or (at your option) any | |
8 | * later version. | |
9 | * | |
9012e89f | 10 | * Copyright (C) 2005-2008 Red Hat Inc. |
a846e9cd MH |
11 | */ |
12 | ||
13 | #include "staprun.h" | |
5eddf13b | 14 | #include <sys/utsname.h> |
a846e9cd MH |
15 | |
16 | /* globals */ | |
a846e9cd | 17 | int ncpus; |
56bae342 | 18 | int use_old_transport = 0; |
a846e9cd | 19 | |
83c5b5fe MH |
20 | static void sigproc(int signum) |
21 | { | |
22 | dbug(2, "sigproc %d (%s)\n", signum, strsignal(signum)); | |
23 | ||
24 | if (signum == SIGCHLD) { | |
25 | pid_t pid = waitpid(-1, NULL, WNOHANG); | |
26 | if (pid != target_pid) | |
27 | return; | |
28 | send_request(STP_EXIT, NULL, 0); | |
29 | } else if (signum == SIGQUIT) | |
aaf2af3e | 30 | cleanup_and_exit(2); |
83c5b5fe MH |
31 | else if (signum == SIGINT || signum == SIGHUP || signum == SIGTERM) |
32 | send_request(STP_EXIT, NULL, 0); | |
33 | } | |
34 | ||
17dd0742 | 35 | static void setup_main_signals(int cleanup) |
83c5b5fe MH |
36 | { |
37 | struct sigaction a; | |
38 | memset(&a, 0, sizeof(a)); | |
39 | sigfillset(&a.sa_mask); | |
17dd0742 MH |
40 | if (cleanup == 0) { |
41 | a.sa_handler = sigproc; | |
42 | sigaction(SIGCHLD, &a, NULL); | |
aaf2af3e | 43 | } else |
17dd0742 | 44 | a.sa_handler = SIG_IGN; |
83c5b5fe MH |
45 | sigaction(SIGINT, &a, NULL); |
46 | sigaction(SIGTERM, &a, NULL); | |
47 | sigaction(SIGHUP, &a, NULL); | |
83c5b5fe MH |
48 | sigaction(SIGQUIT, &a, NULL); |
49 | } | |
50 | ||
a846e9cd MH |
51 | /* |
52 | * start_cmd forks the command given on the command line | |
53 | * with the "-c" option. It will not exec that command | |
54 | * until it received signal SIGUSR1. We do it this way because | |
55 | * we must have the pid of the forked command so it can be set to | |
56 | * the module and made available internally as _stp_target. | |
57 | * SIGUSR1 is sent from stp_main_loop() below when it receives | |
58 | * STP_START from the module. | |
59 | */ | |
60 | void start_cmd(void) | |
61 | { | |
62 | pid_t pid; | |
63 | sigset_t usrset; | |
b574c7b9 MH |
64 | struct sigaction a; |
65 | ||
a846e9cd MH |
66 | sigemptyset(&usrset); |
67 | sigaddset(&usrset, SIGUSR1); | |
83c5b5fe | 68 | pthread_sigmask(SIG_BLOCK, &usrset, NULL); |
a846e9cd | 69 | |
b574c7b9 MH |
70 | /* if we are execing a target cmd, ignore ^C in stapio */ |
71 | /* and let the target cmd get it. */ | |
72 | sigemptyset(&a.sa_mask); | |
73 | a.sa_flags = 0; | |
74 | a.sa_handler = SIG_IGN; | |
75 | sigaction(SIGINT, &a, NULL); | |
76 | ||
aaf2af3e | 77 | dbug(1, "execing target_cmd %s\n", target_cmd); |
a846e9cd | 78 | if ((pid = fork()) < 0) { |
5eddf13b DS |
79 | _perr("fork"); |
80 | exit(1); | |
a846e9cd MH |
81 | } else if (pid == 0) { |
82 | int signum; | |
83 | ||
b574c7b9 MH |
84 | a.sa_handler = SIG_DFL; |
85 | sigaction(SIGINT, &a, NULL); | |
86 | ||
8366a7a1 | 87 | /* commands we fork need to run at normal priority */ |
aaf2af3e FCE |
88 | setpriority(PRIO_PROCESS, 0, 0); |
89 | ||
a846e9cd MH |
90 | /* wait here until signaled */ |
91 | sigwait(&usrset, &signum); | |
92 | ||
93 | if (execl("/bin/sh", "sh", "-c", target_cmd, NULL) < 0) | |
94 | perror(target_cmd); | |
5eddf13b | 95 | _exit(1); |
a846e9cd MH |
96 | } |
97 | target_pid = pid; | |
98 | } | |
99 | ||
100 | /** | |
101 | * system_cmd() executes system commands in response | |
102 | * to an STP_SYSTEM message from the module. These | |
103 | * messages are sent by the system() systemtap function. | |
a846e9cd MH |
104 | */ |
105 | void system_cmd(char *cmd) | |
106 | { | |
107 | pid_t pid; | |
108 | ||
aaf2af3e | 109 | dbug(2, "system %s\n", cmd); |
a846e9cd | 110 | if ((pid = fork()) < 0) { |
5eddf13b | 111 | _perr("fork"); |
a846e9cd | 112 | } else if (pid == 0) { |
aaf2af3e | 113 | setpriority(PRIO_PROCESS, 0, 0); |
a846e9cd | 114 | if (execl("/bin/sh", "sh", "-c", cmd, NULL) < 0) |
5eddf13b DS |
115 | perr("%s", cmd); |
116 | _exit(1); | |
a846e9cd MH |
117 | } |
118 | } | |
119 | ||
5eddf13b DS |
120 | /* This is only used in the old relayfs code */ |
121 | static void read_buffer_info(void) | |
a846e9cd | 122 | { |
5eddf13b DS |
123 | char buf[PATH_MAX]; |
124 | struct statfs st; | |
125 | int fd, len, ret; | |
126 | ||
127 | if (!use_old_transport) | |
128 | return; | |
129 | ||
aaf2af3e | 130 | if (statfs("/sys/kernel/debug", &st) == 0 && (int)st.f_type == (int)DEBUGFS_MAGIC) |
5eddf13b DS |
131 | return; |
132 | ||
133 | if (sprintf_chk(buf, "/proc/systemtap/%s/bufsize", modname)) | |
134 | return; | |
135 | fd = open(buf, O_RDONLY); | |
136 | if (fd < 0) | |
137 | return; | |
138 | ||
139 | len = read(fd, buf, sizeof(buf)); | |
140 | if (len <= 0) { | |
141 | perr("Couldn't read bufsize"); | |
142 | close(fd); | |
143 | return; | |
144 | } | |
145 | ret = sscanf(buf, "%u,%u", &n_subbufs, &subbuf_size); | |
146 | if (ret != 2) | |
147 | perr("Couldn't read bufsize"); | |
148 | ||
149 | dbug(2, "n_subbufs= %u, size=%u\n", n_subbufs, subbuf_size); | |
150 | close(fd); | |
151 | return; | |
a846e9cd MH |
152 | } |
153 | ||
a846e9cd | 154 | /** |
5eddf13b | 155 | * init_stapio - initialize the app |
a846e9cd MH |
156 | * @print_summary: boolean, print summary or not at end of run |
157 | * | |
158 | * Returns 0 on success, negative otherwise. | |
159 | */ | |
5eddf13b | 160 | int init_stapio(void) |
a846e9cd | 161 | { |
5eddf13b | 162 | dbug(2, "init_stapio\n"); |
a846e9cd | 163 | |
5eddf13b | 164 | /* create control channel */ |
2972246a | 165 | use_old_transport = init_ctl_channel(0); |
b38d9a7d | 166 | if (use_old_transport < 0) { |
5eddf13b DS |
167 | err("Failed to initialize control channel.\n"); |
168 | return -1; | |
56bae342 | 169 | } |
5eddf13b | 170 | read_buffer_info(); |
56bae342 | 171 | |
5d65678d | 172 | if (attach_mod) { |
5eddf13b | 173 | dbug(2, "Attaching\n"); |
da5bc006 MH |
174 | if (use_old_transport) { |
175 | if (init_oldrelayfs() < 0) { | |
176 | close_ctl_channel(); | |
177 | return -1; | |
aaf2af3e | 178 | } |
da5bc006 MH |
179 | } else { |
180 | if (init_relayfs() < 0) { | |
181 | close_ctl_channel(); | |
182 | return -1; | |
183 | } | |
5d65678d MH |
184 | } |
185 | return 0; | |
186 | } | |
187 | ||
a846e9cd MH |
188 | /* fork target_cmd if requested. */ |
189 | /* It will not actually exec until signalled. */ | |
190 | if (target_cmd) | |
191 | start_cmd(); | |
192 | ||
193 | return 0; | |
a846e9cd MH |
194 | } |
195 | ||
5eddf13b DS |
196 | /* cleanup_and_exit() closed channels and frees memory |
197 | * then exits with the following status codes: | |
198 | * 1 - failed to initialize. | |
199 | * 2 - disconnected | |
200 | * 3 - initialized | |
201 | */ | |
aaf2af3e | 202 | void cleanup_and_exit(int closed) |
a846e9cd | 203 | { |
a846e9cd MH |
204 | pid_t err; |
205 | static int exiting = 0; | |
206 | ||
207 | if (exiting) | |
208 | return; | |
209 | exiting = 1; | |
210 | ||
17dd0742 MH |
211 | setup_main_signals(1); |
212 | ||
83c5b5fe | 213 | dbug(1, "CLEANUP AND EXIT closed=%d\n", closed); |
a846e9cd MH |
214 | |
215 | /* what about child processes? we will wait for them here. */ | |
216 | err = waitpid(-1, NULL, WNOHANG); | |
217 | if (err >= 0) | |
5eddf13b | 218 | err("\nWaiting for processes to exit\n"); |
aaf2af3e | 219 | while (wait(NULL) > 0) ; |
a846e9cd | 220 | |
56bae342 | 221 | if (use_old_transport) |
da5bc006 | 222 | close_oldrelayfs(closed == 2); |
56bae342 MH |
223 | else |
224 | close_relayfs(); | |
a846e9cd | 225 | |
83c5b5fe | 226 | dbug(1, "closing control channel\n"); |
a846e9cd MH |
227 | close_ctl_channel(); |
228 | ||
5eddf13b | 229 | if (initialized == 2 && closed == 2) { |
aaf2af3e | 230 | err("\nDisconnecting from systemtap module.\n" "To reconnect, type \"staprun -A %s\"\n", modname); |
5eddf13b DS |
231 | } else if (initialized) |
232 | closed = 3; | |
233 | else | |
234 | closed = 1; | |
235 | exit(closed); | |
a846e9cd MH |
236 | } |
237 | ||
a846e9cd MH |
238 | /** |
239 | * stp_main_loop - loop forever reading data | |
240 | */ | |
a846e9cd MH |
241 | |
242 | int stp_main_loop(void) | |
243 | { | |
ff3e10e0 | 244 | ssize_t nb; |
a846e9cd | 245 | void *data; |
aaf2af3e | 246 | uint32_t type; |
a846e9cd | 247 | FILE *ofp = stdout; |
264d469c | 248 | char recvbuf[8196]; |
a846e9cd MH |
249 | |
250 | setvbuf(ofp, (char *)NULL, _IOLBF, 0); | |
17dd0742 | 251 | setup_main_signals(0); |
a846e9cd | 252 | |
83c5b5fe | 253 | dbug(2, "in main loop\n"); |
9012e89f | 254 | send_request(STP_READY, NULL, 0); |
a846e9cd | 255 | |
aaf2af3e | 256 | while (1) { /* handle messages from control channel */ |
a846e9cd MH |
257 | nb = read(control_channel, recvbuf, sizeof(recvbuf)); |
258 | if (nb <= 0) { | |
5eddf13b DS |
259 | if (errno != EINTR) |
260 | _perr("Unexpected EOF in read (nb=%ld)", (long)nb); | |
a846e9cd MH |
261 | continue; |
262 | } | |
a846e9cd | 263 | |
aaf2af3e FCE |
264 | type = *(uint32_t *)recvbuf; |
265 | data = (void *)(recvbuf + sizeof(uint32_t)); | |
266 | nb -= sizeof(uint32_t); | |
267 | ||
268 | switch (type) { | |
a846e9cd MH |
269 | #ifdef STP_OLD_TRANSPORT |
270 | case STP_REALTIME_DATA: | |
aaf2af3e FCE |
271 | { |
272 | ssize_t bw = write(out_fd[0], data, nb); | |
273 | if (bw >= 0 && bw != nb) { | |
274 | nb = nb - bw; | |
275 | bw = write(out_fd[0], data, nb); | |
276 | } | |
277 | if (bw != nb) { | |
278 | _perr("write error (nb=%ld)", (long)nb); | |
279 | cleanup_and_exit(1); | |
280 | } | |
281 | break; | |
ff3e10e0 | 282 | } |
a846e9cd MH |
283 | #endif |
284 | case STP_OOB_DATA: | |
aaf2af3e | 285 | fputs((char *)data, stderr); |
a846e9cd | 286 | break; |
aaf2af3e FCE |
287 | case STP_EXIT: |
288 | { | |
289 | /* module asks us to unload it and exit */ | |
290 | int *closed = (int *)data; | |
291 | dbug(2, "got STP_EXIT, closed=%d\n", *closed); | |
292 | cleanup_and_exit(*closed); | |
293 | break; | |
294 | } | |
295 | case STP_START: | |
296 | { | |
297 | struct _stp_msg_start *t = (struct _stp_msg_start *)data; | |
298 | dbug(2, "probe_start() returned %d\n", t->res); | |
299 | if (t->res < 0) { | |
300 | if (target_cmd) | |
301 | kill(target_pid, SIGKILL); | |
302 | cleanup_and_exit(1); | |
303 | } else if (target_cmd) | |
304 | kill(target_pid, SIGUSR1); | |
305 | break; | |
306 | } | |
a846e9cd | 307 | case STP_SYSTEM: |
aaf2af3e FCE |
308 | { |
309 | struct _stp_msg_cmd *c = (struct _stp_msg_cmd *)data; | |
310 | dbug(2, "STP_SYSTEM: %s\n", c->cmd); | |
311 | system_cmd(c->cmd); | |
312 | break; | |
313 | } | |
a846e9cd | 314 | case STP_TRANSPORT: |
aaf2af3e FCE |
315 | { |
316 | struct _stp_msg_start ts; | |
317 | if (use_old_transport) { | |
318 | if (init_oldrelayfs() < 0) | |
319 | cleanup_and_exit(1); | |
320 | } else { | |
321 | if (init_relayfs() < 0) | |
322 | cleanup_and_exit(1); | |
323 | } | |
324 | ts.target = target_pid; | |
325 | initialized = 2; | |
326 | send_request(STP_START, &ts, sizeof(ts)); | |
327 | if (load_only) | |
328 | cleanup_and_exit(2); | |
329 | break; | |
330 | } | |
331 | case STP_UNWIND: | |
332 | { | |
333 | int len; | |
334 | char *ptr = (char *)data; | |
335 | while (nb > 0) { | |
336 | send_unwind_data(ptr); | |
337 | len = strlen(ptr) + 1; | |
338 | ptr += len; | |
339 | nb -= len; | |
340 | } | |
341 | break; | |
a846e9cd | 342 | } |
a846e9cd | 343 | default: |
5eddf13b | 344 | err("WARNING: ignored message of type %d\n", (type)); |
a846e9cd MH |
345 | } |
346 | } | |
347 | fclose(ofp); | |
348 | return 0; | |
349 | } |