#include "gunicode.h"
#include "virt.h"
#include "def.h"
+#include "missing.h"
int saved_argc = 0;
char **saved_argv = NULL;
};
static const struct table iec[] = {
- { "B", 1 },
- { "K", 1024ULL },
- { "M", 1024ULL*1024ULL },
- { "G", 1024ULL*1024ULL*1024ULL },
- { "T", 1024ULL*1024ULL*1024ULL*1024ULL },
- { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
{ "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
+ { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
+ { "T", 1024ULL*1024ULL*1024ULL*1024ULL },
+ { "G", 1024ULL*1024ULL*1024ULL },
+ { "M", 1024ULL*1024ULL },
+ { "K", 1024ULL },
+ { "B", 1 },
{ "", 1 },
};
static const struct table si[] = {
- { "B", 1 },
- { "K", 1000ULL },
- { "M", 1000ULL*1000ULL },
- { "G", 1000ULL*1000ULL*1000ULL },
- { "T", 1000ULL*1000ULL*1000ULL*1000ULL },
- { "P", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL },
{ "E", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL*1000ULL },
+ { "P", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL },
+ { "T", 1000ULL*1000ULL*1000ULL*1000ULL },
+ { "G", 1000ULL*1000ULL*1000ULL },
+ { "M", 1000ULL*1000ULL },
+ { "K", 1000ULL },
+ { "B", 1 },
{ "", 1 },
};
const struct table *table;
const char *p;
unsigned long long r = 0;
- unsigned n_entries;
+ unsigned n_entries, start_pos = 0;
assert(t);
assert(base == 1000 || base == 1024);
e += strspn(e, WHITESPACE);
- for (i = 0; i < n_entries; i++)
+ for (i = start_pos; i < n_entries; i++)
if (startswith(e, table[i].suffix)) {
unsigned long long tmp;
if ((unsigned long long) l + (frac > 0) > ULLONG_MAX / table[i].factor)
return -ERANGE;
p = e + strlen(table[i].suffix);
+
+ start_pos = i + 1;
break;
}
return endswith(de->d_name, suffix);
}
-void execute_directory(const char *directory, DIR *d, char *argv[]) {
- DIR *_d = NULL;
- struct dirent *de;
- Hashmap *pids = NULL;
+void execute_directory(const char *directory, DIR *d, usec_t timeout, char *argv[]) {
+ pid_t executor_pid;
+ int r;
assert(directory);
- /* Executes all binaries in a directory in parallel and
- * waits for them to finish. */
+ /* Executes all binaries in a directory in parallel and waits
+ * for them to finish. Optionally a timeout is applied. */
- if (!d) {
- if (!(_d = opendir(directory))) {
+ executor_pid = fork();
+ if (executor_pid < 0) {
+ log_error("Failed to fork: %m");
+ return;
- if (errno == ENOENT)
- return;
+ } else if (executor_pid == 0) {
+ _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
+ _cleanup_closedir_ DIR *_d = NULL;
+ struct dirent *de;
+ sigset_t ss;
- log_error("Failed to enumerate directory %s: %m", directory);
- return;
- }
+ /* We fork this all off from a child process so that
+ * we can somewhat cleanly make use of SIGALRM to set
+ * a time limit */
- d = _d;
- }
+ reset_all_signal_handlers();
- if (!(pids = hashmap_new(trivial_hash_func, trivial_compare_func))) {
- log_error("Failed to allocate set.");
- goto finish;
- }
+ assert_se(sigemptyset(&ss) == 0);
+ assert_se(sigprocmask(SIG_SETMASK, &ss, NULL) == 0);
- while ((de = readdir(d))) {
- char *path;
- pid_t pid;
- int k;
+ assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
- if (!dirent_is_file(de))
- continue;
+ if (!d) {
+ d = _d = opendir(directory);
+ if (!d) {
+ if (errno == ENOENT)
+ _exit(EXIT_SUCCESS);
- if (asprintf(&path, "%s/%s", directory, de->d_name) < 0) {
- log_oom();
- continue;
+ log_error("Failed to enumerate directory %s: %m", directory);
+ _exit(EXIT_FAILURE);
+ }
}
- if ((pid = fork()) < 0) {
- log_error("Failed to fork: %m");
- free(path);
- continue;
+ pids = hashmap_new(NULL, NULL);
+ if (!pids) {
+ log_oom();
+ _exit(EXIT_FAILURE);
}
- if (pid == 0) {
- char *_argv[2];
- /* Child */
+ FOREACH_DIRENT(de, d, break) {
+ _cleanup_free_ char *path = NULL;
+ pid_t pid;
- if (!argv) {
- _argv[0] = path;
- _argv[1] = NULL;
- argv = _argv;
- } else
- argv[0] = path;
+ if (!dirent_is_file(de))
+ continue;
- execv(path, argv);
+ if (asprintf(&path, "%s/%s", directory, de->d_name) < 0) {
+ log_oom();
+ _exit(EXIT_FAILURE);
+ }
- log_error("Failed to execute %s: %m", path);
- _exit(EXIT_FAILURE);
- }
+ pid = fork();
+ if (pid < 0) {
+ log_error("Failed to fork: %m");
+ continue;
+ } else if (pid == 0) {
+ char *_argv[2];
- log_debug("Spawned %s as %lu", path, (unsigned long) pid);
+ assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
- if ((k = hashmap_put(pids, UINT_TO_PTR(pid), path)) < 0) {
- log_error("Failed to add PID to set: %s", strerror(-k));
- free(path);
- }
- }
+ if (!argv) {
+ _argv[0] = path;
+ _argv[1] = NULL;
+ argv = _argv;
+ } else
+ argv[0] = path;
- while (!hashmap_isempty(pids)) {
- pid_t pid = PTR_TO_UINT(hashmap_first_key(pids));
- siginfo_t si = {};
- char *path;
+ execv(path, argv);
+ log_error("Failed to execute %s: %m", path);
+ _exit(EXIT_FAILURE);
+ }
- if (waitid(P_PID, pid, &si, WEXITED) < 0) {
- if (errno == EINTR)
- continue;
+ log_debug("Spawned %s as " PID_FMT ".", path, pid);
- log_error("waitid() failed: %m");
- goto finish;
+ r = hashmap_put(pids, UINT_TO_PTR(pid), path);
+ if (r < 0) {
+ log_oom();
+ _exit(EXIT_FAILURE);
+ }
+
+ path = NULL;
}
- if ((path = hashmap_remove(pids, UINT_TO_PTR(si.si_pid)))) {
- if (!is_clean_exit(si.si_code, si.si_status, NULL)) {
- if (si.si_code == CLD_EXITED)
- log_error("%s exited with exit status %i.", path, si.si_status);
- else
- log_error("%s terminated by signal %s.", path, signal_to_string(si.si_status));
- } else
- log_debug("%s exited successfully.", path);
+ /* Abort execution of this process after the
+ * timout. We simply rely on SIGALRM as default action
+ * terminating the process, and turn on alarm(). */
+
+ if (timeout != (usec_t) -1)
+ alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC);
+
+ while (!hashmap_isempty(pids)) {
+ _cleanup_free_ char *path = NULL;
+ pid_t pid;
+
+ pid = PTR_TO_UINT(hashmap_first_key(pids));
+ assert(pid > 0);
+
+ path = hashmap_remove(pids, UINT_TO_PTR(pid));
+ assert(path);
- free(path);
+ wait_for_terminate_and_warn(path, pid);
}
- }
-finish:
- if (_d)
- closedir(_d);
+ _exit(EXIT_SUCCESS);
+ }
- if (pids)
- hashmap_free_free(pids);
+ wait_for_terminate_and_warn(directory, executor_pid);
}
int kill_and_sigcont(pid_t pid, int sig) {
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(sched_policy, int, INT_MAX);
-static const char* const rlimit_table[] = {
+static const char* const rlimit_table[_RLIMIT_MAX] = {
[RLIMIT_CPU] = "LimitCPU",
[RLIMIT_FSIZE] = "LimitFSIZE",
[RLIMIT_DATA] = "LimitDATA",
return NULL;
}
+
+uint64_t physical_memory(void) {
+ long mem;
+
+ /* We return this as uint64_t in case we are running as 32bit
+ * process on a 64bit kernel with huge amounts of memory */
+
+ mem = sysconf(_SC_PHYS_PAGES);
+ assert(mem > 0);
+
+ return (uint64_t) mem * (uint64_t) page_size();
+}