summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjkar8572 <jkar8572>2007-06-18 11:57:32 +0000
committerjkar8572 <jkar8572>2007-06-18 11:57:32 +0000
commit1a9e48b4f8747e9aa7357631afc35c6a0cefdd6f (patch)
treefdbd8284d124da29a4a26ab64cf906529d657ac9
parentd3526fd204c8d2ad3edbe6be259a50a33a3d881b (diff)
Implemented quota netlink daemon.
-rw-r--r--Makefile.in11
-rw-r--r--configure.in30
-rw-r--r--quota.h30
-rw-r--r--quota_nld.c367
4 files changed, 434 insertions, 4 deletions
diff --git a/Makefile.in b/Makefile.in
index f6af35d..33d5514 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,8 +1,10 @@
-PROGS = quotacheck quotaon quota quot repquota warnquota quotastats xqmstats edquota setquota convertquota rpc.rquotad
+PROGS = quotacheck quotaon quota quot repquota warnquota quotastats xqmstats edquota setquota convertquota rpc.rquotad quota_nld
SOURCES = bylabel.c common.c convertquota.c edquota.c pot.c quot.c quota.c quotacheck.c quotacheck_v1.c quotacheck_v2.c quotaio.c quotaio_rpc.c quotaio_v1.c quotaio_v2.c quotaio_xfs.c quotaio_generic.c quotaon.c quotaon_xfs.c quotaops.c quotastats.c quotasys.c repquota.c rquota_client.c rquota_server.c rquota_svc.c setquota.c warnquota.c xqmstats.c svc_socket.c
-VERSIONDEF = -DQUOTA_VERSION=\"3.14\"
+VERSIONDEF = -DQUOTA_VERSION=\"3.15\"
CFLAGS = @CFLAGS@ @EXT2_DIRECT@ -D_GNU_SOURCE -Wall -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 $(VERSIONDEF)
+CPPFLAGS = @CPPFLAGS@
EXT2LIBS = @EXT2LIBS@
+NETLINKLIBS = @NETLINKLIBS@
RPCSRC = rquota.h rquota_xdr.c rquota_clnt.c
LIBS = @LIBS@
LDFLAGS = @LDFLAGS@
@@ -130,6 +132,11 @@ convertquota: convertquota.o $(LIBOBJS)
rpc.rquotad: rquota_server.o rquota_svc.o svc_socket.o $(LIBOBJS)
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
+ifneq ($(NETLINKLIBS),)
+quota_nld: quota_nld.o $(LIBOBJS)
+ $(CC) $(LDFLAGS) -o $@ $^ $(NETLINKLIBS)
+endif
+
pot.o: pot.c pot.h
rquota.h: rquota.x
diff --git a/configure.in b/configure.in
index 7063704..4350a51 100644
--- a/configure.in
+++ b/configure.in
@@ -93,6 +93,31 @@ if test "x$enable_ext2direct" != "xno"; then
fi
AC_SUBST(EXT2LIBS)
+AC_ARG_WITH(dbus_include,
+ [ --with-dbus_include=path Path to directory with dbus include directory [default=/usr/include/dbus-1.0/]],
+ DBUS_INCLUDE="$with_dbus_include",
+ DBUS_INCLUDE="/usr/include/dbus-1.0/")
+AC_ARG_WITH(dbus_arch_include,
+ [ --with-dbus_arch_include=path Path to directory with dbus arch-dependent include directory [default=/usr/lib/dbus-1.0/include/]],
+ DBUS_ARCH_INCLUDE="$with_dbus_arch_include",
+ DBUS_ARCH_INCLUDE="/usr/lib/dbus-1.0/include/")
+
+AC_ARG_ENABLE(netlink,
+ [ --enable-netlink=[yes/no] Compile daemon receiving quota messages via netlink [default=yes].],
+ ,
+ enable_netlink="yes")
+if test "x$enable_netlink" != "xno"; then
+ AC_CHECK_LIB(nl, genl_register, NETLINKLIBS="-lnl $NETLINKLIBS")
+ AC_CHECK_LIB(dbus-1, dbus_bus_get, NETLINKLIBS="-ldbus-1 $NETLINKLIBS")
+ if test "${ac_cv_lib_nl_genl_register}" != "yes" -o "${ac_cv_lib_dbus_1_dbus_bus_get}" != "yes"; then
+ AC_MSG_ERROR([Required libraries for quota netlink daemon not found.]);
+ fi
+ CPPFLAGS="-I $DBUS_INCLUDE -I $DBUS_ARCH_INCLUDE $CPPFLAGS"
+ AC_CHECK_HEADERS(dbus/dbus.h, , AC_MSG_ERROR([Required headers for quota netlink daemon not found.]))
+
+fi
+AC_SUBST(NETLINKLIBS)
+
AC_SEARCH_LIBS(gethostbyname, nsl)
AC_CACHE_VAL(ac_cv_lib_wrap_main,
saved_LIBS="$LIBS"
@@ -135,11 +160,11 @@ AC_ARG_ENABLE(rpcsetquota,
,
enable_rpcsetquota="no")
AC_ARG_ENABLE(xfs_roothack,
- [ --enable-xfs_roothack=[yes/no] Support old XFS root filesystems [default=no].],
+ [ --enable-xfs_roothack=[yes/no] Support old XFS root filesystems [default=no].],
,
enable_xfs_roothack="no")
AC_ARG_ENABLE(bsd_behaviour,
- [ --enable-bsd_behaviour=[yes/no] Mimic BSD behaviour [default=yes].],
+ [ --enable-bsd_behaviour=[yes/no] Mimic BSD behaviour [default=yes].],
,
enable_bsd_behaviour="yes")
AC_ARG_ENABLE(libefence,
@@ -186,5 +211,6 @@ fi
AC_SUBST(LIBMALLOC)
AC_SUBST(INSTMO)
AC_SUBST(ROOTSBIN)
+AC_SUBST(CPPFLAGS)
AC_OUTPUT(Makefile)
diff --git a/quota.h b/quota.h
index 259cea3..ca3db7b 100644
--- a/quota.h
+++ b/quota.h
@@ -102,6 +102,36 @@ struct if_dqinfo {
u_int32_t dqi_valid;
};
+/*
+ * Definitions for quota netlink interface
+ */
+#define QUOTA_NL_NOWARN 0
+#define QUOTA_NL_IHARDWARN 1 /* Inode hardlimit reached */
+#define QUOTA_NL_ISOFTLONGWARN 2 /* Inode grace time expired */
+#define QUOTA_NL_ISOFTWARN 3 /* Inode softlimit reached */
+#define QUOTA_NL_BHARDWARN 4 /* Block hardlimit reached */
+#define QUOTA_NL_BSOFTLONGWARN 5 /* Block grace time expired */
+#define QUOTA_NL_BSOFTWARN 6 /* Block softlimit reached */
+
+enum {
+ QUOTA_NL_C_UNSPEC,
+ QUOTA_NL_C_WARNING,
+ __QUOTA_NL_C_MAX,
+};
+#define QUOTA_NL_C_MAX (__QUOTA_NL_C_MAX - 1)
+
+enum {
+ QUOTA_NL_A_UNSPEC,
+ QUOTA_NL_A_QTYPE,
+ QUOTA_NL_A_EXCESS_ID,
+ QUOTA_NL_A_WARNING,
+ QUOTA_NL_A_DEV_MAJOR,
+ QUOTA_NL_A_DEV_MINOR,
+ QUOTA_NL_A_CAUSED_ID,
+ __QUOTA_NL_A_MAX,
+};
+#define QUOTA_NL_A_MAX (__QUOTA_NL_A_MAX - 1)
+
/* Quota format identifiers */
#define QFMT_VFS_OLD 1
#define QFMT_VFS_V0 2
diff --git a/quota_nld.c b/quota_nld.c
new file mode 100644
index 0000000..827833c
--- /dev/null
+++ b/quota_nld.c
@@ -0,0 +1,367 @@
+/*
+ * A deamon to read quota warning messages from the kernel netlink socket
+ * and either pipe them to the system DBUS or write them to user's console
+ *
+ * Copyright (c) 2007 SUSE CR, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would 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.
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <utmp.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include <netlink/netlink.h>
+#include <netlink/netlink-kernel.h>
+#include <netlink/genl/mngt.h>
+
+#include <dbus/dbus.h>
+
+#include "pot.h"
+#include "common.h"
+#include "quotasys.h"
+#include "quota.h"
+
+char *progname;
+
+static const struct option options[] = {
+ { "version", 0, NULL, 'V' },
+ { "help", 0, NULL, 'h' },
+ { "no-dbus", 0, NULL, 'D' },
+ { "no-console", 0, NULL, 'C' },
+ { "no-daemon", 0, NULL, 'n' },
+ { NULL, 0, NULL, 0 }
+};
+
+struct quota_warning {
+ uint32_t qtype;
+ uint64_t excess_id;
+ uint32_t warntype;
+ uint32_t dev_major;
+ uint32_t dev_minor;
+ uint64_t caused_id;
+};
+
+static int quota_nl_warn_cmd_parser(struct genl_ops *ops, struct genl_cmd *cmd,
+ struct genl_info *info, void *arg);
+
+static struct nla_policy quota_nl_warn_cmd_policy[QUOTA_NL_A_MAX+1] = {
+ [QUOTA_NL_A_QTYPE] = { .type = NLA_U32 },
+ [QUOTA_NL_A_EXCESS_ID] = { .type = NLA_U64 },
+ [QUOTA_NL_A_WARNING] = { .type = NLA_U32 },
+ [QUOTA_NL_A_DEV_MAJOR] = { .type = NLA_U32 },
+ [QUOTA_NL_A_DEV_MINOR] = { .type = NLA_U32 },
+ [QUOTA_NL_A_CAUSED_ID] = { .type = NLA_U64 },
+};
+
+static struct genl_cmd quota_nl_warn_cmd = {
+ .c_id = QUOTA_NL_C_WARNING,
+ .c_name = "Quota warning",
+ .c_maxattr = QUOTA_NL_A_MAX,
+ .c_attr_policy = quota_nl_warn_cmd_policy,
+ .c_msg_parser = quota_nl_warn_cmd_parser,
+};
+
+static struct genl_ops quota_nl_ops = {
+ .o_cmds = &quota_nl_warn_cmd,
+ .o_ncmds = 1,
+ .o_name = "VFS_DQUOT",
+ .o_hdrsize = 0,
+};
+
+/* User options */
+#define FL_NODBUS 1
+#define FL_NOCONSOLE 2
+#define FL_NODAEMON 4
+
+int flags;
+
+void show_help(void)
+{
+ errstr(_("Usage: %s [options]\nOptions are:\n\
+ -h --help shows this text\n\
+ -V --version shows version information\n\
+ -C --no-console do not try to write messages to console\n\
+ -D --no-dbus do not try to write messages to DBUS\n\
+ -n --no-daemon do not detach from tty\n"), progname);
+}
+
+static void parse_options(int argc, char **argv)
+{
+ int opt;
+
+ while ((opt = getopt_long(argc, argv, "VhDCn", options, NULL)) >= 0) {
+ switch (opt) {
+ case 'V':
+ version();
+ exit(0);
+ case 'h':
+ show_help();
+ exit(0);
+ case 'D':
+ flags |= FL_NODBUS;
+ break;
+ case 'C':
+ flags |= FL_NOCONSOLE;
+ break;
+ case 'n':
+ flags |= FL_NODAEMON;
+ break;
+ default:
+ errstr(_("Unknown option '%c'.\n"), opt);
+ show_help();
+ exit(1);
+ }
+ }
+ if (flags & FL_NODBUS && flags & FL_NOCONSOLE) {
+ errstr(_("No possible destination for messages. Nothing to do.\n"));
+ exit(0);
+ }
+}
+
+/* Parse netlink message and process it. */
+static int quota_nl_warn_cmd_parser(struct genl_ops *ops, struct genl_cmd *cmd,
+ struct genl_info *info, void *arg)
+{
+ struct quota_warning *warn = (struct quota_warning *)arg;
+
+ warn->qtype = nla_get_u32(info->attrs[QUOTA_NL_A_QTYPE]);
+ warn->excess_id = nla_get_u64(info->attrs[QUOTA_NL_A_EXCESS_ID]);
+ warn->warntype = nla_get_u32(info->attrs[QUOTA_NL_A_WARNING]);
+ warn->dev_major = nla_get_u32(info->attrs[QUOTA_NL_A_DEV_MAJOR]);
+ warn->dev_minor = nla_get_u32(info->attrs[QUOTA_NL_A_DEV_MINOR]);
+ warn->caused_id = nla_get_u64(info->attrs[QUOTA_NL_A_CAUSED_ID]);
+
+ return 0;
+}
+
+static struct nl_handle *init_netlink(void)
+{
+ struct nl_handle *handle;
+ int ret;
+
+ handle = nl_handle_alloc();
+ if (!handle)
+ die(2, _("Cannot allocate netlink handle!\n"));
+ nl_disable_sequence_check(handle);
+ ret = nl_connect(handle, NETLINK_GENERIC);
+ if (ret < 0)
+ die(2, _("Cannot connect to netlink socket: %s\n"), strerror(-ret));
+ ret = genl_ops_resolve(handle, &quota_nl_ops);
+ if (ret < 0)
+ die(2, _("Cannot resolve quota netlink name: %s\n"), strerror(-ret));
+
+ ret = nl_socket_add_membership(handle, quota_nl_ops.o_id);
+ if (ret < 0)
+ die(2, _("Cannot join quota multicast group: %s\n"), strerror(-ret));
+
+ ret = genl_register(&quota_nl_ops);
+ if (ret < 0)
+ die(2, _("Cannot register netlink family: %s\n"), strerror(-ret));
+
+ return handle;
+}
+
+static DBusConnection *init_dbus(void)
+{
+ DBusConnection *handle;
+ DBusError err;
+
+ dbus_error_init(&err);
+ handle = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
+ if (dbus_error_is_set(&err))
+ die(2, _("Cannot connect to system DBUS: %s\n"), err.message);
+
+ dbus_connection_set_exit_on_disconnect(handle, FALSE);
+ return handle;
+}
+
+static int write_all(int fd, char *buf, int len)
+{
+ int ret;
+
+ while (len) {
+ ret = write(fd, buf, len);
+ if (ret < 0)
+ return -1;
+ buf += ret;
+ len -= ret;
+ }
+ return 0;
+}
+
+#define WARN_BUF_SIZE 512
+
+/* Scan through utmp, find latest used controlling tty and write to it */
+static void write_console_warning(struct quota_warning *warn)
+{
+ struct utmp *uent;
+ char user[MAXNAMELEN];
+ struct stat st;
+ char dev[PATH_MAX];
+ time_t max_atime = 0;
+ char max_dev[PATH_MAX];
+ int fd;
+ char warnbuf[WARN_BUF_SIZE];
+ char *level, *msg;
+
+ uid2user(warn->caused_id, user);
+ strcpy(dev, "/dev/");
+
+ setutent();
+ endutent();
+ while ((uent = getutent())) {
+ if (uent->ut_type != USER_PROCESS)
+ continue;
+ /* Entry for a different user? */
+ if (strcmp(user, uent->ut_user))
+ continue;
+ sstrncpy(dev+5, uent->ut_line, PATH_MAX-5);
+ if (stat(dev, &st) < 0)
+ continue; /* Failed to stat - not a good candidate for warning... */
+ if (max_atime < st.st_atime) {
+ max_atime = st.st_atime;
+ strcpy(max_dev, dev);
+ }
+ }
+ if (!max_atime) {
+ errstr(_("Failed to find tty of user %Lu to report warning to.\n"), (unsigned long long)warn->caused_id);
+ return;
+ }
+ fd = open(max_dev, O_WRONLY);
+ if (fd < 0) {
+ errstr(_("Failed to open tty %s of user %Lu to report warning.\n"), dev, (unsigned long long)warn->caused_id);
+ return;
+ }
+ id2name(warn->excess_id, warn->qtype, user);
+ if (warn->warntype == QUOTA_NL_ISOFTWARN || warn->warntype == QUOTA_NL_BSOFTWARN)
+ level = "Warning";
+ else
+ level = "Error";
+ switch (warn->warntype) {
+ case QUOTA_NL_IHARDWARN:
+ msg = "file limit reached";
+ break;
+ case QUOTA_NL_ISOFTLONGWARN:
+ msg = "file quota exceeded too long";
+ break;
+ case QUOTA_NL_ISOFTWARN:
+ msg = "file quota exceeded";
+ break;
+ case QUOTA_NL_BHARDWARN:
+ msg = "block limit reached";
+ break;
+ case QUOTA_NL_BSOFTLONGWARN:
+ msg = "block quota exceeded too long";
+ break;
+ case QUOTA_NL_BSOFTWARN:
+ msg = "block quota exceeded";
+ break;
+ default:
+ msg = "unknown quota warning";
+ }
+ sprintf(warnbuf, "%s: %s %s %s.\r\n", level, type2name(warn->qtype), user, msg);
+ if (write_all(fd, warnbuf, strlen(warnbuf)) < 0)
+ errstr(_("Failed to write quota message for user %Lu to %s: %s\n"), (unsigned long long)warn->caused_id, dev, strerror(errno));
+ close(fd);
+}
+
+/* Send warning through DBUS */
+static void write_dbus_warning(struct DBusConnection *dhandle, struct quota_warning *warn)
+{
+ DBusMessage* msg;
+ DBusMessageIter args;
+
+ msg = dbus_message_new_signal("/", "com.system.quota.warning", "warning");
+ if (!msg) {
+no_mem:
+ errstr(_("Cannot create DBUS message: No enough memory.\n"));
+ goto out;
+ }
+ dbus_message_iter_init_append(msg, &args);
+ if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &warn->qtype))
+ goto no_mem;
+ if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT64, &warn->excess_id))
+ goto no_mem;
+ if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &warn->warntype))
+ goto no_mem;
+ if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &warn->dev_major))
+ goto no_mem;
+ if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &warn->dev_minor))
+ goto no_mem;
+ if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT64, &warn->caused_id))
+ goto no_mem;
+
+ if (!dbus_connection_send(dhandle, msg, NULL)) {
+ errstr(_("Failed to write message to dbus: No enough memory.\n"));
+ goto out;
+ }
+ dbus_connection_flush(dhandle);
+out:
+ if (msg)
+ dbus_message_unref(msg);
+}
+
+static void run(struct nl_handle *nhandle, struct DBusConnection *dhandle)
+{
+ struct sockaddr_nl nla;
+ struct ucred *creds;
+ struct quota_warning warn;
+ unsigned char *buf;
+ struct nlmsghdr *hdr;
+ int ret;
+
+ while (1) {
+ ret = nl_recv(nhandle, &nla, &buf, &creds);
+ if (ret < 0)
+ die(2, _("Read from netlink socket failed: %s\n"), strerror(-ret));
+ hdr = (struct nlmsghdr *)buf;
+ /* Not message from quota? */
+ if (hdr->nlmsg_type != quota_nl_ops.o_id) {
+ free(buf);
+ continue;
+ }
+ ret = genl_msg_parser(&quota_nl_ops, &nla, (struct nlmsghdr *)buf, &warn);
+ if (!ret) { /* Parsing successful? */
+ if (!(flags & FL_NOCONSOLE))
+ write_console_warning(&warn);
+ if (!(flags & FL_NODBUS))
+ write_dbus_warning(dhandle, &warn);
+ }
+ else
+ errstr(_("Failed parsing netlink command: %s\n"), strerror(errno));
+ free(buf);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ struct nl_handle *nhandle;
+ DBusConnection *dhandle = NULL;
+
+ gettexton();
+ progname = basename(argv[0]);
+ parse_options(argc, argv);
+
+ nhandle = init_netlink();
+ if (!(flags & FL_NODBUS))
+ dhandle = init_dbus();
+ if (!(flags & FL_NODAEMON)) {
+ use_syslog();
+ daemon(0, 0);
+ }
+ run(nhandle, dhandle);
+ return 0;
+}