2 * A deamon to read quota warning messages from the kernel netlink socket
3 * and either pipe them to the system DBUS or write them to user's console
5 * Copyright (c) 2007 SUSE CR, All Rights Reserved.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of version 2 of the GNU General Public License as
9 * published by the Free Software Foundation.
11 * This program is distributed in the hope that it would be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
29 #include <netlink/genl/genl.h>
30 #include <netlink/genl/ctrl.h>
32 #include <dbus/dbus.h>
41 struct quota_warning {
50 static struct nla_policy quota_nl_warn_cmd_policy[QUOTA_NL_A_MAX+1] = {
51 [QUOTA_NL_A_QTYPE] = { .type = NLA_U32 },
52 [QUOTA_NL_A_EXCESS_ID] = { .type = NLA_U64 },
53 [QUOTA_NL_A_WARNING] = { .type = NLA_U32 },
54 [QUOTA_NL_A_DEV_MAJOR] = { .type = NLA_U32 },
55 [QUOTA_NL_A_DEV_MINOR] = { .type = NLA_U32 },
56 [QUOTA_NL_A_CAUSED_ID] = { .type = NLA_U64 },
61 #define FL_NOCONSOLE 2
63 #define FL_PRINTBELOW 8
66 DBusConnection *dhandle;
68 static const struct option options[] = {
69 { "version", 0, NULL, 'V' },
70 { "help", 0, NULL, 'h' },
71 { "no-dbus", 0, NULL, 'D' },
72 { "no-console", 0, NULL, 'C' },
73 { "foreground", 0, NULL, 'F' },
74 { "print-below", 0, NULL, 'b' },
80 errstr(_("Usage: %s [options]\nOptions are:\n\
81 -h --help shows this text\n\
82 -V --version shows version information\n\
83 -C --no-console do not try to write messages to console\n\
84 -b --print-below write to console also information about getting below hard/soft limits\n\
85 -D --no-dbus do not try to write messages to DBUS\n\
86 -F --foreground run daemon in foreground\n"), progname);
89 static void parse_options(int argc, char **argv)
93 while ((opt = getopt_long(argc, argv, "VhDCFb", options, NULL)) >= 0) {
105 flags |= FL_NOCONSOLE;
108 flags |= FL_NODAEMON;
111 flags |= FL_PRINTBELOW;
114 errstr(_("Unknown option '%c'.\n"), opt);
119 if (flags & FL_NODBUS && flags & FL_NOCONSOLE) {
120 errstr(_("No possible destination for messages. Nothing to do.\n"));
125 static void write_console_warning(struct quota_warning *warn);
126 static void write_dbus_warning(struct DBusConnection *dhandle, struct quota_warning *warn);
128 /* Parse netlink message and process it. */
129 static int quota_nl_parser(struct nl_msg *msg, void *arg)
131 struct nlmsghdr *nlh = nlmsg_hdr(msg);
132 struct genlmsghdr *ghdr;
133 struct nlattr *attrs[QUOTA_NL_A_MAX+1];
134 struct quota_warning warn;
137 if (!genlmsg_valid_hdr(nlh, 0))
139 ghdr = nlmsg_data(nlh);
140 /* Unknown message? Ignore... */
141 if (ghdr->cmd != QUOTA_NL_C_WARNING)
144 ret = genlmsg_parse(nlh, 0, attrs, QUOTA_NL_A_MAX, quota_nl_warn_cmd_policy);
146 errstr(_("Error parsing netlink message.\n"));
149 if (!attrs[QUOTA_NL_A_QTYPE] || !attrs[QUOTA_NL_A_EXCESS_ID] ||
150 !attrs[QUOTA_NL_A_WARNING] || !attrs[QUOTA_NL_A_DEV_MAJOR] ||
151 !attrs[QUOTA_NL_A_DEV_MAJOR] || !attrs[QUOTA_NL_A_DEV_MINOR] ||
152 !attrs[QUOTA_NL_A_CAUSED_ID]) {
153 errstr(_("Unknown format of kernel netlink message!\nMaybe your quota tools are too old?\n"));
156 warn.qtype = nla_get_u32(attrs[QUOTA_NL_A_QTYPE]);
157 warn.excess_id = nla_get_u64(attrs[QUOTA_NL_A_EXCESS_ID]);
158 warn.warntype = nla_get_u32(attrs[QUOTA_NL_A_WARNING]);
159 warn.dev_major = nla_get_u32(attrs[QUOTA_NL_A_DEV_MAJOR]);
160 warn.dev_minor = nla_get_u32(attrs[QUOTA_NL_A_DEV_MINOR]);
161 warn.caused_id = nla_get_u64(attrs[QUOTA_NL_A_CAUSED_ID]);
163 if (!(flags & FL_NOCONSOLE))
164 write_console_warning(&warn);
165 if (!(flags & FL_NODBUS))
166 write_dbus_warning(dhandle, &warn);
170 static struct nl_handle *init_netlink(void)
172 struct nl_handle *handle;
175 handle = nl_handle_alloc();
177 die(2, _("Cannot allocate netlink handle!\n"));
178 nl_disable_sequence_check(handle);
179 ret = genl_connect(handle);
181 die(2, _("Cannot connect to netlink socket: %s\n"), strerror(-ret));
182 family = genl_ctrl_resolve(handle, "VFS_DQUOT");
184 die(2, _("Cannot resolve quota netlink name: %s\n"), strerror(-ret));
186 ret = nl_socket_add_membership(handle, family);
188 die(2, _("Cannot join quota multicast group: %s\n"), strerror(-ret));
190 ret = nl_socket_modify_cb(handle, NL_CB_VALID, NL_CB_CUSTOM,
191 quota_nl_parser, NULL);
193 die(2, _("Cannot register callback for"
194 " netlink messages: %s\n"), strerror(-ret));
199 static DBusConnection *init_dbus(void)
201 DBusConnection *handle;
204 dbus_error_init(&err);
205 handle = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
206 if (dbus_error_is_set(&err))
207 die(2, _("Cannot connect to system DBUS: %s\n"), err.message);
209 dbus_connection_set_exit_on_disconnect(handle, FALSE);
213 static int write_all(int fd, char *buf, int len)
218 ret = write(fd, buf, len);
227 #define WARN_BUF_SIZE 512
229 /* Scan through utmp, find latest used controlling tty and write to it */
230 static void write_console_warning(struct quota_warning *warn)
233 char user[MAXNAMELEN];
236 time_t max_atime = 0;
237 char max_dev[PATH_MAX];
239 char warnbuf[WARN_BUF_SIZE];
242 if ((warn->warntype == QUOTA_NL_IHARDBELOW ||
243 warn->warntype == QUOTA_NL_ISOFTBELOW ||
244 warn->warntype == QUOTA_NL_BHARDBELOW ||
245 warn->warntype == QUOTA_NL_BSOFTBELOW) && !(flags & FL_PRINTBELOW))
247 uid2user(warn->caused_id, user);
248 strcpy(dev, "/dev/");
252 while ((uent = getutent())) {
253 if (uent->ut_type != USER_PROCESS)
255 /* Entry for a different user? */
256 if (strcmp(user, uent->ut_user))
258 sstrncpy(dev+5, uent->ut_line, PATH_MAX-5);
259 if (stat(dev, &st) < 0)
260 continue; /* Failed to stat - not a good candidate for warning... */
261 if (max_atime < st.st_atime) {
262 max_atime = st.st_atime;
263 strcpy(max_dev, dev);
267 errstr(_("Failed to find tty of user %llu to report warning to.\n"), (unsigned long long)warn->caused_id);
270 fd = open(max_dev, O_WRONLY);
272 errstr(_("Failed to open tty %s of user %llu to report warning.\n"), dev, (unsigned long long)warn->caused_id);
275 id2name(warn->excess_id, warn->qtype, user);
276 if (warn->warntype == QUOTA_NL_ISOFTWARN ||
277 warn->warntype == QUOTA_NL_BSOFTWARN)
278 level = _("Warning");
279 else if (warn->warntype == QUOTA_NL_IHARDWARN ||
280 warn->warntype == QUOTA_NL_BHARDWARN)
284 switch (warn->warntype) {
285 case QUOTA_NL_IHARDWARN:
286 msg = _("file limit reached");
288 case QUOTA_NL_ISOFTLONGWARN:
289 msg = _("file quota exceeded too long");
291 case QUOTA_NL_ISOFTWARN:
292 msg = _("file quota exceeded");
294 case QUOTA_NL_BHARDWARN:
295 msg = _("block limit reached");
297 case QUOTA_NL_BSOFTLONGWARN:
298 msg = _("block quota exceeded too long");
300 case QUOTA_NL_BSOFTWARN:
301 msg = _("block quota exceeded");
303 case QUOTA_NL_IHARDBELOW:
304 msg = _("got below file limit");
306 case QUOTA_NL_ISOFTBELOW:
307 msg = _("got below file quota");
309 case QUOTA_NL_BHARDBELOW:
310 msg = _("got below block limit");
312 case QUOTA_NL_BSOFTBELOW:
313 msg = _("got below block quota");
316 msg = _("unknown quota warning");
318 sprintf(warnbuf, "%s: %s %s %s.\r\n", level, type2name(warn->qtype), user, msg);
319 if (write_all(fd, warnbuf, strlen(warnbuf)) < 0)
320 errstr(_("Failed to write quota message for user %llu to %s: %s\n"), (unsigned long long)warn->caused_id, dev, strerror(errno));
324 /* Send warning through DBUS */
325 static void write_dbus_warning(struct DBusConnection *dhandle, struct quota_warning *warn)
328 DBusMessageIter args;
330 msg = dbus_message_new_signal("/", "com.system.quota.warning", "warning");
333 errstr(_("Cannot create DBUS message: No enough memory.\n"));
336 dbus_message_iter_init_append(msg, &args);
337 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &warn->qtype))
339 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT64, &warn->excess_id))
341 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &warn->warntype))
343 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &warn->dev_major))
345 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &warn->dev_minor))
347 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT64, &warn->caused_id))
350 if (!dbus_connection_send(dhandle, msg, NULL)) {
351 errstr(_("Failed to write message to dbus: No enough memory.\n"));
354 dbus_connection_flush(dhandle);
357 dbus_message_unref(msg);
360 static void run(struct nl_handle *nhandle)
365 ret = nl_recvmsgs_default(nhandle);
367 errstr(_("Failed to read or parse quota netlink"
368 " message: %s\n"), strerror(-ret));
372 int main(int argc, char **argv)
374 struct nl_handle *nhandle;
377 progname = basename(argv[0]);
378 parse_options(argc, argv);
380 nhandle = init_netlink();
381 if (!(flags & FL_NODBUS))
382 dhandle = init_dbus();
383 if (!(flags & FL_NODAEMON)) {