diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | Makefile | 14 | ||||
-rw-r--r-- | udev-block-notify.c | 207 | ||||
-rw-r--r-- | udev-block-notify.desktop | 8 |
4 files changed, 232 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a8e9da0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*~ +*.o +udev-block-notify diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f207c3f --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +# udev-block-notify - Notify about udev block events + +CC := gcc +CFLAGS += $(shell pkg-config --cflags --libs libudev) \ + $(shell pkg-config --cflags --libs blkid) \ + $(shell pkg-config --cflags --libs libnotify) +VERSION = $(shell git describe --tags --long) + +all: udev-block-notify.c + $(CC) $(CFLAGS) -o udev-block-notify udev-block-notify.c \ + -DVERSION="\"$(VERSION)\"" + +clean: + /bin/rm -f *.o *~ udev-block-notify diff --git a/udev-block-notify.c b/udev-block-notify.c new file mode 100644 index 0000000..5e0d6c3 --- /dev/null +++ b/udev-block-notify.c @@ -0,0 +1,207 @@ +/* + * (C) 2011-2012 by Christian Hesse <mail@eworm.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include <libnotify/notify.h> +#include <libudev.h> +#include <blkid.h> + +#define PROGNAME "udev-block-notify" + +#define NOTIFICATION_TIMEOUT 10000 +#ifndef DEBUG +#define DEBUG 0 +#endif + +#define ICON_ADD "media-removable" +#define ICON_REMOVE "media-removable" +#define ICON_MOVE "media-removable" +#define ICON_CHANGE "media-removable" +#define ICON_DEFAULT "media-removable" + +#define TEXT_TOPIC "Udev Block Notification" +#define TEXT_ADD "Device <b>%s</b> (%i:%i) <b>appeared</b>." +#define TEXT_REMOVE "Device <b>%s</b> (%i:%i) <b>disappeared</b>." +#define TEXT_MOVE "Device <b>%s</b> (%i:%i) was <b>renamed</b>." +#define TEXT_CHANGE "Device <b>%s</b> (%i:%i) media <b>changed</b>." +#define TEXT_DEFAULT "Anything happend to <b>%s</b> (%i:%i)... Don't know." +#define TEXT_TAG "%s\n%s: <i>%s</i>" + +int main (int argc, char ** argv) { + blkid_cache cache = NULL; + blkid_dev blkdev = NULL; + blkid_tag_iterate iter = NULL; + char action; + char *device = NULL, *icon = NULL, *notifystr = NULL, *read = NULL; + const char *type, *value, *devname; + fd_set readfds; + GError *error = NULL; + int fdcount, devnum, errcount = 0; + NotifyNotification *notification; + NotifyNotification ***notificationref; + struct udev_device *dev = NULL; + struct udev_monitor *mon = NULL; + struct udev *udev = NULL; + short int *maxminor; + unsigned short int i, major, minor; + + printf("%s: %s v%s (compiled: " __DATE__ ", " __TIME__ ")\n", argv[0], PROGNAME, VERSION); + + if(!notify_init("Udev-Block-Notification")) { + fprintf(stderr, "%s: Can't create notify.\n", argv[0]); + exit(EXIT_FAILURE); + } + + udev = udev_new(); + if(!udev) { + fprintf(stderr, "%s: Can't create udev.\n", argv[0]); + exit(EXIT_FAILURE); + } + + mon = udev_monitor_new_from_netlink(udev, "udev"); + udev_monitor_filter_add_match_subsystem_devtype(mon, "block", NULL); + udev_monitor_enable_receiving(mon); + + notificationref = malloc(256 * sizeof(size_t)); + for(i = 0; i < 256; i++) + notificationref[i] = NULL; + maxminor = malloc(256 * sizeof(short int)); + for(i = 0; i < 256; i++) + maxminor[i] = -1; + + while (1) { + FD_ZERO(&readfds); + if (mon != NULL) + FD_SET(udev_monitor_get_fd(mon), &readfds); + + fdcount = select(udev_monitor_get_fd(mon) + 1, &readfds, NULL, NULL, NULL); + + if ((mon != NULL) && FD_ISSET(udev_monitor_get_fd(mon), &readfds)) { + dev = udev_monitor_receive_device(mon); + if(dev) { + device = (char *) udev_device_get_sysname(dev); + devnum = udev_device_get_devnum(dev); + major = devnum / 256; + minor = devnum - (major * 256); + + action = udev_device_get_action(dev)[0]; + switch(action) { + case 'a': + // a: add + notifystr = (char *) malloc(strlen(TEXT_ADD) + strlen(device)); + sprintf(notifystr, TEXT_ADD, device, major, minor); + icon = ICON_ADD; + break; + case 'r': + // r: remove + notifystr = (char *) malloc(strlen(TEXT_REMOVE) + strlen(device)); + sprintf(notifystr, TEXT_REMOVE, device, major, minor); + icon = ICON_REMOVE; + break; + case 'm': + // m: move + notifystr = (char *) malloc(strlen(TEXT_MOVE) + strlen(device)); + sprintf(notifystr, TEXT_MOVE, device, major, minor); + icon = ICON_MOVE; + break; + case 'c': + // c: change + notifystr = (char *) malloc(strlen(TEXT_CHANGE) + strlen(device)); + sprintf(notifystr, TEXT_CHANGE, device, major, minor); + icon = ICON_CHANGE; + break; + default: + // we should never get here I think... + notifystr = (char *) malloc(strlen(TEXT_DEFAULT) + strlen(device)); + sprintf(notifystr, TEXT_CHANGE, device, major, minor); + icon = ICON_DEFAULT; + } + + if (blkid_get_cache(&cache, read) != 0) + fprintf(stderr, "%s: Could not get blkid cache.\n", argv[0]); + + if (blkid_probe_all_new(cache) != 0) + fprintf(stderr, "%s: Could not probe new devices.\n", argv[0]); + + if (action != 'r') { + blkdev = blkid_get_dev(cache, udev_device_get_devnode(dev), BLKID_DEV_NORMAL); + + if (blkdev) { + iter = blkid_tag_iterate_begin(blkdev); + + while (blkid_tag_next(iter, &type, &value) == 0) { + notifystr = (char *) realloc(notifystr, strlen(TEXT_TAG) + strlen(notifystr) + strlen(type) + strlen(value)); + sprintf(notifystr, TEXT_TAG, notifystr, type, value); + } + + blkid_tag_iterate_end(iter); + blkid_put_cache(cache); + } else + fprintf(stderr, "%s: Could not get blkid device.\n", argv[0]); + } + +#if DEBUG + printf("%s: %s\n", argv[0], notifystr); +#endif + + if (maxminor[major] < minor) { + notificationref[major] = realloc(notificationref[major], (minor + 1) * sizeof(size_t)); + while(maxminor[major] < minor) + notificationref[major][++maxminor[major]] = NULL; + } + + if (notificationref[major][minor] == NULL) { + notification = notify_notification_new(TEXT_TOPIC, notifystr, icon); + notificationref[major][minor] = notification; + } else { + notification = notificationref[major][minor]; + notify_notification_update(notification, TEXT_TOPIC, notifystr, icon); + } + + notify_notification_set_timeout(notification, NOTIFICATION_TIMEOUT); + notify_notification_set_category(notification, PROGNAME); + notify_notification_set_urgency (notification, NOTIFY_URGENCY_NORMAL); + + while(!notify_notification_show(notification, &error)) { + if (errcount > 1) { + fprintf(stderr, "%s: Looks like we can not reconnect to notification daemon... Exiting.\n", argv[0]); + exit(EXIT_FAILURE); + } else { + g_printerr("%s: Error \"%s\" while trying to show notification. Trying to reconnect.\n", argv[0], error->message); + errcount++; + + g_error_free(error); + error = NULL; + + notify_uninit(); + + usleep(500 * 1000); + + if(!notify_init("Udev-Block-Notification")) { + fprintf(stderr, "%s: Can't create notify.\n", argv[0]); + exit(EXIT_FAILURE); + } + } + } + errcount = 0; + + free(notifystr); + udev_device_unref(dev); + } + + // This is not really needed... But we want to make shure not to eat 100% CPU if anything breaks. ;) + usleep(50 * 1000); + } + } + + udev_unref(udev); + return EXIT_SUCCESS; +} diff --git a/udev-block-notify.desktop b/udev-block-notify.desktop new file mode 100644 index 0000000..58521bd --- /dev/null +++ b/udev-block-notify.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Name=Udev Block Notify +Icon=media-removable +Exec=/usr/bin/udev-block-notify +Terminal=false +Type=Application +StartupNotify=false +Categories=Utility |