Import Upstream version 0.4.0
This commit is contained in:
commit
5686b945c0
|
@ -0,0 +1 @@
|
||||||
|
((c-mode . ((indent-tabs-mode . nil) (c-file-style . "gnu"))))
|
|
@ -0,0 +1,6 @@
|
||||||
|
[*.[ch]]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
indent_brace_style = gnu
|
||||||
|
|
|
@ -0,0 +1,481 @@
|
||||||
|
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1991 Free Software Foundation, Inc.
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
[This is the first released version of the library GPL. It is
|
||||||
|
numbered 2 because it goes with version 2 of the ordinary GPL.]
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
Licenses are intended to guarantee your freedom to share and change
|
||||||
|
free software--to make sure the software is free for all its users.
|
||||||
|
|
||||||
|
This license, the Library General Public License, applies to some
|
||||||
|
specially designated Free Software Foundation software, and to any
|
||||||
|
other libraries whose authors decide to use it. You can use it for
|
||||||
|
your libraries, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
this service if you wish), that you receive source code or can get it
|
||||||
|
if you want it, that you can change the software or use pieces of it
|
||||||
|
in new free programs; and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
anyone to deny you these rights or to ask you to surrender the rights.
|
||||||
|
These restrictions translate to certain responsibilities for you if
|
||||||
|
you distribute copies of the library, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of the library, whether gratis
|
||||||
|
or for a fee, you must give the recipients all the rights that we gave
|
||||||
|
you. You must make sure that they, too, receive or can get the source
|
||||||
|
code. If you link a program with the library, you must provide
|
||||||
|
complete object files to the recipients so that they can relink them
|
||||||
|
with the library, after making changes to the library and recompiling
|
||||||
|
it. And you must show them these terms so they know their rights.
|
||||||
|
|
||||||
|
Our method of protecting your rights has two steps: (1) copyright
|
||||||
|
the library, and (2) offer you this license which gives you legal
|
||||||
|
permission to copy, distribute and/or modify the library.
|
||||||
|
|
||||||
|
Also, for each distributor's protection, we want to make certain
|
||||||
|
that everyone understands that there is no warranty for this free
|
||||||
|
library. If the library is modified by someone else and passed on, we
|
||||||
|
want its recipients to know that what they have is not the original
|
||||||
|
version, so that any problems introduced by others will not reflect on
|
||||||
|
the original authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software
|
||||||
|
patents. We wish to avoid the danger that companies distributing free
|
||||||
|
software will individually obtain patent licenses, thus in effect
|
||||||
|
transforming the program into proprietary software. To prevent this,
|
||||||
|
we have made it clear that any patent must be licensed for everyone's
|
||||||
|
free use or not licensed at all.
|
||||||
|
|
||||||
|
Most GNU software, including some libraries, is covered by the ordinary
|
||||||
|
GNU General Public License, which was designed for utility programs. This
|
||||||
|
license, the GNU Library General Public License, applies to certain
|
||||||
|
designated libraries. This license is quite different from the ordinary
|
||||||
|
one; be sure to read it in full, and don't assume that anything in it is
|
||||||
|
the same as in the ordinary license.
|
||||||
|
|
||||||
|
The reason we have a separate public license for some libraries is that
|
||||||
|
they blur the distinction we usually make between modifying or adding to a
|
||||||
|
program and simply using it. Linking a program with a library, without
|
||||||
|
changing the library, is in some sense simply using the library, and is
|
||||||
|
analogous to running a utility program or application program. However, in
|
||||||
|
a textual and legal sense, the linked executable is a combined work, a
|
||||||
|
derivative of the original library, and the ordinary General Public License
|
||||||
|
treats it as such.
|
||||||
|
|
||||||
|
Because of this blurred distinction, using the ordinary General
|
||||||
|
Public License for libraries did not effectively promote software
|
||||||
|
sharing, because most developers did not use the libraries. We
|
||||||
|
concluded that weaker conditions might promote sharing better.
|
||||||
|
|
||||||
|
However, unrestricted linking of non-free programs would deprive the
|
||||||
|
users of those programs of all benefit from the free status of the
|
||||||
|
libraries themselves. This Library General Public License is intended to
|
||||||
|
permit developers of non-free programs to use free libraries, while
|
||||||
|
preserving your freedom as a user of such programs to change the free
|
||||||
|
libraries that are incorporated in them. (We have not seen how to achieve
|
||||||
|
this as regards changes in header files, but we have achieved it as regards
|
||||||
|
changes in the actual functions of the Library.) The hope is that this
|
||||||
|
will lead to faster development of free libraries.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow. Pay close attention to the difference between a
|
||||||
|
"work based on the library" and a "work that uses the library". The
|
||||||
|
former contains code derived from the library, while the latter only
|
||||||
|
works together with the library.
|
||||||
|
|
||||||
|
Note that it is possible for a library to be covered by the ordinary
|
||||||
|
General Public License rather than by this special one.
|
||||||
|
|
||||||
|
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License Agreement applies to any software library which
|
||||||
|
contains a notice placed by the copyright holder or other authorized
|
||||||
|
party saying it may be distributed under the terms of this Library
|
||||||
|
General Public License (also called "this License"). Each licensee is
|
||||||
|
addressed as "you".
|
||||||
|
|
||||||
|
A "library" means a collection of software functions and/or data
|
||||||
|
prepared so as to be conveniently linked with application programs
|
||||||
|
(which use some of those functions and data) to form executables.
|
||||||
|
|
||||||
|
The "Library", below, refers to any such software library or work
|
||||||
|
which has been distributed under these terms. A "work based on the
|
||||||
|
Library" means either the Library or any derivative work under
|
||||||
|
copyright law: that is to say, a work containing the Library or a
|
||||||
|
portion of it, either verbatim or with modifications and/or translated
|
||||||
|
straightforwardly into another language. (Hereinafter, translation is
|
||||||
|
included without limitation in the term "modification".)
|
||||||
|
|
||||||
|
"Source code" for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For a library, complete source code means
|
||||||
|
all the source code for all modules it contains, plus any associated
|
||||||
|
interface definition files, plus the scripts used to control compilation
|
||||||
|
and installation of the library.
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running a program using the Library is not restricted, and output from
|
||||||
|
such a program is covered only if its contents constitute a work based
|
||||||
|
on the Library (independent of the use of the Library in a tool for
|
||||||
|
writing it). Whether that is true depends on what the Library does
|
||||||
|
and what the program that uses the Library does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Library's
|
||||||
|
complete source code as you receive it, in any medium, provided that
|
||||||
|
you conspicuously and appropriately publish on each copy an
|
||||||
|
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||||
|
all the notices that refer to this License and to the absence of any
|
||||||
|
warranty; and distribute a copy of this License along with the
|
||||||
|
Library.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy,
|
||||||
|
and you may at your option offer warranty protection in exchange for a
|
||||||
|
fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Library or any portion
|
||||||
|
of it, thus forming a work based on the Library, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The modified work must itself be a software library.
|
||||||
|
|
||||||
|
b) You must cause the files modified to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
c) You must cause the whole of the work to be licensed at no
|
||||||
|
charge to all third parties under the terms of this License.
|
||||||
|
|
||||||
|
d) If a facility in the modified Library refers to a function or a
|
||||||
|
table of data to be supplied by an application program that uses
|
||||||
|
the facility, other than as an argument passed when the facility
|
||||||
|
is invoked, then you must make a good faith effort to ensure that,
|
||||||
|
in the event an application does not supply such function or
|
||||||
|
table, the facility still operates, and performs whatever part of
|
||||||
|
its purpose remains meaningful.
|
||||||
|
|
||||||
|
(For example, a function in a library to compute square roots has
|
||||||
|
a purpose that is entirely well-defined independent of the
|
||||||
|
application. Therefore, Subsection 2d requires that any
|
||||||
|
application-supplied function or table used by this function must
|
||||||
|
be optional: if the application does not supply it, the square
|
||||||
|
root function must still compute square roots.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Library,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Library, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote
|
||||||
|
it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Library.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Library
|
||||||
|
with the Library (or with a work based on the Library) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||||
|
License instead of this License to a given copy of the Library. To do
|
||||||
|
this, you must alter all the notices that refer to this License, so
|
||||||
|
that they refer to the ordinary GNU General Public License, version 2,
|
||||||
|
instead of to this License. (If a newer version than version 2 of the
|
||||||
|
ordinary GNU General Public License has appeared, then you can specify
|
||||||
|
that version instead if you wish.) Do not make any other change in
|
||||||
|
these notices.
|
||||||
|
|
||||||
|
Once this change is made in a given copy, it is irreversible for
|
||||||
|
that copy, so the ordinary GNU General Public License applies to all
|
||||||
|
subsequent copies and derivative works made from that copy.
|
||||||
|
|
||||||
|
This option is useful when you wish to copy part of the code of
|
||||||
|
the Library into a program that is not a library.
|
||||||
|
|
||||||
|
4. You may copy and distribute the Library (or a portion or
|
||||||
|
derivative of it, under Section 2) in object code or executable form
|
||||||
|
under the terms of Sections 1 and 2 above provided that you accompany
|
||||||
|
it with the complete corresponding machine-readable source code, which
|
||||||
|
must be distributed under the terms of Sections 1 and 2 above on a
|
||||||
|
medium customarily used for software interchange.
|
||||||
|
|
||||||
|
If distribution of object code is made by offering access to copy
|
||||||
|
from a designated place, then offering equivalent access to copy the
|
||||||
|
source code from the same place satisfies the requirement to
|
||||||
|
distribute the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
5. A program that contains no derivative of any portion of the
|
||||||
|
Library, but is designed to work with the Library by being compiled or
|
||||||
|
linked with it, is called a "work that uses the Library". Such a
|
||||||
|
work, in isolation, is not a derivative work of the Library, and
|
||||||
|
therefore falls outside the scope of this License.
|
||||||
|
|
||||||
|
However, linking a "work that uses the Library" with the Library
|
||||||
|
creates an executable that is a derivative of the Library (because it
|
||||||
|
contains portions of the Library), rather than a "work that uses the
|
||||||
|
library". The executable is therefore covered by this License.
|
||||||
|
Section 6 states terms for distribution of such executables.
|
||||||
|
|
||||||
|
When a "work that uses the Library" uses material from a header file
|
||||||
|
that is part of the Library, the object code for the work may be a
|
||||||
|
derivative work of the Library even though the source code is not.
|
||||||
|
Whether this is true is especially significant if the work can be
|
||||||
|
linked without the Library, or if the work is itself a library. The
|
||||||
|
threshold for this to be true is not precisely defined by law.
|
||||||
|
|
||||||
|
If such an object file uses only numerical parameters, data
|
||||||
|
structure layouts and accessors, and small macros and small inline
|
||||||
|
functions (ten lines or less in length), then the use of the object
|
||||||
|
file is unrestricted, regardless of whether it is legally a derivative
|
||||||
|
work. (Executables containing this object code plus portions of the
|
||||||
|
Library will still fall under Section 6.)
|
||||||
|
|
||||||
|
Otherwise, if the work is a derivative of the Library, you may
|
||||||
|
distribute the object code for the work under the terms of Section 6.
|
||||||
|
Any executables containing that work also fall under Section 6,
|
||||||
|
whether or not they are linked directly with the Library itself.
|
||||||
|
|
||||||
|
6. As an exception to the Sections above, you may also compile or
|
||||||
|
link a "work that uses the Library" with the Library to produce a
|
||||||
|
work containing portions of the Library, and distribute that work
|
||||||
|
under terms of your choice, provided that the terms permit
|
||||||
|
modification of the work for the customer's own use and reverse
|
||||||
|
engineering for debugging such modifications.
|
||||||
|
|
||||||
|
You must give prominent notice with each copy of the work that the
|
||||||
|
Library is used in it and that the Library and its use are covered by
|
||||||
|
this License. You must supply a copy of this License. If the work
|
||||||
|
during execution displays copyright notices, you must include the
|
||||||
|
copyright notice for the Library among them, as well as a reference
|
||||||
|
directing the user to the copy of this License. Also, you must do one
|
||||||
|
of these things:
|
||||||
|
|
||||||
|
a) Accompany the work with the complete corresponding
|
||||||
|
machine-readable source code for the Library including whatever
|
||||||
|
changes were used in the work (which must be distributed under
|
||||||
|
Sections 1 and 2 above); and, if the work is an executable linked
|
||||||
|
with the Library, with the complete machine-readable "work that
|
||||||
|
uses the Library", as object code and/or source code, so that the
|
||||||
|
user can modify the Library and then relink to produce a modified
|
||||||
|
executable containing the modified Library. (It is understood
|
||||||
|
that the user who changes the contents of definitions files in the
|
||||||
|
Library will not necessarily be able to recompile the application
|
||||||
|
to use the modified definitions.)
|
||||||
|
|
||||||
|
b) Accompany the work with a written offer, valid for at
|
||||||
|
least three years, to give the same user the materials
|
||||||
|
specified in Subsection 6a, above, for a charge no more
|
||||||
|
than the cost of performing this distribution.
|
||||||
|
|
||||||
|
c) If distribution of the work is made by offering access to copy
|
||||||
|
from a designated place, offer equivalent access to copy the above
|
||||||
|
specified materials from the same place.
|
||||||
|
|
||||||
|
d) Verify that the user has already received a copy of these
|
||||||
|
materials or that you have already sent this user a copy.
|
||||||
|
|
||||||
|
For an executable, the required form of the "work that uses the
|
||||||
|
Library" must include any data and utility programs needed for
|
||||||
|
reproducing the executable from it. However, as a special exception,
|
||||||
|
the source code distributed need not include anything that is normally
|
||||||
|
distributed (in either source or binary form) with the major
|
||||||
|
components (compiler, kernel, and so on) of the operating system on
|
||||||
|
which the executable runs, unless that component itself accompanies
|
||||||
|
the executable.
|
||||||
|
|
||||||
|
It may happen that this requirement contradicts the license
|
||||||
|
restrictions of other proprietary libraries that do not normally
|
||||||
|
accompany the operating system. Such a contradiction means you cannot
|
||||||
|
use both them and the Library together in an executable that you
|
||||||
|
distribute.
|
||||||
|
|
||||||
|
7. You may place library facilities that are a work based on the
|
||||||
|
Library side-by-side in a single library together with other library
|
||||||
|
facilities not covered by this License, and distribute such a combined
|
||||||
|
library, provided that the separate distribution of the work based on
|
||||||
|
the Library and of the other library facilities is otherwise
|
||||||
|
permitted, and provided that you do these two things:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work
|
||||||
|
based on the Library, uncombined with any other library
|
||||||
|
facilities. This must be distributed under the terms of the
|
||||||
|
Sections above.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library of the fact
|
||||||
|
that part of it is a work based on the Library, and explaining
|
||||||
|
where to find the accompanying uncombined form of the same work.
|
||||||
|
|
||||||
|
8. You may not copy, modify, sublicense, link with, or distribute
|
||||||
|
the Library except as expressly provided under this License. Any
|
||||||
|
attempt otherwise to copy, modify, sublicense, link with, or
|
||||||
|
distribute the Library is void, and will automatically terminate your
|
||||||
|
rights under this License. However, parties who have received copies,
|
||||||
|
or rights, from you under this License will not have their licenses
|
||||||
|
terminated so long as such parties remain in full compliance.
|
||||||
|
|
||||||
|
9. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Library or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Library (or any work based on the
|
||||||
|
Library), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Library or works based on it.
|
||||||
|
|
||||||
|
10. Each time you redistribute the Library (or any work based on the
|
||||||
|
Library), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute, link with or modify the Library
|
||||||
|
subject to these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties to
|
||||||
|
this License.
|
||||||
|
|
||||||
|
11. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Library at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Library by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Library.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under any
|
||||||
|
particular circumstance, the balance of the section is intended to apply,
|
||||||
|
and the section as a whole is intended to apply in other circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
12. If the distribution and/or use of the Library is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Library under this License may add
|
||||||
|
an explicit geographical distribution limitation excluding those countries,
|
||||||
|
so that distribution is permitted only in or among countries not thus
|
||||||
|
excluded. In such case, this License incorporates the limitation as if
|
||||||
|
written in the body of this License.
|
||||||
|
|
||||||
|
13. The Free Software Foundation may publish revised and/or new
|
||||||
|
versions of the Library General Public License from time to time.
|
||||||
|
Such new versions will be similar in spirit to the present version,
|
||||||
|
but may differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Library
|
||||||
|
specifies a version number of this License which applies to it and
|
||||||
|
"any later version", you have the option of following the terms and
|
||||||
|
conditions either of that version or of any later version published by
|
||||||
|
the Free Software Foundation. If the Library does not specify a
|
||||||
|
license version number, you may choose any version ever published by
|
||||||
|
the Free Software Foundation.
|
||||||
|
|
||||||
|
14. If you wish to incorporate parts of the Library into other free
|
||||||
|
programs whose distribution conditions are incompatible with these,
|
||||||
|
write to the author to ask for permission. For software which is
|
||||||
|
copyrighted by the Free Software Foundation, write to the Free
|
||||||
|
Software Foundation; we sometimes make exceptions for this. Our
|
||||||
|
decision will be guided by the two goals of preserving the free status
|
||||||
|
of all derivatives of our free software and of promoting the sharing
|
||||||
|
and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||||
|
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||||
|
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||||
|
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||||
|
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||||
|
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||||
|
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||||
|
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||||
|
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||||
|
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||||
|
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||||
|
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||||
|
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||||
|
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||||
|
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||||
|
DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Libraries
|
||||||
|
|
||||||
|
If you develop a new library, and you want it to be of the greatest
|
||||||
|
possible use to the public, we recommend making it free software that
|
||||||
|
everyone can redistribute and change. You can do so by permitting
|
||||||
|
redistribution under these terms (or, alternatively, under the terms of the
|
||||||
|
ordinary General Public License).
|
||||||
|
|
||||||
|
To apply these terms, attach the following notices to the library. It is
|
||||||
|
safest to attach them to the start of each source file to most effectively
|
||||||
|
convey the exclusion of warranty; and each file should have at least the
|
||||||
|
"copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the library's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||||
|
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 1 April 1990
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
That's all there is to it!
|
|
@ -0,0 +1,13 @@
|
||||||
|
|
||||||
|
bwrap_SOURCES = \
|
||||||
|
$(bwrap_srcpath)/bubblewrap.c \
|
||||||
|
$(bwrap_srcpath)/bind-mount.h \
|
||||||
|
$(bwrap_srcpath)/bind-mount.c \
|
||||||
|
$(bwrap_srcpath)/network.h \
|
||||||
|
$(bwrap_srcpath)/network.c \
|
||||||
|
$(bwrap_srcpath)/utils.h \
|
||||||
|
$(bwrap_srcpath)/utils.c \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
bwrap_CFLAGS = $(AM_CFLAGS)
|
||||||
|
bwrap_LDADD = $(SELINUX_LIBS)
|
|
@ -0,0 +1,18 @@
|
||||||
|
XSLTPROC = xsltproc
|
||||||
|
|
||||||
|
XSLTPROC_FLAGS = \
|
||||||
|
--nonet \
|
||||||
|
--stringparam man.output.quietly 1 \
|
||||||
|
--stringparam funcsynopsis.style ansi \
|
||||||
|
--stringparam man.th.extra1.suppress 1 \
|
||||||
|
--stringparam man.authors.section.enabled 0 \
|
||||||
|
--stringparam man.copyright.section.enabled 0
|
||||||
|
|
||||||
|
.xml.1:
|
||||||
|
$(XSLTPROC) $(XSLTPROC_FLAGS) http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $<
|
||||||
|
|
||||||
|
if ENABLE_MAN
|
||||||
|
man_MANS = bwrap.1
|
||||||
|
CLEANFILES += $(man_MANS)
|
||||||
|
endif
|
||||||
|
EXTRA_DIST += bwrap.xml
|
|
@ -0,0 +1,61 @@
|
||||||
|
AM_CFLAGS = $(WARN_CFLAGS)
|
||||||
|
CLEANFILES =
|
||||||
|
EXTRA_DIST = \
|
||||||
|
.dir-locals.el \
|
||||||
|
.editorconfig \
|
||||||
|
README.md \
|
||||||
|
autogen.sh \
|
||||||
|
demos/bubblewrap-shell.sh \
|
||||||
|
demos/flatpak-run.sh \
|
||||||
|
demos/flatpak.bpf \
|
||||||
|
demos/userns-block-fd.py \
|
||||||
|
packaging/bubblewrap.spec \
|
||||||
|
uncrustify.cfg \
|
||||||
|
uncrustify.sh \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
GITIGNOREFILES = build-aux/ gtk-doc.make config.h.in aclocal.m4
|
||||||
|
|
||||||
|
bin_PROGRAMS = bwrap
|
||||||
|
|
||||||
|
bwrap_srcpath := $(srcdir)
|
||||||
|
include Makefile-bwrap.am
|
||||||
|
|
||||||
|
install-exec-hook:
|
||||||
|
if PRIV_MODE_SETUID
|
||||||
|
$(SUDO_BIN) chown root $(DESTDIR)$(bindir)/bwrap
|
||||||
|
$(SUDO_BIN) chmod u+s $(DESTDIR)$(bindir)/bwrap
|
||||||
|
endif
|
||||||
|
|
||||||
|
check_PROGRAMS = test-bwrap
|
||||||
|
|
||||||
|
test-bwrap: bwrap
|
||||||
|
rm -rf test-bwrap
|
||||||
|
cp bwrap test-bwrap
|
||||||
|
if PRIV_MODE_SETUID
|
||||||
|
$(SUDO_BIN) chown root test-bwrap
|
||||||
|
$(SUDO_BIN) chmod u+s test-bwrap
|
||||||
|
endif
|
||||||
|
|
||||||
|
test_bwrap_SOURCES=
|
||||||
|
|
||||||
|
include Makefile-docs.am
|
||||||
|
|
||||||
|
LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/build-aux/tap-driver.sh
|
||||||
|
LOG_COMPILER =
|
||||||
|
TESTS = tests/test-run.sh
|
||||||
|
TESTS_ENVIRONMENT = BWRAP=$(abs_top_builddir)/test-bwrap
|
||||||
|
|
||||||
|
EXTRA_DIST += $(TESTS)
|
||||||
|
EXTRA_DIST += tests/libtest-core.sh
|
||||||
|
|
||||||
|
if ENABLE_BASH_COMPLETION
|
||||||
|
bashcompletiondir = $(BASH_COMPLETION_DIR)
|
||||||
|
dist_bashcompletion_DATA = completions/bash/bwrap
|
||||||
|
endif
|
||||||
|
|
||||||
|
-include $(top_srcdir)/git.mk
|
||||||
|
|
||||||
|
AM_DISTCHECK_CONFIGURE_FLAGS = \
|
||||||
|
--with-bash-completion-dir="\$(datadir)"/bash-completion/ \
|
||||||
|
$(NULL)
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,180 @@
|
||||||
|
Bubblewrap
|
||||||
|
==========
|
||||||
|
|
||||||
|
Many container runtime tools like `systemd-nspawn`, `docker`,
|
||||||
|
etc. focus on providing infrastructure for system administrators and
|
||||||
|
orchestration tools (e.g. Kubernetes) to run containers.
|
||||||
|
|
||||||
|
These tools are not suitable to give to unprivileged users, because it
|
||||||
|
is trivial to turn such access into to a fully privileged root shell
|
||||||
|
on the host.
|
||||||
|
|
||||||
|
User namespaces
|
||||||
|
---------------
|
||||||
|
|
||||||
|
There is an effort in the Linux kernel called
|
||||||
|
[user namespaces](https://www.google.com/search?q=user+namespaces+site%3Ahttps%3A%2F%2Flwn.net)
|
||||||
|
which attempts to allow unprivileged users to use container features.
|
||||||
|
While significant progress has been made, there are
|
||||||
|
[still concerns](https://lwn.net/Articles/673597/) about it, and
|
||||||
|
it is not available to unprivileged users in several production distributions
|
||||||
|
such as CentOS/Red Hat Enterprise Linux 7, Debian Jessie, etc.
|
||||||
|
|
||||||
|
See for example
|
||||||
|
[CVE-2016-3135](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-3135)
|
||||||
|
which is a local root vulnerability introduced by userns.
|
||||||
|
[This March 2016 post](https://lkml.org/lkml/2016/3/9/555) has some
|
||||||
|
more discussion.
|
||||||
|
|
||||||
|
Bubblewrap could be viewed as setuid implementation of a *subset* of
|
||||||
|
user namespaces. Emphasis on subset - specifically relevant to the
|
||||||
|
above CVE, bubblewrap does not allow control over iptables.
|
||||||
|
|
||||||
|
The original bubblewrap code existed before user namespaces - it inherits code from
|
||||||
|
[xdg-app helper](https://cgit.freedesktop.org/xdg-app/xdg-app/tree/common/xdg-app-helper.c)
|
||||||
|
which in turn distantly derives from
|
||||||
|
[linux-user-chroot](https://git.gnome.org/browse/linux-user-chroot).
|
||||||
|
|
||||||
|
Security
|
||||||
|
--------
|
||||||
|
|
||||||
|
The maintainers of this tool believe that it does not, even when used
|
||||||
|
in combination with typical software installed on that distribution,
|
||||||
|
allow privilege escalation. It may increase the ability of a logged
|
||||||
|
in user to perform denial of service attacks, however.
|
||||||
|
|
||||||
|
In particular, bubblewrap uses `PR_SET_NO_NEW_PRIVS` to turn off
|
||||||
|
setuid binaries, which is the [traditional way](https://en.wikipedia.org/wiki/Chroot#Limitations) to get out of things
|
||||||
|
like chroots.
|
||||||
|
|
||||||
|
Users
|
||||||
|
-----
|
||||||
|
|
||||||
|
This program can be shared by all container tools which perform
|
||||||
|
non-root operation, such as:
|
||||||
|
|
||||||
|
- [Flatpak](http://www.flatpak.org)
|
||||||
|
- [rpm-ostree unprivileged](https://github.com/projectatomic/rpm-ostree/pull/209)
|
||||||
|
- [bwrap-oci](https://github.com/projectatomic/bwrap-oci)
|
||||||
|
|
||||||
|
We would also like to see this be available in Kubernetes/OpenShift
|
||||||
|
clusters. Having the ability for unprivileged users to use container
|
||||||
|
features would make it significantly easier to do interactive
|
||||||
|
debugging scenarios and the like.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
bubblewrap works by creating a new, completely empty, mount
|
||||||
|
namespace where the root is on a tmpfs that is invisible from the
|
||||||
|
host, and will be automatically cleaned up when the last process
|
||||||
|
exits. You can then use commandline options to construct the root
|
||||||
|
filesystem and process environment and command to run in the
|
||||||
|
namespace.
|
||||||
|
|
||||||
|
There's a larger [demo script](./demos/bubblewrap-shell.sh) in the
|
||||||
|
source code, but here's a trimmed down version which runs
|
||||||
|
a new shell reusing the host's `/usr`.
|
||||||
|
|
||||||
|
```
|
||||||
|
bwrap --ro-bind /usr /usr --symlink usr/lib64 /lib64 --proc /proc --dev /dev --unshare-pid bash
|
||||||
|
```
|
||||||
|
|
||||||
|
This is an incomplete example, but useful for purposes of
|
||||||
|
illustration. More often, rather than creating a container using the
|
||||||
|
host's filesystem tree, you want to target a chroot. There, rather
|
||||||
|
than creating the symlink `lib64 -> usr/lib64` in the tmpfs, you might
|
||||||
|
have already created it in the target rootfs.
|
||||||
|
|
||||||
|
Sandboxing
|
||||||
|
----------
|
||||||
|
|
||||||
|
The goal of bubblewrap is to run an application in a sandbox, where it
|
||||||
|
has restricted access to parts of the operating system or user data
|
||||||
|
such as the home directory.
|
||||||
|
|
||||||
|
bubblewrap always creates a new mount namespace, and the user can specify
|
||||||
|
exactly what parts of the filesystem should be visible in the sandbox.
|
||||||
|
Any such directories you specify mounted `nodev` by default, and can be made readonly.
|
||||||
|
|
||||||
|
Additionally you can use these kernel features:
|
||||||
|
|
||||||
|
User namespaces ([CLONE_NEWUSER](http://linux.die.net/man/2/clone)): This hides all but the current uid and gid from the
|
||||||
|
sandbox. You can also change what the value of uid/gid should be in the sandbox.
|
||||||
|
|
||||||
|
IPC namespaces ([CLONE_NEWIPC](http://linux.die.net/man/2/clone)): The sandbox will get its own copy of all the
|
||||||
|
different forms of IPCs, like SysV shared memory and semaphores.
|
||||||
|
|
||||||
|
PID namespaces ([CLONE_NEWPID](http://linux.die.net/man/2/clone)): The sandbox will not see any processes outside the sandbox. Additionally, bubblewrap will run a trivial pid1 inside your container to handle the requirements of reaping children in the sandbox. This avoids what is known now as the [Docker pid 1 problem](https://blog.phusion.nl/2015/01/20/docker-and-the-pid-1-zombie-reaping-problem/).
|
||||||
|
|
||||||
|
|
||||||
|
Network namespaces ([CLONE_NEWNET](http://linux.die.net/man/2/clone)): The sandbox will not see the network. Instead it will have its own network namespace with only a loopback device.
|
||||||
|
|
||||||
|
UTS namespace ([CLONE_NEWUTS](http://linux.die.net/man/2/clone)): The sandbox will have its own hostname.
|
||||||
|
|
||||||
|
Seccomp filters: You can pass in seccomp filters that limit which syscalls can be done in the sandbox. For more information, see [Seccomp](https://en.wikipedia.org/wiki/Seccomp).
|
||||||
|
|
||||||
|
Related project comparison: Firejail
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
[Firejail](https://github.com/netblue30/firejail/tree/master/src/firejail)
|
||||||
|
is similar to Flatpak before bubblewrap was split out in that it combines
|
||||||
|
a setuid tool with a lot of desktop-specific sandboxing features. For
|
||||||
|
example, Firejail knows about Pulseaudio, whereas bubblewrap does not.
|
||||||
|
|
||||||
|
The bubblewrap authors believe it's much easier to audit a small
|
||||||
|
setuid program, and keep features such as Pulseaudio filtering as an
|
||||||
|
unprivileged process, as now occurs in Flatpak.
|
||||||
|
|
||||||
|
Also, @cgwalters thinks trying to
|
||||||
|
[whitelist file paths](https://github.com/netblue30/firejail/blob/37a5a3545ef6d8d03dad8bbd888f53e13274c9e5/src/firejail/fs_whitelist.c#L176)
|
||||||
|
is a bad idea given the myriad ways users have to manipulate paths,
|
||||||
|
and the myriad ways in which system administrators may configure a
|
||||||
|
system. The bubblewrap approach is to only retain a few specific
|
||||||
|
Linux capabilities such as `CAP_SYS_ADMIN`, but to always access the
|
||||||
|
filesystem as the invoking uid. This entirely closes
|
||||||
|
[TOCTTOU attacks](https://cwe.mitre.org/data/definitions/367.html) and
|
||||||
|
such.
|
||||||
|
|
||||||
|
Related project comparison: Sandstorm.io
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
[Sandstorm.io](https://sandstorm.io/) requires unprivileged user
|
||||||
|
namespaces to set up its sandbox, though it could easily be adapted
|
||||||
|
to operate in a setuid mode as well. @cgwalters believes their code is
|
||||||
|
fairly good, but it could still make sense to unify on bubblewrap.
|
||||||
|
However, @kentonv (of Sandstorm) feels that while this makes sense
|
||||||
|
in principle, the switching cost outweighs the practical benefits for
|
||||||
|
now. This decision could be re-evaluated in the future, but it is not
|
||||||
|
being actively pursued today.
|
||||||
|
|
||||||
|
Related project comparison: runc/binctr
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
[runC](https://github.com/opencontainers/runc) is currently working on
|
||||||
|
supporting [rootless containers](https://github.com/opencontainers/runc/pull/774),
|
||||||
|
without needing `setuid` or any other privileges during installation of
|
||||||
|
runC (using unprivileged user namespaces rather than `setuid`),
|
||||||
|
creation, and management of containers. However, the standard mode of
|
||||||
|
using runC is similar to [systemd nspawn](https://www.freedesktop.org/software/systemd/man/systemd-nspawn.html)
|
||||||
|
in that it is tooling intended to be invoked by root.
|
||||||
|
|
||||||
|
The bubblewrap authors believe that runc and systemd-nspawn are not
|
||||||
|
designed to be made setuid, and are distant from supporting such a mode.
|
||||||
|
However with rootless containers, runC will be able to fulfill certain usecases
|
||||||
|
that bubblewrap supports (with the added benefit of being a standardised and
|
||||||
|
complete OCI runtime).
|
||||||
|
|
||||||
|
[binctr](https://github.com/jfrazelle/binctr) is just a wrapper for
|
||||||
|
runC, so inherits all of its design tradeoffs.
|
||||||
|
|
||||||
|
What's with the name?!
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
The name bubblewrap was chosen to convey that this
|
||||||
|
tool runs as the parent of the application (so wraps it in some sense) and creates
|
||||||
|
a protective layer (the sandbox) around it.
|
||||||
|
|
||||||
|
![](bubblewrap.jpg)
|
||||||
|
|
||||||
|
(Bubblewrap cat by [dancing_stupidity](https://www.flickr.com/photos/27549668@N03/))
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,19 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
test -n "$srcdir" || srcdir=`dirname "$0"`
|
||||||
|
test -n "$srcdir" || srcdir=.
|
||||||
|
|
||||||
|
olddir=`pwd`
|
||||||
|
cd $srcdir
|
||||||
|
|
||||||
|
if ! (autoreconf --version >/dev/null 2>&1); then
|
||||||
|
echo "*** No autoreconf found, please install it ***"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p m4
|
||||||
|
|
||||||
|
autoreconf --force --install --verbose
|
||||||
|
|
||||||
|
cd $olddir
|
||||||
|
test -n "$NOCONFIGURE" || "$srcdir/configure" "$@"
|
|
@ -0,0 +1,440 @@
|
||||||
|
/* bubblewrap
|
||||||
|
* Copyright (C) 2016 Alexander Larsson
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <sys/mount.h>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
#include "bind-mount.h"
|
||||||
|
|
||||||
|
static char *
|
||||||
|
skip_token (char *line, bool eat_whitespace)
|
||||||
|
{
|
||||||
|
while (*line != ' ' && *line != '\n')
|
||||||
|
line++;
|
||||||
|
|
||||||
|
if (eat_whitespace && *line == ' ')
|
||||||
|
line++;
|
||||||
|
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
unescape_inline (char *escaped)
|
||||||
|
{
|
||||||
|
char *unescaped, *res;
|
||||||
|
const char *end;
|
||||||
|
|
||||||
|
res = escaped;
|
||||||
|
end = escaped + strlen (escaped);
|
||||||
|
|
||||||
|
unescaped = escaped;
|
||||||
|
while (escaped < end)
|
||||||
|
{
|
||||||
|
if (*escaped == '\\')
|
||||||
|
{
|
||||||
|
*unescaped++ =
|
||||||
|
((escaped[1] - '0') << 6) |
|
||||||
|
((escaped[2] - '0') << 3) |
|
||||||
|
((escaped[3] - '0') << 0);
|
||||||
|
escaped += 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*unescaped++ = *escaped++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*unescaped = 0;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
match_token (const char *token, const char *token_end, const char *str)
|
||||||
|
{
|
||||||
|
while (token != token_end && *token == *str)
|
||||||
|
{
|
||||||
|
token++;
|
||||||
|
str++;
|
||||||
|
}
|
||||||
|
if (token == token_end)
|
||||||
|
return *str == 0;
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned long
|
||||||
|
decode_mountoptions (const char *options)
|
||||||
|
{
|
||||||
|
const char *token, *end_token;
|
||||||
|
int i;
|
||||||
|
unsigned long flags = 0;
|
||||||
|
static const struct { int flag;
|
||||||
|
char *name;
|
||||||
|
} flags_data[] = {
|
||||||
|
{ 0, "rw" },
|
||||||
|
{ MS_RDONLY, "ro" },
|
||||||
|
{ MS_NOSUID, "nosuid" },
|
||||||
|
{ MS_NODEV, "nodev" },
|
||||||
|
{ MS_NOEXEC, "noexec" },
|
||||||
|
{ MS_NOATIME, "noatime" },
|
||||||
|
{ MS_NODIRATIME, "nodiratime" },
|
||||||
|
{ MS_RELATIME, "relatime" },
|
||||||
|
{ 0, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
token = options;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
end_token = strchr (token, ',');
|
||||||
|
if (end_token == NULL)
|
||||||
|
end_token = token + strlen (token);
|
||||||
|
|
||||||
|
for (i = 0; flags_data[i].name != NULL; i++)
|
||||||
|
{
|
||||||
|
if (match_token (token, end_token, flags_data[i].name))
|
||||||
|
{
|
||||||
|
flags |= flags_data[i].flag;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*end_token != 0)
|
||||||
|
token = end_token + 1;
|
||||||
|
else
|
||||||
|
token = NULL;
|
||||||
|
}
|
||||||
|
while (token != NULL);
|
||||||
|
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct MountInfo MountInfo;
|
||||||
|
struct MountInfo {
|
||||||
|
char *mountpoint;
|
||||||
|
unsigned long options;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef MountInfo *MountTab;
|
||||||
|
|
||||||
|
static void
|
||||||
|
mount_tab_free (MountTab tab)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; tab[i].mountpoint != NULL; i++)
|
||||||
|
free (tab[i].mountpoint);
|
||||||
|
free (tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
cleanup_mount_tabp (void *p)
|
||||||
|
{
|
||||||
|
void **pp = (void **) p;
|
||||||
|
|
||||||
|
if (*pp)
|
||||||
|
mount_tab_free ((MountTab)*pp);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define cleanup_mount_tab __attribute__((cleanup (cleanup_mount_tabp)))
|
||||||
|
|
||||||
|
typedef struct MountInfoLine MountInfoLine;
|
||||||
|
struct MountInfoLine {
|
||||||
|
const char *mountpoint;
|
||||||
|
const char *options;
|
||||||
|
bool covered;
|
||||||
|
int id;
|
||||||
|
int parent_id;
|
||||||
|
MountInfoLine *first_child;
|
||||||
|
MountInfoLine *next_sibling;
|
||||||
|
};
|
||||||
|
|
||||||
|
static unsigned int
|
||||||
|
count_lines (const char *data)
|
||||||
|
{
|
||||||
|
unsigned int count = 0;
|
||||||
|
const char *p = data;
|
||||||
|
|
||||||
|
while (*p != 0)
|
||||||
|
{
|
||||||
|
if (*p == '\n')
|
||||||
|
count++;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If missing final newline, add one */
|
||||||
|
if (p > data && *(p-1) != '\n')
|
||||||
|
count++;
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
count_mounts (MountInfoLine *line)
|
||||||
|
{
|
||||||
|
MountInfoLine *child;
|
||||||
|
int res = 0;
|
||||||
|
|
||||||
|
if (!line->covered)
|
||||||
|
res += 1;
|
||||||
|
|
||||||
|
child = line->first_child;
|
||||||
|
while (child != NULL)
|
||||||
|
{
|
||||||
|
res += count_mounts (child);
|
||||||
|
child = child->next_sibling;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MountInfo *
|
||||||
|
collect_mounts (MountInfo *info, MountInfoLine *line)
|
||||||
|
{
|
||||||
|
MountInfoLine *child;
|
||||||
|
|
||||||
|
if (!line->covered)
|
||||||
|
{
|
||||||
|
info->mountpoint = xstrdup (line->mountpoint);
|
||||||
|
info->options = decode_mountoptions (line->options);
|
||||||
|
info ++;
|
||||||
|
}
|
||||||
|
|
||||||
|
child = line->first_child;
|
||||||
|
while (child != NULL)
|
||||||
|
{
|
||||||
|
info = collect_mounts (info, child);
|
||||||
|
child = child->next_sibling;
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MountTab
|
||||||
|
parse_mountinfo (int proc_fd,
|
||||||
|
const char *root_mount)
|
||||||
|
{
|
||||||
|
cleanup_free char *mountinfo = NULL;
|
||||||
|
cleanup_free MountInfoLine *lines = NULL;
|
||||||
|
cleanup_free MountInfoLine **by_id = NULL;
|
||||||
|
cleanup_mount_tab MountTab mount_tab = NULL;
|
||||||
|
MountInfo *end_tab;
|
||||||
|
int n_mounts;
|
||||||
|
char *line;
|
||||||
|
int i;
|
||||||
|
int max_id;
|
||||||
|
unsigned int n_lines;
|
||||||
|
int root;
|
||||||
|
|
||||||
|
mountinfo = load_file_at (proc_fd, "self/mountinfo");
|
||||||
|
if (mountinfo == NULL)
|
||||||
|
die_with_error ("Can't open /proc/self/mountinfo");
|
||||||
|
|
||||||
|
n_lines = count_lines (mountinfo);
|
||||||
|
lines = xcalloc (n_lines * sizeof (MountInfoLine));
|
||||||
|
|
||||||
|
max_id = 0;
|
||||||
|
line = mountinfo;
|
||||||
|
i = 0;
|
||||||
|
root = -1;
|
||||||
|
while (*line != 0)
|
||||||
|
{
|
||||||
|
int rc, consumed = 0;
|
||||||
|
unsigned int maj, min;
|
||||||
|
char *end;
|
||||||
|
char *rest;
|
||||||
|
char *mountpoint;
|
||||||
|
char *mountpoint_end;
|
||||||
|
char *options;
|
||||||
|
char *options_end;
|
||||||
|
char *next_line;
|
||||||
|
|
||||||
|
assert (i < n_lines);
|
||||||
|
|
||||||
|
end = strchr (line, '\n');
|
||||||
|
if (end != NULL)
|
||||||
|
{
|
||||||
|
*end = 0;
|
||||||
|
next_line = end + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
next_line = line + strlen (line);
|
||||||
|
|
||||||
|
rc = sscanf (line, "%d %d %u:%u %n", &lines[i].id, &lines[i].parent_id, &maj, &min, &consumed);
|
||||||
|
if (rc != 4)
|
||||||
|
die ("Can't parse mountinfo line");
|
||||||
|
rest = line + consumed;
|
||||||
|
|
||||||
|
rest = skip_token (rest, TRUE); /* mountroot */
|
||||||
|
mountpoint = rest;
|
||||||
|
rest = skip_token (rest, FALSE); /* mountpoint */
|
||||||
|
mountpoint_end = rest++;
|
||||||
|
options = rest;
|
||||||
|
rest = skip_token (rest, FALSE); /* vfs options */
|
||||||
|
options_end = rest;
|
||||||
|
|
||||||
|
*mountpoint_end = 0;
|
||||||
|
lines[i].mountpoint = unescape_inline (mountpoint);
|
||||||
|
|
||||||
|
*options_end = 0;
|
||||||
|
lines[i].options = options;
|
||||||
|
|
||||||
|
if (lines[i].id > max_id)
|
||||||
|
max_id = lines[i].id;
|
||||||
|
if (lines[i].parent_id > max_id)
|
||||||
|
max_id = lines[i].parent_id;
|
||||||
|
|
||||||
|
if (path_equal (lines[i].mountpoint, root_mount))
|
||||||
|
root = i;
|
||||||
|
|
||||||
|
i++;
|
||||||
|
line = next_line;
|
||||||
|
}
|
||||||
|
assert (i == n_lines);
|
||||||
|
|
||||||
|
if (root == -1)
|
||||||
|
{
|
||||||
|
mount_tab = xcalloc (sizeof (MountInfo) * (1));
|
||||||
|
return steal_pointer (&mount_tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
by_id = xcalloc ((max_id + 1) * sizeof (MountInfoLine*));
|
||||||
|
for (i = 0; i < n_lines; i++)
|
||||||
|
by_id[lines[i].id] = &lines[i];
|
||||||
|
|
||||||
|
for (i = 0; i < n_lines; i++)
|
||||||
|
{
|
||||||
|
MountInfoLine *this = &lines[i];
|
||||||
|
MountInfoLine *parent = by_id[this->parent_id];
|
||||||
|
MountInfoLine **to_sibling;
|
||||||
|
MountInfoLine *sibling;
|
||||||
|
bool covered = FALSE;
|
||||||
|
|
||||||
|
if (!has_path_prefix (this->mountpoint, root_mount))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (parent == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (strcmp (parent->mountpoint, this->mountpoint) == 0)
|
||||||
|
parent->covered = TRUE;
|
||||||
|
|
||||||
|
to_sibling = &parent->first_child;
|
||||||
|
sibling = parent->first_child;
|
||||||
|
while (sibling != NULL)
|
||||||
|
{
|
||||||
|
/* If this mountpoint is a path prefix of the sibling,
|
||||||
|
* say this->mp=/foo/bar and sibling->mp=/foo, then it is
|
||||||
|
* covered by the sibling, and we drop it. */
|
||||||
|
if (has_path_prefix (this->mountpoint, sibling->mountpoint))
|
||||||
|
{
|
||||||
|
covered = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the sibling is a path prefix of this mount point,
|
||||||
|
* say this->mp=/foo and sibling->mp=/foo/bar, then the sibling
|
||||||
|
* is covered, and we drop it.
|
||||||
|
*/
|
||||||
|
if (has_path_prefix (sibling->mountpoint, this->mountpoint))
|
||||||
|
*to_sibling = sibling->next_sibling;
|
||||||
|
else
|
||||||
|
to_sibling = &sibling->next_sibling;
|
||||||
|
sibling = sibling->next_sibling;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (covered)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
*to_sibling = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
n_mounts = count_mounts (&lines[root]);
|
||||||
|
mount_tab = xcalloc (sizeof (MountInfo) * (n_mounts + 1));
|
||||||
|
|
||||||
|
end_tab = collect_mounts (&mount_tab[0], &lines[root]);
|
||||||
|
assert (end_tab == &mount_tab[n_mounts]);
|
||||||
|
|
||||||
|
return steal_pointer (&mount_tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
bind_mount (int proc_fd,
|
||||||
|
const char *src,
|
||||||
|
const char *dest,
|
||||||
|
bind_option_t options)
|
||||||
|
{
|
||||||
|
bool readonly = (options & BIND_READONLY) != 0;
|
||||||
|
bool devices = (options & BIND_DEVICES) != 0;
|
||||||
|
bool recursive = (options & BIND_RECURSIVE) != 0;
|
||||||
|
unsigned long current_flags, new_flags;
|
||||||
|
cleanup_mount_tab MountTab mount_tab = NULL;
|
||||||
|
cleanup_free char *resolved_dest = NULL;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (src)
|
||||||
|
{
|
||||||
|
if (mount (src, dest, NULL, MS_BIND | (recursive ? MS_REC : 0), NULL) != 0)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The mount operation will resolve any symlinks in the destination
|
||||||
|
path, so to find it in the mount table we need to do that too. */
|
||||||
|
resolved_dest = realpath (dest, NULL);
|
||||||
|
if (resolved_dest == NULL)
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
mount_tab = parse_mountinfo (proc_fd, resolved_dest);
|
||||||
|
if (mount_tab[0].mountpoint == NULL)
|
||||||
|
{
|
||||||
|
errno = EINVAL;
|
||||||
|
return 2; /* No mountpoint at dest */
|
||||||
|
}
|
||||||
|
|
||||||
|
assert (path_equal (mount_tab[0].mountpoint, resolved_dest));
|
||||||
|
current_flags = mount_tab[0].options;
|
||||||
|
new_flags = current_flags | (devices ? 0 : MS_NODEV) | MS_NOSUID | (readonly ? MS_RDONLY : 0);
|
||||||
|
if (new_flags != current_flags &&
|
||||||
|
mount ("none", resolved_dest,
|
||||||
|
NULL, MS_BIND | MS_REMOUNT | new_flags, NULL) != 0)
|
||||||
|
return 3;
|
||||||
|
|
||||||
|
/* We need to work around the fact that a bind mount does not apply the flags, so we need to manually
|
||||||
|
* apply the flags to all submounts in the recursive case.
|
||||||
|
* Note: This does not apply the flags to mounts which are later propagated into this namespace.
|
||||||
|
*/
|
||||||
|
if (recursive)
|
||||||
|
{
|
||||||
|
for (i = 1; mount_tab[i].mountpoint != NULL; i++)
|
||||||
|
{
|
||||||
|
current_flags = mount_tab[i].options;
|
||||||
|
new_flags = current_flags | (devices ? 0 : MS_NODEV) | MS_NOSUID | (readonly ? MS_RDONLY : 0);
|
||||||
|
if (new_flags != current_flags &&
|
||||||
|
mount ("none", mount_tab[i].mountpoint,
|
||||||
|
NULL, MS_BIND | MS_REMOUNT | new_flags, NULL) != 0)
|
||||||
|
{
|
||||||
|
/* If we can't read the mountpoint we can't remount it, but that should
|
||||||
|
be safe to ignore because its not something the user can access. */
|
||||||
|
if (errno != EACCES)
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/* bubblewrap
|
||||||
|
* Copyright (C) 2016 Alexander Larsson
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BIND_READONLY = (1 << 0),
|
||||||
|
BIND_DEVICES = (1 << 2),
|
||||||
|
BIND_RECURSIVE = (1 << 3),
|
||||||
|
} bind_option_t;
|
||||||
|
|
||||||
|
int bind_mount (int proc_fd,
|
||||||
|
const char *src,
|
||||||
|
const char *dest,
|
||||||
|
bind_option_t options);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,348 @@
|
||||||
|
#! /bin/sh
|
||||||
|
# Wrapper for compilers which do not understand '-c -o'.
|
||||||
|
|
||||||
|
scriptversion=2018-03-07.03; # UTC
|
||||||
|
|
||||||
|
# Copyright (C) 1999-2018 Free Software Foundation, Inc.
|
||||||
|
# Written by Tom Tromey <tromey@cygnus.com>.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
# any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# As a special exception to the GNU General Public License, if you
|
||||||
|
# distribute this file as part of a program that contains a
|
||||||
|
# configuration script generated by Autoconf, you may include it under
|
||||||
|
# the same distribution terms that you use for the rest of that program.
|
||||||
|
|
||||||
|
# This file is maintained in Automake, please report
|
||||||
|
# bugs to <bug-automake@gnu.org> or send patches to
|
||||||
|
# <automake-patches@gnu.org>.
|
||||||
|
|
||||||
|
nl='
|
||||||
|
'
|
||||||
|
|
||||||
|
# We need space, tab and new line, in precisely that order. Quoting is
|
||||||
|
# there to prevent tools from complaining about whitespace usage.
|
||||||
|
IFS=" "" $nl"
|
||||||
|
|
||||||
|
file_conv=
|
||||||
|
|
||||||
|
# func_file_conv build_file lazy
|
||||||
|
# Convert a $build file to $host form and store it in $file
|
||||||
|
# Currently only supports Windows hosts. If the determined conversion
|
||||||
|
# type is listed in (the comma separated) LAZY, no conversion will
|
||||||
|
# take place.
|
||||||
|
func_file_conv ()
|
||||||
|
{
|
||||||
|
file=$1
|
||||||
|
case $file in
|
||||||
|
/ | /[!/]*) # absolute file, and not a UNC file
|
||||||
|
if test -z "$file_conv"; then
|
||||||
|
# lazily determine how to convert abs files
|
||||||
|
case `uname -s` in
|
||||||
|
MINGW*)
|
||||||
|
file_conv=mingw
|
||||||
|
;;
|
||||||
|
CYGWIN*)
|
||||||
|
file_conv=cygwin
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
file_conv=wine
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
case $file_conv/,$2, in
|
||||||
|
*,$file_conv,*)
|
||||||
|
;;
|
||||||
|
mingw/*)
|
||||||
|
file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
|
||||||
|
;;
|
||||||
|
cygwin/*)
|
||||||
|
file=`cygpath -m "$file" || echo "$file"`
|
||||||
|
;;
|
||||||
|
wine/*)
|
||||||
|
file=`winepath -w "$file" || echo "$file"`
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# func_cl_dashL linkdir
|
||||||
|
# Make cl look for libraries in LINKDIR
|
||||||
|
func_cl_dashL ()
|
||||||
|
{
|
||||||
|
func_file_conv "$1"
|
||||||
|
if test -z "$lib_path"; then
|
||||||
|
lib_path=$file
|
||||||
|
else
|
||||||
|
lib_path="$lib_path;$file"
|
||||||
|
fi
|
||||||
|
linker_opts="$linker_opts -LIBPATH:$file"
|
||||||
|
}
|
||||||
|
|
||||||
|
# func_cl_dashl library
|
||||||
|
# Do a library search-path lookup for cl
|
||||||
|
func_cl_dashl ()
|
||||||
|
{
|
||||||
|
lib=$1
|
||||||
|
found=no
|
||||||
|
save_IFS=$IFS
|
||||||
|
IFS=';'
|
||||||
|
for dir in $lib_path $LIB
|
||||||
|
do
|
||||||
|
IFS=$save_IFS
|
||||||
|
if $shared && test -f "$dir/$lib.dll.lib"; then
|
||||||
|
found=yes
|
||||||
|
lib=$dir/$lib.dll.lib
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
if test -f "$dir/$lib.lib"; then
|
||||||
|
found=yes
|
||||||
|
lib=$dir/$lib.lib
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
if test -f "$dir/lib$lib.a"; then
|
||||||
|
found=yes
|
||||||
|
lib=$dir/lib$lib.a
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
IFS=$save_IFS
|
||||||
|
|
||||||
|
if test "$found" != yes; then
|
||||||
|
lib=$lib.lib
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# func_cl_wrapper cl arg...
|
||||||
|
# Adjust compile command to suit cl
|
||||||
|
func_cl_wrapper ()
|
||||||
|
{
|
||||||
|
# Assume a capable shell
|
||||||
|
lib_path=
|
||||||
|
shared=:
|
||||||
|
linker_opts=
|
||||||
|
for arg
|
||||||
|
do
|
||||||
|
if test -n "$eat"; then
|
||||||
|
eat=
|
||||||
|
else
|
||||||
|
case $1 in
|
||||||
|
-o)
|
||||||
|
# configure might choose to run compile as 'compile cc -o foo foo.c'.
|
||||||
|
eat=1
|
||||||
|
case $2 in
|
||||||
|
*.o | *.[oO][bB][jJ])
|
||||||
|
func_file_conv "$2"
|
||||||
|
set x "$@" -Fo"$file"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
func_file_conv "$2"
|
||||||
|
set x "$@" -Fe"$file"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
-I)
|
||||||
|
eat=1
|
||||||
|
func_file_conv "$2" mingw
|
||||||
|
set x "$@" -I"$file"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-I*)
|
||||||
|
func_file_conv "${1#-I}" mingw
|
||||||
|
set x "$@" -I"$file"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-l)
|
||||||
|
eat=1
|
||||||
|
func_cl_dashl "$2"
|
||||||
|
set x "$@" "$lib"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-l*)
|
||||||
|
func_cl_dashl "${1#-l}"
|
||||||
|
set x "$@" "$lib"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-L)
|
||||||
|
eat=1
|
||||||
|
func_cl_dashL "$2"
|
||||||
|
;;
|
||||||
|
-L*)
|
||||||
|
func_cl_dashL "${1#-L}"
|
||||||
|
;;
|
||||||
|
-static)
|
||||||
|
shared=false
|
||||||
|
;;
|
||||||
|
-Wl,*)
|
||||||
|
arg=${1#-Wl,}
|
||||||
|
save_ifs="$IFS"; IFS=','
|
||||||
|
for flag in $arg; do
|
||||||
|
IFS="$save_ifs"
|
||||||
|
linker_opts="$linker_opts $flag"
|
||||||
|
done
|
||||||
|
IFS="$save_ifs"
|
||||||
|
;;
|
||||||
|
-Xlinker)
|
||||||
|
eat=1
|
||||||
|
linker_opts="$linker_opts $2"
|
||||||
|
;;
|
||||||
|
-*)
|
||||||
|
set x "$@" "$1"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
|
||||||
|
func_file_conv "$1"
|
||||||
|
set x "$@" -Tp"$file"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
|
||||||
|
func_file_conv "$1" mingw
|
||||||
|
set x "$@" "$file"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
set x "$@" "$1"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
if test -n "$linker_opts"; then
|
||||||
|
linker_opts="-link$linker_opts"
|
||||||
|
fi
|
||||||
|
exec "$@" $linker_opts
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
eat=
|
||||||
|
|
||||||
|
case $1 in
|
||||||
|
'')
|
||||||
|
echo "$0: No command. Try '$0 --help' for more information." 1>&2
|
||||||
|
exit 1;
|
||||||
|
;;
|
||||||
|
-h | --h*)
|
||||||
|
cat <<\EOF
|
||||||
|
Usage: compile [--help] [--version] PROGRAM [ARGS]
|
||||||
|
|
||||||
|
Wrapper for compilers which do not understand '-c -o'.
|
||||||
|
Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
|
||||||
|
arguments, and rename the output as expected.
|
||||||
|
|
||||||
|
If you are trying to build a whole package this is not the
|
||||||
|
right script to run: please start by reading the file 'INSTALL'.
|
||||||
|
|
||||||
|
Report bugs to <bug-automake@gnu.org>.
|
||||||
|
EOF
|
||||||
|
exit $?
|
||||||
|
;;
|
||||||
|
-v | --v*)
|
||||||
|
echo "compile $scriptversion"
|
||||||
|
exit $?
|
||||||
|
;;
|
||||||
|
cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \
|
||||||
|
icl | *[/\\]icl | icl.exe | *[/\\]icl.exe )
|
||||||
|
func_cl_wrapper "$@" # Doesn't return...
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
ofile=
|
||||||
|
cfile=
|
||||||
|
|
||||||
|
for arg
|
||||||
|
do
|
||||||
|
if test -n "$eat"; then
|
||||||
|
eat=
|
||||||
|
else
|
||||||
|
case $1 in
|
||||||
|
-o)
|
||||||
|
# configure might choose to run compile as 'compile cc -o foo foo.c'.
|
||||||
|
# So we strip '-o arg' only if arg is an object.
|
||||||
|
eat=1
|
||||||
|
case $2 in
|
||||||
|
*.o | *.obj)
|
||||||
|
ofile=$2
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
set x "$@" -o "$2"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
*.c)
|
||||||
|
cfile=$1
|
||||||
|
set x "$@" "$1"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
set x "$@" "$1"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
if test -z "$ofile" || test -z "$cfile"; then
|
||||||
|
# If no '-o' option was seen then we might have been invoked from a
|
||||||
|
# pattern rule where we don't need one. That is ok -- this is a
|
||||||
|
# normal compilation that the losing compiler can handle. If no
|
||||||
|
# '.c' file was seen then we are probably linking. That is also
|
||||||
|
# ok.
|
||||||
|
exec "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Name of file we expect compiler to create.
|
||||||
|
cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
|
||||||
|
|
||||||
|
# Create the lock directory.
|
||||||
|
# Note: use '[/\\:.-]' here to ensure that we don't use the same name
|
||||||
|
# that we are using for the .o file. Also, base the name on the expected
|
||||||
|
# object file name, since that is what matters with a parallel build.
|
||||||
|
lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
|
||||||
|
while true; do
|
||||||
|
if mkdir "$lockdir" >/dev/null 2>&1; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
# FIXME: race condition here if user kills between mkdir and trap.
|
||||||
|
trap "rmdir '$lockdir'; exit 1" 1 2 15
|
||||||
|
|
||||||
|
# Run the compile.
|
||||||
|
"$@"
|
||||||
|
ret=$?
|
||||||
|
|
||||||
|
if test -f "$cofile"; then
|
||||||
|
test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
|
||||||
|
elif test -f "${cofile}bj"; then
|
||||||
|
test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
|
||||||
|
fi
|
||||||
|
|
||||||
|
rmdir "$lockdir"
|
||||||
|
exit $ret
|
||||||
|
|
||||||
|
# Local Variables:
|
||||||
|
# mode: shell-script
|
||||||
|
# sh-indentation: 2
|
||||||
|
# eval: (add-hook 'before-save-hook 'time-stamp)
|
||||||
|
# time-stamp-start: "scriptversion="
|
||||||
|
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||||
|
# time-stamp-time-zone: "UTC0"
|
||||||
|
# time-stamp-end: "; # UTC"
|
||||||
|
# End:
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,791 @@
|
||||||
|
#! /bin/sh
|
||||||
|
# depcomp - compile a program generating dependencies as side-effects
|
||||||
|
|
||||||
|
scriptversion=2018-03-07.03; # UTC
|
||||||
|
|
||||||
|
# Copyright (C) 1999-2018 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
# any later version.
|
||||||
|
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# As a special exception to the GNU General Public License, if you
|
||||||
|
# distribute this file as part of a program that contains a
|
||||||
|
# configuration script generated by Autoconf, you may include it under
|
||||||
|
# the same distribution terms that you use for the rest of that program.
|
||||||
|
|
||||||
|
# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
|
||||||
|
|
||||||
|
case $1 in
|
||||||
|
'')
|
||||||
|
echo "$0: No command. Try '$0 --help' for more information." 1>&2
|
||||||
|
exit 1;
|
||||||
|
;;
|
||||||
|
-h | --h*)
|
||||||
|
cat <<\EOF
|
||||||
|
Usage: depcomp [--help] [--version] PROGRAM [ARGS]
|
||||||
|
|
||||||
|
Run PROGRAMS ARGS to compile a file, generating dependencies
|
||||||
|
as side-effects.
|
||||||
|
|
||||||
|
Environment variables:
|
||||||
|
depmode Dependency tracking mode.
|
||||||
|
source Source file read by 'PROGRAMS ARGS'.
|
||||||
|
object Object file output by 'PROGRAMS ARGS'.
|
||||||
|
DEPDIR directory where to store dependencies.
|
||||||
|
depfile Dependency file to output.
|
||||||
|
tmpdepfile Temporary file to use when outputting dependencies.
|
||||||
|
libtool Whether libtool is used (yes/no).
|
||||||
|
|
||||||
|
Report bugs to <bug-automake@gnu.org>.
|
||||||
|
EOF
|
||||||
|
exit $?
|
||||||
|
;;
|
||||||
|
-v | --v*)
|
||||||
|
echo "depcomp $scriptversion"
|
||||||
|
exit $?
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Get the directory component of the given path, and save it in the
|
||||||
|
# global variables '$dir'. Note that this directory component will
|
||||||
|
# be either empty or ending with a '/' character. This is deliberate.
|
||||||
|
set_dir_from ()
|
||||||
|
{
|
||||||
|
case $1 in
|
||||||
|
*/*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;;
|
||||||
|
*) dir=;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get the suffix-stripped basename of the given path, and save it the
|
||||||
|
# global variable '$base'.
|
||||||
|
set_base_from ()
|
||||||
|
{
|
||||||
|
base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'`
|
||||||
|
}
|
||||||
|
|
||||||
|
# If no dependency file was actually created by the compiler invocation,
|
||||||
|
# we still have to create a dummy depfile, to avoid errors with the
|
||||||
|
# Makefile "include basename.Plo" scheme.
|
||||||
|
make_dummy_depfile ()
|
||||||
|
{
|
||||||
|
echo "#dummy" > "$depfile"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Factor out some common post-processing of the generated depfile.
|
||||||
|
# Requires the auxiliary global variable '$tmpdepfile' to be set.
|
||||||
|
aix_post_process_depfile ()
|
||||||
|
{
|
||||||
|
# If the compiler actually managed to produce a dependency file,
|
||||||
|
# post-process it.
|
||||||
|
if test -f "$tmpdepfile"; then
|
||||||
|
# Each line is of the form 'foo.o: dependency.h'.
|
||||||
|
# Do two passes, one to just change these to
|
||||||
|
# $object: dependency.h
|
||||||
|
# and one to simply output
|
||||||
|
# dependency.h:
|
||||||
|
# which is needed to avoid the deleted-header problem.
|
||||||
|
{ sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile"
|
||||||
|
sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile"
|
||||||
|
} > "$depfile"
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
else
|
||||||
|
make_dummy_depfile
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# A tabulation character.
|
||||||
|
tab=' '
|
||||||
|
# A newline character.
|
||||||
|
nl='
|
||||||
|
'
|
||||||
|
# Character ranges might be problematic outside the C locale.
|
||||||
|
# These definitions help.
|
||||||
|
upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
||||||
|
lower=abcdefghijklmnopqrstuvwxyz
|
||||||
|
digits=0123456789
|
||||||
|
alpha=${upper}${lower}
|
||||||
|
|
||||||
|
if test -z "$depmode" || test -z "$source" || test -z "$object"; then
|
||||||
|
echo "depcomp: Variables source, object and depmode must be set" 1>&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
|
||||||
|
depfile=${depfile-`echo "$object" |
|
||||||
|
sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
|
||||||
|
tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
|
||||||
|
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
|
||||||
|
# Avoid interferences from the environment.
|
||||||
|
gccflag= dashmflag=
|
||||||
|
|
||||||
|
# Some modes work just like other modes, but use different flags. We
|
||||||
|
# parameterize here, but still list the modes in the big case below,
|
||||||
|
# to make depend.m4 easier to write. Note that we *cannot* use a case
|
||||||
|
# here, because this file can only contain one case statement.
|
||||||
|
if test "$depmode" = hp; then
|
||||||
|
# HP compiler uses -M and no extra arg.
|
||||||
|
gccflag=-M
|
||||||
|
depmode=gcc
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "$depmode" = dashXmstdout; then
|
||||||
|
# This is just like dashmstdout with a different argument.
|
||||||
|
dashmflag=-xM
|
||||||
|
depmode=dashmstdout
|
||||||
|
fi
|
||||||
|
|
||||||
|
cygpath_u="cygpath -u -f -"
|
||||||
|
if test "$depmode" = msvcmsys; then
|
||||||
|
# This is just like msvisualcpp but w/o cygpath translation.
|
||||||
|
# Just convert the backslash-escaped backslashes to single forward
|
||||||
|
# slashes to satisfy depend.m4
|
||||||
|
cygpath_u='sed s,\\\\,/,g'
|
||||||
|
depmode=msvisualcpp
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "$depmode" = msvc7msys; then
|
||||||
|
# This is just like msvc7 but w/o cygpath translation.
|
||||||
|
# Just convert the backslash-escaped backslashes to single forward
|
||||||
|
# slashes to satisfy depend.m4
|
||||||
|
cygpath_u='sed s,\\\\,/,g'
|
||||||
|
depmode=msvc7
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "$depmode" = xlc; then
|
||||||
|
# IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information.
|
||||||
|
gccflag=-qmakedep=gcc,-MF
|
||||||
|
depmode=gcc
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$depmode" in
|
||||||
|
gcc3)
|
||||||
|
## gcc 3 implements dependency tracking that does exactly what
|
||||||
|
## we want. Yay! Note: for some reason libtool 1.4 doesn't like
|
||||||
|
## it if -MD -MP comes after the -MF stuff. Hmm.
|
||||||
|
## Unfortunately, FreeBSD c89 acceptance of flags depends upon
|
||||||
|
## the command line argument order; so add the flags where they
|
||||||
|
## appear in depend2.am. Note that the slowdown incurred here
|
||||||
|
## affects only configure: in makefiles, %FASTDEP% shortcuts this.
|
||||||
|
for arg
|
||||||
|
do
|
||||||
|
case $arg in
|
||||||
|
-c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
|
||||||
|
*) set fnord "$@" "$arg" ;;
|
||||||
|
esac
|
||||||
|
shift # fnord
|
||||||
|
shift # $arg
|
||||||
|
done
|
||||||
|
"$@"
|
||||||
|
stat=$?
|
||||||
|
if test $stat -ne 0; then
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
exit $stat
|
||||||
|
fi
|
||||||
|
mv "$tmpdepfile" "$depfile"
|
||||||
|
;;
|
||||||
|
|
||||||
|
gcc)
|
||||||
|
## Note that this doesn't just cater to obsosete pre-3.x GCC compilers.
|
||||||
|
## but also to in-use compilers like IMB xlc/xlC and the HP C compiler.
|
||||||
|
## (see the conditional assignment to $gccflag above).
|
||||||
|
## There are various ways to get dependency output from gcc. Here's
|
||||||
|
## why we pick this rather obscure method:
|
||||||
|
## - Don't want to use -MD because we'd like the dependencies to end
|
||||||
|
## up in a subdir. Having to rename by hand is ugly.
|
||||||
|
## (We might end up doing this anyway to support other compilers.)
|
||||||
|
## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
|
||||||
|
## -MM, not -M (despite what the docs say). Also, it might not be
|
||||||
|
## supported by the other compilers which use the 'gcc' depmode.
|
||||||
|
## - Using -M directly means running the compiler twice (even worse
|
||||||
|
## than renaming).
|
||||||
|
if test -z "$gccflag"; then
|
||||||
|
gccflag=-MD,
|
||||||
|
fi
|
||||||
|
"$@" -Wp,"$gccflag$tmpdepfile"
|
||||||
|
stat=$?
|
||||||
|
if test $stat -ne 0; then
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
exit $stat
|
||||||
|
fi
|
||||||
|
rm -f "$depfile"
|
||||||
|
echo "$object : \\" > "$depfile"
|
||||||
|
# The second -e expression handles DOS-style file names with drive
|
||||||
|
# letters.
|
||||||
|
sed -e 's/^[^:]*: / /' \
|
||||||
|
-e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
|
||||||
|
## This next piece of magic avoids the "deleted header file" problem.
|
||||||
|
## The problem is that when a header file which appears in a .P file
|
||||||
|
## is deleted, the dependency causes make to die (because there is
|
||||||
|
## typically no way to rebuild the header). We avoid this by adding
|
||||||
|
## dummy dependencies for each header file. Too bad gcc doesn't do
|
||||||
|
## this for us directly.
|
||||||
|
## Some versions of gcc put a space before the ':'. On the theory
|
||||||
|
## that the space means something, we add a space to the output as
|
||||||
|
## well. hp depmode also adds that space, but also prefixes the VPATH
|
||||||
|
## to the object. Take care to not repeat it in the output.
|
||||||
|
## Some versions of the HPUX 10.20 sed can't process this invocation
|
||||||
|
## correctly. Breaking it into two sed invocations is a workaround.
|
||||||
|
tr ' ' "$nl" < "$tmpdepfile" \
|
||||||
|
| sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \
|
||||||
|
| sed -e 's/$/ :/' >> "$depfile"
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
;;
|
||||||
|
|
||||||
|
hp)
|
||||||
|
# This case exists only to let depend.m4 do its work. It works by
|
||||||
|
# looking at the text of this script. This case will never be run,
|
||||||
|
# since it is checked for above.
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
|
||||||
|
sgi)
|
||||||
|
if test "$libtool" = yes; then
|
||||||
|
"$@" "-Wp,-MDupdate,$tmpdepfile"
|
||||||
|
else
|
||||||
|
"$@" -MDupdate "$tmpdepfile"
|
||||||
|
fi
|
||||||
|
stat=$?
|
||||||
|
if test $stat -ne 0; then
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
exit $stat
|
||||||
|
fi
|
||||||
|
rm -f "$depfile"
|
||||||
|
|
||||||
|
if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
|
||||||
|
echo "$object : \\" > "$depfile"
|
||||||
|
# Clip off the initial element (the dependent). Don't try to be
|
||||||
|
# clever and replace this with sed code, as IRIX sed won't handle
|
||||||
|
# lines with more than a fixed number of characters (4096 in
|
||||||
|
# IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
|
||||||
|
# the IRIX cc adds comments like '#:fec' to the end of the
|
||||||
|
# dependency line.
|
||||||
|
tr ' ' "$nl" < "$tmpdepfile" \
|
||||||
|
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \
|
||||||
|
| tr "$nl" ' ' >> "$depfile"
|
||||||
|
echo >> "$depfile"
|
||||||
|
# The second pass generates a dummy entry for each header file.
|
||||||
|
tr ' ' "$nl" < "$tmpdepfile" \
|
||||||
|
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
|
||||||
|
>> "$depfile"
|
||||||
|
else
|
||||||
|
make_dummy_depfile
|
||||||
|
fi
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
;;
|
||||||
|
|
||||||
|
xlc)
|
||||||
|
# This case exists only to let depend.m4 do its work. It works by
|
||||||
|
# looking at the text of this script. This case will never be run,
|
||||||
|
# since it is checked for above.
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
|
||||||
|
aix)
|
||||||
|
# The C for AIX Compiler uses -M and outputs the dependencies
|
||||||
|
# in a .u file. In older versions, this file always lives in the
|
||||||
|
# current directory. Also, the AIX compiler puts '$object:' at the
|
||||||
|
# start of each line; $object doesn't have directory information.
|
||||||
|
# Version 6 uses the directory in both cases.
|
||||||
|
set_dir_from "$object"
|
||||||
|
set_base_from "$object"
|
||||||
|
if test "$libtool" = yes; then
|
||||||
|
tmpdepfile1=$dir$base.u
|
||||||
|
tmpdepfile2=$base.u
|
||||||
|
tmpdepfile3=$dir.libs/$base.u
|
||||||
|
"$@" -Wc,-M
|
||||||
|
else
|
||||||
|
tmpdepfile1=$dir$base.u
|
||||||
|
tmpdepfile2=$dir$base.u
|
||||||
|
tmpdepfile3=$dir$base.u
|
||||||
|
"$@" -M
|
||||||
|
fi
|
||||||
|
stat=$?
|
||||||
|
if test $stat -ne 0; then
|
||||||
|
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||||
|
exit $stat
|
||||||
|
fi
|
||||||
|
|
||||||
|
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||||
|
do
|
||||||
|
test -f "$tmpdepfile" && break
|
||||||
|
done
|
||||||
|
aix_post_process_depfile
|
||||||
|
;;
|
||||||
|
|
||||||
|
tcc)
|
||||||
|
# tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26
|
||||||
|
# FIXME: That version still under development at the moment of writing.
|
||||||
|
# Make that this statement remains true also for stable, released
|
||||||
|
# versions.
|
||||||
|
# It will wrap lines (doesn't matter whether long or short) with a
|
||||||
|
# trailing '\', as in:
|
||||||
|
#
|
||||||
|
# foo.o : \
|
||||||
|
# foo.c \
|
||||||
|
# foo.h \
|
||||||
|
#
|
||||||
|
# It will put a trailing '\' even on the last line, and will use leading
|
||||||
|
# spaces rather than leading tabs (at least since its commit 0394caf7
|
||||||
|
# "Emit spaces for -MD").
|
||||||
|
"$@" -MD -MF "$tmpdepfile"
|
||||||
|
stat=$?
|
||||||
|
if test $stat -ne 0; then
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
exit $stat
|
||||||
|
fi
|
||||||
|
rm -f "$depfile"
|
||||||
|
# Each non-empty line is of the form 'foo.o : \' or ' dep.h \'.
|
||||||
|
# We have to change lines of the first kind to '$object: \'.
|
||||||
|
sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile"
|
||||||
|
# And for each line of the second kind, we have to emit a 'dep.h:'
|
||||||
|
# dummy dependency, to avoid the deleted-header problem.
|
||||||
|
sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile"
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
;;
|
||||||
|
|
||||||
|
## The order of this option in the case statement is important, since the
|
||||||
|
## shell code in configure will try each of these formats in the order
|
||||||
|
## listed in this file. A plain '-MD' option would be understood by many
|
||||||
|
## compilers, so we must ensure this comes after the gcc and icc options.
|
||||||
|
pgcc)
|
||||||
|
# Portland's C compiler understands '-MD'.
|
||||||
|
# Will always output deps to 'file.d' where file is the root name of the
|
||||||
|
# source file under compilation, even if file resides in a subdirectory.
|
||||||
|
# The object file name does not affect the name of the '.d' file.
|
||||||
|
# pgcc 10.2 will output
|
||||||
|
# foo.o: sub/foo.c sub/foo.h
|
||||||
|
# and will wrap long lines using '\' :
|
||||||
|
# foo.o: sub/foo.c ... \
|
||||||
|
# sub/foo.h ... \
|
||||||
|
# ...
|
||||||
|
set_dir_from "$object"
|
||||||
|
# Use the source, not the object, to determine the base name, since
|
||||||
|
# that's sadly what pgcc will do too.
|
||||||
|
set_base_from "$source"
|
||||||
|
tmpdepfile=$base.d
|
||||||
|
|
||||||
|
# For projects that build the same source file twice into different object
|
||||||
|
# files, the pgcc approach of using the *source* file root name can cause
|
||||||
|
# problems in parallel builds. Use a locking strategy to avoid stomping on
|
||||||
|
# the same $tmpdepfile.
|
||||||
|
lockdir=$base.d-lock
|
||||||
|
trap "
|
||||||
|
echo '$0: caught signal, cleaning up...' >&2
|
||||||
|
rmdir '$lockdir'
|
||||||
|
exit 1
|
||||||
|
" 1 2 13 15
|
||||||
|
numtries=100
|
||||||
|
i=$numtries
|
||||||
|
while test $i -gt 0; do
|
||||||
|
# mkdir is a portable test-and-set.
|
||||||
|
if mkdir "$lockdir" 2>/dev/null; then
|
||||||
|
# This process acquired the lock.
|
||||||
|
"$@" -MD
|
||||||
|
stat=$?
|
||||||
|
# Release the lock.
|
||||||
|
rmdir "$lockdir"
|
||||||
|
break
|
||||||
|
else
|
||||||
|
# If the lock is being held by a different process, wait
|
||||||
|
# until the winning process is done or we timeout.
|
||||||
|
while test -d "$lockdir" && test $i -gt 0; do
|
||||||
|
sleep 1
|
||||||
|
i=`expr $i - 1`
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
i=`expr $i - 1`
|
||||||
|
done
|
||||||
|
trap - 1 2 13 15
|
||||||
|
if test $i -le 0; then
|
||||||
|
echo "$0: failed to acquire lock after $numtries attempts" >&2
|
||||||
|
echo "$0: check lockdir '$lockdir'" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test $stat -ne 0; then
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
exit $stat
|
||||||
|
fi
|
||||||
|
rm -f "$depfile"
|
||||||
|
# Each line is of the form `foo.o: dependent.h',
|
||||||
|
# or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
|
||||||
|
# Do two passes, one to just change these to
|
||||||
|
# `$object: dependent.h' and one to simply `dependent.h:'.
|
||||||
|
sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
|
||||||
|
# Some versions of the HPUX 10.20 sed can't process this invocation
|
||||||
|
# correctly. Breaking it into two sed invocations is a workaround.
|
||||||
|
sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \
|
||||||
|
| sed -e 's/$/ :/' >> "$depfile"
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
;;
|
||||||
|
|
||||||
|
hp2)
|
||||||
|
# The "hp" stanza above does not work with aCC (C++) and HP's ia64
|
||||||
|
# compilers, which have integrated preprocessors. The correct option
|
||||||
|
# to use with these is +Maked; it writes dependencies to a file named
|
||||||
|
# 'foo.d', which lands next to the object file, wherever that
|
||||||
|
# happens to be.
|
||||||
|
# Much of this is similar to the tru64 case; see comments there.
|
||||||
|
set_dir_from "$object"
|
||||||
|
set_base_from "$object"
|
||||||
|
if test "$libtool" = yes; then
|
||||||
|
tmpdepfile1=$dir$base.d
|
||||||
|
tmpdepfile2=$dir.libs/$base.d
|
||||||
|
"$@" -Wc,+Maked
|
||||||
|
else
|
||||||
|
tmpdepfile1=$dir$base.d
|
||||||
|
tmpdepfile2=$dir$base.d
|
||||||
|
"$@" +Maked
|
||||||
|
fi
|
||||||
|
stat=$?
|
||||||
|
if test $stat -ne 0; then
|
||||||
|
rm -f "$tmpdepfile1" "$tmpdepfile2"
|
||||||
|
exit $stat
|
||||||
|
fi
|
||||||
|
|
||||||
|
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
|
||||||
|
do
|
||||||
|
test -f "$tmpdepfile" && break
|
||||||
|
done
|
||||||
|
if test -f "$tmpdepfile"; then
|
||||||
|
sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile"
|
||||||
|
# Add 'dependent.h:' lines.
|
||||||
|
sed -ne '2,${
|
||||||
|
s/^ *//
|
||||||
|
s/ \\*$//
|
||||||
|
s/$/:/
|
||||||
|
p
|
||||||
|
}' "$tmpdepfile" >> "$depfile"
|
||||||
|
else
|
||||||
|
make_dummy_depfile
|
||||||
|
fi
|
||||||
|
rm -f "$tmpdepfile" "$tmpdepfile2"
|
||||||
|
;;
|
||||||
|
|
||||||
|
tru64)
|
||||||
|
# The Tru64 compiler uses -MD to generate dependencies as a side
|
||||||
|
# effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'.
|
||||||
|
# At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
|
||||||
|
# dependencies in 'foo.d' instead, so we check for that too.
|
||||||
|
# Subdirectories are respected.
|
||||||
|
set_dir_from "$object"
|
||||||
|
set_base_from "$object"
|
||||||
|
|
||||||
|
if test "$libtool" = yes; then
|
||||||
|
# Libtool generates 2 separate objects for the 2 libraries. These
|
||||||
|
# two compilations output dependencies in $dir.libs/$base.o.d and
|
||||||
|
# in $dir$base.o.d. We have to check for both files, because
|
||||||
|
# one of the two compilations can be disabled. We should prefer
|
||||||
|
# $dir$base.o.d over $dir.libs/$base.o.d because the latter is
|
||||||
|
# automatically cleaned when .libs/ is deleted, while ignoring
|
||||||
|
# the former would cause a distcleancheck panic.
|
||||||
|
tmpdepfile1=$dir$base.o.d # libtool 1.5
|
||||||
|
tmpdepfile2=$dir.libs/$base.o.d # Likewise.
|
||||||
|
tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504
|
||||||
|
"$@" -Wc,-MD
|
||||||
|
else
|
||||||
|
tmpdepfile1=$dir$base.d
|
||||||
|
tmpdepfile2=$dir$base.d
|
||||||
|
tmpdepfile3=$dir$base.d
|
||||||
|
"$@" -MD
|
||||||
|
fi
|
||||||
|
|
||||||
|
stat=$?
|
||||||
|
if test $stat -ne 0; then
|
||||||
|
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||||
|
exit $stat
|
||||||
|
fi
|
||||||
|
|
||||||
|
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||||
|
do
|
||||||
|
test -f "$tmpdepfile" && break
|
||||||
|
done
|
||||||
|
# Same post-processing that is required for AIX mode.
|
||||||
|
aix_post_process_depfile
|
||||||
|
;;
|
||||||
|
|
||||||
|
msvc7)
|
||||||
|
if test "$libtool" = yes; then
|
||||||
|
showIncludes=-Wc,-showIncludes
|
||||||
|
else
|
||||||
|
showIncludes=-showIncludes
|
||||||
|
fi
|
||||||
|
"$@" $showIncludes > "$tmpdepfile"
|
||||||
|
stat=$?
|
||||||
|
grep -v '^Note: including file: ' "$tmpdepfile"
|
||||||
|
if test $stat -ne 0; then
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
exit $stat
|
||||||
|
fi
|
||||||
|
rm -f "$depfile"
|
||||||
|
echo "$object : \\" > "$depfile"
|
||||||
|
# The first sed program below extracts the file names and escapes
|
||||||
|
# backslashes for cygpath. The second sed program outputs the file
|
||||||
|
# name when reading, but also accumulates all include files in the
|
||||||
|
# hold buffer in order to output them again at the end. This only
|
||||||
|
# works with sed implementations that can handle large buffers.
|
||||||
|
sed < "$tmpdepfile" -n '
|
||||||
|
/^Note: including file: *\(.*\)/ {
|
||||||
|
s//\1/
|
||||||
|
s/\\/\\\\/g
|
||||||
|
p
|
||||||
|
}' | $cygpath_u | sort -u | sed -n '
|
||||||
|
s/ /\\ /g
|
||||||
|
s/\(.*\)/'"$tab"'\1 \\/p
|
||||||
|
s/.\(.*\) \\/\1:/
|
||||||
|
H
|
||||||
|
$ {
|
||||||
|
s/.*/'"$tab"'/
|
||||||
|
G
|
||||||
|
p
|
||||||
|
}' >> "$depfile"
|
||||||
|
echo >> "$depfile" # make sure the fragment doesn't end with a backslash
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
;;
|
||||||
|
|
||||||
|
msvc7msys)
|
||||||
|
# This case exists only to let depend.m4 do its work. It works by
|
||||||
|
# looking at the text of this script. This case will never be run,
|
||||||
|
# since it is checked for above.
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
|
||||||
|
#nosideeffect)
|
||||||
|
# This comment above is used by automake to tell side-effect
|
||||||
|
# dependency tracking mechanisms from slower ones.
|
||||||
|
|
||||||
|
dashmstdout)
|
||||||
|
# Important note: in order to support this mode, a compiler *must*
|
||||||
|
# always write the preprocessed file to stdout, regardless of -o.
|
||||||
|
"$@" || exit $?
|
||||||
|
|
||||||
|
# Remove the call to Libtool.
|
||||||
|
if test "$libtool" = yes; then
|
||||||
|
while test "X$1" != 'X--mode=compile'; do
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove '-o $object'.
|
||||||
|
IFS=" "
|
||||||
|
for arg
|
||||||
|
do
|
||||||
|
case $arg in
|
||||||
|
-o)
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
$object)
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
set fnord "$@" "$arg"
|
||||||
|
shift # fnord
|
||||||
|
shift # $arg
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
test -z "$dashmflag" && dashmflag=-M
|
||||||
|
# Require at least two characters before searching for ':'
|
||||||
|
# in the target name. This is to cope with DOS-style filenames:
|
||||||
|
# a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise.
|
||||||
|
"$@" $dashmflag |
|
||||||
|
sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile"
|
||||||
|
rm -f "$depfile"
|
||||||
|
cat < "$tmpdepfile" > "$depfile"
|
||||||
|
# Some versions of the HPUX 10.20 sed can't process this sed invocation
|
||||||
|
# correctly. Breaking it into two sed invocations is a workaround.
|
||||||
|
tr ' ' "$nl" < "$tmpdepfile" \
|
||||||
|
| sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
|
||||||
|
| sed -e 's/$/ :/' >> "$depfile"
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
;;
|
||||||
|
|
||||||
|
dashXmstdout)
|
||||||
|
# This case only exists to satisfy depend.m4. It is never actually
|
||||||
|
# run, as this mode is specially recognized in the preamble.
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
|
||||||
|
makedepend)
|
||||||
|
"$@" || exit $?
|
||||||
|
# Remove any Libtool call
|
||||||
|
if test "$libtool" = yes; then
|
||||||
|
while test "X$1" != 'X--mode=compile'; do
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
# X makedepend
|
||||||
|
shift
|
||||||
|
cleared=no eat=no
|
||||||
|
for arg
|
||||||
|
do
|
||||||
|
case $cleared in
|
||||||
|
no)
|
||||||
|
set ""; shift
|
||||||
|
cleared=yes ;;
|
||||||
|
esac
|
||||||
|
if test $eat = yes; then
|
||||||
|
eat=no
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
case "$arg" in
|
||||||
|
-D*|-I*)
|
||||||
|
set fnord "$@" "$arg"; shift ;;
|
||||||
|
# Strip any option that makedepend may not understand. Remove
|
||||||
|
# the object too, otherwise makedepend will parse it as a source file.
|
||||||
|
-arch)
|
||||||
|
eat=yes ;;
|
||||||
|
-*|$object)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
set fnord "$@" "$arg"; shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
obj_suffix=`echo "$object" | sed 's/^.*\././'`
|
||||||
|
touch "$tmpdepfile"
|
||||||
|
${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
|
||||||
|
rm -f "$depfile"
|
||||||
|
# makedepend may prepend the VPATH from the source file name to the object.
|
||||||
|
# No need to regex-escape $object, excess matching of '.' is harmless.
|
||||||
|
sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile"
|
||||||
|
# Some versions of the HPUX 10.20 sed can't process the last invocation
|
||||||
|
# correctly. Breaking it into two sed invocations is a workaround.
|
||||||
|
sed '1,2d' "$tmpdepfile" \
|
||||||
|
| tr ' ' "$nl" \
|
||||||
|
| sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
|
||||||
|
| sed -e 's/$/ :/' >> "$depfile"
|
||||||
|
rm -f "$tmpdepfile" "$tmpdepfile".bak
|
||||||
|
;;
|
||||||
|
|
||||||
|
cpp)
|
||||||
|
# Important note: in order to support this mode, a compiler *must*
|
||||||
|
# always write the preprocessed file to stdout.
|
||||||
|
"$@" || exit $?
|
||||||
|
|
||||||
|
# Remove the call to Libtool.
|
||||||
|
if test "$libtool" = yes; then
|
||||||
|
while test "X$1" != 'X--mode=compile'; do
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove '-o $object'.
|
||||||
|
IFS=" "
|
||||||
|
for arg
|
||||||
|
do
|
||||||
|
case $arg in
|
||||||
|
-o)
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
$object)
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
set fnord "$@" "$arg"
|
||||||
|
shift # fnord
|
||||||
|
shift # $arg
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
"$@" -E \
|
||||||
|
| sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
|
||||||
|
-e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
|
||||||
|
| sed '$ s: \\$::' > "$tmpdepfile"
|
||||||
|
rm -f "$depfile"
|
||||||
|
echo "$object : \\" > "$depfile"
|
||||||
|
cat < "$tmpdepfile" >> "$depfile"
|
||||||
|
sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
;;
|
||||||
|
|
||||||
|
msvisualcpp)
|
||||||
|
# Important note: in order to support this mode, a compiler *must*
|
||||||
|
# always write the preprocessed file to stdout.
|
||||||
|
"$@" || exit $?
|
||||||
|
|
||||||
|
# Remove the call to Libtool.
|
||||||
|
if test "$libtool" = yes; then
|
||||||
|
while test "X$1" != 'X--mode=compile'; do
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
|
||||||
|
IFS=" "
|
||||||
|
for arg
|
||||||
|
do
|
||||||
|
case "$arg" in
|
||||||
|
-o)
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
$object)
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
"-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
|
||||||
|
set fnord "$@"
|
||||||
|
shift
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
set fnord "$@" "$arg"
|
||||||
|
shift
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
"$@" -E 2>/dev/null |
|
||||||
|
sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
|
||||||
|
rm -f "$depfile"
|
||||||
|
echo "$object : \\" > "$depfile"
|
||||||
|
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile"
|
||||||
|
echo "$tab" >> "$depfile"
|
||||||
|
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
;;
|
||||||
|
|
||||||
|
msvcmsys)
|
||||||
|
# This case exists only to let depend.m4 do its work. It works by
|
||||||
|
# looking at the text of this script. This case will never be run,
|
||||||
|
# since it is checked for above.
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
|
||||||
|
none)
|
||||||
|
exec "$@"
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo "Unknown depmode $depmode" 1>&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
|
||||||
|
# Local Variables:
|
||||||
|
# mode: shell-script
|
||||||
|
# sh-indentation: 2
|
||||||
|
# eval: (add-hook 'before-save-hook 'time-stamp)
|
||||||
|
# time-stamp-start: "scriptversion="
|
||||||
|
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||||
|
# time-stamp-time-zone: "UTC0"
|
||||||
|
# time-stamp-end: "; # UTC"
|
||||||
|
# End:
|
|
@ -0,0 +1,518 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# install - install a program, script, or datafile
|
||||||
|
|
||||||
|
scriptversion=2018-03-11.20; # UTC
|
||||||
|
|
||||||
|
# This originates from X11R5 (mit/util/scripts/install.sh), which was
|
||||||
|
# later released in X11R6 (xc/config/util/install.sh) with the
|
||||||
|
# following copyright and license.
|
||||||
|
#
|
||||||
|
# Copyright (C) 1994 X Consortium
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to
|
||||||
|
# deal in the Software without restriction, including without limitation the
|
||||||
|
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
# sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
|
||||||
|
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
#
|
||||||
|
# Except as contained in this notice, the name of the X Consortium shall not
|
||||||
|
# be used in advertising or otherwise to promote the sale, use or other deal-
|
||||||
|
# ings in this Software without prior written authorization from the X Consor-
|
||||||
|
# tium.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# FSF changes to this file are in the public domain.
|
||||||
|
#
|
||||||
|
# Calling this script install-sh is preferred over install.sh, to prevent
|
||||||
|
# 'make' implicit rules from creating a file called install from it
|
||||||
|
# when there is no Makefile.
|
||||||
|
#
|
||||||
|
# This script is compatible with the BSD install script, but was written
|
||||||
|
# from scratch.
|
||||||
|
|
||||||
|
tab=' '
|
||||||
|
nl='
|
||||||
|
'
|
||||||
|
IFS=" $tab$nl"
|
||||||
|
|
||||||
|
# Set DOITPROG to "echo" to test this script.
|
||||||
|
|
||||||
|
doit=${DOITPROG-}
|
||||||
|
doit_exec=${doit:-exec}
|
||||||
|
|
||||||
|
# Put in absolute file names if you don't have them in your path;
|
||||||
|
# or use environment vars.
|
||||||
|
|
||||||
|
chgrpprog=${CHGRPPROG-chgrp}
|
||||||
|
chmodprog=${CHMODPROG-chmod}
|
||||||
|
chownprog=${CHOWNPROG-chown}
|
||||||
|
cmpprog=${CMPPROG-cmp}
|
||||||
|
cpprog=${CPPROG-cp}
|
||||||
|
mkdirprog=${MKDIRPROG-mkdir}
|
||||||
|
mvprog=${MVPROG-mv}
|
||||||
|
rmprog=${RMPROG-rm}
|
||||||
|
stripprog=${STRIPPROG-strip}
|
||||||
|
|
||||||
|
posix_mkdir=
|
||||||
|
|
||||||
|
# Desired mode of installed file.
|
||||||
|
mode=0755
|
||||||
|
|
||||||
|
chgrpcmd=
|
||||||
|
chmodcmd=$chmodprog
|
||||||
|
chowncmd=
|
||||||
|
mvcmd=$mvprog
|
||||||
|
rmcmd="$rmprog -f"
|
||||||
|
stripcmd=
|
||||||
|
|
||||||
|
src=
|
||||||
|
dst=
|
||||||
|
dir_arg=
|
||||||
|
dst_arg=
|
||||||
|
|
||||||
|
copy_on_change=false
|
||||||
|
is_target_a_directory=possibly
|
||||||
|
|
||||||
|
usage="\
|
||||||
|
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
|
||||||
|
or: $0 [OPTION]... SRCFILES... DIRECTORY
|
||||||
|
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
|
||||||
|
or: $0 [OPTION]... -d DIRECTORIES...
|
||||||
|
|
||||||
|
In the 1st form, copy SRCFILE to DSTFILE.
|
||||||
|
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
|
||||||
|
In the 4th, create DIRECTORIES.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--help display this help and exit.
|
||||||
|
--version display version info and exit.
|
||||||
|
|
||||||
|
-c (ignored)
|
||||||
|
-C install only if different (preserve the last data modification time)
|
||||||
|
-d create directories instead of installing files.
|
||||||
|
-g GROUP $chgrpprog installed files to GROUP.
|
||||||
|
-m MODE $chmodprog installed files to MODE.
|
||||||
|
-o USER $chownprog installed files to USER.
|
||||||
|
-s $stripprog installed files.
|
||||||
|
-t DIRECTORY install into DIRECTORY.
|
||||||
|
-T report an error if DSTFILE is a directory.
|
||||||
|
|
||||||
|
Environment variables override the default commands:
|
||||||
|
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
|
||||||
|
RMPROG STRIPPROG
|
||||||
|
"
|
||||||
|
|
||||||
|
while test $# -ne 0; do
|
||||||
|
case $1 in
|
||||||
|
-c) ;;
|
||||||
|
|
||||||
|
-C) copy_on_change=true;;
|
||||||
|
|
||||||
|
-d) dir_arg=true;;
|
||||||
|
|
||||||
|
-g) chgrpcmd="$chgrpprog $2"
|
||||||
|
shift;;
|
||||||
|
|
||||||
|
--help) echo "$usage"; exit $?;;
|
||||||
|
|
||||||
|
-m) mode=$2
|
||||||
|
case $mode in
|
||||||
|
*' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
|
||||||
|
echo "$0: invalid mode: $mode" >&2
|
||||||
|
exit 1;;
|
||||||
|
esac
|
||||||
|
shift;;
|
||||||
|
|
||||||
|
-o) chowncmd="$chownprog $2"
|
||||||
|
shift;;
|
||||||
|
|
||||||
|
-s) stripcmd=$stripprog;;
|
||||||
|
|
||||||
|
-t)
|
||||||
|
is_target_a_directory=always
|
||||||
|
dst_arg=$2
|
||||||
|
# Protect names problematic for 'test' and other utilities.
|
||||||
|
case $dst_arg in
|
||||||
|
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
||||||
|
esac
|
||||||
|
shift;;
|
||||||
|
|
||||||
|
-T) is_target_a_directory=never;;
|
||||||
|
|
||||||
|
--version) echo "$0 $scriptversion"; exit $?;;
|
||||||
|
|
||||||
|
--) shift
|
||||||
|
break;;
|
||||||
|
|
||||||
|
-*) echo "$0: invalid option: $1" >&2
|
||||||
|
exit 1;;
|
||||||
|
|
||||||
|
*) break;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
# We allow the use of options -d and -T together, by making -d
|
||||||
|
# take the precedence; this is for compatibility with GNU install.
|
||||||
|
|
||||||
|
if test -n "$dir_arg"; then
|
||||||
|
if test -n "$dst_arg"; then
|
||||||
|
echo "$0: target directory not allowed when installing a directory." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
|
||||||
|
# When -d is used, all remaining arguments are directories to create.
|
||||||
|
# When -t is used, the destination is already specified.
|
||||||
|
# Otherwise, the last argument is the destination. Remove it from $@.
|
||||||
|
for arg
|
||||||
|
do
|
||||||
|
if test -n "$dst_arg"; then
|
||||||
|
# $@ is not empty: it contains at least $arg.
|
||||||
|
set fnord "$@" "$dst_arg"
|
||||||
|
shift # fnord
|
||||||
|
fi
|
||||||
|
shift # arg
|
||||||
|
dst_arg=$arg
|
||||||
|
# Protect names problematic for 'test' and other utilities.
|
||||||
|
case $dst_arg in
|
||||||
|
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test $# -eq 0; then
|
||||||
|
if test -z "$dir_arg"; then
|
||||||
|
echo "$0: no input file specified." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# It's OK to call 'install-sh -d' without argument.
|
||||||
|
# This can happen when creating conditional directories.
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test -z "$dir_arg"; then
|
||||||
|
if test $# -gt 1 || test "$is_target_a_directory" = always; then
|
||||||
|
if test ! -d "$dst_arg"; then
|
||||||
|
echo "$0: $dst_arg: Is not a directory." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test -z "$dir_arg"; then
|
||||||
|
do_exit='(exit $ret); exit $ret'
|
||||||
|
trap "ret=129; $do_exit" 1
|
||||||
|
trap "ret=130; $do_exit" 2
|
||||||
|
trap "ret=141; $do_exit" 13
|
||||||
|
trap "ret=143; $do_exit" 15
|
||||||
|
|
||||||
|
# Set umask so as not to create temps with too-generous modes.
|
||||||
|
# However, 'strip' requires both read and write access to temps.
|
||||||
|
case $mode in
|
||||||
|
# Optimize common cases.
|
||||||
|
*644) cp_umask=133;;
|
||||||
|
*755) cp_umask=22;;
|
||||||
|
|
||||||
|
*[0-7])
|
||||||
|
if test -z "$stripcmd"; then
|
||||||
|
u_plus_rw=
|
||||||
|
else
|
||||||
|
u_plus_rw='% 200'
|
||||||
|
fi
|
||||||
|
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
|
||||||
|
*)
|
||||||
|
if test -z "$stripcmd"; then
|
||||||
|
u_plus_rw=
|
||||||
|
else
|
||||||
|
u_plus_rw=,u+rw
|
||||||
|
fi
|
||||||
|
cp_umask=$mode$u_plus_rw;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
for src
|
||||||
|
do
|
||||||
|
# Protect names problematic for 'test' and other utilities.
|
||||||
|
case $src in
|
||||||
|
-* | [=\(\)!]) src=./$src;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if test -n "$dir_arg"; then
|
||||||
|
dst=$src
|
||||||
|
dstdir=$dst
|
||||||
|
test -d "$dstdir"
|
||||||
|
dstdir_status=$?
|
||||||
|
else
|
||||||
|
|
||||||
|
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
|
||||||
|
# might cause directories to be created, which would be especially bad
|
||||||
|
# if $src (and thus $dsttmp) contains '*'.
|
||||||
|
if test ! -f "$src" && test ! -d "$src"; then
|
||||||
|
echo "$0: $src does not exist." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test -z "$dst_arg"; then
|
||||||
|
echo "$0: no destination specified." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
dst=$dst_arg
|
||||||
|
|
||||||
|
# If destination is a directory, append the input filename.
|
||||||
|
if test -d "$dst"; then
|
||||||
|
if test "$is_target_a_directory" = never; then
|
||||||
|
echo "$0: $dst_arg: Is a directory" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
dstdir=$dst
|
||||||
|
dstbase=`basename "$src"`
|
||||||
|
case $dst in
|
||||||
|
*/) dst=$dst$dstbase;;
|
||||||
|
*) dst=$dst/$dstbase;;
|
||||||
|
esac
|
||||||
|
dstdir_status=0
|
||||||
|
else
|
||||||
|
dstdir=`dirname "$dst"`
|
||||||
|
test -d "$dstdir"
|
||||||
|
dstdir_status=$?
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
case $dstdir in
|
||||||
|
*/) dstdirslash=$dstdir;;
|
||||||
|
*) dstdirslash=$dstdir/;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
obsolete_mkdir_used=false
|
||||||
|
|
||||||
|
if test $dstdir_status != 0; then
|
||||||
|
case $posix_mkdir in
|
||||||
|
'')
|
||||||
|
# Create intermediate dirs using mode 755 as modified by the umask.
|
||||||
|
# This is like FreeBSD 'install' as of 1997-10-28.
|
||||||
|
umask=`umask`
|
||||||
|
case $stripcmd.$umask in
|
||||||
|
# Optimize common cases.
|
||||||
|
*[2367][2367]) mkdir_umask=$umask;;
|
||||||
|
.*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
|
||||||
|
|
||||||
|
*[0-7])
|
||||||
|
mkdir_umask=`expr $umask + 22 \
|
||||||
|
- $umask % 100 % 40 + $umask % 20 \
|
||||||
|
- $umask % 10 % 4 + $umask % 2
|
||||||
|
`;;
|
||||||
|
*) mkdir_umask=$umask,go-w;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# With -d, create the new directory with the user-specified mode.
|
||||||
|
# Otherwise, rely on $mkdir_umask.
|
||||||
|
if test -n "$dir_arg"; then
|
||||||
|
mkdir_mode=-m$mode
|
||||||
|
else
|
||||||
|
mkdir_mode=
|
||||||
|
fi
|
||||||
|
|
||||||
|
posix_mkdir=false
|
||||||
|
case $umask in
|
||||||
|
*[123567][0-7][0-7])
|
||||||
|
# POSIX mkdir -p sets u+wx bits regardless of umask, which
|
||||||
|
# is incompatible with FreeBSD 'install' when (umask & 300) != 0.
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# Note that $RANDOM variable is not portable (e.g. dash); Use it
|
||||||
|
# here however when possible just to lower collision chance.
|
||||||
|
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
|
||||||
|
|
||||||
|
trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null; exit $ret' 0
|
||||||
|
|
||||||
|
# Because "mkdir -p" follows existing symlinks and we likely work
|
||||||
|
# directly in world-writeable /tmp, make sure that the '$tmpdir'
|
||||||
|
# directory is successfully created first before we actually test
|
||||||
|
# 'mkdir -p' feature.
|
||||||
|
if (umask $mkdir_umask &&
|
||||||
|
$mkdirprog $mkdir_mode "$tmpdir" &&
|
||||||
|
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
if test -z "$dir_arg" || {
|
||||||
|
# Check for POSIX incompatibilities with -m.
|
||||||
|
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
|
||||||
|
# other-writable bit of parent directory when it shouldn't.
|
||||||
|
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
|
||||||
|
test_tmpdir="$tmpdir/a"
|
||||||
|
ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
|
||||||
|
case $ls_ld_tmpdir in
|
||||||
|
d????-?r-*) different_mode=700;;
|
||||||
|
d????-?--*) different_mode=755;;
|
||||||
|
*) false;;
|
||||||
|
esac &&
|
||||||
|
$mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
|
||||||
|
ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
|
||||||
|
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
then posix_mkdir=:
|
||||||
|
fi
|
||||||
|
rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
|
||||||
|
else
|
||||||
|
# Remove any dirs left behind by ancient mkdir implementations.
|
||||||
|
rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
|
||||||
|
fi
|
||||||
|
trap '' 0;;
|
||||||
|
esac;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if
|
||||||
|
$posix_mkdir && (
|
||||||
|
umask $mkdir_umask &&
|
||||||
|
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
|
||||||
|
)
|
||||||
|
then :
|
||||||
|
else
|
||||||
|
|
||||||
|
# The umask is ridiculous, or mkdir does not conform to POSIX,
|
||||||
|
# or it failed possibly due to a race condition. Create the
|
||||||
|
# directory the slow way, step by step, checking for races as we go.
|
||||||
|
|
||||||
|
case $dstdir in
|
||||||
|
/*) prefix='/';;
|
||||||
|
[-=\(\)!]*) prefix='./';;
|
||||||
|
*) prefix='';;
|
||||||
|
esac
|
||||||
|
|
||||||
|
oIFS=$IFS
|
||||||
|
IFS=/
|
||||||
|
set -f
|
||||||
|
set fnord $dstdir
|
||||||
|
shift
|
||||||
|
set +f
|
||||||
|
IFS=$oIFS
|
||||||
|
|
||||||
|
prefixes=
|
||||||
|
|
||||||
|
for d
|
||||||
|
do
|
||||||
|
test X"$d" = X && continue
|
||||||
|
|
||||||
|
prefix=$prefix$d
|
||||||
|
if test -d "$prefix"; then
|
||||||
|
prefixes=
|
||||||
|
else
|
||||||
|
if $posix_mkdir; then
|
||||||
|
(umask=$mkdir_umask &&
|
||||||
|
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
|
||||||
|
# Don't fail if two instances are running concurrently.
|
||||||
|
test -d "$prefix" || exit 1
|
||||||
|
else
|
||||||
|
case $prefix in
|
||||||
|
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
|
||||||
|
*) qprefix=$prefix;;
|
||||||
|
esac
|
||||||
|
prefixes="$prefixes '$qprefix'"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
prefix=$prefix/
|
||||||
|
done
|
||||||
|
|
||||||
|
if test -n "$prefixes"; then
|
||||||
|
# Don't fail if two instances are running concurrently.
|
||||||
|
(umask $mkdir_umask &&
|
||||||
|
eval "\$doit_exec \$mkdirprog $prefixes") ||
|
||||||
|
test -d "$dstdir" || exit 1
|
||||||
|
obsolete_mkdir_used=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test -n "$dir_arg"; then
|
||||||
|
{ test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
|
||||||
|
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
|
||||||
|
{ test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
|
||||||
|
test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
|
||||||
|
else
|
||||||
|
|
||||||
|
# Make a couple of temp file names in the proper directory.
|
||||||
|
dsttmp=${dstdirslash}_inst.$$_
|
||||||
|
rmtmp=${dstdirslash}_rm.$$_
|
||||||
|
|
||||||
|
# Trap to clean up those temp files at exit.
|
||||||
|
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
|
||||||
|
|
||||||
|
# Copy the file name to the temp name.
|
||||||
|
(umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
|
||||||
|
|
||||||
|
# and set any options; do chmod last to preserve setuid bits.
|
||||||
|
#
|
||||||
|
# If any of these fail, we abort the whole thing. If we want to
|
||||||
|
# ignore errors from any of these, just make sure not to ignore
|
||||||
|
# errors from the above "$doit $cpprog $src $dsttmp" command.
|
||||||
|
#
|
||||||
|
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
|
||||||
|
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
|
||||||
|
{ test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
|
||||||
|
{ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
|
||||||
|
|
||||||
|
# If -C, don't bother to copy if it wouldn't change the file.
|
||||||
|
if $copy_on_change &&
|
||||||
|
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
|
||||||
|
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
|
||||||
|
set -f &&
|
||||||
|
set X $old && old=:$2:$4:$5:$6 &&
|
||||||
|
set X $new && new=:$2:$4:$5:$6 &&
|
||||||
|
set +f &&
|
||||||
|
test "$old" = "$new" &&
|
||||||
|
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
rm -f "$dsttmp"
|
||||||
|
else
|
||||||
|
# Rename the file to the real destination.
|
||||||
|
$doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
|
||||||
|
|
||||||
|
# The rename failed, perhaps because mv can't rename something else
|
||||||
|
# to itself, or perhaps because mv is so ancient that it does not
|
||||||
|
# support -f.
|
||||||
|
{
|
||||||
|
# Now remove or move aside any old file at destination location.
|
||||||
|
# We try this two ways since rm can't unlink itself on some
|
||||||
|
# systems and the destination file might be busy for other
|
||||||
|
# reasons. In this case, the final cleanup might fail but the new
|
||||||
|
# file should still install successfully.
|
||||||
|
{
|
||||||
|
test ! -f "$dst" ||
|
||||||
|
$doit $rmcmd -f "$dst" 2>/dev/null ||
|
||||||
|
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
|
||||||
|
{ $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
|
||||||
|
} ||
|
||||||
|
{ echo "$0: cannot unlink or rename $dst" >&2
|
||||||
|
(exit 1); exit 1
|
||||||
|
}
|
||||||
|
} &&
|
||||||
|
|
||||||
|
# Now rename the file to the real destination.
|
||||||
|
$doit $mvcmd "$dsttmp" "$dst"
|
||||||
|
}
|
||||||
|
fi || exit 1
|
||||||
|
|
||||||
|
trap '' 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Local variables:
|
||||||
|
# eval: (add-hook 'before-save-hook 'time-stamp)
|
||||||
|
# time-stamp-start: "scriptversion="
|
||||||
|
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||||
|
# time-stamp-time-zone: "UTC0"
|
||||||
|
# time-stamp-end: "; # UTC"
|
||||||
|
# End:
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,215 @@
|
||||||
|
#! /bin/sh
|
||||||
|
# Common wrapper for a few potentially missing GNU programs.
|
||||||
|
|
||||||
|
scriptversion=2018-03-07.03; # UTC
|
||||||
|
|
||||||
|
# Copyright (C) 1996-2018 Free Software Foundation, Inc.
|
||||||
|
# Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
|
||||||
|
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
# any later version.
|
||||||
|
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# As a special exception to the GNU General Public License, if you
|
||||||
|
# distribute this file as part of a program that contains a
|
||||||
|
# configuration script generated by Autoconf, you may include it under
|
||||||
|
# the same distribution terms that you use for the rest of that program.
|
||||||
|
|
||||||
|
if test $# -eq 0; then
|
||||||
|
echo 1>&2 "Try '$0 --help' for more information"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
case $1 in
|
||||||
|
|
||||||
|
--is-lightweight)
|
||||||
|
# Used by our autoconf macros to check whether the available missing
|
||||||
|
# script is modern enough.
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
|
||||||
|
--run)
|
||||||
|
# Back-compat with the calling convention used by older automake.
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
|
||||||
|
-h|--h|--he|--hel|--help)
|
||||||
|
echo "\
|
||||||
|
$0 [OPTION]... PROGRAM [ARGUMENT]...
|
||||||
|
|
||||||
|
Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due
|
||||||
|
to PROGRAM being missing or too old.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h, --help display this help and exit
|
||||||
|
-v, --version output version information and exit
|
||||||
|
|
||||||
|
Supported PROGRAM values:
|
||||||
|
aclocal autoconf autoheader autom4te automake makeinfo
|
||||||
|
bison yacc flex lex help2man
|
||||||
|
|
||||||
|
Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and
|
||||||
|
'g' are ignored when checking the name.
|
||||||
|
|
||||||
|
Send bug reports to <bug-automake@gnu.org>."
|
||||||
|
exit $?
|
||||||
|
;;
|
||||||
|
|
||||||
|
-v|--v|--ve|--ver|--vers|--versi|--versio|--version)
|
||||||
|
echo "missing $scriptversion (GNU Automake)"
|
||||||
|
exit $?
|
||||||
|
;;
|
||||||
|
|
||||||
|
-*)
|
||||||
|
echo 1>&2 "$0: unknown '$1' option"
|
||||||
|
echo 1>&2 "Try '$0 --help' for more information"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Run the given program, remember its exit status.
|
||||||
|
"$@"; st=$?
|
||||||
|
|
||||||
|
# If it succeeded, we are done.
|
||||||
|
test $st -eq 0 && exit 0
|
||||||
|
|
||||||
|
# Also exit now if we it failed (or wasn't found), and '--version' was
|
||||||
|
# passed; such an option is passed most likely to detect whether the
|
||||||
|
# program is present and works.
|
||||||
|
case $2 in --version|--help) exit $st;; esac
|
||||||
|
|
||||||
|
# Exit code 63 means version mismatch. This often happens when the user
|
||||||
|
# tries to use an ancient version of a tool on a file that requires a
|
||||||
|
# minimum version.
|
||||||
|
if test $st -eq 63; then
|
||||||
|
msg="probably too old"
|
||||||
|
elif test $st -eq 127; then
|
||||||
|
# Program was missing.
|
||||||
|
msg="missing on your system"
|
||||||
|
else
|
||||||
|
# Program was found and executed, but failed. Give up.
|
||||||
|
exit $st
|
||||||
|
fi
|
||||||
|
|
||||||
|
perl_URL=https://www.perl.org/
|
||||||
|
flex_URL=https://github.com/westes/flex
|
||||||
|
gnu_software_URL=https://www.gnu.org/software
|
||||||
|
|
||||||
|
program_details ()
|
||||||
|
{
|
||||||
|
case $1 in
|
||||||
|
aclocal|automake)
|
||||||
|
echo "The '$1' program is part of the GNU Automake package:"
|
||||||
|
echo "<$gnu_software_URL/automake>"
|
||||||
|
echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:"
|
||||||
|
echo "<$gnu_software_URL/autoconf>"
|
||||||
|
echo "<$gnu_software_URL/m4/>"
|
||||||
|
echo "<$perl_URL>"
|
||||||
|
;;
|
||||||
|
autoconf|autom4te|autoheader)
|
||||||
|
echo "The '$1' program is part of the GNU Autoconf package:"
|
||||||
|
echo "<$gnu_software_URL/autoconf/>"
|
||||||
|
echo "It also requires GNU m4 and Perl in order to run:"
|
||||||
|
echo "<$gnu_software_URL/m4/>"
|
||||||
|
echo "<$perl_URL>"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
give_advice ()
|
||||||
|
{
|
||||||
|
# Normalize program name to check for.
|
||||||
|
normalized_program=`echo "$1" | sed '
|
||||||
|
s/^gnu-//; t
|
||||||
|
s/^gnu//; t
|
||||||
|
s/^g//; t'`
|
||||||
|
|
||||||
|
printf '%s\n' "'$1' is $msg."
|
||||||
|
|
||||||
|
configure_deps="'configure.ac' or m4 files included by 'configure.ac'"
|
||||||
|
case $normalized_program in
|
||||||
|
autoconf*)
|
||||||
|
echo "You should only need it if you modified 'configure.ac',"
|
||||||
|
echo "or m4 files included by it."
|
||||||
|
program_details 'autoconf'
|
||||||
|
;;
|
||||||
|
autoheader*)
|
||||||
|
echo "You should only need it if you modified 'acconfig.h' or"
|
||||||
|
echo "$configure_deps."
|
||||||
|
program_details 'autoheader'
|
||||||
|
;;
|
||||||
|
automake*)
|
||||||
|
echo "You should only need it if you modified 'Makefile.am' or"
|
||||||
|
echo "$configure_deps."
|
||||||
|
program_details 'automake'
|
||||||
|
;;
|
||||||
|
aclocal*)
|
||||||
|
echo "You should only need it if you modified 'acinclude.m4' or"
|
||||||
|
echo "$configure_deps."
|
||||||
|
program_details 'aclocal'
|
||||||
|
;;
|
||||||
|
autom4te*)
|
||||||
|
echo "You might have modified some maintainer files that require"
|
||||||
|
echo "the 'autom4te' program to be rebuilt."
|
||||||
|
program_details 'autom4te'
|
||||||
|
;;
|
||||||
|
bison*|yacc*)
|
||||||
|
echo "You should only need it if you modified a '.y' file."
|
||||||
|
echo "You may want to install the GNU Bison package:"
|
||||||
|
echo "<$gnu_software_URL/bison/>"
|
||||||
|
;;
|
||||||
|
lex*|flex*)
|
||||||
|
echo "You should only need it if you modified a '.l' file."
|
||||||
|
echo "You may want to install the Fast Lexical Analyzer package:"
|
||||||
|
echo "<$flex_URL>"
|
||||||
|
;;
|
||||||
|
help2man*)
|
||||||
|
echo "You should only need it if you modified a dependency" \
|
||||||
|
"of a man page."
|
||||||
|
echo "You may want to install the GNU Help2man package:"
|
||||||
|
echo "<$gnu_software_URL/help2man/>"
|
||||||
|
;;
|
||||||
|
makeinfo*)
|
||||||
|
echo "You should only need it if you modified a '.texi' file, or"
|
||||||
|
echo "any other file indirectly affecting the aspect of the manual."
|
||||||
|
echo "You might want to install the Texinfo package:"
|
||||||
|
echo "<$gnu_software_URL/texinfo/>"
|
||||||
|
echo "The spurious makeinfo call might also be the consequence of"
|
||||||
|
echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might"
|
||||||
|
echo "want to install GNU make:"
|
||||||
|
echo "<$gnu_software_URL/make/>"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "You might have modified some files without having the proper"
|
||||||
|
echo "tools for further handling them. Check the 'README' file, it"
|
||||||
|
echo "often tells you about the needed prerequisites for installing"
|
||||||
|
echo "this package. You may also peek at any GNU archive site, in"
|
||||||
|
echo "case some other package contains this missing '$1' program."
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
give_advice "$1" | sed -e '1s/^/WARNING: /' \
|
||||||
|
-e '2,$s/^/ /' >&2
|
||||||
|
|
||||||
|
# Propagate the correct exit status (expected to be 127 for a program
|
||||||
|
# not found, 63 for a program that failed due to version mismatch).
|
||||||
|
exit $st
|
||||||
|
|
||||||
|
# Local variables:
|
||||||
|
# eval: (add-hook 'before-save-hook 'time-stamp)
|
||||||
|
# time-stamp-start: "scriptversion="
|
||||||
|
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||||
|
# time-stamp-time-zone: "UTC0"
|
||||||
|
# time-stamp-end: "; # UTC"
|
||||||
|
# End:
|
|
@ -0,0 +1,651 @@
|
||||||
|
#! /bin/sh
|
||||||
|
# Copyright (C) 2011-2018 Free Software Foundation, Inc.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
# any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# As a special exception to the GNU General Public License, if you
|
||||||
|
# distribute this file as part of a program that contains a
|
||||||
|
# configuration script generated by Autoconf, you may include it under
|
||||||
|
# the same distribution terms that you use for the rest of that program.
|
||||||
|
|
||||||
|
# This file is maintained in Automake, please report
|
||||||
|
# bugs to <bug-automake@gnu.org> or send patches to
|
||||||
|
# <automake-patches@gnu.org>.
|
||||||
|
|
||||||
|
scriptversion=2013-12-23.17; # UTC
|
||||||
|
|
||||||
|
# Make unconditional expansion of undefined variables an error. This
|
||||||
|
# helps a lot in preventing typo-related bugs.
|
||||||
|
set -u
|
||||||
|
|
||||||
|
me=tap-driver.sh
|
||||||
|
|
||||||
|
fatal ()
|
||||||
|
{
|
||||||
|
echo "$me: fatal: $*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
usage_error ()
|
||||||
|
{
|
||||||
|
echo "$me: $*" >&2
|
||||||
|
print_usage >&2
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
print_usage ()
|
||||||
|
{
|
||||||
|
cat <<END
|
||||||
|
Usage:
|
||||||
|
tap-driver.sh --test-name=NAME --log-file=PATH --trs-file=PATH
|
||||||
|
[--expect-failure={yes|no}] [--color-tests={yes|no}]
|
||||||
|
[--enable-hard-errors={yes|no}] [--ignore-exit]
|
||||||
|
[--diagnostic-string=STRING] [--merge|--no-merge]
|
||||||
|
[--comments|--no-comments] [--] TEST-COMMAND
|
||||||
|
The '--test-name', '-log-file' and '--trs-file' options are mandatory.
|
||||||
|
END
|
||||||
|
}
|
||||||
|
|
||||||
|
# TODO: better error handling in option parsing (in particular, ensure
|
||||||
|
# TODO: $log_file, $trs_file and $test_name are defined).
|
||||||
|
test_name= # Used for reporting.
|
||||||
|
log_file= # Where to save the result and output of the test script.
|
||||||
|
trs_file= # Where to save the metadata of the test run.
|
||||||
|
expect_failure=0
|
||||||
|
color_tests=0
|
||||||
|
merge=0
|
||||||
|
ignore_exit=0
|
||||||
|
comments=0
|
||||||
|
diag_string='#'
|
||||||
|
while test $# -gt 0; do
|
||||||
|
case $1 in
|
||||||
|
--help) print_usage; exit $?;;
|
||||||
|
--version) echo "$me $scriptversion"; exit $?;;
|
||||||
|
--test-name) test_name=$2; shift;;
|
||||||
|
--log-file) log_file=$2; shift;;
|
||||||
|
--trs-file) trs_file=$2; shift;;
|
||||||
|
--color-tests) color_tests=$2; shift;;
|
||||||
|
--expect-failure) expect_failure=$2; shift;;
|
||||||
|
--enable-hard-errors) shift;; # No-op.
|
||||||
|
--merge) merge=1;;
|
||||||
|
--no-merge) merge=0;;
|
||||||
|
--ignore-exit) ignore_exit=1;;
|
||||||
|
--comments) comments=1;;
|
||||||
|
--no-comments) comments=0;;
|
||||||
|
--diagnostic-string) diag_string=$2; shift;;
|
||||||
|
--) shift; break;;
|
||||||
|
-*) usage_error "invalid option: '$1'";;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
test $# -gt 0 || usage_error "missing test command"
|
||||||
|
|
||||||
|
case $expect_failure in
|
||||||
|
yes) expect_failure=1;;
|
||||||
|
*) expect_failure=0;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if test $color_tests = yes; then
|
||||||
|
init_colors='
|
||||||
|
color_map["red"]="[0;31m" # Red.
|
||||||
|
color_map["grn"]="[0;32m" # Green.
|
||||||
|
color_map["lgn"]="[1;32m" # Light green.
|
||||||
|
color_map["blu"]="[1;34m" # Blue.
|
||||||
|
color_map["mgn"]="[0;35m" # Magenta.
|
||||||
|
color_map["std"]="[m" # No color.
|
||||||
|
color_for_result["ERROR"] = "mgn"
|
||||||
|
color_for_result["PASS"] = "grn"
|
||||||
|
color_for_result["XPASS"] = "red"
|
||||||
|
color_for_result["FAIL"] = "red"
|
||||||
|
color_for_result["XFAIL"] = "lgn"
|
||||||
|
color_for_result["SKIP"] = "blu"'
|
||||||
|
else
|
||||||
|
init_colors=''
|
||||||
|
fi
|
||||||
|
|
||||||
|
# :; is there to work around a bug in bash 3.2 (and earlier) which
|
||||||
|
# does not always set '$?' properly on redirection failure.
|
||||||
|
# See the Autoconf manual for more details.
|
||||||
|
:;{
|
||||||
|
(
|
||||||
|
# Ignore common signals (in this subshell only!), to avoid potential
|
||||||
|
# problems with Korn shells. Some Korn shells are known to propagate
|
||||||
|
# to themselves signals that have killed a child process they were
|
||||||
|
# waiting for; this is done at least for SIGINT (and usually only for
|
||||||
|
# it, in truth). Without the `trap' below, such a behaviour could
|
||||||
|
# cause a premature exit in the current subshell, e.g., in case the
|
||||||
|
# test command it runs gets terminated by a SIGINT. Thus, the awk
|
||||||
|
# script we are piping into would never seen the exit status it
|
||||||
|
# expects on its last input line (which is displayed below by the
|
||||||
|
# last `echo $?' statement), and would thus die reporting an internal
|
||||||
|
# error.
|
||||||
|
# For more information, see the Autoconf manual and the threads:
|
||||||
|
# <https://lists.gnu.org/archive/html/bug-autoconf/2011-09/msg00004.html>
|
||||||
|
# <http://mail.opensolaris.org/pipermail/ksh93-integration-discuss/2009-February/004121.html>
|
||||||
|
trap : 1 3 2 13 15
|
||||||
|
if test $merge -gt 0; then
|
||||||
|
exec 2>&1
|
||||||
|
else
|
||||||
|
exec 2>&3
|
||||||
|
fi
|
||||||
|
"$@"
|
||||||
|
echo $?
|
||||||
|
) | LC_ALL=C ${AM_TAP_AWK-awk} \
|
||||||
|
-v me="$me" \
|
||||||
|
-v test_script_name="$test_name" \
|
||||||
|
-v log_file="$log_file" \
|
||||||
|
-v trs_file="$trs_file" \
|
||||||
|
-v expect_failure="$expect_failure" \
|
||||||
|
-v merge="$merge" \
|
||||||
|
-v ignore_exit="$ignore_exit" \
|
||||||
|
-v comments="$comments" \
|
||||||
|
-v diag_string="$diag_string" \
|
||||||
|
'
|
||||||
|
# TODO: the usages of "cat >&3" below could be optimized when using
|
||||||
|
# GNU awk, and/on on systems that supports /dev/fd/.
|
||||||
|
|
||||||
|
# Implementation note: in what follows, `result_obj` will be an
|
||||||
|
# associative array that (partly) simulates a TAP result object
|
||||||
|
# from the `TAP::Parser` perl module.
|
||||||
|
|
||||||
|
## ----------- ##
|
||||||
|
## FUNCTIONS ##
|
||||||
|
## ----------- ##
|
||||||
|
|
||||||
|
function fatal(msg)
|
||||||
|
{
|
||||||
|
print me ": " msg | "cat >&2"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function abort(where)
|
||||||
|
{
|
||||||
|
fatal("internal error " where)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Convert a boolean to a "yes"/"no" string.
|
||||||
|
function yn(bool)
|
||||||
|
{
|
||||||
|
return bool ? "yes" : "no";
|
||||||
|
}
|
||||||
|
|
||||||
|
function add_test_result(result)
|
||||||
|
{
|
||||||
|
if (!test_results_index)
|
||||||
|
test_results_index = 0
|
||||||
|
test_results_list[test_results_index] = result
|
||||||
|
test_results_index += 1
|
||||||
|
test_results_seen[result] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Whether the test script should be re-run by "make recheck".
|
||||||
|
function must_recheck()
|
||||||
|
{
|
||||||
|
for (k in test_results_seen)
|
||||||
|
if (k != "XFAIL" && k != "PASS" && k != "SKIP")
|
||||||
|
return 1
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Whether the content of the log file associated to this test should
|
||||||
|
# be copied into the "global" test-suite.log.
|
||||||
|
function copy_in_global_log()
|
||||||
|
{
|
||||||
|
for (k in test_results_seen)
|
||||||
|
if (k != "PASS")
|
||||||
|
return 1
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_global_test_result()
|
||||||
|
{
|
||||||
|
if ("ERROR" in test_results_seen)
|
||||||
|
return "ERROR"
|
||||||
|
if ("FAIL" in test_results_seen || "XPASS" in test_results_seen)
|
||||||
|
return "FAIL"
|
||||||
|
all_skipped = 1
|
||||||
|
for (k in test_results_seen)
|
||||||
|
if (k != "SKIP")
|
||||||
|
all_skipped = 0
|
||||||
|
if (all_skipped)
|
||||||
|
return "SKIP"
|
||||||
|
return "PASS";
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringify_result_obj(result_obj)
|
||||||
|
{
|
||||||
|
if (result_obj["is_unplanned"] || result_obj["number"] != testno)
|
||||||
|
return "ERROR"
|
||||||
|
|
||||||
|
if (plan_seen == LATE_PLAN)
|
||||||
|
return "ERROR"
|
||||||
|
|
||||||
|
if (result_obj["directive"] == "TODO")
|
||||||
|
return result_obj["is_ok"] ? "XPASS" : "XFAIL"
|
||||||
|
|
||||||
|
if (result_obj["directive"] == "SKIP")
|
||||||
|
return result_obj["is_ok"] ? "SKIP" : COOKED_FAIL;
|
||||||
|
|
||||||
|
if (length(result_obj["directive"]))
|
||||||
|
abort("in function stringify_result_obj()")
|
||||||
|
|
||||||
|
return result_obj["is_ok"] ? COOKED_PASS : COOKED_FAIL
|
||||||
|
}
|
||||||
|
|
||||||
|
function decorate_result(result)
|
||||||
|
{
|
||||||
|
color_name = color_for_result[result]
|
||||||
|
if (color_name)
|
||||||
|
return color_map[color_name] "" result "" color_map["std"]
|
||||||
|
# If we are not using colorized output, or if we do not know how
|
||||||
|
# to colorize the given result, we should return it unchanged.
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
function report(result, details)
|
||||||
|
{
|
||||||
|
if (result ~ /^(X?(PASS|FAIL)|SKIP|ERROR)/)
|
||||||
|
{
|
||||||
|
msg = ": " test_script_name
|
||||||
|
add_test_result(result)
|
||||||
|
}
|
||||||
|
else if (result == "#")
|
||||||
|
{
|
||||||
|
msg = " " test_script_name ":"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
abort("in function report()")
|
||||||
|
}
|
||||||
|
if (length(details))
|
||||||
|
msg = msg " " details
|
||||||
|
# Output on console might be colorized.
|
||||||
|
print decorate_result(result) msg
|
||||||
|
# Log the result in the log file too, to help debugging (this is
|
||||||
|
# especially true when said result is a TAP error or "Bail out!").
|
||||||
|
print result msg | "cat >&3";
|
||||||
|
}
|
||||||
|
|
||||||
|
function testsuite_error(error_message)
|
||||||
|
{
|
||||||
|
report("ERROR", "- " error_message)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handle_tap_result()
|
||||||
|
{
|
||||||
|
details = result_obj["number"];
|
||||||
|
if (length(result_obj["description"]))
|
||||||
|
details = details " " result_obj["description"]
|
||||||
|
|
||||||
|
if (plan_seen == LATE_PLAN)
|
||||||
|
{
|
||||||
|
details = details " # AFTER LATE PLAN";
|
||||||
|
}
|
||||||
|
else if (result_obj["is_unplanned"])
|
||||||
|
{
|
||||||
|
details = details " # UNPLANNED";
|
||||||
|
}
|
||||||
|
else if (result_obj["number"] != testno)
|
||||||
|
{
|
||||||
|
details = sprintf("%s # OUT-OF-ORDER (expecting %d)",
|
||||||
|
details, testno);
|
||||||
|
}
|
||||||
|
else if (result_obj["directive"])
|
||||||
|
{
|
||||||
|
details = details " # " result_obj["directive"];
|
||||||
|
if (length(result_obj["explanation"]))
|
||||||
|
details = details " " result_obj["explanation"]
|
||||||
|
}
|
||||||
|
|
||||||
|
report(stringify_result_obj(result_obj), details)
|
||||||
|
}
|
||||||
|
|
||||||
|
# `skip_reason` should be empty whenever planned > 0.
|
||||||
|
function handle_tap_plan(planned, skip_reason)
|
||||||
|
{
|
||||||
|
planned += 0 # Avoid getting confused if, say, `planned` is "00"
|
||||||
|
if (length(skip_reason) && planned > 0)
|
||||||
|
abort("in function handle_tap_plan()")
|
||||||
|
if (plan_seen)
|
||||||
|
{
|
||||||
|
# Error, only one plan per stream is acceptable.
|
||||||
|
testsuite_error("multiple test plans")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
planned_tests = planned
|
||||||
|
# The TAP plan can come before or after *all* the TAP results; we speak
|
||||||
|
# respectively of an "early" or a "late" plan. If we see the plan line
|
||||||
|
# after at least one TAP result has been seen, assume we have a late
|
||||||
|
# plan; in this case, any further test result seen after the plan will
|
||||||
|
# be flagged as an error.
|
||||||
|
plan_seen = (testno >= 1 ? LATE_PLAN : EARLY_PLAN)
|
||||||
|
# If testno > 0, we have an error ("too many tests run") that will be
|
||||||
|
# automatically dealt with later, so do not worry about it here. If
|
||||||
|
# $plan_seen is true, we have an error due to a repeated plan, and that
|
||||||
|
# has already been dealt with above. Otherwise, we have a valid "plan
|
||||||
|
# with SKIP" specification, and should report it as a particular kind
|
||||||
|
# of SKIP result.
|
||||||
|
if (planned == 0 && testno == 0)
|
||||||
|
{
|
||||||
|
if (length(skip_reason))
|
||||||
|
skip_reason = "- " skip_reason;
|
||||||
|
report("SKIP", skip_reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function extract_tap_comment(line)
|
||||||
|
{
|
||||||
|
if (index(line, diag_string) == 1)
|
||||||
|
{
|
||||||
|
# Strip leading `diag_string` from `line`.
|
||||||
|
line = substr(line, length(diag_string) + 1)
|
||||||
|
# And strip any leading and trailing whitespace left.
|
||||||
|
sub("^[ \t]*", "", line)
|
||||||
|
sub("[ \t]*$", "", line)
|
||||||
|
# Return what is left (if any).
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
# When this function is called, we know that line is a TAP result line,
|
||||||
|
# so that it matches the (perl) RE "^(not )?ok\b".
|
||||||
|
function setup_result_obj(line)
|
||||||
|
{
|
||||||
|
# Get the result, and remove it from the line.
|
||||||
|
result_obj["is_ok"] = (substr(line, 1, 2) == "ok" ? 1 : 0)
|
||||||
|
sub("^(not )?ok[ \t]*", "", line)
|
||||||
|
|
||||||
|
# If the result has an explicit number, get it and strip it; otherwise,
|
||||||
|
# automatically assing the next progresive number to it.
|
||||||
|
if (line ~ /^[0-9]+$/ || line ~ /^[0-9]+[^a-zA-Z0-9_]/)
|
||||||
|
{
|
||||||
|
match(line, "^[0-9]+")
|
||||||
|
# The final `+ 0` is to normalize numbers with leading zeros.
|
||||||
|
result_obj["number"] = substr(line, 1, RLENGTH) + 0
|
||||||
|
line = substr(line, RLENGTH + 1)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result_obj["number"] = testno
|
||||||
|
}
|
||||||
|
|
||||||
|
if (plan_seen == LATE_PLAN)
|
||||||
|
# No further test results are acceptable after a "late" TAP plan
|
||||||
|
# has been seen.
|
||||||
|
result_obj["is_unplanned"] = 1
|
||||||
|
else if (plan_seen && testno > planned_tests)
|
||||||
|
result_obj["is_unplanned"] = 1
|
||||||
|
else
|
||||||
|
result_obj["is_unplanned"] = 0
|
||||||
|
|
||||||
|
# Strip trailing and leading whitespace.
|
||||||
|
sub("^[ \t]*", "", line)
|
||||||
|
sub("[ \t]*$", "", line)
|
||||||
|
|
||||||
|
# This will have to be corrected if we have a "TODO"/"SKIP" directive.
|
||||||
|
result_obj["description"] = line
|
||||||
|
result_obj["directive"] = ""
|
||||||
|
result_obj["explanation"] = ""
|
||||||
|
|
||||||
|
if (index(line, "#") == 0)
|
||||||
|
return # No possible directive, nothing more to do.
|
||||||
|
|
||||||
|
# Directives are case-insensitive.
|
||||||
|
rx = "[ \t]*#[ \t]*([tT][oO][dD][oO]|[sS][kK][iI][pP])[ \t]*"
|
||||||
|
|
||||||
|
# See whether we have the directive, and if yes, where.
|
||||||
|
pos = match(line, rx "$")
|
||||||
|
if (!pos)
|
||||||
|
pos = match(line, rx "[^a-zA-Z0-9_]")
|
||||||
|
|
||||||
|
# If there was no TAP directive, we have nothing more to do.
|
||||||
|
if (!pos)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Let`s now see if the TAP directive has been escaped. For example:
|
||||||
|
# escaped: ok \# SKIP
|
||||||
|
# not escaped: ok \\# SKIP
|
||||||
|
# escaped: ok \\\\\# SKIP
|
||||||
|
# not escaped: ok \ # SKIP
|
||||||
|
if (substr(line, pos, 1) == "#")
|
||||||
|
{
|
||||||
|
bslash_count = 0
|
||||||
|
for (i = pos; i > 1 && substr(line, i - 1, 1) == "\\"; i--)
|
||||||
|
bslash_count += 1
|
||||||
|
if (bslash_count % 2)
|
||||||
|
return # Directive was escaped.
|
||||||
|
}
|
||||||
|
|
||||||
|
# Strip the directive and its explanation (if any) from the test
|
||||||
|
# description.
|
||||||
|
result_obj["description"] = substr(line, 1, pos - 1)
|
||||||
|
# Now remove the test description from the line, that has been dealt
|
||||||
|
# with already.
|
||||||
|
line = substr(line, pos)
|
||||||
|
# Strip the directive, and save its value (normalized to upper case).
|
||||||
|
sub("^[ \t]*#[ \t]*", "", line)
|
||||||
|
result_obj["directive"] = toupper(substr(line, 1, 4))
|
||||||
|
line = substr(line, 5)
|
||||||
|
# Now get the explanation for the directive (if any), with leading
|
||||||
|
# and trailing whitespace removed.
|
||||||
|
sub("^[ \t]*", "", line)
|
||||||
|
sub("[ \t]*$", "", line)
|
||||||
|
result_obj["explanation"] = line
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_test_exit_message(status)
|
||||||
|
{
|
||||||
|
if (status == 0)
|
||||||
|
return ""
|
||||||
|
if (status !~ /^[1-9][0-9]*$/)
|
||||||
|
abort("getting exit status")
|
||||||
|
if (status < 127)
|
||||||
|
exit_details = ""
|
||||||
|
else if (status == 127)
|
||||||
|
exit_details = " (command not found?)"
|
||||||
|
else if (status >= 128 && status <= 255)
|
||||||
|
exit_details = sprintf(" (terminated by signal %d?)", status - 128)
|
||||||
|
else if (status > 256 && status <= 384)
|
||||||
|
# We used to report an "abnormal termination" here, but some Korn
|
||||||
|
# shells, when a child process die due to signal number n, can leave
|
||||||
|
# in $? an exit status of 256+n instead of the more standard 128+n.
|
||||||
|
# Apparently, both behaviours are allowed by POSIX (2008), so be
|
||||||
|
# prepared to handle them both. See also Austing Group report ID
|
||||||
|
# 0000051 <http://www.austingroupbugs.net/view.php?id=51>
|
||||||
|
exit_details = sprintf(" (terminated by signal %d?)", status - 256)
|
||||||
|
else
|
||||||
|
# Never seen in practice.
|
||||||
|
exit_details = " (abnormal termination)"
|
||||||
|
return sprintf("exited with status %d%s", status, exit_details)
|
||||||
|
}
|
||||||
|
|
||||||
|
function write_test_results()
|
||||||
|
{
|
||||||
|
print ":global-test-result: " get_global_test_result() > trs_file
|
||||||
|
print ":recheck: " yn(must_recheck()) > trs_file
|
||||||
|
print ":copy-in-global-log: " yn(copy_in_global_log()) > trs_file
|
||||||
|
for (i = 0; i < test_results_index; i += 1)
|
||||||
|
print ":test-result: " test_results_list[i] > trs_file
|
||||||
|
close(trs_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
BEGIN {
|
||||||
|
|
||||||
|
## ------- ##
|
||||||
|
## SETUP ##
|
||||||
|
## ------- ##
|
||||||
|
|
||||||
|
'"$init_colors"'
|
||||||
|
|
||||||
|
# Properly initialized once the TAP plan is seen.
|
||||||
|
planned_tests = 0
|
||||||
|
|
||||||
|
COOKED_PASS = expect_failure ? "XPASS": "PASS";
|
||||||
|
COOKED_FAIL = expect_failure ? "XFAIL": "FAIL";
|
||||||
|
|
||||||
|
# Enumeration-like constants to remember which kind of plan (if any)
|
||||||
|
# has been seen. It is important that NO_PLAN evaluates "false" as
|
||||||
|
# a boolean.
|
||||||
|
NO_PLAN = 0
|
||||||
|
EARLY_PLAN = 1
|
||||||
|
LATE_PLAN = 2
|
||||||
|
|
||||||
|
testno = 0 # Number of test results seen so far.
|
||||||
|
bailed_out = 0 # Whether a "Bail out!" directive has been seen.
|
||||||
|
|
||||||
|
# Whether the TAP plan has been seen or not, and if yes, which kind
|
||||||
|
# it is ("early" is seen before any test result, "late" otherwise).
|
||||||
|
plan_seen = NO_PLAN
|
||||||
|
|
||||||
|
## --------- ##
|
||||||
|
## PARSING ##
|
||||||
|
## --------- ##
|
||||||
|
|
||||||
|
is_first_read = 1
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
# Involutions required so that we are able to read the exit status
|
||||||
|
# from the last input line.
|
||||||
|
st = getline
|
||||||
|
if (st < 0) # I/O error.
|
||||||
|
fatal("I/O error while reading from input stream")
|
||||||
|
else if (st == 0) # End-of-input
|
||||||
|
{
|
||||||
|
if (is_first_read)
|
||||||
|
abort("in input loop: only one input line")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (is_first_read)
|
||||||
|
{
|
||||||
|
is_first_read = 0
|
||||||
|
nextline = $0
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
curline = nextline
|
||||||
|
nextline = $0
|
||||||
|
$0 = curline
|
||||||
|
}
|
||||||
|
# Copy any input line verbatim into the log file.
|
||||||
|
print | "cat >&3"
|
||||||
|
# Parsing of TAP input should stop after a "Bail out!" directive.
|
||||||
|
if (bailed_out)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# TAP test result.
|
||||||
|
if ($0 ~ /^(not )?ok$/ || $0 ~ /^(not )?ok[^a-zA-Z0-9_]/)
|
||||||
|
{
|
||||||
|
testno += 1
|
||||||
|
setup_result_obj($0)
|
||||||
|
handle_tap_result()
|
||||||
|
}
|
||||||
|
# TAP plan (normal or "SKIP" without explanation).
|
||||||
|
else if ($0 ~ /^1\.\.[0-9]+[ \t]*$/)
|
||||||
|
{
|
||||||
|
# The next two lines will put the number of planned tests in $0.
|
||||||
|
sub("^1\\.\\.", "")
|
||||||
|
sub("[^0-9]*$", "")
|
||||||
|
handle_tap_plan($0, "")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
# TAP "SKIP" plan, with an explanation.
|
||||||
|
else if ($0 ~ /^1\.\.0+[ \t]*#/)
|
||||||
|
{
|
||||||
|
# The next lines will put the skip explanation in $0, stripping
|
||||||
|
# any leading and trailing whitespace. This is a little more
|
||||||
|
# tricky in truth, since we want to also strip a potential leading
|
||||||
|
# "SKIP" string from the message.
|
||||||
|
sub("^[^#]*#[ \t]*(SKIP[: \t][ \t]*)?", "")
|
||||||
|
sub("[ \t]*$", "");
|
||||||
|
handle_tap_plan(0, $0)
|
||||||
|
}
|
||||||
|
# "Bail out!" magic.
|
||||||
|
# Older versions of prove and TAP::Harness (e.g., 3.17) did not
|
||||||
|
# recognize a "Bail out!" directive when preceded by leading
|
||||||
|
# whitespace, but more modern versions (e.g., 3.23) do. So we
|
||||||
|
# emulate the latter, "more modern" behaviour.
|
||||||
|
else if ($0 ~ /^[ \t]*Bail out!/)
|
||||||
|
{
|
||||||
|
bailed_out = 1
|
||||||
|
# Get the bailout message (if any), with leading and trailing
|
||||||
|
# whitespace stripped. The message remains stored in `$0`.
|
||||||
|
sub("^[ \t]*Bail out![ \t]*", "");
|
||||||
|
sub("[ \t]*$", "");
|
||||||
|
# Format the error message for the
|
||||||
|
bailout_message = "Bail out!"
|
||||||
|
if (length($0))
|
||||||
|
bailout_message = bailout_message " " $0
|
||||||
|
testsuite_error(bailout_message)
|
||||||
|
}
|
||||||
|
# Maybe we have too look for dianogtic comments too.
|
||||||
|
else if (comments != 0)
|
||||||
|
{
|
||||||
|
comment = extract_tap_comment($0);
|
||||||
|
if (length(comment))
|
||||||
|
report("#", comment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
## -------- ##
|
||||||
|
## FINISH ##
|
||||||
|
## -------- ##
|
||||||
|
|
||||||
|
# A "Bail out!" directive should cause us to ignore any following TAP
|
||||||
|
# error, as well as a non-zero exit status from the TAP producer.
|
||||||
|
if (!bailed_out)
|
||||||
|
{
|
||||||
|
if (!plan_seen)
|
||||||
|
{
|
||||||
|
testsuite_error("missing test plan")
|
||||||
|
}
|
||||||
|
else if (planned_tests != testno)
|
||||||
|
{
|
||||||
|
bad_amount = testno > planned_tests ? "many" : "few"
|
||||||
|
testsuite_error(sprintf("too %s tests run (expected %d, got %d)",
|
||||||
|
bad_amount, planned_tests, testno))
|
||||||
|
}
|
||||||
|
if (!ignore_exit)
|
||||||
|
{
|
||||||
|
# Fetch exit status from the last line.
|
||||||
|
exit_message = get_test_exit_message(nextline)
|
||||||
|
if (exit_message)
|
||||||
|
testsuite_error(exit_message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
write_test_results()
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
|
||||||
|
} # End of "BEGIN" block.
|
||||||
|
'
|
||||||
|
|
||||||
|
# TODO: document that we consume the file descriptor 3 :-(
|
||||||
|
} 3>"$log_file"
|
||||||
|
|
||||||
|
test $? -eq 0 || fatal "I/O or internal error"
|
||||||
|
|
||||||
|
# Local Variables:
|
||||||
|
# mode: shell-script
|
||||||
|
# sh-indentation: 2
|
||||||
|
# eval: (add-hook 'before-save-hook 'time-stamp)
|
||||||
|
# time-stamp-start: "scriptversion="
|
||||||
|
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||||
|
# time-stamp-time-zone: "UTC0"
|
||||||
|
# time-stamp-end: "; # UTC"
|
||||||
|
# End:
|
|
@ -0,0 +1,148 @@
|
||||||
|
#! /bin/sh
|
||||||
|
# test-driver - basic testsuite driver script.
|
||||||
|
|
||||||
|
scriptversion=2018-03-07.03; # UTC
|
||||||
|
|
||||||
|
# Copyright (C) 2011-2018 Free Software Foundation, Inc.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
# any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# As a special exception to the GNU General Public License, if you
|
||||||
|
# distribute this file as part of a program that contains a
|
||||||
|
# configuration script generated by Autoconf, you may include it under
|
||||||
|
# the same distribution terms that you use for the rest of that program.
|
||||||
|
|
||||||
|
# This file is maintained in Automake, please report
|
||||||
|
# bugs to <bug-automake@gnu.org> or send patches to
|
||||||
|
# <automake-patches@gnu.org>.
|
||||||
|
|
||||||
|
# Make unconditional expansion of undefined variables an error. This
|
||||||
|
# helps a lot in preventing typo-related bugs.
|
||||||
|
set -u
|
||||||
|
|
||||||
|
usage_error ()
|
||||||
|
{
|
||||||
|
echo "$0: $*" >&2
|
||||||
|
print_usage >&2
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
print_usage ()
|
||||||
|
{
|
||||||
|
cat <<END
|
||||||
|
Usage:
|
||||||
|
test-driver --test-name=NAME --log-file=PATH --trs-file=PATH
|
||||||
|
[--expect-failure={yes|no}] [--color-tests={yes|no}]
|
||||||
|
[--enable-hard-errors={yes|no}] [--]
|
||||||
|
TEST-SCRIPT [TEST-SCRIPT-ARGUMENTS]
|
||||||
|
The '--test-name', '--log-file' and '--trs-file' options are mandatory.
|
||||||
|
END
|
||||||
|
}
|
||||||
|
|
||||||
|
test_name= # Used for reporting.
|
||||||
|
log_file= # Where to save the output of the test script.
|
||||||
|
trs_file= # Where to save the metadata of the test run.
|
||||||
|
expect_failure=no
|
||||||
|
color_tests=no
|
||||||
|
enable_hard_errors=yes
|
||||||
|
while test $# -gt 0; do
|
||||||
|
case $1 in
|
||||||
|
--help) print_usage; exit $?;;
|
||||||
|
--version) echo "test-driver $scriptversion"; exit $?;;
|
||||||
|
--test-name) test_name=$2; shift;;
|
||||||
|
--log-file) log_file=$2; shift;;
|
||||||
|
--trs-file) trs_file=$2; shift;;
|
||||||
|
--color-tests) color_tests=$2; shift;;
|
||||||
|
--expect-failure) expect_failure=$2; shift;;
|
||||||
|
--enable-hard-errors) enable_hard_errors=$2; shift;;
|
||||||
|
--) shift; break;;
|
||||||
|
-*) usage_error "invalid option: '$1'";;
|
||||||
|
*) break;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
missing_opts=
|
||||||
|
test x"$test_name" = x && missing_opts="$missing_opts --test-name"
|
||||||
|
test x"$log_file" = x && missing_opts="$missing_opts --log-file"
|
||||||
|
test x"$trs_file" = x && missing_opts="$missing_opts --trs-file"
|
||||||
|
if test x"$missing_opts" != x; then
|
||||||
|
usage_error "the following mandatory options are missing:$missing_opts"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test $# -eq 0; then
|
||||||
|
usage_error "missing argument"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test $color_tests = yes; then
|
||||||
|
# Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'.
|
||||||
|
red='[0;31m' # Red.
|
||||||
|
grn='[0;32m' # Green.
|
||||||
|
lgn='[1;32m' # Light green.
|
||||||
|
blu='[1;34m' # Blue.
|
||||||
|
mgn='[0;35m' # Magenta.
|
||||||
|
std='[m' # No color.
|
||||||
|
else
|
||||||
|
red= grn= lgn= blu= mgn= std=
|
||||||
|
fi
|
||||||
|
|
||||||
|
do_exit='rm -f $log_file $trs_file; (exit $st); exit $st'
|
||||||
|
trap "st=129; $do_exit" 1
|
||||||
|
trap "st=130; $do_exit" 2
|
||||||
|
trap "st=141; $do_exit" 13
|
||||||
|
trap "st=143; $do_exit" 15
|
||||||
|
|
||||||
|
# Test script is run here.
|
||||||
|
"$@" >$log_file 2>&1
|
||||||
|
estatus=$?
|
||||||
|
|
||||||
|
if test $enable_hard_errors = no && test $estatus -eq 99; then
|
||||||
|
tweaked_estatus=1
|
||||||
|
else
|
||||||
|
tweaked_estatus=$estatus
|
||||||
|
fi
|
||||||
|
|
||||||
|
case $tweaked_estatus:$expect_failure in
|
||||||
|
0:yes) col=$red res=XPASS recheck=yes gcopy=yes;;
|
||||||
|
0:*) col=$grn res=PASS recheck=no gcopy=no;;
|
||||||
|
77:*) col=$blu res=SKIP recheck=no gcopy=yes;;
|
||||||
|
99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;;
|
||||||
|
*:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;;
|
||||||
|
*:*) col=$red res=FAIL recheck=yes gcopy=yes;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Report the test outcome and exit status in the logs, so that one can
|
||||||
|
# know whether the test passed or failed simply by looking at the '.log'
|
||||||
|
# file, without the need of also peaking into the corresponding '.trs'
|
||||||
|
# file (automake bug#11814).
|
||||||
|
echo "$res $test_name (exit status: $estatus)" >>$log_file
|
||||||
|
|
||||||
|
# Report outcome to console.
|
||||||
|
echo "${col}${res}${std}: $test_name"
|
||||||
|
|
||||||
|
# Register the test result, and other relevant metadata.
|
||||||
|
echo ":test-result: $res" > $trs_file
|
||||||
|
echo ":global-test-result: $res" >> $trs_file
|
||||||
|
echo ":recheck: $recheck" >> $trs_file
|
||||||
|
echo ":copy-in-global-log: $gcopy" >> $trs_file
|
||||||
|
|
||||||
|
# Local Variables:
|
||||||
|
# mode: shell-script
|
||||||
|
# sh-indentation: 2
|
||||||
|
# eval: (add-hook 'before-save-hook 'time-stamp)
|
||||||
|
# time-stamp-start: "scriptversion="
|
||||||
|
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||||
|
# time-stamp-time-zone: "UTC0"
|
||||||
|
# time-stamp-end: "; # UTC"
|
||||||
|
# End:
|
|
@ -0,0 +1,380 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
|
||||||
|
"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
|
||||||
|
]>
|
||||||
|
<refentry id="bwrap">
|
||||||
|
|
||||||
|
<refentryinfo>
|
||||||
|
<title>bwrap</title>
|
||||||
|
<productname>Project Atomic</productname>
|
||||||
|
<authorgroup>
|
||||||
|
<author>
|
||||||
|
<contrib>Developer</contrib>
|
||||||
|
<firstname>Alexander</firstname>
|
||||||
|
<surname>Larsson</surname>
|
||||||
|
</author>
|
||||||
|
<author>
|
||||||
|
<contrib>Developer</contrib>
|
||||||
|
<firstname>Colin</firstname>
|
||||||
|
<surname>Walters</surname>
|
||||||
|
</author>
|
||||||
|
</authorgroup>
|
||||||
|
</refentryinfo>
|
||||||
|
|
||||||
|
<refmeta>
|
||||||
|
<refentrytitle>bwrap</refentrytitle>
|
||||||
|
<manvolnum>1</manvolnum>
|
||||||
|
<refmiscinfo class="manual">User Commands</refmiscinfo>
|
||||||
|
</refmeta>
|
||||||
|
|
||||||
|
<refnamediv>
|
||||||
|
<refname>bwrap</refname>
|
||||||
|
<refpurpose>container setup utility</refpurpose>
|
||||||
|
</refnamediv>
|
||||||
|
|
||||||
|
<refsynopsisdiv>
|
||||||
|
<cmdsynopsis>
|
||||||
|
<command>bwrap</command>
|
||||||
|
<arg choice="opt" rep="repeat"><replaceable>OPTION</replaceable></arg>
|
||||||
|
<arg choice="opt"><replaceable>COMMAND</replaceable></arg>
|
||||||
|
</cmdsynopsis>
|
||||||
|
</refsynopsisdiv>
|
||||||
|
|
||||||
|
<refsect1><title>Description</title>
|
||||||
|
<para>
|
||||||
|
<command>bwrap</command> is a privileged helper for container setup. You
|
||||||
|
are unlikely to use it directly from the commandline, although that is possible.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
It works by creating a new, completely empty, filesystem namespace where the root
|
||||||
|
is on a tmpfs that is invisible from the host, and which will be automatically
|
||||||
|
cleaned up when the last process exits. You can then use commandline options to
|
||||||
|
construct the root filesystem and process environment for the command to run in
|
||||||
|
the namespace.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
By default, <command>bwrap</command> creates a new mount namespace for the sandbox.
|
||||||
|
Optionally it also sets up new user, ipc, pid, network and uts namespaces (but note the
|
||||||
|
user namespace is required if bwrap is not installed setuid root).
|
||||||
|
The application in the sandbox can be made to run with a different UID and GID.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
If needed (e.g. when using a PID namespace) <command>bwrap</command>
|
||||||
|
is running a minimal pid 1 process in the sandbox that is
|
||||||
|
responsible for reaping zombies. It also detects when the initial
|
||||||
|
application process (pid 2) dies and reports its exit status back to
|
||||||
|
the original spawner. The pid 1 process exits to clean up the
|
||||||
|
sandbox when there are no other processes in the sandbox left.
|
||||||
|
</para>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
<refsect1><title>Options</title>
|
||||||
|
<para>
|
||||||
|
When options are used multiple times, the last option wins, unless otherwise
|
||||||
|
specified.
|
||||||
|
</para>
|
||||||
|
<para>General options:</para>
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--help</option></term>
|
||||||
|
<listitem><para>Print help and exit</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--version</option></term>
|
||||||
|
<listitem><para>Print version</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--args <arg choice="plain">FD</arg></option></term>
|
||||||
|
<listitem><para>
|
||||||
|
Parse nul-separated arguments from the given file descriptor.
|
||||||
|
This option can be used multiple times to parse options from
|
||||||
|
multiple sources.
|
||||||
|
</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
<para>Options related to kernel namespaces:</para>
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--unshare-user</option></term>
|
||||||
|
<listitem><para>Create a new user namespace</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--unshare-user-try</option></term>
|
||||||
|
<listitem><para>Create a new user namespace if possible else skip it</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--unshare-ipc</option></term>
|
||||||
|
<listitem><para>Create a new ipc namespace</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--unshare-pid</option></term>
|
||||||
|
<listitem><para>Create a new pid namespace</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--unshare-net</option></term>
|
||||||
|
<listitem><para>Create a new network namespace</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--unshare-uts</option></term>
|
||||||
|
<listitem><para>Create a new uts namespace</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--unshare-cgroup</option></term>
|
||||||
|
<listitem><para>Create a new cgroup namespace</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--unshare-cgroup-try</option></term>
|
||||||
|
<listitem><para>Create a new cgroup namespace if possible else skip it</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--unshare-all</option></term>
|
||||||
|
<listitem><para>Unshare all possible namespaces. Currently equivalent with: <option>--unshare-user-try</option> <option>--unshare-ipc</option> <option>--unshare-pid</option> <option>--unshare-net</option> <option>--unshare-uts</option> <option>--unshare-cgroup-try</option></para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--userns <arg choice="plain">FD</arg></option></term>
|
||||||
|
<listitem><para>Use an existing user namespace instead of creating a new one. The namespace must fulfil the permission requirements for setns(), which generally means that it must be a decendant of the currently active user namespace, owned by the same user. </para>
|
||||||
|
<para>This is incompatible with --unshare-user, and doesn't work in the setuid version of bubblewrap.</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--userns2 <arg choice="plain">FD</arg></option></term>
|
||||||
|
<listitem><para>After setting up the new namespace, switch into the specified namespace. For this to work the specified namespace must be a decendant of the user namespace used for the setup, so this is only useful in combination with --userns.</para>
|
||||||
|
<para>This is useful because sometimes bubblewrap itself creates nested user namespaces (to work around some kernel issues) and --userns2 can be used to enter these.</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--pidns <arg choice="plain">FD</arg></option></term>
|
||||||
|
<listitem><para>Use an existing pid namespace instead of creating one. This is often used with --userns, because the pid namespace must be owned by the same user namespace that bwrap uses. </para>
|
||||||
|
<para>Note that this can be combined with --unshare-pid, and in that case it means that the sandbox will be in its own pid namespace, which is a child of the passed in one.</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--uid <arg choice="plain">UID</arg></option></term>
|
||||||
|
<listitem><para>Use a custom user id in the sandbox (requires <option>--unshare-user</option>)</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--gid <arg choice="plain">GID</arg></option></term>
|
||||||
|
<listitem><para>Use a custom group id in the sandbox (requires <option>--unshare-user</option>)</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--hostname <arg choice="plain">HOSTNAME</arg></option></term>
|
||||||
|
<listitem><para>Use a custom hostname in the sandbox (requires <option>--unshare-uts</option>)</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
<para>Options about environment setup:</para>
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--chdir <arg choice="plain">DIR</arg></option></term>
|
||||||
|
<listitem><para>Change directory to <arg choice="plain">DIR</arg></para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--setenv <arg choice="plain">VAR</arg> <arg choice="plain">VALUE</arg></option></term>
|
||||||
|
<listitem><para>Set an environment variable</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--unsetenv <arg choice="plain">VAR</arg></option></term>
|
||||||
|
<listitem><para>Unset an environment variable</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
<para>Options for monitoring the sandbox from the outside:</para>
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--lock-file <arg choice="plain">DEST</arg></option></term>
|
||||||
|
<listitem><para>
|
||||||
|
Take a lock on <arg choice="plain">DEST</arg> while the sandbox is running.
|
||||||
|
This option can be used multiple times to take locks on multiple files.
|
||||||
|
</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--sync-fd <arg choice="plain">FD</arg></option></term>
|
||||||
|
<listitem><para>Keep this file descriptor open while the sandbox is running</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
<para>
|
||||||
|
Filesystem related options. These are all operations that modify the filesystem directly, or
|
||||||
|
mounts stuff in the filesystem. These are applied in the order they are given as arguments.
|
||||||
|
Any missing parent directories that are required to create a specified destination are
|
||||||
|
automatically created as needed.
|
||||||
|
</para>
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--bind <arg choice="plain">SRC</arg> <arg choice="plain">DEST</arg></option></term>
|
||||||
|
<listitem><para>Bind mount the host path <arg choice="plain">SRC</arg> on <arg choice="plain">DEST</arg></para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--bind-try <arg choice="plain">SRC</arg> <arg choice="plain">DEST</arg></option></term>
|
||||||
|
<listitem><para>Equal to <option>--bind</option> but ignores non-existent <arg choice="plain">SRC</arg></para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--dev-bind <arg choice="plain">SRC</arg> <arg choice="plain">DEST</arg></option></term>
|
||||||
|
<listitem><para>Bind mount the host path <arg choice="plain">SRC</arg> on <arg choice="plain">DEST</arg>, allowing device access</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--dev-bind-try <arg choice="plain">SRC</arg> <arg choice="plain">DEST</arg></option></term>
|
||||||
|
<listitem><para>Equal to <option>--dev-bind</option> but ignores non-existent <arg choice="plain">SRC</arg></para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--ro-bind <arg choice="plain">SRC</arg> <arg choice="plain">DEST</arg></option></term>
|
||||||
|
<listitem><para>Bind mount the host path <arg choice="plain">SRC</arg> readonly on <arg choice="plain">DEST</arg></para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--ro-bind-try <arg choice="plain">SRC</arg> <arg choice="plain">DEST</arg></option></term>
|
||||||
|
<listitem><para>Equal to <option>--ro-bind</option> but ignores non-existent <arg choice="plain">SRC</arg></para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--remount-ro <arg choice="plain">DEST</arg></option></term>
|
||||||
|
<listitem><para>Remount the path <arg choice="plain">DEST</arg> as readonly. It works only on the specified mount point, without changing any other mount point under the specified path</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--proc <arg choice="plain">DEST</arg></option></term>
|
||||||
|
<listitem><para>Mount procfs on <arg choice="plain">DEST</arg></para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--dev <arg choice="plain">DEST</arg></option></term>
|
||||||
|
<listitem><para>Mount new devtmpfs on <arg choice="plain">DEST</arg></para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--tmpfs <arg choice="plain">DEST</arg></option></term>
|
||||||
|
<listitem><para>Mount new tmpfs on <arg choice="plain">DEST</arg></para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--mqueue <arg choice="plain">DEST</arg></option></term>
|
||||||
|
<listitem><para>Mount new mqueue on <arg choice="plain">DEST</arg></para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--dir <arg choice="plain">DEST</arg></option></term>
|
||||||
|
<listitem><para>Create a directory at <arg choice="plain">DEST</arg></para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--file <arg choice="plain">FD</arg> <arg choice="plain">DEST</arg></option></term>
|
||||||
|
<listitem><para>Copy from the file descriptor <arg choice="plain">FD</arg> to <arg choice="plain">DEST</arg></para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--bind-data <arg choice="plain">FD</arg> <arg choice="plain">DEST</arg></option></term>
|
||||||
|
<listitem><para>Copy from the file descriptor <arg choice="plain">FD</arg> to a file which is bind-mounted on <arg choice="plain">DEST</arg></para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--ro-bind-data <arg choice="plain">FD</arg> <arg choice="plain">DEST</arg></option></term>
|
||||||
|
<listitem><para>Copy from the file descriptor <arg choice="plain">FD</arg> to a file which is bind-mounted readonly on <arg choice="plain">DEST</arg></para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--symlink <arg choice="plain">SRC</arg> <arg choice="plain">DEST</arg></option></term>
|
||||||
|
<listitem><para>Create a symlink at <arg choice="plain">DEST</arg> with target <arg choice="plain">SRC</arg></para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
<para>Lockdown options:</para>
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--seccomp <arg choice="plain">FD</arg></option></term>
|
||||||
|
<listitem><para>
|
||||||
|
Load and use seccomp rules from <arg choice="plain">FD</arg>.
|
||||||
|
The rules need to be in the form of a compiled eBPF program,
|
||||||
|
as generated by seccomp_export_bpf.
|
||||||
|
</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--exec-label <arg choice="plain">LABEL</arg></option></term>
|
||||||
|
<listitem><para>
|
||||||
|
Exec Label from the sandbox. On an SELinux system you can specify the SELinux
|
||||||
|
context for the sandbox process(s).
|
||||||
|
</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--file-label <arg choice="plain">LABEL</arg></option></term>
|
||||||
|
<listitem><para>
|
||||||
|
File label for temporary sandbox content. On an SELinux system you can specify
|
||||||
|
the SELinux context for the sandbox content.
|
||||||
|
</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--block-fd <arg choice="plain">FD</arg></option></term>
|
||||||
|
<listitem><para>
|
||||||
|
Block the sandbox on reading from FD until some data is available.
|
||||||
|
</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--userns-block-fd <arg choice="plain">FD</arg></option></term>
|
||||||
|
<listitem><para>
|
||||||
|
Do not initialize the user namespace but wait on FD until it is ready. This allow
|
||||||
|
external processes (like newuidmap/newgidmap) to setup the user namespace before it
|
||||||
|
is used by the sandbox process.
|
||||||
|
</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--info-fd <arg choice="plain">FD</arg></option></term>
|
||||||
|
<listitem><para>
|
||||||
|
Write information in JSON format about the sandbox to FD.
|
||||||
|
</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--new-session</option></term>
|
||||||
|
<listitem><para>
|
||||||
|
Create a new terminal session for the sandbox (calls setsid()). This
|
||||||
|
disconnects the sandbox from the controlling terminal which means
|
||||||
|
the sandbox can't for instance inject input into the terminal.
|
||||||
|
</para><para>
|
||||||
|
Note: In a general sandbox, if you don't use --new-session, it is
|
||||||
|
recommended to use seccomp to disallow the TIOCSTI ioctl, otherwise
|
||||||
|
the application can feed keyboard input to the terminal.
|
||||||
|
</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--die-with-parent</option></term>
|
||||||
|
<listitem><para>
|
||||||
|
Ensures child process (COMMAND) dies when bwrap's parent dies. Kills (SIGKILL)
|
||||||
|
all bwrap sandbox processes in sequence from parent to child
|
||||||
|
including COMMAND process when bwrap or bwrap's parent dies.
|
||||||
|
See prctl, PR_SET_PDEATHSIG.
|
||||||
|
</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--as-pid-1</option></term>
|
||||||
|
<listitem><para>
|
||||||
|
Do not create a process with PID=1 in the sandbox to reap child processes.
|
||||||
|
</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--cap-add <arg choice="plain">CAP</arg></option></term>
|
||||||
|
<listitem><para>
|
||||||
|
Add the specified capability when running as privileged user. It accepts
|
||||||
|
the special value ALL to add all the permitted caps.
|
||||||
|
</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--cap-drop <arg choice="plain">CAP</arg></option></term>
|
||||||
|
<listitem><para>
|
||||||
|
Drop the specified capability when running as privileged user. It accepts
|
||||||
|
the special value ALL to drop all the caps.
|
||||||
|
|
||||||
|
By default no caps are left in the sandboxed process. The
|
||||||
|
<option>--cap-add</option> and <option>--cap-drop</option>
|
||||||
|
options are processed in the order they are specified on the
|
||||||
|
command line. Please be careful to the order they are specified.
|
||||||
|
</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
<refsect1>
|
||||||
|
<title>Environment</title>
|
||||||
|
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term><envar>HOME</envar></term>
|
||||||
|
<listitem><para>
|
||||||
|
Used as the cwd in the sandbox if <option>--chdir</option> has not been
|
||||||
|
explicitly specified and the current cwd is not present inside the sandbox.
|
||||||
|
The <option>--setenv</option> option can be used to override the value
|
||||||
|
that is used here.
|
||||||
|
</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
<refsect1>
|
||||||
|
<title>Exit status</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The <command>bwrap</command> command returns the exit status of the
|
||||||
|
initial application process (pid 2 in the sandbox).
|
||||||
|
</para>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
</refentry>
|
|
@ -0,0 +1,64 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# bash completion file for bubblewrap commands
|
||||||
|
#
|
||||||
|
|
||||||
|
_bwrap() {
|
||||||
|
local cur prev words cword
|
||||||
|
_init_completion || return
|
||||||
|
|
||||||
|
local boolean_options="
|
||||||
|
--as-pid-1
|
||||||
|
--help
|
||||||
|
--new-session
|
||||||
|
--unshare-cgroup
|
||||||
|
--unshare-cgroup-try
|
||||||
|
--unshare-user
|
||||||
|
--unshare-user-try
|
||||||
|
--unshare-all
|
||||||
|
--unshare-ipc
|
||||||
|
--unshare-net
|
||||||
|
--unshare-pid
|
||||||
|
--unshare-uts
|
||||||
|
--version
|
||||||
|
"
|
||||||
|
|
||||||
|
local options_with_args="
|
||||||
|
$boolean_optons
|
||||||
|
--args
|
||||||
|
--bind
|
||||||
|
--bind-data
|
||||||
|
--block-fd
|
||||||
|
--cap-add
|
||||||
|
--cap-drop
|
||||||
|
--chdir
|
||||||
|
--dev
|
||||||
|
--dev-bind
|
||||||
|
--die-with-parent
|
||||||
|
--dir
|
||||||
|
--exec-label
|
||||||
|
--file
|
||||||
|
--file-label
|
||||||
|
--gid
|
||||||
|
--hostname
|
||||||
|
--info-fd
|
||||||
|
--lock-file
|
||||||
|
--proc
|
||||||
|
--remount-ro
|
||||||
|
--ro-bind
|
||||||
|
--seccomp
|
||||||
|
--setenv
|
||||||
|
--symlink
|
||||||
|
--sync-fd
|
||||||
|
--uid
|
||||||
|
--unsetenv
|
||||||
|
--userns-block-fd
|
||||||
|
"
|
||||||
|
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=( $( compgen -W "$boolean_options $options_with_args" -- "$cur" ) )
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
complete -F _bwrap bwrap
|
|
@ -0,0 +1,104 @@
|
||||||
|
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||||
|
|
||||||
|
/* Define if userns should be used by default in suid mode */
|
||||||
|
#undef ENABLE_REQUIRE_USERNS
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||||
|
#undef HAVE_INTTYPES_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `cap' library (-lcap). */
|
||||||
|
#undef HAVE_LIBCAP
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <memory.h> header file. */
|
||||||
|
#undef HAVE_MEMORY_H
|
||||||
|
|
||||||
|
/* Define if SELinux is available */
|
||||||
|
#undef HAVE_SELINUX
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stdint.h> header file. */
|
||||||
|
#undef HAVE_STDINT_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||||
|
#undef HAVE_STDLIB_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <strings.h> header file. */
|
||||||
|
#undef HAVE_STRINGS_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <string.h> header file. */
|
||||||
|
#undef HAVE_STRING_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/capability.h> header file. */
|
||||||
|
#undef HAVE_SYS_CAPABILITY_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||||
|
#undef HAVE_SYS_STAT_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||||
|
#undef HAVE_SYS_TYPES_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <unistd.h> header file. */
|
||||||
|
#undef HAVE_UNISTD_H
|
||||||
|
|
||||||
|
/* Define to the address where bug reports for this package should be sent. */
|
||||||
|
#undef PACKAGE_BUGREPORT
|
||||||
|
|
||||||
|
/* Define to the full name of this package. */
|
||||||
|
#undef PACKAGE_NAME
|
||||||
|
|
||||||
|
/* Define to the full name and version of this package. */
|
||||||
|
#undef PACKAGE_STRING
|
||||||
|
|
||||||
|
/* Define to the one symbol short name of this package. */
|
||||||
|
#undef PACKAGE_TARNAME
|
||||||
|
|
||||||
|
/* Define to the home page for this package. */
|
||||||
|
#undef PACKAGE_URL
|
||||||
|
|
||||||
|
/* Define to the version of this package. */
|
||||||
|
#undef PACKAGE_VERSION
|
||||||
|
|
||||||
|
/* Define to 1 if you have the ANSI C header files. */
|
||||||
|
#undef STDC_HEADERS
|
||||||
|
|
||||||
|
/* Enable extensions on AIX 3, Interix. */
|
||||||
|
#ifndef _ALL_SOURCE
|
||||||
|
# undef _ALL_SOURCE
|
||||||
|
#endif
|
||||||
|
/* Enable GNU extensions on systems that have them. */
|
||||||
|
#ifndef _GNU_SOURCE
|
||||||
|
# undef _GNU_SOURCE
|
||||||
|
#endif
|
||||||
|
/* Enable threading extensions on Solaris. */
|
||||||
|
#ifndef _POSIX_PTHREAD_SEMANTICS
|
||||||
|
# undef _POSIX_PTHREAD_SEMANTICS
|
||||||
|
#endif
|
||||||
|
/* Enable extensions on HP NonStop. */
|
||||||
|
#ifndef _TANDEM_SOURCE
|
||||||
|
# undef _TANDEM_SOURCE
|
||||||
|
#endif
|
||||||
|
/* Enable general extensions on Solaris. */
|
||||||
|
#ifndef __EXTENSIONS__
|
||||||
|
# undef __EXTENSIONS__
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Enable large inode numbers on Mac OS X 10.5. */
|
||||||
|
#ifndef _DARWIN_USE_64_BIT_INODE
|
||||||
|
# define _DARWIN_USE_64_BIT_INODE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Number of bits in a file offset, on hosts where this is settable. */
|
||||||
|
#undef _FILE_OFFSET_BITS
|
||||||
|
|
||||||
|
/* Define for large files, on AIX-style hosts. */
|
||||||
|
#undef _LARGE_FILES
|
||||||
|
|
||||||
|
/* Define to 1 if on MINIX. */
|
||||||
|
#undef _MINIX
|
||||||
|
|
||||||
|
/* Define to 2 if the system does not provide POSIX.1 features except with
|
||||||
|
this defined. */
|
||||||
|
#undef _POSIX_1_SOURCE
|
||||||
|
|
||||||
|
/* Define to 1 if you need to in order for `stat' and other things to work. */
|
||||||
|
#undef _POSIX_SOURCE
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,136 @@
|
||||||
|
AC_PREREQ([2.63])
|
||||||
|
AC_INIT([bubblewrap], [0.4.0], [atomic-devel@projectatomic.io])
|
||||||
|
AC_CONFIG_HEADER([config.h])
|
||||||
|
AC_CONFIG_MACRO_DIR([m4])
|
||||||
|
AC_CONFIG_AUX_DIR([build-aux])
|
||||||
|
|
||||||
|
AC_USE_SYSTEM_EXTENSIONS
|
||||||
|
|
||||||
|
AM_INIT_AUTOMAKE([1.11 -Wno-portability foreign no-define tar-ustar no-dist-gzip dist-xz])
|
||||||
|
AM_MAINTAINER_MODE([enable])
|
||||||
|
AM_SILENT_RULES([yes])
|
||||||
|
|
||||||
|
AC_SYS_LARGEFILE
|
||||||
|
|
||||||
|
AC_PROG_CC
|
||||||
|
AM_PROG_CC_C_O
|
||||||
|
|
||||||
|
AC_CHECK_HEADERS([sys/capability.h], [], [AC_MSG_ERROR([*** POSIX caps headers not found])])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE(man,
|
||||||
|
[AS_HELP_STRING([--enable-man],
|
||||||
|
[generate man pages [default=auto]])],,
|
||||||
|
enable_man=maybe)
|
||||||
|
|
||||||
|
AS_IF([test "$enable_man" != no], [
|
||||||
|
AC_PATH_PROG([XSLTPROC], [xsltproc], [])
|
||||||
|
AS_IF([test -z "$XSLTPROC"], [
|
||||||
|
AS_IF([test "$enable_man" = yes], [
|
||||||
|
AC_MSG_ERROR([xsltproc is required for --enable-man])
|
||||||
|
])
|
||||||
|
enable_man=no
|
||||||
|
], [
|
||||||
|
enable_man=yes
|
||||||
|
])
|
||||||
|
])
|
||||||
|
AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no)
|
||||||
|
|
||||||
|
AC_ARG_WITH([bash-completion-dir],
|
||||||
|
AS_HELP_STRING([--with-bash-completion-dir[=PATH]],
|
||||||
|
[Install the bash auto-completion script in this directory. @<:@default=yes@:>@]),
|
||||||
|
[],
|
||||||
|
[with_bash_completion_dir=yes])
|
||||||
|
|
||||||
|
if test "x$with_bash_completion_dir" = "xyes"; then
|
||||||
|
PKG_CHECK_MODULES([BASH_COMPLETION], [bash-completion >= 2.0],
|
||||||
|
[BASH_COMPLETION_DIR="`pkg-config --variable=completionsdir bash-completion`"],
|
||||||
|
[BASH_COMPLETION_DIR="$datadir/bash-completion/completions"])
|
||||||
|
else
|
||||||
|
BASH_COMPLETION_DIR="$with_bash_completion_dir"
|
||||||
|
fi
|
||||||
|
|
||||||
|
AC_SUBST([BASH_COMPLETION_DIR])
|
||||||
|
AM_CONDITIONAL([ENABLE_BASH_COMPLETION],[test "x$with_bash_completion_dir" != "xno"])
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
have_selinux=no
|
||||||
|
AC_ARG_ENABLE(selinux, AS_HELP_STRING([--disable-selinux], [Disable optional SELINUX support]))
|
||||||
|
if test "x$enable_selinux" != "xno"; then
|
||||||
|
PKG_CHECK_MODULES([SELINUX], [libselinux >= 2.1.9],
|
||||||
|
[AC_DEFINE(HAVE_SELINUX, 1, [Define if SELinux is available])
|
||||||
|
have_selinux=yes
|
||||||
|
M4_DEFINES="$M4_DEFINES -DHAVE_SELINUX"],
|
||||||
|
[have_selinux=no])
|
||||||
|
if test "x$have_selinux" = xno -a "x$enable_selinux" = xyes; then
|
||||||
|
AC_MSG_ERROR([*** SELinux support requested but libraries not found])
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
AM_CONDITIONAL(HAVE_SELINUX, [test "$have_selinux" = "yes"])
|
||||||
|
|
||||||
|
dnl Keep this in sync with ostree, except remove -Werror=declaration-after-statement
|
||||||
|
CC_CHECK_FLAGS_APPEND([WARN_CFLAGS], [CFLAGS], [\
|
||||||
|
-pipe \
|
||||||
|
-Wall \
|
||||||
|
-Werror=empty-body \
|
||||||
|
-Werror=strict-prototypes \
|
||||||
|
-Werror=missing-prototypes \
|
||||||
|
-Werror=implicit-function-declaration \
|
||||||
|
"-Werror=format=2 -Werror=format-security -Werror=format-nonliteral" \
|
||||||
|
-Werror=pointer-arith -Werror=init-self \
|
||||||
|
-Werror=missing-declarations \
|
||||||
|
-Werror=return-type \
|
||||||
|
-Werror=overflow \
|
||||||
|
-Werror=int-conversion \
|
||||||
|
-Werror=parenthesis \
|
||||||
|
-Werror=incompatible-pointer-types \
|
||||||
|
-Werror=misleading-indentation \
|
||||||
|
-Werror=missing-include-dirs -Werror=aggregate-return \
|
||||||
|
])
|
||||||
|
AC_SUBST(WARN_CFLAGS)
|
||||||
|
|
||||||
|
AC_CHECK_LIB(cap, cap_from_text)
|
||||||
|
|
||||||
|
if test "$ac_cv_lib_cap_cap_from_text" != "yes"; then
|
||||||
|
AC_MSG_ERROR([*** libcap requested but not found])
|
||||||
|
fi
|
||||||
|
|
||||||
|
AC_ARG_WITH(priv-mode,
|
||||||
|
AS_HELP_STRING([--with-priv-mode=setuid/none],
|
||||||
|
[How to set privilege-raising during make install]),
|
||||||
|
[],
|
||||||
|
[with_priv_mode="none"])
|
||||||
|
|
||||||
|
AM_CONDITIONAL(PRIV_MODE_SETUID, test "x$with_priv_mode" = "xsetuid")
|
||||||
|
|
||||||
|
AC_ARG_ENABLE(sudo,
|
||||||
|
AS_HELP_STRING([--enable-sudo],[Use sudo to set privileged mode on binaries during install (only needed if --with-priv-mode used)]),
|
||||||
|
[SUDO_BIN="sudo"], [SUDO_BIN=""])
|
||||||
|
AC_SUBST([SUDO_BIN])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE(require-userns,
|
||||||
|
AS_HELP_STRING([--enable-require-userns=yes/no (default no)],
|
||||||
|
[Require user namespaces by default when installed suid]),
|
||||||
|
[],
|
||||||
|
[enable_require_userns="no"])
|
||||||
|
|
||||||
|
AS_IF([ test "x$enable_require_userns" = "xyes" ], [
|
||||||
|
AC_DEFINE(ENABLE_REQUIRE_USERNS, 1, [Define if userns should be used by default in suid mode])
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_PROG_AWK
|
||||||
|
AC_REQUIRE_AUX_FILE([tap-driver.sh])
|
||||||
|
|
||||||
|
AC_CONFIG_FILES([
|
||||||
|
Makefile
|
||||||
|
])
|
||||||
|
AC_OUTPUT
|
||||||
|
|
||||||
|
echo "
|
||||||
|
bubblewrap $VERSION
|
||||||
|
===================
|
||||||
|
|
||||||
|
man pages (xsltproc): $enable_man
|
||||||
|
SELinux: $have_selinux
|
||||||
|
setuid mode on make install: $with_priv_mode
|
||||||
|
require default userns: $enable_require_userns
|
||||||
|
mysteriously satisfying to pop: yes"
|
||||||
|
echo ""
|
|
@ -0,0 +1,34 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Use bubblewrap to run /bin/sh reusing the host OS binaries (/usr), but with
|
||||||
|
# separate /tmp, /home, /var, /run, and /etc. For /etc we just inherit the
|
||||||
|
# host's resolv.conf, and set up "stub" passwd/group files. Not sharing
|
||||||
|
# /home for example is intentional. If you wanted to, you could design
|
||||||
|
# a bwrap-using program that shared individual parts of /home, perhaps
|
||||||
|
# public content.
|
||||||
|
#
|
||||||
|
# Another way to build on this example is to remove --share-net to disable
|
||||||
|
# networking.
|
||||||
|
set -euo pipefail
|
||||||
|
(exec bwrap --ro-bind /usr /usr \
|
||||||
|
--dir /tmp \
|
||||||
|
--dir /var \
|
||||||
|
--symlink ../tmp var/tmp \
|
||||||
|
--proc /proc \
|
||||||
|
--dev /dev \
|
||||||
|
--ro-bind /etc/resolv.conf /etc/resolv.conf \
|
||||||
|
--symlink usr/lib /lib \
|
||||||
|
--symlink usr/lib64 /lib64 \
|
||||||
|
--symlink usr/bin /bin \
|
||||||
|
--symlink usr/sbin /sbin \
|
||||||
|
--chdir / \
|
||||||
|
--unshare-all \
|
||||||
|
--share-net \
|
||||||
|
--die-with-parent \
|
||||||
|
--dir /run/user/$(id -u) \
|
||||||
|
--setenv XDG_RUNTIME_DIR "/run/user/`id -u`" \
|
||||||
|
--setenv PS1 "bwrap-demo$ " \
|
||||||
|
--file 11 /etc/passwd \
|
||||||
|
--file 12 /etc/group \
|
||||||
|
/bin/sh) \
|
||||||
|
11< <(getent passwd $UID 65534) \
|
||||||
|
12< <(getent group $(id -g) 65534)
|
|
@ -0,0 +1,65 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# For this to work you first have to run these commands:
|
||||||
|
# curl -O http://sdk.gnome.org/nightly/keys/nightly.gpg
|
||||||
|
# flatpak --user remote-add --gpg-key=nightly.gpg gnome-nightly http://sdk.gnome.org/nightly/repo/
|
||||||
|
# flatpak --user install gnome-nightly org.gnome.Platform
|
||||||
|
# flatpak --user install gnome-nightly org.gnome.Weather
|
||||||
|
|
||||||
|
mkdir -p ~/.var/app/org.gnome.Weather/cache ~/.var/app/org.gnome.Weather/config ~/.var/app/org.gnome.Weather/data
|
||||||
|
|
||||||
|
(
|
||||||
|
exec bwrap \
|
||||||
|
--ro-bind ~/.local/share/flatpak/runtime/org.gnome.Platform/x86_64/master/active/files /usr \
|
||||||
|
--lock-file /usr/.ref \
|
||||||
|
--ro-bind ~/.local/share/flatpak/app/org.gnome.Weather/x86_64/master/active/files/ /app \
|
||||||
|
--lock-file /app/.ref \
|
||||||
|
--dev /dev \
|
||||||
|
--proc /proc \
|
||||||
|
--dir /tmp \
|
||||||
|
--symlink /tmp /var/tmp \
|
||||||
|
--symlink /run /var/run \
|
||||||
|
--symlink usr/lib /lib \
|
||||||
|
--symlink usr/lib64 /lib64 \
|
||||||
|
--symlink usr/bin /bin \
|
||||||
|
--symlink usr/sbin /sbin \
|
||||||
|
--symlink usr/etc /etc \
|
||||||
|
--dir /run/user/`id -u` \
|
||||||
|
--ro-bind /etc/machine-id /usr/etc/machine-id \
|
||||||
|
--ro-bind /etc/resolv.conf /run/host/monitor/resolv.conf \
|
||||||
|
--ro-bind /sys/block /sys/block \
|
||||||
|
--ro-bind /sys/bus /sys/bus \
|
||||||
|
--ro-bind /sys/class /sys/class \
|
||||||
|
--ro-bind /sys/dev /sys/dev \
|
||||||
|
--ro-bind /sys/devices /sys/devices \
|
||||||
|
--dev-bind /dev/dri /dev/dri \
|
||||||
|
--bind /tmp/.X11-unix/X0 /tmp/.X11-unix/X99 \
|
||||||
|
--bind ~/.var/app/org.gnome.Weather ~/.var/app/org.gnome.Weather \
|
||||||
|
--bind ~/.config/dconf ~/.config/dconf \
|
||||||
|
--bind /run/user/`id -u`/dconf /run/user/`id -u`/dconf \
|
||||||
|
--unshare-pid \
|
||||||
|
--setenv XDG_RUNTIME_DIR "/run/user/`id -u`" \
|
||||||
|
--setenv DISPLAY :99 \
|
||||||
|
--setenv GI_TYPELIB_PATH /app/lib/girepository-1.0 \
|
||||||
|
--setenv GST_PLUGIN_PATH /app/lib/gstreamer-1.0 \
|
||||||
|
--setenv LD_LIBRARY_PATH /app/lib:/usr/lib/GL \
|
||||||
|
--setenv DCONF_USER_CONFIG_DIR .config/dconf \
|
||||||
|
--setenv PATH /app/bin:/usr/bin \
|
||||||
|
--setenv XDG_CONFIG_DIRS /app/etc/xdg:/etc/xdg \
|
||||||
|
--setenv XDG_DATA_DIRS /app/share:/usr/share \
|
||||||
|
--setenv SHELL /bin/sh \
|
||||||
|
--setenv XDG_CACHE_HOME ~/.var/app/org.gnome.Weather/cache \
|
||||||
|
--setenv XDG_CONFIG_HOME ~/.var/app/org.gnome.Weather/config \
|
||||||
|
--setenv XDG_DATA_HOME ~/.var/app/org.gnome.Weather/data \
|
||||||
|
--file 10 /run/user/`id -u`/flatpak-info \
|
||||||
|
--bind-data 11 /usr/etc/passwd \
|
||||||
|
--bind-data 12 /usr/etc/group \
|
||||||
|
--seccomp 13 \
|
||||||
|
/bin/sh) \
|
||||||
|
11< <(getent passwd $UID 65534 ) \
|
||||||
|
12< <(getent group $(id -g) 65534) \
|
||||||
|
13< `dirname $0`/flatpak.bpf \
|
||||||
|
10<<EOF
|
||||||
|
[Application]
|
||||||
|
name=org.gnome.Weather
|
||||||
|
runtime=runtime/org.gnome.Platform/x86_64/master
|
||||||
|
EOF
|
Binary file not shown.
|
@ -0,0 +1,40 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import os, select, subprocess, sys, json
|
||||||
|
|
||||||
|
pipe_info = os.pipe()
|
||||||
|
userns_block = os.pipe()
|
||||||
|
|
||||||
|
pid = os.fork()
|
||||||
|
|
||||||
|
if pid != 0:
|
||||||
|
os.close(pipe_info[1])
|
||||||
|
os.close(userns_block[0])
|
||||||
|
|
||||||
|
select.select([pipe_info[0]], [], [])
|
||||||
|
|
||||||
|
data = json.load(os.fdopen(pipe_info[0]))
|
||||||
|
child_pid = str(data['child-pid'])
|
||||||
|
|
||||||
|
subprocess.call(["newuidmap", child_pid, "0", str(os.getuid()), "1"])
|
||||||
|
subprocess.call(["newgidmap", child_pid, "0", str(os.getgid()), "1"])
|
||||||
|
|
||||||
|
os.write(userns_block[1], b'1')
|
||||||
|
else:
|
||||||
|
os.close(pipe_info[0])
|
||||||
|
os.close(userns_block[1])
|
||||||
|
|
||||||
|
if sys.version_info >= (3, 4):
|
||||||
|
os.set_inheritable(pipe_info[1], True)
|
||||||
|
os.set_inheritable(userns_block[0], True)
|
||||||
|
|
||||||
|
args = ["bwrap",
|
||||||
|
"bwrap",
|
||||||
|
"--unshare-all",
|
||||||
|
"--unshare-user",
|
||||||
|
"--userns-block-fd", "%i" % userns_block[0],
|
||||||
|
"--info-fd", "%i" % pipe_info[1],
|
||||||
|
"--bind", "/", "/",
|
||||||
|
"cat", "/proc/self/uid_map"]
|
||||||
|
|
||||||
|
os.execlp(*args)
|
|
@ -0,0 +1,292 @@
|
||||||
|
dnl Macros to check the presence of generic (non-typed) symbols.
|
||||||
|
dnl Copyright (c) 2006-2008 Diego Pettenò <flameeyes@gmail.com>
|
||||||
|
dnl Copyright (c) 2006-2008 xine project
|
||||||
|
dnl Copyright (c) 2012 Lucas De Marchi <lucas.de.marchi@gmail.com>
|
||||||
|
dnl
|
||||||
|
dnl This program is free software; you can redistribute it and/or modify
|
||||||
|
dnl it under the terms of the GNU General Public License as published by
|
||||||
|
dnl the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
dnl any later version.
|
||||||
|
dnl
|
||||||
|
dnl This program is distributed in the hope that it will be useful,
|
||||||
|
dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
dnl GNU General Public License for more details.
|
||||||
|
dnl
|
||||||
|
dnl You should have received a copy of the GNU General Public License
|
||||||
|
dnl along with this program; if not, write to the Free Software
|
||||||
|
dnl Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||||
|
dnl 02110-1301, USA.
|
||||||
|
dnl
|
||||||
|
dnl As a special exception, the copyright owners of the
|
||||||
|
dnl macro gives unlimited permission to copy, distribute and modify the
|
||||||
|
dnl configure scripts that are the output of Autoconf when processing the
|
||||||
|
dnl Macro. You need not follow the terms of the GNU General Public
|
||||||
|
dnl License when using or distributing such scripts, even though portions
|
||||||
|
dnl of the text of the Macro appear in them. The GNU General Public
|
||||||
|
dnl License (GPL) does govern all other use of the material that
|
||||||
|
dnl constitutes the Autoconf Macro.
|
||||||
|
dnl
|
||||||
|
dnl This special exception to the GPL applies to versions of the
|
||||||
|
dnl Autoconf Macro released by this project. When you make and
|
||||||
|
dnl distribute a modified version of the Autoconf Macro, you may extend
|
||||||
|
dnl this special exception to the GPL to apply to your modified version as
|
||||||
|
dnl well.
|
||||||
|
|
||||||
|
dnl Check if FLAG in ENV-VAR is supported by compiler and append it
|
||||||
|
dnl to WHERE-TO-APPEND variable. Note that we invert -Wno-* checks to
|
||||||
|
dnl -W* as gcc cannot test for negated warnings. If a C snippet is passed,
|
||||||
|
dnl use it, otherwise use a simple main() definition that just returns 0.
|
||||||
|
dnl CC_CHECK_FLAG_APPEND([WHERE-TO-APPEND], [ENV-VAR], [FLAG], [C-SNIPPET])
|
||||||
|
|
||||||
|
AC_DEFUN([CC_CHECK_FLAG_APPEND], [
|
||||||
|
AC_CACHE_CHECK([if $CC supports flag $3 in envvar $2],
|
||||||
|
AS_TR_SH([cc_cv_$2_$3]),
|
||||||
|
[eval "AS_TR_SH([cc_save_$2])='${$2}'"
|
||||||
|
eval "AS_TR_SH([$2])='${cc_save_$2} -Werror `echo "$3" | sed 's/^-Wno-/-W/'`'"
|
||||||
|
AC_LINK_IFELSE([AC_LANG_SOURCE(ifelse([$4], [],
|
||||||
|
[int main(void) { return 0; } ],
|
||||||
|
[$4]))],
|
||||||
|
[eval "AS_TR_SH([cc_cv_$2_$3])='yes'"],
|
||||||
|
[eval "AS_TR_SH([cc_cv_$2_$3])='no'"])
|
||||||
|
eval "AS_TR_SH([$2])='$cc_save_$2'"])
|
||||||
|
|
||||||
|
AS_IF([eval test x$]AS_TR_SH([cc_cv_$2_$3])[ = xyes],
|
||||||
|
[eval "$1='${$1} $3'"])
|
||||||
|
])
|
||||||
|
|
||||||
|
dnl CC_CHECK_FLAGS_APPEND([WHERE-TO-APPEND], [ENV-VAR], [FLAG1 FLAG2], [C-SNIPPET])
|
||||||
|
AC_DEFUN([CC_CHECK_FLAGS_APPEND], [
|
||||||
|
for flag in [$3]; do
|
||||||
|
CC_CHECK_FLAG_APPEND([$1], [$2], $flag, [$4])
|
||||||
|
done
|
||||||
|
])
|
||||||
|
|
||||||
|
dnl Check if the flag is supported by linker (cacheable)
|
||||||
|
dnl CC_CHECK_LDFLAGS([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND])
|
||||||
|
|
||||||
|
AC_DEFUN([CC_CHECK_LDFLAGS], [
|
||||||
|
AC_CACHE_CHECK([if $CC supports $1 flag],
|
||||||
|
AS_TR_SH([cc_cv_ldflags_$1]),
|
||||||
|
[ac_save_LDFLAGS="$LDFLAGS"
|
||||||
|
LDFLAGS="$LDFLAGS $1"
|
||||||
|
AC_LINK_IFELSE([int main() { return 1; }],
|
||||||
|
[eval "AS_TR_SH([cc_cv_ldflags_$1])='yes'"],
|
||||||
|
[eval "AS_TR_SH([cc_cv_ldflags_$1])="])
|
||||||
|
LDFLAGS="$ac_save_LDFLAGS"
|
||||||
|
])
|
||||||
|
|
||||||
|
AS_IF([eval test x$]AS_TR_SH([cc_cv_ldflags_$1])[ = xyes],
|
||||||
|
[$2], [$3])
|
||||||
|
])
|
||||||
|
|
||||||
|
dnl define the LDFLAGS_NOUNDEFINED variable with the correct value for
|
||||||
|
dnl the current linker to avoid undefined references in a shared object.
|
||||||
|
AC_DEFUN([CC_NOUNDEFINED], [
|
||||||
|
dnl We check $host for which systems to enable this for.
|
||||||
|
AC_REQUIRE([AC_CANONICAL_HOST])
|
||||||
|
|
||||||
|
case $host in
|
||||||
|
dnl FreeBSD (et al.) does not complete linking for shared objects when pthreads
|
||||||
|
dnl are requested, as different implementations are present; to avoid problems
|
||||||
|
dnl use -Wl,-z,defs only for those platform not behaving this way.
|
||||||
|
*-freebsd* | *-openbsd*) ;;
|
||||||
|
*)
|
||||||
|
dnl First of all check for the --no-undefined variant of GNU ld. This allows
|
||||||
|
dnl for a much more readable command line, so that people can understand what
|
||||||
|
dnl it does without going to look for what the heck -z defs does.
|
||||||
|
for possible_flags in "-Wl,--no-undefined" "-Wl,-z,defs"; do
|
||||||
|
CC_CHECK_LDFLAGS([$possible_flags], [LDFLAGS_NOUNDEFINED="$possible_flags"])
|
||||||
|
break
|
||||||
|
done
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
AC_SUBST([LDFLAGS_NOUNDEFINED])
|
||||||
|
])
|
||||||
|
|
||||||
|
dnl Check for a -Werror flag or equivalent. -Werror is the GCC
|
||||||
|
dnl and ICC flag that tells the compiler to treat all the warnings
|
||||||
|
dnl as fatal. We usually need this option to make sure that some
|
||||||
|
dnl constructs (like attributes) are not simply ignored.
|
||||||
|
dnl
|
||||||
|
dnl Other compilers don't support -Werror per se, but they support
|
||||||
|
dnl an equivalent flag:
|
||||||
|
dnl - Sun Studio compiler supports -errwarn=%all
|
||||||
|
AC_DEFUN([CC_CHECK_WERROR], [
|
||||||
|
AC_CACHE_CHECK(
|
||||||
|
[for $CC way to treat warnings as errors],
|
||||||
|
[cc_cv_werror],
|
||||||
|
[CC_CHECK_CFLAGS_SILENT([-Werror], [cc_cv_werror=-Werror],
|
||||||
|
[CC_CHECK_CFLAGS_SILENT([-errwarn=%all], [cc_cv_werror=-errwarn=%all])])
|
||||||
|
])
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_DEFUN([CC_CHECK_ATTRIBUTE], [
|
||||||
|
AC_REQUIRE([CC_CHECK_WERROR])
|
||||||
|
AC_CACHE_CHECK([if $CC supports __attribute__(( ifelse([$2], , [$1], [$2]) ))],
|
||||||
|
AS_TR_SH([cc_cv_attribute_$1]),
|
||||||
|
[ac_save_CFLAGS="$CFLAGS"
|
||||||
|
CFLAGS="$CFLAGS $cc_cv_werror"
|
||||||
|
AC_COMPILE_IFELSE([AC_LANG_SOURCE([$3])],
|
||||||
|
[eval "AS_TR_SH([cc_cv_attribute_$1])='yes'"],
|
||||||
|
[eval "AS_TR_SH([cc_cv_attribute_$1])='no'"])
|
||||||
|
CFLAGS="$ac_save_CFLAGS"
|
||||||
|
])
|
||||||
|
|
||||||
|
AS_IF([eval test x$]AS_TR_SH([cc_cv_attribute_$1])[ = xyes],
|
||||||
|
[AC_DEFINE(
|
||||||
|
AS_TR_CPP([SUPPORT_ATTRIBUTE_$1]), 1,
|
||||||
|
[Define this if the compiler supports __attribute__(( ifelse([$2], , [$1], [$2]) ))]
|
||||||
|
)
|
||||||
|
$4],
|
||||||
|
[$5])
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_DEFUN([CC_ATTRIBUTE_CONSTRUCTOR], [
|
||||||
|
CC_CHECK_ATTRIBUTE(
|
||||||
|
[constructor],,
|
||||||
|
[void __attribute__((constructor)) ctor() { int a; }],
|
||||||
|
[$1], [$2])
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_DEFUN([CC_ATTRIBUTE_FORMAT], [
|
||||||
|
CC_CHECK_ATTRIBUTE(
|
||||||
|
[format], [format(printf, n, n)],
|
||||||
|
[void __attribute__((format(printf, 1, 2))) printflike(const char *fmt, ...) { fmt = (void *)0; }],
|
||||||
|
[$1], [$2])
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_DEFUN([CC_ATTRIBUTE_FORMAT_ARG], [
|
||||||
|
CC_CHECK_ATTRIBUTE(
|
||||||
|
[format_arg], [format_arg(printf)],
|
||||||
|
[char *__attribute__((format_arg(1))) gettextlike(const char *fmt) { fmt = (void *)0; }],
|
||||||
|
[$1], [$2])
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_DEFUN([CC_ATTRIBUTE_VISIBILITY], [
|
||||||
|
CC_CHECK_ATTRIBUTE(
|
||||||
|
[visibility_$1], [visibility("$1")],
|
||||||
|
[void __attribute__((visibility("$1"))) $1_function() { }],
|
||||||
|
[$2], [$3])
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_DEFUN([CC_ATTRIBUTE_NONNULL], [
|
||||||
|
CC_CHECK_ATTRIBUTE(
|
||||||
|
[nonnull], [nonnull()],
|
||||||
|
[void __attribute__((nonnull())) some_function(void *foo, void *bar) { foo = (void*)0; bar = (void*)0; }],
|
||||||
|
[$1], [$2])
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_DEFUN([CC_ATTRIBUTE_UNUSED], [
|
||||||
|
CC_CHECK_ATTRIBUTE(
|
||||||
|
[unused], ,
|
||||||
|
[void some_function(void *foo, __attribute__((unused)) void *bar);],
|
||||||
|
[$1], [$2])
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_DEFUN([CC_ATTRIBUTE_SENTINEL], [
|
||||||
|
CC_CHECK_ATTRIBUTE(
|
||||||
|
[sentinel], ,
|
||||||
|
[void some_function(void *foo, ...) __attribute__((sentinel));],
|
||||||
|
[$1], [$2])
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_DEFUN([CC_ATTRIBUTE_DEPRECATED], [
|
||||||
|
CC_CHECK_ATTRIBUTE(
|
||||||
|
[deprecated], ,
|
||||||
|
[void some_function(void *foo, ...) __attribute__((deprecated));],
|
||||||
|
[$1], [$2])
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_DEFUN([CC_ATTRIBUTE_ALIAS], [
|
||||||
|
CC_CHECK_ATTRIBUTE(
|
||||||
|
[alias], [weak, alias],
|
||||||
|
[void other_function(void *foo) { }
|
||||||
|
void some_function(void *foo) __attribute__((weak, alias("other_function")));],
|
||||||
|
[$1], [$2])
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_DEFUN([CC_ATTRIBUTE_MALLOC], [
|
||||||
|
CC_CHECK_ATTRIBUTE(
|
||||||
|
[malloc], ,
|
||||||
|
[void * __attribute__((malloc)) my_alloc(int n);],
|
||||||
|
[$1], [$2])
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_DEFUN([CC_ATTRIBUTE_PACKED], [
|
||||||
|
CC_CHECK_ATTRIBUTE(
|
||||||
|
[packed], ,
|
||||||
|
[struct astructure { char a; int b; long c; void *d; } __attribute__((packed));],
|
||||||
|
[$1], [$2])
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_DEFUN([CC_ATTRIBUTE_CONST], [
|
||||||
|
CC_CHECK_ATTRIBUTE(
|
||||||
|
[const], ,
|
||||||
|
[int __attribute__((const)) twopow(int n) { return 1 << n; } ],
|
||||||
|
[$1], [$2])
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_DEFUN([CC_FLAG_VISIBILITY], [
|
||||||
|
AC_REQUIRE([CC_CHECK_WERROR])
|
||||||
|
AC_CACHE_CHECK([if $CC supports -fvisibility=hidden],
|
||||||
|
[cc_cv_flag_visibility],
|
||||||
|
[cc_flag_visibility_save_CFLAGS="$CFLAGS"
|
||||||
|
CFLAGS="$CFLAGS $cc_cv_werror"
|
||||||
|
CC_CHECK_CFLAGS_SILENT([-fvisibility=hidden],
|
||||||
|
cc_cv_flag_visibility='yes',
|
||||||
|
cc_cv_flag_visibility='no')
|
||||||
|
CFLAGS="$cc_flag_visibility_save_CFLAGS"])
|
||||||
|
|
||||||
|
AS_IF([test "x$cc_cv_flag_visibility" = "xyes"],
|
||||||
|
[AC_DEFINE([SUPPORT_FLAG_VISIBILITY], 1,
|
||||||
|
[Define this if the compiler supports the -fvisibility flag])
|
||||||
|
$1],
|
||||||
|
[$2])
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_DEFUN([CC_FUNC_EXPECT], [
|
||||||
|
AC_REQUIRE([CC_CHECK_WERROR])
|
||||||
|
AC_CACHE_CHECK([if compiler has __builtin_expect function],
|
||||||
|
[cc_cv_func_expect],
|
||||||
|
[ac_save_CFLAGS="$CFLAGS"
|
||||||
|
CFLAGS="$CFLAGS $cc_cv_werror"
|
||||||
|
AC_COMPILE_IFELSE([AC_LANG_SOURCE(
|
||||||
|
[int some_function() {
|
||||||
|
int a = 3;
|
||||||
|
return (int)__builtin_expect(a, 3);
|
||||||
|
}])],
|
||||||
|
[cc_cv_func_expect=yes],
|
||||||
|
[cc_cv_func_expect=no])
|
||||||
|
CFLAGS="$ac_save_CFLAGS"
|
||||||
|
])
|
||||||
|
|
||||||
|
AS_IF([test "x$cc_cv_func_expect" = "xyes"],
|
||||||
|
[AC_DEFINE([SUPPORT__BUILTIN_EXPECT], 1,
|
||||||
|
[Define this if the compiler supports __builtin_expect() function])
|
||||||
|
$1],
|
||||||
|
[$2])
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_DEFUN([CC_ATTRIBUTE_ALIGNED], [
|
||||||
|
AC_REQUIRE([CC_CHECK_WERROR])
|
||||||
|
AC_CACHE_CHECK([highest __attribute__ ((aligned ())) supported],
|
||||||
|
[cc_cv_attribute_aligned],
|
||||||
|
[ac_save_CFLAGS="$CFLAGS"
|
||||||
|
CFLAGS="$CFLAGS $cc_cv_werror"
|
||||||
|
for cc_attribute_align_try in 64 32 16 8 4 2; do
|
||||||
|
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
|
||||||
|
int main() {
|
||||||
|
static char c __attribute__ ((aligned($cc_attribute_align_try))) = 0;
|
||||||
|
return c;
|
||||||
|
}])], [cc_cv_attribute_aligned=$cc_attribute_align_try; break])
|
||||||
|
done
|
||||||
|
CFLAGS="$ac_save_CFLAGS"
|
||||||
|
])
|
||||||
|
|
||||||
|
if test "x$cc_cv_attribute_aligned" != "x"; then
|
||||||
|
AC_DEFINE_UNQUOTED([ATTRIBUTE_ALIGNED_MAX], [$cc_cv_attribute_aligned],
|
||||||
|
[Define the highest alignment supported])
|
||||||
|
fi
|
||||||
|
])
|
|
@ -0,0 +1,198 @@
|
||||||
|
/* bubblewrap
|
||||||
|
* Copyright (C) 2016 Alexander Larsson
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <linux/loop.h>
|
||||||
|
#include <linux/netlink.h>
|
||||||
|
#include <linux/rtnetlink.h>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
#include "network.h"
|
||||||
|
|
||||||
|
static void *
|
||||||
|
add_rta (struct nlmsghdr *header,
|
||||||
|
int type,
|
||||||
|
size_t size)
|
||||||
|
{
|
||||||
|
struct rtattr *rta;
|
||||||
|
size_t rta_size = RTA_LENGTH (size);
|
||||||
|
|
||||||
|
rta = (struct rtattr *) ((char *) header + NLMSG_ALIGN (header->nlmsg_len));
|
||||||
|
rta->rta_type = type;
|
||||||
|
rta->rta_len = rta_size;
|
||||||
|
|
||||||
|
header->nlmsg_len = NLMSG_ALIGN (header->nlmsg_len) + rta_size;
|
||||||
|
|
||||||
|
return RTA_DATA (rta);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
rtnl_send_request (int rtnl_fd,
|
||||||
|
struct nlmsghdr *header)
|
||||||
|
{
|
||||||
|
struct sockaddr_nl dst_addr = { AF_NETLINK, 0 };
|
||||||
|
ssize_t sent;
|
||||||
|
|
||||||
|
sent = sendto (rtnl_fd, (void *) header, header->nlmsg_len, 0,
|
||||||
|
(struct sockaddr *) &dst_addr, sizeof (dst_addr));
|
||||||
|
if (sent < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
rtnl_read_reply (int rtnl_fd,
|
||||||
|
int seq_nr)
|
||||||
|
{
|
||||||
|
char buffer[1024];
|
||||||
|
ssize_t received;
|
||||||
|
struct nlmsghdr *rheader;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
received = recv (rtnl_fd, buffer, sizeof (buffer), 0);
|
||||||
|
if (received < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
rheader = (struct nlmsghdr *) buffer;
|
||||||
|
while (received >= NLMSG_HDRLEN)
|
||||||
|
{
|
||||||
|
if (rheader->nlmsg_seq != seq_nr)
|
||||||
|
return -1;
|
||||||
|
if (rheader->nlmsg_pid != getpid ())
|
||||||
|
return -1;
|
||||||
|
if (rheader->nlmsg_type == NLMSG_ERROR)
|
||||||
|
{
|
||||||
|
uint32_t *err = NLMSG_DATA (rheader);
|
||||||
|
if (*err == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (rheader->nlmsg_type == NLMSG_DONE)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
rheader = NLMSG_NEXT (rheader, received);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
rtnl_do_request (int rtnl_fd,
|
||||||
|
struct nlmsghdr *header)
|
||||||
|
{
|
||||||
|
if (rtnl_send_request (rtnl_fd, header) != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (rtnl_read_reply (rtnl_fd, header->nlmsg_seq) != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct nlmsghdr *
|
||||||
|
rtnl_setup_request (char *buffer,
|
||||||
|
int type,
|
||||||
|
int flags,
|
||||||
|
size_t size)
|
||||||
|
{
|
||||||
|
struct nlmsghdr *header;
|
||||||
|
size_t len = NLMSG_LENGTH (size);
|
||||||
|
static uint32_t counter = 0;
|
||||||
|
|
||||||
|
memset (buffer, 0, len);
|
||||||
|
|
||||||
|
header = (struct nlmsghdr *) buffer;
|
||||||
|
header->nlmsg_len = len;
|
||||||
|
header->nlmsg_type = type;
|
||||||
|
header->nlmsg_flags = flags | NLM_F_REQUEST;
|
||||||
|
header->nlmsg_seq = counter++;
|
||||||
|
header->nlmsg_pid = getpid ();
|
||||||
|
|
||||||
|
return (struct nlmsghdr *) header;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
loopback_setup (void)
|
||||||
|
{
|
||||||
|
int r, if_loopback;
|
||||||
|
cleanup_fd int rtnl_fd = -1;
|
||||||
|
char buffer[1024];
|
||||||
|
struct sockaddr_nl src_addr = { AF_NETLINK, 0 };
|
||||||
|
struct nlmsghdr *header;
|
||||||
|
struct ifaddrmsg *addmsg;
|
||||||
|
struct ifinfomsg *infomsg;
|
||||||
|
struct in_addr *ip_addr;
|
||||||
|
|
||||||
|
src_addr.nl_pid = getpid ();
|
||||||
|
|
||||||
|
if_loopback = (int) if_nametoindex ("lo");
|
||||||
|
if (if_loopback <= 0)
|
||||||
|
die_with_error ("loopback: Failed to look up lo");
|
||||||
|
|
||||||
|
rtnl_fd = socket (PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
|
||||||
|
if (rtnl_fd < 0)
|
||||||
|
die_with_error ("loopback: Failed to create NETLINK_ROUTE socket");
|
||||||
|
|
||||||
|
r = bind (rtnl_fd, (struct sockaddr *) &src_addr, sizeof (src_addr));
|
||||||
|
if (r < 0)
|
||||||
|
die_with_error ("loopback: Failed to bind NETLINK_ROUTE socket");
|
||||||
|
|
||||||
|
header = rtnl_setup_request (buffer, RTM_NEWADDR,
|
||||||
|
NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK,
|
||||||
|
sizeof (struct ifaddrmsg));
|
||||||
|
addmsg = NLMSG_DATA (header);
|
||||||
|
|
||||||
|
addmsg->ifa_family = AF_INET;
|
||||||
|
addmsg->ifa_prefixlen = 8;
|
||||||
|
addmsg->ifa_flags = IFA_F_PERMANENT;
|
||||||
|
addmsg->ifa_scope = RT_SCOPE_HOST;
|
||||||
|
addmsg->ifa_index = if_loopback;
|
||||||
|
|
||||||
|
ip_addr = add_rta (header, IFA_LOCAL, sizeof (*ip_addr));
|
||||||
|
ip_addr->s_addr = htonl (INADDR_LOOPBACK);
|
||||||
|
|
||||||
|
ip_addr = add_rta (header, IFA_ADDRESS, sizeof (*ip_addr));
|
||||||
|
ip_addr->s_addr = htonl (INADDR_LOOPBACK);
|
||||||
|
|
||||||
|
assert (header->nlmsg_len < sizeof (buffer));
|
||||||
|
|
||||||
|
if (rtnl_do_request (rtnl_fd, header) != 0)
|
||||||
|
die_with_error ("loopback: Failed RTM_NEWADDR");
|
||||||
|
|
||||||
|
header = rtnl_setup_request (buffer, RTM_NEWLINK,
|
||||||
|
NLM_F_ACK,
|
||||||
|
sizeof (struct ifinfomsg));
|
||||||
|
infomsg = NLMSG_DATA (header);
|
||||||
|
|
||||||
|
infomsg->ifi_family = AF_UNSPEC;
|
||||||
|
infomsg->ifi_type = 0;
|
||||||
|
infomsg->ifi_index = if_loopback;
|
||||||
|
infomsg->ifi_flags = IFF_UP;
|
||||||
|
infomsg->ifi_change = IFF_UP;
|
||||||
|
|
||||||
|
assert (header->nlmsg_len < sizeof (buffer));
|
||||||
|
|
||||||
|
if (rtnl_do_request (rtnl_fd, header) != 0)
|
||||||
|
die_with_error ("loopback: Failed RTM_NEWLINK");
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
/* bubblewrap
|
||||||
|
* Copyright (C) 2016 Alexander Larsson
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
void loopback_setup (void);
|
|
@ -0,0 +1,49 @@
|
||||||
|
%global commit0 66d12bb23b04e201c5846e325f0b10930ed802f8
|
||||||
|
%global shortcommit0 %(c=%{commit0}; echo ${c:0:7})
|
||||||
|
|
||||||
|
Summary: Core execution tool for unprivileged containers
|
||||||
|
Name: bubblewrap
|
||||||
|
Version: 0
|
||||||
|
Release: 1%{?dist}
|
||||||
|
#VCS: git:https://github.com/projectatomic/bubblewrap
|
||||||
|
Source0: https://github.com/projectatomic/%{name}/archive/%{commit0}.tar.gz#/%{name}-%{shortcommit0}.tar.gz
|
||||||
|
License: LGPLv2+
|
||||||
|
URL: https://github.com/projectatomic/bubblewrap
|
||||||
|
|
||||||
|
BuildRequires: git
|
||||||
|
# We always run autogen.sh
|
||||||
|
BuildRequires: autoconf automake libtool
|
||||||
|
BuildRequires: libcap-devel
|
||||||
|
BuildRequires: pkgconfig(libselinux)
|
||||||
|
BuildRequires: libxslt
|
||||||
|
BuildRequires: docbook-style-xsl
|
||||||
|
|
||||||
|
%description
|
||||||
|
Bubblewrap (/usr/bin/bwrap) is a core execution engine for unprivileged
|
||||||
|
containers that works as a setuid binary on kernels without
|
||||||
|
user namespaces.
|
||||||
|
|
||||||
|
%prep
|
||||||
|
%autosetup -Sgit -n %{name}-%{version}
|
||||||
|
|
||||||
|
%build
|
||||||
|
env NOCONFIGURE=1 ./autogen.sh
|
||||||
|
%configure --disable-silent-rules --with-priv-mode=none
|
||||||
|
|
||||||
|
make %{?_smp_mflags}
|
||||||
|
|
||||||
|
%install
|
||||||
|
make install DESTDIR=$RPM_BUILD_ROOT INSTALL="install -p -c"
|
||||||
|
find $RPM_BUILD_ROOT -name '*.la' -delete
|
||||||
|
|
||||||
|
%files
|
||||||
|
%license COPYING
|
||||||
|
%doc README.md
|
||||||
|
%{_datadir}/bash-completion/completions/bwrap
|
||||||
|
%if (0%{?rhel} != 0 && 0%{?rhel} <= 7)
|
||||||
|
%attr(4755,root,root) %{_bindir}/bwrap
|
||||||
|
%else
|
||||||
|
%{_bindir}/bwrap
|
||||||
|
%endif
|
||||||
|
%{_mandir}/man1/*
|
||||||
|
|
|
@ -0,0 +1,164 @@
|
||||||
|
# Core source library for shell script tests; the
|
||||||
|
# canonical version lives in:
|
||||||
|
#
|
||||||
|
# https://github.com/ostreedev/ostree
|
||||||
|
#
|
||||||
|
# Known copies are in the following repos:
|
||||||
|
#
|
||||||
|
# - https://github.com/projectatomic/rpm-ostree
|
||||||
|
#
|
||||||
|
# Copyright (C) 2017 Colin Walters <walters@verbum.org>
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the
|
||||||
|
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
# Boston, MA 02111-1307, USA.
|
||||||
|
|
||||||
|
fatal() {
|
||||||
|
echo $@ 1>&2; exit 1
|
||||||
|
}
|
||||||
|
# fatal() is shorter to type, but retain this alias
|
||||||
|
assert_not_reached () {
|
||||||
|
fatal "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Some tests look for specific English strings. Use a UTF-8 version
|
||||||
|
# of the C (POSIX) locale if we have one, or fall back to POSIX
|
||||||
|
# (https://sourceware.org/glibc/wiki/Proposals/C.UTF-8)
|
||||||
|
if locale -a | grep C.UTF-8 >/dev/null; then
|
||||||
|
export LC_ALL=C.UTF-8
|
||||||
|
else
|
||||||
|
export LC_ALL=C
|
||||||
|
fi
|
||||||
|
|
||||||
|
# This should really be the default IMO
|
||||||
|
export G_DEBUG=fatal-warnings
|
||||||
|
|
||||||
|
assert_streq () {
|
||||||
|
test "$1" = "$2" || fatal "$1 != $2"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_str_match () {
|
||||||
|
if ! echo "$1" | grep -E -q "$2"; then
|
||||||
|
fatal "$1 does not match regexp $2"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_not_streq () {
|
||||||
|
(! test "$1" = "$2") || fatal "$1 == $2"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_has_file () {
|
||||||
|
test -f "$1" || fatal "Couldn't find '$1'"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_has_dir () {
|
||||||
|
test -d "$1" || fatal "Couldn't find '$1'"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Dump ls -al + file contents to stderr, then fatal()
|
||||||
|
_fatal_print_file() {
|
||||||
|
file="$1"
|
||||||
|
shift
|
||||||
|
ls -al "$file" >&2
|
||||||
|
sed -e 's/^/# /' < "$file" >&2
|
||||||
|
fatal "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
_fatal_print_files() {
|
||||||
|
file1="$1"
|
||||||
|
shift
|
||||||
|
file2="$1"
|
||||||
|
shift
|
||||||
|
ls -al "$file1" >&2
|
||||||
|
sed -e 's/^/# /' < "$file1" >&2
|
||||||
|
ls -al "$file2" >&2
|
||||||
|
sed -e 's/^/# /' < "$file2" >&2
|
||||||
|
fatal "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_not_has_file () {
|
||||||
|
if test -f "$1"; then
|
||||||
|
_fatal_print_file "$1" "File '$1' exists"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_not_file_has_content () {
|
||||||
|
fpath=$1
|
||||||
|
shift
|
||||||
|
for re in "$@"; do
|
||||||
|
if grep -q -e "$re" "$fpath"; then
|
||||||
|
_fatal_print_file "$fpath" "File '$fpath' matches regexp '$re'"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_not_has_dir () {
|
||||||
|
if test -d "$1"; then
|
||||||
|
fatal "Directory '$1' exists"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_file_has_content () {
|
||||||
|
fpath=$1
|
||||||
|
shift
|
||||||
|
for re in "$@"; do
|
||||||
|
if ! grep -q -e "$re" "$fpath"; then
|
||||||
|
_fatal_print_file "$fpath" "File '$fpath' doesn't match regexp '$re'"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_file_has_content_literal () {
|
||||||
|
if ! grep -q -F -e "$2" "$1"; then
|
||||||
|
_fatal_print_file "$1" "File '$1' doesn't match fixed string list '$2'"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_file_has_mode () {
|
||||||
|
mode=$(stat -c '%a' $1)
|
||||||
|
if [ "$mode" != "$2" ]; then
|
||||||
|
fatal "File '$1' has wrong mode: expected $2, but got $mode"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_symlink_has_content () {
|
||||||
|
if ! test -L "$1"; then
|
||||||
|
fatal "File '$1' is not a symbolic link"
|
||||||
|
fi
|
||||||
|
if ! readlink "$1" | grep -q -e "$2"; then
|
||||||
|
_fatal_print_file "$1" "Symbolic link '$1' doesn't match regexp '$2'"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_file_empty() {
|
||||||
|
if test -s "$1"; then
|
||||||
|
_fatal_print_file "$1" "File '$1' is not empty"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_files_equal() {
|
||||||
|
if ! cmp "$1" "$2"; then
|
||||||
|
_fatal_print_files "$1" "$2" "File '$1' and '$2' is not equal"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Use to skip all of these tests
|
||||||
|
skip() {
|
||||||
|
echo "1..0 # SKIP" "$@"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
extract_child_pid() {
|
||||||
|
grep child-pid "$1" | sed "s/^.*: \([0-9]*\).*/\1/"
|
||||||
|
}
|
|
@ -0,0 +1,379 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -xeuo pipefail
|
||||||
|
|
||||||
|
# Make sure /sbin/getpcaps etc. are in our PATH even if non-root
|
||||||
|
PATH="$PATH:/usr/sbin:/sbin"
|
||||||
|
|
||||||
|
srcd=$(cd $(dirname $0) && pwd)
|
||||||
|
|
||||||
|
. ${srcd}/libtest-core.sh
|
||||||
|
|
||||||
|
bn=$(basename $0)
|
||||||
|
tempdir=$(mktemp -d /var/tmp/tap-test.XXXXXX)
|
||||||
|
touch ${tempdir}/.testtmp
|
||||||
|
function cleanup () {
|
||||||
|
if test -n "${TEST_SKIP_CLEANUP:-}"; then
|
||||||
|
echo "Skipping cleanup of ${test_tmpdir}"
|
||||||
|
else if test -f ${tempdir}/.test; then
|
||||||
|
rm "${tempdir}" -rf
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
cd ${tempdir}
|
||||||
|
|
||||||
|
: "${BWRAP:=bwrap}"
|
||||||
|
if test -u "$(type -p ${BWRAP})"; then
|
||||||
|
bwrap_is_suid=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
FUSE_DIR=
|
||||||
|
for mp in $(cat /proc/self/mounts | grep " fuse[. ]" | grep user_id=$(id -u) | awk '{print $2}'); do
|
||||||
|
if test -d $mp; then
|
||||||
|
echo Using $mp as test fuse mount
|
||||||
|
FUSE_DIR=$mp
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if test "$(id -u)" = "0"; then
|
||||||
|
is_uidzero=true
|
||||||
|
else
|
||||||
|
is_uidzero=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
# This is supposed to be an otherwise readable file in an unreadable (by the user) dir
|
||||||
|
UNREADABLE=/root/.bashrc
|
||||||
|
if ${is_uidzero} || test -x `dirname $UNREADABLE`; then
|
||||||
|
UNREADABLE=
|
||||||
|
fi
|
||||||
|
|
||||||
|
# https://github.com/projectatomic/bubblewrap/issues/217
|
||||||
|
# are we on a merged-/usr system?
|
||||||
|
if [ /lib -ef /usr/lib ]; then
|
||||||
|
BWRAP_RO_HOST_ARGS="--ro-bind /usr /usr
|
||||||
|
--ro-bind /etc /etc
|
||||||
|
--dir /var/tmp
|
||||||
|
--symlink usr/lib /lib
|
||||||
|
--symlink usr/lib64 /lib64
|
||||||
|
--symlink usr/bin /bin
|
||||||
|
--symlink usr/sbin /sbin
|
||||||
|
--proc /proc
|
||||||
|
--dev /dev"
|
||||||
|
else
|
||||||
|
BWRAP_RO_HOST_ARGS="--ro-bind /usr /usr
|
||||||
|
--ro-bind /etc /etc
|
||||||
|
--ro-bind /bin /bin
|
||||||
|
--ro-bind /lib /lib
|
||||||
|
--ro-bind-try /lib64 /lib64
|
||||||
|
--ro-bind /sbin /sbin
|
||||||
|
--dir /var/tmp
|
||||||
|
--proc /proc
|
||||||
|
--dev /dev"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Default arg, bind whole host fs to /, tmpfs on /tmp
|
||||||
|
RUN="${BWRAP} --bind / / --tmpfs /tmp"
|
||||||
|
|
||||||
|
if ! $RUN true; then
|
||||||
|
skip Seems like bwrap is not working at all. Maybe setuid is not working
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "1..49"
|
||||||
|
|
||||||
|
# Test help
|
||||||
|
${BWRAP} --help > help.txt
|
||||||
|
assert_file_has_content help.txt "usage: ${BWRAP}"
|
||||||
|
echo "ok - Help works"
|
||||||
|
|
||||||
|
for ALT in "" "--unshare-user-try" "--unshare-pid" "--unshare-user-try --unshare-pid"; do
|
||||||
|
# Test fuse fs as bind source
|
||||||
|
if [ x$FUSE_DIR != x ]; then
|
||||||
|
$RUN $ALT --proc /proc --dev /dev --bind $FUSE_DIR /tmp/foo true
|
||||||
|
echo "ok - can bind-mount a FUSE directory with $ALT"
|
||||||
|
else
|
||||||
|
echo "ok # SKIP no FUSE support"
|
||||||
|
fi
|
||||||
|
# no --dev => no devpts => no map_root workaround
|
||||||
|
$RUN $ALT --proc /proc true
|
||||||
|
echo "ok - can mount /proc with $ALT"
|
||||||
|
# No network
|
||||||
|
$RUN $ALT --unshare-net --proc /proc --dev /dev true
|
||||||
|
echo "ok - can unshare network, create new /dev with $ALT"
|
||||||
|
# Unreadable file
|
||||||
|
echo -n "expect EPERM: " >&2
|
||||||
|
|
||||||
|
# Test caps when bwrap is not setuid
|
||||||
|
if test -n "${bwrap_is_suid:-}"; then
|
||||||
|
CAP="--cap-add ALL"
|
||||||
|
else
|
||||||
|
CAP=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! ${is_uidzero} && $RUN $CAP $ALT --unshare-net --proc /proc --bind /etc/shadow /tmp/foo cat /etc/shadow; then
|
||||||
|
assert_not_reached Could read /etc/shadow
|
||||||
|
fi
|
||||||
|
echo "ok - cannot read /etc/shadow with $ALT"
|
||||||
|
# Unreadable dir
|
||||||
|
if [ x$UNREADABLE != x ]; then
|
||||||
|
echo -n "expect EPERM: " >&2
|
||||||
|
if $RUN $ALT --unshare-net --proc /proc --dev /dev --bind $UNREADABLE /tmp/foo cat /tmp/foo ; then
|
||||||
|
assert_not_reached Could read $UNREADABLE
|
||||||
|
fi
|
||||||
|
echo "ok - cannot read $UNREADABLE with $ALT"
|
||||||
|
else
|
||||||
|
echo "ok # SKIP not sure what unreadable file to use"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# bind dest in symlink (https://github.com/projectatomic/bubblewrap/pull/119)
|
||||||
|
$RUN $ALT --dir /tmp/dir --symlink dir /tmp/link --bind /etc /tmp/link true
|
||||||
|
echo "ok - can bind a destination over a symlink"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Test devices
|
||||||
|
$RUN --unshare-pid --dev /dev ls -al /dev/{stdin,stdout,stderr,null,random,urandom,fd,core} >/dev/null
|
||||||
|
echo "ok - all expected devices were created"
|
||||||
|
|
||||||
|
# Test --as-pid-1
|
||||||
|
$RUN --unshare-pid --as-pid-1 --bind / / bash -c 'echo $$' > as_pid_1.txt
|
||||||
|
assert_file_has_content as_pid_1.txt "1"
|
||||||
|
echo "ok - can run as pid 1"
|
||||||
|
|
||||||
|
# Test --info-fd and --json-status-fd
|
||||||
|
if $RUN --unshare-all --info-fd 42 --json-status-fd 43 -- bash -c 'exit 42' 42>info.json 43>json-status.json 2>err.txt; then
|
||||||
|
fatal "should have been exit 42"
|
||||||
|
fi
|
||||||
|
assert_file_has_content info.json '"child-pid": [0-9]'
|
||||||
|
assert_file_has_content json-status.json '"child-pid": [0-9]'
|
||||||
|
assert_file_has_content_literal json-status.json '"exit-code": 42'
|
||||||
|
echo "ok info and json-status fd"
|
||||||
|
|
||||||
|
DATA=$($RUN --proc /proc --unshare-all --info-fd 42 --json-status-fd 43 -- bash -c 'stat -L --format "%n %i" /proc/self/ns/*' 42>info.json 43>json-status.json 2>err.txt)
|
||||||
|
|
||||||
|
for NS in "ipc" "mnt" "net" "pid" "uts"; do
|
||||||
|
|
||||||
|
want=$(echo "$DATA" | grep "/proc/self/ns/$NS" | awk '{print $2}')
|
||||||
|
assert_file_has_content info.json "$want"
|
||||||
|
assert_file_has_content json-status.json "$want"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "ok namespace id info in info and json-status fd"
|
||||||
|
|
||||||
|
if ! which strace 2>/dev/null || ! strace -h | grep -v -e default | grep -e fault; then
|
||||||
|
echo "ok - # SKIP no strace fault injection"
|
||||||
|
else
|
||||||
|
! strace -o /dev/null -f -e trace=prctl -e fault=prctl:when=39 $RUN --die-with-parent --json-status-fd 42 true 42>json-status.json
|
||||||
|
assert_not_file_has_content json-status.json '"exit-code": [0-9]'
|
||||||
|
echo "ok pre-exec failure doesn't include exit-code in json-status"
|
||||||
|
fi
|
||||||
|
|
||||||
|
notanexecutable=/
|
||||||
|
$RUN --json-status-fd 42 $notanexecutable 42>json-status.json || true
|
||||||
|
assert_not_file_has_content json-status.json '"exit-code": [0-9]'
|
||||||
|
echo "ok exec failure doesn't include exit-code in json-status"
|
||||||
|
|
||||||
|
# These tests require --unshare-user
|
||||||
|
if test -n "${bwrap_is_suid:-}"; then
|
||||||
|
echo "ok - # SKIP no --cap-add support"
|
||||||
|
echo "ok - # SKIP no --cap-add support"
|
||||||
|
else
|
||||||
|
BWRAP_RECURSE="$BWRAP --unshare-all --uid 0 --gid 0 --cap-add ALL --bind / / --bind /proc /proc"
|
||||||
|
$BWRAP_RECURSE -- $BWRAP --unshare-all --bind / / --bind /proc /proc echo hello > recursive_proc.txt
|
||||||
|
assert_file_has_content recursive_proc.txt "hello"
|
||||||
|
echo "ok - can mount /proc recursively"
|
||||||
|
|
||||||
|
$BWRAP_RECURSE -- $BWRAP --unshare-all ${BWRAP_RO_HOST_ARGS} findmnt > recursive-newroot.txt
|
||||||
|
assert_file_has_content recursive-newroot.txt "/usr"
|
||||||
|
echo "ok - can pivot to new rootfs recursively"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test error prefixing
|
||||||
|
if $RUN --unshare-pid --bind /source-enoent /dest true 2>err.txt; then
|
||||||
|
assert_not_reached "bound nonexistent source"
|
||||||
|
fi
|
||||||
|
assert_file_has_content err.txt "^bwrap: Can't find source path.*source-enoent"
|
||||||
|
echo "ok error prefxing"
|
||||||
|
|
||||||
|
if ! ${is_uidzero}; then
|
||||||
|
# When invoked as non-root, check that by default we have no caps left
|
||||||
|
for OPT in "" "--unshare-user-try --as-pid-1" "--unshare-user-try" "--as-pid-1"; do
|
||||||
|
e=0
|
||||||
|
$RUN $OPT --unshare-pid getpcaps 1 2> caps.test || e=$?
|
||||||
|
sed -e 's/^/# /' < caps.test >&2
|
||||||
|
test "$e" = 0
|
||||||
|
assert_not_file_has_content caps.test ': =.*cap'
|
||||||
|
done
|
||||||
|
echo "ok - we have no caps as uid != 0"
|
||||||
|
else
|
||||||
|
capsh --print > caps.orig
|
||||||
|
for OPT in "" "--as-pid-1"; do
|
||||||
|
$RUN $OPT --unshare-pid capsh --print >caps.test
|
||||||
|
diff -u caps.orig caps.test
|
||||||
|
done
|
||||||
|
# And test that we can drop all, as well as specific caps
|
||||||
|
$RUN $OPT --cap-drop ALL --unshare-pid capsh --print >caps.test
|
||||||
|
assert_file_has_content caps.test 'Current: =$'
|
||||||
|
# Check for dropping kill/fowner (we assume all uid 0 callers have this)
|
||||||
|
$RUN $OPT --cap-drop CAP_KILL --cap-drop CAP_FOWNER --unshare-pid capsh --print >caps.test
|
||||||
|
assert_not_file_has_content caps.test '^Current: =.*cap_kill'
|
||||||
|
assert_not_file_has_content caps.test '^Current: =.*cap_fowner'
|
||||||
|
# But we should still have net_bind_service for example
|
||||||
|
assert_file_has_content caps.test '^Current: =.*cap_net_bind_service'
|
||||||
|
echo "ok - we have the expected caps as uid 0"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test --die-with-parent
|
||||||
|
|
||||||
|
cat >lockf-n.py <<EOF
|
||||||
|
#!/usr/bin/env python
|
||||||
|
import struct,fcntl,sys
|
||||||
|
path = sys.argv[1]
|
||||||
|
if sys.argv[2] == 'wait':
|
||||||
|
locktype = fcntl.F_SETLKW
|
||||||
|
else:
|
||||||
|
locktype = fcntl.F_SETLK
|
||||||
|
lockdata = struct.pack("hhqqhh", fcntl.F_WRLCK, 0, 0, 0, 0, 0)
|
||||||
|
fd=open(sys.argv[1], 'a')
|
||||||
|
try:
|
||||||
|
fcntl.fcntl(fd.fileno(), locktype, lockdata)
|
||||||
|
except IOError as e:
|
||||||
|
sys.exit(1)
|
||||||
|
sys.exit(0)
|
||||||
|
EOF
|
||||||
|
chmod a+x lockf-n.py
|
||||||
|
touch lock
|
||||||
|
|
||||||
|
for die_with_parent_argv in "--die-with-parent" "--die-with-parent --unshare-pid"; do
|
||||||
|
# We have to loop here, because bwrap doesn't wait for the lock if
|
||||||
|
# another process is holding it. If we're unlucky, lockf-n.py will
|
||||||
|
# be holding it.
|
||||||
|
/bin/bash -c "while true; do $RUN ${die_with_parent_argv} --lock-file $(pwd)/lock sleep 1h; done" &
|
||||||
|
childshellpid=$!
|
||||||
|
|
||||||
|
# Wait for lock to be taken (yes hacky)
|
||||||
|
for x in $(seq 10); do
|
||||||
|
if ./lockf-n.py ./lock nowait; then
|
||||||
|
sleep 1
|
||||||
|
else
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if ./lockf-n.py ./lock nowait; then
|
||||||
|
assert_not_reached "timed out waiting for lock"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Kill the shell, which should kill bwrap (and the sleep)
|
||||||
|
kill -9 ${childshellpid}
|
||||||
|
# Lock file should be unlocked
|
||||||
|
./lockf-n.py ./lock wait
|
||||||
|
echo "ok die with parent ${die_with_parent_argv}"
|
||||||
|
done
|
||||||
|
|
||||||
|
printf '%s--dir\0/tmp/hello/world\0' '' > test.args
|
||||||
|
$RUN --args 3 test -d /tmp/hello/world 3<test.args
|
||||||
|
echo "ok - we can parse arguments from a fd"
|
||||||
|
|
||||||
|
mkdir bin
|
||||||
|
echo "#!/bin/sh" > bin/--inadvisable-executable-name--
|
||||||
|
echo "echo hello" >> bin/--inadvisable-executable-name--
|
||||||
|
chmod +x bin/--inadvisable-executable-name--
|
||||||
|
PATH="${srcd}:$PATH" $RUN -- sh -c "echo hello" > stdout
|
||||||
|
assert_file_has_content stdout hello
|
||||||
|
echo "ok - we can run with --"
|
||||||
|
PATH="$(pwd)/bin:$PATH" $RUN -- --inadvisable-executable-name-- > stdout
|
||||||
|
assert_file_has_content stdout hello
|
||||||
|
echo "ok - we can run an inadvisable executable name with --"
|
||||||
|
if $RUN -- --dev-bind /dev /dev sh -c 'echo should not have run'; then
|
||||||
|
assert_not_reached "'--dev-bind' should have been interpreted as a (silly) executable name"
|
||||||
|
fi
|
||||||
|
echo "ok - options like --dev-bind are defanged by --"
|
||||||
|
|
||||||
|
if command -v mktemp > /dev/null; then
|
||||||
|
tempfile="$(mktemp /tmp/bwrap-test-XXXXXXXX)"
|
||||||
|
echo "hello" > "$tempfile"
|
||||||
|
$BWRAP --bind / / cat "$tempfile" > stdout
|
||||||
|
assert_file_has_content stdout hello
|
||||||
|
echo "ok - bind-mount of / exposes real /tmp"
|
||||||
|
$BWRAP --bind / / --bind /tmp /tmp cat "$tempfile" > stdout
|
||||||
|
assert_file_has_content stdout hello
|
||||||
|
echo "ok - bind-mount of /tmp exposes real /tmp"
|
||||||
|
if [ -d /mnt ]; then
|
||||||
|
$BWRAP --bind / / --bind /tmp /mnt cat "/mnt/${tempfile#/tmp/}" > stdout
|
||||||
|
assert_file_has_content stdout hello
|
||||||
|
echo "ok - bind-mount of /tmp onto /mnt exposes real /tmp"
|
||||||
|
else
|
||||||
|
echo "ok - # SKIP /mnt does not exist"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "ok - # SKIP mktemp not found"
|
||||||
|
echo "ok - # SKIP mktemp not found"
|
||||||
|
echo "ok - # SKIP mktemp not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if $RUN test -d /tmp/oldroot; then
|
||||||
|
assert_not_reached "/tmp/oldroot should not be visible"
|
||||||
|
fi
|
||||||
|
if $RUN test -d /tmp/newroot; then
|
||||||
|
assert_not_reached "/tmp/newroot should not be visible"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "hello" > input.$$
|
||||||
|
$BWRAP --bind / / --bind "$(pwd)" /tmp cat /tmp/input.$$ > stdout
|
||||||
|
assert_file_has_content stdout hello
|
||||||
|
if $BWRAP --bind / / --bind "$(pwd)" /tmp test -d /tmp/oldroot; then
|
||||||
|
assert_not_reached "/tmp/oldroot should not be visible"
|
||||||
|
fi
|
||||||
|
if $BWRAP --bind / / --bind "$(pwd)" /tmp test -d /tmp/newroot; then
|
||||||
|
assert_not_reached "/tmp/newroot should not be visible"
|
||||||
|
fi
|
||||||
|
echo "ok - we can mount another directory onto /tmp"
|
||||||
|
|
||||||
|
echo "hello" > input.$$
|
||||||
|
$RUN --bind "$(pwd)" /tmp/here cat /tmp/here/input.$$ > stdout
|
||||||
|
assert_file_has_content stdout hello
|
||||||
|
if $RUN --bind "$(pwd)" /tmp/here test -d /tmp/oldroot; then
|
||||||
|
assert_not_reached "/tmp/oldroot should not be visible"
|
||||||
|
fi
|
||||||
|
if $RUN --bind "$(pwd)" /tmp/here test -d /tmp/newroot; then
|
||||||
|
assert_not_reached "/tmp/newroot should not be visible"
|
||||||
|
fi
|
||||||
|
echo "ok - we can mount another directory inside /tmp"
|
||||||
|
|
||||||
|
# These tests need user namespaces
|
||||||
|
if test -n "${bwrap_is_suid:-}"; then
|
||||||
|
echo "ok - # SKIP no setuid support for --unshare-user"
|
||||||
|
echo "ok - # SKIP no setuid support for --unshare-user"
|
||||||
|
else
|
||||||
|
mkfifo donepipe
|
||||||
|
|
||||||
|
$RUN --info-fd 42 --unshare-user sh -c 'readlink /proc/self/ns/user > sandbox-userns; cat < donepipe' 42>info.json &
|
||||||
|
while ! test -f sandbox-userns; do sleep 1; done
|
||||||
|
SANDBOX1PID=$(extract_child_pid info.json)
|
||||||
|
|
||||||
|
$RUN --userns 11 readlink /proc/self/ns/user > sandbox2-userns 11< /proc/$SANDBOX1PID/ns/user
|
||||||
|
echo foo > donepipe
|
||||||
|
|
||||||
|
assert_files_equal sandbox-userns sandbox2-userns
|
||||||
|
|
||||||
|
rm donepipe info.json sandbox-userns
|
||||||
|
|
||||||
|
echo "ok - Test --userns"
|
||||||
|
|
||||||
|
mkfifo donepipe
|
||||||
|
$RUN --info-fd 42 --unshare-user --unshare-pid sh -c 'readlink /proc/self/ns/pid > sandbox-pidns; cat < donepipe' 42>info.json &
|
||||||
|
while ! test -f sandbox-pidns; do sleep 1; done
|
||||||
|
SANDBOX1PID=$(extract_child_pid info.json)
|
||||||
|
|
||||||
|
$RUN --userns 11 --pidns 12 readlink /proc/self/ns/pid > sandbox2-pidns 11< /proc/$SANDBOX1PID/ns/user 12< /proc/$SANDBOX1PID/ns/pid
|
||||||
|
echo foo > donepipe
|
||||||
|
|
||||||
|
assert_files_equal sandbox-pidns sandbox2-pidns
|
||||||
|
|
||||||
|
rm donepipe info.json sandbox-pidns
|
||||||
|
|
||||||
|
echo "ok - Test --pidns"
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
echo "ok - End of test"
|
|
@ -0,0 +1,136 @@
|
||||||
|
newlines lf
|
||||||
|
|
||||||
|
input_tab_size 8
|
||||||
|
output_tab_size 8
|
||||||
|
|
||||||
|
string_escape_char 92
|
||||||
|
string_escape_char2 0
|
||||||
|
|
||||||
|
# indenting
|
||||||
|
indent_columns 2
|
||||||
|
indent_with_tabs 0
|
||||||
|
indent_align_string True
|
||||||
|
indent_brace 2
|
||||||
|
indent_braces false
|
||||||
|
indent_braces_no_func True
|
||||||
|
indent_func_call_param false
|
||||||
|
indent_func_def_param false
|
||||||
|
indent_func_proto_param false
|
||||||
|
indent_switch_case 0
|
||||||
|
indent_case_brace 2
|
||||||
|
indent_paren_close 1
|
||||||
|
|
||||||
|
# spacing
|
||||||
|
sp_arith Add
|
||||||
|
sp_assign Add
|
||||||
|
sp_enum_assign Add
|
||||||
|
sp_bool Add
|
||||||
|
sp_compare Add
|
||||||
|
sp_inside_paren Remove
|
||||||
|
sp_inside_fparens Remove
|
||||||
|
sp_func_def_paren Force
|
||||||
|
sp_func_proto_paren Force
|
||||||
|
sp_paren_paren Remove
|
||||||
|
sp_balance_nested_parens False
|
||||||
|
sp_paren_brace Remove
|
||||||
|
sp_before_square Remove
|
||||||
|
sp_before_squares Remove
|
||||||
|
sp_inside_square Remove
|
||||||
|
sp_before_ptr_star Add
|
||||||
|
sp_between_ptr_star Remove
|
||||||
|
sp_after_comma Add
|
||||||
|
sp_before_comma Remove
|
||||||
|
sp_after_cast Add
|
||||||
|
sp_sizeof_paren Add
|
||||||
|
sp_not Remove
|
||||||
|
sp_inv Remove
|
||||||
|
sp_addr Remove
|
||||||
|
sp_member Remove
|
||||||
|
sp_deref Remove
|
||||||
|
sp_sign Remove
|
||||||
|
sp_incdec Remove
|
||||||
|
sp_attribute_paren remove
|
||||||
|
sp_macro Force
|
||||||
|
sp_func_call_paren Force
|
||||||
|
sp_func_call_user_paren Remove
|
||||||
|
set func_call_user _ N_ C_ g_autoptr g_auto
|
||||||
|
sp_brace_typedef add
|
||||||
|
sp_cond_colon add
|
||||||
|
sp_cond_question add
|
||||||
|
sp_defined_paren remove
|
||||||
|
|
||||||
|
# alignment
|
||||||
|
align_keep_tabs False
|
||||||
|
align_with_tabs False
|
||||||
|
align_on_tabstop False
|
||||||
|
align_number_left True
|
||||||
|
align_func_params True
|
||||||
|
align_var_def_span 0
|
||||||
|
align_var_def_amp_style 1
|
||||||
|
align_var_def_colon true
|
||||||
|
align_enum_equ_span 0
|
||||||
|
align_var_struct_span 2
|
||||||
|
align_var_def_star_style 2
|
||||||
|
align_var_def_amp_style 2
|
||||||
|
align_typedef_span 2
|
||||||
|
align_typedef_func 0
|
||||||
|
align_typedef_star_style 2
|
||||||
|
align_typedef_amp_style 2
|
||||||
|
|
||||||
|
# newlines
|
||||||
|
nl_assign_leave_one_liners True
|
||||||
|
nl_enum_leave_one_liners False
|
||||||
|
nl_func_leave_one_liners False
|
||||||
|
nl_if_leave_one_liners False
|
||||||
|
nl_end_of_file Add
|
||||||
|
nl_assign_brace Remove
|
||||||
|
nl_func_var_def_blk 1
|
||||||
|
nl_fcall_brace Add
|
||||||
|
nl_enum_brace Remove
|
||||||
|
nl_struct_brace Force
|
||||||
|
nl_union_brace Force
|
||||||
|
nl_if_brace Force
|
||||||
|
nl_brace_else Force
|
||||||
|
nl_elseif_brace Force
|
||||||
|
nl_else_brace Add
|
||||||
|
nl_for_brace Force
|
||||||
|
nl_while_brace Force
|
||||||
|
nl_do_brace Force
|
||||||
|
nl_brace_while Force
|
||||||
|
nl_switch_brace Force
|
||||||
|
nl_before_case True
|
||||||
|
nl_after_case False
|
||||||
|
nl_func_type_name Force
|
||||||
|
nl_func_proto_type_name Remove
|
||||||
|
nl_func_paren Remove
|
||||||
|
nl_func_decl_start Remove
|
||||||
|
nl_func_decl_args Force
|
||||||
|
nl_func_decl_end Remove
|
||||||
|
nl_fdef_brace Force
|
||||||
|
nl_after_return False
|
||||||
|
nl_define_macro False
|
||||||
|
nl_create_if_one_liner False
|
||||||
|
nl_create_for_one_liner False
|
||||||
|
nl_create_while_one_liner False
|
||||||
|
nl_after_semicolon True
|
||||||
|
nl_multi_line_cond true
|
||||||
|
|
||||||
|
# mod
|
||||||
|
# I'd like these to be remove, but that removes brackets in if { if { foo } }, which i dislike
|
||||||
|
# Not clear what to do about that...
|
||||||
|
mod_full_brace_for Remove
|
||||||
|
mod_full_brace_if Remove
|
||||||
|
mod_full_brace_if_chain True
|
||||||
|
mod_full_brace_while Remove
|
||||||
|
mod_full_brace_do Remove
|
||||||
|
mod_full_brace_nl 3
|
||||||
|
mod_paren_on_return Remove
|
||||||
|
|
||||||
|
# line splitting
|
||||||
|
#code_width = 78
|
||||||
|
ls_for_split_full True
|
||||||
|
ls_func_split_full True
|
||||||
|
|
||||||
|
# positioning
|
||||||
|
pos_bool Trail
|
||||||
|
pos_conditional Trail
|
|
@ -0,0 +1,2 @@
|
||||||
|
#!/bin/sh
|
||||||
|
uncrustify -c uncrustify.cfg --no-backup `git ls-tree --name-only -r HEAD | grep \\\.[ch]$`
|
|
@ -0,0 +1,826 @@
|
||||||
|
/* bubblewrap
|
||||||
|
* Copyright (C) 2016 Alexander Larsson
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#ifdef HAVE_SELINUX
|
||||||
|
#include <selinux/selinux.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void
|
||||||
|
die_with_error (const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
int errsv;
|
||||||
|
|
||||||
|
fprintf (stderr, "bwrap: ");
|
||||||
|
|
||||||
|
errsv = errno;
|
||||||
|
|
||||||
|
va_start (args, format);
|
||||||
|
vfprintf (stderr, format, args);
|
||||||
|
va_end (args);
|
||||||
|
|
||||||
|
fprintf (stderr, ": %s\n", strerror (errsv));
|
||||||
|
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
die (const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
fprintf (stderr, "bwrap: ");
|
||||||
|
|
||||||
|
va_start (args, format);
|
||||||
|
vfprintf (stderr, format, args);
|
||||||
|
va_end (args);
|
||||||
|
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
die_unless_label_valid (const char *label)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_SELINUX
|
||||||
|
if (is_selinux_enabled () == 1)
|
||||||
|
{
|
||||||
|
if (security_check_context ((security_context_t) label) < 0)
|
||||||
|
die_with_error ("invalid label %s", label);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
die ("labeling not supported on this system");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
die_oom (void)
|
||||||
|
{
|
||||||
|
fputs ("Out of memory\n", stderr);
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fork, return in child, exiting the previous parent */
|
||||||
|
void
|
||||||
|
fork_intermediate_child (void)
|
||||||
|
{
|
||||||
|
int pid = fork ();
|
||||||
|
if (pid == -1)
|
||||||
|
die_with_error ("Can't fork for --pidns");
|
||||||
|
|
||||||
|
/* Parent is an process not needed */
|
||||||
|
if (pid != 0)
|
||||||
|
exit (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
xmalloc (size_t size)
|
||||||
|
{
|
||||||
|
void *res = malloc (size);
|
||||||
|
|
||||||
|
if (res == NULL)
|
||||||
|
die_oom ();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
xcalloc (size_t size)
|
||||||
|
{
|
||||||
|
void *res = calloc (1, size);
|
||||||
|
|
||||||
|
if (res == NULL)
|
||||||
|
die_oom ();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
xrealloc (void *ptr, size_t size)
|
||||||
|
{
|
||||||
|
void *res = realloc (ptr, size);
|
||||||
|
|
||||||
|
if (size != 0 && res == NULL)
|
||||||
|
die_oom ();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
xstrdup (const char *str)
|
||||||
|
{
|
||||||
|
char *res;
|
||||||
|
|
||||||
|
assert (str != NULL);
|
||||||
|
|
||||||
|
res = strdup (str);
|
||||||
|
if (res == NULL)
|
||||||
|
die_oom ();
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
strfreev (char **str_array)
|
||||||
|
{
|
||||||
|
if (str_array)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; str_array[i] != NULL; i++)
|
||||||
|
free (str_array[i]);
|
||||||
|
|
||||||
|
free (str_array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compares if str has a specific path prefix. This differs
|
||||||
|
from a regular prefix in two ways. First of all there may
|
||||||
|
be multiple slashes separating the path elements, and
|
||||||
|
secondly, if a prefix is matched that has to be en entire
|
||||||
|
path element. For instance /a/prefix matches /a/prefix/foo/bar,
|
||||||
|
but not /a/prefixfoo/bar. */
|
||||||
|
bool
|
||||||
|
has_path_prefix (const char *str,
|
||||||
|
const char *prefix)
|
||||||
|
{
|
||||||
|
while (TRUE)
|
||||||
|
{
|
||||||
|
/* Skip consecutive slashes to reach next path
|
||||||
|
element */
|
||||||
|
while (*str == '/')
|
||||||
|
str++;
|
||||||
|
while (*prefix == '/')
|
||||||
|
prefix++;
|
||||||
|
|
||||||
|
/* No more prefix path elements? Done! */
|
||||||
|
if (*prefix == 0)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
/* Compare path element */
|
||||||
|
while (*prefix != 0 && *prefix != '/')
|
||||||
|
{
|
||||||
|
if (*str != *prefix)
|
||||||
|
return FALSE;
|
||||||
|
str++;
|
||||||
|
prefix++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Matched prefix path element,
|
||||||
|
must be entire str path element */
|
||||||
|
if (*str != '/' && *str != 0)
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
path_equal (const char *path1,
|
||||||
|
const char *path2)
|
||||||
|
{
|
||||||
|
while (TRUE)
|
||||||
|
{
|
||||||
|
/* Skip consecutive slashes to reach next path
|
||||||
|
element */
|
||||||
|
while (*path1 == '/')
|
||||||
|
path1++;
|
||||||
|
while (*path2 == '/')
|
||||||
|
path2++;
|
||||||
|
|
||||||
|
/* No more prefix path elements? Done! */
|
||||||
|
if (*path1 == 0 || *path2 == 0)
|
||||||
|
return *path1 == 0 && *path2 == 0;
|
||||||
|
|
||||||
|
/* Compare path element */
|
||||||
|
while (*path1 != 0 && *path1 != '/')
|
||||||
|
{
|
||||||
|
if (*path1 != *path2)
|
||||||
|
return FALSE;
|
||||||
|
path1++;
|
||||||
|
path2++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Matched path1 path element, must be entire path element */
|
||||||
|
if (*path2 != '/' && *path2 != 0)
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
has_prefix (const char *str,
|
||||||
|
const char *prefix)
|
||||||
|
{
|
||||||
|
return strncmp (str, prefix, strlen (prefix)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
xsetenv (const char *name, const char *value, int overwrite)
|
||||||
|
{
|
||||||
|
if (setenv (name, value, overwrite))
|
||||||
|
die ("setenv failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
xunsetenv (const char *name)
|
||||||
|
{
|
||||||
|
if (unsetenv (name))
|
||||||
|
die ("unsetenv failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
strconcat (const char *s1,
|
||||||
|
const char *s2)
|
||||||
|
{
|
||||||
|
size_t len = 0;
|
||||||
|
char *res;
|
||||||
|
|
||||||
|
if (s1)
|
||||||
|
len += strlen (s1);
|
||||||
|
if (s2)
|
||||||
|
len += strlen (s2);
|
||||||
|
|
||||||
|
res = xmalloc (len + 1);
|
||||||
|
*res = 0;
|
||||||
|
if (s1)
|
||||||
|
strcat (res, s1);
|
||||||
|
if (s2)
|
||||||
|
strcat (res, s2);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
strconcat3 (const char *s1,
|
||||||
|
const char *s2,
|
||||||
|
const char *s3)
|
||||||
|
{
|
||||||
|
size_t len = 0;
|
||||||
|
char *res;
|
||||||
|
|
||||||
|
if (s1)
|
||||||
|
len += strlen (s1);
|
||||||
|
if (s2)
|
||||||
|
len += strlen (s2);
|
||||||
|
if (s3)
|
||||||
|
len += strlen (s3);
|
||||||
|
|
||||||
|
res = xmalloc (len + 1);
|
||||||
|
*res = 0;
|
||||||
|
if (s1)
|
||||||
|
strcat (res, s1);
|
||||||
|
if (s2)
|
||||||
|
strcat (res, s2);
|
||||||
|
if (s3)
|
||||||
|
strcat (res, s3);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
xasprintf (const char *format,
|
||||||
|
...)
|
||||||
|
{
|
||||||
|
char *buffer = NULL;
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start (args, format);
|
||||||
|
if (vasprintf (&buffer, format, args) == -1)
|
||||||
|
die_oom ();
|
||||||
|
va_end (args);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
fdwalk (int proc_fd, int (*cb)(void *data,
|
||||||
|
int fd), void *data)
|
||||||
|
{
|
||||||
|
int open_max;
|
||||||
|
int fd;
|
||||||
|
int dfd;
|
||||||
|
int res = 0;
|
||||||
|
DIR *d;
|
||||||
|
|
||||||
|
dfd = openat (proc_fd, "self/fd", O_DIRECTORY | O_RDONLY | O_NONBLOCK | O_CLOEXEC | O_NOCTTY);
|
||||||
|
if (dfd == -1)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
if ((d = fdopendir (dfd)))
|
||||||
|
{
|
||||||
|
struct dirent *de;
|
||||||
|
|
||||||
|
while ((de = readdir (d)))
|
||||||
|
{
|
||||||
|
long l;
|
||||||
|
char *e = NULL;
|
||||||
|
|
||||||
|
if (de->d_name[0] == '.')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
l = strtol (de->d_name, &e, 10);
|
||||||
|
if (errno != 0 || !e || *e)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
fd = (int) l;
|
||||||
|
|
||||||
|
if ((long) fd != l)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (fd == dirfd (d))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ((res = cb (data, fd)) != 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir (d);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
open_max = sysconf (_SC_OPEN_MAX);
|
||||||
|
|
||||||
|
for (fd = 0; fd < open_max; fd++)
|
||||||
|
if ((res = cb (data, fd)) != 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sets errno on error (!= 0), ENOSPC on short write */
|
||||||
|
int
|
||||||
|
write_to_fd (int fd,
|
||||||
|
const char *content,
|
||||||
|
ssize_t len)
|
||||||
|
{
|
||||||
|
ssize_t res;
|
||||||
|
|
||||||
|
while (len > 0)
|
||||||
|
{
|
||||||
|
res = write (fd, content, len);
|
||||||
|
if (res < 0 && errno == EINTR)
|
||||||
|
continue;
|
||||||
|
if (res <= 0)
|
||||||
|
{
|
||||||
|
if (res == 0) /* Unexpected short write, should not happen when writing to a file */
|
||||||
|
errno = ENOSPC;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
len -= res;
|
||||||
|
content += res;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sets errno on error (!= 0), ENOSPC on short write */
|
||||||
|
int
|
||||||
|
write_file_at (int dirfd,
|
||||||
|
const char *path,
|
||||||
|
const char *content)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
bool res;
|
||||||
|
int errsv;
|
||||||
|
|
||||||
|
fd = openat (dirfd, path, O_RDWR | O_CLOEXEC, 0);
|
||||||
|
if (fd == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
res = 0;
|
||||||
|
if (content)
|
||||||
|
res = write_to_fd (fd, content, strlen (content));
|
||||||
|
|
||||||
|
errsv = errno;
|
||||||
|
close (fd);
|
||||||
|
errno = errsv;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sets errno on error (!= 0), ENOSPC on short write */
|
||||||
|
int
|
||||||
|
create_file (const char *path,
|
||||||
|
mode_t mode,
|
||||||
|
const char *content)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
int res;
|
||||||
|
int errsv;
|
||||||
|
|
||||||
|
fd = creat (path, mode);
|
||||||
|
if (fd == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
res = 0;
|
||||||
|
if (content)
|
||||||
|
res = write_to_fd (fd, content, strlen (content));
|
||||||
|
|
||||||
|
errsv = errno;
|
||||||
|
close (fd);
|
||||||
|
errno = errsv;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ensure_file (const char *path,
|
||||||
|
mode_t mode)
|
||||||
|
{
|
||||||
|
struct stat buf;
|
||||||
|
|
||||||
|
/* We check this ahead of time, otherwise
|
||||||
|
the create file will fail in the read-only
|
||||||
|
case with EROFS instead of EEXIST */
|
||||||
|
if (stat (path, &buf) == 0 &&
|
||||||
|
S_ISREG (buf.st_mode))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (create_file (path, mode, NULL) != 0 && errno != EEXIST)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define BUFSIZE 8192
|
||||||
|
/* Sets errno on error (!= 0), ENOSPC on short write */
|
||||||
|
int
|
||||||
|
copy_file_data (int sfd,
|
||||||
|
int dfd)
|
||||||
|
{
|
||||||
|
char buffer[BUFSIZE];
|
||||||
|
ssize_t bytes_read;
|
||||||
|
|
||||||
|
while (TRUE)
|
||||||
|
{
|
||||||
|
bytes_read = read (sfd, buffer, BUFSIZE);
|
||||||
|
if (bytes_read == -1)
|
||||||
|
{
|
||||||
|
if (errno == EINTR)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytes_read == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (write_to_fd (dfd, buffer, bytes_read) != 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sets errno on error (!= 0), ENOSPC on short write */
|
||||||
|
int
|
||||||
|
copy_file (const char *src_path,
|
||||||
|
const char *dst_path,
|
||||||
|
mode_t mode)
|
||||||
|
{
|
||||||
|
int sfd;
|
||||||
|
int dfd;
|
||||||
|
int res;
|
||||||
|
int errsv;
|
||||||
|
|
||||||
|
sfd = open (src_path, O_CLOEXEC | O_RDONLY);
|
||||||
|
if (sfd == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
dfd = creat (dst_path, mode);
|
||||||
|
if (dfd == -1)
|
||||||
|
{
|
||||||
|
errsv = errno;
|
||||||
|
close (sfd);
|
||||||
|
errno = errsv;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = copy_file_data (sfd, dfd);
|
||||||
|
|
||||||
|
errsv = errno;
|
||||||
|
close (sfd);
|
||||||
|
close (dfd);
|
||||||
|
errno = errsv;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sets errno on error (== NULL),
|
||||||
|
* Always ensures terminating zero */
|
||||||
|
char *
|
||||||
|
load_file_data (int fd,
|
||||||
|
size_t *size)
|
||||||
|
{
|
||||||
|
cleanup_free char *data = NULL;
|
||||||
|
ssize_t data_read;
|
||||||
|
ssize_t data_len;
|
||||||
|
ssize_t res;
|
||||||
|
int errsv;
|
||||||
|
|
||||||
|
data_read = 0;
|
||||||
|
data_len = 4080;
|
||||||
|
data = xmalloc (data_len);
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (data_len == data_read + 1)
|
||||||
|
{
|
||||||
|
data_len *= 2;
|
||||||
|
data = xrealloc (data, data_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
do
|
||||||
|
res = read (fd, data + data_read, data_len - data_read - 1);
|
||||||
|
while (res < 0 && errno == EINTR);
|
||||||
|
|
||||||
|
if (res < 0)
|
||||||
|
{
|
||||||
|
errsv = errno;
|
||||||
|
close (fd);
|
||||||
|
errno = errsv;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
data_read += res;
|
||||||
|
}
|
||||||
|
while (res > 0);
|
||||||
|
|
||||||
|
data[data_read] = 0;
|
||||||
|
|
||||||
|
if (size)
|
||||||
|
*size = (size_t) data_read;
|
||||||
|
|
||||||
|
return steal_pointer (&data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sets errno on error (== NULL),
|
||||||
|
* Always ensures terminating zero */
|
||||||
|
char *
|
||||||
|
load_file_at (int dirfd,
|
||||||
|
const char *path)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
char *data;
|
||||||
|
int errsv;
|
||||||
|
|
||||||
|
fd = openat (dirfd, path, O_CLOEXEC | O_RDONLY);
|
||||||
|
if (fd == -1)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
data = load_file_data (fd, NULL);
|
||||||
|
|
||||||
|
errsv = errno;
|
||||||
|
close (fd);
|
||||||
|
errno = errsv;
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sets errno on error (< 0) */
|
||||||
|
int
|
||||||
|
get_file_mode (const char *pathname)
|
||||||
|
{
|
||||||
|
struct stat buf;
|
||||||
|
|
||||||
|
if (stat (pathname, &buf) != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return buf.st_mode & S_IFMT;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ensure_dir (const char *path,
|
||||||
|
mode_t mode)
|
||||||
|
{
|
||||||
|
struct stat buf;
|
||||||
|
|
||||||
|
/* We check this ahead of time, otherwise
|
||||||
|
the mkdir call can fail in the read-only
|
||||||
|
case with EROFS instead of EEXIST on some
|
||||||
|
filesystems (such as NFS) */
|
||||||
|
if (stat (path, &buf) == 0)
|
||||||
|
{
|
||||||
|
if (!S_ISDIR (buf.st_mode))
|
||||||
|
{
|
||||||
|
errno = ENOTDIR;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mkdir (path, mode) == -1 && errno != EEXIST)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Sets errno on error (!= 0) */
|
||||||
|
int
|
||||||
|
mkdir_with_parents (const char *pathname,
|
||||||
|
int mode,
|
||||||
|
bool create_last)
|
||||||
|
{
|
||||||
|
cleanup_free char *fn = NULL;
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
if (pathname == NULL || *pathname == '\0')
|
||||||
|
{
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn = xstrdup (pathname);
|
||||||
|
|
||||||
|
p = fn;
|
||||||
|
while (*p == '/')
|
||||||
|
p++;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
while (*p && *p != '/')
|
||||||
|
p++;
|
||||||
|
|
||||||
|
if (!*p)
|
||||||
|
p = NULL;
|
||||||
|
else
|
||||||
|
*p = '\0';
|
||||||
|
|
||||||
|
if (!create_last && p == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (ensure_dir (fn, mode) != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (p)
|
||||||
|
{
|
||||||
|
*p++ = '/';
|
||||||
|
while (*p && *p == '/')
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (p);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send an ucred with current pid/uid/gid over a socket, it can be
|
||||||
|
read back with read_pid_from_socket(), and then the kernel has
|
||||||
|
translated it between namespaces as needed. */
|
||||||
|
void
|
||||||
|
send_pid_on_socket (int socket)
|
||||||
|
{
|
||||||
|
char buf[1] = { 0 };
|
||||||
|
struct msghdr msg = {};
|
||||||
|
struct iovec iov = { buf, sizeof (buf) };
|
||||||
|
const ssize_t control_len_snd = CMSG_SPACE(sizeof(struct ucred));
|
||||||
|
char control_buf_snd[control_len_snd];
|
||||||
|
struct cmsghdr *cmsg;
|
||||||
|
struct ucred *cred;
|
||||||
|
|
||||||
|
msg.msg_iov = &iov;
|
||||||
|
msg.msg_iovlen = 1;
|
||||||
|
msg.msg_control = control_buf_snd;
|
||||||
|
msg.msg_controllen = control_len_snd;
|
||||||
|
|
||||||
|
cmsg = CMSG_FIRSTHDR(&msg);
|
||||||
|
cmsg->cmsg_level = SOL_SOCKET;
|
||||||
|
cmsg->cmsg_type = SCM_CREDENTIALS;
|
||||||
|
cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
|
||||||
|
cred = (struct ucred *)CMSG_DATA(cmsg);
|
||||||
|
|
||||||
|
cred->pid = getpid ();
|
||||||
|
cred->uid = geteuid ();
|
||||||
|
cred->gid = getegid ();
|
||||||
|
|
||||||
|
if (sendmsg (socket, &msg, 0) < 0)
|
||||||
|
die_with_error ("Can't send pid");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
create_pid_socketpair (int sockets[2])
|
||||||
|
{
|
||||||
|
int enable = 1;
|
||||||
|
|
||||||
|
if (socketpair (AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sockets) != 0)
|
||||||
|
die_with_error ("Can't create intermediate pids socket");
|
||||||
|
|
||||||
|
if (setsockopt (sockets[0], SOL_SOCKET, SO_PASSCRED, &enable, sizeof (enable)) < 0)
|
||||||
|
die_with_error ("Can't set SO_PASSCRED");
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
read_pid_from_socket (int socket)
|
||||||
|
{
|
||||||
|
char recv_buf[1] = { 0 };
|
||||||
|
struct msghdr msg = {};
|
||||||
|
struct iovec iov = { recv_buf, sizeof (recv_buf) };
|
||||||
|
const ssize_t control_len_rcv = CMSG_SPACE(sizeof(struct ucred));
|
||||||
|
char control_buf_rcv[control_len_rcv];
|
||||||
|
struct cmsghdr* cmsg;
|
||||||
|
|
||||||
|
msg.msg_iov = &iov;
|
||||||
|
msg.msg_iovlen = 1;
|
||||||
|
msg.msg_control = control_buf_rcv;
|
||||||
|
msg.msg_controllen = control_len_rcv;
|
||||||
|
|
||||||
|
if (recvmsg (socket, &msg, 0) < 0)
|
||||||
|
die_with_error ("Cant read pid from socket");
|
||||||
|
|
||||||
|
if (msg.msg_controllen <= 0)
|
||||||
|
die ("Unexpected short read from pid socket");
|
||||||
|
|
||||||
|
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg))
|
||||||
|
{
|
||||||
|
const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0);
|
||||||
|
if (cmsg->cmsg_level == SOL_SOCKET &&
|
||||||
|
cmsg->cmsg_type == SCM_CREDENTIALS &&
|
||||||
|
payload_len == sizeof(struct ucred))
|
||||||
|
{
|
||||||
|
struct ucred *cred = (struct ucred *)CMSG_DATA(cmsg);
|
||||||
|
return cred->pid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
die ("No pid returned on socket");
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
raw_clone (unsigned long flags,
|
||||||
|
void *child_stack)
|
||||||
|
{
|
||||||
|
#if defined(__s390__) || defined(__CRIS__)
|
||||||
|
/* On s390 and cris the order of the first and second arguments
|
||||||
|
* of the raw clone() system call is reversed. */
|
||||||
|
return (int) syscall (__NR_clone, child_stack, flags);
|
||||||
|
#else
|
||||||
|
return (int) syscall (__NR_clone, flags, child_stack);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
pivot_root (const char * new_root, const char * put_old)
|
||||||
|
{
|
||||||
|
#ifdef __NR_pivot_root
|
||||||
|
return syscall (__NR_pivot_root, new_root, put_old);
|
||||||
|
#else
|
||||||
|
errno = ENOSYS;
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
label_mount (const char *opt, const char *mount_label)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_SELINUX
|
||||||
|
if (mount_label)
|
||||||
|
{
|
||||||
|
if (opt)
|
||||||
|
return xasprintf ("%s,context=\"%s\"", opt, mount_label);
|
||||||
|
else
|
||||||
|
return xasprintf ("context=\"%s\"", mount_label);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (opt)
|
||||||
|
return xstrdup (opt);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
label_create_file (const char *file_label)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_SELINUX
|
||||||
|
if (is_selinux_enabled () > 0 && file_label)
|
||||||
|
return setfscreatecon ((security_context_t) file_label);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
label_exec (const char *exec_label)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_SELINUX
|
||||||
|
if (is_selinux_enabled () > 0 && exec_label)
|
||||||
|
return setexeccon ((security_context_t) exec_label);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,173 @@
|
||||||
|
/* bubblewrap
|
||||||
|
* Copyright (C) 2016 Alexander Larsson
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
#define __debug__(x) printf x
|
||||||
|
#else
|
||||||
|
#define __debug__(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define UNUSED __attribute__((__unused__))
|
||||||
|
|
||||||
|
#define N_ELEMENTS(arr) (sizeof (arr) / sizeof ((arr)[0]))
|
||||||
|
|
||||||
|
#define TRUE 1
|
||||||
|
#define FALSE 0
|
||||||
|
typedef int bool;
|
||||||
|
|
||||||
|
#define PIPE_READ_END 0
|
||||||
|
#define PIPE_WRITE_END 1
|
||||||
|
|
||||||
|
void die_with_error (const char *format,
|
||||||
|
...) __attribute__((__noreturn__)) __attribute__((format (printf, 1, 2)));
|
||||||
|
void die (const char *format,
|
||||||
|
...) __attribute__((__noreturn__)) __attribute__((format (printf, 1, 2)));
|
||||||
|
void die_oom (void) __attribute__((__noreturn__));
|
||||||
|
void die_unless_label_valid (const char *label);
|
||||||
|
|
||||||
|
void fork_intermediate_child (void);
|
||||||
|
|
||||||
|
void *xmalloc (size_t size);
|
||||||
|
void *xcalloc (size_t size);
|
||||||
|
void *xrealloc (void *ptr,
|
||||||
|
size_t size);
|
||||||
|
char *xstrdup (const char *str);
|
||||||
|
void strfreev (char **str_array);
|
||||||
|
void xsetenv (const char *name,
|
||||||
|
const char *value,
|
||||||
|
int overwrite);
|
||||||
|
void xunsetenv (const char *name);
|
||||||
|
char *strconcat (const char *s1,
|
||||||
|
const char *s2);
|
||||||
|
char *strconcat3 (const char *s1,
|
||||||
|
const char *s2,
|
||||||
|
const char *s3);
|
||||||
|
char * xasprintf (const char *format,
|
||||||
|
...) __attribute__((format (printf, 1, 2)));
|
||||||
|
bool has_prefix (const char *str,
|
||||||
|
const char *prefix);
|
||||||
|
bool has_path_prefix (const char *str,
|
||||||
|
const char *prefix);
|
||||||
|
bool path_equal (const char *path1,
|
||||||
|
const char *path2);
|
||||||
|
int fdwalk (int proc_fd,
|
||||||
|
int (*cb)(void *data,
|
||||||
|
int fd),
|
||||||
|
void *data);
|
||||||
|
char *load_file_data (int fd,
|
||||||
|
size_t *size);
|
||||||
|
char *load_file_at (int dirfd,
|
||||||
|
const char *path);
|
||||||
|
int write_file_at (int dirfd,
|
||||||
|
const char *path,
|
||||||
|
const char *content);
|
||||||
|
int write_to_fd (int fd,
|
||||||
|
const char *content,
|
||||||
|
ssize_t len);
|
||||||
|
int copy_file_data (int sfd,
|
||||||
|
int dfd);
|
||||||
|
int copy_file (const char *src_path,
|
||||||
|
const char *dst_path,
|
||||||
|
mode_t mode);
|
||||||
|
int create_file (const char *path,
|
||||||
|
mode_t mode,
|
||||||
|
const char *content);
|
||||||
|
int ensure_file (const char *path,
|
||||||
|
mode_t mode);
|
||||||
|
int ensure_dir (const char *path,
|
||||||
|
mode_t mode);
|
||||||
|
int get_file_mode (const char *pathname);
|
||||||
|
int mkdir_with_parents (const char *pathname,
|
||||||
|
int mode,
|
||||||
|
bool create_last);
|
||||||
|
void create_pid_socketpair (int sockets[2]);
|
||||||
|
void send_pid_on_socket (int socket);
|
||||||
|
int read_pid_from_socket (int socket);
|
||||||
|
|
||||||
|
/* syscall wrappers */
|
||||||
|
int raw_clone (unsigned long flags,
|
||||||
|
void *child_stack);
|
||||||
|
int pivot_root (const char *new_root,
|
||||||
|
const char *put_old);
|
||||||
|
char *label_mount (const char *opt,
|
||||||
|
const char *mount_label);
|
||||||
|
int label_exec (const char *exec_label);
|
||||||
|
int label_create_file (const char *file_label);
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
cleanup_freep (void *p)
|
||||||
|
{
|
||||||
|
void **pp = (void **) p;
|
||||||
|
|
||||||
|
if (*pp)
|
||||||
|
free (*pp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
cleanup_strvp (void *p)
|
||||||
|
{
|
||||||
|
void **pp = (void **) p;
|
||||||
|
|
||||||
|
strfreev (*pp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
cleanup_fdp (int *fdp)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
assert (fdp);
|
||||||
|
|
||||||
|
fd = *fdp;
|
||||||
|
if (fd != -1)
|
||||||
|
(void) close (fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define cleanup_free __attribute__((cleanup (cleanup_freep)))
|
||||||
|
#define cleanup_fd __attribute__((cleanup (cleanup_fdp)))
|
||||||
|
#define cleanup_strv __attribute__((cleanup (cleanup_strvp)))
|
||||||
|
|
||||||
|
static inline void *
|
||||||
|
steal_pointer (void *pp)
|
||||||
|
{
|
||||||
|
void **ptr = (void **) pp;
|
||||||
|
void *ref;
|
||||||
|
|
||||||
|
ref = *ptr;
|
||||||
|
*ptr = NULL;
|
||||||
|
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* type safety */
|
||||||
|
#define steal_pointer(pp) \
|
||||||
|
(0 ? (*(pp)) : (steal_pointer) (pp))
|
Loading…
Reference in New Issue