Imported Upstream version 3.3.12
[platform/upstream/procps-ng.git] / pmap.c
1 /*
2  * pmap.c - print process memory mapping
3  * Copyright 2002 Albert Cahalan
4  *
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.
9  *
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.
14  *
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
18  */
19
20 #include <ctype.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <getopt.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/ipc.h>
28 #include <sys/shm.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <unistd.h>
32 #include <assert.h>
33
34 #include "c.h"
35 #include "fileutils.h"
36 #include "nls.h"
37 #include "proc/escape.h"
38 #include "xalloc.h"
39 #include "proc/readproc.h"
40 #include "proc/version.h"
41
42 const char *nls_Address,
43            *nls_Offset,
44            *nls_Device,
45            *nls_Mapping,
46            *nls_Perm,
47            *nls_Inode,
48            *nls_Kbytes,
49            *nls_Mode,
50            *nls_RSS,
51            *nls_Dirty;
52
53 static void nls_initialize(void)
54 {
55         setlocale (LC_ALL, "");
56         bindtextdomain(PACKAGE, LOCALEDIR);
57         textdomain(PACKAGE);
58
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");
65
66         /* these headings are used only by the -X/-XX options,
67            and are not derived literally from /proc/#/smaps */
68         nls_Perm    = _("Perm");
69         nls_Inode   = _("Inode");
70
71         /* these are potentially used for options other than -X/-XX, */
72         nls_Kbytes  = _("Kbytes");
73         nls_Mode    = _("Mode");
74         nls_RSS     = _("RSS");
75         nls_Dirty   = _("Dirty");
76 }
77
78 static int justify_print(const char *str, int width, int right)
79 {
80         if (width < 1)
81                 puts(str);
82         else {
83                 int len = strlen(str);
84                 if (width < len) width = len;
85                 printf(right ? "%*.*s " : "%-*.*s ", width, width, str);
86         }
87         return width;
88 }
89
90 static int integer_width(unsigned KLONG number)
91 {
92         int result;
93
94         result = !(number > 0);
95         while (number) {
96                 result++;
97                 number /= 10;
98         }
99
100         return result;
101 }
102
103
104 static void __attribute__ ((__noreturn__))
105 usage(FILE * out)
106 {
107         fputs(USAGE_HEADER, out);
108         fprintf(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);
129 }
130
131 static char mapbuf[1024];
132 static char cmdbuf[512];
133
134 static unsigned KLONG range_low;
135 static unsigned KLONG range_high = ~0ull;
136
137 static int c_option;
138 static int C_option;
139 static int d_option;
140 static int n_option;
141 static int N_option;
142 static int q_option;
143 static int x_option;
144 static int X_option;
145
146 static int map_desc_showpath;
147
148 static unsigned shm_minor = ~0u;
149
150 static void discover_shm_minor(void)
151 {
152         void *addr;
153         int shmid;
154         char mapbuf_b[256];
155
156         if (!freopen("/proc/self/maps", "r", stdin))
157                 return;
158
159         /* create */
160         shmid = shmget(IPC_PRIVATE, 42, IPC_CREAT | 0666);
161         if (shmid == -1)
162                 /* failed; oh well */
163                 return;
164         /* attach */
165         addr = shmat(shmid, NULL, SHM_RDONLY);
166         if (addr == (void *)-1)
167                 goto out_destroy;
168
169         while (fgets(mapbuf_b, sizeof mapbuf_b, stdin)) {
170                 char perms[32];
171                 /* to clean up unprintables */
172                 char *tmp;
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,
178                        &inode);
179                 tmp = strchr(mapbuf_b, '\n');
180                 if (tmp)
181                         *tmp = '\0';
182                 tmp = mapbuf_b;
183                 while (*tmp) {
184                         if (!isprint(*tmp))
185                                 *tmp = '?';
186                         tmp++;
187                 }
188                 if (start > (unsigned long)addr)
189                         continue;
190                 if (dev_major)
191                         continue;
192                 if (perms[3] != 's')
193                         continue;
194                 if (strstr(mapbuf_b, "/SYSV")) {
195                         shm_minor = dev_minor;
196                         break;
197                 }
198         }
199
200         if (shmdt(addr))
201                 perror(_("shared memory detach"));
202
203 out_destroy:
204         if (shmctl(shmid, IPC_RMID, NULL) && errno != EINVAL)
205                 perror(_("shared memory remove"));
206
207         return;
208 }
209
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)
214 {
215         char *cp;
216
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);
220                 return shmbuf;
221         }
222
223         cp = strrchr(mapbuf_b, '/');
224         if (cp) {
225                 if (showpath)
226                         return strchr(mapbuf_b, '/');
227                 return cp[1] ? cp + 1 : cp;
228         }
229
230         cp = strchr(mapbuf_b, '/');
231         if (cp) {
232                 if (showpath)
233                         return cp;
234                 /* it WILL succeed */
235                 return strrchr(cp, '/') + 1;
236         }
237
238         cp = _("  [ anon ]");
239         if ((p->start_stack >= addr) && (p->start_stack <= addr + len))
240                 cp = _("  [ stack ]");
241         return cp;
242 }
243
244
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 */
250
251 struct listnode {
252         char description[DETAIL_LENGTH];
253         char value_str[NUM_LENGTH];
254         unsigned KLONG value;
255         unsigned KLONG total;
256         int max_width;
257         struct listnode *next;
258 };
259
260 static struct listnode *listhead=NULL, *listtail=NULL, *listnode;
261
262
263 struct cnf_listnode {
264         char description[DETAIL_LENGTH];
265         struct cnf_listnode *next;
266 };
267
268 static struct cnf_listnode *cnf_listhead=NULL, *cnf_listnode;
269
270 static int is_unimportant (const char *s)
271 {
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;
280         return 0;
281 }
282
283 /* check, whether we want to display the field or not */
284 static int is_enabled (const char *s)
285 {
286         if (X_option == 1) return !is_unimportant(s);
287
288         if (c_option) {  /* taking the list of disabled fields from the rc file */
289
290                 for (cnf_listnode = cnf_listhead; cnf_listnode; cnf_listnode = cnf_listnode -> next) {
291                         if (!strcmp(s, cnf_listnode -> description)) return 1;
292                 }
293                 return 0;
294
295         }
296
297         return 1;
298 }
299
300
301 static void print_extended_maps (FILE *f)
302 {
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;
311
312         ret = fgets(mapbuf, sizeof mapbuf, f);
313         firstmapping = 2;
314         while (ret != NULL) {
315                 /* === READ MAPPING === */
316                 map_desc[0] = '\0';
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! */
324                 if (nfields < 6)
325                         xerrx(EXIT_FAILURE, _("Unknown format in smaps file!"));
326                 /* If line too long we dump everything else. */
327                 c = mapbuf[strlen(mapbuf) - 1];
328                 while (c != '\n') {
329                         ret = fgets(mapbuf, sizeof mapbuf, f);
330                         c = mapbuf[strlen(mapbuf) - 1];
331                 }
332
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);
339
340                 ret = fgets(mapbuf, sizeof mapbuf, f);
341                 nfields = sscanf(mapbuf, "%"DETL"[^:]: %"NUML"[0-9] kB %c",
342                                  detail_desc, value_str, &c);
343                 listnode = listhead;
344                 /* === READ MAPPING DETAILS === */
345                 while (ret != NULL && nfields == 2) {
346
347                         if (!is_enabled(detail_desc)) goto loop_end;
348
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);
355                                         listhead = listnode;
356                                 } else {
357                                         listtail->next = listnode;
358                                 }
359                                 listtail = 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;
364                         } else {
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"),
369                                               mapbuf);
370                         }
371                         strcpy(listnode->value_str, value_str);
372                         sscanf(value_str, "%"KLF"u", &listnode->value);
373                         if (firstmapping == 2) {
374                                 listnode->total += listnode->value;
375                                 if (q_option) {
376                                         maxw_ = strlen(listnode->value_str);
377                                         if (maxw_ > listnode->max_width)
378                                                 listnode->max_width = maxw_;
379                                 }
380                         }
381                         listnode = listnode->next;
382 loop_end:
383                         ret = fgets(mapbuf, sizeof mapbuf, f);
384                         nfields = sscanf(mapbuf, "%"DETL"[^:]: %"NUML"[0-9] kB %c",
385                                          detail_desc, value_str, &c);
386                 }
387
388                 /* === GET VMFLAGS === */
389                 nfields = sscanf(mapbuf, "VmFlags: %[a-z ]", vmflags);
390                 if (nfields == 1) {
391                         if (! has_vmflags) has_vmflags = 1;
392                         ret = fgets(mapbuf, sizeof mapbuf, f);
393                         if (strlen(vmflags) > maxwv) maxwv = strlen(vmflags);
394                 }
395
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 */
401
402                                 if (!q_option) {
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_;
408                                         }
409                                 }
410
411                         }
412                 } else {                 /* the maximum widths have been measured, we've already reached the printing stage */
413                         /* === PRINT THIS MAPPING === */
414
415                         /* Print header */
416                         if (firstmapping && !q_option) {
417
418                                 maxw1 = justify_print(nls_Address, maxw1, 1);
419
420                                 if (is_enabled(nls_Perm))
421                                         maxw2 = justify_print(nls_Perm, maxw2, 1);
422
423                                 if (is_enabled(nls_Offset))
424                                         maxw3 = justify_print(nls_Offset, maxw3, 1);
425
426                                 if (is_enabled(nls_Device))
427                                         maxw4 = justify_print(nls_Device, maxw4, 1);
428
429                                 if (is_enabled(nls_Inode))
430                                         maxw5 = justify_print(nls_Inode, maxw5, 1);
431
432                                 for (listnode=listhead; listnode!=NULL; listnode=listnode->next)
433                                         justify_print(listnode->description, listnode->max_width, 1);
434
435                                 if (has_vmflags && is_enabled("VmFlags"))
436                                         printf(" %*s", maxwv, "VmFlags");
437
438                                 if (is_enabled(nls_Mapping))
439                                         justify_print(nls_Mapping, 0, 0);
440                                 else
441                                         printf("\n");
442                         }
443
444                         /* Print data */
445                         printf("%*s", maxw1, start);    /* Address field is always enabled */
446
447                         if (is_enabled(nls_Perm))
448                                 printf(" %*s", maxw2, perms);
449
450                         if (is_enabled(nls_Offset))
451                                 printf(" %*s", maxw3, offset);
452
453                         if (is_enabled(nls_Device))
454                                 printf(" %*s", maxw4, dev);
455
456                         if (is_enabled(nls_Inode))
457                                 printf(" %*s", maxw5, inode);
458
459                         for (listnode=listhead; listnode!=NULL; listnode=listnode->next)
460                                 printf(" %*s", listnode->max_width, listnode->value_str);
461
462                         if (has_vmflags && is_enabled("VmFlags"))
463                                 printf(" %*s", maxwv, vmflags);
464
465                         if (is_enabled(nls_Mapping)) {
466                                 if (map_desc_showpath) {
467                                         printf(" %s", map_desc);
468                                 } else {
469                                         map_basename = strrchr(map_desc, '/');
470                                         if (!map_basename) {
471                                                 printf(" %s", map_desc);
472                                         } else {
473                                                 printf(" %s", map_basename + 1);
474                                         }
475
476                                 }
477                         }
478
479                         printf("\n");
480
481                         firstmapping = 0;
482
483                 }
484         }
485         /* === PRINT TOTALS === */
486         if (!q_option && listhead!=NULL) { /* footer enabled and non-empty */
487
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;
493
494                 for (i=0; i<footer_gap; i++) putc(' ', stdout);
495
496                 for (listnode=listhead; listnode!=NULL; listnode=listnode->next) {
497                         for (i=0; i<listnode->max_width; i++)
498                                 putc('=', stdout);
499                         putc(' ', stdout);
500                 }
501
502                 putc('\n', stdout);
503
504                 for (i=0; i<footer_gap; i++) putc(' ', stdout);
505
506                 for (listnode=listhead; listnode!=NULL; listnode=listnode->next)
507                         printf("%*lu ", listnode->max_width, listnode->total);
508
509                 fputs("KB \n", stdout);
510         }
511         /* We don't free() the list, it's used for all PIDs passed as arguments */
512 }
513
514 static int one_proc(proc_t * p)
515 {
516         char buf[32];
517         FILE *fp;
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;
530
531         /* Overkill, but who knows what is proper? The "w" prog uses
532          * the tty width to determine this.
533          */
534         int maxcmd = 0xfffff;
535
536         escape_command(cmdbuf, p, sizeof cmdbuf, &maxcmd,
537                        ESC_ARGS | ESC_BRACKETS);
538         printf("%u:   %s\n", p->tgid, cmdbuf);
539
540         if (x_option || X_option || c_option) {
541                 sprintf(buf, "/proc/%u/smaps", p->tgid);
542                 if ((fp = fopen(buf, "r")) == NULL)
543                         return 1;
544         } else {
545                 sprintf(buf, "/proc/%u/maps", p->tgid);
546                 if ((fp = fopen(buf, "r")) == NULL)
547                         return 1;
548         }
549
550         if (X_option || c_option) {
551                 print_extended_maps(fp);
552                 return 0;
553         }
554
555         if (x_option) {
556                 maxw1 = 16;
557                 if (sizeof(KLONG) == 4) maxw1 = 8;
558                 maxw2 = maxw3 = maxw4 = 7;
559                 maxw5 = 5;
560                 if (!q_option) {
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);
567                 }
568         }
569
570         if (d_option) {
571                 maxw1 = 16;
572                 if (sizeof(KLONG) == 4) maxw1 = 8;
573                 maxw2 = 7;
574                 maxw3 = 5;
575                 maxw4 = 16;
576                 maxw5 = 9;
577                 if (!q_option) {
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);
584                 }
585         }
586
587         while (fgets(mapbuf, sizeof mapbuf, fp)) {
588                 char perms[32];
589                 /* to clean up unprintables */
590                 char *tmp;
591                 unsigned KLONG start, end;
592                 unsigned long long file_offset, inode;
593                 unsigned dev_major, dev_minor;
594                 unsigned long long smap_value;
595                 char smap_key[21];
596
597                 /* hex values are lower case or numeric, keys are upper */
598                 if (mapbuf[0] >= 'A' && mapbuf[0] <= 'Z') {
599                         /* Its a key */
600                         if (sscanf
601                             (mapbuf, "%20[^:]: %llu", smap_key,
602                              &smap_value) == 2) {
603                                 if (strncmp("Rss", smap_key, 3) == 0) {
604                                         rss = smap_value;
605                                         total_rss += smap_value;
606                                         continue;
607                                 }
608                                 if (strncmp("Shared_Dirty", smap_key, 12) == 0) {
609                                         shared_dirty = smap_value;
610                                         total_shared_dirty += smap_value;
611                                         continue;
612                                 }
613                                 if (strncmp("Private_Dirty", smap_key, 13) == 0) {
614                                         private_dirty = smap_value;
615                                         total_private_dirty += smap_value;
616                                         continue;
617                                 }
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",
621                                                maxw1, start,
622                                                maxw2, (unsigned long)(diff >> 10),
623                                                maxw3, rss,
624                                                maxw4, (private_dirty + shared_dirty),
625                                                maxw5, perms,
626                                                cp2);
627                                         /* reset some counters */
628                                         rss = shared_dirty = private_dirty = 0ull;
629                                         diff = 0;
630                                         continue;
631                                 }
632                         }
633                         /* Other keys or not a key-value pair */
634                         continue;
635                 }
636                 sscanf(mapbuf, "%" KLF "x-%" KLF "x %31s %llx %x:%x %llu", &start,
637                        &end, perms, &file_offset, &dev_major, &dev_minor,
638                        &inode);
639
640                 if (end - 1 < range_low)
641                         continue;
642                 if (range_high < start)
643                         break;
644
645                 tmp = strchr(mapbuf, '\n');
646                 if (tmp)
647                         *tmp = '\0';
648                 tmp = mapbuf;
649                 while (*tmp) {
650                         if (!isprint(*tmp))
651                                 *tmp = '?';
652                         tmp++;
653                 }
654
655                 diff = end - start;
656                 if (perms[3] == 's')
657                         total_shared += diff;
658                 if (perms[3] == 'p') {
659                         perms[3] = '-';
660                         if (perms[1] == 'w')
661                                 total_private_writeable += diff;
662                         else
663                                 total_private_readonly += diff;
664                 }
665                 /* format used by Solaris 9 and procps-3.2.0+ an 'R'
666                  * if swap not reserved (MAP_NORESERVE, SysV ISM
667                  * shared mem, etc.)
668                  */
669                 perms[4] = '-';
670                 perms[5] = '\0';
671
672                 if (x_option) {
673                         cp2 =
674                             mapping_name(p, start, diff, mapbuf, map_desc_showpath, dev_major,
675                                          dev_minor, inode);
676                         /* printed with the keys */
677                         continue;
678                 }
679                 if (d_option) {
680                         const char *cp =
681                             mapping_name(p, start, diff, mapbuf, map_desc_showpath, dev_major,
682                                          dev_minor, inode);
683                         printf("%0*" KLF "x %*lu %*s %0*llx %*.*s%03x:%05x %s\n",
684                                maxw1, start,
685                                maxw2, (unsigned long)(diff >> 10),
686                                maxw3, perms,
687                                maxw4, file_offset,
688                                (maxw5-9), (maxw5-9), " ", dev_major, dev_minor,
689                                cp);
690                 }
691                 if (!x_option && !d_option) {
692                         const char *cp =
693                             mapping_name(p, start, diff, mapbuf, map_desc_showpath, dev_major,
694                                          dev_minor, inode);
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);
699                 }
700
701         }
702         fclose(fp);
703         if (!q_option) {
704                 if (x_option) {
705                         if (sizeof(KLONG) == 4)
706                                 justify_print("--------", maxw1, 0);
707                         else
708                                 justify_print("----------------", maxw1, 0);
709                         justify_print("-------", maxw2, 1);
710                         justify_print("-------", maxw3, 1);
711                         justify_print("-------", maxw4, 1);
712                         printf("\n");
713
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,
719                                 maxw3, total_rss,
720                                 maxw4, (total_shared_dirty +
721                                         total_private_dirty));
722                 }
723                 if (d_option) {
724                         printf
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);
729                 }
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);
737                         else
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);
743                 }
744         }
745
746         return 0;
747 }
748
749 static void range_arguments(char *optarg)
750 {
751         char *arg1;
752         char *arg2;
753
754         arg1 = xstrdup(optarg);
755         arg2 = strchr(arg1, ',');
756         if (arg2)
757                 *arg2 = '\0';
758         if (arg2)
759                 ++arg2;
760         else
761                 arg2 = arg1;
762         if (arg1 && *arg1)
763                 range_low = STRTOUKL(arg1, &arg1, 16);
764         if (*arg2)
765                 range_high = STRTOUKL(arg2, &arg2, 16);
766         if (arg1 && (*arg1 || *arg2))
767                 xerrx(EXIT_FAILURE, "%s: '%s'", _("failed to parse argument"),
768                       optarg);
769 }
770
771
772 #define MAX_CNF_LINE_LEN                1024
773
774 #define SECTION_ID_NONE                 0
775 #define SECTION_ID_UNSUPPORTED          1
776
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
780
781 #define SECTION_STR_MAPPING             "[Mapping]"
782 #define SECTION_STR_MAPPING_LEN         (sizeof(SECTION_STR_MAPPING) - 1)
783 #define SECTION_ID_MAPPING              3
784
785 static int config_read (char *rc_filename)
786 {
787         FILE *f;
788         char line_buf[MAX_CNF_LINE_LEN + 1];
789         char tmp_buf [MAX_CNF_LINE_LEN + 1];
790         char *trimmed;
791         int length;
792         char *tail, *token;
793         int line_cnt, section_id;
794
795         f = fopen(rc_filename, "r");
796
797         if (!f) return 0; /* can't open the file for reading */
798
799         line_cnt = 0;
800         section_id = SECTION_ID_NONE;
801
802         while (fgets (line_buf, MAX_CNF_LINE_LEN + 1, f)) {
803
804                 line_cnt++;
805
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') ;
816                 }
817
818                 /* trim leading whitespaces */
819                 trimmed = line_buf;
820                 while (*trimmed == ' ' || *trimmed == '\t') trimmed++;
821
822                 /* skip comments and empty lines */
823                 if (*trimmed == '#' || *trimmed == '\0') continue;
824
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;
832                         } else {
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);
837                                         trimmed++;
838                                 } else {
839                                         xwarnx(_("syntax error found in the config - line %d"), line_cnt);
840                                 }
841                         }
842
843                         /* trim trailing whitespaces */
844                         while (*trimmed == ' ' || *trimmed == '\t') trimmed++;
845
846                         /* skip comments and empty tails */
847                         if (*trimmed == '#' || *trimmed == '\0') continue;
848
849                         /* anything else found on the section line ??? */
850                         xwarnx(_("syntax error found in the config - line %d"), line_cnt);
851                 }
852
853                 switch (section_id) {
854                         case SECTION_ID_FIELDS_DISPLAY:
855                                 token = strtok (trimmed, " \t");
856
857                                 if (token) {
858                                         tail = strtok (NULL, " \t");
859
860                                         if (tail && *tail != '#') {
861                                                 xwarnx(_("syntax error found in the config - line %d"), line_cnt);
862                                         }
863
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;
869                                 }
870
871                                 break;
872
873                         case SECTION_ID_MAPPING:
874                                 token = strtok (trimmed, " \t");
875
876                                 if (token) {
877                                         tail = strtok (NULL, " \t");
878
879                                         if (tail && *tail != '#') {
880                                                 xwarnx(_("syntax error found in the config - line %d"), line_cnt);
881                                         }
882
883                                         if (!strcmp(token,"ShowPath")) map_desc_showpath = !map_desc_showpath;
884                                 }
885
886                                 break;
887
888                         case SECTION_ID_UNSUPPORTED:
889                                 break; /* ignore the content */
890
891                         default:
892                                 xwarnx(_("syntax error found in the config - line %d"), line_cnt);
893                 }
894         }
895
896         fclose(f);
897
898         return 1;
899 }
900
901
902 static int config_create (char *rc_filename)
903 {
904         FILE *f;
905
906         /* check if rc exists */
907         f = fopen(rc_filename, "r");
908
909         if (f) {  /* file exists ... let user to delete/remove it first */
910                 fclose(f);
911                 xwarnx(_("the file already exists - delete or rename it first"));
912                 return 0;
913         }
914
915         /* file doesn't exist */
916
917         f = fopen(rc_filename, "w");
918
919         if (!f) return 0; /* can't open the file for writing */
920
921         /* current rc template, might change in the future */
922         fprintf(f,"# pmap's Config File\n");
923         fprintf(f,"\n");
924         fprintf(f,"# All the entries are case sensitive.\n");
925         fprintf(f,"# Unsupported entries are ignored!\n");
926         fprintf(f,"\n");
927         fprintf(f,"[Fields Display]\n");
928         fprintf(f,"\n");
929         fprintf(f,"# To enable a field uncomment its entry\n");
930         fprintf(f,"\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");
936         fprintf(f,"#Rss\n");
937         fprintf(f,"#Pss\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);
951         fprintf(f,"\n");
952         fprintf(f,"\n");
953         fprintf(f,"[Mapping]\n");
954         fprintf(f,"\n");
955         fprintf(f,"# to show paths in the mapping column uncomment the following line\n");
956         fprintf(f,"#ShowPath\n");
957         fprintf(f,"\n");
958
959         fclose(f);
960
961         return 1;
962 }
963
964
965 /* returns rc filename based on the program_invocation_short_name */
966 static char *get_default_rc_filename(void)
967 {
968         char *rc_filename;
969         int rc_filename_len;
970         const char *homedir;
971
972         homedir = getenv("HOME");
973         if (!homedir) {
974                 xwarnx(_("HOME variable undefined"));
975                 return NULL;
976         }
977
978         rc_filename_len = snprintf(NULL, 0, "%s/.%src", homedir, program_invocation_short_name);
979
980         rc_filename = (char *) calloc (1, rc_filename_len + 1);
981         if (!rc_filename) {
982                 xwarnx(_("memory allocation failed"));
983                 return NULL;
984         }
985
986         snprintf(rc_filename, rc_filename_len + 1, "%s/.%src", homedir, program_invocation_short_name);
987
988         return rc_filename;
989 }
990
991
992 int main(int argc, char **argv)
993 {
994         pid_t *pidlist;
995         unsigned count = 0;
996         PROCTAB *PT;
997         proc_t p;
998         int ret = 0, c, conf_ret;
999         char *rc_filename = NULL;
1000
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'},
1013                 {NULL, 0, NULL, 0}
1014         };
1015
1016 #ifdef HAVE_PROGRAM_INVOCATION_NAME
1017         program_invocation_name = program_invocation_short_name;
1018 #endif
1019         nls_initialize();
1020         atexit(close_stdout);
1021
1022         if (argc < 2)
1023                 usage(stderr);
1024
1025         while ((c = getopt_long(argc, argv, "xXrdqA:hVcC:nN:p", longopts, NULL)) != -1)
1026                 switch (c) {
1027                 case 'x':
1028                         x_option = 1;
1029                         break;
1030                 case 'X':
1031                         X_option++;
1032                         break;
1033                 case 'r':
1034                         xwarnx(_("option -r is ignored as SunOS compatibility"));
1035                         break;
1036                 case 'd':
1037                         d_option = 1;
1038                         break;
1039                 case 'q':
1040                         q_option = 1;
1041                         break;
1042                 case 'A':
1043                         range_arguments(optarg);
1044                         break;
1045                 case 'h':
1046                         usage(stdout);
1047                 case 'V':
1048                         printf(PROCPS_NG_VERSION);
1049                         return EXIT_SUCCESS;
1050                 case 'c':
1051                         c_option = 1;
1052                         break;
1053                 case 'C':
1054                         C_option = 1;
1055                         rc_filename = optarg;
1056                         break;
1057                 case 'n':
1058                         n_option = 1;
1059                         break;
1060                 case 'N':
1061                         N_option = 1;
1062                         rc_filename = optarg;
1063                         break;
1064                 case 'p':
1065                         map_desc_showpath = 1;
1066                         break;
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 */
1073                 default:
1074                         usage(stderr);
1075                 }
1076
1077         argc -= optind;
1078         argv += optind;
1079
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"));
1082
1083         if ((n_option || N_option) && (q_option || map_desc_showpath))
1084                 xerrx(EXIT_FAILURE, _("options -p, -q are mutually exclusive with -n, -N"));
1085
1086         if ((n_option || N_option) && argc > 0)
1087                 xerrx(EXIT_FAILURE, _("too many arguments"));
1088
1089         if (N_option) {
1090                 if (config_create(rc_filename)) {
1091                         xwarnx(_("rc file successfully created, feel free to edit the content"));
1092                         return (EXIT_SUCCESS);
1093                 } else {
1094                         xerrx(EXIT_FAILURE, _("couldn't create the rc file"));
1095                 }
1096         }
1097
1098         if (n_option) {
1099                 rc_filename = get_default_rc_filename();
1100
1101                 if (!rc_filename) return(EXIT_FAILURE);
1102
1103                 conf_ret = config_create(rc_filename); free(rc_filename);
1104
1105                 if (conf_ret) {
1106                         xwarnx(_("~/.%src file successfully created, feel free to edit the content"), program_invocation_short_name);
1107                         return (EXIT_SUCCESS);
1108                 } else {
1109                         xerrx(EXIT_FAILURE, _("couldn't create ~/.%src"), program_invocation_short_name);
1110                 }
1111         }
1112
1113         if (argc < 1)
1114                 xerrx(EXIT_FAILURE, _("argument missing"));
1115
1116         if (C_option) c_option = 1;
1117
1118         if (c_option) {
1119
1120                 if (!C_option) rc_filename = get_default_rc_filename();
1121
1122                 if (!rc_filename) return(EXIT_FAILURE);
1123
1124                 conf_ret = config_read(rc_filename);
1125
1126                 if (!conf_ret) {
1127                         if (C_option) {
1128                                 xerrx(EXIT_FAILURE, _("couldn't read the rc file"));
1129                         } else {
1130                                 xwarnx(_("couldn't read ~/.%src"), program_invocation_short_name);
1131                                 free(rc_filename);
1132                                 return(EXIT_FAILURE);
1133                         }
1134                 }
1135
1136         }
1137
1138         pidlist = xmalloc(sizeof(pid_t) * (argc+1));
1139
1140         while (*argv) {
1141                 char *walk = *argv++;
1142                 char *endp;
1143                 unsigned long pid;
1144                 if (!strncmp("/proc/", walk, 6)) {
1145                         walk += 6;
1146                         /*  user allowed to do: pmap /proc/PID */
1147                         if (*walk < '0' || *walk > '9')
1148                                 continue;
1149                 }
1150                 if (*walk < '0' || *walk > '9')
1151                         usage(stderr);
1152                 pid = strtoul(walk, &endp, 0);
1153                 if (pid < 1ul || pid > 0x7ffffffful || *endp)
1154                         usage(stderr);
1155                 pidlist[count++] = pid;
1156         }
1157
1158         discover_shm_minor();
1159
1160         memset(&p, '\0', sizeof(p));
1161         /* old libproc interface is zero-terminated */
1162         pidlist[count] = 0;
1163         PT = openproc(PROC_FILLSTAT | PROC_FILLARG | PROC_PID, pidlist);
1164         while (readproc(PT, &p)) {
1165                 ret |= one_proc(&p);
1166                 count--;
1167         }
1168         closeproc(PT);
1169         free(pidlist);
1170
1171         /* cleaning the list used for the -c/-X/-XX modes */
1172         for (listnode = listhead; listnode != NULL ; ) {
1173                 listnode = listnode -> next;
1174                 free(listhead);
1175                 listhead = listnode;
1176         }
1177
1178         /* cleaning the list used for the -c mode */
1179         for (cnf_listnode = cnf_listhead; cnf_listnode != NULL ; ) {
1180                 cnf_listnode = cnf_listnode -> next;
1181                 free(cnf_listhead);
1182                 cnf_listhead = cnf_listnode;
1183         }
1184
1185         if (count)
1186                 /* didn't find all processes asked for */
1187                 ret |= 42;
1188         return ret;
1189 }