sepolicy-analyze: Add ability to list all attributes.

This could be useful in diffs between policy versions.

Bug: 37357742
Test: sepolicy-analyze lists all attributes in precompiled_policy.
Change-Id: I6532a93d4102cf9cb12b73ee8ed86ece368f9131
diff --git a/tools/sepolicy-analyze/README b/tools/sepolicy-analyze/README
index fdee588..c6657ec 100644
--- a/tools/sepolicy-analyze/README
+++ b/tools/sepolicy-analyze/README
@@ -69,6 +69,10 @@
 
     Displays the attributes associated with the specified type name.
 
+    sepolicy-analyze out/target/product/<board>/root/sepolicy attribute -l
+
+    Displays all attributes in the policy.
+
     NEVERALLOW CHECKING (neverallow)
     sepolicy-analyze out/target/product/<board>/root/sepolicy neverallow \
     [-w] [-d] [-f neverallows.conf] | [-n "neverallow string"]
diff --git a/tools/sepolicy-analyze/attribute.c b/tools/sepolicy-analyze/attribute.c
index ae98aa9..f7c9b4c 100644
--- a/tools/sepolicy-analyze/attribute.c
+++ b/tools/sepolicy-analyze/attribute.c
@@ -3,7 +3,7 @@
 #include "attribute.h"
 
 void attribute_usage() {
-    fprintf(stderr, "\tattribute <name> [-r|--reverse]\n");
+    fprintf(stderr, "\tattribute [-l|--list] [-r|--reverse] <name>\n");
 }
 
 static void retrieve_mapping(policydb_t *policydb, struct type_datum *dat, char *name, int reverse) {
@@ -53,29 +53,58 @@
     return 0;
 }
 
+static int print_attr(__attribute__ ((unused)) hashtab_key_t k,
+                      hashtab_datum_t d, void *args) {
+    struct type_datum *dat = (struct type_datum *)d;
+    policydb_t *pdb = (policydb_t *)args;
+    if (!dat) {
+        fprintf(stderr, "type encountered without datum!\n");
+        return -1;
+    }
+    if (dat->flavor == TYPE_ATTRIB) {
+        printf("%s\n", pdb->p_type_val_to_name[dat->s.value - 1]);
+    }
+    return 0;
+}
+
+static int list_all_attributes(policydb_t *policydb) {
+    return hashtab_map(policydb->p_types.table, print_attr, policydb);
+}
+
 int attribute_func (int argc, char **argv, policydb_t *policydb) {
+    int rc = -1;
+    int list = 0;
     int reverse = 0;
     char ch;
 
     struct option attribute_options[] = {
+        {"list", no_argument, NULL, 'l'},
         {"reverse", no_argument, NULL, 'r'},
         {NULL, 0, NULL, 0}
     };
 
-    while ((ch = getopt_long(argc, argv, "r", attribute_options, NULL)) != -1) {
+    while ((ch = getopt_long(argc, argv, "lr", attribute_options, NULL)) != -1) {
         switch (ch) {
+        case 'l':
+            list = 1;
+            break;
         case 'r':
             reverse = 1;
             break;
         default:
             USAGE_ERROR = true;
-            return -1;
+            goto out;
         }
     }
 
-    if (argc != 2 && !(reverse && argc == 3)) {
+    if ((argc != 2 && !(reverse && argc == 3)) || (list && reverse)) {
         USAGE_ERROR = true;
-        return -1;
+        goto out;
     }
-    return list_attribute(policydb, argv[optind], reverse);
+    if (list)
+        rc = list_all_attributes(policydb);
+    else
+        rc = list_attribute(policydb, argv[optind], reverse);
+ out:
+    return rc;
 }