1# Authors: Karl MacMillan <kmacmillan@mentalrootkit.com>
2#
3# Copyright (C) 2006 Red Hat
4# see file 'COPYING' for use and warranty information
5#
6# This program is free software; you can redistribute it and/or
7# modify it under the terms of the GNU General Public License as
8# published by the Free Software Foundation; version 2 only
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20"""
21Classes and functions for the output of reference policy modules.
22
23This module takes a refpolicy.Module object and formats it for
24output using the ModuleWriter object. By separating the output
25in this way the other parts of Madison can focus solely on
26generating policy. This keeps the semantic / syntactic issues
27cleanly separated from the formatting issues.
28"""
29
30import refpolicy
31import util
32
33class ModuleWriter:
34    def __init__(self):
35        self.fd = None
36        self.module = None
37        self.sort = True
38        self.requires = True
39
40    def write(self, module, fd):
41        self.module = module
42
43        if self.sort:
44            sort_filter(self.module)
45
46        # FIXME - make this handle nesting
47        for node, depth in refpolicy.walktree(self.module, showdepth=True):
48            fd.write("%s\n" % str(node))
49
50# Helper functions for sort_filter - this is all done old school
51# C style rather than with polymorphic methods because this sorting
52# is specific to output. It is not necessarily the comparison you
53# want generally.
54
55# Compare two IdSets - we could probably do something clever
56# with different here, but this works.
57def id_set_cmp(x, y):
58    xl = util.set_to_list(x)
59    xl.sort()
60    yl = util.set_to_list(y)
61    yl.sort()
62
63    if len(xl) != len(yl):
64        return cmp(xl[0], yl[0])
65    for v in zip(xl, yl):
66        if v[0] != v[1]:
67            return cmp(v[0], v[1])
68    return 0
69
70# Compare two avrules
71def avrule_cmp(a, b):
72    ret = id_set_cmp(a.src_types, b.src_types)
73    if ret is not 0:
74        return ret
75    ret = id_set_cmp(a.tgt_types, b.tgt_types)
76    if ret is not 0:
77        return ret
78    ret = id_set_cmp(a.obj_classes, b.obj_classes)
79    if ret is not 0:
80        return ret
81
82    # At this point, who cares - just return something
83    return cmp(len(a.perms), len(b.perms))
84
85# Compare two interface calls
86def ifcall_cmp(a, b):
87    if a.args[0] != b.args[0]:
88        return cmp(a.args[0], b.args[0])
89    return cmp(a.ifname, b.ifname)
90
91# Compare an two avrules or interface calls
92def rule_cmp(a, b):
93    if isinstance(a, refpolicy.InterfaceCall):
94        if isinstance(b, refpolicy.InterfaceCall):
95            return ifcall_cmp(a, b)
96        else:
97            return id_set_cmp([a.args[0]], b.src_types)
98    else:
99        if isinstance(b, refpolicy.AVRule):
100            return avrule_cmp(a,b)
101        else:
102            return id_set_cmp(a.src_types, [b.args[0]])
103
104def role_type_cmp(a, b):
105    return cmp(a.role, b.role)
106
107def sort_filter(module):
108    """Sort and group the output for readability.
109    """
110    def sort_node(node):
111        c = []
112
113        # Module statement
114        for mod in node.module_declarations():
115            c.append(mod)
116            c.append(refpolicy.Comment())
117
118        # Requires
119        for require in node.requires():
120            c.append(require)
121        c.append(refpolicy.Comment())
122
123        # Rules
124        #
125        # We are going to group output by source type (which
126        # we assume is the first argument for interfaces).
127        rules = []
128        rules.extend(node.avrules())
129        rules.extend(node.interface_calls())
130        rules.sort(rule_cmp)
131
132        cur = None
133        sep_rules = []
134        for rule in rules:
135            if isinstance(rule, refpolicy.InterfaceCall):
136                x = rule.args[0]
137            else:
138                x = util.first(rule.src_types)
139
140            if cur != x:
141                if cur:
142                    sep_rules.append(refpolicy.Comment())
143                cur = x
144                comment = refpolicy.Comment()
145                comment.lines.append("============= %s ==============" % cur)
146                sep_rules.append(comment)
147            sep_rules.append(rule)
148
149        c.extend(sep_rules)
150
151
152        ras = []
153        ras.extend(node.role_types())
154        ras.sort(role_type_cmp)
155        if len(ras):
156            comment = refpolicy.Comment()
157            comment.lines.append("============= ROLES ==============")
158            c.append(comment)
159
160
161        c.extend(ras)
162
163        # Everything else
164        for child in node.children:
165            if child not in c:
166                c.append(child)
167
168        node.children = c
169
170    for node in module.nodes():
171        sort_node(node)
172
173
174