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