diff options
author | Patrick McCarty <patrick.mccarty@linux.intel.com> | 2013-02-08 13:26:27 -0800 |
---|---|---|
committer | Patrick McCarty <patrick.mccarty@linux.intel.com> | 2013-02-08 13:26:27 -0800 |
commit | 9bb81f8a90ecc8b70c955bff72ec59dd3d9e5ae7 (patch) | |
tree | 881eebfa461e4f8aa6b6f44b96ac0decd3bc887a /misc.c | |
download | lsof-9bb81f8a90ecc8b70c955bff72ec59dd3d9e5ae7.tar.gz lsof-9bb81f8a90ecc8b70c955bff72ec59dd3d9e5ae7.tar.bz2 lsof-9bb81f8a90ecc8b70c955bff72ec59dd3d9e5ae7.zip |
Imported Upstream version 4.87upstream/4.87
Diffstat (limited to 'misc.c')
-rw-r--r-- | misc.c | 1653 |
1 files changed, 1653 insertions, 0 deletions
@@ -0,0 +1,1653 @@ +/* + * 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.27 2013/01/02 17:14:59 abe Exp $"; +#endif + + +#include "lsof.h" + +#if defined(HASWIDECHAR) +# if defined(WIDECHARINCL) +#include WIDECHARINCL +# endif /* defined(WIDECHARINCL) */ +# if defined(HASWCTYPE_H) +#include <wctype.h> +# endif /* defined(HASWCTYPE_H) */ +#endif /* defined(HASWIDECHAR) */ + + +/* + * 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); +} |