static int read_usec(sd_id128_t vendor, const char *name, usec_t *u) { _cleanup_free_ void *i = NULL; _cleanup_free_ char *j = NULL; size_t is; int r; uint64_t x; assert(name); assert(u); r = efi_get_variable(EFI_VENDOR_LOADER, name, NULL, &i, &is); if (r < 0) return r; j = utf16_to_utf8(i, is); if (!j) return -ENOMEM; r = safe_atou64(j, &x); if (r < 0) return r; *u = x; return 0; }
int config_parse_uint64( const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { uint64_t *u = data; int r; assert(filename); assert(lvalue); assert(rvalue); assert(data); r = safe_atou64(rvalue, u); if (r < 0) { log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue); return 0; } return 0; }
static void test_safe_atou64(void) { int r; uint64_t l; r = safe_atou64("12345", &l); assert_se(r == 0); assert_se(l == 12345); r = safe_atou64(" 12345", &l); assert_se(r == 0); assert_se(l == 12345); r = safe_atou64("18446744073709551617", &l); assert_se(r == -ERANGE); r = safe_atou64("-1", &l); assert_se(r == -ERANGE); r = safe_atou64(" -1", &l); assert_se(r == -ERANGE); r = safe_atou64("junk", &l); assert_se(r == -EINVAL); r = safe_atou64("123x", &l); assert_se(r == -EINVAL); }
int block_bump_request_nr(const char *p) { struct stat st; uint64_t u; char *ap = NULL, *line = NULL; int r; dev_t d; assert(p); if (stat(p, &st) < 0) return -errno; if (major(st.st_dev) == 0) return 0; d = st.st_dev; block_get_whole_disk(d, &d); if (asprintf(&ap, "/sys/dev/block/%u:%u/queue/nr_requests", major(d), minor(d)) < 0) { r= -ENOMEM; goto finish; } r = read_one_line_file(ap, &line); if (r < 0) { if (r == -ENOENT) r = 0; goto finish; } r = safe_atou64(line, &u); if (r >= 0 && u >= BUMP_REQUEST_NR) { r = 0; goto finish; } free(line); line = NULL; if (asprintf(&line, "%lu", (unsigned long) BUMP_REQUEST_NR) < 0) { r = -ENOMEM; goto finish; } r = write_string_file(ap, line); if (r < 0) goto finish; log_info("Bumped block_nr parameter of %u:%u to %lu. This is a temporary hack and should be removed one day.", major(d), minor(d), (unsigned long) BUMP_REQUEST_NR); r = 1; finish: free(ap); free(line); return r; }
int timestamp_deserialize(const char *value, usec_t *timestamp) { int r; assert(value); r = safe_atou64(value, timestamp); if (r < 0) return log_debug_errno(r, "Failed to parse timestamp value \"%s\": %m", value); return r; }
static int output_timestamp_monotonic(FILE *f, sd_journal *j, const char *monotonic) { sd_id128_t boot_id; uint64_t t; int r; assert(f); assert(j); r = -ENXIO; if (monotonic) r = safe_atou64(monotonic, &t); if (r < 0) r = sd_journal_get_monotonic_usec(j, &t, &boot_id); if (r < 0) return log_error_errno(r, "Failed to get monotonic timestamp: %m"); fprintf(f, "[%5"PRI_USEC".%06"PRI_USEC"]", t / USEC_PER_SEC, t % USEC_PER_SEC); return 1 + 5 + 1 + 6 + 1; }
int block_get_readahead(const char *p, uint64_t *bytes) { struct stat st; char *ap = NULL, *line = NULL; int r; dev_t d; uint64_t u; assert(p); assert(bytes); if (stat(p, &st) < 0) return -errno; if (major(st.st_dev) == 0) return 0; d = st.st_dev; block_get_whole_disk(d, &d); if (asprintf(&ap, "/sys/dev/block/%u:%u/bdi/read_ahead_kb", major(d), minor(d)) < 0) { r = -ENOMEM; goto finish; } r = read_one_line_file(ap, &line); if (r < 0) goto finish; r = safe_atou64(line, &u); if (r < 0) goto finish; *bytes = u * 1024ULL; finish: free(ap); free(line); return r; }
static int output_timestamp_realtime(FILE *f, sd_journal *j, OutputMode mode, OutputFlags flags, const char *realtime) { char buf[MAX(FORMAT_TIMESTAMP_MAX, 64)]; struct tm *(*gettime_r)(const time_t *, struct tm *); struct tm tm; uint64_t x; time_t t; int r; assert(f); assert(j); if (realtime) r = safe_atou64(realtime, &x); if (!realtime || r < 0 || !VALID_REALTIME(x)) r = sd_journal_get_realtime_usec(j, &x); if (r < 0) return log_error_errno(r, "Failed to get realtime timestamp: %m"); if (IN_SET(mode, OUTPUT_SHORT_FULL, OUTPUT_WITH_UNIT)) { const char *k; if (flags & OUTPUT_UTC) k = format_timestamp_utc(buf, sizeof(buf), x); else k = format_timestamp(buf, sizeof(buf), x); if (!k) { log_error("Failed to format timestamp: %"PRIu64, x); return -EINVAL; } } else { char usec[7]; gettime_r = (flags & OUTPUT_UTC) ? gmtime_r : localtime_r; t = (time_t) (x / USEC_PER_SEC); switch (mode) { case OUTPUT_SHORT_UNIX: xsprintf(buf, "%10"PRI_TIME".%06"PRIu64, t, x % USEC_PER_SEC); break; case OUTPUT_SHORT_ISO: if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", gettime_r(&t, &tm)) <= 0) { log_error("Failed to format ISO time"); return -EINVAL; } break; case OUTPUT_SHORT_ISO_PRECISE: /* No usec in strftime, so we leave space and copy over */ if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S.xxxxxx%z", gettime_r(&t, &tm)) <= 0) { log_error("Failed to format ISO-precise time"); return -EINVAL; } xsprintf(usec, "%06"PRI_USEC, x % USEC_PER_SEC); memcpy(buf + 20, usec, 6); break; case OUTPUT_SHORT: case OUTPUT_SHORT_PRECISE: if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm)) <= 0) { log_error("Failed to format syslog time"); return -EINVAL; } if (mode == OUTPUT_SHORT_PRECISE) { size_t k; assert(sizeof(buf) > strlen(buf)); k = sizeof(buf) - strlen(buf); r = snprintf(buf + strlen(buf), k, ".%06"PRIu64, x % USEC_PER_SEC); if (r <= 0 || (size_t) r >= k) { /* too long? */ log_error("Failed to format precise time"); return -EINVAL; } } break; default: assert_not_reached("Unknown time format"); } } fputs(buf, f); return (int) strlen(buf); }
static int process( const char *controller, const char *path, Hashmap *a, Hashmap *b, unsigned iteration, Group **ret) { Group *g; int r; assert(controller); assert(path); assert(a); g = hashmap_get(a, path); if (!g) { g = hashmap_get(b, path); if (!g) { g = new0(Group, 1); if (!g) return -ENOMEM; g->path = strdup(path); if (!g->path) { group_free(g); return -ENOMEM; } r = hashmap_put(a, g->path, g); if (r < 0) { group_free(g); return r; } } else { r = hashmap_move_one(a, b, path); if (r < 0) return r; g->cpu_valid = g->memory_valid = g->io_valid = g->n_tasks_valid = false; } } if (streq(controller, SYSTEMD_CGROUP_CONTROLLER) && IN_SET(arg_count, COUNT_ALL_PROCESSES, COUNT_USERSPACE_PROCESSES)) { _cleanup_fclose_ FILE *f = NULL; pid_t pid; r = cg_enumerate_processes(controller, path, &f); if (r == -ENOENT) return 0; if (r < 0) return r; g->n_tasks = 0; while (cg_read_pid(f, &pid) > 0) { if (arg_count == COUNT_USERSPACE_PROCESSES && is_kernel_thread(pid) > 0) continue; g->n_tasks++; } if (g->n_tasks > 0) g->n_tasks_valid = true; } else if (streq(controller, "pids") && arg_count == COUNT_PIDS) { _cleanup_free_ char *p = NULL, *v = NULL; r = cg_get_path(controller, path, "pids.current", &p); if (r < 0) return r; r = read_one_line_file(p, &v); if (r == -ENOENT) return 0; if (r < 0) return r; r = safe_atou64(v, &g->n_tasks); if (r < 0) return r; if (g->n_tasks > 0) g->n_tasks_valid = true; } else if (streq(controller, "cpu") || streq(controller, "cpuacct")) { _cleanup_free_ char *p = NULL, *v = NULL; uint64_t new_usage; nsec_t timestamp; if (cg_all_unified() > 0) { const char *keys[] = { "usage_usec", NULL }; _cleanup_free_ char *val = NULL; if (!streq(controller, "cpu")) return 0; r = cg_get_keyed_attribute("cpu", path, "cpu.stat", keys, &val); if (r == -ENOENT) return 0; if (r < 0) return r; r = safe_atou64(val, &new_usage); if (r < 0) return r; new_usage *= NSEC_PER_USEC; } else { if (!streq(controller, "cpuacct")) return 0; r = cg_get_path(controller, path, "cpuacct.usage", &p); if (r < 0) return r; r = read_one_line_file(p, &v); if (r == -ENOENT) return 0; if (r < 0) return r; r = safe_atou64(v, &new_usage); if (r < 0) return r; } timestamp = now_nsec(CLOCK_MONOTONIC); if (g->cpu_iteration == iteration - 1 && (nsec_t) new_usage > g->cpu_usage) { nsec_t x, y; x = timestamp - g->cpu_timestamp; if (x < 1) x = 1; y = (nsec_t) new_usage - g->cpu_usage; g->cpu_fraction = (double) y / (double) x; g->cpu_valid = true; } g->cpu_usage = (nsec_t) new_usage; g->cpu_timestamp = timestamp; g->cpu_iteration = iteration; } else if (streq(controller, "memory")) { _cleanup_free_ char *p = NULL, *v = NULL; if (cg_all_unified() <= 0) r = cg_get_path(controller, path, "memory.usage_in_bytes", &p); else r = cg_get_path(controller, path, "memory.current", &p); if (r < 0) return r; r = read_one_line_file(p, &v); if (r == -ENOENT) return 0; if (r < 0) return r; r = safe_atou64(v, &g->memory); if (r < 0) return r; if (g->memory > 0) g->memory_valid = true; } else if ((streq(controller, "io") && cg_all_unified() > 0) || (streq(controller, "blkio") && cg_all_unified() <= 0)) { _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *p = NULL; bool unified = cg_all_unified() > 0; uint64_t wr = 0, rd = 0; nsec_t timestamp; r = cg_get_path(controller, path, unified ? "io.stat" : "blkio.io_service_bytes", &p); if (r < 0) return r; f = fopen(p, "re"); if (!f) { if (errno == ENOENT) return 0; return -errno; } for (;;) { char line[LINE_MAX], *l; uint64_t k, *q; if (!fgets(line, sizeof(line), f)) break; /* Trim and skip the device */ l = strstrip(line); l += strcspn(l, WHITESPACE); l += strspn(l, WHITESPACE); if (unified) { while (!isempty(l)) { if (sscanf(l, "rbytes=%" SCNu64, &k)) rd += k; else if (sscanf(l, "wbytes=%" SCNu64, &k)) wr += k; l += strcspn(l, WHITESPACE); l += strspn(l, WHITESPACE); } } else { if (first_word(l, "Read")) { l += 4; q = &rd; } else if (first_word(l, "Write")) { l += 5; q = ≀ } else continue; l += strspn(l, WHITESPACE); r = safe_atou64(l, &k); if (r < 0) continue; *q += k; } } timestamp = now_nsec(CLOCK_MONOTONIC); if (g->io_iteration == iteration - 1) { uint64_t x, yr, yw; x = (uint64_t) (timestamp - g->io_timestamp); if (x < 1) x = 1; if (rd > g->io_input) yr = rd - g->io_input; else yr = 0; if (wr > g->io_output) yw = wr - g->io_output; else yw = 0; if (yr > 0 || yw > 0) { g->io_input_bps = (yr * 1000000000ULL) / x; g->io_output_bps = (yw * 1000000000ULL) / x; g->io_valid = true; } } g->io_input = rd; g->io_output = wr; g->io_timestamp = timestamp; g->io_iteration = iteration; } if (ret) *ret = g; return 0; }
int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) { const char *eq, *field; int r; assert(m); assert(assignment); eq = strchr(assignment, '='); if (!eq) { log_error("Not an assignment: %s", assignment); return -EINVAL; } field = strndupa(assignment, eq - assignment); eq ++; if (streq(field, "CPUQuota")) { if (isempty(eq)) { r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "CPUQuotaPerSecUSec"); if (r < 0) return bus_log_create_error(r); r = sd_bus_message_append(m, "v", "t", USEC_INFINITY); } else if (endswith(eq, "%")) { double percent; if (sscanf(eq, "%lf%%", &percent) != 1 || percent <= 0) { log_error("CPU quota '%s' invalid.", eq); return -EINVAL; } r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "CPUQuotaPerSecUSec"); if (r < 0) return bus_log_create_error(r); r = sd_bus_message_append(m, "v", "t", (usec_t) percent * USEC_PER_SEC / 100); } else { log_error("CPU quota needs to be in percent."); return -EINVAL; } if (r < 0) return bus_log_create_error(r); return 0; } r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); if (r < 0) return bus_log_create_error(r); if (STR_IN_SET(field, "CPUAccounting", "MemoryAccounting", "BlockIOAccounting", "SendSIGHUP", "SendSIGKILL")) { r = parse_boolean(eq); if (r < 0) { log_error("Failed to parse boolean assignment %s.", assignment); return -EINVAL; } r = sd_bus_message_append(m, "v", "b", r); } else if (streq(field, "MemoryLimit")) { off_t bytes; r = parse_size(eq, 1024, &bytes); if (r < 0) { log_error("Failed to parse bytes specification %s", assignment); return -EINVAL; } r = sd_bus_message_append(m, "v", "t", (uint64_t) bytes); } else if (STR_IN_SET(field, "CPUShares", "BlockIOWeight")) { uint64_t u; r = safe_atou64(eq, &u); if (r < 0) { log_error("Failed to parse %s value %s.", field, eq); return -EINVAL; } r = sd_bus_message_append(m, "v", "t", u); } else if (STR_IN_SET(field, "User", "Group", "DevicePolicy", "KillMode")) r = sd_bus_message_append(m, "v", "s", eq); else if (streq(field, "DeviceAllow")) { if (isempty(eq)) r = sd_bus_message_append(m, "v", "a(ss)", 0); else { const char *path, *rwm, *e; e = strchr(eq, ' '); if (e) { path = strndupa(eq, e - eq); rwm = e+1; } else { path = eq; rwm = ""; } if (!path_startswith(path, "/dev")) { log_error("%s is not a device file in /dev.", path); return -EINVAL; } r = sd_bus_message_append(m, "v", "a(ss)", 1, path, rwm); } } else if (STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) { if (isempty(eq)) r = sd_bus_message_append(m, "v", "a(st)", 0); else { const char *path, *bandwidth, *e; off_t bytes; e = strchr(eq, ' '); if (e) { path = strndupa(eq, e - eq); bandwidth = e+1; } else { log_error("Failed to parse %s value %s.", field, eq); return -EINVAL; } if (!path_startswith(path, "/dev")) { log_error("%s is not a device file in /dev.", path); return -EINVAL; } r = parse_size(bandwidth, 1000, &bytes); if (r < 0) { log_error("Failed to parse byte value %s.", bandwidth); return -EINVAL; } r = sd_bus_message_append(m, "v", "a(st)", 1, path, (uint64_t) bytes); } } else if (streq(field, "BlockIODeviceWeight")) { if (isempty(eq)) r = sd_bus_message_append(m, "v", "a(st)", 0); else { const char *path, *weight, *e; uint64_t u; e = strchr(eq, ' '); if (e) { path = strndupa(eq, e - eq); weight = e+1; } else { log_error("Failed to parse %s value %s.", field, eq); return -EINVAL; } if (!path_startswith(path, "/dev")) { log_error("%s is not a device file in /dev.", path); return -EINVAL; } r = safe_atou64(weight, &u); if (r < 0) { log_error("Failed to parse %s value %s.", field, weight); return -EINVAL; } r = sd_bus_message_append(m, "v", "a(st)", path, u); } } else if (rlimit_from_string(field) >= 0) { uint64_t rl; if (streq(eq, "infinity")) rl = (uint64_t) -1; else { r = safe_atou64(eq, &rl); if (r < 0) { log_error("Invalid resource limit: %s", eq); return -EINVAL; } } r = sd_bus_message_append(m, "v", "t", rl); } else if (streq(field, "Nice")) { int32_t i; r = safe_atoi32(eq, &i); if (r < 0) { log_error("Failed to parse %s value %s.", field, eq); return -EINVAL; } r = sd_bus_message_append(m, "v", "i", i); } else if (streq(field, "Environment")) { r = sd_bus_message_append(m, "v", "as", 1, eq); } else if (streq(field, "KillSignal")) { int sig; sig = signal_from_string_try_harder(eq); if (sig < 0) { log_error("Failed to parse %s value %s.", field, eq); return -EINVAL; } r = sd_bus_message_append(m, "v", "i", sig); } else { log_error("Unknown assignment %s.", assignment); return -EINVAL; } if (r < 0) return bus_log_create_error(r); return 0; }
static int process(const char *controller, const char *path, Hashmap *a, Hashmap *b, unsigned iteration) { Group *g; int r; FILE *f = NULL; pid_t pid; unsigned n; assert(controller); assert(path); assert(a); g = hashmap_get(a, path); if (!g) { g = hashmap_get(b, path); if (!g) { g = new0(Group, 1); if (!g) return -ENOMEM; g->path = strdup(path); if (!g->path) { group_free(g); return -ENOMEM; } r = hashmap_put(a, g->path, g); if (r < 0) { group_free(g); return r; } } else { assert_se(hashmap_move_one(a, b, path) == 0); g->cpu_valid = g->memory_valid = g->io_valid = g->n_tasks_valid = false; } } /* Regardless which controller, let's find the maximum number * of processes in any of it */ r = cg_enumerate_processes(controller, path, &f); if (r < 0) return r; n = 0; while (cg_read_pid(f, &pid) > 0) n++; fclose(f); if (n > 0) { if (g->n_tasks_valid) g->n_tasks = MAX(g->n_tasks, n); else g->n_tasks = n; g->n_tasks_valid = true; } if (streq(controller, "cpuacct")) { uint64_t new_usage; char *p, *v; struct timespec ts; r = cg_get_path(controller, path, "cpuacct.usage", &p); if (r < 0) return r; r = read_one_line_file(p, &v); free(p); if (r < 0) return r; r = safe_atou64(v, &new_usage); free(v); if (r < 0) return r; assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); if (g->cpu_iteration == iteration - 1) { uint64_t x, y; x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) - ((uint64_t) g->cpu_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->cpu_timestamp.tv_nsec); y = new_usage - g->cpu_usage; if (y > 0) { g->cpu_fraction = (double) y / (double) x; g->cpu_valid = true; } } g->cpu_usage = new_usage; g->cpu_timestamp = ts; g->cpu_iteration = iteration; } else if (streq(controller, "memory")) { char *p, *v; r = cg_get_path(controller, path, "memory.usage_in_bytes", &p); if (r < 0) return r; r = read_one_line_file(p, &v); free(p); if (r < 0) return r; r = safe_atou64(v, &g->memory); free(v); if (r < 0) return r; if (g->memory > 0) g->memory_valid = true; } else if (streq(controller, "blkio")) { char *p; uint64_t wr = 0, rd = 0; struct timespec ts; r = cg_get_path(controller, path, "blkio.io_service_bytes", &p); if (r < 0) return r; f = fopen(p, "re"); free(p); if (!f) return -errno; for (;;) { char line[LINE_MAX], *l; uint64_t k, *q; if (!fgets(line, sizeof(line), f)) break; l = strstrip(line); l += strcspn(l, WHITESPACE); l += strspn(l, WHITESPACE); if (first_word(l, "Read")) { l += 4; q = &rd; } else if (first_word(l, "Write")) { l += 5; q = ≀ } else continue; l += strspn(l, WHITESPACE); r = safe_atou64(l, &k); if (r < 0) continue; *q += k; } fclose(f); assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); if (g->io_iteration == iteration - 1) { uint64_t x, yr, yw; x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) - ((uint64_t) g->io_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->io_timestamp.tv_nsec); yr = rd - g->io_input; yw = wr - g->io_output; if (yr > 0 || yw > 0) { g->io_input_bps = (yr * 1000000000ULL) / x; g->io_output_bps = (yw * 1000000000ULL) / x; g->io_valid = true; } } g->io_input = rd; g->io_output = wr; g->io_timestamp = ts; g->io_iteration = iteration; } return 0; }
static int save_external_coredump( const char *context[_CONTEXT_MAX], int input_fd, char **ret_filename, int *ret_node_fd, int *ret_data_fd, uint64_t *ret_size) { _cleanup_free_ char *fn = NULL, *tmp = NULL; _cleanup_close_ int fd = -1; uint64_t rlimit, max_size; struct stat st; uid_t uid; int r; assert(context); assert(ret_filename); assert(ret_node_fd); assert(ret_data_fd); assert(ret_size); r = parse_uid(context[CONTEXT_UID], &uid); if (r < 0) return log_error_errno(r, "Failed to parse UID: %m"); r = safe_atou64(context[CONTEXT_RLIMIT], &rlimit); if (r < 0) return log_error_errno(r, "Failed to parse resource limit: %s", context[CONTEXT_RLIMIT]); if (rlimit <= 0) { /* Is coredumping disabled? Then don't bother saving/processing the coredump */ log_info("Core Dumping has been disabled for process %s (%s).", context[CONTEXT_PID], context[CONTEXT_COMM]); return -EBADSLT; } /* Never store more than the process configured, or than we actually shall keep or process */ max_size = MIN(rlimit, MAX(arg_process_size_max, arg_external_size_max)); r = make_filename(context, &fn); if (r < 0) return log_error_errno(r, "Failed to determine coredump file name: %m"); mkdir_p_label("/var/lib/systemd/coredump", 0755); fd = open_tmpfile_linkable(fn, O_RDWR|O_CLOEXEC, &tmp); if (fd < 0) return log_error_errno(fd, "Failed to create temporary file for coredump %s: %m", fn); r = copy_bytes(input_fd, fd, max_size, false); if (r == -EFBIG) { log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.", context[CONTEXT_PID], context[CONTEXT_COMM]); goto fail; } else if (IN_SET(r, -EDQUOT, -ENOSPC)) { log_error("Not enough disk space for coredump of %s (%s), refusing.", context[CONTEXT_PID], context[CONTEXT_COMM]); goto fail; } else if (r < 0) { log_error_errno(r, "Failed to dump coredump to file: %m"); goto fail; } if (fstat(fd, &st) < 0) { log_error_errno(errno, "Failed to fstat coredump %s: %m", coredump_tmpfile_name(tmp)); goto fail; } if (lseek(fd, 0, SEEK_SET) == (off_t) -1) { log_error_errno(errno, "Failed to seek on %s: %m", coredump_tmpfile_name(tmp)); goto fail; } #if defined(HAVE_XZ) || defined(HAVE_LZ4) /* If we will remove the coredump anyway, do not compress. */ if (maybe_remove_external_coredump(NULL, st.st_size) == 0 && arg_compress) { _cleanup_free_ char *fn_compressed = NULL, *tmp_compressed = NULL; _cleanup_close_ int fd_compressed = -1; fn_compressed = strappend(fn, COMPRESSED_EXT); if (!fn_compressed) { log_oom(); goto uncompressed; } fd_compressed = open_tmpfile_linkable(fn_compressed, O_RDWR|O_CLOEXEC, &tmp_compressed); if (fd_compressed < 0) { log_error_errno(fd_compressed, "Failed to create temporary file for coredump %s: %m", fn_compressed); goto uncompressed; } r = compress_stream(fd, fd_compressed, -1); if (r < 0) { log_error_errno(r, "Failed to compress %s: %m", coredump_tmpfile_name(tmp_compressed)); goto fail_compressed; } r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, context, uid); if (r < 0) goto fail_compressed; /* OK, this worked, we can get rid of the uncompressed version now */ if (tmp) unlink_noerrno(tmp); *ret_filename = fn_compressed; /* compressed */ *ret_node_fd = fd_compressed; /* compressed */ *ret_data_fd = fd; /* uncompressed */ *ret_size = (uint64_t) st.st_size; /* uncompressed */ fn_compressed = NULL; fd = fd_compressed = -1; return 0; fail_compressed: if (tmp_compressed) (void) unlink(tmp_compressed); } uncompressed: #endif r = fix_permissions(fd, tmp, fn, context, uid); if (r < 0) goto fail; *ret_filename = fn; *ret_data_fd = fd; *ret_node_fd = -1; *ret_size = (uint64_t) st.st_size; fn = NULL; fd = -1; return 0; fail: if (tmp) (void) unlink(tmp); return r; }
static int save_external_coredump( const char *context[_CONTEXT_MAX], int input_fd, char **ret_filename, int *ret_node_fd, int *ret_data_fd, uint64_t *ret_size) { _cleanup_free_ char *fn = NULL, *tmp = NULL; _cleanup_close_ int fd = -1; uint64_t rlimit, max_size; struct stat st; uid_t uid; int r; assert(context); assert(ret_filename); assert(ret_node_fd); assert(ret_data_fd); assert(ret_size); r = parse_uid(context[CONTEXT_UID], &uid); if (r < 0) return log_error_errno(r, "Failed to parse UID: %m"); r = safe_atou64(context[CONTEXT_RLIMIT], &rlimit); if (r < 0) return log_error_errno(r, "Failed to parse resource limit: %s", context[CONTEXT_RLIMIT]); if (rlimit < page_size()) { /* Is coredumping disabled? Then don't bother saving/processing the coredump. * Anything below PAGE_SIZE cannot give a readable coredump (the kernel uses * ELF_EXEC_PAGESIZE which is not easily accessible, but is usually the same as PAGE_SIZE. */ log_info("Resource limits disable core dumping for process %s (%s).", context[CONTEXT_PID], context[CONTEXT_COMM]); return -EBADSLT; } /* Never store more than the process configured, or than we actually shall keep or process */ max_size = MIN(rlimit, MAX(arg_process_size_max, storage_size_max())); r = make_filename(context, &fn); if (r < 0) return log_error_errno(r, "Failed to determine coredump file name: %m"); mkdir_p_label("/var/lib/systemd/coredump", 0755); fd = open_tmpfile_linkable(fn, O_RDWR|O_CLOEXEC, &tmp); if (fd < 0) return log_error_errno(fd, "Failed to create temporary file for coredump %s: %m", fn); r = copy_bytes(input_fd, fd, max_size, false); if (r < 0) { log_error_errno(r, "Cannot store coredump of %s (%s): %m", context[CONTEXT_PID], context[CONTEXT_COMM]); goto fail; } else if (r == 1) log_struct(LOG_INFO, LOG_MESSAGE("Core file was truncated to %zu bytes.", max_size), "SIZE_LIMIT=%zu", max_size, LOG_MESSAGE_ID(SD_MESSAGE_TRUNCATED_CORE), NULL); if (fstat(fd, &st) < 0) { log_error_errno(errno, "Failed to fstat core file %s: %m", coredump_tmpfile_name(tmp)); goto fail; } if (lseek(fd, 0, SEEK_SET) == (off_t) -1) { log_error_errno(errno, "Failed to seek on %s: %m", coredump_tmpfile_name(tmp)); goto fail; } #if defined(HAVE_XZ) || defined(HAVE_LZ4) /* If we will remove the coredump anyway, do not compress. */ if (arg_compress && !maybe_remove_external_coredump(NULL, st.st_size)) { _cleanup_free_ char *fn_compressed = NULL, *tmp_compressed = NULL; _cleanup_close_ int fd_compressed = -1; fn_compressed = strappend(fn, COMPRESSED_EXT); if (!fn_compressed) { log_oom(); goto uncompressed; } fd_compressed = open_tmpfile_linkable(fn_compressed, O_RDWR|O_CLOEXEC, &tmp_compressed); if (fd_compressed < 0) { log_error_errno(fd_compressed, "Failed to create temporary file for coredump %s: %m", fn_compressed); goto uncompressed; } r = compress_stream(fd, fd_compressed, -1); if (r < 0) { log_error_errno(r, "Failed to compress %s: %m", coredump_tmpfile_name(tmp_compressed)); goto fail_compressed; } r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, context, uid); if (r < 0) goto fail_compressed; /* OK, this worked, we can get rid of the uncompressed version now */ if (tmp) unlink_noerrno(tmp); *ret_filename = fn_compressed; /* compressed */ *ret_node_fd = fd_compressed; /* compressed */ *ret_data_fd = fd; /* uncompressed */ *ret_size = (uint64_t) st.st_size; /* uncompressed */ fn_compressed = NULL; fd = fd_compressed = -1; return 0; fail_compressed: if (tmp_compressed) (void) unlink(tmp_compressed); } uncompressed: #endif r = fix_permissions(fd, tmp, fn, context, uid); if (r < 0) goto fail; *ret_filename = fn; *ret_data_fd = fd; *ret_node_fd = -1; *ret_size = (uint64_t) st.st_size; fn = NULL; fd = -1; return 0; fail: if (tmp) (void) unlink(tmp); return r; }
static void dev_kmsg_record(Server *s, const char *p, size_t l) { _cleanup_free_ char *message = NULL, *syslog_priority = NULL, *syslog_pid = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *source_time = NULL, *identifier = NULL, *pid = NULL; struct iovec iovec[N_IOVEC_META_FIELDS + 7 + N_IOVEC_KERNEL_FIELDS + 2 + N_IOVEC_UDEV_FIELDS]; char *kernel_device = NULL; unsigned long long usec; size_t n = 0, z = 0, j; int priority, r; char *e, *f, *k; uint64_t serial; size_t pl; assert(s); assert(p); if (l <= 0) return; e = memchr(p, ',', l); if (!e) return; *e = 0; r = safe_atoi(p, &priority); if (r < 0 || priority < 0 || priority > 999) return; if (s->forward_to_kmsg && (priority & LOG_FACMASK) != LOG_KERN) return; l -= (e - p) + 1; p = e + 1; e = memchr(p, ',', l); if (!e) return; *e = 0; r = safe_atou64(p, &serial); if (r < 0) return; if (s->kernel_seqnum) { /* We already read this one? */ if (serial < *s->kernel_seqnum) return; /* Did we lose any? */ if (serial > *s->kernel_seqnum) server_driver_message(s, 0, "MESSAGE_ID=" SD_MESSAGE_JOURNAL_MISSED_STR, LOG_MESSAGE("Missed %"PRIu64" kernel messages", serial - *s->kernel_seqnum), NULL); /* Make sure we never read this one again. Note that * we always store the next message serial we expect * here, simply because this makes handling the first * message with serial 0 easy. */ *s->kernel_seqnum = serial + 1; } l -= (e - p) + 1; p = e + 1; f = memchr(p, ';', l); if (!f) return; /* Kernel 3.6 has the flags field, kernel 3.5 lacks that */ e = memchr(p, ',', l); if (!e || f < e) e = f; *e = 0; r = safe_atollu(p, &usec); if (r < 0) return; l -= (f - p) + 1; p = f + 1; e = memchr(p, '\n', l); if (!e) return; *e = 0; pl = e - p; l -= (e - p) + 1; k = e + 1; for (j = 0; l > 0 && j < N_IOVEC_KERNEL_FIELDS; j++) { char *m; /* Metadata fields attached */ if (*k != ' ') break; k++, l--; e = memchr(k, '\n', l); if (!e) return; *e = 0; if (cunescape_length_with_prefix(k, e - k, "_KERNEL_", UNESCAPE_RELAX, &m) < 0) break; if (startswith(m, "_KERNEL_DEVICE=")) kernel_device = m + 15; iovec[n++] = IOVEC_MAKE_STRING(m); z++; l -= (e - k) + 1; k = e + 1; } if (kernel_device) { struct udev_device *ud; ud = udev_device_new_from_device_id(s->udev, kernel_device); if (ud) { const char *g; struct udev_list_entry *ll; char *b; g = udev_device_get_devnode(ud); if (g) { b = strappend("_UDEV_DEVNODE=", g); if (b) { iovec[n++] = IOVEC_MAKE_STRING(b); z++; } } g = udev_device_get_sysname(ud); if (g) { b = strappend("_UDEV_SYSNAME=", g); if (b) { iovec[n++] = IOVEC_MAKE_STRING(b); z++; } } j = 0; ll = udev_device_get_devlinks_list_entry(ud); udev_list_entry_foreach(ll, ll) { if (j > N_IOVEC_UDEV_FIELDS) break; g = udev_list_entry_get_name(ll); if (g) { b = strappend("_UDEV_DEVLINK=", g); if (b) { iovec[n++] = IOVEC_MAKE_STRING(b); z++; } } j++; } udev_device_unref(ud); } } if (asprintf(&source_time, "_SOURCE_MONOTONIC_TIMESTAMP=%llu", usec) >= 0) iovec[n++] = IOVEC_MAKE_STRING(source_time); iovec[n++] = IOVEC_MAKE_STRING("_TRANSPORT=kernel"); if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0) iovec[n++] = IOVEC_MAKE_STRING(syslog_priority); if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0) iovec[n++] = IOVEC_MAKE_STRING(syslog_facility); if ((priority & LOG_FACMASK) == LOG_KERN) iovec[n++] = IOVEC_MAKE_STRING("SYSLOG_IDENTIFIER=kernel"); else { pl -= syslog_parse_identifier((const char**) &p, &identifier, &pid); /* Avoid any messages we generated ourselves via * log_info() and friends. */ if (pid && is_us(pid)) goto finish; if (identifier) { syslog_identifier = strappend("SYSLOG_IDENTIFIER=", identifier); if (syslog_identifier) iovec[n++] = IOVEC_MAKE_STRING(syslog_identifier); } if (pid) { syslog_pid = strappend("SYSLOG_PID=", pid); if (syslog_pid) iovec[n++] = IOVEC_MAKE_STRING(syslog_pid); } } if (cunescape_length_with_prefix(p, pl, "MESSAGE=", UNESCAPE_RELAX, &message) >= 0) iovec[n++] = IOVEC_MAKE_STRING(message); server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), NULL, NULL, priority, 0); finish: for (j = 0; j < z; j++) free(iovec[j].iov_base); }
static int condition_test_needs_update(Condition *c) { const char *p; struct stat usr, other; assert(c); assert(c->parameter); assert(c->type == CONDITION_NEEDS_UPDATE); /* If the file system is read-only we shouldn't suggest an update */ if (path_is_read_only_fs(c->parameter) > 0) return false; /* Any other failure means we should allow the condition to be true, * so that we rather invoke too many update tools than too * few. */ if (!path_is_absolute(c->parameter)) return true; p = strjoina(c->parameter, "/.updated"); if (lstat(p, &other) < 0) return true; if (lstat("/usr/", &usr) < 0) return true; /* * First, compare seconds as they are always accurate... */ if (usr.st_mtim.tv_sec != other.st_mtim.tv_sec) return usr.st_mtim.tv_sec > other.st_mtim.tv_sec; /* * ...then compare nanoseconds. * * A false positive is only possible when /usr's nanoseconds > 0 * (otherwise /usr cannot be strictly newer than the target file) * AND the target file's nanoseconds == 0 * (otherwise the filesystem supports nsec timestamps, see stat(2)). */ if (usr.st_mtim.tv_nsec > 0 && other.st_mtim.tv_nsec == 0) { _cleanup_free_ char *timestamp_str = NULL; uint64_t timestamp; int r; r = parse_env_file(p, NULL, "TIMESTAMP_NSEC", ×tamp_str, NULL); if (r < 0) { log_error_errno(r, "Failed to parse timestamp file '%s', using mtime: %m", p); return true; } else if (r == 0) { log_debug("No data in timestamp file '%s', using mtime", p); return true; } r = safe_atou64(timestamp_str, ×tamp); if (r < 0) { log_error_errno(r, "Failed to parse timestamp value '%s' in file '%s', using mtime: %m", timestamp_str, p); return true; } timespec_store(&other.st_mtim, timestamp); } return usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec; }
static int device_amend(sd_device *device, const char *key, const char *value) { int r; assert(device); assert(key); assert(value); if (streq(key, "DEVPATH")) { char *path; path = strjoina("/sys", value); /* the caller must verify or trust this data (e.g., if it comes from the kernel) */ r = device_set_syspath(device, path, false); if (r < 0) return log_device_debug_errno(device, r, "sd-device: Failed to set syspath to '%s': %m", path); } else if (streq(key, "SUBSYSTEM")) { r = device_set_subsystem(device, value); if (r < 0) return log_device_debug_errno(device, r, "sd-device: Failed to set subsystem to '%s': %m", value); } else if (streq(key, "DEVTYPE")) { r = device_set_devtype(device, value); if (r < 0) return log_device_debug_errno(device, r, "sd-device: Failed to set devtype to '%s': %m", value); } else if (streq(key, "DEVNAME")) { r = device_set_devname(device, value); if (r < 0) return log_device_debug_errno(device, r, "sd-device: Failed to set devname to '%s': %m", value); } else if (streq(key, "USEC_INITIALIZED")) { usec_t t; r = safe_atou64(value, &t); if (r < 0) return log_device_debug_errno(device, r, "sd-device: Failed to parse timestamp '%s': %m", value); r = device_set_usec_initialized(device, t); if (r < 0) return log_device_debug_errno(device, r, "sd-device: Failed to set usec-initialized to '%s': %m", value); } else if (streq(key, "DRIVER")) { r = device_set_driver(device, value); if (r < 0) return log_device_debug_errno(device, r, "sd-device: Failed to set driver to '%s': %m", value); } else if (streq(key, "IFINDEX")) { r = device_set_ifindex(device, value); if (r < 0) return log_device_debug_errno(device, r, "sd-device: Failed to set ifindex to '%s': %m", value); } else if (streq(key, "DEVMODE")) { r = device_set_devmode(device, value); if (r < 0) return log_device_debug_errno(device, r, "sd-device: Failed to set devmode to '%s': %m", value); } else if (streq(key, "DEVUID")) { r = device_set_devuid(device, value); if (r < 0) return log_device_debug_errno(device, r, "sd-device: Failed to set devuid to '%s': %m", value); } else if (streq(key, "DEVGID")) { r = device_set_devgid(device, value); if (r < 0) return log_device_debug_errno(device, r, "sd-device: Failed to set devgid to '%s': %m", value); } else if (streq(key, "DEVLINKS")) { const char *word, *state; size_t l; FOREACH_WORD(word, l, value, state) { char devlink[l + 1]; strncpy(devlink, word, l); devlink[l] = '\0'; r = device_add_devlink(device, devlink); if (r < 0) return log_device_debug_errno(device, r, "sd-device: Failed to add devlink '%s': %m", devlink); } } else if (streq(key, "TAGS")) {