From owner-freebsd-bugs@FreeBSD.ORG Mon Apr 19 19:10:17 2004 Return-Path: Delivered-To: freebsd-bugs@hub.freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id C347E16A4CE for ; Mon, 19 Apr 2004 19:10:17 -0700 (PDT) Received: from freefall.freebsd.org (freefall.freebsd.org [216.136.204.21]) by mx1.FreeBSD.org (Postfix) with ESMTP id 0ED9243D45 for ; Mon, 19 Apr 2004 19:10:16 -0700 (PDT) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (gnats@localhost [127.0.0.1]) i3K2AFbv011857 for ; Mon, 19 Apr 2004 19:10:15 -0700 (PDT) (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.12.10/8.12.10/Submit) id i3K2AFfu011856; Mon, 19 Apr 2004 19:10:15 -0700 (PDT) (envelope-from gnats) Resent-Date: Mon, 19 Apr 2004 19:10:15 -0700 (PDT) Resent-Message-Id: <200404200210.i3K2AFfu011856@freefall.freebsd.org> Resent-From: FreeBSD-gnats-submit@FreeBSD.org (GNATS Filer) Resent-To: freebsd-bugs@FreeBSD.org Resent-Reply-To: FreeBSD-gnats-submit@FreeBSD.org, Cyrille Lefevre Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 7C2DB16A4CE for ; Mon, 19 Apr 2004 19:05:53 -0700 (PDT) Received: from ioskeha.hittite.isp.9tel.net (ioskeha.hittite.isp.9tel.net [62.62.156.27]) by mx1.FreeBSD.org (Postfix) with ESMTP id B063043D49 for ; Mon, 19 Apr 2004 19:05:51 -0700 (PDT) (envelope-from cyrille.lefevre@laposte.net) Received: from mail.gits.dyndns.org (unknown [81.185.56.92]) by ioskeha.hittite.isp.9tel.net (Postfix) with ESMTP id D8D1F17B457 for ; Tue, 20 Apr 2004 04:06:11 +0200 (CEST) Received: from gits.gits.fr.invalid (IDENT:oz97i2xyrql34mif@localhost [127.0.0.1])i3K25XJn087598 for ; Tue, 20 Apr 2004 04:05:33 +0200 (CEST) (envelope-from cyrille.lefevre@laposte.net) Received: by gits.gits.fr.invalid (tmda-sendmail, from uid 0); Tue, 20 Apr 2004 04:05:32 +0200 (CEST) Message-Id: <20040420020525.GA66658@gits.dyndns.org> Date: Tue, 20 Apr 2004 04:05:25 +0200 From: Cyrille Lefevre To: FreeBSD-gnats-submit@FreeBSD.org X-Send-Pr-Version: 3.113 Subject: bin/65803: ps enhancements (posix syntax, and more) X-BeenThere: freebsd-bugs@freebsd.org X-Mailman-Version: 2.1.1 Precedence: list Reply-To: Cyrille Lefevre List-Id: Bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 20 Apr 2004 02:10:17 -0000 >Number: 65803 >Category: bin >Synopsis: ps enhancements (posix syntax, and more) >Confidential: no >Severity: non-critical >Priority: medium >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: change-request >Submitter-Id: current-users >Arrival-Date: Mon Apr 19 19:10:15 PDT 2004 >Closed-Date: >Last-Modified: >Originator: Cyrille Lefevre >Release: FreeBSD 5.2-CURRENT i386 >Organization: ACME >Environment: System: FreeBSD gits 5.2-CURRENT FreeBSD 5.2-CURRENT #14: Mon Apr 19 05:29:50 CEST 2004 root@gits:/disk3/freebsd/current/obj/disk3/freebsd/current/src/sys/CUSTOM i386 >Description: here is a summarized description of changes. unfortunatelly, the manual page has not been rewritten yet to reflect all these changes, sorry. it's on my way, but only when I'll know which changes will be accepted or rejected. enhancements to the bsd syntax are : -A same as posix -A. -a show all processes with a controlling terminal. in sunos compat mode (-Q), don't show session leader processes. -G grouplist same as posix -G (as recently implemented). -g show all processes with a controlling terminal and session leader processes. (-k keylist) sort by the specified keyword(s) (option taken from netbsd, *not implemented yet*). (-K) don't show kernel thread (option taken from openbsd -k, *not implemented yet*). -n numerical output of the user and wchan fields (option taken from sunos). -p pidlist accept a list of pids (as recently implemented). -s show signal output, same as -O SFMT (option taken from tru64, conflicts w/ netbsd -s, aka show threads w/ different fields, IMHO, this may be implemented as part of freebsd -H). -t ttyslist accept a list of ttys (as recently implemented). also accept ttyXX in addition to /dev/ttyXX and XX (as required by susv3). -U userlist accept a list of effective user names (as recently implemented). also accept euids in addition of effective user names. -X don't show processes without a controlling terminal (opposite of -x) (as recently implemented). -Z the label field is always prepended. output is formated more intelligently when combining options such as -j, -l, -s, -u and -v. format strings are combined instead of being concatenated. so, field are always in the same place. old way : $ ps -lu UID PID PPID CPU PRI NI VSZ RSS MWCHAN STAT TT TIME COMMAND USER %CPU %MEM STARTED $ps -ul USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND UID PPID CPU PRI NI MWCHAN new way : $ ps -lu # or ps -ul USER UID PID PPID CPU PRI NI %CPU %MEM VSZ RSS MWCHAN TT STAT STARTED TIME COMMAND standard posix syntax is : -A show all processes, whether or not they have a controlling terminal or they are a session leader process. -a show all processes with a controlling terminal except session leader processes. -d show all processes with or without a controlling terminal except session leader processes. -e show all processes (same as -A). -f user oriented output. -G grouplist select processes by effective group name(s) or effective group id(s) (well, the standard says that is should be only id(s) and not name(s), which isn't coherent w/ -U and -u !). -g pgidlist select processes by process group id(s). -l long format output. -n namelist see below. -o fmtlist similar to bsd -o fmtlist. -p pidlist select processes by process id(s). -t ttylist select processes by terminal name(s) which may have the form co (for the system console), XX, ttyXX ot /dev/ttyXX. -U userlist select processes by real user name(s) or real user id(s). -u userlist select processes by effective user name(s) or effective user id(s). enhancements to the posix syntax are : -C cmdlist select processes by process name (option taken from hpux). -c omits the nice field and replaces the c field with the class field (option taken from solaris). -j job control oriented output (option taken from almost all sysv implementations) -L show threads (option taken from solaris). -M label field prepended (option taken from irix). -m show threads (option taken from tru64). -n system [-n core] specifies an alternative system namelist file and optionally, a core file if repeated (this is non standard). -O same as -o pid,fmtlist,tty,time,comm -o help behaves as bsd -L. -P includes the psr field (option taken from solaris). -s sidlist select processes by session id (option taken from almost all sysv implementations). -T show threads (option taken from irix). -x wide output format (option taken from hpux, similar to bsd -ww). -y omits the f field and replaces the addr field with the rss field (option taken from solaris). both syntax : -q enable or disable the posix compatibility. -Q enable or disable compatibility with sunos and tru64. in bsd mode, leader processes aren't selected by default, uses -g to show them. in bsd and posix modes, size are displayed in KMGPT format (ex.: 1.54M instead of 1580). The lists used as argument are either an identifier or a list of identifiers enclosed in " " (double quotes) and separated from one another by a comma or one or more spaces, or both. Because of the way the shell treats spaces and tabs, you need to quote space-separated lists. Options with a list used as argument may be specified more than once, and one or more of them may be empty since at least one of the options contains a specifier, if all the options does not contain a specifier, a warning message is displayed and ps exits. ex.: ps -p , -p ,$$, is right and refer to the current shell only while ps -p , -p '' is wrong. Format string which contains both an equal sign and a comma are considered to be comma only separated list. so, both -o "pid=process id,ppid=parent pid,command=command line" and -o "pid=child ppid=parent command=command" are well parsed, unfortunately, mixing "user=user name,pid=pid command" isn't... When printing using the command keyword, a process that has exited and has a parent that has not yet waited for the process (in other words, a zombie) is listed as ``''. and a process which is blocked while trying to exit is listed as ``'' If the command vector cannot be located (usually because it has not been set, as is the case of system processes and/or kernel threads) the com- mand name is printed within square brackets. If the command vector cannot be located (usually because it has not been set, as is the case of system processes and/or kernel threads) the com- mand name is printed within square brackets. modified keywords : state The state is given by a sequence of characters, for example, ``RWNA''. The first character indicates the run state of the process: O Marks a running process (on processor). (state taken from solaris and irix) Additional characters after these, if any, indicate additional state information: > The process has specified a soft limit on memory requirements and is currently exceeding that limit; such a process is (necessarily) not swapped. (historical state taken from 4.3bsd) a The process is using scheduler activations. (state taken from netbsd) K The process is a kernel thread (mutially exclusive w/ L). (state taken from openbsd) comm command (the argv[0] value) (description change) etime elapsed time since the process was started, in the form [[dd-]hh:]mm:ss (description change taken from netbsd) if WITH_MAXRSS is defined -- implemented lim memory usage limit (description change taken from openbsd) logname login name of user who started the process (alias login) (description change taken from netbsd/openbsd) paddr kernel virtual address of the struct proc belonging to the process (description change taken from netbsd) added keywords (bsd) : keyword header description ctime CTIME accumulated CPU time of all children that have exited [netbsd] dsiz DSIZ data size (in Kbytes) [openbsd] egid EGID effective group id [netbsd/openbsd] egroup EGROUP group name (from egid) [netbsd/openbsd] euid EUID effective user id [netbsd/openbsd] euser EUSER user name (from euid) [netbsd/openbsd] gid GID effective group id [netbsd/openbsd] group GROUP group name (from gid) [openbsd/hpux/irix/solaris/tru64] groups GROUPS group access list [netbsd/openbsd] groupnames GROUPNAMES group names (from group access list) [netbsd/openbsd] holdcnt HOLDCNT number of holds on the process (if non-zero, process can't be swapped) [netbsd/openbsd] rssize alias to rsz [43reno/44bsd/netbsd/openbsd/tru64] rsz RSZ alias to rss [43reno/44bsd/netbsd/openbsd/tru64] ssiz SSIZ stack size (in Kbytes) [openbsd] svgroup SVGROUP group name (from svgid) [netbsd/openbsd] svuser SVUSER user name (from svuid) [netbsd/openbsd] if WITH_UMASK is defined umask UMAKS process umask [43reno/tru64] required keywords (posix) : keyword header description addr ADDR alias to paddr [hpux/solaris/tru64] c C alias to cpu [solaris/tru64] class CLS process class scheduling [irix/solaris] psr PSR processor number to which the process is bound [solaris/tru64] s S alias to state [solaris] stime STIME started time of the process, in the form hh:mm:ss or mmm:dd if the process is older than one day. [hpux/irix/solaris/tru64] sz SZ alias to vsz [hpux/tru64] if COMPAT_TRU64 is defined DFMT similar to "pid tname state cputime command" F5FMT similar to "uname pid ppid pcpu start tt time command" FL5FMT similar to "f state uid pid ppid pcpu pri nice rss wchan start time command" JFMT similar to "user pid ppid pgid sess jobc state tname cputime command" L5FMT similar to "f state uid pid ppid pcpu pri nice rss wchan tt time ucomm" LFMT similar to "uid pid ppid cp pri nice vsz rss wchan state tname cputime command" RUSAGE similar to "minflt majflt nswap inblock oublock msgsnd msgrcv nsigs nvcsw nivcsw" SCHED similar to "user pcpu pri usrpri nice psxpri psr policy pset" SFMT similar to "uid pid cursig sig sigmask sigignore sigcatch stat tname command" THREAD similar to "user pcpu pri scnt wchan usertime systime" UFMT similar to "uname pid pcpu pmem vsz rss tt state start time command" VFMT similar to "pid tt state time sl pagein vsz rss pcpu pmem command" keyword header description cp CP alias to cpu [43reno/tru64] if WITH_CURSIG is defined cursig CURSIG current signal [tru64] policy POL alias to class [tru64] pset PSET current processor set (not implemented yet) (hpux/solaris/tru64) psxpri alias to pri [tru64] scnt SCNT suspend count (not implmented yet) [tru64] sess SESS alias to sid [43reno/netbsd/openbsd/tru64] if WITH_CHILDTIMES is defined systime SYSTIME accumulated system CPU time [tru64] tsess TSESS alias to tsid [43reno/netbsd/openbsd/tru64] uname alias to user [43reno/tru64] if WITH_CHILDTIMES is defined usertime USRTIME accumulated user CPU time [tru64] if WITH_EXTRA_ALIASES is defined keyword header description cls alias to class [hpux] cmd CMD alias to command [tru64] dev alias to tdev [43reno/tru64] flag alias to f (43reno/irix/tru64) fname FNAME alias to ucomm [solaris] intpri alias to pri [hpux] ivcsw IVCSW alias to nicvsw [43reno/tru64] ktracep KTRACEP tracing vnode [43reno/netbsd/openbsd] longtname alias to tty [43reno/tru64] longtty alias to tty [43reno/tru64] maxrss alias to lim (43reno) opri alias to pri [solaris] osz alias to sz [solaris] p P alias to psr [irix] p_rss alias to rss [43reno/tru64] procaddr alias to paddr (43reno) resident alias to re (43reno) runame alias to ruser [43reno/tru64] scount alias to scnt [tru64] session alias to sess [tru64] size alias to sz [just for convenience] slp alias to sl [43reno/tru64] slptime alias to sl [43reno/tru64] tname alias to tt [43reno/tru64] traceflag alias to ktrace (43reno) tsession alias to tsess [43reno/tru64] if WITH_UMASK is defined u_cmask alias to umask [43reno/tru64] u_procp alias to uprocp [43reno/tru64] util alias to c [irix] vcsw VCSW alias to nvcsw [43reno/tru64] wname alias to mwchan [irix] if WITH_NOP_ALIASES is defined keyword header description emul EMUL name of system call emulation environment (keyword taken from openbsd) lid LID ID of the LWP [netbsd] lstate symbolic LWP state [netbsd] lwp LWP ID of the LWP [solaris] nlwp NLWP number of LWPs in the process [netbsd/solaris] p_ru P_RU resource usage pointer (valid only for zombie) [43reno/44bsd/netbsd/openbsd] poip POIP pageouts in progress (43reno) prmgrp PRMGRP [hpux] prmid PRMID [hpux] projid PROJID project ID of the process [solaris] project PROJECT project name of the process [solaris] rlink RLINK reverse link on run queue [43reno/44bsd/netbsd/openbsd] rlwp RLWP number of LWPs on a processor or run queue [netbsd] status STATUS process status [tru64] taskid TASKID task ID of the process [solaris] trs TRS text resident size [43reno] here is a detailled description of source level changes : *** fmt.c const char * prototypes added fmt_argv() say [xxx] instead of " (xxx)" if argv is empty as on netbsd. mostly for system processes and zombies. *** extern.h cflag -> commandonly now deleted -- replaced by gettime() vhead -> varlist fmtsep, posix and extended added class() added fmt_argv() w/ const char * prototype gettime() added gname()/s_gname() added groups() added groupnames() added lockname() sorted maxrss() #if defined(WITH_MAXRSS) nop() added parsefmt() w/ const char * prototype rgroupname() -> rgname() s_rgroupname() -> s_rgname() s_time() added stime() added svgname()/s_svgname() added svuname()/s_svuname() added upr() added if COMPAT_TRU64 is defined systime()/usertime() added *** keyword.c var array modified and reformated so values are almost always aligned the same way. new keywords are + prefixed, modified keywords are ! prefixed ! acflag 3 -> 4 (as xstat) + addr=ADDR -> paddr ! args 16 -> MAXCOMLEN (see sys/proc.h) + c=C -> cpu (alias w/ different header) + class=CLS -> class() ! comm=COMMAND -> ucomm (alias w/ different header) ! command 16 -> MAXCOMLEN (see sys/proc.h) + ctime=CTIME -> cldtime()/s_time() + dsiz -> kvar(ki_dsize) + egid=EGID -> gid + egroup=EGROUP -> group + euid=EUID -> uid + euser=EUSER -> user + gid=GID -> kvar(ki_groups[0]) + group=GROUP -> gname()/s_gname() + groupnames=GROUPNAMES -> groupnames() (no dynamic sizing for instance) + groups=GROUPS -> groups() (no dynamic sizing for instance) + holdcnt=HOLDCNT -> kval(ki_lock) ! inblk 4 -> 5 (medium counter) if WITH_MAXRSS is defined ! lim nop (still "-") -> maxrss (requires kernel patch) ! login MAXLOGNAME-1 -> USERLEN (macro to MAXLOGNAME-1) ! majflt 4 -> 7 (great counter) ! minflt 4 -> 7 (great counter) ! msgrcv 4 -> 7 (great counter) ! msgsnd 4 -> 7 (great counter) ! nice 2 -> 3 (-20 to 20) ! nsigs 4 -> 5 (medium counter) ! nswap 4 -> 5 (medium counter) ! oublk 4 -> 5 (medium counter) ! pagein 6 -> 7 (great counter) + psr=PSR -> kvar(ki_oncpu) ! rss 4 -> 5 (medium counter, as vsz) ! rtprio priorityr() is buggy (xxx:xxx instead of normal, etc) + rssize -> rsz + rsz=RSZ -> rss + s=S -> state (alias w/ different header) + ssiz -> kvar(ki_ssize) + stime=STIME -> stime() + svgroup=SVGROUP -> svgname()/s_svgname() + svuser=SVUSER -> svuname()/s_svuname() + sz=SZ -> vsz ! tdev 4 -> 6 (xxx,xxx) ! time 9 -> s_time() ! tpgid 4 -> PIDLEN ! tsiz 4 -> 5 (medium counter) ! tt 3 -> 2 (as the manual says and other systems does) if WITH_UMASK is defined + umask=UMASK -> kvar(ki_cmask) (requires a kernel patch) ! upr kvar(ki_pri.pri_user) -> upr() (scaled to PZERO) ! uprocp -> paddr (alias w/ different header) if COMPAT_TRU64 is defined + DFMT, F5FMT, FL5FMT, JFMT, L5FMT, LFMT, RUSAGE, SCHED, SFMT, THREAD, UFMT, VFMT multiple aliases + cp=CP -> cpu (alias w/ different header) if WITH_CURSIG is defined + cursig=CURSIG -> nop (requires kernel patch) + policy=POL -> psr (alias w/ different header) + pset=PSET -> nop (not implementable right now) + psxpri=PRI -> pri (alias w/ different header) + scnt=SCNT -> nop () + sess=SESS -> sid (alias w/ different header) if WITH_CHILDTIMES is defined + systime=SYSTIME -> systime()/s_time() (requires kernel patch) + tsess=TSESS -> tsid (alias w/ different header) + uname -> user (alias) if WITH_CHILDTIMES is defined + usertime=USRTIME -> usertime()/s_time() (requires kernel patch) parsefmt() converted to sys/queue.h and rewritten to handle the new "internal format string" format which looks like : keyword[:flags in reverse polish notation][=header] reverse polish notation: x by itself, * means any, x! means not x, xy| means x | y, xy& means x & y, xy^ means x ^ y. [xyz] is a more compact and readable form of xy|z| [a-z] isn't supported and is understood as a-|z| () may be used for readability but are no-op. bsd flags: d12jlsuv, bsd modifiers: nZ posix flags: d12fjl, posix modifiers: cMPy both modifiers: X if extended. this avoid duplicating similar format strings and on/off options. also, blank separator is ignored if the format string contains "=". findvar() rewritten to handle header specification on aliases. now, you may say "ps -O acflg=XXX" as well as "ps -O acfalg=XXX" initially, these functions have been rewritten to avoid duplicated keywords. this has been fixed, since. also, prototypes are now const char *. *** print.c static function prototypes added. printheader() converted to sys/queue.h arguments() converted to sys/queue.h, zombies are marked . if posix, left is forced to 80. command() same as arguments(). cflag -> commandonly. ucomm() zombies are marked . also, don't pad if last field. state() 'O' (onproc) state added depending on ki_oncpu != NOCPU, I'm right ? '>', 'K' and 'a' sub-states added pri() scalepri() macro added upr() added, same as pri(). rgroupname()/s_rgroupname() -> rgname()/s_rgname() to match netbsd/openbsd gname()/s_gname() added to handle the effective group-id. groups() added to handle additional group-ids. groupnames() added to handle additional group-names. svgname()/s_svgname() added to handle the saved group-id. svuname()/s_svuname() added to handle the saved user-id. gettime() added in place of the now global variable. started()/lstarted() gcc -Wall do not like %y, %b and %c ! stime() added vsize() converted to printsize() (see below) printtime()/s_time() added to handle posix vs. no posix times cputime() converted to printtime() cldtime() added elapsed() converted to printtime() nop() added maxrss() handled using kernel mods priorityr() debuggyfied :) PRI_ITHD added class() added, based on priorityr(). printsize() added to handle tru64 size output format (see -Q above) $ ps -u root 96687 0.0 0.8 1528 980 p1 OL+ 7:13PM 0:00.05 ps -u $ ps -Qu root 91419 0.0 0.8 1.49M 980K p1 OL+ 7:13PM 0:00.05 ps -XXu $ ps -qlcy OL+ 0 1328 16850 208 96 980 284 - ttyp1 00:00:00 ps $ ps -Qqcly OL+ 0 76337 16850 208 96 980K 284K - ttyp1 00:00:00 ps printval() PGTOK converted to printsize() label() '-' instead of nothing #if defined(COMPAT_TRU64) systime() added, printtime() of ki_rusage.ru_stime usertime() added, printtime() of ki_rusage.ru_utime summing children systime and usertime requires kernel mods... many some functions buf[x] -> buf[BUFLEN] *** ps.h stddef.h and sys/queue.h added LTIMELEN/STIMELEN/UIDLEN/USERLEN/KOFF added VARENT converted to sys/queue.h *** ps.c many variables made static and many other variables made local varlist converted to sys/queue.h struct list added. xkeep/xkeep_implied (was xflg) -> noctty leader added optfatal -> fatal function prototypes modified: fmt() -> format() kvm_t * parameter added saveuser() kvm_t * parameter added *fmt deleted getopt loop moved from main() to parseopts() sets posix if getenv("POSIX") is != 0 (this is #defined) or if the command name contains a 'x'. sets extended is the command name begins with an 's'. nokudge made differently (maybe the old way, don't remember) nselectors / noctty / leader / all / nkept / are handled differently. the "new" process selection code has been integrated (or instead of and) and moved to findproc() to avoid goto's. findproc() added, processes w/ a controlling terminal are selected using KI_CTTY (this is #defined). parseopts() added. getuids -> parselst() to handle list parameters. addelem_*/*_list deleted (sorry, the new code conflicts with mine). find_varentry()/scanvars()/dynsizevars()/sizevars() converted to sys/queue.h fmt() -> format() w/ kvm_t * as parameter saveuser() w/ kvm_t * as parameter, system processes are marked [] needcomm/needenv && UREADOK() fallback added pscomp() shortened... kludge_oldps_options() posix disabled if no - usage() updated. >How-To-Repeat: n/a >Fix: Index: Makefile =================================================================== RCS file: /home/ncvs/src/bin/ps/Makefile,v retrieving revision 1.25 diff -u -I$Id.*$ -I$.+BSD.*$ -r1.25 Makefile --- Makefile 23 Feb 2004 20:05:14 -0000 1.25 +++ Makefile 20 Apr 2004 01:57:26 -0000 @@ -3,6 +3,8 @@ PROG= ps SRCS= fmt.c keyword.c nlist.c print.c ps.c +LINKS= ${BINDIR}/ps ${BINDIR}/xps \ + ${BINDIR}/ps ${BINDIR}/sps WARNS?= 5 # # To support "lazy" ps for non root/wheel users @@ -11,6 +13,57 @@ # on large systems. # CFLAGS+=-DLAZY_PS +# +# To display some runtime debugging informations. +# +.if defined(DEBUG_FLAGS) +CFLAGS+=-DDEBUG +.endif +# +# enable posix mode through POSIX environment variable. +# +#CFLAGS+= -DWITH_POSIX_ENV +# +# select processes with a controlling terminals using +# KI_CTTY instead of P_CONTROLT. +# +CFLAGS+= -DWITH_CTTY +# +# kernel supports ki_childstime and ki_childstime instead of ki_childtime. +# (tru64 systime and usertime keywords). +# +CFLAGS+= -DWITH_CHILDTIMES +# +# kernel supports ki_cursig (cursig keyword). +# +CFLAGS+= -DWITH_CURSIG +# +# kernel supports ki_maxrss (lim keyword). +# +CFLAGS+= -DWITH_MAXRSS +# +# kernel supports ki_cmask (umask keyword). +# +CFLAGS+= -DWITH_UMASK +# +# enable tru64 keywords (*FMT, cp, cursig, policy, pset, psxpri, +# scnt, sess, systime, tname, tsess, uname, usertime). +# +CFLAGS+= -DCOMPAT_TRU64 +# +# enable yet more keywords (cls, cmd, dev, flag, fname, intpri, +# ivcsw, ktracep, longtname, longtty, maxrss, opri, osz, p, p_rss, +# procaddr, resident, runame, scount, session, size, slp, slptime, +# traceflag, tsession, u_cmask, u_procp, util, vcsw, wname). +# +#CFLAGS+= -DWITH_EXTRA_ALIASES +# +# enable no-op keywords, aka not implemented yet. +# (emul, lid, lstate, lwp, nlwp, p_ru, poip, prmgrp, prmid, +# projid, project, rlink, rlwp, status, taskid, trs). +# +#CFLAGS+= -DWITH_NOP_ALIASES +# DPADD= ${LIBM} ${LIBKVM} LDADD= -lm -lkvm #BINGRP= kmem Index: extern.h =================================================================== RCS file: /home/ncvs/src/bin/ps/extern.h,v retrieving revision 1.32 diff -u -I$Id.*$ -I$.+BSD.*$ -r1.32 extern.h --- extern.h 6 Apr 2004 20:06:49 -0000 1.32 +++ extern.h 19 Apr 2004 22:15:39 -0000 @@ -27,60 +27,81 @@ * SUCH DAMAGE. * * @(#)extern.h 8.3 (Berkeley) 4/2/94 - * $FreeBSD$ + * $FreeBSD: src/bin/ps/extern.h,v 1.9 1999/08/27 23:14:50 peter Exp $ */ -struct kinfo; -struct nlist; -struct var; -struct varent; - extern fixpt_t ccpu; -extern int cflag, eval, fscale, nlistread, rawcpu; +extern int commandonly, eval, fscale, nlistread, rawcpu; extern unsigned long mempages; -extern time_t now; extern int sumrusage, termwidth, totwidth; -extern VARENT *vhead; +extern VARLIST varlist; +extern const char fmtsep[]; + +extern int posix; +extern int extended; __BEGIN_DECLS void arguments(KINFO *, VARENT *); +void class(KINFO *, VARENT *); void command(KINFO *, VARENT *); void cputime(KINFO *, VARENT *); +void cldtime(KINFO *, VARENT *); int donlist(void); void elapsed(KINFO *, VARENT *); VARENT *find_varentry(VAR *); -const char *fmt_argv(char **, char *, size_t); +const char *fmt_argv(const char **, const char *, size_t); double getpcpu(const KINFO *); +void gettime(time_t *, struct timeval *); +void gname(KINFO *, VARENT *); +void groups(KINFO *, VARENT *); +void groupnames(KINFO *, VARENT *); void kvar(KINFO *, VARENT *); void label(KINFO *, VARENT *); +void lockname(KINFO *, VARENT *); void logname(KINFO *, VARENT *); void longtname(KINFO *, VARENT *); void lstarted(KINFO *, VARENT *); +#if defined(WITH_MAXRSS) void maxrss(KINFO *, VARENT *); -void lockname(KINFO *, VARENT *); +#endif void mwchan(KINFO *, VARENT *); +void nop(KINFO *, VARENT *); void nwchan(KINFO *, VARENT *); void pagein(KINFO *, VARENT *); -void parsefmt(const char *, int); +void parsefmt(const char *, const char *); void pcpu(KINFO *, VARENT *); void pmem(KINFO *, VARENT *); void pri(KINFO *, VARENT *); void printheader(void); void priorityr(KINFO *, VARENT *); -void rgroupname(KINFO *, VARENT *); +void rgname(KINFO *, VARENT *); void runame(KINFO *, VARENT *); void rvar(KINFO *, VARENT *); +int s_gname(KINFO *); int s_label(KINFO *); -int s_rgroupname(KINFO *); +int s_rgname(KINFO *); int s_runame(KINFO *); +int s_svgname(KINFO *); +int s_svuname(KINFO *); +int s_time(KINFO *); int s_uname(KINFO *); void showkey(void); void started(KINFO *, VARENT *); void state(KINFO *, VARENT *); +void stime(KINFO *, VARENT *); +void svuname(KINFO *, VARENT *); +void svgname(KINFO *, VARENT *); +#if defined(COMPAT_TRU64) +void systime(KINFO *, VARENT *); +#endif void tdev(KINFO *, VARENT *); void tname(KINFO *, VARENT *); void ucomm(KINFO *, VARENT *); void uname(KINFO *, VARENT *); +void upr(KINFO *, VARENT *); +#if defined(COMPAT_TRU64) +void usertime(KINFO *, VARENT *); +#endif void vsize(KINFO *, VARENT *); void wchan(KINFO *, VARENT *); __END_DECLS Index: fmt.c =================================================================== RCS file: /home/ncvs/src/bin/ps/fmt.c,v retrieving revision 1.30 diff -u -I$Id.*$ -I$.+BSD.*$ -r1.30 fmt.c --- fmt.c 6 Apr 2004 20:06:49 -0000 1.30 +++ fmt.c 17 Apr 2004 20:26:57 -0000 @@ -51,16 +51,17 @@ #include "ps.h" -static char *cmdpart(char *); -static char *shquote(char **); +static const char *cmdpart(const char *); +static const char *shquote(const char **); -static char * -shquote(char **argv) +static const char * +shquote(const char **argv) { long arg_max; static size_t buf_size; size_t len; - char **p, *dst, *src; + char *dst; + const char **p, *src; static char *buf = NULL; if (buf == NULL) { @@ -72,7 +73,6 @@ if ((buf = malloc(buf_size)) == NULL) errx(1, "malloc failed"); } - if (*argv == NULL) { buf[0] = '\0'; return (buf); @@ -96,19 +96,20 @@ return (buf); } -static char * -cmdpart(char *arg0) +static const char * +cmdpart(const char *arg0) { - char *cp; + const char *cp; return ((cp = strrchr(arg0, '/')) != NULL ? cp + 1 : arg0); } const char * -fmt_argv(char **argv, char *cmd, size_t maxlen) +fmt_argv(const char **argv, const char *cmd, size_t maxlen) { size_t len; - char *ap, *cp; + const char *ap; + char *cp; if (argv == NULL || argv[0] == NULL) { if (cmd == NULL) @@ -123,9 +124,9 @@ if (cp == NULL) errx(1, "malloc failed"); if (ap == NULL) - sprintf(cp, " (%.*s)", (int)maxlen, cmd); + (void)sprintf(cp, "[%.*s]", (int)maxlen, cmd); else if (strncmp(cmdpart(argv[0]), cmd, maxlen) != 0) - sprintf(cp, "%s (%.*s)", ap, (int)maxlen, cmd); + (void)sprintf(cp, "%s (%.*s)", ap, (int)maxlen, cmd); else (void) strcpy(cp, ap); return (cp); Index: keyword.c =================================================================== RCS file: /home/ncvs/src/bin/ps/keyword.c,v retrieving revision 1.66 diff -u -I$Id.*$ -I$.+BSD.*$ -r1.66 keyword.c --- keyword.c 6 Apr 2004 20:06:49 -0000 1.66 +++ keyword.c 20 Apr 2004 00:34:28 -0000 @@ -43,162 +43,391 @@ #include #include -#include #include #include #include #include "ps.h" -static VAR *findvar(char *, int, char **header); +static int parseexpr(char *, const char *); +static void parsefmt1(char *, const char *, const char *); +static VAR *findvar(const char *, const char *); static int vcmp(const void *, const void *); +const char fmtsep[] = " ,\t\n"; /* separators is this order precisely */ + /* Compute offset in common structures. */ -#define KOFF(x) offsetof(struct kinfo_proc, x) +/* KOFF() has been moved to ps.h */ #define ROFF(x) offsetof(struct rusage, x) -#define UIDFMT "u" -#define UIDLEN 5 -#define PIDFMT "d" -#define PIDLEN 5 -#define USERLEN (MAXLOGNAME - 1) +#define CLSLEN 6 /* class */ +#define LCNTFMT "ld" /* rvar */ +#define LCNTLEN 7 /* majflt, minflt, msgrcv, msgsnd */ +#define SCNTFMT "ld" /* rvar */ +#define SCNTLEN 5 /* inblk, nivcsw, nsigs, nswap, nvcsw, oublk, + pagein, scnt */ +#undef COMMLEN +#define COMMLEN MAXCOMLEN /* args, command, ucomm */ +#define DATELEN 28 /* lstart */ +#define DEVLEN 6 /* tdev */ +#define EMULLEN 7 /* emul */ +#define FLAGFMT "x" /* kvar */ +#define FLAGLEN 7 /* f */ +#define GRPLEN USERLEN /* group, rgroup, svgroup */ +#define KTRCFMT "x" /* kvar */ +#define KTRCLEN 8 /* ktrace */ +#define KPTRFMT "lx" /* kvar */ +#define KPTRLEN 8 /* paddr, nwchan */ +#define LABLLEN SHRT_MAX /* label */ +#define LOCKLEN 6 /* lockname */ +#define PRIOFMT "d" /* kvar */ +#define PRIOLEN 3 /* cpu, intpri, jobc, nice, opri, pri, upr, + pset, psr */ +#define RTPRLEN 7 /* rtprio */ +#define PERCLEN 4 /* %cpu, %mem */ +#define PIDFMT "d" /* kvar */ +#define PIDLEN 5 /* pgid, pid, ppid, sid, tpgid, tsid */ +#define K127FMT "d" /* kvar */ +#define K127LEN 3 /* re, sl */ +#define SHEXFMT "x" /* kvar */ +#define SHEXLEN 4 /* acflag, xstat */ +#define SIGFMT "x" /* kvar */ +#define SIGLEN 8 /* cursig, sig, sigcatch, sigignore, sigmask */ +#define LSZFMT "ld" /* kvar, rvar */ +#define LSZLEN 5 /* lim, rss, vsz */ +#define SSZFMT "ld" /* kvar, rvar */ +#define SSZLEN 4 /* dsiz, ssiz, sz, tsiz */ +#define STATLEN 4 /* state */ +#define STRTLEN 7 /* start */ +/* LTIMELEN has been moved to ps.h : etime */ +/* STIMELEN has been moved to ps.h : stime */ +#define TIMELEN LTIMELEN /* ctime, systime, time, usertime */ +#define LTTYLEN 10 /* tty */ +#define STTYLEN 3 /* tt */ +#define UIDFMT "u" /* kvar */ +/* UIDLEN has been moved to ps.h : gid, rgid, ruid, svgid, svuid, uid */ +#define UMSKFMT "#o" +#define UMSKLEN 4 +/* USERLEN has been moved to ps.h : login, ruser, user, svuser */ +#define WCHNLEN 6 /* mwchan */ + +#define ALIAS(n, h, a) \ + { n, h, a, 0, NULL, NULL, 0, 0, CHAR, NULL, 0 } +#define K127(n, h, s, t, lf) \ + { n, h, NULL, INF127, kvar, NULL, lf##LEN, KOFF(s), t, lf##FMT, 0 } +#define KVAR(n, h, s, t, lf) \ + { n, h, NULL, 0, kvar, NULL, lf##LEN, KOFF(s), t, lf##FMT, 0 } +#define RVAR(n, h, s, t, lf) \ + { n, h, NULL, 0, rvar, NULL, lf##LEN, ROFF(s), t, lf##FMT, 0 } +#define SIZE(n, h, fl, fn, sz, ln) \ + { n, h, NULL, fl|DSIZ, fn, sz, ln##LEN, 0, CHAR, NULL, 0 } +#define CALL(n, h, fl, fn, ln) \ + { n, h, NULL, fl, fn, NULL, ln##LEN, 0, CHAR, NULL, 0 } + +#define PID(n, h, s) KVAR(n, h, s, UINT, PID) +#define SIG(n, h, s) KVAR(n, h, s, UINT, SIG) +#define UID(n, h, s) KVAR(n, h, s, UINT, UID) +#define GID(n, h, s) UID(n, h, s) /* PLEASE KEEP THE TABLE BELOW SORTED ALPHABETICALLY!!! */ -static VAR var[] = { - {"%cpu", "%CPU", NULL, 0, pcpu, NULL, 4, 0, CHAR, NULL, 0}, - {"%mem", "%MEM", NULL, 0, pmem, NULL, 4, 0, CHAR, NULL, 0}, - {"acflag", "ACFLG", NULL, 0, kvar, NULL, 3, KOFF(ki_acflag), USHORT, - "x", 0}, - {"acflg", "", "acflag", 0, NULL, NULL, 0, 0, CHAR, NULL, 0}, - {"args", "COMMAND", NULL, COMM|LJUST|USER, arguments, NULL, 16, 0, - CHAR, NULL, 0}, - {"blocked", "", "sigmask", 0, NULL, NULL, 0, 0, CHAR, NULL, 0}, - {"caught", "", "sigcatch", 0, NULL, NULL, 0, 0, CHAR, NULL, 0}, - {"comm", "COMMAND", NULL, LJUST, ucomm, NULL, MAXCOMLEN, 0, CHAR, - NULL, 0}, - {"command", "COMMAND", NULL, COMM|LJUST|USER, command, NULL, 16, 0, - CHAR, NULL, 0}, - {"cpu", "CPU", NULL, 0, kvar, NULL, 3, KOFF(ki_estcpu), UINT, "d", - 0}, - {"cputime", "", "time", 0, NULL, NULL, 0, 0, CHAR, NULL, 0}, - {"etime", "ELAPSED", NULL, USER, elapsed, NULL, 12, 0, CHAR, NULL, 0}, - {"f", "F", NULL, 0, kvar, NULL, 7, KOFF(ki_flag), INT, "x", 0}, - {"flags", "", "f", 0, NULL, NULL, 0, 0, CHAR, NULL, 0}, - {"ignored", "", "sigignore", 0, NULL, NULL, 0, 0, CHAR, NULL, 0}, - {"inblk", "INBLK", NULL, USER, rvar, NULL, 4, ROFF(ru_inblock), LONG, - "ld", 0}, - {"inblock", "", "inblk", 0, NULL, NULL, 0, 0, CHAR, NULL, 0}, - {"jobc", "JOBC", NULL, 0, kvar, NULL, 4, KOFF(ki_jobc), SHORT, "d", - 0}, - {"ktrace", "KTRACE", NULL, 0, kvar, NULL, 8, KOFF(ki_traceflag), INT, - "x", 0}, - {"label", "LABEL", NULL, LJUST|DSIZ, label, s_label, SHRT_MAX, 0, CHAR, - NULL, 0}, - {"lim", "LIM", NULL, 0, maxrss, NULL, 5, 0, CHAR, NULL, 0}, - {"lockname", "LOCK", NULL, LJUST, lockname, NULL, 6, 0, CHAR, NULL, - 0}, - {"login", "LOGIN", NULL, LJUST, logname, NULL, MAXLOGNAME-1, 0, CHAR, - NULL, 0}, - {"logname", "", "login", 0, NULL, NULL, 0, 0, CHAR, NULL, 0}, - {"lstart", "STARTED", NULL, LJUST|USER, lstarted, NULL, 28, 0, CHAR, - NULL, 0}, - {"majflt", "MAJFLT", NULL, USER, rvar, NULL, 4, ROFF(ru_majflt), - LONG, "ld", 0}, - {"minflt", "MINFLT", NULL, USER, rvar, NULL, 4, ROFF(ru_minflt), - LONG, "ld", 0}, - {"msgrcv", "MSGRCV", NULL, USER, rvar, NULL, 4, ROFF(ru_msgrcv), - LONG, "ld", 0}, - {"msgsnd", "MSGSND", NULL, USER, rvar, NULL, 4, ROFF(ru_msgsnd), - LONG, "ld", 0}, - {"mwchan", "MWCHAN", NULL, LJUST, mwchan, NULL, 6, 0, CHAR, NULL, 0}, - {"ni", "", "nice", 0, NULL, NULL, 0, 0, CHAR, NULL, 0}, - {"nice", "NI", NULL, 0, kvar, NULL, 2, KOFF(ki_nice), CHAR, "d", - 0}, - {"nivcsw", "NIVCSW", NULL, USER, rvar, NULL, 5, ROFF(ru_nivcsw), - LONG, "ld", 0}, - {"nsignals", "", "nsigs", 0, NULL, NULL, 0, 0, CHAR, NULL, 0}, - {"nsigs", "NSIGS", NULL, USER, rvar, NULL, 4, ROFF(ru_nsignals), - LONG, "ld", 0}, - {"nswap", "NSWAP", NULL, USER, rvar, NULL, 4, ROFF(ru_nswap), - LONG, "ld", 0}, - {"nvcsw", "NVCSW", NULL, USER, rvar, NULL, 5, ROFF(ru_nvcsw), - LONG, "ld", 0}, - {"nwchan", "NWCHAN", NULL, LJUST, nwchan, NULL, 8, 0, CHAR, NULL, 0}, - {"oublk", "OUBLK", NULL, USER, rvar, NULL, 4, ROFF(ru_oublock), - LONG, "ld", 0}, - {"oublock", "", "oublk", 0, NULL, NULL, 0, 0, CHAR, NULL, 0}, - {"paddr", "PADDR", NULL, 0, kvar, NULL, 8, KOFF(ki_paddr), KPTR, - "lx", 0}, - {"pagein", "PAGEIN", NULL, USER, pagein, NULL, 6, 0, CHAR, NULL, 0}, - {"pcpu", "", "%cpu", 0, NULL, NULL, 0, 0, CHAR, NULL, 0}, - {"pending", "", "sig", 0, NULL, NULL, 0, 0, CHAR, NULL, 0}, - {"pgid", "PGID", NULL, 0, kvar, NULL, PIDLEN, KOFF(ki_pgid), UINT, - PIDFMT, 0}, - {"pid", "PID", NULL, 0, kvar, NULL, PIDLEN, KOFF(ki_pid), UINT, - PIDFMT, 0}, - {"pmem", "", "%mem", 0, NULL, NULL, 0, 0, CHAR, NULL, 0}, - {"ppid", "PPID", NULL, 0, kvar, NULL, PIDLEN, KOFF(ki_ppid), UINT, - PIDFMT, 0}, - {"pri", "PRI", NULL, 0, pri, NULL, 3, 0, CHAR, NULL, 0}, - {"re", "RE", NULL, INF127, kvar, NULL, 3, KOFF(ki_swtime), UINT, "d", - 0}, - {"rgid", "RGID", NULL, 0, kvar, NULL, UIDLEN, KOFF(ki_rgid), - UINT, UIDFMT, 0}, - {"rgroup", "RGROUP", NULL, LJUST|DSIZ, rgroupname, s_rgroupname, - USERLEN, 0, CHAR, NULL, 0}, - {"rss", "RSS", NULL, 0, kvar, NULL, 4, KOFF(ki_rssize), PGTOK, "ld", 0}, - {"rtprio", "RTPRIO", NULL, 0, priorityr, NULL, 7, KOFF(ki_pri), CHAR, - NULL, 0}, - {"ruid", "RUID", NULL, 0, kvar, NULL, UIDLEN, KOFF(ki_ruid), - UINT, UIDFMT, 0}, - {"ruser", "RUSER", NULL, LJUST|DSIZ, runame, s_runame, USERLEN, - 0, CHAR, NULL, 0}, - {"sid", "SID", NULL, 0, kvar, NULL, PIDLEN, KOFF(ki_sid), UINT, - PIDFMT, 0}, - {"sig", "PENDING", NULL, 0, kvar, NULL, 8, KOFF(ki_siglist), INT, - "x", 0}, - {"sigcatch", "CAUGHT", NULL, 0, kvar, NULL, 8, KOFF(ki_sigcatch), - UINT, "x", 0}, - {"sigignore", "IGNORED", NULL, 0, kvar, NULL, 8, KOFF(ki_sigignore), - UINT, "x", 0}, - {"sigmask", "BLOCKED", NULL, 0, kvar, NULL, 8, KOFF(ki_sigmask), - UINT, "x", 0}, - {"sl", "SL", NULL, INF127, kvar, NULL, 3, KOFF(ki_slptime), UINT, "d", - 0}, - {"start", "STARTED", NULL, LJUST|USER, started, NULL, 7, 0, CHAR, NULL, - 0}, - {"stat", "", "state", 0, NULL, NULL, 0, 0, CHAR, NULL, 0}, - {"state", "STAT", NULL, 0, state, NULL, 4, 0, CHAR, NULL, 0}, - {"svgid", "SVGID", NULL, 0, kvar, NULL, UIDLEN, KOFF(ki_svgid), - UINT, UIDFMT, 0}, - {"svuid", "SVUID", NULL, 0, kvar, NULL, UIDLEN, KOFF(ki_svuid), - UINT, UIDFMT, 0}, - {"tdev", "TDEV", NULL, 0, tdev, NULL, 4, 0, CHAR, NULL, 0}, - {"time", "TIME", NULL, USER, cputime, NULL, 9, 0, CHAR, NULL, 0}, - {"tpgid", "TPGID", NULL, 0, kvar, NULL, 4, KOFF(ki_tpgid), UINT, - PIDFMT, 0}, - {"tsid", "TSID", NULL, 0, kvar, NULL, PIDLEN, KOFF(ki_tsid), UINT, - PIDFMT, 0}, - {"tsiz", "TSIZ", NULL, 0, kvar, NULL, 4, KOFF(ki_tsize), PGTOK, "ld", 0}, - {"tt", "TT ", NULL, 0, tname, NULL, 4, 0, CHAR, NULL, 0}, - {"tty", "TTY", NULL, LJUST, longtname, NULL, 8, 0, CHAR, NULL, 0}, - {"ucomm", "UCOMM", NULL, LJUST, ucomm, NULL, MAXCOMLEN, 0, CHAR, NULL, - 0}, - {"uid", "UID", NULL, 0, kvar, NULL, UIDLEN, KOFF(ki_uid), UINT, - UIDFMT, 0}, - {"upr", "UPR", NULL, 0, kvar, NULL, 3, KOFF(ki_pri.pri_user), UCHAR, - "d", 0}, - {"uprocp", "UPROCP", NULL, 0, kvar, NULL, 8, KOFF(ki_paddr), KPTR, - "lx", 0}, - {"user", "USER", NULL, LJUST|DSIZ, uname, s_uname, USERLEN, 0, CHAR, - NULL, 0}, - {"usrpri", "", "upr", 0, NULL, NULL, 0, 0, CHAR, NULL, 0}, - {"vsize", "", "vsz", 0, NULL, NULL, 0, 0, CHAR, NULL, 0}, - {"vsz", "VSZ", NULL, 0, vsize, NULL, 5, 0, CHAR, NULL, 0}, - {"wchan", "WCHAN", NULL, LJUST, wchan, NULL, 6, 0, CHAR, NULL, 0}, - {"xstat", "XSTAT", NULL, 0, kvar, NULL, 4, KOFF(ki_xstat), USHORT, - "x", 0}, - {"", NULL, NULL, 0, NULL, NULL, 0, 0, CHAR, NULL, 0}, +/* _:required #:optional +:add-on */ +/* 3:bsd43reno 4:bsd44lite2 f:freebsd n:netbsd o:openbsd */ +/* x:susv3 h:hpux i:irix s:solaris t:tru64 */ +VAR var[] = { +/* 34fnoxhist*/ +/*_34fno t*/ CALL("%cpu", "%CPU", 0, pcpu, PERC), +/*_34fno t*/ CALL("%mem", "%MEM", 0, pmem, PERC), +#if defined(COMPAT_TRU64) +/*-3 t*/ ALIAS("DFMT", NULL, "pid tname state cputime command"), +/*- t*/ ALIAS("F5FMT", NULL, + "uname pid ppid pcpu start tt time command"), +/*- t*/ ALIAS("FL5FMT", NULL, + "f state uid pid ppid pcpu pri nice rss wchan start time command"), +/*-3 t*/ ALIAS("JFMT", NULL, + "user pid ppid pgid sess jobc state tname cputime command"), +/*- t*/ ALIAS("L5FMT", NULL, + "f state uid pid ppid pcpu pri nice rss wchan tt time ucomm"), +/*-3 t*/ ALIAS("LFMT", NULL, + "uid pid ppid cp pri nice vsz rss wchan state tname cputime command"), +/*- t NUMA="user psr pset RAD NSG s tty time command"*/ +/*-3 t*/ ALIAS("RUSAGE", NULL, + "minflt majflt nswap inblock oublock msgsnd msgrcv nsigs nvcsw nivcsw"), +/*- t*/ ALIAS("SCHED", NULL, + "user pcpu pri usrpri nice psxpri psr policy pset"), +/*-3 t*/ ALIAS("SFMT", NULL, + "uid pid cursig sig sigmask sigignore sigcatch stat tname command"), +/*- t*/ ALIAS("THREAD", NULL, + "user pcpu pri scnt wchan usertime systime"), +/*-3 t*/ ALIAS("UFMT", NULL, + "uname pid pcpu pmem vsz rss tt state start time command"), +/*-3 t*/ ALIAS("VFMT", NULL, + "pid tt state time sl pagein vsz rss pcpu pmem command"), +#endif +/* 34fno t*/ KVAR("acflag", "ACFLG", ki_acflag, USHORT, SHEX), +/* 34fno t*/ ALIAS("acflg", NULL, "acflag"), +/*_ xh st*/ ALIAS("addr", "ADDR", "paddr"), +/*_ f oxhist*/ CALL("args", "COMMAND", COMM|LJUST|USER, arguments, COMM), +/* 34fno t*/ ALIAS("blocked", NULL, "sigmask"), +/*_ x st*/ ALIAS("c", "C", "cpu"), +/* 34fno t*/ ALIAS("caught", NULL, "sigcatch"), +/*_ is */ CALL("class", "CLS", LJUST, class, CLS), +#if defined(WITH_EXTRA_ALIASES) +/*# h */ ALIAS("cls", NULL, "class"), +/*# t*/ ALIAS("cmd", "CMD", "command"), +#endif +/*_ f oxhist*/ ALIAS("comm", "COMMAND", "ucomm"), +/*_34fno t*/ CALL("command", "COMMAND", COMM|LJUST|USER, command, COMM), +#if defined(COMPAT_TRU64) +/*-3 t*/ ALIAS("cp", "CP", "cpu"), +#endif +/*_34fno hi t*/ KVAR("cpu", "CPU", ki_estcpu, UINT, PRIO), +/*34 fno t*/ ALIAS("cputime", NULL, "time"), +/*+ n */ SIZE("ctime", "CTIME", USER, cldtime, s_time, TIME), +#if defined(COMPAT_TRU64) +#if defined(WITH_CURSIG) +/*- t*/ SIG("cursig", "CURSIG", ki_cursig), +#else +/*- t*/ CALL("cursig", "CURSIG", 0, nop, SIG), /* XXX nop */ +#endif +#endif +#if defined(WITH_EXTRA_ALIASES) +/*#3 t*/ ALIAS("dev", NULL, "tdev"), +#endif +/*+ o */ KVAR("dsiz", "DSIZ", ki_dsize, PGTOK, SSZ), +#if defined(WITH_NOP_ALIASES) +/*# o */ CALL("emul", "EMUL", 0, nop, EMUL), /* XXX nop */ +#endif +/*+ no */ ALIAS("egid", "EGID", "gid"), +/*+ no */ ALIAS("egroup", "EGROUP", "group"), +/* f oxhist*/ CALL("etime", "ELAPSED", USER, elapsed, LTIME), +/*+ no */ ALIAS("euid", "EUID", "uid"), +/*+ no */ ALIAS("euser", "EUSER", "user"), +/*_34fnox st*/ KVAR("f", "F", ki_flag, INT, FLAG), +#if defined(WITH_EXTRA_ALIASES) +/*#3 i t*/ ALIAS("flag", NULL, "f"), +#endif +/* 4fno h */ ALIAS("flags", NULL, "f"), +#if defined(WITH_EXTRA_ALIASES) +/*# s */ ALIAS("fname", NULL, "ucomm"), +#endif +/*+ no h s */ GID("gid", "GID", ki_groups), +/*+ oxhist*/ SIZE("group", "GROUP", LJUST, gname, s_gname, GRP), +/*+ no */ CALL("groupnames", "GROUPNAMES", LJUST, groupnames, GRP), +/*+ no */ CALL("groups", "GROUPS", LJUST, groups, GRP), +/*+ no */ KVAR("holdcnt", "HOLDCNT", ki_lock, CHAR, SCNT), +/* 34fno t*/ ALIAS("ignored", NULL, "sigignore"), +/* 34fno t*/ RVAR("inblk", "INBLK", ru_inblock, LONG, SCNT), +/* 34fno t*/ ALIAS("inblock", NULL, "inblk"), +#if defined(WITH_EXTRA_ALIASES) +/*# h */ ALIAS("intpri","PRI","pri"), +/*#3 t*/ ALIAS("ivcsw","IVCSW","nivcsw"), +#endif +/*_34fno t*/ KVAR("jobc", "JOBC", ki_jobc, SHORT, PRIO), +/* 34fno */ KVAR("ktrace", "KTRACE", ki_traceflag, INT, KTRC), +#if defined(WITH_EXTRA_ALIASES) +/*#3 no */ KVAR("ktracep", "KTRACEP", ki_tracep, KPTR, KPTR), +#endif +/*_ f i */ SIZE("label", "LABEL", LJUST, label, s_label, LABL), +#if defined(WITH_NOP_ALIASES) +/*# n */ CALL("lid", "LID", 0, nop, PID), /* XXX nop */ +#endif +#if defined(WITH_MAXRSS) +/*_34fno */ CALL("lim", "LIM", 0, maxrss, LSZ), +#else +/*_34fno */ CALL("lim", "LIM", 0, nop, LSZ), /* XXX nop */ +#endif +/* f */ CALL("lockname", "LOCK", LJUST, lockname, LOCK), +/* 34fno */ CALL("login", "LOGIN", LJUST, logname, USER), +/* 34fno t*/ ALIAS("logname", NULL, "login"), +#if defined(WITH_EXTRA_ALIASES) +/*#3 t*/ ALIAS("longtname", NULL, "tty"), +/*#3 t*/ ALIAS("longtty", NULL, "tty"), +#endif +/* 34fno t*/ CALL("lstart", "STARTED", LJUST|USER, lstarted, DATE), +#if defined(WITH_NOP_ALIASES) +/*# n */ CALL("lstate", "LSTATE", 0, nop, STAT), /* XXX nop */ +/*# s */ CALL("lwp", "LWP", 0, nop, PID), /* XXX nop */ +#endif +/* 34fno t*/ RVAR("majflt", "MAJFLT", ru_majflt, LONG, LCNT), +#if defined(WITH_EXTRA_ALIASES) +/*#3 */ ALIAS("maxrss", NULL, "lim"), +#endif +/* 34fno t*/ RVAR("minflt", "MINFLT", ru_minflt, LONG, LCNT), +/* 34fno t*/ RVAR("msgrcv", "MSGRCV", ru_msgrcv, LONG, LCNT), +/* 34fno t*/ RVAR("msgsnd", "MSGSND", ru_msgsnd, LONG, LCNT), +/*_ f */ CALL("mwchan", "MWCHAN", LJUST, mwchan, WCHN), +/* 34fno t*/ ALIAS("ni", NULL, "nice"), +/*_34fnoxhist*/ KVAR("nice", "NI", ki_nice, CHAR, PRIO), +#if defined(WITH_NOP_ALIASES) +/*# n s */ CALL("nlwp", "NLWP", 0, nop, PID), /* XXX nop */ +#endif +/* 34fno t*/ RVAR("nivcsw", "NIVCSW", ru_nivcsw, LONG, SCNT), +/* 34fno t*/ ALIAS("nsignals", NULL, "nsigs"), +/* 34fno t*/ RVAR("nsigs", "NSIGS", ru_nsignals, LONG, SCNT), +/* 34fno t*/ RVAR("nswap", "NSWAP", ru_nswap, LONG, SCNT), +/* 34fno t*/ RVAR("nvcsw", "NVCSW", ru_nvcsw, LONG, SCNT), +/*_34fno t*/ CALL("nwchan", "NWCHAN", LJUST, nwchan, KPTR), +#if defined(WITH_NOP_ALIASES) +/*# s */ ALIAS("opri", NULL, "pri"), +/*# s */ ALIAS("osz", NULL, "sz"), +#endif +/* 34fno t*/ RVAR("oublk", "OUBLK", ru_oublock, LONG, SCNT), +/* 34fno t*/ ALIAS("oublock", NULL, "oublk"), +#if defined(WITH_EXTRA_ALIASES) +/*# i */ ALIAS("p", "P", "psr"), +/*#3 t*/ ALIAS("p_rss", NULL, "rss"), +#endif +#if defined(WITH_NOP_ALIASES) +/*#34 no */ CALL("p_ru", "P_RU", 0, nop, KPTR), /* XXX nop */ +#endif +/* 34fno */ KVAR("paddr", "PADDR", ki_paddr, KPTR, KPTR), +/*_34fno t*/ CALL("pagein", "PAGEIN", USER, pagein, SCNT), +/* 34fnoxhist*/ ALIAS("pcpu", NULL, "%cpu"), +/* 34fno t*/ ALIAS("pending", NULL, "sig"), +/*_34fnoxhist*/ PID("pgid", "PGID", ki_pgid), +/*_34fnoxhist*/ PID("pid", "PID", ki_pid), +/* 34fno st*/ ALIAS("pmem", NULL, "%mem"), +#if defined(WITH_NOP_ALIASES) +/*#3 */ CALL("poip", "POIP", 0, nop, SCNT), /* XXX nop */ +#endif +#if defined(COMPAT_TRU64) +/*- t*/ ALIAS("policy", "POL", "class"), +#endif +/*_34fnoxhist*/ PID("ppid", "PPID", ki_ppid), +/*_34fno h st*/ CALL("pri", "PRI", 0, pri, PRIO), +#if defined(WITH_NOP_ALIASES) +/*# h */ CALL("prmgrp", "PRMGRP", 0, nop, GRP), /* XXX nop */ +/*# h */ CALL("prmid", "PRMID", 0, nop, PID), /* XXX nop */ +/*# s */ CALL("project", "PROJECT", 0, nop, USER),/* XXX nop */ +/*# s */ CALL("projid", "PROJID", 0, nop, PID), /* XXX nop */ +#endif +#if defined(WITH_EXTRA_ALIASES) +/*#3 */ ALIAS("procaddr", NULL, "paddr"), +#endif +#if defined(COMPAT_TRU64) +/*- h st*/ CALL("pset", "PSET", 0, nop, PRIO), /* XXX nop */ +#endif +/*_ st*/ KVAR("psr", "PSR", ki_oncpu, UCHAR, PRIO), +#if defined(COMPAT_TRU64) +/*- t*/ ALIAS("psxpri", NULL, "pri"), +#endif +/*_34fno */ K127("re", "RE", ki_swtime, UINT, K127), +#if defined(WITH_EXTRA_ALIASES) +/*#3 */ ALIAS("resident", NULL, "re"), +#endif +/* 34fno h st*/ GID("rgid", "RGID", ki_rgid), +/* fnoxhist*/ SIZE("rgroup", "RGROUP", LJUST, rgname, s_rgname, GRP), +#if defined(WITH_NOP_ALIASES) +/*#34 no */ CALL("rlink", "RLINK", 0, nop, KPTR), /* XXX nop */ +/*# n */ CALL("rlwp", "RLWP", 0, nop, PID), /* XXX nop */ +#endif +/*_34fno st*/ KVAR("rss", "RSS", ki_rssize, PGTOK, LSZ), +/*+34 no t*/ ALIAS("rssize", NULL, "rsz"), +/*+34 no t*/ ALIAS("rsz", "RSZ", "rss"), +/* f */ CALL("rtprio", "RTPRIO", 0, priorityr, RTPR), +/* 34fno h st*/ UID("ruid", "RUID", ki_ruid), +#if defined(WITH_EXTRA_ALIASES) +/*#3 t*/ ALIAS("runame", NULL, "ruser"), +#endif +/* 34fnoxhist*/ SIZE("ruser", "RUSER", LJUST, runame, s_runame, USER), +/*_ x s */ ALIAS("s", "S", "state"), +#if defined(COMPAT_TRU64) +/*- t*/ CALL("scnt", "SCNT", 0, nop, SCNT), /* XXX nop p_suspcount? */ +#if defined(WITH_EXTRA_ALIASES) +/*# t*/ ALIAS("scount", NULL, "scnt"), +#endif +/*-3 no t*/ ALIAS("sess", "SESS", "sid"), +#if defined(WITH_EXTRA_ALIASES) +/*#3 t*/ ALIAS("session", NULL, "sess"), +#endif +#endif +/*_ fn h s */ PID("sid", "SID", ki_sid), +/*_34fno t*/ SIG("sig", "PENDING", ki_siglist), +/*_34fno t*/ SIG("sigcatch", "CAUGHT", ki_sigcatch), +/*_34fno t*/ SIG("sigignore", "IGNORED", ki_sigignore), +/*_34fno t*/ SIG("sigmask", "BLOCKED", ki_sigmask), +#if defined(WITH_EXTRA_ALIASES) +/*# */ ALIAS("size", NULL, "sz"), +#endif +/*_34fno t*/ K127("sl", "SL", ki_slptime, UINT, K127), +#if defined(WITH_EXTRA_ALIASES) +/*#3 t*/ ALIAS("slp", NULL, "sl"), +/*#3 t*/ ALIAS("slptime", NULL, "sl"), +#endif +/*+ o */ KVAR("ssiz", "SSIZ", ki_ssize, PGTOK, SSZ), +/*_34fno t*/ CALL("start", "STARTED", LJUST|USER, started, STRT), +/* 34fno t*/ ALIAS("stat", NULL, "state"), +/*_34fno hi t*/ CALL("state", "STAT", 0, state, STAT), +#if defined(WITH_NOP_ALIASES) +/*# t*/ CALL("status", "STATUS", 0, nop, STAT), /* XXX nop ki_valid? */ +#endif +/*_ xhist*/ CALL("stime", "STIME", USER, stime, STIME), +/* 34fno t*/ GID("svgid", "SVGID", ki_svgid), +/*+ no */ SIZE("svgroup", "SVGROUP", LJUST, svgname, s_svgname, GRP), +/* 34fno t*/ UID("svuid", "SVUID", ki_svuid), +/*+ no */ SIZE("svuser", "SVUSER", LJUST, svuname, s_svuname, USER), +#if defined(COMPAT_TRU64) +/*- t*/ SIZE("systime", "SYSTIME", USER, systime, s_time, TIME), +#endif +/*_ xh t*/ ALIAS("sz", "SZ", "vsz"), +#if defined(WITH_NOP_ALIASES) +/*# s */ CALL("taskid", "TASKID", 0, nop, PID), +#endif +/* 34fno t*/ CALL("tdev", "TDEV", 0, tdev, DEV), +/*_34fnoxhist*/ SIZE("time", "TIME", USER, cputime, s_time, TIME), +#if defined(WITH_EXTRA_ALIASES) +/*#3 t*/ ALIAS("tname", NULL, "tt"), +#endif +/*_34fno t*/ PID("tpgid", "TPGID", ki_tpgid), +#if defined(WITH_EXTRA_ALIASES) +/*#3 */ ALIAS("traceflag", NULL, "ktrace"), +#endif +#if defined(WITH_NOP_ALIASES) +/*#3 */ CALL("trs", "TRS", 0, nop, LSZ), /* XXX nop */ +#endif +#if defined(COMPAT_TRU64) +/*-3 no t*/ ALIAS("tsess", "TSESS", "tsid"), +#if defined(WITH_EXTRA_ALIASES) +/*#3 t*/ ALIAS("tsession", NULL, "tsess"), +#endif +#endif +/* f */ PID("tsid", "TSID", ki_tsid), +/*_34fno */ KVAR("tsiz", "TSIZ", ki_tsize, PGTOK, SSZ), +/* 34fno t*/ CALL("tt", "TT ", 0, tname, STTY), +/*_34fnoxhist*/ CALL("tty", "TTY", LJUST, longtname, LTTY), +#if defined(WITH_EXTRA_ALIASES) +/*#3 t*/ ALIAS("u_cmask", NULL, "umask"), +/*#3 t*/ ALIAS("u_procp", NULL, "uprocp"), +#endif +/* 34fno t*/ CALL("ucomm", "UCOMM", LJUST, ucomm, COMM), +/*_34fnoxhist*/ UID("uid", "UID", ki_uid), +#if defined(WITH_UMASK) +/*+3 t*/ KVAR("umask", "UMASK", ki_cmask, USHORT, UMSK), +#else +/*+3 t*/ CALL("umask", "UMASK", 0, nop, UMSK), /* XXX nop */ +#endif +#if defined(COMPAT_TRU64) +/*-3 t*/ ALIAS("uname", NULL, "user"), +#endif +/* 34fno */ CALL("upr", "UPR", 0, upr, PRIO), +/* 3 f t*/ ALIAS("uprocp", "UPROCP", "paddr"), +/*_34fnoxhist*/ SIZE("user", "USER", LJUST, uname, s_uname, USER), +#if defined(COMPAT_TRU64) +/*- t*/ SIZE("usertime", "USRTIME", USER, usertime, s_time, TIME), +#endif +/* 34fno t*/ ALIAS("usrpri", NULL, "upr"), +#if defined(WITH_EXTRA_ALIASES) +/*# i */ ALIAS("util", NULL, "c"), +/*#3 t*/ ALIAS("vcsw", "VCSW", "nvcsw"), +#endif +/* 34fno t*/ ALIAS("vsize", NULL, "vsz"), +/*_34fnoxhist*/ CALL("vsz", "VSZ", 0, vsize, LSZ), +/*_34fnoxhist*/ CALL("wchan", "WCHAN", LJUST, wchan, WCHN), +#if defined(WITH_EXTRA_ALIASES) +/*# i */ ALIAS("wname", NULL, "mwchan"), +#endif +/* 34fno */ KVAR("xstat", "XSTAT", ki_xstat, USHORT, SHEX), + ALIAS("", NULL, NULL) }; void @@ -223,103 +452,176 @@ } void -parsefmt(const char *p, int user) +parsefmt(const char *p, const char *f) { - static struct varent *vtail; - char *tempstr, *tempstr1; + char *cp; + + cp = strdup(p); + if (!cp) + errx(1, "malloc failed"); + parsefmt1(cp, f, NULL); +} -#define FMTSEP " \t,\n" - tempstr1 = tempstr = strdup(p); - while (tempstr && *tempstr) { - char *cp, *hp; - VAR *v; - struct varent *vent; - - /* - * If an item contains an equals sign, it specifies a column - * header, may contain embedded separator characters and - * is always the last item. - */ - if (tempstr[strcspn(tempstr, "="FMTSEP)] != '=') - while ((cp = strsep(&tempstr, FMTSEP)) != NULL && - *cp == '\0') - /* void */; - else { - cp = tempstr; - tempstr = NULL; +static int +parseexpr(char *e, const char *f) +{ +#define STACKSIZE 10 +#define STACKUNDERFLOW -1 +#define STACKOVERFLOW -2 +#define SYNTAXERROR -3 + int stack[STACKSIZE]; + int top; + char *p; + + for (top = -1; *e; e++) { + switch (*e) { + case '(': + case ')': + /* nop */ + break; + case '&': + if (--top < 0) + return(STACKUNDERFLOW); + stack[top] = stack[top] & stack[top+1]; + break; + case '|': + if (--top < 0) + return(STACKUNDERFLOW); + stack[top] = stack[top] | stack[top+1]; + break; + case '^': + if (--top < 0) + return(STACKUNDERFLOW); + stack[top] = stack[top] ^ stack[top+1]; + break; + case '!': + if (top < 0) + return(STACKUNDERFLOW); + stack[top] = !stack[top]; + break; + case '[': + if (++top >= STACKSIZE) + return(STACKOVERFLOW); + for (p = ++e; *e && *e != ']'; e++) + if (strchr("[()&|^!", *e)) + return(SYNTAXERROR); + if (*e) { + *e = '\0'; + stack[top] = strpbrk(f, p) != NULL; + break; + } + /*FALLTHROUGH*/ + case ']': + return(SYNTAXERROR); + default: + if (++top >= STACKSIZE) + return(STACKOVERFLOW); + stack[top] = *e == '*' || strchr(f, *e) != NULL; } - if (cp == NULL || !(v = findvar(cp, user, &hp))) + } + if (top == 0) + return(stack[top]); + return(SYNTAXERROR); +} + +static void +parsefmt1(char *p, const char *f, const char *h) +{ + char *cp, *hp, *ep; + VAR *v; + VARENT *vent; + const char *sep; + int rc; + + sep = fmtsep + (strchr(p, '=') != NULL); +loop: + while (p && *p) { + while ((cp = strsep(&p, sep)) != NULL && *cp == '\0') + /* void */; + if (cp == NULL) continue; - if (!user) { - /* - * If the user is NOT adding this field manually, - * get on with our lives if this VAR is already - * represented in the list. - */ - vent = find_varentry(v); - if (vent != NULL) - continue; + if ((hp = strpbrk(cp, ":=")) != NULL) { + switch (*hp) { + case ':': + *hp++ = '\0'; + if ((hp = strchr(ep = hp, '=')) != NULL) + *hp++ = '\0'; + if (f && *f && (rc = parseexpr(ep, f)) <= 0) { + const char *msg; + + switch (rc) { + case STACKUNDERFLOW: + msg = "stack underflow"; + break; + case STACKOVERFLOW: + msg = "stack overflow"; + break; + case SYNTAXERROR: + msg = "syntax error"; + break; + default: + msg = "ignored keyword"; + break; + } + if (rc < 0) + warnx("%s in %s:%s", msg, + cp, hp); + continue; + } + break; + case '=': + *hp++ = '\0'; + } } - if ((vent = malloc(sizeof(struct varent))) == NULL) - errx(1, "malloc failed"); - vent->header = v->header; - if (hp) { - hp = strdup(hp); - if (hp) - vent->header = hp; - } - vent->var = malloc(sizeof(*vent->var)); - if (vent->var == NULL) + if (!(v = findvar(cp, hp ? hp : h))) + continue; + STAILQ_FOREACH(vent, &varlist, list) + if (!strcmp(v->name, vent->var->name)) { + warnx("%s: duplicate keyword", v->name); + goto loop; + } + if ((vent = malloc(sizeof(VARENT))) == NULL) errx(1, "malloc failed"); - memcpy(vent->var, v, sizeof(*vent->var)); - vent->next = NULL; - if (vhead == NULL) - vhead = vtail = vent; - else { - vtail->next = vent; - vtail = vent; - } + vent->var = v; + STAILQ_INSERT_TAIL(&varlist, vent, list); } - free(tempstr1); - if (!vhead) { - warnx("no valid keywords; valid keywords:"); - showkey(); - exit(1); + if (STAILQ_EMPTY(&varlist)) { + if (posix) { + warnx("no valid keywords; valid keywords:"); + showkey(); + exit(1); + } else + errx(1, "no valid keywords"); } } static VAR * -findvar(char *p, int user, char **header) +findvar(const char *p, const char *h) { VAR *v, key; - char *hp; - - hp = strchr(p, '='); - if (hp) - *hp++ = '\0'; key.name = p; v = bsearch(&key, var, sizeof(var)/sizeof(VAR) - 1, sizeof(VAR), vcmp); - - if (v && v->alias) { - if (hp) { - warnx("%s: illegal keyword specification", p); - eval = 1; - } - parsefmt(v->alias, user); - return ((VAR *)NULL); - } - if (!v) { + if (v) { + if (v->alias) { + char *alias; + + if ((alias = strdup(v->alias)) == NULL) + errx(1, "malloc failed"); + parsefmt1(alias, NULL, h ? h : v->header); + free(alias); + return ((VAR *)NULL); + } else if (h && (v->header = strdup(h)) == NULL) + errx(1, "malloc failed"); + } else { warnx("%s: keyword not found", p); eval = 1; } - if (header) - *header = hp; return (v); } static int vcmp(const void *a, const void *b) { - return (strcmp(((const VAR *)a)->name, ((const VAR *)b)->name)); + return (strcmp(((const VAR *)a)->name, ((const VAR *)b)->name)); } Index: print.c =================================================================== RCS file: /home/ncvs/src/bin/ps/print.c,v retrieving revision 1.85 diff -u -I$Id.*$ -I$.+BSD.*$ -r1.85 print.c --- print.c 6 Apr 2004 20:06:49 -0000 1.85 +++ print.c 20 Apr 2004 00:53:49 -0000 @@ -41,7 +41,6 @@ #include #include #include - #include #include #include @@ -64,31 +63,37 @@ #define ps_pgtok(a) (((a) * getpagesize()) / 1024) +#define FMTLEN 64 +#define BUFLEN 128 + +static const char defunct[] = ""; + +static void printtime(time_t, time_t, short, int); +static void printsize(const char *, short, u_long); +static double getpmem(KINFO *); +static void printval(char *, VAR *v); + void printheader(void) { VAR *v; - struct varent *vent; - int allempty; + VARENT *vent; - allempty = 1; - for (vent = vhead; vent; vent = vent->next) - if (*vent->header != '\0') { - allempty = 0; + STAILQ_FOREACH(vent, &varlist, list) + if (*vent->var->header) break; - } - if (allempty) + if (!vent) return; - for (vent = vhead; vent; vent = vent->next) { + STAILQ_FOREACH(vent, &varlist, list) { v = vent->var; if (v->flag & LJUST) { - if (vent->next == NULL) /* last one */ - (void)printf("%s", vent->header); + if (STAILQ_NEXT(vent, list) == NULL) /* last one */ + (void)printf("%s", v->header); else - (void)printf("%-*s", v->width, vent->header); + (void)printf("%-*s", v->width, v->header); } else - (void)printf("%*s", v->width, vent->header); - if (vent->next != NULL) + (void)printf("%*s", v->width, v->header); + if (STAILQ_NEXT(vent, list) != NULL) (void)putchar(' '); } (void)putchar('\n'); @@ -102,11 +107,17 @@ char *cp, *vis_args; v = ve->var; + if (k->ki_p->ki_stat == SZOMB) { + if (STAILQ_NEXT(ve, list) == NULL) /* last field, don't pad */ + (void)printf("%s", defunct); + else + (void)printf("%-*s", v->width, defunct); + return; + } if ((vis_args = malloc(strlen(k->ki_args) * 4 + 1)) == NULL) errx(1, "malloc failed"); strvis(vis_args, k->ki_args, VIS_TAB | VIS_NL | VIS_NOSLASH); - if (ve->next == NULL) { - /* last field */ + if (STAILQ_NEXT(ve, list) == NULL) { /* last field */ if (termwidth == UNLIMITED) { (void)printf("%s", vis_args); } else { @@ -130,8 +141,15 @@ char *cp, *vis_env, *vis_args; v = ve->var; - if (cflag) { - if (ve->next == NULL) /* last field, don't pad */ + if (k->ki_p->ki_stat == SZOMB) { + if (STAILQ_NEXT(ve, list) == NULL) /* last field, don't pad */ + (void)printf("%s", defunct); + else + (void)printf("%-*s", v->width, defunct); + return; + } + if (commandonly) { + if (STAILQ_NEXT(ve, list) == NULL) /* last field, don't pad */ (void)printf("%s", k->ki_p->ki_comm); else (void)printf("%-*s", v->width, k->ki_p->ki_comm); @@ -147,8 +165,7 @@ } else vis_env = NULL; - if (ve->next == NULL) { - /* last field */ + if (STAILQ_NEXT(ve, list) == NULL) { /* last field */ if (termwidth == UNLIMITED) { if (vis_env) (void)printf("%s ", vis_env); @@ -161,7 +178,7 @@ while (--left >= 0 && *cp) (void)putchar(*cp++); if (--left >= 0) - putchar(' '); + (void)putchar(' '); } for (cp = vis_args; --left >= 0 && *cp != '\0';) (void)putchar(*cp++); @@ -180,7 +197,17 @@ VAR *v; v = ve->var; - (void)printf("%-*s", v->width, k->ki_p->ki_comm); + if (k->ki_p->ki_stat == SZOMB) { + if (STAILQ_NEXT(ve, list) == NULL) /* last field, don't pad */ + (void)printf("%s", defunct); + else + (void)printf("%-*s", v->width, defunct); + } else { + if (STAILQ_NEXT(ve, list) == NULL) /* last field, don't pad */ + (void)printf("%s", k->ki_p->ki_comm); + else + (void)printf("%-*s", v->width, k->ki_p->ki_comm); + } } void @@ -221,8 +248,11 @@ break; case SRUN: + if (k->ki_p->ki_oncpu != NOCPU) + *cp = 'O'; + else case SIDL: - *cp = 'R'; + *cp = 'R'; break; case SWAIT: @@ -243,6 +273,10 @@ cp++; if (!(sflag & PS_INMEM)) *cp++ = 'W'; +#if defined(WITH_MAXRSS) + else if (pgtok(k->ki_p->ki_rssize) > (k->ki_p->ki_maxrss / 1024)) + *cp++ = '>'; +#endif if (k->ki_p->ki_nice < NZERO) *cp++ = '<'; else if (k->ki_p->ki_nice > NZERO) @@ -253,10 +287,14 @@ *cp++ = 'E'; if (flag & P_PPWAIT) *cp++ = 'V'; - if ((flag & P_SYSTEM) || k->ki_p->ki_lock > 0) + if (flag & P_SYSTEM) + *cp++ = 'K'; + else if (k->ki_p->ki_lock > 0) *cp++ = 'L'; if (k->ki_p->ki_kiflag & KI_SLEADER) *cp++ = 's'; + if (flag & P_SA) + *cp++ = 'a'; if ((flag & P_CONTROLT) && k->ki_p->ki_pgid == k->ki_p->ki_tpgid) *cp++ = '+'; if (flag & P_JAILED) @@ -265,13 +303,23 @@ (void)printf("%-*s", v->width, buf); } +#define scalepri(x) ((x) - PZERO) void pri(KINFO *k, VARENT *ve) { VAR *v; v = ve->var; - (void)printf("%*d", v->width, k->ki_p->ki_pri.pri_level - PZERO); + (void)printf("%*d", v->width, scalepri(k->ki_p->ki_pri.pri_level)); +} + +void +upr(KINFO *k, VARENT *ve) +{ + VAR *v; + + v = ve->var; + (void)printf("%*d", v->width, scalepri(k->ki_p->ki_pri.pri_user)); } void @@ -290,7 +338,7 @@ } void -rgroupname(KINFO *k, VARENT *ve) +rgname(KINFO *k, VARENT *ve) { VAR *v; @@ -299,7 +347,7 @@ } int -s_rgroupname(KINFO *k) +s_rgname(KINFO *k) { return (strlen(group_from_gid(k->ki_p->ki_rgid, 0))); } @@ -319,6 +367,117 @@ return (strlen(user_from_uid(k->ki_p->ki_ruid, 0))); } +void +gname(KINFO *k, VARENT *ve) +{ + VAR *v; + + v = ve->var; + (void)printf("%-*s", v->width, + group_from_gid(k->ki_p->ki_groups[0], 0)); +} + +int +s_gname(KINFO *k) +{ + return (strlen(group_from_gid(k->ki_p->ki_groups[0], 0))); +} + +void +groups(KINFO *k, VARENT *ve) +{ + VAR *v; + char buf[UIDLEN + 2]; + const char *sep; + int left, i, n; + + v = ve->var; + if (k->ki_p->ki_ngroups <= 1) { + (void)printf("%-*s", v->width, "-"); + return; + } + if (STAILQ_NEXT(ve, list) != NULL || termwidth == UNLIMITED) /* last + field */ + left = v->width; + else { + left = termwidth - (totwidth - v->width); + if (left < 1) /* already wrapped, just use std width */ + left = v->width; + } + sep = ""; + for (i = 1; i < k->ki_p->ki_ngroups && left >= 0; i++, left -= n) { + n = snprintf(buf, sizeof(buf), "%s%d", sep, + k->ki_p->ki_groups[i]); + n = MIN(n, left); + (void)printf("%*.*s", n, n, buf); + sep = ","; + } + if (STAILQ_NEXT(ve, list) != NULL && left > 0) + (void)printf("%*s", left, ""); +} + +void +groupnames(KINFO *k, VARENT *ve) +{ + VAR *v; + char buf[USERLEN + 2]; + const char *sep; + int left, i, n; + + v = ve->var; + if (k->ki_p->ki_ngroups <= 1) { + (void)printf("%-*s", v->width, "-"); + return; + } + if (STAILQ_NEXT(ve, list) != NULL || termwidth == UNLIMITED) /* last + field */ + left = v->width; + else { + left = termwidth - (totwidth - v->width); + if (left < 1) /* already wrapped, just use std width */ + left = v->width; + } + sep = ""; + for (i = 1; i < k->ki_p->ki_ngroups && left >= 0; i++, left -= n) { + n = snprintf(buf, sizeof(buf), "%s%s", sep, + group_from_gid(k->ki_p->ki_groups[i], 0)); + n = MIN(n, left); + (void)printf("%*.*s", n, n, buf); + sep = ","; + } + if (STAILQ_NEXT(ve, list) != NULL && left > 0) + (void)printf("%*s", left, ""); +} + +void +svgname(KINFO *k, VARENT *ve) +{ + VAR *v; + + v = ve->var; + (void)printf("%-*s", v->width, group_from_gid(k->ki_p->ki_svgid, 0)); +} + +int +s_svgname(KINFO *k) +{ + return (strlen(group_from_gid(k->ki_p->ki_svgid, 0))); +} + +void +svuname(KINFO *k, VARENT *ve) +{ + VAR *v; + + v = ve->var; + (void)printf("%-*s", v->width, user_from_uid(k->ki_p->ki_svuid, 0)); +} + +int +s_svuname(KINFO *k) +{ + return (strlen(user_from_uid(k->ki_p->ki_svuid, 0))); +} void tdev(KINFO *k, VARENT *ve) @@ -374,40 +533,89 @@ } void +gettime(time_t *tp, struct timeval *tvp) +{ + static time_t t; + static struct timeval tv; + + if (!t) { + (void)time(&t); + (void)gettimeofday(&tv, NULL); + } + if (tp) + *tp = t; + if (tvp) + *tvp = tv; +} + +void started(KINFO *k, VARENT *ve) { VAR *v; + time_t _now; time_t then; + time_t _elapsed; struct tm *tp; - static int use_ampm = -1; - char buf[100]; + static int use_ampm = -1; + char buf[BUFLEN]; v = ve->var; if (!k->ki_valid) { - (void)printf("%-*s", v->width, "-"); + (void)printf("%*s", v->width, "-"); return; } if (use_ampm < 0) use_ampm = (*nl_langinfo(T_FMT_AMPM) != '\0'); then = k->ki_p->ki_start.tv_sec; + gettime(&_now, NULL); + _elapsed = _now - then; tp = localtime(&then); - if (now - k->ki_p->ki_start.tv_sec < 24 * 3600) { + if (_elapsed < 24 * 3600) { (void)strftime(buf, sizeof(buf), use_ampm ? "%l:%M%p" : "%k:%M ", tp); - } else if (now - k->ki_p->ki_start.tv_sec < 7 * 86400) { + } else if (_elapsed < 7 * 86400) { (void)strftime(buf, sizeof(buf), use_ampm ? "%a%I%p" : "%a%H ", tp); - } else - (void)strftime(buf, sizeof(buf), "%e%b%y", tp); + } else { + static const char fmt[] = "%e%b%y"; /* avoid gcc warning */ + (void)strftime(buf, sizeof(buf), fmt, tp); + } (void)printf("%-*s", v->width, buf); } void +stime(KINFO *k, VARENT *ve) +{ + VAR *v; + time_t _now; + time_t then; + time_t _elapsed; + struct tm *tp; + char buf[BUFLEN]; + + v = ve->var; + if (!k->ki_valid) { + (void)printf("%*s", v->width, "-"); + return; + } + then = k->ki_p->ki_start.tv_sec; + gettime(&_now, NULL); + _elapsed = _now - then; + tp = localtime(&then); + if (_elapsed < 24 * 3600) + (void)strftime(buf, sizeof(buf), "%T", tp); + else + (void)strftime(buf, sizeof(buf), "%b%d", tp); + (void)printf("%*s", v->width, buf); +} + +void lstarted(KINFO *k, VARENT *ve) { VAR *v; time_t then; - char buf[100]; + char buf[BUFLEN]; + static const char fmt[] = "%c"; /* avoid gcc warning */ v = ve->var; if (!k->ki_valid) { @@ -415,7 +623,7 @@ return; } then = k->ki_p->ki_start.tv_sec; - (void)strftime(buf, sizeof(buf), "%c", localtime(&then)); + (void)strftime(buf, sizeof(buf), fmt, localtime(&then)); (void)printf("%-*s", v->width, buf); } @@ -446,7 +654,7 @@ (void)printf("%-*.*s", v->width, v->width, k->ki_p->ki_wmesg); else - (void)printf("%-*lx", v->width, + (void)printf("%*lx", v->width, (long)k->ki_p->ki_wchan); } else (void)printf("%-*s", v->width, "-"); @@ -476,7 +684,7 @@ (void)printf("%-*.*s", v->width, v->width, k->ki_p->ki_wmesg); else - (void)printf("%-*lx", v->width, + (void)printf("%*lx", v->width, (long)k->ki_p->ki_wchan); } else if (k->ki_p->ki_kiflag & KI_LOCKBLOCK) { if (k->ki_p->ki_lockname[0]) { @@ -494,7 +702,64 @@ VAR *v; v = ve->var; - (void)printf("%*lu", v->width, (u_long)(k->ki_p->ki_size / 1024)); + printsize("%*lu", v->width, (u_long)(k->ki_p->ki_size / 1024)); +} + +static void +printtime(time_t secs, time_t psecs, short width, int _etime) +{ + time_t mins; + char buf[BUFLEN]; + + /* + * time adjustments + */ + if (psecs >= 1000000) + secs++, psecs -= 1000000; + else if (psecs < 0) + secs--, psecs += 1000000; + mins = secs / 60; + secs %= 60; + if (_etime || posix) { + time_t hours, days; + + hours = mins / 60; + days = hours / 24; + hours %= 24; + mins %= 60; + if (days) + (void)snprintf(buf, sizeof(buf), + "%02d-%02d:%02d:%02d", days, hours, mins, secs); + else if (!_etime || hours) + (void)snprintf(buf, sizeof(buf), "%02d:%02d:%02d", + hours, mins, secs); + else + (void)snprintf(buf, sizeof(buf), "%02d:%02d", + mins, secs); + } else { + static char decimal_point; + + if (decimal_point == '\0') + decimal_point = localeconv()->decimal_point[0]; + /* + * round and scale to 100's + */ + if (secs || psecs) { + psecs = (psecs + 5000) / 10000; + secs += psecs / 100; + psecs %= 100; + } + (void)snprintf(buf, sizeof(buf), "%3d:%02d.%02d", + mins, secs, psecs); + } + (void)printf("%*s", width, buf); +} + +/* ARGSUSED */ +int +s_time(KINFO *k __unused) +{ + return (posix ? LTIMELEN : STIMELEN); } void @@ -503,11 +768,7 @@ VAR *v; long secs; long psecs; /* "parts" of a second. first micro, then centi */ - char obuff[128]; - static char decimal_point; - if (decimal_point == '\0') - decimal_point = localeconv()->decimal_point[0]; v = ve->var; if (k->ki_p->ki_stat == SZOMB || !k->ki_valid) { secs = 0; @@ -521,47 +782,114 @@ secs = k->ki_p->ki_runtime / 1000000; psecs = k->ki_p->ki_runtime % 1000000; if (sumrusage) { +#if defined(WITH_CHILDTIMES) + secs += k->ki_p->ki_childstime.tv_sec + + k->ki_p->ki_childutime.tv_sec; + psecs += k->ki_p->ki_childstime.tv_usec + + k->ki_p->ki_childutime.tv_usec; +#else secs += k->ki_p->ki_childtime.tv_sec; psecs += k->ki_p->ki_childtime.tv_usec; +#endif } - /* - * round and scale to 100's - */ - psecs = (psecs + 5000) / 10000; - secs += psecs / 100; - psecs = psecs % 100; - } - (void)snprintf(obuff, sizeof(obuff), "%3ld:%02ld%c%02ld", - secs / 60, secs % 60, decimal_point, psecs); - (void)printf("%*s", v->width, obuff); + } + printtime(secs, psecs, v->width, 0); +} + +void +cldtime(KINFO *k, VARENT *ve) +{ + VAR *v; + long secs; + long psecs; /* "parts" of a second. first micro, then centi */ + + v = ve->var; + if (k->ki_p->ki_stat == SZOMB || !k->ki_valid) { + secs = 0; + psecs = 0; + } else { +#if defined(WITH_CHILDTIMES) + secs = k->ki_p->ki_childstime.tv_sec + + k->ki_p->ki_childutime.tv_sec; + psecs = k->ki_p->ki_childstime.tv_usec + + k->ki_p->ki_childutime.tv_usec; +#else + secs = k->ki_p->ki_childtime.tv_sec; + psecs = k->ki_p->ki_childtime.tv_usec; +#endif + } + printtime(secs, psecs, v->width, 0); } void elapsed(KINFO *k, VARENT *ve) { VAR *v; - time_t val; - int days, hours, mins, secs; - char obuff[128]; - - v = ve->var; - val = now - k->ki_p->ki_start.tv_sec; - days = val / (24 * 60 * 60); - val %= 24 * 60 * 60; - hours = val / (60 * 60); - val %= 60 * 60; - mins = val / 60; - secs = val % 60; - if (days != 0) - (void)snprintf(obuff, sizeof(obuff), "%3d-%02d:%02d:%02d", - days, hours, mins, secs); - else if (hours != 0) - (void)snprintf(obuff, sizeof(obuff), "%02d:%02d:%02d", - hours, mins, secs); - else - (void)snprintf(obuff, sizeof(obuff), "%02d:%02d", mins, secs); - (void)printf("%*s", v->width, obuff); + struct timeval _now; + long psecs, secs; + + v = ve->var; + if (!k->ki_valid) { + (void)printf("%*s", v->width, "-"); + return; + } + gettime(NULL, &_now); + secs = _now.tv_sec - k->ki_p->ki_start.tv_sec; + psecs = _now.tv_usec - k->ki_p->ki_start.tv_usec; + printtime(secs, psecs, v->width, 1); +} + +#if defined(COMPAT_TRU64) +void +systime(KINFO *k, VARENT *ve) +{ + VAR *v; + long secs; + long psecs; /* "parts" of a second. first micro, then centi */ + + v = ve->var; + if (k->ki_p->ki_stat == SZOMB || !k->ki_valid) { + secs = 0; + psecs = 0; + } else { + secs = k->ki_p->ki_rusage.ru_stime.tv_sec; + psecs = k->ki_p->ki_rusage.ru_stime.tv_usec; +#if defined(WITH_CHILDTIMES) + if (sumrusage) { + secs += k->ki_p->ki_childstime.tv_sec; + psecs += k->ki_p->ki_childstime.tv_usec; + } +#endif + } + printtime(secs, psecs, v->width, 0); } +#endif + +#if defined(COMPAT_TRU64) +void +usertime(KINFO *k, VARENT *ve) +{ + VAR *v; + long secs; + long psecs; /* "parts" of a second. first micro, then centi */ + + v = ve->var; + if (k->ki_p->ki_stat == SZOMB || !k->ki_valid) { + secs = 0; + psecs = 0; + } else { + secs = k->ki_p->ki_rusage.ru_utime.tv_sec; + psecs = k->ki_p->ki_rusage.ru_utime.tv_usec; +#if defined(WITH_CHILDTIMES) + if (sumrusage) { + secs += k->ki_p->ki_childutime.tv_sec; + psecs += k->ki_p->ki_childutime.tv_usec; + } +#endif + } + printtime(secs, psecs, v->width, 0); +} +#endif double getpcpu(const KINFO *k) @@ -633,53 +961,137 @@ /* ARGSUSED */ void -maxrss(KINFO *k __unused, VARENT *ve) +nop(KINFO *k __unused, VARENT *ve) { VAR *v; v = ve->var; - /* XXX not yet */ (void)printf("%*s", v->width, "-"); } +#if defined(WITH_MAXRSS) +void +maxrss(KINFO *k, VARENT *ve) +{ + VAR *v; + + v = ve->var; + if (k->ki_p->ki_maxrss != RLIM_INFINITY) + printsize("%*lu", v->width, + (u_long)(k->ki_p->ki_maxrss / 1024)); + else + (void)printf("%*s", v->width, "-"); +} +#endif + + void priorityr(KINFO *k, VARENT *ve) { VAR *v; - struct priority *lpri; char str[8]; - unsigned class, level; - + unsigned _class, level; + v = ve->var; - lpri = &k->ki_p->ki_pri; - class = lpri->pri_class; - level = lpri->pri_level; - switch (class) { + _class = k->ki_p->ki_pri.pri_class; + level = k->ki_p->ki_pri.pri_level; + switch (_class) { + case PRI_ITHD: + (void)strncpy(str, "intr", sizeof(str)); + break; case PRI_REALTIME: - snprintf(str, sizeof(str), "real:%u", level); + (void)snprintf(str, sizeof(str), "real:%u", level); break; case PRI_TIMESHARE: - strncpy(str, "normal", sizeof(str)); + (void)strncpy(str, "normal", sizeof(str)); break; case PRI_IDLE: - snprintf(str, sizeof(str), "idle:%u", level); + (void)snprintf(str, sizeof(str), "idle:%u", level); break; default: - snprintf(str, sizeof(str), "%u:%u", class, level); + (void)snprintf(str, sizeof(str), "%u:%u", _class, level); break; } str[sizeof(str) - 1] = '\0'; (void)printf("%*s", v->width, str); } +void +class(KINFO *k, VARENT *ve) +{ + VAR *v; + char str[8]; + unsigned _class; + + v = ve->var; + _class = k->ki_p->ki_pri.pri_class; + switch (_class) { + case PRI_ITHD: + (void)strncpy(str, "intr", sizeof(str)); /* FP? */ + break; + case PRI_REALTIME: + (void)strncpy(str, "real", sizeof(str)); /* RT? */ + break; + case PRI_TIMESHARE: + (void)strncpy(str, "normal", sizeof(str)); /* TS? */ + break; + case PRI_IDLE: + (void)strncpy(str, "idle", sizeof(str)); /* ID? */ + break; + default: + (void)snprintf(str, sizeof(str), "%u", _class); + break; + } + str[sizeof(str) - 1] = '\0'; + (void)printf("%-*s", v->width, str); +} + +void +printsize(const char *ofmt, short width, u_long kbytes) +{ + if (extended) { + static const char units[] = "KMGTP"; + static const char skip[] = "%-*"; + const char *kp, *fp, *sp; + char fmt[BUFLEN], buf[BUFLEN]; + size_t n; + u_long left; + + for (kp = units, n = strlen(units), left = 0; n; --n) + if (kbytes >= 1024) { + left = kbytes % 1024; + kbytes /= 1024; + kp++; + } else + break; + for (sp = skip, fp = ofmt;*sp && *fp; sp++) + if (*sp == *fp) + fp++; + if (left) { + left *= 100; + left /= 1024; + if (!(left % 10)) + left /= 10; + (void)snprintf(fmt, sizeof(fmt), "%%%s.%%%s", fp, fp); + (void)snprintf(buf, sizeof(buf), fmt, kbytes, left); + } else { + (void)snprintf(fmt, sizeof(fmt), "%%%s", fp); + (void)snprintf(buf, sizeof(buf), fmt, kbytes); + } + (void)printf("%*s", width-1, buf); + (void)putchar(*kp); + } else + (void)printf(ofmt, width, kbytes); +} + /* * Generic output routines. Print fields from various prototype * structures. */ static void -printval(void *bp, VAR *v) +printval(char *bp, VAR *v) { - static char ofmt[32] = "%"; + static char ofmt[FMTLEN] = "%"; const char *fcp; char *cp; @@ -721,7 +1133,7 @@ (void)printf(ofmt, v->width, *(u_long *)bp); break; case PGTOK: - (void)printf(ofmt, v->width, ps_pgtok(*(u_long *)bp)); + printsize(ofmt, v->width, ps_pgtok(*(u_long *)bp)); break; default: errx(1, "unknown type %d", v->type); @@ -774,7 +1186,7 @@ (void)printf("%-*s", v->width, string); free(string); } else - (void)printf("%-*s", v->width, ""); + (void)printf("%-*s", v->width, "-"); return; } Index: ps.1 =================================================================== RCS file: /home/ncvs/src/bin/ps/ps.1,v retrieving revision 1.75 diff -u -I$Id.*$ -I$.+BSD.*$ -r1.75 ps.1 --- ps.1 6 Apr 2004 20:06:49 -0000 1.75 +++ ps.1 18 Apr 2004 17:23:13 -0000 @@ -57,6 +57,20 @@ .Op , Ns Ar username Ns No ... .Xc .Oc +.Nm ps5 +.Op Fl AaBdefjl +.Op Fl C Ar cmdlist +.Op Fl g Ar pgrplist +.Op Fl G Ar grouplist +.Op Fl m Ar core +.Op Fl n Ar system +.Op Fl O Ar fmt +.Op Fl o Ar fmt +.Op Fl p Ar pidlist +.Op Fl t Ar ttylist +.Op Fl u Ar userlist +.Op Fl U Ar userlist +.Op Fl w Ar swap .Nm .Op Fl L .Sh DESCRIPTION @@ -95,6 +109,11 @@ options will change the sort order. If more than one sorting option was given, then the selected processes will be sorted by the last sorting option which was specified. +.Pp +.Nm psx +utility understands +.At v4 +command line options and displays process informations in the related format. .Pp For the processes which have been selected for display, the information to display is selected based on a set of keywords (see the Index: ps.c =================================================================== RCS file: /home/ncvs/src/bin/ps/ps.c,v retrieving revision 1.88 diff -u -I$Id.*$ -I$.+BSD.*$ -r1.88 ps.c --- ps.c 6 Apr 2004 20:06:49 -0000 1.88 +++ ps.c 20 Apr 2004 01:31:42 -0000 @@ -73,107 +73,145 @@ #include "ps.h" -#define W_SEP " \t" /* "Whitespace" list separators */ -#define T_SEP "," /* "Terminate-element" list separators */ - -#ifdef LAZY_PS -#define DEF_UREAD 0 -#define OPT_LAZY_f "f" -#else -#define DEF_UREAD 1 /* Always do the more-expensive read. */ -#define OPT_LAZY_f /* I.e., the `-f' option is not added. */ +#ifndef PID_MAX +#define PID_MAX 99999 /* sys/proc.h */ #endif - -int cflag; /* -c */ -int eval; /* Exit value */ -time_t now; /* Current time(3) value */ -int rawcpu; /* -C */ -int sumrusage; /* -S */ -int termwidth; /* Width of the screen (0 == infinity). */ -int totwidth; /* Calculated-width of requested variables. */ - -struct varent *vhead; - -static int forceuread = DEF_UREAD; /* Do extra work to get u-area. */ -static kvm_t *kd; -static KINFO *kinfo; -static int needcomm; /* -o "command" */ -static int needenv; /* -e */ -static int needuser; /* -o "user" */ -static int optfatal; /* Fatal error parsing some list-option. */ - -static enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT; - -struct listinfo; -typedef int addelem_rtn(struct listinfo *_inf, const char *_elem); - -struct listinfo { +#define BUCKETS 32 /* power of 2 */ +#define COLSENV "COLUMNS" /* getenv() */ +#define POSIXENV "POSIX" /* getenv() */ +#define OPTLEN 64 +#define FMTLEN 16 + +enum ltype { CMD, FMT, GID, PGID, PID, RUID, SID, TTY, UID, _LTYPE }; +enum sort { DEFAULT, SORTMEM, SORTCPU }; + +typedef struct list { + enum ltype type; + size_t size; + size_t off; + int op; + const char *msg; + const char *msg2; int count; - int maxcount; - int elemsize; - addelem_rtn *addelem; - const char *lname; + int noval; union { + char **cmds; + char *fmt; gid_t *gids; pid_t *pids; dev_t *ttys; uid_t *uids; void *ptr; } l; +} LIST; + +/* + * Global variables + */ + +VARLIST varlist = STAILQ_HEAD_INITIALIZER(varlist); + +int commandonly; /* -c */ +int eval; /* Exit value */ +int rawcpu; /* -C */ +int sumrusage; /* -S */ +int termwidth; /* -w : width of screen (0 == infinity) */ +int totwidth; /* Calculated width of requested variables */ + +int posix; /* -q | atoi(getenv(POSIXENV)) != 0 */ +int extended; /* -Q */ + +/* + * Local variables + */ + /* bsd options | posix options */ +static int all; /* -A, -a or -g | -A, -a, -d, or -e */ +static int noctty; /* -A, -x or -X | -A, -d or -e */ +static int leader; /* -A or -g | -A or -e */ +static int needenv; /* -e | nop */ +#if defined(LAZY_PS) +static int forceuread; /* -f | nop */ +#else +static int forceuread = 1; /* Do extra work to get u-area. */ +#endif +static int showthreads; /* -H */ +static int prtheader; /* -h */ +static const char *memf; /* -M | -n -n */ +static enum sort sortby = DEFAULT; /* -m | -r */ +static const char *nlistf; /* -N | -n */ +static const char *devnull = _PATH_DEVNULL; +static int dropgid; /* -M or -N | -n */ +static int needcomm; /* -o command */ +static int needuser; /* -o user */ + +/* PLEASE KEEP THE TABLE BELOW SORTED ALPHABETICALLY!!! */ +static LIST lists[] = { +#define _LIST(t, s, o, k, m1, m2) \ + { t, sizeof(s), KOFF(o), k, m1, m2, 0, 0, { 0 } } + _LIST(CMD, char *, ki_comm, 0, + "command", NULL), /* nop | -C */ + _LIST(FMT, char, ki_sparestrings, 0, + "format", NULL), /* -O or -o | ditto */ + _LIST(GID, gid_t, ki_rgid, 0, + "group", " name or id"), /* -G | -G */ + _LIST(PGID, pid_t, ki_pgid, KERN_PROC_PGRP, + "process group id", NULL), /* nop | -g */ + _LIST(PID, pid_t, ki_pid, KERN_PROC_PID, + "process id", NULL), /* -p | -p */ + _LIST(RUID, uid_t, ki_ruid, KERN_PROC_RUID, + "user", " name or id"), /* nop | -U */ + _LIST(SID, pid_t, ki_sid, /*KERN_PROC_SESSION*/0, + "session id", NULL), /* nop | -s */ + _LIST(TTY, dev_t, ki_tdev, KERN_PROC_TTY, + "terminal", NULL), /* -T or -t | -t */ + _LIST(UID, uid_t, ki_uid, KERN_PROC_UID, + "user", " name or id"), /* -U | -u */ + _LIST(_LTYPE, int, ki_sparestrings, 0, NULL, NULL), +#undef _LIST }; -static int addelem_gid(struct listinfo *, const char *); -static int addelem_pid(struct listinfo *, const char *); -static int addelem_tty(struct listinfo *, const char *); -static int addelem_uid(struct listinfo *, const char *); -static void add_list(struct listinfo *, const char *); -static void dynsizevars(KINFO *); -static void *expand_list(struct listinfo *); -static const char * - fmt(char **(*)(kvm_t *, const struct kinfo_proc *, int), - KINFO *, char *, int); -static void free_list(struct listinfo *); -static void init_list(struct listinfo *, addelem_rtn, int, const char *); -static char *kludge_oldps_options(char *); -static int pscomp(const void *, const void *); -static void saveuser(KINFO *); -static void scanvars(void); -static void sizevars(void); -static void usage(void); - -static char dfmt[] = "pid,tt,state,time,command"; -static char jfmt[] = "user,pid,ppid,pgid,jobc,state,tt,time,command"; -static char lfmt[] = "uid,pid,ppid,cpu,pri,nice,vsz,rss,mwchan,state," - "tt,time,command"; -static char o1[] = "pid"; -static char o2[] = "tt,state,time,command"; -static char ufmt[] = "user,pid,%cpu,%mem,vsz,rss,tt,state,start,time,command"; -static char vfmt[] = "pid,state,time,sl,re,pagein,vsz,rss,lim,tsiz," - "%cpu,%mem,command"; -static char Zfmt[] = "label"; +static KINFO *kinfo; +static struct winsize ws; +static int fatal; /* fatal error while parsing some list-option */ -#define PS_ARGS "AaCce" OPT_LAZY_f "G:gHhjLlM:mN:O:o:p:rSTt:U:uvwXxZ" +/* + * Local functions + */ +static const char *format(kvm_t *, + char **(*)(kvm_t *, const struct kinfo_proc *, int), + KINFO *, const char *, int); +static char *kludge_oldps_options(char *); +static int findproc(const struct kinfo_proc *, int); +static void parseopts(int, char *const *); +static void parselst(const char *, LIST *); +static int pscomp(const void *, const void *); +static void saveuser(kvm_t *, KINFO *); +static void scanvars(void); +static void dynsizevars(KINFO *); +static void sizevars(void); +static void usage(char); int main(int argc, char *argv[]) { - struct listinfo gidlist, pgrplist, pidlist; - struct listinfo ruidlist, sesslist, ttylist, uidlist; - struct kinfo_proc *kp; - struct varent *vent; - struct winsize ws; - const char *cp, *nlistf, *memf; - char *cols; - int all, ch, dropgid, elem, flag, _fmt, i, lineno; - int nentries, nocludge, nkept, nselectors; - int prtheader, showthreads, wflag, what, xkeep, xkeep_implied; - char errbuf[_POSIX2_LINE_MAX]; + kvm_t *kd; /* kvm file descriptor */ + struct kinfo_proc *kp; /* kvm_getprocs() */ + VARENT *vent; + LIST *list; + int i, lineno; /* indices */ + int what, flag, nentries; /* kvm_getprocs() */ + int nkept, nselectors; + char errbuf[_POSIX2_LINE_MAX]; /* kvm_openfiles() */ + const char *cp; + static char optargs[] = "OoptUMN"; + + memf = nlistf = devnull; (void) setlocale(LC_ALL, ""); - time(&now); /* Used by routines in print.c. */ + gettime(NULL, NULL); /* Used by routines in print.c. */ - if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0') - termwidth = atoi(cols); + if ((cp = getenv("COLUMNS")) != NULL && *cp != '\0') + termwidth = atoi(cp); /* XXX ERANGE */ else if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ws) == -1) || @@ -182,726 +220,1006 @@ else termwidth = ws.ws_col - 1; + if ((cp = strrchr(argv[0], '/')) != NULL) + cp++; + else + cp = argv[0]; + posix = strchr(cp, 'x') != NULL; /* as in psx */ + extended = *cp == 's'; /* as in sps */ +#if defined(WITH_POSIX_ENV) + if ((cp = getenv(POSIXENV)) != NULL && *cp != '\0') + posix = atoi(cp); /* XXX ERANGE */ +#endif /* * Don't apply a kludge if the first argument is an option taking an * argument */ - if (argc > 1) { - nocludge = 0; - if (argv[1][0] == '-') { - for (cp = PS_ARGS; *cp != '\0'; cp++) { - if (*cp != ':') - continue; - if (*(cp - 1) == argv[1][1]) { - nocludge = 1; - break; - } + if (argc > 1 && !(argv[1][0] == '-' && strchr(optargs, argv[1][1]))) + argv[1] = kludge_oldps_options(argv[1]); + + all = noctty = leader = -1; + parseopts(argc, argv); + + /* + * Display an error message for options with only empty arguments + * which is fatal or count how many arguments where specified + */ + nselectors = 0; + for (list = lists; list->type != _LTYPE; list++) { + if (list->noval) { + warnx("no %s%s specified", list->msg, + list->msg2 ? list->msg2 : ""); + fatal = 1; + } + if (list->type != FMT) + nselectors += list->count; + } + + if (fatal) + exit(fatal); + +#if defined(BACKWARD_COMPATIBILITY) + if (!posix && *argv) { + nlistf = *argv; + if (*++argv) { + memf = *argv; + } + } +#endif + /* + * Discard setgid privileges if not the running kernel so that bad + * guys can't print interesting stuff from kernel memory. + */ + if (dropgid) { + setgid(getgid()); + setuid(getuid()); + } + + kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf); + if (kd == 0) + errx(1, "%s", errbuf); + +#if defined(DEBUG) +fprintf(stderr, "all:noctty:leader:nselectors:extended == %d:%d:%d:%d:%d -> ", + all, noctty, leader, nselectors, extended); +#endif + /* + * show processes without a controlling terminal if... see below. + * posix : show them also if -d was given. + * bsd : show them also if -x was given + * (or don't show them if -X xas given). + */ + if (noctty < 0) + noctty = nselectors; + /* + * posix : select leader processes if at least one of -C, -G, -g + * -p, -s, -t, -U or -u, where given. + * select them also if -A or -e where given. + * bsd : select leader processes if at least one of -G, -p, -t, + * -U where given. select them also if -A is given. + * in sunos compat mode, select them if -g is given. + */ + if (leader < 0) + leader = posix ? nselectors : !extended; + /* + * find all processes if ... see above. + * posix : find them also if -d was given. + * bsd : find them also if -a or -g where given. + */ + if (all < 0) + all = nselectors; +#if defined(DEBUG) +fprintf(stderr, "%d:%d:%d:%d:%d -> ", all, noctty, leader, nselectors, extended); +#endif + if (!all) { + char buf[UIDLEN]; + + snprintf(buf, sizeof(buf), "%d", getuid()); + parselst(buf, &lists[UID]); + nselectors = 1; + } +#if defined(DEBUG) +fprintf(stderr, "%d:%d:%d:%d:%d\n", all, noctty, leader, nselectors, extended); +#endif + /* + * scan requested variables, noting what structures are needed, + * and adjusting header widths as appropriate. + */ + scanvars(); + + /* + * get proc list + */ + what = showthreads ? KERN_PROC_ALL : KERN_PROC_PROC; + flag = 0; + if (nselectors == 1) { + for (list = lists; list->type != _LTYPE; list++) + if (list->count && list->op) { + what = list->op | showthreads; + flag = (int)((int *)list->l.ptr)[0]; + nselectors = 0; + break; } + } + + /* + * select procs + */ + nentries = -1; + kp = kvm_getprocs(kd, what, flag, &nentries); + if (!kp || nentries < 0) + errx(1, "%s", kvm_geterr(kd)); + nkept = 0; + if (nentries > 0) { + if ((kinfo = malloc(nentries * sizeof(*kinfo))) == NULL) + errx(1, "malloc failed"); + for (i = nentries; --i >= 0; ++kp) + if (findproc(kp, nselectors)) { + kinfo[nkept].ki_p = kp; + if (needuser) + saveuser(kd, &kinfo[nkept]); + dynsizevars(&kinfo[nkept]); + nkept++; + } + } +#if defined(DEBUG) +fprintf(stderr, "what:flag:nentries:nkept == %d:%d:%d:%d\n", + what, flag, nentries, nkept); +#endif + sizevars(); + + /* + * print header + */ + printheader(); + + /* + * oops! no proc, goodbye. + */ + if (nkept == 0) + exit(1); + + /* + * sort proc list + */ + qsort(kinfo, nkept, sizeof(KINFO), pscomp); + + /* + * for each proc, call each variable output function. + */ + for (i = lineno = 0; i < nkept; i++) { + STAILQ_FOREACH(vent, &varlist, list) { + (vent->var->oproc)(&kinfo[i], vent); + if (STAILQ_NEXT(vent, list) != NULL) + (void)putchar(' '); + } + (void)putchar('\n'); + if (prtheader && lineno++ == prtheader - 4) { + (void)putchar('\n'); + printheader(); + lineno = 0; } - if (nocludge == 0) - argv[1] = kludge_oldps_options(argv[1]); } +#ifdef notneeded + for (i = 0; i < lists[CMD].count; i++) + free(lists[CMD].l.cmds[i]); + for (list = lists; list->type != _LTYPE; list++) + free(list->l.ptr); +#endif + exit(eval); +} - all = dropgid = _fmt = nselectors = optfatal = 0; - prtheader = showthreads = wflag = xkeep_implied = 0; - xkeep = -1; /* Neither -x nor -X. */ - init_list(&gidlist, addelem_gid, sizeof(gid_t), "group"); - init_list(&pgrplist, addelem_pid, sizeof(pid_t), "process group"); - init_list(&pidlist, addelem_pid, sizeof(pid_t), "process id"); - init_list(&ruidlist, addelem_uid, sizeof(uid_t), "ruser"); - init_list(&sesslist, addelem_pid, sizeof(pid_t), "session id"); - init_list(&ttylist, addelem_tty, sizeof(dev_t), "tty"); - init_list(&uidlist, addelem_uid, sizeof(uid_t), "user"); - memf = nlistf = _PATH_DEVNULL; - while ((ch = getopt(argc, argv, PS_ARGS)) != -1) - switch((char)ch) { - case 'A': - /* - * Exactly the same as `-ax'. This has been - * added for compatability with SUSv3, but for - * now it will not be described in the man page. - */ - nselectors++; - all = xkeep = 1; +int +findproc(const struct kinfo_proc *kp, int nselectors) +{ + int i; + + for (i = 0; i < lists[PID].count; i++) + if (kp->ki_pid == lists[PID].l.pids[i]) + return(1); + if ( +#if defined(WITH_CTTY) + (!noctty && !(kp->ki_kiflag & KI_CTTY)) || +#else + (!posix && !noctty && + (kp->ki_tdev == NODEV || !(kp->ki_flag & P_CONTROLT))) || + (posix && !noctty && !(kp->ki_kiflag & KI_CTTY)) || +#endif + (!leader && (kp->ki_kiflag & KI_SLEADER))) + return(0); + if (!nselectors) + return(1); +#if 1 + { + LIST *list; + + for (list = lists; list->type != _LTYPE; list++) + switch (list->type) { + case FMT: + case PID: + continue; + case CMD: + for (i = 0; i < list->count; i++) + if (!strcmp((const char *)((const char *)kp + + list->off), + ((const char **)list->l.ptr)[i])) + return(1); break; + default: + for (i = 0; i < list->count; i++) + if (*(const u_int *)((const char *)kp + + list->off) + == ((const u_int *)list->l.ptr)[i]) + return(1); + } + } +#else + for (i = 0; i < lists[UID].count; i++) + if (kp->ki_uid == lists[UID].l.uids[i]) + return(1); + for (i = 0; i < lists[TTY].count; i++) + if (kp->ki_tdev == lists[TTY].l.ttys[i]) + return(1); + for (i = 0; i < lists[GID].count; i++) + if (kp->ki_rgid == lists[GID].l.gids[i]) + return(1); + for (i = 0; i < lists[PGID].count; i++) + if (kp->ki_pgid == lists[PGID].l.pids[i]) + return(1); + for (i = 0; i < lists[RUID].count; i++) + if (kp->ki_ruid == lists[RUID].l.uids[i]) + return(1); + for (i = 0; i < lists[SID].count; i++) + if (kp->ki_sid == lists[SID].l.pids[i]) + return(1); + for (i = 0; i < lists[CMD].count; i++) + if (!strcmp(kp->ki_comm, lists[CMD].l.cmds[i])) + return(1); +#endif + return(0); +} + +static void +parseopts(int argc, char *const *argv) +{ + static char bsd_opts[] = "AaCcefG:gHhjLlM:mN:nO:o:p:QqrSsTt:U:uvwxXZ"; + static char psx_opts[] = "AaC:cdefG:g:jLlMmn:O:o:Pp:Qqs:Tt:u:U:xyz"; + /* internal format syntax: + * keyword[:flags in reverse polish notation][=header] + * reverse polish notation: + * x by itself, * means any, x! means not x, + * xy| means x | y, xy& means x & y, xy^ means x ^ y. + * [xyz] is a more compact and readable form of xy|z| + * [a-z] isn't supported and is understood as a-|z| + * () may be used for readability but are no-op. + * bsd flags: d12jlsuv, bsd modifiers: nZ + * posix flags: d12fjl, posix modifiers: cMPy + * both modifiers: X if extended. + */ +/* +Z: prepend label +d: pid tt state time command +j: user pid ppid pgid (sid) jobc state tt time command +l: uid pid ppid cpu pri nice vsz rss mwchan state tt time command +n: user -> uid, mwchan -> nwchan +u: user pid %cpu %mem vsz rss tt state start time command +s: uid pid sig sigmask sigignore sigcatch state tt command +v: pid (tt) state time sl re pagein vsz rss lim tsiz %cpu %mem command +*/ + static char bsd_fmtstr[] = "\ +label:Z user:[ju](n!)& uid:([ju]n&)([ls]([ju]!)&)| pid:2! ppid:[jl] \ +pgid:j sid:jX& tpgid:jX& jobc:j sig:s sigmask:s sigignore:s sigcatch:s \ +cpu:l pri:l nice:l %cpu:u %mem:u vsz:[lu] rss:[lu] mwchan:l(n!)& \ +nwchan:ln& tt:[d2u](vX&)| state:1! tt:[jls]([du]!)& start:u time:1! \ +sl:v re:v pagein:v vsz:v([lu]!)& rss:v([lu]!)& lim:v tsiz:v %cpu:v(u!)& \ +%mem:v(u!)& command:1! \ +"; +/* +M: prepend label +P: add psr +c: omit nice, c -> cls +d: pid tty time comm +f: user pid ppid c stime tty time args +j: pid ppid pgid sid tty time comm +l: f s uid pid ppid c pri nice addr sz wchan tty time comm +y: omit f, addr -> rss +*/ + static char psx_fmtstr[] = "\ +label:M f:l(y!)& s:l user:f uid:l(f!)& pid:2! ppid:[fjl] pgid:j \ +sid:j tpgid:jX& c:[fl](c!)& class:[fl]c& psr:P pri:l nice:l(c!)& \ +addr:l(y!)& rss:ly& sz:l wchan:l stime:f tty:1! time:1! comm:[1f]! \ +args:f \ +"; + + int c; + int cflag; /* -c */ + int fflag; /* -f */ + int jflag; /* -j */ + int lflag; /* -l */ + int Mflag; /* -M */ + int nflag; /* -n */ + int Oflag; /* -O */ + int oflag; /* -o */ + int Pflag; /* -P */ + int sflag; /* -s */ + int uflag; /* -u */ + int vflag; /* -v */ + int wflag; /* -w */ + int yflag; /* -y */ + int Zflag; /* -Z */ + + cflag = fflag = jflag = lflag = Mflag = nflag = Oflag = oflag = 0; + Pflag = sflag = uflag = vflag = wflag = yflag = Zflag = 0; + + while ((c = getopt(argc, argv, posix ? psx_opts : bsd_opts)) != -1) + switch(c) { case 'a': - nselectors++; + /* + * posix : show all processes with a controlling + * terminal except session leader processes. + * bsd : show all processes with a controlling + * terminal. in sunos compat mode (-Q), + * don't show session leader processes. + */ all = 1; break; case 'C': - rawcpu = 1; + /* + * posix (hpux) : -C cmdlist + * select processes by command name. + * bsd : raw cpu usage instead of the average one. + */ + if (posix) + parselst(optarg, &lists[CMD]); + else + rawcpu = 1; break; case 'c': - cflag = 1; + /* + * posix (!susv3) : omits the nice field and + * replaces the c field with the class field. + * bsd : a sort of replaces the command field + * with the ucomm field. + */ + if (posix) + cflag = 1; + else + commandonly = 1; break; - case 'e': /* XXX set ufmt */ - needenv = 1; + case 'd': + /* + * posix only : show all processes except session + * leader processes. + */ + all = noctty = 1; break; -#ifdef LAZY_PS - case 'f': - if (getuid() == 0 || getgid() == 0) - forceuread = 1; + case 'e': + /* + * posix : show all processes. + * bsd : display the environment. + */ + if (posix) + case 'A': + /* + * posix / bsd : show all processes. + */ + all = noctty = leader = 1; + else + needenv = 1; break; + case 'f': + /* + * posix : user oriented output. + * bsd : read from swapped out processes. + */ + if (posix) { + fflag = 1; + if (termwidth < 131) + termwidth = 131; + } else +#if defined(LAZY_PS) + if (getuid() == 0 || getgid() == 0) + forceuread = 1; +#else + usage(c); #endif + break; case 'G': - add_list(&gidlist, optarg); - xkeep_implied = 1; - nselectors++; + /* + * posix / bsd : -G grouplist + * select processes by effective group name(s) + * or effective group id(s). + */ + parselst(optarg, &lists[GID]); break; case 'g': -#if 0 - /*- - * XXX - This SUSv3 behavior is still under debate - * since it conflicts with the (undocumented) - * `-g' option. So we skip it for now. - */ - add_list(&pgrplist, optarg); - xkeep_implied = 1; - nselectors++; - break; -#else + /* + * posix : -g pgidlist + * select processes by process group id(s). + * bsd : show all processes with a controlling + * terminal and session leader processes. + */ + if (posix) + parselst(optarg, &lists[PGID]); + else /* The historical BSD-ish (from SunOS) behavior. */ - break; /* no-op */ -#endif + all = leader = 1; + break; case 'H': + /* + * bsd only (freebsd) : show threads. + */ showthreads = KERN_PROC_INC_THREAD; break; case 'h': + /* + * bsd only : repeats the header line. + */ prtheader = ws.ws_row > 5 ? ws.ws_row : 22; break; case 'j': - parsefmt(jfmt, 0); - _fmt = 1; - jfmt[0] = '\0'; + /* + * posix (!susv3) / bsd : job control oriented output. + */ + jflag = 1; break; +#if 0 + case 'k': + /* + * bsd only : conflicts! + * netbsd : -k keylist + * sort by the specified keyword(s). + * openbsd : don't show kernel thread. + * proposed : -k keylist and -K ? + */ + break; +#endif case 'L': - showkey(); - exit(0); + /* + * posix (solaris) : show threads. + * bsd : show keywords. + */ + if (posix) + showthreads = KERN_PROC_INC_THREAD; + else { + showkey(); + exit(0); + } case 'l': - parsefmt(lfmt, 0); - _fmt = 1; - lfmt[0] = '\0'; + /* + * posix / bsd : long format output. + */ + lflag = 1; break; case 'M': - memf = optarg; - dropgid = 1; + /* + * posix (irix) : prepend the label field. + * bsd : -M core + * specifies an alternative core file. + */ + if (posix) + Mflag = 1; + else { + memf = optarg; + dropgid = 1; + } break; case 'm': - sortby = SORTMEM; + /* + * posix (tru64) : show threads. + * bsd : sort by memory usage. + */ + if (posix) + showthreads = KERN_PROC_INC_THREAD; + else + sortby = SORTMEM; + break; + case 'n': + /* + * posix : -n nlist [-n core] + * specifies an alternative system namelist file + * (and core file). + * bsd : numerical output of the user and wchan fields. + */ + if (posix) { + if (nlistf == devnull) + nlistf = optarg; + else if (memf == devnull) + memf = optarg; + dropgid = 1; + } else + nflag = 1; break; case 'N': + /* + * bsd only : -N nlist + * specifies an alternative system namelist file. + */ nlistf = optarg; dropgid = 1; break; case 'O': - parsefmt(o1, 1); - parsefmt(optarg, 1); - parsefmt(o2, 1); - o1[0] = o2[0] = '\0'; - _fmt = 1; - break; + /* + * user defined output. + * posix : same as -o pid,fmtlist,tty,time,comm + * bsd : same as -o pid,fmtlist,tt,state,time,command + */ + Oflag = 1; + /* FALLTHROUGH */ case 'o': - parsefmt(optarg, 1); - _fmt = 1; + /* + * user defined output. + * posix / bsd : -o fmtlist + * posix only : -o help behaves as bsd -L. + */ + if (posix && !strcmp(optarg, "help")) { + showkey(); + exit(0); + } + parselst(optarg, &lists[FMT]); + oflag = 1; + break; + case 'P': + /* + * posix (solaris) : includes the psr field. + */ + Pflag = 1; break; case 'p': - add_list(&pidlist, optarg); /* - * Note: `-p' does not *set* xkeep, but any values - * from pidlist are checked before xkeep is. That - * way they are always matched, even if the user - * specifies `-X'. + * posix / bsd : -p pidlist + * select processes by process id(s). */ - nselectors++; + parselst(optarg, &lists[PID]); break; -#if 0 - case 'R': - /*- - * XXX - This un-standard option is still under - * debate. This is what SUSv3 defines as - * the `-U' option, and while it would be - * nice to have, it could cause even more - * confusion to implement it as `-R'. - */ - add_list(&ruidlist, optarg); - xkeep_implied = 1; - nselectors++; + case 'Q': + /* + * enable or disable compatibility with sunos and tru64 + * sunos : leader processes aren't selected by default, + * uses -g to select them. + * tru64 : displays size in KMG format. + * ex. : 1.54M instead of 1580 + */ + extended = !extended; + break; + case 'q': + /* + * enable or disable the posix compatibility. + */ + posix = !posix; break; -#endif case 'r': + /* + * bsd only : sort by cpu usage. + */ sortby = SORTCPU; break; case 'S': + /* + * bsd only : accumulates children times. + */ sumrusage = 1; break; -#if 0 case 's': - /*- - * XXX - This non-standard option is still under - * debate. This *is* supported on Solaris, - * Linux, and IRIX, but conflicts with `-s' - * on NetBSD and maybe some older BSD's. - */ - add_list(&sesslist, optarg); - xkeep_implied = 1; - nselectors++; + /* + * posix : -s sidlist + * select processes by session id(s). + * bsd (bsd43) : same as -o SFMT + * signal oriented output. + * PS : conflict w/ NetBSD -s (show thread). + */ + if (posix) + parselst(optarg, &lists[SID]); + else + sflag = 1; break; -#endif case 'T': + /* + * posix (irix) : show threads. + * bsd : same as -t `tty` + */ + if (posix) { + showthreads = KERN_PROC_INC_THREAD; + break; + } if ((optarg = ttyname(STDIN_FILENO)) == NULL) errx(1, "stdin: not a terminal"); /* FALLTHROUGH */ case 't': - add_list(&ttylist, optarg); - xkeep_implied = 1; - nselectors++; + /* + * posix/bsd : -t ttylist + * select processes by terminal name(s). + */ + parselst(optarg, &lists[TTY]); break; case 'U': - /* This is what SUSv3 defines as the `-u' option. */ - add_list(&uidlist, optarg); - xkeep_implied = 1; - nselectors++; + /* + * posix : -U ruserlist + * select processes by real user name(s) + * or real user id(s). + * bsd : -U userlist + * select processes by effective user name(s) + * or effective user id(s). + */ + if (posix) + parselst(optarg, &lists[RUID]); + else + parselst(optarg, &lists[UID]); break; case 'u': - parsefmt(ufmt, 0); - sortby = SORTCPU; - _fmt = 1; - ufmt[0] = '\0'; + /* + * posix : -U userlist + * select processes by effective user name(s) + * or effective user id(s). + * bsd : same as -o UFMT -r + * user oriented output. + */ + if (posix) + parselst(optarg, &lists[RUID]); + else { + sortby = SORTCPU; + uflag = 1; + } break; case 'v': - parsefmt(vfmt, 0); + /* + * bsd only : same as -o VFMT -m + * virtual memory oriented output. + */ sortby = SORTMEM; - _fmt = 1; - vfmt[0] = '\0'; + vflag = 1; break; case 'w': + /* + * bsd only : wide format output. + */ if (wflag) termwidth = UNLIMITED; else if (termwidth < 131) termwidth = 131; - wflag++; + wflag = 1; break; case 'X': /* - * Note that `-X' and `-x' are not standard "selector" - * options. For most selector-options, we check *all* - * processes to see if any are matched by the given - * value(s). After we have a set of all the matched - * processes, then `-X' and `-x' govern whether we - * modify that *matched* set for processes which do - * not have a controlling terminal. `-X' causes - * those processes to be deleted from the matched - * set, while `-x' causes them to be kept. + * bsd only : don't show processes without a + * controlling terminal (opposite of -x). */ - xkeep = 0; + noctty = 0; break; case 'x': - xkeep = 1; + /* + * posix (hpux) : wide format output. + * bsd : show processes without a controlling + * terminal. + */ + if (posix) + termwidth = UNLIMITED; + else + noctty = 1; + break; + case 'y': + /* + * posix (solaris) : omits the f field and replaces + * the addr field with the rss field. + */ + yflag = 1; break; case 'Z': - parsefmt(Zfmt, 0); - Zfmt[0] = '\0'; + /* + * bsd only : prepend the label field. + */ + Zflag = 1; break; case '?': default: - usage(); + usage(0); } + argc -= optind; argv += optind; - if (optfatal) - exit(1); /* Error messages already printed. */ - if (xkeep < 0) /* Neither -X nor -x was specified. */ - xkeep = xkeep_implied; - -#define BACKWARD_COMPATIBILITY -#ifdef BACKWARD_COMPATIBILITY - if (*argv) { - nlistf = *argv; - if (*++argv) - memf = *argv; - } -#endif - /* - * Discard setgid privileges if not the running kernel so that bad - * guys can't print interesting stuff from kernel memory. - */ - if (dropgid) { - setgid(getgid()); - setuid(getuid()); - } - - kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf); - if (kd == 0) - errx(1, "%s", errbuf); - if (!_fmt) - parsefmt(dfmt, 0); + if (oflag) { + if ((posix && Mflag) || (!posix && Zflag)) + parsefmt(posix ? psx_fmtstr : bsd_fmtstr, + posix ? "M" : "Z"); + if (Oflag) + parsefmt(posix ? psx_fmtstr : bsd_fmtstr, "1"); + parsefmt(lists[FMT].l.fmt, ""); + if (Oflag) + parsefmt(posix ? psx_fmtstr : bsd_fmtstr, "2"); + } else { + char flags[FMTLEN]; /* lsuvjnZT\0 | fljcMPyT\0 */ + char *cp; - if (nselectors == 0) { - uidlist.l.ptr = malloc(sizeof(uid_t)); - if (uidlist.l.ptr == NULL) - errx(1, "malloc failed"); - nselectors = 1; - uidlist.count = uidlist.maxcount = 1; - *uidlist.l.uids = getuid(); + cp = flags; + if (posix) { + if (fflag) + *cp++ = 'f'; + if (lflag) + *cp++ = 'l'; + } else { + if (lflag) + *cp++ = 'l'; + if (sflag) + *cp++ = 's'; + if (uflag) + *cp++ = 'u'; + if (vflag) + *cp++ = 'v'; + } + if (jflag) + *cp++ = 'j'; + if (cp == flags) + *cp++ = 'd'; + if (posix) { + if (cflag) + *cp++ = 'c'; + if (Mflag) + *cp++ = 'M'; + if (Pflag) + *cp++ = 'P'; + if (yflag) + *cp++ = 'y'; + } else { + if (nflag) + *cp++ = 'n'; + if (Zflag) + *cp++ = 'Z'; + } + if (extended) + *cp++ = 'X'; + *cp = '\0'; + parsefmt(posix ? psx_fmtstr : bsd_fmtstr, flags); } +} - /* - * scan requested variables, noting what structures are needed, - * and adjusting header widths as appropriate. - */ - scanvars(); +static void +parselst(const char *arg, LIST *list) +{ + const char *sep; + char *cmd; + /* const char *fmt; */ + /* gid_t gid; */ + struct group *grp; + pid_t pid; + /* dev_t tty; */ + struct stat sb; + const char *ttypath; + /* uid_t uid; */ + struct passwd *pwd; + size_t len, lo, hi, need; + char buf[MAX(MAX(MAXCOMLEN + 1, MAXLOGNAME), SPECNAMELEN + 1)]; + char pathbuf[SPECNAMELEN + 1]; + char *endp; + int eno; + u_long u_tmp; + long tmp; + + /* fmt = NULL; gid = 0; tty = 0; uid = 0; */ + cmd = NULL; grp = NULL; pwd = NULL; pid = 0; + sep = fmtsep; /* - * Get process list. If the user requested just one selector- - * option, then kvm_getprocs can be asked to return just those - * processes. Otherwise, have it return all processes, and - * then this routine will search that full list and select the - * processes which match any of the user's selector-options. + * format string which contains both an equal sign and a comma + * are considered to be comma only separated list. so, both + * -o "pid=process id,ppid=parent pid,command=command line" and + * -o "pid=child ppid=parent command=command" are well parsed, + * unfortunately, mixing "user=user name,pid=pid command" isn't... */ - what = showthreads != 0 ? KERN_PROC_ALL : KERN_PROC_PROC; - flag = 0; - if (nselectors == 1) { - /* XXX - Apparently there's no KERN_PROC_GID flag. */ - if (pgrplist.count == 1) { - what = KERN_PROC_PGRP | showthreads; - flag = *pgrplist.l.pids; - nselectors = 0; - } else if (pidlist.count == 1) { - what = KERN_PROC_PID | showthreads; - flag = *pidlist.l.pids; - nselectors = 0; - } else if (ruidlist.count == 1) { - what = KERN_PROC_RUID | showthreads; - flag = *ruidlist.l.uids; - nselectors = 0; -#if 0 - /*- - * XXX - KERN_PROC_SESSION causes error in kvm_getprocs? - * For now, always do sid-matching in this routine. - */ - } else if (sesslist.count == 1) { - what = KERN_PROC_SESSION | showthreads; - flag = *sesslist.l.pids; - nselectors = 0; -#endif - } else if (ttylist.count == 1) { - what = KERN_PROC_TTY | showthreads; - flag = *ttylist.l.ttys; - nselectors = 0; - } else if (uidlist.count == 1) { - what = KERN_PROC_UID | showthreads; - flag = *uidlist.l.uids; - nselectors = 0; - } else if (all) { - /* No need for this routine to select processes. */ - nselectors = 0; - } - } + if (list->type == FMT && strchr(arg, '=') && strchr(arg, ',')) + sep++; /* comma only, no white space */ - /* - * select procs - */ - nentries = -1; - kp = kvm_getprocs(kd, what, flag, &nentries); - if ((kp == NULL && nentries > 0) || (kp != NULL && nentries < 0)) - errx(1, "%s", kvm_geterr(kd)); - nkept = 0; - if (nentries > 0) { - if ((kinfo = malloc(nentries * sizeof(*kinfo))) == NULL) - errx(1, "malloc failed"); - for (i = nentries; --i >= 0; ++kp) { + for (arg += strspn(arg, sep); + (len = strcspn(arg, sep)) > 0; + arg += len + strspn(arg + len, sep)) { + list->noval = 0; + need = 1; + + switch (list->type) { + case CMD: /* - * If the user specified multiple selection-criteria, - * then keep any process matched by the inclusive OR - * of all the selection-criteria given. - */ - if (pidlist.count > 0) { - for (elem = 0; elem < pidlist.count; elem++) - if (kp->ki_pid == pidlist.l.pids[elem]) - goto keepit; + * allowed command name must be smaller than MAXCOMLEN. + */ + if (len > MAXCOMLEN) { + warnx("%.*s: %s name too long", (int)len, arg, + list->msg); + fatal = 1; + continue; } + (void)strlcpy(buf, arg, len + 1); + cmd = strdup(buf); + if (!cmd) + errx(1, "malloc failed"); + break; + case FMT: + fmt_hack: + /* fmt = arg; */ + need += len + 1; + break; + case GID: + case RUID: + case UID: /* - * Note that we had to process pidlist before - * filtering out processes which do not have - * a controlling terminal. - */ - if (xkeep == 0) { - if ((kp->ki_tdev == NODEV || - (kp->ki_flag & P_CONTROLT) == 0)) - continue; + * allowed group or user name must be smaller than + * MAXLOGNAME and gid or uid must be a positive integer + * between 0 and GID_MAX or UID_MAX respectively. + */ + if (len >= MAXLOGNAME) { + warnx("%.*s: %s name too long", (int)len, arg, + list->msg); + fatal = 1; + continue; } - if (nselectors == 0) - goto keepit; - if (gidlist.count > 0) { - for (elem = 0; elem < gidlist.count; elem++) - if (kp->ki_rgid == gidlist.l.gids[elem]) - goto keepit; + (void)strlcpy(buf, arg, len + 1); + errno = 0; + u_tmp = strtoul(buf, &endp, 10); + eno = errno; + if ((list->type == GID && + !((grp = getgrnam(buf)) != NULL || + ((endp != buf && *endp == '\0' && + (gid_t)u_tmp <= GID_MAX) && + (grp = getgrgid((gid_t)u_tmp)) != NULL))) || + (list->type != GID && + !((pwd = getpwnam(buf)) != NULL || + ((endp != buf && *endp == '\0' && + (uid_t)u_tmp <= UID_MAX) && + (pwd = getpwuid((uid_t)u_tmp)) != NULL)))) { + if (endp == buf) + warnx("%s: no such %s name", buf, + list->msg); + else if (eno == ERANGE) { + errno = eno; + warnx("%s: %s id too large", buf, + list->msg); + } else + warnx("%s: invalid %s id", buf, + list->msg); + fatal = 1; + continue; } - if (pgrplist.count > 0) { - for (elem = 0; elem < pgrplist.count; elem++) - if (kp->ki_pgid == - pgrplist.l.pids[elem]) - goto keepit; + /* + if (list->type == GID) + gid = grp->gr_gid; + else + uid = pwd->pw_uid; + */ + break; + case PGID: + case PID: + case SID: + /* + * allowed p*id must be a positive integer + * between 0 and PID_MAX. + */ + if (len >= sizeof(buf)) { + warnx("%.*s: %s too long", (int)len, arg, + list->msg); + fatal = 1; + continue; } - if (ruidlist.count > 0) { - for (elem = 0; elem < ruidlist.count; elem++) - if (kp->ki_ruid == - ruidlist.l.uids[elem]) - goto keepit; + (void)strlcpy(buf, arg, len + 1); + errno = 0; + tmp = strtol(buf, &endp, 10); + if (errno == ERANGE) { + warnx("%.*s: %s too large", (int)len, arg, + list->msg); + fatal = 1; + continue; + } else if (endp == buf || *endp || + tmp < 0 || tmp > PID_MAX) { + warnx("%.*s: invalid %s", (int)len, arg, + list->msg); + fatal = 1; + continue; } - if (sesslist.count > 0) { - for (elem = 0; elem < sesslist.count; elem++) - if (kp->ki_sid == sesslist.l.pids[elem]) - goto keepit; + pid = (pid_t)tmp; + break; + case TTY: + /* + * allowed terminal name are : + * co -> /dev/console + * XX -> /dev/ttyXX + * ttyXX -> /dev/ttyXX + * /dev/ttyXX -> /dev/ttyXX + */ + if ((len + (strlen(_PATH_TTY) * *arg != '/')) + > SPECNAMELEN) { + warnx("%.*s: path name too long", + (int)len, arg); + fatal = 1; + continue; } - if (ttylist.count > 0) { - for (elem = 0; elem < ttylist.count; elem++) - if (kp->ki_tdev == ttylist.l.ttys[elem]) - goto keepit; + (void)strlcpy(buf, arg, len + 1); + if (strcmp(buf, "co") == 0) + ttypath = _PATH_CONSOLE; + else if (*buf != '/') { + (void)snprintf(pathbuf, + sizeof(buf), "%s%s", + !strncmp(buf, "tty", 3) ? _PATH_DEV : + _PATH_TTY, buf); + ttypath = pathbuf; + } else + ttypath = buf; + if (stat(ttypath, &sb) == -1) { + warn("%s", ttypath); + fatal = 1; + continue; } - if (uidlist.count > 0) { - for (elem = 0; elem < uidlist.count; elem++) - if (kp->ki_uid == uidlist.l.uids[elem]) - goto keepit; + if (!S_ISCHR(sb.st_mode)) { + warnx("%s: not a %s", ttypath, list->msg); + fatal = 1; + continue; } - /* - * This process did not match any of the user's - * selector-options, so skip the process. - */ - continue; - - keepit: - kinfo[nkept].ki_p = kp; - if (needuser) - saveuser(&kinfo[nkept]); - dynsizevars(&kinfo[nkept]); - nkept++; + /* tty = sb.st_rdev; */ + break; + default: + break; } - } - sizevars(); - - /* - * print header - */ - printheader(); - if (nkept == 0) - exit(1); - - /* - * sort proc list - */ - qsort(kinfo, nkept, sizeof(KINFO), pscomp); - /* - * For each process, call each variable output function. - */ - for (i = lineno = 0; i < nkept; i++) { - for (vent = vhead; vent; vent = vent->next) { - (vent->var->oproc)(&kinfo[i], vent); - if (vent->next != NULL) - (void)putchar(' '); - } - (void)putchar('\n'); - if (prtheader && lineno++ == prtheader - 4) { - (void)putchar('\n'); - printheader(); - lineno = 0; + lo = rounddown(list->count + need, BUCKETS); + hi = roundup2(list->count, BUCKETS); + if (lo >= hi) { + int n = roundup2(list->count + need, BUCKETS); + if ((list->l.ptr = realloc(list->l.ptr, n * list->size)) + == NULL) + errx(1, "realloc failed"); } - } - free_list(&gidlist); - free_list(&pidlist); - free_list(&pgrplist); - free_list(&ruidlist); - free_list(&sesslist); - free_list(&ttylist); - free_list(&uidlist); - - exit(eval); -} - -static int -addelem_gid(struct listinfo *inf, const char *elem) -{ - struct group *grp; - const char *nameorID; - char *endp; - u_long bigtemp; - - if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) { - if (*elem == '\0') - warnx("Invalid (zero-length) %s name", inf->lname); - else - warnx("%s name too long: %s", inf->lname, elem); - optfatal = 1; - return (0); /* Do not add this value. */ - } - - /* - * SUSv3 states that `ps -G grouplist' should match "real-group - * ID numbers", and does not mention group-names. I do want to - * also support group-names, so this tries for a group-id first, - * and only tries for a name if that doesn't work. This is the - * opposite order of what is done in addelem_uid(), but in - * practice the order would only matter for group-names which - * are all-numeric. - */ - grp = NULL; - nameorID = "named"; - errno = 0; - bigtemp = strtoul(elem, &endp, 10); - if (errno == 0 && *endp == '\0' && bigtemp <= GID_MAX) { - nameorID = "name or ID matches"; - grp = getgrgid((gid_t)bigtemp); - } - if (grp == NULL) - grp = getgrnam(elem); - if (grp == NULL) { - warnx("No %s %s '%s'", inf->lname, nameorID, elem); - optfatal = 1; - return (0); /* Do not add this value. */ - } - if (inf->count >= inf->maxcount) - expand_list(inf); - inf->l.gids[(inf->count)++] = grp->gr_gid; - return (1); -} - -#define BSD_PID_MAX 99999 /* Copy of PID_MAX from sys/proc.h. */ -static int -addelem_pid(struct listinfo *inf, const char *elem) -{ - char *endp; - long tempid; - - if (*elem == '\0') - tempid = 0L; - else { - errno = 0; - tempid = strtol(elem, &endp, 10); - if (*endp != '\0' || tempid < 0 || elem == endp) { - warnx("Invalid %s: %s", inf->lname, elem); - errno = ERANGE; - } else if (errno != 0 || tempid > BSD_PID_MAX) { - warnx("%s too large: %s", inf->lname, elem); - errno = ERANGE; - } - if (errno == ERANGE) { - optfatal = 1; - return (0); /* Do not add this value. */ + switch (list->type) { + case CMD: + list->l.cmds[list->count++] = cmd; + break; + case FMT: + list->l.fmt[list->count++] = ','; + strlcpy(list->l.fmt + list->count, /*fmt*/ arg, + len + 1); + list->count += len; + break; + case GID: + list->l.gids[list->count++] = /*gid*/ grp->gr_gid; + break; + case PGID: + case PID: + case SID: + list->l.pids[list->count++] = pid; + break; + case TTY: + list->l.ttys[list->count++] = /*tty*/ sb.st_rdev; + break; + case RUID: + case UID: + list->l.uids[list->count++] = /*uid*/ pwd->pw_uid; + break; + default: + break; } } - if (inf->count >= inf->maxcount) - expand_list(inf); - inf->l.pids[(inf->count)++] = tempid; - return (1); -} -#undef BSD_PID_MAX - -static int -addelem_tty(struct listinfo *inf, const char *elem) -{ - const char *ttypath; - struct stat sb; - char pathbuf[PATH_MAX]; - - if (strcmp(elem, "co") == 0) - ttypath = strdup(_PATH_CONSOLE); - else if (*elem == '/') - ttypath = elem; - else { - strlcpy(pathbuf, _PATH_TTY, sizeof(pathbuf)); - strlcat(pathbuf, elem, sizeof(pathbuf)); - ttypath = pathbuf; - } - - if (stat(ttypath, &sb) == -1) { - warn("%s", ttypath); - optfatal = 1; - return (0); /* Do not add this value. */ - } - if (!S_ISCHR(sb.st_mode)) { - warn("%s: Not a terminal", ttypath); - optfatal = 1; - return (0); /* Do not add this value. */ + switch (list->type) { + case GID: + endgrent(); + break; + case UID: + case RUID: + endpwent(); + break; + default: + break; } - if (inf->count >= inf->maxcount) - expand_list(inf); - inf->l.ttys[(inf->count)++] = sb.st_rdev; - return (1); -} - -static int -addelem_uid(struct listinfo *inf, const char *elem) -{ - struct passwd *pwd; - char *endp; - u_long bigtemp; - - if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) { - if (*elem == '\0') - warnx("Invalid (zero-length) %s name", inf->lname); - else - warnx("%s name too long: %s", inf->lname, elem); - optfatal = 1; - return (0); /* Do not add this value. */ - } - - pwd = getpwnam(elem); - if (pwd == NULL) { - errno = 0; - bigtemp = strtoul(elem, &endp, 10); - if (errno != 0 || *endp != '\0' || bigtemp > UID_MAX) - warnx("No %s named '%s'", inf->lname, elem); - else { - /* The string is all digits, so it might be a userID. */ - pwd = getpwuid((uid_t)bigtemp); - if (pwd == NULL) - warnx("No %s name or ID matches '%s'", - inf->lname, elem); - } - } - if (pwd == NULL) { - /* - * These used to be treated as minor warnings (and the - * option was simply ignored), but now they are fatal - * errors (and the command will be aborted). - */ - optfatal = 1; - return (0); /* Do not add this value. */ - } - - if (inf->count >= inf->maxcount) - expand_list(inf); - inf->l.uids[(inf->count)++] = pwd->pw_uid; - return (1); -} - -static void -add_list(struct listinfo *inf, const char *argp) -{ - const char *savep; - char *cp, *endp; - int toolong; - char elemcopy[PATH_MAX]; - - while (*argp != '\0') { - while (*argp != '\0' && strchr(W_SEP, *argp) != NULL) - argp++; - savep = argp; - toolong = 0; - cp = elemcopy; - if (strchr(T_SEP, *argp) == NULL) { - endp = elemcopy + sizeof(elemcopy) - 1; - while (*argp != '\0' && cp <= endp && - strchr(W_SEP T_SEP, *argp) == NULL) - *cp++ = *argp++; - if (cp > endp) - toolong = 1; - } - if (!toolong) { - *cp = '\0'; -#ifndef ADD_PS_LISTRESET - /* - * This is how the standard expects lists to - * be handled. - */ - inf->addelem(inf, elemcopy); -#else - /*- - * This would add a simple non-standard-but-convienent - * feature. - * - * XXX - The first time I tried to add this check, - * it increased the total size of `ps' by 3940 - * bytes on i386! That's 12% of the entire - * program! The `ps.o' file grew by only about - * 40 bytes, but the final (stripped) executable - * in /bin/ps grew by 12%. I have not had time - * to investigate, so skip the feature for now. - */ - /* - * We now have a single element. Add it to the - * list, unless the element is ":". In that case, - * reset the list so previous entries are ignored. - */ - if (strcmp(elemcopy, ":") == 0) - inf->count = 0; - else - inf->addelem(inf, elemcopy); -#endif - } else { - /* - * The string is too long to copy. Find the end - * of the string to print out the warning message. - */ - while (*argp != '\0' && strchr(W_SEP T_SEP, - *argp) == NULL) - argp++; - warnx("Value too long: %.*s", (int)(argp - savep), - savep); - optfatal = 1; - } + /* + * mark blank only, comma only or empty argument for later check. + */ + if (!list->count) { /* - * Skip over any number of trailing whitespace characters, - * but only one (at most) trailing element-terminating - * character. + * format string must not be null. */ - while (*argp != '\0' && strchr(W_SEP, *argp) != NULL) - argp++; - if (*argp != '\0' && strchr(T_SEP, *argp) != NULL) { - argp++; - /* Catch case where string ended with a comma. */ - if (*argp == '\0') - inf->addelem(inf, argp); - } - } -} - -static void * -expand_list(struct listinfo *inf) -{ - void *newlist; - int newmax; - - newmax = (inf->maxcount + 1) << 1; - newlist = realloc(inf->l.ptr, newmax * inf->elemsize); - if (newlist == NULL) { - free(inf->l.ptr); - errx(1, "realloc to %d %ss failed", newmax, - inf->lname); + if (list->type == FMT) { + need = 1; + goto fmt_hack; + } else + list->noval = 1; } - inf->maxcount = newmax; - inf->l.ptr = newlist; - - return (newlist); -} - -static void -free_list(struct listinfo *inf) -{ - - inf->count = inf->elemsize = inf->maxcount = 0; - if (inf->l.ptr != NULL) - free(inf->l.ptr); - inf->addelem = NULL; - inf->lname = NULL; - inf->l.ptr = NULL; -} - -static void -init_list(struct listinfo *inf, addelem_rtn artn, int elemsize, - const char *lname) -{ - - inf->count = inf->maxcount = 0; - inf->elemsize = elemsize; - inf->addelem = artn; - inf->lname = lname; - inf->l.ptr = NULL; } VARENT * @@ -909,7 +1227,7 @@ { struct varent *vent; - for (vent = vhead; vent; vent = vent->next) { + STAILQ_FOREACH(vent, &varlist, list) { if (strcmp(vent->var->name, v->name) == 0) return vent; } @@ -917,12 +1235,12 @@ } static void -scanvars(void) +scanvars() { - struct varent *vent; + VARENT *vent; VAR *v; - for (vent = vhead; vent; vent = vent->next) { + STAILQ_FOREACH(vent, &varlist, list) { v = vent->var; if (v->flag & DSIZ) { v->dwidth = v->width; @@ -938,15 +1256,15 @@ static void dynsizevars(KINFO *ki) { - struct varent *vent; + VARENT *vent; VAR *v; int i; - for (vent = vhead; vent; vent = vent->next) { + STAILQ_FOREACH(vent, &varlist, list) { v = vent->var; if (!(v->flag & DSIZ)) continue; - i = (v->sproc)( ki); + i = (v->sproc)(ki); if (v->width < i) v->width = i; if (v->width > v->dwidth) @@ -957,13 +1275,13 @@ static void sizevars(void) { - struct varent *vent; + VARENT *vent; VAR *v; int i; - for (vent = vhead; vent; vent = vent->next) { + STAILQ_FOREACH(vent, &varlist, list) { v = vent->var; - i = strlen(vent->header); + i = strlen(v->header); if (v->width < i) v->width = i; totwidth += v->width + 1; /* +1 for space */ @@ -972,21 +1290,19 @@ } static const char * -fmt(char **(*fn)(kvm_t *, const struct kinfo_proc *, int), KINFO *ki, - char *comm, int maxlen) +format(kvm_t *kd, + char **(*fn) (kvm_t *, const struct kinfo_proc *, int), + KINFO *ki, const char *comm, int maxlen) { - const char *s; - - s = fmt_argv((*fn)(kd, ki->ki_p, termwidth), comm, maxlen); - return (s); + return (fmt_argv((const char **)(*fn)(kd, ki->ki_p, termwidth), + comm, maxlen)); } #define UREADOK(ki) (forceuread || (ki->ki_p->ki_sflag & PS_INMEM)) static void -saveuser(KINFO *ki) +saveuser(kvm_t *kd, KINFO *ki) { - if (ki->ki_p->ki_sflag & PS_INMEM) { /* * The u-area might be swapped out, and we can't get @@ -1000,70 +1316,58 @@ /* * save arguments if needed */ - if (needcomm && (UREADOK(ki) || (ki->ki_p->ki_args != NULL))) { - ki->ki_args = strdup(fmt(kvm_getargv, ki, ki->ki_p->ki_comm, - MAXCOMLEN)); - } else if (needcomm) { - asprintf(&ki->ki_args, "(%s)", ki->ki_p->ki_comm); - } else { + if (needcomm) { + if (UREADOK(ki) || (ki->ki_p->ki_args != NULL)) + ki->ki_args = strdup(format(kd, kvm_getargv, + ki, ki->ki_p->ki_comm, MAXCOMLEN)); + else if (ki->ki_p->ki_flag & P_SYSTEM) + (void)asprintf(&ki->ki_args, "[%s]", ki->ki_p->ki_comm); + else + (void)asprintf(&ki->ki_args, "(%s)", ki->ki_p->ki_comm); + if (!ki->ki_args) + errx(1, "malloc failed"); + } else ki->ki_args = NULL; - } - if (needenv && UREADOK(ki)) { - ki->ki_env = strdup(fmt(kvm_getenvv, ki, (char *)NULL, 0)); - } else if (needenv) { - ki->ki_env = malloc(3); - strcpy(ki->ki_env, "()"); - } else { + if (needenv) { + if (UREADOK(ki)) + ki->ki_env = strdup(format(kd, kvm_getenvv, + ki, (char *)NULL, 0)); + else + (void)asprintf(&ki->ki_env, "()"); + if (!ki->ki_env) + errx(1, "malloc failed"); + } else ki->ki_env = NULL; - } } static int pscomp(const void *a, const void *b) { const KINFO *ka, *kb; - double cpua, cpub; - segsz_t sizea, sizeb; + int i; +#define VSIZE(k) ((k)->ki_p->ki_dsize + (k)->ki_p->ki_ssize + \ + (k)->ki_p->ki_tsize) ka = a; kb = b; /* SORTCPU and SORTMEM are sorted in descending order. */ - if (sortby == SORTCPU) { - cpua = getpcpu(ka); - cpub = getpcpu(kb); - if (cpua < cpub) - return (1); - if (cpua > cpub) - return (-1); - } - if (sortby == SORTMEM) { - sizea = ka->ki_p->ki_tsize + ka->ki_p->ki_dsize + - ka->ki_p->ki_ssize; - sizeb = kb->ki_p->ki_tsize + kb->ki_p->ki_dsize + - kb->ki_p->ki_ssize; - if (sizea < sizeb) - return (1); - if (sizea > sizeb) - return (-1); - } + if (sortby == SORTCPU && (i = (int)(getpcpu(kb) - getpcpu(ka))) != 0) + return(i); + if (sortby == SORTMEM && + (i = (int)(VSIZE(kb) - VSIZE(ka))) != 0) + return(i); /* * TTY's are sorted in ascending order, except that all NODEV * processes come before all processes with a device. */ - if (ka->ki_p->ki_tdev == NODEV && kb->ki_p->ki_tdev != NODEV) - return (-1); - if (ka->ki_p->ki_tdev != NODEV && kb->ki_p->ki_tdev == NODEV) - return (1); - if (ka->ki_p->ki_tdev < kb->ki_p->ki_tdev) - return (-1); - if (ka->ki_p->ki_tdev > kb->ki_p->ki_tdev) - return (1); + if (ka->ki_p->ki_tdev == NODEV && kb->ki_p->ki_tdev != NODEV) + return(-1); + if (ka->ki_p->ki_tdev != NODEV && kb->ki_p->ki_tdev == NODEV) + return(1); + if ((i = (int)(ka->ki_p->ki_tdev - kb->ki_p->ki_tdev)) != 0) + return(i); /* PID's are sorted in ascending order. */ - if (ka->ki_p->ki_pid < kb->ki_p->ki_pid) - return (-1); - if (ka->ki_p->ki_pid > kb->ki_p->ki_pid) - return (1); - return (0); + return((int)(ka->ki_p->ki_pid - kb->ki_p->ki_pid)); } /* @@ -1096,8 +1400,10 @@ /* * options begin with '-' */ - if (*s != '-') + if (*s != '-') { + posix = 0; *ns++ = '-'; /* add option flag */ + } /* * gaze to end of argv[1] */ @@ -1138,14 +1444,28 @@ } static void -usage(void) +usage(char c) { -#define SINGLE_OPTS "[-aC" OPT_LAZY_f "HhjlmrSTuvwXxZ]" +#if defined(LAZY_PS) + static char optf[] = "f"; +#else + static char optf[] = ""; +#endif - (void)fprintf(stderr, "%s\n%s\n%s\n%s\n", - "usage: ps " SINGLE_OPTS " [-G gid[,gid]] [-O|o fmt]", - " [-p pid[,pid]] [-t tty[,tty]] [-U user[,user]]", - " [-M core] [-N system]", - " ps [-L]"); + if (c) + warnx("illegal option -- %c", c); + if (posix) + (void)fprintf(stderr, "\ +usage: ps [-AacdefjLlMmPQqTxy] [-C cmdlist] [-G grouplist] [-g pgrplist]\n\ + [-O|-o fmt] [-p pidlist] [-s sidlist] [-t ttylist] [-U userlist]\n\ + [-u userlist] [-n system [-n core]]\n\ + ps [-o help]\n\ +"); + else + (void)fprintf(stderr, "\ +usage: ps [-AaCce%sgHhjlmnQqrSsTuvwxXZ] [-G grouplist] [-O|-o fmtlist]\n\ + [-p pidlist] [-t ttylist] [-U userlist] [-M core] [-N system]\n\ + ps [-L]\n\ +", optf); exit(1); } Index: ps.h =================================================================== RCS file: /home/ncvs/src/bin/ps/ps.h,v retrieving revision 1.17 diff -u -I$Id.*$ -I$.+BSD.*$ -r1.17 ps.h --- ps.h 6 Apr 2004 20:06:49 -0000 1.17 +++ ps.h 17 Apr 2004 21:06:16 -0000 @@ -27,10 +27,20 @@ * SUCH DAMAGE. * * @(#)ps.h 8.1 (Berkeley) 5/31/93 - * $FreeBSD$ + * $FreeBSD: src/bin/ps/ps.h,v 1.7 1999/08/27 23:14:52 peter Exp $ */ +#include +#include + +#define LTIMELEN 12 +#define STIMELEN 9 +#define UIDLEN 5 +#define USERLEN (MAXLOGNAME - 1) #define UNLIMITED 0 /* unlimited terminal width */ + +#define KOFF(x) offsetof(struct kinfo_proc, x) + enum type { CHAR, UCHAR, SHORT, USHORT, INT, UINT, LONG, ULONG, KPTR, PGTOK }; typedef struct kinfo { @@ -41,9 +51,10 @@ } KINFO; /* Variables. */ +typedef STAILQ_HEAD(varlist, varent) VARLIST; + typedef struct varent { - const char *header; - struct varent *next; + STAILQ_ENTRY(varent) list; struct var *var; } VARENT; Index: sys/kern/kern_proc.c =================================================================== RCS file: /home/ncvs/src/sys/kern/kern_proc.c,v retrieving revision 1.202 diff -u -I$Id.*$ -I$.+BSD.*$ -r1.202 kern_proc.c --- sys/kern/kern_proc.c 5 Apr 2004 21:03:34 -0000 1.202 +++ sys/kern/kern_proc.c 17 Apr 2004 19:27:08 -0000 @@ -647,6 +647,7 @@ mtx_unlock(&ktrace_mtx); #endif kp->ki_fd = p->p_fd; + kp->ki_cmask = p->p_fd ? p->p_fd->fd_cmask : CMASK; kp->ki_vmspace = p->p_vmspace; if (p->p_ucred) { kp->ki_uid = p->p_ucred->cr_uid; @@ -671,6 +672,7 @@ p->p_state != PRS_ZOMBIE && p->p_vmspace != NULL) { struct vmspace *vm = p->p_vmspace; + struct rlimit rsslim; kp->ki_size = vm->vm_map.size; kp->ki_rssize = vmspace_resident_count(vm); /*XXX*/ @@ -686,15 +688,16 @@ kp->ki_tsize = vm->vm_tsize; kp->ki_dsize = vm->vm_dsize; kp->ki_ssize = vm->vm_ssize; + /* from sys/vm/vm_pageout.c */ + lim_rlimit(p, RLIMIT_RSS, &rsslim); + kp->ki_maxrss = qmin(rsslim.rlim_cur, rsslim.rlim_max); } if ((p->p_sflag & PS_INMEM) && p->p_stats) { kp->ki_start = p->p_stats->p_start; timevaladd(&kp->ki_start, &boottime); kp->ki_rusage = p->p_stats->p_ru; - kp->ki_childtime.tv_sec = p->p_stats->p_cru.ru_utime.tv_sec + - p->p_stats->p_cru.ru_stime.tv_sec; - kp->ki_childtime.tv_usec = p->p_stats->p_cru.ru_utime.tv_usec + - p->p_stats->p_cru.ru_stime.tv_usec; + kp->ki_childstime = p->p_stats->p_cru.ru_stime; + kp->ki_childutime = p->p_stats->p_cru.ru_utime; } if (p->p_state != PRS_ZOMBIE) { #if 0 @@ -802,6 +805,7 @@ strlcpy(kp->ki_comm, p->p_comm, sizeof(kp->ki_comm)); strlcpy(kp->ki_ocomm, p->p_comm, sizeof(kp->ki_ocomm)); } + kp->ki_cursig = p->p_sig; kp->ki_siglist = p->p_siglist; SIGSETOR(kp->ki_siglist, td->td_siglist); kp->ki_sigmask = td->td_sigmask; Index: sys/sys/user.h =================================================================== RCS file: /home/ncvs/src/sys/sys/user.h,v retrieving revision 1.55 diff -u -I$Id.*$ -I$.+BSD.*$ -r1.55 user.h --- sys/sys/user.h 7 Apr 2004 04:19:50 -0000 1.55 +++ sys/sys/user.h 15 Apr 2004 22:52:24 -0000 @@ -134,7 +134,7 @@ u_int ki_swtime; /* Time swapped in or out */ u_int64_t ki_runtime; /* Real time in microsec */ struct timeval ki_start; /* starting time */ - struct timeval ki_childtime; /* time used by process children */ + struct timeval ki_childstime; /*system time used by process children*/ long ki_flag; /* P_* flags */ long ki_kiflag; /* KI_* flags (below) */ int ki_traceflag; /* Kernel trace points */ @@ -156,7 +156,11 @@ long ki_tdflags; /* XXXKSE kthread flag */ struct pcb *ki_pcb; /* kernel virtual addr of pcb */ void *ki_kstack; /* kernel virtual addr of stack */ - long ki_spare[22]; /* spare constants */ + u_short ki_cmask; /* mask for file creation */ + int ki_cursig; /* signals pending to this process */ + rlim_t ki_maxrss; /* maximum resident set size */ + struct timeval ki_childutime; /* user time used by process children */ + long ki_spare[16+(sizeof(long)!=sizeof(int))*2]; /* spare constants */ }; void fill_kinfo_proc(struct proc *, struct kinfo_proc *); >Release-Note: >Audit-Trail: >Unformatted: