Commit | Line | Data |
---|---|---|
d26e3629 | 1 | /* Linux-specific functions to retrieve OS data. |
326b0c12 | 2 | |
42a4f53d | 3 | Copyright (C) 2009-2019 Free Software Foundation, Inc. |
d26e3629 KY |
4 | |
5 | This file is part of GDB. | |
6 | ||
7 | This program is free software; you can redistribute it and/or modify | |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation; either version 3 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | This program is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
19 | ||
268a13a5 | 20 | #include "gdbsupport/common-defs.h" |
d26e3629 KY |
21 | #include "linux-osdata.h" |
22 | ||
23 | #include <sys/types.h> | |
b245bdfc | 24 | #include <sys/sysinfo.h> |
d26e3629 | 25 | #include <ctype.h> |
d26e3629 KY |
26 | #include <utmp.h> |
27 | #include <time.h> | |
28 | #include <unistd.h> | |
29 | #include <pwd.h> | |
30 | #include <grp.h> | |
31 | #include <netdb.h> | |
32 | #include <netinet/in.h> | |
33 | #include <arpa/inet.h> | |
34 | ||
268a13a5 TT |
35 | #include "gdbsupport/xml-utils.h" |
36 | #include "gdbsupport/buffer.h" | |
2978b111 | 37 | #include <dirent.h> |
53ce3c39 | 38 | #include <sys/stat.h> |
268a13a5 | 39 | #include "gdbsupport/filestuff.h" |
b129dcac | 40 | #include <algorithm> |
d26e3629 | 41 | |
2978b111 TT |
42 | #define NAMELEN(dirent) strlen ((dirent)->d_name) |
43 | ||
85d4a676 SS |
44 | /* Define PID_T to be a fixed size that is at least as large as pid_t, |
45 | so that reading pid values embedded in /proc works | |
46 | consistently. */ | |
47 | ||
48 | typedef long long PID_T; | |
49 | ||
50 | /* Define TIME_T to be at least as large as time_t, so that reading | |
51 | time values embedded in /proc works consistently. */ | |
52 | ||
53 | typedef long long TIME_T; | |
54 | ||
55 | #define MAX_PID_T_STRLEN (sizeof ("-9223372036854775808") - 1) | |
56 | ||
57 | /* Returns the CPU core that thread PTID is currently running on. */ | |
326b0c12 | 58 | |
2e794194 JK |
59 | /* Compute and return the processor core of a given thread. */ |
60 | ||
d26e3629 KY |
61 | int |
62 | linux_common_core_of_thread (ptid_t ptid) | |
63 | { | |
85d4a676 | 64 | char filename[sizeof ("/proc//task//stat") + 2 * MAX_PID_T_STRLEN]; |
d26e3629 KY |
65 | char *content = NULL; |
66 | char *p; | |
67 | char *ts = 0; | |
68 | int content_read = 0; | |
69 | int i; | |
70 | int core; | |
71 | ||
85d4a676 | 72 | sprintf (filename, "/proc/%lld/task/%lld/stat", |
e38504b3 | 73 | (PID_T) ptid.pid (), (PID_T) ptid.lwp ()); |
d419f42d | 74 | gdb_file_up f = gdb_fopen_cloexec (filename, "r"); |
d26e3629 KY |
75 | if (!f) |
76 | return -1; | |
77 | ||
78 | for (;;) | |
79 | { | |
80 | int n; | |
224c3ddb | 81 | content = (char *) xrealloc (content, content_read + 1024); |
d419f42d | 82 | n = fread (content + content_read, 1, 1024, f.get ()); |
d26e3629 KY |
83 | content_read += n; |
84 | if (n < 1024) | |
85 | { | |
86 | content[content_read] = '\0'; | |
87 | break; | |
88 | } | |
89 | } | |
90 | ||
184cd072 JK |
91 | /* ps command also relies on no trailing fields ever contain ')'. */ |
92 | p = strrchr (content, ')'); | |
d26e3629 KY |
93 | if (p != NULL) |
94 | p++; | |
95 | ||
96 | /* If the first field after program name has index 0, then core number is | |
97 | the field with index 36. There's no constant for that anywhere. */ | |
98 | if (p != NULL) | |
99 | p = strtok_r (p, " ", &ts); | |
100 | for (i = 0; p != NULL && i != 36; ++i) | |
101 | p = strtok_r (NULL, " ", &ts); | |
102 | ||
103 | if (p == NULL || sscanf (p, "%d", &core) == 0) | |
104 | core = -1; | |
105 | ||
106 | xfree (content); | |
d26e3629 KY |
107 | |
108 | return core; | |
109 | } | |
110 | ||
85d4a676 SS |
111 | /* Finds the command-line of process PID and copies it into COMMAND. |
112 | At most MAXLEN characters are copied. If the command-line cannot | |
113 | be found, PID is copied into command in text-form. */ | |
114 | ||
d26e3629 | 115 | static void |
85d4a676 | 116 | command_from_pid (char *command, int maxlen, PID_T pid) |
d26e3629 | 117 | { |
528e1572 | 118 | std::string stat_path = string_printf ("/proc/%lld/stat", pid); |
d419f42d | 119 | gdb_file_up fp = gdb_fopen_cloexec (stat_path, "r"); |
326b0c12 | 120 | |
d26e3629 | 121 | command[0] = '\0'; |
326b0c12 | 122 | |
d26e3629 KY |
123 | if (fp) |
124 | { | |
125 | /* sizeof (cmd) should be greater or equal to TASK_COMM_LEN (in | |
126 | include/linux/sched.h in the Linux kernel sources) plus two | |
127 | (for the brackets). */ | |
f60db4f0 | 128 | char cmd[18]; |
85d4a676 | 129 | PID_T stat_pid; |
d419f42d | 130 | int items_read = fscanf (fp.get (), "%lld %17s", &stat_pid, cmd); |
326b0c12 | 131 | |
d26e3629 KY |
132 | if (items_read == 2 && pid == stat_pid) |
133 | { | |
134 | cmd[strlen (cmd) - 1] = '\0'; /* Remove trailing parenthesis. */ | |
135 | strncpy (command, cmd + 1, maxlen); /* Ignore leading parenthesis. */ | |
136 | } | |
d26e3629 KY |
137 | } |
138 | else | |
139 | { | |
140 | /* Return the PID if a /proc entry for the process cannot be found. */ | |
85d4a676 | 141 | snprintf (command, maxlen, "%lld", pid); |
d26e3629 KY |
142 | } |
143 | ||
144 | command[maxlen - 1] = '\0'; /* Ensure string is null-terminated. */ | |
d26e3629 KY |
145 | } |
146 | ||
85d4a676 SS |
147 | /* Returns the command-line of the process with the given PID. The |
148 | returned string needs to be freed using xfree after use. */ | |
d26e3629 KY |
149 | |
150 | static char * | |
85d4a676 | 151 | commandline_from_pid (PID_T pid) |
d26e3629 | 152 | { |
a9925d4f | 153 | std::string pathname = string_printf ("/proc/%lld/cmdline", pid); |
d26e3629 | 154 | char *commandline = NULL; |
d419f42d | 155 | gdb_file_up f = gdb_fopen_cloexec (pathname, "r"); |
d26e3629 KY |
156 | |
157 | if (f) | |
158 | { | |
159 | size_t len = 0; | |
160 | ||
d419f42d | 161 | while (!feof (f.get ())) |
d26e3629 KY |
162 | { |
163 | char buf[1024]; | |
d419f42d | 164 | size_t read_bytes = fread (buf, 1, sizeof (buf), f.get ()); |
326b0c12 | 165 | |
d26e3629 KY |
166 | if (read_bytes) |
167 | { | |
168 | commandline = (char *) xrealloc (commandline, len + read_bytes + 1); | |
169 | memcpy (commandline + len, buf, read_bytes); | |
170 | len += read_bytes; | |
171 | } | |
172 | } | |
173 | ||
d26e3629 KY |
174 | if (commandline) |
175 | { | |
176 | size_t i; | |
177 | ||
178 | /* Replace null characters with spaces. */ | |
179 | for (i = 0; i < len; ++i) | |
180 | if (commandline[i] == '\0') | |
181 | commandline[i] = ' '; | |
182 | ||
183 | commandline[len] = '\0'; | |
184 | } | |
185 | else | |
186 | { | |
85d4a676 SS |
187 | /* Return the command in square brackets if the command-line |
188 | is empty. */ | |
d26e3629 KY |
189 | commandline = (char *) xmalloc (32); |
190 | commandline[0] = '['; | |
191 | command_from_pid (commandline + 1, 31, pid); | |
192 | ||
193 | len = strlen (commandline); | |
194 | if (len < 31) | |
195 | strcat (commandline, "]"); | |
196 | } | |
197 | } | |
198 | ||
d26e3629 KY |
199 | return commandline; |
200 | } | |
201 | ||
85d4a676 SS |
202 | /* Finds the user name for the user UID and copies it into USER. At |
203 | most MAXLEN characters are copied. */ | |
204 | ||
d26e3629 KY |
205 | static void |
206 | user_from_uid (char *user, int maxlen, uid_t uid) | |
207 | { | |
208 | struct passwd *pwentry = getpwuid (uid); | |
326b0c12 | 209 | |
d26e3629 KY |
210 | if (pwentry) |
211 | { | |
212 | strncpy (user, pwentry->pw_name, maxlen); | |
85d4a676 SS |
213 | /* Ensure that the user name is null-terminated. */ |
214 | user[maxlen - 1] = '\0'; | |
d26e3629 KY |
215 | } |
216 | else | |
217 | user[0] = '\0'; | |
218 | } | |
219 | ||
85d4a676 SS |
220 | /* Finds the owner of process PID and returns the user id in OWNER. |
221 | Returns 0 if the owner was found, -1 otherwise. */ | |
222 | ||
d26e3629 | 223 | static int |
85d4a676 | 224 | get_process_owner (uid_t *owner, PID_T pid) |
d26e3629 KY |
225 | { |
226 | struct stat statbuf; | |
85d4a676 | 227 | char procentry[sizeof ("/proc/") + MAX_PID_T_STRLEN]; |
d26e3629 | 228 | |
85d4a676 | 229 | sprintf (procentry, "/proc/%lld", pid); |
326b0c12 | 230 | |
d26e3629 KY |
231 | if (stat (procentry, &statbuf) == 0 && S_ISDIR (statbuf.st_mode)) |
232 | { | |
233 | *owner = statbuf.st_uid; | |
234 | return 0; | |
235 | } | |
236 | else | |
237 | return -1; | |
238 | } | |
239 | ||
85d4a676 | 240 | /* Find the CPU cores used by process PID and return them in CORES. |
184cd072 | 241 | CORES points to an array of NUM_CORES elements. */ |
d26e3629 KY |
242 | |
243 | static int | |
184cd072 | 244 | get_cores_used_by_process (PID_T pid, int *cores, const int num_cores) |
d26e3629 | 245 | { |
85d4a676 | 246 | char taskdir[sizeof ("/proc/") + MAX_PID_T_STRLEN + sizeof ("/task") - 1]; |
d26e3629 KY |
247 | DIR *dir; |
248 | struct dirent *dp; | |
249 | int task_count = 0; | |
250 | ||
85d4a676 | 251 | sprintf (taskdir, "/proc/%lld/task", pid); |
d26e3629 | 252 | dir = opendir (taskdir); |
e5798bef | 253 | if (dir) |
d26e3629 | 254 | { |
e5798bef PA |
255 | while ((dp = readdir (dir)) != NULL) |
256 | { | |
85d4a676 | 257 | PID_T tid; |
e5798bef | 258 | int core; |
d26e3629 | 259 | |
e5798bef | 260 | if (!isdigit (dp->d_name[0]) |
85d4a676 | 261 | || NAMELEN (dp) > MAX_PID_T_STRLEN) |
e5798bef | 262 | continue; |
d26e3629 | 263 | |
85d4a676 | 264 | sscanf (dp->d_name, "%lld", &tid); |
fd79271b TT |
265 | core = linux_common_core_of_thread (ptid_t ((pid_t) pid, |
266 | (pid_t) tid, 0)); | |
d26e3629 | 267 | |
184cd072 | 268 | if (core >= 0 && core < num_cores) |
e5798bef PA |
269 | { |
270 | ++cores[core]; | |
271 | ++task_count; | |
272 | } | |
d26e3629 | 273 | } |
d26e3629 | 274 | |
e5798bef PA |
275 | closedir (dir); |
276 | } | |
d26e3629 KY |
277 | |
278 | return task_count; | |
279 | } | |
280 | ||
750b258e PW |
281 | static void |
282 | linux_xfer_osdata_processes (struct buffer *buffer) | |
d26e3629 | 283 | { |
750b258e | 284 | DIR *dirp; |
d26e3629 | 285 | |
750b258e | 286 | buffer_grow_str (buffer, "<osdata type=\"processes\">\n"); |
d26e3629 | 287 | |
750b258e PW |
288 | dirp = opendir ("/proc"); |
289 | if (dirp) | |
290 | { | |
291 | const int num_cores = sysconf (_SC_NPROCESSORS_ONLN); | |
292 | struct dirent *dp; | |
d26e3629 | 293 | |
750b258e | 294 | while ((dp = readdir (dirp)) != NULL) |
d26e3629 | 295 | { |
750b258e PW |
296 | PID_T pid; |
297 | uid_t owner; | |
298 | char user[UT_NAMESIZE]; | |
299 | char *command_line; | |
300 | int *cores; | |
301 | int task_count; | |
302 | char *cores_str; | |
303 | int i; | |
d26e3629 | 304 | |
750b258e PW |
305 | if (!isdigit (dp->d_name[0]) |
306 | || NAMELEN (dp) > MAX_PID_T_STRLEN) | |
307 | continue; | |
326b0c12 | 308 | |
750b258e PW |
309 | sscanf (dp->d_name, "%lld", &pid); |
310 | command_line = commandline_from_pid (pid); | |
311 | ||
312 | if (get_process_owner (&owner, pid) == 0) | |
313 | user_from_uid (user, sizeof (user), owner); | |
314 | else | |
315 | strcpy (user, "?"); | |
316 | ||
317 | /* Find CPU cores used by the process. */ | |
318 | cores = XCNEWVEC (int, num_cores); | |
319 | task_count = get_cores_used_by_process (pid, cores, num_cores); | |
320 | cores_str = (char *) xcalloc (task_count, sizeof ("4294967295") + 1); | |
321 | ||
322 | for (i = 0; i < num_cores && task_count > 0; ++i) | |
323 | if (cores[i]) | |
324 | { | |
325 | char core_str[sizeof ("4294967295")]; | |
326 | ||
327 | sprintf (core_str, "%d", i); | |
328 | strcat (cores_str, core_str); | |
329 | ||
330 | task_count -= cores[i]; | |
331 | if (task_count > 0) | |
332 | strcat (cores_str, ","); | |
333 | } | |
334 | ||
335 | xfree (cores); | |
336 | ||
337 | buffer_xml_printf | |
338 | (buffer, | |
339 | "<item>" | |
340 | "<column name=\"pid\">%lld</column>" | |
341 | "<column name=\"user\">%s</column>" | |
342 | "<column name=\"command\">%s</column>" | |
343 | "<column name=\"cores\">%s</column>" | |
344 | "</item>", | |
345 | pid, | |
346 | user, | |
347 | command_line ? command_line : "", | |
348 | cores_str); | |
349 | ||
350 | xfree (command_line); | |
351 | xfree (cores_str); | |
d26e3629 KY |
352 | } |
353 | ||
750b258e | 354 | closedir (dirp); |
d26e3629 KY |
355 | } |
356 | ||
750b258e | 357 | buffer_grow_str0 (buffer, "</osdata>\n"); |
d26e3629 KY |
358 | } |
359 | ||
b129dcac | 360 | /* A simple PID/PGID pair. */ |
85d4a676 | 361 | |
b129dcac | 362 | struct pid_pgid_entry |
85d4a676 | 363 | { |
b129dcac SM |
364 | pid_pgid_entry (PID_T pid_, PID_T pgid_) |
365 | : pid (pid_), pgid (pgid_) | |
366 | {} | |
85d4a676 | 367 | |
b129dcac SM |
368 | /* Return true if this pid is the leader of its process group. */ |
369 | ||
370 | bool is_leader () const | |
371 | { | |
372 | return pid == pgid; | |
373 | } | |
374 | ||
824dfcc3 | 375 | bool operator< (const pid_pgid_entry &other) const |
b129dcac SM |
376 | { |
377 | /* Sort by PGID. */ | |
378 | if (this->pgid != other.pgid) | |
379 | return this->pgid < other.pgid; | |
380 | ||
381 | /* Process group leaders always come first... */ | |
382 | if (this->is_leader ()) | |
463c08d1 TT |
383 | { |
384 | if (!other.is_leader ()) | |
385 | return true; | |
386 | } | |
387 | else if (other.is_leader ()) | |
b129dcac SM |
388 | return false; |
389 | ||
390 | /* ...else sort by PID. */ | |
391 | return this->pid < other.pid; | |
392 | } | |
393 | ||
394 | PID_T pid, pgid; | |
395 | }; | |
85d4a676 | 396 | |
750b258e | 397 | /* Collect all process groups from /proc in BUFFER. */ |
85d4a676 | 398 | |
750b258e PW |
399 | static void |
400 | linux_xfer_osdata_processgroups (struct buffer *buffer) | |
85d4a676 | 401 | { |
750b258e | 402 | DIR *dirp; |
85d4a676 | 403 | |
750b258e PW |
404 | buffer_grow_str (buffer, "<osdata type=\"process groups\">\n"); |
405 | ||
406 | dirp = opendir ("/proc"); | |
407 | if (dirp) | |
85d4a676 | 408 | { |
750b258e PW |
409 | std::vector<pid_pgid_entry> process_list; |
410 | struct dirent *dp; | |
85d4a676 | 411 | |
750b258e | 412 | process_list.reserve (512); |
85d4a676 | 413 | |
750b258e PW |
414 | /* Build list consisting of PIDs followed by their |
415 | associated PGID. */ | |
416 | while ((dp = readdir (dirp)) != NULL) | |
85d4a676 | 417 | { |
750b258e | 418 | PID_T pid, pgid; |
85d4a676 | 419 | |
750b258e PW |
420 | if (!isdigit (dp->d_name[0]) |
421 | || NAMELEN (dp) > MAX_PID_T_STRLEN) | |
422 | continue; | |
85d4a676 | 423 | |
750b258e PW |
424 | sscanf (dp->d_name, "%lld", &pid); |
425 | pgid = getpgid (pid); | |
85d4a676 | 426 | |
750b258e PW |
427 | if (pgid > 0) |
428 | process_list.emplace_back (pid, pgid); | |
429 | } | |
85d4a676 | 430 | |
750b258e | 431 | closedir (dirp); |
85d4a676 | 432 | |
750b258e PW |
433 | /* Sort the process list. */ |
434 | std::sort (process_list.begin (), process_list.end ()); | |
85d4a676 | 435 | |
750b258e PW |
436 | for (const pid_pgid_entry &entry : process_list) |
437 | { | |
438 | PID_T pid = entry.pid; | |
439 | PID_T pgid = entry.pgid; | |
440 | char leader_command[32]; | |
441 | char *command_line; | |
442 | ||
443 | command_from_pid (leader_command, sizeof (leader_command), pgid); | |
444 | command_line = commandline_from_pid (pid); | |
445 | ||
446 | buffer_xml_printf | |
447 | (buffer, | |
448 | "<item>" | |
449 | "<column name=\"pgid\">%lld</column>" | |
450 | "<column name=\"leader command\">%s</column>" | |
451 | "<column name=\"pid\">%lld</column>" | |
452 | "<column name=\"command line\">%s</column>" | |
453 | "</item>", | |
454 | pgid, | |
455 | leader_command, | |
456 | pid, | |
457 | command_line ? command_line : ""); | |
458 | ||
459 | xfree (command_line); | |
326b0c12 | 460 | } |
85d4a676 SS |
461 | } |
462 | ||
750b258e | 463 | buffer_grow_str0 (buffer, "</osdata>\n"); |
85d4a676 SS |
464 | } |
465 | ||
466 | /* Collect all the threads in /proc by iterating through processes and | |
750b258e | 467 | then tasks within each process in BUFFER. */ |
85d4a676 | 468 | |
750b258e PW |
469 | static void |
470 | linux_xfer_osdata_threads (struct buffer *buffer) | |
d26e3629 | 471 | { |
750b258e | 472 | DIR *dirp; |
d26e3629 | 473 | |
750b258e | 474 | buffer_grow_str (buffer, "<osdata type=\"threads\">\n"); |
d26e3629 | 475 | |
750b258e PW |
476 | dirp = opendir ("/proc"); |
477 | if (dirp) | |
478 | { | |
479 | struct dirent *dp; | |
d26e3629 | 480 | |
750b258e | 481 | while ((dp = readdir (dirp)) != NULL) |
d26e3629 | 482 | { |
750b258e PW |
483 | struct stat statbuf; |
484 | char procentry[sizeof ("/proc/4294967295")]; | |
d26e3629 | 485 | |
750b258e PW |
486 | if (!isdigit (dp->d_name[0]) |
487 | || NAMELEN (dp) > sizeof ("4294967295") - 1) | |
488 | continue; | |
d26e3629 | 489 | |
750b258e PW |
490 | xsnprintf (procentry, sizeof (procentry), "/proc/%s", |
491 | dp->d_name); | |
492 | if (stat (procentry, &statbuf) == 0 | |
493 | && S_ISDIR (statbuf.st_mode)) | |
494 | { | |
495 | DIR *dirp2; | |
496 | PID_T pid; | |
497 | char command[32]; | |
d26e3629 | 498 | |
750b258e PW |
499 | std::string pathname |
500 | = string_printf ("/proc/%s/task", dp->d_name); | |
d26e3629 | 501 | |
750b258e PW |
502 | pid = atoi (dp->d_name); |
503 | command_from_pid (command, sizeof (command), pid); | |
326b0c12 | 504 | |
750b258e | 505 | dirp2 = opendir (pathname.c_str ()); |
d26e3629 | 506 | |
750b258e PW |
507 | if (dirp2) |
508 | { | |
509 | struct dirent *dp2; | |
d26e3629 | 510 | |
750b258e | 511 | while ((dp2 = readdir (dirp2)) != NULL) |
d26e3629 | 512 | { |
750b258e PW |
513 | PID_T tid; |
514 | int core; | |
515 | ||
516 | if (!isdigit (dp2->d_name[0]) | |
517 | || NAMELEN (dp2) > sizeof ("4294967295") - 1) | |
518 | continue; | |
519 | ||
520 | tid = atoi (dp2->d_name); | |
521 | core = linux_common_core_of_thread (ptid_t (pid, tid, 0)); | |
522 | ||
523 | buffer_xml_printf | |
524 | (buffer, | |
525 | "<item>" | |
526 | "<column name=\"pid\">%lld</column>" | |
527 | "<column name=\"command\">%s</column>" | |
528 | "<column name=\"tid\">%lld</column>" | |
529 | "<column name=\"core\">%d</column>" | |
530 | "</item>", | |
531 | pid, | |
532 | command, | |
533 | tid, | |
534 | core); | |
d26e3629 | 535 | } |
750b258e PW |
536 | |
537 | closedir (dirp2); | |
d26e3629 KY |
538 | } |
539 | } | |
d26e3629 KY |
540 | } |
541 | ||
750b258e | 542 | closedir (dirp); |
d26e3629 KY |
543 | } |
544 | ||
750b258e | 545 | buffer_grow_str0 (buffer, "</osdata>\n"); |
d26e3629 KY |
546 | } |
547 | ||
750b258e | 548 | /* Collect data about the cpus/cores on the system in BUFFER. */ |
d33279b3 | 549 | |
750b258e PW |
550 | static void |
551 | linux_xfer_osdata_cpus (struct buffer *buffer) | |
d33279b3 | 552 | { |
750b258e | 553 | int first_item = 1; |
d33279b3 | 554 | |
750b258e | 555 | buffer_grow_str (buffer, "<osdata type=\"cpus\">\n"); |
d33279b3 | 556 | |
750b258e PW |
557 | gdb_file_up fp = gdb_fopen_cloexec ("/proc/cpuinfo", "r"); |
558 | if (fp != NULL) | |
559 | { | |
560 | char buf[8192]; | |
d33279b3 | 561 | |
750b258e | 562 | do |
d33279b3 | 563 | { |
750b258e | 564 | if (fgets (buf, sizeof (buf), fp.get ())) |
d33279b3 | 565 | { |
750b258e PW |
566 | char *key, *value; |
567 | int i = 0; | |
d33279b3 | 568 | |
ca3a04f6 CB |
569 | char *saveptr; |
570 | key = strtok_r (buf, ":", &saveptr); | |
750b258e PW |
571 | if (key == NULL) |
572 | continue; | |
d33279b3 | 573 | |
ca3a04f6 | 574 | value = strtok_r (NULL, ":", &saveptr); |
750b258e PW |
575 | if (value == NULL) |
576 | continue; | |
d33279b3 | 577 | |
750b258e PW |
578 | while (key[i] != '\t' && key[i] != '\0') |
579 | i++; | |
d33279b3 | 580 | |
750b258e | 581 | key[i] = '\0'; |
d33279b3 | 582 | |
750b258e PW |
583 | i = 0; |
584 | while (value[i] != '\t' && value[i] != '\0') | |
585 | i++; | |
d33279b3 | 586 | |
750b258e | 587 | value[i] = '\0'; |
d33279b3 | 588 | |
750b258e PW |
589 | if (strcmp (key, "processor") == 0) |
590 | { | |
591 | if (first_item) | |
592 | buffer_grow_str (buffer, "<item>"); | |
593 | else | |
594 | buffer_grow_str (buffer, "</item><item>"); | |
d33279b3 | 595 | |
750b258e | 596 | first_item = 0; |
d33279b3 | 597 | } |
d33279b3 | 598 | |
750b258e PW |
599 | buffer_xml_printf (buffer, |
600 | "<column name=\"%s\">%s</column>", | |
601 | key, | |
602 | value); | |
603 | } | |
d33279b3 | 604 | } |
750b258e | 605 | while (!feof (fp.get ())); |
d33279b3 | 606 | |
750b258e PW |
607 | if (first_item == 0) |
608 | buffer_grow_str (buffer, "</item>"); | |
d33279b3 AT |
609 | } |
610 | ||
750b258e | 611 | buffer_grow_str0 (buffer, "</osdata>\n"); |
d33279b3 AT |
612 | } |
613 | ||
85d4a676 | 614 | /* Collect all the open file descriptors found in /proc and put the details |
750b258e | 615 | found about them into BUFFER. */ |
85d4a676 | 616 | |
750b258e PW |
617 | static void |
618 | linux_xfer_osdata_fds (struct buffer *buffer) | |
85d4a676 | 619 | { |
750b258e | 620 | DIR *dirp; |
85d4a676 | 621 | |
750b258e | 622 | buffer_grow_str (buffer, "<osdata type=\"files\">\n"); |
85d4a676 | 623 | |
750b258e PW |
624 | dirp = opendir ("/proc"); |
625 | if (dirp) | |
626 | { | |
627 | struct dirent *dp; | |
85d4a676 | 628 | |
750b258e | 629 | while ((dp = readdir (dirp)) != NULL) |
85d4a676 | 630 | { |
750b258e PW |
631 | struct stat statbuf; |
632 | char procentry[sizeof ("/proc/4294967295")]; | |
85d4a676 | 633 | |
750b258e PW |
634 | if (!isdigit (dp->d_name[0]) |
635 | || NAMELEN (dp) > sizeof ("4294967295") - 1) | |
636 | continue; | |
85d4a676 | 637 | |
750b258e PW |
638 | xsnprintf (procentry, sizeof (procentry), "/proc/%s", |
639 | dp->d_name); | |
640 | if (stat (procentry, &statbuf) == 0 | |
641 | && S_ISDIR (statbuf.st_mode)) | |
642 | { | |
643 | DIR *dirp2; | |
644 | PID_T pid; | |
645 | char command[32]; | |
85d4a676 | 646 | |
750b258e PW |
647 | pid = atoi (dp->d_name); |
648 | command_from_pid (command, sizeof (command), pid); | |
85d4a676 | 649 | |
750b258e PW |
650 | std::string pathname |
651 | = string_printf ("/proc/%s/fd", dp->d_name); | |
652 | dirp2 = opendir (pathname.c_str ()); | |
85d4a676 | 653 | |
750b258e PW |
654 | if (dirp2) |
655 | { | |
656 | struct dirent *dp2; | |
85d4a676 | 657 | |
750b258e | 658 | while ((dp2 = readdir (dirp2)) != NULL) |
85d4a676 | 659 | { |
750b258e PW |
660 | char buf[1000]; |
661 | ssize_t rslt; | |
662 | ||
663 | if (!isdigit (dp2->d_name[0])) | |
664 | continue; | |
665 | ||
666 | std::string fdname | |
667 | = string_printf ("%s/%s", pathname.c_str (), | |
668 | dp2->d_name); | |
669 | rslt = readlink (fdname.c_str (), buf, | |
670 | sizeof (buf) - 1); | |
671 | if (rslt >= 0) | |
672 | buf[rslt] = '\0'; | |
673 | ||
674 | buffer_xml_printf | |
675 | (buffer, | |
676 | "<item>" | |
677 | "<column name=\"pid\">%s</column>" | |
678 | "<column name=\"command\">%s</column>" | |
679 | "<column name=\"file descriptor\">%s</column>" | |
680 | "<column name=\"name\">%s</column>" | |
681 | "</item>", | |
682 | dp->d_name, | |
683 | command, | |
684 | dp2->d_name, | |
685 | (rslt >= 0 ? buf : dp2->d_name)); | |
85d4a676 | 686 | } |
750b258e PW |
687 | |
688 | closedir (dirp2); | |
85d4a676 SS |
689 | } |
690 | } | |
85d4a676 SS |
691 | } |
692 | ||
750b258e | 693 | closedir (dirp); |
85d4a676 SS |
694 | } |
695 | ||
750b258e | 696 | buffer_grow_str0 (buffer, "</osdata>\n"); |
85d4a676 SS |
697 | } |
698 | ||
699 | /* Returns the socket state STATE in textual form. */ | |
700 | ||
701 | static const char * | |
702 | format_socket_state (unsigned char state) | |
703 | { | |
704 | /* Copied from include/net/tcp_states.h in the Linux kernel sources. */ | |
705 | enum { | |
706 | TCP_ESTABLISHED = 1, | |
707 | TCP_SYN_SENT, | |
708 | TCP_SYN_RECV, | |
709 | TCP_FIN_WAIT1, | |
710 | TCP_FIN_WAIT2, | |
711 | TCP_TIME_WAIT, | |
712 | TCP_CLOSE, | |
713 | TCP_CLOSE_WAIT, | |
714 | TCP_LAST_ACK, | |
715 | TCP_LISTEN, | |
716 | TCP_CLOSING | |
717 | }; | |
718 | ||
719 | switch (state) | |
720 | { | |
721 | case TCP_ESTABLISHED: | |
722 | return "ESTABLISHED"; | |
723 | case TCP_SYN_SENT: | |
724 | return "SYN_SENT"; | |
725 | case TCP_SYN_RECV: | |
726 | return "SYN_RECV"; | |
727 | case TCP_FIN_WAIT1: | |
728 | return "FIN_WAIT1"; | |
729 | case TCP_FIN_WAIT2: | |
730 | return "FIN_WAIT2"; | |
731 | case TCP_TIME_WAIT: | |
732 | return "TIME_WAIT"; | |
733 | case TCP_CLOSE: | |
734 | return "CLOSE"; | |
735 | case TCP_CLOSE_WAIT: | |
736 | return "CLOSE_WAIT"; | |
737 | case TCP_LAST_ACK: | |
738 | return "LAST_ACK"; | |
739 | case TCP_LISTEN: | |
740 | return "LISTEN"; | |
741 | case TCP_CLOSING: | |
742 | return "CLOSING"; | |
743 | default: | |
744 | return "(unknown)"; | |
745 | } | |
746 | } | |
747 | ||
748 | union socket_addr | |
749 | { | |
750 | struct sockaddr sa; | |
751 | struct sockaddr_in sin; | |
752 | struct sockaddr_in6 sin6; | |
753 | }; | |
754 | ||
755 | /* Auxiliary function used by linux_xfer_osdata_isocket. Formats | |
756 | information for all open internet sockets of type FAMILY on the | |
757 | system into BUFFER. If TCP is set, only TCP sockets are processed, | |
758 | otherwise only UDP sockets are processed. */ | |
759 | ||
760 | static void | |
761 | print_sockets (unsigned short family, int tcp, struct buffer *buffer) | |
762 | { | |
763 | const char *proc_file; | |
85d4a676 SS |
764 | |
765 | if (family == AF_INET) | |
766 | proc_file = tcp ? "/proc/net/tcp" : "/proc/net/udp"; | |
767 | else if (family == AF_INET6) | |
768 | proc_file = tcp ? "/proc/net/tcp6" : "/proc/net/udp6"; | |
769 | else | |
770 | return; | |
771 | ||
d419f42d | 772 | gdb_file_up fp = gdb_fopen_cloexec (proc_file, "r"); |
85d4a676 SS |
773 | if (fp) |
774 | { | |
775 | char buf[8192]; | |
776 | ||
777 | do | |
778 | { | |
d419f42d | 779 | if (fgets (buf, sizeof (buf), fp.get ())) |
85d4a676 SS |
780 | { |
781 | uid_t uid; | |
85d4a676 | 782 | unsigned int local_port, remote_port, state; |
85d4a676 | 783 | char local_address[NI_MAXHOST], remote_address[NI_MAXHOST]; |
85d4a676 SS |
784 | int result; |
785 | ||
f60db4f0 GB |
786 | #if NI_MAXHOST <= 32 |
787 | #error "local_address and remote_address buffers too small" | |
788 | #endif | |
789 | ||
85d4a676 | 790 | result = sscanf (buf, |
f60db4f0 | 791 | "%*d: %32[0-9A-F]:%X %32[0-9A-F]:%X %X %*X:%*X %*X:%*X %*X %d %*d %*u %*s\n", |
85d4a676 SS |
792 | local_address, &local_port, |
793 | remote_address, &remote_port, | |
794 | &state, | |
f60db4f0 | 795 | &uid); |
326b0c12 | 796 | |
f60db4f0 | 797 | if (result == 6) |
85d4a676 SS |
798 | { |
799 | union socket_addr locaddr, remaddr; | |
800 | size_t addr_size; | |
801 | char user[UT_NAMESIZE]; | |
802 | char local_service[NI_MAXSERV], remote_service[NI_MAXSERV]; | |
803 | ||
804 | if (family == AF_INET) | |
805 | { | |
806 | sscanf (local_address, "%X", | |
807 | &locaddr.sin.sin_addr.s_addr); | |
808 | sscanf (remote_address, "%X", | |
809 | &remaddr.sin.sin_addr.s_addr); | |
326b0c12 | 810 | |
85d4a676 SS |
811 | locaddr.sin.sin_port = htons (local_port); |
812 | remaddr.sin.sin_port = htons (remote_port); | |
813 | ||
814 | addr_size = sizeof (struct sockaddr_in); | |
815 | } | |
816 | else | |
817 | { | |
818 | sscanf (local_address, "%8X%8X%8X%8X", | |
819 | locaddr.sin6.sin6_addr.s6_addr32, | |
820 | locaddr.sin6.sin6_addr.s6_addr32 + 1, | |
821 | locaddr.sin6.sin6_addr.s6_addr32 + 2, | |
822 | locaddr.sin6.sin6_addr.s6_addr32 + 3); | |
823 | sscanf (remote_address, "%8X%8X%8X%8X", | |
824 | remaddr.sin6.sin6_addr.s6_addr32, | |
825 | remaddr.sin6.sin6_addr.s6_addr32 + 1, | |
826 | remaddr.sin6.sin6_addr.s6_addr32 + 2, | |
827 | remaddr.sin6.sin6_addr.s6_addr32 + 3); | |
828 | ||
829 | locaddr.sin6.sin6_port = htons (local_port); | |
830 | remaddr.sin6.sin6_port = htons (remote_port); | |
326b0c12 | 831 | |
85d4a676 SS |
832 | locaddr.sin6.sin6_flowinfo = 0; |
833 | remaddr.sin6.sin6_flowinfo = 0; | |
834 | locaddr.sin6.sin6_scope_id = 0; | |
835 | remaddr.sin6.sin6_scope_id = 0; | |
836 | ||
837 | addr_size = sizeof (struct sockaddr_in6); | |
838 | } | |
326b0c12 | 839 | |
85d4a676 | 840 | locaddr.sa.sa_family = remaddr.sa.sa_family = family; |
326b0c12 | 841 | |
85d4a676 SS |
842 | result = getnameinfo (&locaddr.sa, addr_size, |
843 | local_address, sizeof (local_address), | |
844 | local_service, sizeof (local_service), | |
845 | NI_NUMERICHOST | NI_NUMERICSERV | |
846 | | (tcp ? 0 : NI_DGRAM)); | |
847 | if (result) | |
848 | continue; | |
326b0c12 | 849 | |
85d4a676 SS |
850 | result = getnameinfo (&remaddr.sa, addr_size, |
851 | remote_address, | |
852 | sizeof (remote_address), | |
853 | remote_service, | |
854 | sizeof (remote_service), | |
855 | NI_NUMERICHOST | NI_NUMERICSERV | |
856 | | (tcp ? 0 : NI_DGRAM)); | |
857 | if (result) | |
858 | continue; | |
326b0c12 | 859 | |
85d4a676 | 860 | user_from_uid (user, sizeof (user), uid); |
326b0c12 | 861 | |
85d4a676 SS |
862 | buffer_xml_printf ( |
863 | buffer, | |
864 | "<item>" | |
865 | "<column name=\"local address\">%s</column>" | |
866 | "<column name=\"local port\">%s</column>" | |
867 | "<column name=\"remote address\">%s</column>" | |
868 | "<column name=\"remote port\">%s</column>" | |
869 | "<column name=\"state\">%s</column>" | |
870 | "<column name=\"user\">%s</column>" | |
326b0c12 | 871 | "<column name=\"family\">%s</column>" |
85d4a676 SS |
872 | "<column name=\"protocol\">%s</column>" |
873 | "</item>", | |
874 | local_address, | |
875 | local_service, | |
876 | remote_address, | |
877 | remote_service, | |
878 | format_socket_state (state), | |
879 | user, | |
880 | (family == AF_INET) ? "INET" : "INET6", | |
881 | tcp ? "STREAM" : "DGRAM"); | |
882 | } | |
883 | } | |
884 | } | |
d419f42d | 885 | while (!feof (fp.get ())); |
85d4a676 SS |
886 | } |
887 | } | |
888 | ||
750b258e | 889 | /* Collect data about internet sockets and write it into BUFFER. */ |
85d4a676 | 890 | |
750b258e PW |
891 | static void |
892 | linux_xfer_osdata_isockets (struct buffer *buffer) | |
85d4a676 | 893 | { |
750b258e | 894 | buffer_grow_str (buffer, "<osdata type=\"I sockets\">\n"); |
85d4a676 | 895 | |
750b258e PW |
896 | print_sockets (AF_INET, 1, buffer); |
897 | print_sockets (AF_INET, 0, buffer); | |
898 | print_sockets (AF_INET6, 1, buffer); | |
899 | print_sockets (AF_INET6, 0, buffer); | |
85d4a676 | 900 | |
750b258e | 901 | buffer_grow_str0 (buffer, "</osdata>\n"); |
85d4a676 SS |
902 | } |
903 | ||
904 | /* Converts the time SECONDS into textual form and copies it into a | |
905 | buffer TIME, with at most MAXLEN characters copied. */ | |
906 | ||
907 | static void | |
908 | time_from_time_t (char *time, int maxlen, TIME_T seconds) | |
909 | { | |
910 | if (!seconds) | |
911 | time[0] = '\0'; | |
912 | else | |
913 | { | |
914 | time_t t = (time_t) seconds; | |
326b0c12 | 915 | |
85d4a676 SS |
916 | strncpy (time, ctime (&t), maxlen); |
917 | time[maxlen - 1] = '\0'; | |
918 | } | |
919 | } | |
920 | ||
921 | /* Finds the group name for the group GID and copies it into GROUP. | |
922 | At most MAXLEN characters are copied. */ | |
923 | ||
924 | static void | |
925 | group_from_gid (char *group, int maxlen, gid_t gid) | |
926 | { | |
927 | struct group *grentry = getgrgid (gid); | |
326b0c12 | 928 | |
85d4a676 SS |
929 | if (grentry) |
930 | { | |
931 | strncpy (group, grentry->gr_name, maxlen); | |
932 | /* Ensure that the group name is null-terminated. */ | |
933 | group[maxlen - 1] = '\0'; | |
934 | } | |
935 | else | |
936 | group[0] = '\0'; | |
937 | } | |
938 | ||
939 | /* Collect data about shared memory recorded in /proc and write it | |
750b258e | 940 | into BUFFER. */ |
85d4a676 | 941 | |
750b258e PW |
942 | static void |
943 | linux_xfer_osdata_shm (struct buffer *buffer) | |
85d4a676 | 944 | { |
750b258e | 945 | buffer_grow_str (buffer, "<osdata type=\"shared memory\">\n"); |
85d4a676 | 946 | |
750b258e PW |
947 | gdb_file_up fp = gdb_fopen_cloexec ("/proc/sysvipc/shm", "r"); |
948 | if (fp) | |
85d4a676 | 949 | { |
750b258e | 950 | char buf[8192]; |
85d4a676 | 951 | |
750b258e PW |
952 | do |
953 | { | |
954 | if (fgets (buf, sizeof (buf), fp.get ())) | |
85d4a676 | 955 | { |
750b258e PW |
956 | key_t key; |
957 | uid_t uid, cuid; | |
958 | gid_t gid, cgid; | |
959 | PID_T cpid, lpid; | |
960 | int shmid, size, nattch; | |
961 | TIME_T atime, dtime, ctime; | |
962 | unsigned int perms; | |
963 | int items_read; | |
964 | ||
965 | items_read = sscanf (buf, | |
966 | "%d %d %o %d %lld %lld %d %u %u %u %u %lld %lld %lld", | |
967 | &key, &shmid, &perms, &size, | |
968 | &cpid, &lpid, | |
969 | &nattch, | |
970 | &uid, &gid, &cuid, &cgid, | |
971 | &atime, &dtime, &ctime); | |
972 | ||
973 | if (items_read == 14) | |
85d4a676 | 974 | { |
750b258e PW |
975 | char user[UT_NAMESIZE], group[UT_NAMESIZE]; |
976 | char cuser[UT_NAMESIZE], cgroup[UT_NAMESIZE]; | |
977 | char ccmd[32], lcmd[32]; | |
978 | char atime_str[32], dtime_str[32], ctime_str[32]; | |
979 | ||
980 | user_from_uid (user, sizeof (user), uid); | |
981 | group_from_gid (group, sizeof (group), gid); | |
982 | user_from_uid (cuser, sizeof (cuser), cuid); | |
983 | group_from_gid (cgroup, sizeof (cgroup), cgid); | |
984 | ||
985 | command_from_pid (ccmd, sizeof (ccmd), cpid); | |
986 | command_from_pid (lcmd, sizeof (lcmd), lpid); | |
987 | ||
988 | time_from_time_t (atime_str, sizeof (atime_str), atime); | |
989 | time_from_time_t (dtime_str, sizeof (dtime_str), dtime); | |
990 | time_from_time_t (ctime_str, sizeof (ctime_str), ctime); | |
991 | ||
992 | buffer_xml_printf | |
993 | (buffer, | |
994 | "<item>" | |
995 | "<column name=\"key\">%d</column>" | |
996 | "<column name=\"shmid\">%d</column>" | |
997 | "<column name=\"permissions\">%o</column>" | |
998 | "<column name=\"size\">%d</column>" | |
999 | "<column name=\"creator command\">%s</column>" | |
1000 | "<column name=\"last op. command\">%s</column>" | |
1001 | "<column name=\"num attached\">%d</column>" | |
1002 | "<column name=\"user\">%s</column>" | |
1003 | "<column name=\"group\">%s</column>" | |
1004 | "<column name=\"creator user\">%s</column>" | |
1005 | "<column name=\"creator group\">%s</column>" | |
1006 | "<column name=\"last shmat() time\">%s</column>" | |
1007 | "<column name=\"last shmdt() time\">%s</column>" | |
1008 | "<column name=\"last shmctl() time\">%s</column>" | |
1009 | "</item>", | |
1010 | key, | |
1011 | shmid, | |
1012 | perms, | |
1013 | size, | |
1014 | ccmd, | |
1015 | lcmd, | |
1016 | nattch, | |
1017 | user, | |
1018 | group, | |
1019 | cuser, | |
1020 | cgroup, | |
1021 | atime_str, | |
1022 | dtime_str, | |
1023 | ctime_str); | |
85d4a676 SS |
1024 | } |
1025 | } | |
85d4a676 | 1026 | } |
750b258e | 1027 | while (!feof (fp.get ())); |
85d4a676 SS |
1028 | } |
1029 | ||
750b258e | 1030 | buffer_grow_str0 (buffer, "</osdata>\n"); |
85d4a676 SS |
1031 | } |
1032 | ||
1033 | /* Collect data about semaphores recorded in /proc and write it | |
750b258e | 1034 | into BUFFER. */ |
85d4a676 | 1035 | |
750b258e PW |
1036 | static void |
1037 | linux_xfer_osdata_sem (struct buffer *buffer) | |
85d4a676 | 1038 | { |
750b258e | 1039 | buffer_grow_str (buffer, "<osdata type=\"semaphores\">\n"); |
85d4a676 | 1040 | |
750b258e PW |
1041 | gdb_file_up fp = gdb_fopen_cloexec ("/proc/sysvipc/sem", "r"); |
1042 | if (fp) | |
85d4a676 | 1043 | { |
750b258e | 1044 | char buf[8192]; |
326b0c12 | 1045 | |
750b258e PW |
1046 | do |
1047 | { | |
1048 | if (fgets (buf, sizeof (buf), fp.get ())) | |
85d4a676 | 1049 | { |
750b258e PW |
1050 | key_t key; |
1051 | uid_t uid, cuid; | |
1052 | gid_t gid, cgid; | |
1053 | unsigned int perms, nsems; | |
1054 | int semid; | |
1055 | TIME_T otime, ctime; | |
1056 | int items_read; | |
1057 | ||
1058 | items_read = sscanf (buf, | |
1059 | "%d %d %o %u %d %d %d %d %lld %lld", | |
1060 | &key, &semid, &perms, &nsems, | |
1061 | &uid, &gid, &cuid, &cgid, | |
1062 | &otime, &ctime); | |
1063 | ||
1064 | if (items_read == 10) | |
85d4a676 | 1065 | { |
750b258e PW |
1066 | char user[UT_NAMESIZE], group[UT_NAMESIZE]; |
1067 | char cuser[UT_NAMESIZE], cgroup[UT_NAMESIZE]; | |
1068 | char otime_str[32], ctime_str[32]; | |
1069 | ||
1070 | user_from_uid (user, sizeof (user), uid); | |
1071 | group_from_gid (group, sizeof (group), gid); | |
1072 | user_from_uid (cuser, sizeof (cuser), cuid); | |
1073 | group_from_gid (cgroup, sizeof (cgroup), cgid); | |
1074 | ||
1075 | time_from_time_t (otime_str, sizeof (otime_str), otime); | |
1076 | time_from_time_t (ctime_str, sizeof (ctime_str), ctime); | |
1077 | ||
1078 | buffer_xml_printf | |
1079 | (buffer, | |
1080 | "<item>" | |
1081 | "<column name=\"key\">%d</column>" | |
1082 | "<column name=\"semid\">%d</column>" | |
1083 | "<column name=\"permissions\">%o</column>" | |
1084 | "<column name=\"num semaphores\">%u</column>" | |
1085 | "<column name=\"user\">%s</column>" | |
1086 | "<column name=\"group\">%s</column>" | |
1087 | "<column name=\"creator user\">%s</column>" | |
1088 | "<column name=\"creator group\">%s</column>" | |
1089 | "<column name=\"last semop() time\">%s</column>" | |
1090 | "<column name=\"last semctl() time\">%s</column>" | |
1091 | "</item>", | |
1092 | key, | |
1093 | semid, | |
1094 | perms, | |
1095 | nsems, | |
1096 | user, | |
1097 | group, | |
1098 | cuser, | |
1099 | cgroup, | |
1100 | otime_str, | |
1101 | ctime_str); | |
85d4a676 SS |
1102 | } |
1103 | } | |
85d4a676 | 1104 | } |
750b258e | 1105 | while (!feof (fp.get ())); |
85d4a676 SS |
1106 | } |
1107 | ||
750b258e | 1108 | buffer_grow_str0 (buffer, "</osdata>\n"); |
85d4a676 SS |
1109 | } |
1110 | ||
1111 | /* Collect data about message queues recorded in /proc and write it | |
750b258e | 1112 | into BUFFER. */ |
85d4a676 | 1113 | |
750b258e PW |
1114 | static void |
1115 | linux_xfer_osdata_msg (struct buffer *buffer) | |
85d4a676 | 1116 | { |
750b258e | 1117 | buffer_grow_str (buffer, "<osdata type=\"message queues\">\n"); |
85d4a676 | 1118 | |
750b258e PW |
1119 | gdb_file_up fp = gdb_fopen_cloexec ("/proc/sysvipc/msg", "r"); |
1120 | if (fp) | |
85d4a676 | 1121 | { |
750b258e | 1122 | char buf[8192]; |
326b0c12 | 1123 | |
750b258e PW |
1124 | do |
1125 | { | |
1126 | if (fgets (buf, sizeof (buf), fp.get ())) | |
85d4a676 | 1127 | { |
750b258e PW |
1128 | key_t key; |
1129 | PID_T lspid, lrpid; | |
1130 | uid_t uid, cuid; | |
1131 | gid_t gid, cgid; | |
1132 | unsigned int perms, cbytes, qnum; | |
1133 | int msqid; | |
1134 | TIME_T stime, rtime, ctime; | |
1135 | int items_read; | |
1136 | ||
1137 | items_read = sscanf (buf, | |
1138 | "%d %d %o %u %u %lld %lld %d %d %d %d %lld %lld %lld", | |
1139 | &key, &msqid, &perms, &cbytes, &qnum, | |
1140 | &lspid, &lrpid, &uid, &gid, &cuid, &cgid, | |
1141 | &stime, &rtime, &ctime); | |
1142 | ||
1143 | if (items_read == 14) | |
85d4a676 | 1144 | { |
750b258e PW |
1145 | char user[UT_NAMESIZE], group[UT_NAMESIZE]; |
1146 | char cuser[UT_NAMESIZE], cgroup[UT_NAMESIZE]; | |
1147 | char lscmd[32], lrcmd[32]; | |
1148 | char stime_str[32], rtime_str[32], ctime_str[32]; | |
1149 | ||
1150 | user_from_uid (user, sizeof (user), uid); | |
1151 | group_from_gid (group, sizeof (group), gid); | |
1152 | user_from_uid (cuser, sizeof (cuser), cuid); | |
1153 | group_from_gid (cgroup, sizeof (cgroup), cgid); | |
1154 | ||
1155 | command_from_pid (lscmd, sizeof (lscmd), lspid); | |
1156 | command_from_pid (lrcmd, sizeof (lrcmd), lrpid); | |
1157 | ||
1158 | time_from_time_t (stime_str, sizeof (stime_str), stime); | |
1159 | time_from_time_t (rtime_str, sizeof (rtime_str), rtime); | |
1160 | time_from_time_t (ctime_str, sizeof (ctime_str), ctime); | |
1161 | ||
1162 | buffer_xml_printf | |
1163 | (buffer, | |
1164 | "<item>" | |
1165 | "<column name=\"key\">%d</column>" | |
1166 | "<column name=\"msqid\">%d</column>" | |
1167 | "<column name=\"permissions\">%o</column>" | |
1168 | "<column name=\"num used bytes\">%u</column>" | |
1169 | "<column name=\"num messages\">%u</column>" | |
1170 | "<column name=\"last msgsnd() command\">%s</column>" | |
1171 | "<column name=\"last msgrcv() command\">%s</column>" | |
1172 | "<column name=\"user\">%s</column>" | |
1173 | "<column name=\"group\">%s</column>" | |
1174 | "<column name=\"creator user\">%s</column>" | |
1175 | "<column name=\"creator group\">%s</column>" | |
1176 | "<column name=\"last msgsnd() time\">%s</column>" | |
1177 | "<column name=\"last msgrcv() time\">%s</column>" | |
1178 | "<column name=\"last msgctl() time\">%s</column>" | |
1179 | "</item>", | |
1180 | key, | |
1181 | msqid, | |
1182 | perms, | |
1183 | cbytes, | |
1184 | qnum, | |
1185 | lscmd, | |
1186 | lrcmd, | |
1187 | user, | |
1188 | group, | |
1189 | cuser, | |
1190 | cgroup, | |
1191 | stime_str, | |
1192 | rtime_str, | |
1193 | ctime_str); | |
85d4a676 SS |
1194 | } |
1195 | } | |
85d4a676 | 1196 | } |
750b258e | 1197 | while (!feof (fp.get ())); |
85d4a676 SS |
1198 | } |
1199 | ||
750b258e | 1200 | buffer_grow_str0 (buffer, "</osdata>\n"); |
85d4a676 SS |
1201 | } |
1202 | ||
1203 | /* Collect data about loaded kernel modules and write it into | |
750b258e | 1204 | BUFFER. */ |
85d4a676 | 1205 | |
750b258e PW |
1206 | static void |
1207 | linux_xfer_osdata_modules (struct buffer *buffer) | |
85d4a676 | 1208 | { |
750b258e | 1209 | buffer_grow_str (buffer, "<osdata type=\"modules\">\n"); |
85d4a676 | 1210 | |
750b258e PW |
1211 | gdb_file_up fp = gdb_fopen_cloexec ("/proc/modules", "r"); |
1212 | if (fp) | |
85d4a676 | 1213 | { |
750b258e | 1214 | char buf[8192]; |
326b0c12 | 1215 | |
750b258e PW |
1216 | do |
1217 | { | |
1218 | if (fgets (buf, sizeof (buf), fp.get ())) | |
85d4a676 | 1219 | { |
ca3a04f6 | 1220 | char *name, *dependencies, *status, *tmp, *saveptr; |
750b258e PW |
1221 | unsigned int size; |
1222 | unsigned long long address; | |
1223 | int uses; | |
85d4a676 | 1224 | |
ca3a04f6 | 1225 | name = strtok_r (buf, " ", &saveptr); |
750b258e PW |
1226 | if (name == NULL) |
1227 | continue; | |
f60db4f0 | 1228 | |
ca3a04f6 | 1229 | tmp = strtok_r (NULL, " ", &saveptr); |
750b258e PW |
1230 | if (tmp == NULL) |
1231 | continue; | |
1232 | if (sscanf (tmp, "%u", &size) != 1) | |
1233 | continue; | |
f60db4f0 | 1234 | |
ca3a04f6 | 1235 | tmp = strtok_r (NULL, " ", &saveptr); |
750b258e PW |
1236 | if (tmp == NULL) |
1237 | continue; | |
1238 | if (sscanf (tmp, "%d", &uses) != 1) | |
1239 | continue; | |
f60db4f0 | 1240 | |
ca3a04f6 | 1241 | dependencies = strtok_r (NULL, " ", &saveptr); |
750b258e PW |
1242 | if (dependencies == NULL) |
1243 | continue; | |
f60db4f0 | 1244 | |
ca3a04f6 | 1245 | status = strtok_r (NULL, " ", &saveptr); |
750b258e PW |
1246 | if (status == NULL) |
1247 | continue; | |
f60db4f0 | 1248 | |
ca3a04f6 | 1249 | tmp = strtok_r (NULL, "\n", &saveptr); |
750b258e PW |
1250 | if (tmp == NULL) |
1251 | continue; | |
1252 | if (sscanf (tmp, "%llx", &address) != 1) | |
1253 | continue; | |
f60db4f0 | 1254 | |
750b258e PW |
1255 | buffer_xml_printf (buffer, |
1256 | "<item>" | |
1257 | "<column name=\"name\">%s</column>" | |
1258 | "<column name=\"size\">%u</column>" | |
1259 | "<column name=\"num uses\">%d</column>" | |
1260 | "<column name=\"dependencies\">%s</column>" | |
1261 | "<column name=\"status\">%s</column>" | |
1262 | "<column name=\"address\">%llx</column>" | |
1263 | "</item>", | |
1264 | name, | |
1265 | size, | |
1266 | uses, | |
1267 | dependencies, | |
1268 | status, | |
1269 | address); | |
85d4a676 | 1270 | } |
85d4a676 | 1271 | } |
750b258e | 1272 | while (!feof (fp.get ())); |
85d4a676 SS |
1273 | } |
1274 | ||
750b258e | 1275 | buffer_grow_str0 (buffer, "</osdata>\n"); |
85d4a676 SS |
1276 | } |
1277 | ||
c8749e58 | 1278 | static void linux_xfer_osdata_info_os_types (struct buffer *buffer); |
750b258e | 1279 | |
d26e3629 | 1280 | struct osdata_type { |
a121b7c1 PA |
1281 | const char *type; |
1282 | const char *title; | |
1283 | const char *description; | |
750b258e PW |
1284 | void (*take_snapshot) (struct buffer *buffer); |
1285 | LONGEST len_avail; | |
1286 | struct buffer buffer; | |
d26e3629 | 1287 | } osdata_table[] = { |
750b258e PW |
1288 | { "types", "Types", "Listing of info os types you can list", |
1289 | linux_xfer_osdata_info_os_types, -1 }, | |
d33279b3 | 1290 | { "cpus", "CPUs", "Listing of all cpus/cores on the system", |
750b258e | 1291 | linux_xfer_osdata_cpus, -1 }, |
d33279b3 | 1292 | { "files", "File descriptors", "Listing of all file descriptors", |
750b258e | 1293 | linux_xfer_osdata_fds, -1 }, |
d33279b3 | 1294 | { "modules", "Kernel modules", "Listing of all loaded kernel modules", |
750b258e | 1295 | linux_xfer_osdata_modules, -1 }, |
d33279b3 | 1296 | { "msg", "Message queues", "Listing of all message queues", |
750b258e | 1297 | linux_xfer_osdata_msg, -1 }, |
71caed83 | 1298 | { "processes", "Processes", "Listing of all processes", |
750b258e | 1299 | linux_xfer_osdata_processes, -1 }, |
71caed83 | 1300 | { "procgroups", "Process groups", "Listing of all process groups", |
750b258e | 1301 | linux_xfer_osdata_processgroups, -1 }, |
71caed83 | 1302 | { "semaphores", "Semaphores", "Listing of all semaphores", |
750b258e | 1303 | linux_xfer_osdata_sem, -1 }, |
d33279b3 | 1304 | { "shm", "Shared-memory regions", "Listing of all shared-memory regions", |
750b258e | 1305 | linux_xfer_osdata_shm, -1 }, |
d33279b3 | 1306 | { "sockets", "Sockets", "Listing of all internet-domain sockets", |
750b258e | 1307 | linux_xfer_osdata_isockets, -1 }, |
d33279b3 | 1308 | { "threads", "Threads", "Listing of all threads", |
750b258e | 1309 | linux_xfer_osdata_threads, -1 }, |
d26e3629 KY |
1310 | { NULL, NULL, NULL } |
1311 | }; | |
1312 | ||
750b258e PW |
1313 | /* Collect data about all types info os can show in BUFFER. */ |
1314 | ||
1315 | static void | |
1316 | linux_xfer_osdata_info_os_types (struct buffer *buffer) | |
d26e3629 | 1317 | { |
750b258e PW |
1318 | buffer_grow_str (buffer, "<osdata type=\"types\">\n"); |
1319 | ||
1320 | /* Start the below loop at 1, as we do not want to list ourselves. */ | |
1321 | for (int i = 1; osdata_table[i].type; ++i) | |
1322 | buffer_xml_printf (buffer, | |
1323 | "<item>" | |
1324 | "<column name=\"Type\">%s</column>" | |
1325 | "<column name=\"Description\">%s</column>" | |
1326 | "<column name=\"Title\">%s</column>" | |
1327 | "</item>", | |
1328 | osdata_table[i].type, | |
1329 | osdata_table[i].description, | |
1330 | osdata_table[i].title); | |
1331 | ||
1332 | buffer_grow_str0 (buffer, "</osdata>\n"); | |
1333 | } | |
d26e3629 | 1334 | |
d26e3629 | 1335 | |
750b258e PW |
1336 | /* Copies up to LEN bytes in READBUF from offset OFFSET in OSD->BUFFER. |
1337 | If OFFSET is zero, first calls OSD->TAKE_SNAPSHOT. */ | |
d26e3629 | 1338 | |
750b258e PW |
1339 | static LONGEST |
1340 | common_getter (struct osdata_type *osd, | |
1341 | gdb_byte *readbuf, ULONGEST offset, ULONGEST len) | |
1342 | { | |
1343 | gdb_assert (readbuf); | |
1344 | ||
1345 | if (offset == 0) | |
1346 | { | |
1347 | if (osd->len_avail != -1 && osd->len_avail != 0) | |
1348 | buffer_free (&osd->buffer); | |
1349 | osd->len_avail = 0; | |
1350 | buffer_init (&osd->buffer); | |
1351 | (osd->take_snapshot) (&osd->buffer); | |
1352 | osd->len_avail = strlen (osd->buffer.buffer); | |
1353 | } | |
1354 | if (offset >= osd->len_avail) | |
1355 | { | |
1356 | /* Done. Get rid of the buffer. */ | |
1357 | buffer_free (&osd->buffer); | |
1358 | osd->len_avail = 0; | |
1359 | return 0; | |
1360 | } | |
1361 | if (len > osd->len_avail - offset) | |
1362 | len = osd->len_avail - offset; | |
1363 | memcpy (readbuf, osd->buffer.buffer + offset, len); | |
d26e3629 | 1364 | |
750b258e PW |
1365 | return len; |
1366 | ||
1367 | } | |
d26e3629 | 1368 | |
750b258e PW |
1369 | LONGEST |
1370 | linux_common_xfer_osdata (const char *annex, gdb_byte *readbuf, | |
1371 | ULONGEST offset, ULONGEST len) | |
1372 | { | |
1373 | if (!annex || *annex == '\0') | |
1374 | { | |
1375 | return common_getter (&osdata_table[0], | |
1376 | readbuf, offset, len); | |
d26e3629 KY |
1377 | } |
1378 | else | |
1379 | { | |
1380 | int i; | |
1381 | ||
1382 | for (i = 0; osdata_table[i].type; ++i) | |
1383 | { | |
1384 | if (strcmp (annex, osdata_table[i].type) == 0) | |
750b258e PW |
1385 | return common_getter (&osdata_table[i], |
1386 | readbuf, offset, len); | |
d26e3629 KY |
1387 | } |
1388 | ||
1389 | return 0; | |
1390 | } | |
1391 | } |