diff -pruN 1.2.1-3/debian/changelog 1.3.0-1/debian/changelog
--- 1.2.1-3/debian/changelog	2022-05-12 22:30:00.000000000 +0000
+++ 1.3.0-1/debian/changelog	2022-08-03 19:40:01.000000000 +0000
@@ -1,3 +1,12 @@
+psi-notify (1.3.0-1) unstable; urgency=low
+
+  * Update to 1.3.0
+  * add `make install`
+  * `README.md`: update link to desktop notifications spec
+  * `README.md`: add links to Debian and Ubuntu packages and Repology
+
+ -- Michel Alexandre Salim <michel@michel-slm.name>  Wed, 03 Aug 2022 14:40:01 -0500
+
 psi-notify (1.2.1-3) unstable; urgency=low
 
   * Fix license (should be Expat not MIT)
diff -pruN 1.2.1-3/debian/patches/add-make-install.diff 1.3.0-1/debian/patches/add-make-install.diff
--- 1.2.1-3/debian/patches/add-make-install.diff	1970-01-01 00:00:00.000000000 +0000
+++ 1.3.0-1/debian/patches/add-make-install.diff	2022-08-03 19:28:02.000000000 +0000
@@ -0,0 +1,30 @@
+Add `make install` target
+
+Sent upstream in https://github.com/cdown/psi-notify/pull/27
+--- a/Makefile
++++ b/Makefile
+@@ -1,6 +1,10 @@
+ CFLAGS:=-std=gnu11 -O2 -pedantic -Wall -Wextra -Werror $(shell pkg-config --cflags libnotify) $(CFLAGS)
+ LDFLAGS:=$(shell pkg-config --libs libnotify) $(LDFLAGS)
+ 
++INSTALL:=install
++prefix:=/usr/local
++bindir:=$(prefix)/bin
++
+ WANT_SD_NOTIFY=1
+ HAS_LIBSYSTEMD=$(shell pkg-config libsystemd && echo 1 || echo 0)
+ 
+@@ -57,6 +61,13 @@ clang-tidy:
+ 	# DeprecatedOrUnsafeBufferHandling: See https://stackoverflow.com/a/50724865/945780
+ 	clang-tidy psi-notify.c -checks=-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling -- $(CFLAGS) $(LDFLAGS)
+ 
++install: all
++	mkdir -p $(DESTDIR)$(bindir)/
++	$(INSTALL) -pt $(DESTDIR)$(bindir)/ $(EXECUTABLES)
++ifeq ($(HAS_LIBSYSTEMD),1)
++	$(INSTALL) -Dp -m 644 psi-notify.service $(DESTDIR)$(prefix)/lib/systemd/user/psi-notify.service
++endif
++
+ test: CFLAGS+=-D_FORTIFY_SOURCE=2
+ test:
+ 	$(CC) $(CFLAGS) test/test.c -o test/test $(LIBS) $(LDFLAGS)
diff -pruN 1.2.1-3/debian/patches/readme-update.diff 1.3.0-1/debian/patches/readme-update.diff
--- 1.2.1-3/debian/patches/readme-update.diff	1970-01-01 00:00:00.000000000 +0000
+++ 1.3.0-1/debian/patches/readme-update.diff	2022-08-03 19:38:46.000000000 +0000
@@ -0,0 +1,40 @@
+Update README.md
+
+- add installation instructions for Debian and Ubuntu
+- add link to repology
+- update link to desktop notifications spec
+
+Sent upstream in https://github.com/cdown/psi-notify/pull/28
+--- a/README.md
++++ b/README.md
+@@ -17,7 +17,7 @@ utilisation graphs and other metrics can
+ - Runs unprivileged
+ - Minimal resource usage
+ - Works with any notifier using [Desktop
+-  Notifications](http://www.galago-project.org/specs/notification/0.9/index.html)
++  Notifications](https://specifications.freedesktop.org/notification-spec/latest/)
+ 
+ ## Requirements
+ 
+@@ -29,10 +29,21 @@ utilisation graphs and other metrics can
+ On Arch, the [psi-notify AUR
+ package](https://aur.archlinux.org/packages/psi-notify/) is available.
+ 
++On Debian (bookworm and sid), the [psi-notify package
++](https://packages.debian.org/search?keywords=psi-notify) is available.
++
+ On Fedora and RHEL/CentOS 8, the [psi-notify
+ package](https://src.fedoraproject.org/rpms/psi-notify) is available in
+ Fedora/EPEL.
+ 
++On Ubuntu, the [psi-notify package
++](https://packages.ubuntu.com/search?keywords=psi-notify) is in kinetic. For
++older releases, please use the [psi-notify PPA
++](https://launchpad.net/~michel-slm/+archive/ubuntu/psi-notify).
++
++You can also find packages in [Repology
++](https://repology.org/project/psi-notify/versions).
++
+ Otherwise, manual installation is as simple as running `make` and putting the
+ resulting `psi-notify` binary in your PATH. You will need `libnotify`
+ installed.
diff -pruN 1.2.1-3/debian/patches/series 1.3.0-1/debian/patches/series
--- 1.2.1-3/debian/patches/series	1970-01-01 00:00:00.000000000 +0000
+++ 1.3.0-1/debian/patches/series	2022-08-03 19:36:43.000000000 +0000
@@ -0,0 +1,2 @@
+add-make-install.diff
+readme-update.diff
diff -pruN 1.2.1-3/debian/psi-notify.install 1.3.0-1/debian/psi-notify.install
--- 1.2.1-3/debian/psi-notify.install	2022-05-09 23:16:40.000000000 +0000
+++ 1.3.0-1/debian/psi-notify.install	1970-01-01 00:00:00.000000000 +0000
@@ -1,2 +0,0 @@
-psi-notify /usr/bin
-psi-notify.service /usr/lib/systemd/user
diff -pruN 1.2.1-3/debian/rules 1.3.0-1/debian/rules
--- 1.2.1-3/debian/rules	2022-05-12 22:30:00.000000000 +0000
+++ 1.3.0-1/debian/rules	2022-08-03 17:50:00.000000000 +0000
@@ -4,3 +4,6 @@ export DEB_BUILD_MAINT_OPTIONS = hardeni
 
 %:
 	dh $@
+
+override_dh_auto_install:
+	dh_auto_install -- prefix=/usr
diff -pruN 1.2.1-3/psi-notify.c 1.3.0-1/psi-notify.c
--- 1.2.1-3/psi-notify.c	2020-06-07 23:09:41.000000000 +0000
+++ 1.3.0-1/psi-notify.c	2022-05-06 16:16:10.000000000 +0000
@@ -4,6 +4,7 @@
 #include <libnotify/notify.h>
 #include <linux/limits.h>
 #include <pwd.h>
+#include <signal.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -29,7 +30,7 @@ static char output_buf[512];
 static Resource *all_res[] = {&cfg.cpu, &cfg.memory, &cfg.io};
 static bool using_seat = false;
 static const time_t expiry_sec = 10;
-static const double alert_clear_penalty = 5.0;
+static const double alert_clear_hysteresis = 5.0;
 
 #define DEFAULT_ALERT_STATE                                                    \
     { NULL, 0, A_INACTIVE }
@@ -61,9 +62,9 @@ static void configure_signal_handlers(vo
         sigaction(SIGHUP,
                   &(const struct sigaction){.sa_handler = request_reload_config,
                                             .sa_flags = SA_RESTART},
-                  NULL) >= 0);
-    expect(sigaction(SIGTERM, &sa_exit, NULL) >= 0);
-    expect(sigaction(SIGINT, &sa_exit, NULL) >= 0);
+                  NULL) == 0);
+    expect(sigaction(SIGTERM, &sa_exit, NULL) == 0);
+    expect(sigaction(SIGINT, &sa_exit, NULL) == 0);
 }
 
 static void alert_destroy(NotifyNotification *n) {
@@ -297,11 +298,13 @@ static int config_update_from_file(FILE
     if (f) {
         config_reset_user_facing();
     } else {
+        ret = -errno;
+
         if (config_reload_pending) {
             /* This was from a SIGHUP, so we already have a config. Keep it. */
             warn("Config reload request ignored, cannot open %s: %s\n",
                  config_path, strerror(errno));
-            return -errno;
+            return ret;
         }
 
         if (*config_path) {
@@ -319,8 +322,8 @@ static int config_update_from_file(FILE
         cfg.memory.thresholds.avg10.some = 10.00;
         cfg.io.thresholds.avg10.full = 15.00;
 
-        ret = -errno;
-        goto out_update_watchdog;
+        watchdog_update_usec();
+        return ret;
     }
 
     while (fgets(line, sizeof(line), f)) {
@@ -357,10 +360,7 @@ static int config_update_from_file(FILE
     }
 
     fclose(f);
-
-out_update_watchdog:
     watchdog_update_usec();
-
     return ret;
 }
 
@@ -396,11 +396,38 @@ static int config_init(FILE **override_c
     cfg.io.human_name = "I/O";
     cfg.io.has_full = 1;
 
+    /* Currently not user configurable, file an issue if you need it to be. */
+    cfg.io_min_blocked_tasks = 2;
+
     (void)config_update_from_file(override_config);
 
     return 0;
 }
 
+static int get_nr_blocked_tasks(void) {
+    /*
+     * If in future system wide metrics prove not granular enough for purpose,
+     * iterate cgroup.threads recursively inside the seat cgroup.
+     */
+    FILE *f = fopen("/proc/stat", "re");
+    int procs_blocked, ch;
+
+    expect(f);
+
+    while (!feof(f)) {
+        if (fscanf(f, "procs_blocked %d", &procs_blocked) == 1) {
+            fclose(f);
+            return procs_blocked;
+        } else {
+            while ((ch = fgetc(f)) != EOF && ch != '\n')
+                ;
+        }
+    }
+
+    fclose(f);
+    return -ENODATA;
+}
+
 /*
  * 64 is len("some avg10=100.00 avg60=100.00 avg300=100.00") + a bit more to
  * make sure fgets() reads past total= and seeks up to \n.
@@ -413,8 +440,8 @@ static int config_init(FILE **override_c
 
 #define MIN_PSI 1.0
 
-static double psi_penalty(double orig_psi) {
-    const double penalised_psi = orig_psi - alert_clear_penalty;
+static double psi_hysteresis(double orig_psi) {
+    const double penalised_psi = orig_psi - alert_clear_hysteresis;
 
     if (penalised_psi < MIN_PSI) {
         /* Too small to make granular volatility decisions. */
@@ -448,23 +475,42 @@ static AlertState pressure_check_single_
             return A_ACTIVE;
         }
 
-        if (COMPARE_THRESH(psi_penalty(r->thresholds.avg10.some), avg10) ||
-            COMPARE_THRESH(psi_penalty(r->thresholds.avg60.some), avg60) ||
-            COMPARE_THRESH(psi_penalty(r->thresholds.avg300.some), avg300)) {
+        if (COMPARE_THRESH(psi_hysteresis(r->thresholds.avg10.some), avg10) ||
+            COMPARE_THRESH(psi_hysteresis(r->thresholds.avg60.some), avg60) ||
+            COMPARE_THRESH(psi_hysteresis(r->thresholds.avg300.some), avg300)) {
             return A_STABILISING;
         }
 
         return A_INACTIVE;
     } else if (streq(type, "full")) {
+        if (r->type == RT_IO &&
+            active_notif[r->type].last_state == A_INACTIVE) {
+            int ret;
+
+            /*
+             * On a desktop system there's usually very few runnable tasks,
+             * which means that a single task doing slow I/O can
+             * disproportionately bump IO full for the whole system or user
+             * scope. To work around this, require that at least two tasks are
+             * blocked to issue warnings based on IO metrics. Checking if the
+             * last state was inactive avoids flapping if the blocked number
+             * varies repeatedly.
+             */
+            ret = get_nr_blocked_tasks();
+            if (ret >= 0 && ret < cfg.io_min_blocked_tasks) {
+                return A_INACTIVE;
+            }
+        }
+
         if (COMPARE_THRESH(r->thresholds.avg10.full, avg10) ||
             COMPARE_THRESH(r->thresholds.avg60.full, avg60) ||
             COMPARE_THRESH(r->thresholds.avg300.full, avg300)) {
             return A_ACTIVE;
         }
 
-        if (COMPARE_THRESH(psi_penalty(r->thresholds.avg10.full), avg10) ||
-            COMPARE_THRESH(psi_penalty(r->thresholds.avg60.full), avg60) ||
-            COMPARE_THRESH(psi_penalty(r->thresholds.avg300.full), avg300)) {
+        if (COMPARE_THRESH(psi_hysteresis(r->thresholds.avg10.full), avg10) ||
+            COMPARE_THRESH(psi_hysteresis(r->thresholds.avg60.full), avg60) ||
+            COMPARE_THRESH(psi_hysteresis(r->thresholds.avg300.full), avg300)) {
             return A_STABILISING;
         }
 
@@ -529,25 +575,20 @@ static AlertState pressure_check(const R
     }
 
     ret = pressure_check_single_line(f, r);
-    if (ret != A_INACTIVE) {
-        goto out_fclose;
+    if (ret == A_INACTIVE && r->has_full) {
+        ret = pressure_check_single_line(f, r);
     }
 
-    if (!r->has_full) {
-        goto out_fclose;
-    }
-
-    ret = pressure_check_single_line(f, r);
-
-out_fclose:
     fclose(f);
     return ret;
 }
 
 #define LOG_ALERT_STATE(r, state)                                              \
-    expect(*r->human_name);                                                    \
-    info("%c%s alert: %s\n", toupper(r->human_name[0]), r->human_name + 1,     \
-         state)
+    do {                                                                       \
+        expect(*r->human_name);                                                \
+        info("%c%s alert: %s\n", toupper(r->human_name[0]), r->human_name + 1, \
+             state);                                                           \
+    } while (0)
 
 /* 0 means already active, 1 means newly active. */
 static int alert_user_if_new(const Resource *r) {
@@ -734,12 +775,22 @@ static void print_config(void) {
     printf("\n");
 }
 
+static void block_all_signals(void) {
+    sigset_t mask;
+    sigfillset(&mask);
+    expect(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
+}
+
+static void unblock_all_signals(void) {
+    sigset_t mask;
+    sigemptyset(&mask);
+    expect(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
+}
+
 #ifndef UNIT_TEST
 int main(int argc, char *argv[]) {
     unsigned long num_iters = 0;
 
-    (void)argv;
-
     if (argc != 1) {
         die("%s doesn't accept any arguments.\n", argv[0]);
     }
@@ -755,6 +806,9 @@ int main(int argc, char *argv[]) {
 
     expect(setvbuf(stdout, output_buf, _IOLBF, sizeof(output_buf)) == 0);
     configure_signal_handlers();
+
+    /* Before glib spawns threads, make sure we're blocked. */
+    block_all_signals();
     expect(notify_init("psi-notify"));
 
     if (using_seat) {
@@ -784,6 +838,8 @@ int main(int argc, char *argv[]) {
 
         for_each_arr (i, all_res) { pressure_check_notify_if_new(all_res[i]); }
 
+        unblock_all_signals();
+
         if (config_reload_pending) {
             sd_notify(0, "RELOADING=1\nSTATUS=Reloading config...");
             if (config_update_from_file(NULL) == 0) {
@@ -802,8 +858,12 @@ int main(int argc, char *argv[]) {
         }
 
         ++num_iters;
+
+        block_all_signals();
     }
 
+    unblock_all_signals();
+
     info("Terminating after %lu intervals elapsed.\n", num_iters);
     sd_notify(0, "STOPPING=1\nSTATUS=Tearing down...");
 
diff -pruN 1.2.1-3/psi-notify.h 1.3.0-1/psi-notify.h
--- 1.2.1-3/psi-notify.h	2020-06-07 23:09:41.000000000 +0000
+++ 1.3.0-1/psi-notify.h	2022-05-06 16:16:10.000000000 +0000
@@ -39,6 +39,7 @@ typedef struct {
     time_t update_interval;
     bool log_pressures;
     int psi_dir_fd;
+    int io_min_blocked_tasks;
 } Config;
 
 typedef struct {
@@ -52,8 +53,10 @@ typedef struct {
 #define info(format, ...) printf("INFO: " format, __VA_ARGS__)
 #define warn(format, ...) fprintf(stderr, "WARN: " format, __VA_ARGS__)
 #define die(format, ...)                                                       \
-    fprintf(stderr, "FATAL: " format, __VA_ARGS__);                            \
-    abort()
+    do {                                                                       \
+        fprintf(stderr, "FATAL: " format, __VA_ARGS__);                        \
+        abort();                                                               \
+    } while (0)
 
 #define unreachable() die("%s\n", "Allegedly unreachable code reached\n")
 #define expect(x)                                                              \
@@ -64,7 +67,10 @@ typedef struct {
     } while (0)
 
 #define snprintf_check(buf, len, fmt, ...)                                     \
-    expect((size_t)snprintf(buf, len, fmt, __VA_ARGS__) < (len))
+    do {                                                                       \
+        int needed = snprintf(buf, len, fmt, __VA_ARGS__);                     \
+        expect(needed >= 0 && (size_t)needed < (len));                         \
+    } while (0)
 
 #define for_each_arr(i, items)                                                 \
     for (i = 0; i < sizeof(items) / sizeof(items[0]); i++)
diff -pruN 1.2.1-3/README.md 1.3.0-1/README.md
--- 1.2.1-3/README.md	2020-06-07 23:09:41.000000000 +0000
+++ 1.3.0-1/README.md	2022-05-06 16:16:10.000000000 +0000
@@ -1,4 +1,4 @@
-# psi-notify | [![Tests](https://img.shields.io/travis/cdown/psi-notify/master.svg)](https://travis-ci.com/cdown/psi-notify) [![Fuzzer](https://img.shields.io/travis/cdown/psi-notify/master.svg?label=fuzz)](https://travis-ci.com/cdown/psi-notify) [![LGTM](https://img.shields.io/lgtm/grade/cpp/github/cdown/psi-notify.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/cdown/psi-notify/alerts/?mode=list)
+# psi-notify | [![Tests](https://img.shields.io/travis/com/cdown/psi-notify/master.svg)](https://travis-ci.com/cdown/psi-notify) [![Fuzzer](https://img.shields.io/travis/com/cdown/psi-notify/master.svg?label=fuzz)](https://travis-ci.com/cdown/psi-notify) [![LGTM](https://img.shields.io/lgtm/grade/cpp/github/cdown/psi-notify.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/cdown/psi-notify/alerts/?mode=list)
 
 **tl;dr: psi-notify can alert you when resources on your machine are becoming
 oversaturated, and allow you to take action *before* your system slows to a
diff -pruN 1.2.1-3/test/test.c 1.3.0-1/test/test.c
--- 1.2.1-3/test/test.c	2020-06-07 23:09:41.000000000 +0000
+++ 1.3.0-1/test/test.c	2022-05-06 16:16:10.000000000 +0000
@@ -84,18 +84,18 @@ static bool test_pressure_check(void) {
 
     /* Threshold are too low, but we're within 5 pct boundary. */
     psi_f = fmemopen((void *)raw_psi, strlen(raw_psi), "r");
-    cfg.io.thresholds.avg300.full = 90.00;
-    t_assert(pressure_check(&cfg.io, psi_f) == A_STABILISING);
+    cfg.memory.thresholds.avg300.full = 90.00;
+    t_assert(pressure_check(&cfg.memory, psi_f) == A_STABILISING);
 
     /* Add another threshold which trips. */
     psi_f = fmemopen((void *)raw_psi, strlen(raw_psi), "r");
-    cfg.io.thresholds.avg300.full = 9.99;
-    t_assert(pressure_check(&cfg.io, psi_f) == A_ACTIVE);
+    cfg.memory.thresholds.avg300.full = 9.99;
+    t_assert(pressure_check(&cfg.memory, psi_f) == A_ACTIVE);
 
     /* Thresholds are too low. */
     psi_f = fmemopen((void *)raw_psi, strlen(raw_psi), "r");
-    cfg.io.thresholds.avg300.full = 95.00;
-    t_assert(pressure_check(&cfg.io, psi_f) == A_INACTIVE);
+    cfg.memory.thresholds.avg300.full = 95.00;
+    t_assert(pressure_check(&cfg.memory, psi_f) == A_INACTIVE);
 
     return true;
 }
diff -pruN 1.2.1-3/.travis.yml 1.3.0-1/.travis.yml
--- 1.2.1-3/.travis.yml	2020-06-07 23:09:41.000000000 +0000
+++ 1.3.0-1/.travis.yml	2022-05-06 16:16:10.000000000 +0000
@@ -55,12 +55,13 @@ jobs:
         - make clean clang-everything WANT_SD_NOTIFY=0
       arch: s390x
 
-    - name: Run arm64 tests
-      script:
-        - make test
-        - make clean clang-tidy
-        - make clean clang-everything
-        - make clean test WANT_SD_NOTIFY=0
-        - make clean clang-tidy WANT_SD_NOTIFY=0
-        - make clean clang-everything WANT_SD_NOTIFY=0
-      arch: arm64
+    # ARM64 machines are super unreliable to reserve on Travis, sigh...
+    #- name: Run arm64 tests
+    #  script:
+    #    - make test
+    #    - make clean clang-tidy
+    #    - make clean clang-everything
+    #    - make clean test WANT_SD_NOTIFY=0
+    #    - make clean clang-tidy WANT_SD_NOTIFY=0
+    #    - make clean clang-everything WANT_SD_NOTIFY=0
+    #  arch: arm64
