Compare commits

...

No commits in common. "master" and "gh-pages" have entirely different histories.

69 changed files with 14691 additions and 6709 deletions

0
.nojekyll Normal file
View file

View file

@ -1,14 +0,0 @@
language: c
sudo: true
install:
- sudo apt-get update
- sudo apt-get install pkg-config libdbus-1-dev libcap-ng-dev
before_script:
- ./bootstrap.py
- ./waf configure
script:
- ./waf build

23
COPYING
View file

@ -1,23 +0,0 @@
Copyright (c) 2013, Alessandro Ghedini <alessandro@ghedini.me>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -1,308 +0,0 @@
pflask
======
.. image:: https://travis-ci.org/ghedo/pflask.png
:target: https://travis-ci.org/ghedo/pflask
pflask_ is a simple tool for creating process containers on LInux. It can be
used for running single commands or even booting a whole operating system
inside an isolated environment, where the filesystem hierarchy, networking,
process tree, IPC subsystems and host/domain name can be insulated from the
host system and other containers.
.. _pflask: https://ghedo.github.io/pflask
Getting Started
---------------
pflask doesn't need any configuration and can be run without any arguments
as follows:
.. code-block:: bash
$ sudo pflask
By default a new container will be created and a bash shell will be started,
but a custom command can also be specified:
.. code-block:: bash
$ sudo pflask -- id
uid=0(root) gid=0(root) gruppi=0(root)
The container can also be run inside a private root directory by using the
``--chroot`` option:
.. code-block:: bash
$ sudo pflask --chroot=/path/to/rootfs -- id
uid=0(root) gid=0(root) gruppi=0(root)
This can be used, for example, as a replacement for the ``chroot(8)`` command.
It's even possible to invoke the init binary and boot the whole operating
system inside the container:
.. code-block:: bash
$ sudo pflask --chroot=/path/to/rootfs -- /sbin/init
Note that pflask doesn't provide any support for creating the rootfs, but can
piggyback on existing tools. For example the ``debootstrap(8)`` command can be
used for creating a Debian rootfs as follows:
.. code-block:: bash
$ sudo debootstrap sid /path/to/rootfs http://httpredir.debian.org/debian
For more information on pflask usage, have a look at the `man page`_.
.. _`man page`: https://ghedo.github.io/pflask/pflask.html
Networking
~~~~~~~~~~
Using the ``--netif`` option the networking of the container will be
disconnected from the host system and all network interfaces will be made
unavailable to the container:
.. code-block:: bash
$ sudo pflask --netif -- ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
The ``--netif`` option can also be used to create private network interfaces:
.. code-block:: bash
$ sudo pflask --netif=macvlan:eth0:net0 -- ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
5: net0@if2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default
link/ether 92:e4:c2:9b:a4:75 brd ff:ff:ff:ff:ff:ff link-netnsid 0
Interfaces created inside the container will be automatically destroyed once
the container terminates.
The command above will create a new ``macvlan`` interface called ``net0``, from
the ``eth0`` host interface. ``macvlan`` interfaces can be used to give an
additional MAC address to a network adapter and make it look like a completely
different device.
pflask can also create other `types of network interfaces`_, have a look at the
manpage for more information.
.. _`types of network interfaces`: https://ghedo.github.io/pflask/pflask.html#netif
Filesystem
~~~~~~~~~~
By default a new mount namespace is created for the container, so that
filesystems mounted inside it won't affect the host system. The ``--mount``
option can then be used to create new mount points before the execution of the
supplied command.
.. code-block:: bash
$ sudo pflask --chroot=/path/to/rootfs --mount=bind:/tmp:/tmp
The command above will bind mount the host's ``/tmp`` directory into the
container's ``/tmp``, so that files can be exchanged between them.
pflask can also create other `types of mount points`_, have a look at the
manpage for more information.
.. _`types of mount points`: https://ghedo.github.io/pflask/pflask.html#mount
Additionally, using the ``--ephemeral`` option it's possible to tell pflask to
discard any change applied to the root filesystem once the container terminates:
.. code-block:: bash
$ sudo pflask --chroot=/path/to/rootfs --ephemeral -- /sbin/init
This can be used for example for a build environment, where dependencies can
be installed at every run on a clean rootfs, without the need to recreate the
rootfs every time.
Unprivileged containers
~~~~~~~~~~~~~~~~~~~~~~~
All the commands above have been executed with root privileges, but pflask can
be invoked, with some limitations, by unprivileged users as well, as long as
user namespaces are supported by the host system.
.. code-block:: bash
$ pflask --user=$USER -- id
uid=1000(ghedo) gid=1000(ghedo) gruppi=1000(ghedo)
For example, on recent Debian versions user namespaces are enabled, but are
restricted to the root user only. To enable them for unprivileged users run:
.. code-block:: bash
$ sudo sysctl kernel.unprivileged_userns_clone=1
This functionality can be used to run every-day user applications such as a
web browser inside a container:
.. code-block:: bash
$ pflask --user=$USER --mount=tmp:$HOME -- chromium --disable-setuid-sandbox
The command above uses the ``--mount`` option to create a ``tmpfs`` mount point
on the ``$HOME`` directory, so that the application (chromium in the example)
won't be able to access the user's private files, and any modification to the
home directory will be discarded once the container terminates.
The ``--chroot`` option can be used with unprivileged containers as well, but
requires some additional configuration.
The first step is assigning a set of additional UIDs and GIDs to the current
user (``$USER``). These will be used by pflask inside the container:
.. code-block:: bash
$ sudo usermod --add-subuids 100000-165535 $USER
$ sudo usermod --add-subgids 100000-165535 $USER
Note that the commands above require root privileges, but have to be run only
once.
Then any time an unprivileged ``chroot(8)`` is needed, the following command
can be run:
.. code-block:: bash
$ pflask --user-map=0:100000:65536 --chroot=/path/to/rootfs
Note that the ``newuidmap(1)`` and ``newgidmap(1)`` commands need to be
installed for any of this to work: on Debian/Ubuntu systems they are provided
by the ``uidmap`` package.
Background containers
~~~~~~~~~~~~~~~~~~~~~
Containers can be detached from the current terminal as soon as they are
created by using the ``--detach`` option:
.. code-block:: bash
$ sudo pflask --chroot=/path/to/rootfs --detach
and then later reattached (even to a different terminal) with the ``--attach``
option:
.. code-block:: bash
$ pidof pflask
29076
$ pflask --attach=29076
Where ``29076`` is the PID of the detached pflask process. Once reattached, it
can be detached again by pressing ``^@`` (Ctrl + @).
machined integration
~~~~~~~~~~~~~~~~~~~~
Containers created with pflask are automatically registered with the machined_
daemon, if installed and running. The ``machinectl(1)`` command can then be
used to list and manipulate running containers.
Let's create one container as follows:
.. code-block:: bash
$ sudo pflask --chroot=/path/to/rootfs -- /sbin/init
Running containers can be listed using the ``list`` command:
.. code-block:: bash
$ machinectl --no-pager list
MACHINE CLASS SERVICE
pflask-19170 container pflask
1 machines listed.
and information regarding a single container can be retrieved with the ``show``
command:
.. code-block:: bash
$ machinectl --no-pager show pflask-19170
Name=pflask-19170
Id=00000000000000000000000000000000
Timestamp=gio 2015-06-25 20:28:34 CEST
TimestampMonotonic=8860409172
Service=pflask
Unit=machine-pflask\x5cx2d19170.scope
Leader=19170
Class=container
RootDirectory=/home/ghedo/local/debian
State=running
Additionally, the ``status`` command will show more information regarding the
status of the container:
.. code-block:: bash
$ machinectl --no-pager status pflask-19170
pflask-19170
Since: gio 2015-06-25 20:28:34 CEST; 1min 21s ago
Leader: 19170 (systemd)
Service: pflask; class container
Root: /home/ghedo/local/debian
OS: Debian GNU/Linux stretch/sid
Unit: machine-pflask\x2d19170.scope
├─19170 /lib/systemd/systemd
└─system.slice
├─systemd-journald.service
│ └─19184 /lib/systemd/systemd-journald
└─console-getty.service
└─19216 /sbin/agetty --noclear --keep-baud console 115200 3...
giu 25 20:28:34 kronk systemd[1]: Started Container pflask-19170.
giu 25 20:28:34 kronk systemd[1]: Starting Container pflask-19170.
One can even log into the container using the ``login`` command (note that
the dbus daemon needs to be running inside the container for this to work):
.. code-block:: bash
$ sudo machinectl login pflask-19170
Connected to machine pflask-19170. Press ^] three times within 1s to exit session.
Debian GNU/Linux stretch/sid kronk pts/0
kronk login:
And finally the container can be terminated using either the ``poweroff`` or
``terminate`` commands:
.. code-block:: bash
$ sudo machinectl poweroff pflask-19170
.. _machined: http://www.freedesktop.org/wiki/Software/systemd/machined/
Building
--------
pflask is distributed as source code. Build with:
.. code-block:: bash
$ ./bootstrap.py
$ ./waf configure
$ ./waf build
Copyright
---------
Copyright (C) 2013 Alessandro Ghedini <alessandro@ghedini.me>
See COPYING_ for the license.
.. _COPYING: https://github.com/ghedo/pflask/tree/master/COPYING

View file

@ -22,11 +22,6 @@ host system and other containers.
OPTIONS
-------
.. option:: -b, --caps=<+cap1>,<cap2>,<-cap3>
Change the effective capabilities inside the container. See CAPABILITIES_
for more information.
.. option:: -r, --chroot=<dir>
Change the root directory inside the container.
@ -51,22 +46,16 @@ OPTIONS
.. option:: -u, --user=<user>
Run the command under the specified user. This also automatically creates
a user namespace and maps the user *user* to itself, unless ``--user-map``
is also used.
This is not always the correct behaviour (e.g. when using ``--user`` with
``--chroot`` without ``--user-map``), so the ``--no-userns`` option can be
used to disable the user namespace.
Run the command under the specified user.
.. option:: -e, --user-map=<map>
Create a user namespace and map container users to host users. The *map*
argument is composed of three values separated by ``:``: the first userid
as seen in the user namespace of the container, the first userid as seen
on the host, and a range indicating the number of consecutive ids to map.
Map container users to host users. The *map* argument is composed of three
values separated by ``:``: the first userid as seen in the user namespace of
the container, the first userid as seen on the host, and a range indicating
the number of consecutive ids to map.
Example: ``--user-map=0:100000:65536``
Example: ``--user-map=0:100000,65536``
.. option:: -w, --ephemeral
@ -221,26 +210,6 @@ No additional configuration will be applied to them.
Example: ``--netif=veth:veth0:eth0``
CAPABILITIES
------------
pflask can modify the set of capabilties enabled for the container using the
``--caps`` option.
This takes a list of comma-separated capability names, optionally prefixed by
``+`` (to add the capability to the set) or ``-`` (to drop the capability from
the set).
Valid capability names are the same name as defined in <linux/capabilities.h>
with the ``CAP_`` prefix removed. The string case does not matter. The first
specified capability name can be the alias 'all' to specify either a full or
an empty initial set. Container processes will start by default with a full
set e.g. ``--caps=all``.
Full set example: ``--caps=all,-chown,-setuid,-setgid`` or equivalently ``--caps=-chown,-setuid,-setgid``
Empty set example: ``--caps=-all,+chown``
AUTHOR
------

BIN
_static/ajax-loader.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

537
_static/basic.css Normal file
View file

@ -0,0 +1,537 @@
/*
* basic.css
* ~~~~~~~~~
*
* Sphinx stylesheet -- basic theme.
*
* :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
/* -- main layout ----------------------------------------------------------- */
div.clearer {
clear: both;
}
/* -- relbar ---------------------------------------------------------------- */
div.related {
width: 100%;
font-size: 90%;
}
div.related h3 {
display: none;
}
div.related ul {
margin: 0;
padding: 0 0 0 10px;
list-style: none;
}
div.related li {
display: inline;
}
div.related li.right {
float: right;
margin-right: 5px;
}
/* -- sidebar --------------------------------------------------------------- */
div.sphinxsidebarwrapper {
padding: 10px 5px 0 10px;
}
div.sphinxsidebar {
float: left;
width: 230px;
margin-left: -100%;
font-size: 90%;
}
div.sphinxsidebar ul {
list-style: none;
}
div.sphinxsidebar ul ul,
div.sphinxsidebar ul.want-points {
margin-left: 20px;
list-style: square;
}
div.sphinxsidebar ul ul {
margin-top: 0;
margin-bottom: 0;
}
div.sphinxsidebar form {
margin-top: 10px;
}
div.sphinxsidebar input {
border: 1px solid #98dbcc;
font-family: sans-serif;
font-size: 1em;
}
div.sphinxsidebar #searchbox input[type="text"] {
width: 170px;
}
div.sphinxsidebar #searchbox input[type="submit"] {
width: 30px;
}
img {
border: 0;
max-width: 100%;
}
/* -- search page ----------------------------------------------------------- */
ul.search {
margin: 10px 0 0 20px;
padding: 0;
}
ul.search li {
padding: 5px 0 5px 20px;
background-image: url(file.png);
background-repeat: no-repeat;
background-position: 0 7px;
}
ul.search li a {
font-weight: bold;
}
ul.search li div.context {
color: #888;
margin: 2px 0 0 30px;
text-align: left;
}
ul.keywordmatches li.goodmatch a {
font-weight: bold;
}
/* -- index page ------------------------------------------------------------ */
table.contentstable {
width: 90%;
}
table.contentstable p.biglink {
line-height: 150%;
}
a.biglink {
font-size: 1.3em;
}
span.linkdescr {
font-style: italic;
padding-top: 5px;
font-size: 90%;
}
/* -- general index --------------------------------------------------------- */
table.indextable {
width: 100%;
}
table.indextable td {
text-align: left;
vertical-align: top;
}
table.indextable dl, table.indextable dd {
margin-top: 0;
margin-bottom: 0;
}
table.indextable tr.pcap {
height: 10px;
}
table.indextable tr.cap {
margin-top: 10px;
background-color: #f2f2f2;
}
img.toggler {
margin-right: 3px;
margin-top: 3px;
cursor: pointer;
}
div.modindex-jumpbox {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
margin: 1em 0 1em 0;
padding: 0.4em;
}
div.genindex-jumpbox {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
margin: 1em 0 1em 0;
padding: 0.4em;
}
/* -- general body styles --------------------------------------------------- */
a.headerlink {
visibility: hidden;
}
h1:hover > a.headerlink,
h2:hover > a.headerlink,
h3:hover > a.headerlink,
h4:hover > a.headerlink,
h5:hover > a.headerlink,
h6:hover > a.headerlink,
dt:hover > a.headerlink {
visibility: visible;
}
div.body p.caption {
text-align: inherit;
}
div.body td {
text-align: left;
}
.field-list ul {
padding-left: 1em;
}
.first {
margin-top: 0 !important;
}
p.rubric {
margin-top: 30px;
font-weight: bold;
}
img.align-left, .figure.align-left, object.align-left {
clear: left;
float: left;
margin-right: 1em;
}
img.align-right, .figure.align-right, object.align-right {
clear: right;
float: right;
margin-left: 1em;
}
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left;
}
.align-center {
text-align: center;
}
.align-right {
text-align: right;
}
/* -- sidebars -------------------------------------------------------------- */
div.sidebar {
margin: 0 0 0.5em 1em;
border: 1px solid #ddb;
padding: 7px 7px 0 7px;
background-color: #ffe;
width: 40%;
float: right;
}
p.sidebar-title {
font-weight: bold;
}
/* -- topics ---------------------------------------------------------------- */
div.topic {
border: 1px solid #ccc;
padding: 7px 7px 0 7px;
margin: 10px 0 10px 0;
}
p.topic-title {
font-size: 1.1em;
font-weight: bold;
margin-top: 10px;
}
/* -- admonitions ----------------------------------------------------------- */
div.admonition {
margin-top: 10px;
margin-bottom: 10px;
padding: 7px;
}
div.admonition dt {
font-weight: bold;
}
div.admonition dl {
margin-bottom: 0;
}
p.admonition-title {
margin: 0px 10px 5px 0px;
font-weight: bold;
}
div.body p.centered {
text-align: center;
margin-top: 25px;
}
/* -- tables ---------------------------------------------------------------- */
table.docutils {
border: 0;
border-collapse: collapse;
}
table.docutils td, table.docutils th {
padding: 1px 8px 1px 5px;
border-top: 0;
border-left: 0;
border-right: 0;
border-bottom: 1px solid #aaa;
}
table.field-list td, table.field-list th {
border: 0 !important;
}
table.footnote td, table.footnote th {
border: 0 !important;
}
th {
text-align: left;
padding-right: 5px;
}
table.citation {
border-left: solid 1px gray;
margin-left: 1px;
}
table.citation td {
border-bottom: none;
}
/* -- other body styles ----------------------------------------------------- */
ol.arabic {
list-style: decimal;
}
ol.loweralpha {
list-style: lower-alpha;
}
ol.upperalpha {
list-style: upper-alpha;
}
ol.lowerroman {
list-style: lower-roman;
}
ol.upperroman {
list-style: upper-roman;
}
dl {
margin-bottom: 15px;
}
dd p {
margin-top: 0px;
}
dd ul, dd table {
margin-bottom: 10px;
}
dd {
margin-top: 3px;
margin-bottom: 10px;
margin-left: 30px;
}
dt:target, .highlighted {
background-color: #fbe54e;
}
dl.glossary dt {
font-weight: bold;
font-size: 1.1em;
}
.field-list ul {
margin: 0;
padding-left: 1em;
}
.field-list p {
margin: 0;
}
.optional {
font-size: 1.3em;
}
.versionmodified {
font-style: italic;
}
.system-message {
background-color: #fda;
padding: 5px;
border: 3px solid red;
}
.footnote:target {
background-color: #ffa;
}
.line-block {
display: block;
margin-top: 1em;
margin-bottom: 1em;
}
.line-block .line-block {
margin-top: 0;
margin-bottom: 0;
margin-left: 1.5em;
}
.guilabel, .menuselection {
font-family: sans-serif;
}
.accelerator {
text-decoration: underline;
}
.classifier {
font-style: oblique;
}
abbr, acronym {
border-bottom: dotted 1px;
cursor: help;
}
/* -- code displays --------------------------------------------------------- */
pre {
overflow: auto;
overflow-y: hidden; /* fixes display issues on Chrome browsers */
}
td.linenos pre {
padding: 5px 0px;
border: 0;
background-color: transparent;
color: #aaa;
}
table.highlighttable {
margin-left: 0.5em;
}
table.highlighttable td {
padding: 0 0.5em 0 0.5em;
}
tt.descname {
background-color: transparent;
font-weight: bold;
font-size: 1.2em;
}
tt.descclassname {
background-color: transparent;
}
tt.xref, a tt {
background-color: transparent;
font-weight: bold;
}
h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
background-color: transparent;
}
.viewcode-link {
float: right;
}
.viewcode-back {
float: right;
font-family: sans-serif;
}
div.viewcode-block:target {
margin: -1px -10px;
padding: 0 10px;
}
/* -- math display ---------------------------------------------------------- */
img.math {
vertical-align: middle;
}
div.body div.math p {
text-align: center;
}
span.eqno {
float: right;
}
/* -- printout stylesheet --------------------------------------------------- */
@media print {
div.document,
div.documentwrapper,
div.bodywrapper {
margin: 0 !important;
width: 100%;
}
div.sphinxsidebar,
div.related,
div.footer,
#top-link {
display: none;
}
}

BIN
_static/comment-bright.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
_static/comment-close.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
_static/comment.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

238
_static/doctools.js Normal file
View file

@ -0,0 +1,238 @@
/*
* doctools.js
* ~~~~~~~~~~~
*
* Sphinx JavaScript utilities for all documentation.
*
* :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
/**
* select a different prefix for underscore
*/
$u = _.noConflict();
/**
* make the code below compatible with browsers without
* an installed firebug like debugger
if (!window.console || !console.firebug) {
var names = ["log", "debug", "info", "warn", "error", "assert", "dir",
"dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace",
"profile", "profileEnd"];
window.console = {};
for (var i = 0; i < names.length; ++i)
window.console[names[i]] = function() {};
}
*/
/**
* small helper function to urldecode strings
*/
jQuery.urldecode = function(x) {
return decodeURIComponent(x).replace(/\+/g, ' ');
};
/**
* small helper function to urlencode strings
*/
jQuery.urlencode = encodeURIComponent;
/**
* This function returns the parsed url parameters of the
* current request. Multiple values per key are supported,
* it will always return arrays of strings for the value parts.
*/
jQuery.getQueryParameters = function(s) {
if (typeof s == 'undefined')
s = document.location.search;
var parts = s.substr(s.indexOf('?') + 1).split('&');
var result = {};
for (var i = 0; i < parts.length; i++) {
var tmp = parts[i].split('=', 2);
var key = jQuery.urldecode(tmp[0]);
var value = jQuery.urldecode(tmp[1]);
if (key in result)
result[key].push(value);
else
result[key] = [value];
}
return result;
};
/**
* highlight a given string on a jquery object by wrapping it in
* span elements with the given class name.
*/
jQuery.fn.highlightText = function(text, className) {
function highlight(node) {
if (node.nodeType == 3) {
var val = node.nodeValue;
var pos = val.toLowerCase().indexOf(text);
if (pos >= 0 && !jQuery(node.parentNode).hasClass(className)) {
var span = document.createElement("span");
span.className = className;
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
node.parentNode.insertBefore(span, node.parentNode.insertBefore(
document.createTextNode(val.substr(pos + text.length)),
node.nextSibling));
node.nodeValue = val.substr(0, pos);
}
}
else if (!jQuery(node).is("button, select, textarea")) {
jQuery.each(node.childNodes, function() {
highlight(this);
});
}
}
return this.each(function() {
highlight(this);
});
};
/**
* Small JavaScript module for the documentation.
*/
var Documentation = {
init : function() {
this.fixFirefoxAnchorBug();
this.highlightSearchWords();
this.initIndexTable();
},
/**
* i18n support
*/
TRANSLATIONS : {},
PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; },
LOCALE : 'unknown',
// gettext and ngettext don't access this so that the functions
// can safely bound to a different name (_ = Documentation.gettext)
gettext : function(string) {
var translated = Documentation.TRANSLATIONS[string];
if (typeof translated == 'undefined')
return string;
return (typeof translated == 'string') ? translated : translated[0];
},
ngettext : function(singular, plural, n) {
var translated = Documentation.TRANSLATIONS[singular];
if (typeof translated == 'undefined')
return (n == 1) ? singular : plural;
return translated[Documentation.PLURALEXPR(n)];
},
addTranslations : function(catalog) {
for (var key in catalog.messages)
this.TRANSLATIONS[key] = catalog.messages[key];
this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')');
this.LOCALE = catalog.locale;
},
/**
* add context elements like header anchor links
*/
addContextElements : function() {
$('div[id] > :header:first').each(function() {
$('<a class="headerlink">\u00B6</a>').
attr('href', '#' + this.id).
attr('title', _('Permalink to this headline')).
appendTo(this);
});
$('dt[id]').each(function() {
$('<a class="headerlink">\u00B6</a>').
attr('href', '#' + this.id).
attr('title', _('Permalink to this definition')).
appendTo(this);
});
},
/**
* workaround a firefox stupidity
*/
fixFirefoxAnchorBug : function() {
if (document.location.hash && $.browser.mozilla)
window.setTimeout(function() {
document.location.href += '';
}, 10);
},
/**
* highlight the search words provided in the url in the text
*/
highlightSearchWords : function() {
var params = $.getQueryParameters();
var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : [];
if (terms.length) {
var body = $('div.body');
if (!body.length) {
body = $('body');
}
window.setTimeout(function() {
$.each(terms, function() {
body.highlightText(this.toLowerCase(), 'highlighted');
});
}, 10);
$('<p class="highlight-link"><a href="javascript:Documentation.' +
'hideSearchWords()">' + _('Hide Search Matches') + '</a></p>')
.appendTo($('#searchbox'));
}
},
/**
* init the domain index toggle buttons
*/
initIndexTable : function() {
var togglers = $('img.toggler').click(function() {
var src = $(this).attr('src');
var idnum = $(this).attr('id').substr(7);
$('tr.cg-' + idnum).toggle();
if (src.substr(-9) == 'minus.png')
$(this).attr('src', src.substr(0, src.length-9) + 'plus.png');
else
$(this).attr('src', src.substr(0, src.length-8) + 'minus.png');
}).css('display', '');
if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) {
togglers.click();
}
},
/**
* helper function to hide the search marks again
*/
hideSearchWords : function() {
$('#searchbox .highlight-link').fadeOut(300);
$('span.highlighted').removeClass('highlighted');
},
/**
* make the url absolute
*/
makeURL : function(relativeURL) {
return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL;
},
/**
* get the current relative url
*/
getCurrentURL : function() {
var path = document.location.pathname;
var parts = path.split(/\//);
$.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() {
if (this == '..')
parts.pop();
});
var url = parts.join('/');
return path.substring(url.lastIndexOf('/') + 1, path.length - 1);
}
};
// quick alias for translations
_ = Documentation.gettext;
$(document).ready(function() {
Documentation.init();
});

BIN
_static/down-pressed.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 B

BIN
_static/down.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 363 B

BIN
_static/file.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 392 B

9404
_static/jquery.js vendored Normal file

File diff suppressed because it is too large Load diff

BIN
_static/minus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

245
_static/nature.css Normal file
View file

@ -0,0 +1,245 @@
/*
* nature.css_t
* ~~~~~~~~~~~~
*
* Sphinx stylesheet -- nature theme.
*
* :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@import url("basic.css");
/* -- page layout ----------------------------------------------------------- */
body {
font-family: Arial, sans-serif;
font-size: 100%;
background-color: #111;
color: #555;
margin: 0;
padding: 0;
}
div.documentwrapper {
float: left;
width: 100%;
}
div.bodywrapper {
margin: 0 0 0 230px;
}
hr {
border: 1px solid #B1B4B6;
}
div.document {
background-color: #eee;
}
div.body {
background-color: #ffffff;
color: #3E4349;
padding: 0 30px 30px 30px;
font-size: 0.9em;
}
div.footer {
color: #555;
width: 100%;
padding: 13px 0;
text-align: center;
font-size: 75%;
}
div.footer a {
color: #444;
text-decoration: underline;
}
div.related {
background-color: #6BA81E;
line-height: 32px;
color: #fff;
text-shadow: 0px 1px 0 #444;
font-size: 0.9em;
}
div.related a {
color: #E2F3CC;
}
div.sphinxsidebar {
font-size: 0.75em;
line-height: 1.5em;
}
div.sphinxsidebarwrapper{
padding: 20px 0;
}
div.sphinxsidebar h3,
div.sphinxsidebar h4 {
font-family: Arial, sans-serif;
color: #222;
font-size: 1.2em;
font-weight: normal;
margin: 0;
padding: 5px 10px;
background-color: #ddd;
text-shadow: 1px 1px 0 white
}
div.sphinxsidebar h4{
font-size: 1.1em;
}
div.sphinxsidebar h3 a {
color: #444;
}
div.sphinxsidebar p {
color: #888;
padding: 5px 20px;
}
div.sphinxsidebar p.topless {
}
div.sphinxsidebar ul {
margin: 10px 20px;
padding: 0;
color: #000;
}
div.sphinxsidebar a {
color: #444;
}
div.sphinxsidebar input {
border: 1px solid #ccc;
font-family: sans-serif;
font-size: 1em;
}
div.sphinxsidebar input[type=text]{
margin-left: 20px;
}
/* -- body styles ----------------------------------------------------------- */
a {
color: #005B81;
text-decoration: none;
}
a:hover {
color: #E32E00;
text-decoration: underline;
}
div.body h1,
div.body h2,
div.body h3,
div.body h4,
div.body h5,
div.body h6 {
font-family: Arial, sans-serif;
background-color: #BED4EB;
font-weight: normal;
color: #212224;
margin: 30px 0px 10px 0px;
padding: 5px 0 5px 10px;
text-shadow: 0px 1px 0 white
}
div.body h1 { border-top: 20px solid white; margin-top: 0; font-size: 200%; }
div.body h2 { font-size: 150%; background-color: #C8D5E3; }
div.body h3 { font-size: 120%; background-color: #D8DEE3; }
div.body h4 { font-size: 110%; background-color: #D8DEE3; }
div.body h5 { font-size: 100%; background-color: #D8DEE3; }
div.body h6 { font-size: 100%; background-color: #D8DEE3; }
a.headerlink {
color: #c60f0f;
font-size: 0.8em;
padding: 0 4px 0 4px;
text-decoration: none;
}
a.headerlink:hover {
background-color: #c60f0f;
color: white;
}
div.body p, div.body dd, div.body li {
line-height: 1.5em;
}
div.admonition p.admonition-title + p {
display: inline;
}
div.highlight{
background-color: white;
}
div.note {
background-color: #eee;
border: 1px solid #ccc;
}
div.seealso {
background-color: #ffc;
border: 1px solid #ff6;
}
div.topic {
background-color: #eee;
}
div.warning {
background-color: #ffe4e4;
border: 1px solid #f66;
}
p.admonition-title {
display: inline;
}
p.admonition-title:after {
content: ":";
}
pre {
padding: 10px;
background-color: White;
color: #222;
line-height: 1.2em;
border: 1px solid #C6C9CB;
font-size: 1.1em;
margin: 1.5em 0 1.5em 0;
-webkit-box-shadow: 1px 1px 1px #d8d8d8;
-moz-box-shadow: 1px 1px 1px #d8d8d8;
}
tt {
background-color: #ecf0f3;
color: #222;
/* padding: 1px 2px; */
font-size: 1.1em;
font-family: monospace;
}
.viewcode-back {
font-family: Arial, sans-serif;
}
div.viewcode-block:target {
background-color: #f4debf;
border-top: 1px solid #ac9;
border-bottom: 1px solid #ac9;
}

BIN
_static/plus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

63
_static/pygments.css Normal file
View file

@ -0,0 +1,63 @@
.highlight .hll { background-color: #ffffcc }
.highlight { background: #eeffcc; }
.highlight .c { color: #408090; font-style: italic } /* Comment */
.highlight .err { border: 1px solid #FF0000 } /* Error */
.highlight .k { color: #007020; font-weight: bold } /* Keyword */
.highlight .o { color: #666666 } /* Operator */
.highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */
.highlight .cp { color: #007020 } /* Comment.Preproc */
.highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */
.highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #A00000 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #FF0000 } /* Generic.Error */
.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
.highlight .gi { color: #00A000 } /* Generic.Inserted */
.highlight .go { color: #333333 } /* Generic.Output */
.highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
.highlight .gt { color: #0044DD } /* Generic.Traceback */
.highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #007020 } /* Keyword.Pseudo */
.highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #902000 } /* Keyword.Type */
.highlight .m { color: #208050 } /* Literal.Number */
.highlight .s { color: #4070a0 } /* Literal.String */
.highlight .na { color: #4070a0 } /* Name.Attribute */
.highlight .nb { color: #007020 } /* Name.Builtin */
.highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */
.highlight .no { color: #60add5 } /* Name.Constant */
.highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */
.highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */
.highlight .ne { color: #007020 } /* Name.Exception */
.highlight .nf { color: #06287e } /* Name.Function */
.highlight .nl { color: #002070; font-weight: bold } /* Name.Label */
.highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */
.highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #bb60d5 } /* Name.Variable */
.highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #208050 } /* Literal.Number.Bin */
.highlight .mf { color: #208050 } /* Literal.Number.Float */
.highlight .mh { color: #208050 } /* Literal.Number.Hex */
.highlight .mi { color: #208050 } /* Literal.Number.Integer */
.highlight .mo { color: #208050 } /* Literal.Number.Oct */
.highlight .sb { color: #4070a0 } /* Literal.String.Backtick */
.highlight .sc { color: #4070a0 } /* Literal.String.Char */
.highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */
.highlight .s2 { color: #4070a0 } /* Literal.String.Double */
.highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */
.highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */
.highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */
.highlight .sx { color: #c65d09 } /* Literal.String.Other */
.highlight .sr { color: #235388 } /* Literal.String.Regex */
.highlight .s1 { color: #4070a0 } /* Literal.String.Single */
.highlight .ss { color: #517918 } /* Literal.String.Symbol */
.highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */
.highlight .vc { color: #bb60d5 } /* Name.Variable.Class */
.highlight .vg { color: #bb60d5 } /* Name.Variable.Global */
.highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */
.highlight .il { color: #208050 } /* Literal.Number.Integer.Long */

622
_static/searchtools.js Normal file
View file

@ -0,0 +1,622 @@
/*
* searchtools.js_t
* ~~~~~~~~~~~~~~~~
*
* Sphinx JavaScript utilties for the full-text search.
*
* :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
/**
* Porter Stemmer
*/
var Stemmer = function() {
var step2list = {
ational: 'ate',
tional: 'tion',
enci: 'ence',
anci: 'ance',
izer: 'ize',
bli: 'ble',
alli: 'al',
entli: 'ent',
eli: 'e',
ousli: 'ous',
ization: 'ize',
ation: 'ate',
ator: 'ate',
alism: 'al',
iveness: 'ive',
fulness: 'ful',
ousness: 'ous',
aliti: 'al',
iviti: 'ive',
biliti: 'ble',
logi: 'log'
};
var step3list = {
icate: 'ic',
ative: '',
alize: 'al',
iciti: 'ic',
ical: 'ic',
ful: '',
ness: ''
};
var c = "[^aeiou]"; // consonant
var v = "[aeiouy]"; // vowel
var C = c + "[^aeiouy]*"; // consonant sequence
var V = v + "[aeiou]*"; // vowel sequence
var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0
var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1
var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1
var s_v = "^(" + C + ")?" + v; // vowel in stem
this.stemWord = function (w) {
var stem;
var suffix;
var firstch;
var origword = w;
if (w.length < 3)
return w;
var re;
var re2;
var re3;
var re4;
firstch = w.substr(0,1);
if (firstch == "y")
w = firstch.toUpperCase() + w.substr(1);
// Step 1a
re = /^(.+?)(ss|i)es$/;
re2 = /^(.+?)([^s])s$/;
if (re.test(w))
w = w.replace(re,"$1$2");
else if (re2.test(w))
w = w.replace(re2,"$1$2");
// Step 1b
re = /^(.+?)eed$/;
re2 = /^(.+?)(ed|ing)$/;
if (re.test(w)) {
var fp = re.exec(w);
re = new RegExp(mgr0);
if (re.test(fp[1])) {
re = /.$/;
w = w.replace(re,"");
}
}
else if (re2.test(w)) {
var fp = re2.exec(w);
stem = fp[1];
re2 = new RegExp(s_v);
if (re2.test(stem)) {
w = stem;
re2 = /(at|bl|iz)$/;
re3 = new RegExp("([^aeiouylsz])\\1$");
re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
if (re2.test(w))
w = w + "e";
else if (re3.test(w)) {
re = /.$/;
w = w.replace(re,"");
}
else if (re4.test(w))
w = w + "e";
}
}
// Step 1c
re = /^(.+?)y$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(s_v);
if (re.test(stem))
w = stem + "i";
}
// Step 2
re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
suffix = fp[2];
re = new RegExp(mgr0);
if (re.test(stem))
w = stem + step2list[suffix];
}
// Step 3
re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
suffix = fp[2];
re = new RegExp(mgr0);
if (re.test(stem))
w = stem + step3list[suffix];
}
// Step 4
re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
re2 = /^(.+?)(s|t)(ion)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(mgr1);
if (re.test(stem))
w = stem;
}
else if (re2.test(w)) {
var fp = re2.exec(w);
stem = fp[1] + fp[2];
re2 = new RegExp(mgr1);
if (re2.test(stem))
w = stem;
}
// Step 5
re = /^(.+?)e$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(mgr1);
re2 = new RegExp(meq1);
re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
w = stem;
}
re = /ll$/;
re2 = new RegExp(mgr1);
if (re.test(w) && re2.test(w)) {
re = /.$/;
w = w.replace(re,"");
}
// and turn initial Y back to y
if (firstch == "y")
w = firstch.toLowerCase() + w.substr(1);
return w;
}
}
/**
* Simple result scoring code.
*/
var Scorer = {
// Implement the following function to further tweak the score for each result
// The function takes a result array [filename, title, anchor, descr, score]
// and returns the new score.
/*
score: function(result) {
return result[4];
},
*/
// query matches the full name of an object
objNameMatch: 11,
// or matches in the last dotted part of the object name
objPartialMatch: 6,
// Additive scores depending on the priority of the object
objPrio: {0: 15, // used to be importantResults
1: 5, // used to be objectResults
2: -5}, // used to be unimportantResults
// Used when the priority is not in the mapping.
objPrioDefault: 0,
// query found in title
title: 15,
// query found in terms
term: 5
};
/**
* Search Module
*/
var Search = {
_index : null,
_queued_query : null,
_pulse_status : -1,
init : function() {
var params = $.getQueryParameters();
if (params.q) {
var query = params.q[0];
$('input[name="q"]')[0].value = query;
this.performSearch(query);
}
},
loadIndex : function(url) {
$.ajax({type: "GET", url: url, data: null,
dataType: "script", cache: true,
complete: function(jqxhr, textstatus) {
if (textstatus != "success") {
document.getElementById("searchindexloader").src = url;
}
}});
},
setIndex : function(index) {
var q;
this._index = index;
if ((q = this._queued_query) !== null) {
this._queued_query = null;
Search.query(q);
}
},
hasIndex : function() {
return this._index !== null;
},
deferQuery : function(query) {
this._queued_query = query;
},
stopPulse : function() {
this._pulse_status = 0;
},
startPulse : function() {
if (this._pulse_status >= 0)
return;
function pulse() {
var i;
Search._pulse_status = (Search._pulse_status + 1) % 4;
var dotString = '';
for (i = 0; i < Search._pulse_status; i++)
dotString += '.';
Search.dots.text(dotString);
if (Search._pulse_status > -1)
window.setTimeout(pulse, 500);
}
pulse();
},
/**
* perform a search for something (or wait until index is loaded)
*/
performSearch : function(query) {
// create the required interface elements
this.out = $('#search-results');
this.title = $('<h2>' + _('Searching') + '</h2>').appendTo(this.out);
this.dots = $('<span></span>').appendTo(this.title);
this.status = $('<p style="display: none"></p>').appendTo(this.out);
this.output = $('<ul class="search"/>').appendTo(this.out);
$('#search-progress').text(_('Preparing search...'));
this.startPulse();
// index already loaded, the browser was quick!
if (this.hasIndex())
this.query(query);
else
this.deferQuery(query);
},
/**
* execute search (requires search index to be loaded)
*/
query : function(query) {
var i;
var stopwords = ["a","and","are","as","at","be","but","by","for","if","in","into","is","it","near","no","not","of","on","or","such","that","the","their","then","there","these","they","this","to","was","will","with"];
// stem the searchterms and add them to the correct list
var stemmer = new Stemmer();
var searchterms = [];
var excluded = [];
var hlterms = [];
var tmp = query.split(/\s+/);
var objectterms = [];
for (i = 0; i < tmp.length; i++) {
if (tmp[i] !== "") {
objectterms.push(tmp[i].toLowerCase());
}
if ($u.indexOf(stopwords, tmp[i].toLowerCase()) != -1 || tmp[i].match(/^\d+$/) ||
tmp[i] === "") {
// skip this "word"
continue;
}
// stem the word
var word = stemmer.stemWord(tmp[i].toLowerCase());
var toAppend;
// select the correct list
if (word[0] == '-') {
toAppend = excluded;
word = word.substr(1);
}
else {
toAppend = searchterms;
hlterms.push(tmp[i].toLowerCase());
}
// only add if not already in the list
if (!$u.contains(toAppend, word))
toAppend.push(word);
}
var highlightstring = '?highlight=' + $.urlencode(hlterms.join(" "));
// console.debug('SEARCH: searching for:');
// console.info('required: ', searchterms);
// console.info('excluded: ', excluded);
// prepare search
var terms = this._index.terms;
var titleterms = this._index.titleterms;
// array of [filename, title, anchor, descr, score]
var results = [];
$('#search-progress').empty();
// lookup as object
for (i = 0; i < objectterms.length; i++) {
var others = [].concat(objectterms.slice(0, i),
objectterms.slice(i+1, objectterms.length));
results = results.concat(this.performObjectSearch(objectterms[i], others));
}
// lookup as search terms in fulltext
results = results.concat(this.performTermsSearch(searchterms, excluded, terms, Scorer.term))
.concat(this.performTermsSearch(searchterms, excluded, titleterms, Scorer.title));
// let the scorer override scores with a custom scoring function
if (Scorer.score) {
for (i = 0; i < results.length; i++)
results[i][4] = Scorer.score(results[i]);
}
// now sort the results by score (in opposite order of appearance, since the
// display function below uses pop() to retrieve items) and then
// alphabetically
results.sort(function(a, b) {
var left = a[4];
var right = b[4];
if (left > right) {
return 1;
} else if (left < right) {
return -1;
} else {
// same score: sort alphabetically
left = a[1].toLowerCase();
right = b[1].toLowerCase();
return (left > right) ? -1 : ((left < right) ? 1 : 0);
}
});
// for debugging
//Search.lastresults = results.slice(); // a copy
//console.info('search results:', Search.lastresults);
// print the results
var resultCount = results.length;
function displayNextItem() {
// results left, load the summary and display it
if (results.length) {
var item = results.pop();
var listItem = $('<li style="display:none"></li>');
if (DOCUMENTATION_OPTIONS.FILE_SUFFIX === '') {
// dirhtml builder
var dirname = item[0] + '/';
if (dirname.match(/\/index\/$/)) {
dirname = dirname.substring(0, dirname.length-6);
} else if (dirname == 'index/') {
dirname = '';
}
listItem.append($('<a/>').attr('href',
DOCUMENTATION_OPTIONS.URL_ROOT + dirname +
highlightstring + item[2]).html(item[1]));
} else {
// normal html builders
listItem.append($('<a/>').attr('href',
item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX +
highlightstring + item[2]).html(item[1]));
}
if (item[3]) {
listItem.append($('<span> (' + item[3] + ')</span>'));
Search.output.append(listItem);
listItem.slideDown(5, function() {
displayNextItem();
});
} else if (DOCUMENTATION_OPTIONS.HAS_SOURCE) {
$.ajax({url: DOCUMENTATION_OPTIONS.URL_ROOT + '_sources/' + item[0] + '.txt',
dataType: "text",
complete: function(jqxhr, textstatus) {
var data = jqxhr.responseText;
if (data !== '') {
listItem.append(Search.makeSearchSummary(data, searchterms, hlterms));
}
Search.output.append(listItem);
listItem.slideDown(5, function() {
displayNextItem();
});
}});
} else {
// no source available, just display title
Search.output.append(listItem);
listItem.slideDown(5, function() {
displayNextItem();
});
}
}
// search finished, update title and status message
else {
Search.stopPulse();
Search.title.text(_('Search Results'));
if (!resultCount)
Search.status.text(_('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.'));
else
Search.status.text(_('Search finished, found %s page(s) matching the search query.').replace('%s', resultCount));
Search.status.fadeIn(500);
}
}
displayNextItem();
},
/**
* search for object names
*/
performObjectSearch : function(object, otherterms) {
var filenames = this._index.filenames;
var objects = this._index.objects;
var objnames = this._index.objnames;
var titles = this._index.titles;
var i;
var results = [];
for (var prefix in objects) {
for (var name in objects[prefix]) {
var fullname = (prefix ? prefix + '.' : '') + name;
if (fullname.toLowerCase().indexOf(object) > -1) {
var score = 0;
var parts = fullname.split('.');
// check for different match types: exact matches of full name or
// "last name" (i.e. last dotted part)
if (fullname == object || parts[parts.length - 1] == object) {
score += Scorer.objNameMatch;
// matches in last name
} else if (parts[parts.length - 1].indexOf(object) > -1) {
score += Scorer.objPartialMatch;
}
var match = objects[prefix][name];
var objname = objnames[match[1]][2];
var title = titles[match[0]];
// If more than one term searched for, we require other words to be
// found in the name/title/description
if (otherterms.length > 0) {
var haystack = (prefix + ' ' + name + ' ' +
objname + ' ' + title).toLowerCase();
var allfound = true;
for (i = 0; i < otherterms.length; i++) {
if (haystack.indexOf(otherterms[i]) == -1) {
allfound = false;
break;
}
}
if (!allfound) {
continue;
}
}
var descr = objname + _(', in ') + title;
var anchor = match[3];
if (anchor === '')
anchor = fullname;
else if (anchor == '-')
anchor = objnames[match[1]][1] + '-' + fullname;
// add custom score for some objects according to scorer
if (Scorer.objPrio.hasOwnProperty(match[2])) {
score += Scorer.objPrio[match[2]];
} else {
score += Scorer.objPrioDefault;
}
results.push([filenames[match[0]], fullname, '#'+anchor, descr, score]);
}
}
}
return results;
},
/**
* search for full-text terms in the index
*/
performTermsSearch : function(searchterms, excluded, terms, score) {
var filenames = this._index.filenames;
var titles = this._index.titles;
var i, j, file, files;
var fileMap = {};
var results = [];
// perform the search on the required terms
for (i = 0; i < searchterms.length; i++) {
var word = searchterms[i];
// no match but word was a required one
if ((files = terms[word]) === undefined)
break;
if (files.length === undefined) {
files = [files];
}
// create the mapping
for (j = 0; j < files.length; j++) {
file = files[j];
if (file in fileMap)
fileMap[file].push(word);
else
fileMap[file] = [word];
}
}
// now check if the files don't contain excluded terms
for (file in fileMap) {
var valid = true;
// check if all requirements are matched
if (fileMap[file].length != searchterms.length)
continue;
// ensure that none of the excluded terms is in the search result
for (i = 0; i < excluded.length; i++) {
if (terms[excluded[i]] == file ||
$u.contains(terms[excluded[i]] || [], file)) {
valid = false;
break;
}
}
// if we have still a valid result we can add it to the result list
if (valid) {
results.push([filenames[file], titles[file], '', null, score]);
}
}
return results;
},
/**
* helper function to return a node containing the
* search summary for a given text. keywords is a list
* of stemmed words, hlwords is the list of normal, unstemmed
* words. the first one is used to find the occurance, the
* latter for highlighting it.
*/
makeSearchSummary : function(text, keywords, hlwords) {
var textLower = text.toLowerCase();
var start = 0;
$.each(keywords, function() {
var i = textLower.indexOf(this.toLowerCase());
if (i > -1)
start = i;
});
start = Math.max(start - 120, 0);
var excerpt = ((start > 0) ? '...' : '') +
$.trim(text.substr(start, 240)) +
((start + 240 - text.length) ? '...' : '');
var rv = $('<div class="context"></div>').text(excerpt);
$.each(hlwords, function() {
rv = rv.highlightText(this, 'highlighted');
});
return rv;
}
};
$(document).ready(function() {
Search.init();
});

1415
_static/underscore.js Normal file

File diff suppressed because it is too large Load diff

BIN
_static/up-pressed.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 B

BIN
_static/up.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 363 B

808
_static/websupport.js Normal file
View file

@ -0,0 +1,808 @@
/*
* websupport.js
* ~~~~~~~~~~~~~
*
* sphinx.websupport utilties for all documentation.
*
* :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
(function($) {
$.fn.autogrow = function() {
return this.each(function() {
var textarea = this;
$.fn.autogrow.resize(textarea);
$(textarea)
.focus(function() {
textarea.interval = setInterval(function() {
$.fn.autogrow.resize(textarea);
}, 500);
})
.blur(function() {
clearInterval(textarea.interval);
});
});
};
$.fn.autogrow.resize = function(textarea) {
var lineHeight = parseInt($(textarea).css('line-height'), 10);
var lines = textarea.value.split('\n');
var columns = textarea.cols;
var lineCount = 0;
$.each(lines, function() {
lineCount += Math.ceil(this.length / columns) || 1;
});
var height = lineHeight * (lineCount + 1);
$(textarea).css('height', height);
};
})(jQuery);
(function($) {
var comp, by;
function init() {
initEvents();
initComparator();
}
function initEvents() {
$('a.comment-close').live("click", function(event) {
event.preventDefault();
hide($(this).attr('id').substring(2));
});
$('a.vote').live("click", function(event) {
event.preventDefault();
handleVote($(this));
});
$('a.reply').live("click", function(event) {
event.preventDefault();
openReply($(this).attr('id').substring(2));
});
$('a.close-reply').live("click", function(event) {
event.preventDefault();
closeReply($(this).attr('id').substring(2));
});
$('a.sort-option').live("click", function(event) {
event.preventDefault();
handleReSort($(this));
});
$('a.show-proposal').live("click", function(event) {
event.preventDefault();
showProposal($(this).attr('id').substring(2));
});
$('a.hide-proposal').live("click", function(event) {
event.preventDefault();
hideProposal($(this).attr('id').substring(2));
});
$('a.show-propose-change').live("click", function(event) {
event.preventDefault();
showProposeChange($(this).attr('id').substring(2));
});
$('a.hide-propose-change').live("click", function(event) {
event.preventDefault();
hideProposeChange($(this).attr('id').substring(2));
});
$('a.accept-comment').live("click", function(event) {
event.preventDefault();
acceptComment($(this).attr('id').substring(2));
});
$('a.delete-comment').live("click", function(event) {
event.preventDefault();
deleteComment($(this).attr('id').substring(2));
});
$('a.comment-markup').live("click", function(event) {
event.preventDefault();
toggleCommentMarkupBox($(this).attr('id').substring(2));
});
}
/**
* Set comp, which is a comparator function used for sorting and
* inserting comments into the list.
*/
function setComparator() {
// If the first three letters are "asc", sort in ascending order
// and remove the prefix.
if (by.substring(0,3) == 'asc') {
var i = by.substring(3);
comp = function(a, b) { return a[i] - b[i]; };
} else {
// Otherwise sort in descending order.
comp = function(a, b) { return b[by] - a[by]; };
}
// Reset link styles and format the selected sort option.
$('a.sel').attr('href', '#').removeClass('sel');
$('a.by' + by).removeAttr('href').addClass('sel');
}
/**
* Create a comp function. If the user has preferences stored in
* the sortBy cookie, use those, otherwise use the default.
*/
function initComparator() {
by = 'rating'; // Default to sort by rating.
// If the sortBy cookie is set, use that instead.
if (document.cookie.length > 0) {
var start = document.cookie.indexOf('sortBy=');
if (start != -1) {
start = start + 7;
var end = document.cookie.indexOf(";", start);
if (end == -1) {
end = document.cookie.length;
by = unescape(document.cookie.substring(start, end));
}
}
}
setComparator();
}
/**
* Show a comment div.
*/
function show(id) {
$('#ao' + id).hide();
$('#ah' + id).show();
var context = $.extend({id: id}, opts);
var popup = $(renderTemplate(popupTemplate, context)).hide();
popup.find('textarea[name="proposal"]').hide();
popup.find('a.by' + by).addClass('sel');
var form = popup.find('#cf' + id);
form.submit(function(event) {
event.preventDefault();
addComment(form);
});
$('#s' + id).after(popup);
popup.slideDown('fast', function() {
getComments(id);
});
}
/**
* Hide a comment div.
*/
function hide(id) {
$('#ah' + id).hide();
$('#ao' + id).show();
var div = $('#sc' + id);
div.slideUp('fast', function() {
div.remove();
});
}
/**
* Perform an ajax request to get comments for a node
* and insert the comments into the comments tree.
*/
function getComments(id) {
$.ajax({
type: 'GET',
url: opts.getCommentsURL,
data: {node: id},
success: function(data, textStatus, request) {
var ul = $('#cl' + id);
var speed = 100;
$('#cf' + id)
.find('textarea[name="proposal"]')
.data('source', data.source);
if (data.comments.length === 0) {
ul.html('<li>No comments yet.</li>');
ul.data('empty', true);
} else {
// If there are comments, sort them and put them in the list.
var comments = sortComments(data.comments);
speed = data.comments.length * 100;
appendComments(comments, ul);
ul.data('empty', false);
}
$('#cn' + id).slideUp(speed + 200);
ul.slideDown(speed);
},
error: function(request, textStatus, error) {
showError('Oops, there was a problem retrieving the comments.');
},
dataType: 'json'
});
}
/**
* Add a comment via ajax and insert the comment into the comment tree.
*/
function addComment(form) {
var node_id = form.find('input[name="node"]').val();
var parent_id = form.find('input[name="parent"]').val();
var text = form.find('textarea[name="comment"]').val();
var proposal = form.find('textarea[name="proposal"]').val();
if (text == '') {
showError('Please enter a comment.');
return;
}
// Disable the form that is being submitted.
form.find('textarea,input').attr('disabled', 'disabled');
// Send the comment to the server.
$.ajax({
type: "POST",
url: opts.addCommentURL,
dataType: 'json',
data: {
node: node_id,
parent: parent_id,
text: text,
proposal: proposal
},
success: function(data, textStatus, error) {
// Reset the form.
if (node_id) {
hideProposeChange(node_id);
}
form.find('textarea')
.val('')
.add(form.find('input'))
.removeAttr('disabled');
var ul = $('#cl' + (node_id || parent_id));
if (ul.data('empty')) {
$(ul).empty();
ul.data('empty', false);
}
insertComment(data.comment);
var ao = $('#ao' + node_id);
ao.find('img').attr({'src': opts.commentBrightImage});
if (node_id) {
// if this was a "root" comment, remove the commenting box
// (the user can get it back by reopening the comment popup)
$('#ca' + node_id).slideUp();
}
},
error: function(request, textStatus, error) {
form.find('textarea,input').removeAttr('disabled');
showError('Oops, there was a problem adding the comment.');
}
});
}
/**
* Recursively append comments to the main comment list and children
* lists, creating the comment tree.
*/
function appendComments(comments, ul) {
$.each(comments, function() {
var div = createCommentDiv(this);
ul.append($(document.createElement('li')).html(div));
appendComments(this.children, div.find('ul.comment-children'));
// To avoid stagnating data, don't store the comments children in data.
this.children = null;
div.data('comment', this);
});
}
/**
* After adding a new comment, it must be inserted in the correct
* location in the comment tree.
*/
function insertComment(comment) {
var div = createCommentDiv(comment);
// To avoid stagnating data, don't store the comments children in data.
comment.children = null;
div.data('comment', comment);
var ul = $('#cl' + (comment.node || comment.parent));
var siblings = getChildren(ul);
var li = $(document.createElement('li'));
li.hide();
// Determine where in the parents children list to insert this comment.
for(i=0; i < siblings.length; i++) {
if (comp(comment, siblings[i]) <= 0) {
$('#cd' + siblings[i].id)
.parent()
.before(li.html(div));
li.slideDown('fast');
return;
}
}
// If we get here, this comment rates lower than all the others,
// or it is the only comment in the list.
ul.append(li.html(div));
li.slideDown('fast');
}
function acceptComment(id) {
$.ajax({
type: 'POST',
url: opts.acceptCommentURL,
data: {id: id},
success: function(data, textStatus, request) {
$('#cm' + id).fadeOut('fast');
$('#cd' + id).removeClass('moderate');
},
error: function(request, textStatus, error) {
showError('Oops, there was a problem accepting the comment.');
}
});
}
function deleteComment(id) {
$.ajax({
type: 'POST',
url: opts.deleteCommentURL,
data: {id: id},
success: function(data, textStatus, request) {
var div = $('#cd' + id);
if (data == 'delete') {
// Moderator mode: remove the comment and all children immediately
div.slideUp('fast', function() {
div.remove();
});
return;
}
// User mode: only mark the comment as deleted
div
.find('span.user-id:first')
.text('[deleted]').end()
.find('div.comment-text:first')
.text('[deleted]').end()
.find('#cm' + id + ', #dc' + id + ', #ac' + id + ', #rc' + id +
', #sp' + id + ', #hp' + id + ', #cr' + id + ', #rl' + id)
.remove();
var comment = div.data('comment');
comment.username = '[deleted]';
comment.text = '[deleted]';
div.data('comment', comment);
},
error: function(request, textStatus, error) {
showError('Oops, there was a problem deleting the comment.');
}
});
}
function showProposal(id) {
$('#sp' + id).hide();
$('#hp' + id).show();
$('#pr' + id).slideDown('fast');
}
function hideProposal(id) {
$('#hp' + id).hide();
$('#sp' + id).show();
$('#pr' + id).slideUp('fast');
}
function showProposeChange(id) {
$('#pc' + id).hide();
$('#hc' + id).show();
var textarea = $('#pt' + id);
textarea.val(textarea.data('source'));
$.fn.autogrow.resize(textarea[0]);
textarea.slideDown('fast');
}
function hideProposeChange(id) {
$('#hc' + id).hide();
$('#pc' + id).show();
var textarea = $('#pt' + id);
textarea.val('').removeAttr('disabled');
textarea.slideUp('fast');
}
function toggleCommentMarkupBox(id) {
$('#mb' + id).toggle();
}
/** Handle when the user clicks on a sort by link. */
function handleReSort(link) {
var classes = link.attr('class').split(/\s+/);
for (var i=0; i<classes.length; i++) {
if (classes[i] != 'sort-option') {
by = classes[i].substring(2);
}
}
setComparator();
// Save/update the sortBy cookie.
var expiration = new Date();
expiration.setDate(expiration.getDate() + 365);
document.cookie= 'sortBy=' + escape(by) +
';expires=' + expiration.toUTCString();
$('ul.comment-ul').each(function(index, ul) {
var comments = getChildren($(ul), true);
comments = sortComments(comments);
appendComments(comments, $(ul).empty());
});
}
/**
* Function to process a vote when a user clicks an arrow.
*/
function handleVote(link) {
if (!opts.voting) {
showError("You'll need to login to vote.");
return;
}
var id = link.attr('id');
if (!id) {
// Didn't click on one of the voting arrows.
return;
}
// If it is an unvote, the new vote value is 0,
// Otherwise it's 1 for an upvote, or -1 for a downvote.
var value = 0;
if (id.charAt(1) != 'u') {
value = id.charAt(0) == 'u' ? 1 : -1;
}
// The data to be sent to the server.
var d = {
comment_id: id.substring(2),
value: value
};
// Swap the vote and unvote links.
link.hide();
$('#' + id.charAt(0) + (id.charAt(1) == 'u' ? 'v' : 'u') + d.comment_id)
.show();
// The div the comment is displayed in.
var div = $('div#cd' + d.comment_id);
var data = div.data('comment');
// If this is not an unvote, and the other vote arrow has
// already been pressed, unpress it.
if ((d.value !== 0) && (data.vote === d.value * -1)) {
$('#' + (d.value == 1 ? 'd' : 'u') + 'u' + d.comment_id).hide();
$('#' + (d.value == 1 ? 'd' : 'u') + 'v' + d.comment_id).show();
}
// Update the comments rating in the local data.
data.rating += (data.vote === 0) ? d.value : (d.value - data.vote);
data.vote = d.value;
div.data('comment', data);
// Change the rating text.
div.find('.rating:first')
.text(data.rating + ' point' + (data.rating == 1 ? '' : 's'));
// Send the vote information to the server.
$.ajax({
type: "POST",
url: opts.processVoteURL,
data: d,
error: function(request, textStatus, error) {
showError('Oops, there was a problem casting that vote.');
}
});
}
/**
* Open a reply form used to reply to an existing comment.
*/
function openReply(id) {
// Swap out the reply link for the hide link
$('#rl' + id).hide();
$('#cr' + id).show();
// Add the reply li to the children ul.
var div = $(renderTemplate(replyTemplate, {id: id})).hide();
$('#cl' + id)
.prepend(div)
// Setup the submit handler for the reply form.
.find('#rf' + id)
.submit(function(event) {
event.preventDefault();
addComment($('#rf' + id));
closeReply(id);
})
.find('input[type=button]')
.click(function() {
closeReply(id);
});
div.slideDown('fast', function() {
$('#rf' + id).find('textarea').focus();
});
}
/**
* Close the reply form opened with openReply.
*/
function closeReply(id) {
// Remove the reply div from the DOM.
$('#rd' + id).slideUp('fast', function() {
$(this).remove();
});
// Swap out the hide link for the reply link
$('#cr' + id).hide();
$('#rl' + id).show();
}
/**
* Recursively sort a tree of comments using the comp comparator.
*/
function sortComments(comments) {
comments.sort(comp);
$.each(comments, function() {
this.children = sortComments(this.children);
});
return comments;
}
/**
* Get the children comments from a ul. If recursive is true,
* recursively include childrens' children.
*/
function getChildren(ul, recursive) {
var children = [];
ul.children().children("[id^='cd']")
.each(function() {
var comment = $(this).data('comment');
if (recursive)
comment.children = getChildren($(this).find('#cl' + comment.id), true);
children.push(comment);
});
return children;
}
/** Create a div to display a comment in. */
function createCommentDiv(comment) {
if (!comment.displayed && !opts.moderator) {
return $('<div class="moderate">Thank you! Your comment will show up '
+ 'once it is has been approved by a moderator.</div>');
}
// Prettify the comment rating.
comment.pretty_rating = comment.rating + ' point' +
(comment.rating == 1 ? '' : 's');
// Make a class (for displaying not yet moderated comments differently)
comment.css_class = comment.displayed ? '' : ' moderate';
// Create a div for this comment.
var context = $.extend({}, opts, comment);
var div = $(renderTemplate(commentTemplate, context));
// If the user has voted on this comment, highlight the correct arrow.
if (comment.vote) {
var direction = (comment.vote == 1) ? 'u' : 'd';
div.find('#' + direction + 'v' + comment.id).hide();
div.find('#' + direction + 'u' + comment.id).show();
}
if (opts.moderator || comment.text != '[deleted]') {
div.find('a.reply').show();
if (comment.proposal_diff)
div.find('#sp' + comment.id).show();
if (opts.moderator && !comment.displayed)
div.find('#cm' + comment.id).show();
if (opts.moderator || (opts.username == comment.username))
div.find('#dc' + comment.id).show();
}
return div;
}
/**
* A simple template renderer. Placeholders such as <%id%> are replaced
* by context['id'] with items being escaped. Placeholders such as <#id#>
* are not escaped.
*/
function renderTemplate(template, context) {
var esc = $(document.createElement('div'));
function handle(ph, escape) {
var cur = context;
$.each(ph.split('.'), function() {
cur = cur[this];
});
return escape ? esc.text(cur || "").html() : cur;
}
return template.replace(/<([%#])([\w\.]*)\1>/g, function() {
return handle(arguments[2], arguments[1] == '%' ? true : false);
});
}
/** Flash an error message briefly. */
function showError(message) {
$(document.createElement('div')).attr({'class': 'popup-error'})
.append($(document.createElement('div'))
.attr({'class': 'error-message'}).text(message))
.appendTo('body')
.fadeIn("slow")
.delay(2000)
.fadeOut("slow");
}
/** Add a link the user uses to open the comments popup. */
$.fn.comment = function() {
return this.each(function() {
var id = $(this).attr('id').substring(1);
var count = COMMENT_METADATA[id];
var title = count + ' comment' + (count == 1 ? '' : 's');
var image = count > 0 ? opts.commentBrightImage : opts.commentImage;
var addcls = count == 0 ? ' nocomment' : '';
$(this)
.append(
$(document.createElement('a')).attr({
href: '#',
'class': 'sphinx-comment-open' + addcls,
id: 'ao' + id
})
.append($(document.createElement('img')).attr({
src: image,
alt: 'comment',
title: title
}))
.click(function(event) {
event.preventDefault();
show($(this).attr('id').substring(2));
})
)
.append(
$(document.createElement('a')).attr({
href: '#',
'class': 'sphinx-comment-close hidden',
id: 'ah' + id
})
.append($(document.createElement('img')).attr({
src: opts.closeCommentImage,
alt: 'close',
title: 'close'
}))
.click(function(event) {
event.preventDefault();
hide($(this).attr('id').substring(2));
})
);
});
};
var opts = {
processVoteURL: '/_process_vote',
addCommentURL: '/_add_comment',
getCommentsURL: '/_get_comments',
acceptCommentURL: '/_accept_comment',
deleteCommentURL: '/_delete_comment',
commentImage: '/static/_static/comment.png',
closeCommentImage: '/static/_static/comment-close.png',
loadingImage: '/static/_static/ajax-loader.gif',
commentBrightImage: '/static/_static/comment-bright.png',
upArrow: '/static/_static/up.png',
downArrow: '/static/_static/down.png',
upArrowPressed: '/static/_static/up-pressed.png',
downArrowPressed: '/static/_static/down-pressed.png',
voting: false,
moderator: false
};
if (typeof COMMENT_OPTIONS != "undefined") {
opts = jQuery.extend(opts, COMMENT_OPTIONS);
}
var popupTemplate = '\
<div class="sphinx-comments" id="sc<%id%>">\
<p class="sort-options">\
Sort by:\
<a href="#" class="sort-option byrating">best rated</a>\
<a href="#" class="sort-option byascage">newest</a>\
<a href="#" class="sort-option byage">oldest</a>\
</p>\
<div class="comment-header">Comments</div>\
<div class="comment-loading" id="cn<%id%>">\
loading comments... <img src="<%loadingImage%>" alt="" /></div>\
<ul id="cl<%id%>" class="comment-ul"></ul>\
<div id="ca<%id%>">\
<p class="add-a-comment">Add a comment\
(<a href="#" class="comment-markup" id="ab<%id%>">markup</a>):</p>\
<div class="comment-markup-box" id="mb<%id%>">\
reStructured text markup: <i>*emph*</i>, <b>**strong**</b>, \
<tt>``code``</tt>, \
code blocks: <tt>::</tt> and an indented block after blank line</div>\
<form method="post" id="cf<%id%>" class="comment-form" action="">\
<textarea name="comment" cols="80"></textarea>\
<p class="propose-button">\
<a href="#" id="pc<%id%>" class="show-propose-change">\
Propose a change &#9657;\
</a>\
<a href="#" id="hc<%id%>" class="hide-propose-change">\
Propose a change &#9663;\
</a>\
</p>\
<textarea name="proposal" id="pt<%id%>" cols="80"\
spellcheck="false"></textarea>\
<input type="submit" value="Add comment" />\
<input type="hidden" name="node" value="<%id%>" />\
<input type="hidden" name="parent" value="" />\
</form>\
</div>\
</div>';
var commentTemplate = '\
<div id="cd<%id%>" class="sphinx-comment<%css_class%>">\
<div class="vote">\
<div class="arrow">\
<a href="#" id="uv<%id%>" class="vote" title="vote up">\
<img src="<%upArrow%>" />\
</a>\
<a href="#" id="uu<%id%>" class="un vote" title="vote up">\
<img src="<%upArrowPressed%>" />\
</a>\
</div>\
<div class="arrow">\
<a href="#" id="dv<%id%>" class="vote" title="vote down">\
<img src="<%downArrow%>" id="da<%id%>" />\
</a>\
<a href="#" id="du<%id%>" class="un vote" title="vote down">\
<img src="<%downArrowPressed%>" />\
</a>\
</div>\
</div>\
<div class="comment-content">\
<p class="tagline comment">\
<span class="user-id"><%username%></span>\
<span class="rating"><%pretty_rating%></span>\
<span class="delta"><%time.delta%></span>\
</p>\
<div class="comment-text comment"><#text#></div>\
<p class="comment-opts comment">\
<a href="#" class="reply hidden" id="rl<%id%>">reply &#9657;</a>\
<a href="#" class="close-reply" id="cr<%id%>">reply &#9663;</a>\
<a href="#" id="sp<%id%>" class="show-proposal">proposal &#9657;</a>\
<a href="#" id="hp<%id%>" class="hide-proposal">proposal &#9663;</a>\
<a href="#" id="dc<%id%>" class="delete-comment hidden">delete</a>\
<span id="cm<%id%>" class="moderation hidden">\
<a href="#" id="ac<%id%>" class="accept-comment">accept</a>\
</span>\
</p>\
<pre class="proposal" id="pr<%id%>">\
<#proposal_diff#>\
</pre>\
<ul class="comment-children" id="cl<%id%>"></ul>\
</div>\
<div class="clearleft"></div>\
</div>\
</div>';
var replyTemplate = '\
<li>\
<div class="reply-div" id="rd<%id%>">\
<form id="rf<%id%>">\
<textarea name="comment" cols="80"></textarea>\
<input type="submit" value="Add reply" />\
<input type="button" value="Cancel" />\
<input type="hidden" name="parent" value="<%id%>" />\
<input type="hidden" name="node" value="" />\
</form>\
</div>\
</li>';
$(document).ready(function() {
init();
});
})(jQuery);
$(document).ready(function() {
// add comment anchors for all paragraphs that are commentable
$('.sphinx-has-comment').comment();
// highlight search words in search results
$("div.context").each(function() {
var params = $.getQueryParameters();
var terms = (params.q) ? params.q[0].split(/\s+/) : [];
var result = $(this);
$.each(terms, function() {
result.highlightText(this.toLowerCase(), 'highlighted');
});
});
// directly open comment window if requested
var anchor = document.location.hash;
if (anchor.substring(0, 9) == '#comment-') {
$('#ao' + anchor.substring(9)).click();
document.location.hash = '#s' + anchor.substring(9);
}
});

View file

@ -1,38 +0,0 @@
#!/usr/bin/env python
# This script simply downloads waf to the current directory
from __future__ import print_function
import os, sys, stat, hashlib, subprocess
WAFRELEASE = "waf-1.8.6"
WAFURL = "http://ftp.waf.io/pub/release/" + WAFRELEASE
SHA256HASH = "81c4e6a3144c7b2021a839e7277bdaf1cedbbc87302186897b4ae03f4effcbf5"
if os.path.exists("waf"):
wafver = subprocess.check_output(['./waf', '--version']).decode()
if WAFRELEASE.split('-')[1] == wafver.split(' ')[1]:
print("Found 'waf', skipping download.")
sys.exit(0)
try:
from urllib.request import urlopen
except:
from urllib2 import urlopen
print("Downloading %s..." % WAFURL)
waf = urlopen(WAFURL).read()
if SHA256HASH == hashlib.sha256(waf).hexdigest():
with open("waf", "wb") as wf:
wf.write(waf)
os.chmod("waf", os.stat("waf").st_mode | stat.S_IXUSR)
print("Checksum verified.")
else:
print("The checksum of the downloaded file does not match!")
print(" - got: {}".format(hashlib.sha256(waf).hexdigest()))
print(" - expected: {}".format(SHA256HASH))
print("Please download and verify the file manually.")
sys.exit(1)

757
deps/ut/utlist.h vendored
View file

@ -1,757 +0,0 @@
/*
Copyright (c) 2007-2014, Troy D. Hanson http://troydhanson.github.com/uthash/
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef UTLIST_H
#define UTLIST_H
#define UTLIST_VERSION 1.9.9
#include <assert.h>
/*
* This file contains macros to manipulate singly and doubly-linked lists.
*
* 1. LL_ macros: singly-linked lists.
* 2. DL_ macros: doubly-linked lists.
* 3. CDL_ macros: circular doubly-linked lists.
*
* To use singly-linked lists, your structure must have a "next" pointer.
* To use doubly-linked lists, your structure must "prev" and "next" pointers.
* Either way, the pointer to the head of the list must be initialized to NULL.
*
* ----------------.EXAMPLE -------------------------
* struct item {
* int id;
* struct item *prev, *next;
* }
*
* struct item *list = NULL:
*
* int main() {
* struct item *item;
* ... allocate and populate item ...
* DL_APPEND(list, item);
* }
* --------------------------------------------------
*
* For doubly-linked lists, the append and delete macros are O(1)
* For singly-linked lists, append and delete are O(n) but prepend is O(1)
* The sort macro is O(n log(n)) for all types of single/double/circular lists.
*/
/* These macros use decltype or the earlier __typeof GNU extension.
As decltype is only available in newer compilers (VS2010 or gcc 4.3+
when compiling c++ code), this code uses whatever method is needed
or, for VS2008 where neither is available, uses casting workarounds. */
#ifdef _MSC_VER /* MS compiler */
#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */
#define LDECLTYPE(x) decltype(x)
#else /* VS2008 or older (or VS2010 in C mode) */
#define NO_DECLTYPE
#define LDECLTYPE(x) char*
#endif
#elif defined(__ICCARM__)
#define NO_DECLTYPE
#define LDECLTYPE(x) char*
#else /* GNU, Sun and other compilers */
#define LDECLTYPE(x) __typeof(x)
#endif
/* for VS2008 we use some workarounds to get around the lack of decltype,
* namely, we always reassign our tmp variable to the list head if we need
* to dereference its prev/next pointers, and save/restore the real head.*/
#ifdef NO_DECLTYPE
#define _SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); }
#define _NEXT(elt,list,next) ((char*)((list)->next))
#define _NEXTASGN(elt,list,to,next) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); }
/* #define _PREV(elt,list,prev) ((char*)((list)->prev)) */
#define _PREVASGN(elt,list,to,prev) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); }
#define _RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; }
#define _CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); }
#else
#define _SV(elt,list)
#define _NEXT(elt,list,next) ((elt)->next)
#define _NEXTASGN(elt,list,to,next) ((elt)->next)=(to)
/* #define _PREV(elt,list,prev) ((elt)->prev) */
#define _PREVASGN(elt,list,to,prev) ((elt)->prev)=(to)
#define _RS(list)
#define _CASTASGN(a,b) (a)=(b)
#endif
/******************************************************************************
* The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort *
* Unwieldy variable names used here to avoid shadowing passed-in variables. *
*****************************************************************************/
#define LL_SORT(list, cmp) \
LL_SORT2(list, cmp, next)
#define LL_SORT2(list, cmp, next) \
do { \
LDECLTYPE(list) _ls_p; \
LDECLTYPE(list) _ls_q; \
LDECLTYPE(list) _ls_e; \
LDECLTYPE(list) _ls_tail; \
int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \
if (list) { \
_ls_insize = 1; \
_ls_looping = 1; \
while (_ls_looping) { \
_CASTASGN(_ls_p,list); \
list = NULL; \
_ls_tail = NULL; \
_ls_nmerges = 0; \
while (_ls_p) { \
_ls_nmerges++; \
_ls_q = _ls_p; \
_ls_psize = 0; \
for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \
_ls_psize++; \
_SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list); \
if (!_ls_q) break; \
} \
_ls_qsize = _ls_insize; \
while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \
if (_ls_psize == 0) { \
_ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \
_NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \
} else if (_ls_qsize == 0 || !_ls_q) { \
_ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \
_NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \
} else if (cmp(_ls_p,_ls_q) <= 0) { \
_ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \
_NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \
} else { \
_ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \
_NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \
} \
if (_ls_tail) { \
_SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \
} else { \
_CASTASGN(list,_ls_e); \
} \
_ls_tail = _ls_e; \
} \
_ls_p = _ls_q; \
} \
if (_ls_tail) { \
_SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list); \
} \
if (_ls_nmerges <= 1) { \
_ls_looping=0; \
} \
_ls_insize *= 2; \
} \
} \
} while (0)
#define DL_SORT(list, cmp) \
DL_SORT2(list, cmp, prev, next)
#define DL_SORT2(list, cmp, prev, next) \
do { \
LDECLTYPE(list) _ls_p; \
LDECLTYPE(list) _ls_q; \
LDECLTYPE(list) _ls_e; \
LDECLTYPE(list) _ls_tail; \
int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \
if (list) { \
_ls_insize = 1; \
_ls_looping = 1; \
while (_ls_looping) { \
_CASTASGN(_ls_p,list); \
list = NULL; \
_ls_tail = NULL; \
_ls_nmerges = 0; \
while (_ls_p) { \
_ls_nmerges++; \
_ls_q = _ls_p; \
_ls_psize = 0; \
for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \
_ls_psize++; \
_SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list); \
if (!_ls_q) break; \
} \
_ls_qsize = _ls_insize; \
while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \
if (_ls_psize == 0) { \
_ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \
_NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \
} else if (_ls_qsize == 0 || !_ls_q) { \
_ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \
_NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \
} else if (cmp(_ls_p,_ls_q) <= 0) { \
_ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \
_NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \
} else { \
_ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \
_NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \
} \
if (_ls_tail) { \
_SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \
} else { \
_CASTASGN(list,_ls_e); \
} \
_SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list); \
_ls_tail = _ls_e; \
} \
_ls_p = _ls_q; \
} \
_CASTASGN(list->prev, _ls_tail); \
_SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list); \
if (_ls_nmerges <= 1) { \
_ls_looping=0; \
} \
_ls_insize *= 2; \
} \
} \
} while (0)
#define CDL_SORT(list, cmp) \
CDL_SORT2(list, cmp, prev, next)
#define CDL_SORT2(list, cmp, prev, next) \
do { \
LDECLTYPE(list) _ls_p; \
LDECLTYPE(list) _ls_q; \
LDECLTYPE(list) _ls_e; \
LDECLTYPE(list) _ls_tail; \
LDECLTYPE(list) _ls_oldhead; \
LDECLTYPE(list) _tmp; \
int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \
if (list) { \
_ls_insize = 1; \
_ls_looping = 1; \
while (_ls_looping) { \
_CASTASGN(_ls_p,list); \
_CASTASGN(_ls_oldhead,list); \
list = NULL; \
_ls_tail = NULL; \
_ls_nmerges = 0; \
while (_ls_p) { \
_ls_nmerges++; \
_ls_q = _ls_p; \
_ls_psize = 0; \
for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \
_ls_psize++; \
_SV(_ls_q,list); \
if (_NEXT(_ls_q,list,next) == _ls_oldhead) { \
_ls_q = NULL; \
} else { \
_ls_q = _NEXT(_ls_q,list,next); \
} \
_RS(list); \
if (!_ls_q) break; \
} \
_ls_qsize = _ls_insize; \
while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \
if (_ls_psize == 0) { \
_ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \
_NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \
if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \
} else if (_ls_qsize == 0 || !_ls_q) { \
_ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \
_NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \
if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \
} else if (cmp(_ls_p,_ls_q) <= 0) { \
_ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \
_NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \
if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \
} else { \
_ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \
_NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \
if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \
} \
if (_ls_tail) { \
_SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \
} else { \
_CASTASGN(list,_ls_e); \
} \
_SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list); \
_ls_tail = _ls_e; \
} \
_ls_p = _ls_q; \
} \
_CASTASGN(list->prev,_ls_tail); \
_CASTASGN(_tmp,list); \
_SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_tmp,next); _RS(list); \
if (_ls_nmerges <= 1) { \
_ls_looping=0; \
} \
_ls_insize *= 2; \
} \
} \
} while (0)
/******************************************************************************
* singly linked list macros (non-circular) *
*****************************************************************************/
#define LL_PREPEND(head,add) \
LL_PREPEND2(head,add,next)
#define LL_PREPEND2(head,add,next) \
do { \
(add)->next = head; \
head = add; \
} while (0)
#define LL_CONCAT(head1,head2) \
LL_CONCAT2(head1,head2,next)
#define LL_CONCAT2(head1,head2,next) \
do { \
LDECLTYPE(head1) _tmp; \
if (head1) { \
_tmp = head1; \
while (_tmp->next) { _tmp = _tmp->next; } \
_tmp->next=(head2); \
} else { \
(head1)=(head2); \
} \
} while (0)
#define LL_APPEND(head,add) \
LL_APPEND2(head,add,next)
#define LL_APPEND2(head,add,next) \
do { \
LDECLTYPE(head) _tmp; \
(add)->next=NULL; \
if (head) { \
_tmp = head; \
while (_tmp->next) { _tmp = _tmp->next; } \
_tmp->next=(add); \
} else { \
(head)=(add); \
} \
} while (0)
#define LL_DELETE(head,del) \
LL_DELETE2(head,del,next)
#define LL_DELETE2(head,del,next) \
do { \
LDECLTYPE(head) _tmp; \
if ((head) == (del)) { \
(head)=(head)->next; \
} else { \
_tmp = head; \
while (_tmp->next && (_tmp->next != (del))) { \
_tmp = _tmp->next; \
} \
if (_tmp->next) { \
_tmp->next = ((del)->next); \
} \
} \
} while (0)
/* Here are VS2008 replacements for LL_APPEND and LL_DELETE */
#define LL_APPEND_VS2008(head,add) \
LL_APPEND2_VS2008(head,add,next)
#define LL_APPEND2_VS2008(head,add,next) \
do { \
if (head) { \
(add)->next = head; /* use add->next as a temp variable */ \
while ((add)->next->next) { (add)->next = (add)->next->next; } \
(add)->next->next=(add); \
} else { \
(head)=(add); \
} \
(add)->next=NULL; \
} while (0)
#define LL_DELETE_VS2008(head,del) \
LL_DELETE2_VS2008(head,del,next)
#define LL_DELETE2_VS2008(head,del,next) \
do { \
if ((head) == (del)) { \
(head)=(head)->next; \
} else { \
char *_tmp = (char*)(head); \
while ((head)->next && ((head)->next != (del))) { \
head = (head)->next; \
} \
if ((head)->next) { \
(head)->next = ((del)->next); \
} \
{ \
char **_head_alias = (char**)&(head); \
*_head_alias = _tmp; \
} \
} \
} while (0)
#ifdef NO_DECLTYPE
#undef LL_APPEND
#define LL_APPEND LL_APPEND_VS2008
#undef LL_DELETE
#define LL_DELETE LL_DELETE_VS2008
#undef LL_DELETE2
#define LL_DELETE2 LL_DELETE2_VS2008
#undef LL_APPEND2
#define LL_APPEND2 LL_APPEND2_VS2008
#undef LL_CONCAT /* no LL_CONCAT_VS2008 */
#undef DL_CONCAT /* no DL_CONCAT_VS2008 */
#endif
/* end VS2008 replacements */
#define LL_COUNT(head,el,counter) \
LL_COUNT2(head,el,counter,next) \
#define LL_COUNT2(head,el,counter,next) \
{ \
counter = 0; \
LL_FOREACH2(head,el,next){ ++counter; } \
}
#define LL_FOREACH(head,el) \
LL_FOREACH2(head,el,next)
#define LL_FOREACH2(head,el,next) \
for(el=head;el;el=(el)->next)
#define LL_FOREACH_SAFE(head,el,tmp) \
LL_FOREACH_SAFE2(head,el,tmp,next)
#define LL_FOREACH_SAFE2(head,el,tmp,next) \
for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp)
#define LL_SEARCH_SCALAR(head,out,field,val) \
LL_SEARCH_SCALAR2(head,out,field,val,next)
#define LL_SEARCH_SCALAR2(head,out,field,val,next) \
do { \
LL_FOREACH2(head,out,next) { \
if ((out)->field == (val)) break; \
} \
} while(0)
#define LL_SEARCH(head,out,elt,cmp) \
LL_SEARCH2(head,out,elt,cmp,next)
#define LL_SEARCH2(head,out,elt,cmp,next) \
do { \
LL_FOREACH2(head,out,next) { \
if ((cmp(out,elt))==0) break; \
} \
} while(0)
#define LL_REPLACE_ELEM(head, el, add) \
do { \
LDECLTYPE(head) _tmp; \
assert(head != NULL); \
assert(el != NULL); \
assert(add != NULL); \
(add)->next = (el)->next; \
if ((head) == (el)) { \
(head) = (add); \
} else { \
_tmp = head; \
while (_tmp->next && (_tmp->next != (el))) { \
_tmp = _tmp->next; \
} \
if (_tmp->next) { \
_tmp->next = (add); \
} \
} \
} while (0)
#define LL_PREPEND_ELEM(head, el, add) \
do { \
LDECLTYPE(head) _tmp; \
assert(head != NULL); \
assert(el != NULL); \
assert(add != NULL); \
(add)->next = (el); \
if ((head) == (el)) { \
(head) = (add); \
} else { \
_tmp = head; \
while (_tmp->next && (_tmp->next != (el))) { \
_tmp = _tmp->next; \
} \
if (_tmp->next) { \
_tmp->next = (add); \
} \
} \
} while (0) \
/******************************************************************************
* doubly linked list macros (non-circular) *
*****************************************************************************/
#define DL_PREPEND(head,add) \
DL_PREPEND2(head,add,prev,next)
#define DL_PREPEND2(head,add,prev,next) \
do { \
(add)->next = head; \
if (head) { \
(add)->prev = (head)->prev; \
(head)->prev = (add); \
} else { \
(add)->prev = (add); \
} \
(head) = (add); \
} while (0)
#define DL_APPEND(head,add) \
DL_APPEND2(head,add,prev,next)
#define DL_APPEND2(head,add,prev,next) \
do { \
if (head) { \
(add)->prev = (head)->prev; \
(head)->prev->next = (add); \
(head)->prev = (add); \
(add)->next = NULL; \
} else { \
(head)=(add); \
(head)->prev = (head); \
(head)->next = NULL; \
} \
} while (0)
#define DL_CONCAT(head1,head2) \
DL_CONCAT2(head1,head2,prev,next)
#define DL_CONCAT2(head1,head2,prev,next) \
do { \
LDECLTYPE(head1) _tmp; \
if (head2) { \
if (head1) { \
_tmp = (head2)->prev; \
(head2)->prev = (head1)->prev; \
(head1)->prev->next = (head2); \
(head1)->prev = _tmp; \
} else { \
(head1)=(head2); \
} \
} \
} while (0)
#define DL_DELETE(head,del) \
DL_DELETE2(head,del,prev,next)
#define DL_DELETE2(head,del,prev,next) \
do { \
assert((del)->prev != NULL); \
if ((del)->prev == (del)) { \
(head)=NULL; \
} else if ((del)==(head)) { \
(del)->next->prev = (del)->prev; \
(head) = (del)->next; \
} else { \
(del)->prev->next = (del)->next; \
if ((del)->next) { \
(del)->next->prev = (del)->prev; \
} else { \
(head)->prev = (del)->prev; \
} \
} \
} while (0)
#define DL_COUNT(head,el,counter) \
DL_COUNT2(head,el,counter,next) \
#define DL_COUNT2(head,el,counter,next) \
{ \
counter = 0; \
DL_FOREACH2(head,el,next){ ++counter; } \
}
#define DL_FOREACH(head,el) \
DL_FOREACH2(head,el,next)
#define DL_FOREACH2(head,el,next) \
for(el=head;el;el=(el)->next)
/* this version is safe for deleting the elements during iteration */
#define DL_FOREACH_SAFE(head,el,tmp) \
DL_FOREACH_SAFE2(head,el,tmp,next)
#define DL_FOREACH_SAFE2(head,el,tmp,next) \
for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp)
/* these are identical to their singly-linked list counterparts */
#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR
#define DL_SEARCH LL_SEARCH
#define DL_SEARCH_SCALAR2 LL_SEARCH_SCALAR2
#define DL_SEARCH2 LL_SEARCH2
#define DL_REPLACE_ELEM(head, el, add) \
do { \
assert(head != NULL); \
assert(el != NULL); \
assert(add != NULL); \
if ((head) == (el)) { \
(head) = (add); \
(add)->next = (el)->next; \
if ((el)->next == NULL) { \
(add)->prev = (add); \
} else { \
(add)->prev = (el)->prev; \
(add)->next->prev = (add); \
} \
} else { \
(add)->next = (el)->next; \
(add)->prev = (el)->prev; \
(add)->prev->next = (add); \
if ((el)->next == NULL) { \
(head)->prev = (add); \
} else { \
(add)->next->prev = (add); \
} \
} \
} while (0)
#define DL_PREPEND_ELEM(head, el, add) \
do { \
assert(head != NULL); \
assert(el != NULL); \
assert(add != NULL); \
(add)->next = (el); \
(add)->prev = (el)->prev; \
(el)->prev = (add); \
if ((head) == (el)) { \
(head) = (add); \
} else { \
(add)->prev->next = (add); \
} \
} while (0) \
/******************************************************************************
* circular doubly linked list macros *
*****************************************************************************/
#define CDL_PREPEND(head,add) \
CDL_PREPEND2(head,add,prev,next)
#define CDL_PREPEND2(head,add,prev,next) \
do { \
if (head) { \
(add)->prev = (head)->prev; \
(add)->next = (head); \
(head)->prev = (add); \
(add)->prev->next = (add); \
} else { \
(add)->prev = (add); \
(add)->next = (add); \
} \
(head)=(add); \
} while (0)
#define CDL_DELETE(head,del) \
CDL_DELETE2(head,del,prev,next)
#define CDL_DELETE2(head,del,prev,next) \
do { \
if ( ((head)==(del)) && ((head)->next == (head))) { \
(head) = 0L; \
} else { \
(del)->next->prev = (del)->prev; \
(del)->prev->next = (del)->next; \
if ((del) == (head)) (head)=(del)->next; \
} \
} while (0)
#define CDL_COUNT(head,el,counter) \
CDL_COUNT2(head,el,counter,next) \
#define CDL_COUNT2(head, el, counter,next) \
{ \
counter = 0; \
CDL_FOREACH2(head,el,next){ ++counter; } \
}
#define CDL_FOREACH(head,el) \
CDL_FOREACH2(head,el,next)
#define CDL_FOREACH2(head,el,next) \
for(el=head;el;el=((el)->next==head ? 0L : (el)->next))
#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \
CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next)
#define CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) \
for((el)=(head), ((tmp1)=(head)?((head)->prev):NULL); \
(el) && ((tmp2)=(el)->next, 1); \
((el) = (((el)==(tmp1)) ? 0L : (tmp2))))
#define CDL_SEARCH_SCALAR(head,out,field,val) \
CDL_SEARCH_SCALAR2(head,out,field,val,next)
#define CDL_SEARCH_SCALAR2(head,out,field,val,next) \
do { \
CDL_FOREACH2(head,out,next) { \
if ((out)->field == (val)) break; \
} \
} while(0)
#define CDL_SEARCH(head,out,elt,cmp) \
CDL_SEARCH2(head,out,elt,cmp,next)
#define CDL_SEARCH2(head,out,elt,cmp,next) \
do { \
CDL_FOREACH2(head,out,next) { \
if ((cmp(out,elt))==0) break; \
} \
} while(0)
#define CDL_REPLACE_ELEM(head, el, add) \
do { \
assert(head != NULL); \
assert(el != NULL); \
assert(add != NULL); \
if ((el)->next == (el)) { \
(add)->next = (add); \
(add)->prev = (add); \
(head) = (add); \
} else { \
(add)->next = (el)->next; \
(add)->prev = (el)->prev; \
(add)->next->prev = (add); \
(add)->prev->next = (add); \
if ((head) == (el)) { \
(head) = (add); \
} \
} \
} while (0)
#define CDL_PREPEND_ELEM(head, el, add) \
do { \
assert(head != NULL); \
assert(el != NULL); \
assert(add != NULL); \
(add)->next = (el); \
(add)->prev = (el)->prev; \
(el)->prev = (add); \
(add)->prev->next = (add); \
if ((head) == (el)) { \
(head) = (add); \
} \
} while (0) \
#endif /* UTLIST_H */

View file

@ -1,196 +0,0 @@
# -*- coding: utf-8 -*-
#
# pflask documentation build configuration file, created by
# sphinx-quickstart on Sun Mar 22 14:43:38 2015.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys
import os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = []
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'pflask'
copyright = u'2013, Alessandro Ghedini'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '@VERSION@'
# The full version, including alpha/beta/rc tags.
release = version
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
#keep_warnings = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'nature'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#html_extra_path = []
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'pflaskdoc'
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('pflask', 'pflask',
u'tool for creating Linux namespace containers',
[u'Alessandro Ghedini'], 1),
('pflask-debuild', 'pflask-debuild',
u'build Debian packages inside Linux namespace containers',
[u'Alessandro Ghedini'], 1)
]
# If true, show URL addresses after external links.
#man_show_urls = False

View file

@ -1,29 +0,0 @@
#compdef pflask
# pflask zsh completion
local curcontext="$curcontext" state state_descr line
typeset -A opt_args
_arguments -C -S \
{--mount=,-m}'[Create a new mount point inside the container]:mount spec' \
{--netif=,-n+}'[Create a new network namespace and optionally move a network interface inside it]' \
{--user=,-u}'[Run the command under the specified user]:user' \
{--user-map=,-e}'[Map container users to host users]:map' \
{--chroot=,-r}'[Change the root directory inside the container]:directory:_directories' \
{--chdir=,-c}'[Change the current directory inside the container]:directory' \
{--ephemeral,-w}'[Discard changes to /]' \
{--cgroup=,-g}'[Create new cgroups and move the container inside them]:cgroup spec' \
{--detach,-d}'[Detach from terminal]' \
{--attach=,-a}'[Attach to the specified detached process]:PID' \
{--setenv=,-s}'[Set additional environment variables]:env variable' \
{--keepenv,-k}'[Do not clear environment]' \
{--hostname=,-t}'[Set the container hostname]:hostname' \
{--no-userns,-U}'[Disable user namespace support]' \
{--no-mountns,-M}'[Disable mount namespace support]' \
{--no-netns,-N}'[Disable net namespace support]' \
{--no-ipcns,-I}'[Disable IPC namespace support]' \
{--no-utsns,-H}'[Disable UTS namespace support]' \
{--no-pidns,-P}'[Disable PID namespace support]' && rc=0
return rc

400
genindex.html Normal file
View file

@ -0,0 +1,400 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Index &mdash; pflask 0.2 documentation</title>
<link rel="stylesheet" href="_static/nature.css" type="text/css" />
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT: './',
VERSION: '0.2',
COLLAPSE_INDEX: false,
FILE_SUFFIX: '.html',
HAS_SOURCE: true
};
</script>
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/underscore.js"></script>
<script type="text/javascript" src="_static/doctools.js"></script>
<link rel="top" title="pflask 0.2 documentation" href="index.html" />
</head>
<body>
<div class="related">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="#" title="General Index"
accesskey="I">index</a></li>
<li><a href="index.html">pflask 0.2 documentation</a> &raquo;</li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body">
<h1 id="index">Index</h1>
<div class="genindex-jumpbox">
<a href="#Symbols"><strong>Symbols</strong></a>
| <a href="#P"><strong>P</strong></a>
</div>
<h2 id="Symbols">Symbols</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%" valign="top"><dl>
<dt>
-a, --attach=&lt;pid&gt;
</dt>
<dd><dl>
<dt><a href="pflask.html#cmdoption-pflask-a">pflask command line option</a>
</dt>
</dl></dd>
<dt>
-c, --chdir=&lt;dir&gt;
</dt>
<dd><dl>
<dt><a href="pflask.html#cmdoption-pflask-c">pflask command line option</a>
</dt>
</dl></dd>
<dt>
-d, --detach
</dt>
<dd><dl>
<dt><a href="pflask.html#cmdoption-pflask-d">pflask command line option</a>
</dt>
</dl></dd>
<dt>
-e, --user-map=&lt;map&gt;
</dt>
<dd><dl>
<dt><a href="pflask.html#cmdoption-pflask-e">pflask command line option</a>
</dt>
</dl></dd>
<dt>
-g, --cgroup=&lt;controller&gt;
</dt>
<dd><dl>
<dt><a href="pflask.html#cmdoption-pflask-g">pflask command line option</a>
</dt>
</dl></dd>
<dt>
-H, --no-utsns
</dt>
<dd><dl>
<dt><a href="pflask.html#cmdoption-pflask-H">pflask command line option</a>
</dt>
</dl></dd>
<dt>
-I, --no-ipcns
</dt>
<dd><dl>
<dt><a href="pflask.html#cmdoption-pflask-I">pflask command line option</a>
</dt>
</dl></dd>
<dt>
-k, --keepenv
</dt>
<dd><dl>
<dt><a href="pflask.html#cmdoption-pflask-k">pflask command line option</a>
</dt>
</dl></dd>
<dt>
-m, --mount=&lt;type&gt;:&lt;opts&gt;
</dt>
<dd><dl>
<dt><a href="pflask.html#cmdoption-pflask-m">pflask command line option</a>
</dt>
</dl></dd>
<dt>
-M, --no-mountns
</dt>
<dd><dl>
<dt><a href="pflask.html#cmdoption-pflask-M">pflask command line option</a>
</dt>
</dl></dd>
</dl></td>
<td style="width: 33%" valign="top"><dl>
<dt>
-n, --netif[=&lt;opts&gt;]
</dt>
<dd><dl>
<dt><a href="pflask.html#cmdoption-pflask-n">pflask command line option</a>
</dt>
</dl></dd>
<dt>
-N, --no-netns
</dt>
<dd><dl>
<dt><a href="pflask.html#cmdoption-pflask-N">pflask command line option</a>
</dt>
</dl></dd>
<dt>
-P, --no-pidns
</dt>
<dd><dl>
<dt><a href="pflask.html#cmdoption-pflask-P">pflask command line option</a>
</dt>
</dl></dd>
<dt>
-r, --chroot=&lt;dir&gt;
</dt>
<dd><dl>
<dt><a href="pflask.html#cmdoption-pflask-r">pflask command line option</a>
</dt>
</dl></dd>
<dt>
-s, --setenv=&lt;name&gt;=&lt;value&gt;[,&lt;name&gt;=&lt;value&gt; ...]
</dt>
<dd><dl>
<dt><a href="pflask.html#cmdoption-pflask-s">pflask command line option</a>
</dt>
</dl></dd>
<dt>
-t, --hostname
</dt>
<dd><dl>
<dt><a href="pflask.html#cmdoption-pflask-t">pflask command line option</a>
</dt>
</dl></dd>
<dt>
-U, --no-userns
</dt>
<dd><dl>
<dt><a href="pflask.html#cmdoption-pflask-U">pflask command line option</a>
</dt>
</dl></dd>
<dt>
-u, --user=&lt;user&gt;
</dt>
<dd><dl>
<dt><a href="pflask.html#cmdoption-pflask-u">pflask command line option</a>
</dt>
</dl></dd>
<dt>
-w, --ephemeral
</dt>
<dd><dl>
<dt><a href="pflask.html#cmdoption-pflask-w">pflask command line option</a>
</dt>
</dl></dd>
</dl></td>
</tr></table>
<h2 id="P">P</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%" valign="top"><dl>
<dt>
pflask command line option
</dt>
<dd><dl>
<dt><a href="pflask.html#cmdoption-pflask-H">-H, --no-utsns</a>
</dt>
<dt><a href="pflask.html#cmdoption-pflask-I">-I, --no-ipcns</a>
</dt>
<dt><a href="pflask.html#cmdoption-pflask-M">-M, --no-mountns</a>
</dt>
<dt><a href="pflask.html#cmdoption-pflask-N">-N, --no-netns</a>
</dt>
<dt><a href="pflask.html#cmdoption-pflask-P">-P, --no-pidns</a>
</dt>
<dt><a href="pflask.html#cmdoption-pflask-U">-U, --no-userns</a>
</dt>
<dt><a href="pflask.html#cmdoption-pflask-a">-a, --attach=&lt;pid&gt;</a>
</dt>
<dt><a href="pflask.html#cmdoption-pflask-c">-c, --chdir=&lt;dir&gt;</a>
</dt>
<dt><a href="pflask.html#cmdoption-pflask-d">-d, --detach</a>
</dt>
<dt><a href="pflask.html#cmdoption-pflask-e">-e, --user-map=&lt;map&gt;</a>
</dt>
<dt><a href="pflask.html#cmdoption-pflask-g">-g, --cgroup=&lt;controller&gt;</a>
</dt>
<dt><a href="pflask.html#cmdoption-pflask-k">-k, --keepenv</a>
</dt>
<dt><a href="pflask.html#cmdoption-pflask-m">-m, --mount=&lt;type&gt;:&lt;opts&gt;</a>
</dt>
<dt><a href="pflask.html#cmdoption-pflask-n">-n, --netif[=&lt;opts&gt;]</a>
</dt>
<dt><a href="pflask.html#cmdoption-pflask-r">-r, --chroot=&lt;dir&gt;</a>
</dt>
<dt><a href="pflask.html#cmdoption-pflask-s">-s, --setenv=&lt;name&gt;=&lt;value&gt;[,&lt;name&gt;=&lt;value&gt; ...]</a>
</dt>
<dt><a href="pflask.html#cmdoption-pflask-t">-t, --hostname</a>
</dt>
<dt><a href="pflask.html#cmdoption-pflask-u">-u, --user=&lt;user&gt;</a>
</dt>
<dt><a href="pflask.html#cmdoption-pflask-w">-w, --ephemeral</a>
</dt>
</dl></dd>
</dl></td>
</tr></table>
</div>
</div>
</div>
<div class="sphinxsidebar">
<div class="sphinxsidebarwrapper">
<div id="searchbox" style="display: none">
<h3>Quick search</h3>
<form class="search" action="search.html" method="get">
<input type="text" name="q" />
<input type="submit" value="Go" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
<p class="searchtip" style="font-size: 90%">
Enter search terms or a module, class or function name.
</p>
</div>
<script type="text/javascript">$('#searchbox').show(0);</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="related">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="#" title="General Index"
>index</a></li>
<li><a href="index.html">pflask 0.2 documentation</a> &raquo;</li>
</ul>
</div>
<div class="footer">
&copy; Copyright 2013, Alessandro Ghedini.
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.2.3.
</div>
</body>
</html>

337
index.html Normal file
View file

@ -0,0 +1,337 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>pflask &mdash; pflask 0.2 documentation</title>
<link rel="stylesheet" href="_static/nature.css" type="text/css" />
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT: './',
VERSION: '0.2',
COLLAPSE_INDEX: false,
FILE_SUFFIX: '.html',
HAS_SOURCE: true
};
</script>
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/underscore.js"></script>
<script type="text/javascript" src="_static/doctools.js"></script>
<link rel="top" title="pflask 0.2 documentation" href="#" />
</head>
<body>
<div class="related">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
accesskey="I">index</a></li>
<li><a href="#">pflask 0.2 documentation</a> &raquo;</li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body">
<div class="section" id="pflask">
<h1>pflask<a class="headerlink" href="#pflask" title="Permalink to this headline"></a></h1>
<a class="reference external image-reference" href="https://travis-ci.org/ghedo/pflask"><img alt="https://travis-ci.org/ghedo/pflask.png" src="https://travis-ci.org/ghedo/pflask.png" /></a>
<p><a class="reference external" href="https://ghedo.github.io/pflask">pflask</a> is a simple tool for creating process containers on LInux. It can be
used for running single commands or even booting a whole operating system
inside an isolated environment, where the filesystem hierarchy, networking,
process tree, IPC subsystems and host/domain name can be insulated from the
host system and other containers.</p>
<div class="section" id="getting-started">
<h2>Getting Started<a class="headerlink" href="#getting-started" title="Permalink to this headline"></a></h2>
<p>pflask doesn&#8217;t need any configuration and can be run without any arguments
as follows:</p>
<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>sudo pflask
</pre></div>
</div>
<p>By default a new container will be created and a bash shell will be started,
but a custom command can also be specified:</p>
<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>sudo pflask -- id
<span class="nv">uid</span><span class="o">=</span>0<span class="o">(</span>root<span class="o">)</span> <span class="nv">gid</span><span class="o">=</span>0<span class="o">(</span>root<span class="o">)</span> <span class="nv">gruppi</span><span class="o">=</span>0<span class="o">(</span>root<span class="o">)</span>
</pre></div>
</div>
<p>The container can also be run inside a private root directory by using the
<tt class="docutils literal"><span class="pre">--chroot</span></tt> option:</p>
<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>sudo pflask --chroot<span class="o">=</span>/path/to/rootfs -- id
<span class="nv">uid</span><span class="o">=</span>0<span class="o">(</span>root<span class="o">)</span> <span class="nv">gid</span><span class="o">=</span>0<span class="o">(</span>root<span class="o">)</span> <span class="nv">gruppi</span><span class="o">=</span>0<span class="o">(</span>root<span class="o">)</span>
</pre></div>
</div>
<p>This can be used, for example, as a replacement for the <tt class="docutils literal"><span class="pre">chroot(8)</span></tt> command.
It&#8217;s even possible to invoke the init binary and boot the whole operating
system inside the container:</p>
<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>sudo pflask --chroot<span class="o">=</span>/path/to/rootfs -- /sbin/init
</pre></div>
</div>
<p>Note that pflask doesn&#8217;t provide any support for creating the rootfs, but can
piggyback on existing tools. For example the <tt class="docutils literal"><span class="pre">debootstrap(8)</span></tt> command can be
used for creating a Debian rootfs as follows:</p>
<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>sudo debootstrap sid /path/to/rootfs http://httpredir.debian.org/debian
</pre></div>
</div>
<p>For more information on pflask usage, have a look at the <a class="reference external" href="https://ghedo.github.io/pflask/pflask.html">man page</a>.</p>
<div class="section" id="networking">
<h3>Networking<a class="headerlink" href="#networking" title="Permalink to this headline"></a></h3>
<p>Using the <tt class="docutils literal"><span class="pre">--netif</span></tt> option the networking of the container will be
disconnected from the host system and all network interfaces will be made
unavailable to the container:</p>
<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>sudo pflask --netif -- ip link
1: lo: &lt;LOOPBACK,UP,LOWER_UP&gt; mtu <span class="m">65536</span> qdisc noqueue state UNKNOWN mode DEFAULT group default
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
</pre></div>
</div>
<p>The <tt class="docutils literal"><span class="pre">--netif</span></tt> option can also be used to create private network interfaces:</p>
<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>sudo pflask --netif<span class="o">=</span>macvlan:eth0:net0 -- ip link
1: lo: &lt;LOOPBACK,UP,LOWER_UP&gt; mtu <span class="m">65536</span> qdisc noqueue state UNKNOWN mode DEFAULT group default
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
5: net0@if2: &lt;BROADCAST,MULTICAST&gt; mtu <span class="m">1500</span> qdisc noop state DOWN mode DEFAULT group default
link/ether 92:e4:c2:9b:a4:75 brd ff:ff:ff:ff:ff:ff link-netnsid 0
</pre></div>
</div>
<p>Interfaces created inside the container will be automatically destroyed once
the container terminates.</p>
<p>The command above will create a new <tt class="docutils literal"><span class="pre">macvlan</span></tt> interface called <tt class="docutils literal"><span class="pre">net0</span></tt>, from
the <tt class="docutils literal"><span class="pre">eth0</span></tt> host interface. <tt class="docutils literal"><span class="pre">macvlan</span></tt> interfaces can be used to give an
additional MAC address to a network adapter and make it look like a completely
different device.</p>
<p>pflask can also create other <a class="reference external" href="https://ghedo.github.io/pflask/pflask.html#netif">types of network interfaces</a>, have a look at the
manpage for more information.</p>
</div>
<div class="section" id="filesystem">
<h3>Filesystem<a class="headerlink" href="#filesystem" title="Permalink to this headline"></a></h3>
<p>By default a new mount namespace is created for the container, so that
filesystems mounted inside it won&#8217;t affect the host system. The <tt class="docutils literal"><span class="pre">--mount</span></tt>
option can then be used to create new mount points before the execution of the
supplied command.</p>
<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>sudo pflask --chroot<span class="o">=</span>/path/to/rootfs --mount<span class="o">=</span><span class="nb">bind</span>:/tmp:/tmp
</pre></div>
</div>
<p>The command above will bind mount the host&#8217;s <tt class="docutils literal"><span class="pre">/tmp</span></tt> directory into the
container&#8217;s <tt class="docutils literal"><span class="pre">/tmp</span></tt>, so that files can be exchanged between them.</p>
<p>pflask can also create other <a class="reference external" href="https://ghedo.github.io/pflask/pflask.html#mount">types of mount points</a>, have a look at the
manpage for more information.</p>
<p>Additionally, using the <tt class="docutils literal"><span class="pre">--ephemeral</span></tt> option it&#8217;s possible to tell pflask to
discard any change applied to the root filesystem once the container terminates:</p>
<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>sudo pflask --chroot<span class="o">=</span>/path/to/rootfs --ephemeral -- /sbin/init
</pre></div>
</div>
<p>This can be used for example for a build environment, where dependencies can
be installed at every run on a clean rootfs, without the need to recreate the
rootfs every time.</p>
</div>
<div class="section" id="unprivileged-containers">
<h3>Unprivileged containers<a class="headerlink" href="#unprivileged-containers" title="Permalink to this headline"></a></h3>
<p>All the commands above have been executed with root privileges, but pflask can
be invoked, with some limitations, by unprivileged users as well, as long as
user namespaces are supported by the host system.</p>
<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>pflask --user<span class="o">=</span><span class="nv">$USER</span> -- id
<span class="nv">uid</span><span class="o">=</span>1000<span class="o">(</span>ghedo<span class="o">)</span> <span class="nv">gid</span><span class="o">=</span>1000<span class="o">(</span>ghedo<span class="o">)</span> <span class="nv">gruppi</span><span class="o">=</span>1000<span class="o">(</span>ghedo<span class="o">)</span>
</pre></div>
</div>
<p>For example, on recent Debian versions user namespaces are enabled, but are
restricted to the root user only. To enable them for unprivileged users run:</p>
<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>sudo sysctl kernel.unprivileged_userns_clone<span class="o">=</span>1
</pre></div>
</div>
<p>This functionality can be used to run every-day user applications such as a
web browser inside a container:</p>
<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>pflask --user<span class="o">=</span><span class="nv">$USER</span> --mount<span class="o">=</span>tmp:<span class="nv">$HOME</span> -- chromium --disable-setuid-sandbox
</pre></div>
</div>
<p>The command above uses the <tt class="docutils literal"><span class="pre">--mount</span></tt> option to create a <tt class="docutils literal"><span class="pre">tmpfs</span></tt> mount point
on the <tt class="docutils literal"><span class="pre">$HOME</span></tt> directory, so that the application (chromium in the example)
won&#8217;t be able to access the user&#8217;s private files, and any modification to the
home directory will be discarded once the container terminates.</p>
<p>The <tt class="docutils literal"><span class="pre">--chroot</span></tt> option can be used with unprivileged containers as well, but
requires some additional configuration.</p>
<p>The first step is assigning a set of additional UIDs and GIDs to the current
user (<tt class="docutils literal"><span class="pre">$USER</span></tt>). These will be used by pflask inside the container:</p>
<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>sudo usermod --add-subuids 100000-165535 <span class="nv">$USER</span>
<span class="nv">$ </span>sudo usermod --add-subgids 100000-165535 <span class="nv">$USER</span>
</pre></div>
</div>
<p>Note that the commands above require root privileges, but have to be run only
once.</p>
<p>Then any time an unprivileged <tt class="docutils literal"><span class="pre">chroot(8)</span></tt> is needed, the following command
can be run:</p>
<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>pflask --user-map<span class="o">=</span>0:100000:65536 --chroot<span class="o">=</span>/path/to/rootfs
</pre></div>
</div>
<p>Note that the <tt class="docutils literal"><span class="pre">newuidmap(1)</span></tt> and <tt class="docutils literal"><span class="pre">newgidmap(1)</span></tt> commands need to be
installed for any of this to work: on Debian/Ubuntu systems they are provided
by the <tt class="docutils literal"><span class="pre">uidmap</span></tt> package.</p>
</div>
<div class="section" id="background-containers">
<h3>Background containers<a class="headerlink" href="#background-containers" title="Permalink to this headline"></a></h3>
<p>Containers can be detached from the current terminal as soon as they are
created by using the <tt class="docutils literal"><span class="pre">--detach</span></tt> option:</p>
<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>sudo pflask --chroot<span class="o">=</span>/path/to/rootfs --detach
</pre></div>
</div>
<p>and then later reattached (even to a different terminal) with the <tt class="docutils literal"><span class="pre">--attach</span></tt>
option:</p>
<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>pidof pflask
29076
<span class="nv">$ </span>pflask --attach<span class="o">=</span>29076
</pre></div>
</div>
<p>Where <tt class="docutils literal"><span class="pre">29076</span></tt> is the PID of the detached pflask process. Once reattached, it
can be detached again by pressing <tt class="docutils literal"><span class="pre">^&#64;</span></tt> (Ctrl + &#64;).</p>
</div>
<div class="section" id="machined-integration">
<h3>machined integration<a class="headerlink" href="#machined-integration" title="Permalink to this headline"></a></h3>
<p>Containers created with pflask are automatically registered with the <a class="reference external" href="http://www.freedesktop.org/wiki/Software/systemd/machined/">machined</a>
daemon, if installed and running. The <tt class="docutils literal"><span class="pre">machinectl(1)</span></tt> command can then be
used to list and manipulate running containers.</p>
<p>Let&#8217;s create one container as follows:</p>
<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>sudo pflask --chroot<span class="o">=</span>/path/to/rootfs -- /sbin/init
</pre></div>
</div>
<p>Running containers can be listed using the <tt class="docutils literal"><span class="pre">list</span></tt> command:</p>
<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>machinectl --no-pager list
MACHINE CLASS SERVICE
pflask-19170 container pflask
<span class="m">1</span> machines listed.
</pre></div>
</div>
<p>and information regarding a single container can be retrieved with the <tt class="docutils literal"><span class="pre">show</span></tt>
command:</p>
<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>machinectl --no-pager show pflask-19170
<span class="nv">Name</span><span class="o">=</span>pflask-19170
<span class="nv">Id</span><span class="o">=</span>00000000000000000000000000000000
<span class="nv">Timestamp</span><span class="o">=</span>gio 2015-06-25 20:28:34 CEST
<span class="nv">TimestampMonotonic</span><span class="o">=</span>8860409172
<span class="nv">Service</span><span class="o">=</span>pflask
<span class="nv">Unit</span><span class="o">=</span>machine-pflask<span class="se">\x</span>5cx2d19170.scope
<span class="nv">Leader</span><span class="o">=</span>19170
<span class="nv">Class</span><span class="o">=</span>container
<span class="nv">RootDirectory</span><span class="o">=</span>/home/ghedo/local/debian
<span class="nv">State</span><span class="o">=</span>running
</pre></div>
</div>
<p>Additionally, the <tt class="docutils literal"><span class="pre">status</span></tt> command will show more information regarding the
status of the container:</p>
<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>machinectl --no-pager status pflask-19170
pflask-19170
Since: gio 2015-06-25 20:28:34 CEST<span class="p">;</span> 1min 21s ago
Leader: <span class="m">19170</span> <span class="o">(</span>systemd<span class="o">)</span>
Service: pflask<span class="p">;</span> class container
Root: /home/ghedo/local/debian
OS: Debian GNU/Linux stretch/sid
Unit: machine-pflask<span class="se">\x</span>2d19170.scope
├─19170 /lib/systemd/systemd
└─system.slice
├─systemd-journald.service
│ └─19184 /lib/systemd/systemd-journald
└─console-getty.service
└─19216 /sbin/agetty --noclear --keep-baud console <span class="m">115200</span> 3...
giu <span class="m">25</span> 20:28:34 kronk systemd<span class="o">[</span>1<span class="o">]</span>: Started Container pflask-19170.
giu <span class="m">25</span> 20:28:34 kronk systemd<span class="o">[</span>1<span class="o">]</span>: Starting Container pflask-19170.
</pre></div>
</div>
<p>One can even log into the container using the <tt class="docutils literal"><span class="pre">login</span></tt> command (note that
the dbus daemon needs to be running inside the container for this to work):</p>
<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>sudo machinectl login pflask-19170
Connected to machine pflask-19170. Press ^<span class="o">]</span> three <span class="nb">times </span>within 1s to <span class="nb">exit </span>session.
Debian GNU/Linux stretch/sid kronk pts/0
kronk login:
</pre></div>
</div>
<p>And finally the container can be terminated using either the <tt class="docutils literal"><span class="pre">poweroff</span></tt> or
<tt class="docutils literal"><span class="pre">terminate</span></tt> commands:</p>
<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>sudo machinectl poweroff pflask-19170
</pre></div>
</div>
</div>
</div>
<div class="section" id="building">
<h2>Building<a class="headerlink" href="#building" title="Permalink to this headline"></a></h2>
<p>pflask is distributed as source code. Build with:</p>
<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>./bootstrap.py
<span class="nv">$ </span>./waf configure
<span class="nv">$ </span>./waf build
</pre></div>
</div>
</div>
<div class="section" id="copyright">
<h2>Copyright<a class="headerlink" href="#copyright" title="Permalink to this headline"></a></h2>
<p>Copyright (C) 2013 Alessandro Ghedini &lt;<a class="reference external" href="mailto:alessandro&#37;&#52;&#48;ghedini&#46;me">alessandro<span>&#64;</span>ghedini<span>&#46;</span>me</a>&gt;</p>
<p>See <a class="reference external" href="https://github.com/ghedo/pflask/tree/master/COPYING">COPYING</a> for the license.</p>
</div>
</div>
</div>
</div>
</div>
<div class="sphinxsidebar">
<div class="sphinxsidebarwrapper">
<h3><a href="#">Table Of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">pflask</a><ul>
<li><a class="reference internal" href="#getting-started">Getting Started</a><ul>
<li><a class="reference internal" href="#networking">Networking</a></li>
<li><a class="reference internal" href="#filesystem">Filesystem</a></li>
<li><a class="reference internal" href="#unprivileged-containers">Unprivileged containers</a></li>
<li><a class="reference internal" href="#background-containers">Background containers</a></li>
<li><a class="reference internal" href="#machined-integration">machined integration</a></li>
</ul>
</li>
<li><a class="reference internal" href="#building">Building</a></li>
<li><a class="reference internal" href="#copyright">Copyright</a></li>
</ul>
</li>
</ul>
<h3>This Page</h3>
<ul class="this-page-menu">
<li><a href="_sources/index.txt"
rel="nofollow">Show Source</a></li>
</ul>
<div id="searchbox" style="display: none">
<h3>Quick search</h3>
<form class="search" action="search.html" method="get">
<input type="text" name="q" />
<input type="submit" value="Go" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
<p class="searchtip" style="font-size: 90%">
Enter search terms or a module, class or function name.
</p>
</div>
<script type="text/javascript">$('#searchbox').show(0);</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="related">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
>index</a></li>
<li><a href="#">pflask 0.2 documentation</a> &raquo;</li>
</ul>
</div>
<div class="footer">
&copy; Copyright 2013, Alessandro Ghedini.
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.2.3.
</div>
</body>
</html>

6
objects.inv Normal file
View file

@ -0,0 +1,6 @@
# Sphinx inventory version 2
# Project: pflask
# Version: 0.2
# The remainder of this file is compressed using zlib.
<EFBFBD>”INÄ0D÷9…%XÀÂH½åô¢QKˆ¸íß±OòÀp{B;A„aQÞë¹*þv8±\Ô}ˆÅÏv,ž­ÈÓ<C388>.Î^I§šÎzÍøÀ¹¥<sF¡š)<29>ŒC3tÈÅ G=ݸðAˆR„Ôðñ^Köäö _vôš' ÷âR§
H¡~ú _€þô¿‚þ ôñ©)“:®ˆ Õã—¤v$™(áœÇžÇ8¿~8è šˆ"ù—Ž~Ñ(¸:Y…öÊT:¾æry@è ¡¨ÉQuüÎÁɸ ŒWôv¡¬8e|¦ÞùºÐPvªZbûOihøÜïT<C3AF>U7»Ûôf¹m°Õ~™†‘ü=Vu)ñ=ýŸÔMÚ*™DZNòËß´eç§f8Š˜?G

172
pflask-debuild.html Normal file
View file

@ -0,0 +1,172 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>pflask-debuild &mdash; pflask 0.2 documentation</title>
<link rel="stylesheet" href="_static/nature.css" type="text/css" />
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT: './',
VERSION: '0.2',
COLLAPSE_INDEX: false,
FILE_SUFFIX: '.html',
HAS_SOURCE: true
};
</script>
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/underscore.js"></script>
<script type="text/javascript" src="_static/doctools.js"></script>
<link rel="top" title="pflask 0.2 documentation" href="index.html" />
</head>
<body>
<div class="related">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
accesskey="I">index</a></li>
<li><a href="index.html">pflask 0.2 documentation</a> &raquo;</li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body">
<div class="section" id="pflask-debuild">
<span id="pflask-debuild-1"></span><h1>pflask-debuild<a class="headerlink" href="#pflask-debuild" title="Permalink to this headline"></a></h1>
<div class="section" id="synopsis">
<h2>SYNOPSIS<a class="headerlink" href="#synopsis" title="Permalink to this headline"></a></h2>
<p><strong>pflask-debuild</strong></p>
</div>
<div class="section" id="description">
<h2>DESCRIPTION<a class="headerlink" href="#description" title="Permalink to this headline"></a></h2>
<p><strong>pflask-debuild</strong> is a wrapper aroung pflask that builds Debian packages
inside a Linux namespace container. It is also able to run <tt class="docutils literal"><span class="pre">lintian(1)</span></tt> on
the generated .changes file and sign the resulting package using <tt class="docutils literal"><span class="pre">debsign(1)</span></tt>.</p>
</div>
<div class="section" id="environment">
<h2>ENVIRONMENT<a class="headerlink" href="#environment" title="Permalink to this headline"></a></h2>
<div class="section" id="dist">
<h3>DIST<a class="headerlink" href="#dist" title="Permalink to this headline"></a></h3>
<p>The Debian release (e.g. <em>unstable</em>). It will be used when searching for the
base Debian install under <tt class="docutils literal"><span class="pre">/var/cache/pflask</span></tt>. By default will be read from
the <tt class="docutils literal"><span class="pre">debian/changelog</span></tt> of the package to be built.</p>
</div>
<div class="section" id="arch">
<h3>ARCH<a class="headerlink" href="#arch" title="Permalink to this headline"></a></h3>
<p>The Debian architecture (e.g. <em>amd64</em>). It will be used when searching for
the base Debian install under <tt class="docutils literal"><span class="pre">/var/cache/pflask</span></tt>. If none is specified,
<tt class="docutils literal"><span class="pre">dpkg-architecture(1)</span></tt> is used.</p>
</div>
<div class="section" id="debuild-dpkg-buildpackage-opts">
<h3>DEBUILD_DPKG_BUILDPACKAGE_OPTS<a class="headerlink" href="#debuild-dpkg-buildpackage-opts" title="Permalink to this headline"></a></h3>
<p>Options that should be passed to <tt class="docutils literal"><span class="pre">dpkg-buildpackage(1)</span></tt>.</p>
</div>
<div class="section" id="debuild-lintian">
<h3>DEBUILD_LINTIAN<a class="headerlink" href="#debuild-lintian" title="Permalink to this headline"></a></h3>
<p>If set to <em>yes</em> (default) <tt class="docutils literal"><span class="pre">lintian(1)</span></tt> will be run.</p>
</div>
<div class="section" id="debuild-lintian-opts">
<h3>DEBUILD_LINTIAN_OPTS<a class="headerlink" href="#debuild-lintian-opts" title="Permalink to this headline"></a></h3>
<p>Options that should be passed to <tt class="docutils literal"><span class="pre">lintian(1)</span></tt> (by default <em>-IE &#8211;pedantic</em>
will be used).</p>
</div>
</div>
<div class="section" id="files">
<h2>FILES<a class="headerlink" href="#files" title="Permalink to this headline"></a></h2>
<div class="section" id="devscripts">
<h3>~/.devscripts<a class="headerlink" href="#devscripts" title="Permalink to this headline"></a></h3>
<p>User-specific configuration file.</p>
</div>
<div class="section" id="etc-devscripts-conf">
<h3>/etc/devscripts.conf<a class="headerlink" href="#etc-devscripts-conf" title="Permalink to this headline"></a></h3>
<p>System-wide configuration file.</p>
</div>
</div>
<div class="section" id="author">
<h2>AUTHOR<a class="headerlink" href="#author" title="Permalink to this headline"></a></h2>
<p>Alessandro Ghedini &lt;<a class="reference external" href="mailto:alessandro&#37;&#52;&#48;ghedini&#46;me">alessandro<span>&#64;</span>ghedini<span>&#46;</span>me</a>&gt;</p>
</div>
<div class="section" id="copyright">
<h2>COPYRIGHT<a class="headerlink" href="#copyright" title="Permalink to this headline"></a></h2>
<p>Copyright (C) 2013 Alessandro Ghedini &lt;<a class="reference external" href="mailto:alessandro&#37;&#52;&#48;ghedini&#46;me">alessandro<span>&#64;</span>ghedini<span>&#46;</span>me</a>&gt;</p>
<p>This program is released under the 2 clause BSD license.</p>
</div>
</div>
</div>
</div>
</div>
<div class="sphinxsidebar">
<div class="sphinxsidebarwrapper">
<h3><a href="index.html">Table Of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">pflask-debuild</a><ul>
<li><a class="reference internal" href="#synopsis">SYNOPSIS</a></li>
<li><a class="reference internal" href="#description">DESCRIPTION</a></li>
<li><a class="reference internal" href="#environment">ENVIRONMENT</a><ul>
<li><a class="reference internal" href="#dist">DIST</a></li>
<li><a class="reference internal" href="#arch">ARCH</a></li>
<li><a class="reference internal" href="#debuild-dpkg-buildpackage-opts">DEBUILD_DPKG_BUILDPACKAGE_OPTS</a></li>
<li><a class="reference internal" href="#debuild-lintian">DEBUILD_LINTIAN</a></li>
<li><a class="reference internal" href="#debuild-lintian-opts">DEBUILD_LINTIAN_OPTS</a></li>
</ul>
</li>
<li><a class="reference internal" href="#files">FILES</a><ul>
<li><a class="reference internal" href="#devscripts">~/.devscripts</a></li>
<li><a class="reference internal" href="#etc-devscripts-conf">/etc/devscripts.conf</a></li>
</ul>
</li>
<li><a class="reference internal" href="#author">AUTHOR</a></li>
<li><a class="reference internal" href="#copyright">COPYRIGHT</a></li>
</ul>
</li>
</ul>
<h3>This Page</h3>
<ul class="this-page-menu">
<li><a href="_sources/pflask-debuild.txt"
rel="nofollow">Show Source</a></li>
</ul>
<div id="searchbox" style="display: none">
<h3>Quick search</h3>
<form class="search" action="search.html" method="get">
<input type="text" name="q" />
<input type="submit" value="Go" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
<p class="searchtip" style="font-size: 90%">
Enter search terms or a module, class or function name.
</p>
</div>
<script type="text/javascript">$('#searchbox').show(0);</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="related">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
>index</a></li>
<li><a href="index.html">pflask 0.2 documentation</a> &raquo;</li>
</ul>
</div>
<div class="footer">
&copy; Copyright 2013, Alessandro Ghedini.
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.2.3.
</div>
</body>
</html>

338
pflask.html Normal file
View file

@ -0,0 +1,338 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>pflask &mdash; pflask 0.2 documentation</title>
<link rel="stylesheet" href="_static/nature.css" type="text/css" />
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT: './',
VERSION: '0.2',
COLLAPSE_INDEX: false,
FILE_SUFFIX: '.html',
HAS_SOURCE: true
};
</script>
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/underscore.js"></script>
<script type="text/javascript" src="_static/doctools.js"></script>
<link rel="top" title="pflask 0.2 documentation" href="index.html" />
</head>
<body>
<div class="related">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
accesskey="I">index</a></li>
<li><a href="index.html">pflask 0.2 documentation</a> &raquo;</li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body">
<div class="section" id="pflask">
<span id="pflask-1"></span><h1>pflask<a class="headerlink" href="#pflask" title="Permalink to this headline"></a></h1>
<div class="section" id="synopsis">
<h2>SYNOPSIS<a class="headerlink" href="#synopsis" title="Permalink to this headline"></a></h2>
<p><strong>pflask [options] [&#8211;] [command ...]</strong></p>
</div>
<div class="section" id="description">
<h2>DESCRIPTION<a class="headerlink" href="#description" title="Permalink to this headline"></a></h2>
<p><strong>pflask</strong> is a simple tool for creating process containers on LInux. It can be
used for running single commands or even booting a whole operating system
inside an isolated environment, where the filesystem hierarchy, networking,
process tree, IPC subsystems and host/domain name can be insulated from the
host system and other containers.</p>
</div>
<div class="section" id="options">
<h2>OPTIONS<a class="headerlink" href="#options" title="Permalink to this headline"></a></h2>
<dl class="option">
<dt id="cmdoption-pflask-r">
<span id="cmdoption-pflask--chroot"></span><tt class="descname">-r</tt><tt class="descclassname"></tt><tt class="descclassname">, </tt><tt class="descname">--chroot</tt><tt class="descclassname">=&lt;dir&gt;</tt><a class="headerlink" href="#cmdoption-pflask-r" title="Permalink to this definition"></a></dt>
<dd><p>Change the root directory inside the container.</p>
</dd></dl>
<dl class="option">
<dt id="cmdoption-pflask-c">
<span id="cmdoption-pflask--chdir"></span><tt class="descname">-c</tt><tt class="descclassname"></tt><tt class="descclassname">, </tt><tt class="descname">--chdir</tt><tt class="descclassname">=&lt;dir&gt;</tt><a class="headerlink" href="#cmdoption-pflask-c" title="Permalink to this definition"></a></dt>
<dd><p>Change the current directory inside the container.</p>
</dd></dl>
<dl class="option">
<dt id="cmdoption-pflask-t">
<span id="cmdoption-pflask--hostname"></span><tt class="descname">-t</tt><tt class="descclassname"></tt><tt class="descclassname">, </tt><tt class="descname">--hostname</tt><tt class="descclassname"></tt><a class="headerlink" href="#cmdoption-pflask-t" title="Permalink to this definition"></a></dt>
<dd><p>Set the container hostname.</p>
</dd></dl>
<dl class="option">
<dt id="cmdoption-pflask-m">
<span id="cmdoption-pflask--mount"></span><tt class="descname">-m</tt><tt class="descclassname"></tt><tt class="descclassname">, </tt><tt class="descname">--mount</tt><tt class="descclassname">=&lt;type&gt;:&lt;opts&gt;</tt><a class="headerlink" href="#cmdoption-pflask-m" title="Permalink to this definition"></a></dt>
<dd><p>Create a new <em>type</em> mount point inside the container. See <a class="reference internal" href="#mount">MOUNT</a> for more
information.</p>
</dd></dl>
<dl class="option">
<dt id="cmdoption-pflask-n">
<span id="cmdoption-pflask--netif"></span><tt class="descname">-n</tt><tt class="descclassname"></tt><tt class="descclassname">, </tt><tt class="descname">--netif</tt><tt class="descclassname">[=&lt;opts&gt;]</tt><a class="headerlink" href="#cmdoption-pflask-n" title="Permalink to this definition"></a></dt>
<dd><p>Disconnect the container networking from the host. See <a class="reference internal" href="#netif">NETIF</a> for more
information.</p>
</dd></dl>
<dl class="option">
<dt id="cmdoption-pflask-u">
<span id="cmdoption-pflask--user"></span><tt class="descname">-u</tt><tt class="descclassname"></tt><tt class="descclassname">, </tt><tt class="descname">--user</tt><tt class="descclassname">=&lt;user&gt;</tt><a class="headerlink" href="#cmdoption-pflask-u" title="Permalink to this definition"></a></dt>
<dd><p>Run the command under the specified user.</p>
</dd></dl>
<dl class="option">
<dt id="cmdoption-pflask-e">
<span id="cmdoption-pflask--user-map"></span><tt class="descname">-e</tt><tt class="descclassname"></tt><tt class="descclassname">, </tt><tt class="descname">--user-map</tt><tt class="descclassname">=&lt;map&gt;</tt><a class="headerlink" href="#cmdoption-pflask-e" title="Permalink to this definition"></a></dt>
<dd><p>Map container users to host users. The <em>map</em> argument is composed of three
values separated by <tt class="docutils literal"><span class="pre">:</span></tt>: the first userid as seen in the user namespace of
the container, the first userid as seen on the host, and a range indicating
the number of consecutive ids to map.</p>
<p>Example: <tt class="docutils literal"><span class="pre">--user-map=0:100000,65536</span></tt></p>
</dd></dl>
<dl class="option">
<dt id="cmdoption-pflask-w">
<span id="cmdoption-pflask--ephemeral"></span><tt class="descname">-w</tt><tt class="descclassname"></tt><tt class="descclassname">, </tt><tt class="descname">--ephemeral</tt><tt class="descclassname"></tt><a class="headerlink" href="#cmdoption-pflask-w" title="Permalink to this definition"></a></dt>
<dd><p>Discard any change to / once the container exits. This can only be used
along with <tt class="docutils literal"><span class="pre">--chroot</span></tt> and requires support for the <a class="reference internal" href="#overlay">overlay</a> mount type.</p>
</dd></dl>
<dl class="option">
<dt id="cmdoption-pflask-g">
<span id="cmdoption-pflask--cgroup"></span><tt class="descname">-g</tt><tt class="descclassname"></tt><tt class="descclassname">, </tt><tt class="descname">--cgroup</tt><tt class="descclassname">=&lt;controller&gt;</tt><a class="headerlink" href="#cmdoption-pflask-g" title="Permalink to this definition"></a></dt>
<dd><p>Create a new cgroup in the given controller and move the container inside
it.</p>
</dd></dl>
<dl class="option">
<dt id="cmdoption-pflask-d">
<span id="cmdoption-pflask--detach"></span><tt class="descname">-d</tt><tt class="descclassname"></tt><tt class="descclassname">, </tt><tt class="descname">--detach</tt><tt class="descclassname"></tt><a class="headerlink" href="#cmdoption-pflask-d" title="Permalink to this definition"></a></dt>
<dd><p>Detach from terminal.</p>
</dd></dl>
<dl class="option">
<dt id="cmdoption-pflask-a">
<span id="cmdoption-pflask--attach"></span><tt class="descname">-a</tt><tt class="descclassname"></tt><tt class="descclassname">, </tt><tt class="descname">--attach</tt><tt class="descclassname">=&lt;pid&gt;</tt><a class="headerlink" href="#cmdoption-pflask-a" title="Permalink to this definition"></a></dt>
<dd><p>Attach to the <em>pid</em> detached process. Only a process with the same UID of
the detached process can attach to it. To detach again press <cite>^&#64;</cite> (Ctrl + &#64;).</p>
</dd></dl>
<dl class="option">
<dt id="cmdoption-pflask-s">
<span id="cmdoption-pflask--setenv"></span><tt class="descname">-s</tt><tt class="descclassname"></tt><tt class="descclassname">, </tt><tt class="descname">--setenv</tt><tt class="descclassname">=&lt;name&gt;=&lt;value&gt;[,&lt;name&gt;=&lt;value&gt; ...]</tt><a class="headerlink" href="#cmdoption-pflask-s" title="Permalink to this definition"></a></dt>
<dd><p>Set additional environment variables. It takes a comma-separated list of
variables of the form <cite>name=value</cite>. This option may be used more than once.</p>
</dd></dl>
<dl class="option">
<dt id="cmdoption-pflask-k">
<span id="cmdoption-pflask--keepenv"></span><tt class="descname">-k</tt><tt class="descclassname"></tt><tt class="descclassname">, </tt><tt class="descname">--keepenv</tt><tt class="descclassname"></tt><a class="headerlink" href="#cmdoption-pflask-k" title="Permalink to this definition"></a></dt>
<dd><p>Do not clear environment (only relevant when used with <tt class="docutils literal"><span class="pre">--chroot</span></tt>).</p>
</dd></dl>
<dl class="option">
<dt id="cmdoption-pflask-U">
<span id="cmdoption-pflask--no-userns"></span><tt class="descname">-U</tt><tt class="descclassname"></tt><tt class="descclassname">, </tt><tt class="descname">--no-userns</tt><tt class="descclassname"></tt><a class="headerlink" href="#cmdoption-pflask-U" title="Permalink to this definition"></a></dt>
<dd><p>Disable user namespace.</p>
</dd></dl>
<dl class="option">
<dt id="cmdoption-pflask-M">
<span id="cmdoption-pflask--no-mountns"></span><tt class="descname">-M</tt><tt class="descclassname"></tt><tt class="descclassname">, </tt><tt class="descname">--no-mountns</tt><tt class="descclassname"></tt><a class="headerlink" href="#cmdoption-pflask-M" title="Permalink to this definition"></a></dt>
<dd><p>Disable mount namespace.</p>
</dd></dl>
<dl class="option">
<dt id="cmdoption-pflask-N">
<span id="cmdoption-pflask--no-netns"></span><tt class="descname">-N</tt><tt class="descclassname"></tt><tt class="descclassname">, </tt><tt class="descname">--no-netns</tt><tt class="descclassname"></tt><a class="headerlink" href="#cmdoption-pflask-N" title="Permalink to this definition"></a></dt>
<dd><p>Disable net namespace.</p>
</dd></dl>
<dl class="option">
<dt id="cmdoption-pflask-I">
<span id="cmdoption-pflask--no-ipcns"></span><tt class="descname">-I</tt><tt class="descclassname"></tt><tt class="descclassname">, </tt><tt class="descname">--no-ipcns</tt><tt class="descclassname"></tt><a class="headerlink" href="#cmdoption-pflask-I" title="Permalink to this definition"></a></dt>
<dd><p>Disable IPC namespace.</p>
</dd></dl>
<dl class="option">
<dt id="cmdoption-pflask-H">
<span id="cmdoption-pflask--no-utsns"></span><tt class="descname">-H</tt><tt class="descclassname"></tt><tt class="descclassname">, </tt><tt class="descname">--no-utsns</tt><tt class="descclassname"></tt><a class="headerlink" href="#cmdoption-pflask-H" title="Permalink to this definition"></a></dt>
<dd><p>Disable UTS namespace.</p>
</dd></dl>
<dl class="option">
<dt id="cmdoption-pflask-P">
<span id="cmdoption-pflask--no-pidns"></span><tt class="descname">-P</tt><tt class="descclassname"></tt><tt class="descclassname">, </tt><tt class="descname">--no-pidns</tt><tt class="descclassname"></tt><a class="headerlink" href="#cmdoption-pflask-P" title="Permalink to this definition"></a></dt>
<dd><p>Disable PID namespace.</p>
</dd></dl>
</div>
<div class="section" id="mount">
<h2>MOUNT<a class="headerlink" href="#mount" title="Permalink to this headline"></a></h2>
<p>pflask can create the following mount point types using the <tt class="docutils literal"><span class="pre">--mount</span></tt> option:</p>
<div class="section" id="bind">
<h3>bind<a class="headerlink" href="#bind" title="Permalink to this headline"></a></h3>
<p><tt class="docutils literal"><span class="pre">--mount=bind:&lt;host_path&gt;:&lt;container_path&gt;</span></tt></p>
<p>Bind mounts the <em>host_path</em> directory/file on the host filesystem to the
<em>container_path</em> directory/file in the container. If the <tt class="docutils literal"><span class="pre">--chroot</span></tt> option is
used, the destination path will be relative to the chroot directory.</p>
<p>Example: <tt class="docutils literal"><span class="pre">--mount=bind:/source/path:/dest/path</span></tt></p>
</div>
<div class="section" id="bind-ro">
<h3>bind-ro<a class="headerlink" href="#bind-ro" title="Permalink to this headline"></a></h3>
<p><tt class="docutils literal"><span class="pre">--mount=bind-ro:&lt;host_path&gt;:&lt;container_path&gt;</span></tt></p>
<p>Same as <tt class="docutils literal"><span class="pre">bind</span></tt>, but makes the mount point read-only. If the <tt class="docutils literal"><span class="pre">--chroot</span></tt>
option is used, the destination path will be relative to the chroot directory.</p>
<p>Example: <tt class="docutils literal"><span class="pre">--mount=bind-ro:/source/path:/dest/path</span></tt></p>
</div>
<div class="section" id="overlay">
<h3>overlay<a class="headerlink" href="#overlay" title="Permalink to this headline"></a></h3>
<p><tt class="docutils literal"><span class="pre">--mount=overla:&lt;root_dir&gt;:&lt;dest&gt;:&lt;work_dir&gt;</span></tt></p>
<p>Stacks the host <em>root_dir</em> directory on top of the container&#8217;s <em>dest</em> directory
using either AuFS or OverlayFS depending on what is found at compile-time. If
the <tt class="docutils literal"><span class="pre">--chroot</span></tt> option is used, the destination path will be relative to the
chroot directory. The <em>work_dir</em> directory needs to be an empty directory on
the same filesystem as <em>root_dir</em>.</p>
<p>Note that AuFS and OverlayFS don&#8217;t support user namespaces, so the <tt class="docutils literal"><span class="pre">--user</span></tt>
option is incompatible with this mount type unless <tt class="docutils literal"><span class="pre">--no-userns</span></tt> is also used.</p>
<p>Example: <tt class="docutils literal"><span class="pre">--mount=overlay:/overlay/path:/dest/path:/overlay/work</span></tt></p>
</div>
<div class="section" id="tmp">
<h3>tmp<a class="headerlink" href="#tmp" title="Permalink to this headline"></a></h3>
<p><tt class="docutils literal"><span class="pre">--mount=tmp:&lt;dest&gt;</span></tt></p>
<p>Mounts a temporary in-memory filesystem on the <em>dest</em> directory inside the
container.</p>
<p>Example: <tt class="docutils literal"><span class="pre">--mount=tmp:/dest/path</span></tt></p>
</div>
</div>
<div class="section" id="netif">
<h2>NETIF<a class="headerlink" href="#netif" title="Permalink to this headline"></a></h2>
<p>pflask will create a new network namespace when the <tt class="docutils literal"><span class="pre">--netif</span></tt> option is used.
If one of the following arguments is provided, a network interface will also be
created inside the container:</p>
<div class="section" id="move-and-rename">
<h3>move and rename<a class="headerlink" href="#move-and-rename" title="Permalink to this headline"></a></h3>
<p><tt class="docutils literal"><span class="pre">--netif=&lt;dev&gt;:&lt;name&gt;</span></tt></p>
<p>Moves the <em>dev</em> network interface from the host to the container, and renames
it to <em>name</em>. No additional configuration will be applied to it.</p>
<p>Example: <tt class="docutils literal"><span class="pre">--netif=vxlan0:eth0</span></tt></p>
</div>
<div class="section" id="macvlan">
<h3>macvlan<a class="headerlink" href="#macvlan" title="Permalink to this headline"></a></h3>
<p><tt class="docutils literal"><span class="pre">--netif=macvlan:&lt;master&gt;:&lt;name&gt;</span></tt></p>
<p>Creates a <tt class="docutils literal"><span class="pre">macvlan</span></tt> network interface using <em>master</em> as master interface,
moves it inside the container and renames it to <em>name</em>. No additional
configuration will be applied to it.</p>
<p>Example: <tt class="docutils literal"><span class="pre">--netif=macvlan:eth0:eth0</span></tt></p>
</div>
<div class="section" id="ipvlan">
<h3>ipvlan<a class="headerlink" href="#ipvlan" title="Permalink to this headline"></a></h3>
<p><tt class="docutils literal"><span class="pre">--netif=ipvlan:&lt;master&gt;:&lt;name&gt;</span></tt></p>
<p>Same as <tt class="docutils literal"><span class="pre">macvlan</span></tt> but an <tt class="docutils literal"><span class="pre">ipvlan</span></tt> interface will be created instead. No
additional configuration will be applied to it.</p>
<p>Example: <tt class="docutils literal"><span class="pre">--netif=ipvlan:eth0:eth0</span></tt></p>
</div>
<div class="section" id="veth">
<h3>veth<a class="headerlink" href="#veth" title="Permalink to this headline"></a></h3>
<p><tt class="docutils literal"><span class="pre">--netif=veth:&lt;name_outside&gt;:&lt;name_inside&gt;</span></tt></p>
<p>Creates a pair of <tt class="docutils literal"><span class="pre">veth</span></tt> network interfaces called <em>name_outside</em> and
<em>name_inside</em>. The <em>name_inside</em> twin will then be moved inside the container.
No additional configuration will be applied to them.</p>
<p>Example: <tt class="docutils literal"><span class="pre">--netif=veth:veth0:eth0</span></tt></p>
</div>
</div>
<div class="section" id="author">
<h2>AUTHOR<a class="headerlink" href="#author" title="Permalink to this headline"></a></h2>
<p>Alessandro Ghedini &lt;<a class="reference external" href="mailto:alessandro&#37;&#52;&#48;ghedini&#46;me">alessandro<span>&#64;</span>ghedini<span>&#46;</span>me</a>&gt;</p>
</div>
<div class="section" id="copyright">
<h2>COPYRIGHT<a class="headerlink" href="#copyright" title="Permalink to this headline"></a></h2>
<p>Copyright (C) 2013 Alessandro Ghedini &lt;<a class="reference external" href="mailto:alessandro&#37;&#52;&#48;ghedini&#46;me">alessandro<span>&#64;</span>ghedini<span>&#46;</span>me</a>&gt;</p>
<p>This program is released under the 2 clause BSD license.</p>
</div>
</div>
</div>
</div>
</div>
<div class="sphinxsidebar">
<div class="sphinxsidebarwrapper">
<h3><a href="index.html">Table Of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">pflask</a><ul>
<li><a class="reference internal" href="#synopsis">SYNOPSIS</a></li>
<li><a class="reference internal" href="#description">DESCRIPTION</a></li>
<li><a class="reference internal" href="#options">OPTIONS</a></li>
<li><a class="reference internal" href="#mount">MOUNT</a><ul>
<li><a class="reference internal" href="#bind">bind</a></li>
<li><a class="reference internal" href="#bind-ro">bind-ro</a></li>
<li><a class="reference internal" href="#overlay">overlay</a></li>
<li><a class="reference internal" href="#tmp">tmp</a></li>
</ul>
</li>
<li><a class="reference internal" href="#netif">NETIF</a><ul>
<li><a class="reference internal" href="#move-and-rename">move and rename</a></li>
<li><a class="reference internal" href="#macvlan">macvlan</a></li>
<li><a class="reference internal" href="#ipvlan">ipvlan</a></li>
<li><a class="reference internal" href="#veth">veth</a></li>
</ul>
</li>
<li><a class="reference internal" href="#author">AUTHOR</a></li>
<li><a class="reference internal" href="#copyright">COPYRIGHT</a></li>
</ul>
</li>
</ul>
<h3>This Page</h3>
<ul class="this-page-menu">
<li><a href="_sources/pflask.txt"
rel="nofollow">Show Source</a></li>
</ul>
<div id="searchbox" style="display: none">
<h3>Quick search</h3>
<form class="search" action="search.html" method="get">
<input type="text" name="q" />
<input type="submit" value="Go" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
<p class="searchtip" style="font-size: 90%">
Enter search terms or a module, class or function name.
</p>
</div>
<script type="text/javascript">$('#searchbox').show(0);</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="related">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
>index</a></li>
<li><a href="index.html">pflask 0.2 documentation</a> &raquo;</li>
</ul>
</div>
<div class="footer">
&copy; Copyright 2013, Alessandro Ghedini.
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.2.3.
</div>
</body>
</html>

99
search.html Normal file
View file

@ -0,0 +1,99 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Search &mdash; pflask 0.2 documentation</title>
<link rel="stylesheet" href="_static/nature.css" type="text/css" />
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT: './',
VERSION: '0.2',
COLLAPSE_INDEX: false,
FILE_SUFFIX: '.html',
HAS_SOURCE: true
};
</script>
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/underscore.js"></script>
<script type="text/javascript" src="_static/doctools.js"></script>
<script type="text/javascript" src="_static/searchtools.js"></script>
<link rel="top" title="pflask 0.2 documentation" href="index.html" />
<script type="text/javascript">
jQuery(function() { Search.loadIndex("searchindex.js"); });
</script>
<script type="text/javascript" id="searchindexloader"></script>
</head>
<body>
<div class="related">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
accesskey="I">index</a></li>
<li><a href="index.html">pflask 0.2 documentation</a> &raquo;</li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body">
<h1 id="search-documentation">Search</h1>
<div id="fallback" class="admonition warning">
<script type="text/javascript">$('#fallback').hide();</script>
<p>
Please activate JavaScript to enable the search
functionality.
</p>
</div>
<p>
From here you can search these documents. Enter your search
words into the box below and click "search". Note that the search
function will automatically search for all of the words. Pages
containing fewer words won't appear in the result list.
</p>
<form action="" method="get">
<input type="text" name="q" value="" />
<input type="submit" value="search" />
<span id="search-progress" style="padding-left: 10px"></span>
</form>
<div id="search-results">
</div>
</div>
</div>
</div>
<div class="sphinxsidebar">
<div class="sphinxsidebarwrapper">
</div>
</div>
<div class="clearer"></div>
</div>
<div class="related">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
>index</a></li>
<li><a href="index.html">pflask 0.2 documentation</a> &raquo;</li>
</ul>
</div>
<div class="footer">
&copy; Copyright 2013, Alessandro Ghedini.
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.2.3.
</div>
</body>
</html>

1
searchindex.js Normal file

File diff suppressed because one or more lines are too long

View file

@ -1,111 +0,0 @@
/*
* The process in the flask.
*
* Copyright (c) 2013, gdm85
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <string.h>
#include <cap-ng.h>
#include "ut/utlist.h"
#include "printf.h"
#include "util.h"
struct capability {
capng_act_t action;
int capability;
struct capability *next, *prev;
};
void capability_add(struct capability **caps, char *cap) {
int rc;
struct capability *c = malloc(sizeof(struct capability));
fail_if(!c, "OOM");
fail_if(!cap[0], "Invalid empty capability name");
switch (cap[0]) {
case '+':
c->action = CAPNG_ADD;
cap++;
break;
case '-':
c->action = CAPNG_DROP;
cap++;
break;
default:
c->action = CAPNG_ADD;
break;
}
if (!strcasecmp(cap, "all")) {
/* *caps == NULL means that the list is empty */
fail_if(*caps, "Alias 'all' is valid only as first capability");
c->capability = -1;
} else {
rc = capng_name_to_capability(cap);
fail_if(rc < 0, "Invalid capability name: '%s'", cap);
c->capability = rc;
}
DL_APPEND(*caps, c);
}
void setup_capabilities(struct capability *caps) {
int rc;
struct capability *i = NULL;
capng_get_caps_process();
DL_FOREACH(caps, i) {
if (i->capability < 0) {
if (i->action == CAPNG_DROP)
capng_clear(CAPNG_SELECT_BOTH);
continue;
}
rc = capng_update(i->action, CAPNG_EFFECTIVE |
CAPNG_PERMITTED |
CAPNG_INHERITABLE |
CAPNG_BOUNDING_SET,
i->capability);
fail_if(rc != 0, "Error updating capabilities");
}
rc = capng_apply(CAPNG_SELECT_BOTH);
fail_if(rc != 0, "Error applying capabilities");
}

View file

@ -1,34 +0,0 @@
/*
* The process in the flask.
*
* Copyright (c) 2015, gdm85
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
struct capability;
void capability_add(struct capability **caps, char *cap);
void setup_capabilities(struct capability *caps);

View file

@ -1,128 +0,0 @@
/*
* The process in the flask.
*
* Copyright (c) 2013, Alessandro Ghedini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include "ut/utlist.h"
#include "printf.h"
#include "util.h"
#define CGROUP_BASE "/sys/fs/cgroup"
struct cgroup {
char *controller;
char *name;
struct cgroup *next, *prev;
};
static void create_cgroup(const char *controller, const char *name);
static void attach_cgroup(const char *controller, const char *name, pid_t pid);
static void destroy_cgroup(const char *controller, const char *name);
void cgroup_add(struct cgroup **groups, char *controller) {
int rc;
pid_t pid = getpid();
struct cgroup *cg = malloc(sizeof(struct cgroup));
fail_if(!cg, "OOM");
cg->controller = strdup(controller);
rc = asprintf(&cg->name, "pflask.%d", pid);
fail_if(rc < 0, "OOM");
DL_APPEND(*groups, cg);
}
void setup_cgroup(struct cgroup *groups, pid_t pid) {
struct cgroup *i = NULL;
DL_FOREACH(groups, i) {
create_cgroup(i->controller, i->name);
attach_cgroup(i->controller, i->name, pid);
}
}
void clean_cgroup(struct cgroup *groups) {
struct cgroup *i = NULL;
DL_FOREACH(groups, i) {
destroy_cgroup(i->controller, i->name);
}
}
static void create_cgroup(const char *controller, const char *name) {
int rc;
_free_ char *path = NULL;
rc = asprintf(&path, CGROUP_BASE "/%s/%s", controller, name);
fail_if(rc < 0, "OOM");
rc = mkdir(path, 0755);
sys_fail_if((rc < 0) && (errno != EEXIST), "Error creating cgroup");
}
static void attach_cgroup(const char *controller, const char *name, pid_t pid) {
int rc;
FILE *tasks = NULL;
_free_ char *path = NULL;
rc = asprintf(&path, CGROUP_BASE "/%s/%s/tasks", controller, name);
fail_if(rc < 0, "OOM");
tasks = fopen(path, "w");
sys_fail_if(!tasks, "Error opening cgroup");
fprintf(tasks, "%d\n", pid);
rc = fclose(tasks);
sys_fail_if(rc < 0, "Error closing cgroup");
}
static void destroy_cgroup(const char *controller, const char *name) {
int rc;
_free_ char *path = NULL;
rc = asprintf(&path, CGROUP_BASE "/%s/%s", controller, name);
fail_if(rc < 0, "OOM");
rc = rmdir(path);
sys_fail_if(rc < 0, "Error destroying cgroup");
}

View file

@ -1,36 +0,0 @@
/*
* The process in the flask.
*
* Copyright (c) 2013, Alessandro Ghedini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
struct cgroup;
void cgroup_add(struct cgroup **groups, char *controller);
void setup_cgroup(struct cgroup *groups, pid_t pid);
void clean_cgroup(struct cgroup *groups);

File diff suppressed because it is too large Load diff

View file

@ -1,44 +0,0 @@
package "pflask"
version "0.2"
option "chroot" r "Change the root directory inside the container"
string optional
option "chdir" c "Change the current directory inside the container"
string optional
option "hostname" t "Set the container hostname"
string optional
option "mount" m "Create a new mount point inside the container"
string optional multiple
option "netif" n "Disconnect the container networking from the host"
string optional argoptional multiple
option "user" u "Run the command under the specified user"
string default="root" optional
option "user-map" e "Map container users to host users"
string optional multiple
option "ephemeral" w "Discard changes to /"
flag off dependon="chroot"
option "cgroup" g "Create a new cgroup and move the container inside it"
string optional multiple
option "caps" b "Change the effective capabilities inside the container"
string default="+all" optional multiple
option "detach" d "Detach from terminal"
flag off
option "attach" a "Attach to the specified detached process"
int optional
option "setenv" s "Set additional environment variables"
string optional multiple
option "keepenv" k "Do not clear environment"
flag off
option "no-userns" U "Disable user namespace support"
flag off
option "no-mountns" M "Disable mount namespace support"
flag off
option "no-netns" N "Disable net namespace support"
flag off
option "no-ipcns" I "Disable IPC namespace support"
flag off
option "no-utsns" H "Disable UTS namespace support"
flag off
option "no-pidns" P "Disable PID namespace support"
flag off

View file

@ -1,256 +0,0 @@
/** @file cmdline.h
* @brief The header file for the command line option parser
* generated by GNU Gengetopt version 2.22.6
* http://www.gnu.org/software/gengetopt.
* DO NOT modify this file, since it can be overwritten
* @author GNU Gengetopt by Lorenzo Bettini */
#ifndef CMDLINE_H
#define CMDLINE_H
/* If we use autoconf. */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h> /* for FILE */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#ifndef CMDLINE_PARSER_PACKAGE
/** @brief the program name (used for printing errors) */
#define CMDLINE_PARSER_PACKAGE "pflask"
#endif
#ifndef CMDLINE_PARSER_PACKAGE_NAME
/** @brief the complete program name (used for help and version) */
#define CMDLINE_PARSER_PACKAGE_NAME "pflask"
#endif
#ifndef CMDLINE_PARSER_VERSION
/** @brief the program version */
#define CMDLINE_PARSER_VERSION "0.2"
#endif
/** @brief Where the command line options are stored */
struct gengetopt_args_info
{
const char *help_help; /**< @brief Print help and exit help description. */
const char *version_help; /**< @brief Print version and exit help description. */
char * chroot_arg; /**< @brief Change the root directory inside the container. */
char * chroot_orig; /**< @brief Change the root directory inside the container original value given at command line. */
const char *chroot_help; /**< @brief Change the root directory inside the container help description. */
char * chdir_arg; /**< @brief Change the current directory inside the container. */
char * chdir_orig; /**< @brief Change the current directory inside the container original value given at command line. */
const char *chdir_help; /**< @brief Change the current directory inside the container help description. */
char * hostname_arg; /**< @brief Set the container hostname. */
char * hostname_orig; /**< @brief Set the container hostname original value given at command line. */
const char *hostname_help; /**< @brief Set the container hostname help description. */
char ** mount_arg; /**< @brief Create a new mount point inside the container. */
char ** mount_orig; /**< @brief Create a new mount point inside the container original value given at command line. */
unsigned int mount_min; /**< @brief Create a new mount point inside the container's minimum occurreces */
unsigned int mount_max; /**< @brief Create a new mount point inside the container's maximum occurreces */
const char *mount_help; /**< @brief Create a new mount point inside the container help description. */
char ** netif_arg; /**< @brief Disconnect the container networking from the host. */
char ** netif_orig; /**< @brief Disconnect the container networking from the host original value given at command line. */
unsigned int netif_min; /**< @brief Disconnect the container networking from the host's minimum occurreces */
unsigned int netif_max; /**< @brief Disconnect the container networking from the host's maximum occurreces */
const char *netif_help; /**< @brief Disconnect the container networking from the host help description. */
char * user_arg; /**< @brief Run the command under the specified user (default='root'). */
char * user_orig; /**< @brief Run the command under the specified user original value given at command line. */
const char *user_help; /**< @brief Run the command under the specified user help description. */
char ** user_map_arg; /**< @brief Map container users to host users. */
char ** user_map_orig; /**< @brief Map container users to host users original value given at command line. */
unsigned int user_map_min; /**< @brief Map container users to host users's minimum occurreces */
unsigned int user_map_max; /**< @brief Map container users to host users's maximum occurreces */
const char *user_map_help; /**< @brief Map container users to host users help description. */
int ephemeral_flag; /**< @brief Discard changes to / (default=off). */
const char *ephemeral_help; /**< @brief Discard changes to / help description. */
char ** cgroup_arg; /**< @brief Create a new cgroup and move the container inside it. */
char ** cgroup_orig; /**< @brief Create a new cgroup and move the container inside it original value given at command line. */
unsigned int cgroup_min; /**< @brief Create a new cgroup and move the container inside it's minimum occurreces */
unsigned int cgroup_max; /**< @brief Create a new cgroup and move the container inside it's maximum occurreces */
const char *cgroup_help; /**< @brief Create a new cgroup and move the container inside it help description. */
char ** caps_arg; /**< @brief Change the effective capabilities inside the container (default='+all'). */
char ** caps_orig; /**< @brief Change the effective capabilities inside the container original value given at command line. */
unsigned int caps_min; /**< @brief Change the effective capabilities inside the container's minimum occurreces */
unsigned int caps_max; /**< @brief Change the effective capabilities inside the container's maximum occurreces */
const char *caps_help; /**< @brief Change the effective capabilities inside the container help description. */
int detach_flag; /**< @brief Detach from terminal (default=off). */
const char *detach_help; /**< @brief Detach from terminal help description. */
int attach_arg; /**< @brief Attach to the specified detached process. */
char * attach_orig; /**< @brief Attach to the specified detached process original value given at command line. */
const char *attach_help; /**< @brief Attach to the specified detached process help description. */
char ** setenv_arg; /**< @brief Set additional environment variables. */
char ** setenv_orig; /**< @brief Set additional environment variables original value given at command line. */
unsigned int setenv_min; /**< @brief Set additional environment variables's minimum occurreces */
unsigned int setenv_max; /**< @brief Set additional environment variables's maximum occurreces */
const char *setenv_help; /**< @brief Set additional environment variables help description. */
int keepenv_flag; /**< @brief Do not clear environment (default=off). */
const char *keepenv_help; /**< @brief Do not clear environment help description. */
int no_userns_flag; /**< @brief Disable user namespace support (default=off). */
const char *no_userns_help; /**< @brief Disable user namespace support help description. */
int no_mountns_flag; /**< @brief Disable mount namespace support (default=off). */
const char *no_mountns_help; /**< @brief Disable mount namespace support help description. */
int no_netns_flag; /**< @brief Disable net namespace support (default=off). */
const char *no_netns_help; /**< @brief Disable net namespace support help description. */
int no_ipcns_flag; /**< @brief Disable IPC namespace support (default=off). */
const char *no_ipcns_help; /**< @brief Disable IPC namespace support help description. */
int no_utsns_flag; /**< @brief Disable UTS namespace support (default=off). */
const char *no_utsns_help; /**< @brief Disable UTS namespace support help description. */
int no_pidns_flag; /**< @brief Disable PID namespace support (default=off). */
const char *no_pidns_help; /**< @brief Disable PID namespace support help description. */
unsigned int help_given ; /**< @brief Whether help was given. */
unsigned int version_given ; /**< @brief Whether version was given. */
unsigned int chroot_given ; /**< @brief Whether chroot was given. */
unsigned int chdir_given ; /**< @brief Whether chdir was given. */
unsigned int hostname_given ; /**< @brief Whether hostname was given. */
unsigned int mount_given ; /**< @brief Whether mount was given. */
unsigned int netif_given ; /**< @brief Whether netif was given. */
unsigned int user_given ; /**< @brief Whether user was given. */
unsigned int user_map_given ; /**< @brief Whether user-map was given. */
unsigned int ephemeral_given ; /**< @brief Whether ephemeral was given. */
unsigned int cgroup_given ; /**< @brief Whether cgroup was given. */
unsigned int caps_given ; /**< @brief Whether caps was given. */
unsigned int detach_given ; /**< @brief Whether detach was given. */
unsigned int attach_given ; /**< @brief Whether attach was given. */
unsigned int setenv_given ; /**< @brief Whether setenv was given. */
unsigned int keepenv_given ; /**< @brief Whether keepenv was given. */
unsigned int no_userns_given ; /**< @brief Whether no-userns was given. */
unsigned int no_mountns_given ; /**< @brief Whether no-mountns was given. */
unsigned int no_netns_given ; /**< @brief Whether no-netns was given. */
unsigned int no_ipcns_given ; /**< @brief Whether no-ipcns was given. */
unsigned int no_utsns_given ; /**< @brief Whether no-utsns was given. */
unsigned int no_pidns_given ; /**< @brief Whether no-pidns was given. */
} ;
/** @brief The additional parameters to pass to parser functions */
struct cmdline_parser_params
{
int override; /**< @brief whether to override possibly already present options (default 0) */
int initialize; /**< @brief whether to initialize the option structure gengetopt_args_info (default 1) */
int check_required; /**< @brief whether to check that all required options were provided (default 1) */
int check_ambiguity; /**< @brief whether to check for options already specified in the option structure gengetopt_args_info (default 0) */
int print_errors; /**< @brief whether getopt_long should print an error message for a bad option (default 1) */
} ;
/** @brief the purpose string of the program */
extern const char *gengetopt_args_info_purpose;
/** @brief the usage string of the program */
extern const char *gengetopt_args_info_usage;
/** @brief the description string of the program */
extern const char *gengetopt_args_info_description;
/** @brief all the lines making the help output */
extern const char *gengetopt_args_info_help[];
/**
* The command line parser
* @param argc the number of command line options
* @param argv the command line options
* @param args_info the structure where option information will be stored
* @return 0 if everything went fine, NON 0 if an error took place
*/
int cmdline_parser (int argc, char **argv,
struct gengetopt_args_info *args_info);
/**
* The command line parser (version with additional parameters - deprecated)
* @param argc the number of command line options
* @param argv the command line options
* @param args_info the structure where option information will be stored
* @param override whether to override possibly already present options
* @param initialize whether to initialize the option structure my_args_info
* @param check_required whether to check that all required options were provided
* @return 0 if everything went fine, NON 0 if an error took place
* @deprecated use cmdline_parser_ext() instead
*/
int cmdline_parser2 (int argc, char **argv,
struct gengetopt_args_info *args_info,
int override, int initialize, int check_required);
/**
* The command line parser (version with additional parameters)
* @param argc the number of command line options
* @param argv the command line options
* @param args_info the structure where option information will be stored
* @param params additional parameters for the parser
* @return 0 if everything went fine, NON 0 if an error took place
*/
int cmdline_parser_ext (int argc, char **argv,
struct gengetopt_args_info *args_info,
struct cmdline_parser_params *params);
/**
* Save the contents of the option struct into an already open FILE stream.
* @param outfile the stream where to dump options
* @param args_info the option struct to dump
* @return 0 if everything went fine, NON 0 if an error took place
*/
int cmdline_parser_dump(FILE *outfile,
struct gengetopt_args_info *args_info);
/**
* Save the contents of the option struct into a (text) file.
* This file can be read by the config file parser (if generated by gengetopt)
* @param filename the file where to save
* @param args_info the option struct to save
* @return 0 if everything went fine, NON 0 if an error took place
*/
int cmdline_parser_file_save(const char *filename,
struct gengetopt_args_info *args_info);
/**
* Print the help
*/
void cmdline_parser_print_help(void);
/**
* Print the version
*/
void cmdline_parser_print_version(void);
/**
* Initializes all the fields a cmdline_parser_params structure
* to their default values
* @param params the structure to initialize
*/
void cmdline_parser_params_init(struct cmdline_parser_params *params);
/**
* Allocates dynamically a cmdline_parser_params structure and initializes
* all its fields to their default values
* @return the created and initialized cmdline_parser_params structure
*/
struct cmdline_parser_params *cmdline_parser_params_create(void);
/**
* Initializes the passed gengetopt_args_info structure's fields
* (also set default values for options that have a default)
* @param args_info the structure to initialize
*/
void cmdline_parser_init (struct gengetopt_args_info *args_info);
/**
* Deallocates the string fields of the gengetopt_args_info structure
* (but does not deallocate the structure itself)
* @param args_info the structure to deallocate
*/
void cmdline_parser_free (struct gengetopt_args_info *args_info);
/**
* Checks that all the required options were specified
* @param args_info the structure to check
* @param prog_name the name of the program that will be used to print
* possible errors
* @return
*/
int cmdline_parser_required (struct gengetopt_args_info *args_info,
const char *prog_name);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* CMDLINE_H */

219
src/dev.c
View file

@ -1,219 +0,0 @@
/*
* The process in the flask.
*
* Copyright (c) 2013, Alessandro Ghedini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdbool.h>
#include <fcntl.h>
#include <unistd.h>
#include <sched.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include "user.h"
#include "sync.h"
#include "printf.h"
#include "util.h"
void setup_ptmx(const char *dest) {
int rc;
_free_ char *target = NULL;
rc = asprintf(&target, "%s/dev/ptmx", dest);
fail_if(rc < 0, "OOM");
rc = symlink("/dev/pts/ptmx", target);
sys_fail_if(rc < 0, "Error creating symlink '%s'", target);
}
void setup_console_owner(char *path, struct user *u) {
int rc;
pid_t pid;
int sync[2];
struct stat sb;
uid_t hostuid = geteuid();
gid_t hostgid = getegid();
uid_t rootuid;
gid_t rootgid;
struct user *users = NULL;
unsigned int tmp;
if (!user_get_mapped_root(u, 'u', &tmp))
fail_printf("No mapping for container root user");
rootuid = (uid_t) tmp;
if (!user_get_mapped_root(u, 'g', &tmp))
fail_printf("No mapping for container root group");
rootgid = (uid_t) tmp;
if (!geteuid()) {
rc = chown(path, rootuid, rootgid);
sys_fail_if(rc < 0, "Error chowning '%s'", path);
return;
}
if (rootuid == geteuid())
return;
if (stat(path, &sb) < 0)
sysf_printf("stat(%s)", path);
if (sb.st_uid == geteuid() && chown(path, -1, hostgid) < 0)
sysf_printf("Error chgrping '%s'", path);
user_add_map(&users, 'u', 0, rootuid, 1);
user_add_map(&users, 'u', hostuid, hostuid, 1);
user_add_map(&users, 'g', 0, rootgid, 1);
user_add_map(&users, 'g', (gid_t) sb.st_gid,
rootgid + (gid_t) sb.st_gid, 1);
user_add_map(&users, 'g', hostgid, hostgid, 1);
sync_init(sync);
pid = fork();
if (!pid) {
_free_ char *chown_cmd = NULL;
unshare(CLONE_NEWNS | CLONE_NEWUSER);
sync_barrier_parent(sync, SYNC_START);
sync_close(sync);
setup_user("root");
rc = chown(path, 0, sb.st_gid);
sys_fail_if(rc < 0, "Error chowning '%s'", path);
exit(0);
}
sync_wait_child(sync, SYNC_START);
setup_user_map(users, pid);
sync_wake_child(sync, SYNC_DONE);
sync_close(sync);
waitpid(pid, &rc, 0);
return;
}
void setup_console(const char *dest, const char *console) {
int rc;
_free_ char *target = NULL;
rc = asprintf(&target, "%s/dev/console", dest);
fail_if(rc < 0, "OOM");
rc = chmod(console, 0600);
sys_fail_if(rc < 0, "Error chmoding '%s'", console);
rc = mount(console, target, NULL, MS_BIND, NULL);
sys_fail_if(rc < 0, "Error bind mounting '%s'", target);
}
void setup_symlinks(const char *dest) {
int rc;
const char *src[] = {
"/proc/kcore",
"/proc/self/fd",
"/proc/self/fd/0",
"/proc/self/fd/1",
"/proc/self/fd/2"
};
const char *dst[] = {
"/dev/core",
"/dev/fd",
"/dev/stdin",
"/dev/stdout",
"/dev/stderr"
};
for (size_t i = 0; i < sizeof(src) / sizeof(*src); i++) {
_free_ char *link = NULL;
rc = asprintf(&link, "%s/%s", dest, dst[i]);
fail_if(rc < 0, "OOM");
rc = symlink(src[i], link);
sys_fail_if(rc < 0, "Error creating symlink '%s'", link);
}
}
void setup_nodes(const char *dest) {
int rc;
mode_t u = umask(0000);
const char *nodes[] = {
"/dev/console",
"/dev/tty",
"/dev/full",
"/dev/null",
"/dev/zero",
"/dev/random",
"/dev/urandom",
};
for (size_t i = 0; i < sizeof(nodes) / sizeof(*nodes); i++) {
_close_ int fd = -1;
_free_ char *target = NULL;
rc = asprintf(&target, "%s%s", dest, nodes[i]);
fail_if(rc < 0, "OOM");
fd = open(target, O_WRONLY | O_CREAT | O_CLOEXEC, 644);
sys_fail_if(fd < 0, "Error creating file '%s'", target);
rc = mount(nodes[i], target, NULL, MS_BIND, NULL);
sys_fail_if(rc < 0, "Error bind mounting '%s'", target);
}
umask(u);
}

View file

@ -1,35 +0,0 @@
/*
* The process in the flask.
*
* Copyright (c) 2013, Alessandro Ghedini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
void setup_ptmx(const char *dest);
void setup_console_owner(char *path, struct user *users);
void setup_console(const char *dest, const char *console);
void setup_symlinks(const char *dest);
void setup_nodes(const char *dest);

View file

@ -1,125 +0,0 @@
/*
* The process in the flask.
*
* Copyright (c) 2013, Alessandro Ghedini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <unistd.h>
#include <dbus/dbus.h>
#include "printf.h"
#include "util.h"
void register_machine(pid_t pid, const char *dest) {
int rc;
DBusError err;
DBusConnection *conn;
DBusMessageIter args;
DBusMessage *req, *rep;
DBusMessageIter uuid_iter, scope_iter;
_free_ char *name = NULL;
char *app = "pflask";
unsigned char uuid[16];
char *type = "container";
dbus_error_init(&err);
rc = asprintf(&name, "pflask-%d", pid);
fail_if(rc < 0, "OOM");
conn = dbus_bus_get_private(DBUS_BUS_SYSTEM, &err);
if (dbus_error_is_set(&err))
return;
req = dbus_message_new_method_call(
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
"CreateMachine"
);
dbus_message_iter_init_append(req, &args);
/* name */
if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &name))
fail_printf("OOM");
/* id */
if (!dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "y",
&uuid_iter))
fail_printf("OOM");
if (!dbus_message_iter_append_fixed_array(&uuid_iter, DBUS_TYPE_BYTE,
uuid, 0))
fail_printf("OOM");
if (!dbus_message_iter_close_container(&args, &uuid_iter))
fail_printf("OOM");
/* service */
if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &app))
fail_printf("OOM");
/* type */
if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &type))
fail_printf("OOM");
/* leader */
if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &pid))
fail_printf("OOM");
/* root */
if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &dest))
fail_printf("OOM");
/* scope properties */
if (!dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "(sv)",
&scope_iter))
fail_printf("OOM");
if (!dbus_message_iter_close_container(&args, &scope_iter))
fail_printf("OOM");
rep = dbus_connection_send_with_reply_and_block(conn, req, -1, &err);
if (dbus_error_is_set(&err))
goto done;
dbus_message_unref(rep);
done:
dbus_message_unref(req);
dbus_connection_close(conn);
dbus_error_free(&err);
}

View file

@ -1,31 +0,0 @@
/*
* The process in the flask.
*
* Copyright (c) 2013, Alessandro Ghedini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
void register_machine(pid_t pid, const char *dest);

View file

@ -1,309 +0,0 @@
/*
* The process in the flask.
*
* Copyright (c) 2013, Alessandro Ghedini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/version.h>
#include "ut/utlist.h"
#include "mount.h"
#include "path.h"
#include "printf.h"
#include "util.h"
struct mount {
char *src;
char *dst;
char *type;
unsigned long flags;
void *data;
struct mount *next, *prev;
};
struct overlay {
char *overlay;
char *workdir;
char type;
};
static void make_bind_dest(struct mount *m, const char *dest);
static void make_overlay_opts(struct mount *m, const char *dest);
static void mount_add_overlay(struct mount **mounts, const char *overlay,
const char *dst, const char *work);
void mount_add(struct mount **mounts, const char *src, const char *dst,
const char *type, unsigned long f, void *d) {
struct mount *mnt = malloc(sizeof(struct mount));
fail_if(!mnt, "OOM");
mnt->src = src ? strdup(src) : NULL;
mnt->dst = dst ? strdup(dst) : NULL;
mnt->type = type ? strdup(type) : NULL;
mnt->flags = f;
if (type && !strcmp(type, "overlay"))
mnt->data = d;
else
mnt->data = d ? strdup(d) : NULL;
DL_APPEND(*mounts, mnt);
}
void mount_add_from_spec(struct mount **mounts, const char *spec) {
size_t c;
_free_ char **opts = NULL;
_free_ char *tmp = strdup(spec);
fail_if(!tmp, "OOM");
c = split_str(tmp, &opts, ":");
fail_if(!c, "Invalid mount spec '%s': not enough args",spec);
if (!strncmp(opts[0], "bind", 4)) {
fail_if(c < 3, "Invalid mount spec '%s': not enough args",spec);
if (!path_is_absolute(opts[1]))
fail_printf("Invalid mount spec '%s': path not absolute", spec);
if (!path_is_absolute(opts[2]))
fail_printf("Invalid mount spec '%s': path not absolute", spec);
mount_add(mounts, opts[1], opts[2], "bind", MS_BIND, NULL);
if (!strncmp(opts[0], "bind-ro", 8))
mount_add(mounts, opts[1], opts[2], "bind-ro",
MS_REMOUNT | MS_BIND | MS_RDONLY, NULL);
} else if (!strncmp(opts[0], "overlay", 8)) {
fail_if(c < 4, "Invalid mount spec '%s': not enough args",spec);
if (!path_is_absolute(opts[1]))
fail_printf("Invalid mount spec '%s': path not absolute", spec);
if (!path_is_absolute(opts[2]))
fail_printf("Invalid mount spec '%s': path not absolute", spec);
if (!path_is_absolute(opts[3]))
fail_printf("Invalid mount spec '%s': path not absolute", spec);
mount_add_overlay(mounts, opts[1], opts[2], opts[3]);
} else if (!strncmp(opts[0], "tmp", 4)) {
fail_if(c < 2, "Invalid mount spec '%s': not enough args",spec);
if (!path_is_absolute(opts[1]))
fail_printf("Invalid mount spec '%s': path not absolute", spec);
mount_add(mounts, "tmpfs", opts[1], "tmpfs", 0, NULL);
} else {
fail_printf("Invalid mount type '%s'", opts[0]);
}
}
void setup_mount(struct mount *mounts, const char *dest, const char *ephemeral_dir) {
int rc;
struct mount *sys_mounts = NULL;
struct mount *i = NULL;
_free_ char *mount_spec = NULL;
_free_ char *root_dir = NULL;
_free_ char *work_dir = NULL;
_free_ char *procsys_dir = NULL;
rc = mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL);
sys_fail_if(rc < 0, "Error mounting slave /");
if (dest != NULL) {
if (ephemeral_dir != NULL) {
rc = mount("tmpfs", ephemeral_dir, "tmpfs", 0, NULL);
sys_fail_if(rc < 0, "Error mounting tmpfs");
root_dir = path_prefix_root(ephemeral_dir, "root");
rc = mkdir(root_dir, 0755);
sys_fail_if(rc < 0, "Error creating directory '%s'",
root_dir);
work_dir = path_prefix_root(ephemeral_dir, "work");
rc = mkdir(work_dir, 0755);
sys_fail_if(rc < 0, "Error creating directory '%s'",
work_dir);
mount_add_overlay(&sys_mounts, root_dir, "/", work_dir);
}
mount_add(&sys_mounts, "proc", "/proc", "proc",
MS_NOSUID | MS_NOEXEC | MS_NODEV, NULL);
procsys_dir = path_prefix_root(dest, "/proc/sys");
mount_add(&sys_mounts, procsys_dir, "/proc/sys", "proc/sys",
MS_BIND, NULL);
mount_add(&sys_mounts, NULL, "/proc/sys", "proc/sys-ro",
MS_BIND | MS_RDONLY | MS_REMOUNT, NULL);
mount_add(&sys_mounts, "sysfs", "/sys", "sysfs",
MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_RDONLY, NULL);
mount_add(&sys_mounts, "tmpfs", "/dev", "tmpfs",
MS_NOSUID | MS_STRICTATIME, "mode=755");
mount_add(&sys_mounts, "devpts", "/dev/pts", "devpts",
MS_NOSUID | MS_NOEXEC,
"newinstance,ptmxmode=0666,mode=0620,gid=5");
mount_add(&sys_mounts, "tmpfs", "/dev/shm", "tmpfs",
MS_NOSUID | MS_STRICTATIME | MS_NODEV, "mode=1777");
mount_add(&sys_mounts, "tmpfs", "/run", "tmpfs",
MS_NOSUID | MS_NODEV | MS_STRICTATIME, "mode=755");
}
DL_CONCAT(sys_mounts, mounts);
DL_FOREACH(sys_mounts, i) {
_free_ char *mnt_dest = path_prefix_root(dest, i->dst);
if (!strcmp(i->type, "overlay"))
make_overlay_opts(i, mnt_dest);
if (!strcmp(i->type, "bind") || !strcmp(i->type, "bind-ro")) {
make_bind_dest(i, mnt_dest);
} else {
rc = mkdir(mnt_dest, 0755);
if (rc < 0) {
struct stat sb;
switch (errno) {
case EEXIST:
if (!stat(mnt_dest, &sb) &&
!S_ISDIR(sb.st_mode))
fail_printf("Not a directory");
break;
default:
sysf_printf("mkdir(%s)", mnt_dest);
break;
}
}
}
rc = mount(i->src, mnt_dest, i->type, i->flags, i->data);
sys_fail_if(rc < 0, "Error mounting '%s'", i->type);
}
}
static void make_bind_dest(struct mount *m, const char *dest) {
int rc;
struct stat src_sb, dst_sb;
rc = stat(m->src, &src_sb);
sys_fail_if(rc < 0, "stat(%s)", m->src);
if (stat(dest, &dst_sb) >= 0) {
if (S_ISDIR(src_sb.st_mode) && !S_ISDIR(dst_sb.st_mode))
fail_printf("Could not bind mount dir %s on file %s",
m->src, dest);
if (!S_ISDIR(src_sb.st_mode) && S_ISDIR(dst_sb.st_mode))
fail_printf("Could not bind mount file %s on dir %s",
m->src, dest);
} else if (errno == ENOENT) {
if (!S_ISDIR(src_sb.st_mode)) {
_close_ int fd = -1;
fd = open(dest,
O_WRONLY | O_CREAT | O_CLOEXEC | O_NOCTTY,
0644);
rc = fd;
} else {
rc = mkdir(dest, 0755);
}
sys_fail_if(rc < 0, "Could not create mount dest %s", dest);
} else {
sysf_printf("Error opening '%s'", dest);
}
}
static void make_overlay_opts(struct mount *m, const char *dest) {
int rc;
_free_ struct overlay *ovl = m->data;
char *overlay = ovl->overlay;
char *workdir = ovl->workdir;
char *overlayfs_opts = NULL;
if (ovl->type == 'a') {
rc = asprintf(&overlayfs_opts, "br:%s=rw:%s=ro", overlay, dest);
fail_if(rc < 0, "OOM");
} else if (ovl->type == 'o') {
rc = asprintf(&overlayfs_opts,
"upperdir=%s,lowerdir=%s,workdir=%s",
overlay, dest, workdir);
fail_if(rc < 0, "OOM");
}
m->data = overlayfs_opts;
}
static void mount_add_overlay(struct mount **mounts, const char *overlay,
const char *dst, const char *workdir) {
struct overlay *ovl = malloc(sizeof(struct overlay));
ovl->overlay = strdup(overlay);
ovl->workdir = strdup(workdir);
#ifdef HAVE_AUFS
ovl->type = 'a';
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
ovl->type = 'o';
#else
fail_printf("The 'overlay' mount type is not supported");
#endif
mount_add(mounts, NULL, dst, "overlay", 0, ovl);
}

View file

@ -1,38 +0,0 @@
/*
* The process in the flask.
*
* Copyright (c) 2013, Alessandro Ghedini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
struct mount;
void mount_add(struct mount **mounts, const char *src, const char *dst,
const char *type, unsigned long f, void *d);
void mount_add_from_spec(struct mount **mounts, const char *spec);
void setup_mount(struct mount *mounts, const char *dest, const char *ephemeral_dir);

View file

@ -1,315 +0,0 @@
/*
* The process in the flask.
*
* Copyright (c) 2013, Alessandro Ghedini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include <net/if.h>
#include <linux/rtnetlink.h>
#include <linux/veth.h>
#include "ut/utlist.h"
#include "netif.h"
#include "nl.h"
#include "printf.h"
#include "util.h"
struct netif {
enum netif_type type;
char *dev;
char *name;
struct netif *next, *prev;
} netif;
static void if_up(int sock, int if_index);
static void move_and_rename_if(int sock, pid_t pid, int i, char *new_name);
static void create_macvlan(int sock, int master, char *name);
static void create_ipvlan(int sock, int master, char *name);
static void create_veth_pair(int sock, char *name_out, char *name_in);
void netif_add(struct netif **ifs, enum netif_type type, char *dev, char *name) {
struct netif *nif = malloc(sizeof(struct netif));
fail_if(!nif, "OOM");
nif->dev = strdup(dev);
nif->name = strdup(name);
nif->type = type;
DL_APPEND(*ifs, nif);
}
void netif_add_from_spec(struct netif **ifs, const char *spec) {
_free_ char *tmp = NULL;
_free_ char **opts = NULL;
if (!spec) return;
tmp = strdup(spec);
fail_if(!tmp, "OOM");
size_t c = split_str(tmp, &opts, ":");
fail_if(!c, "Invalid netif spec '%s': not enough args", spec);
if (if_nametoindex(opts[0])) {
fail_if(c < 2, "Invalid netif spec '%s': not enough args",spec);
netif_add(ifs, MOVE, opts[0], opts[1]);
} else if (!strncmp(opts[0], "macvlan", 8)) {
fail_if(c < 3, "Invalid netif spec '%s': not enough args",spec);
netif_add(ifs, MACVLAN, opts[1], opts[2]);
} else if (!strncmp(opts[0], "ipvlan", 8)) {
fail_if(c < 3, "Invalid netif spec '%s': not enough args",spec);
netif_add(ifs, IPVLAN, opts[1], opts[2]);
} else if (!strncmp(opts[0], "veth", 5)) {
fail_if(c < 3, "Invalid netif spec '%s': not enough args",spec);
netif_add(ifs, VETH, opts[1], opts[2]);
} else {
fail_printf("Invalid netif spec '%s'", spec);
}
}
void setup_netif(struct netif *ifs, pid_t pid) {
int rc;
_close_ int sock = nl_open();
struct netif *i = NULL;
DL_FOREACH(ifs, i) {
unsigned int if_index = 0;
switch (i->type) {
case MACVLAN: {
_free_ char *name = NULL;
rc = asprintf(&name, "pflask-%d", pid);
fail_if(rc < 0, "OOM");
if_index = if_nametoindex(i->dev);
sys_fail_if(!if_index, "Error searching for '%s'", i->dev);
create_macvlan(sock, if_index, name);
if_index = if_nametoindex(name);
break;
}
case IPVLAN: {
_free_ char *name = NULL;
rc = asprintf(&name, "pflask-%d", pid);
fail_if(rc < 0, "OOM");
if_index = if_nametoindex(i->dev);
sys_fail_if(!if_index, "Error searching for '%s'", i->dev);
create_ipvlan(sock, if_index, name);
if_index = if_nametoindex(name);
break;
}
case VETH: {
_free_ char *name = NULL;
rc = asprintf(&name, "pflask-%d", pid);
fail_if(rc < 0, "OOM");
create_veth_pair(sock, i->dev, name);
if_index = if_nametoindex(name);
sys_fail_if(!if_index, "Error searching for '%s'", name);
break;
}
case MOVE:
if_index = if_nametoindex(i->dev);
sys_fail_if(!if_index, "Error searching for '%s'", i->dev);
break;
}
move_and_rename_if(sock, pid, if_index, i->name);
}
}
void config_netif(void) {
_close_ int sock = nl_open();
if_up(sock, 1);
}
static void if_up(int sock, int if_index) {
_free_ struct nlmsg *req = malloc(NLMSG_GOOD_SIZE);
req->hdr.nlmsg_seq = 1;
req->hdr.nlmsg_type = RTM_NEWLINK;
req->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
req->msg.ifi.ifi_family = AF_UNSPEC;
req->msg.ifi.ifi_index = if_index;
req->msg.ifi.ifi_flags = IFF_UP;
req->msg.ifi.ifi_change = IFF_UP;
nl_send(sock, req);
nl_recv(sock, req);
if (req->hdr.nlmsg_type == NLMSG_ERROR)
sys_fail_if(req->msg.err.error < 0,
"Error sending netlink request");
}
static void move_and_rename_if(int sock, pid_t pid, int if_index, char *new_name) {
_free_ struct nlmsg *req = malloc(NLMSG_GOOD_SIZE);
req->hdr.nlmsg_seq = 1;
req->hdr.nlmsg_type = RTM_NEWLINK;
req->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
req->msg.ifi.ifi_family = AF_UNSPEC;
req->msg.ifi.ifi_index = if_index;
rtattr_append(req, IFLA_NET_NS_PID, &pid, sizeof(pid));
rtattr_append(req, IFLA_IFNAME, new_name, strlen(new_name) + 1);
nl_send(sock, req);
nl_recv(sock, req);
if (req->hdr.nlmsg_type == NLMSG_ERROR)
sys_fail_if(req->msg.err.error < 0,
"Error sending netlink request");
}
static void create_macvlan(int sock, int master, char *name) {
struct rtattr *nested = NULL;
_free_ struct nlmsg *req = malloc(NLMSG_GOOD_SIZE);
req->hdr.nlmsg_seq = 1;
req->hdr.nlmsg_type = RTM_NEWLINK;
req->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
req->hdr.nlmsg_flags = NLM_F_REQUEST |
NLM_F_CREATE |
NLM_F_EXCL |
NLM_F_ACK;
req->msg.ifi.ifi_family = AF_UNSPEC;
nested = rtattr_start_nested(req, IFLA_LINKINFO);
rtattr_append(req, IFLA_INFO_KIND, "macvlan", 8);
rtattr_end_nested(req, nested);
rtattr_append(req, IFLA_LINK, &master, sizeof(master));
rtattr_append(req, IFLA_IFNAME, name, strlen(name) + 1);
nl_send(sock, req);
nl_recv(sock, req);
if (req->hdr.nlmsg_type == NLMSG_ERROR)
sys_fail_if(req->msg.err.error < 0,
"Error sending netlink request");
}
static void create_ipvlan(int sock, int master, char *name) {
struct rtattr *nested = NULL;
_free_ struct nlmsg *req = malloc(NLMSG_GOOD_SIZE);
req->hdr.nlmsg_seq = 1;
req->hdr.nlmsg_type = RTM_NEWLINK;
req->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
req->hdr.nlmsg_flags = NLM_F_REQUEST |
NLM_F_CREATE |
NLM_F_EXCL |
NLM_F_ACK;
req->msg.ifi.ifi_family = AF_UNSPEC;
nested = rtattr_start_nested(req, IFLA_LINKINFO);
rtattr_append(req, IFLA_INFO_KIND, "ipvlan", 7);
rtattr_end_nested(req, nested);
rtattr_append(req, IFLA_LINK, &master, sizeof(master));
rtattr_append(req, IFLA_IFNAME, name, strlen(name) + 1);
nl_send(sock, req);
nl_recv(sock, req);
if (req->hdr.nlmsg_type == NLMSG_ERROR)
sys_fail_if(req->msg.err.error < 0,
"Error sending netlink request");
}
static void create_veth_pair(int sock, char *name_out, char *name_in) {
struct rtattr *nested_info = NULL;
struct rtattr *nested_data = NULL;
struct rtattr *nested_peer = NULL;
_free_ struct nlmsg *req = malloc(NLMSG_GOOD_SIZE);
req->hdr.nlmsg_seq = 1;
req->hdr.nlmsg_type = RTM_NEWLINK;
req->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
req->hdr.nlmsg_flags = NLM_F_REQUEST |
NLM_F_CREATE |
NLM_F_EXCL |
NLM_F_ACK;
req->msg.ifi.ifi_family = AF_UNSPEC;
nested_info = rtattr_start_nested(req, IFLA_LINKINFO);
rtattr_append(req, IFLA_INFO_KIND, "veth", 5);
nested_data = rtattr_start_nested(req, IFLA_INFO_DATA);
nested_peer = rtattr_start_nested(req, VETH_INFO_PEER);
req->hdr.nlmsg_len += sizeof(struct ifinfomsg);
rtattr_append(req, IFLA_IFNAME, name_in, strlen(name_in) + 1);
rtattr_end_nested(req, nested_peer);
rtattr_end_nested(req, nested_data);
rtattr_end_nested(req, nested_info);
rtattr_append(req, IFLA_IFNAME, name_out, strlen(name_out) + 1);
nl_send(sock, req);
nl_recv(sock, req);
if (req->hdr.nlmsg_type == NLMSG_ERROR)
sys_fail_if(req->msg.err.error < 0,
"Error sending netlink request");
}

View file

@ -1,46 +0,0 @@
/*
* The process in the flask.
*
* Copyright (c) 2013, Alessandro Ghedini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
enum netif_type {
MOVE,
MACVLAN,
IPVLAN,
VETH,
};
struct netif;
void netif_add(struct netif **ifs, enum netif_type type, char *dev, char *name);
void netif_add_from_spec(struct netif **ifs, const char *spec);
void setup_netif(struct netif *ifs, pid_t pid);
void config_netif(void);

135
src/nl.c
View file

@ -1,135 +0,0 @@
/*
* The process in the flask.
*
* Copyright (c) 2013, Alessandro Ghedini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <linux/rtnetlink.h>
#include "nl.h"
#include "printf.h"
#include "util.h"
void rtattr_append(struct nlmsg *nlmsg, int attr, void *d, size_t len) {
struct rtattr *rtattr;
size_t rtalen = RTA_LENGTH(len);
rtattr = NLMSG_TAIL(&nlmsg->hdr);
rtattr->rta_type = attr;
rtattr->rta_len = rtalen;
memcpy(RTA_DATA(rtattr), d, len);
nlmsg->hdr.nlmsg_len = NLMSG_ALIGN(nlmsg->hdr.nlmsg_len) +
RTA_ALIGN(rtalen);
}
struct rtattr *rtattr_start_nested(struct nlmsg *nlmsg, int attr) {
struct rtattr *rtattr = NLMSG_TAIL(&nlmsg->hdr);
rtattr_append(nlmsg, attr, NULL, 0);
return rtattr;
}
void rtattr_end_nested(struct nlmsg *nlmsg, struct rtattr *rtattr) {
rtattr->rta_len = (char *) NLMSG_TAIL(&nlmsg->hdr) - (char *) rtattr;
}
int nl_open(void) {
int rc;
int sock = -1;
struct sockaddr_nl addr;
sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
sys_fail_if(sock < 0, "Error creating netlink socket");
addr.nl_family = AF_NETLINK;
addr.nl_pad = 0;
addr.nl_pid = getpid();
addr.nl_groups = 0;
rc = bind(sock, (struct sockaddr *) &addr, sizeof(struct sockaddr_nl));
sys_fail_if(rc < 0, "Error binding netlink socket");
return sock;
}
void nl_send(int sock, struct nlmsg *nlmsg) {
int rc;
struct sockaddr_nl addr;
struct iovec iov = {
.iov_base = (void *) nlmsg,
.iov_len = nlmsg->hdr.nlmsg_len
};
struct msghdr msg = {
.msg_name = &addr,
.msg_namelen = sizeof(struct sockaddr_nl),
.msg_iov = &iov,
.msg_iovlen = 1
};
memset(&addr, 0, sizeof(struct sockaddr_nl));
addr.nl_family = AF_NETLINK;
addr.nl_pid = 0;
addr.nl_groups = 0;
rc = sendmsg(sock, &msg, 0);
sys_fail_if(rc < 0, "Error sending netlink message");
}
void nl_recv(int sock, struct nlmsg *nlmsg) {
int rc;
struct sockaddr_nl addr;
struct iovec iov = {
.iov_base = (void *) nlmsg,
.iov_len = nlmsg->hdr.nlmsg_len
};
struct msghdr msg = {
.msg_name = &addr,
.msg_namelen = sizeof(struct sockaddr_nl),
.msg_iov = &iov,
.msg_iovlen = 1
};
memset(&addr, 0, sizeof(struct sockaddr_nl));
addr.nl_family = AF_NETLINK;
addr.nl_pid = 0;
addr.nl_groups = 0;
rc = recvmsg(sock, &msg, 0);
sys_fail_if(rc < 0, "Error receiving netlink message");
}

View file

@ -1,51 +0,0 @@
/*
* The process in the flask.
*
* Copyright (c) 2013, Alessandro Ghedini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
struct nlmsg {
struct nlmsghdr hdr;
union {
struct ifinfomsg ifi;
struct nlmsgerr err;
} msg;
};
#define NLMSG_TAIL(nmsg) \
((struct rtattr *) (((char *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
#define NLMSG_GOOD_SIZE (sizeof(struct nlmsg) * 256)
int nl_open(void);
void nl_send(int sock, struct nlmsg *nlmsg);
void nl_recv(int sock, struct nlmsg *nlmsg);
void rtattr_append(struct nlmsg *nlmsg, int attr, void *d, size_t len);
struct rtattr *rtattr_start_nested(struct nlmsg *nlmsg, int attr);
void rtattr_end_nested(struct nlmsg *nlmsg, struct rtattr *rtattr);

View file

@ -1,153 +0,0 @@
/*
* The process in the flask.
*
* Copyright (c) 2013, Alessandro Ghedini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include "printf.h"
#include "util.h"
int path_compare(const char *a, const char *b) {
int d;
/* A relative path and an abolute path must not compare as equal.
* Which one is sorted before the other does not really matter.
* Here a relative path is ordered before an absolute path. */
d = (a[0] == '/') - (b[0] == '/');
if (d)
return d;
for (;;) {
size_t j, k;
a += strspn(a, "/");
b += strspn(b, "/");
if (*a == 0 && *b == 0)
return 0;
/* Order prefixes first: "/foo" before "/foo/bar" */
if (*a == 0)
return -1;
if (*b == 0)
return 1;
j = strcspn(a, "/");
k = strcspn(b, "/");
/* Alphabetical sort: "/foo/aaa" before "/foo/b" */
d = memcmp(a, b, MIN(j, k));
if (d)
return (d > 0) - (d < 0); /* sign of d */
/* Sort "/foo/a" before "/foo/aaa" */
d = (j > k) - (j < k); /* sign of (j - k) */
if (d)
return d;
a += j;
b += k;
}
}
char *path_prefix_root(const char *root, const char *path) {
char *n, *p;
size_t l;
/* If root is passed, prefixes path with it. Otherwise returns
* it as is. */
/* First, drop duplicate prefixing slashes from the path */
while (path[0] == '/' && path[1] == '/')
path++;
if (!root || !root[0] ||
(path_compare(root, "/") == 0) ||
(path_compare(root, path) == 0))
return strdup(path);
l = strlen(root) + 1 + strlen(path) + 1;
n = malloc(l);
if (!n)
return NULL;
p = stpcpy(n, root);
while (p > n && p[-1] == '/')
p--;
if (path[0] != '/')
*(p++) = '/';
strcpy(p, path);
return n;
}
char *on_path(char *cmd, const char *rootfs) {
int rc;
_free_ char *path = NULL;
char *iter = NULL;
char *entry = NULL;
char *saveptr = NULL;
char *cmd_path = NULL;
path = getenv("PATH");
if (!path)
return NULL;
path = strdup(path);
if (!path)
return NULL;
iter = path;
while ((entry = strtok_r(iter, ":", &saveptr))) {
iter = NULL;
if (rootfs)
rc = asprintf(&cmd_path, "%s/%s/%s", rootfs, entry, cmd);
else
rc = asprintf(&cmd_path, "%s/%s", entry, cmd);
if (rc >= 0 && !access(cmd_path, X_OK))
return cmd_path;
}
return NULL;
}
bool path_is_absolute(const char *p) {
return p[0] == '/';
}

View file

@ -1,37 +0,0 @@
/*
* The process in the flask.
*
* Copyright (c) 2013, Alessandro Ghedini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
int path_compare(const char *a, const char *b);
char *path_prefix_root(const char *root, const char *path);
char *on_path(char *cmd, const char *rootfs);
bool path_is_absolute(const char *p);

View file

@ -1,406 +0,0 @@
/*
* The process in the flask.
*
* Copyright (c) 2013, Alessandro Ghedini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <syslog.h>
#include <sys/syscall.h>
#include <linux/sched.h>
#include <getopt.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include "cmdline.h"
#include "capabilities.h"
#include "pty.h"
#include "user.h"
#include "dev.h"
#include "machine.h"
#include "mount.h"
#include "cgroup.h"
#include "netif.h"
#include "sync.h"
#include "printf.h"
#include "util.h"
static size_t validate_optlist(const char *name, const char *opts);
static void do_daemonize(void);
static void do_chroot(const char *dest);
static pid_t do_clone(int *flags);
int main(int argc, char *argv[]) {
int rc, sync[2];
pid_t pid = -1;
siginfo_t status;
struct mount *mounts = NULL;
struct netif *netifs = NULL;
struct cgroup *cgroups = NULL;
struct user *users = NULL;
#if HAVE_LIBCAP_NG
struct capability *caps = NULL;
#endif
char *master;
_close_ int master_fd = -1;
char ephemeral_dir[] = "/tmp/pflask-ephemeral-XXXXXX";
int clone_flags = CLONE_NEWNS |
CLONE_NEWIPC |
CLONE_NEWPID |
CLONE_NEWUTS;
struct gengetopt_args_info args;
if (cmdline_parser(argc, argv, &args) != 0)
return 1;
for (unsigned int i = 0; i < args.mount_given; i++) {
validate_optlist("--mount", args.mount_arg[i]);
mount_add_from_spec(&mounts, args.mount_arg[i]);
}
for (unsigned int i = 0; i < args.netif_given; i++) {
clone_flags |= CLONE_NEWNET;
if (args.netif_arg != NULL) {
validate_optlist("--netif", args.netif_arg[i]);
netif_add_from_spec(&netifs, args.netif_arg[i]);
}
}
if (args.user_given && !args.user_map_given) {
uid_t uid;
gid_t gid;
clone_flags |= CLONE_NEWUSER;
if (user_get_uid_gid(args.user_arg, &uid, &gid)) {
user_add_map(&users, 'u', uid, uid, 1);
user_add_map(&users, 'g', gid, gid, 1);
}
}
for (unsigned int i = 0; i < args.user_map_given; i++) {
size_t count;
uid_t id, host_id;
char *start = args.user_map_arg[i], *end = NULL;
validate_optlist("--user-map", args.user_map_arg[i]);
clone_flags |= CLONE_NEWUSER;
id = strtoul(start, &end, 10);
if (*end != ':')
fail_printf("Invalid value '%s' for --user-map",
args.user_map_arg[i]);
start = end + 1;
host_id = strtoul(start, &end, 10);
if (*end != ':')
fail_printf("Invalid value '%s' for --user-map",
args.user_map_arg[i]);
start = end + 1;
count = strtoul(start, &end, 10);
if (*end != '\0')
fail_printf("Invalid value '%s' for --user-map",
args.user_map_arg[i]);
user_add_map(&users, 'u', id, host_id, count);
user_add_map(&users, 'g', id, host_id, count);
}
for (unsigned int i = 0; i < args.cgroup_given; i++)
cgroup_add(&cgroups, args.cgroup_arg[i]);
#if HAVE_LIBCAP_NG
for (unsigned int i = 0; i < args.caps_given; i++)
capability_add(&caps, args.caps_arg[i]);
#endif
if (args.no_userns_flag)
clone_flags &= ~(CLONE_NEWUSER);
if (args.no_mountns_flag)
clone_flags &= ~(CLONE_NEWNS);
if (args.no_netns_flag)
clone_flags &= ~(CLONE_NEWNET);
if (args.no_ipcns_flag)
clone_flags &= ~(CLONE_NEWIPC);
if (args.no_utsns_flag)
clone_flags &= ~(CLONE_NEWUTS);
if (args.no_pidns_flag)
clone_flags &= ~(CLONE_NEWPID);
if (args.attach_given) {
master_fd = recv_pty(args.attach_arg);
fail_if(master_fd < 0, "Invalid PID '%u'", args.attach_arg);
process_pty(master_fd);
return 0;
}
open_master_pty(&master_fd, &master);
if (args.detach_flag)
do_daemonize();
sync_init(sync);
if (args.ephemeral_flag) {
if (!mkdtemp(ephemeral_dir))
sysf_printf("mkdtemp()");
}
pid = do_clone(&clone_flags);
if (!pid) {
closep(&master_fd);
rc = prctl(PR_SET_PDEATHSIG, SIGKILL);
sys_fail_if(rc < 0, "prctl(PR_SET_PDEATHSIG)");
rc = setsid();
sys_fail_if(rc < 0, "setsid()");
sync_barrier_parent(sync, SYNC_START);
sync_close(sync);
open_slave_pty(master);
setup_user(args.user_arg);
if (args.hostname_given) {
rc = sethostname(args.hostname_arg,
strlen(args.hostname_arg));
sys_fail_if(rc < 0, "Error setting hostname");
}
setup_mount(mounts, args.chroot_arg, args.ephemeral_flag ?
ephemeral_dir : NULL);
if (args.chroot_given) {
setup_nodes(args.chroot_arg);
setup_ptmx(args.chroot_arg);
setup_symlinks(args.chroot_arg);
setup_console(args.chroot_arg, master);
do_chroot(args.chroot_arg);
}
if (clone_flags & CLONE_NEWNET)
config_netif();
umask(0022);
#if HAVE_LIBCAP_NG
setup_capabilities(caps);
#endif
if (args.chdir_given) {
rc = chdir(args.chdir_arg);
sys_fail_if(rc < 0, "Error changing cwd");
}
if (args.chroot_given) {
char *term = getenv("TERM");
if (!args.keepenv_flag)
clearenv();
setenv("PATH", "/usr/sbin:/usr/bin:/sbin:/bin", 1);
setenv("USER", args.user_arg, 1);
setenv("LOGNAME", args.user_arg, 1);
setenv("TERM", term, 1);
}
for (unsigned int i = 0; i < args.setenv_given; i++) {
rc = putenv(strdup(args.setenv_arg[i]));
sys_fail_if(rc != 0, "Error setting environment");
}
setenv("container", "pflask", 1);
if (argc > optind)
rc = execvpe(argv[optind], argv + optind, environ);
else
rc = execle("/bin/bash", "-bash", NULL, environ);
sys_fail_if(rc < 0, "Error executing command");
}
sync_wait_child(sync, SYNC_START);
if (args.chroot_given && (clone_flags & CLONE_NEWUSER))
setup_console_owner(master, users);
setup_cgroup(cgroups, pid);
setup_netif(netifs, pid);
#ifdef HAVE_DBUS
register_machine(pid, args.chroot_given ? args.chroot_arg : "");
#endif
if (clone_flags & CLONE_NEWUSER)
setup_user_map(users, pid);
sync_wake_child(sync, SYNC_DONE);
sync_close(sync);
if (args.detach_flag)
serve_pty(master_fd);
else
process_pty(master_fd);
kill(pid, SIGKILL);
rc = waitid(P_PID, pid, &status, WEXITED);
sys_fail_if(rc < 0, "Error waiting for child");
switch (status.si_code) {
case CLD_EXITED:
if (status.si_status != 0)
err_printf("Child failed with code '%d'",
status.si_status);
else
ok_printf("Child exited");
break;
case CLD_KILLED:
err_printf("Child was terminated");
break;
default:
err_printf("Child failed");
break;
}
sync_close(sync);
clean_cgroup(cgroups);
if (args.ephemeral_flag) {
rc = rmdir(ephemeral_dir);
sys_fail_if(rc != 0, "Error deleting ephemeral directory: %s",
ephemeral_dir);
}
return status.si_status;
}
static size_t validate_optlist(const char *name, const char *opts) {
size_t i, c;
_free_ char **vars = NULL;
_free_ char *tmp = NULL;
if (!opts || !*opts)
return 0;
tmp = strdup(opts);
fail_if(!tmp, "OOM");
c = split_str(tmp, &vars, ":");
fail_if(!c, "Invalid value '%s' for %s", opts, name);
for (i = 0; i < c; i++) {
if (vars[i] == '\0')
fail_printf("Invalid value '%s' for %s", opts, name);
}
return c;
}
static void do_daemonize(void) {
int rc;
openlog("pflask", LOG_NDELAY | LOG_PID, LOG_DAEMON);
use_syslog = 1;
rc = daemon(0, 0);
sys_fail_if(rc < 0, "Error daemonizing");
}
static void do_chroot(const char *dest) {
int rc;
rc = chdir(dest);
sys_fail_if(rc < 0, "chdir()");
rc = chroot(".");
sys_fail_if(rc < 0, "Error chrooting");
rc = chdir("/");
sys_fail_if(rc < 0, "chdir(/)");
}
static pid_t do_clone(int *flags) {
pid_t pid;
*flags |= SIGCHLD;
pid = syscall(__NR_clone, *flags, NULL);
if (pid < 0) {
if (errno == EINVAL) {
*flags &= ~(CLONE_NEWUSER);
pid = syscall(__NR_clone, *flags, NULL);
}
}
sys_fail_if(pid < 0, "Error cloning process");
return pid;
}

View file

@ -1,113 +0,0 @@
/*
* The process in the flask.
*
* Copyright (c) 2015, Alessandro Ghedini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <stdarg.h>
#include <stdbool.h>
#include <string.h>
#include <syslog.h>
#include <limits.h>
#include "printf.h"
#include "util.h"
int use_syslog = 0;
static void do_log(const char *prefix, const char *fmt, va_list args, bool c);
void ok_printf(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
do_log("[" COLOR_GREEN "" COLOR_OFF "] ", fmt, args, false);
va_end(args);
}
void debug_printf(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
do_log("[" COLOR_YELLOW "¡" COLOR_OFF "] ", fmt, args, false);
va_end(args);
}
void err_printf(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
do_log("[" COLOR_RED "" COLOR_OFF "] ", fmt, args, false);
va_end(args);
}
void fail_printf(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
do_log("[" COLOR_RED "" COLOR_OFF "] ", fmt, args, true);
va_end(args);
_exit(EXIT_FAILURE);
}
void sysf_printf(const char *fmt, ...) {
int rc;
va_list args;
_free_ char *format = NULL;
rc = asprintf(&format, "%s: %s", fmt, strerror(errno));
fail_if(rc < 0, "OOM");
va_start(args, fmt);
do_log("[" COLOR_RED "" COLOR_OFF "] ", format, args, true);
va_end(args);
_exit(EXIT_FAILURE);
}
static void do_log(const char *pre, const char *fmt, va_list args, bool cursor) {
int rc;
static char format[LINE_MAX];
if (use_syslog || !isatty(STDERR_FILENO))
rc = snprintf(format, LINE_MAX, "%s\n", fmt);
else
rc = snprintf(format, LINE_MAX, "\r" LINE_CLEAR "%s%s%s\n",
cursor ? CURSOR_SHOW : "", pre, fmt);
if (rc < 0) fail_printf("EIO");
if (use_syslog == 1)
vsyslog(LOG_CRIT, format, args);
else
vfprintf(stderr, format, args);
}

View file

@ -1,48 +0,0 @@
/*
* The process in the flask.
*
* Copyright (c) 2015, Alessandro Ghedini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define COLOR_GREEN ""
#define COLOR_YELLOW ""
#define COLOR_RED ""
#define COLOR_BGRED ""
#define COLOR_OFF ""
#define LINE_CLEAR ""
#define CURSOR_HIDE "[?25l"
#define CURSOR_SHOW "[?25h"
int use_syslog;
void ok_printf(const char *fmt, ...);
void debug_printf(const char *fmt, ...);
void err_printf(const char *fmt, ...);
void fail_printf(const char *fmt, ...);
void sysf_printf(const char *fmt, ...);

445
src/pty.c
View file

@ -1,445 +0,0 @@
/*
* The process in the flask.
*
* Copyright (c) 2013, Alessandro Ghedini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <sys/epoll.h>
#include <sys/signalfd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "printf.h"
#include "util.h"
#define SOCKET_PATH "@/com/github/ghedo/pflask/%u"
static struct termios stdin_attr;
static struct winsize stdin_ws;
static int recv_fd(int sock);
static void send_fd(int sock, int fd);
void open_master_pty(int *master_fd, char **master_name) {
int rc;
rc = tcgetattr(STDIN_FILENO, &stdin_attr);
sys_fail_if(rc < 0, "tcgetattr()");
rc = ioctl(STDIN_FILENO, TIOCGWINSZ, &stdin_ws);
sys_fail_if(rc < 0, "ioctl(TIOCGWINSZ)");
*master_fd = posix_openpt(O_RDWR | O_NOCTTY | O_NDELAY);
sys_fail_if(*master_fd < 0, "Error opening master pty");
*master_name = ptsname(*master_fd);
sys_fail_if(!*master_name, "ptsname()");
rc = unlockpt(*master_fd);
sys_fail_if(rc < 0, "Error unlocking master pty");
}
void open_slave_pty(const char *master_name) {
int rc;
_close_ int slave_fd = -1;
slave_fd = open(master_name, O_RDWR, 0);
sys_fail_if(slave_fd < 0, "Error opening slave pty");
if (!isatty(slave_fd)) fail_printf("Not a TTY");
rc = dup2(slave_fd, STDIN_FILENO);
sys_fail_if(rc < 0, "dup2(STDIN)");
rc = dup2(slave_fd, STDOUT_FILENO);
sys_fail_if(rc < 0, "dup2(STDOUT)");
rc = dup2(slave_fd, STDERR_FILENO);
sys_fail_if(rc < 0, "dup2(STDERR)");
rc = tcsetattr(slave_fd, TCSANOW, &stdin_attr);
sys_fail_if(rc < 0, "tcsetattr()");
rc = ioctl(slave_fd, TIOCSWINSZ, &stdin_ws);
sys_fail_if(rc < 0, "ioctl(TIOCWINSZ)");
}
void process_pty(int master_fd) {
int rc;
sigset_t mask;
_close_ int epoll_fd = -1;
_close_ int signal_fd = -1;
struct termios raw_attr;
struct epoll_event stdin_ev, master_ev, signal_ev, events[3];
int line_max = sysconf(_SC_LINE_MAX);
if (line_max < 0) sysf_printf("sysconf()");
memcpy(&raw_attr, &stdin_attr, sizeof(stdin_attr));
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGTERM);
sigaddset(&mask, SIGCHLD);
sigaddset(&mask, SIGWINCH);
sigaddset(&mask, SIGRTMIN + 4);
rc = sigprocmask(SIG_BLOCK, &mask, NULL);
sys_fail_if(rc < 0, "sigprocmask()");
signal_fd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
if (signal_fd < 0) sysf_printf("signalfd()");
rc = tcgetattr(STDIN_FILENO, &stdin_attr);
sys_fail_if(rc < 0, "tcgetattr()");
cfmakeraw(&raw_attr);
raw_attr.c_lflag &= ~ECHO;
rc = tcsetattr(STDIN_FILENO, TCSANOW, &raw_attr);
sys_fail_if(rc < 0, "tcsetattr()");
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd < 0) sysf_printf("epoll_create1()");
stdin_ev.events = EPOLLIN; stdin_ev.data.fd = STDIN_FILENO;
rc = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, stdin_ev.data.fd, &stdin_ev);
sys_fail_if(rc < 0, "epoll_ctl(STDIN_FILENO)");
master_ev.events = EPOLLIN; master_ev.data.fd = master_fd;
rc = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, master_ev.data.fd, &master_ev);
sys_fail_if(rc < 0, "epoll_ctl(master_fd)");
signal_ev.events = EPOLLIN; signal_ev.data.fd = signal_fd;
rc = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, signal_ev.data.fd, &signal_ev);
sys_fail_if(rc < 0, "epoll_ctl(signal_fd)");
while (1) {
char buf[line_max];
rc = epoll_wait(epoll_fd, events, 1, -1);
sys_fail_if(rc < 0, "epoll_wait()");
if (events[0].data.fd == STDIN_FILENO) {
char *p;
int rc = read(STDIN_FILENO, buf, line_max);
if (!rc)
goto done;
else if (rc < 0)
goto done;
rc = write(master_fd, buf, rc);
sys_fail_if(rc < 0, "write()");
for (p = buf; p < buf + rc; p++) {
if (*p == '\0')
goto done;
}
}
if (events[0].data.fd == master_fd) {
rc = read(master_fd, buf, line_max);
if (!rc)
goto done;
else if (rc < 0)
goto done;
rc = write(STDOUT_FILENO, buf, rc);
sys_fail_if(rc < 0, "write()");
}
if (events[0].data.fd == signal_fd) {
struct signalfd_siginfo fdsi;
rc = read(signal_fd, &fdsi, sizeof(fdsi));
if (rc != sizeof(fdsi)) sysf_printf("read()");
switch (fdsi.ssi_signo) {
case SIGWINCH: {
struct winsize ws;
rc = ioctl(STDIN_FILENO,TIOCGWINSZ,&ws);
sys_fail_if(rc < 0, "ioctl()");
rc = ioctl(master_fd, TIOCSWINSZ, &ws);
sys_fail_if(rc < 0, "ioctl()");
break;
}
case SIGINT:
case SIGTERM:
case SIGCHLD:
goto done;
}
if (fdsi.ssi_signo == (unsigned int) SIGRTMIN + 4)
goto done;
}
}
done:
rc = tcsetattr(STDIN_FILENO, TCSANOW, &stdin_attr);
sys_fail_if(rc < 0, "tcsetattr()");
}
void serve_pty(int fd) {
int rc;
pid_t pid;
sigset_t mask;
_close_ int sock = -1;
_close_ int epoll_fd = -1;
_close_ int signal_fd = -1;
_free_ char *path = NULL;
struct epoll_event sock_ev, signal_ev, events[3];
struct sockaddr_un servaddr_un;
pid = getpid();
memset(&servaddr_un, 0, sizeof(struct sockaddr_un));
rc = asprintf(&path, SOCKET_PATH, pid);
fail_if(rc < 0, "OOM");
if ((size_t) rc >= sizeof(servaddr_un.sun_path))
fail_printf("Socket path too long");
memset(&servaddr_un, 0, sizeof(struct sockaddr_un));
servaddr_un.sun_family = AF_UNIX;
snprintf(servaddr_un.sun_path, sizeof(servaddr_un.sun_path), "%s", path);
servaddr_un.sun_path[0] = '\0';
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0) sysf_printf("socket()");
rc = bind(sock, (struct sockaddr *) &servaddr_un, sizeof(struct sockaddr_un));
sys_fail_if(rc < 0, "bind()");
rc = listen(sock, 1);
sys_fail_if(rc < 0, "listen()");
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGTERM);
sigaddset(&mask, SIGCHLD);
sigaddset(&mask, SIGRTMIN + 4);
rc = sigprocmask(SIG_BLOCK, &mask, NULL);
sys_fail_if(rc < 0, "sigprocmask()");
signal_fd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
if (signal_fd < 0) sysf_printf("signalfd()");
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd < 0) sysf_printf("epoll_create1()");
sock_ev.events = EPOLLIN; sock_ev.data.fd = sock;
rc = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sock_ev.data.fd, &sock_ev);
sys_fail_if(rc < 0, "epoll_ctl(STDIN_FILENO)");
signal_ev.events = EPOLLIN; signal_ev.data.fd = signal_fd;
rc = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, signal_ev.data.fd, &signal_ev);
sys_fail_if(rc < 0, "epoll_ctl(signal_fd)");
while (1) {
rc = epoll_wait(epoll_fd, events, 1, -1);
sys_fail_if(rc < 0, "epoll_wait()");
if (events[0].data.fd == sock) {
socklen_t len;
struct ucred ucred;
_close_ int send_sock = -1;
send_sock = accept(sock, (struct sockaddr *) NULL,NULL);
if (send_sock < 0) sysf_printf("accept()");
len = sizeof(struct ucred);
rc = getsockopt(send_sock, SOL_SOCKET, SO_PEERCRED,
&ucred, &len);
sys_fail_if(rc < 0, "getsockopt(SO_PEERCRED)");
if (ucred.uid == geteuid())
send_fd(send_sock, fd);
}
if (events[0].data.fd == signal_fd) {
struct signalfd_siginfo fdsi;
rc = read(signal_fd, &fdsi, sizeof(fdsi));
if (rc != sizeof(fdsi)) sysf_printf("read()");
switch (fdsi.ssi_signo) {
case SIGINT:
case SIGTERM:
case SIGCHLD:
return;
}
if (fdsi.ssi_signo == (unsigned int) SIGRTMIN + 4)
return;
}
}
}
int recv_pty(pid_t pid) {
int rc;
_close_ int sock = -1;
_free_ char *path = NULL;
struct sockaddr_un servaddr_un;
rc = asprintf(&path, SOCKET_PATH, pid);
fail_if(rc < 0, "OOM");
if ((size_t) rc >= sizeof(servaddr_un.sun_path))
fail_printf("Socket path too long");
memset(&servaddr_un, 0, sizeof(struct sockaddr_un));
servaddr_un.sun_family = AF_UNIX;
snprintf(servaddr_un.sun_path, sizeof(servaddr_un.sun_path), "%s", path);
servaddr_un.sun_path[0] = '\0';
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0) sysf_printf("socket()");
rc = connect(sock, (struct sockaddr *) &servaddr_un, sizeof(struct sockaddr_un));
sys_fail_if(rc < 0, "connect()");
return recv_fd(sock);
}
static void send_fd(int sock, int fd) {
int rc;
union {
struct cmsghdr cmsg;
char control[CMSG_SPACE(sizeof(int))];
} msg_control;
struct iovec iov = {
.iov_base = "x",
.iov_len = 1
};
struct msghdr msg = {
.msg_name = NULL,
.msg_namelen = 0,
.msg_iov = &iov,
.msg_iovlen = 1,
.msg_flags = 0,
.msg_control = &msg_control,
.msg_controllen = sizeof(msg_control)
};
struct cmsghdr *cmsg;
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
rc = sendmsg(sock, &msg, 0);
sys_fail_if(rc < 0, "sendmsg()");
}
static int recv_fd(int sock) {
int rc;
union {
struct cmsghdr cmsg;
char control[CMSG_SPACE(sizeof(int))];
} msg_control;
struct cmsghdr *cmsg;
char buf[192];
struct iovec iov = {
.iov_base = buf,
.iov_len = sizeof(buf)
};
struct msghdr msg = {
.msg_name = NULL,
.msg_namelen = 0,
.msg_iov = &iov,
.msg_iovlen = 1,
.msg_flags = 0,
.msg_control = &msg_control,
.msg_controllen = sizeof(msg_control)
};
rc = recvmsg(sock, &msg, 0);
sys_fail_if(rc < 0, "recvmsg()");
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
int fd;
if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)) ||
cmsg->cmsg_level != SOL_SOCKET ||
cmsg->cmsg_type != SCM_RIGHTS)
continue;
fd = *((int *) CMSG_DATA(cmsg));
return fd;
}
return -1;
}

View file

@ -1,37 +0,0 @@
/*
* The process in the flask.
*
* Copyright (c) 2013, Alessandro Ghedini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
void open_master_pty(int *master_fd, char **master_name);
void open_slave_pty(const char *master_name);
void process_pty(int master_fd);
void serve_pty(int fd);
int recv_pty(pid_t pid);

View file

@ -1,121 +0,0 @@
/*
* The process in the flask.
*
* Copyright (c) 2013, Alessandro Ghedini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "printf.h"
#include "util.h"
int sync_init(int fd[2]) {
int rc;
rc = socketpair(AF_LOCAL, SOCK_STREAM, 0, fd);
sys_fail_if(rc < 0, "Error creating socket pair");
rc = fcntl(fd[0], F_SETFD, FD_CLOEXEC);
sys_fail_if(rc < 0, "Error setting FD_CLOEXEC");
return 0;
}
static int sync_wait(int fd, int seq) {
int rc;
int sync = -1;
rc = read(fd, &sync, sizeof(sync));
sys_fail_if(rc < 0, "Error reading from socket");
if (!rc)
return 0;
if (sync != seq)
fail_printf("Invalid sync sequence: %d != %d", seq, sync);
return 0;
}
int sync_wait_child(int fd[2], int seq) {
return sync_wait(fd[1], seq);
}
int sync_wait_parent(int fd[2], int seq) {
return sync_wait(fd[0], seq);
}
static int sync_wake(int fd, int seq) {
int rc;
rc = write(fd, &seq, sizeof(seq));
sys_fail_if(rc < 0, "Error waking process");
return 0;
}
int sync_wake_child(int fd[2], int seq) {
return sync_wake(fd[1], seq);
}
int sync_wake_parent(int fd[2], int seq) {
return sync_wake(fd[0], seq);
}
static int sync_barrier(int fd, int seq) {
if (sync_wake(fd, seq))
return -1;
return sync_wait(fd, seq + 1);
}
int sync_barrier_child(int fd[2], int seq) {
return sync_barrier(fd[1], seq);
}
int sync_barrier_parent(int fd[2], int seq) {
return sync_barrier(fd[0], seq);
}
void sync_close_child(int fd[2]) {
if (fd[0] != -1)
closep(&fd[0]);
}
void sync_close_parent(int fd[2]) {
if (fd[1] != -1)
closep(&fd[1]);
}
void sync_close(int fd[2]) {
sync_close_child(fd);
sync_close_parent(fd);
}

View file

@ -1,49 +0,0 @@
/*
* The process in the flask.
*
* Copyright (c) 2013, Alessandro Ghedini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
enum {
SYNC_START,
SYNC_DONE,
};
int sync_init(int fd[2]);
int sync_wait_child(int fd[2], int seq);
int sync_wait_parent(int fd[2], int seq);
int sync_wake_child(int fd[2], int seq);
int sync_wake_parent(int fd[2], int seq);
int sync_barrier_child(int fd[2], int seq);
int sync_barrier_parent(int fd[2], int seq);
void sync_close_child(int fd[2]);
void sync_close_parent(int fd[2]);
void sync_close(int fd[2]);

View file

@ -1,184 +0,0 @@
/*
* The process in the flask.
*
* Copyright (c) 2013, Alessandro Ghedini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdbool.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <pwd.h>
#include <grp.h>
#include "ut/utlist.h"
#include "user.h"
#include "path.h"
#include "printf.h"
#include "util.h"
struct user {
char type;
uid_t id;
uid_t host_id;
size_t count;
struct user *next, *prev;
};
void user_add_map(struct user **users, char type, uid_t id, uid_t host_id,
size_t count) {
struct user *usr = malloc(sizeof(struct user));
fail_if(!usr, "OOM");
usr->type = type;
usr->id = id;
usr->host_id = host_id;
usr->count = count;
DL_APPEND(*users, usr);
}
void setup_user_map2(struct user *users, char type, pid_t pid) {
int rc;
struct user *i;
_free_ char *map = strdup("");
_free_ char *have_cmd = on_path("newuidmap", NULL);
if (!have_cmd && geteuid() != 0)
fail_printf("Unprivileged containers need the newuidmap/newgidmap executables");
DL_FOREACH(users, i) {
char *tmp = NULL;
if (i->type != type)
continue;
rc = asprintf(&tmp, "%s%u %u %lu%c", map,
i->id, i->host_id, i->count,
have_cmd ? ' ' : '\n');
fail_if(rc < 0, "OOM");
freep(&map);
map = tmp;
}
if (have_cmd != NULL) {
_free_ char *cmd = NULL;
rc = asprintf(&map, "new%cidmap %u %s", type, pid, map);
fail_if(rc < 0, "OOM");
rc = system(map);
fail_if(rc != 0, "new%cidmap returned %d", type, rc);
} else {
_close_ int map_fd = -1;
_free_ char *map_file = NULL;
rc = asprintf(&map_file, "/proc/%d/%cid_map", pid, type);
fail_if(rc < 0, "OOM");
map_fd = open(map_file, O_RDWR);
sys_fail_if(map_fd < 0, "Error opening file '%s'", map_file);
rc = write(map_fd, map, strlen(map));
sys_fail_if(rc < 0, "Error writing to file '%s'", map_file);
}
}
void setup_user_map(struct user *users, pid_t pid) {
setup_user_map2(users, 'u', pid);
setup_user_map2(users, 'g', pid);
}
void setup_user(const char *user) {
int rc;
uid_t pw_uid;
uid_t pw_gid;
if (!user_get_uid_gid(user, &pw_uid, &pw_gid))
return;
rc = setresgid(pw_gid, pw_gid, pw_gid);
sys_fail_if(rc < 0, "Error settign GID");
rc = setresuid(pw_uid, pw_uid, pw_uid);
sys_fail_if(rc < 0, "Error setting UID");
rc = setgroups(0, NULL);
sys_fail_if(rc < 0, "Error setting groups");
}
bool user_get_mapped_root(struct user *users, char type, unsigned *id) {
struct user *i;
DL_FOREACH(users, i) {
if (i->type != type)
continue;
if (i->id != 0)
continue;
*id = i->host_id;
return true;
}
return false;
}
bool user_get_uid_gid(const char *user, uid_t *uid, gid_t *gid) {
struct passwd *pwd;
*uid = 0;
*gid = 0;
if (strncmp(user, "root", 5)) {
errno = 0;
pwd = getpwnam(user);
if (!pwd && !errno) {
err_printf("Invalid user '%s'", user);
return false;
}
sys_fail_if(!pwd && errno, "Error getting user");
*uid = pwd->pw_uid;
*gid = pwd->pw_gid;
}
return true;
}

View file

@ -1,40 +0,0 @@
/*
* The process in the flask.
*
* Copyright (c) 2013, Alessandro Ghedini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
struct user;
void user_add_map(struct user **users, char type, uid_t id, uid_t host_id,
size_t count);
void setup_user_map(struct user *users, pid_t pid);
void setup_user(const char *user);
bool user_get_mapped_root(struct user *users, char type, unsigned *id);
bool user_get_uid_gid(const char *user, uid_t *uid, gid_t *gid);

View file

@ -1,61 +0,0 @@
/*
* The process in the flask.
*
* Copyright (c) 2013, Alessandro Ghedini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdlib.h>
#include <string.h>
#include "printf.h"
#include "util.h"
size_t split_str(char *orig, char ***dest, char *needle) {
size_t size = 0;
char *token = NULL;
if (!orig || !dest)
return 0;
token = strtok(orig, needle);
do {
char **tmp = realloc(*dest, sizeof(char *) * (size + 1));
if (!tmp) {
if (*dest != NULL)
free(*dest);
return 0;
}
*dest = tmp;
(*dest)[size++] = token;
} while ((token = strtok(NULL, needle)) != NULL);
return size;
}

View file

@ -1,73 +0,0 @@
/*
* The process in the flask.
*
* Copyright (c) 2013, Alessandro Ghedini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdlib.h>
#include <unistd.h>
#define MIN(a, b) ((a) > (b) ? (b) : (a))
#define fail_if_(cond, fmt, ...) \
do { \
if ((cond)) \
fail_printf(fmt, __VA_ARGS__); \
} while (0)
#define fail_if(...) fail_if_(__VA_ARGS__, "")
#define sys_fail_if_(cond, fmt, ...) \
do { \
if ((cond)) \
sysf_printf(fmt, __VA_ARGS__); \
} while (0)
#define sys_fail_if(...) sys_fail_if_(__VA_ARGS__, "")
#define _free_ __attribute__((cleanup(freep)))
#define _close_ __attribute__((cleanup(closep)))
static inline void freep(void *p) {
if (!p) return;
free(*(void **) p);
*(void **)p = NULL;
}
static inline void closep(int *p) {
int rc;
if (*p == -1)
return;
rc = close(*p);
sys_fail_if(rc < 0, "Error closing fd");
*p = -1;
}
size_t split_str(char *orig, char ***dest, char *needle);

View file

@ -1,152 +0,0 @@
#!/bin/bash
#
# Build Debian packages inside Linux namespaces.
#
# Copyright (c) 2013, Alessandro Ghedini
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set -e
export LANG=C
export LC_ALL=C
if [ -z "$PFLASK_ROOTCMD" ]; then
PFLASK_ROOTCMD="sudo -E"
fi
if [ -z "$DEBUILD_DPKG_BUILDPACKAGE_OPTS" ]; then
DEBUILD_DPKG_BUILDPACKAGE_OPTS=""
fi
if [ -z "$DEBUILD_LINTIAN" ]; then
DEBUILD_LINTIAN="yes"
fi
if [ -z "$DEBUILD_LINTIAN_OPTS" ]; then
DEBUILD_LINTIAN_OPTS="-IE --pedantic"
fi
if [ -f "$HOME/.devscripts" ]; then
source "$HOME/.devscripts"
fi
if [ -f "/etc/devscripts.conf" ]; then
source "/etc/devscripts.conf"
fi
if [ -z "${DIST}" ] && [ -r "debian/changelog" ]; then
DIST=$(dpkg-parsechangelog | awk '/^Distribution: / {print $2}')
fi
: ${DIST:="$(lsb_release --short --codename)"}
: ${ARCH:="$(dpkg --print-architecture)"}
NAME="$DIST-$ARCH"
BASEDIR="/var/cache/pflask/base-$NAME"
BUILDDIR=$(pwd)
PKGDIR=$(basename $BUILDDIR)
RESDIR=$(dirname $BUILDDIR)
TMPDIR="/mnt"
if [ -r "debian/changelog" ]; then
SRC=$(dpkg-parsechangelog | sed -rne 's,^Source: (.+).*,\1,p')
VER=$(dpkg-parsechangelog | sed -rne 's,^Version: ([0-9]:)*(.+).*,\2,p')
elif [ "$1" != "--create" ] && [ "$1" != "--update" ]; then
echo "E: Can't find 'debian/changelog'"
exit -1
fi
CHANGES_FILE="${SRC}_${VER}_${ARCH}.changes"
DSC_FILE="${SRC}_${VER}.dsc"
DEBIAN_FILE="${SRC}_${VER}.debian.tar"
DIFF_FILE="${SRC}_${VER}.diff.tar.gz"
LOG_FILE="${SRC}_${VER}.build"
PFLASK_TOOL="$PFLASK_ROOTCMD $(which pflask)"
APT_TOOL="apt-get --no-install-recommends -y --force-yes"
TOOLS_TOOL="$APT_TOOL install devscripts equivs fakeroot lintian"
BDEPS_TOOL="mk-build-deps -r -i debian/control -t '$APT_TOOL'"
BUILD_TOOL="dpkg-buildpackage -tc -rfakeroot $DEBUILD_DPKG_BUILDPACKAGE_OPTS"
LINT_TOOL="lintian --allow-root $DEBUILD_LINTIAN_OPTS ../$CHANGES_FILE"
CHOWN_TOOL="chown --from=root:root"
DSIGN_TOOL="debsign"
if [ "$1" != "--create" ] && [ ! -d "$BASEDIR" ]; then
echo "E: Missing base root directory '$BASEDIR'"
exit -1
fi
BUILD_CMD="$TOOLS_TOOL && $BDEPS_TOOL && $BUILD_TOOL; FAIL=\$?"
BUILD_CMD="$BUILD_CMD && $CHOWN_TOOL -R $UID:$UID $TMPDIR/$PKGDIR"
FILES="$CHANGES_FILE $DSC_FILE $DEBIAN_FILE.gz $DEBIAN_FILE.xz $DIFF_FILE"
if [ -r "debian/control" ]; then
for PKG in `dh_listpackages`;
do
FILES="$FILES ${PKG}_${VER}_${ARCH}.deb"
FILES="$FILES ${PKG}_${VER}_${ARCH}.udeb"
FILES="$FILES ${PKG}_${VER}_all.deb"
FILES="$FILES ${PKG}_${VER}_all.udeb"
done
elif [ "$1" != "--create" ] && [ "$1" != "--update" ]; then
echo "E: Can't find 'debian/control'"
exit -1
fi
for FILE in $FILES
do
BUILD_CMD="$BUILD_CMD && [ -f $TMPDIR/$FILE ] && $CHOWN_TOOL $UID:$UID $TMPDIR/$FILE || true"
done
BUILD_CMD="$BUILD_CMD && [ \$FAIL = 0 ]"
if [ "$DEBUILD_LINTIAN" = "yes" ]; then
BUILD_CMD="$BUILD_CMD && $LINT_TOOL"
fi
if [ "$1" = "--create" ]; then
shift
$PFLASK_ROOTCMD debootstrap \
--include=devscripts,equivs,fakeroot,lintian \
--arch=$ARCH --variant=buildd $DIST $BASEDIR $@
elif [ "$1" = "--update" ]; then
$PFLASK_TOOL --chroot $BASEDIR \
-- \
sh -c "$APT_TOOL update && $APT_TOOL dist-upgrade && $APT_TOOL autoremove --purge && $APT_TOOL autoclean"
else
$PFLASK_TOOL --keepenv --chroot $BASEDIR --ephemeral \
--mount "bind:$RESDIR:$TMPDIR" \
--chdir "/mnt/$PKGDIR" \
-- \
sh -c "$BUILD_CMD" | tee "../$LOG_FILE"
$DSIGN_TOOL "$RESDIR/$CHANGES_FILE"
fi

179
wscript
View file

@ -1,179 +0,0 @@
# Copyright (C) 2015 Alessandro Ghedini <alessandro@ghedini.me>
# This file is released under the 2 clause BSD license, see COPYING
import re
from waflib import Utils
APPNAME = 'pflask'
VERSION = '0.2'
_INSTALL_DIRS_LIST = [
('bindir', '${DESTDIR}${PREFIX}/bin', 'binary files'),
('datadir', '${DESTDIR}${PREFIX}/share', 'data files'),
('docdir', '${DATADIR}/doc/pflask', 'documentation files'),
('mandir', '${DATADIR}/man', 'man pages '),
('zshdir', '${DATADIR}/zsh/site-functions','zsh completion functions'),
]
def options(opt):
opt.load('compiler_c')
group = opt.get_option_group("build and install options")
for ident, default, desc in _INSTALL_DIRS_LIST:
group.add_option('--{0}'.format(ident),
type = 'string',
dest = ident,
default = default,
help = 'directory for installing {0} [{1}]' \
.format(desc, default))
opt.add_option('--sanitize', action='store', default=None,
help='enable specified sanotizer (address, thread, ...)')
def configure(cfg):
def my_check_cc(ctx, dep, **kw_ext):
kw_ext['uselib_store'] = dep
if ctx.check_cc(**kw_ext):
ctx.env.deps.append(dep)
def my_check_cfg(ctx, dep, **kw_ext):
kw_ext['args'] = '--cflags --libs'
kw_ext['uselib_store'] = dep
if ctx.check_cfg(**kw_ext):
ctx.env.deps.append(dep)
def my_check_os(ctx):
ctx.env.deps.append("os-{0}".format(ctx.env.DEST_OS))
cfg.load('compiler_c')
for ident, _, _ in _INSTALL_DIRS_LIST:
varname = ident.upper()
cfg.env[varname] = getattr(cfg.options, ident)
# keep substituting vars, until the paths are fully expanded
while re.match('\$\{([^}]+)\}', cfg.env[varname]):
cfg.env[varname] = \
Utils.subst_vars(cfg.env[varname], cfg.env)
cfg.env.CFLAGS += [ '-Wall', '-Wextra', '-pedantic', '-g', '-std=gnu99' ]
cfg.env.CPPFLAGS += [ '-D_GNU_SOURCE' ]
cfg.env.deps = []
# OS
my_check_os(cfg)
# AuFS
my_check_cc(cfg, 'aufs', header_name='linux/aufs_type.h',
define_name='HAVE_AUFS', mandatory=False)
# libdbus
my_check_cfg(cfg, 'dbus', package='dbus-1', mandatory=False)
# libcap-ng
my_check_cfg(cfg, 'libcap-ng', package='libcap-ng', mandatory=False)
# sphinx
cfg.find_program('sphinx-build', mandatory=False)
if cfg.options.sanitize:
cflags = [ '-fsanitize=' + cfg.options.sanitize ]
lflags = [ '-fsanitize=' + cfg.options.sanitize ]
if cfg.options.sanitize == 'thread':
cflags += [ '-fPIC' ]
lflags += [ '-pie' ]
if cfg.check_cc(cflags=cflags,linkflags=lflags,mandatory=False):
cfg.env.CFLAGS += cflags
cfg.env.LINKFLAGS += lflags
def build(bld):
def filter_sources(ctx, sources):
def __source_file__(source):
if isinstance(source, tuple):
return source[0]
else:
return source
def __check_filter__(dependency):
if dependency.find('!') == 0:
dependency = dependency.lstrip('!')
return dependency not in ctx.env.deps
else:
return dependency in ctx.env.deps
def __unpack_and_check_filter__(source):
try:
_, dependency = source
return __check_filter__(dependency)
except ValueError:
return True
return [__source_file__(source) for source in sources \
if __unpack_and_check_filter__(source)]
sources = [
# sources
( 'src/capabilities.c', 'libcap-ng'),
( 'src/cgroup.c' ),
( 'src/cmdline.c' ),
( 'src/dev.c' ),
( 'src/machine.c', 'dbus' ),
( 'src/mount.c' ),
( 'src/netif.c' ),
( 'src/nl.c' ),
( 'src/path.c' ),
( 'src/pflask.c' ),
( 'src/printf.c' ),
( 'src/pty.c' ),
( 'src/sync.c' ),
( 'src/user.c' ),
( 'src/util.c' ),
]
bld.env.append_value('INCLUDES', ['deps', 'src'])
bld(
name = 'pflask',
features = 'c cprogram',
source = filter_sources(bld, sources),
target = 'pflask',
use = bld.env.deps,
install_path = bld.env.BINDIR,
)
bld.install_files('${BINDIR}', bld.path.ant_glob('tools/pflask-*'),
chmod=Utils.O755)
bld.install_as('${ZSHDIR}/_pflask', 'etc/pflask.zsh-completion')
if bld.env['SPHINX_BUILD']:
bld(
name = 'docs config',
features = 'subst',
source = 'docs/conf.py.in',
target = 'docs/conf.py',
VERSION = VERSION,
)
bld(
name = 'man docs',
cwd = 'docs',
rule = 'sphinx-build -c ../build/docs/ -b man . ../build/docs/man',
source = bld.path.ant_glob('docs/pflask.rst') +
bld.path.ant_glob('build/docs/conf.py'),
target = 'docs/man/pflask.1 docs/man/pflask-debuild.1',
install_path = bld.env.MANDIR
)
bld(
name = 'html docs',
cwd = 'docs',
rule = 'sphinx-build -c ../build/docs/ -b html . ../build/docs/html',
source = bld.path.ant_glob('docs/*.rst') +
bld.path.ant_glob('docs/README.rst') +
bld.path.ant_glob('build/docs/conf.py'),
target = 'docs/html/index.html',
)