Merge remote-tracking branch 'origin/pr/297'

* origin/pr/297:
  doc: remove useless _static generated by sphinx
  cleanup-dispvms: fix python shebang
  spec: fix missing dependency
  Fix Sphinx 2 new API for Fedora 31+
  doc: Make PEP8 happier
  qmemmand: separate SystemState init xc and xs to a 'init' method
  doc: drop moved elsewhere components
This commit is contained in:
Marek Marczykowski-Górecki 2019-11-27 02:12:37 +01:00
commit fd8e89c546
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724
9 changed files with 90 additions and 149 deletions

View File

@ -166,4 +166,5 @@ autoxml.rst: ../relaxng/qubes.rng example.xml
.PHONY: install
install: man
mkdir -p $(DESTDIR)/usr/share/man/man1
rm -rf $(BUILDDIR)/man/_static
cp $(BUILDDIR)/man/* $(DESTDIR)/usr/share/man/man1/

View File

@ -22,8 +22,6 @@ manpages and API documentation. For primary user documentation, see
qubes-exc
qubes-ext
qubes-log
qubes-mgmt
qubes-policy
qubes-backup
qubes-tools/index
qubes-tests

View File

@ -1,95 +0,0 @@
:py:mod:`qubes.policy` -- Qubes RPC policy
==========================================
Every Qubes domain can trigger various RPC services, but if such call would be
allowed depends on Qubes RPC policy (qrexec policy in short).
Qrexec policy format
--------------------
Policy consists of a file, which is parsed line-by-line. First matching line
is used as an action.
Each line consist of three values separated by white characters (space(s), tab(s)):
1. Source specification, which is one of:
- domain name
- `$anyvm` - any domain
- `$tag:some-tag` - VM having tag `some-tag`
- `$type:vm-type` - VM of `vm-type` type, available types:
AppVM, TemplateVM, StandaloneVM, DispVM
2. Target specification, one of:
- domain name
- `$anyvm` - any domain, excluding dom0
- `$tag:some-tag` - domain having tag `some-tag`
- `$type:vm-type` - domain of `vm-type` type, available types:
AppVM, TemplateVM, StandaloneVM, DispVM
- `$default` - used when caller did not specified any VM
- `$dispvm:vm-name` - _new_ Disposable VM created from AppVM `vm-name`
- `$dispvm:$tag:some-tag` - _new_ Disposable VM created from AppVM tagged with `some-tag`
- `$dispvm` - _new_ Disposable VM created from AppVM pointed by caller
property `default_dispvm`, which defaults to global property `default_dispvm`
- `$adminvm` - Admin VM aka dom0
Dom0 can only be matched explicitly - either as `dom0` or `$adminvm` keyword.
None of `$anyvm`, `$tag:some-tag`, `$type:AdminVM` will match.
3. Action and optional action parameters, one of:
- `allow` - allow the call, without further questions; optional parameters:
- `target=` - override caller provided call target -
possible values are: domain name, `$dispvm` or `$dispvm:vm-name`
- `user=` - call the service using this user, instead of the user
pointed by target VM's `default_user` property
- `deny` - deny the call, without further questions; no optional
parameters are supported
- `ask` - ask the user for confirmation; optional parameters:
- `target=` - override user provided call target
- `user=` - call the service using this user, instead of the user
pointed by target VM's `default_user` property
- `default_target=` - suggest this target when prompting the user for
confirmation
Alternatively, a line may consist of a single keyword `$include:` followed by a
path. This will load a given file as its content would be in place of
`$include` line. Relative paths are resolved relative to
`/etc/qubes-rpc/policy` directory.
Evaluating `ask` action
-----------------------
When qrexec policy specify `ask` action, the user is asked whether the call
should be allowed or denied. In addition to that, user also need to choose
target domain. User have to choose from a set of targets specified by the
policy. Such set is calculated using the algorithm below:
1. If `ask` action have `target=` option specified, only that target is
considered. A prompt window will allow to choose only this value and it will
also be pre-filled value.
2. If no `target=` option is specified, all rules are evaluated to see what
target domains (for a given source domain) would result in `ask` or `allow`
action. If any of them have `target=` option set, that value is used instead of
the one specified in "target" column (for this particular line). Then the user
is presented with a confirmation dialog and an option to choose from those
domains.
3. If `default_target=` option is set, it is used as
suggested value, otherwise no suggestion is made (regardless of calling domain
specified any target or not).
Module contents
---------------
.. automodule:: qubespolicy
:members:
:show-inheritance:
.. vim: ts=3 sw=3 et

View File

@ -2,6 +2,7 @@
import os
import sys
sys.path.insert(0, os.path.abspath('../'))
import argparse
@ -9,12 +10,14 @@ import qubes.dochelpers
parser = argparse.ArgumentParser(description='prepare new manpage for command')
parser.add_argument('command', metavar='COMMAND',
help='program\'s command name; this should translate to '
'qubes.tools.<command_name>')
help='program\'s command name; this should translate to '
'qubes.tools.<command_name>')
def main():
args = parser.parse_args()
sys.stdout.write(qubes.dochelpers.prepare_manpage(args.command))
if __name__ == '__main__':
main()

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/python3
from qubesadmin import Qubes

View File

@ -18,11 +18,11 @@
# License along with this library; if not, see <https://www.gnu.org/licenses/>.
#
'''Documentation helpers.
"""Documentation helpers.
This module contains classes and functions which help to maintain documentation,
particularly our custom Sphinx extension.
'''
"""
import argparse
import io
@ -41,12 +41,17 @@ import sphinx
import sphinx.errors
import sphinx.locale
import sphinx.util.docfields
from sphinx.util import logging
import qubes.tools
SUBCOMMANDS_TITLE = 'COMMANDS'
OPTIONS_TITLE = 'OPTIONS'
try:
log = logging.getLogger(__name__)
except AttributeError:
log = None
class GithubTicket:
# pylint: disable=too-few-public-methods
@ -55,14 +60,15 @@ class GithubTicket:
self.summary = data['title']
self.uri = data['html_url']
def fetch_ticket_info(app, number):
'''Fetch info about particular trac ticket given
"""Fetch info about particular trac ticket given
:param app: Sphinx app object
:param str number: number of the ticket, without #
:rtype: mapping
:raises: urllib.error.HTTPError
'''
"""
response = urllib.request.urlopen(urllib.request.Request(
app.config.ticket_base_uri.format(number=number),
@ -71,8 +77,9 @@ def fetch_ticket_info(app, number):
'User-agent': __name__}))
return GithubTicket(json.load(response))
def ticket(name, rawtext, text, lineno, inliner, options=None, content=None):
'''Link to qubes ticket
"""Link to qubes ticket
:param str name: The role name used in the document
:param str rawtext: The entire markup snippet, with role
@ -82,7 +89,7 @@ def ticket(name, rawtext, text, lineno, inliner, options=None, content=None):
that called this function
:param options: Directive options for customisation
:param content: The directive content for customisation
''' # pylint: disable=unused-argument
""" # pylint: disable=unused-argument
if options is None:
options = {}
@ -117,20 +124,23 @@ class versioncheck(docutils.nodes.warning):
# pylint: disable=invalid-name
pass
def visit(self, node):
self.visit_admonition(node, 'version')
def depart(self, node):
self.depart_admonition(node)
sphinx.locale.admonitionlabels['version'] = 'Version mismatch'
class VersionCheck(docutils.parsers.rst.Directive):
'''Directive versioncheck
"""Directive versioncheck
Check if current version (from ``conf.py``) equals version specified as
argument. If not, generate warning.'''
argument. If not, generate warning."""
has_content = True
required_arguments = 1
@ -145,16 +155,16 @@ class VersionCheck(docutils.parsers.rst.Directive):
if current == version:
return []
text = ' '.join('''This manual page was written for version **{}**, but
text = ' '.join("""This manual page was written for version **{}**, but
current version at the time when this page was generated is **{}**.
This may or may not mean that page is outdated or has
inconsistencies.'''.format(version, current).split())
inconsistencies.""".format(version, current).split())
node = versioncheck(text)
node['classes'] = ['admonition', 'warning']
self.state.nested_parse(docutils.statemachine.StringList([text]),
self.content_offset, node)
self.content_offset, node)
return [node]
@ -168,14 +178,14 @@ def prepare_manpage(command):
stream.write('.. program:: {}\n\n'.format(command))
stream.write(make_rst_section(
':program:`{}` -- {}'.format(command, parser.description), '='))
stream.write('''.. warning::
stream.write(""".. warning::
This page was autogenerated from command-line parser. It shouldn't be 1:1
conversion, because it would add little value. Please revise it and add
more descriptive help, which normally won't fit in standard ``--help``
option.
After rewrite, please remove this admonition.\n\n''')
After rewrite, please remove this admonition.\n\n""")
stream.write(make_rst_section('Synopsis', '-'))
usage = ' '.join(parser.format_usage().strip().split())
@ -189,34 +199,35 @@ def prepare_manpage(command):
stream.write(make_rst_section('Options', '-'))
for action in parser._actions: # pylint: disable=protected-access
for action in parser._actions: # pylint: disable=protected-access
stream.write('.. option:: ')
if action.metavar:
stream.write(', '.join('{}{}{}'.format(
option,
'=' if option.startswith('--') else ' ',
action.metavar)
for option in sorted(action.option_strings)))
option,
'=' if option.startswith('--') else ' ',
action.metavar)
for option in sorted(action.option_strings)))
else:
stream.write(', '.join(sorted(action.option_strings)))
stream.write('\n\n {}\n\n'.format(action.help))
stream.write(make_rst_section('Authors', '-'))
stream.write('''\
stream.write("""\
| Joanna Rutkowska <joanna at invisiblethingslab dot com>
| Rafal Wojtczuk <rafal at invisiblethingslab dot com>
| Marek Marczykowski <marmarek at invisiblethingslab dot com>
| Wojtek Porczyk <woju at invisiblethingslab dot com>
.. vim: ts=3 sw=3 et tw=80
''')
""")
return stream.getvalue()
class OptionsCheckVisitor(docutils.nodes.SparseNodeVisitor):
''' Checks if the visited option nodes and the specified args are in sync.
'''
""" Checks if the visited option nodes and the specified args are in sync.
"""
def __init__(self, command, args, document):
assert isinstance(args, set)
docutils.nodes.SparseNodeVisitor.__init__(self, document)
@ -224,14 +235,13 @@ class OptionsCheckVisitor(docutils.nodes.SparseNodeVisitor):
self.args = args
def visit_desc(self, node):
''' Skips all but 'option' elements '''
""" Skips all but 'option' elements """
# pylint: disable=no-self-use
if not node.get('desctype', None) == 'option':
raise docutils.nodes.SkipChildren
def visit_desc_name(self, node):
''' Checks if the option is defined `self.args` '''
""" Checks if the option is defined `self.args` """
if not isinstance(node[0], docutils.nodes.Text):
raise sphinx.errors.SphinxError('first child should be Text')
@ -243,14 +253,14 @@ class OptionsCheckVisitor(docutils.nodes.SparseNodeVisitor):
'No such argument for {!r}: {!r}'.format(self.command, arg))
def check_undocumented_arguments(self, ignored_options=None):
''' Call this to check if any undocumented arguments are left.
""" Call this to check if any undocumented arguments are left.
While the documentation talks about a
'SparseNodeVisitor.depart_document()' function, this function does
not exists. (For details see implementation of
:py:meth:`NodeVisitor.dispatch_departure()`) So we need to
manually call this.
'''
"""
if ignored_options is None:
ignored_options = set()
left_over_args = self.args - ignored_options
@ -261,9 +271,9 @@ class OptionsCheckVisitor(docutils.nodes.SparseNodeVisitor):
class CommandCheckVisitor(docutils.nodes.SparseNodeVisitor):
''' Checks if the visited sub command section nodes and the specified sub
""" Checks if the visited sub command section nodes and the specified sub
command args are in sync.
'''
"""
def __init__(self, command, sub_commands, document):
docutils.nodes.SparseNodeVisitor.__init__(self, document)
@ -271,12 +281,12 @@ class CommandCheckVisitor(docutils.nodes.SparseNodeVisitor):
self.sub_commands = sub_commands
def visit_section(self, node):
''' Checks if the visited sub-command section nodes exists and it
""" Checks if the visited sub-command section nodes exists and it
options are in sync.
Uses :py:class:`OptionsCheckVisitor` for checking
sub-commands options
'''
"""
# pylint: disable=no-self-use
title = str(node[0][0])
if title.upper() == SUBCOMMANDS_TITLE:
@ -296,10 +306,10 @@ class CommandCheckVisitor(docutils.nodes.SparseNodeVisitor):
'No such sub-command {!r}'.format(sub_cmd))
def visit_Text(self, node):
''' If the visited text node starts with 'alias: ', all the provided
""" If the visited text node starts with 'alias: ', all the provided
comma separted alias in this node, are removed from
`self.sub_commands`
'''
"""
# pylint: disable=invalid-name
text = str(node).strip()
if text.startswith('aliases:'):
@ -308,16 +318,15 @@ class CommandCheckVisitor(docutils.nodes.SparseNodeVisitor):
assert alias in self.sub_commands
del self.sub_commands[alias]
def check_undocumented_sub_commands(self):
''' Call this to check if any undocumented sub_commands are left.
""" Call this to check if any undocumented sub_commands are left.
While the documentation talks about a
'SparseNodeVisitor.depart_document()' function, this function does
not exists. (For details see implementation of
:py:meth:`NodeVisitor.dispatch_departure()`) So we need to
manually call this.
'''
"""
if self.sub_commands:
raise sphinx.errors.SphinxError(
'Undocumented commands for {!r}: {!r}'.format(
@ -325,15 +334,22 @@ class CommandCheckVisitor(docutils.nodes.SparseNodeVisitor):
class ManpageCheckVisitor(docutils.nodes.SparseNodeVisitor):
''' Checks if the sub-commands and options specified in the 'COMMAND' and
""" Checks if the sub-commands and options specified in the 'COMMAND' and
'OPTIONS' (case insensitve) sections in sync the command parser.
'''
"""
def __init__(self, app, command, document):
docutils.nodes.SparseNodeVisitor.__init__(self, document)
try:
parser = qubes.tools.get_parser_for_command(command)
except ImportError:
app.warn('cannot import module for command')
msg = 'cannot import module for command'
if log:
log.warning(msg)
else:
# Handle legacy
app.warn(msg)
self.parser = None
return
except AttributeError:
@ -362,16 +378,16 @@ class ManpageCheckVisitor(docutils.nodes.SparseNodeVisitor):
self.options.update(action.option_strings)
def visit_section(self, node):
''' If section title is OPTIONS or COMMANDS dispatch the apropriate
""" If section title is OPTIONS or COMMANDS dispatch the apropriate
`NodeVisitor`.
'''
"""
if self.parser is None:
return
section_title = str(node[0][0]).upper()
if section_title == OPTIONS_TITLE:
options_visitor = OptionsCheckVisitor(self.command, self.options,
self.document)
self.document)
node.walkabout(options_visitor)
options_visitor.check_undocumented_arguments()
elif section_title == SUBCOMMANDS_TITLE:
@ -380,15 +396,22 @@ class ManpageCheckVisitor(docutils.nodes.SparseNodeVisitor):
node.walkabout(sub_cmd_visitor)
sub_cmd_visitor.check_undocumented_sub_commands()
def check_man_args(app, doctree, docname):
''' Checks the manpage for undocumented or obsolete sub-commands and
""" Checks the manpage for undocumented or obsolete sub-commands and
options.
'''
"""
dirname, command = os.path.split(docname)
if os.path.basename(dirname) != 'manpages':
return
app.info('Checking arguments for {!r}'.format(command))
msg = 'Checking arguments for {!r}'.format(command)
if log:
log.info(msg)
else:
# Handle legacy
app.info(msg)
doctree.walk(ManpageCheckVisitor(app, command, doctree))
@ -398,6 +421,7 @@ def check_man_args(app, doctree, docname):
event_sig_re = re.compile(r'([a-zA-Z-:<>]+)\s*\((.*)\)')
def parse_event(env, sig, signode):
# pylint: disable=unused-argument
m = event_sig_re.match(sig)
@ -413,6 +437,7 @@ def parse_event(env, sig, signode):
signode += plist
return name
#
# end of codelifting
#
@ -433,17 +458,17 @@ def setup(app):
'env')
app.add_config_value('break_to_pdb', False, 'env')
app.add_node(versioncheck,
html=(visit, depart),
man=(visit, depart))
html=(visit, depart),
man=(visit, depart))
app.add_directive('versioncheck', VersionCheck)
fdesc = sphinx.util.docfields.GroupedField('parameter', label='Parameters',
names=['param'], can_collapse=True)
names=['param'],
can_collapse=True)
app.add_object_type('event', 'event', 'pair: %s; event', parse_event,
doc_field_types=[fdesc])
app.connect('doctree-resolved', break_to_pdb)
app.connect('doctree-resolved', check_man_args)
# vim: ts=4 sw=4 et

View File

@ -57,6 +57,10 @@ class SystemState(object):
self.log.debug('SystemState()')
self.domdict = {}
self.xc = None
self.xs = None
def init(self):
self.xc = xen.lowlevel.xc.xc()
self.xs = xen.lowlevel.xs.xs()
self.BALOON_DELAY = 0.1

View File

@ -276,6 +276,10 @@ def main():
log.debug('instantiating server')
os.umask(0)
# Initialize the connection to Xen and to XenStore
system_state.init()
server = socketserver.UnixStreamServer(SOCK_PATH, QMemmanReqHandler)
os.umask(0o077)

View File

@ -57,6 +57,7 @@ BuildRequires: python3-lxml
BuildRequires: libvirt-python3
BuildRequires: python3-dbus
BuildRequires: python3-PyYAML
BuildRequires: python3-xen
Requires(post): systemd-units
Requires(preun): systemd-units