* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
"@(#) Copyright (c) 1980 Regents of the University of California.\n\
static char sccsid
[] = "@(#)pstat.c 5.28 (Berkeley) %G%";
#define clear(x) ((int)x &~ KERNBASE)
while ((ch
= getopt(argc
, argv
, "TafvikptU:sxnu")) != EOF
)
sscanf(optarg
, "%d", &upid
);
fprintf(stderr
, "pstat: use [ -U pid ] for -u\n");
fprintf(stderr
, "usage: pstat -[Tafiptsx] [-U [pid]] [system] [core]\n");
if (kvm_openfiles(fnlist
, fcore
, NULL
) == -1) {
syserror("kvm_openfiles: %s", kvm_geterr());
if (kvm_nlist(nl
) != 0) {
syserror("kvm_nlist: %s", kvm_geterr());
if (!(filf
| totflg
| vnof
| prcf
| txtf
| ttyf
| usrf
| swpf
)) {
printf("pstat: one or more of -[aivxptfsU] is required\n");
register struct e_vnode
*e_vnodebase
, *endvnode
, *evp
;
register struct vnode
*vp
;
register struct mount
*maddr
= NULL
, *mp
;
register struct inode
*ip
;
struct e_vnode
*loadvnodes();
e_vnodebase
= loadvnodes(&numvnodes
);
printf("%7d vnodes\n", numvnodes
);
endvnode
= e_vnodebase
+ numvnodes
;
printf("%d active vnodes\n", numvnodes
);
for (evp
= e_vnodebase
; evp
< endvnode
; evp
++) {
if (vp
->v_mount
!= maddr
) {
if ((mp
= getmnt(vp
->v_mount
)) == NULL
)
vnode_print(evp
->avnode
, vp
);
printf("ADDR TYP VFLAG USE HOLD");
printf("%8x %s %5s %4d %4d",
avnode
, type
, flags
, vp
->v_usecount
, vp
->v_holdcnt
);
printf(" FILEID IFLAG RDEV|SZ");
struct inode
*ip
= VTOI(vp
);
char flagbuf
[16], *flags
= flagbuf
;
printf(" %6d %5s", ip
->i_number
, flagbuf
);
type
= ip
->i_mode
& S_IFMT
;
if (type
== S_IFCHR
|| type
== S_IFBLK
)
if (nflg
|| ((name
= devname(ip
->i_rdev
, type
)) == NULL
))
major(ip
->i_rdev
), minor(ip
->i_rdev
));
printf(" %7d", ip
->i_size
);
printf(" FILEID NFLAG RDEV|SZ");
struct nfsnode
*np
= VTONFS(vp
);
char flagbuf
[16], *flags
= flagbuf
;
printf(" %6d %5s", VT
.va_fileid
, flagbuf
);
type
= VT
.va_mode
& S_IFMT
;
if (type
== S_IFCHR
|| type
== S_IFBLK
)
if (nflg
|| ((name
= devname(VT
.va_rdev
, type
)) == NULL
))
major(VT
.va_rdev
), minor(VT
.va_rdev
));
printf(" %7d", np
->n_size
);
* Given a pointer to a mount structure in kernel space,
* read it in and return a usable pointer to it.
register struct mtab
*mt
;
for (mt
= mhead
; mt
!= NULL
; mt
= mt
->next
)
if ((mt
= (struct mtab
*)malloc(sizeof (struct mtab
))) == NULL
) {
if (kvm_read((off_t
)maddr
, &mt
->mount
, sizeof(struct mount
)) !=
error("can't read mount table at %x", maddr
);
printf("%s %s on %s", type
, ST
.f_mntfromname
, ST
.f_mntonname
);
if (flags
= mp
->mnt_flag
) {
if (flags
& MNT_RDONLY
) {
printf("%srdonly", comma
);
if (flags
& MNT_SYNCHRONOUS
) {
printf("%ssynchronous", comma
);
flags
&= ~MNT_SYNCHRONOUS
;
if (flags
& MNT_NOEXEC
) {
printf("%snoexec", comma
);
if (flags
& MNT_NOSUID
) {
printf("%snosuid", comma
);
printf("%snodev", comma
);
if (flags
& MNT_EXPORTED
) {
printf("%sexport", comma
);
if (flags
& MNT_EXRDONLY
) {
printf("%sexrdonly", comma
);
printf("%slocal", comma
);
printf("%squota", comma
);
/* filesystem control flags */
if (flags
& MNT_UPDATE
) {
printf("%supdate", comma
);
if (flags
& MNT_MPBUSY
) {
if (flags
& MNT_MPWANT
) {
if (flags
& MNT_UNMOUNT
) {
printf("%sunmount", comma
);
printf("%sunknown_flags:%x", flags
);
struct e_vnode
*vnodebase
;
error("vnodes on dead kernel, not impl yet\n");
if ((ret
= getkerninfo(KINFO_VNODE
, NULL
, NULL
, 0)) == -1) {
syserror("can't get estimate for kerninfo");
if ((vnodebase
= (struct e_vnode
*)malloc(copysize
))
if ((ret
= getkerninfo(KINFO_VNODE
, vnodebase
, ©size
, 0))
syserror("can't get vnode list");
if (copysize
% sizeof (struct e_vnode
)) {
error("vnode size mismatch");
*avnodes
= copysize
/ sizeof (struct e_vnode
);
kvm_read(loc
, &word
, sizeof (word
));
register struct text
*xp
;
struct text
*xtext
, *atext
;
ntext
= getword(nl
[SNTEXT
].n_value
);
xtext
= (struct text
*)calloc(ntext
, sizeof (struct text
));
atext
= (struct text
*)getword(nl
[STEXT
].n_value
);
if (ntext
< 0 || ntext
> 10000) {
fprintf(stderr
, "number of texts is preposterous (%d)\n",
fprintf(stderr
, "can't allocate memory for text table\n");
kvm_read(atext
, xtext
, ntext
* sizeof (struct text
));
for (xp
= xtext
; xp
< &xtext
[ntext
]; xp
++) {
printf("%3d/%3d texts active, %3d used\n", ntx
, ntext
, ntxca
);
printf("%d/%d active texts, %d used\n", ntx
, ntext
, ntxca
);
LOC FLAGS DADDR CADDR RSS SIZE VPTR CNT CCNT FORW BACK\n");
for (xp
= xtext
; xp
< &xtext
[ntext
]; xp
++) {
printf("%8.1x", atext
+ (xp
- xtext
));
putf(xp
->x_flag
&XPAGV
, 'P');
putf(xp
->x_flag
&XTRC
, 'T');
putf(xp
->x_flag
&XWRIT
, 'W');
putf(xp
->x_flag
&XLOAD
, 'L');
putf(xp
->x_flag
&XLOCK
, 'K');
putf(xp
->x_flag
&XWANT
, 'w');
printf("%5x", xp
->x_daddr
[0]);
printf("%10x", xp
->x_caddr
);
printf("%5d", xp
->x_rssize
);
printf("%5d", xp
->x_size
);
printf("%10.1x", xp
->x_vptr
);
printf("%5d", xp
->x_count
&0377);
printf("%5d", xp
->x_ccount
);
printf("%10x", xp
->x_forw
);
printf("%9x", xp
->x_back
);
struct proc
*xproc
, *aproc
;
register struct proc
*pp
;
nproc
= getword(nl
[SNPROC
].n_value
);
xproc
= (struct proc
*)calloc(nproc
, sizeof (struct proc
));
aproc
= (struct proc
*)getword(nl
[SPROC
].n_value
);
if (nproc
< 0 || nproc
> 100000) {
fprintf(stderr
, "number of procs is preposterous (%d)\n",
fprintf(stderr
, "can't allocate memory for proc table\n");
kvm_read(aproc
, xproc
, nproc
* sizeof (struct proc
));
for (pp
=xproc
; pp
< &xproc
[nproc
]; pp
++)
printf("%3d/%3d processes\n", np
, nproc
);
printf("%d/%d processes\n", np
, nproc
);
printf(" LOC S F POIP PRI SIG UID SLP TIM CPU NI PID PPID ADDR RSS SRSS SIZE WCHAN LINK TEXTP\n");
for (pp
=xproc
; pp
<&xproc
[nproc
]; pp
++) {
if (pp
->p_stat
==0 && allflg
==0)
printf("%8x", aproc
+ (pp
- xproc
));
printf(" %2d", pp
->p_stat
);
printf(" %8x", pp
->p_flag
);
printf(" %4d", pp
->p_poip
);
printf(" %3d", pp
->p_pri
);
printf(" %8x", pp
->p_sig
);
printf(" %4d", pp
->p_uid
);
printf(" %3d", pp
->p_slptime
);
printf(" %3d", pp
->p_time
);
printf(" %4d", pp
->p_cpu
&0377);
printf(" %3d", pp
->p_nice
);
printf(" %6d", pp
->p_pid
);
printf(" %6d", pp
->p_ppid
);
if (pp->p_flag & SLOAD) {
kvm_read(pp->p_addr, &apte, sizeof(apte));
printf(" %8x", apte.pg_pfnum);
printf(" %8x", pp->p_swaddr);
printf(" %4x", pp
->p_rssize
);
printf(" %4x", pp
->p_swrss
);
printf(" %5x", pp
->p_dsize
+pp
->p_ssize
);
printf(" %7x", clear(pp
->p_wchan
));
printf(" %7x", clear(pp
->p_link
));
printf(" %7x", clear(pp
->p_textp
));
char mesg
[] = " LINE RAW CAN OUT HWT LWT ADDR COL STATE SESS PGID DISC\n";
if ((tty
= (struct tty
*)malloc(ttyspace
* sizeof(*tty
))) == 0) {
printf("pstat: out of memory\n");
kvm_read((long)nl
[SCONS
].n_value
, tty
, sizeof(*tty
));
if (nl
[SNQD
].n_type
!= 0)
if (nl
[SNDZ
].n_type
!= 0)
dottytype("dz", SDZ
, SNDZ
);
if (nl
[SNDH
].n_type
!= 0)
dottytype("dh", SDH
, SNDH
);
if (nl
[SNDMF
].n_type
!= 0)
dottytype("dmf", SDMF
, SNDMF
);
if (nl
[SNDHU
].n_type
!= 0)
dottytype("dhu", SDHU
, SNDHU
);
if (nl
[SNDMZ
].n_type
!= 0)
dottytype("dmz", SDMZ
, SNDMZ
);
if (nl
[SNVX
].n_type
!= 0)
dottytype("vx", SVX
, SNVX
);
if (nl
[SNMP
].n_type
!= 0)
dottytype("mp", SMP
, SNMP
);
if (nl
[SNITE
].n_type
!= 0)
dottytype("ite", SITE
, SNITE
);
if (nl
[SNDCA
].n_type
!= 0)
dottytype("dca", SDCA
, SNDCA
);
if (nl
[SNDCM
].n_type
!= 0)
dottytype("dcm", SDCM
, SNDCM
);
if (nl
[SNDCL
].n_type
!= 0)
dottytype("dcl", SDCL
, SNDCL
);
if (nl
[SNPTY
].n_type
!= 0)
dottytype("pty", SPTY
, SNPTY
);
* Special case the qdss: there are 4 ttys per qdss,
* but only the first of each is used as a tty.
kvm_read((long)nl
[SNQD
].n_value
, &nqd
, sizeof(nqd
));
kvm_read((long)nl
[SQD
].n_value
, tty
, nqd
* sizeof(struct tty
) * 4);
for (tp
= tty
; tp
< &tty
[nqd
* 4]; tp
+= 4)
dottytype(name
, type
, number
)
if (tty
== (struct tty
*)0)
kvm_read((long)nl
[number
].n_value
, &ntty
, sizeof(ntty
));
printf("%d %s lines\n", ntty
, name
);
if ((tty
= (struct tty
*)realloc(tty
, ttyspace
* sizeof(*tty
))) == 0) {
printf("pstat: out of memory\n");
kvm_read((long)nl
[type
].n_value
, tty
, ntty
* sizeof(struct tty
));
for (tp
= tty
; tp
< &tty
[ntty
]; tp
++)
TS_WCOLL
, 'I', /* running short on letters ! */
if (nflg
|| tp
->t_dev
== 0 || /* XXX */
(name
= devname(tp
->t_dev
, S_IFCHR
)) == NULL
)
printf("%2d %3d ", tp
->t_rawq
.c_cc
, tp
->t_canq
.c_cc
);
printf("%3d %4d %3d %8x %3d ", tp
->t_outq
.c_cc
,
tp
->t_hiwat
, tp
->t_lowat
, tp
->t_addr
, tp
->t_col
);
for (i
= j
= 0; ttystates
[i
].flag
; i
++)
if (tp
->t_state
&ttystates
[i
].flag
)
state
[j
++] = ttystates
[i
].val
;
printf("%-4s %6x", state
, (u_long
)tp
->t_session
& ~KERNBASE
);
if (tp
->t_pgrp
== NULL
|| kvm_read(&tp
->t_pgrp
->pg_id
, &pgid
,
sizeof (pid_t
)) != sizeof (pid_t
))
printf("%d\n", tp
->t_line
);
register struct user
*up
;
register struct nameidata
*nd
;
if ((ret
= kvm_getprocs(KINFO_PROC_PID
, upid
)) != 1) {
error("kvm_getproc: %s", kvm_geterr());
error("can't locate process %d", upid
);
if ((p
= kvm_nextproc()) == NULL
) {
error("kvm_nextproc: %s", kvm_geterr());
if ((up
= kvm_getu(p
)) == NULL
) {
error("kvm_getu: %s", kvm_geterr());
while (ip
< (int *)((char *)&up
->u_pcb
+ sizeof (struct pcb
))) {
printf("procp\t%#x\n", up
->u_procp
);
printf("ar0\t%#x\n", up
->u_ar0
);
printf("sizes\ttext %d data %d stack %d\n",
up
->u_tsize
, up
->u_dsize
, up
->u_ssize
);
for (i
=0; i
<sizeof(label_t
)/sizeof(int); i
++) {
printf("%#11x", up
->u_ssave
.val
[i
]);
printf("odsize\t%#x\n", up
->u_odsize
);
printf("ossize\t%#x\n", up
->u_ossize
);
printf("outime\t%d\n", up
->u_outime
);
printf("mmap\t%#x\n", up
->u_mmap
);
printf("%#x ", up
->u_signal
[i
]);
printf("%#x ", up
->u_sigmask
[i
]);
printf("sigonstack\t%#x\n", up
->u_sigonstack
);
printf("sigintr\t%#x\n", up
->u_sigintr
);
printf("oldmask\t%#x\n", up
->u_oldmask
);
printf("sigstack\t%#x %#x\n",
up
->u_sigstack
.ss_sp
, up
->u_sigstack
.ss_onstack
);
printf("sig\t%#x\n", up
->u_sig
);
printf("code\t%#x\n", up
->u_code
);
for (i
=0; i
<NOFILE
; i
++) {
printf("%#11x", up
->u_ofile
[i
]);
for (i
=0; i
<NOFILE
; i
++) {
printf("%#11x", up
->u_pofile
[i
]);
printf("lastfile\t%d\n", up
->u_lastfile
);
printf("cmask\t%#o\n", up
->u_cmask
);
printf("start\t%ld secs %ld usecs\n",
up
->u_start
.tv_sec
, up
->u_start
.tv_usec
);
printf("acflag\t%#x\n", up
->u_acflag
);
printf("prof\t%#x %#x %#x %#x\n", up
->u_prof
.pr_base
, up
->u_prof
.pr_size
,
up
->u_prof
.pr_off
, up
->u_prof
.pr_scale
);
for (i
= 0; i
< sizeof(up
->u_ru
)/sizeof(int); i
++)
for (i
= 0; i
< sizeof(up
->u_cru
)/sizeof(int); i
++)
struct file
*xfile
, *afile
;
register struct file
*fp
;
static char *dtypes
[] = { "???", "inode", "socket" };
nfile
= getword(nl
[SNFILE
].n_value
);
xfile
= (struct file
*)calloc(nfile
, sizeof (struct file
));
afile
= (struct file
*)getword(nl
[SFIL
].n_value
);
if (nfile
< 0 || nfile
> 100000) {
fprintf(stderr
, "number of files is preposterous (%d)\n",
fprintf(stderr
, "can't allocate memory for file table\n");
kvm_read(afile
, xfile
, nfile
* sizeof (struct file
));
for (fp
=xfile
; fp
< &xfile
[nfile
]; fp
++)
printf("%3d/%3d files\n", nf
, nfile
);
printf("%d/%d open files\n", nf
, nfile
);
printf(" LOC TYPE FLG CNT MSG DATA OFFSET\n");
for (fp
=xfile
,loc
=(int)afile
; fp
< &xfile
[nfile
]; fp
++,loc
+=sizeof(xfile
[0])) {
if (fp
->f_type
<= DTYPE_SOCKET
)
printf("%-8.8s", dtypes
[fp
->f_type
]);
printf("%8d", fp
->f_type
);
putf(fp
->f_flag
&FREAD
, 'R');
putf(fp
->f_flag
&FWRITE
, 'W');
putf(fp
->f_flag
&FAPPEND
, 'A');
putf(fp
->f_flag
&FSHLOCK
, 'S');
putf(fp
->f_flag
&FEXLOCK
, 'X');
putf(fp
->f_flag
&FASYNC
, 'I');
printf(" %3d", mask(fp
->f_count
));
printf(" %3d", mask(fp
->f_msgcount
));
printf(" %8.1x", fp
->f_data
);
printf(" %x\n", fp
->f_offset
);
printf(" %ld\n", fp
->f_offset
);
int dmmin
, dmmax
, nswdev
;
struct swdevt
*swdevt
, *sw
;
register struct proc
*pp
;
int nswap
, used
, tused
, free
, waste
;
register struct mapent
*me
;
register struct text
*xp
;
nproc
= getword(nl
[SNPROC
].n_value
);
ntext
= getword(nl
[SNTEXT
].n_value
);
if (nproc
< 0 || nproc
> 10000 || ntext
< 0 || ntext
> 10000) {
fprintf(stderr
, "number of procs/texts is preposterous (%d, %d)\n",
proc
= (struct proc
*)calloc(nproc
, sizeof (struct proc
));
fprintf(stderr
, "can't allocate memory for proc table\n");
xtext
= (struct text
*)calloc(ntext
, sizeof (struct text
));
fprintf(stderr
, "can't allocate memory for text table\n");
nswapmap
= getword(nl
[SNSWAPMAP
].n_value
);
swapmap
= (struct map
*)calloc(nswapmap
, sizeof (struct map
));
fprintf(stderr
, "can't allocate memory for swapmap\n");
nswdev
= getword(nl
[SNSWDEV
].n_value
);
swdevt
= (struct swdevt
*)calloc(nswdev
, sizeof (struct swdevt
));
fprintf(stderr
, "can't allocate memory for swdevt table\n");
kvm_read(nl
[SSWDEVT
].n_value
, swdevt
,
nswdev
* sizeof (struct swdevt
));
kvm_read(getword(nl
[SPROC
].n_value
), proc
,
nproc
* sizeof (struct proc
));
kvm_read(getword(nl
[STEXT
].n_value
), xtext
,
ntext
* sizeof (struct text
));
kvm_read(getword(nl
[SWAPMAP
].n_value
), swapmap
,
nswapmap
* sizeof (struct map
));
swapmap
->m_name
= "swap";
swapmap
->m_limit
= (struct mapent
*)&swapmap
[nswapmap
];
dmmin
= getword(nl
[SDMMIN
].n_value
);
dmmax
= getword(nl
[SDMMAX
].n_value
);
for (sw
= swdevt
; sw
< &swdevt
[nswdev
]; sw
++)
for (me
= (struct mapent
*)(swapmap
+1);
me
< (struct mapent
*)&swapmap
[nswapmap
]; me
++)
for (xp
= xtext
; xp
< &xtext
[ntext
]; xp
++)
tused
+= ctod(clrnd(xp
->x_size
));
tused
+= ctod(clrnd(ctopt(xp
->x_size
)));
for (pp
= proc
; pp
< &proc
[nproc
]; pp
++) {
if (pp
->p_stat
== 0 || pp
->p_stat
== SZOMB
)
db
= ctod(pp
->p_dsize
), sb
= up(db
);
db
= ctod(pp
->p_ssize
), sb
= up(db
);
if ((pp
->p_flag
&SLOAD
) == 0)
used
+= ctod(vusize(pp
));
#define btok(x) ((x) / (1024 / DEV_BSIZE))
printf("%3d/%3d 00k swap\n",
btok(used
/100), btok((used
+free
)/100));
printf("%dk used (%dk text), %dk free, %dk wasted, %dk missing\n",
btok(used
), btok(tused
), btok(free
), btok(waste
),
/* a dmmax/2 block goes to argmap */
btok(nswap
- dmmax
/2 - (used
+ free
)));
for (i
= dmmax
; i
>= dmmin
; i
/= 2) {
while (rmalloc(swapmap
, i
) != 0)
if (j
) printf("%d*%dk ", j
, btok(i
));
for (me
= (struct mapent
*)(swapmap
+1);
me
< (struct mapent
*)&swapmap
[nswapmap
]; me
++)
printf("%d*1k\n", btok(free
));
* Compute number of pages to be allocated to the u. area
* and data and stack area page tables, which are stored on the
* disk immediately after the u. area.
register int tsz
= p
->p_tsize
/ NPTEPG
;
* We do not need page table space on the disk for page
* table pages wholly containing text.
clrnd(ctopt(p
->p_tsize
+p
->p_dsize
+p
->p_ssize
+UPAGES
)) - tsz
));
* Allocate 'size' units from the given
* map. Return the base of the allocated space.
* In a map, the addresses are increasing and the
* list is terminated by a 0 size.
* Algorithm is first-fit.
* This routine knows about the interleaving of the swapmap
register struct mapent
*ep
= (struct mapent
*)(mp
+1);
register struct mapent
*bp
;
if (size
<= 0 || size
> dmmax
)
* Search for a piece of the resource map which has enough
* free space to accomodate the request.
for (bp
= ep
; bp
->m_size
; bp
++) {
if (bp
->m_size
>= size
) {
* If allocating from swapmap,
* then have to respect interleaving
(first
= dmmax
- bp
->m_addr
%dmmax
) < bp
->m_size
) {
if (bp
->m_size
- first
< size
)
addr
= bp
->m_addr
+ first
;
rest
= bp
->m_size
- first
- size
;
rmfree(mp
, rest
, addr
+size
);
* If there is no space left of the piece
* we allocated from, move the rest of
* the pieces to the left.
if ((bp
->m_size
-= size
) == 0) {
(bp
-1)->m_addr
= bp
->m_addr
;
} while ((bp
-1)->m_size
= bp
->m_size
);
* Free the previously allocated space at addr
* of size units into the specified map.
* Sort addr into map and combine on
* one or both ends if possible.
register struct mapent
*bp
;
* Both address and size must be
* positive, or the protocol has broken down.
if (addr
<= 0 || size
<= 0)
* Locate the piece of the map which starts after the
* returned space (or the end of the map).
firstbp
= bp
= (struct mapent
*)(mp
+ 1);
for (; bp
->m_addr
<= addr
&& bp
->m_size
!= 0; bp
++)
* If the piece on the left abuts us,
* then we should combine with it.
if (bp
> firstbp
&& (bp
-1)->m_addr
+(bp
-1)->m_size
>= addr
) {
* Check no overlap (internal error).
if ((bp
-1)->m_addr
+(bp
-1)->m_size
> addr
)
* Add into piece on the left by increasing its size.
* If the combined piece abuts the piece on
* the right now, compress it in also,
* by shifting the remaining pieces of the map over.
if (bp
->m_addr
&& addr
+size
>= bp
->m_addr
) {
if (addr
+size
> bp
->m_addr
)
(bp
-1)->m_size
+= bp
->m_size
;
(bp
-1)->m_addr
= bp
->m_addr
;
(bp
-1)->m_size
= bp
->m_size
;
* Don't abut on the left, check for abutting on
if (addr
+size
>= bp
->m_addr
&& bp
->m_size
) {
if (addr
+size
> bp
->m_addr
)
* Don't abut at all. Make a new entry
* and check for map overflow.
* Segment at bp is to be the delimiter;
* If there is not room for it
* then the table is too full
* and we must discard something.
if (bp
+1 > mp
->m_limit
) {
* Back bp up to last available segment.
* which contains a segment already and must
* be made into the delimiter.
* Discard second to last entry,
* since it is presumably smaller than the last
* and move the last entry back one.
printf("%s: rmap ovflo, lost [%d,%d)\n", mp
->m_name
,
(bp
-1)->m_addr
, (bp
-1)->m_addr
+(bp
-1)->m_size
);
bp
[0].m_size
= bp
[0].m_addr
= 0;
fprintf(stderr
, "%s: ", Program
);
fmt
= va_arg(ap
, char *);
(void) vfprintf(stderr
, fmt
, ap
);
fprintf(stderr
, "%s: ", Program
);
fmt
= va_arg(ap
, char *);
(void) vfprintf(stderr
, fmt
, ap
);
fprintf(stderr
, ": %s\n", strerror(errno
));