| /** |
| * 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. |
| */ |
| |
| #include "configuration.h" |
| #include "task-controller.h" |
| |
| #include <dirent.h> |
| #include <fcntl.h> |
| #include <fts.h> |
| #include <errno.h> |
| #include <grp.h> |
| #include <unistd.h> |
| #include <signal.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| |
| #define USER_DIR_PATTERN "%s/taskTracker/%s" |
| |
| #define TT_JOB_DIR_PATTERN USER_DIR_PATTERN "/jobcache/%s" |
| |
| #define ATTEMPT_DIR_PATTERN TT_JOB_DIR_PATTERN "/%s/work" |
| |
| #define TASK_SCRIPT "taskjvm.sh" |
| |
| #define TT_LOCAL_TASK_DIR_PATTERN "%s/taskTracker/%s/jobcache/%s/%s" |
| |
| #define TT_SYS_DIR_KEY "mapreduce.cluster.local.dir" |
| |
| #define TT_LOG_DIR_KEY "hadoop.log.dir" |
| |
| #define JOB_FILENAME "job.xml" |
| |
| #define CREDENTIALS_FILENAME "jobToken" |
| |
| #define MIN_USERID_KEY "min.user.id" |
| |
| static const int DEFAULT_MIN_USERID = 1000; |
| |
| #define BANNED_USERS_KEY "banned.users" |
| |
| static const char* DEFAULT_BANNED_USERS[] = {"mapred", "hdfs", "bin", 0}; |
| |
| //struct to store the user details |
| struct passwd *user_detail = NULL; |
| |
| FILE* LOGFILE = NULL; |
| |
| static uid_t tt_uid = -1; |
| static gid_t tt_gid = -1; |
| |
| void set_tasktracker_uid(uid_t user, gid_t group) { |
| tt_uid = user; |
| tt_gid = group; |
| } |
| |
| /** |
| * get the executable filename. |
| */ |
| char* get_executable() { |
| char buffer[PATH_MAX]; |
| snprintf(buffer, PATH_MAX, "/proc/%u/exe", getpid()); |
| char *filename = malloc(PATH_MAX); |
| ssize_t len = readlink(buffer, filename, PATH_MAX); |
| if (len == -1) { |
| fprintf(stderr, "Can't get executable name from %s - %s\n", buffer, |
| strerror(errno)); |
| exit(-1); |
| } else if (len >= PATH_MAX) { |
| fprintf(LOGFILE, "Executable name %.*s is longer than %d characters.\n", |
| PATH_MAX, filename, PATH_MAX); |
| exit(-1); |
| } |
| filename[len] = '\0'; |
| return filename; |
| } |
| |
| /** |
| * Check the permissions on taskcontroller to make sure that security is |
| * promisable. For this, we need task-controller binary to |
| * * be user-owned by root |
| * * be group-owned by a configured special group. |
| * * others do not have any permissions |
| * * be setuid/setgid |
| */ |
| int check_taskcontroller_permissions(char *executable_file) { |
| |
| errno = 0; |
| char * resolved_path = realpath(executable_file, NULL); |
| if (resolved_path == NULL) { |
| fprintf(LOGFILE, |
| "Error resolving the canonical name for the executable : %s!", |
| strerror(errno)); |
| return -1; |
| } |
| |
| struct stat filestat; |
| errno = 0; |
| if (stat(resolved_path, &filestat) != 0) { |
| fprintf(LOGFILE, |
| "Could not stat the executable : %s!.\n", strerror(errno)); |
| return -1; |
| } |
| |
| uid_t binary_euid = filestat.st_uid; // Binary's user owner |
| gid_t binary_gid = filestat.st_gid; // Binary's group owner |
| |
| // Effective uid should be root |
| if (binary_euid != 0) { |
| fprintf(LOGFILE, |
| "The task-controller binary should be user-owned by root.\n"); |
| return -1; |
| } |
| |
| if (binary_gid != getgid()) { |
| fprintf(LOGFILE, "The configured tasktracker group %d is different from" |
| " the group of the executable %d\n", getgid(), binary_gid); |
| return -1; |
| } |
| |
| // check others do not have read/write/execute permissions |
| if ((filestat.st_mode & S_IROTH) == S_IROTH || (filestat.st_mode & S_IWOTH) |
| == S_IWOTH || (filestat.st_mode & S_IXOTH) == S_IXOTH) { |
| fprintf(LOGFILE, |
| "The task-controller binary should not have read or write or" |
| " execute for others.\n"); |
| return -1; |
| } |
| |
| // Binary should be setuid/setgid executable |
| if ((filestat.st_mode & S_ISUID) == 0) { |
| fprintf(LOGFILE, "The task-controller binary should be set setuid.\n"); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * Change the effective user id to limit damage. |
| */ |
| static int change_effective_user(uid_t user, gid_t group) { |
| if (geteuid() == user) { |
| return 0; |
| } |
| if (seteuid(0) != 0) { |
| return -1; |
| } |
| if (setegid(group) != 0) { |
| fprintf(LOGFILE, "Failed to set effective group id %d - %s\n", group, |
| strerror(errno)); |
| return -1; |
| } |
| if (seteuid(user) != 0) { |
| fprintf(LOGFILE, "Failed to set effective user id %d - %s\n", user, |
| strerror(errno)); |
| return -1; |
| } |
| return 0; |
| } |
| |
| /** |
| * Change the real and effective user and group to abandon the super user |
| * priviledges. |
| */ |
| int change_user(uid_t user, gid_t group) { |
| if (user == getuid() && user == geteuid() && |
| group == getgid() && group == getegid()) { |
| return 0; |
| } |
| |
| if (seteuid(0) != 0) { |
| fprintf(LOGFILE, "unable to reacquire root - %s\n", strerror(errno)); |
| fprintf(LOGFILE, "Real: %d:%d; Effective: %d:%d\n", |
| getuid(), getgid(), geteuid(), getegid()); |
| return SETUID_OPER_FAILED; |
| } |
| if (setgid(group) != 0) { |
| fprintf(LOGFILE, "unable to set group to %d - %s\n", group, |
| strerror(errno)); |
| fprintf(LOGFILE, "Real: %d:%d; Effective: %d:%d\n", |
| getuid(), getgid(), geteuid(), getegid()); |
| return SETUID_OPER_FAILED; |
| } |
| if (setuid(user) != 0) { |
| fprintf(LOGFILE, "unable to set user to %d - %s\n", user, strerror(errno)); |
| fprintf(LOGFILE, "Real: %d:%d; Effective: %d:%d\n", |
| getuid(), getgid(), geteuid(), getegid()); |
| return SETUID_OPER_FAILED; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * Utility function to concatenate argB to argA using the concat_pattern. |
| */ |
| char *concatenate(char *concat_pattern, char *return_path_name, |
| int numArgs, ...) { |
| va_list ap; |
| va_start(ap, numArgs); |
| int strlen_args = 0; |
| char *arg = NULL; |
| int j; |
| for (j = 0; j < numArgs; j++) { |
| arg = va_arg(ap, char*); |
| if (arg == NULL) { |
| fprintf(LOGFILE, "One of the arguments passed for %s in null.\n", |
| return_path_name); |
| return NULL; |
| } |
| strlen_args += strlen(arg); |
| } |
| va_end(ap); |
| |
| char *return_path = NULL; |
| int str_len = strlen(concat_pattern) + strlen_args + 1; |
| |
| return_path = (char *) malloc(str_len); |
| if (return_path == NULL) { |
| fprintf(LOGFILE, "Unable to allocate memory for %s.\n", return_path_name); |
| return NULL; |
| } |
| va_start(ap, numArgs); |
| vsnprintf(return_path, str_len, concat_pattern, ap); |
| va_end(ap); |
| return return_path; |
| } |
| |
| /** |
| * Get the job-directory path from tt_root, user name and job-id |
| */ |
| char *get_job_directory(const char * tt_root, const char *user, |
| const char *jobid) { |
| return concatenate(TT_JOB_DIR_PATTERN, "job_dir_path", 3, tt_root, user, |
| jobid); |
| } |
| |
| /** |
| * Get the user directory of a particular user |
| */ |
| char *get_user_directory(const char *tt_root, const char *user) { |
| return concatenate(USER_DIR_PATTERN, "user_dir_path", 2, tt_root, user); |
| } |
| |
| char *get_job_work_directory(const char *job_dir) { |
| return concatenate("%s/work", "job work", 1, job_dir); |
| } |
| |
| /** |
| * Get the attempt directory for the given attempt_id |
| */ |
| char *get_attempt_work_directory(const char *tt_root, const char *user, |
| const char *job_id, const char *attempt_id) { |
| return concatenate(ATTEMPT_DIR_PATTERN, "attempt_dir_path", 4, |
| tt_root, user, job_id, attempt_id); |
| } |
| |
| char *get_task_launcher_file(const char* work_dir) { |
| return concatenate("%s/%s", "task launcher", 2, work_dir, TASK_SCRIPT); |
| } |
| |
| /** |
| * Get the job log directory. |
| * Ensures that the result is a realpath and that it is underneath the |
| * tt log root. |
| */ |
| char* get_job_log_directory(const char* jobid) { |
| char* log_dir = get_value(TT_LOG_DIR_KEY); |
| if (log_dir == NULL) { |
| fprintf(LOGFILE, "Log directory %s is not configured.\n", TT_LOG_DIR_KEY); |
| return NULL; |
| } |
| char *result = concatenate("%s/userlogs/%s", "job log dir", 2, log_dir, |
| jobid); |
| if (result == NULL) { |
| fprintf(LOGFILE, "failed to get memory in get_job_log_directory for %s" |
| " and %s\n", log_dir, jobid); |
| } |
| free(log_dir); |
| return result; |
| } |
| |
| /* |
| * Get a user subdirectory. |
| */ |
| char *get_user_subdirectory(const char *tt_root, |
| const char *user, |
| const char *subdir) { |
| char * user_dir = get_user_directory(tt_root, user); |
| char * result = concatenate("%s/%s", "user subdir", 2, |
| user_dir, subdir); |
| free(user_dir); |
| return result; |
| } |
| |
| /** |
| * Ensure that the given path and all of the parent directories are created |
| * with the desired permissions. |
| */ |
| int mkdirs(const char* path, mode_t perm) { |
| char *buffer = strdup(path); |
| char *token; |
| int cwd = open("/", O_RDONLY); |
| if (cwd == -1) { |
| fprintf(LOGFILE, "Can't open / in %s - %s\n", path, strerror(errno)); |
| free(buffer); |
| return -1; |
| } |
| for(token = strtok(buffer, "/"); token != NULL; token = strtok(NULL, "/")) { |
| if (mkdirat(cwd, token, perm) != 0) { |
| if (errno != EEXIST) { |
| fprintf(LOGFILE, "Can't create directory %s in %s - %s\n", |
| token, path, strerror(errno)); |
| close(cwd); |
| free(buffer); |
| return -1; |
| } |
| } |
| int new_dir = openat(cwd, token, O_RDONLY); |
| close(cwd); |
| cwd = new_dir; |
| if (cwd == -1) { |
| fprintf(LOGFILE, "Can't open %s in %s - %s\n", token, path, |
| strerror(errno)); |
| free(buffer); |
| return -1; |
| } |
| } |
| free(buffer); |
| close(cwd); |
| return 0; |
| } |
| |
| /** |
| * Function to prepare the attempt directories for the task JVM. |
| * It creates the task work and log directories. |
| */ |
| static int create_attempt_directories(const char* user, const char *job_id, |
| const char *task_id) { |
| // create dirs as 0750 |
| const mode_t perms = S_IRWXU | S_IRGRP | S_IXGRP; |
| if (job_id == NULL || task_id == NULL || user == NULL) { |
| fprintf(LOGFILE, |
| "Either task_id is null or the user passed is null.\n"); |
| return -1; |
| } |
| int result = 0; |
| |
| char **local_dir = get_values(TT_SYS_DIR_KEY); |
| |
| if (local_dir == NULL) { |
| fprintf(LOGFILE, "%s is not configured.\n", TT_SYS_DIR_KEY); |
| return -1; |
| } |
| |
| char **local_dir_ptr; |
| for(local_dir_ptr = local_dir; *local_dir_ptr != NULL; ++local_dir_ptr) { |
| char *task_dir = get_attempt_work_directory(*local_dir_ptr, user, job_id, |
| task_id); |
| if (task_dir == NULL) { |
| free_values(local_dir); |
| return -1; |
| } |
| if (mkdirs(task_dir, perms) != 0) { |
| // continue on to create other task directories |
| free(task_dir); |
| } else { |
| free(task_dir); |
| } |
| } |
| free_values(local_dir); |
| |
| // also make the directory for the task logs |
| char *job_task_name = malloc(strlen(job_id) + strlen(task_id) + 2); |
| if (job_task_name == NULL) { |
| fprintf(LOGFILE, "Malloc of job task name failed\n"); |
| result = -1; |
| } else { |
| sprintf(job_task_name, "%s/%s", job_id, task_id); |
| char *log_dir = get_job_log_directory(job_task_name); |
| free(job_task_name); |
| if (log_dir == NULL) { |
| result = -1; |
| } else if (mkdirs(log_dir, perms) != 0) { |
| result = -1; |
| } |
| free(log_dir); |
| } |
| return result; |
| } |
| |
| /** |
| * Load the user information for a given user name. |
| */ |
| static struct passwd* get_user_info(const char* user) { |
| int string_size = sysconf(_SC_GETPW_R_SIZE_MAX); |
| void* buffer = malloc(string_size + sizeof(struct passwd)); |
| struct passwd *result = NULL; |
| if (getpwnam_r(user, buffer, buffer + sizeof(struct passwd), string_size, |
| &result) != 0) { |
| free(buffer); |
| fprintf(LOGFILE, "Can't get user information %s - %s\n", user, |
| strerror(errno)); |
| return NULL; |
| } |
| return result; |
| } |
| |
| /** |
| * Is the user a real user account? |
| * Checks: |
| * 1. Not root |
| * 2. UID is above the minimum configured. |
| * 3. Not in banned user list |
| * Returns NULL on failure |
| */ |
| struct passwd* check_user(const char *user) { |
| if (strcmp(user, "root") == 0) { |
| fprintf(LOGFILE, "Running as root is not allowed\n"); |
| return NULL; |
| } |
| char *min_uid_str = get_value(MIN_USERID_KEY); |
| int min_uid = DEFAULT_MIN_USERID; |
| if (min_uid_str != NULL) { |
| char *end_ptr = NULL; |
| min_uid = strtol(min_uid_str, &end_ptr, 10); |
| if (min_uid_str == end_ptr || *end_ptr != '\0') { |
| fprintf(LOGFILE, "Illegal value of %s for %s in configuration\n", |
| min_uid_str, MIN_USERID_KEY); |
| free(min_uid_str); |
| return NULL; |
| } |
| free(min_uid_str); |
| } |
| struct passwd *user_info = get_user_info(user); |
| if (NULL == user_info) { |
| fprintf(LOGFILE, "User %s not found\n", user); |
| return NULL; |
| } |
| if (user_info->pw_uid < min_uid) { |
| fprintf(LOGFILE, "Requested user %s has id %d, which is below the " |
| "minimum allowed %d\n", user, user_info->pw_uid, min_uid); |
| free(user_info); |
| return NULL; |
| } |
| char **banned_users = get_values(BANNED_USERS_KEY); |
| char **banned_user = (banned_users == NULL) ? |
| (char**) DEFAULT_BANNED_USERS : banned_users; |
| for(; *banned_user; ++banned_user) { |
| if (strcmp(*banned_user, user) == 0) { |
| free(user_info); |
| fprintf(LOGFILE, "Requested user %s is banned\n", user); |
| return NULL; |
| } |
| } |
| if (banned_users != NULL) { |
| free_values(banned_users); |
| } |
| return user_info; |
| } |
| |
| /** |
| * function used to populate and user_details structure. |
| */ |
| int set_user(const char *user) { |
| // free any old user |
| if (user_detail != NULL) { |
| free(user_detail); |
| user_detail = NULL; |
| } |
| user_detail = check_user(user); |
| if (user_detail == NULL) { |
| return -1; |
| } |
| return change_effective_user(user_detail->pw_uid, user_detail->pw_gid); |
| } |
| |
| /** |
| * Change the ownership of the given file or directory to the new user. |
| */ |
| static int change_owner(const char* path, uid_t user, gid_t group) { |
| if (geteuid() == user && getegid() == group) { |
| return 0; |
| } else { |
| uid_t old_user = geteuid(); |
| gid_t old_group = getegid(); |
| if (change_effective_user(0, group) != 0) { |
| return -1; |
| } |
| 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 change_effective_user(old_user, old_group); |
| } |
| } |
| |
| /** |
| * Create a top level directory for the user. |
| * It assumes that the parent directory is *not* writable by the user. |
| * It creates directories with 02700 permissions owned by the user |
| * and with the group set to the task tracker group. |
| * return non-0 on failure |
| */ |
| int create_directory_for_user(const char* path) { |
| // set 2750 permissions and group sticky bit |
| mode_t permissions = S_IRWXU | S_IRGRP | S_IXGRP | S_ISGID; |
| uid_t user = geteuid(); |
| gid_t group = getegid(); |
| int ret = 0; |
| ret = change_effective_user(tt_uid, tt_gid); |
| if (ret == 0) { |
| if (mkdir(path, permissions) == 0) { |
| // need to reassert the group sticky bit |
| if (chmod(path, permissions) != 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, tt_gid) != 0) { |
| ret = -1; |
| } |
| } else if (errno == EEXIST) { |
| struct stat file_stat; |
| if (stat(path, &file_stat) != 0) { |
| fprintf(LOGFILE, "Can't stat directory %s - %s\n", path, |
| strerror(errno)); |
| ret = -1; |
| } else { |
| if (file_stat.st_uid != user || |
| file_stat.st_gid != tt_gid) { |
| fprintf(LOGFILE, "Directory %s owned by wrong user or group. " |
| "Expected %d:%d and found %d:%d.\n", |
| path, user, tt_gid, file_stat.st_uid, file_stat.st_gid); |
| ret = -1; |
| } |
| } |
| } else { |
| fprintf(LOGFILE, "Failed to create directory %s - %s\n", path, |
| strerror(errno)); |
| ret = -1; |
| } |
| } |
| if (change_effective_user(user, group) != 0) { |
| ret = -1; |
| } |
| return ret; |
| } |
| |
| /** |
| * Open a file as the tasktracker and return a file descriptor for it. |
| * Returns -1 on error |
| */ |
| static int open_file_as_task_tracker(const char* filename) { |
| uid_t user = geteuid(); |
| gid_t group = getegid(); |
| if (change_effective_user(tt_uid, tt_gid) != 0) { |
| return -1; |
| } |
| int result = open(filename, O_RDONLY); |
| if (result == -1) { |
| fprintf(LOGFILE, "Can't open file %s as task tracker - %s\n", filename, |
| strerror(errno)); |
| } |
| if (change_effective_user(user, group)) { |
| result = -1; |
| } |
| return result; |
| } |
| |
| /** |
| * Copy a file from a fd to a given filename. |
| * The new file must not exist and it is created with permissions perm. |
| * The input stream is closed. |
| * Return 0 if everything is ok. |
| */ |
| static int copy_file(int input, const char* in_filename, |
| const char* out_filename, mode_t perm) { |
| const int buffer_size = 128*1024; |
| char buffer[buffer_size]; |
| int out_fd = open(out_filename, O_WRONLY|O_CREAT|O_EXCL|O_NOFOLLOW, perm); |
| if (out_fd == -1) { |
| fprintf(LOGFILE, "Can't open %s for output - %s\n", out_filename, |
| strerror(errno)); |
| return -1; |
| } |
| ssize_t len = read(input, buffer, buffer_size); |
| while (len > 0) { |
| ssize_t pos = 0; |
| while (pos < len) { |
| ssize_t write_result = write(out_fd, buffer + pos, len - pos); |
| if (write_result <= 0) { |
| fprintf(LOGFILE, "Error writing to %s - %s\n", out_filename, |
| strerror(errno)); |
| close(out_fd); |
| return -1; |
| } |
| pos += write_result; |
| } |
| len = read(input, buffer, buffer_size); |
| } |
| if (len < 0) { |
| fprintf(LOGFILE, "Failed to read file %s - %s\n", in_filename, |
| strerror(errno)); |
| close(out_fd); |
| return -1; |
| } |
| if (close(out_fd) != 0) { |
| fprintf(LOGFILE, "Failed to close file %s - %s\n", out_filename, |
| strerror(errno)); |
| return -1; |
| } |
| close(input); |
| return 0; |
| } |
| |
| /** |
| * Function to initialize the user directories of a user. |
| */ |
| int initialize_user(const char *user) { |
| char **local_dir = get_values(TT_SYS_DIR_KEY); |
| if (local_dir == NULL) { |
| fprintf(LOGFILE, "%s is not configured.\n", TT_SYS_DIR_KEY); |
| return INVALID_TT_ROOT; |
| } |
| |
| char *user_dir; |
| char **local_dir_ptr = local_dir; |
| int failed = 0; |
| for(local_dir_ptr = local_dir; *local_dir_ptr != 0; ++local_dir_ptr) { |
| user_dir = get_user_directory(*local_dir_ptr, user); |
| if (user_dir == NULL) { |
| fprintf(LOGFILE, "Couldn't get userdir directory for %s.\n", user); |
| failed = 1; |
| break; |
| } |
| if (create_directory_for_user(user_dir) != 0) { |
| failed = 1; |
| } |
| free(user_dir); |
| } |
| free_values(local_dir); |
| return failed ? INITIALIZE_USER_FAILED : 0; |
| } |
| |
| /** |
| * Function to prepare the job directories for the task JVM. |
| */ |
| int initialize_job(const char *user, const char *jobid, |
| const char* credentials, const char* job_xml, |
| char* const* args) { |
| if (jobid == NULL || user == NULL) { |
| fprintf(LOGFILE, "Either jobid is null or the user passed is null.\n"); |
| return INVALID_ARGUMENT_NUMBER; |
| } |
| |
| // create the user directory |
| int result = initialize_user(user); |
| if (result != 0) { |
| return result; |
| } |
| |
| // create the log directory for the job |
| char *job_log_dir = get_job_log_directory(jobid); |
| if (job_log_dir == NULL) { |
| return -1; |
| } |
| result = create_directory_for_user(job_log_dir); |
| free(job_log_dir); |
| if (result != 0) { |
| return -1; |
| } |
| |
| // open up the credentials file |
| int cred_file = open_file_as_task_tracker(credentials); |
| if (cred_file == -1) { |
| return -1; |
| } |
| |
| int job_file = open_file_as_task_tracker(job_xml); |
| if (job_file == -1) { |
| return -1; |
| } |
| |
| // give up root privs |
| if (change_user(user_detail->pw_uid, user_detail->pw_gid) != 0) { |
| return -1; |
| } |
| |
| // 750 |
| mode_t permissions = S_IRWXU | S_IRGRP | S_IXGRP; |
| char **tt_roots = get_values(TT_SYS_DIR_KEY); |
| |
| if (tt_roots == NULL) { |
| return INVALID_CONFIG_FILE; |
| } |
| |
| char **tt_root; |
| char *primary_job_dir = NULL; |
| for(tt_root=tt_roots; *tt_root != NULL; ++tt_root) { |
| char *job_dir = get_job_directory(*tt_root, user, jobid); |
| if (job_dir == NULL) { |
| // try the next one |
| } else if (mkdirs(job_dir, permissions) != 0) { |
| free(job_dir); |
| } else if (primary_job_dir == NULL) { |
| primary_job_dir = job_dir; |
| } else { |
| free(job_dir); |
| } |
| } |
| free_values(tt_roots); |
| if (primary_job_dir == NULL) { |
| fprintf(LOGFILE, "Did not create any job directories\n"); |
| return -1; |
| } |
| |
| char *cred_file_name = concatenate("%s/%s", "cred file", 2, |
| primary_job_dir, CREDENTIALS_FILENAME); |
| if (cred_file_name == NULL) { |
| return -1; |
| } |
| if (copy_file(cred_file, credentials, cred_file_name, S_IRUSR|S_IWUSR) != 0){ |
| return -1; |
| } |
| char *job_file_name = concatenate("%s/%s", "job file", 2, |
| primary_job_dir, JOB_FILENAME); |
| if (job_file_name == NULL) { |
| return -1; |
| } |
| if (copy_file(job_file, job_xml, job_file_name, |
| S_IRUSR|S_IWUSR|S_IRGRP) != 0) { |
| return -1; |
| } |
| fclose(stdin); |
| fflush(LOGFILE); |
| if (LOGFILE != stdout) { |
| fclose(stdout); |
| } |
| fclose(stderr); |
| chdir(primary_job_dir); |
| execvp(args[0], args); |
| fprintf(LOGFILE, "Failure to exec job initialization process - %s\n", |
| strerror(errno)); |
| return -1; |
| } |
| |
| /* |
| * Function used to launch a task as the provided user. It does the following : |
| * 1) Creates attempt work dir and log dir to be accessible by the child |
| * 2) Copies the script file from the TT to the work directory |
| * 3) Sets up the environment |
| * 4) Does an execlp on the same in order to replace the current image with |
| * task image. |
| */ |
| int run_task_as_user(const char *user, const char *job_id, |
| const char *task_id, const char *work_dir, |
| const char *script_name) { |
| int exit_code = -1; |
| char *task_script_path = NULL; |
| if (create_attempt_directories(user, job_id, task_id) != 0) { |
| goto cleanup; |
| } |
| int task_file_source = open_file_as_task_tracker(script_name); |
| if (task_file_source == -1) { |
| goto cleanup; |
| } |
| task_script_path = get_task_launcher_file(work_dir); |
| if (task_script_path == NULL) { |
| exit_code = OUT_OF_MEMORY; |
| goto cleanup; |
| } |
| if (copy_file(task_file_source, script_name,task_script_path,S_IRWXU) != 0) { |
| goto cleanup; |
| } |
| |
| //change the user |
| fcloseall(); |
| umask(0027); |
| if (chdir(work_dir) != 0) { |
| fprintf(LOGFILE, "Can't change directory to %s -%s\n", work_dir, |
| strerror(errno)); |
| goto cleanup; |
| } |
| if (change_user(user_detail->pw_uid, user_detail->pw_gid) != 0) { |
| exit_code = SETUID_OPER_FAILED; |
| goto cleanup; |
| } |
| |
| if (execlp(task_script_path, task_script_path, NULL) != 0) { |
| fprintf(LOGFILE, "Couldn't execute the task jvm file %s - %s", |
| task_script_path, strerror(errno)); |
| exit_code = UNABLE_TO_EXECUTE_TASK_SCRIPT; |
| goto cleanup; |
| } |
| exit_code = 0; |
| |
| cleanup: |
| free(task_script_path); |
| return exit_code; |
| } |
| |
| /** |
| * Function used to signal a task launched by the user. |
| * The function sends appropriate signal to the process group |
| * specified by the task_pid. |
| */ |
| int signal_user_task(const char *user, int pid, int sig) { |
| if(pid <= 0) { |
| return INVALID_TASK_PID; |
| } |
| |
| if (change_user(user_detail->pw_uid, user_detail->pw_gid) != 0) { |
| return SETUID_OPER_FAILED; |
| } |
| |
| //Don't continue if the process-group is not alive anymore. |
| int has_group = 1; |
| if (kill(-pid,0) < 0) { |
| if (kill(pid, 0) < 0) { |
| if (errno == ESRCH) { |
| return INVALID_TASK_PID; |
| } |
| fprintf(LOGFILE, "Error signalling task %d with %d - %s\n", |
| pid, sig, strerror(errno)); |
| return -1; |
| } else { |
| has_group = 0; |
| } |
| } |
| |
| if (kill((has_group ? -1 : 1) * pid, sig) < 0) { |
| if(errno != ESRCH) { |
| fprintf(LOGFILE, |
| "Error signalling process group %d with signal %d - %s\n", |
| -pid, sig, strerror(errno)); |
| return UNABLE_TO_KILL_TASK; |
| } else { |
| return INVALID_TASK_PID; |
| } |
| } |
| fprintf(LOGFILE, "Killing process %s%d with %d\n", |
| (has_group ? "group " :""), pid, sig); |
| return 0; |
| } |
| |
| /** |
| * Delete a final directory as the task tracker user. |
| */ |
| static int rmdir_as_tasktracker(const char* path) { |
| int user_uid = geteuid(); |
| int user_gid = getegid(); |
| int ret = change_effective_user(tt_uid, tt_gid); |
| if (ret == 0) { |
| if (rmdir(path) != 0) { |
| fprintf(LOGFILE, "rmdir of %s failed - %s\n", path, strerror(errno)); |
| ret = -1; |
| } |
| } |
| // always change back |
| if (change_effective_user(user_uid, user_gid) != 0) { |
| ret = -1; |
| } |
| return ret; |
| } |
| |
| /** |
| * Recursively delete the given path. |
| * full_path : the path to delete |
| * needs_tt_user: the top level directory must be deleted by the tt user. |
| */ |
| static int delete_path(const char *full_path, |
| int needs_tt_user) { |
| int exit_code = 0; |
| |
| if (full_path == NULL) { |
| fprintf(LOGFILE, "Path is null\n"); |
| exit_code = UNABLE_TO_BUILD_PATH; // may be malloc failed |
| } else { |
| char *(paths[]) = {strdup(full_path), 0}; |
| if (paths[0] == NULL) { |
| fprintf(LOGFILE, "Malloc failed in delete_path\n"); |
| return -1; |
| } |
| // check to make sure the directory exists |
| if (access(full_path, F_OK) != 0) { |
| if (errno == ENOENT) { |
| free(paths[0]); |
| return 0; |
| } |
| } |
| FTS* tree = fts_open(paths, FTS_PHYSICAL | FTS_XDEV, NULL); |
| FTSENT* entry = NULL; |
| int ret = 0; |
| |
| if (tree == NULL) { |
| fprintf(LOGFILE, |
| "Cannot open file traversal structure for the path %s:%s.\n", |
| full_path, strerror(errno)); |
| free(paths[0]); |
| return -1; |
| } |
| while (((entry = fts_read(tree)) != NULL) && exit_code == 0) { |
| switch (entry->fts_info) { |
| |
| case FTS_DP: // A directory being visited in post-order |
| if (!needs_tt_user || |
| strcmp(entry->fts_path, full_path) != 0) { |
| if (rmdir(entry->fts_accpath) != 0) { |
| fprintf(LOGFILE, "Couldn't delete directory %s - %s\n", |
| entry->fts_path, strerror(errno)); |
| exit_code = -1; |
| } |
| } |
| break; |
| |
| case FTS_F: // A regular file |
| case FTS_SL: // A symbolic link |
| case FTS_SLNONE: // A broken symbolic link |
| case FTS_DEFAULT: // Unknown type of file |
| if (unlink(entry->fts_accpath) != 0) { |
| fprintf(LOGFILE, "Couldn't delete file %s - %s\n", entry->fts_path, |
| strerror(errno)); |
| exit_code = -1; |
| } |
| break; |
| |
| case FTS_DNR: // Unreadable directory |
| fprintf(LOGFILE, "Unreadable directory %s. Skipping..\n", |
| entry->fts_path); |
| break; |
| |
| case FTS_D: // A directory in pre-order |
| // if the directory isn't readable, chmod it |
| if ((entry->fts_statp->st_mode & 0200) == 0) { |
| fprintf(LOGFILE, "Unreadable directory %s, chmoding.\n", |
| entry->fts_path); |
| if (chmod(entry->fts_accpath, 0700) != 0) { |
| fprintf(LOGFILE, "Error chmoding %s - %s, continuing\n", |
| entry->fts_path, strerror(errno)); |
| } |
| } |
| break; |
| |
| case FTS_NS: // A file with no stat(2) information |
| // usually a root directory that doesn't exist |
| fprintf(LOGFILE, "Directory not found %s\n", entry->fts_path); |
| break; |
| |
| case FTS_DC: // A directory that causes a cycle |
| case FTS_DOT: // A dot directory |
| case FTS_NSOK: // No stat information requested |
| break; |
| |
| case FTS_ERR: // Error return |
| fprintf(LOGFILE, "Error traversing directory %s - %s\n", |
| entry->fts_path, strerror(entry->fts_errno)); |
| exit_code = -1; |
| break; |
| break; |
| default: |
| exit_code = -1; |
| break; |
| } |
| } |
| ret = fts_close(tree); |
| if (exit_code == 0 && ret != 0) { |
| fprintf(LOGFILE, "Error in fts_close while deleting %s\n", full_path); |
| exit_code = -1; |
| } |
| if (needs_tt_user) { |
| // If the delete failed, try a final rmdir as root on the top level. |
| // That handles the case where the top level directory is in a directory |
| // that is owned by the task tracker. |
| exit_code = rmdir_as_tasktracker(full_path); |
| } |
| free(paths[0]); |
| } |
| return exit_code; |
| } |
| |
| /** |
| * Delete the given directory as the user from each of the tt_root directories |
| * user: the user doing the delete |
| * subdir: the subdir to delete (if baseDirs is empty, this is treated as |
| an absolute path) |
| * baseDirs: (optional) the baseDirs where the subdir is located |
| */ |
| int delete_as_user(const char *user, |
| const char *subdir, |
| char* const* baseDirs) { |
| int ret = 0; |
| |
| char** ptr; |
| |
| if (baseDirs == NULL || *baseDirs == NULL) { |
| return delete_path(subdir, strlen(subdir) == 0); |
| } |
| // do the delete |
| for(ptr = (char**)baseDirs; *ptr != NULL; ++ptr) { |
| char* full_path = concatenate("%s/%s", "user subdir", 2, |
| *ptr, subdir); |
| if (full_path == NULL) { |
| return -1; |
| } |
| int this_ret = delete_path(full_path, strlen(subdir) == 0); |
| free(full_path); |
| // delete as much as we can, but remember the error |
| if (this_ret != 0) { |
| ret = this_ret; |
| } |
| } |
| return ret; |
| } |
| |
| /** |
| * delete a given log directory |
| */ |
| int delete_log_directory(const char *subdir) { |
| char* log_subdir = get_job_log_directory(subdir); |
| int ret = -1; |
| if (log_subdir != NULL) { |
| ret = delete_path(log_subdir, strchr(subdir, '/') == NULL); |
| } |
| free(log_subdir); |
| return ret; |
| } |