#include "run.h" #include "log.h" #include "util/array.h" #include "util/fs.h" #include #include #include #include #include #include #include #include #include #include /* as per mktemp(3p) the last 6 characters of object_marker_tmpl must be Xs */ static const char object_marker_tmpl[] = ".skonfig-XXXXXX"; double tv_diff(struct timespec *start, struct timespec *end) { return (end->tv_sec + end->tv_nsec/10e9) - (start->tv_sec + start->tv_nsec/10e9); } /** * sk_run_gen_workdir_path: * determine the path to use for the local workdir and create it if necessary. * * @param config: config struct * @param dest: pointer to a buffer of at least PATH_MAX+1 length. * @return pointer to dest, on error: NULL */ char *sk_run_gen_workdir_path(struct sk_config *config, char *dest) { dest[PATH_MAX] = '\0'; if (NULL != config->global.out_path) { (void)strncpy(dest, config->global.out_path, PATH_MAX); } else { /* generate a random directory */ if (NULL == mktempdir(dest)) { sk_error("local_workdir creation failed (%s): %s", dest, "retries exhausted"); return NULL; } } if (dest[PATH_MAX]) { goto too_long; } return dest; too_long: sk_error("local_workdir too long: %s", dest); return NULL; } sk_run_error sk_run_prepare_local_hostdir(const char *local_hostdir) { static const char *subdirs[] = { "bin", "conf", "explorer", "object", "stdout", "stderr" }; char subpath[PATH_MAX+1]; /* create local host dir */ if (mkdir(local_hostdir, S_IRWXU)) { sk_error("failed to create host directory (%s): %s", local_hostdir, strerror(errno)); return SK_RUN_GENERIC_ERROR; } /* create sub directories of host_dir */ for (size_t i = 0; i < arraylen(subdirs); ++i) { if (PATH_MAX < pathjoin_r( subpath, PATH_MAX+1, local_hostdir, subdirs[i])) { goto path_overflow; } if (mkdir(subpath, S_IRWXU)) { sk_error("failed to create directory (%s): %s", subpath, strerror(errno)); return SK_RUN_GENERIC_ERROR; } } /* create messages file */ if (PATH_MAX < pathjoin_r(subpath, PATH_MAX+1, local_hostdir, "messages")) { goto path_overflow; } if (0 > creatfile(subpath, S_IRUSR | S_IWUSR)) { sk_error("failed to create %s: %s", subpath, strerror(errno)); return SK_RUN_GENERIC_ERROR; } return SK_RUN_OK; path_overflow: sk_error("generated local workdir path is longer than PATH_MAX"); return SK_RUN_GENERIC_ERROR; } sk_run_error sk_run_prepare_remote_workdir( const char *target_host, bool dry_run, struct sk_config *config) { /* TODO */ return SK_RUN_OK; } sk_run_error sk_run_link_local_hostdir( const char *local_hostdir, struct sk_config *config) { static const char *conf_subdirs_linked[] = { "explorer", "files", "manifest", "type" }; char tmppath[PATH_MAX+1]; /* create linked host_dir/conf sub directories */ for (size_t i = 0; i < arraylen(conf_subdirs_linked); ++i) { if (PATH_MAX < pathjoin_r( tmppath, PATH_MAX+1, local_hostdir, "conf", conf_subdirs_linked[i])) { goto path_overflow; } if (mkdir(tmppath, S_IRWXU)) { sk_error("failed to create directory (%s): %s", tmppath, strerror(errno)); return SK_RUN_GENERIC_ERROR; } } /* iterate over SK_PATH directories and link their conents to the hostdir */ if (NULL == config->global.conf_dir) { /* nothing to do */ return SK_RUN_OK; } for (size_t i = 0; NULL != config->global.conf_dir[i]; ++i) { const char *conf_src = config->global.conf_dir[i]; sk_debug("Checking conf_dir %s ...", conf_src); for (size_t j = 0; j < arraylen(conf_subdirs_linked); ++j) { if (PATH_MAX < pathjoin_r( tmppath, PATH_MAX+1, conf_src, conf_subdirs_linked[j])) { goto path_overflow; } /* check if said subdir exists in the current SK_PATH element */ if (!exists(tmppath)) continue; DIR *dirp = opendir(tmppath); if (NULL == dirp) { sk_error("cannot open directory (%s): %s", tmppath, strerror(errno)); continue; } struct dirent *dp; char src[PATH_MAX+1], dst[PATH_MAX+1]; while (NULL != (dp = readdir(dirp))) { if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) { continue; } if (PATH_MAX < pathjoin_r( tmppath, PATH_MAX+1, conf_src, conf_subdirs_linked[j], dp->d_name)) { sk_error("source path too long: %s/%s/%s", conf_src, conf_subdirs_linked[j], dp->d_name); continue; } if (NULL == realpath(tmppath, src)) { sk_error("cannot resolve path %s: %s", tmppath, strerror(errno)); continue; } if (PATH_MAX < pathjoin_r( dst, PATH_MAX+1, local_hostdir, "conf", conf_subdirs_linked[j], dp->d_name)) { sk_error("destination path too long for conf/%s/%s", conf_subdirs_linked[j], dp->d_name); continue; } if (exists(dst)) { /* already exists -> remove first */ remove(dst); } sk_trace("Linking %s -> %s", dst, src); if (symlink(src, dst)) { sk_error( "Linking %s %s to %s failed: %s", conf_subdirs_linked[j], src, dst, strerror(errno)); } } (void)closedir(dirp); } } return SK_RUN_OK; path_overflow: sk_error("path is longer than PATH_MAX"); return SK_RUN_GENERIC_ERROR; } sk_run_error sk_run_install_type_cmds(const char *local_hostdir) { static const char *runner_path = LIBEXECDIR stringize(DIRSEP) "type-runner"; char types_dir[PATH_MAX+1]; if (PATH_MAX < pathjoin_r( types_dir, PATH_MAX+1, local_hostdir, "conf", "type")) { sk_error("path is longer than PATH_MAX"); return SK_RUN_GENERIC_ERROR; } DIR *dirp = opendir(types_dir); struct dirent *dp; while (NULL != (dp = readdir(dirp))) { if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) { continue; } char dst[PATH_MAX+1]; if (PATH_MAX < pathjoin_r( dst, PATH_MAX+1, local_hostdir, "bin", dp->d_name)) { sk_error( "path for type command is longer than PATH_MAX. " "Cannot link type runner.", dp->d_name); continue; } sk_trace("Linking type runner: %s -> %s", dst, runner_path); if (symlink(runner_path, dst)) { sk_error( "Linking %s to %s failed: %s", runner_path, dst, strerror(errno)); } } return SK_RUN_OK; } sk_run_error sk_run_setup_object_marker_file( const char *restrict local_hostdir, const char *restrict object_marker) { char file[PATH_MAX+1]; if (PATH_MAX < pathjoin_r( file, PATH_MAX+1, local_hostdir, "object_marker")) { sk_error("object_marker file name too long"); return SK_RUN_GENERIC_ERROR; } FILE *fh = fopen(file, "w"); if (NULL == fh) { sk_error("could not open object_marker file: %s", strerror(errno)); return SK_RUN_GENERIC_ERROR; } size_t len = strlen(object_marker); if (len > fwrite(object_marker, sizeof(char), len, fh)) { sk_error("failed to store object marker: %s", strerror(errno)); return SK_RUN_GENERIC_ERROR; } (void)fputc('\n', fh); (void)fclose(fh); sk_trace("Object marker %s saved in %s", object_marker, file); return SK_RUN_OK; } sk_run_error sk_run_exec_global_explorers( const char *target_host, bool dry_run, const char *local_hostdir, struct sk_config *config) { char explorers_dir[PATH_MAX+1]; if (PATH_MAX < pathjoin_r( explorers_dir, PATH_MAX+1, local_hostdir, "explorer")) { sk_error("global explorers path is longer than PATH_MAX"); return SK_RUN_GENERIC_ERROR; } sk_debug("Running global explorers"); /* transfer global exploers to target */ /* TODO */ /* execute global explorers */ /* TODO: implement multiple jobs */ /* TODO */ return SK_RUN_OK; } sk_run_error sk_run_exec_init_manifest( const char *target_host, bool dry_run, const char *local_hostdir, struct sk_config *config) { /* check if the initial manifest file exists */ const char *init_manifest = config->global.init_manifest; if (!exists(init_manifest)) { sk_error("cannot find initial manifest (%s): %s", init_manifest, strerror(errno)); return SK_RUN_GENERIC_ERROR; } sk_error("%s: running manifest not yet implemented.", target_host); return SK_RUN_GENERIC_ERROR; } sk_run_error sk_run_cleanup(/* ??? */) { sk_debug("Running cleanup commands"); /* TODO */ return SK_RUN_OK; } sk_run_error sk_run_host( const char *target_host, bool dry_run, const char *local_hostdir, struct sk_config *config) { sk_run_error rv = SK_RUN_OK; struct timespec start, end; const char *run_type = (!dry_run ? "configuration" : "dry"); (void)clock_gettime(CLOCK_MONOTONIC, &start); sk_info("%s: Starting %s run", target_host, run_type); /* create the local hostdir */ rv = sk_run_prepare_local_hostdir(local_hostdir); if (SK_RUN_OK != rv) return rv; if (setenv("__global", local_hostdir, 1)) { rv = SK_RUN_GENERIC_ERROR; goto ret; } /* create conf path and link conf dirs */ rv = sk_run_link_local_hostdir(local_hostdir, config); if (SK_RUN_OK != rv) goto ret; /* link types for type runner */ rv = sk_run_install_type_cmds(local_hostdir); if (SK_RUN_OK != rv) goto ret; /* generate and store object marker file */ char object_marker[sizeof(object_marker_tmpl)/sizeof(char)]; #if HAVE_MKTEMP strcpy(object_marker, object_marker_tmpl); mktemp(object_marker); if (!*object_marker) { sk_error("generating object marker failed: %s", strerror(errno)); rv = SK_RUN_GENERIC_ERROR; goto ret; } #else #error no available implementation to generate object marker #endif rv = sk_run_setup_object_marker_file(local_hostdir, object_marker); if (SK_RUN_OK != rv) goto ret; /* create remote files directories */ rv = sk_run_prepare_remote_workdir(target_host, dry_run, config); if (SK_RUN_OK != rv) goto ret; /* execute global explorers */ rv = sk_run_exec_global_explorers( target_host, dry_run, local_hostdir, config); if (SK_RUN_OK != rv) goto ret; /* execute initial manifest */ rv = sk_run_exec_init_manifest( target_host, dry_run, local_hostdir, config); if (SK_RUN_OK != rv) goto ret; /* iterate */ /* TODO */ /* clean up */ rv = sk_run_cleanup(); if (SK_RUN_OK != rv) goto ret; ret: if (SK_RUN_OK == rv) { (void)clock_gettime(CLOCK_MONOTONIC, &end); sk_info("%s: Finished %s run in %.2f seconds", target_host, run_type, tv_diff(&start, &end)); } return rv; } sk_run_error sk_run_start( const char *target_host, bool dry_run, struct sk_config *config) { sk_run_error rv = SK_RUN_OK; char local_workdir[PATH_MAX+1] = ""; char local_hostdir[PATH_MAX+1] = ""; if (1 < config->global.jobs) { sk_warn("using multiple jobs is currently not implemented."); } if (NULL == target_host) { sk_error("target host missing"); return (rv = SK_RUN_GENERIC_ERROR); } /* determine local workdir */ if ((NULL == sk_run_gen_workdir_path(config, local_workdir))) { return SK_RUN_GENERIC_ERROR; } sk_debug("%s: local work directory: %s", target_host, local_workdir); if (PATH_MAX < pathjoin_r( local_hostdir, PATH_MAX+1, local_workdir, target_host)) { sk_error("%s: local host directory name is longer than PATH_MAX", target_host); return SK_RUN_GENERIC_ERROR; } rv = sk_run_host(target_host, dry_run, local_hostdir, config); if (SK_RUN_OK != rv) { sk_error("failed to configure the following host: %s", target_host); } else { /* cleanup local workdir */ if (*local_workdir && -1 == rmtree(local_workdir)) { sk_error("failed to clean up workdir (%s): %s", local_workdir, strerror(errno)); } } return rv; }