Compare commits
129 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 580cceb50e | |||
| b90b21f26b | |||
| 6d583cb5d8 | |||
| c776068cd2 | |||
| b8d80fcb35 | |||
| 32c2ccdd8c | |||
| 58784f7002 | |||
| f295e6c571 | |||
| b02825620a | |||
| 2327307b81 | |||
| 7c1ae363a8 | |||
| a18af36a03 | |||
| 526b2e8c53 | |||
| 73625233f6 | |||
| 08de7e33a4 | |||
| c871dfc998 | |||
| dccc5796b6 | |||
| 0994cc314e | |||
| 09b1f1f1db | |||
| e10417a907 | |||
| 8f75563920 | |||
| 2e00c7a4b1 | |||
| 72d8a0f512 | |||
| 130d6cdb63 | |||
| e2fd52190a | |||
| 7d8450e5ab | |||
| 95e95d2910 | |||
| 75aeff4acd | |||
| bebd3d2a98 | |||
| 0e2bf7e804 | |||
| f5a2c21f90 | |||
| 53d80c1474 | |||
| 9bed3bed3f | |||
| 1a094c1a09 | |||
| 4ed46deaf2 | |||
| 0724413eda | |||
| eb6dbb6953 | |||
| 5c5849dca7 | |||
| 048181ae7f | |||
| 8e3345a60f | |||
| 33a21b5089 | |||
| dd006382f5 | |||
| a34432ccf9 | |||
| f2804921a3 | |||
| 74577a6806 | |||
| 2076025208 | |||
| 77126ccf1f | |||
| 46b7d7c1a7 | |||
| 791a208afd | |||
| b832002c97 | |||
| 8a2f276003 | |||
| aab923e3cd | |||
| cc4bac3410 | |||
| 18d4aa6d7d | |||
| 046ec8dbf4 | |||
| 874b4556ef | |||
| c2aa5c0b9a | |||
| f745c49112 | |||
| 319d859107 | |||
| b97903f781 | |||
| a30697a28d | |||
| aad3212d22 | |||
| 3624bcb114 | |||
| 8fbc59a258 | |||
| 5a8da0022a | |||
| 6089ba6f40 | |||
| ee6e8a6fa3 | |||
| 42f5280a03 | |||
| affffaf134 | |||
| 6ab270fb1a | |||
| 2d0bed6b13 | |||
| c43134e36f | |||
| b9d23ddb87 | |||
| 205dedae4f | |||
| d70f15b5e8 | |||
| 6f63beb1fc | |||
| f7557c6ee7 | |||
| 0b598965b0 | |||
| 8325d347d6 | |||
| 0bdf801043 | |||
| 6e3b053372 | |||
| c6e72c5f28 | |||
| 986a42bcd1 | |||
| 0c6bab8640 | |||
| b99afd19f0 | |||
| 3d6dfabd8d | |||
| 67adcb59ed | |||
| db0ab55bef | |||
| 00b79d1a2f | |||
| f1517af09a | |||
| c0bf1515fd | |||
| 872089883c | |||
| 431755becd | |||
| 0a42b90390 | |||
| cd3ed2e450 | |||
| c929d39df1 | |||
| 1a5ef6c5a7 | |||
| ce3406b20f | |||
| 0d407db171 | |||
| 4eb751a218 | |||
| 0f44267ea1 | |||
| 9baea4494b | |||
| 41afbd1ced | |||
| f2d6921b74 | |||
| b690daa95f | |||
| deb3c25e51 | |||
| a520896325 | |||
| 707ed01059 | |||
| 6903c36157 | |||
| d0df422f9b | |||
| 882740f8a1 | |||
| 5043ef3c7d | |||
| cdd79a0935 | |||
| 776b4f4cec | |||
| 31bfd1b055 | |||
| 74838f9efc | |||
| 54ba81191e | |||
| 521ba9b124 | |||
| 3db69c2c2f | |||
| b2ff316e20 | |||
| 94f54c0638 | |||
| 9c8d062669 | |||
| ca482036c7 | |||
| d7821aa790 | |||
| 16cb1a8ec6 | |||
| 31ba9ebea4 | |||
| 8893c2f906 | |||
| 3520cb56d1 | |||
| 96444b4156 |
37
.gitignore
vendored
@ -1,46 +1,11 @@
|
|||||||
/*.bak
|
/*.bak
|
||||||
/*.lo
|
|
||||||
/*.o
|
/*.o
|
||||||
/*.orig
|
/*.orig
|
||||||
/*.rej
|
/*.rej
|
||||||
/*.tab.c
|
/*.tab.c
|
||||||
/*~
|
/*~
|
||||||
/.*.sw[nop]
|
/.*.sw[nop]
|
||||||
/.deps
|
|
||||||
/.dirstamp
|
/.dirstamp
|
||||||
/.gitignore
|
/.gitignore
|
||||||
/.libs
|
/_build
|
||||||
/GPATH
|
|
||||||
/GRTAGS
|
|
||||||
/GSYMS
|
|
||||||
/GTAGS
|
|
||||||
/ID
|
|
||||||
/Makefile
|
|
||||||
/Makefile.in
|
|
||||||
/TAGS
|
|
||||||
/_libs
|
|
||||||
/autom4te.cache
|
|
||||||
/config.cache
|
|
||||||
/config.h
|
|
||||||
/config.log
|
|
||||||
/config.lt
|
|
||||||
/config.status
|
|
||||||
/config.status.lineno
|
|
||||||
/configure
|
|
||||||
/configure.lineno
|
|
||||||
/intltool-extract.in
|
|
||||||
/intltool-merge.in
|
|
||||||
/intltool-update.in
|
|
||||||
/libtool
|
|
||||||
/po/*.gmo
|
|
||||||
/po/*.mo
|
|
||||||
/po/.intltool-merge-cache
|
|
||||||
/po/Makefile
|
|
||||||
/po/Makefile.in
|
|
||||||
/po/Makefile.in.in
|
|
||||||
/po/POTFILES
|
|
||||||
/po/fprintd.pot
|
|
||||||
/po/stamp-it
|
|
||||||
/so_locations
|
|
||||||
/stamp-h1
|
|
||||||
/tags
|
/tags
|
||||||
|
|||||||
108
.gitlab-ci.yml
@ -1,36 +1,98 @@
|
|||||||
image: fedora:rawhide
|
include:
|
||||||
|
- project: 'libfprint/libfprint'
|
||||||
|
ref: master
|
||||||
|
file: '/.gitlab-ci/libfprint-templates.yaml'
|
||||||
|
- project: 'wayland/ci-templates'
|
||||||
|
ref: master
|
||||||
|
file: '/templates/fedora.yml'
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
DEPENDENCIES: dbus-glib-devel pam-devel polkit-devel
|
extends: .libfprint_common_variables
|
||||||
gtk-doc meson intltool autoconf automake libtool
|
FEDORA_TAG: rawhide
|
||||||
gcc gcc-c++ glibc-devel make
|
FEDORA_VERSION: rawhide
|
||||||
DEPENDENCIES_STABLE: $DEPENDENCIES libfprint-devel
|
FEDORA_IMAGE: "$CI_REGISTRY/libfprint/$CI_PROJECT_NAME/fedora/$FEDORA_VERSION:$FEDORA_TAG"
|
||||||
DEPENDENCIES_DEV: $DEPENDENCIES git
|
DEPENDENCIES: dbus-glib-devel
|
||||||
# Sync'ed up with https://gitlab.freedesktop.org/libfprint/libfprint/blob/master/.gitlab-ci.yml
|
gcc
|
||||||
DEPENDENCIES_LIBFPRINT: libusb1-devel glib2-devel nss-devel pixman-devel systemd
|
gcovr
|
||||||
meson gtk-doc gcc gcc-c++ glibc-devel libX11-devel
|
gettext
|
||||||
libXv-devel
|
git
|
||||||
|
glibc-devel
|
||||||
|
gtk-doc
|
||||||
|
libfprint-devel
|
||||||
|
meson
|
||||||
|
pam-devel
|
||||||
|
polkit-devel
|
||||||
|
python3-dbusmock
|
||||||
|
python3-libpamtest
|
||||||
|
systemd-devel
|
||||||
|
|
||||||
build_stable:
|
image: "$FEDORA_IMAGE"
|
||||||
before_script:
|
|
||||||
- dnf update -y --nogpgcheck && dnf install -y --nogpgcheck $DEPENDENCIES_STABLE
|
|
||||||
script:
|
|
||||||
- ./autogen.sh --disable-dependency-tracking
|
|
||||||
- make
|
|
||||||
- make install
|
|
||||||
|
|
||||||
build_dev:
|
.install_libfprint_dev: &install_libfprint_dev
|
||||||
before_script:
|
before_script:
|
||||||
- dnf update -y --nogpgcheck && dnf install -y --nogpgcheck $DEPENDENCIES_LIBFPRINT $DEPENDENCIES_DEV
|
# Make sure we don't build or link against the system libfprint
|
||||||
|
- dnf remove -y libfprint-devel
|
||||||
- git clone https://gitlab.freedesktop.org/libfprint/libfprint.git
|
- git clone https://gitlab.freedesktop.org/libfprint/libfprint.git
|
||||||
- cd libfprint
|
- cd libfprint
|
||||||
- meson . _build --prefix=/usr
|
- meson . _build --prefix=/usr -Ddrivers=virtual_image -Ddoc=false
|
||||||
- ninja -C _build
|
- ninja -C _build
|
||||||
- ninja -C _build install
|
- ninja -C _build install
|
||||||
- cd ..
|
- cd ..
|
||||||
# So we don't get error about this libfprint file
|
# So we don't get error about this libfprint file
|
||||||
- echo "libfprint/demo/gtk-libfprint-test.ui" >> po/POTFILES.skip
|
- echo "libfprint/demo/gtk-libfprint-test.ui" >> po/POTFILES.skip
|
||||||
|
|
||||||
|
build_stable:
|
||||||
|
except:
|
||||||
|
variables:
|
||||||
|
- $FPRINT_CRON_TASK == "BUILD_CI_IMAGES"
|
||||||
|
# FIXME: Stable builds will fail until libfprintv 2 reaches rawhide
|
||||||
|
allow_failure: true
|
||||||
script:
|
script:
|
||||||
- ./autogen.sh --disable-dependency-tracking
|
- meson _build
|
||||||
- make
|
- ninja -C _build -v
|
||||||
- make install
|
- ninja -C _build -v install
|
||||||
|
|
||||||
|
build_dev:
|
||||||
|
except:
|
||||||
|
variables:
|
||||||
|
- $FPRINT_CRON_TASK == "BUILD_CI_IMAGES"
|
||||||
|
<<: *install_libfprint_dev
|
||||||
|
script:
|
||||||
|
- meson _build --werror -Dgtk_doc=true
|
||||||
|
- ninja -C _build -v
|
||||||
|
- ninja -C _build -v install
|
||||||
|
artifacts:
|
||||||
|
name: log
|
||||||
|
when: on_failure
|
||||||
|
paths:
|
||||||
|
- _build/meson-logs/*.txt
|
||||||
|
|
||||||
|
test_dev:
|
||||||
|
except:
|
||||||
|
variables:
|
||||||
|
- $FPRINT_CRON_TASK == "BUILD_CI_IMAGES"
|
||||||
|
stage: test
|
||||||
|
<<: *install_libfprint_dev
|
||||||
|
script:
|
||||||
|
- meson _build -Db_coverage=true
|
||||||
|
- meson test -C _build --verbose --no-stdsplit --timeout-multiplier 3
|
||||||
|
- ninja -C _build coverage
|
||||||
|
- cat _build/meson-logs/coverage.txt
|
||||||
|
artifacts:
|
||||||
|
name: log-and-coverage
|
||||||
|
when: always
|
||||||
|
paths:
|
||||||
|
- _build/meson-logs
|
||||||
|
|
||||||
|
# CONTAINERS creation stage
|
||||||
|
container_fedora_build:
|
||||||
|
extends: .fedora@container-build
|
||||||
|
only:
|
||||||
|
variables:
|
||||||
|
- $FPRINT_CRON_TASK == "BUILD_CI_IMAGES"
|
||||||
|
variables:
|
||||||
|
GIT_STRATEGY: none # no need to pull the whole tree for rebuilding the image
|
||||||
|
# a list of packages to install
|
||||||
|
FEDORA_RPMS:
|
||||||
|
$DEPENDENCIES
|
||||||
|
$LIBFPRINT_DEPENDENCIES
|
||||||
|
|||||||
237
INSTALL
@ -1,237 +0,0 @@
|
|||||||
Installation Instructions
|
|
||||||
*************************
|
|
||||||
|
|
||||||
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
|
|
||||||
2006, 2007 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
This file is free documentation; the Free Software Foundation gives
|
|
||||||
unlimited permission to copy, distribute and modify it.
|
|
||||||
|
|
||||||
Basic Installation
|
|
||||||
==================
|
|
||||||
|
|
||||||
Briefly, the shell commands `./configure; make; make install' should
|
|
||||||
configure, build, and install this package. The following
|
|
||||||
more-detailed instructions are generic; see the `README' file for
|
|
||||||
instructions specific to this package.
|
|
||||||
|
|
||||||
The `configure' shell script attempts to guess correct values for
|
|
||||||
various system-dependent variables used during compilation. It uses
|
|
||||||
those values to create a `Makefile' in each directory of the package.
|
|
||||||
It may also create one or more `.h' files containing system-dependent
|
|
||||||
definitions. Finally, it creates a shell script `config.status' that
|
|
||||||
you can run in the future to recreate the current configuration, and a
|
|
||||||
file `config.log' containing compiler output (useful mainly for
|
|
||||||
debugging `configure').
|
|
||||||
|
|
||||||
It can also use an optional file (typically called `config.cache'
|
|
||||||
and enabled with `--cache-file=config.cache' or simply `-C') that saves
|
|
||||||
the results of its tests to speed up reconfiguring. Caching is
|
|
||||||
disabled by default to prevent problems with accidental use of stale
|
|
||||||
cache files.
|
|
||||||
|
|
||||||
If you need to do unusual things to compile the package, please try
|
|
||||||
to figure out how `configure' could check whether to do them, and mail
|
|
||||||
diffs or instructions to the address given in the `README' so they can
|
|
||||||
be considered for the next release. If you are using the cache, and at
|
|
||||||
some point `config.cache' contains results you don't want to keep, you
|
|
||||||
may remove or edit it.
|
|
||||||
|
|
||||||
The file `configure.ac' (or `configure.in') is used to create
|
|
||||||
`configure' by a program called `autoconf'. You need `configure.ac' if
|
|
||||||
you want to change it or regenerate `configure' using a newer version
|
|
||||||
of `autoconf'.
|
|
||||||
|
|
||||||
The simplest way to compile this package is:
|
|
||||||
|
|
||||||
1. `cd' to the directory containing the package's source code and type
|
|
||||||
`./configure' to configure the package for your system.
|
|
||||||
|
|
||||||
Running `configure' might take a while. While running, it prints
|
|
||||||
some messages telling which features it is checking for.
|
|
||||||
|
|
||||||
2. Type `make' to compile the package.
|
|
||||||
|
|
||||||
3. Optionally, type `make check' to run any self-tests that come with
|
|
||||||
the package.
|
|
||||||
|
|
||||||
4. Type `make install' to install the programs and any data files and
|
|
||||||
documentation.
|
|
||||||
|
|
||||||
5. You can remove the program binaries and object files from the
|
|
||||||
source code directory by typing `make clean'. To also remove the
|
|
||||||
files that `configure' created (so you can compile the package for
|
|
||||||
a different kind of computer), type `make distclean'. There is
|
|
||||||
also a `make maintainer-clean' target, but that is intended mainly
|
|
||||||
for the package's developers. If you use it, you may have to get
|
|
||||||
all sorts of other programs in order to regenerate files that came
|
|
||||||
with the distribution.
|
|
||||||
|
|
||||||
6. Often, you can also type `make uninstall' to remove the installed
|
|
||||||
files again.
|
|
||||||
|
|
||||||
Compilers and Options
|
|
||||||
=====================
|
|
||||||
|
|
||||||
Some systems require unusual options for compilation or linking that the
|
|
||||||
`configure' script does not know about. Run `./configure --help' for
|
|
||||||
details on some of the pertinent environment variables.
|
|
||||||
|
|
||||||
You can give `configure' initial values for configuration parameters
|
|
||||||
by setting variables in the command line or in the environment. Here
|
|
||||||
is an example:
|
|
||||||
|
|
||||||
./configure CC=c99 CFLAGS=-g LIBS=-lposix
|
|
||||||
|
|
||||||
*Note Defining Variables::, for more details.
|
|
||||||
|
|
||||||
Compiling For Multiple Architectures
|
|
||||||
====================================
|
|
||||||
|
|
||||||
You can compile the package for more than one kind of computer at the
|
|
||||||
same time, by placing the object files for each architecture in their
|
|
||||||
own directory. To do this, you can use GNU `make'. `cd' to the
|
|
||||||
directory where you want the object files and executables to go and run
|
|
||||||
the `configure' script. `configure' automatically checks for the
|
|
||||||
source code in the directory that `configure' is in and in `..'.
|
|
||||||
|
|
||||||
With a non-GNU `make', it is safer to compile the package for one
|
|
||||||
architecture at a time in the source code directory. After you have
|
|
||||||
installed the package for one architecture, use `make distclean' before
|
|
||||||
reconfiguring for another architecture.
|
|
||||||
|
|
||||||
Installation Names
|
|
||||||
==================
|
|
||||||
|
|
||||||
By default, `make install' installs the package's commands under
|
|
||||||
`/usr/local/bin', include files under `/usr/local/include', etc. You
|
|
||||||
can specify an installation prefix other than `/usr/local' by giving
|
|
||||||
`configure' the option `--prefix=PREFIX'.
|
|
||||||
|
|
||||||
You can specify separate installation prefixes for
|
|
||||||
architecture-specific files and architecture-independent files. If you
|
|
||||||
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
|
|
||||||
PREFIX as the prefix for installing programs and libraries.
|
|
||||||
Documentation and other data files still use the regular prefix.
|
|
||||||
|
|
||||||
In addition, if you use an unusual directory layout you can give
|
|
||||||
options like `--bindir=DIR' to specify different values for particular
|
|
||||||
kinds of files. Run `configure --help' for a list of the directories
|
|
||||||
you can set and what kinds of files go in them.
|
|
||||||
|
|
||||||
If the package supports it, you can cause programs to be installed
|
|
||||||
with an extra prefix or suffix on their names by giving `configure' the
|
|
||||||
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
|
|
||||||
|
|
||||||
Optional Features
|
|
||||||
=================
|
|
||||||
|
|
||||||
Some packages pay attention to `--enable-FEATURE' options to
|
|
||||||
`configure', where FEATURE indicates an optional part of the package.
|
|
||||||
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
|
|
||||||
is something like `gnu-as' or `x' (for the X Window System). The
|
|
||||||
`README' should mention any `--enable-' and `--with-' options that the
|
|
||||||
package recognizes.
|
|
||||||
|
|
||||||
For packages that use the X Window System, `configure' can usually
|
|
||||||
find the X include and library files automatically, but if it doesn't,
|
|
||||||
you can use the `configure' options `--x-includes=DIR' and
|
|
||||||
`--x-libraries=DIR' to specify their locations.
|
|
||||||
|
|
||||||
Specifying the System Type
|
|
||||||
==========================
|
|
||||||
|
|
||||||
There may be some features `configure' cannot figure out automatically,
|
|
||||||
but needs to determine by the type of machine the package will run on.
|
|
||||||
Usually, assuming the package is built to be run on the _same_
|
|
||||||
architectures, `configure' can figure that out, but if it prints a
|
|
||||||
message saying it cannot guess the machine type, give it the
|
|
||||||
`--build=TYPE' option. TYPE can either be a short name for the system
|
|
||||||
type, such as `sun4', or a canonical name which has the form:
|
|
||||||
|
|
||||||
CPU-COMPANY-SYSTEM
|
|
||||||
|
|
||||||
where SYSTEM can have one of these forms:
|
|
||||||
|
|
||||||
OS KERNEL-OS
|
|
||||||
|
|
||||||
See the file `config.sub' for the possible values of each field. If
|
|
||||||
`config.sub' isn't included in this package, then this package doesn't
|
|
||||||
need to know the machine type.
|
|
||||||
|
|
||||||
If you are _building_ compiler tools for cross-compiling, you should
|
|
||||||
use the option `--target=TYPE' to select the type of system they will
|
|
||||||
produce code for.
|
|
||||||
|
|
||||||
If you want to _use_ a cross compiler, that generates code for a
|
|
||||||
platform different from the build platform, you should specify the
|
|
||||||
"host" platform (i.e., that on which the generated programs will
|
|
||||||
eventually be run) with `--host=TYPE'.
|
|
||||||
|
|
||||||
Sharing Defaults
|
|
||||||
================
|
|
||||||
|
|
||||||
If you want to set default values for `configure' scripts to share, you
|
|
||||||
can create a site shell script called `config.site' that gives default
|
|
||||||
values for variables like `CC', `cache_file', and `prefix'.
|
|
||||||
`configure' looks for `PREFIX/share/config.site' if it exists, then
|
|
||||||
`PREFIX/etc/config.site' if it exists. Or, you can set the
|
|
||||||
`CONFIG_SITE' environment variable to the location of the site script.
|
|
||||||
A warning: not all `configure' scripts look for a site script.
|
|
||||||
|
|
||||||
Defining Variables
|
|
||||||
==================
|
|
||||||
|
|
||||||
Variables not defined in a site shell script can be set in the
|
|
||||||
environment passed to `configure'. However, some packages may run
|
|
||||||
configure again during the build, and the customized values of these
|
|
||||||
variables may be lost. In order to avoid this problem, you should set
|
|
||||||
them in the `configure' command line, using `VAR=value'. For example:
|
|
||||||
|
|
||||||
./configure CC=/usr/local2/bin/gcc
|
|
||||||
|
|
||||||
causes the specified `gcc' to be used as the C compiler (unless it is
|
|
||||||
overridden in the site shell script).
|
|
||||||
|
|
||||||
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
|
|
||||||
an Autoconf bug. Until the bug is fixed you can use this workaround:
|
|
||||||
|
|
||||||
CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
|
|
||||||
|
|
||||||
`configure' Invocation
|
|
||||||
======================
|
|
||||||
|
|
||||||
`configure' recognizes the following options to control how it operates.
|
|
||||||
|
|
||||||
`--help'
|
|
||||||
`-h'
|
|
||||||
Print a summary of the options to `configure', and exit.
|
|
||||||
|
|
||||||
`--version'
|
|
||||||
`-V'
|
|
||||||
Print the version of Autoconf used to generate the `configure'
|
|
||||||
script, and exit.
|
|
||||||
|
|
||||||
`--cache-file=FILE'
|
|
||||||
Enable the cache: use and save the results of the tests in FILE,
|
|
||||||
traditionally `config.cache'. FILE defaults to `/dev/null' to
|
|
||||||
disable caching.
|
|
||||||
|
|
||||||
`--config-cache'
|
|
||||||
`-C'
|
|
||||||
Alias for `--cache-file=config.cache'.
|
|
||||||
|
|
||||||
`--quiet'
|
|
||||||
`--silent'
|
|
||||||
`-q'
|
|
||||||
Do not print messages saying which checks are being made. To
|
|
||||||
suppress all normal output, redirect it to `/dev/null' (any error
|
|
||||||
messages will still be shown).
|
|
||||||
|
|
||||||
`--srcdir=DIR'
|
|
||||||
Look for the package's source code in directory DIR. Usually
|
|
||||||
`configure' can determine that directory automatically.
|
|
||||||
|
|
||||||
`configure' also accepts some other, not widely useful, options. Run
|
|
||||||
`configure --help' for more details.
|
|
||||||
|
|
||||||
29
Makefile.am
@ -1,29 +0,0 @@
|
|||||||
AUTOMAKE_OPTIONS = dist-bzip2
|
|
||||||
SUBDIRS = src data tests pam doc po
|
|
||||||
EXTRA_DIST = TODO intltool-extract.in intltool-merge.in intltool-update.in
|
|
||||||
|
|
||||||
DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc --with-systemdsystemunitdir='$${libdir}/systemd/system-distcheck'
|
|
||||||
|
|
||||||
all: check create-pot
|
|
||||||
|
|
||||||
check: check-translations
|
|
||||||
|
|
||||||
create-pot:
|
|
||||||
$(MAKE) -C po fprintd.pot
|
|
||||||
|
|
||||||
check-translations:
|
|
||||||
@for i in $(top_srcdir)/po/*.po ; do \
|
|
||||||
if ! grep -q `basename $$i | sed 's,.po,,'` $(top_srcdir)/po/LINGUAS ; then \
|
|
||||||
echo '**********************************'; \
|
|
||||||
echo '***' `basename $$i | sed 's,.po,,'` missing from po/LINGUAS '***' ; \
|
|
||||||
echo '**********************************'; \
|
|
||||||
exit 1; \
|
|
||||||
fi; \
|
|
||||||
done;
|
|
||||||
|
|
||||||
update-translations: create-pot check-translations
|
|
||||||
@tx -r $(srcdir) pull --all --force --skip
|
|
||||||
@if [ ! -d .tx ] ; then mkdir .tx ; fi ; if [ x$(srcdir) != x$(builddir) ] ; then cp -f $(srcdir)/.tx/config .tx/ ; fi
|
|
||||||
@tx push --source
|
|
||||||
|
|
||||||
-include $(top_srcdir)/git.mk
|
|
||||||
15
NEWS
@ -1,6 +1,21 @@
|
|||||||
This file lists notable changes in each release. For the full history of all
|
This file lists notable changes in each release. For the full history of all
|
||||||
changes, see ChangeLog.
|
changes, see ChangeLog.
|
||||||
|
|
||||||
|
version 1.90.1:
|
||||||
|
- Add support for prints saved on the fingerprint device itself
|
||||||
|
- Add integration tests using the virtual image driver, and further
|
||||||
|
tests for the utilities
|
||||||
|
- Port build system to meson
|
||||||
|
- Loads of build warnings and memory leak fixes
|
||||||
|
|
||||||
|
- PAM module:
|
||||||
|
- Port PAM module to sd-bus from dbus-glib
|
||||||
|
- Use systemd to not ask for a fingerprint scan on remote logins
|
||||||
|
- Add man page for PAM module
|
||||||
|
- Add tests
|
||||||
|
|
||||||
|
This version requires libfprint 1.90.1, a 2.0 pre-release.
|
||||||
|
|
||||||
version 0.9.0:
|
version 0.9.0:
|
||||||
- Fix hangs when there the verification error was "retry"
|
- Fix hangs when there the verification error was "retry"
|
||||||
- Update for fp_get_pollfds() changes
|
- Update for fp_get_pollfds() changes
|
||||||
|
|||||||
@ -1,7 +1,13 @@
|
|||||||
Transifex.net Token Verification
|
Updating translations
|
||||||
=================================
|
=====================
|
||||||
|
|
||||||
The list of tokens bellow guarantee the respective users to be able to enable
|
The update-transifex.sh script should be run regularly to both pull
|
||||||
|
translations from the Transifex service, and push new strings to translate.
|
||||||
|
|
||||||
|
Transifex.net Token Verification
|
||||||
|
================================
|
||||||
|
|
||||||
|
The list of tokens below guarantee the respective users to be able to enable
|
||||||
submission on components using the following repository url:
|
submission on components using the following repository url:
|
||||||
|
|
||||||
https://gitlab.freedesktop.org/libfprint/fprintd/
|
https://gitlab.freedesktop.org/libfprint/fprintd/
|
||||||
|
|||||||
182
acinclude.m4
@ -1,182 +0,0 @@
|
|||||||
dnl AS_AC_EXPAND(VAR, CONFIGURE_VAR)
|
|
||||||
dnl
|
|
||||||
dnl example
|
|
||||||
dnl AS_AC_EXPAND(SYSCONFDIR, $sysconfdir)
|
|
||||||
dnl will set SYSCONFDIR to /usr/local/etc if prefix=/usr/local
|
|
||||||
|
|
||||||
AC_DEFUN([AS_AC_EXPAND],
|
|
||||||
[
|
|
||||||
EXP_VAR=[$1]
|
|
||||||
FROM_VAR=[$2]
|
|
||||||
|
|
||||||
dnl first expand prefix and exec_prefix if necessary
|
|
||||||
prefix_save=$prefix
|
|
||||||
exec_prefix_save=$exec_prefix
|
|
||||||
|
|
||||||
dnl if no prefix given, then use /usr/local, the default prefix
|
|
||||||
if test "x$prefix" = "xNONE"; then
|
|
||||||
prefix=$ac_default_prefix
|
|
||||||
fi
|
|
||||||
dnl if no exec_prefix given, then use prefix
|
|
||||||
if test "x$exec_prefix" = "xNONE"; then
|
|
||||||
exec_prefix=$prefix
|
|
||||||
fi
|
|
||||||
|
|
||||||
full_var="$FROM_VAR"
|
|
||||||
dnl loop until it doesn't change anymore
|
|
||||||
while true; do
|
|
||||||
new_full_var="`eval echo $full_var`"
|
|
||||||
if test "x$new_full_var"="x$full_var"; then break; fi
|
|
||||||
full_var=$new_full_var
|
|
||||||
done
|
|
||||||
|
|
||||||
dnl clean up
|
|
||||||
full_var=$new_full_var
|
|
||||||
AC_SUBST([$1], "$full_var")
|
|
||||||
|
|
||||||
dnl restore prefix and exec_prefix
|
|
||||||
prefix=$prefix_save
|
|
||||||
exec_prefix=$exec_prefix_save
|
|
||||||
])
|
|
||||||
|
|
||||||
dnl GNOME_COMPILE_WARNINGS
|
|
||||||
dnl Turn on many useful compiler warnings
|
|
||||||
dnl For now, only works on GCC
|
|
||||||
AC_DEFUN([GNOME_COMPILE_WARNINGS],[
|
|
||||||
dnl ******************************
|
|
||||||
dnl More compiler warnings
|
|
||||||
dnl ******************************
|
|
||||||
|
|
||||||
AC_ARG_ENABLE(compile-warnings,
|
|
||||||
AC_HELP_STRING([--enable-compile-warnings=@<:@no/minimum/yes/maximum/error@:>@],
|
|
||||||
[Turn on compiler warnings]),,
|
|
||||||
[enable_compile_warnings="m4_default([$1],[yes])"])
|
|
||||||
|
|
||||||
warnCFLAGS=
|
|
||||||
if test "x$GCC" != xyes; then
|
|
||||||
enable_compile_warnings=no
|
|
||||||
fi
|
|
||||||
|
|
||||||
warning_flags=
|
|
||||||
realsave_CFLAGS="$CFLAGS"
|
|
||||||
|
|
||||||
case "$enable_compile_warnings" in
|
|
||||||
no)
|
|
||||||
warning_flags=
|
|
||||||
;;
|
|
||||||
minimum)
|
|
||||||
warning_flags="-Wall"
|
|
||||||
;;
|
|
||||||
yes)
|
|
||||||
warning_flags="-Wall -Wmissing-prototypes"
|
|
||||||
;;
|
|
||||||
maximum|error)
|
|
||||||
warning_flags="-Wall -Wmissing-prototypes -Wnested-externs -Wpointer-arith"
|
|
||||||
CFLAGS="$warning_flags $CFLAGS"
|
|
||||||
for option in -Wno-sign-compare; do
|
|
||||||
SAVE_CFLAGS="$CFLAGS"
|
|
||||||
CFLAGS="$CFLAGS $option"
|
|
||||||
AC_MSG_CHECKING([whether gcc understands $option])
|
|
||||||
AC_TRY_COMPILE([], [],
|
|
||||||
has_option=yes,
|
|
||||||
has_option=no,)
|
|
||||||
CFLAGS="$SAVE_CFLAGS"
|
|
||||||
AC_MSG_RESULT($has_option)
|
|
||||||
if test $has_option = yes; then
|
|
||||||
warning_flags="$warning_flags $option"
|
|
||||||
fi
|
|
||||||
unset has_option
|
|
||||||
unset SAVE_CFLAGS
|
|
||||||
done
|
|
||||||
unset option
|
|
||||||
if test "$enable_compile_warnings" = "error" ; then
|
|
||||||
warning_flags="$warning_flags -Werror"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
AC_MSG_ERROR(Unknown argument '$enable_compile_warnings' to --enable-compile-warnings)
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
CFLAGS="$realsave_CFLAGS"
|
|
||||||
AC_MSG_CHECKING(what warning flags to pass to the C compiler)
|
|
||||||
AC_MSG_RESULT($warning_flags)
|
|
||||||
|
|
||||||
AC_ARG_ENABLE(iso-c,
|
|
||||||
AC_HELP_STRING([--enable-iso-c],
|
|
||||||
[Try to warn if code is not ISO C ]),,
|
|
||||||
[enable_iso_c=no])
|
|
||||||
|
|
||||||
AC_MSG_CHECKING(what language compliance flags to pass to the C compiler)
|
|
||||||
complCFLAGS=
|
|
||||||
if test "x$enable_iso_c" != "xno"; then
|
|
||||||
if test "x$GCC" = "xyes"; then
|
|
||||||
case " $CFLAGS " in
|
|
||||||
*[\ \ ]-ansi[\ \ ]*) ;;
|
|
||||||
*) complCFLAGS="$complCFLAGS -ansi" ;;
|
|
||||||
esac
|
|
||||||
case " $CFLAGS " in
|
|
||||||
*[\ \ ]-pedantic[\ \ ]*) ;;
|
|
||||||
*) complCFLAGS="$complCFLAGS -pedantic" ;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
AC_MSG_RESULT($complCFLAGS)
|
|
||||||
|
|
||||||
WARN_CFLAGS="$warning_flags $complCFLAGS"
|
|
||||||
AC_SUBST(WARN_CFLAGS)
|
|
||||||
])
|
|
||||||
|
|
||||||
dnl For C++, do basically the same thing.
|
|
||||||
|
|
||||||
AC_DEFUN([GNOME_CXX_WARNINGS],[
|
|
||||||
AC_ARG_ENABLE(cxx-warnings,
|
|
||||||
AC_HELP_STRING([--enable-cxx-warnings=@<:@no/minimum/yes@:>@]
|
|
||||||
[Turn on compiler warnings.]),,
|
|
||||||
[enable_cxx_warnings="m4_default([$1],[minimum])"])
|
|
||||||
|
|
||||||
AC_MSG_CHECKING(what warning flags to pass to the C++ compiler)
|
|
||||||
warnCXXFLAGS=
|
|
||||||
if test "x$GXX" != xyes; then
|
|
||||||
enable_cxx_warnings=no
|
|
||||||
fi
|
|
||||||
if test "x$enable_cxx_warnings" != "xno"; then
|
|
||||||
if test "x$GXX" = "xyes"; then
|
|
||||||
case " $CXXFLAGS " in
|
|
||||||
*[\ \ ]-Wall[\ \ ]*) ;;
|
|
||||||
*) warnCXXFLAGS="-Wall -Wno-unused" ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
## -W is not all that useful. And it cannot be controlled
|
|
||||||
## with individual -Wno-xxx flags, unlike -Wall
|
|
||||||
if test "x$enable_cxx_warnings" = "xyes"; then
|
|
||||||
warnCXXFLAGS="$warnCXXFLAGS -Wshadow -Woverloaded-virtual"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
AC_MSG_RESULT($warnCXXFLAGS)
|
|
||||||
|
|
||||||
AC_ARG_ENABLE(iso-cxx,
|
|
||||||
AC_HELP_STRING([--enable-iso-cxx],
|
|
||||||
[Try to warn if code is not ISO C++ ]),,
|
|
||||||
[enable_iso_cxx=no])
|
|
||||||
|
|
||||||
AC_MSG_CHECKING(what language compliance flags to pass to the C++ compiler)
|
|
||||||
complCXXFLAGS=
|
|
||||||
if test "x$enable_iso_cxx" != "xno"; then
|
|
||||||
if test "x$GXX" = "xyes"; then
|
|
||||||
case " $CXXFLAGS " in
|
|
||||||
*[\ \ ]-ansi[\ \ ]*) ;;
|
|
||||||
*) complCXXFLAGS="$complCXXFLAGS -ansi" ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
case " $CXXFLAGS " in
|
|
||||||
*[\ \ ]-pedantic[\ \ ]*) ;;
|
|
||||||
*) complCXXFLAGS="$complCXXFLAGS -pedantic" ;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
AC_MSG_RESULT($complCXXFLAGS)
|
|
||||||
|
|
||||||
WARN_CXXFLAGS="$CXXFLAGS $warnCXXFLAGS $complCXXFLAGS"
|
|
||||||
AC_SUBST(WARN_CXXFLAGS)
|
|
||||||
])
|
|
||||||
22
autogen.sh
@ -1,22 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
srcdir=`dirname $0`
|
|
||||||
test -z "$srcdir" && srcdir=.
|
|
||||||
|
|
||||||
olddir=`pwd`
|
|
||||||
cd "$srcdir"
|
|
||||||
|
|
||||||
aclocal || exit 1
|
|
||||||
autoheader || exit 1
|
|
||||||
glib-gettextize -f -c || exit 1
|
|
||||||
gtkdocize --copy || exit 1
|
|
||||||
intltoolize -c -f || exit 1
|
|
||||||
libtoolize -c || exit 1
|
|
||||||
autoconf || exit 1
|
|
||||||
automake -a -c || exit 1
|
|
||||||
|
|
||||||
cd "$olddir"
|
|
||||||
|
|
||||||
if test -z "$NOCONFIGURE"; then
|
|
||||||
$srcdir/configure --enable-maintainer-mode $*
|
|
||||||
fi
|
|
||||||
11
config.h.in
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/* Define to the Gettext package name */
|
||||||
|
#mesondefine GETTEXT_PACKAGE
|
||||||
|
|
||||||
|
/* Version number of package */
|
||||||
|
#mesondefine PACKAGE_VERSION
|
||||||
|
|
||||||
|
/* Where the configuration file will be located */
|
||||||
|
#mesondefine SYSCONFDIR
|
||||||
|
|
||||||
|
/* Define to the version of this package. */
|
||||||
|
#mesondefine VERSION
|
||||||
90
configure.ac
@ -1,90 +0,0 @@
|
|||||||
AC_INIT([fprintd], [0.9.0])
|
|
||||||
AM_INIT_AUTOMAKE([1.11 dist-xz no-dist-gzip check-news])
|
|
||||||
AC_CONFIG_SRCDIR([src/main.c])
|
|
||||||
AC_CONFIG_HEADERS([config.h])
|
|
||||||
|
|
||||||
# Enable silent build when available (Automake 1.11)
|
|
||||||
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
|
|
||||||
|
|
||||||
AC_PREREQ([2.50])
|
|
||||||
AC_PROG_LIBTOOL
|
|
||||||
AC_PROG_CC
|
|
||||||
AM_PROG_CC_C_O
|
|
||||||
|
|
||||||
GETTEXT_PACKAGE=fprintd
|
|
||||||
AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, ["$GETTEXT_PACKAGE"], [Define to the Gettext package name])
|
|
||||||
AC_SUBST(GETTEXT_PACKAGE)
|
|
||||||
AM_GLIB_GNU_GETTEXT
|
|
||||||
IT_PROG_INTLTOOL([0.35.0])
|
|
||||||
|
|
||||||
PKG_CHECK_MODULES(FPRINT, [libfprint > 0.1.0])
|
|
||||||
AC_SUBST(FPRINT_LIBS)
|
|
||||||
AC_SUBST(FPRINT_CFLAGS)
|
|
||||||
|
|
||||||
PKG_CHECK_MODULES(GLIB, glib-2.0 dbus-glib-1)
|
|
||||||
AC_SUBST(GLIB_CFLAGS)
|
|
||||||
AC_SUBST(GLIB_LIBS)
|
|
||||||
|
|
||||||
PKG_CHECK_MODULES(DAEMON, glib-2.0 dbus-glib-1 gmodule-2.0 polkit-gobject-1 >= 0.91 gio-2.0 >= 2.26)
|
|
||||||
AC_SUBST(DAEMON_LIBS)
|
|
||||||
AC_SUBST(DAEMON_CFLAGS)
|
|
||||||
|
|
||||||
AC_ARG_ENABLE(pam, AC_HELP_STRING([--enable-pam],[Build the fprintd PAM module]), enable_pam="$enableval", enable_pam=yes)
|
|
||||||
has_pam=no
|
|
||||||
if test x$enable_pam = xyes; then
|
|
||||||
has_pam=yes
|
|
||||||
AC_CHECK_HEADER([security/pam_modules.h], [has_pam=yes] , [has_pam=no])
|
|
||||||
if test x$has_pam = xyes; then
|
|
||||||
has_pam=no
|
|
||||||
AC_CHECK_LIB(pam, pam_start, [PAM_LIBS="-lpam"
|
|
||||||
has_pam=yes],
|
|
||||||
has_pam=no)
|
|
||||||
fi
|
|
||||||
AC_SUBST(PAM_LIBS)
|
|
||||||
fi
|
|
||||||
AM_CONDITIONAL(HAVE_PAM, test "x$has_pam" = "xyes")
|
|
||||||
|
|
||||||
AC_MSG_CHECKING(for PAM headers and library)
|
|
||||||
AC_MSG_RESULT([$has_pam])
|
|
||||||
|
|
||||||
|
|
||||||
AC_CHECK_PROG([XMLLINT], [xmllint], [xmllint])
|
|
||||||
AC_CHECK_PROG([XSLTPROC], [xsltproc], [xsltproc])
|
|
||||||
AC_CHECK_PROG([POD2MAN], [pod2man], [pod2man])
|
|
||||||
AM_CONDITIONAL(BUILD_MAN, test x"$POD2MAN" != "x")
|
|
||||||
|
|
||||||
GTK_DOC_CHECK([1.3])
|
|
||||||
|
|
||||||
AC_ARG_WITH([systemdsystemunitdir],
|
|
||||||
AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]),
|
|
||||||
[],
|
|
||||||
[with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)])
|
|
||||||
AS_IF([test "x$with_systemdsystemunitdir" != "xno"], [
|
|
||||||
# StateDirectory was introduced in systemd 235
|
|
||||||
PKG_CHECK_MODULES(SYSTEMD, systemd >= 235)
|
|
||||||
AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])
|
|
||||||
])
|
|
||||||
AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$systemdsystemunitdir"])
|
|
||||||
|
|
||||||
AS_AC_EXPAND(DATADIR, $datadir)
|
|
||||||
|
|
||||||
DBUS_SERVICES_DIR="$DATADIR/dbus-1/services"
|
|
||||||
AC_SUBST(DBUS_SERVICES_DIR)
|
|
||||||
AC_DEFINE_UNQUOTED(DBUS_SERVICES_DIR, "$DBUS_SERVICES_DIR", [Where services dir for DBUS is])
|
|
||||||
|
|
||||||
AS_AC_EXPAND(SYSCONFDIR, $sysconfdir)
|
|
||||||
AC_DEFINE_UNQUOTED(SYSCONFDIR, "$SYSCONFDIR", [Where the configuration file will be located])
|
|
||||||
|
|
||||||
GNOME_COMPILE_WARNINGS
|
|
||||||
|
|
||||||
AC_OUTPUT([
|
|
||||||
Makefile
|
|
||||||
src/Makefile
|
|
||||||
data/Makefile
|
|
||||||
tests/Makefile
|
|
||||||
pam/Makefile
|
|
||||||
doc/Makefile
|
|
||||||
doc/version.xml
|
|
||||||
doc/dbus/Makefile
|
|
||||||
po/Makefile.in
|
|
||||||
])
|
|
||||||
@ -1,44 +0,0 @@
|
|||||||
|
|
||||||
dbus_servicesdir = $(datadir)/dbus-1/system-services
|
|
||||||
dbus_services_in_files = net.reactivated.Fprint.service.in
|
|
||||||
dbus_services_DATA = $(dbus_services_in_files:.service.in=.service)
|
|
||||||
|
|
||||||
$(dbus_services_DATA): $(dbus_services_in_files)
|
|
||||||
sed -e "s|\@LIBEXECDIR\@|$(libexecdir)|" $< > $@
|
|
||||||
|
|
||||||
dbus_confdir = $(sysconfdir)/dbus-1/system.d
|
|
||||||
dbus_conf_DATA = net.reactivated.Fprint.conf
|
|
||||||
|
|
||||||
systemdservice_in_files = fprintd.service.in
|
|
||||||
|
|
||||||
if HAVE_SYSTEMD
|
|
||||||
systemdservicedir = $(systemdsystemunitdir)
|
|
||||||
systemdservice_DATA = $(systemdservice_in_files:.service.in=.service)
|
|
||||||
$(systemdservice_DATA): $(systemdservice_in_files) Makefile
|
|
||||||
@sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@
|
|
||||||
endif
|
|
||||||
|
|
||||||
polkitdir = $(datadir)/polkit-1/actions
|
|
||||||
polkit_in_files = net.reactivated.fprint.device.policy.in
|
|
||||||
|
|
||||||
@INTLTOOL_POLICY_RULE@
|
|
||||||
polkit_DATA = $(polkit_in_files:.policy.in=.policy)
|
|
||||||
|
|
||||||
confdir = $(sysconfdir)/
|
|
||||||
conf_DATA = fprintd.conf
|
|
||||||
|
|
||||||
man_MANS =
|
|
||||||
|
|
||||||
if BUILD_MAN
|
|
||||||
man_MANS += fprintd.1
|
|
||||||
fprintd.1: fprintd.pod
|
|
||||||
$(AM_V_GEN) pod2man -c "" -s 1 -q none -n fprintd -r freedesktop $< > $@
|
|
||||||
endif
|
|
||||||
|
|
||||||
EXTRA_DIST = $(dbus_services_in_files) $(dbus_conf_DATA) $(polkit_in_files) $(conf_DATA) $(systemdservice_in_files) fprintd.pod
|
|
||||||
CLEANFILES = $(polkit_DATA) $(dbus_services_DATA) $(systemdservice_DATA) fprintd.1
|
|
||||||
|
|
||||||
check:
|
|
||||||
@$(XMLLINT) --noout $(polkit_DATA)
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
.\" Automatically generated by Pod::Man 2.23 (Pod::Simple 3.14)
|
.\" Automatically generated by Pod::Man 4.12 (Pod::Simple 3.39)
|
||||||
.\"
|
.\"
|
||||||
.\" Standard preamble:
|
.\" Standard preamble:
|
||||||
.\" ========================================================================
|
.\" ========================================================================
|
||||||
@ -38,27 +38,36 @@
|
|||||||
. ds PI \(*p
|
. ds PI \(*p
|
||||||
. ds L" ``
|
. ds L" ``
|
||||||
. ds R" ''
|
. ds R" ''
|
||||||
|
. ds C`
|
||||||
|
. ds C'
|
||||||
'br\}
|
'br\}
|
||||||
.\"
|
.\"
|
||||||
.\" Escape single quotes in literal strings from groff's Unicode transform.
|
.\" Escape single quotes in literal strings from groff's Unicode transform.
|
||||||
.ie \n(.g .ds Aq \(aq
|
.ie \n(.g .ds Aq \(aq
|
||||||
.el .ds Aq '
|
.el .ds Aq '
|
||||||
.\"
|
.\"
|
||||||
.\" If the F register is turned on, we'll generate index entries on stderr for
|
.\" If the F register is >0, we'll generate index entries on stderr for
|
||||||
.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
|
.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
|
||||||
.\" entries marked with X<> in POD. Of course, you'll have to process the
|
.\" entries marked with X<> in POD. Of course, you'll have to process the
|
||||||
.\" output yourself in some meaningful fashion.
|
.\" output yourself in some meaningful fashion.
|
||||||
.ie \nF \{\
|
.\"
|
||||||
. de IX
|
.\" Avoid warning from groff about undefined register 'F'.
|
||||||
. tm Index:\\$1\t\\n%\t"\\$2"
|
.de IX
|
||||||
..
|
..
|
||||||
. nr % 0
|
.nr rF 0
|
||||||
. rr F
|
.if \n(.g .if rF .nr rF 1
|
||||||
.\}
|
.if (\n(rF:(\n(.g==0)) \{\
|
||||||
.el \{\
|
. if \nF \{\
|
||||||
. de IX
|
. de IX
|
||||||
|
. tm Index:\\$1\t\\n%\t"\\$2"
|
||||||
..
|
..
|
||||||
|
. if !\nF==2 \{\
|
||||||
|
. nr % 0
|
||||||
|
. nr F 2
|
||||||
|
. \}
|
||||||
|
. \}
|
||||||
.\}
|
.\}
|
||||||
|
.rr rF
|
||||||
.\"
|
.\"
|
||||||
.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
|
.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
|
||||||
.\" Fear. Run. Save yourself. No user-serviceable parts.
|
.\" Fear. Run. Save yourself. No user-serviceable parts.
|
||||||
@ -124,16 +133,16 @@
|
|||||||
.\" ========================================================================
|
.\" ========================================================================
|
||||||
.\"
|
.\"
|
||||||
.IX Title "fprintd 1"
|
.IX Title "fprintd 1"
|
||||||
.TH fprintd 1 "2010-08-16" "freedesktop" ""
|
.TH fprintd 1 "2020-01-24" "freedesktop" ""
|
||||||
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
|
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
|
||||||
.\" way too many mistakes in technical documents.
|
.\" way too many mistakes in technical documents.
|
||||||
.if n .ad l
|
.if n .ad l
|
||||||
.nh
|
.nh
|
||||||
.SH "NAME"
|
.SH "NAME"
|
||||||
fprintd \- Fingerprint management daemon, and test applications
|
fprintd \- Fingerprint management daemon, and test applications
|
||||||
.SH "SYNOPSYS"
|
.SH "SYNOPSIS"
|
||||||
.IX Header "SYNOPSYS"
|
.IX Header "SYNOPSIS"
|
||||||
\&\fBfprintd-enroll\fR [username]
|
\&\fBfprintd-enroll\fR [\-f finger] [usename]
|
||||||
.PP
|
.PP
|
||||||
\&\fBfprintd-list\fR username [usernames...]
|
\&\fBfprintd-list\fR username [usernames...]
|
||||||
.PP
|
.PP
|
||||||
@ -185,7 +194,7 @@ Will list the user's enrolled fingerprints.
|
|||||||
.SS "fprintd-verify"
|
.SS "fprintd-verify"
|
||||||
.IX Subsection "fprintd-verify"
|
.IX Subsection "fprintd-verify"
|
||||||
.RS 8
|
.RS 8
|
||||||
Will enroll the user's right index finger into the database.
|
Will verify the user's fingerprints against the database.
|
||||||
.RE
|
.RE
|
||||||
.SS "fprintd-enroll"
|
.SS "fprintd-enroll"
|
||||||
.IX Subsection "fprintd-enroll"
|
.IX Subsection "fprintd-enroll"
|
||||||
@ -200,5 +209,5 @@ Will enroll the user's right index finger into the database.
|
|||||||
By default, fprintd stores the fingerprints in \fB/var/lib/fprint/\fR
|
By default, fprintd stores the fingerprints in \fB/var/lib/fprint/\fR
|
||||||
.SH "SEE ALSO"
|
.SH "SEE ALSO"
|
||||||
.IX Header "SEE ALSO"
|
.IX Header "SEE ALSO"
|
||||||
.IP "\fBdbus-daemon\fR, \fBgnome-about-me\fR" 8
|
.IP "\fBgnome-control-center\fR" 8
|
||||||
.IX Item "dbus-daemon, gnome-about-me"
|
.IX Item "gnome-control-center"
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
fprintd - Fingerprint management daemon, and test applications
|
fprintd - Fingerprint management daemon, and test applications
|
||||||
|
|
||||||
=head1 SYNOPSYS
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
B<fprintd-enroll> [-f finger] [usename]
|
B<fprintd-enroll> [-f finger] [usename]
|
||||||
|
|
||||||
@ -75,7 +75,7 @@ Will list the user's enrolled fingerprints.
|
|||||||
|
|
||||||
=over 8
|
=over 8
|
||||||
|
|
||||||
Will enroll the user's right index finger into the database.
|
Will verify the user's fingerprints against the database.
|
||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
@ -99,7 +99,7 @@ By default, fprintd stores the fingerprints in B</var/lib/fprint/>
|
|||||||
|
|
||||||
=over 8
|
=over 8
|
||||||
|
|
||||||
=item B<dbus-daemon>, B<gnome-about-me>
|
=item B<gnome-control-center>
|
||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
|
|||||||
70
data/meson.build
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
install_data('net.reactivated.Fprint.conf',
|
||||||
|
install_dir: dbus_conf_dir)
|
||||||
|
|
||||||
|
configure_file(
|
||||||
|
configuration: configuration_data({
|
||||||
|
'LIBEXECDIR': fprintd_installdir,
|
||||||
|
}),
|
||||||
|
input: 'net.reactivated.Fprint.service.in',
|
||||||
|
output: 'net.reactivated.Fprint.service',
|
||||||
|
install: true,
|
||||||
|
install_dir: dbus_service_dir,
|
||||||
|
)
|
||||||
|
|
||||||
|
configure_file(
|
||||||
|
configuration: configuration_data({
|
||||||
|
'libexecdir': fprintd_installdir,
|
||||||
|
}),
|
||||||
|
input: 'fprintd.service.in',
|
||||||
|
output: 'fprintd.service',
|
||||||
|
install: true,
|
||||||
|
install_dir: systemd_unit_dir,
|
||||||
|
)
|
||||||
|
|
||||||
|
polkit_policy = 'net.reactivated.fprint.device.policy'
|
||||||
|
polkit_policy_target = i18n.merge_file(polkit_policy,
|
||||||
|
input: '@0@.in'.format(polkit_policy),
|
||||||
|
output: polkit_policy,
|
||||||
|
po_dir: meson.source_root() / 'po',
|
||||||
|
install: true,
|
||||||
|
install_dir: polkit_policy_directory,
|
||||||
|
)
|
||||||
|
|
||||||
|
if xmllint.found()
|
||||||
|
test(polkit_policy,
|
||||||
|
xmllint,
|
||||||
|
depends: polkit_policy_target,
|
||||||
|
args: [
|
||||||
|
'--noout',
|
||||||
|
polkit_policy_target.full_path(),
|
||||||
|
])
|
||||||
|
endif
|
||||||
|
|
||||||
|
install_data('fprintd.conf',
|
||||||
|
install_dir: sysconfdir)
|
||||||
|
|
||||||
|
if get_option('man')
|
||||||
|
manfiles = {
|
||||||
|
'fprintd': 1,
|
||||||
|
'pam_fprintd': 8,
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach man_name, man_section: manfiles
|
||||||
|
custom_target('man_' + man_name + '.' + man_section.to_string(),
|
||||||
|
input: man_name + '.pod',
|
||||||
|
output: man_name + '.' + man_section.to_string(),
|
||||||
|
command: [
|
||||||
|
pod2man,
|
||||||
|
'-c', '',
|
||||||
|
'-s', man_section.to_string(),
|
||||||
|
'-q', 'none',
|
||||||
|
'-n', man_name,
|
||||||
|
'-r', 'freedesktop',
|
||||||
|
'@INPUT@',
|
||||||
|
'@OUTPUT@',
|
||||||
|
],
|
||||||
|
install: true,
|
||||||
|
install_dir: datadir / 'man' / 'man' + man_section.to_string(),
|
||||||
|
)
|
||||||
|
endforeach
|
||||||
|
endif
|
||||||
@ -10,8 +10,8 @@
|
|||||||
<icon_name>fprint</icon_name>
|
<icon_name>fprint</icon_name>
|
||||||
|
|
||||||
<action id="net.reactivated.fprint.device.verify">
|
<action id="net.reactivated.fprint.device.verify">
|
||||||
<_description>Verify a fingerprint</_description>
|
<description>Verify a fingerprint</description>
|
||||||
<_message>Privileges are required to verify fingerprints.</_message>
|
<message>Privileges are required to verify fingerprints.</message>
|
||||||
<defaults>
|
<defaults>
|
||||||
<allow_any>no</allow_any>
|
<allow_any>no</allow_any>
|
||||||
<allow_inactive>no</allow_inactive>
|
<allow_inactive>no</allow_inactive>
|
||||||
@ -20,8 +20,8 @@
|
|||||||
</action>
|
</action>
|
||||||
|
|
||||||
<action id="net.reactivated.fprint.device.enroll">
|
<action id="net.reactivated.fprint.device.enroll">
|
||||||
<_description>Enroll new fingerprints</_description>
|
<description>Enroll new fingerprints</description>
|
||||||
<_message>Privileges are required to enroll new fingerprints.</_message>
|
<message>Privileges are required to enroll new fingerprints.</message>
|
||||||
<defaults>
|
<defaults>
|
||||||
<allow_any>no</allow_any>
|
<allow_any>no</allow_any>
|
||||||
<allow_inactive>no</allow_inactive>
|
<allow_inactive>no</allow_inactive>
|
||||||
@ -30,8 +30,8 @@
|
|||||||
</action>
|
</action>
|
||||||
|
|
||||||
<action id="net.reactivated.fprint.device.setusername">
|
<action id="net.reactivated.fprint.device.setusername">
|
||||||
<_description>Select a user to enroll</_description>
|
<description>Select a user to enroll</description>
|
||||||
<_message>Privileges are required to enroll new fingerprints for other users.</_message>
|
<message>Privileges are required to enroll new fingerprints for other users.</message>
|
||||||
<defaults>
|
<defaults>
|
||||||
<allow_any>no</allow_any>
|
<allow_any>no</allow_any>
|
||||||
<allow_inactive>no</allow_inactive>
|
<allow_inactive>no</allow_inactive>
|
||||||
|
|||||||
184
data/pam_fprintd.8
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
.\" Automatically generated by Pod::Man 4.12 (Pod::Simple 3.39)
|
||||||
|
.\"
|
||||||
|
.\" Standard preamble:
|
||||||
|
.\" ========================================================================
|
||||||
|
.de Sp \" Vertical space (when we can't use .PP)
|
||||||
|
.if t .sp .5v
|
||||||
|
.if n .sp
|
||||||
|
..
|
||||||
|
.de Vb \" Begin verbatim text
|
||||||
|
.ft CW
|
||||||
|
.nf
|
||||||
|
.ne \\$1
|
||||||
|
..
|
||||||
|
.de Ve \" End verbatim text
|
||||||
|
.ft R
|
||||||
|
.fi
|
||||||
|
..
|
||||||
|
.\" Set up some character translations and predefined strings. \*(-- will
|
||||||
|
.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
|
||||||
|
.\" double quote, and \*(R" will give a right double quote. \*(C+ will
|
||||||
|
.\" give a nicer C++. Capital omega is used to do unbreakable dashes and
|
||||||
|
.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
|
||||||
|
.\" nothing in troff, for use with C<>.
|
||||||
|
.tr \(*W-
|
||||||
|
.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
|
||||||
|
.ie n \{\
|
||||||
|
. ds -- \(*W-
|
||||||
|
. ds PI pi
|
||||||
|
. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
|
||||||
|
. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
|
||||||
|
. ds L" ""
|
||||||
|
. ds R" ""
|
||||||
|
. ds C`
|
||||||
|
. ds C'
|
||||||
|
'br\}
|
||||||
|
.el\{\
|
||||||
|
. ds -- \|\(em\|
|
||||||
|
. ds PI \(*p
|
||||||
|
. ds L" ``
|
||||||
|
. ds R" ''
|
||||||
|
. ds C`
|
||||||
|
. ds C'
|
||||||
|
'br\}
|
||||||
|
.\"
|
||||||
|
.\" Escape single quotes in literal strings from groff's Unicode transform.
|
||||||
|
.ie \n(.g .ds Aq \(aq
|
||||||
|
.el .ds Aq '
|
||||||
|
.\"
|
||||||
|
.\" If the F register is >0, we'll generate index entries on stderr for
|
||||||
|
.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
|
||||||
|
.\" entries marked with X<> in POD. Of course, you'll have to process the
|
||||||
|
.\" output yourself in some meaningful fashion.
|
||||||
|
.\"
|
||||||
|
.\" Avoid warning from groff about undefined register 'F'.
|
||||||
|
.de IX
|
||||||
|
..
|
||||||
|
.nr rF 0
|
||||||
|
.if \n(.g .if rF .nr rF 1
|
||||||
|
.if (\n(rF:(\n(.g==0)) \{\
|
||||||
|
. if \nF \{\
|
||||||
|
. de IX
|
||||||
|
. tm Index:\\$1\t\\n%\t"\\$2"
|
||||||
|
..
|
||||||
|
. if !\nF==2 \{\
|
||||||
|
. nr % 0
|
||||||
|
. nr F 2
|
||||||
|
. \}
|
||||||
|
. \}
|
||||||
|
.\}
|
||||||
|
.rr rF
|
||||||
|
.\"
|
||||||
|
.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
|
||||||
|
.\" Fear. Run. Save yourself. No user-serviceable parts.
|
||||||
|
. \" fudge factors for nroff and troff
|
||||||
|
.if n \{\
|
||||||
|
. ds #H 0
|
||||||
|
. ds #V .8m
|
||||||
|
. ds #F .3m
|
||||||
|
. ds #[ \f1
|
||||||
|
. ds #] \fP
|
||||||
|
.\}
|
||||||
|
.if t \{\
|
||||||
|
. ds #H ((1u-(\\\\n(.fu%2u))*.13m)
|
||||||
|
. ds #V .6m
|
||||||
|
. ds #F 0
|
||||||
|
. ds #[ \&
|
||||||
|
. ds #] \&
|
||||||
|
.\}
|
||||||
|
. \" simple accents for nroff and troff
|
||||||
|
.if n \{\
|
||||||
|
. ds ' \&
|
||||||
|
. ds ` \&
|
||||||
|
. ds ^ \&
|
||||||
|
. ds , \&
|
||||||
|
. ds ~ ~
|
||||||
|
. ds /
|
||||||
|
.\}
|
||||||
|
.if t \{\
|
||||||
|
. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
|
||||||
|
. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
|
||||||
|
. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
|
||||||
|
. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
|
||||||
|
. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
|
||||||
|
. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
|
||||||
|
.\}
|
||||||
|
. \" troff and (daisy-wheel) nroff accents
|
||||||
|
.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
|
||||||
|
.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
|
||||||
|
.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
|
||||||
|
.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
|
||||||
|
.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
|
||||||
|
.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
|
||||||
|
.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
|
||||||
|
.ds ae a\h'-(\w'a'u*4/10)'e
|
||||||
|
.ds Ae A\h'-(\w'A'u*4/10)'E
|
||||||
|
. \" corrections for vroff
|
||||||
|
.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
|
||||||
|
.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
|
||||||
|
. \" for low resolution devices (crt and lpr)
|
||||||
|
.if \n(.H>23 .if \n(.V>19 \
|
||||||
|
\{\
|
||||||
|
. ds : e
|
||||||
|
. ds 8 ss
|
||||||
|
. ds o a
|
||||||
|
. ds d- d\h'-1'\(ga
|
||||||
|
. ds D- D\h'-1'\(hy
|
||||||
|
. ds th \o'bp'
|
||||||
|
. ds Th \o'LP'
|
||||||
|
. ds ae ae
|
||||||
|
. ds Ae AE
|
||||||
|
.\}
|
||||||
|
.rm #[ #] #H #V #F C
|
||||||
|
.\" ========================================================================
|
||||||
|
.\"
|
||||||
|
.IX Title "pam_fprintd 8"
|
||||||
|
.TH pam_fprintd 8 "2020-01-24" "freedesktop" ""
|
||||||
|
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
|
||||||
|
.\" way too many mistakes in technical documents.
|
||||||
|
.if n .ad l
|
||||||
|
.nh
|
||||||
|
.SH "NAME"
|
||||||
|
pam_fprintd \- PAM module to authenticate against fprintd, the fingerprint daemon
|
||||||
|
.SH "SYNOPSIS"
|
||||||
|
.IX Header "SYNOPSIS"
|
||||||
|
\&\fBpam_fprintd.so\fR [debug|debug=[\fIon\fR|\fIoff\fR|\fItrue\fR|\fIfalse\fR|\fI1\fR|\fI0\fR]] [max\-tries=\fI\s-1MAX_TRIES\s0\fR] [timeout=\fI\s-1TIMEOUT\s0\fR]
|
||||||
|
.SH "DESCRIPTION"
|
||||||
|
.IX Header "DESCRIPTION"
|
||||||
|
The pam_fprintd module is used to verify a user's fingerprints against fingerprints
|
||||||
|
enrolled using fprintd, the fingerprint management daemon.
|
||||||
|
.SH "OPTIONS"
|
||||||
|
.IX Header "OPTIONS"
|
||||||
|
.IP "\fBdebug\fR" 8
|
||||||
|
.IX Item "debug"
|
||||||
|
.PD 0
|
||||||
|
.IP "\fBdebug=[\f(BIon\fB|\f(BIoff\fB|\f(BItrue\fB|\f(BIfalse\fB|\f(BI1\fB|\f(BI0\fB]\fR" 8
|
||||||
|
.IX Item "debug=[on|off|true|false|1|0]"
|
||||||
|
.PD
|
||||||
|
Whether debug should be turned on or off. Debug messages will be generated using
|
||||||
|
pam_syslog which means that they will be saved in the systemd journal by default.
|
||||||
|
.IP "\fBmax\-tries=\f(BI\s-1MAX_TRIES\s0\fB\fR" 8
|
||||||
|
.IX Item "max-tries=MAX_TRIES"
|
||||||
|
The number of attempts at fingerprint authentication to try before returning an
|
||||||
|
authentication failure. The minimum, and default, number of tries is 3.
|
||||||
|
.IP "\fBtimeout=\f(BI\s-1TIMEOUT\s0\fB\fR" 8
|
||||||
|
.IX Item "timeout=TIMEOUT"
|
||||||
|
The amount of time before returning an authentication failure. The default timeout
|
||||||
|
is 30 seconds, with 10 seconds being the minimum.
|
||||||
|
.SH "LIMITATIONS"
|
||||||
|
.IX Header "LIMITATIONS"
|
||||||
|
The \s-1PAM\s0 stack is by design a serialised authentication, so it is not
|
||||||
|
possible for pam_fprintd to allow authentication through passwords and
|
||||||
|
fingerprints at the same time.
|
||||||
|
.PP
|
||||||
|
It is up to the application using the \s-1PAM\s0 services to implement separate
|
||||||
|
\&\s-1PAM\s0 processes and run separate authentication stacks separately. This
|
||||||
|
is the way multiple authentication methods are made available to users
|
||||||
|
of gdm for example.
|
||||||
|
.SH "AUTHOR"
|
||||||
|
.IX Header "AUTHOR"
|
||||||
|
\&\fBfprintd\fR was written by Bastien Nocera.
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
.IX Header "SEE ALSO"
|
||||||
|
.IP "\fBfprintd\fR, \fB\s-1PAM\s0\fR" 8
|
||||||
|
.IX Item "fprintd, PAM"
|
||||||
63
data/pam_fprintd.pod
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
pam_fprintd - PAM module to authenticate against fprintd, the fingerprint daemon
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
B<pam_fprintd.so> [debug|debug=[I<on>|I<off>|I<true>|I<false>|I<1>|I<0>]] [max-tries=I<MAX_TRIES>] [timeout=I<TIMEOUT>]
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
The pam_fprintd module is used to verify a user's fingerprints against fingerprints
|
||||||
|
enrolled using fprintd, the fingerprint management daemon.
|
||||||
|
|
||||||
|
=head1 OPTIONS
|
||||||
|
|
||||||
|
=over 8
|
||||||
|
|
||||||
|
=item B<debug>
|
||||||
|
|
||||||
|
=item B<debug=[I<on>|I<off>|I<true>|I<false>|I<1>|I<0>]>
|
||||||
|
|
||||||
|
Whether debug should be turned on or off. Debug messages will be generated using
|
||||||
|
pam_syslog which means that they will be saved in the systemd journal by default.
|
||||||
|
|
||||||
|
=item B<max-tries=I<MAX_TRIES>>
|
||||||
|
|
||||||
|
The number of attempts at fingerprint authentication to try before returning an
|
||||||
|
authentication failure. The minimum, and default, number of tries is 3.
|
||||||
|
|
||||||
|
=item B<timeout=I<TIMEOUT>>
|
||||||
|
|
||||||
|
The amount of time before returning an authentication failure. The default timeout
|
||||||
|
is 30 seconds, with 10 seconds being the minimum.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head1 LIMITATIONS
|
||||||
|
|
||||||
|
=over 8
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
The PAM stack is by design a serialised authentication, so it is not
|
||||||
|
possible for pam_fprintd to allow authentication through passwords and
|
||||||
|
fingerprints at the same time.
|
||||||
|
|
||||||
|
It is up to the application using the PAM services to implement separate
|
||||||
|
PAM processes and run separate authentication stacks separately. This
|
||||||
|
is the way multiple authentication methods are made available to users
|
||||||
|
of gdm for example.
|
||||||
|
|
||||||
|
=head1 AUTHOR
|
||||||
|
|
||||||
|
B<fprintd> was written by Bastien Nocera.
|
||||||
|
|
||||||
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
=over 8
|
||||||
|
|
||||||
|
=item B<fprintd>, B<PAM>
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
@ -1,81 +0,0 @@
|
|||||||
|
|
||||||
SUBDIRS = dbus
|
|
||||||
|
|
||||||
NULL =
|
|
||||||
|
|
||||||
AUTOMAKE_OPTIONS = 1.7
|
|
||||||
|
|
||||||
# The name of the module.
|
|
||||||
DOC_MODULE=fprintd
|
|
||||||
|
|
||||||
# The top-level SGML file.
|
|
||||||
DOC_MAIN_SGML_FILE=fprintd-docs.xml
|
|
||||||
|
|
||||||
# Extra options to supply to gtkdoc-scan
|
|
||||||
SCAN_OPTIONS=--ignore-headers=config.h
|
|
||||||
|
|
||||||
# The directory containing the source code. Relative to $(srcdir)
|
|
||||||
DOC_SOURCE_DIR=../src
|
|
||||||
|
|
||||||
# Used for dependencies
|
|
||||||
HFILE_GLOB=
|
|
||||||
#$(top_srcdir)/policy/*.h
|
|
||||||
CFILE_GLOB=
|
|
||||||
#$(top_srcdir)/policy/*.c
|
|
||||||
|
|
||||||
# Headers to ignore
|
|
||||||
IGNORE_HFILES= \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
# CFLAGS and LDFLAGS for compiling scan program. Only needed
|
|
||||||
# if $(DOC_MODULE).types is non-empty.
|
|
||||||
INCLUDES = \
|
|
||||||
$(GLIB_CFLAGS) \
|
|
||||||
-I$(top_srcdir)/src \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
GTKDOC_LIBS = \
|
|
||||||
$(GLIB_LIBS) \
|
|
||||||
$(top_builddir)/src/libfprintd.la \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
# Extra options to supply to gtkdoc-mkdb
|
|
||||||
MKDB_OPTIONS=--sgml-mode --output-format=xml
|
|
||||||
|
|
||||||
# Extra options to supply to gtkdoc-mktmpl
|
|
||||||
MKTMPL_OPTIONS=
|
|
||||||
|
|
||||||
# Non-autogenerated SGML files to be included in $(DOC_MAIN_SGML_FILE)
|
|
||||||
content_files = \
|
|
||||||
version.xml \
|
|
||||||
dbus/net.reactivated.Fprint.Manager.ref.xml \
|
|
||||||
dbus/net.reactivated.Fprint.Device.ref.xml \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
# Images to copy into HTML directory
|
|
||||||
HTML_IMAGES = \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
# Extra options to supply to gtkdoc-fixref
|
|
||||||
FIXXREF_OPTIONS=
|
|
||||||
|
|
||||||
DISTCLEANFILES = \
|
|
||||||
net.reactivated.Fprint.Manager.ref.xml \
|
|
||||||
net.reactivated.Fprint.Device.ref.xml
|
|
||||||
|
|
||||||
MAINTAINERCLEANFILES = \
|
|
||||||
*~ \
|
|
||||||
Makefile.in \
|
|
||||||
fprintd.types \
|
|
||||||
fprintd-*.txt \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
if ENABLE_GTK_DOC
|
|
||||||
include $(top_srcdir)/gtk-doc.make
|
|
||||||
else
|
|
||||||
EXTRA_DIST = fprintd-docs.xml
|
|
||||||
endif
|
|
||||||
|
|
||||||
# Version information for marking the documentation
|
|
||||||
EXTRA_DIST += version.xml.in
|
|
||||||
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
|
|
||||||
noinst_DATA = net.reactivated.Fprint.Manager.ref.xml net.reactivated.Fprint.Device.ref.xml
|
|
||||||
|
|
||||||
net.reactivated.Fprint.Manager.ref.xml : $(top_srcdir)/src/manager.xml $(top_srcdir)/doc/dbus/spec-to-docbook.xsl
|
|
||||||
echo "<?xml version=\"1.0\"?>""<!DOCTYPE refentry PUBLIC \"-//OASIS//DTD DocBook XML V4.1.2//EN\" \"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd\">" > $@
|
|
||||||
$(XSLTPROC) $(top_srcdir)/doc/dbus/spec-to-docbook.xsl $< | tail -n +2 >> $@
|
|
||||||
|
|
||||||
net.reactivated.Fprint.Device.ref.xml : $(top_srcdir)/src/device.xml $(top_srcdir)/doc/dbus/spec-to-docbook.xsl
|
|
||||||
echo "<?xml version=\"1.0\"?>""<!DOCTYPE refentry PUBLIC \"-//OASIS//DTD DocBook XML V4.1.2//EN\" \"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd\">" > $@
|
|
||||||
$(XSLTPROC) $(top_srcdir)/doc/dbus/spec-to-docbook.xsl $< | tail -n +2 >> $@
|
|
||||||
|
|
||||||
EXTRA_DIST = spec-to-docbook.xsl dbus-introspect-docs.dtd
|
|
||||||
|
|
||||||
clean-local :
|
|
||||||
rm -f *~ *.ref.xml
|
|
||||||
29
doc/dbus/meson.build
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
docbook_xml_header = custom_target('docbook_xml_header',
|
||||||
|
output: 'docbook-xml-header.xml',
|
||||||
|
command: [
|
||||||
|
'echo', '-n',
|
||||||
|
'<?xml version="1.0"?>',
|
||||||
|
'<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">',
|
||||||
|
],
|
||||||
|
capture: true,
|
||||||
|
)
|
||||||
|
|
||||||
|
dbus_interfaces_refs = []
|
||||||
|
foreach interface_file: dbus_interfaces_files
|
||||||
|
basename = run_command('basename', interface_file.full_path(), '.xml').stdout().strip()
|
||||||
|
dbus_interfaces_refs += custom_target(basename + '_ref',
|
||||||
|
input: docbook_xml_header,
|
||||||
|
output: basename + '.ref.xml',
|
||||||
|
build_by_default: true,
|
||||||
|
depends: interface_file,
|
||||||
|
capture: true,
|
||||||
|
command: [
|
||||||
|
bash, '-c',
|
||||||
|
'cat @INPUT@;' +
|
||||||
|
xsltproc.path() + ' @0@/@1@ '.format(
|
||||||
|
meson.source_root(),
|
||||||
|
files('spec-to-docbook.xsl')[0]) +
|
||||||
|
interface_file.full_path() + '| tail -n +2;',
|
||||||
|
],
|
||||||
|
)
|
||||||
|
endforeach
|
||||||
27
doc/meson.build
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
subdir('dbus')
|
||||||
|
|
||||||
|
version_file = configure_file(
|
||||||
|
input: 'version.xml.in',
|
||||||
|
output: 'version.xml',
|
||||||
|
configuration: configuration_data({
|
||||||
|
'VERSION': meson.project_version(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
gnome.gtkdoc(meson.project_name(),
|
||||||
|
main_xml: 'fprintd-docs.xml',
|
||||||
|
src_dir: meson.source_root() / 'src',
|
||||||
|
dependencies: [
|
||||||
|
declare_dependency(
|
||||||
|
sources: dbus_interfaces_refs,
|
||||||
|
link_with: libfprintd_private,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
content_files: [
|
||||||
|
version_file,
|
||||||
|
dbus_interfaces_refs,
|
||||||
|
],
|
||||||
|
ignore_headers: [
|
||||||
|
'config.h',
|
||||||
|
],
|
||||||
|
install: true)
|
||||||
196
git.mk
@ -1,196 +0,0 @@
|
|||||||
# git.mk
|
|
||||||
#
|
|
||||||
# Copyright 2009, Red Hat, Inc.
|
|
||||||
# Written by Behdad Esfahbod
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
# The canonical source for this file is pango/git.mk, or whereever the
|
|
||||||
# header of pango/git.mk suggests in the future.
|
|
||||||
#
|
|
||||||
# To use in your project, import this file in your git repo's toplevel,
|
|
||||||
# then do "make -f git.mk". This modifies all Makefile.am files in
|
|
||||||
# your project to include git.mk.
|
|
||||||
#
|
|
||||||
# This enables automatic .gitignore generation. If you need to ignore
|
|
||||||
# more files, add them to the GITIGNOREFILES variable in your Makefile.am.
|
|
||||||
# But think twice before doing that. If a file has to be in .gitignore,
|
|
||||||
# chances are very high that it's a generated file and should be in one
|
|
||||||
# of MOSTLYCLEANFILES, CLEANFILES, DISTCLEANFILES, or MAINTAINERCLEANFILES.
|
|
||||||
#
|
|
||||||
# The only case that you need to manually add a file to GITIGNOREFILES is
|
|
||||||
# when remove files in one of mostlyclean-local, clean-local, distclean-local,
|
|
||||||
# or maintainer-clean-local.
|
|
||||||
#
|
|
||||||
# Note that for files like editor backup, etc, there are better places to
|
|
||||||
# ignore them. See "man gitignore".
|
|
||||||
#
|
|
||||||
# If "make maintainer-clean" removes the files but they are not recognized
|
|
||||||
# by this script (that is, if "git status" shows untracked files still), send
|
|
||||||
# me the output of "git status" as well as your Makefile.am and Makefile for
|
|
||||||
# the directories involved.
|
|
||||||
#
|
|
||||||
# For a list of toplevel files that should be in MAINTAINERCLEANFILES, see
|
|
||||||
# pango/Makefile.am.
|
|
||||||
#
|
|
||||||
# Don't EXTRA_DIST this file. It is supposed to only live in git clones,
|
|
||||||
# not tarballs. It serves no useful purpose in tarballs and clutters the
|
|
||||||
# build dir.
|
|
||||||
#
|
|
||||||
# This file knows how to handle autoconf, automake, libtool, gtk-doc,
|
|
||||||
# gnome-doc-utils, intltool, GSettings.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# KNOWN ISSUES:
|
|
||||||
#
|
|
||||||
# - Recursive configure doesn't work as $(top_srcdir)/git.mk inside the
|
|
||||||
# submodule doesn't find us. If you have configure.{in,ac} files in
|
|
||||||
# subdirs, add a proxy git.mk file in those dirs that simply does:
|
|
||||||
# "include $(top_srcdir)/../git.mk". Add more ..'s to your taste.
|
|
||||||
# And add those files to git. See vte/gnome-pty-helper/git.mk for
|
|
||||||
# example.
|
|
||||||
#
|
|
||||||
|
|
||||||
git-all: git-mk-install
|
|
||||||
|
|
||||||
git-mk-install:
|
|
||||||
@echo Installing git makefile
|
|
||||||
@any_failed=; find $(top_srcdir) -name Makefile.am | while read x; do \
|
|
||||||
if grep 'include .*/git.mk' $$x >/dev/null; then \
|
|
||||||
echo $$x already includes git.mk; \
|
|
||||||
else \
|
|
||||||
failed=; \
|
|
||||||
echo "Updating $$x"; \
|
|
||||||
{ cat $$x; \
|
|
||||||
echo ''; \
|
|
||||||
echo '-include $$(top_srcdir)/git.mk'; \
|
|
||||||
} > $$x.tmp || failed=1; \
|
|
||||||
if test x$$failed = x; then \
|
|
||||||
mv $$x.tmp $$x || failed=1; \
|
|
||||||
fi; \
|
|
||||||
if test x$$failed = x; then : else \
|
|
||||||
echo Failed updating $$x; >&2 \
|
|
||||||
any_failed=1; \
|
|
||||||
fi; \
|
|
||||||
fi; done; test -z "$$any_failed"
|
|
||||||
|
|
||||||
.PHONY: git-all git-mk-install
|
|
||||||
|
|
||||||
|
|
||||||
### .gitignore generation
|
|
||||||
|
|
||||||
$(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk
|
|
||||||
$(AM_V_GEN) \
|
|
||||||
{ \
|
|
||||||
if test "x$(DOC_MODULE)" = x -o "x$(DOC_MAIN_SGML_FILE)" = x; then :; else \
|
|
||||||
for x in \
|
|
||||||
$(DOC_MODULE)-decl-list.txt \
|
|
||||||
$(DOC_MODULE)-decl.txt \
|
|
||||||
tmpl/$(DOC_MODULE)-unused.sgml \
|
|
||||||
"tmpl/*.bak" \
|
|
||||||
xml html \
|
|
||||||
; do echo /$$x; done; \
|
|
||||||
fi; \
|
|
||||||
if test "x$(DOC_MODULE)" = x -o "x$(DOC_LINGUAS)" = x; then :; else \
|
|
||||||
for x in \
|
|
||||||
$(_DOC_C_DOCS) \
|
|
||||||
$(_DOC_LC_DOCS) \
|
|
||||||
$(_DOC_OMF_ALL) \
|
|
||||||
$(_DOC_DSK_ALL) \
|
|
||||||
$(_DOC_HTML_ALL) \
|
|
||||||
$(_DOC_POFILES) \
|
|
||||||
$(_DOC_MOFILES) \
|
|
||||||
$(DOC_H_FILE) \
|
|
||||||
"*/.xml2po.mo" \
|
|
||||||
"*/*.omf.out" \
|
|
||||||
; do echo /$$x; done; \
|
|
||||||
fi; \
|
|
||||||
if test "x$(gsettings_SCHEMAS)" = x; then :; else \
|
|
||||||
for x in \
|
|
||||||
$(gsettings_SCHEMAS:.xml=.valid) \
|
|
||||||
$(gsettings__enum_file) \
|
|
||||||
; do echo /$$x; done; \
|
|
||||||
fi; \
|
|
||||||
if test -f $(srcdir)/po/Makefile.in.in; then \
|
|
||||||
for x in \
|
|
||||||
po/Makefile.in.in \
|
|
||||||
po/Makefile.in \
|
|
||||||
po/Makefile \
|
|
||||||
po/POTFILES \
|
|
||||||
po/stamp-it \
|
|
||||||
po/.intltool-merge-cache \
|
|
||||||
"po/*.gmo" \
|
|
||||||
"po/*.mo" \
|
|
||||||
po/$(GETTEXT_PACKAGE).pot \
|
|
||||||
intltool-extract.in \
|
|
||||||
intltool-merge.in \
|
|
||||||
intltool-update.in \
|
|
||||||
; do echo /$$x; done; \
|
|
||||||
fi; \
|
|
||||||
if test -f $(srcdir)/configure; then \
|
|
||||||
for x in \
|
|
||||||
autom4te.cache \
|
|
||||||
configure \
|
|
||||||
config.h \
|
|
||||||
stamp-h1 \
|
|
||||||
libtool \
|
|
||||||
config.lt \
|
|
||||||
; do echo /$$x; done; \
|
|
||||||
fi; \
|
|
||||||
for x in \
|
|
||||||
.gitignore \
|
|
||||||
$(GITIGNOREFILES) \
|
|
||||||
$(CLEANFILES) \
|
|
||||||
$(PROGRAMS) \
|
|
||||||
$(check_PROGRAMS) \
|
|
||||||
$(EXTRA_PROGRAMS) \
|
|
||||||
$(LTLIBRARIES) \
|
|
||||||
so_locations \
|
|
||||||
.libs _libs \
|
|
||||||
$(MOSTLYCLEANFILES) \
|
|
||||||
"*.$(OBJEXT)" \
|
|
||||||
"*.lo" \
|
|
||||||
$(DISTCLEANFILES) \
|
|
||||||
$(am__CONFIG_DISTCLEAN_FILES) \
|
|
||||||
$(CONFIG_CLEAN_FILES) \
|
|
||||||
TAGS ID GTAGS GRTAGS GSYMS GPATH tags \
|
|
||||||
"*.tab.c" \
|
|
||||||
$(MAINTAINERCLEANFILES) \
|
|
||||||
$(BUILT_SOURCES) \
|
|
||||||
$(DEPDIR) \
|
|
||||||
Makefile \
|
|
||||||
Makefile.in \
|
|
||||||
"*.orig" \
|
|
||||||
"*.rej" \
|
|
||||||
"*.bak" \
|
|
||||||
"*~" \
|
|
||||||
".*.sw[nop]" \
|
|
||||||
".dirstamp" \
|
|
||||||
; do echo /$$x; done; \
|
|
||||||
} | \
|
|
||||||
sed "s@^/`echo "$(srcdir)" | sed 's/\(.\)/[\1]/g'`/@/@" | \
|
|
||||||
sed 's@/[.]/@/@g' | \
|
|
||||||
LC_ALL=C sort | uniq > $@.tmp && \
|
|
||||||
mv $@.tmp $@;
|
|
||||||
|
|
||||||
all: $(srcdir)/.gitignore gitignore-recurse-maybe
|
|
||||||
gitignore-recurse-maybe:
|
|
||||||
@if test "x$(SUBDIRS)" = "x$(DIST_SUBDIRS)"; then :; else \
|
|
||||||
$(MAKE) $(AM_MAKEFLAGS) gitignore-recurse; \
|
|
||||||
fi;
|
|
||||||
gitignore-recurse:
|
|
||||||
@for subdir in $(DIST_SUBDIRS); do \
|
|
||||||
case " $(SUBDIRS) " in \
|
|
||||||
*" $$subdir "*) :;; \
|
|
||||||
*) test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) .gitignore gitignore-recurse || echo "Skipping $$subdir");; \
|
|
||||||
esac; \
|
|
||||||
done
|
|
||||||
gitignore: $(srcdir)/.gitignore gitignore-recurse
|
|
||||||
|
|
||||||
maintainer-clean: gitignore-clean
|
|
||||||
gitignore-clean:
|
|
||||||
-rm -f $(srcdir)/.gitignore
|
|
||||||
|
|
||||||
.PHONY: gitignore-clean gitignore gitignore-recurse gitignore-recurse-maybe
|
|
||||||
182
meson.build
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
project('fprintd', 'c',
|
||||||
|
version: '1.90.1',
|
||||||
|
license: 'GPLv2+',
|
||||||
|
default_options: [
|
||||||
|
'buildtype=debugoptimized',
|
||||||
|
'warning_level=1',
|
||||||
|
'c_std=gnu99',
|
||||||
|
],
|
||||||
|
meson_version: '>= 0.50.0')
|
||||||
|
|
||||||
|
gnome = import('gnome')
|
||||||
|
i18n = import('i18n')
|
||||||
|
|
||||||
|
cc = meson.get_compiler('c')
|
||||||
|
host_system = host_machine.system()
|
||||||
|
glib_min_version = '2.56'
|
||||||
|
libfprint_min_version = '1.90.0'
|
||||||
|
|
||||||
|
fprintd_installdir = get_option('prefix') / get_option('libexecdir')
|
||||||
|
fprintd_plugindir = get_option('prefix') / get_option('libdir') / meson.project_name() / 'modules'
|
||||||
|
storage_path = get_option('prefix') / get_option('localstatedir') / 'lib/fprint'
|
||||||
|
localedir = get_option('prefix') / get_option('localedir')
|
||||||
|
datadir = get_option('prefix') / get_option('datadir')
|
||||||
|
sysconfdir = get_option('sysconfdir')
|
||||||
|
if get_option('prefix') != '/usr'
|
||||||
|
sysconfdir = get_option('prefix') / sysconfdir
|
||||||
|
endif
|
||||||
|
|
||||||
|
common_cflags = cc.get_supported_arguments([
|
||||||
|
'-fno-strict-aliasing',
|
||||||
|
'-Wall',
|
||||||
|
'-Wcast-align',
|
||||||
|
'-Werror=address',
|
||||||
|
'-Werror=array-bounds',
|
||||||
|
'-Werror=empty-body',
|
||||||
|
'-Werror=implicit',
|
||||||
|
'-Werror=init-self',
|
||||||
|
'-Werror=int-to-pointer-cast',
|
||||||
|
'-Werror=main',
|
||||||
|
'-Werror=missing-braces',
|
||||||
|
'-Werror=nonnull',
|
||||||
|
'-Werror=pointer-to-int-cast',
|
||||||
|
'-Werror=redundant-decls',
|
||||||
|
'-Werror=return-type',
|
||||||
|
'-Werror=sequence-point',
|
||||||
|
'-Werror=trigraphs',
|
||||||
|
'-Werror=write-strings',
|
||||||
|
'-Wformat-nonliteral',
|
||||||
|
'-Wformat-security',
|
||||||
|
'-Wformat=2',
|
||||||
|
'-Wignored-qualifiers',
|
||||||
|
'-Wimplicit-function-declaration',
|
||||||
|
'-Wlogical-op',
|
||||||
|
'-Wmissing-declarations',
|
||||||
|
'-Wmissing-format-attribute',
|
||||||
|
'-Wmissing-include-dirs',
|
||||||
|
'-Wmissing-noreturn',
|
||||||
|
'-Wmissing-prototypes',
|
||||||
|
'-Wnested-externs',
|
||||||
|
'-Wold-style-definition',
|
||||||
|
'-Wpointer-arith',
|
||||||
|
'-Wshadow',
|
||||||
|
'-Wstrict-prototypes',
|
||||||
|
'-Wtype-limits',
|
||||||
|
'-Wundef',
|
||||||
|
'-Wunused',
|
||||||
|
])
|
||||||
|
add_project_arguments(common_cflags, language: 'c')
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
glib_dep = dependency('glib-2.0', version: '>=' + glib_min_version)
|
||||||
|
gio_dep = dependency('gio-2.0', version: '>=' + glib_min_version)
|
||||||
|
gmodule_dep = dependency('gmodule-2.0', version: '>=' + glib_min_version)
|
||||||
|
libfprint_dep = dependency('libfprint-2', version: '>=' + libfprint_min_version)
|
||||||
|
polkit_gobject_dep = dependency('polkit-gobject-1', version: '>= 0.91')
|
||||||
|
dbus_dep = dependency('dbus-1', required: false)
|
||||||
|
dbus_glib_dep = dependency('dbus-glib-1')
|
||||||
|
libsystemd_dep = dependency('libsystemd', required: get_option('pam'))
|
||||||
|
pam_dep = cc.find_library('pam',
|
||||||
|
required: get_option('pam'),
|
||||||
|
has_headers: 'security/pam_modules.h',
|
||||||
|
)
|
||||||
|
|
||||||
|
pod2man = find_program('pod2man', required: get_option('man'))
|
||||||
|
xsltproc = find_program('xsltproc', required: get_option('gtk_doc'))
|
||||||
|
|
||||||
|
# StateDirectory was introduced in systemd 235
|
||||||
|
systemd_dep = dependency('systemd', version: '>= 235')
|
||||||
|
systemd_unit_dir = get_option('systemd_system_unit_dir')
|
||||||
|
|
||||||
|
if systemd_unit_dir == ''
|
||||||
|
systemd_unit_dir = systemd_dep.get_pkgconfig_variable('systemdsystemunitdir')
|
||||||
|
endif
|
||||||
|
|
||||||
|
dbus_service_dir = get_option('dbus_service_dir')
|
||||||
|
dbus_data_dir = datadir
|
||||||
|
dbus_interfaces_dir = ''
|
||||||
|
|
||||||
|
if dbus_dep.found()
|
||||||
|
if dbus_service_dir == ''
|
||||||
|
dbus_service_dir = dbus_dep.get_pkgconfig_variable('system_bus_services_dir')
|
||||||
|
endif
|
||||||
|
dbus_interfaces_dir = dbus_dep.get_pkgconfig_variable('interfaces_dir')
|
||||||
|
dbus_data_dir = dbus_dep.get_pkgconfig_variable('datadir')
|
||||||
|
endif
|
||||||
|
|
||||||
|
dbus_conf_dir = dbus_data_dir / 'dbus-1/system.d'
|
||||||
|
|
||||||
|
if dbus_service_dir == ''
|
||||||
|
dbus_service_dir = datadir / 'dbus-1/system-services'
|
||||||
|
endif
|
||||||
|
if dbus_interfaces_dir == ''
|
||||||
|
dbus_interfaces_dir = datadir / 'dbus-1/interfaces'
|
||||||
|
endif
|
||||||
|
|
||||||
|
polkit_policy_directory = polkit_gobject_dep.get_pkgconfig_variable('policydir')
|
||||||
|
|
||||||
|
# Tests dependencies
|
||||||
|
pam_wrapper_dep = dependency('pam_wrapper', required: get_option('pam'))
|
||||||
|
|
||||||
|
xmllint = find_program('xmllint', required: false)
|
||||||
|
python3 = find_program('python3') # No meson without it!
|
||||||
|
python3_test_modules = {
|
||||||
|
'cairo': true,
|
||||||
|
'dbus': true,
|
||||||
|
'dbusmock': true,
|
||||||
|
'gi': true,
|
||||||
|
'pypamtest': get_option('pam'),
|
||||||
|
}
|
||||||
|
python3_available_modules = []
|
||||||
|
|
||||||
|
foreach module, required : python3_test_modules
|
||||||
|
if required and run_command(python3, '-c', 'import @0@'.format(module)).returncode() != 0
|
||||||
|
error('Python3 module \'' + module + '\' required by test suite not found')
|
||||||
|
endif
|
||||||
|
endforeach
|
||||||
|
|
||||||
|
cdata = configuration_data()
|
||||||
|
cdata.set_quoted('GETTEXT_PACKAGE', meson.project_name())
|
||||||
|
cdata.set_quoted('PACKAGE_VERSION', meson.project_version())
|
||||||
|
cdata.set_quoted('VERSION', meson.project_version())
|
||||||
|
cdata.set_quoted('SYSCONFDIR', sysconfdir)
|
||||||
|
|
||||||
|
config_h = configure_file(
|
||||||
|
input: 'config.h.in',
|
||||||
|
output: 'config.h',
|
||||||
|
configuration: cdata
|
||||||
|
)
|
||||||
|
|
||||||
|
subdir('src')
|
||||||
|
subdir('data')
|
||||||
|
subdir('utils')
|
||||||
|
if get_option('pam')
|
||||||
|
subdir('pam')
|
||||||
|
endif
|
||||||
|
if get_option('gtk_doc')
|
||||||
|
subdir('doc')
|
||||||
|
endif
|
||||||
|
subdir('tests')
|
||||||
|
subdir('po')
|
||||||
|
|
||||||
|
output = []
|
||||||
|
output += 'System paths:'
|
||||||
|
output += ' prefix: ' + get_option('prefix')
|
||||||
|
output += ' fprintd daemon directory: ' + fprintd_installdir
|
||||||
|
output += ' fprintd modules directory: ' + fprintd_plugindir
|
||||||
|
output += ' fprintd prints storage directory: ' + storage_path
|
||||||
|
output += ' DBus configuration directory: ' + dbus_conf_dir
|
||||||
|
output += ' DBus service directory: ' + dbus_service_dir
|
||||||
|
output += ' DBus interfaces directory: ' + dbus_interfaces_dir
|
||||||
|
output += ' Polkit policy directory: ' + polkit_policy_directory
|
||||||
|
output += ' Systemd service directory: ' + systemd_unit_dir
|
||||||
|
if get_option('pam')
|
||||||
|
output += ' PAM module directory: ' + pam_modules_dir
|
||||||
|
endif
|
||||||
|
output += '\nOptional features:\n'
|
||||||
|
output += ' PAM module: ' + pam_dep.found().to_string()
|
||||||
|
output += ' Manuals: ' + get_option('man').to_string()
|
||||||
|
output += ' GTK Doc: ' + get_option('gtk_doc').to_string()
|
||||||
|
output += ' XML Linter ' + xmllint.found().to_string()
|
||||||
|
|
||||||
|
message('\n'+'\n'.join(output)+'\n')
|
||||||
21
meson_options.txt
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
option('pam',
|
||||||
|
description: 'Build the fprintd PAM module',
|
||||||
|
type: 'boolean',
|
||||||
|
value: true)
|
||||||
|
option('man',
|
||||||
|
description: 'Generate the man files',
|
||||||
|
type: 'boolean',
|
||||||
|
value: true)
|
||||||
|
option('systemd_system_unit_dir',
|
||||||
|
description: 'Directory for systemd service files',
|
||||||
|
type: 'string')
|
||||||
|
option('dbus_service_dir',
|
||||||
|
description: 'Directory for dbus service files',
|
||||||
|
type: 'string')
|
||||||
|
option('pam_modules_dir',
|
||||||
|
description: 'Directory for PAM modules',
|
||||||
|
type: 'string')
|
||||||
|
option('gtk_doc',
|
||||||
|
type: 'boolean',
|
||||||
|
value: false,
|
||||||
|
description: 'Use gtk-doc to build documentation')
|
||||||
@ -1,22 +0,0 @@
|
|||||||
if HAVE_PAM
|
|
||||||
|
|
||||||
pammod_LTLIBRARIES = pam_fprintd.la
|
|
||||||
pammoddir=$(libdir)/security
|
|
||||||
|
|
||||||
pam_fprintd_la_SOURCES = pam_fprintd.c $(MARSHALFILES)
|
|
||||||
pam_fprintd_la_CFLAGS = -fPIC $(WARN_CFLAGS) $(GLIB_CFLAGS) -DLOCALEDIR="\"$(localedir)\""
|
|
||||||
pam_fprintd_la_LDFLAGS = -avoid-version -module -Wl,-z,nodelete
|
|
||||||
pam_fprintd_la_LIBADD = $(PAM_LIBS) $(GLIB_LIBS)
|
|
||||||
|
|
||||||
MARSHALFILES = marshal.c marshal.h
|
|
||||||
GLIB_GENMARSHAL=`pkg-config --variable=glib_genmarshal glib-2.0`
|
|
||||||
BUILT_SOURCES = $(MARSHALFILES)
|
|
||||||
|
|
||||||
marshal.h: $(top_srcdir)/src/fprintd-marshal.list
|
|
||||||
( $(GLIB_GENMARSHAL) --prefix=fprintd_marshal $(top_srcdir)/src/fprintd-marshal.list --header > marshal.h )
|
|
||||||
marshal.c: marshal.h
|
|
||||||
( $(GLIB_GENMARSHAL) --prefix=fprintd_marshal $(top_srcdir)/src/fprintd-marshal.list --body --header > marshal.c )
|
|
||||||
|
|
||||||
endif
|
|
||||||
|
|
||||||
EXTRA_DIST = pam_fprintd.c fingerprint-strings.h
|
|
||||||
@ -20,6 +20,23 @@
|
|||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef _GNU_SOURCE
|
||||||
|
#error _GNU_SOURCE must be defined
|
||||||
|
#endif
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#define GNUC_UNUSED __attribute__((__unused__))
|
||||||
|
|
||||||
|
static bool str_equal (const char *a, const char *b)
|
||||||
|
{
|
||||||
|
if (a == NULL && b == NULL)
|
||||||
|
return true;
|
||||||
|
if (a == NULL || b == NULL)
|
||||||
|
return false;
|
||||||
|
return (strcmp (a, b) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
const char *dbus_name;
|
const char *dbus_name;
|
||||||
const char *place_str_generic;
|
const char *place_str_generic;
|
||||||
@ -88,7 +105,7 @@ struct {
|
|||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
|
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
|
||||||
|
|
||||||
G_GNUC_UNUSED static char *finger_str_to_msg(const char *finger_name, const char *driver_name, gboolean is_swipe)
|
GNUC_UNUSED static char *finger_str_to_msg(const char *finger_name, const char *driver_name, bool is_swipe)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -96,17 +113,25 @@ G_GNUC_UNUSED static char *finger_str_to_msg(const char *finger_name, const char
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
for (i = 0; fingers[i].dbus_name != NULL; i++) {
|
for (i = 0; fingers[i].dbus_name != NULL; i++) {
|
||||||
if (g_str_equal (fingers[i].dbus_name, finger_name)) {
|
if (str_equal (fingers[i].dbus_name, finger_name)) {
|
||||||
if (is_swipe == FALSE) {
|
if (is_swipe == false) {
|
||||||
if (driver_name)
|
if (driver_name) {
|
||||||
return g_strdup_printf (TR (fingers[i].place_str_specific), driver_name);
|
char *s;
|
||||||
else
|
int ret;
|
||||||
return g_strdup (TR (fingers[i].place_str_generic));
|
ret = asprintf (&s, TR (fingers[i].place_str_specific), driver_name);
|
||||||
|
return ret >= 0 ? s : NULL;
|
||||||
|
} else {
|
||||||
|
return strdup (TR (fingers[i].place_str_generic));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (driver_name)
|
if (driver_name) {
|
||||||
return g_strdup_printf (TR (fingers[i].swipe_str_specific), driver_name);
|
char *s;
|
||||||
else
|
int ret;
|
||||||
return g_strdup (TR (fingers[i].swipe_str_generic));
|
ret = asprintf (&s, TR (fingers[i].swipe_str_specific), driver_name);
|
||||||
|
return ret >= 0 ? s : NULL;
|
||||||
|
} else {
|
||||||
|
return strdup (TR (fingers[i].swipe_str_generic));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -121,13 +146,13 @@ G_GNUC_UNUSED static char *finger_str_to_msg(const char *finger_name, const char
|
|||||||
* verify-match
|
* verify-match
|
||||||
* verify-unknown-error
|
* verify-unknown-error
|
||||||
*/
|
*/
|
||||||
G_GNUC_UNUSED static const char *verify_result_str_to_msg(const char *result, gboolean is_swipe)
|
GNUC_UNUSED static const char *verify_result_str_to_msg(const char *result, bool is_swipe)
|
||||||
{
|
{
|
||||||
if (result == NULL)
|
if (result == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (strcmp (result, "verify-retry-scan") == 0) {
|
if (strcmp (result, "verify-retry-scan") == 0) {
|
||||||
if (is_swipe == FALSE)
|
if (is_swipe == false)
|
||||||
return N_("Place your finger on the reader again");
|
return N_("Place your finger on the reader again");
|
||||||
else
|
else
|
||||||
return N_("Swipe your finger again");
|
return N_("Swipe your finger again");
|
||||||
@ -147,13 +172,13 @@ G_GNUC_UNUSED static const char *verify_result_str_to_msg(const char *result, gb
|
|||||||
* enroll-failed
|
* enroll-failed
|
||||||
* enroll-unknown-error
|
* enroll-unknown-error
|
||||||
*/
|
*/
|
||||||
G_GNUC_UNUSED static const char *enroll_result_str_to_msg(const char *result, gboolean is_swipe)
|
GNUC_UNUSED static const char *enroll_result_str_to_msg(const char *result, bool is_swipe)
|
||||||
{
|
{
|
||||||
if (result == NULL)
|
if (result == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (strcmp (result, "enroll-retry-scan") == 0 || strcmp (result, "enroll-stage-passed") == 0) {
|
if (strcmp (result, "enroll-retry-scan") == 0 || strcmp (result, "enroll-stage-passed") == 0) {
|
||||||
if (is_swipe == FALSE)
|
if (is_swipe == false)
|
||||||
return N_("Place your finger on the reader again");
|
return N_("Place your finger on the reader again");
|
||||||
else
|
else
|
||||||
return N_("Swipe your finger again");
|
return N_("Swipe your finger again");
|
||||||
|
|||||||
30
pam/meson.build
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
mapfile = files('pam_fprintd.ver')
|
||||||
|
pam_modules_dir = get_option('pam_modules_dir')
|
||||||
|
if pam_modules_dir == ''
|
||||||
|
pam_modules_dir = '/' / get_option('libdir') / 'security'
|
||||||
|
endif
|
||||||
|
|
||||||
|
pam_fprintd = shared_module('pam_fprintd',
|
||||||
|
name_prefix: '',
|
||||||
|
include_directories: [
|
||||||
|
include_directories('..'),
|
||||||
|
],
|
||||||
|
sources: [
|
||||||
|
'pam_fprintd.c',
|
||||||
|
'fingerprint-strings.h',
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
libsystemd_dep,
|
||||||
|
pam_dep,
|
||||||
|
],
|
||||||
|
c_args: [
|
||||||
|
'-DLOCALEDIR="@0@"'.format(localedir),
|
||||||
|
],
|
||||||
|
link_args: [
|
||||||
|
'-Wl,--version-script,@0@/@1@'.format(meson.source_root(), mapfile[0]),
|
||||||
|
'-Wl,--unresolved-symbols=report-all',
|
||||||
|
],
|
||||||
|
link_depends: mapfile,
|
||||||
|
install: true,
|
||||||
|
install_dir: pam_modules_dir,
|
||||||
|
)
|
||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* pam_fprint: PAM module for fingerprint authentication through fprintd
|
* pam_fprint: PAM module for fingerprint authentication through fprintd
|
||||||
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
|
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
|
||||||
* Copyright (C) 2008 Bastien Nocera <hadess@hadess.net>
|
* Copyright (C) 2008-2014, 2017-2020 Bastien Nocera <hadess@hadess.net>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -20,50 +20,64 @@
|
|||||||
|
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
#include <glib/gi18n-lib.h>
|
#include <libintl.h>
|
||||||
#include <dbus/dbus-glib-bindings.h>
|
#include <systemd/sd-bus.h>
|
||||||
#include <dbus/dbus-glib-lowlevel.h>
|
#include <systemd/sd-login.h>
|
||||||
|
|
||||||
#define PAM_SM_AUTH
|
#define PAM_SM_AUTH
|
||||||
#include <security/pam_modules.h>
|
#include <security/pam_modules.h>
|
||||||
|
#include <security/pam_ext.h>
|
||||||
|
|
||||||
#include "marshal.h"
|
#define _(s) ((char *) dgettext (GETTEXT_PACKAGE, s))
|
||||||
|
|
||||||
#define TR(s) dgettext(GETTEXT_PACKAGE, s)
|
#define TR(s) dgettext(GETTEXT_PACKAGE, s)
|
||||||
|
#define N_(s) (s)
|
||||||
|
|
||||||
#include "fingerprint-strings.h"
|
#include "fingerprint-strings.h"
|
||||||
|
|
||||||
#define DEFAULT_MAX_TRIES 3
|
#define DEFAULT_MAX_TRIES 3
|
||||||
#define DEFAULT_TIMEOUT 30
|
#define DEFAULT_TIMEOUT 30
|
||||||
|
#define MIN_TIMEOUT 10
|
||||||
|
|
||||||
|
#define DEBUG_MATCH "debug="
|
||||||
#define MAX_TRIES_MATCH "max-tries="
|
#define MAX_TRIES_MATCH "max-tries="
|
||||||
#define TIMEOUT_MATCH "timeout="
|
#define TIMEOUT_MATCH "timeout="
|
||||||
|
|
||||||
#define D(pamh, ...) { \
|
static bool debug = false;
|
||||||
if (debug) { \
|
static unsigned max_tries = DEFAULT_MAX_TRIES;
|
||||||
char *s; \
|
static unsigned timeout = DEFAULT_TIMEOUT;
|
||||||
s = g_strdup_printf (__VA_ARGS__); \
|
|
||||||
send_debug_msg (pamh, s); \
|
#define USEC_PER_SEC ((uint64_t) 1000000ULL)
|
||||||
g_free (s); \
|
#define NSEC_PER_USEC ((uint64_t) 1000ULL)
|
||||||
} \
|
|
||||||
|
static uint64_t
|
||||||
|
now (void)
|
||||||
|
{
|
||||||
|
struct timespec ts;
|
||||||
|
clock_gettime (CLOCK_MONOTONIC, &ts);
|
||||||
|
return (uint64_t) ts.tv_sec * USEC_PER_SEC + (uint64_t) ts.tv_nsec / NSEC_PER_USEC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool str_has_prefix (const char *s, const char *prefix)
|
||||||
|
{
|
||||||
|
if (s == NULL || prefix == NULL)
|
||||||
|
return false;
|
||||||
|
return (strncmp (s, prefix, strlen (prefix)) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean debug = FALSE;
|
static bool send_msg(pam_handle_t *pamh, const char *msg, int style)
|
||||||
static guint max_tries = DEFAULT_MAX_TRIES;
|
|
||||||
static guint timeout = DEFAULT_TIMEOUT;
|
|
||||||
|
|
||||||
static gboolean send_info_msg(pam_handle_t *pamh, const char *msg)
|
|
||||||
{
|
{
|
||||||
const struct pam_message mymsg = {
|
const struct pam_message mymsg = {
|
||||||
.msg_style = PAM_TEXT_INFO,
|
.msg_style = style,
|
||||||
.msg = msg,
|
.msg = msg,
|
||||||
};
|
};
|
||||||
const struct pam_message *msgp = &mymsg;
|
const struct pam_message *msgp = &mymsg;
|
||||||
@ -73,407 +87,512 @@ static gboolean send_info_msg(pam_handle_t *pamh, const char *msg)
|
|||||||
|
|
||||||
r = pam_get_item(pamh, PAM_CONV, (const void **) &pc);
|
r = pam_get_item(pamh, PAM_CONV, (const void **) &pc);
|
||||||
if (r != PAM_SUCCESS)
|
if (r != PAM_SUCCESS)
|
||||||
return FALSE;
|
return false;
|
||||||
|
|
||||||
if (!pc || !pc->conv)
|
if (!pc || !pc->conv)
|
||||||
return FALSE;
|
return false;
|
||||||
|
|
||||||
return (pc->conv(1, &msgp, &resp, pc->appdata_ptr) == PAM_SUCCESS);
|
return (pc->conv(1, &msgp, &resp, pc->appdata_ptr) == PAM_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean send_err_msg(pam_handle_t *pamh, const char *msg)
|
static bool send_info_msg(pam_handle_t *pamh, const char *msg)
|
||||||
{
|
{
|
||||||
const struct pam_message mymsg = {
|
return send_msg(pamh, msg, PAM_TEXT_INFO);
|
||||||
.msg_style = PAM_ERROR_MSG,
|
}
|
||||||
.msg = msg,
|
|
||||||
};
|
static bool send_err_msg(pam_handle_t *pamh, const char *msg)
|
||||||
const struct pam_message *msgp = &mymsg;
|
{
|
||||||
const struct pam_conv *pc;
|
return send_msg(pamh, msg, PAM_ERROR_MSG);
|
||||||
struct pam_response *resp;
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
open_device (pam_handle_t *pamh,
|
||||||
|
sd_bus *bus,
|
||||||
|
bool *has_multiple_devices)
|
||||||
|
{
|
||||||
|
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
sd_bus_message *m = NULL;
|
||||||
|
size_t num_devices;
|
||||||
|
const char *path = NULL;
|
||||||
|
char *ret;
|
||||||
|
const char *s;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
r = pam_get_item(pamh, PAM_CONV, (const void **) &pc);
|
*has_multiple_devices = false;
|
||||||
if (r != PAM_SUCCESS)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
if (!pc || !pc->conv)
|
r = sd_bus_call_method (bus,
|
||||||
return FALSE;
|
"net.reactivated.Fprint",
|
||||||
|
"/net/reactivated/Fprint/Manager",
|
||||||
return (pc->conv(1, &msgp, &resp, pc->appdata_ptr) == PAM_SUCCESS);
|
"net.reactivated.Fprint.Manager",
|
||||||
}
|
"GetDevices",
|
||||||
|
&error,
|
||||||
static void send_debug_msg(pam_handle_t *pamh, const char *msg)
|
&m,
|
||||||
{
|
NULL);
|
||||||
gconstpointer item;
|
if (r < 0) {
|
||||||
const char *service;
|
pam_syslog (pamh, LOG_ERR, "GetDevices failed: %s", error.message);
|
||||||
|
sd_bus_error_free (&error);
|
||||||
if (pam_get_item(pamh, PAM_SERVICE, &item) != PAM_SUCCESS || !item)
|
|
||||||
service = "<unknown>";
|
|
||||||
else
|
|
||||||
service = item;
|
|
||||||
|
|
||||||
openlog (service, LOG_CONS | LOG_PID, LOG_AUTHPRIV);
|
|
||||||
|
|
||||||
syslog (LOG_AUTHPRIV|LOG_WARNING, "%s(%s): %s", "pam_fprintd", service, msg);
|
|
||||||
|
|
||||||
closelog ();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static DBusGProxy *create_manager (pam_handle_t *pamh, DBusGConnection **ret_conn, GMainLoop **ret_loop)
|
|
||||||
{
|
|
||||||
DBusGConnection *connection;
|
|
||||||
DBusConnection *conn;
|
|
||||||
DBusGProxy *manager;
|
|
||||||
DBusError error;
|
|
||||||
GMainLoop *loop;
|
|
||||||
GMainContext *ctx;
|
|
||||||
|
|
||||||
/* Otherwise dbus-glib doesn't setup it value types */
|
|
||||||
connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, NULL);
|
|
||||||
|
|
||||||
if (connection != NULL)
|
|
||||||
dbus_g_connection_unref (connection);
|
|
||||||
|
|
||||||
/* And set us up a private D-Bus connection */
|
|
||||||
dbus_error_init (&error);
|
|
||||||
conn = dbus_bus_get_private (DBUS_BUS_SYSTEM, &error);
|
|
||||||
if (conn == NULL) {
|
|
||||||
D(pamh, "Error with getting the bus: %s", error.message);
|
|
||||||
dbus_error_free (&error);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set up our own main loop context */
|
r = sd_bus_message_enter_container (m, 'a', "o");
|
||||||
ctx = g_main_context_new ();
|
if (r < 0) {
|
||||||
loop = g_main_loop_new (ctx, FALSE);
|
pam_syslog (pamh, LOG_ERR, "Failed to parse answer from GetDevices(): %d", r);
|
||||||
dbus_connection_setup_with_g_main (conn, ctx);
|
goto out;
|
||||||
|
|
||||||
connection = dbus_connection_get_g_connection (conn);
|
|
||||||
|
|
||||||
manager = dbus_g_proxy_new_for_name(connection,
|
|
||||||
"net.reactivated.Fprint",
|
|
||||||
"/net/reactivated/Fprint/Manager",
|
|
||||||
"net.reactivated.Fprint.Manager");
|
|
||||||
*ret_conn = connection;
|
|
||||||
*ret_loop = loop;
|
|
||||||
|
|
||||||
return manager;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void close_and_unref (DBusGConnection *connection)
|
|
||||||
{
|
|
||||||
DBusConnection *conn;
|
|
||||||
|
|
||||||
conn = dbus_g_connection_get_connection (connection);
|
|
||||||
dbus_connection_close (conn);
|
|
||||||
dbus_g_connection_unref (connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void unref_loop (GMainLoop *loop)
|
|
||||||
{
|
|
||||||
GMainContext *ctx;
|
|
||||||
|
|
||||||
/* The main context was created separately, so
|
|
||||||
* we'll need to unref it ourselves */
|
|
||||||
ctx = g_main_loop_get_context (loop);
|
|
||||||
g_main_loop_unref (loop);
|
|
||||||
g_main_context_unref (ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define DBUS_TYPE_G_OBJECT_PATH_ARRAY (dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH))
|
|
||||||
|
|
||||||
static DBusGProxy *open_device(pam_handle_t *pamh, DBusGConnection *connection, DBusGProxy *manager, gboolean *has_multiple_devices)
|
|
||||||
{
|
|
||||||
GError *error = NULL;
|
|
||||||
const char *path;
|
|
||||||
DBusGProxy *dev;
|
|
||||||
GPtrArray *paths_array;
|
|
||||||
const char **paths;
|
|
||||||
|
|
||||||
if (!dbus_g_proxy_call (manager, "GetDevices", &error,
|
|
||||||
G_TYPE_INVALID, DBUS_TYPE_G_OBJECT_PATH_ARRAY,
|
|
||||||
&paths_array, G_TYPE_INVALID)) {
|
|
||||||
D(pamh, "get_devices failed: %s", error->message);
|
|
||||||
g_error_free (error);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (paths_array == NULL || paths_array->len == 0) {
|
r = sd_bus_message_read_basic (m, 'o', &path);
|
||||||
if (paths_array != NULL)
|
if (r < 0)
|
||||||
g_ptr_array_free (paths_array, TRUE);
|
goto out;
|
||||||
D(pamh, "No devices found\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
*has_multiple_devices = (paths_array->len > 1);
|
num_devices = 1;
|
||||||
paths = (const char **)paths_array->pdata;
|
while ((r = sd_bus_message_read_basic(m, 'o', &s)) > 0)
|
||||||
path = paths[0];
|
num_devices++;
|
||||||
|
*has_multiple_devices = (num_devices > 1);
|
||||||
|
if (debug)
|
||||||
|
pam_syslog(pamh, LOG_DEBUG, "Using device %s (out of %ld devices)", path, num_devices);
|
||||||
|
|
||||||
D(pamh, "Using device %s\n", path);
|
sd_bus_message_exit_container (m);
|
||||||
|
|
||||||
dev = dbus_g_proxy_new_for_name(connection,
|
out:
|
||||||
"net.reactivated.Fprint",
|
ret = path ? strdup (path) : NULL;
|
||||||
path,
|
sd_bus_message_unref (m);
|
||||||
"net.reactivated.Fprint.Device");
|
return ret;
|
||||||
|
|
||||||
g_ptr_array_free (paths_array, TRUE);
|
|
||||||
|
|
||||||
return dev;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
guint max_tries;
|
unsigned max_tries;
|
||||||
char *result;
|
char *result;
|
||||||
gboolean timed_out;
|
bool timed_out;
|
||||||
gboolean is_swipe;
|
bool is_swipe;
|
||||||
pam_handle_t *pamh;
|
pam_handle_t *pamh;
|
||||||
GMainLoop *loop;
|
|
||||||
|
|
||||||
char *driver;
|
char *driver;
|
||||||
} verify_data;
|
} verify_data;
|
||||||
|
|
||||||
static void verify_result(GObject *object, const char *result, gboolean done, gpointer user_data)
|
static int
|
||||||
|
verify_result (sd_bus_message *m,
|
||||||
|
void *userdata,
|
||||||
|
sd_bus_error *ret_error)
|
||||||
{
|
{
|
||||||
verify_data *data = user_data;
|
verify_data *data = userdata;
|
||||||
const char *msg;
|
const char *msg;
|
||||||
|
const char *result = NULL;
|
||||||
|
/* see https://github.com/systemd/systemd/issues/14643 */
|
||||||
|
uint64_t done = false;
|
||||||
|
int r;
|
||||||
|
|
||||||
D(data->pamh, "Verify result: %s\n", result);
|
if (!sd_bus_message_is_signal(m, "net.reactivated.Fprint.Device", "VerifyStatus")) {
|
||||||
if (done != FALSE) {
|
pam_syslog (data->pamh, LOG_ERR, "Not the signal we expected (iface: %s, member: %s)",
|
||||||
data->result = g_strdup (result);
|
sd_bus_message_get_interface (m),
|
||||||
g_main_loop_quit (data->loop);
|
sd_bus_message_get_member (m));
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
msg = TR(verify_result_str_to_msg (result, data->is_swipe));
|
if ((r = sd_bus_message_read (m, "sb", &result, &done)) < 0) {
|
||||||
|
pam_syslog (data->pamh, LOG_ERR, "Failed to parse VerifyResult signal: %d", r);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug)
|
||||||
|
pam_syslog (data->pamh, LOG_DEBUG, "Verify result: %s (done: %d)", result, done ? 1 : 0);
|
||||||
|
|
||||||
|
if (done) {
|
||||||
|
data->result = strdup (result);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg = _(verify_result_str_to_msg (result, data->is_swipe));
|
||||||
send_err_msg (data->pamh, msg);
|
send_err_msg (data->pamh, msg);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void verify_finger_selected(GObject *object, const char *finger_name, gpointer user_data)
|
static int
|
||||||
|
verify_finger_selected (sd_bus_message *m,
|
||||||
|
void *userdata,
|
||||||
|
sd_bus_error *ret_error)
|
||||||
{
|
{
|
||||||
verify_data *data = user_data;
|
verify_data *data = userdata;
|
||||||
|
const char *finger_name = NULL;
|
||||||
char *msg;
|
char *msg;
|
||||||
|
|
||||||
msg = finger_str_to_msg(finger_name, data->driver, data->is_swipe);
|
if (sd_bus_message_read_basic (m, 's', &finger_name) < 0) {
|
||||||
|
pam_syslog (data->pamh, LOG_ERR, "Failed to parse VerifyFingerSelected signal: %m");
|
||||||
D(data->pamh, "verify_finger_selected %s", msg);
|
return 0;
|
||||||
send_info_msg (data->pamh, msg);
|
|
||||||
g_free (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean verify_timeout_cb (gpointer user_data)
|
|
||||||
{
|
|
||||||
verify_data *data = user_data;
|
|
||||||
|
|
||||||
data->timed_out = TRUE;
|
|
||||||
send_info_msg (data->pamh, "Verification timed out");
|
|
||||||
g_main_loop_quit (data->loop);
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int do_verify(GMainLoop *loop, pam_handle_t *pamh, DBusGProxy *dev, gboolean has_multiple_devices)
|
|
||||||
{
|
|
||||||
GError *error = NULL;
|
|
||||||
GHashTable *props;
|
|
||||||
DBusGProxy *p;
|
|
||||||
verify_data *data;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
data = g_new0 (verify_data, 1);
|
|
||||||
data->max_tries = max_tries;
|
|
||||||
data->pamh = pamh;
|
|
||||||
data->loop = loop;
|
|
||||||
|
|
||||||
/* Get some properties for the device */
|
|
||||||
p = dbus_g_proxy_new_from_proxy (dev, "org.freedesktop.DBus.Properties", NULL);
|
|
||||||
|
|
||||||
if (dbus_g_proxy_call (p, "GetAll", NULL, G_TYPE_STRING, "net.reactivated.Fprint.Device", G_TYPE_INVALID,
|
|
||||||
dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE), &props, G_TYPE_INVALID)) {
|
|
||||||
const char *scan_type;
|
|
||||||
if (has_multiple_devices)
|
|
||||||
data->driver = g_value_dup_string (g_hash_table_lookup (props, "name"));
|
|
||||||
scan_type = g_value_dup_string (g_hash_table_lookup (props, "scan-type"));
|
|
||||||
if (g_str_equal (scan_type, "swipe"))
|
|
||||||
data->is_swipe = TRUE;
|
|
||||||
g_hash_table_destroy (props);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
g_object_unref (p);
|
msg = finger_str_to_msg(finger_name, data->driver, data->is_swipe);
|
||||||
|
if (debug)
|
||||||
|
pam_syslog (data->pamh, LOG_DEBUG, "verify_finger_selected %s", msg);
|
||||||
|
send_info_msg (data->pamh, msg);
|
||||||
|
free (msg);
|
||||||
|
|
||||||
dbus_g_proxy_add_signal(dev, "VerifyStatus", G_TYPE_STRING, G_TYPE_BOOLEAN, NULL);
|
return 0;
|
||||||
dbus_g_proxy_add_signal(dev, "VerifyFingerSelected", G_TYPE_STRING, NULL);
|
}
|
||||||
dbus_g_proxy_connect_signal(dev, "VerifyStatus", G_CALLBACK(verify_result),
|
|
||||||
data, NULL);
|
/* See https://github.com/systemd/systemd/issues/14636 */
|
||||||
dbus_g_proxy_connect_signal(dev, "VerifyFingerSelected", G_CALLBACK(verify_finger_selected),
|
static int
|
||||||
data, NULL);
|
get_property_string (sd_bus *bus,
|
||||||
|
const char *destination,
|
||||||
|
const char *path,
|
||||||
|
const char *interface,
|
||||||
|
const char *member,
|
||||||
|
sd_bus_error *error,
|
||||||
|
char **ret) {
|
||||||
|
|
||||||
|
sd_bus_message *reply = NULL;
|
||||||
|
const char *s;
|
||||||
|
char *n;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", interface, member);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = sd_bus_message_enter_container(reply, 'v', "s");
|
||||||
|
if (r < 0)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
r = sd_bus_message_read_basic(reply, 's', &s);
|
||||||
|
if (r < 0)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
n = strdup(s);
|
||||||
|
if (!n) {
|
||||||
|
r = -ENOMEM;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
sd_bus_message_unref (reply);
|
||||||
|
|
||||||
|
*ret = n;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
if (reply != NULL)
|
||||||
|
sd_bus_message_unref (reply);
|
||||||
|
return sd_bus_error_set_errno(error, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
do_verify (pam_handle_t *pamh,
|
||||||
|
sd_bus *bus,
|
||||||
|
const char *dev,
|
||||||
|
bool has_multiple_devices)
|
||||||
|
{
|
||||||
|
verify_data *data;
|
||||||
|
sd_bus_slot *verify_status_slot, *verify_finger_selected_slot;
|
||||||
|
char *scan_type = NULL;
|
||||||
|
int ret;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
data = calloc (1, sizeof(verify_data));
|
||||||
|
data->max_tries = max_tries;
|
||||||
|
data->pamh = pamh;
|
||||||
|
|
||||||
|
/* Get some properties for the device */
|
||||||
|
r = get_property_string (bus,
|
||||||
|
"net.reactivated.Fprint",
|
||||||
|
dev,
|
||||||
|
"net.reactivated.Fprint.Device",
|
||||||
|
"scan-type",
|
||||||
|
NULL,
|
||||||
|
&scan_type);
|
||||||
|
if (r < 0)
|
||||||
|
pam_syslog (data->pamh, LOG_ERR, "Failed to get scan-type for %s: %d", dev, r);
|
||||||
|
if (debug)
|
||||||
|
pam_syslog (data->pamh, LOG_DEBUG, "scan-type for %s: %s", dev, scan_type);
|
||||||
|
if (str_equal (scan_type, "swipe"))
|
||||||
|
data->is_swipe = true;
|
||||||
|
free (scan_type);
|
||||||
|
|
||||||
|
if (has_multiple_devices) {
|
||||||
|
get_property_string (bus,
|
||||||
|
"net.reactivated.Fprint",
|
||||||
|
dev,
|
||||||
|
"net.reactivated.Fprint.Device",
|
||||||
|
"name",
|
||||||
|
NULL,
|
||||||
|
&data->driver);
|
||||||
|
if (r < 0)
|
||||||
|
pam_syslog (data->pamh, LOG_ERR, "Failed to get driver name for %s: %d", dev, r);
|
||||||
|
if (debug && r == 0)
|
||||||
|
pam_syslog (data->pamh, LOG_DEBUG, "driver name for %s: %s", dev, data->driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
verify_status_slot = NULL;
|
||||||
|
sd_bus_match_signal (bus,
|
||||||
|
&verify_status_slot,
|
||||||
|
"net.reactivated.Fprint",
|
||||||
|
dev,
|
||||||
|
"net.reactivated.Fprint.Device",
|
||||||
|
"VerifyStatus",
|
||||||
|
verify_result,
|
||||||
|
data);
|
||||||
|
|
||||||
|
verify_finger_selected_slot = NULL;
|
||||||
|
sd_bus_match_signal (bus,
|
||||||
|
&verify_finger_selected_slot,
|
||||||
|
"net.reactivated.Fprint",
|
||||||
|
dev,
|
||||||
|
"net.reactivated.Fprint.Device",
|
||||||
|
"VerifyFingerSelected",
|
||||||
|
verify_finger_selected,
|
||||||
|
data);
|
||||||
|
|
||||||
ret = PAM_AUTH_ERR;
|
ret = PAM_AUTH_ERR;
|
||||||
|
|
||||||
while (ret == PAM_AUTH_ERR && data->max_tries > 0) {
|
while (ret == PAM_AUTH_ERR && data->max_tries > 0) {
|
||||||
GSource *source;
|
uint64_t verification_end = now () + (timeout * USEC_PER_SEC);
|
||||||
|
sd_bus_message *m = NULL;
|
||||||
|
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
|
||||||
/* Set up the timeout on our non-default context */
|
data->timed_out = false;
|
||||||
source = g_timeout_source_new_seconds (timeout);
|
|
||||||
g_source_attach (source, g_main_loop_get_context (loop));
|
|
||||||
g_source_set_callback (source, verify_timeout_cb, data, NULL);
|
|
||||||
|
|
||||||
data->timed_out = FALSE;
|
r = sd_bus_call_method (bus,
|
||||||
|
"net.reactivated.Fprint",
|
||||||
|
dev,
|
||||||
|
"net.reactivated.Fprint.Device",
|
||||||
|
"VerifyStart",
|
||||||
|
&error,
|
||||||
|
&m,
|
||||||
|
"s",
|
||||||
|
"any");
|
||||||
|
|
||||||
if (!dbus_g_proxy_call (dev, "VerifyStart", &error, G_TYPE_STRING, "any", G_TYPE_INVALID, G_TYPE_INVALID)) {
|
if (r < 0) {
|
||||||
if (dbus_g_error_has_name(error, "net.reactivated.Fprint.Error.NoEnrolledPrints"))
|
if (sd_bus_error_has_name (&error, "net.reactivated.Fprint.Error.NoEnrolledPrints"))
|
||||||
ret = PAM_USER_UNKNOWN;
|
ret = PAM_USER_UNKNOWN;
|
||||||
|
|
||||||
D(pamh, "VerifyStart failed: %s", error->message);
|
if (debug)
|
||||||
g_error_free (error);
|
pam_syslog (pamh, LOG_DEBUG, "VerifyStart failed: %s", error.message);
|
||||||
|
sd_bus_error_free (&error);
|
||||||
g_source_destroy (source);
|
|
||||||
g_source_unref (source);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_main_loop_run (loop);
|
for (;;) {
|
||||||
|
int64_t wait_time;
|
||||||
|
|
||||||
g_source_destroy (source);
|
wait_time = verification_end - now();
|
||||||
g_source_unref (source);
|
if (wait_time <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
r = sd_bus_process (bus, NULL);
|
||||||
|
if (r < 0)
|
||||||
|
break;
|
||||||
|
if (data->result != NULL)
|
||||||
|
break;
|
||||||
|
if (r == 0) {
|
||||||
|
if (debug) {
|
||||||
|
pam_syslog(pamh, LOG_DEBUG, "Waiting for %"PRId64" seconds (%"PRId64" usecs)",
|
||||||
|
wait_time / USEC_PER_SEC,
|
||||||
|
wait_time);
|
||||||
|
}
|
||||||
|
r = sd_bus_wait (bus, wait_time);
|
||||||
|
if (r < 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (now () >= verification_end) {
|
||||||
|
data->timed_out = true;
|
||||||
|
send_info_msg (data->pamh, _("Verification timed out"));
|
||||||
|
}
|
||||||
|
|
||||||
/* Ignore errors from VerifyStop */
|
/* Ignore errors from VerifyStop */
|
||||||
dbus_g_proxy_call (dev, "VerifyStop", NULL, G_TYPE_INVALID, G_TYPE_INVALID);
|
sd_bus_call_method (bus,
|
||||||
|
"net.reactivated.Fprint",
|
||||||
|
dev,
|
||||||
|
"net.reactivated.Fprint.Device",
|
||||||
|
"VerifyStop",
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
|
||||||
if (data->timed_out) {
|
if (data->timed_out) {
|
||||||
ret = PAM_AUTHINFO_UNAVAIL;
|
ret = PAM_AUTHINFO_UNAVAIL;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
if (g_str_equal (data->result, "verify-no-match")) {
|
if (str_equal (data->result, "verify-no-match")) {
|
||||||
send_err_msg (data->pamh, "Failed to match fingerprint");
|
send_err_msg (data->pamh, "Failed to match fingerprint");
|
||||||
ret = PAM_AUTH_ERR;
|
ret = PAM_AUTH_ERR;
|
||||||
} else if (g_str_equal (data->result, "verify-match"))
|
} else if (str_equal (data->result, "verify-match")) {
|
||||||
ret = PAM_SUCCESS;
|
ret = PAM_SUCCESS;
|
||||||
else if (g_str_equal (data->result, "verify-unknown-error"))
|
break;
|
||||||
|
} else if (str_equal (data->result, "verify-unknown-error")) {
|
||||||
ret = PAM_AUTHINFO_UNAVAIL;
|
ret = PAM_AUTHINFO_UNAVAIL;
|
||||||
else if (g_str_equal (data->result, "verify-disconnected")) {
|
} else if (str_equal (data->result, "verify-disconnected")) {
|
||||||
ret = PAM_AUTHINFO_UNAVAIL;
|
ret = PAM_AUTHINFO_UNAVAIL;
|
||||||
g_free (data->result);
|
free (data->result);
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
send_info_msg (data->pamh, "An unknown error occurred");
|
send_info_msg (data->pamh, _("An unknown error occurred"));
|
||||||
ret = PAM_AUTH_ERR;
|
ret = PAM_AUTH_ERR;
|
||||||
g_free (data->result);
|
free (data->result);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
g_free (data->result);
|
free (data->result);
|
||||||
data->result = NULL;
|
data->result = NULL;
|
||||||
}
|
}
|
||||||
data->max_tries--;
|
data->max_tries--;
|
||||||
}
|
}
|
||||||
|
|
||||||
dbus_g_proxy_disconnect_signal(dev, "VerifyStatus", G_CALLBACK(verify_result), data);
|
if (data->max_tries == 0)
|
||||||
dbus_g_proxy_disconnect_signal(dev, "VerifyFingerSelected", G_CALLBACK(verify_finger_selected), data);
|
ret = PAM_MAXTRIES;
|
||||||
|
|
||||||
g_free (data->driver);
|
sd_bus_slot_unref (verify_status_slot);
|
||||||
g_free (data);
|
sd_bus_slot_unref (verify_finger_selected_slot);
|
||||||
|
|
||||||
|
if (data->result)
|
||||||
|
free (data->result);
|
||||||
|
free (data->driver);
|
||||||
|
free (data);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean user_has_prints(DBusGProxy *dev, const char *username)
|
static bool
|
||||||
|
user_has_prints (pam_handle_t *pamh,
|
||||||
|
sd_bus *bus,
|
||||||
|
const char *dev,
|
||||||
|
const char *username)
|
||||||
{
|
{
|
||||||
char **fingers;
|
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
gboolean have_prints;
|
sd_bus_message *m = NULL;
|
||||||
|
size_t num_fingers = 0;
|
||||||
|
const char *s;
|
||||||
|
int r;
|
||||||
|
|
||||||
if (!dbus_g_proxy_call (dev, "ListEnrolledFingers", NULL,
|
r = sd_bus_call_method (bus,
|
||||||
G_TYPE_STRING, username, G_TYPE_INVALID,
|
"net.reactivated.Fprint",
|
||||||
G_TYPE_STRV, &fingers, G_TYPE_INVALID)) {
|
dev,
|
||||||
|
"net.reactivated.Fprint.Device",
|
||||||
|
"ListEnrolledFingers",
|
||||||
|
&error,
|
||||||
|
&m,
|
||||||
|
"s",
|
||||||
|
username);
|
||||||
|
if (r < 0) {
|
||||||
/* If ListEnrolledFingers fails then verification should
|
/* If ListEnrolledFingers fails then verification should
|
||||||
* also fail (both use the same underlying call), so we
|
* also fail (both use the same underlying call), so we
|
||||||
* report FALSE here and bail out early. */
|
* report false here and bail out early. */
|
||||||
return FALSE;
|
if (debug) {
|
||||||
|
pam_syslog (pamh, LOG_DEBUG, "ListEnrolledFingers failed for %s: %s",
|
||||||
|
username, error.message);
|
||||||
|
}
|
||||||
|
sd_bus_error_free (&error);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
have_prints = fingers != NULL && g_strv_length (fingers) > 0;
|
r = sd_bus_message_enter_container (m, 'a', "s");
|
||||||
g_strfreev (fingers);
|
if (r < 0) {
|
||||||
|
pam_syslog (pamh, LOG_ERR, "Failed to parse answer from ListEnrolledFingers(): %d", r);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
return have_prints;
|
num_fingers = 0;
|
||||||
|
while ((r = sd_bus_message_read_basic(m, 's', &s)) > 0)
|
||||||
|
num_fingers++;
|
||||||
|
sd_bus_message_exit_container (m);
|
||||||
|
|
||||||
|
out:
|
||||||
|
sd_bus_message_unref (m);
|
||||||
|
return (num_fingers > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void release_device(pam_handle_t *pamh, DBusGProxy *dev)
|
static void
|
||||||
|
release_device (pam_handle_t *pamh,
|
||||||
|
sd_bus *bus,
|
||||||
|
const char *dev)
|
||||||
{
|
{
|
||||||
GError *error = NULL;
|
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
if (!dbus_g_proxy_call (dev, "Release", &error, G_TYPE_INVALID, G_TYPE_INVALID)) {
|
int r;
|
||||||
D(pamh, "ReleaseDevice failed: %s\n", error->message);
|
|
||||||
g_error_free (error);
|
r = sd_bus_call_method (bus,
|
||||||
|
"net.reactivated.Fprint",
|
||||||
|
dev,
|
||||||
|
"net.reactivated.Fprint.Device",
|
||||||
|
"Release",
|
||||||
|
&error,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
if (r < 0) {
|
||||||
|
pam_syslog (pamh, LOG_ERR, "ReleaseDevice failed: %s", error.message);
|
||||||
|
sd_bus_error_free (&error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean claim_device(pam_handle_t *pamh, DBusGProxy *dev, const char *username)
|
static bool
|
||||||
|
claim_device (pam_handle_t *pamh,
|
||||||
|
sd_bus *bus,
|
||||||
|
const char *dev,
|
||||||
|
const char *username)
|
||||||
{
|
{
|
||||||
GError *error = NULL;
|
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
if (!dbus_g_proxy_call (dev, "Claim", &error, G_TYPE_STRING, username, G_TYPE_INVALID, G_TYPE_INVALID)) {
|
r = sd_bus_call_method (bus,
|
||||||
D(pamh, "failed to claim device %s\n", error->message);
|
"net.reactivated.Fprint",
|
||||||
g_error_free (error);
|
dev,
|
||||||
return FALSE;
|
"net.reactivated.Fprint.Device",
|
||||||
|
"Claim",
|
||||||
|
&error,
|
||||||
|
NULL,
|
||||||
|
"s",
|
||||||
|
username);
|
||||||
|
if (r < 0) {
|
||||||
|
if (debug)
|
||||||
|
pam_syslog (pamh, LOG_DEBUG, "failed to claim device %s", error.message);
|
||||||
|
sd_bus_error_free (&error);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return TRUE;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_auth(pam_handle_t *pamh, const char *username)
|
static int do_auth(pam_handle_t *pamh, const char *username)
|
||||||
{
|
{
|
||||||
DBusGProxy *manager;
|
char *dev;
|
||||||
DBusGConnection *connection;
|
bool have_prints;
|
||||||
DBusGProxy *dev;
|
bool has_multiple_devices;
|
||||||
GMainLoop *loop;
|
|
||||||
gboolean have_prints;
|
|
||||||
gboolean has_multiple_devices;
|
|
||||||
int ret = PAM_AUTHINFO_UNAVAIL;
|
int ret = PAM_AUTHINFO_UNAVAIL;
|
||||||
|
sd_bus *bus = NULL;
|
||||||
|
|
||||||
manager = create_manager (pamh, &connection, &loop);
|
if (sd_bus_open_system (&bus) < 0) {
|
||||||
if (manager == NULL)
|
pam_syslog (pamh, LOG_ERR, "Error with getting the bus: %m");
|
||||||
return PAM_AUTHINFO_UNAVAIL;
|
|
||||||
|
|
||||||
dev = open_device(pamh, connection, manager, &has_multiple_devices);
|
|
||||||
g_object_unref (manager);
|
|
||||||
if (!dev) {
|
|
||||||
unref_loop (loop);
|
|
||||||
close_and_unref (connection);
|
|
||||||
return PAM_AUTHINFO_UNAVAIL;
|
return PAM_AUTHINFO_UNAVAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
have_prints = user_has_prints(dev, username);
|
dev = open_device (pamh, bus, &has_multiple_devices);
|
||||||
D(pamh, "prints registered: %s\n", have_prints ? "yes" : "no");
|
if (dev == NULL) {
|
||||||
|
sd_bus_unref (bus);
|
||||||
if (have_prints) {
|
return PAM_AUTHINFO_UNAVAIL;
|
||||||
if (claim_device (pamh, dev, username)) {
|
|
||||||
ret = do_verify (loop, pamh, dev, has_multiple_devices);
|
|
||||||
release_device (pamh, dev);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unref_loop (loop);
|
have_prints = user_has_prints (pamh, bus, dev, username);
|
||||||
g_object_unref (dev);
|
if (debug)
|
||||||
close_and_unref (connection);
|
pam_syslog (pamh, LOG_DEBUG, "prints registered: %s\n", have_prints ? "yes" : "no");
|
||||||
|
|
||||||
|
if (!have_prints)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (claim_device (pamh, bus, dev, username)) {
|
||||||
|
ret = do_verify (pamh, bus, dev, has_multiple_devices);
|
||||||
|
release_device (pamh, bus, dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
free (dev);
|
||||||
|
sd_bus_unref (bus);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc,
|
static bool
|
||||||
const char **argv)
|
is_remote (pam_handle_t *pamh)
|
||||||
{
|
{
|
||||||
const char *rhost = NULL;
|
const char *rhost = NULL;
|
||||||
const char *username;
|
|
||||||
guint i;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
|
|
||||||
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
|
|
||||||
|
|
||||||
#if !GLIB_CHECK_VERSION (2, 36, 0)
|
|
||||||
g_type_init();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
dbus_g_object_register_marshaller (fprintd_marshal_VOID__STRING_BOOLEAN,
|
|
||||||
G_TYPE_NONE, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_INVALID);
|
|
||||||
|
|
||||||
pam_get_item(pamh, PAM_RHOST, (const void **)(const void*) &rhost);
|
pam_get_item(pamh, PAM_RHOST, (const void **)(const void*) &rhost);
|
||||||
|
|
||||||
@ -481,32 +600,78 @@ PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc,
|
|||||||
* "localhost" if the host is local.
|
* "localhost" if the host is local.
|
||||||
* We want to not run for known remote hosts */
|
* We want to not run for known remote hosts */
|
||||||
if (rhost != NULL &&
|
if (rhost != NULL &&
|
||||||
rhost != '\0' &&
|
*rhost != '\0' &&
|
||||||
strcmp (rhost, "localhost") != 0) {
|
strcmp (rhost, "localhost") != 0) {
|
||||||
return PAM_AUTHINFO_UNAVAIL;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sd_session_is_remote (NULL) > 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc,
|
||||||
|
const char **argv)
|
||||||
|
{
|
||||||
|
const char *username;
|
||||||
|
unsigned i;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
|
||||||
|
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
|
||||||
|
|
||||||
|
if (is_remote (pamh))
|
||||||
|
return PAM_AUTHINFO_UNAVAIL;
|
||||||
|
|
||||||
r = pam_get_user(pamh, &username, NULL);
|
r = pam_get_user(pamh, &username, NULL);
|
||||||
if (r != PAM_SUCCESS)
|
if (r != PAM_SUCCESS)
|
||||||
return PAM_AUTHINFO_UNAVAIL;
|
return PAM_AUTHINFO_UNAVAIL;
|
||||||
|
|
||||||
for (i = 0; i < argc; i++) {
|
for (i = 0; i < argc; i++) {
|
||||||
if (argv[i] != NULL) {
|
if (argv[i] != NULL) {
|
||||||
if(g_str_equal (argv[i], "debug")) {
|
if (str_equal (argv[i], "debug")) {
|
||||||
g_message ("debug on");
|
pam_syslog (pamh, LOG_DEBUG, "debug on");
|
||||||
debug = TRUE;
|
debug = true;
|
||||||
}
|
} else if (str_has_prefix (argv[i], DEBUG_MATCH)) {
|
||||||
else if (strncmp(argv[i], MAX_TRIES_MATCH, strlen (MAX_TRIES_MATCH)) == 0 && strlen(argv[i]) == strlen (MAX_TRIES_MATCH) + 1) {
|
pam_syslog (pamh, LOG_DEBUG, "debug on");
|
||||||
|
const char *value;
|
||||||
|
|
||||||
|
value = argv[i] + strlen (DEBUG_MATCH);
|
||||||
|
if (str_equal (value, "on") ||
|
||||||
|
str_equal (value, "true") ||
|
||||||
|
str_equal (value, "1")) {
|
||||||
|
pam_syslog (pamh, LOG_DEBUG, "debug on");
|
||||||
|
debug = true;
|
||||||
|
} else if (str_equal (value, "off") ||
|
||||||
|
str_equal (value, "false") ||
|
||||||
|
str_equal (value, "0")) {
|
||||||
|
debug = false;
|
||||||
|
} else {
|
||||||
|
pam_syslog (pamh, LOG_DEBUG, "invalid debug value '%s', disabling", value);
|
||||||
|
}
|
||||||
|
} else if (str_has_prefix (argv[i], MAX_TRIES_MATCH) && strlen(argv[i]) == strlen (MAX_TRIES_MATCH) + 1) {
|
||||||
max_tries = atoi (argv[i] + strlen (MAX_TRIES_MATCH));
|
max_tries = atoi (argv[i] + strlen (MAX_TRIES_MATCH));
|
||||||
if (max_tries < 1)
|
if (max_tries < 1) {
|
||||||
|
if (debug) {
|
||||||
|
pam_syslog (pamh, LOG_DEBUG, "invalid max tries '%s', using %d",
|
||||||
|
argv[i] + strlen (MAX_TRIES_MATCH), DEFAULT_MAX_TRIES);
|
||||||
|
}
|
||||||
max_tries = DEFAULT_MAX_TRIES;
|
max_tries = DEFAULT_MAX_TRIES;
|
||||||
D(pamh, "max_tries specified as: %d", max_tries);
|
}
|
||||||
}
|
if (debug)
|
||||||
else if (strncmp(argv[i], TIMEOUT_MATCH, strlen (TIMEOUT_MATCH)) == 0 && strlen(argv[i]) <= strlen (TIMEOUT_MATCH) + 2) {
|
pam_syslog (pamh, LOG_DEBUG, "max_tries specified as: %d", max_tries);
|
||||||
|
} else if (str_has_prefix (argv[i], TIMEOUT_MATCH) && strlen(argv[i]) <= strlen (TIMEOUT_MATCH) + 2) {
|
||||||
timeout = atoi (argv[i] + strlen (TIMEOUT_MATCH));
|
timeout = atoi (argv[i] + strlen (TIMEOUT_MATCH));
|
||||||
if (timeout < 10)
|
if (timeout < MIN_TIMEOUT) {
|
||||||
timeout = DEFAULT_TIMEOUT;
|
if (debug) {
|
||||||
D(pamh, "timeout specified as: %d", timeout);
|
pam_syslog (pamh, LOG_DEBUG, "timeout %d secs too low, using %d",
|
||||||
|
timeout, MIN_TIMEOUT);
|
||||||
|
}
|
||||||
|
timeout = MIN_TIMEOUT;
|
||||||
|
} else if (debug) {
|
||||||
|
pam_syslog (pamh, LOG_DEBUG, "timeout specified as: %d secs", timeout);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
6
pam/pam_fprintd.ver
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
global:
|
||||||
|
pam_*;
|
||||||
|
local:
|
||||||
|
*;
|
||||||
|
};
|
||||||
@ -3,3 +3,4 @@ src/main.c
|
|||||||
src/manager.c
|
src/manager.c
|
||||||
src/device.c
|
src/device.c
|
||||||
pam/fingerprint-strings.h
|
pam/fingerprint-strings.h
|
||||||
|
pam/pam_fprintd.c
|
||||||
|
|||||||
14
po/check-translations.sh
Executable file
@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
po_dir=$(dirname $0)
|
||||||
|
|
||||||
|
for i in $po_dir/*.po ; do
|
||||||
|
if ! grep -q `basename $i | sed 's,.po,,'` $po_dir/LINGUAS; then
|
||||||
|
echo '**********************************';
|
||||||
|
echo '***' `basename $i | sed 's,.po,,'` missing from po/LINGUAS '***' ;
|
||||||
|
echo '**********************************';
|
||||||
|
exit 1;
|
||||||
|
fi;
|
||||||
|
done;
|
||||||
|
|
||||||
|
exit 0
|
||||||
6
po/meson.build
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
i18n.gettext(meson.project_name(), preset: 'glib')
|
||||||
|
|
||||||
|
test('check-translations',
|
||||||
|
find_program('check-translations.sh'),
|
||||||
|
suite: ['dist'],
|
||||||
|
)
|
||||||
@ -1,55 +0,0 @@
|
|||||||
BUILT_SOURCES = manager-dbus-glue.h device-dbus-glue.h $(MARSHALFILES) $(interfaces_DATA)
|
|
||||||
noinst_HEADERS = $(BUILT_SOURCES)
|
|
||||||
|
|
||||||
CLEANFILES = $(BUILT_SOURCES)
|
|
||||||
EXTRA_DIST = manager.xml device.xml fprintd-marshal.list
|
|
||||||
|
|
||||||
libexec_PROGRAMS = fprintd
|
|
||||||
noinst_LTLIBRARIES = libfprintd.la
|
|
||||||
|
|
||||||
AM_CFLAGS = \
|
|
||||||
$(WARN_CFLAGS) \
|
|
||||||
$(FPRINT_CFLAGS) \
|
|
||||||
$(DAEMON_CFLAGS) \
|
|
||||||
-DG_LOG_DOMAIN=\""fprintd"\" \
|
|
||||||
-DLOCALEDIR=\""$(datadir)/locale"\" \
|
|
||||||
-DPLUGINDIR=\""$(libdir)/fprintd/modules"\"
|
|
||||||
|
|
||||||
libfprintd_la_SOURCES = \
|
|
||||||
manager.c device.c \
|
|
||||||
$(MARSHALFILES) \
|
|
||||||
fprintd.h
|
|
||||||
libfprintd_la_LIBADD = $(FPRINT_LIBS) $(DAEMON_LIBS)
|
|
||||||
libfprintd_la_LDFLAGS = -no-undefined
|
|
||||||
|
|
||||||
fprintd_SOURCES = \
|
|
||||||
main.c \
|
|
||||||
loop.c loop.h \
|
|
||||||
file_storage.c file_storage.h storage.h
|
|
||||||
fprintd_LDADD = libfprintd.la
|
|
||||||
|
|
||||||
interfaces_DATA = net.reactivated.Fprint.Manager.xml net.reactivated.Fprint.Device.xml
|
|
||||||
net.reactivated.Fprint.Manager.xml: manager.xml
|
|
||||||
cat $< > $@
|
|
||||||
net.reactivated.Fprint.Device.xml: device.xml
|
|
||||||
cat $< > $@
|
|
||||||
interfacesdir = $(datadir)/dbus-1/interfaces/
|
|
||||||
|
|
||||||
manager-dbus-glue.h: manager.xml
|
|
||||||
dbus-binding-tool --prefix=fprint_manager --mode=glib-server $< --output=$@
|
|
||||||
|
|
||||||
device-dbus-glue.h: device.xml
|
|
||||||
dbus-binding-tool --prefix=fprint_device --mode=glib-server $< --output=$@
|
|
||||||
|
|
||||||
MARSHALFILES = fprintd-marshal.c fprintd-marshal.h
|
|
||||||
GLIB_GENMARSHAL=`pkg-config --variable=glib_genmarshal glib-2.0`
|
|
||||||
|
|
||||||
fprintd-marshal.h: fprintd-marshal.list
|
|
||||||
( $(GLIB_GENMARSHAL) --prefix=fprintd_marshal $(srcdir)/fprintd-marshal.list --header > fprintd-marshal.h )
|
|
||||||
fprintd-marshal.c: fprintd-marshal.h
|
|
||||||
( $(GLIB_GENMARSHAL) --prefix=fprintd_marshal $(srcdir)/fprintd-marshal.list --body --header > fprintd-marshal.c )
|
|
||||||
|
|
||||||
install-data-hook:
|
|
||||||
if test -w $(DESTDIR)$(prefix)/; then \
|
|
||||||
mkdir -p $(DESTDIR)$(localstatedir)/lib/fprint; \
|
|
||||||
fi
|
|
||||||
1081
src/device.c
@ -245,6 +245,16 @@
|
|||||||
The user should remove their finger from the reader and retry scanning their finger, the enrollment is still ongoing.
|
The user should remove their finger from the reader and retry scanning their finger, the enrollment is still ongoing.
|
||||||
</doc:definition>
|
</doc:definition>
|
||||||
</doc:item>
|
</doc:item>
|
||||||
|
<doc:item>
|
||||||
|
<doc:term>enroll-data-full</doc:term>
|
||||||
|
<doc:definition>
|
||||||
|
No further prints can be enrolled on this device, <doc:ref type="method" to="Device.EnrollStop">Device.EnrollStop</doc:ref> should now be called.
|
||||||
|
|
||||||
|
<doc:ref type="method" to="DeleteEnrolledFingers2">Delete other prints</doc:ref> from the device first to continue
|
||||||
|
(e.g. from other users). Note that old prints or prints from other operating systems may be deleted automatically
|
||||||
|
to resolve this error without any notification.
|
||||||
|
</doc:definition>
|
||||||
|
</doc:item>
|
||||||
<doc:item>
|
<doc:item>
|
||||||
<doc:term>enroll-disconnected</doc:term>
|
<doc:term>enroll-disconnected</doc:term>
|
||||||
<doc:definition>
|
<doc:definition>
|
||||||
@ -301,6 +311,29 @@
|
|||||||
<doc:para>
|
<doc:para>
|
||||||
Delete all the enrolled fingerprints for the chosen user.
|
Delete all the enrolled fingerprints for the chosen user.
|
||||||
</doc:para>
|
</doc:para>
|
||||||
|
<doc:para>
|
||||||
|
This call only exists for compatibility reasons, you should instead claim the device using
|
||||||
|
<doc:ref type="method" to="Device.Claim">Device.Claim</doc:ref> and then call
|
||||||
|
<doc:ref type="method" to="DeleteEnrolledFingers2">DeleteEnrolledFingers2</doc:ref>.
|
||||||
|
</doc:para>
|
||||||
|
</doc:description>
|
||||||
|
|
||||||
|
<doc:errors>
|
||||||
|
<doc:error name="&ERROR_PERMISSION_DENIED;">if the caller lacks the appropriate PolicyKit authorization</doc:error>
|
||||||
|
</doc:errors>
|
||||||
|
</doc:doc>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!-- ************************************************************ -->
|
||||||
|
|
||||||
|
<method name="DeleteEnrolledFingers2">
|
||||||
|
<annotation name="org.freedesktop.DBus.GLib.Async" value="" />
|
||||||
|
|
||||||
|
<doc:doc>
|
||||||
|
<doc:description>
|
||||||
|
<doc:para>
|
||||||
|
Delete all the enrolled fingerprints for the user currently claiming the device with <doc:ref type="method" to="Device.Claim">Device.Claim</doc:ref>.
|
||||||
|
</doc:para>
|
||||||
</doc:description>
|
</doc:description>
|
||||||
|
|
||||||
<doc:errors>
|
<doc:errors>
|
||||||
@ -458,7 +491,7 @@
|
|||||||
<doc:doc>
|
<doc:doc>
|
||||||
<doc:description>
|
<doc:description>
|
||||||
<doc:para>
|
<doc:para>
|
||||||
Start enrollemnt for the selected finger. You need to have claimed the device using
|
Start enrollment for the selected finger. You need to have claimed the device using
|
||||||
<doc:ref type="method" to="Device.Claim">Device.Claim</doc:ref> before calling
|
<doc:ref type="method" to="Device.Claim">Device.Claim</doc:ref> before calling
|
||||||
this method. Enrollment status is sent through <doc:ref type="signal" to="Device::EnrollStatus">Device::EnrollStatus</doc:ref>.
|
this method. Enrollment status is sent through <doc:ref type="signal" to="Device::EnrollStatus">Device::EnrollStatus</doc:ref>.
|
||||||
</doc:para>
|
</doc:para>
|
||||||
@ -468,7 +501,7 @@
|
|||||||
<doc:error name="&ERROR_PERMISSION_DENIED;">if the caller lacks the appropriate PolicyKit authorization</doc:error>
|
<doc:error name="&ERROR_PERMISSION_DENIED;">if the caller lacks the appropriate PolicyKit authorization</doc:error>
|
||||||
<doc:error name="&ERROR_CLAIM_DEVICE;">if the device was not claimed</doc:error>
|
<doc:error name="&ERROR_CLAIM_DEVICE;">if the device was not claimed</doc:error>
|
||||||
<doc:error name="&ERROR_ALREADY_IN_USE;">if the device was already being used</doc:error>
|
<doc:error name="&ERROR_ALREADY_IN_USE;">if the device was already being used</doc:error>
|
||||||
<doc:error name="&ERROR_INVALID_FINGERNAME;">if there are no enrolled prints for the chosen user</doc:error>
|
<doc:error name="&ERROR_INVALID_FINGERNAME;">if the finger name passed is invalid</doc:error>
|
||||||
<doc:error name="&ERROR_INTERNAL;">if there was an internal error</doc:error>
|
<doc:error name="&ERROR_INTERNAL;">if there was an internal error</doc:error>
|
||||||
</doc:errors>
|
</doc:errors>
|
||||||
|
|
||||||
|
|||||||
@ -35,29 +35,34 @@
|
|||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <glib/gstdio.h>
|
#include <glib/gstdio.h>
|
||||||
|
|
||||||
#include <libfprint/fprint.h>
|
#include <fprint.h>
|
||||||
|
|
||||||
#include "file_storage.h"
|
#include "file_storage.h"
|
||||||
|
|
||||||
#define FILE_STORAGE_PATH "/var/lib/fprint"
|
#define FILE_STORAGE_PATH "/var/lib/fprint"
|
||||||
#define DIR_PERMS 0700
|
#define DIR_PERMS 0700
|
||||||
|
|
||||||
#define FP_FINGER_IS_VALID(finger) \
|
static const char *get_storage_path(void)
|
||||||
((finger) >= LEFT_THUMB && (finger) <= RIGHT_LITTLE)
|
|
||||||
|
|
||||||
static char *get_path_to_storedir(uint16_t driver_id, uint32_t devtype, char *base_store)
|
|
||||||
{
|
{
|
||||||
char idstr[5];
|
const char *path;
|
||||||
char devtypestr[9];
|
|
||||||
|
|
||||||
g_snprintf(idstr, sizeof(idstr), "%04x", driver_id);
|
/* set by systemd >= 240 to an absolute path
|
||||||
g_snprintf(devtypestr, sizeof(devtypestr), "%08x", devtype);
|
* taking into account the StateDirectory
|
||||||
|
* unit file setting */
|
||||||
|
path = g_getenv ("STATE_DIRECTORY");
|
||||||
|
if (path != NULL)
|
||||||
|
return path;
|
||||||
|
|
||||||
return g_build_filename(base_store, idstr, devtypestr, NULL);
|
return FILE_STORAGE_PATH;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *__get_path_to_print(uint16_t driver_id, uint32_t devtype,
|
static char *get_path_to_storedir(const char *driver, const char * device_id, char *base_store)
|
||||||
enum fp_finger finger, char *base_store)
|
{
|
||||||
|
return g_build_filename(base_store, driver, device_id, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *__get_path_to_print(const char *driver, const char * device_id,
|
||||||
|
FpFinger finger, char *base_store)
|
||||||
{
|
{
|
||||||
char *dirpath;
|
char *dirpath;
|
||||||
char *path;
|
char *path;
|
||||||
@ -65,90 +70,85 @@ static char *__get_path_to_print(uint16_t driver_id, uint32_t devtype,
|
|||||||
|
|
||||||
g_snprintf(fingername, 2, "%x", finger);
|
g_snprintf(fingername, 2, "%x", finger);
|
||||||
|
|
||||||
dirpath = get_path_to_storedir(driver_id, devtype, base_store);
|
dirpath = get_path_to_storedir(driver, device_id, base_store);
|
||||||
path = g_build_filename(dirpath, fingername, NULL);
|
path = g_build_filename(dirpath, fingername, NULL);
|
||||||
g_free(dirpath);
|
g_free(dirpath);
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *get_path_to_print(struct fp_dev *dev, enum fp_finger finger, char *base_store)
|
static char *get_path_to_print(FpDevice *dev, FpFinger finger, char *base_store)
|
||||||
{
|
{
|
||||||
return __get_path_to_print(fp_driver_get_driver_id(fp_dev_get_driver(dev)),
|
return __get_path_to_print(fp_device_get_driver (dev),
|
||||||
fp_dev_get_devtype(dev), finger, base_store);
|
fp_device_get_device_id(dev),
|
||||||
|
finger,
|
||||||
|
base_store);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *get_path_to_print_dscv(struct fp_dscv_dev *dev, enum fp_finger finger, char *base_store)
|
static char *get_path_to_print_dscv(FpDevice *dev, FpFinger finger, char *base_store)
|
||||||
{
|
{
|
||||||
return __get_path_to_print(fp_driver_get_driver_id(fp_dscv_dev_get_driver(dev)),
|
return __get_path_to_print(fp_device_get_driver (dev),
|
||||||
fp_dscv_dev_get_devtype(dev), finger, base_store);
|
fp_device_get_device_id(dev),
|
||||||
|
finger,
|
||||||
|
base_store);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *file_storage_get_basestore_for_username(const char *username)
|
static char *file_storage_get_basestore_for_username(const char *username)
|
||||||
{
|
{
|
||||||
return g_build_filename(FILE_STORAGE_PATH, username, NULL);
|
return g_build_filename(get_storage_path(), username, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if username == NULL function will use current username */
|
int file_storage_print_data_save(FpPrint *print)
|
||||||
int file_storage_print_data_save(struct fp_print_data *data,
|
|
||||||
enum fp_finger finger, const char *username)
|
|
||||||
{
|
{
|
||||||
GError *err = NULL;
|
g_autoptr(GError) err = NULL;
|
||||||
char *path, *dirpath;
|
g_autofree char *path = NULL;
|
||||||
size_t len;
|
g_autofree char *dirpath = NULL;
|
||||||
|
g_autofree char *base_store = NULL;
|
||||||
|
g_autofree char *buf = NULL;
|
||||||
|
gsize len;
|
||||||
int r;
|
int r;
|
||||||
char *base_store = NULL;
|
|
||||||
char *buf = NULL;
|
|
||||||
|
|
||||||
base_store = file_storage_get_basestore_for_username(username);
|
base_store = file_storage_get_basestore_for_username(fp_print_get_username (print));
|
||||||
|
|
||||||
len = fp_print_data_get_data(data, (guchar **) &buf);
|
if (!fp_print_serialize (print, (guchar **) &buf, &len, &err)) {
|
||||||
if (!len) {
|
g_warning ("Error serializing data: %s", err->message);
|
||||||
g_free(base_store);
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
path = __get_path_to_print(fp_print_data_get_driver_id(data), fp_print_data_get_devtype(data), finger, base_store);
|
path = __get_path_to_print(fp_print_get_driver (print),
|
||||||
|
fp_print_get_device_id (print),
|
||||||
|
fp_print_get_finger (print),
|
||||||
|
base_store);
|
||||||
dirpath = g_path_get_dirname(path);
|
dirpath = g_path_get_dirname(path);
|
||||||
r = g_mkdir_with_parents(dirpath, DIR_PERMS);
|
r = g_mkdir_with_parents(dirpath, DIR_PERMS);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
g_debug("file_storage_print_data_save(): could not mkdir(\"%s\"): %s",
|
g_debug("file_storage_print_data_save(): could not mkdir(\"%s\"): %s",
|
||||||
dirpath, g_strerror(r));
|
dirpath, g_strerror(r));
|
||||||
g_free(dirpath);
|
return r;
|
||||||
g_free(path);
|
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
g_free(dirpath);
|
|
||||||
|
|
||||||
//fp_dbg("saving to %s", path);
|
//fp_dbg("saving to %s", path);
|
||||||
g_file_set_contents(path, buf, len, &err);
|
g_file_set_contents(path, buf, len, &err);
|
||||||
g_free(path);
|
|
||||||
if (err) {
|
if (err) {
|
||||||
r = err->code;
|
|
||||||
g_debug("file_storage_print_data_save(): could not save '%s': %s",
|
g_debug("file_storage_print_data_save(): could not save '%s': %s",
|
||||||
path, err->message);
|
path, err->message);
|
||||||
g_error_free(err);
|
|
||||||
/* FIXME interpret error codes */
|
/* FIXME interpret error codes */
|
||||||
goto out;
|
return err->code;
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
return 0;
|
||||||
g_clear_pointer(&buf, free);
|
|
||||||
g_clear_pointer(&base_store, g_free);
|
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int load_from_file(char *path, struct fp_print_data **data)
|
static int load_from_file(char *path, FpPrint **print)
|
||||||
{
|
{
|
||||||
|
g_autoptr(GError) err = NULL;
|
||||||
gsize length;
|
gsize length;
|
||||||
char *contents;
|
g_autofree char *contents = NULL;
|
||||||
GError *err = NULL;
|
FpPrint *new;
|
||||||
struct fp_print_data *fdata;
|
|
||||||
|
|
||||||
//fp_dbg("from %s", path);
|
//fp_dbg("from %s", path);
|
||||||
g_file_get_contents(path, &contents, &length, &err);
|
g_file_get_contents(path, &contents, &length, &err);
|
||||||
if (err) {
|
if (err) {
|
||||||
int r = err->code;
|
int r = err->code;
|
||||||
g_error_free(err);
|
|
||||||
/* FIXME interpret more error codes */
|
/* FIXME interpret more error codes */
|
||||||
if (r == G_FILE_ERROR_NOENT)
|
if (r == G_FILE_ERROR_NOENT)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
@ -156,71 +156,71 @@ static int load_from_file(char *path, struct fp_print_data **data)
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
fdata = fp_print_data_from_data((guchar *) contents, length);
|
new = fp_print_deserialize ((guchar *) contents, length, &err);
|
||||||
g_free(contents);
|
if (!new) {
|
||||||
if (!fdata)
|
g_print ("Error deserializing data: %s", err->message);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
*data = fdata;
|
}
|
||||||
|
|
||||||
|
*print = new;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int file_storage_print_data_load(struct fp_dev *dev,
|
int file_storage_print_data_load(FpDevice *dev,
|
||||||
enum fp_finger finger, struct fp_print_data **data, const char *username)
|
FpFinger finger,
|
||||||
|
const char *username,
|
||||||
|
FpPrint **print)
|
||||||
{
|
{
|
||||||
gchar *path;
|
g_autofree gchar *path = NULL;
|
||||||
struct fp_print_data *fdata = NULL;
|
g_autofree gchar *base_store = NULL;
|
||||||
|
FpPrint *new = NULL;
|
||||||
int r;
|
int r;
|
||||||
char *base_store = NULL;
|
|
||||||
|
|
||||||
base_store = file_storage_get_basestore_for_username(username);
|
base_store = file_storage_get_basestore_for_username(username);
|
||||||
|
|
||||||
path = get_path_to_print(dev, finger, base_store);
|
path = get_path_to_print(dev, finger, base_store);
|
||||||
r = load_from_file(path, &fdata);
|
r = load_from_file(path, &new);
|
||||||
g_debug ("file_storage_print_data_load(): loaded '%s' %s",
|
g_debug ("file_storage_print_data_load(): loaded '%s' %s",
|
||||||
path, g_strerror(r));
|
path, g_strerror(r));
|
||||||
g_free(path);
|
|
||||||
g_free(base_store);
|
|
||||||
if (r)
|
if (r)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
if (!fp_dev_supports_print_data(dev, fdata)) {
|
if (!fp_print_compatible (new, dev)) {
|
||||||
fp_print_data_free(fdata);
|
g_object_unref (new);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
*data = fdata;
|
*print = new;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int file_storage_print_data_delete(struct fp_dscv_dev *dev,
|
int file_storage_print_data_delete(FpDevice *dev, FpFinger finger, const char *username)
|
||||||
enum fp_finger finger, const char *username)
|
|
||||||
{
|
{
|
||||||
|
g_autofree gchar *base_store = NULL;
|
||||||
|
g_autofree gchar *path = NULL;
|
||||||
int r;
|
int r;
|
||||||
char *base_store, *path;
|
|
||||||
|
|
||||||
base_store = file_storage_get_basestore_for_username(username);
|
base_store = file_storage_get_basestore_for_username(username);
|
||||||
|
|
||||||
path = get_path_to_print_dscv(dev, finger, base_store);
|
path = get_path_to_print_dscv(dev, finger, base_store);
|
||||||
|
|
||||||
r = g_unlink(path);
|
r = g_unlink(path);
|
||||||
g_debug("file_storage_print_data_delete(): unlink(\"%s\") %s",
|
g_debug("file_storage_print_data_delete(): unlink(\"%s\") %s",
|
||||||
path, g_strerror(r));
|
path, g_strerror(r));
|
||||||
g_free(path);
|
|
||||||
g_free(base_store);
|
|
||||||
|
|
||||||
/* FIXME: cleanup empty directory */
|
/* FIXME: cleanup empty directory */
|
||||||
return r;
|
return g_unlink(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
static GSList *scan_dev_storedir(char *devpath, uint16_t driver_id,
|
static GSList *scan_dev_storedir(char *devpath,
|
||||||
uint32_t devtype, GSList *list)
|
GSList *list)
|
||||||
{
|
{
|
||||||
GError *err = NULL;
|
g_autoptr(GError) err = NULL;
|
||||||
const gchar *ent;
|
const gchar *ent;
|
||||||
|
|
||||||
GDir *dir = g_dir_open(devpath, 0, &err);
|
GDir *dir = g_dir_open(devpath, 0, &err);
|
||||||
if (!dir) {
|
if (!dir) {
|
||||||
g_debug("scan_dev_storedir(): opendir(\"%s\") failed: %s", devpath, err->message);
|
g_debug("scan_dev_storedir(): opendir(\"%s\") failed: %s", devpath, err->message);
|
||||||
g_error_free(err);
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,26 +245,46 @@ static GSList *scan_dev_storedir(char *devpath, uint16_t driver_id,
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
GSList *file_storage_discover_prints(struct fp_dscv_dev *dev, const char *username)
|
GSList *file_storage_discover_prints(FpDevice *dev, const char *username)
|
||||||
{
|
{
|
||||||
GSList *list = NULL;
|
GSList *list = NULL;
|
||||||
char *base_store = NULL;
|
g_autofree gchar *base_store = NULL;
|
||||||
char *storedir = NULL;
|
g_autofree gchar *storedir = NULL;
|
||||||
|
|
||||||
base_store = file_storage_get_basestore_for_username(username);
|
base_store = file_storage_get_basestore_for_username(username);
|
||||||
|
|
||||||
storedir = get_path_to_storedir(fp_driver_get_driver_id(fp_dscv_dev_get_driver(dev)),
|
storedir = get_path_to_storedir(fp_device_get_driver (dev),
|
||||||
fp_dscv_dev_get_devtype(dev), base_store);
|
fp_device_get_device_id (dev),
|
||||||
|
base_store);
|
||||||
|
|
||||||
g_debug ("file_storage_discover_prints() for user '%s' in '%s'",
|
g_debug ("file_storage_discover_prints() for user '%s' in '%s'",
|
||||||
username, storedir);
|
username, storedir);
|
||||||
|
|
||||||
list = scan_dev_storedir(storedir, fp_driver_get_driver_id(fp_dscv_dev_get_driver(dev)),
|
list = scan_dev_storedir(storedir, list);
|
||||||
fp_dscv_dev_get_devtype(dev), list);
|
|
||||||
|
|
||||||
g_free(base_store);
|
return list;
|
||||||
g_free(storedir);
|
}
|
||||||
|
|
||||||
|
GSList *file_storage_discover_users(void)
|
||||||
|
{
|
||||||
|
g_autoptr(GError) err = NULL;
|
||||||
|
GSList *list = NULL;
|
||||||
|
const gchar *ent;
|
||||||
|
GDir *dir = g_dir_open(get_storage_path(), 0, &err);
|
||||||
|
|
||||||
|
if (!dir) {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((ent = g_dir_read_name(dir))) {
|
||||||
|
/* ent is a username */
|
||||||
|
if (*ent == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
list = g_slist_prepend(list, g_strdup (ent));
|
||||||
|
}
|
||||||
|
|
||||||
|
g_dir_close(dir);
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -18,24 +18,22 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef FILE_STORAGE_H
|
#pragma once
|
||||||
|
|
||||||
#define FILE_STORAGE_H
|
int file_storage_print_data_save(FpPrint *print);
|
||||||
|
|
||||||
int file_storage_print_data_save(struct fp_print_data *data,
|
int file_storage_print_data_load(FpDevice *dev,
|
||||||
enum fp_finger finger, const char *username);
|
FpFinger finger,
|
||||||
|
const char *username,
|
||||||
|
FpPrint **print);
|
||||||
|
|
||||||
|
int file_storage_print_data_delete(FpDevice *dev,
|
||||||
int file_storage_print_data_load(struct fp_dev *dev,
|
FpFinger finger,
|
||||||
enum fp_finger finger, struct fp_print_data **data, const char *username);
|
const char *username);
|
||||||
|
|
||||||
int file_storage_print_data_delete(struct fp_dscv_dev *dev,
|
|
||||||
enum fp_finger finger, const char *username);
|
|
||||||
|
|
||||||
int file_storage_init(void);
|
int file_storage_init(void);
|
||||||
|
|
||||||
int file_storage_deinit(void);
|
int file_storage_deinit(void);
|
||||||
|
|
||||||
GSList *file_storage_discover_prints(struct fp_dscv_dev *dev, const char *username);
|
GSList *file_storage_discover_prints(FpDevice *dev, const char *username);
|
||||||
|
GSList *file_storage_discover_users(void);
|
||||||
#endif
|
|
||||||
|
|||||||
@ -17,16 +17,15 @@
|
|||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __FPRINTD_H__
|
#pragma once
|
||||||
#define __FPRINTD_H__
|
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <dbus/dbus-glib.h>
|
#include <dbus/dbus-glib.h>
|
||||||
|
#include <fprint.h>
|
||||||
|
|
||||||
/* General */
|
/* General */
|
||||||
#define TIMEOUT 30
|
#define TIMEOUT 30
|
||||||
#define FPRINT_SERVICE_NAME "net.reactivated.Fprint"
|
#define FPRINT_SERVICE_NAME "net.reactivated.Fprint"
|
||||||
extern DBusGConnection *fprintd_dbus_conn;
|
|
||||||
|
|
||||||
/* Errors */
|
/* Errors */
|
||||||
GQuark fprint_error_quark(void);
|
GQuark fprint_error_quark(void);
|
||||||
@ -38,7 +37,7 @@ GType fprint_error_get_type(void);
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
FPRINT_ERROR_CLAIM_DEVICE, /* developer didn't claim the device */
|
FPRINT_ERROR_CLAIM_DEVICE, /* developer didn't claim the device */
|
||||||
FPRINT_ERROR_ALREADY_IN_USE, /* device is already claimed by somebody else */
|
FPRINT_ERROR_ALREADY_IN_USE, /* device is already claimed by somebody else */
|
||||||
FPRINT_ERROR_INTERNAL, /* internal error occured */
|
FPRINT_ERROR_INTERNAL, /* internal error occurred */
|
||||||
FPRINT_ERROR_PERMISSION_DENIED, /* PolicyKit refused the action */
|
FPRINT_ERROR_PERMISSION_DENIED, /* PolicyKit refused the action */
|
||||||
FPRINT_ERROR_NO_ENROLLED_PRINTS, /* No prints are enrolled */
|
FPRINT_ERROR_NO_ENROLLED_PRINTS, /* No prints are enrolled */
|
||||||
FPRINT_ERROR_NO_ACTION_IN_PROGRESS, /* No actions currently in progress */
|
FPRINT_ERROR_NO_ACTION_IN_PROGRESS, /* No actions currently in progress */
|
||||||
@ -48,56 +47,29 @@ typedef enum {
|
|||||||
|
|
||||||
/* Manager */
|
/* Manager */
|
||||||
#define FPRINT_TYPE_MANAGER (fprint_manager_get_type())
|
#define FPRINT_TYPE_MANAGER (fprint_manager_get_type())
|
||||||
#define FPRINT_MANAGER(object) (G_TYPE_CHECK_INSTANCE_CAST((object), FPRINT_TYPE_MANAGER, FprintManager))
|
G_DECLARE_FINAL_TYPE (FprintManager, fprint_manager, FPRINT, MANAGER, GObject)
|
||||||
#define FPRINT_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), FPRINT_TYPE_MANAGER, FprintManagerClass))
|
|
||||||
#define FPRINT_IS_MANAGER(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), FPRINT_TYPE_MANAGER))
|
|
||||||
#define FPRINT_IS_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), FPRINT_TYPE_MANAGER))
|
|
||||||
#define FPRINT_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FPRINT_TYPE_MANAGER, FprintManagerClass))
|
|
||||||
|
|
||||||
struct FprintManager {
|
struct _FprintManager {
|
||||||
GObject parent;
|
GObject parent;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FprintManagerClass {
|
|
||||||
GObjectClass parent;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct FprintManager FprintManager;
|
|
||||||
typedef struct FprintManagerClass FprintManagerClass;
|
|
||||||
|
|
||||||
FprintManager *fprint_manager_new(gboolean no_timeout);
|
FprintManager *fprint_manager_new(gboolean no_timeout);
|
||||||
GType fprint_manager_get_type(void);
|
|
||||||
|
|
||||||
/* Device */
|
/* Device */
|
||||||
#define FPRINT_TYPE_DEVICE (fprint_device_get_type())
|
#define FPRINT_TYPE_DEVICE (fprint_device_get_type())
|
||||||
#define FPRINT_DEVICE(object) (G_TYPE_CHECK_INSTANCE_CAST((object), FPRINT_DEVICE_TYPE, FprintDevice))
|
G_DECLARE_FINAL_TYPE (FprintDevice, fprint_device, FPRINT, DEVICE, GObject)
|
||||||
#define FPRINT_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), FPRINT_DEVICE_TYPE, FprintDeviceClass))
|
|
||||||
#define FPRINT_IS_DEVICE(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), FPRINT_TYPE_DEVICE))
|
|
||||||
#define FPRINT_IS_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), FPRINT_TYPE_DEVICE))
|
|
||||||
#define FPRINT_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FPRINT_TYPE_DEVICE, FprintDeviceClass))
|
|
||||||
|
|
||||||
struct FprintDevice {
|
struct _FprintDevice {
|
||||||
GObject parent;
|
GObject parent;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FprintDeviceClass {
|
FprintDevice *fprint_device_new(FpDevice *dev);
|
||||||
GObjectClass parent;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct FprintDevice FprintDevice;
|
|
||||||
typedef struct FprintDeviceClass FprintDeviceClass;
|
|
||||||
|
|
||||||
FprintDevice *fprint_device_new(struct fp_dscv_dev *ddev);
|
|
||||||
GType fprint_device_get_type(void);
|
|
||||||
guint32 _fprint_device_get_id(FprintDevice *rdev);
|
guint32 _fprint_device_get_id(FprintDevice *rdev);
|
||||||
/* Print */
|
/* Print */
|
||||||
/* TODO */
|
/* TODO */
|
||||||
|
|
||||||
/* Binding data included in main.c thorugh server-bindings.h which individual
|
/* Binding data included in main.c through server-bindings.h which individual
|
||||||
* class implementations need to access.
|
* class implementations need to access.
|
||||||
*/
|
*/
|
||||||
extern const DBusGObjectInfo dbus_glib_fprint_manager_object_info;
|
extern const DBusGObjectInfo dbus_glib_fprint_manager_object_info;
|
||||||
extern const DBusGObjectInfo dbus_glib_fprint_device_object_info;
|
extern const DBusGObjectInfo dbus_glib_fprint_device_object_info;
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|||||||
196
src/loop.c
@ -1,196 +0,0 @@
|
|||||||
/*
|
|
||||||
* fprint D-Bus daemon
|
|
||||||
* Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
|
|
||||||
*
|
|
||||||
* 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 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, write to the Free Software Foundation, Inc.,
|
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <glib.h>
|
|
||||||
#include <libfprint/fprint.h>
|
|
||||||
|
|
||||||
#include <poll.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "loop.h"
|
|
||||||
|
|
||||||
struct fdsource {
|
|
||||||
GSource source;
|
|
||||||
GSList *pollfds;
|
|
||||||
};
|
|
||||||
|
|
||||||
static gboolean source_prepare(GSource *source, gint *timeout)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
struct timeval tv;
|
|
||||||
|
|
||||||
r = fp_get_next_timeout(&tv);
|
|
||||||
if (r == 0) {
|
|
||||||
*timeout = -1;
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!timerisset(&tv))
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
*timeout = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean source_check(GSource *source)
|
|
||||||
{
|
|
||||||
struct fdsource *_fdsource = (struct fdsource *) source;
|
|
||||||
GSList *l;
|
|
||||||
struct timeval tv;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
if (!_fdsource->pollfds)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
for (l = _fdsource->pollfds; l != NULL; l = l->next) {
|
|
||||||
GPollFD *pollfd = l->data;
|
|
||||||
|
|
||||||
if (pollfd->revents)
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = fp_get_next_timeout(&tv);
|
|
||||||
if (r == 1 && !timerisset(&tv))
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean source_dispatch(GSource *source, GSourceFunc callback,
|
|
||||||
gpointer data)
|
|
||||||
{
|
|
||||||
struct timeval zerotimeout = {
|
|
||||||
.tv_sec = 0,
|
|
||||||
.tv_usec = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* FIXME error handling */
|
|
||||||
fp_handle_events_timeout(&zerotimeout);
|
|
||||||
|
|
||||||
/* FIXME whats the return value used for? */
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void source_finalize(GSource *source)
|
|
||||||
{
|
|
||||||
struct fdsource *_fdsource = (struct fdsource *) source;
|
|
||||||
GSList *l;
|
|
||||||
|
|
||||||
if (!_fdsource->pollfds)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (l = _fdsource->pollfds; l != NULL; l = l->next) {
|
|
||||||
GPollFD *pollfd = l->data;
|
|
||||||
|
|
||||||
g_source_remove_poll((GSource *) _fdsource, pollfd);
|
|
||||||
g_slice_free(GPollFD, pollfd);
|
|
||||||
_fdsource->pollfds = g_slist_delete_link(_fdsource->pollfds, l);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_slist_free(_fdsource->pollfds);
|
|
||||||
}
|
|
||||||
|
|
||||||
static GSourceFuncs sourcefuncs = {
|
|
||||||
.prepare = source_prepare,
|
|
||||||
.check = source_check,
|
|
||||||
.dispatch = source_dispatch,
|
|
||||||
.finalize = source_finalize,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct fdsource *fdsource = NULL;
|
|
||||||
|
|
||||||
static void pollfd_add(int fd, short events)
|
|
||||||
{
|
|
||||||
GPollFD *pollfd;
|
|
||||||
|
|
||||||
pollfd = g_slice_new(GPollFD);
|
|
||||||
pollfd->fd = fd;
|
|
||||||
pollfd->events = 0;
|
|
||||||
pollfd->revents = 0;
|
|
||||||
if (events & POLLIN)
|
|
||||||
pollfd->events |= G_IO_IN;
|
|
||||||
if (events & POLLOUT)
|
|
||||||
pollfd->events |= G_IO_OUT;
|
|
||||||
|
|
||||||
fdsource->pollfds = g_slist_prepend(fdsource->pollfds, pollfd);
|
|
||||||
g_source_add_poll((GSource *) fdsource, pollfd);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pollfd_added_cb(int fd, short events)
|
|
||||||
{
|
|
||||||
g_debug("now monitoring fd %d", fd);
|
|
||||||
pollfd_add(fd, events);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pollfd_removed_cb(int fd)
|
|
||||||
{
|
|
||||||
GSList *l;
|
|
||||||
|
|
||||||
g_debug("no longer monitoring fd %d", fd);
|
|
||||||
|
|
||||||
if (!fdsource->pollfds) {
|
|
||||||
g_debug("cannot remove from list as list is empty?");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (l = fdsource->pollfds; l != NULL; l = l->next) {
|
|
||||||
GPollFD *pollfd = l->data;
|
|
||||||
|
|
||||||
if (pollfd->fd != fd)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
g_source_remove_poll((GSource *) fdsource, pollfd);
|
|
||||||
g_slice_free(GPollFD, pollfd);
|
|
||||||
fdsource->pollfds = g_slist_delete_link(fdsource->pollfds, l);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_error("couldn't find fd %d in list\n", fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
int setup_pollfds(void)
|
|
||||||
{
|
|
||||||
ssize_t numfds;
|
|
||||||
size_t i;
|
|
||||||
struct fp_pollfd *fpfds;
|
|
||||||
GSource *gsource;
|
|
||||||
|
|
||||||
gsource = g_source_new(&sourcefuncs, sizeof(struct fdsource));
|
|
||||||
fdsource = (struct fdsource *) gsource;
|
|
||||||
fdsource->pollfds = NULL;
|
|
||||||
|
|
||||||
numfds = fp_get_pollfds(&fpfds);
|
|
||||||
if (numfds < 0) {
|
|
||||||
if (fpfds)
|
|
||||||
free(fpfds);
|
|
||||||
return (int) numfds;
|
|
||||||
} else if (numfds > 0) {
|
|
||||||
for (i = 0; i < numfds; i++) {
|
|
||||||
struct fp_pollfd *fpfd = &fpfds[i];
|
|
||||||
pollfd_add(fpfd->fd, fpfd->events);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free(fpfds);
|
|
||||||
fp_set_pollfd_notifiers(pollfd_added_cb, pollfd_removed_cb);
|
|
||||||
g_source_attach(gsource, NULL);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
27
src/loop.h
@ -1,27 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
|
|
||||||
*
|
|
||||||
* 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 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, write to the Free Software Foundation, Inc.,
|
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef POLL_H
|
|
||||||
|
|
||||||
#define POLL_H
|
|
||||||
|
|
||||||
int setup_pollfds(void);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
67
src/main.c
@ -25,16 +25,18 @@
|
|||||||
#include <dbus/dbus-glib-bindings.h>
|
#include <dbus/dbus-glib-bindings.h>
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <glib/gi18n.h>
|
#include <glib/gi18n.h>
|
||||||
#include <libfprint/fprint.h>
|
#include <fprint.h>
|
||||||
#include <glib-object.h>
|
#include <glib-object.h>
|
||||||
|
#include <glib-unix.h>
|
||||||
#include <gmodule.h>
|
#include <gmodule.h>
|
||||||
|
|
||||||
#include "fprintd.h"
|
#include "fprintd.h"
|
||||||
#include "loop.h"
|
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
#include "file_storage.h"
|
#include "file_storage.h"
|
||||||
|
|
||||||
extern DBusGConnection *fprintd_dbus_conn;
|
fp_storage store;
|
||||||
|
|
||||||
|
DBusGConnection *fprintd_dbus_conn = NULL;
|
||||||
static gboolean no_timeout = FALSE;
|
static gboolean no_timeout = FALSE;
|
||||||
static gboolean g_fatal_warnings = FALSE;
|
static gboolean g_fatal_warnings = FALSE;
|
||||||
|
|
||||||
@ -47,6 +49,7 @@ set_storage_file (void)
|
|||||||
store.print_data_load = &file_storage_print_data_load;
|
store.print_data_load = &file_storage_print_data_load;
|
||||||
store.print_data_delete = &file_storage_print_data_delete;
|
store.print_data_delete = &file_storage_print_data_delete;
|
||||||
store.discover_prints = &file_storage_discover_prints;
|
store.discover_prints = &file_storage_discover_prints;
|
||||||
|
store.discover_users = &file_storage_discover_users;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
@ -127,6 +130,14 @@ static const GOptionEntry entries[] = {
|
|||||||
{ NULL }
|
{ NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static gboolean sigterm_callback(gpointer data)
|
||||||
|
{
|
||||||
|
GMainLoop *loop = data;
|
||||||
|
|
||||||
|
g_main_loop_quit (loop);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
GOptionContext *context;
|
GOptionContext *context;
|
||||||
@ -135,7 +146,8 @@ int main(int argc, char **argv)
|
|||||||
FprintManager *manager;
|
FprintManager *manager;
|
||||||
DBusGProxy *driver_proxy;
|
DBusGProxy *driver_proxy;
|
||||||
guint32 request_name_ret;
|
guint32 request_name_ret;
|
||||||
int r = 0;
|
|
||||||
|
setlocale (LC_ALL, "");
|
||||||
|
|
||||||
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
|
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
|
||||||
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
|
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
|
||||||
@ -144,10 +156,6 @@ int main(int argc, char **argv)
|
|||||||
context = g_option_context_new ("Fingerprint handler daemon");
|
context = g_option_context_new ("Fingerprint handler daemon");
|
||||||
g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
|
g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
|
||||||
|
|
||||||
#if !GLIB_CHECK_VERSION (2, 36, 0)
|
|
||||||
g_type_init();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (g_option_context_parse (context, &argc, &argv, &error) == FALSE) {
|
if (g_option_context_parse (context, &argc, &argv, &error) == FALSE) {
|
||||||
g_warning ("couldn't parse command-line options: %s\n", error->message);
|
g_warning ("couldn't parse command-line options: %s\n", error->message);
|
||||||
g_error_free (error);
|
g_error_free (error);
|
||||||
@ -172,37 +180,14 @@ int main(int argc, char **argv)
|
|||||||
driver_proxy = dbus_g_proxy_new_for_name(fprintd_dbus_conn,
|
driver_proxy = dbus_g_proxy_new_for_name(fprintd_dbus_conn,
|
||||||
DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS);
|
DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS);
|
||||||
|
|
||||||
|
|
||||||
if (!org_freedesktop_DBus_request_name(driver_proxy, FPRINT_SERVICE_NAME,
|
|
||||||
0, &request_name_ret, &error)) {
|
|
||||||
g_warning("Failed to get name: %s", error->message);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request_name_ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
|
|
||||||
g_warning ("Got result code %u from requesting name", request_name_ret);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Load the configuration file,
|
/* Load the configuration file,
|
||||||
* and the default storage plugin */
|
* and the default storage plugin */
|
||||||
if (!load_conf())
|
if (!load_conf())
|
||||||
set_storage_file ();
|
set_storage_file ();
|
||||||
store.init ();
|
store.init ();
|
||||||
|
|
||||||
r = fp_init();
|
|
||||||
if (r < 0) {
|
|
||||||
g_warning("fprint init failed with error %d\n", r);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
loop = g_main_loop_new(NULL, FALSE);
|
loop = g_main_loop_new(NULL, FALSE);
|
||||||
|
g_unix_signal_add (SIGTERM, sigterm_callback, loop);
|
||||||
r = setup_pollfds();
|
|
||||||
if (r < 0) {
|
|
||||||
g_warning("pollfd setup failed\n");
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_debug("Launching FprintObject");
|
g_debug("Launching FprintObject");
|
||||||
|
|
||||||
@ -210,6 +195,22 @@ int main(int argc, char **argv)
|
|||||||
* all fprintd users */
|
* all fprintd users */
|
||||||
manager = fprint_manager_new(no_timeout);
|
manager = fprint_manager_new(no_timeout);
|
||||||
|
|
||||||
|
/* Obtain the well-known name after the manager has been initialized.
|
||||||
|
* Otherwise a client immediately enumerating the devices will not see
|
||||||
|
* any. */
|
||||||
|
if (!org_freedesktop_DBus_request_name(driver_proxy, FPRINT_SERVICE_NAME,
|
||||||
|
0, &request_name_ret, &error)) {
|
||||||
|
g_warning("Failed to get name: %s", error->message);
|
||||||
|
g_object_unref (manager);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request_name_ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
|
||||||
|
g_warning ("Got result code %u from requesting name", request_name_ret);
|
||||||
|
g_object_unref (manager);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
g_debug("D-Bus service launched with name: %s", FPRINT_SERVICE_NAME);
|
g_debug("D-Bus service launched with name: %s", FPRINT_SERVICE_NAME);
|
||||||
|
|
||||||
g_debug("entering main loop");
|
g_debug("entering main loop");
|
||||||
@ -218,8 +219,6 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
g_object_unref (manager);
|
g_object_unref (manager);
|
||||||
|
|
||||||
err:
|
|
||||||
fp_exit();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
133
src/manager.c
@ -22,12 +22,12 @@
|
|||||||
#include <dbus/dbus-glib-bindings.h>
|
#include <dbus/dbus-glib-bindings.h>
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <glib/gi18n.h>
|
#include <glib/gi18n.h>
|
||||||
#include <libfprint/fprint.h>
|
#include <fprint.h>
|
||||||
#include <glib-object.h>
|
#include <glib-object.h>
|
||||||
|
|
||||||
#include "fprintd.h"
|
#include "fprintd.h"
|
||||||
|
|
||||||
DBusGConnection *fprintd_dbus_conn;
|
extern DBusGConnection *fprintd_dbus_conn;
|
||||||
|
|
||||||
static gboolean fprint_manager_get_devices(FprintManager *manager,
|
static gboolean fprint_manager_get_devices(FprintManager *manager,
|
||||||
GPtrArray **devices, GError **error);
|
GPtrArray **devices, GError **error);
|
||||||
@ -35,27 +35,25 @@ static gboolean fprint_manager_get_default_device(FprintManager *manager,
|
|||||||
const char **device, GError **error);
|
const char **device, GError **error);
|
||||||
#include "manager-dbus-glue.h"
|
#include "manager-dbus-glue.h"
|
||||||
|
|
||||||
static GObjectClass *parent_class = NULL;
|
|
||||||
|
|
||||||
G_DEFINE_TYPE(FprintManager, fprint_manager, G_TYPE_OBJECT);
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
FpContext *context;
|
||||||
GSList *dev_registry;
|
GSList *dev_registry;
|
||||||
gboolean no_timeout;
|
gboolean no_timeout;
|
||||||
guint timeout_id;
|
guint timeout_id;
|
||||||
} FprintManagerPrivate;
|
} FprintManagerPrivate;
|
||||||
|
|
||||||
#define FPRINT_MANAGER_GET_PRIVATE(o) \
|
G_DEFINE_TYPE_WITH_CODE(FprintManager, fprint_manager, G_TYPE_OBJECT, G_ADD_PRIVATE (FprintManager))
|
||||||
(G_TYPE_INSTANCE_GET_PRIVATE ((o), FPRINT_TYPE_MANAGER, FprintManagerPrivate))
|
|
||||||
|
|
||||||
static void fprint_manager_finalize(GObject *object)
|
static void fprint_manager_finalize(GObject *object)
|
||||||
{
|
{
|
||||||
FprintManagerPrivate *priv = FPRINT_MANAGER_GET_PRIVATE (object);
|
FprintManagerPrivate *priv = fprint_manager_get_instance_private (FPRINT_MANAGER (object));
|
||||||
|
|
||||||
|
g_clear_object (&priv->context);
|
||||||
g_slist_free(priv->dev_registry);
|
g_slist_free(priv->dev_registry);
|
||||||
|
|
||||||
G_OBJECT_CLASS(parent_class)->finalize(object);
|
G_OBJECT_CLASS(fprint_manager_parent_class)->finalize(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fprint_manager_class_init(FprintManagerClass *klass)
|
static void fprint_manager_class_init(FprintManagerClass *klass)
|
||||||
@ -64,10 +62,7 @@ static void fprint_manager_class_init(FprintManagerClass *klass)
|
|||||||
&dbus_glib_fprint_manager_object_info);
|
&dbus_glib_fprint_manager_object_info);
|
||||||
dbus_g_error_domain_register (FPRINT_ERROR, FPRINT_ERROR_DBUS_INTERFACE, FPRINT_TYPE_ERROR);
|
dbus_g_error_domain_register (FPRINT_ERROR, FPRINT_ERROR_DBUS_INTERFACE, FPRINT_TYPE_ERROR);
|
||||||
|
|
||||||
g_type_class_add_private ((GObjectClass *) klass, sizeof (FprintManagerPrivate));
|
|
||||||
|
|
||||||
G_OBJECT_CLASS(klass)->finalize = fprint_manager_finalize;
|
G_OBJECT_CLASS(klass)->finalize = fprint_manager_finalize;
|
||||||
parent_class = g_type_class_peek_parent(klass);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static gchar *get_device_path(FprintDevice *rdev)
|
static gchar *get_device_path(FprintDevice *rdev)
|
||||||
@ -87,7 +82,7 @@ fprint_manager_timeout_cb (FprintManager *manager)
|
|||||||
static void
|
static void
|
||||||
fprint_manager_in_use_notified (FprintDevice *rdev, GParamSpec *spec, FprintManager *manager)
|
fprint_manager_in_use_notified (FprintDevice *rdev, GParamSpec *spec, FprintManager *manager)
|
||||||
{
|
{
|
||||||
FprintManagerPrivate *priv = FPRINT_MANAGER_GET_PRIVATE (manager);
|
FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);
|
||||||
guint num_devices_used = 0;
|
guint num_devices_used = 0;
|
||||||
GSList *l;
|
GSList *l;
|
||||||
gboolean in_use;
|
gboolean in_use;
|
||||||
@ -111,33 +106,80 @@ fprint_manager_in_use_notified (FprintDevice *rdev, GParamSpec *spec, FprintMana
|
|||||||
priv->timeout_id = g_timeout_add_seconds (TIMEOUT, (GSourceFunc) fprint_manager_timeout_cb, manager);
|
priv->timeout_id = g_timeout_add_seconds (TIMEOUT, (GSourceFunc) fprint_manager_timeout_cb, manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
device_added_cb (FprintManager *manager, FpDevice *device, FpContext *context)
|
||||||
|
{
|
||||||
|
FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);
|
||||||
|
FprintDevice *rdev = fprint_device_new(device);
|
||||||
|
g_autofree gchar *path = NULL;
|
||||||
|
|
||||||
|
g_signal_connect (G_OBJECT(rdev), "notify::in-use",
|
||||||
|
G_CALLBACK (fprint_manager_in_use_notified), manager);
|
||||||
|
|
||||||
|
priv->dev_registry = g_slist_prepend (priv->dev_registry, rdev);
|
||||||
|
path = get_device_path (rdev);
|
||||||
|
dbus_g_connection_register_g_object(fprintd_dbus_conn, path,
|
||||||
|
G_OBJECT(rdev));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
device_removed_cb (FprintManager *manager, FpDevice *device, FpContext *context)
|
||||||
|
{
|
||||||
|
FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);
|
||||||
|
GSList *item;
|
||||||
|
g_autofree gchar *path = NULL;
|
||||||
|
|
||||||
|
for (item = priv->dev_registry; item; item = item->next) {
|
||||||
|
FprintDevice *rdev;
|
||||||
|
g_autoptr(FpDevice) dev = NULL;
|
||||||
|
|
||||||
|
rdev = item->data;
|
||||||
|
|
||||||
|
g_object_get (rdev, "dev", &dev, NULL);
|
||||||
|
if (dev != device)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
priv->dev_registry = g_slist_delete_link (priv->dev_registry, item);
|
||||||
|
|
||||||
|
dbus_g_connection_unregister_g_object(fprintd_dbus_conn, G_OBJECT(rdev));
|
||||||
|
|
||||||
|
g_signal_handlers_disconnect_by_data (rdev, manager);
|
||||||
|
g_object_unref (rdev);
|
||||||
|
|
||||||
|
/* We cannot continue to iterate at this point, but we don't need to either */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The device that disappeared might have been in-use.
|
||||||
|
* Do we need to do anything else in this case to clean up more gracefully? */
|
||||||
|
fprint_manager_in_use_notified (NULL, NULL, manager);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
fprint_manager_init (FprintManager *manager)
|
fprint_manager_init (FprintManager *manager)
|
||||||
{
|
{
|
||||||
FprintManagerPrivate *priv = FPRINT_MANAGER_GET_PRIVATE (manager);
|
FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);
|
||||||
struct fp_dscv_dev **discovered_devs = fp_discover_devs();
|
|
||||||
struct fp_dscv_dev *ddev;
|
priv->context = fp_context_new ();
|
||||||
int i = 0;
|
|
||||||
|
/* And register the signals for initial enumeration and hotplug. */
|
||||||
|
g_signal_connect_object (priv->context,
|
||||||
|
"device-added",
|
||||||
|
(GCallback) device_added_cb,
|
||||||
|
manager,
|
||||||
|
G_CONNECT_SWAPPED);
|
||||||
|
|
||||||
|
g_signal_connect_object (priv->context,
|
||||||
|
"device-removed",
|
||||||
|
(GCallback) device_removed_cb,
|
||||||
|
manager,
|
||||||
|
G_CONNECT_SWAPPED);
|
||||||
|
|
||||||
|
/* Prepare everything by enumerating all devices. */
|
||||||
|
fp_context_enumerate (priv->context);
|
||||||
|
|
||||||
dbus_g_connection_register_g_object(fprintd_dbus_conn,
|
dbus_g_connection_register_g_object(fprintd_dbus_conn,
|
||||||
"/net/reactivated/Fprint/Manager", G_OBJECT(manager));
|
"/net/reactivated/Fprint/Manager", G_OBJECT(manager));
|
||||||
|
|
||||||
if (discovered_devs == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
while ((ddev = discovered_devs[i++]) != NULL) {
|
|
||||||
FprintDevice *rdev = fprint_device_new(ddev);
|
|
||||||
gchar *path;
|
|
||||||
|
|
||||||
g_signal_connect (G_OBJECT(rdev), "notify::in-use",
|
|
||||||
G_CALLBACK (fprint_manager_in_use_notified), manager);
|
|
||||||
|
|
||||||
priv->dev_registry = g_slist_prepend(priv->dev_registry, rdev);
|
|
||||||
path = get_device_path(rdev);
|
|
||||||
dbus_g_connection_register_g_object(fprintd_dbus_conn, path,
|
|
||||||
G_OBJECT(rdev));
|
|
||||||
g_free(path);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FprintManager *fprint_manager_new(gboolean no_timeout)
|
FprintManager *fprint_manager_new(gboolean no_timeout)
|
||||||
@ -146,7 +188,7 @@ FprintManager *fprint_manager_new(gboolean no_timeout)
|
|||||||
GObject *object;
|
GObject *object;
|
||||||
|
|
||||||
object = g_object_new(FPRINT_TYPE_MANAGER, NULL);
|
object = g_object_new(FPRINT_TYPE_MANAGER, NULL);
|
||||||
priv = FPRINT_MANAGER_GET_PRIVATE (object);
|
priv = fprint_manager_get_instance_private (FPRINT_MANAGER (object));
|
||||||
priv->no_timeout = no_timeout;
|
priv->no_timeout = no_timeout;
|
||||||
|
|
||||||
if (!priv->no_timeout)
|
if (!priv->no_timeout)
|
||||||
@ -158,11 +200,15 @@ FprintManager *fprint_manager_new(gboolean no_timeout)
|
|||||||
static gboolean fprint_manager_get_devices(FprintManager *manager,
|
static gboolean fprint_manager_get_devices(FprintManager *manager,
|
||||||
GPtrArray **devices, GError **error)
|
GPtrArray **devices, GError **error)
|
||||||
{
|
{
|
||||||
FprintManagerPrivate *priv = FPRINT_MANAGER_GET_PRIVATE (manager);
|
FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);
|
||||||
GSList *elem = g_slist_reverse(g_slist_copy(priv->dev_registry));
|
GSList *elem;
|
||||||
GSList *l;
|
GSList *l;
|
||||||
int num_open = g_slist_length(elem);
|
int num_open;
|
||||||
GPtrArray *devs = g_ptr_array_sized_new(num_open);
|
GPtrArray *devs;
|
||||||
|
|
||||||
|
elem = g_slist_reverse(g_slist_copy(priv->dev_registry));
|
||||||
|
num_open = g_slist_length(elem);
|
||||||
|
devs = g_ptr_array_sized_new(num_open);
|
||||||
|
|
||||||
if (num_open > 0) {
|
if (num_open > 0) {
|
||||||
for (l = elem; l != NULL; l = l->next) {
|
for (l = elem; l != NULL; l = l->next) {
|
||||||
@ -180,9 +226,12 @@ static gboolean fprint_manager_get_devices(FprintManager *manager,
|
|||||||
static gboolean fprint_manager_get_default_device(FprintManager *manager,
|
static gboolean fprint_manager_get_default_device(FprintManager *manager,
|
||||||
const char **device, GError **error)
|
const char **device, GError **error)
|
||||||
{
|
{
|
||||||
FprintManagerPrivate *priv = FPRINT_MANAGER_GET_PRIVATE (manager);
|
FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);
|
||||||
GSList *elem = priv->dev_registry;
|
GSList *elem;;
|
||||||
int num_open = g_slist_length(elem);
|
int num_open;
|
||||||
|
|
||||||
|
elem = priv->dev_registry;
|
||||||
|
num_open = g_slist_length(elem);
|
||||||
|
|
||||||
if (num_open > 0) {
|
if (num_open > 0) {
|
||||||
*device = get_device_path (g_slist_last (elem)->data);
|
*device = get_device_path (g_slist_last (elem)->data);
|
||||||
|
|||||||
81
src/meson.build
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
fprintd_marshal = gnome.genmarshal('fprintd-marshal',
|
||||||
|
prefix: 'fprintd_marshal',
|
||||||
|
sources: 'fprintd-marshal.list',
|
||||||
|
valist_marshallers: true,
|
||||||
|
)
|
||||||
|
|
||||||
|
bash = find_program('bash')
|
||||||
|
dbus_binding_tool = find_program('dbus-binding-tool')
|
||||||
|
dbus_interfaces = ['Manager', 'Device']
|
||||||
|
dbus_interfaces_files = []
|
||||||
|
dbus_server_glue_sources = []
|
||||||
|
|
||||||
|
foreach interface_name: dbus_interfaces
|
||||||
|
interface = interface_name.to_lower()
|
||||||
|
interface_file = interface + '.xml'
|
||||||
|
glue_name = interface + '-dbus-glue.h'
|
||||||
|
dbus_server_glue_sources += custom_target(glue_name,
|
||||||
|
input: interface_file,
|
||||||
|
output: glue_name,
|
||||||
|
command: [
|
||||||
|
dbus_binding_tool,
|
||||||
|
'--prefix=fprint_' + interface,
|
||||||
|
'--mode=glib-server',
|
||||||
|
'--output=@OUTPUT@',
|
||||||
|
'@INPUT@',
|
||||||
|
])
|
||||||
|
|
||||||
|
dbus_interfaces_files += custom_target('dbus_interface_' + interface,
|
||||||
|
input: interface_file,
|
||||||
|
output: 'net.reactivated.Fprint.@0@.xml'.format(interface_name),
|
||||||
|
command: ['cp', '@INPUT@', '@OUTPUT@'],
|
||||||
|
install: true,
|
||||||
|
install_dir: dbus_interfaces_dir,
|
||||||
|
)
|
||||||
|
endforeach
|
||||||
|
|
||||||
|
fprintd_deps = declare_dependency(
|
||||||
|
include_directories: [
|
||||||
|
include_directories('..'),
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
dbus_glib_dep,
|
||||||
|
glib_dep,
|
||||||
|
gio_dep,
|
||||||
|
gmodule_dep,
|
||||||
|
libfprint_dep,
|
||||||
|
polkit_gobject_dep,
|
||||||
|
],
|
||||||
|
compile_args: [
|
||||||
|
'-DG_LOG_DOMAIN="@0@"'.format(meson.project_name()),
|
||||||
|
'-DLOCALEDIR="@0@"'.format(localedir),
|
||||||
|
'-DPLUGINDIR="@0@"'.format(fprintd_plugindir),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
libfprintd_private = static_library('fprintd-private',
|
||||||
|
sources: [
|
||||||
|
'device.c',
|
||||||
|
'fprintd.h',
|
||||||
|
'manager.c',
|
||||||
|
dbus_server_glue_sources,
|
||||||
|
fprintd_marshal,
|
||||||
|
],
|
||||||
|
dependencies: fprintd_deps,
|
||||||
|
gnu_symbol_visibility: 'hidden',
|
||||||
|
)
|
||||||
|
|
||||||
|
fprintd = executable('fprintd',
|
||||||
|
sources: [
|
||||||
|
'file_storage.c',
|
||||||
|
'file_storage.h',
|
||||||
|
'fprintd.h',
|
||||||
|
'main.c',
|
||||||
|
'storage.h',
|
||||||
|
],
|
||||||
|
dependencies: fprintd_deps,
|
||||||
|
link_with: libfprintd_private,
|
||||||
|
gnu_symbol_visibility: 'hidden',
|
||||||
|
install: true,
|
||||||
|
install_dir: fprintd_installdir,
|
||||||
|
)
|
||||||
@ -18,17 +18,18 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef STORAGE_H
|
#pragma once
|
||||||
|
|
||||||
#define STORAGE_H
|
typedef int (*storage_print_data_save)(FpPrint *print);
|
||||||
|
typedef int (*storage_print_data_load)(FpDevice *dev,
|
||||||
typedef int (*storage_print_data_save)(struct fp_print_data *data,
|
FpFinger finger,
|
||||||
enum fp_finger finger, const char *username);
|
const char *username,
|
||||||
typedef int (*storage_print_data_load)(struct fp_dev *dev,
|
FpPrint **print);
|
||||||
enum fp_finger finger, struct fp_print_data **data, const char *username);
|
typedef int (*storage_print_data_delete)(FpDevice *dev,
|
||||||
typedef int (*storage_print_data_delete)(struct fp_dscv_dev *dev,
|
FpFinger finger,
|
||||||
enum fp_finger finger, const char *username);
|
const char *username);
|
||||||
typedef GSList *(*storage_discover_prints)(struct fp_dscv_dev *dev, const char *username);
|
typedef GSList *(*storage_discover_prints)(FpDevice *dev, const char *username);
|
||||||
|
typedef GSList *(*storage_discover_users)(void);
|
||||||
typedef int (*storage_init)(void);
|
typedef int (*storage_init)(void);
|
||||||
typedef int (*storage_deinit)(void);
|
typedef int (*storage_deinit)(void);
|
||||||
|
|
||||||
@ -39,12 +40,10 @@ struct storage {
|
|||||||
storage_print_data_load print_data_load;
|
storage_print_data_load print_data_load;
|
||||||
storage_print_data_delete print_data_delete;
|
storage_print_data_delete print_data_delete;
|
||||||
storage_discover_prints discover_prints;
|
storage_discover_prints discover_prints;
|
||||||
|
storage_discover_users discover_users;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct storage fp_storage;
|
typedef struct storage fp_storage;
|
||||||
|
|
||||||
/* The currently setup store */
|
/* The currently setup store */
|
||||||
fp_storage store;
|
extern fp_storage store;
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|||||||
@ -1,35 +0,0 @@
|
|||||||
BUILT_SOURCES = manager-dbus-glue.h device-dbus-glue.h $(MARSHALFILES)
|
|
||||||
noinst_HEADERS = $(BUILT_SOURCES)
|
|
||||||
CLEANFILES = $(BUILT_SOURCES)
|
|
||||||
|
|
||||||
bin_PROGRAMS = fprintd-verify fprintd-enroll fprintd-list fprintd-delete
|
|
||||||
|
|
||||||
fprintd_verify_SOURCES = verify.c $(MARSHALFILES)
|
|
||||||
fprintd_verify_CFLAGS = $(WARN_CFLAGS) $(GLIB_CFLAGS)
|
|
||||||
fprintd_verify_LDADD = $(GLIB_LIBS)
|
|
||||||
|
|
||||||
fprintd_enroll_SOURCES = enroll.c $(MARSHALFILES)
|
|
||||||
fprintd_enroll_CFLAGS = $(WARN_CFLAGS) $(GLIB_CFLAGS) -I$(top_srcdir)/pam
|
|
||||||
fprintd_enroll_LDADD = $(GLIB_LIBS)
|
|
||||||
|
|
||||||
fprintd_list_SOURCES = list.c
|
|
||||||
fprintd_list_CFLAGS = $(WARN_CFLAGS) $(GLIB_CFLAGS)
|
|
||||||
fprintd_list_LDADD = $(GLIB_LIBS)
|
|
||||||
|
|
||||||
fprintd_delete_SOURCES = delete.c
|
|
||||||
fprintd_delete_CFLAGS = $(WARN_CFLAGS) $(GLIB_CFLAGS)
|
|
||||||
fprintd_delete_LDADD = $(GLIB_LIBS)
|
|
||||||
|
|
||||||
manager-dbus-glue.h: ../src/manager.xml
|
|
||||||
dbus-binding-tool --prefix=fprint_manager --mode=glib-client $< --output=$@
|
|
||||||
|
|
||||||
device-dbus-glue.h: ../src/device.xml
|
|
||||||
dbus-binding-tool --prefix=fprint_device --mode=glib-client $< --output=$@
|
|
||||||
|
|
||||||
MARSHALFILES = marshal.c marshal.h
|
|
||||||
GLIB_GENMARSHAL=`pkg-config --variable=glib_genmarshal glib-2.0`
|
|
||||||
|
|
||||||
marshal.h: $(top_srcdir)/src/fprintd-marshal.list
|
|
||||||
( $(GLIB_GENMARSHAL) --prefix=fprintd_marshal $(top_srcdir)/src/fprintd-marshal.list --header > marshal.h )
|
|
||||||
marshal.c: marshal.h
|
|
||||||
( $(GLIB_GENMARSHAL) --prefix=fprintd_marshal $(top_srcdir)/src/fprintd-marshal.list --body --header > marshal.c )
|
|
||||||
346
tests/dbusmock/fprintd.py
Normal file
@ -0,0 +1,346 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
'''fprintd mock template
|
||||||
|
|
||||||
|
This creates the expected methods and properties of the
|
||||||
|
net.reactivated.Fprint.Manager object (/net/reactivated/Fprint/Manager)
|
||||||
|
but no devices.
|
||||||
|
'''
|
||||||
|
|
||||||
|
# This program is free software; you can redistribute it and/or modify it under
|
||||||
|
# the terms of the GNU Lesser General Public License as published by the Free
|
||||||
|
# Software Foundation; either version 3 of the License, or (at your option) any
|
||||||
|
# later version. See http://www.gnu.org/copyleft/lgpl.html for the full text
|
||||||
|
# of the license.
|
||||||
|
|
||||||
|
__author__ = 'Bastien Nocera'
|
||||||
|
__email__ = 'hadess@hadess.net'
|
||||||
|
__copyright__ = '(c) 2020 Red Hat Inc.'
|
||||||
|
__license__ = 'LGPL 3+'
|
||||||
|
|
||||||
|
import dbus
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
from dbusmock import MOCK_IFACE, mockobject
|
||||||
|
|
||||||
|
BUS_NAME = 'net.reactivated.Fprint'
|
||||||
|
MAIN_OBJ = '/net/reactivated/Fprint/Manager'
|
||||||
|
SYSTEM_BUS = True
|
||||||
|
IS_OBJECT_MANAGER = False
|
||||||
|
|
||||||
|
MAIN_IFACE = 'net.reactivated.Fprint.Manager'
|
||||||
|
MANAGER_MOCK_IFACE = 'net.reactivated.Fprint.Manager.Mock'
|
||||||
|
|
||||||
|
DEVICE_IFACE = 'net.reactivated.Fprint.Device'
|
||||||
|
DEVICE_MOCK_IFACE = 'net.reactivated.Fprint.Device.Mock'
|
||||||
|
|
||||||
|
VALID_FINGER_NAMES = [
|
||||||
|
'left-thumb',
|
||||||
|
'left-index-finger',
|
||||||
|
'left-middle-finger',
|
||||||
|
'left-ring-finger',
|
||||||
|
'left-little-finger',
|
||||||
|
'right-thumb',
|
||||||
|
'right-index-finger',
|
||||||
|
'right-middle-finger',
|
||||||
|
'right-ring-finger',
|
||||||
|
'right-little-finger'
|
||||||
|
]
|
||||||
|
|
||||||
|
VALID_VERIFY_STATUS = [
|
||||||
|
'verify-no-match',
|
||||||
|
'verify-match',
|
||||||
|
'verify-retry-scan',
|
||||||
|
'verify-swipe-too-short',
|
||||||
|
'verify-finger-not-centered',
|
||||||
|
'verify-remove-and-retry',
|
||||||
|
'verify-disconnected',
|
||||||
|
'verify-unknown-error'
|
||||||
|
]
|
||||||
|
|
||||||
|
VALID_ENROLL_STATUS = [
|
||||||
|
'enroll-completed',
|
||||||
|
'enroll-failed',
|
||||||
|
'enroll-stage-passed',
|
||||||
|
'enroll-retry-scan',
|
||||||
|
'enroll-swipe-too-short',
|
||||||
|
'enroll-finger-not-centered',
|
||||||
|
'enroll-remove-and-retry',
|
||||||
|
'enroll-data-full',
|
||||||
|
'enroll-disconnected',
|
||||||
|
'enroll-unknown-error'
|
||||||
|
]
|
||||||
|
|
||||||
|
def load(mock, parameters):
|
||||||
|
fprintd = mockobject.objects[MAIN_OBJ]
|
||||||
|
mock.last_device_id = 0
|
||||||
|
fprintd.fingers = {}
|
||||||
|
mock.loop = asyncio.new_event_loop()
|
||||||
|
|
||||||
|
@dbus.service.method(MAIN_IFACE,
|
||||||
|
in_signature='', out_signature='ao')
|
||||||
|
def GetDevices(self):
|
||||||
|
return [(k) for k in mockobject.objects.keys() if "/Device/" in k]
|
||||||
|
|
||||||
|
@dbus.service.method(MAIN_IFACE,
|
||||||
|
in_signature='', out_signature='o')
|
||||||
|
def GetDefaultDevice(self):
|
||||||
|
devices = self.GetDevices()
|
||||||
|
if len(devices) < 1:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
'No devices available',
|
||||||
|
name='net.reactivated.Fprint.Error.NoSuchDevice')
|
||||||
|
return devices[0]
|
||||||
|
|
||||||
|
@dbus.service.method(MANAGER_MOCK_IFACE,
|
||||||
|
in_signature='sis', out_signature='s')
|
||||||
|
def AddDevice(self, device_name, num_enroll_stages, scan_type):
|
||||||
|
'''Convenience method to add a fingerprint reader device
|
||||||
|
|
||||||
|
You have to specify a device name, the number of enrollment
|
||||||
|
stages it would use (> 0) and the scan type, as a string
|
||||||
|
(either 'press' or 'swipe')
|
||||||
|
'''
|
||||||
|
|
||||||
|
if scan_type not in ['swipe', 'press']:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
'Invalid scan_type \'%s\'.' % scan_type,
|
||||||
|
name='org.freedesktop.DBus.Error.InvalidArgs')
|
||||||
|
|
||||||
|
if num_enroll_stages <= 0:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
'Invalid num_enroll_stages \'%s\'.' % num_enroll_stages,
|
||||||
|
name='org.freedesktop.DBus.Error.InvalidArgs')
|
||||||
|
|
||||||
|
self.last_device_id += 1
|
||||||
|
path = '/net/reactivated/Fprint/Device/%d' % self.last_device_id
|
||||||
|
device_properties = {
|
||||||
|
'name': dbus.String(device_name, variant_level=1),
|
||||||
|
'num-enroll-stages': dbus.Int32(num_enroll_stages, variant_level=1),
|
||||||
|
'scan-type': scan_type
|
||||||
|
}
|
||||||
|
|
||||||
|
self.AddObject(path,
|
||||||
|
DEVICE_IFACE,
|
||||||
|
# Properties
|
||||||
|
device_properties,
|
||||||
|
# Methods
|
||||||
|
[
|
||||||
|
('ListEnrolledFingers', 's', 'as', ListEnrolledFingers),
|
||||||
|
('DeleteEnrolledFingers', 's', '', DeleteEnrolledFingers),
|
||||||
|
('DeleteEnrolledFingers2', '', '', DeleteEnrolledFingers2),
|
||||||
|
('Claim', 's', '', Claim),
|
||||||
|
('Release', '', '', Release),
|
||||||
|
('VerifyStart', 's', '', VerifyStart),
|
||||||
|
('VerifyStop', '', '', VerifyStop),
|
||||||
|
('EnrollStart', 's', '', EnrollStart),
|
||||||
|
('EnrollStop', '', '', EnrollStop)
|
||||||
|
])
|
||||||
|
|
||||||
|
device = mockobject.objects[path]
|
||||||
|
device.fingers = {}
|
||||||
|
device.claimed_user = None
|
||||||
|
device.action = None
|
||||||
|
device.verify_script = []
|
||||||
|
|
||||||
|
return path
|
||||||
|
|
||||||
|
@dbus.service.method(DEVICE_IFACE,
|
||||||
|
in_signature='s', out_signature='as')
|
||||||
|
def ListEnrolledFingers(device, user):
|
||||||
|
if user in device.fingers:
|
||||||
|
return device.fingers[user]
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
'No enrolled prints in device %s for user %s' % (device.path, user),
|
||||||
|
name='net.reactivated.Fprint.Error.NoEnrolledPrints')
|
||||||
|
|
||||||
|
@dbus.service.method(DEVICE_IFACE,
|
||||||
|
in_signature='s', out_signature='')
|
||||||
|
def DeleteEnrolledFingers(device, user):
|
||||||
|
device.fingers[user] = []
|
||||||
|
|
||||||
|
@dbus.service.method(DEVICE_IFACE,
|
||||||
|
in_signature='', out_signature='')
|
||||||
|
def DeleteEnrolledFingers2(device):
|
||||||
|
if not device.claimed_user:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
'Device was not claimed before use',
|
||||||
|
name='net.reactivated.Fprint.Error.ClaimDevice')
|
||||||
|
device.fingers[device.claimed_user] = []
|
||||||
|
|
||||||
|
@dbus.service.method(DEVICE_IFACE,
|
||||||
|
in_signature='s', out_signature='')
|
||||||
|
def Claim(device, user):
|
||||||
|
if device.claimed_user:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
'Device already in use by %s' % device.claimed_user,
|
||||||
|
name='net.reactivated.Fprint.Error.AlreadyInUse')
|
||||||
|
|
||||||
|
device.claimed_user = user
|
||||||
|
|
||||||
|
@dbus.service.method(DEVICE_IFACE,
|
||||||
|
in_signature='', out_signature='')
|
||||||
|
def Release(device):
|
||||||
|
if not device.claimed_user:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
'Device was not claimed before use',
|
||||||
|
name='net.reactivated.Fprint.Error.ClaimDevice')
|
||||||
|
device.claimed_user = None
|
||||||
|
|
||||||
|
def can_verify_finger(device, finger_name):
|
||||||
|
# We should already have checked that there are enrolled fingers
|
||||||
|
if finger_name == 'any':
|
||||||
|
return True
|
||||||
|
if finger_name in device.fingers[device.claimed_user]:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def send_verify_script(device, script):
|
||||||
|
for [result, done, timeout] in device.verify_script:
|
||||||
|
await asyncio.sleep(timeout)
|
||||||
|
device.EmitSignal(DEVICE_IFACE, 'VerifyStatus', 'sb', [
|
||||||
|
result,
|
||||||
|
done
|
||||||
|
])
|
||||||
|
|
||||||
|
@dbus.service.method(DEVICE_IFACE,
|
||||||
|
in_signature='s', out_signature='')
|
||||||
|
def VerifyStart(device, finger_name):
|
||||||
|
if not device.claimed_user:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
'Device was not claimed before use',
|
||||||
|
name='net.reactivated.Fprint.Error.ClaimDevice')
|
||||||
|
if device.claimed_user not in device.fingers:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
'No enrolled prints for user \'%s\'' % device.claimed_user,
|
||||||
|
name='net.reactivated.Fprint.Error.NoEnrolledPrints')
|
||||||
|
if not finger_name:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
'Invalid empty finger_name.',
|
||||||
|
name='org.freedesktop.DBus.Error.InvalidArgs')
|
||||||
|
if not can_verify_finger(device, finger_name):
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
'Finger \'%s\' not enrolled.' % finger_name,
|
||||||
|
name='org.freedesktop.DBus.Error.Internal')
|
||||||
|
if device.action:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
'Action \'%s\' already in progress' % device.action,
|
||||||
|
name='net.reactivated.Fprint.Error.AlreadyInUse')
|
||||||
|
device.action = 'verify'
|
||||||
|
|
||||||
|
if finger_name == 'any':
|
||||||
|
finger_name = device.fingers[device.claimed_user][0]
|
||||||
|
device.EmitSignal(DEVICE_IFACE, 'VerifyFingerSelected', 's', [
|
||||||
|
finger_name
|
||||||
|
])
|
||||||
|
|
||||||
|
if device.verify_script is not None and len(device.verify_script) > 0:
|
||||||
|
asyncio.run(send_verify_script(device, device.verify_script))
|
||||||
|
|
||||||
|
|
||||||
|
@dbus.service.method(DEVICE_MOCK_IFACE,
|
||||||
|
in_signature='sb', out_signature='')
|
||||||
|
def EmitVerifyStatus(device, result, done):
|
||||||
|
if (not device.action) or (device.action != 'verify'):
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
'Cannot send verify statuses when not verifying',
|
||||||
|
name='org.freedesktop.DBus.Error.InvalidArgs')
|
||||||
|
if result not in VALID_VERIFY_STATUS:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
'Unknown verify status \'%s\'' % result,
|
||||||
|
name='org.freedesktop.DBus.Error.InvalidArgs')
|
||||||
|
device.EmitSignal(DEVICE_IFACE, 'VerifyStatus', 'sb', [
|
||||||
|
result,
|
||||||
|
done
|
||||||
|
])
|
||||||
|
|
||||||
|
@dbus.service.method(DEVICE_IFACE,
|
||||||
|
in_signature='', out_signature='')
|
||||||
|
def VerifyStop(device):
|
||||||
|
if device.action != 'verify':
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
'No verification to stop',
|
||||||
|
name='net.reactivated.Fprint.Error.NoActionInProgress')
|
||||||
|
device.action = None
|
||||||
|
|
||||||
|
@dbus.service.method(DEVICE_IFACE,
|
||||||
|
in_signature='s', out_signature='')
|
||||||
|
def EnrollStart(device, finger_name):
|
||||||
|
if finger_name not in VALID_FINGER_NAMES:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
'Invalid finger name \'%s\'' % finger_name,
|
||||||
|
name='net.reactivated.Fprint.Error.InvalidFingername')
|
||||||
|
if not device.claimed_user:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
'Device was not claimed before use',
|
||||||
|
name='net.reactivated.Fprint.Error.ClaimDevice')
|
||||||
|
if device.action:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
'Action \'%s\' already in progress' % device.action,
|
||||||
|
name='net.reactivated.Fprint.Error.AlreadyInUse')
|
||||||
|
device.action = 'enroll'
|
||||||
|
|
||||||
|
@dbus.service.method(DEVICE_MOCK_IFACE,
|
||||||
|
in_signature='sb', out_signature='')
|
||||||
|
def EmitEnrollStatus(device, result, done):
|
||||||
|
if (not device.action) or (device.action != 'enroll'):
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
'Cannot send enroll statuses when not enrolling',
|
||||||
|
name='org.freedesktop.DBus.Error.InvalidArgs')
|
||||||
|
if result not in VALID_ENROLL_STATUS:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
'Unknown enroll status \'%s\'' % result,
|
||||||
|
name='org.freedesktop.DBus.Error.InvalidArgs')
|
||||||
|
device.EmitSignal(DEVICE_IFACE, 'EnrollStatus', 'sb', [
|
||||||
|
result,
|
||||||
|
done
|
||||||
|
])
|
||||||
|
# FIXME save enrolled finger?
|
||||||
|
|
||||||
|
@dbus.service.method(DEVICE_IFACE,
|
||||||
|
in_signature='', out_signature='')
|
||||||
|
def EnrollStop(device):
|
||||||
|
if device.action != 'enroll':
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
'No enrollment to stop',
|
||||||
|
name='net.reactivated.Fprint.Error.NoActionInProgress')
|
||||||
|
device.action = None
|
||||||
|
|
||||||
|
@dbus.service.method(DEVICE_MOCK_IFACE,
|
||||||
|
in_signature='sas', out_signature='')
|
||||||
|
def SetEnrolledFingers(device, user, fingers):
|
||||||
|
'''Convenience method to set the list of enrolled fingers.
|
||||||
|
|
||||||
|
The device_path is the return value from AddDevice(), and the
|
||||||
|
array of fingers must only contain valid finger names.
|
||||||
|
|
||||||
|
Returns nothing.
|
||||||
|
'''
|
||||||
|
|
||||||
|
if len(fingers) < 1:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
'Fingers array must not be empty',
|
||||||
|
name='org.freedesktop.DBus.Error.InvalidArgs')
|
||||||
|
|
||||||
|
for k in fingers:
|
||||||
|
if k not in VALID_FINGER_NAMES:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
'Invalid finger name \'%s\'' % k,
|
||||||
|
name='org.freedesktop.DBus.Error.InvalidArgs')
|
||||||
|
|
||||||
|
device.fingers[user] = fingers
|
||||||
|
|
||||||
|
@dbus.service.method(DEVICE_MOCK_IFACE,
|
||||||
|
in_signature='a(sbi)', out_signature='')
|
||||||
|
def SetVerifyScript(device, script):
|
||||||
|
'''Convenience method to set the verification script.
|
||||||
|
|
||||||
|
After VerifyStart is called, signal results will be sent in order after
|
||||||
|
a certain timeout declared in seconds. The array contains each
|
||||||
|
'result' followed by the 'done' argument for VerifyStatus, and the
|
||||||
|
amount of time to wait before each signal is sent.
|
||||||
|
|
||||||
|
Returns nothing.
|
||||||
|
'''
|
||||||
|
|
||||||
|
device.verify_script = script
|
||||||
530
tests/fprintd.py
Executable file
@ -0,0 +1,530 @@
|
|||||||
|
#! /usr/bin/env python3
|
||||||
|
# Copyright © 2017, 2019 Red Hat, Inc
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 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
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
# Authors:
|
||||||
|
# Christian J. Kellner <christian@kellner.me>
|
||||||
|
# Benjamin Berg <bberg@redhat.com>
|
||||||
|
# Marco Trevisan <marco.trevisan@canonical.com>
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
import time
|
||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import glob
|
||||||
|
import shutil
|
||||||
|
import socket
|
||||||
|
import struct
|
||||||
|
import dbusmock
|
||||||
|
import gi
|
||||||
|
from gi.repository import GLib, Gio
|
||||||
|
import cairo
|
||||||
|
|
||||||
|
try:
|
||||||
|
from subprocess import DEVNULL
|
||||||
|
except ImportError:
|
||||||
|
DEVNULL = open(os.devnull, 'wb')
|
||||||
|
|
||||||
|
SERVICE_FILE = '/usr/share/dbus-1/system-services/net.reactivated.Fprint.service'
|
||||||
|
|
||||||
|
def get_timeout(topic='default'):
|
||||||
|
vals = {
|
||||||
|
'valgrind': {
|
||||||
|
'test': 300,
|
||||||
|
'default': 20,
|
||||||
|
'daemon_start': 60
|
||||||
|
},
|
||||||
|
'default': {
|
||||||
|
'test': 60,
|
||||||
|
'default': 3,
|
||||||
|
'daemon_start': 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
valgrind = os.getenv('VALGRIND')
|
||||||
|
lut = vals['valgrind' if valgrind is not None else 'default']
|
||||||
|
if topic not in lut:
|
||||||
|
raise ValueError('invalid topic')
|
||||||
|
return lut[topic]
|
||||||
|
|
||||||
|
|
||||||
|
# Copied from libfprint tests
|
||||||
|
class Connection:
|
||||||
|
|
||||||
|
def __init__(self, addr):
|
||||||
|
self.addr = addr
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
self.con = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||||
|
self.con.connect(self.addr)
|
||||||
|
return self.con
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
self.con.close()
|
||||||
|
del self.con
|
||||||
|
|
||||||
|
def load_image(img):
|
||||||
|
png = cairo.ImageSurface.create_from_png(img)
|
||||||
|
|
||||||
|
# Cairo wants 4 byte aligned rows, so just add a few pixel if necessary
|
||||||
|
w = png.get_width()
|
||||||
|
h = png.get_height()
|
||||||
|
w = (w + 3) // 4 * 4
|
||||||
|
h = (h + 3) // 4 * 4
|
||||||
|
img = cairo.ImageSurface(cairo.Format.A8, w, h)
|
||||||
|
cr = cairo.Context(img)
|
||||||
|
|
||||||
|
cr.set_source_rgba(1, 1, 1, 1)
|
||||||
|
cr.paint()
|
||||||
|
|
||||||
|
cr.set_source_rgba(0, 0, 0, 0)
|
||||||
|
cr.set_operator(cairo.OPERATOR_SOURCE)
|
||||||
|
|
||||||
|
cr.set_source_surface(png)
|
||||||
|
cr.paint()
|
||||||
|
|
||||||
|
return img
|
||||||
|
|
||||||
|
if hasattr(os.environ, 'TOPSRCDIR'):
|
||||||
|
root = os.environ['TOPSRCDIR']
|
||||||
|
else:
|
||||||
|
root = os.path.join(os.path.dirname(__file__), '..')
|
||||||
|
|
||||||
|
imgdir = os.path.join(root, 'tests', 'prints')
|
||||||
|
|
||||||
|
ctx = GLib.main_context_default()
|
||||||
|
|
||||||
|
class FPrintdTest(dbusmock.DBusTestCase):
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def path_from_service_file(sf):
|
||||||
|
with open(SERVICE_FILE) as f:
|
||||||
|
for line in f:
|
||||||
|
if not line.startswith('Exec='):
|
||||||
|
continue
|
||||||
|
return line.split('=', 1)[1].strip()
|
||||||
|
return None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
fprintd = None
|
||||||
|
|
||||||
|
if 'FPRINT_BUILD_DIR' in os.environ:
|
||||||
|
print('Testing local build')
|
||||||
|
build_dir = os.environ['FPRINT_BUILD_DIR']
|
||||||
|
fprintd = os.path.join(build_dir, 'fprintd')
|
||||||
|
elif 'UNDER_JHBUILD' in os.environ:
|
||||||
|
print('Testing JHBuild version')
|
||||||
|
jhbuild_prefix = os.environ['JHBUILD_PREFIX']
|
||||||
|
fprintd = os.path.join(jhbuild_prefix, 'libexec', 'fprintd')
|
||||||
|
else:
|
||||||
|
print('Testing installed system binaries')
|
||||||
|
fprintd = cls.path_from_service_file(SERVICE_FILE)
|
||||||
|
|
||||||
|
assert fprintd is not None, 'failed to find daemon'
|
||||||
|
cls.paths = {'daemon': fprintd }
|
||||||
|
|
||||||
|
|
||||||
|
cls.tmpdir = tempfile.mkdtemp(prefix='libfprint-')
|
||||||
|
|
||||||
|
cls.sockaddr = os.path.join(cls.tmpdir, 'virtual-image.socket')
|
||||||
|
os.environ['FP_VIRTUAL_IMAGE'] = cls.sockaddr
|
||||||
|
|
||||||
|
cls.prints = {}
|
||||||
|
for f in glob.glob(os.path.join(imgdir, '*.png')):
|
||||||
|
n = os.path.basename(f)[:-4]
|
||||||
|
cls.prints[n] = load_image(f)
|
||||||
|
|
||||||
|
|
||||||
|
cls.test_bus = Gio.TestDBus.new(Gio.TestDBusFlags.NONE)
|
||||||
|
cls.test_bus.up()
|
||||||
|
try:
|
||||||
|
del os.environ['DBUS_SESSION_BUS_ADDRESS']
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
os.environ['DBUS_SYSTEM_BUS_ADDRESS'] = cls.test_bus.get_bus_address()
|
||||||
|
cls.dbus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
cls.test_bus.down()
|
||||||
|
shutil.rmtree(cls.tmpdir)
|
||||||
|
dbusmock.DBusTestCase.tearDownClass()
|
||||||
|
|
||||||
|
|
||||||
|
def daemon_start(self):
|
||||||
|
timeout = get_timeout('daemon_start') # seconds
|
||||||
|
env = os.environ.copy()
|
||||||
|
env['G_DEBUG'] = 'fatal-criticals'
|
||||||
|
env['STATE_DIRECTORY'] = self.state_dir
|
||||||
|
env['RUNTIME_DIRECTORY'] = self.run_dir
|
||||||
|
|
||||||
|
argv = [self.paths['daemon'], '-t']
|
||||||
|
valgrind = os.getenv('VALGRIND')
|
||||||
|
if valgrind is not None:
|
||||||
|
argv.insert(0, 'valgrind')
|
||||||
|
argv.insert(1, '--leak-check=full')
|
||||||
|
if os.path.exists(valgrind):
|
||||||
|
argv.insert(2, '--suppressions=%s' % valgrind)
|
||||||
|
self.valgrind = True
|
||||||
|
self.daemon = subprocess.Popen(argv,
|
||||||
|
env=env,
|
||||||
|
stdout=None,
|
||||||
|
stderr=subprocess.STDOUT)
|
||||||
|
self.device = None
|
||||||
|
|
||||||
|
timeout_count = timeout * 10
|
||||||
|
timeout_sleep = 0.1
|
||||||
|
while timeout_count > 0:
|
||||||
|
time.sleep(timeout_sleep)
|
||||||
|
timeout_count -= 1
|
||||||
|
try:
|
||||||
|
self.manager = Gio.DBusProxy.new_sync(self.dbus,
|
||||||
|
Gio.DBusProxyFlags.DO_NOT_AUTO_START,
|
||||||
|
None,
|
||||||
|
'net.reactivated.Fprint',
|
||||||
|
'/net/reactivated/Fprint/Manager',
|
||||||
|
'net.reactivated.Fprint.Manager',
|
||||||
|
None)
|
||||||
|
|
||||||
|
devices = self.manager.GetDevices()
|
||||||
|
# Find the virtual device, just in case it is a local run
|
||||||
|
# and there is another usable sensor available locally
|
||||||
|
for path in devices:
|
||||||
|
dev = Gio.DBusProxy.new_sync(self.dbus,
|
||||||
|
Gio.DBusProxyFlags.DO_NOT_AUTO_START,
|
||||||
|
None,
|
||||||
|
'net.reactivated.Fprint',
|
||||||
|
path,
|
||||||
|
'net.reactivated.Fprint.Device',
|
||||||
|
None)
|
||||||
|
|
||||||
|
if 'Virtual image device' in str(dev.get_cached_property('name')):
|
||||||
|
self.device = dev
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
print('Did not find virtual device! Probably libfprint was build without the corresponding driver!')
|
||||||
|
|
||||||
|
break
|
||||||
|
except GLib.GError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
timeout_time = timeout * 10 * timeout_sleep
|
||||||
|
self.fail('daemon did not start in %d seconds' % timeout_time)
|
||||||
|
|
||||||
|
def daemon_stop(self):
|
||||||
|
|
||||||
|
if self.daemon:
|
||||||
|
try:
|
||||||
|
self.daemon.terminate()
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
self.daemon.wait(timeout=2)
|
||||||
|
|
||||||
|
self.daemon = None
|
||||||
|
self.client = None
|
||||||
|
|
||||||
|
def polkitd_start(self):
|
||||||
|
self._polkitd, self._polkitd_obj = self.spawn_server_template(
|
||||||
|
'polkitd', {}, stdout=DEVNULL)
|
||||||
|
|
||||||
|
def polkitd_stop(self):
|
||||||
|
if self._polkitd is None:
|
||||||
|
return
|
||||||
|
self._polkitd.terminate()
|
||||||
|
self._polkitd.wait()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.test_dir = tempfile.mkdtemp()
|
||||||
|
self.state_dir = os.path.join(self.test_dir, 'state')
|
||||||
|
self.run_dir = os.path.join(self.test_dir, 'run')
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
shutil.rmtree(self.test_dir)
|
||||||
|
|
||||||
|
# From libfprint tests
|
||||||
|
def send_retry(self, retry_error=1):
|
||||||
|
# The default (1) is too-short
|
||||||
|
with Connection(self.sockaddr) as con:
|
||||||
|
con.sendall(struct.pack('ii', -1, retry_error))
|
||||||
|
|
||||||
|
# From libfprint tests
|
||||||
|
def send_image(self, image):
|
||||||
|
img = self.prints[image]
|
||||||
|
with Connection(self.sockaddr) as con:
|
||||||
|
mem = img.get_data()
|
||||||
|
mem = mem.tobytes()
|
||||||
|
self.assertEqual(len(mem), img.get_width() * img.get_height())
|
||||||
|
|
||||||
|
encoded_img = struct.pack('ii', img.get_width(), img.get_height())
|
||||||
|
encoded_img += mem
|
||||||
|
|
||||||
|
con.sendall(encoded_img)
|
||||||
|
|
||||||
|
|
||||||
|
class FPrintdVirtualDeviceTest(FPrintdTest):
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
|
||||||
|
self.polkitd_start()
|
||||||
|
self.daemon_start()
|
||||||
|
|
||||||
|
if self.device is None:
|
||||||
|
self.daemon_stop()
|
||||||
|
self.polkitd_stop()
|
||||||
|
self.skipTest("Need virtual_image device to run the test")
|
||||||
|
|
||||||
|
def timeout_cb(*args):
|
||||||
|
# Note: With meson we could just rely on it to kill us
|
||||||
|
print("Test timed out, hard exiting")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
self.test_timeout = GLib.timeout_add(get_timeout('test') * 1000, timeout_cb)
|
||||||
|
|
||||||
|
self._polkitd_obj.SetAllowed(['net.reactivated.fprint.device.setusername',
|
||||||
|
'net.reactivated.fprint.device.enroll',
|
||||||
|
'net.reactivated.fprint.device.verify'])
|
||||||
|
|
||||||
|
def signal_cb(proxy, sender, signal, params):
|
||||||
|
print(signal, params)
|
||||||
|
if signal == 'EnrollStatus':
|
||||||
|
self._abort = params[1]
|
||||||
|
self._last_result = params[0]
|
||||||
|
|
||||||
|
if not self._abort and self._last_result == 'enroll-stage-passed':
|
||||||
|
self.send_image('whorl')
|
||||||
|
elif self._abort:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self._abort = True
|
||||||
|
self._last_result = 'Unexpected signal values'
|
||||||
|
print('Unexpected signal values')
|
||||||
|
elif signal == 'VerifyFingerSelected':
|
||||||
|
pass
|
||||||
|
elif signal == 'VerifyStatus':
|
||||||
|
self._abort = True
|
||||||
|
self._last_result = params[0]
|
||||||
|
self._verify_stopped = params[1]
|
||||||
|
else:
|
||||||
|
self._abort = True
|
||||||
|
self._last_result = 'Unexpected signal'
|
||||||
|
|
||||||
|
self.g_signal_id = self.device.connect('g-signal', signal_cb)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super().tearDown()
|
||||||
|
|
||||||
|
GLib.source_remove(self.test_timeout)
|
||||||
|
self.device.disconnect(self.g_signal_id)
|
||||||
|
|
||||||
|
self.daemon_stop()
|
||||||
|
self.polkitd_stop()
|
||||||
|
|
||||||
|
def assertFprintError(self, fprint_error):
|
||||||
|
return self.assertRaisesRegex(GLib.Error,
|
||||||
|
'.*net\.reactivated\.Fprint\.Error\.{}.*'.format(fprint_error))
|
||||||
|
|
||||||
|
def test_allowed_claim(self):
|
||||||
|
self._polkitd_obj.SetAllowed(['net.reactivated.fprint.device.setusername',
|
||||||
|
'net.reactivated.fprint.device.enroll'])
|
||||||
|
self.device.Claim('(s)', 'testuser')
|
||||||
|
self.device.Release()
|
||||||
|
|
||||||
|
def test_unallowed_claim(self):
|
||||||
|
self._polkitd_obj.SetAllowed([''])
|
||||||
|
|
||||||
|
with self.assertFprintError('PermissionDenied'):
|
||||||
|
self.device.Claim('(s)', 'testuser')
|
||||||
|
|
||||||
|
self._polkitd_obj.SetAllowed(['net.reactivated.fprint.device.setusername'])
|
||||||
|
|
||||||
|
with self.assertFprintError('PermissionDenied'):
|
||||||
|
self.device.Claim('(s)', 'testuser')
|
||||||
|
|
||||||
|
self._polkitd_obj.SetAllowed(['net.reactivated.fprint.device.enroll'])
|
||||||
|
|
||||||
|
with self.assertFprintError('PermissionDenied'):
|
||||||
|
self.device.Claim('(s)', 'testuser')
|
||||||
|
|
||||||
|
def test_multiple_claims(self):
|
||||||
|
self.device.Claim('(s)', 'testuser')
|
||||||
|
|
||||||
|
with self.assertFprintError('AlreadyInUse'):
|
||||||
|
self.device.Claim('(s)', 'testuser')
|
||||||
|
|
||||||
|
self.device.Release()
|
||||||
|
|
||||||
|
def test_unallowed_release(self):
|
||||||
|
self.device.Claim('(s)', 'testuser')
|
||||||
|
|
||||||
|
self._polkitd_obj.SetAllowed([''])
|
||||||
|
|
||||||
|
with self.assertFprintError('PermissionDenied'):
|
||||||
|
self.device.Release()
|
||||||
|
|
||||||
|
self._polkitd_obj.SetAllowed(['net.reactivated.fprint.device.setusername'])
|
||||||
|
|
||||||
|
with self.assertFprintError('PermissionDenied'):
|
||||||
|
self.device.Release()
|
||||||
|
|
||||||
|
self._polkitd_obj.SetAllowed(['net.reactivated.fprint.device.enroll'])
|
||||||
|
self.device.Release()
|
||||||
|
|
||||||
|
def test_unclaimed_release(self):
|
||||||
|
with self.assertFprintError('ClaimDevice'):
|
||||||
|
self.device.Release()
|
||||||
|
|
||||||
|
def test_unclaimed_verify_start(self):
|
||||||
|
with self.assertFprintError('ClaimDevice'):
|
||||||
|
self.device.VerifyStart('(s)', 'any')
|
||||||
|
|
||||||
|
def test_unclaimed_verify_stop(self):
|
||||||
|
with self.assertFprintError('ClaimDevice'):
|
||||||
|
self.device.VerifyStop()
|
||||||
|
|
||||||
|
def test_unclaimed_enroll_start(self):
|
||||||
|
with self.assertFprintError('ClaimDevice'):
|
||||||
|
self.device.EnrollStart('(s)', 'left-index-finger')
|
||||||
|
|
||||||
|
def test_unclaimed_enroll_stop(self):
|
||||||
|
with self.assertFprintError('ClaimDevice'):
|
||||||
|
self.device.EnrollStop()
|
||||||
|
|
||||||
|
def test_wrong_finger_enroll_start(self):
|
||||||
|
self.device.Claim('(s)', 'testuser')
|
||||||
|
|
||||||
|
with self.assertFprintError('InvalidFingername'):
|
||||||
|
self.device.EnrollStart('(s)', 'any')
|
||||||
|
|
||||||
|
self.device.Release()
|
||||||
|
|
||||||
|
def test_unclaimed_delete_enrolled_fingers(self):
|
||||||
|
self.device.DeleteEnrolledFingers('(s)', 'testuser')
|
||||||
|
|
||||||
|
def test_unclaimed_delete_enrolled_fingers2(self):
|
||||||
|
with self.assertFprintError('ClaimDevice'):
|
||||||
|
self.device.DeleteEnrolledFingers2()
|
||||||
|
|
||||||
|
def test_unclaimed_list_enrolled_fingers(self):
|
||||||
|
with self.assertFprintError('NoEnrolledPrints'):
|
||||||
|
self.device.ListEnrolledFingers('(s)', 'testuser')
|
||||||
|
|
||||||
|
def test_enroll_verify_list_delete(self):
|
||||||
|
|
||||||
|
self.device.Claim('(s)', 'testuser')
|
||||||
|
|
||||||
|
with self.assertFprintError('NoEnrolledPrints'):
|
||||||
|
self.device.ListEnrolledFingers('(s)', 'testuser')
|
||||||
|
|
||||||
|
with self.assertFprintError('NoEnrolledPrints'):
|
||||||
|
self.device.ListEnrolledFingers('(s)', 'nottestuser')
|
||||||
|
|
||||||
|
self.device.EnrollStart('(s)', 'right-index-finger')
|
||||||
|
|
||||||
|
self.send_image('whorl')
|
||||||
|
|
||||||
|
self._abort = False
|
||||||
|
while not self._abort:
|
||||||
|
ctx.iteration(True)
|
||||||
|
|
||||||
|
self.assertEqual(self._last_result, 'enroll-completed')
|
||||||
|
|
||||||
|
self.device.EnrollStop()
|
||||||
|
|
||||||
|
self.assertTrue(os.path.exists(os.path.join(self.state_dir, 'testuser/virtual_image/0/7')))
|
||||||
|
|
||||||
|
with self.assertFprintError('NoEnrolledPrints'):
|
||||||
|
self.device.ListEnrolledFingers('(s)', 'nottestuser')
|
||||||
|
|
||||||
|
self.assertEqual(self.device.ListEnrolledFingers('(s)', 'testuser'), ['right-index-finger'])
|
||||||
|
|
||||||
|
# Finger is enrolled, try to verify it
|
||||||
|
self.device.VerifyStart('(s)', 'any')
|
||||||
|
|
||||||
|
# Try a wrong print; will stop verification
|
||||||
|
self.send_image('tented_arch')
|
||||||
|
self._abort = False
|
||||||
|
while not self._abort:
|
||||||
|
ctx.iteration(True)
|
||||||
|
self.assertTrue(self._verify_stopped)
|
||||||
|
self.assertEqual(self._last_result, 'verify-no-match')
|
||||||
|
|
||||||
|
self.device.VerifyStop()
|
||||||
|
self.device.VerifyStart('(s)', 'any')
|
||||||
|
|
||||||
|
# Send a retry error (swipe too short); will not stop verification
|
||||||
|
self.send_retry()
|
||||||
|
self._abort = False
|
||||||
|
while not self._abort:
|
||||||
|
ctx.iteration(True)
|
||||||
|
self.assertFalse(self._verify_stopped)
|
||||||
|
self.assertEqual(self._last_result, 'verify-swipe-too-short')
|
||||||
|
|
||||||
|
# Try the correct print; will stop verification
|
||||||
|
self.send_image('whorl')
|
||||||
|
self._abort = False
|
||||||
|
while not self._abort:
|
||||||
|
ctx.iteration(True)
|
||||||
|
self.assertTrue(self._verify_stopped)
|
||||||
|
self.assertEqual(self._last_result, 'verify-match')
|
||||||
|
|
||||||
|
self.assertEqual(self.device.ListEnrolledFingers('(s)', 'testuser'), ['right-index-finger'])
|
||||||
|
|
||||||
|
# And delete the print(s) again
|
||||||
|
self.device.DeleteEnrolledFingers('(s)', 'testuser')
|
||||||
|
|
||||||
|
self.assertFalse(os.path.exists(os.path.join(self.state_dir, 'testuser/virtual_image/0/7')))
|
||||||
|
|
||||||
|
with self.assertFprintError('NoEnrolledPrints'):
|
||||||
|
self.device.ListEnrolledFingers('(s)', 'testuser')
|
||||||
|
|
||||||
|
self.device.Release()
|
||||||
|
|
||||||
|
def test_enroll_delete2(self):
|
||||||
|
|
||||||
|
self.device.Claim('(s)', 'testuser')
|
||||||
|
|
||||||
|
self.device.EnrollStart('(s)', 'right-index-finger')
|
||||||
|
|
||||||
|
self.send_image('whorl')
|
||||||
|
|
||||||
|
self._abort = False
|
||||||
|
while not self._abort:
|
||||||
|
ctx.iteration(True)
|
||||||
|
|
||||||
|
self.assertEqual(self._last_result, 'enroll-completed')
|
||||||
|
|
||||||
|
self.device.EnrollStop()
|
||||||
|
|
||||||
|
self.assertTrue(os.path.exists(os.path.join(self.state_dir, 'testuser/virtual_image/0/7')))
|
||||||
|
|
||||||
|
# And delete the print(s) again using the new API
|
||||||
|
self.device.DeleteEnrolledFingers2()
|
||||||
|
|
||||||
|
self.assertFalse(os.path.exists(os.path.join(self.state_dir, 'testuser/virtual_image/0/7')))
|
||||||
|
|
||||||
|
self.device.Release()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
if len(sys.argv) == 2 and sys.argv[1] == "list-tests":
|
||||||
|
for machine, human in list_tests():
|
||||||
|
print("%s %s" % (machine, human), end="\n")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
unittest.main(verbosity=2)
|
||||||
47
tests/meson.build
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
tests = [
|
||||||
|
'fprintd',
|
||||||
|
'test_fprintd_utils',
|
||||||
|
]
|
||||||
|
|
||||||
|
foreach t: tests
|
||||||
|
test(t,
|
||||||
|
python3,
|
||||||
|
args: meson.current_source_dir() / t + '.py',
|
||||||
|
suite: ['daemon'],
|
||||||
|
depends: [
|
||||||
|
fprintd,
|
||||||
|
fprintd_utils,
|
||||||
|
],
|
||||||
|
env: [
|
||||||
|
'G_DEBUG=fatal-criticals',
|
||||||
|
'G_MESSAGES_DEBUG=all',
|
||||||
|
'FPRINT_BUILD_DIR=' + meson.build_root() / 'src',
|
||||||
|
'TOPSRCDIR=' + meson.source_root(),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
endforeach
|
||||||
|
|
||||||
|
add_test_setup('default_setup',
|
||||||
|
is_default: true,
|
||||||
|
env: [
|
||||||
|
'G_SLICE=always-malloc',
|
||||||
|
'MALLOC_CHECK_=2',
|
||||||
|
'MALLOC_PERTURB_=55',
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
if find_program('valgrind', required: false).found()
|
||||||
|
glib_share = glib_dep.get_pkgconfig_variable('prefix') / 'share' / glib_dep.name()
|
||||||
|
glib_suppressions = glib_share + '/valgrind/glib.supp'
|
||||||
|
add_test_setup('valgrind',
|
||||||
|
env: [
|
||||||
|
'G_SLICE=always-malloc',
|
||||||
|
'VALGRIND=' + glib_suppressions,
|
||||||
|
],
|
||||||
|
timeout_multiplier: 5
|
||||||
|
)
|
||||||
|
endif
|
||||||
|
|
||||||
|
if get_option('pam')
|
||||||
|
subdir('pam')
|
||||||
|
endif
|
||||||
27
tests/pam/meson.build
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
subdir('services')
|
||||||
|
|
||||||
|
tests = [
|
||||||
|
'test_pam_fprintd',
|
||||||
|
]
|
||||||
|
|
||||||
|
foreach t: tests
|
||||||
|
test(t,
|
||||||
|
python3,
|
||||||
|
args: meson.current_source_dir() / t + '.py',
|
||||||
|
suite: ['PAM'],
|
||||||
|
depends: [
|
||||||
|
pam_fprintd,
|
||||||
|
pam_service_file,
|
||||||
|
],
|
||||||
|
env: [
|
||||||
|
'TOPBUILDDIR=' + meson.build_root(),
|
||||||
|
'TOPSRCDIR=' + meson.source_root(),
|
||||||
|
'LD_PRELOAD=libpam_wrapper.so',
|
||||||
|
'PAM_WRAPPER=1',
|
||||||
|
'PAM_WRAPPER_DEBUGLEVEL=2',
|
||||||
|
'PAM_WRAPPER_SERVICE_DIR=' + meson.current_build_dir() / 'services',
|
||||||
|
'G_DEBUG=fatal-warnings',
|
||||||
|
],
|
||||||
|
timeout: 60,
|
||||||
|
)
|
||||||
|
endforeach
|
||||||
1
tests/pam/services/fprintd-pam-test.in
Normal file
@ -0,0 +1 @@
|
|||||||
|
auth required @FPRINTDPAMPATH@ debug timeout=10
|
||||||
13
tests/pam/services/meson.build
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# Meson doesn't allow to have configure_file's as targets we depend on... Meh!
|
||||||
|
pam_service_file = custom_target('pam_test_service_file',
|
||||||
|
output: 'null',
|
||||||
|
command: 'true',
|
||||||
|
depends: pam_fprintd,
|
||||||
|
depend_files: configure_file(
|
||||||
|
input: 'fprintd-pam-test.in',
|
||||||
|
output: 'fprintd-pam-test',
|
||||||
|
configuration: configuration_data({
|
||||||
|
'FPRINTDPAMPATH': pam_fprintd.full_path(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
169
tests/pam/test_pam_fprintd.py
Executable file
@ -0,0 +1,169 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
# This program is free software; you can redistribute it and/or modify it under
|
||||||
|
# the terms of the GNU Lesser General Public License as published by the Free
|
||||||
|
# Software Foundation; either version 3 of the License, or (at your option) any
|
||||||
|
# later version. See http://www.gnu.org/copyleft/lgpl.html for the full text
|
||||||
|
# of the license.
|
||||||
|
|
||||||
|
__author__ = 'Bastien Nocera'
|
||||||
|
__email__ = 'hadess@hadess.net'
|
||||||
|
__copyright__ = '(c) 2020 Red Hat Inc.'
|
||||||
|
__license__ = 'LGPL 3+'
|
||||||
|
|
||||||
|
import tempfile
|
||||||
|
import unittest
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
import dbus
|
||||||
|
import dbus.mainloop.glib
|
||||||
|
import dbusmock
|
||||||
|
import fcntl
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import pypamtest
|
||||||
|
|
||||||
|
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
|
||||||
|
|
||||||
|
PAM_SUCCESS = 0
|
||||||
|
PAM_AUTH_ERR = 7
|
||||||
|
PAM_AUTHINFO_UNAVAIL = 9
|
||||||
|
PAM_USER_UNKNOWN = 10
|
||||||
|
PAM_MAXTRIES = 11
|
||||||
|
|
||||||
|
class TestPamFprintd(dbusmock.DBusTestCase):
|
||||||
|
'''Test pam_fprintd'''
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def start_monitor(klass):
|
||||||
|
'''Start dbus-monitor'''
|
||||||
|
|
||||||
|
workdir = os.environ['TOPBUILDDIR'] + '/tests/pam/'
|
||||||
|
klass.monitor_log = open(os.path.join(workdir, 'dbus-monitor.log'), 'wb', buffering=0)
|
||||||
|
klass.monitor = subprocess.Popen(['dbus-monitor', '--monitor', '--system'],
|
||||||
|
stdout=klass.monitor_log,
|
||||||
|
stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def stop_monitor(klass):
|
||||||
|
'''Stop dbus-monitor'''
|
||||||
|
|
||||||
|
assert klass.monitor
|
||||||
|
klass.monitor.terminate()
|
||||||
|
klass.monitor.wait()
|
||||||
|
|
||||||
|
klass.monitor_log.flush()
|
||||||
|
klass.monitor_log.close()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(klass):
|
||||||
|
klass.start_system_bus()
|
||||||
|
klass.start_monitor()
|
||||||
|
klass.dbus_con = klass.get_dbus(True)
|
||||||
|
|
||||||
|
template_path = './'
|
||||||
|
if 'TOPSRCDIR' in os.environ:
|
||||||
|
template_path = os.environ['TOPSRCDIR'] + '/tests/'
|
||||||
|
klass.template_name = template_path + 'dbusmock/fprintd.py'
|
||||||
|
print ('Using template from %s' % klass.template_name)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(klass):
|
||||||
|
klass.stop_monitor()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
(self.p_mock, self.obj_fprintd_manager) = self.spawn_server_template(
|
||||||
|
self.template_name, {}, stdout=subprocess.PIPE)
|
||||||
|
# set log to nonblocking
|
||||||
|
flags = fcntl.fcntl(self.p_mock.stdout, fcntl.F_GETFL)
|
||||||
|
fcntl.fcntl(self.p_mock.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK)
|
||||||
|
self.obj_fprintd_mock = dbus.Interface(self.obj_fprintd_manager, 'net.reactivated.Fprint.Manager.Mock')
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.p_mock.terminate()
|
||||||
|
self.p_mock.wait()
|
||||||
|
|
||||||
|
def setup_device(self):
|
||||||
|
device_path = self.obj_fprintd_mock.AddDevice('FDO Trigger Finger Laser Reader', 3, 'swipe')
|
||||||
|
self.device_mock = self.dbus_con.get_object('net.reactivated.Fprint', device_path)
|
||||||
|
self.device_mock.SetEnrolledFingers('toto', ['left-little-finger', 'right-little-finger'])
|
||||||
|
|
||||||
|
def test_pam_fprintd_auth(self):
|
||||||
|
self.setup_device()
|
||||||
|
script = [
|
||||||
|
( 'verify-match', True, 2 )
|
||||||
|
]
|
||||||
|
self.device_mock.SetVerifyScript(script)
|
||||||
|
|
||||||
|
tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE, expected_rv=PAM_SUCCESS)
|
||||||
|
res = pypamtest.run_pamtest("toto", "fprintd-pam-test", [tc], [ 'unused' ])
|
||||||
|
|
||||||
|
self.assertRegex(res.info[0], r'Swipe your left little finger across the fingerprint reader')
|
||||||
|
self.assertEqual(len(res.errors), 0)
|
||||||
|
|
||||||
|
def test_pam_fprintd_dual_reader_auth(self):
|
||||||
|
device_path = self.obj_fprintd_mock.AddDevice('FDO Sandpaper Reader', 3, 'press')
|
||||||
|
sandpaper_device_mock = self.dbus_con.get_object('net.reactivated.Fprint', device_path)
|
||||||
|
sandpaper_device_mock.SetEnrolledFingers('toto', ['left-middle-finger', 'right-middle-finger'])
|
||||||
|
script = [
|
||||||
|
( 'verify-match', True, 2 )
|
||||||
|
]
|
||||||
|
sandpaper_device_mock.SetVerifyScript(script)
|
||||||
|
|
||||||
|
# Add a 2nd device
|
||||||
|
self.setup_device()
|
||||||
|
|
||||||
|
tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE, expected_rv=PAM_SUCCESS)
|
||||||
|
res = pypamtest.run_pamtest("toto", "fprintd-pam-test", [tc], [ 'unused' ])
|
||||||
|
|
||||||
|
self.assertRegex(res.info[0], r'Place your left middle finger on FDO Sandpaper Reader')
|
||||||
|
self.assertEqual(len(res.errors), 0)
|
||||||
|
|
||||||
|
def test_pam_fprintd_last_try_auth(self):
|
||||||
|
self.setup_device()
|
||||||
|
script = [
|
||||||
|
( 'verify-no-match', True, 1 ),
|
||||||
|
( 'verify-no-match', True, 1 ),
|
||||||
|
( 'verify-match', True, 1 ),
|
||||||
|
]
|
||||||
|
self.device_mock.SetVerifyScript(script)
|
||||||
|
|
||||||
|
tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE, expected_rv=PAM_SUCCESS)
|
||||||
|
res = pypamtest.run_pamtest("toto", "fprintd-pam-test", [tc], [ 'unused' ])
|
||||||
|
|
||||||
|
self.assertRegex(res.info[0], r'Swipe your left little finger across the fingerprint reader')
|
||||||
|
self.assertEqual(len(res.errors), 2)
|
||||||
|
self.assertRegex(res.errors[0], r'Failed to match fingerprint')
|
||||||
|
self.assertRegex(res.errors[1], r'Failed to match fingerprint')
|
||||||
|
|
||||||
|
def test_pam_fprintd_failed_auth(self):
|
||||||
|
self.setup_device()
|
||||||
|
script = [
|
||||||
|
( 'verify-no-match', True, 1 ),
|
||||||
|
( 'verify-no-match', True, 1 ),
|
||||||
|
( 'verify-no-match', True, 1 ),
|
||||||
|
]
|
||||||
|
self.device_mock.SetVerifyScript(script)
|
||||||
|
|
||||||
|
tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE, expected_rv=PAM_MAXTRIES)
|
||||||
|
res = pypamtest.run_pamtest("toto", "fprintd-pam-test", [tc], [ 'unused' ])
|
||||||
|
|
||||||
|
self.assertRegex(res.info[0], r'Swipe your left little finger across the fingerprint reader')
|
||||||
|
self.assertEqual(len(res.errors), 3)
|
||||||
|
self.assertRegex(res.errors[0], r'Failed to match fingerprint')
|
||||||
|
self.assertRegex(res.errors[1], r'Failed to match fingerprint')
|
||||||
|
self.assertRegex(res.errors[2], r'Failed to match fingerprint')
|
||||||
|
|
||||||
|
def test_pam_timeout(self):
|
||||||
|
self.setup_device()
|
||||||
|
|
||||||
|
tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE, expected_rv=PAM_AUTHINFO_UNAVAIL)
|
||||||
|
res = pypamtest.run_pamtest("toto", "fprintd-pam-test", [tc], [ 'unused' ])
|
||||||
|
self.assertRegex(res.info[1], r'Verification timed out')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
if 'PAM_WRAPPER_SERVICE_DIR' not in os.environ:
|
||||||
|
print('Cannot run test without environment set correctly, run "meson test" instead')
|
||||||
|
sys.exit(1)
|
||||||
|
# set stream to sys.stderr to get debug output
|
||||||
|
unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=2))
|
||||||
3
tests/prints/README
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
These are example images from NIST and are in the public domain.
|
||||||
|
|
||||||
|
The PNG files have been generated by using the greyscale data as a mask.
|
||||||
BIN
tests/prints/arch.jpg
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
tests/prints/arch.png
Normal file
|
After Width: | Height: | Size: 120 KiB |
BIN
tests/prints/loop-right.jpg
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
tests/prints/loop-right.png
Normal file
|
After Width: | Height: | Size: 120 KiB |
BIN
tests/prints/tented_arch.jpg
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
tests/prints/tented_arch.png
Normal file
|
After Width: | Height: | Size: 107 KiB |
BIN
tests/prints/whorl.jpg
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
tests/prints/whorl.png
Normal file
|
After Width: | Height: | Size: 102 KiB |
187
tests/test_fprintd_utils.py
Executable file
@ -0,0 +1,187 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
# This program is free software; you can redistribute it and/or modify it under
|
||||||
|
# the terms of the GNU Lesser General Public License as published by the Free
|
||||||
|
# Software Foundation; either version 3 of the License, or (at your option) any
|
||||||
|
# later version. See http://www.gnu.org/copyleft/lgpl.html for the full text
|
||||||
|
# of the license.
|
||||||
|
|
||||||
|
__author__ = 'Bastien Nocera'
|
||||||
|
__email__ = 'hadess@hadess.net'
|
||||||
|
__copyright__ = '(c) 2020 Red Hat Inc.'
|
||||||
|
__license__ = 'LGPL 3+'
|
||||||
|
|
||||||
|
import tempfile
|
||||||
|
import unittest
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
import dbus
|
||||||
|
import dbus.mainloop.glib
|
||||||
|
import dbusmock
|
||||||
|
import fcntl
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
|
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
|
||||||
|
|
||||||
|
class TestFprintd(dbusmock.DBusTestCase):
|
||||||
|
'''Test fprintd utilities'''
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(klass):
|
||||||
|
klass.start_system_bus()
|
||||||
|
klass.dbus_con = klass.get_dbus(True)
|
||||||
|
klass.sleep_time = 0.5
|
||||||
|
|
||||||
|
template_path = './'
|
||||||
|
if 'TOPSRCDIR' in os.environ:
|
||||||
|
template_path = os.environ['TOPSRCDIR'] + '/tests/'
|
||||||
|
klass.template_name = template_path + 'dbusmock/fprintd.py'
|
||||||
|
print ('Using template from %s' % klass.template_name)
|
||||||
|
|
||||||
|
klass.tools_prefix = ''
|
||||||
|
if 'FPRINT_BUILD_DIR' in os.environ:
|
||||||
|
klass.tools_prefix = os.environ['FPRINT_BUILD_DIR'] + '/../utils/'
|
||||||
|
print ('Using tools from %s' % klass.tools_prefix)
|
||||||
|
else:
|
||||||
|
print ('Using tools from $PATH')
|
||||||
|
|
||||||
|
klass.wrapper_args = []
|
||||||
|
klass.valgrind = False
|
||||||
|
if 'VALGRIND' in os.environ:
|
||||||
|
valgrind = os.environ['VALGRIND']
|
||||||
|
if valgrind is not None:
|
||||||
|
klass.valgrind = True
|
||||||
|
klass.sleep_time *= 4
|
||||||
|
klass.wrapper_args = ['valgrind', '--leak-check=full']
|
||||||
|
if os.path.exists(valgrind):
|
||||||
|
klass.wrapper_args += ['--suppressions={}'.format(valgrind)]
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
(self.p_mock, self.obj_fprintd_manager) = self.spawn_server_template(
|
||||||
|
self.template_name, {}, stdout=subprocess.PIPE)
|
||||||
|
# set log to nonblocking
|
||||||
|
flags = fcntl.fcntl(self.p_mock.stdout, fcntl.F_GETFL)
|
||||||
|
fcntl.fcntl(self.p_mock.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK)
|
||||||
|
self.obj_fprintd_mock = dbus.Interface(self.obj_fprintd_manager, 'net.reactivated.Fprint.Manager.Mock')
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.p_mock.terminate()
|
||||||
|
self.p_mock.wait()
|
||||||
|
|
||||||
|
def setup_device(self):
|
||||||
|
device_path = self.obj_fprintd_mock.AddDevice('FDO Trigger Finger Laser Reader', 3, 'swipe')
|
||||||
|
self.device_mock = self.dbus_con.get_object('net.reactivated.Fprint', device_path)
|
||||||
|
self.device_mock.SetEnrolledFingers('toto', ['left-little-finger', 'right-little-finger'])
|
||||||
|
|
||||||
|
def test_fprintd_enroll(self):
|
||||||
|
self.setup_device()
|
||||||
|
|
||||||
|
mock_log = tempfile.NamedTemporaryFile()
|
||||||
|
process = subprocess.Popen(self.wrapper_args + [self.tools_prefix + 'fprintd-enroll', '-f', 'right-index-finger', 'toto'],
|
||||||
|
stdout=mock_log,
|
||||||
|
stderr=subprocess.STDOUT,
|
||||||
|
universal_newlines=True)
|
||||||
|
|
||||||
|
time.sleep(self.sleep_time)
|
||||||
|
|
||||||
|
with open(mock_log.name) as f:
|
||||||
|
out = f.read()
|
||||||
|
self.assertRegex(out, r'right-index-finger')
|
||||||
|
|
||||||
|
self.device_mock.EmitEnrollStatus('enroll-completed', True)
|
||||||
|
time.sleep(self.sleep_time)
|
||||||
|
|
||||||
|
with open(mock_log.name) as f:
|
||||||
|
out = f.read()
|
||||||
|
self.assertRegex(out, 'Enroll result: enroll-completed')
|
||||||
|
|
||||||
|
def test_fprintd_verify(self):
|
||||||
|
self.setup_device()
|
||||||
|
|
||||||
|
mock_log = tempfile.NamedTemporaryFile()
|
||||||
|
process = subprocess.Popen(self.wrapper_args + [self.tools_prefix + 'fprintd-verify', 'toto'],
|
||||||
|
stdout=mock_log,
|
||||||
|
stderr=subprocess.STDOUT,
|
||||||
|
universal_newlines=True)
|
||||||
|
|
||||||
|
time.sleep(self.sleep_time)
|
||||||
|
|
||||||
|
with open(mock_log.name) as f:
|
||||||
|
out = f.read()
|
||||||
|
self.assertRegex(out, r'left-little-finger')
|
||||||
|
self.assertNotRegex(out, 'Verify result: verify-match \(done\)')
|
||||||
|
|
||||||
|
self.device_mock.EmitVerifyStatus('verify-match', True)
|
||||||
|
time.sleep(self.sleep_time)
|
||||||
|
|
||||||
|
with open(mock_log.name) as f:
|
||||||
|
out = f.read()
|
||||||
|
self.assertRegex(out, 'Verify result: verify-match \(done\)')
|
||||||
|
|
||||||
|
def test_fprintd_verify_script(self):
|
||||||
|
self.setup_device()
|
||||||
|
script = [
|
||||||
|
( 'verify-match', True, 2 )
|
||||||
|
]
|
||||||
|
self.device_mock.SetVerifyScript(script)
|
||||||
|
|
||||||
|
mock_log = tempfile.NamedTemporaryFile()
|
||||||
|
process = subprocess.Popen(self.wrapper_args + [self.tools_prefix + 'fprintd-verify', 'toto'],
|
||||||
|
stdout=mock_log,
|
||||||
|
stderr=subprocess.STDOUT,
|
||||||
|
universal_newlines=True)
|
||||||
|
|
||||||
|
time.sleep(self.sleep_time)
|
||||||
|
|
||||||
|
with open(mock_log.name) as f:
|
||||||
|
out = f.read()
|
||||||
|
self.assertRegex(out, r'left-little-finger')
|
||||||
|
self.assertNotRegex(out, 'Verify result: verify-match \(done\)')
|
||||||
|
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
with open(mock_log.name) as f:
|
||||||
|
out = f.read()
|
||||||
|
self.assertRegex(out, 'Verify result: verify-match \(done\)')
|
||||||
|
|
||||||
|
def test_fprintd_list(self):
|
||||||
|
self.setup_device()
|
||||||
|
|
||||||
|
# Rick has no fingerprints enrolled
|
||||||
|
out = subprocess.check_output(self.wrapper_args + [self.tools_prefix + 'fprintd-list', 'rick'],
|
||||||
|
stderr=subprocess.STDOUT,
|
||||||
|
universal_newlines=True)
|
||||||
|
self.assertRegex(out, r'has no fingers enrolled for')
|
||||||
|
|
||||||
|
# Toto does
|
||||||
|
out = subprocess.check_output(self.wrapper_args + [self.tools_prefix + 'fprintd-list', 'toto'],
|
||||||
|
universal_newlines=True)
|
||||||
|
self.assertRegex(out, r'left-little-finger')
|
||||||
|
self.assertRegex(out, r'right-little-finger')
|
||||||
|
|
||||||
|
def test_fprintd_delete(self):
|
||||||
|
self.setup_device()
|
||||||
|
|
||||||
|
# Has fingerprints enrolled
|
||||||
|
out = subprocess.check_output(self.wrapper_args + [self.tools_prefix + 'fprintd-list', 'toto'],
|
||||||
|
stderr=subprocess.STDOUT,
|
||||||
|
universal_newlines=True)
|
||||||
|
self.assertRegex(out, r'left-little-finger')
|
||||||
|
self.assertRegex(out, r'right-little-finger')
|
||||||
|
|
||||||
|
# Delete fingerprints
|
||||||
|
out = subprocess.check_output(self.wrapper_args + [self.tools_prefix + 'fprintd-delete', 'toto'],
|
||||||
|
stderr=subprocess.STDOUT,
|
||||||
|
universal_newlines=True)
|
||||||
|
self.assertRegex(out, r'Fingerprints deleted')
|
||||||
|
|
||||||
|
# Doesn't have fingerprints
|
||||||
|
out = subprocess.check_output(self.wrapper_args + [self.tools_prefix + 'fprintd-list', 'toto'],
|
||||||
|
stderr=subprocess.STDOUT,
|
||||||
|
universal_newlines=True)
|
||||||
|
self.assertRegex(out, r'has no fingers enrolled for')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# avoid writing to stderr
|
||||||
|
unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=2))
|
||||||
9
update-transifex.sh
Executable file
@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
test -f .tx/config || exit 1
|
||||||
|
|
||||||
|
echo Pulling translations from Transifex
|
||||||
|
tx --root `dirname $0` pull --all --force --skip
|
||||||
|
|
||||||
|
echo Pushing strings to Transifex
|
||||||
|
tx push --source
|
||||||
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <locale.h>
|
||||||
#include <dbus/dbus-glib-bindings.h>
|
#include <dbus/dbus-glib-bindings.h>
|
||||||
#include "manager-dbus-glue.h"
|
#include "manager-dbus-glue.h"
|
||||||
#include "device-dbus-glue.h"
|
#include "device-dbus-glue.h"
|
||||||
@ -54,7 +55,12 @@ static void delete_fingerprints(DBusGProxy *dev, const char *username)
|
|||||||
exit (1);
|
exit (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!net_reactivated_Fprint_Device_delete_enrolled_fingers(dev, username, &error)) {
|
if (!net_reactivated_Fprint_Device_claim(dev, username, &error)) {
|
||||||
|
g_print("failed to claim device: %s\n", error->message);
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!net_reactivated_Fprint_Device_delete_enrolled_fingers2(dev, &error)) {
|
||||||
if (dbus_g_error_has_name (error, "net.reactivated.Fprint.Error.NoEnrolledPrints") == FALSE) {
|
if (dbus_g_error_has_name (error, "net.reactivated.Fprint.Error.NoEnrolledPrints") == FALSE) {
|
||||||
g_print("ListEnrolledFingers failed: %s\n", error->message);
|
g_print("ListEnrolledFingers failed: %s\n", error->message);
|
||||||
exit (1);
|
exit (1);
|
||||||
@ -64,6 +70,12 @@ static void delete_fingerprints(DBusGProxy *dev, const char *username)
|
|||||||
} else {
|
} else {
|
||||||
g_print ("Fingerprints deleted on %s\n", g_value_get_string (g_hash_table_lookup (props, "name")));
|
g_print ("Fingerprints deleted on %s\n", g_value_get_string (g_hash_table_lookup (props, "name")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!net_reactivated_Fprint_Device_release(dev, &error)) {
|
||||||
|
g_print("ReleaseDevice failed: %s\n", error->message);
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
|
||||||
g_hash_table_destroy (props);
|
g_hash_table_destroy (props);
|
||||||
g_object_unref (p);
|
g_object_unref (p);
|
||||||
}
|
}
|
||||||
@ -114,9 +126,7 @@ static void process_devices(char **argv)
|
|||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
#if !GLIB_CHECK_VERSION (2, 36, 0)
|
setlocale (LC_ALL, "");
|
||||||
g_type_init();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
create_manager();
|
create_manager();
|
||||||
|
|
||||||
@ -17,8 +17,10 @@
|
|||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <locale.h>
|
||||||
#include <dbus/dbus-glib-bindings.h>
|
#include <dbus/dbus-glib-bindings.h>
|
||||||
#include "manager-dbus-glue.h"
|
#include "manager-dbus-glue.h"
|
||||||
#include "device-dbus-glue.h"
|
#include "device-dbus-glue.h"
|
||||||
@ -30,7 +32,7 @@
|
|||||||
|
|
||||||
static DBusGProxy *manager = NULL;
|
static DBusGProxy *manager = NULL;
|
||||||
static DBusGConnection *connection = NULL;
|
static DBusGConnection *connection = NULL;
|
||||||
static char *finger_name = "right-index-finger";
|
static char *finger_name = NULL;
|
||||||
static char **usernames = NULL;
|
static char **usernames = NULL;
|
||||||
|
|
||||||
static void create_manager(void)
|
static void create_manager(void)
|
||||||
@ -159,9 +161,7 @@ int main(int argc, char **argv)
|
|||||||
GError *err = NULL;
|
GError *err = NULL;
|
||||||
DBusGProxy *dev;
|
DBusGProxy *dev;
|
||||||
|
|
||||||
#if !GLIB_CHECK_VERSION (2, 36, 0)
|
setlocale (LC_ALL, "");
|
||||||
g_type_init();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
dbus_g_object_register_marshaller (fprintd_marshal_VOID__STRING_BOOLEAN,
|
dbus_g_object_register_marshaller (fprintd_marshal_VOID__STRING_BOOLEAN,
|
||||||
G_TYPE_NONE, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_INVALID);
|
G_TYPE_NONE, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_INVALID);
|
||||||
@ -175,11 +175,16 @@ int main(int argc, char **argv)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (finger_name == NULL)
|
||||||
|
finger_name = g_strdup("right-index-finger");
|
||||||
|
|
||||||
create_manager();
|
create_manager();
|
||||||
|
|
||||||
dev = open_device(usernames ? usernames[0] : NULL);
|
dev = open_device(usernames ? usernames[0] : NULL);
|
||||||
do_enroll(dev);
|
do_enroll(dev);
|
||||||
release_device(dev);
|
release_device(dev);
|
||||||
|
g_free(finger_name);
|
||||||
|
g_strfreev(usernames);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <locale.h>
|
||||||
#include <dbus/dbus-glib-bindings.h>
|
#include <dbus/dbus-glib-bindings.h>
|
||||||
#include "manager-dbus-glue.h"
|
#include "manager-dbus-glue.h"
|
||||||
#include "device-dbus-glue.h"
|
#include "device-dbus-glue.h"
|
||||||
@ -130,9 +131,7 @@ static void process_devices(char **argv)
|
|||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
#if !GLIB_CHECK_VERSION (2, 36, 0)
|
setlocale (LC_ALL, "");
|
||||||
g_type_init();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
create_manager();
|
create_manager();
|
||||||
|
|
||||||
67
utils/meson.build
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
dbus_client_glue_sources = []
|
||||||
|
|
||||||
|
foreach interface_name: dbus_interfaces
|
||||||
|
interface = interface_name.to_lower()
|
||||||
|
interface_file = meson.source_root() / 'src' / interface + '.xml'
|
||||||
|
glue_name = interface + '-dbus-glue.h'
|
||||||
|
dbus_client_glue_sources += custom_target(glue_name,
|
||||||
|
input: interface_file,
|
||||||
|
output: glue_name,
|
||||||
|
command: [
|
||||||
|
dbus_binding_tool,
|
||||||
|
'--prefix=fprint_' + interface,
|
||||||
|
'--mode=glib-client',
|
||||||
|
'--output=@OUTPUT@',
|
||||||
|
'@INPUT@',
|
||||||
|
])
|
||||||
|
endforeach
|
||||||
|
|
||||||
|
utils_marshal = custom_target('utils_marshal',
|
||||||
|
depends: fprintd_marshal,
|
||||||
|
input: fprintd_marshal,
|
||||||
|
output: ['marshal.c', 'marshal.h'],
|
||||||
|
command: [bash, '-c',
|
||||||
|
'cp @INPUT0@ @OUTPUT0@;' +
|
||||||
|
'cp @INPUT1@ @OUTPUT1@;' +
|
||||||
|
'sed s/fprintd-//g -i ' + meson.current_build_dir() / 'marshal*.{h,c}']
|
||||||
|
)
|
||||||
|
|
||||||
|
libfprintd_utils_dep = declare_dependency(
|
||||||
|
include_directories: [
|
||||||
|
include_directories('../pam'),
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
glib_dep,
|
||||||
|
dbus_glib_dep,
|
||||||
|
],
|
||||||
|
sources: [
|
||||||
|
utils_marshal,
|
||||||
|
dbus_client_glue_sources,
|
||||||
|
],
|
||||||
|
link_with: static_library('fprintd_utils',
|
||||||
|
sources: [
|
||||||
|
dbus_client_glue_sources,
|
||||||
|
utils_marshal,
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
glib_dep,
|
||||||
|
]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
utils = [
|
||||||
|
'delete',
|
||||||
|
'enroll',
|
||||||
|
'list',
|
||||||
|
'verify',
|
||||||
|
]
|
||||||
|
|
||||||
|
fprintd_utils = []
|
||||||
|
|
||||||
|
foreach util: utils
|
||||||
|
fprintd_utils += executable('fprintd-' + util,
|
||||||
|
sources: util + '.c',
|
||||||
|
dependencies: libfprintd_utils_dep,
|
||||||
|
install: true,
|
||||||
|
)
|
||||||
|
endforeach
|
||||||
@ -20,6 +20,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <locale.h>
|
||||||
#include <dbus/dbus-glib-bindings.h>
|
#include <dbus/dbus-glib-bindings.h>
|
||||||
#include "manager-dbus-glue.h"
|
#include "manager-dbus-glue.h"
|
||||||
#include "device-dbus-glue.h"
|
#include "device-dbus-glue.h"
|
||||||
@ -170,11 +171,9 @@ int main(int argc, char **argv)
|
|||||||
GOptionContext *context;
|
GOptionContext *context;
|
||||||
GError *err = NULL;
|
GError *err = NULL;
|
||||||
DBusGProxy *dev;
|
DBusGProxy *dev;
|
||||||
char *username;
|
const char *username = NULL;
|
||||||
|
|
||||||
#if !GLIB_CHECK_VERSION (2, 36, 0)
|
setlocale (LC_ALL, "");
|
||||||
g_type_init();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
dbus_g_object_register_marshaller (fprintd_marshal_VOID__STRING_BOOLEAN,
|
dbus_g_object_register_marshaller (fprintd_marshal_VOID__STRING_BOOLEAN,
|
||||||
G_TYPE_NONE, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_INVALID);
|
G_TYPE_NONE, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_INVALID);
|
||||||