/[gentoo-projects]/pax-utils/pspax.c
Gentoo

Contents of /pax-utils/pspax.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.51 - (show annotations) (download) (as text)
Sun Nov 18 07:39:45 2012 UTC (16 months, 3 weeks ago) by vapier
Branch: MAIN
Changes since 1.50: +1 -4 lines
File MIME type: text/x-csrc
scanelf/pspax: drop PT_LOAD counts since more than "normal" is not a bug and is semi-common with some targets, and the warning has out lived its usefulness -- it was added as an initial sanity check to get a feel for the real world

1 /******************************************************************************/
2 /* THE BEER-WARE LICENSE (Revision 42): */
3 /* As long as you retain this notice you can do whatever you want with this */
4 /* stuff. If we meet some day, and you think this stuff is worth it, */
5 /* you can buy me a beer in return. Ned Ludd. --solarx */
6 /******************************************************************************/
7
8 /*
9 * normal compile.
10 * cc -o pspax pspax.c
11 * or with libcap.
12 * cc -o pspax pspax.c -DWANT_SYSCAP -lcap
13 */
14
15 static const char rcsid[] = "$Id: pspax.c,v 1.50 2011/09/27 19:58:09 vapier Exp $";
16 const char argv0[] = "pspax";
17
18 #include "paxinc.h"
19 #include <grp.h>
20
21 #ifdef WANT_SYSCAP
22 # undef _POSIX_SOURCE
23 # include <sys/capability.h>
24 # define WRAP_SYSCAP(x) x
25 #else
26 # define WRAP_SYSCAP(x)
27 #endif
28
29 #define PROC_DIR "/proc"
30
31 /* variables to control behavior */
32 static char show_all = 0;
33 static char verbose = 0;
34 static char show_banner = 1;
35 static char show_phdr = 0;
36 static char show_addr = 0;
37 static char noexec = 1;
38 static char writeexec = 1;
39 static char wide_output = 0;
40 static pid_t show_pid = 0;
41 static uid_t show_uid = -1;
42 static gid_t show_gid = -1;
43
44 static FILE *proc_fopen(pid_t pid, const char *file)
45 {
46 char path[__PAX_UTILS_PATH_MAX];
47 snprintf(path, sizeof(path), PROC_DIR "/%u/%s", pid, file);
48 path[sizeof(path) - 1] = '\0';
49 return fopen(path, "r");
50 }
51
52 static char *get_proc_name_cmdline(pid_t pid)
53 {
54 FILE *fp;
55 static char str[1024];
56
57 fp = proc_fopen(pid, "cmdline");
58 if (fp == NULL)
59 return NULL;
60
61 if (fscanf(fp, "%s.1023", str) != 1) {
62 fclose(fp);
63 return NULL;
64 }
65 return (str);
66 }
67
68 static char *get_proc_name(pid_t pid)
69 {
70 FILE *fp;
71 static char str[BUFSIZ];
72
73 if (wide_output)
74 return get_proc_name_cmdline(pid);
75
76 fp = proc_fopen(pid, "stat");
77 if (fp == NULL)
78 return NULL;
79
80 if (fscanf(fp, "%*d %s.16", str) != 1) {
81 fclose(fp);
82 return NULL;
83 }
84
85 if (*str) {
86 str[strlen(str) - 1] = '\0';
87 str[16] = 0;
88 }
89 fclose(fp);
90
91 return (str+1);
92 }
93
94 static int get_proc_maps(pid_t pid)
95 {
96 FILE *fp;
97 static char str[BUFSIZ];
98
99 if ((fp = proc_fopen(pid, "maps")) == NULL)
100 return -1;
101
102 while (fgets(str, sizeof(str), fp)) {
103 char *p;
104 if ((p = strchr(str, ' ')) != NULL) {
105 if (strlen(p) < 6)
106 continue;
107 /* 0x0-0x0 rwxp fffff000 00:00 0 */
108 /* 0x0-0x0 R+W+XP fffff000 00:00 0 */
109 ++p; /* ' ' */
110 ++p; /* r */
111 if (*p == '+')
112 ++p;
113 /* FIXME: all of wx, w+, +x, ++ indicate w|x */
114 if (tolower(*p) == 'w') {
115 ++p;
116 if (*p == '+')
117 ++p;
118 if (tolower(*p) == 'x') {
119 fclose(fp);
120 return 1;
121 }
122 }
123 }
124 }
125 fclose(fp);
126
127 return 0;
128 }
129
130 static int print_executable_mappings(pid_t pid)
131 {
132 FILE *fp;
133 static char str[BUFSIZ];
134
135 if ((fp = proc_fopen(pid, "maps")) == NULL)
136 return -1;
137
138 while (fgets(str, sizeof(str), fp)) {
139 char *p;
140 if ((p = strchr(str, ' ')) != NULL) {
141 if (strlen(p) < 6)
142 continue;
143 /* 0x0-0x0 rwxp fffff000 00:00 0 */
144 /* 0x0-0x0 R+W+XP fffff000 00:00 0 */
145 ++p; /* ' ' */
146 ++p; /* r */
147 if (*p == '+')
148 ++p;
149 /* FIXME: all of wx, w+, +x, ++ indicate w|x */
150 if (tolower(*p) == 'w') {
151 ++p;
152 if (*p == '+')
153 ++p;
154 if (tolower(*p) == 'x')
155 printf(" %s", str);
156 }
157 }
158 }
159 fclose(fp);
160
161 return 0;
162 }
163
164 #ifdef __BOUNDS_CHECKING_ON
165 # define NOTE_TO_SELF warn( \
166 "This is bullshit but getpwuid() is leaking memory and I wasted a few hrs 1 day tracking it down in pspax\n" \
167 "Later on I forgot I tracked it down before and saw pspax leaking memory so I tracked it down all over again (silly me)\n" \
168 "Hopefully the getpwuid()/nis/nss/pam or whatever wont suck later on in the future.")
169 #else
170 # define NOTE_TO_SELF
171 #endif
172
173 static struct passwd *get_proc_passwd(pid_t pid)
174 {
175 struct stat st;
176 struct passwd *pwd;
177 char path[__PAX_UTILS_PATH_MAX];
178
179 snprintf(path, sizeof(path), PROC_DIR "/%u/stat", pid);
180
181 if (stat(path, &st) != -1)
182 if ((pwd = getpwuid(st.st_uid)) != NULL)
183 return pwd;
184
185 return NULL;
186 }
187
188 static char *get_proc_status(pid_t pid, const char *name)
189 {
190 FILE *fp;
191 size_t len;
192 static char str[BUFSIZ];
193
194 if ((fp = proc_fopen(pid, "status")) == NULL)
195 return NULL;
196
197 len = strlen(name);
198 while (fgets(str, sizeof(str), fp)) {
199 if (strncasecmp(str, name, len) != 0)
200 continue;
201 if (str[len] == ':') {
202 fclose(fp);
203 str[strlen(str) - 1] = 0;
204 return (str + len + 2);
205 }
206 }
207 fclose(fp);
208
209 return NULL;
210 }
211
212 static char *get_pid_attr(pid_t pid)
213 {
214 FILE *fp;
215 char *p;
216 static char buf[BUFSIZ];
217
218 if ((fp = proc_fopen(pid, "attr/current")) == NULL)
219 return NULL;
220
221 if (fgets(buf, sizeof(buf), fp) != NULL)
222 if ((p = strchr(buf, '\n')) != NULL)
223 *p = 0;
224 fclose(fp);
225
226 return buf;
227 }
228
229 static char *get_pid_addr(pid_t pid)
230 {
231 FILE *fp;
232 char *p;
233 static char buf[BUFSIZ];
234
235 if ((fp = proc_fopen(pid, "ipaddr")) == NULL)
236 return NULL;
237
238 if (fgets(buf, sizeof(buf), fp) != NULL)
239 if ((p = strchr(buf, '\n')) != NULL)
240 *p = 0;
241 fclose(fp);
242
243 return buf;
244 }
245
246 static const char *get_proc_type(pid_t pid)
247 {
248 char fname[32];
249 elfobj *elf;
250 const char *ret;
251
252 snprintf(fname, sizeof(fname), PROC_DIR "/%u/exe", pid);
253 if ((elf = readelf(fname)) == NULL)
254 return NULL;
255 ret = get_elfetype(elf);
256 unreadelf(elf);
257 return ret;
258 }
259
260 static char *scanelf_file_phdr(elfobj *elf)
261 {
262 static char ret[8];
263 unsigned long i, off, multi_stack, multi_load;
264
265 memcpy(ret, "--- ---\0", 8);
266
267 multi_stack = multi_load = 0;
268
269 if (elf->phdr) {
270 uint32_t flags;
271 #define SHOW_PHDR(B) \
272 if (elf->elf_class == ELFCLASS ## B) { \
273 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
274 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
275 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
276 if (EGET(phdr[i].p_type) == PT_GNU_STACK) { \
277 if (multi_stack++) warnf("%s: multiple PT_GNU_STACK's !?", elf->filename); \
278 off = 0; \
279 } else if (EGET(phdr[i].p_type) == PT_LOAD) { \
280 off = 4; \
281 } else \
282 continue; \
283 flags = EGET(phdr[i].p_flags); \
284 memcpy(ret+off, gnu_short_stack_flags(flags), 3); \
285 } \
286 }
287 SHOW_PHDR(32)
288 SHOW_PHDR(64)
289 }
290
291 return ret;
292 }
293 /* we scan the elf file two times when the -e flag is given. But we don't need -e very often so big deal */
294 static const char *get_proc_phdr(pid_t pid)
295 {
296 char fname[32];
297 elfobj *elf;
298 const char *ret;
299
300 snprintf(fname, sizeof(fname), PROC_DIR "/%u/exe", pid);
301 if ((elf = readelf(fname)) == NULL)
302 return NULL;
303 ret = scanelf_file_phdr(elf);
304 unreadelf(elf);
305 return ret;
306 }
307
308 static void pspax(const char *find_name)
309 {
310 register DIR *dir;
311 register struct dirent *de;
312 pid_t pid;
313 pid_t ppid = show_pid;
314 int have_attr, have_addr, wx;
315 struct passwd *pwd;
316 struct stat st;
317 const char *pax, *type, *name, *attr, *addr;
318 char *caps;
319 WRAP_SYSCAP(ssize_t length; cap_t cap_d;)
320
321 WRAP_SYSCAP(cap_d = cap_init());
322
323 dir = opendir(PROC_DIR);
324 if (dir == NULL || chdir(PROC_DIR))
325 errp(PROC_DIR);
326
327 if (access("/proc/self/attr/current", R_OK) != -1)
328 have_attr = 1;
329 else
330 have_attr = 0;
331
332 if ((access("/proc/self/ipaddr", R_OK) != -1) && show_addr)
333 have_addr = 1;
334 else
335 have_addr = 0;
336
337 if (show_banner)
338 printf("%-8s %-6s %-6s %-4s %-10s %-16s %-4s %-4s %s %s\n",
339 "USER", "PID", "PAX", "MAPS", "ETYPE", "NAME", "CAPS", have_attr ? "ATTR" : "",
340 have_addr ? "IPADDR" : "", show_phdr ? "STACK LOAD" : "");
341
342 while ((de = readdir(dir))) {
343 errno = 0;
344 stat(de->d_name, &st);
345 if ((errno != ENOENT) && (errno != EACCES)) {
346 pid = (pid_t) atoi((char *) basename((char *) de->d_name));
347 if (find_name && pid) {
348 char *str = get_proc_name(pid);
349 if (!str)
350 continue;
351 if (strcmp(str, find_name) != 0)
352 pid = 0;
353 }
354 if (((ppid > 0) && (pid != ppid)) || !pid)
355 continue;
356
357 wx = get_proc_maps(pid);
358
359 if (noexec != writeexec) {
360 if ((wx == 1) && (writeexec != wx))
361 goto next_pid;
362
363 if ((wx == 0) && writeexec)
364 goto next_pid;
365 }
366
367 pwd = get_proc_passwd(pid);
368 pax = get_proc_status(pid, "PAX");
369 type = get_proc_type(pid);
370 name = get_proc_name(pid);
371 attr = (have_attr ? get_pid_attr(pid) : NULL);
372 addr = (have_addr ? get_pid_addr(pid) : NULL);
373
374 if (show_uid != -1 && pwd)
375 if (pwd->pw_uid != show_uid)
376 continue;
377
378 if (show_gid != -1 && pwd)
379 if (pwd->pw_gid != show_gid)
380 continue;
381
382 /* this is a non-POSIX function */
383 caps = NULL;
384 WRAP_SYSCAP(capgetp(pid, cap_d));
385 WRAP_SYSCAP(caps = cap_to_text(cap_d, &length));
386
387 if (pwd && strlen(pwd->pw_name) >= 8)
388 pwd->pw_name[8] = 0;
389
390 if (show_all || type) {
391 printf("%-8s %-6d %-6s %-4s %-10s %-16s %-4s %s %s %s\n",
392 pwd ? pwd->pw_name : "--------",
393 pid,
394 pax ? pax : "---",
395 (wx == 1) ? "w|x" : (wx == -1) ? "---" : "w^x",
396 type ? type : "-------",
397 name ? name : "-----",
398 caps ? caps : " = ",
399 attr ? attr : "",
400 addr ? addr : "",
401 show_phdr ? get_proc_phdr(pid) : "");
402 if (verbose && wx)
403 print_executable_mappings(pid);
404 }
405
406 WRAP_SYSCAP(if (caps) cap_free(caps));
407
408 next_pid:
409 continue;
410 }
411 }
412 closedir(dir);
413 }
414
415 /* usage / invocation handling functions */
416 #define PARSE_FLAGS "aeip:u:g:nwWvCBhV"
417 #define a_argument required_argument
418 static struct option const long_opts[] = {
419 {"all", no_argument, NULL, 'a'},
420 {"header", no_argument, NULL, 'e'},
421 {"ipaddr", no_argument, NULL, 'i'},
422 {"pid", a_argument, NULL, 'p'},
423 {"user", a_argument, NULL, 'u'},
424 {"group", a_argument, NULL, 'g'},
425 {"nx", no_argument, NULL, 'n'},
426 {"wx", no_argument, NULL, 'w'},
427 {"wide", no_argument, NULL, 'W'},
428 {"verbose", no_argument, NULL, 'v'},
429 {"nocolor", no_argument, NULL, 'C'},
430 {"nobanner", no_argument, NULL, 'B'},
431 {"help", no_argument, NULL, 'h'},
432 {"version", no_argument, NULL, 'V'},
433 {NULL, no_argument, NULL, 0x0}
434 };
435
436 static const char * const opts_help[] = {
437 "Show all processes",
438 "Print GNU_STACK/PT_LOAD markings",
439 "Print ipaddr info if supported",
440 "Process ID/pid #",
441 "Process user/uid #",
442 "Process group/gid #",
443 "Only display w^x processes",
444 "Only display w|x processes",
445 "Wide output display of cmdline",
446 "Be verbose about executable mappings",
447 "Don't emit color in output",
448 "Don't display the header",
449 "Print this help and exit",
450 "Print version and exit",
451 NULL
452 };
453
454 /* display usage and exit */
455 static void usage(int status)
456 {
457 int i;
458 printf("* List ELF/PaX information about running processes\n\n"
459 "Usage: %s [options]\n\n", argv0);
460 fputs("Options:\n", stdout);
461 for (i = 0; long_opts[i].name; ++i)
462 printf(" -%c, --%-12s* %s\n", long_opts[i].val,
463 long_opts[i].name, opts_help[i]);
464 #ifdef MANLYPAGE
465 for (i = 0; long_opts[i].name; ++i)
466 printf(".TP\n\\fB\\-%c, \\-\\-%s\\fR\n%s\n", long_opts[i].val,
467 long_opts[i].name, opts_help[i]);
468 #endif
469 exit(status);
470 }
471
472 /* parse command line arguments and preform needed actions */
473 static void parseargs(int argc, char *argv[])
474 {
475 int flag;
476 struct passwd *pwd = NULL;
477 struct group *gwd = NULL;
478
479 opterr = 0;
480 while ((flag=getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) {
481 switch (flag) {
482
483 case 'V': /* version info */
484 printf("pax-utils-%s: %s compiled %s\n%s\n"
485 "%s written for Gentoo by <solar and vapier @ gentoo.org>\n",
486 VERSION, __FILE__, __DATE__, rcsid, argv0);
487 exit(EXIT_SUCCESS);
488 break;
489 case 'h': usage(EXIT_SUCCESS); break;
490
491 case 'C': color_init(true); break;
492 case 'B': show_banner = 0; break;
493 case 'a': show_all = 1; break;
494 case 'e': show_phdr = 1; break;
495 case 'i': show_addr = 1; break;
496 case 'p': show_pid = atoi(optarg); break;
497 case 'n': noexec = 1; writeexec = 0; break;
498 case 'w': noexec = 0; writeexec = 1; break;
499 case 'W': wide_output = 1; break;
500 case 'v': verbose++; break;
501 case 'u':
502 show_uid = atoi(optarg);
503 if (show_uid == 0 && (strcmp(optarg, "0") != 0)) {
504 pwd = getpwnam(optarg);
505 if (pwd)
506 show_uid = pwd->pw_uid;
507 else
508 err("unknown uid");
509 }
510 break;
511 case 'g':
512 show_gid = atoi(optarg);
513 if (show_gid == 0 && (strcmp(optarg, "0") != 0)) {
514 gwd = getgrnam(optarg);
515 if (gwd)
516 show_gid = gwd->gr_gid;
517 else
518 err("unknown gid");
519 }
520 break;
521 case ':':
522 case '?':
523 warn("Unknown option or missing parameter");
524 usage(EXIT_FAILURE);
525 break;
526 default:
527 err("Unhandled option '%c'", flag);
528 break;
529 }
530 }
531 }
532
533 int main(int argc, char *argv[])
534 {
535 char *name = NULL;
536
537 color_init(false);
538 parseargs(argc, argv);
539
540 if ((optind < argc) && (show_pid == 0))
541 name = argv[optind];
542
543 pspax(name);
544
545 NOTE_TO_SELF;
546 return EXIT_SUCCESS;
547 }

  ViewVC Help
Powered by ViewVC 1.1.20