int write_env_file(const char *fname, char **l) { _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *p = NULL; char **i; int r; assert(fname); r = fopen_temporary(fname, &f, &p); if (r < 0) return r; fchmod_umask(fileno(f), 0644); STRV_FOREACH(i, l) write_env_var(f, *i); r = fflush_and_check(f); if (r >= 0) { if (rename(p, fname) >= 0) return 0; r = -errno; } unlink(p); return r; }
static int write_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSet *domains) { Iterator i; fputs_unlocked("# This file is managed by man:systemd-resolved(8). Do not edit.\n#\n" "# This is a dynamic resolv.conf file for connecting local clients directly to\n" "# all known DNS servers.\n#\n" "# Third party programs must not access this file directly, but only through the\n" "# symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a different way,\n" "# replace this symlink by a static file or a different symlink.\n#\n" "# See man:systemd-resolved.service(8) for details about the supported modes of\n" "# operation for /etc/resolv.conf.\n\n", f); if (ordered_set_isempty(dns)) fputs_unlocked("# No DNS servers known.\n", f); else { unsigned count = 0; DnsServer *s; ORDERED_SET_FOREACH(s, dns, i) write_resolv_conf_server(s, f, &count); } if (!ordered_set_isempty(domains)) write_resolv_conf_search(domains, f); return fflush_and_check(f); }
int write_string_stream_ts( FILE *f, const char *line, WriteStringFileFlags flags, struct timespec *ts) { assert(f); assert(line); fputs(line, f); if (!(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n")) fputc('\n', f); if (ts) { struct timespec twice[2] = {*ts, *ts}; if (futimens(fileno(f), twice) < 0) return -errno; } if (flags & WRITE_STRING_FILE_SYNC) return fflush_sync_and_check(f); else return fflush_and_check(f); }
int calendar_spec_to_string(const CalendarSpec *c, char **p) { char *buf = NULL; size_t sz = 0; FILE *f; int r; assert(c); assert(p); f = open_memstream(&buf, &sz); if (!f) return -ENOMEM; if (c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS) { format_weekdays(f, c); fputc(' ', f); } format_chain(f, 4, c->year, false); fputc('-', f); format_chain(f, 2, c->month, false); fputc(c->end_of_month ? '~' : '-', f); format_chain(f, 2, c->day, false); fputc(' ', f); format_chain(f, 2, c->hour, false); fputc(':', f); format_chain(f, 2, c->minute, false); fputc(':', f); format_chain(f, 2, c->microsecond, true); if (c->utc) fputs(" UTC", f); else if (IN_SET(c->dst, 0, 1)) { /* If daylight saving is explicitly on or off, let's show the used timezone. */ tzset(); if (!isempty(tzname[c->dst])) { fputc(' ', f); fputs(tzname[c->dst], f); } } r = fflush_and_check(f); if (r < 0) { free(buf); fclose(f); return r; } fclose(f); *p = buf; return 0; }
int write_string_stream(FILE *f, const char *line, bool enforce_newline) { assert(f); assert(line); fputs(line, f); if (enforce_newline && !endswith(line, "\n")) fputc('\n', f); return fflush_and_check(f); }
static int write_fsck_sysroot_service(const char *dir, const char *what) { _cleanup_free_ char *device = NULL, *escaped = NULL, *escaped2 = NULL; _cleanup_fclose_ FILE *f = NULL; const char *unit; int r; escaped = specifier_escape(what); if (!escaped) return log_oom(); escaped2 = cescape(escaped); if (!escaped2) return log_oom(); unit = strjoina(dir, "/systemd-fsck-root.service"); log_debug("Creating %s", unit); r = unit_name_from_path(what, ".device", &device); if (r < 0) return log_error_errno(r, "Failed to convert device \"%s\" to unit name: %m", what); f = fopen(unit, "wxe"); if (!f) return log_error_errno(errno, "Failed to create unit file %s: %m", unit); fprintf(f, "# Automatically generated by %1$s\n\n" "[Unit]\n" "Description=File System Check on %2$s\n" "Documentation=man:systemd-fsck-root.service(8)\n" "DefaultDependencies=no\n" "BindsTo=%3$s\n" "After=initrd-root-device.target local-fs-pre.target %3$s\n" "Before=shutdown.target\n" "\n" "[Service]\n" "Type=oneshot\n" "RemainAfterExit=yes\n" "ExecStart=" SYSTEMD_FSCK_PATH " %4$s\n" "TimeoutSec=0\n", program_invocation_short_name, escaped, device, escaped2); r = fflush_and_check(f); if (r < 0) return log_error_errno(r, "Failed to write unit file %s: %m", unit); return 0; }
int write_string_stream_ts( FILE *f, const char *line, WriteStringFileFlags flags, struct timespec *ts) { bool needs_nl; int r; assert(f); assert(line); if (ferror(f)) return -EIO; needs_nl = !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n"); if (needs_nl && (flags & WRITE_STRING_FILE_DISABLE_BUFFER)) { /* If STDIO buffering was disabled, then let's append the newline character to the string itself, so * that the write goes out in one go, instead of two */ line = strjoina(line, "\n"); needs_nl = false; } if (fputs(line, f) == EOF) return -errno; if (needs_nl) if (fputc('\n', f) == EOF) return -errno; if (flags & WRITE_STRING_FILE_SYNC) r = fflush_sync_and_check(f); else r = fflush_and_check(f); if (r < 0) return r; if (ts) { struct timespec twice[2] = {*ts, *ts}; if (futimens(fileno(f), twice) < 0) return -errno; } return 0; }
static int write_root_shadow(const char *path, const struct spwd *p) { _cleanup_fclose_ FILE *f = NULL; assert(path); assert(p); RUN_WITH_UMASK(0777) f = fopen(path, "wex"); if (!f) return -errno; errno = 0; if (putspent(p, f) != 0) return errno ? -errno : -EIO; return fflush_and_check(f); }
int calendar_spec_to_string(const CalendarSpec *c, char **p) { char *buf = NULL; size_t sz = 0; FILE *f; int r; assert(c); assert(p); f = open_memstream(&buf, &sz); if (!f) return -ENOMEM; if (c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS) { format_weekdays(f, c); fputc(' ', f); } format_chain(f, 4, c->year); fputc('-', f); format_chain(f, 2, c->month); fputc('-', f); format_chain(f, 2, c->day); fputc(' ', f); format_chain(f, 2, c->hour); fputc(':', f); format_chain(f, 2, c->minute); fputc(':', f); format_chain(f, 2, c->second); if (c->utc) fputs(" UTC", f); r = fflush_and_check(f); if (r < 0) { free(buf); fclose(f); return r; } fclose(f); *p = buf; return 0; }
static int add_swap(const char *path) { _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL; _cleanup_fclose_ FILE *f = NULL; int r; assert(path); log_debug("Adding swap: %s", path); r = unit_name_from_path(path, ".swap", &name); if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); unit = strjoin(arg_dest, "/", name, NULL); if (!unit) return log_oom(); f = fopen(unit, "wxe"); if (!f) return log_error_errno(errno, "Failed to create unit file %s: %m", unit); fprintf(f, "# Automatically generated by systemd-gpt-auto-generator\n\n" "[Unit]\n" "Description=Swap Partition\n" "Documentation=man:systemd-gpt-auto-generator(8)\n\n" "[Swap]\n" "What=%s\n", path); r = fflush_and_check(f); if (r < 0) return log_error_errno(r, "Failed to write unit file %s: %m", unit); lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL); if (!lnk) return log_oom(); mkdir_parents_label(lnk, 0755); if (symlink(unit, lnk) < 0) return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); return 0; }
static int write_stub_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSet *domains) { fputs_unlocked("# This file is managed by man:systemd-resolved(8). Do not edit.\n" "#\n" "# This is a dynamic resolv.conf file for connecting local clients to the\n" "# internal DNS stub resolver of systemd-resolved. This file lists all\n" "# configured search domains.\n" "#\n" "# Run \"resolvectl status\" to see details about the uplink DNS servers\n" "# currently in use.\n" "#\n" "# Third party programs must not access this file directly, but only through the\n" "# symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a different way,\n" "# replace this symlink by a static file or a different symlink.\n" "#\n" "# See man:systemd-resolved.service(8) for details about the supported modes of\n" "# operation for /etc/resolv.conf.\n" "\n" "nameserver 127.0.0.53\n", f); if (!ordered_set_isempty(domains)) write_resolv_conf_search(domains, f); return fflush_and_check(f); }
static int stdout_stream_save(StdoutStream *s) { _cleanup_free_ char *temp_path = NULL; _cleanup_fclose_ FILE *f = NULL; int r; assert(s); if (s->state != STDOUT_STREAM_RUNNING) return 0; if (!s->state_file) { struct stat st; r = fstat(s->fd, &st); if (r < 0) return log_warning_errno(errno, "Failed to stat connected stream: %m"); /* We use device and inode numbers as identifier for the stream */ if (asprintf(&s->state_file, "/run/systemd/journal/streams/%lu:%lu", (unsigned long) st.st_dev, (unsigned long) st.st_ino) < 0) return log_oom(); } mkdir_p("/run/systemd/journal/streams", 0755); r = fopen_temporary(s->state_file, &f, &temp_path); if (r < 0) goto fail; fprintf(f, "# This is private data. Do not parse\n" "PRIORITY=%i\n" "LEVEL_PREFIX=%i\n" "FORWARD_TO_SYSLOG=%i\n" "FORWARD_TO_KMSG=%i\n" "FORWARD_TO_CONSOLE=%i\n" "STREAM_ID=%s\n", s->priority, s->level_prefix, s->forward_to_syslog, s->forward_to_kmsg, s->forward_to_console, s->id_field + strlen("_STREAM_ID=")); if (!isempty(s->identifier)) { _cleanup_free_ char *escaped; escaped = cescape(s->identifier); if (!escaped) { r = -ENOMEM; goto fail; } fprintf(f, "IDENTIFIER=%s\n", escaped); } if (!isempty(s->unit_id)) { _cleanup_free_ char *escaped; escaped = cescape(s->unit_id); if (!escaped) { r = -ENOMEM; goto fail; } fprintf(f, "UNIT=%s\n", escaped); } r = fflush_and_check(f); if (r < 0) goto fail; if (rename(temp_path, s->state_file) < 0) { r = -errno; goto fail; } if (!s->fdstore && !s->in_notify_queue) { LIST_PREPEND(stdout_stream_notify_queue, s->server->stdout_streams_notify_queue, s); s->in_notify_queue = true; if (s->server->notify_event_source) { r = sd_event_source_set_enabled(s->server->notify_event_source, SD_EVENT_ON); if (r < 0) log_warning_errno(r, "Failed to enable notify event source: %m"); } } return 0; fail: (void) unlink(s->state_file); if (temp_path) (void) unlink(temp_path); return log_error_errno(r, "Failed to save stream data %s: %m", s->state_file); }
int generator_hook_up_mkfs( const char *dir, const char *what, const char *where, const char *type) { _cleanup_free_ char *node = NULL, *unit = NULL, *escaped = NULL, *where_unit = NULL; _cleanup_fclose_ FILE *f = NULL; const char *unit_file; int r; node = fstab_node_to_udev_node(what); if (!node) return log_oom(); /* Nothing to work on. */ if (!is_device_path(node)) { log_error("Cannot format something that is not a device node: %s", node); return -EINVAL; } if (!type || streq(type, "auto")) { log_error("Cannot format partition %s, filesystem type is not specified", node); return -EINVAL; } r = unit_name_from_path_instance("systemd-mkfs", node, ".service", &unit); if (r < 0) return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m", node); unit_file = strjoina(dir, "/", unit); log_debug("Creating %s", unit_file); escaped = cescape(node); if (!escaped) return log_oom(); r = unit_name_from_path(where, ".mount", &where_unit); if (r < 0) return log_error_errno(r, "Failed to make unit name from path \"%s\": %m", where); f = fopen(unit_file, "wxe"); if (!f) return log_error_errno(errno, "Failed to create unit file %s: %m", unit_file); fprintf(f, "# Automatically generated by %s\n\n" "[Unit]\n" "Description=Make File System on %%f\n" "Documentation=man:[email protected](8)\n" "DefaultDependencies=no\n" "BindsTo=%%i.device\n" "After=%%i.device\n" /* fsck might or might not be used, so let's be safe and order * ourselves before both [email protected] and the mount unit. */ "Before=systemd-fsck@%%i.service\n" "Before=%s\n" "Before=shutdown.target\n" "\n" "[Service]\n" "Type=oneshot\n" "RemainAfterExit=yes\n" "ExecStart="SYSTEMD_MAKEFS_PATH " %s %s\n" "TimeoutSec=0\n", program_invocation_short_name, where_unit, type, escaped); // XXX: what about local-fs-pre.target? r = fflush_and_check(f); if (r < 0) return log_error_errno(r, "Failed to write unit file %s: %m", unit_file); return generator_add_symlink(dir, where_unit, "requires", unit); }
int generator_hook_up_mkswap( const char *dir, const char *what) { _cleanup_free_ char *node = NULL, *unit = NULL, *escaped = NULL, *where_unit = NULL; _cleanup_fclose_ FILE *f = NULL; const char *unit_file; int r; node = fstab_node_to_udev_node(what); if (!node) return log_oom(); /* Nothing to work on. */ if (!is_device_path(node)) { log_error("Cannot format something that is not a device node: %s", node); return -EINVAL; } r = unit_name_from_path_instance("systemd-mkswap", node, ".service", &unit); if (r < 0) return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m", node); unit_file = strjoina(dir, "/", unit); log_debug("Creating %s", unit_file); escaped = cescape(node); if (!escaped) return log_oom(); r = unit_name_from_path(what, ".swap", &where_unit); if (r < 0) return log_error_errno(r, "Failed to make unit name from path \"%s\": %m", what); f = fopen(unit_file, "wxe"); if (!f) return log_error_errno(errno, "Failed to create unit file %s: %m", unit_file); fprintf(f, "# Automatically generated by %s\n\n" "[Unit]\n" "Description=Make Swap on %%f\n" "Documentation=man:[email protected](8)\n" "DefaultDependencies=no\n" "BindsTo=%%i.device\n" "After=%%i.device\n" "Before=%s\n" "Before=shutdown.target\n" "\n" "[Service]\n" "Type=oneshot\n" "RemainAfterExit=yes\n" "ExecStart="SYSTEMD_MAKEFS_PATH " swap %s\n" "TimeoutSec=0\n", program_invocation_short_name, where_unit, escaped); r = fflush_and_check(f); if (r < 0) return log_error_errno(r, "Failed to write unit file %s: %m", unit_file); return generator_add_symlink(dir, where_unit, "requires", unit); }
static int x11_write_data(Context *c) { _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *temp_path = NULL; int r; if (isempty(c->x11_layout) && isempty(c->x11_model) && isempty(c->x11_variant) && isempty(c->x11_options)) { if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) return errno == ENOENT ? 0 : -errno; return 0; } mkdir_p_label("/etc/X11/xorg.conf.d", 0755); r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path); if (r < 0) return r; fchmod(fileno(f), 0644); fputs("# Read and parsed by systemd-localed. It's probably wise not to edit this file\n" "# manually too freely.\n" "Section \"InputClass\"\n" " Identifier \"system-keyboard\"\n" " MatchIsKeyboard \"on\"\n", f); if (!isempty(c->x11_layout)) fprintf(f, " Option \"XkbLayout\" \"%s\"\n", c->x11_layout); if (!isempty(c->x11_model)) fprintf(f, " Option \"XkbModel\" \"%s\"\n", c->x11_model); if (!isempty(c->x11_variant)) fprintf(f, " Option \"XkbVariant\" \"%s\"\n", c->x11_variant); if (!isempty(c->x11_options)) fprintf(f, " Option \"XkbOptions\" \"%s\"\n", c->x11_options); fputs("EndSection\n", f); r = fflush_and_check(f); if (r < 0) goto fail; if (rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) { r = -errno; goto fail; } return 0; fail: (void) unlink("/etc/X11/xorg.conf.d/00-keyboard.conf"); if (temp_path) (void) unlink(temp_path); return r; }
static int add_swap( const char *what, struct mntent *me, MountpointFlags flags) { _cleanup_free_ char *name = NULL; _cleanup_fclose_ FILE *f = NULL; int r; assert(what); assert(me); if (access("/proc/swaps", F_OK) < 0) { log_info("Swap not supported, ignoring fstab swap entry for %s.", what); return 0; } if (detect_container() > 0) { log_info("Running in a container, ignoring fstab swap entry for %s.", what); return 0; } r = unit_name_from_path(what, ".swap", &name); if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); r = generator_open_unit_file(arg_dest, "/etc/fstab", name, &f); if (r < 0) return r; fputs("# Automatically generated by systemd-fstab-generator\n\n" "[Unit]\n" "SourcePath=/etc/fstab\n" "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n\n" "[Swap]\n", f); r = write_what(f, what); if (r < 0) return r; r = write_options(f, me->mnt_opts); if (r < 0) return r; r = fflush_and_check(f); if (r < 0) return log_error_errno(r, "Failed to write unit file %s: %m", name); /* use what as where, to have a nicer error message */ r = generator_write_timeouts(arg_dest, what, what, me->mnt_opts, NULL); if (r < 0) return r; if (flags & MAKEFS) { r = generator_hook_up_mkswap(arg_dest, what); if (r < 0) return r; } if (flags & GROWFS) /* TODO: swap devices must be wiped and recreated */ log_warning("%s: growing swap devices is currently unsupported.", what); if (!(flags & NOAUTO)) { r = generator_add_symlink(arg_dest, SPECIAL_SWAP_TARGET, (flags & NOFAIL) ? "wants" : "requires", name); if (r < 0) return r; } return 0; }
static int create_device(void) { _cleanup_free_ char *u = NULL, *v = NULL, *d = NULL, *e = NULL, *u_escaped = NULL, *v_escaped = NULL, *root_hash_escaped = NULL; _cleanup_fclose_ FILE *f = NULL; const char *to; int r; /* If all three pieces of information are missing, then verity is turned off */ if (!arg_root_hash && !arg_data_what && !arg_hash_what) return 0; /* if one of them is missing however, the data is simply incomplete and this is an error */ if (!arg_root_hash) log_error("Verity information incomplete, root hash unspecified."); if (!arg_data_what) log_error("Verity information incomplete, root data device unspecified."); if (!arg_hash_what) log_error("Verity information incomplete, root hash device unspecified."); if (!arg_root_hash || !arg_data_what || !arg_hash_what) return -EINVAL; log_debug("Using root verity data device %s,\n" " hash device %s,\n" " and root hash %s.", arg_data_what, arg_hash_what, arg_root_hash); u = fstab_node_to_udev_node(arg_data_what); if (!u) return log_oom(); v = fstab_node_to_udev_node(arg_hash_what); if (!v) return log_oom(); u_escaped = specifier_escape(u); if (!u_escaped) return log_oom(); v_escaped = specifier_escape(v); if (!v_escaped) return log_oom(); r = unit_name_from_path(u, ".device", &d); if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); r = unit_name_from_path(v, ".device", &e); if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); root_hash_escaped = specifier_escape(arg_root_hash); if (!root_hash_escaped) return log_oom(); r = generator_open_unit_file(arg_dest, NULL, SYSTEMD_VERITYSETUP_SERVICE, &f); if (r < 0) return r; fprintf(f, "[Unit]\n" "Description=Integrity Protection Setup for %%I\n" "Documentation=man:systemd-veritysetup-generator(8) man:[email protected](8)\n" "SourcePath=/proc/cmdline\n" "DefaultDependencies=no\n" "Conflicts=umount.target\n" "BindsTo=%s %s\n" "IgnoreOnIsolate=true\n" "After=cryptsetup-pre.target %s %s\n" "Before=cryptsetup.target umount.target\n" "\n[Service]\n" "Type=oneshot\n" "RemainAfterExit=yes\n" "ExecStart=" ROOTLIBEXECDIR "/systemd-veritysetup attach root '%s' '%s' '%s'\n" "ExecStop=" ROOTLIBEXECDIR "/systemd-veritysetup detach root\n", d, e, d, e, u_escaped, v_escaped, root_hash_escaped); r = fflush_and_check(f); if (r < 0) return log_error_errno(r, "Failed to write file unit "SYSTEMD_VERITYSETUP_SERVICE": %m"); to = strjoina(arg_dest, "/cryptsetup.target.requires/" SYSTEMD_VERITYSETUP_SERVICE); (void) mkdir_parents(to, 0755); if (symlink("../" SYSTEMD_VERITYSETUP_SERVICE, to) < 0) return log_error_errno(errno, "Failed to create symlink %s: %m", to); return 0; }
static int add_swap( const char *what, struct mntent *me, bool noauto, bool nofail) { _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL, *filtered = NULL; _cleanup_fclose_ FILE *f = NULL; int r, pri = -1; const char *opts; assert(what); assert(me); if (access("/proc/swaps", F_OK) < 0) { log_info("Swap not supported, ignoring fstab swap entry for %s.", what); return 0; } if (detect_container(NULL) > 0) { log_info("Running in a container, ignoring fstab swap entry for %s.", what); return 0; } opts = me->mnt_opts; r = fstab_find_pri(opts, &pri); if (r < 0) { log_error_errno(r, "Failed to parse priority, ignoring: %m"); /* Remove invalid pri field */ r = fstab_filter_options(opts, "pri\0", NULL, NULL, &filtered); if (r < 0) return log_error_errno(r, "Failed to parse options: %m"); opts = filtered; } name = unit_name_from_path(what, ".swap"); if (!name) return log_oom(); unit = strjoin(arg_dest, "/", name, NULL); if (!unit) return log_oom(); f = fopen(unit, "wxe"); if (!f) { if (errno == EEXIST) log_error("Failed to create swap unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit); else log_error_errno(errno, "Failed to create unit file %s: %m", unit); return -errno; } fprintf(f, "# Automatically generated by systemd-fstab-generator\n\n" "[Unit]\n" "SourcePath=/etc/fstab\n" "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n\n" "[Swap]\n" "What=%s\n", what); /* Note that we currently pass the priority field twice, once * in Priority=, and once in Options= */ if (pri >= 0) fprintf(f, "Priority=%i\n", pri); if (!isempty(opts) && !streq(opts, "defaults")) fprintf(f, "Options=%s\n", opts); r = fflush_and_check(f); if (r < 0) return log_error_errno(r, "Failed to write unit file %s: %m", unit); /* use what as where, to have a nicer error message */ r = generator_write_timeouts(arg_dest, what, what, opts, NULL); if (r < 0) return r; if (!noauto) { lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET, nofail ? ".wants/" : ".requires/", name, NULL); if (!lnk) return log_oom(); mkdir_parents_label(lnk, 0755); if (symlink(unit, lnk) < 0) return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); } return 0; }
static int copy_file(const char *from, const char *to, bool force) { _cleanup_fclose_ FILE *f = NULL, *g = NULL; char *p; int r; struct timespec t[2]; struct stat st; assert(from); assert(to); f = fopen(from, "re"); if (!f) return log_error_errno(errno, "Failed to open \"%s\" for reading: %m", from); if (!force) { /* If this is an update, then let's compare versions first */ r = version_check(fileno(f), from, to); if (r < 0) return r; } p = strjoina(to, "~"); g = fopen(p, "wxe"); if (!g) { /* Directory doesn't exist yet? Then let's skip this... */ if (!force && errno == ENOENT) return 0; return log_error_errno(errno, "Failed to open \"%s\" for writing: %m", to); } rewind(f); do { size_t k; uint8_t buf[32*1024]; k = fread(buf, 1, sizeof(buf), f); if (ferror(f)) { r = log_error_errno(EIO, "Failed to read \"%s\": %m", from); goto error; } if (k == 0) break; fwrite(buf, 1, k, g); if (ferror(g)) { r = log_error_errno(EIO, "Failed to write \"%s\": %m", to); goto error; } } while (!feof(f)); r = fflush_and_check(g); if (r < 0) { log_error_errno(r, "Failed to write \"%s\": %m", to); goto error; } r = fstat(fileno(f), &st); if (r < 0) { r = log_error_errno(errno, "Failed to get file timestamps of \"%s\": %m", from); goto error; } t[0] = st.st_atim; t[1] = st.st_mtim; r = futimens(fileno(g), t); if (r < 0) { r = log_error_errno(errno, "Failed to set file timestamps on \"%s\": %m", p); goto error; } if (rename(p, to) < 0) { r = log_error_errno(errno, "Failed to rename \"%s\" to \"%s\": %m", p, to); goto error; } log_info("Copied \"%s\" to \"%s\".", from, to); return 0; error: (void) unlink(p); return r; }
static int create_disk( const char *name, const char *device, const char *password, const char *options) { _cleanup_free_ char *p = NULL, *n = NULL, *d = NULL, *u = NULL, *e = NULL, *filtered = NULL, *u_escaped = NULL, *password_escaped = NULL, *filtered_escaped = NULL, *name_escaped = NULL; _cleanup_fclose_ FILE *f = NULL; const char *dmname; bool noauto, nofail, tmp, swap, netdev; int r; assert(name); assert(device); noauto = fstab_test_yes_no_option(options, "noauto\0" "auto\0"); nofail = fstab_test_yes_no_option(options, "nofail\0" "fail\0"); tmp = fstab_test_option(options, "tmp\0"); swap = fstab_test_option(options, "swap\0"); netdev = fstab_test_option(options, "_netdev\0"); if (tmp && swap) { log_error("Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.", name); return -EINVAL; } name_escaped = specifier_escape(name); if (!name_escaped) return log_oom(); e = unit_name_escape(name); if (!e) return log_oom(); r = unit_name_build("systemd-cryptsetup", e, ".service", &n); if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); p = strjoin(arg_dest, "/", n); if (!p) return log_oom(); u = fstab_node_to_udev_node(device); if (!u) return log_oom(); u_escaped = specifier_escape(u); if (!u_escaped) return log_oom(); r = unit_name_from_path(u, ".device", &d); if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); password_escaped = specifier_escape(password); if (!password_escaped) return log_oom(); f = fopen(p, "wxe"); if (!f) return log_error_errno(errno, "Failed to create unit file %s: %m", p); fprintf(f, "# Automatically generated by systemd-cryptsetup-generator\n\n" "[Unit]\n" "Description=Cryptography Setup for %%I\n" "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:[email protected](8)\n" "SourcePath=/etc/crypttab\n" "DefaultDependencies=no\n" "Conflicts=umount.target\n" "IgnoreOnIsolate=true\n" "After=%s\n", netdev ? "remote-fs-pre.target" : "cryptsetup-pre.target"); if (!nofail) fprintf(f, "Before=%s\n", netdev ? "remote-cryptsetup.target" : "cryptsetup.target"); if (password) { if (STR_IN_SET(password, "/dev/urandom", "/dev/random", "/dev/hw_random")) fputs_unlocked("After=systemd-random-seed.service\n", f); else if (!STR_IN_SET(password, "-", "none")) { _cleanup_free_ char *uu; uu = fstab_node_to_udev_node(password); if (!uu) return log_oom(); if (!path_equal(uu, "/dev/null")) { if (path_startswith(uu, "/dev/")) { _cleanup_free_ char *dd = NULL; r = unit_name_from_path(uu, ".device", &dd); if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); fprintf(f, "After=%1$s\nRequires=%1$s\n", dd); } else fprintf(f, "RequiresMountsFor=%s\n", password_escaped); } } } if (path_startswith(u, "/dev/")) { fprintf(f, "BindsTo=%s\n" "After=%s\n" "Before=umount.target\n", d, d); if (swap) fputs_unlocked("Before=dev-mapper-%i.swap\n", f); } else fprintf(f, "RequiresMountsFor=%s\n", u_escaped); r = generator_write_timeouts(arg_dest, device, name, options, &filtered); if (r < 0) return r; filtered_escaped = specifier_escape(filtered); if (!filtered_escaped) return log_oom(); fprintf(f, "\n[Service]\n" "Type=oneshot\n" "RemainAfterExit=yes\n" "TimeoutSec=0\n" /* the binary handles timeouts anyway */ "KeyringMode=shared\n" /* make sure we can share cached keys among instances */ "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n" "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n", name_escaped, u_escaped, strempty(password_escaped), strempty(filtered_escaped), name_escaped); if (tmp) fprintf(f, "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n", name_escaped); if (swap) fprintf(f, "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n", name_escaped); r = fflush_and_check(f); if (r < 0) return log_error_errno(r, "Failed to write file %s: %m", p); if (!noauto) { r = generator_add_symlink(arg_dest, d, "wants", n); if (r < 0) return r; r = generator_add_symlink(arg_dest, netdev ? "remote-cryptsetup.target" : "cryptsetup.target", nofail ? "wants" : "requires", n); if (r < 0) return r; } dmname = strjoina("dev-mapper-", e, ".device"); r = generator_add_symlink(arg_dest, dmname, "requires", n); if (r < 0) return r; if (!noauto && !nofail) { r = write_drop_in(arg_dest, dmname, 90, "device-timeout", "# Automatically generated by systemd-cryptsetup-generator \n\n" "[Unit]\nJobTimeoutSec=0"); if (r < 0) return log_error_errno(r, "Failed to write device drop-in: %m"); } return 0; }
static int create_dbus_files( const char *path, const char *name, const char *service, const char *exec, const char *user, const char *type) { _cleanup_free_ char *b = NULL, *s = NULL, *lnk = NULL; _cleanup_fclose_ FILE *f = NULL; int r; assert(path); assert(name); assert(service || exec); if (!service) { _cleanup_free_ char *a = NULL; s = strjoin("dbus-", name, ".service", NULL); if (!s) return log_oom(); a = strjoin(arg_dest_late, "/", s, NULL); if (!a) return log_oom(); f = fopen(a, "wxe"); if (!f) return log_error_errno(errno, "Failed to create %s: %m", a); fprintf(f, "# Automatically generated by systemd-dbus1-generator\n\n" "[Unit]\n" "SourcePath=%s\n" "Description=DBUS1: %s\n" "Documentation=man:systemd-dbus1-generator(8)\n\n" "[Service]\n" "ExecStart=%s\n" "Type=dbus\n" "BusName=%s\n", path, name, exec, name); if (user) fprintf(f, "User=%s\n", user); if (type) { fprintf(f, "Environment=DBUS_STARTER_BUS_TYPE=%s\n", type); if (streq(type, "system")) fprintf(f, "Environment=DBUS_STARTER_ADDRESS=" DEFAULT_SYSTEM_BUS_ADDRESS "\n"); else if (streq(type, "session")) { char *run; run = getenv("XDG_RUNTIME_DIR"); if (!run) { log_error("XDG_RUNTIME_DIR not set."); return -EINVAL; } fprintf(f, "Environment=DBUS_STARTER_ADDRESS="KERNEL_USER_BUS_ADDRESS_FMT ";" UNIX_USER_BUS_ADDRESS_FMT "\n", getuid(), run); } } r = fflush_and_check(f); if (r < 0) return log_error_errno(r, "Failed to write %s: %m", a); f = safe_fclose(f); service = s; } b = strjoin(arg_dest_late, "/", name, ".busname", NULL); if (!b) return log_oom(); f = fopen(b, "wxe"); if (!f) return log_error_errno(errno, "Failed to create %s: %m", b); fprintf(f, "# Automatically generated by systemd-dbus1-generator\n\n" "[Unit]\n" "SourcePath=%s\n" "Description=DBUS1: %s\n" "Documentation=man:systemd-dbus1-generator(8)\n\n" "[BusName]\n" "Name=%s\n" "Service=%s\n" "AllowWorld=talk\n", path, name, name, service); r = fflush_and_check(f); if (r < 0) return log_error_errno(r, "Failed to write %s: %m", b); lnk = strjoin(arg_dest_late, "/" SPECIAL_BUSNAMES_TARGET ".wants/", name, ".busname", NULL); if (!lnk) return log_oom(); mkdir_parents_label(lnk, 0755); if (symlink(b, lnk)) return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); return 0; }
static int add_mount( const char *id, const char *what, const char *where, const char *fstype, bool rw, const char *options, const char *description, const char *post) { _cleanup_free_ char *unit = NULL, *lnk = NULL, *crypto_what = NULL, *p = NULL; _cleanup_fclose_ FILE *f = NULL; int r; assert(id); assert(what); assert(where); assert(description); log_debug("Adding %s: %s %s", where, what, strna(fstype)); if (streq_ptr(fstype, "crypto_LUKS")) { r = add_cryptsetup(id, what, rw, &crypto_what); if (r < 0) return r; what = crypto_what; fstype = NULL; } r = unit_name_from_path(where, ".mount", &unit); if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); p = strjoin(arg_dest, "/", unit, NULL); if (!p) return log_oom(); f = fopen(p, "wxe"); if (!f) return log_error_errno(errno, "Failed to create unit file %s: %m", unit); fprintf(f, "# Automatically generated by systemd-gpt-auto-generator\n\n" "[Unit]\n" "Description=%s\n" "Documentation=man:systemd-gpt-auto-generator(8)\n", description); if (post) fprintf(f, "Before=%s\n", post); r = generator_write_fsck_deps(f, arg_dest, what, where, fstype); if (r < 0) return r; fprintf(f, "\n" "[Mount]\n" "What=%s\n" "Where=%s\n", what, where); if (fstype) fprintf(f, "Type=%s\n", fstype); if (options) fprintf(f, "Options=%s,%s\n", options, rw ? "rw" : "ro"); else fprintf(f, "Options=%s\n", rw ? "rw" : "ro"); r = fflush_and_check(f); if (r < 0) return log_error_errno(r, "Failed to write unit file %s: %m", p); if (post) { lnk = strjoin(arg_dest, "/", post, ".requires/", unit, NULL); if (!lnk) return log_oom(); mkdir_parents_label(lnk, 0755); if (symlink(p, lnk) < 0) return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); } return 0; }
static int add_swap( const char *what, struct mntent *me, bool noauto, bool nofail) { _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL; _cleanup_fclose_ FILE *f = NULL; int r; assert(what); assert(me); if (access("/proc/swaps", F_OK) < 0) { log_info("Swap not supported, ignoring fstab swap entry for %s.", what); return 0; } if (detect_container() > 0) { log_info("Running in a container, ignoring fstab swap entry for %s.", what); return 0; } r = unit_name_from_path(what, ".swap", &name); if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); unit = strjoin(arg_dest, "/", name); if (!unit) return log_oom(); f = fopen(unit, "wxe"); if (!f) return log_error_errno(errno, errno == EEXIST ? "Failed to create swap unit file %s, as it already exists. Duplicate entry in /etc/fstab?" : "Failed to create unit file %s: %m", unit); fprintf(f, "# Automatically generated by systemd-fstab-generator\n\n" "[Unit]\n" "SourcePath=/etc/fstab\n" "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n\n" "[Swap]\n" "What=%s\n", what); if (!isempty(me->mnt_opts) && !streq(me->mnt_opts, "defaults")) fprintf(f, "Options=%s\n", me->mnt_opts); r = fflush_and_check(f); if (r < 0) return log_error_errno(r, "Failed to write unit file %s: %m", unit); /* use what as where, to have a nicer error message */ r = generator_write_timeouts(arg_dest, what, what, me->mnt_opts, NULL); if (r < 0) return r; if (!noauto) { lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET, nofail ? ".wants/" : ".requires/", name, NULL); if (!lnk) return log_oom(); mkdir_parents_label(lnk, 0755); if (symlink(unit, lnk) < 0) return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); } return 0; }
static int add_automount( const char *id, const char *what, const char *where, const char *fstype, bool rw, const char *options, const char *description, usec_t timeout) { _cleanup_free_ char *unit = NULL, *lnk = NULL; _cleanup_free_ char *opt, *p = NULL; _cleanup_fclose_ FILE *f = NULL; int r; assert(id); assert(where); assert(description); if (options) opt = strjoin(options, ",noauto", NULL); else opt = strdup("noauto"); if (!opt) return log_oom(); r = add_mount(id, what, where, fstype, rw, opt, description, NULL); if (r < 0) return r; r = unit_name_from_path(where, ".automount", &unit); if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); p = strjoin(arg_dest, "/", unit, NULL); if (!p) return log_oom(); f = fopen(p, "wxe"); if (!f) return log_error_errno(errno, "Failed to create unit file %s: %m", unit); fprintf(f, "# Automatically generated by systemd-gpt-auto-generator\n\n" "[Unit]\n" "Description=%s\n" "Documentation=man:systemd-gpt-auto-generator(8)\n" "[Automount]\n" "Where=%s\n" "TimeoutIdleSec=%lld\n", description, where, (unsigned long long)timeout / USEC_PER_SEC); r = fflush_and_check(f); if (r < 0) return log_error_errno(r, "Failed to write unit file %s: %m", p); lnk = strjoin(arg_dest, "/" SPECIAL_LOCAL_FS_TARGET ".wants/", unit, NULL); if (!lnk) return log_oom(); mkdir_parents_label(lnk, 0755); if (symlink(p, lnk) < 0) return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); return 0; }
static int create_disk( const char *name, const char *device, const char *password, const char *options) { _cleanup_free_ char *p = NULL, *n = NULL, *d = NULL, *u = NULL, *to = NULL, *e = NULL, *filtered = NULL; _cleanup_fclose_ FILE *f = NULL; bool noauto, nofail, tmp, swap; char *from; int r; assert(name); assert(device); noauto = fstab_test_yes_no_option(options, "noauto\0" "auto\0"); nofail = fstab_test_yes_no_option(options, "nofail\0" "fail\0"); tmp = fstab_test_option(options, "tmp\0"); swap = fstab_test_option(options, "swap\0"); if (tmp && swap) { log_error("Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.", name); return -EINVAL; } e = unit_name_escape(name); if (!e) return log_oom(); r = unit_name_build("systemd-cryptsetup", e, ".service", &n); if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); p = strjoin(arg_dest, "/", n, NULL); if (!p) return log_oom(); u = fstab_node_to_udev_node(device); if (!u) return log_oom(); r = unit_name_from_path(u, ".device", &d); if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); f = fopen(p, "wxe"); if (!f) return log_error_errno(errno, "Failed to create unit file %s: %m", p); fputs( "# Automatically generated by systemd-cryptsetup-generator\n\n" "[Unit]\n" "Description=Cryptography Setup for %I\n" "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:[email protected](8)\n" "SourcePath=/etc/crypttab\n" "DefaultDependencies=no\n" "Conflicts=umount.target\n" "BindsTo=dev-mapper-%i.device\n" "IgnoreOnIsolate=true\n" "After=cryptsetup-pre.target\n", f); if (!nofail) fprintf(f, "Before=cryptsetup.target\n"); if (password) { if (STR_IN_SET(password, "/dev/urandom", "/dev/random", "/dev/hw_random")) fputs("After=systemd-random-seed.service\n", f); else if (!streq(password, "-") && !streq(password, "none")) { _cleanup_free_ char *uu; uu = fstab_node_to_udev_node(password); if (!uu) return log_oom(); if (!path_equal(uu, "/dev/null")) { if (is_device_path(uu)) { _cleanup_free_ char *dd = NULL; r = unit_name_from_path(uu, ".device", &dd); if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); fprintf(f, "After=%1$s\nRequires=%1$s\n", dd); } else fprintf(f, "RequiresMountsFor=%s\n", password); } } } if (is_device_path(u)) fprintf(f, "BindsTo=%s\n" "After=%s\n" "Before=umount.target\n", d, d); else fprintf(f, "RequiresMountsFor=%s\n", u); r = generator_write_timeouts(arg_dest, device, name, options, &filtered); if (r < 0) return r; fprintf(f, "\n[Service]\n" "Type=oneshot\n" "RemainAfterExit=yes\n" "TimeoutSec=0\n" /* the binary handles timeouts anyway */ "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n" "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n", name, u, strempty(password), strempty(filtered), name); if (tmp) fprintf(f, "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n", name); if (swap) fprintf(f, "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n", name); r = fflush_and_check(f); if (r < 0) return log_error_errno(r, "Failed to write file %s: %m", p); from = strjoina("../", n); if (!noauto) { to = strjoin(arg_dest, "/", d, ".wants/", n, NULL); if (!to) return log_oom(); mkdir_parents_label(to, 0755); if (symlink(from, to) < 0) return log_error_errno(errno, "Failed to create symlink %s: %m", to); free(to); if (!nofail) to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL); else to = strjoin(arg_dest, "/cryptsetup.target.wants/", n, NULL); if (!to) return log_oom(); mkdir_parents_label(to, 0755); if (symlink(from, to) < 0) return log_error_errno(errno, "Failed to create symlink %s: %m", to); } free(to); to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL); if (!to) return log_oom(); mkdir_parents_label(to, 0755); if (symlink(from, to) < 0) return log_error_errno(errno, "Failed to create symlink %s: %m", to); if (!noauto && !nofail) { _cleanup_free_ char *dmname; dmname = strjoin("dev-mapper-", e, ".device", NULL); if (!dmname) return log_oom(); r = write_drop_in(arg_dest, dmname, 90, "device-timeout", "# Automatically generated by systemd-cryptsetup-generator \n\n" "[Unit]\nJobTimeoutSec=0"); if (r < 0) return log_error_errno(r, "Failed to write device drop-in: %m"); } return 0; }
static int add_cryptsetup(const char *id, const char *what, bool rw, char **device) { _cleanup_free_ char *e = NULL, *n = NULL, *p = NULL, *d = NULL, *to = NULL; _cleanup_fclose_ FILE *f = NULL; char *from, *ret; int r; assert(id); assert(what); assert(device); r = unit_name_from_path(what, ".device", &d); if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); e = unit_name_escape(id); if (!e) return log_oom(); r = unit_name_build("systemd-cryptsetup", e, ".service", &n); if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); p = strjoin(arg_dest, "/", n, NULL); if (!p) return log_oom(); f = fopen(p, "wxe"); if (!f) return log_error_errno(errno, "Failed to create unit file %s: %m", p); fprintf(f, "# Automatically generated by systemd-gpt-auto-generator\n\n" "[Unit]\n" "Description=Cryptography Setup for %%I\n" "Documentation=man:systemd-gpt-auto-generator(8) man:[email protected](8)\n" "DefaultDependencies=no\n" "Conflicts=umount.target\n" "BindsTo=dev-mapper-%%i.device %s\n" "Before=umount.target cryptsetup.target\n" "After=%s\n" "IgnoreOnIsolate=true\n" "[Service]\n" "Type=oneshot\n" "RemainAfterExit=yes\n" "TimeoutSec=0\n" /* the binary handles timeouts anyway */ "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '' '%s'\n" "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n", d, d, id, what, rw ? "" : "read-only", id); r = fflush_and_check(f); if (r < 0) return log_error_errno(r, "Failed to write file %s: %m", p); from = strjoina("../", n); to = strjoin(arg_dest, "/", d, ".wants/", n, NULL); if (!to) return log_oom(); mkdir_parents_label(to, 0755); if (symlink(from, to) < 0) return log_error_errno(errno, "Failed to create symlink %s: %m", to); free(to); to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL); if (!to) return log_oom(); mkdir_parents_label(to, 0755); if (symlink(from, to) < 0) return log_error_errno(errno, "Failed to create symlink %s: %m", to); free(to); to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL); if (!to) return log_oom(); mkdir_parents_label(to, 0755); if (symlink(from, to) < 0) return log_error_errno(errno, "Failed to create symlink %s: %m", to); free(p); p = strjoin(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf", NULL); if (!p) return log_oom(); mkdir_parents_label(p, 0755); r = write_string_file(p, "# Automatically generated by systemd-gpt-auto-generator\n\n" "[Unit]\n" "JobTimeoutSec=0\n", WRITE_STRING_FILE_CREATE); /* the binary handles timeouts anyway */ if (r < 0) return log_error_errno(r, "Failed to write device drop-in: %m"); ret = strappend("/dev/mapper/", id); if (!ret) return log_oom(); *device = ret; return 0; }
static opcode_status_ty spawn(string_list_ty *args, string_ty *input, int *pid_p, string_ty *host_binding, opcode_context_ty *ocp) { size_t j; int fd; string_ty *iname; int pid; opcode_status_ty status; static char **argv; static size_t argvlen; string_list_ty cmd; sub_context_ty *scp; char *shell; trace(("spawn()\n{\n")); assert(args); /* * build the input file, if required */ status = opcode_status_error; fd = -1; iname = 0; if (input) { /* * He has given a string to be used as input to the command, * so write it out to a file, and then redirect the input. */ iname = temporary_filename(); fd = open(iname->str_text, O_RDWR | O_CREAT | O_TRUNC, 0666); if (fd < 0) fatal_intl_open(iname->str_text); if (unlink(iname->str_text)) { error_intl_unlink(iname->str_text); goto done; } if ( write(fd, input->str_text, input->str_length) < 0 || lseek(fd, 0L, SEEK_SET) ) { error_intl_write(iname->str_text); goto done; } } /* * See if there is a host_binding in effect. */ if (host_binding) { static string_ty *key; string_list_ty *slp; string_ty *s; string_ty *rcmd; string_ty *rcmdq; string_ty *result_fn; FILE *result_fp; string_ty *cwd; string_ty *script_fn; FILE *script_fp; /* * Work out the name of the remote shell command. */ if (!key) key = str_from_c("parallel_rsh"); slp = id_var_search(ocp, key); if (slp) string_list_copy_constructor(&cmd, slp); else { static string_ty *rsh; if (!rsh) rsh = str_from_c(CONF_REMOTE_SHELL); string_list_constructor(&cmd); string_list_append(&cmd, rsh); } /* * Add the name of the host. */ string_list_append(&cmd, host_binding); /* * The remote end will need to change directory to where * we are now, since it is *highly* unlikely that we are * in our home directory. */ cwd = os_curdir(); /* do not str_free this */ /* * We are going to create a small shell script contining * the command to be executed. This deals with various * weird and obscure $SHELL settings, and it also deals * with command lines so long that rsh barfs. * * Yes, there is a potential race condition here. * Between writing the script and executing it, we * could be tricked into executing something else. * Especially if there is a symlink lying in wait. */ script_fn = dot_temporary_filename(); script_fp = fopen_and_check(script_fn->str_text, "w"); /* * what shell are we using */ shell = getenv("SHELL"); if (!shell || !*shell) shell = CONF_SHELL; fprintf(script_fp, "#!%s\n", shell); /* * Now write the actual command to the script file. */ rcmd = wl2str(args, 0, args->nstrings, (char *)0); fprintf(script_fp, "%s\n", rcmd->str_text); str_free(rcmd); fflush_and_check(script_fp, script_fn->str_text); fclose_and_check(script_fp, script_fn->str_text); chmod_and_check(script_fn->str_text, 0755); /* * This is the result file. It is where the remote * command will stash its exit status. We seed it with * failure in case the rsh fails altogether. * * And, yes, there is a race conditon and we could be * lied to if they are quick. */ result_fn = dot_temporary_filename(); result_fp = fopen_and_check(result_fn->str_text, "w"); fprintf(result_fp, "42\n"); fflush_and_check(result_fp, result_fn->str_text); fclose_and_check(result_fp, result_fn->str_text); /* * Because rsh(1) always returns an exit status of zero, * we have to make other arrangements to obtain the exit * status. * * We actually need to be a more devious, so that we can * get the exit status back. Write it to a temporary * file server side, then read and remove it client side. * * This is the server side command. It gets quoted * again to protect it from the client side shell. * * We use sh explicitly, because the shell at the other * end could be csh, tcsh, or something even stranger, * and we need to guarantee the command will execute * exactly the way we need. */ rcmd = str_format ( "sh -c 'cd %s && sh %s %s/%s; echo $? > %s/%s'", cwd->str_text, (option_test(OPTION_ERROK) ? "-c" : "-ce"), cwd->str_text, script_fn->str_text, cwd->str_text, result_fn->str_text ); rcmdq = str_quote_shell(rcmd); str_free(rcmd); string_list_append(&cmd, rcmdq); str_free(rcmdq); /* * This is the rest of the server side command. * * On some systems this gives ``Text file busy'' errors, * as if the script file has yet to be released by * the kernel. Since we *aren't* executing it when the * RM command runs, I have no idea how to avoid this * (other than ignoring it with -f). Maybe this is some * kind of NFS latency? */ s = str_format ( "&& exit `cat %s;rm -f %s %s`", result_fn->str_text, result_fn->str_text, script_fn->str_text ); string_list_append(&cmd, s); str_free(s); str_free(result_fn); str_free(script_fn); /* * Because the command has a semicolon and back quotes, * we need to hand it to a shell (as alluded to above). * Assemble into a single string. */ rcmd = wl2str(&cmd, 0, cmd.nstrings, (char *)0); string_list_destructor(&cmd); /* * Build the final command to be executed. * It even cleans up after itself. */ s = str_from_c("sh"); string_list_append(&cmd, s); str_free(s); s = str_from_c("-c"); string_list_append(&cmd, s); str_free(s); string_list_append(&cmd, rcmd); str_free(rcmd); } else { /* * build the command */ if (os_execute_magic_characters_list(args)) { string_ty *str; /* * what shell are we using */ shell = getenv("SHELL"); if (!shell || !*shell) shell = CONF_SHELL; string_list_constructor(&cmd); str = str_from_c(shell); string_list_append(&cmd, str); str_free(str); if (option_test(OPTION_ERROK)) str = str_from_c("-c"); else str = str_from_c("-ce"); string_list_append(&cmd, str); str_free(str); str = wl2str(args, 0, args->nstrings - 1, (char *)0); string_list_append(&cmd, str); str_free(str); } else string_list_copy_constructor(&cmd, args); } /* * build the argv array */ if (!argv) { argvlen = cmd.nstrings + 1; argv = mem_alloc(argvlen * sizeof(char *)); } else if (argvlen < cmd.nstrings + 1) { argvlen = cmd.nstrings + 1; argv = mem_change_size(argv, argvlen * sizeof(char *)); } if (cmd.nstrings == 0) { string_list_destructor(&cmd); status = opcode_status_success; goto done; } for (j = 0; j < cmd.nstrings; ++j) argv[j] = cmd.string[j]->str_text; argv[cmd.nstrings] = 0; /* * spawn the child process */ switch (pid = fork()) { case -1: scp = sub_context_new(); sub_errno_set(scp); error_intl(scp, i18n("fork(): $errno")); sub_context_delete(scp); break; case 0: /* * child */ if (fd >= 0) { if (close(0) && errno != EBADF) { string_ty *fn0; int err; err = errno; scp = sub_context_new(); fn0 = subst_intl(scp, "standard input"); /* re-use substitution context */ sub_errno_setx(scp, err); sub_var_set_string(scp, "File_Name", fn0); fatal_intl ( scp, i18n("close $filename: $errno") ); /* NOTREACHED */ sub_context_delete(scp); str_free(fn0); } if (dup(fd) < 0) { scp = sub_context_new(); sub_errno_set(scp); fatal_intl(scp, i18n("dup(): $errno")); /* NOTREACHED */ sub_context_delete(scp); } close(fd); } if (argv[0][0] == '/') execv(argv[0], argv); else execvp(argv[0], argv); scp = sub_context_new(); sub_errno_set(scp); sub_var_set_charstar(scp, "File_Name", argv[0]); fatal_intl(scp, i18n("exec $filename: $errno")); /* NOTREACHED */ sub_context_delete(scp); default: /* * parent */ if (fd >= 0) { close(fd); fd = -1; } string_list_destructor(&cmd); status = opcode_status_wait; trace(("pid = %d;\n", pid)); *pid_p = pid; break; } done: if (fd >= 0) close(fd); if (iname) str_free(iname); trace(("return %s;\n", opcode_status_name(status))); trace(("}\n")); return status; }