procps/src/pmap.c
Craig Small 8fcfb01a81 misc: Update the copyrights of files
The copyrights of the source files were all out of date and were not
the same format. This has been corrected. The source of the authors
was examining the git log for each file.

Signed-off-by: Craig Small <csmall@dropbear.xyz>
2023-02-07 21:09:48 +11:00

1212 lines
32 KiB
C

/*
* pmap.c - print process memory mapping
*
* Copyright © 2010-2023 Craig Small <csmall@dropbear.xyz>
* Copyright © 2011-2023 Jim Warner <james.warner@comcast.net>
* Copyright © 2011-2012 Sami Kerola <kerolasa@iki.fi>
* Copyright © 2002-2009 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 <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <assert.h>
#include <limits.h>
#include "c.h"
#include "fileutils.h"
#include "nls.h"
#include "xalloc.h"
#include "pids.h"
static struct pids_info *Pids_info;
enum pids_item Pid_items[] = {
PIDS_ID_PID, PIDS_ID_TGID,
PIDS_CMDLINE, PIDS_ADDR_STACK_START };
enum rel_items { pid, tgid, cmdline, start_stack };
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 long 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=<file> read the rc from file\n"), out);
fputs(_(" -n, --create-rc create new default rc\n"), out);
fputs(_(" -N, --create-rc-to=<file> 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=<low>[,<high>] 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 unsigned long range_low;
static unsigned long range_high = ~0ul;
static int c_option = 0;
static int C_option = 0;
static int d_option = 0;
static int n_option = 0;
static int N_option = 0;
static int q_option = 0;
static int x_option = 0;
static int X_option = 0;
static int map_desc_showpath;
static unsigned shm_minor = ~0u;
static void discover_shm_minor(void)
{
void *addr;
int shmid;
char mapbuf_b[256];
FILE *fp;
if ( (fp = fopen("/proc/self/maps", "r")) == NULL)
return;
/* create */
shmid = shmget(IPC_PRIVATE, 42, IPC_CREAT | 0666);
if (shmid == -1)
{
/* failed; oh well */
fclose(fp);
return;
}
/* attach */
addr = shmat(shmid, NULL, SHM_RDONLY);
if (addr == (void *)-1)
goto out_destroy;
while (fgets(mapbuf_b, sizeof mapbuf_b, fp)) {
char perms[32];
/* to clean up unprintables */
char *tmp;
unsigned long start, end;
unsigned long long file_offset, inode;
unsigned dev_major, dev_minor;
if (sscanf(mapbuf_b, "%lx-%lx %31s %llx %x:%x %llu", &start,
&end, perms, &file_offset, &dev_major, &dev_minor, &inode) < 6)
continue;
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:
fclose(fp);
if (shmctl(shmid, IPC_RMID, NULL) && errno != EINVAL)
perror(_("shared memory remove"));
return;
}
static const char *mapping_name(struct pids_stack *p, unsigned long addr,
unsigned long len, const char *mapbuf_b,
unsigned showpath, unsigned dev_major,
unsigned dev_minor, unsigned long long inode)
{
const 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 = _(" [ anon ]");
if (PIDS_VAL(start_stack, ul_int, p, Pids_info) >= addr
&& (PIDS_VAL(start_stack, ul_int, p, Pids_info) <= 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 128 /* 30 2-char space-separated flags == 90+1, but be safe */
#define VMFL "127" /* for format strings */
struct listnode {
char description[DETAIL_LENGTH];
char value_str[NUM_LENGTH];
unsigned long value;
unsigned long 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') {
fgets(mapbuf, sizeof mapbuf, f);
if (!ret || !mapbuf[0])
xerrx(EXIT_FAILURE, _("Unknown format in smaps file!"));
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 = ret ? sscanf(mapbuf, "%"DETL"[^:]: %"NUML"[0-9] kB %c",
detail_desc, value_str, &c) : 0;
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 (listnode == NULL)
xerrx(EXIT_FAILURE, _("ERROR: memory allocation failed"));
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, "%lu", &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 = ret ? sscanf(mapbuf, "%"DETL"[^:]: %"NUML"[0-9] kB %c",
detail_desc, value_str, &c) : 0;
}
/* === GET VMFLAGS === */
nfields = ret ? sscanf(mapbuf, "VmFlags: %"VMFL"[a-z ]", vmflags) : 0;
if (nfields == 1) {
int len = strlen(vmflags);
if (len > 0 && vmflags[len-1] == ' ') vmflags[--len] = '\0';
if (len > maxwv) maxwv = len;
if (! has_vmflags) has_vmflags = 1;
ret = fgets(mapbuf, sizeof mapbuf, f);
}
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"))
maxwv = justify_print("VmFlags", maxwv, 1);
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; i<footer_gap; i++) putc(' ', stdout);
for (listnode=listhead; listnode!=NULL; listnode=listnode->next) {
for (i=0; i<listnode->max_width; i++)
putc('=', stdout);
putc(' ', stdout);
}
putc('\n', stdout);
for (i=0; i<footer_gap; i++) putc(' ', stdout);
for (listnode=listhead; listnode!=NULL; listnode=listnode->next)
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 */
}
// variable placed here to silence compiler 'uninitialized' warning
static unsigned long start_To_Avoid_Warning;
static int one_proc (struct pids_stack *p)
{
char buf[32];
FILE *fp;
unsigned long total_shared = 0ul;
unsigned long total_private_readonly = 0ul;
unsigned long total_private_writeable = 0ul;
unsigned long diff = 0;
unsigned long end;
char perms[32] = "";
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;
printf("%u: %s\n", PIDS_VAL(tgid, s_int, p, Pids_info), PIDS_VAL(cmdline, str, p, Pids_info));
if (x_option || X_option || c_option) {
snprintf(buf, sizeof buf, "/proc/%u/smaps", PIDS_VAL(tgid, s_int, p, Pids_info));
if ((fp = fopen(buf, "r")) == NULL)
return 1;
} else {
snprintf(buf, sizeof buf, "/proc/%u/maps", PIDS_VAL(tgid, s_int, p, Pids_info));
if ((fp = fopen(buf, "r")) == NULL)
return 1;
}
if (X_option || c_option) {
print_extended_maps(fp);
fclose(fp);
return 0;
}
if (x_option) {
maxw1 = 16;
if (sizeof(long) == 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(long) == 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)) {
/* to clean up unprintables */
char *tmp;
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 */
if (cp2)
printf("%0*lx %*lu %*llu %*llu %*s %s\n",
maxw1, start_To_Avoid_Warning,
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 = end = 0;
perms[0] = '\0';
cp2 = NULL;
continue;
}
}
/* Other keys or not a key-value pair */
continue;
}
sscanf(mapbuf, "%lx-%lx %31s %llx %x:%x %llu",
&start_To_Avoid_Warning,
&end, perms, &file_offset, &dev_major, &dev_minor,
&inode);
if (end - 1 < range_low)
continue;
if (range_high < start_To_Avoid_Warning)
break;
tmp = strchr(mapbuf, '\n');
if (tmp)
*tmp = '\0';
tmp = mapbuf;
while (*tmp) {
if (!isprint(*tmp))
*tmp = '?';
tmp++;
}
diff = end - start_To_Avoid_Warning;
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_To_Avoid_Warning, 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_To_Avoid_Warning, diff, mapbuf, map_desc_showpath, dev_major,
dev_minor, inode);
printf("%0*lx %*lu %*s %0*llx %*.*s%03x:%05x %s\n",
maxw1, start_To_Avoid_Warning,
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_To_Avoid_Warning, diff, mapbuf, map_desc_showpath, dev_major,
dev_minor, inode);
printf((sizeof(long) == 8)
? "%016lx %6luK %s %s\n"
: "%08lx %6luK %s %s\n",
start_To_Avoid_Warning, (unsigned long)(diff >> 10), perms, cp);
}
}
fclose(fp);
if (!q_option) {
if (x_option) {
if (sizeof(long) == 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(long) == 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 *buf, *arg1, *arg2;
if ((buf = xstrdup(optarg)) == NULL) {
xerrx(EXIT_FAILURE, "%s: '%s'", _("failed to parse argument"),
(optarg?optarg:"(null)"));
}
arg1 = buf;
arg2 = strchr(arg1, ',');
if (arg2)
*arg2++ = '\0';
else
arg2 = arg1;
if (arg1[0] != '\0')
range_low = strtoul(arg1, &arg1, 16);
if (arg2[0] != '\0')
range_high = strtoul(arg2, &arg2, 16);
if (*arg1 || *arg2) {
free(buf);
xerrx(EXIT_FAILURE, "%s: '%s'", _("failed to parse argument"),
optarg);
}
free(buf);
}
#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 (const char *rc_filename)
{
FILE *f;
if (rc_filename == NULL)
return 0;
/* 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)
{
struct pids_fetch *pids_fetch;
unsigned *pidlist;
int reap_count, user_count;
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);
}
}
}
if ((size_t)argc >= INT_MAX / sizeof(pid_t))
xerrx(EXIT_FAILURE, _("too many arguments"));
if (procps_pids_new(&Pids_info, Pid_items, 4))
xerrx(EXIT_FAILURE, _("library failed pids statistics"));
pidlist = xmalloc(sizeof(pid_t) * argc);
user_count = 0;
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[user_count++] = pid;
}
discover_shm_minor();
if (!(pids_fetch = procps_pids_select(Pids_info, pidlist, user_count, PIDS_SELECT_PID)))
xerrx(EXIT_FAILURE, _("library failed pids statistics"));
for (reap_count = 0; reap_count < pids_fetch->counts->total; reap_count++) {
ret |= one_proc(pids_fetch->stacks[reap_count]);
}
free(pidlist);
procps_pids_unref(&Pids_info);
/* 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 (reap_count < user_count)
/* didn't find all processes asked for */
ret |= 42;
return ret;
}