Print this page
8158 Want named threads API
9857 proc manpages should have LIBRARY section


   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2013 Gary Mills
  24  *
  25  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  26  * Use is subject to license terms.
  27  *
  28  * Portions Copyright 2009 Chad Mynhier

  29  */
  30 
  31 #include <sys/types.h>
  32 #include <sys/resource.h>
  33 #include <sys/loadavg.h>
  34 #include <sys/time.h>
  35 #include <sys/pset.h>
  36 #include <sys/vm_usage.h>
  37 #include <zone.h>
  38 #include <libzonecfg.h>
  39 
  40 #include <stdio.h>
  41 #include <stdlib.h>
  42 #include <unistd.h>
  43 #include <dirent.h>
  44 #include <string.h>
  45 #include <errno.h>
  46 #include <poll.h>
  47 #include <ctype.h>
  48 #include <fcntl.h>


  69 #if     defined(ERR)
  70 #undef  ERR
  71 #endif
  72 
  73 #ifndef TEXT_DOMAIN                     /* should be defined by cc -D */
  74 #define TEXT_DOMAIN     "SYS_TEST"      /* use this only if it wasn't */
  75 #endif
  76 
  77 #include <curses.h>
  78 #include <term.h>
  79 
  80 #define LOGIN_WIDTH     8
  81 #define ZONE_WIDTH      28
  82 #define PROJECT_WIDTH   28
  83 
  84 #define PSINFO_HEADER_PROC \
  85 "   PID USERNAME  SIZE   RSS STATE  PRI NICE      TIME  CPU PROCESS/NLWP       "
  86 #define PSINFO_HEADER_PROC_LGRP \
  87 "   PID USERNAME  SIZE   RSS STATE  PRI NICE      TIME  CPU LGRP PROCESS/NLWP  "
  88 #define PSINFO_HEADER_LWP \
  89 "   PID USERNAME  SIZE   RSS STATE  PRI NICE      TIME  CPU PROCESS/LWPID      "
  90 #define PSINFO_HEADER_LWP_LGRP \
  91 "   PID USERNAME  SIZE   RSS STATE  PRI NICE      TIME  CPU LGRP PROCESS/LWPID "
  92 #define USAGE_HEADER_PROC \
  93 "   PID USERNAME USR SYS TRP TFL DFL LCK SLP LAT VCX ICX SCL SIG PROCESS/NLWP  "
  94 #define USAGE_HEADER_LWP \
  95 "   PID USERNAME USR SYS TRP TFL DFL LCK SLP LAT VCX ICX SCL SIG PROCESS/LWPID "
  96 #define USER_HEADER_PROC \
  97 " NPROC USERNAME  SWAP   RSS MEMORY      TIME  CPU                             "
  98 #define USER_HEADER_LWP \
  99 "  NLWP USERNAME  SWAP   RSS MEMORY      TIME  CPU                             "
 100 #define TASK_HEADER_PROC \
 101 "TASKID    NPROC  SWAP   RSS MEMORY      TIME  CPU PROJECT                     "
 102 #define TASK_HEADER_LWP \
 103 "TASKID     NLWP  SWAP   RSS MEMORY      TIME  CPU PROJECT                     "
 104 #define PROJECT_HEADER_PROC \
 105 "PROJID    NPROC  SWAP   RSS MEMORY      TIME  CPU PROJECT                     "
 106 #define PROJECT_HEADER_LWP \
 107 "PROJID     NLWP  SWAP   RSS MEMORY      TIME  CPU PROJECT                     "
 108 #define ZONE_HEADER_PROC \
 109 "ZONEID    NPROC  SWAP   RSS MEMORY      TIME  CPU ZONE                        "
 110 #define ZONE_HEADER_LWP \
 111 "ZONEID     NLWP  SWAP   RSS MEMORY      TIME  CPU ZONE                        "
 112 #define PSINFO_LINE \
 113 "%6d %-8s %5s %5s %-6s %3s  %3s %9s %3.3s%% %-.16s/%d"
 114 #define PSINFO_LINE_LGRP \
 115 "%6d %-8s %5s %5s %-6s %3s  %3s %9s %3.3s%% %4d %-.16s/%d"
 116 #define USAGE_LINE \
 117 "%6d %-8s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s "\
 118 "%3.3s %3.3s %-.12s/%d"
 119 #define USER_LINE \
 120 "%6d %-8s %5.5s %5.5s   %3.3s%% %9s %3.3s%%"
 121 #define TASK_LINE \
 122 "%6d %8d %5s %5s   %3.3s%% %9s %3.3s%% %28s"
 123 #define PROJECT_LINE \
 124 "%6d %8d %5s %5s   %3.3s%% %9s %3.3s%% %28s"
 125 #define ZONE_LINE \
 126 "%6d %8d %5s %5s   %3.3s%% %9s %3.3s%% %28s"
 127 
 128 #define TOTAL_LINE \
 129 "Total: %d processes, %d lwps, load averages: %3.2f, %3.2f, %3.2f"
 130 
 131 /* global variables */
 132 
 133 static char     *t_ulon;                        /* termcap: start underline */
 134 static char     *t_uloff;                       /* termcap: end underline */
 135 static char     *t_up;                          /* termcap: cursor 1 line up */
 136 static char     *t_eol;                         /* termcap: clear end of line */
 137 static char     *t_smcup;                       /* termcap: cursor mvcap on */
 138 static char     *t_rmcup;                       /* termcap: cursor mvcap off */


 155 static uint_t   total_procs;                    /* total number of procs */
 156 static uint_t   total_lwps;                     /* total number of lwps */
 157 static float    total_cpu;                      /* total cpu usage */
 158 static float    total_mem;                      /* total memory usage */
 159 
 160 static list_t   lwps;                           /* list of lwps/processes */
 161 static list_t   users;                          /* list of users */
 162 static list_t   tasks;                          /* list of tasks */
 163 static list_t   projects;                       /* list of projects */
 164 static list_t   zones;                          /* list of zones */
 165 static list_t   lgroups;                        /* list of lgroups */
 166 
 167 static volatile uint_t sigwinch = 0;
 168 static volatile uint_t sigtstp = 0;
 169 static volatile uint_t sigterm = 0;
 170 
 171 static long pagesize;
 172 
 173 /* default settings */
 174 
 175 static optdesc_t opts = {
 176         5,                      /* interval between updates, seconds */
 177         15,                     /* number of lines in top part */
 178         5,                      /* number of lines in bottom part */
 179         -1,                     /* number of iterations; infinitely */
 180         OPT_PSINFO | OPT_FULLSCREEN | OPT_USEHOME | OPT_TERMCAP,
 181         -1                      /* sort in decreasing order */
 182 };
 183 
 184 /*
 185  * Print timestamp as decimal reprentation of time_t value (-d u was specified)
 186  * or the standard date format (-d d was specified).
 187  */
 188 static void
 189 print_timestamp(void)
 190 {
 191         time_t t = time(NULL);
 192         static char *fmt = NULL;
 193 
 194         /* We only need to retrieve this once per invocation */
 195         if (fmt == NULL)


 344                  * gathered from psinfo.
 345                  */
 346         }
 347         free(results);
 348 }
 349 
 350 /*
 351  * A routine to display the contents of the list on the screen
 352  */
 353 static void
 354 list_print(list_t *list)
 355 {
 356         lwp_info_t *lwp;
 357         id_info_t *id;
 358         char usr[4], sys[4], trp[4], tfl[4];
 359         char dfl[4], lck[4], slp[4], lat[4];
 360         char vcx[4], icx[4], scl[4], sig[4];
 361         char psize[6], prssize[6], pmem[6], pcpu[6], ptime[12];
 362         char pstate[7], pnice[4], ppri[4];
 363         char pname[LOGNAME_MAX+1];

 364         char projname[PROJNAME_MAX+1];
 365         char zonename[ZONENAME_MAX+1];
 366         float cpu, mem;
 367         double loadavg[3] = {0, 0, 0};
 368         int i, lwpid;
 369 
 370         if (list->l_size == 0)
 371                 return;
 372 
 373         if (foreach_element(&set_tbl, &loadavg, psetloadavg) == 0) {
 374                 /*
 375                  * If processor sets aren't specified, we display system-wide
 376                  * load averages.
 377                  */
 378                 (void) getloadavg(loadavg, 3);
 379         }
 380 
 381         if (((opts.o_outpmode & OPT_UDATE) || (opts.o_outpmode & OPT_DDATE)) &&
 382             ((list->l_type == LT_LWPS) || !(opts.o_outpmode & OPT_SPLIT)))
 383                 print_timestamp();
 384         if (opts.o_outpmode & OPT_TTY)
 385                 (void) putchar('\r');
 386         (void) putp(t_ulon);
 387 

 388         switch (list->l_type) {
 389         case LT_PROJECTS:
 390                 if (opts.o_outpmode & OPT_LWPS)
 391                         (void) printf(PROJECT_HEADER_LWP);
 392                 else
 393                         (void) printf(PROJECT_HEADER_PROC);
 394                 break;
 395         case LT_TASKS:
 396                 if (opts.o_outpmode & OPT_LWPS)
 397                         (void) printf(TASK_HEADER_LWP);
 398                 else
 399                         (void) printf(TASK_HEADER_PROC);
 400                 break;
 401         case LT_ZONES:
 402                 if (opts.o_outpmode & OPT_LWPS)
 403                         (void) printf(ZONE_HEADER_LWP);
 404                 else
 405                         (void) printf(ZONE_HEADER_PROC);
 406                 break;
 407         case LT_USERS:
 408                 if (opts.o_outpmode & OPT_LWPS)
 409                         (void) printf(USER_HEADER_LWP);
 410                 else
 411                         (void) printf(USER_HEADER_PROC);
 412                 break;
 413         case LT_LWPS:
 414                 if (opts.o_outpmode & OPT_LWPS) {
 415                         if (opts.o_outpmode & OPT_PSINFO) {
 416                                 if (opts.o_outpmode & OPT_LGRP)
 417                                         (void) printf(PSINFO_HEADER_LWP_LGRP);
 418                                 else
 419                                         (void) printf(PSINFO_HEADER_LWP);
 420                         }
 421                         if (opts.o_outpmode & OPT_MSACCT)
 422                                 (void) printf(USAGE_HEADER_LWP);
 423                 } else {
 424                         if (opts.o_outpmode & OPT_PSINFO) {
 425                                 if (opts.o_outpmode & OPT_LGRP)
 426                                         (void) printf(PSINFO_HEADER_PROC_LGRP);
 427                                 else
 428                                         (void) printf(PSINFO_HEADER_PROC);
 429                         }
 430                         if (opts.o_outpmode & OPT_MSACCT)
 431                                 (void) printf(USAGE_HEADER_PROC);
 432                 }
 433                 break;
 434         }
 435 




 436         (void) putp(t_uloff);
 437         (void) putp(t_eol);
 438         (void) putchar('\n');
 439 
 440         for (i = 0; i < list->l_used; i++) {
 441                 switch (list->l_type) {
 442                 case LT_PROJECTS:
 443                 case LT_TASKS:
 444                 case LT_USERS:
 445                 case LT_ZONES:
 446                         id = list->l_ptrs[i];
 447                         /*
 448                          * CPU usage and memory usage normalization
 449                          */
 450                         if (total_cpu >= 100)
 451                                 cpu = (100 * id->id_pctcpu) / total_cpu;
 452                         else
 453                                 cpu = id->id_pctcpu;
 454                         if (id->id_sizematch == B_FALSE && total_mem >= 100)
 455                                 mem = (100 * id->id_pctmem) / total_mem;


 482                         if (list->l_type == LT_PROJECTS)
 483                                 (void) printf(PROJECT_LINE, (int)id->id_projid,
 484                                     id->id_nproc, psize, prssize, pmem, ptime,
 485                                     pcpu, projname);
 486                         else if (list->l_type == LT_TASKS)
 487                                 (void) printf(TASK_LINE, (int)id->id_taskid,
 488                                     id->id_nproc, psize, prssize, pmem, ptime,
 489                                     pcpu, projname);
 490                         else if (list->l_type == LT_ZONES)
 491                                 (void) printf(ZONE_LINE, (int)id->id_zoneid,
 492                                     id->id_nproc, psize, prssize, pmem, ptime,
 493                                     pcpu, zonename);
 494                         else
 495                                 (void) printf(USER_LINE, id->id_nproc, pname,
 496                                     psize, prssize, pmem, ptime, pcpu);
 497                         (void) putp(t_eol);
 498                         (void) putchar('\n');
 499                         break;
 500                 case LT_LWPS:
 501                         lwp = list->l_ptrs[i];
 502                         if (opts.o_outpmode & OPT_LWPS)
 503                                 lwpid = lwp->li_info.pr_lwp.pr_lwpid;
 504                         else
 505                                 lwpid = lwp->li_info.pr_nlwp +
 506                                     lwp->li_info.pr_nzomb;
 507                         pwd_getname(lwp->li_info.pr_uid, pname, sizeof (pname),
 508                             opts.o_outpmode & OPT_NORESOLVE,
 509                             opts.o_outpmode & (OPT_TERMCAP|OPT_TRUNC),
 510                             LOGIN_WIDTH);

 511                         if (opts.o_outpmode & OPT_PSINFO) {
 512                                 Format_size(psize, lwp->li_info.pr_size, 6);
 513                                 Format_size(prssize, lwp->li_info.pr_rssize, 6);
 514                                 Format_state(pstate,
 515                                     lwp->li_info.pr_lwp.pr_sname,
 516                                     lwp->li_info.pr_lwp.pr_onpro, 7);
 517                                 if (strcmp(lwp->li_info.pr_lwp.pr_clname,
 518                                     "RT") == 0 ||
 519                                     strcmp(lwp->li_info.pr_lwp.pr_clname,
 520                                     "SYS") == 0 ||
 521                                     lwp->li_info.pr_lwp.pr_sname == 'Z')
 522                                         (void) strcpy(pnice, "  -");
 523                                 else
 524                                         Format_num(pnice,
 525                                             lwp->li_info.pr_lwp.pr_nice - NZERO,
 526                                             4);
 527                                 Format_num(ppri, lwp->li_info.pr_lwp.pr_pri, 4);
 528                                 Format_pct(pcpu,
 529                                     FRC2PCT(lwp->li_info.pr_lwp.pr_pctcpu), 4);
 530                                 if (opts.o_outpmode & OPT_LWPS)
 531                                         Format_time(ptime,
 532                                             lwp->li_info.pr_lwp.pr_time.tv_sec,
 533                                             10);
 534                                 else
 535                                         Format_time(ptime,
 536                                             lwp->li_info.pr_time.tv_sec, 10);
 537                                 if (opts.o_outpmode & OPT_TTY)
 538                                         (void) putchar('\r');
 539                                 stripfname(lwp->li_info.pr_fname);
 540                                 if (opts.o_outpmode & OPT_LGRP) {
 541                                         (void) printf(PSINFO_LINE_LGRP,
 542                                             (int)lwp->li_info.pr_pid, pname,
 543                                             psize, prssize, pstate,
 544                                             ppri, pnice, ptime, pcpu,
 545                                             (int)lwp->li_info.pr_lwp.pr_lgrp,
 546                                             lwp->li_info.pr_fname, lwpid);
 547                                 } else {
 548                                         (void) printf(PSINFO_LINE,
 549                                             (int)lwp->li_info.pr_pid, pname,
 550                                             psize, prssize,
 551                                             pstate, ppri, pnice,
 552                                             ptime, pcpu,
 553                                             lwp->li_info.pr_fname, lwpid);
 554                                 }
 555                                 (void) putp(t_eol);
 556                                 (void) putchar('\n');
 557                         }
 558                         if (opts.o_outpmode & OPT_MSACCT) {
 559                                 Format_pct(usr, lwp->li_usr, 4);
 560                                 Format_pct(sys, lwp->li_sys, 4);
 561                                 Format_pct(slp, lwp->li_slp, 4);
 562                                 Format_num(vcx, lwp->li_vcx, 4);
 563                                 Format_num(icx, lwp->li_icx, 4);
 564                                 Format_num(scl, lwp->li_scl, 4);
 565                                 Format_num(sig, lwp->li_sig, 4);
 566                                 Format_pct(trp, lwp->li_trp, 4);
 567                                 Format_pct(tfl, lwp->li_tfl, 4);
 568                                 Format_pct(dfl, lwp->li_dfl, 4);
 569                                 Format_pct(lck, lwp->li_lck, 4);
 570                                 Format_pct(lat, lwp->li_lat, 4);
 571                                 if (opts.o_outpmode & OPT_TTY)
 572                                         (void) putchar('\r');
 573                                 stripfname(lwp->li_info.pr_fname);
 574                                 (void) printf(USAGE_LINE,
 575                                     (int)lwp->li_info.pr_pid, pname,
 576                                     usr, sys, trp, tfl, dfl, lck,
 577                                     slp, lat, vcx, icx, scl, sig,
 578                                     lwp->li_info.pr_fname, lwpid);
 579                                 (void) putp(t_eol);
 580                                 (void) putchar('\n');
 581                         }
 582                         break;
 583                 }
 584         }
 585 
 586         if (opts.o_outpmode & OPT_TTY)
 587                 (void) putchar('\r');
 588         if (opts.o_outpmode & OPT_TERMCAP) {
 589                 switch (list->l_type) {
 590                 case LT_PROJECTS:
 591                 case LT_USERS:
 592                 case LT_TASKS:
 593                 case LT_ZONES:
 594                         while (i++ < opts.o_nbottom) {
 595                                 (void) putp(t_eol);
 596                                 (void) putchar('\n');
 597                         }
 598                         break;


 860         }
 861         return (0);
 862 }
 863 
 864 static void
 865 add_proc(psinfo_t *psinfo)
 866 {
 867         lwp_info_t *lwp;
 868         id_t lwpid;
 869         pid_t pid = psinfo->pr_pid;
 870 
 871         lwpid = psinfo->pr_lwp.pr_lwpid;
 872         if ((lwp = lwpid_get(pid, lwpid)) == NULL)
 873                 lwp = list_add_lwp(&lwps, pid, lwpid);
 874         lwp->li_flags |= LWP_ALIVE | LWP_REPRESENT;
 875         (void) memcpy(&lwp->li_info, psinfo, sizeof (psinfo_t));
 876         lwp->li_info.pr_lwp.pr_pctcpu = lwp->li_info.pr_pctcpu;
 877 }
 878 
 879 static void





















 880 add_lwp(psinfo_t *psinfo, lwpsinfo_t *lwpsinfo, int flags)
 881 {
 882         lwp_info_t *lwp;
 883         pid_t pid = psinfo->pr_pid;
 884         id_t lwpid = lwpsinfo->pr_lwpid;
 885 
 886         if ((lwp = lwpid_get(pid, lwpid)) == NULL)
 887                 lwp = list_add_lwp(&lwps, pid, lwpid);
 888         lwp->li_flags &= ~LWP_REPRESENT;
 889         lwp->li_flags |= LWP_ALIVE;
 890         lwp->li_flags |= flags;
 891         (void) memcpy(&lwp->li_info, psinfo,
 892             sizeof (psinfo_t) - sizeof (lwpsinfo_t));
 893         (void) memcpy(&lwp->li_info.pr_lwp, lwpsinfo, sizeof (lwpsinfo_t));

 894 }
 895 
 896 static void
 897 prstat_scandir(DIR *procdir)
 898 {
 899         char *pidstr;
 900         pid_t pid;
 901         id_t lwpid;
 902         size_t entsz;
 903         long nlwps, nent, i;
 904         char *buf, *ptr;
 905 
 906         fds_t *fds;
 907         lwp_info_t *lwp;
 908         dirent_t *direntp;
 909 
 910         prheader_t      header;
 911         psinfo_t        psinfo;
 912         prusage_t       usage;
 913         lwpsinfo_t      *lwpsinfo;


1096                         if (opts.o_outpmode & OPT_TASKS)
1097                                 list_update(&tasks, lwp);
1098                         if (opts.o_outpmode & OPT_PROJECTS)
1099                                 list_update(&projects, lwp);
1100                         if (opts.o_outpmode & OPT_ZONES)
1101                                 list_update(&zones, lwp);
1102                         if (opts.o_outpmode & OPT_LGRP)
1103                                 list_update(&lgroups, lwp);
1104                         lwp->li_flags &= ~LWP_ALIVE;
1105                         lwp = lwp->li_next;
1106 
1107                 } else {
1108                         lwp_next = lwp->li_next;
1109                         list_remove_lwp(&lwps, lwp);
1110                         lwp = lwp_next;
1111                 }
1112         }
1113 }
1114 
1115 static void
1116 curses_on()
1117 {
1118         if ((opts.o_outpmode & OPT_TERMCAP) && (is_curses_on == FALSE)) {
1119                 (void) initscr();
1120                 (void) nonl();
1121                 (void) putp(t_smcup);
1122                 is_curses_on = TRUE;
1123         }
1124 }
1125 
1126 static void
1127 curses_off()
1128 {
1129         if ((is_curses_on == TRUE) && (opts.o_outpmode & OPT_TERMCAP)) {
1130                 (void) putp(t_rmcup);
1131                 (void) endwin();
1132                 is_curses_on = FALSE;
1133         }
1134         (void) fflush(stdout);
1135 }
1136 
1137 static int
1138 nlines()
1139 {
1140         struct winsize ws;
1141         char *envp;
1142         int n;



1143         if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) {
1144                 if (ws.ws_row > 0)
1145                         return (ws.ws_row);




1146         }
1147         if (envp = getenv("LINES")) {

1148                 if ((n = Atoi(envp)) > 0) {
1149                         opts.o_outpmode &= ~OPT_USEHOME;
1150                         return (n);
1151                 }
1152         }
1153         return (-1);






1154 }
1155 
1156 static void
1157 setmovecur()
1158 {
1159         int i, n;
1160         if ((opts.o_outpmode & OPT_FULLSCREEN) &&
1161             (opts.o_outpmode & OPT_USEHOME)) {
1162                 movecur = t_home;
1163                 return;
1164         }
1165         if (opts.o_outpmode & OPT_SPLIT) {
1166                 if (opts.o_ntop == 0)
1167                         n = opts.o_nbottom + 1;
1168                 else
1169                         n = opts.o_ntop + opts.o_nbottom + 2;
1170         } else {
1171                 if (opts.o_outpmode & OPT_USERS)
1172                         n = opts.o_nbottom + 1;
1173                 else
1174                         n = opts.o_ntop + 1;
1175         }
1176         if (((opts.o_outpmode & OPT_UDATE) || (opts.o_outpmode & OPT_DDATE)))
1177                 n++;
1178 
1179         if (movecur != NULL && movecur != empty_string && movecur != t_home)
1180                 free(movecur);
1181         movecur = Zalloc(strlen(t_up) * (n + 5));
1182         for (i = 0; i <= n; i++)
1183                 (void) strcat(movecur, t_up);
1184 }
1185 
1186 static int
1187 setsize()
1188 {
1189         static int oldn = 0;
1190         int n;
1191 
1192         if (opts.o_outpmode & OPT_FULLSCREEN) {
1193                 n = nlines();


1194                 if (n == oldn)
1195                         return (0);
1196                 oldn = n;
1197                 if (n == -1) {
1198                         opts.o_outpmode &= ~OPT_USEHOME;
1199                         setmovecur();           /* set default window size */
1200                         return (1);
1201                 }
1202                 n = n - 3;      /* minus header, total and cursor lines */
1203                 if ((opts.o_outpmode & OPT_UDATE) ||
1204                     (opts.o_outpmode & OPT_DDATE))
1205                         n--;    /* minus timestamp */
1206                 if (n < 1)
1207                         Die(gettext("window is too small (try -n)\n"));
1208                 if (opts.o_outpmode & OPT_SPLIT) {
1209                         if (n < 8) {
1210                                 Die(gettext("window is too small (try -n)\n"));
1211                         } else {
1212                                 opts.o_ntop = (n / 4) * 3;
1213                                 opts.o_nbottom = n - 1 - opts.o_ntop;
1214                         }
1215                 } else {
1216                         if (opts.o_outpmode & OPT_USERS)
1217                                 opts.o_nbottom = n;




   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2013 Gary Mills
  24  *
  25  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  26  * Use is subject to license terms.
  27  *
  28  * Portions Copyright 2009 Chad Mynhier
  29  * Copyright 2018 Joyent, Inc.  All rights reserved.
  30  */
  31 
  32 #include <sys/types.h>
  33 #include <sys/resource.h>
  34 #include <sys/loadavg.h>
  35 #include <sys/time.h>
  36 #include <sys/pset.h>
  37 #include <sys/vm_usage.h>
  38 #include <zone.h>
  39 #include <libzonecfg.h>
  40 
  41 #include <stdio.h>
  42 #include <stdlib.h>
  43 #include <unistd.h>
  44 #include <dirent.h>
  45 #include <string.h>
  46 #include <errno.h>
  47 #include <poll.h>
  48 #include <ctype.h>
  49 #include <fcntl.h>


  70 #if     defined(ERR)
  71 #undef  ERR
  72 #endif
  73 
  74 #ifndef TEXT_DOMAIN                     /* should be defined by cc -D */
  75 #define TEXT_DOMAIN     "SYS_TEST"      /* use this only if it wasn't */
  76 #endif
  77 
  78 #include <curses.h>
  79 #include <term.h>
  80 
  81 #define LOGIN_WIDTH     8
  82 #define ZONE_WIDTH      28
  83 #define PROJECT_WIDTH   28
  84 
  85 #define PSINFO_HEADER_PROC \
  86 "   PID USERNAME  SIZE   RSS STATE  PRI NICE      TIME  CPU PROCESS/NLWP       "
  87 #define PSINFO_HEADER_PROC_LGRP \
  88 "   PID USERNAME  SIZE   RSS STATE  PRI NICE      TIME  CPU LGRP PROCESS/NLWP  "
  89 #define PSINFO_HEADER_LWP \
  90 "   PID USERNAME  SIZE   RSS STATE  PRI NICE      TIME  CPU PROCESS/LWP        "
  91 #define PSINFO_HEADER_LWP_LGRP \
  92 "   PID USERNAME  SIZE   RSS STATE  PRI NICE      TIME  CPU LGRP PROCESS/LWP   "
  93 #define USAGE_HEADER_PROC \
  94 "   PID USERNAME USR SYS TRP TFL DFL LCK SLP LAT VCX ICX SCL SIG PROCESS/NLWP  "
  95 #define USAGE_HEADER_LWP \
  96 "   PID USERNAME USR SYS TRP TFL DFL LCK SLP LAT VCX ICX SCL SIG PROCESS/LWP   "
  97 #define USER_HEADER_PROC \
  98 " NPROC USERNAME  SWAP   RSS MEMORY      TIME  CPU                             "
  99 #define USER_HEADER_LWP \
 100 "  NLWP USERNAME  SWAP   RSS MEMORY      TIME  CPU                             "
 101 #define TASK_HEADER_PROC \
 102 "TASKID    NPROC  SWAP   RSS MEMORY      TIME  CPU PROJECT                     "
 103 #define TASK_HEADER_LWP \
 104 "TASKID     NLWP  SWAP   RSS MEMORY      TIME  CPU PROJECT                     "
 105 #define PROJECT_HEADER_PROC \
 106 "PROJID    NPROC  SWAP   RSS MEMORY      TIME  CPU PROJECT                     "
 107 #define PROJECT_HEADER_LWP \
 108 "PROJID     NLWP  SWAP   RSS MEMORY      TIME  CPU PROJECT                     "
 109 #define ZONE_HEADER_PROC \
 110 "ZONEID    NPROC  SWAP   RSS MEMORY      TIME  CPU ZONE                        "
 111 #define ZONE_HEADER_LWP \
 112 "ZONEID     NLWP  SWAP   RSS MEMORY      TIME  CPU ZONE                        "
 113 #define PSINFO_LINE \
 114 "%6d %-8s %5s %5s %-6s %3s  %3s %9s %3.3s%% %s"
 115 #define PSINFO_LINE_LGRP \
 116 "%6d %-8s %5s %5s %-6s %3s  %3s %9s %3.3s%% %4d %s"
 117 #define USAGE_LINE \
 118 "%6d %-8s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s "\
 119 "%3.3s %3.3s %s"
 120 #define USER_LINE \
 121 "%6d %-8s %5.5s %5.5s   %3.3s%% %9s %3.3s%%"
 122 #define TASK_LINE \
 123 "%6d %8d %5s %5s   %3.3s%% %9s %3.3s%% %28s"
 124 #define PROJECT_LINE \
 125 "%6d %8d %5s %5s   %3.3s%% %9s %3.3s%% %28s"
 126 #define ZONE_LINE \
 127 "%6d %8d %5s %5s   %3.3s%% %9s %3.3s%% %28s"
 128 
 129 #define TOTAL_LINE \
 130 "Total: %d processes, %d lwps, load averages: %3.2f, %3.2f, %3.2f"
 131 
 132 /* global variables */
 133 
 134 static char     *t_ulon;                        /* termcap: start underline */
 135 static char     *t_uloff;                       /* termcap: end underline */
 136 static char     *t_up;                          /* termcap: cursor 1 line up */
 137 static char     *t_eol;                         /* termcap: clear end of line */
 138 static char     *t_smcup;                       /* termcap: cursor mvcap on */
 139 static char     *t_rmcup;                       /* termcap: cursor mvcap off */


 156 static uint_t   total_procs;                    /* total number of procs */
 157 static uint_t   total_lwps;                     /* total number of lwps */
 158 static float    total_cpu;                      /* total cpu usage */
 159 static float    total_mem;                      /* total memory usage */
 160 
 161 static list_t   lwps;                           /* list of lwps/processes */
 162 static list_t   users;                          /* list of users */
 163 static list_t   tasks;                          /* list of tasks */
 164 static list_t   projects;                       /* list of projects */
 165 static list_t   zones;                          /* list of zones */
 166 static list_t   lgroups;                        /* list of lgroups */
 167 
 168 static volatile uint_t sigwinch = 0;
 169 static volatile uint_t sigtstp = 0;
 170 static volatile uint_t sigterm = 0;
 171 
 172 static long pagesize;
 173 
 174 /* default settings */
 175 
 176 optdesc_t opts = {
 177         5,                      /* interval between updates, seconds */
 178         15,                     /* number of lines in top part */
 179         5,                      /* number of lines in bottom part */
 180         -1,                     /* number of iterations; infinitely */
 181         OPT_PSINFO | OPT_FULLSCREEN | OPT_USEHOME | OPT_TERMCAP,
 182         -1                      /* sort in decreasing order */
 183 };
 184 
 185 /*
 186  * Print timestamp as decimal reprentation of time_t value (-d u was specified)
 187  * or the standard date format (-d d was specified).
 188  */
 189 static void
 190 print_timestamp(void)
 191 {
 192         time_t t = time(NULL);
 193         static char *fmt = NULL;
 194 
 195         /* We only need to retrieve this once per invocation */
 196         if (fmt == NULL)


 345                  * gathered from psinfo.
 346                  */
 347         }
 348         free(results);
 349 }
 350 
 351 /*
 352  * A routine to display the contents of the list on the screen
 353  */
 354 static void
 355 list_print(list_t *list)
 356 {
 357         lwp_info_t *lwp;
 358         id_info_t *id;
 359         char usr[4], sys[4], trp[4], tfl[4];
 360         char dfl[4], lck[4], slp[4], lat[4];
 361         char vcx[4], icx[4], scl[4], sig[4];
 362         char psize[6], prssize[6], pmem[6], pcpu[6], ptime[12];
 363         char pstate[7], pnice[4], ppri[4];
 364         char pname[LOGNAME_MAX+1];
 365         char name[PRFNSZ + THREAD_NAME_MAX + 2];
 366         char projname[PROJNAME_MAX+1];
 367         char zonename[ZONENAME_MAX+1];
 368         float cpu, mem;
 369         double loadavg[3] = {0, 0, 0};
 370         int i, n;
 371 
 372         if (list->l_size == 0)
 373                 return;
 374 
 375         if (foreach_element(&set_tbl, &loadavg, psetloadavg) == 0) {
 376                 /*
 377                  * If processor sets aren't specified, we display system-wide
 378                  * load averages.
 379                  */
 380                 (void) getloadavg(loadavg, 3);
 381         }
 382 
 383         if (((opts.o_outpmode & OPT_UDATE) || (opts.o_outpmode & OPT_DDATE)) &&
 384             ((list->l_type == LT_LWPS) || !(opts.o_outpmode & OPT_SPLIT)))
 385                 print_timestamp();
 386         if (opts.o_outpmode & OPT_TTY)
 387                 (void) putchar('\r');
 388         (void) putp(t_ulon);
 389 
 390         n = opts.o_cols;
 391         switch (list->l_type) {
 392         case LT_PROJECTS:
 393                 if (opts.o_outpmode & OPT_LWPS)
 394                         n = printf(PROJECT_HEADER_LWP);
 395                 else
 396                         n = printf(PROJECT_HEADER_PROC);
 397                 break;
 398         case LT_TASKS:
 399                 if (opts.o_outpmode & OPT_LWPS)
 400                         n = printf(TASK_HEADER_LWP);
 401                 else
 402                         n = printf(TASK_HEADER_PROC);
 403                 break;
 404         case LT_ZONES:
 405                 if (opts.o_outpmode & OPT_LWPS)
 406                         n = printf(ZONE_HEADER_LWP);
 407                 else
 408                         n = printf(ZONE_HEADER_PROC);
 409                 break;
 410         case LT_USERS:
 411                 if (opts.o_outpmode & OPT_LWPS)
 412                         n = printf(USER_HEADER_LWP);
 413                 else
 414                         n = printf(USER_HEADER_PROC);
 415                 break;
 416         case LT_LWPS:
 417                 if (opts.o_outpmode & OPT_LWPS) {
 418                         if (opts.o_outpmode & OPT_PSINFO) {
 419                                 if (opts.o_outpmode & OPT_LGRP)
 420                                         n = printf(PSINFO_HEADER_LWP_LGRP);
 421                                 else
 422                                         n = printf(PSINFO_HEADER_LWP);
 423                         }
 424                         if (opts.o_outpmode & OPT_MSACCT)
 425                                 n = printf(USAGE_HEADER_LWP);
 426                 } else {
 427                         if (opts.o_outpmode & OPT_PSINFO) {
 428                                 if (opts.o_outpmode & OPT_LGRP)
 429                                         n = printf(PSINFO_HEADER_PROC_LGRP);
 430                                 else
 431                                         n = printf(PSINFO_HEADER_PROC);
 432                         }
 433                         if (opts.o_outpmode & OPT_MSACCT)
 434                                 n = printf(USAGE_HEADER_PROC);
 435                 }
 436                 break;
 437         }
 438 
 439         /* Pad out the header line so the underline spans the whole width */
 440         if ((opts.o_outpmode & OPT_TERMCAP) && n < opts.o_cols)
 441                 (void) printf("%*s", (int)(opts.o_cols - n), "");
 442 
 443         (void) putp(t_uloff);
 444         (void) putp(t_eol);
 445         (void) putchar('\n');
 446 
 447         for (i = 0; i < list->l_used; i++) {
 448                 switch (list->l_type) {
 449                 case LT_PROJECTS:
 450                 case LT_TASKS:
 451                 case LT_USERS:
 452                 case LT_ZONES:
 453                         id = list->l_ptrs[i];
 454                         /*
 455                          * CPU usage and memory usage normalization
 456                          */
 457                         if (total_cpu >= 100)
 458                                 cpu = (100 * id->id_pctcpu) / total_cpu;
 459                         else
 460                                 cpu = id->id_pctcpu;
 461                         if (id->id_sizematch == B_FALSE && total_mem >= 100)
 462                                 mem = (100 * id->id_pctmem) / total_mem;


 489                         if (list->l_type == LT_PROJECTS)
 490                                 (void) printf(PROJECT_LINE, (int)id->id_projid,
 491                                     id->id_nproc, psize, prssize, pmem, ptime,
 492                                     pcpu, projname);
 493                         else if (list->l_type == LT_TASKS)
 494                                 (void) printf(TASK_LINE, (int)id->id_taskid,
 495                                     id->id_nproc, psize, prssize, pmem, ptime,
 496                                     pcpu, projname);
 497                         else if (list->l_type == LT_ZONES)
 498                                 (void) printf(ZONE_LINE, (int)id->id_zoneid,
 499                                     id->id_nproc, psize, prssize, pmem, ptime,
 500                                     pcpu, zonename);
 501                         else
 502                                 (void) printf(USER_LINE, id->id_nproc, pname,
 503                                     psize, prssize, pmem, ptime, pcpu);
 504                         (void) putp(t_eol);
 505                         (void) putchar('\n');
 506                         break;
 507                 case LT_LWPS:
 508                         lwp = list->l_ptrs[i];
 509 
 510                         format_name(lwp, name, sizeof (name));
 511 


 512                         pwd_getname(lwp->li_info.pr_uid, pname, sizeof (pname),
 513                             opts.o_outpmode & OPT_NORESOLVE,
 514                             opts.o_outpmode & (OPT_TERMCAP|OPT_TRUNC),
 515                             LOGIN_WIDTH);
 516 
 517                         if (opts.o_outpmode & OPT_PSINFO) {
 518                                 Format_size(psize, lwp->li_info.pr_size, 6);
 519                                 Format_size(prssize, lwp->li_info.pr_rssize, 6);
 520                                 Format_state(pstate,
 521                                     lwp->li_info.pr_lwp.pr_sname,
 522                                     lwp->li_info.pr_lwp.pr_onpro, 7);
 523                                 if (strcmp(lwp->li_info.pr_lwp.pr_clname,
 524                                     "RT") == 0 ||
 525                                     strcmp(lwp->li_info.pr_lwp.pr_clname,
 526                                     "SYS") == 0 ||
 527                                     lwp->li_info.pr_lwp.pr_sname == 'Z')
 528                                         (void) strcpy(pnice, "  -");
 529                                 else
 530                                         Format_num(pnice,
 531                                             lwp->li_info.pr_lwp.pr_nice - NZERO,
 532                                             4);
 533                                 Format_num(ppri, lwp->li_info.pr_lwp.pr_pri, 4);
 534                                 Format_pct(pcpu,
 535                                     FRC2PCT(lwp->li_info.pr_lwp.pr_pctcpu), 4);
 536                                 if (opts.o_outpmode & OPT_LWPS)
 537                                         Format_time(ptime,
 538                                             lwp->li_info.pr_lwp.pr_time.tv_sec,
 539                                             10);
 540                                 else
 541                                         Format_time(ptime,
 542                                             lwp->li_info.pr_time.tv_sec, 10);
 543                                 if (opts.o_outpmode & OPT_TTY)
 544                                         (void) putchar('\r');

 545                                 if (opts.o_outpmode & OPT_LGRP) {
 546                                         (void) printf(PSINFO_LINE_LGRP,
 547                                             (int)lwp->li_info.pr_pid, pname,
 548                                             psize, prssize, pstate,
 549                                             ppri, pnice, ptime, pcpu,
 550                                             lwp->li_info.pr_lwp.pr_lgrp, name);

 551                                 } else {
 552                                         (void) printf(PSINFO_LINE,
 553                                             (int)lwp->li_info.pr_pid, pname,
 554                                             psize, prssize, pstate, ppri, pnice,
 555                                             ptime, pcpu, name);


 556                                 }
 557                                 (void) putp(t_eol);
 558                                 (void) putchar('\n');
 559                         }
 560                         if (opts.o_outpmode & OPT_MSACCT) {
 561                                 Format_pct(usr, lwp->li_usr, 4);
 562                                 Format_pct(sys, lwp->li_sys, 4);
 563                                 Format_pct(slp, lwp->li_slp, 4);
 564                                 Format_num(vcx, lwp->li_vcx, 4);
 565                                 Format_num(icx, lwp->li_icx, 4);
 566                                 Format_num(scl, lwp->li_scl, 4);
 567                                 Format_num(sig, lwp->li_sig, 4);
 568                                 Format_pct(trp, lwp->li_trp, 4);
 569                                 Format_pct(tfl, lwp->li_tfl, 4);
 570                                 Format_pct(dfl, lwp->li_dfl, 4);
 571                                 Format_pct(lck, lwp->li_lck, 4);
 572                                 Format_pct(lat, lwp->li_lat, 4);
 573                                 if (opts.o_outpmode & OPT_TTY)
 574                                         (void) putchar('\r');

 575                                 (void) printf(USAGE_LINE,
 576                                     (int)lwp->li_info.pr_pid, pname,
 577                                     usr, sys, trp, tfl, dfl, lck,
 578                                     slp, lat, vcx, icx, scl, sig,
 579                                     name);
 580                                 (void) putp(t_eol);
 581                                 (void) putchar('\n');
 582                         }
 583                         break;
 584                 }
 585         }
 586 
 587         if (opts.o_outpmode & OPT_TTY)
 588                 (void) putchar('\r');
 589         if (opts.o_outpmode & OPT_TERMCAP) {
 590                 switch (list->l_type) {
 591                 case LT_PROJECTS:
 592                 case LT_USERS:
 593                 case LT_TASKS:
 594                 case LT_ZONES:
 595                         while (i++ < opts.o_nbottom) {
 596                                 (void) putp(t_eol);
 597                                 (void) putchar('\n');
 598                         }
 599                         break;


 861         }
 862         return (0);
 863 }
 864 
 865 static void
 866 add_proc(psinfo_t *psinfo)
 867 {
 868         lwp_info_t *lwp;
 869         id_t lwpid;
 870         pid_t pid = psinfo->pr_pid;
 871 
 872         lwpid = psinfo->pr_lwp.pr_lwpid;
 873         if ((lwp = lwpid_get(pid, lwpid)) == NULL)
 874                 lwp = list_add_lwp(&lwps, pid, lwpid);
 875         lwp->li_flags |= LWP_ALIVE | LWP_REPRESENT;
 876         (void) memcpy(&lwp->li_info, psinfo, sizeof (psinfo_t));
 877         lwp->li_info.pr_lwp.pr_pctcpu = lwp->li_info.pr_pctcpu;
 878 }
 879 
 880 static void
 881 get_lwpname(pid_t pid, id_t lwpid, char *buf, size_t bufsize)
 882 {
 883         char *path = NULL;
 884         int fd;
 885 
 886         buf[0] = '\0';
 887 
 888         if (asprintf(&path, "/proc/%d/lwp/%d/lwpname",
 889             (int)pid, (int)lwpid) == -1)
 890                 return;
 891 
 892         if ((fd = open(path, O_RDONLY)) != -1) {
 893                 (void) read(fd, buf, bufsize);
 894                 buf[bufsize - 1] = '\0';
 895                 (void) close(fd);
 896         }
 897 
 898         free(path);
 899 }
 900 
 901 static void
 902 add_lwp(psinfo_t *psinfo, lwpsinfo_t *lwpsinfo, int flags)
 903 {
 904         lwp_info_t *lwp;
 905         pid_t pid = psinfo->pr_pid;
 906         id_t lwpid = lwpsinfo->pr_lwpid;
 907 
 908         if ((lwp = lwpid_get(pid, lwpid)) == NULL)
 909                 lwp = list_add_lwp(&lwps, pid, lwpid);
 910         lwp->li_flags &= ~LWP_REPRESENT;
 911         lwp->li_flags |= LWP_ALIVE;
 912         lwp->li_flags |= flags;
 913         (void) memcpy(&lwp->li_info, psinfo,
 914             sizeof (psinfo_t) - sizeof (lwpsinfo_t));
 915         (void) memcpy(&lwp->li_info.pr_lwp, lwpsinfo, sizeof (lwpsinfo_t));
 916         get_lwpname(pid, lwpid, lwp->li_lwpname, sizeof (lwp->li_lwpname));
 917 }
 918 
 919 static void
 920 prstat_scandir(DIR *procdir)
 921 {
 922         char *pidstr;
 923         pid_t pid;
 924         id_t lwpid;
 925         size_t entsz;
 926         long nlwps, nent, i;
 927         char *buf, *ptr;
 928 
 929         fds_t *fds;
 930         lwp_info_t *lwp;
 931         dirent_t *direntp;
 932 
 933         prheader_t      header;
 934         psinfo_t        psinfo;
 935         prusage_t       usage;
 936         lwpsinfo_t      *lwpsinfo;


1119                         if (opts.o_outpmode & OPT_TASKS)
1120                                 list_update(&tasks, lwp);
1121                         if (opts.o_outpmode & OPT_PROJECTS)
1122                                 list_update(&projects, lwp);
1123                         if (opts.o_outpmode & OPT_ZONES)
1124                                 list_update(&zones, lwp);
1125                         if (opts.o_outpmode & OPT_LGRP)
1126                                 list_update(&lgroups, lwp);
1127                         lwp->li_flags &= ~LWP_ALIVE;
1128                         lwp = lwp->li_next;
1129 
1130                 } else {
1131                         lwp_next = lwp->li_next;
1132                         list_remove_lwp(&lwps, lwp);
1133                         lwp = lwp_next;
1134                 }
1135         }
1136 }
1137 
1138 static void
1139 curses_on(void)
1140 {
1141         if ((opts.o_outpmode & OPT_TERMCAP) && (is_curses_on == FALSE)) {
1142                 (void) initscr();
1143                 (void) nonl();
1144                 (void) putp(t_smcup);
1145                 is_curses_on = TRUE;
1146         }
1147 }
1148 
1149 static void
1150 curses_off(void)
1151 {
1152         if ((is_curses_on == TRUE) && (opts.o_outpmode & OPT_TERMCAP)) {
1153                 (void) putp(t_rmcup);
1154                 (void) endwin();
1155                 is_curses_on = FALSE;
1156         }
1157         (void) fflush(stdout);
1158 }
1159 
1160 static int
1161 nlines(int *linesp, int *colsp)
1162 {
1163         struct winsize ws;
1164         char *envp;
1165         int n;
1166 
1167         *linesp = -1;
1168         *colsp = -1;
1169         if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) {
1170                 if (ws.ws_row > 0)
1171                         *linesp = ws.ws_row;
1172                 if (ws.ws_col > 0)
1173                         *colsp = ws.ws_col;
1174                 if (ws.ws_row > 0 && ws.ws_col > 0)
1175                         return (0);
1176         }
1177 
1178         if ((envp = getenv("LINES")) != NULL) {
1179                 if ((n = Atoi(envp)) > 0) {
1180                         opts.o_outpmode &= ~OPT_USEHOME;
1181                         *linesp = n;
1182                 }
1183         }
1184         if ((envp = getenv("COLUMNS")) != NULL) {
1185                 if ((n = Atoi(envp)) > 0) {
1186                         *colsp = n;
1187                 }
1188         }
1189 
1190         return ((*linesp > 0 && *colsp > 0) ? 0 : -1);
1191 }
1192 
1193 static void
1194 setmovecur(void)
1195 {
1196         int i, n;
1197         if ((opts.o_outpmode & OPT_FULLSCREEN) &&
1198             (opts.o_outpmode & OPT_USEHOME)) {
1199                 movecur = t_home;
1200                 return;
1201         }
1202         if (opts.o_outpmode & OPT_SPLIT) {
1203                 if (opts.o_ntop == 0)
1204                         n = opts.o_nbottom + 1;
1205                 else
1206                         n = opts.o_ntop + opts.o_nbottom + 2;
1207         } else {
1208                 if (opts.o_outpmode & OPT_USERS)
1209                         n = opts.o_nbottom + 1;
1210                 else
1211                         n = opts.o_ntop + 1;
1212         }
1213         if (((opts.o_outpmode & OPT_UDATE) || (opts.o_outpmode & OPT_DDATE)))
1214                 n++;
1215 
1216         if (movecur != NULL && movecur != empty_string && movecur != t_home)
1217                 free(movecur);
1218         movecur = Zalloc(strlen(t_up) * (n + 5));
1219         for (i = 0; i <= n; i++)
1220                 (void) strcat(movecur, t_up);
1221 }
1222 
1223 static int
1224 setsize(void)
1225 {
1226         static int oldn = 0;
1227         int cols, n, ret;
1228 
1229         if (opts.o_outpmode & OPT_FULLSCREEN) {
1230                 ret = nlines(&n, &cols);
1231                 if (ret != -1)
1232                         opts.o_cols = cols;
1233                 if (n == oldn)
1234                         return (0);
1235                 oldn = n;
1236                 if (ret == -1) {
1237                         opts.o_outpmode &= ~OPT_USEHOME;
1238                         setmovecur();           /* set default window size */
1239                         return (1);
1240                 }
1241                 n = n - 3;      /* minus header, total and cursor lines */
1242                 if ((opts.o_outpmode & OPT_UDATE) ||
1243                     (opts.o_outpmode & OPT_DDATE))
1244                         n--;    /* minus timestamp */
1245                 if (n < 1)
1246                         Die(gettext("window is too small (try -n)\n"));
1247                 if (opts.o_outpmode & OPT_SPLIT) {
1248                         if (n < 8) {
1249                                 Die(gettext("window is too small (try -n)\n"));
1250                         } else {
1251                                 opts.o_ntop = (n / 4) * 3;
1252                                 opts.o_nbottom = n - 1 - opts.o_ntop;
1253                         }
1254                 } else {
1255                         if (opts.o_outpmode & OPT_USERS)
1256                                 opts.o_nbottom = n;