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
30 #include <netlink/genl/genl.h>
31 #include <netlink/genl/ctrl.h>
33 #include <dbus/dbus.h>
42 struct quota_warning {
51 static struct nla_policy quota_nl_warn_cmd_policy[QUOTA_NL_A_MAX+1] = {
52 [QUOTA_NL_A_QTYPE] = { .type = NLA_U32 },
53 [QUOTA_NL_A_EXCESS_ID] = { .type = NLA_U64 },
54 [QUOTA_NL_A_WARNING] = { .type = NLA_U32 },
55 [QUOTA_NL_A_DEV_MAJOR] = { .type = NLA_U32 },
56 [QUOTA_NL_A_DEV_MINOR] = { .type = NLA_U32 },
57 [QUOTA_NL_A_CAUSED_ID] = { .type = NLA_U64 },
62 #define FL_NOCONSOLE 2
64 #define FL_PRINTBELOW 8
67 static DBusConnection *dhandle;
69 static const struct option options[] = {
70 { "version", 0, NULL, 'V' },
71 { "help", 0, NULL, 'h' },
72 { "no-dbus", 0, NULL, 'D' },
73 { "no-console", 0, NULL, 'C' },
74 { "foreground", 0, NULL, 'F' },
75 { "print-below", 0, NULL, 'b' },
79 static void show_help(void)
81 errstr(_("Usage: %s [options]\nOptions are:\n\
82 -h --help shows this text\n\
83 -V --version shows version information\n\
84 -C --no-console do not try to write messages to console\n\
85 -b --print-below write to console also information about getting below hard/soft limits\n\
86 -D --no-dbus do not try to write messages to DBUS\n\
87 -F --foreground run daemon in foreground\n"), progname);
90 static void parse_options(int argc, char **argv)
94 while ((opt = getopt_long(argc, argv, "VhDCFb", options, NULL)) >= 0) {
106 flags |= FL_NOCONSOLE;
109 flags |= FL_NODAEMON;
112 flags |= FL_PRINTBELOW;
115 errstr(_("Unknown option '%c'.\n"), opt);
120 if (flags & FL_NODBUS && flags & FL_NOCONSOLE) {
121 errstr(_("No possible destination for messages. Nothing to do.\n"));
126 static void write_console_warning(struct quota_warning *warn);
127 static void write_dbus_warning(struct DBusConnection *dhandle, struct quota_warning *warn);
129 /* Parse netlink message and process it. */
130 static int quota_nl_parser(struct nl_msg *msg, void *arg)
132 struct nlmsghdr *nlh = nlmsg_hdr(msg);
133 struct genlmsghdr *ghdr;
134 struct nlattr *attrs[QUOTA_NL_A_MAX+1];
135 struct quota_warning warn;
138 if (!genlmsg_valid_hdr(nlh, 0))
140 ghdr = nlmsg_data(nlh);
141 /* Unknown message? Ignore... */
142 if (ghdr->cmd != QUOTA_NL_C_WARNING)
145 ret = genlmsg_parse(nlh, 0, attrs, QUOTA_NL_A_MAX, quota_nl_warn_cmd_policy);
147 errstr(_("Error parsing netlink message.\n"));
150 if (!attrs[QUOTA_NL_A_QTYPE] || !attrs[QUOTA_NL_A_EXCESS_ID] ||
151 !attrs[QUOTA_NL_A_WARNING] || !attrs[QUOTA_NL_A_DEV_MAJOR] ||
152 !attrs[QUOTA_NL_A_DEV_MAJOR] || !attrs[QUOTA_NL_A_DEV_MINOR] ||
153 !attrs[QUOTA_NL_A_CAUSED_ID]) {
154 errstr(_("Unknown format of kernel netlink message!\nMaybe your quota tools are too old?\n"));
157 warn.qtype = nla_get_u32(attrs[QUOTA_NL_A_QTYPE]);
158 warn.excess_id = nla_get_u64(attrs[QUOTA_NL_A_EXCESS_ID]);
159 warn.warntype = nla_get_u32(attrs[QUOTA_NL_A_WARNING]);
160 warn.dev_major = nla_get_u32(attrs[QUOTA_NL_A_DEV_MAJOR]);
161 warn.dev_minor = nla_get_u32(attrs[QUOTA_NL_A_DEV_MINOR]);
162 warn.caused_id = nla_get_u64(attrs[QUOTA_NL_A_CAUSED_ID]);
164 if (!(flags & FL_NOCONSOLE))
165 write_console_warning(&warn);
166 if (!(flags & FL_NODBUS))
167 write_dbus_warning(dhandle, &warn);
171 static struct nl_handle *init_netlink(void)
173 struct nl_handle *handle;
176 handle = nl_handle_alloc();
178 die(2, _("Cannot allocate netlink handle!\n"));
179 nl_disable_sequence_check(handle);
180 ret = genl_connect(handle);
182 die(2, _("Cannot connect to netlink socket: %s\n"), strerror(-ret));
183 family = genl_ctrl_resolve(handle, "VFS_DQUOT");
185 die(2, _("Cannot resolve quota netlink name: %s\n"), strerror(-ret));
187 ret = nl_socket_add_membership(handle, family);
189 die(2, _("Cannot join quota multicast group: %s\n"), strerror(-ret));
191 ret = nl_socket_modify_cb(handle, NL_CB_VALID, NL_CB_CUSTOM,
192 quota_nl_parser, NULL);
194 die(2, _("Cannot register callback for"
195 " netlink messages: %s\n"), strerror(-ret));
200 static DBusConnection *init_dbus(void)
202 DBusConnection *handle;
205 dbus_error_init(&err);
206 handle = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
207 if (dbus_error_is_set(&err))
208 die(2, _("Cannot connect to system DBUS: %s\n"), err.message);
210 dbus_connection_set_exit_on_disconnect(handle, FALSE);
214 static int write_all(int fd, char *buf, int len)
219 ret = write(fd, buf, len);
228 #define WARN_BUF_SIZE 512
230 /* Scan through utmp, find latest used controlling tty and write to it */
231 static void write_console_warning(struct quota_warning *warn)
234 char user[MAXNAMELEN];
237 time_t max_atime = 0;
238 char max_dev[PATH_MAX];
240 char warnbuf[WARN_BUF_SIZE];
243 if ((warn->warntype == QUOTA_NL_IHARDBELOW ||
244 warn->warntype == QUOTA_NL_ISOFTBELOW ||
245 warn->warntype == QUOTA_NL_BHARDBELOW ||
246 warn->warntype == QUOTA_NL_BSOFTBELOW) && !(flags & FL_PRINTBELOW))
248 uid2user(warn->caused_id, user);
249 strcpy(dev, "/dev/");
253 while ((uent = getutent())) {
254 if (uent->ut_type != USER_PROCESS)
256 /* Entry for a different user? */
257 if (strcmp(user, uent->ut_user))
259 sstrncpy(dev+5, uent->ut_line, PATH_MAX-5);
260 if (stat(dev, &st) < 0)
261 continue; /* Failed to stat - not a good candidate for warning... */
262 if (max_atime < st.st_atime) {
263 max_atime = st.st_atime;
264 strcpy(max_dev, dev);
269 * This can happen quite easily so don't spam syslog with
272 if (flags & FL_NODAEMON)
273 errstr(_("Failed to find tty of user %llu to report warning to.\n"), (unsigned long long)warn->caused_id);
276 fd = open(max_dev, O_WRONLY);
278 errstr(_("Failed to open tty %s of user %llu to report warning.\n"), dev, (unsigned long long)warn->caused_id);
281 id2name(warn->excess_id, warn->qtype, user);
282 if (warn->warntype == QUOTA_NL_ISOFTWARN ||
283 warn->warntype == QUOTA_NL_BSOFTWARN)
284 level = _("Warning");
285 else if (warn->warntype == QUOTA_NL_IHARDWARN ||
286 warn->warntype == QUOTA_NL_BHARDWARN)
290 switch (warn->warntype) {
291 case QUOTA_NL_IHARDWARN:
292 msg = _("file limit reached");
294 case QUOTA_NL_ISOFTLONGWARN:
295 msg = _("file quota exceeded too long");
297 case QUOTA_NL_ISOFTWARN:
298 msg = _("file quota exceeded");
300 case QUOTA_NL_BHARDWARN:
301 msg = _("block limit reached");
303 case QUOTA_NL_BSOFTLONGWARN:
304 msg = _("block quota exceeded too long");
306 case QUOTA_NL_BSOFTWARN:
307 msg = _("block quota exceeded");
309 case QUOTA_NL_IHARDBELOW:
310 msg = _("got below file limit");
312 case QUOTA_NL_ISOFTBELOW:
313 msg = _("got below file quota");
315 case QUOTA_NL_BHARDBELOW:
316 msg = _("got below block limit");
318 case QUOTA_NL_BSOFTBELOW:
319 msg = _("got below block quota");
322 msg = _("unknown quota warning");
324 sprintf(warnbuf, "%s: %s %s %s.\r\n", level, type2name(warn->qtype), user, msg);
325 if (write_all(fd, warnbuf, strlen(warnbuf)) < 0)
326 errstr(_("Failed to write quota message for user %llu to %s: %s\n"), (unsigned long long)warn->caused_id, dev, strerror(errno));
330 /* Send warning through DBUS */
331 static void write_dbus_warning(struct DBusConnection *dhandle, struct quota_warning *warn)
334 DBusMessageIter args;
336 msg = dbus_message_new_signal("/", "com.system.quota.warning", "warning");
339 errstr(_("Cannot create DBUS message: No enough memory.\n"));
342 dbus_message_iter_init_append(msg, &args);
343 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &warn->qtype))
345 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT64, &warn->excess_id))
347 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &warn->warntype))
349 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &warn->dev_major))
351 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &warn->dev_minor))
353 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT64, &warn->caused_id))
356 if (!dbus_connection_send(dhandle, msg, NULL)) {
357 errstr(_("Failed to write message to dbus: No enough memory.\n"));
360 dbus_connection_flush(dhandle);
363 dbus_message_unref(msg);
366 static void run(struct nl_handle *nhandle)
371 ret = nl_recvmsgs_default(nhandle);
373 errstr(_("Failed to read or parse quota netlink"
374 " message: %s\n"), strerror(-ret));
378 /* Build file name (absolute path) to PID file of this daemon.
379 * The returned name is allocated on heap. */
380 static char *build_pid_file_name(void)
382 char *pid_name = NULL;
384 errstr(_("Undefined program name.\n"));
387 pid_name = malloc(9 + strlen(progname) + 4 + 1);
389 errstr(_("Not enough memory to build PID file name.\n"));
392 sprintf(pid_name, "/var/run/%s.pid", progname);
396 /* Store daemon's PID to file */
397 static int store_pid(void)
402 pid_name = build_pid_file_name();
406 pid_file = fopen(pid_name, "w");
408 errstr(_("Could not open PID file '%s': %s\n"),
409 pid_name, strerror(errno));
413 if (fprintf(pid_file, "%jd\n", (intmax_t)getpid()) < 0) {
414 errstr(_("Could not write daemon's PID into '%s'.\n"),
420 if (fclose(pid_file)) {
421 errstr(_("Could not close PID file '%s'.\n"), pid_name);
430 /* Handler for SIGTERM to remove PID file */
431 static void remove_pid(int signal)
435 pid_name = build_pid_file_name();
443 /* Store daemon's PID into file and register its removal on SIGTERM */
444 static void use_pid_file(void)
446 struct sigaction term_action;
448 term_action.sa_handler = remove_pid;
449 term_action.sa_flags = 0;
450 if (sigaction(SIGTERM, &term_action, NULL))
451 errstr(_("Could not register PID file removal on SIGTERM.\n"));
453 errstr(_("Could not store my PID %jd.\n"), (intmax_t )getpid());
456 int main(int argc, char **argv)
458 struct nl_handle *nhandle;
461 progname = basename(argv[0]);
462 parse_options(argc, argv);
464 nhandle = init_netlink();
465 if (!(flags & FL_NODBUS))
466 dhandle = init_dbus();
467 if (!(flags & FL_NODAEMON)) {