checkfc: validate that all rules are matching

For the file backend, libselinux keeps track of which rules has matched.
Set up the callback and capture any log message from selinux_stats. If,
at least one rule has not been used, exit with the status code 1.

Bug: 299839280
Test: checkfc -t ./private/file_contexts ./tests/plat_file_contexts_test
Change-Id: I33d88b4234756cd13e29c5c8c081d97b6590810e
diff --git a/tools/checkfc.c b/tools/checkfc.c
index 05826f9..051e24b 100644
--- a/tools/checkfc.c
+++ b/tools/checkfc.c
@@ -271,6 +271,19 @@
      printf("%s\n", result_str[result]);
 }
 
+static int warnings = 0;
+static int log_callback(int type, const char *fmt, ...) {
+    va_list ap;
+
+    if (type == SELINUX_WARNING) {
+        warnings += 1;
+    }
+    va_start(ap, fmt);
+    vfprintf(stderr, fmt, ap);
+    va_end(ap);
+    return 0;
+}
+
 static void do_test_data_and_die_on_error(struct selinux_opt opts[], unsigned int backend,
         char *paths[])
 {
@@ -329,7 +342,15 @@
 
     // Prints the coverage of file_contexts on the test data. It includes
     // warnings for rules that have not been hit by any test example.
+    union selinux_callback cb;
+    cb.func_log = log_callback;
+    selinux_set_callback(SELINUX_CB_LOG, cb);
     selabel_stats(global_state.sepolicy.sehnd[0]);
+    if (warnings) {
+        fprintf(stderr, "No test entries were found for the contexts above. " \
+                        "You may need to update %s.\n", paths[1]);
+        exit(1);
+    }
 }
 
 static void do_fc_check_and_die_on_error(struct selinux_opt opts[], unsigned int backend, filemode mode,