blob: 3c1c32b8d501997cc470e69b2af9b13272d1c024 [file] [log] [blame]
/*
* misc.c - common miscellaneous functions for lsof
*/
/*
* Copyright 1994 Purdue Research Foundation, West Lafayette, Indiana
* 47907. All rights reserved.
*
* Written by Victor A. Abell
*
* This software is not subject to any license of the American Telephone
* and Telegraph Company or the Regents of the University of California.
*
* Permission is granted to anyone to use this software for any purpose on
* any computer system, and to alter it and redistribute it freely, subject
* to the following restrictions:
*
* 1. Neither the authors nor Purdue University are responsible for any
* consequences of the use of this software.
*
* 2. The origin of this software must not be misrepresented, either by
* explicit claim or by omission. Credit to the authors and Purdue
* University must appear in documentation and sources.
*
* 3. Altered versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 4. This notice may not be removed or altered.
*/
#ifndef lint
static char copyright[] =
"@(#) Copyright 1994 Purdue Research Foundation.\nAll rights reserved.\n";
static char *rcsid = "$Id: misc.c,v 1.26 2008/10/21 16:21:41 abe Exp $";
#endif
#include "lsof.h"
#if defined(HASWIDECHAR) && defined(WIDECHARINCL)
#include WIDECHARINCL
#endif /* defined(HASWIDECHAR) && defined(WIDECHARINCL) */
/*
* Local definitions
*/
#if !defined(MAXSYMLINKS)
#define MAXSYMLINKS 32
#endif /* !defined(MAXSYMLINKS) */
/*
* Local function prototypes
*/
_PROTOTYPE(static void closePipes,(void));
_PROTOTYPE(static int dolstat,(char *path, char *buf, int len));
_PROTOTYPE(static int dostat,(char *path, char *buf, int len));
_PROTOTYPE(static int doreadlink,(char *path, char *buf, int len));
_PROTOTYPE(static int doinchild,(int (*fn)(), char *fp, char *rbuf, int rbln));
#if defined(HASINTSIGNAL)
_PROTOTYPE(static int handleint,(int sig));
#else /* !defined(HASINTSIGNAL) */
_PROTOTYPE(static void handleint,(int sig));
#endif /* defined(HASINTSIGNAL) */
_PROTOTYPE(static char *safepup,(unsigned int c, int *cl));
/*
* Local variables
*/
static pid_t Cpid = 0; /* child PID */
static jmp_buf Jmp_buf; /* jump buffer */
static int Pipes[] = /* pipes for child process */
{ -1, -1, -1, -1 };
static int CtSigs[] = { 0, SIGINT, SIGKILL };
/* child termination signals (in order
* of application) -- the first is a
* dummy to allow pipe closure to
* cause the child to exit */
#define NCTSIGS (sizeof(CtSigs) / sizeof(int))
#if defined(HASNLIST)
/*
* build-Nl() - build kernel name list table
*/
static struct drive_Nl *Build_Nl = (struct drive_Nl *)NULL;
/* the default Drive_Nl address */
void
build_Nl(d)
struct drive_Nl *d; /* data to drive the construction */
{
struct drive_Nl *dp;
int i, n;
for (dp = d, n = 0; dp->nn; dp++, n++)
;
if (n < 1) {
(void) fprintf(stderr,
"%s: can't calculate kernel name list length\n", Pn);
Exit(1);
}
if (!(Nl = (struct NLIST_TYPE *)calloc((n + 1),
sizeof(struct NLIST_TYPE))))
{
(void) fprintf(stderr,
"%s: can't allocate %d bytes to kernel name list structure\n",
Pn, (int)((n + 1) * sizeof(struct NLIST_TYPE)));
Exit(1);
}
for (dp = d, i = 0; i < n; dp++, i++) {
Nl[i].NL_NAME = dp->knm;
}
Nll = (int)((n + 1) * sizeof(struct NLIST_TYPE));
Build_Nl = d;
}
#endif /* defined(HASNLIST) */
/*
* childx() - make child process exit (if possible)
*/
void
childx()
{
static int at, sx;
pid_t wpid;
if (Cpid > 1) {
/*
* First close the pipes to and from the child. That should cause the
* child to exit. Compute alarm time shares.
*/
(void) closePipes();
if ((at = TmLimit / NCTSIGS) < TMLIMMIN)
at = TMLIMMIN;
/*
* Loop, waiting for the child to exit. After the first pass, help
* the child exit by sending it signals.
*/
for (sx = 0; sx < NCTSIGS; sx++) {
if (setjmp(Jmp_buf)) {
/*
* An alarm has rung. Disable further alarms.
*
* If there are more signals to send, continue the signal loop.
*
* If the last signal has been sent, issue a warning (unless
* warninge have been suppressed) and exit the signal loop.
*/
(void) alarm(0);
(void) signal(SIGALRM, SIG_DFL);
if (sx < (NCTSIGS - 1))
continue;
if (!Fwarn)
(void) fprintf(stderr,
"%s: WARNING -- child process %d may be hung.\n",
Pn, (int)Cpid);
break;
}
/*
* Send the next signal to the child process, after the first pass
* through the loop.
*
* Wrap the wait() with an alarm.
*/
if (sx)
(void) kill(Cpid, CtSigs[sx]);
(void) signal(SIGALRM, handleint);
(void) alarm(at);
wpid = (pid_t) wait(NULL);
(void) alarm(0);
(void) signal(SIGALRM, SIG_DFL);
if (wpid == Cpid)
break;
}
Cpid = 0;
}
}
/*
* closePipes() - close open pipe file descriptors
*/
static void
closePipes()
{
int i;
for (i = 0; i < 4; i++) {
if (Pipes[i] >= 0) {
(void) close(Pipes[i]);
Pipes[i] = -1;
}
}
}
/*
* compdev() - compare Devtp[] entries
*/
int
compdev(a1, a2)
COMP_P *a1, *a2;
{
struct l_dev **p1 = (struct l_dev **)a1;
struct l_dev **p2 = (struct l_dev **)a2;
if ((dev_t)((*p1)->rdev) < (dev_t)((*p2)->rdev))
return(-1);
if ((dev_t)((*p1)->rdev) > (dev_t)((*p2)->rdev))
return(1);
if ((INODETYPE)((*p1)->inode) < (INODETYPE)((*p2)->inode))
return(-1);
if ((INODETYPE)((*p1)->inode) > (INODETYPE)((*p2)->inode))
return(1);
return(strcmp((*p1)->name, (*p2)->name));
}
/*
* doinchild() -- do a function in a child process
*/
static int
doinchild(fn, fp, rbuf, rbln)
int (*fn)(); /* function to perform */
char *fp; /* function parameter */
char *rbuf; /* response buffer */
int rbln; /* response buffer length */
{
int en, rv;
/*
* Check reply buffer size.
*/
if (!Fovhd && rbln > MAXPATHLEN) {
(void) fprintf(stderr,
"%s: doinchild error; response buffer too large: %d\n",
Pn, rbln);
Exit(1);
}
/*
* Set up to handle an alarm signal; handle an alarm signal; build
* pipes for exchanging information with a child process; start the
* child process; and perform functions in the child process.
*/
if (!Fovhd) {
if (setjmp(Jmp_buf)) {
/*
* Process an alarm that has rung.
*/
(void) alarm(0);
(void) signal(SIGALRM, SIG_DFL);
(void) childx();
errno = ETIMEDOUT;
return(1);
} else if (!Cpid) {
/*
* Create pipes to exchange function information with a child
* process.
*/
if (pipe(Pipes) < 0 || pipe(&Pipes[2]) < 0) {
(void) fprintf(stderr, "%s: can't open pipes: %s\n",
Pn, strerror(errno));
Exit(1);
}
/*
* Fork a child to execute functions.
*/
if ((Cpid = fork()) == 0) {
/*
* Begin the child process.
*/
int fd, nd, r_al, r_rbln;
char r_arg[MAXPATHLEN+1], r_rbuf[MAXPATHLEN+1];
int (*r_fn)();
/*
* Close all open file descriptors except Pipes[0] and
* Pipes[3].
*/
for (fd = 0, nd = GET_MAX_FD(); fd < nd; fd++) {
if (fd == Pipes[0] || fd == Pipes[3])
continue;
(void) close(fd);
if (fd == Pipes[1])
Pipes[1] = -1;
else if (fd == Pipes[2])
Pipes[2] = -1;
}
if (Pipes[1] >= 0) {
(void) close(Pipes[1]);
Pipes[1] = -1;
}
if (Pipes[2] >= 0) {
(void) close(Pipes[2]);
Pipes[2] = -1;
}
/*
* Read function requests, process them, and return replies.
*/
for (;;) {
if (read(Pipes[0], (char *)&r_fn, sizeof(r_fn))
!= (int)sizeof(r_fn)
|| read(Pipes[0], (char *)&r_al, sizeof(int))
!= (int)sizeof(int)
|| r_al < 1
|| r_al > (int)sizeof(r_arg)
|| read(Pipes[0], r_arg, r_al) != r_al
|| read(Pipes[0], (char *)&r_rbln, sizeof(r_rbln))
!= (int)sizeof(r_rbln)
|| r_rbln < 1 || r_rbln > (int)sizeof(r_rbuf))
break;
rv = r_fn(r_arg, r_rbuf, r_rbln);
en = errno;
if (write(Pipes[3], (char *)&rv, sizeof(rv))
!= sizeof(rv)
|| write(Pipes[3], (char *)&en, sizeof(en))
!= sizeof(en)
|| write(Pipes[3], r_rbuf, r_rbln) != r_rbln)
break;
}
(void) _exit(0);
}
/*
* Continue in the parent process to finish the setup.
*/
if (Cpid < 0) {
(void) fprintf(stderr, "%s: can't fork: %s\n",
Pn, strerror(errno));
Exit(1);
}
(void) close(Pipes[0]);
(void) close(Pipes[3]);
Pipes[0] = Pipes[3] = -1;
}
}
if (!Fovhd) {
int len;
/*
* Send a function to the child and wait for the response.
*/
len = strlen(fp) + 1;
(void) signal(SIGALRM, handleint);
(void) alarm(TmLimit);
if (write(Pipes[1], (char *)&fn, sizeof(fn)) != sizeof(fn)
|| write(Pipes[1], (char *)&len, sizeof(len)) != sizeof(len)
|| write(Pipes[1], fp, len) != len
|| write(Pipes[1], (char *)&rbln, sizeof(rbln)) != sizeof(rbln)
|| read(Pipes[2], (char *)&rv, sizeof(rv)) != sizeof(rv)
|| read(Pipes[2], (char *)&en, sizeof(en)) != sizeof(en)
|| read(Pipes[2], rbuf, rbln) != rbln) {
(void) alarm(0);
(void) signal(SIGALRM, SIG_DFL);
(void) childx();
errno = ECHILD;
return(-1);
}
} else {
/*
* Do the operation directly -- not in a child.
*/
(void) signal(SIGALRM, handleint);
(void) alarm(TmLimit);
rv = fn(fp, rbuf, rbln);
en = errno;
}
/*
* Function completed, response collected -- complete the operation.
*/
(void) alarm(0);
(void) signal(SIGALRM, SIG_DFL);
errno = en;
return(rv);
}
/*
* dolstat() - do an lstat() function
*/
static int
dolstat(path, rbuf, rbln)
char *path; /* path */
char *rbuf; /* response buffer */
int rbln; /* response buffer length */
/* ARGSUSED */
{
return(lstat(path, (struct stat *)rbuf));
}
/*
* doreadlink() -- do a readlink() function
*/
static int
doreadlink(path, rbuf, rbln)
char *path; /* path */
char *rbuf; /* response buffer */
int rbln; /* response buffer length */
{
return(readlink(path, rbuf, rbln));
}
/*
* dostat() - do a stat() function
*/
static int
dostat(path, rbuf, rbln)
char *path; /* path */
char *rbuf; /* response buffer */
int rbln; /* response buffer length */
/* ARGSUSED */
{
return(stat(path, (struct stat *)rbuf));
}
#if defined(WILLDROPGID)
/*
* dropgid() - drop setgid permission
*/
void
dropgid()
{
if (!Setuidroot && Setgid) {
if (setgid(Mygid) < 0) {
(void) fprintf(stderr, "%s: can't setgid(%d): %s\n",
Pn, (int)Mygid, strerror(errno));
Exit(1);
}
Setgid = 0;
}
}
#endif /* defined(WILLDROPGID) */
/*
* enter_dev_ch() - enter device characters in file structure
*/
void
enter_dev_ch(m)
char *m;
{
char *mp;
if (!m || *m == '\0')
return;
if (!(mp = mkstrcpy(m, (MALLOC_S *)NULL))) {
(void) fprintf(stderr, "%s: no more dev_ch space at PID %d: \n",
Pn, Lp->pid);
safestrprt(m, stderr, 1);
Exit(1);
}
if (Lf->dev_ch)
(void) free((FREE_P *)Lf->dev_ch);
Lf->dev_ch = mp;
}
/*
* enter_IPstate() -- enter a TCP or UDP state
*/
void
enter_IPstate(ty, nm, nr)
char *ty; /* type -- TCP or UDP */
char *nm; /* state name (may be NULL) */
int nr; /* state number */
{
#if defined(USE_LIB_PRINT_TCPTPI)
TcpNstates = nr;
#else /* !defined(USE_LIB_PRINT_TCPTPI) */
int al, i, j, oc, nn, ns, off, tx;
char *cp;
MALLOC_S len;
/*
* Check the type name and set the type index.
*/
if (!ty) {
(void) fprintf(stderr,
"%s: no type specified to enter_IPstate()\n", Pn);
Exit(1);
}
if (!strcmp(ty, "TCP"))
tx = 0;
else if (!strcmp(ty, "UDP"))
tx = 1;
else {
(void) fprintf(stderr, "%s: unknown type for enter_IPstate: %s\n",
Pn, ty);
Exit(1);
}
/*
* If the name argument is NULL, reduce the allocated table to its minimum
* size.
*/
if (!nm) {
if (tx) {
if (UdpSt) {
if (!UdpNstates) {
(void) free((MALLOC_P *)UdpSt);
UdpSt = (char **)NULL;
}
if (UdpNstates < UdpStAlloc) {
len = (MALLOC_S)(UdpNstates * sizeof(char *));
if (!(UdpSt = (char **)realloc((MALLOC_P *)UdpSt, len)))
{
(void) fprintf(stderr,
"%s: can't reduce UdpSt[]\n", Pn);
Exit(1);
}
}
UdpStAlloc = UdpNstates;
}
} else {
if (TcpSt) {
if (!TcpNstates) {
(void) free((MALLOC_P *)TcpSt);
TcpSt = (char **)NULL;
}
if (TcpNstates < TcpStAlloc) {
len = (MALLOC_S)(TcpNstates * sizeof(char *));
if (!(TcpSt = (char **)realloc((MALLOC_P *)TcpSt, len)))
{
(void) fprintf(stderr,
"%s: can't reduce TcpSt[]\n", Pn);
Exit(1);
}
}
TcpStAlloc = TcpNstates;
}
}
return;
}
/*
* Check the name and number.
*/
if ((len = (size_t)strlen(nm)) < 1) {
(void) fprintf(stderr,
"%s: bad %s name (\"%s\"), number=%d\n", Pn, ty, nm, nr);
Exit(1);
}
/*
* Make a copy of the name.
*/
if (!(cp = mkstrcpy(nm, (MALLOC_S *)NULL))) {
(void) fprintf(stderr,
"%s: enter_IPstate(): no %s space for %s\n",
Pn, ty, nm);
Exit(1);
}
/*
* Set the necessary offset for using nr as an index. If it is
* a new offset, adjust previous entries.
*/
if ((nr < 0) && ((off = -nr) > (tx ? UdpStOff : TcpStOff))) {
if (tx ? UdpSt : TcpSt) {
/*
* A new, larger offset (smaller negative state number) could mean
* a previously allocated state table must be enlarged and its
* previous entries moved.
*/
oc = off - (tx ? UdpStOff : TcpStOff);
al = tx ? UdpStAlloc : TcpStAlloc;
ns = tx ? UdpNstates : TcpNstates;
if ((nn = ns + oc) >= al) {
while ((nn + 5) > al) {
al += TCPUDPALLOC;
}
len = (MALLOC_S)(al * sizeof(char *));
if (tx) {
if (!(UdpSt = (char **)realloc((MALLOC_P *)UdpSt, len)))
goto no_IP_space;
UdpStAlloc = al;
} else {
if (!(TcpSt = (char **)realloc((MALLOC_P *)TcpSt, len)))
goto no_IP_space;
TcpStAlloc = al;
}
for (i = 0, j = oc; i < oc; i++, j++) {
if (tx) {
if (i < UdpNstates)
UdpSt[j] = UdpSt[i];
UdpSt[i] = (char *)NULL;
} else {
if (i < TcpNstates)
TcpSt[j] = TcpSt[i];
TcpSt[i] = (char *)NULL;
}
}
if (tx)
UdpNstates += oc;
else
TcpNstates += oc;
}
}
if (tx)
UdpStOff = off;
else
TcpStOff = off;
}
/*
* Enter name as {Tc|Ud}pSt[nr + {Tc|Ud}pStOff].
*
* Allocate space, as required.
*/
al = tx ? UdpStAlloc : TcpStAlloc;
off = tx ? UdpStOff : TcpStOff;
nn = nr + off + 1;
if (nn > al) {
i = tx ? UdpNstates : TcpNstates;
while ((nn + 5) > al) {
al += TCPUDPALLOC;
}
len = (MALLOC_S)(al * sizeof(char *));
if (tx) {
if (UdpSt)
UdpSt = (char **)realloc((MALLOC_P *)UdpSt, len);
else
UdpSt = (char **)malloc(len);
if (!UdpSt) {
no_IP_space:
(void) fprintf(stderr, "%s: no %s state space\n", Pn, ty);
Exit(1);
}
UdpNstates = nn;
UdpStAlloc = al;
} else {
if (TcpSt)
TcpSt = (char **)realloc((MALLOC_P *)TcpSt, len);
else
TcpSt = (char **)malloc(len);
if (!TcpSt)
goto no_IP_space;
TcpNstates = nn;
TcpStAlloc = al;
}
while (i < al) {
if (tx)
UdpSt[i] = (char *)NULL;
else
TcpSt[i] = (char *)NULL;
i++;
}
} else {
if (tx) {
if (nn > UdpNstates)
UdpNstates = nn;
} else {
if (nn > TcpNstates)
TcpNstates = nn;
}
}
if (tx) {
if (UdpSt[nr + UdpStOff]) {
dup_IP_state:
(void) fprintf(stderr,
"%s: duplicate %s state %d (already %s): %s\n",
Pn, ty, nr,
tx ? UdpSt[nr + UdpStOff] : TcpSt[nr + TcpStOff],
nm);
Exit(1);
}
UdpSt[nr + UdpStOff] = cp;
} else {
if (TcpSt[nr + TcpStOff])
goto dup_IP_state;
TcpSt[nr + TcpStOff] = cp;
}
#endif /* defined(USE_LIB_PRINT_TCPTPI) */
}
/*
* enter_nm() - enter name in local file structure
*/
void
enter_nm(m)
char *m;
{
char *mp;
if (!m || *m == '\0')
return;
if (!(mp = mkstrcpy(m, (MALLOC_S *)NULL))) {
(void) fprintf(stderr, "%s: no more nm space at PID %d for: ",
Pn, Lp->pid);
safestrprt(m, stderr, 1);
Exit(1);
}
if (Lf->nm)
(void) free((FREE_P *)Lf->nm);
Lf->nm = mp;
}
/*
* Exit() - do a clean exit()
*/
void
Exit(xv)
int xv; /* exit() value */
{
(void) childx();
#if defined(HASDCACHE)
if (DCrebuilt && !Fwarn)
(void) fprintf(stderr, "%s: WARNING: %s was updated.\n",
Pn, DCpath[DCpathX]);
#endif /* defined(HASDCACHE) */
exit(xv);
}
#if defined(HASNLIST)
/*
* get_Nl_value() - get Nl value for nickname
*/
int
get_Nl_value(nn, d, v)
char *nn; /* nickname of requested entry */
struct drive_Nl *d; /* drive_Nl table that built Nl
* (if NULL, use Build_Nl) */
KA_T *v; /* returned value (if NULL,
* return nothing) */
{
int i;
if (!Nl || !Nll)
return(-1);
if (!d)
d = Build_Nl;
for (i = 0; d->nn; d++, i++) {
if (strcmp(d->nn, nn) == 0) {
if (v)
*v = (KA_T)Nl[i].n_value;
return(i);
}
}
return(-1);
}
#endif /* defined(HASNLIST) */
/*
* handleint() - handle an interrupt
*/
#if defined(HASINTSIGNAL)
static int
#else
static void
#endif
/* ARGSUSED */
handleint(sig)
int sig;
{
longjmp(Jmp_buf, 1);
}
/*
* hashbyname() - hash by name
*/
int
hashbyname(nm, mod)
char *nm; /* pointer to NUL-terminated name */
int mod; /* hash modulus */
{
int i, j;
for (i = j = 0; *nm; nm++) {
i ^= (int)*nm << j;
if (++j > 7)
j = 0;
}
return(((int)(i * 31415)) & (mod - 1));
}
/*
* is_nw_addr() - is this network address selected?
*/
int
is_nw_addr(ia, p, af)
unsigned char *ia; /* Internet address */
int p; /* port */
int af; /* address family -- e.g., AF_INET,
* AF_INET6 */
{
struct nwad *n;
if (!(n = Nwad))
return(0);
for (; n; n = n->next) {
if (n->proto) {
if (strcasecmp(n->proto, Lf->iproto) != 0)
continue;
}
if (af && n->af && af != n->af)
continue;
#if defined(HASIPv6)
if (af == AF_INET6) {
if (n->a[15] || n->a[14] || n->a[13] || n->a[12]
|| n->a[11] || n->a[10] || n->a[9] || n->a[8]
|| n->a[7] || n->a[6] || n->a[5] || n->a[4]
|| n->a[3] || n->a[2] || n->a[1] || n->a[0]) {
if (ia[15] != n->a[15] || ia[14] != n->a[14]
|| ia[13] != n->a[13] || ia[12] != n->a[12]
|| ia[11] != n->a[11] || ia[10] != n->a[10]
|| ia[9] != n->a[9] || ia[8] != n->a[8]
|| ia[7] != n->a[7] || ia[6] != n->a[6]
|| ia[5] != n->a[5] || ia[4] != n->a[4]
|| ia[3] != n->a[3] || ia[2] != n->a[2]
|| ia[1] != n->a[1] || ia[0] != n->a[0])
continue;
}
} else if (af == AF_INET)
#endif /* defined(HASIPv6) */
{
if (n->a[3] || n->a[2] || n->a[1] || n->a[0]) {
if (ia[3] != n->a[3] || ia[2] != n->a[2]
|| ia[1] != n->a[1] || ia[0] != n->a[0])
continue;
}
}
#if defined(HASIPv6)
else
continue;
#endif /* defined(HASIPv6) */
if (n->sport == -1 || (p >= n->sport && p <= n->eport)) {
n->f = 1;
return(1);
}
}
return(0);
}
/*
* mkstrcpy() - make a string copy in malloc()'d space
*
* return: copy pointer
* copy length (optional)
*/
char *
mkstrcpy(src, rlp)
char *src; /* source */
MALLOC_S *rlp; /* returned length pointer (optional)
* The returned length is an strlen()
* equivalent */
{
MALLOC_S len;
char *ns;
len = (MALLOC_S)(src ? strlen(src) : 0);
ns = (char *)malloc(len + 1);
if (ns) {
if (src)
(void) snpf(ns, len + 1, "%s", src);
else
*ns = '\0';
}
if (rlp)
*rlp = len;
return(ns);
}
/*
* mkstrcat() - make a catenated copy of up to three strings under optional
* string-by-string count control
*
* return: copy pointer
* copy string length (optional)
*/
char *
mkstrcat(s1, l1, s2, l2, s3, l3, clp)
char *s1; /* source string 1 */
int l1; /* length of string 1 (-1 if none) */
char *s2; /* source string 2 */
int l2; /* length of string 2 (-1 if none) */
char *s3; /* source string 3 (optional) */
int l3 ; /* length of string 3 (-1 if none) */
MALLOC_S *clp; /* pointer to return of copy length
* (optional) */
{
MALLOC_S cl, len1, len2, len3;
char *cp;
if (s1)
len1 = (MALLOC_S)((l1 >= 0) ? l1 : strlen(s1));
else
len1 = (MALLOC_S)0;
if (s2)
len2 = (MALLOC_S)((l2 >= 0) ? l2 : strlen(s2));
else
len2 = (MALLOC_S)0;
if (s3)
len3 = (MALLOC_S)((l3 >= 0) ? l3 : strlen(s3));
else
len3 = (MALLOC_S)0;
cl = len1 + len2 + len3;
if ((cp = (char *)malloc(cl + 1))) {
char *tp = cp;
if (s1 && len1) {
(void) strncpy(tp, s1, len1);
tp += len1;
}
if (s2 && len2) {
(void) strncpy(tp, s2, len2);
tp += len2;
}
if (s3 && len3) {
(void) strncpy(tp, s3, len3);
tp += len3;
}
*tp = '\0';
}
if (clp)
*clp = cl;
return(cp);
}
/*
* is_readable() -- is file readable
*/
int
is_readable(path, msg)
char *path; /* file path */
int msg; /* issue warning message if 1 */
{
if (access(path, R_OK) < 0) {
if (!Fwarn && msg == 1)
(void) fprintf(stderr, ACCESSERRFMT, Pn, path, strerror(errno));
return(0);
}
return(1);
}
/*
* lstatsafely() - lstat path safely (i. e., with timeout)
*/
int
lstatsafely(path, buf)
char *path; /* file path */
struct stat *buf; /* stat buffer address */
{
if (Fblock) {
if (!Fwarn)
(void) fprintf(stderr,
"%s: avoiding stat(%s): -b was specified.\n",
Pn, path);
errno = EWOULDBLOCK;
return(1);
}
return(doinchild(dolstat, path, (char *)buf, sizeof(struct stat)));
}
/*
* Readlink() - read and interpret file system symbolic links
*/
char *
Readlink(arg)
char *arg; /* argument to be interpreted */
{
char abuf[MAXPATHLEN+1];
int alen;
char *ap;
char *argp1, *argp2;
int i, len, llen, slen;
char lbuf[MAXPATHLEN+1];
static char *op = (char *)NULL;
static int ss = 0;
char *s1;
static char **stk = (char **)NULL;
static int sx = 0;
char tbuf[MAXPATHLEN+1];
/*
* See if avoiding kernel blocks.
*/
if (Fblock) {
if (!Fwarn) {
(void) fprintf(stderr, "%s: avoiding readlink(", Pn);
safestrprt(arg, stderr, 0);
(void) fprintf(stderr, "): -b was specified.\n");
}
op = (char *)NULL;
return(arg);
}
/*
* Save the original path.
*/
if (!op)
op = arg;
/*
* Evaluate each component of the argument for a symbolic link.
*/
for (alen = 0, ap = abuf, argp1 = argp2 = arg; *argp2; argp1 = argp2 ) {
for (argp2 = argp1 + 1; *argp2 && *argp2 != '/'; argp2++)
;
if ((len = argp2 - arg) >= (int)sizeof(tbuf)) {
path_too_long:
if (!Fwarn) {
(void) fprintf(stderr,
"%s: readlink() path too long: ", Pn);
safestrprt(op ? op : arg, stderr, 1);
}
op = (char *)NULL;
return((char *)NULL);
}
(void) strncpy(tbuf, arg, len);
tbuf[len] = '\0';
/*
* Dereference a symbolic link.
*/
if ((llen=doinchild(doreadlink,tbuf,lbuf,sizeof(lbuf) - 1)) >= 0) {
/*
* If the link is a new absolute path, replace
* the previous assembly with it.
*/
if (lbuf[0] == '/') {
(void) strncpy(abuf, lbuf, llen);
ap = &abuf[llen];
*ap = '\0';
alen = llen;
continue;
}
lbuf[llen] = '\0';
s1 = lbuf;
} else {
llen = argp2 - argp1;
s1 = argp1;
}
/*
* Make sure two components are separated by a `/'.
*
* If the first component is not a link, don't force
* a leading '/'.
*
* If the first component is a link and the source of
* the link has a leading '/', force a leading '/'.
*/
if (*s1 == '/')
slen = 1;
else {
if (alen > 0) {
/*
* This is not the first component.
*/
if (abuf[alen - 1] == '/')
slen = 1;
else
slen = 2;
} else {
/*
* This is the first component.
*/
if (s1 == lbuf && tbuf[0] == '/')
slen = 2;
else
slen = 1;
}
}
/*
* Add to the path assembly.
*/
if ((alen + llen + slen) >= (int)sizeof(abuf))
goto path_too_long;
if (slen == 2)
*ap++ = '/';
(void) strncpy(ap, s1, llen);
ap += llen;
*ap = '\0';
alen += (llen + slen - 1);
}
/*
* If the assembled path and argument are the same, free all but the
* last string in the stack, and return the argument.
*/
if (strcmp(arg, abuf) == 0) {
for (i = 0; i < sx; i++) {
if (i < (sx - 1))
(void) free((FREE_P *)stk[i]);
stk[i] = (char *)NULL;
}
sx = 0;
op = (char *)NULL;
return(arg);
}
/*
* If the assembled path and argument are different, add it to the
* string stack, then Readlink() it.
*/
if (!(s1 = mkstrcpy(abuf, (MALLOC_S *)NULL))) {
no_readlink_space:
(void) fprintf(stderr, "%s: no Readlink string space for ", Pn);
safestrprt(abuf, stderr, 1);
Exit(1);
}
if (sx >= MAXSYMLINKS) {
/*
* If there are too many symbolic links, report an error, clear
* the stack, and return no path.
*/
if (!Fwarn) {
(void) fprintf(stderr,
"%s: too many (> %d) symbolic links in readlink() path: ",
Pn, MAXSYMLINKS);
safestrprt(op ? op : arg, stderr, 1);
}
for (i = 0; i < sx; i++) {
(void) free((FREE_P *)stk[i]);
stk[i] = (char *)NULL;
}
(void) free((FREE_P *)stk);
stk = (char **)NULL;
ss = sx = 0;
op = (char *)NULL;
return((char *)NULL);
}
if (++sx > ss) {
if (!stk)
stk = (char **)malloc((MALLOC_S)(sizeof(char *) * sx));
else
stk = (char **)realloc((MALLOC_P *)stk,
(MALLOC_S)(sizeof(char *) * sx));
if (!stk)
goto no_readlink_space;
ss = sx;
}
stk[sx - 1] = s1;
return(Readlink(s1));
}
#if defined(HASSTREAMS)
/*
* readstdata() - read stream's stdata structure
*/
int
readstdata(addr, buf)
KA_T addr; /* stdata address in kernel*/
struct stdata *buf; /* buffer addess */
{
if (!addr
|| kread(addr, (char *)buf, sizeof(struct stdata))) {
(void) snpf(Namech, Namechl, "no stream data in %s",
print_kptr(addr, (char *)NULL, 0));
return(1);
}
return(0);
}
/*
* readsthead() - read stream head
*/
int
readsthead(addr, buf)
KA_T addr; /* starting queue pointer in kernel */
struct queue *buf; /* buffer for queue head */
{
KA_T qp;
if (!addr) {
(void) snpf(Namech, Namechl, "no stream queue head");
return(1);
}
for (qp = addr; qp; qp = (KA_T)buf->q_next) {
if (kread(qp, (char *)buf, sizeof(struct queue))) {
(void) snpf(Namech, Namechl, "bad stream queue link at %s",
print_kptr(qp, (char *)NULL, 0));
return(1);
}
}
return(0);
}
/*
* readstidnm() - read stream module ID name
*/
int
readstidnm(addr, buf, len)
KA_T addr; /* module ID name address in kernel */
char *buf; /* receiving buffer address */
READLEN_T len; /* buffer length */
{
if (!addr || kread(addr, buf, len)) {
(void) snpf(Namech, Namechl, "can't read module ID name from %s",
print_kptr(addr, (char *)NULL, 0));
return(1);
}
return(0);
}
/*
* readstmin() - read stream's module info
*/
int
readstmin(addr, buf)
KA_T addr; /* module info address in kernel */
struct module_info *buf; /* receiving buffer address */
{
if (!addr || kread(addr, (char *)buf, sizeof(struct module_info))) {
(void) snpf(Namech, Namechl, "can't read module info from %s",
print_kptr(addr, (char *)NULL, 0));
return(1);
}
return(0);
}
/*
* readstqinit() - read stream's queue information structure
*/
int
readstqinit(addr, buf)
KA_T addr; /* queue info address in kernel */
struct qinit *buf; /* receiving buffer address */
{
if (!addr || kread(addr, (char *)buf, sizeof(struct qinit))) {
(void) snpf(Namech, Namechl, "can't read queue info from %s",
print_kptr(addr, (char *)NULL, 0));
return(1);
}
return(0);
}
#endif /* HASSTREAMS */
/*
* safepup() - safely print an unprintable character -- i.e., print it in a
* printable form
*
* return: char * to printable equivalent
* cl = strlen(printable equivalent)
*/
static char *
safepup(c, cl)
unsigned int c; /* unprintable (i.e., !isprint())
* character */
int *cl; /* returned printable strlen -- NULL if
* no return needed */
{
int len;
char *rp;
static char up[8];
if (c < 0x20) {
switch (c) {
case '\b':
rp = "\\b";
break;
case '\f':
rp = "\\f";
break;
case '\n':
rp = "\\n";
break;
case '\r':
rp = "\\r";
break;
case '\t':
rp = "\\t";
break;
default:
(void) snpf(up, sizeof(up), "^%c", c + 0x40);
rp = up;
}
len = 2;
} else if (c == 0xff) {
rp = "^?";
len = 2;
} else {
(void) snpf(up, sizeof(up), "\\x%02x", (int)(c & 0xff));
rp = up;
len = 4;
}
if (cl)
*cl = len;
return(rp);
}
/*
* safestrlen() - calculate a "safe" string length -- i.e., compute space for
* non-printable characters when printed in a printable form
*/
int
safestrlen(sp, flags)
char *sp; /* string pointer */
int flags; /* flags:
* bit 0: 0 (0) = no NL
* 1 (1) = add trailing NL
* 1: 0 (0) = ' ' printable
* 1 (2) = ' ' not printable
*/
{
char c;
int len = 0;
c = (flags & 2) ? ' ' : '\0';
if (sp) {
for (; *sp; sp++) {
if (!isprint((unsigned char)*sp) || *sp == c) {
if (*sp < 0x20 || (unsigned char)*sp == 0xff)
len += 2; /* length of \. or ^. form */
else
len += 4; /* length of "\x%02x" printf */
} else
len++;
}
}
return(len);
}
/*
* safestrprt() - print a string "safely" to the indicated stream -- i.e.,
* print unprintable characters in a printable form
*/
void
safestrprt(sp, fs, flags)
char *sp; /* string to print pointer pointer */
FILE *fs; /* destination stream -- e.g., stderr
* or stdout */
int flags; /* flags:
* bit 0: 0 (0) = no NL
* 1 (1) = add trailing NL
* 1: 0 (0) = ' ' printable
* 1 (2) = ' ' not printable
* 2: 0 (0) = print string as is
* 1 (4) = surround string
* with '"'
* 4: 0 (0) = print ending '\n'
* 1 (8) = don't print ending
* '\n'
*/
{
char c;
int lnc, lnt, sl;
#if defined(HASWIDECHAR)
wchar_t w;
int wcmx = MB_CUR_MAX;
#else /* !defined(HASWIDECHAR) */
static int wcmx = 1;
#endif /* defined(HASWIDECHAR) */
c = (flags & 2) ? ' ' : '\0';
if (flags & 4)
putc('"', fs);
if (sp) {
for (sl = strlen(sp); *sp; sl -= lnc, sp += lnc) {
#if defined(HASWIDECHAR)
if (wcmx > 1) {
lnc = mblen(sp, sl);
if (lnc > 1) {
if ((mbtowc(&w, sp, sl) == lnc) && iswprint(w)) {
for (lnt = 0; lnt < lnc; lnt++) {
putc((int)*(sp + lnt), fs);
}
} else {
for (lnt = 0; lnt < lnc; lnt++) {
fputs(safepup((unsigned int)*(sp + lnt),
(int *)NULL), fs);
}
}
continue;
} else
lnc = 1;
} else
lnc = 1;
#else /* !defined(HASWIDECHAR) */
lnc = 1;
#endif /* defined(HASWIDECHAR) */
if (isprint((unsigned char)*sp) && *sp != c)
putc((int)(*sp & 0xff), fs);
else {
if ((flags & 8) && (*sp == '\n') && !*(sp + 1))
break;
fputs(safepup((unsigned int)*sp, (int *)NULL), fs);
}
}
}
if (flags & 4)
putc('"', fs);
if (flags & 1)
putc('\n', fs);
}
/*
* safestrprtn() - print a specified number of characters from a string
* "safely" to the indicated stream
*/
void
safestrprtn(sp, len, fs, flags)
char *sp; /* string to print pointer pointer */
int len; /* safe number of characters to
* print */
FILE *fs; /* destination stream -- e.g., stderr
* or stdout */
int flags; /* flags:
* bit 0: 0 (0) = no NL
* 1 (1) = add trailing NL
* 1: 0 (0) = ' ' printable
* 1 (2) = ' ' not printable
* 2: 0 (0) = print string as is
* 1 (4) = surround string
* with '"'
* 4: 0 (0) = print ending '\n'
* 1 (8) = don't print ending
* '\n'
*/
{
char c, *up;
int cl, i;
if (flags & 4)
putc('"', fs);
if (sp) {
c = (flags & 2) ? ' ' : '\0';
for (i = 0; i < len && *sp; sp++) {
if (isprint((unsigned char)*sp) && *sp != c) {
putc((int)(*sp & 0xff), fs);
i++;
} else {
if ((flags & 8) && (*sp == '\n') && !*(sp + 1))
break;
up = safepup((unsigned int)*sp, &cl);
if ((i + cl) > len)
break;
fputs(up, fs);
i += cl;
}
}
} else
i = 0;
for (; i < len; i++)
putc(' ', fs);
if (flags & 4)
putc('"', fs);
if (flags & 1)
putc('\n', fs);
}
/*
* statsafely() - stat path safely (i. e., with timeout)
*/
int
statsafely(path, buf)
char *path; /* file path */
struct stat *buf; /* stat buffer address */
{
if (Fblock) {
if (!Fwarn)
(void) fprintf(stderr,
"%s: avoiding stat(%s): -b was specified.\n",
Pn, path);
errno = EWOULDBLOCK;
return(1);
}
return(doinchild(dostat, path, (char *)buf, sizeof(struct stat)));
}
/*
* stkdir() - stack directory name
*/
void
stkdir(p)
char *p; /* directory path */
{
MALLOC_S len;
/*
* Provide adequate space for directory stack pointers.
*/
if (Dstkx >= Dstkn) {
Dstkn += 128;
len = (MALLOC_S)(Dstkn * sizeof(char *));
if (!Dstk)
Dstk = (char **)malloc(len);
else
Dstk = (char **)realloc((MALLOC_P *)Dstk, len);
if (!Dstk) {
(void) fprintf(stderr,
"%s: no space for directory stack at: ", Pn);
safestrprt(p, stderr, 1);
Exit(1);
}
}
/*
* Allocate space for the name, copy it there and put its pointer on the stack.
*/
if (!(Dstk[Dstkx] = mkstrcpy(p, (MALLOC_S *)NULL))) {
(void) fprintf(stderr, "%s: no space for: ", Pn);
safestrprt(p, stderr, 1);
Exit(1);
}
Dstkx++;
}
/*
* x2dev() - convert hexadecimal ASCII string to device number
*/
char *
x2dev(s, d)
char *s; /* ASCII string */
dev_t *d; /* device receptacle */
{
char *cp, *cp1;
int n;
dev_t r;
/*
* Skip an optional leading 0x. Count the number of hex digits up to the end
* of the string, or to a space, or to a comma. Return an error if an unknown
* character is encountered. If the count is larger than (2 * sizeof(dev_t))
* -- e.g., because of sign extension -- ignore excess leading hex 0xf digits,
* but return an error if an excess leading digit isn't 0xf.
*/
if (strncasecmp(s, "0x", 2) == 0)
s += 2;
for (cp = s, n = 0; *cp; cp++, n++) {
if (isdigit((unsigned char)*cp))
continue;
if ((unsigned char)*cp >= 'a' && (unsigned char)*cp <= 'f')
continue;
if ((unsigned char)*cp >= 'A' && (unsigned char)*cp <= 'F')
continue;
if (*cp == ' ' || *cp == ',')
break;
return((char *)NULL);
}
if (!n)
return((char *)NULL);
if (n > (2 * (int)sizeof(dev_t))) {
cp1 = s;
s += (n - (2 * sizeof(dev_t)));
while (cp1 < s) {
if (*cp1 != 'f' && *cp1 != 'F')
return((char *)NULL);
cp1++;
}
}
/*
* Assemble the validated hex digits of the device number, starting at a point
* in the string relevant to sizeof(dev_t).
*/
for (r = 0; s < cp; s++) {
r = r << 4;
if (isdigit((unsigned char)*s))
r |= (unsigned char)(*s - '0') & 0xf;
else {
if (isupper((unsigned char)*s))
r |= ((unsigned char)(*s - 'A') + 10) & 0xf;
else
r |= ((unsigned char)(*s - 'a') + 10) & 0xf;
}
}
*d = r;
return(s);
}