/* * 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); }