/* * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ #undef _LARGEFILE64_SOURCE #define _LARGEFILE64_SOURCE 1 /* * processutil_md.c - Functions used both by java.lang.UNIXProcess * native code and processhelper.c */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "processutil_md.h" /** * Cached value of JVM's effective PATH. * (We don't support putenv("PATH=...") in native code) */ const char *jlup_parentPath; /** * Split, canonicalized version of parentPath */ const char * const *jlup_parentPathv; int jlup_isAsciiDigit(char c) { return c >= '0' && c <= '9'; } void jlup_moveDescriptor(int fd_from, int fd_to) { if (fd_from != fd_to) { dup2(fd_from, fd_to); close(fd_from); } } void jlup_initVectorFromBlock(const char**vector, const char* block, int count) { int i; const char *p; for (i = 0, p = block; i < count; i++) { /* Invariant: p always points to the start of a C string. */ vector[i] = p; while (*(p++)); } vector[count] = NULL; } void jlup_initialize(void *env) { jlup_parentPath = jlup_effectivePath(); jlup_parentPathv = jlup_splitPath(env, jlup_parentPath); } int jlup_closeDescriptors(int from_fd) { DIR *dp; struct dirent64 *dirp; /* We're trying to close all file descriptors, but opendir() might * itself be implemented using a file descriptor, and we certainly * don't want to close that while it's in use. We assume that if * opendir() is implemented using a file descriptor, then it uses * the lowest numbered file descriptor, just like open(). So we * close a couple explicitly. */ close(from_fd); /* for possible use by opendir() */ close(from_fd + 1); /* another one for good luck */ if ((dp = opendir("/proc/self/fd")) == NULL) return 0; /* We use readdir64 instead of readdir to work around Solaris bug * 6395699: /proc/self/fd fails to report file descriptors >= 1024 on Solaris 9 */ while ((dirp = readdir64(dp)) != NULL) { int fd; if (jlup_isAsciiDigit(dirp->d_name[0]) && (fd = strtol(dirp->d_name, NULL, 10)) >= from_fd + 2) close(fd); } closedir(dp); return 1; } /** * If PATH is not defined, the OS provides some default value. * Unfortunately, there's no portable way to get this value. * Fortunately, it's only needed if the child has PATH while we do not. */ const char* jlup_defaultPath(void) { #ifdef __solaris__ /* These really are the Solaris defaults! */ return (geteuid() == 0 || getuid() == 0) ? "/usr/xpg4/bin:/usr/ccs/bin:/usr/bin:/opt/SUNWspro/bin:/usr/sbin" : "/usr/xpg4/bin:/usr/ccs/bin:/usr/bin:/opt/SUNWspro/bin:"; #else return ":/bin:/usr/bin"; /* glibc */ #endif } const char* jlup_effectivePath(void) { const char *s = getenv("PATH"); return (s != NULL) ? s : jlup_defaultPath(); } int jlup_countOccurrences(const char *s, char c) { int count; for (count = 0; *s != '\0'; s++) count += (*s == c); return count; } const char * const * jlup_splitPath(void *env, const char *path) { const char *p, *q; char **pathv; int i; int count = jlup_countOccurrences(path, ':') + 1; pathv = jlup_xmalloc (env, (count+1)*sizeof(char *)); pathv[count] = NULL; for (p = path, i = 0; i < count; i++, p = q + 1) { for (q = p; (*q != ':') && (*q != '\0'); q++) ; if (q == p) /* empty PATH component => "." */ pathv[i] = "./"; else { int addSlash = ((*(q - 1)) != '/'); pathv[i] = jlup_xmalloc(env, q - p + addSlash + 1); memcpy(pathv[i], p, q - p); if (addSlash) pathv[i][q - p] = '/'; pathv[i][q - p + addSlash] = '\0'; } } return (const char * const *) pathv; } /* Version of execvpe when child's PATH differs from parent's */ int jlup_execvp_usingParentPath(const char *file, const char *const argv[]) { char expanded_file[PATH_MAX]; int filelen = strlen(file); int sticky_errno = 0; const char * const * dirs; /* Search parent's PATH */ for (dirs = jlup_parentPathv; *dirs; dirs++) { const char * dir = *dirs; int dirlen = strlen(dir); if (filelen + dirlen + 1 >= PATH_MAX) { /* Resist the urge to remove this limit; * calling malloc after fork is unsafe. */ errno = ENAMETOOLONG; continue; } strcpy(expanded_file, dir); strcpy(expanded_file + dirlen, file); execvp(expanded_file, (char **) argv); /* There are 3 responses to various classes of errno: * return immediately, continue (especially for ENOENT), * or continue with "sticky" errno. * * From exec(3): * * If permission is denied for a file (the attempted * execve returned EACCES), these functions will continue * searching the rest of the search path. If no other * file is found, however, they will return with the * global variable errno set to EACCES. */ switch (errno) { case EACCES: sticky_errno = errno; /* FALLTHRU */ case ENOENT: case ENOTDIR: #ifdef ELOOP case ELOOP: #endif #ifdef ESTALE case ESTALE: #endif #ifdef ENODEV case ENODEV: #endif #ifdef ETIMEDOUT case ETIMEDOUT: #endif break; /* Try other directories in PATH */ default: return -1; } } if (sticky_errno != 0) errno = sticky_errno; return -1; } /* execvpe should have been included in the Unix standards. */ int jlup_execvpe(const char *file, const char *const argv[], const char *const envp[]) { /* This is one of the rare times it's more portable to declare an * external symbol explicitly, rather than via a system header. * The declaration is standardized as part of UNIX98, but there is * no standard (not even de-facto) header file where the * declaration is to be found. See: * http://www.opengroup.org/onlinepubs/009695399/functions/environ.html * http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_02.html * * "All identifiers in this volume of IEEE Std 1003.1-2001, except * environ, are defined in at least one of the headers" (!) */ extern char **environ; if (envp != NULL) environ = (char **) envp; if (/* Parent and child environment the same? Use child PATH. */ (envp == NULL) /* http://www.opengroup.org/onlinepubs/009695399/functions/exec.html * "If the file argument contains a slash character, it is used as * the pathname for this file. Otherwise, the path prefix for this * file is obtained by a search of the directories passed in the * PATH environment variable" */ || (strchr(file, '/') != NULL) /* Parent and child PATH the same? Use child PATH. */ || (strcmp(jlup_parentPath, jlup_effectivePath()) == 0) /* We want ENOENT, not EACCES, for zero-length program names. */ || (*file == '\0')) { return execvp(file, (char **) argv); } else { return jlup_execvp_usingParentPath(file, argv); } } void jlup_closeSafely(int fd) { if (fd != -1) close(fd); } /* * Reads nbyte bytes from file descriptor fd into buf, * The read operation is retried in case of EINTR or partial reads. * * Returns number of bytes read (normally nbyte, but may be less in * case of EOF). In case of read errors, returns -1 and sets errno. */ ssize_t jlup_readFully(int fd, void *buf, size_t nbyte) { ssize_t remaining = nbyte; for (;;) { ssize_t n = read(fd, buf, remaining); if (n == 0) { return nbyte - remaining; } else if (n > 0) { remaining -= n; if (remaining <= 0) return nbyte; /* We were interrupted in the middle of reading the bytes. * Unlikely, but possible. */ buf = (void *) (((char *)buf) + n); } else if (errno == EINTR) { /* Strange signals like SIGJVM1 are possible at any time. * See http://www.dreamsongs.com/WorseIsBetter.html */ } else { return -1; } } }