/* * pmap.c - print process memory mapping * Copyright 2002 Albert Cahalan * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "c.h" #include "fileutils.h" #include "nls.h" #include "proc/escape.h" #include "xalloc.h" #include "proc/readproc.h" #include "proc/version.h" const char *nls_Address, *nls_Offset, *nls_Device, *nls_Mapping, *nls_Perm, *nls_Inode, *nls_Kbytes, *nls_Mode, *nls_RSS, *nls_Dirty; static void nls_initialize(void) { setlocale (LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); /* these are the headings shared across all options, though their widths when output might differ */ nls_Address = _("Address"); nls_Offset = _("Offset"); nls_Device = _("Device"); nls_Mapping = _("Mapping"); /* these headings are used only by the -X/-XX options, and are not derived literally from /proc/#/smaps */ nls_Perm = _("Perm"); nls_Inode = _("Inode"); /* these are potentially used for options other than -X/-XX, */ nls_Kbytes = _("Kbytes"); nls_Mode = _("Mode"); nls_RSS = _("RSS"); nls_Dirty = _("Dirty"); } static int justify_print(const char *str, int width, int right) { if (width < 1) puts(str); else { int len = strlen(str); if (width < len) width = len; printf(right ? "%*.*s " : "%-*.*s ", width, width, str); } return width; } static int integer_width(unsigned KLONG number) { int result; result = !(number > 0); while (number) { result++; number /= 10; } return result; } static void __attribute__ ((__noreturn__)) usage(FILE * out) { fputs(USAGE_HEADER, out); fprintf(out, _(" %s [options] PID [PID ...]\n"), program_invocation_short_name); fputs(USAGE_OPTIONS, out); fputs(_(" -x, --extended show details\n"), out); fputs(_(" -X show even more details\n"), out); fputs(_(" WARNING: format changes according to /proc/PID/smaps\n"), out); fputs(_(" -XX show everything the kernel provides\n"), out); fputs(_(" -c, --read-rc read the default rc\n"), out); fputs(_(" -C, --read-rc-from= read the rc from file\n"), out); fputs(_(" -n, --create-rc create new default rc\n"), out); fputs(_(" -N, --create-rc-to= create new rc to file\n"), out); fputs(_(" NOTE: pid arguments are not allowed with -n, -N\n"), out); fputs(_(" -d, --device show the device format\n"), out); fputs(_(" -q, --quiet do not display header and footer\n"), out); fputs(_(" -p, --show-path show path in the mapping\n"), out); fputs(_(" -A, --range=[,] limit results to the given range\n"), out); fputs(USAGE_SEPARATOR, out); fputs(USAGE_HELP, out); fputs(USAGE_VERSION, out); fprintf(out, USAGE_MAN_TAIL("pmap(1)")); exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); } static char mapbuf[1024]; static char cmdbuf[512]; static unsigned KLONG range_low; static unsigned KLONG range_high = ~0ull; static int c_option; static int C_option; static int d_option; static int n_option; static int N_option; static int q_option; static int x_option; static int X_option; static int map_desc_showpath; static unsigned shm_minor = ~0u; static void discover_shm_minor(void) { void *addr; int shmid; char mapbuf_b[256]; if (!freopen("/proc/self/maps", "r", stdin)) return; /* create */ shmid = shmget(IPC_PRIVATE, 42, IPC_CREAT | 0666); if (shmid == -1) /* failed; oh well */ return; /* attach */ addr = shmat(shmid, NULL, SHM_RDONLY); if (addr == (void *)-1) goto out_destroy; while (fgets(mapbuf_b, sizeof mapbuf_b, stdin)) { char perms[32]; /* to clean up unprintables */ char *tmp; unsigned KLONG start, end; unsigned long long file_offset, inode; unsigned dev_major, dev_minor; sscanf(mapbuf_b, "%" KLF "x-%" KLF "x %31s %llx %x:%x %llu", &start, &end, perms, &file_offset, &dev_major, &dev_minor, &inode); tmp = strchr(mapbuf_b, '\n'); if (tmp) *tmp = '\0'; tmp = mapbuf_b; while (*tmp) { if (!isprint(*tmp)) *tmp = '?'; tmp++; } if (start > (unsigned long)addr) continue; if (dev_major) continue; if (perms[3] != 's') continue; if (strstr(mapbuf_b, "/SYSV")) { shm_minor = dev_minor; break; } } if (shmdt(addr)) perror(_("shared memory detach")); out_destroy: if (shmctl(shmid, IPC_RMID, NULL) && errno != EINVAL) perror(_("shared memory remove")); return; } static char *mapping_name(proc_t * p, unsigned KLONG addr, unsigned KLONG len, const char *mapbuf_b, unsigned showpath, unsigned dev_major, unsigned dev_minor, unsigned long long inode) { char *cp; if (!dev_major && dev_minor == shm_minor && strstr(mapbuf_b, "/SYSV")) { static char shmbuf[64]; snprintf(shmbuf, sizeof shmbuf, " [ shmid=0x%llx ]", inode); return shmbuf; } cp = strrchr(mapbuf_b, '/'); if (cp) { if (showpath) return strchr(mapbuf_b, '/'); return cp[1] ? cp + 1 : cp; } cp = strchr(mapbuf_b, '/'); if (cp) { if (showpath) return cp; /* it WILL succeed */ return strrchr(cp, '/') + 1; } cp = _(" [ anon ]"); if ((p->start_stack >= addr) && (p->start_stack <= addr + len)) cp = _(" [ stack ]"); return cp; } #define DETAIL_LENGTH 32 #define DETL "31" /* for format strings */ #define NUM_LENGTH 21 /* python says: len(str(2**64)) == 20 */ #define NUML "20" /* for format strings */ #define VMFLAGS_LENGTH 81 /* There are 27 posible 2 character vmflags as of this patch */ struct listnode { char description[DETAIL_LENGTH]; char value_str[NUM_LENGTH]; unsigned KLONG value; unsigned KLONG total; int max_width; struct listnode *next; }; static struct listnode *listhead=NULL, *listtail=NULL, *listnode; struct cnf_listnode { char description[DETAIL_LENGTH]; struct cnf_listnode *next; }; static struct cnf_listnode *cnf_listhead=NULL, *cnf_listnode; static int is_unimportant (const char *s) { if (strcmp(s, "AnonHugePages") == 0) return 1; if (strcmp(s, "KernelPageSize") == 0) return 1; if (strcmp(s, "MMUPageSize") == 0) return 1; if (strcmp(s, "Shared_Dirty") == 0) return 1; if (strcmp(s, "Private_Dirty") == 0) return 1; if (strcmp(s, "Shared_Clean") == 0) return 1; if (strcmp(s, "Private_Clean") == 0) return 1; if (strcmp(s, "VmFlags") == 0) return 1; return 0; } /* check, whether we want to display the field or not */ static int is_enabled (const char *s) { if (X_option == 1) return !is_unimportant(s); if (c_option) { /* taking the list of disabled fields from the rc file */ for (cnf_listnode = cnf_listhead; cnf_listnode; cnf_listnode = cnf_listnode -> next) { if (!strcmp(s, cnf_listnode -> description)) return 1; } return 0; } return 1; } static void print_extended_maps (FILE *f) { char perms[DETAIL_LENGTH], map_desc[128], detail_desc[DETAIL_LENGTH], value_str[NUM_LENGTH], start[NUM_LENGTH], end[NUM_LENGTH], offset[NUM_LENGTH], inode[NUM_LENGTH], dev[64], vmflags[VMFLAGS_LENGTH]; int maxw1=0, maxw2=0, maxw3=0, maxw4=0, maxw5=0, maxwv=0; int nfields, firstmapping, footer_gap, i, maxw_; char *ret, *map_basename, c, has_vmflags = 0; ret = fgets(mapbuf, sizeof mapbuf, f); firstmapping = 2; while (ret != NULL) { /* === READ MAPPING === */ map_desc[0] = '\0'; nfields = sscanf(mapbuf, "%"NUML"[0-9a-f]-%"NUML"[0-9a-f] " "%"DETL"s %"NUML"[0-9a-f] " "%63[0-9a-f:] %"NUML"s %127[^\n]", start, end, perms, offset, dev, inode, map_desc); /* Must read at least up to inode, else something has changed! */ if (nfields < 6) xerrx(EXIT_FAILURE, _("Unknown format in smaps file!")); /* If line too long we dump everything else. */ c = mapbuf[strlen(mapbuf) - 1]; while (c != '\n') { ret = fgets(mapbuf, sizeof mapbuf, f); c = mapbuf[strlen(mapbuf) - 1]; } /* Store maximum widths for printing nice later */ if (strlen(start ) > maxw1) maxw1 = strlen(start); if (strlen(perms ) > maxw2) maxw2 = strlen(perms); if (strlen(offset) > maxw3) maxw3 = strlen(offset); if (strlen(dev ) > maxw4) maxw4 = strlen(dev); if (strlen(inode ) > maxw5) maxw5 = strlen(inode); ret = fgets(mapbuf, sizeof mapbuf, f); nfields = sscanf(mapbuf, "%"DETL"[^:]: %"NUML"[0-9] kB %c", detail_desc, value_str, &c); listnode = listhead; /* === READ MAPPING DETAILS === */ while (ret != NULL && nfields == 2) { if (!is_enabled(detail_desc)) goto loop_end; /* === CREATE LIST AND FILL description FIELD === */ if (listnode == NULL) { assert(firstmapping == 2); listnode = calloc(1, sizeof *listnode); if (listhead == NULL) { assert(listtail == NULL); listhead = listnode; } else { listtail->next = listnode; } listtail = listnode; /* listnode was calloc()ed so all fields are already NULL! */ strcpy(listnode->description, detail_desc); if (!q_option) listnode->max_width = strlen(detail_desc); else listnode->max_width = 0; } else { /* === LIST EXISTS === */ if (strcmp(listnode->description, detail_desc) != 0) xerrx(EXIT_FAILURE, "ERROR: %s %s", _("inconsistent detail field in smaps file, line:\n"), mapbuf); } strcpy(listnode->value_str, value_str); sscanf(value_str, "%"KLF"u", &listnode->value); if (firstmapping == 2) { listnode->total += listnode->value; if (q_option) { maxw_ = strlen(listnode->value_str); if (maxw_ > listnode->max_width) listnode->max_width = maxw_; } } listnode = listnode->next; loop_end: ret = fgets(mapbuf, sizeof mapbuf, f); nfields = sscanf(mapbuf, "%"DETL"[^:]: %"NUML"[0-9] kB %c", detail_desc, value_str, &c); } /* === GET VMFLAGS === */ nfields = sscanf(mapbuf, "VmFlags: %[a-z ]", vmflags); if (nfields == 1) { if (! has_vmflags) has_vmflags = 1; ret = fgets(mapbuf, sizeof mapbuf, f); if (strlen(vmflags) > maxwv) maxwv = strlen(vmflags); } if (firstmapping == 2) { /* width measurement stage, do not print anything yet */ if (ret == NULL) { /* once the end of file is reached ...*/ firstmapping = 1; /* ... we reset the file position to the beginning of the file */ fseek(f, 0, SEEK_SET); /* ... and repeat the process with printing enabled */ ret = fgets(mapbuf, sizeof mapbuf, f); /* this is not ideal and needs to be redesigned one day */ if (!q_option) { /* calculate width of totals */ for (listnode=listhead; listnode!=NULL; listnode=listnode->next) { maxw_ = integer_width(listnode->total); if (maxw_ > listnode->max_width) listnode->max_width = maxw_; } } } } else { /* the maximum widths have been measured, we've already reached the printing stage */ /* === PRINT THIS MAPPING === */ /* Print header */ if (firstmapping && !q_option) { maxw1 = justify_print(nls_Address, maxw1, 1); if (is_enabled(nls_Perm)) maxw2 = justify_print(nls_Perm, maxw2, 1); if (is_enabled(nls_Offset)) maxw3 = justify_print(nls_Offset, maxw3, 1); if (is_enabled(nls_Device)) maxw4 = justify_print(nls_Device, maxw4, 1); if (is_enabled(nls_Inode)) maxw5 = justify_print(nls_Inode, maxw5, 1); for (listnode=listhead; listnode!=NULL; listnode=listnode->next) justify_print(listnode->description, listnode->max_width, 1); if (has_vmflags && is_enabled("VmFlags")) printf(" %*s", maxwv, "VmFlags"); if (is_enabled(nls_Mapping)) justify_print(nls_Mapping, 0, 0); else printf("\n"); } /* Print data */ printf("%*s", maxw1, start); /* Address field is always enabled */ if (is_enabled(nls_Perm)) printf(" %*s", maxw2, perms); if (is_enabled(nls_Offset)) printf(" %*s", maxw3, offset); if (is_enabled(nls_Device)) printf(" %*s", maxw4, dev); if (is_enabled(nls_Inode)) printf(" %*s", maxw5, inode); for (listnode=listhead; listnode!=NULL; listnode=listnode->next) printf(" %*s", listnode->max_width, listnode->value_str); if (has_vmflags && is_enabled("VmFlags")) printf(" %*s", maxwv, vmflags); if (is_enabled(nls_Mapping)) { if (map_desc_showpath) { printf(" %s", map_desc); } else { map_basename = strrchr(map_desc, '/'); if (!map_basename) { printf(" %s", map_desc); } else { printf(" %s", map_basename + 1); } } } printf("\n"); firstmapping = 0; } } /* === PRINT TOTALS === */ if (!q_option && listhead!=NULL) { /* footer enabled and non-empty */ footer_gap = maxw1 + 1; /* Address field is always enabled */ if (is_enabled(nls_Perm )) footer_gap += maxw2 + 1; if (is_enabled(nls_Offset)) footer_gap += maxw3 + 1; if (is_enabled(nls_Device)) footer_gap += maxw4 + 1; if (is_enabled(nls_Inode )) footer_gap += maxw5 + 1; for (i=0; inext) { for (i=0; imax_width; i++) putc('=', stdout); putc(' ', stdout); } putc('\n', stdout); for (i=0; inext) printf("%*lu ", listnode->max_width, listnode->total); fputs("KB \n", stdout); } /* We don't free() the list, it's used for all PIDs passed as arguments */ } static int one_proc(proc_t * p) { char buf[32]; FILE *fp; unsigned long total_shared = 0ul; unsigned long total_private_readonly = 0ul; unsigned long total_private_writeable = 0ul; unsigned KLONG diff = 0; const char *cp2 = NULL; unsigned long long rss = 0ull; unsigned long long private_dirty = 0ull; unsigned long long shared_dirty = 0ull; unsigned long long total_rss = 0ull; unsigned long long total_private_dirty = 0ull; unsigned long long total_shared_dirty = 0ull; int maxw1=0, maxw2=0, maxw3=0, maxw4=0, maxw5=0; /* Overkill, but who knows what is proper? The "w" prog uses * the tty width to determine this. */ int maxcmd = 0xfffff; escape_command(cmdbuf, p, sizeof cmdbuf, &maxcmd, ESC_ARGS | ESC_BRACKETS); printf("%u: %s\n", p->tgid, cmdbuf); if (x_option || X_option || c_option) { sprintf(buf, "/proc/%u/smaps", p->tgid); if ((fp = fopen(buf, "r")) == NULL) return 1; } else { sprintf(buf, "/proc/%u/maps", p->tgid); if ((fp = fopen(buf, "r")) == NULL) return 1; } if (X_option || c_option) { print_extended_maps(fp); return 0; } if (x_option) { maxw1 = 16; if (sizeof(KLONG) == 4) maxw1 = 8; maxw2 = maxw3 = maxw4 = 7; maxw5 = 5; if (!q_option) { maxw1 = justify_print(nls_Address, maxw1, 0); maxw2 = justify_print(nls_Kbytes, maxw2, 1); maxw3 = justify_print(nls_RSS, maxw3, 1); maxw4 = justify_print(nls_Dirty, maxw4, 1); maxw5 = justify_print(nls_Mode, maxw5, 0); justify_print(nls_Mapping, 0, 0); } } if (d_option) { maxw1 = 16; if (sizeof(KLONG) == 4) maxw1 = 8; maxw2 = 7; maxw3 = 5; maxw4 = 16; maxw5 = 9; if (!q_option) { maxw1 = justify_print(nls_Address, maxw1, 0); maxw2 = justify_print(nls_Kbytes, maxw2, 1); maxw3 = justify_print(nls_Mode, maxw3, 0); maxw4 = justify_print(nls_Offset, maxw4, 0); maxw5 = justify_print(nls_Device, maxw5, 0); justify_print(nls_Mapping, 0, 0); } } while (fgets(mapbuf, sizeof mapbuf, fp)) { char perms[32]; /* to clean up unprintables */ char *tmp; unsigned KLONG start, end; unsigned long long file_offset, inode; unsigned dev_major, dev_minor; unsigned long long smap_value; char smap_key[21]; /* hex values are lower case or numeric, keys are upper */ if (mapbuf[0] >= 'A' && mapbuf[0] <= 'Z') { /* Its a key */ if (sscanf(mapbuf, "%20[^:]: %llu", smap_key, &smap_value) == 2) { if (strcmp("Rss", smap_key) == 0) { rss = smap_value; total_rss += smap_value; continue; } if (strcmp("Shared_Dirty", smap_key) == 0) { shared_dirty = smap_value; total_shared_dirty += smap_value; continue; } if (strcmp("Private_Dirty", smap_key) == 0) { private_dirty = smap_value; total_private_dirty += smap_value; continue; } if (strcmp("Swap", smap_key) == 0) { /*doesn't matter as long as last */ printf("%0*" KLF "x %*lu %*llu %*llu %*s %s\n", maxw1, start, maxw2, (unsigned long)(diff >> 10), maxw3, rss, maxw4, (private_dirty + shared_dirty), maxw5, perms, cp2); /* reset some counters */ rss = shared_dirty = private_dirty = 0ull; diff = 0; continue; } } /* Other keys or not a key-value pair */ continue; } sscanf(mapbuf, "%" KLF "x-%" KLF "x %31s %llx %x:%x %llu", &start, &end, perms, &file_offset, &dev_major, &dev_minor, &inode); if (end - 1 < range_low) continue; if (range_high < start) break; tmp = strchr(mapbuf, '\n'); if (tmp) *tmp = '\0'; tmp = mapbuf; while (*tmp) { if (!isprint(*tmp)) *tmp = '?'; tmp++; } diff = end - start; if (perms[3] == 's') total_shared += diff; if (perms[3] == 'p') { perms[3] = '-'; if (perms[1] == 'w') total_private_writeable += diff; else total_private_readonly += diff; } /* format used by Solaris 9 and procps-3.2.0+ an 'R' * if swap not reserved (MAP_NORESERVE, SysV ISM * shared mem, etc.) */ perms[4] = '-'; perms[5] = '\0'; if (x_option) { cp2 = mapping_name(p, start, diff, mapbuf, map_desc_showpath, dev_major, dev_minor, inode); /* printed with the keys */ continue; } if (d_option) { const char *cp = mapping_name(p, start, diff, mapbuf, map_desc_showpath, dev_major, dev_minor, inode); printf("%0*" KLF "x %*lu %*s %0*llx %*.*s%03x:%05x %s\n", maxw1, start, maxw2, (unsigned long)(diff >> 10), maxw3, perms, maxw4, file_offset, (maxw5-9), (maxw5-9), " ", dev_major, dev_minor, cp); } if (!x_option && !d_option) { const char *cp = mapping_name(p, start, diff, mapbuf, map_desc_showpath, dev_major, dev_minor, inode); printf((sizeof(KLONG) == 8) ? "%016" KLF "x %6luK %s %s\n" : "%08lx %6luK %s %s\n", start, (unsigned long)(diff >> 10), perms, cp); } } fclose(fp); if (!q_option) { if (x_option) { if (sizeof(KLONG) == 4) justify_print("--------", maxw1, 0); else justify_print("----------------", maxw1, 0); justify_print("-------", maxw2, 1); justify_print("-------", maxw3, 1); justify_print("-------", maxw4, 1); printf("\n"); printf("%-*s ", maxw1, _("total kB")); printf("%*ld %*llu %*llu\n", maxw2, (total_shared + total_private_writeable + total_private_readonly) >> 10, maxw3, total_rss, maxw4, (total_shared_dirty + total_private_dirty)); } if (d_option) { printf (_("mapped: %ldK writeable/private: %ldK shared: %ldK\n"), (total_shared + total_private_writeable + total_private_readonly) >> 10, total_private_writeable >> 10, total_shared >> 10); } if (!x_option && !d_option) { if (sizeof(KLONG) == 8) /* Translation Hint: keep total string length * as 24 characters. Adjust %16 if needed*/ printf(_(" total %16ldK\n"), (total_shared + total_private_writeable + total_private_readonly) >> 10); else /* Translation Hint: keep total string length * as 16 characters. Adjust %8 if needed*/ printf(_(" total %8ldK\n"), (total_shared + total_private_writeable + total_private_readonly) >> 10); } } return 0; } static void range_arguments(const char *optarg) { char *arg1; char *arg2; char *const copy = xstrdup(optarg); if (!copy) goto fail; arg1 = copy; arg2 = strchr(arg1, ','); if (arg2) *arg2++ = '\0'; else arg2 = arg1; if (*arg1) range_low = STRTOUKL(arg1, &arg1, 16); if (*arg2) range_high = STRTOUKL(arg2, &arg2, 16); if (*arg1 || *arg2) goto fail; free(copy); return; fail: xerrx(EXIT_FAILURE, "%s: '%s'", _("failed to parse argument"), optarg); } #define MAX_CNF_LINE_LEN 1024 #define SECTION_ID_NONE 0 #define SECTION_ID_UNSUPPORTED 1 #define SECTION_STR_FIELDS_DISPLAY "[Fields Display]" #define SECTION_STR_FIELDS_DISPLAY_LEN (sizeof(SECTION_STR_FIELDS_DISPLAY) - 1) #define SECTION_ID_FIELDS_DISPLAY 2 #define SECTION_STR_MAPPING "[Mapping]" #define SECTION_STR_MAPPING_LEN (sizeof(SECTION_STR_MAPPING) - 1) #define SECTION_ID_MAPPING 3 static int config_read (char *rc_filename) { FILE *f; char line_buf[MAX_CNF_LINE_LEN + 1]; char tmp_buf [MAX_CNF_LINE_LEN + 1]; char *trimmed; int length; char *tail, *token; int line_cnt, section_id; f = fopen(rc_filename, "r"); if (!f) return 0; /* can't open the file for reading */ line_cnt = 0; section_id = SECTION_ID_NONE; while (fgets (line_buf, MAX_CNF_LINE_LEN + 1, f)) { line_cnt++; /* get rid of the LF char */ length = strlen(line_buf); if (length > 0 && line_buf[length - 1] == '\n') { line_buf[length - 1] = '\0'; } else if (length == MAX_CNF_LINE_LEN) { /* no LF char -> line too long */ xwarnx(_("config line too long - line %d"), line_cnt); /* ignoring the tail */ while (fgets (tmp_buf, MAX_CNF_LINE_LEN + 1, f) && (length = strlen(tmp_buf))>0 && tmp_buf[length - 1] != '\n') ; } /* trim leading whitespaces */ trimmed = line_buf; while (*trimmed == ' ' || *trimmed == '\t') trimmed++; /* skip comments and empty lines */ if (*trimmed == '#' || *trimmed == '\0') continue; if (*trimmed == '[') { /* section */ if (!strncmp(trimmed, SECTION_STR_FIELDS_DISPLAY, SECTION_STR_FIELDS_DISPLAY_LEN)) { trimmed += SECTION_STR_FIELDS_DISPLAY_LEN; section_id = SECTION_ID_FIELDS_DISPLAY; } else if (!strncmp(trimmed, SECTION_STR_MAPPING, SECTION_STR_MAPPING_LEN)) { trimmed += SECTION_STR_MAPPING_LEN; section_id = SECTION_ID_MAPPING; } else { while (*trimmed != ']' || *trimmed == '\0') trimmed++; if (*trimmed == ']') { section_id = SECTION_ID_UNSUPPORTED; xwarnx(_("unsupported section found in the config - line %d"), line_cnt); trimmed++; } else { xwarnx(_("syntax error found in the config - line %d"), line_cnt); } } /* trim trailing whitespaces */ while (*trimmed == ' ' || *trimmed == '\t') trimmed++; /* skip comments and empty tails */ if (*trimmed == '#' || *trimmed == '\0') continue; /* anything else found on the section line ??? */ xwarnx(_("syntax error found in the config - line %d"), line_cnt); } switch (section_id) { case SECTION_ID_FIELDS_DISPLAY: token = strtok (trimmed, " \t"); if (token) { tail = strtok (NULL, " \t"); if (tail && *tail != '#') { xwarnx(_("syntax error found in the config - line %d"), line_cnt); } /* add the field in the list */ cnf_listnode = calloc(1, sizeof *cnf_listnode); snprintf(cnf_listnode -> description, sizeof(cnf_listnode -> description), "%s", token); cnf_listnode -> next = cnf_listhead; cnf_listhead = cnf_listnode; } break; case SECTION_ID_MAPPING: token = strtok (trimmed, " \t"); if (token) { tail = strtok (NULL, " \t"); if (tail && *tail != '#') { xwarnx(_("syntax error found in the config - line %d"), line_cnt); } if (!strcmp(token,"ShowPath")) map_desc_showpath = !map_desc_showpath; } break; case SECTION_ID_UNSUPPORTED: break; /* ignore the content */ default: xwarnx(_("syntax error found in the config - line %d"), line_cnt); } } fclose(f); return 1; } static int config_create (char *rc_filename) { FILE *f; /* check if rc exists */ f = fopen(rc_filename, "r"); if (f) { /* file exists ... let user to delete/remove it first */ fclose(f); xwarnx(_("the file already exists - delete or rename it first")); return 0; } /* file doesn't exist */ f = fopen(rc_filename, "w"); if (!f) return 0; /* can't open the file for writing */ /* current rc template, might change in the future */ fprintf(f,"# pmap's Config File\n"); fprintf(f,"\n"); fprintf(f,"# All the entries are case sensitive.\n"); fprintf(f,"# Unsupported entries are ignored!\n"); fprintf(f,"\n"); fprintf(f,"[Fields Display]\n"); fprintf(f,"\n"); fprintf(f,"# To enable a field uncomment its entry\n"); fprintf(f,"\n"); fprintf(f,"#%s\n", nls_Perm); fprintf(f,"#%s\n", nls_Offset); fprintf(f,"#%s\n", nls_Device); fprintf(f,"#%s\n", nls_Inode); fprintf(f,"#Size\n"); fprintf(f,"#Rss\n"); fprintf(f,"#Pss\n"); fprintf(f,"#Shared_Clean\n"); fprintf(f,"#Shared_Dirty\n"); fprintf(f,"#Private_Clean\n"); fprintf(f,"#Private_Dirty\n"); fprintf(f,"#Referenced\n"); fprintf(f,"#Anonymous\n"); fprintf(f,"#AnonHugePages\n"); fprintf(f,"#Swap\n"); fprintf(f,"#KernelPageSize\n"); fprintf(f,"#MMUPageSize\n"); fprintf(f,"#Locked\n"); fprintf(f,"#VmFlags\n"); fprintf(f,"#%s\n", nls_Mapping); fprintf(f,"\n"); fprintf(f,"\n"); fprintf(f,"[Mapping]\n"); fprintf(f,"\n"); fprintf(f,"# to show paths in the mapping column uncomment the following line\n"); fprintf(f,"#ShowPath\n"); fprintf(f,"\n"); fclose(f); return 1; } /* returns rc filename based on the program_invocation_short_name */ static char *get_default_rc_filename(void) { char *rc_filename; int rc_filename_len; const char *homedir; homedir = getenv("HOME"); if (!homedir) { xwarnx(_("HOME variable undefined")); return NULL; } rc_filename_len = snprintf(NULL, 0, "%s/.%src", homedir, program_invocation_short_name); rc_filename = (char *) calloc (1, rc_filename_len + 1); if (!rc_filename) { xwarnx(_("memory allocation failed")); return NULL; } snprintf(rc_filename, rc_filename_len + 1, "%s/.%src", homedir, program_invocation_short_name); return rc_filename; } int main(int argc, char **argv) { pid_t *pidlist; unsigned count = 0; PROCTAB *PT; proc_t p; int ret = 0, c, conf_ret; char *rc_filename = NULL; static const struct option longopts[] = { {"extended", no_argument, NULL, 'x'}, {"device", no_argument, NULL, 'd'}, {"quiet", no_argument, NULL, 'q'}, {"range", required_argument, NULL, 'A'}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, {"read-rc", no_argument, NULL, 'c'}, {"read-rc-from", required_argument, NULL, 'C'}, {"create-rc", no_argument, NULL, 'n'}, {"create-rc-to", required_argument, NULL, 'N'}, {"show-path", no_argument, NULL, 'p'}, {NULL, 0, NULL, 0} }; #ifdef HAVE_PROGRAM_INVOCATION_NAME program_invocation_name = program_invocation_short_name; #endif nls_initialize(); atexit(close_stdout); if (argc < 2) usage(stderr); while ((c = getopt_long(argc, argv, "xXrdqA:hVcC:nN:p", longopts, NULL)) != -1) switch (c) { case 'x': x_option = 1; break; case 'X': X_option++; break; case 'r': xwarnx(_("option -r is ignored as SunOS compatibility")); break; case 'd': d_option = 1; break; case 'q': q_option = 1; break; case 'A': range_arguments(optarg); break; case 'h': usage(stdout); case 'V': printf(PROCPS_NG_VERSION); return EXIT_SUCCESS; case 'c': c_option = 1; break; case 'C': C_option = 1; rc_filename = optarg; break; case 'n': n_option = 1; break; case 'N': N_option = 1; rc_filename = optarg; break; case 'p': map_desc_showpath = 1; break; case 'a': /* Sun prints anon/swap reservations */ case 'F': /* Sun forces hostile ptrace-like grab */ case 'l': /* Sun shows unresolved dynamic names */ case 'L': /* Sun shows lgroup info */ case 's': /* Sun shows page sizes */ case 'S': /* Sun shows swap reservations */ default: usage(stderr); } argc -= optind; argv += optind; if (c_option + C_option + d_option + n_option + N_option + x_option + !!X_option > 1) xerrx(EXIT_FAILURE, _("options -c, -C, -d, -n, -N, -x, -X are mutually exclusive")); if ((n_option || N_option) && (q_option || map_desc_showpath)) xerrx(EXIT_FAILURE, _("options -p, -q are mutually exclusive with -n, -N")); if ((n_option || N_option) && argc > 0) xerrx(EXIT_FAILURE, _("too many arguments")); if (N_option) { if (config_create(rc_filename)) { xwarnx(_("rc file successfully created, feel free to edit the content")); return (EXIT_SUCCESS); } else { xerrx(EXIT_FAILURE, _("couldn't create the rc file")); } } if (n_option) { rc_filename = get_default_rc_filename(); if (!rc_filename) return(EXIT_FAILURE); conf_ret = config_create(rc_filename); free(rc_filename); if (conf_ret) { xwarnx(_("~/.%src file successfully created, feel free to edit the content"), program_invocation_short_name); return (EXIT_SUCCESS); } else { xerrx(EXIT_FAILURE, _("couldn't create ~/.%src"), program_invocation_short_name); } } if (argc < 1) xerrx(EXIT_FAILURE, _("argument missing")); if (C_option) c_option = 1; if (c_option) { if (!C_option) rc_filename = get_default_rc_filename(); if (!rc_filename) return(EXIT_FAILURE); conf_ret = config_read(rc_filename); if (!conf_ret) { if (C_option) { xerrx(EXIT_FAILURE, _("couldn't read the rc file")); } else { xwarnx(_("couldn't read ~/.%src"), program_invocation_short_name); free(rc_filename); return(EXIT_FAILURE); } } } pidlist = xmalloc(sizeof(pid_t) * (argc+1)); while (*argv) { char *walk = *argv++; char *endp; unsigned long pid; if (!strncmp("/proc/", walk, 6)) { walk += 6; /* user allowed to do: pmap /proc/PID */ if (*walk < '0' || *walk > '9') continue; } if (*walk < '0' || *walk > '9') usage(stderr); pid = strtoul(walk, &endp, 0); if (pid < 1ul || pid > 0x7ffffffful || *endp) usage(stderr); pidlist[count++] = pid; } discover_shm_minor(); memset(&p, '\0', sizeof(p)); /* old libproc interface is zero-terminated */ pidlist[count] = 0; PT = openproc(PROC_FILLSTAT | PROC_FILLARG | PROC_PID, pidlist); while (readproc(PT, &p)) { ret |= one_proc(&p); count--; } closeproc(PT); free(pidlist); /* cleaning the list used for the -c/-X/-XX modes */ for (listnode = listhead; listnode != NULL ; ) { listnode = listnode -> next; free(listhead); listhead = listnode; } /* cleaning the list used for the -c mode */ for (cnf_listnode = cnf_listhead; cnf_listnode != NULL ; ) { cnf_listnode = cnf_listnode -> next; free(cnf_listhead); cnf_listhead = cnf_listnode; } if (count) /* didn't find all processes asked for */ ret |= 42; return ret; }