Discussion:
[systemd-devel] [PATCH 1/2] Adding unmount functions to be used in shutdown
f***@profusion.mobi
2010-09-27 20:24:53 UTC
Permalink
From: Fabiano Fidencio <***@profusion.mobi>

This functions will unmount all filesystem that aren't api
(and those which fail will be remounted read-only).
Filesystems are being read from /proc/self/mountinfo.
---
Makefile.am | 1 +
src/umount.c | 183 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/umount.h | 27 +++++++++
3 files changed, 211 insertions(+), 0 deletions(-)
create mode 100644 src/umount.c
create mode 100644 src/umount.h

diff --git a/Makefile.am b/Makefile.am
index 70a6c19..b1c0438 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -337,6 +337,7 @@ libsystemd_core_la_SOURCES = \
src/service.c \
src/automount.c \
src/mount.c \
+ src/umount.c \
src/swap.c \
src/device.c \
src/target.c \
diff --git a/src/umount.c b/src/umount.c
new file mode 100644
index 0000000..8f8ca10
--- /dev/null
+++ b/src/umount.c
@@ -0,0 +1,183 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 ProFUSION embedded systems
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/mount.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "list.h"
+#include "log.h"
+#include "mount-setup.h"
+#include "umount.h"
+#include "util.h"
+
+typedef struct MountPoint {
+ char *path;
+ bool read_only;
+ LIST_FIELDS (struct MountPoint, mount_point);
+} MountPoint;
+
+static MountPoint *mount_point_alloc(char *path) {
+ MountPoint *mp;
+
+ mp = new(MountPoint, 1);
+ mp->path = path;
+ mp->read_only = false;
+
+ return mp;
+}
+
+static void mount_point_remove_and_free(MountPoint *mp, MountPoint **mp_list_head) {
+ LIST_REMOVE(MountPoint, mount_point, *mp_list_head, mp);
+
+ free(mp->path);
+ free(mp);
+}
+
+static void mount_points_list_free(MountPoint **mp_list_head) {
+ MountPoint *mp, *mp_next;
+ LIST_FOREACH_SAFE(mount_point, mp, mp_next, *mp_list_head)
+ mount_point_remove_and_free(mp, mp_list_head);
+}
+
+static int mount_points_list_get(MountPoint **mp_list_head) {
+ FILE *proc_self_mountinfo;
+ char *path, *p;
+ int r;
+
+ if (!(proc_self_mountinfo = fopen("/proc/self/mountinfo", "re")))
+ return -errno;
+
+ for (;;) {
+ int k;
+ MountPoint *mp;
+
+ path = p = NULL;
+
+ if ((k = fscanf(proc_self_mountinfo,
+ "%*s " /* (1) mount id */
+ "%*s " /* (2) parent id */
+ "%*s " /* (3) major:minor */
+ "%*s " /* (4) root */
+ "%ms " /* (5) mount point */
+ "%*s" /* (6) mount options */
+ "%*[^-]" /* (7) optional fields */
+ "- " /* (8) seperator */
+ "%*s " /* (9) file system type */
+ "%*s" /* (10) mount source */
+ "%*s" /* (11) mount options 2 */
+ "%*[^\n]", /* some rubbish at the end */
+ &path)) != 1) {
+ if (k == EOF)
+ break;
+
+ r = -EBADMSG;
+ goto finish;
+ }
+
+ if (mount_point_is_api(path))
+ goto free;
+
+ if (!(p = cunescape(path))) {
+ r = -ENOMEM;
+ free(p);
+ goto finish;
+ }
+
+ mp = mount_point_alloc(p);
+ LIST_PREPEND(MountPoint, mount_point, *mp_list_head, mp);
+free:
+ free(path);
+ }
+
+ r = 0;
+
+finish:
+ fclose(proc_self_mountinfo);
+
+ free(path);
+
+ return r;
+}
+
+static void mount_points_list_umount(MountPoint **mp_list_head, bool *changed) {
+ MountPoint *mp, *mp_next;
+
+ LIST_FOREACH_SAFE(mount_point, mp, mp_next, *mp_list_head) {
+ if (!(strcmp(mp->path, "/")))
+ continue;
+
+ /* Trying to umount. Forcing to umount if busy (only for NFS mounts) */
+ if (umount2(mp->path, MNT_FORCE) == 0) {
+ if (!(*changed))
+ *changed = true;
+ mount_point_remove_and_free(mp, mp_list_head);
+ log_debug("umount: \"%s\" was umounted succesfully\n", mp->path);
+
+ } else
+ log_debug("umount: forced umount of \"%s\" failed!\n", mp->path);
+
+ }
+}
+
+static void mount_points_list_remount_read_only(MountPoint **mp_list_head, bool *changed) {
+ MountPoint *mp, *mp_next;
+
+ LIST_FOREACH_SAFE(mount_point, mp, mp_next, *mp_list_head) {
+ if (mp->read_only)
+ continue;
+
+ /* Trying to remount read-only */
+ if (mount(NULL, mp->path, NULL, MS_MGC_VAL|MS_REMOUNT|MS_RDONLY, NULL) == 0) {
+ mp->read_only = true;
+ if (!(*changed))
+ *changed = true;
+ log_debug("umount: \"%s\" busy - remounted read-only\n", mp->path);
+ } else {
+ log_error("Cannot remount \"%s\" read-only\n", mp->path);
+ sleep(1);
+ }
+ }
+}
+
+void umount_init(void) {
+ LIST_HEAD(MountPoint, mp_list_head);
+ int r;
+ bool changed;
+
+ LIST_HEAD_INIT(MountPoint, mp_list_head);
+ if ((r = mount_points_list_get(&mp_list_head)) < 0)
+ log_debug("An error [%d] ocurred getting mount point's list - "
+ "umounting the mount points that are possible", r);
+
+ do {
+ changed = false;
+ mount_points_list_umount(&mp_list_head, &changed);
+ } while (!changed);
+
+ do {
+ changed = false;
+ mount_points_list_remount_read_only(&mp_list_head, &changed);
+ } while (!changed);
+
+ mount_points_list_free(&mp_list_head);
+}
diff --git a/src/umount.h b/src/umount.h
new file mode 100644
index 0000000..6a69e67
--- /dev/null
+++ b/src/umount.h
@@ -0,0 +1,27 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooumounthfoo
+#define fooumounthfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 ProFUSION embedded systems
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+void umount_init(void);
+
+#endif
--
1.7.3
f***@profusion.mobi
2010-09-27 20:24:54 UTC
Permalink
From: Fabiano Fidencio <***@profusion.mobi>

Just removing a parenthesis
---
src/list.h | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/src/list.h b/src/list.h
index 9380f36..e49d953 100644
--- a/src/list.h
+++ b/src/list.h
@@ -77,7 +77,7 @@
do { \
t *_item = (item); \
assert(_item); \
- while ((_item->name##_prev) \
+ while (_item->name##_prev) \
_item = _item->name##_prev; \
(head) = _item; \
} while (false)
--
1.7.3
Fabiano Fidencio
2010-09-27 21:34:55 UTC
Permalink
This functions will unmount all filesystem that aren't api
(and those which fail will be remounted read-only).
Filesystems are being read from /proc/self/mountinfo.
---
Makefile.am | 1 +
src/umount.c | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/umount.h | 27 +++++++++
3 files changed, 212 insertions(+), 0 deletions(-)
create mode 100644 src/umount.c
create mode 100644 src/umount.h

diff --git a/Makefile.am b/Makefile.am
index 70a6c19..b1c0438 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -337,6 +337,7 @@ libsystemd_core_la_SOURCES = \
src/service.c \
src/automount.c \
src/mount.c \
+ src/umount.c \
src/swap.c \
src/device.c \
src/target.c \
diff --git a/src/umount.c b/src/umount.c
new file mode 100644
index 0000000..f38113e
--- /dev/null
+++ b/src/umount.c
@@ -0,0 +1,184 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 ProFUSION embedded systems
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/mount.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "list.h"
+#include "log.h"
+#include "mount-setup.h"
+#include "umount.h"
+#include "util.h"
+
+typedef struct MountPoint {
+ char *path;
+ bool read_only;
+ LIST_FIELDS (struct MountPoint, mount_point);
+} MountPoint;
+
+static MountPoint *mount_point_alloc(char *path) {
+ MountPoint *mp;
+
+ mp = new(MountPoint, 1);
+ mp->path = path;
+ mp->read_only = false;
+
+ return mp;
+}
+
+static void mount_point_remove_and_free(MountPoint *mp, MountPoint **mp_list_head) {
+ LIST_REMOVE(MountPoint, mount_point, *mp_list_head, mp);
+
+ free(mp->path);
+ free(mp);
+}
+
+static void mount_points_list_free(MountPoint **mp_list_head) {
+ MountPoint *mp, *mp_next;
+ LIST_FOREACH_SAFE(mount_point, mp, mp_next, *mp_list_head)
+ mount_point_remove_and_free(mp, mp_list_head);
+}
+
+static int mount_points_list_get(MountPoint **mp_list_head) {
+ FILE *proc_self_mountinfo;
+ char *path, *p;
+ int r;
+
+ if (!(proc_self_mountinfo = fopen("/proc/self/mountinfo", "re")))
+ return -errno;
+
+ for (;;) {
+ int k;
+ MountPoint *mp;
+
+ path = p = NULL;
+
+ if ((k = fscanf(proc_self_mountinfo,
+ "%*s " /* (1) mount id */
+ "%*s " /* (2) parent id */
+ "%*s " /* (3) major:minor */
+ "%*s " /* (4) root */
+ "%ms " /* (5) mount point */
+ "%*s" /* (6) mount options */
+ "%*[^-]" /* (7) optional fields */
+ "- " /* (8) seperator */
+ "%*s " /* (9) file system type */
+ "%*s" /* (10) mount source */
+ "%*s" /* (11) mount options 2 */
+ "%*[^\n]", /* some rubbish at the end */
+ &path)) != 1) {
+ if (k == EOF)
+ break;
+
+ r = -EBADMSG;
+ goto finish;
+ }
+
+ if (mount_point_is_api(path))
+ goto free;
+
+ if (!(p = cunescape(path))) {
+ r = -ENOMEM;
+ free(p);
+ goto finish;
+ }
+
+ mp = mount_point_alloc(p);
+ LIST_PREPEND(MountPoint, mount_point, *mp_list_head, mp);
+free:
+ free(path);
+ }
+
+ r = 0;
+
+finish:
+ fclose(proc_self_mountinfo);
+
+ free(path);
+
+ return r;
+}
+
+static void mount_points_list_umount(MountPoint **mp_list_head, bool *changed) {
+ MountPoint *mp, *mp_next;
+
+ LIST_FOREACH_SAFE(mount_point, mp, mp_next, *mp_list_head) {
+ if (!(strcmp(mp->path, "/")))
+ continue;
+
+ /* Trying to umount. Forcing to umount if busy (only for NFS mounts) */
+ if (umount2(mp->path, MNT_FORCE) == 0) {
+ if (!(*changed))
+ *changed = true;
+ mount_point_remove_and_free(mp, mp_list_head);
+ log_debug("umount: \"%s\" was umounted succesfully\n", mp->path);
+
+ } else
+ log_debug("umount: forced umount of \"%s\" failed!\n", mp->path);
+
+ }
+}
+
+static void mount_points_list_remount_read_only(MountPoint **mp_list_head, bool *changed) {
+ MountPoint *mp, *mp_next;
+
+ LIST_FOREACH_SAFE(mount_point, mp, mp_next, *mp_list_head) {
+ if (mp->read_only)
+ continue;
+
+ /* Trying to remount read-only */
+ if (mount(NULL, mp->path, NULL, MS_MGC_VAL|MS_REMOUNT|MS_RDONLY, NULL) == 0) {
+ mp->read_only = true;
+ if (!(*changed))
+ *changed = true;
+ mount_point_remove_and_free(mp, mp_list_head);
+ log_debug("umount: \"%s\" busy - remounted read-only\n", mp->path);
+ } else {
+ log_error("Cannot remount \"%s\" read-only\n", mp->path);
+ sleep(1);
+ }
+ }
+}
+
+void umount_init(void) {
+ LIST_HEAD(MountPoint, mp_list_head);
+ int r;
+ bool changed;
+
+ LIST_HEAD_INIT(MountPoint, mp_list_head);
+ if ((r = mount_points_list_get(&mp_list_head)) < 0)
+ log_debug("An error [%d] ocurred getting mount point's list - "
+ "umounting the mount points that are possible", r);
+
+ do {
+ changed = false;
+ mount_points_list_umount(&mp_list_head, &changed);
+ } while (!changed);
+
+ do {
+ changed = false;
+ mount_points_list_remount_read_only(&mp_list_head, &changed);
+ } while (!changed);
+
+ mount_points_list_free(&mp_list_head);
+}
diff --git a/src/umount.h b/src/umount.h
new file mode 100644
index 0000000..6a69e67
--- /dev/null
+++ b/src/umount.h
@@ -0,0 +1,27 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooumounthfoo
+#define fooumounthfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 ProFUSION embedded systems
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+void umount_init(void);
+
+#endif
--
1.7.3
Fabiano Fidencio
2010-09-27 21:34:56 UTC
Permalink
Just removing a parenthesis
---
src/list.h | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/src/list.h b/src/list.h
index 9380f36..e49d953 100644
--- a/src/list.h
+++ b/src/list.h
@@ -77,7 +77,7 @@
do { \
t *_item = (item); \
assert(_item); \
- while ((_item->name##_prev) \
+ while (_item->name##_prev) \
_item = _item->name##_prev; \
(head) = _item; \
} while (false)
--
1.7.3
Lennart Poettering
2010-09-28 19:57:51 UTC
Permalink
Post by f***@profusion.mobi
Just removing a parenthesis
Applied! Thanks!

Lennart
--
Lennart Poettering - Red Hat, Inc.
Lennart Poettering
2010-09-28 19:56:38 UTC
Permalink
Post by f***@profusion.mobi
+
+static void mount_points_list_free(MountPoint **mp_list_head) {
+ MountPoint *mp, *mp_next;
+ LIST_FOREACH_SAFE(mount_point, mp, mp_next, *mp_list_head)
+ mount_point_remove_and_free(mp, mp_list_head);
+}
I'd just do

while (*list)
mount_point_remove_and_free(*list, *list)

Looks more readable to me.
Post by f***@profusion.mobi
+ if ((k = fscanf(proc_self_mountinfo,
+ "%*s " /* (1) mount id */
+ "%*s " /* (2) parent id */
+ "%*s " /* (3) major:minor */
+ "%*s " /* (4) root */
+ "%ms " /* (5) mount point */
+ "%*s" /* (6) mount options */
+ "%*[^-]" /* (7) optional fields */
+ "- " /* (8) seperator */
+ "%*s " /* (9) file system type */
+ "%*s" /* (10) mount source */
+ "%*s" /* (11) mount options 2 */
+ "%*[^\n]", /* some rubbish at the end */
+ &path)) != 1) {
+ if (k == EOF)
+ break;
+
+ r = -EBADMSG;
+ goto finish;
+ }
Thinking about this we probably should just go on with the next line
when we fail to parse something, maybe with a warning. I figure mount.c
exits here too, but I guess we should fix that.
Post by f***@profusion.mobi
+
+ if (mount_point_is_api(path))
+ goto free;
+
+ if (!(p = cunescape(path))) {
+ r = -ENOMEM;
+ free(p);
+ goto finish;
+ }
+
+ mp = mount_point_alloc(p);
You need to check for OOM here!
Post by f***@profusion.mobi
+
+ LIST_FOREACH_SAFE(mount_point, mp, mp_next, *mp_list_head) {
+ if (!(strcmp(mp->path, "/")))
+ continue;
We have this little macro streq() which is a bit nicer to read.
Post by f***@profusion.mobi
+
+ /* Trying to umount. Forcing to umount if busy (only for NFS mounts) */
+ if (umount2(mp->path, MNT_FORCE) == 0) {
+ if (!(*changed))
+ *changed = true;
Hmm, why do you test *changed first?
Post by f***@profusion.mobi
+ mount_point_remove_and_free(mp, mp_list_head);
+ log_debug("umount: \"%s\" was umounted succesfully\n", mp->path);
+
+ } else
+ log_debug("umount: forced umount of \"%s\" failed!\n", mp->path);
Please output error message here, and do so as log_warn(). Use %m.
Post by f***@profusion.mobi
+ if (mount(NULL, mp->path, NULL, MS_MGC_VAL|MS_REMOUNT|MS_RDONLY, NULL) == 0) {
+ mp->read_only = true;
+ if (!(*changed))
+ *changed = true;
+ mount_point_remove_and_free(mp, mp_list_head);
+ log_debug("umount: \"%s\" busy - remounted read-only\n", mp->path);
+ } else {
+ log_error("Cannot remount \"%s\" read-only\n", mp->path);
+ sleep(1);
Similar here.

Otherwise looks fine!

Lennart
--
Lennart Poettering - Red Hat, Inc.
f***@profusion.mobi
2010-09-30 21:10:47 UTC
Permalink
From: Fabiano Fidencio <***@profusion.mobi>

This functions are working as follows:
- Send a SIGTERM to all process
- Send a SIGKILL to all process
- Try to umount all mount points
- Try to remount read-only all mount points that can't
be umounted
- Call shutdown

If one step fail, shutdown will be aborted
---
Makefile.am | 14 ++++++++++-
src/halt.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 89 insertions(+), 1 deletions(-)
create mode 100644 src/halt.c

diff --git a/Makefile.am b/Makefile.am
index 06b2cca..4d8e181 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -89,6 +89,7 @@ rootlibexec_PROGRAMS = \
systemd-update-utmp \
systemd-random-seed \
systemd-shutdownd \
+ systemd-halt \
systemd-modules-load \
systemd-remount-api-vfs \
systemd-kmsg-syslogd \
@@ -349,7 +350,7 @@ libsystemd_core_la_SOURCES = \
src/service.c \
src/automount.c \
src/mount.c \
- src/umount.c \
+ src/halt.c \
src/swap.c \
src/device.c \
src/target.c \
@@ -629,6 +630,17 @@ systemd_shutdownd_CFLAGS = \
systemd_shutdownd_LDADD = \
libsystemd-basic.la

+systemd_halt_SOURCES = \
+ src/mount-setup.c \
+ src/umount.c \
+ src/halt.c
+
+systemd_halt_CFLAGS = \
+ $(AM_CFLAGS)
+
+systemd_halt_LDADD = \
+ libsystemd-basic.la
+
systemd_modules_load_SOURCES = \
src/modules-load.c

diff --git a/src/halt.c b/src/halt.c
new file mode 100644
index 0000000..cce5dee
--- /dev/null
+++ b/src/halt.c
@@ -0,0 +1,76 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 ProFUSION embedded systems
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/reboot.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "umount.h"
+
+static volatile unsigned int run = 1;
+
+static void sig_alarm(int sig) {
+ run = 0;
+}
+
+int main(int argc, char *argv[]) {
+ int c, cmd;
+
+ if (getppid() != 1)
+ return -1;
+
+ if (argc != 2)
+ return -1;
+
+ switch ((c = getopt(argc, argv, "hr"))) {
+ case 'h':
+ cmd = RB_POWER_OFF;
+ break;
+ case 'r':
+ cmd = RB_AUTOBOOT;
+ break;
+ default:
+ return -1;
+ }
+
+ /* lock us into memory */
+ mlockall(MCL_CURRENT|MCL_FUTURE);
+
+ signal(SIGALRM, sig_alarm);
+ kill(-1, SIGTERM);
+ alarm(10);
+ while (run) {
+ pid_t r = wait(NULL);
+ if (r == (pid_t)-1 && errno == ECHILD)
+ run = 0;
+ }
+ alarm(0);
+ kill(-1, SIGKILL);
+
+ if (umount_init() < 0)
+ return -1;
+
+ return reboot(cmd);
+}
--
1.7.3
Gustavo Sverzut Barbieri
2010-10-01 03:58:17 UTC
Permalink
   - Send a SIGTERM to all process
   - Send a SIGKILL to all process
   - Try to umount all mount points
   - Try to remount read-only all mount points that can't
   be umounted
   - Call shutdown
   If one step fail, shutdown will be aborted
Fidencio just tested this standalone, and I went into a crashed
systemd setup that made me unable to fix it, but maybe we need to
reset signal handlers to not suffer from kill(-1, SIGXYZ).

Anyway, if you have some ideas about the design, other than minor
details to make it work being execv() from main.c if (getpid() == 1)
(in the place of freeze()).
--
Gustavo Sverzut Barbieri
http://profusion.mobi embedded systems
--------------------------------------
MSN: ***@gmail.com
Skype: gsbarbieri
Mobile: +55 (19) 9225-2202
f***@profusion.mobi
2010-09-30 21:10:48 UTC
Permalink
From: Fabiano Fidencio <***@profusion.mobi>

---
src/mount.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/src/mount.c b/src/mount.c
index 2fb5822..52743db 100644
--- a/src/mount.c
+++ b/src/mount.c
@@ -1420,7 +1420,7 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
"%ms " /* (5) mount point */
"%ms" /* (6) mount options */
"%*[^-]" /* (7) optional fields */
- "- " /* (8) seperator */
+ "- " /* (8) separator */
"%ms " /* (9) file system type */
"%ms" /* (10) mount source */
"%ms" /* (11) mount options 2 */
--
1.7.3
Lennart Poettering
2010-10-05 20:28:50 UTC
Permalink
Post by f***@profusion.mobi
---
src/mount.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/src/mount.c b/src/mount.c
index 2fb5822..52743db 100644
--- a/src/mount.c
+++ b/src/mount.c
@@ -1420,7 +1420,7 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
"%ms " /* (5) mount point */
"%ms" /* (6) mount options */
"%*[^-]" /* (7) optional fields */
- "- " /* (8) seperator */
+ "- " /* (8) separator */
"%ms " /* (9) file system type */
"%ms" /* (10) mount source */
"%ms" /* (11) mount options 2 */
Already fixed by Daves's patch I merged a few minutes ago.

Thanks,

Lennart
--
Lennart Poettering - Red Hat, Inc.
f***@profusion.mobi
2010-09-30 21:10:45 UTC
Permalink
From: Fabiano Fidêncio <***@gmail.com>

The current behavior of the /proc/self/mountinfo's parser
is stop the parser if the parser fail.
I'm only changing this behavior to try the next line and adding
a warning if occurs a fail.
---
src/mount.c | 9 +++++----
1 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/src/mount.c b/src/mount.c
index 3c08baf..2fb5822 100644
--- a/src/mount.c
+++ b/src/mount.c
@@ -1400,14 +1400,14 @@ finish:
}

static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
- int r;
+ int r, i;
char *device, *path, *options, *options2, *fstype, *d, *p, *o;

assert(m);

rewind(m->proc_self_mountinfo);

- for (;;) {
+ for (i =1;;i++) {
int k;

device = path = options = options2 = fstype = d = p = o = NULL;
@@ -1434,8 +1434,8 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
if (k == EOF)
break;

- r = -EBADMSG;
- goto finish;
+ log_warning("Line %d of /proc/self/mountinfo cannot be read", i);
+ goto clean_up;
}

if (asprintf(&o, "%s,%s", options, options2) < 0) {
@@ -1452,6 +1452,7 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
if ((r = mount_add_one(m, d, p, o, fstype, true, set_flags)) < 0)
goto finish;

+clean_up:
free(device);
free(path);
free(options);
--
1.7.3
f***@profusion.mobi
2010-09-30 21:10:46 UTC
Permalink
From: Fabiano Fidencio <***@profusion.mobi>

This functions will unmount all filesystem that aren't api
(and those which fail will be remounted read-only).
Filesystems are being read from /proc/self/mountinfo.
---
Makefile.am | 1 +
src/umount.c | 183 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/umount.h | 27 +++++++++
3 files changed, 211 insertions(+), 0 deletions(-)
create mode 100644 src/umount.c
create mode 100644 src/umount.h

diff --git a/Makefile.am b/Makefile.am
index 0a33c06..06b2cca 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -349,6 +349,7 @@ libsystemd_core_la_SOURCES = \
src/service.c \
src/automount.c \
src/mount.c \
+ src/umount.c \
src/swap.c \
src/device.c \
src/target.c \
diff --git a/src/umount.c b/src/umount.c
new file mode 100644
index 0000000..e017474
--- /dev/null
+++ b/src/umount.c
@@ -0,0 +1,183 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 ProFUSION embedded systems
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/mount.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "list.h"
+#include "mount-setup.h"
+#include "umount.h"
+#include "util.h"
+
+typedef struct MountPoint {
+ char *path;
+ bool read_only;
+ LIST_FIELDS (struct MountPoint, mount_point);
+} MountPoint;
+
+static MountPoint *mount_point_alloc(char *path) {
+ MountPoint *mp;
+
+ if (!(mp = new(MountPoint, 1)))
+ return NULL;
+
+ mp->path = path;
+ mp->read_only = false;
+
+ return mp;
+}
+
+static void mount_point_remove_and_free(MountPoint *mount_point, MountPoint **mount_point_list_head) {
+ LIST_REMOVE(MountPoint, mount_point, *mount_point_list_head, mount_point);
+
+ free(mount_point->path);
+ free(mount_point);
+}
+
+static void mount_points_list_free(MountPoint **mount_point_list_head) {
+ while(*mount_point_list_head)
+ mount_point_remove_and_free(*mount_point_list_head, mount_point_list_head);
+}
+
+static int mount_points_list_get(MountPoint **mount_point_list_head) {
+ FILE *proc_self_mountinfo;
+ char *path, *p;
+ int r, i;
+
+ if (!(proc_self_mountinfo = fopen("/proc/self/mountinfo", "re")))
+ return -errno;
+
+ for (i = 1;;i++) {
+ int k;
+ MountPoint *mp;
+
+ path = p = NULL;
+
+ if ((k = fscanf(proc_self_mountinfo,
+ "%*s " /* (1) mount id */
+ "%*s " /* (2) parent id */
+ "%*s " /* (3) major:minor */
+ "%*s " /* (4) root */
+ "%ms " /* (5) mount point */
+ "%*s" /* (6) mount options */
+ "%*[^-]" /* (7) optional fields */
+ "- " /* (8) separator */
+ "%*s " /* (9) file system type */
+ "%*s" /* (10) mount source */
+ "%*s" /* (11) mount options 2 */
+ "%*[^\n]", /* some rubbish at the end */
+ &path)) != 1) {
+ if (k == EOF)
+ break;
+
+ free(path);
+ continue;
+ }
+
+ if (mount_point_is_api(path)) {
+ free(path);
+ continue;
+ }
+
+ if (!(p = cunescape(path))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(mp = mount_point_alloc(p))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+ LIST_PREPEND(MountPoint, mount_point, *mount_point_list_head, mp);
+
+ free(path);
+ }
+
+ r = 0;
+
+finish:
+ fclose(proc_self_mountinfo);
+
+ free(path);
+
+ return r;
+}
+
+static void mount_points_list_umount(MountPoint **mount_point_list_head, bool *changed) {
+ MountPoint *mp, *mp_next;
+
+ LIST_FOREACH_SAFE(mount_point, mp, mp_next, *mount_point_list_head) {
+ if (streq(mp->path, "/"))
+ continue;
+
+ /* Trying to umount. Forcing to umount if busy (only for NFS mounts) */
+ if (umount2(mp->path, MNT_FORCE) == 0) {
+ *changed = true;
+ mount_point_remove_and_free(mp, mount_point_list_head);
+ }
+ }
+}
+
+static int mount_points_list_remount_read_only(MountPoint **mount_point_list_head, bool *changed) {
+ MountPoint *mp, *mp_next;
+ int r = 0;
+
+ LIST_FOREACH_SAFE(mount_point, mp, mp_next, *mount_point_list_head) {
+ if (mp->read_only)
+ continue;
+
+ /* Trying to remount read-only */
+ if (mount(NULL, mp->path, NULL, MS_MGC_VAL|MS_REMOUNT|MS_RDONLY, NULL) == 0) {
+ mp->read_only = true;
+ *changed = true;
+ mount_point_remove_and_free(mp, mount_point_list_head);
+ } else
+ r = -1;
+ }
+
+ return r;
+}
+
+int umount_init(void) {
+ LIST_HEAD(MountPoint, mp_list_head);
+ int r;
+ bool changed;
+
+ /* Preventing that we won't block umounts */
+ if ((chdir("/")) == -1)
+ return -1;
+
+ LIST_HEAD_INIT(MountPoint, mp_list_head);
+ if (mount_points_list_get(&mp_list_head) < 0)
+ return -1;
+
+ for (changed = false; changed;)
+ mount_points_list_umount(&mp_list_head, &changed);
+
+ for (changed = false; changed;)
+ r = mount_points_list_remount_read_only(&mp_list_head, &changed);
+
+ mount_points_list_free(&mp_list_head);
+
+ return r;
+}
diff --git a/src/umount.h b/src/umount.h
new file mode 100644
index 0000000..37f4e24
--- /dev/null
+++ b/src/umount.h
@@ -0,0 +1,27 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooumounthfoo
+#define fooumounthfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 ProFUSION embedded systems
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+int umount_init(void);
+
+#endif
--
1.7.3
Lennart Poettering
2010-10-05 20:35:55 UTC
Permalink
Post by f***@profusion.mobi
+int umount_init(void) {
+ LIST_HEAD(MountPoint, mp_list_head);
+ int r;
+ bool changed;
This isn't really an "initialization", is it? I'd prefer the name umount_all().
Post by f***@profusion.mobi
+
+ /* Preventing that we won't block umounts */
+ if ((chdir("/")) == -1)
+ return -1;
In systemd we use negative errno error codes, like the kernel (also see
CODING_STYLE). In this case:

if (chdir("/") < 0)
return -errno;
Post by f***@profusion.mobi
+
+ LIST_HEAD_INIT(MountPoint, mp_list_head);
+ if (mount_points_list_get(&mp_list_head) < 0)
+ return -1;
Same thing here.

if ((r = mount_points_list_get(&mp_list_head)) < 0)
return r;

(Assuming m_p_l_g() returns an errno error code, too)
Post by f***@profusion.mobi
+
+ for (changed = false; changed;)
+ mount_points_list_umount(&mp_list_head, &changed);
+
+ for (changed = false; changed;)
+ r = mount_points_list_remount_read_only(&mp_list_head, &changed);
+
+ mount_points_list_free(&mp_list_head);
+
+ return r;
I think it would make sense to return the last error we
encounter. Currently if a later mount_points_list_remount_read_only()
succeeds an earlier error is ignored.

Lennart
--
Lennart Poettering - Red Hat, Inc.
f***@profusion.mobi
2010-09-30 23:09:05 UTC
Permalink
From: Fabiano Fidencio <***@profusion.mobi>

This functions will unmount all filesystem that aren't api
(and those which fail will be remounted read-only).
Filesystems are being read from /proc/self/mountinfo.
---
Makefile.am | 1 +
src/umount.c | 183 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/umount.h | 27 +++++++++
3 files changed, 211 insertions(+), 0 deletions(-)
create mode 100644 src/umount.c
create mode 100644 src/umount.h

diff --git a/Makefile.am b/Makefile.am
index 0a33c06..06b2cca 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -349,6 +349,7 @@ libsystemd_core_la_SOURCES = \
src/service.c \
src/automount.c \
src/mount.c \
+ src/umount.c \
src/swap.c \
src/device.c \
src/target.c \
diff --git a/src/umount.c b/src/umount.c
new file mode 100644
index 0000000..9030b78
--- /dev/null
+++ b/src/umount.c
@@ -0,0 +1,183 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 ProFUSION embedded systems
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/mount.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "list.h"
+#include "mount-setup.h"
+#include "umount.h"
+#include "util.h"
+
+typedef struct MountPoint {
+ char *path;
+ bool read_only;
+ LIST_FIELDS (struct MountPoint, mount_point);
+} MountPoint;
+
+static MountPoint *mount_point_alloc(char *path) {
+ MountPoint *mp;
+
+ if (!(mp = new(MountPoint, 1)))
+ return NULL;
+
+ mp->path = path;
+ mp->read_only = false;
+
+ return mp;
+}
+
+static void mount_point_remove_and_free(MountPoint *mount_point, MountPoint **mount_point_list_head) {
+ LIST_REMOVE(MountPoint, mount_point, *mount_point_list_head, mount_point);
+
+ free(mount_point->path);
+ free(mount_point);
+}
+
+static void mount_points_list_free(MountPoint **mount_point_list_head) {
+ while(*mount_point_list_head)
+ mount_point_remove_and_free(*mount_point_list_head, mount_point_list_head);
+}
+
+static int mount_points_list_get(MountPoint **mount_point_list_head) {
+ FILE *proc_self_mountinfo;
+ char *path, *p;
+ int r;
+
+ if (!(proc_self_mountinfo = fopen("/proc/self/mountinfo", "re")))
+ return -errno;
+
+ for (;;) {
+ int k;
+ MountPoint *mp;
+
+ path = p = NULL;
+
+ if ((k = fscanf(proc_self_mountinfo,
+ "%*s " /* (1) mount id */
+ "%*s " /* (2) parent id */
+ "%*s " /* (3) major:minor */
+ "%*s " /* (4) root */
+ "%ms " /* (5) mount point */
+ "%*s" /* (6) mount options */
+ "%*[^-]" /* (7) optional fields */
+ "- " /* (8) separator */
+ "%*s " /* (9) file system type */
+ "%*s" /* (10) mount source */
+ "%*s" /* (11) mount options 2 */
+ "%*[^\n]", /* some rubbish at the end */
+ &path)) != 1) {
+ if (k == EOF)
+ break;
+
+ free(path);
+ continue;
+ }
+
+ if (mount_point_is_api(path)) {
+ free(path);
+ continue;
+ }
+
+ if (!(p = cunescape(path))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(mp = mount_point_alloc(p))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+ LIST_PREPEND(MountPoint, mount_point, *mount_point_list_head, mp);
+
+ free(path);
+ }
+
+ r = 0;
+
+finish:
+ fclose(proc_self_mountinfo);
+
+ free(path);
+
+ return r;
+}
+
+static void mount_points_list_umount(MountPoint **mount_point_list_head, bool *changed) {
+ MountPoint *mp, *mp_next;
+
+ LIST_FOREACH_SAFE(mount_point, mp, mp_next, *mount_point_list_head) {
+ if (streq(mp->path, "/"))
+ continue;
+
+ /* Trying to umount. Forcing to umount if busy (only for NFS mounts) */
+ if (umount2(mp->path, MNT_FORCE) == 0) {
+ *changed = true;
+ mount_point_remove_and_free(mp, mount_point_list_head);
+ }
+ }
+}
+
+static int mount_points_list_remount_read_only(MountPoint **mount_point_list_head, bool *changed) {
+ MountPoint *mp, *mp_next;
+ int r = 0;
+
+ LIST_FOREACH_SAFE(mount_point, mp, mp_next, *mount_point_list_head) {
+ if (mp->read_only)
+ continue;
+
+ /* Trying to remount read-only */
+ if (mount(NULL, mp->path, NULL, MS_MGC_VAL|MS_REMOUNT|MS_RDONLY, NULL) == 0) {
+ mp->read_only = true;
+ *changed = true;
+ mount_point_remove_and_free(mp, mount_point_list_head);
+ } else
+ r = -1;
+ }
+
+ return r;
+}
+
+int umount_init(void) {
+ LIST_HEAD(MountPoint, mp_list_head);
+ int r;
+ bool changed;
+
+ /* Preventing that we won't block umounts */
+ if ((chdir("/")) == -1)
+ return -1;
+
+ LIST_HEAD_INIT(MountPoint, mp_list_head);
+ if (mount_points_list_get(&mp_list_head) < 0)
+ return -1;
+
+ for (changed = false; changed;)
+ mount_points_list_umount(&mp_list_head, &changed);
+
+ for (changed = false; changed;)
+ r = mount_points_list_remount_read_only(&mp_list_head, &changed);
+
+ mount_points_list_free(&mp_list_head);
+
+ return r;
+}
diff --git a/src/umount.h b/src/umount.h
new file mode 100644
index 0000000..37f4e24
--- /dev/null
+++ b/src/umount.h
@@ -0,0 +1,27 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooumounthfoo
+#define fooumounthfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 ProFUSION embedded systems
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+int umount_init(void);
+
+#endif
--
1.7.3
Andrey Borzenkov
2010-10-01 03:11:31 UTC
Permalink
Post by f***@profusion.mobi
- Send a SIGTERM to all process
- Send a SIGKILL to all process
- Try to umount all mount points
- Try to remount read-only all mount points that can't
be umounted
- Call shutdown
If one step fail, shutdown will be aborted
May we have them as three different units, please?
- killall
- umountall
- shutdown
There could be alternative shutdown methods (like kexec) desired; also there
may be extra tasks to do between umounting and shutdown, like calling UPS
switch off command.

Thank you!
Gustavo Sverzut Barbieri
2010-10-01 03:55:48 UTC
Permalink
Post by Andrey Borzenkov
Post by f***@profusion.mobi
- Send a SIGTERM to all process
- Send a SIGKILL to all process
- Try to umount all mount points
- Try to remount read-only all mount points that can't
be umounted
- Call shutdown
If one step fail, shutdown will be aborted
May we have them as three different units, please?
- killall
- umountall
- shutdown
There could be alternative shutdown methods (like kexec) desired; also there
may be extra tasks to do between umounting and shutdown, like calling UPS
switch off command.
We (including lennart) have been discussing about the benefits to do
it in post-systemd, this will not be run as a regular init, but post
manager_loop(). Being pid1 we can use kill(-1), wait() will work for
every process, etc.

if we need things like UPS, better to make that uniform and always
call it if present.

BR,
--
Gustavo Sverzut Barbieri
http://profusion.mobi embedded systems
--------------------------------------
MSN: ***@gmail.com
Skype: gsbarbieri
Mobile: +55 (19) 9225-2202
Fabiano Fidêncio
2010-10-01 04:03:35 UTC
Permalink
2010/10/1 Andrey Borzenkov <***@mail.ru>:

Hey Andrey!
Post by Andrey Borzenkov
Post by f***@profusion.mobi
- Send a SIGTERM to all process
- Send a SIGKILL to all process
- Try to umount all mount points
- Try to remount read-only all mount points that can't
be umounted
- Call shutdown
If one step fail, shutdown will be aborted
May we have them as three different units, please?
- killall
- umountall
- shutdown
I was talking with Lennart and some things need be done, yet.
One of this things is support halt/shutdown/reboot/kexec.
Post by Andrey Borzenkov
There could be alternative shutdown methods (like kexec) desired; also there
may be extra tasks to do between umounting and shutdown, like calling UPS
switch off command.
Thank you!
Thank you for the comment. :-)
Post by Andrey Borzenkov
_______________________________________________
systemd-devel mailing list
http://lists.freedesktop.org/mailman/listinfo/systemd-devel
--
Fabiano Fidêncio
ProFUSION embedded systems
http://www.profusion.mobi
Lennart Poettering
2010-10-01 17:53:13 UTC
Permalink
Post by Andrey Borzenkov
Post by f***@profusion.mobi
- Call shutdown
If one step fail, shutdown will be aborted
May we have them as three different units, please?
- killall
- umountall
- shutdown
I don't think we want that, as Gustavo already pointed out.
Post by Andrey Borzenkov
There could be alternative shutdown methods (like kexec) desired; also there
may be extra tasks to do between umounting and shutdown, like calling UPS
switch off command.
I am not sure stuff like UPS switch off really makes sense since it is
inherenlty racy. How is that ever supposed to work? How would we from
userspace know when exactly the kernel is finished shutting down? And
even if we knew how would we tell anybody about this since at this time
the kernel is already stopped and userspace would have ceased to
exist...

Note that the kernel does number of things when going down, such as
syncing and more. Since that is the case it must also be the kernel
which afterwards talks to the UPS. Shutting down UPS from userspace is
just racy, as the power might go away at a time the kernel is still busy
with stuff.

Lennart
--
Lennart Poettering - Red Hat, Inc.
f***@profusion.mobi
2010-10-05 15:41:06 UTC
Permalink
From: Fabiano Fidêncio <***@gmail.com>

The current behavior of the /proc/self/mountinfo's parser
is stop the parser if the parser fail.
I'm only changing this behavior to try the next line and adding
a warning if occurs a fail.
---
src/mount.c | 9 +++++----
1 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/src/mount.c b/src/mount.c
index 3c08baf..2fb5822 100644
--- a/src/mount.c
+++ b/src/mount.c
@@ -1400,14 +1400,14 @@ finish:
}

static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
- int r;
+ int r, i;
char *device, *path, *options, *options2, *fstype, *d, *p, *o;

assert(m);

rewind(m->proc_self_mountinfo);

- for (;;) {
+ for (i =1;;i++) {
int k;

device = path = options = options2 = fstype = d = p = o = NULL;
@@ -1434,8 +1434,8 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
if (k == EOF)
break;

- r = -EBADMSG;
- goto finish;
+ log_warning("Line %d of /proc/self/mountinfo cannot be read", i);
+ goto clean_up;
}

if (asprintf(&o, "%s,%s", options, options2) < 0) {
@@ -1452,6 +1452,7 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
if ((r = mount_add_one(m, d, p, o, fstype, true, set_flags)) < 0)
goto finish;

+clean_up:
free(device);
free(path);
free(options);
--
1.7.3
Lennart Poettering
2010-10-05 20:27:48 UTC
Permalink
Post by f***@profusion.mobi
The current behavior of the /proc/self/mountinfo's parser
is stop the parser if the parser fail.
I'm only changing this behavior to try the next line and adding
a warning if occurs a fail.
Applied.

Thanks,

Lennart
--
Lennart Poettering - Red Hat, Inc.
f***@profusion.mobi
2010-10-05 15:41:07 UTC
Permalink
From: Fabiano Fidencio <***@profusion.mobi>

---
src/mount.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/src/mount.c b/src/mount.c
index 2fb5822..52743db 100644
--- a/src/mount.c
+++ b/src/mount.c
@@ -1420,7 +1420,7 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
"%ms " /* (5) mount point */
"%ms" /* (6) mount options */
"%*[^-]" /* (7) optional fields */
- "- " /* (8) seperator */
+ "- " /* (8) separator */
"%ms " /* (9) file system type */
"%ms" /* (10) mount source */
"%ms" /* (11) mount options 2 */
--
1.7.3
f***@profusion.mobi
2010-10-05 15:41:08 UTC
Permalink
From: Fabiano Fidencio <***@profusion.mobi>

This functions are working as follows:
- Send a SIGTERM to all processes that may be finished.
- Send a SIGKILL to all processes that still live and
may be finished.
- Try to mount all mount points
- Try to remount read-only all mount points that can't
be umounted
- Umount all swap devices.
- Call [poweroff|halt|reboot|kexec]

TODO:
- umount dms
- umount loopbacks
---
Makefile.am | 15 +++++
src/shutdown.c | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 176 insertions(+), 0 deletions(-)
create mode 100644 src/shutdown.c

diff --git a/Makefile.am b/Makefile.am
index 0a33c06..2db3827 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -35,6 +35,7 @@ tmpfilesdir=$(sysconfdir)/tmpfiles.d
# And these are the special ones for /
rootdir=@rootdir@
rootbindir=$(rootdir)/bin
+rootsbindir=$(rootdir)/sbin
rootlibexecdir=$(rootdir)/lib/systemd
systemunitdir=$(rootdir)/lib/systemd/system

@@ -50,9 +51,11 @@ AM_CPPFLAGS = \
-DSESSION_DATA_UNIT_PATH=\"$(sessionunitdir)\" \
-DCGROUP_AGENT_PATH=\"$(rootlibexecdir)/systemd-cgroups-agent\" \
-DSYSTEMD_BINARY_PATH=\"$(rootbindir)/systemd\" \
+ -DSYSTEMD_SHUTDOWN_BINARY_PATH=\"$(rootlibexecdir)/systemd-shutdown\" \
-DSYSTEMCTL_BINARY_PATH=\"$(rootbindir)/systemctl\" \
-DRUNTIME_DIR=\"$(localstatedir)/run\" \
-DRANDOM_SEED=\"$(localstatedir)/lib/random-seed\" \
+ -DKEXEC_BINARY_PATH=\"$(rootsbindir)/kexec\" \
-I $(top_srcdir)/src

if TARGET_GENTOO
@@ -89,6 +92,7 @@ rootlibexec_PROGRAMS = \
systemd-update-utmp \
systemd-random-seed \
systemd-shutdownd \
+ systemd-shutdown \
systemd-modules-load \
systemd-remount-api-vfs \
systemd-kmsg-syslogd \
@@ -628,6 +632,17 @@ systemd_shutdownd_CFLAGS = \
systemd_shutdownd_LDADD = \
libsystemd-basic.la

+systemd_shutdown_SOURCES = \
+ src/mount-setup.c \
+ src/umount.c \
+ src/shutdown.c
+
+systemd_shutdown_CFLAGS = \
+ $(AM_CFLAGS)
+
+systemd_shutdown_LDADD = \
+ libsystemd-basic.la
+
systemd_modules_load_SOURCES = \
src/modules-load.c

diff --git a/src/shutdown.c b/src/shutdown.c
new file mode 100644
index 0000000..95c19a3
--- /dev/null
+++ b/src/shutdown.c
@@ -0,0 +1,161 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 ProFUSION embedded systems
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/reboot.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "log.h"
+#include "umount.h"
+#include "util.h"
+
+#define TIMEOUT_USEC 5000000
+
+static bool ignore_proc(pid_t pid) {
+ if (pid == 1)
+ return true;
+
+ return false;
+}
+
+static unsigned int killall(DIR *dir, int sign) {
+ struct dirent *d;
+ pid_t pid;
+ unsigned int processes = 0;
+
+ while ((d = readdir(dir))) {
+
+ if ((pid = atoi(d->d_name)) == 0)
+ continue;
+
+ if (ignore_proc(pid))
+ continue;
+
+ kill(pid, sign);
+ processes++;
+ }
+
+ return processes;
+}
+
+static unsigned int send_signal(DIR *dir, int sign) {
+ sigset_t mask;
+ usec_t until;
+ unsigned int processes;
+ struct timespec ts;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGCHLD);
+
+ processes = killall(dir, sign);
+
+ until = now(CLOCK_REALTIME) + TIMEOUT_USEC;
+ for (;;) {
+ usec_t n;
+
+ for (;;) {
+ n = now(CLOCK_REALTIME);
+ if (n >= until)
+ goto finish;
+
+ if (waitpid(-1, NULL, WNOHANG) <= 0)
+ break;
+
+ if (--processes == 0)
+ goto finish;
+ }
+
+ timespec_store(&ts, until - n);
+ sigtimedwait(&mask, NULL, &ts);
+ }
+
+finish:
+ return processes;
+}
+
+int main(int argc, char *argv[]) {
+ DIR *dir;
+ int cmd;
+ const char *args[5] = {KEXEC_BINARY_PATH, "-e", "-x", "-f", NULL};
+
+ if (getpid() != 1)
+ return EXIT_FAILURE;
+
+ if (argc != 2)
+ return EXIT_FAILURE;
+
+ log_set_target(LOG_TARGET_SYSLOG_OR_KMSG);
+ log_parse_environment();
+ log_open();
+
+ if (streq(argv[1], "reboot"))
+ cmd = RB_AUTOBOOT;
+ else if (streq(argv[1], "poweroff"))
+ cmd = RB_POWER_OFF;
+ else if (streq(argv[1], "halt"))
+ cmd = RB_HALT_SYSTEM;
+
+ /* lock us into memory */
+ mlockall(MCL_CURRENT|MCL_FUTURE);
+
+ /* opening /proc to read all processes */
+ if ((dir = opendir("/proc")) == NULL)
+ return EXIT_FAILURE;
+
+ /* sending SIGTERM and SIGKILL to all processes */
+ kill(-1, SIGSTOP);
+ log_info("Sending SIGTERM To Processes");
+ if ((send_signal(dir, SIGTERM)) > 0) {
+ log_info("Sending SIGKILL To Processes");
+ send_signal(dir, SIGKILL);
+ }
+ kill(-1, SIGCONT);
+
+ /* preventing that we won't block umounts */
+ if (chdir("/") == -1)
+ return EXIT_FAILURE;
+
+ /* umount all mountpoints, swaps, dm and loops */
+ log_info("Unmounting Filesystems");
+ umount_all();
+ log_info("Disabling Swaps");
+ swapoff_all();
+ /* FIXME:
+ * - undm_all()
+ * - unloop_all()
+ * - handling devicemapper
+ */
+
+ sync();
+
+ if (cmd == RB_AUTOBOOT)
+ execv(args[0], (char * const *) args);
+
+ return reboot(cmd);
+}
--
1.7.3
Michael Biebl
2010-10-05 16:25:36 UTC
Permalink
   - Send a SIGTERM to all processes that may be finished.
   - Send a SIGKILL to all processes that still live and
   may be finished.
   - Try to mount all mount points
^ unmount, I guess
   - Try to remount read-only all mount points that can't
   be umounted
   - Umount all swap devices.
   - Call [poweroff|halt|reboot|kexec]
Just to be sure:
There still will be a separate killall service and unmounting in halt
is only a last resort, i.e.
mount points handled by systemd should already be unmounted when this
binary is called?
--
Why is it that all of the instruments seeking intelligent life in the
universe are pointed away from Earth?
Lennart Poettering
2010-10-05 20:50:33 UTC
Permalink
Post by Michael Biebl
   - Send a SIGTERM to all processes that may be finished.
   - Send a SIGKILL to all processes that still live and
   may be finished.
   - Try to mount all mount points
^ unmount, I guess
   - Try to remount read-only all mount points that can't
   be umounted
   - Umount all swap devices.
   - Call [poweroff|halt|reboot|kexec]
There still will be a separate killall service and unmounting in halt
is only a last resort, i.e.
mount points handled by systemd should already be unmounted when this
binary is called?
Yes and no.

The normal service handling should make sure that normal services should
not be around anymore when this code is invoked. However, there's stuff
that's not exactly "normal services". Most prominently user sessions. To
cover those I plan to add a service that kills all user sessions on
shutdown very early, i.e. before we kill all the services. That's
something that has been missing in sysv for quite a while and we now can
add that here for the first time.

And then there's everything that moves itself to a different cgroup in
the systemd hierarchy. Which privileged code might do.

Lennart
--
Lennart Poettering - Red Hat, Inc.
Lennart Poettering
2010-10-05 20:47:04 UTC
Permalink
Post by f***@profusion.mobi
+
+#define TIMEOUT_USEC 5000000
To make this more readable please write 5*USEC_PER_SEC here.
Post by f***@profusion.mobi
+static bool ignore_proc(pid_t pid) {
+ if (pid == 1)
+ return true;
+
+ return false;
+}
+
+static unsigned int killall(DIR *dir, int sign) {
+ struct dirent *d;
+ pid_t pid;
+ unsigned int processes = 0;
+
+ while ((d = readdir(dir))) {
+
+ if ((pid = atoi(d->d_name)) == 0)
+ continue;
atoi() is evil... please use parse_pid() here!
Post by f***@profusion.mobi
+
+ if (ignore_proc(pid))
+ continue;
+
+ kill(pid, sign);
+ processes++;
We should increase the counter here only if the kill actually worked I believe.
Post by f***@profusion.mobi
+ }
+
+ return processes;
+}
+
+static unsigned int send_signal(DIR *dir, int sign) {
+ sigset_t mask;
+ usec_t until;
+ unsigned int processes;
+ struct timespec ts;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGCHLD);
sigprocmask() is missing here? You need to make sure SIGCHLD is not
delivered via signal handlers here anymore.
Post by f***@profusion.mobi
+ processes = killall(dir, sign);
+
+ until = now(CLOCK_REALTIME) + TIMEOUT_USEC;
+ for (;;) {
+ usec_t n;
+
+ for (;;) {
+ n = now(CLOCK_REALTIME);
+ if (n >= until)
+ goto finish;
+
+ if (waitpid(-1, NULL, WNOHANG) <= 0)
+ break;
+
+ if (--processes == 0)
+ goto finish;
+ }
+
+ timespec_store(&ts, until - n);
+ sigtimedwait(&mask, NULL, &ts);
Please handle errors here. Write a log message at least.
Post by f***@profusion.mobi
+ }
+
+ return processes;
+}
+
+int main(int argc, char *argv[]) {
+ DIR *dir;
+ int cmd;
+ const char *args[5] = {KEXEC_BINARY_PATH, "-e", "-x", "-f", NULL};
+
+ if (getpid() != 1)
+ return EXIT_FAILURE;
+
+ if (argc != 2)
+ return EXIT_FAILURE;
Please add explanatory log messages here.
Post by f***@profusion.mobi
+
+ log_set_target(LOG_TARGET_SYSLOG_OR_KMSG);
+ log_parse_environment();
+ log_open();
+
+ if (streq(argv[1], "reboot"))
+ cmd = RB_AUTOBOOT;
+ else if (streq(argv[1], "poweroff"))
+ cmd = RB_POWER_OFF;
+ else if (streq(argv[1], "halt"))
+ cmd = RB_HALT_SYSTEM;
+
+ /* lock us into memory */
+ mlockall(MCL_CURRENT|MCL_FUTURE);
+
+ /* opening /proc to read all processes */
+ if ((dir = opendir("/proc")) == NULL)
+ return EXIT_FAILURE;
+
+ /* sending SIGTERM and SIGKILL to all processes */
+ kill(-1, SIGSTOP);
+ log_info("Sending SIGTERM To Processes");
+ if ((send_signal(dir, SIGTERM)) > 0) {
+ log_info("Sending SIGKILL To Processes");
+ send_signal(dir, SIGKILL);
+ }
+ kill(-1, SIGCONT);
+
+ /* preventing that we won't block umounts */
+ if (chdir("/") == -1)
+ return EXIT_FAILURE;
+
+ /* umount all mountpoints, swaps, dm and loops */
+ log_info("Unmounting Filesystems");
+ umount_all();
+ log_info("Disabling Swaps");
+ swapoff_all();
+ * - undm_all()
+ * - unloop_all()
+ * - handling devicemapper
+ */
You need a loop here. i.e. a swap you drop might make it possible to
unmount another fs (because the swap was about a swap file, not a swap
device). And so on.
Post by f***@profusion.mobi
+
+ sync();
+
+ if (cmd == RB_AUTOBOOT)
+ execv(args[0], (char * const *) args);
Please add a proper verb above so that we can distuingish kexec and
normal reboots. (We probably want to have this all the way so that
there's a first klass kexec target and everything.
Post by f***@profusion.mobi
+
+ return reboot(cmd);
+}
reboot() returns -1 on failure, which is not suitable for retruning from main().

Otherwise looks good!

Lennart
--
Lennart Poettering - Red Hat, Inc.
f***@profusion.mobi
2010-10-05 15:41:09 UTC
Permalink
From: Fabiano Fidencio <***@profusion.mobi>

This functions will:
- umount all mount points that aren't API
- remount read-only all mount points that can't be umounted
- umount all swap devices.

TODO:
- umount dms
- umount loopbacks

Mountpoints are being read from /proc/self/mountinfo.
Swaps are being read from /proc/swaps
---
Makefile.am | 1 +
src/umount.c | 253 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/umount.h | 29 +++++++
3 files changed, 283 insertions(+), 0 deletions(-)
create mode 100644 src/umount.c
create mode 100644 src/umount.h

diff --git a/Makefile.am b/Makefile.am
index 2db3827..1625a67 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -353,6 +353,7 @@ libsystemd_core_la_SOURCES = \
src/service.c \
src/automount.c \
src/mount.c \
+ src/umount.c \
src/swap.c \
src/device.c \
src/target.c \
diff --git a/src/umount.c b/src/umount.c
new file mode 100644
index 0000000..9d403c6
--- /dev/null
+++ b/src/umount.c
@@ -0,0 +1,253 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 ProFUSION embedded systems
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/mount.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/swap.h>
+
+#include "list.h"
+#include "mount-setup.h"
+#include "umount.h"
+#include "util.h"
+
+typedef struct MountPoint {
+ char *path;
+ bool read_only;
+ LIST_FIELDS (struct MountPoint, mount_point);
+} MountPoint;
+
+static MountPoint *mount_point_alloc(char *path) {
+ MountPoint *mp;
+
+ if (!(mp = new(MountPoint, 1)))
+ return NULL;
+
+ mp->path = path;
+ mp->read_only = false;
+
+ return mp;
+}
+
+static void mount_point_remove_and_free(MountPoint *mount_point, MountPoint **mount_point_list_head) {
+ LIST_REMOVE(MountPoint, mount_point, *mount_point_list_head, mount_point);
+
+ free(mount_point->path);
+ free(mount_point);
+}
+
+static void mount_points_list_free(MountPoint **mount_point_list_head) {
+ while (*mount_point_list_head)
+ mount_point_remove_and_free(*mount_point_list_head, mount_point_list_head);
+}
+
+static int mount_points_list_get(MountPoint **mount_point_list_head) {
+ FILE *proc_self_mountinfo;
+ char *path, *p;
+ int r;
+
+ if (!(proc_self_mountinfo = fopen("/proc/self/mountinfo", "re")))
+ return -errno;
+
+ for (;;) {
+ int k;
+ MountPoint *mp;
+
+ path = p = NULL;
+
+ if ((k = fscanf(proc_self_mountinfo,
+ "%*s " /* (1) mount id */
+ "%*s " /* (2) parent id */
+ "%*s " /* (3) major:minor */
+ "%*s " /* (4) root */
+ "%ms " /* (5) mount point */
+ "%*s" /* (6) mount options */
+ "%*[^-]" /* (7) optional fields */
+ "- " /* (8) separator */
+ "%*s " /* (9) file system type */
+ "%*s" /* (10) mount source */
+ "%*s" /* (11) mount options 2 */
+ "%*[^\n]", /* some rubbish at the end */
+ &path)) != 1) {
+ if (k == EOF)
+ break;
+
+ free(path);
+ continue;
+ }
+
+ if (mount_point_is_api(path)) {
+ free(path);
+ continue;
+ }
+
+ if (!(p = cunescape(path))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(mp = mount_point_alloc(p))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+ LIST_PREPEND(MountPoint, mount_point, *mount_point_list_head, mp);
+
+ free(path);
+ }
+
+ r = 0;
+
+finish:
+ fclose(proc_self_mountinfo);
+
+ free(path);
+
+ return r;
+}
+
+static int swap_list_get(MountPoint **swap_list_head) {
+ FILE *proc_swaps;
+
+ if (!(proc_swaps = fopen("/proc/swaps", "re")))
+ return -errno;
+
+ (void) fscanf(proc_swaps, "%*s %*s %*s %*s %*s\n");
+
+ for (;;) {
+ MountPoint *swap;
+ char *dev = NULL, *d;
+ int k;
+
+ if ((k = fscanf(proc_swaps,
+ "%ms " /* device/file */
+ "%*s " /* type of swap */
+ "%*s " /* swap size */
+ "%*s " /* used */
+ "%*s\n", /* priority */
+ &dev)) != 1) {
+
+ if (k == EOF)
+ break;
+
+ free(dev);
+ return -EBADMSG;
+ }
+
+ d = cunescape(dev);
+ free(dev);
+
+ if (!d)
+ return -ENOMEM;
+
+ swap = mount_point_alloc(d);
+ if (!swap) {
+ free(d);
+ return -ENOMEM;
+ }
+
+ LIST_PREPEND(MountPoint, mount_point, *swap_list_head, swap);
+ }
+
+ return 0;
+}
+
+static int mount_points_list_umount(MountPoint **mount_point_list_head) {
+ MountPoint *mp, *mp_next;
+ int r = 0;
+
+ LIST_FOREACH_SAFE(mount_point, mp, mp_next, *mount_point_list_head) {
+ if (streq(mp->path, "/"))
+ continue;
+
+ /* Trying to umount. Forcing to umount if busy (only for NFS mounts) */
+ if (umount2(mp->path, MNT_FORCE) == 0) {
+ mount_point_remove_and_free(mp, mount_point_list_head);
+ r++;
+ }
+ }
+
+ return r;
+}
+
+static int mount_points_list_remount_read_only(MountPoint **mount_point_list_head) {
+ MountPoint *mp, *mp_next;
+ int r = 0;
+
+ LIST_FOREACH_SAFE(mount_point, mp, mp_next, *mount_point_list_head) {
+ if (mp->read_only)
+ continue;
+
+ /* Trying to remount read-only */
+ if (mount(NULL, mp->path, NULL, MS_MGC_VAL|MS_REMOUNT|MS_RDONLY, NULL) == 0) {
+ mp->read_only = true;
+ mount_point_remove_and_free(mp, mount_point_list_head);
+ r++;
+ }
+ }
+
+ return r;
+}
+
+static int swap_points_list_off(MountPoint **swap_list_head) {
+ MountPoint *swap, *swap_next;
+ int r = 0;
+
+ LIST_FOREACH_SAFE(mount_point, swap, swap_next, *swap_list_head) {
+ if (swapoff(swap->path) == 0) {
+ mount_point_remove_and_free(swap, swap_list_head);
+ r++;
+ }
+
+ }
+
+ return r;
+}
+
+int umount_all(void) {
+ LIST_HEAD(MountPoint, mp_list_head);
+
+ LIST_HEAD_INIT(MountPoint, mp_list_head);
+ if (mount_points_list_get(&mp_list_head) < 0)
+ return -ENOMEM;
+
+ while (mount_points_list_umount(&mp_list_head));
+
+ while (mount_points_list_remount_read_only(&mp_list_head));
+
+ mount_points_list_free(&mp_list_head);
+
+ return 0;
+}
+
+int swapoff_all(void) {
+ LIST_HEAD(MountPoint, swap_list_head);
+
+ LIST_HEAD_INIT(MountPoint, swap_list_head);
+ if (swap_list_get(&swap_list_head) < 0)
+ return -ENOMEM;
+
+ while (swap_points_list_off(&swap_list_head));
+
+ mount_points_list_free(&swap_list_head);
+
+ return 0;
+}
diff --git a/src/umount.h b/src/umount.h
new file mode 100644
index 0000000..3e55c1c
--- /dev/null
+++ b/src/umount.h
@@ -0,0 +1,29 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooumounthfoo
+#define fooumounthfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 ProFUSION embedded systems
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+int umount_all(void);
+
+int swapoff_all(void);
+
+#endif
--
1.7.3
Lennart Poettering
2010-10-05 20:38:40 UTC
Permalink
Post by f***@profusion.mobi
+static int swap_list_get(MountPoint **swap_list_head) {
+ FILE *proc_swaps;
+
+ if (!(proc_swaps = fopen("/proc/swaps", "re")))
+ return -errno;
+
+ (void) fscanf(proc_swaps, "%*s %*s %*s %*s %*s\n");
+
+ for (;;) {
+ MountPoint *swap;
+ char *dev = NULL, *d;
+ int k;
+
+ if ((k = fscanf(proc_swaps,
+ "%ms " /* device/file */
+ "%*s " /* type of swap */
+ "%*s " /* swap size */
+ "%*s " /* used */
+ "%*s\n", /* priority */
+ &dev)) != 1) {
+
+ if (k == EOF)
+ break;
+
+ free(dev);
+ return -EBADMSG;
Please just "continue" in this error case, too, much like you handle
mountinfo.

Lennart
--
Lennart Poettering - Red Hat, Inc.
Gustavo Sverzut Barbieri
2010-10-05 20:41:53 UTC
Permalink
On Tue, Oct 5, 2010 at 5:38 PM, Lennart Poettering
Post by Lennart Poettering
Post by f***@profusion.mobi
+static int swap_list_get(MountPoint **swap_list_head) {
+        FILE *proc_swaps;
+
+        if (!(proc_swaps = fopen("/proc/swaps", "re")))
+                return -errno;
+
+        (void) fscanf(proc_swaps, "%*s %*s %*s %*s %*s\n");
+
+        for (;;) {
+                MountPoint *swap;
+                char *dev = NULL, *d;
+                int k;
+
+                if ((k = fscanf(proc_swaps,
+                                "%ms " /* device/file */
+                                "%*s " /* type of swap */
+                                "%*s " /* swap size */
+                                "%*s " /* used */
+                                "%*s\n", /* priority */
+                                &dev)) != 1) {
+
+                        if (k == EOF)
+                                break;
+
+                        free(dev);
+                        return -EBADMSG;
Please just "continue" in this error case, too, much like you handle
mountinfo.
Yes, I've asked fidencio to be more permissive with errors trying to
continue as far as possible, that also includes not return from the
shutdown program when some error occurs. Your comment goes in the same
lines :-)
--
Gustavo Sverzut Barbieri
http://profusion.mobi embedded systems
--------------------------------------
MSN: ***@gmail.com
Skype: gsbarbieri
Mobile: +55 (19) 9225-22
f***@profusion.mobi
2010-10-07 04:03:34 UTC
Permalink
From: Fabiano Fidencio <***@profusion.mobi>

As we have streq to repesent (strcmp(a, b) == 0),I'm adding
strneq to represent (strncmp(a, b, n) == 0), that will be used
in umount.c (at least).
---
src/util.h | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/src/util.h b/src/util.h
index f21aecf..618e7cf 100644
--- a/src/util.h
+++ b/src/util.h
@@ -82,6 +82,7 @@ usec_t timeval_load(const struct timeval *tv);
struct timeval *timeval_store(struct timeval *tv, usec_t u);

#define streq(a,b) (strcmp((a),(b)) == 0)
+#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0)

bool streq_ptr(const char *a, const char *b);
--
1.7.3
f***@profusion.mobi
2010-10-07 04:03:35 UTC
Permalink
From: Fabiano Fidencio <***@profusion.mobi>

This functions will:
- umount all mount points that aren't API
- remount read-only all mount points that can't be umounted
- umount all swap devices.
- detach all loopback devices

TODO:
- umount dms

Mountpoints are being read from /proc/self/mountinfo.
Swaps are being read from /proc/swaps.
Loop devices from /sys/class/block/loop*.
---
.gitignore | 1 +
src/umount.c | 411 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/umount.h | 31 +++++
3 files changed, 443 insertions(+), 0 deletions(-)
create mode 100644 src/umount.c
create mode 100644 src/umount.h

diff --git a/.gitignore b/.gitignore
index 9ba0758..2ba000c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+systemd-shutdown
systemd-tmpfiles
systemd-readahead-collect
systemd-readahead-replay
diff --git a/src/umount.c b/src/umount.c
new file mode 100644
index 0000000..386b72f
--- /dev/null
+++ b/src/umount.c
@@ -0,0 +1,411 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 ProFUSION embedded systems
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/swap.h>
+#include <unistd.h>
+#include <linux/loop.h>
+#include <libudev.h>
+
+#include "list.h"
+#include "mount-setup.h"
+#include "umount.h"
+#include "util.h"
+
+typedef struct MountPoint {
+ char *path;
+ bool read_only;
+ LIST_FIELDS (struct MountPoint, mount_point);
+} MountPoint;
+
+static MountPoint *mount_point_alloc(char *path) {
+ MountPoint *mp;
+
+ if (!(mp = new(MountPoint, 1)))
+ return NULL;
+
+ mp->path = path;
+ mp->read_only = false;
+
+ return mp;
+}
+
+static void mount_point_remove_and_free(MountPoint *mount_point, MountPoint **mount_point_list_head) {
+ LIST_REMOVE(MountPoint, mount_point, *mount_point_list_head, mount_point);
+
+ free(mount_point->path);
+ free(mount_point);
+}
+
+static void mount_points_list_free(MountPoint **mount_point_list_head) {
+ while (*mount_point_list_head)
+ mount_point_remove_and_free(*mount_point_list_head, mount_point_list_head);
+}
+
+static int mount_points_list_get(MountPoint **mount_point_list_head) {
+ FILE *proc_self_mountinfo;
+ char *path, *p;
+ unsigned int i;
+ int r;
+
+ if (!(proc_self_mountinfo = fopen("/proc/self/mountinfo", "re")))
+ return -errno;
+
+ for (i = 1;; i++) {
+ int k;
+ MountPoint *mp;
+
+ path = p = NULL;
+
+ if ((k = fscanf(proc_self_mountinfo,
+ "%*s " /* (1) mount id */
+ "%*s " /* (2) parent id */
+ "%*s " /* (3) major:minor */
+ "%*s " /* (4) root */
+ "%ms " /* (5) mount point */
+ "%*s" /* (6) mount options */
+ "%*[^-]" /* (7) optional fields */
+ "- " /* (8) separator */
+ "%*s " /* (9) file system type */
+ "%*s" /* (10) mount source */
+ "%*s" /* (11) mount options 2 */
+ "%*[^\n]", /* some rubbish at the end */
+ &path)) != 1) {
+ if (k == EOF)
+ break;
+
+ log_warning("Failed to parse /proc/self/mountinfo:%u.", i);
+
+ free(path);
+ continue;
+ }
+
+ if (mount_point_is_api(path)) {
+ free(path);
+ continue;
+ }
+
+ if (!(p = cunescape(path))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(mp = mount_point_alloc(p))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+ LIST_PREPEND(MountPoint, mount_point, *mount_point_list_head, mp);
+
+ free(path);
+ }
+
+ r = 0;
+
+finish:
+ fclose(proc_self_mountinfo);
+
+ free(path);
+
+ return r;
+}
+
+static int swap_list_get(MountPoint **swap_list_head) {
+ FILE *proc_swaps;
+ unsigned int i;
+ int r;
+
+ if (!(proc_swaps = fopen("/proc/swaps", "re")))
+ return -errno;
+
+ (void) fscanf(proc_swaps, "%*s %*s %*s %*s %*s\n");
+
+ for (i = 2;; i++) {
+ MountPoint *swap;
+ char *dev = NULL, *d;
+ int k;
+
+ if ((k = fscanf(proc_swaps,
+ "%ms " /* device/file */
+ "%*s " /* type of swap */
+ "%*s " /* swap size */
+ "%*s " /* used */
+ "%*s\n", /* priority */
+ &dev)) != 1) {
+
+ if (k == EOF)
+ break;
+
+ log_warning("Failed to parse /proc/swaps:%u.", i);
+
+ free(dev);
+ continue;
+ }
+
+ if (endswith(dev, "(deleted)")) {
+ free(dev);
+ continue;
+ }
+
+ d = cunescape(dev);
+ free(dev);
+
+ if (!d) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ swap = mount_point_alloc(d);
+ if (!swap) {
+ free(d);
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ LIST_PREPEND(MountPoint, mount_point, *swap_list_head, swap);
+ }
+
+ r = 0;
+
+finish:
+ fclose(proc_swaps);
+
+ return r;
+}
+
+static int loopback_list_get(MountPoint **loopback_list_head) {
+ int r;
+ struct udev *udev;
+ struct udev_enumerate *e = NULL;
+ struct udev_list_entry *item = NULL, *first = NULL;
+
+ if (!(udev = udev_new())) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(e = udev_enumerate_new(udev))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (udev_enumerate_add_match_subsystem(e, "block") < 0) {
+ r = -EIO;
+ goto finish;
+ }
+
+ if (udev_enumerate_scan_devices(e) < 0) {
+ r = -EIO;
+ goto finish;
+ }
+
+ first = udev_enumerate_get_list_entry(e);
+
+ udev_list_entry_foreach(item, first) {
+ MountPoint *lb;
+ char buf[PATH_MAX];
+ char *loop;
+
+ snprintf(buf, sizeof(buf), "%s", udev_list_entry_get_name(item));
+ if (strstr(buf, "loop"))
+ loop = cunescape(buf);
+ else
+ continue;
+
+ if (!loop) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ lb = mount_point_alloc(loop);
+ if (!lb) {
+ free(loop);
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ LIST_PREPEND(MountPoint, mount_point, *loopback_list_head, lb);
+ }
+
+ r = 0;
+
+finish:
+ if (e)
+ udev_enumerate_unref(e);
+
+ free(udev);
+ return r;
+}
+
+static int delete_loopback(const char *device) {
+ int fd, r;
+
+ if ((fd = open(device, O_RDONLY|O_CLOEXEC)) < 0) {
+ if (errno == ENOENT) {
+ log_debug("Loop device %s does not exist.", device);
+ errno = 0;
+ return 0;
+ }
+ return -errno;
+ }
+
+ ioctl(fd, LOOP_CLR_FD, 0);
+ r = errno;
+ close_nointr(fd);
+
+ if (r == ENXIO) /* not bound, so no error */
+ r = 0;
+ errno = r;
+ return -errno;
+}
+
+static int mount_points_list_umount(MountPoint **mount_point_list_head) {
+ MountPoint *mp, *mp_next;
+ int failed = 0;
+
+ LIST_FOREACH_SAFE(mount_point, mp, mp_next, *mount_point_list_head) {
+ if (streq(mp->path, "/"))
+ continue;
+
+ /* Trying to umount. Forcing to umount if busy (only for NFS mounts) */
+ if (umount2(mp->path, MNT_FORCE) == 0)
+ mount_point_remove_and_free(mp, mount_point_list_head);
+ else {
+ log_debug("Could not unmount %s: %m", mp->path);
+ failed++;
+ }
+ }
+
+ return failed;
+}
+
+static int mount_points_list_remount_read_only(MountPoint **mount_point_list_head) {
+ MountPoint *mp, *mp_next;
+ int failed = 0;
+
+ LIST_FOREACH_SAFE(mount_point, mp, mp_next, *mount_point_list_head) {
+ if (mp->read_only)
+ continue;
+
+ /* Trying to remount read-only */
+ if (mount(NULL, mp->path, NULL, MS_MGC_VAL|MS_REMOUNT|MS_RDONLY, NULL) == 0) {
+ mp->read_only = true;
+ mount_point_remove_and_free(mp, mount_point_list_head);
+ } else {
+ log_debug("Could not remount as read-only %s: %m", mp->path);
+ failed++;
+ }
+ }
+
+ return failed;
+}
+
+static int swap_points_list_off(MountPoint **swap_list_head) {
+ MountPoint *swap, *swap_next;
+ int failed = 0;
+
+ LIST_FOREACH_SAFE(mount_point, swap, swap_next, *swap_list_head) {
+ if (swapoff(swap->path) == 0)
+ mount_point_remove_and_free(swap, swap_list_head);
+ else {
+ log_debug("Could not swapoff %s: %m", swap->path);
+ failed++;
+ }
+ }
+
+ return failed;
+}
+
+static int loopback_points_list_detach(MountPoint **loopback_list_head) {
+ MountPoint *loopback, *loopback_next;
+ int failed = 0;
+
+ LIST_FOREACH_SAFE(mount_point, loopback, loopback_next, *loopback_list_head) {
+ if (delete_loopback(loopback->path) == 0)
+ mount_point_remove_and_free(loopback, loopback_list_head);
+ else {
+ log_debug("Could not delete loopback %s: %m", loopback->path);
+ failed++;
+ }
+ }
+
+ return failed;
+}
+
+int umount_all(void) {
+ int r;
+ LIST_HEAD(MountPoint, mp_list_head);
+
+ LIST_HEAD_INIT(MountPoint, mp_list_head);
+
+ r = mount_points_list_get(&mp_list_head);
+ if (r < 0)
+ goto end;
+
+ r = mount_points_list_umount(&mp_list_head);
+ if (r <= 0)
+ goto end;
+
+ r = mount_points_list_remount_read_only(&mp_list_head);
+
+ end:
+ mount_points_list_free(&mp_list_head);
+
+ return r;
+}
+
+int swapoff_all(void) {
+ int r;
+ LIST_HEAD(MountPoint, swap_list_head);
+
+ LIST_HEAD_INIT(MountPoint, swap_list_head);
+
+ r = swap_list_get(&swap_list_head);
+ if (r < 0)
+ goto end;
+
+ r = swap_points_list_off(&swap_list_head);
+
+ end:
+ mount_points_list_free(&swap_list_head);
+
+ return r;
+}
+
+int loopback_detach_all(void) {
+ int r;
+ LIST_HEAD(MountPoint, loopback_list_head);
+
+ LIST_HEAD_INIT(MountPoint, loopback_list_head);
+
+ r = loopback_list_get(&loopback_list_head);
+ if (r < 0)
+ goto end;
+
+ r = loopback_points_list_detach(&loopback_list_head);
+
+ end:
+ mount_points_list_free(&loopback_list_head);
+
+ return r;
+}
diff --git a/src/umount.h b/src/umount.h
new file mode 100644
index 0000000..aeccc00
--- /dev/null
+++ b/src/umount.h
@@ -0,0 +1,31 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooumounthfoo
+#define fooumounthfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 ProFUSION embedded systems
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+int umount_all(void);
+
+int swapoff_all(void);
+
+int loopback_detach_all(void);
+
+#endif
--
1.7.3
Kay Sievers
2010-10-07 07:24:40 UTC
Permalink
+        if (udev_enumerate_add_match_subsystem(e, "block") < 0) {
+                r = -EIO;
+                goto finish;
+        }
+
+        if (udev_enumerate_scan_devices(e) < 0) {
+                r = -EIO;
+                goto finish;
+        }
+
+        first = udev_enumerate_get_list_entry(e);
+
+        udev_list_entry_foreach(item, first) {
+                MountPoint *lb;
+                char buf[PATH_MAX];
+                char *loop;
+
+                snprintf(buf, sizeof(buf), "%s", udev_list_entry_get_name(item));
+                if (strstr(buf, "loop"))
I think, here you can let libudev return only the loop devices with:
Gustavo Sverzut Barbieri
2010-10-07 10:58:32 UTC
Permalink
+        if (udev_enumerate_add_match_subsystem(e, "block") < 0) {
+                r = -EIO;
+                goto finish;
+        }
+
+        if (udev_enumerate_scan_devices(e) < 0) {
+                r = -EIO;
+                goto finish;
+        }
+
+        first = udev_enumerate_get_list_entry(e);
+
+        udev_list_entry_foreach(item, first) {
+                MountPoint *lb;
+                char buf[PATH_MAX];
+                char *loop;
+
+                snprintf(buf, sizeof(buf), "%s", udev_list_entry_get_name(item));
+                if (strstr(buf, "loop"))
 udev_enumerate_add_match_sysname(, "loop*");
and the snprintf() is totally useless, the strstr case should be early
continue "if (!strstr(..)) continue"
--
Gustavo Sverzut Barbieri
http://profusion.mobi embedded systems
--------------------------------------
MSN: ***@gmail.com
Skype: gsbarbieri
Mobile: +55 (19) 9225-2202
f***@profusion.mobi
2010-10-07 04:03:36 UTC
Permalink
From: Fabiano Fidencio <***@profusion.mobi>

This functions are working as follows:
- Send a SIGTERM to all processes that may be finished
- Send a SIGKILL to all processes that still live and may be finished
- Try to unmount all mount points
- Try to remount read-only all mount points that can't be umounted
- Umount all swap devices
- Umount and detach all loopback devices
- Call [poweroff|halt|reboot|kexec]

TODO:
- Umount device-mapper.
- Make log work. So far it is being useless as we do not parse
/etc/systemd/system.conf, kernel command line but just
environment, however we're executed by init and thus have no
useful variables. Forcing it to target=kmsg/console and
level=debug also does not produce any output, however writing to
/dev/console does work (hack used during debug).
---
Makefile.am | 17 +++
src/shutdown.c | 345 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 362 insertions(+), 0 deletions(-)
create mode 100644 src/shutdown.c

diff --git a/Makefile.am b/Makefile.am
index 4307db1..7be70f4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -35,6 +35,7 @@ tmpfilesdir=$(sysconfdir)/tmpfiles.d
# And these are the special ones for /
rootdir=@rootdir@
rootbindir=$(rootdir)/bin
+rootsbindir=$(rootdir)/sbin
rootlibexecdir=$(rootdir)/lib/systemd
systemunitdir=$(rootdir)/lib/systemd/system

@@ -50,9 +51,11 @@ AM_CPPFLAGS = \
-DSESSION_DATA_UNIT_PATH=\"$(sessionunitdir)\" \
-DCGROUP_AGENT_PATH=\"$(rootlibexecdir)/systemd-cgroups-agent\" \
-DSYSTEMD_BINARY_PATH=\"$(rootbindir)/systemd\" \
+ -DSYSTEMD_SHUTDOWN_BINARY_PATH=\"$(rootlibexecdir)/systemd-shutdown\" \
-DSYSTEMCTL_BINARY_PATH=\"$(rootbindir)/systemctl\" \
-DRUNTIME_DIR=\"$(localstatedir)/run\" \
-DRANDOM_SEED=\"$(localstatedir)/lib/random-seed\" \
+ -DKEXEC_BINARY_PATH=\"$(rootsbindir)/kexec\" \
-I $(top_srcdir)/src

if TARGET_GENTOO
@@ -89,6 +92,7 @@ rootlibexec_PROGRAMS = \
systemd-update-utmp \
systemd-random-seed \
systemd-shutdownd \
+ systemd-shutdown \
systemd-modules-load \
systemd-remount-api-vfs \
systemd-kmsg-syslogd \
@@ -632,6 +636,19 @@ systemd_shutdownd_CFLAGS = \
systemd_shutdownd_LDADD = \
libsystemd-basic.la

+systemd_shutdown_SOURCES = \
+ src/mount-setup.c \
+ src/umount.c \
+ src/shutdown.c
+
+systemd_shutdown_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(UDEV_CFLAGS)
+
+systemd_shutdown_LDADD = \
+ libsystemd-basic.la \
+ $(UDEV_LIBS)
+
systemd_modules_load_SOURCES = \
src/modules-load.c

diff --git a/src/shutdown.c b/src/shutdown.c
new file mode 100644
index 0000000..1bacd56
--- /dev/null
+++ b/src/shutdown.c
@@ -0,0 +1,345 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 ProFUSION embedded systems
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/reboot.h>
+#include <linux/reboot.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "log.h"
+#include "umount.h"
+#include "util.h"
+
+#define TIMEOUT_USEC (5 * USEC_PER_SEC)
+#define FINALIZE_ATTEMPTS 50
+#define FINALIZE_CRITICAL_ATTEMPTS 10
+
+_noreturn_ static void freeze(void) {
+ for (;;)
+ pause();
+}
+
+static bool ignore_proc(pid_t pid) {
+ if (pid == 1)
+ return true;
+
+ /* TODO: add more ignore rules here: device-mapper, etc */
+
+ return false;
+}
+
+static bool is_kernel_thread(pid_t pid)
+{
+ char buf[PATH_MAX];
+ FILE *f;
+ char c;
+ size_t count;
+
+ snprintf(buf, sizeof(buf), "/proc/%lu/cmdline", (unsigned long)pid);
+ f = fopen(buf, "re");
+ if (!f)
+ return true; /* not really, but has the desired effect */
+
+ count = fread(&c, 1, 1, f);
+ fclose(f);
+ return count != 1;
+}
+
+static int killall(int sign) {
+ DIR *dir;
+ struct dirent *d;
+ unsigned int processes = 0;
+
+ if ((dir = opendir("/proc")) == NULL)
+ return -errno;
+
+ while ((d = readdir(dir))) {
+ pid_t pid;
+
+ if (parse_pid(d->d_name, &pid) < 0)
+ continue;
+
+ if (is_kernel_thread(pid))
+ continue;
+
+ if (ignore_proc(pid))
+ continue;
+
+ if (kill(pid, sign) == 0)
+ processes++;
+ else
+ log_warning("Could not kill %d: %m", pid);
+ }
+
+ closedir(dir);
+
+ return processes;
+}
+
+static int send_signal(int sign) {
+ sigset_t mask, oldmask;
+ usec_t until;
+ int processes;
+ struct timespec ts;
+
+ assert_se(sigemptyset(&mask) == 0);
+ assert_se(sigaddset(&mask, SIGCHLD) == 0);
+ if (sigprocmask(SIG_BLOCK, &mask, &oldmask) != 0)
+ return -errno;
+
+ if (kill(-1, SIGSTOP) < 0)
+ log_warning("Failed kill(-1, SIGSTOP): %m");
+
+ processes = killall(sign);
+
+ if (kill(-1, SIGCONT) < 0)
+ log_warning("Failed kill(-1, SIGCONT): %m");
+
+ if (processes <= 0)
+ goto finish;
+
+ until = now(CLOCK_MONOTONIC) + TIMEOUT_USEC;
+ for (;;) {
+ usec_t n = now(CLOCK_MONOTONIC);
+ for (;;) {
+ pid_t pid = waitpid(-1, NULL, WNOHANG);
+ if (pid == 0)
+ break;
+ else if (pid < 0 && errno == ECHILD) {
+ processes = 0;
+ goto finish;
+ }
+
+ if (--processes == 0)
+ goto finish;
+ }
+
+ if (n >= until)
+ goto finish;
+
+ timespec_store(&ts, until - n);
+ if (sigtimedwait(&mask, NULL, &ts) != SIGCHLD)
+ log_warning("Failed: sigtimedwait did not return SIGCHLD: %m");
+ }
+
+finish:
+ sigprocmask(SIG_SETMASK, &oldmask, NULL);
+
+ return processes;
+}
+
+static int rescue_send_signal(int sign) {
+ sigset_t mask, oldmask;
+ usec_t until;
+ struct timespec ts;
+ int r;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGCHLD);
+ if (sigprocmask(SIG_BLOCK, &mask, &oldmask) != 0)
+ return -errno;
+
+ if (kill(-1, SIGSTOP) < 0)
+ log_warning("Failed kill(-1, SIGSTOP): %m");
+
+ r = kill(-1, sign);
+ if (r < 0)
+ log_warning("Failed kill(-1, %d): %m", sign);
+
+ if (kill(-1, SIGCONT) < 0)
+ log_warning("Failed kill(-1, SIGCONT): %m");
+
+ if (r < 0)
+ goto finish;
+
+ until = now(CLOCK_MONOTONIC) + TIMEOUT_USEC;
+ for (;;) {
+ usec_t n = now(CLOCK_MONOTONIC);
+ for (;;) {
+ pid_t pid = waitpid(-1, NULL, WNOHANG);
+ if (pid == 0)
+ break;
+ else if (pid < 0 && errno == ECHILD)
+ goto finish;
+ }
+
+ if (n >= until)
+ goto finish;
+
+ timespec_store(&ts, until - n);
+ if (sigtimedwait(&mask, NULL, &ts) != SIGCHLD)
+ log_warning("Failed: sigtimedwait did not return SIGCHLD: %m");
+ }
+
+finish:
+ sigprocmask(SIG_SETMASK, &oldmask, NULL);
+
+ return r;
+}
+
+
+int main(int argc, char *argv[]) {
+ int cmd, r, retries;
+ bool need_umount = true, need_swapoff = true, need_loop_detach = true;
+
+ log_parse_environment();
+ log_set_target(LOG_TARGET_KMSG); /* syslog will die if not gone yet */
+ log_open();
+
+ if (getpid() != 1) {
+ log_error("Not executed by init (pid-1).");
+ r = -EPERM;
+ goto error;
+ }
+
+ if (argc != 2) {
+ log_error("Invalid number of arguments.");
+ r = -EINVAL;
+ goto error;
+ }
+
+ if (streq(argv[1], "reboot"))
+ cmd = RB_AUTOBOOT;
+ else if (streq(argv[1], "poweroff"))
+ cmd = RB_POWER_OFF;
+ else if (streq(argv[1], "halt"))
+ cmd = RB_HALT_SYSTEM;
+ else if (streq(argv[1], "kexec"))
+ cmd = LINUX_REBOOT_CMD_KEXEC;
+ else {
+ log_error("Unknown action '%s'.", argv[1]);
+ r = -EINVAL;
+ goto error;
+ }
+
+ /* lock us into memory */
+ if (mlockall(MCL_CURRENT|MCL_FUTURE) != 0)
+ log_warning("Cannot lock process memory: %m");
+
+ log_info("Sending SIGTERM to processes");
+ r = send_signal(SIGTERM);
+ if (r < 0)
+ log_warning("Cannot send SIGTERM to all process: %s", strerror(r));
+
+ log_info("Sending SIGKILL to processes");
+ r = send_signal(SIGKILL);
+ if (r < 0)
+ log_warning("Cannot send SIGKILL to all process: %s", strerror(r));
+
+
+ /* preventing that we won't block umounts */
+ if (chdir("/") != 0)
+ log_warning("Cannot chdir(\"/\"): %m. Unmounts likely to fail.");
+
+ /* umount all mountpoints, swaps, and loopback devices */
+ retries = FINALIZE_ATTEMPTS;
+ while (need_umount || need_swapoff || need_loop_detach) {
+ if (need_umount) {
+ log_info("Unmounting filesystems.");
+ r = umount_all();
+ if (r == 0)
+ need_umount = false;
+ else if (r > 0)
+ log_warning("Not all filesystems unmounted, %d left.", r);
+ else
+ log_error("Error unmounting filesystems: %s", strerror(-r));
+ }
+
+ if (need_swapoff) {
+ log_info("Disabling swaps.");
+ r = swapoff_all();
+ if (r == 0)
+ need_swapoff = false;
+ else if (r > 0)
+ log_warning("Not all swaps are off, %d left.", r);
+ else
+ log_error("Error turning off swaps: %s", strerror(-r));
+ }
+
+ if (need_loop_detach) {
+ log_info("Detaching loop devices.");
+ r = loopback_detach_all();
+ if (r == 0)
+ need_loop_detach = false;
+ else if (r > 0)
+ log_warning("Not all loop devices detached, %d left.", r);
+ else
+ log_error("Error detaching loop devices: %s", strerror(-r));
+
+ }
+
+ if (need_umount || need_swapoff || need_loop_detach) {
+ retries--;
+
+ if (retries <= FINALIZE_CRITICAL_ATTEMPTS) {
+ log_warning("Approaching critical level to finalize filesystem and devices, try to kill all processes.");
+ rescue_send_signal(SIGTERM);
+ rescue_send_signal(SIGKILL);
+ }
+
+ if (retries > 0)
+ log_info("Action still required, %d tries left", retries);
+ else {
+ log_error("Tried enough but still action required need_umount=%d, need_swapoff=%d, need_loop_detach=%d", need_umount, need_swapoff, need_loop_detach);
+ r = -EBUSY;
+ goto error;
+ }
+ }
+ }
+
+ sync();
+
+ if (cmd == LINUX_REBOOT_CMD_KEXEC) {
+ /* we cheat and exec kexec to avoid doing all its work */
+ pid_t pid = fork();
+ if (pid < 0) {
+ log_error("Could not fork: %m. Falling back to reboot.");
+ cmd = RB_AUTOBOOT;
+ } else if (pid > 0) {
+ waitpid(pid, NULL, 0);
+ log_warning("Failed %s -e -x -f. Falling back to reboot", KEXEC_BINARY_PATH);
+ cmd = RB_AUTOBOOT;
+ } else {
+ const char *args[5] = {KEXEC_BINARY_PATH, "-e", "-x", "-f", NULL};
+ execv(args[0], (char * const *) args);
+ return EXIT_FAILURE;
+ }
+ }
+
+ reboot(cmd);
+ r = errno;
+
+ error:
+ sync();
+ if (r < 0)
+ r = -r;
+ log_error("Critical error while doing system shutdown: %s", strerror(r));
+ freeze();
+ return 0;
+}
--
1.7.3
f***@profusion.mobi
2010-10-07 04:03:37 UTC
Permalink
From: Fabiano Fidencio <***@profusion.mobi>

---
README | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/README b/README
index 430d091..12dc9fe 100644
--- a/README
+++ b/README
@@ -28,7 +28,7 @@ AUTHOR:

REQUIREMENTS:
Linux kernel >= 2.6.30 (with autofs4, devtmpfs, cgroups)
- libudev >= 151
+ libudev >= 160
libcgroup >= 0.35
vala >= 0.80
gtk+ >= 2.20
--
1.7.3
f***@profusion.mobi
2010-10-07 11:24:39 UTC
Permalink
From: Fabiano Fidencio <***@profusion.mobi>

As we have streq to repesent (strcmp(a, b) == 0),I'm adding
strneq to represent (strncmp(a, b, n) == 0), that will be used
in umount.c (at least).
---
src/util.h | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/src/util.h b/src/util.h
index f21aecf..618e7cf 100644
--- a/src/util.h
+++ b/src/util.h
@@ -82,6 +82,7 @@ usec_t timeval_load(const struct timeval *tv);
struct timeval *timeval_store(struct timeval *tv, usec_t u);

#define streq(a,b) (strcmp((a),(b)) == 0)
+#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0)

bool streq_ptr(const char *a, const char *b);
--
1.7.3
f***@profusion.mobi
2010-10-07 11:24:41 UTC
Permalink
From: Fabiano Fidencio <***@profusion.mobi>

This functions are working as follows:
- Send a SIGTERM to all processes that may be finished
- Send a SIGKILL to all processes that still live and may be finished
- Try to unmount all mount points
- Try to remount read-only all mount points that can't be umounted
- Umount all swap devices
- Umount and detach all loopback devices
- Call [poweroff|halt|reboot|kexec]

TODO:
- Umount device-mapper.
- Make log work. So far it is being useless as we do not parse
/etc/systemd/system.conf, kernel command line but just
environment, however we're executed by init and thus have no
useful variables. Forcing it to target=kmsg/console and
level=debug also does not produce any output, however writing to
/dev/console does work (hack used during debug).
---
Makefile.am | 17 +++
src/shutdown.c | 345 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 362 insertions(+), 0 deletions(-)
create mode 100644 src/shutdown.c

diff --git a/Makefile.am b/Makefile.am
index 4307db1..7be70f4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -35,6 +35,7 @@ tmpfilesdir=$(sysconfdir)/tmpfiles.d
# And these are the special ones for /
rootdir=@rootdir@
rootbindir=$(rootdir)/bin
+rootsbindir=$(rootdir)/sbin
rootlibexecdir=$(rootdir)/lib/systemd
systemunitdir=$(rootdir)/lib/systemd/system

@@ -50,9 +51,11 @@ AM_CPPFLAGS = \
-DSESSION_DATA_UNIT_PATH=\"$(sessionunitdir)\" \
-DCGROUP_AGENT_PATH=\"$(rootlibexecdir)/systemd-cgroups-agent\" \
-DSYSTEMD_BINARY_PATH=\"$(rootbindir)/systemd\" \
+ -DSYSTEMD_SHUTDOWN_BINARY_PATH=\"$(rootlibexecdir)/systemd-shutdown\" \
-DSYSTEMCTL_BINARY_PATH=\"$(rootbindir)/systemctl\" \
-DRUNTIME_DIR=\"$(localstatedir)/run\" \
-DRANDOM_SEED=\"$(localstatedir)/lib/random-seed\" \
+ -DKEXEC_BINARY_PATH=\"$(rootsbindir)/kexec\" \
-I $(top_srcdir)/src

if TARGET_GENTOO
@@ -89,6 +92,7 @@ rootlibexec_PROGRAMS = \
systemd-update-utmp \
systemd-random-seed \
systemd-shutdownd \
+ systemd-shutdown \
systemd-modules-load \
systemd-remount-api-vfs \
systemd-kmsg-syslogd \
@@ -632,6 +636,19 @@ systemd_shutdownd_CFLAGS = \
systemd_shutdownd_LDADD = \
libsystemd-basic.la

+systemd_shutdown_SOURCES = \
+ src/mount-setup.c \
+ src/umount.c \
+ src/shutdown.c
+
+systemd_shutdown_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(UDEV_CFLAGS)
+
+systemd_shutdown_LDADD = \
+ libsystemd-basic.la \
+ $(UDEV_LIBS)
+
systemd_modules_load_SOURCES = \
src/modules-load.c

diff --git a/src/shutdown.c b/src/shutdown.c
new file mode 100644
index 0000000..023648c
--- /dev/null
+++ b/src/shutdown.c
@@ -0,0 +1,345 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 ProFUSION embedded systems
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/reboot.h>
+#include <linux/reboot.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "log.h"
+#include "umount.h"
+#include "util.h"
+
+#define TIMEOUT_USEC (5 * USEC_PER_SEC)
+#define FINALIZE_ATTEMPTS 50
+#define FINALIZE_CRITICAL_ATTEMPTS 10
+
+_noreturn_ static void freeze(void) {
+ for (;;)
+ pause();
+}
+
+static bool ignore_proc(pid_t pid) {
+ if (pid == 1)
+ return true;
+
+ /* TODO: add more ignore rules here: device-mapper, etc */
+
+ return false;
+}
+
+static bool is_kernel_thread(pid_t pid)
+{
+ char buf[PATH_MAX];
+ FILE *f;
+ char c;
+ size_t count;
+
+ snprintf(buf, sizeof(buf), "/proc/%lu/cmdline", (unsigned long)pid);
+ f = fopen(buf, "re");
+ if (!f)
+ return true; /* not really, but has the desired effect */
+
+ count = fread(&c, 1, 1, f);
+ fclose(f);
+ return count != 1;
+}
+
+static int killall(int sign) {
+ DIR *dir;
+ struct dirent *d;
+ unsigned int processes = 0;
+
+ if ((dir = opendir("/proc")) == NULL)
+ return -errno;
+
+ while ((d = readdir(dir))) {
+ pid_t pid;
+
+ if (parse_pid(d->d_name, &pid) < 0)
+ continue;
+
+ if (is_kernel_thread(pid))
+ continue;
+
+ if (ignore_proc(pid))
+ continue;
+
+ if (kill(pid, sign) == 0)
+ processes++;
+ else
+ log_warning("Could not kill %d: %m", pid);
+ }
+
+ closedir(dir);
+
+ return processes;
+}
+
+static int send_signal(int sign) {
+ sigset_t mask, oldmask;
+ usec_t until;
+ int processes;
+ struct timespec ts;
+
+ assert_se(sigemptyset(&mask) == 0);
+ assert_se(sigaddset(&mask, SIGCHLD) == 0);
+ if (sigprocmask(SIG_BLOCK, &mask, &oldmask) != 0)
+ return -errno;
+
+ if (kill(-1, SIGSTOP) < 0)
+ log_warning("Failed kill(-1, SIGSTOP): %m");
+
+ processes = killall(sign);
+
+ if (kill(-1, SIGCONT) < 0)
+ log_warning("Failed kill(-1, SIGCONT): %m");
+
+ if (processes <= 0)
+ goto finish;
+
+ until = now(CLOCK_MONOTONIC) + TIMEOUT_USEC;
+ for (;;) {
+ usec_t n = now(CLOCK_MONOTONIC);
+ for (;;) {
+ pid_t pid = waitpid(-1, NULL, WNOHANG);
+ if (pid == 0)
+ break;
+ else if (pid < 0 && errno == ECHILD) {
+ processes = 0;
+ goto finish;
+ }
+
+ if (--processes == 0)
+ goto finish;
+ }
+
+ if (n >= until)
+ goto finish;
+
+ timespec_store(&ts, until - n);
+ if (sigtimedwait(&mask, NULL, &ts) != SIGCHLD)
+ log_warning("Failed: sigtimedwait did not return SIGCHLD: %m");
+ }
+
+finish:
+ sigprocmask(SIG_SETMASK, &oldmask, NULL);
+
+ return processes;
+}
+
+static int rescue_send_signal(int sign) {
+ sigset_t mask, oldmask;
+ usec_t until;
+ struct timespec ts;
+ int r;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGCHLD);
+ if (sigprocmask(SIG_BLOCK, &mask, &oldmask) != 0)
+ return -errno;
+
+ if (kill(-1, SIGSTOP) < 0)
+ log_warning("Failed kill(-1, SIGSTOP): %m");
+
+ r = kill(-1, sign);
+ if (r < 0)
+ log_warning("Failed kill(-1, %d): %m", sign);
+
+ if (kill(-1, SIGCONT) < 0)
+ log_warning("Failed kill(-1, SIGCONT): %m");
+
+ if (r < 0)
+ goto finish;
+
+ until = now(CLOCK_MONOTONIC) + TIMEOUT_USEC;
+ for (;;) {
+ usec_t n = now(CLOCK_MONOTONIC);
+ for (;;) {
+ pid_t pid = waitpid(-1, NULL, WNOHANG);
+ if (pid == 0)
+ break;
+ else if (pid < 0 && errno == ECHILD)
+ goto finish;
+ }
+
+ if (n >= until)
+ goto finish;
+
+ timespec_store(&ts, until - n);
+ if (sigtimedwait(&mask, NULL, &ts) != SIGCHLD)
+ log_warning("Failed: sigtimedwait did not return SIGCHLD: %m");
+ }
+
+finish:
+ sigprocmask(SIG_SETMASK, &oldmask, NULL);
+
+ return r;
+}
+
+
+int main(int argc, char *argv[]) {
+ int cmd, r, retries;
+ bool need_umount = true, need_swapoff = true, need_loop_detach = true;
+
+ log_parse_environment();
+ log_set_target(LOG_TARGET_KMSG); /* syslog will die if not gone yet */
+ log_open();
+
+ if (getpid() != 1) {
+ log_error("Not executed by init (pid-1).");
+ r = -EPERM;
+ goto error;
+ }
+
+ if (argc != 2) {
+ log_error("Invalid number of arguments.");
+ r = -EINVAL;
+ goto error;
+ }
+
+ if (streq(argv[1], "reboot"))
+ cmd = RB_AUTOBOOT;
+ else if (streq(argv[1], "poweroff"))
+ cmd = RB_POWER_OFF;
+ else if (streq(argv[1], "halt"))
+ cmd = RB_HALT_SYSTEM;
+ else if (streq(argv[1], "kexec"))
+ cmd = LINUX_REBOOT_CMD_KEXEC;
+ else {
+ log_error("Unknown action '%s'.", argv[1]);
+ r = -EINVAL;
+ goto error;
+ }
+
+ /* lock us into memory */
+ if (mlockall(MCL_CURRENT|MCL_FUTURE) != 0)
+ log_warning("Cannot lock process memory: %m");
+
+ log_info("Sending SIGTERM to processes");
+ r = send_signal(SIGTERM);
+ if (r < 0)
+ log_warning("Cannot send SIGTERM to all process: %s", strerror(r));
+
+ log_info("Sending SIGKILL to processes");
+ r = send_signal(SIGKILL);
+ if (r < 0)
+ log_warning("Cannot send SIGKILL to all process: %s", strerror(r));
+
+
+ /* preventing that we won't block umounts */
+ if (chdir("/") != 0)
+ log_warning("Cannot chdir(\"/\"): %m. Unmounts likely to fail.");
+
+ /* umount all mountpoints, swaps, and loopback devices */
+ retries = FINALIZE_ATTEMPTS;
+ while (need_umount || need_swapoff || need_loop_detach) {
+ if (need_umount) {
+ log_info("Unmounting filesystems.");
+ r = umount_all();
+ if (r == 0)
+ need_umount = false;
+ else if (r > 0)
+ log_warning("Not all filesystems unmounted, %d left.", r);
+ else
+ log_error("Error unmounting filesystems: %s", strerror(-r));
+ }
+
+ if (need_swapoff) {
+ log_info("Disabling swaps.");
+ r = swapoff_all();
+ if (r == 0)
+ need_swapoff = false;
+ else if (r > 0)
+ log_warning("Not all swaps are off, %d left.", r);
+ else
+ log_error("Error turning off swaps: %s", strerror(-r));
+ }
+
+ if (need_loop_detach) {
+ log_info("Detaching loop devices.");
+ r = loopback_detach_all();
+ if (r == 0)
+ need_loop_detach = false;
+ else if (r > 0)
+ log_warning("Not all loop devices detached, %d left.", r);
+ else
+ log_error("Error detaching loop devices: %s", strerror(-r));
+
+ }
+
+ if (need_umount || need_swapoff || need_loop_detach) {
+ retries--;
+
+ if (retries <= FINALIZE_CRITICAL_ATTEMPTS) {
+ log_warning("Approaching critical level to finalize filesystem and devices, try to kill all processes.");
+ rescue_send_signal(SIGTERM);
+ rescue_send_signal(SIGKILL);
+ }
+
+ if (retries > 0)
+ log_info("Action still required, %d tries left", retries);
+ else {
+ log_error("Tried enough but still action required need_umount=%d, need_swapoff=%d, need_loop_detach=%d", need_umount, need_swapoff, need_loop_detach);
+ r = -EBUSY;
+ goto error;
+ }
+ }
+ }
+
+ sync();
+
+ if (cmd == LINUX_REBOOT_CMD_KEXEC) {
+ /* we cheat and exec kexec to avoid doing all its work */
+ pid_t pid = fork();
+ if (pid < 0) {
+ log_error("Could not fork: %m. Falling back to reboot.");
+ cmd = RB_AUTOBOOT;
+ } else if (pid > 0) {
+ waitpid(pid, NULL, 0);
+ log_warning("Failed %s -e -x -f. Falling back to reboot", KEXEC_BINARY_PATH);
+ cmd = RB_AUTOBOOT;
+ } else {
+ const char *args[5] = {KEXEC_BINARY_PATH, "-e", "-f", "-x", NULL};
+ execv(args[0], (char * const *) args);
+ return EXIT_FAILURE;
+ }
+ }
+
+ reboot(cmd);
+ r = errno;
+
+ error:
+ sync();
+ if (r < 0)
+ r = -r;
+ log_error("Critical error while doing system shutdown: %s", strerror(r));
+ freeze();
+ return 0;
+}
--
1.7.3
f***@profusion.mobi
2010-10-07 11:24:42 UTC
Permalink
From: Fabiano Fidencio <***@profusion.mobi>

---
README | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/README b/README
index 430d091..12dc9fe 100644
--- a/README
+++ b/README
@@ -28,7 +28,7 @@ AUTHOR:

REQUIREMENTS:
Linux kernel >= 2.6.30 (with autofs4, devtmpfs, cgroups)
- libudev >= 151
+ libudev >= 160
libcgroup >= 0.35
vala >= 0.80
gtk+ >= 2.20
--
1.7.3
Lennart Poettering
2010-10-07 17:25:26 UTC
Permalink
Merged all four patches! Thanks a lot of your work. This is quite a
valuable contribution!

Thanks,

Lennart
--
Lennart Poettering - Red Hat, Inc.
f***@profusion.mobi
2010-10-07 11:24:40 UTC
Permalink
From: Fabiano Fidencio <***@profusion.mobi>

This functions will:
- umount all mount points that aren't API
- remount read-only all mount points that can't be umounted
- umount all swap devices.
- detach all loopback devices

TODO:
- umount dms

Mountpoints are being read from /proc/self/mountinfo.
Swaps are being read from /proc/swaps.
Loop devices from /sys/class/block/loop*.
---
.gitignore | 1 +
src/umount.c | 406 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/umount.h | 31 +++++
3 files changed, 438 insertions(+), 0 deletions(-)
create mode 100644 src/umount.c
create mode 100644 src/umount.h

diff --git a/.gitignore b/.gitignore
index 9ba0758..2ba000c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+systemd-shutdown
systemd-tmpfiles
systemd-readahead-collect
systemd-readahead-replay
diff --git a/src/umount.c b/src/umount.c
new file mode 100644
index 0000000..a4f9558
--- /dev/null
+++ b/src/umount.c
@@ -0,0 +1,406 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 ProFUSION embedded systems
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/swap.h>
+#include <unistd.h>
+#include <linux/loop.h>
+#include <libudev.h>
+
+#include "list.h"
+#include "mount-setup.h"
+#include "umount.h"
+#include "util.h"
+
+typedef struct MountPoint {
+ char *path;
+ bool read_only;
+ LIST_FIELDS (struct MountPoint, mount_point);
+} MountPoint;
+
+static MountPoint *mount_point_alloc(char *path) {
+ MountPoint *mp;
+
+ if (!(mp = new(MountPoint, 1)))
+ return NULL;
+
+ mp->path = path;
+ mp->read_only = false;
+
+ return mp;
+}
+
+static void mount_point_remove_and_free(MountPoint *mount_point, MountPoint **mount_point_list_head) {
+ LIST_REMOVE(MountPoint, mount_point, *mount_point_list_head, mount_point);
+
+ free(mount_point->path);
+ free(mount_point);
+}
+
+static void mount_points_list_free(MountPoint **mount_point_list_head) {
+ while (*mount_point_list_head)
+ mount_point_remove_and_free(*mount_point_list_head, mount_point_list_head);
+}
+
+static int mount_points_list_get(MountPoint **mount_point_list_head) {
+ FILE *proc_self_mountinfo;
+ char *path, *p;
+ unsigned int i;
+ int r;
+
+ if (!(proc_self_mountinfo = fopen("/proc/self/mountinfo", "re")))
+ return -errno;
+
+ for (i = 1;; i++) {
+ int k;
+ MountPoint *mp;
+
+ path = p = NULL;
+
+ if ((k = fscanf(proc_self_mountinfo,
+ "%*s " /* (1) mount id */
+ "%*s " /* (2) parent id */
+ "%*s " /* (3) major:minor */
+ "%*s " /* (4) root */
+ "%ms " /* (5) mount point */
+ "%*s" /* (6) mount options */
+ "%*[^-]" /* (7) optional fields */
+ "- " /* (8) separator */
+ "%*s " /* (9) file system type */
+ "%*s" /* (10) mount source */
+ "%*s" /* (11) mount options 2 */
+ "%*[^\n]", /* some rubbish at the end */
+ &path)) != 1) {
+ if (k == EOF)
+ break;
+
+ log_warning("Failed to parse /proc/self/mountinfo:%u.", i);
+
+ free(path);
+ continue;
+ }
+
+ if (mount_point_is_api(path)) {
+ free(path);
+ continue;
+ }
+
+ if (!(p = cunescape(path))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(mp = mount_point_alloc(p))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+ LIST_PREPEND(MountPoint, mount_point, *mount_point_list_head, mp);
+
+ free(path);
+ }
+
+ r = 0;
+
+finish:
+ fclose(proc_self_mountinfo);
+
+ free(path);
+
+ return r;
+}
+
+static int swap_list_get(MountPoint **swap_list_head) {
+ FILE *proc_swaps;
+ unsigned int i;
+ int r;
+
+ if (!(proc_swaps = fopen("/proc/swaps", "re")))
+ return -errno;
+
+ (void) fscanf(proc_swaps, "%*s %*s %*s %*s %*s\n");
+
+ for (i = 2;; i++) {
+ MountPoint *swap;
+ char *dev = NULL, *d;
+ int k;
+
+ if ((k = fscanf(proc_swaps,
+ "%ms " /* device/file */
+ "%*s " /* type of swap */
+ "%*s " /* swap size */
+ "%*s " /* used */
+ "%*s\n", /* priority */
+ &dev)) != 1) {
+
+ if (k == EOF)
+ break;
+
+ log_warning("Failed to parse /proc/swaps:%u.", i);
+
+ free(dev);
+ continue;
+ }
+
+ if (endswith(dev, "(deleted)")) {
+ free(dev);
+ continue;
+ }
+
+ d = cunescape(dev);
+ free(dev);
+
+ if (!d) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ swap = mount_point_alloc(d);
+ if (!swap) {
+ free(d);
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ LIST_PREPEND(MountPoint, mount_point, *swap_list_head, swap);
+ }
+
+ r = 0;
+
+finish:
+ fclose(proc_swaps);
+
+ return r;
+}
+
+static int loopback_list_get(MountPoint **loopback_list_head) {
+ int r;
+ struct udev *udev;
+ struct udev_enumerate *e = NULL;
+ struct udev_list_entry *item = NULL, *first = NULL;
+
+ if (!(udev = udev_new())) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(e = udev_enumerate_new(udev))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (udev_enumerate_add_match_sysname(e, "loop*") < 0) {
+ r = -EIO;
+ goto finish;
+ }
+
+ if (udev_enumerate_scan_devices(e) < 0) {
+ r = -EIO;
+ goto finish;
+ }
+
+ first = udev_enumerate_get_list_entry(e);
+
+ udev_list_entry_foreach(item, first) {
+ MountPoint *lb;
+ char buf[PATH_MAX];
+ char *loop;
+
+ loop = cunescape(udev_list_entry_get_name(item));
+ if (!loop) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ lb = mount_point_alloc(loop);
+ if (!lb) {
+ free(loop);
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ LIST_PREPEND(MountPoint, mount_point, *loopback_list_head, lb);
+ }
+
+ r = 0;
+
+finish:
+ if (e)
+ udev_enumerate_unref(e);
+
+ free(udev);
+ return r;
+}
+
+static int delete_loopback(const char *device) {
+ int fd, r;
+
+ if ((fd = open(device, O_RDONLY|O_CLOEXEC)) < 0) {
+ if (errno == ENOENT) {
+ log_debug("Loop device %s does not exist.", device);
+ errno = 0;
+ return 0;
+ }
+ return -errno;
+ }
+
+ ioctl(fd, LOOP_CLR_FD, 0);
+ r = errno;
+ close_nointr(fd);
+
+ if (r == ENXIO) /* not bound, so no error */
+ r = 0;
+ errno = r;
+ return -errno;
+}
+
+static int mount_points_list_umount(MountPoint **mount_point_list_head) {
+ MountPoint *mp, *mp_next;
+ int failed = 0;
+
+ LIST_FOREACH_SAFE(mount_point, mp, mp_next, *mount_point_list_head) {
+ if (streq(mp->path, "/"))
+ continue;
+
+ /* Trying to umount. Forcing to umount if busy (only for NFS mounts) */
+ if (umount2(mp->path, MNT_FORCE) == 0)
+ mount_point_remove_and_free(mp, mount_point_list_head);
+ else {
+ log_debug("Could not unmount %s: %m", mp->path);
+ failed++;
+ }
+ }
+
+ return failed;
+}
+
+static int mount_points_list_remount_read_only(MountPoint **mount_point_list_head) {
+ MountPoint *mp, *mp_next;
+ int failed = 0;
+
+ LIST_FOREACH_SAFE(mount_point, mp, mp_next, *mount_point_list_head) {
+ if (mp->read_only)
+ continue;
+
+ /* Trying to remount read-only */
+ if (mount(NULL, mp->path, NULL, MS_MGC_VAL|MS_REMOUNT|MS_RDONLY, NULL) == 0) {
+ mp->read_only = true;
+ mount_point_remove_and_free(mp, mount_point_list_head);
+ } else {
+ log_debug("Could not remount as read-only %s: %m", mp->path);
+ failed++;
+ }
+ }
+
+ return failed;
+}
+
+static int swap_points_list_off(MountPoint **swap_list_head) {
+ MountPoint *swap, *swap_next;
+ int failed = 0;
+
+ LIST_FOREACH_SAFE(mount_point, swap, swap_next, *swap_list_head) {
+ if (swapoff(swap->path) == 0)
+ mount_point_remove_and_free(swap, swap_list_head);
+ else {
+ log_debug("Could not swapoff %s: %m", swap->path);
+ failed++;
+ }
+ }
+
+ return failed;
+}
+
+static int loopback_points_list_detach(MountPoint **loopback_list_head) {
+ MountPoint *loopback, *loopback_next;
+ int failed = 0;
+
+ LIST_FOREACH_SAFE(mount_point, loopback, loopback_next, *loopback_list_head) {
+ if (delete_loopback(loopback->path) == 0)
+ mount_point_remove_and_free(loopback, loopback_list_head);
+ else {
+ log_debug("Could not delete loopback %s: %m", loopback->path);
+ failed++;
+ }
+ }
+
+ return failed;
+}
+
+int umount_all(void) {
+ int r;
+ LIST_HEAD(MountPoint, mp_list_head);
+
+ LIST_HEAD_INIT(MountPoint, mp_list_head);
+
+ r = mount_points_list_get(&mp_list_head);
+ if (r < 0)
+ goto end;
+
+ r = mount_points_list_umount(&mp_list_head);
+ if (r <= 0)
+ goto end;
+
+ r = mount_points_list_remount_read_only(&mp_list_head);
+
+ end:
+ mount_points_list_free(&mp_list_head);
+
+ return r;
+}
+
+int swapoff_all(void) {
+ int r;
+ LIST_HEAD(MountPoint, swap_list_head);
+
+ LIST_HEAD_INIT(MountPoint, swap_list_head);
+
+ r = swap_list_get(&swap_list_head);
+ if (r < 0)
+ goto end;
+
+ r = swap_points_list_off(&swap_list_head);
+
+ end:
+ mount_points_list_free(&swap_list_head);
+
+ return r;
+}
+
+int loopback_detach_all(void) {
+ int r;
+ LIST_HEAD(MountPoint, loopback_list_head);
+
+ LIST_HEAD_INIT(MountPoint, loopback_list_head);
+
+ r = loopback_list_get(&loopback_list_head);
+ if (r < 0)
+ goto end;
+
+ r = loopback_points_list_detach(&loopback_list_head);
+
+ end:
+ mount_points_list_free(&loopback_list_head);
+
+ return r;
+}
diff --git a/src/umount.h b/src/umount.h
new file mode 100644
index 0000000..aeccc00
--- /dev/null
+++ b/src/umount.h
@@ -0,0 +1,31 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooumounthfoo
+#define fooumounthfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 ProFUSION embedded systems
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+int umount_all(void);
+
+int swapoff_all(void);
+
+int loopback_detach_all(void);
+
+#endif
--
1.7.3
Kay Sievers
2010-10-07 11:33:44 UTC
Permalink
Post by f***@profusion.mobi
+static int loopback_list_get(MountPoint **loopback_list_head) {
+        int r;
+        struct udev *udev;
+        struct udev_enumerate *e = NULL;
+        struct udev_list_entry *item = NULL, *first = NULL;
+
+        if (!(udev = udev_new())) {
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (!(e = udev_enumerate_new(udev))) {
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (udev_enumerate_add_match_sysname(e, "loop*") < 0) {
+                r = -EIO;
+                goto finish;
+        }
Oh, please don't drop the "block" match. :) It will iterate over the
entire sysfs ot
Fabiano Fidêncio
2010-10-07 11:37:56 UTC
Permalink
Post by Kay Sievers
Post by f***@profusion.mobi
+static int loopback_list_get(MountPoint **loopback_list_head) {
+        int r;
+        struct udev *udev;
+        struct udev_enumerate *e = NULL;
+        struct udev_list_entry *item = NULL, *first = NULL;
+
+        if (!(udev = udev_new())) {
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (!(e = udev_enumerate_new(udev))) {
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (udev_enumerate_add_match_sysname(e, "loop*") < 0) {
+                r = -EIO;
+                goto finish;
+        }
Oh, please don't drop the "block" match. :) It will iterate over the
entire sysfs otherwise.
Oh, sorry,
Post by Kay Sievers
Kay
--
Fabiano Fidêncio
ProFUSION embedded systems
http://www.profusion.mobi
f***@profusion.mobi
2010-10-07 12:43:57 UTC
Permalink
From: Fabiano Fidencio <***@profusion.mobi>

This functions will:
- umount all mount points that aren't API
- remount read-only all mount points that can't be umounted
- umount all swap devices.
- detach all loopback devices

TODO:
- umount dms

Mountpoints are being read from /proc/self/mountinfo.
Swaps are being read from /proc/swaps.
Loop devices from /sys/class/block/loop*.
---
.gitignore | 1 +
src/umount.c | 410 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/umount.h | 31 +++++
3 files changed, 442 insertions(+), 0 deletions(-)
create mode 100644 src/umount.c
create mode 100644 src/umount.h

diff --git a/.gitignore b/.gitignore
index 9ba0758..2ba000c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+systemd-shutdown
systemd-tmpfiles
systemd-readahead-collect
systemd-readahead-replay
diff --git a/src/umount.c b/src/umount.c
new file mode 100644
index 0000000..4842d40
--- /dev/null
+++ b/src/umount.c
@@ -0,0 +1,410 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 ProFUSION embedded systems
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/swap.h>
+#include <unistd.h>
+#include <linux/loop.h>
+#include <libudev.h>
+
+#include "list.h"
+#include "mount-setup.h"
+#include "umount.h"
+#include "util.h"
+
+typedef struct MountPoint {
+ char *path;
+ bool read_only;
+ LIST_FIELDS (struct MountPoint, mount_point);
+} MountPoint;
+
+static MountPoint *mount_point_alloc(char *path) {
+ MountPoint *mp;
+
+ if (!(mp = new(MountPoint, 1)))
+ return NULL;
+
+ mp->path = path;
+ mp->read_only = false;
+
+ return mp;
+}
+
+static void mount_point_remove_and_free(MountPoint *mount_point, MountPoint **mount_point_list_head) {
+ LIST_REMOVE(MountPoint, mount_point, *mount_point_list_head, mount_point);
+
+ free(mount_point->path);
+ free(mount_point);
+}
+
+static void mount_points_list_free(MountPoint **mount_point_list_head) {
+ while (*mount_point_list_head)
+ mount_point_remove_and_free(*mount_point_list_head, mount_point_list_head);
+}
+
+static int mount_points_list_get(MountPoint **mount_point_list_head) {
+ FILE *proc_self_mountinfo;
+ char *path, *p;
+ unsigned int i;
+ int r;
+
+ if (!(proc_self_mountinfo = fopen("/proc/self/mountinfo", "re")))
+ return -errno;
+
+ for (i = 1;; i++) {
+ int k;
+ MountPoint *mp;
+
+ path = p = NULL;
+
+ if ((k = fscanf(proc_self_mountinfo,
+ "%*s " /* (1) mount id */
+ "%*s " /* (2) parent id */
+ "%*s " /* (3) major:minor */
+ "%*s " /* (4) root */
+ "%ms " /* (5) mount point */
+ "%*s" /* (6) mount options */
+ "%*[^-]" /* (7) optional fields */
+ "- " /* (8) separator */
+ "%*s " /* (9) file system type */
+ "%*s" /* (10) mount source */
+ "%*s" /* (11) mount options 2 */
+ "%*[^\n]", /* some rubbish at the end */
+ &path)) != 1) {
+ if (k == EOF)
+ break;
+
+ log_warning("Failed to parse /proc/self/mountinfo:%u.", i);
+
+ free(path);
+ continue;
+ }
+
+ if (mount_point_is_api(path)) {
+ free(path);
+ continue;
+ }
+
+ if (!(p = cunescape(path))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(mp = mount_point_alloc(p))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+ LIST_PREPEND(MountPoint, mount_point, *mount_point_list_head, mp);
+
+ free(path);
+ }
+
+ r = 0;
+
+finish:
+ fclose(proc_self_mountinfo);
+
+ free(path);
+
+ return r;
+}
+
+static int swap_list_get(MountPoint **swap_list_head) {
+ FILE *proc_swaps;
+ unsigned int i;
+ int r;
+
+ if (!(proc_swaps = fopen("/proc/swaps", "re")))
+ return -errno;
+
+ (void) fscanf(proc_swaps, "%*s %*s %*s %*s %*s\n");
+
+ for (i = 2;; i++) {
+ MountPoint *swap;
+ char *dev = NULL, *d;
+ int k;
+
+ if ((k = fscanf(proc_swaps,
+ "%ms " /* device/file */
+ "%*s " /* type of swap */
+ "%*s " /* swap size */
+ "%*s " /* used */
+ "%*s\n", /* priority */
+ &dev)) != 1) {
+
+ if (k == EOF)
+ break;
+
+ log_warning("Failed to parse /proc/swaps:%u.", i);
+
+ free(dev);
+ continue;
+ }
+
+ if (endswith(dev, "(deleted)")) {
+ free(dev);
+ continue;
+ }
+
+ d = cunescape(dev);
+ free(dev);
+
+ if (!d) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ swap = mount_point_alloc(d);
+ if (!swap) {
+ free(d);
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ LIST_PREPEND(MountPoint, mount_point, *swap_list_head, swap);
+ }
+
+ r = 0;
+
+finish:
+ fclose(proc_swaps);
+
+ return r;
+}
+
+static int loopback_list_get(MountPoint **loopback_list_head) {
+ int r;
+ struct udev *udev;
+ struct udev_enumerate *e = NULL;
+ struct udev_list_entry *item = NULL, *first = NULL;
+
+ if (!(udev = udev_new())) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(e = udev_enumerate_new(udev))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (udev_enumerate_add_match_subsystem(e, "block") < 0) {
+ r = -EIO;
+ goto finish;
+ }
+
+ if (udev_enumerate_add_match_sysname(e, "loop*") < 0) {
+ r = -EIO;
+ goto finish;
+ }
+
+ if (udev_enumerate_scan_devices(e) < 0) {
+ r = -EIO;
+ goto finish;
+ }
+
+ first = udev_enumerate_get_list_entry(e);
+
+ udev_list_entry_foreach(item, first) {
+ MountPoint *lb;
+ char *loop;
+
+ loop = cunescape(udev_list_entry_get_name(item));
+ if (!loop) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ lb = mount_point_alloc(loop);
+ if (!lb) {
+ free(loop);
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ LIST_PREPEND(MountPoint, mount_point, *loopback_list_head, lb);
+ }
+
+ r = 0;
+
+finish:
+ if (e)
+ udev_enumerate_unref(e);
+
+ free(udev);
+ return r;
+}
+
+static int delete_loopback(const char *device) {
+ int fd, r;
+
+ if ((fd = open(device, O_RDONLY|O_CLOEXEC)) < 0) {
+ if (errno == ENOENT) {
+ log_debug("Loop device %s does not exist.", device);
+ errno = 0;
+ return 0;
+ }
+ return -errno;
+ }
+
+ ioctl(fd, LOOP_CLR_FD, 0);
+ r = errno;
+ close_nointr(fd);
+
+ if (r == ENXIO) /* not bound, so no error */
+ r = 0;
+ errno = r;
+ return -errno;
+}
+
+static int mount_points_list_umount(MountPoint **mount_point_list_head) {
+ MountPoint *mp, *mp_next;
+ int failed = 0;
+
+ LIST_FOREACH_SAFE(mount_point, mp, mp_next, *mount_point_list_head) {
+ if (streq(mp->path, "/"))
+ continue;
+
+ /* Trying to umount. Forcing to umount if busy (only for NFS mounts) */
+ if (umount2(mp->path, MNT_FORCE) == 0)
+ mount_point_remove_and_free(mp, mount_point_list_head);
+ else {
+ log_debug("Could not unmount %s: %m", mp->path);
+ failed++;
+ }
+ }
+
+ return failed;
+}
+
+static int mount_points_list_remount_read_only(MountPoint **mount_point_list_head) {
+ MountPoint *mp, *mp_next;
+ int failed = 0;
+
+ LIST_FOREACH_SAFE(mount_point, mp, mp_next, *mount_point_list_head) {
+ if (mp->read_only)
+ continue;
+
+ /* Trying to remount read-only */
+ if (mount(NULL, mp->path, NULL, MS_MGC_VAL|MS_REMOUNT|MS_RDONLY, NULL) == 0) {
+ mp->read_only = true;
+ mount_point_remove_and_free(mp, mount_point_list_head);
+ } else {
+ log_debug("Could not remount as read-only %s: %m", mp->path);
+ failed++;
+ }
+ }
+
+ return failed;
+}
+
+static int swap_points_list_off(MountPoint **swap_list_head) {
+ MountPoint *swap, *swap_next;
+ int failed = 0;
+
+ LIST_FOREACH_SAFE(mount_point, swap, swap_next, *swap_list_head) {
+ if (swapoff(swap->path) == 0)
+ mount_point_remove_and_free(swap, swap_list_head);
+ else {
+ log_debug("Could not swapoff %s: %m", swap->path);
+ failed++;
+ }
+ }
+
+ return failed;
+}
+
+static int loopback_points_list_detach(MountPoint **loopback_list_head) {
+ MountPoint *loopback, *loopback_next;
+ int failed = 0;
+
+ LIST_FOREACH_SAFE(mount_point, loopback, loopback_next, *loopback_list_head) {
+ if (delete_loopback(loopback->path) == 0)
+ mount_point_remove_and_free(loopback, loopback_list_head);
+ else {
+ log_debug("Could not delete loopback %s: %m", loopback->path);
+ failed++;
+ }
+ }
+
+ return failed;
+}
+
+int umount_all(void) {
+ int r;
+ LIST_HEAD(MountPoint, mp_list_head);
+
+ LIST_HEAD_INIT(MountPoint, mp_list_head);
+
+ r = mount_points_list_get(&mp_list_head);
+ if (r < 0)
+ goto end;
+
+ r = mount_points_list_umount(&mp_list_head);
+ if (r <= 0)
+ goto end;
+
+ r = mount_points_list_remount_read_only(&mp_list_head);
+
+ end:
+ mount_points_list_free(&mp_list_head);
+
+ return r;
+}
+
+int swapoff_all(void) {
+ int r;
+ LIST_HEAD(MountPoint, swap_list_head);
+
+ LIST_HEAD_INIT(MountPoint, swap_list_head);
+
+ r = swap_list_get(&swap_list_head);
+ if (r < 0)
+ goto end;
+
+ r = swap_points_list_off(&swap_list_head);
+
+ end:
+ mount_points_list_free(&swap_list_head);
+
+ return r;
+}
+
+int loopback_detach_all(void) {
+ int r;
+ LIST_HEAD(MountPoint, loopback_list_head);
+
+ LIST_HEAD_INIT(MountPoint, loopback_list_head);
+
+ r = loopback_list_get(&loopback_list_head);
+ if (r < 0)
+ goto end;
+
+ r = loopback_points_list_detach(&loopback_list_head);
+
+ end:
+ mount_points_list_free(&loopback_list_head);
+
+ return r;
+}
diff --git a/src/umount.h b/src/umount.h
new file mode 100644
index 0000000..aeccc00
--- /dev/null
+++ b/src/umount.h
@@ -0,0 +1,31 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooumounthfoo
+#define fooumounthfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 ProFUSION embedded systems
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+int umount_all(void);
+
+int swapoff_all(void);
+
+int loopback_detach_all(void);
+
+#endif
--
1.7.3
Koen Kooi
2011-09-22 13:26:51 UTC
Permalink
Sample output: http://dominion.thruhere.net/koen/angstrom/systemd/omap4430-panda-201109221422.svg

Signed-off-by: Koen Kooi <***@dominion.thruhere.net>
---

This is just a start to see if something like this is acceptable, feedback welcome!

src/systemd-analyze | 20 +++++++++++++-------
1 files changed, 13 insertions(+), 7 deletions(-)

diff --git a/src/systemd-analyze b/src/systemd-analyze
index d0db984..480f7aa 100755
--- a/src/systemd-analyze
+++ b/src/systemd-analyze
@@ -116,7 +116,8 @@ elif sys.argv[1] == 'plot':
data = acquire_time_data()
s = sorted(data, key = lambda i: i[1])

- count = 0
+ # start at one to account for the kernel bar
+ count = 1

for name, ixt, aet, axt, iet in s:

@@ -130,7 +131,7 @@ elif sys.argv[1] == 'plot':
bar_space = bar_height * 0.1

# 1000px = 10s, 1px = 10ms
- width = (finish_time - start_time)/10000 + border*2
+ width = (finish_time)/10000 + border*2
height = count * (bar_height + bar_space) + border * 2

if width < 1000:
@@ -147,7 +148,7 @@ elif sys.argv[1] == 'plot':
context.set_line_width(1)
context.set_source_rgb(0.7, 0.7, 0.7)

- for x in range(0, max((finish_time - start_time)/10000,110), 100):
+ for x in range(0, max((finish_time)/10000,110), 100):
context.move_to(x, 0)
context.line_to(x, height-border*2)

@@ -163,11 +164,16 @@ elif sys.argv[1] == 'plot':
banner = "Running on %s (%s %s) %s" % (os.uname()[1], os.uname()[2], os.uname()[3], os.uname()[4])
draw_text(context, 0, -15, banner, hcenter = 0, vcenter = 1)

- for x in range(0, max((finish_time - start_time)/10000,110), 100):
+ for x in range(0, max((finish_time)/10000,110), 100):
draw_text(context, x, -5, "%lus" % (x/100), vcenter = 0, hcenter = 0)

y = 0

+ # draw box for kernel boot time
+ draw_box(context, 0, y, start_time/10000, bar_height, 0.8, 0.6, 0.6)
+ draw_text(context, 10, y + bar_height/2, "kernel", hcenter = 0)
+ y += bar_height + bar_space
+
for name, ixt, aet, axt, iet in s:

drawn = False
@@ -176,7 +182,7 @@ elif sys.argv[1] == 'plot':
if ixt >= start_time and ixt <= finish_time:

# Activating
- a = ixt - start_time
+ a = ixt
b = min(filter(lambda x: x >= ixt, (aet, axt, iet, finish_time))) - ixt

draw_box(context, a/10000, y, b/10000, bar_height, 1, 0, 0)
@@ -188,7 +194,7 @@ elif sys.argv[1] == 'plot':
if aet >= start_time and aet <= finish_time:

# Active
- a = aet - start_time
+ a = aet
b = min(filter(lambda x: x >= aet, (axt, iet, finish_time))) - aet

draw_box(context, a/10000, y, b/10000, bar_height, .8, .6, .6)
@@ -200,7 +206,7 @@ elif sys.argv[1] == 'plot':
if axt >= start_time and axt <= finish_time:

# Deactivating
- a = axt - start_time
+ a = axt
b = min(filter(lambda x: x >= axt, (iet, finish_time))) - axt

draw_box(context, a/10000, y, b/10000, bar_height, .6, .4, .4)
--
1.6.6.1
Lennart Poettering
2011-09-23 14:29:09 UTC
Permalink
Post by Koen Kooi
Sample output: http://dominion.thruhere.net/koen/angstrom/systemd/omap4430-panda-201109221422.svg
I'd be willing to merge this, but only if it also covers initrd!

Lennart
--
Lennart Poettering - Red Hat, Inc.
Koen Kooi
2011-09-25 17:13:08 UTC
Permalink
Post by Lennart Poettering
Post by Koen Kooi
Sample output: http://dominion.thruhere.net/koen/angstrom/systemd/omap4430-panda-201109221422.svg
I'd be willing to merge this, but only if it also covers initrd!
Good, I'll take a stab at that!
t***@jklm.no
2012-06-27 23:05:08 UTC
Permalink
From: Tom Gundersen <***@jklm.no>

---
man/systemd-ask-password-console.service.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/man/systemd-ask-password-console.service.xml b/man/systemd-ask-password-console.service.xml
index 7017c0d..0baf882 100644
--- a/man/systemd-ask-password-console.service.xml
+++ b/man/systemd-ask-password-console.service.xml
@@ -79,7 +79,7 @@
about the system password logic.</para>

<para>Note that these services invoke
- <citerefentry><refentrytitle>systemd-ask-password-agent</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ <citerefentry><refentrytitle>systemd-tty-ask-password-agent</refentrytitle><manvolnum>1</manvolnum></citerefentry>
with the <command>--watch --console</command>
resp. <command>--watch --wall</command> command line
parameters.</para>
@@ -89,7 +89,7 @@
<title>See Also</title>
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>systemd-ask-password-agent</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-tty-ask-password-agent</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>wall</refentrytitle><manvolnum>1</manvolnum></citerefentry>
</para>
</refsect1>
--
1.7.11.1
Lennart Poettering
2012-06-28 11:24:13 UTC
Permalink
Applied! Thanks!

Lennart
--
Lennart Poettering - Red Hat, Inc.
Yang Zhiyong
2013-11-04 03:26:07 UTC
Permalink
This patch adds parameters checking for 'udevadm settle':
- Add extraneous argument checking.
If exist extraneous argument,the program will print stderr and exit.
- Change "seconds"'s type from "int" to "unsigned int".
And use safe_atou() for catching invalid timeout value.
---

src/udev/udevadm-settle.c | 22 +++++++++++++++-------
1 file changed, 15 insertions(+), 7 deletions(-)

diff --git a/src/udev/udevadm-settle.c b/src/udev/udevadm-settle.c
index c4fc4ee..b991da2 100644
--- a/src/udev/udevadm-settle.c
+++ b/src/udev/udevadm-settle.c
@@ -35,6 +35,7 @@
#include <sys/types.h>

#include "udev.h"
+#include "util.h"

static int adm_settle(struct udev *udev, int argc, char *argv[])
{
@@ -56,14 +57,19 @@ static int adm_settle(struct udev *udev, int argc, char *argv[])
struct pollfd pfd[1] = { {.fd = -1}, };
struct udev_queue *udev_queue = NULL;
int rc = EXIT_FAILURE;
-
+ int r = -1;
for (;;) {
int option;
- int seconds;
+ unsigned int seconds;

option = getopt_long(argc, argv, "s:e:t:E:qh", options, NULL);
- if (option == -1)
+ if (option == -1) {
+ if (optind < argc) {
+ fprintf(stderr, "Extraneous argument: '%s'\n",argv[optind]);
+ exit(EXIT_FAILURE);
+ }
break;
+ }

switch (option) {
case 's':
@@ -73,11 +79,13 @@ static int adm_settle(struct udev *udev, int argc, char *argv[])
end = strtoull(optarg, NULL, 0);
break;
case 't':
- seconds = atoi(optarg);
- if (seconds >= 0)
+ r = safe_atou(optarg,&seconds);
+ if (r < 0) {
+ fprintf(stderr, "Invalid timeout value '%s': %s\n", optarg, strerror(-r));
+ exit(EXIT_FAILURE);
+ } else {
timeout = seconds;
- else
- fprintf(stderr, "invalid timeout value\n");
+ }
break;
case 'q':
quiet = 1;
--
1.8.1
Loading...