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 | ||
0747795c | 20 | #include "common/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 | ||
0747795c TT |
35 | #include "common/xml-utils.h" |
36 | #include "common/buffer.h" | |
2978b111 | 37 | #include <dirent.h> |
53ce3c39 | 38 | #include <sys/stat.h> |
0747795c | 39 | #include "common/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 | |
750b258e PW |
569 | key = strtok (buf, ":"); |
570 | if (key == NULL) | |
571 | continue; | |
d33279b3 | 572 | |
750b258e PW |
573 | value = strtok (NULL, ":"); |
574 | if (value == NULL) | |
575 | continue; | |
d33279b3 | 576 | |
750b258e PW |
577 | while (key[i] != '\t' && key[i] != '\0') |
578 | i++; | |
d33279b3 | 579 | |
750b258e | 580 | key[i] = '\0'; |
d33279b3 | 581 | |
750b258e PW |
582 | i = 0; |
583 | while (value[i] != '\t' && value[i] != '\0') | |
584 | i++; | |
d33279b3 | 585 | |
750b258e | 586 | value[i] = '\0'; |
d33279b3 | 587 | |
750b258e PW |
588 | if (strcmp (key, "processor") == 0) |
589 | { | |
590 | if (first_item) | |
591 | buffer_grow_str (buffer, "<item>"); | |
592 | else | |
593 | buffer_grow_str (buffer, "</item><item>"); | |
d33279b3 | 594 | |
750b258e | 595 | first_item = 0; |
d33279b3 | 596 | } |
d33279b3 | 597 | |
750b258e PW |
598 | buffer_xml_printf (buffer, |
599 | "<column name=\"%s\">%s</column>", | |
600 | key, | |
601 | value); | |
602 | } | |
d33279b3 | 603 | } |
750b258e | 604 | while (!feof (fp.get ())); |
d33279b3 | 605 | |
750b258e PW |
606 | if (first_item == 0) |
607 | buffer_grow_str (buffer, "</item>"); | |
d33279b3 AT |
608 | } |
609 | ||
750b258e | 610 | buffer_grow_str0 (buffer, "</osdata>\n"); |
d33279b3 AT |
611 | } |
612 | ||
85d4a676 | 613 | /* Collect all the open file descriptors found in /proc and put the details |
750b258e | 614 | found about them into BUFFER. */ |
85d4a676 | 615 | |
750b258e PW |
616 | static void |
617 | linux_xfer_osdata_fds (struct buffer *buffer) | |
85d4a676 | 618 | { |
750b258e | 619 | DIR *dirp; |
85d4a676 | 620 | |
750b258e | 621 | buffer_grow_str (buffer, "<osdata type=\"files\">\n"); |
85d4a676 | 622 | |
750b258e PW |
623 | dirp = opendir ("/proc"); |
624 | if (dirp) | |
625 | { | |
626 | struct dirent *dp; | |
85d4a676 | 627 | |
750b258e | 628 | while ((dp = readdir (dirp)) != NULL) |
85d4a676 | 629 | { |
750b258e PW |
630 | struct stat statbuf; |
631 | char procentry[sizeof ("/proc/4294967295")]; | |
85d4a676 | 632 | |
750b258e PW |
633 | if (!isdigit (dp->d_name[0]) |
634 | || NAMELEN (dp) > sizeof ("4294967295") - 1) | |
635 | continue; | |
85d4a676 | 636 | |
750b258e PW |
637 | xsnprintf (procentry, sizeof (procentry), "/proc/%s", |
638 | dp->d_name); | |
639 | if (stat (procentry, &statbuf) == 0 | |
640 | && S_ISDIR (statbuf.st_mode)) | |
641 | { | |
642 | DIR *dirp2; | |
643 | PID_T pid; | |
644 | char command[32]; | |
85d4a676 | 645 | |
750b258e PW |
646 | pid = atoi (dp->d_name); |
647 | command_from_pid (command, sizeof (command), pid); | |
85d4a676 | 648 | |
750b258e PW |
649 | std::string pathname |
650 | = string_printf ("/proc/%s/fd", dp->d_name); | |
651 | dirp2 = opendir (pathname.c_str ()); | |
85d4a676 | 652 | |
750b258e PW |
653 | if (dirp2) |
654 | { | |
655 | struct dirent *dp2; | |
85d4a676 | 656 | |
750b258e | 657 | while ((dp2 = readdir (dirp2)) != NULL) |
85d4a676 | 658 | { |
750b258e PW |
659 | char buf[1000]; |
660 | ssize_t rslt; | |
661 | ||
662 | if (!isdigit (dp2->d_name[0])) | |
663 | continue; | |
664 | ||
665 | std::string fdname | |
666 | = string_printf ("%s/%s", pathname.c_str (), | |
667 | dp2->d_name); | |
668 | rslt = readlink (fdname.c_str (), buf, | |
669 | sizeof (buf) - 1); | |
670 | if (rslt >= 0) | |
671 | buf[rslt] = '\0'; | |
672 | ||
673 | buffer_xml_printf | |
674 | (buffer, | |
675 | "<item>" | |
676 | "<column name=\"pid\">%s</column>" | |
677 | "<column name=\"command\">%s</column>" | |
678 | "<column name=\"file descriptor\">%s</column>" | |
679 | "<column name=\"name\">%s</column>" | |
680 | "</item>", | |
681 | dp->d_name, | |
682 | command, | |
683 | dp2->d_name, | |
684 | (rslt >= 0 ? buf : dp2->d_name)); | |
85d4a676 | 685 | } |
750b258e PW |
686 | |
687 | closedir (dirp2); | |
85d4a676 SS |
688 | } |
689 | } | |
85d4a676 SS |
690 | } |
691 | ||
750b258e | 692 | closedir (dirp); |
85d4a676 SS |
693 | } |
694 | ||
750b258e | 695 | buffer_grow_str0 (buffer, "</osdata>\n"); |
85d4a676 SS |
696 | } |
697 | ||
698 | /* Returns the socket state STATE in textual form. */ | |
699 | ||
700 | static const char * | |
701 | format_socket_state (unsigned char state) | |
702 | { | |
703 | /* Copied from include/net/tcp_states.h in the Linux kernel sources. */ | |
704 | enum { | |
705 | TCP_ESTABLISHED = 1, | |
706 | TCP_SYN_SENT, | |
707 | TCP_SYN_RECV, | |
708 | TCP_FIN_WAIT1, | |
709 | TCP_FIN_WAIT2, | |
710 | TCP_TIME_WAIT, | |
711 | TCP_CLOSE, | |
712 | TCP_CLOSE_WAIT, | |
713 | TCP_LAST_ACK, | |
714 | TCP_LISTEN, | |
715 | TCP_CLOSING | |
716 | }; | |
717 | ||
718 | switch (state) | |
719 | { | |
720 | case TCP_ESTABLISHED: | |
721 | return "ESTABLISHED"; | |
722 | case TCP_SYN_SENT: | |
723 | return "SYN_SENT"; | |
724 | case TCP_SYN_RECV: | |
725 | return "SYN_RECV"; | |
726 | case TCP_FIN_WAIT1: | |
727 | return "FIN_WAIT1"; | |
728 | case TCP_FIN_WAIT2: | |
729 | return "FIN_WAIT2"; | |
730 | case TCP_TIME_WAIT: | |
731 | return "TIME_WAIT"; | |
732 | case TCP_CLOSE: | |
733 | return "CLOSE"; | |
734 | case TCP_CLOSE_WAIT: | |
735 | return "CLOSE_WAIT"; | |
736 | case TCP_LAST_ACK: | |
737 | return "LAST_ACK"; | |
738 | case TCP_LISTEN: | |
739 | return "LISTEN"; | |
740 | case TCP_CLOSING: | |
741 | return "CLOSING"; | |
742 | default: | |
743 | return "(unknown)"; | |
744 | } | |
745 | } | |
746 | ||
747 | union socket_addr | |
748 | { | |
749 | struct sockaddr sa; | |
750 | struct sockaddr_in sin; | |
751 | struct sockaddr_in6 sin6; | |
752 | }; | |
753 | ||
754 | /* Auxiliary function used by linux_xfer_osdata_isocket. Formats | |
755 | information for all open internet sockets of type FAMILY on the | |
756 | system into BUFFER. If TCP is set, only TCP sockets are processed, | |
757 | otherwise only UDP sockets are processed. */ | |
758 | ||
759 | static void | |
760 | print_sockets (unsigned short family, int tcp, struct buffer *buffer) | |
761 | { | |
762 | const char *proc_file; | |
85d4a676 SS |
763 | |
764 | if (family == AF_INET) | |
765 | proc_file = tcp ? "/proc/net/tcp" : "/proc/net/udp"; | |
766 | else if (family == AF_INET6) | |
767 | proc_file = tcp ? "/proc/net/tcp6" : "/proc/net/udp6"; | |
768 | else | |
769 | return; | |
770 | ||
d419f42d | 771 | gdb_file_up fp = gdb_fopen_cloexec (proc_file, "r"); |
85d4a676 SS |
772 | if (fp) |
773 | { | |
774 | char buf[8192]; | |
775 | ||
776 | do | |
777 | { | |
d419f42d | 778 | if (fgets (buf, sizeof (buf), fp.get ())) |
85d4a676 SS |
779 | { |
780 | uid_t uid; | |
85d4a676 | 781 | unsigned int local_port, remote_port, state; |
85d4a676 | 782 | char local_address[NI_MAXHOST], remote_address[NI_MAXHOST]; |
85d4a676 SS |
783 | int result; |
784 | ||
f60db4f0 GB |
785 | #if NI_MAXHOST <= 32 |
786 | #error "local_address and remote_address buffers too small" | |
787 | #endif | |
788 | ||
85d4a676 | 789 | result = sscanf (buf, |
f60db4f0 | 790 | "%*d: %32[0-9A-F]:%X %32[0-9A-F]:%X %X %*X:%*X %*X:%*X %*X %d %*d %*u %*s\n", |
85d4a676 SS |
791 | local_address, &local_port, |
792 | remote_address, &remote_port, | |
793 | &state, | |
f60db4f0 | 794 | &uid); |
326b0c12 | 795 | |
f60db4f0 | 796 | if (result == 6) |
85d4a676 SS |
797 | { |
798 | union socket_addr locaddr, remaddr; | |
799 | size_t addr_size; | |
800 | char user[UT_NAMESIZE]; | |
801 | char local_service[NI_MAXSERV], remote_service[NI_MAXSERV]; | |
802 | ||
803 | if (family == AF_INET) | |
804 | { | |
805 | sscanf (local_address, "%X", | |
806 | &locaddr.sin.sin_addr.s_addr); | |
807 | sscanf (remote_address, "%X", | |
808 | &remaddr.sin.sin_addr.s_addr); | |
326b0c12 | 809 | |
85d4a676 SS |
810 | locaddr.sin.sin_port = htons (local_port); |
811 | remaddr.sin.sin_port = htons (remote_port); | |
812 | ||
813 | addr_size = sizeof (struct sockaddr_in); | |
814 | } | |
815 | else | |
816 | { | |
817 | sscanf (local_address, "%8X%8X%8X%8X", | |
818 | locaddr.sin6.sin6_addr.s6_addr32, | |
819 | locaddr.sin6.sin6_addr.s6_addr32 + 1, | |
820 | locaddr.sin6.sin6_addr.s6_addr32 + 2, | |
821 | locaddr.sin6.sin6_addr.s6_addr32 + 3); | |
822 | sscanf (remote_address, "%8X%8X%8X%8X", | |
823 | remaddr.sin6.sin6_addr.s6_addr32, | |
824 | remaddr.sin6.sin6_addr.s6_addr32 + 1, | |
825 | remaddr.sin6.sin6_addr.s6_addr32 + 2, | |
826 | remaddr.sin6.sin6_addr.s6_addr32 + 3); | |
827 | ||
828 | locaddr.sin6.sin6_port = htons (local_port); | |
829 | remaddr.sin6.sin6_port = htons (remote_port); | |
326b0c12 | 830 | |
85d4a676 SS |
831 | locaddr.sin6.sin6_flowinfo = 0; |
832 | remaddr.sin6.sin6_flowinfo = 0; | |
833 | locaddr.sin6.sin6_scope_id = 0; | |
834 | remaddr.sin6.sin6_scope_id = 0; | |
835 | ||
836 | addr_size = sizeof (struct sockaddr_in6); | |
837 | } | |
326b0c12 | 838 | |
85d4a676 | 839 | locaddr.sa.sa_family = remaddr.sa.sa_family = family; |
326b0c12 | 840 | |
85d4a676 SS |
841 | result = getnameinfo (&locaddr.sa, addr_size, |
842 | local_address, sizeof (local_address), | |
843 | local_service, sizeof (local_service), | |
844 | NI_NUMERICHOST | NI_NUMERICSERV | |
845 | | (tcp ? 0 : NI_DGRAM)); | |
846 | if (result) | |
847 | continue; | |
326b0c12 | 848 | |
85d4a676 SS |
849 | result = getnameinfo (&remaddr.sa, addr_size, |
850 | remote_address, | |
851 | sizeof (remote_address), | |
852 | remote_service, | |
853 | sizeof (remote_service), | |
854 | NI_NUMERICHOST | NI_NUMERICSERV | |
855 | | (tcp ? 0 : NI_DGRAM)); | |
856 | if (result) | |
857 | continue; | |
326b0c12 | 858 | |
85d4a676 | 859 | user_from_uid (user, sizeof (user), uid); |
326b0c12 | 860 | |
85d4a676 SS |
861 | buffer_xml_printf ( |
862 | buffer, | |
863 | "<item>" | |
864 | "<column name=\"local address\">%s</column>" | |
865 | "<column name=\"local port\">%s</column>" | |
866 | "<column name=\"remote address\">%s</column>" | |
867 | "<column name=\"remote port\">%s</column>" | |
868 | "<column name=\"state\">%s</column>" | |
869 | "<column name=\"user\">%s</column>" | |
326b0c12 | 870 | "<column name=\"family\">%s</column>" |
85d4a676 SS |
871 | "<column name=\"protocol\">%s</column>" |
872 | "</item>", | |
873 | local_address, | |
874 | local_service, | |
875 | remote_address, | |
876 | remote_service, | |
877 | format_socket_state (state), | |
878 | user, | |
879 | (family == AF_INET) ? "INET" : "INET6", | |
880 | tcp ? "STREAM" : "DGRAM"); | |
881 | } | |
882 | } | |
883 | } | |
d419f42d | 884 | while (!feof (fp.get ())); |
85d4a676 SS |
885 | } |
886 | } | |
887 | ||
750b258e | 888 | /* Collect data about internet sockets and write it into BUFFER. */ |
85d4a676 | 889 | |
750b258e PW |
890 | static void |
891 | linux_xfer_osdata_isockets (struct buffer *buffer) | |
85d4a676 | 892 | { |
750b258e | 893 | buffer_grow_str (buffer, "<osdata type=\"I sockets\">\n"); |
85d4a676 | 894 | |
750b258e PW |
895 | print_sockets (AF_INET, 1, buffer); |
896 | print_sockets (AF_INET, 0, buffer); | |
897 | print_sockets (AF_INET6, 1, buffer); | |
898 | print_sockets (AF_INET6, 0, buffer); | |
85d4a676 | 899 | |
750b258e | 900 | buffer_grow_str0 (buffer, "</osdata>\n"); |
85d4a676 SS |
901 | } |
902 | ||
903 | /* Converts the time SECONDS into textual form and copies it into a | |
904 | buffer TIME, with at most MAXLEN characters copied. */ | |
905 | ||
906 | static void | |
907 | time_from_time_t (char *time, int maxlen, TIME_T seconds) | |
908 | { | |
909 | if (!seconds) | |
910 | time[0] = '\0'; | |
911 | else | |
912 | { | |
913 | time_t t = (time_t) seconds; | |
326b0c12 | 914 | |
85d4a676 SS |
915 | strncpy (time, ctime (&t), maxlen); |
916 | time[maxlen - 1] = '\0'; | |
917 | } | |
918 | } | |
919 | ||
920 | /* Finds the group name for the group GID and copies it into GROUP. | |
921 | At most MAXLEN characters are copied. */ | |
922 | ||
923 | static void | |
924 | group_from_gid (char *group, int maxlen, gid_t gid) | |
925 | { | |
926 | struct group *grentry = getgrgid (gid); | |
326b0c12 | 927 | |
85d4a676 SS |
928 | if (grentry) |
929 | { | |
930 | strncpy (group, grentry->gr_name, maxlen); | |
931 | /* Ensure that the group name is null-terminated. */ | |
932 | group[maxlen - 1] = '\0'; | |
933 | } | |
934 | else | |
935 | group[0] = '\0'; | |
936 | } | |
937 | ||
938 | /* Collect data about shared memory recorded in /proc and write it | |
750b258e | 939 | into BUFFER. */ |
85d4a676 | 940 | |
750b258e PW |
941 | static void |
942 | linux_xfer_osdata_shm (struct buffer *buffer) | |
85d4a676 | 943 | { |
750b258e | 944 | buffer_grow_str (buffer, "<osdata type=\"shared memory\">\n"); |
85d4a676 | 945 | |
750b258e PW |
946 | gdb_file_up fp = gdb_fopen_cloexec ("/proc/sysvipc/shm", "r"); |
947 | if (fp) | |
85d4a676 | 948 | { |
750b258e | 949 | char buf[8192]; |
85d4a676 | 950 | |
750b258e PW |
951 | do |
952 | { | |
953 | if (fgets (buf, sizeof (buf), fp.get ())) | |
85d4a676 | 954 | { |
750b258e PW |
955 | key_t key; |
956 | uid_t uid, cuid; | |
957 | gid_t gid, cgid; | |
958 | PID_T cpid, lpid; | |
959 | int shmid, size, nattch; | |
960 | TIME_T atime, dtime, ctime; | |
961 | unsigned int perms; | |
962 | int items_read; | |
963 | ||
964 | items_read = sscanf (buf, | |
965 | "%d %d %o %d %lld %lld %d %u %u %u %u %lld %lld %lld", | |
966 | &key, &shmid, &perms, &size, | |
967 | &cpid, &lpid, | |
968 | &nattch, | |
969 | &uid, &gid, &cuid, &cgid, | |
970 | &atime, &dtime, &ctime); | |
971 | ||
972 | if (items_read == 14) | |
85d4a676 | 973 | { |
750b258e PW |
974 | char user[UT_NAMESIZE], group[UT_NAMESIZE]; |
975 | char cuser[UT_NAMESIZE], cgroup[UT_NAMESIZE]; | |
976 | char ccmd[32], lcmd[32]; | |
977 | char atime_str[32], dtime_str[32], ctime_str[32]; | |
978 | ||
979 | user_from_uid (user, sizeof (user), uid); | |
980 | group_from_gid (group, sizeof (group), gid); | |
981 | user_from_uid (cuser, sizeof (cuser), cuid); | |
982 | group_from_gid (cgroup, sizeof (cgroup), cgid); | |
983 | ||
984 | command_from_pid (ccmd, sizeof (ccmd), cpid); | |
985 | command_from_pid (lcmd, sizeof (lcmd), lpid); | |
986 | ||
987 | time_from_time_t (atime_str, sizeof (atime_str), atime); | |
988 | time_from_time_t (dtime_str, sizeof (dtime_str), dtime); | |
989 | time_from_time_t (ctime_str, sizeof (ctime_str), ctime); | |
990 | ||
991 | buffer_xml_printf | |
992 | (buffer, | |
993 | "<item>" | |
994 | "<column name=\"key\">%d</column>" | |
995 | "<column name=\"shmid\">%d</column>" | |
996 | "<column name=\"permissions\">%o</column>" | |
997 | "<column name=\"size\">%d</column>" | |
998 | "<column name=\"creator command\">%s</column>" | |
999 | "<column name=\"last op. command\">%s</column>" | |
1000 | "<column name=\"num attached\">%d</column>" | |
1001 | "<column name=\"user\">%s</column>" | |
1002 | "<column name=\"group\">%s</column>" | |
1003 | "<column name=\"creator user\">%s</column>" | |
1004 | "<column name=\"creator group\">%s</column>" | |
1005 | "<column name=\"last shmat() time\">%s</column>" | |
1006 | "<column name=\"last shmdt() time\">%s</column>" | |
1007 | "<column name=\"last shmctl() time\">%s</column>" | |
1008 | "</item>", | |
1009 | key, | |
1010 | shmid, | |
1011 | perms, | |
1012 | size, | |
1013 | ccmd, | |
1014 | lcmd, | |
1015 | nattch, | |
1016 | user, | |
1017 | group, | |
1018 | cuser, | |
1019 | cgroup, | |
1020 | atime_str, | |
1021 | dtime_str, | |
1022 | ctime_str); | |
85d4a676 SS |
1023 | } |
1024 | } | |
85d4a676 | 1025 | } |
750b258e | 1026 | while (!feof (fp.get ())); |
85d4a676 SS |
1027 | } |
1028 | ||
750b258e | 1029 | buffer_grow_str0 (buffer, "</osdata>\n"); |
85d4a676 SS |
1030 | } |
1031 | ||
1032 | /* Collect data about semaphores recorded in /proc and write it | |
750b258e | 1033 | into BUFFER. */ |
85d4a676 | 1034 | |
750b258e PW |
1035 | static void |
1036 | linux_xfer_osdata_sem (struct buffer *buffer) | |
85d4a676 | 1037 | { |
750b258e | 1038 | buffer_grow_str (buffer, "<osdata type=\"semaphores\">\n"); |
85d4a676 | 1039 | |
750b258e PW |
1040 | gdb_file_up fp = gdb_fopen_cloexec ("/proc/sysvipc/sem", "r"); |
1041 | if (fp) | |
85d4a676 | 1042 | { |
750b258e | 1043 | char buf[8192]; |
326b0c12 | 1044 | |
750b258e PW |
1045 | do |
1046 | { | |
1047 | if (fgets (buf, sizeof (buf), fp.get ())) | |
85d4a676 | 1048 | { |
750b258e PW |
1049 | key_t key; |
1050 | uid_t uid, cuid; | |
1051 | gid_t gid, cgid; | |
1052 | unsigned int perms, nsems; | |
1053 | int semid; | |
1054 | TIME_T otime, ctime; | |
1055 | int items_read; | |
1056 | ||
1057 | items_read = sscanf (buf, | |
1058 | "%d %d %o %u %d %d %d %d %lld %lld", | |
1059 | &key, &semid, &perms, &nsems, | |
1060 | &uid, &gid, &cuid, &cgid, | |
1061 | &otime, &ctime); | |
1062 | ||
1063 | if (items_read == 10) | |
85d4a676 | 1064 | { |
750b258e PW |
1065 | char user[UT_NAMESIZE], group[UT_NAMESIZE]; |
1066 | char cuser[UT_NAMESIZE], cgroup[UT_NAMESIZE]; | |
1067 | char otime_str[32], ctime_str[32]; | |
1068 | ||
1069 | user_from_uid (user, sizeof (user), uid); | |
1070 | group_from_gid (group, sizeof (group), gid); | |
1071 | user_from_uid (cuser, sizeof (cuser), cuid); | |
1072 | group_from_gid (cgroup, sizeof (cgroup), cgid); | |
1073 | ||
1074 | time_from_time_t (otime_str, sizeof (otime_str), otime); | |
1075 | time_from_time_t (ctime_str, sizeof (ctime_str), ctime); | |
1076 | ||
1077 | buffer_xml_printf | |
1078 | (buffer, | |
1079 | "<item>" | |
1080 | "<column name=\"key\">%d</column>" | |
1081 | "<column name=\"semid\">%d</column>" | |
1082 | "<column name=\"permissions\">%o</column>" | |
1083 | "<column name=\"num semaphores\">%u</column>" | |
1084 | "<column name=\"user\">%s</column>" | |
1085 | "<column name=\"group\">%s</column>" | |
1086 | "<column name=\"creator user\">%s</column>" | |
1087 | "<column name=\"creator group\">%s</column>" | |
1088 | "<column name=\"last semop() time\">%s</column>" | |
1089 | "<column name=\"last semctl() time\">%s</column>" | |
1090 | "</item>", | |
1091 | key, | |
1092 | semid, | |
1093 | perms, | |
1094 | nsems, | |
1095 | user, | |
1096 | group, | |
1097 | cuser, | |
1098 | cgroup, | |
1099 | otime_str, | |
1100 | ctime_str); | |
85d4a676 SS |
1101 | } |
1102 | } | |
85d4a676 | 1103 | } |
750b258e | 1104 | while (!feof (fp.get ())); |
85d4a676 SS |
1105 | } |
1106 | ||
750b258e | 1107 | buffer_grow_str0 (buffer, "</osdata>\n"); |
85d4a676 SS |
1108 | } |
1109 | ||
1110 | /* Collect data about message queues recorded in /proc and write it | |
750b258e | 1111 | into BUFFER. */ |
85d4a676 | 1112 | |
750b258e PW |
1113 | static void |
1114 | linux_xfer_osdata_msg (struct buffer *buffer) | |
85d4a676 | 1115 | { |
750b258e | 1116 | buffer_grow_str (buffer, "<osdata type=\"message queues\">\n"); |
85d4a676 | 1117 | |
750b258e PW |
1118 | gdb_file_up fp = gdb_fopen_cloexec ("/proc/sysvipc/msg", "r"); |
1119 | if (fp) | |
85d4a676 | 1120 | { |
750b258e | 1121 | char buf[8192]; |
326b0c12 | 1122 | |
750b258e PW |
1123 | do |
1124 | { | |
1125 | if (fgets (buf, sizeof (buf), fp.get ())) | |
85d4a676 | 1126 | { |
750b258e PW |
1127 | key_t key; |
1128 | PID_T lspid, lrpid; | |
1129 | uid_t uid, cuid; | |
1130 | gid_t gid, cgid; | |
1131 | unsigned int perms, cbytes, qnum; | |
1132 | int msqid; | |
1133 | TIME_T stime, rtime, ctime; | |
1134 | int items_read; | |
1135 | ||
1136 | items_read = sscanf (buf, | |
1137 | "%d %d %o %u %u %lld %lld %d %d %d %d %lld %lld %lld", | |
1138 | &key, &msqid, &perms, &cbytes, &qnum, | |
1139 | &lspid, &lrpid, &uid, &gid, &cuid, &cgid, | |
1140 | &stime, &rtime, &ctime); | |
1141 | ||
1142 | if (items_read == 14) | |
85d4a676 | 1143 | { |
750b258e PW |
1144 | char user[UT_NAMESIZE], group[UT_NAMESIZE]; |
1145 | char cuser[UT_NAMESIZE], cgroup[UT_NAMESIZE]; | |
1146 | char lscmd[32], lrcmd[32]; | |
1147 | char stime_str[32], rtime_str[32], ctime_str[32]; | |
1148 | ||
1149 | user_from_uid (user, sizeof (user), uid); | |
1150 | group_from_gid (group, sizeof (group), gid); | |
1151 | user_from_uid (cuser, sizeof (cuser), cuid); | |
1152 | group_from_gid (cgroup, sizeof (cgroup), cgid); | |
1153 | ||
1154 | command_from_pid (lscmd, sizeof (lscmd), lspid); | |
1155 | command_from_pid (lrcmd, sizeof (lrcmd), lrpid); | |
1156 | ||
1157 | time_from_time_t (stime_str, sizeof (stime_str), stime); | |
1158 | time_from_time_t (rtime_str, sizeof (rtime_str), rtime); | |
1159 | time_from_time_t (ctime_str, sizeof (ctime_str), ctime); | |
1160 | ||
1161 | buffer_xml_printf | |
1162 | (buffer, | |
1163 | "<item>" | |
1164 | "<column name=\"key\">%d</column>" | |
1165 | "<column name=\"msqid\">%d</column>" | |
1166 | "<column name=\"permissions\">%o</column>" | |
1167 | "<column name=\"num used bytes\">%u</column>" | |
1168 | "<column name=\"num messages\">%u</column>" | |
1169 | "<column name=\"last msgsnd() command\">%s</column>" | |
1170 | "<column name=\"last msgrcv() command\">%s</column>" | |
1171 | "<column name=\"user\">%s</column>" | |
1172 | "<column name=\"group\">%s</column>" | |
1173 | "<column name=\"creator user\">%s</column>" | |
1174 | "<column name=\"creator group\">%s</column>" | |
1175 | "<column name=\"last msgsnd() time\">%s</column>" | |
1176 | "<column name=\"last msgrcv() time\">%s</column>" | |
1177 | "<column name=\"last msgctl() time\">%s</column>" | |
1178 | "</item>", | |
1179 | key, | |
1180 | msqid, | |
1181 | perms, | |
1182 | cbytes, | |
1183 | qnum, | |
1184 | lscmd, | |
1185 | lrcmd, | |
1186 | user, | |
1187 | group, | |
1188 | cuser, | |
1189 | cgroup, | |
1190 | stime_str, | |
1191 | rtime_str, | |
1192 | ctime_str); | |
85d4a676 SS |
1193 | } |
1194 | } | |
85d4a676 | 1195 | } |
750b258e | 1196 | while (!feof (fp.get ())); |
85d4a676 SS |
1197 | } |
1198 | ||
750b258e | 1199 | buffer_grow_str0 (buffer, "</osdata>\n"); |
85d4a676 SS |
1200 | } |
1201 | ||
1202 | /* Collect data about loaded kernel modules and write it into | |
750b258e | 1203 | BUFFER. */ |
85d4a676 | 1204 | |
750b258e PW |
1205 | static void |
1206 | linux_xfer_osdata_modules (struct buffer *buffer) | |
85d4a676 | 1207 | { |
750b258e | 1208 | buffer_grow_str (buffer, "<osdata type=\"modules\">\n"); |
85d4a676 | 1209 | |
750b258e PW |
1210 | gdb_file_up fp = gdb_fopen_cloexec ("/proc/modules", "r"); |
1211 | if (fp) | |
85d4a676 | 1212 | { |
750b258e | 1213 | char buf[8192]; |
326b0c12 | 1214 | |
750b258e PW |
1215 | do |
1216 | { | |
1217 | if (fgets (buf, sizeof (buf), fp.get ())) | |
85d4a676 | 1218 | { |
750b258e PW |
1219 | char *name, *dependencies, *status, *tmp; |
1220 | unsigned int size; | |
1221 | unsigned long long address; | |
1222 | int uses; | |
85d4a676 | 1223 | |
750b258e PW |
1224 | name = strtok (buf, " "); |
1225 | if (name == NULL) | |
1226 | continue; | |
f60db4f0 | 1227 | |
750b258e PW |
1228 | tmp = strtok (NULL, " "); |
1229 | if (tmp == NULL) | |
1230 | continue; | |
1231 | if (sscanf (tmp, "%u", &size) != 1) | |
1232 | continue; | |
f60db4f0 | 1233 | |
750b258e PW |
1234 | tmp = strtok (NULL, " "); |
1235 | if (tmp == NULL) | |
1236 | continue; | |
1237 | if (sscanf (tmp, "%d", &uses) != 1) | |
1238 | continue; | |
f60db4f0 | 1239 | |
750b258e PW |
1240 | dependencies = strtok (NULL, " "); |
1241 | if (dependencies == NULL) | |
1242 | continue; | |
f60db4f0 | 1243 | |
750b258e PW |
1244 | status = strtok (NULL, " "); |
1245 | if (status == NULL) | |
1246 | continue; | |
f60db4f0 | 1247 | |
750b258e PW |
1248 | tmp = strtok (NULL, "\n"); |
1249 | if (tmp == NULL) | |
1250 | continue; | |
1251 | if (sscanf (tmp, "%llx", &address) != 1) | |
1252 | continue; | |
f60db4f0 | 1253 | |
750b258e PW |
1254 | buffer_xml_printf (buffer, |
1255 | "<item>" | |
1256 | "<column name=\"name\">%s</column>" | |
1257 | "<column name=\"size\">%u</column>" | |
1258 | "<column name=\"num uses\">%d</column>" | |
1259 | "<column name=\"dependencies\">%s</column>" | |
1260 | "<column name=\"status\">%s</column>" | |
1261 | "<column name=\"address\">%llx</column>" | |
1262 | "</item>", | |
1263 | name, | |
1264 | size, | |
1265 | uses, | |
1266 | dependencies, | |
1267 | status, | |
1268 | address); | |
85d4a676 | 1269 | } |
85d4a676 | 1270 | } |
750b258e | 1271 | while (!feof (fp.get ())); |
85d4a676 SS |
1272 | } |
1273 | ||
750b258e | 1274 | buffer_grow_str0 (buffer, "</osdata>\n"); |
85d4a676 SS |
1275 | } |
1276 | ||
c8749e58 | 1277 | static void linux_xfer_osdata_info_os_types (struct buffer *buffer); |
750b258e | 1278 | |
d26e3629 | 1279 | struct osdata_type { |
a121b7c1 PA |
1280 | const char *type; |
1281 | const char *title; | |
1282 | const char *description; | |
750b258e PW |
1283 | void (*take_snapshot) (struct buffer *buffer); |
1284 | LONGEST len_avail; | |
1285 | struct buffer buffer; | |
d26e3629 | 1286 | } osdata_table[] = { |
750b258e PW |
1287 | { "types", "Types", "Listing of info os types you can list", |
1288 | linux_xfer_osdata_info_os_types, -1 }, | |
d33279b3 | 1289 | { "cpus", "CPUs", "Listing of all cpus/cores on the system", |
750b258e | 1290 | linux_xfer_osdata_cpus, -1 }, |
d33279b3 | 1291 | { "files", "File descriptors", "Listing of all file descriptors", |
750b258e | 1292 | linux_xfer_osdata_fds, -1 }, |
d33279b3 | 1293 | { "modules", "Kernel modules", "Listing of all loaded kernel modules", |
750b258e | 1294 | linux_xfer_osdata_modules, -1 }, |
d33279b3 | 1295 | { "msg", "Message queues", "Listing of all message queues", |
750b258e | 1296 | linux_xfer_osdata_msg, -1 }, |
71caed83 | 1297 | { "processes", "Processes", "Listing of all processes", |
750b258e | 1298 | linux_xfer_osdata_processes, -1 }, |
71caed83 | 1299 | { "procgroups", "Process groups", "Listing of all process groups", |
750b258e | 1300 | linux_xfer_osdata_processgroups, -1 }, |
71caed83 | 1301 | { "semaphores", "Semaphores", "Listing of all semaphores", |
750b258e | 1302 | linux_xfer_osdata_sem, -1 }, |
d33279b3 | 1303 | { "shm", "Shared-memory regions", "Listing of all shared-memory regions", |
750b258e | 1304 | linux_xfer_osdata_shm, -1 }, |
d33279b3 | 1305 | { "sockets", "Sockets", "Listing of all internet-domain sockets", |
750b258e | 1306 | linux_xfer_osdata_isockets, -1 }, |
d33279b3 | 1307 | { "threads", "Threads", "Listing of all threads", |
750b258e | 1308 | linux_xfer_osdata_threads, -1 }, |
d26e3629 KY |
1309 | { NULL, NULL, NULL } |
1310 | }; | |
1311 | ||
750b258e PW |
1312 | /* Collect data about all types info os can show in BUFFER. */ |
1313 | ||
1314 | static void | |
1315 | linux_xfer_osdata_info_os_types (struct buffer *buffer) | |
d26e3629 | 1316 | { |
750b258e PW |
1317 | buffer_grow_str (buffer, "<osdata type=\"types\">\n"); |
1318 | ||
1319 | /* Start the below loop at 1, as we do not want to list ourselves. */ | |
1320 | for (int i = 1; osdata_table[i].type; ++i) | |
1321 | buffer_xml_printf (buffer, | |
1322 | "<item>" | |
1323 | "<column name=\"Type\">%s</column>" | |
1324 | "<column name=\"Description\">%s</column>" | |
1325 | "<column name=\"Title\">%s</column>" | |
1326 | "</item>", | |
1327 | osdata_table[i].type, | |
1328 | osdata_table[i].description, | |
1329 | osdata_table[i].title); | |
1330 | ||
1331 | buffer_grow_str0 (buffer, "</osdata>\n"); | |
1332 | } | |
d26e3629 | 1333 | |
d26e3629 | 1334 | |
750b258e PW |
1335 | /* Copies up to LEN bytes in READBUF from offset OFFSET in OSD->BUFFER. |
1336 | If OFFSET is zero, first calls OSD->TAKE_SNAPSHOT. */ | |
d26e3629 | 1337 | |
750b258e PW |
1338 | static LONGEST |
1339 | common_getter (struct osdata_type *osd, | |
1340 | gdb_byte *readbuf, ULONGEST offset, ULONGEST len) | |
1341 | { | |
1342 | gdb_assert (readbuf); | |
1343 | ||
1344 | if (offset == 0) | |
1345 | { | |
1346 | if (osd->len_avail != -1 && osd->len_avail != 0) | |
1347 | buffer_free (&osd->buffer); | |
1348 | osd->len_avail = 0; | |
1349 | buffer_init (&osd->buffer); | |
1350 | (osd->take_snapshot) (&osd->buffer); | |
1351 | osd->len_avail = strlen (osd->buffer.buffer); | |
1352 | } | |
1353 | if (offset >= osd->len_avail) | |
1354 | { | |
1355 | /* Done. Get rid of the buffer. */ | |
1356 | buffer_free (&osd->buffer); | |
1357 | osd->len_avail = 0; | |
1358 | return 0; | |
1359 | } | |
1360 | if (len > osd->len_avail - offset) | |
1361 | len = osd->len_avail - offset; | |
1362 | memcpy (readbuf, osd->buffer.buffer + offset, len); | |
d26e3629 | 1363 | |
750b258e PW |
1364 | return len; |
1365 | ||
1366 | } | |
d26e3629 | 1367 | |
750b258e PW |
1368 | LONGEST |
1369 | linux_common_xfer_osdata (const char *annex, gdb_byte *readbuf, | |
1370 | ULONGEST offset, ULONGEST len) | |
1371 | { | |
1372 | if (!annex || *annex == '\0') | |
1373 | { | |
1374 | return common_getter (&osdata_table[0], | |
1375 | readbuf, offset, len); | |
d26e3629 KY |
1376 | } |
1377 | else | |
1378 | { | |
1379 | int i; | |
1380 | ||
1381 | for (i = 0; osdata_table[i].type; ++i) | |
1382 | { | |
1383 | if (strcmp (annex, osdata_table[i].type) == 0) | |
750b258e PW |
1384 | return common_getter (&osdata_table[i], |
1385 | readbuf, offset, len); | |
d26e3629 KY |
1386 | } |
1387 | ||
1388 | return 0; | |
1389 | } | |
1390 | } |