2 * pmap.c - print process memory mapping
3 * Copyright 2002 Albert Cahalan
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
30 #include <sys/types.h>
35 #include "fileutils.h"
37 #include "proc/escape.h"
39 #include "proc/readproc.h"
40 #include "proc/version.h"
42 const char *nls_Address,
53 static void nls_initialize(void)
55 setlocale (LC_ALL, "");
56 bindtextdomain(PACKAGE, LOCALEDIR);
59 /* these are the headings shared across all options,
60 though their widths when output might differ */
61 nls_Address = _("Address");
62 nls_Offset = _("Offset");
63 nls_Device = _("Device");
64 nls_Mapping = _("Mapping");
66 /* these headings are used only by the -X/-XX options,
67 and are not derived literally from /proc/#/smaps */
69 nls_Inode = _("Inode");
71 /* these are potentially used for options other than -X/-XX, */
72 nls_Kbytes = _("Kbytes");
75 nls_Dirty = _("Dirty");
78 static int justify_print(const char *str, int width, int right)
83 int len = strlen(str);
84 if (width < len) width = len;
85 printf(right ? "%*.*s " : "%-*.*s ", width, width, str);
90 static int integer_width(unsigned KLONG number)
94 result = !(number > 0);
104 static void __attribute__ ((__noreturn__))
107 fputs(USAGE_HEADER, out);
109 _(" %s [options] PID [PID ...]\n"), program_invocation_short_name);
110 fputs(USAGE_OPTIONS, out);
111 fputs(_(" -x, --extended show details\n"), out);
112 fputs(_(" -X show even more details\n"), out);
113 fputs(_(" WARNING: format changes according to /proc/PID/smaps\n"), out);
114 fputs(_(" -XX show everything the kernel provides\n"), out);
115 fputs(_(" -c, --read-rc read the default rc\n"), out);
116 fputs(_(" -C, --read-rc-from=<file> read the rc from file\n"), out);
117 fputs(_(" -n, --create-rc create new default rc\n"), out);
118 fputs(_(" -N, --create-rc-to=<file> create new rc to file\n"), out);
119 fputs(_(" NOTE: pid arguments are not allowed with -n, -N\n"), out);
120 fputs(_(" -d, --device show the device format\n"), out);
121 fputs(_(" -q, --quiet do not display header and footer\n"), out);
122 fputs(_(" -p, --show-path show path in the mapping\n"), out);
123 fputs(_(" -A, --range=<low>[,<high>] limit results to the given range\n"), out);
124 fputs(USAGE_SEPARATOR, out);
125 fputs(USAGE_HELP, out);
126 fputs(USAGE_VERSION, out);
127 fprintf(out, USAGE_MAN_TAIL("pmap(1)"));
128 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
131 static char mapbuf[1024];
132 static char cmdbuf[512];
134 static unsigned KLONG range_low;
135 static unsigned KLONG range_high = ~0ull;
146 static int map_desc_showpath;
148 static unsigned shm_minor = ~0u;
150 static void discover_shm_minor(void)
156 if (!freopen("/proc/self/maps", "r", stdin))
160 shmid = shmget(IPC_PRIVATE, 42, IPC_CREAT | 0666);
162 /* failed; oh well */
165 addr = shmat(shmid, NULL, SHM_RDONLY);
166 if (addr == (void *)-1)
169 while (fgets(mapbuf_b, sizeof mapbuf_b, stdin)) {
171 /* to clean up unprintables */
173 unsigned KLONG start, end;
174 unsigned long long file_offset, inode;
175 unsigned dev_major, dev_minor;
176 sscanf(mapbuf_b, "%" KLF "x-%" KLF "x %31s %llx %x:%x %llu", &start,
177 &end, perms, &file_offset, &dev_major, &dev_minor,
179 tmp = strchr(mapbuf_b, '\n');
188 if (start > (unsigned long)addr)
194 if (strstr(mapbuf_b, "/SYSV")) {
195 shm_minor = dev_minor;
201 perror(_("shared memory detach"));
204 if (shmctl(shmid, IPC_RMID, NULL) && errno != EINVAL)
205 perror(_("shared memory remove"));
210 static char *mapping_name(proc_t * p, unsigned KLONG addr,
211 unsigned KLONG len, const char *mapbuf_b,
212 unsigned showpath, unsigned dev_major,
213 unsigned dev_minor, unsigned long long inode)
217 if (!dev_major && dev_minor == shm_minor && strstr(mapbuf_b, "/SYSV")) {
218 static char shmbuf[64];
219 snprintf(shmbuf, sizeof shmbuf, " [ shmid=0x%llx ]", inode);
223 cp = strrchr(mapbuf_b, '/');
226 return strchr(mapbuf_b, '/');
227 return cp[1] ? cp + 1 : cp;
230 cp = strchr(mapbuf_b, '/');
234 /* it WILL succeed */
235 return strrchr(cp, '/') + 1;
239 if ((p->start_stack >= addr) && (p->start_stack <= addr + len))
240 cp = _(" [ stack ]");
245 #define DETAIL_LENGTH 32
246 #define DETL "31" /* for format strings */
247 #define NUM_LENGTH 21 /* python says: len(str(2**64)) == 20 */
248 #define NUML "20" /* for format strings */
249 #define VMFLAGS_LENGTH 81 /* There are 27 posible 2 character vmflags as of this patch */
252 char description[DETAIL_LENGTH];
253 char value_str[NUM_LENGTH];
254 unsigned KLONG value;
255 unsigned KLONG total;
257 struct listnode *next;
260 static struct listnode *listhead=NULL, *listtail=NULL, *listnode;
263 struct cnf_listnode {
264 char description[DETAIL_LENGTH];
265 struct cnf_listnode *next;
268 static struct cnf_listnode *cnf_listhead=NULL, *cnf_listnode;
270 static int is_unimportant (const char *s)
272 if (strcmp(s, "AnonHugePages") == 0) return 1;
273 if (strcmp(s, "KernelPageSize") == 0) return 1;
274 if (strcmp(s, "MMUPageSize") == 0) return 1;
275 if (strcmp(s, "Shared_Dirty") == 0) return 1;
276 if (strcmp(s, "Private_Dirty") == 0) return 1;
277 if (strcmp(s, "Shared_Clean") == 0) return 1;
278 if (strcmp(s, "Private_Clean") == 0) return 1;
279 if (strcmp(s, "VmFlags") == 0) return 1;
283 /* check, whether we want to display the field or not */
284 static int is_enabled (const char *s)
286 if (X_option == 1) return !is_unimportant(s);
288 if (c_option) { /* taking the list of disabled fields from the rc file */
290 for (cnf_listnode = cnf_listhead; cnf_listnode; cnf_listnode = cnf_listnode -> next) {
291 if (!strcmp(s, cnf_listnode -> description)) return 1;
301 static void print_extended_maps (FILE *f)
303 char perms[DETAIL_LENGTH], map_desc[128],
304 detail_desc[DETAIL_LENGTH], value_str[NUM_LENGTH],
305 start[NUM_LENGTH], end[NUM_LENGTH],
306 offset[NUM_LENGTH], inode[NUM_LENGTH],
307 dev[64], vmflags[VMFLAGS_LENGTH];
308 int maxw1=0, maxw2=0, maxw3=0, maxw4=0, maxw5=0, maxwv=0;
309 int nfields, firstmapping, footer_gap, i, maxw_;
310 char *ret, *map_basename, c, has_vmflags = 0;
312 ret = fgets(mapbuf, sizeof mapbuf, f);
314 while (ret != NULL) {
315 /* === READ MAPPING === */
317 nfields = sscanf(mapbuf,
318 "%"NUML"[0-9a-f]-%"NUML"[0-9a-f] "
319 "%"DETL"s %"NUML"[0-9a-f] "
320 "%63[0-9a-f:] %"NUML"s %127[^\n]",
321 start, end, perms, offset,
322 dev, inode, map_desc);
323 /* Must read at least up to inode, else something has changed! */
325 xerrx(EXIT_FAILURE, _("Unknown format in smaps file!"));
326 /* If line too long we dump everything else. */
327 c = mapbuf[strlen(mapbuf) - 1];
329 ret = fgets(mapbuf, sizeof mapbuf, f);
330 c = mapbuf[strlen(mapbuf) - 1];
333 /* Store maximum widths for printing nice later */
334 if (strlen(start ) > maxw1) maxw1 = strlen(start);
335 if (strlen(perms ) > maxw2) maxw2 = strlen(perms);
336 if (strlen(offset) > maxw3) maxw3 = strlen(offset);
337 if (strlen(dev ) > maxw4) maxw4 = strlen(dev);
338 if (strlen(inode ) > maxw5) maxw5 = strlen(inode);
340 ret = fgets(mapbuf, sizeof mapbuf, f);
341 nfields = sscanf(mapbuf, "%"DETL"[^:]: %"NUML"[0-9] kB %c",
342 detail_desc, value_str, &c);
344 /* === READ MAPPING DETAILS === */
345 while (ret != NULL && nfields == 2) {
347 if (!is_enabled(detail_desc)) goto loop_end;
349 /* === CREATE LIST AND FILL description FIELD === */
350 if (listnode == NULL) {
351 assert(firstmapping == 2);
352 listnode = calloc(1, sizeof *listnode);
353 if (listhead == NULL) {
354 assert(listtail == NULL);
357 listtail->next = listnode;
360 /* listnode was calloc()ed so all fields are already NULL! */
361 strcpy(listnode->description, detail_desc);
362 if (!q_option) listnode->max_width = strlen(detail_desc);
363 else listnode->max_width = 0;
365 /* === LIST EXISTS === */
366 if (strcmp(listnode->description, detail_desc) != 0)
367 xerrx(EXIT_FAILURE, "ERROR: %s %s",
368 _("inconsistent detail field in smaps file, line:\n"),
371 strcpy(listnode->value_str, value_str);
372 sscanf(value_str, "%"KLF"u", &listnode->value);
373 if (firstmapping == 2) {
374 listnode->total += listnode->value;
376 maxw_ = strlen(listnode->value_str);
377 if (maxw_ > listnode->max_width)
378 listnode->max_width = maxw_;
381 listnode = listnode->next;
383 ret = fgets(mapbuf, sizeof mapbuf, f);
384 nfields = sscanf(mapbuf, "%"DETL"[^:]: %"NUML"[0-9] kB %c",
385 detail_desc, value_str, &c);
388 /* === GET VMFLAGS === */
389 nfields = sscanf(mapbuf, "VmFlags: %[a-z ]", vmflags);
391 if (! has_vmflags) has_vmflags = 1;
392 ret = fgets(mapbuf, sizeof mapbuf, f);
393 if (strlen(vmflags) > maxwv) maxwv = strlen(vmflags);
396 if (firstmapping == 2) { /* width measurement stage, do not print anything yet */
397 if (ret == NULL) { /* once the end of file is reached ...*/
398 firstmapping = 1; /* ... we reset the file position to the beginning of the file */
399 fseek(f, 0, SEEK_SET); /* ... and repeat the process with printing enabled */
400 ret = fgets(mapbuf, sizeof mapbuf, f); /* this is not ideal and needs to be redesigned one day */
403 /* calculate width of totals */
404 for (listnode=listhead; listnode!=NULL; listnode=listnode->next) {
405 maxw_ = integer_width(listnode->total);
406 if (maxw_ > listnode->max_width)
407 listnode->max_width = maxw_;
412 } else { /* the maximum widths have been measured, we've already reached the printing stage */
413 /* === PRINT THIS MAPPING === */
416 if (firstmapping && !q_option) {
418 maxw1 = justify_print(nls_Address, maxw1, 1);
420 if (is_enabled(nls_Perm))
421 maxw2 = justify_print(nls_Perm, maxw2, 1);
423 if (is_enabled(nls_Offset))
424 maxw3 = justify_print(nls_Offset, maxw3, 1);
426 if (is_enabled(nls_Device))
427 maxw4 = justify_print(nls_Device, maxw4, 1);
429 if (is_enabled(nls_Inode))
430 maxw5 = justify_print(nls_Inode, maxw5, 1);
432 for (listnode=listhead; listnode!=NULL; listnode=listnode->next)
433 justify_print(listnode->description, listnode->max_width, 1);
435 if (has_vmflags && is_enabled("VmFlags"))
436 printf(" %*s", maxwv, "VmFlags");
438 if (is_enabled(nls_Mapping))
439 justify_print(nls_Mapping, 0, 0);
445 printf("%*s", maxw1, start); /* Address field is always enabled */
447 if (is_enabled(nls_Perm))
448 printf(" %*s", maxw2, perms);
450 if (is_enabled(nls_Offset))
451 printf(" %*s", maxw3, offset);
453 if (is_enabled(nls_Device))
454 printf(" %*s", maxw4, dev);
456 if (is_enabled(nls_Inode))
457 printf(" %*s", maxw5, inode);
459 for (listnode=listhead; listnode!=NULL; listnode=listnode->next)
460 printf(" %*s", listnode->max_width, listnode->value_str);
462 if (has_vmflags && is_enabled("VmFlags"))
463 printf(" %*s", maxwv, vmflags);
465 if (is_enabled(nls_Mapping)) {
466 if (map_desc_showpath) {
467 printf(" %s", map_desc);
469 map_basename = strrchr(map_desc, '/');
471 printf(" %s", map_desc);
473 printf(" %s", map_basename + 1);
485 /* === PRINT TOTALS === */
486 if (!q_option && listhead!=NULL) { /* footer enabled and non-empty */
488 footer_gap = maxw1 + 1; /* Address field is always enabled */
489 if (is_enabled(nls_Perm )) footer_gap += maxw2 + 1;
490 if (is_enabled(nls_Offset)) footer_gap += maxw3 + 1;
491 if (is_enabled(nls_Device)) footer_gap += maxw4 + 1;
492 if (is_enabled(nls_Inode )) footer_gap += maxw5 + 1;
494 for (i=0; i<footer_gap; i++) putc(' ', stdout);
496 for (listnode=listhead; listnode!=NULL; listnode=listnode->next) {
497 for (i=0; i<listnode->max_width; i++)
504 for (i=0; i<footer_gap; i++) putc(' ', stdout);
506 for (listnode=listhead; listnode!=NULL; listnode=listnode->next)
507 printf("%*lu ", listnode->max_width, listnode->total);
509 fputs("KB \n", stdout);
511 /* We don't free() the list, it's used for all PIDs passed as arguments */
514 static int one_proc(proc_t * p)
518 unsigned long total_shared = 0ul;
519 unsigned long total_private_readonly = 0ul;
520 unsigned long total_private_writeable = 0ul;
521 unsigned KLONG diff = 0;
522 const char *cp2 = NULL;
523 unsigned long long rss = 0ull;
524 unsigned long long private_dirty = 0ull;
525 unsigned long long shared_dirty = 0ull;
526 unsigned long long total_rss = 0ull;
527 unsigned long long total_private_dirty = 0ull;
528 unsigned long long total_shared_dirty = 0ull;
529 int maxw1=0, maxw2=0, maxw3=0, maxw4=0, maxw5=0;
531 /* Overkill, but who knows what is proper? The "w" prog uses
532 * the tty width to determine this.
534 int maxcmd = 0xfffff;
536 escape_command(cmdbuf, p, sizeof cmdbuf, &maxcmd,
537 ESC_ARGS | ESC_BRACKETS);
538 printf("%u: %s\n", p->tgid, cmdbuf);
540 if (x_option || X_option || c_option) {
541 sprintf(buf, "/proc/%u/smaps", p->tgid);
542 if ((fp = fopen(buf, "r")) == NULL)
545 sprintf(buf, "/proc/%u/maps", p->tgid);
546 if ((fp = fopen(buf, "r")) == NULL)
550 if (X_option || c_option) {
551 print_extended_maps(fp);
557 if (sizeof(KLONG) == 4) maxw1 = 8;
558 maxw2 = maxw3 = maxw4 = 7;
561 maxw1 = justify_print(nls_Address, maxw1, 0);
562 maxw2 = justify_print(nls_Kbytes, maxw2, 1);
563 maxw3 = justify_print(nls_RSS, maxw3, 1);
564 maxw4 = justify_print(nls_Dirty, maxw4, 1);
565 maxw5 = justify_print(nls_Mode, maxw5, 0);
566 justify_print(nls_Mapping, 0, 0);
572 if (sizeof(KLONG) == 4) maxw1 = 8;
578 maxw1 = justify_print(nls_Address, maxw1, 0);
579 maxw2 = justify_print(nls_Kbytes, maxw2, 1);
580 maxw3 = justify_print(nls_Mode, maxw3, 0);
581 maxw4 = justify_print(nls_Offset, maxw4, 0);
582 maxw5 = justify_print(nls_Device, maxw5, 0);
583 justify_print(nls_Mapping, 0, 0);
587 while (fgets(mapbuf, sizeof mapbuf, fp)) {
589 /* to clean up unprintables */
591 unsigned KLONG start, end;
592 unsigned long long file_offset, inode;
593 unsigned dev_major, dev_minor;
594 unsigned long long smap_value;
597 /* hex values are lower case or numeric, keys are upper */
598 if (mapbuf[0] >= 'A' && mapbuf[0] <= 'Z') {
601 (mapbuf, "%20[^:]: %llu", smap_key,
603 if (strncmp("Rss", smap_key, 3) == 0) {
605 total_rss += smap_value;
608 if (strncmp("Shared_Dirty", smap_key, 12) == 0) {
609 shared_dirty = smap_value;
610 total_shared_dirty += smap_value;
613 if (strncmp("Private_Dirty", smap_key, 13) == 0) {
614 private_dirty = smap_value;
615 total_private_dirty += smap_value;
618 if (strncmp("Swap", smap_key, 4) == 0) {
619 /*doesn't matter as long as last */
620 printf("%0*" KLF "x %*lu %*llu %*llu %*s %s\n",
622 maxw2, (unsigned long)(diff >> 10),
624 maxw4, (private_dirty + shared_dirty),
627 /* reset some counters */
628 rss = shared_dirty = private_dirty = 0ull;
633 /* Other keys or not a key-value pair */
636 sscanf(mapbuf, "%" KLF "x-%" KLF "x %31s %llx %x:%x %llu", &start,
637 &end, perms, &file_offset, &dev_major, &dev_minor,
640 if (end - 1 < range_low)
642 if (range_high < start)
645 tmp = strchr(mapbuf, '\n');
657 total_shared += diff;
658 if (perms[3] == 'p') {
661 total_private_writeable += diff;
663 total_private_readonly += diff;
665 /* format used by Solaris 9 and procps-3.2.0+ an 'R'
666 * if swap not reserved (MAP_NORESERVE, SysV ISM
674 mapping_name(p, start, diff, mapbuf, map_desc_showpath, dev_major,
676 /* printed with the keys */
681 mapping_name(p, start, diff, mapbuf, map_desc_showpath, dev_major,
683 printf("%0*" KLF "x %*lu %*s %0*llx %*.*s%03x:%05x %s\n",
685 maxw2, (unsigned long)(diff >> 10),
688 (maxw5-9), (maxw5-9), " ", dev_major, dev_minor,
691 if (!x_option && !d_option) {
693 mapping_name(p, start, diff, mapbuf, map_desc_showpath, dev_major,
695 printf((sizeof(KLONG) == 8)
696 ? "%016" KLF "x %6luK %s %s\n"
697 : "%08lx %6luK %s %s\n",
698 start, (unsigned long)(diff >> 10), perms, cp);
705 if (sizeof(KLONG) == 4)
706 justify_print("--------", maxw1, 0);
708 justify_print("----------------", maxw1, 0);
709 justify_print("-------", maxw2, 1);
710 justify_print("-------", maxw3, 1);
711 justify_print("-------", maxw4, 1);
714 printf("%-*s ", maxw1, _("total kB"));
715 printf("%*ld %*llu %*llu\n",
716 maxw2, (total_shared +
717 total_private_writeable +
718 total_private_readonly) >> 10,
720 maxw4, (total_shared_dirty +
721 total_private_dirty));
725 (_("mapped: %ldK writeable/private: %ldK shared: %ldK\n"),
726 (total_shared + total_private_writeable +
727 total_private_readonly) >> 10,
728 total_private_writeable >> 10, total_shared >> 10);
730 if (!x_option && !d_option) {
731 if (sizeof(KLONG) == 8)
732 /* Translation Hint: keep total string length
733 * as 24 characters. Adjust %16 if needed*/
734 printf(_(" total %16ldK\n"),
735 (total_shared + total_private_writeable +
736 total_private_readonly) >> 10);
738 /* Translation Hint: keep total string length
739 * as 16 characters. Adjust %8 if needed*/
740 printf(_(" total %8ldK\n"),
741 (total_shared + total_private_writeable +
742 total_private_readonly) >> 10);
749 static void range_arguments(char *optarg)
754 arg1 = xstrdup(optarg);
755 arg2 = strchr(arg1, ',');
763 range_low = STRTOUKL(arg1, &arg1, 16);
765 range_high = STRTOUKL(arg2, &arg2, 16);
766 if (arg1 && (*arg1 || *arg2))
767 xerrx(EXIT_FAILURE, "%s: '%s'", _("failed to parse argument"),
772 #define MAX_CNF_LINE_LEN 1024
774 #define SECTION_ID_NONE 0
775 #define SECTION_ID_UNSUPPORTED 1
777 #define SECTION_STR_FIELDS_DISPLAY "[Fields Display]"
778 #define SECTION_STR_FIELDS_DISPLAY_LEN (sizeof(SECTION_STR_FIELDS_DISPLAY) - 1)
779 #define SECTION_ID_FIELDS_DISPLAY 2
781 #define SECTION_STR_MAPPING "[Mapping]"
782 #define SECTION_STR_MAPPING_LEN (sizeof(SECTION_STR_MAPPING) - 1)
783 #define SECTION_ID_MAPPING 3
785 static int config_read (char *rc_filename)
788 char line_buf[MAX_CNF_LINE_LEN + 1];
789 char tmp_buf [MAX_CNF_LINE_LEN + 1];
793 int line_cnt, section_id;
795 f = fopen(rc_filename, "r");
797 if (!f) return 0; /* can't open the file for reading */
800 section_id = SECTION_ID_NONE;
802 while (fgets (line_buf, MAX_CNF_LINE_LEN + 1, f)) {
806 /* get rid of the LF char */
807 length = strlen(line_buf);
808 if (length > 0 && line_buf[length - 1] == '\n') {
809 line_buf[length - 1] = '\0';
810 } else if (length == MAX_CNF_LINE_LEN) { /* no LF char -> line too long */
811 xwarnx(_("config line too long - line %d"), line_cnt);
812 /* ignoring the tail */
813 while (fgets (tmp_buf, MAX_CNF_LINE_LEN + 1, f) &&
814 (length = strlen(tmp_buf))>0 &&
815 tmp_buf[length - 1] != '\n') ;
818 /* trim leading whitespaces */
820 while (*trimmed == ' ' || *trimmed == '\t') trimmed++;
822 /* skip comments and empty lines */
823 if (*trimmed == '#' || *trimmed == '\0') continue;
825 if (*trimmed == '[') { /* section */
826 if (!strncmp(trimmed, SECTION_STR_FIELDS_DISPLAY, SECTION_STR_FIELDS_DISPLAY_LEN)) {
827 trimmed += SECTION_STR_FIELDS_DISPLAY_LEN;
828 section_id = SECTION_ID_FIELDS_DISPLAY;
829 } else if (!strncmp(trimmed, SECTION_STR_MAPPING, SECTION_STR_MAPPING_LEN)) {
830 trimmed += SECTION_STR_MAPPING_LEN;
831 section_id = SECTION_ID_MAPPING;
833 while (*trimmed != ']' || *trimmed == '\0') trimmed++;
834 if (*trimmed == ']') {
835 section_id = SECTION_ID_UNSUPPORTED;
836 xwarnx(_("unsupported section found in the config - line %d"), line_cnt);
839 xwarnx(_("syntax error found in the config - line %d"), line_cnt);
843 /* trim trailing whitespaces */
844 while (*trimmed == ' ' || *trimmed == '\t') trimmed++;
846 /* skip comments and empty tails */
847 if (*trimmed == '#' || *trimmed == '\0') continue;
849 /* anything else found on the section line ??? */
850 xwarnx(_("syntax error found in the config - line %d"), line_cnt);
853 switch (section_id) {
854 case SECTION_ID_FIELDS_DISPLAY:
855 token = strtok (trimmed, " \t");
858 tail = strtok (NULL, " \t");
860 if (tail && *tail != '#') {
861 xwarnx(_("syntax error found in the config - line %d"), line_cnt);
864 /* add the field in the list */
865 cnf_listnode = calloc(1, sizeof *cnf_listnode);
866 snprintf(cnf_listnode -> description, sizeof(cnf_listnode -> description), "%s", token);
867 cnf_listnode -> next = cnf_listhead;
868 cnf_listhead = cnf_listnode;
873 case SECTION_ID_MAPPING:
874 token = strtok (trimmed, " \t");
877 tail = strtok (NULL, " \t");
879 if (tail && *tail != '#') {
880 xwarnx(_("syntax error found in the config - line %d"), line_cnt);
883 if (!strcmp(token,"ShowPath")) map_desc_showpath = !map_desc_showpath;
888 case SECTION_ID_UNSUPPORTED:
889 break; /* ignore the content */
892 xwarnx(_("syntax error found in the config - line %d"), line_cnt);
902 static int config_create (char *rc_filename)
906 /* check if rc exists */
907 f = fopen(rc_filename, "r");
909 if (f) { /* file exists ... let user to delete/remove it first */
911 xwarnx(_("the file already exists - delete or rename it first"));
915 /* file doesn't exist */
917 f = fopen(rc_filename, "w");
919 if (!f) return 0; /* can't open the file for writing */
921 /* current rc template, might change in the future */
922 fprintf(f,"# pmap's Config File\n");
924 fprintf(f,"# All the entries are case sensitive.\n");
925 fprintf(f,"# Unsupported entries are ignored!\n");
927 fprintf(f,"[Fields Display]\n");
929 fprintf(f,"# To enable a field uncomment its entry\n");
931 fprintf(f,"#%s\n", nls_Perm);
932 fprintf(f,"#%s\n", nls_Offset);
933 fprintf(f,"#%s\n", nls_Device);
934 fprintf(f,"#%s\n", nls_Inode);
935 fprintf(f,"#Size\n");
938 fprintf(f,"#Shared_Clean\n");
939 fprintf(f,"#Shared_Dirty\n");
940 fprintf(f,"#Private_Clean\n");
941 fprintf(f,"#Private_Dirty\n");
942 fprintf(f,"#Referenced\n");
943 fprintf(f,"#Anonymous\n");
944 fprintf(f,"#AnonHugePages\n");
945 fprintf(f,"#Swap\n");
946 fprintf(f,"#KernelPageSize\n");
947 fprintf(f,"#MMUPageSize\n");
948 fprintf(f,"#Locked\n");
949 fprintf(f,"#VmFlags\n");
950 fprintf(f,"#%s\n", nls_Mapping);
953 fprintf(f,"[Mapping]\n");
955 fprintf(f,"# to show paths in the mapping column uncomment the following line\n");
956 fprintf(f,"#ShowPath\n");
965 /* returns rc filename based on the program_invocation_short_name */
966 static char *get_default_rc_filename(void)
972 homedir = getenv("HOME");
974 xwarnx(_("HOME variable undefined"));
978 rc_filename_len = snprintf(NULL, 0, "%s/.%src", homedir, program_invocation_short_name);
980 rc_filename = (char *) calloc (1, rc_filename_len + 1);
982 xwarnx(_("memory allocation failed"));
986 snprintf(rc_filename, rc_filename_len + 1, "%s/.%src", homedir, program_invocation_short_name);
992 int main(int argc, char **argv)
998 int ret = 0, c, conf_ret;
999 char *rc_filename = NULL;
1001 static const struct option longopts[] = {
1002 {"extended", no_argument, NULL, 'x'},
1003 {"device", no_argument, NULL, 'd'},
1004 {"quiet", no_argument, NULL, 'q'},
1005 {"range", required_argument, NULL, 'A'},
1006 {"help", no_argument, NULL, 'h'},
1007 {"version", no_argument, NULL, 'V'},
1008 {"read-rc", no_argument, NULL, 'c'},
1009 {"read-rc-from", required_argument, NULL, 'C'},
1010 {"create-rc", no_argument, NULL, 'n'},
1011 {"create-rc-to", required_argument, NULL, 'N'},
1012 {"show-path", no_argument, NULL, 'p'},
1016 #ifdef HAVE_PROGRAM_INVOCATION_NAME
1017 program_invocation_name = program_invocation_short_name;
1020 atexit(close_stdout);
1025 while ((c = getopt_long(argc, argv, "xXrdqA:hVcC:nN:p", longopts, NULL)) != -1)
1034 xwarnx(_("option -r is ignored as SunOS compatibility"));
1043 range_arguments(optarg);
1048 printf(PROCPS_NG_VERSION);
1049 return EXIT_SUCCESS;
1055 rc_filename = optarg;
1062 rc_filename = optarg;
1065 map_desc_showpath = 1;
1067 case 'a': /* Sun prints anon/swap reservations */
1068 case 'F': /* Sun forces hostile ptrace-like grab */
1069 case 'l': /* Sun shows unresolved dynamic names */
1070 case 'L': /* Sun shows lgroup info */
1071 case 's': /* Sun shows page sizes */
1072 case 'S': /* Sun shows swap reservations */
1080 if (c_option + C_option + d_option + n_option + N_option + x_option + !!X_option > 1)
1081 xerrx(EXIT_FAILURE, _("options -c, -C, -d, -n, -N, -x, -X are mutually exclusive"));
1083 if ((n_option || N_option) && (q_option || map_desc_showpath))
1084 xerrx(EXIT_FAILURE, _("options -p, -q are mutually exclusive with -n, -N"));
1086 if ((n_option || N_option) && argc > 0)
1087 xerrx(EXIT_FAILURE, _("too many arguments"));
1090 if (config_create(rc_filename)) {
1091 xwarnx(_("rc file successfully created, feel free to edit the content"));
1092 return (EXIT_SUCCESS);
1094 xerrx(EXIT_FAILURE, _("couldn't create the rc file"));
1099 rc_filename = get_default_rc_filename();
1101 if (!rc_filename) return(EXIT_FAILURE);
1103 conf_ret = config_create(rc_filename); free(rc_filename);
1106 xwarnx(_("~/.%src file successfully created, feel free to edit the content"), program_invocation_short_name);
1107 return (EXIT_SUCCESS);
1109 xerrx(EXIT_FAILURE, _("couldn't create ~/.%src"), program_invocation_short_name);
1114 xerrx(EXIT_FAILURE, _("argument missing"));
1116 if (C_option) c_option = 1;
1120 if (!C_option) rc_filename = get_default_rc_filename();
1122 if (!rc_filename) return(EXIT_FAILURE);
1124 conf_ret = config_read(rc_filename);
1128 xerrx(EXIT_FAILURE, _("couldn't read the rc file"));
1130 xwarnx(_("couldn't read ~/.%src"), program_invocation_short_name);
1132 return(EXIT_FAILURE);
1138 pidlist = xmalloc(sizeof(pid_t) * (argc+1));
1141 char *walk = *argv++;
1144 if (!strncmp("/proc/", walk, 6)) {
1146 /* user allowed to do: pmap /proc/PID */
1147 if (*walk < '0' || *walk > '9')
1150 if (*walk < '0' || *walk > '9')
1152 pid = strtoul(walk, &endp, 0);
1153 if (pid < 1ul || pid > 0x7ffffffful || *endp)
1155 pidlist[count++] = pid;
1158 discover_shm_minor();
1160 memset(&p, '\0', sizeof(p));
1161 /* old libproc interface is zero-terminated */
1163 PT = openproc(PROC_FILLSTAT | PROC_FILLARG | PROC_PID, pidlist);
1164 while (readproc(PT, &p)) {
1165 ret |= one_proc(&p);
1171 /* cleaning the list used for the -c/-X/-XX modes */
1172 for (listnode = listhead; listnode != NULL ; ) {
1173 listnode = listnode -> next;
1175 listhead = listnode;
1178 /* cleaning the list used for the -c mode */
1179 for (cnf_listnode = cnf_listhead; cnf_listnode != NULL ; ) {
1180 cnf_listnode = cnf_listnode -> next;
1182 cnf_listhead = cnf_listnode;
1186 /* didn't find all processes asked for */