YARN-5456. container-executor support for FreeBSD, NetBSD, and others if conf path is absolute. Contributed by Allen Wittenauer.

(cherry picked from commit b913677365)
This commit is contained in:
Chris Nauroth 2016-08-02 22:24:34 -07:00 committed by Varun Vasudev
parent 198bd84b33
commit 5251de00fa
8 changed files with 167 additions and 24 deletions

View File

@ -19,10 +19,20 @@ cmake_minimum_required(VERSION 2.6 FATAL_ERROR)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/../../../../../hadoop-common-project/hadoop-common) list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/../../../../../hadoop-common-project/hadoop-common)
include(HadoopCommon) include(HadoopCommon)
# determine if container-executor.conf.dir is an absolute
# path in case the OS we're compiling on doesn't have
# a hook in get_executable. We'll use this define
# later in the code to potentially throw a compile error
string(REGEX MATCH . HCD_ONE "${HADOOP_CONF_DIR}")
string(COMPARE EQUAL ${HCD_ONE} / HADOOP_CONF_DIR_IS_ABS)
# Note: can't use -D_FILE_OFFSET_BITS=64, see MAPREDUCE-4258 # Note: can't use -D_FILE_OFFSET_BITS=64, see MAPREDUCE-4258
string(REPLACE "-D_FILE_OFFSET_BITS=64" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") string(REPLACE "-D_FILE_OFFSET_BITS=64" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
string(REPLACE "-D_FILE_OFFSET_BITS=64" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") string(REPLACE "-D_FILE_OFFSET_BITS=64" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
include(CheckIncludeFiles)
check_include_files("sys/types.h;sys/sysctl.h" HAVE_SYS_SYSCTL_H)
include(CheckFunctionExists) include(CheckFunctionExists)
check_function_exists(canonicalize_file_name HAVE_CANONICALIZE_FILE_NAME) check_function_exists(canonicalize_file_name HAVE_CANONICALIZE_FILE_NAME)
check_function_exists(fcloseall HAVE_FCLOSEALL) check_function_exists(fcloseall HAVE_FCLOSEALL)
@ -32,6 +42,9 @@ check_function_exists(fstatat HAVE_FSTATAT)
check_function_exists(openat HAVE_OPENAT) check_function_exists(openat HAVE_OPENAT)
check_function_exists(unlinkat HAVE_UNLINKAT) check_function_exists(unlinkat HAVE_UNLINKAT)
include(CheckSymbolExists)
check_symbol_exists(sysctl "sys/types.h;sys/sysctl.h" HAVE_SYSCTL)
if(APPLE) if(APPLE)
include_directories( /System/Library/Frameworks ) include_directories( /System/Library/Frameworks )
find_library(COCOA_LIBRARY Cocoa) find_library(COCOA_LIBRARY Cocoa)

View File

@ -18,14 +18,26 @@
#ifndef CONFIG_H #ifndef CONFIG_H
#define CONFIG_H #define CONFIG_H
/* custom configs */
#cmakedefine HADOOP_CONF_DIR "@HADOOP_CONF_DIR@" #cmakedefine HADOOP_CONF_DIR "@HADOOP_CONF_DIR@"
#cmakedefine HADOOP_CONF_DIR_IS_ABS "@HADOOP_CONF_DIR_IS_ABS@"
/* specific functions */
#cmakedefine HAVE_CANONICALIZE_FILE_NAME @HAVE_CANONICALIZE_FILE_NAME@ #cmakedefine HAVE_CANONICALIZE_FILE_NAME @HAVE_CANONICALIZE_FILE_NAME@
#cmakedefine HAVE_FCHMODAT @HAVE_FCHMODAT@ #cmakedefine HAVE_FCHMODAT @HAVE_FCHMODAT@
#cmakedefine HAVE_FCLOSEALL @HAVE_FCLOSEALL@ #cmakedefine HAVE_FCLOSEALL @HAVE_FCLOSEALL@
#cmakedefine HAVE_FDOPENDIR @HAVE_FDOPENDIR@ #cmakedefine HAVE_FDOPENDIR @HAVE_FDOPENDIR@
#cmakedefine HAVE_FSTATAT @HAVE_FSTATAT@ #cmakedefine HAVE_FSTATAT @HAVE_FSTATAT@
#cmakedefine HAVE_OPENAT @HAVE_OPENAT@ #cmakedefine HAVE_OPENAT @HAVE_OPENAT@
#cmakedefine HAVE_SYSCTL @HAVE_SYSCTL@
#cmakedefine HAVE_UNLINKAT @HAVE_UNLINKAT@ #cmakedefine HAVE_UNLINKAT @HAVE_UNLINKAT@
/* specific headers */
#cmakedefine HAVE_SYS_SYSCTL_H @HAVE_SYS_SYSCTL_H@
#endif #endif

View File

@ -16,6 +16,10 @@
* limitations under the License. * limitations under the License.
*/ */
#ifdef __FreeBSD__
#define _WITH_GETLINE
#endif
#include <stddef.h> #include <stddef.h>
/** Define a platform-independent constant instead of using PATH_MAX */ /** Define a platform-independent constant instead of using PATH_MAX */

View File

@ -846,7 +846,23 @@ int set_user(const char *user) {
*/ */
static int change_owner(const char* path, uid_t user, gid_t group) { static int change_owner(const char* path, uid_t user, gid_t group) {
if (geteuid() == user && getegid() == group) { if (geteuid() == user && getegid() == group) {
/*
* On the BSDs, this is not a guaranteed shortcut
* since group permissions are inherited
*/
#if defined(__FreeBSD__) || defined(__NetBSD__)
if (chown(path, user, group) != 0) {
fprintf(LOGFILE, "Can't chown %s to %d:%d - %s\n", path, user, group,
strerror(errno));
return -1;
}
return 0; return 0;
#else
return 0;
#endif
} else { } else {
uid_t old_user = geteuid(); uid_t old_user = geteuid();
gid_t old_group = getegid(); gid_t old_group = getegid();
@ -884,14 +900,14 @@ int create_directory_for_user(const char* path) {
if (ret == 0) { if (ret == 0) {
if (0 == mkdir(path, permissions) || EEXIST == errno) { if (0 == mkdir(path, permissions) || EEXIST == errno) {
// need to reassert the group sticky bit // need to reassert the group sticky bit
if (chmod(path, permissions) != 0) { if (change_owner(path, user, nm_gid) != 0) {
fprintf(LOGFILE, "Can't chmod %s to add the sticky bit - %s\n",
path, strerror(errno));
ret = -1;
} else if (change_owner(path, user, nm_gid) != 0) {
fprintf(LOGFILE, "Failed to chown %s to %d:%d: %s\n", path, user, nm_gid, fprintf(LOGFILE, "Failed to chown %s to %d:%d: %s\n", path, user, nm_gid,
strerror(errno)); strerror(errno));
ret = -1; ret = -1;
} else if (chmod(path, permissions) != 0) {
fprintf(LOGFILE, "Can't chmod %s to add the sticky bit - %s\n",
path, strerror(errno));
ret = -1;
} }
} else { } else {
fprintf(LOGFILE, "Failed to create directory %s - %s\n", path, fprintf(LOGFILE, "Failed to create directory %s - %s\n", path,

View File

@ -15,6 +15,12 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
/* FreeBSD protects the getline() prototype. See getline(3) for more */
#ifdef __FreeBSD__
#define _WITH_GETLINE
#endif
#include <pwd.h> #include <pwd.h>
#include <stdio.h> #include <stdio.h>
#include <sys/types.h> #include <sys/types.h>
@ -101,7 +107,7 @@ extern FILE *LOGFILE;
extern FILE *ERRORFILE; extern FILE *ERRORFILE;
// get the executable's filename // get the executable's filename
char* get_executable(); char* get_executable(char *argv0);
//function used to load the configurations present in the secure config //function used to load the configurations present in the secure config
void read_executor_config(const char* file_name); void read_executor_config(const char* file_name);

View File

@ -28,23 +28,27 @@
* *
*/ */
#include "config.h"
#include "configuration.h"
#include "container-executor.h"
#include <errno.h> #include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include "config.h"
#include "configuration.h" #ifdef HAVE_SYS_SYSCTL_H
#include "container-executor.h" #include <sys/types.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#endif
/* /*
* A generic function to read a link and return * A generic function to read a link and return
* the value for use with System V procfs. * the value for use with System V procfs.
* With much thanks to Tom Killian, Roger Faulkner, * With much thanks to Tom Killian, Roger Faulkner,
* and Ron Gomes, this is pretty generic code. * and Ron Gomes, this is pretty generic code.
*
* The various BSDs do not have (reliably)
* have /proc. Custom implementations follow.
*/ */
char *__get_exec_readproc(char *procfn) { char *__get_exec_readproc(char *procfn) {
@ -53,7 +57,7 @@ char *__get_exec_readproc(char *procfn) {
filename = malloc(EXECUTOR_PATH_MAX); filename = malloc(EXECUTOR_PATH_MAX);
if (!filename) { if (!filename) {
fprintf(ERRORFILE,"cannot allocate memory for filename: %s\n",strerror(errno)); fprintf(ERRORFILE,"cannot allocate memory for filename before readlink: %s\n",strerror(errno));
exit(-1); exit(-1);
} }
len = readlink(procfn, filename, EXECUTOR_PATH_MAX); len = readlink(procfn, filename, EXECUTOR_PATH_MAX);
@ -62,14 +66,45 @@ char *__get_exec_readproc(char *procfn) {
strerror(errno)); strerror(errno));
exit(-1); exit(-1);
} else if (len >= EXECUTOR_PATH_MAX) { } else if (len >= EXECUTOR_PATH_MAX) {
fprintf(ERRORFILE,"Executable name %.*s is longer than %d characters.\n", fprintf(ERRORFILE,"Resolved path for %s [%s] is longer than %d characters.\n",
EXECUTOR_PATH_MAX, filename, EXECUTOR_PATH_MAX); procfn, filename, EXECUTOR_PATH_MAX);
exit(-1); exit(-1);
} }
filename[len] = '\0'; filename[len] = '\0';
return filename; return filename;
} }
#ifdef HAVE_SYSCTL
/*
* A generic function to ask the kernel via sysctl.
* This is used by most of the open source BSDs, as
* many do not reliably have a /proc mounted.
*/
char *__get_exec_sysctl(int *mib)
{
char buffer[EXECUTOR_PATH_MAX];
char *filename;
size_t len;
len = sizeof(buffer);
if (sysctl(mib, 4, buffer, &len, NULL, 0) == -1) {
fprintf(ERRORFILE,"Can't get executable name from kernel: %s\n",
strerror(errno));
exit(-1);
}
filename=malloc(EXECUTOR_PATH_MAX);
if (!filename) {
fprintf(ERRORFILE,"cannot allocate memory for filename after sysctl: %s\n",strerror(errno));
exit(-1);
}
snprintf(filename,EXECUTOR_PATH_MAX,"%s",buffer);
return filename;
}
#endif /* HAVE_SYSCTL */
#ifdef __APPLE__ #ifdef __APPLE__
/* /*
@ -80,13 +115,13 @@ char *__get_exec_readproc(char *procfn) {
#include <libproc.h> #include <libproc.h>
char* get_executable() { char* get_executable(char *argv0) {
char *filename; char *filename;
pid_t pid; pid_t pid;
filename = malloc(PROC_PIDPATHINFO_MAXSIZE); filename = malloc(PROC_PIDPATHINFO_MAXSIZE);
if (!filename) { if (!filename) {
fprintf(ERRORFILE,"cannot allocate memory for filename: %s\n",strerror(errno)); fprintf(ERRORFILE,"cannot allocate memory for filename before proc_pidpath: %s\n",strerror(errno));
exit(-1); exit(-1);
} }
pid = getpid(); pid = getpid();
@ -98,13 +133,33 @@ char* get_executable() {
return filename; return filename;
} }
#elif defined(__FreeBSD__)
char* get_executable(char *argv0) {
static int mib[] = {
CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1
};
return __get_exec_sysctl(mib);
}
#elif defined(__linux) #elif defined(__linux)
char* get_executable() { char* get_executable(char *argv0) {
return __get_exec_readproc("/proc/self/exe"); return __get_exec_readproc("/proc/self/exe");
} }
#elif defined(__NetBSD__) && defined(KERN_PROC_PATHNAME)
/* Only really new NetBSD kernels have KERN_PROC_PATHNAME */
char* get_executable(char *argv0) {
static int mib[] = {
CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME,
};
return __get_exec_sysctl(mib);
}
#elif defined(__sun) #elif defined(__sun)
/* /*
@ -116,12 +171,45 @@ char* get_executable() {
* doesn't exist on Solaris hasn't read the proc(4) man page.) * doesn't exist on Solaris hasn't read the proc(4) man page.)
*/ */
char* get_executable() { char* get_executable(char *argv0) {
return __get_exec_readproc("/proc/self/path/a.out"); return __get_exec_readproc("/proc/self/path/a.out");
} }
#elif defined(HADOOP_CONF_DIR_IS_ABS)
/*
* This is the fallback for operating systems where
* we don't know how to ask the kernel where the executable
* is located. It is only used if the maven property
* container-executor.conf.dir is set to an absolute path
* for security reasons.
*/
char* get_executable (char *argv0) {
char *filename;
#ifdef HAVE_CANONICALIZE_FILE_NAME
filename=canonicalize_file_name(argv0);
#else
filename=realpath(argv0,NULL);
#endif
if (!filename) {
fprintf(ERRORFILE,"realpath of executable: %s\n",strerror(errno));
exit(-1);
}
return filename;
}
#else #else
#error Cannot safely determine executable path on this operating system. /*
* If we ended up here, we're on an operating system that doesn't
* match any of the above. This means either the OS needs to get a
* code added or the container-executor.conf.dir maven property
* should be set to an absolute path.
*/
#endif #error Cannot safely determine executable path with a relative HADOOP_CONF_DIR on this operating system.
#endif /* platform checks */

View File

@ -139,9 +139,9 @@ in case of validation failures. Also sets up configuration / group information e
This function is to be called in every invocation of container-executor, irrespective This function is to be called in every invocation of container-executor, irrespective
of whether an explicit checksetup operation is requested. */ of whether an explicit checksetup operation is requested. */
static void assert_valid_setup() { static void assert_valid_setup(char *argv0) {
int ret; int ret;
char *executable_file = get_executable(); char *executable_file = get_executable(argv0);
if (!executable_file) { if (!executable_file) {
fprintf(ERRORFILE,"realpath of executable: %s\n",strerror(errno)); fprintf(ERRORFILE,"realpath of executable: %s\n",strerror(errno));
flush_and_close_log_files(); flush_and_close_log_files();
@ -518,7 +518,7 @@ static int validate_run_as_user_commands(int argc, char **argv, int *operation)
int main(int argc, char **argv) { int main(int argc, char **argv) {
open_log_files(); open_log_files();
assert_valid_setup(); assert_valid_setup(argv[0]);
int operation; int operation;
int ret = validate_arguments(argc, argv, &operation); int ret = validate_arguments(argc, argv, &operation);

View File

@ -1031,6 +1031,7 @@ int main(int argc, char **argv) {
LOGFILE = stdout; LOGFILE = stdout;
ERRORFILE = stderr; ERRORFILE = stderr;
printf("Attempting to clean up from any previous runs\n");
// clean up any junk from previous run // clean up any junk from previous run
if (system("chmod -R u=rwx " TEST_ROOT "; rm -fr " TEST_ROOT)) { if (system("chmod -R u=rwx " TEST_ROOT "; rm -fr " TEST_ROOT)) {
exit(1); exit(1);
@ -1043,6 +1044,9 @@ int main(int argc, char **argv) {
if (write_config_file(TEST_ROOT "/test.cfg", 1) != 0) { if (write_config_file(TEST_ROOT "/test.cfg", 1) != 0) {
exit(1); exit(1);
} }
printf("\nOur executable is %s\n",get_executable(argv[0]));
read_executor_config(TEST_ROOT "/test.cfg"); read_executor_config(TEST_ROOT "/test.cfg");
local_dirs = extract_values(strdup(NM_LOCAL_DIRS)); local_dirs = extract_values(strdup(NM_LOCAL_DIRS));