Import Upstream version 2.6.1
This commit is contained in:
commit
6dab3e834c
|
@ -0,0 +1 @@
|
|||
2.6.1
|
|
@ -0,0 +1,504 @@
|
|||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin St, 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 Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
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 Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
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 and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
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 other code 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.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
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, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser 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 combine 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) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) 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.
|
||||
|
||||
d) 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.
|
||||
|
||||
e) 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 materials to be 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 with
|
||||
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 Lesser 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 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., 51 Franklin St, 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,181 @@
|
|||
SUBDIRS = src
|
||||
ACLOCAL_AMFLAGS = --install -I m4
|
||||
|
||||
lib_LTLIBRARIES = libcacard.la
|
||||
|
||||
libcacard_la_SOURCES = \
|
||||
src/cac.c \
|
||||
src/cac-aca.c \
|
||||
src/cac-aca.h \
|
||||
src/gp.c \
|
||||
src/gp.h \
|
||||
src/capcsc.h \
|
||||
src/card_7816.c \
|
||||
src/common.c \
|
||||
src/common.h \
|
||||
src/event.c \
|
||||
src/glib-compat.h \
|
||||
src/simpletlv.c \
|
||||
src/simpletlv.h \
|
||||
src/vcard.c \
|
||||
src/vcard_emul_nss.c \
|
||||
src/vcard_emul_type.c \
|
||||
src/vcardt.c \
|
||||
src/vcardt_internal.h \
|
||||
src/vreader.c \
|
||||
$(NULL)
|
||||
|
||||
if ENABLE_PCSC
|
||||
libcacard_la_SOURCES += src/capcsc.c
|
||||
endif
|
||||
|
||||
libcacard_includedir = $(includedir)/cacard
|
||||
libcacard_include_HEADERS = \
|
||||
src/cac.h \
|
||||
src/card_7816.h \
|
||||
src/card_7816t.h \
|
||||
src/eventt.h \
|
||||
src/libcacard.h \
|
||||
src/vcard.h \
|
||||
src/vcard_emul.h \
|
||||
src/vcard_emul_type.h \
|
||||
src/vcardt.h \
|
||||
src/vevent.h \
|
||||
src/vreader.h \
|
||||
src/vreadert.h \
|
||||
src/vscard_common.h \
|
||||
$(NULL)
|
||||
|
||||
libcacard_la_LIBADD = $(CACARD_LIBS) $(PCSC_LIBS)
|
||||
libcacard_la_LDFLAGS = \
|
||||
-export-symbols $(srcdir)/src/libcacard.syms \
|
||||
-no-undefined \
|
||||
-version-info 0:0:0 \
|
||||
$(AM_LDFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
if OS_WIN32
|
||||
libcacard_la_LIBADD += -lws2_32
|
||||
endif
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = libcacard.pc
|
||||
|
||||
include $(srcdir)/build-aux/glib-tap.mk
|
||||
|
||||
noinst_PROGRAMS += vscclient
|
||||
vscclient_SOURCES = src/vscclient.c
|
||||
vscclient_LDADD = libcacard.la $(GTHREAD_LIBS) $(PCSC_LIBS)
|
||||
vscclient_CFLAGS = $(AM_CPPFLAGS) $(GTHREAD_CFLAGS) $(PCSC_CFLAGS)
|
||||
|
||||
if OS_WIN32
|
||||
vscclient_CFLAGS += -D__USE_MINGW_ANSI_STDIO=1
|
||||
endif
|
||||
|
||||
tests/softhsm2.conf:
|
||||
$(AM_V_GEN)(cd tests/ && $(abs_srcdir)/tests/setup-softhsm2.sh)
|
||||
|
||||
clean-local:
|
||||
rm -rf tests/hwdb tests/tokens tests/softhsm2.conf
|
||||
|
||||
EXTRA_DIST += tests/setup-softhsm2.sh
|
||||
|
||||
AM_TESTS_ENVIRONMENT += \
|
||||
SOFTHSM2_CONF=tests/softhsm2.conf
|
||||
|
||||
test_programs = \
|
||||
tests/libcacard \
|
||||
tests/simpletlv \
|
||||
tests/hwtests \
|
||||
$(NULL)
|
||||
|
||||
tests_libcacard_SOURCES = \
|
||||
tests/common.c \
|
||||
tests/common.h \
|
||||
tests/libcacard.c \
|
||||
$(NULL)
|
||||
tests_libcacard_LDADD = \
|
||||
libcacard.la \
|
||||
src/common.lo \
|
||||
src/simpletlv.lo \
|
||||
$(NULL)
|
||||
|
||||
tests_simpletlv_LDADD = \
|
||||
libcacard.la \
|
||||
src/common.lo \
|
||||
src/simpletlv.lo \
|
||||
$(NULL)
|
||||
|
||||
tests_hwtests_SOURCES = \
|
||||
tests/common.c \
|
||||
tests/common.h \
|
||||
tests/hwtests.c \
|
||||
$(NULL)
|
||||
tests_hwtests_LDADD = \
|
||||
libcacard.la \
|
||||
src/common.lo \
|
||||
src/simpletlv.lo \
|
||||
$(NULL)
|
||||
tests_hwtests_DEPENDENCIES = tests/softhsm2.conf
|
||||
|
||||
@CODE_COVERAGE_RULES@
|
||||
|
||||
AM_CPPFLAGS = \
|
||||
-DG_LOG_DOMAIN=\"libcacard\" \
|
||||
-DLIBCACARD_COMPILATION \
|
||||
$(CACARD_CFLAGS) \
|
||||
$(CODE_COVERAGE_CFLAGS) \
|
||||
$(PCSC_CFLAGS) \
|
||||
$(WARN_CFLAGS) \
|
||||
-I$(srcdir)/src \
|
||||
$(NULL)
|
||||
AM_LDFLAGS = $(CODE_COVERAGE_LDFLAGS) $(WARN_LDFLAGS)
|
||||
|
||||
EXTRA_DIST += \
|
||||
NEWS \
|
||||
README.md \
|
||||
docs/libcacard.txt \
|
||||
src/libcacard.syms \
|
||||
build-aux/tap-driver.sh \
|
||||
build-aux/tap-test \
|
||||
tests/db/cert8.db \
|
||||
tests/db/key3.db \
|
||||
tests/db/secmod.db \
|
||||
tests/cert.cfg \
|
||||
$(NULL)
|
||||
|
||||
# aclocal will copy m4 files from pkg-config/autoconf-archives
|
||||
MAINTAINERCLEANFILES += $(srcdir)/m4/pkg.m4
|
||||
MAINTAINERCLEANFILES += $(srcdir)/m4/ax_*.m4
|
||||
|
||||
MAINTAINERCLEANFILES += \
|
||||
$(GITIGNORE_MAINTAINERCLEANFILES_TOPLEVEL) \
|
||||
$(GITIGNORE_MAINTAINERCLEANFILES_MAKEFILE_IN) \
|
||||
$(GITIGNORE_MAINTAINERCLEANFILES_M4_LIBTOOL)
|
||||
|
||||
# see git-version-gen
|
||||
dist-hook: gen-ChangeLog
|
||||
echo $(VERSION) > $(distdir)/.tarball-version
|
||||
|
||||
$(top_srcdir)/.version:
|
||||
echo $(VERSION) > $@-t && mv $@-t $@
|
||||
|
||||
BUILT_SOURCES += $(top_srcdir)/.version
|
||||
EXTRA_DIST += \
|
||||
$(top_srcdir)/.version \
|
||||
build-aux/git-version-gen \
|
||||
$(NULL)
|
||||
|
||||
gen-ChangeLog:
|
||||
@if test -d "$(srcdir)/.git"; then \
|
||||
echo Generating ChangeLog... ; \
|
||||
( cd "$(srcdir)" \
|
||||
&& $(top_srcdir)/build-aux/missing --run git log --stat ) > ChangeLog.tmp \
|
||||
&& mv -f ChangeLog.tmp $(top_distdir)/ChangeLog \
|
||||
|| ( rm -f ChangeLog.tmp; \
|
||||
echo Failed to generate ChangeLog >&2 ); \
|
||||
else \
|
||||
echo A git checkout is required to generate a ChangeLog >&2; \
|
||||
fi
|
||||
|
||||
-include $(top_srcdir)/git.mk
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,50 @@
|
|||
v2.6.1
|
||||
======
|
||||
|
||||
- various bug fixes (memory corruption issues which would cause crashes in
|
||||
spice-gtk)
|
||||
|
||||
v2.6.0
|
||||
======
|
||||
|
||||
- provides implementation of GSC-IS 2.1 (aka CAC version 2) to improve
|
||||
interoperatibility with guest software using the emulated or shared
|
||||
smart cards. The previously implemented CACv1 specification is no
|
||||
longer supported by any other application so the old code is gone
|
||||
and any application depending on this old standard will not work
|
||||
anymore.
|
||||
- vscclient is no longer installed, as it is not an end-user support
|
||||
solution
|
||||
- various bug & leak fixes
|
||||
|
||||
v2.5.3
|
||||
======
|
||||
|
||||
- fix memory leak in vcard_apdu_new
|
||||
- do not fail, if the caller didn't pick up response
|
||||
from previous call (to please opensc)
|
||||
- some scan-build errors fixed
|
||||
|
||||
|
||||
v2.5.2
|
||||
======
|
||||
|
||||
- remove libcacard.h usage warning (to silence qemu 2.5.0 build)
|
||||
|
||||
v2.5.1
|
||||
======
|
||||
|
||||
- fix mingw cross-compilation
|
||||
- add error checking to vcard_emul_options()
|
||||
- add VCARD_DIRECT implementation to provide direct communication with
|
||||
a smartcard using libpcsclite (--enable-pcsc)
|
||||
- add a top-level libcacard.h header compatible with c++
|
||||
- add test & code coverage and travis CI integration
|
||||
- bug fix: delete the reader entry after queueing an event, not before
|
||||
|
||||
v2.5.0
|
||||
======
|
||||
|
||||
This is the first standalone release after the split from qemu.git
|
||||
(the last release of libcacard was part of qemu 2.4). The hosting and
|
||||
maintainance of libcacard is now part of the Spice project.
|
|
@ -0,0 +1,28 @@
|
|||
# libcacard [![pipeline status](https://gitlab.freedesktop.org/spice/libcacard/badges/master/pipeline.svg)](https://gitlab.freedesktop.org/spice/libcacard/commits/master) [![coverage report](https://gitlab.freedesktop.org/spice/libcacard/badges/master/coverage.svg)](https://gitlab.freedesktop.org/spice/libcacard/commits/master)
|
||||
|
||||
CAC (Common Access Card) library
|
||||
|
||||
This library provides emulation of smart cards to a virtual card
|
||||
reader running in a guest virtual machine.
|
||||
|
||||
It implements DoD CAC standard with separate pki containers
|
||||
(compatible coolkey), using certificates read from NSS.
|
||||
|
||||
For more information and API documentation, read the docs/libcacard.txt file.
|
||||
|
||||
# History
|
||||
|
||||
This project used to be part of qemu until version 2.5. The history
|
||||
has been preserved and it inherits the tags and version.
|
||||
|
||||
# Authors
|
||||
|
||||
This project was originally developped by:
|
||||
|
||||
- Alon Levy <alevy@redhat.com>
|
||||
- Robert Relyea <rrelyea@redhat.com>
|
||||
|
||||
Extended to new GSC-IS 2.1 standard by:
|
||||
|
||||
- Jakub Jelen <jjelen@redhat.com>
|
||||
|
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=2016-01-11.22; # UTC
|
||||
|
||||
# Copyright (C) 1999-2017 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 <http://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 'write-file-hooks '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=2016-01-11.22; # UTC
|
||||
|
||||
# Copyright (C) 1999-2017 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 <http://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 'write-file-hooks '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,225 @@
|
|||
#!/bin/sh
|
||||
# Print a version string.
|
||||
scriptversion=2012-12-31.23; # UTC
|
||||
|
||||
# Copyright (C) 2007-2013 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 3 of the License, 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/.
|
||||
# It may be run two ways:
|
||||
# - from a git repository in which the "git describe" command below
|
||||
# produces useful output (thus requiring at least one signed tag)
|
||||
# - from a non-git-repo directory containing a .tarball-version file, which
|
||||
# presumes this script is invoked like "./git-version-gen .tarball-version".
|
||||
|
||||
# In order to use intra-version strings in your project, you will need two
|
||||
# separate generated version string files:
|
||||
#
|
||||
# .tarball-version - present only in a distribution tarball, and not in
|
||||
# a checked-out repository. Created with contents that were learned at
|
||||
# the last time autoconf was run, and used by git-version-gen. Must not
|
||||
# be present in either $(srcdir) or $(builddir) for git-version-gen to
|
||||
# give accurate answers during normal development with a checked out tree,
|
||||
# but must be present in a tarball when there is no version control system.
|
||||
# Therefore, it cannot be used in any dependencies. GNUmakefile has
|
||||
# hooks to force a reconfigure at distribution time to get the value
|
||||
# correct, without penalizing normal development with extra reconfigures.
|
||||
#
|
||||
# .version - present in a checked-out repository and in a distribution
|
||||
# tarball. Usable in dependencies, particularly for files that don't
|
||||
# want to depend on config.h but do want to track version changes.
|
||||
# Delete this file prior to any autoconf run where you want to rebuild
|
||||
# files to pick up a version string change; and leave it stale to
|
||||
# minimize rebuild time after unrelated changes to configure sources.
|
||||
#
|
||||
# As with any generated file in a VC'd directory, you should add
|
||||
# /.version to .gitignore, so that you don't accidentally commit it.
|
||||
# .tarball-version is never generated in a VC'd directory, so needn't
|
||||
# be listed there.
|
||||
#
|
||||
# Use the following line in your configure.ac, so that $(VERSION) will
|
||||
# automatically be up-to-date each time configure is run (and note that
|
||||
# since configure.ac no longer includes a version string, Makefile rules
|
||||
# should not depend on configure.ac for version updates).
|
||||
#
|
||||
# AC_INIT([GNU project],
|
||||
# m4_esyscmd([build-aux/git-version-gen .tarball-version]),
|
||||
# [bug-project@example])
|
||||
#
|
||||
# Then use the following lines in your Makefile.am, so that .version
|
||||
# will be present for dependencies, and so that .version and
|
||||
# .tarball-version will exist in distribution tarballs.
|
||||
#
|
||||
# EXTRA_DIST = $(top_srcdir)/.version
|
||||
# BUILT_SOURCES = $(top_srcdir)/.version
|
||||
# $(top_srcdir)/.version:
|
||||
# echo $(VERSION) > $@-t && mv $@-t $@
|
||||
# dist-hook:
|
||||
# echo $(VERSION) > $(distdir)/.tarball-version
|
||||
|
||||
|
||||
me=$0
|
||||
|
||||
version="git-version-gen $scriptversion
|
||||
|
||||
Copyright 2011 Free Software Foundation, Inc.
|
||||
There is NO warranty. You may redistribute this software
|
||||
under the terms of the GNU General Public License.
|
||||
For more information about these matters, see the files named COPYING."
|
||||
|
||||
usage="\
|
||||
Usage: $me [OPTION]... \$srcdir/.tarball-version [TAG-NORMALIZATION-SED-SCRIPT]
|
||||
Print a version string.
|
||||
|
||||
Options:
|
||||
|
||||
--prefix prefix of git tags (default 'v')
|
||||
--fallback fallback version to use if \"git --version\" fails
|
||||
|
||||
--help display this help and exit
|
||||
--version output version information and exit
|
||||
|
||||
Running without arguments will suffice in most cases."
|
||||
|
||||
prefix=v
|
||||
fallback=
|
||||
|
||||
while test $# -gt 0; do
|
||||
case $1 in
|
||||
--help) echo "$usage"; exit 0;;
|
||||
--version) echo "$version"; exit 0;;
|
||||
--prefix) shift; prefix="$1";;
|
||||
--fallback) shift; fallback="$1";;
|
||||
-*)
|
||||
echo "$0: Unknown option '$1'." >&2
|
||||
echo "$0: Try '--help' for more information." >&2
|
||||
exit 1;;
|
||||
*)
|
||||
if test "x$tarball_version_file" = x; then
|
||||
tarball_version_file="$1"
|
||||
elif test "x$tag_sed_script" = x; then
|
||||
tag_sed_script="$1"
|
||||
else
|
||||
echo "$0: extra non-option argument '$1'." >&2
|
||||
exit 1
|
||||
fi;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if test "x$tarball_version_file" = x; then
|
||||
echo "$usage"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
tag_sed_script="${tag_sed_script:-s/x/x/}"
|
||||
|
||||
nl='
|
||||
'
|
||||
|
||||
# Avoid meddling by environment variable of the same name.
|
||||
v=
|
||||
v_from_git=
|
||||
|
||||
# First see if there is a tarball-only version file.
|
||||
# then try "git describe", then default.
|
||||
if test -f $tarball_version_file
|
||||
then
|
||||
v=`cat $tarball_version_file` || v=
|
||||
case $v in
|
||||
*$nl*) v= ;; # reject multi-line output
|
||||
[0-9]*) ;;
|
||||
*) v= ;;
|
||||
esac
|
||||
test "x$v" = x \
|
||||
&& echo "$0: WARNING: $tarball_version_file is missing or damaged" 1>&2
|
||||
fi
|
||||
|
||||
if test "x$v" != x
|
||||
then
|
||||
: # use $v
|
||||
# Otherwise, if there is at least one git commit involving the working
|
||||
# directory, and "git describe" output looks sensible, use that to
|
||||
# derive a version string.
|
||||
elif test "`git log -1 --pretty=format:x . 2>&1`" = x \
|
||||
&& v=`git describe --abbrev=4 --match="$prefix*" HEAD 2>/dev/null \
|
||||
|| git describe --abbrev=4 HEAD 2>/dev/null` \
|
||||
&& v=`printf '%s\n' "$v" | sed "$tag_sed_script"` \
|
||||
&& case $v in
|
||||
$prefix[0-9]*) ;;
|
||||
*) (exit 1) ;;
|
||||
esac
|
||||
then
|
||||
# Is this a new git that lists number of commits since the last
|
||||
# tag or the previous older version that did not?
|
||||
# Newer: v6.10-77-g0f8faeb
|
||||
# Older: v6.10-g0f8faeb
|
||||
case $v in
|
||||
*-*-*) : git describe is okay three part flavor ;;
|
||||
*-*)
|
||||
: git describe is older two part flavor
|
||||
# Recreate the number of commits and rewrite such that the
|
||||
# result is the same as if we were using the newer version
|
||||
# of git describe.
|
||||
vtag=`echo "$v" | sed 's/-.*//'`
|
||||
commit_list=`git rev-list "$vtag"..HEAD 2>/dev/null` \
|
||||
|| { commit_list=failed;
|
||||
echo "$0: WARNING: git rev-list failed" 1>&2; }
|
||||
numcommits=`echo "$commit_list" | wc -l`
|
||||
v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`;
|
||||
test "$commit_list" = failed && v=UNKNOWN
|
||||
;;
|
||||
esac
|
||||
|
||||
# Change the first '-' to a '.', so version-comparing tools work properly.
|
||||
# Remove the "g" in git describe's output string, to save a byte.
|
||||
v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`;
|
||||
v_from_git=1
|
||||
elif test "x$fallback" = x || git --version >/dev/null 2>&1; then
|
||||
v=UNKNOWN
|
||||
else
|
||||
v=$fallback
|
||||
fi
|
||||
|
||||
v=`echo "$v" |sed "s/^$prefix//"`
|
||||
|
||||
# Test whether to append the "-dirty" suffix only if the version
|
||||
# string we're using came from git. I.e., skip the test if it's "UNKNOWN"
|
||||
# or if it came from .tarball-version.
|
||||
if test "x$v_from_git" != x; then
|
||||
# Don't declare a version "dirty" merely because a time stamp has changed.
|
||||
git update-index --refresh > /dev/null 2>&1
|
||||
|
||||
dirty=`exec 2>/dev/null;git diff-index --name-only HEAD` || dirty=
|
||||
case "$dirty" in
|
||||
'') ;;
|
||||
*) # Append the suffix only if there isn't one already.
|
||||
case $v in
|
||||
*-dirty) ;;
|
||||
*) v="$v-dirty" ;;
|
||||
esac ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Omit the trailing newline, so that m4_esyscmd can use the result directly.
|
||||
echo "$v" | tr -d "$nl"
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
|
@ -0,0 +1,150 @@
|
|||
# GLIB - Library of useful C routines
|
||||
|
||||
AM_TESTS_ENVIRONMENT= \
|
||||
G_TEST_SRCDIR="$(abs_srcdir)/tests" \
|
||||
G_TEST_BUILDDIR="$(abs_builddir)/tests" \
|
||||
G_DEBUG="gc-friendly cleanup" \
|
||||
MALLOC_CHECK_=2 \
|
||||
MALLOC_PERTURB_=$$(($${RANDOM:-256} % 256))
|
||||
LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/build-aux/tap-driver.sh
|
||||
LOG_COMPILER = $(top_srcdir)/build-aux/tap-test
|
||||
|
||||
NULL =
|
||||
|
||||
# initialize variables for unconditional += appending
|
||||
BUILT_SOURCES =
|
||||
BUILT_EXTRA_DIST =
|
||||
CLEANFILES = *.log *.trs
|
||||
DISTCLEANFILES =
|
||||
MAINTAINERCLEANFILES =
|
||||
EXTRA_DIST =
|
||||
TESTS =
|
||||
|
||||
installed_test_LTLIBRARIES =
|
||||
installed_test_PROGRAMS =
|
||||
installed_test_SCRIPTS =
|
||||
nobase_installed_test_DATA =
|
||||
|
||||
noinst_LTLIBRARIES =
|
||||
noinst_PROGRAMS =
|
||||
noinst_SCRIPTS =
|
||||
noinst_DATA =
|
||||
|
||||
check_LTLIBRARIES =
|
||||
check_PROGRAMS =
|
||||
check_SCRIPTS =
|
||||
check_DATA =
|
||||
|
||||
# We support a fairly large range of possible variables. It is expected that all types of files in a test suite
|
||||
# will belong in exactly one of the following variables.
|
||||
#
|
||||
# First, we support the usual automake suffixes, but in lowercase, with the customary meaning:
|
||||
#
|
||||
# test_programs, test_scripts, test_data, test_ltlibraries
|
||||
#
|
||||
# The above are used to list files that are involved in both uninstalled and installed testing. The
|
||||
# test_programs and test_scripts are taken to be actual testcases and will be run as part of the test suite.
|
||||
# Note that _data is always used with the nobase_ automake variable name to ensure that installed test data is
|
||||
# installed in the same way as it appears in the package layout.
|
||||
#
|
||||
# In order to mark a particular file as being only for one type of testing, use 'installed' or 'uninstalled',
|
||||
# like so:
|
||||
#
|
||||
# installed_test_programs, uninstalled_test_programs
|
||||
# installed_test_scripts, uninstalled_test_scripts
|
||||
# installed_test_data, uninstalled_test_data
|
||||
# installed_test_ltlibraries, uninstalled_test_ltlibraries
|
||||
#
|
||||
# Additionally, we support 'extra' infixes for programs and scripts. This is used for support programs/scripts
|
||||
# that should not themselves be run as testcases (but exist to be used from other testcases):
|
||||
#
|
||||
# test_extra_programs, installed_test_extra_programs, uninstalled_test_extra_programs
|
||||
# test_extra_scripts, installed_test_extra_scripts, uninstalled_test_extra_scripts
|
||||
#
|
||||
# Additionally, for _scripts and _data, we support the customary dist_ prefix so that the named script or data
|
||||
# file automatically end up in the tarball.
|
||||
#
|
||||
# dist_test_scripts, dist_test_data, dist_test_extra_scripts
|
||||
# dist_installed_test_scripts, dist_installed_test_data, dist_installed_test_extra_scripts
|
||||
# dist_uninstalled_test_scripts, dist_uninstalled_test_data, dist_uninstalled_test_extra_scripts
|
||||
#
|
||||
# Note that no file is automatically disted unless it appears in one of the dist_ variables. This follows the
|
||||
# standard automake convention of not disting programs scripts or data by default.
|
||||
#
|
||||
# test_programs, test_scripts, uninstalled_test_programs and uninstalled_test_scripts (as well as their disted
|
||||
# variants) will be run as part of the in-tree 'make check'. These are all assumed to be runnable under
|
||||
# gtester. That's a bit strange for scripts, but it's possible.
|
||||
|
||||
TESTS += $(test_programs) $(test_scripts) $(uninstalled_test_programs) $(uninstalled_test_scripts) \
|
||||
$(dist_test_scripts) $(dist_uninstalled_test_scripts)
|
||||
|
||||
# Note: build even the installed-only targets during 'make check' to ensure that they still work.
|
||||
# We need to do a bit of trickery here and manage disting via EXTRA_DIST instead of using dist_ prefixes to
|
||||
# prevent automake from mistreating gmake functions like $(wildcard ...) and $(addprefix ...) as if they were
|
||||
# filenames, including removing duplicate instances of the opening part before the space, eg. '$(addprefix'.
|
||||
all_test_programs = $(test_programs) $(uninstalled_test_programs) $(installed_test_programs) \
|
||||
$(test_extra_programs) $(uninstalled_test_extra_programs) $(installed_test_extra_programs)
|
||||
all_test_scripts = $(test_scripts) $(uninstalled_test_scripts) $(installed_test_scripts) \
|
||||
$(test_extra_scripts) $(uninstalled_test_extra_scripts) $(installed_test_extra_scripts)
|
||||
all_dist_test_scripts = $(dist_test_scripts) $(dist_uninstalled_test_scripts) $(dist_installed_test_scripts) \
|
||||
$(dist_test_extra_scripts) $(dist_uninstalled_test_extra_scripts) $(dist_installed_test_extra_scripts)
|
||||
all_test_scripts += $(all_dist_test_scripts)
|
||||
EXTRA_DIST += $(all_dist_test_scripts)
|
||||
all_test_data = $(test_data) $(uninstalled_test_data) $(installed_test_data)
|
||||
all_dist_test_data = $(dist_test_data) $(dist_uninstalled_test_data) $(dist_installed_test_data)
|
||||
all_test_data += $(all_dist_test_data)
|
||||
EXTRA_DIST += $(all_dist_test_data)
|
||||
all_test_ltlibs = $(test_ltlibraries) $(uninstalled_test_ltlibraries) $(installed_test_ltlibraries)
|
||||
|
||||
if ENABLE_ALWAYS_BUILD_TESTS
|
||||
noinst_LTLIBRARIES += $(all_test_ltlibs)
|
||||
noinst_PROGRAMS += $(all_test_programs)
|
||||
noinst_SCRIPTS += $(all_test_scripts)
|
||||
noinst_DATA += $(all_test_data)
|
||||
else
|
||||
check_LTLIBRARIES += $(all_test_ltlibs)
|
||||
check_PROGRAMS += $(all_test_programs)
|
||||
check_SCRIPTS += $(all_test_scripts)
|
||||
check_DATA += $(all_test_data)
|
||||
endif
|
||||
|
||||
if ENABLE_INSTALLED_TESTS
|
||||
installed_test_PROGRAMS += $(test_programs) $(installed_test_programs) \
|
||||
$(test_extra_programs) $(installed_test_extra_programs)
|
||||
installed_test_SCRIPTS += $(test_scripts) $(installed_test_scripts) \
|
||||
$(test_extra_scripts) $(test_installed_extra_scripts)
|
||||
installed_test_SCRIPTS += $(dist_test_scripts) $(dist_test_extra_scripts) \
|
||||
$(dist_installed_test_scripts) $(dist_installed_test_extra_scripts)
|
||||
nobase_installed_test_DATA += $(test_data) $(installed_test_data)
|
||||
nobase_installed_test_DATA += $(dist_test_data) $(dist_installed_test_data)
|
||||
installed_test_LTLIBRARIES += $(test_ltlibraries) $(installed_test_ltlibraries)
|
||||
installed_testcases = $(test_programs) $(installed_test_programs) \
|
||||
$(test_scripts) $(installed_test_scripts) \
|
||||
$(dist_test_scripts) $(dist_installed_test_scripts)
|
||||
|
||||
installed_test_meta_DATA = $(installed_testcases:=.test)
|
||||
|
||||
%.test: %$(EXEEXT) Makefile
|
||||
$(AM_V_GEN) (echo '[Test]' > $@.tmp; \
|
||||
echo 'Type=session' >> $@.tmp; \
|
||||
echo 'Exec=$(installed_testdir)/$(notdir $<) --tap' >> $@.tmp; \
|
||||
echo 'Output=TAP' >> $@.tmp; \
|
||||
mv $@.tmp $@)
|
||||
|
||||
CLEANFILES += $(installed_test_meta_DATA)
|
||||
|
||||
endif
|
||||
|
||||
VALGRIND_ARGS = \
|
||||
--leak-check=full \
|
||||
--child-silent-after-fork=yes \
|
||||
--suppressions=/usr/share/glib-2.0/valgrind/glib.supp \
|
||||
--suppressions=$(abs_top_srcdir)/build-aux/nss.supp \
|
||||
--num-callers=18 \
|
||||
$(NULL)
|
||||
|
||||
memcheck-local: $(all_test_programs)
|
||||
$(MAKE) check-am TESTS="$(all_test_programs)" \
|
||||
TESTS_ENVIRONMENT="G_DEBUG='gc-friendly cleanup'" \
|
||||
LOG_FLAGS="libtool --mode=execute valgrind $(VALGRIND_ARGS) --quiet --log-fd=7" \
|
||||
AM_TESTS_FD_REDIRECT="7>&2"
|
|
@ -0,0 +1,501 @@
|
|||
#!/bin/sh
|
||||
# install - install a program, script, or datafile
|
||||
|
||||
scriptversion=2016-01-11.22; # 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; won't work
|
||||
# if double slashes aren't ignored.
|
||||
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
|
||||
dst=$dstdir/`basename "$src"`
|
||||
dstdir_status=0
|
||||
else
|
||||
dstdir=`dirname "$dst"`
|
||||
test -d "$dstdir"
|
||||
dstdir_status=$?
|
||||
fi
|
||||
fi
|
||||
|
||||
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.
|
||||
;;
|
||||
*)
|
||||
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
|
||||
trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
|
||||
|
||||
if (umask $mkdir_umask &&
|
||||
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/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.
|
||||
ls_ld_tmpdir=`ls -ld "$tmpdir"`
|
||||
case $ls_ld_tmpdir in
|
||||
d????-?r-*) different_mode=700;;
|
||||
d????-?--*) different_mode=755;;
|
||||
*) false;;
|
||||
esac &&
|
||||
$mkdirprog -m$different_mode -p -- "$tmpdir" && {
|
||||
ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
|
||||
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
|
||||
}
|
||||
}
|
||||
then posix_mkdir=:
|
||||
fi
|
||||
rmdir "$tmpdir/d" "$tmpdir"
|
||||
else
|
||||
# Remove any dirs left behind by ancient mkdir implementations.
|
||||
rmdir ./$mkdir_mode ./-p ./-- 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=$dstdir/_inst.$$_
|
||||
rmtmp=$dstdir/_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 'write-file-hooks '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=2016-01-11.22; # UTC
|
||||
|
||||
# Copyright (C) 1996-2017 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 <http://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=http://www.perl.org/
|
||||
flex_URL=http://flex.sourceforge.net/
|
||||
gnu_software_URL=http://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 'write-file-hooks '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,652 @@
|
|||
#! /bin/sh
|
||||
# Copyright (C) 2011-2013 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 <http://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=2011-12-27.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:
|
||||
# <http://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" \
|
||||
'
|
||||
# FIXME: the usages of "cat >&3" below could be optimized when using
|
||||
# FIXME: 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
|
||||
}
|
||||
|
||||
# FIXME: this can certainly be improved ...
|
||||
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 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
|
@ -0,0 +1,5 @@
|
|||
#! /bin/sh
|
||||
|
||||
# run a GTest in tap mode. The test binary is passed as $1
|
||||
|
||||
$@ -k --tap
|
|
@ -0,0 +1,148 @@
|
|||
#!/bin/sh
|
||||
# test-driver - basic testsuite driver script.
|
||||
|
||||
scriptversion=2016-01-11.22; # UTC
|
||||
|
||||
# Copyright (C) 2011-2017 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 <http://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 'write-file-hooks '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,70 @@
|
|||
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||
|
||||
/* Define if debugging is enabled */
|
||||
#undef DEBUG
|
||||
|
||||
/* pcsc support */
|
||||
#undef ENABLE_PCSC
|
||||
|
||||
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||
#undef HAVE_DLFCN_H
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#undef HAVE_INTTYPES_H
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#undef HAVE_MEMORY_H
|
||||
|
||||
/* 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/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 sub-directory where libtool stores uninstalled libraries. */
|
||||
#undef LT_OBJDIR
|
||||
|
||||
/* Define if debugging is disabled */
|
||||
#undef NDEBUG
|
||||
|
||||
/* Name of package */
|
||||
#undef PACKAGE
|
||||
|
||||
/* 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
|
||||
|
||||
/* Version number of package */
|
||||
#undef VERSION
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,75 @@
|
|||
AC_INIT([libcacard], [m4_esyscmd(build-aux/git-version-gen .tarball-version)],
|
||||
[spice-devel@lists.freedesktop.org])
|
||||
AX_IS_RELEASE([git-directory])
|
||||
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_AUX_DIR([build-aux])
|
||||
|
||||
AM_INIT_AUTOMAKE([foreign subdir-objects no-dist-gzip dist-xz])
|
||||
AM_MAINTAINER_MODE([enable])
|
||||
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
|
||||
|
||||
AC_CANONICAL_HOST
|
||||
AC_MSG_CHECKING([for native Win32])
|
||||
case "$host_os" in
|
||||
*mingw*|*cygwin*)
|
||||
os_win32=yes
|
||||
;;
|
||||
*)
|
||||
os_win32=no
|
||||
;;
|
||||
esac
|
||||
AC_MSG_RESULT([$os_win32])
|
||||
AM_CONDITIONAL([OS_WIN32],[test "$os_win32" = "yes"])
|
||||
|
||||
AX_CHECK_ENABLE_DEBUG([yes],[DEBUG])
|
||||
|
||||
AC_PROG_CC
|
||||
AM_PROG_CC_C_O
|
||||
LT_INIT([disable-static win32-dll])
|
||||
PKG_PROG_PKG_CONFIG
|
||||
|
||||
AX_COMPILER_FLAGS([WARN_CFLAGS],[WARN_LDFLAGS])
|
||||
AX_CODE_COVERAGE()
|
||||
|
||||
PKG_CHECK_MODULES(CACARD, [glib-2.0 >= 2.22 nss >= 3.12.8])
|
||||
PKG_CHECK_MODULES(GTHREAD, [gthread-2.0])
|
||||
|
||||
dnl === --enable-pcsc ==========================================================
|
||||
|
||||
AC_ARG_ENABLE([pcsc],
|
||||
AS_HELP_STRING([--disable-pcsc],
|
||||
[do not build passthrough support]),,
|
||||
[enable_pcsc=auto])
|
||||
if test "x$enable_pcsc" != "xno"; then
|
||||
PKG_CHECK_MODULES(PCSC, [libpcsclite], [have_pcsc=yes], [have_pcsc=no])
|
||||
if test "x$have_pcsc" = "xno" -a "x$enable_pcsc" = "xyes"; then
|
||||
AC_MSG_ERROR([pcsc support explicitly requested, but libpcsclite couldn't be found])
|
||||
fi
|
||||
if test "x$have_pcsc" = "xyes"; then
|
||||
enable_pcsc=yes
|
||||
AC_DEFINE([ENABLE_PCSC], 1, [pcsc support])
|
||||
else
|
||||
enable_pcsc=no
|
||||
fi
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_PCSC, test "x$enable_pcsc" = "xyes")
|
||||
|
||||
GLIB_TESTS
|
||||
|
||||
AC_CONFIG_FILES([
|
||||
Makefile
|
||||
src/Makefile
|
||||
libcacard.pc
|
||||
])
|
||||
AC_OUTPUT
|
||||
|
||||
AC_MSG_NOTICE([
|
||||
|
||||
libcacard - $VERSION
|
||||
|
||||
• Prefix: $prefix
|
||||
• PCSC enabled: $enable_pcsc
|
||||
• Code coverage: $enable_code_coverage
|
||||
])
|
|
@ -0,0 +1,504 @@
|
|||
This file documents the CAC (Common Access Card) library in the libcacard
|
||||
subdirectory.
|
||||
|
||||
Virtual Smart Card Emulator
|
||||
|
||||
This emulator is designed to provide emulation of actual smart cards to a
|
||||
virtual card reader running in a guest virtual machine. The emulated smart
|
||||
cards can be representations of real smart cards, where the necessary functions
|
||||
such as signing, card removal/insertion, etc. are mapped to real, physical
|
||||
cards which are shared with the client machine the emulator is running on, or
|
||||
the cards could be pure software constructs.
|
||||
|
||||
The emulator is structured to allow multiple replaceable or additional pieces,
|
||||
so it can be easily modified for future requirements. The primary envisioned
|
||||
modifications are:
|
||||
|
||||
1) The socket connection to the virtual card reader (presumably a CCID reader,
|
||||
but other ISO-7816 compatible readers could be used). The code that handles
|
||||
this is in vscclient.c.
|
||||
|
||||
2) The virtual card low level emulation. This is currently supplied by using
|
||||
NSS. This emulation could be replaced by implementations based on other
|
||||
security libraries, including but not limitted to openssl+pkcs#11 library,
|
||||
raw pkcs#11, Microsoft CAPI, direct opensc calls, etc. The code that handles
|
||||
this is in vcard_emul_nss.c.
|
||||
|
||||
3) Emulation for new types of cards. The current implementation emulates the
|
||||
original DoD CAC standard with separate pki containers. This emulator lives in
|
||||
cac.c. More than one card type emulator could be included. Other cards could
|
||||
be emulated as well, including PIV, newer versions of CAC, PKCS #15, etc.
|
||||
|
||||
--------------------
|
||||
Replacing the Socket Based Virtual Reader Interface.
|
||||
|
||||
The current implementation contains a replaceable module vscclient.c. The
|
||||
current vscclient.c implements a sockets interface to the virtual ccid reader
|
||||
on the guest. CCID commands that are pertinent to emulation are passed
|
||||
across the socket, and their responses are passed back along that same socket.
|
||||
The protocol that vscclient uses is defined in vscard_common.h and connects
|
||||
to a qemu ccid usb device. Since this socket runs as a client, vscclient.c
|
||||
implements a program with a main entry. It also handles argument parsing for
|
||||
the emulator.
|
||||
|
||||
An application that wants to use the virtual reader can replace vscclient.c
|
||||
with its own implementation that connects to its own CCID reader. The calls
|
||||
that the CCID reader can call are:
|
||||
|
||||
VReaderList * vreader_get_reader_list();
|
||||
|
||||
This function returns a list of virtual readers. These readers may map to
|
||||
physical devices, or simulated devices depending on vcard the back end. Each
|
||||
reader in the list should represent a reader to the virtual machine. Virtual
|
||||
USB address mapping is left to the CCID reader front end. This call can be
|
||||
made any time to get an updated list. The returned list is a copy of the
|
||||
internal list that can be referenced by the caller without locking. This copy
|
||||
must be freed by the caller with vreader_list_delete when it is no longer
|
||||
needed.
|
||||
|
||||
VReaderListEntry *vreader_list_get_first(VReaderList *);
|
||||
|
||||
This function gets the first entry on the reader list. Along with
|
||||
vreader_list_get_next(), vreader_list_get_first() can be used to walk the
|
||||
reader list returned from vreader_get_reader_list(). VReaderListEntries are
|
||||
part of the list themselves and do not need to be freed separately from the
|
||||
list. If there are no entries on the list, it will return NULL.
|
||||
|
||||
VReaderListEntry *vreader_list_get_next(VReaderListEntry *);
|
||||
|
||||
This function gets the next entry in the list. If there are no more entries
|
||||
it will return NULL.
|
||||
|
||||
VReader * vreader_list_get_reader(VReaderListEntry *)
|
||||
|
||||
This function returns the reader stored in the reader List entry. Caller gets
|
||||
a new reference to a reader. The caller must free its reference when it is
|
||||
finished with vreader_free().
|
||||
|
||||
void vreader_free(VReader *reader);
|
||||
|
||||
This function frees a reference to a reader. Readers are reference counted
|
||||
and are automatically deleted when the last reference is freed.
|
||||
|
||||
void vreader_list_delete(VReaderList *list);
|
||||
|
||||
This function frees the list, all the elements on the list, and all the
|
||||
reader references held by the list.
|
||||
|
||||
VReaderStatus vreader_power_on(VReader *reader, char *atr, int *len);
|
||||
|
||||
This function simulates a card power on. A virtual card does not care about
|
||||
the actual voltage and other physical parameters, but it does care that the
|
||||
card is actually on or off. Cycling the card causes the card to reset. If
|
||||
the caller provides enough space, vreader_power_on will return the ATR of
|
||||
the virtual card. The amount of space provided in atr should be indicated
|
||||
in *len. The function modifies *len to be the actual length of of the
|
||||
returned ATR.
|
||||
|
||||
VReaderStatus vreader_power_off(VReader *reader);
|
||||
|
||||
This function simulates a power off of a virtual card.
|
||||
|
||||
VReaderStatus vreader_xfer_bytes(VReader *reader, unsigne char *send_buf,
|
||||
int send_buf_len,
|
||||
unsigned char *receive_buf,
|
||||
int receive_buf_len);
|
||||
|
||||
This function sends a raw apdu to a card and returns the card's response.
|
||||
The CCID front end should return the response back. Most of the emulation
|
||||
is driven from these APDUs.
|
||||
|
||||
VReaderStatus vreader_card_is_present(VReader *reader);
|
||||
|
||||
This function returns whether or not the reader has a card inserted. The
|
||||
vreader_power_on, vreader_power_off, and vreader_xfer_bytes will return
|
||||
VREADER_NO_CARD.
|
||||
|
||||
const char *vreader_get_name(VReader *reader);
|
||||
|
||||
This function returns the name of the reader. The name comes from the card
|
||||
emulator level and is usually related to the name of the physical reader.
|
||||
|
||||
VReaderID vreader_get_id(VReader *reader);
|
||||
|
||||
This function returns the id of a reader. All readers start out with an id
|
||||
of -1. The application can set the id with vreader_set_id.
|
||||
|
||||
VReaderStatus vreader_get_id(VReader *reader, VReaderID id);
|
||||
|
||||
This function sets the reader id. The application is responsible for making
|
||||
sure that the id is unique for all readers it is actively using.
|
||||
|
||||
VReader *vreader_find_reader_by_id(VReaderID id);
|
||||
|
||||
This function returns the reader which matches the id. If two readers match,
|
||||
only one is returned. The function returns NULL if the id is -1.
|
||||
|
||||
Event *vevent_wait_next_vevent();
|
||||
|
||||
This function blocks waiting for reader and card insertion events. There
|
||||
will be one event for each card insertion, each card removal, each reader
|
||||
insertion and each reader removal. At start up, events are created for all
|
||||
the initial readers found, as well as all the cards that are inserted.
|
||||
|
||||
Event *vevent_get_next_vevent();
|
||||
|
||||
This function returns a pending event if it exists, otherwise it returns
|
||||
NULL. It does not block.
|
||||
|
||||
----------------
|
||||
Card Type Emulator: Adding a New Virtual Card Type
|
||||
|
||||
The ISO 7816 card spec describes 2 types of cards:
|
||||
1) File system cards, where the smartcard is managed by reading and writing
|
||||
data to files in a file system. There is currently only boiler plate
|
||||
implemented for file system cards.
|
||||
2) VM cards, where the card has loadable applets which perform the card
|
||||
functions. The current implementation supports VM cards.
|
||||
|
||||
In the case of VM cards, the difference between various types of cards is
|
||||
really what applets have been installed in that card. This structure is
|
||||
mirrored in card type emulators. The 7816 emulator already handles the basic
|
||||
ISO 7186 commands. Card type emulators simply need to add the virtual applets
|
||||
which emulate the real card applets. Card type emulators have exactly one
|
||||
public entry point:
|
||||
|
||||
VCARDStatus xxx_card_init(VCard *card, const char *flags,
|
||||
const unsigned char *cert[],
|
||||
int cert_len[],
|
||||
VCardKey *key[],
|
||||
int cert_count);
|
||||
|
||||
The parameters for this are:
|
||||
card - the virtual card structure which will represent this card.
|
||||
flags - option flags that may be specific to this card type.
|
||||
cert - array of binary certificates.
|
||||
cert_len - array of lengths of each of the certificates specified in cert.
|
||||
key - array of opaque key structures representing the private keys on
|
||||
the card.
|
||||
cert_count - number of entries in cert, cert_len, and key arrays.
|
||||
|
||||
Any cert, cert_len, or key with the same index are matching sets. That is
|
||||
cert[0] is cert_len[0] long and has the corresponding private key of key[0].
|
||||
|
||||
The card type emulator is expected to own the VCardKeys, but it should copy
|
||||
any raw cert data it wants to save. It can create new applets and add them to
|
||||
the card using the following functions:
|
||||
|
||||
VCardApplet *vcard_new_applet(VCardProcessAPDU apdu_func,
|
||||
VCardResetApplet reset_func,
|
||||
const unsigned char *aid,
|
||||
int aid_len);
|
||||
|
||||
This function creates a new applet. Applet structures store the following
|
||||
information:
|
||||
1) the AID of the applet (set by aid and aid_len).
|
||||
2) a function to handle APDUs for this applet. (set by apdu_func, more on
|
||||
this below).
|
||||
3) a function to reset the applet state when the applet is selected.
|
||||
(set by reset_func, more on this below).
|
||||
3) applet private data, a data pointer used by the card type emulator to
|
||||
store any data or state it needs to complete requests. (set by a
|
||||
separate call).
|
||||
4) applet private data free, a function used to free the applet private
|
||||
data when the applet itself is destroyed.
|
||||
The created applet can be added to the card with vcard_add_applet below.
|
||||
|
||||
void vcard_set_applet_private(VCardApplet *applet,
|
||||
VCardAppletPrivate *private,
|
||||
VCardAppletPrivateFree private_free);
|
||||
This function sets the private data and the corresponding free function.
|
||||
VCardAppletPrivate is an opaque data structure to the rest of the emulator.
|
||||
The card type emulator can define it any way it wants by defining
|
||||
struct VCardAppletPrivateStruct {};. If there is already a private data
|
||||
structure on the applet, the old one is freed before the new one is set up.
|
||||
passing two NULL clear any existing private data.
|
||||
|
||||
VCardStatus vcard_add_applet(VCard *card, VCardApplet *applet);
|
||||
|
||||
Add an applet onto the list of applets attached to the card. Once an applet
|
||||
has been added, it can be selected by its AID, and then commands will be
|
||||
routed to it VCardProcessAPDU function. This function adopts the applet that
|
||||
is passed into it. Note: 2 applets with the same AID should not be added to
|
||||
the same card. It is permissible to add more than one applet. Multiple applets
|
||||
may have the same VCardPRocessAPDU entry point.
|
||||
|
||||
The certs and keys should be attached to private data associated with one or
|
||||
more appropriate applets for that card. Control will come to the card type
|
||||
emulators once one of its applets are selected through the VCardProcessAPDU
|
||||
function it specified when it created the applet.
|
||||
|
||||
The signature of VCardResetApplet is:
|
||||
VCardStatus (*VCardResetApplet) (VCard *card, int channel);
|
||||
This function will reset the any internal applet state that needs to be
|
||||
cleared after a select applet call. It should return VCARD_DONE;
|
||||
|
||||
The signature of VCardProcessAPDU is:
|
||||
VCardStatus (*VCardProcessAPDU)(VCard *card, VCardAPDU *apdu,
|
||||
VCardResponse **response);
|
||||
This function examines the APDU and determines whether it should process
|
||||
the apdu directly, reject the apdu as invalid, or pass the apdu on to
|
||||
the basic 7816 emulator for processing.
|
||||
If the 7816 emulator should process the apdu, then the VCardProcessAPDU
|
||||
should return VCARD_NEXT.
|
||||
If there is an error, then VCardProcessAPDU should return an error
|
||||
response using vcard_make_response and the appropriate 7816 error code
|
||||
(see card_7816t.h) or vcard_make_response with a card type specific error
|
||||
code. It should then return VCARD_DONE.
|
||||
If the apdu can be processed correctly, VCardProcessAPDU should do so,
|
||||
set the response value appropriately for that APDU, and return VCARD_DONE.
|
||||
VCardProcessAPDU should always set the response if it returns VCARD_DONE.
|
||||
It should always either return VCARD_DONE or VCARD_NEXT.
|
||||
|
||||
Parsing the APDU --
|
||||
|
||||
Prior to processing calling the card type emulator's VCardProcessAPDU function, the emulator has already decoded the APDU header and set several fields:
|
||||
|
||||
apdu->a_data - The raw apdu data bytes.
|
||||
apdu->a_len - The len of the raw apdu data.
|
||||
apdu->a_body - The start of any post header parameter data.
|
||||
apdu->a_Lc - The parameter length value.
|
||||
apdu->a_Le - The expected length of any returned data.
|
||||
apdu->a_cla - The raw apdu class.
|
||||
apdu->a_channel - The channel (decoded from the class).
|
||||
apdu->a_secure_messaging_type - The decoded secure messaging type
|
||||
(from class).
|
||||
apdu->a_type - The decode class type.
|
||||
apdu->a_gen_type - the generic class type (7816, PROPRIETARY, RFU, PTS).
|
||||
apdu->a_ins - The instruction byte.
|
||||
apdu->a_p1 - Parameter 1.
|
||||
apdu->a_p2 - Parameter 2.
|
||||
|
||||
Creating a Response --
|
||||
|
||||
The expected result of any APDU call is a response. The card type emulator must
|
||||
set *response with an appropriate VCardResponse value if it returns VCARD_DONE.
|
||||
Responses could be as simple as returning a 2 byte status word response, to as
|
||||
complex as returning a block of data along with a 2 byte response. Which is
|
||||
returned will depend on the semantics of the APDU. The following functions will
|
||||
create card responses.
|
||||
|
||||
VCardResponse *vcard_make_response(VCard7816Status status);
|
||||
|
||||
This is the most basic function to get a response. This function will
|
||||
return a response the consists solely one 2 byte status code. If that status
|
||||
code is defined in card_7816t.h, then this function is guaranteed to
|
||||
return a response with that status. If a cart type specific status code
|
||||
is passed and vcard_make_response fails to allocate the appropriate memory
|
||||
for that response, then vcard_make_response will return a VCardResponse
|
||||
of VCARD7816_STATUS_EXC_ERROR_MEMORY. In any case, this function is
|
||||
guaranteed to return a valid VCardResponse.
|
||||
|
||||
VCardResponse *vcard_response_new(unsigned char *buf, int len,
|
||||
VCard7816Status status);
|
||||
|
||||
This function is similar to vcard_make_response except it includes some
|
||||
returned data with the response. It could also fail to allocate enough
|
||||
memory, in which case it will return NULL.
|
||||
|
||||
VCardResponse *vcard_response_new_status_bytes(unsigned char sw1,
|
||||
unsigned char sw2);
|
||||
|
||||
Sometimes in 7816 the response bytes are treated as two separate bytes with
|
||||
split meanings. This function allows you to create a response based on
|
||||
two separate bytes. This function could fail, in which case it will return
|
||||
NULL.
|
||||
|
||||
VCardResponse *vcard_response_new_bytes(unsigned char *buf, int len,
|
||||
unsigned char sw1,
|
||||
unsigned char sw2);
|
||||
|
||||
This function is the same as vcard_response_new except you may specify
|
||||
the status as two separate bytes like vcard_response_new_status_bytes.
|
||||
|
||||
|
||||
Implementing functionality ---
|
||||
|
||||
The following helper functions access information about the current card
|
||||
and applet.
|
||||
|
||||
VCARDAppletPrivate *vcard_get_current_applet_private(VCard *card,
|
||||
int channel);
|
||||
|
||||
This function returns any private data set by the card type emulator on
|
||||
the currently selected applet. The card type emulator keeps track of the
|
||||
current applet state in this data structure. Any certs and keys associated
|
||||
with a particular applet is also stored here.
|
||||
|
||||
int vcard_emul_get_login_count(VCard *card);
|
||||
|
||||
This function returns the number of remaining login attempts for this
|
||||
card. If the card emulator does not know, or the card does not have a
|
||||
way of giving this information, this function returns -1.
|
||||
|
||||
|
||||
VCard7816Status vcard_emul_login(VCard *card, unsigned char *pin,
|
||||
int pin_len);
|
||||
|
||||
This function logs into the card and returns the standard 7816 status
|
||||
word depending on the success or failure of the call.
|
||||
|
||||
void vcard_emul_delete_key(VCardKey *key);
|
||||
|
||||
This function frees the VCardKey passed in to xxxx_card_init. The card
|
||||
type emulator is responsible for freeing this key when it no longer needs
|
||||
it.
|
||||
|
||||
VCard7816Status vcard_emul_rsa_op(VCard *card, VCardKey *key,
|
||||
unsigned char *buffer,
|
||||
int buffer_size);
|
||||
|
||||
This function does a raw rsa op on the buffer with the given key.
|
||||
|
||||
int vcard_emul_rsa_bits(VCardKey *key);
|
||||
|
||||
This function returns the size of RSA key in bits.
|
||||
|
||||
unsigned char *vcard_emul_read_object(VCard *card,
|
||||
const unsigned char *label,
|
||||
unsigned int *ret_len);
|
||||
|
||||
This function reads generic data from underlying smart card by the label,
|
||||
if avaialble.
|
||||
|
||||
|
||||
The sample card type emulator is found in cac.c. It implements the cac specific
|
||||
applets. Only those applets needed by the coolkey pkcs#11 driver on the guest
|
||||
have been implemented. To support the full range CAC middleware, a complete CAC
|
||||
card according to the CAC specs should be implemented here.
|
||||
|
||||
------------------------------
|
||||
Virtual Card Emulator
|
||||
|
||||
This code accesses both real smart cards and simulated smart cards through
|
||||
services provided on the client. The current implementation uses NSS, which
|
||||
already knows how to talk to various PKCS #11 modules on the client, and is
|
||||
portable to most operating systems. A particular emulator can have only one
|
||||
virtual card implementation at a time.
|
||||
|
||||
The virtual card emulator consists of a series of virtual card services. In
|
||||
addition to the services describe above (services starting with
|
||||
vcard_emul_xxxx), the virtual card emulator also provides the following
|
||||
functions:
|
||||
|
||||
VCardEmulError vcard_emul_init(cont VCardEmulOptions *options);
|
||||
|
||||
The options structure is built by another function in the virtual card
|
||||
interface where a string of virtual card emulator specific strings are
|
||||
mapped to the options. The actual structure is defined by the virtual card
|
||||
emulator and is used to determine the configuration of soft cards, or to
|
||||
determine which physical cards to present to the guest.
|
||||
|
||||
The vcard_emul_init function will build up sets of readers, create any
|
||||
threads that are needed to watch for changes in the reader state. If readers
|
||||
have cards present in them, they are also initialized.
|
||||
|
||||
Readers are created with the function.
|
||||
|
||||
VReader *vreader_new(VReaderEmul *reader_emul,
|
||||
VReaderEmulFree reader_emul_free);
|
||||
|
||||
The freeFunc is used to free the VReaderEmul * when the reader is
|
||||
destroyed. The VReaderEmul structure is an opaque structure to the
|
||||
rest of the code, but defined by the virtual card emulator, which can
|
||||
use it to store any reader specific state.
|
||||
|
||||
Once the reader has been created, it can be added to the front end with the
|
||||
call:
|
||||
|
||||
VReaderStatus vreader_add_reader(VReader *reader);
|
||||
|
||||
This function will automatically generate the appropriate new reader
|
||||
events and add the reader to the list.
|
||||
|
||||
To create a new card, the virtual card emulator will call a similar
|
||||
function.
|
||||
|
||||
VCard *vcard_new(VCardEmul *card_emul,
|
||||
VCardEmulFree card_emul_free);
|
||||
|
||||
Like vreader_new, this function takes a virtual card emulator specific
|
||||
structure which it uses to keep track of the card state.
|
||||
|
||||
Once the card is created, it is attached to a card type emulator with the
|
||||
following function:
|
||||
|
||||
VCardStatus vcard_init(VCard *vcard, VCardEmulType type,
|
||||
const char *flags,
|
||||
unsigned char *const *certs,
|
||||
int *cert_len,
|
||||
VCardKey *key[],
|
||||
int cert_count);
|
||||
|
||||
The vcard is the value returned from vcard_new. The type is the
|
||||
card type emulator that this card should presented to the guest as.
|
||||
The flags are card type emulator specific options. The certs,
|
||||
cert_len, and keys are all arrays of length cert_count. These are
|
||||
the same of the parameters xxxx_card_init() accepts.
|
||||
|
||||
Finally the card is associated with its reader by the call:
|
||||
|
||||
VReaderStatus vreader_insert_card(VReader *vreader, VCard *vcard);
|
||||
|
||||
This function, like vreader_add_reader, will take care of any event
|
||||
notification for the card insert.
|
||||
|
||||
|
||||
VCardEmulError vcard_emul_force_card_remove(VReader *vreader);
|
||||
|
||||
Force a card that is present to appear to be removed to the guest, even if
|
||||
that card is a physical card and is present.
|
||||
|
||||
|
||||
VCardEmulError vcard_emul_force_card_insert(VReader *reader);
|
||||
|
||||
Force a card that has been removed by vcard_emul_force_card_remove to be
|
||||
reinserted from the point of view of the guest. This will only work if the
|
||||
card is physically present (which is always true fro a soft card).
|
||||
|
||||
void vcard_emul_get_atr(Vcard *card, unsigned char *atr, int *atr_len);
|
||||
|
||||
Return the virtual ATR for the card. By convention this should be the value
|
||||
VCARD_ATR_PREFIX(size) followed by several ascii bytes related to this
|
||||
particular emulator. For instance the NSS emulator returns
|
||||
{VCARD_ATR_PREFIX(3), 'N', 'S', 'S' }. Do not return more data then *atr_len;
|
||||
|
||||
void vcard_emul_reset(VCard *card, VCardPower power)
|
||||
|
||||
Set the state of 'card' to the current power level and reset its internal
|
||||
state (logout, etc).
|
||||
|
||||
-------------------------------------------------------
|
||||
List of files and their function:
|
||||
docs/libcacard.txt - This file
|
||||
src/card_7816.c - emulate basic 7816 functionality. Parse APDUs.
|
||||
src/card_7816.h - apdu and response services definitions.
|
||||
src/card_7816t.h - 7816 specific structures, types and definitions.
|
||||
src/event.c - event handling code.
|
||||
src/event.h - event handling services definitions.
|
||||
src/eventt.h - event handling structures and types
|
||||
src/vcard.c - handle common virtual card services like creation, destruction,
|
||||
and applet management.
|
||||
src/vcard.h - common virtual card services function definitions.
|
||||
src/vcardt.h - comon virtual card types
|
||||
src/vreader.c - common virtual reader services.
|
||||
src/vreader.h - common virtual reader services definitions.
|
||||
src/vreadert.h - comon virtual reader types.
|
||||
src/vcard_emul_type.c - manage the card type emulators.
|
||||
src/vcard_emul_type.h - definitions for card type emulators.
|
||||
src/cac.c - card type emulator for CAC cards
|
||||
src/cac-aca.c - implementation of CAC's ACA applet related buffers
|
||||
src/gp.c - basic Global Platform card manager emulation
|
||||
src/vcard_emul.h - virtual card emulator service definitions.
|
||||
src/vcard_emul_nss.c - virtual card emulator implementation for nss.
|
||||
src/vscclient.c - socket connection to guest qemu usb driver.
|
||||
src/vscard_common.h - common header with the guest qemu usb driver.
|
||||
src/mutex.h - header file for machine independent mutexes.
|
||||
src/common.c - Utilities functions
|
||||
src/common.h - header file utilities functions
|
||||
src/simpletlv.c - Simple TLV encoding functions
|
||||
src/simpletlv.h - header file for Simple TLV encoding helpers
|
||||
tests/libcacard.c - Test for the whole smart card emulation
|
||||
tests/simpletlv.c - Unit tests for SimpleTLV encoding and decoding functions
|
||||
tests/hwtests.c - Tests intended to be ran against real card if available
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: cacard
|
||||
Description: CA Card library
|
||||
Version: @PACKAGE_VERSION@
|
||||
Requires: glib-2.0
|
||||
Requires.private: nss
|
||||
Libs: -L${libdir} -lcacard
|
||||
Cflags: -I${includedir}/cacard
|
|
@ -0,0 +1,67 @@
|
|||
# ============================================================================
|
||||
# https://www.gnu.org/software/autoconf-archive/ax_append_compile_flags.html
|
||||
# ============================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_APPEND_COMPILE_FLAGS([FLAG1 FLAG2 ...], [FLAGS-VARIABLE], [EXTRA-FLAGS], [INPUT])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# For every FLAG1, FLAG2 it is checked whether the compiler works with the
|
||||
# flag. If it does, the flag is added FLAGS-VARIABLE
|
||||
#
|
||||
# If FLAGS-VARIABLE is not specified, the current language's flags (e.g.
|
||||
# CFLAGS) is used. During the check the flag is always added to the
|
||||
# current language's flags.
|
||||
#
|
||||
# If EXTRA-FLAGS is defined, it is added to the current language's default
|
||||
# flags (e.g. CFLAGS) when the check is done. The check is thus made with
|
||||
# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
|
||||
# force the compiler to issue an error when a bad flag is given.
|
||||
#
|
||||
# INPUT gives an alternative input source to AC_COMPILE_IFELSE.
|
||||
#
|
||||
# NOTE: This macro depends on the AX_APPEND_FLAG and
|
||||
# AX_CHECK_COMPILE_FLAG. Please keep this macro in sync with
|
||||
# AX_APPEND_LINK_FLAGS.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.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 3 of the License, 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, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 6
|
||||
|
||||
AC_DEFUN([AX_APPEND_COMPILE_FLAGS],
|
||||
[AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG])
|
||||
AX_REQUIRE_DEFINED([AX_APPEND_FLAG])
|
||||
for flag in $1; do
|
||||
AX_CHECK_COMPILE_FLAG([$flag], [AX_APPEND_FLAG([$flag], [$2])], [], [$3], [$4])
|
||||
done
|
||||
])dnl AX_APPEND_COMPILE_FLAGS
|
|
@ -0,0 +1,71 @@
|
|||
# ===========================================================================
|
||||
# https://www.gnu.org/software/autoconf-archive/ax_append_flag.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_APPEND_FLAG(FLAG, [FLAGS-VARIABLE])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# FLAG is appended to the FLAGS-VARIABLE shell variable, with a space
|
||||
# added in between.
|
||||
#
|
||||
# If FLAGS-VARIABLE is not specified, the current language's flags (e.g.
|
||||
# CFLAGS) is used. FLAGS-VARIABLE is not changed if it already contains
|
||||
# FLAG. If FLAGS-VARIABLE is unset in the shell, it is set to exactly
|
||||
# FLAG.
|
||||
#
|
||||
# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
|
||||
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.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 3 of the License, 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, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 7
|
||||
|
||||
AC_DEFUN([AX_APPEND_FLAG],
|
||||
[dnl
|
||||
AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_SET_IF
|
||||
AS_VAR_PUSHDEF([FLAGS], [m4_default($2,_AC_LANG_PREFIX[FLAGS])])
|
||||
AS_VAR_SET_IF(FLAGS,[
|
||||
AS_CASE([" AS_VAR_GET(FLAGS) "],
|
||||
[*" $1 "*], [AC_RUN_LOG([: FLAGS already contains $1])],
|
||||
[
|
||||
AS_VAR_APPEND(FLAGS,[" $1"])
|
||||
AC_RUN_LOG([: FLAGS="$FLAGS"])
|
||||
])
|
||||
],
|
||||
[
|
||||
AS_VAR_SET(FLAGS,[$1])
|
||||
AC_RUN_LOG([: FLAGS="$FLAGS"])
|
||||
])
|
||||
AS_VAR_POPDEF([FLAGS])dnl
|
||||
])dnl AX_APPEND_FLAG
|
|
@ -0,0 +1,65 @@
|
|||
# ===========================================================================
|
||||
# https://www.gnu.org/software/autoconf-archive/ax_append_link_flags.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_APPEND_LINK_FLAGS([FLAG1 FLAG2 ...], [FLAGS-VARIABLE], [EXTRA-FLAGS], [INPUT])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# For every FLAG1, FLAG2 it is checked whether the linker works with the
|
||||
# flag. If it does, the flag is added FLAGS-VARIABLE
|
||||
#
|
||||
# If FLAGS-VARIABLE is not specified, the linker's flags (LDFLAGS) is
|
||||
# used. During the check the flag is always added to the linker's flags.
|
||||
#
|
||||
# If EXTRA-FLAGS is defined, it is added to the linker's default flags
|
||||
# when the check is done. The check is thus made with the flags: "LDFLAGS
|
||||
# EXTRA-FLAGS FLAG". This can for example be used to force the linker to
|
||||
# issue an error when a bad flag is given.
|
||||
#
|
||||
# INPUT gives an alternative input source to AC_COMPILE_IFELSE.
|
||||
#
|
||||
# NOTE: This macro depends on the AX_APPEND_FLAG and AX_CHECK_LINK_FLAG.
|
||||
# Please keep this macro in sync with AX_APPEND_COMPILE_FLAGS.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.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 3 of the License, 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, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 6
|
||||
|
||||
AC_DEFUN([AX_APPEND_LINK_FLAGS],
|
||||
[AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG])
|
||||
AX_REQUIRE_DEFINED([AX_APPEND_FLAG])
|
||||
for flag in $1; do
|
||||
AX_CHECK_LINK_FLAG([$flag], [AX_APPEND_FLAG([$flag], [m4_default([$2], [LDFLAGS])])], [], [$3], [$4])
|
||||
done
|
||||
])dnl AX_APPEND_LINK_FLAGS
|
|
@ -0,0 +1,74 @@
|
|||
# ===========================================================================
|
||||
# https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Check whether the given FLAG works with the current language's compiler
|
||||
# or gives an error. (Warnings, however, are ignored)
|
||||
#
|
||||
# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
|
||||
# success/failure.
|
||||
#
|
||||
# If EXTRA-FLAGS is defined, it is added to the current language's default
|
||||
# flags (e.g. CFLAGS) when the check is done. The check is thus made with
|
||||
# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
|
||||
# force the compiler to issue an error when a bad flag is given.
|
||||
#
|
||||
# INPUT gives an alternative input source to AC_COMPILE_IFELSE.
|
||||
#
|
||||
# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
|
||||
# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
|
||||
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.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 3 of the License, 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, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 5
|
||||
|
||||
AC_DEFUN([AX_CHECK_COMPILE_FLAG],
|
||||
[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
|
||||
AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
|
||||
AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
|
||||
ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
|
||||
_AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
|
||||
AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
|
||||
[AS_VAR_SET(CACHEVAR,[yes])],
|
||||
[AS_VAR_SET(CACHEVAR,[no])])
|
||||
_AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
|
||||
AS_VAR_IF(CACHEVAR,yes,
|
||||
[m4_default([$2], :)],
|
||||
[m4_default([$3], :)])
|
||||
AS_VAR_POPDEF([CACHEVAR])dnl
|
||||
])dnl AX_CHECK_COMPILE_FLAGS
|
|
@ -0,0 +1,124 @@
|
|||
# ===========================================================================
|
||||
# https://www.gnu.org/software/autoconf-archive/ax_check_enable_debug.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_CHECK_ENABLE_DEBUG([enable by default=yes/info/profile/no], [ENABLE DEBUG VARIABLES ...], [DISABLE DEBUG VARIABLES NDEBUG ...], [IS-RELEASE])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Check for the presence of an --enable-debug option to configure, with
|
||||
# the specified default value used when the option is not present. Return
|
||||
# the value in the variable $ax_enable_debug.
|
||||
#
|
||||
# Specifying 'yes' adds '-g -O0' to the compilation flags for all
|
||||
# languages. Specifying 'info' adds '-g' to the compilation flags.
|
||||
# Specifying 'profile' adds '-g -pg' to the compilation flags and '-pg' to
|
||||
# the linking flags. Otherwise, nothing is added.
|
||||
#
|
||||
# Define the variables listed in the second argument if debug is enabled,
|
||||
# defaulting to no variables. Defines the variables listed in the third
|
||||
# argument if debug is disabled, defaulting to NDEBUG. All lists of
|
||||
# variables should be space-separated.
|
||||
#
|
||||
# If debug is not enabled, ensure AC_PROG_* will not add debugging flags.
|
||||
# Should be invoked prior to any AC_PROG_* compiler checks.
|
||||
#
|
||||
# IS-RELEASE can be used to change the default to 'no' when making a
|
||||
# release. Set IS-RELEASE to 'yes' or 'no' as appropriate. By default, it
|
||||
# uses the value of $ax_is_release, so if you are using the AX_IS_RELEASE
|
||||
# macro, there is no need to pass this parameter.
|
||||
#
|
||||
# AX_IS_RELEASE([git-directory])
|
||||
# AX_CHECK_ENABLE_DEBUG()
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2011 Rhys Ulerich <rhys.ulerich@gmail.com>
|
||||
# Copyright (c) 2014, 2015 Philip Withnall <philip@tecnocode.co.uk>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved.
|
||||
|
||||
#serial 8
|
||||
|
||||
AC_DEFUN([AX_CHECK_ENABLE_DEBUG],[
|
||||
AC_BEFORE([$0],[AC_PROG_CC])dnl
|
||||
AC_BEFORE([$0],[AC_PROG_CXX])dnl
|
||||
AC_BEFORE([$0],[AC_PROG_F77])dnl
|
||||
AC_BEFORE([$0],[AC_PROG_FC])dnl
|
||||
|
||||
AC_MSG_CHECKING(whether to enable debugging)
|
||||
|
||||
ax_enable_debug_default=m4_tolower(m4_normalize(ifelse([$1],,[no],[$1])))
|
||||
ax_enable_debug_is_release=m4_tolower(m4_normalize(ifelse([$4],,
|
||||
[$ax_is_release],
|
||||
[$4])))
|
||||
|
||||
# If this is a release, override the default.
|
||||
AS_IF([test "$ax_enable_debug_is_release" = "yes"],
|
||||
[ax_enable_debug_default="no"])
|
||||
|
||||
m4_define(ax_enable_debug_vars,[m4_normalize(ifelse([$2],,,[$2]))])
|
||||
m4_define(ax_disable_debug_vars,[m4_normalize(ifelse([$3],,[NDEBUG],[$3]))])
|
||||
|
||||
AC_ARG_ENABLE(debug,
|
||||
[AS_HELP_STRING([--enable-debug=]@<:@yes/info/profile/no@:>@,[compile with debugging])],
|
||||
[],enable_debug=$ax_enable_debug_default)
|
||||
|
||||
# empty mean debug yes
|
||||
AS_IF([test "x$enable_debug" = "x"],
|
||||
[enable_debug="yes"])
|
||||
|
||||
# case of debug
|
||||
AS_CASE([$enable_debug],
|
||||
[yes],[
|
||||
AC_MSG_RESULT(yes)
|
||||
CFLAGS="${CFLAGS} -g -O0"
|
||||
CXXFLAGS="${CXXFLAGS} -g -O0"
|
||||
FFLAGS="${FFLAGS} -g -O0"
|
||||
FCFLAGS="${FCFLAGS} -g -O0"
|
||||
OBJCFLAGS="${OBJCFLAGS} -g -O0"
|
||||
],
|
||||
[info],[
|
||||
AC_MSG_RESULT(info)
|
||||
CFLAGS="${CFLAGS} -g"
|
||||
CXXFLAGS="${CXXFLAGS} -g"
|
||||
FFLAGS="${FFLAGS} -g"
|
||||
FCFLAGS="${FCFLAGS} -g"
|
||||
OBJCFLAGS="${OBJCFLAGS} -g"
|
||||
],
|
||||
[profile],[
|
||||
AC_MSG_RESULT(profile)
|
||||
CFLAGS="${CFLAGS} -g -pg"
|
||||
CXXFLAGS="${CXXFLAGS} -g -pg"
|
||||
FFLAGS="${FFLAGS} -g -pg"
|
||||
FCFLAGS="${FCFLAGS} -g -pg"
|
||||
OBJCFLAGS="${OBJCFLAGS} -g -pg"
|
||||
LDFLAGS="${LDFLAGS} -pg"
|
||||
],
|
||||
[
|
||||
AC_MSG_RESULT(no)
|
||||
dnl Ensure AC_PROG_CC/CXX/F77/FC/OBJC will not enable debug flags
|
||||
dnl by setting any unset environment flag variables
|
||||
AS_IF([test "x${CFLAGS+set}" != "xset"],
|
||||
[CFLAGS=""])
|
||||
AS_IF([test "x${CXXFLAGS+set}" != "xset"],
|
||||
[CXXFLAGS=""])
|
||||
AS_IF([test "x${FFLAGS+set}" != "xset"],
|
||||
[FFLAGS=""])
|
||||
AS_IF([test "x${FCFLAGS+set}" != "xset"],
|
||||
[FCFLAGS=""])
|
||||
AS_IF([test "x${OBJCFLAGS+set}" != "xset"],
|
||||
[OBJCFLAGS=""])
|
||||
])
|
||||
|
||||
dnl Define various variables if debugging is disabled.
|
||||
dnl assert.h is a NOP if NDEBUG is defined, so define it by default.
|
||||
AS_IF([test "x$enable_debug" = "xyes"],
|
||||
[m4_map_args_w(ax_enable_debug_vars, [AC_DEFINE(], [,,[Define if debugging is enabled])])],
|
||||
[m4_map_args_w(ax_disable_debug_vars, [AC_DEFINE(], [,,[Define if debugging is disabled])])])
|
||||
ax_enable_debug=$enable_debug
|
||||
])
|
|
@ -0,0 +1,74 @@
|
|||
# ===========================================================================
|
||||
# https://www.gnu.org/software/autoconf-archive/ax_check_link_flag.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_CHECK_LINK_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Check whether the given FLAG works with the linker or gives an error.
|
||||
# (Warnings, however, are ignored)
|
||||
#
|
||||
# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
|
||||
# success/failure.
|
||||
#
|
||||
# If EXTRA-FLAGS is defined, it is added to the linker's default flags
|
||||
# when the check is done. The check is thus made with the flags: "LDFLAGS
|
||||
# EXTRA-FLAGS FLAG". This can for example be used to force the linker to
|
||||
# issue an error when a bad flag is given.
|
||||
#
|
||||
# INPUT gives an alternative input source to AC_LINK_IFELSE.
|
||||
#
|
||||
# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
|
||||
# macro in sync with AX_CHECK_{PREPROC,COMPILE}_FLAG.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
|
||||
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.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 3 of the License, 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, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 5
|
||||
|
||||
AC_DEFUN([AX_CHECK_LINK_FLAG],
|
||||
[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
|
||||
AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_ldflags_$4_$1])dnl
|
||||
AC_CACHE_CHECK([whether the linker accepts $1], CACHEVAR, [
|
||||
ax_check_save_flags=$LDFLAGS
|
||||
LDFLAGS="$LDFLAGS $4 $1"
|
||||
AC_LINK_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
|
||||
[AS_VAR_SET(CACHEVAR,[yes])],
|
||||
[AS_VAR_SET(CACHEVAR,[no])])
|
||||
LDFLAGS=$ax_check_save_flags])
|
||||
AS_VAR_IF(CACHEVAR,yes,
|
||||
[m4_default([$2], :)],
|
||||
[m4_default([$3], :)])
|
||||
AS_VAR_POPDEF([CACHEVAR])dnl
|
||||
])dnl AX_CHECK_LINK_FLAGS
|
|
@ -0,0 +1,264 @@
|
|||
# ===========================================================================
|
||||
# https://www.gnu.org/software/autoconf-archive/ax_code_coverage.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_CODE_COVERAGE()
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Defines CODE_COVERAGE_CPPFLAGS, CODE_COVERAGE_CFLAGS,
|
||||
# CODE_COVERAGE_CXXFLAGS and CODE_COVERAGE_LIBS which should be included
|
||||
# in the CPPFLAGS, CFLAGS CXXFLAGS and LIBS/LIBADD variables of every
|
||||
# build target (program or library) which should be built with code
|
||||
# coverage support. Also defines CODE_COVERAGE_RULES which should be
|
||||
# substituted in your Makefile; and $enable_code_coverage which can be
|
||||
# used in subsequent configure output. CODE_COVERAGE_ENABLED is defined
|
||||
# and substituted, and corresponds to the value of the
|
||||
# --enable-code-coverage option, which defaults to being disabled.
|
||||
#
|
||||
# Test also for gcov program and create GCOV variable that could be
|
||||
# substituted.
|
||||
#
|
||||
# Note that all optimization flags in CFLAGS must be disabled when code
|
||||
# coverage is enabled.
|
||||
#
|
||||
# Usage example:
|
||||
#
|
||||
# configure.ac:
|
||||
#
|
||||
# AX_CODE_COVERAGE
|
||||
#
|
||||
# Makefile.am:
|
||||
#
|
||||
# @CODE_COVERAGE_RULES@
|
||||
# my_program_LIBS = ... $(CODE_COVERAGE_LIBS) ...
|
||||
# my_program_CPPFLAGS = ... $(CODE_COVERAGE_CPPFLAGS) ...
|
||||
# my_program_CFLAGS = ... $(CODE_COVERAGE_CFLAGS) ...
|
||||
# my_program_CXXFLAGS = ... $(CODE_COVERAGE_CXXFLAGS) ...
|
||||
#
|
||||
# This results in a "check-code-coverage" rule being added to any
|
||||
# Makefile.am which includes "@CODE_COVERAGE_RULES@" (assuming the module
|
||||
# has been configured with --enable-code-coverage). Running `make
|
||||
# check-code-coverage` in that directory will run the module's test suite
|
||||
# (`make check`) and build a code coverage report detailing the code which
|
||||
# was touched, then print the URI for the report.
|
||||
#
|
||||
# In earlier versions of this macro, CODE_COVERAGE_LDFLAGS was defined
|
||||
# instead of CODE_COVERAGE_LIBS. They are both still defined, but use of
|
||||
# CODE_COVERAGE_LIBS is preferred for clarity; CODE_COVERAGE_LDFLAGS is
|
||||
# deprecated. They have the same value.
|
||||
#
|
||||
# This code was derived from Makefile.decl in GLib, originally licenced
|
||||
# under LGPLv2.1+.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2012, 2016 Philip Withnall
|
||||
# Copyright (c) 2012 Xan Lopez
|
||||
# Copyright (c) 2012 Christian Persch
|
||||
# Copyright (c) 2012 Paolo Borelli
|
||||
# Copyright (c) 2012 Dan Winship
|
||||
# Copyright (c) 2015 Bastien ROUCARIES
|
||||
#
|
||||
# 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.1 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 program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#serial 25
|
||||
|
||||
AC_DEFUN([AX_CODE_COVERAGE],[
|
||||
dnl Check for --enable-code-coverage
|
||||
AC_REQUIRE([AC_PROG_SED])
|
||||
|
||||
# allow to override gcov location
|
||||
AC_ARG_WITH([gcov],
|
||||
[AS_HELP_STRING([--with-gcov[=GCOV]], [use given GCOV for coverage (GCOV=gcov).])],
|
||||
[_AX_CODE_COVERAGE_GCOV_PROG_WITH=$with_gcov],
|
||||
[_AX_CODE_COVERAGE_GCOV_PROG_WITH=gcov])
|
||||
|
||||
AC_MSG_CHECKING([whether to build with code coverage support])
|
||||
AC_ARG_ENABLE([code-coverage],
|
||||
AS_HELP_STRING([--enable-code-coverage],
|
||||
[Whether to enable code coverage support]),,
|
||||
enable_code_coverage=no)
|
||||
|
||||
AM_CONDITIONAL([CODE_COVERAGE_ENABLED], [test x$enable_code_coverage = xyes])
|
||||
AC_SUBST([CODE_COVERAGE_ENABLED], [$enable_code_coverage])
|
||||
AC_MSG_RESULT($enable_code_coverage)
|
||||
|
||||
AS_IF([ test "$enable_code_coverage" = "yes" ], [
|
||||
# check for gcov
|
||||
AC_CHECK_TOOL([GCOV],
|
||||
[$_AX_CODE_COVERAGE_GCOV_PROG_WITH],
|
||||
[:])
|
||||
AS_IF([test "X$GCOV" = "X:"],
|
||||
[AC_MSG_ERROR([gcov is needed to do coverage])])
|
||||
AC_SUBST([GCOV])
|
||||
|
||||
dnl Check if gcc is being used
|
||||
AS_IF([ test "$GCC" = "no" ], [
|
||||
AC_MSG_ERROR([not compiling with gcc, which is required for gcov code coverage])
|
||||
])
|
||||
|
||||
AC_CHECK_PROG([LCOV], [lcov], [lcov])
|
||||
AC_CHECK_PROG([GENHTML], [genhtml], [genhtml])
|
||||
|
||||
AS_IF([ test -z "$LCOV" ], [
|
||||
AC_MSG_ERROR([To enable code coverage reporting you must have lcov installed])
|
||||
])
|
||||
|
||||
AS_IF([ test -z "$GENHTML" ], [
|
||||
AC_MSG_ERROR([Could not find genhtml from the lcov package])
|
||||
])
|
||||
|
||||
dnl Build the code coverage flags
|
||||
dnl Define CODE_COVERAGE_LDFLAGS for backwards compatibility
|
||||
CODE_COVERAGE_CPPFLAGS="-DNDEBUG"
|
||||
CODE_COVERAGE_CFLAGS="-O0 -g -fprofile-arcs -ftest-coverage"
|
||||
CODE_COVERAGE_CXXFLAGS="-O0 -g -fprofile-arcs -ftest-coverage"
|
||||
CODE_COVERAGE_LIBS="-lgcov"
|
||||
CODE_COVERAGE_LDFLAGS="$CODE_COVERAGE_LIBS"
|
||||
|
||||
AC_SUBST([CODE_COVERAGE_CPPFLAGS])
|
||||
AC_SUBST([CODE_COVERAGE_CFLAGS])
|
||||
AC_SUBST([CODE_COVERAGE_CXXFLAGS])
|
||||
AC_SUBST([CODE_COVERAGE_LIBS])
|
||||
AC_SUBST([CODE_COVERAGE_LDFLAGS])
|
||||
|
||||
[CODE_COVERAGE_RULES_CHECK='
|
||||
-$(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) -k check
|
||||
$(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) code-coverage-capture
|
||||
']
|
||||
[CODE_COVERAGE_RULES_CAPTURE='
|
||||
$(code_coverage_v_lcov_cap)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --capture --output-file "$(CODE_COVERAGE_OUTPUT_FILE).tmp" --test-name "$(call code_coverage_sanitize,$(PACKAGE_NAME)-$(PACKAGE_VERSION))" --no-checksum --compat-libtool $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_OPTIONS)
|
||||
$(code_coverage_v_lcov_ign)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --remove "$(CODE_COVERAGE_OUTPUT_FILE).tmp" "/tmp/*" $(CODE_COVERAGE_IGNORE_PATTERN) --output-file "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_RMOPTS)
|
||||
-@rm -f $(CODE_COVERAGE_OUTPUT_FILE).tmp
|
||||
$(code_coverage_v_genhtml)LANG=C $(GENHTML) $(code_coverage_quiet) $(addprefix --prefix ,$(CODE_COVERAGE_DIRECTORY)) --output-directory "$(CODE_COVERAGE_OUTPUT_DIRECTORY)" --title "$(PACKAGE_NAME)-$(PACKAGE_VERSION) Code Coverage" --legend --show-details "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_GENHTML_OPTIONS)
|
||||
@echo "file://$(abs_builddir)/$(CODE_COVERAGE_OUTPUT_DIRECTORY)/index.html"
|
||||
']
|
||||
[CODE_COVERAGE_RULES_CLEAN='
|
||||
clean: code-coverage-clean
|
||||
distclean: code-coverage-clean
|
||||
code-coverage-clean:
|
||||
-$(LCOV) --directory $(top_builddir) -z
|
||||
-rm -rf $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_FILE).tmp $(CODE_COVERAGE_OUTPUT_DIRECTORY)
|
||||
-find . \( -name "*.gcda" -o -name "*.gcno" -o -name "*.gcov" \) -delete
|
||||
']
|
||||
], [
|
||||
[CODE_COVERAGE_RULES_CHECK='
|
||||
@echo "Need to reconfigure with --enable-code-coverage"
|
||||
']
|
||||
CODE_COVERAGE_RULES_CAPTURE="$CODE_COVERAGE_RULES_CHECK"
|
||||
CODE_COVERAGE_RULES_CLEAN=''
|
||||
])
|
||||
|
||||
[CODE_COVERAGE_RULES='
|
||||
# Code coverage
|
||||
#
|
||||
# Optional:
|
||||
# - CODE_COVERAGE_DIRECTORY: Top-level directory for code coverage reporting.
|
||||
# Multiple directories may be specified, separated by whitespace.
|
||||
# (Default: $(top_builddir))
|
||||
# - CODE_COVERAGE_OUTPUT_FILE: Filename and path for the .info file generated
|
||||
# by lcov for code coverage. (Default:
|
||||
# $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info)
|
||||
# - CODE_COVERAGE_OUTPUT_DIRECTORY: Directory for generated code coverage
|
||||
# reports to be created. (Default:
|
||||
# $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage)
|
||||
# - CODE_COVERAGE_BRANCH_COVERAGE: Set to 1 to enforce branch coverage,
|
||||
# set to 0 to disable it and leave empty to stay with the default.
|
||||
# (Default: empty)
|
||||
# - CODE_COVERAGE_LCOV_SHOPTS_DEFAULT: Extra options shared between both lcov
|
||||
# instances. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE)
|
||||
# - CODE_COVERAGE_LCOV_SHOPTS: Extra options to shared between both lcov
|
||||
# instances. (Default: $CODE_COVERAGE_LCOV_SHOPTS_DEFAULT)
|
||||
# - CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH: --gcov-tool pathtogcov
|
||||
# - CODE_COVERAGE_LCOV_OPTIONS_DEFAULT: Extra options to pass to the
|
||||
# collecting lcov instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH)
|
||||
# - CODE_COVERAGE_LCOV_OPTIONS: Extra options to pass to the collecting lcov
|
||||
# instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_DEFAULT)
|
||||
# - CODE_COVERAGE_LCOV_RMOPTS_DEFAULT: Extra options to pass to the filtering
|
||||
# lcov instance. (Default: empty)
|
||||
# - CODE_COVERAGE_LCOV_RMOPTS: Extra options to pass to the filtering lcov
|
||||
# instance. (Default: $CODE_COVERAGE_LCOV_RMOPTS_DEFAULT)
|
||||
# - CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT: Extra options to pass to the
|
||||
# genhtml instance. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE)
|
||||
# - CODE_COVERAGE_GENHTML_OPTIONS: Extra options to pass to the genhtml
|
||||
# instance. (Default: $CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT)
|
||||
# - CODE_COVERAGE_IGNORE_PATTERN: Extra glob pattern of files to ignore
|
||||
#
|
||||
# The generated report will be titled using the $(PACKAGE_NAME) and
|
||||
# $(PACKAGE_VERSION). In order to add the current git hash to the title,
|
||||
# use the git-version-gen script, available online.
|
||||
|
||||
# Optional variables
|
||||
CODE_COVERAGE_DIRECTORY ?= $(top_builddir)
|
||||
CODE_COVERAGE_OUTPUT_FILE ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info
|
||||
CODE_COVERAGE_OUTPUT_DIRECTORY ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage
|
||||
CODE_COVERAGE_BRANCH_COVERAGE ?=
|
||||
CODE_COVERAGE_LCOV_SHOPTS_DEFAULT ?= $(if $(CODE_COVERAGE_BRANCH_COVERAGE),\
|
||||
--rc lcov_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE))
|
||||
CODE_COVERAGE_LCOV_SHOPTS ?= $(CODE_COVERAGE_LCOV_SHOPTS_DEFAULT)
|
||||
CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH ?= --gcov-tool "$(GCOV)"
|
||||
CODE_COVERAGE_LCOV_OPTIONS_DEFAULT ?= $(CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH)
|
||||
CODE_COVERAGE_LCOV_OPTIONS ?= $(CODE_COVERAGE_LCOV_OPTIONS_DEFAULT)
|
||||
CODE_COVERAGE_LCOV_RMOPTS_DEFAULT ?=
|
||||
CODE_COVERAGE_LCOV_RMOPTS ?= $(CODE_COVERAGE_LCOV_RMOPTS_DEFAULT)
|
||||
CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT ?=\
|
||||
$(if $(CODE_COVERAGE_BRANCH_COVERAGE),\
|
||||
--rc genhtml_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE))
|
||||
CODE_COVERAGE_GENHTML_OPTIONS ?= $(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT)
|
||||
CODE_COVERAGE_IGNORE_PATTERN ?=
|
||||
|
||||
GITIGNOREFILES ?=
|
||||
GITIGNOREFILES += $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_DIRECTORY)
|
||||
|
||||
code_coverage_v_lcov_cap = $(code_coverage_v_lcov_cap_$(V))
|
||||
code_coverage_v_lcov_cap_ = $(code_coverage_v_lcov_cap_$(AM_DEFAULT_VERBOSITY))
|
||||
code_coverage_v_lcov_cap_0 = @echo " LCOV --capture"\
|
||||
$(CODE_COVERAGE_OUTPUT_FILE);
|
||||
code_coverage_v_lcov_ign = $(code_coverage_v_lcov_ign_$(V))
|
||||
code_coverage_v_lcov_ign_ = $(code_coverage_v_lcov_ign_$(AM_DEFAULT_VERBOSITY))
|
||||
code_coverage_v_lcov_ign_0 = @echo " LCOV --remove /tmp/*"\
|
||||
$(CODE_COVERAGE_IGNORE_PATTERN);
|
||||
code_coverage_v_genhtml = $(code_coverage_v_genhtml_$(V))
|
||||
code_coverage_v_genhtml_ = $(code_coverage_v_genhtml_$(AM_DEFAULT_VERBOSITY))
|
||||
code_coverage_v_genhtml_0 = @echo " GEN " $(CODE_COVERAGE_OUTPUT_DIRECTORY);
|
||||
code_coverage_quiet = $(code_coverage_quiet_$(V))
|
||||
code_coverage_quiet_ = $(code_coverage_quiet_$(AM_DEFAULT_VERBOSITY))
|
||||
code_coverage_quiet_0 = --quiet
|
||||
|
||||
# sanitizes the test-name: replaces with underscores: dashes and dots
|
||||
code_coverage_sanitize = $(subst -,_,$(subst .,_,$(1)))
|
||||
|
||||
# Use recursive makes in order to ignore errors during check
|
||||
check-code-coverage:'"$CODE_COVERAGE_RULES_CHECK"'
|
||||
|
||||
# Capture code coverage data
|
||||
code-coverage-capture: code-coverage-capture-hook'"$CODE_COVERAGE_RULES_CAPTURE"'
|
||||
|
||||
# Hook rule executed before code-coverage-capture, overridable by the user
|
||||
code-coverage-capture-hook:
|
||||
|
||||
'"$CODE_COVERAGE_RULES_CLEAN"'
|
||||
|
||||
A''M_DISTCHECK_CONFIGURE_FLAGS ?=
|
||||
A''M_DISTCHECK_CONFIGURE_FLAGS += --disable-code-coverage
|
||||
|
||||
.PHONY: check-code-coverage code-coverage-capture code-coverage-capture-hook code-coverage-clean
|
||||
']
|
||||
|
||||
AC_SUBST([CODE_COVERAGE_RULES])
|
||||
m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([CODE_COVERAGE_RULES])])
|
||||
])
|
|
@ -0,0 +1,158 @@
|
|||
# ===========================================================================
|
||||
# https://www.gnu.org/software/autoconf-archive/ax_compiler_flags.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_COMPILER_FLAGS([CFLAGS-VARIABLE], [LDFLAGS-VARIABLE], [IS-RELEASE], [EXTRA-BASE-CFLAGS], [EXTRA-YES-CFLAGS], [UNUSED], [UNUSED], [UNUSED], [EXTRA-BASE-LDFLAGS], [EXTRA-YES-LDFLAGS], [UNUSED], [UNUSED], [UNUSED])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Check for the presence of an --enable-compile-warnings option to
|
||||
# configure, defaulting to "error" in normal operation, or "yes" if
|
||||
# IS-RELEASE is equal to "yes". Return the value in the variable
|
||||
# $ax_enable_compile_warnings.
|
||||
#
|
||||
# Depending on the value of --enable-compile-warnings, different compiler
|
||||
# warnings are checked to see if they work with the current compiler and,
|
||||
# if so, are appended to CFLAGS-VARIABLE and LDFLAGS-VARIABLE. This
|
||||
# allows a consistent set of baseline compiler warnings to be used across
|
||||
# a code base, irrespective of any warnings enabled locally by individual
|
||||
# developers. By standardising the warnings used by all developers of a
|
||||
# project, the project can commit to a zero-warnings policy, using -Werror
|
||||
# to prevent compilation if new warnings are introduced. This makes
|
||||
# catching bugs which are flagged by warnings a lot easier.
|
||||
#
|
||||
# By providing a consistent --enable-compile-warnings argument across all
|
||||
# projects using this macro, continuous integration systems can easily be
|
||||
# configured the same for all projects. Automated systems or build
|
||||
# systems aimed at beginners may want to pass the --disable-Werror
|
||||
# argument to unconditionally prevent warnings being fatal.
|
||||
#
|
||||
# --enable-compile-warnings can take the values:
|
||||
#
|
||||
# * no: Base compiler warnings only; not even -Wall.
|
||||
# * yes: The above, plus a broad range of useful warnings.
|
||||
# * error: The above, plus -Werror so that all warnings are fatal.
|
||||
# Use --disable-Werror to override this and disable fatal
|
||||
# warnings.
|
||||
#
|
||||
# The set of base and enabled flags can be augmented using the
|
||||
# EXTRA-*-CFLAGS and EXTRA-*-LDFLAGS variables, which are tested and
|
||||
# appended to the output variable if --enable-compile-warnings is not
|
||||
# "no". Flags should not be disabled using these arguments, as the entire
|
||||
# point of AX_COMPILER_FLAGS is to enforce a consistent set of useful
|
||||
# compiler warnings on code, using warnings which have been chosen for low
|
||||
# false positive rates. If a compiler emits false positives for a
|
||||
# warning, a #pragma should be used in the code to disable the warning
|
||||
# locally. See:
|
||||
#
|
||||
# https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gcc/Diagnostic-Pragmas.html#Diagnostic-Pragmas
|
||||
#
|
||||
# The EXTRA-* variables should only be used to supply extra warning flags,
|
||||
# and not general purpose compiler flags, as they are controlled by
|
||||
# configure options such as --disable-Werror.
|
||||
#
|
||||
# IS-RELEASE can be used to disable -Werror when making a release, which
|
||||
# is useful for those hairy moments when you just want to get the release
|
||||
# done as quickly as possible. Set it to "yes" to disable -Werror. By
|
||||
# default, it uses the value of $ax_is_release, so if you are using the
|
||||
# AX_IS_RELEASE macro, there is no need to pass this parameter. For
|
||||
# example:
|
||||
#
|
||||
# AX_IS_RELEASE([git-directory])
|
||||
# AX_COMPILER_FLAGS()
|
||||
#
|
||||
# CFLAGS-VARIABLE defaults to WARN_CFLAGS, and LDFLAGS-VARIABLE defaults
|
||||
# to WARN_LDFLAGS. Both variables are AC_SUBST-ed by this macro, but must
|
||||
# be manually added to the CFLAGS and LDFLAGS variables for each target in
|
||||
# the code base.
|
||||
#
|
||||
# If C++ language support is enabled with AC_PROG_CXX, which must occur
|
||||
# before this macro in configure.ac, warning flags for the C++ compiler
|
||||
# are AC_SUBST-ed as WARN_CXXFLAGS, and must be manually added to the
|
||||
# CXXFLAGS variables for each target in the code base. EXTRA-*-CFLAGS can
|
||||
# be used to augment the base and enabled flags.
|
||||
#
|
||||
# Warning flags for g-ir-scanner (from GObject Introspection) are
|
||||
# AC_SUBST-ed as WARN_SCANNERFLAGS. This variable must be manually added
|
||||
# to the SCANNERFLAGS variable for each GIR target in the code base. If
|
||||
# extra g-ir-scanner flags need to be enabled, the AX_COMPILER_FLAGS_GIR
|
||||
# macro must be invoked manually.
|
||||
#
|
||||
# AX_COMPILER_FLAGS may add support for other tools in future, in addition
|
||||
# to the compiler and linker. No extra EXTRA-* variables will be added
|
||||
# for those tools, and all extra support will still use the single
|
||||
# --enable-compile-warnings configure option. For finer grained control
|
||||
# over the flags for individual tools, use AX_COMPILER_FLAGS_CFLAGS,
|
||||
# AX_COMPILER_FLAGS_LDFLAGS and AX_COMPILER_FLAGS_* for new tools.
|
||||
#
|
||||
# The UNUSED variables date from a previous version of this macro, and are
|
||||
# automatically appended to the preceding non-UNUSED variable. They should
|
||||
# be left empty in new uses of the macro.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2014, 2015 Philip Withnall <philip@tecnocode.co.uk>
|
||||
# Copyright (c) 2015 David King <amigadave@amigadave.com>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 14
|
||||
|
||||
# _AX_COMPILER_FLAGS_LANG([LANGNAME])
|
||||
m4_defun([_AX_COMPILER_FLAGS_LANG],
|
||||
[m4_ifdef([_AX_COMPILER_FLAGS_LANG_]$1[_enabled], [],
|
||||
[m4_define([_AX_COMPILER_FLAGS_LANG_]$1[_enabled], [])dnl
|
||||
AX_REQUIRE_DEFINED([AX_COMPILER_FLAGS_]$1[FLAGS])])dnl
|
||||
])
|
||||
|
||||
AC_DEFUN([AX_COMPILER_FLAGS],[
|
||||
# C support is enabled by default.
|
||||
_AX_COMPILER_FLAGS_LANG([C])
|
||||
# Only enable C++ support if AC_PROG_CXX is called. The redefinition of
|
||||
# AC_PROG_CXX is so that a fatal error is emitted if this macro is called
|
||||
# before AC_PROG_CXX, which would otherwise cause no C++ warnings to be
|
||||
# checked.
|
||||
AC_PROVIDE_IFELSE([AC_PROG_CXX],
|
||||
[_AX_COMPILER_FLAGS_LANG([CXX])],
|
||||
[m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[_AX_COMPILER_FLAGS_LANG([CXX])])])
|
||||
AX_REQUIRE_DEFINED([AX_COMPILER_FLAGS_LDFLAGS])
|
||||
|
||||
# Default value for IS-RELEASE is $ax_is_release
|
||||
ax_compiler_flags_is_release=m4_tolower(m4_normalize(ifelse([$3],,
|
||||
[$ax_is_release],
|
||||
[$3])))
|
||||
|
||||
AC_ARG_ENABLE([compile-warnings],
|
||||
AS_HELP_STRING([--enable-compile-warnings=@<:@no/yes/error@:>@],
|
||||
[Enable compiler warnings and errors]),,
|
||||
[AS_IF([test "$ax_compiler_flags_is_release" = "yes"],
|
||||
[enable_compile_warnings="yes"],
|
||||
[enable_compile_warnings="error"])])
|
||||
AC_ARG_ENABLE([Werror],
|
||||
AS_HELP_STRING([--disable-Werror],
|
||||
[Unconditionally make all compiler warnings non-fatal]),,
|
||||
[enable_Werror=maybe])
|
||||
|
||||
# Return the user's chosen warning level
|
||||
AS_IF([test "$enable_Werror" = "no" -a \
|
||||
"$enable_compile_warnings" = "error"],[
|
||||
enable_compile_warnings="yes"
|
||||
])
|
||||
|
||||
ax_enable_compile_warnings=$enable_compile_warnings
|
||||
|
||||
AX_COMPILER_FLAGS_CFLAGS([$1],[$ax_compiler_flags_is_release],
|
||||
[$4],[$5 $6 $7 $8])
|
||||
m4_ifdef([_AX_COMPILER_FLAGS_LANG_CXX_enabled],
|
||||
[AX_COMPILER_FLAGS_CXXFLAGS([WARN_CXXFLAGS],
|
||||
[$ax_compiler_flags_is_release],
|
||||
[$4],[$5 $6 $7 $8])])
|
||||
AX_COMPILER_FLAGS_LDFLAGS([$2],[$ax_compiler_flags_is_release],
|
||||
[$9],[$10 $11 $12 $13])
|
||||
AX_COMPILER_FLAGS_GIR([WARN_SCANNERFLAGS],[$ax_compiler_flags_is_release])
|
||||
])dnl AX_COMPILER_FLAGS
|
|
@ -0,0 +1,160 @@
|
|||
# =============================================================================
|
||||
# https://www.gnu.org/software/autoconf-archive/ax_compiler_flags_cflags.html
|
||||
# =============================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_COMPILER_FLAGS_CFLAGS([VARIABLE], [IS-RELEASE], [EXTRA-BASE-FLAGS], [EXTRA-YES-FLAGS])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Add warning flags for the C compiler to VARIABLE, which defaults to
|
||||
# WARN_CFLAGS. VARIABLE is AC_SUBST-ed by this macro, but must be
|
||||
# manually added to the CFLAGS variable for each target in the code base.
|
||||
#
|
||||
# This macro depends on the environment set up by AX_COMPILER_FLAGS.
|
||||
# Specifically, it uses the value of $ax_enable_compile_warnings to decide
|
||||
# which flags to enable.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2014, 2015 Philip Withnall <philip@tecnocode.co.uk>
|
||||
# Copyright (c) 2017, 2018 Reini Urban <rurban@cpan.org>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 16
|
||||
|
||||
AC_DEFUN([AX_COMPILER_FLAGS_CFLAGS],[
|
||||
AC_REQUIRE([AC_PROG_SED])
|
||||
AX_REQUIRE_DEFINED([AX_APPEND_COMPILE_FLAGS])
|
||||
AX_REQUIRE_DEFINED([AX_APPEND_FLAG])
|
||||
AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG])
|
||||
|
||||
# Variable names
|
||||
m4_define([ax_warn_cflags_variable],
|
||||
[m4_normalize(ifelse([$1],,[WARN_CFLAGS],[$1]))])
|
||||
|
||||
AC_LANG_PUSH([C])
|
||||
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
|
||||
[#ifndef __cplusplus
|
||||
#error "no C++"
|
||||
#endif]])],
|
||||
[ax_compiler_cxx=yes;],
|
||||
[ax_compiler_cxx=no;])
|
||||
|
||||
# Always pass -Werror=unknown-warning-option to get Clang to fail on bad
|
||||
# flags, otherwise they are always appended to the warn_cflags variable, and
|
||||
# Clang warns on them for every compilation unit.
|
||||
# If this is passed to GCC, it will explode, so the flag must be enabled
|
||||
# conditionally.
|
||||
AX_CHECK_COMPILE_FLAG([-Werror=unknown-warning-option],[
|
||||
ax_compiler_flags_test="-Werror=unknown-warning-option"
|
||||
],[
|
||||
ax_compiler_flags_test=""
|
||||
])
|
||||
|
||||
# Check that -Wno-suggest-attribute=format is supported
|
||||
AX_CHECK_COMPILE_FLAG([-Wno-suggest-attribute=format],[
|
||||
ax_compiler_no_suggest_attribute_flags="-Wno-suggest-attribute=format"
|
||||
],[
|
||||
ax_compiler_no_suggest_attribute_flags=""
|
||||
])
|
||||
|
||||
# Base flags
|
||||
AX_APPEND_COMPILE_FLAGS([ dnl
|
||||
-fno-strict-aliasing dnl
|
||||
$3 dnl
|
||||
],ax_warn_cflags_variable,[$ax_compiler_flags_test])
|
||||
|
||||
AS_IF([test "$ax_enable_compile_warnings" != "no"],[
|
||||
# "yes" flags
|
||||
AX_APPEND_COMPILE_FLAGS([ dnl
|
||||
-Wall dnl
|
||||
-Wextra dnl
|
||||
-Wundef dnl
|
||||
-Wwrite-strings dnl
|
||||
-Wpointer-arith dnl
|
||||
-Wmissing-declarations dnl
|
||||
-Wredundant-decls dnl
|
||||
-Wno-unused-parameter dnl
|
||||
-Wno-missing-field-initializers dnl
|
||||
-Wformat=2 dnl
|
||||
-Wcast-align dnl
|
||||
-Wformat-nonliteral dnl
|
||||
-Wformat-security dnl
|
||||
-Wsign-compare dnl
|
||||
-Wstrict-aliasing dnl
|
||||
-Wshadow dnl
|
||||
-Winline dnl
|
||||
-Wpacked dnl
|
||||
-Wmissing-format-attribute dnl
|
||||
-Wmissing-noreturn dnl
|
||||
-Winit-self dnl
|
||||
-Wredundant-decls dnl
|
||||
-Wmissing-include-dirs dnl
|
||||
-Wunused-but-set-variable dnl
|
||||
-Warray-bounds dnl
|
||||
-Wreturn-type dnl
|
||||
-Wswitch-enum dnl
|
||||
-Wswitch-default dnl
|
||||
-Wduplicated-cond dnl
|
||||
-Wduplicated-branches dnl
|
||||
-Wlogical-op dnl
|
||||
-Wrestrict dnl
|
||||
-Wnull-dereference dnl
|
||||
-Wdouble-promotion dnl
|
||||
$4 dnl
|
||||
$5 dnl
|
||||
$6 dnl
|
||||
$7 dnl
|
||||
],ax_warn_cflags_variable,[$ax_compiler_flags_test])
|
||||
if test "$ax_compiler_cxx" = "no" ; then
|
||||
# C-only flags. Warn in C++
|
||||
AX_APPEND_COMPILE_FLAGS([ dnl
|
||||
-Wnested-externs dnl
|
||||
-Wmissing-prototypes dnl
|
||||
-Wstrict-prototypes dnl
|
||||
-Wdeclaration-after-statement dnl
|
||||
-Wimplicit-function-declaration dnl
|
||||
-Wold-style-definition dnl
|
||||
-Wjump-misses-init dnl
|
||||
],ax_warn_cflags_variable,[$ax_compiler_flags_test])
|
||||
fi
|
||||
])
|
||||
AS_IF([test "$ax_enable_compile_warnings" = "error"],[
|
||||
# "error" flags; -Werror has to be appended unconditionally because
|
||||
# it's not possible to test for
|
||||
#
|
||||
# suggest-attribute=format is disabled because it gives too many false
|
||||
# positives
|
||||
AX_APPEND_FLAG([-Werror],ax_warn_cflags_variable)
|
||||
|
||||
AX_APPEND_COMPILE_FLAGS([ dnl
|
||||
[$ax_compiler_no_suggest_attribute_flags] dnl
|
||||
],ax_warn_cflags_variable,[$ax_compiler_flags_test])
|
||||
])
|
||||
|
||||
# In the flags below, when disabling specific flags, always add *both*
|
||||
# -Wno-foo and -Wno-error=foo. This fixes the situation where (for example)
|
||||
# we enable -Werror, disable a flag, and a build bot passes CFLAGS=-Wall,
|
||||
# which effectively turns that flag back on again as an error.
|
||||
for flag in $ax_warn_cflags_variable; do
|
||||
AS_CASE([$flag],
|
||||
[-Wno-*=*],[],
|
||||
[-Wno-*],[
|
||||
AX_APPEND_COMPILE_FLAGS([-Wno-error=$(AS_ECHO([$flag]) | $SED 's/^-Wno-//')],
|
||||
ax_warn_cflags_variable,
|
||||
[$ax_compiler_flags_test])
|
||||
])
|
||||
done
|
||||
|
||||
AC_LANG_POP([C])
|
||||
|
||||
# Substitute the variables
|
||||
AC_SUBST(ax_warn_cflags_variable)
|
||||
])dnl AX_COMPILER_FLAGS
|
|
@ -0,0 +1,60 @@
|
|||
# ===========================================================================
|
||||
# https://www.gnu.org/software/autoconf-archive/ax_compiler_flags_gir.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_COMPILER_FLAGS_GIR([VARIABLE], [IS-RELEASE], [EXTRA-BASE-FLAGS], [EXTRA-YES-FLAGS])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Add warning flags for the g-ir-scanner (from GObject Introspection) to
|
||||
# VARIABLE, which defaults to WARN_SCANNERFLAGS. VARIABLE is AC_SUBST-ed
|
||||
# by this macro, but must be manually added to the SCANNERFLAGS variable
|
||||
# for each GIR target in the code base.
|
||||
#
|
||||
# This macro depends on the environment set up by AX_COMPILER_FLAGS.
|
||||
# Specifically, it uses the value of $ax_enable_compile_warnings to decide
|
||||
# which flags to enable.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2015 Philip Withnall <philip@tecnocode.co.uk>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 6
|
||||
|
||||
AC_DEFUN([AX_COMPILER_FLAGS_GIR],[
|
||||
AX_REQUIRE_DEFINED([AX_APPEND_FLAG])
|
||||
|
||||
# Variable names
|
||||
m4_define([ax_warn_scannerflags_variable],
|
||||
[m4_normalize(ifelse([$1],,[WARN_SCANNERFLAGS],[$1]))])
|
||||
|
||||
# Base flags
|
||||
AX_APPEND_FLAG([$3],ax_warn_scannerflags_variable)
|
||||
|
||||
AS_IF([test "$ax_enable_compile_warnings" != "no"],[
|
||||
# "yes" flags
|
||||
AX_APPEND_FLAG([ dnl
|
||||
--warn-all dnl
|
||||
$4 dnl
|
||||
$5 dnl
|
||||
$6 dnl
|
||||
$7 dnl
|
||||
],ax_warn_scannerflags_variable)
|
||||
])
|
||||
AS_IF([test "$ax_enable_compile_warnings" = "error"],[
|
||||
# "error" flags
|
||||
AX_APPEND_FLAG([ dnl
|
||||
--warn-error dnl
|
||||
],ax_warn_scannerflags_variable)
|
||||
])
|
||||
|
||||
# Substitute the variables
|
||||
AC_SUBST(ax_warn_scannerflags_variable)
|
||||
])dnl AX_COMPILER_FLAGS
|
|
@ -0,0 +1,111 @@
|
|||
# ==============================================================================
|
||||
# https://www.gnu.org/software/autoconf-archive/ax_compiler_flags_ldflags.html
|
||||
# ==============================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_COMPILER_FLAGS_LDFLAGS([VARIABLE], [IS-RELEASE], [EXTRA-BASE-FLAGS], [EXTRA-YES-FLAGS])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Add warning flags for the linker to VARIABLE, which defaults to
|
||||
# WARN_LDFLAGS. VARIABLE is AC_SUBST-ed by this macro, but must be
|
||||
# manually added to the LDFLAGS variable for each target in the code base.
|
||||
#
|
||||
# This macro depends on the environment set up by AX_COMPILER_FLAGS.
|
||||
# Specifically, it uses the value of $ax_enable_compile_warnings to decide
|
||||
# which flags to enable.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2014, 2015 Philip Withnall <philip@tecnocode.co.uk>
|
||||
# Copyright (c) 2017, 2018 Reini Urban <rurban@cpan.org>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 9
|
||||
|
||||
AC_DEFUN([AX_COMPILER_FLAGS_LDFLAGS],[
|
||||
AX_REQUIRE_DEFINED([AX_APPEND_LINK_FLAGS])
|
||||
AX_REQUIRE_DEFINED([AX_APPEND_FLAG])
|
||||
AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG])
|
||||
AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG])
|
||||
|
||||
# Variable names
|
||||
m4_define([ax_warn_ldflags_variable],
|
||||
[m4_normalize(ifelse([$1],,[WARN_LDFLAGS],[$1]))])
|
||||
|
||||
# Always pass -Werror=unknown-warning-option to get Clang to fail on bad
|
||||
# flags, otherwise they are always appended to the warn_ldflags variable,
|
||||
# and Clang warns on them for every compilation unit.
|
||||
# If this is passed to GCC, it will explode, so the flag must be enabled
|
||||
# conditionally.
|
||||
AX_CHECK_COMPILE_FLAG([-Werror=unknown-warning-option],[
|
||||
ax_compiler_flags_test="-Werror=unknown-warning-option"
|
||||
],[
|
||||
ax_compiler_flags_test=""
|
||||
])
|
||||
|
||||
AX_CHECK_LINK_FLAG([-Wl,--as-needed], [
|
||||
AX_APPEND_LINK_FLAGS([-Wl,--as-needed],
|
||||
[AM_LDFLAGS],[$ax_compiler_flags_test])
|
||||
])
|
||||
AX_CHECK_LINK_FLAG([-Wl,-z,relro], [
|
||||
AX_APPEND_LINK_FLAGS([-Wl,-z,relro],
|
||||
[AM_LDFLAGS],[$ax_compiler_flags_test])
|
||||
])
|
||||
AX_CHECK_LINK_FLAG([-Wl,-z,now], [
|
||||
AX_APPEND_LINK_FLAGS([-Wl,-z,now],
|
||||
[AM_LDFLAGS],[$ax_compiler_flags_test])
|
||||
])
|
||||
AX_CHECK_LINK_FLAG([-Wl,-z,noexecstack], [
|
||||
AX_APPEND_LINK_FLAGS([-Wl,-z,noexecstack],
|
||||
[AM_LDFLAGS],[$ax_compiler_flags_test])
|
||||
])
|
||||
# textonly, retpolineplt not yet
|
||||
|
||||
# macOS and cygwin linker do not have --as-needed
|
||||
AX_CHECK_LINK_FLAG([-Wl,--no-as-needed], [
|
||||
ax_compiler_flags_as_needed_option="-Wl,--no-as-needed"
|
||||
], [
|
||||
ax_compiler_flags_as_needed_option=""
|
||||
])
|
||||
|
||||
# macOS linker speaks with a different accent
|
||||
ax_compiler_flags_fatal_warnings_option=""
|
||||
AX_CHECK_LINK_FLAG([-Wl,--fatal-warnings], [
|
||||
ax_compiler_flags_fatal_warnings_option="-Wl,--fatal-warnings"
|
||||
])
|
||||
AX_CHECK_LINK_FLAG([-Wl,-fatal_warnings], [
|
||||
ax_compiler_flags_fatal_warnings_option="-Wl,-fatal_warnings"
|
||||
])
|
||||
|
||||
# Base flags
|
||||
AX_APPEND_LINK_FLAGS([ dnl
|
||||
$ax_compiler_flags_as_needed_option dnl
|
||||
$3 dnl
|
||||
],ax_warn_ldflags_variable,[$ax_compiler_flags_test])
|
||||
|
||||
AS_IF([test "$ax_enable_compile_warnings" != "no"],[
|
||||
# "yes" flags
|
||||
AX_APPEND_LINK_FLAGS([$4 $5 $6 $7],
|
||||
ax_warn_ldflags_variable,
|
||||
[$ax_compiler_flags_test])
|
||||
])
|
||||
AS_IF([test "$ax_enable_compile_warnings" = "error"],[
|
||||
# "error" flags; -Werror has to be appended unconditionally because
|
||||
# it's not possible to test for
|
||||
#
|
||||
# suggest-attribute=format is disabled because it gives too many false
|
||||
# positives
|
||||
AX_APPEND_LINK_FLAGS([ dnl
|
||||
$ax_compiler_flags_fatal_warnings_option dnl
|
||||
],ax_warn_ldflags_variable,[$ax_compiler_flags_test])
|
||||
])
|
||||
|
||||
# Substitute the variables
|
||||
AC_SUBST(ax_warn_ldflags_variable)
|
||||
])dnl AX_COMPILER_FLAGS
|
|
@ -0,0 +1,80 @@
|
|||
# ===========================================================================
|
||||
# https://www.gnu.org/software/autoconf-archive/ax_is_release.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_IS_RELEASE(POLICY)
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Determine whether the code is being configured as a release, or from
|
||||
# git. Set the ax_is_release variable to 'yes' or 'no'.
|
||||
#
|
||||
# If building a release version, it is recommended that the configure
|
||||
# script disable compiler errors and debug features, by conditionalising
|
||||
# them on the ax_is_release variable. If building from git, these
|
||||
# features should be enabled.
|
||||
#
|
||||
# The POLICY parameter specifies how ax_is_release is determined. It can
|
||||
# take the following values:
|
||||
#
|
||||
# * git-directory: ax_is_release will be 'no' if a '.git' directory exists
|
||||
# * minor-version: ax_is_release will be 'no' if the minor version number
|
||||
# in $PACKAGE_VERSION is odd; this assumes
|
||||
# $PACKAGE_VERSION follows the 'major.minor.micro' scheme
|
||||
# * micro-version: ax_is_release will be 'no' if the micro version number
|
||||
# in $PACKAGE_VERSION is odd; this assumes
|
||||
# $PACKAGE_VERSION follows the 'major.minor.micro' scheme
|
||||
# * dash-version: ax_is_release will be 'no' if there is a dash '-'
|
||||
# in $PACKAGE_VERSION, for example 1.2-pre3, 1.2.42-a8b9
|
||||
# or 2.0-dirty (in particular this is suitable for use
|
||||
# with git-version-gen)
|
||||
# * always: ax_is_release will always be 'yes'
|
||||
# * never: ax_is_release will always be 'no'
|
||||
#
|
||||
# Other policies may be added in future.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2015 Philip Withnall <philip@tecnocode.co.uk>
|
||||
# Copyright (c) 2016 Collabora Ltd.
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved.
|
||||
|
||||
#serial 7
|
||||
|
||||
AC_DEFUN([AX_IS_RELEASE],[
|
||||
AC_BEFORE([AC_INIT],[$0])
|
||||
|
||||
m4_case([$1],
|
||||
[git-directory],[
|
||||
# $is_release = (.git directory does not exist)
|
||||
AS_IF([test -d ${srcdir}/.git],[ax_is_release=no],[ax_is_release=yes])
|
||||
],
|
||||
[minor-version],[
|
||||
# $is_release = ($minor_version is even)
|
||||
minor_version=`echo "$PACKAGE_VERSION" | sed 's/[[^.]][[^.]]*.\([[^.]][[^.]]*\).*/\1/'`
|
||||
AS_IF([test "$(( $minor_version % 2 ))" -ne 0],
|
||||
[ax_is_release=no],[ax_is_release=yes])
|
||||
],
|
||||
[micro-version],[
|
||||
# $is_release = ($micro_version is even)
|
||||
micro_version=`echo "$PACKAGE_VERSION" | sed 's/[[^.]]*\.[[^.]]*\.\([[^.]]*\).*/\1/'`
|
||||
AS_IF([test "$(( $micro_version % 2 ))" -ne 0],
|
||||
[ax_is_release=no],[ax_is_release=yes])
|
||||
],
|
||||
[dash-version],[
|
||||
# $is_release = ($PACKAGE_VERSION has a dash)
|
||||
AS_CASE([$PACKAGE_VERSION],
|
||||
[*-*], [ax_is_release=no],
|
||||
[*], [ax_is_release=yes])
|
||||
],
|
||||
[always],[ax_is_release=yes],
|
||||
[never],[ax_is_release=no],
|
||||
[
|
||||
AC_MSG_ERROR([Invalid policy. Valid policies: git-directory, minor-version, micro-version, dash-version, always, never.])
|
||||
])
|
||||
])
|
|
@ -0,0 +1,37 @@
|
|||
# ===========================================================================
|
||||
# https://www.gnu.org/software/autoconf-archive/ax_require_defined.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_REQUIRE_DEFINED(MACRO)
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# AX_REQUIRE_DEFINED is a simple helper for making sure other macros have
|
||||
# been defined and thus are available for use. This avoids random issues
|
||||
# where a macro isn't expanded. Instead the configure script emits a
|
||||
# non-fatal:
|
||||
#
|
||||
# ./configure: line 1673: AX_CFLAGS_WARN_ALL: command not found
|
||||
#
|
||||
# It's like AC_REQUIRE except it doesn't expand the required macro.
|
||||
#
|
||||
# Here's an example:
|
||||
#
|
||||
# AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG])
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2014 Mike Frysinger <vapier@gentoo.org>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 2
|
||||
|
||||
AC_DEFUN([AX_REQUIRE_DEFINED], [dnl
|
||||
m4_ifndef([$1], [m4_fatal([macro ]$1[ is not defined; is a m4 file missing?])])
|
||||
])dnl AX_REQUIRE_DEFINED
|
|
@ -0,0 +1,28 @@
|
|||
dnl GLIB_TESTS
|
||||
dnl
|
||||
|
||||
AC_DEFUN([GLIB_TESTS],
|
||||
[
|
||||
AC_ARG_ENABLE(installed-tests,
|
||||
AS_HELP_STRING([--enable-installed-tests],
|
||||
[Enable installation of some test cases]),
|
||||
[case ${enableval} in
|
||||
yes) ENABLE_INSTALLED_TESTS="1" ;;
|
||||
no) ENABLE_INSTALLED_TESTS="" ;;
|
||||
*) AC_MSG_ERROR([bad value ${enableval} for --enable-installed-tests]) ;;
|
||||
esac])
|
||||
AM_CONDITIONAL([ENABLE_INSTALLED_TESTS], test "$ENABLE_INSTALLED_TESTS" = "1")
|
||||
AC_ARG_ENABLE(always-build-tests,
|
||||
AS_HELP_STRING([--enable-always-build-tests],
|
||||
[Enable always building tests during 'make all']),
|
||||
[case ${enableval} in
|
||||
yes) ENABLE_ALWAYS_BUILD_TESTS="1" ;;
|
||||
no) ENABLE_ALWAYS_BUILD_TESTS="" ;;
|
||||
*) AC_MSG_ERROR([bad value ${enableval} for --enable-always-build-tests]) ;;
|
||||
esac])
|
||||
AM_CONDITIONAL([ENABLE_ALWAYS_BUILD_TESTS], test "$ENABLE_ALWAYS_BUILD_TESTS" = "1")
|
||||
if test "$ENABLE_INSTALLED_TESTS" = "1"; then
|
||||
AC_SUBST(installed_test_metadir, [${datadir}/installed-tests/]AC_PACKAGE_NAME)
|
||||
AC_SUBST(installed_testdir, [${libexecdir}/installed-tests/]AC_PACKAGE_NAME)
|
||||
fi
|
||||
])
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,437 @@
|
|||
# Helper functions for option handling. -*- Autoconf -*-
|
||||
#
|
||||
# Copyright (C) 2004-2005, 2007-2009, 2011-2015 Free Software
|
||||
# Foundation, Inc.
|
||||
# Written by Gary V. Vaughan, 2004
|
||||
#
|
||||
# This file is free software; the Free Software Foundation gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
|
||||
# serial 8 ltoptions.m4
|
||||
|
||||
# This is to help aclocal find these macros, as it can't see m4_define.
|
||||
AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])])
|
||||
|
||||
|
||||
# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME)
|
||||
# ------------------------------------------
|
||||
m4_define([_LT_MANGLE_OPTION],
|
||||
[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])])
|
||||
|
||||
|
||||
# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME)
|
||||
# ---------------------------------------
|
||||
# Set option OPTION-NAME for macro MACRO-NAME, and if there is a
|
||||
# matching handler defined, dispatch to it. Other OPTION-NAMEs are
|
||||
# saved as a flag.
|
||||
m4_define([_LT_SET_OPTION],
|
||||
[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl
|
||||
m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]),
|
||||
_LT_MANGLE_DEFUN([$1], [$2]),
|
||||
[m4_warning([Unknown $1 option '$2'])])[]dnl
|
||||
])
|
||||
|
||||
|
||||
# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET])
|
||||
# ------------------------------------------------------------
|
||||
# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
|
||||
m4_define([_LT_IF_OPTION],
|
||||
[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])])
|
||||
|
||||
|
||||
# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET)
|
||||
# -------------------------------------------------------
|
||||
# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME
|
||||
# are set.
|
||||
m4_define([_LT_UNLESS_OPTIONS],
|
||||
[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
|
||||
[m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option),
|
||||
[m4_define([$0_found])])])[]dnl
|
||||
m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3
|
||||
])[]dnl
|
||||
])
|
||||
|
||||
|
||||
# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST)
|
||||
# ----------------------------------------
|
||||
# OPTION-LIST is a space-separated list of Libtool options associated
|
||||
# with MACRO-NAME. If any OPTION has a matching handler declared with
|
||||
# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about
|
||||
# the unknown option and exit.
|
||||
m4_defun([_LT_SET_OPTIONS],
|
||||
[# Set options
|
||||
m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
|
||||
[_LT_SET_OPTION([$1], _LT_Option)])
|
||||
|
||||
m4_if([$1],[LT_INIT],[
|
||||
dnl
|
||||
dnl Simply set some default values (i.e off) if boolean options were not
|
||||
dnl specified:
|
||||
_LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no
|
||||
])
|
||||
_LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no
|
||||
])
|
||||
dnl
|
||||
dnl If no reference was made to various pairs of opposing options, then
|
||||
dnl we run the default mode handler for the pair. For example, if neither
|
||||
dnl 'shared' nor 'disable-shared' was passed, we enable building of shared
|
||||
dnl archives by default:
|
||||
_LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED])
|
||||
_LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC])
|
||||
_LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC])
|
||||
_LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install],
|
||||
[_LT_ENABLE_FAST_INSTALL])
|
||||
_LT_UNLESS_OPTIONS([LT_INIT], [aix-soname=aix aix-soname=both aix-soname=svr4],
|
||||
[_LT_WITH_AIX_SONAME([aix])])
|
||||
])
|
||||
])# _LT_SET_OPTIONS
|
||||
|
||||
|
||||
## --------------------------------- ##
|
||||
## Macros to handle LT_INIT options. ##
|
||||
## --------------------------------- ##
|
||||
|
||||
# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME)
|
||||
# -----------------------------------------
|
||||
m4_define([_LT_MANGLE_DEFUN],
|
||||
[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])])
|
||||
|
||||
|
||||
# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE)
|
||||
# -----------------------------------------------
|
||||
m4_define([LT_OPTION_DEFINE],
|
||||
[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl
|
||||
])# LT_OPTION_DEFINE
|
||||
|
||||
|
||||
# dlopen
|
||||
# ------
|
||||
LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes
|
||||
])
|
||||
|
||||
AU_DEFUN([AC_LIBTOOL_DLOPEN],
|
||||
[_LT_SET_OPTION([LT_INIT], [dlopen])
|
||||
AC_DIAGNOSE([obsolete],
|
||||
[$0: Remove this warning and the call to _LT_SET_OPTION when you
|
||||
put the 'dlopen' option into LT_INIT's first parameter.])
|
||||
])
|
||||
|
||||
dnl aclocal-1.4 backwards compatibility:
|
||||
dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], [])
|
||||
|
||||
|
||||
# win32-dll
|
||||
# ---------
|
||||
# Declare package support for building win32 dll's.
|
||||
LT_OPTION_DEFINE([LT_INIT], [win32-dll],
|
||||
[enable_win32_dll=yes
|
||||
|
||||
case $host in
|
||||
*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*)
|
||||
AC_CHECK_TOOL(AS, as, false)
|
||||
AC_CHECK_TOOL(DLLTOOL, dlltool, false)
|
||||
AC_CHECK_TOOL(OBJDUMP, objdump, false)
|
||||
;;
|
||||
esac
|
||||
|
||||
test -z "$AS" && AS=as
|
||||
_LT_DECL([], [AS], [1], [Assembler program])dnl
|
||||
|
||||
test -z "$DLLTOOL" && DLLTOOL=dlltool
|
||||
_LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl
|
||||
|
||||
test -z "$OBJDUMP" && OBJDUMP=objdump
|
||||
_LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl
|
||||
])# win32-dll
|
||||
|
||||
AU_DEFUN([AC_LIBTOOL_WIN32_DLL],
|
||||
[AC_REQUIRE([AC_CANONICAL_HOST])dnl
|
||||
_LT_SET_OPTION([LT_INIT], [win32-dll])
|
||||
AC_DIAGNOSE([obsolete],
|
||||
[$0: Remove this warning and the call to _LT_SET_OPTION when you
|
||||
put the 'win32-dll' option into LT_INIT's first parameter.])
|
||||
])
|
||||
|
||||
dnl aclocal-1.4 backwards compatibility:
|
||||
dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [])
|
||||
|
||||
|
||||
# _LT_ENABLE_SHARED([DEFAULT])
|
||||
# ----------------------------
|
||||
# implement the --enable-shared flag, and supports the 'shared' and
|
||||
# 'disable-shared' LT_INIT options.
|
||||
# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'.
|
||||
m4_define([_LT_ENABLE_SHARED],
|
||||
[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl
|
||||
AC_ARG_ENABLE([shared],
|
||||
[AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@],
|
||||
[build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])],
|
||||
[p=${PACKAGE-default}
|
||||
case $enableval in
|
||||
yes) enable_shared=yes ;;
|
||||
no) enable_shared=no ;;
|
||||
*)
|
||||
enable_shared=no
|
||||
# Look at the argument we got. We use all the common list separators.
|
||||
lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
|
||||
for pkg in $enableval; do
|
||||
IFS=$lt_save_ifs
|
||||
if test "X$pkg" = "X$p"; then
|
||||
enable_shared=yes
|
||||
fi
|
||||
done
|
||||
IFS=$lt_save_ifs
|
||||
;;
|
||||
esac],
|
||||
[enable_shared=]_LT_ENABLE_SHARED_DEFAULT)
|
||||
|
||||
_LT_DECL([build_libtool_libs], [enable_shared], [0],
|
||||
[Whether or not to build shared libraries])
|
||||
])# _LT_ENABLE_SHARED
|
||||
|
||||
LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])])
|
||||
LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])])
|
||||
|
||||
# Old names:
|
||||
AC_DEFUN([AC_ENABLE_SHARED],
|
||||
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared])
|
||||
])
|
||||
|
||||
AC_DEFUN([AC_DISABLE_SHARED],
|
||||
[_LT_SET_OPTION([LT_INIT], [disable-shared])
|
||||
])
|
||||
|
||||
AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)])
|
||||
AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)])
|
||||
|
||||
dnl aclocal-1.4 backwards compatibility:
|
||||
dnl AC_DEFUN([AM_ENABLE_SHARED], [])
|
||||
dnl AC_DEFUN([AM_DISABLE_SHARED], [])
|
||||
|
||||
|
||||
|
||||
# _LT_ENABLE_STATIC([DEFAULT])
|
||||
# ----------------------------
|
||||
# implement the --enable-static flag, and support the 'static' and
|
||||
# 'disable-static' LT_INIT options.
|
||||
# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'.
|
||||
m4_define([_LT_ENABLE_STATIC],
|
||||
[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl
|
||||
AC_ARG_ENABLE([static],
|
||||
[AS_HELP_STRING([--enable-static@<:@=PKGS@:>@],
|
||||
[build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])],
|
||||
[p=${PACKAGE-default}
|
||||
case $enableval in
|
||||
yes) enable_static=yes ;;
|
||||
no) enable_static=no ;;
|
||||
*)
|
||||
enable_static=no
|
||||
# Look at the argument we got. We use all the common list separators.
|
||||
lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
|
||||
for pkg in $enableval; do
|
||||
IFS=$lt_save_ifs
|
||||
if test "X$pkg" = "X$p"; then
|
||||
enable_static=yes
|
||||
fi
|
||||
done
|
||||
IFS=$lt_save_ifs
|
||||
;;
|
||||
esac],
|
||||
[enable_static=]_LT_ENABLE_STATIC_DEFAULT)
|
||||
|
||||
_LT_DECL([build_old_libs], [enable_static], [0],
|
||||
[Whether or not to build static libraries])
|
||||
])# _LT_ENABLE_STATIC
|
||||
|
||||
LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])])
|
||||
LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])])
|
||||
|
||||
# Old names:
|
||||
AC_DEFUN([AC_ENABLE_STATIC],
|
||||
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static])
|
||||
])
|
||||
|
||||
AC_DEFUN([AC_DISABLE_STATIC],
|
||||
[_LT_SET_OPTION([LT_INIT], [disable-static])
|
||||
])
|
||||
|
||||
AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)])
|
||||
AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)])
|
||||
|
||||
dnl aclocal-1.4 backwards compatibility:
|
||||
dnl AC_DEFUN([AM_ENABLE_STATIC], [])
|
||||
dnl AC_DEFUN([AM_DISABLE_STATIC], [])
|
||||
|
||||
|
||||
|
||||
# _LT_ENABLE_FAST_INSTALL([DEFAULT])
|
||||
# ----------------------------------
|
||||
# implement the --enable-fast-install flag, and support the 'fast-install'
|
||||
# and 'disable-fast-install' LT_INIT options.
|
||||
# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'.
|
||||
m4_define([_LT_ENABLE_FAST_INSTALL],
|
||||
[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl
|
||||
AC_ARG_ENABLE([fast-install],
|
||||
[AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@],
|
||||
[optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])],
|
||||
[p=${PACKAGE-default}
|
||||
case $enableval in
|
||||
yes) enable_fast_install=yes ;;
|
||||
no) enable_fast_install=no ;;
|
||||
*)
|
||||
enable_fast_install=no
|
||||
# Look at the argument we got. We use all the common list separators.
|
||||
lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
|
||||
for pkg in $enableval; do
|
||||
IFS=$lt_save_ifs
|
||||
if test "X$pkg" = "X$p"; then
|
||||
enable_fast_install=yes
|
||||
fi
|
||||
done
|
||||
IFS=$lt_save_ifs
|
||||
;;
|
||||
esac],
|
||||
[enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT)
|
||||
|
||||
_LT_DECL([fast_install], [enable_fast_install], [0],
|
||||
[Whether or not to optimize for fast installation])dnl
|
||||
])# _LT_ENABLE_FAST_INSTALL
|
||||
|
||||
LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])])
|
||||
LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])])
|
||||
|
||||
# Old names:
|
||||
AU_DEFUN([AC_ENABLE_FAST_INSTALL],
|
||||
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install])
|
||||
AC_DIAGNOSE([obsolete],
|
||||
[$0: Remove this warning and the call to _LT_SET_OPTION when you put
|
||||
the 'fast-install' option into LT_INIT's first parameter.])
|
||||
])
|
||||
|
||||
AU_DEFUN([AC_DISABLE_FAST_INSTALL],
|
||||
[_LT_SET_OPTION([LT_INIT], [disable-fast-install])
|
||||
AC_DIAGNOSE([obsolete],
|
||||
[$0: Remove this warning and the call to _LT_SET_OPTION when you put
|
||||
the 'disable-fast-install' option into LT_INIT's first parameter.])
|
||||
])
|
||||
|
||||
dnl aclocal-1.4 backwards compatibility:
|
||||
dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], [])
|
||||
dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], [])
|
||||
|
||||
|
||||
# _LT_WITH_AIX_SONAME([DEFAULT])
|
||||
# ----------------------------------
|
||||
# implement the --with-aix-soname flag, and support the `aix-soname=aix'
|
||||
# and `aix-soname=both' and `aix-soname=svr4' LT_INIT options. DEFAULT
|
||||
# is either `aix', `both' or `svr4'. If omitted, it defaults to `aix'.
|
||||
m4_define([_LT_WITH_AIX_SONAME],
|
||||
[m4_define([_LT_WITH_AIX_SONAME_DEFAULT], [m4_if($1, svr4, svr4, m4_if($1, both, both, aix))])dnl
|
||||
shared_archive_member_spec=
|
||||
case $host,$enable_shared in
|
||||
power*-*-aix[[5-9]]*,yes)
|
||||
AC_MSG_CHECKING([which variant of shared library versioning to provide])
|
||||
AC_ARG_WITH([aix-soname],
|
||||
[AS_HELP_STRING([--with-aix-soname=aix|svr4|both],
|
||||
[shared library versioning (aka "SONAME") variant to provide on AIX, @<:@default=]_LT_WITH_AIX_SONAME_DEFAULT[@:>@.])],
|
||||
[case $withval in
|
||||
aix|svr4|both)
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([Unknown argument to --with-aix-soname])
|
||||
;;
|
||||
esac
|
||||
lt_cv_with_aix_soname=$with_aix_soname],
|
||||
[AC_CACHE_VAL([lt_cv_with_aix_soname],
|
||||
[lt_cv_with_aix_soname=]_LT_WITH_AIX_SONAME_DEFAULT)
|
||||
with_aix_soname=$lt_cv_with_aix_soname])
|
||||
AC_MSG_RESULT([$with_aix_soname])
|
||||
if test aix != "$with_aix_soname"; then
|
||||
# For the AIX way of multilib, we name the shared archive member
|
||||
# based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o',
|
||||
# and 'shr.imp' or 'shr_64.imp', respectively, for the Import File.
|
||||
# Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag,
|
||||
# the AIX toolchain works better with OBJECT_MODE set (default 32).
|
||||
if test 64 = "${OBJECT_MODE-32}"; then
|
||||
shared_archive_member_spec=shr_64
|
||||
else
|
||||
shared_archive_member_spec=shr
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
with_aix_soname=aix
|
||||
;;
|
||||
esac
|
||||
|
||||
_LT_DECL([], [shared_archive_member_spec], [0],
|
||||
[Shared archive member basename, for filename based shared library versioning on AIX])dnl
|
||||
])# _LT_WITH_AIX_SONAME
|
||||
|
||||
LT_OPTION_DEFINE([LT_INIT], [aix-soname=aix], [_LT_WITH_AIX_SONAME([aix])])
|
||||
LT_OPTION_DEFINE([LT_INIT], [aix-soname=both], [_LT_WITH_AIX_SONAME([both])])
|
||||
LT_OPTION_DEFINE([LT_INIT], [aix-soname=svr4], [_LT_WITH_AIX_SONAME([svr4])])
|
||||
|
||||
|
||||
# _LT_WITH_PIC([MODE])
|
||||
# --------------------
|
||||
# implement the --with-pic flag, and support the 'pic-only' and 'no-pic'
|
||||
# LT_INIT options.
|
||||
# MODE is either 'yes' or 'no'. If omitted, it defaults to 'both'.
|
||||
m4_define([_LT_WITH_PIC],
|
||||
[AC_ARG_WITH([pic],
|
||||
[AS_HELP_STRING([--with-pic@<:@=PKGS@:>@],
|
||||
[try to use only PIC/non-PIC objects @<:@default=use both@:>@])],
|
||||
[lt_p=${PACKAGE-default}
|
||||
case $withval in
|
||||
yes|no) pic_mode=$withval ;;
|
||||
*)
|
||||
pic_mode=default
|
||||
# Look at the argument we got. We use all the common list separators.
|
||||
lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
|
||||
for lt_pkg in $withval; do
|
||||
IFS=$lt_save_ifs
|
||||
if test "X$lt_pkg" = "X$lt_p"; then
|
||||
pic_mode=yes
|
||||
fi
|
||||
done
|
||||
IFS=$lt_save_ifs
|
||||
;;
|
||||
esac],
|
||||
[pic_mode=m4_default([$1], [default])])
|
||||
|
||||
_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl
|
||||
])# _LT_WITH_PIC
|
||||
|
||||
LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])])
|
||||
LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])])
|
||||
|
||||
# Old name:
|
||||
AU_DEFUN([AC_LIBTOOL_PICMODE],
|
||||
[_LT_SET_OPTION([LT_INIT], [pic-only])
|
||||
AC_DIAGNOSE([obsolete],
|
||||
[$0: Remove this warning and the call to _LT_SET_OPTION when you
|
||||
put the 'pic-only' option into LT_INIT's first parameter.])
|
||||
])
|
||||
|
||||
dnl aclocal-1.4 backwards compatibility:
|
||||
dnl AC_DEFUN([AC_LIBTOOL_PICMODE], [])
|
||||
|
||||
## ----------------- ##
|
||||
## LTDL_INIT Options ##
|
||||
## ----------------- ##
|
||||
|
||||
m4_define([_LTDL_MODE], [])
|
||||
LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive],
|
||||
[m4_define([_LTDL_MODE], [nonrecursive])])
|
||||
LT_OPTION_DEFINE([LTDL_INIT], [recursive],
|
||||
[m4_define([_LTDL_MODE], [recursive])])
|
||||
LT_OPTION_DEFINE([LTDL_INIT], [subproject],
|
||||
[m4_define([_LTDL_MODE], [subproject])])
|
||||
|
||||
m4_define([_LTDL_TYPE], [])
|
||||
LT_OPTION_DEFINE([LTDL_INIT], [installable],
|
||||
[m4_define([_LTDL_TYPE], [installable])])
|
||||
LT_OPTION_DEFINE([LTDL_INIT], [convenience],
|
||||
[m4_define([_LTDL_TYPE], [convenience])])
|
|
@ -0,0 +1,124 @@
|
|||
# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*-
|
||||
#
|
||||
# Copyright (C) 2004-2005, 2007-2008, 2011-2015 Free Software
|
||||
# Foundation, Inc.
|
||||
# Written by Gary V. Vaughan, 2004
|
||||
#
|
||||
# This file is free software; the Free Software Foundation gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
|
||||
# serial 6 ltsugar.m4
|
||||
|
||||
# This is to help aclocal find these macros, as it can't see m4_define.
|
||||
AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])])
|
||||
|
||||
|
||||
# lt_join(SEP, ARG1, [ARG2...])
|
||||
# -----------------------------
|
||||
# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their
|
||||
# associated separator.
|
||||
# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier
|
||||
# versions in m4sugar had bugs.
|
||||
m4_define([lt_join],
|
||||
[m4_if([$#], [1], [],
|
||||
[$#], [2], [[$2]],
|
||||
[m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])])
|
||||
m4_define([_lt_join],
|
||||
[m4_if([$#$2], [2], [],
|
||||
[m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])])
|
||||
|
||||
|
||||
# lt_car(LIST)
|
||||
# lt_cdr(LIST)
|
||||
# ------------
|
||||
# Manipulate m4 lists.
|
||||
# These macros are necessary as long as will still need to support
|
||||
# Autoconf-2.59, which quotes differently.
|
||||
m4_define([lt_car], [[$1]])
|
||||
m4_define([lt_cdr],
|
||||
[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])],
|
||||
[$#], 1, [],
|
||||
[m4_dquote(m4_shift($@))])])
|
||||
m4_define([lt_unquote], $1)
|
||||
|
||||
|
||||
# lt_append(MACRO-NAME, STRING, [SEPARATOR])
|
||||
# ------------------------------------------
|
||||
# Redefine MACRO-NAME to hold its former content plus 'SEPARATOR''STRING'.
|
||||
# Note that neither SEPARATOR nor STRING are expanded; they are appended
|
||||
# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked).
|
||||
# No SEPARATOR is output if MACRO-NAME was previously undefined (different
|
||||
# than defined and empty).
|
||||
#
|
||||
# This macro is needed until we can rely on Autoconf 2.62, since earlier
|
||||
# versions of m4sugar mistakenly expanded SEPARATOR but not STRING.
|
||||
m4_define([lt_append],
|
||||
[m4_define([$1],
|
||||
m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])])
|
||||
|
||||
|
||||
|
||||
# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...])
|
||||
# ----------------------------------------------------------
|
||||
# Produce a SEP delimited list of all paired combinations of elements of
|
||||
# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list
|
||||
# has the form PREFIXmINFIXSUFFIXn.
|
||||
# Needed until we can rely on m4_combine added in Autoconf 2.62.
|
||||
m4_define([lt_combine],
|
||||
[m4_if(m4_eval([$# > 3]), [1],
|
||||
[m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl
|
||||
[[m4_foreach([_Lt_prefix], [$2],
|
||||
[m4_foreach([_Lt_suffix],
|
||||
]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[,
|
||||
[_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])])
|
||||
|
||||
|
||||
# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ])
|
||||
# -----------------------------------------------------------------------
|
||||
# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited
|
||||
# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ.
|
||||
m4_define([lt_if_append_uniq],
|
||||
[m4_ifdef([$1],
|
||||
[m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1],
|
||||
[lt_append([$1], [$2], [$3])$4],
|
||||
[$5])],
|
||||
[lt_append([$1], [$2], [$3])$4])])
|
||||
|
||||
|
||||
# lt_dict_add(DICT, KEY, VALUE)
|
||||
# -----------------------------
|
||||
m4_define([lt_dict_add],
|
||||
[m4_define([$1($2)], [$3])])
|
||||
|
||||
|
||||
# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE)
|
||||
# --------------------------------------------
|
||||
m4_define([lt_dict_add_subkey],
|
||||
[m4_define([$1($2:$3)], [$4])])
|
||||
|
||||
|
||||
# lt_dict_fetch(DICT, KEY, [SUBKEY])
|
||||
# ----------------------------------
|
||||
m4_define([lt_dict_fetch],
|
||||
[m4_ifval([$3],
|
||||
m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]),
|
||||
m4_ifdef([$1($2)], [m4_defn([$1($2)])]))])
|
||||
|
||||
|
||||
# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE])
|
||||
# -----------------------------------------------------------------
|
||||
m4_define([lt_if_dict_fetch],
|
||||
[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4],
|
||||
[$5],
|
||||
[$6])])
|
||||
|
||||
|
||||
# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...])
|
||||
# --------------------------------------------------------------
|
||||
m4_define([lt_dict_filter],
|
||||
[m4_if([$5], [], [],
|
||||
[lt_join(m4_quote(m4_default([$4], [[, ]])),
|
||||
lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]),
|
||||
[lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl
|
||||
])
|
|
@ -0,0 +1,23 @@
|
|||
# ltversion.m4 -- version numbers -*- Autoconf -*-
|
||||
#
|
||||
# Copyright (C) 2004, 2011-2015 Free Software Foundation, Inc.
|
||||
# Written by Scott James Remnant, 2004
|
||||
#
|
||||
# This file is free software; the Free Software Foundation gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
|
||||
# @configure_input@
|
||||
|
||||
# serial 4179 ltversion.m4
|
||||
# This file is part of GNU Libtool
|
||||
|
||||
m4_define([LT_PACKAGE_VERSION], [2.4.6])
|
||||
m4_define([LT_PACKAGE_REVISION], [2.4.6])
|
||||
|
||||
AC_DEFUN([LTVERSION_VERSION],
|
||||
[macro_version='2.4.6'
|
||||
macro_revision='2.4.6'
|
||||
_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?])
|
||||
_LT_DECL(, macro_revision, 0)
|
||||
])
|
|
@ -0,0 +1,99 @@
|
|||
# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*-
|
||||
#
|
||||
# Copyright (C) 2004-2005, 2007, 2009, 2011-2015 Free Software
|
||||
# Foundation, Inc.
|
||||
# Written by Scott James Remnant, 2004.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
|
||||
# serial 5 lt~obsolete.m4
|
||||
|
||||
# These exist entirely to fool aclocal when bootstrapping libtool.
|
||||
#
|
||||
# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN),
|
||||
# which have later been changed to m4_define as they aren't part of the
|
||||
# exported API, or moved to Autoconf or Automake where they belong.
|
||||
#
|
||||
# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN
|
||||
# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us
|
||||
# using a macro with the same name in our local m4/libtool.m4 it'll
|
||||
# pull the old libtool.m4 in (it doesn't see our shiny new m4_define
|
||||
# and doesn't know about Autoconf macros at all.)
|
||||
#
|
||||
# So we provide this file, which has a silly filename so it's always
|
||||
# included after everything else. This provides aclocal with the
|
||||
# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything
|
||||
# because those macros already exist, or will be overwritten later.
|
||||
# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6.
|
||||
#
|
||||
# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here.
|
||||
# Yes, that means every name once taken will need to remain here until
|
||||
# we give up compatibility with versions before 1.7, at which point
|
||||
# we need to keep only those names which we still refer to.
|
||||
|
||||
# This is to help aclocal find these macros, as it can't see m4_define.
|
||||
AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])])
|
||||
|
||||
m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])])
|
||||
m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])])
|
||||
m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])])
|
||||
m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])])
|
||||
m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])])
|
||||
m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])])
|
||||
m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])])
|
||||
m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])])
|
||||
m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])])
|
||||
m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])])
|
||||
m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])])
|
||||
m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])])
|
||||
m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])])
|
||||
m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])])
|
||||
m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])])
|
||||
m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])])
|
||||
m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])])
|
||||
m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])])
|
||||
m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])])
|
||||
m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])])
|
||||
m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])])
|
||||
m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])])
|
||||
m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])])
|
||||
m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])])
|
||||
m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])])
|
||||
m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])])
|
||||
m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])])
|
||||
m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])])
|
||||
m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])])
|
||||
m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])])
|
||||
m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])])
|
||||
m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])])
|
||||
m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])])
|
||||
m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])])
|
||||
m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])])
|
||||
m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])])
|
||||
m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])])
|
||||
m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])])
|
||||
m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])])
|
||||
m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])])
|
||||
m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])])
|
||||
m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])])
|
||||
m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])])
|
||||
m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])])
|
||||
m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])])
|
||||
m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])])
|
||||
m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])])
|
||||
m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])])
|
||||
m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])])
|
||||
m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])])
|
||||
m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])])
|
||||
m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])])
|
||||
m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])])
|
||||
m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])])
|
||||
m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])])
|
||||
m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])])
|
||||
m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])])
|
||||
m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])])
|
||||
m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])])
|
||||
m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])])
|
||||
m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])])
|
|
@ -0,0 +1,343 @@
|
|||
# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
|
||||
# serial 11 (pkg-config-0.29.1)
|
||||
|
||||
dnl Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
|
||||
dnl Copyright © 2012-2015 Dan Nicholson <dbn.lists@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 of the License, or
|
||||
dnl (at your option) any later version.
|
||||
dnl
|
||||
dnl This program is distributed in the hope that it will be useful, but
|
||||
dnl WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
dnl 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., 59 Temple Place - Suite 330, Boston, MA
|
||||
dnl 02111-1307, USA.
|
||||
dnl
|
||||
dnl As a special exception to the GNU General Public License, if you
|
||||
dnl distribute this file as part of a program that contains a
|
||||
dnl configuration script generated by Autoconf, you may include it under
|
||||
dnl the same distribution terms that you use for the rest of that
|
||||
dnl program.
|
||||
|
||||
dnl PKG_PREREQ(MIN-VERSION)
|
||||
dnl -----------------------
|
||||
dnl Since: 0.29
|
||||
dnl
|
||||
dnl Verify that the version of the pkg-config macros are at least
|
||||
dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's
|
||||
dnl installed version of pkg-config, this checks the developer's version
|
||||
dnl of pkg.m4 when generating configure.
|
||||
dnl
|
||||
dnl To ensure that this macro is defined, also add:
|
||||
dnl m4_ifndef([PKG_PREREQ],
|
||||
dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])])
|
||||
dnl
|
||||
dnl See the "Since" comment for each macro you use to see what version
|
||||
dnl of the macros you require.
|
||||
m4_defun([PKG_PREREQ],
|
||||
[m4_define([PKG_MACROS_VERSION], [0.29.1])
|
||||
m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1,
|
||||
[m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])])
|
||||
])dnl PKG_PREREQ
|
||||
|
||||
dnl PKG_PROG_PKG_CONFIG([MIN-VERSION])
|
||||
dnl ----------------------------------
|
||||
dnl Since: 0.16
|
||||
dnl
|
||||
dnl Search for the pkg-config tool and set the PKG_CONFIG variable to
|
||||
dnl first found in the path. Checks that the version of pkg-config found
|
||||
dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is
|
||||
dnl used since that's the first version where most current features of
|
||||
dnl pkg-config existed.
|
||||
AC_DEFUN([PKG_PROG_PKG_CONFIG],
|
||||
[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
|
||||
m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$])
|
||||
m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$])
|
||||
AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])
|
||||
AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path])
|
||||
AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path])
|
||||
|
||||
if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
|
||||
AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
|
||||
fi
|
||||
if test -n "$PKG_CONFIG"; then
|
||||
_pkg_min_version=m4_default([$1], [0.9.0])
|
||||
AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
|
||||
if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
|
||||
AC_MSG_RESULT([yes])
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
PKG_CONFIG=""
|
||||
fi
|
||||
fi[]dnl
|
||||
])dnl PKG_PROG_PKG_CONFIG
|
||||
|
||||
dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||
dnl -------------------------------------------------------------------
|
||||
dnl Since: 0.18
|
||||
dnl
|
||||
dnl Check to see whether a particular set of modules exists. Similar to
|
||||
dnl PKG_CHECK_MODULES(), but does not set variables or print errors.
|
||||
dnl
|
||||
dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
|
||||
dnl only at the first occurence in configure.ac, so if the first place
|
||||
dnl it's called might be skipped (such as if it is within an "if", you
|
||||
dnl have to call PKG_CHECK_EXISTS manually
|
||||
AC_DEFUN([PKG_CHECK_EXISTS],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
|
||||
m4_default([$2], [:])
|
||||
m4_ifvaln([$3], [else
|
||||
$3])dnl
|
||||
fi])
|
||||
|
||||
dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
|
||||
dnl ---------------------------------------------
|
||||
dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting
|
||||
dnl pkg_failed based on the result.
|
||||
m4_define([_PKG_CONFIG],
|
||||
[if test -n "$$1"; then
|
||||
pkg_cv_[]$1="$$1"
|
||||
elif test -n "$PKG_CONFIG"; then
|
||||
PKG_CHECK_EXISTS([$3],
|
||||
[pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`
|
||||
test "x$?" != "x0" && pkg_failed=yes ],
|
||||
[pkg_failed=yes])
|
||||
else
|
||||
pkg_failed=untried
|
||||
fi[]dnl
|
||||
])dnl _PKG_CONFIG
|
||||
|
||||
dnl _PKG_SHORT_ERRORS_SUPPORTED
|
||||
dnl ---------------------------
|
||||
dnl Internal check to see if pkg-config supports short errors.
|
||||
AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
|
||||
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
|
||||
_pkg_short_errors_supported=yes
|
||||
else
|
||||
_pkg_short_errors_supported=no
|
||||
fi[]dnl
|
||||
])dnl _PKG_SHORT_ERRORS_SUPPORTED
|
||||
|
||||
|
||||
dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
|
||||
dnl [ACTION-IF-NOT-FOUND])
|
||||
dnl --------------------------------------------------------------
|
||||
dnl Since: 0.4.0
|
||||
dnl
|
||||
dnl Note that if there is a possibility the first call to
|
||||
dnl PKG_CHECK_MODULES might not happen, you should be sure to include an
|
||||
dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
|
||||
AC_DEFUN([PKG_CHECK_MODULES],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
||||
AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
|
||||
AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
|
||||
|
||||
pkg_failed=no
|
||||
AC_MSG_CHECKING([for $1])
|
||||
|
||||
_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
|
||||
_PKG_CONFIG([$1][_LIBS], [libs], [$2])
|
||||
|
||||
m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
|
||||
and $1[]_LIBS to avoid the need to call pkg-config.
|
||||
See the pkg-config man page for more details.])
|
||||
|
||||
if test $pkg_failed = yes; then
|
||||
AC_MSG_RESULT([no])
|
||||
_PKG_SHORT_ERRORS_SUPPORTED
|
||||
if test $_pkg_short_errors_supported = yes; then
|
||||
$1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1`
|
||||
else
|
||||
$1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1`
|
||||
fi
|
||||
# Put the nasty error message in config.log where it belongs
|
||||
echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
|
||||
|
||||
m4_default([$4], [AC_MSG_ERROR(
|
||||
[Package requirements ($2) were not met:
|
||||
|
||||
$$1_PKG_ERRORS
|
||||
|
||||
Consider adjusting the PKG_CONFIG_PATH environment variable if you
|
||||
installed software in a non-standard prefix.
|
||||
|
||||
_PKG_TEXT])[]dnl
|
||||
])
|
||||
elif test $pkg_failed = untried; then
|
||||
AC_MSG_RESULT([no])
|
||||
m4_default([$4], [AC_MSG_FAILURE(
|
||||
[The pkg-config script could not be found or is too old. Make sure it
|
||||
is in your PATH or set the PKG_CONFIG environment variable to the full
|
||||
path to pkg-config.
|
||||
|
||||
_PKG_TEXT
|
||||
|
||||
To get pkg-config, see <http://pkg-config.freedesktop.org/>.])[]dnl
|
||||
])
|
||||
else
|
||||
$1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
|
||||
$1[]_LIBS=$pkg_cv_[]$1[]_LIBS
|
||||
AC_MSG_RESULT([yes])
|
||||
$3
|
||||
fi[]dnl
|
||||
])dnl PKG_CHECK_MODULES
|
||||
|
||||
|
||||
dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
|
||||
dnl [ACTION-IF-NOT-FOUND])
|
||||
dnl ---------------------------------------------------------------------
|
||||
dnl Since: 0.29
|
||||
dnl
|
||||
dnl Checks for existence of MODULES and gathers its build flags with
|
||||
dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags
|
||||
dnl and VARIABLE-PREFIX_LIBS from --libs.
|
||||
dnl
|
||||
dnl Note that if there is a possibility the first call to
|
||||
dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to
|
||||
dnl include an explicit call to PKG_PROG_PKG_CONFIG in your
|
||||
dnl configure.ac.
|
||||
AC_DEFUN([PKG_CHECK_MODULES_STATIC],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
||||
_save_PKG_CONFIG=$PKG_CONFIG
|
||||
PKG_CONFIG="$PKG_CONFIG --static"
|
||||
PKG_CHECK_MODULES($@)
|
||||
PKG_CONFIG=$_save_PKG_CONFIG[]dnl
|
||||
])dnl PKG_CHECK_MODULES_STATIC
|
||||
|
||||
|
||||
dnl PKG_INSTALLDIR([DIRECTORY])
|
||||
dnl -------------------------
|
||||
dnl Since: 0.27
|
||||
dnl
|
||||
dnl Substitutes the variable pkgconfigdir as the location where a module
|
||||
dnl should install pkg-config .pc files. By default the directory is
|
||||
dnl $libdir/pkgconfig, but the default can be changed by passing
|
||||
dnl DIRECTORY. The user can override through the --with-pkgconfigdir
|
||||
dnl parameter.
|
||||
AC_DEFUN([PKG_INSTALLDIR],
|
||||
[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])])
|
||||
m4_pushdef([pkg_description],
|
||||
[pkg-config installation directory @<:@]pkg_default[@:>@])
|
||||
AC_ARG_WITH([pkgconfigdir],
|
||||
[AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],,
|
||||
[with_pkgconfigdir=]pkg_default)
|
||||
AC_SUBST([pkgconfigdir], [$with_pkgconfigdir])
|
||||
m4_popdef([pkg_default])
|
||||
m4_popdef([pkg_description])
|
||||
])dnl PKG_INSTALLDIR
|
||||
|
||||
|
||||
dnl PKG_NOARCH_INSTALLDIR([DIRECTORY])
|
||||
dnl --------------------------------
|
||||
dnl Since: 0.27
|
||||
dnl
|
||||
dnl Substitutes the variable noarch_pkgconfigdir as the location where a
|
||||
dnl module should install arch-independent pkg-config .pc files. By
|
||||
dnl default the directory is $datadir/pkgconfig, but the default can be
|
||||
dnl changed by passing DIRECTORY. The user can override through the
|
||||
dnl --with-noarch-pkgconfigdir parameter.
|
||||
AC_DEFUN([PKG_NOARCH_INSTALLDIR],
|
||||
[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])])
|
||||
m4_pushdef([pkg_description],
|
||||
[pkg-config arch-independent installation directory @<:@]pkg_default[@:>@])
|
||||
AC_ARG_WITH([noarch-pkgconfigdir],
|
||||
[AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],,
|
||||
[with_noarch_pkgconfigdir=]pkg_default)
|
||||
AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir])
|
||||
m4_popdef([pkg_default])
|
||||
m4_popdef([pkg_description])
|
||||
])dnl PKG_NOARCH_INSTALLDIR
|
||||
|
||||
|
||||
dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE,
|
||||
dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||
dnl -------------------------------------------
|
||||
dnl Since: 0.28
|
||||
dnl
|
||||
dnl Retrieves the value of the pkg-config variable for the given module.
|
||||
AC_DEFUN([PKG_CHECK_VAR],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
||||
AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl
|
||||
|
||||
_PKG_CONFIG([$1], [variable="][$3]["], [$2])
|
||||
AS_VAR_COPY([$1], [pkg_cv_][$1])
|
||||
|
||||
AS_VAR_IF([$1], [""], [$5], [$4])dnl
|
||||
])dnl PKG_CHECK_VAR
|
||||
|
||||
dnl PKG_WITH_MODULES(VARIABLE-PREFIX, MODULES,
|
||||
dnl [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND],
|
||||
dnl [DESCRIPTION], [DEFAULT])
|
||||
dnl ------------------------------------------
|
||||
dnl
|
||||
dnl Prepare a "--with-" configure option using the lowercase
|
||||
dnl [VARIABLE-PREFIX] name, merging the behaviour of AC_ARG_WITH and
|
||||
dnl PKG_CHECK_MODULES in a single macro.
|
||||
AC_DEFUN([PKG_WITH_MODULES],
|
||||
[
|
||||
m4_pushdef([with_arg], m4_tolower([$1]))
|
||||
|
||||
m4_pushdef([description],
|
||||
[m4_default([$5], [build with ]with_arg[ support])])
|
||||
|
||||
m4_pushdef([def_arg], [m4_default([$6], [auto])])
|
||||
m4_pushdef([def_action_if_found], [AS_TR_SH([with_]with_arg)=yes])
|
||||
m4_pushdef([def_action_if_not_found], [AS_TR_SH([with_]with_arg)=no])
|
||||
|
||||
m4_case(def_arg,
|
||||
[yes],[m4_pushdef([with_without], [--without-]with_arg)],
|
||||
[m4_pushdef([with_without],[--with-]with_arg)])
|
||||
|
||||
AC_ARG_WITH(with_arg,
|
||||
AS_HELP_STRING(with_without, description[ @<:@default=]def_arg[@:>@]),,
|
||||
[AS_TR_SH([with_]with_arg)=def_arg])
|
||||
|
||||
AS_CASE([$AS_TR_SH([with_]with_arg)],
|
||||
[yes],[PKG_CHECK_MODULES([$1],[$2],$3,$4)],
|
||||
[auto],[PKG_CHECK_MODULES([$1],[$2],
|
||||
[m4_n([def_action_if_found]) $3],
|
||||
[m4_n([def_action_if_not_found]) $4])])
|
||||
|
||||
m4_popdef([with_arg])
|
||||
m4_popdef([description])
|
||||
m4_popdef([def_arg])
|
||||
|
||||
])dnl PKG_WITH_MODULES
|
||||
|
||||
dnl PKG_HAVE_WITH_MODULES(VARIABLE-PREFIX, MODULES,
|
||||
dnl [DESCRIPTION], [DEFAULT])
|
||||
dnl -----------------------------------------------
|
||||
dnl
|
||||
dnl Convenience macro to trigger AM_CONDITIONAL after PKG_WITH_MODULES
|
||||
dnl check._[VARIABLE-PREFIX] is exported as make variable.
|
||||
AC_DEFUN([PKG_HAVE_WITH_MODULES],
|
||||
[
|
||||
PKG_WITH_MODULES([$1],[$2],,,[$3],[$4])
|
||||
|
||||
AM_CONDITIONAL([HAVE_][$1],
|
||||
[test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"])
|
||||
])dnl PKG_HAVE_WITH_MODULES
|
||||
|
||||
dnl PKG_HAVE_DEFINE_WITH_MODULES(VARIABLE-PREFIX, MODULES,
|
||||
dnl [DESCRIPTION], [DEFAULT])
|
||||
dnl ------------------------------------------------------
|
||||
dnl
|
||||
dnl Convenience macro to run AM_CONDITIONAL and AC_DEFINE after
|
||||
dnl PKG_WITH_MODULES check. HAVE_[VARIABLE-PREFIX] is exported as make
|
||||
dnl and preprocessor variable.
|
||||
AC_DEFUN([PKG_HAVE_DEFINE_WITH_MODULES],
|
||||
[
|
||||
PKG_HAVE_WITH_MODULES([$1],[$2],[$3],[$4])
|
||||
|
||||
AS_IF([test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"],
|
||||
[AC_DEFINE([HAVE_][$1], 1, [Enable ]m4_tolower([$1])[ support])])
|
||||
])dnl PKG_HAVE_DEFINE_WITH_MODULES
|
|
@ -0,0 +1 @@
|
|||
-include $(top_srcdir)/git.mk
|
|
@ -0,0 +1,473 @@
|
|||
# Makefile.in generated by automake 1.15.1 from Makefile.am.
|
||||
# @configure_input@
|
||||
|
||||
# Copyright (C) 1994-2017 Free Software Foundation, Inc.
|
||||
|
||||
# This Makefile.in is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
|
||||
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
# PARTICULAR PURPOSE.
|
||||
|
||||
@SET_MAKE@
|
||||
VPATH = @srcdir@
|
||||
am__is_gnu_make = { \
|
||||
if test -z '$(MAKELEVEL)'; then \
|
||||
false; \
|
||||
elif test -n '$(MAKE_HOST)'; then \
|
||||
true; \
|
||||
elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
|
||||
true; \
|
||||
else \
|
||||
false; \
|
||||
fi; \
|
||||
}
|
||||
am__make_running_with_option = \
|
||||
case $${target_option-} in \
|
||||
?) ;; \
|
||||
*) echo "am__make_running_with_option: internal error: invalid" \
|
||||
"target option '$${target_option-}' specified" >&2; \
|
||||
exit 1;; \
|
||||
esac; \
|
||||
has_opt=no; \
|
||||
sane_makeflags=$$MAKEFLAGS; \
|
||||
if $(am__is_gnu_make); then \
|
||||
sane_makeflags=$$MFLAGS; \
|
||||
else \
|
||||
case $$MAKEFLAGS in \
|
||||
*\\[\ \ ]*) \
|
||||
bs=\\; \
|
||||
sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
|
||||
| sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
|
||||
esac; \
|
||||
fi; \
|
||||
skip_next=no; \
|
||||
strip_trailopt () \
|
||||
{ \
|
||||
flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
|
||||
}; \
|
||||
for flg in $$sane_makeflags; do \
|
||||
test $$skip_next = yes && { skip_next=no; continue; }; \
|
||||
case $$flg in \
|
||||
*=*|--*) continue;; \
|
||||
-*I) strip_trailopt 'I'; skip_next=yes;; \
|
||||
-*I?*) strip_trailopt 'I';; \
|
||||
-*O) strip_trailopt 'O'; skip_next=yes;; \
|
||||
-*O?*) strip_trailopt 'O';; \
|
||||
-*l) strip_trailopt 'l'; skip_next=yes;; \
|
||||
-*l?*) strip_trailopt 'l';; \
|
||||
-[dEDm]) skip_next=yes;; \
|
||||
-[JT]) skip_next=yes;; \
|
||||
esac; \
|
||||
case $$flg in \
|
||||
*$$target_option*) has_opt=yes; break;; \
|
||||
esac; \
|
||||
done; \
|
||||
test $$has_opt = yes
|
||||
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
|
||||
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
|
||||
pkgdatadir = $(datadir)/@PACKAGE@
|
||||
pkgincludedir = $(includedir)/@PACKAGE@
|
||||
pkglibdir = $(libdir)/@PACKAGE@
|
||||
pkglibexecdir = $(libexecdir)/@PACKAGE@
|
||||
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
|
||||
install_sh_DATA = $(install_sh) -c -m 644
|
||||
install_sh_PROGRAM = $(install_sh) -c
|
||||
install_sh_SCRIPT = $(install_sh) -c
|
||||
INSTALL_HEADER = $(INSTALL_DATA)
|
||||
transform = $(program_transform_name)
|
||||
NORMAL_INSTALL = :
|
||||
PRE_INSTALL = :
|
||||
POST_INSTALL = :
|
||||
NORMAL_UNINSTALL = :
|
||||
PRE_UNINSTALL = :
|
||||
POST_UNINSTALL = :
|
||||
build_triplet = @build@
|
||||
host_triplet = @host@
|
||||
subdir = src
|
||||
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
|
||||
am__aclocal_m4_deps = $(top_srcdir)/m4/ax_append_compile_flags.m4 \
|
||||
$(top_srcdir)/m4/ax_append_flag.m4 \
|
||||
$(top_srcdir)/m4/ax_append_link_flags.m4 \
|
||||
$(top_srcdir)/m4/ax_check_compile_flag.m4 \
|
||||
$(top_srcdir)/m4/ax_check_enable_debug.m4 \
|
||||
$(top_srcdir)/m4/ax_check_link_flag.m4 \
|
||||
$(top_srcdir)/m4/ax_code_coverage.m4 \
|
||||
$(top_srcdir)/m4/ax_compiler_flags.m4 \
|
||||
$(top_srcdir)/m4/ax_compiler_flags_cflags.m4 \
|
||||
$(top_srcdir)/m4/ax_compiler_flags_gir.m4 \
|
||||
$(top_srcdir)/m4/ax_compiler_flags_ldflags.m4 \
|
||||
$(top_srcdir)/m4/ax_is_release.m4 \
|
||||
$(top_srcdir)/m4/ax_require_defined.m4 \
|
||||
$(top_srcdir)/m4/glibtests.m4 $(top_srcdir)/m4/libtool.m4 \
|
||||
$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
|
||||
$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
|
||||
$(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac
|
||||
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
|
||||
$(ACLOCAL_M4)
|
||||
DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
|
||||
mkinstalldirs = $(install_sh) -d
|
||||
CONFIG_HEADER = $(top_builddir)/config.h
|
||||
CONFIG_CLEAN_FILES =
|
||||
CONFIG_CLEAN_VPATH_FILES =
|
||||
AM_V_P = $(am__v_P_@AM_V@)
|
||||
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
|
||||
am__v_P_0 = false
|
||||
am__v_P_1 = :
|
||||
AM_V_GEN = $(am__v_GEN_@AM_V@)
|
||||
am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
|
||||
am__v_GEN_0 = @echo " GEN " $@;
|
||||
am__v_GEN_1 =
|
||||
AM_V_at = $(am__v_at_@AM_V@)
|
||||
am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
|
||||
am__v_at_0 = @
|
||||
am__v_at_1 =
|
||||
SOURCES =
|
||||
DIST_SOURCES =
|
||||
am__can_run_installinfo = \
|
||||
case $$AM_UPDATE_INFO_DIR in \
|
||||
n|no|NO) false;; \
|
||||
*) (install-info --version) >/dev/null 2>&1;; \
|
||||
esac
|
||||
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
|
||||
am__DIST_COMMON = $(srcdir)/Makefile.in
|
||||
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
|
||||
ACLOCAL = @ACLOCAL@
|
||||
AMTAR = @AMTAR@
|
||||
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
|
||||
AR = @AR@
|
||||
AS = @AS@
|
||||
AUTOCONF = @AUTOCONF@
|
||||
AUTOHEADER = @AUTOHEADER@
|
||||
AUTOMAKE = @AUTOMAKE@
|
||||
AWK = @AWK@
|
||||
CACARD_CFLAGS = @CACARD_CFLAGS@
|
||||
CACARD_LIBS = @CACARD_LIBS@
|
||||
CC = @CC@
|
||||
CCDEPMODE = @CCDEPMODE@
|
||||
CFLAGS = @CFLAGS@
|
||||
CODE_COVERAGE_CFLAGS = @CODE_COVERAGE_CFLAGS@
|
||||
CODE_COVERAGE_CPPFLAGS = @CODE_COVERAGE_CPPFLAGS@
|
||||
CODE_COVERAGE_CXXFLAGS = @CODE_COVERAGE_CXXFLAGS@
|
||||
CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@
|
||||
CODE_COVERAGE_LDFLAGS = @CODE_COVERAGE_LDFLAGS@
|
||||
CODE_COVERAGE_LIBS = @CODE_COVERAGE_LIBS@
|
||||
CPP = @CPP@
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
CYGPATH_W = @CYGPATH_W@
|
||||
DEFS = @DEFS@
|
||||
DEPDIR = @DEPDIR@
|
||||
DLLTOOL = @DLLTOOL@
|
||||
DSYMUTIL = @DSYMUTIL@
|
||||
DUMPBIN = @DUMPBIN@
|
||||
ECHO_C = @ECHO_C@
|
||||
ECHO_N = @ECHO_N@
|
||||
ECHO_T = @ECHO_T@
|
||||
EGREP = @EGREP@
|
||||
EXEEXT = @EXEEXT@
|
||||
FGREP = @FGREP@
|
||||
GCOV = @GCOV@
|
||||
GENHTML = @GENHTML@
|
||||
GREP = @GREP@
|
||||
GTHREAD_CFLAGS = @GTHREAD_CFLAGS@
|
||||
GTHREAD_LIBS = @GTHREAD_LIBS@
|
||||
INSTALL = @INSTALL@
|
||||
INSTALL_DATA = @INSTALL_DATA@
|
||||
INSTALL_PROGRAM = @INSTALL_PROGRAM@
|
||||
INSTALL_SCRIPT = @INSTALL_SCRIPT@
|
||||
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
|
||||
LCOV = @LCOV@
|
||||
LD = @LD@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBOBJS = @LIBOBJS@
|
||||
LIBS = @LIBS@
|
||||
LIBTOOL = @LIBTOOL@
|
||||
LIPO = @LIPO@
|
||||
LN_S = @LN_S@
|
||||
LTLIBOBJS = @LTLIBOBJS@
|
||||
LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
|
||||
MAINT = @MAINT@
|
||||
MAKEINFO = @MAKEINFO@
|
||||
MANIFEST_TOOL = @MANIFEST_TOOL@
|
||||
MKDIR_P = @MKDIR_P@
|
||||
NM = @NM@
|
||||
NMEDIT = @NMEDIT@
|
||||
OBJDUMP = @OBJDUMP@
|
||||
OBJEXT = @OBJEXT@
|
||||
OTOOL = @OTOOL@
|
||||
OTOOL64 = @OTOOL64@
|
||||
PACKAGE = @PACKAGE@
|
||||
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
|
||||
PACKAGE_NAME = @PACKAGE_NAME@
|
||||
PACKAGE_STRING = @PACKAGE_STRING@
|
||||
PACKAGE_TARNAME = @PACKAGE_TARNAME@
|
||||
PACKAGE_URL = @PACKAGE_URL@
|
||||
PACKAGE_VERSION = @PACKAGE_VERSION@
|
||||
PATH_SEPARATOR = @PATH_SEPARATOR@
|
||||
PCSC_CFLAGS = @PCSC_CFLAGS@
|
||||
PCSC_LIBS = @PCSC_LIBS@
|
||||
PKG_CONFIG = @PKG_CONFIG@
|
||||
PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
|
||||
PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
|
||||
RANLIB = @RANLIB@
|
||||
SED = @SED@
|
||||
SET_MAKE = @SET_MAKE@
|
||||
SHELL = @SHELL@
|
||||
STRIP = @STRIP@
|
||||
VERSION = @VERSION@
|
||||
WARN_CFLAGS = @WARN_CFLAGS@
|
||||
WARN_LDFLAGS = @WARN_LDFLAGS@
|
||||
WARN_SCANNERFLAGS = @WARN_SCANNERFLAGS@
|
||||
abs_builddir = @abs_builddir@
|
||||
abs_srcdir = @abs_srcdir@
|
||||
abs_top_builddir = @abs_top_builddir@
|
||||
abs_top_srcdir = @abs_top_srcdir@
|
||||
ac_ct_AR = @ac_ct_AR@
|
||||
ac_ct_CC = @ac_ct_CC@
|
||||
ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
|
||||
am__include = @am__include@
|
||||
am__leading_dot = @am__leading_dot@
|
||||
am__quote = @am__quote@
|
||||
am__tar = @am__tar@
|
||||
am__untar = @am__untar@
|
||||
bindir = @bindir@
|
||||
build = @build@
|
||||
build_alias = @build_alias@
|
||||
build_cpu = @build_cpu@
|
||||
build_os = @build_os@
|
||||
build_vendor = @build_vendor@
|
||||
builddir = @builddir@
|
||||
datadir = @datadir@
|
||||
datarootdir = @datarootdir@
|
||||
docdir = @docdir@
|
||||
dvidir = @dvidir@
|
||||
exec_prefix = @exec_prefix@
|
||||
host = @host@
|
||||
host_alias = @host_alias@
|
||||
host_cpu = @host_cpu@
|
||||
host_os = @host_os@
|
||||
host_vendor = @host_vendor@
|
||||
htmldir = @htmldir@
|
||||
includedir = @includedir@
|
||||
infodir = @infodir@
|
||||
install_sh = @install_sh@
|
||||
installed_test_metadir = @installed_test_metadir@
|
||||
installed_testdir = @installed_testdir@
|
||||
libdir = @libdir@
|
||||
libexecdir = @libexecdir@
|
||||
localedir = @localedir@
|
||||
localstatedir = @localstatedir@
|
||||
mandir = @mandir@
|
||||
mkdir_p = @mkdir_p@
|
||||
oldincludedir = @oldincludedir@
|
||||
pdfdir = @pdfdir@
|
||||
prefix = @prefix@
|
||||
program_transform_name = @program_transform_name@
|
||||
psdir = @psdir@
|
||||
sbindir = @sbindir@
|
||||
sharedstatedir = @sharedstatedir@
|
||||
srcdir = @srcdir@
|
||||
sysconfdir = @sysconfdir@
|
||||
target_alias = @target_alias@
|
||||
top_build_prefix = @top_build_prefix@
|
||||
top_builddir = @top_builddir@
|
||||
top_srcdir = @top_srcdir@
|
||||
all: all-am
|
||||
|
||||
.SUFFIXES:
|
||||
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
|
||||
@for dep in $?; do \
|
||||
case '$(am__configure_deps)' in \
|
||||
*$$dep*) \
|
||||
( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
|
||||
&& { if test -f $@; then exit 0; else break; fi; }; \
|
||||
exit 1;; \
|
||||
esac; \
|
||||
done; \
|
||||
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \
|
||||
$(am__cd) $(top_srcdir) && \
|
||||
$(AUTOMAKE) --foreign src/Makefile
|
||||
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
|
||||
@case '$?' in \
|
||||
*config.status*) \
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
|
||||
*) \
|
||||
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
|
||||
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
|
||||
esac;
|
||||
|
||||
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
|
||||
$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
$(am__aclocal_m4_deps):
|
||||
|
||||
mostlyclean-libtool:
|
||||
-rm -f *.lo
|
||||
|
||||
clean-libtool:
|
||||
-rm -rf .libs _libs
|
||||
tags TAGS:
|
||||
|
||||
ctags CTAGS:
|
||||
|
||||
cscope cscopelist:
|
||||
|
||||
|
||||
distdir: $(DISTFILES)
|
||||
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
|
||||
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
|
||||
list='$(DISTFILES)'; \
|
||||
dist_files=`for file in $$list; do echo $$file; done | \
|
||||
sed -e "s|^$$srcdirstrip/||;t" \
|
||||
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
|
||||
case $$dist_files in \
|
||||
*/*) $(MKDIR_P) `echo "$$dist_files" | \
|
||||
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
|
||||
sort -u` ;; \
|
||||
esac; \
|
||||
for file in $$dist_files; do \
|
||||
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
|
||||
if test -d $$d/$$file; then \
|
||||
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
|
||||
if test -d "$(distdir)/$$file"; then \
|
||||
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
|
||||
fi; \
|
||||
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
|
||||
cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
|
||||
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
|
||||
fi; \
|
||||
cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
|
||||
else \
|
||||
test -f "$(distdir)/$$file" \
|
||||
|| cp -p $$d/$$file "$(distdir)/$$file" \
|
||||
|| exit 1; \
|
||||
fi; \
|
||||
done
|
||||
check-am: all-am
|
||||
check: check-am
|
||||
all-am: Makefile
|
||||
installdirs:
|
||||
install: install-am
|
||||
install-exec: install-exec-am
|
||||
install-data: install-data-am
|
||||
uninstall: uninstall-am
|
||||
|
||||
install-am: all-am
|
||||
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
|
||||
|
||||
installcheck: installcheck-am
|
||||
install-strip:
|
||||
if test -z '$(STRIP)'; then \
|
||||
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
|
||||
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
|
||||
install; \
|
||||
else \
|
||||
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
|
||||
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
|
||||
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
|
||||
fi
|
||||
mostlyclean-generic:
|
||||
|
||||
clean-generic:
|
||||
|
||||
distclean-generic:
|
||||
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
|
||||
-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
|
||||
|
||||
maintainer-clean-generic:
|
||||
@echo "This command is intended for maintainers to use"
|
||||
@echo "it deletes files that may require special tools to rebuild."
|
||||
clean: clean-am
|
||||
|
||||
clean-am: clean-generic clean-libtool mostlyclean-am
|
||||
|
||||
distclean: distclean-am
|
||||
-rm -f Makefile
|
||||
distclean-am: clean-am distclean-generic
|
||||
|
||||
dvi: dvi-am
|
||||
|
||||
dvi-am:
|
||||
|
||||
html: html-am
|
||||
|
||||
html-am:
|
||||
|
||||
info: info-am
|
||||
|
||||
info-am:
|
||||
|
||||
install-data-am:
|
||||
|
||||
install-dvi: install-dvi-am
|
||||
|
||||
install-dvi-am:
|
||||
|
||||
install-exec-am:
|
||||
|
||||
install-html: install-html-am
|
||||
|
||||
install-html-am:
|
||||
|
||||
install-info: install-info-am
|
||||
|
||||
install-info-am:
|
||||
|
||||
install-man:
|
||||
|
||||
install-pdf: install-pdf-am
|
||||
|
||||
install-pdf-am:
|
||||
|
||||
install-ps: install-ps-am
|
||||
|
||||
install-ps-am:
|
||||
|
||||
installcheck-am:
|
||||
|
||||
maintainer-clean: maintainer-clean-am
|
||||
-rm -f Makefile
|
||||
maintainer-clean-am: distclean-am maintainer-clean-generic
|
||||
|
||||
mostlyclean: mostlyclean-am
|
||||
|
||||
mostlyclean-am: mostlyclean-generic mostlyclean-libtool
|
||||
|
||||
pdf: pdf-am
|
||||
|
||||
pdf-am:
|
||||
|
||||
ps: ps-am
|
||||
|
||||
ps-am:
|
||||
|
||||
uninstall-am:
|
||||
|
||||
.MAKE: install-am install-strip
|
||||
|
||||
.PHONY: all all-am check check-am clean clean-generic clean-libtool \
|
||||
cscopelist-am ctags-am distclean distclean-generic \
|
||||
distclean-libtool distdir dvi dvi-am html html-am info info-am \
|
||||
install install-am install-data install-data-am install-dvi \
|
||||
install-dvi-am install-exec install-exec-am install-html \
|
||||
install-html-am install-info install-info-am install-man \
|
||||
install-pdf install-pdf-am install-ps install-ps-am \
|
||||
install-strip installcheck installcheck-am installdirs \
|
||||
maintainer-clean maintainer-clean-generic mostlyclean \
|
||||
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
|
||||
tags-am uninstall uninstall-am
|
||||
|
||||
.PRECIOUS: Makefile
|
||||
|
||||
-include $(top_srcdir)/git.mk
|
||||
|
||||
# Tell versions [3.59,3.63) of GNU make to not export all variables.
|
||||
# Otherwise a system limit (for SysV at least) may be exceeded.
|
||||
.NOEXPORT:
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* implement the ACA applet for the CAC card.
|
||||
*
|
||||
* Adaptation to GSC-IS 2.1:
|
||||
* https://nvlpubs.nist.gov/nistpubs/Legacy/IR/nistir6887e2003.pdf
|
||||
*
|
||||
* Copyright 2018 Red Hat, Inc.
|
||||
*
|
||||
* Author: Jakub Jelen <jjelen@redhat.com>
|
||||
*
|
||||
* This code is licensed under the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "glib-compat.h"
|
||||
#include "card_7816t.h"
|
||||
#include "cac.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
VCardResponse *
|
||||
cac_aca_get_acr_response(VCard *card, int Le, unsigned char *acrid, int format);
|
||||
|
||||
VCardResponse *
|
||||
cac_aca_get_applet_acr_response(VCard *card, int Le, unsigned int pki_applets,
|
||||
unsigned char *aid, unsigned int aid_len,
|
||||
unsigned char *coid, int format);
|
||||
VCardResponse *
|
||||
cac_aca_get_amp_response(VCard *card, int Le, int format);
|
||||
|
||||
VCardResponse *
|
||||
cac_aca_get_service_response(VCard *card, int Le, unsigned int pki_applets,
|
||||
int format);
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* defines the entry point for the cac card. Only used by cac.c and
|
||||
* vcard_emul_type.c
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
#ifndef CAC_H
|
||||
#define CAC_H 1
|
||||
|
||||
#include "vcard.h"
|
||||
#include "vreader.h"
|
||||
|
||||
#define CAC_GET_PROPERTIES 0x56
|
||||
#define CAC_GET_ACR 0x4c
|
||||
#define CAC_READ_BUFFER 0x52 /* CACv2 */
|
||||
#define CAC_UPDATE_BUFFER 0x58
|
||||
#define CAC_SIGN_DECRYPT 0x42
|
||||
#define CAC_GET_CERTIFICATE 0x36 /* CACv1 */
|
||||
|
||||
/* read file TAGs for CACv2 */
|
||||
#define CAC_FILE_TAG 0x01
|
||||
#define CAC_FILE_VALUE 0x02
|
||||
|
||||
/* PKI applet tags */
|
||||
#define CAC_PKI_TAG_CERTIFICATE 0x70
|
||||
#define CAC_PKI_TAG_CERTINFO 0x71
|
||||
#define CAC_PKI_TAG_MSCUID 0x72
|
||||
#define CAC_PKI_TAG_ERROR_DETECTION_CODE 0xFE
|
||||
|
||||
/* ACA applet tags */
|
||||
#define CAC_ACR_NUM_ENTRIES 0xA1
|
||||
#define CAC_ACR_ENTRY 0xA0
|
||||
#define CAC_ACR_NUM_APPLETS 0x81
|
||||
#define CAC_ACR_APPLET_ACR 0x80
|
||||
#define CAC_ACR_OBJECT_ACR 0x82
|
||||
#define CAC_ACR_AMP_NUM_ENTRIES 0x91
|
||||
#define CAC_ACR_AMP_ENTRY 0x90
|
||||
#define CAC_ACR_AID 0x92
|
||||
#define CAC_ACR_SERVICE_NUM_ENTRIES 0x94
|
||||
#define CAC_ACR_SERVICE_ENTRY 0x93
|
||||
|
||||
/* CCC applet tags */
|
||||
#define CAC_CCC_CARD_IDENTIFIER 0xF0
|
||||
#define CAC_CCC_CAPABILITY_CONTAINER_VERSION 0xF1
|
||||
#define CAC_CCC_CAPABILITY_GRAMMAR_VERSION 0xF2
|
||||
#define CAC_CCC_APPLICATION_CARDURL 0xF3
|
||||
#define CAC_CCC_PKCS15 0xF4
|
||||
#define CAC_CCC_REGISTERED_DATA_MODEL_NUMBER 0xF5
|
||||
#define CAC_CCC_ACCESS_CONTROL_RULE_TABLE 0xF6
|
||||
#define CAC_CCC_CARD_APDUS 0xF7
|
||||
#define CAC_CCC_REDIRECTION_TAG 0xFA
|
||||
#define CAC_CCC_CAPABILITY_TUPLES 0xFB
|
||||
#define CAC_CCC_STATUS_TUPLES 0xFC
|
||||
#define CAC_CCC_NEXT_CCC 0xFD
|
||||
#define CAC_CCC_ERROR_DETECTION_CODE 0xFE
|
||||
|
||||
/* Applet properties tags */
|
||||
#define CAC_PROPERTIES_APPLET_INFORMATION 0x01
|
||||
#define CAC_PROPERTIES_NUMBER_OBJECTS 0x40
|
||||
#define CAC_PROPERTIES_OBJECT_ID 0x41
|
||||
#define CAC_PROPERTIES_BUFFER_PROPERTIES 0x42
|
||||
#define CAC_PROPERTIES_PKI_PROPERTIES 0x43
|
||||
#define CAC_PROPERTIES_TV_OBJECT 0x50
|
||||
#define CAC_PROPERTIES_PKI_OBJECT 0x51
|
||||
|
||||
/* Buffer formats */
|
||||
#define CAC_FORMAT_SIMPLETLV 1
|
||||
#define CAC_FORMAT_EXTENDED 2
|
||||
|
||||
|
||||
/*
|
||||
* Initialize the cac card. This is the only public function in this file. All
|
||||
* the rest are connected through function pointers.
|
||||
*/
|
||||
VCardStatus cac_card_init(VReader *reader, VCard *card, const char *params,
|
||||
unsigned char * const *cert, int cert_len[],
|
||||
VCardKey *key[] /* adopt the keys*/,
|
||||
int cert_count);
|
||||
#endif
|
|
@ -0,0 +1,501 @@
|
|||
/*
|
||||
* Supply a vreader using the PC/SC interface.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "glib-compat.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "vcard.h"
|
||||
#include "card_7816.h"
|
||||
#include "capcsc.h"
|
||||
#include "vreader.h"
|
||||
#include "vevent.h"
|
||||
|
||||
#include <PCSC/wintypes.h>
|
||||
#include <PCSC/winscard.h>
|
||||
|
||||
|
||||
typedef struct _PCSCContext PCSCContext;
|
||||
|
||||
typedef struct {
|
||||
PCSCContext *context;
|
||||
int index;
|
||||
char *name;
|
||||
DWORD protocol;
|
||||
DWORD state;
|
||||
SCARDHANDLE card;
|
||||
BYTE atr[MAX_ATR_SIZE];
|
||||
DWORD atrlen;
|
||||
int card_connected;
|
||||
unsigned long request_count;
|
||||
} SCardReader;
|
||||
|
||||
typedef struct _PCSCContext {
|
||||
SCARDCONTEXT context;
|
||||
SCardReader readers[CAPCSC_MAX_READERS];
|
||||
int reader_count;
|
||||
int readers_changed;
|
||||
GThread *thread;
|
||||
CompatGMutex lock;
|
||||
} PCSCContext;
|
||||
|
||||
|
||||
static void delete_reader(PCSCContext *pc, int i)
|
||||
{
|
||||
SCardReader *r = &pc->readers[i];
|
||||
g_free(r->name);
|
||||
r->name = NULL;
|
||||
|
||||
if (i < (pc->reader_count - 1)) {
|
||||
int rem = pc->reader_count - i - 1;
|
||||
memmove(&pc->readers[i], &pc->readers[i + 1],
|
||||
sizeof(SCardReader) * rem);
|
||||
}
|
||||
|
||||
pc->reader_count--;
|
||||
}
|
||||
|
||||
static void delete_reader_cb(VReaderEmul *ve)
|
||||
{
|
||||
SCardReader *r = (SCardReader *) ve;
|
||||
|
||||
g_mutex_lock(&r->context->lock);
|
||||
delete_reader(r->context, r->index);
|
||||
g_mutex_unlock(&r->context->lock);
|
||||
}
|
||||
|
||||
static int new_reader(PCSCContext *pc, const char *name, DWORD state)
|
||||
{
|
||||
SCardReader *r;
|
||||
VReader *vreader;
|
||||
|
||||
if (pc->reader_count >= CAPCSC_MAX_READERS - 1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
r = &pc->readers[pc->reader_count];
|
||||
memset(r, 0, sizeof(*r));
|
||||
r->index = pc->reader_count++;
|
||||
r->context = pc;
|
||||
r->name = g_strdup(name);
|
||||
|
||||
vreader = vreader_new(name, (VReaderEmul *) r, delete_reader_cb);
|
||||
vreader_add_reader(vreader);
|
||||
vreader_free(vreader);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int find_reader(PCSCContext *pc, const char *name)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < pc->reader_count; i++)
|
||||
if (strcmp(pc->readers[i].name, name) == 0) {
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static int scan_for_readers(PCSCContext *pc)
|
||||
{
|
||||
LONG rc;
|
||||
|
||||
int i;
|
||||
char buf[8192];
|
||||
DWORD buflen = sizeof(buf);
|
||||
|
||||
char *p;
|
||||
int matches[CAPCSC_MAX_READERS] = { 0, };
|
||||
|
||||
g_mutex_lock(&pc->lock);
|
||||
|
||||
pc->readers_changed = 1;
|
||||
memset(buf, 0, sizeof(buf));
|
||||
rc = SCardListReaders(pc->context, NULL, buf, &buflen);
|
||||
if (rc == SCARD_E_NO_READERS_AVAILABLE) {
|
||||
rc = 0;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (rc != SCARD_S_SUCCESS) {
|
||||
fprintf(stderr, "SCardListReaders failed: %s (0x%lX)\n",
|
||||
pcsc_stringify_error(rc), rc);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (p = buf; p && p < buf + sizeof(buf); p += (strlen(p) + 1)) {
|
||||
if (strlen(p) > 0) {
|
||||
i = find_reader(pc, p);
|
||||
if (i >= 0) {
|
||||
matches[i]++;
|
||||
} else {
|
||||
if (!new_reader(pc, p, SCARD_STATE_UNAWARE)) {
|
||||
matches[pc->reader_count - 1]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
|
||||
exit:
|
||||
i = pc->reader_count - 1;
|
||||
g_mutex_unlock(&pc->lock);
|
||||
|
||||
for (; i >= 0; i--) {
|
||||
if (!matches[i]) {
|
||||
VReader *reader = vreader_get_reader_by_name(pc->readers[i].name);
|
||||
if (reader) {
|
||||
vreader_free(reader);
|
||||
vreader_remove_reader(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int init_pcsc(PCSCContext *pc)
|
||||
{
|
||||
LONG rc;
|
||||
|
||||
memset(pc, 0, sizeof(*pc));
|
||||
|
||||
rc = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &pc->context);
|
||||
if (rc != SCARD_S_SUCCESS) {
|
||||
fprintf(stderr, "SCardEstablishContext: "
|
||||
"Cannot Connect to Resource Manager %lX\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void prepare_reader_states(PCSCContext *pc, SCARD_READERSTATE **states,
|
||||
DWORD *reader_count)
|
||||
{
|
||||
SCARD_READERSTATE *state;
|
||||
int i;
|
||||
|
||||
if (*states) {
|
||||
g_free(*states);
|
||||
}
|
||||
|
||||
*reader_count = pc->reader_count;
|
||||
|
||||
(*reader_count)++;
|
||||
*states = g_malloc((*reader_count) * sizeof(**states));
|
||||
memset(*states, 0, sizeof((*reader_count) * sizeof(**states)));
|
||||
|
||||
for (i = 0, state = *states; i < pc->reader_count; i++, state++) {
|
||||
state->szReader = pc->readers[i].name;
|
||||
state->dwCurrentState = pc->readers[i].state;
|
||||
}
|
||||
|
||||
/* Leave a space to be notified of new readers */
|
||||
state->szReader = "\\\\?PnP?\\Notification";
|
||||
state->dwCurrentState = SCARD_STATE_UNAWARE;
|
||||
}
|
||||
|
||||
static int connect_card(SCardReader *r)
|
||||
{
|
||||
LONG rc;
|
||||
|
||||
r->protocol = -1;
|
||||
rc = SCardConnect(r->context->context, r->name, SCARD_SHARE_SHARED,
|
||||
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
|
||||
&r->card, &r->protocol);
|
||||
if (rc != SCARD_S_SUCCESS) {
|
||||
fprintf(stderr, "Failed to connect to a card reader: %s (0x%lX)\n",
|
||||
pcsc_stringify_error(rc), rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
r->card_connected = 1;
|
||||
r->request_count = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static LONG send_receive(SCardReader *r, BYTE *transmit, DWORD transmit_len,
|
||||
BYTE *receive, DWORD *receive_len)
|
||||
{
|
||||
const SCARD_IO_REQUEST *send_header;
|
||||
SCARD_IO_REQUEST receive_header;
|
||||
LONG rc;
|
||||
|
||||
if (!r->card_connected) {
|
||||
rc = connect_card(r);
|
||||
if (rc) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
if (r->protocol == SCARD_PROTOCOL_T0) {
|
||||
send_header = SCARD_PCI_T0;
|
||||
} else if (r->protocol == SCARD_PROTOCOL_T1) {
|
||||
send_header = SCARD_PCI_T1;
|
||||
} else {
|
||||
fprintf(stderr, "Unknown protocol %lX\n", r->protocol);
|
||||
return 1;
|
||||
}
|
||||
|
||||
rc = SCardTransmit(r->card, send_header, transmit, transmit_len,
|
||||
&receive_header, receive, receive_len);
|
||||
if (rc != SCARD_S_SUCCESS) {
|
||||
fprintf(stderr, "Failed to transmit %ld bytes: %s (0x%lX)\n",
|
||||
transmit_len, pcsc_stringify_error(rc), rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static VCardStatus apdu_cb(VCard *card, VCardAPDU *apdu,
|
||||
VCardResponse **response)
|
||||
{
|
||||
VCardStatus ret = VCARD_DONE;
|
||||
SCardReader *r = (SCardReader *) vcard_get_private(card);
|
||||
BYTE outbuf[4096];
|
||||
DWORD outlen = sizeof(outbuf);
|
||||
LONG rc;
|
||||
|
||||
rc = send_receive(r, apdu->a_data, apdu->a_len, outbuf, &outlen);
|
||||
if (rc || outlen < 2) {
|
||||
ret = VCARD_FAIL;
|
||||
} else {
|
||||
*response = vcard_response_new_data(outbuf, outlen - 2);
|
||||
if (*response == NULL) {
|
||||
return VCARD_FAIL;
|
||||
}
|
||||
vcard_response_set_status_bytes(*response, outbuf[outlen - 2],
|
||||
outbuf[outlen - 1]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static VCardStatus reset_cb(VCard *card, int channel)
|
||||
{
|
||||
SCardReader *r = (SCardReader *) vcard_get_private(card);
|
||||
LONG rc;
|
||||
|
||||
/* vreader_power_on is a bit too free with it's resets.
|
||||
And a reconnect is expensive; as much as 10-20 seconds.
|
||||
Hence, we discard any initial reconnect request. */
|
||||
if (r->request_count++ == 0) {
|
||||
return VCARD_DONE;
|
||||
}
|
||||
|
||||
rc = SCardReconnect(r->card, SCARD_SHARE_SHARED,
|
||||
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
|
||||
SCARD_RESET_CARD, &r->protocol);
|
||||
if (rc != SCARD_S_SUCCESS) {
|
||||
fprintf(stderr, "Failed to reconnect to a card reader: %s (0x%lX)\n",
|
||||
pcsc_stringify_error(rc), rc);
|
||||
return VCARD_FAIL;
|
||||
}
|
||||
return VCARD_DONE;
|
||||
}
|
||||
|
||||
static void get_atr_cb(VCard *card, unsigned char *atr, int *atr_len)
|
||||
{
|
||||
SCardReader *r = (SCardReader *) vcard_get_private(card);
|
||||
*atr_len = r->atrlen;
|
||||
if (atr) {
|
||||
memcpy(atr, r->atr, r->atrlen);
|
||||
}
|
||||
}
|
||||
|
||||
static void delete_card_cb(VCardEmul *ve)
|
||||
{
|
||||
fprintf(stderr, "TODO, got a delete_card_cb\n");
|
||||
}
|
||||
|
||||
static void insert_card(SCardReader *r, SCARD_READERSTATE *s)
|
||||
{
|
||||
VReader *reader;
|
||||
VCardApplet *applet;
|
||||
VCard *card;
|
||||
|
||||
memcpy(r->atr, s->rgbAtr, MIN(sizeof(r->atr), sizeof(s->rgbAtr)));
|
||||
r->atrlen = s->cbAtr;
|
||||
|
||||
reader = vreader_get_reader_by_name(r->name);
|
||||
if (!reader) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (connect_card(r)) {
|
||||
return;
|
||||
}
|
||||
|
||||
applet =
|
||||
vcard_new_applet(apdu_cb,
|
||||
reset_cb,
|
||||
(const unsigned char *)CAPCSC_APPLET,
|
||||
strlen(CAPCSC_APPLET));
|
||||
if (!applet) {
|
||||
return;
|
||||
}
|
||||
|
||||
card = vcard_new((VCardEmul *) r, delete_card_cb);
|
||||
if (!card) {
|
||||
vcard_delete_applet(applet);
|
||||
vreader_free(reader);
|
||||
return;
|
||||
}
|
||||
|
||||
vcard_set_type(card, VCARD_DIRECT);
|
||||
vcard_set_atr_func(card, get_atr_cb);
|
||||
vcard_add_applet(card, applet);
|
||||
|
||||
vreader_insert_card(reader, card);
|
||||
vreader_free(reader);
|
||||
}
|
||||
|
||||
static void remove_card(SCardReader *r)
|
||||
{
|
||||
LONG rc;
|
||||
VReader *reader;
|
||||
|
||||
memset(r->atr, 0, sizeof(r->atr));
|
||||
r->atrlen = 0;
|
||||
|
||||
rc = SCardDisconnect(r->card, SCARD_LEAVE_CARD);
|
||||
if (rc != SCARD_S_SUCCESS) {
|
||||
fprintf(stderr, "Non fatal info:"
|
||||
"failed to disconnect card reader: %s (0x%lX)\n",
|
||||
pcsc_stringify_error(rc), rc);
|
||||
}
|
||||
r->card_connected = 0;
|
||||
|
||||
reader = vreader_get_reader_by_name(r->name);
|
||||
if (!reader) {
|
||||
return;
|
||||
}
|
||||
|
||||
vreader_insert_card(reader, NULL);
|
||||
vreader_free(reader);
|
||||
}
|
||||
|
||||
static void process_reader_change(SCardReader *r, SCARD_READERSTATE *s)
|
||||
{
|
||||
if (s->dwEventState & SCARD_STATE_PRESENT) {
|
||||
insert_card(r, s);
|
||||
} else if (s->dwEventState & SCARD_STATE_EMPTY) {
|
||||
remove_card(r);
|
||||
} else {
|
||||
fprintf(stderr, "Unexpected card state change from %lx to %lx:\n",
|
||||
r->state, s->dwEventState);
|
||||
}
|
||||
|
||||
r->state = s->dwEventState & ~SCARD_STATE_CHANGED;
|
||||
}
|
||||
|
||||
/*
|
||||
* This thread looks for card and reader insertions and puts events on the
|
||||
* event queue.
|
||||
*/
|
||||
static gpointer event_thread(gpointer arg)
|
||||
{
|
||||
PCSCContext *pc = (PCSCContext *) arg;
|
||||
DWORD reader_count = 0;
|
||||
SCARD_READERSTATE *reader_states = NULL;
|
||||
LONG rc;
|
||||
|
||||
scan_for_readers(pc);
|
||||
|
||||
do {
|
||||
DWORD i;
|
||||
DWORD timeout = INFINITE;
|
||||
|
||||
g_mutex_lock(&pc->lock);
|
||||
if (pc->readers_changed) {
|
||||
prepare_reader_states(pc, &reader_states, &reader_count);
|
||||
timeout = 0;
|
||||
} else if (reader_count > 1) {
|
||||
timeout = 0;
|
||||
}
|
||||
|
||||
pc->readers_changed = 0;
|
||||
g_mutex_unlock(&pc->lock);
|
||||
|
||||
rc = SCardGetStatusChange(pc->context, timeout, reader_states,
|
||||
reader_count);
|
||||
|
||||
/* If we have a new reader, or an unknown reader,
|
||||
rescan and go back and do it again */
|
||||
if ((rc == SCARD_S_SUCCESS && (reader_states[reader_count - 1].dwEventState & SCARD_STATE_CHANGED))
|
||||
||
|
||||
rc == SCARD_E_UNKNOWN_READER) {
|
||||
scan_for_readers(pc);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rc != SCARD_S_SUCCESS && rc != SCARD_E_TIMEOUT) {
|
||||
fprintf(stderr, "Unexpected SCardGetStatusChange ret %lx(%s)\n",
|
||||
rc, pcsc_stringify_error(rc));
|
||||
continue;
|
||||
}
|
||||
|
||||
g_mutex_lock(&pc->lock);
|
||||
|
||||
for (i = 0; i < reader_count; i++) {
|
||||
if (reader_states[i].dwEventState & SCARD_STATE_CHANGED) {
|
||||
process_reader_change(&pc->readers[i], &reader_states[i]);
|
||||
pc->readers_changed++;
|
||||
}
|
||||
|
||||
}
|
||||
g_mutex_unlock(&pc->lock);
|
||||
|
||||
/* libpcsclite is only thread safe at a high level. If we constantly
|
||||
hold long calls into SCardGetStatusChange, we'll starve any running
|
||||
clients. So, if we have an active session, and nothing has changed
|
||||
on our front, we just idle. */
|
||||
if (!pc->readers_changed && reader_count > 1) {
|
||||
g_usleep(CAPCSC_POLL_TIME * 1000);
|
||||
}
|
||||
|
||||
|
||||
} while (1);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* We poll the PC/SC interface, looking for device changes
|
||||
*/
|
||||
static int new_event_thread(PCSCContext *pc)
|
||||
{
|
||||
pc->thread = g_thread_new("capcsc_event_thread", event_thread, pc);
|
||||
return pc->thread == NULL;
|
||||
}
|
||||
|
||||
|
||||
static PCSCContext context;
|
||||
|
||||
int capcsc_init(void)
|
||||
{
|
||||
g_mutex_init(&context.lock);
|
||||
|
||||
if (init_pcsc(&context)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (new_event_thread(&context)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
#ifndef CAPCSC_H
|
||||
#define CAPCSC_H 1
|
||||
|
||||
#define CAPCSC_POLL_TIME 50 /* ms - Time we will poll for */
|
||||
/* card change when a */
|
||||
/* reader is connected */
|
||||
#define CAPCSC_MAX_READERS 16
|
||||
|
||||
#define CAPCSC_APPLET "CAPCSC APPLET"
|
||||
|
||||
int capcsc_init(void);
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,808 @@
|
|||
/*
|
||||
* Implement the 7816 portion of the card spec
|
||||
*
|
||||
* This code is licensed under the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "glib-compat.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "vcard.h"
|
||||
#include "vcard_emul.h"
|
||||
#include "card_7816.h"
|
||||
|
||||
|
||||
/* Global Platform Card Manager applet AID */
|
||||
static const unsigned char gp_aid[] = {
|
||||
0xa0, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00 };
|
||||
/* Global Platform Card Manager response on select applet */
|
||||
static const unsigned char gp_response[] = {
|
||||
0x6F, 0x19, 0x84, 0x08, 0xA0, 0x00, 0x00, 0x00,
|
||||
0x03, 0x00, 0x00, 0x00, 0xA5, 0x0D, 0x9F, 0x6E,
|
||||
0x06, 0x12, 0x91, 0x51, 0x81, 0x01, 0x00, 0x9F,
|
||||
0x65, 0x01, 0xFF};
|
||||
|
||||
|
||||
/*
|
||||
* set the status bytes based on the status word
|
||||
*/
|
||||
static void
|
||||
vcard_response_set_status(VCardResponse *response, vcard_7816_status_t status)
|
||||
{
|
||||
unsigned char sw1, sw2;
|
||||
response->b_status = status; /* make sure the status and swX representations
|
||||
* are consistent */
|
||||
sw1 = (status >> 8) & 0xff;
|
||||
sw2 = status & 0xff;
|
||||
response->b_sw1 = sw1;
|
||||
response->b_sw2 = sw2;
|
||||
response->b_data[response->b_len] = sw1;
|
||||
response->b_data[response->b_len+1] = sw2;
|
||||
}
|
||||
|
||||
/*
|
||||
* set the status bytes in a response buffer
|
||||
*/
|
||||
void
|
||||
vcard_response_set_status_bytes(VCardResponse *response,
|
||||
unsigned char sw1, unsigned char sw2)
|
||||
{
|
||||
response->b_status = sw1 << 8 | sw2;
|
||||
response->b_sw1 = sw1;
|
||||
response->b_sw2 = sw2;
|
||||
response->b_data[response->b_len] = sw1;
|
||||
response->b_data[response->b_len+1] = sw2;
|
||||
}
|
||||
|
||||
/*
|
||||
* allocate a VCardResponse structure, plus space for the data buffer, and
|
||||
* set up everything but the resonse bytes.
|
||||
*/
|
||||
VCardResponse *
|
||||
vcard_response_new_data(const unsigned char *buf, int len)
|
||||
{
|
||||
VCardResponse *new_response;
|
||||
|
||||
new_response = g_new(VCardResponse, 1);
|
||||
new_response->b_data = g_malloc(len + 2);
|
||||
memcpy(new_response->b_data, buf, len);
|
||||
new_response->b_total_len = len+2;
|
||||
new_response->b_len = len;
|
||||
new_response->b_type = VCARD_MALLOC;
|
||||
return new_response;
|
||||
}
|
||||
|
||||
static VCardResponse *
|
||||
vcard_init_buffer_response(VCard *card, const unsigned char *buf, int len)
|
||||
{
|
||||
VCardResponse *response;
|
||||
VCardBufferResponse *buffer_response;
|
||||
|
||||
buffer_response = vcard_get_buffer_response(card);
|
||||
if (buffer_response) {
|
||||
vcard_set_buffer_response(card, NULL);
|
||||
vcard_buffer_response_delete(buffer_response);
|
||||
}
|
||||
buffer_response = vcard_buffer_response_new(buf, len);
|
||||
if (buffer_response == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
response = vcard_response_new_status_bytes(VCARD7816_SW1_RESPONSE_BYTES,
|
||||
len > 255 ? 0 : len);
|
||||
if (response == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
vcard_set_buffer_response(card, buffer_response);
|
||||
return response;
|
||||
}
|
||||
|
||||
/*
|
||||
* general buffer to hold results from APDU calls
|
||||
*/
|
||||
VCardResponse *
|
||||
vcard_response_new(VCard *card, const unsigned char *buf,
|
||||
int len, int Le, vcard_7816_status_t status)
|
||||
{
|
||||
VCardResponse *new_response;
|
||||
|
||||
g_debug("%s: Sending response (len = %d, Le = %d)", __func__, len, Le);
|
||||
if (len > Le) {
|
||||
return vcard_init_buffer_response(card, buf, len);
|
||||
}
|
||||
new_response = vcard_response_new_data(buf, len);
|
||||
if (new_response == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
vcard_response_set_status(new_response, status);
|
||||
return new_response;
|
||||
}
|
||||
|
||||
/*
|
||||
* general buffer to hold results from APDU calls
|
||||
*/
|
||||
VCardResponse *
|
||||
vcard_response_new_bytes(VCard *card, unsigned char *buf, int len, int Le,
|
||||
unsigned char sw1, unsigned char sw2)
|
||||
{
|
||||
VCardResponse *new_response;
|
||||
|
||||
g_debug("%s: Sending response (len = %d, Le = %d)", __func__, len, Le);
|
||||
if (len > Le) {
|
||||
return vcard_init_buffer_response(card, buf, len);
|
||||
}
|
||||
new_response = vcard_response_new_data(buf, len);
|
||||
if (new_response == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
vcard_response_set_status_bytes(new_response, sw1, sw2);
|
||||
return new_response;
|
||||
}
|
||||
|
||||
/*
|
||||
* get a new Response buffer that only has a status.
|
||||
*/
|
||||
static VCardResponse *
|
||||
vcard_response_new_status(vcard_7816_status_t status)
|
||||
{
|
||||
VCardResponse *new_response;
|
||||
|
||||
new_response = g_new(VCardResponse, 1);
|
||||
new_response->b_data = &new_response->b_sw1;
|
||||
new_response->b_len = 0;
|
||||
new_response->b_total_len = 2;
|
||||
new_response->b_type = VCARD_MALLOC_STRUCT;
|
||||
vcard_response_set_status(new_response, status);
|
||||
return new_response;
|
||||
}
|
||||
|
||||
/*
|
||||
* same as above, but specify the status as separate bytes
|
||||
*/
|
||||
VCardResponse *
|
||||
vcard_response_new_status_bytes(unsigned char sw1, unsigned char sw2)
|
||||
{
|
||||
VCardResponse *new_response;
|
||||
|
||||
new_response = g_new(VCardResponse, 1);
|
||||
new_response->b_data = &new_response->b_sw1;
|
||||
new_response->b_len = 0;
|
||||
new_response->b_total_len = 2;
|
||||
new_response->b_type = VCARD_MALLOC_STRUCT;
|
||||
vcard_response_set_status_bytes(new_response, sw1, sw2);
|
||||
return new_response;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* free the response buffer. The Buffer has a type to handle the buffer
|
||||
* allocated in other ways than through malloc.
|
||||
*/
|
||||
void
|
||||
vcard_response_delete(VCardResponse *response)
|
||||
{
|
||||
if (response == NULL) {
|
||||
return;
|
||||
}
|
||||
switch (response->b_type) {
|
||||
case VCARD_MALLOC:
|
||||
/* everything was malloc'ed */
|
||||
g_free(response->b_data);
|
||||
g_free(response);
|
||||
break;
|
||||
case VCARD_MALLOC_DATA:
|
||||
/* only the data buffer was malloc'ed */
|
||||
g_free(response->b_data);
|
||||
break;
|
||||
case VCARD_MALLOC_STRUCT:
|
||||
/* only the structure was malloc'ed */
|
||||
g_free(response);
|
||||
break;
|
||||
case VCARD_STATIC:
|
||||
break;
|
||||
default:
|
||||
g_warn_if_reached();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* decode the class bit and set our generic type field, channel, and
|
||||
* secure messaging values.
|
||||
*/
|
||||
static vcard_7816_status_t
|
||||
vcard_apdu_set_class(VCardAPDU *apdu) {
|
||||
apdu->a_channel = 0;
|
||||
apdu->a_secure_messaging = 0;
|
||||
apdu->a_type = apdu->a_cla & 0xf0;
|
||||
apdu->a_gen_type = VCARD_7816_ISO;
|
||||
|
||||
/* parse the class tables 8 & 9 of the 7816-4 Part 4 spec */
|
||||
switch (apdu->a_type) {
|
||||
/* we only support the basic types */
|
||||
case 0x00:
|
||||
case 0x80:
|
||||
case 0x90:
|
||||
case 0xa0:
|
||||
apdu->a_channel = apdu->a_cla & 3;
|
||||
apdu->a_secure_messaging = apdu->a_cla & 0xe;
|
||||
break;
|
||||
case 0xb0:
|
||||
case 0xc0:
|
||||
break;
|
||||
|
||||
case 0x10:
|
||||
case 0x20:
|
||||
case 0x30:
|
||||
case 0x40:
|
||||
case 0x50:
|
||||
case 0x60:
|
||||
case 0x70:
|
||||
/* Reserved for future use */
|
||||
apdu->a_gen_type = VCARD_7816_RFU;
|
||||
break;
|
||||
case 0xd0:
|
||||
case 0xe0:
|
||||
case 0xf0:
|
||||
default:
|
||||
apdu->a_gen_type =
|
||||
(apdu->a_cla == 0xff) ? VCARD_7816_PTS : VCARD_7816_PROPRIETARY;
|
||||
break;
|
||||
}
|
||||
return VCARD7816_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* set the Le and Lc fields according to table 5 of the
|
||||
* 7816-4 part 4 spec
|
||||
*/
|
||||
static vcard_7816_status_t
|
||||
vcard_apdu_set_length(VCardAPDU *apdu)
|
||||
{
|
||||
int L, Le;
|
||||
|
||||
/* process according to table 5 of the 7816-4 Part 4 spec.
|
||||
* variable names match the variables in the spec */
|
||||
L = apdu->a_len-4; /* fixed APDU header */
|
||||
apdu->a_Lc = 0;
|
||||
apdu->a_Le = 0;
|
||||
apdu->a_body = NULL;
|
||||
switch (L) {
|
||||
case 0:
|
||||
/* 1 minimal apdu */
|
||||
return VCARD7816_STATUS_SUCCESS;
|
||||
case 1:
|
||||
/* 2S only return values apdu */
|
||||
/* zero maps to 256 here */
|
||||
apdu->a_Le = apdu->a_header->ah_Le ?
|
||||
apdu->a_header->ah_Le : 256;
|
||||
return VCARD7816_STATUS_SUCCESS;
|
||||
default:
|
||||
/* if the ah_Le byte is zero and we have more than
|
||||
* 1 byte in the header, then we must be using extended Le and Lc.
|
||||
* process the extended now. */
|
||||
if (apdu->a_header->ah_Le == 0) {
|
||||
if (L < 3) {
|
||||
/* coding error, need at least 3 bytes */
|
||||
return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
|
||||
}
|
||||
/* calculate the first extended value. Could be either Le or Lc */
|
||||
Le = (apdu->a_header->ah_body[0] << 8)
|
||||
| apdu->a_header->ah_body[1];
|
||||
if (L == 3) {
|
||||
/* 2E extended, return data only */
|
||||
/* zero maps to 65536 */
|
||||
apdu->a_Le = Le ? Le : 65536;
|
||||
return VCARD7816_STATUS_SUCCESS;
|
||||
}
|
||||
if (Le == 0) {
|
||||
/* reserved for future use, probably for next time we need
|
||||
* to extend the lengths */
|
||||
return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
|
||||
}
|
||||
/* we know that the first extended value is Lc now */
|
||||
apdu->a_Lc = Le;
|
||||
apdu->a_body = &apdu->a_header->ah_body[2];
|
||||
if (L == Le+3) {
|
||||
/* 3E extended, only body parameters */
|
||||
return VCARD7816_STATUS_SUCCESS;
|
||||
}
|
||||
if (L == Le+5) {
|
||||
/* 4E extended, parameters and return data */
|
||||
Le = (apdu->a_data[apdu->a_len-2] << 8)
|
||||
| apdu->a_data[apdu->a_len-1];
|
||||
apdu->a_Le = Le ? Le : 65536;
|
||||
return VCARD7816_STATUS_SUCCESS;
|
||||
}
|
||||
return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
|
||||
}
|
||||
/* not extended */
|
||||
apdu->a_Lc = apdu->a_header->ah_Le;
|
||||
apdu->a_body = &apdu->a_header->ah_body[0];
|
||||
if (L == apdu->a_Lc + 1) {
|
||||
/* 3S only body parameters */
|
||||
return VCARD7816_STATUS_SUCCESS;
|
||||
}
|
||||
if (L == apdu->a_Lc + 2) {
|
||||
/* 4S parameters and return data */
|
||||
Le = apdu->a_data[apdu->a_len-1];
|
||||
apdu->a_Le = Le ? Le : 256;
|
||||
return VCARD7816_STATUS_SUCCESS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
|
||||
}
|
||||
|
||||
/*
|
||||
* create a new APDU from a raw set of bytes. This will decode all the
|
||||
* above fields. users of VCARDAPDU's can then depend on the already decoded
|
||||
* values.
|
||||
*/
|
||||
VCardAPDU *
|
||||
vcard_apdu_new(unsigned char *raw_apdu, int len, vcard_7816_status_t *status)
|
||||
{
|
||||
VCardAPDU *new_apdu;
|
||||
|
||||
*status = VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE;
|
||||
if (len < 4) {
|
||||
*status = VCARD7816_STATUS_ERROR_WRONG_LENGTH;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
new_apdu = g_new(VCardAPDU, 1);
|
||||
new_apdu->a_data = g_memdup(raw_apdu, len);
|
||||
new_apdu->a_len = len;
|
||||
*status = vcard_apdu_set_class(new_apdu);
|
||||
if (*status != VCARD7816_STATUS_SUCCESS) {
|
||||
vcard_apdu_delete(new_apdu);
|
||||
return NULL;
|
||||
}
|
||||
*status = vcard_apdu_set_length(new_apdu);
|
||||
if (*status != VCARD7816_STATUS_SUCCESS) {
|
||||
vcard_apdu_delete(new_apdu);
|
||||
new_apdu = NULL;
|
||||
}
|
||||
return new_apdu;
|
||||
}
|
||||
|
||||
void
|
||||
vcard_apdu_delete(VCardAPDU *apdu)
|
||||
{
|
||||
if (apdu == NULL) {
|
||||
return;
|
||||
}
|
||||
g_free(apdu->a_data);
|
||||
g_free(apdu);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* declare response buffers for all the 7816 defined error codes
|
||||
*/
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_SUCCESS)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_RET_CORUPT)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_CHANGE)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_FILE_FILLED)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR_CHANGE)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_LENGTH)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(
|
||||
VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_INVALID)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_NO_EF)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_PARAMETERS)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(
|
||||
VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_FILE_NOT_FOUND)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_P1_P2_INCORRECT)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_NOT_FOUND)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_INS_CODE_INVALID)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CLA_INVALID)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_GENERAL)
|
||||
|
||||
/*
|
||||
* return a single response code. This function cannot fail. It will always
|
||||
* return a response.
|
||||
*/
|
||||
VCardResponse *
|
||||
vcard_make_response(vcard_7816_status_t status)
|
||||
{
|
||||
VCardResponse *response;
|
||||
|
||||
switch (status) {
|
||||
/* known 7816 response codes */
|
||||
case VCARD7816_STATUS_SUCCESS:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_SUCCESS);
|
||||
case VCARD7816_STATUS_WARNING:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_WARNING);
|
||||
case VCARD7816_STATUS_WARNING_RET_CORUPT:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_WARNING_RET_CORUPT);
|
||||
case VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE);
|
||||
case VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED);
|
||||
case VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID);
|
||||
case VCARD7816_STATUS_WARNING_CHANGE:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_WARNING_CHANGE);
|
||||
case VCARD7816_STATUS_WARNING_FILE_FILLED:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_WARNING_FILE_FILLED);
|
||||
case VCARD7816_STATUS_EXC_ERROR:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_EXC_ERROR);
|
||||
case VCARD7816_STATUS_EXC_ERROR_CHANGE:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_EXC_ERROR_CHANGE);
|
||||
case VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
|
||||
case VCARD7816_STATUS_ERROR_WRONG_LENGTH:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_WRONG_LENGTH);
|
||||
case VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED);
|
||||
case VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED);
|
||||
case VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED);
|
||||
case VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
|
||||
case VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE);
|
||||
case VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED);
|
||||
case VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED);
|
||||
case VCARD7816_STATUS_ERROR_DATA_INVALID:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_DATA_INVALID);
|
||||
case VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED);
|
||||
case VCARD7816_STATUS_ERROR_DATA_NO_EF:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_DATA_NO_EF);
|
||||
case VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING);
|
||||
case VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT);
|
||||
case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_WRONG_PARAMETERS);
|
||||
case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA);
|
||||
case VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED);
|
||||
case VCARD7816_STATUS_ERROR_FILE_NOT_FOUND:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_FILE_NOT_FOUND);
|
||||
case VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND);
|
||||
case VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE);
|
||||
case VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT);
|
||||
case VCARD7816_STATUS_ERROR_P1_P2_INCORRECT:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_P1_P2_INCORRECT);
|
||||
case VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT);
|
||||
case VCARD7816_STATUS_ERROR_DATA_NOT_FOUND:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_DATA_NOT_FOUND);
|
||||
case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2);
|
||||
case VCARD7816_STATUS_ERROR_INS_CODE_INVALID:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_INS_CODE_INVALID);
|
||||
case VCARD7816_STATUS_ERROR_CLA_INVALID:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_CLA_INVALID);
|
||||
case VCARD7816_STATUS_ERROR_GENERAL:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_GENERAL);
|
||||
default:
|
||||
/* we don't know this status code, create a response buffer to
|
||||
* hold it */
|
||||
response = vcard_response_new_status(status);
|
||||
if (response == NULL) {
|
||||
/* couldn't allocate the buffer, return memmory error */
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Add File card support here if you need it.
|
||||
*/
|
||||
static VCardStatus
|
||||
vcard7816_file_system_process_apdu(VCard *card, VCardAPDU *apdu,
|
||||
VCardResponse **response)
|
||||
{
|
||||
/* TODO: if we want to support a virtual file system card, we do it here.
|
||||
* It would probably be a pkcs #15 card type */
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
|
||||
return VCARD_DONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* VM card (including java cards)
|
||||
*/
|
||||
static VCardStatus
|
||||
vcard7816_vm_process_apdu(VCard *card, VCardAPDU *apdu,
|
||||
VCardResponse **response)
|
||||
{
|
||||
int bytes_to_copy, next_byte_count, count;
|
||||
VCardApplet *current_applet;
|
||||
VCardBufferResponse *buffer_response;
|
||||
vcard_7816_status_t status;
|
||||
|
||||
/* parse the class first */
|
||||
if (apdu->a_gen_type != VCARD_7816_ISO) {
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
|
||||
return VCARD_DONE;
|
||||
}
|
||||
|
||||
/* use a switch so that if we need to support secure channel stuff later,
|
||||
* we know where to put it */
|
||||
switch (apdu->a_secure_messaging) {
|
||||
case 0x0: /* no SM */
|
||||
break;
|
||||
case 0x4: /* proprietary SM */
|
||||
case 0x8: /* header not authenticated */
|
||||
case 0xc: /* header authenticated */
|
||||
default:
|
||||
/* for now, don't try to support secure channel stuff in the
|
||||
* virtual card. */
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED);
|
||||
return VCARD_DONE;
|
||||
}
|
||||
|
||||
/* now parse the instruction */
|
||||
switch (apdu->a_ins) {
|
||||
case VCARD7816_INS_MANAGE_CHANNEL: /* secure channel op */
|
||||
case VCARD7816_INS_EXTERNAL_AUTHENTICATE: /* secure channel op */
|
||||
case VCARD7816_INS_GET_CHALLENGE: /* secure channel op */
|
||||
case VCARD7816_INS_INTERNAL_AUTHENTICATE: /* secure channel op */
|
||||
case VCARD7816_INS_ERASE_BINARY: /* applet control op */
|
||||
case VCARD7816_INS_READ_BINARY: /* applet control op */
|
||||
case VCARD7816_INS_WRITE_BINARY: /* applet control op */
|
||||
case VCARD7816_INS_UPDATE_BINARY: /* applet control op */
|
||||
case VCARD7816_INS_READ_RECORD: /* file op */
|
||||
case VCARD7816_INS_WRITE_RECORD: /* file op */
|
||||
case VCARD7816_INS_UPDATE_RECORD: /* file op */
|
||||
case VCARD7816_INS_APPEND_RECORD: /* file op */
|
||||
case VCARD7816_INS_ENVELOPE:
|
||||
case VCARD7816_INS_PUT_DATA:
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
|
||||
break;
|
||||
|
||||
case VCARD7816_INS_SELECT_FILE:
|
||||
/* GSC-IS: 5.3.3.2 Select Applet APDU: P1 = 0x04 */
|
||||
if (apdu->a_p1 != 0x04) {
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED);
|
||||
break;
|
||||
}
|
||||
|
||||
/* side effect, deselect the current applet if no applet has been found
|
||||
*/
|
||||
current_applet = vcard_find_applet(card, apdu->a_body, apdu->a_Lc);
|
||||
vcard_select_applet(card, apdu->a_channel, current_applet);
|
||||
if (current_applet) {
|
||||
VCardApplet *gp_applet = vcard_find_applet(card,
|
||||
gp_aid, sizeof(gp_aid));
|
||||
if (current_applet == gp_applet) {
|
||||
/* if the new applet is Global Platform Card Manager, we need to
|
||||
* return a response (from Card Specification v2.3.1):
|
||||
*
|
||||
* 6F 19 : FCI Template
|
||||
* 84 08 : Application / file AID
|
||||
* A0 00 00 00 03 00 00 00
|
||||
* A5 0D : Proprietary data
|
||||
* 9F 6E 06 : Application Producution Life Cycle
|
||||
* 12 91 51 81 01 00
|
||||
* 9F 65 01 : Maximum Length of data field in comand message
|
||||
* FF
|
||||
*/
|
||||
*response = vcard_response_new(card, gp_response,
|
||||
sizeof(gp_response), apdu->a_Le, VCARD7816_STATUS_SUCCESS);
|
||||
} else {
|
||||
unsigned char fci_template[] = {
|
||||
0x6F, 0x0B, 0x84, 0x07, 0xA0, 0x00, 0x00, 0x00,
|
||||
0x79, 0x03, 0x00, 0xA5, 0x00};
|
||||
/* with GSC-IS 2 applets, we do not need to return anything
|
||||
* for select applet, but cards generally do, at least this
|
||||
* FCI template stub:
|
||||
*
|
||||
* 6F 0B : FCI Template
|
||||
* 84 07 : Application / file AID
|
||||
* A0 00 00 00 79 03 00
|
||||
* A5 00 : Proprietary data
|
||||
*/
|
||||
/* Insert the correct AID in the structure */
|
||||
g_assert_cmpint(apdu->a_Lc, ==, 7);
|
||||
memcpy(&fci_template[4], apdu->a_body, apdu->a_Lc);
|
||||
*response = vcard_response_new(card, fci_template,
|
||||
sizeof(fci_template), apdu->a_Le, VCARD7816_STATUS_SUCCESS);
|
||||
}
|
||||
} else {
|
||||
/* the real CAC returns (SW1=0x6A, SW2=0x82) */
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_FILE_NOT_FOUND);
|
||||
}
|
||||
break;
|
||||
|
||||
case VCARD7816_INS_VERIFY:
|
||||
if ((apdu->a_p1 != 0x00) || (apdu->a_p2 != 0x00)) {
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_WRONG_PARAMETERS);
|
||||
} else {
|
||||
if (apdu->a_Lc == 0) {
|
||||
/* handle pin count if possible */
|
||||
count = vcard_emul_get_login_count(card);
|
||||
if (count < 0) {
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_DATA_NOT_FOUND);
|
||||
} else {
|
||||
if (count > 0xf) {
|
||||
count = 0xf;
|
||||
}
|
||||
*response = vcard_response_new_status_bytes(
|
||||
VCARD7816_SW1_WARNING_CHANGE,
|
||||
0xc0 | count);
|
||||
if (*response == NULL) {
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
status = vcard_emul_login(card, apdu->a_body, apdu->a_Lc);
|
||||
*response = vcard_make_response(status);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case VCARD7816_INS_GET_RESPONSE:
|
||||
buffer_response = vcard_get_buffer_response(card);
|
||||
if (!buffer_response) {
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_DATA_NOT_FOUND);
|
||||
/* handle error */
|
||||
break;
|
||||
}
|
||||
bytes_to_copy = MIN(buffer_response->len, apdu->a_Le);
|
||||
next_byte_count = MIN(256, buffer_response->len - bytes_to_copy);
|
||||
*response = vcard_response_new_bytes(
|
||||
card, buffer_response->current, bytes_to_copy,
|
||||
apdu->a_Le,
|
||||
next_byte_count ?
|
||||
VCARD7816_SW1_RESPONSE_BYTES : VCARD7816_SW1_SUCCESS,
|
||||
next_byte_count);
|
||||
buffer_response->current += bytes_to_copy;
|
||||
buffer_response->len -= bytes_to_copy;
|
||||
if (*response == NULL || (next_byte_count == 0)) {
|
||||
vcard_set_buffer_response(card, NULL);
|
||||
vcard_buffer_response_delete(buffer_response);
|
||||
}
|
||||
if (*response == NULL) {
|
||||
*response =
|
||||
vcard_make_response(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
|
||||
}
|
||||
break;
|
||||
|
||||
case VCARD7816_INS_GET_DATA:
|
||||
*response =
|
||||
vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
|
||||
break;
|
||||
|
||||
default:
|
||||
*response =
|
||||
vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
|
||||
break;
|
||||
}
|
||||
|
||||
/* response should have been set somewhere */
|
||||
g_assert(*response != NULL);
|
||||
return VCARD_DONE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* APDU processing starts here. This routes the card processing stuff to the
|
||||
* right location.
|
||||
*/
|
||||
VCardStatus
|
||||
vcard_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response)
|
||||
{
|
||||
VCardStatus status;
|
||||
VCardBufferResponse *buffer_response;
|
||||
|
||||
/* first handle any PTS commands, which aren't really APDU's */
|
||||
if (apdu->a_type == VCARD_7816_PTS) {
|
||||
/* the PTS responses aren't really responses either */
|
||||
*response = vcard_response_new_data(apdu->a_data, apdu->a_len);
|
||||
/* PTS responses have no status bytes */
|
||||
(*response)->b_total_len = (*response)->b_len;
|
||||
return VCARD_DONE;
|
||||
}
|
||||
buffer_response = vcard_get_buffer_response(card);
|
||||
if (buffer_response && apdu->a_ins != VCARD7816_INS_GET_RESPONSE) {
|
||||
/* clear out buffer_response, do not return an error */
|
||||
vcard_set_buffer_response(card, NULL);
|
||||
vcard_buffer_response_delete(buffer_response);
|
||||
}
|
||||
|
||||
status = vcard_process_applet_apdu(card, apdu, response);
|
||||
if (status != VCARD_NEXT) {
|
||||
return status;
|
||||
}
|
||||
switch (vcard_get_type(card)) {
|
||||
case VCARD_FILE_SYSTEM:
|
||||
return vcard7816_file_system_process_apdu(card, apdu, response);
|
||||
case VCARD_VM:
|
||||
return vcard7816_vm_process_apdu(card, apdu, response);
|
||||
case VCARD_DIRECT:
|
||||
/* if we are type direct, then the applet should handle everything */
|
||||
g_assert(!"VCARD_DIRECT: applet failure");
|
||||
break;
|
||||
default:
|
||||
g_warn_if_reached();
|
||||
}
|
||||
*response =
|
||||
vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
|
||||
return VCARD_DONE;
|
||||
}
|
||||
|
||||
/* vim: set ts=4 sw=4 tw=0 noet expandtab: */
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Implement the 7816 portion of the card spec
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
#ifndef CARD_7816_H
|
||||
#define CARD_7816_H 1
|
||||
|
||||
#include "card_7816t.h"
|
||||
#include "vcardt.h"
|
||||
|
||||
/*
|
||||
* constructors for VCardResponse's
|
||||
*/
|
||||
/* response from a return buffer and a status */
|
||||
VCardResponse *vcard_response_new(VCard *card, const unsigned char *buf, int len,
|
||||
int Le, vcard_7816_status_t status);
|
||||
/* response from a return buffer and status bytes */
|
||||
VCardResponse *vcard_response_new_bytes(VCard *card, unsigned char *buf,
|
||||
int len, int Le,
|
||||
unsigned char sw1, unsigned char sw2);
|
||||
/* response from just status bytes */
|
||||
VCardResponse *vcard_response_new_status_bytes(unsigned char sw1,
|
||||
unsigned char sw2);
|
||||
/* response from just status: NOTE this cannot fail, it will always return a
|
||||
* valid response, if it can't allocate memory, the response will be
|
||||
* VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE */
|
||||
VCardResponse *vcard_make_response(vcard_7816_status_t status);
|
||||
|
||||
/* create a raw response (status has already been encoded */
|
||||
VCardResponse *vcard_response_new_data(const unsigned char *buf, int len);
|
||||
|
||||
void vcard_response_set_status_bytes(VCardResponse *response,
|
||||
unsigned char sw1, unsigned char sw2);
|
||||
|
||||
/*
|
||||
* destructor for VCardResponse.
|
||||
* Can be called with a NULL response
|
||||
*/
|
||||
void vcard_response_delete(VCardResponse *response);
|
||||
|
||||
/*
|
||||
* constructor for VCardAPDU
|
||||
*/
|
||||
VCardAPDU *vcard_apdu_new(unsigned char *raw_apdu, int len,
|
||||
unsigned short *status);
|
||||
|
||||
/*
|
||||
* destructor for VCardAPDU
|
||||
* Can be called with a NULL apdu
|
||||
*/
|
||||
void vcard_apdu_delete(VCardAPDU *apdu);
|
||||
|
||||
/*
|
||||
* APDU processing starts here. This routes the card processing stuff to the
|
||||
* right location. Always returns a valid response.
|
||||
*/
|
||||
VCardStatus vcard_process_apdu(VCard *card, VCardAPDU *apdu,
|
||||
VCardResponse **response);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* Implement the 7816 portion of the card spec
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
#ifndef CARD_7816T_H
|
||||
#define CARD_7816T_H 1
|
||||
|
||||
typedef unsigned short vcard_7816_status_t;
|
||||
|
||||
struct VCardResponseStruct {
|
||||
unsigned char *b_data;
|
||||
vcard_7816_status_t b_status;
|
||||
unsigned char b_sw1;
|
||||
unsigned char b_sw2;
|
||||
int b_len;
|
||||
int b_total_len;
|
||||
enum VCardResponseBufferType {
|
||||
VCARD_MALLOC,
|
||||
VCARD_MALLOC_DATA,
|
||||
VCARD_MALLOC_STRUCT,
|
||||
VCARD_STATIC
|
||||
} b_type;
|
||||
};
|
||||
|
||||
#define VCARD_RESPONSE_NEW_STATIC_STATUS(stat) \
|
||||
static const VCardResponse VCardResponse##stat = \
|
||||
{(unsigned char *)&VCardResponse##stat.b_sw1, (stat), ((stat) >> 8), \
|
||||
((stat) & 0xff), 0, 2, VCARD_STATIC};
|
||||
|
||||
#define VCARD_RESPONSE_NEW_STATIC_STATUS_BYTES(sw1, sw2) \
|
||||
static const VCardResponse VCARDResponse##sw1 = \
|
||||
{(unsigned char *)&VCardResponse##name.b_sw1, ((sw1) << 8 | (sw2)), \
|
||||
(sw1), (sw2), 0, 2, VCARD_STATIC};
|
||||
|
||||
/* cast away the const, callers need may need to 'free' the
|
||||
* result, and const implies that they don't */
|
||||
#define VCARD_RESPONSE_GET_STATIC(name) \
|
||||
((VCardResponse *)(&VCardResponse##name))
|
||||
|
||||
typedef enum {
|
||||
VCARD_7816_ISO,
|
||||
VCARD_7816_RFU,
|
||||
VCARD_7816_PTS,
|
||||
VCARD_7816_PROPRIETARY
|
||||
} VCardAPDUType;
|
||||
|
||||
|
||||
/*
|
||||
* 7816 header. All APDU's have this header.
|
||||
* They must be laid out in this order.
|
||||
*/
|
||||
struct VCardAPDUHeader {
|
||||
unsigned char ah_cla;
|
||||
unsigned char ah_ins;
|
||||
unsigned char ah_p1;
|
||||
unsigned char ah_p2;
|
||||
unsigned char ah_Le;
|
||||
unsigned char ah_body[1]; /* indefinite length */
|
||||
};
|
||||
|
||||
/*
|
||||
* 7816 APDU structure. The raw bytes are stored in the union and can be
|
||||
* accessed directly through u.data (which is aliased as a_data).
|
||||
*
|
||||
* Names of the fields match the 7816 documentation.
|
||||
*/
|
||||
struct VCardAPDUStruct {
|
||||
int a_len; /* length of the whole buffer, including header */
|
||||
int a_Lc; /* 7816 Lc (parameter length) value */
|
||||
int a_Le; /* 7816 Le (expected result length) value */
|
||||
unsigned char *a_body; /* pointer to the parameter */
|
||||
int a_channel; /* decoded channel */
|
||||
int a_secure_messaging; /* decoded secure messaging type */
|
||||
int a_type; /* decoded type from cla (top nibble of class) */
|
||||
VCardAPDUType a_gen_type; /* generic type (7816, PROPRIETARY, RFU, etc) */
|
||||
union {
|
||||
struct VCardAPDUHeader *header;
|
||||
unsigned char *data;
|
||||
} u;
|
||||
/* give the subfields a unified look */
|
||||
#define a_header u.header
|
||||
#define a_data u.data
|
||||
#define a_cla a_header->ah_cla /* class */
|
||||
#define a_ins a_header->ah_ins /* instruction */
|
||||
#define a_p1 a_header->ah_p1 /* parameter 1 */
|
||||
#define a_p2 a_header->ah_p2 /* parameter 2 */
|
||||
};
|
||||
|
||||
/* 7816 status codes */
|
||||
#define VCARD7816_STATUS_SUCCESS 0x9000
|
||||
#define VCARD7816_STATUS_WARNING 0x6200
|
||||
#define VCARD7816_STATUS_WARNING_RET_CORUPT 0x6281
|
||||
#define VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE 0x6282
|
||||
#define VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED 0x6283
|
||||
#define VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID 0x6284
|
||||
#define VCARD7816_STATUS_WARNING_CHANGE 0x6300
|
||||
#define VCARD7816_STATUS_WARNING_FILE_FILLED 0x6381
|
||||
#define VCARD7816_STATUS_EXC_ERROR 0x6400
|
||||
#define VCARD7816_STATUS_EXC_ERROR_CHANGE 0x6500
|
||||
#define VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE 0x6581
|
||||
#define VCARD7816_STATUS_ERROR_WRONG_LENGTH 0x6700
|
||||
#define VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED 0x6800
|
||||
#define VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED 0x6881
|
||||
#define VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED 0x6882
|
||||
#define VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED 0x6900
|
||||
#define VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE 0x6981
|
||||
#define VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED 0x6982
|
||||
#define VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED 0x6983
|
||||
#define VCARD7816_STATUS_ERROR_DATA_INVALID 0x6984
|
||||
#define VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED 0x6985
|
||||
#define VCARD7816_STATUS_ERROR_DATA_NO_EF 0x6986
|
||||
#define VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING 0x6987
|
||||
#define VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT 0x6988
|
||||
#define VCARD7816_STATUS_ERROR_WRONG_PARAMETERS 0x6a00
|
||||
#define VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA 0x6a80
|
||||
#define VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED 0x6a81
|
||||
#define VCARD7816_STATUS_ERROR_FILE_NOT_FOUND 0x6a82
|
||||
#define VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND 0x6a83
|
||||
#define VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE 0x6a84
|
||||
#define VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT 0x6a85
|
||||
#define VCARD7816_STATUS_ERROR_P1_P2_INCORRECT 0x6a86
|
||||
#define VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT 0x6a87
|
||||
#define VCARD7816_STATUS_ERROR_DATA_NOT_FOUND 0x6a88
|
||||
#define VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2 0x6b00
|
||||
#define VCARD7816_STATUS_ERROR_INS_CODE_INVALID 0x6d00
|
||||
#define VCARD7816_STATUS_ERROR_CLA_INVALID 0x6e00
|
||||
#define VCARD7816_STATUS_ERROR_GENERAL 0x6f00
|
||||
/* 7816 sw1 codes */
|
||||
#define VCARD7816_SW1_SUCCESS 0x90
|
||||
#define VCARD7816_SW1_RESPONSE_BYTES 0x61
|
||||
#define VCARD7816_SW1_WARNING 0x62
|
||||
#define VCARD7816_SW1_WARNING_CHANGE 0x63
|
||||
#define VCARD7816_SW1_EXC_ERROR 0x64
|
||||
#define VCARD7816_SW1_EXC_ERROR_CHANGE 0x65
|
||||
#define VCARD7816_SW1_ERROR_WRONG_LENGTH 0x67
|
||||
#define VCARD7816_SW1_CLA_ERROR 0x68
|
||||
#define VCARD7816_SW1_COMMAND_ERROR 0x69
|
||||
#define VCARD7816_SW1_P1_P2_ERROR 0x6a
|
||||
#define VCARD7816_SW1_LE_ERROR 0x6c
|
||||
#define VCARD7816_SW1_INS_ERROR 0x6d
|
||||
#define VCARD7816_SW1_CLA_NOT_SUPPORTED 0x6e
|
||||
|
||||
/* 7816 Instructions */
|
||||
#define VCARD7816_INS_MANAGE_CHANNEL 0x70
|
||||
#define VCARD7816_INS_EXTERNAL_AUTHENTICATE 0x82
|
||||
#define VCARD7816_INS_GET_CHALLENGE 0x84
|
||||
#define VCARD7816_INS_INTERNAL_AUTHENTICATE 0x88
|
||||
#define VCARD7816_INS_ERASE_BINARY 0x0e
|
||||
#define VCARD7816_INS_READ_BINARY 0xb0
|
||||
#define VCARD7816_INS_WRITE_BINARY 0xd0
|
||||
#define VCARD7816_INS_UPDATE_BINARY 0xd6
|
||||
#define VCARD7816_INS_READ_RECORD 0xb2
|
||||
#define VCARD7816_INS_WRITE_RECORD 0xd2
|
||||
#define VCARD7816_INS_UPDATE_RECORD 0xdc
|
||||
#define VCARD7816_INS_APPEND_RECORD 0xe2
|
||||
#define VCARD7816_INS_ENVELOPE 0xc2
|
||||
#define VCARD7816_INS_PUT_DATA 0xda
|
||||
#define VCARD7816_INS_GET_DATA 0xca
|
||||
#define VCARD7816_INS_SELECT_FILE 0xa4
|
||||
#define VCARD7816_INS_VERIFY 0x20
|
||||
#define VCARD7816_INS_GET_RESPONSE 0xc0
|
||||
|
||||
#endif
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* common.c: Utility functions for libcacard
|
||||
*
|
||||
* Copyright (C) 2016 - 2018 Red Hat, Inc.
|
||||
*
|
||||
* Authors: Robert Relyea <rrelyea@redhat.com>
|
||||
* Jakub Jelen <jjelen@redhat.com>
|
||||
*
|
||||
* 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.1 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
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "vcard.h"
|
||||
#include "common.h"
|
||||
|
||||
unsigned char *
|
||||
ushort2lebytes(unsigned char *buf, unsigned short x)
|
||||
{
|
||||
if (buf != NULL) {
|
||||
buf[0] = (unsigned char) (x & 0xff);
|
||||
buf[1] = (unsigned char) ((x >> 8) & 0xff);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
unsigned short
|
||||
lebytes2ushort(const unsigned char *buf)
|
||||
{
|
||||
if (buf == NULL)
|
||||
return 0U;
|
||||
return (unsigned short)buf[1] << 8 | (unsigned short)buf[0];
|
||||
}
|
||||
|
||||
#define MAX_STATIC_BYTES 1024
|
||||
static char hexdump_buffer[5*MAX_STATIC_BYTES + 1];
|
||||
/*
|
||||
* Creates printable representation in hexadecimal format of the data
|
||||
* provided in the buf buffer. A static buffer will be used, which
|
||||
* can hold up to 1024 bytes (longer will get truncated).
|
||||
*
|
||||
* The dumping loop will print 5 visible characters at a time, but since it's
|
||||
* using sprintf, we also need to account for the '\0' it appends to the end of
|
||||
* the string on the last iteration, or we'll overflow the buffer we are
|
||||
* printing to.
|
||||
*/
|
||||
char *
|
||||
hex_dump(const unsigned char *buf, size_t buflen)
|
||||
{
|
||||
char *p, *start;
|
||||
size_t i;
|
||||
|
||||
if (buflen <= 0)
|
||||
return NULL;
|
||||
|
||||
start = hexdump_buffer;
|
||||
buflen = MIN(buflen, MAX_STATIC_BYTES);
|
||||
|
||||
p = start;
|
||||
for (i = 0; i < buflen; i++) {
|
||||
sprintf(p, "0x%02X ", buf[i]);
|
||||
p += 5;
|
||||
}
|
||||
/* terminate */
|
||||
*--p = '\x00';
|
||||
return start;
|
||||
}
|
||||
/* vim: set ts=4 sw=4 tw=0 noet expandtab: */
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* common.h: Utility functions for libcacard
|
||||
*
|
||||
* Copyright (C) 2016 - 2018 Red Hat, Inc.
|
||||
*
|
||||
* Authors: Robert Relyea <rrelyea@redhat.com>
|
||||
* Jakub Jelen <jjelen@redhat.com>
|
||||
*
|
||||
* 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.1 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
|
||||
*/
|
||||
|
||||
#ifndef _COMMON_H
|
||||
#define _COMMON_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
unsigned char *ushort2lebytes(unsigned char *buf, unsigned short x);
|
||||
unsigned short lebytes2ushort(const unsigned char *buf);
|
||||
|
||||
char *hex_dump(const unsigned char *buf, size_t buflen);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* event queue implementation.
|
||||
*
|
||||
* This code is licensed under the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "glib-compat.h"
|
||||
|
||||
#include "vcard.h"
|
||||
#include "vreader.h"
|
||||
#include "vevent.h"
|
||||
|
||||
VEvent *
|
||||
vevent_new(VEventType type, VReader *reader, VCard *card)
|
||||
{
|
||||
VEvent *new_vevent;
|
||||
|
||||
new_vevent = g_new(VEvent, 1);
|
||||
new_vevent->next = NULL;
|
||||
new_vevent->type = type;
|
||||
new_vevent->reader = vreader_reference(reader);
|
||||
new_vevent->card = vcard_reference(card);
|
||||
|
||||
return new_vevent;
|
||||
}
|
||||
|
||||
void
|
||||
vevent_delete(VEvent *vevent)
|
||||
{
|
||||
if (vevent == NULL) {
|
||||
return;
|
||||
}
|
||||
vreader_free(vevent->reader);
|
||||
vcard_free(vevent->card);
|
||||
g_free(vevent);
|
||||
}
|
||||
|
||||
/*
|
||||
* VEvent queue management
|
||||
*/
|
||||
|
||||
static VEvent *vevent_queue_head;
|
||||
static VEvent *vevent_queue_tail;
|
||||
static CompatGMutex vevent_queue_lock;
|
||||
static CompatGCond vevent_queue_condition;
|
||||
|
||||
void vevent_queue_init(void)
|
||||
{
|
||||
vevent_queue_head = vevent_queue_tail = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
vevent_queue_vevent(VEvent *vevent)
|
||||
{
|
||||
vevent->next = NULL;
|
||||
g_mutex_lock(&vevent_queue_lock);
|
||||
if (vevent_queue_head) {
|
||||
assert(vevent_queue_tail);
|
||||
vevent_queue_tail->next = vevent;
|
||||
} else {
|
||||
vevent_queue_head = vevent;
|
||||
}
|
||||
vevent_queue_tail = vevent;
|
||||
g_cond_signal(&vevent_queue_condition);
|
||||
g_mutex_unlock(&vevent_queue_lock);
|
||||
}
|
||||
|
||||
/* must have lock */
|
||||
static VEvent *
|
||||
vevent_dequeue_vevent(void)
|
||||
{
|
||||
VEvent *vevent = NULL;
|
||||
if (vevent_queue_head) {
|
||||
vevent = vevent_queue_head;
|
||||
vevent_queue_head = vevent->next;
|
||||
vevent->next = NULL;
|
||||
}
|
||||
return vevent;
|
||||
}
|
||||
|
||||
VEvent *vevent_wait_next_vevent(void)
|
||||
{
|
||||
VEvent *vevent;
|
||||
|
||||
g_mutex_lock(&vevent_queue_lock);
|
||||
while ((vevent = vevent_dequeue_vevent()) == NULL) {
|
||||
g_cond_wait(&vevent_queue_condition, &vevent_queue_lock);
|
||||
}
|
||||
g_mutex_unlock(&vevent_queue_lock);
|
||||
return vevent;
|
||||
}
|
||||
|
||||
VEvent *vevent_get_next_vevent(void)
|
||||
{
|
||||
VEvent *vevent;
|
||||
|
||||
g_mutex_lock(&vevent_queue_lock);
|
||||
vevent = vevent_dequeue_vevent();
|
||||
g_mutex_unlock(&vevent_queue_lock);
|
||||
return vevent;
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef EVENTT_H
|
||||
#define EVENTT_H 1
|
||||
|
||||
#include "vreadert.h"
|
||||
#include "vcardt.h"
|
||||
|
||||
typedef struct VEventStruct VEvent;
|
||||
|
||||
typedef enum {
|
||||
VEVENT_READER_INSERT,
|
||||
VEVENT_READER_REMOVE,
|
||||
VEVENT_CARD_INSERT,
|
||||
VEVENT_CARD_REMOVE,
|
||||
VEVENT_LAST,
|
||||
} VEventType;
|
||||
|
||||
struct VEventStruct {
|
||||
VEvent *next;
|
||||
VEventType type;
|
||||
VReader *reader;
|
||||
VCard *card;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* GLIB Compatibility Functions
|
||||
*
|
||||
* Copyright IBM, Corp. 2013
|
||||
*
|
||||
* Authors:
|
||||
* Anthony Liguori <aliguori@us.ibm.com>
|
||||
* Michael Tokarev <mjt@tls.msk.ru>
|
||||
* Paolo Bonzini <pbonzini@redhat.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLIB_COMPAT_H
|
||||
#define GLIB_COMPAT_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#if !GLIB_CHECK_VERSION(2, 31, 0)
|
||||
/* before glib-2.31, GMutex and GCond was dynamic-only (there was a separate
|
||||
* GStaticMutex, but it didn't work with condition variables).
|
||||
*
|
||||
* Our implementation uses GOnce to fake a static implementation that does
|
||||
* not require separate initialization.
|
||||
* We need to rename the types to avoid passing our CompatGMutex/CompatGCond
|
||||
* by mistake to a function that expects GMutex/GCond. However, for ease
|
||||
* of use we keep the GLib function names. GLib uses macros for the
|
||||
* implementation, we use inline functions instead and undefine the macros.
|
||||
*/
|
||||
|
||||
typedef struct CompatGMutex {
|
||||
GOnce once;
|
||||
} CompatGMutex;
|
||||
|
||||
typedef struct CompatGCond {
|
||||
GOnce once;
|
||||
} CompatGCond;
|
||||
|
||||
static inline gpointer do_g_mutex_new(gpointer unused)
|
||||
{
|
||||
return (gpointer) g_mutex_new();
|
||||
}
|
||||
|
||||
static inline void g_mutex_init(CompatGMutex *mutex)
|
||||
{
|
||||
mutex->once = (GOnce) G_ONCE_INIT;
|
||||
}
|
||||
|
||||
static inline void g_mutex_clear(CompatGMutex *mutex)
|
||||
{
|
||||
g_assert(mutex->once.status != G_ONCE_STATUS_PROGRESS);
|
||||
if (mutex->once.retval) {
|
||||
g_mutex_free((GMutex *) mutex->once.retval);
|
||||
}
|
||||
mutex->once = (GOnce) G_ONCE_INIT;
|
||||
}
|
||||
|
||||
static inline void (g_mutex_lock)(CompatGMutex *mutex)
|
||||
{
|
||||
g_once(&mutex->once, do_g_mutex_new, NULL);
|
||||
g_mutex_lock((GMutex *) mutex->once.retval);
|
||||
}
|
||||
#undef g_mutex_lock
|
||||
|
||||
static inline gboolean (g_mutex_trylock)(CompatGMutex *mutex)
|
||||
{
|
||||
g_once(&mutex->once, do_g_mutex_new, NULL);
|
||||
return g_mutex_trylock((GMutex *) mutex->once.retval);
|
||||
}
|
||||
#undef g_mutex_trylock
|
||||
|
||||
|
||||
static inline void (g_mutex_unlock)(CompatGMutex *mutex)
|
||||
{
|
||||
g_mutex_unlock((GMutex *) mutex->once.retval);
|
||||
}
|
||||
#undef g_mutex_unlock
|
||||
|
||||
static inline gpointer do_g_cond_new(gpointer unused)
|
||||
{
|
||||
return (gpointer) g_cond_new();
|
||||
}
|
||||
|
||||
static inline void g_cond_init(CompatGCond *cond)
|
||||
{
|
||||
cond->once = (GOnce) G_ONCE_INIT;
|
||||
}
|
||||
|
||||
static inline void g_cond_clear(CompatGCond *cond)
|
||||
{
|
||||
g_assert(cond->once.status != G_ONCE_STATUS_PROGRESS);
|
||||
if (cond->once.retval) {
|
||||
g_cond_free((GCond *) cond->once.retval);
|
||||
}
|
||||
cond->once = (GOnce) G_ONCE_INIT;
|
||||
}
|
||||
|
||||
static inline void (g_cond_wait)(CompatGCond *cond, CompatGMutex *mutex)
|
||||
{
|
||||
g_assert(mutex->once.status != G_ONCE_STATUS_PROGRESS);
|
||||
g_once(&cond->once, do_g_cond_new, NULL);
|
||||
g_cond_wait((GCond *) cond->once.retval, (GMutex *) mutex->once.retval);
|
||||
}
|
||||
#undef g_cond_wait
|
||||
|
||||
static inline void (g_cond_broadcast)(CompatGCond *cond)
|
||||
{
|
||||
g_once(&cond->once, do_g_cond_new, NULL);
|
||||
g_cond_broadcast((GCond *) cond->once.retval);
|
||||
}
|
||||
#undef g_cond_broadcast
|
||||
|
||||
static inline void (g_cond_signal)(CompatGCond *cond)
|
||||
{
|
||||
g_once(&cond->once, do_g_cond_new, NULL);
|
||||
g_cond_signal((GCond *) cond->once.retval);
|
||||
}
|
||||
#undef g_cond_signal
|
||||
|
||||
|
||||
/* before 2.31 there was no g_thread_new() */
|
||||
static inline GThread *g_thread_new(const char *name,
|
||||
GThreadFunc func, gpointer data)
|
||||
{
|
||||
GThread *thread = g_thread_create(func, data, TRUE, NULL);
|
||||
if (!thread) {
|
||||
g_error("creating thread");
|
||||
}
|
||||
return thread;
|
||||
}
|
||||
#else
|
||||
#define CompatGMutex GMutex
|
||||
#define CompatGCond GCond
|
||||
#endif /* glib 2.31 */
|
||||
|
||||
#endif
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* defines the entry point for the Global Plarform Applet emulation. Only used
|
||||
* by vcard_emul_type.c
|
||||
*
|
||||
* Copyright 2018 Red Hat, Inc.
|
||||
*
|
||||
* Author: Jakub Jelen <jjelen@redhat.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "glib-compat.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "gp.h"
|
||||
#include "vcard.h"
|
||||
#include "vcard_emul.h"
|
||||
#include "card_7816.h"
|
||||
|
||||
static unsigned char gp_container_aid[] = {
|
||||
0xa0, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00 };
|
||||
|
||||
/* Data returned for Get Data Instruction */
|
||||
static unsigned char gp_get_data[] = {
|
||||
0x9F, 0x7F, 0x2A, 0x40, 0x70, 0x50, 0x72, 0x12,
|
||||
0x91, 0x51, 0x81, 0x01, 0x00, 0x70, 0x70, 0x00,
|
||||
0x00, 0x58, 0xBD, 0x36, 0x0E, 0x40, 0x82, 0x70,
|
||||
0x90, 0x12, 0x93, 0x70, 0x90, 0x04, 0x44, 0x72,
|
||||
0x00, 0x00, 0x01, 0x00, 0x40, 0x04, 0x45, 0x84,
|
||||
0x00, 0x00, 0x2C, 0x19, 0xB5
|
||||
};
|
||||
|
||||
static VCardStatus
|
||||
gp_applet_container_process_apdu(VCard *card, VCardAPDU *apdu,
|
||||
VCardResponse **response)
|
||||
{
|
||||
VCardStatus ret = VCARD_FAIL;
|
||||
unsigned int tag;
|
||||
|
||||
switch (apdu->a_ins) {
|
||||
case GP_GET_DATA:
|
||||
/* GET DATA isntruction for tags:
|
||||
* 00 66 (not found):
|
||||
* 9F 7F (len = 2D):
|
||||
* 9F 7F 2A 40 70 50 72 12 91 51 81 01 00 70 70 00
|
||||
* 00 58 BD 36 0E 40 82 70 90 12 93 70 90 04 44 72
|
||||
* 00 00 01 00 40 04 45 84 00 00 2C 19 B5
|
||||
*/
|
||||
tag = (apdu->a_p1 & 0xff) << 8 | (apdu->a_p2 & 0xff);
|
||||
if (tag == 0x9f7f) {
|
||||
*response = vcard_response_new(card, gp_get_data,
|
||||
sizeof(gp_get_data), apdu->a_Le, VCARD7816_STATUS_SUCCESS);
|
||||
ret = VCARD_DONE;
|
||||
break;
|
||||
}
|
||||
*response = vcard_make_response(VCARD7816_STATUS_ERROR_DATA_NOT_FOUND);
|
||||
ret = VCARD_DONE;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Let the ISO 7816 code to handle other APDUs */
|
||||
ret = VCARD_NEXT;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initialize the cac card. This is the only public function in this file. All
|
||||
* the rest are connected through function pointers.
|
||||
*/
|
||||
VCardStatus
|
||||
gp_card_init(VReader *reader, VCard *card)
|
||||
{
|
||||
VCardApplet *applet;
|
||||
|
||||
/* create Card Manager container */
|
||||
applet = vcard_new_applet(gp_applet_container_process_apdu,
|
||||
NULL, gp_container_aid,
|
||||
sizeof(gp_container_aid));
|
||||
if (applet == NULL) {
|
||||
goto failure;
|
||||
}
|
||||
vcard_add_applet(card, applet);
|
||||
|
||||
return VCARD_DONE;
|
||||
|
||||
failure:
|
||||
return VCARD_FAIL;
|
||||
}
|
||||
|
||||
/* vim: set ts=4 sw=4 tw=0 noet expandtab: */
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* defines the entry point for the Global Plarform Applet emulation. Only used
|
||||
* by vcard_emul_type.c
|
||||
*
|
||||
* Copyright 2018 Red Hat, Inc.
|
||||
*
|
||||
* Author: Jakub Jelen <jjelen@redhat.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
#ifndef GP_H
|
||||
#define GP_H 1
|
||||
|
||||
#include "vcard.h"
|
||||
#include "vreader.h"
|
||||
|
||||
#define GP_GET_DATA 0xCA
|
||||
|
||||
/*
|
||||
* Initialize the Global Platform Applet. This is the only public function in
|
||||
* this file. All the rest are connected through function pointers.
|
||||
*/
|
||||
VCardStatus
|
||||
gp_card_init(VReader *reader, VCard *card);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
#ifndef LIBCACARD_H
|
||||
#define LIBCACARD_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define __LIBCACARD_H_INSIDE__
|
||||
|
||||
#include "cac.h"
|
||||
#include "card_7816.h"
|
||||
#include "card_7816t.h"
|
||||
#include "eventt.h"
|
||||
#include "vcard_emul.h"
|
||||
#include "vcard_emul_type.h"
|
||||
#include "vcard.h"
|
||||
#include "vcardt.h"
|
||||
#include "vevent.h"
|
||||
#include "vreader.h"
|
||||
#include "vreadert.h"
|
||||
#include "vscard_common.h"
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
|
@ -0,0 +1,77 @@
|
|||
cac_card_init
|
||||
vcard_add_applet
|
||||
vcard_apdu_delete
|
||||
vcard_apdu_new
|
||||
vcard_applet_get_aid
|
||||
vcard_buffer_response_delete
|
||||
vcard_buffer_response_new
|
||||
vcard_delete_applet
|
||||
vcard_emul_delete_key
|
||||
vcard_emul_force_card_insert
|
||||
vcard_emul_force_card_remove
|
||||
vcard_emul_get_atr
|
||||
vcard_emul_get_login_count
|
||||
vcard_emul_init
|
||||
vcard_emul_login
|
||||
vcard_emul_options
|
||||
vcard_emul_replay_insertion_events
|
||||
vcard_emul_reset
|
||||
vcard_emul_rsa_op
|
||||
vcard_emul_type_from_string
|
||||
vcard_emul_type_select
|
||||
vcard_emul_usage
|
||||
vcard_find_applet
|
||||
vcard_free
|
||||
vcard_get_atr
|
||||
vcard_get_buffer_response
|
||||
vcard_get_current_applet_private
|
||||
vcard_get_private
|
||||
vcard_get_type
|
||||
vcard_init
|
||||
vcard_make_response
|
||||
vcard_new
|
||||
vcard_new_applet
|
||||
vcard_process_apdu
|
||||
vcard_process_applet_apdu
|
||||
vcard_reference
|
||||
vcard_reset
|
||||
vcard_response_delete
|
||||
vcard_response_new
|
||||
vcard_response_new_bytes
|
||||
vcard_response_new_data
|
||||
vcard_response_new_status_bytes
|
||||
vcard_response_set_status_bytes
|
||||
vcard_select_applet
|
||||
vcard_set_applet_private
|
||||
vcard_set_atr_func
|
||||
vcard_set_buffer_response
|
||||
vcard_set_type
|
||||
vevent_delete
|
||||
vevent_get_next_vevent
|
||||
vevent_new
|
||||
vevent_queue_init
|
||||
vevent_queue_vevent
|
||||
vevent_wait_next_vevent
|
||||
vreader_add_reader
|
||||
vreader_card_is_present
|
||||
vreader_free
|
||||
vreader_get_id
|
||||
vreader_get_name
|
||||
vreader_get_private
|
||||
vreader_get_reader_by_id
|
||||
vreader_get_reader_by_name
|
||||
vreader_get_reader_list
|
||||
vreader_init
|
||||
vreader_insert_card
|
||||
vreader_list_delete
|
||||
vreader_list_get_first
|
||||
vreader_list_get_next
|
||||
vreader_list_get_reader
|
||||
vreader_new
|
||||
vreader_power_off
|
||||
vreader_power_on
|
||||
vreader_queue_card_event
|
||||
vreader_reference
|
||||
vreader_remove_reader
|
||||
vreader_set_id
|
||||
vreader_xfr_bytes
|
|
@ -0,0 +1,348 @@
|
|||
/*
|
||||
* simpletlv.c: Simple TLV encoding and decoding functions
|
||||
*
|
||||
* Copyright (C) 2016 - 2018 Red Hat, Inc.
|
||||
*
|
||||
* Authors: Robert Relyea <rrelyea@redhat.com>
|
||||
* Jakub Jelen <jjelen@redhat.com>
|
||||
*
|
||||
* 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.1 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
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <glib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "simpletlv.h"
|
||||
#include "common.h"
|
||||
|
||||
int
|
||||
simpletlv_get_length(struct simpletlv_member *tlv, size_t tlv_len,
|
||||
enum simpletlv_buffer_type buffer_type)
|
||||
{
|
||||
size_t i, len = 0;
|
||||
int child_length;
|
||||
|
||||
for (i = 0; i < tlv_len; i++) {
|
||||
/* This TLV is skipped */
|
||||
if (tlv[i].type == SIMPLETLV_TYPE_NONE)
|
||||
continue;
|
||||
|
||||
/* We can not unambiguously split the buffers
|
||||
* for recursive structures
|
||||
*/
|
||||
if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND
|
||||
&& buffer_type != SIMPLETLV_BOTH)
|
||||
return -1;
|
||||
|
||||
child_length = tlv[i].length;
|
||||
if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND) {
|
||||
child_length = simpletlv_get_length(tlv[i].value.child,
|
||||
tlv[i].length, SIMPLETLV_BOTH);
|
||||
}
|
||||
if (buffer_type & SIMPLETLV_TL) {
|
||||
len += 1/*TAG*/;
|
||||
if (child_length < 255)
|
||||
len += 1;
|
||||
else
|
||||
len += 3;
|
||||
}
|
||||
if (buffer_type & SIMPLETLV_VALUE) {
|
||||
len += child_length;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static int
|
||||
simpletlv_encode_internal(struct simpletlv_member *tlv, size_t tlv_len,
|
||||
unsigned char **out, size_t outlen,
|
||||
unsigned char **newptr, int buffer_type)
|
||||
{
|
||||
unsigned char *tmp = NULL, *a = NULL, *p, *newp;
|
||||
size_t tmp_len = 0, p_len, i;
|
||||
int expect_len = 0, rv;
|
||||
|
||||
expect_len = simpletlv_get_length(tlv, tlv_len, buffer_type);
|
||||
if (expect_len <= 0)
|
||||
return expect_len;
|
||||
|
||||
if (outlen == 0) {
|
||||
/* allocate a new buffer */
|
||||
a = g_malloc(expect_len);
|
||||
tmp = a;
|
||||
tmp_len = expect_len;
|
||||
} else if ((int)outlen >= expect_len) {
|
||||
tmp = *out;
|
||||
tmp_len = outlen;
|
||||
} else {
|
||||
/* we can not fit the data */
|
||||
return -1;
|
||||
}
|
||||
p = tmp;
|
||||
p_len = tmp_len;
|
||||
for (i = 0; i < tlv_len; i++) {
|
||||
size_t child_length = tlv[i].length;
|
||||
|
||||
/* This TLV is skipped */
|
||||
if (tlv[i].type == SIMPLETLV_TYPE_NONE)
|
||||
continue;
|
||||
|
||||
if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND) {
|
||||
child_length = simpletlv_get_length(tlv[i].value.child,
|
||||
tlv[i].length, SIMPLETLV_BOTH);
|
||||
}
|
||||
if (buffer_type & SIMPLETLV_TL) {
|
||||
rv = simpletlv_put_tag(tlv[i].tag, child_length,
|
||||
p, p_len, &newp);
|
||||
if (rv < 0)
|
||||
goto failure;
|
||||
p = newp;
|
||||
}
|
||||
if (buffer_type & SIMPLETLV_VALUE) {
|
||||
if (tlv[i].type == SIMPLETLV_TYPE_LEAF) {
|
||||
memcpy(p, tlv[i].value.value, tlv[i].length);
|
||||
p += tlv[i].length;
|
||||
} else {
|
||||
/* recurse */
|
||||
rv = simpletlv_encode_internal(tlv[i].value.child,
|
||||
tlv[i].length, &p, p_len, &newp, buffer_type);
|
||||
if (rv < 0)
|
||||
goto failure;
|
||||
p = newp;
|
||||
}
|
||||
}
|
||||
p_len = tmp_len - (p - tmp);
|
||||
}
|
||||
if (newptr)
|
||||
*newptr = p;
|
||||
if (out)
|
||||
*out = tmp;
|
||||
return tmp_len - p_len;
|
||||
|
||||
failure:
|
||||
g_free(a);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
simpletlv_encode(struct simpletlv_member *tlv, size_t tlv_len,
|
||||
unsigned char **out, size_t outlen, unsigned char **newptr)
|
||||
{
|
||||
return simpletlv_encode_internal(tlv, tlv_len, out, outlen, newptr,
|
||||
SIMPLETLV_BOTH);
|
||||
}
|
||||
|
||||
int
|
||||
simpletlv_encode_tl(struct simpletlv_member *tlv, size_t tlv_len,
|
||||
unsigned char **out, size_t outlen, unsigned char **newptr)
|
||||
{
|
||||
return simpletlv_encode_internal(tlv, tlv_len, out, outlen, newptr,
|
||||
SIMPLETLV_TL);
|
||||
}
|
||||
|
||||
int
|
||||
simpletlv_encode_val(struct simpletlv_member *tlv, size_t tlv_len,
|
||||
unsigned char **out, size_t outlen, unsigned char **newptr)
|
||||
{
|
||||
return simpletlv_encode_internal(tlv, tlv_len, out, outlen, newptr,
|
||||
SIMPLETLV_VALUE);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Put a tag/length record to a file in Simple TLV based on the datalen
|
||||
* content length.
|
||||
*/
|
||||
int
|
||||
simpletlv_put_tag(unsigned char tag, size_t datalen, unsigned char *out,
|
||||
size_t outlen, unsigned char **ptr)
|
||||
{
|
||||
unsigned char *p = out;
|
||||
|
||||
if (outlen < 2 || (outlen < 4 && datalen >= 0xff))
|
||||
return -1;
|
||||
|
||||
/* tag is just number between 0x01 and 0xFE */
|
||||
if (tag == 0x00 || tag == 0xff)
|
||||
return -1;
|
||||
|
||||
*p++ = tag; /* tag is single byte */
|
||||
if (datalen < 0xff) {
|
||||
/* short value up to 255 */
|
||||
*p++ = (unsigned char)datalen; /* is in the second byte */
|
||||
} else if (datalen < 0xffff) {
|
||||
/* longer values up to 65535 */
|
||||
*p++ = (unsigned char)0xff; /* first byte is 0xff */
|
||||
*p++ = (unsigned char)datalen & 0xff;
|
||||
*p++ = (unsigned char)(datalen >> 8) & 0xff; /* LE */
|
||||
} else {
|
||||
/* we can't store more than two bytes in Simple TLV */
|
||||
return -1;
|
||||
}
|
||||
if (ptr != NULL)
|
||||
*ptr = p;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read the TL file and return appropriate tag and the length of associated
|
||||
* content.
|
||||
*/
|
||||
int
|
||||
simpletlv_read_tag(unsigned char **buf, size_t buflen, unsigned char *tag_out,
|
||||
size_t *taglen)
|
||||
{
|
||||
size_t len;
|
||||
unsigned char *p = *buf;
|
||||
|
||||
if (buflen < 2) {
|
||||
*buf = p+buflen;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*tag_out = *p++;
|
||||
len = *p++;
|
||||
if (len == 0xff) {
|
||||
/* don't crash on bad data */
|
||||
if (buflen < 4) {
|
||||
*taglen = 0;
|
||||
return -1;
|
||||
}
|
||||
/* skip two bytes (the size) */
|
||||
len = lebytes2ushort(p);
|
||||
p+=2;
|
||||
}
|
||||
*taglen = len;
|
||||
*buf = p;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Merges two structures into one, creating a new shallow copy of both
|
||||
* of the structures.
|
||||
* Resulting length is the sum of a_len and b_len arguments.
|
||||
*/
|
||||
struct simpletlv_member *
|
||||
simpletlv_merge(const struct simpletlv_member *a, size_t a_len,
|
||||
const struct simpletlv_member *b, size_t b_len)
|
||||
{
|
||||
int offset;
|
||||
struct simpletlv_member *r;
|
||||
size_t r_len = a_len + b_len;
|
||||
|
||||
r = g_new(struct simpletlv_member, r_len);
|
||||
|
||||
/* the ugly way */
|
||||
offset = a_len * sizeof(struct simpletlv_member);
|
||||
memcpy(r, a, offset);
|
||||
memcpy(&r[a_len], b, b_len * sizeof(struct simpletlv_member));
|
||||
return r;
|
||||
}
|
||||
|
||||
void
|
||||
simpletlv_free(struct simpletlv_member *tlv, size_t tlvlen)
|
||||
{
|
||||
size_t i;
|
||||
if (tlv == NULL)
|
||||
return;
|
||||
|
||||
for (i = 0; i < tlvlen; i++) {
|
||||
if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND) {
|
||||
simpletlv_free(tlv[i].value.child, tlv[i].length);
|
||||
} else {
|
||||
g_free(tlv[i].value.value);
|
||||
}
|
||||
}
|
||||
g_free(tlv);
|
||||
}
|
||||
|
||||
struct simpletlv_member *
|
||||
simpletlv_clone(struct simpletlv_member *tlv, size_t tlvlen)
|
||||
{
|
||||
size_t i = 0, j;
|
||||
struct simpletlv_member *new = NULL;
|
||||
|
||||
new = g_new(struct simpletlv_member, tlvlen);
|
||||
|
||||
for (i = 0; i < tlvlen; i++) {
|
||||
new[i].type = tlv[i].type;
|
||||
new[i].tag = tlv[i].tag;
|
||||
new[i].length = tlv[i].length;
|
||||
if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND) {
|
||||
new[i].value.child = simpletlv_clone(
|
||||
tlv[i].value.child, tlv[i].length);
|
||||
if (new[i].value.child == NULL)
|
||||
goto failure;
|
||||
} else {
|
||||
new[i].value.value = g_new(unsigned char, tlv[i].length);
|
||||
memcpy(new[i].value.value, tlv[i].value.value,
|
||||
tlv[i].length);
|
||||
}
|
||||
}
|
||||
return new;
|
||||
|
||||
failure:
|
||||
for (j = 0; j < i; i++) {
|
||||
if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND) {
|
||||
simpletlv_free(new[i].value.child, new[i].length);
|
||||
} else {
|
||||
g_free(new[i].value.value);
|
||||
}
|
||||
}
|
||||
g_free(new);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct simpletlv_member *
|
||||
simpletlv_parse(unsigned char *data, size_t data_len, size_t *outtlv_len)
|
||||
{
|
||||
unsigned char *p, *p_end;
|
||||
unsigned char tag;
|
||||
size_t vlen;
|
||||
GArray *tlv = g_array_new(FALSE, FALSE, sizeof(struct simpletlv_member));
|
||||
|
||||
p = data;
|
||||
p_end = p + data_len;
|
||||
while (p < p_end) {
|
||||
struct simpletlv_member tlvp;
|
||||
|
||||
/* we can return what was parsed successfully */
|
||||
if (simpletlv_read_tag(&p, p_end - p, &tag, &vlen) < 0) {
|
||||
break;
|
||||
}
|
||||
if (vlen > (size_t) (p_end - p)) {
|
||||
break;
|
||||
}
|
||||
|
||||
tlvp.tag = tag;
|
||||
tlvp.length = vlen;
|
||||
tlvp.value.value = g_memdup(p, vlen);
|
||||
tlvp.type = SIMPLETLV_TYPE_LEAF;
|
||||
g_array_append_val(tlv, tlvp);
|
||||
|
||||
p += vlen;
|
||||
}
|
||||
|
||||
*outtlv_len = tlv->len;
|
||||
return (struct simpletlv_member *)g_array_free(tlv, FALSE);
|
||||
}
|
||||
|
||||
/* vim: set ts=4 sw=4 tw=0 noet expandtab: */
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* simpletlv.h: Simple TLV header file
|
||||
*
|
||||
* Copyright (C) 2016 Red Hat, Inc.
|
||||
*
|
||||
* Authors: Robert Relyea <rrelyea@redhat.com>
|
||||
* Jakub Jelen <jjelen@redhat.com>
|
||||
*
|
||||
* 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.1 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
|
||||
*/
|
||||
|
||||
#ifndef _SIMPLETLV_H
|
||||
#define _SIMPLETLV_H
|
||||
|
||||
enum simpletlv_type {
|
||||
SIMPLETLV_TYPE_NONE = 0,
|
||||
SIMPLETLV_TYPE_LEAF = 1,
|
||||
SIMPLETLV_TYPE_COMPOUND = 2
|
||||
};
|
||||
|
||||
enum simpletlv_buffer_type {
|
||||
SIMPLETLV_TL = 0x01,
|
||||
SIMPLETLV_VALUE = 0x02,
|
||||
SIMPLETLV_BOTH = 0x03
|
||||
};
|
||||
|
||||
struct simpletlv_member {
|
||||
unsigned char tag;
|
||||
unsigned int length;
|
||||
union {
|
||||
unsigned char *value;
|
||||
struct simpletlv_member *child;
|
||||
} value;
|
||||
enum simpletlv_type type;
|
||||
};
|
||||
|
||||
/*
|
||||
* Calculate expected length of TLV buffer
|
||||
* @param ltv array of TLV structures to encode
|
||||
* @param tlvlen number of members in the array to encode
|
||||
* @param buffer_type Encode only tags + lengths, values or both
|
||||
* @return The length of the data in the TLV record,
|
||||
* -1 on errors
|
||||
*/
|
||||
int
|
||||
simpletlv_get_length(struct simpletlv_member *, size_t,
|
||||
enum simpletlv_buffer_type);
|
||||
|
||||
/*
|
||||
* Deallocate all parts of dynamically allocated SimpleTLV structure
|
||||
*/
|
||||
void
|
||||
simpletlv_free(struct simpletlv_member *tlv, size_t tlvlen);
|
||||
|
||||
/*
|
||||
* Merges two structures into one, creating a new shallow copy of both
|
||||
* of the structures.
|
||||
* Resulting length is the sum of a_len and b_len arguments.
|
||||
*/
|
||||
struct simpletlv_member *
|
||||
simpletlv_merge(const struct simpletlv_member *a, size_t a_len,
|
||||
const struct simpletlv_member *b, size_t b_len);
|
||||
|
||||
/*
|
||||
* Encode structure into SimpleTLV format, TL together with V
|
||||
* @param tlv array of TLV structures to encode
|
||||
* @param tlvlen number of members in the array to encode
|
||||
* @param out Byte array to write into
|
||||
* @param outlen The length of output array
|
||||
* @param ptr The end of TLV record
|
||||
* @return The length of the encoded data, -1 on errors
|
||||
*/
|
||||
int
|
||||
simpletlv_encode(struct simpletlv_member *tlv, size_t tlv_len,
|
||||
unsigned char **out, size_t outlen, unsigned char **ptr);
|
||||
|
||||
int
|
||||
simpletlv_encode_tl(struct simpletlv_member *tlv, size_t tlv_len,
|
||||
unsigned char **out, size_t outlen, unsigned char **newptr);
|
||||
|
||||
int
|
||||
simpletlv_encode_val(struct simpletlv_member *tlv, size_t tlv_len,
|
||||
unsigned char **out, size_t outlen, unsigned char **newptr);
|
||||
|
||||
/*
|
||||
* Create a tag/length file in Simple TLV based on the val_len content length
|
||||
* @param tag Tag to store into the TL file
|
||||
* @param datalen Data length to store into the TL file
|
||||
* @param out TL byte array to write into
|
||||
* @param outlen The length of the output array
|
||||
* @param ptr The end of the TL record written
|
||||
* @return SC_SUCCESS for correct input
|
||||
*/
|
||||
int
|
||||
simpletlv_put_tag(unsigned char tag, size_t datalen, unsigned char *out,
|
||||
size_t outlen, unsigned char **ptr);
|
||||
|
||||
/* get the Simple TLV tag and length.
|
||||
* @param buf Pointer to the TL file
|
||||
* @param buflen The length of TL file
|
||||
* @param tag_out The tag from the TL file
|
||||
* @param taglen The length of the V record
|
||||
* @return SC_SUCCESS on valid input
|
||||
*/
|
||||
int
|
||||
simpletlv_read_tag(unsigned char **buf, size_t buflen,
|
||||
unsigned char *tag_out, size_t *taglen);
|
||||
|
||||
|
||||
/* create a deep copy of the SimpleTLV structure
|
||||
*
|
||||
* The calling function is responsible for freeing the structure and
|
||||
* all its children by calling simpletlv_free().
|
||||
*/
|
||||
struct simpletlv_member *
|
||||
simpletlv_clone(struct simpletlv_member *tlv, size_t tlvlen);
|
||||
|
||||
/* parse the SimpleTLV compound buffer into internal simpletlv structures
|
||||
*
|
||||
* The returned structure is NEVER recursive, since thre is no unambiguous
|
||||
* way how to determine the recursive structures without the knowledge of
|
||||
* a scheme in advance.
|
||||
*
|
||||
* The calling function is responsible for freeing the structure and its
|
||||
* children by calling simpletlv_free().
|
||||
*
|
||||
*/
|
||||
struct simpletlv_member *
|
||||
simpletlv_parse(unsigned char *data, size_t data_len, size_t *outtlv_len);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,321 @@
|
|||
/*
|
||||
* implement the Java card standard.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "glib-compat.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "vcard.h"
|
||||
#include "vcard_emul.h"
|
||||
#include "card_7816t.h"
|
||||
|
||||
struct VCardAppletStruct {
|
||||
VCardApplet *next;
|
||||
VCardProcessAPDU process_apdu;
|
||||
VCardResetApplet reset_applet;
|
||||
unsigned char *aid;
|
||||
int aid_len;
|
||||
void *applet_private;
|
||||
VCardAppletPrivateFree applet_private_free;
|
||||
};
|
||||
|
||||
struct VCardStruct {
|
||||
int reference_count;
|
||||
VCardApplet *applet_list;
|
||||
VCardApplet *current_applet[MAX_CHANNEL];
|
||||
VCardBufferResponse *vcard_buffer_response;
|
||||
VCardType type;
|
||||
VCardEmul *vcard_private;
|
||||
VCardEmulFree vcard_private_free;
|
||||
VCardGetAtr vcard_get_atr;
|
||||
};
|
||||
|
||||
VCardBufferResponse *
|
||||
vcard_buffer_response_new(const unsigned char *buffer, int size)
|
||||
{
|
||||
VCardBufferResponse *new_buffer;
|
||||
|
||||
new_buffer = g_new(VCardBufferResponse, 1);
|
||||
new_buffer->buffer = (unsigned char *)g_memdup(buffer, size);
|
||||
new_buffer->buffer_len = size;
|
||||
new_buffer->current = new_buffer->buffer;
|
||||
new_buffer->len = size;
|
||||
return new_buffer;
|
||||
}
|
||||
|
||||
void
|
||||
vcard_buffer_response_delete(VCardBufferResponse *buffer_response)
|
||||
{
|
||||
if (buffer_response == NULL) {
|
||||
return;
|
||||
}
|
||||
g_free(buffer_response->buffer);
|
||||
g_free(buffer_response);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* clean up state after a reset
|
||||
*/
|
||||
void
|
||||
vcard_reset(VCard *card, VCardPower power)
|
||||
{
|
||||
int i;
|
||||
VCardApplet *applet = NULL;
|
||||
|
||||
if (card->type == VCARD_DIRECT) {
|
||||
/* select the last applet */
|
||||
VCardApplet *current_applet = NULL;
|
||||
for (current_applet = card->applet_list; current_applet;
|
||||
current_applet = current_applet->next) {
|
||||
applet = current_applet;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < MAX_CHANNEL; i++) {
|
||||
card->current_applet[i] = applet;
|
||||
}
|
||||
if (card->vcard_buffer_response) {
|
||||
vcard_buffer_response_delete(card->vcard_buffer_response);
|
||||
card->vcard_buffer_response = NULL;
|
||||
}
|
||||
vcard_emul_reset(card, power);
|
||||
if (applet) {
|
||||
applet->reset_applet(card, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* applet utilities */
|
||||
|
||||
/*
|
||||
* applet utilities
|
||||
*/
|
||||
/* constructor */
|
||||
VCardApplet *
|
||||
vcard_new_applet(VCardProcessAPDU applet_process_function,
|
||||
VCardResetApplet applet_reset_function,
|
||||
const unsigned char *aid, int aid_len)
|
||||
{
|
||||
VCardApplet *applet;
|
||||
|
||||
applet = g_new0(VCardApplet, 1);
|
||||
applet->process_apdu = applet_process_function;
|
||||
applet->reset_applet = applet_reset_function;
|
||||
|
||||
applet->aid = g_memdup(aid, aid_len);
|
||||
applet->aid_len = aid_len;
|
||||
return applet;
|
||||
}
|
||||
|
||||
/* destructor */
|
||||
void
|
||||
vcard_delete_applet(VCardApplet *applet)
|
||||
{
|
||||
if (applet == NULL) {
|
||||
return;
|
||||
}
|
||||
if (applet->applet_private_free) {
|
||||
applet->applet_private_free(applet->applet_private);
|
||||
}
|
||||
g_free(applet->aid);
|
||||
g_free(applet);
|
||||
}
|
||||
|
||||
/* accessor */
|
||||
void
|
||||
vcard_set_applet_private(VCardApplet *applet, VCardAppletPrivate *private,
|
||||
VCardAppletPrivateFree private_free)
|
||||
{
|
||||
if (applet->applet_private_free) {
|
||||
applet->applet_private_free(applet->applet_private);
|
||||
}
|
||||
applet->applet_private = private;
|
||||
applet->applet_private_free = private_free;
|
||||
}
|
||||
|
||||
VCard *
|
||||
vcard_new(VCardEmul *private, VCardEmulFree private_free)
|
||||
{
|
||||
VCard *new_card;
|
||||
|
||||
new_card = g_new0(VCard, 1);
|
||||
new_card->type = VCARD_VM;
|
||||
new_card->vcard_private = private;
|
||||
new_card->vcard_private_free = private_free;
|
||||
new_card->reference_count = 1;
|
||||
return new_card;
|
||||
}
|
||||
|
||||
VCard *
|
||||
vcard_reference(VCard *vcard)
|
||||
{
|
||||
if (vcard == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
vcard->reference_count++;
|
||||
return vcard;
|
||||
}
|
||||
|
||||
void
|
||||
vcard_free(VCard *vcard)
|
||||
{
|
||||
VCardApplet *current_applet;
|
||||
VCardApplet *next_applet;
|
||||
|
||||
if (vcard == NULL) {
|
||||
return;
|
||||
}
|
||||
vcard->reference_count--;
|
||||
if (vcard->reference_count != 0) {
|
||||
return;
|
||||
}
|
||||
if (vcard->vcard_private_free) {
|
||||
(*vcard->vcard_private_free)(vcard->vcard_private);
|
||||
}
|
||||
for (current_applet = vcard->applet_list; current_applet;
|
||||
current_applet = next_applet) {
|
||||
next_applet = current_applet->next;
|
||||
vcard_delete_applet(current_applet);
|
||||
}
|
||||
vcard_buffer_response_delete(vcard->vcard_buffer_response);
|
||||
g_free(vcard);
|
||||
}
|
||||
|
||||
void
|
||||
vcard_get_atr(VCard *vcard, unsigned char *atr, int *atr_len)
|
||||
{
|
||||
if (vcard->vcard_get_atr) {
|
||||
(*vcard->vcard_get_atr)(vcard, atr, atr_len);
|
||||
return;
|
||||
}
|
||||
vcard_emul_get_atr(vcard, atr, atr_len);
|
||||
}
|
||||
|
||||
void
|
||||
vcard_set_atr_func(VCard *card, VCardGetAtr get_atr)
|
||||
{
|
||||
card->vcard_get_atr = get_atr;
|
||||
}
|
||||
|
||||
|
||||
VCardStatus
|
||||
vcard_add_applet(VCard *card, VCardApplet *applet)
|
||||
{
|
||||
applet->next = card->applet_list;
|
||||
card->applet_list = applet;
|
||||
/* if our card-type is direct, always call the applet */
|
||||
if (card->type == VCARD_DIRECT) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_CHANNEL; i++) {
|
||||
card->current_applet[i] = applet;
|
||||
}
|
||||
}
|
||||
return VCARD_DONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* manage applets
|
||||
*/
|
||||
VCardApplet *
|
||||
vcard_find_applet(VCard *card, const unsigned char *aid, int aid_len)
|
||||
{
|
||||
VCardApplet *current_applet;
|
||||
|
||||
for (current_applet = card->applet_list; current_applet;
|
||||
current_applet = current_applet->next) {
|
||||
if (current_applet->aid_len != aid_len) {
|
||||
continue;
|
||||
}
|
||||
if (memcmp(current_applet->aid, aid, aid_len) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return current_applet;
|
||||
}
|
||||
|
||||
unsigned char *
|
||||
vcard_applet_get_aid(VCardApplet *applet, int *aid_len)
|
||||
{
|
||||
if (applet == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
*aid_len = applet->aid_len;
|
||||
return applet->aid;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
vcard_select_applet(VCard *card, int channel, VCardApplet *applet)
|
||||
{
|
||||
g_assert(channel >= 0 && channel < MAX_CHANNEL);
|
||||
|
||||
card->current_applet[channel] = applet;
|
||||
/* reset the applet */
|
||||
if (applet && applet->reset_applet) {
|
||||
applet->reset_applet(card, channel);
|
||||
}
|
||||
}
|
||||
|
||||
VCardAppletPrivate *
|
||||
vcard_get_current_applet_private(VCard *card, int channel)
|
||||
{
|
||||
VCardApplet *applet = card->current_applet[channel];
|
||||
|
||||
if (applet == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return applet->applet_private;
|
||||
}
|
||||
|
||||
VCardStatus
|
||||
vcard_process_applet_apdu(VCard *card, VCardAPDU *apdu,
|
||||
VCardResponse **response)
|
||||
{
|
||||
if (card->current_applet[apdu->a_channel]) {
|
||||
return card->current_applet[apdu->a_channel]->process_apdu(
|
||||
card, apdu, response);
|
||||
}
|
||||
return VCARD_NEXT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Accessor functions
|
||||
*/
|
||||
/* accessor functions for the response buffer */
|
||||
VCardBufferResponse *
|
||||
vcard_get_buffer_response(VCard *card)
|
||||
{
|
||||
return card->vcard_buffer_response;
|
||||
}
|
||||
|
||||
void
|
||||
vcard_set_buffer_response(VCard *card, VCardBufferResponse *buffer)
|
||||
{
|
||||
card->vcard_buffer_response = buffer;
|
||||
}
|
||||
|
||||
|
||||
/* accessor functions for the type */
|
||||
VCardType
|
||||
vcard_get_type(VCard *card)
|
||||
{
|
||||
return card->type;
|
||||
}
|
||||
|
||||
void
|
||||
vcard_set_type(VCard *card, VCardType type)
|
||||
{
|
||||
card->type = type;
|
||||
}
|
||||
|
||||
/* accessor for private data */
|
||||
VCardEmul *
|
||||
vcard_get_private(VCard *vcard)
|
||||
{
|
||||
return vcard->vcard_private;
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
#ifndef VCARD_H
|
||||
#define VCARD_H 1
|
||||
|
||||
#include "vcardt.h"
|
||||
|
||||
/*
|
||||
* response buffer constructors and destructors.
|
||||
*
|
||||
* response buffers are used when we need to return more data than will fit in
|
||||
* a normal APDU response (nominally 254 bytes).
|
||||
*/
|
||||
VCardBufferResponse *vcard_buffer_response_new(const unsigned char *buffer, int size);
|
||||
void vcard_buffer_response_delete(VCardBufferResponse *buffer_response);
|
||||
|
||||
|
||||
/*
|
||||
* clean up state on reset
|
||||
*/
|
||||
void vcard_reset(VCard *card, VCardPower power);
|
||||
|
||||
/*
|
||||
* applet utilities
|
||||
*/
|
||||
/*
|
||||
* Constructor for a VCardApplet
|
||||
*/
|
||||
VCardApplet *vcard_new_applet(VCardProcessAPDU applet_process_function,
|
||||
VCardResetApplet applet_reset_function,
|
||||
const unsigned char *aid, int aid_len);
|
||||
|
||||
/*
|
||||
* destructor for a VCardApplet
|
||||
* Can be called with a NULL applet
|
||||
*/
|
||||
void vcard_delete_applet(VCardApplet *applet);
|
||||
|
||||
/* accessor - set the card type specific private data */
|
||||
void vcard_set_applet_private(VCardApplet *applet, VCardAppletPrivate *_private,
|
||||
VCardAppletPrivateFree private_free);
|
||||
|
||||
/* set type of vcard */
|
||||
void vcard_set_type(VCard *card, VCardType type);
|
||||
|
||||
/*
|
||||
* utilities interacting with the current applet
|
||||
*/
|
||||
/* add a new applet to a card */
|
||||
VCardStatus vcard_add_applet(VCard *card, VCardApplet *applet);
|
||||
/* find the applet on the card with the given aid */
|
||||
VCardApplet *vcard_find_applet(VCard *card, const unsigned char *aid, int aid_len);
|
||||
/* set the following applet to be current on the given channel */
|
||||
void vcard_select_applet(VCard *card, int channel, VCardApplet *applet);
|
||||
/* get the card type specific private data on the given channel */
|
||||
VCardAppletPrivate *vcard_get_current_applet_private(VCard *card, int channel);
|
||||
/* fetch the applet's id */
|
||||
unsigned char *vcard_applet_get_aid(VCardApplet *applet, int *aid_len);
|
||||
|
||||
/* process the apdu for the current selected applet/file */
|
||||
VCardStatus vcard_process_applet_apdu(VCard *card, VCardAPDU *apdu,
|
||||
VCardResponse **response);
|
||||
/*
|
||||
* VCard utilities
|
||||
*/
|
||||
/* constructor */
|
||||
VCard *vcard_new(VCardEmul *_private, VCardEmulFree private_free);
|
||||
/* get a reference */
|
||||
VCard *vcard_reference(VCard *);
|
||||
/* destructor (reference counted) */
|
||||
void vcard_free(VCard *);
|
||||
/* get the atr from the card */
|
||||
void vcard_get_atr(VCard *card, unsigned char *atr, int *atr_len);
|
||||
void vcard_set_atr_func(VCard *card, VCardGetAtr vcard_get_atr);
|
||||
|
||||
/* accessor functions for the response buffer */
|
||||
VCardBufferResponse *vcard_get_buffer_response(VCard *card);
|
||||
void vcard_set_buffer_response(VCard *card, VCardBufferResponse *buffer);
|
||||
/* accessor functions for the type */
|
||||
VCardType vcard_get_type(VCard *card);
|
||||
/* get the private data */
|
||||
VCardEmul *vcard_get_private(VCard *card);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* This is the actual card emulator.
|
||||
*
|
||||
* These functions can be implemented in different ways on different platforms
|
||||
* using the underlying system primitives. For Linux it uses NSS, though direct
|
||||
* to PKCS #11, openssl+pkcs11, or even gnu crypto libraries+pkcs #11 could be
|
||||
* used. On Windows CAPI could be used.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef VCARD_EMUL_H
|
||||
#define VCARD_EMUL_H 1
|
||||
|
||||
#include "card_7816t.h"
|
||||
#include "vcard.h"
|
||||
#include "vcard_emul_type.h"
|
||||
|
||||
/*
|
||||
* types
|
||||
*/
|
||||
typedef enum {
|
||||
VCARD_EMUL_OK = 0,
|
||||
VCARD_EMUL_FAIL,
|
||||
/* return values by vcard_emul_init */
|
||||
VCARD_EMUL_INIT_ALREADY_INITED,
|
||||
} VCardEmulError;
|
||||
|
||||
/* options are emul specific. call card_emul_parse_args to change a string
|
||||
* To an options struct */
|
||||
typedef struct VCardEmulOptionsStruct VCardEmulOptions;
|
||||
|
||||
/*
|
||||
* Login functions
|
||||
*/
|
||||
/* return the number of login attempts still possible on the card. if unknown,
|
||||
* return -1 */
|
||||
int vcard_emul_get_login_count(VCard *card);
|
||||
/* login into the card, return the 7816 status word (sw2 || sw1) */
|
||||
vcard_7816_status_t vcard_emul_login(VCard *card, unsigned char *pin,
|
||||
int pin_len);
|
||||
void vcard_emul_logout(VCard *card);
|
||||
|
||||
/*
|
||||
* key functions
|
||||
*/
|
||||
/* delete a key */
|
||||
void vcard_emul_delete_key(VCardKey *key);
|
||||
int vcard_emul_rsa_bits(VCardKey *key);
|
||||
/* RSA sign/decrypt with the key, signature happens 'in place' */
|
||||
vcard_7816_status_t vcard_emul_rsa_op(VCard *card, VCardKey *key,
|
||||
unsigned char *buffer, int buffer_size);
|
||||
|
||||
void vcard_emul_reset(VCard *card, VCardPower power);
|
||||
void vcard_emul_get_atr(VCard *card, unsigned char *atr, int *atr_len);
|
||||
|
||||
/* Re-insert of a card that has been removed by force removal */
|
||||
VCardEmulError vcard_emul_force_card_insert(VReader *vreader);
|
||||
/* Force a card removal even if the card is not physically removed */
|
||||
VCardEmulError vcard_emul_force_card_remove(VReader *vreader);
|
||||
|
||||
VCardEmulOptions *vcard_emul_options(const char *args);
|
||||
VCardEmulError vcard_emul_init(const VCardEmulOptions *options);
|
||||
void vcard_emul_replay_insertion_events(void);
|
||||
void vcard_emul_usage(void);
|
||||
|
||||
unsigned char *vcard_emul_read_object(VCard *card, const char *label,
|
||||
unsigned int *ret_len);
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* This file contains utility functions which abstract the different card
|
||||
* types. The goal is that new card types can easily be added by simply
|
||||
* changing this file and vcard_emul_type.h. It is currently not a requirement
|
||||
* to dynamically add new card types.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
#include "config.h"
|
||||
|
||||
#include <strings.h>
|
||||
#include "vcardt.h"
|
||||
#include "vcard_emul_type.h"
|
||||
#include "cac.h"
|
||||
#include "gp.h"
|
||||
#include "glib-compat.h"
|
||||
|
||||
VCardStatus vcard_init(VReader *vreader, VCard *vcard,
|
||||
VCardEmulType type, const char *params,
|
||||
unsigned char *const *cert, int cert_len[],
|
||||
VCardKey *key[], int cert_count)
|
||||
{
|
||||
int rv;
|
||||
|
||||
switch (type) {
|
||||
case VCARD_EMUL_NONE:
|
||||
break;
|
||||
case VCARD_EMUL_CAC:
|
||||
rv = cac_card_init(vreader, vcard, params,
|
||||
cert, cert_len, key, cert_count);
|
||||
if (rv == VCARD_DONE)
|
||||
rv = gp_card_init(vreader, vcard);
|
||||
return rv;
|
||||
/* add new ones here */
|
||||
case VCARD_EMUL_PASSTHRU:
|
||||
default:
|
||||
g_warn_if_reached();
|
||||
break;
|
||||
}
|
||||
return VCARD_FAIL;
|
||||
}
|
||||
|
||||
VCardEmulType vcard_emul_type_select(VReader *vreader)
|
||||
{
|
||||
/* return the default */
|
||||
return VCARD_EMUL_CAC;
|
||||
}
|
||||
|
||||
VCardEmulType vcard_emul_type_from_string(const char *type_string)
|
||||
{
|
||||
if (strcasecmp(type_string, "CAC") == 0) {
|
||||
return VCARD_EMUL_CAC;
|
||||
}
|
||||
#ifdef ENABLE_PCSC
|
||||
if (strcasecmp(type_string, "PASSTHRU") == 0) {
|
||||
return VCARD_EMUL_PASSTHRU;
|
||||
}
|
||||
#endif
|
||||
return VCARD_EMUL_NONE;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* This header file abstracts the different card types. The goal is new card
|
||||
* types can easily be added by simply changing this file and
|
||||
* vcard_emul_type.c. It is currently not a requirement to dynamically add new
|
||||
* card types.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef VCARD_EMUL_TYPE_H
|
||||
#define VCARD_EMUL_TYPE_H 1
|
||||
|
||||
#include "vcardt.h"
|
||||
#include "vreadert.h"
|
||||
|
||||
/*
|
||||
* types
|
||||
*/
|
||||
typedef enum {
|
||||
VCARD_EMUL_NONE = 0,
|
||||
VCARD_EMUL_CAC,
|
||||
VCARD_EMUL_PASSTHRU
|
||||
} VCardEmulType;
|
||||
|
||||
/* functions used by the rest of the emulator */
|
||||
VCardStatus vcard_init(VReader *vreader, VCard *vcard, VCardEmulType type,
|
||||
const char *params, unsigned char * const *cert,
|
||||
int cert_len[], VCardKey *key[], int cert_count);
|
||||
VCardEmulType vcard_emul_type_select(VReader *vreader);
|
||||
VCardEmulType vcard_emul_type_from_string(const char *type_string);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,40 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include "vcardt.h"
|
||||
|
||||
#include "vcardt_internal.h"
|
||||
|
||||
/* create an ATR with appropriate historical bytes */
|
||||
#define ATR_TS_DIRECT_CONVENTION 0x3b
|
||||
#define ATR_TA_PRESENT 0x10
|
||||
#define ATR_TB_PRESENT 0x20
|
||||
#define ATR_TC_PRESENT 0x40
|
||||
#define ATR_TD_PRESENT 0x80
|
||||
|
||||
unsigned char *vcard_alloc_atr(const char *postfix, int *atr_len)
|
||||
{
|
||||
int postfix_len;
|
||||
const char prefix[] = "VCARD_";
|
||||
const char default_postfix[] = "DEFAULT";
|
||||
const int prefix_len = sizeof(prefix) - 1;
|
||||
int total_len;
|
||||
unsigned char *atr;
|
||||
|
||||
if (postfix == NULL) {
|
||||
postfix = default_postfix;
|
||||
}
|
||||
postfix_len = strlen(postfix);
|
||||
total_len = 3 + prefix_len + postfix_len;
|
||||
atr = g_malloc(total_len);
|
||||
atr[0] = ATR_TS_DIRECT_CONVENTION;
|
||||
atr[1] = ATR_TD_PRESENT + prefix_len + postfix_len;
|
||||
atr[2] = 0x00;
|
||||
memcpy(&atr[3], prefix, prefix_len);
|
||||
memcpy(&atr[3 + prefix_len], postfix, postfix_len);
|
||||
if (atr_len) {
|
||||
*atr_len = total_len;
|
||||
}
|
||||
return atr;
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
#ifndef VCARDT_H
|
||||
#define VCARDT_H 1
|
||||
|
||||
/*
|
||||
* these should come from some common spice header file
|
||||
*/
|
||||
#include <assert.h>
|
||||
#ifndef MIN
|
||||
#define MIN(x, y) ((x) > (y) ? (y) : (x))
|
||||
#define MAX(x, y) ((x) > (y) ? (x) : (y))
|
||||
#endif
|
||||
|
||||
typedef struct VCardStruct VCard;
|
||||
typedef struct VCardAPDUStruct VCardAPDU;
|
||||
typedef struct VCardResponseStruct VCardResponse;
|
||||
typedef struct VCardBufferResponseStruct VCardBufferResponse;
|
||||
typedef struct VCardAppletStruct VCardApplet;
|
||||
typedef struct VCardAppletPrivateStruct VCardAppletPrivate;
|
||||
typedef struct VCardKeyStruct VCardKey; /* opaque */
|
||||
typedef struct VCardEmulStruct VCardEmul;
|
||||
|
||||
#define MAX_CHANNEL 4
|
||||
|
||||
typedef enum {
|
||||
VCARD_DONE,
|
||||
VCARD_NEXT,
|
||||
VCARD_FAIL
|
||||
} VCardStatus;
|
||||
|
||||
typedef enum {
|
||||
VCARD_FILE_SYSTEM,
|
||||
VCARD_VM,
|
||||
VCARD_DIRECT
|
||||
} VCardType;
|
||||
|
||||
typedef enum {
|
||||
VCARD_POWER_ON,
|
||||
VCARD_POWER_OFF
|
||||
} VCardPower;
|
||||
|
||||
typedef VCardStatus (*VCardProcessAPDU)(VCard *card, VCardAPDU *apdu,
|
||||
VCardResponse **response);
|
||||
typedef VCardStatus (*VCardResetApplet)(VCard *card, int channel);
|
||||
typedef void (*VCardAppletPrivateFree) (VCardAppletPrivate *);
|
||||
typedef void (*VCardEmulFree) (VCardEmul *);
|
||||
typedef void (*VCardGetAtr) (VCard *, unsigned char *atr, int *atr_len);
|
||||
|
||||
struct VCardBufferResponseStruct {
|
||||
unsigned char *buffer;
|
||||
int buffer_len;
|
||||
unsigned char *current;
|
||||
int len;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,6 @@
|
|||
#ifndef VCARDT_INTERNAL_H
|
||||
#define VCARDT_INTERNAL_H
|
||||
|
||||
unsigned char *vcard_alloc_atr(const char *postfix, int *atr_len);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
#ifndef EVENT_H
|
||||
#define EVENT_H 1
|
||||
|
||||
#include "eventt.h"
|
||||
#include "vreadert.h"
|
||||
#include "vcardt.h"
|
||||
|
||||
VEvent *vevent_new(VEventType type, VReader *reader, VCard *card);
|
||||
void vevent_delete(VEvent *);
|
||||
|
||||
/*
|
||||
* VEvent queueing services
|
||||
*/
|
||||
void vevent_queue_vevent(VEvent *);
|
||||
void vevent_queue_init(void);
|
||||
|
||||
/*
|
||||
* VEvent dequeing services
|
||||
*/
|
||||
VEvent *vevent_wait_next_vevent(void);
|
||||
VEvent *vevent_get_next_vevent(void);
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,583 @@
|
|||
/*
|
||||
* emulate the reader
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "glib-compat.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "vcard.h"
|
||||
#include "vcard_emul.h"
|
||||
#include "card_7816.h"
|
||||
#include "vreader.h"
|
||||
#include "vevent.h"
|
||||
#include "cac.h" /* just for debugging defines */
|
||||
|
||||
struct VReaderStruct {
|
||||
int reference_count;
|
||||
VCard *card;
|
||||
char *name;
|
||||
vreader_id_t id;
|
||||
CompatGMutex lock;
|
||||
VReaderEmul *reader_private;
|
||||
VReaderEmulFree reader_private_free;
|
||||
};
|
||||
|
||||
/*
|
||||
* Debug helpers
|
||||
*/
|
||||
|
||||
static const char *
|
||||
apdu_ins_to_string(int ins)
|
||||
{
|
||||
switch (ins) {
|
||||
case VCARD7816_INS_MANAGE_CHANNEL:
|
||||
return "manage channel";
|
||||
case VCARD7816_INS_EXTERNAL_AUTHENTICATE:
|
||||
return "external authenticate";
|
||||
case VCARD7816_INS_GET_CHALLENGE:
|
||||
return "get challenge";
|
||||
case VCARD7816_INS_INTERNAL_AUTHENTICATE:
|
||||
return "internal authenticate";
|
||||
case VCARD7816_INS_ERASE_BINARY:
|
||||
return "erase binary";
|
||||
case VCARD7816_INS_READ_BINARY:
|
||||
return "read binary";
|
||||
case VCARD7816_INS_WRITE_BINARY:
|
||||
return "write binary";
|
||||
case VCARD7816_INS_UPDATE_BINARY:
|
||||
return "update binary";
|
||||
case VCARD7816_INS_READ_RECORD:
|
||||
return "read record";
|
||||
case VCARD7816_INS_WRITE_RECORD:
|
||||
return "write record";
|
||||
case VCARD7816_INS_UPDATE_RECORD:
|
||||
return "update record";
|
||||
case VCARD7816_INS_APPEND_RECORD:
|
||||
return "append record";
|
||||
case VCARD7816_INS_ENVELOPE:
|
||||
return "envelope";
|
||||
case VCARD7816_INS_PUT_DATA:
|
||||
return "put data";
|
||||
case VCARD7816_INS_GET_DATA:
|
||||
return "get data";
|
||||
case VCARD7816_INS_SELECT_FILE:
|
||||
return "select file";
|
||||
case VCARD7816_INS_VERIFY:
|
||||
return "verify";
|
||||
case VCARD7816_INS_GET_RESPONSE:
|
||||
return "get response";
|
||||
case CAC_GET_PROPERTIES:
|
||||
return "get properties";
|
||||
case CAC_GET_ACR:
|
||||
return "get acr";
|
||||
case CAC_READ_BUFFER:
|
||||
return "read buffer";
|
||||
case CAC_UPDATE_BUFFER:
|
||||
return "update buffer";
|
||||
case CAC_SIGN_DECRYPT:
|
||||
return "sign decrypt";
|
||||
case CAC_GET_CERTIFICATE:
|
||||
return "get certificate";
|
||||
default:
|
||||
g_return_val_if_reached("unknown");
|
||||
}
|
||||
}
|
||||
|
||||
/* manage locking */
|
||||
static inline void
|
||||
vreader_lock(VReader *reader)
|
||||
{
|
||||
g_mutex_lock(&reader->lock);
|
||||
}
|
||||
|
||||
static inline void
|
||||
vreader_unlock(VReader *reader)
|
||||
{
|
||||
g_mutex_unlock(&reader->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* vreader constructor
|
||||
*/
|
||||
VReader *
|
||||
vreader_new(const char *name, VReaderEmul *private,
|
||||
VReaderEmulFree private_free)
|
||||
{
|
||||
VReader *reader;
|
||||
|
||||
reader = g_new(VReader, 1);
|
||||
g_mutex_init(&reader->lock);
|
||||
reader->reference_count = 1;
|
||||
reader->name = g_strdup(name);
|
||||
reader->card = NULL;
|
||||
reader->id = (vreader_id_t)-1;
|
||||
reader->reader_private = private;
|
||||
reader->reader_private_free = private_free;
|
||||
return reader;
|
||||
}
|
||||
|
||||
/* get a reference */
|
||||
VReader*
|
||||
vreader_reference(VReader *reader)
|
||||
{
|
||||
if (reader == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
vreader_lock(reader);
|
||||
reader->reference_count++;
|
||||
vreader_unlock(reader);
|
||||
return reader;
|
||||
}
|
||||
|
||||
/* free a reference */
|
||||
void
|
||||
vreader_free(VReader *reader)
|
||||
{
|
||||
if (reader == NULL) {
|
||||
return;
|
||||
}
|
||||
vreader_lock(reader);
|
||||
if (reader->reference_count-- > 1) {
|
||||
vreader_unlock(reader);
|
||||
return;
|
||||
}
|
||||
vreader_unlock(reader);
|
||||
g_mutex_clear(&reader->lock);
|
||||
if (reader->card) {
|
||||
vcard_free(reader->card);
|
||||
}
|
||||
g_free(reader->name);
|
||||
if (reader->reader_private_free) {
|
||||
reader->reader_private_free(reader->reader_private);
|
||||
}
|
||||
g_free(reader);
|
||||
}
|
||||
|
||||
static VCard *
|
||||
vreader_get_card(VReader *reader)
|
||||
{
|
||||
VCard *card;
|
||||
|
||||
vreader_lock(reader);
|
||||
card = vcard_reference(reader->card);
|
||||
vreader_unlock(reader);
|
||||
return card;
|
||||
}
|
||||
|
||||
VReaderStatus
|
||||
vreader_card_is_present(VReader *reader)
|
||||
{
|
||||
VCard *card = vreader_get_card(reader);
|
||||
|
||||
if (card == NULL) {
|
||||
return VREADER_NO_CARD;
|
||||
}
|
||||
vcard_free(card);
|
||||
return VREADER_OK;
|
||||
}
|
||||
|
||||
vreader_id_t
|
||||
vreader_get_id(VReader *reader)
|
||||
{
|
||||
if (reader == NULL) {
|
||||
return (vreader_id_t)-1;
|
||||
}
|
||||
return reader->id;
|
||||
}
|
||||
|
||||
VReaderStatus
|
||||
vreader_set_id(VReader *reader, vreader_id_t id)
|
||||
{
|
||||
if (reader == NULL) {
|
||||
return VREADER_NO_CARD;
|
||||
}
|
||||
reader->id = id;
|
||||
return VREADER_OK;
|
||||
}
|
||||
|
||||
const char *
|
||||
vreader_get_name(VReader *reader)
|
||||
{
|
||||
if (reader == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return reader->name;
|
||||
}
|
||||
|
||||
VReaderEmul *
|
||||
vreader_get_private(VReader *reader)
|
||||
{
|
||||
return reader->reader_private;
|
||||
}
|
||||
|
||||
static VReaderStatus
|
||||
vreader_reset(VReader *reader, VCardPower power, unsigned char *atr, int *len)
|
||||
{
|
||||
VCard *card = vreader_get_card(reader);
|
||||
|
||||
if (card == NULL) {
|
||||
return VREADER_NO_CARD;
|
||||
}
|
||||
/*
|
||||
* clean up our state
|
||||
*/
|
||||
vcard_reset(card, power);
|
||||
if (atr) {
|
||||
vcard_get_atr(card, atr, len);
|
||||
}
|
||||
vcard_free(card); /* free our reference */
|
||||
return VREADER_OK;
|
||||
}
|
||||
|
||||
VReaderStatus
|
||||
vreader_power_on(VReader *reader, unsigned char *atr, int *len)
|
||||
{
|
||||
return vreader_reset(reader, VCARD_POWER_ON, atr, len);
|
||||
}
|
||||
|
||||
VReaderStatus
|
||||
vreader_power_off(VReader *reader)
|
||||
{
|
||||
return vreader_reset(reader, VCARD_POWER_OFF, NULL, 0);
|
||||
}
|
||||
|
||||
|
||||
VReaderStatus
|
||||
vreader_xfr_bytes(VReader *reader,
|
||||
unsigned char *send_buf, int send_buf_len,
|
||||
unsigned char *receive_buf, int *receive_buf_len)
|
||||
{
|
||||
VCardAPDU *apdu;
|
||||
VCardResponse *response = NULL;
|
||||
VCardStatus card_status;
|
||||
VReaderStatus ret;
|
||||
unsigned short status;
|
||||
VCard *card = vreader_get_card(reader);
|
||||
int size;
|
||||
|
||||
if (card == NULL) {
|
||||
return VREADER_NO_CARD;
|
||||
}
|
||||
|
||||
apdu = vcard_apdu_new(send_buf, send_buf_len, &status);
|
||||
if (apdu == NULL) {
|
||||
response = vcard_make_response(status);
|
||||
card_status = VCARD_DONE;
|
||||
} else {
|
||||
g_debug("%s: CLS=0x%x,INS=0x%x,P1=0x%x,P2=0x%x,Lc=%d,Le=%d %s",
|
||||
__func__, apdu->a_cla, apdu->a_ins, apdu->a_p1, apdu->a_p2,
|
||||
apdu->a_Lc, apdu->a_Le, apdu_ins_to_string(apdu->a_ins));
|
||||
card_status = vcard_process_apdu(card, apdu, &response);
|
||||
if (response) {
|
||||
g_debug("%s: status=%d sw1=0x%x sw2=0x%x len=%d (total=%d)",
|
||||
__func__, response->b_status, response->b_sw1,
|
||||
response->b_sw2, response->b_len, response->b_total_len);
|
||||
}
|
||||
}
|
||||
if (card_status == VCARD_FAIL) {
|
||||
*receive_buf_len = 0;
|
||||
ret = VREADER_NO_CARD;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
assert(card_status == VCARD_DONE && response);
|
||||
size = MIN(*receive_buf_len, response->b_total_len);
|
||||
memcpy(receive_buf, response->b_data, size);
|
||||
*receive_buf_len = size;
|
||||
ret = VREADER_OK;
|
||||
|
||||
exit:
|
||||
vcard_response_delete(response);
|
||||
vcard_apdu_delete(apdu);
|
||||
vcard_free(card); /* free our reference */
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct VReaderListStruct {
|
||||
VReaderListEntry *head;
|
||||
VReaderListEntry *tail;
|
||||
};
|
||||
|
||||
struct VReaderListEntryStruct {
|
||||
VReaderListEntry *next;
|
||||
VReaderListEntry *prev;
|
||||
VReader *reader;
|
||||
};
|
||||
|
||||
|
||||
static VReaderListEntry *
|
||||
vreader_list_entry_new(VReader *reader)
|
||||
{
|
||||
VReaderListEntry *new_reader_list_entry;
|
||||
|
||||
new_reader_list_entry = g_new0(VReaderListEntry, 1);
|
||||
new_reader_list_entry->reader = vreader_reference(reader);
|
||||
return new_reader_list_entry;
|
||||
}
|
||||
|
||||
static void
|
||||
vreader_list_entry_delete(VReaderListEntry *entry)
|
||||
{
|
||||
if (entry == NULL) {
|
||||
return;
|
||||
}
|
||||
vreader_free(entry->reader);
|
||||
g_free(entry);
|
||||
}
|
||||
|
||||
|
||||
static VReaderList *
|
||||
vreader_list_new(void)
|
||||
{
|
||||
VReaderList *new_reader_list;
|
||||
|
||||
new_reader_list = g_new0(VReaderList, 1);
|
||||
return new_reader_list;
|
||||
}
|
||||
|
||||
void
|
||||
vreader_list_delete(VReaderList *list)
|
||||
{
|
||||
VReaderListEntry *current_entry;
|
||||
VReaderListEntry *next_entry;
|
||||
for (current_entry = vreader_list_get_first(list); current_entry;
|
||||
current_entry = next_entry) {
|
||||
next_entry = vreader_list_get_next(current_entry);
|
||||
vreader_list_entry_delete(current_entry);
|
||||
}
|
||||
g_free(list);
|
||||
}
|
||||
|
||||
|
||||
VReaderListEntry *
|
||||
vreader_list_get_first(VReaderList *list)
|
||||
{
|
||||
return list ? list->head : NULL;
|
||||
}
|
||||
|
||||
VReaderListEntry *
|
||||
vreader_list_get_next(VReaderListEntry *current)
|
||||
{
|
||||
return current ? current->next : NULL;
|
||||
}
|
||||
|
||||
VReader *
|
||||
vreader_list_get_reader(VReaderListEntry *entry)
|
||||
{
|
||||
return entry ? vreader_reference(entry->reader) : NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
vreader_queue(VReaderList *list, VReaderListEntry *entry)
|
||||
{
|
||||
if (entry == NULL) {
|
||||
return;
|
||||
}
|
||||
entry->next = NULL;
|
||||
entry->prev = list->tail;
|
||||
if (list->head) {
|
||||
list->tail->next = entry;
|
||||
} else {
|
||||
list->head = entry;
|
||||
}
|
||||
list->tail = entry;
|
||||
}
|
||||
|
||||
static void
|
||||
vreader_dequeue(VReaderList *list, VReaderListEntry *entry)
|
||||
{
|
||||
if (entry == NULL) {
|
||||
return;
|
||||
}
|
||||
if (entry->next == NULL) {
|
||||
list->tail = entry->prev;
|
||||
} else if (entry->prev == NULL) {
|
||||
list->head = entry->next;
|
||||
} else {
|
||||
entry->prev->next = entry->next;
|
||||
entry->next->prev = entry->prev;
|
||||
}
|
||||
if ((list->tail == NULL) || (list->head == NULL)) {
|
||||
list->head = list->tail = NULL;
|
||||
}
|
||||
entry->next = entry->prev = NULL;
|
||||
}
|
||||
|
||||
static VReaderList *vreader_list;
|
||||
static CompatGMutex vreader_list_mutex;
|
||||
|
||||
static void
|
||||
vreader_list_init(void)
|
||||
{
|
||||
vreader_list = vreader_list_new();
|
||||
}
|
||||
|
||||
static void
|
||||
vreader_list_lock(void)
|
||||
{
|
||||
g_mutex_lock(&vreader_list_mutex);
|
||||
}
|
||||
|
||||
static void
|
||||
vreader_list_unlock(void)
|
||||
{
|
||||
g_mutex_unlock(&vreader_list_mutex);
|
||||
}
|
||||
|
||||
static VReaderList *
|
||||
vreader_copy_list(VReaderList *list)
|
||||
{
|
||||
VReaderList *new_list;
|
||||
VReaderListEntry *current_entry;
|
||||
|
||||
new_list = vreader_list_new();
|
||||
if (new_list == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
for (current_entry = vreader_list_get_first(list); current_entry;
|
||||
current_entry = vreader_list_get_next(current_entry)) {
|
||||
VReader *reader = vreader_list_get_reader(current_entry);
|
||||
VReaderListEntry *new_entry = vreader_list_entry_new(reader);
|
||||
|
||||
vreader_free(reader);
|
||||
vreader_queue(new_list, new_entry);
|
||||
}
|
||||
return new_list;
|
||||
}
|
||||
|
||||
VReaderList *
|
||||
vreader_get_reader_list(void)
|
||||
{
|
||||
VReaderList *new_reader_list;
|
||||
|
||||
vreader_list_lock();
|
||||
new_reader_list = vreader_copy_list(vreader_list);
|
||||
vreader_list_unlock();
|
||||
return new_reader_list;
|
||||
}
|
||||
|
||||
VReader *
|
||||
vreader_get_reader_by_id(vreader_id_t id)
|
||||
{
|
||||
VReader *reader = NULL;
|
||||
VReaderListEntry *current_entry;
|
||||
|
||||
if (id == (vreader_id_t) -1) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
vreader_list_lock();
|
||||
for (current_entry = vreader_list_get_first(vreader_list); current_entry;
|
||||
current_entry = vreader_list_get_next(current_entry)) {
|
||||
VReader *creader = vreader_list_get_reader(current_entry);
|
||||
if (creader->id == id) {
|
||||
reader = creader;
|
||||
break;
|
||||
}
|
||||
vreader_free(creader);
|
||||
}
|
||||
vreader_list_unlock();
|
||||
return reader;
|
||||
}
|
||||
|
||||
VReader *
|
||||
vreader_get_reader_by_name(const char *name)
|
||||
{
|
||||
VReader *reader = NULL;
|
||||
VReaderListEntry *current_entry;
|
||||
|
||||
vreader_list_lock();
|
||||
for (current_entry = vreader_list_get_first(vreader_list); current_entry;
|
||||
current_entry = vreader_list_get_next(current_entry)) {
|
||||
VReader *creader = vreader_list_get_reader(current_entry);
|
||||
if (strcmp(creader->name, name) == 0) {
|
||||
reader = creader;
|
||||
break;
|
||||
}
|
||||
vreader_free(creader);
|
||||
}
|
||||
vreader_list_unlock();
|
||||
return reader;
|
||||
}
|
||||
|
||||
/* called from card_emul to initialize the readers */
|
||||
VReaderStatus
|
||||
vreader_add_reader(VReader *reader)
|
||||
{
|
||||
VReaderListEntry *reader_entry;
|
||||
|
||||
reader_entry = vreader_list_entry_new(reader);
|
||||
if (reader_entry == NULL) {
|
||||
return VREADER_OUT_OF_MEMORY;
|
||||
}
|
||||
vreader_list_lock();
|
||||
vreader_queue(vreader_list, reader_entry);
|
||||
vreader_list_unlock();
|
||||
vevent_queue_vevent(vevent_new(VEVENT_READER_INSERT, reader, NULL));
|
||||
return VREADER_OK;
|
||||
}
|
||||
|
||||
|
||||
VReaderStatus
|
||||
vreader_remove_reader(VReader *reader)
|
||||
{
|
||||
VReaderListEntry *current_entry;
|
||||
|
||||
vreader_list_lock();
|
||||
for (current_entry = vreader_list_get_first(vreader_list); current_entry;
|
||||
current_entry = vreader_list_get_next(current_entry)) {
|
||||
if (current_entry->reader == reader) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
vreader_dequeue(vreader_list, current_entry);
|
||||
vreader_list_unlock();
|
||||
vevent_queue_vevent(vevent_new(VEVENT_READER_REMOVE, reader, NULL));
|
||||
vreader_list_entry_delete(current_entry);
|
||||
return VREADER_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate VEVENT_CARD_INSERT or VEVENT_CARD_REMOVE based on vreader
|
||||
* state. Separated from vreader_insert_card to allow replaying events
|
||||
* for a given state.
|
||||
*/
|
||||
void
|
||||
vreader_queue_card_event(VReader *reader)
|
||||
{
|
||||
vevent_queue_vevent(vevent_new(
|
||||
reader->card ? VEVENT_CARD_INSERT : VEVENT_CARD_REMOVE, reader,
|
||||
reader->card));
|
||||
}
|
||||
|
||||
/*
|
||||
* insert/remove a new card. for removal, card == NULL
|
||||
*/
|
||||
VReaderStatus
|
||||
vreader_insert_card(VReader *reader, VCard *card)
|
||||
{
|
||||
vreader_lock(reader);
|
||||
if (reader->card) {
|
||||
/* decrement reference count */
|
||||
vcard_free(reader->card);
|
||||
reader->card = NULL;
|
||||
}
|
||||
reader->card = vcard_reference(card);
|
||||
vreader_unlock(reader);
|
||||
vreader_queue_card_event(reader);
|
||||
return VREADER_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* initialize all the static reader structures
|
||||
*/
|
||||
void
|
||||
vreader_init(void)
|
||||
{
|
||||
vreader_list_init();
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef VREADER_H
|
||||
#define VREADER_H 1
|
||||
|
||||
#include "eventt.h"
|
||||
#include "vreadert.h"
|
||||
#include "vcardt.h"
|
||||
|
||||
/*
|
||||
* calls for reader front end
|
||||
*/
|
||||
VReaderStatus vreader_power_on(VReader *reader, unsigned char *atr, int *len);
|
||||
VReaderStatus vreader_power_off(VReader *reader);
|
||||
VReaderStatus vreader_xfr_bytes(VReader *reader, unsigned char *send_buf,
|
||||
int send_buf_len, unsigned char *receive_buf,
|
||||
int *receive_buf_len);
|
||||
|
||||
/* constructor */
|
||||
VReader *vreader_new(const char *readerName, VReaderEmul *emul_private,
|
||||
VReaderEmulFree private_free);
|
||||
/* get a new reference to a reader */
|
||||
VReader *vreader_reference(VReader *reader);
|
||||
/* "destructor" (readers are reference counted) */
|
||||
void vreader_free(VReader *reader);
|
||||
|
||||
/* accessors */
|
||||
VReaderEmul *vreader_get_private(VReader *);
|
||||
VReaderStatus vreader_card_is_present(VReader *reader);
|
||||
void vreader_queue_card_event(VReader *reader);
|
||||
const char *vreader_get_name(VReader *reader);
|
||||
vreader_id_t vreader_get_id(VReader *reader);
|
||||
VReaderStatus vreader_set_id(VReader *reader, vreader_id_t id);
|
||||
|
||||
/* list operations */
|
||||
VReaderList *vreader_get_reader_list(void);
|
||||
void vreader_list_delete(VReaderList *list);
|
||||
VReader *vreader_list_get_reader(VReaderListEntry *entry);
|
||||
VReaderListEntry *vreader_list_get_first(VReaderList *list);
|
||||
VReaderListEntry *vreader_list_get_next(VReaderListEntry *list);
|
||||
VReader *vreader_get_reader_by_id(vreader_id_t id);
|
||||
VReader *vreader_get_reader_by_name(const char *name);
|
||||
|
||||
/*
|
||||
* list tools for vcard_emul
|
||||
*/
|
||||
void vreader_init(void);
|
||||
VReaderStatus vreader_add_reader(VReader *reader);
|
||||
VReaderStatus vreader_remove_reader(VReader *reader);
|
||||
VReaderStatus vreader_insert_card(VReader *reader, VCard *card);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef VREADERT_H
|
||||
#define VREADERT_H 1
|
||||
|
||||
typedef enum {
|
||||
VREADER_OK = 0,
|
||||
VREADER_NO_CARD,
|
||||
VREADER_OUT_OF_MEMORY
|
||||
} VReaderStatus;
|
||||
|
||||
typedef unsigned int vreader_id_t;
|
||||
typedef struct VReaderStruct VReader;
|
||||
typedef struct VReaderListStruct VReaderList;
|
||||
typedef struct VReaderListEntryStruct VReaderListEntry;
|
||||
|
||||
typedef struct VReaderEmulStruct VReaderEmul;
|
||||
typedef void (*VReaderEmulFree)(VReaderEmul *);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
/* Virtual Smart Card protocol definition
|
||||
*
|
||||
* This protocol is between a host using virtual smart card readers,
|
||||
* and a client providing the smart cards, perhaps by emulating them or by
|
||||
* access to real cards.
|
||||
*
|
||||
* Definitions for this protocol:
|
||||
* Host - user of the card
|
||||
* Client - owner of the card
|
||||
*
|
||||
* The current implementation passes the raw APDU's from 7816 and additionally
|
||||
* contains messages to setup and teardown readers, handle insertion and
|
||||
* removal of cards, negotiate the protocol via capabilities and provide
|
||||
* for error responses.
|
||||
*
|
||||
* Copyright (c) 2011 Red Hat.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef VSCARD_COMMON_H
|
||||
#define VSCARD_COMMON_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <glib.h>
|
||||
|
||||
#define VERSION_MAJOR_BITS 11
|
||||
#define VERSION_MIDDLE_BITS 11
|
||||
#define VERSION_MINOR_BITS 10
|
||||
|
||||
#define MAKE_VERSION(major, middle, minor) \
|
||||
((major << (VERSION_MINOR_BITS + VERSION_MIDDLE_BITS)) \
|
||||
| (middle << VERSION_MINOR_BITS) \
|
||||
| (minor))
|
||||
|
||||
/*
|
||||
* IMPORTANT NOTE on VERSION
|
||||
*
|
||||
* The version below MUST be changed whenever a change in this file is made.
|
||||
*
|
||||
* The last digit, the minor, is for bug fix changes only.
|
||||
*
|
||||
* The middle digit is for backward / forward compatible changes, updates
|
||||
* to the existing messages, addition of fields.
|
||||
*
|
||||
* The major digit is for a breaking change of protocol, presumably
|
||||
* something that cannot be accommodated with the existing protocol.
|
||||
*/
|
||||
|
||||
#define VSCARD_VERSION MAKE_VERSION(0, 0, 2)
|
||||
|
||||
typedef enum VSCMsgType {
|
||||
VSC_Init = 1,
|
||||
VSC_Error,
|
||||
VSC_ReaderAdd,
|
||||
VSC_ReaderRemove,
|
||||
VSC_ATR,
|
||||
VSC_CardRemove,
|
||||
VSC_APDU,
|
||||
VSC_Flush,
|
||||
VSC_FlushComplete
|
||||
} VSCMsgType;
|
||||
|
||||
typedef enum VSCErrorCode {
|
||||
VSC_SUCCESS = 0,
|
||||
VSC_GENERAL_ERROR = 1,
|
||||
VSC_CANNOT_ADD_MORE_READERS,
|
||||
VSC_CARD_ALREAY_INSERTED,
|
||||
} VSCErrorCode;
|
||||
|
||||
#define VSCARD_UNDEFINED_READER_ID 0xffffffff
|
||||
#define VSCARD_MINIMAL_READER_ID 0
|
||||
|
||||
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
|
||||
#define VSCARD_MAGIC_CONST(f) \
|
||||
((uint32_t)(((f)[0])|((f)[1]<<8)|((f)[2]<<16)|((f)[3]<<24)))
|
||||
#else
|
||||
#define VSCARD_MAGIC_CONST(f) \
|
||||
((uint32_t)(((f)[3])|((f)[2]<<8)|((f)[1]<<16)|((f)[0]<<24)))
|
||||
#endif
|
||||
|
||||
#define VSCARD_MAGIC VSCARD_MAGIC_CONST("VSCD")
|
||||
/*
|
||||
* Header
|
||||
* Each message starts with the header.
|
||||
* type - message type
|
||||
* reader_id - used by messages that are reader specific
|
||||
* length - length of payload (not including header, i.e. zero for
|
||||
* messages containing empty payloads)
|
||||
*/
|
||||
typedef struct VSCMsgHeader {
|
||||
uint32_t type;
|
||||
uint32_t reader_id;
|
||||
uint32_t length;
|
||||
uint8_t data[0];
|
||||
} VSCMsgHeader;
|
||||
|
||||
/*
|
||||
* VSCMsgInit Client <-> Host
|
||||
* Client sends it on connection, with its own capabilities.
|
||||
* Host replies with VSCMsgInit filling in its capabilities.
|
||||
*
|
||||
* It is not meant to be used for negotiation, i.e. sending more then
|
||||
* once from any side, but could be used for that in the future.
|
||||
*/
|
||||
typedef struct VSCMsgInit {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
uint32_t capabilities[1]; /* receiver must check length,
|
||||
array may grow in the future*/
|
||||
} VSCMsgInit;
|
||||
|
||||
/*
|
||||
* VSCMsgError Client <-> Host
|
||||
* This message is a response to any of:
|
||||
* Reader Add
|
||||
* Reader Remove
|
||||
* Card Remove
|
||||
* If the operation was successful then VSC_SUCCESS
|
||||
* is returned, other wise a specific error code.
|
||||
*/
|
||||
typedef struct VSCMsgError {
|
||||
uint32_t code;
|
||||
} VSCMsgError;
|
||||
|
||||
/*
|
||||
* VSCMsgReaderAdd Client -> Host
|
||||
* Host replies with allocated reader id in VSCMsgError with code==SUCCESS.
|
||||
*
|
||||
* name - name of the reader on client side, UTF-8 encoded. Only used
|
||||
* for client presentation (may be translated to the device presented to the
|
||||
* guest), protocol wise only reader_id is important.
|
||||
*/
|
||||
typedef struct VSCMsgReaderAdd {
|
||||
uint8_t name[0];
|
||||
} VSCMsgReaderAdd;
|
||||
|
||||
/*
|
||||
* VSCMsgReaderRemove Client -> Host
|
||||
* The client's reader has been removed.
|
||||
*/
|
||||
typedef struct VSCMsgReaderRemove {
|
||||
} VSCMsgReaderRemove;
|
||||
|
||||
/*
|
||||
* VSCMsgATR Client -> Host
|
||||
* Answer to reset. Sent for card insertion or card reset. The reset/insertion
|
||||
* happens on the client side, they do not require any action from the host.
|
||||
*/
|
||||
typedef struct VSCMsgATR {
|
||||
uint8_t atr[0];
|
||||
} VSCMsgATR;
|
||||
|
||||
/*
|
||||
* VSCMsgCardRemove Client -> Host
|
||||
* The client card has been removed.
|
||||
*/
|
||||
typedef struct VSCMsgCardRemove {
|
||||
} VSCMsgCardRemove;
|
||||
|
||||
/*
|
||||
* VSCMsgAPDU Client <-> Host
|
||||
* Main reason of existence. Transfer a single APDU in either direction.
|
||||
*/
|
||||
typedef struct VSCMsgAPDU {
|
||||
uint8_t data[0];
|
||||
} VSCMsgAPDU;
|
||||
|
||||
/*
|
||||
* VSCMsgFlush Host -> Client
|
||||
* Request client to send a FlushComplete message when it is done
|
||||
* servicing all outstanding APDUs
|
||||
*/
|
||||
typedef struct VSCMsgFlush {
|
||||
} VSCMsgFlush;
|
||||
|
||||
/*
|
||||
* VSCMsgFlush Client -> Host
|
||||
* Client response to Flush after all APDUs have been processed and
|
||||
* responses sent.
|
||||
*/
|
||||
typedef struct VSCMsgFlushComplete {
|
||||
} VSCMsgFlushComplete;
|
||||
|
||||
#endif /* VSCARD_COMMON_H */
|
|
@ -0,0 +1,934 @@
|
|||
/*
|
||||
* Tester for VSCARD protocol, client side.
|
||||
*
|
||||
* Can be used with ccid-card-passthru.
|
||||
*
|
||||
* Copyright (c) 2011 Red Hat.
|
||||
* Written by Alon Levy.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef _WIN32
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#define closesocket(x) close(x)
|
||||
#else
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_PCSC)
|
||||
# ifdef __APPLE__
|
||||
# include <PCSC/winscard.h>
|
||||
# include <PCSC/wintypes.h>
|
||||
# else
|
||||
# include <winscard.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include "glib-compat.h"
|
||||
|
||||
#include "vscard_common.h"
|
||||
|
||||
#include "vreader.h"
|
||||
#include "vcard_emul.h"
|
||||
#include "vevent.h"
|
||||
|
||||
static int verbose;
|
||||
static int with_pcsc;
|
||||
|
||||
static void
|
||||
print_byte_array(
|
||||
uint8_t *arrBytes,
|
||||
int nSize
|
||||
) {
|
||||
int i;
|
||||
for (i = 0; i < nSize; i++) {
|
||||
printf("%02X ", arrBytes[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void
|
||||
print_usage(void) {
|
||||
printf("vscclient OPTIONS <host> <port>\n");
|
||||
printf(" -e <emul_args> - Emulator arguments, see below\n");
|
||||
printf(" -c <certname> - Software emulation certificates\n");
|
||||
printf(" -d <level> - Debug level\n");
|
||||
printf(" -p - Use real smartcard to compare with emulator\n");
|
||||
vcard_emul_usage();
|
||||
}
|
||||
|
||||
#if defined(ENABLE_PCSC)
|
||||
static SCARD_IO_REQUEST scard_pci;
|
||||
static SCARDHANDLE scard;
|
||||
static SCARDCONTEXT scard_ctxt;
|
||||
|
||||
static gboolean pcsc_transmit(BYTE *cmd, LONG cmdlen, BYTE *recv, int *recvlen)
|
||||
{
|
||||
LONG rv;
|
||||
|
||||
rv = SCardTransmit(scard, &scard_pci, cmd, cmdlen,
|
||||
NULL, recv, (DWORD*)recvlen);
|
||||
g_return_val_if_fail(rv == SCARD_S_SUCCESS, FALSE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gboolean pcsc_init(void)
|
||||
{
|
||||
LONG rv;
|
||||
DWORD nreaders, protocol;
|
||||
LPTSTR readers;
|
||||
|
||||
rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &scard_ctxt);
|
||||
g_return_val_if_fail(rv == SCARD_S_SUCCESS, FALSE);
|
||||
|
||||
#ifdef SCARD_AUTOALLOCATE
|
||||
nreaders = SCARD_AUTOALLOCATE;
|
||||
|
||||
rv = SCardListReaders(scard_ctxt, NULL, (LPTSTR)&readers, &nreaders);
|
||||
g_return_val_if_fail(rv == SCARD_S_SUCCESS, FALSE);
|
||||
#else
|
||||
rv = SCardListReaders(scard_ctxt, NULL, NULL, &nreaders);
|
||||
g_return_val_if_fail(rv == SCARD_S_SUCCESS, FALSE);
|
||||
|
||||
readers = g_new0(char, nreaders);
|
||||
rv = SCardListReaders(scard_ctxt, NULL, readers, &nreaders);
|
||||
g_return_val_if_fail(rv == SCARD_S_SUCCESS, FALSE);
|
||||
#endif
|
||||
printf("reader name: %s\n", readers);
|
||||
|
||||
rv = SCardConnect(scard_ctxt, readers, SCARD_SHARE_SHARED,
|
||||
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &scard, &protocol);
|
||||
g_return_val_if_fail(rv == SCARD_S_SUCCESS, FALSE);
|
||||
|
||||
switch(protocol) {
|
||||
case SCARD_PROTOCOL_T0:
|
||||
scard_pci = *SCARD_PCI_T0;
|
||||
break;
|
||||
|
||||
case SCARD_PROTOCOL_T1:
|
||||
scard_pci = *SCARD_PCI_T1;
|
||||
break;
|
||||
default:
|
||||
g_return_val_if_reached(FALSE);
|
||||
}
|
||||
|
||||
#ifdef SCARD_AUTOALLOCATE
|
||||
rv = SCardFreeMemory(scard_ctxt, readers);
|
||||
g_warn_if_fail(rv == SCARD_S_SUCCESS);
|
||||
#else
|
||||
g_free(readers);
|
||||
#endif
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void pcsc_deinit(void)
|
||||
{
|
||||
LONG rv;
|
||||
|
||||
rv = SCardDisconnect(scard, SCARD_LEAVE_CARD);
|
||||
g_warn_if_fail(rv == SCARD_S_SUCCESS);
|
||||
scard = 0;
|
||||
|
||||
rv = SCardReleaseContext(scard_ctxt);
|
||||
g_warn_if_fail(rv == SCARD_S_SUCCESS);
|
||||
scard_ctxt = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static GIOChannel *channel_socket;
|
||||
static GByteArray *socket_to_send;
|
||||
static CompatGMutex socket_to_send_lock;
|
||||
static guint socket_tag;
|
||||
|
||||
static void
|
||||
update_socket_watch(void);
|
||||
|
||||
static gboolean
|
||||
do_socket_send(GIOChannel *source,
|
||||
GIOCondition condition,
|
||||
gpointer data)
|
||||
{
|
||||
gsize bw;
|
||||
GError *err = NULL;
|
||||
|
||||
g_return_val_if_fail(socket_to_send->len != 0, FALSE);
|
||||
g_return_val_if_fail(condition & G_IO_OUT, FALSE);
|
||||
|
||||
g_io_channel_write_chars(channel_socket,
|
||||
(gchar *)socket_to_send->data, socket_to_send->len, &bw, &err);
|
||||
if (err != NULL) {
|
||||
g_error("Error while sending socket %s", err->message);
|
||||
return FALSE;
|
||||
}
|
||||
g_byte_array_remove_range(socket_to_send, 0, bw);
|
||||
|
||||
if (socket_to_send->len == 0) {
|
||||
update_socket_watch();
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
socket_prepare_sending(gpointer user_data)
|
||||
{
|
||||
update_socket_watch();
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int
|
||||
send_msg(
|
||||
VSCMsgType type,
|
||||
uint32_t reader_id,
|
||||
const void *msg,
|
||||
unsigned int length
|
||||
) {
|
||||
VSCMsgHeader mhHeader;
|
||||
|
||||
g_mutex_lock(&socket_to_send_lock);
|
||||
|
||||
if (verbose > 10) {
|
||||
printf("sending type=%d id=%u, len =%u (0x%x)\n",
|
||||
type, reader_id, length, length);
|
||||
}
|
||||
|
||||
mhHeader.type = htonl(type);
|
||||
mhHeader.reader_id = 0;
|
||||
mhHeader.length = htonl(length);
|
||||
g_byte_array_append(socket_to_send, (guint8 *)&mhHeader, sizeof(mhHeader));
|
||||
g_byte_array_append(socket_to_send, (guint8 *)msg, length);
|
||||
g_idle_add(socket_prepare_sending, NULL);
|
||||
|
||||
g_mutex_unlock(&socket_to_send_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static VReader *pending_reader;
|
||||
static CompatGMutex pending_reader_lock;
|
||||
static CompatGCond pending_reader_condition;
|
||||
|
||||
#define MAX_ATR_LEN 40
|
||||
static gpointer
|
||||
event_thread(gpointer arg)
|
||||
{
|
||||
unsigned char atr[MAX_ATR_LEN];
|
||||
int atr_len;
|
||||
VEvent *event;
|
||||
unsigned int reader_id;
|
||||
|
||||
|
||||
while (1) {
|
||||
const char *reader_name;
|
||||
|
||||
event = vevent_wait_next_vevent();
|
||||
if (event == NULL) {
|
||||
break;
|
||||
}
|
||||
reader_id = vreader_get_id(event->reader);
|
||||
if (reader_id == VSCARD_UNDEFINED_READER_ID &&
|
||||
event->type != VEVENT_READER_INSERT) {
|
||||
/* ignore events from readers qemu has rejected */
|
||||
/* if qemu is still deciding on this reader, wait to see if need to
|
||||
* forward this event */
|
||||
g_mutex_lock(&pending_reader_lock);
|
||||
if (!pending_reader || (pending_reader != event->reader)) {
|
||||
/* wasn't for a pending reader, this reader has already been
|
||||
* rejected by qemu */
|
||||
g_mutex_unlock(&pending_reader_lock);
|
||||
vevent_delete(event);
|
||||
continue;
|
||||
}
|
||||
/* this reader hasn't been told its status from qemu yet, wait for
|
||||
* that status */
|
||||
while (pending_reader != NULL) {
|
||||
g_cond_wait(&pending_reader_condition, &pending_reader_lock);
|
||||
}
|
||||
g_mutex_unlock(&pending_reader_lock);
|
||||
/* now recheck the id */
|
||||
reader_id = vreader_get_id(event->reader);
|
||||
if (reader_id == VSCARD_UNDEFINED_READER_ID) {
|
||||
/* this reader was rejected */
|
||||
vevent_delete(event);
|
||||
continue;
|
||||
}
|
||||
/* reader was accepted, now forward the event */
|
||||
}
|
||||
switch (event->type) {
|
||||
case VEVENT_READER_INSERT:
|
||||
/* tell qemu to insert a new CCID reader */
|
||||
/* wait until qemu has responded to our first reader insert
|
||||
* before we send a second. That way we won't confuse the responses
|
||||
* */
|
||||
g_mutex_lock(&pending_reader_lock);
|
||||
while (pending_reader != NULL) {
|
||||
g_cond_wait(&pending_reader_condition, &pending_reader_lock);
|
||||
}
|
||||
pending_reader = vreader_reference(event->reader);
|
||||
g_mutex_unlock(&pending_reader_lock);
|
||||
reader_name = vreader_get_name(event->reader);
|
||||
if (verbose > 10) {
|
||||
printf(" READER INSERT: %s\n", reader_name);
|
||||
}
|
||||
send_msg(VSC_ReaderAdd,
|
||||
reader_id, /* currerntly VSCARD_UNDEFINED_READER_ID */
|
||||
NULL, 0 /* TODO reader_name, strlen(reader_name) */);
|
||||
break;
|
||||
case VEVENT_READER_REMOVE:
|
||||
/* future, tell qemu that an old CCID reader has been removed */
|
||||
if (verbose > 10) {
|
||||
printf(" READER REMOVE: %u\n", reader_id);
|
||||
}
|
||||
send_msg(VSC_ReaderRemove, reader_id, NULL, 0);
|
||||
break;
|
||||
case VEVENT_CARD_INSERT:
|
||||
/* get the ATR (intended as a response to a power on from the
|
||||
* reader */
|
||||
atr_len = MAX_ATR_LEN;
|
||||
vreader_power_on(event->reader, atr, &atr_len);
|
||||
/* ATR call functions as a Card Insert event */
|
||||
if (verbose > 10) {
|
||||
printf(" CARD INSERT %u: ", reader_id);
|
||||
print_byte_array(atr, atr_len);
|
||||
}
|
||||
send_msg(VSC_ATR, reader_id, atr, atr_len);
|
||||
break;
|
||||
case VEVENT_CARD_REMOVE:
|
||||
/* Card removed */
|
||||
if (verbose > 10) {
|
||||
printf(" CARD REMOVE %u:\n", reader_id);
|
||||
}
|
||||
send_msg(VSC_CardRemove, reader_id, NULL, 0);
|
||||
break;
|
||||
case VEVENT_LAST:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
vevent_delete(event);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static unsigned int
|
||||
get_id_from_string(char *string, unsigned int default_id)
|
||||
{
|
||||
unsigned int id = atoi(string);
|
||||
|
||||
/* don't accidentally swith to zero because no numbers have been supplied */
|
||||
if ((id == 0) && *string != '0') {
|
||||
return default_id;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
static int
|
||||
on_host_init(VSCMsgHeader *mhHeader, VSCMsgInit *incoming)
|
||||
{
|
||||
uint32_t *capabilities = (incoming->capabilities);
|
||||
int num_capabilities =
|
||||
1 + ((mhHeader->length - sizeof(VSCMsgInit)) / sizeof(uint32_t));
|
||||
int i;
|
||||
|
||||
incoming->version = ntohl(incoming->version);
|
||||
if (incoming->version != VSCARD_VERSION) {
|
||||
if (verbose > 0) {
|
||||
printf("warning: host has version %d, we have %d\n",
|
||||
verbose, VSCARD_VERSION);
|
||||
}
|
||||
}
|
||||
if (incoming->magic != VSCARD_MAGIC) {
|
||||
printf("unexpected magic: got %d, expected %d\n",
|
||||
incoming->magic, VSCARD_MAGIC);
|
||||
return -1;
|
||||
}
|
||||
for (i = 0 ; i < num_capabilities; ++i) {
|
||||
capabilities[i] = ntohl(capabilities[i]);
|
||||
}
|
||||
/* Future: check capabilities */
|
||||
/* remove whatever reader might be left in qemu,
|
||||
* in case of an unclean previous exit. */
|
||||
send_msg(VSC_ReaderRemove, VSCARD_MINIMAL_READER_ID, NULL, 0);
|
||||
/* launch the event_thread. This will trigger reader adds for all the
|
||||
* existing readers */
|
||||
g_thread_new("vsc/event", event_thread, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
enum {
|
||||
STATE_HEADER,
|
||||
STATE_MESSAGE,
|
||||
};
|
||||
|
||||
#define APDUBufSize 270
|
||||
|
||||
static gboolean
|
||||
do_socket_read(GIOChannel *source,
|
||||
GIOCondition condition,
|
||||
gpointer data)
|
||||
{
|
||||
int rv;
|
||||
int dwSendLength;
|
||||
int dwRecvLength;
|
||||
uint8_t pbRecvBuffer[APDUBufSize];
|
||||
static uint8_t pbSendBuffer[APDUBufSize];
|
||||
VReaderStatus reader_status;
|
||||
VReader *reader = NULL;
|
||||
static VSCMsgHeader mhHeader;
|
||||
VSCMsgError error_msg;
|
||||
GError *err = NULL;
|
||||
VSCMsgInit init;
|
||||
|
||||
static gchar *buf;
|
||||
static gsize br, to_read;
|
||||
static int state = STATE_HEADER;
|
||||
|
||||
if (state == STATE_HEADER && to_read == 0) {
|
||||
buf = (gchar *)&mhHeader;
|
||||
to_read = sizeof(mhHeader);
|
||||
}
|
||||
|
||||
if (to_read > 0) {
|
||||
g_io_channel_read_chars(source, (gchar *)buf, to_read, &br, &err);
|
||||
if (err != NULL) {
|
||||
g_error("error while reading: %s", err->message);
|
||||
}
|
||||
buf += br;
|
||||
to_read -= br;
|
||||
if (to_read != 0) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (state == STATE_HEADER) {
|
||||
mhHeader.type = ntohl(mhHeader.type);
|
||||
mhHeader.reader_id = ntohl(mhHeader.reader_id);
|
||||
mhHeader.length = ntohl(mhHeader.length);
|
||||
if (verbose) {
|
||||
printf("Header: type=%d, reader_id=%u length=%d (0x%x)\n",
|
||||
mhHeader.type, mhHeader.reader_id, mhHeader.length,
|
||||
mhHeader.length);
|
||||
}
|
||||
switch (mhHeader.type) {
|
||||
case VSC_APDU:
|
||||
case VSC_Flush:
|
||||
case VSC_Error:
|
||||
case VSC_Init:
|
||||
buf = (gchar *)pbSendBuffer;
|
||||
to_read = mhHeader.length;
|
||||
state = STATE_MESSAGE;
|
||||
return TRUE;
|
||||
default:
|
||||
fprintf(stderr, "Unexpected message of type 0x%X\n", mhHeader.type);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (state == STATE_MESSAGE) {
|
||||
char *reply = NULL;
|
||||
#if defined(ENABLE_PCSC)
|
||||
int reply_size;
|
||||
#endif
|
||||
|
||||
switch (mhHeader.type) {
|
||||
case VSC_APDU:
|
||||
if (verbose) {
|
||||
static int n = 0;
|
||||
printf("\n\n >>> %d recv APDU: \n", n++);
|
||||
print_byte_array(pbSendBuffer, mhHeader.length);
|
||||
}
|
||||
|
||||
/* Transmit received APDU */
|
||||
dwSendLength = mhHeader.length;
|
||||
dwRecvLength = sizeof(pbRecvBuffer);
|
||||
reader = vreader_get_reader_by_id(mhHeader.reader_id);
|
||||
reader_status = vreader_xfr_bytes(reader,
|
||||
pbSendBuffer, dwSendLength,
|
||||
pbRecvBuffer, &dwRecvLength);
|
||||
if (verbose) {
|
||||
printf("libcacard response: ");
|
||||
print_byte_array(pbRecvBuffer, dwRecvLength);
|
||||
}
|
||||
|
||||
#if defined(ENABLE_PCSC)
|
||||
if (with_pcsc) {
|
||||
reply_size = dwRecvLength;
|
||||
reply = g_memdup(pbRecvBuffer, reply_size);
|
||||
|
||||
dwSendLength = mhHeader.length;
|
||||
dwRecvLength = sizeof(pbRecvBuffer);
|
||||
|
||||
if (!pcsc_transmit(pbSendBuffer, dwSendLength,
|
||||
pbRecvBuffer, &dwRecvLength))
|
||||
reader_status = VREADER_OK;
|
||||
else
|
||||
reader_status = VREADER_NO_CARD;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (reader_status == VREADER_OK) {
|
||||
mhHeader.length = dwRecvLength;
|
||||
#if defined(ENABLE_PCSC)
|
||||
if (with_pcsc && verbose) {
|
||||
int diff = (unsigned int) reply_size != mhHeader.length ||
|
||||
memcmp(pbRecvBuffer, reply, reply_size);
|
||||
printf("HW response:%s ", diff ? "\x1B[31m!!!\x1B[0m" : "");
|
||||
print_byte_array(pbRecvBuffer, mhHeader.length);
|
||||
}
|
||||
#endif
|
||||
send_msg(VSC_APDU, mhHeader.reader_id,
|
||||
pbRecvBuffer, dwRecvLength);
|
||||
} else {
|
||||
rv = reader_status; /* warning: not meaningful */
|
||||
send_msg(VSC_Error, mhHeader.reader_id, &rv, sizeof(uint32_t));
|
||||
}
|
||||
g_free(reply);
|
||||
vreader_free(reader);
|
||||
reader = NULL; /* we've freed it, don't use it by accident
|
||||
again */
|
||||
break;
|
||||
case VSC_Flush:
|
||||
/* TODO: actually flush */
|
||||
send_msg(VSC_FlushComplete, mhHeader.reader_id, NULL, 0);
|
||||
break;
|
||||
case VSC_Error:
|
||||
memcpy(&error_msg, pbSendBuffer, sizeof(VSCMsgError));
|
||||
if (error_msg.code == VSC_SUCCESS) {
|
||||
g_mutex_lock(&pending_reader_lock);
|
||||
if (pending_reader) {
|
||||
vreader_set_id(pending_reader, mhHeader.reader_id);
|
||||
vreader_free(pending_reader);
|
||||
pending_reader = NULL;
|
||||
g_cond_signal(&pending_reader_condition);
|
||||
}
|
||||
g_mutex_unlock(&pending_reader_lock);
|
||||
break;
|
||||
}
|
||||
printf("warning: qemu refused to add reader\n");
|
||||
if (error_msg.code == VSC_CANNOT_ADD_MORE_READERS) {
|
||||
/* clear pending reader, qemu can't handle any more */
|
||||
g_mutex_lock(&pending_reader_lock);
|
||||
if (pending_reader) {
|
||||
pending_reader = NULL;
|
||||
/* make sure the event loop doesn't hang */
|
||||
g_cond_signal(&pending_reader_condition);
|
||||
}
|
||||
g_mutex_unlock(&pending_reader_lock);
|
||||
}
|
||||
break;
|
||||
case VSC_Init:
|
||||
memcpy(&init, pbSendBuffer, sizeof(VSCMsgInit));
|
||||
if (on_host_init(&mhHeader, &init) < 0) {
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
state = STATE_HEADER;
|
||||
}
|
||||
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
do_socket(GIOChannel *source,
|
||||
GIOCondition condition,
|
||||
gpointer data)
|
||||
{
|
||||
/* not sure if two watches work well with a single win32 sources */
|
||||
if (condition & G_IO_OUT) {
|
||||
if (!do_socket_send(source, condition, data)) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (condition & G_IO_IN) {
|
||||
if (!do_socket_read(source, condition, data)) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
update_socket_watch(void)
|
||||
{
|
||||
gboolean out = socket_to_send->len > 0;
|
||||
|
||||
if (socket_tag != 0) {
|
||||
g_source_remove(socket_tag);
|
||||
}
|
||||
|
||||
socket_tag = g_io_add_watch(channel_socket,
|
||||
G_IO_IN | (out ? G_IO_OUT : 0), do_socket, NULL);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
do_command(GIOChannel *source,
|
||||
GIOCondition condition,
|
||||
gpointer data)
|
||||
{
|
||||
char *string;
|
||||
VCardEmulError error;
|
||||
static unsigned int default_reader_id;
|
||||
unsigned int reader_id;
|
||||
VReader *reader = NULL;
|
||||
GError *err = NULL;
|
||||
|
||||
g_assert(condition & G_IO_IN);
|
||||
|
||||
reader_id = default_reader_id;
|
||||
g_io_channel_read_line(source, &string, NULL, NULL, &err);
|
||||
if (err != NULL) {
|
||||
g_error("Error while reading command: %s", err->message);
|
||||
}
|
||||
|
||||
if (string != NULL) {
|
||||
if (strncmp(string, "exit", 4) == 0) {
|
||||
/* remove all the readers */
|
||||
VReaderList *list = vreader_get_reader_list();
|
||||
VReaderListEntry *reader_entry;
|
||||
printf("Active Readers:\n");
|
||||
for (reader_entry = vreader_list_get_first(list); reader_entry;
|
||||
reader_entry = vreader_list_get_next(reader_entry)) {
|
||||
VReader *r = vreader_list_get_reader(reader_entry);
|
||||
vreader_id_t id;
|
||||
id = vreader_get_id(r);
|
||||
if (id == (vreader_id_t)-1) {
|
||||
continue;
|
||||
}
|
||||
/* be nice and signal card removal first (qemu probably should
|
||||
* do this itself) */
|
||||
if (vreader_card_is_present(r) == VREADER_OK) {
|
||||
send_msg(VSC_CardRemove, id, NULL, 0);
|
||||
}
|
||||
send_msg(VSC_ReaderRemove, id, NULL, 0);
|
||||
}
|
||||
exit(0);
|
||||
} else if (strncmp(string, "insert", 6) == 0) {
|
||||
if (string[6] == ' ') {
|
||||
reader_id = get_id_from_string(&string[7], reader_id);
|
||||
}
|
||||
reader = vreader_get_reader_by_id(reader_id);
|
||||
if (reader != NULL) {
|
||||
error = vcard_emul_force_card_insert(reader);
|
||||
printf("insert %s, returned %d\n",
|
||||
vreader_get_name(reader), error);
|
||||
} else {
|
||||
printf("no reader by id %u found\n", reader_id);
|
||||
}
|
||||
} else if (strncmp(string, "remove", 6) == 0) {
|
||||
if (string[6] == ' ') {
|
||||
reader_id = get_id_from_string(&string[7], reader_id);
|
||||
}
|
||||
reader = vreader_get_reader_by_id(reader_id);
|
||||
if (reader != NULL) {
|
||||
error = vcard_emul_force_card_remove(reader);
|
||||
printf("remove %s, returned %d\n",
|
||||
vreader_get_name(reader), error);
|
||||
} else {
|
||||
printf("no reader by id %u found\n", reader_id);
|
||||
}
|
||||
} else if (strncmp(string, "select", 6) == 0) {
|
||||
if (string[6] == ' ') {
|
||||
reader_id = get_id_from_string(&string[7],
|
||||
VSCARD_UNDEFINED_READER_ID);
|
||||
}
|
||||
if (reader_id != VSCARD_UNDEFINED_READER_ID) {
|
||||
reader = vreader_get_reader_by_id(reader_id);
|
||||
}
|
||||
if (reader) {
|
||||
printf("Selecting reader %u, %s\n", reader_id,
|
||||
vreader_get_name(reader));
|
||||
default_reader_id = reader_id;
|
||||
} else {
|
||||
printf("Reader with id %u not found\n", reader_id);
|
||||
}
|
||||
} else if (strncmp(string, "debug", 5) == 0) {
|
||||
if (string[5] == ' ') {
|
||||
verbose = get_id_from_string(&string[6], 0);
|
||||
}
|
||||
printf("debug level = %d\n", verbose);
|
||||
} else if (strncmp(string, "list", 4) == 0) {
|
||||
VReaderList *list = vreader_get_reader_list();
|
||||
VReaderListEntry *reader_entry;
|
||||
printf("Active Readers:\n");
|
||||
for (reader_entry = vreader_list_get_first(list); reader_entry;
|
||||
reader_entry = vreader_list_get_next(reader_entry)) {
|
||||
VReader *r = vreader_list_get_reader(reader_entry);
|
||||
vreader_id_t id;
|
||||
id = vreader_get_id(r);
|
||||
if (id == (vreader_id_t)-1) {
|
||||
continue;
|
||||
}
|
||||
printf("%3u %s %s\n", id,
|
||||
vreader_card_is_present(r) == VREADER_OK ?
|
||||
"CARD_PRESENT" : " ",
|
||||
vreader_get_name(r));
|
||||
}
|
||||
printf("Inactive Readers:\n");
|
||||
for (reader_entry = vreader_list_get_first(list); reader_entry;
|
||||
reader_entry = vreader_list_get_next(reader_entry)) {
|
||||
VReader *r = vreader_list_get_reader(reader_entry);
|
||||
vreader_id_t id;
|
||||
id = vreader_get_id(reader);
|
||||
if (id != (vreader_id_t)-1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("INA %s %s\n",
|
||||
vreader_card_is_present(r) == VREADER_OK ?
|
||||
"CARD_PRESENT" : " ",
|
||||
vreader_get_name(r));
|
||||
}
|
||||
vreader_list_delete(list);
|
||||
} else if (*string != 0) {
|
||||
printf("valid commands:\n");
|
||||
printf("insert [reader_id]\n");
|
||||
printf("remove [reader_id]\n");
|
||||
printf("select reader_id\n");
|
||||
printf("list\n");
|
||||
printf("debug [level]\n");
|
||||
printf("exit\n");
|
||||
}
|
||||
}
|
||||
vreader_free(reader);
|
||||
printf("> ");
|
||||
fflush(stdout);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* just for ease of parsing command line arguments. */
|
||||
#define MAX_CERTS 100
|
||||
|
||||
static int
|
||||
connect_to_qemu(
|
||||
const char *host,
|
||||
const char *port
|
||||
) {
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *server = NULL;
|
||||
int ret, sock;
|
||||
|
||||
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock < 0) {
|
||||
/* Error */
|
||||
fprintf(stderr, "Error opening socket!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = 0;
|
||||
hints.ai_protocol = 0; /* Any protocol */
|
||||
|
||||
ret = getaddrinfo(host, port, &hints, &server);
|
||||
|
||||
if (ret != 0) {
|
||||
/* Error */
|
||||
fprintf(stderr, "getaddrinfo failed\n");
|
||||
goto cleanup_socket;
|
||||
}
|
||||
|
||||
if (connect(sock, server->ai_addr, server->ai_addrlen) < 0) {
|
||||
/* Error */
|
||||
fprintf(stderr, "Could not connect\n");
|
||||
goto cleanup_socket;
|
||||
}
|
||||
if (verbose) {
|
||||
printf("Connected (sizeof Header=%zd)!\n", sizeof(VSCMsgHeader));
|
||||
}
|
||||
|
||||
freeaddrinfo(server);
|
||||
return sock;
|
||||
|
||||
cleanup_socket:
|
||||
if (server) {
|
||||
freeaddrinfo(server);
|
||||
}
|
||||
closesocket(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
main(
|
||||
int argc,
|
||||
char *argv[]
|
||||
) {
|
||||
GMainLoop *loop;
|
||||
GIOChannel *channel_stdin;
|
||||
char *qemu_host;
|
||||
char *qemu_port;
|
||||
VSCMsgInit init;
|
||||
VCardEmulOptions *command_line_options = NULL;
|
||||
|
||||
char *cert_names[MAX_CERTS];
|
||||
char *emul_args = NULL;
|
||||
int cert_count = 0;
|
||||
int c, sock;
|
||||
|
||||
#ifdef _WIN32
|
||||
WSADATA Data;
|
||||
|
||||
if (WSAStartup(MAKEWORD(2, 2), &Data) != 0) {
|
||||
c = WSAGetLastError();
|
||||
fprintf(stderr, "WSAStartup: %d\n", c);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
#if !GLIB_CHECK_VERSION(2, 31, 0)
|
||||
if (!g_thread_supported()) {
|
||||
g_thread_init(NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
while ((c = getopt(argc, argv, "c:e:d:p")) != -1) {
|
||||
if (c == '?') {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case 'c':
|
||||
assert(optarg != NULL);
|
||||
if (cert_count >= MAX_CERTS) {
|
||||
printf("too many certificates (max = %d)\n", MAX_CERTS);
|
||||
exit(5);
|
||||
}
|
||||
cert_names[cert_count++] = optarg;
|
||||
break;
|
||||
case 'e':
|
||||
assert(optarg != NULL);
|
||||
emul_args = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
assert(optarg != NULL);
|
||||
verbose = get_id_from_string(optarg, 1);
|
||||
break;
|
||||
case 'p':
|
||||
with_pcsc = 1;
|
||||
break;
|
||||
default:
|
||||
g_warn_if_reached();
|
||||
}
|
||||
}
|
||||
|
||||
if (argc - optind != 2) {
|
||||
print_usage();
|
||||
exit(4);
|
||||
}
|
||||
|
||||
if (cert_count > 0) {
|
||||
char *new_args;
|
||||
int len, i;
|
||||
/* if we've given some -c options, we clearly we want do so some
|
||||
* software emulation. add that emulation now. this is NSS Emulator
|
||||
* specific */
|
||||
if (emul_args == NULL) {
|
||||
emul_args = (char *)"db=\"/etc/pki/nssdb\"";
|
||||
}
|
||||
#define SOFT_STRING ",soft=(,Virtual Reader,CAC,,"
|
||||
/* 2 == close paren & null */
|
||||
len = strlen(emul_args) + strlen(SOFT_STRING) + 2;
|
||||
for (i = 0; i < cert_count; i++) {
|
||||
len += strlen(cert_names[i])+1; /* 1 == comma */
|
||||
}
|
||||
new_args = g_malloc(len);
|
||||
strcpy(new_args, emul_args);
|
||||
strcat(new_args, SOFT_STRING);
|
||||
for (i = 0; i < cert_count; i++) {
|
||||
strcat(new_args, cert_names[i]);
|
||||
strcat(new_args, ",");
|
||||
}
|
||||
strcat(new_args, ")");
|
||||
emul_args = new_args;
|
||||
}
|
||||
if (emul_args) {
|
||||
command_line_options = vcard_emul_options(emul_args);
|
||||
}
|
||||
|
||||
qemu_host = g_strdup(argv[argc - 2]);
|
||||
qemu_port = g_strdup(argv[argc - 1]);
|
||||
sock = connect_to_qemu(qemu_host, qemu_port);
|
||||
if (sock == -1) {
|
||||
fprintf(stderr, "error opening socket, exiting.\n");
|
||||
exit(5);
|
||||
}
|
||||
|
||||
socket_to_send = g_byte_array_new();
|
||||
vcard_emul_init(command_line_options);
|
||||
loop = g_main_loop_new(NULL, TRUE);
|
||||
|
||||
printf("> ");
|
||||
fflush(stdout);
|
||||
|
||||
#ifdef _WIN32
|
||||
channel_stdin = g_io_channel_win32_new_fd(STDIN_FILENO);
|
||||
#else
|
||||
channel_stdin = g_io_channel_unix_new(STDIN_FILENO);
|
||||
#endif
|
||||
g_io_add_watch(channel_stdin, G_IO_IN, do_command, NULL);
|
||||
#ifdef _WIN32
|
||||
channel_socket = g_io_channel_win32_new_socket(sock);
|
||||
#else
|
||||
channel_socket = g_io_channel_unix_new(sock);
|
||||
#endif
|
||||
g_io_channel_set_encoding(channel_socket, NULL, NULL);
|
||||
/* we buffer ourself for thread safety reasons */
|
||||
g_io_channel_set_buffered(channel_socket, FALSE);
|
||||
|
||||
if (with_pcsc) {
|
||||
#if defined(ENABLE_PCSC)
|
||||
if (!pcsc_init())
|
||||
return 1;
|
||||
#else
|
||||
printf("No PCSC support\n");
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Send init message, Host responds (and then we send reader attachments) */
|
||||
init = (VSCMsgInit) {
|
||||
.version = htonl(VSCARD_VERSION),
|
||||
.magic = VSCARD_MAGIC,
|
||||
.capabilities = {0}
|
||||
};
|
||||
send_msg(VSC_Init, 0, &init, sizeof(init));
|
||||
|
||||
g_main_loop_run(loop);
|
||||
g_main_loop_unref(loop);
|
||||
|
||||
g_io_channel_unref(channel_stdin);
|
||||
g_io_channel_unref(channel_socket);
|
||||
g_byte_array_free(socket_to_send, TRUE);
|
||||
|
||||
closesocket(sock);
|
||||
|
||||
#if defined(ENABLE_PCSC)
|
||||
pcsc_deinit();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
organization = "OpenSC"
|
||||
expiration_days = 365
|
||||
email = "none@example.org"
|
||||
signing_key
|
||||
encryption_key
|
||||
|
|
@ -0,0 +1,821 @@
|
|||
/*
|
||||
* Shared test functions for libCACard
|
||||
*
|
||||
* Copyright 2018 Red Hat, Inc.
|
||||
*
|
||||
* Author: Jakub Jelen <jjelen@redhat.com>
|
||||
*
|
||||
* This code is licensed under the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
#include <glib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "simpletlv.h"
|
||||
|
||||
int key_bits = 0;
|
||||
int hw_tests = 0;
|
||||
|
||||
|
||||
static void select_coid(VReader *reader, unsigned char *coid,
|
||||
int expect_success)
|
||||
{
|
||||
VReaderStatus status;
|
||||
int dwRecvLength = APDUBufSize;
|
||||
uint8_t pbRecvBuffer[APDUBufSize];
|
||||
uint8_t selfile[] = {
|
||||
0x00, 0xa4, 0x02, 0x00, 0x02, 0x00, 0x00
|
||||
};
|
||||
size_t selfile_len = sizeof(selfile);
|
||||
|
||||
memcpy(&selfile[5], coid, 2);
|
||||
|
||||
g_debug("%s: Select OID 0x%02x 0x%02x", __func__, coid[0], coid[1]);
|
||||
g_assert_nonnull(reader);
|
||||
status = vreader_xfr_bytes(reader,
|
||||
selfile, selfile_len,
|
||||
pbRecvBuffer, &dwRecvLength);
|
||||
g_assert_cmpint(status, ==, VREADER_OK);
|
||||
if (expect_success) {
|
||||
g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_SUCCESS);
|
||||
g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, 0x00);
|
||||
} else {
|
||||
g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_P1_P2_ERROR);
|
||||
g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, 0x82);
|
||||
}
|
||||
}
|
||||
|
||||
void select_coid_good(VReader *reader, unsigned char *coid)
|
||||
{
|
||||
select_coid(reader, coid, 1);
|
||||
}
|
||||
|
||||
void select_coid_bad(VReader *reader, unsigned char *coid)
|
||||
{
|
||||
select_coid(reader, coid, 0);
|
||||
}
|
||||
|
||||
|
||||
int select_aid_response(VReader *reader, unsigned char *aid,
|
||||
unsigned int aid_len, int response_len)
|
||||
{
|
||||
VReaderStatus status;
|
||||
int dwRecvLength = APDUBufSize;
|
||||
uint8_t pbRecvBuffer[APDUBufSize];
|
||||
uint8_t selfile[] = {
|
||||
0x00, 0xa4, 0x04, 0x00, 0x07, 0xa0, 0x00, 0x00, 0x00, 0x79, 0x01, 0x00
|
||||
};
|
||||
size_t selfile_len = sizeof(selfile);
|
||||
|
||||
g_assert_cmpint(aid_len, ==, 7);
|
||||
memcpy(&selfile[5], aid, aid_len);
|
||||
|
||||
g_debug("%s: Select applet with AID 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x",
|
||||
__func__, aid[0], aid[1], aid[2], aid[3], aid[4], aid[5], aid[6]);
|
||||
g_assert_nonnull(reader);
|
||||
status = vreader_xfr_bytes(reader,
|
||||
selfile, selfile_len,
|
||||
pbRecvBuffer, &dwRecvLength);
|
||||
g_assert_cmpint(status, ==, VREADER_OK);
|
||||
if (response_len > 0) {
|
||||
/* we expect specific amount of response bytes */
|
||||
g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_RESPONSE_BYTES);
|
||||
g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, response_len);
|
||||
} else {
|
||||
/* the default response length is 13 (FCI stub) */
|
||||
g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_RESPONSE_BYTES);
|
||||
g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, 0x0d);
|
||||
}
|
||||
return pbRecvBuffer[dwRecvLength-2];
|
||||
}
|
||||
|
||||
void select_aid(VReader *reader, unsigned char *aid, unsigned int aid_len)
|
||||
{
|
||||
(void) select_aid_response(reader, aid, aid_len, 0);
|
||||
}
|
||||
|
||||
void get_properties_coid(VReader *reader, const unsigned char coid[2],
|
||||
int object_type)
|
||||
{
|
||||
int dwRecvLength = APDUBufSize;
|
||||
VReaderStatus status;
|
||||
uint8_t pbRecvBuffer[APDUBufSize], *p, *p_end, *p2, *p2_end;
|
||||
uint8_t get_properties[] = {
|
||||
/* Get properties [Le] */
|
||||
0x80, 0x56, 0x01, 0x00, 0x00
|
||||
};
|
||||
uint8_t get_properties_tag[] = {
|
||||
/* Get properties [tag list] [Le] */
|
||||
0x80, 0x56, 0x02, 0x00, 0x02, 0x01, 0x01, 0x00
|
||||
};
|
||||
int verified_pki_properties = 0;
|
||||
int num_objects = 0, num_objects_expected = -1;
|
||||
int have_applet_information = 0;
|
||||
|
||||
status = vreader_xfr_bytes(reader,
|
||||
get_properties, sizeof(get_properties),
|
||||
pbRecvBuffer, &dwRecvLength);
|
||||
g_assert_cmpint(status, ==, VREADER_OK);
|
||||
g_assert_cmpint(dwRecvLength, ==, 2);
|
||||
/* for too long Le, the cards return LE_ERROR with correct length to ask */
|
||||
g_assert_cmpint(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_LE_ERROR);
|
||||
g_assert_cmpint(pbRecvBuffer[dwRecvLength-1], >, 0);
|
||||
|
||||
/* Update the APDU to match Le field from response and resend */
|
||||
get_properties[4] = pbRecvBuffer[1];
|
||||
dwRecvLength = APDUBufSize;
|
||||
status = vreader_xfr_bytes(reader,
|
||||
get_properties, sizeof(get_properties),
|
||||
pbRecvBuffer, &dwRecvLength);
|
||||
g_assert_cmpint(status, ==, VREADER_OK);
|
||||
g_assert_cmpint(dwRecvLength, >, 2);
|
||||
g_assert_cmpint(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_SUCCESS);
|
||||
g_assert_cmpint(pbRecvBuffer[dwRecvLength-1], ==, 0x00);
|
||||
|
||||
/* try to parse the response, if it makes sense */
|
||||
p = pbRecvBuffer;
|
||||
p_end = p + dwRecvLength - 2;
|
||||
while (p < p_end) {
|
||||
uint8_t tag;
|
||||
size_t vlen;
|
||||
if (simpletlv_read_tag(&p, p_end - p, &tag, &vlen) < 0) {
|
||||
g_debug("The generated SimpleTLV can not be parsed");
|
||||
g_assert_not_reached();
|
||||
}
|
||||
g_debug("Tag: 0x%02x, Len: %" G_GSIZE_FORMAT, tag, vlen);
|
||||
g_assert_cmpint(vlen, <=, p_end - p);
|
||||
|
||||
switch (tag) {
|
||||
case 0x01: /* Applet Information */
|
||||
g_assert_cmpint(vlen, ==, 5);
|
||||
g_assert_cmphex(*p, ==, 0x10); /* Applet family */
|
||||
have_applet_information = 1;
|
||||
break;
|
||||
|
||||
case 0x40: /* Number of objects */
|
||||
g_assert_cmpint(vlen, ==, 1);
|
||||
if (num_objects_expected != -1) {
|
||||
g_debug("Received multiple number-of-objects tags");
|
||||
g_assert_not_reached();
|
||||
}
|
||||
num_objects_expected = *p;
|
||||
break;
|
||||
|
||||
case 0x50: /* TV Object */
|
||||
case 0x51: /* PKI Object */
|
||||
/* recursive SimpleTLV structure */
|
||||
p2 = p;
|
||||
p2_end = p + vlen;
|
||||
while (p2 < p2_end) {
|
||||
uint8_t tag2;
|
||||
size_t vlen2;
|
||||
if (simpletlv_read_tag(&p2, p2_end - p2, &tag2, &vlen2) < 0) {
|
||||
g_debug("The generated SimpleTLV can not be parsed");
|
||||
g_assert_not_reached();
|
||||
}
|
||||
g_assert_cmpint(vlen2, <=, p2_end - p2);
|
||||
g_debug(" Tag: 0x%02x, Len: %" G_GSIZE_FORMAT, tag2, vlen2);
|
||||
|
||||
switch (tag2) {
|
||||
case 0x41: /* Object ID */
|
||||
g_assert_cmpmem(p2, vlen2, coid, 2);
|
||||
break;
|
||||
|
||||
case 0x42: /* Buffer properties */
|
||||
g_assert_cmpint(vlen2, ==, 5);
|
||||
if (object_type != TEST_EMPTY_BUFFER)
|
||||
g_assert_cmpint(p2[0], ==, 0x00);
|
||||
break;
|
||||
|
||||
case 0x43: /* PKI properties */
|
||||
g_assert_cmphex(p2[0], ==, 0x06);
|
||||
if (hw_tests) {
|
||||
/* Assuming CAC card with 1024 b RSA keys */
|
||||
key_bits = 1024;
|
||||
} else {
|
||||
/* Assuming 2048 b RSA keys */
|
||||
key_bits = 2048;
|
||||
}
|
||||
g_assert_cmphex(p2[1], ==, (key_bits / 8 / 8));
|
||||
g_assert_cmphex(p2[2], ==, 0x01);
|
||||
g_assert_cmphex(p2[3], ==, 0x01);
|
||||
verified_pki_properties = 1;
|
||||
break;
|
||||
|
||||
case 0x26:
|
||||
g_assert_cmpint(vlen2, ==, 1);
|
||||
g_assert_cmphex(p2[0], ==, 0x01);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_debug("Unknown tag in object: 0x%02x", tag2);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
p2 += vlen2;
|
||||
}
|
||||
/* one more object processed */
|
||||
num_objects++;
|
||||
break;
|
||||
|
||||
case 0x39:
|
||||
g_assert_cmpint(vlen, ==, 1);
|
||||
g_assert_cmphex(p[0], ==, 0x00);
|
||||
break;
|
||||
|
||||
case 0x3A:
|
||||
g_assert_cmpint(vlen, ==, 7);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_debug("Unknown tag in properties buffer: 0x%02x", tag);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
p += vlen;
|
||||
}
|
||||
|
||||
if (num_objects_expected != -1) {
|
||||
g_assert_cmpint(num_objects, ==, num_objects_expected);
|
||||
}
|
||||
|
||||
if (object_type == TEST_EMPTY_BUFFER) {
|
||||
g_assert_cmpint(num_objects_expected, ==, 1);
|
||||
}
|
||||
|
||||
if (object_type == TEST_EMPTY) {
|
||||
g_assert_cmpint(num_objects_expected, ==, 0);
|
||||
}
|
||||
|
||||
if (object_type == TEST_PKI) {
|
||||
g_assert_cmpint(verified_pki_properties, ==, 1);
|
||||
}
|
||||
|
||||
g_assert_cmpint(have_applet_information, ==, 1);
|
||||
|
||||
/* Try to list only some properties */
|
||||
dwRecvLength = APDUBufSize;
|
||||
status = vreader_xfr_bytes(reader,
|
||||
get_properties_tag, sizeof(get_properties_tag),
|
||||
pbRecvBuffer, &dwRecvLength);
|
||||
g_assert_cmpint(status, ==, VREADER_OK);
|
||||
g_assert_cmpint(dwRecvLength, ==, 2);
|
||||
g_assert_cmpint(pbRecvBuffer[0], ==, VCARD7816_SW1_LE_ERROR);
|
||||
g_assert_cmpint(pbRecvBuffer[1], ==, 0x0e); /* Two applet information buffers */
|
||||
|
||||
/* Update the APDU to match Le field from response and resend */
|
||||
get_properties_tag[7] = pbRecvBuffer[1];
|
||||
dwRecvLength = APDUBufSize;
|
||||
status = vreader_xfr_bytes(reader,
|
||||
get_properties_tag, sizeof(get_properties_tag),
|
||||
pbRecvBuffer, &dwRecvLength);
|
||||
g_assert_cmpint(status, ==, VREADER_OK);
|
||||
g_assert_cmpint(dwRecvLength, ==, 16); /* Two applet information buffers + status */
|
||||
g_assert_cmpint(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_SUCCESS);
|
||||
g_assert_cmpint(pbRecvBuffer[dwRecvLength-1], ==, 0x00);
|
||||
|
||||
|
||||
/* Test the undocumented P1 = 0x40 */
|
||||
dwRecvLength = APDUBufSize;
|
||||
get_properties[2] = 0x40;
|
||||
get_properties[4] = 0x00;
|
||||
status = vreader_xfr_bytes(reader,
|
||||
get_properties, sizeof(get_properties),
|
||||
pbRecvBuffer, &dwRecvLength);
|
||||
g_assert_cmpint(status, ==, VREADER_OK);
|
||||
/* for too long Le, the cards return LE_ERROR with correct length to ask */
|
||||
g_assert_cmpint(dwRecvLength, ==, 2);
|
||||
g_assert_cmpint(pbRecvBuffer[0], ==, VCARD7816_SW1_LE_ERROR);
|
||||
g_assert_cmpint(pbRecvBuffer[1], !=, 0x00);
|
||||
|
||||
/* Update the APDU to match Le field from response and resend */
|
||||
get_properties[4] = pbRecvBuffer[1];
|
||||
dwRecvLength = APDUBufSize;
|
||||
status = vreader_xfr_bytes(reader,
|
||||
get_properties, sizeof(get_properties),
|
||||
pbRecvBuffer, &dwRecvLength);
|
||||
g_assert_cmpint(status, ==, VREADER_OK);
|
||||
g_assert_cmpint(dwRecvLength, >, 2);
|
||||
g_assert_cmpint(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_SUCCESS);
|
||||
g_assert_cmpint(pbRecvBuffer[dwRecvLength-1], ==, 0x00);
|
||||
|
||||
}
|
||||
|
||||
void get_properties(VReader *reader, int object_type)
|
||||
{
|
||||
unsigned char coid[2];
|
||||
switch (object_type) {
|
||||
case TEST_PKI:
|
||||
// XXX only the first PKI for now
|
||||
coid[0] = 0x01;
|
||||
coid[1] = 0x00;
|
||||
get_properties_coid(reader, coid, object_type);
|
||||
break;
|
||||
|
||||
case TEST_CCC:
|
||||
coid[0] = 0xDB;
|
||||
coid[1] = 0x00;
|
||||
get_properties_coid(reader, coid, object_type);
|
||||
break;
|
||||
|
||||
case TEST_ACA:
|
||||
coid[0] = 0x03;
|
||||
coid[1] = 0x00;
|
||||
get_properties_coid(reader, coid, object_type);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_debug("Got unknown object type");
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
void read_buffer(VReader *reader, uint8_t type, int object_type)
|
||||
{
|
||||
int dwRecvLength = APDUBufSize, dwLength, dwReadLength, offset;
|
||||
VReaderStatus status;
|
||||
uint8_t pbRecvBuffer[APDUBufSize];
|
||||
uint8_t *data;
|
||||
uint8_t read_buffer[] = {
|
||||
/*Read Buffer OFFSET TYPE LENGTH a_Le */
|
||||
0x80, 0x52, 0x00, 0x00, 0x02, 0x01, 0x02, 0x02
|
||||
};
|
||||
int card_urls = 0;
|
||||
|
||||
dwRecvLength = 4;
|
||||
read_buffer[5] = type;
|
||||
status = vreader_xfr_bytes(reader,
|
||||
read_buffer, sizeof(read_buffer),
|
||||
pbRecvBuffer, &dwRecvLength);
|
||||
g_assert_cmpint(status, ==, VREADER_OK);
|
||||
g_assert_cmpint(dwRecvLength, ==, 4);
|
||||
g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_SUCCESS);
|
||||
g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, 0x00);
|
||||
|
||||
dwLength = (pbRecvBuffer[0] & 0xff) | ((pbRecvBuffer[1] << 8) & 0xff);
|
||||
if (dwLength == 0)
|
||||
return;
|
||||
|
||||
data = g_malloc(dwLength);
|
||||
offset = 0x02;
|
||||
do {
|
||||
dwReadLength = MIN(255, dwLength);
|
||||
dwRecvLength = dwReadLength+2;
|
||||
read_buffer[2] = (unsigned char) ((offset >> 8) & 0xff);
|
||||
read_buffer[3] = (unsigned char) (offset & 0xff);
|
||||
read_buffer[6] = (unsigned char) (dwReadLength);
|
||||
read_buffer[7] = (unsigned char) (dwReadLength);
|
||||
status = vreader_xfr_bytes(reader,
|
||||
read_buffer, sizeof(read_buffer),
|
||||
pbRecvBuffer, &dwRecvLength);
|
||||
g_assert_cmpint(status, ==, VREADER_OK);
|
||||
g_assert_cmpint(dwRecvLength, ==, dwReadLength + 2);
|
||||
g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_SUCCESS);
|
||||
g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, 0x00);
|
||||
|
||||
memcpy(data + offset - 2, pbRecvBuffer, dwReadLength);
|
||||
offset += dwLength;
|
||||
dwLength -= dwReadLength;
|
||||
} while (dwLength != 0);
|
||||
|
||||
/* Try to parse the TAG buffer, if it makes sense */
|
||||
if (type == CAC_FILE_TAG) {
|
||||
uint8_t *p = data;
|
||||
uint8_t *p_end = p + offset - 2;
|
||||
while (p < p_end) {
|
||||
uint8_t tag;
|
||||
size_t vlen;
|
||||
if (simpletlv_read_tag(&p, p_end - p, &tag, &vlen) < 0) {
|
||||
g_debug("The generated SimpleTLV can not be parsed");
|
||||
g_assert_not_reached();
|
||||
}
|
||||
g_debug("Tag: 0x%02x, Len: %" G_GSIZE_FORMAT, tag, vlen);
|
||||
|
||||
switch (tag) {
|
||||
case 0xF3: /* CardURL from CCC */
|
||||
if (object_type == TEST_CCC) {
|
||||
card_urls++;
|
||||
} else {
|
||||
g_debug("CardURLs found outside of CCC buffer");
|
||||
g_assert_not_reached();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (object_type == TEST_CCC)
|
||||
g_assert_cmpint(card_urls, ==, 11 + 3);
|
||||
}
|
||||
g_free(data);
|
||||
}
|
||||
|
||||
void select_applet(VReader *reader, int type)
|
||||
{
|
||||
uint8_t selfile_ccc[] = {
|
||||
/* Select CCC Applet */
|
||||
0xa0, 0x00, 0x00, 0x01, 0x16, 0xDB, 0x00
|
||||
};
|
||||
uint8_t selfile_aca[] = {
|
||||
/* Select ACA Applet */
|
||||
0xa0, 0x00, 0x00, 0x00, 0x79, 0x03, 0x00
|
||||
};
|
||||
uint8_t selfile_pki[] = {
|
||||
/* Select first PKI Applet */
|
||||
0xa0, 0x00, 0x00, 0x00, 0x79, 0x01, 0x00
|
||||
};
|
||||
uint8_t selfile_passthrough[] = {
|
||||
/* Select Person Instance (passthrough) */
|
||||
0xa0, 0x00, 0x00, 0x00, 0x79, 0x02, 0x00
|
||||
};
|
||||
uint8_t selfile_empty[] = {
|
||||
0xA0, 0x00, 0x00, 0x00, 0x79, 0x02, 0xF0
|
||||
};
|
||||
uint8_t *aid = NULL;
|
||||
size_t aid_len = 0;
|
||||
|
||||
switch (type) {
|
||||
case TEST_PKI:
|
||||
aid = selfile_pki;
|
||||
aid_len = sizeof(selfile_pki);
|
||||
break;
|
||||
|
||||
case TEST_CCC:
|
||||
aid = selfile_ccc;
|
||||
aid_len = sizeof(selfile_ccc);
|
||||
break;
|
||||
|
||||
case TEST_ACA:
|
||||
aid = selfile_aca;
|
||||
aid_len = sizeof(selfile_aca);
|
||||
break;
|
||||
|
||||
case TEST_PASSTHROUGH:
|
||||
aid = selfile_passthrough;
|
||||
aid_len = sizeof(selfile_passthrough);
|
||||
break;
|
||||
|
||||
case TEST_EMPTY:
|
||||
aid = selfile_empty;
|
||||
aid_len = sizeof(selfile_empty);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
g_assert_nonnull(aid);
|
||||
|
||||
select_aid(reader, aid, aid_len);
|
||||
}
|
||||
|
||||
void do_sign(VReader *reader, int parts)
|
||||
{
|
||||
VReaderStatus status;
|
||||
int dwRecvLength = APDUBufSize;
|
||||
uint8_t pbRecvBuffer[APDUBufSize];
|
||||
uint8_t sign[] = {
|
||||
/* VERIFY [p1,p2=0 ] [Lc ] [2048b keys: 256 bytes of PKCS#1.5 padded data] */
|
||||
0x80, 0x42, 0x00, 0x00, 0x00, 0x01, 0x00,
|
||||
0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0x00, 0x64, 0x61, 0x74, 0x61, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x20,
|
||||
0x28, 0x6d, 0x61, 0x78, 0x20, 0x31, 0x30, 0x30, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x29, 0x0a
|
||||
};
|
||||
int sign_len = sizeof(sign);
|
||||
uint8_t getresp[] = {
|
||||
/* Get Response (max we can get) */
|
||||
0x00, 0xc0, 0x00, 0x00, 0x00
|
||||
};
|
||||
g_assert_nonnull(reader);
|
||||
|
||||
/* Adjust the buffers to match the key lengths, if already retrieved */
|
||||
if (key_bits && key_bits < 2048) {
|
||||
sign[4] = key_bits/8; /* less than 2048b will fit the length into one byte */
|
||||
sign[5] = 0x00;
|
||||
sign[6] = 0x01;
|
||||
memcpy(&sign[7], &sign[sign_len-key_bits/8+2], key_bits/8-2);
|
||||
sign_len = 5 + key_bits/8;
|
||||
}
|
||||
|
||||
/* The driver supports signatures while data are passed in more separate APDUs */
|
||||
if (parts) {
|
||||
int split = 0x47;
|
||||
/* we have not sent the whole buffer */
|
||||
sign[2] = 0x80;
|
||||
sign[4] = split;
|
||||
sign[5] = 0x00;
|
||||
sign[6] = 0x01;
|
||||
sign[7] = 0xFF;
|
||||
sign[8] = 0xFF;
|
||||
sign_len = 5 + split;
|
||||
|
||||
status = vreader_xfr_bytes(reader,
|
||||
sign, sign_len,
|
||||
pbRecvBuffer, &dwRecvLength);
|
||||
g_assert_cmpint(status, ==, VREADER_OK);
|
||||
g_assert_cmpint(dwRecvLength, ==, 2);
|
||||
g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_SUCCESS);
|
||||
g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, 0x00);
|
||||
|
||||
/* the next message will send the rest of the buffer */
|
||||
sign[2] = 0x00;
|
||||
if (key_bits)
|
||||
sign[4] = key_bits/8 - split;
|
||||
else
|
||||
sign[4] = 256 - split;
|
||||
memmove(&sign[5], &sign[5+2+split], sign[4]);
|
||||
sign_len = 5 + sign[4];
|
||||
}
|
||||
|
||||
dwRecvLength = APDUBufSize;
|
||||
status = vreader_xfr_bytes(reader,
|
||||
sign, sign_len,
|
||||
pbRecvBuffer, &dwRecvLength);
|
||||
g_assert_cmpint(status, ==, VREADER_OK);
|
||||
g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_RESPONSE_BYTES);
|
||||
g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, (unsigned char) (key_bits/8));
|
||||
|
||||
|
||||
/* fetch the actual response */
|
||||
dwRecvLength = APDUBufSize;
|
||||
status = vreader_xfr_bytes(reader,
|
||||
getresp, sizeof(getresp),
|
||||
pbRecvBuffer, &dwRecvLength);
|
||||
g_assert_cmpint(status, ==, VREADER_OK);
|
||||
g_assert_cmpint(dwRecvLength, ==, key_bits/8+2);
|
||||
g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_SUCCESS);
|
||||
g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, 0x00);
|
||||
|
||||
}
|
||||
|
||||
void test_empty_applets(void)
|
||||
{
|
||||
uint8_t applet_02fb[] = {
|
||||
0xA0, 0x00, 0x00, 0x00, 0x79, 0x02, 0xFB
|
||||
};
|
||||
uint8_t applet_1201[] = {
|
||||
0xA0, 0x00, 0x00, 0x00, 0x79, 0x12, 0x01
|
||||
};
|
||||
uint8_t applet_1202[] = {
|
||||
0xA0, 0x00, 0x00, 0x00, 0x79, 0x12, 0x02
|
||||
};
|
||||
uint8_t applet_02f0[] = {
|
||||
0xA0, 0x00, 0x00, 0x00, 0x79, 0x02, 0xF0
|
||||
};
|
||||
uint8_t applet_02f1[] = {
|
||||
0xA0, 0x00, 0x00, 0x00, 0x79, 0x02, 0xF1
|
||||
};
|
||||
uint8_t applet_02f2[] = {
|
||||
0xA0, 0x00, 0x00, 0x00, 0x79, 0x02, 0xF2
|
||||
};
|
||||
uint8_t coid[2] = {0x02, 0xFB};
|
||||
|
||||
VReader *reader = vreader_get_reader_by_id(0);
|
||||
|
||||
/* Skip the HW tests without physical card */
|
||||
if (isHWTests() && vreader_card_is_present(reader) != VREADER_OK) {
|
||||
vreader_free(reader);
|
||||
g_test_skip("No physical card found");
|
||||
return;
|
||||
}
|
||||
|
||||
/* select the empty applet A00000007902FB, which should be empty buffer */
|
||||
select_aid(reader, applet_02fb, sizeof(applet_02fb));
|
||||
|
||||
/* get properties */
|
||||
get_properties_coid(reader, coid, TEST_EMPTY_BUFFER);
|
||||
|
||||
/* get the TAG buffer length */
|
||||
read_buffer(reader, CAC_FILE_TAG, TEST_EMPTY_BUFFER);
|
||||
|
||||
/* get the VALUE buffer length */
|
||||
read_buffer(reader, CAC_FILE_VALUE, TEST_EMPTY_BUFFER);
|
||||
|
||||
|
||||
/* select the empty applet A0000000791201, which should be empty buffer */
|
||||
select_aid(reader, applet_1201, sizeof(applet_1201));
|
||||
coid[0] = 0x12;
|
||||
coid[1] = 0x01;
|
||||
|
||||
/* get properties */
|
||||
get_properties_coid(reader, coid, TEST_EMPTY_BUFFER);
|
||||
|
||||
/* get the TAG buffer length */
|
||||
read_buffer(reader, CAC_FILE_TAG, TEST_EMPTY_BUFFER);
|
||||
|
||||
/* get the VALUE buffer length */
|
||||
read_buffer(reader, CAC_FILE_VALUE, TEST_EMPTY_BUFFER);
|
||||
|
||||
|
||||
/* select the empty applet A0000000791202, which should be empty buffer */
|
||||
select_aid(reader, applet_1202, sizeof(applet_1202));
|
||||
coid[0] = 0x12;
|
||||
coid[1] = 0x02;
|
||||
|
||||
/* get properties */
|
||||
get_properties_coid(reader, coid, TEST_EMPTY_BUFFER);
|
||||
|
||||
/* get the TAG buffer length */
|
||||
read_buffer(reader, CAC_FILE_TAG, TEST_EMPTY_BUFFER);
|
||||
|
||||
/* get the VALUE buffer length */
|
||||
read_buffer(reader, CAC_FILE_VALUE, TEST_EMPTY_BUFFER);
|
||||
|
||||
|
||||
/* select the empty applet A00000007902F0, which should be empty */
|
||||
select_aid(reader, applet_02f0, sizeof(applet_02f0));
|
||||
|
||||
/* get properties */
|
||||
get_properties_coid(reader, NULL, TEST_EMPTY);
|
||||
|
||||
|
||||
/* select the empty applet A00000007902F1, which should be empty */
|
||||
select_aid(reader, applet_02f1, sizeof(applet_02f1));
|
||||
|
||||
/* get properties */
|
||||
get_properties_coid(reader, NULL, TEST_EMPTY);
|
||||
|
||||
|
||||
/* select the empty applet A00000007902F2, which should be empty */
|
||||
select_aid(reader, applet_02f2, sizeof(applet_02f2));
|
||||
|
||||
/* get properties */
|
||||
get_properties_coid(reader, NULL, TEST_EMPTY);
|
||||
|
||||
|
||||
vreader_free(reader); /* get by id ref */
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that access method without provided buffer returns valid
|
||||
* SW and allow us to get the response with the following APDU
|
||||
*
|
||||
* opensc-tool -s 00A4040007A000000116DB00 -s 80520000020102 -s 00C0000002 \
|
||||
* -s 00520002020134 -s 00C0000034
|
||||
*/
|
||||
void test_get_response(void)
|
||||
{
|
||||
VReader *reader = vreader_get_reader_by_id(0);
|
||||
int dwRecvLength = APDUBufSize, dwLength;
|
||||
VReaderStatus status;
|
||||
uint8_t pbRecvBuffer[APDUBufSize];
|
||||
uint8_t getresp[] = {
|
||||
/* Get Response (max we can get) */
|
||||
0x00, 0xc0, 0x00, 0x00, 0x00
|
||||
};
|
||||
uint8_t read_buffer[] = {
|
||||
/*Read Buffer OFFSET TYPE LENGTH */
|
||||
0x00, 0x52, 0x00, 0x00, 0x02, 0x01, 0x02 /* no L_e */
|
||||
};
|
||||
|
||||
/* Skip the HW tests without physical card */
|
||||
if (isHWTests() && vreader_card_is_present(reader) != VREADER_OK) {
|
||||
vreader_free(reader);
|
||||
g_test_skip("No physical card found");
|
||||
return;
|
||||
}
|
||||
|
||||
/* select CCC */
|
||||
select_applet(reader, TEST_CCC);
|
||||
|
||||
/* read buffer without response buffer. Ignore the response. */
|
||||
dwRecvLength = 2;
|
||||
status = vreader_xfr_bytes(reader,
|
||||
read_buffer, sizeof(read_buffer),
|
||||
pbRecvBuffer, &dwRecvLength);
|
||||
g_assert_cmpint(status, ==, VREADER_OK);
|
||||
g_assert_cmpint(dwRecvLength, ==, 2);
|
||||
g_assert_cmpint(pbRecvBuffer[0], ==, VCARD7816_SW1_RESPONSE_BYTES);
|
||||
g_assert_cmpint(pbRecvBuffer[1], ==, 0x02);
|
||||
|
||||
/* read buffer without response buffer */
|
||||
dwRecvLength = 2;
|
||||
status = vreader_xfr_bytes(reader,
|
||||
read_buffer, sizeof(read_buffer),
|
||||
pbRecvBuffer, &dwRecvLength);
|
||||
g_assert_cmpint(status, ==, VREADER_OK);
|
||||
g_assert_cmpint(dwRecvLength, ==, 2);
|
||||
g_assert_cmpint(pbRecvBuffer[0], ==, VCARD7816_SW1_RESPONSE_BYTES);
|
||||
g_assert_cmpint(pbRecvBuffer[1], ==, 0x02);
|
||||
|
||||
/* fetch the actual response */
|
||||
dwRecvLength = 4;
|
||||
getresp[4] = 0x02;
|
||||
status = vreader_xfr_bytes(reader,
|
||||
getresp, sizeof(getresp),
|
||||
pbRecvBuffer, &dwRecvLength);
|
||||
g_assert_cmpint(status, ==, VREADER_OK);
|
||||
g_assert_cmpint(dwRecvLength, ==, 4);
|
||||
g_assert_cmphex(pbRecvBuffer[2], ==, VCARD7816_SW1_SUCCESS);
|
||||
g_assert_cmphex(pbRecvBuffer[3], ==, 0x00);
|
||||
|
||||
/* the same with offset */
|
||||
dwLength = (pbRecvBuffer[0] & 0xff) | ((pbRecvBuffer[1] << 8) & 0xff);
|
||||
dwRecvLength = dwLength + 2;
|
||||
read_buffer[3] = 0x02; // offset
|
||||
read_buffer[6] = dwLength;
|
||||
status = vreader_xfr_bytes(reader,
|
||||
read_buffer, sizeof(read_buffer),
|
||||
pbRecvBuffer, &dwRecvLength);
|
||||
g_assert_cmpint(status, ==, VREADER_OK);
|
||||
g_assert_cmpint(dwRecvLength, ==, 2);
|
||||
g_assert_cmpint(pbRecvBuffer[0], ==, VCARD7816_SW1_RESPONSE_BYTES);
|
||||
g_assert_cmpint(pbRecvBuffer[1], ==, dwLength);
|
||||
|
||||
/* fetch the actual response */
|
||||
dwRecvLength = dwLength + 2;
|
||||
getresp[4] = dwLength;
|
||||
status = vreader_xfr_bytes(reader,
|
||||
getresp, sizeof(getresp),
|
||||
pbRecvBuffer, &dwRecvLength);
|
||||
g_assert_cmpint(status, ==, VREADER_OK);
|
||||
g_assert_cmpint(dwRecvLength, ==, dwLength + 2);
|
||||
g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_SUCCESS);
|
||||
g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, 0x00);
|
||||
|
||||
/* If we ask again, when there is no pending response */
|
||||
dwRecvLength = dwLength + 2;
|
||||
getresp[4] = dwLength;
|
||||
status = vreader_xfr_bytes(reader,
|
||||
getresp, sizeof(getresp),
|
||||
pbRecvBuffer, &dwRecvLength);
|
||||
g_assert_cmpint(status, ==, VREADER_OK);
|
||||
g_assert_cmpint(dwRecvLength, ==, 2);
|
||||
g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_P1_P2_ERROR);
|
||||
g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, 0x88);
|
||||
|
||||
vreader_free(reader); /* get by id ref */
|
||||
}
|
||||
|
||||
void check_login_count(void)
|
||||
{
|
||||
VReader *reader = vreader_get_reader_by_id(0);
|
||||
VReaderStatus status;
|
||||
int dwRecvLength = APDUBufSize;
|
||||
uint8_t pbRecvBuffer[APDUBufSize];
|
||||
uint8_t login[] = {
|
||||
/* VERIFY [p1,p2=0 ] [Lc] */
|
||||
0x00, 0x20, 0x00, 0x00, 0x00
|
||||
};
|
||||
g_assert_nonnull(reader);
|
||||
|
||||
/* Skip the HW tests without physical card */
|
||||
if (isHWTests() && vreader_card_is_present(reader) != VREADER_OK) {
|
||||
vreader_free(reader);
|
||||
g_test_skip("No physical card found");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get login count */
|
||||
status = vreader_xfr_bytes(reader,
|
||||
login, sizeof(login),
|
||||
pbRecvBuffer, &dwRecvLength);
|
||||
g_assert_cmpint(status, ==, VREADER_OK);
|
||||
/* NSS does not know how to do this yet */
|
||||
g_assert_cmphex(pbRecvBuffer[0], ==, VCARD7816_SW1_P1_P2_ERROR);
|
||||
g_assert_cmphex(pbRecvBuffer[1], ==, 0x88);
|
||||
|
||||
/* P1 = 0x01 is invalid */
|
||||
login[2] = 0x01;
|
||||
status = vreader_xfr_bytes(reader,
|
||||
login, sizeof(login),
|
||||
pbRecvBuffer, &dwRecvLength);
|
||||
g_assert_cmpint(status, ==, VREADER_OK);
|
||||
g_assert_cmphex(pbRecvBuffer[0], ==, VCARD7816_SW1_P1_P2_ERROR);
|
||||
g_assert_cmphex(pbRecvBuffer[1], ==, 0x00);
|
||||
|
||||
vreader_free(reader);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
isHWTests(void)
|
||||
{
|
||||
return hw_tests;
|
||||
}
|
||||
|
||||
void
|
||||
setHWTests(int new_value)
|
||||
{
|
||||
hw_tests = new_value;
|
||||
}
|
||||
|
||||
int
|
||||
getBits(void)
|
||||
{
|
||||
return key_bits;
|
||||
}
|
||||
|
||||
|
||||
/* vim: set ts=4 sw=4 tw=0 noet expandtab: */
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Shared test functions for libCACard
|
||||
*
|
||||
* Copyright 2018 Red Hat, Inc.
|
||||
*
|
||||
* Author: Jakub Jelen <jjelen@redhat.com>
|
||||
*
|
||||
* This code is licensed under the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
#ifndef _TESTS_COMMON
|
||||
#define _TESTS_COMMON
|
||||
|
||||
#include "libcacard.h"
|
||||
|
||||
#define APDUBufSize 270
|
||||
|
||||
enum {
|
||||
TEST_PKI = 1,
|
||||
TEST_CCC = 2,
|
||||
TEST_ACA = 3,
|
||||
TEST_GENERIC = 4,
|
||||
TEST_EMPTY_BUFFER = 5,
|
||||
TEST_EMPTY = 6,
|
||||
TEST_PASSTHROUGH = 7,
|
||||
};
|
||||
|
||||
void select_coid_good(VReader *reader, unsigned char *coid);
|
||||
void select_coid_bad(VReader *reader, unsigned char *coid);
|
||||
|
||||
int select_aid_response(VReader *reader, unsigned char *aid,
|
||||
unsigned int aid_len, int response);
|
||||
void select_aid(VReader *reader, unsigned char *aid, unsigned int aid_len);
|
||||
void select_applet(VReader *reader, int type);
|
||||
|
||||
void get_properties_coid(VReader *reader, const unsigned char coid[2], int object_type);
|
||||
void get_properties(VReader *reader, int object_type);
|
||||
|
||||
void read_buffer(VReader *reader, uint8_t type, int object_type);
|
||||
|
||||
void do_sign(VReader *reader, int parts);
|
||||
|
||||
void test_empty_applets(void);
|
||||
|
||||
void test_get_response(void);
|
||||
|
||||
void check_login_count(void);
|
||||
|
||||
int isHWTests(void);
|
||||
void setHWTests(int);
|
||||
|
||||
int getBits(void);
|
||||
|
||||
#endif /* _TESTS_COMMON */
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,371 @@
|
|||
/*
|
||||
* Test mirroring of CAC smart card
|
||||
*
|
||||
* Copyright 2018 Red Hat, Inc.
|
||||
*
|
||||
* Author: Jakub Jelen <jjelen@redhat.com>
|
||||
*
|
||||
* This code is licensed under the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include <glib.h>
|
||||
#include <string.h>
|
||||
#include "libcacard.h"
|
||||
#include "simpletlv.h"
|
||||
#include "common.h"
|
||||
|
||||
#define ARGS "db=\"sql:%s\" use_hw=removable"
|
||||
#define LOGIN_PIN "77777777"
|
||||
|
||||
static GMainLoop *loop;
|
||||
static GThread *thread;
|
||||
static guint nreaders;
|
||||
static GMutex mutex;
|
||||
static GCond cond;
|
||||
|
||||
static gpointer
|
||||
events_thread(gpointer arg)
|
||||
{
|
||||
unsigned int reader_id;
|
||||
VEvent *event;
|
||||
|
||||
while (1) {
|
||||
event = vevent_wait_next_vevent();
|
||||
if (event == NULL || event->type == VEVENT_LAST) {
|
||||
vevent_delete(event);
|
||||
break;
|
||||
}
|
||||
reader_id = vreader_get_id(event->reader);
|
||||
if (reader_id == VSCARD_UNDEFINED_READER_ID) {
|
||||
g_mutex_lock(&mutex);
|
||||
vreader_set_id(event->reader, nreaders++);
|
||||
g_cond_signal(&cond);
|
||||
g_mutex_unlock(&mutex);
|
||||
reader_id = vreader_get_id(event->reader);
|
||||
}
|
||||
switch (event->type) {
|
||||
case VEVENT_READER_INSERT:
|
||||
case VEVENT_READER_REMOVE:
|
||||
case VEVENT_CARD_INSERT:
|
||||
case VEVENT_CARD_REMOVE:
|
||||
break;
|
||||
case VEVENT_LAST:
|
||||
default:
|
||||
g_warn_if_reached();
|
||||
break;
|
||||
}
|
||||
vevent_delete(event);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void libcacard_init(void)
|
||||
{
|
||||
VCardEmulOptions *command_line_options = NULL;
|
||||
gchar *dbdir = g_test_build_filename(G_TEST_BUILT, "hwdb", NULL);
|
||||
gchar *args = g_strdup_printf(ARGS, dbdir);
|
||||
VCardEmulError ret;
|
||||
|
||||
thread = g_thread_new("test/events", events_thread, NULL);
|
||||
|
||||
command_line_options = vcard_emul_options(args);
|
||||
ret = vcard_emul_init(command_line_options);
|
||||
g_assert_cmpint(ret, ==, VCARD_EMUL_OK);
|
||||
|
||||
/* We test with real hardware */
|
||||
setHWTests(1);
|
||||
|
||||
/* Do not assume any specific reader name here */
|
||||
|
||||
g_mutex_lock(&mutex);
|
||||
while (nreaders < 2)
|
||||
g_cond_wait(&cond, &mutex);
|
||||
g_mutex_unlock(&mutex);
|
||||
|
||||
g_free(args);
|
||||
g_free(dbdir);
|
||||
}
|
||||
|
||||
static void test_list(void)
|
||||
{
|
||||
VReaderList *list = vreader_get_reader_list();
|
||||
VReaderListEntry *reader_entry;
|
||||
int cards = 0;
|
||||
|
||||
for (reader_entry = vreader_list_get_first(list); reader_entry;
|
||||
reader_entry = vreader_list_get_next(reader_entry)) {
|
||||
VReader *r = vreader_list_get_reader(reader_entry);
|
||||
vreader_id_t id;
|
||||
id = vreader_get_id(r);
|
||||
g_debug("%s: VReader name = %s, card = %d, %u", __func__, vreader_get_name(r), vreader_card_is_present(r), id);
|
||||
g_assert_cmpint(id, !=, VSCARD_UNDEFINED_READER_ID);
|
||||
if (vreader_card_is_present(r) == VREADER_OK) {
|
||||
cards++;
|
||||
}
|
||||
vreader_free(r);
|
||||
}
|
||||
vreader_list_delete(list);
|
||||
|
||||
if (cards == 0) {
|
||||
g_test_skip("No physical card found");
|
||||
return;
|
||||
}
|
||||
|
||||
g_assert_cmpint(cards, ==, 1);
|
||||
}
|
||||
|
||||
static void do_login(VReader *reader)
|
||||
{
|
||||
VReaderStatus status;
|
||||
int dwRecvLength = APDUBufSize;
|
||||
uint8_t pbRecvBuffer[APDUBufSize];
|
||||
uint8_t login[] = {
|
||||
/* VERIFY [p1,p2=0 ] [Lc] [pin 77777777 ] */
|
||||
0x00, 0x20, 0x00, 0x00, 0x08,
|
||||
//0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
int login_len, pin_len;
|
||||
|
||||
g_assert_nonnull(reader);
|
||||
|
||||
/* Set the pin from constant */
|
||||
pin_len = strlen(LOGIN_PIN);
|
||||
login[4] = pin_len;
|
||||
memcpy(&login[5], LOGIN_PIN, pin_len);
|
||||
login_len = 5 + pin_len;
|
||||
|
||||
status = vreader_xfr_bytes(reader,
|
||||
login, login_len,
|
||||
pbRecvBuffer, &dwRecvLength);
|
||||
g_assert_cmpint(status, ==, VREADER_OK);
|
||||
g_assert_cmphex(pbRecvBuffer[0], ==, VCARD7816_SW1_SUCCESS);
|
||||
g_assert_cmphex(pbRecvBuffer[1], ==, 0x00);
|
||||
}
|
||||
|
||||
static void test_passthrough_applets(void)
|
||||
{
|
||||
uint8_t applet_person[] = {
|
||||
/*Read Buffer OFFSET TYPE LENGTH */
|
||||
0xA0, 0x00, 0x00, 0x00, 0x79, 0x02, 0x00
|
||||
};
|
||||
uint8_t applet_personnel[] = {
|
||||
/*Read Buffer OFFSET TYPE LENGTH */
|
||||
0xA0, 0x00, 0x00, 0x00, 0x79, 0x02, 0x01
|
||||
};
|
||||
uint8_t person_coid[2] = {0x02, 0x00};
|
||||
uint8_t personnel_coid[2] = {0x02, 0x01};
|
||||
|
||||
VReader *reader = vreader_get_reader_by_id(0);
|
||||
|
||||
/* Skip the HW tests without physical card */
|
||||
if (vreader_card_is_present(reader) != VREADER_OK) {
|
||||
vreader_free(reader);
|
||||
g_test_skip("No physical card found");
|
||||
return;
|
||||
}
|
||||
|
||||
/* select the Person Instance applet A0000000790200 */
|
||||
select_aid(reader, applet_person, sizeof(applet_person));
|
||||
|
||||
/* get properties */
|
||||
get_properties_coid(reader, person_coid, TEST_GENERIC);
|
||||
|
||||
/* These objects requires a PIN to read the value buffer */
|
||||
do_login(reader);
|
||||
|
||||
/* get the TAG buffer length */
|
||||
read_buffer(reader, CAC_FILE_TAG, TEST_GENERIC);
|
||||
|
||||
/* get the VALUE buffer length */
|
||||
read_buffer(reader, CAC_FILE_VALUE, TEST_GENERIC);
|
||||
|
||||
|
||||
/* select the Personnel applet A0000000790201 */
|
||||
select_aid(reader, applet_personnel, sizeof(applet_personnel));
|
||||
|
||||
/* get properties */
|
||||
get_properties_coid(reader, personnel_coid, TEST_GENERIC);
|
||||
|
||||
/* get the TAG buffer */
|
||||
read_buffer(reader, CAC_FILE_TAG, TEST_GENERIC);
|
||||
|
||||
/* get the VALUE buffer */
|
||||
read_buffer(reader, CAC_FILE_VALUE, TEST_GENERIC);
|
||||
|
||||
vreader_free(reader); /* get by id ref */
|
||||
}
|
||||
|
||||
static void test_login(void)
|
||||
{
|
||||
VReader *reader = vreader_get_reader_by_id(0);
|
||||
|
||||
/* Skip the HW tests without physical card */
|
||||
if (vreader_card_is_present(reader) != VREADER_OK) {
|
||||
vreader_free(reader);
|
||||
g_test_skip("No physical card found");
|
||||
return;
|
||||
}
|
||||
|
||||
/* select the ACA */
|
||||
select_applet(reader, TEST_ACA);
|
||||
|
||||
do_login(reader);
|
||||
|
||||
vreader_free(reader); /* get by id ref */
|
||||
}
|
||||
|
||||
static void test_sign(void)
|
||||
{
|
||||
VReader *reader = vreader_get_reader_by_id(0);
|
||||
|
||||
/* Skip the HW tests without physical card */
|
||||
if (vreader_card_is_present(reader) != VREADER_OK) {
|
||||
vreader_free(reader);
|
||||
g_test_skip("No physical card found");
|
||||
return;
|
||||
}
|
||||
|
||||
/* select the ACA */
|
||||
select_applet(reader, TEST_ACA);
|
||||
|
||||
do_login(reader);
|
||||
|
||||
/* select the PKI */
|
||||
select_applet(reader, TEST_PKI);
|
||||
|
||||
/* get properties to figure out the key length */
|
||||
get_properties(reader, TEST_PKI);
|
||||
|
||||
do_sign(reader, 0);
|
||||
|
||||
/* test also multipart signatures */
|
||||
do_sign(reader, 1);
|
||||
|
||||
vreader_free(reader); /* get by id ref */
|
||||
}
|
||||
|
||||
/* Try to pass bad formatted PKCS#1.5 data and make sure the libcacard does not
|
||||
* crash while handling them
|
||||
*/
|
||||
static void test_sign_bad_data_x509(void)
|
||||
{
|
||||
VReader *reader = vreader_get_reader_by_id(0);
|
||||
VReaderStatus status;
|
||||
int dwRecvLength = APDUBufSize;
|
||||
uint8_t pbRecvBuffer[APDUBufSize];
|
||||
uint8_t sign[] = {
|
||||
/* VERIFY [p1,p2=0 ] [Lc ] [2048b keys: 256 bytes of non PKCS#1.5 data] */
|
||||
0x80, 0x42, 0x00, 0x00, 0x00, 0x01, 0x00,
|
||||
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/* ^--- the second byte of data should be 0x01 for signatures */
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0x00, 0x64, 0x61, 0x74, 0x61, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x20,
|
||||
0x28, 0x6d, 0x61, 0x78, 0x20, 0x31, 0x30, 0x30, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x29, 0x0a,
|
||||
0x00 /* <-- [Le] */
|
||||
};
|
||||
int sign_len = sizeof(sign);
|
||||
int key_bits = getBits();
|
||||
|
||||
g_assert_nonnull(reader);
|
||||
|
||||
/* Skip the HW tests without physical card */
|
||||
if (vreader_card_is_present(reader) != VREADER_OK) {
|
||||
vreader_free(reader);
|
||||
g_test_skip("No physical card found");
|
||||
return;
|
||||
}
|
||||
|
||||
/* run the actual test */
|
||||
|
||||
key_bits = getBits();
|
||||
/* Adjust the buffers to match the key lengths, if already retrieved */
|
||||
if (key_bits && key_bits < 2048) {
|
||||
int payload_len = key_bits/8; /* RSA signature has the same length as the key */
|
||||
sign[4] = payload_len; /* less than 2048b will fit the length into one byte */
|
||||
sign[5] = 0x00; /* PKCS#1.5 padding first byte */
|
||||
/*sign[6] = 0x01; <- this should be 0x01 for PKCS#1.5 signatures */
|
||||
memmove(&sign[6], &sign[sign_len - payload_len], payload_len - 1);
|
||||
sign_len = 5 /* [APDU header] */ + payload_len + 1 /* [Le] */;
|
||||
sign[sign_len-1] = 0x00; /* [Le] */
|
||||
}
|
||||
|
||||
dwRecvLength = APDUBufSize;
|
||||
status = vreader_xfr_bytes(reader,
|
||||
sign, sign_len,
|
||||
pbRecvBuffer, &dwRecvLength);
|
||||
g_assert_cmpint(status, ==, VREADER_OK);
|
||||
/* We expect one of the following results:
|
||||
* * VCARD7816_STATUS_ERROR_DATA_INVALID: Invalid data
|
||||
* * VCARD7816_STATUS_SUCCESS: Properly signed data
|
||||
*
|
||||
* we should not crash as with 2.5.3
|
||||
*/
|
||||
if (pbRecvBuffer[dwRecvLength-2] == VCARD7816_SW1_SUCCESS) {
|
||||
g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_SUCCESS);
|
||||
g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, 0x00);
|
||||
} else {
|
||||
g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_COMMAND_ERROR);
|
||||
g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, 0x84);
|
||||
}
|
||||
|
||||
/* no need to fetch the actual response */
|
||||
vreader_free(reader); /* get by id ref */
|
||||
}
|
||||
|
||||
static void libcacard_finalize(void)
|
||||
{
|
||||
VReader *reader = vreader_get_reader_by_id(0);
|
||||
|
||||
/* This probably supposed to be a event that terminates the loop */
|
||||
vevent_queue_vevent(vevent_new(VEVENT_LAST, reader, NULL));
|
||||
|
||||
/* join */
|
||||
g_thread_join(thread);
|
||||
|
||||
/* Clean up */
|
||||
vreader_free(reader);
|
||||
vreader_free(reader);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ret;
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
|
||||
loop = g_main_loop_new(NULL, TRUE);
|
||||
|
||||
libcacard_init();
|
||||
|
||||
g_test_add_func("/hw-tests/list", test_list);
|
||||
g_test_add_func("/hw-tests/passthrough-applet", test_passthrough_applets);
|
||||
g_test_add_func("/hw-tests/check-login-count", check_login_count);
|
||||
g_test_add_func("/hw-tests/login", test_login);
|
||||
g_test_add_func("/hw-tests/sign", test_sign);
|
||||
g_test_add_func("/hw-tests/sign-bad-data", test_sign_bad_data_x509);
|
||||
g_test_add_func("/hw-tests/empty-applets", test_empty_applets);
|
||||
g_test_add_func("/hw-tests/get-response", test_get_response);
|
||||
|
||||
ret = g_test_run();
|
||||
|
||||
libcacard_finalize();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* vim: set ts=4 sw=4 tw=0 noet expandtab: */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,93 @@
|
|||
#/bin/bash
|
||||
|
||||
SRCDIR=`dirname "$0"`
|
||||
NSSDB=hwdb
|
||||
CONF=softhsm2.conf
|
||||
SOPIN="12345678"
|
||||
PIN="77777777"
|
||||
export GNUTLS_PIN=$PIN
|
||||
|
||||
P11LIB=/usr/lib64/pkcs11/libsofthsm2.so
|
||||
|
||||
generate_cert() {
|
||||
TYPE="$1"
|
||||
ID="$2"
|
||||
LABEL="$3"
|
||||
|
||||
# Generate key pair
|
||||
pkcs11-tool --keypairgen --key-type="$TYPE" --login --pin=$PIN \
|
||||
--module="$P11LIB" --label="$LABEL" --id=$ID
|
||||
|
||||
if [[ "$?" -ne "0" ]]; then
|
||||
echo "Couldn't generate $TYPE key pair"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# check type value for the PKCS#11 URI (RHEL7 is using old "object-type")
|
||||
TYPE_KEY="type"
|
||||
p11tool --list-all --provider="$P11LIB" --login | grep "object-type" && \
|
||||
TYPE_KEY="object-type"
|
||||
|
||||
# Generate certificate
|
||||
certtool --generate-self-signed --outfile="$TYPE.cert" --template=$SRCDIR/cert.cfg \
|
||||
--provider="$P11LIB" --load-privkey "pkcs11:object=$LABEL;$TYPE_KEY=private" \
|
||||
--load-pubkey "pkcs11:object=$LABEL;$TYPE_KEY=public"
|
||||
# convert to DER:
|
||||
openssl x509 -inform PEM -outform DER -in "$TYPE.cert" -out "$TYPE.cert.der"
|
||||
# Write certificate
|
||||
pkcs11-tool --write-object "$TYPE.cert.der" --type=cert --id=$ID \
|
||||
--label="$LABEL" --module="$P11LIB"
|
||||
|
||||
rm "$TYPE.cert" "$TYPE.cert.der"
|
||||
|
||||
p11tool --login --provider="$P11LIB" --list-all
|
||||
}
|
||||
|
||||
# Check requirements
|
||||
if [ ! -f $(which pkcs11-tool) ]; then
|
||||
echo "ERROR: Need 'opensc' package to run tests"
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -f $(which p11tool) -o ! -f $(which certtool) ]; then
|
||||
echo "ERROR: Need 'gnutls-utils' package to run tests"
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -f $(which modutil) ]; then
|
||||
echo "ERROR: Need 'nss-tools' package to run tests"
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -f $(which openssl) ]; then
|
||||
echo "ERROR: Need 'openssl' package to run tests"
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -f $(which softhsm2-util) ]; then
|
||||
echo "ERROR: Need 'softhsm' package to run tests"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
|
||||
export SOFTHSM2_CONF="$CONF"
|
||||
# SoftHSM configuration file
|
||||
if [ ! -f "$CONF" ]; then
|
||||
echo "directories.tokendir = `pwd`/tokens/" > $CONF
|
||||
echo "slots.removable = true" >> $CONF
|
||||
fi
|
||||
|
||||
# SoftHSM configuration directory
|
||||
if [ ! -d "tokens" ]; then
|
||||
mkdir "tokens"
|
||||
|
||||
# Init token
|
||||
softhsm2-util --init-token --slot 0 --label "SC test" --so-pin="$SOPIN" --pin="$PIN"
|
||||
|
||||
# Generate 1024b RSA Key pair
|
||||
generate_cert "RSA:1024" "01" "RSA_auth"
|
||||
generate_cert "RSA:1024" "02" "RSA_sign"
|
||||
fi
|
||||
# NSS DB
|
||||
if [ ! -d "$NSSDB" ]; then
|
||||
mkdir "$NSSDB"
|
||||
modutil -create -dbdir "sql:$NSSDB" -force
|
||||
modutil -add "SoftHSM PKCS#11" -dbdir "sql:$NSSDB" -libfile "$P11LIB" -force
|
||||
fi
|
|
@ -0,0 +1,375 @@
|
|||
#include <glib.h>
|
||||
#include <string.h>
|
||||
#include "simpletlv.h"
|
||||
|
||||
static GMainLoop *loop;
|
||||
|
||||
/* Test that length estimations are sane */
|
||||
static void test_length_simple(void)
|
||||
{
|
||||
size_t length = 0;
|
||||
unsigned char simple_value[] = "\x10\x11";
|
||||
unsigned char long_value[256] = "Long data value";
|
||||
static struct simpletlv_member simple[1] = {
|
||||
{0x25, 2, {/*.value = simple_value*/}, SIMPLETLV_TYPE_LEAF}
|
||||
};
|
||||
simple[0].value.value = simple_value;
|
||||
|
||||
/* Simple short value to TLV */
|
||||
length = simpletlv_get_length(simple, 1, SIMPLETLV_BOTH);
|
||||
g_assert_cmpint(length, ==, 4);
|
||||
|
||||
/* Simple short value to TL */
|
||||
length = simpletlv_get_length(simple, 1, SIMPLETLV_TL);
|
||||
g_assert_cmpint(length, ==, 2);
|
||||
|
||||
/* Simple short value to V */
|
||||
length = simpletlv_get_length(simple, 1, SIMPLETLV_VALUE);
|
||||
g_assert_cmpint(length, ==, 2);
|
||||
|
||||
|
||||
/* Prepare long value */
|
||||
simple[0].value.value = long_value;
|
||||
simple[0].length = 256;
|
||||
|
||||
|
||||
/* Simple long value to TLV */
|
||||
length = simpletlv_get_length(simple, 1, SIMPLETLV_BOTH);
|
||||
g_assert_cmpint(length, ==, 260);
|
||||
|
||||
/* Simple long value to TL */
|
||||
length = simpletlv_get_length(simple, 1, SIMPLETLV_TL);
|
||||
g_assert_cmpint(length, ==, 4);
|
||||
|
||||
/* Simple long value to V */
|
||||
length = simpletlv_get_length(simple, 1, SIMPLETLV_VALUE);
|
||||
g_assert_cmpint(length, ==, 256);
|
||||
}
|
||||
|
||||
static void test_length_nested(void)
|
||||
{
|
||||
int length = 0;
|
||||
unsigned char simple_value[] = "\x12\x14";
|
||||
static struct simpletlv_member simple[1] = {
|
||||
{0x25, 2, {/*.value = simple_value*/}, SIMPLETLV_TYPE_LEAF}
|
||||
};
|
||||
static struct simpletlv_member nested[1] = {
|
||||
{0x72, 1, {/*.child = simple*/}, SIMPLETLV_TYPE_COMPOUND}
|
||||
};
|
||||
simple[0].value.value = simple_value;
|
||||
nested[0].value.child = simple;
|
||||
|
||||
/* Simple short value to TLV */
|
||||
length = simpletlv_get_length(nested, 1, SIMPLETLV_BOTH);
|
||||
g_assert_cmpint(length, ==, 6);
|
||||
|
||||
/* Nested structures do not support splitting TL and V buffers ?? */
|
||||
/* Simple short value to TL */
|
||||
length = simpletlv_get_length(nested, 1, SIMPLETLV_TL);
|
||||
g_assert_cmpint(length, ==, -1);
|
||||
|
||||
/* Simple short value to V */
|
||||
length = simpletlv_get_length(nested, 1, SIMPLETLV_VALUE);
|
||||
g_assert_cmpint(length, ==, -1);
|
||||
}
|
||||
|
||||
static void test_length_skipped(void)
|
||||
{
|
||||
size_t length = 0;
|
||||
unsigned char simple_value[] = "\x12\x14";
|
||||
unsigned char simple_value2[] = "\x16\x18";
|
||||
static struct simpletlv_member simple[2] = {
|
||||
{0x25, 2, {/*.value = simple_value*/}, SIMPLETLV_TYPE_LEAF},
|
||||
{0x30, 2, {/*.value = simple_value2*/}, SIMPLETLV_TYPE_NONE}
|
||||
};
|
||||
simple[0].value.value = simple_value;
|
||||
simple[1].value.value = simple_value2;
|
||||
|
||||
/* Simple short value to TLV */
|
||||
length = simpletlv_get_length(simple, 2, SIMPLETLV_BOTH);
|
||||
g_assert_cmpint(length, ==, 4);
|
||||
|
||||
/* Simple short value to TL */
|
||||
length = simpletlv_get_length(simple, 2, SIMPLETLV_TL);
|
||||
g_assert_cmpint(length, ==, 2);
|
||||
|
||||
/* Simple short value to V */
|
||||
length = simpletlv_get_length(simple, 2, SIMPLETLV_VALUE);
|
||||
g_assert_cmpint(length, ==, 2);
|
||||
}
|
||||
|
||||
/* Test that we can encode arbitrary data into Simple TLV */
|
||||
static void test_encode_simple(void)
|
||||
{
|
||||
unsigned char *result = NULL;
|
||||
int result_len = 0;
|
||||
unsigned char simple_value[] = "\x10\x11";
|
||||
unsigned char simple_encoded[] = "\x25\x02\x10\x11";
|
||||
unsigned char long_value[256] = "Long data value";
|
||||
unsigned char long_encoded[261] = "\x25\xFF\x00\x01Long data value";
|
||||
static struct simpletlv_member simple[1] = {
|
||||
{0x25, 2, {/*.value = simple_value*/}, SIMPLETLV_TYPE_LEAF}
|
||||
};
|
||||
simple[0].value.value = simple_value;
|
||||
|
||||
/* Encode simple short TLV with automatic allocation */
|
||||
result = NULL;
|
||||
result_len = simpletlv_encode(simple, 1, &result, 0, NULL);
|
||||
g_assert_cmpmem(result, result_len, simple_encoded, 4);
|
||||
g_free(result);
|
||||
|
||||
/* Encode simple short TLV with pre-allocated buffer (long enough) */
|
||||
result = g_malloc(10);
|
||||
result_len = simpletlv_encode(simple, 1, &result, 10, NULL);
|
||||
g_assert_cmpmem(result, result_len, simple_encoded, 4);
|
||||
g_free(result);
|
||||
|
||||
/* Encode simple short TLV with pre-allocated buffer (too short) */
|
||||
result = g_malloc(2);
|
||||
result_len = simpletlv_encode(simple, 1, &result, 2, NULL);
|
||||
g_assert_cmpint(result_len, ==, -1);
|
||||
g_free(result);
|
||||
|
||||
/* Encode only TL part */
|
||||
result = NULL;
|
||||
result_len = simpletlv_encode_tl(simple, 1, &result, 0, NULL);
|
||||
g_assert_cmpmem(result, result_len, "\x25\x02", 2);
|
||||
g_free(result);
|
||||
|
||||
/* Encode only VALUE part (equals to the value itself) */
|
||||
result = NULL;
|
||||
result_len = simpletlv_encode_val(simple, 1, &result, 0, NULL);
|
||||
g_assert_cmpmem(result, result_len, simple_value, 2);
|
||||
g_free(result);
|
||||
|
||||
|
||||
/* Prepare long value */
|
||||
memset(long_value+15, '\x00', 256-15);
|
||||
memset(long_encoded+19, '\x00', 256-15);
|
||||
simple[0].value.value = long_value;
|
||||
simple[0].length = 256;
|
||||
|
||||
|
||||
/* Encode simple long TLV with automatic allocation */
|
||||
result = NULL;
|
||||
result_len = simpletlv_encode(simple, 1, &result, 0, NULL);
|
||||
g_assert_cmpmem(result, result_len, long_encoded, 260);
|
||||
g_free(result);
|
||||
|
||||
/* Encode simple long TLV with pre-allocated buffer (long enough) */
|
||||
result = g_malloc(300);
|
||||
result_len = simpletlv_encode(simple, 1, &result, 300, NULL);
|
||||
g_assert_cmpmem(result, result_len, long_encoded, 260);
|
||||
g_free(result);
|
||||
|
||||
}
|
||||
|
||||
/* Test that we can encode nested data into Simple TLV */
|
||||
static void test_encode_nested(void)
|
||||
{
|
||||
unsigned char *result = NULL;
|
||||
int result_len = 0;
|
||||
unsigned char simple_value[] = "\x12\x14";
|
||||
unsigned char encoded[] = "\x72\x04\x25\x02\x12\x14";
|
||||
static struct simpletlv_member simple[1] = {
|
||||
{0x25, 2, {/*.value = simple_value*/}, SIMPLETLV_TYPE_LEAF}
|
||||
};
|
||||
static struct simpletlv_member nested[1] = {
|
||||
{0x72, 1, {/*.child = simple*/}, SIMPLETLV_TYPE_COMPOUND}
|
||||
};
|
||||
simple[0].value.value = simple_value;
|
||||
nested[0].value.child = simple;
|
||||
|
||||
/* Encode nested TLV with automatic allocation */
|
||||
result = NULL;
|
||||
result_len = simpletlv_encode(nested, 1, &result, 0, NULL);
|
||||
g_assert_cmpmem(result, result_len, encoded, 6);
|
||||
g_free(result);
|
||||
|
||||
/* Encode nested TLV with pre-allocated buffer (long enough) */
|
||||
result = g_malloc(10);
|
||||
result_len = simpletlv_encode(nested, 1, &result, 10, NULL);
|
||||
g_assert_cmpmem(result, result_len, encoded, 6);
|
||||
g_free(result);
|
||||
|
||||
/* Encode simple short TLV with pre-allocated buffer (too short) */
|
||||
result = g_malloc(4);
|
||||
result_len = simpletlv_encode(nested, 1, &result, 4, NULL);
|
||||
g_assert_cmpint(result_len, ==, -1);
|
||||
g_free(result);
|
||||
|
||||
/* Encode only TL part */
|
||||
result = NULL;
|
||||
result_len = simpletlv_encode_tl(nested, 1, &result, 0, NULL);
|
||||
g_assert_cmpint(result_len, ==, -1);
|
||||
|
||||
/* Encode only VALUE part (equals to the value itself) */
|
||||
result = NULL;
|
||||
result_len = simpletlv_encode_val(nested, 1, &result, 0, NULL);
|
||||
g_assert_cmpint(result_len, ==, -1);
|
||||
}
|
||||
|
||||
static void test_encode_skipped(void)
|
||||
{
|
||||
unsigned char *result = NULL;
|
||||
size_t result_len = 0;
|
||||
unsigned char simple_value[] = "\x12\x14";
|
||||
unsigned char simple_value2[] = "\x16\x18";
|
||||
static struct simpletlv_member simple[2] = {
|
||||
{0x25, 2, {/*.value = simple_value*/}, SIMPLETLV_TYPE_LEAF},
|
||||
{0x30, 2, {/*.value = simple_value2*/}, SIMPLETLV_TYPE_NONE}
|
||||
};
|
||||
unsigned char encoded[] = "\x25\x02\x12\x14";
|
||||
|
||||
simple[0].value.value = simple_value;
|
||||
simple[1].value.value = simple_value2;
|
||||
|
||||
/* Simple short value to TLV */
|
||||
result = NULL;
|
||||
result_len = simpletlv_encode(simple, 2, &result, 0, NULL);
|
||||
g_assert_cmpmem(result, result_len, encoded, 4);
|
||||
g_free(result);
|
||||
|
||||
/* Simple short value to TL */
|
||||
result = NULL;
|
||||
result_len = simpletlv_encode_tl(simple, 2, &result, 0, NULL);
|
||||
g_assert_cmpmem(result, result_len, "\x25\x02", 2);
|
||||
g_free(result);
|
||||
|
||||
/* Simple short value to V */
|
||||
result = NULL;
|
||||
result_len = simpletlv_encode_val(simple, 2, &result, 0, NULL);
|
||||
g_assert_cmpmem(result, result_len, "\x12\x14", 2);
|
||||
g_free(result);
|
||||
}
|
||||
|
||||
static void test_clone_simple(void)
|
||||
{
|
||||
unsigned char *result = NULL;
|
||||
size_t result_len = 0;
|
||||
unsigned char simple_value[] = "\x14\x18";
|
||||
unsigned char simple_value2[] = "\x64\x24\x44";
|
||||
static struct simpletlv_member simple[2] = {
|
||||
{0x13, 2, {/*.value = simple_value*/}, SIMPLETLV_TYPE_LEAF},
|
||||
{0xDD, 3, {/*.value = simple_value2*/}, SIMPLETLV_TYPE_LEAF}
|
||||
};
|
||||
unsigned char encoded[] = "\x13\x02\x14\x18\xDD\x03\x64\x24\x44";
|
||||
struct simpletlv_member *clone;
|
||||
|
||||
simple[0].value.value = simple_value;
|
||||
simple[1].value.value = simple_value2;
|
||||
|
||||
clone = simpletlv_clone(simple, 2);
|
||||
g_assert_nonnull(clone);
|
||||
|
||||
result = NULL;
|
||||
result_len = simpletlv_encode(clone, 2, &result, 0, NULL);
|
||||
g_assert_cmpmem(result, result_len, encoded, 9);
|
||||
g_free(result);
|
||||
simpletlv_free(clone, 2);
|
||||
}
|
||||
|
||||
static void test_parse_simple(void)
|
||||
{
|
||||
unsigned char data[] = "\x13\x02\x14\x18\xDD\x03\x64\x24\x44";
|
||||
size_t data_len = 9, tlv_len = 0;
|
||||
struct simpletlv_member *tlv;
|
||||
|
||||
tlv = simpletlv_parse(data, data_len, &tlv_len);
|
||||
g_assert_cmpint(tlv_len, ==, 2);
|
||||
|
||||
g_assert_cmpint(tlv[0].tag, ==, 0x13);
|
||||
g_assert_cmpint(tlv[0].length, ==, 0x02);
|
||||
g_assert_cmpint(tlv[0].type, ==, SIMPLETLV_TYPE_LEAF);
|
||||
g_assert_cmpmem(tlv[0].value.value, tlv[0].length, "\x14\x18", 2);
|
||||
|
||||
g_assert_cmpint(tlv[1].tag, ==, 0xDD);
|
||||
g_assert_cmpint(tlv[1].length, ==, 0x03);
|
||||
g_assert_cmpint(tlv[1].type, ==, SIMPLETLV_TYPE_LEAF);
|
||||
g_assert_cmpmem(tlv[1].value.value, tlv[1].length, "\x64\x24\x44", 3);
|
||||
|
||||
simpletlv_free(tlv, tlv_len);
|
||||
}
|
||||
|
||||
/* If there is some gargabe in the end of buffer, we would like to
|
||||
* return at least what was properly parsed, rahter than to fail
|
||||
* hard.
|
||||
* Also makes sure we do not leak memory or crash on bad data.
|
||||
*
|
||||
* This is an issue for OpenSC at this moment, which fails to encode
|
||||
* last TLV into the compound buffer for data objects.
|
||||
*/
|
||||
static void test_parse_last_bad(void)
|
||||
{
|
||||
size_t data_len = 9;
|
||||
unsigned char data[] = "\x13\x02\x14\x18\x28\x13\x64\x24\x44";
|
||||
/* this length is oveflow -^ */
|
||||
size_t data2_len = 7;
|
||||
unsigned char data2[] = "\x13\x02\x14\x18\x28\xFF\xFF";
|
||||
/* this length is missing last byte -^ */
|
||||
size_t data3_len = 5;
|
||||
unsigned char data3[] = "\x13\x02\x14\x18\x12";
|
||||
/* this length is missed completely -^ */
|
||||
size_t tlv_len = 0;
|
||||
struct simpletlv_member *tlv;
|
||||
|
||||
/* Test the overflow length in the last member */
|
||||
tlv = simpletlv_parse(data, data_len, &tlv_len);
|
||||
g_assert_cmpint(tlv_len, ==, 1);
|
||||
|
||||
g_assert_cmpint(tlv[0].tag, ==, 0x13);
|
||||
g_assert_cmpint(tlv[0].length, ==, 0x02);
|
||||
g_assert_cmpint(tlv[0].type, ==, SIMPLETLV_TYPE_LEAF);
|
||||
g_assert_cmpmem(tlv[0].value.value, tlv[0].length, "\x14\x18", 2);
|
||||
|
||||
simpletlv_free(tlv, tlv_len);
|
||||
|
||||
/* Test the truncated length item in last member */
|
||||
tlv = simpletlv_parse(data2, data2_len, &tlv_len);
|
||||
g_assert_cmpint(tlv_len, ==, 1);
|
||||
|
||||
g_assert_cmpint(tlv[0].tag, ==, 0x13);
|
||||
g_assert_cmpint(tlv[0].length, ==, 0x02);
|
||||
g_assert_cmpint(tlv[0].type, ==, SIMPLETLV_TYPE_LEAF);
|
||||
g_assert_cmpmem(tlv[0].value.value, tlv[0].length, "\x14\x18", 2);
|
||||
|
||||
simpletlv_free(tlv, tlv_len);
|
||||
|
||||
/* Test the missing length item in last member */
|
||||
tlv = simpletlv_parse(data3, data3_len, &tlv_len);
|
||||
g_assert_cmpint(tlv_len, ==, 1);
|
||||
|
||||
g_assert_cmpint(tlv[0].tag, ==, 0x13);
|
||||
g_assert_cmpint(tlv[0].length, ==, 0x02);
|
||||
g_assert_cmpint(tlv[0].type, ==, SIMPLETLV_TYPE_LEAF);
|
||||
g_assert_cmpmem(tlv[0].value.value, tlv[0].length, "\x14\x18", 2);
|
||||
|
||||
simpletlv_free(tlv, tlv_len);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ret;
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
|
||||
loop = g_main_loop_new(NULL, TRUE);
|
||||
|
||||
g_test_add_func("/simpletlv/length/simple", test_length_simple);
|
||||
g_test_add_func("/simpletlv/length/nested", test_length_nested);
|
||||
g_test_add_func("/simpletlv/length/skipped", test_length_skipped);
|
||||
g_test_add_func("/simpletlv/encode/simple", test_encode_simple);
|
||||
g_test_add_func("/simpletlv/encode/nested", test_encode_nested);
|
||||
g_test_add_func("/simpletlv/encode/skipped", test_encode_skipped);
|
||||
g_test_add_func("/simpletlv/parse/simple", test_parse_simple);
|
||||
g_test_add_func("/simpletlv/parse/last_bad", test_parse_last_bad);
|
||||
g_test_add_func("/simpletlv/clone/simple", test_clone_simple);
|
||||
|
||||
ret = g_test_run();
|
||||
|
||||
g_main_loop_unref(loop);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* vim: set ts=4 sw=4 tw=0 noet expandtab: */
|
Loading…
Reference in New Issue