From 151b190e79e64824552e01849352ca8f6ac7dedb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 4 Apr 2011 03:36:42 +0200 Subject: [PATCH] binfmt: add binfmt tool to set up binfmt_misc at boot --- .gitignore | 1 + Makefile.am | 22 +++- man/binfmt.d.xml | 103 ++++++++++++++++++ src/binfmt.c | 187 ++++++++++++++++++++++++++++++++ src/util.c | 11 +- units/.gitignore | 1 + units/systemd-binfmt.service.in | 19 ++++ 7 files changed, 340 insertions(+), 4 deletions(-) create mode 100644 man/binfmt.d.xml create mode 100644 src/binfmt.c create mode 100644 units/systemd-binfmt.service.in diff --git a/.gitignore b/.gitignore index 7213a8ba..be44cb60 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-binfmt systemd-getty-generator systemd-nspawn systemd-stdio-bridge diff --git a/Makefile.am b/Makefile.am index 7bf32aa8..4fd3e625 100644 --- a/Makefile.am +++ b/Makefile.am @@ -144,7 +144,8 @@ rootlibexec_PROGRAMS = \ systemd-timestamp \ systemd-ac-power \ systemd-detect-virt \ - systemd-sysctl + systemd-sysctl \ + systemd-binfmt systemgenerator_PROGRAMS = \ systemd-getty-generator @@ -300,6 +301,7 @@ nodist_systemunit_DATA = \ units/systemd-ask-password-wall.service \ units/systemd-ask-password-console.service \ units/systemd-sysctl.service \ + units/systemd-binfmt.service \ units/halt.service \ units/poweroff.service \ units/reboot.service \ @@ -343,6 +345,7 @@ EXTRA_DIST = \ units/systemd-ask-password-wall.service.in \ units/systemd-ask-password-console.service.in \ units/systemd-sysctl.service.in \ + units/systemd-binfmt.service.in \ units/halt.service.in \ units/poweroff.service.in \ units/reboot.service.in \ @@ -569,7 +572,8 @@ MANPAGES = \ man/vconsole.conf.5 \ man/locale.conf.5 \ man/os-release.5 \ - man/modules-load.d.5 + man/modules-load.d.5 \ + man/binfmt.d.5 MANPAGES_ALIAS = \ man/reboot.8 \ @@ -799,6 +803,15 @@ systemd_sysctl_CFLAGS = \ systemd_sysctl_LDADD = \ libsystemd-basic.la +systemd_binfmt_SOURCES = \ + src/binfmt.c + +systemd_binfmt_CFLAGS = \ + $(AM_CFLAGS) + +systemd_binfmt_LDADD = \ + libsystemd-basic.la + systemd_fsck_SOURCES = \ src/fsck.c \ src/dbus-common.c @@ -1240,9 +1253,10 @@ CLEANFILES += \ install-data-hook: $(MKDIR_P) -m 0755 \ - $(DESTDIR)$(sysconfdir)/modules-load.d \ $(DESTDIR)$(tmpfilesdir) \ + $(DESTDIR)$(sysconfdir)/modules-load.d \ $(DESTDIR)$(sysconfdir)/sysctl.d \ + $(DESTDIR)$(sysconfdir)/binfmt.d \ $(DESTDIR)$(systemshutdowndir) \ $(DESTDIR)$(systemgeneratordir) \ $(DESTDIR)$(usergeneratordir) @@ -1364,6 +1378,7 @@ install-data-hook: systemd-random-seed-load.service \ systemd-tmpfiles-setup.service \ systemd-sysctl.service \ + systemd-binfmt.service \ systemd-ask-password-console.path \ systemd-kmsg-syslogd.service \ cryptsetup.target && \ @@ -1377,6 +1392,7 @@ install-data-hook: $(LN_S) ../systemd-random-seed-load.service systemd-random-seed-load.service && \ $(LN_S) ../systemd-tmpfiles-setup.service systemd-tmpfiles-setup.service && \ $(LN_S) ../systemd-sysctl.service systemd-sysctl.service && \ + $(LN_S) ../systemd-binfmt.service systemd-binfmt.service && \ $(LN_S) ../systemd-ask-password-console.path systemd-ask-password-console.path && \ $(LN_S) ../systemd-kmsg-syslogd.service && \ $(LN_S) ../cryptsetup.target cryptsetup.target ) diff --git a/man/binfmt.d.xml b/man/binfmt.d.xml new file mode 100644 index 00000000..d2a0d793 --- /dev/null +++ b/man/binfmt.d.xml @@ -0,0 +1,103 @@ + + + + + + + + binfmt.d + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + binfmt.d + 5 + + + + binfmt.d + Configure additional binary formats at boot + + + + /etc/binfmt.d/*.conf + + + + Description + + systemd uses + /etc/binfmt.d/ to configure + additional binary formats to register during boot in + the kernel. Each configuration file is named in the + style of + /etc/binfmt.d/<program>.conf. + + + + + + Configuration Format + + Each file contains a list of binfmt_misc kernel + binary format rules. Consult binfmt_misc.txt + for more information on registration of additional + binary formats and how to write rules. + + Empty lines and lines beginning with ; and # are + ignored. Note that this means you may not use ; and # + as delimiter in binary format rules. + + Configuration files are loaded in alphabetical + order. To ensure that a specific rule takes precedence + over another place it in a file with an alphabetically + later name. + + + + + Example + + /etc/binfmt.d/wine.conf example: + + # Start WINE on Windows executables +:DOSWin:M::MZ::/usr/bin/wine: + + + + + See Also + + systemd1, + wine8 + + + + diff --git a/src/binfmt.c b/src/binfmt.c new file mode 100644 index 00000000..6ebd212e --- /dev/null +++ b/src/binfmt.c @@ -0,0 +1,187 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "util.h" + +static int delete_rule(const char *rule) { + char *x, *fn, *e; + int r; + + assert(rule[0]); + + if (!(x = strdup(rule))) + return -ENOMEM; + + e = strchrnul(x+1, x[0]); + *e = 0; + + asprintf(&fn, "/proc/sys/fs/binfmt_misc/%s", x+1); + free(x); + + if (!fn) + return -ENOMEM; + + r = write_one_line_file(fn, "-1"); + free(fn); + + return r; +} + +static int apply_rule(const char *rule) { + int r; + + delete_rule(rule); + + if ((r = write_one_line_file("/proc/sys/fs/binfmt_misc/register", rule)) < 0) { + log_error("Failed to add binary format: %s", strerror(-r)); + return r; + } + + return 0; +} + +static int apply_file(const char *path, bool ignore_enoent) { + FILE *f; + int r = 0; + + assert(path); + + if (!(f = fopen(path, "re"))) { + if (ignore_enoent && errno == ENOENT) + return 0; + + log_error("Failed to open file '%s', ignoring: %m", path); + return -errno; + } + + while (!feof(f)) { + char l[LINE_MAX], *p; + int k; + + if (!fgets(l, sizeof(l), f)) { + if (feof(f)) + break; + + log_error("Failed to read file '%s', ignoring: %m", path); + r = -errno; + goto finish; + } + + p = strstrip(l); + + if (!*p) + continue; + + if (strchr(COMMENTS, *p)) + continue; + + if ((k = apply_rule(p)) < 0 && r == 0) + r = k; + } + +finish: + fclose(f); + + return r; +} + +static int scandir_filter(const struct dirent *d) { + assert(d); + + if (ignore_file(d->d_name)) + return 0; + + if (d->d_type != DT_REG && + d->d_type != DT_LNK && + d->d_type != DT_UNKNOWN) + return 0; + + return endswith(d->d_name, ".conf"); +} + +static int apply_tree(const char *path) { + struct dirent **de = NULL; + int n, i, r = 0; + + if ((n = scandir(path, &de, scandir_filter, alphasort)) < 0) { + + if (errno == ENOENT) + return 0; + + log_error("Failed to enumerate %s files: %m", path); + return -errno; + } + + for (i = 0; i < n; i++) { + char *fn; + int k; + + k = asprintf(&fn, "%s/%s", path, de[i]->d_name); + free(de[i]); + + if (k < 0) { + log_error("Failed to allocate file name."); + + if (r == 0) + r = -ENOMEM; + continue; + } + + if ((k = apply_file(fn, true)) < 0 && r == 0) + r = k; + } + + free(de); + + return r; +} + +int main(int argc, char *argv[]) { + int r = 0; + + if (argc > 2) { + log_error("This program expects one or no arguments."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + if (argc > 1) + r = apply_file(argv[1], false); + else { + /* Flush out all rules */ + write_one_line_file("/proc/sys/fs/binfmt_misc/status", "-1"); + + r = apply_tree("/etc/binfmt.d"); + } + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/util.c b/src/util.c index a1686ed3..5daafdf7 100644 --- a/src/util.c +++ b/src/util.c @@ -513,7 +513,16 @@ int write_one_line_file(const char *fn, const char *line) { if (!endswith(line, "\n")) fputc('\n', f); - r = 0; + fflush(f); + + if (ferror(f)) { + if (errno != 0) + r = -errno; + else + r = -EIO; + } else + r = 0; + finish: fclose(f); return r; diff --git a/units/.gitignore b/units/.gitignore index 41ab51b2..cbf9f255 100644 --- a/units/.gitignore +++ b/units/.gitignore @@ -34,3 +34,4 @@ remote-fs.target systemd-update-utmp-runlevel.service systemd-update-utmp-shutdown.service test-env-replace +systemd-binfmt.service diff --git a/units/systemd-binfmt.service.in b/units/systemd-binfmt.service.in new file mode 100644 index 00000000..0bf6df20 --- /dev/null +++ b/units/systemd-binfmt.service.in @@ -0,0 +1,19 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +[Unit] +Description=Set Up Additional Binary Formats +DefaultDependencies=no +Conflicts=shutdown.target +After=systemd-readahead-collect.service systemd-readahead-replay.service proc-sys-fs-binfmt_misc.automount +Before=sysinit.target shutdown.target +ConditionDirectoryNotEmpty=/etc/binfmt.d + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-binfmt -- 2.39.5