blob: 49ae0937f92d12d127f3e6d129f0c4d61503f0ad [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* This code implements OS-specific ways to get the absolute
* filename of the executable. Typically, one would use
* realpath(argv[0]) (or equivalent), however, because this
* code runs as setuid and will be used later on to determine
* relative paths, we want something a big more secure
* since argv[0] is replaceable by malicious code.
*
* NOTE! The value returned will be free()'d later on!
*
*/
#include "config.h"
#include "configuration.h"
#include "container-executor.h"
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#ifdef HAVE_SYS_SYSCTL_H
#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#endif
/*
* A generic function to read a link and return
* the value for use with System V procfs.
* With much thanks to Tom Killian, Roger Faulkner,
* and Ron Gomes, this is pretty generic code.
*/
char *__get_exec_readproc(char *procfn) {
char *filename;
ssize_t len;
filename = malloc(EXECUTOR_PATH_MAX);
if (!filename) {
fprintf(ERRORFILE,"cannot allocate memory for filename before readlink: %s\n",strerror(errno));
exit(-1);
}
len = readlink(procfn, filename, EXECUTOR_PATH_MAX);
if (len == -1) {
fprintf(ERRORFILE,"Can't get executable name from %s - %s\n", procfn,
strerror(errno));
exit(-1);
} else if (len >= EXECUTOR_PATH_MAX) {
fprintf(ERRORFILE,"Resolved path for %s [%s] is longer than %d characters.\n",
procfn, filename, EXECUTOR_PATH_MAX);
exit(-1);
}
filename[len] = '\0';
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__
/*
* Mac OS X doesn't have a procfs, but there is
* libproc which we can use instead. It is available
* in most modern versions of OS X as of this writing (2016).
*/
#include <libproc.h>
char* get_executable(char *argv0) {
char *filename;
pid_t pid;
filename = malloc(PROC_PIDPATHINFO_MAXSIZE);
if (!filename) {
fprintf(ERRORFILE,"cannot allocate memory for filename before proc_pidpath: %s\n",strerror(errno));
exit(-1);
}
pid = getpid();
if (proc_pidpath(pid,filename,PROC_PIDPATHINFO_MAXSIZE) <= 0) {
fprintf(ERRORFILE,"Can't get executable name from pid %u - %s\n", pid,
strerror(errno));
exit(-1);
}
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)
char* get_executable(char *argv0) {
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)
/*
* It's tempting to use getexecname(), but there is no guarantee
* we will get a full path and worse, we'd be reliant on getcwd()
* being where our exec is at. Instead, we'll use the /proc
* method, using the "invisible" /proc/self link that only the
* process itself can see. (Anyone that tells you /proc/self
* doesn't exist on Solaris hasn't read the proc(4) man page.)
*/
char* get_executable(char *argv0) {
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
/*
* 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.
*/
#error Cannot safely determine executable path with a relative HADOOP_CONF_DIR on this operating system.
#endif /* platform checks */