--- /dev/null Thu May 21 10:42:55 2009 +++ new/src/solaris/native/java/lang/processutil_md.c Thu May 21 10:42:54 2009 @@ -0,0 +1,324 @@ +/* + * 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; + } + } +}