Compare commits
No commits in common. "gh-pages" and "master" have entirely different histories.
14
.travis.yml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
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
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
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.
|
||||
308
README.rst
Normal file
|
|
@ -0,0 +1,308 @@
|
|||
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
|
||||
|
Before Width: | Height: | Size: 673 B |
|
|
@ -1,537 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
|
@ -1,238 +0,0 @@
|
|||
/*
|
||||
* 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();
|
||||
});
|
||||
|
Before Width: | Height: | Size: 368 B |
BIN
_static/down.png
|
Before Width: | Height: | Size: 363 B |
BIN
_static/file.png
|
Before Width: | Height: | Size: 392 B |
9404
_static/jquery.js
vendored
|
Before Width: | Height: | Size: 199 B |
|
|
@ -1,245 +0,0 @@
|
|||
/*
|
||||
* 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
|
Before Width: | Height: | Size: 199 B |
|
|
@ -1,63 +0,0 @@
|
|||
.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 */
|
||||
|
|
@ -1,622 +0,0 @@
|
|||
/*
|
||||
* 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();
|
||||
});
|
||||
|
Before Width: | Height: | Size: 372 B |
BIN
_static/up.png
|
Before Width: | Height: | Size: 363 B |
|
|
@ -1,808 +0,0 @@
|
|||
/*
|
||||
* 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 ▹\
|
||||
</a>\
|
||||
<a href="#" id="hc<%id%>" class="hide-propose-change">\
|
||||
Propose a change ▿\
|
||||
</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 ▹</a>\
|
||||
<a href="#" class="close-reply" id="cr<%id%>">reply ▿</a>\
|
||||
<a href="#" id="sp<%id%>" class="show-proposal">proposal ▹</a>\
|
||||
<a href="#" id="hp<%id%>" class="hide-proposal">proposal ▿</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);
|
||||
}
|
||||
});
|
||||
38
bootstrap.py
Executable file
|
|
@ -0,0 +1,38 @@
|
|||
#!/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
Normal file
|
|
@ -0,0 +1,757 @@
|
|||
/*
|
||||
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 */
|
||||
|
||||
196
docs/conf.py.in
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
# -*- 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
|
||||
|
|
@ -22,6 +22,11 @@ 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.
|
||||
|
|
@ -46,16 +51,22 @@ OPTIONS
|
|||
|
||||
.. option:: -u, --user=<user>
|
||||
|
||||
Run the command under the specified 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.
|
||||
|
||||
.. option:: -e, --user-map=<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.
|
||||
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.
|
||||
|
||||
Example: ``--user-map=0:100000,65536``
|
||||
Example: ``--user-map=0:100000:65536``
|
||||
|
||||
.. option:: -w, --ephemeral
|
||||
|
||||
|
|
@ -210,6 +221,26 @@ 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
|
||||
------
|
||||
|
||||
29
etc/pflask.zsh-completion
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#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
|
|
@ -1,400 +0,0 @@
|
|||
|
||||
<!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 — 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> »</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=<pid>
|
||||
</dt>
|
||||
|
||||
<dd><dl>
|
||||
|
||||
<dt><a href="pflask.html#cmdoption-pflask-a">pflask command line option</a>
|
||||
</dt>
|
||||
|
||||
</dl></dd>
|
||||
|
||||
<dt>
|
||||
-c, --chdir=<dir>
|
||||
</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=<map>
|
||||
</dt>
|
||||
|
||||
<dd><dl>
|
||||
|
||||
<dt><a href="pflask.html#cmdoption-pflask-e">pflask command line option</a>
|
||||
</dt>
|
||||
|
||||
</dl></dd>
|
||||
|
||||
<dt>
|
||||
-g, --cgroup=<controller>
|
||||
</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=<type>:<opts>
|
||||
</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[=<opts>]
|
||||
</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=<dir>
|
||||
</dt>
|
||||
|
||||
<dd><dl>
|
||||
|
||||
<dt><a href="pflask.html#cmdoption-pflask-r">pflask command line option</a>
|
||||
</dt>
|
||||
|
||||
</dl></dd>
|
||||
|
||||
<dt>
|
||||
-s, --setenv=<name>=<value>[,<name>=<value> ...]
|
||||
</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=<user>
|
||||
</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=<pid></a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="pflask.html#cmdoption-pflask-c">-c, --chdir=<dir></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=<map></a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="pflask.html#cmdoption-pflask-g">-g, --cgroup=<controller></a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="pflask.html#cmdoption-pflask-k">-k, --keepenv</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="pflask.html#cmdoption-pflask-m">-m, --mount=<type>:<opts></a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="pflask.html#cmdoption-pflask-n">-n, --netif[=<opts>]</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="pflask.html#cmdoption-pflask-r">-r, --chroot=<dir></a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="pflask.html#cmdoption-pflask-s">-s, --setenv=<name>=<value>[,<name>=<value> ...]</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="pflask.html#cmdoption-pflask-t">-t, --hostname</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="pflask.html#cmdoption-pflask-u">-u, --user=<user></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> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Copyright 2013, Alessandro Ghedini.
|
||||
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.2.3.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
337
index.html
|
|
@ -1,337 +0,0 @@
|
|||
<!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 — 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> »</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’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’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’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: <LOOPBACK,UP,LOWER_UP> 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: <LOOPBACK,UP,LOWER_UP> 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: <BROADCAST,MULTICAST> 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’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’s <tt class="docutils literal"><span class="pre">/tmp</span></tt> directory into the
|
||||
container’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’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’t be able to access the user’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">^@</span></tt> (Ctrl + @).</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’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 <<a class="reference external" href="mailto:alessandro%40ghedini.me">alessandro<span>@</span>ghedini<span>.</span>me</a>></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> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Copyright 2013, Alessandro Ghedini.
|
||||
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.2.3.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
# Sphinx inventory version 2
|
||||
# Project: pflask
|
||||
# Version: 0.2
|
||||
# The remainder of this file is compressed using zlib.
|
||||
xÚ<EFBFBD>”INÄ0D÷9…%XÀÂH½åô¢QKˆ¸íß±OòÀp{B;A„aQÞ–ë¹*þv8±\Ô}ˆÅÏv,žÈÓ<C388>.Î^I§šÎ›zÍøÀ¹S¨¥<sF¡š)<29>ŒC3tÈÅG=ݸðAˆR„Ôðñ^Köäö _vôš'÷âR§
|
||||
H¡~ú _€þô¿‚þ ôñ©)“:®ˆÕã—¤v$™(áœÇžÇ8¿~8è
šˆ"ù—Ž~Ñ(¸:Y…öÊT:¾æry@è
¡¨ÉQuüÎÁɸ ŒWôv¡¬8‘e|¦ÞùºÐPvªZbûOihøÜïT<C3AF>U7»Ûôf¹m°Õ~™†‘ü=Vu)ñ=ýŸÔMÚ*™DZNòËß´eç§f8Š‘†˜?G
|
||||
|
|
@ -1,172 +0,0 @@
|
|||
<!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 — 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> »</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 –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 <<a class="reference external" href="mailto:alessandro%40ghedini.me">alessandro<span>@</span>ghedini<span>.</span>me</a>></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 <<a class="reference external" href="mailto:alessandro%40ghedini.me">alessandro<span>@</span>ghedini<span>.</span>me</a>></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> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Copyright 2013, Alessandro Ghedini.
|
||||
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.2.3.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
338
pflask.html
|
|
@ -1,338 +0,0 @@
|
|||
<!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 — 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> »</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] [–] [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">=<dir></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">=<dir></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">=<type>:<opts></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">[=<opts>]</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">=<user></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">=<map></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">=<controller></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">=<pid></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>^@</cite> (Ctrl + @).</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">=<name>=<value>[,<name>=<value> ...]</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:<host_path>:<container_path></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:<host_path>:<container_path></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:<root_dir>:<dest>:<work_dir></span></tt></p>
|
||||
<p>Stacks the host <em>root_dir</em> directory on top of the container’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’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:<dest></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=<dev>:<name></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:<master>:<name></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:<master>:<name></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:<name_outside>:<name_inside></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 <<a class="reference external" href="mailto:alessandro%40ghedini.me">alessandro<span>@</span>ghedini<span>.</span>me</a>></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 <<a class="reference external" href="mailto:alessandro%40ghedini.me">alessandro<span>@</span>ghedini<span>.</span>me</a>></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> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Copyright 2013, Alessandro Ghedini.
|
||||
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.2.3.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
99
search.html
|
|
@ -1,99 +0,0 @@
|
|||
<!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 — 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> »</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> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Copyright 2013, Alessandro Ghedini.
|
||||
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.2.3.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
111
src/capabilities.c
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* 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");
|
||||
}
|
||||
34
src/capabilities.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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);
|
||||
128
src/cgroup.c
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* 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");
|
||||
}
|
||||
36
src/cgroup.h
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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);
|
||||
1296
src/cmdline.c
Normal file
44
src/cmdline.ggo
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
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
|
||||
256
src/cmdline.h
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
/** @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
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
35
src/dev.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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);
|
||||
125
src/machine.c
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
31
src/machine.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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);
|
||||
309
src/mount.c
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
38
src/mount.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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);
|
||||
315
src/netif.c
Normal file
|
|
@ -0,0 +1,315 @@
|
|||
/*
|
||||
* 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");
|
||||
}
|
||||
46
src/netif.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* 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");
|
||||
}
|
||||
51
src/nl.h
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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);
|
||||
153
src/path.c
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* 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] == '/';
|
||||
}
|
||||
37
src/path.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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);
|
||||
406
src/pflask.c
Normal file
|
|
@ -0,0 +1,406 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
113
src/printf.c
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
48
src/printf.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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 "[1;32m"
|
||||
#define COLOR_YELLOW "[1;33m"
|
||||
#define COLOR_RED "[1;31m"
|
||||
#define COLOR_BGRED "[1;41m"
|
||||
#define COLOR_OFF "[0m"
|
||||
|
||||
#define LINE_CLEAR "[0K"
|
||||
|
||||
#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
Normal file
|
|
@ -0,0 +1,445 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
37
src/pty.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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);
|
||||
121
src/sync.c
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
49
src/sync.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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]);
|
||||
184
src/user.c
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
40
src/user.h
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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);
|
||||
61
src/util.c
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
73
src/util.h
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* 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);
|
||||
152
tools/pflask-debuild
Executable file
|
|
@ -0,0 +1,152 @@
|
|||
#!/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
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
# 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',
|
||||
)
|
||||