chiark / gitweb /
tree-wide: remove Lennart's copyright lines
[elogind.git] / src / shared / clean-ipc.c
index 8c60bdc1f2be77ae301b7d95a96ccde5ac87690d..52b7bb0e309cd4bb315dec0508ab5f02f230b000 100644 (file)
@@ -1,21 +1,4 @@
-/***
-  This file is part of systemd.
-
-  Copyright 2014 Lennart Poettering
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 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
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
+/* SPDX-License-Identifier: LGPL-2.1+ */
 
 #include <dirent.h>
 #include <errno.h>
@@ -36,7 +19,7 @@
 #include "dirent-util.h"
 #include "fd-util.h"
 #include "fileio.h"
-#include "formats-util.h"
+#include "format-util.h"
 #include "log.h"
 #include "macro.h"
 #include "string-util.h"
@@ -54,7 +37,7 @@ static bool match_uid_gid(uid_t subject_uid, gid_t subject_gid, uid_t delete_uid
         return false;
 }
 
-static int clean_sysvipc_shm(uid_t delete_uid, gid_t delete_gid) {
+static int clean_sysvipc_shm(uid_t delete_uid, gid_t delete_gid, bool rm) {
         _cleanup_fclose_ FILE *f = NULL;
         char line[LINE_MAX];
         bool first = true;
@@ -92,17 +75,23 @@ static int clean_sysvipc_shm(uid_t delete_uid, gid_t delete_gid) {
                 if (!match_uid_gid(uid, gid, delete_uid, delete_gid))
                         continue;
 
+                if (!rm)
+                        return 1;
+
                 if (shmctl(shmid, IPC_RMID, NULL) < 0) {
 
                         /* Ignore entries that are already deleted */
-                        if (errno == EIDRM || errno == EINVAL)
+                        if (IN_SET(errno, EIDRM, EINVAL))
                                 continue;
 
                         ret = log_warning_errno(errno,
                                                 "Failed to remove SysV shared memory segment %i: %m",
                                                 shmid);
-                } else
+                } else {
                         log_debug("Removed SysV shared memory segment %i.", shmid);
+                        if (ret == 0)
+                                ret = 1;
+                }
         }
 
         return ret;
@@ -111,7 +100,7 @@ fail:
         return log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m");
 }
 
-static int clean_sysvipc_sem(uid_t delete_uid, gid_t delete_gid) {
+static int clean_sysvipc_sem(uid_t delete_uid, gid_t delete_gid, bool rm) {
         _cleanup_fclose_ FILE *f = NULL;
         char line[LINE_MAX];
         bool first = true;
@@ -144,17 +133,23 @@ static int clean_sysvipc_sem(uid_t delete_uid, gid_t delete_gid) {
                 if (!match_uid_gid(uid, gid, delete_uid, delete_gid))
                         continue;
 
+                if (!rm)
+                        return 1;
+
                 if (semctl(semid, 0, IPC_RMID) < 0) {
 
                         /* Ignore entries that are already deleted */
-                        if (errno == EIDRM || errno == EINVAL)
+                        if (IN_SET(errno, EIDRM, EINVAL))
                                 continue;
 
                         ret = log_warning_errno(errno,
                                                 "Failed to remove SysV semaphores object %i: %m",
                                                 semid);
-                } else
+                } else {
                         log_debug("Removed SysV semaphore %i.", semid);
+                        if (ret == 0)
+                                ret = 1;
+                }
         }
 
         return ret;
@@ -163,7 +158,7 @@ fail:
         return log_warning_errno(errno, "Failed to read /proc/sysvipc/sem: %m");
 }
 
-static int clean_sysvipc_msg(uid_t delete_uid, gid_t delete_gid) {
+static int clean_sysvipc_msg(uid_t delete_uid, gid_t delete_gid, bool rm) {
         _cleanup_fclose_ FILE *f = NULL;
         char line[LINE_MAX];
         bool first = true;
@@ -197,17 +192,23 @@ static int clean_sysvipc_msg(uid_t delete_uid, gid_t delete_gid) {
                 if (!match_uid_gid(uid, gid, delete_uid, delete_gid))
                         continue;
 
+                if (!rm)
+                        return 1;
+
                 if (msgctl(msgid, IPC_RMID, NULL) < 0) {
 
                         /* Ignore entries that are already deleted */
-                        if (errno == EIDRM || errno == EINVAL)
+                        if (IN_SET(errno, EIDRM, EINVAL))
                                 continue;
 
                         ret = log_warning_errno(errno,
                                                 "Failed to remove SysV message queue %i: %m",
                                                 msgid);
-                } else
+                } else {
                         log_debug("Removed SysV message queue %i.", msgid);
+                        if (ret == 0)
+                                ret = 1;
+                }
         }
 
         return ret;
@@ -216,7 +217,7 @@ fail:
         return log_warning_errno(errno, "Failed to read /proc/sysvipc/msg: %m");
 }
 
-static int clean_posix_shm_internal(DIR *dir, uid_t uid, gid_t gid) {
+static int clean_posix_shm_internal(DIR *dir, uid_t uid, gid_t gid, bool rm) {
         struct dirent *de;
         int ret = 0, r;
 
@@ -225,7 +226,7 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid, gid_t gid) {
         FOREACH_DIRENT_ALL(de, dir, goto fail) {
                 struct stat st;
 
-                if (STR_IN_SET(de->d_name, "..", "."))
+                if (dot_or_dot_dot(de->d_name))
                         continue;
 
                 if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
@@ -236,9 +237,6 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid, gid_t gid) {
                         continue;
                 }
 
-                if (!match_uid_gid(st.st_uid, st.st_gid, uid, gid))
-                        continue;
-
                 if (S_ISDIR(st.st_mode)) {
                         _cleanup_closedir_ DIR *kid;
 
@@ -247,29 +245,47 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid, gid_t gid) {
                                 if (errno != ENOENT)
                                         ret = log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name);
                         } else {
-                                r = clean_posix_shm_internal(kid, uid, gid);
+                                r = clean_posix_shm_internal(kid, uid, gid, rm);
                                 if (r < 0)
                                         ret = r;
                         }
 
+                        if (!match_uid_gid(st.st_uid, st.st_gid, uid, gid))
+                                continue;
+
+                        if (!rm)
+                                return 1;
+
                         if (unlinkat(dirfd(dir), de->d_name, AT_REMOVEDIR) < 0) {
 
                                 if (errno == ENOENT)
                                         continue;
 
                                 ret = log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name);
-                        } else
+                        } else {
                                 log_debug("Removed POSIX shared memory directory %s", de->d_name);
+                                if (ret == 0)
+                                        ret = 1;
+                        }
                 } else {
 
+                        if (!match_uid_gid(st.st_uid, st.st_gid, uid, gid))
+                                continue;
+
+                        if (!rm)
+                                return 1;
+
                         if (unlinkat(dirfd(dir), de->d_name, 0) < 0) {
 
                                 if (errno == ENOENT)
                                         continue;
 
                                 ret = log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name);
-                        } else
+                        } else {
                                 log_debug("Removed POSIX shared memory segment %s", de->d_name);
+                                if (ret == 0)
+                                        ret = 1;
+                        }
                 }
         }
 
@@ -279,7 +295,7 @@ fail:
         return log_warning_errno(errno, "Failed to read /dev/shm: %m");
 }
 
-static int clean_posix_shm(uid_t uid, gid_t gid) {
+static int clean_posix_shm(uid_t uid, gid_t gid, bool rm) {
         _cleanup_closedir_ DIR *dir = NULL;
 
         dir = opendir("/dev/shm");
@@ -290,11 +306,11 @@ static int clean_posix_shm(uid_t uid, gid_t gid) {
                 return log_warning_errno(errno, "Failed to open /dev/shm: %m");
         }
 
-        return clean_posix_shm_internal(dir, uid, gid);
+        return clean_posix_shm_internal(dir, uid, gid, rm);
 }
 
 #if 0 /// UNNEEDED by elogind
-static int clean_posix_mq(uid_t uid, gid_t gid) {
+static int clean_posix_mq(uid_t uid, gid_t gid, bool rm) {
         _cleanup_closedir_ DIR *dir = NULL;
         struct dirent *de;
         int ret = 0;
@@ -311,7 +327,7 @@ static int clean_posix_mq(uid_t uid, gid_t gid) {
                 struct stat st;
                 char fn[1+strlen(de->d_name)+1];
 
-                if (STR_IN_SET(de->d_name, "..", "."))
+                if (dot_or_dot_dot(de->d_name))
                         continue;
 
                 if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
@@ -327,6 +343,9 @@ static int clean_posix_mq(uid_t uid, gid_t gid) {
                 if (!match_uid_gid(st.st_uid, st.st_gid, uid, gid))
                         continue;
 
+                if (!rm)
+                        return 1;
+
                 fn[0] = '/';
                 strcpy(fn+1, de->d_name);
 
@@ -337,8 +356,11 @@ static int clean_posix_mq(uid_t uid, gid_t gid) {
                         ret = log_warning_errno(errno,
                                                 "Failed to unlink POSIX message queue %s: %m",
                                                 fn);
-                } else
+                } else {
                         log_debug("Removed POSIX message queue %s", fn);
+                        if (ret == 0)
+                                ret = 1;
+                }
         }
 
         return ret;
@@ -348,48 +370,89 @@ fail:
 }
 #endif // 0
 
-int clean_ipc(uid_t uid, gid_t gid) {
+int clean_ipc_internal(uid_t uid, gid_t gid, bool rm) {
         int ret = 0, r;
 
+        /* If 'rm' is true, clean all IPC objects owned by either the specified UID or the specified GID. Return the
+         * last error encountered or == 0 if no matching IPC objects have been found or > 0 if matching IPC objects
+         * have been found and have been removed.
+         *
+         * If 'rm' is false, just search for IPC objects owned by either the specified UID or the specified GID. In
+         * this case we return < 0 on error, > 0 if we found a matching object, == 0 if we didn't.
+         *
+         * As special rule: if UID/GID is specified as root we'll silently not clean up things, and always claim that
+         * there are IPC objects for it. */
+
+        if (uid == 0) {
+                if (!rm)
+                        return 1;
+
+                uid = UID_INVALID;
+        }
+        if (gid == 0) {
+                if (!rm)
+                        return 1;
+
+                gid = GID_INVALID;
+        }
+
         /* Anything to do? */
         if (!uid_is_valid(uid) && !gid_is_valid(gid))
                 return 0;
 
-        /* Refuse to clean IPC of the root user */
-        if (uid == 0 && gid == 0)
-                return 0;
-
-        r = clean_sysvipc_shm(uid, gid);
-        if (r < 0)
-                ret = r;
+        r = clean_sysvipc_shm(uid, gid, rm);
+        if (r != 0) {
+                if (!rm)
+                        return r;
+                if (ret == 0)
+                        ret = r;
+        }
 
-        r = clean_sysvipc_sem(uid, gid);
-        if (r < 0)
-                ret = r;
+#if 0 /// elogind does not use mq_open anywhere
+#endif // 0
+        r = clean_sysvipc_sem(uid, gid, rm);
+        if (r != 0) {
+                if (!rm)
+                        return r;
+                if (ret == 0)
+                        ret = r;
+        }
 
-        r = clean_sysvipc_msg(uid, gid);
-        if (r < 0)
-                ret = r;
+        r = clean_sysvipc_msg(uid, gid, rm);
+        if (r != 0) {
+                if (!rm)
+                        return r;
+                if (ret == 0)
+                        ret = r;
+        }
 
-        r = clean_posix_shm(uid, gid);
-        if (r < 0)
-                ret = r;
+        r = clean_posix_shm(uid, gid, rm);
+        if (r != 0) {
+                if (!rm)
+                        return r;
+                if (ret == 0)
+                        ret = r;
+        }
 
-#if 0 /// elogind does not use mq_open anywhere
-        r = clean_posix_mq(uid, gid);
-        if (r < 0)
-                ret = r;
+#if 0 /// Nothing in elogind uses mqueues
+        r = clean_posix_mq(uid, gid, rm);
+        if (r != 0) {
+                if (!rm)
+                        return r;
+                if (ret == 0)
+                        ret = r;
+        }
 #endif // 0
 
         return ret;
 }
 
 int clean_ipc_by_uid(uid_t uid) {
-        return clean_ipc(uid, GID_INVALID);
+        return clean_ipc_internal(uid, GID_INVALID, true);
 }
 
 #if 0 /// UNNEEDED by elogind
 int clean_ipc_by_gid(gid_t gid) {
-        return clean_ipc(UID_INVALID, gid);
+        return clean_ipc_internal(UID_INVALID, gid, true);
 }
 #endif // 0